Skip to main content
bluewaters213
Associate III
February 6, 2013
Question

6 CH PWM on TIM1 (What is Wrong with my Code)

  • February 6, 2013
  • 4 replies
  • 998 views
Posted on February 06, 2013 at 11:09

Hello,

I am currently migrating from Microchip MIP to ST ARM Cortex M3 so I am new to Cortex M3.I have been trying for days to configure Advance TIM1 in STM32F103RB to output 6 Channel PWM but it has prove to be a difficult task. When I probe PA8--PA10 using my Salae Logic Analyzer, there are no PWM on those Pins..

I have gone through my Code, Data sheet and App Note several times but I can not get it to Work. I do not want to use ST Peripheral Library. I prefer bare metal because it enable me to understand the Hardware.My Code is below and I desperately need help

Note: CoCoox IDE and ARM GNU 4.7 C Compiler Used in building the Code.

=====================

#include ''stm32f10x.h''

void PWM_init(void);

int main(void)

{

  volatile uint32_t dly;

  RCC-> CFGR |= RCC_CFGR_ADCPRE_DIV4;     //ADC divided by 4

  /*Enable all GPIO Clock & Alternate Function Clock*/

  RCC->APB2ENR = RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN;

  /*Enable ADC1 & ADC2 Clock*/

  RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN;

  /*Enable TIMER1 & USART1 Clock*/

  RCC->APB2ENR |= RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN;

  /*Enable TIMER2, TIMER3 & I2C1 Clock*/

  RCC->APB1ENR = RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN |RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM4EN;

  RCC->AHBENR = RCC_AHBENR_DMA1EN ;   //DMA1 clock enable

  GPIOB->CRH = 0x44444222;          //Configure PB8 & PB9 as Output (Push Pull)

  GPIOA->CRH = 0x44444AAA;           //PA8 - PA10 Alternate Function Push Pull Output

  AFIO->MAPR |= AFIO_MAPR_USART1_REMAP; // Remap TX=>PB6, RX=>PB7)

  PWM_init();                       //Initialize 20KHz PWM on CH 1-3

    while(1)

    {

        for(dly = 0; dly < 500000; dly++);

        GPIOB->BSRR = (1 << 8);

        GPIOB->BSRR = (1 << 9);

        for(dly = 0; dly < 500000; dly++);

        GPIOB->BRR = (1 << 8);

        GPIOB->BRR = (1 << 9);

    }

}

/* PWM CH1=>PA8, PWM CH2=>PA9, PWM CH1=>PA10*/

void PWM_init()

{ /* channel 1&2 is configured as output*/

  TIM1->CCMR1 &= ~(TIM_CCMR1_CC1S | TIM_CCMR1_CC2S);

  TIM1->CCMR2 &= ~(TIM_CCMR2_CC3S);    //channel 3 is configured as output

  /* Channel 1,2& 3 active high */

  TIM1->CCER = TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P;

  TIM1->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //PWM mode 1

  TIM1->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1; //PWM mode 1

  TIM1->CR1 = TIM_CR1_CMS_1  | TIM_CR1_ARPE;  //Center-aligned mode 2

  TIM1->CR2 = TIM_CR2_CCPC;                    //CCxE, CCxNE and OCxM bits are preloaded

  TIM1->BDTR = TIM_BDTR_MOE | TIM_BDTR_AOE | TIM_BDTR_OSSR;

  TIM1->PSC = 0;

  TIM1->ARR = 2800;        // Auto reload value 2800 (PWM Period = 50us)

  TIM1->CCR1 = 1400;       // Start PWM duty for channel 1

  TIM1->CCR2 = 700;        // Start PWM duty for channel 2

  TIM1->CCR3 = 350;        // Start PWM duty for channel 3

  /* CH 1-3 Output Compare Enable*/

  TIM1->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E;

  TIM1->CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE;

  TIM1->CR1 = TIM_CR1_CEN;  // Counter enable

}

===================

Thanks

Please Forgive my English
    This topic has been closed for replies.

    4 replies

    waclawek.jan
    Super User
    February 6, 2013
    Posted on February 06, 2013 at 11:59

    You overwrite previous value of TIM1->CCER (twice).

    It might perhaps be a good idea to write to one register only once per function.

    [EDIT] You also don't set mode for channel 2. [/EDIT]

    JW
    Tesla DeLorean
    Guru
    February 6, 2013
    Posted on February 06, 2013 at 12:02

    I do not want to use ST Peripheral Library. I prefer bare metal because it enable me to understand the Hardware.

    Awesome, so you're several days into a task that will take several weeks. Does this also preclude you from fashioning a working example with the library, and reviewing the source code for it, a task that might consume an hour or less.

    I desperately need help

    A good time then to reflect on your strategy, should I double down, or approach the problem from a different direction?

    Are you running the part at 56 MHz, in which case ARR = 2800 - 1, if 72 MHz ARR = 3600 - 1 would be the value for 20KHz

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    bluewaters213
    Associate III
    February 6, 2013
    Posted on February 06, 2013 at 18:01

    Hello,

    Thanks for the reply. I have it working now. I use a different approach as suggested..please find below the working code.Thanks alot

    #include ''stm32f10x.h''

    void Init_6CHPWM();

    int main(void)

    {

    volatile uint32_t dly;

    RCC-> CFGR |= RCC_CFGR_ADCPRE_DIV4; //ADC divided by 4

    /*Enable all GPIO Clock & Alternate Function Clock*/

    RCC->APB2ENR = RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN;

    /*Enable ADC1 & ADC2 Clock*/

    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN;

    /*Enable TIMER1 & USART1 Clock*/

    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN;

    /*Enable TIMER2, TIMER3 & I2C1 Clock*/

    RCC->APB1ENR = RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN |RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM4EN;

    RCC->AHBENR = RCC_AHBENR_DMA1EN ; //DMA1 clock enable

    GPIOB->CRL = 0x444444AA; //Configure PB0 & PB1 as Alternate Function Push Pull Output

    GPIOB->CRH = 0x44444222; //Configure PB8 & PB9 as Output (Push Pull)

    GPIOA->CRL = 0xA4444444; //PA7 Alternate Function Push Pull Output

    GPIOA->CRH = 0x44444AAA; //PA8 - PA10 Alternate Function Push Pull Output

    /*Partial remap TIM1: CH1/PA8,CH2/PA9,CH3/PA10,CH4/PA11,CH1N/PA7,CH2N/PB0,CH3N/PB1

    * Remap USART1: TX/PB6, RXPB7 */

    AFIO->MAPR |= 0x00000044;

    Init_6CHPWM(); //Initialize 20KHz PWM on CH 1-3

    while(1)

    {

    for(dly = 0; dly < 500000; dly++);

    GPIOB->BSRR = (1 << 8);

    GPIOB->BSRR = (1 << 9);

    for(dly = 0; dly < 500000; dly++);

    GPIOB->BRR = (1 << 8);

    GPIOB->BRR = (1 << 9);

    }

    }

    void Init_6CHPWM()

    {

    TIM1->CCMR1 |= 0x00006868; //CH1 & CH2 Output Compare, Preload, PWM1 Enable

    TIM1->CCMR2 |= 0x00000068; //CH3 Output Compare, Preload, PWM1 Enable

    /*CH1, CH2, CH3 and their Corresponding Complement Output Enable*/

    TIM1->CCER |= 0x00000555;

    TIM1->CR1 |= 0x000000A0; // Centre Align Mode 1 & Auto-reload Preload enable

    TIM1->PSC = 0;

    TIM1->ARR = (1400 - 1); // Auto reload value 1400 (Period = 50us)

    TIM1->CCR1 = 700; // Start PWM duty for channel 1

    TIM1->CCR2 = 500; // Start PWM duty for channel 2

    TIM1->CCR3 = 350; // Start PWM duty for channel 3

    TIM1->BDTR |= 0x00008800; // Enable Main O/P & Enable OSSR

    TIM1->CR1 |= 0x00000001; //Counter Enable

    }

    ________________

    Attachments :

    6_CH_PWM.jpg : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006HtTT&d=%2Fa%2F0X0000000aQT%2F3USRSsVWWP0ZuBk2ft.k1iOe9Q.TqEcdBc_h0EZ0JN4&asPdf=false
    waclawek.jan
    Super User
    February 6, 2013
    Posted on February 06, 2013 at 18:27

    I like the symbolic names for the bits/fields more.

    I also use an amended version of the stm32f4xx.h file (a better idea would be perhaps to create another header file, but I am not going to revert it now), so I have e.g.

    // OCyM bits determine generation of OCyREF signal

    #define  TIM_CCMR_OCM__FROZEN                0  // output compare mode is FROZEN, OCyREF does not change

    #define  TIM_CCMR_OCM__HIGH_ON_MATCH         1  // signal OCyREF goes high (active) upon compare match

    #define  TIM_CCMR_OCM__LOW_ON_MATCH          2  // signal OCyREF goes low (inactive) upon compare match

    #define  TIM_CCMR_OCM__TOGGLE_ON_MATCH       3  // signal OCyREF toggles upon compare match

    #define  TIM_CCMR_OCM__FORCE_LOW             4  // OCyREF <- 0 (inactive)

    #define  TIM_CCMR_OCM__FORCE_HIGH            5  // OCyREF <- 1 (active)

    #define  TIM_CCMR_OCM__PWM1                  6  // if upcounting, OCyREF = (TIMx_CNT < TIMx_CCRy) ? 1 : 0; if downcounting OCyREF = (TIMx_CNT > TIMx_CCRy) ? 0 : 1;

    #define  TIM_CCMR_OCM__PWM2                  7  // if upcounting, OCyREF = (TIMx_CNT < TIMx_CCRy) ? 0 : 1; if downcounting OCyREF = (TIMx_CNT > TIMx_CCRy) ? 1 : 0;

    and then I write things like

      // select OC2 as compare, mode is toggle, and enable the output

      TIM3->CCMR1 |= 0

        OR ( TIM_CCMR_CCS__OUTPUT          * TIM_CCMR1_CC2S_0)   /* CC3S[1:0] bits (Capture/Compare 3 Selection) */

        OR ( TIM_CCMR_OCM__TOGGLE_ON_MATCH * TIM_CCMR1_OC2M_0)   /* OC3M[2:0] bits (Output Compare 3 Mode) */

      ;

    YMMV.

    JW