這篇文章主要介紹基本的驅動也是用的最多的協議類驅動中的SPI,I2C和UART。首先從最簡單的UART也就是串口講起:
1.UART
UART由兩根線也就是TX,RX以及波特率產生器組成,操作比較簡單,配置好后,就可以發送接收數據了,注意有的MCU需要接收數據時清除某些標記。如:
2.SPI
SPI一般有三根線組成即CLK,MOSI,MISO,數據輸入和輸出是單獨的一根線。一般的操作都是先發控制指令,再發地址,接着才是數據。例:
3.I2C
I2C一般由兩根線組成,即SDA,SCL,一根時鍾線,一根數據線。關於I2C相關的開始信號,響應信號,停止信號時序見后面附錄圖片,這些信號一般都是通用封裝好的,封裝函數見附件(IO口模擬I2C)。
下面重點介紹I2C的使用操作,一般通信步驟是開始信號,從機地址+讀或寫標記,等待回應,發送數據,等待回應(接收數據,發送回應(最后一次接收則不發回應)),停止信號,代碼示例:
最后,再總結與強調一下
SPI、I2C、UART三種串行總線協議的區別:
1,字面意思上:
SPI(Serial Peripheral Interface:串行外設接口);
I2C(INTER IC BUS)
UART(Universal Asynchronous Receiver Transmitter:通用異步收發器)
2.各總線的信號線:
SPI總線由三條信號線組成:串行時鍾(SCLK)、串行數據輸出(SDO)、串行數據輸入(SDI)。SPI總線可以實現 多個SPI設備互相連接。提供SPI串行時鍾的SPI設備為SPI主機或主設備(Master),其他設備為SPI從機或從設備(Slave)。主從設備間可以實現全雙工通信,當有多個從設備時,還可以增加一條從設備選擇線。
I2C總線是雙向、兩線(SCL、SDA)、串行、多主控(multi-master)接口標准,具有總線仲裁機制,非常適合在器件之間進行近距離、非經常性的數據通信。在它的協議體系中,傳輸數據時都會帶上目的設備的設備地址,因此可以實現設備組網。一根數據線上傳輸的一條報文包括:開始信號+設備地址+命令+數據+(ACK)+停止信號
UART總線是異步串口,因此一般比前兩種同步串口的結構要復雜很多,一般由波特率產生器(產生的波特率等於傳輸波特率的16倍)、UART接收器、UART發送器組成,硬件上由兩根線,一根用於發送,一根用於接收。
從第二點明顯可以看出,SPI和UART可以實現全雙工,但I2C不行;
總結:I2C線更少,但是技術上也更加麻煩些,因為I2C需要有雙向IO的支持,而且使用上拉電阻,抗干擾能力較弱,一般用於同一板卡上芯片之間的通信,較少用於遠距離通信。SPI實現要簡單一些,UART需要固定的波特率,就是說兩位數據的間隔要相等,而SPI則無所謂,因為它是有時鍾的協議。
個人學習筆記附錄:
代碼附件:

1 /***************************************************************************** 2 * Copyright (C) 2014-2015 China Aerospace Telecommunications Ltd. All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : PT810 dev_I2C.c 5 * Description : I2C Drive operation center 6 * Created : 2016.10.13. 7 * Author : Yu Weifeng 8 * Function List : 9 * Last Modified : 10 * History : 11 ******************************************************************************/ 12 #include "stm32f4xx_hal.h" 13 #include "CBasicTools.h" 14 #include "Config.h" 15 #include "dev_LightDistanceSensor.h" 16 #include "core_CM4.h" 17 #include "ucos_ii.h" 18 19 static void I2C_DevConfig(void); 20 static void I2C_Start(); 21 static void I2C_SendByte(u8 i_ucData); 22 static u8 I2C_ReadByte(u8 i_ucAck); 23 static u8 I2C_WaitAck(); 24 static void I2C_Stop(); 25 26 static T_I2C_DevManage g_tI2C_Dev ={ 27 .name="I2C_Dev", 28 .DevConfig =I2C_DevConfig, 29 .DevI2C_Start =I2C_Start, 30 .DevI2C_SendByte =I2C_SendByte, 31 .DevI2C_ReadByte =I2C_ReadByte, 32 .DevI2C_WaitAck =I2C_WaitAck, 33 .DevI2C_Stop =I2C_Stop, 34 }; 35 //IO方向設置 36 #define SDA_IN() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;} //PB9輸入模式 37 #define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9輸出模式 38 39 /***************************************************************************** 40 -Fuction : I2C_DevInit 41 -Description : I2C_DevInit 42 -Input : 43 -Output : 44 -Return : True/False 45 * Modify Date Version Author Modification 46 * ----------------------------------------------- 47 * 2016/10/13 V1.0.0 Yu Weifeng Created 48 ******************************************************************************/ 49 void I2C_DevInit() 50 { 51 RegisterI2C_Dev(&g_tI2C_Dev); 52 } 53 54 /***************************************************************************** 55 -Fuction : DelayUs 56 -Description : DelayUs 57 -Input : 58 -Output : 59 -Return : True/False 60 * Modify Date Version Author Modification 61 * ----------------------------------------------- 62 * 2016/10/13 V1.0.0 Yu Weifeng Created 63 ******************************************************************************/ 64 static void DelayUs(u8 i_ucTime) 65 { 66 u16 wTime = 75*i_ucTime; 67 while(wTime--); 68 } 69 //延時nus 70 //nus:要延時的us數. 71 //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5) 72 void delay_us(u32 nus) 73 { 74 u32 ticks; 75 u32 told,tnow,tcnt=0; 76 u32 reload=SysTick->LOAD; //LOAD的值 77 ticks=nus*84; //系統時鍾晶振為84M晶振 78 OSSchedLock(); //阻止OS調度,防止打斷us延時 79 told=SysTick->VAL; //剛進入時的計數器值 80 while(1) 81 { 82 tnow=SysTick->VAL; 83 if(tnow!=told) 84 { 85 if(tnow<told)tcnt+=told-tnow; //這里注意一下SYSTICK是一個遞減的計數器就可以了. 86 else tcnt+=reload-tnow+told; 87 told=tnow; 88 if(tcnt>=ticks)break; //時間超過/等於要延遲的時間,則退出. 89 } 90 }; 91 OSSchedUnlock(); //恢復OS調度 92 } 93 94 /***************************************************************************** 95 -Fuction : I2C_SDA_SET 96 -Description : I2C_SDA_SET 97 -Input : 98 -Output : 99 -Return : True/False 100 * Modify Date Version Author Modification 101 * ----------------------------------------------- 102 * 2016/10/13 V1.0.0 Yu Weifeng Created 103 ******************************************************************************/ 104 static u8 I2C_SDA_SET(u8 i_ucSetValue) 105 { 106 u8 ret=FALSE; 107 if(GPIO_PIN_SET==i_ucSetValue) 108 { 109 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET); //對應引腳PB 110 ret=TRUE; 111 } 112 else if(GPIO_PIN_RESET==i_ucSetValue) 113 { 114 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET); //對應引腳PB 115 ret=TRUE; 116 } 117 else 118 { 119 ret=FALSE; 120 DebugPrintf(ERR"I2C_SDA_SET format err\r\n"); 121 } 122 return ret; 123 } 124 /***************************************************************************** 125 -Fuction : I2C_SDA_READ 126 -Description : I2C_SDA_READ 127 -Input : 128 -Output : 129 -Return : 130 * Modify Date Version Author Modification 131 * ----------------------------------------------- 132 * 2016/10/13 V1.0.0 Yu Weifeng Created 133 ******************************************************************************/ 134 static u8 I2C_SDA_READ() 135 { 136 u8 ret=0; 137 ret=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9); 138 return ret; 139 } 140 /***************************************************************************** 141 -Fuction : I2C_SCL_SET 142 -Description : I2C_SCL_SET 143 -Input : 144 -Output : 145 -Return : True/False 146 * Modify Date Version Author Modification 147 * ----------------------------------------------- 148 * 2016/10/13 V1.0.0 Yu Weifeng Created 149 ******************************************************************************/ 150 static u8 I2C_SCL_SET(u8 i_ucSetValue) 151 { 152 u8 ret=FALSE; 153 if(GPIO_PIN_SET==i_ucSetValue) 154 { 155 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET); //對應引腳PB 156 ret=TRUE; 157 } 158 else if(GPIO_PIN_RESET==i_ucSetValue) 159 { 160 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET); //對應引腳PB 161 ret=TRUE; 162 } 163 else 164 { 165 ret=FALSE; 166 DebugPrintf(ERR"I2C_SCL_SET format err\r\n"); 167 } 168 return ret; 169 } 170 171 /***************************************************************************** 172 -Fuction : I2C_DevConfig 173 -Description : I2C_DevConfig 174 -Input : 175 -Output : 176 -Return : 177 * Modify Date Version Author Modification 178 * ----------------------------------------------- 179 * 2016/10/13 V1.0.0 Yu Weifeng Created 180 ******************************************************************************/ 181 static void I2C_DevConfig(void) 182 { 183 GPIO_InitTypeDef GPIO_Initure; 184 185 __HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOH時鍾 186 187 //PB8,9初始化設置 188 GPIO_Initure.Pin=GPIO_PIN_8|GPIO_PIN_9; 189 GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出 190 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 191 GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速 192 HAL_GPIO_Init(GPIOB,&GPIO_Initure); 193 194 I2C_SDA_SET(1); 195 I2C_SCL_SET(1); 196 } 197 /***************************************************************************** 198 -Fuction : I2C_Start 199 -Description : I2C_Start//產生IIC起始信號 200 -Input : 201 -Output : 202 -Return : 203 * Modify Date Version Author Modification 204 * ----------------------------------------------- 205 * 2016/09/23 V1.0.0 Yu Weifeng Created 206 ******************************************************************************/ 207 static void I2C_Start() 208 { 209 SDA_OUT(); //sda線輸出 210 I2C_SDA_SET(1); 211 I2C_SCL_SET(1); 212 DelayUs(4); 213 I2C_SDA_SET(0);//START:when CLK is high,DATA change form high to low 214 DelayUs(4); 215 I2C_SCL_SET(0);//鉗住I2C總線,准備發送或接收數據 216 } 217 /***************************************************************************** 218 -Fuction : I2C_Stop 219 -Description : I2C_Stop//產生IIC起始信號 220 -Input : 221 -Output : 222 -Return : 223 * Modify Date Version Author Modification 224 * ----------------------------------------------- 225 * 2016/09/23 V1.0.0 Yu Weifeng Created 226 ******************************************************************************/ 227 static void I2C_Stop() 228 { 229 SDA_OUT();//sda線輸出 230 I2C_SCL_SET(0); 231 I2C_SDA_SET(0);//STOP:when CLK is high DATA change form low to high 232 DelayUs(4); 233 I2C_SCL_SET(1); 234 I2C_SDA_SET(1);//發送I2C總線結束信號 235 DelayUs(4); 236 } 237 /***************************************************************************** 238 -Fuction : I2C_AckGenerate 239 -Description : I2C_AckGenerate////產生ACK應答 240 -Input : 241 -Output : 242 -Return : 243 * Modify Date Version Author Modification 244 * ----------------------------------------------- 245 * 2016/10/13 V1.0.0 Yu Weifeng Created 246 ******************************************************************************/ 247 static void I2C_AckGenerate() 248 { 249 I2C_SCL_SET(0); 250 SDA_OUT(); 251 I2C_SDA_SET(0); 252 DelayUs(2); 253 I2C_SCL_SET(1); 254 DelayUs(2); 255 I2C_SCL_SET(0); 256 } 257 /***************************************************************************** 258 -Fuction : I2C_AckGenerate 259 -Description : I2C_AckGenerate//////不產生ACK應答 260 -Input : 261 -Output : 262 -Return : 263 * Modify Date Version Author Modification 264 * ----------------------------------------------- 265 * 2016/10/13 V1.0.0 Yu Weifeng Created 266 ******************************************************************************/ 267 static void I2C_AckNoGenerate() 268 { 269 I2C_SCL_SET(0); 270 SDA_OUT(); 271 I2C_SDA_SET(1); 272 DelayUs(2); 273 I2C_SCL_SET(1); 274 DelayUs(2); 275 I2C_SCL_SET(0); 276 } 277 /***************************************************************************** 278 -Fuction : I2C_SendByte 279 -Description : I2C_SendByte//// 280 //IIC發送一個字節 281 //返回從機有無應答 282 //1,有應答 283 //0,無應答 284 -Input : 285 -Output : 286 -Return : 287 * Modify Date Version Author Modification 288 * ----------------------------------------------- 289 * 2016/10/13 V1.0.0 Yu Weifeng Created 290 ******************************************************************************/ 291 static void I2C_SendByte(u8 i_ucData) 292 { 293 u8 c; 294 SDA_OUT(); 295 I2C_SCL_SET(0);//拉低時鍾開始數據傳輸 296 for(c=0;c<8;c++) 297 { 298 I2C_SDA_SET((i_ucData&0x80)>>7); 299 i_ucData<<=1; 300 DelayUs(2); //對TEA5767這三個延時都是必須的 301 I2C_SCL_SET(1); 302 DelayUs(2); 303 I2C_SCL_SET(0); 304 DelayUs(2); 305 } 306 } 307 /***************************************************************************** 308 -Fuction : I2C_OnDev 309 -Description : I2C_OnDev 310 //等待應答信號到來 311 //返回值:0,接收應答失敗 312 // 1,接收應答成功 313 314 -Input : 315 -Output : 316 -Return : 317 * Modify Date Version Author Modification 318 * ----------------------------------------------- 319 * 2016/09/23 V1.0.0 Yu Weifeng Created 320 ******************************************************************************/ 321 static u8 I2C_WaitAck() 322 { 323 u8 ucErrTime=0; 324 u8 ret=FALSE; 325 SDA_IN(); //SDA設置為輸入 326 I2C_SDA_SET(1); 327 DelayUs(1); 328 I2C_SCL_SET(1); 329 DelayUs(1); 330 while(I2C_SDA_READ()) 331 { 332 ucErrTime++; 333 if(ucErrTime>250) 334 { 335 I2C_Stop(); 336 ret=FALSE; 337 return ret; 338 } 339 } 340 I2C_SCL_SET(0);//時鍾輸出0 341 ret=TRUE; 342 return ret; 343 } 344 345 /***************************************************************************** 346 -Fuction : I2C_ReadByte 347 -Description : I2C_ReadByte//// 348 //讀1個字節,ack=1時,發送ACK,ack=0,發送nACK 349 -Input : 350 -Output : 351 -Return : 352 * Modify Date Version Author Modification 353 * ----------------------------------------------- 354 * 2016/10/13 V1.0.0 Yu Weifeng Created 355 ******************************************************************************/ 356 static u8 I2C_ReadByte(u8 i_ucAck) 357 { 358 u8 c; 359 u8 ucReceive=0; 360 SDA_IN();//SDA設置為輸入 361 for(c=0;c<8;c++ ) 362 { 363 I2C_SCL_SET(0); 364 DelayUs(2); 365 I2C_SCL_SET(1); 366 ucReceive<<=1; 367 if(I2C_SDA_READ()) 368 ucReceive++; 369 DelayUs(1); 370 } 371 if (!i_ucAck) 372 I2C_AckNoGenerate();//發送nACK 373 else 374 I2C_AckGenerate(); //發送ACK 375 return ucReceive; 376 }