1. 在STM32F103RCT6 單片機(jī)上跑FreeRTOS 實(shí)時(shí)操作系統(tǒng),使用串口USART1 通訊,發(fā)送 – 接收數(shù)據(jù),實(shí)現(xiàn)上位機(jī)與下位機(jī)的通信
使用 FreeRTOS 提供的隊(duì)列(Queue)機(jī)制來實(shí)現(xiàn)數(shù)據(jù)的接收和發(fā)送
2. USART1 配置:
TX - PA9
RX - PA10
波特率:9600
數(shù)據(jù)位:8bit
校驗(yàn)位:無(wú)
停止位:1bit
數(shù)據(jù)格式:
RX: 55 AA 06 00 06 31 02 24 01 FC 80
TX: 55 AA 06 00 06 32 01 24 01 B8 70
55 AA – 幀頭
06 - 數(shù)據(jù)字節(jié)數(shù),不包括幀頭,不包括校驗(yàn)位
00 06 – 模塊
31 02 24-- 數(shù)據(jù)方向:從上位機(jī)(安卓LCD顯示屏)到下位機(jī)(STM32)
32 01 24-- 數(shù)據(jù)方向:從下位機(jī)(STM32) 到上位機(jī)(安卓LCD顯示屏)
01 - payload 要發(fā)送的數(shù)據(jù)具體內(nèi)容
FC 80 / B8 70 – CRC 16bit 校驗(yàn)方法計(jì)算出來的,用06 00 06 31 02 24 01 使用CRC計(jì)算器可以計(jì)算出來 FC 80
CRC在線計(jì)算網(wǎng)址:
http://www.ip33.com/crc.html
3. 實(shí)現(xiàn)代碼:
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#define USART1_BAUDRATE 9600
#define USART1_TX_PIN GPIO_Pin_9
#define USART1_RX_PIN GPIO_Pin_10
#define USART1_GPIO GPIOA
#define USART1_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART1_CLK RCC_APB2Periph_USART1
#define MODBUS_SLAVE_ADDR 0x01
#define RX_BUF_SIZE 15
#define TX_BUF_SIZE 15
static QueueHandle_t rx_queue;
static QueueHandle_t tx_queue;
static TaskHandle_t task_handle;
void USART1_Init() {
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// Enable clocks
RCC_APB2PeriphClockCmd(USART1_CLK | USART1_GPIO_CLK, ENABLE);
// Configure USART1 pins
GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN | USART1_RX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(USART1_GPIO, &GPIO_InitStructure);
// Configure USART1
USART_InitStructure.USART_BaudRate = USART1_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(USART1, &USART_InitStructure);
// Enable USART1 interrupts
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// Enable USART1
USART_Cmd(USART1, ENABLE);
// Initialize queues
rx_queue = xQueueCreate(RX_BUF_SIZE, sizeof(uint8_t));
tx_queue = xQueueCreate(TX_BUF_SIZE, sizeof(uint8_t));
// Create task for handling USART1 data
xTaskCreate(USART1_Task, "USART1 Task", 1024, NULL, 1, &task_handle);
}
void USART1_Task(void *pvParameters) {
while (1) {
// Wait for data to be received
uint8_t data;
while (xQueueReceive(rx_queue, &data, portMAX_DELAY) == pdFALSE);
// Process received data here
// ...
// Send response data
uint8_t resp_data[] = {MODBUS_SLAVE_ADDR, /* response data */};
uint16_t resp_len = sizeof(resp_data) / sizeof(uint8_t);
xQueueSend(tx_queue, resp_data, resp_len * sizeof(uint8_t));
}
}
void USART1_IRQHandler(void) {
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t data = USART_ReceiveData(USART1);
xQueueSendFromISR(rx_queue, &data, &xHigherPriorityTaskWoken);
}
if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) {
uint8_t data;
if (xQueueReceiveFromISR(tx_queue, &data, &xHigherPriorityTaskWoken) == pdTRUE) {
USART_SendData(USART1, data);
} else {
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
}
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
int main(void) {
// Initialize USART1
USART1_Init();
// Start the scheduler
vTaskStartScheduler();
}
在代碼中,首先使用 USART1_Init 函數(shù)初始化 USART1,并使用 FreeRTOS 提供的 xQueueCreate 函數(shù)創(chuàng)建兩個(gè)隊(duì)列,一個(gè)用于接收數(shù)據(jù),一個(gè)用于發(fā)送數(shù)據(jù)。然后使用 xTaskCreate 函數(shù)創(chuàng)建一個(gè)任務(wù)(USART1_Task),用于處理 USART1 數(shù)據(jù)。在任務(wù)中,使用 xQueueReceive 函數(shù)不斷等待接收數(shù)據(jù),并使用 xQueueSend 函數(shù)發(fā)送響應(yīng)數(shù)據(jù)。在 USART1_IRQHandler 中,使用 xQueueSendFromISR 和 xQueueReceiveFromISR 函數(shù)將接收到的數(shù)據(jù)和需要發(fā)送的數(shù)據(jù)加入相應(yīng)的隊(duì)列中,并啟用或禁用 USART1 的 TXE 中斷來控制數(shù)據(jù)的發(fā)送。
需要注意的是,在任務(wù)和中斷中使用的隊(duì)列必須定義為全局變量,以避免在棧上分配空間時(shí)出現(xiàn)問題。另外,由于 FreeRTOS 使用了搶占式調(diào)度方式,因此在任務(wù)和中斷中使用的隊(duì)列必須具有線程安全性,否則可能會(huì)導(dǎo)致數(shù)據(jù)丟失或者死鎖等問題。
其中:
1.
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
portBASE_TYPE 是 FreeRTOS 中定義的一個(gè)數(shù)據(jù)類型,用于表示任務(wù)調(diào)度器是否需要進(jìn)行任務(wù)切換。在 FreeRTOS 中,任務(wù)調(diào)度器采用搶占式策略來決定下一次執(zhí)行哪個(gè)任務(wù)。當(dāng)某個(gè)任務(wù)需要讓出 CPU 時(shí)間時(shí),會(huì)向任務(wù)調(diào)度器發(fā)送一個(gè)任務(wù)切換請(qǐng)求,請(qǐng)求調(diào)度器立即切換到較高優(yōu)先級(jí)的任務(wù)。
在 ISR 中使用 portBASE_TYPE 變量是為了確保中斷服務(wù)程序能夠安全地與任務(wù)調(diào)度器進(jìn)行交互,以避免數(shù)據(jù)競(jìng)爭(zhēng)和死鎖等問題。在 ISR 中聲明和初始化一個(gè)名為 xHigherPriorityTaskWoken 的變量,用于指示是否需要通知任務(wù)調(diào)度器進(jìn)行任務(wù)切換。如果變量被設(shè)置為 pdTRUE,則表明當(dāng)前中斷服務(wù)程序結(jié)束后需要調(diào)用 portEND_SWITCHING_ISR 函數(shù)以切換到更高優(yōu)先級(jí)的任務(wù);如果變量被設(shè)置為 pdFALSE,則表明系統(tǒng)無(wú)需進(jìn)行任務(wù)切換,可以繼續(xù)執(zhí)行當(dāng)前任務(wù)。
當(dāng)在 ISR 中完成隊(duì)列操作或其他可能導(dǎo)致任務(wù)切換的操作時(shí),應(yīng)該及時(shí)更新 xHigherPriorityTaskWoken 變量的值,以確保任務(wù)調(diào)度器能夠及時(shí)響應(yīng)任務(wù)切換請(qǐng)求。同時(shí),在結(jié)束 ISR 之前,也應(yīng)該調(diào)用 portEND_SWITCHING_ISR 函數(shù)來通知任務(wù)調(diào)度器進(jìn)行任務(wù)切換,以確保多任務(wù)處理的正確性和穩(wěn)定性。
2.
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t data = USART_ReceiveData(USART1);
xQueueSendFromISR(rx_queue, &data, &xHigherPriorityTaskWoken);
}
這段代碼是在 USART1 的中斷服務(wù)程序(IRQHandler)中處理 USART1 的接收中斷(USART_IT_RXNE)的。
當(dāng) USART1 接收到新的數(shù)據(jù)時(shí),會(huì)觸發(fā) USART1 的接收中斷。在中斷服務(wù)程序中,我們首先檢查 RXNE(RX not empty)標(biāo)志位是否被置位,如果被置位,則表明有新的數(shù)據(jù)已經(jīng)接收到了。接著,我們調(diào)用 USART_ReceiveData 函數(shù)從數(shù)據(jù)緩沖區(qū)寄存器中讀取一個(gè)字節(jié)的數(shù)據(jù),并將其放入接收隊(duì)列(rx_queue)中。
需要注意的是,在中斷服務(wù)程序中,我們需要使用 xQueueSendFromISR 函數(shù)來往隊(duì)列中發(fā)送數(shù)據(jù),而不能使用常規(guī)的 xQueueSend 函數(shù)。這是因?yàn)樵谥袛喾?wù)程序中調(diào)用隊(duì)列操作函數(shù)時(shí),需要確保線程安全性,以避免數(shù)據(jù)競(jìng)爭(zhēng)和死鎖等問題。同時(shí),我們還需聲明和初始化一個(gè)名為 xHigherPriorityTaskWoken 的變量,用來指示是否需要在中斷服務(wù)程序結(jié)束后調(diào)用 portEND_SWITCHING_ISR 函數(shù)以切換到更高優(yōu)先級(jí)的任務(wù)。
總之,這段代碼用于在 USART1 中斷服務(wù)程序中處理接收中斷,其中涉及到了隊(duì)列和線程安全相關(guān)的知識(shí)點(diǎn)。
3.
if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) {
uint8_t data;
if (xQueueReceiveFromISR(tx_queue, &data, &xHigherPriorityTaskWoken) == pdTRUE) {
USART_SendData(USART1, data);
} else {
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
}
這段代碼是在 USART1 的中斷服務(wù)程序(IRQHandler)中處理 USART1 的發(fā)送中斷(USART_IT_TXE)的。
當(dāng) USART1 發(fā)送數(shù)據(jù)緩沖區(qū)寄存器空(TXE=1)時(shí),會(huì)觸發(fā) USART1 的發(fā)送中斷。在中斷服務(wù)程序中,我們首先檢查 TXE 標(biāo)志位是否被設(shè)置,如果被設(shè)置,則表明數(shù)據(jù)緩沖區(qū)寄存器已經(jīng)準(zhǔn)備好接受新的數(shù)據(jù)進(jìn)行發(fā)送。
接著,我們嘗試從發(fā)送隊(duì)列(tx_queue)中取出一個(gè)字節(jié)的數(shù)據(jù),并使用 USART_SendData 函數(shù)將其發(fā)送出去。如果隊(duì)列中沒有數(shù)據(jù)需要發(fā)送,則需要禁用 USART1 的 TXE 中斷,以避免不必要的中斷響應(yīng)。
需要注意的是,在中斷服務(wù)程序中,我們需要使用 xQueueReceiveFromISR 函數(shù)來從隊(duì)列中獲取數(shù)據(jù),而不能使用常規(guī)的 xQueueReceive 函數(shù)。這是因?yàn)樵谥袛喾?wù)程序中調(diào)用隊(duì)列操作函數(shù)時(shí),需要確保線程安全性,以避免數(shù)據(jù)競(jìng)爭(zhēng)和死鎖等問題。同時(shí),我們還需聲明和初始化一個(gè)名為 xHigherPriorityTaskWoken 的變量,用來指示是否需要在中斷服務(wù)程序結(jié)束后調(diào)用 portEND_SWITCHING_ISR 函數(shù)以切換到更高優(yōu)先級(jí)的任務(wù)。
總之,這段代碼用于在 USART1 中斷服務(wù)程序中處理發(fā)送中斷,其中涉及到了隊(duì)列和線程安全相關(guān)的知識(shí)點(diǎn)。
4.
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR 是 FreeRTOS 提供的一個(gè)函數(shù),用于結(jié)束 ISR 并檢測(cè)是否需要任務(wù)切換。在中斷服務(wù)程序結(jié)束時(shí),我們需要根據(jù)變量 xHigherPriorityTaskWoken 的值來決定是否進(jìn)行任務(wù)切換。
如果 xHigherPriorityTaskWoken 被設(shè)置為 pdTRUE,則表明當(dāng)前中斷服務(wù)程序結(jié)束后需要調(diào)用 portEND_SWITCHING_ISR 函數(shù)以切換到更高優(yōu)先級(jí)的任務(wù);如果 xHigherPriorityTaskWoken 被設(shè)置為 pdFALSE,則表明系統(tǒng)無(wú)需進(jìn)行任務(wù)切換,可以繼續(xù)執(zhí)行當(dāng)前任務(wù)。
當(dāng)需要進(jìn)行任務(wù)切換時(shí),我們調(diào)用 portEND_SWITCHING_ISR 函數(shù)將控制權(quán)交回給任務(wù)調(diào)度器,并讓調(diào)度器立即執(zhí)行高優(yōu)先級(jí)任務(wù)。具體來說,portEND_SWITCHING_ISR 函數(shù)會(huì)使用 pendSV 任務(wù)向處理器發(fā)送一個(gè)軟件中斷信號(hào),觸發(fā)任務(wù)切換流程。在此過程中,調(diào)度器會(huì)檢查所有任務(wù)的狀態(tài),并根據(jù)任務(wù)的優(yōu)先級(jí)、時(shí)間片和阻塞狀態(tài)等因素來決定下一次執(zhí)行哪個(gè)任務(wù)。文章來源:http://www.zghlxwxcb.cn/news/detail-605266.html
總之,portEND_SWITCHING_ISR 函數(shù)是在中斷服務(wù)程序中用于結(jié)束 ISR 并檢測(cè)是否需要任務(wù)切換的重要函數(shù)。它可以確保所有任務(wù)得到合理的調(diào)度和執(zhí)行,從而實(shí)現(xiàn)高效、穩(wěn)定和可靠的多任務(wù)處理。文章來源地址http://www.zghlxwxcb.cn/news/detail-605266.html
到了這里,關(guān)于STM32F103RCT6 -- 基于FreeRTOS 的USART1 串口通訊的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!