历史上的今天
返回首页

历史上的今天

今天是: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单片机的外设资源,能够让开发效率大大提高。


   后续就是要对这些函数进行封装,用来作为舵机控制的统一接口,直接传递舵机控制的角度,来达到控制舵机的目的


推荐阅读

史海拾趣

AEMC Instruments公司的发展小趣事

随着产品的不断完善和升级,AEMC Instruments公司开始积极拓展市场。公司加强与国际市场的合作,通过参加国际展会、建立全球分销商网络等方式,将产品推向全球。同时,公司还积极寻求与国际知名企业的合作,共同研发新产品和技术,以进一步提升产品的竞争力。这些举措不仅扩大了公司的市场份额,也提高了公司的国际知名度。

Bellin公司的发展小趣事

AEMC Instruments公司始终注重产品品质和质量管理。公司建立了完善的质量管理体系,并通过了多项国际认证,如ISO 9001质量管理体系认证等。这些认证不仅证明了公司在品质管理方面的实力,也提高了客户对产品的信任度。同时,公司还加强了对供应商和生产过程的管理,确保从原材料采购到产品生产的每一个环节都符合国际标准和客户要求。

CHERRY公司的发展小趣事

Cherry公司的研发部门一直是其核心竞争力所在。在一位年轻小伙子的带领下,Cherry在1983年成功研发出了具有里程碑意义的MX机械轴。这一创新产品不仅提升了键盘的使用寿命和舒适度,还解决了多个按键同时按下时的冲突问题。MX轴的诞生标志着Cherry在机械键盘领域取得了重大突破,也为公司赢得了更多的市场份额。

First Switchtech公司的发展小趣事

面对日益激烈的全球竞争,First Switchtech公司(或类似公司)制定了积极的国际化战略。公司首先在欧洲市场设立了分支机构,通过深入了解当地市场需求和文化背景,成功推出了符合欧洲标准的电子开关产品。随后,公司进一步拓展北美、亚洲等市场,通过本地化生产和营销策略,实现了全球市场的覆盖。这一过程中,公司不仅提升了品牌知名度,还积累了丰富的国际市场运营经验。

科达嘉CODACA公司的发展小趣事

科达嘉电子高度重视质量管理,先后通过了ISO9001质量管理体系、ISO14001环境管理体系认证。公司不仅建立了完善的质量检测流程,还成立了专门的检测中心,对产品进行严格的质量把控。这些措施确保了科达嘉电子产品的稳定性和可靠性,进一步提升了公司在市场上的竞争力。

Glorious Sources Co Ltd公司的发展小趣事
如加入自动增益控制(AGC)电路以自动调节放大倍数,或加入数字信号处理技术以提高信号处理的精度和效率。

问答坊 | AI 解惑

关于verilog的一个问题

请帮我分析个程序吧我用40M晶振,那一个时钟周期就是25nsclk为输入,ledout为输出,想每一秒让ledout的电平变化一次程序编译都通过了,但就是下到实验板上后灯不闪请指教 module delay1s(clk,ledout);output ledout;integer count1,count2;//intege ...…

查看全部问答>

一些变频器说明书

大家共享,希望大家有更多的变频器,PLC,伺服驱动等资料与大家共享 [ 本帖最后由 yurongchun 于 2008-5-27 10:38 编辑 ]…

查看全部问答>

05月11日 CPU部分电路图,待商榷版

昨天花了一天时间画的原理图,这是CPU控制部分,与数据采集板的接口部分还没有打网络标号(包括电平转换的芯片),打算先把这一块板给做出來调软件,本来打算用LPC2103作为从控制器,后来画到后面想想还是换成了MSP430F135,因为LPC2103要用双电源供电,如果 ...…

查看全部问答>

Linux下ARM汇编点滴

第一部分 Linux下ARM汇编语法尽管在Linux下使用C或C++编写程序很方便,但汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作 ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法 ...…

查看全部问答>

ST五张图片展现STM8L的低功耗

太阳能供电 一块土豆就可以供电,相比较某些产品用苹果展示,土豆应该是更省电的吧,呵呵 利用RF无线传输能量 利用手摇电筒制作成发电机供电 一杯热水就能供电?杯子底部的热电偶产生电能,一样可以供MCU使用 背景资料: 意法半 ...…

查看全部问答>

自制简单LED频闪灯

在千篇一律的DIY、MOD场合里,许多玩家费尽心机所改造出来的灯光效果都是静止的,如果在Lanparty上千人一面的改造MOD比比皆是,你的改造又怎么能脱颖而出呢?增加一个动感的超炫灯光效果势在必行!想像一下在姹紫嫣红的炫目光彩照耀下,观众早已疲 ...…

查看全部问答>

动了谁的PPP?

把手机作为modem连接PC,用PC的调制解调器拨号时,究竟modem的PPP协议栈是否工作?谁看过相关的源码能确定这个问题。…

查看全部问答>

在获取滚动条的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 ...…

查看全部问答>