欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32F407配置pca9685驱动

程序员文章站 2022-04-01 16:48:49
...

STM32F407配置pca9685驱动

STM32F407配置pca9685驱动
pca9685是16路12位PWM信号发生器,可用于控制舵机、led、电机等设备,i2c通信,节省主机资源。在淘宝上随处可见,Arduino用它非常方便,不过STM32要想使用它必须要写好驱动才行,本文简述如何配置其驱动以及一些需要注意的地方。

pca9685简介

当然对于老手,看datasheet是最好的选择pca9685datasheet
对于我这种新手,自然是看高手的文章比较容易上手了~

我在网上看到的对pca9685总结的最好的一篇文章推荐给大家:生命不息折腾不止
博主写得非常详细,本文某些片段也是参考博主的这篇文章,在此致谢。

pca9685用的是IIC通信,如果有同学对IIC不熟悉,要先去了解一下~

STM32F407配置pca9685驱动

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波的频率,看下面这个公式即可:
STM32F407配置pca9685驱动
其中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双轴舵机:
STM32F407配置pca9685驱动
周期20ms,所以在设置频率的时候要设为50;占空比0.5ms~2.5ms分别对应0°~180°,且成线性关系,所以计算公式为:

PWM=4096×0.5+(2.50.5)×angle/18020

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)