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@0:eeb86d8390aa, 2016-02-24 (annotated)
- Committer:
- apullin
- Date:
- Wed Feb 24 09:26:33 2016 +0000
- Revision:
- 0:eeb86d8390aa
Initial commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
apullin | 0:eeb86d8390aa | 1 | #include "mbed.h" |
apullin | 0:eeb86d8390aa | 2 | |
apullin | 0:eeb86d8390aa | 3 | void SetupTimer0(void); |
apullin | 0:eeb86d8390aa | 4 | void Timer0_IRQHandler(void); |
apullin | 0:eeb86d8390aa | 5 | void SetupTimer1(void); |
apullin | 0:eeb86d8390aa | 6 | void Timer1_IRQHandler(void); |
apullin | 0:eeb86d8390aa | 7 | void StartTimers(void); |
apullin | 0:eeb86d8390aa | 8 | |
apullin | 0:eeb86d8390aa | 9 | DigitalOut PWMA(LED1); |
apullin | 0:eeb86d8390aa | 10 | DigitalOut PWMB(LED2); |
apullin | 0:eeb86d8390aa | 11 | DigitalOut PWMC(LED3); |
apullin | 0:eeb86d8390aa | 12 | DigitalOut PWMD(LED4); |
apullin | 0:eeb86d8390aa | 13 | |
apullin | 0:eeb86d8390aa | 14 | int main() { |
apullin | 0:eeb86d8390aa | 15 | |
apullin | 0:eeb86d8390aa | 16 | uint32_t temp = LPC_SC->PCLKSEL0; |
apullin | 0:eeb86d8390aa | 17 | temp = temp & ~(1 << 2); //clear PCLK_TIMER0 bits |
apullin | 0:eeb86d8390aa | 18 | temp = temp | (0b01 << 2); // PCLK_TIMER0 = 0b01 = CCLK |
apullin | 0:eeb86d8390aa | 19 | temp = temp & ~(1 << 4); //clear PCLK_TIMER1 bits |
apullin | 0:eeb86d8390aa | 20 | temp = temp | (0b01 << 4); // PCLK_TIMER1 = 0b01 = CCLK |
apullin | 0:eeb86d8390aa | 21 | |
apullin | 0:eeb86d8390aa | 22 | //SetupTimerPCLK(); //TODO |
apullin | 0:eeb86d8390aa | 23 | |
apullin | 0:eeb86d8390aa | 24 | SetupTimer0(); |
apullin | 0:eeb86d8390aa | 25 | SetupTimer1(); |
apullin | 0:eeb86d8390aa | 26 | //SetupADC(); //TODO |
apullin | 0:eeb86d8390aa | 27 | |
apullin | 0:eeb86d8390aa | 28 | StartTimers(); |
apullin | 0:eeb86d8390aa | 29 | |
apullin | 0:eeb86d8390aa | 30 | while(1) { |
apullin | 0:eeb86d8390aa | 31 | // myled2 = 1; |
apullin | 0:eeb86d8390aa | 32 | // wait(0.2); |
apullin | 0:eeb86d8390aa | 33 | // myled2 = 0; |
apullin | 0:eeb86d8390aa | 34 | // wait(0.2); |
apullin | 0:eeb86d8390aa | 35 | } |
apullin | 0:eeb86d8390aa | 36 | |
apullin | 0:eeb86d8390aa | 37 | //Timer0 -> Limit = PWM period |
apullin | 0:eeb86d8390aa | 38 | // MR0 = DCA (software set) |
apullin | 0:eeb86d8390aa | 39 | // MR1 = DCB (software set) |
apullin | 0:eeb86d8390aa | 40 | // MR2 = Software ADC trigger |
apullin | 0:eeb86d8390aa | 41 | //Timer1 -> Limit = PWM period (same as Timer0.Limit) |
apullin | 0:eeb86d8390aa | 42 | // MR0 = DCC (software set) |
apullin | 0:eeb86d8390aa | 43 | // MR1 = DCD (software set) |
apullin | 0:eeb86d8390aa | 44 | } |
apullin | 0:eeb86d8390aa | 45 | |
apullin | 0:eeb86d8390aa | 46 | //Pseudocode: |
apullin | 0:eeb86d8390aa | 47 | /* |
apullin | 0:eeb86d8390aa | 48 | Timer0 ISR{ |
apullin | 0:eeb86d8390aa | 49 | if ISR source is LIMIT: |
apullin | 0:eeb86d8390aa | 50 | if MR0 = 0: DCA to LOW |
apullin | 0:eeb86d8390aa | 51 | else: : DCA to HIGH |
apullin | 0:eeb86d8390aa | 52 | if MR1 = 0: DCB to LOW |
apullin | 0:eeb86d8390aa | 53 | else: : DCB to HIGH |
apullin | 0:eeb86d8390aa | 54 | |
apullin | 0:eeb86d8390aa | 55 | if ISR source is MR0: |
apullin | 0:eeb86d8390aa | 56 | DCA to LOW |
apullin | 0:eeb86d8390aa | 57 | |
apullin | 0:eeb86d8390aa | 58 | if ISR source is MR1: |
apullin | 0:eeb86d8390aa | 59 | DCB to LOW |
apullin | 0:eeb86d8390aa | 60 | |
apullin | 0:eeb86d8390aa | 61 | if ISR source is MR2: |
apullin | 0:eeb86d8390aa | 62 | Start ADC sample & convert |
apullin | 0:eeb86d8390aa | 63 | |
apullin | 0:eeb86d8390aa | 64 | clear Timer0 IF |
apullin | 0:eeb86d8390aa | 65 | } |
apullin | 0:eeb86d8390aa | 66 | |
apullin | 0:eeb86d8390aa | 67 | Timer1 ISR{ |
apullin | 0:eeb86d8390aa | 68 | if ISR source is LIMIT: |
apullin | 0:eeb86d8390aa | 69 | if MR0 = 0: DCC to LOW |
apullin | 0:eeb86d8390aa | 70 | else: : DCC to HIGH |
apullin | 0:eeb86d8390aa | 71 | if MR1 = 0: DCD to LOW |
apullin | 0:eeb86d8390aa | 72 | else: : DCD to HIGH |
apullin | 0:eeb86d8390aa | 73 | |
apullin | 0:eeb86d8390aa | 74 | if ISR source is MR0: |
apullin | 0:eeb86d8390aa | 75 | DCC to LOW |
apullin | 0:eeb86d8390aa | 76 | |
apullin | 0:eeb86d8390aa | 77 | if ISR source is MR1: |
apullin | 0:eeb86d8390aa | 78 | DCD to LOW |
apullin | 0:eeb86d8390aa | 79 | |
apullin | 0:eeb86d8390aa | 80 | clear Timer1 IF |
apullin | 0:eeb86d8390aa | 81 | } |
apullin | 0:eeb86d8390aa | 82 | |
apullin | 0:eeb86d8390aa | 83 | ADC ISR{ |
apullin | 0:eeb86d8390aa | 84 | if ISR source is conversion done: |
apullin | 0:eeb86d8390aa | 85 | copy data from ADC buffer to ZOH variable |
apullin | 0:eeb86d8390aa | 86 | |
apullin | 0:eeb86d8390aa | 87 | clear ADC IF |
apullin | 0:eeb86d8390aa | 88 | } |
apullin | 0:eeb86d8390aa | 89 | */ |
apullin | 0:eeb86d8390aa | 90 | |
apullin | 0:eeb86d8390aa | 91 | void SetupTimer1(void) |
apullin | 0:eeb86d8390aa | 92 | { |
apullin | 0:eeb86d8390aa | 93 | LPC_SC->PCONP |= 1 << 2; // Power on Timer` |
apullin | 0:eeb86d8390aa | 94 | LPC_TIM1->TCR = 0x2; // Reset and set to timer mode |
apullin | 0:eeb86d8390aa | 95 | LPC_TIM1->CTCR = 0x0; |
apullin | 0:eeb86d8390aa | 96 | LPC_TIM1->PR = 0; // No prescale |
apullin | 0:eeb86d8390aa | 97 | LPC_TIM1->MR0 = 3000; // PWM period ; TODO: check on scope |
apullin | 0:eeb86d8390aa | 98 | LPC_TIM1->MR1 = 0; //DCC |
apullin | 0:eeb86d8390aa | 99 | LPC_TIM1->MR2 = 0; //DCD |
apullin | 0:eeb86d8390aa | 100 | LPC_TIM1->MCR = (1<<0) //MR0 interrupt - PWM end of period |
apullin | 0:eeb86d8390aa | 101 | & (1<<1) //MR0 resets timer - PWM period |
apullin | 0:eeb86d8390aa | 102 | & (1<<3) //MR1 interrupt - PWM DCC falling edge |
apullin | 0:eeb86d8390aa | 103 | & (1<<6); //MR2 interrupt - PWM DCD falling edge |
apullin | 0:eeb86d8390aa | 104 | |
apullin | 0:eeb86d8390aa | 105 | // Enable the ISR vector |
apullin | 0:eeb86d8390aa | 106 | NVIC_SetVector (TIMER1_IRQn, (uint32_t)&Timer1_IRQHandler); |
apullin | 0:eeb86d8390aa | 107 | NVIC_EnableIRQ(TIMER1_IRQn); |
apullin | 0:eeb86d8390aa | 108 | } |
apullin | 0:eeb86d8390aa | 109 | |
apullin | 0:eeb86d8390aa | 110 | |
apullin | 0:eeb86d8390aa | 111 | void SetupTimer0(void) |
apullin | 0:eeb86d8390aa | 112 | { |
apullin | 0:eeb86d8390aa | 113 | LPC_SC-> PCONP |= 1 << 1; // Power on Timer0 |
apullin | 0:eeb86d8390aa | 114 | LPC_TIM0->TCR = 0x2; // Reset and set to timer mode |
apullin | 0:eeb86d8390aa | 115 | LPC_TIM0->CTCR = 0x0; |
apullin | 0:eeb86d8390aa | 116 | LPC_TIM0->PR = 0; // No prescale |
apullin | 0:eeb86d8390aa | 117 | LPC_TIM1->MR0 = 3000; // PWM period |
apullin | 0:eeb86d8390aa | 118 | LPC_TIM1->MR3 = 2950; //ADC trigger point ; TODO: check on scope |
apullin | 0:eeb86d8390aa | 119 | LPC_TIM1->MR1 = 0; //DCA |
apullin | 0:eeb86d8390aa | 120 | LPC_TIM1->MR2 = 0; //DCB |
apullin | 0:eeb86d8390aa | 121 | LPC_TIM0->MCR = (1<<0) //MR0 interrupt - PWM end of period |
apullin | 0:eeb86d8390aa | 122 | & (1<<1) //MR0 resets timer - PWM period |
apullin | 0:eeb86d8390aa | 123 | & (1<<3) //MR1 interrupt - PWM DCA falling edge |
apullin | 0:eeb86d8390aa | 124 | & (1<<6); //MR2 interrupt - PWM DCB falling edge |
apullin | 0:eeb86d8390aa | 125 | //& (1<<9); //MR3 interrupt - PWM ADC trigger - doesn't use interrupt; TODO: check on scope with INT enabled |
apullin | 0:eeb86d8390aa | 126 | |
apullin | 0:eeb86d8390aa | 127 | // Enable the ISR vector |
apullin | 0:eeb86d8390aa | 128 | NVIC_SetVector (TIMER0_IRQn, (uint32_t)&Timer0_IRQHandler); |
apullin | 0:eeb86d8390aa | 129 | NVIC_EnableIRQ(TIMER0_IRQn); |
apullin | 0:eeb86d8390aa | 130 | } |
apullin | 0:eeb86d8390aa | 131 | |
apullin | 0:eeb86d8390aa | 132 | void StartTimers(void){ |
apullin | 0:eeb86d8390aa | 133 | LPC_TIM0->TCR = 1; // Enable Timer0 |
apullin | 0:eeb86d8390aa | 134 | LPC_TIM1->TCR = 1; // Enable Timer0 |
apullin | 0:eeb86d8390aa | 135 | } |
apullin | 0:eeb86d8390aa | 136 | |
apullin | 0:eeb86d8390aa | 137 | #define TIM0_IRQ_MR0 (1<<0) |
apullin | 0:eeb86d8390aa | 138 | #define TIM0_IRQ_MR1 (1<<1) |
apullin | 0:eeb86d8390aa | 139 | #define TIM0_IRQ_MR2 (1<<2) |
apullin | 0:eeb86d8390aa | 140 | #define TIM0_IRQ_MR3 (1<<3) |
apullin | 0:eeb86d8390aa | 141 | |
apullin | 0:eeb86d8390aa | 142 | #define TIM1_IRQ_MR0 (1<<0) |
apullin | 0:eeb86d8390aa | 143 | #define TIM1_IRQ_MR1 (1<<1) |
apullin | 0:eeb86d8390aa | 144 | #define TIM1_IRQ_MR2 (1<<2) |
apullin | 0:eeb86d8390aa | 145 | #define TIM1_IRQ_MR3 (1<<3) |
apullin | 0:eeb86d8390aa | 146 | |
apullin | 0:eeb86d8390aa | 147 | void Timer0_IRQHandler(void) |
apullin | 0:eeb86d8390aa | 148 | { |
apullin | 0:eeb86d8390aa | 149 | //uint32_t ir = LPC_TIM0->IR; |
apullin | 0:eeb86d8390aa | 150 | |
apullin | 0:eeb86d8390aa | 151 | if(LPC_TIM0->IR & TIM0_IRQ_MR0){ //PWM period end/start |
apullin | 0:eeb86d8390aa | 152 | if(LPC_TIM0->MR1 > 0){ |
apullin | 0:eeb86d8390aa | 153 | PWMA = 1; |
apullin | 0:eeb86d8390aa | 154 | } else{ |
apullin | 0:eeb86d8390aa | 155 | PWMA = 0; |
apullin | 0:eeb86d8390aa | 156 | } |
apullin | 0:eeb86d8390aa | 157 | |
apullin | 0:eeb86d8390aa | 158 | if(LPC_TIM0->MR2 > 0){ |
apullin | 0:eeb86d8390aa | 159 | PWMB = 1; |
apullin | 0:eeb86d8390aa | 160 | } else{ |
apullin | 0:eeb86d8390aa | 161 | PWMB = 0; |
apullin | 0:eeb86d8390aa | 162 | } |
apullin | 0:eeb86d8390aa | 163 | |
apullin | 0:eeb86d8390aa | 164 | LPC_TIM0->IR &= ~(1 << 0); //clear MR0 interrupt flag |
apullin | 0:eeb86d8390aa | 165 | } |
apullin | 0:eeb86d8390aa | 166 | |
apullin | 0:eeb86d8390aa | 167 | if(LPC_TIM0->IR & TIM0_IRQ_MR1){ //PWMA expire |
apullin | 0:eeb86d8390aa | 168 | PWMA = 0; |
apullin | 0:eeb86d8390aa | 169 | LPC_TIM0->IR &= ~(1 << 1); //clear MR1 interrupt flag |
apullin | 0:eeb86d8390aa | 170 | } |
apullin | 0:eeb86d8390aa | 171 | |
apullin | 0:eeb86d8390aa | 172 | if(LPC_TIM0->IR & TIM0_IRQ_MR2){ //PWMB expire |
apullin | 0:eeb86d8390aa | 173 | PWMB = 0; |
apullin | 0:eeb86d8390aa | 174 | LPC_TIM0->IR &= ~(1 << 2); //clear MR2 interrupt flag |
apullin | 0:eeb86d8390aa | 175 | } |
apullin | 0:eeb86d8390aa | 176 | |
apullin | 0:eeb86d8390aa | 177 | //ADC triggering should be done automatically by the peripherals, when MAT0.3 is hit. |
apullin | 0:eeb86d8390aa | 178 | //This interrupt is not used, except for debugging and testing. |
apullin | 0:eeb86d8390aa | 179 | //if(LPC_TIM0->IR & TIM0_IRQ_MR2){ //ADC trigger |
apullin | 0:eeb86d8390aa | 180 | // //Software ADC start here |
apullin | 0:eeb86d8390aa | 181 | // LPC_TIM0->IR &= ~(1 << 3); //clear MR3 interrupt flag |
apullin | 0:eeb86d8390aa | 182 | //} |
apullin | 0:eeb86d8390aa | 183 | |
apullin | 0:eeb86d8390aa | 184 | } |
apullin | 0:eeb86d8390aa | 185 | |
apullin | 0:eeb86d8390aa | 186 | void Timer1_IRQHandler(void) |
apullin | 0:eeb86d8390aa | 187 | { |
apullin | 0:eeb86d8390aa | 188 | if(LPC_TIM1->IR & TIM1_IRQ_MR0){ //PWM period end/start |
apullin | 0:eeb86d8390aa | 189 | if(LPC_TIM1->MR1 > 0){ |
apullin | 0:eeb86d8390aa | 190 | PWMC = 1; |
apullin | 0:eeb86d8390aa | 191 | } else{ |
apullin | 0:eeb86d8390aa | 192 | PWMC = 0; |
apullin | 0:eeb86d8390aa | 193 | } |
apullin | 0:eeb86d8390aa | 194 | |
apullin | 0:eeb86d8390aa | 195 | if(LPC_TIM1->MR2 > 0){ |
apullin | 0:eeb86d8390aa | 196 | PWMD = 1; |
apullin | 0:eeb86d8390aa | 197 | } else{ |
apullin | 0:eeb86d8390aa | 198 | PWMD = 0; |
apullin | 0:eeb86d8390aa | 199 | } |
apullin | 0:eeb86d8390aa | 200 | |
apullin | 0:eeb86d8390aa | 201 | LPC_TIM1->IR &= ~(1 << 0); //clear MR0 interrupt flag |
apullin | 0:eeb86d8390aa | 202 | } |
apullin | 0:eeb86d8390aa | 203 | |
apullin | 0:eeb86d8390aa | 204 | if(LPC_TIM1->IR & TIM1_IRQ_MR1){ //PWMA expire |
apullin | 0:eeb86d8390aa | 205 | PWMC = 0; |
apullin | 0:eeb86d8390aa | 206 | LPC_TIM1->IR &= ~(1 << 1); //clear MR1 interrupt flag |
apullin | 0:eeb86d8390aa | 207 | } |
apullin | 0:eeb86d8390aa | 208 | |
apullin | 0:eeb86d8390aa | 209 | if(LPC_TIM1->IR & TIM1_IRQ_MR2){ //PWMB expire |
apullin | 0:eeb86d8390aa | 210 | PWMD = 0; |
apullin | 0:eeb86d8390aa | 211 | LPC_TIM1->IR &= ~(1 << 2); //clear MR2 interrupt flag |
apullin | 0:eeb86d8390aa | 212 | } |
apullin | 0:eeb86d8390aa | 213 | } |