IIC详解
来源:互联网 发布:战略管理 知乎 编辑:程序博客网 时间:2024/06/14 14:59
1、IIC的定义:
IIC 即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦半导体公司在八十年代初设计出来的一种简单、双向、二线制、同步串行总线,主要是用来连接整体电路(ICS) ,IIC是一种多向控制总线,也就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实时数据传输的控制源。这种方式简化了信号传输总线接口。I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
SDA(数据线)和SCL(时钟线)都是集电极开路或漏极开路结构的,只能输出“0”,不能输出“1”,都必须接上拉电阻。
当需要输入数据时,将一个读数据用的输入端接在输出端。
SCL线的时钟同步:SCL由于具有线“与”的逻辑功能,即只要有一个节点发送低电平时,总线上就表现为低电平。当所有的节点都发送高电平时,总线才能表现为高电平。正是由于线“与”逻辑功能的原理,当多个节点同时发送时钟信号时,在总线上表现的是统一的时钟信号。
♣. SDA线的仲裁:SDA线的仲裁也是建立在总线具有线“与”逻辑功能的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,退出竞争。SDA线的仲裁可以保证I2C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线。
IIC的时序图
2、启动信号
因为 只有两条线,从时序图上 看 也是 很简洁 的。
下面由GIF动画展示启动信号的过程
动画中红色的阴影区域,表示 IIC 启动信号
- /*******************************************************************************
- ** Function name: I2C_Start
- ** Descriptions: 开始发送一个I2C起始信号
- ** Input parameters: None
- ** output parameters: None
- ** Created Date:
- *******************************************************************************/
- void I2C_Start(void)
- {
- //IO输出
- SDA_OUT();
- SCL_OUT();
- I2C_DELAY();
- //IO置高
- SDA_SET();
- SCL_SET();
- //延时
- I2C_DELAY();
- //为低
- SDA_CLR();
- I2C_DELAY();
- I2C_DELAY();
- SCL_CLR();
- }
/********************************************************************************* Function name: I2C_Start** Descriptions: 开始发送一个I2C起始信号** Input parameters: None** output parameters: None** Created Date: *******************************************************************************/void I2C_Start(void){//IO输出SDA_OUT(); SCL_OUT(); I2C_DELAY();//IO置高SDA_SET(); SCL_SET(); //延时I2C_DELAY(); //为低SDA_CLR();I2C_DELAY();I2C_DELAY();SCL_CLR();}
3、停止信号
理解了开始信号,再来看 停止信号,分分秒秒 就会的事情,前提:先理解 开始信号后,再来看 停止信号。- /*******************************************************************************
- ** Function name: I2C_Stop
- ** Descriptions: 发送一个I2C总线结束信号
- ** Input parameters: None
- ** output parameters: None
- ** Created Date:
- *******************************************************************************/
- void I2C_Stop(void)
- {
- //IO输出
- SDA_OUT();
- SCL_OUT();
- //IO置0
- SDA_CLR();
- SCL_CLR();
- I2C_DELAY();
- SCL_SET();
- //延时
- I2C_DELAY();
- I2C_DELAY();
- I2C_DELAY();
- //SDA置1
- SDA_SET();
- I2C_DELAY();
- I2C_DELAY();
- }
/********************************************************************************* Function name: I2C_Stop** Descriptions: 发送一个I2C总线结束信号** Input parameters: None** output parameters: None** Created Date: *******************************************************************************/void I2C_Stop(void){//IO输出SDA_OUT(); SCL_OUT();//IO置0SDA_CLR(); SCL_CLR(); I2C_DELAY();SCL_SET();//延时I2C_DELAY(); I2C_DELAY();I2C_DELAY();//SDA置1SDA_SET();I2C_DELAY();I2C_DELAY();}
4、IIC 写 一个 字节
学习了 启动 和停止 信号后,就可以开始学习 写入一个字节 命令了该命令 过程是,
文字描述还是比较 抽象 的,看一下 动画
利用 C51 编写代码,如下
- *******************************************************************************
- ** Function name: I2C_Send_byte
- ** Descriptions: 主机从I2C总线发送一个字节
- ** Input parameters: data
- ** output parameters: 0-false 1_ture
- ** Created Date:
- *******************************************************************************/
- uint8 I2C_Send_byte(uint8 data)
- {
- uint8 k;
- //发送8bit数据
- for(k=0;k<8;k++){
- I2C_DELAY();
- if(data&0x80){
- SDA_SET();
- }
- else{
- SDA_CLR();
- }
- data=data<<1;
- I2C_DELAY();
- SCL_SET();
- I2C_DELAY();
- I2C_DELAY();
- SCL_CLR();
- }
- //延时读取ACK响应
- I2C_DELAY();
- SDA_SET();
- //置为输入线
- SDA_IN();
- I2C_DELAY();
- SCL_SET();
- I2C_DELAY(); //这里出现了问题,延时变的无限大
- //读数据
- k=SDA_READ();
- I2C_DELAY();
- SCL_CLR();
- I2C_DELAY();
- SDA_OUT();
- if(k){ ////NACK响应
- return 0;
- }
- return 1;
- }
********************************************************************************* Function name: I2C_Send_byte** Descriptions: 主机从I2C总线发送一个字节** Input parameters: data** output parameters: 0-false 1_ture** Created Date: *******************************************************************************/uint8 I2C_Send_byte(uint8 data){uint8 k;//发送8bit数据for(k=0;k<8;k++){I2C_DELAY();if(data&0x80){SDA_SET();}else{SDA_CLR();}data=data<<1;I2C_DELAY();SCL_SET();I2C_DELAY();I2C_DELAY();SCL_CLR();}//延时读取ACK响应I2C_DELAY();SDA_SET();//置为输入线SDA_IN();I2C_DELAY();SCL_SET(); I2C_DELAY(); //这里出现了问题,延时变的无限大//读数据k=SDA_READ();I2C_DELAY();SCL_CLR();I2C_DELAY();SDA_OUT();if(k){ ////NACK响应return 0;}return 1;}
5、IIC 读取一个字节
读取字节 的 资料 不多,和写一个字节有区别,不过 如果理解怎样写一个字节后,再理解 读字节 难度不大。- /*******************************************************************************
- ** Function name: I2C_Receive_byte
- ** Descriptions: 主机从I2C总线接收一个字节
- ** Input parameters: None
- ** output parameters: data
- ** Created Date:
- *******************************************************************************/
- uint8 I2C_Receive_byte(void)
- {
- uint32 k,data;
- //接收8bit数据
- //置为输入线
- SDA_IN();
- data=0;
- for(k=0;k<8;k++){
- I2C_DELAY();
- SCL_SET();
- I2C_DELAY();
- //读数据
- data=data|SDA_READ();
- data=data<<1;
- I2C_DELAY();
- SCL_CLR();
- I2C_DELAY();
- }
- data=data>>1; //往回移动1次
- //返回ACK响应
- //置为输出线
- SDA_OUT();
- SDA_CLR(); //输出0-ACK
- I2C_DELAY();
- SCL_SET();
- I2C_DELAY();
- I2C_DELAY();
- SCL_CLR();
- I2C_DELAY();
- SDA_OUT();
- //返回读取的数据
- return (uint8)data;
- }
/********************************************************************************* Function name: I2C_Receive_byte** Descriptions: 主机从I2C总线接收一个字节** Input parameters: None** output parameters: data** Created Date: *******************************************************************************/uint8 I2C_Receive_byte(void){ uint32 k,data; //接收8bit数据 //置为输入线 SDA_IN(); data=0; for(k=0;k<8;k++){ I2C_DELAY(); SCL_SET(); I2C_DELAY(); //读数据 data=data|SDA_READ(); data=data<<1; I2C_DELAY(); SCL_CLR(); I2C_DELAY(); } data=data>>1; //往回移动1次 //返回ACK响应 //置为输出线 SDA_OUT(); SDA_CLR(); //输出0-ACK I2C_DELAY(); SCL_SET(); I2C_DELAY(); I2C_DELAY(); SCL_CLR(); I2C_DELAY(); SDA_OUT(); //返回读取的数据 return (uint8)data;}
6、IIC发应答和检测应答
MASTER 指主控制端,在一般系统中就是我们常说的单片机了;SLAVE是指具备I2C协议的专用IC
1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
7、器件的地址
再来看一些常用器件的地址码
8、写一个字节(下挂多个设备)
写一个字节方式,写完一个字节后就发停止符
9、指定任意地址读(下挂多个设备)
- /*******************************************************************************
- ** Function name: I2C_Write_nbyte
- ** Descriptions: 向I2C总线器件上指定的地址写入多个数据
- ** Input parameters: 1=addr,2_sub_addr,3-p,4-num
- ** output parameters: 0-false,1-ture
- ** Created Date:
- *******************************************************************************/
- uint8 I2C_Write_nbyte(uint8 addr,uint8 sub_addr,uint8 *data_cache,uint32 num)
- {
- uint32 temp_state,k;
- //启动I2C总线
- I2C_Start();
- I2C_Start();
- //发送器件地址
- temp_state=I2C_Send_byte(addr&0xFE);//写模式
- if(!temp_state){
- goto err_i2c;
- }
- //发送器件子地址
- temp_state=I2C_Send_byte(sub_addr);
- if(!temp_state){
- goto err_i2c;
- }
- //发送后面的数据
- for(k=0;k<num;k++){
- temp_state=I2C_Send_byte(data_cache[k]);//写
- if(!temp_state){
- goto err_i2c;
- }
- }
- I2C_Stop();
- return 1;
- err_i2c:
- I2C_Stop();
- return 0;
- }
- /*******************************************************************************
- ** Function name: I2C_Read_nbyte
- ** Descriptions: 从I2C总线器件上指定的地址读取多个数据
- ** Input parameters: 1=addr,2_sub_addr,3-p,4-num
- ** output parameters: 0-false,1-ture
- ** Created Date:
- *******************************************************************************/
- uint8 I2C_Read_nbyte(uint8 addr,uint8 sub_addr,uint8 *data_cache,uint32 num)
- {
- uint32 temp_state,k;
- //启动I2C总线
- I2C_Start();
- I2C_Start();
- //发送器件地址
- temp_state=I2C_Send_byte(addr&0xFE);//写模式
- if(!temp_state){
- goto err_i2c;
- }
- //发送器件子地址
- temp_state=I2C_Send_byte(sub_addr);
- if(!temp_state){
- goto err_i2c;
- }
- //重启动I2C总线
- I2C_Start();
- //发送器件地址
- temp_state=I2C_Send_byte(addr|0x01);//读模式
- if(!temp_state){
- goto err_i2c;
- }
- //读取后面的数据
- for(k=0;k<num;k++){
- data_cache[k]=I2C_Receive_byte();//读取数据
- }
- I2C_Stop();
- return 1;
- err_i2c:
- I2C_Stop();
- //I2C_Start();
- return 0;
- }
/********************************************************************************* Function name: I2C_Write_nbyte** Descriptions: 向I2C总线器件上指定的地址写入多个数据** Input parameters: 1=addr,2_sub_addr,3-p,4-num** output parameters: 0-false,1-ture** Created Date: *******************************************************************************/uint8 I2C_Write_nbyte(uint8 addr,uint8 sub_addr,uint8 *data_cache,uint32 num){ uint32 temp_state,k; //启动I2C总线 I2C_Start(); I2C_Start(); //发送器件地址 temp_state=I2C_Send_byte(addr&0xFE);//写模式 if(!temp_state){ goto err_i2c; } //发送器件子地址 temp_state=I2C_Send_byte(sub_addr); if(!temp_state){ goto err_i2c; } //发送后面的数据 for(k=0;k<num;k++){ temp_state=I2C_Send_byte(data_cache[k]);//写 if(!temp_state){ goto err_i2c; } } I2C_Stop(); return 1;err_i2c: I2C_Stop(); return 0;}/********************************************************************************* Function name: I2C_Read_nbyte** Descriptions: 从I2C总线器件上指定的地址读取多个数据** Input parameters: 1=addr,2_sub_addr,3-p,4-num** output parameters: 0-false,1-ture** Created Date: *******************************************************************************/uint8 I2C_Read_nbyte(uint8 addr,uint8 sub_addr,uint8 *data_cache,uint32 num){ uint32 temp_state,k; //启动I2C总线 I2C_Start(); I2C_Start(); //发送器件地址 temp_state=I2C_Send_byte(addr&0xFE);//写模式 if(!temp_state){ goto err_i2c; } //发送器件子地址 temp_state=I2C_Send_byte(sub_addr); if(!temp_state){ goto err_i2c; } //重启动I2C总线 I2C_Start(); //发送器件地址 temp_state=I2C_Send_byte(addr|0x01);//读模式 if(!temp_state){ goto err_i2c; } //读取后面的数据 for(k=0;k<num;k++){ data_cache[k]=I2C_Receive_byte();//读取数据 } I2C_Stop(); return 1;err_i2c: I2C_Stop(); //I2C_Start(); return 0;}
10、写字符串 和 读字符串(下挂多个设备)
写字符串方式,写完一个字节后 继续 写字节,直到发停止符读字符串方式,读完一个字节后 继续 读字节,直到发停止符
11、PCF8563芯片的简介
驱动程序如下:
- /*********************************************************************************************************
- ** Function name: PCF8563_Set
- ** Descriptions: 设置PCF8563
- ** input parameters: NONE
- ** Output parameters: NONE
- ** Returned value: NONE
- *********************************************************************************************************/
- uint8 PCF8563_Set(timeType *tp)
- {
- uint32 k;
- uint8 year;
- uint8 week;
- uint8 tempdata[7];
- ///////转换成BCD码
- tempdata[0] = tp->second;
- tempdata[1] = tp->minute;
- tempdata[2] = tp->hour;
- tempdata[3] = tp->day;
- if(tp->week==7){
- week=0;
- }
- else{
- week=tp->week;
- }
- tempdata[4] = week;
- tempdata[5] = tp->month;
- //if (tp->year >= 2000){
- tempdata[5] &= 0x7F; ////世纪位清零
- // year = tp->year - 2000;
- tempdata[6] = tp->year;
- //}
- //else{
- // tempdata[5] |= 0x80; ////世纪位置位
- // year = tp->year - 1900;
- // tempdata[6] = HEC_to_BCD(year);
- //}
- //判断是否校时成功
- k=0;
- while(k<10){
- if(I2C_Write_nbyte(PCF8563ADR, 0x02,tempdata, 7)==1){
- return(1);
- }
- k++;
- //延时等待3ms
- #if WDOG_EN
- wdogFeed();
- #endif
- SysCtlDelay(40*1000);
- }
- return(0);
- }
- /*********************************************************************************************************
- ** Function name: PCF8563_Read
- ** Descriptions: 读取PCF8563
- ** input parameters: tp 指向时间结构体的指针
- ** Output parameters: FALSE or TRUE
- ** Returned value: NONE
- *********************************************************************************************************/
- uint8 PCF8563_Read(timeType *tp)
- {
- uint32 time_flag;
- uint32 div_run_4;
- uint8 G_time_temp[7];
- timeType time_G;
- time_flag=0;
- if(I2C_Read_nbyte(PCF8563ADR, 0x02,G_time_temp,7) == 0){
- return 0; //////直接返回,使用以前的旧值
- }
- time_G.second = BCD_to_HEC((G_time_temp[0]&0x7f));
- time_G.minute = BCD_to_HEC((G_time_temp[1]&0x7f));
- time_G.hour = BCD_to_HEC((G_time_temp[2]&0x3f));
- time_G.day = BCD_to_HEC((G_time_temp[3]&0x3f));
- time_G.week = BCD_to_HEC((G_time_temp[4]&0x07));
- if(time_G.week==0){
- time_G.week=7;
- }
- time_G.month = BCD_to_HEC((G_time_temp[5]&0x1f));
- time_G.year = BCD_to_HEC(G_time_temp[6]);
- if(G_time_temp[5]&0x80){
- time_G.year+=1900;
- }
- else{
- time_G.year+=2000;
- }
- //判断是否是闰年,在1949~2049年之内
- div_run_4 = time_G.year / 4;
- div_run_4 = div_run_4 * 4;
- //检查是否整除
- if(div_run_4 == time_G.year){
- //说明闰年
- div_run_4 = 1;
- }
- else{
- //非闰年
- div_run_4 = 0;
- }
- //////获得实际的时间值
- //对时间进行检查
- //1949~2049
- if(time_G.second > 59){ //秒错误
- time_flag++;
- }
- if(time_G.minute > 59){ //分错误
- time_flag++;
- }
- if(time_G.hour > 23){ //时错误
- time_flag++;
- }
- if(time_G.day > 31){ //日错误
- time_flag++;
- }
- if(time_G.day== 0){ //日错误
- time_flag++;
- }
- if(time_G.month > 12){ //月错误
- time_flag++;
- }
- if(time_G.month == 0){ //月错误
- time_flag++;
- }
- //检查月与日
- //检查二月
- if(div_run_4){
- //检查闰月
- if((time_G.month == 2) && (time_G.day > 29)){ //月错误
- time_flag++;
- }
- }
- else{
- //检查闰月
- if((time_G.month == 2) && (time_G.day > 28)){ //月错误
- time_flag++;
- }
- }
- //检查其他月
- switch(time_G.month){
- case 1:
- case 3:
- case 5:
- case 7:
- case 8:
- case 10:
- case 12:{
- if(time_G.day>31){ //日错误
- time_flag++;
- }
- break;
- }
- case 4:
- case 6:
- case 9:
- case 11:{
- if(time_G.day>30){ //日错误
- time_flag++;
- }
- break;
- }
- }
- if(time_G.week>=8){ //周错误
- time_flag++;
- }
- if((time_G.year>=2050)||(time_G.year<1949)){ //周错误
- time_flag++;
- }
- if(time_flag!=0){
- rtc_state=1;
- return 4;
- }
- *tp=time_G;
- return 1;
- }
- /*********************************************************************************************************
- ** Function name: PCF8563_Read_BCD
- ** Descriptions: 读取PCF8563,BCD码表示
- ** input parameters: tp 指向时间结构体的指针
- ** Output parameters: FALSE or TRUE
- ** Returned value: NONE
- *********************************************************************************************************/
- uint8 PCF8563_Read_BCD(timeType *tp)
- {
- uint8 temp[7];
- if(I2C_Read_nbyte(PCF8563ADR, 0x02,temp,7) == 0){
- return 0; //////直接返回,使用以前的旧值
- }
- tp->second = ((temp[0]&0x7f));
- tp->minute = ((temp[1]&0x7f));
- tp->hour = ((temp[2]&0x3f));
- tp->day = ((temp[3]&0x3f));
- tp->week = ((temp[4]&0x07));
- if(tp->week==0){
- tp->week=7;
- }
- tp->month = temp[5]&0x1f;
- tp->year = temp[6];
- if(temp[5]&0x80){
- tp->year |= 0x1900;
- }
- else{
- tp->year |= 0x2000;
- }
- return 1;
- }
/*********************************************************************************************************** Function name: PCF8563_Set** Descriptions: 设置PCF8563** input parameters: NONE** Output parameters: NONE** Returned value: NONE*********************************************************************************************************/uint8 PCF8563_Set(timeType *tp){uint32 k;uint8 year;uint8 week;uint8 tempdata[7];///////转换成BCD码tempdata[0] = tp->second;tempdata[1] = tp->minute;tempdata[2] = tp->hour;tempdata[3] = tp->day;if(tp->week==7){week=0;}else{week=tp->week; } tempdata[4] = week;tempdata[5] = tp->month;//if (tp->year >= 2000){tempdata[5] &= 0x7F; ////世纪位清零// year = tp->year - 2000;tempdata[6] = tp->year;//}//else{// tempdata[5] |= 0x80; ////世纪位置位// year = tp->year - 1900;// tempdata[6] = HEC_to_BCD(year);//} //判断是否校时成功k=0;while(k<10){ if(I2C_Write_nbyte(PCF8563ADR, 0x02,tempdata, 7)==1){ return(1);}k++;//延时等待3ms
以上程序完整代码和PCF8563的中文数据手册下载地址:点击下载
- IIC详解
- IIC详解
- IIC时序详解
- IIC时序详解
- linux IIC详解
- S3C2440硬件IIC详解
- LINUX IIC 驱动详解
- IIC时序详解
- IIC总线时序详解
- IIC总线详解
- IIC总线时序详解
- linux IIC详解
- linux IIC driver详解以及 IIC sensor 驱动
- IIC
- IIC
- IIC
- IIC
- IIC
- 洋葱架构简介——分离是为了更好的结合
- 环境变量的配置
- FileStore::mkjournal()&&FileJournal::create()
- Hadoop集群配置ssh时,slave无法连接到master
- Spring RedisTemplate使用多种operation
- IIC详解
- 【Eternallyc】JAVA的多态
- 二维数组初始化的形式有?
- 引用类型(四)——基本包装类型(二)
- 字符串-leetcode 541. Reverse String II
- C++中随机函数rand()和srand()的用法
- QT遍历窗体的控件
- C语言入门第二篇,基本数据类型
- @RequestBody和@RequestParam区别