协议— IIC(模拟IIC原理与使用)

基础认识

模拟IIC是模拟IIC通信时序,一些单片机有硬件IIC接口,如果没有硬件IIC可以通过普通GPIO模拟得到,这里将介绍如何实现模拟IIC

数据线:SDA

时钟线:SCL

注意:

1.只允许有一个主设备,总线上可以挂接多个从设备,

2.总线连线一般不超过2米

3.两线(SDA,SCL)的总线连接,两条总线都需要1~10K的上拉电阻

3.每个器件地址唯一(7位地址,最低位0为发送,1为读取),最多127个器件地址,每次通信之前需要先发送地址

起始/停止信号

起始信号:

当时钟线和数据线都为高电平时,IIC总线上的所有从设备都处于空闲状态。当时钟线和数据线都为高电平时,数据线从高电平到低电平的跳动,被定义为起始信号。

 1 //IIC起始信号,SDA下降沿
 2 void iic_start()
 3 {
 4     SDA_OUT_Mode(); //设置SDA为输出
 5     SDA_Control(1);            
 6     SCL_Control(1);
 7     delay_us(4);
 8      SDA_Control(0);//下降沿
 9     delay_us(4);
10     SCL_Control(0);//准备发送数据
11 }

停止信号:

当时钟线为高电平,数据线为低电平时,数据线从低到高的跳动,被定义为停止信号。

 1 //IIC停止信号,SDA上升沿
 2 void iic_stop()
 3 {
 4     SDA_OUT_Mode();//设置SDA为输出
 5     SCL_Control(0);
 6     SDA_Control(0);
 7      delay_us(4);
 8     SCL_Control(1);
 9     delay_us(4);        
10        SDA_Control(1);//上升沿
11 }

主机收发一个字节

主机发送数据信号:

时钟线高电平和数据线为低电平时,当时钟线拉低之后,IIC从设备会收到一个数据0;

时钟线高电平和数据线为高电平时,当时钟线拉低之后,IIC从设备会收到一个数据1。

编程思路:因为时钟线为高电平时,数据线是不能动作的,因为有可能会无触发为起始或者停止信号,所以必须先把时钟线拉低后再去改变数据线的电平,然后数据线电平不变的情况下,拉高时钟线然后再拉低时钟线,以发送数据0或1。

 1 //IIC发送一个字节    
 2 void iic_write_byte(u8 c)
 3 { 
 4    u8 i;   
 5    SDA_OUT_Mode();//设置SDA为输出    
 6    SCL_Control(0);//拉低时钟开始数据传输
 7    for(i=0;i<8;i++)
 8    {              
 9       SDA_Control((c&0x80)>>7);//数据变化
10       c<<=1;       
11       delay_us(2);
12       SCL_Control(1);//时钟线产生下降沿
13       delay_us(2); 
14       SCL_Control(0);
15       delay_us(2);
16     }
17 }

应答信号的获取:

当主设备发送一个字节完成后从设备会产生一个应答信号,读取是否有应答信号可以判断出主设备是否发送发出数据完成,并被从设备接收,但此判断非必须的,编程可以不考虑

 1 //等待应答信号到来
 2 //返回值:1,接收应答失败
 3 //        0,接收应答成功
 4 u8 iic_wait_ack(void)
 5 {
 6     u8 outTime=0;
 7         SDA_OUT_Mode();//设置SDA为输出
 8     SDA_Control(1);delay_us(1); //此条后SDA电平已经无变化
 9     SCL_Control(1);delay_us(1);    
10         SDA_IN_Mode(); //设置SDA为输入  
11     while(SDA_Read())
12     {
13        outTime++;
14        if(outTime>250)
15        {
16           iic_stop();
17           return 1;
18         }
19     }
20     SCL_Control(0);//时钟输出0        
21     return 0;  
22 }

主机接收数据信号:

 1 //产生ACK应答
 2 void iic_ack(void)
 3 {
 4     SCL_Control(0);
 5     SDA_OUT_Mode();
 6     SDA_Control(0);
 7     delay_us(2);
 8     SCL_Control(1);
 9     delay_us(2);
10     SCL_Control(0);
11 }
12 //不产生ACK应答            
13 void iic_nack(void)
14 {
15     SCL_Control(0);
16     SDA_OUT_Mode();
17     SDA_Control(1);
18     delay_us(2);
19     SCL_Control(1);
20     delay_us(2);
21     SCL_Control(0);
22 }    
23   
24 //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
25 u8 iic_read_byte(unsigned char ack)
26 {
27     unsigned char i,receive=0;
28     SCL_Control(0);//拉低时钟开始数据传输
29     SDA_OUT_Mode();//设置SDA为输出
30     SDA_Control(1); //此条后SDA电平已经无变化
31     SDA_IN_Mode(); //设置SDA为输入  
32     for(i=0;i<8;i++ )
33     {
34        SCL_Control(1);//锁定数据,让数据不变化
35        receive<<=1;
36        if(SDA_Read())receive++;  //读取数据 
37        delay_us(1); 
38        SCL_Control(0);//释放锁定,开始下一个数据检测
39        delay_us(2);
40    }    
41    /*
42    if (!ack) iic_nack();//发送nACK
43    else  iic_ack(); //发送ACK   
44    */
45    return receive;
46 }

根据地址收发一个数据

器件地址(Device Addr):7位,最低位为0表示写数据,最低位为1表示读数据

数据地址(Register Addr):数据保存的地址

写:

 读:

 1 //根据地址写数据
 2 void IIC_Write(u8 addr,uint8_t data)
 3 {
 4      iic_start();  //起始信号
 5      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
 6      iic_wait_ack(); //等待应答
 7      iic_write_byte(addr); //发送数据地址
 8      iic_wait_ack(); 
 9      iic_write_byte(data);//发送数据                           
10      iic_wait_ack();                     
11      iic_stop();//产生一个停止条件 
12 }
13 
14 //根据地址读取数据
15 u8 IIC_Read(u8 addr)  //读寄存器或读数据
16 {    
17      u8 data;
18      iic_start();//起始信号  
19      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
20      iic_wait_ack();//等待应答
21      iic_write_byte(addr); //发送数据地址
22      iic_wait_ack(); 
23      iic_start();      
24      iic_write_byte(I2C_SLAVE_ADDRESS7|0x01);//发器件地址,低位为1,表示读
25      iic_wait_ack();
26      data=iic_read_byte(0);//读取一个字节
27      iic_stop();//产生一个停止条件        
28      return data;
29 }

工程整体

STM8L151K4T6单片机测试工程参考:

  1 #include "stm8l15x.h"
  2 #include "TCA8418.h"
  3 
  4 #define I2C_SLAVE_ADDRESS7     0x68
  5 
  6 //延时函数,大致延时,
  7 //如果需要标准延时,请使用定时器
  8 void delay_us(u8 n)
  9 {
 10   unsigned int x , y;
 11   for(x = n; x > 0; x--);
 12     for(y =30; y > 0 ; y--);
 13 }
 14 
 15 /*
 16 SCL PC1
 17 SDA PC0
 18 */
 19 
 20 #define SCL_PORT   GPIOC  
 21 #define SCL_PIN    GPIO_Pin_1 
 22 
 23 #define SDA_PORT   GPIOC
 24 #define SDA_PIN    GPIO_Pin_0
 25 
 26 //SCL输出电平
 27 void SCL_Control(u8 c){
 28   if(c==0) GPIO_WriteBit(SCL_PORT , SCL_PIN ,RESET);  //低电平
 29   else  GPIO_WriteBit(SCL_PORT , SCL_PIN ,SET);  //高电平
 30 }
 31 //SDA输出电平
 32 void SDA_Control(u8 c){
 33    if(c==0) GPIO_WriteBit(SDA_PORT , SDA_PIN ,RESET);  //低电平
 34    else  GPIO_WriteBit(SDA_PORT , SDA_PIN ,SET);  //高电平
 35 }
 36 //读取SDA的电平
 37 u8 SDA_Read(){
 38   if(GPIO_ReadInputDataBit(SDA_PORT , SDA_PIN)== 0) return 0;
 39   return 1;
 40 }
 41 //设置SDA为输入模式
 42 void SDA_IN_Mode(){
 43    GPIO_Init(SDA_PORT , SDA_PIN , GPIO_Mode_In_PU_No_IT);//输入
 44 }
 45 //设置SDA为输出模式
 46 void SDA_OUT_Mode(){
 47    GPIO_Init(SDA_PORT , SDA_PIN , GPIO_Mode_Out_PP_Low_Fast);//输出
 48 }
 49 
 50 //IIC初始化
 51 void IIC_Init()
 52 {
 53   GPIO_Init(SCL_PORT , SCL_PIN , GPIO_Mode_Out_PP_Low_Fast);//输出
 54   SDA_OUT_Mode();
 55   SDA_Control(1);
 56   SCL_Control(1);
 57 }
 58 
 59 
 60 //IIC起始信号,SDA下降沿
 61 void iic_start()
 62 {
 63     SDA_OUT_Mode(); //设置SDA为输出
 64     SDA_Control(1);            
 65     SCL_Control(1);
 66     delay_us(4);
 67      SDA_Control(0);//下降沿
 68     delay_us(4);
 69     SCL_Control(0);//准备发送数据
 70 }    
 71   
 72 //IIC停止信号,SDA上升沿
 73 void iic_stop()
 74 {
 75     SDA_OUT_Mode();//设置SDA为输出
 76     SCL_Control(0);
 77     SDA_Control(0);
 78      delay_us(4);
 79     SCL_Control(1);
 80     delay_us(4);        
 81        SDA_Control(1);//上升沿
 82 }
 83 
 84 //等待应答信号到来
 85 //返回值:1,接收应答失败
 86 //        0,接收应答成功
 87 u8 iic_wait_ack(void)
 88 {
 89     u8 outTime=0;
 90         SDA_OUT_Mode();//设置SDA为输出
 91     SDA_Control(1);delay_us(1); //此条后SDA电平已经无变化
 92     SCL_Control(1);delay_us(1);    
 93         SDA_IN_Mode(); //设置SDA为输入  
 94     while(SDA_Read())
 95     {
 96        outTime++;
 97        if(outTime>250)
 98        {
 99           iic_stop();
100           return 1;
101         }
102     }
103     SCL_Control(0);//时钟输出0        
104     return 0;  
105 }
106 //IIC发送一个字节    
107 void iic_write_byte(u8 c)
108 { 
109    u8 i;   
110    SDA_OUT_Mode();//设置SDA为输出    
111    SCL_Control(0);//拉低时钟开始数据传输
112    for(i=0;i<8;i++)
113    {              
114       SDA_Control((c&0x80)>>7);//数据变化
115       c<<=1;       
116       delay_us(2);
117       SCL_Control(1);//时钟线产生下降沿
118       delay_us(2); 
119       SCL_Control(0);
120       delay_us(2);
121     }
122 }
123 
124 //产生ACK应答
125 void iic_ack(void)
126 {
127     SCL_Control(0);
128     SDA_OUT_Mode();
129     SDA_Control(0);
130     delay_us(2);
131     SCL_Control(1);
132     delay_us(2);
133     SCL_Control(0);
134 }
135 //不产生ACK应答            
136 void iic_nack(void)
137 {
138     SCL_Control(0);
139     SDA_OUT_Mode();
140     SDA_Control(1);
141     delay_us(2);
142     SCL_Control(1);
143     delay_us(2);
144     SCL_Control(0);
145 }    
146   
147 //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
148 u8 iic_read_byte(unsigned char ack)
149 {
150     unsigned char i,receive=0;
151     SCL_Control(0);//拉低时钟开始数据传输
152     SDA_OUT_Mode();//设置SDA为输出
153     SDA_Control(1); //此条后SDA电平已经无变化
154     SDA_IN_Mode(); //设置SDA为输入  
155     for(i=0;i<8;i++ )
156     {
157        SCL_Control(1);//锁定数据,让数据不变化
158        receive<<=1;
159        if(SDA_Read())receive++;  //读取数据 
160        delay_us(1); 
161        SCL_Control(0);//释放锁定,开始下一个数据检测
162        delay_us(2);
163    }    
164 
165    if (!ack) iic_nack();//发送nACK
166    else  iic_ack(); //发送ACK   
167    
168    return receive;
169 }
170 
171 //根据地址写数据
172 void IIC_Write(u8 addr,u8 data)
173 {
174      iic_start();  //起始信号
175      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
176      iic_wait_ack(); //等待应答
177      iic_write_byte(addr); //发送数据地址
178      iic_wait_ack(); 
179      iic_write_byte(data);//发送数据                           
180      iic_wait_ack();                     
181      iic_stop();//产生一个停止条件 
182 }
183 
184 
185 //根据地址读取数据
186 void IIC_Read(u8 addr,u8 *datax) //读寄存器或读数据
187 {    
188      iic_start();//起始信号  
189      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
190      iic_wait_ack();//等待应答
191      iic_write_byte(addr); //发送数据地址
192      iic_wait_ack(); 
193      iic_start();      
194      iic_write_byte(I2C_SLAVE_ADDRESS7|0x01);//发器件地址,低位为1,表示读
195      iic_wait_ack();
196      *datax =iic_read_byte(0);//读取一个字节
197      iic_stop();//产生一个停止条件        
198 }
199 u8 R_Value=0;
200 void Test_Check(){
201     IIC_Init();//IIC初始化
202     //写读同一个地址,可以判断IIC是否成功
203     IIC_Write(0x01,0xB2);//写数据到0x01
204     IIC_Read(0x01,&R_Value);//将0x01的数据读取
205 }

https://blog.csdn.net/return_oops/article/details/80965437

https://www.bilibili.com/video/av73030246

原文地址:https://www.cnblogs.com/dongxiaodong/p/9801684.html