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.

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
apullin
Date:
Wed Feb 24 09:26:33 2016 +0000
Commit message:
Initial commit

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r eeb86d8390aa main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Feb 24 09:26:33 2016 +0000
@@ -0,0 +1,213 @@
+#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
+    }
+}
diff -r 000000000000 -r eeb86d8390aa mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Feb 24 09:26:33 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/6f327212ef96
\ No newline at end of file