1. 程式人生 > >STM32系統學習——USART(串列埠通訊)

STM32系統學習——USART(串列埠通訊)

串列埠通訊是一種裝置間非常常用的序列通行方式,其簡單便捷,大部分電子裝置都支援。

一、物理層

常用RS-232標準,主要規定了訊號的用途、通訊介面以及訊號的電平標準。
   
“DB9介面”之間通過串列埠訊號線建立起連線,串列埠訊號線使用”RS-232標準“傳輸資料訊號,這些訊號通過記過電平轉換晶片轉換成控制器能識別的TLL標準的電平訊號,才能實現通訊。
1.電平標準
可分為TTL標準以及RS-232標準。
常見的電子電路中常見TTL的電平標準,理想狀態使用5V表示二進位制邏輯1,0V表示邏輯0;而為了增加串列埠通訊的遠距離傳輸以及抗干擾能力,RS-232使用-15V表示邏輯1,+15V表示邏輯0。
  


因為控制器一般使用TTL電平標準,所以常常使用MA3232晶片將TTL以及RS-232電平的訊號進行互相轉換。

2.RS-232訊號線
最初RS-232串列埠標準常用於計算機、路由與調製調節器(“貓”)之間通訊,裝置被分為資料終端裝置DTE(計算機、路由)和資料通訊裝置DCE(調製調節器)。舊臺式計算機,一般都有RS-232標準的COM口,也稱DB9介面。

                                                          DB9訊號線說明


公頭標準接法,只要使用直通型串列埠線連線起來即可。

二、協議層

協議層中,規定了資料包的內容,它由起始位、主體資料、校驗位以及停止位組成,通訊雙方的資料包格式要約定一致才能正常收發資料 。
1、波特率
非同步通訊中由於沒有時鐘訊號,所以2個通訊裝置需約定好波特率,常見的有4800、9600、115200等。
2、通訊的起始和停止訊號
串列埠通訊的一個數據包從起始訊號開始,知道停止訊號結束。資料包的起始訊號由一個邏輯0的資料位表示,而資料包的停止訊號可由0.5、1、1.5或2個邏輯1的資料位表示,只要雙方約定一致即可。
3、有效資料
在資料包的起始位之後緊接著的就是要傳輸的主體資料內容,也稱為有效資料,有效
資料的長度常被約定為 5、6、7或 8位長
4、資料校驗
在有效資料之後,有一個可選的資料校驗位。由於資料通訊相對容易受到外部干擾導致傳輸資料出現偏差,可以在傳輸過程加上校驗位來解決這個問題。校驗方法有奇校驗(odd)、偶校驗(even)、0校驗(space)、1校驗(mark)以及無校驗(noparity)。
奇校驗要求有效資料和校驗位中“1”的個數為奇數,比如一個 8 位長的有效資料為:01101001,此時總共有 4 個“1”,為達到奇校驗效果,校驗位為“1”,最後傳輸的資料將是 8 位的有效資料加上 1 位的校驗位總共 9 位。
偶校驗與奇校驗要求剛好相反,要求幀資料和校驗位中“1”的個數為偶數,比如資料幀:11001010,此時資料幀“1”的個數為 4 個,所以偶校驗位為“0”。
0 校驗是不管有效資料中的內容是什麼,校驗位總為“0”,1 校驗是校驗位總為“1”。

三、STM32的USART簡介
通用同步非同步收發器是一個序列通訊裝置,可以靈活的與外部裝置進行全雙工資料交換。有別與USART,還有一個UART,它在USART基礎上裁剪掉了同步通訊功能,只有非同步通訊。簡單區分同步和非同步就是看通訊時需不需要對外提供時鐘輸出,我們平時用的串列埠通訊基本都是 UART。
串列埠通訊一般是以幀格式傳輸資料,即一幀一幀傳輸,每幀包含有起始訊號、資料資訊、停止資訊,可能還有校驗資訊。
USART 滿足外部裝置對工業標準 NRZ 非同步序列資料格式的要求,並且使用了小數波特率發生器,可以提供多種波特率,使得它的應用更加廣泛。USART 支援同步單向通訊和半雙工單線通訊;還支援局域互連網路 LIN、智慧卡(SmartCard)協議與 lrDA(紅外線資料協會) SIR ENDEC規範。
USART支援使用 DMA,可實現高速資料通訊。

四、USART功能框圖剖析
  
下文結合圖片看加深理解。
1、功能引腳
TX:傳送資料輸出引腳。
RX:接收。
SW_RX:資料接收引腳,屬於內部引腳。
nRTS:請求以傳送,n表示低電平有效。如果使能 RTS 流控制,當USART接收器準備好接收新資料時就會將nRTS變成低電平;當接收暫存器已滿時,nRTS將被設定為高電平。該引腳只適用於硬體流控制。
nCTS:清除以傳送(Clear To Send),n表示低電平有效。如果使能 CTS流控制,傳送器在傳送下一幀資料之前會檢測 nCTS 引腳,如果為低電平,表示可以傳送資料,如果為高電平則在傳送完當前資料幀之後停止傳送。該引腳只適用於硬體流控制。
SCLK:傳送器時鐘輸出引腳。這個引腳僅適用於同步模式。
USART:下圖是STM32F103VET6晶片的USART引腳
這裡寫圖片描述
USART1的時鐘來源於APB2匯流排時鐘,最大頻率為72MHZ,其他4個時鐘來源於APB1匯流排時鐘,最大頻率36MHZ。UART只有非同步傳輸功能,沒有SCLK、nCTS和nRTS功能引腳。

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。
USART 支援 DMA 傳輸,可以實現高速資料傳輸。

3.控制器
USART有專門控制傳送的傳送器、控制接收的接收器,還有喚醒單元、中斷控制等。
使用USART之前需要向USART_CR1暫存器的UE位置1使能USART,UE位用於開啟供給串列埠的時鐘。傳送或者接收資料字長可選8或9位,由USARTT_CR1的M位控制。
1)傳送器
當USART_CR1暫存器的傳送使能位TE置1時,啟動資料傳送,傳送移位暫存器的資料會在TX引腳輸出,低位在前,高位在後。如果是同步模式SCLK也輸出時鐘訊號。
一個字元幀傳送需要3部分:起始位、資料幀、停止位。起始位是一個位週期的低電平,位週期就是每一位佔用的時間 ;資料幀就是我們要傳送的8或9位資料,資料是最低位開始傳輸的;停止位是一定時間週期的高電平。
停止位的時間長短可以通過USART控制暫存器2(USART_CR2)的STOP[1:0]位控制,可選0.5個、1個、1.5個、2個停止位。預設使用1個停止位。2個停止位適用於正常USART模式、單線模式和調變解調器模式。0.5和1.5個停止位用於智慧卡模式。
當發使能位TE置1之後,傳送器開始會發送一個空閒幀(一個數據幀長度的高電平),接下來就可以往USART_DR暫存器寫入要傳送的資料。在寫入最後一個數據後,需等待USART狀態暫存器(USART_SR)的TC位為1,表示資料傳輸完成。USART_CR1暫存器的TCIE位置1,則產生中斷。
傳送資料時,幾個重要的標誌位如下:
TE:傳送使能。
TXE:傳送暫存器為空,傳送單個位元組時使用。
TC:傳送完成,傳送多個位元組資料時候使用。
TXIE:傳送完成中斷使能。
2)接收器
將CR1暫存器的RE位置1,使能USART接收,使得接收器在RX線開始搜尋起始位。在確定起始位後,就根據RX線電平狀態把資料存放在接收移位暫存器內。接收完成後就把接收移位暫存器的資料移到PDR內,並把USART_SR暫存器的RXNE位置。如果USART_CR2暫存器的RXNEIE置1可以產生中斷。
接收資料時,幾個重要的標誌位如下:
RE: 接收使能。
RXNE:讀資料暫存器非空。
RXNEIE:傳送完成中斷使能。

4.小數波特率生成
USART 的傳送器和接收器使用相同的波特率。計算公式如下:
 
其中,f PLCK 為 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 作為例項講解,即 f PLCK =72MHz。為得到 115200bps 的波特率,此時:

115200 =72000000/(16 ∗ USARTDIV)

解 得 USARTDIV=39.0625 , 可 算 得 DIV_Fraction=0.0625*16=1=0x01 ,DIV_Mantissa=39=0x17,即應該設定 USART_BRR 的值為 0x171。

5.校驗控制
STM32F103系列控制器USART支援奇偶校驗。使用校驗位時,串列埠傳輸的長度將在8位資料幀上加上1位的校驗位,總共9位,此時USART_CR1暫存器的M位需要設定位1,即9資料位。將USART_CR1暫存器的PCE位置1就可以啟動奇偶校驗控制,奇偶校驗由硬體自動完成。啟動了奇偶校驗控制之後,傳送資料幀時會自動新增校驗位,接收資料自動驗證校驗位。接收資料時如果出現奇偶校驗位驗證失敗,會將USART_SR暫存器的PE置1,並可以產生奇偶校驗中斷。
使用了奇偶校驗控制位後,每個字元幀的格式變成了:起始位+資料幀+校驗位+停止位。

6.中斷控制
         

五、USART初始化結構體
初始化結構體的定義在stm32f10x_usart.h檔案中,初始化庫函式定義在stm32f10x_usart.c中。

                                            USART初始化結構體

typedef struct {
 uint32_t USART_BaudRate; // 波特率
 uint16_t USART_WordLength; // 字長
 uint16_t USART_StopBits; // 停止位
 uint16_t USART_Parity; // 校驗位
 uint16_t USART_Mode; // USART 模式
 uint16_t USART_HardwareFlowControl; // 硬體流控制
 } USART_InitTypeDef;

1)USART_BaudRate:波特率設定。標準庫函式會根據設定值計算得到USARTDIV值,從而設定USART_BRR的暫存器值。
2)USART_WordLength:.資料幀字長,它設定USART_CR1暫存器M位的值。如果沒有使能奇偶位校驗控制,一般使用8資料位。
3)USART_StopBits停止位設定。
4)USART_Parity:奇偶校驗控制選擇。
5)USART_Mode:USART模式選擇有USART_Mode_Rx和USART_Mode_Tx,允許使用邏輯或運算選擇兩個,它設定 USART_CR1暫存器的 RE 位和 TE位。
6)USART_HardwareFlowControl:硬體流控制選擇,只有在硬體流控制模式下才有效,可選有,使能RTS、使能CTS、同時使能RTS和CTS、不使用硬體流。

當使用同步模式,需配置SCLK引腳輸出脈衝的屬性,標準庫使用一個時鐘初始化結構體USART_ClockInitTypeDef來設定,該結構體內容只有在同步模式下才設定。

                                                   USART時鐘初始化結構體

 typedef struct {
 uint16_t USART_Clock; // 時鐘使能控制
 uint16_t USART_CPOL; // 時鐘極性
 uint16_t USART_CPHA; // 時鐘相位
 uint16_t USART_LastBit; // 最尾位時鐘脈衝
 } USART_ClockInitTypeDef;

1)USART_Clock:同步模式下SCLK引腳上時鐘輸出使能控制,可選禁止時鐘輸出(USART_Clock_Disable)或開啟時鐘輸出(USART_Clock_Enable);如果使用同步模式傳送,一般都需要開啟時鐘。它設定 USART_CR2 暫存器的 CLKEN 位的值。 ݶ
2)USART_CPOL:同步模式下 SCLK 引腳上輸出時鐘極性設定,可設定在空閒時SCLK引腳為低電平(USART_CPOL_Low)或高電平(USART_CPOL_High)。它設定USART_CR2暫存器的 CPOL位的值。
3)USART_CPHA:同步模式下 SCLK 引腳上輸出時鐘相位設定,可設定在時鐘第一個變化沿捕獲資料(USART_CPHA_1Edge)或在時鐘第二個變化沿捕獲資料。它設定 USART_CR2暫存器的 CPHA位的值。USART_CPHA與 USART_CPOL配合使用可以獲得多種模式時鐘關係。
4)USART_LastBit:選擇在傳送最後一個數據位的時候時鐘脈衝是否在SCLK引腳輸出,可以是不輸出脈衝(USART_LastBit_Disable) 、 輸出脈衝(USART_LastBit_Enable)。它設定 USART_CR2 暫存器的 LBCL位的值。
 

六、USART1接發通訊實驗
USART 只需兩根訊號線即可完成雙向通訊,對硬體要求低,使得很多模組都預留USART 介面來實現與其他模組或者控制器進行資料傳輸,比如 GSM 模組,WIFI 模組、藍芽模組等等。在硬體設計時,注意還需要一根“共地線”。
來編寫一個程式實現開發板與電腦通訊,在開發板上電時通過USART傳送一串字串給電腦,然後開發板進入中斷接收等待狀態,如果電腦有傳送資料過來,開發板就會產生中斷,我們在中斷服務函式接收資料,並馬上把資料返回傳送給電腦。

1、程式設計思路

1) 使能 RX和 TX 引腳 GPIO時鐘和 USART時鐘;
2) 初始化 GPIO,並將 GPIO複用到 USART上;
3) 配置 USART 引數;
4) 配置中斷控制器並使能 USART接收中斷;
5) 使能 USART;
6) 在 USART接收中斷服務函式實現資料接收和傳送。
2、程式碼分析
1)GPIO和USART巨集定義

 /**
 * 串列埠巨集定義,不同的串列埠掛載的匯流排和 IO 不一樣,移植時需要修改這幾個巨集
 */
 // 串列埠 1-USART1
 #define DEBUG_USARTx USART1
 #define DEBUG_USART_CLK RCC_APB2Periph_USART1
 #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
 #define DEBUG_USART_BAUDRATE 115200
 // USART GPIO 引腳巨集定義
 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
 #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd

 #define DEBUG_USART_TX_GPIO_PORT GPIOA
 #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
 #define DEBUG_USART_RX_GPIO_PORT GPIOA
 #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10

 #define DEBUG_USART_IRQ USART1_IRQn
 #define DEBUG_USART_IRQHandler USART1_IRQHandler

使用巨集定義方便程式移植和升級 。開發板中的 CH340G 的收發引腳預設通過跳帽連線到 USART1,如果想使用其他串列埠,可以把 CH340G 跟 USART1 直接的連線跳帽拔掉,然後再把其他串列埠的 IO用杜邦線接到 CH340G的收發引腳即可。
這裡我們使用USART1,設定波特率為115200,選定USART的GPIO為PA9和PA10。

2)巢狀向量中斷控制器NVIC配置 

static void NVIC_Configuration(void)
 {
 NVIC_InitTypeDef NVIC_InitStructure;
 /* 巢狀向量中斷控制器組選擇 */
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

 /* 配置 USART 為中斷源 */
 NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
 /* 搶斷優先順序為 1 */
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
 /* 子優先順序為 1 */
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
 /* 使能中斷 */
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 /* 初始化配置 NVIC */
 NVIC_Init(&NVIC_InitStructure);
 }

3)USART初始化配置

void USART_Config(void)
 {
 GPIO_InitTypeDef GPIO_InitStructure;
 USART_InitTypeDef USART_InitStructure;

 // 開啟串列埠 GPIO 的時鐘
 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

 // 開啟串列埠外設的時鐘
 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

 // 將 USART Tx 的 GPIO 配置為推輓複用模式
 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

 // 將 USART Rx 的 GPIO 配置為浮空輸入模式
 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

 // 配置串列埠的工作引數
 // 配置波特率
 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
 // 配置 針資料字長
 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
 // 配置停止位
 USART_InitStructure.USART_StopBits = USART_StopBits_1;
 // 配置校驗位
 USART_InitStructure.USART_Parity = USART_Parity_No ;
 // 配置硬體流控制
 USART_InitStructure.USART_HardwareFlowControl =
 USART_HardwareFlowControl_None;
 // 配置工作模式,收發一起
 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 // 完成串列埠的初始化配置
 USART_Init(DEBUG_USARTx, &USART_InitStructure);

 // 串列埠中斷優先順序配置
 NVIC_Configuration();

 // 使能串列埠接收中斷
 USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

 // 使能串列埠
 USART_Cmd(DEBUG_USARTx, ENABLE);
 }

呼叫RCC_APB2PeriphClockCmd函式開啟GPIO埠時鐘,使用GPIO之前必須開啟對應的時鐘。RCC_APB2PeriphClockCmd函式開啟USART時鐘。
使用GPIO之前需要初始化配置它,並且還要新增特殊設定,因為我們使用它作為外設引腳,一般都有特殊功能,模式設定為複用功能,把串列埠的Tx引腳配置為複用推輓輸出,Rx引腳為浮空輸入,資料完全由外部輸入決定。
配置USAT1通訊引數為:波特率115200,字長8,1個停止位,沒有校驗位,不使用硬體流控制,收發一體工作模式,然後呼叫USART初始化函式完成配置。
USART接收中斷,需要配置NVIC,這裡呼叫NVIC_Configuration函式完成配置,然後呼叫USART_ITConfig函式使能USART接收中斷。
最後 呼叫USART_Cmd函式使能USART,最終配置的是USART_CR1的UE位,具體作用是開啟USART工作時鐘,沒有時鐘那USART這個外設就工作不了。

4)字元傳送

/***************** 傳送一個字元 **********************/
 void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
 {
 /* 傳送一個位元組資料到 USART */
 USART_SendData(pUSARTx,ch);

 /* 等待發送資料暫存器為空 */
 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
 }

 /***************** 傳送字串 **********************/
 void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
 {
 unsigned int k=0;
 do {
 Usart_SendByte( pUSARTx, *(str + k) );
 k++;
 } while (*(str + k)!='\0');

 /* 等待發送完成 */
 while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) {
 }
 }

Usart_SendByte用來指定USART傳送一個ASCLL碼字元,它有2個形參:第一個USART第二個待發送的字元,它通過呼叫庫函式USART_SendData來實現等待,並且增加了等待發送完成功能,它接收兩個引數:一個是USART,一個是事件標誌。這裡迴圈檢測傳送資料暫存器這個標誌,當跳出while迴圈時,說明發送資料暫存器為空。
Usart_SendString函式用來發送一個字串,實際呼叫Usar_SendByte函式傳送每個字元,直到遇到空字元才停止傳送。最後使用迴圈檢測傳送完成的事件標誌TC,保證資料完成後才退出函式。

5)USART中斷服務函式

void DEBUG_USART_IRQHandler(void)
 {
 uint8_t ucTemp;
 if (USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
 {
 ucTemp = USART_ReceiveData( DEBUG_USARTx );
 USART_SendData(USARTx,ucTemp);
 }

 }

該程式碼在stm32f10x_it.c檔案中的,用來集中存放中斷服務函式。當使能了中斷並中斷髮生時,就會執行這裡的中斷服務函式。
6)main函式

int main(void)
 {
 /*初始化 USART 配置模式為 115200 8-N-1,中斷接收*/
 USART_Config();

 Usart_SendString( DEBUG_USARTx,"這是一個串列埠中斷接收回顯實驗\n");

 while (1) {

 }
 }

USART_Config()函式完成USART初始化配置,包括GPIO USART配置,接收中斷使能等。
接下來呼叫字元傳送函式把資料發給串列埠除錯助手。
最後什麼也不做,等待接收中斷產生,並在中斷服務函式中回傳資料。

轉自:https://blog.csdn.net/zxh1592000/article/details/78656609