STM32F407配置pca9685驱动
STM32F407配置pca9685驱动
pca9685是16路12位PWM信号发生器,可用于控制舵机、led、电机等设备,i2c通信,节省主机资源。在淘宝上随处可见,Arduino用它非常方便,不过STM32要想使用它必须要写好驱动才行,本文简述如何配置其驱动以及一些需要注意的地方。
pca9685简介
当然对于老手,看datasheet是最好的选择pca9685datasheet
对于我这种新手,自然是看高手的文章比较容易上手了~
我在网上看到的对pca9685总结的最好的一篇文章推荐给大家:生命不息折腾不止
博主写得非常详细,本文某些片段也是参考博主的这篇文章,在此致谢。
pca9685用的是IIC通信,如果有同学对IIC不熟悉,要先去了解一下~
pca9685驱动
网上有不少写pca9685的文章,写驱动的也有,不过我试过的大多有错误,我详细地进行了修改,并且调试成功,走了不少弯路。
pcf8574.h:
头文件的话主要就是寄存器地址和IIC通信的函数声明,比较简单。
内部地址(hex) | 名称 | 功能 |
---|---|---|
Harry Potter | Gryffindor | 90 |
Hermione Granger | Gryffindor | 100 |
Draco Malfoy | Slytherin | 90 |
00 | MODE1 | 设置寄存器1 |
01 | MODE2 | 设置寄存器2 |
02 | SUBADR1 | i2c-bus subaddress1 |
03 | SUBADR2 | i2c-bus subaddress2 |
04 | SUBADR3 | i2c-bus subaddress3 |
05 | ALLCALLADR | |
06 | LED0_ON_L | |
07 | LED0_ON_H | |
08 | LED0_OFF_L | |
09 | LED0_OFF_H | |
… | … | … |
0x06 + 4*X | LEDX_ON_L | |
0x06 + 4*X + 1 | LEDX_ON_H | |
0x06 + 4*X + 2 | LEDX_OFF_L | |
0x06 + 4*X + 3 | LEDX_OFF_H | |
… | … | … 上面共16路通道 |
FA | ALL_LED_ON_L | |
FB | ALL_LED_ON_H | |
FC | ALL_LED_OFF_L | |
FD | ALL_LED_OFF_H | |
FE | PRE_SCALE 控制周期的寄存器 | |
FF | TestMode |
照着上面的表格写头文件就好了 - -
#ifndef __PCF8574_H
#define __PCF8574_H
#include "sys.h"
#include "myiic.h"
#define PCA9685_adrr 0x80
#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4
#define PCA9685_MODE1 0x0
#define PCA9685_PRESCALE 0xFE
#define LED0_ON_L 0x6
#define LED0_ON_H 0x7
#define LED0_OFF_L 0x8
#define LED0_OFF_H 0x9
#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD
void PCA9685_write(unsigned char reg,unsigned char data);
u8 PCA9685_read(unsigned char reg);
void setPWMFreq(u8 freq);
void setPWM(u8 num, u16 on, u16 off);
void down();
void up();
#endif
pcf8574.c:
PCA9685的读写操作没什么好说的,直接调用原子哥写好的IIC相关函数即可:
void PCA9685_write(unsigned char reg,unsigned char data)
{
IIC_Start();
IIC_Send_Byte(PCA9685_adrr);
IIC_Wait_Ack();
IIC_Send_Byte(reg);
IIC_Wait_Ack();
IIC_Send_Byte(data);
IIC_Wait_Ack();
IIC_Stop();
}
u8 PCA9685_read(unsigned char reg)
{
u8 res;
IIC_Start();
IIC_Send_Byte(PCA9685_adrr);
IIC_Wait_Ack();
IIC_Send_Byte(reg);
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(PCA9685_adrr|0X01);
IIC_Wait_Ack();
res=IIC_Read_Byte(0);
IIC_Stop();
return res;
}
设置PWM波的频率,看下面这个公式即可:
其中osc_clock是时钟,根据上面的寄存器设置选择是内部25MHz时钟还是外部时钟。
update_rate是频率,比如周期是20ms,那么频率就是50。
注意:实际应用中发现有误差,需要加入校准,要把udpate_rate乘以0.915。
包括从网上下载的arduino驱动中也加入了此校准。
注意要使用浮点型变量作为中间量,否则会计算错误,另外不要忘记四舍五入。
void setPWMFreq(u8 freq)
{
u8 prescale,oldmode,newmode;
double prescaleval;
prescaleval = 25000000.0/(4096*freq*0.915);
prescale = (u8)floor(prescaleval+0.5)-1;
oldmode = PCA9685_read(PCA9685_MODE1);
newmode = (oldmode&0x7F) | 0x10; // sleep
PCA9685_write(PCA9685_MODE1, newmode); // go to sleep
PCA9685_write(PCA9685_PRESCALE, prescale); // set the prescaler
PCA9685_write(PCA9685_MODE1, oldmode);
delay_ms(5);
PCA9685_write(PCA9685_MODE1, oldmode | 0xa1);
}
setPWM函数,向相应的寄存器(LED0_ON_L、LED0_ON_H、LED0_OFF_L、LED0_OFF_H)写值即可。
其中num为舵机PWM输出引脚0~15,on是PWM上升计数值0~4096,off是PWM下降计数值0~4096。
他的PWM发生原理是这样的:
一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时,off/4096的值就是PWM的占空比。
void setPWM(u8 num, u16 on, u16 off)
{
PCA9685_write(LED0_ON_L+4*num,on);
PCA9685_write(LED0_ON_H+4*num,on>>8);
PCA9685_write(LED0_OFF_L+4*num,off);
PCA9685_write(LED0_OFF_H+4*num,off>>8);
}
为了方便使用,我写了一个输入角度输出相应PWM值(off值)的函数。
这个要针对舵机进行修改,我用的是乐幻索尔的LDX218双轴舵机:
周期20ms,所以在设置频率的时候要设为50;占空比0.5ms~2.5ms分别对应0°~180°,且成线性关系,所以计算公式为:
u16 calculate_PWM(u8 angle){
return (int)(204.8*(0.5+angle*1.0/90));
}
然后就是一些动作组了,这个就随便写一个8个舵机全部置零位和全部置90°:
注意IIC通信间隔的问题,每调用一次setPWM()函数就要延迟1ms,否则会出现莫名其妙的BUG,这个很重要~
void down(){
u16 pwm = calculate_PWM(0);
setPWM(0x0,0,pwm);
delay_ms(1);
setPWM(0x1,0,pwm);
delay_ms(1);
setPWM(0x2,0,pwm);
delay_ms(1);
setPWM(0x3,0,pwm);
delay_ms(1);
setPWM(0x4,0,pwm);
delay_ms(1);
setPWM(0x5,0,pwm);
delay_ms(1);
setPWM(0x6,0,pwm);
delay_ms(1);
setPWM(0x7,0,pwm);
}
void up(){
u16 pwm = calculate_PWM(90);
setPWM(0x0,0,pwm);
delay_ms(1);
setPWM(0x1,0,pwm);
delay_ms(1);
setPWM(0x2,0,pwm);
delay_ms(1);
setPWM(0x3,0,pwm);
delay_ms(1);
setPWM(0x4,0,pwm);
delay_ms(1);
setPWM(0x5,0,pwm);
delay_ms(1);
setPWM(0x6,0,pwm);
delay_ms(1);
setPWM(0x7,0,pwm);
}
应用与调试
我是想做一个用蓝牙控制一个四足机器人的,机器人有8个舵机,完整的工程代码已经跑通,感兴趣的可以下载看看:
蓝牙控制8个舵机(STM32F407+pca9685+HC06)
推荐阅读
-
Struts2(二)— Result结果配置、Servlet的API的访问、模型驱动、属性驱动
-
MyEclipse 配置SQL Server 2008数据库驱动操作步骤
-
centos7 无线网卡驱动的安装及无线网络的配置详解
-
安装ElasticSearch搜索工具并配置Python驱动的方法
-
Docker 生产环境之日志 - 配置日志驱动程序的输出
-
ThinkPad 安装 Ubuntu 18.10 系统 -- 高分屏各项配置与Nvdia独显驱动
-
Win10下VS2015(WDK10)驱动开发环境配置
-
Ubuntu16.04 配置显卡驱动+CUDA10.1+cudnn
-
海思Hi3559A/CV100 DDR4驱动配置手册
-
大数据驱动力 社会资本的全球配置