历史上的今天
今天是:2025年04月09日(星期三)
2019年04月09日 | STM32之使用PWM控制多路舵机
2019-04-09 来源:eefocus
前言
最近在玩一个6自由度的机械臂,我手上这台机械臂的核心控制器件就是那六个能够180度旋转的舵机了。想想之前在学校还没有系统性的把舵机给玩明白,所以就索性拿手上的STM32来自己写驱动代码,将6个舵机给驱动起来。
舵机控制原理
舵机的控制原理还是比较简单的,而且控制的角度和精度能够比较好的按照开发者的意愿来进行,因此经常被应用与一些控制类器械中,如机械手、云台、2自由度摄像头等产品中。
舵机的外接线一般分为3根线,电源线、地线和信号线,而控制舵机转动,就是通过信号线给舵机发送一系列的周期信号(一般的舵机的能接收的信号周期为20ms),然后通过控制周期信号的高电平的持续时间来达到控制舵机转动的目的。我手上的舵机就是根据高电平持续时间(0.5ms~2.5ms)来实现0~180的转动的。下面附上一张舵机周期信号控制和转动角度的图片说明。

当然了,周期信号的产生可以使用很多方式,但是使用PWM来控制高电平的占空比不失为一种最好的应用方式。在STM32中,STM32的定时器也都提供有PWM的功能。下面就说明一下STM32输出PWM的具体实现方式。
使用STM32控制单个舵机
在STM32中控制舵机,实际上就是开发STM32上的PWM功能,这部分功能需要配置STM32的定时器和GPIO复用共功能,然后就是通过修改定时器计数器的比较寄存器的数值来达到控制PWM的高电平占空比的目的。
这里以STM32F767为例,说明一下具体的实现。
下面使用到了STM32F7中的定时器3作为主定时器,使用通道4来产生PWM。需要注意的定时器3的通道4是通过GPIOB1接口输出的,因此还需要将GPIOB1配置为复用输出功能。PWM的输出比较极性设置为高。
TIM_HandleTypeDef TIM3_Handler;
TIM_OC_InitTypeDef TIM3_CH4Handler;
//arr 为自动重装值
//psc 为时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3;
TIM3_Handler.Init.Prescaler=psc;
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;
TIM3_Handler.Init.Period=arr;
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM3_Handler);
TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1;
TIM3_CH4Handler.Pulse=arr/2;
TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_HIGH; //设置输出比较极性
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);
}
//htim 为定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_Initure.Pin=GPIO_PIN_1;
GPIO_Initure.Mode=GPIO_MODE_AF_PP;
GPIO_Initure.Pull=GPIO_PULLUP;
GPIO_Initure.Speed=GPIO_SPEED_HIGH;
GPIO_Initure.Alternate= GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}
//设置定时器的比较寄存器的值
void TIM_SetTIM3Compare4(u32 compare)
{
TIM3->CCR4=compare;
}
以上的配置代码完成之后,就可以在主函数里面进行调用,并且使用PWM功能了。
//配置定时器3的自动重装值和时钟预分频数,定时器3使用的时钟为108MHz,这里进行108分频,定时器的预分频设置为1MHz
//自动重装值设置为20000,既该定时器每计数20000次触发一次中断,预分频为1MHz,则周期为20ms
TIM3_PWM_Init(20000-1,108-1)
定时器初始化配置完之后,在主循环之前要对舵机进行一次复位操作,从上面的图中可以看出,在高电平持续时间为1.5ms的情况下,舵机能够回中,因此需要调用以下函数,设置定时器的初始比较寄存器的值为1500,即可以使得定时器复位回中。
TIM_SetTIM3Compare4(1500);
需要说明一下,在修改定时器的比较寄存器的值的过程中,要有一些短暂的延时,以保证舵机将动作执行完成。
使用STM32控制多路舵机
单个舵机控制能够完成之后,就可以尝试使用STM32的定时器控制多路舵机了。STM32的定时器中通用定时器能够产生多达4路PWM输出,高级定时器TIM1和TIM8还能够产生多达7路定时器。因此,我们完全可以使用STM32的一个定时器外设控制多路舵机,通过修改不同通道的比较寄存器的值,来达到控制舵机运动的目的。
下面就以STM32F7控制4路舵机为例,贴出来定时器配置相关代码。
TIM_HandleTypeDef TIM3_Handler; //定时器句柄
TIM_OC_InitTypeDef TIM3_CH1Handler; //定时器3通道1句柄
TIM_OC_InitTypeDef TIM3_CH2Handler; //定时器3通道2句柄
TIM_OC_InitTypeDef TIM3_CH3Handler; //定时器3通道3句柄
TIM_OC_InitTypeDef TIM3_CH4Handler; //定时器3通道4句柄
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3; //定时器3
TIM3_Handler.Init.Prescaler=psc; //定时器分频
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
TIM3_Handler.Init.Period=arr; //自动重装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM3_Handler); //初始化PWM
TIM3_CH1Handler.OCMode=TIM_OCMODE_PWM1;
TIM3_CH1Handler.Pulse=arr/2;
TIM3_CH1Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH1Handler,TIM_CHANNEL_1);//配置TIM3通道1
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_1);//开启PWM通道1
TIM3_CH2Handler.OCMode=TIM_OCMODE_PWM1;
TIM3_CH2Handler.Pulse=arr/2;
TIM3_CH2Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH2Handler,TIM_CHANNEL_2);//配置TIM3通道2
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_2);//开启PWM通道2
TIM3_CH3Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
TIM3_CH3Handler.Pulse=arr/2;
TIM3_CH3Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH3Handler,TIM_CHANNEL_3);//配置TIM3通道3
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_3);//开启PWM通道3
TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
TIM3_CH4Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比,
//默认比较值为自动重装载值的一半,即占空比为50%
TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道4
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道4
}
//此函数会被HAL_TIM_PWM_Init()调用 //htim:定时器句柄 void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_TIM3_CLK_ENABLE(); //使能定时器3 __HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟 GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7; //PA6 PA7 GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推完输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF2_TIM3; //PB1复用为TIM3_CH4 HAL_GPIO_Init(GPIOA,&GPIO_Initure); GPIO_Initure.Pin=GPIO_PIN_1; //PB1 GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推完输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF2_TIM3; //PB1复用为TIM3_CH4 HAL_GPIO_Init(GPIOB,&GPIO_Initure); GPIO_Initure.Pin=GPIO_PIN_0 ; //PB0 GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推完输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF2_TIM3; //PB1复用为TIM3_CH3 HAL_GPIO_Init(GPIOB,&GPIO_Initure); }
//设置TIM3通道1的占空比 //compare:比较值 void TIM_SetTIM3Compare1(u32 compare) { TIM3->CCR1=compare; }
//设置TIM3通道2的占空比 //compare:比较值 void TIM_SetTIM3Compare2(u32 compare) { TIM3->CCR2=compare; }
//设置TIM通道4的占空比 //compare:比较值 void TIM_SetTIM3Compare4(u32 compare) { TIM3->CCR4=compare; }
//设置TIM通道3的占空比 //compare:比较值 void TIM_SetTIM3Compare3(u32 compare) { TIM3->CCR3=compare; }
总结
善于利用STM32单片机的外设资源,能够让开发效率大大提高。
后续就是要对这些函数进行封装,用来作为舵机控制的统一接口,直接传递舵机控制的角度,来达到控制舵机的目的
史海拾趣
|
请帮我分析个程序吧我用40M晶振,那一个时钟周期就是25nsclk为输入,ledout为输出,想每一秒让ledout的电平变化一次程序编译都通过了,但就是下到实验板上后灯不闪请指教 module delay1s(clk,ledout);output ledout;integer count1,count2;//intege ...… 查看全部问答> |
|
昨天花了一天时间画的原理图,这是CPU控制部分,与数据采集板的接口部分还没有打网络标号(包括电平转换的芯片),打算先把这一块板给做出來调软件,本来打算用LPC2103作为从控制器,后来画到后面想想还是换成了MSP430F135,因为LPC2103要用双电源供电,如果 ...… 查看全部问答> |
|
第一部分 Linux下ARM汇编语法尽管在Linux下使用C或C++编写程序很方便,但汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作 ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法 ...… 查看全部问答> |
|
太阳能供电 一块土豆就可以供电,相比较某些产品用苹果展示,土豆应该是更省电的吧,呵呵 利用RF无线传输能量 利用手摇电筒制作成发电机供电 一杯热水就能供电?杯子底部的热电偶产生电能,一样可以供MCU使用 背景资料: 意法半 ...… 查看全部问答> |
|
在千篇一律的DIY、MOD场合里,许多玩家费尽心机所改造出来的灯光效果都是静止的,如果在Lanparty上千人一面的改造MOD比比皆是,你的改造又怎么能脱颖而出呢?增加一个动感的超炫灯光效果势在必行!想像一下在姹紫嫣红的炫目光彩照耀下,观众早已疲 ...… 查看全部问答> |
|
在获取滚动条的SB_THUMBPOSITION事件位置值,总是不对 当触发滚动条的SB_THUMBPOSITION事件时,我把滚动条的滑块托到最后,得到值不是SCROLLINFO中的nMax值,为什么啊。… 查看全部问答> |
|
猎头职位:Hardware Design Engineer (欧洲一家做控制系统的公司,地点:上海) Listed in London Stock Exchange, our client is a global group of five businesses that supply control and automation solutions that deliver efficiency, safety and effectiveness in process automation, controls and transportation e ...… 查看全部问答> |




