国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

STM32串口通信—串口的接收和發(fā)送詳解

這篇具有很好參考價值的文章主要介紹了STM32串口通信—串口的接收和發(fā)送詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

目錄

前言:

STM32串口通信基礎(chǔ)知識:

1,STM32里的串口通信

2,串口的發(fā)送和接收

串口發(fā)送:

串口接收:

串口在STM32中的配置:

1. RCC開啟USART、串口TX/RX所對應(yīng)的GPIO口

2. 初始化GPIO口

3. 串口初始化

4. 串口使能

5. 串口發(fā)送數(shù)據(jù)

串口接收的兩種實現(xiàn)方式:

1,輪詢方式:

2,中斷方式:

查詢RXNE標(biāo)志位

?使用中斷

實戰(zhàn)演練:

1. 初始化LED燈相應(yīng)的GPIO口

2. 初始化USART3

3. 實現(xiàn)發(fā)送功能

4. 實現(xiàn)接收字符串功能

1,通過輪詢的方式檢查是否接收到了特定的字符串:

2,通過中斷的方式實現(xiàn)USART3接收特定的字符串:

1. 配置NVIC以使能USART3中斷

2. 在USART3初始化函數(shù)中開啟接收中斷

3. 編寫USART3的中斷服務(wù)函數(shù)來處理接收到的字節(jié)

5. 主函數(shù)

總結(jié):


前言:

本文在于記錄自己最近做項目過程中遇到的問題和總結(jié),各種情況下串口通信在STM32的實際使用方面占有很大的比重,本文主要對串口的發(fā)送和接受做了一個詳細(xì)的總結(jié)和規(guī)劃,同時也對串口通信做一個簡要的總結(jié)。

STM32串口通信基礎(chǔ)知識:

1,STM32里的串口通信

在STM32里,串口通信是USART,STM32可以通過串口和其他設(shè)備進(jìn)行傳輸并行數(shù)據(jù),是全雙工,異步時鐘控制,設(shè)備之間是點(diǎn)對點(diǎn)的傳輸。對應(yīng)的STM32引腳分別是RX和TX端。STM32的串口資源有USART1、USART2、USART3.

串口的幾個重要的參數(shù):

  • 波特率,串口通信的速率
  • 空閑,一般為高電平
  • 起始位,標(biāo)志一個數(shù)據(jù)幀的開始,固定為低電平。當(dāng)數(shù)據(jù)開始發(fā)送時,產(chǎn)生一個下降沿。(空閑–>起始位)
  • 數(shù)據(jù)位,發(fā)送數(shù)據(jù)幀,1為高電平,0為低電平。低位先行。
    比如 發(fā)送數(shù)據(jù)幀0x0F 在數(shù)據(jù)幀里就是低位線性 即 1111 0000
  • 校驗位,用于數(shù)據(jù)驗證,根據(jù)數(shù)據(jù)位的計算得來。有奇校驗,偶校驗和無校驗。
  • 停止位,用于數(shù)據(jù)的間隔,固定為高電平。數(shù)據(jù)幀發(fā)送完成后,產(chǎn)生一個上升沿。(數(shù)據(jù)傳輸–>停止位)

下方就是一個字節(jié)數(shù)據(jù)的傳輸過程,從圖中可以看出,串口發(fā)送的數(shù)據(jù)一般都是以數(shù)據(jù)幀的形式進(jìn)行傳輸,每個數(shù)據(jù)幀都由起始位,數(shù)據(jù)位,停止位組成, 且停止位可變。

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

2,串口的發(fā)送和接收

USART是STM32內(nèi)部集成的硬件外設(shè),可以根據(jù)數(shù)據(jù)寄存器的一個字節(jié)數(shù)據(jù)自動生成數(shù)據(jù)幀時序,從TX引腳發(fā)送出去,也可以自動接收RX引腳的數(shù)據(jù)幀時序,拼接成一個字節(jié)數(shù)據(jù),存放在數(shù)據(jù)寄存器里。

當(dāng)配置好USART的電路之后,直接讀取數(shù)據(jù)寄存器,就可以自動發(fā)送數(shù)據(jù)和接收數(shù)據(jù)了。在發(fā)送和接收的模塊有4個重要的寄存器

  • 發(fā)送數(shù)據(jù)寄存器TDR
  • 發(fā)送移位寄存器,把一個字節(jié)的數(shù)據(jù)一位一位的移出去
  • 接收數(shù)據(jù)寄存器RDR
  • 接收移位寄存器,把一個字節(jié)的數(shù)據(jù)

下方為串口的發(fā)送和接收圖解:

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

串口發(fā)送:

在配置串口的各個參數(shù)時,可以選擇發(fā)送數(shù)據(jù)幀的數(shù)據(jù)位的大小,可選8位或9位。

串口發(fā)送數(shù)據(jù)實際上就是對發(fā)送數(shù)據(jù)寄存器TDR進(jìn)行寫操作。

1. 當(dāng)串口發(fā)送數(shù)據(jù)時,會檢測發(fā)送移位寄存器是不是有數(shù)據(jù)正在移位,如果沒有移位,那么這個數(shù)據(jù)就會立刻轉(zhuǎn)移到發(fā)送移位寄存器里。準(zhǔn)備發(fā)送。

2. 當(dāng)數(shù)據(jù)移動到移位寄存器時,會產(chǎn)生一個TXE發(fā)送寄存器空標(biāo)志位,該位描述如下。當(dāng)TXE被置1,那么就可以在TDR寫入下一個數(shù)據(jù)了。即發(fā)送下一個數(shù)據(jù)。

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

3. 發(fā)送移位寄存器在發(fā)送器控制的控制下,向右移位,一位一位的把數(shù)據(jù)傳輸?shù)絋X引腳。stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

4. 數(shù)據(jù)移位完成后,新的數(shù)據(jù)就會再次從TDR轉(zhuǎn)移到發(fā)送移位寄存器里來,依次重復(fù)1-3的過程。通過讀取TXE標(biāo)志位來判斷是否發(fā)送下一個數(shù)據(jù)。

串口接收:
  1. 數(shù)據(jù)從RX引腳通向接收移位寄存器,在接收控制的控制下,一位一位的讀取RX的電平,把第一位放在最高位,然后右移,移位八次之后就可以接收一個字節(jié)了。
  2. 當(dāng)一個字節(jié)數(shù)據(jù)移位完成之后,這一個字節(jié)的數(shù)據(jù)就會整體的移到接收數(shù)據(jù)寄存器RDR里來。
  3. 在轉(zhuǎn)移時會置RXNE接收標(biāo)志位,即RDR寄存器非空,下方為該位的描述。當(dāng)被置1后,就說明數(shù)據(jù)可以被讀出。

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

下圖即為串口接收的工作流程

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

串口在STM32中的配置:

1. RCC開啟USART、串口TX/RX所對應(yīng)的GPIO口

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);  //開啟USART2的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);   //開啟GPIOA的時鐘

2. 初始化GPIO口

這里注意哈,根據(jù)自己的需求來配置GPIO口,發(fā)送和接收是都需要還是只需要其中一個。然后對應(yīng)的根據(jù)引腳定義表來初始化對應(yīng)的GPIO口。

USART3對應(yīng)的引腳

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

USART2對應(yīng)的引腳

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

USART1對應(yīng)的引腳

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

這里根據(jù)手冊來看,RX引腳模式配置成浮空輸入或者上拉輸入。TX引腳模式配置成復(fù)用推挽輸出。

    GPIO_InitTypeDef GPIO_InitStructure;

    // USART3 TX -> PB10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);   

    // USART3 RX -> PB11
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

3. 串口初始化

注意哈,USART_Init()這個函數(shù),是用來配置串口的相關(guān)參數(shù)的。

  • USART_BaudRate 串口通信使用的波特率 一般是9600或者是115200,這里我們給9600
  • USART_HardwareFlowControl 是否選擇硬件流觸發(fā),一般這個我們也不選,所以選擇無硬件流觸發(fā)。
  • USART_Mode 這個參數(shù)要注意了哈,串口的模式,發(fā)送模式還是接收模式,還是兩者都有,這里使用收發(fā)模式
  • USART_Parity 校驗位,可以選擇奇偶校驗和不校驗。沒有需求就直接無校驗
  • USART_StopBits 停止位 有1、0.5、2位,我們這里選1位停止位
  • USART_WordLength 數(shù)據(jù)位 有8位和9位可以選擇
 //串口初始化
	USART_InitTypeDef USART_InitStruct;
	USART_StructInit(&USART_InitStruct);  //初始默認(rèn)值
	USART_InitStruct.USART_BaudRate=9600;
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;   //不使用硬件流觸發(fā)
	USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;	//收發(fā)模式(TX 發(fā)送模式  RX 接收模式)   			
	USART_InitStruct.USART_Parity=USART_Parity_No;   		//不選擇校驗
	USART_InitStruct.USART_StopBits=USART_StopBits_1;  		//停止位1位
	USART_InitStruct.USART_WordLength=USART_WordLength_8b;	//數(shù)據(jù)位8位
	USART_Init(USART3,&USART_InitStruct);

4. 串口使能

//串口使能
   USART_Cmd(USART3,ENABLE);

5. 串口發(fā)送數(shù)據(jù)

注意哈,我們要判斷TXE標(biāo)志位的狀態(tài)。0,數(shù)據(jù)還沒有被轉(zhuǎn)移到移位寄存器;1,數(shù)據(jù)已經(jīng)被轉(zhuǎn)移到移位寄存器。當(dāng)TXE標(biāo)志位為1時,就說明可以發(fā)送下一個數(shù)據(jù)了。詳細(xì)過程可看上面串口發(fā)送的解釋。

//串口3發(fā)送一個字節(jié)
void Usart3_SendByte(u8 val)
{
    USART_SendData(USART3, val);
	//0 表示數(shù)據(jù)還未轉(zhuǎn)移到移位寄存器 循環(huán)等待 1 數(shù)據(jù)已經(jīng)被轉(zhuǎn)移到了移位寄存器可以發(fā)送數(shù)據(jù)
    while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);	//等待發(fā)送完成,不需要手動清零 再次寫入TDR時會自動清零	
}

經(jīng)過上述五步的配置,單片機(jī)就可以通過串口發(fā)送數(shù)據(jù)了。

串口接收的兩種實現(xiàn)方式:

串口接收通??梢酝ㄟ^輪詢(Polling)中斷(Interrupt)兩種方式來實現(xiàn)。

  • 輪詢方式就是通過不斷的查詢RXNE標(biāo)志位,通過判斷RXNE位的狀態(tài)來確定數(shù)據(jù)是否接收。
  • 中斷方式就是通過配置接收輸出控制通道,配置NVIC,在中斷服務(wù)子函數(shù)里進(jìn)行數(shù)據(jù)的接收。

1,輪詢方式:

在輪詢方式中,程序通過不斷地查詢串口接收緩沖區(qū)是否有數(shù)據(jù)到達(dá)。當(dāng)檢測到數(shù)據(jù)到達(dá)時,程序立即讀取接收緩沖區(qū)中的數(shù)據(jù)。

優(yōu)點(diǎn):

  • 實現(xiàn)簡單,易于理解。
  • 可以直接在接收到數(shù)據(jù)后立即進(jìn)行處理。
  • 適合于數(shù)據(jù)傳輸量不大且CPU負(fù)荷較輕的場合。

缺點(diǎn):

  • 需要不斷地輪詢串口接收緩沖區(qū),占用 CPU 資源。
  • 無法及時響應(yīng)其他任務(wù)或事件。
  • 效率較低,可能會錯過一些數(shù)據(jù)。

實現(xiàn)步驟:
配置串口:設(shè)置串口的波特率、數(shù)據(jù)位數(shù)、停止位等參數(shù)。
輪詢狀態(tài)寄存器:不斷檢查USART的狀態(tài)寄存器,判斷接收緩沖區(qū)是否有新數(shù)據(jù)。
讀取數(shù)據(jù):一旦發(fā)現(xiàn)有新數(shù)據(jù),立即從數(shù)據(jù)寄存器讀取數(shù)據(jù)。?

示例代碼:

// 定義一個函數(shù),用于輪詢方式接收 USART 數(shù)據(jù)
void USART_Receive_Polling(void) {
    // 進(jìn)入一個無限循環(huán)
    while(1) {
        // 檢查接收緩沖區(qū)是否有數(shù)據(jù)
        if(USART_GetFlagStatus(USART3, USART_FLAG_RXNE) == SET) {
            // 如果接收緩沖區(qū)有數(shù)據(jù)
            // 從接收緩沖區(qū)讀取數(shù)據(jù)
            uint8_t data = USART_ReceiveData(USART3);
            // 處理接收到的數(shù)據(jù)
            // 這里可以添加代碼來解析和處理接收到的數(shù)據(jù)
        }
        // 如果接收緩沖區(qū)無數(shù)據(jù),則繼續(xù)輪詢
        // 可以添加延時以降低 CPU 占用率
        // delay_ms(10);
    }
}

2,中斷方式:

在中斷方式中,程序允許 MCU 在接收到數(shù)據(jù)時觸發(fā)串口接收中斷,并在中斷服務(wù)函數(shù)中處理接收到的數(shù)據(jù)。當(dāng)接收緩沖區(qū)有新數(shù)據(jù)時,硬件自動產(chǎn)生一個中斷,CPU響應(yīng)這個中斷并執(zhí)行中斷服務(wù)程序來處理接收到的數(shù)據(jù)。

優(yōu)點(diǎn):

  • 采用了中斷機(jī)制,不需要不斷地輪詢串口接收緩沖區(qū),減少了 CPU 的占用率。
  • 效率高,可以及時響應(yīng)其他任務(wù)或事件,提高了系統(tǒng)的實時性。
  • 適合于數(shù)據(jù)量大或?qū)崟r性要求高的應(yīng)用

缺點(diǎn):

  • 實現(xiàn)相對復(fù)雜,需要編寫中斷服務(wù)函數(shù)。
  • 在中斷服務(wù)函數(shù)中對數(shù)據(jù)的處理需要考慮中斷嵌套、優(yōu)先級等問題,需要謹(jǐn)慎設(shè)計。

實現(xiàn)步驟:
配置串口:同輪詢方式。
使能中斷:在串口初始化中,使能USART的接收中斷。
編寫中斷服務(wù)程序:實現(xiàn)USART的中斷服務(wù)函數(shù),該函數(shù)會在接收到新數(shù)據(jù)時被調(diào)用。
數(shù)據(jù)處理:在中斷服務(wù)程序中讀取接收到的數(shù)據(jù),并進(jìn)行相應(yīng)的處理。

示例代碼:

// USART3中斷處理函數(shù)
void USART3_IRQHandler(void) {
    // 檢查 USART3 接收中斷標(biāo)志位是否被設(shè)置
    if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {
        // 如果接收緩沖區(qū)非空
        // 從接收緩沖區(qū)讀取數(shù)據(jù)
        uint8_t data = USART_ReceiveData(USART3);
        // 處理接收到的數(shù)據(jù)
        // 在這里可以添加代碼來處理接收到的數(shù)據(jù)
    }
}

// 使 USART3 接收中斷
void USART_Receive_Interrupt(void) {
    // 使能串口接收中斷
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
    
    // 配置 USART3 中斷優(yōu)先級
    NVIC_InitTypeDef NVIC_InitStructure;
    // 設(shè)置中斷通道為 USART3
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    // 設(shè)置中斷搶占優(yōu)先級為0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    // 設(shè)置中斷響應(yīng)優(yōu)先級為0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    // 使能中斷通道
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    // 初始化 NVIC
    NVIC_Init(&NVIC_InitStructure);
    
    // 進(jìn)入一個無限循環(huán),等待中斷服務(wù)函數(shù)處理接收到的數(shù)據(jù)
    while(1) {
        // 在中斷服務(wù)函數(shù)中處理接收到的數(shù)據(jù)
    }
}

查詢RXNE標(biāo)志位

這里我們還是來看一看RXNE標(biāo)志位的描述

stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件
上圖描述,為0時數(shù)據(jù)沒有收到,為1時收到了數(shù)據(jù),數(shù)據(jù)可以從RDR里讀出

所以在主程序里不斷讀取RXNE標(biāo)志位,如果為1,表示數(shù)據(jù)可以讀出

uint8_t RX_Data; // 定義一個全局變量 RX_Data,用于存儲接收到的數(shù)據(jù)

int main() // 主函數(shù)入口
{ 
    Serial_Init(); // 初始化串口

    Serial_SendByte(0x16); // 向串口發(fā)送一個字節(jié)數(shù)據(jù) 0x16

    while(1) // 進(jìn)入一個無限循環(huán)
    {
        if(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == SET) // 檢查 USART2 接收緩沖區(qū)是否有數(shù)據(jù),0=RESET 循環(huán)等待 ,1=SET 可以接收數(shù)據(jù)
        {
            RX_Data = USART_ReceiveData(USART2); // 如果接收緩沖區(qū)有數(shù)據(jù),則從中讀取數(shù)據(jù)并存儲到 RX_Data 中
            Serial_SendByte(RX_Data); // 將接收到的數(shù)據(jù)發(fā)送回串口
        }
    }
}

if(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == SET)

// 檢查 USART2 接收緩沖區(qū)是否有數(shù)據(jù),0=RESET 循環(huán)等待 ,1=SET 可以接收數(shù)據(jù)

下圖為程序現(xiàn)象:pc向單片機(jī)發(fā)送數(shù)據(jù)0x15,單片機(jī)接收數(shù)據(jù)0x15,并且把接收到的數(shù)據(jù)作為數(shù)據(jù)發(fā)送到pc,在pc上顯示0x15。
stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

?使用中斷

  • 通過配置串口的接收作為中斷源,開啟中斷輸出控制,配置NVIC。開啟中斷通道。
// 開啟 USART2 接收中斷
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

// 配置 NVIC(Nested Vectored Interrupt Controller,嵌套向量中斷控制器)

// 設(shè)置 NVIC 分組優(yōu)先級,選擇分組 2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

// 定義一個 NVIC_InitTypeDef 結(jié)構(gòu)體變量,用于配置中斷控制器
NVIC_InitTypeDef NVIC_InitStruct;

// 設(shè)置中斷通道為 USART2,即選擇 USART2 的中斷通道
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;

// 使能中斷通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

// 設(shè)置搶占優(yōu)先級為 1
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;

// 設(shè)置子優(yōu)先級為 1
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;

// 將配置好的 NVIC_InitStruct 結(jié)構(gòu)體變量傳入 NVIC_Init 函數(shù)中,對 NVIC 進(jìn)行配置
NVIC_Init(&NVIC_InitStruct);
  • 中斷服務(wù)子函數(shù)
    中斷服務(wù)子函數(shù)寫好后,就可以在中斷里讀取接收到的數(shù)據(jù)了。
    當(dāng)接收到數(shù)據(jù)后,觸發(fā)接收中斷,主程序暫停執(zhí)行。接收完數(shù)據(jù)后主程序回復(fù)執(zhí)行。當(dāng)接收到數(shù)據(jù)時,就觸發(fā)中斷。
void USART2_IRQHandler(void)
{
    // 檢查 USART2 接收緩沖區(qū)是否有數(shù)據(jù)
    if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)   // RXNE 標(biāo)志位為1 表示可以接收數(shù)據(jù)
    {
        // 從接收緩沖區(qū)讀取數(shù)據(jù)并存儲到 RX_Data 中
        RX_Data = USART_ReceiveData(USART2);

        // 設(shè)置標(biāo)志位 Flag 為 1,表示已接收到數(shù)據(jù)
        Flag = 1;

        // 清除 USART2 接收中斷標(biāo)志位 RXNE
        USART_ClearITPendingBit(USART2, USART_IT_RXNE);  // 清除 RXNE 標(biāo)志位
    }
}
  • 主程序測試
uint8_t RX_Data; // 定義一個全局變量 RX_Data,用于存儲接收到的數(shù)據(jù)
uint8_t Flag;    // 定義一個全局變量 Flag,用于表示是否接收到數(shù)據(jù)的標(biāo)志位

int main() // 主函數(shù)入口
{
    Serial_Init(); // 初始化串口
    Serial_SendByte(0x16); // 向串口發(fā)送一個字節(jié)數(shù)據(jù) 0x16

    while(1) // 進(jìn)入一個無限循環(huán)
    {
        if(Flag == 1) // 如果接收到數(shù)據(jù)的標(biāo)志位為 1
        {
            Serial_SendByte(RX_Data); // 向串口發(fā)送接收到的數(shù)據(jù),這里可以改為自己需要的邏輯
        }
    }
}

void USART2_IRQHandler(void) // USART2 中斷服務(wù)函數(shù)
{
    if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET) // 如果 USART2 接收到數(shù)據(jù)
    {
        RX_Data = USART_ReceiveData(USART2); // 從接收緩沖區(qū)讀取數(shù)據(jù)并存儲到 RX_Data 中
        Flag = 1; // 設(shè)置接收到數(shù)據(jù)的標(biāo)志位 Flag 為 1
        USART_ClearITPendingBit(USART2, USART_IT_RXNE); // 清除 USART2 的接收中斷標(biāo)志位 RXNE,準(zhǔn)備接收下一次數(shù)據(jù)
    }
}

下圖為程序現(xiàn)象:可以看到,串口確實收到了數(shù)據(jù),只是我把接收到的數(shù)據(jù)0xFE放在了while循環(huán)里,這說明數(shù)據(jù)接收是成功的,使用中斷是可行的。
stm32 串口收發(fā),STM32,stm32,串口通信,單片機(jī),嵌入式硬件

實戰(zhàn)演練:

要求:使用stm32f103C8T6,使用標(biāo)準(zhǔn)庫,硬件方面使用到了一個LED燈,要求在PC端串口助手發(fā)送"led on",單片機(jī)的usart3接收到PC端發(fā)送的led on時,打開LED燈,同時向PC端發(fā)送“已打開”

為了實現(xiàn)提出的要求,你需要按照以下步驟進(jìn)行編程和硬件配置:

  1. 硬件連接

    • LED 燈連接到單片機(jī)的一個 GPIO 端口(比如 PA0)。
    • USART3 需要連接到 PC 通過串口或者通過串口轉(zhuǎn)USB模塊。
  2. 軟件實現(xiàn)(使用STM32標(biāo)準(zhǔn)庫)

以下提供一個粗略的實現(xiàn)示例:

首先,確保你已經(jīng)在項目中正確配置了 STM32F103 的標(biāo)準(zhǔn)庫,以及正確設(shè)置系統(tǒng)時鐘。

1. 初始化LED燈相應(yīng)的GPIO口

void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure; // 定義一個 GPIO_InitTypeDef 結(jié)構(gòu)體變量,用于配置 GPIO
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 開啟 GPIOA 時鐘
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 設(shè)置要初始化的引腳為 PA0
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 設(shè)置引腳工作模式為推挽輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 設(shè)置引腳的輸出速度為 50MHz
    
    GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化 GPIOA 的 PA0 引腳
    GPIO_SetBits(GPIOA, GPIO_Pin_0); // 將 PA0 引腳輸出高電平,默認(rèn)關(guān)閉 LED
}

2. 初始化USART3

void USART3_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure; // 定義一個 GPIO_InitTypeDef 結(jié)構(gòu)體變量,用于配置 GPIO
    USART_InitTypeDef USART_InitStructure; // 定義一個 USART_InitTypeDef 結(jié)構(gòu)體變量,用于配置 USART
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 打開 GPIOB 的時鐘
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); // 打開 USART3 的時鐘
    
    // 配置 USART3 的 TX 引腳為復(fù)用推挽輸出模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // 設(shè)置要初始化的引腳為 PB10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 設(shè)置引腳工作模式為復(fù)用推挽輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 設(shè)置引腳的輸出速度為 50MHz
    GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化 GPIOB 的 PB10 引腳
    
    // 配置 USART3 的 RX 引腳為浮空輸入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // 設(shè)置要初始化的引腳為 PB11
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 設(shè)置引腳工作模式為浮空輸入
    GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化 GPIOB 的 PB11 引腳
    
    // 配置 USART3 的通信參數(shù)
    USART_InitStructure.USART_BaudRate = 9600; // 設(shè)置波特率為 9600
    USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 設(shè)置數(shù)據(jù)位長度為 8 位
    USART_InitStructure.USART_StopBits = USART_StopBits_1; // 設(shè)置停止位為 1 位
    USART_InitStructure.USART_Parity = USART_Parity_No; // 設(shè)置校驗位為無校驗
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 設(shè)置硬件流控制為無流控
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 設(shè)置 USART 的工作模式為接收和發(fā)送都使能
    USART_Init(USART3, &USART_InitStructure); // 初始化 USART3
    
    USART_Cmd(USART3, ENABLE); // 使能 USART3
}

3. 實現(xiàn)發(fā)送功能

void USART3_SendChar(char ch) {
    // 發(fā)送字符數(shù)據(jù) ch 到 USART3
    USART_SendData(USART3, (uint8_t) ch);

    // 等待發(fā)送完成
    while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
}

4. 實現(xiàn)接收字符串功能

此部分可以通過中斷或者輪詢的方式實現(xiàn)。

1,通過輪詢的方式檢查是否接收到了特定的字符串:
void checkReceive(void) {
    uint8_t data; // 定義一個無符號 8 位整數(shù)型變量 data,用于存儲接收到的數(shù)據(jù)
    char buffer[8]; // 定義一個長度為 8 的字符數(shù)組 buffer,用于存儲接收到的數(shù)據(jù)
    int i = 0; // 定義一個整型變量 i,用于索引 buffer 數(shù)組
    
    // 進(jìn)入一個循環(huán),循環(huán)條件是 i 小于 7
    while (i < 7) {
        // 檢查 USART3 接收緩沖區(qū)是否非空,即是否有數(shù)據(jù)可讀
        if (USART_GetFlagStatus(USART3, USART_FLAG_RXNE) != RESET) {
            // 如果接收緩沖區(qū)非空,則讀取接收到的數(shù)據(jù)并存儲到 data 變量中
            data = (uint8_t)USART_ReceiveData(USART3);
            // 將讀取到的數(shù)據(jù)存儲到 buffer 數(shù)組中,并將索引 i 自增
            buffer[i++] = data;
        }
    }
    
    // 在 buffer 數(shù)組末尾添加字符串結(jié)束標(biāo)志 '\0'
    buffer[i] = '\0';
    
    // 比較 buffer 數(shù)組中的內(nèi)容是否為 "led on"
    if (strcmp(buffer, "led on") == 0) {
        // 如果接收到的數(shù)據(jù)是 "led on",則執(zhí)行以下操作
        
        // 打開 LED 燈,即將 GPIOA 的 PA0 引腳輸出低電平
        GPIO_ResetBits(GPIOA, GPIO_Pin_0);
        
        // 定義一個指向字符串常量 "已打開" 的指針 msg
        char *msg = "已打開";
        
        // 進(jìn)入一個循環(huán),循環(huán)條件是指針 msg 指向的字符不為空字符 '\0'
        while (*msg) {
            // 發(fā)送指針 msg 指向的字符到 USART3,然后指針 msg 自增
            USART3_SendChar(*msg++);
        }
    }
}

注意:這里并沒有添加中斷服務(wù)程序,也沒有實現(xiàn)字符緩存區(qū)的溢出處理,此外,發(fā)送和接收字符的精確處理邏輯可能需要根據(jù)實際需求調(diào)整。實際項目中還可能需要考慮debounce(消抖)和更加復(fù)雜的串口命令解析。

務(wù)必確保單片機(jī)的時鐘配置正確,并且USART3的引腳與你連接的外設(shè)相匹配。在進(jìn)行硬件連線時也要確保正確連接。

2,通過中斷的方式實現(xiàn)USART3接收特定的字符串:

要通過中斷方式實現(xiàn)USART3接收字符串,我們需要做幾件事情:

  1. 配置NVIC以使能USART3中斷。
  2. 在USART3初始化函數(shù)中開啟接收中斷
  3. 編寫USART3的中斷服務(wù)函數(shù)來處理接收到的字節(jié)。

這種實現(xiàn)方式相比輪詢,可以有效減少CPU的負(fù)擔(dān),特別是在數(shù)據(jù)不頻繁接收時。

1. 配置NVIC以使能USART3中斷

USART3_Init函數(shù)中,初始化USART3后,你應(yīng)該使能中斷:

NVIC_InitTypeDef NVIC_InitStructure; // 定義一個 NVIC_InitTypeDef 結(jié)構(gòu)體變量,用于配置 NVIC 中斷控制器

// 設(shè)置 NVIC 優(yōu)先級分組為 2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

// 設(shè)置中斷源為 USART3
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
// 設(shè)置搶占優(yōu)先級為 1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// 設(shè)置子優(yōu)先級為 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
// 使能中斷通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// 初始化 NVIC
NVIC_Init(&NVIC_InitStructure);

// 使能 USART3 接收中斷
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
2. 在USART3初始化函數(shù)中開啟接收中斷

已經(jīng)在上面的步驟中通過調(diào)用USART_ITConfig來實現(xiàn)了。

3. 編寫USART3的中斷服務(wù)函數(shù)來處理接收到的字節(jié)

你要定義一個緩沖區(qū)來存儲接收到的字符,并在接收到整個字符串后進(jìn)行處理:

#define BUFFER_SIZE 100 // 定義緩沖區(qū)大小為 100
char buffer[BUFFER_SIZE]; // 聲明一個大小為 BUFFER_SIZE 的字符數(shù)組作為接收緩沖區(qū)
volatile unsigned int buffer_index = 0; // 聲明一個無符號整數(shù)變量,用于表示當(dāng)前緩沖區(qū)的索引位置,使用 volatile 關(guān)鍵字聲明,表示在中斷中可能被改變,需要及時更新

void USART3_IRQHandler(void) { // 定義 USART3 的中斷服務(wù)函數(shù)
    // 檢查是否接收到數(shù)據(jù)
    if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) { // 如果接收到 USART3 的接收中斷標(biāo)志位
        char data = (char)USART_ReceiveData(USART3); // 讀取接收到的數(shù)據(jù),并轉(zhuǎn)換為字符類型

        // 簡單的字符串終止判斷(例如以換行結(jié)束)
        if (data != '\n' && buffer_index < BUFFER_SIZE - 1) { // 如果接收到的字符不是換行且緩沖區(qū)索引未超過最大長度減一
            buffer[buffer_index++] = data; // 將接收到的字符存入緩沖區(qū)中,并更新索引
        } else { 
            buffer[buffer_index] = '\0'; // 確保字符串結(jié)束,即在緩沖區(qū)最后添加 '\0' 表示字符串結(jié)束

            // 檢查接收到的命令
            if (strcmp(buffer, "led on") == 0) { // 如果接收到的命令是 "led on"
                GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 點(diǎn)亮 LED
                char *msg = "已打開\n"; // 定義一個指向字符串的指針,表示要發(fā)送的消息
                while (*msg) { // 循環(huán)發(fā)送消息中的每一個字符,直到遇到 '\0' 表示字符串結(jié)束
                    USART3_SendChar(*msg++); // 發(fā)送字符并將指針移向下一個字符
                }
            }

            // 重置索引,準(zhǔn)備下一次接收
            buffer_index = 0; // 將緩沖區(qū)索引重置為 0,準(zhǔn)備接收下一條命令
        }

        USART_ClearITPendingBit(USART3, USART_IT_RXNE); // 清除接收中斷標(biāo)志位,準(zhǔn)備下一次接收中斷
    }
}

這個示例代碼會在接收到一串字符后處理這串字符。如果接收到的字符串是"led on"加上換行符'\n',它將點(diǎn)亮LED并通過USART3發(fā)送回"已打開\n"。

注意:實際上你可能需要添加更多的錯誤處理和緩沖區(qū)管理來處理可能出現(xiàn)的錯誤和異常情況(比如緩沖區(qū)溢出)。

此外,為了讓上述代碼正常工作,請確保你的USART3接收中斷已經(jīng)正確配置,并且你的系統(tǒng)時鐘設(shè)置支持你的串口通信需求。你可能還需要根據(jù)你的具體硬件連接調(diào)整GPIO端口初始化和LED操作的代碼。

5. 主函數(shù)

在主函數(shù)里初始化LED和USART3,然后不斷檢查串口接收:

int main(void) {
    SystemInit();  // 調(diào)用 SystemInit() 函數(shù)初始化系統(tǒng)時鐘,這通常是啟動代碼中的一部分,用于初始化系統(tǒng)的時鐘和基本的硬件設(shè)置。

    LED_Init();  // 調(diào)用 LED_Init() 函數(shù)初始化 LED,準(zhǔn)備控制 LED 燈的狀態(tài)。

    USART3_Init(); // 調(diào)用 USART3_Init() 函數(shù)初始化 USART3,配置 USART3 的通信參數(shù)和引腳連接等。

    while (1) {
        checkReceive(); // 循環(huán)調(diào)用 checkReceive() 函數(shù),用于檢查是否接收到特定命令,并根據(jù)接收到的命令執(zhí)行相應(yīng)的操作。
    }
}

注:這里的實戰(zhàn)演練講的不是很清晰,建議移步這篇文章:通過串口中斷的方式進(jìn)行ASR-01S模塊與STM32通信(問題與解決)

總結(jié):

本文大致總結(jié)了串口的發(fā)送和接收。

串口的配置,使用查詢或者中斷來接收數(shù)據(jù)。

串口的使用會很常用到,所以在這里對串口做一個總結(jié),也算是對之前知識的一個回顧和總結(jié),加強(qiáng)印象。

參考鏈接:

https://gitcode.csdn.net/65e6e6b81a836825ed787581.html?dp_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MjgwNDA1NSwiZXhwIjoxNzEwNjEwNTI5LCJpYXQiOjE3MTAwMDU3MjksInVzZXJuYW1lIjoid2VpeGluXzUxMDI4NTg0In0.1Eac2jHw_Iz7Nc6l36BNEre9cCLz_gXA_Lz_OQnvtLc文章來源地址http://www.zghlxwxcb.cn/news/detail-844660.html

到了這里,關(guān)于STM32串口通信—串口的接收和發(fā)送詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • STM-32:USART串口協(xié)議、串口外設(shè)—數(shù)據(jù)發(fā)送/數(shù)據(jù)發(fā)送+接收

    STM-32:USART串口協(xié)議、串口外設(shè)—數(shù)據(jù)發(fā)送/數(shù)據(jù)發(fā)送+接收

    通信的目的:將一個設(shè)備的數(shù)據(jù)傳送到另一個設(shè)備,擴(kuò)展硬件系統(tǒng)。比如STM32芯片里面集成了很多功能模塊,如定時器計數(shù)、PWM輸出、AD采集等等,這些都是芯片內(nèi)部的電路,它們的配置寄存器、數(shù)據(jù)寄存器都在芯片里面,操作簡單,直接讀寫就行。但是有些功能STM32內(nèi)部沒有

    2024年02月04日
    瀏覽(93)
  • 【STM32】STM32F103C8T6串口通信,實現(xiàn)3個串口收發(fā)數(shù)據(jù)

    串口通信(Serial Communications)實現(xiàn)單片機(jī)與電腦或者其它外設(shè)進(jìn)行通信,通信時只需兩根線(TX,RX)就可以實現(xiàn)數(shù)據(jù)傳輸。STM32f103有三個串口,分別為串口1(RX PA10, TX PA 9),串口2(RX PA3,TX PA2),串口3(RX PB11,TX PB10)。 以下代碼是配置三個串口: usart.c usart.h main.c 注意,

    2024年02月12日
    瀏覽(28)
  • STM32--HAl庫串口接收與發(fā)送

    STM32--HAl庫串口接收與發(fā)送

    ? ? ? ? 在此之前,我們已經(jīng)學(xué)習(xí)了單片機(jī)串口通信的原理(江科協(xié)),再寫一遍我個人認(rèn)知:世界上任何兩個事物如果要進(jìn)行交流的話,那必然需要兩個東西進(jìn)行通信的,就像兩個人之間,兩個國人之間用普通話就能夠清楚的交流,但是讓我們中國人和外國人去交流可能會

    2024年04月10日
    瀏覽(23)
  • STM32標(biāo)準(zhǔn)庫開發(fā)——串口發(fā)送/單字節(jié)接收

    STM32標(biāo)準(zhǔn)庫開發(fā)——串口發(fā)送/單字節(jié)接收

    串口發(fā)送信息 啟動串口一的時鐘 初始化對應(yīng)串口一的時鐘,引腳,將TX引腳設(shè)置為復(fù)用推挽輸出。 配置串口一配置寄存器,設(shè)置波特率為9600,關(guān)閉硬件流控,不使用校驗位,數(shù)據(jù)長度為八字節(jié) 封裝串口發(fā)送字節(jié)函數(shù) 封裝串口發(fā)送字符串函數(shù) 封裝串口發(fā)送數(shù)組函數(shù) 封裝串口

    2024年01月23日
    瀏覽(26)
  • 【STM32】HAL庫 串口中斷發(fā)送與接收

    【STM32】HAL庫 串口中斷發(fā)送與接收

    【STM32】HAL庫 新建MDK工程 【STM32】HAL庫 串口輪詢發(fā)送 使用stm32串口中斷發(fā)送和中斷接收 在主函數(shù)前開啟中斷,接受字節(jié)數(shù)為5 接受5個字節(jié)后,進(jìn)入中斷接收完成回調(diào)函數(shù),重新再開啟中斷,并把接收到的數(shù)據(jù)返回 修改接收數(shù)組長度,改為開啟串口空閑中斷 接收事件回調(diào)函數(shù)

    2024年02月08日
    瀏覽(31)
  • 最詳細(xì)STM32,cubeMX串口發(fā)送,接收數(shù)據(jù)

    最詳細(xì)STM32,cubeMX串口發(fā)送,接收數(shù)據(jù)

    這篇文章將詳細(xì)介紹 串口 發(fā)送數(shù)據(jù),接受數(shù)據(jù)。 實驗開發(fā)板:STM32F103C8T6。 所需軟件:keil5 , cubeMX 。 實驗?zāi)康模毫私?串口的基礎(chǔ)知識,掌握串口如何發(fā)送,接收數(shù)據(jù) 。 實驗:串口發(fā)送數(shù)據(jù)點(diǎn)亮 led。 如果想了解串口的基礎(chǔ)知識可以參考我之前的文章: STM32Cube串口USART發(fā)送

    2024年02月04日
    瀏覽(126)
  • STM32實現(xiàn)三個串口同時開啟發(fā)送接收數(shù)據(jù)

    STM32實現(xiàn)三個串口同時開啟發(fā)送接收數(shù)據(jù)

    ????????實現(xiàn)STM32開通三個串口,每個串口都可以實現(xiàn)接收和發(fā)送數(shù)據(jù)。 ???????? 編程時,嚴(yán)禁在中斷函數(shù)中寫入發(fā)送串口數(shù)據(jù)代碼,否則會出錯,具體原因不清楚(有大佬知道的話幫我指出),可能原因是DR寄存器沖突導(dǎo)致。 ? ? ? ? RX,TX連接到A9,A10使用串口1,使

    2024年04月13日
    瀏覽(89)
  • 18、江科大stm32視頻學(xué)習(xí)筆記——USART串口發(fā)送&串口發(fā)送和接收

    18、江科大stm32視頻學(xué)習(xí)筆記——USART串口發(fā)送&串口發(fā)送和接收

    目錄 一、USART串口發(fā)送? 1、電路圖 2、printf函數(shù)的移植方法 3、serial.c 4、main.c 5、解決直接寫漢字,編譯器報錯 二 、USART串口發(fā)送和接收 1、查詢實現(xiàn) 2、中斷實現(xiàn)? (1)在Serial.c中添加的代碼 (2)主函數(shù)中調(diào)用 (3)思路 (4)完整的Serial.c代碼 (5)mian.c ?要交叉連接,所

    2023年04月08日
    瀏覽(20)
  • 【STM32】HAL庫 STM32CubeMX——DMA (串口DMA發(fā)送接收)

    【STM32】HAL庫 STM32CubeMX——DMA (串口DMA發(fā)送接收)

    軟件: STM32CubeMX KEIL5 mcuisp 串口通信助手 硬件: STM32F103C8Tx 杜邦線,面包板,USB轉(zhuǎn)TTL DMA,全稱Direct Memory Access,即直接存儲器訪問。 DMA傳輸將數(shù)據(jù)從一個地址空間復(fù)制到另一個地址空間,提供在外設(shè)和存儲器之間或者存儲器和存儲器之間的高速數(shù)據(jù)傳輸。 我們知道系統(tǒng)的運(yùn)

    2024年02月12日
    瀏覽(24)
  • STM32 LL庫 串口DMA發(fā)送接收配置教程

    本文詳細(xì)介紹了如何在STM32中使用LL庫進(jìn)行串口DMA發(fā)送和接收的配置,包括STM32CubeMX的配置、代碼初始化、發(fā)送功能和接收功能。通過本教程,您將學(xué)會如何正確配置串口DMA,并實現(xiàn)高效的數(shù)據(jù)傳輸。

    2024年02月10日
    瀏覽(218)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包