衡山派开发板RT-Thread实战:SG90舵机PWM驱动与角度控制详解
衡山派开发板RT-Thread实战:SG90舵机PWM驱动与角度控制详解
最近在做一个机械臂的小项目,用到了SG90舵机,正好手头有衡山派(HSPI)开发板,就想着把舵机驱动移植到RT-Thread系统上。整个过程下来,发现从原理理解到代码实现,再到实际调试,有不少需要注意的细节。今天我就把这次实战的经验整理出来,手把手教你怎么在衡山派开发板上用PWM精准控制SG90舵机的角度。
这篇文章适合正在学习嵌入式开发,特别是对RISC-V、RT-Thread和舵机控制感兴趣的朋友。我会从SG90舵机的基本原理讲起,一步步带你完成驱动代码的移植、配置和验证,最后还会分析核心代码,让你不仅知道怎么做,更明白为什么这么做。
1. 认识我们的“演员”:SG90舵机
在开始写代码之前,咱们得先了解要控制的对象。SG90是一种微型舵机,在机器人、航模里非常常见。
核心参数一览:
| 参数 | 规格 | 说明 |
|---|---|---|
| 驱动电压 | 3V ~ 7.2V | 推荐使用5V供电,电压太低扭矩不足,太高可能烧坏。 |
| 工作扭矩 | 1.6 kg/cm | 在1厘米的力臂上能产生1.6公斤的力,驱动小负载没问题。 |
| 控制信号 | PWM(脉冲宽度调制) | 这是关键,舵机角度由PWM信号的脉冲宽度决定。 |
| 转动角度 | 180度 | 这是我们今天要控制的范围,从0度到180度。 |
| 信号周期 | 20 ms | 控制信号需要每20ms发送一次,也就是频率为50Hz。 |
注意:市面上还有一种360度连续旋转的舵机,它不能控制角度,只能控制旋转速度和方向。咱们今天用的是180度的版本,别买错了。
PWM控制原理(小白版):你可以把舵机想象成一个听话的“小弟”。你不需要告诉它具体转多少度,而是通过发送一种特殊的“暗号”(PWM波)来指挥它。这个暗号就是每20ms发送一个高电平脉冲,舵机会根据这个高电平持续的时间长短,来判断应该转到哪个位置。
- 0.5ms脉冲-> 对应0度位置
- 1.5ms脉冲-> 对应90度中间位置
- 2.5ms脉冲-> 对应180度位置
所以,控制角度的核心任务,就是让衡山派开发板产生一个周期20ms(50Hz),且高电平宽度在0.5ms到2.5ms之间可调的PWM信号。
2. 工程搭建与驱动移植
拿到舵机后,第一步不是急着接线写代码,而是把开发环境准备好,把官方提供的驱动代码“搬”到我们的工程里。
2.1 获取资料与驱动代码
原始资料里提供了驱动代码的压缩包,你需要从资料下载中心找到它。通常它会被放在类似\luban-lite\application\rt-thread\helloworld\user-bsp的文件夹下。如果你没看到user-bsp这个文件夹,说明你还没完成模块移植的前置配置,需要先去完成必要的环境搭建操作。
2.2 修改Kconfig文件
驱动代码放好后,需要修改工程的Kconfig文件,这样后续的配置菜单里才会出现SG90舵机的选项。
- 用VSCode打开你的工程,找到这个文件:
application\rt-thread\helloworld\Kconfig。 - 在文件末尾的
#endif语句之前,添加下面这行代码:
这行代码的作用是告诉构建系统:“嘿,我这里还有一个SG90舵机的配置目录,你把它也包含进来。”# SG90舵机 source "application/rt-thread/helloworld/user-bsp/sg90-steering-engine/Kconfig"
2.3 使用menuconfig配置工程
接下来,我们要在图形化配置界面里启用SG90舵机模块。
- 双击工程根目录下的
win_env.bat脚本,打开RT-Thread的Env配置工具。 - 在Env命令行中,输入
scons --list-def查看所有可用的默认配置。找到名为d13x_JLC_rt-thread_helloworld的配置(这是衡山派开发板的默认配置),记住它的编号(比如是7)。 - 应用这个配置:
或者直接用配置名:scons --apply-def=7scons --apply-def=d13x_JLC_rt-thread_helloworld_defconfig - 输入
scons --menuconfig进入图形化配置菜单。 - 在菜单中,用方向键找到
Porting code using the LCKFB module选项,按Y键选中它(前面会出现[*])。 - 按回车键进入这个子菜单。
- 在里面找到
Using SG90 steering engine选项,同样按Y键选中它。 - 最后,用左右方向键切换到
<Save>,按回车保存配置,然后退出菜单。
2.4 编译与烧录
配置保存后,就可以编译了。在Env命令行中输入:
scons如果你的电脑CPU核心多,想加快编译速度,可以用-j参数,比如scons -j16。编译成功后,在\luban-lite\output\d13x_JLC_rt-thread_helloworld\images目录下会生成一个d13x_JLC_v1.0.0.img的镜像文件。
最后,使用烧录工具将这个镜像文件烧录到衡山派开发板中,具体烧录方法可以参考官方文档。
3. 核心代码解析与原理
烧录完成,硬件准备就绪,现在我们来深入看看驱动代码到底是怎么工作的。理解了代码,你才能举一反三。
3.1 头文件与宏定义 (bsp_sg90.h)
头文件很简单,就是声明了三个我们要用到的函数:
int SG90_Init(void); // 初始化舵机 int SG90_DeInit(void); // 反初始化(关闭) int Set_Servo_Angle(uint32_t Angle); // 设置角度,范围0-1803.2 初始化函数 (SG90_Init)
这是整个驱动的起点,在bsp_sg90.c文件中。我把它拆开一步步讲:
第一步:降低PWM时钟频率
ret = hal_pwm_set_tb(SG90_PWMA_CHANNEL, 1000000);这行代码非常关键!衡山派开发板PWM的默认时钟频率是24MHz。在这个频率下,计数器计满一个20ms周期所需要的数值会非常大,可能超出寄存器能设置的范围,导致无法产生我们需要的长周期信号。所以这里先把PWM1通道的时钟频率降低到1MHz,这样后续设置周期值就游刃有余了。这是我调试时遇到的第一个坑,原文也特别强调了。
第二步:查找PWM设备
pwm_dev = (struct rt_device_pwm *)rt_device_find(SG90_PWM_NAME);RT-Thread中,外设都抽象为“设备”。这行代码就是根据设备名(这里是”pwm”)在系统中找到PWM设备,拿到它的操作句柄。
第三步:设置PWM周期和初始脉宽
ret = rt_pwm_set(pwm_dev, SG90_PWMA_CHANNEL, SG90_PERIOD, 0);pwm_dev: 刚才找到的设备句柄。SG90_PWMA_CHANNEL: 使用的PWM通道号,这里是1。SG90_PERIOD: 周期值,定义为20000000纳秒(ns),也就是20毫秒(ms)。0: 初始脉冲宽度(高电平时间),设为0意味着舵机初始状态没有脉冲,是安全的。
第四步:使能PWM输出
ret = rt_pwm_enable(pwm_dev, SG90_PWMA_CHANNEL);最后这步是“打开开关”,让PWM信号实际从对应的硬件引脚输出。
3.3 角度控制函数 (Set_Servo_Angle)
这是最核心的函数,实现了角度到脉冲宽度的映射。
int Set_Servo_Angle(uint32_t Angle) { float pulse; // 1. 角度范围检查 if (Angle > 180) { LOG_E("Angle out of range !!"); return -RT_ERROR; } // 2. 角度到脉宽的换算(核心算法) pulse = 500000.0f + ((2000000.0f / 180.0f) * (float)Angle); // 3. 设置新的脉宽 if (RT_EOK != rt_pwm_set(pwm_dev, SG90_PWMA_CHANNEL, SG90_PERIOD, (uint32_t)pulse)) { LOG_E("rt_pwm_set failed !!"); return -RT_ERROR; } return RT_EOK; }重点解释一下换算公式:
500000.0f: 这是0度角对应的脉冲宽度,单位是纳秒(ns),即0.5ms。2000000.0f / 180.0f: 这是“每度对应的脉冲宽度增量”。总脉冲变化范围是2.5ms - 0.5ms = 2.0ms,即2,000,000 ns。把它平分成180份,得到每度大约11111 ns。(2000000.0f / 180.0f) * (float)Angle: 计算当前角度相对于0度需要增加的脉冲宽度。- 两者相加,就得到了目标角度对应的精确脉冲宽度。
例如,设置90度时:pulse = 500000 + (11111 * 90) ≈ 1,500,000 ns,正好是1.5ms。
3.4 测试线程 (test_sg90_steering_engine.c)
官方提供了一个测试线程,它创建了一个任务,让舵机在0到180度之间自动来回摆动,非常适合验证驱动是否正常工作。
线程入口函数逻辑:
- 初始化舵机 (
SG90_Init)。 - 依次将舵机转到45度、90度、0度,每个状态保持1秒,做一个“开机自检”动作。
- 进入主循环,让角度变量
Angle从0递增到180,再递减回0,循环往复。 - 每次循环调用
Set_Servo_Angle(Angle)设置角度,并延时100ms。这个延时决定了舵机运动的速度,延时越小,运动越快。
如何使用这个测试:代码最后用MSH_CMD_EXPORT将函数导出为了Finsh/MSH命令行命令。这意味着当程序运行起来,并通过串口连接到开发板后,你只需要在终端里输入:
test_sg90_module舵机就会开始自动来回转动。想停止它,就输入:
test_exit_sg90_module4. 硬件连接与最终验证
代码烧录进去之后,最后一步就是把舵机接上开发板。
接线很简单:
- 舵机红线(电源)-> 接开发板的5V引脚。
- 舵机棕线(地)-> 接开发板的GND引脚。
- 舵机橙线(信号)-> 接开发板指定的PWM输出引脚(具体是哪个引脚,需要根据你使用的PWM通道和衡山派的引脚复用表来确定,请参考开发板原理图)。
提示:务必确保共地!即开发板和舵机的地线(GND)要连接在一起,这是信号正常工作的基础。
连接好硬件和串口调试器后,给开发板上电。打开串口终端(如Putty、MobaXterm等),设置波特率为115200。
在终端中,输入test_sg90_module命令并回车。如果一切顺利,你应该能立刻听到舵机转动的声音,并看到它开始周期性地在0到180度之间摆动。
看到舵机乖乖听你指挥转动起来,是不是很有成就感?这个过程涵盖了从原理理解、环境配置、代码移植、编译烧录到硬件验证的完整嵌入式开发流程。你可以基于这个驱动,去开发更复杂的项目,比如多舵机协同的机械臂、云台摄像头等等。遇到问题别怕,多看看串口日志,理解每个函数返回值的含义,调试的过程就是进步最快的时候。
