![](/media/cache/group/underwritten_car_negative.png.50x50_q85.png)
System Management code
Dependencies: mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP
Fork of SystemManagement by
Diff: Libs/IMD/IMD.cpp
- Revision:
- 30:91af74a299e1
- Child:
- 36:0afc0fc8f86b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Libs/IMD/IMD.cpp Thu Nov 13 10:53:10 2014 +0000 @@ -0,0 +1,201 @@ +#include "IMD.h" +#include <math.h> + +static IMD* instance[4] = { 0 }; // Access member from static frame, one IMD permitted per timer module (4 total IMD objects) + +const uint32_t PCLK = 24000000; // Timer counting clock = 24Mhz +const uint32_t TIMEOUT_TICKS = PCLK*ZERO_HZ_TIMEOUT; // Zeroing timeout in clock ticks = seconds * PCLK +const float EXTRA = 0.01; // Margin on the IMD PWM limits + +// Interrupt functions, must be static context +void tim0_IRQ() { + if (LPC_TIM0->IR & (1<<4|1<<5)) instance[0]->edgeIRQ(); // Capture pin interrupt + if (LPC_TIM0->IR & 1) instance[0]->zeroIRQ(); // MR0 interrupt + LPC_TIM0->IR=0x3F; // Clear interrupt flags +} +void tim1_IRQ() { + if (LPC_TIM1->IR & (1<<4|1<<5)) instance[1]->edgeIRQ(); + if (LPC_TIM1->IR & 1) instance[1]->zeroIRQ(); + LPC_TIM1->IR=0x3F; // Clear interrupt flags +} +void tim2_IRQ() { + if (LPC_TIM2->IR & (1<<4|1<<5)) instance[2]->edgeIRQ(); + if (LPC_TIM2->IR & 1) instance[2]->zeroIRQ(); + LPC_TIM2->IR=0x3F; // Clear interrupt flags +} +void tim3_IRQ() { + if (LPC_TIM3->IR & (1<<4|1<<5)) instance[3]->edgeIRQ(); + if (LPC_TIM3->IR & 1) instance[3]->zeroIRQ(); + LPC_TIM3->IR=0x3F; // Clear interrupt flags +} + +bool cmpPin(LPC_pin p1, LPC_pin p2) { + if (p1.gpio_addr == p2.gpio_addr && p1.gpio_num == p2.gpio_num) return true; + else return false; +} + +IMD::IMD(LPC_pin _pin) { + // Setup the timer/pin access variables + if (cmpPin(_pin, p1_26)) { // CAP0.0 + timer=0; + pin=0; + timerBase=LPC_TIM0; + } else if (cmpPin(_pin, p1_27)) { // CAP0.1 + timer=0; + pin=1; + timerBase=LPC_TIM0; + } else if (cmpPin(_pin, p1_18)) { // CAP1.0 + timer=1; + pin=0; + timerBase=LPC_TIM1; + } else if (cmpPin(_pin, p1_19)) { // CAP1.1 + timer=1; + pin=1; + timerBase=LPC_TIM1; + } else if (cmpPin(_pin, p0_4)) { // CAP2.0 + timer=2; + pin=0; + timerBase=LPC_TIM2; + } else if (cmpPin(_pin, p0_5)) { // CAP2.1 + timer=2; + pin=1; + timerBase=LPC_TIM2; + } else if (cmpPin(_pin, p0_23)) { // CAP3.0 + timer=3; + pin=0; + timerBase=LPC_TIM3; + } else if (cmpPin(_pin, p0_24)) { // CAP3.1 + timer=3; + pin=1; + timerBase=LPC_TIM3; + } else { // Invalid pin + timerBase=0; + pin=0; + timer=0; + return; + } + + instance[timer] = this; + first = true; + + startTime = 0; + widthTicks = 0; // Zero low, so that duty = 0/1 = 0% + periodTicks = 1; + + // Enable power and set pclk at 24Mhz + if (timer==0) { + LPC_SC->PCONP |= (1<<1); + LPC_SC->PCLKSEL0 &= ~(3<<2); + } if (timer==1) { + LPC_SC->PCONP |= (1<<2); + LPC_SC->PCLKSEL0 &= ~(3<<4); + } if (timer==2) { + LPC_SC->PCONP |= (1<<22); + LPC_SC->PCLKSEL1 &= ~(3<<12); + } if (timer==3) { + LPC_SC->PCONP |= (1<<23); + LPC_SC->PCLKSEL1 &= ~(3<<14); + } + + *(_pin.pinsel_addr) |= 3 << _pin.selmode_num; // Set pin as capture pin + *(_pin.pinmode_addr) |= 3 << _pin.selmode_num; // Pull down + + timerBase->TCR=2; // Stop counter and hold at 0, for configuration + timerBase->IR=0x3F; // Clear any interrupt flags + timerBase->CTCR=0; // Use pclk, not external pin + timerBase->PR=0; // No prescale value, clock at full pclk=24Mhz + timerBase->EMR=0; // Do not use external match pins + + if (timer == 0) NVIC_SetVector(TIMER0_IRQn, (uint32_t)&tim0_IRQ); // Set irq handler for timer0 + if (timer == 1) NVIC_SetVector(TIMER1_IRQn, (uint32_t)&tim1_IRQ); // Set irq handler for timer1 + if (timer == 2) NVIC_SetVector(TIMER2_IRQn, (uint32_t)&tim2_IRQ); // Set irq handler for timer2 + if (timer == 3) NVIC_SetVector(TIMER3_IRQn, (uint32_t)&tim3_IRQ); // Set irq handler for timer3 + + NVIC_EnableIRQ((IRQn_Type)(timer+1)); + NVIC_SetPriority((IRQn_Type)(timer+1), 0); // Highest priority (default) + + timerBase->CCR = pin?5<<3:5; // Generate interrupt on rising edge of capture pin + timerBase->MCR = 1; // Interrupt on MR0 to establish the zero speed timeout + zeroIRQ(); // Zero the values + timerBase->TCR = 1; // Start counting, GO! +} + +void IMD::edgeIRQ() { + bool rising = pin?timerBase->CCR&(1<<3):timerBase->CCR&1; + uint32_t capTime = pin?timerBase->CR1:timerBase->CR0; // Get the time of the capture event + timerBase->MR0 = capTime + TIMEOUT_TICKS; // Move the zero timeout ahead + + // Special case - on first pulse after a timeout or on startup, period cannot be calculated + // so set startTime such that periodTicks remains unchanged from its zero state (periodTicks=1) + if (first) { + first = false; + startTime = capTime - 1; + } + if (rising) { + periodTicks = capTime - startTime; // Get the period on Rising edge + startTime = capTime; // Set the start of the next pulse + } else { + widthTicks = capTime - startTime; // Get the pulse width on Falling edge + } + + // Switch interrupt types to capture the next edge + if (rising) timerBase->CCR = pin?6<<3:6; + else timerBase->CCR = pin?5<<3:5; +} + +void IMD::zeroIRQ() { + periodTicks = 1; + first = true; // Reset the first edge detection case + + bool rising = pin?timerBase->CCR&(1<<3):timerBase->CCR&1; + // Timeout occurred after FALLING edge, RISING edge never happened + if (rising) { + widthTicks = 0; // Signal is low = 0/1 = 0% duty + } else { + widthTicks = 1; // Signal is high = 1/1 = 100% duty + } +} +float IMD::frequency() { + // Handle the case where we want to say 0Hz not infinity Hz + if (periodTicks == 1 || periodTicks == 0) return 0; + else return (float)(PCLK)/(float)(periodTicks); +} +float IMD::duty() { + return (float)(widthTicks)/(float)(periodTicks); +} + +char IMD::status() { + float freq = frequency(); + if (freq == 0) return OFF; // IMD off + else if (05 < freq && freq <= 15) return NORMAL; // 10Hz normal mode + else if (15 < freq && freq <= 25) return UNDERVOLT; // 20Hz undervoltage mode + else if (25 < freq && freq <= 35) return SPEEDSTART; // 30Hz speed start mode + else if (35 < freq && freq <= 45) return ERROR; // 40Hz IMD error + else if (45 < freq && freq <= 55) return GROUNDERR; // 50Hz Ground error + else return INVALID; // Invalid +} + +float IMD::resistance() { + char stat = status(); + float dut = duty(); + + // In normal or undervoltage mode, where Rf is available + if (stat == NORMAL || stat == UNDERVOLT) { + if (0.05-EXTRA <= dut && dut <= 0.95+EXTRA) { + if (dut > 0.95) dut = 0.95; // Ceiling + if (dut < 0.05) dut = 0.05; // Floor so it doesnt mess up the equation below + float rf = (0.9*1200e3/(dut-0.05)) - 1200e3; + if (rf < 1000) rf = 0; // Floor to hit 0 + if (rf > 50e6) rf = 50e6; // Ceil to stay at 50e6 + return rf; + } + else return NAN; + } + // In speed start, where only good/bad estimate is available + if (stat == SPEEDSTART) { + if (0.05-EXTRA <= dut && dut <= 0.10+EXTRA) return 50e6; // Good + else if (0.90-EXTRA <= dut && dut <= 0.95+EXTRA) return 0; // Bad + else return NAN; + } + return NAN; // Measurement not available in this mode +} \ No newline at end of file