基礎認識
模擬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 }
