软硬件平台
软件
STM32CubeIDE 1.7.0
Saleae Logic 1.2.18
硬件
STM32F103ZET6
MG995
Saleae Logic Analyer
工程初始化
RCC(系统时钟)
HSE(外部高速时钟) -> Crystal/Ceramic Resonator(晶振 / 陶瓷谐振器)
HSE(外部低速时钟) -> Crystal/Ceramic Resonator(晶振 / 陶瓷谐振器)
HCLK(AHB总线时钟) -> 72MHz
APB2 Prescaier(APB2总线预分频器) -> 1(即不分频)
TIM1(高级控制定时器)
Clock Source(时钟源) -> Internal Clock(内部时钟)
Channel1(通道1) -> PWM Generation CH1(PWM模式通道1)
Prescaier(预分频器) -> 720-1
TIM1所使用的APB2时钟为72MHz,现期望获得100KHz(10us)时钟信号
Counter Period(计数周期) -> 2000-1
时钟周期为10us,MG995舵机信号周期为20ms
auto-reload preload(自动重装载) -> Enable(使能)
代码部分
开启通道PWM输出
/* main.c */
/* 开启定时器 1 通道 1 PWM */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
舵机控制
由于发现不同的舵机的控制信号不一样,但是大同小异,所以就将定时器通道以及舵机控制信号的一些指标通过define的方式定义,方便移植。
/* servo.c */ #include "servo.h" /* 设置所使用的定时器 */ #define servo_htim htim1 /* 设置所使用的通道 */ #define servo_channel TIM_CHANNEL_1 /* 设置定时器计数值 */ #define servo_plus 2000 /* 设置舵机控制信号周期(ms) */ #define servo_cycle_ms 20.0 /* 设置舵机控制信号最短脉冲宽度(ms) */ #define servo_min_ms 0.5 /* 设置舵机控制信号1ms脉冲宽度变化所对应的角度变化值 */ #define servo_1ms_angle 90.0 void servo_control(int angle) { int value; /* 目标角度为0时需要单独处理 */ if(angle == 0) { value = ((servo_min_ms / servo_cycle_ms) * servo_plus); } /* 通过计算目标角脉冲宽度来计算所需要的脉冲计数值 */ else { value = (((servo_min_ms + (angle / servo_1ms_angle)) / servo_cycle_ms) * servo_plus); } /* 改变定时器 1 通道 1 的脉冲计数值 */ __HAL_TIM_SET_COMPARE(&servo1_htim,servo1_channel,value); }
/* servo.h */ #ifndef INC_SERVO_H_ #define INC_SERVO_H_ #include "main.h" #include "tim.h" void servo_control(int angle); #endif /* INC_SERVO_H_ */
测试
测试代码
/* 舵机转到0°位置 */ servo1_control(0); HAL_Delay(1000); /* 舵机转到45°位置 */ servo1_control(45); HAL_Delay(1000); /* 舵机转到90°位置 */ servo1_control(90); HAL_Delay(1000); /* 舵机转到120°位置 */ servo1_control(135); HAL_Delay(1000); /* 舵机转到180°位置 */ servo1_control(180); HAL_Delay(1000);
测试结果
舵机转到0°位置
舵机转到45°位置
舵机转到90°位置
舵机转到135°位置
舵机转到180°位置
Debug
对于下面这条语句我最初想当然的加了一个 -1 ,后来采集出来的脉冲宽度少了10us,下面对这个问题的。
value = (((servo_min_ms + (angle / servo_1ms_angle)) / servo_cycle_ms) * servo_plus);
上面这条语句是被下面这条语句所使用的。
__HAL_TIM_SET_COMPARE(&servo1_htim,servo1_channel,value);
查询数据手册,发现有如下描述:
进一步的,看一下我们的 value 指向到哪里,可以在 stm32f1xx_hal_tim.h 中看到这样的描述。
如果我没有理解错的话,value 值最终被指向Capture Compare Register,那就是手册中的CCRx了。
再进一步可以在手册中发现OCXREF信号为高电平有效信号,CCxIF是一个置中断状态寄存器中的标志位也是高有效。
分析到现在,很显然,ARR的值在我们设置的过程中是需要-1的,不然会多计数一个。
而CCRx的值就是表示计数的次数,那么在这里加上-1的话就会少计数一次。
至此,这个bug已经分析完成了,那么在以后执行相关操作的时候这个点就要注意了,不要赋错值。