前言
前两天用F28335控制四位共阳数码管玩了一下(链接在这),这两天换一个,用F28335来控制舵机。
舵机控制原理
PWM原理
PWM(Pulse Width Modulation),指脉冲宽度调制,是一种模拟控制方式。它的思想很简单,就是一种等效的思想:一个5V的信号作用0.5秒可以等效成一个2.5V的信号作用1秒。所以通过控制信号的通断时间,就可以在恒压信号的基础上输出不同大小的信号。
舵机相关知识
舵机由外壳、电机、控制电路以及减速器组成,因为常用于舵面(飞行器操纵面)的控制,所以叫舵机。根据转动范围,舵机可以分为90°、180°和360°舵机等。我手头上的这个是360°舵机,所以它可以做整圈转动。360°舵机和其他舵机不一样的是,其他舵机可以控制其转动角度,而360°舵机只能控制其转动速度和转动方向。
(图片来自网络,侵删)
舵机一般有三根线,分别是电源线、地线以及信号线。舵机的控制是通过在信号线上施加不同占空比的PWM信号而实现的。
实现过程
PWM模块配置
PWM信号的配置主要是周期以及占空比,和控制开关器件不一样,由于这里PWM是作为舵机的控制信号,不需要避免开关器件同时接通的问题,因此不需要对死区做出配置。
首先进行相关的宏定义,以便理解和后期修改,包括时钟频率、PWM频率和周期,代码如下:
#if (CPU_FRQ_150MHZ)
#define CPU_CLK 150e6 // SYSCLK = 150MHz
#endif
#if (CPU_FRQ_100MHZ)
#define CPU_CLK 100e6 // SYSCLK = 100MHz
#endif
#define TBCTLVAL 0x200E // Up-down cnt, timebase = SYSCLKOUT 控制寄存器的值
#define SERVO_CLK 50 // SERVOCLK = 50Hz
#define SERVO_SP CPU_CLK/(2*SERVO_CLK*8*12) // 周期寄存器的值,由于采用增减计数,所以周期寄存器的值为系统时钟频率除PWM频率再除2
然后需要对PWM的输出管脚进行配置,用的是 InitEPwm1Gpio 函数,这个函数是自带的例程函数,位于 DSP2833x_EPwm.c 文件中,代码如下:
void InitEPwm1Gpio(void)
{
EALLOW;
/* Enable internal pull-up for the selected pins */
// Pull-ups can be enabled or disabled by the user.
// This will enable the pullups for the specified pins.
// Comment out other unwanted lines.
GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // Enable pull-up on GPIO0 (EPWM1A)
GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0; // Enable pull-up on GPIO1 (EPWM1B)
/* Configure ePWM-1 pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be ePWM1 functional pins.
// Comment out other unwanted lines.
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // Configure GPIO0 as EPWM1A
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // Configure GPIO1 as EPWM1B
EDIS;
}
最后,需要对PWM的各个模块进行配置,包括时基模块、比较计数模块、动作模块、死区模块、错误联防模块、事件触发模块以及斩波模块。不过由于我们这个是基础应用,因此死区模块、错误联防模块、事件触发模块和斩波模块我们实际上并没有涉及。配置代码如下:
void ePWM1Setup()
{
// clear time-base submodule
EPwm1Regs.TBSTS.all = 0; // clear TBSTS
EPwm1Regs.TBPHS.half.TBPHS = 0; // set TBPHS to 0
EPwm1Regs.TBCTR = 0; // clear TB counter
// configure counter-compare submodule
EPwm1Regs.CMPCTL.all = 0x50; // SHDWAMODE = 1, SHDWBMODE = 1, disable shadow mode
EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0;
EPwm1Regs.CMPCTL.bit.SHDWBMODE = 0; // enable shadow mode
EPwm1Regs.CMPA.half.CMPA = SERVO_SP * (1 - 0.05); // dutycycle of ePWMxA
EPwm1Regs.CMPB = SERVO_SP / 8; // dutycycle 0f ePWMxB
// configure action-qualifier submodule
EPwm1Regs.AQCTLA.all = 0x0060; // CAD = 01: EPwm1A = 0 when CTR = CMPA and the counter is decrementing;
// CAU = 10: EPwm1A = 1 when CTR = CMPA and the counter is incrementing;
EPwm1Regs.AQCTLB.all = 0x0900; // CBD = 10: EPwm1B = 1 when CTR = CMPB and the counter is decrementing;
// CAU = 01: EPwm1A = 0 when CTR = CMPB and the counter is incrementing;
EPwm1Regs.AQSFRC.all = 0; // no effect under immediate mode
EPwm1Regs.AQCSFRC.all = 0; // continuous force disabled
// configure dead-band submodule
EPwm1Regs.DBCTL.all = 0x03; // POSEL = 00: Neither EPWMxA nor EPWMxB is inverted (default);
// OUT_MODE = 11: Dead-band is fully enabled for both rising-edge delay on output EPWMxA and falling-edge
// delay on output EPWMxB;
// IN_MODE = 00: EPWMxA In is the source for both falling-edge and rising-edge delay.
EPwm1Regs.DBRED = 0;
EPwm1Regs.DBFED = 0; // delay for rise-edge and fall-edge respectively
// configure trip-zone submodule
EPwm1Regs.TZSEL.all = 0; // clear all TZ registers because we don't use it
EPwm1Regs.TZCTL.all = 0;
EPwm1Regs.TZEINT.all = 0;
EPwm1Regs.TZFLG.all = 0;
EPwm1Regs.TZCLR.all = 0;
EPwm1Regs.TZFRC.all = 0;
// configure event-trigger submodule
EPwm1Regs.ETSEL.all = 0x000A; // SOCBEN = 0 SOCAEN = 0: EPWMxSOCB EPWMxSOCA disabled;
// INTEN = 1: interrupt enabled;
// INTSEL = 010: interrupt when TBCTR = TBPRD
EPwm1Regs.ETFLG.all = 0;
EPwm1Regs.ETCLR.all = 0;
EPwm1Regs.ETFRC.all = 0;
EPwm1Regs.ETPS.bit.INTPRD = 11;
// configure PWM-chopper submodule
EPwm1Regs.PCCTL.all = 0; // clear PCCTL register because we don't use it
// configure time-base submodule
EPwm1Regs.TBCTL.all = 0x2D8E; // FREE,SOFT = 00: Stop after the next time-base counter increment or decrement;
// PHSDIR = 1: Count up after the synchronization event
// CLKDIV = 011 HSPCLKDIV = 011: TBCLK = SYSCLKOUT/8/6
// SWFSYNC = 0: no effect
// SYNCOSEL = 00: the source of the EPWMxSYNCO signal is set to EPWMxSYNCI
// PRDLD = 1: shadow registers disabled
// PHSEN = 1: Load the time-base counter with the phase register
// CTRMODE = 10: Up-down-count mode
EPwm1Regs.TBCTL.bit.CLKDIV = 100;
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 110;
EPwm1Regs.TBPRD = SERVO_SP;
}
初步效果
360度舵机转动效果
![](/qrcode.jpg)
转速、方向控制
在前面我们只是使舵机转起来了,但是我们还需要去控制它的转速以及方向。这要求我们清楚不同占空比所对应的舵机转速以及方向,所以在上面的基础上增添如下内容,每隔一定时间改变PWM信号的占空比,观察舵机的表现,代码如下:
cnt_PWM++;
if (duty_cycle_2 >= 0.1)
duty_cycle_2 = 0;
if (cnt_PWM % 4 == 0)
duty_cycle_2 += 0.01;
EPwm1Regs.CMPA.half.CMPA = SERVO_SP * (1 - duty_cycle_2);
实验过程如下面视频所示:
不同占空比下的舵机表现
根据实验过程,可以整理得下表:
占空比/% | 舵机表现 |
---|---|
0 | 不转 |
1 | 顺时针较快转动 |
2 | 同上 |
3 | 顺时针较慢转动 |
4 | 逆时针更慢转动 |
5 | 逆时针较快转动 |
6 | 同上 |
7 | 不转 |
8 | 同上 |
9 | 同上 |
10 | 同上 |
由上表可以清楚看到,作用不同占空比的PWM信号,舵机表现出不同的转速以及方向,不过这个只是作为参考的目测结果,具体的转速多少需要后期结合其他模块进一步测量。
注意事项
- 由于开发板能力有限,无法直接给舵机供电,所以我们这里采用开关电源给舵机供电。这个时候要注意将开关电源的COM端与开发板的地端相连,不然舵机无法正常运行。这应该是因为不相连的话就没有共地,会导致PWM信号与舵机内部的基准信号的比较出现问题。当时调试了很久都没反应,还以为是舵机坏了或者是PWM模块配置有问题。
- 另外,由于舵机控制所需的PWM信号频率较低,因此如果直接用系统时钟作为时钟源的话,周期值会超过周期寄存器的上限,因此需要设置时基控制寄存器TBCTL的CLKDIV位和HSPCLKDIV位来对系统时钟进行分频。
总结
至此,我们就成功利用F28335的PWM模块来产生控制信号,驱动舵机的旋转。同时,依然,由于本人水平有限,这个实验各个方面肯定都还有可以改进的地方;另外,文中的知识阐述可能也会有错误的地方,欢迎大家批评指正。还有的话,从视频可以看到,舵机运行过程中有抖动,没看出来是什么原因,希望大佬们可以提出宝贵意见orz