STM32F105 与PCA6416 IIC通信(软件)

这段时间在做公司的一个项目,由于需要用到比较多的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;			//output
		GPIO_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;
		else
			GPIO_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();
		else
			I2C_Send_Ack();
		
		pBuffer++;
		num--;
	}
	I2C_Stop();
}

以上便是此次学习IIC的主要过程,不足之处烦请指出,与君共勉。













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值