🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
>[success] **技术支持说明:** >**1**.一般以自主学习为主 > **2**.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/nb-iot) > **3**.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> ## **串口通信基础理论** **1.USART简介** USART全称为Universal Synchronous/Asynchronous Receiver/Transmitter,即通用串行 同步/异步 接收/发送器。USART中包含串行通信、同步通信和异步通信几个概念,接下来逐一介绍一下。 <br/> **2.并行通信和串行通信简介** 并行通信是指同时发送各个数据位(bit),使用并行通信发送8个比特位的示意图如图所示。 ![](https://img.kancloud.cn/a6/5e/a65e5f2b5759a3bacf931e9a499b90ac_1000x632.png =250x) ### 串行通信是指一个接一个地发送各个比特位,通过串行通信发送8个比特位的示意图如图所示。 ![](https://img.kancloud.cn/92/f9/92f9b7ebbc2112c5632e2cc8582c602e_2560x771.png =500x) 一般地,并行通信的速度比串行通信的速度更快,但所需要的数据引脚也更多。 <br/> **3.异步通信与同步通信简介** 举个简单的例子来说明异步通信与同步通信的主要区别: (1)异步通信类似于手机发短信,其特点是随时可以发送,而且每次只能发送一条消息。 (2)同步通信类似于手机打电话,其特点是必须在对方接通后才能通话,而且在对方接通后想聊天(通信)多久都可以。 ### **1)异步通信的特点** (1)接收设备要时刻做好接收数据的准备。 (2)发送设备随时可以发送数据。 (3)发送设备每次只能发送一个数据帧,即一段较长的数据需要被划分为多个数据帧,且数据帧的组成格式规定如下: * ① 1个起始位。 * ② 5、7或8个数据位,即要传送的信息。 * ③ 1个奇偶校验位。 * ④ 1~2个停止位,规定为1。 异步通信的示意图如图所示。 ![](https://img.kancloud.cn/33/2d/332d57689b21a82bf4d0254b16129432_2560x803.png =500x) ### 数据帧格式如图所示。 ![](https://img.kancloud.cn/83/3e/833e1453c2ae9d96a529cf70ec4d6e64_1000x304.png =500x) **2)同步通信的特点** (1)发送设备在发送消息前必须要先和接收设备做时钟频率同步。 (2)发送设备每次发送的是数据块,一个数据块可以理解为多个字节,且消息格式如下: * 2个同步字符作为一个数据块的起始标志。 * 多个连续传输的的数据字节。 * 2个字节的CRC码。CRC的全称是Cyclical Redundancy Check(循环冗余校验),是数据通信领域中常用的一种差错校验码,数据的接收者可以根据此码来判断所接收到数据是否发生错乱。 同步通信的示意图如图所示。 ![](https://img.kancloud.cn/1c/ef/1cef20c35399325938b58425fd912a42_998x450.png) ### 数据块(数据帧)的格式如图所示。 ![](https://img.kancloud.cn/11/51/115185ae18b050bff105ca8363f2d1fd_2560x329.png) ### <br/> **3.UART简介** UART的全称是Universal Asynchronous Receiver/Transmitter(通用串行异步收发器),也称作串口,可以理解为在USART的基础上去掉了同步通信的支持,通常用于主机与嵌入式设备之间的通信,例如配套的ZigBee开发板便可以使用UART与PC主机通信。 <br/> **4.SPI简介** SPI的全称是Serial Peripheral Interface(串行外设接口),USART的一种,是一种具有速度快、全双工和同步通信特点的通信总线。 <br/> ## **串口通信 API 设计** 串口通信功能应该提供初始化、发送和接收信息API,如图所示。 ![](https://img.kancloud.cn/72/d6/72d6beb6a0c6fdb2589c423eff633ad8_466x474.png =300x) ### 其中的接收串口信息与按键类似,都是被动输入的,同样地可以使用注册回调的思想,即上层应用在HAL中注册已给回调函数,一旦HAL检测到需要接收串口信息,就会回调给上层应用。 <br/> **编写代码** 笔者在本节课配套的源代码中新建了 hal\_uart.h 和 hal\_uart.c文件,如图所示。 ![](https://img.kancloud.cn/b7/54/b7549abffdcc722a3625e6be8b3d0e72_254x542.png =250x) ### 打开本节课配套的代码,笔者把hal\_uart.c以及必要的标准库文件添加进工程了,如图所示。 ![](https://img.kancloud.cn/b7/97/b79757222701136351fb25ba60ceae45_375x618.png =300x) <br/> hal_uart.h文件的代码如下: ### ``` #ifndef __HAL_UART_H__ #define __HAL_UART_H__ /* * 串口通信初始化 * * @param baudrate - 串口通信波特率 */ void halUartInit(unsigned long baudrate); /* * 注册接收串口信息的回调函数 * * @param onIRQ - 回调函数,接收到到串口信息时自动调用此函数 */ void halUartSetIRQCallback(void (*onIRQ)(unsigned char byte)); /* * 通过串口发送信息 * * @param buf - 待发送的信息的存储地址 * @param len - 待发送的信息的数据长度 */ void halUartWrite(unsigned char *buf, unsigned int len); #endif /* #ifndef __HAL_UART_H__ */ ``` <br/> hal_uart.c文件的代码如下: ``` #include "hal_uart.h" #include "stm32f0xx_usart.h" //保存串口通信回调函数 static void (*halUartOnIRQ)(unsigned char byte) = 0; static void halUartGpioInit(void); static void halUartParamInit(unsigned long baudrate); static void halUartIRQInit(void); /* * 串口通信初始化 * * @param baudrate - 串口通信波特率 */ void halUartInit(unsigned long baudrate) { halUartGpioInit(); halUartParamInit(baudrate); halUartIRQInit(); } /* * 注册接收串口信息的回调函数 * * @param onIRQ - 回调函数,接收到到串口信息时自动调用此函数 */ void halUartSetIRQCallback(void (*onIRQ)(unsigned char byte)) { halUartOnIRQ = onIRQ; } /* * 通过串口发送信息 * * @param buf - 待发送的信息的存储地址 * @param len - 待发送的信息的数据长度 */ void halUartWrite(unsigned char *buf, unsigned int len) { for (unsigned int i = 0; i < len; i++) { USART_SendData(USART1, buf[i]); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } } /* * 初始化串口通信相关的GPIO */ void halUartGpioInit() { GPIO_InitTypeDef uart1Tx; GPIO_InitTypeDef uart1Rx; /* TX */ uart1Tx.GPIO_Pin = GPIO_Pin_9, //使用PA9作为串口发送接口 uart1Tx.GPIO_Speed = GPIO_Speed_10MHz, uart1Tx.GPIO_Mode = GPIO_Mode_AF, uart1Tx.GPIO_PuPd = GPIO_PuPd_NOPULL, /* RX */ uart1Rx.GPIO_Pin = GPIO_Pin_10,//使用PA10作为串口接收接口 uart1Rx.GPIO_Speed = GPIO_Speed_10MHz, uart1Rx.GPIO_Mode = GPIO_Mode_AF, uart1Rx.GPIO_PuPd = GPIO_PuPd_NOPULL, RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1); GPIO_Init(GPIOA, &uart1Tx); GPIO_Init(GPIOA, &uart1Rx); } /* * 初始化串口通信配置 */ void halUartParamInit(unsigned long baudrate) { USART_InitTypeDef uartConfig; uartConfig.USART_BaudRate = baudrate; uartConfig.USART_WordLength = USART_WordLength_8b; uartConfig.USART_Parity = USART_Parity_No; uartConfig.USART_StopBits = USART_StopBits_1; uartConfig.USART_HardwareFlowControl = USART_HardwareFlowControl_None; uartConfig.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); USART_Init(USART1, &uartConfig); USART_Cmd(USART1, ENABLE); } /* * 初始化串口通信的中断请求 */ void halUartIRQInit() { NVIC_InitTypeDef uartNVIC; uartNVIC.NVIC_IRQChannel = USART1_IRQn; uartNVIC.NVIC_IRQChannelPriority = 0; uartNVIC.NVIC_IRQChannelCmd = ENABLE; USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_ITConfig(USART1, USART_IT_TC, ENABLE); NVIC_Init(&uartNVIC); } /* * 串口通信中断处理函数。当串口接收到数据时,便会自动产生中断并执行此函数 */ void USART1_IRQHandler(void) { unsigned char byte = 0; if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { byte = USART_ReceiveData(USART1); // Auto to clear RXNE flag when read! if (halUartOnIRQ != 0) halUartOnIRQ(byte);//执行串口通信回调函数 } else{ USART_ClearFlag(USART1,USART_FLAG_TC);//清理中断标志 } } ``` 上述的初始化代码较为复杂,读者暂时可以直接套用上述代码,留作后期再深入学习相关原理。 <br/> ## **使用串口通信 HAL API** 编写好串口通信 HAL API后,串口通信的使用非常简单。在配套工程的main.c文件中添加如下代码: ### ``` /* * 通过串口接收到信息时的回调函数 * @param byte - 接收到的数据 */ static void onUartIRQ(unsigned char byte) { halUartWrite(&byte, 1);//把接收到的数据原封不动地发送回去 } int main(void) { halSystemInit();//系统初始化 halUartInit(115200);//串口通信初始化,并设置波特率为115200 halUartSetIRQCallback(onUartIRQ);//注册串口通信回调函数,当通过串口接收到信息时自动调用此函数 /* Infinite loop */ while (1) {} } ``` <br/> #### **代码测试** 1.编译链接工程代码,把生成的Hex文件烧录到开发板中。 2.把STM32开发板的第1、2、3和4打到右边,第5和6位打到左边,如图所示。 ![](https://img.kancloud.cn/58/9a/589a66f16f8ca111d36b54fac452805c_1076x1045.png =250x) ### 接着,使用配套的Micro USB线连接开发板到电脑上。 ### 3.打开串口助手,在端口选项框中选择开发板对应的端口,如图所示。 ![](https://img.kancloud.cn/dd/9d/dd9ddc3699e8471d25a8527372c50189_1166x1032.png =400x) ### 4.按如图所示配置好数据位、校验位、停止位和流控等相关配置。 ![](https://img.kancloud.cn/cd/68/cd68b15155eb6d075d8741869447b521_1164x1030.png =400x) ### 5.点击打开串口按钮,如果上述步骤操作可以看到“COMx OPENED”的提示,如图所示。 ![](https://img.kancloud.cn/82/b4/82b4d26bffe601948444cb9937986a25_1164x1028.png =400x) ### 6.在输入框中输入任意的字符,然后点击“发送”按钮把这些字符通过串口的发送给开发板,如图所示。 ![](https://img.kancloud.cn/8b/77/8b77c772db1d5e6c9049bbcaa25ee3c0_1170x1038.png =400x) ### 7.开发板把通过串口接收到的数据原封不动地通过串口发送回去,于是读者可以在串口助手中查看到这些数据,如图所示。 ![](https://img.kancloud.cn/a8/95/a895b6040f8cab39a1512f027eff23e2_1166x1038.png =400x) <br/> <br/> ## **商务合作** 如有以下需求,可扫码添加管理员好友,注明“**商务合作**” * 项目定制开发,技术范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等; * 入驻平台,成为讲师; * 接项目赚外快; * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](https://img.kancloud.cn/b7/5a/b75ac8c3da945f20568771f5935c388f_430x430.png =150x) (非商务合作**勿扰**,此处**非**技术支持)