Subversion Repositories Projects

Rev

Rev 771 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
771 - 1
/****************************************************************************
902 - 2
 *   Copyright (C) 2009-2011 by Claas Anders "CaScAdE" Rathje               *
771 - 3
 *   admiralcascade@gmail.com                                               *
4
 *   Project-URL: http://www.mylifesucks.de/oss/c-strom/                    *
5
 *                                                                          *
6
 *   This program is free software; you can redistribute it and/or modify   *
7
 *   it under the terms of the GNU General Public License as published by   *
8
 *   the Free Software Foundation; either version 2 of the License.         *
9
 *                                                                          *
10
 *   This program is distributed in the hope that it will be useful,        *
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
13
 *   GNU General Public License for more details.                           *
14
 *                                                                          *
15
 *   You should have received a copy of the GNU General Public License      *
16
 *   along with this program; if not, write to the                          *
17
 *   Free Software Foundation, Inc.,                                        *
18
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.              *
19
 *                                                                          *
20
 *   Thanks to:                                                             *
21
 *   Klaus "akku" Buettner for the hardware                                 *
22
 *   All people at http://www.rn-wissen.de especially for the i2c stuff     *
23
 *                                                                          *
24
 ****************************************************************************/
25
 
26
#include <avr/io.h>
27
#include <avr/eeprom.h>
28
#include <avr/pgmspace.h>
29
#include <avr/interrupt.h>
30
#include <util/delay.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include "C-Strom.h"
34
#include "spi_union.h"
35
#include "i2c_slave.h"
36
 
37
uint8_t EEMEM ee_checkbyte1 = CHECKBYTE1;
38
uint8_t EEMEM ee_checkbyte2 = CHECKBYTE2;
39
uint16_t EEMEM ee_cal_ampere = 512;
40
uint8_t EEMEM ee_sensor = 50;
41
uint8_t EEMEM ee_prim_r1 = 47, ee_prim_r2 = 150;
42
uint8_t EEMEM ee_anin_r1 = 47, ee_anin_r2 = 150;
43
uint8_t EEMEM ee_config = 0;
44
 
45
volatile uint8_t CSTROM_FLAGS = 0;
46
volatile uint8_t CSTROM_CONFIG = 0;
47
 
48
// we could use ee_cal_ampere but eeprom is slow :)
49
volatile uint16_t cal_ampere = 512;
50
volatile uint8_t sensor = 50;
51
volatile uint8_t prim_r1 = 47, prim_r2 = 150;
52
volatile uint8_t anin_r1 = 47, anin_r2 = 150;
53
volatile int16_t ampere, volt, anin_volt, transfer_ampere;
54
volatile int32_t transfer_mah, mah;
55
volatile int16_t average_ampere = 0;
56
volatile uint8_t hwver = 10;
57
// global space for int conversion to string
58
char s[10];
59
 
60
// spi buffer
61
union SPI_buffer_t SPI_buffer;
62
 
63
// PD7 High
64
void PD7_H() {
65
        PORTD |=  (1 << PD7);
66
}
67
 
68
// PD7 Low
69
void PD7_L() {
70
        PORTD &= ~(1 << PD7);
71
}
72
 
73
void (*LED_ON)(void) = PD7_H;
74
void (*LED_OFF)(void) = PD7_L;
75
 
76
 
77
void ampere_calibrate();
78
void save_eeprom();
79
void help(uint8_t);
80
 
81
/*ISR(__vector_default) {
82
    asm("nop");
83
}*/
84
 
85
/**
86
 * decimal itoa for 10th values
87
 */
88
char *itoa_dec(int val, char* s) {
89
        itoa(val, s, 10);
90
        //char x = 0;
91
        for (uint8_t i = 0; i < 9; i++) {
92
                if (s[i] == 0 && i > 0) {
93
                        if (i == 1) {
94
                                s[i+1] = s[i-1];       
95
                                s[i-1] = '0';
96
                                s[i] = '.';
97
                                s[i+2] = 0;    
98
                        } else {
99
                                s[i] = s[i-1];
100
                                s[i-1] = '.';
101
                                s[i+1] = 0;
102
                        }
103
                        break;
104
                }
105
        }
106
        return s;
107
}
108
 
109
/**
110
 * init uart
111
 */
112
void uart_init() {
113
    UBRRL = (F_CPU / (16UL * BAUD_RATE)) - 1;
114
 
115
    // Enable receiver and transmitter; enable RX interrupt
116
    UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
117
 
118
    //asynchronous 8N1
119
    UCSRC = (1 << URSEL) | (3 << UCSZ0);
120
}
121
 
122
/**
123
 * send a single <character> through uart
124
 */
125
void uart_putc(unsigned char character) {
126
    // wait until UDR ready
127
    while (!(UCSRA & (1 << UDRE)));
128
    UDR = character;
129
}
130
 
131
/**
132
 * send a <string> throught uart
133
 */
134
void uart_puts(char *s) {
135
    while (*s) {
136
        uart_putc(*s);
137
        s++;
138
    }
139
}
140
 
141
/**
142
 * send a <string> from pgm space throught uart
143
 */
144
void uart_puts_pgm(char *string) {
145
        while (pgm_read_byte(string) != 0x00)
146
                uart_putc(pgm_read_byte(string++));
147
}
148
 
149
/**
150
 * change the sensor type
151
 */
152
void sensor_change(uint8_t new_value) {
153
        if (new_value < 10) new_value = 0;
154
        else if (new_value > 250) new_value = 250;
155
        sensor = new_value;
156
        uart_puts_pgm(PSTR("\r\nSensor is now: "));
157
        uart_puts(itoa(sensor, s, 10));
158
        uart_puts("A\r\n");
159
}
160
 
161
/**
162
 * change the r2 value
163
 */
164
void r2_change(uint8_t which, uint8_t new_value) {     
165
        if (which == V_ANIN) {         
166
                uart_puts_pgm(PSTR("\r\nANIN R2 is now: "));
167
                anin_r2 = new_value;
168
                uart_puts(itoa_dec(anin_r2, s));
169
        } else {
170
                uart_puts_pgm(PSTR("\r\nPRIMARY R2 is now: "));
171
                prim_r2 = new_value;
172
                uart_puts(itoa_dec(prim_r2, s));
173
        }
174
        uart_puts_pgm(PSTR("kOhm\r\n"));
175
}
176
 
177
/**
178
 * enable/disable TWI
179
 */
180
void twi_change() {    
181
        uart_puts_pgm(PSTR("\r\nTWI turned "));
182
        if (CSTROM_CONFIG & CSTROM_TWI) {
183
                uart_puts_pgm(PSTR("ON"));
184
        } else {
185
                uart_puts_pgm(PSTR("OFF"));
186
        }
187
        uart_puts_pgm(PSTR(". Please restart...\r\n"));
188
}
189
 
190
 
191
 
192
 
193
/**
194
 * Interrupt handler for received data through UART1
195
 */
196
SIGNAL(SIG_UART_RECV) {
197
        unsigned char c = UDR;
198
        switch (c) {
199
                case 'c':
200
                        ampere_calibrate();
201
                        break;
202
                case 's':
203
                        save_eeprom();
204
                        break;
205
                case '+':
206
                        sensor_change(100);
207
                        break;
208
                case '-':
209
                        sensor_change(50);
210
                        break;
211
                case 'e':
212
                        if (hwver == 11) r2_change(V_ANIN, anin_r2 + 1);
213
                        break;
214
                case 'd':
215
                        if (hwver == 11) r2_change(V_ANIN, anin_r2 - 1);
216
                        break;
217
                case 'r':
218
                        r2_change(V_PRIMARY, prim_r2 + 1);
219
                        break;
220
                case 'f':
221
                        r2_change(V_PRIMARY, prim_r2 - 1);
222
                        break;
223
                case 'T':
224
                        CSTROM_CONFIG ^= CSTROM_TWI;
225
                        twi_change();
226
                        break;
227
                case 'h':
228
                        help(0);
229
                        break;
230
                default:
231
                        asm("nop"); // :-)
232
        }
233
}
234
 
235
/**
236
 * Interrupt handler for transmitting data through UART1
237
 */
238
SIGNAL(SIG_UART_TRANS) {
239
}
240
 
241
/**
242
 * Read out the ADC channel <channel>
243
 */
244
uint16_t readADC(uint8_t channel) {
245
        uint8_t i;
246
        uint16_t result = 0;
247
 
248
        // enable ADC and set clk div to 64
249
        ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);
250
 
251
        _delay_us(5);
252
 
253
        // set up channel
254
        ADMUX = channel;
255
        // use internal reference
256
        //ADMUX |= (1<<REFS1) | (1<<REFS0);
257
 
258
        // init ADC for a dummy readout
259
        ADCSRA |= (1<<ADSC);
260
        // wait for conversion to be complete
261
        while(ADCSRA & (1<<ADSC));
262
 
263
        // read in three times and get the average
264
        for(i=0; i<3; i++) {
265
                // start conversion
266
                ADCSRA |= (1<<ADSC);
267
 
268
                // wait for conversion to be complete
269
                while(ADCSRA & (1<<ADSC));
270
 
271
                // add up result
272
                result += ADCW;
273
        }
274
 
275
        // disable ADC
276
        ADCSRA &= ~(1<<ADEN);
277
 
278
        // get average
279
        result /= 3;
280
 
281
        return result;
282
}
283
 
284
 
285
/**
286
 * init SPI slave interrupt conrolled
287
 */
288
void Init_Slave_IntContr (void) {
289
        volatile char IOReg;
290
        // Set PB4(MISO) as output
291
        DDRB    = (1<<PB4);
292
                // MOSI Pullup
293
                PORTB |= _BV(3);
294
        // Enable SPI Interrupt and SPI in Slave Mode
295
        SPCR  = (1<<SPIE)|(1<<SPE);
296
        IOReg   = SPSR; // Clear SPIF bit in SPSR
297
        IOReg   = SPDR;
298
                SPCR |= _BV(SPIE); // duplicated
299
}
300
 
301
 
302
 
303
/**
304
 * SPI interrupt handling
305
 */
306
ISR(SPI_STC_vect) {
307
        LED_ON();
308
 
309
        unsigned char foo;
310
        foo = SPDR;
311
        //uart_putc(foo);
312
        switch (foo) {
313
                case 'A': // requested ampere high bits for next transmission
314
                        CSTROM_FLAGS |= CSTROM_SPILOCKED;
315
                        foo = SPI_buffer.buffer.c[0];
316
                        break;
317
                case 'B': // requested low bits
318
                        foo = SPI_buffer.buffer.c[1];
319
                        break;
320
                case 'C': // wasted ampere high bits in next
321
                        foo = SPI_buffer.buffer.c[2];
322
                        break;
323
                case 'D': // 2nd highest 8bits
324
                        foo = SPI_buffer.buffer.c[3];
325
                        break;
326
                case 'E': // 3rd highest 8bits
327
                        foo = SPI_buffer.buffer.c[4];
328
                        break;
329
                case 'F': // lowest 8bits
330
                        foo = SPI_buffer.buffer.c[5];
331
                        break;
332
                case 'G': // lowest 8bits
333
                        foo = SPI_buffer.buffer.c[6];
334
                        break;
335
                case 'H': // lowest 8bits
336
                        foo = SPI_buffer.buffer.c[7];
337
                        break;
338
                case 'I': // challange over
339
                        foo = 'd'; // done :)
340
                        CSTROM_FLAGS &= ~CSTROM_SPILOCKED;
341
                        break;
342
                default:  // what else? nothin now
343
                        foo = 'X';
344
        }
345
        // write back foo in next transmission
346
        SPDR = foo;
347
 
348
        //uart_putc(foo);
349
 
350
        LED_OFF();
351
}
352
 
353
/**
354
 * read data saved in eeprom
355
 */
356
void get_eeprom() {
357
        if (eeprom_read_byte(&ee_checkbyte1) == CHECKBYTE1 && eeprom_read_byte(&ee_checkbyte2) == CHECKBYTE2) {
358
                uart_puts("\tLoading data from eeprom...");
359
                sensor = eeprom_read_byte(&ee_sensor);
360
                cal_ampere = eeprom_read_word(&ee_cal_ampere);
361
                anin_r1 = eeprom_read_byte(&ee_anin_r1);
362
                anin_r2 = eeprom_read_byte(&ee_anin_r2);
363
                prim_r1 = eeprom_read_byte(&ee_prim_r1);
364
                prim_r2 = eeprom_read_byte(&ee_prim_r2);
365
                CSTROM_CONFIG = eeprom_read_byte(&ee_config);
366
                uart_puts("done\r\n");
367
        } else {
368
                uart_puts("\tNo data found in eeprom, using default data...\r\n");
369
        }
370
}
371
 
372
/**
373
 * save data to eeprom
374
 */
375
void save_eeprom() {
376
        uart_puts("\r\nSaving data to eeprom...");
377
        eeprom_write_byte(&ee_checkbyte1, CHECKBYTE1);
378
        eeprom_write_byte(&ee_checkbyte2, CHECKBYTE2);
379
        eeprom_write_byte(&ee_sensor, sensor);
380
        eeprom_write_word(&ee_cal_ampere, cal_ampere);
381
        //if (hwver == 11)  {
382
        // why not saving when not needed, there is space
383
                eeprom_write_byte(&ee_anin_r1, anin_r1);
384
                eeprom_write_byte(&ee_anin_r2, anin_r2);               
385
        //}
386
        eeprom_write_byte(&ee_prim_r1, prim_r1);
387
        eeprom_write_byte(&ee_prim_r2, prim_r2);       
388
        eeprom_write_byte(&ee_config, CSTROM_CONFIG);  
389
        uart_puts("done\r\n");
390
}
391
 
392
/**
393
 * calibrate the current sensor... has to be 0A during this time!
394
 */
395
void ampere_calibrate() {
396
        cli();
397
        uart_puts("\r\nCalibrating...");
398
        uint16_t temp_cal = 0;
399
        for (uint8_t i = 0; i < 10; i++) {
400
                temp_cal += readADC(0);
401
                uart_puts("#");
402
                _delay_ms(100);
403
        }
404
        cal_ampere = temp_cal / 10;
405
        uart_puts("done. Offset is now: ");
406
        uart_puts(itoa(cal_ampere, s, 10));
407
        uart_puts("\r\n");
408
        sei();
409
}
410
 
411
 
412
volatile uint16_t timer = 0, cs = 0;
413
/**
414
 * init timer0
415
 */
416
void init_timer0(void){
417
        // set up timer
418
        TCCR0 |= (1 << CS00) | (1 << CS01); // timer0 prescaler 64
419
        TIMSK |= (1 << TOIE0); // enable overflow timer0
420
}
421
 
422
/**
423
 * timer overflow handler, should be 1ms
424
 */
425
SIGNAL(SIG_OVERFLOW0) {
426
        TCNT0 = 131; // preload
427
        timer++;
428
        // this should be 100ms
429
        if (timer == 100) {
430
                timer = 0;
431
                cs++;
432
                average_ampere += ampere;
433
                CSTROM_FLAGS |= CSTROM_WRITEUART;
434
        }
435
        // this should be 1s
436
        if (cs == 10) {
437
                cs = 0;
438
                mah += average_ampere / 360;
439
                average_ampere = 0;
440
        }
441
}
442
 
443
/**
444
 * write <len> through uart spaces
445
 */
446
void write_space(uint8_t len) {
447
        while (len--) {
448
                uart_putc(' ');
449
        }
450
}
451
 
452
 
453
 
454
/**
455
 * check which hardware version we have here
456
 */
457
void check_hw() {
458
        // check if pin was output and has pullup
459
        uint8_t old_DDRD7 =  DDRD & (1 << PD7);
460
        uint8_t old_PORTD7 =  PORTD & (1 << PD7);
461
 
462
        // if it was, make it input
463
        if (old_DDRD7) DDRD &= ~(1 << PD7); // PD7 input (LED)
464
        if (!old_PORTD7) PORTD |= (1 << PD7); // PD7 enable pullup (LED)
465
 
466
 
467
        if (PIND & (1 << PD7)) {
468
                hwver = 11;
469
                LED_ON = PD7_L;
470
                LED_OFF = PD7_H;
471
        }
472
 
473
 
474
        // output again
475
        if (!old_PORTD7) PORTD &= ~(1 << PD7); // PD7 disable pullup (LED)
476
        if (old_DDRD7) DDRD |= (1 << PD7); // PD7 output (LED)
477
}
478
 
479
 
480
/**
481
 * call for help whenever needed
482
 */
483
void help(uint8_t load) {
484
    uart_puts_pgm(PSTR("\r\nC-STROM\r\n\tBUILD: "));
485
        uart_puts_pgm(PSTR(BUILDDATE));
486
        uart_puts("\r\n\tHW: ");
487
        uart_puts(itoa_dec(hwver, s));
488
 
489
        uart_puts("\r\n");
490
 
491
        if (load) get_eeprom();
492
 
493
        uart_puts_pgm(PSTR("\tSensor: "));
494
        uart_puts(itoa(sensor, s, 10));
495
        uart_puts_pgm(PSTR("A\tCalibration: "));
496
        uart_puts(itoa(cal_ampere, s, 10));
497
 
498
        uart_puts_pgm(PSTR("\r\n\tTWI is "));
499
        if (CSTROM_CONFIG & CSTROM_TWI) {
500
                uart_puts_pgm(PSTR("ON, SPI may not work!!!"));
501
        } else {
502
                uart_puts_pgm(PSTR("OFF"));
503
        }
504
 
505
 
506
        uart_puts_pgm(PSTR("\r\n\tPIMARY R2: "));
507
        uart_puts(itoa_dec(prim_r2, s));
508
        if (hwver == 11) {
509
                uart_puts_pgm(PSTR("kOhm"));
510
                uart_puts_pgm(PSTR("\tANIN R2: "));
511
                uart_puts(itoa_dec(anin_r2, s));
512
        }
513
        uart_puts_pgm(PSTR("kOhm\r\n"));
514
 
515
        uart_puts_pgm(PSTR("\tCommands available:\r\n"));
516
        uart_puts_pgm(PSTR("\t\th : help on commands (this)\r\n"));
517
        uart_puts_pgm(PSTR("\t\tc : calibrate ampere\r\n"));
518
        uart_puts_pgm(PSTR("\t\tT : toggle TWI (may break SPI communication!)\r\n"));
519
        uart_puts_pgm(PSTR("\t\t+/- : to change sensor\r\n"));
520
        uart_puts_pgm(PSTR("\t\tr/f : to change PRIMARY-R2 Value\r\n"));
521
        if (hwver == 11) {
522
                uart_puts_pgm(PSTR("\t\te/d : to change ANIN-R2 Value\r\n"));
523
        }
524
        uart_puts_pgm(PSTR("\t\ts : save values\r\n"));
525
        uart_puts_pgm(PSTR("\tnow enjoy it and have fun...\r\n\r\n"));
526
}
527
 
528
 
529
/**
530
 * Main
531
 */
532
int main (void) {
533
        DDRD |= (1 << PD7); // PD7 output (LED)
534
 
535
        check_hw();
536
        uart_init();
537
 
538
        Init_Slave_IntContr();
539
        init_timer0();
540
 
541
        sei();   // Enable Global Interrupts
542
 
543
    uart_puts("\x1B[2J\x1B[H"); // clear serial
544
 
545
        help(1);
546
 
547
        if (CSTROM_CONFIG & CSTROM_TWI) init_twi_slave(CSTROM_I2C);
548
 
549
        int16_t raw_volt = 0, raw_ampere = 0, raw_aninvolt = 0;
550
        char c[10] = "         ";
551
        c[9] = 0;
552
 
553
        //strom_data  = *((SPI_strom_data_t*) &spi_buffer);
554
        //*spi_buffer = *((uint8_t*) (void*) &strom_data);
555
 
556
        LED_ON();
557
 
558
        while (1) { // Loop Forever
559
 
560
                // we have got a normal voltage measuring circuit that takes the lipo-voltage
561
                raw_volt = readADC(1);
562
                /* according to what i read about voltage divider it is
563
                   Uo = Ue * (R1 / (R2 + R1))
564
                   Ue = Uo * (R2 + R1) / R1
565
                   the board has got r1 = 4.7k and r2 = 15k
566
                   but since 1step is 0,0048828125V = 4,8828125mV and not 5mV there
567
                   is some conversion to do for raw_volt --**-> Uo
568
                   this should end up in 10th of volts */
569
                raw_volt = ((uint32_t)raw_volt * (uint32_t)48828) / (uint32_t)10000;
570
                volt = (int16_t) (((uint32_t)raw_volt * (uint32_t)(prim_r1 + prim_r2)) / (uint32_t)prim_r1) / 100;
571
                if (volt < 0) volt = 0;
572
 
573
                // and we have got a seccond voltage measuring circuit for user voltages
574
                raw_aninvolt = readADC(2);
575
                /* some conversion to do for raw_volt --**-> Uo
576
                   this should end up in 10th of volts */
577
                raw_aninvolt = ((uint32_t)raw_aninvolt * (uint32_t)48828) / (uint32_t)10000;
578
                anin_volt = (int16_t) (((uint32_t)raw_aninvolt * (uint32_t)(anin_r1 + anin_r2)) / (uint32_t)anin_r1) / 100;
579
                if (anin_volt < 0) anin_volt = 0;
580
 
581
                raw_ampere = readADC(0);
582
                /* according to datasheet sensitivity is nominal 40mV per A for the 50A chip
583
                   this would mean 50A ^= 2V since 0A is set to 2.5V output Voltage we get
584
                   a range of 0.5V till 4.5V for the full range.
585
                   the atmega ADC features 0...5V range divided into 10bit ^= 1024 steps
586
                   so 0,0048828125V, or 4,8828125mV, is one step
587
                   this leads us to 0,8192 steps per 0,1A and somehow the below formula
588
                   and i know that 32bit is evil, but what else does this device has to do? :)
589
                   this should end up in 100th of ampere */
590
                ampere = (int16_t) (((int32_t)(((int16_t)raw_ampere - (int16_t)cal_ampere)) * (int32_t)10000) / (int32_t) 819);
591
                if (sensor == 100) ampere *= 2;
592
 
593
                if ((CSTROM_FLAGS & CSTROM_WRITEUART)) {
594
                        uart_puts("V: ");
595
                        uart_puts(itoa_dec(volt, s));
596
                        write_space(10-strlen(s));
597
 
598
                        uart_puts("AN-IN V: ");
599
                        uart_puts(itoa_dec(anin_volt, s));
600
                        write_space(10-strlen(s));
601
 
602
                        uart_puts("A: ");
603
                        uart_puts(itoa(ampere, s, 10));
604
                        write_space(10-strlen(s));
605
 
606
                        uart_puts("C: ");
607
                        uart_puts(itoa(mah, s, 10));
608
                        write_space(10-strlen(s));
609
 
610
                        uart_puts("\r");
611
                        CSTROM_FLAGS &= ~CSTROM_WRITEUART;
612
                }
613
 
614
                //spi_buff
615
                if (!(CSTROM_FLAGS & CSTROM_SPILOCKED)) {
616
                        // TESTTING 
617
                        if (!(CSTROM_CONFIG & CSTROM_TWI)) CSTROM_FLAGS |= CSTROM_SPILOCKED;
618
                        SPI_buffer.data.ampere = ampere;
619
                        SPI_buffer.data.mah = mah;
620
                        if (hwver == 11) {
621
                                SPI_buffer.data.volt = anin_volt;
622
                        } else {
623
                                SPI_buffer.data.volt = volt;
624
                        }
625
                }
626
        }
627
   return 0;
628
}