>[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個比特位的示意圖如圖所示。

###
串行通信是指一個接一個地發送各個比特位,通過串行通信發送8個比特位的示意圖如圖所示。

一般地,并行通信的速度比串行通信的速度更快,但所需要的數據引腳也更多。
<br/>
**3.異步通信與同步通信簡介**
舉個簡單的例子來說明異步通信與同步通信的主要區別:
(1)異步通信類似于手機發短信,其特點是隨時可以發送,而且每次只能發送一條消息。
(2)同步通信類似于手機打電話,其特點是必須在對方接通后才能通話,而且在對方接通后想聊天(通信)多久都可以。
###
**1)異步通信的特點**
(1)接收設備要時刻做好接收數據的準備。
(2)發送設備隨時可以發送數據。
(3)發送設備每次只能發送一個數據幀,即一段較長的數據需要被劃分為多個數據幀,且數據幀的組成格式規定如下:
* ① 1個起始位。
* ② 5、7或8個數據位,即要傳送的信息。
* ③ 1個奇偶校驗位。
* ④ 1~2個停止位,規定為1。
異步通信的示意圖如圖所示。

###
數據幀格式如圖所示。

**2)同步通信的特點**
(1)發送設備在發送消息前必須要先和接收設備做時鐘頻率同步。
(2)發送設備每次發送的是數據塊,一個數據塊可以理解為多個字節,且消息格式如下:
* 2個同步字符作為一個數據塊的起始標志。
* 多個連續傳輸的的數據字節。
* 2個字節的CRC碼。CRC的全稱是Cyclical Redundancy Check(循環冗余校驗),是數據通信領域中常用的一種差錯校驗碼,數據的接收者可以根據此碼來判斷所接收到數據是否發生錯亂。
同步通信的示意圖如圖所示。

###
數據塊(數據幀)的格式如圖所示。

###
<br/>
**3.UART簡介**
UART的全稱是Universal Asynchronous Receiver/Transmitter(通用串行異步收發器),也稱作串口,可以理解為在USART的基礎上去掉了同步通信的支持,通常用于主機與嵌入式設備之間的通信,例如配套的ZigBee開發板便可以使用UART與PC主機通信。
<br/>
**4.SPI簡介**
SPI的全稱是Serial Peripheral Interface(串行外設接口),USART的一種,是一種具有速度快、全雙工和同步通信特點的通信總線。
<br/>
## **串口通信 API 設計**
串口通信功能應該提供初始化、發送和接收信息API,如圖所示。

###
其中的接收串口信息與按鍵類似,都是被動輸入的,同樣地可以使用注冊回調的思想,即上層應用在HAL中注冊已給回調函數,一旦HAL檢測到需要接收串口信息,就會回調給上層應用。
<br/>
**編寫代碼**
筆者在本節課配套的源代碼中新建了 hal\_uart.h 和 hal\_uart.c文件,如圖所示。

###
打開本節課配套的代碼,筆者把hal\_uart.c以及必要的標準庫文件添加進工程了,如圖所示。

<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位打到左邊,如圖所示。

###
接著,使用配套的Micro USB線連接開發板到電腦上。
###
3.打開串口助手,在端口選項框中選擇開發板對應的端口,如圖所示。

###
4.按如圖所示配置好數據位、校驗位、停止位和流控等相關配置。

###
5.點擊打開串口按鈕,如果上述步驟操作可以看到“COMx OPENED”的提示,如圖所示。

###
6.在輸入框中輸入任意的字符,然后點擊“發送”按鈕把這些字符通過串口的發送給開發板,如圖所示。

###
7.開發板把通過串口接收到的數據原封不動地通過串口發送回去,于是讀者可以在串口助手中查看到這些數據,如圖所示。

<br/>
<br/>
## **商務合作**
如有以下需求,可掃碼添加管理員好友,注明“**商務合作**”
* 項目定制開發,技術范圍:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等;
* 入駐平臺,成為講師;
* 接項目賺外快;
* 善學坊官網:[www.sxf-iot.com](https://www.sxf-iot.com/)

(非商務合作**勿擾**,此處**非**技術支持)
- 課程簡介
- 配套資源下載
- 配套開發套件簡介
- 簡介
- 硬件組成 & 技術參數
- STM32 Pro 主板 原理圖 & PCB圖
- STM32 Std 主板 原理圖 & PCB圖
- 板載設備使用說明
- STM32開發指南
- 1. 搭建開發環境
- 1.1 Keil MDK簡介與安裝
- 1.2 STM32 Pack 簡介與安裝
- 1.3 CH34x 驅動簡介與安裝
- 1.4 ISP 串口下載工具
- 1.5 串口調試工具
- 2. STM32 開發基礎
- 2.1 新建工程
- 2.2 實現第1個程序
- 2.3 Hex 文件燒錄詳解
- 3. 移植官方標準工程模板
- 4. GPIO實驗:LED
- 5. 系統延時應用
- 6. GPIO實驗:按鍵
- 7. GPIO中斷實驗——按鍵觸發實驗
- 8. 使用定時器TIM3
- 9. 串口通信實驗
- 10. ADC 實驗
- 11. OLED顯示器實驗
- 12. SDK 設計思想
- 13. SDK 架構解析
- 14. 多任務應用實驗
- 15. 輸入型任務:按鍵輸入實驗
- 16. 輸入型任務:串口接收實驗
- 17. 高精度溫濕度傳感器實驗
- 進階課程