【前言】
这次准备驱动IIC,首先采取模拟IIC的驱动,这里跟大家分享如何书写IIC的start、stop、ack、sendbyte,readbyte的时序。
【IIC时序】

【实现步骤】
1、新建iic.c/h并添加进HW分组中。
2、找到原理图,确定IIC的SCL、SDA的IO。在原图中SCL为PC00,SDA为PC01。

3、首先书写IIC两个IO的GPIO的初始化,分为时钟使能,由于开发板上有4.7K上拉电阻,这里SCL定义为推挽输出,SDA为开漏输出。初始化代码如下:
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
//开启GPIOA时钟
__RCC_GPIOC_CLK_ENABLE();
//GPIO配置
GPIO_InitStructure.Pins = GPIO_PIN_0;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(CW_GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.Pins = GPIO_PIN_1;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_Init(CW_GPIOC, &GPIO_InitStructure);
//初始化后,把空线设为空闲
IIC_SCL_H;
IIC_SDA_H;
}4、为了方便阅读,在iic.h中宏义,高低电平的输出,以及GPIO电平的获取。
/* IIC IO高低电平的设置 */ #define IIC_SCL_H CW_GPIOC->BSRR = GPIO_PIN_0 #define IIC_SCL_L CW_GPIOC->BRR = GPIO_PIN_0 #define IIC_SDA_H CW_GPIOC->BSRR = GPIO_PIN_1 #define IIC_SDA_L CW_GPIOC->BRR = GPIO_PIN_1 /* SDA 电平获取 */ #define READ_SDA GPIO_ReadPin(CW_GPIOC, GPIO_PIN_1)
5、IIC起始信号,SCL保持高电平期间,数据线SDA上的电平被拉低
void IIC_Start(void)
{
IIC_SCL_H;
IIC_SDA_H;
Delay_US(2);
IIC_SDA_L;
Delay_US(2);
IIC_SCL_L;
}6、IIC停止信号:SCL保持高电平期间,数据线SDA被释放,返回高电平
void IIC_Stop(void)
{
/* SCL保持高电平期间,数据线SDA被释放,返回高电平 */
IIC_SCL_H;
IIC_SDA_L;
Delay_US(2);
IIC_SDA_H;
Delay_US(2);
IIC_SCL_L;
}7、获取ACK应答
首选读取SDA的电平,如果读取到低电平,说明从机有应答,然后再读取一次,确保稳定的低电平。
uint8_t IIC_Wait_Ack(int16_t timeout)
{
do{
timeout --;
Delay_US(2);
}while((READ_SDA) && (timeout>=0));
if(timeout<0)
return 1;
/* 判断SDA 稳定维持为低电平 */
IIC_SCL_H;
Delay_US(2);
if(0 != READ_SDA)
return 2;
IIC_SCL_L;
Delay_US(2);
return 0;
}8、发送一个字节
void IIC_Send_Byte(uint8_t txd)
{
int i;
for(i = 7; i >= 0; i--)
{
IIC_SCL_L;
if(txd & BIT(i))
IIC_SDA_H;
else
IIC_SDA_L;
Delay_US(2);
IIC_SCL_H;
Delay_US(2);
}
IIC_SCL_L;
Delay_US(2);
IIC_SDA_H;
}8、读取一个字节,发送完成产生应该信号
ACK信号:发送者在ACK时钟脉冲期间释放SDA线,接收者可以将SDA拉低并在时钟信号为高时保持低电平。
NACK信号:当在第9个时钟脉冲的时候SDA线保持高电平,就被定义为NACK信号。
uint8_t IIC_Read_Byte(uint8_t ack)
{
int8_t i;
uint8_t rxd;
rxd = 0;
for(i = 7; i >= 0; i--)
{
IIC_SCL_L;
Delay_US(2);
IIC_SCL_H;
if(0 != READ_SDA)
rxd |= BIT(i);
Delay_US(2);
}
IIC_SCL_L;
Delay_US(2);
//开始应签
if(ack)
{
IIC_SDA_L;
IIC_SCL_H;
Delay_US(2);
IIC_SCL_L;
IIC_SDA_H;
Delay_US(2);
}else{
IIC_SDA_H;
IIC_SCL_H;
Delay_US(2);
IIC_SCL_L;
Delay_US(2);
}
return rxd;
}到此IIC时序、写、读函数全部写完。保存好后,就可以供其他的驱动来使用。此例程也可以通过修改Init、宏定义进方便的进行移植。
我要赚赏金
