二、舵机驱动原理及使用PWM驱动的意义
众所周知,舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。在高档遥控玩具,如飞机、潜艇模型,遥控机器人中已经得到了普遍应用。他可以根据输入脉冲信号的高电平时间直接输出对应的角度,高电平时间和角度的关系如下图所示:
可以看到上图中每一个脉冲的周期是20ms(相邻两个上升沿信号之间的时间差为20ms),即频率为50HZ,其次脉冲宽度为0.5ms舵机处于0度位置,脉冲宽度为2.5ms,舵机处于180度位置,脉冲宽度为1.5ms舵机处于90度位置,中间的角度就可以按照这个关系去映射咯。
当然上述的波形直接用软件的延时也是可以时间的对吧,例如我要转到0度,我也可以这样弄。现在ISP软件的软件延时计算器里生成如下两个延时:
然后假设使用P60端口那么将端口初始化为准双向口或者推挽输出口,随后写如下代码就可以将舵机保持在0度了对不。
- P60=1;
- Delay500us();
- P60=0;
- Delay500us();
- Delay19ms();
复制代码
当然,用延时也能输出的波形,定时器当然也可以,但是用延时太浪费MCU的性能,尤其是多路舵机同时以不同的角度控制的话,简直生不如dead;这里强烈不建议,定时器的话虽然能解决CPU占用的问题,但是他会频繁的进入中断,对于我们做产品的人来说,如果能有最优解那么定时器的方法并不可取。这里直接使用硬件的PWM,我们只要配置好周期,往比较寄存器写入比较值就能直接输出PWM了,不需要进入中断,更不需要延时,只要MCU不断电,这个PWM能跑到地老天荒,毕竟这是硬件实现的,稳定性更高,精度更高。
三、代码的实现
这里直接贴上我们的测试代码再来分段讲解:
复制代码
注:以上代码基于开天斧的试验箱程序“25-高级PWM1-PWM2-PWM3-PWM4,驱动P6口呼吸灯实验程序”改编。
上述代码:
1.main函数之前就是头文件应用,寄存器定义,变量定义等,具体的可以查阅手册对照哈,这里暂时先不赘述了。
2.main函数开始先将打开这个P_SW2的寄存器的最高位,因为手册说过要用一些特殊寄存器必须打开这,我们的PWM的寄存器都在这里面。
3.然后就是PWMx_Flag和PWMx_Duty(x取值0-3)这两个变量的初始化了,PWMx_Flag是用来设定当前PWM的高电平时间是越来越大还是越来越小的,PWMx_Duty就是当前PWM的高电平计数值。PWMx_Flag为0,PWMx_Duty会往上计数,数值越来越大,角度也越来越大,反之亦然。
4.然后是定时器的初始化,我们想要他每隔一定的时间转动一定的角度,那么这个“一定的时间”怎么来的,我们就用定时器来实现咯,定时器初始化如上,这里初始化为1ms一次,当然自己不会使用定时器代码的话,我们依旧可以借助工具——ISP软件
5.然后开始是PWM的初始化部分,
5.1 先是PWMA_CCER1 = 0x00; PWMA_CCER2 = 0x00;这里先把捕获/比较寄存器的数值全部清空,在写模式
5.2 然后PWMA_CCMR1 = 0x68; 这一步主要是为了将PWM设置为“PWM模式1”
上图看起来可能不太清晰,这里直接上图,如下图,运行的时候有一个计数器PWM_CNT,如果是向上计数他会从0开始计数到我们设定的PWM_ARR的值,到了设定值会变成0重新开始计数,这个计数值我们称之为PWM_CNT,然后我们设定一个比较值PWM_CCR,只要CNT小于CCR,就输出高电平,反之输出低电平,那么我们只要设置好ARR就可以调节输出的频率,调节CCR就可以改变输出的高电平时间对不对。(一个PWM有4组通道,4组占用一个ARR也就是四组PWM频率相同,每组通道有独立的CCR,也就是每组通道的占空比都可以自由设定,且每组通道都有PWMA_CCMR寄存器,也就是每组PWM都可以设置独立的工作模式)
5.3这里PWMA_CCER1 = 0x55; 这里主要是为了开启捕获/比较的通道和开启比较输出。
5.4 这里四行是为了设定我们的频率,和上图的5.2图中所讲的一样,设定ARR的值。
PWMA_PSCRH = (u8)(PWM_PSCR>>8); //设置分频
PWMA_PSCRL = (u8)(PWM_PSCR);
PWMA_ARRH = (u8)(PWM_PERIOD >> 8); //设置周期时间
PWMA_ARRL = (u8)PWM_PERIOD;
计算的频率公式如上所示,这里PWM_PSCR = 99,PWM_PERIOD = 4799,主频24M,这里我们设置为边沿对齐模式(后面讲)所以输出频率= 24000000/(99+1)/(4799+1)=50hz刚刚好:lol。
5.5下面几行代码就是控制哪几个通道输出。我们之前提过一个PWM有四组通道,一组有2个输出通道,这里把八个都打开了,其实我们驱动一舵机只要一个就够了,这里我们就先不去动他(我懒!)
PWMA_ENO = 0x00;
PWMA_ENO |= ENO1P; //使能输出
PWMA_ENO |= ENO1N; //使能输出
PWMA_ENO |= ENO2P; //使能输出
PWMA_ENO |= ENO2N; //使能输出
PWMA_ENO |= ENO3P; //使能输出
PWMA_ENO |= ENO3N; //使能输出
PWMA_ENO |= ENO4P; //使能输出
PWMA_ENO |= ENO4N; //使能输出
5.6 下面几行代码就是选择输出的通道,因为我们有好多组通道可以选择,如果这
时候刚巧某个通道的某一个IO口被占用了,我们就可以换一个,具体可以如图
PWMA_PS = 0x00; //高级 PWM 通道输出脚选择位
PWMA_PS |= PWM1_3; //选择 PWM1_3 通道
PWMA_PS |= PWM2_3; //选择 PWM2_3 通道
PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
PWMA_PS |= PWM4_3; //选择 PWM4_3 通道
可以看下图,我们这个程序里配置的就是P6端口的输出,当然你也可以指定你要的端口,只要这表里有就可输出。
5.7 PWMA_BKR = 0x80; 因为之前我们已经开启了CC1E位,我们这里遵循手册只要打开MOEn这个位就可以开启OC和OCn的输出了。
5.8 PWMA_CR1 |= 0x01; 这里设置了边沿模式,并且开启了向上计数的模式
至此PWM的地方就配置完成了。当然我们自己写代码可以不需要从头到尾深究每一个寄存器,我们可以参考手册的案例,参考试验箱例程,基于这个修改自己想要的模式或者功能就好了,非常的方便。
最后,我们在定时器中断里,如果PWM1_Flag是0,就是向上计数,每隔50ms让PWM1_Duty增加10,让角度越来越大到了最大数值就把PWM1_Flag变成1,每隔50ms让PWM1_Duty减少10,角度越来越小,记得这个变量更新了以后,把我们最后的数值写入CCR的比较值寄存器,是不是初始化完了输出PWM就非常的方便了,修改这一个寄存器就可以输出我们想要的任意波形。我们这个程序最终就是4路舵机(P60,P62,P64,P66四个端口)角度在0-180度之间不断的来回运动。这里需要注意舵机不要突然给他一个瞬态变化的值,不然瞬态电流会非常大,比如你上一秒还是0度的数值,下一秒就输出180度,我们要给他缓缓的变换角度,0先到45,再到90,再到135,再到180,千万别一下就给他那啥了~另外我们这个试验用的小舵机,大功率的舵机请单独供电,不然MCU容易掉电重启。
还有这个最大值和最小值是怎么来的,因为计数的PWM_ARR是不是4799,从0-4799总共4800个数字。已知4800数值对应的是20ms的周期,我们想要高电平时间时间是10ms,那么这个CCR的数值就是2400对不对,我们想要高电平时间是5ms,那么这个CCR的数值就是1200对不对,所以这里我们最小的高电平时间是0.5ms,最大是2.5ms,然后你们也会计算了吧~
四、测试结果
先用示波器观察我们的输出波形是不是我们写的这个样子,示波器的表笔接P60,地线接上,然后观察是波形。
可以看到示波器自动测量的参数,在屏幕下方有显示,频率,周期和正脉宽的信息,频率基本稳定在50,周期约为20ms,正脉宽在500us-2.5ms之间变化,这就是我们的代码要实现的效果