完整代码会放文章后面
一、IIC地址问题
在淘宝上购买PCA9685,卖家一般会给你发一个资料包,其中的一个PDF文档介绍模块使用的,在介绍IIC地址时设置部分是错的!
A0其实是IIC地址的第1位,并不是第0位,第0位是读写位,写模式设置位0,读模式设置为1。
默认的地址是0x80,即A5到A0都不连接的情况
A0连上时,写操作地址值为1000 0010 地址值:0x82
使用寄存器自动增量读操作时第二次发送从机地址,最后一位的值为1(第一次发送从机地址最后一位时仍为0)
看一下官方文档的介绍
启用寄存器自动增量读操作---->
顺带补上写操作的用法
二、 mode1相关位的设置参考下图
三、 PCA9685初始化操作如下:
/*********************************
PCA9685初始化并设置舵机初始角度值
**********************************/
void pca9685_Init(){
u8 num;
IIC_Init(); //初始化IIC总线
pca9685_Write(PCA9685_MODE1,0x0); //在频率设置之前,必须有 (MODE1所有位置0)
setPWMFreq(50);
for(num=0;num<15;num++){
set_ServoAngle(num,90);
}
}
至于为什么要将mode1置零,可能是为了清除掉上电操作自动运行的旧模式。
上代码:
.h文件
#ifndef _PCA9685_H
#define _PCA9685_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 pca9685_Init(void);
void servo_Test(void);
#endif
.c文件
#include "PCA9685.h"
#include "usart.h"
#include "myiic.h"
#include "delay.h"
#include "math.h"
/****************
向PCA9685写一个字节
reg:寄存器的地址值
data:要写入的值
****************/
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();
}
/****************
从PCA9685读一个字节
读取寄存器的值
reg:寄存器地址值
****************/
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;
}
/****************
设置PCA9685的PWM频率值
freq=50Hz 周期 t=1/50=0.02s=20ms
****************/
void setPWMFreq(u8 freq){
u8 prescale,oldmode,newmode;
double prescaleval;
prescaleval = 25000000.0/(4096*freq*0.915);
prescale = (u8)floor(prescaleval+0.5)-1; //floor --->round down(向下取整)
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); //0xa1:1010 0001
}
/****************
设置每个通道的PWM值
num=0~15
on:
off:
****************/
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);
}
/****************
设置每个通道舵机的角度值
servoNum=0~15
servoAngle=0~180
****************/
void set_ServoAngle(u8 servoNum,u16 servoAngle){
u16 pwm;
pwm=(u16)204.8*(0.5+(servoAngle*1.0/90)); //4096/20ms=204.8/1ms
//(舵机驱动PWM周期是20ms,高低电平变化在0.5ms~2.5ms之间才能有效驱动舵机)
setPWM(servoNum, 0, pwm);
}
/*********************************
PCA9685初始化并设置舵机初始角度值
**********************************/
void pca9685_Init(){
u8 num;
IIC_Init(); //初始化IIC总线
pca9685_Write(PCA9685_MODE1,0x0); //在频率设置之前,必须有 (MODE1所有位置0)
setPWMFreq(50);
for(num=0;num<15;num++){
set_ServoAngle(num,90);
}
}
后面有时间再补上PID和位置控制