当前位置: 代码迷 >> 综合 >> STM32F105 与PCA6416 IIC通信(软件)
  详细解决方案

STM32F105 与PCA6416 IIC通信(软件)

热度:66   发布时间:2023-10-26 19:29:05.0

这段时间在做公司的一个项目,由于需要用到比较多的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的主要过程,不足之处烦请指出,与君共勉。