root/trunk/HighVoltageControllerIMPROVED.c

Revision 18, 18.9 kB (checked in by mpaulh, 3 years ago)

--

Line 
1 #include <math.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <inttypes.h>
5 #include <avr/eeprom.h>
6 #include <avr/interrupt.h>
7 #include <avr/io.h>
8 #include <avr/pgmspace.h>
9 #include <avr/sleep.h>
10 #include <avr/wdt.h>
11
12 //#define MAX_THROTTLE 511
13 //#define MIN_THROTTLE 0
14
15
16 #define F_OSC 16000000                     /* oscillator-frequency in Hz */
17 #define UART_BAUD_RATE 19200
18 #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)
19
20
21 #define PRE_SCALED_ZERO_THROTTLE 712 // Anything bigger than 989 means 0% throttle.
22 #define PRE_SCALED_MAX_THROTTLE 413 // Anything smaller than 683 means 100% throttle.
23 #define PRE_SCALED_OPEN_CIRCUIT_THROTTLE        150     // Indicates a bad throttle.  Over ?? kOhms.
24 //#define MIN_TEMP 744                          // Start limiting current at 69 degC
25 //#define MAX_TEMP 808                          // Max temperature allowed is 80 degC
26 #define MAX_CURRENT 506
27 #define THERMAL_CUTBACK_START 780       // start at 75 degC, end at 85 degC.
28
29 //#define MAX_THROTTLE
30
31 // FOR CURRENT, using the LEM HASS 300, the 10 bit A/D converter outputs:
32
33 // It makes for a range of 213.
34 // I also want the throttle to have the same range as the current.
35 // Right now, 0 throttle is 0, and max throttle is 511.
36 // I need to map the current onto 0, 511.
37 // 500 amps -----> 725
38 // 0 amps   -----> 512
39
40 // READ CURRENT.
41 // IT'S IN [512, 512+213]
42 // subtract 512
43 // now its in [0, 213]
44 // times 24
45 // divide by 10.
46 // Now it's in the range [0, 511.2]
47
48
49
50 // There are 4 Analog to Digital Voltages.  The input of each will range from 0 to 5v.
51 // After the A/D conversion, the value will be between 0 and 1023.
52 // 0 will mean 0 volts was measured.  1023 will mean that 5v was measured.
53 volatile int16_t throttlePos=0; // the desired current.  in [0, 255].
54 volatile int16_t _throttlePos = 0;
55 volatile int16_t pwmDuty = 0;
56 volatile int16_t temperature = 0;       // temperature measurement. Monitor the MOSFETs with a thermistor.
57 volatile int16_t current = 0;   // How much current is being used on a moment by moment basis.
58 volatile int16_t _current = 0;
59 volatile int16_t tempCurrent = 0;
60
61
62 volatile int16_t currentSum = 0;
63 volatile int16_t aveCurrent = 0;
64 volatile int16_t vRef = 512;                    // vRef is the average measurement of the current sensor at 0 volts.
65 volatile int16_t tempCounter = 0; // counter for temperature, so it will only be checked every couple seconds.
66 volatile int16_t j = 0;
67 volatile int16_t i = 0;
68 volatile int16_t dummy = 0;
69 volatile uint16_t ISRCounter = 0;
70 volatile int16_t innerDelay = 0;
71
72 //volatile int16_t temp = 0;
73
74 volatile int16_t overshoot = 0;
75
76 volatile char relayLow = 1;
77 volatile char cSREG;
78
79 volatile unsigned char onesPlace=0, tensPlace=0, hundredsPlace=0, timeForChrisStuff = 0;
80
81
82
83 void InitUSART(void) {
84         // set baud rate
85         UBRRH = (uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)>>8);
86         UBRRL = (uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);
87
88         // Enable receiver and transmitter; enable RX interrupt
89 //      UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
90         UCSRB = (1 << TXEN);// | (1 << RXCIE);
91
92         //asynchronous 8N1
93         UCSRC = (1 << URSEL) | (3 << UCSZ0);
94 }
95
96 // INTERRUPT can be interrupted
97 // SIGNAL can't be interrupted
98 //SIGNAL (SIG_UART_RECV) { // USART RX interrupt
99 //      unsigned char c;
100 //      c = UDR;
101 //      PutCharUSART(c);
102 //}
103
104 void PutCharUSART(unsigned char c) {
105    // wait until UDR ready
106         while(!(UCSRA & (1 << UDRE)));  // wait until the USART data register is empty.
107         UDR = c;    // send character
108 }
109
110
111 // Initialize the Pulse Width Modulator timer.
112 // Let's use timer 1, the 16 bit timer.  The others are 8 bit.
113 // 1.  Select the bits that you want set in TCCR1A.  See pg. 98 for help with this.
114 // 2.  Set the speed of your pwm by setting the bits you want in TCCR1B.  See pg. 100.
115 // 3.  Enable Pin B1 as the output for the pwm signal that you made.  See pg. 97.
116 // 4.  Initialize the value of the pwm to 0.  (may not be necessary)
117
118 // *TESTED PREVIOUSLY*
119 void InitPWM (void)
120 {
121         // Note:  _BV means the bit value.  So, _BV(COM1A1) is 128, because COM1A1 is bit 7 (2^7) in the TCCR1A register.
122         // Non-inverted mode!
123 //      TCCR1A = _BV(COM1A1) | _BV(WGM10); // Pg. 98. Phase correct PWM Mode, TOP = 511.
124         TCCR1A = 128 + 2;  // set COM1A1 and WGM11
125
126         //TCCR1B &= (128+64+32);        // This turns the binary number 111xxxxx into 11100000
127         //TCCR1B |= _BV(CS10); // pg. 100. Pre-scaler = 1.
128         TCCR1B = 1;  // pg. 100.  Pre-scaler = 1.
129
130         // Enable OC1A (the Output Compare Match A Output Pin) as the output pin of the PWM at pin B1.
131         // DDRB (Port B Data Direction Register) bit 1 must be set to 1 to make Pin B1 act as the
132         //   Output Pin for the PWM.
133         PORTB &= (128 + 64 + 32 + 16 + 8 + 4 +    1); // this sets PB1 (the pwm output) to 0 at startup.  Notice '2' is missing!
134         DDRB = _BV(PINB1); // Enable pin b1 to act as an output.  Pg. 52.
135
136     // OCR1A is the throttle value. 
137         // Set Initial PWM duty value to 0, both high and low bytes.
138     OCR1A = 0;
139
140         TIMSK = _BV(TOIE1);     // make an interrupt happen each time the timer reaches the bottom.
141         sei(); // set global interrupt enable bit.
142 }
143
144 // This initializes the analog to digital converter.
145 // 1.  Enable the converter by setting the ADEN bit in ADCSRA
146 // 2.  Choose the sample speed, by messing with ADPS0, ADPS1, and ADPS2.
147 // 3.  Choose what voltage you want the converter to view as the highest.  The lowest will be 0.
148 //              To do that, you must mess with the REFS bits in the ADMUX register.
149 // 4.  Choose which A/D channel you want to use by messing with the MUX bits in ADMUX register.
150 // 5.  To start a conversion, you must set the ADSC bit in the ADCSRA.  Then you have to wait
151 //      for it to finish.  The result is in the ADC data register, so save it in some variable.
152
153 // *TESTED PREVIOUSLY*
154 void InitADC(void) {   
155         ADCSRA = _BV(ADEN) | _BV(ADPS2);// | _BV(ADPS0);// pg. 208. How fast to sample ADC? 16MHz/16
156
157         ADMUX = _BV(REFS0);     // Use AVcc as the reference voltage for the ADC, pg. 206
158 }
159
160
161 void DoChrisStuff(void) {
162         // toggle relay every second.
163         if (relayLow) {
164                 PORTD &= 0b01111111; // bring pin d7 low
165                 PORTD &= 0b10111111; // Bring pin d6 low
166                 relayLow = 0; // now switch it for next time.
167         }
168         else {
169                 PORTD |= 0b10000000; // bring pin d7 high
170                 PORTD |= 0b01000000; // Bring pin d6 high
171                 relayLow = 1;
172         }
173         // current is in the range of 0 to 512, which is 0 to 500 amps.  Pretend it's 512 amps.
174         // ex:  current = 218 = 
175         // I need to output '2', '1', '8', 0x0D, 0x0A
176
177         onesPlace = (tempCurrent & 15) + 48;  // add 48 to make it a character instead of a number.  See ascii table.
178         tempCurrent = tempCurrent >> 4;  // now we're onto the 10's place.
179         tensPlace = (tempCurrent & 15) + 48;  // add 48 to make it a character instead of a number.  See ascii table.
180         tempCurrent = tempCurrent >> 4;  // now we're onto the 100's place.
181         hundredsPlace = (tempCurrent & 15) + 48;  // add 48 to make it a character instead of a number.  See ascii table.
182
183         PutCharUSART(hundredsPlace);
184         PutCharUSART(tensPlace);
185         PutCharUSART(onesPlace);
186         PutCharUSART(0x0D);     // carriage return, line feed
187         PutCharUSART(0x0A);
188 }
189
190 // This checks the Analog to Digital Converter Throttle input.
191 // 1.  Choose which ADC channel (which pin on the chip) you want to convert the voltage of. Pg. 206.
192 // 2.  Set the ADSC bit to 1.  This starts the conversion.
193 // 3.  Wait for the conversion to finish.  (Do nothing until it's done)
194 // 4.  The result is saved in ADC.  Move that result to a variable for later.
195
196 void ReadThrottle(void) {
197         // Select channel 0.
198
199         ADMUX &= (128+64+32+16);        // = 11110000.  So, this sets MUX to 0000, and leaves the top 4 bits unchanged.
200                                                                 // pg. 206
201
202         // Read throttle position.
203         // Pg. 198.
204
205         ADCSRA |= 64;   // 64  = 01000000.  So, this sets the ADSC Start Conversion Bit.
206         while (ADCSRA & 64);    // Do nothing until the conversion is done.
207         throttlePos = ADC;
208
209 // zero throttle is 989 OR GREATER.  That's a 10% dead zone.
210 // Full throttle is 683
211
212 //      if (throttlePos < PRE_SCALED_OPEN_CIRCUIT_THROTTLE) {   // Bad Throttle!  More than ??kOhms from a 5k pot!
213 //              throttlePos = 0;
214 //              for (i = 0; i < 16; i++) {
215 //                      ADCSRA |= 64;   // 64  = 01000000.  So, this sets the ADSC Start Conversion Bit.
216 //                      while (ADCSRA & 64);    // Do nothing until the conversion is done.
217 //                      dummy = ADC;
218 //                      throttlePos += dummy;
219 //              }
220 //              throttlePos >>= 4; // find the average of the 16 throttle reads, to make sure it wasn't just a bad read.
221 //              if (throttlePos < PRE_SCALED_OPEN_CIRCUIT_THROTTLE) {  // if the average of 16 throttle reads was bad...
222 //                      OCR1A = 0;  // make sure PWM duty is 0.  Kill the throttle.
223 //                      while (1) {
224 //                              wdt_reset();  // get a new dang throttle!  hahaha!
225                                                                 // don't let the watchdog reset the program.
226 //                      }
227 //              }
228 //      }
229
230         if (throttlePos < PRE_SCALED_MAX_THROTTLE) // 683 is max throttle this time.
231                 throttlePos = PRE_SCALED_MAX_THROTTLE; // 683
232
233
234         if (throttlePos > PRE_SCALED_ZERO_THROTTLE) // 989, allowing for 10% dead zone.
235                 throttlePos = PRE_SCALED_ZERO_THROTTLE; // 989.
236
237         throttlePos -= PRE_SCALED_MAX_THROTTLE;
238         // Now throttlePos is in [0, 306].
239         throttlePos = (PRE_SCALED_ZERO_THROTTLE - PRE_SCALED_MAX_THROTTLE) - throttlePos;
240         // Now, 0 throttle is 0, and max throttle is 306.
241         //
242         //throttlePos *= 1.66;
243         // do throttlePos*53/32.
244
245         //throttlePos += (2*throttlePos)/3;
246         throttlePos *= 53;
247         throttlePos >>= 5;      // divide by 32.
248         // now throttlePos is in [0, 507], about identical to current range.
249         // this is important because the PWM range is 0 to 511, where 0 means 0% duty, and 511 means 100% duty.
250 }
251
252 // *TESTED IN SIMULATOR*
253 void ReadCurrent(void) {
254
255         // Select channel 2.
256         ADMUX &= (128+64+32+16);        // = 11110000.  So, this sets MUX to 0000, and leaves the top 4 bits unchanged.
257                                                                 // pg. 206
258         ADMUX+=2;               // This sets MUX to 0002, setting the A/D converter input to PIN 25, the current monitor
259                                         // on the chip.
260
261         // Read current
262         // Pg. 198.
263         ADCSRA |= 64;   // 64  = 01000000.  So, this sets the ADSC Start Conversion Bit.
264         while (ADCSRA & 64);    // Do nothing until the conversion is done.
265         current = ADC;
266
267
268         // Current starts in [512, 512 + 213]  (if it's in 0 to 500 amps for the LEM 300)
269         if (current < vRef) // if slightly below 0, just call it 0.
270                 current = vRef;
271         current -= vRef;
272         // Now current is in the range [0, 213] or so...
273         current *= 19; // (19/8 is almost 2.4)
274         current >>= 3;
275 //      current <<= 2; // now it's in [0, 512] for LEM 500.
276         // Now current is in [0, 506] or so, close to same as throttle range.
277 }
278
279 // *TESTED PREVIOUSLY*
280
281 void ReadTemperature(void) {
282         ADMUX &= (128+64+32+16);        // = 11110000.  So, this sets MUX to 0000, and leaves the top 4 bits unchanged.
283                                                                 // pg. 206
284         ADMUX++;                // This sets MUX to 0001, setting the A/D converter input to PIN 24, the temperature monitor.
285                                         // on the chip.
286
287         // Read temperature.
288         // Pg. 198.
289         ADCSRA |= 64;   // 64  = 01000000.  So, this sets the ADSC Start Conversion Bit.
290         while (ADCSRA & 64);    // Do nothing until the conversion is done.
291         temperature = ADC;
292 }
293
294 // *TESTED PREVIOUSLY*
295 void HighPedalLockout(void) {
296 //      pwmDuty = 0;
297         OCR1A = 0;
298         do {
299                 ReadThrottle();
300                 wdt_reset(); 
301         } while (throttlePos > 0);
302 }
303
304 // At 8 MHz, a 'for' loop of 100 iterations takes 280 microseconds according to the debugging simulator.
305 // To call the function takes 4.6 microseconds.
306 // So, a delay of 21 is about one PWM period.  2 nand gates in series need 1 microsecond to change
307 // their state, so at least a delay of 1 is fine for resetting the flip flop.
308
309 // It is important to have Optimization turned off in Configuration Options.
310 // The optimizations don't let you do a loop for no reason.
311 // *TESTED IN SIMULATOR*
312 // watch out for the watchdog timeout if you use too large of a delay.
313 void Delay(int k) {
314         //volatile int m;
315         int m;
316         for (m = 0; m < k; m++);
317         return;
318 }
319
320 void BigDelay(int k) {
321         int m, n;
322        
323         for (m = 0; m < k; m++) {
324                 for (n = 0; n < 1000; n++);
325         }
326 }
327
328 // This checks for and deals with an over current event!  haha.  If there is an overcurrent event,
329 // it delays, and then clears the overcurrent event by pulsing port b2 low for a moment.
330
331 // *TESTED IN SIMULATOR*
332 // Nothing bad happens as far as I can tell if the interrupt happens at any point in this function.
333 /*
334 void CheckForAndDealWithOverCurrentEvent(void) {
335 // for things configured as inputs, to read the value of the input, you read pinb, not portb!
336 // TO SET VALUES, YOU MUST USE PORTB, NOT PINB!
337
338         // if pin b0 is 1, then an overcurrent event has occurred.  Turn on the mosfet driver once
339         // the current has dropped below MAX_CURRENT, and after a small extra delay.
340
341         if (PINB & 1) {
342 //              do {
343 //                      ReadCurrent();
344                         //wdt_reset();
345 //              } while (current > MAX_CURRENT);
346 //              Delay(50);
347                 // Fran suggests to delay for at least 4 periods.
348 //              Delay(100); // slightly over ?? periods.
349                 // One period is 62.5 us.
350
351
352                 // At this point, portb2 is high.  It was initialized high at the beginning.
353                 // clear the overcurrent event.  Bring port b2 low...
354                 PORTB &= 128 + 64 + 32 + 16 + 8     + 2 + 1; // Set port b2 to 0.
355                 Delay(2);
356                 // Now bring port b2 high again...  This clears the overcurrent event.
357                 PORTB |= 4; // Set port b2 to 1.
358         }
359         // else, everything is fine!  Do nothing!
360         return;
361 }
362 */
363
364
365 ISR (TIMER1_OVF_vect) {
366         ReadCurrent();
367
368         ISRCounter++;
369 //      currentSum += current;
370 //      if (ISRCounter % 32 == 0) {
371 //              _current = currentSum >> 5;  // the average of the last 32 reads!
372                                                                                 // the very first read will be current = 0. but who cares.
373 //              currentSum = 0;  // reset currentSum.
374 //      }
375
376         if (PINB & 1) { // clear overcurrent event.
377                 // At this point, portb2 is high.  It was initialized high at the beginning.
378                 // clear the overcurrent event.  Bring port b2 low...
379                 PORTB &= 128 + 64 + 32 + 16 + 8     + 2 + 1; // Set port b2 to 0.
380                 Delay(2);
381                 // Now bring port b2 high again...  This clears the overcurrent event.
382                 PORTB |= 4; // Set port b2 to 1.
383                 if (pwmDuty)
384                         pwmDuty--;
385         }
386
387         if ((ISRCounter & 16383) == 16383) {
388                 ReadTemperature();
389                 //ISRCounter = 0;
390                 timeForChrisStuff = 1;
391         }
392         //else if ((ISRCounter & 16383) == 16383) {
393                 //DoChrisStuff();
394         //      timeForChrisStuff = 1;
395         //}
396         else if ((ISRCounter & 15) == 15) {
397                 ReadThrottle();
398                 _throttlePos = throttlePos;
399
400
401                 // The temperature is an input from the A/D converter.  It is in the range of 0 to 1023.
402                 // These numbers are specific to the Thermistor Part# B57862S103F40 from Digikey.
403
404                 // 25 degC      327
405                 // 30 degC      377
406                 // 35 degC      429
407                 // 40 degC      480
408                 // 45 degC      537
409                 // 50 degC      580
410                 // 55 degC      626
411                 // 60 degC      670
412                 // 65 degC      710
413                 // 70 degC      746
414                 // 75 degC      779
415                 // 80 degC      808
416                 // 85 degC      834
417                 // 90 degC      857
418                 // 95 degC      877
419                 // 100 degC     895
420                 // This shifts the current limit down based on how hot the controller is.
421                 // If the temperature is about 70 degC, the current limit is 437 amps.
422                 // If the temperature is about 75 degC, the current limit is about 250 amps.
423                 // At around 80 degC, the current limit is 0.
424
425                 if (temperature > THERMAL_CUTBACK_START) {      // start of thermal cutback.
426                         if (temperature < THERMAL_CUTBACK_START+8) // throttlePos = 7/8*throttlePos.
427                                 _throttlePos = (7*throttlePos) >> 3;
428
429                         else if (temperature < THERMAL_CUTBACK_START + 16)      // throttlePos = 6/8*throttlePos;
430                                 _throttlePos = (6*throttlePos) >> 3;           
431
432                         else if (temperature < THERMAL_CUTBACK_START + 24)      // throttlePos = 5/8*throttlePos;
433                                 _throttlePos = (5*throttlePos) >> 3;
434        
435                         else if (temperature < THERMAL_CUTBACK_START + 32)      // throttlePos = 4/8*throttlePos;
436                                 _throttlePos = throttlePos >> 1;               
437        
438                         else if (temperature < THERMAL_CUTBACK_START + 40)      // throttlePos = 3/8*throttlePos;
439                                 _throttlePos = 3*(throttlePos >> 3);
440        
441                         else if (temperature < THERMAL_CUTBACK_START + 48)      // throttlePos = 2/8*throttlePos;
442                                 _throttlePos = throttlePos >> 2;
443
444                         else if (temperature < THERMAL_CUTBACK_START + 56)      // throttlePos = 1/8*throttlePos;
445                                 _throttlePos = throttlePos >> 3;
446
447                         else
448                                 _throttlePos = 0;
449                 }
450                 if (pwmDuty > _throttlePos) {
451                         if (pwmDuty)
452                                 pwmDuty--;
453                 }
454                 else if (_current > _throttlePos) {  // if current > max_current
455                         if (pwmDuty)
456                                 pwmDuty--;
457                 }
458                 else if (pwmDuty < _throttlePos) {
459                         pwmDuty++;
460                 }
461                 OCR1A = pwmDuty;
462         }
463         return;
464 }
465
466 // *TESTED IN SIMULATOR*
467 // Pin b0 is going to be a flag to let the controller know that the hardware shut off the mosfets.
468 // Pin b0 is 0 if the hardware has not shut off the mosfets.  If you read the value of pin b0 is
469 // 1, then a hardware overcurrent shutdown has occurred.  You then have a delay, and clear the
470 // hardware overcurrent event by changing port b2 from 1 to 0, and then back to 1.
471 // First pin b0 needs to be initialized as an input and pin b2 initialized as an output.
472
473 // *TESTED IN SIMULATOR*
474 void InitializeIOPins(void) {
475
476         DDRB |= 4;      // Configure PINB2 as output
477         // The stupid documentation says this next step might be necessary.  See pg. 52.
478         PORTB &= 128 + 64 + 32 + 16 + 8     + 2 + 1; // Set pin b2 to 0.
479
480         // Pulsing pin b2 low for a moment guarantees that the state of the flip flop in the hardware overcurrent
481         // shutdown circuit is known.
482         PORTB |= 4;     // set pin b2 to 1.
483         Delay(5);
484         PORTB &= 128 + 64 + 32 + 16 + 8     + 2 + 1; // Set Pinb2 to 0.
485         Delay(5);
486         PORTB |= 4;     // set it to 1.
487
488         DDRD |= _BV(PD7);  // configure pin d7 for output.  It is a relay that turns on the main contactor.
489         DDRD |= _BV(PD6);  // configure pin d6 for output.  It is the IDLE LED.  Pulse it every second for Chris.
490
491         // Now configure pin b0 as an input pin.  It will be the indicator of when a hardware overcurrent
492         // shutdown has occurred.  Reading a 0 from this pin means there has NOT been an
493         // overcurrent shutdown, so everything is working normally.  Reading a 1 from this pin means
494         // that an overcurrent shutdown has happened.  So, you will have to delay for a bit, and then
495         // output a trip reset at port b2 to turn the mosfet driver back on.
496         
497         // setting DDRB bit 'n' to 0 makes that bit configured for input.
498         DDRB &= 128 + 64 + 32 + 16 + 8 + 4 + 2; // DDRB = XXXXXXX0  Configure PINB0 as input.
499         PORTB |= 1;     // Maybe unnecessary intermediate step.  See page 52.
500         PORTB &= 128 + 64 + 32 + 16 + 8 + 4 + 2; // PORTB = XXXXXXX0 Turn off the pull-up resistor at pin b0.
501         return;
502 }
503
504 int main (void) {
505         InitADC();      // initialize the analog to digital converter
506         InitializeIOPins(); // pin b0 acts as an input, pin b2 acts as an output.
507         InitUSART();
508 //      wdt_enable(WDTO_2S);
509 //      HighPedalLockout();
510
511     InitPWM();  // initialize the pulse width modulator.  Set up the interrupt last!!! idiot!
512         // Some time tests...
513         // with A/D clock = system clock / 64, it takes about 362 microseconds per loop below.
514         // with A/D clock = system clock / 16, it takes about 129 microseconds per loop below.
515         // with A/D clock = system clock / 16, it takes about 28  microseconds per A/D conversion.
516     while (1) { // do this forever     
517                 tempCurrent = current; //
518                 if (timeForChrisStuff) {
519                         DoChrisStuff();
520                         timeForChrisStuff = 0;
521                 }
522 //              wdt_reset();
523         }
524     return (0);
525 }
526
Note: See TracBrowser for help on using the browser.