这篇文章主要介绍基本的驱动也是用的最多的协议类驱动中的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 }