So I'm trying to control a servo by PWM using STM32L1. below is the full code, no library needed. When I upload and run this code, the servo ticks several times instead of actually moving smoothly to the required positions 600(0*), 1100(50*), 1600(100*), and 2100(150*). I believe this is related to the prescale value calculation, plus I'm not sure whether mentioning the ARR in micro-seconds is correct, if not, how do I configure it to read microseconds instead of milliseconds. Please refer to the notes in the code for further details.
#include <stdio.h>
#include "stm32l1xx.h"
// Keil::Device:Startup
// initialization of GPIOB
void TIM4_Init(){
RCC->AHBRSTR |= RCC_AHBRSTR_GPIOARST; /* Reset GPIOB clock */
RCC->AHBENR |= RCC_AHBENR_GPIOBEN; /* Enable GPIOB clock */
GPIOB->MODER &= ~(0x03 << (2*6)); /* Clear bit 11 & 12 Alternate mode*/
GPIOB->MODER |= 0x02 << (2*6); /* set as Alternate mode*/
GPIOB->OSPEEDR &= ~(0x03<< (2*6)); /* 40 MHz speed */
GPIOB->OSPEEDR |= 0x03<< (2*6); /* 40 MHz speed */
GPIOB->PUPDR &= ~(1<<6); /* NO PULL-UP PULL-DOWN */
GPIOB->OTYPER &= ~(1<<6); /* PUSH-PULL */
GPIOB->AFR[0] |= 0x2 << (4*6); /* Pin6 set as alternate function 2 (TIM4) */
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
TIM4->PSC = 16; //prescale value, AHB/APB1 Fmax=32MHz / 2
TIM4->ARR = 20000-1; //motor Freq = 50Hz, Period(ARR)= 1/50 = 20000us
// initialization of TIM & PWM
TIM4->CCMR1 |= TIM_CCMR1_OC1M; // 111: PWM mode 2 - In upcounting, channel 1 is inactive
//as long as TIMx_CNT<TIMx_CCR1 else active.
// In downcounting, channel 1 is active as long as
//TIMx_CNT>TIMx_CCR1 else inactive.
TIM4->CCMR1 |= TIM_CCMR1_OC1PE;
TIM4->CR1 |= TIM_CR1_ARPE;
TIM4->CCER |= TIM_CCER_CC1E;
TIM4->EGR |= TIM_EGR_UG;
TIM4->SR &= ~TIM_SR_UIF;
TIM4->DIER |= TIM_DIER_UIE;
TIM4->CR1 |= TIM_CR1_CEN;
}
// set servo to 4 positions in sequence
int main(void){
int i;
int position=600; // initial motor position
TIM4_Init();
while (1){
if ((position >=600)|| (position <=2100))
position = position+500; // motor positions will be 600(0*), 1100(50*), 1600(100*)
//, 2100(150*)
TIM4->CCR1 = position;
for(i=0;i<1000;i++); // short delay
}
}
That depends on your clock settings. You need to configure your clock first as many L1 have the default clock after the reset 2.097MHz only and the period with your settings is 0.152598951 sek, and the pulse width received by the servo is 0.07 to 0.15sek which is ~75times to long (I presume that PCS should be 15 to archive 16*20000 clocks period). With your setting the timer clock should be more than 100MHz which is impossible for L1
Timers count ticks and how long the tick takes depends on the clock speed, and timer clock dividers.
To archive 20ms you need to leave PSC with zero value and ARR = 41939
Of course do 1ms will be 2097 and 2ms - 4194
You need to calculate the correct values
The easiest way to configure PLL is using the WYSIWYG CubeMX clock editor