STM32學習筆記——串口通信


9.1 STM32串口簡介
    9.2 硬件設計
    9.3 軟件設計
    9.4 下載驗證

 

 

1)STM32F1串口資源介紹: 
    串口作為MCU的重要外部接口,同時也是軟件開發重要的調試手段,其重要性不言而喻。現在基本上所有的
MCU都帶有串口。
    STM32的串口資源相當豐富,功能很強勁。STM32F103ZET6最多可提供5路串口,有分數波特率發生器、支持
同步單線通信和半雙工單線通信、支持LIN、支持調制解調器操作、智能卡協議和IrDA SIR ENDEC規范、具有DMA等。
2)串口管腳在芯片上的位置和特性:
    表21 USART管腳配置
————————————————

 

 

8.3.8 USART復用功能重映射
參見復用重映射和調試I/O配置寄存器(AFIO_MAPR)
表45 USART3重映射
(注:部分重映射只適用於64、100和144腳的封裝;完全重映射只適用於100和144腳的封裝)

 

 表46 USART2重映射(重映射只適用於100和144腳的封裝)

 

 表47 USART1重映射

 

 

 

 

4)設置串口:
    對於復用功能的IO口,首先要使能GPIO時鍾,然后使能復用功能時鍾,同時,把GPIO模式設置為復用功能
對應的模式。之后就是初始化設置串口參數,包括波特率、停止位等。接下來使能串口。同時,如果開啟了
串口的中斷,要初始化NVIC設置中斷優先級,最后編寫中斷服務函數。
     串口設置的一般步驟總結:
① 使能串口時鍾,使能GPIO時鍾;
② 復位串口;
③ 設置GPIO端口模式;
④ 初始化串口參數;
⑤ 開啟中斷並初始化NVIC(如果需要開啟中斷才需要這個步驟);
⑥ 使能串口;
⑦ 編寫中斷處理函數。
 
    下面簡單介紹一下與串口基本配置直接相關的幾個固件庫函數。
    ① 串口時鍾使能。
    串口是掛在APB2下面的外設,所以使能函數為:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
    ② 串口復位。
    當外設出現異常時,需要通過復位設置,實現該外設的復位,然后重新配置這個外設以達到使其重新工作
的目的。一般在系統剛開始配置外設時,先執行復位該外設的操作。復位是在USART_DeInit()函數中完成的。
void USART_DeInit(USART_TypeDef* USARTx); //串口復位
比如要復位串口1,調用:
USART_DeInit(USART1); //串口1復位
    ③ 串口參數初始化。
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
    這個函數的第一個參數是指定初始化的串口標號,這里選擇USART1。
    第二個參數是一個USART_InitTypeDef類型的結構體指針,其成員變量用來配置串口的一些參數。一般格式如下:
USART_InitStructure.USART_BaudRate = bound; //設置波特率;
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字長為8位數據格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //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(USART1, &USART_InitStructure); //初始化串口
    從上面的初始化格式可以看出,初始化需要配置參數為:波特率、字長、停止位、奇偶校驗位、硬件數據流控制、模式(收/發)。我們可以根據需要設置這些參數。
    ④ 數據發送和接收。
    STM32的發送和接收是通過數據寄存器USART_DR來實現的,這是一個雙寄存器,包含了TDR和RDR。
當向該寄存器寫數據時,串口就會自動發送;當收到數據時,也存在該寄存器中。
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);    //向USART_DR寄存器寫入一個數據
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);            //讀取串口接收到的數據
    ⑤ 串口狀態。
    串口的狀態可以通過寄存器USART_SR讀取,各位描述如下:
31~10 9 8 7 6 5 4 3 2 1 0
保留 CTS LBD TXE TC RXNE IDLE ORE NE FE PE
    這里關注兩位:Bit5:RXNE;Bit6:TC
    RXNE(讀數據寄存器非空):當該位被置一時,說明已經有數據被接收了,並且可以讀出來了。此時應盡快讀取USART_DR。
    讀取USART_DR或向該位寫0,都可以清除該位。
    TC(發送完成):當該位被置位時,說明USART_DR中的數據已經被發送完成了。如果設置了這個位的中斷,則會產生中斷。
    清零該位的兩種方法:a、讀取USART_SR,寫USART_DR。b、直接向該位寫0。
    讀取串口狀態的函數:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
函數的第二個參數很關鍵,它表示我們要查看串口的哪個狀態,比如要判斷讀寄存器是否為非kong(RXNE),執行:
USART_GetFlagStatus(USART1, USART_FLAG_RXNE);
如果要判斷發送是否完成(TC),執行:
USART_GetFlagStatus(USART1, USART_FLAG_TC);
這些標志號在MDK里面通過宏定義實現:
#define USART_IT_PE ((uint16_t)0x0028)
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
#define USART_IT_IDLE ((uint16_t)0x0424)
#define USART_IT_LBD ((uint16_t)0x0846)
#define USART_IT_CTS ((uint16_t)0x096A)
#define USART_IT_ERR ((uint16_t)0x0060)
#define USART_IT_ORE ((uint16_t)0x0360)
#define USART_IT_NE ((uint16_t)0x0260)
#define USART_IT_FE ((uint16_t)0x0160)
    ⑥ 串口使能。
USART_Cmd(USART1, ENABLE); //使能串口
    ⑦ 開啟串口響應中斷。
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
    函數第二個參數很關鍵,它表示使能串口的類型,即使能哪種串口中斷,因為串口中斷有很多種。比如:
在接收到數據時(RXNE讀寄存器非空),產生中斷,則開啟中斷的方法為:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //開啟中斷,接收到數據中斷
在發送數據結束時(TC,發送完成)要產生中斷,則開啟中斷的方法為:
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
    ⑧ 獲取相應中斷狀態。
     我們使能了某個中斷,則當中斷發生時,就會設置狀態寄存器中的某個標志位。
    在中斷處理函數中,要判斷哪種中斷是否發生,可以判斷對應的某個標志位。使用的函數是:
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
    比如,使能了串口發送完成中斷,則當中斷發生時,可以在中斷處理函數中使用下面語句來判斷是否發生串口發送完成中斷。
USART_GetITStatus(USART1, USART_IT_TC);
返回值是SET,說明串口發送完成中斷發生了。
9.2 硬件設計
    本實驗用到的硬件資源有:
1)指示燈DS0;
2)串口1。
9.3 軟件設計
本章代碼設計比較簡單,因為串口初始化和接收的代碼用的是SYSTEM文件夾下面的串口內容。這里對代碼部分稍作講解。
//初始化IO串口1
//bound:波特率
void uart_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//① 串口時鍾使能,GPIO時鍾使能,復用時鍾使能:在使用一個內置外設的時候,首先要使能相應的GPIO時鍾,然后使能復用功能時鍾和內置外設時鍾
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1和GPIOA時鍾
//② 串口復位
USART_DeInit(USART1); //復位串口1
//③ GPIO端口模式配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //USART1_TX PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //USART1_RX PA.10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.10
//④ 串口參數初始化
USART_InitStructure.USART_BaudRate = bound; //設置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字長8位
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(USART1, &USART_InitStructure); //初始化串口
#ifEN_USART1_RX //如果使能了接收
//⑤ NVIC初始化
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //搶占優先級 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能IRQ通道
NVIC_Init(&NVIC_InitStructure); //初始化中斷優先級
//⑥ 開啟中斷
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //開啟中斷
//⑦ 使能串口
USART_Cmd(USART1, ENABLE); //使能串口
}
    從代碼中看出,初始化串口的過程,和前面介紹的一致,用標號①~⑦表示了順序:
① 串口時鍾使能,GPIO時鍾使能;
② 串口復位;
③ GPIO端口模式設置;
④ 串口參數初始化;
⑤ 初始化NVIC;
⑥ 開啟中斷;
⑦ 使能串口。
    復用功能下GPIO模式怎么判定:
    需要查看《中文參考手冊V10》P110的表格“8.1.11外設的GPIO配置”,這個我們在前面的端口復用章節有提到,這里再講解一下。
    串口GPIO模式配置表:
序號 USART引腳 配置 GPIO配置
1 USARTx_TX 全雙工模式 推挽復用輸出
半雙工模式 推挽復用輸出
1 USARTx_Rx 全雙工模式 浮空輸入或帶上拉輸入
半雙工模式 未用,可作為通用I/O
    查看手冊得知,配置全雙工的串口1,則TX(PA9)需要配置為推挽復用輸出,RX(PA10)配置為浮空輸入或帶上拉輸入。
 中斷處理函數
void USART1_IRQHandler(void) //串口1中斷服務程序
{
u8 Res;

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾)
{
Res =USART_ReceiveData(USART1); //讀取接收到的數據
if((USART_RX_STA&0x8000)==0) //接收未完成
{
if(USART_RX_STA&0x4000) //接收到了0x0d
{
if(Res!=0x0a)
USART_RX_STA=0; //接收錯誤,重新開始
else
USART_RX_STA|=0x8000; //接收完成了
}
else //還沒收到0X0D
{
if(Res==0x0d)
USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))
USART_RX_STA=0; //接收數據錯誤,重新開始接收
}
}
}
}
}
    函數體里面通過函數:
if(USART_GetITStatus(USART1,
USART_IT_RXNE)!=
RESET)
判斷是否是接收中斷,如果是,則讀取串口接收到的數據:
Res=USART_ReceiveData(USART1);
    接下來對數據進行分析:
    這里設計了一個小小的接收協議:通過這個函數,配合一個數組USART_RX_BUF[],一個接收狀態標志
USART_RX_STA實現對串口數據的接收管理。
    USART_RX_STA各位定義如下:
                                              USART_RX_STA
bit15 bit14 bit13~0
接收完成標志 接收到0x0d標志 接收到的有效數據個數
    USART_RX_BUF的大小由USART_REC_LEN定義。
    設計思路如下:
    當接收到從電腦發過來的數據后,把接收到的數據保存在USART_RX_BUF中,同時在接收狀態標志
USART_RX_STA中計算接收到的有效數據個數,當收到回車(0x0d、0x0a)的第一個字節0x0d時,計時器
將不再增加,等待0x0a的到來。
    如果0x0a沒有到,則認為此次接收失敗,重新開始下一次接收。
    如果順利接收到0x0a,則標記USART_RX_STA的第15位,這樣完成一次接收,並等待該位被其他程序
清除,從而開始下一次接收。
    如果在收到數據超過 USART_REC_LEN的時候,還沒有收到0x0d,則會丟棄前面的數據,重新接收。
9.4 下載驗證

————————————————
版權聲明:本文為CSDN博主「sz189981」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sz189981/article/details/69487046

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM