这段时间在做公司的一个项目,由于需要用到比较多的IO口,硬件方面便采用PCA6416进行扩展,采样IIC通信。现将自己的学习过程分享,希望与志同道合之人共同进步。本次IIC通信,主机为STM32 105, 从机为PCA6416
个人觉得IIC通信时序其实并不难,难点在于应答信号的处理。现在首先了解一下IIC通信时序和IIC总线的几个状态(这方面内容可百度):
1.空闲状态:IIC总线SDA和SCL均处于高电平;
2. 起始信号:在SCL高电平期间,SDA电平由高变低,开始通信之前必须由主机发送起始信号;
3.停止信号:在SCL高电平期间,SDA电平由低变高,结束通信时由主机发送停止信号;
4.应答信号:每传送完一个字节数据后,都必须有应答信号,应答信号为低电平;应答信号分两种:
4.1.主机发送数据时,从机应答信号给主机,主机接收应答信号;
4.2.主机读取数据时,主机应答信号给从机,从机接收应答信号;
5.1从PCA6416的数据手册中,可以直到pca6416地址定义如下:
高6位为固定地址,ADDR位根据实际电路设定(高电平为1,低电平为0),读/写位(读--1,写--0);主机每发送完一个字节后都必须接收从机的应答信号;主机每接收完一个字节数据后,都必须发送一个应答信号(非应答信号)
5.2接下来了解下PCA6416的命令字节,方便后续代码的阅读。从数据手册可以查得6416部分命令字节定义如下:
5.3 pca6416写命令的主要步骤:(1)启动条件;(2)发送从机地址0x40(写);(3)发送命令字节(确定PCA6416哪个寄存器接收后面的数据);(4)发送有效数据;(5)停止条件;从手册上可查阅时序图:
5.4 pca6416读命令的主要步骤:(1)启动条件;(2)发送从机地址0x40(写);(3)发送命令字节(确定哪些寄存器即将被访问);(4)重新启动;(5)发送从机地址0x41(读);(6)接收完数据应答;(7)停止条件
6.接下来便是完整的程序代码:
/************PCA6416 I2C接口设置(端口根据实际电路原理设置)*************************/
#define PCA6416_PORT GPIOB
#define PCA6416_RST GPIO_Pin_12
#define PCA6416_SCL GPIO_Pin_10
#define PCA6416_SDA GPIO_Pin_11
#define PCA6416_DELAY I2C_Delay(3000) //延时
//以下PCA6416的地址从数据手册查询
#define PCA6416_DEVICE_ADDR 0x40 //6416地址(写操作) 0x41(读操作)
#define PCA6416_REG_IN0 0x00 //输入寄存器0
#define PCA6416_REG_IN1 0x01 //输入寄存器1
#define PCA6416_REG_OUT0 0x02 //输出寄存器0
#define PCA6416_REG_OUT1 0x03 //输出寄存器1
#define PCA6416_REG_POL0 0x04 //极性反转寄存器0
#define PCA6416_REG_POL1 0x05 //极性反转寄存器1
#define PCA6416_REG_CFG0 0x06 //方向配置寄存器0
#define PCA6416_REG_CFG1 0x07 //方向配置寄存器1
void I2C_PORT_Init(void);
void I2C_SDA_DIR_SET(u8 io_dir);
void I2C_Ready(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack(void);
void I2C_Send_Ack(void);
void I2C_Send_NoAck(void);
void I2C_Write_Byte(u8 a);
u8 I2C_Read_Byte(void);
void I2C_Write_PCA6416(u8 addr, u8 reg_addr, u8 low_byte);
void I2C_Read_PCA6416(u8 addr, u8 reg_addr, u8 *pBuffer, u16 num);
void I2C_PORT_Init(void) //IIC端口初始化
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_InitStructure.GPIO_Pin = PCA6416_SCL;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(PCA6416_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = PCA6416_SDA;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(PCA6416_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = PCA6416_RST;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(PCA6416_PORT, &GPIO_InitStructure);GPIO_SetBits(PCA6416_PORT,PCA6416_RST);I2C_Ready();
}
void I2C_Delay(unsigned int count)
{
while (count)
count--;
}
void I2C_SDA_DIR_SET(u8 io_dir) //sda引脚输入输出设置
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); if(io_dir==0){GPIO_InitStructure.GPIO_Pin = PCA6416_SDA;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //outputGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(PCA6416_PORT, &GPIO_InitStructure); }else if(io_dir==1){GPIO_InitStructure.GPIO_Pin = PCA6416_SDA; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //(I2C经常使用开漏模式,但开漏需要外部上拉,而实际电路没有上拉,所以采用上拉输入)GPIO_Init(PCA6416_PORT, &GPIO_InitStructure); }
}
void I2C_Ready(void)
{//GPIO_SetBits(PCA6416_PORT,PCA6416_RST);//PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;
}
void I2C_Start(void) //起始条件
{GPIO_SetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;GPIO_ResetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;GPIO_ResetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;
}
void I2C_Stop(void) //停止条件
{GPIO_ResetBits(PCA6416_PORT, PCA6416_SDA);PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT, PCA6416_SCL);PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;
}
void I2C_Ack(void) //读取从机应答信号
{//u16 i;GPIO_ResetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;I2C_SDA_DIR_SET(1); //while((GPIO_ReadInputDataBit(PCA6416_PORT,PCA6416_SDA))&&(i<0x2b0)) {i++;}PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;PCA6416_DELAY;I2C_SDA_DIR_SET(0);GPIO_ResetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;PCA6416_DELAY;
}
void I2C_Send_Ack(void) //主机发送应答信号
{GPIO_ResetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;PCA6416_DELAY;GPIO_ResetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SDA);
}
void I2C_Send_NoAck(void) //主机发送非应答信号
{GPIO_SetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;PCA6416_DELAY;GPIO_ResetBits(PCA6416_PORT,PCA6416_SCL);PCA6416_DELAY;}
void I2C_Write_Byte(u8 a) //主机发送一个字节
{char i;for(i=0;i<8;i++){GPIO_ResetBits(PCA6416_PORT,PCA6416_SCL); //scl=0;PCA6416_DELAY;PCA6416_DELAY;if(a&0x80)GPIO_SetBits(PCA6416_PORT,PCA6416_SDA); //sda=1;elseGPIO_ResetBits(PCA6416_PORT,PCA6416_SDA);a=a<<1;PCA6416_DELAY;GPIO_SetBits(PCA6416_PORT,PCA6416_SCL); //scl=1;PCA6416_DELAY;PCA6416_DELAY;}GPIO_ResetBits(PCA6416_PORT,PCA6416_SCL); PCA6416_DELAY;
}
u8 I2C_Read_Byte(void) //主机读取一个字节
{u8 i,temp;temp = 0;GPIO_SetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;I2C_SDA_DIR_SET(1);PCA6416_DELAY;for(i=0;i<8;i++){GPIO_SetBits(PCA6416_PORT, PCA6416_SCL); PCA6416_DELAY;PCA6416_DELAY;temp = ((temp<<1)|(GPIO_ReadInputDataBit(PCA6416_PORT, PCA6416_SDA)));PCA6416_DELAY;GPIO_ResetBits(PCA6416_PORT, PCA6416_SCL); PCA6416_DELAY;PCA6416_DELAY;}I2C_SDA_DIR_SET(0);PCA6416_DELAY;GPIO_ResetBits(PCA6416_PORT,PCA6416_SDA);PCA6416_DELAY;return temp;
}
void I2C_Write_PCA6416(u8 addr, u8 reg_addr, u8 low_byte) //主机完整发送数据
{I2C_Start();I2C_Write_Byte(addr);I2C_Ack();I2C_Write_Byte(reg_addr);I2C_Ack();I2C_Write_Byte(low_byte);I2C_Ack();I2C_Stop();
}
void I2C_Read_PCA6416(u8 addr, u8 reg_addr, u8 *pBuffer, u16 num) //主机完整读取数据
{I2C_Start();I2C_Write_Byte(addr);I2C_Ack();I2C_Write_Byte(reg_addr);I2C_Ack();I2C_Start();I2C_Write_Byte(addr | 0x01);I2C_Ack();while (num){*pBuffer = I2C_Read_Byte();if (num == 1)I2C_Send_NoAck();elseI2C_Send_Ack();pBuffer++;num--;}I2C_Stop();
}
以上便是此次学习IIC的主要过程,不足之处烦请指出,与君共勉。