先说说废话,第一篇博客文章,文中有不足之处或者有错误的地方还望指正。
IIC 总线(AT24C02)
一、先了解了解IIC总线协议基本知识
1.IIC协议是二线制,信号线包含SDA和SCL,且信号线是双向的,开路结构,需要通过上拉电阻到VCC,具体的电阻值影响的是信号反应速度和驱动能力。
2.IC传输时,要保持SCL为高电平不变,SDA保持稳定;IIC开始的条件:SCL保持高电平,SDA从高电平跳跃到低电平,IIC停止通讯的条件:SCL保持高电平,SDA从低电平跳跃到高电平。
3.串行的8位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s(一般不用考虑)。
单片机一般最高使用到400KBit/s(具体查看数据手册可知),可知IIC的时钟周期可以设置为:2.5us - 10us。
二、以下是此次学习IIC通讯协议,调试过程中一些要点
1.只有当SCL为高电平时,SDA线上的数据才有效,且在SCL电平升高前,SDA电平要保持稳定。
2.SCL,SDA为高电平时,总线为空闲状态。
3.在主机读数据前,应把SDA拉高,释放SDA,交由从机控制。
4.要注意每种功能实现时总线的时序性(比如:start -> write - ack)。
5.要注意写数据周期不能超过write cycle time(byte or page),查看数据手册可知。
6.在写完数据后读取数据时,需做延时,否则会读取不到数据(具体时间和设置时钟周期有关)。
7.在接收ACK时,应先释放SDA,在SCL为高电平时接收(与第3点一样)。
三、调试过程:先看数据手册时序图
1.启动、停止IIC
启动过程:拉高SCL,SDA (延时)-->拉低SDA(延时) -->拉低SCL(延时),最后拉低SCL是为了控制总线为忙状态
停止过程:拉高SDA(延时) -->拉高SCL(延时) -->拉高SDA(延时)
void IIC_START(void) { SDA_H; SCL_H; delay_us(5); SDA_L; delay_us(5); SCL_L; //控制总线为忙状态 delay_us(5); }
void IIC_STOP(void) { // SCL_L;// SDA_L; delay_us(5); SCL_H; delay_us(5); SDA_H; delay_us(5); }
2.写数据、读数据
这里需要特别注意,读数据时,需先将SDA拉高,释放控制权,交由从机控制。
写数据:先将数据准备好(SDA拉高、或拉低)-->拉高SCL(延时)(此时从机读取SDA的电平) -->拉低SCL(延时)(等待下一位数据,即DATA CHANG)
读数据:拉高SDA(延时) -->拉高SCL(延时) -->读取数据 -->拉低SCL(延时)(等待下一位数据,即DATA CHANG)
void IIC_Write_Byte(u8 ucdata) { u8 i = 8; // SCL_L;// while(i--) { if(ucdata & 0x80) { // 1.先准备数据,从机在SCL为高电平时读入数据 SDA_H; } else { SDA_L; } delay_us(5); SCL_H; // 2.拉高SCL,从机读取稳定数据 delay_us(5); SCL_L; // 3.拉低SCL,等待下一位数据 ucdata <<= 1; } }
u8 IIC_Read_Byte(void) { u8 i = 8, ucdata = 0x00; // SCL_L;// SDA_H; // 1.先拉高SDA,释放SDA总线 delay_us(5); while(i--) { SCL_H; // 2.再拉高SCL,从机在SCL为高电平时输出稳定的数据 delay_us(5); ucdata <<= 1; if(Read_SDA) { //读数据 ucdata = ucdata | 0x01; } SCL_L; // 3.拉低SCL,等待下一位数据 delay_us(5); } return ucdata; }
3.获取应答、回复应答
在读数据或写数据,在第9个时钟周期回复应答或获取应答,即读取8bit数据或写8bit数据后应答。其实这里与读数据,写数据是一样的,只是这个过程只有一个周期。
回复应答(写数据),获取应答(读数据)。
u8 IIC_Get_ACK(void) { u8 wait_tiem,ack; ack = 0; SDA_H; // 1.先拉高SDA,释放SDA控制,交给从机控制 delay_us(5); SCL_H; // 2.再拉高SCL,从机输出稳定数据 delay_us(5); while(Read_SDA) { //主机读取数据 wait_tiem++; if(wait_tiem > 10) { ack = 1; break; } } delay_us(5); SCL_L; // 3.拉低SCL,完成一个时钟周期 return ack; }
void IIC_Set_ACK(u8 ack) { if(ack) { SDA_H; // 1.先准备数据 } else { SDA_L; } // SCL_L; delay_us(5); SCL_H; // 2.拉高SCL,从机读取稳定的数据 delay_us(5); SCL_L; // 3.拉低SCL,完成一个时钟周期 }
IIC总线协议底层程序完成,接下来是写芯片应用程序,读写过程一定要严格按照芯片数据手册的时序。
4.应用(AC24C02)
①.首先要知道设备地址,数据存储地址(个人理解)。AT24C02芯片是一个2K 大小的EEPROM存储芯片,2 * 1024 = 2048 字节,总共256个数据。
设备地址由固定的“1010” + 三位硬件选择位(与硬件连接有关) + 一位读/写命令位;
数据存储地址:0 - 255(0x00 - 0xFF)。
②写数据,可以单次写8位(字节写),或多次写8位(页写);1K/2K的EEPROM的芯片以8位数据(即一个字节)为一页。
byte write:启动 --> 写设备地址+写命令 -->获取应答(ack) -->写位置地址 -->获取应答 -->写数据 -->获取应答 --> 停止
page write:与byte write 过程一样,写完一页数据获取到从机应答后继续写数据,直到写完最后一个数据获取到应答后发送停止。
/*************************** **函数名称:IIC_Write_Data **参 数:DeviceAddr:设备地址 WordAddr:数据存储地址 *Data :需要写的数据 num :写个数 **作 用:随机写数据,返回值为1:写数据成功;返回值为0:写数据失败 **日 期:2020.2.17 **作 者:Yangyang ****************************/ u8 IIC_Write_Data(u8 DeviceAddr,u16 WordAddr,u8 *Data,u8 num) { u8 AddrTemp, i; IIC_START(); //启动 IIC_Write_Byte(DeviceAddr & 0xfe); //设备地址 + 写命令 if(IIC_Get_ACK()) { IIC_STOP(); return 0; } AddrTemp = (WordAddr & 0xff); //存储数据地址 IIC_Write_Byte(AddrTemp); if(IIC_Get_ACK()) { IIC_STOP(); return 0; } for(i = 0; i < num; i++) { IIC_Write_Byte(*(Data + i));//写数据 if(IIC_Get_ACK()) { IIC_STOP(); return 0; } } IIC_STOP(); delay_ms(3);//最少需延时2ms,在写完数据后读数据,不延时会读取数据失败(也可以函数外部延时) return 1; }
③读数据,读数据可分为当前地址读数据、随机地址读数据和顺序地址读数据(此处就不说当前地址读数据,基本不用)
随机读数据:启动 --> 先写设备地址 + 写命令 -->获取应答 --> 写读取数据的位置 -->获取应答 --> 再启动 --> 写设备地址 + 读命令 --> 获取应答 --> 读取数据 --> 回复不应答 --> 停止
顺序读数据:读取方式与随机读数据相似,在读取数据后是回复应答,当读到最后一个数据后再回复不应答:....读取数据n-1 --> 回复应答 -->读取数据n --> 回复不应答 --> 停止
/*************************** **函数名称:IIC_Read_Data **参 数:DeviceAddr: 设备地址 WordAddr: 数据存储地址 *Data : 读取的数据 num : 读取个数 **作 用:随机读取数据,顺序读取数据,返回值为1:读取数据成功;返回值为0:读取数据失败 **日 期:2020.2.17 **作 者:Yangyang ****************************/ u8 IIC_Read_Data(u8 DeviceAddr,u16 WordAddr,u8 *Data,u8 num) { u8 AddrTemp, i; IIC_START(); //启动 IIC_Write_Byte(DeviceAddr & 0xfe); //设备地址 + 写命令 if(IIC_Get_ACK()) { IIC_STOP(); //Internally organized with 32 pages of 8 byteseach, return 0; //the 2K requires an 8-bit data word address for random word addressing. } //此说明在数据手册可知 AddrTemp = (WordAddr & 0xff); //地址可取0 - 255 IIC_Write_Byte(AddrTemp); if(IIC_Get_ACK()) { IIC_STOP(); return 0; } delay_us(100); IIC_START(); //再启动 IIC_Write_Byte(DeviceAddr | 0x01); //设备地址 + 读命令 if(IIC_Get_ACK()) { // IIC_STOP(); return 0; } for(i = 0; i < num; i++) { *(Data + i) = IIC_Read_Byte(); //读数据 if(i < num - 1) { IIC_Set_ACK(0); //接收N-1个数据后应该从机 } else { //接收最后一个数据时不应答从机 IIC_Set_ACK(1); } } IIC_STOP(); //停止 return 1; }
④测试
使用硬件:STM32F103 + AT24C02
#define at24c02_byte_num 8
u8 IIC_ref_data[at24c02_byte_num] = {0};
u8 IIC_ref_data2[at24c02_byte_num] = {0};
u8 IIC_write_data[at24c02_byte_num] = {0x20,0x20,0x02,0x29,0x21,0x51,0x55,0x55}
u8 Test_IIC(void) { u8 i = 0; IIC_Write_Data(0xa0,0,IIC_write_data,at24c02_byte_num); // delay_ms(3); i = IIC_Read_Data(0xa0,0,IIC_ref_data2,at24c02_byte_num); return i; }