3 串列埠通訊(基於stm32f103)
1 串列埠通訊
1.1 串列埠通訊協議簡介
1、分為兩層
(1)物理層:規定通訊系統中具有機械、電子功能部分的特性,確保原始資料在物理媒體的傳輸。其實就是硬體部分。
(2)協議層:協議層主要規定通訊邏輯,統一收發雙方的資料打包、解包標準。其實就是軟體部分。
1.2 串列埠通訊兩端裝置型別
1、RS232標準
RS232標準串列埠通訊結構圖
(1)RS232標準串列埠主要用於工業裝置直接通訊
(2)電平轉換晶片一般有MAX3232,SP3232
(3)DB9 標準的公頭及母頭接法,如下圖
(4)DB9串列埠線
(5)RS-232 與 TTL 電平區別
2、USB轉串列埠
(1)USB轉串列埠通訊結構圖
(2)USB轉串列埠主要用於裝置跟電腦通訊
(3)電平轉換晶片一般有CH340、PL2303、CP2102、FT232
(4)使用的時候電腦端需要安裝電平轉換晶片的驅動
3、原生的串列埠到串列埠
(1)原生串列埠到串列埠通訊結構圖
(2)原生的串列埠通訊主要是控制器跟串列埠的裝置或者感測器通訊,不需要經過電平轉換晶片來轉換電平,直接就用TTL電平通訊
(3)GPS模組、GSM模組、串列埠轉WIFI模組、HC04藍芽模組
1.3 串列埠資料包的基本組成
1、起始位
由一個邏輯 0的資料位表示
2、結束位
由 0.5、 1、 1.5 或 2 個邏輯 1的資料位表示
3、有效資料
在起始位後緊接著的就是有效資料,有效資料的長度常被約定為
4、校驗位:
可選,為的是資料的抗干擾性。校驗方法分為:
(1)奇校驗(odd)
有效資料和校驗位中“ 1”的個數為奇數,比如一個 8 位長的有效資料為: 01101001,此時總共有 4 個“ 1”,為達到奇校驗效果,校驗位為“ 1”,最後傳輸的資料將是 8 位的有效資料加上 1 位的校驗位總共 9 位
(2)偶校驗(even)
有效資料和校驗位中“ 1”的個數為偶數,比如一個 8 位長的有效資料為: 01101001,此時總共有 4 個“ 1”,為達到偶校驗效果,校驗位為“
(3)0校驗(space)
0 校驗是不管有效資料中的內容是什麼,校驗位總為“ 0”
(4)1校驗(mark)
1 校驗是校驗位總為“ 1”。
(5)無校驗(noparity)
無校驗就是資料包中不包含校驗位。
1.4 串列埠功能框圖
1-引腳2-資料暫存器3-控制器4-波特率
1、功能引腳
TX:傳送資料輸出引腳。
RX:接收資料輸入引腳。
SW_RX:資料接收引腳,只用於單線和智慧卡模式,屬於內部引腳,沒有具體外部引腳。
nRTS:請求以傳送(Request To Send),n 表示低電平有效。如果使能 RTS 流控制,當USART 接收器準備好接收新資料時就會將 nRTS 變成低電平;當接收暫存器已滿時,nRTS 將被設定為高電平。該引腳只適用於硬體流控制。
nCTS:清除以傳送(Clear To Send),n 表示低電平有效。如果使能 CTS 流控制,傳送器在傳送下一幀資料之前會檢測 nCTS 引腳,如果為低電平,表示可以傳送資料,如果為高電平則在傳送完當前資料幀之後停止傳送。該引腳只適用於硬體流控制。
SCLK:傳送器時鐘輸出引腳。這個引腳僅適用於同步模式。
USART 引腳在 STM32F103ZET6 晶片具體分佈見表
2、資料暫存器
USART 資料暫存器(USART_DR)只有低 9 位有效,並且第 9 位資料是否有效要取決於USART 控制暫存器 1(USART_CR1)的 M 位設定,當M 位為 0 時表示 8 位資料字長,當M位為1表示9位資料字長,我們一般使用 8 位資料字長。USART_DR 包含了已傳送的資料或者接收到的資料。USART_DR 實際是包含了兩個暫存器,一個專門用於傳送的可寫 TDR,一個專門用於接收的可讀 RDR。當進行傳送操作時,往 USART_DR 寫入資料會自動儲存在 TDR內;當進行讀取操作時,向 USART_DR讀取資料會自動提取 RDR資料。TDR 和 RDR 都是介於系統匯流排和移位暫存器之間。序列通訊是一個位一個位傳輸的,傳送時把 TDR 內容轉移到傳送移位暫存器,然後把移位暫存器資料每一位傳送出去,接收時把接收到的每一位順序儲存在接收移位暫存器內然後才轉移到 RDR。
3、控制器
USART 有專門控制傳送的傳送器、控制接收的接收器,還有喚醒單元、中斷控制等等。使用USART之前需要向USART_CR1暫存器的UE位置1使能 USART,UE位用來開啟供給給串列埠的時鐘。傳送或者接收資料字長可選8位或9位,由USART_CR1的M位控制。
4、傳送器
當 USART_CR1 暫存器的傳送使能位 TE置1時,啟動資料傳送,傳送移位暫存器的資料會在 TX 引腳輸出,低位在前,高位在後。如果是同步模式 SCLK 也輸出時鐘訊號。一個字元幀傳送需要三個部分:起始位+資料幀+停止位。起始位是一個位週期的低電平,位週期就是每一位佔用的時間;資料幀就是我們要傳送的 8 位或 9 位資料,資料是從最低位開始傳輸的;停止位是一定時間週期的高電平。停止位時間長短是可以通過 USART 控制暫存器2(USART_CR2)的STOP[1:0]位控制,可選 0.5 個、1 個、1.5 個和 2 個停止位。預設使用 1 個停止位。2 個停止位適用於正常USART 模式、單線模式和調變解調器模式。0.5 個和 1.5 個停止位用於智慧卡模式。當選擇 8 位字長,使用 1 個停止位時,具體傳送字元時序圖見圖
當傳送使能位 TE 置 1 之後,傳送器開始會先發送一個空閒幀(一個數據幀長度的高電平),接下來就可以往 USART_DR 暫存器寫入要傳送的資料。在寫入最後一個數據後,需要等待 USART 狀態暫存器(USART_SR)的 TC 位為 1,表示資料傳輸完成,如果USART_CR1 暫存器的 TCIE 位置 1,將產生中斷。
在傳送資料時,程式設計的時候有幾個比較重要的標誌位我們來總結下。
名稱 | 描述 |
TE | 傳送使能 |
TXE | 傳送暫存器為空,傳送單個位元組的時候使用 |
TC | 傳送完成,傳送多個位元組資料的時候使用 |
TXIE | 傳送完成中斷使能 |
5、接收器
如果將 USART_CR1 暫存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 線開始搜尋起始位。在確定到起始位後就根據 RX 線電平狀態把資料存放在接收移位暫存器內。
接收完成後就把接收移位暫存器資料移到 RDR 內,並把 USART_SR 暫存器的 RXNE 位置1,同時如果 USART_CR2 暫存器的 RXNEIE 置 1 的話可以產生中斷。
在接收資料時,程式設計的時候有幾個比較重要的標誌位我們來總結下。
名稱 |
描述 |
RE |
接收使能 |
RXNE |
讀資料暫存器非空 |
RENEIE |
接收完成中斷使能 |
6、小數波特率生成
波特率指資料訊號對載波的調製速率,它用單位時間內載波調製狀態改變次數來表示,單位為波特。位元率指單位時間內傳輸的位元數,單位 bit/s(bps)。對於USART波特率與位元率相等,以後不區分這兩個概念。波特率越大,傳輸速率越快。USART 的傳送器和接收器使用相同的波特率。計算公式如下:
其中,fPLCK 為 USART 時鐘, USARTDIV 是一個存放在波特率暫存器(USART_BRR)的一個無符號定點數。其中 DIV_Mantissa[11:0] 位定義USARTDIV的整數部分DIV_Fraction[3:0]位定義 USARTDIV 的小數部分。例如:DIV_Mantissa=24(0x18),DIV_Fraction=10(0x0A),此時USART_BRR值為0x18A;那麼 USARTDIV 的小數位 10/16=0.625;整數位24,最終USARTDIV 的值為 24.625。 如果知道 USARTDIV 值為 27.68,那麼 DIV_Fraction=16*0.68=10.88,最接近的正整數為11,所以DIV_Fraction[3:0]為0xB;DIV_Mantissa=整數(27.68)=27,即為0x1B。波特率的常用值有 2400、9600、19200、115200。下面以例項講解如何設定暫存器值 得到波特率的值。
我們知道 USART1 使用 APB2 匯流排時鐘,最高可達 72MHz,其他 USART 的最高頻率為 36MHz。我們選取 USART1 作為例項講解,即 fPLCK=72MHz。為得到 115200bps 的波特率,此時:
解得USARTDIV=39.0625,可算得 DIV_Fraction=0.0625*16=1=0x01 ,DIV_Mantissa=39=0x17,即應該設定 USART_BRR 的值為 0x171。
7、校驗控制
STM32F103 系列控制器USART支援奇偶校驗。當使用校驗位時,串列埠傳輸的長度將是8 位的資料幀加上1位的校驗位總共9位,此時 USART_CR1暫存器的M位需要設定為1,即9資料位。將USART_CR1暫存器的PCE位置1就可以啟動奇偶校驗控制,奇偶校驗由硬體自動完成。啟動了奇偶校驗控制之後,在傳送資料幀時會自動新增校驗位,接收資料時自動驗證校驗位。接收資料時如果出現奇偶校驗位驗證失敗,會見USART_SR暫存器的PE位置1,並可以產生奇偶校驗中斷。
使能了奇偶校驗控制後,每個字元幀的格式將變成:起始位+資料幀+校驗位+停止位。
8、中斷控制
USART 有多箇中斷請求事件,具體見表
中斷事件 |
事件標誌 |
使能控制位 |
傳送資料暫存器為空 |
TXE |
TXEIE |
CTS標誌 |
CTS |
CTSIE |
傳送完成 |
TC |
TCIE |
準備好讀取接收到的資料 |
RXNE |
RXNEIE |
檢測到上溢錯誤 |
ORE |
|
檢測到空閒線路 |
IDLE |
IELEIE |
奇偶檢驗錯誤 |
PE |
PEIE |
斷路標誌 |
LBD |
LBDIE |
多緩衝通訊中的噪聲標誌、上溢錯誤和幀錯誤 |
NF/ORE/FE |
EIE |
1.5 重要結構體及函式
HAL 庫函式對每個外設都建立了一個初始化結構體,比如 USART_InitTypeDef,結構體成員用於設定外設工作引數,並由外設初始化配置函式,比如 USART_Init()呼叫,這些
設定引數將會設定外設相應的暫存器,達到配置外設工作環境的目的。
初始化結構體和初始化庫函式配合使用是 HAL 庫精髓所在,理解了初始化結構體每個成員意義基本上就可以對該外設運用自如了。初始化結構體定義在 stm32f1xx_hal_usart.h
檔案中,初始化庫函式定義在 stm32f1xx_hal_usart.c 檔案中,程式設計時我們可以結合這兩個檔案內註釋使用。
1.5.1 結構體
1、USART 初始化結構體
typedefstruct{ uint32_tBaudRate;//波特率 uint32_tWordLength;//字長 uint32_tStopBits;//停止位 uint32_tParity;//校驗位 uint32_tMode;//UART模式 uint32_tHwFlowCtl;//硬體流控制 uint32_tOverSampling;//過取樣模式 uint32_tCLKLastBit;//最尾位時鐘脈衝 }USART_InitTypeDef;
1) BaudRate:波特率設定。一般設定為 2400、9600、19200、115200。
2) WordLength:資料幀字長,可選8位或9位。它設定UART_CR1暫存器的M位的值。如果沒有使能奇偶校驗控制,一般使用8資料位;如果使能了奇偶校驗則一般設定為9資料位。
3) StopBits:停止位設定,可選 0.5 個、1 個、1.5 個和 2 個停止位,它設定USART_CR2 暫存器的 STOP[1:0]位的值,一般我們選擇 1 個停止位。
4) Parity :奇偶校驗控制選擇,可選USART_PARITY_NONE ( 無校驗 ) 、USART_PARITY_EVEN (偶校驗)以及 USART_PARITY_ODD (奇校驗),它設定 UART_CR1 暫存器的 PCE 位和 PS 位的值。
5) Mode:UART 模式選擇,有 USART_MODE_RX 和 USART_MODE_TX,允許使用邏輯或運算選擇兩個,它設定 USART_CR1 暫存器的 RE 位和 TE 位。
2、同步時鐘初始化結構體
1 typedefstruct 2 { 3 uint16_tUSART_Clock;//同步時鐘CR2_CLKEN 4 uint16_tUSART_CPOL;//極性CR2_CPOL 5 uint16_tUSART_CPHA;//相位CR2_CPHA 6 uint16_tUSART_LastBit;//最後一個位的時鐘脈衝CR2_LBC 7 }USART_ClockInitTypeDef;
1.5.2 函式
1-串列埠初始化函式
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
2-中斷配置函式
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)
3-串列埠使能函式
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
4-資料傳送函式
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
5-資料接收函式
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
6-中斷狀態位獲取函式
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
1.6 UART1收發通訊實驗
USART 只需兩根訊號線即可完成雙向通訊,對硬體要求低,使得很多模組都預留USART 介面來實現與其他模組或者控制器進行資料傳輸,比如 GSM 模組,WIFI 模組、藍芽模組等等。在硬體設計時,注意還需要一根“共地線”。
我們不僅僅可以將資料傳送到串列埠除錯助手,我們還可以在串列埠除錯助手傳送資料給控制器,控制器程式根據接收到的資料進行下一步工作。首先,我們來編寫一個程式實現開發板與電腦通訊,在開發板上電時通過 USART傳送一串字串給電腦,然後開發板進入中斷接收等待狀態,如果電腦有傳送資料過來,開發板就會產生中斷,我們在中斷服務函式接收資料,並馬上把資料返回傳送給電腦。
1.6.1 硬體設計
為利用 USART 實現開發板與電腦通訊,需要用到一個 USB 轉 USART 的 IC,我們選擇CH340G晶片來實現這個功能,CH340G 是一個 USB 匯流排的轉接晶片,實現 USB 轉USART、USB 轉 lrDA 紅外或者 USB 轉印表機介面,我們使用其 USB 轉 USART 功能。具體電路設計如下圖,我們將 CH340G 的 TXD 引腳與 USART1 的 RX 引腳連線,CH340G的RXD引腳與USART1的TX引腳連線。CH340G 晶片整合在開發板上,其地線(GND)已與控制器的GND連通。
1.6.2 軟體設計
建立了兩個檔案:bsp_usart.c和bsp _usart.h檔案用來存放 USART 驅動程式及相關巨集定義。
1. 程式設計要點
1) 使能 RX 和 TX 引腳 GPIO 時鐘和 USART 時鐘;
2) 初始化 GPIO,並將 GPIO 複用到 USART 上;
3) 配置 USART 引數;
4) 配置中斷控制器並使能 USART 接收中斷;
5) 使能 USART;
6) 在 USART 接收中斷服務函式實現資料接收和傳送。
2. 程式碼實現
(1)建立bsp_usart.h
1 #ifndef_BSP_USART_H_ 2 #define_BSP_USART_H_ 3 4 //串列埠1-USART1 5 #defineDEBUG_USARTxUSART1 6 #defineDEBUG_USART_CLKRCC_APB2Periph_USART1 7 #defineDEBUG_USART_APBxClkCmdRCC_APB2PeriphClockCmd 8 #defineDEBUG_USART_BAUDRATE115200 9 //USARTGPIO引腳定義 10 #defineDEBUG_USART_GPIO_CLK(RCC_APB2Periph_GPIOA) 11 #defineDEBUG_USART_GPIO_APBxClkCmdRCC_APB2PeriphClockCmd 12 13 #defineDEBUG_USART_TX_GPIO_PORTGPIOA 14 #defineDEBUG_USART_TX_GPIO_PINGPIO_Pin_9 15 #defineDEBUG_USART_RX_GPIO_PORTGPIOA 16 #defineDEBUG_USART_RX_GPIO_PINGPIO_Pin_10 17 #defineDEBUG_USART_IRQUSART1_IRQn 18 #defineDEBUG_USART_IRQHandlerUSART1_IRQHandler 19 20 voidUSART_Config(void); 21 voidUsart_SendByte(USART_TypeDef*pUSARTx,uint8_tdata); 22 voidUsart_SendHalfWord(USART_TypeDef*pUSARTx,uint16_tdata); 23 voidUsart_SendArray(USART_TypeDef*pUSARTx,uint8_t*array,uint8_tnum); 24 voidUsart_SendStr(USART_TypeDef*pUSARTx,uint8_t*str); 25 26 #endif/*_BSP_USART_H_*/
(2)建立bsp_usart.c檔案
1 #include"bsp_usart.h" 2 staticvoidNVIC_Configuration(void) 3 { 4 NVIC_InitTypeDefNVIC_InitStructure; 5 6 /*巢狀向量中斷控制器組選擇*/ 7 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 8 9 /*配置USART為中斷源*/ 10 NVIC_InitStructure.NVIC_IRQChannel=DEBUG_USART_IRQ;//USART1_IRQn 11 /*搶斷優先順序*/ 12 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; 13 /*子優先順序*/ 14 NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; 15 /*使能中斷*/ 16 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; 17 /*初始化配置NVIC*/ 18 NVIC_Init(&NVIC_InitStructure); 19 } 20 21 voidUSART_Config(void) 22 { 23 GPIO_InitTypeDefGPIO_InitStructure; 24 USART_InitTypeDefUSART_InitStructure; 25 //開啟串列埠GPIO時鐘 26 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK,ENABLE); 27 28 //開啟串列埠外設時鐘 29 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK,ENABLE); 30 31 //將USARTTx的GPIO配置為推輓複用模式 32 GPIO_InitStructure.GPIO_Pin=DEBUG_USART_TX_GPIO_PIN;//GPIO_Pin_9 33 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; 34 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 35 GPIO_Init(DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStructure); 36 37 //將USARTRx的GPIO配置為浮空輸入模式 38 GPIO_InitStructure.GPIO_Pin=DEBUG_USART_RX_GPIO_PIN;//GPIO_Pin_10 39 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; 40 GPIO_Init(DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStructure); 41 42 //配置串列埠工作引數 43 //配置波特率 44 USART_InitStructure.USART_BaudRate=DEBUG_USART_BAUDRATE;//115200 45 //配置幀資料字長 46 USART_InitStructure.USART_WordLength=USART_WordLength_8b; 47 //配置停止位 48 USART_InitStructure.USART_StopBits=USART_StopBits_1; 49 //配置校驗位 50 USART_InitStructure.USART_Parity=USART_Parity_No; 51 //配置硬體控制流 52 USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; 53 //配置工作模式,收發一起 54 USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; 55 //完成串列埠初始化配置 56 USART_Init(DEBUG_USARTx,&USART_InitStructure); 57 58 //串列埠中斷優先順序配置 59 NVIC_Configuration(); 60 61 //使能接收中斷 62 USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,ENABLE); 63 64 //使能串列埠 65 USART_Cmd(DEBUG_USARTx,ENABLE); 66 } 67 68 /*傳送一個位元組的資料*/ 69 voidUsart_SendByte(USART_TypeDef*pUSARTx,uint8_tdata) 70 { 71 USART_SendData(pUSARTx,data); 72 while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET); 73 } 74 75 /*傳送兩個位元組的資料*/ 76 voidUsart_SendHalfWord(USART_TypeDef*pUSARTx,uint16_tdata) 77 { 78 uint8_ttemp_h,temp_l; 79 80 temp_h=(data&0xff00)>>8; 81 temp_l=data&0xff; 82 83 USART_SendData(pUSARTx,temp_h); 84 while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET); 85 86 USART_SendData(pUSARTx,temp_l); 87 while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET); 88 } 89 90 /*傳送8為資料的陣列*/ 91 voidUsart_SendArray(USART_TypeDef*pUSARTx,uint8_t*array,uint8_tnum) 92 { 93 uint8_ti; 94 for(i=0;i<num;i++) 95 { 96 Usart_SendByte(pUSARTx,array[i]); 97 } 98 while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); 99 } 100 101 /*傳送字串*/ 102 voidUsart_SendStr(USART_TypeDef*pUSARTx,uint8_t*str) 103 { 104 uint8_ti=0; 105 do 106 { 107 Usart_SendByte(pUSARTx,*(str+i)); 108 i++; 109 }while(*(str+i)!='\0'); 110 while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); 111 } 112 113 //重定向C庫函式printf到串列埠,重定向後可使用printf函式 114 intfputc(intch,FILE*f) 115 { 116 /*傳送一個位元組資料到串列埠*/ 117 USART_SendData(DEBUG_USARTx,(uint8_t)ch); 118 119 /*等待發送完畢*/ 120 while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TXE)==RESET); 121 122 return(ch); 123 } 124 125 //重定向C庫函式scanf到串列埠,重定向後可使用printf、getchar等函式 126 intfgetc(FILE*f) 127 { 128 /*等待串列埠輸入資料*/ 129 while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_RXNE)==RESET); 130 131 return(int)USART_ReceiveData(DEBUG_USARTx); 132 }
(3)建立main.c
1 intmain(void) 2 { 3 uint8_ta[10]={88,2,3,4,5,6,7,8,9,10}; 4 USART_Config(); 5 6 //Usart_SendByte(DEBUG_USARTx,'a'); 7 //Usart_SendHalfWord(DEBUG_USARTx,0xff56); 8 //Usart_SendArray(DEBUG_USARTx,a,10); 9 //Usart_SendStr(DEBUG_USARTx,"傳送字串測試\n"); 10 11 printf("串列埠printf函式測試\n"); 12 13 while(1) 14 { 15 } 16 }