stm32 - IIC
STM32-IIC
1. 基本概念
半双工同步通信的串行通信接口
2. 引脚说明
SDA
SCK/SCL
IIC 总线只需要两根引脚就可以实现通信,一根是数据线(SDA Serial Data),
另一根是时钟线(SCL Serial Clock)
在空闲状态下所有器件的时钟线(SCL)和数据线(SDA)都被总线的上拉电阻拉高,这样就可以把SDA引脚和SCL引脚设置为开漏模式即可,好处是防止短路。
思考:如果IIC总线上挂载了多个外围器件,如何与某一个器件进行单独通信?
器件地址
3. 传输方向
1. 在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。
2. 如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送。
3. 如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下,主机负责产生定时时钟和终止数据传送。
4. 通信过程
1. 空闲状态
点击查看代码
//3.空闲状态下SDA和SCL都为高电平
IIC_SCL_WRITE(1);
IIC_SDA_WRITE(1);
指的是不传输任何数据的时候就被称为空闲状态,IIC总线规定SDA数据线和SCL时钟线在不传输数据的时候都应该设置高电平,表示空闲。
2. 开始信号
点击查看代码
//IIC总线开始信号
void IIC_StartSignal(void)
{
//1.设置SDA引脚为输出模式
IIC_SDAOutputMode();
//2.确保SDA和SCL为高电平
IIC_SCL_WRITE(1);
IIC_SDA_WRITE(1);
Delay_us(5); //提高程序可靠性
//3.把SDA引脚拉低
IIC_SDA_WRITE(0);
Delay_us(5); // IIC总线的通信速率为100KHZ 1000000us = 100000HZ 10us = 1HZ
//4.把SCL引脚拉低,表示准备通信
IIC_SCL_WRITE(0);
}
开始信号由主机发出,表示打算和所有的从器件进行通信,IIC总线规定在SCL时钟线保持高电平期间,把SDA数据线拉低,表示开始信号。
3. 数据发送
在主机发送开始信号后,就可以发送数据或者地址,IIC总线规定数据的收发都是MSB(高位先出),由于只有一个数据线,所以IIC采用串行方式把数据的每个bit位发出去。
点击查看代码
//主机发送数据(从机读取数据)
//IIC的发送字节
void IIC_SendByte(uint8_t Byte)
{
uint8_t i = 0;
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.把SCL引脚电平拉低,此时主机准备数据
SCL_SET(0);
delay_us(5);
//3.循环发送8bit,遵循MSB高位先出
for(i=0;i<8;i++)
{
//4.判断待发送的字节的最高位
if( Byte & 0x80 )
{
SDA_SET(1);
}
else
SDA_SET(0);
Byte <<= 1;
delay_us(5);
//5.把SCL电平拉高,此时从机读取bit
SCL_SET(1);
delay_us(5);
//6.把SCL引脚电平拉低,此时主机准备下一个bit
SCL_SET(0);
delay_us(5);
}
}
由于SCL提供的脉冲周期是有规律的,所以IIC总线规定只能在SCL脉冲周期的高电平期间进行数据的读取或者写入,在SCL脉冲周期的低电平期间可以进行数据的修改。
4. 应答信号
IIC总线增加了应答机制,在主机发送一个字节数据之后,从机在第9个脉冲周期进行应答,如果SDA为0,则表示应答,如果SDA=1,则表示无应答,如果从机没有应答,则主机应该发送停止信号,表示停止通信。
点击查看代码
//主机等待从机应答 返回0 说明从机应答 返回1 说明从机没应答
//判断从机是否应答
bool IIC_IsSlaveACK(void)
{
bool ack;
//1.设置SDA引脚为输入模式
IIC_SDAConfig(GPIO_Mode_IN);
//2.把SCL引脚电平拉低,此时为第9个脉冲的低电平,从机准备bit
SCL_SET(0);
delay_us(5);
//3.把SCL引脚电平拉高,此时为第9个脉冲的高电平,主机读取状态
SCL_SET(1);
delay_us(5);
//4.主机读取状态 1 表示未应答 0 表示已应答
if(SDA_READ)
ack = false;
else
ack = true;
//5.把SCL引脚电平拉低
SCL_SET(0);
delay_us(5);
return ack;
}
//从机发送数据,主机接收数据并发送应答信号
//ack=1 表示不应答 ack=0 表示要应答
void IIC_MasterACK(uint8_t ack)
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.把SCL引脚电平拉低,此时主机准备
SCL_SET(0);
delay_us(5);
//3.判断ack的状态,从而对SDA操作
if(ack)
SDA_SET(1);
else
SDA_SET(0);
delay_us(5);
//4.把SCL电平拉高,此时从机读取bit
SCL_SET(1);
delay_us(5);
//5.把SCL引脚电平拉低
SCL_SET(0);
delay_us(5);
}
5. 数据接收
在主机发送开始信号后,就可以发送数据或者地址,IIC总线规定数据的收发都是MSB(高位先出),由于只有一个数据线,所以IIC采用串行方式把数据的每个bit位发出去。
点击查看代码
//主机读取数据(从机发送数据)
//IIC读取字节
uint8_t IIC_ReadByte(void)
{
uint8_t i = 0,data = 0;
//1.设置SDA引脚为输入模式
IIC_SDAConfig(GPIO_Mode_IN);
//2.把SCL引脚电平拉低,此时从机准备数据
SCL_SET(0);
delay_us(5);
//3.循环读取8bit,遵循MSB高位先出
for (i = 0; i < 8; ++i)
{
//4.把SCL电平拉高,此时主机读取bit
SCL_SET(1);
delay_us(5);
//5.主机读取bit
data <<= 1;
data |= SDA_READ;
//delay_us(5);
//6.把SCL引脚电平拉低,此时从机准备下一个bit数据
SCL_SET(0);
delay_us(5);
}
//7.返回结果
return data;
}
由于SCL提供的脉冲周期是有规律的,所以IIC总线规定只能在SCL脉冲周期的高电平期间进行数据的读取或者写入,在SCL脉冲周期的低电平期间可以进行数据的修改。
6. 停止信号
停止信号由主机发出,表示不打算和从器件继续通信,IIC总线规定在SCL时钟线保持高电平期间,把SDA数据线拉高,表示停止信号。
点击查看代码
//IIC总线停止信号
void IIC_StopSignal(void)
{
//1.设置SDA引脚为输出模式
IIC_SDAOutputMode();
//2.确保SDA和SCL为低电平
IIC_SCL_WRITE(0);
IIC_SDA_WRITE(0);
//4.把SCL引脚拉高
IIC_SCL_WRITE(1);
Delay_us(5);
//5.把SDA引脚拉高
IIC_SDA_WRITE(1);
Delay_us(5); //确保SDA的电平状态可以被其他从器件检测到
}
5. 集合应用举例
点击查看往AT24C02字节写入代码
//AT24C02字节写入的指令
bool AT24C02_ByteWrite(uint8_t Addr,uint8_t Byte)
{
//1.开始信号
IIC_Start();
//2.器件地址
IIC_SendByte(0xA0);
//3.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
return false;
}
//4.存储地址
IIC_SendByte(Addr);
//5.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Memory Address Error\r\n");
return false;
}
//6.发送数据
IIC_SendByte(Byte);
//7.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Data Error\r\n");
return false;
}
//8.停止信号
IIC_Stop();
//9.延时5ms
delay_ms(5);
return true;
}
点击查看AT24C02的当前地址读代码
//AT24C02的当前地址读指令
uint8_t AT24C02_CurrentAddressRead(void)
{
uint8_t recv = 0;
//1.开始信号
IIC_Start();
//2.器件地址
IIC_SendByte(0xA1);
//3.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
return false;
}
//4.读取字节
recv = IIC_ReadByte();
//5.发送应答
IIC_MasterACK(0x01);
//6.停止信号
IIC_Stop();
return recv;
}