相关材料
IIC总线协议
Verilog实现IIC主机对从机的写操作(zybo z7板运行代码)
pca9685的舵机控制
程序框图
单片机IIC控制流程框图
代码实现
IIC实现代码(前面的宏定义适用于MSP430)
#define uchar unsigned char
#define uint unsigned int
//=====================================延时函数=================================
#define CPU_F ((double)8000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
//=====================================IIC地址设置=================================
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)--(6050)
#define SlaveAddress 0x80 //IIC写入时的地址字节数据,+1为读取--(PCA9685)
//=====================================IIC端口定义=================================
#define SCL1 P1OUT|=BIT4
#define SCL0 P1OUT&= ~BIT4
#define SDA1 P1OUT|=BIT5 //IIC数据引脚
#define SDA0 P1OUT&= ~BIT5
#define SDAIN P1DIR&= ~BIT5
#define SDAOUT P1DIR|=BIT5
#define SDADATA (P1IN&BIT5)
//================================I2C起始信号================================
void I2C_Start()
{
SDA1; //拉高数据线
SCL1; //拉高时钟线
delay_us(5); //延时
SDA0; //产生下降沿
delay_us(5);
SCL0; //拉低时钟线
}
//================================I2C停止信号================================
void I2C_Stop()
{
SDA0; //拉高数据线
SCL1;
delay_us(5);
SDA1; //产生上升沿
delay_us(5);
}
//================================I2C发送应答信号 入口参数:ack (0:ACK 1:NAK)================================
void I2C_SendACK(uchar ack)
{
SDAOUT;
if(ack)
SDA1;
else
SDA0;
//SDA=ack; //读应答信号
SCL1;
delay_us(5);
SCL0; //拉低时钟线
delay_us(5);
}
//================================I2C接收应答信号================================
uchar I2C_RecvACK()
{
uchar cy;
SCL1;
SDAIN;
delay_us(5);
if(SDADATA)
{
cy=1;
}
else
{
cy=0;
}
//cy=SDA; //读应答信号
SCL0;
delay_us(5);
SDAOUT;
return cy;
}
//================================向I2C总线发送一个字节数据================================
void I2C_SendByte(uchar dat)
{
uchar i;
for(i=0;i<8;i++) //8位计数器
{
if((dat<<i)&0x80)
{
SDA1;
}
else
{
SDA0;
}
//SDA=cy; //送数据口
SCL1;
delay_us(5);
SCL0;
delay_us(5);
}
I2C_RecvACK();
}
//================================从I2C总线接收一个字节数据================================
uchar I2C_RecvByte()
{
uchar i;
uchar dat=0,cy;
SDA1; //使能内部上拉,准备读取数据,
SDAIN; //8位计数器
for(i=0;i<8;i++)
{
dat<<=1;
SCL1;
delay_us(5);
if(SDADATA)
{
cy=1;
}
else
{
cy=0;
}
dat|=cy; //读数据
SCL0;
delay_us(5);
}
SDAOUT;
return dat;
}
//=======================向I2C设备写入一个字节数据======================================
void Single_WriteI2C(uchar REG_Address,uchar REG_data)
{
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //内部寄存器地址
I2C_SendByte(REG_data); //内部寄存器数据
I2C_Stop(); //发送停止信号
}
//======================从I2C设备读取一个字节数据=====================================
uchar Single_ReadI2C(uchar REG_Address)
{
uchar REG_data;
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //发送存储单元地址,从0开始
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=I2C_RecvByte(); //读出寄存器数据
I2C_SendACK(1); //接收应答信号
I2C_Stop(); //停止信号
return REG_data;
}
//======================================合成数据======================================
int GetData(uchar REG_Address)
{
char H,L;
H=Single_ReadI2C(REG_Address);
L=Single_ReadI2C(REG_Address+1);
return (H<<8)+L; //合成数据
}
单片机调用程序(对应于pca9685驱动板从机)
设置PWM频率
void setPWMFreq(float freq)
{
uint prescale,oldmode,newmode;
float prescaleval;
freq_re=freq;
freq *= 0.96; // 用于纠正频率偏差
prescaleval = 25000000;
prescaleval /= 4096;
prescaleval /= freq;
prescaleval -= 1;
// prescale = floor(prescaleval + 0.5);
prescale=(int)(prescaleval + 0.5 );
oldmode = Single_ReadI2C(PCA9685_MODE1);
newmode = (oldmode&0x7F) | 0x10; // sleep
Single_WriteI2C(PCA9685_MODE1, newmode); //调用之前写的IIC写入函数,go to sleep
Single_WriteI2C(PCA9685_PRESCALE, prescale); // set the prescaler
Single_WriteI2C(PCA9685_MODE1, oldmode);
delay_ms(2);
Single_WriteI2C(PCA9685_MODE1, oldmode | 0xa1);
}
设置pca9685各个舵机输出寄存器的值
// =========================输出通道设置,占空比设置从0到ON跳成高 再到off跳成低====================================
void setPWM(uint num, uint on, uint off) //0--4096
{
Single_WriteI2C(LED0_ON_L+4*num,on);
Single_WriteI2C(LED0_ON_H+4*num,on>>8);
Single_WriteI2C(LED0_OFF_L+4*num,off);
Single_WriteI2C(LED0_OFF_H+4*num,off>>8);
}
// =========================num通道,ms高电平毫秒====================================
void setPWM_ms(uint num,float time_ms)
{
uint time;
time=(uint)((time_ms*freq_re/1000)*4096);
setPWM(num, 0, time) ;
}
/******************************************************************************************************
* 名 称:setARC()
* 功 能:设置舵机角度
* 入口参数:舵机口num,角度arc
* 出口参数:无
* 说 明:
* 范 例:无
******************************************************************************************************/
void setARC(uint num,float arc)
{
float time_ms=arc*2/180+0.5;
setPWM_ms(num,time_ms);
}