A concept for how to generate 2 synchronous PWM signals with independent duty cycle, and a synchronous interrupt for trigger ADC conversion. This is targeting PWM-ADC-BEMF velocity control of a brushed DC motor. Due to limitations in the routing of the mbed board, and the design of the peripherals in the LPC17xx MCU, the PWM's are synthesized with a combination of timer matches and software handling to shape the signal. Code is untested for functionality. Testing with a scope for correct function is critical.
main.cpp
- Committer:
- apullin
- Date:
- 2016-02-24
- Revision:
- 0:eeb86d8390aa
File content as of revision 0:eeb86d8390aa:
#include "mbed.h" void SetupTimer0(void); void Timer0_IRQHandler(void); void SetupTimer1(void); void Timer1_IRQHandler(void); void StartTimers(void); DigitalOut PWMA(LED1); DigitalOut PWMB(LED2); DigitalOut PWMC(LED3); DigitalOut PWMD(LED4); int main() { uint32_t temp = LPC_SC->PCLKSEL0; temp = temp & ~(1 << 2); //clear PCLK_TIMER0 bits temp = temp | (0b01 << 2); // PCLK_TIMER0 = 0b01 = CCLK temp = temp & ~(1 << 4); //clear PCLK_TIMER1 bits temp = temp | (0b01 << 4); // PCLK_TIMER1 = 0b01 = CCLK //SetupTimerPCLK(); //TODO SetupTimer0(); SetupTimer1(); //SetupADC(); //TODO StartTimers(); while(1) { // myled2 = 1; // wait(0.2); // myled2 = 0; // wait(0.2); } //Timer0 -> Limit = PWM period // MR0 = DCA (software set) // MR1 = DCB (software set) // MR2 = Software ADC trigger //Timer1 -> Limit = PWM period (same as Timer0.Limit) // MR0 = DCC (software set) // MR1 = DCD (software set) } //Pseudocode: /* Timer0 ISR{ if ISR source is LIMIT: if MR0 = 0: DCA to LOW else: : DCA to HIGH if MR1 = 0: DCB to LOW else: : DCB to HIGH if ISR source is MR0: DCA to LOW if ISR source is MR1: DCB to LOW if ISR source is MR2: Start ADC sample & convert clear Timer0 IF } Timer1 ISR{ if ISR source is LIMIT: if MR0 = 0: DCC to LOW else: : DCC to HIGH if MR1 = 0: DCD to LOW else: : DCD to HIGH if ISR source is MR0: DCC to LOW if ISR source is MR1: DCD to LOW clear Timer1 IF } ADC ISR{ if ISR source is conversion done: copy data from ADC buffer to ZOH variable clear ADC IF } */ void SetupTimer1(void) { LPC_SC->PCONP |= 1 << 2; // Power on Timer` LPC_TIM1->TCR = 0x2; // Reset and set to timer mode LPC_TIM1->CTCR = 0x0; LPC_TIM1->PR = 0; // No prescale LPC_TIM1->MR0 = 3000; // PWM period ; TODO: check on scope LPC_TIM1->MR1 = 0; //DCC LPC_TIM1->MR2 = 0; //DCD LPC_TIM1->MCR = (1<<0) //MR0 interrupt - PWM end of period & (1<<1) //MR0 resets timer - PWM period & (1<<3) //MR1 interrupt - PWM DCC falling edge & (1<<6); //MR2 interrupt - PWM DCD falling edge // Enable the ISR vector NVIC_SetVector (TIMER1_IRQn, (uint32_t)&Timer1_IRQHandler); NVIC_EnableIRQ(TIMER1_IRQn); } void SetupTimer0(void) { LPC_SC-> PCONP |= 1 << 1; // Power on Timer0 LPC_TIM0->TCR = 0x2; // Reset and set to timer mode LPC_TIM0->CTCR = 0x0; LPC_TIM0->PR = 0; // No prescale LPC_TIM1->MR0 = 3000; // PWM period LPC_TIM1->MR3 = 2950; //ADC trigger point ; TODO: check on scope LPC_TIM1->MR1 = 0; //DCA LPC_TIM1->MR2 = 0; //DCB LPC_TIM0->MCR = (1<<0) //MR0 interrupt - PWM end of period & (1<<1) //MR0 resets timer - PWM period & (1<<3) //MR1 interrupt - PWM DCA falling edge & (1<<6); //MR2 interrupt - PWM DCB falling edge //& (1<<9); //MR3 interrupt - PWM ADC trigger - doesn't use interrupt; TODO: check on scope with INT enabled // Enable the ISR vector NVIC_SetVector (TIMER0_IRQn, (uint32_t)&Timer0_IRQHandler); NVIC_EnableIRQ(TIMER0_IRQn); } void StartTimers(void){ LPC_TIM0->TCR = 1; // Enable Timer0 LPC_TIM1->TCR = 1; // Enable Timer0 } #define TIM0_IRQ_MR0 (1<<0) #define TIM0_IRQ_MR1 (1<<1) #define TIM0_IRQ_MR2 (1<<2) #define TIM0_IRQ_MR3 (1<<3) #define TIM1_IRQ_MR0 (1<<0) #define TIM1_IRQ_MR1 (1<<1) #define TIM1_IRQ_MR2 (1<<2) #define TIM1_IRQ_MR3 (1<<3) void Timer0_IRQHandler(void) { //uint32_t ir = LPC_TIM0->IR; if(LPC_TIM0->IR & TIM0_IRQ_MR0){ //PWM period end/start if(LPC_TIM0->MR1 > 0){ PWMA = 1; } else{ PWMA = 0; } if(LPC_TIM0->MR2 > 0){ PWMB = 1; } else{ PWMB = 0; } LPC_TIM0->IR &= ~(1 << 0); //clear MR0 interrupt flag } if(LPC_TIM0->IR & TIM0_IRQ_MR1){ //PWMA expire PWMA = 0; LPC_TIM0->IR &= ~(1 << 1); //clear MR1 interrupt flag } if(LPC_TIM0->IR & TIM0_IRQ_MR2){ //PWMB expire PWMB = 0; LPC_TIM0->IR &= ~(1 << 2); //clear MR2 interrupt flag } //ADC triggering should be done automatically by the peripherals, when MAT0.3 is hit. //This interrupt is not used, except for debugging and testing. //if(LPC_TIM0->IR & TIM0_IRQ_MR2){ //ADC trigger // //Software ADC start here // LPC_TIM0->IR &= ~(1 << 3); //clear MR3 interrupt flag //} } void Timer1_IRQHandler(void) { if(LPC_TIM1->IR & TIM1_IRQ_MR0){ //PWM period end/start if(LPC_TIM1->MR1 > 0){ PWMC = 1; } else{ PWMC = 0; } if(LPC_TIM1->MR2 > 0){ PWMD = 1; } else{ PWMD = 0; } LPC_TIM1->IR &= ~(1 << 0); //clear MR0 interrupt flag } if(LPC_TIM1->IR & TIM1_IRQ_MR1){ //PWMA expire PWMC = 0; LPC_TIM1->IR &= ~(1 << 1); //clear MR1 interrupt flag } if(LPC_TIM1->IR & TIM1_IRQ_MR2){ //PWMB expire PWMD = 0; LPC_TIM1->IR &= ~(1 << 2); //clear MR2 interrupt flag } }