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
}