Project

General

Profile

Statistics
| Revision:

root / branches / autonomous_recharging / code / projects / autonomous_recharging / archs / Charging.c @ 955

History | View | Annotate | Download (14.4 KB)

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/sleep.h>
4
#include "i2c.h"
5

    
6
#define TRICKLE
7

    
8

    
9
//These are in ADC steps; if any values change in the charging
10
//circuitry these will have to be recalculated
11
#ifndef TRICKLE
12
        #define FAST_I 450//660 //660 fries the 5A fuse!!
13
#else
14
        #define FAST_I 250//20//Works => 1.3A
15
#endif
16

    
17
#define TEST_CURR 550
18

    
19
#define MAX_V 1020//990 //7.75V
20

    
21

    
22
#define MAX_T 300
23
#define MIN_T 730
24
//range is 0 to 45 C
25
//cal tests:
26
//room temp - 25
27
//value ~500, varies from battery to battery, but is consistent on one battery
28
//freezer 737
29
//heat gun at a distance 461
30

    
31
//change in one minute
32
#define MAX_DT 30
33
#define MAX_DV 30
34

    
35

    
36
//The following times are in seconds
37
#define MAX_FAST_TIME 5400
38
#define MAX_TRICKLE_TIME 600
39

    
40
//debug pins
41
#define debug_time PA3
42
#define debug_curr PA4
43
#define debug_volt PA5
44
#define debug_temp PA6
45
#define debug_12in PA7
46

    
47
//be sure admux also sets the MUX5 bit which is in ADCSRB
48
#define ADMUX_I
49
#define ADMUX_V
50
#define ADMUX_T
51

    
52
#define ROBOT_TX PB1
53
#define ROBOT_RX PB2
54
#define PWM PB3
55
#define DETECT_12V PB6
56

    
57
#define LED1 PB4 //Green
58
#define LED2 PB5 //Red
59

    
60

    
61
//LED States:
62
//Red - Fast Charging
63
//Green - Trickle Charging
64
//Both steady - done charging
65
//Both Blinking - Error
66

    
67
#define INT_COUNT 2 //interrupts per second
68
#define AVG_COUNT 64 //number of times to count current
69

    
70
//To enable the PWM write :         TCCR1B = (_Bv(CS10));//enable PWM
71

    
72
uint8_t interrupt_count = INT_COUNT;
73

    
74
volatile uint32_t abs_time=1; // start at one second so it doesnt do the minute checks right away
75
volatile uint8_t new_second=0; //only used as a boolean
76

    
77
volatile uint8_t error=0;
78
volatile uint8_t status;
79

    
80
volatile uint8_t steady_current = 0;
81

    
82
//DT must be triggered twice in a row
83
volatile uint8_t last_DT = 0;
84
//same for DV
85
volatile uint8_t last_DV = 0;
86

    
87
uint16_t tot_err;
88
uint8_t last_neg=0;
89

    
90
#define FAST_CHARGE 1
91
#define TRICKLE_CHARGE 2
92

    
93

    
94

    
95
void wait(int ops)
96
{
97
        int i = 0;
98
        while(i<ops)
99
                i++;
100
}
101

    
102

    
103

    
104
int avg_ADC(void)
105
{
106
        int av;
107
        char i;
108

    
109
        //Calculate a average out of the next 8 A/D conversions
110
    for(av=0,i=8;i;--i)
111
    {
112
        ADCSRA |= _BV(ADSC);                      // start new A/D conversion
113
        while (!(ADCSRA & (_BV(ADIF))))        // wait until ADC is ready
114
            ;      
115
        av = av+ADC;
116
    }
117
    av = av/8;
118
        
119
        //ADCSRA &= ~_BV(ADEN);
120

    
121
        return av;
122
        
123
}
124

    
125
int get_voltage(void)
126
{
127
        ADMUX = _BV(MUX0);
128
        
129
        ADCSRB &= ~_BV(MUX5);
130
        
131
        return avg_ADC();
132
}
133

    
134
int get_current(void)
135
{
136
        ADMUX = _BV(MUX1);
137
        
138
        ADCSRB |= _BV(MUX5);
139
        
140
        return avg_ADC();
141
}
142

    
143
int get_temperature(void)
144
{
145
        ADMUX = _BV(MUX1);
146
        
147
        ADCSRB &= ~_BV(MUX5);
148
        
149
        return avg_ADC();
150
}
151

    
152
int get_avg_voltage(void)
153
{
154
        int count=0;
155
        uint32_t sum=0;
156
                
157
                //OCR1B =120;
158
        while(count < AVG_COUNT)
159
        {
160
                sum += get_voltage();
161
                count++;
162
        }
163

    
164
        return sum/AVG_COUNT;
165
}
166

    
167
int get_avg_current(void)
168
{
169
        int count=0;
170
        uint32_t sum=0;
171
                
172
                //OCR1B =120;
173
        while(count < AVG_COUNT)
174
        {
175
                sum += get_current();
176
                count++;
177
        }
178

    
179
        return sum/AVG_COUNT;
180
}
181

    
182
int get_avg_temperature(void)
183
{
184
        int count=0;
185
        uint32_t sum=0;
186
                
187
                //OCR1B =120;
188
        while(count < AVG_COUNT)
189
        {
190
                sum += get_temperature();
191
                count++;
192
        }
193

    
194
        return sum/AVG_COUNT;
195
}
196

    
197

    
198
uint8_t supply_voltage(void)
199
{
200
        return PINB & _BV(DETECT_12V);
201
}
202

    
203
void clear_err(void)
204
{
205
        error=0;
206
        PORTB &= ~(_BV(LED1)|_BV(LED2));
207
        
208
        if(status==FAST_CHARGE)
209
                PORTB |= _BV(LED2);
210
                
211
        if(status==TRICKLE_CHARGE)
212
                PORTB |= _BV(LED1);
213
}
214
        
215
void wait_8th(void)
216
{
217
        uint8_t start = abs_time % 8;
218
        
219
        while(abs_time % 8 == start)
220
        {
221
                /*if(supply_voltage())
222
                        PORTB |= _BV(LED1);
223
                else
224
                        PORTB &= ~_BV(LED1);
225
                if(get_voltage()>100)
226
                        PORTB |= _BV(LED2);
227
                else
228
                        PORTB &= ~_BV(LED2);*/
229
        }
230
}
231

    
232
void send_err(void)
233
{
234
        OCR1B=0;//turn off the PWM to be safe
235

    
236
        PORTB &= ~(_BV(LED1)|_BV(LED2));
237
        if(status!=0)//leave last error if there was one
238
                PORTA &= ~(_BV(debug_time)|_BV(debug_curr)|_BV(debug_volt)|_BV(debug_temp)|_BV(debug_12in)); 
239
        error=1;
240
        status=0;
241
}
242

    
243
void send_done(void)
244
{
245
        char tempData;
246
        //Finished, leave
247
        tempData = 'F';
248
        i2c_putpacket(0x01, &tempData, 1);
249
        
250
        PORTA &= ~(_BV(debug_time)|_BV(debug_curr)|_BV(debug_volt)|_BV(debug_temp)|_BV(debug_12in)); 
251
        
252
}
253

    
254
void setup(void)
255
{
256
        DDRA = (_BV(debug_time)|_BV(debug_curr)|_BV(debug_volt)|_BV(debug_temp)|_BV(debug_12in));
257
        PORTA = 0x00;
258
        DDRB = (_BV(ROBOT_TX)|_BV(PWM)|_BV(LED1)|_BV(LED2)); //confiure output pins
259
        PORTB = 0x00;
260
        
261
        ADCSRA = (_BV(ADEN)|_BV(ADPS2)|_BV(ADPS1)); //start ADC with a division factor of 64
262
        
263
        TCCR0B = (_BV(CS01)); //set timer 0 for realtime mode
264
        TCCR0A = (_BV(TCW0));
265
        TIMSK = (_BV(TOIE0)); //enable overflow interrupts
266
        
267
        TCCR1A = (_BV(COM1B1)|_BV(PWM1B)|_BV(COM1A1)|_BV(PWM1A)); //clear timer 1 on compare, set at 0x00. Fast PWM mode
268
        TCCR1B |= _BV(CS12)|_BV(CS10); //leave timer on and set compare to 0 to make output off        
269
        OCR1B = 0;
270
        OCR1A = 0;
271
    
272
    error = 0;
273
        
274
        sei();
275
}
276

    
277
int regulate_current(int i)
278
{
279
        if(status==0)
280
        {
281
                OCR1B = 0;
282
                return 0;
283
        }
284
                
285
        PORTA ^= _BV(debug_curr);
286
        
287
        int curr=0;
288
        int count=0;
289
        int diff=0;
290
    int newcurr=OCR1B;
291
        uint32_t sum=0;
292
                
293
                //OCR1B =120;
294
        while(count < AVG_COUNT)
295
        {
296
                sum += get_current();
297
                count++;
298
        }
299

    
300
        curr = sum/AVG_COUNT;
301
        //OCR1A = curr >> 2;
302
        
303
        
304
         //old linear regulation
305
         /*
306
        if(OCR1B <255 && curr < i)
307
                        OCR1B++;
308
        else if(OCR1B >0 && curr > i)
309
                        OCR1B--;
310
        
311
        return curr;*/
312
        
313
        
314
        
315
        //cool control law stuff
316
        
317
        //P:
318
        diff=i-curr;
319
        
320
        //OCR1B = diff/4 + curr/4; //K*Ierr + Kguess
321
    newcurr += diff/4;
322
    
323
        
324
        //I:
325
        tot_err += diff;
326
        
327
        newcurr += tot_err/2;
328
        
329
        //sum:
330
        
331
        if(newcurr>255){
332
        OCR1B = 255;
333
                last_neg=0;
334
    }else if(newcurr>0){
335
        OCR1B = newcurr;
336
                last_neg=0;
337
    }else { //negative
338
                if((++last_neg)==3)
339
                        OCR1B = 1;
340
                //else, no change to OCR
341
                        
342
    }
343
        
344
        
345
        
346
        //1024/255
347
        /*if(OCR1B <255 && curr<i)
348
                OCR1B += (i-curr)/4;
349
        else if(OCR1B >0 && curr > i)
350
                OCR1B -= (curr-i)/4;*/
351
        //differential regulation (sort of)
352
        /*diff = i-curr;
353
        
354
        if(diff<8)
355
                steady_current=1;
356
        else
357
                steady_current=0;
358
        
359
        OCR1B += diff/4;*/
360
    
361
    return curr;
362
        
363
}
364

    
365
void check_voltage(void)
366
{
367
        int volt = get_avg_voltage();
368
        
369
        
370
        if(volt > MAX_V)
371
        {
372
                OCR1A = 128;
373
                send_err();
374
                PORTA |= _BV(debug_volt);
375
                status = 0;
376
        }
377
}
378

    
379
void check_temperature(void)
380
{
381
        int temp = get_avg_temperature();
382
        
383
        //temp readings are reversed
384
        if(temp < MAX_T || temp > MIN_T)
385
        {
386
                OCR1A = 192;
387
                send_err();
388
                PORTA |= _BV(debug_temp);
389
                status=0;
390
        }
391
}
392

    
393

    
394
//takes a 7-bit ionteger and displays it on the 7 LEDs with the Green being the MSB
395
void LED_out(int i)
396
{
397
        if(i & 64)
398
                PORTB |= _BV(LED1);
399
        else
400
                PORTB &= ~_BV(LED1);
401
                
402
        if(i & 32)
403
                PORTB |= _BV(LED2);
404
        else
405
                PORTB &= ~_BV(LED2);
406
                
407
        if(i & 16)
408
                PORTA |= _BV(PA3);
409
        else
410
                PORTA &= ~_BV(PA3);
411
        
412
        if(i & 8)
413
                PORTA |= _BV(PA4);
414
        else
415
                PORTA &= ~_BV(PA4);
416
                
417
        if(i & 4)
418
                PORTA |= _BV(PA5);
419
        else
420
                PORTA &= ~_BV(PA5);
421
        
422
        if(i & 2)
423
                PORTA |= _BV(PA6);
424
        else
425
                PORTA &= ~_BV(PA6);
426
                
427
        if(i & 1)
428
                PORTA |= _BV(PA7);
429
        else
430
                PORTA &= ~_BV(PA7);
431
}
432

    
433

    
434

    
435
void test_board(void)
436
{
437
        setup();
438
        abs_time = 0;
439
        status = 1;
440
        char tempData[5];
441
        i2c_init();
442
        int volt;
443
        int temp;
444
        int curr;
445
        int meas_count;
446
        int mod=0;
447
        sei();
448
        OCR1B = 0;
449
        while(1)
450
        {
451
                mod=abs_time%4;
452
                while(abs_time%4==mod);
453
                
454
                /*if((abs_time>>3)%3==0)
455
                        OCR1B=21;
456
                else if((abs_time>>3)%3==1)
457
                        OCR1B=57;
458
                else
459
                        OCR1B=85;*/
460
                
461
                tempData[0] = 'C';
462
                tempData[1] = abs_time>>8;
463
                tempData[2] = abs_time&0xFF;
464
                i2c_putpacket(0x01, tempData, 3);
465
                
466
                mod=abs_time%4;
467
                while(abs_time%4==mod);
468
                //{
469
                        if(supply_voltage())
470
                        {
471
                                curr = regulate_current(FAST_I);
472
                                //curr = get_avg_current();
473
                                //OCR1B = 100;
474
                        }
475
                        else
476
                        {
477
                                curr = 0;
478
                                OCR1B = 0;
479
                        }
480
                //}
481
                
482

    
483
                tempData[0] = 'P';
484
                tempData[1] = 0;
485
                tempData[2] = OCR1B;
486
                i2c_putpacket(0x01, tempData, 3);
487
                tempData[0] = 'I';
488
                tempData[1] = curr>>8;
489
                tempData[2] = curr&0xFF;
490
                i2c_putpacket(0x01, tempData, 3);
491
                curr=6666;
492
                
493
                mod=abs_time%4;
494
                while(abs_time%4==mod);
495
                //{
496
                        volt = get_avg_voltage();
497
                //}
498

    
499
                tempData[0] = 'V';
500
                tempData[1] = volt>>8;
501
                tempData[2] = volt&0xFF;
502
                i2c_putpacket(0x01, tempData, 3);
503

    
504
                volt=6666;
505
                
506
                mod=abs_time%4;
507
                while(abs_time%4==mod);
508
                //{
509
                        temp = get_avg_temperature();
510
                        
511
                //}
512
                
513
                tempData[0] = 'T';
514
                tempData[1] = temp>>8;
515
                tempData[2] = temp&0xFF;
516
                i2c_putpacket(0x01, tempData, 3);
517

    
518
                temp=6666;
519
        }
520
        int c=0,oc;
521
        oc=get_avg_temperature();
522
        //this will read the temperature and output it to the LEDS.
523
        //to read the value, enter the LEDs as binary, with the bottom green as the MSB.
524
        //after two second the LEDS will toggle to the next 7 bits
525
        /*while(1)
526
        {
527
                c=oc-get_avg_temperature();
528
                oc+=c;
529
                
530
                if(c<0)
531
                        c=-c;
532
                
533
                if(c<5)
534
                        break;
535
        }*/
536
/*
537
        while(1)
538
        {
539
                LED_out(oc>>7);
540
                for(c=0;c<16;c++)
541
                        wait_8th();
542
                LED_out(oc);
543
                for(c=0;c<16;c++)
544
                        wait_8th();
545
        }*/
546
        
547
        
548
        /*PORTB |= (_BV(LED1)|_BV(LED2));
549
        
550
        while(1)
551
        {
552
                PORTA ^= _BV(debug_time);
553
                wait_8th();
554
                c=get_current()-FAST_I;
555
                if(c<0)
556
                        c=-c;
557
                if(c<4)
558
                        PORTA |= _BV(debug_curr);
559
                else
560
                        PORTA &= ~_BV(debug_curr);
561
                wait_8th();
562
                if(get_avg_voltage() > 50)
563
                        PORTA |= _BV(debug_volt);
564
                else
565
                        PORTA &= ~_BV(debug_volt);
566
                wait_8th();
567
                if(get_avg_temperature() > MAX_T)
568
                        PORTA |= _BV(debug_temp);
569
                else
570
                        PORTA &= ~_BV(debug_temp);
571
                wait_8th();
572
                if(supply_voltage())
573
                        PORTA |= _BV(debug_12in);
574
                else
575
                        PORTA &= ~_BV(debug_12in);
576
                wait_8th();
577
        }*/
578
        
579
}
580

    
581

    
582
int main(void)
583
{
584
        tot_err=0;
585
        test_board();
586
        new_second=0;
587
        char tempData[5];        //For i2c communication
588
        //test_board();
589
        char noVoltagePrintFlag = 0;
590
  
591
        setup();
592
        i2c_init();
593
        
594
        /*GIMSK = (_BV(PCIE0)); //enable PCINT interrupts
595
        PCMSK1 = (_BV(PCINT10)); //enable pin change interrupt on ROBOT_RX
596
        MCUCR = (_BV(SE)|_BV(SM1));// (power-down mode)
597
        */
598
        
599
        OCR1B=0;
600
        
601
        sei();
602
        
603
        tempData[0] = 'S';
604
        tempData[1] = 'S';
605
        tempData[2] = 'S';
606
        tempData[3] = 'S';
607
        tempData[4] = 'S';
608
        i2c_putpacket(0x01, tempData, 5);        
609
        
610
        int temp=0;
611
        int last_temp = get_avg_temperature();
612
        
613
        int volt=0;
614
        int last_volt = get_avg_voltage();
615
        
616
        
617
        while(1)
618
        {
619
                /*GIMSK = (_BV(PCIE0)); //enable PCINT interrupts
620
                sleep_cpu();*/
621
                
622
                PORTB=0;//clear outputs
623
                
624
                GIMSK = 0;
625
                
626
                error=0;
627
                
628
                OCR1B = 0;
629
                
630
                //wait for 12v source
631
                
632
                while(!supply_voltage()) {
633
                        if (!noVoltagePrintFlag) {
634
                                tempData[0] = 'N';
635
                                tempData[1] = 'N';
636
                                tempData[2] = 'N';
637
                                tempData[3] = 'N';
638
                                tempData[4] = 'N';
639
                                i2c_putpacket(0x01, tempData, 5);
640
                                noVoltagePrintFlag = 1;
641
                        }
642
                }
643
                
644
                noVoltagePrintFlag = 0;
645
                
646
                //Contact 
647
                tempData[0] = 'C';
648
                tempData[1] = 'C';
649
                tempData[2] = 'C';
650
                tempData[3] = 'C';
651
                tempData[4] = 'C';
652
                i2c_putpacket(0x01, tempData, 5);
653
                
654
                PORTA |= _BV(debug_12in);
655
                
656
                abs_time=1;
657

    
658
                //--------------FAST CHARGE-----------------------
659
                //split seconds into eights as following:
660
                //1: abs_volt
661
                //2: reg
662
                //3: abs_temp
663
                //4: reg
664
                //5: abs_volt
665
                //6: reg
666
                //7: abs_temp
667
                //between seconds: abs time and minute checks, which takes an extra 8th
668
                status = FAST_CHARGE;
669
                
670
                PORTB |= _BV(LED2);
671
                
672
                while(status == FAST_CHARGE)
673
                {
674
                        
675
                        if(!supply_voltage())
676
                        {
677
                                
678
                                
679
                                send_err();
680
                                //Lost Contact
681
                                tempData[0] = 'N';
682
                                tempData[1] = 'N';
683
                                tempData[2] = 'N';
684
                                tempData[3] = 'N';
685
                                tempData[4] = 'N';
686
                                i2c_putpacket(0x01, tempData, 5);
687
                                
688
                                PORTA &= ~_BV(debug_12in);
689
                                while(!supply_voltage());
690
                                
691
                                //Contact again
692
                                tempData[0] = 'C';
693
                                tempData[1] = 'C';
694
                                tempData[2] = 'C';
695
                                tempData[3] = 'C';
696
                                tempData[4] = 'C';
697
                                i2c_putpacket(0x01, tempData, 5);
698
                                
699
                                PORTA |= _BV(debug_12in);
700
                                clear_err();
701
                        }
702
                        
703
                        check_voltage();
704
                        wait_8th();
705
                        regulate_current(FAST_I);
706
                        wait_8th();
707
                        check_temperature();
708
                        wait_8th();
709
                        regulate_current(FAST_I);
710
                        wait_8th();
711
                        check_voltage();
712
                        wait_8th();
713
                        regulate_current(FAST_I);
714
                        wait_8th();
715
                        check_temperature();
716
                        wait_8th();
717
                        
718
#ifdef TRICKLE
719
                        
720
                        if(abs_time > 9600) //90 minute time limit
721
                        {
722
                                OCR1A = 32;
723
                                send_done();
724
                                PORTA |= _BV(debug_time);
725
                                break;
726
                        }
727

    
728
#else
729

    
730
                        if(abs_time > 43200) //90 minute time limit
731
                        {
732
                                OCR1A = 32;
733
                                send_err();
734
                                PORTA |= _BV(debug_time);
735
                                break;
736
                        }
737
                        
738
                        //minute checks
739
                        if( (abs_time >> 3)%55 == 0/* && steady_current*/)
740
                        {
741
                                PORTA ^= _BV(debug_time);
742
                                
743
                                temp = get_avg_temperature();
744
                                wait_8th(); //to avoid interference
745
                                volt = get_avg_voltage(); 
746
                                
747
                                OCR1A = volt >> 2;
748
                                
749
                                //seems to be some random drops when connected to a power supply
750
                                //may need a fudge factor but maybe not becuase voltage should be climbing during charge
751
                                if(volt < last_volt - MAX_DV)
752
                                {
753
          PORTA |= _BV(debug_volt);
754
          if(last_DV)
755
          {
756
            OCR1A = 64;
757
            
758
            send_done();
759
            PORTA |= _BV(debug_volt);
760
            status = TRICKLE_CHARGE;
761
          }
762
          else
763
            last_DV=1;
764
          
765
                                }
766
        else
767
        {
768
          last_DV=0;
769
          PORTA &= ~_BV(debug_volt);
770
         }
771
                                
772
                                
773
                                if(temp < last_temp - MAX_DT)
774
                                {
775
          PORTA |= _BV(debug_temp);
776
          if(last_DT)
777
          {       
778
            OCR1A = 128;
779
            
780
            send_done();
781
            PORTA |= _BV(debug_temp);
782
            status = TRICKLE_CHARGE;
783
          }
784
          else
785
            last_DT=1;
786
                                }
787
        else
788
        {
789
          last_DT=0;
790
          PORTA &= ~_BV(debug_temp);
791
        }
792
                                
793
                                last_volt = volt;
794
                                last_temp = temp;
795
                        }
796
#endif
797

    
798
                }
799
                if (error) {
800
                        //We have an error...let's bail out of this ship captain!
801
                        tempData[0] = 'F';
802
                        tempData[1] = 'F';
803
                        tempData[2] = 'F';
804
                        tempData[3] = 'F';
805
                        tempData[4] = 'F';
806
                        i2c_putpacket(0x01, tempData, 5);
807
                }
808
                
809
                //PORTB ^= _BV(LED2);
810
                
811
        }
812

    
813
        
814
        return 1;
815
}
816

    
817
ISR(TIMER0_OVF_vect)
818
{
819
        if(error)
820
                PORTB ^= (_BV(LED1)|_BV(LED2));
821

    
822
        interrupt_count--;
823
        if(interrupt_count==0)
824
        {
825
                abs_time++;
826
                new_second=1;
827
                        
828
                interrupt_count=INT_COUNT;
829
        }
830
}
831

    
832
ISR(PCINT_vect){;} //so the interrupt doesnt go to the reset vector