数据和应答信号都规定在SCL在高电平期间,SDA电平稳定;SCL在低电平期间,SDA电平才可以变化。要不然容易被误认为起始或停止信号。
应答信号:1. 第九个SCL之前的低电平期间将SDA拉低
2. 确保在SCL为高电平时,SDA为稳定的低电平
GPIO模拟IIC:
1. 初始化,初始先拉高SDA,SCL
//IO操作函数
#define IIC_SCL_H GPIO_SetBits(GPIO_SCL, GPIO_SCL_PIN) //SCL
#define IIC_SCL_L GPIO_ResetBits(GPIO_SCL, GPIO_SCL_PIN) //SCL#define IIC_SDA_H GPIO_SetBits(GPIO_SDA, GPIO_SDA_PIN) //SDA
#define IIC_SDA_L GPIO_ResetBits(GPIO_SDA, GPIO_SDA_PIN) //SDAvoid IIC_Init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ; //开漏输出
//IIC一般配置为开漏输出。开漏输出模式天然支持输入功能:
//当 MOS 管截止时,引脚可通过上拉电阻检测外部信号(高 / 低电平),实现双向通信。GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);IIC_SCL_H;//PB6,PB7 输出高IIC_SDA_H;
}
2.起始信号。先全部拉高,等一会拉低SDA。等起始信号完成后,把SCL拉低,确保通信时序进入数据传输准备状态
void IIC_Start(void)
{IIC_SCL_H;IIC_SDA_H;delay_us(4);IIC_SDA_L;delay_us(4);IIC_SCL_L;//钳住I2C总线,准备发送或接收数据 }
3. 发送数据:(iic发送数据从高位到地位)
对传入的8个字节进行8次循环处理:
总线:先拉低SCL准备传数据,通过判断数1或0据来拉高或拉低SDA。最后再拉高SCL,让SDA稳定一段时间。
数据判断:
// txd = 1010 1010txd & (0x1<<(7)) //1左移7位 1000 0000// 高位 (7)1 & 1 = 1,if成立txd & (0x1<<(6)) // //1左移6位 0100 0000//// 高位 (6)0 & 1 = 0,if不成立
具体而言,for从7到0循环,满足iic发送数据从高位到地位的逻辑。
void IIC_Send_Byte(u8 txd)
{int8_t i;for(i = 7; i >= 0; i--){IIC_SCL_L;//拉低时钟开始数据传输if(txd & (0x1<<(i)))IIC_SDA_H;elseIIC_SDA_L;delay_us(2);IIC_SCL_H;delay_us(2);}IIC_SCL_L;IIC_SDA_H; //发送一个字节后,将SDA拉高,等待从机拉低SDA的应答信号}
4.主机等待从机的应答:
主机等待从机的应答信号,是 I2C 通信中主机(发送方)确认从机(接收方)是否成功接收数据的关键操作。
在 I2C 通信中,当主机向从机发送数据(如写入从机地址、发送指令或数据字节)后,按照协议规定:
主机发送完 1 字节(8 位)数据后,会释放 SDA 线的控制权,等待从机在第 9 个时钟周期内返回应答信号。
从机若成功接收数据,会将 SDA 线拉低(ACK 应答);若未接收或接收错误,会让 SDA 线保持高电平(NACK 非应答)。
主机通过 IIC_Wait_Ack 函数检测 SDA 线的状态,判断从机是否正确接收了数据。
u8 IIC_Wait_Ack(int timeout) //单位 us
{do{timeout--;delay_us(2);}while((READ_SDA)&&(timeout>=0)); // 循环等待:SDA为高电平且未超时// 若循环是因为timeout < 0结束,超时未收到应答(SDA始终为高),返回1if(timeout<0) return 1; // 若SDA仍为高电平,表示收到NACK,返回2IIC_SCL_H; // 拉高SCL,进入应答检测的时钟周期delay_us(2);if(READ_SDA!=0) return 2; // 否则SDA为低电平,表示收到ACK,返回0IIC_SCL_L; // 拉低SCL,结束检测delay_us(2);return 0;
}
. 应答:先拉高SCL,再拉低SDA,再将SCL拉低,完成ACK
//产生ACK应答
void IIC_Ack(void)
{IIC_SCL_H;IIC_SDA_L;delay_us(2);IIC_SCL_L;IIC_SDA_H;delay_us(2);
}
5.不应答:先拉高SCL,再拉高SDA,再将SCL拉低,无法完成ACK
void IIC_NAck(void)
{IIC_SDA_H;IIC_SCL_H;delay_us(2);IIC_SCL_L;delay_us(2);
}
IIC一般配置为开漏输出。开漏输出模式天然支持输入功能:当 MOS 管截止时,引脚可通过上拉电阻检测外部信号(高 / 低电平),实现双向通信。
关于OD开漏输出和PP推挽输出:
区别之处:PP的p-mos接VDD(器件的电源电压);DD的P-MOS没有VDD。
当输出控制1,p管打开n管截止,此时PP输出高电压,DD在没有接Vcc(电路的主电源)和上拉电阻的情况下无法输出高电平,只有接上才可输出高电平。
当输出控制0,n管打开p管截止,两者都是输出低电平。(n-mos导通时电阻很小,分压后DD相当于无电压输出)