協議— 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM