一.背景
在上篇,博主给大家介绍了关于舵机的相关知识以及PWM的控制原理,并且根据410c的特点写了个GPIO口模拟PWM的驱动,但是尽管输出的波形图还算稳定,但相对来讲,PWM作为稳定的硬件通讯信号,是需要时刻保持信号的输出,换言之,假如一个周期20ms,脉宽50%的波形,CPU平均在10ms内调度GPIO进行实时电平改变。上篇博客的stree.c驱动里,我们采用在工作队列里udelay的方式来实现(具体参考http://blog.csdn.net/ad3600/article/details/59484467这篇博客),这在linux这种分时调度系统里是相当消耗CPU资源的做法(CPU占用率:android系统里占25%+,纯linux系统占80%+),但如果换成usleep,或者干脆采用软件定时器来做延时控制,这种方式虽然能极大地降低CPU的占用率,但在linux的这类分时系统里无法保证及时的硬件操作的实时响应,实测波形如下:
图1 PWM波形1
图2 PWM波形2
图3 PWM波形3
可以看到一个固定占宽比的波形,抓拍出来的脉宽相当不稳定(110mv 143mv 118mv之间跳动),我们知道在固定频率的PWM下,脉宽的占空比在这里就是相当于舵机的转角控制信号,如此不稳定的信号完全无法满足使用。
那么有什么方式能解决这个问题呢?
二.I2C转PWM
我们曾说过,本质上讲,PWM跟SPI,I2C,UART一样,都是一种通讯协议,那么既然410c没有通讯模块,我们是否可以通过通讯协议转化的方式来实现在410c对PWM信号的输出?
答案是肯定的!
博主找到一款模块来实现I2C转PWM,就是飞利浦很早出的PCA9685驱动模块!
图4 PCA9685实物图
图5 PCA9685 功能框图
PCA9685是一款I2C总线接口的16位LED控制器,该控制器特别为红/绿/蓝/琥珀(RGBA)色的混合应用进行了优化。每个LED输出都有自己12位分辨率(4096级)固定频率的独立PWM控制器。该PWM控制器运行在40Hz到1000Hz范围的频率下,占空比在0%到100%范围内可调,用于设置LED到一个确定的亮度值。所有输出都设置为相同的PWM频率。
每个LED的输出状态可以为关、开(没有PWM控制),或者由其独立PWM控制器的值来确定。LED输出驱动可以编程为在5V电压下具有25mA电流吸收能力(灌电流)的开漏模式或者在5V电压下可吸收25mA灌电流及提供10mA拉电流的推挽模式。PCA9685的工作电压范围为2.3V到5.5V,其输出可承受5.5V电压。LED可以直接连接到LED输出管脚(高达25mA,5.5V),对于大电流或者高电压的LED可以由PCA9685加上外部的驱动器以及少量分立元件来驱动。
在这里,我们将这个模块用来控制我们的舵机!
三.PCA9685驱动
1.实物连接图:因为410c的I2C GPIO输出的信号是1.8V的电平,而PCA9685接收的是5V的电平信号,这里我们用面包板搭了个小小的电平转换电路。
图6 实物连接图
2.linux pca9685驱动
(1).驱动加载
pca9685在linux下的驱动,从linux3.0以后已经集成在代码里,大家可以在~/linux/driver/pwm的目录里找到pwm-pca9685.c的代码,只需要在410c的kernel里遵循i2c从设备注册的规则配置DTS就可以使用。在这里给大家分享我配置的DTS那部分
i2c@78b6000 {
/* On Low speed expansion */
label = "LS-I2C0";
status = "okay";
pca9685@40 {
compatible = "nxp,pca9685-pwm";
reg = <0x40>; /*i2c slave addr*/
};
};
需要注意的是PCA9685的I2C地址是可选的,大家可以通过短接A0~A5的PIN配置I2C地址,默认全不短接的地址是0x40,这个要注意,是个小坑。
(2).驱动测试
将编译好的pca9685.ko insmod到410c的Debian(linux)系统后,大家可以看到在系统在/sys/class/pwm里会有 pwmchip0这个文件结点,而它就是我们注册成功的pwm控制结点,大家可以通过如下操作测试加载的驱动:
1).打开pwm通道0:
sudo chmod 666 /sys/class/pwm/pwmchip0/export
sudo echo 0 > /sys/class/pwm/pwmchip0/export
这时候,我们会看到/sys/class/pwm/pwmchip0目录里会多了个pwm0的目录,这也是0通道的控制目录。
2).设置通道0的PWM 周期和脉宽(这里举例为周期为20ms,脉宽1ms)
sudo chmod 666 /sys/class/pwm/pwmchip0/pwm0/period
sudo chmod 666 /sys/class/pwm/pwmchip0/pwm0/duty_cycle
sudo echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/period /*设置周期20*10^6纳秒*/
sudo echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
将舵机接到通道0上,我们可以动态地控制duty_cycle(脉宽)的值来实现角度控制!
图7 舵机控制图