1.I2C的通信协议
它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。
IIC是半双工通信方式。
1.空闲状态
SDA与SCL同时处于高电平状态时候为空闲状态
2.开始信号
当SCL为高电平期间,SDA由高电平到低电平的跳变,启动信号是一种电平跳变时序信号,而不是一个电平信号。
3.停止信号
当SCL为高电平期间,SDA由低电平到高电平的跳变。
4.应答信号
发送器每次发送一个字节,就在时钟脉冲9期间释放数据线,接收器会发送一个应答,高电平为无效应答,低电平为有效应答。
注意:在第9个信号时SDA需要拉低,而且确保在该时钟的高电平期间为稳定的低电平。
5.数据有效性
I2C总线进行数据转送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟信号上的信号为低电平期间,数据线上的高电平或低电平在允许变化。
数据在SCL与上下降沿的时候需要已经稳定了
6.数据转输
在I2C总线上传送的每一位数据都有一个时钟脉冲相对应,即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
2.I2C底层代码
1.初始化
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOH_CLK_ENABLE(); //使能GPIOH时钟
//PH4,5初始化设置
GPIO_Initure.Pin=GPIO_PIN_4|GPIO_PIN_5;
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
IIC_SDA=1; //空闲的时候需要高电平
IIC_SCL=1;
}
2.起始信号
当SCL为高电平期间,SDA由高电平到低电平的跳变,启动信号是一种电平跳变时序信号,而不是一个电平信号。
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
3.停止信号
当SCL为高电平期间,SDA由低电平到高电平的跳变。
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
delay_us(4);
IIC_SDA=1;//发送I2C总线结束信号
}
4.应答信号
发送器每次发送一个字节,就在时钟脉冲9期间释放数据线,接收器会发送一个应答,高电平为无效应答,低电平为有效应答。
产生应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
等待应答
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
5.发送数据
发送数据
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的,拉高期间发送数据
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
接收数据
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
3.I2C硬件连接![请添加图片描述](https://i-blog.csdnimg.cn/blog_migrate/551853043766f114901abdbf475f61d1.png)
4.EEPROM存储
A0,A1,A2地址线,WP使能位,SCL,SDA数据转输线
写数据操作
进入写模式,写地址,写date
读数据操作
进入写模式,写读的地址,进入读模式,读数据
5.EEPROM操作代码
写操作
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址
}else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
delay_ms(10);
}
读操作
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//发送高地址
}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
return temp;
}
6.实验程序
通过I2C想EEPROM写入程序,并读取出来
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "sdram.h"
#include "24cxx.h"
//要写入到24c02的字符串数组
const u8 TEXT_Buffer[]={"Apollo STM32F4 IIC TEST"};
#define SIZE sizeof(TEXT_Buffer)
int main(void)
{
u8 key;
u16 i=0;
u8 datatemp[SIZE];
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
SDRAM_Init(); //初始化SDRAM
LCD_Init(); //初始化LCD
AT24CXX_Init(); //初始化IIC
while(AT24CXX_Check())//检测不到24c02
{
delay_ms(500);
LED0=!LED0;//DS0闪烁
}
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRES)//KEY1按下,写入24C02
{
LCD_Fill(0,170,239,319,WHITE);//清除半屏
AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
printf("写入完成");
}
if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
{
AT24CXX_Read(0,datatemp,SIZE);
printf("%s",datatemp);
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//提示系统正在运行
i=0;
}
}
}