智能道闸实现
前言
最近在做黑龙江东方学院鸿蒙班的物联网实验——智能道闸项目。基于STM32L431主控+鸿蒙LiteOS,利用定时器PWM输出驱动两路舵机,对比智慧交通、智慧家居两块扩展板差异,修改驱动完成双道闸同步开关。本篇把整个实验原理、代码修改要点、踩坑记录完整整理出来,适合刚上手鸿蒙物联网开发的同学参考。
一、实验硬件环境
- 主控板:NBV2.0(STM32L431VC)
- 扩展板:智慧家居扩展板(支持两路舵机端子)
- 执行器件:2个MG996R舵机
- 系统:华为鸿蒙LiteOS
- 调试工具:串口助手、Keil工程
⚠️硬件区别重点
- 智慧交通扩展板:硬件只引出1路舵机接口,只能驱动单个舵机;
- 智慧家居扩展板:引出两路舵机端子,可以同时挂载两个舵机。
引脚分配:
PA11→ TIM1_CH4:舵机1 PWM输出PA15→ TIM2_CH1:舵机2 PWM输出
二、工作原理
舵机依靠50Hz PWM信号控制旋转角度。 本实验TIM1、TIM2配置:预分频=799,自动重装载值=1999,输出标准50Hz舵机驱动波形。
改变定时器通道CCR比较值,就可以调整PWM高电平时间,从而控制舵机旋转不同角度。
GPIO引脚不能当作普通输出,需要配置为定时器复用推挽输出,才能输出PWM波形驱动舵机。
三、软件架构分析
3.1 main主函数:LiteOS启动流程
main函数不写业务逻辑,只负责硬件与操作系统初始化,业务逻辑放在任务中执行。 执行顺序:硬件HAL初始化 → 系统时钟 → 显示/控制任务初始化 → LiteOS内核初始化 → 创建任务 → LOS_Start()启动操作系统。
int main(void)
{
UINT32 uwRet = LOS_OK;
/*初始化*/
HAL_Init(); //硬件初始化
sysclkinit(); //系统时钟初始化
task_view_init(); //显示任务初始化
task_control_init(); //控制任务初始化
uwRet = LOS_KernelInit(); //操作系统初始化
if (uwRet != LOS_OK)
{
return LOS_NOK;
}
uwRet = creat_control_task(); //创建控制任务
if (uwRet != LOS_OK)
{
return LOS_NOK;
}
uwRet = creat_view_task(); //创建显示任务
if (uwRet != LOS_OK)
{
return LOS_NOK;
}
LOS_Start(); //启动操作系统
while (1)
{
}
}
重点:
LOS_Start()之后,操作系统接管CPU,main函数的while(1)永远不会执行。
3.2 舵机驱动 p0080.c 修改核心
原始代码适配智慧交通扩展板,只能单路舵机;想要智慧家居扩展板两路舵机同时动作,两处必须修改:
① P0080_Init 初始化函数
同时启动TIM1_CH4、TIM2_CH1两路PWM通道,上电舵机默认0°(道闸关闭状态)
void P0080_Init(void)
{
MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
Set_Servo_PWM(DEGREE_0);
}
② Set_Servo_PWM角度设置函数
设置角度时,同时更新两个定时器通道比较寄存器,让两个舵机同步接收PWM指令。
void Set_Servo_PWM(unsigned int degree)
{
unsigned int pwm = 0;
switch ( degree )
{
case DEGREE_MINOR_90:
pwm = 50;
break;
case DEGREE_MINOR_45:
pwm = 100;
break;
case DEGREE_0:
pwm = 150;
break;
case DEGREE_45:
pwm = 200;
break;
case DEGREE_90:
pwm = 250;
break;
default:
pwm = 50;
break;
}
__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,pwm);
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_1,pwm);
}
对外业务调用接口:
//道闸抬起,舵机转到90°
void Servo_Open(void)
{
Set_Servo_PWM(DEGREE_90);
}
//道闸落下,舵机回到0°
void Servo_Close(void)
{
Set_Servo_PWM(DEGREE_0);
}
3.3 PA11、PA15引脚复用配置
引脚配置在HAL_TIM_MspPostInit函数内部,把PA11、PA15设置复用推挽输出,映射到对应定时器通道。
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim->Instance==TIM1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
//PA11 --> TIM1_CH4
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
else if(htim->Instance==TIM2)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
//PA15 --> TIM2_CH1
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
四、实验现象
- NBV2.0主板插接智慧家居扩展板,两个舵机接到两路舵机端子,编译下载程序上电;
- 调用
Servo_Open():两路舵机同步旋转至90°,模拟双道闸抬杆开启; - 调用
Servo_Close():两路舵机回到0°,模拟双道闸落杆关闭; - 串口打印状态信息,LiteOS任务运行稳定无崩溃,TIM1、TIM2正常输出PWM波形。
对比测试:换到智慧交通扩展板,硬件仅引出一路端子,只能看到单个舵机动作。双舵机正常工作,软硬件两者缺一不可。
五、开发踩坑总结
- ❗定时器初始化完成不等于输出PWM,必须调用
HAL_TIM_PWM_Start开启通道,否则引脚没有波形输出; - PA11、PA15必须配置为定时器复用推挽输出,如果配置成普通GPIO,舵机完全不会动;
- 想要两个舵机同时动作:硬件需要智慧家居扩展板,软件需要同时设置两个定时器通道CCR值;
- 角度与PWM比较值的映射关系不能写错,数值不对舵机角度会发生偏移;
- LiteOS开发模式:main只做初始化,业务逻辑要写在任务函数中,
LOS_Start()之后main死循环不会运行。
六、拓展优化思路
- 时序优化:给两个舵机分配不同PWM值,实现道闸先后开、先后关,复刻真实停车场场景;
- 传感联动:外接红外检测传感器,检测车辆自动开关道闸;
- 云端控制:通过WiFi连接物联网云平台,实现网页/小程序远程控制道闸;
- 硬件保护:增加舵机堵转检测逻辑,避免电机长时间卡死烧毁。
写在最后
这个实验完整演示了STM32定时器PWM、GPIO复用、鸿蒙LiteOS任务开发,硬件扩展板的差异很容易被忽略,很多同学代码改完舵机不转,问题就出在软硬件不匹配。如果你正在做鸿蒙物联网课程实验,希望本篇博客对你有帮助。
更多推荐


所有评论(0)