零、参考资料:
IIC协议图解 韦东山 STM32通信模拟 I2C 韦东山 STM32通信硬件 I2C 51 IIC程序 串行总线I2C STM32 IIC详解 有动图
软件IIC
一、IIC程序:
0、数据有效性
在SCL高电平的时候采样,也就是有效。低电平的时候切换数据。
1、端口定义和端口初始化
?软件IIC的本质就是通过GPIO的输入与输出功能,来模拟实现硬件IIC的功能,按照时序图规定的通信协议进行模拟通信。
#define SCL_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_8)
#define SCL_Set() GPIO_SetBits(GPIOB,GPIO_Pin_8)
#define SDA_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_9)
#define SDA_Set() GPIO_SetBits(GPIOB,GPIO_Pin_9)
#define SDA_Read() GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)
?端口初始化,SCL时钟引脚只有输出功能,而SDA数据引脚既有输出功能,又有输入功能。STM32的GPIO的输入输出不能共用,因此需要在SDA引脚在不同输入输出功能时候,分别初始化为推挽输出和浮空输入。
void SCL_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_8);
}
void SDA_GPIO_Out_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_9);
}
void SDA_GPIO_Read_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_9);
}
void IIC_Init(void)
{
SCL_GPIO_Init();
SDA_GPIO_Out_Init();
}
2、IIC总线启动和停止条件
时序图
void IIC_Start(void)
{
SDA_GPIO_Out_Init();
SDA_Set();
SCL_Set();
delay_us(6);
SDA_Clr();
delay_us(6);
SCL_Clr();
}
void IIC_Stop(void)
{
SDA_GPIO_Out_Init();
SDA_Clr();
SDA_Set();
delay_us(6);
SDA_Set();
delay_us(6);
}
3、IIC发送与等待应答
时序图
void IIC_SendAck(unsigned char ackbit)
{
SDA_GPIO_Out_Init();
SCL_Clr();
if(ackbit ==1)
SDA_Set();
else if(ackbit == 0)
SDA_Clr();
delay_us(6);
SCL_Set();
delay_us(6);
SCL_Clr();
SDA_Set();
delay_us(6);
}
unsigned char IIC_WaitAck(void)
{
unsigned char ackbit = 0;
SDA_GPIO_Read_Init();
SCL_Set();
delay_us(6);
ackbit = SDA_Read();
SCL_Clr();
delay_us(6);
return ackbit;
}
Notes: ?●在开始和停止条件之间从发送机到接收机传输的数据字节数不受限制。 ?●每个八位数据字节后跟一个应答位。 ?●应答位是发送器将总线置为高电平,而来自主机的SCL也产生与应答有关的额外时钟脉冲。 ?●被寻址的从机接收机必须在接收到每个字节后产生一个应答。同样,主机必须在接收到从机发送机中移出的每个字节后产生应答。 ?●应答的设备必须在应答时钟脉冲期间拉低SDA线,以使SDA线在应答时钟脉冲的高电平期间稳定为低电平。 ?●主接收机必须通过不在从机时钟输出的最后一个字节上生成应答,向发射机发送数据结束信号。在这种情况下,发送机必须将数据线保持为高电平,以使主机能够产生停止条件。
4、通过I2C总线发送和接收数据
?●具体的通信格式和芯片有关,仅介绍发送和接收单个字节。
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
SDA_GPIO_Out_Init();
for(i=0; i<8; i++)
{
SCL_Clr();
delay_us(6);
if(byt & 0x80) SDA_Set();
else SDA_Clr();
delay_us(6);
SCL_Set();
byt <<= 1;
delay_us(6);
}
SCL_Clr();
}
unsigned char IIC_RecByte(void)
{
unsigned char i, da = 0;
SDA_GPIO_Read_Init();
for(i=0; i<8; i++)
{
SCL_Set();
delay_us(6);
da <<= 1;
if(SDA_Read()) da |= 1;
SCL_Clr();
delay_us(6);
}
return da;
}
Notes: ?●所有的SDA 信号变化都要在SCL 时钟为低电平时进行,除了开始和结束标志。 ?●I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。 ?●I2C总线上进行一次数据传输的通信格式,即I2C总线的完整时序。 ?●当主控器接收数据时,在最后一个数据字节,必须发送一个非应答信号,使受控器释放数据线,以便主控器产生一个停止信号来终止总线的数据传送。
二、应用-LM75B:
?●恩智浦和TI好像都有类似的芯片。 NXP TI
unsigned int LM75BD_ReadTemp(void)
{
unsigned char High_temp = 0;
unsigned char Low_temp = 0;
unsigned int temp = 0;
unsigned char ack = 0;
IIC_Start();
IIC_SendByte(0x90);
ack = IIC_WaitAck();
if(ack == 1)
{
OLED_ShowString(5,30,"ERROR 1 ack",12,1);
ack = 0;
}
IIC_SendByte(0x00);
ack = IIC_WaitAck();
if(ack == 1)
{
OLED_ShowString(5,40,"ERROR 2 ack",12,1);
ack = 0;
}
IIC_Start();
IIC_SendByte(0x91);
ack = IIC_WaitAck();
if(ack == 1)
{
OLED_ShowString(5,50,"ERROR 3 ack",12,1);
ack = 0;
}
High_temp = IIC_RecByte();
IIC_SendAck(0);
Low_temp = IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
temp = ((High_temp<<8) | Low_temp);
return (temp>>5);
}
void LM75BD_Init(void)
{
ui_temperature = LM75BD_ReadTemp();
if( (ui_temperature & 0x8000) == 0x8000)
{
ui_temperature &= 0x7fff;
sprintf((char *)puc_temp_Buf,"temperature:-%7.3f",(float)ui_temperature*0.125);
}
else if( (ui_temperature & 0x8000) == 0)
{
sprintf((char *)puc_temp_Buf,"temperature:%7.3f",(float)ui_temperature*0.125);
}
OLED_ShowString(5,20,puc_temp_Buf,12,1);
}
void LM75BD_Temp_Proc(void)
{
if( ui_LM75BD_flag >= 200)
{
ui_LM75BD_flag = 0;
ui_temperature = LM75BD_ReadTemp();
if( (ui_temperature & 0x8000) == 0x8000)
{
ui_temperature &= 0x7fff;
sprintf((char *)puc_temp_Buf,"temperature:-%7.3f",(float)ui_temperature*0.125);
}
else if( (ui_temperature & 0x8000) == 0)
{
sprintf((char *)puc_temp_Buf,"temperature:%7.3f",(float)ui_temperature*0.125);
}
OLED_ShowString(5,20,puc_temp_Buf,12,1);
}
}
三、时序图示波器测试:
?●对照时序图,可以在波形图上找出启动和停止条件,以及应答和八个数据传输时钟。
硬件IIC
|