使用STM32L TIM PWM控制伺服电机
Using STM32L TIM PWM to control Servo Motor
所以我正在尝试使用 STM32L1 通过 PWM 控制伺服。下面是完整的代码,不需要库。
当我上传 运行 这段代码时,舵机会滴答作响几次,而不是实际平稳地移动到所需位置 600(0*)、1100(50*)、1600(100*) 和 2100(150*) .
我相信这与预分频值计算有关,而且我不确定以微秒为单位提及 ARR 是否正确,如果不正确,我如何配置它以读取微秒而不是毫秒。
详情请参考代码中的注释。
#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
}
}
这取决于您的时钟设置。您需要先配置时钟,因为许多 L1 在复位后只有 2.097MHz 的默认时钟,您设置的周期为 0.152598951 sek,伺服接收的脉冲宽度为 0.07 至 0.15sek,长约 75 倍(我假设 PCS 应该是 15 来存档 16*20000 个时钟周期)。根据您的设置,定时器时钟应该超过 100MHz,这对于 L1
是不可能的
定时器计算滴答声,滴答声持续多长时间取决于时钟速度和定时器时钟分频器。
要存档 20 毫秒,您需要将 PSC 保留为零值且 ARR = 41939
当然,1 毫秒为 2097,2 毫秒为 4194
您需要计算正确的值
配置 PLL 的最简单方法是使用所见即所得的 CubeMX 时钟编辑器
正如@PeterJ 提到的,这实际上取决于您的时钟设置。我会在这里更精确一些。
你的 ARR 看起来不错,因为很明显你的经期是 20 毫秒。
但是你的 PSC 应该根据 ARR 来设置,以达到 1kHz。为此,您必须知道时钟频率。
旁注,根据 stm 文档,PSC 值应比所需值小 1 个单位(例如,如果你想要 16,则应将 15 写入寄存器)。
The counter clock frequency CK_CNT is equal to fCK_PSC / (PSC[15:0] + 1)
所以我正在尝试使用 STM32L1 通过 PWM 控制伺服。下面是完整的代码,不需要库。 当我上传 运行 这段代码时,舵机会滴答作响几次,而不是实际平稳地移动到所需位置 600(0*)、1100(50*)、1600(100*) 和 2100(150*) . 我相信这与预分频值计算有关,而且我不确定以微秒为单位提及 ARR 是否正确,如果不正确,我如何配置它以读取微秒而不是毫秒。 详情请参考代码中的注释。
#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
}
}
这取决于您的时钟设置。您需要先配置时钟,因为许多 L1 在复位后只有 2.097MHz 的默认时钟,您设置的周期为 0.152598951 sek,伺服接收的脉冲宽度为 0.07 至 0.15sek,长约 75 倍(我假设 PCS 应该是 15 来存档 16*20000 个时钟周期)。根据您的设置,定时器时钟应该超过 100MHz,这对于 L1
是不可能的定时器计算滴答声,滴答声持续多长时间取决于时钟速度和定时器时钟分频器。
要存档 20 毫秒,您需要将 PSC 保留为零值且 ARR = 41939
当然,1 毫秒为 2097,2 毫秒为 4194
您需要计算正确的值
配置 PLL 的最简单方法是使用所见即所得的 CubeMX 时钟编辑器
正如@PeterJ 提到的,这实际上取决于您的时钟设置。我会在这里更精确一些。
你的 ARR 看起来不错,因为很明显你的经期是 20 毫秒。
但是你的 PSC 应该根据 ARR 来设置,以达到 1kHz。为此,您必须知道时钟频率。
旁注,根据 stm 文档,PSC 值应比所需值小 1 个单位(例如,如果你想要 16,则应将 15 写入寄存器)。
The counter clock frequency CK_CNT is equal to fCK_PSC / (PSC[15:0] + 1)