cancel
Showing results for 
Search instead for 
Did you mean: 

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

bluewaters213
Associate III
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
4 REPLIES 4
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
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 Up vote any posts that you find helpful, it shows what's working..
bluewaters213
Associate III
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
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