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

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
    }
}