在嵌入式开发中,舵机是一种常见的执行机构,广泛应用于机器人、智能小车等项目中。本文将以一个简单的 STM32 PWM 舵机项目为例,深入剖析其底层原理,并提供详细的代码实现和避坑指南。对于想要入门嵌入式开发,特别是想玩转 STM32 的朋友来说,这是一个很好的起点。通过学习本篇,你将能够掌握使用 STM32 的 PWM 功能控制舵机的基本方法,为后续更复杂的项目打下坚实的基础。
舵机原理与 PWM 控制
舵机的工作原理
舵机内部主要由电机、减速齿轮组、控制电路和电位器(或编码器)组成。控制电路接收外部控制信号,驱动电机旋转,通过减速齿轮组带动输出轴转动。同时,电位器或编码器检测输出轴的位置,并将位置信息反馈给控制电路,形成闭环控制,从而实现精确的角度控制。
PWM 信号控制舵机
舵机通常使用 PWM (Pulse Width Modulation,脉冲宽度调制) 信号进行控制。PWM 信号的占空比决定了舵机的转动角度。一般来说,PWM 信号的周期固定为 20ms,占空比在 0.5ms 到 2.5ms 之间变化,对应舵机的 0 度到 180 度。不同的舵机可能略有差异,具体数值需要参考舵机的数据手册。
STM32 PWM 舵机项目实践
硬件连接
- STM32 开发板:选择一款带有 PWM 输出功能的 STM32 开发板,例如 STM32F103C8T6 (俗称小蓝板)。
- 舵机:选择一款常用的 9g 舵机。
- 杜邦线:用于连接 STM32 开发板和舵机。
将舵机的信号线连接到 STM32 的 PWM 输出引脚,电源线连接到 5V 电源,地线连接到 GND。
软件代码实现
以下代码使用 HAL 库进行开发,展示了如何配置 STM32 的 PWM 功能,并控制舵机的转动角度。
#include "stm32f1xx_hal.h"
TIM_HandleTypeDef htim3; // 定义定时器句柄
void SystemClock_Config(void); // 系统时钟配置函数
static void MX_GPIO_Init(void); // GPIO 初始化函数
static void MX_TIM3_Init(void); // 定时器3 初始化函数
int main(void)
{
HAL_Init(); // 初始化 HAL 库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化 GPIO
MX_TIM3_Init(); // 初始化 TIM3
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动 PWM 输出,使用通道 1
while (1)
{
// 控制舵机转动到 0 度
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 50); // 0.5ms 对应的 CCR 值,需要根据实际情况调整
HAL_Delay(1000);
// 控制舵机转动到 90 度
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 150); // 1.5ms 对应的 CCR 值,需要根据实际情况调整
HAL_Delay(1000);
// 控制舵机转动到 180 度
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 250); // 2.5ms 对应的 CCR 值,需要根据实际情况调整
HAL_Delay(1000);
}
}
// 初始化 TIM3 为 PWM 模式
static void MX_TIM3_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim3.Instance = TIM3;
htim3.Init.Prescaler = 719; // 预分频系数,根据时钟频率计算
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1999; // 计数周期,设置 PWM 频率为 50Hz (20ms)
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWM 模式 1
sConfigOC.Pulse = 0; // 初始脉冲宽度
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
}
// 其他初始化函数省略,具体参考 STM32 HAL 库文档
代码解释:
TIM_HandleTypeDef htim3;:定义一个定时器句柄,用于操作定时器 3。MX_TIM3_Init():初始化定时器 3 为 PWM 模式,设置预分频系数、计数周期等参数。 这里的预分频系数需要根据你的STM32主频来配置。比如你用了72Mhz,那么预分频系数可以设置为719,这样进入定时器的时钟就是100Khz,周期设置为1999,这样就可以得到大约50Hz的PWM频率了(100Khz / 2000 = 50Hz)。HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);:启动 PWM 输出,使用通道 1。__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 50);:设置 PWM 的占空比,这里的 50 对应 0.5ms 的脉冲宽度。这里的值需要根据实际舵机进行调整。
常见问题与避坑指南
- 舵机不转动: 检查电源连接是否正确,PWM 信号线是否连接到正确的引脚,代码中的预分频系数和计数周期是否设置正确。
- 舵机抖动: 可能是 PWM 信号不稳定,可以尝试增加滤波电容,或者调整 PWM 的频率。
- 舵机转动角度不准确: 需要根据舵机的数据手册,调整代码中的占空比与角度的对应关系。
- 时钟配置错误: STM32 的时钟配置至关重要,需要确保时钟频率设置正确,才能保证 PWM 信号的准确性。 可以使用 STM32CubeMX 工具来辅助时钟配置。
- HAL库版本兼容性: 不同版本的 HAL 库在API的调用上可能存在差异,注意参考对应版本的官方文档。
总结
通过本文的介绍,相信你已经掌握了使用 STM32 的 PWM 功能控制舵机的基本方法。希望你能够将所学知识应用到实际项目中,创造出更多有趣的作品。在实际应用中,还可以结合其他传感器和模块,例如超声波传感器、红外传感器等,实现更复杂的功能,例如自动避障、路径规划等。 这需要你深入理解单片机的中断、定时器以及各种通信协议,比如I2C、SPI、UART 等等。 此外,熟练使用调试工具,例如 J-Link 或 ST-Link,也是提高开发效率的关键。 掌握了这些技能,你就可以搭建出更加复杂的嵌入式系统,甚至可以尝试使用 RTOS (Real-Time Operating System) 来管理你的程序,例如 FreeRTOS 或 RT-Thread。 这些技能对于成为一名合格的嵌入式工程师来说至关重要。
冠军资讯
代码一只喵