这里写自定义目录标题
- 前言
- PWM概述
- 代码讲解
- 舵机原理
前言
在学习的过程中发现,及时复习是一个好的学习习惯,本文记录使用STM32F407ZGT6控制20KG 270度舵机的学习过程,以便日后复习。
PWM概述
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。广泛应用在从测量、通信到功率控制与变换的许多领域中。简单一点,就是对脉冲宽度的控制,PWM 原理如图:
“图一”
其中:
CNT:计数器
ARR:重装值寄存器
CCRx:比较寄存器
通过参考手册中可知,stm32f407出了定时器6,7没有产生PWM功能,其他定时器都有。我上网看了一些教程,发现大多都是利用PA0
的定时器2通道1产生PWM,但是我PA0使用了按键触发外部中断,所以换成了PA1定时器2通道2。
在配置PWM之前,再来说一个知识,占空比。
占空比:占空比是指在一个脉冲循环内,通电时间(可能是高电平也可能是低电平)相对于总时间所占的比例。
这里我上网找了一张图片,其实很简单,就是脉冲宽度占总时间的比例,就是占空比。
代码讲解
废话不多说,我们以便将代码,一边说,上代码:
首先初始化结构体,开启定时器2和端口A的时钟,这没什么好说的。
GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM2时钟使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA时钟
然后看stm32f407ZGT6的引脚定义,这小小的一步其实很关键,记得刚开始学习的时候没有看引脚定义,当时用到了PA14,但是PA14主功能并不是普通的GPIO,所以怎么都不能成功看到效果,我找到了PA1的引脚定义如下图
另外,我还找到了全面的STM32F407ZGT6引脚功能表,大家要使用请自动移步(好吧,只是我懒,懒得在搬过来了,见谅)https://blog.csdn.net/u011510016/article/details/100404880
通过图中可以看出PA1的主功能就是PA1,但是我们要使用它的定时器功能,所以要复用一下,关于复用的概念,我就不在此赘述了
直接上代码:
GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM2); //GPIOA1复用为定时器2
这个函数很好理解啊,就是把A端口1号引脚复用为定时器2模式
这里我帮大家找到了这个函数的注释,大家英语水平都很好,咱也就不翻译了,直接看就好,是不是嘎嘎简单!
接下来就是引脚的配置了
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //GPIOA1GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA1
下面关于定时器的配置还是有必要好好絮叨絮叨滴,我先把代码奉上,然后一点点讲解
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);//初始化定时器2//初始化TIM2 Channe2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较使能输出TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低TIM_OCInitStructure.TIM_Pulse = 0;//比较初始值TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2 OC2TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM2在CCR2是的预装载寄存器TIM_ARRPreloadConfig(TIM2,ENABLE);//ARPE使能TIM_Cmd(TIM2, ENABLE); //使能TIM2
其实也不是很多,靓仔,听我一一道来:
我们先来看定时器分频和自动重装载值,这两句我觉得是最重要的了
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
这里我又为大家找来了一张图
大家不要看别的,直接看这
定时器2是普通定时器,它使能APB1硬件时钟,根据中文开发指南
APB1先是4分频,所以预分频要×2
定时器2时间计算方法: 频率=(168/4×2)/TIM_TimeBaseStructure.TIM_Prescaler
计数时间=TIM_TimeBaseStructure.TIM_Period /频率
就比如我的定时器分频TIM_TimeBaseStructure.TIM_Prescaler=psc;中psc为8399,那么它的频率就为84MHZ/8400=10000HZ,到此肯定会有靓仔有疑惑了——你他喵的psc定义的为8399,那么为什么你计算频率的时候除了8400呢,你小子框我
有问题很好,但是我没有骗你,为啥呢,因为我的计数初值为0,而0-8399为8400个数,所以是除8400!
好了,我们继续。
知道了我们的频率为10KHZ,那么就意味着计10000个数对应的时间为1秒,也就是说周期就为1/10000秒,也就是100ms。
好,分频值解释好了,我们再来说自动重装载值arr得定义意味着啥
看我文章的第一张图,也就是图一(好吧,这句话确实是句废花),自动重装载值,见名知意,就是自动重新装载的值(喵的,好像又是一句废花),就比如你设置从0开始计数,自动重装载值为100,那么计数就是0,1,2,3,…99,100,然后接下来自动又变为0,1,2,3,…99,100。(好吧,只可意会不可言传,受不了了,我就只能这么解释了)这两句解释完了,我们接着看。
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
PWM模式也是一个细节,在手册中是这么解释的:
以通用定时器 (TIM2 到 TIM5)计数模式为例:
110:PWM 模式 1––在递增计数模式下,只要 TIMx_CNT<TIMx_CCR1,通道 1 便为有 效状态,否则为无效状态。在递减计数模式下,只要 TIMx_CNT>TIMx_CCR1,通道 1 便为 无效状态 (OC1REF=0),否则为有效状态 (OC1REF=1)。
111:PWM 模式 2––在递增计数模式下,只要 TIMx_CNT<TIMx_CCR1,通道 1 便为无 效状态,否则为有效状态。在递减计数模式下,只要 TIMx_CNT>TIMx_CCR1,通道 1 便为 有效状态,否则为无效状态
通道有效,则输出你设定的有效电平(极性电平)
通道无效,则输出你设定与有效电平相反电平(极性电平)
总结就是。
PWM模式1一只要TIMx_ CNT< TIMx_ CCR1,通道1便为有效状态,否则为无效状态。
PWM模式2-一只要 TIMx_ CNT < TIMx _CCR1,通道1便为无效状态,否则为有效状态。
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2 OC2
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM2在CCR2是的预装载寄存器
TIM_OC2Init、TIM_OC2PreloadConfig这两个函数,其实就是指定是哪个通道的,之前都是配置定时器2,但是我们用到的是通道2,但是之前并没有配置通道2,这里配置,在这里我还专门为大家找了函数解释
看英文的第一句,就是Channel2,不用我多说,懂得都懂。
对比一下TIM_OC1Init的解释就是Channel1。
OK,最后再来看一下main函数,在main初始化中写
TIM2_PWM_Init(199,8399);
大家可以算一下,我这个PWM的周期,很简单为20ms。讲了半天PWM,终于要到舵机了。
舵机原理
舵机和PWM之间有啥关系呢?
对于一般的舵机来说,所对应的PWM波的周期为20ms,即舵机接收的PWM信号频率为50HZ。
其空占比与转动角度的关系。普通舵机的舵量是0~180°。脉冲宽度范围 0.5ms ~ 2.5ms,我又为大家找了张图,说明一下,这图是我引用的
但是,我们使用的舵机为270度,并不是180度,所以我们还要重新映射一下,我去网上看了一下卖家给的信息,他是这么说的
就是0.5ms对应0度,2.5ms对应270度,1.5ms对应135度,我列了张表格,这个如果看懂了上面的计算方法,自己很容易就可以计算出来对应的数值
输入信号脉冲宽度 | 0.5 | 0.83 | 1.16 | 1.5 | 1.83 | 2.16 | 2.5 |
---|---|---|---|---|---|---|---|
舵机输出角度 | 0 | 45 | 90 | 135 | 180 | 225 | 270 |
Compare2 | 5 | 8 | 12 | 15 | 18 | 22 | 25 |
其中Compare2我解释一下来自void TIM_SetCompare2(TIM_TypeDef* TIMx, uint32_t Compare2)这个函数
这个函数,可以改变指定定时器的CCRX值,这里所对应的时间大家可以自己计算。
在while(1)中就可以写
Delay_ms(1000); TIM_SetCompare2(TIM2,5); //修改比较值,修改占空比对应0度Delay_ms(1000); TIM_SetCompare2(TIM2,8); //修改比较值,修改占空比对应45度Delay_ms(1000); TIM_SetCompare2(TIM2,12); //修改比较值,修改占空比对应90度Delay_ms(1000); TIM_SetCompare2(TIM2,15); //修改比较值,修改占空比对应135度Delay_ms(1000); TIM_SetCompare2(TIM2,18); //修改比较值,修改占空比对应180度Delay_ms(1000); TIM_SetCompare2(TIM2,22); //修改比较值,修改占空比对应225度Delay_ms(1000); TIM_SetCompare2(TIM2,25); //修改比较值,修改占空比对应270度
来观察舵机的运动情况