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.
