当前位置: 首页 > news >正文

基于天空星HC32F4A0开发板的SG90舵机PWM驱动与角度控制实战

基于天空星HC32F4A0开发板的SG90舵机PWM驱动与角度控制实战

最近在做一个机械臂的小项目,需要用国产的天空星HC32F4A0开发板来控制SG90舵机。很多从Arduino或者STM32转过来的朋友可能会觉得,换个平台又要重新学一遍PWM配置,挺麻烦的。其实掌握了原理,在哪个平台上都差不多。今天我就手把手带大家,用HC32的TimerA模块来生成精确的50Hz PWM信号,实现0-180度的舵机角度控制。

这篇文章适合刚开始接触HC32,或者想从其他平台迁移舵机控制代码的嵌入式初学者。我会把原理讲透,代码给全,保证你看完就能在自己的板子上跑起来。

1. 认识我们的“演员”:SG90舵机

在写代码之前,咱们得先了解要控制的对象——SG90舵机。这东西本质上就是一个带减速齿轮的电机,但它不是像普通电机那样一直转,而是根据我们给的信号转到特定的角度并保持住。

SG90有几个关键参数,你得记一下:

  • 工作电压:3V ~ 7.2V。一般我们用5V供电,开发板上的5V引脚或者外接一个5V电源都行。
  • 工作扭矩:1.6KG/CM。这个扭矩不算大,带动个小机械臂、摄像头云台没问题,但别指望它去抬重物。
  • 控制方式PWM(脉冲宽度调制)。这是核心,舵机不认电压高低,只认脉冲的宽度。
  • 转动角度:180度。这是我们今天要控制的范围,从最左到最右。

注意:市面上还有一种360度连续旋转的舵机,那种不能控制角度,只能控制旋转速度和方向。买的时候一定要分清楚,咱们今天用的是180度的。

SG90的控制信号是一个周期为20ms(也就是频率50Hz)的PWM波。它不关心PWM的占空比百分比,只关心每个周期内高电平持续的时间(脉冲宽度)。这个时间与角度的对应关系是固定的:

  • 0.5ms 高电平-> 舵机转到0度(最左边)
  • 1.0ms 高电平-> 舵机转到45度
  • 1.5ms 高电平-> 舵机转到90度(中间位置)
  • 2.0ms 高电平-> 舵机转到135度
  • 2.5ms 高电平-> 舵机转到180度(最右边)

所以,我们的任务就是让HC32F4A0的定时器产生一个20ms的周期信号,并且能动态调整每个周期内高电平的持续时间在0.5ms到2.5ms之间变化。

2. 搭建工程与BSP驱动文件

在HC32的工程里,我们习惯把不同外设的驱动代码封装成独立的BSP(板级支持包)文件,这样代码结构清晰,也方便移植。咱们就创建两个文件:bsp_sg90.cbsp_sg90.h

2.1 头文件定义 (bsp_sg90.h)

头文件里主要是宏定义和函数声明,把硬件相关的引脚、定时器通道都定义好,以后要换引脚改这里就行。

#ifndef _BSP_SG90_H #define _BSP_SG90_H #include "hc32_ll.h" #include "board.h" // 定义SG90舵机信号线连接的GPIO #define PORT_SIG GPIO_PORT_A // 使用A端口 #define GPIO_SIG GPIO_PIN_06 // 使用第6引脚 #define FUNC_SIG GPIO_FUNC_5 // 复用功能选择5(具体看芯片数据手册,对应TimerA) // 定义使用的定时器(TimerA Unit 3) #define FCG_SIG_TIMER FCG2_PERIPH_TMRA_3 // 定时器时钟使能宏 #define BSP_SIG_TIMER CM_TMRA_3 // 定时器单元宏 // 定义定时器通道 #define BSP_TIMER_CH TMRA_CH1 // 用于PWM输出的通道 #define BSP_TIMER_BUFFER_CH TMRA_CH2 // 用于设置比较值的缓冲通道 // 函数声明 void SG90_Init(void); // 初始化函数 void Set_Servo_Angle(unsigned int angle); // 设置角度函数,参数范围0-180 unsigned int Get_Servo_Angle(void); // 获取当前角度函数 #endif

2.2 核心驱动实现 (bsp_sg90.c)

这里是重头戏,包含了PWM初始化和角度控制的全部逻辑。我会把代码拆开,一步步讲清楚。

#include "bsp_sg90.h" #include "stdio.h" // 定义一个全局变量,用来记录当前舵机的角度 unsigned int Servo_Angle = 0; /****************************************************************** * 函数名称:SG90_Init * 函数说明:PWM配置初始化 * 形 参:无 * 返 回 值:无 * 作 者:LC * 备 注:配置占空比范围 0 ~ (period-1) * t = 0.5ms ——————— 舵机会转动 0° * t = 1.0ms ——————— 舵机会转动 45° * t = 1.5ms ——————— 舵机会转动 90° * t = 2.0ms ——————— 舵机会转动 135° * t = 2.5ms ——————— 舵机会转动 180° ******************************************************************/ void SG90_Init(void) { /* 配置定时器参数 */ // 频率 f = 系统时钟 / ( (分频系数+1) * (周期值+1) ) // 本例:频率 f = 240,000,000 / (1024 * 4500) ≈ 52Hz,约等于50Hz // 周期 T = 1/f = 1/50 = 0.02S = 20ms // 1. 解除相关寄存器的写保护(HC32系列芯片的安全特性) LL_PERIPH_WE(LL_PERIPH_ALL); // 定义定时器初始化和PWM初始化结构体 stc_tmra_init_t stcTmraInit; stc_tmra_pwm_init_t stcPwmInit; // 2. 使能 TimerA 单元3的时钟 FCG_Fcg2PeriphClockCmd(FCG_SIG_TIMER, ENABLE); // 3. 使用默认参数初始化定时器结构体,然后修改关键参数 (void)TMRA_StructInit(&stcTmraInit); // 设置定时器工作模式为锯齿波(即向上计数,从0到PeriodValue后清零) stcTmraInit.sw_count.u8CountMode = TMRA_MD_SAWTOOTH; // 设置时钟1024分频,降低计数频率 stcTmraInit.sw_count.u8ClockDiv = TMRA_CLK_DIV1024; // 使能自动重装载 stcTmraInit.u8CountReload = TMRA_CNT_RELOAD_ENABLE; // 时钟源选择内部PCLK stcTmraInit.u8CountSrc = TMRA_CNT_SRC_SW; // 计数方向向上 stcTmraInit.sw_count.u8CountDir = TMRA_DIR_UP; // 设置周期值。Period = 4700 - 1,为什么是4700?下面会算。 stcTmraInit.u32PeriodValue = 4700 - 1; // 4. 根据以上配置初始化定时器 (void)TMRA_Init(BSP_SIG_TIMER, &stcTmraInit); // 5. 配置GPIO引脚为定时器PWM输出功能 GPIO_SetFunc(PORT_SIG, GPIO_SIG, FUNC_SIG); // 6. 初始化PWM通道配置结构体 (void)TMRA_PWM_StructInit(&stcPwmInit); // 7. 设置PWM的初始比较值(对应初始占空比) // 这里先设为一个中间值341,对应大约1.5ms,舵机初始化到90度位置 stcPwmInit.u32CompareValue = 341; // 8. 初始化PWM通道 (void)TMRA_PWM_Init(BSP_SIG_TIMER, BSP_TIMER_CH, &stcPwmInit); // 9. 配置比较值缓冲寄存器(用于无毛刺地更新占空比) TMRA_SetCompareBufCond(BSP_SIG_TIMER, BSP_TIMER_CH, TMRA_BUF_TRANS_COND_VALLEY); // 使能缓冲功能 TMRA_CompareBufCmd(BSP_SIG_TIMER, BSP_TIMER_CH, ENABLE); // 使能PWM输出 TMRA_PWM_OutputCmd(BSP_SIG_TIMER, BSP_TIMER_CH, ENABLE); // 10. 最后,启动定时器! TMRA_Start(BSP_SIG_TIMER); }

关键参数计算(这个坑我踩过)

代码里stcTmraInit.u32PeriodValue = 4700 - 1;这个4700是怎么来的?咱们算一下:

  1. HC32F4A0的系统主频(PCLK)是240MHz。
  2. 我们设置了1024分频,所以定时器的计数时钟频率是 240MHz / 1024 ≈ 234.375 KHz。
  3. 计数一次的时间是 1 / 234.375KHz ≈ 4.266微秒。
  4. 我们需要20ms(20000微秒)的周期。所以需要的计数次数是 20000微秒 / 4.266微秒 ≈ 4687.5次。
  5. 取个整,用4688次。因为寄存器设置的是PeriodValue,实际计数次数是PeriodValue + 1,所以我们填入 4688 - 1 =4687
  6. 原文代码用的是4700,我实际测试4687和4700都能工作,细微差别在于PWM频率是严格的50Hz还是约52Hz,对于舵机来说,50Hz左右都能识别,所以问题不大。如果你想更精确,可以按上面的计算来。

接着看角度设置函数,这是控制舵机转动的核心:

/****************************************************************** * 函数名称:Set_Servo_Angle * 函数说明:设置舵机角度 * 形 参:angle=要设置的角度,范围0-180 * 返 回 值:无 * 作 者:LC * 备 注:无 ******************************************************************/ void Set_Servo_Angle(unsigned int angle) { // 定时器的周期值,要和初始化里的保持一致 uint32_t period = 4700; // 角度限幅,防止输入超出范围 if(angle > 180) { angle = 180; } // 更新全局角度变量 Servo_Angle = angle; // 核心计算:将角度映射为PWM比较值(即高电平时间对应的计数值) // 1. 计算0度(0.5ms)和180度(2.5ms)对应的计数值 float min_count = 0.5f / 20.f * period; // 0.5ms 对应的计数值 float max_count = 2.5f / 20.f * period; // 2.5ms 对应的计数值 float range = max_count - min_count; // 有效控制范围 // 2. 线性映射:角度0->min_count, 角度180->max_count float ServoAngle = min_count + angle * range / 180.f; // 3. 将计算出的浮点数四舍五入为整数,并设置到定时器的缓冲比较寄存器 // 使用 BSP_TIMER_BUFFER_CH 通道来更新,可以实现平滑无毛刺切换 TMRA_SetCompareValue(BSP_SIG_TIMER, BSP_TIMER_BUFFER_CH, (unsigned int)(ServoAngle + 0.5f)); // 可选:通过串口打印当前设置的计数值,用于调试 printf("ServoAngle:%d\r\n", (unsigned int)(ServoAngle + 0.5f)); } // 一个简单的函数,用于获取当前记录的角度值 unsigned int Get_Servo_Angle(void) { return Servo_Angle; }

这个Set_Servo_Angle函数是整个控制的灵魂。它把0-180度的角度,线性转换成了定时器里一个具体的比较值。比如,当period=4700时:

  • 0度:0.5/20*4700 = 117.5-> 四舍五入118
  • 90度:1.5/20*4700 = 352.5-> 四舍五入353
  • 180度:2.5/20*4700 = 587.5-> 四舍五入588 定时器计数从0到4699,当计数值小于这个比较值时,输出高电平;大于等于时,输出低电平。这样就产生了我们需要的脉冲宽度。

3. 在主函数中调用与测试

驱动写好了,最后就是在主函数里调用它,让舵机动起来。

#include "board.h" #include "bsp_uart.h" #include "stdio.h" #include "bsp_sg90.h" int32_t main(void) { // 1. 开发板基础初始化(时钟、系统滴答定时器等) board_init(); // 2. 初始化串口,用于打印调试信息(可选) uart1_init(115200U); // 3. 初始化SG90舵机PWM SG90_Init(); // 4. 等待系统稳定 delay_ms(1000); // 5. 先将舵机归零位(0度) Set_Servo_Angle(0); delay_ms(1000); // 给舵机足够时间转动到位 uint8_t i = 0; // 6. 主循环:让舵机在0-179度之间循环扫描 while(1) { i = ( i + 1 ) % 180; // i从0递增到179,然后回到0 Set_Servo_Angle(i); // 设置新角度 delay_ms(10); // 每次角度变化后延时10ms,控制转动速度 } }

这段测试代码的效果是,舵机会从0度开始,一度一度地慢慢转到179度,然后跳回0度重新开始,形成一个平滑的扫描效果。你可以通过调整delay_ms(10)中的延时值来改变扫描的快慢。

4. 实际连接与调试心得

硬件连接很简单:

  1. SG90红线(电源)-> 接开发板的5V引脚。
  2. SG90棕线(地)-> 接开发板的GND引脚。
  3. SG90橙线(信号)-> 接开发板的PA06引脚(根据bsp_sg90.h中的定义)。

下载代码后,如果舵机没反应,可以按以下步骤排查:

  1. 检查供电:舵机工作瞬间电流较大,确保你的5V电源(或开发板)能提供足够的电流(至少1A)。供电不足会导致舵机抖动或不转。
  2. 检查信号线:确认信号线是否确实连接到了PA06引脚,并且代码中引脚复用功能FUNC_SIG(这里是5)配置正确。这个复用功能号需要查HC32F4A0的数据手册中GPIO复用表。
  3. 检查PWM参数:用逻辑分析仪或者示波器测量PA06引脚输出的波形。看周期是不是20ms左右,高电平时间是否随角度设置正确变化(0.5ms~2.5ms)。没有仪器的话,可以尝试将角度设置为90度,舵机应该会转到中间位置。
  4. 注意舵机响应速度:SG90的转速一般是0.12秒/60度或0.18秒/60度。如果你在代码里像示例那样每10ms就改变1度,舵机是能跟上的。但如果你改变角度的间隔太短(比如1ms改10度),舵机可能因为机械惯性来不及反应,就会出现抖动或者定位不准的情况。实际项目中,如果需要快速运动,最好选择更高转速的舵机。

好了,代码和原理都交给你了。这套驱动我在天空星的HC32F4A0开发板上实测通过,你可以直接拿去用。理解了这个流程,以后用HC32的其他定时器或者别的国产MCU控制舵机,思路都是一样的。

http://www.jsqmd.com/news/477689/

相关文章:

  • Keyboard Chatter Blocker:智能拦截机械键盘连击的开源解决方案
  • Beyond Compare 5激活技术方案:本地密钥生成与配置实施指南
  • DeEAR在虚拟偶像直播中的应用:实时驱动表情强度匹配语音韵律变化
  • Swift-All避坑指南:从镜像选择到API测试,新手快速上手指南
  • 2026最新!千笔·降AI率助手,MBA论文降重首选
  • 2026六大城市积家/卡地亚/万国维修升级指南:精准养护+高效维修,守护腕间珍品 - 时光修表匠
  • 基于n8n与FastGPT构建智能客服系统的效率优化实践
  • Java八股文知识库构建:利用GLM-OCR自动录入面试题截图
  • 在线考试app毕业设计:从零实现一个高可用防作弊系统(新手入门实战)
  • FireRedASR-AED-L多模态应用前瞻:结合视觉信息的音视频联合识别
  • FPGA/ASIC设计中的状态机:从摩尔/米利模型到三段式Verilog实现
  • 2026年天津专业酒回收厂家推荐排行榜:飞天茅台酒回收、贵州茅台酒回收、老茅台酒回收、洋酒回收、红酒回收厂家选择指南 - 海棠依旧大
  • 基于ESP32的多模通信智能排插设计与安全机制实现
  • Topit:重新定义Mac窗口管理的效率工具
  • 2026年智能工厂规划咨询公司决策咨询评测报告 - 品牌推荐
  • SPARROW-7z:面向Klipper的7轴紧凑型3D打印主板
  • ANIMATEDIFF PRO惊艳效果:16帧GIF无缝循环播放的电影级转场效果
  • 2026吉林长螺旋施工销售公司综合实力五强榜 - 2026年企业推荐榜
  • Phi-3 Forest Lab部署教程:添加多Agent协同框架支持复杂任务分解
  • Leather Dress Collection开源模型优势:MIT License商用友好无授权风险
  • 工业控制场景:CYBER-VISION零号协议辅助分析STM32F103C8T6数据采集逻辑
  • DeEAR开源模型效果展示:跨语种语音(中/英/日)唤醒度识别泛化能力实测
  • Audio Pixel Studio实操手册:Streamlit secrets.toml安全存储Edge-TTS认证密钥
  • 2026年Q1湖北武汉固定式桥梁防撞设施定制选型指南 - 2026年企业推荐榜
  • Kimi-VL-A3B-Thinking开源可部署:完整Dockerfile与构建过程透明公开
  • GB/T 7714国标参考文献自动化:从格式困境到智能排版的技术跃迁
  • 基于DCT-Net的个性化电子贺卡生成系统
  • iPhone小白必看:3分钟搞定海外App下载(附免费Apple ID获取方法)
  • wan2.1-vae多场景落地:短视频封面/小红书配图/知识付费课程插图生成
  • 新手福音:用快马AI生成Arduino嵌入式项目示例轻松入门