>[success] **技術支持說明**
> 1.**客服**提供簡單的技術支持,一般自主學習為主
> 2.可到官方問答社區中提問:[**去提問**](https://bbs.csdn.net/forums/nb-iot)
> 3.工程師**會盡快**解答社區問題,但他們是一線開發,【**難以保證**】解答時效,解答辛苦,感謝理解!
<br/>
## **串口通信基礎理論**
#### **1\. USART**
全稱為Universal Synchronous/Asynchronous Receiver/Transmitter,即通用串行 同步/異步 接收/發送器。
<br/>
#### **2\. 并行通信和串行通信**
* 并行通信是指同時發送各個數據位(bit),使用并行通信發送8個位數據的示意圖如下。

###
* 串行通信是指一個接一個地發送各個數據位,通過串行通信發送8位數據的示意圖如下:

###
一般地,并行通信的速度比串行通信的速度更快,但所需要的數據引腳也更多。
<br/>
#### **3.異步通信與同步通信**
舉個簡單的例子說明異步通信與同步通信的區別,
* 發短信: 屬于異步通信,隨時可以發,而且每次只能發送一條消息;
* 打電話: 屬于同步通信,必須對方接通后才能通話,對方接通后,想聊多久都可以。
###
**異步通信的特點**
1.接收設備時刻做好接收數據的準備
2.發送設備隨時可以發送數據
3.發送設備每次只能發送一個字符,且字符的組成格式規定如下:
??A. 1位起始位,規定為低電0
??B. 5~8位數據位,即要傳送的有效信息
??C. 1位奇偶校驗位
??D. 1~2位停止位,規定為高電平1
###
異步通信的示意圖如下。

###
數據幀格式如下。

###
**同步通信的特點**
1.發送設備在發送消息前必須先和接收設備做時鐘頻率同步。
2.發送設備每次發送的是數據塊(可以理解為很多個字節),且消息格式如下:
??A. 2個同步字符作為一個數據塊(信息幀)的起始標志;
??B. n個連續傳送的數據
??C. 2個字節循環冗余校驗碼(CRC)
###
同步通信的示意圖如下。

###
數據幀格式如下。

<br/>
#### **4\. UART**
串口通信,是一種串行異步收發的通信模式。配套的開發板具備串口通信能力,開發者可以用來與上位機通信。
<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,
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,
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.按如圖所示把開發板的撥碼開關的第1~4位打到右邊,第5、6位打到左邊

###
3.打開串口助手,給開發板發送數據,可以看到發送的數據原封不動地發送回來了,如圖所示。

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

(非商務合作**勿擾**,此處**非**技術支持)
- 課程介紹
- 配套資源下載
- 配套開發套件簡介
- 簡介
- 硬件組成 & 技術參數
- 電路原理圖 & PCB圖
- 撥碼開關使用說明
- 第一部分:無線通信 開發指南
- 1.1.1 NB-IoT:技術簡介
- 1.1.2 NB:CH34x USB轉串口驅動安裝
- 1.1.3 NB:AT 指令開發與測試
- 1.1.4 NB:基礎指令集簡介
- 1.1.5 NB:云端服務器
- 1.1.5.1 PuTTY 簡介與安裝
- 1.1.5.2 登錄云端服務器
- 1.1.6 NB:移遠官方工具簡介
- 1.1.7 NB:使用UDP協議與云端服務器通信
- 1.1.8 NB:使用TCP協議與云端服務器通信
- 1.1.9 NB:使用MQTT協議與云端服務器通信
- 進階課程
- 第二部分:STM32 開發指南
- 2.1 搭建開發環境
- 2.1.1 Keil MDK 簡介與安裝
- 2.1.2 STM32 Pack 簡介與安裝
- 2.1.3 CH34x 驅動簡介與安裝
- 2.1.4 其他開發工具
- 2.2 STM32 開發基礎
- 2.2.1 新建工程
- 2.2.2 實現第1個程序
- 2.2.3 Hex 文件燒錄詳解
- 2.3 移植官方標準工程模板
- 2.4 GPIO實驗——LED燈
- 2.5 系統延時應用
- 2.6 GPIO實驗——按鍵
- 2.7 GPIO中斷實驗——按鍵觸發
- 2.8 使用定時器TIM3
- 2.9 串口通信實驗
- 2.10 ADC 實驗
- 2.11 OLED顯示器實驗
- 2.12 SDK 設計思想
- 2.13 SDK 架構解析
- 2.14 多任務應用
- 2.15 輸入型任務:按鍵輸入
- 2.16 輸入型任務:串口接收
- 課外篇:項目實戰
- 基于STM32+NB-IoT的溫濕度采集
- 系統簡介
- 系統搭建
- 系統詳解
- 1.代碼編譯與架構說明
- 2.DHT11溫濕度傳感器
- 3. 數據通信任務說明
- 版權聲明與免責聲明