接口/总线/驱动
要用IIC读写AT24C02,首先要了解IIC是什么。
I²C(Inter-Integrated Circuit)字面上的意思是集成威廉希尔官方网站 之间,它其实是I²C Bus简称,所以中文应该叫集成威廉希尔官方网站 总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。I²C的正确读法为“I平方C”("I-squared-C"),而“I二C”("I-two-C")则是另一种错误但被广泛使用的读法。自2006年10月1日起,使用I²C协议已经不需要支付专利费,但制造商仍然需要付费以获取I²C从属设备地址。
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。设备上的串行数据线SDA接口威廉希尔官方网站 应该是双向的,输出威廉希尔官方网站 用于向总线上发送数据,输入威廉希尔官方网站 用于接收总线上的数据。
通过以上介绍,现在开始编写IIC的GPIO口初始化,SCL、SDA分别对应PB8、PB9引脚。IIC的速度最快400Khz,并且可以兼容0~100KHz的IIC总线系统,所以引脚输出速度只要配置为2M,这个速度还是远远超过IIC的速度,这里就要在引脚电平变换时加入稍微的延时让速度符合IIC协议的要求。
void IIC_Init()
{
RCC- >AHB1ENR = 1< < 1; //开端口B时钟
GPIOB- >MODER &=~ (0x03< < 16);
GPIOB- >MODER &=~ (0x03< < 18);
GPIOB- >MODER |= (0x01< < 16); //通用输出模式
GPIOB- >MODER |= (0x01< < 18); //通用输出模式
GPIOB- >OTYPER &=~ (1< < 8); //开漏输出
GPIOB- >OTYPER &=~ (1< < 9); //开漏输出
GPIOB- >OSPEEDR &=~ (0x03< < 16); //输出速度2Mhz
GPIOB- >OSPEEDR &=~ (0x03< < 18); //输出速度2Mhz
GPIOB- >PUPDR &=~ (0x03< < 16); //无上下拉
GPIOB- >PUPDR &=~ (0x03< < 18); //无上下拉
GPIOB- >ODR |= (0x01< < 8); //初始输出为1
GPIOB- >ODR |= (0x01< < 9); //初始输出为1
}
初始化完GPIO,接着根据IIC协议编写代码。
从时序图可以看出起始和停止信号时SCL都要拉高,起始时,SDA由高拉低,停止时SDA由低拉高。收发数据时,SCL处于低电平期间允许改变数据,高电平期间数据有效。所以在每个操作结束后可以把SCL拉低,防止误操作。为了兼容AT24C02的写周期5ms,在停止时,需要再延时5ms。
收发多个数据时,要多一个应答位,应答时继续收发数据,非应答时不再收发。SDA高电平时为非应答,低电平时为应答信号。
void IIC_Start()
{
IIC_SCL_H();
IIC_SDA_H();
IIC_Delayus(5);
IIC_SDA_L();
IIC_Delayus(5);
IIC_SCL_L();
IIC_Delayus(5);
}
void IIC_Stop()
{
IIC_SCL_H();
IIC_SDA_L();
IIC_Delayus(5);
IIC_SDA_H();
IIC_Delayms(5); //兼容AT24C02的写周期
}
void IIC_SendByte(u8 dat)
{
u8 i = 0;
for(i = 0;i< 8;i++)
{
IIC_SCL_L();
IIC_Delayus(5);
if(dat & 0x80)
IIC_SDA_H();
else
IIC_SDA_L();
IIC_Delayus(5);
dat < <= 1;
IIC_SCL_H();
IIC_Delayus(5);
}
IIC_SCL_L();
IIC_Delayus(5);
}
u8 IIC_ReadByte()
{
u8 i = 0,temp = 0;
IIC_SCL_L(); //拉低scl,准备发数据
IIC_SDA_H(); //主机输出高,切断输出通道,使引脚配置变为输入
IIC_Delay_us(5);
for(i = 0;i< 8;i++)
{
temp < <= 1; //空出最后一位,准备接收
IIC_SCL_L();
IIC_Delayus(5);
IIC_SCL_H();
IIC_Delayus(5);
if(GPIOB- >IDR & (1< < 9))
temp |= 1;
IIC_Delayus(5);
}
IIC_SCL_L();
IIC_Delayus(5);
return temp;
}
u8 IIC_CheckAck()
{
u8 ack = 0;
IIC_SCL_L(); //这两行可以忽略,因为发送完后,总是拉低SCL
IIC_SDA_H(); //主机释放SDA总线,同时切断引脚输出回路
IIC_Delayus(5);
IIC_SCL_H();
IIC_Delayus(5);
if(GPIOB- >IDR & (1< < 9))
ack = 1;
else
ack = 0;
IIC_Delayus(5);
IIC_SCL_L();
IIC_Delayus(5);
return ack;
}
void IIC_AcktoSlave()
{
IIC_SCL_L(); //拉低电平,让数据改变
IIC_Delayus(5);
IIC_SDA_L();
IIC_Delayus(5);
IIC_SCL_H();
IIC_Delayus(5);
IIC_SCL_L();
IIC_Delayus(5);
}
void IIC_NoAcktoSlave()
{
IIC_SCL_L();
IIC_Delayus(5);
IIC_SDA_H();
IIC_Delayus(5);
IIC_SCL_H();
IIC_Delayus(5);
IIC_SCL_L();
IIC_Delayus(5);
}
这样IIIC读写AT24C02的协议层就编写完成了。
全部0条评论
快来发表一下你的评论吧 !