一、IIC的定义
I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS开发用于连接微控制器及其外围设备。IIC是一种多向控制总线,就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实施数据传输的控制源。这种方式简化信号传输总线。
它由数据线SDA和时钟SCL构成的串行总线,接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上,可发送接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。
IIC是半双工通信方式。
二、多主机I2C总线系统结构
2)多设备的使用举例
每个接到I2C总线的设备都有一个唯一的地址
三、I2C协议
空闲状态
开始信号
停止信号
应答信号
数据的有效性
数据传输
我们以斗地主为例:
1)空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
2)起始信号与停止信号
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
3)应答信号ACK
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
4)数据有效性
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
5)数据的传送
在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
STM32F4的IIC代码如下:
- IIC.h
- #ifndef __MYIIC_H
- #define __MYIIC_H
- #include "sys.h"
- //IO方向设置
- #define SDA_IN() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;} //PB9输入模式
- #define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
- //IO操作函数
- #define IIC_SCL PBout(8) //SCL
- #define IIC_SDA PBout(9) //SDA
- #define READ_SDA PBin(9) //输入SDA
- //IIC所有操作函数
- void IIC_Init(void); //初始化IIC的IO口
- void IIC_Start(void); //发送IIC开始信号
- void IIC_Stop(void); //发送IIC停止信号
- void IIC_Send_Byte(u8 txd); //IIC发送一个字节
- u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
- u8 IIC_Wait_Ack(void); //IIC等待ACK信号
- void IIC_Ack(void); //IIC发送ACK信号
- void IIC_NAck(void); //IIC不发送ACK信号
- void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
- u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
- #endif
复制代码
四、EEPROM(24C02)的使用
总容量是256(2K/8)个字节。接口:IIC
芯片图:
这里的A0=A1=A2=0
24C02读写字节时序
写:
读:
24C02的代码如下:
- 24cxx.c:
- #include "24cxx.h"
- #include "delay.h"
- //初始化IIC接口
- void AT24CXX_Init(void)
- {
- IIC_Init();//IIC初始化
- }
- //在AT24CXX指定地址读出一个数据
- //ReadAddr:开始读数的地址
- //返回值 :读到的数据
- 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;
- }
- //在AT24CXX指定地址写入一个数据
- //WriteAddr :写入数据的目的地址
- //DataToWrite:要写入的数据
- 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);
- }
- //在AT24CXX里面的指定地址开始写入长度为Len的数据
- //该函数用于写入16bit或者32bit的数据.
- //WriteAddr :开始写入的地址
- //DataToWrite:数据数组首地址
- //Len :要写入数据的长度2,4
- void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
- {
- u8 t;
- for(t=0;t<Len;t++)
- {
- AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
- }
- }
- //在AT24CXX里面的指定地址开始读出长度为Len的数据
- //该函数用于读出16bit或者32bit的数据.
- //ReadAddr :开始读出的地址
- //返回值 :数据
- //Len :要读出数据的长度2,4
- u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
- {
- u8 t;
- u32 temp=0;
- for(t=0;t<Len;t++)
- {
- temp<<=8;
- temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
- }
- return temp;
- }
- //检查AT24CXX是否正常
- //这里用了24XX的最后一个地址(255)来存储标志字.
- //如果用其他24C系列,这个地址要修改
- //返回1:检测失败
- //返回0:检测成功
- u8 AT24CXX_Check(void)
- {
- u8 temp;
- temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
- if(temp==0X55)return 0;
- else//排除第一次初始化的情况
- {
- AT24CXX_WriteOneByte(255,0X55);
- temp=AT24CXX_ReadOneByte(255);
- if(temp==0X55)return 0;
- }
- return 1;
- }
- //在AT24CXX里面的指定地址开始读出指定个数的数据
- //ReadAddr :开始读出的地址 对24c02为0~255
- //pBuffer :数据数组首地址
- //NumToRead:要读出数据的个数
- void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
- {
- while(NumToRead)
- {
- *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
- NumToRead--;
- }
- }
- //在AT24CXX里面的指定地址开始写入指定个数的数据
- //WriteAddr :开始写入的地址 对24c02为0~255
- //pBuffer :数据数组首地址
- //NumToWrite:要写入数据的个数
- void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
- {
- while(NumToWrite--)
- {
- AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
- WriteAddr++;
- pBuffer++;
- }
- }
复制代码- 24cxx.h:
- #ifndef __24CXX_H
- #define __24CXX_H
- #include "myiic.h"
- #define AT24C01 127
- #define AT24C02 255
- #define AT24C04 511
- #define AT24C08 1023
- #define AT24C16 2047
- #define AT24C32 4095
- #define AT24C64 8191
- #define AT24C128 16383
- #define AT24C256 32767
- //Mini STM32开发板使用的是24c02,所以定义EE_TYPE为AT24C02
- #define EE_TYPE AT24C02
-
- u8 AT24CXX_ReadOneByte(u16 ReadAddr); //指定地址读取一个字节
- void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite); //指定地址写入一个字节
- void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址开始写入指定长度的数据
- u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len); //指定地址开始读取指定长度数据
- void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据
- void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); //从指定地址开始读出指定长度的数据
- u8 AT24CXX_Check(void); //检查器件
- void AT24CXX_Init(void); //初始化IIC
- #endif
- main.c:
- #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "led.h"
- #include "lcd.h"
- #include "24cxx.h"
- #include "key.h"
- //要写入到24c02的字符串数组
- const u8 TEXT_Buffer[]={"Explorer STM32F4 IIC TEST"};
- #define SIZE sizeof(TEXT_Buffer)
-
- int main(void)
- {
- u8 key;
- u16 i=0;
- u8 datatemp[SIZE];
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
- delay_init(168); //初始化延时函数
- uart_init(115200); //初始化串口波特率为115200
-
- LED_Init(); //初始化LED
- KEY_Init(); //按键初始化
- AT24CXX_Init(); //IIC初始化
-
- while(AT24CXX_Check())//检测不到24c02
- {
- printf("24C02 Check Failed!");
- delay_ms(500);
- printf("Please Check! ");
- delay_ms(500);
- LED0=!LED0;//DS0闪烁
- }
-
- while(1)
- {
- key=KEY_Scan(0);
- if(key==KEY1_PRES)//KEY1按下,写入24C02
- {
- printf("Start Write 24C02....");
- AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
- printf("24C02 Write Finished!"); //提示传送完成
- }
- if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
- {
- printf("Start Read 24C02.... ");
- AT24CXX_Read(0,datatemp,SIZE);
- printf("The Data Readed Is: \r\n ");//提示传送完成
- printf("%s\r\n ",datatemp);
- }
- }
- }
复制代码
|