2018-02-02 05:49 AM
Hello! I have a question: How to generate a PWM on STM32F103C8T6 in Keil uVision 5. I watched some tutorials but I couldn't make it work, I want to enable TIM1 on pin PA11 and PA12. PWM needs to run at 20kHz => period = 3600.
It will be used for motor control on a robot. This is what I managed to do:
&sharpinclude 'stm32f10x.h'
int main(void){RCC-> APB2ENR |= RCC_APB2ENR_TIM1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; GPIOA->ODR |= GPIO_ODR_ODR11; GPIOA->CRH |= GPIO_CRH_MODE11_1; GPIOA->CRH &= ~(GPIO_CRH_MODE11_0);TIM1->PSC = 0;
TIM1->ARR = 3600; TIM1->CCR4 = 1800; TIM1->CCMR2 |= TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4PE; TIM1->CCER |= TIM_CCER_CC4E; TIM1->BDTR |= TIM_BDTR_MOE; TIM1->CR1 |= TIM_CR1_CEN; TIM1->EGR |= TIM_EGR_UG; while(1) {//User code
}}I watched this tutorials:
https://www.youtube.com/watch?v=xs9lGF--QcU
https://www.youtube.com/watch?v=fJZl_rACO-0
https://www.youtube.com/watch?v=UHqqhKXVmSc
Maybe if you have some more tutorials on PWM and encoder readings with timers I would be very glad!
Thank you in advance!
Any help would be appreciated!
#rotary-encoder #stm32 #stm32f103c8t6 #tim-pwm2018-02-02 06:17 AM
a few suggestions:
1) I generally organize my pwm code into two pieces: 1) initialize the time base, like setting up the prescaler and period, set up the particular channels, 2) change the channel's duty cycle.
if I were to do your case, I would have written two pieces, t1pwm_init(1, 3600); and t1pwm4_setdc();
you have most of the code pieces there but if you want I can share mine with you.
2) '
TIM1->CCMR2 |= TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4PE; '
that's a very dangerous piece of code as it assume a certain state of CCMR2 prior to that line. I would have done a more explicit line.
hope it helps.
2018-02-02 08:09 AM
Thank you for a quick reply. I would be grateful for your code.
2018-02-02 08:41 AM
You need to set also the CNF11 bits in GPIO_CRH to Alternate function.
(Btw., there is no TIM4 functionality for PA12)
JW
2018-02-02 11:29 AM
here is what I have, tested on a STM32F100 and should work across STMF0/1/2/3/4..., with minor modifications (mostly on the GPIO and RCC portion of the code)
/*
MAPR Bits 7:6 TIM1_REMAP[1:0]: TIM1 remapping
These bits are set and cleared by software. They control the mapping of TIM1 channels 1 to 4, 1N to 3N, external trigger (ETR) and Break input (BKIN) on the GPIO ports.
00: No remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PB12, CH1N/PB13, CH2N/PB14, CH3N/PB15)
01: Partial remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PA6, CH1N/PA7, CH2N/PB0, CH3N/PB1)
10: not used
11: Full remap (ETR/PE7, CH1/PE9, CH2/PE11, CH3/PE13, CH4/PE14, BKIN/PE15, CH1N/PE8, CH2N/PE10, CH3N/PE12)
*/
//initialize pwm to TxCCP_PS (prescaler) and TxCCP_PR (period)
void pwm1_init(uint16_t TxCCP_PS, uint16_t TxCCP_PR) {
//route the clock to timer
//route the clock to timer
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
//source from internal clock -> disable slave mode
TIM1->SMCR &=~TIM_SMCR_SMS;//clear sms->use internal clock
//stop the timer to configure it
//TIM1->CR1 &=~TIM_CR1_CEN;//clear cen. 0=disable the timer, 1=enable the timer
//TIM1->CR1 &=~TIM_CR1_CKD;//clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved
//TIM1->CR1 &=~TIM_CR1_DIR;//clear DIR bit. 0=upcounter, 1=downcounter
//TIM1->CR1 &=~TIM_CR1_OPM;//clear opm bit. 0=periodic timer, 1=one-shot timer
//or to simply zero the register
//TIM1->CR1 = 0;//much easier
TIM1->CR1 =(0<<8) |//0->1:1 clock, 1->2:1 clock, 2->4:1 clock, 3->reserved
(0<<7) |//1->APR buffered, 0->APR not buffered
(0<<5) |//0->edge-aligned, 1->center-aligned mode 1, 2->center-aligned mode 2, 3->center-aligned mode 3
(0<<4) |//0->upcounter, 1->downcounter
(0<<3) |//0->continous mode, 1->one pulse mode
(0<<2) |//update request source
(0<<1) |//0->UEV enabled, 1->UEV disabled
(0<<0) |//0->counter disabled, 1->counter enabled
0x00;
TIM1->CR2 = 0;//default value
TIM1->SMCR= 0;//default value
//set the prescaler
TIM1->PSC = TxCCP_PS - 1;//set the prescaler
TIM1->RCR = 0;//repetition counter = 0 (=no repetition)
TIM1->ARR = -1;//auto reload register / period = 0; - need to change for downcounters
TIM1->CNT = 0;//reset the counter
//clear the status register bits for capture / compare flags
TIM1->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF);
//disable the interrupt by clearing the enable bits
TIM1->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE);
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;//enable AFIO
AFIO->MAPR = (AFIO->MAPR &~AFIO_MAPR_TIM1_REMAP) | (AFIO_MAPR_TIM1_REMAP & TIM1REMAP);//select the remap scheme
//configure CCP1..4
#if defined(USE_TIM1CH1)
//configure CCP1
TIM1->CCMR1 = (TIM1->CCMR1 &~0x00ff) |
(0<<7) |//0->OC1REF not affedted by ETRF, 1->OC1REF cleared by ETRF high
(6<<4) |//0->frozen (for time base), 1->active on match, 2->inactive on match, 3->toggle, 4->inactive, 5->active, 6->pwm mode 1, 7->pwm mode 2
(0<<3) |//0->preload disabled, 1->preload enabled
(0<<2) |//0->fast disabled, 1->fast enabled
(0<<0) |//0->ch1 as output, 1->ch1 as input, 2->ch1 as input, 3->ch1 as input
0x00;
TIM1->CCER = (TIM1->CCER &~(0x0f << 0)) |
(0<< 3) |//0->normal polarity for CC1N, 1->inverse polarity
(0<< 2) |//0->disable CC1N, 1->enable CC1N
(0<< 1) |//0->normal polarity for CC1, 1->inverse polarity
(1<< 0) |//1->enable CC1, 0->disable CC1
0x00;
TIM1->CCR1 = 0;//0% duty cycle
//configure gpio
#if TIM1REMAP == TIM1REMAP0
//00: No remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PB12, CH1N/PB13, CH2N/PB14, CH3N/PB15)
//configure GPIO
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//enable GPIO clock
//each 4-bit group (CNF1..0 + MODE1..0) controls a pin
//CNF1..0=0b10 for push-pull, 0b11 for OD
//MODE1..0=0b01->10Mhz output, 0b10->2Mhz output, 0b11->50Mhz
GPIOA->CRH = (GPIOA->CRH &~(0x0f << (4*(8%8)))) | (0b1001 << (4*(8%8)));//set CNF1..0
#endif
#if TIM1REMAP == TIM1REMAP1
//01: Partial remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PA6, CH1N/PA7, CH2N/PB0, CH3N/PB1)
//configure GPIO
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//enable GPIO clock
//each 4-bit group (CNF1..0 + MODE1..0) controls a pin
//CNF1..0=0b10 for push-pull, 0b11 for OD
//MODE1..0=0b01->10Mhz output, 0b10->2Mhz output, 0b11->50Mhz
GPIOA->CRH = (GPIOA->CRH &~(0x0f << (4*(8%8)))) | (0b1001 << (4*(8%8)));//set CNF1..0
#endif
#if TIM1REMAP == TIM1REMAP2
//10: not used
#endif
#if TIM1REMAP == TIM1REMAP3
//11: Full remap (ETR/PE7, CH1/PE9, CH2/PE11, CH3/PE13, CH4/PE14, BKIN/PE15, CH1N/PE8, CH2N/PE10, CH3N/PE12)
//configure GPIO
RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;//enable GPIO clock
//each 4-bit group (CNF1..0 + MODE1..0) controls a pin
//CNF1..0=0b10 for push-pull, 0b11 for OD
//MODE1..0=0b01->10Mhz output, 0b10->2Mhz output, 0b11->50Mhz
GPIOE->CRH = (GPIOE->CRH &~(0x0f << (4*(9%8)))) | (0b1001 << (4*(9%8)));//set CNF1..0
#endif
#endif
//T1CCP2..4 deleted to save space
TIM1->EGR = 0xff;//force an update
//TIM1->BDTR |= TIM_BDTR_MOE;//enable MOE bit
//enable the timer.
TIM1->CR1 |= TIM_CR1_CEN;//enable the timer
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
The code was borrowed from LM3S/LM4F/TM4C so you will see some Luminary / TI terminology there.
I took out T1CCP2..4 to save you some space.
Setting duty cycle is done by a group of macros.
//set duty cycle
#define pwm1_setdc1(dc)TIM1->CCR1 = (dc)
//T1CCP2..4 not shown�?�?�?�?�?�?
The code is designed assuming that initializing the pwm module is done in-frequently, so that the changing of duty cycle is made to execute as fast as possible.
The code should be fairly easy to be parted to other parts.
Note that the AF framework used on those F1 parts isn't the greatest. later chips, like F0 and F3/4, have much more friendlier AF mechanisms and will make your life here much easier.
2018-02-04 02:05 AM
Ohh, I meant PA11 and PA For learning purposes I moved to PA8 (TIM1 CH1).Do you have any example code?
I did some research, but it still doesn't work(code below). I still don
't
know whathenry.****
meant with dangerous piece of code:'
TIM1->CCMR2 |= TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4PE; '
I would be really grateful for some example code and more explanation becauseI am really new in stm32 and bit writing!
#include 'cmsis_boot/stm32f10x.h'
int main(void){ //clock enable RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; RCC-> APB2ENR |= RCC_APB2ENR_AFIOEN; RCC-> APB2ENR |= RCC_APB2ENR_TIM1EN;//max Output speed:
GPIOA->CRH |= GPIO_CRH_MODE8; //alternate function: GPIOA->CRH |= GPIO_CRH_CNF8_1;//Timer settings:
TIM1->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE; //PWM mode 1
TIM1->PSC = 0;
TIM1->ARR = 3600; TIM1->CCR1 = 1800;TIM1->CCER |= TIM_CCER_CC1E; //enable TIM Output
TIM1->BDTR |= TIM_BDTR_MOE; //Main output enableTIM1->CR1 |= TIM_CR1_CEN;
TIM1->EGR |= TIM_EGR_UG;while(1)
{//User code
} }2018-02-04 06:08 AM
These need to be written as N-1, ie divide by 1000 set as 999
TIM1->PSC = 0; // DIV1
TIM1->ARR = 3600; // DIV3601
2018-02-13 04:26 PM
#include 'stm32f10x.h'
int main(void){
RCC-> APB2ENR |= RCC_APB2ENR_TIM1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
GPIOA->CRH &=0xFFFF0FFF; //PA11
GPIOA->CRH |=0x0000B000; //Alternate function output Push-pull Output mode, max speed 50 MHz.
TIM1->PSC = 0;
TIM1->ARR = 3600;
TIM1->CCR4 = 1800;
TIM1->CCMR2 |= TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4PE;
TIM1->CCER |= TIM_CCER_CC4E;
TIM1->BDTR |= TIM_BDTR_MOE;
TIM1->CR1 |= TIM_CR1_CEN;
TIM1->EGR |= TIM_EGR_UG;
while(1)
{ //User code
}
}