当前位置: 首页 > news >正文

STM32F103RCT6 -- 基于FreeRTOS 的USART1 串口通讯

1. 在STM32F103RCT6 单片机上跑FreeRTOS 实时操作系统,使用串口USART1 通讯,发送 – 接收数据,实现上位机与下位机的通信

使用 FreeRTOS 提供的队列(Queue)机制来实现数据的接收和发送

2. USART1 配置:

TX - PA9
RX - PA10
波特率:9600
数据位:8bit
校验位:无
停止位:1bit

数据格式:
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 - 数据字节数,不包括帧头,不包括校验位
00 06 – 模块
31 02 24-- 数据方向:从上位机(安卓LCD显示屏)到下位机(STM32)
32 01 24-- 数据方向:从下位机(STM32) 到上位机(安卓LCD显示屏)
01 - payload 要发送的数据具体内容
FC 80 / B8 70 – CRC 16bit 校验方法计算出来的,用06 00 06 31 02 24 01 使用CRC计算器可以计算出来 FC 80

CRC在线计算网址:

http://www.ip33.com/crc.html

3. 实现代码:

#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     15static 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 clocksRCC_APB2PeriphClockCmd(USART1_CLK | USART1_GPIO_CLK, ENABLE);// Configure USART1 pinsGPIO_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 USART1USART_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 interruptsUSART_ITConfig(USART1, USART_IT_RXNE, ENABLE);// Enable USART1USART_Cmd(USART1, ENABLE);// Initialize queuesrx_queue = xQueueCreate(RX_BUF_SIZE, sizeof(uint8_t));tx_queue = xQueueCreate(TX_BUF_SIZE, sizeof(uint8_t));// Create task for handling USART1 dataxTaskCreate(USART1_Task, "USART1 Task", 1024, NULL, 1, &task_handle);
}void USART1_Task(void *pvParameters) {while (1) {// Wait for data to be receiveduint8_t data;while (xQueueReceive(rx_queue, &data, portMAX_DELAY) == pdFALSE);// Process received data here// ...// Send response datauint8_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 USART1USART1_Init();// Start the schedulervTaskStartScheduler();
}

在代码中,首先使用 USART1_Init 函数初始化 USART1,并使用 FreeRTOS 提供的 xQueueCreate 函数创建两个队列,一个用于接收数据,一个用于发送数据。然后使用 xTaskCreate 函数创建一个任务(USART1_Task),用于处理 USART1 数据。在任务中,使用 xQueueReceive 函数不断等待接收数据,并使用 xQueueSend 函数发送响应数据。在 USART1_IRQHandler 中,使用 xQueueSendFromISR 和 xQueueReceiveFromISR 函数将接收到的数据和需要发送的数据加入相应的队列中,并启用或禁用 USART1 的 TXE 中断来控制数据的发送。

需要注意的是,在任务和中断中使用的队列必须定义为全局变量,以避免在栈上分配空间时出现问题。另外,由于 FreeRTOS 使用了抢占式调度方式,因此在任务和中断中使用的队列必须具有线程安全性,否则可能会导致数据丢失或者死锁等问题。

其中:

1.

portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

portBASE_TYPE 是 FreeRTOS 中定义的一个数据类型,用于表示任务调度器是否需要进行任务切换。在 FreeRTOS 中,任务调度器采用抢占式策略来决定下一次执行哪个任务。当某个任务需要让出 CPU 时间时,会向任务调度器发送一个任务切换请求,请求调度器立即切换到较高优先级的任务。

在 ISR 中使用 portBASE_TYPE 变量是为了确保中断服务程序能够安全地与任务调度器进行交互,以避免数据竞争和死锁等问题。在 ISR 中声明和初始化一个名为 xHigherPriorityTaskWoken 的变量,用于指示是否需要通知任务调度器进行任务切换。如果变量被设置为 pdTRUE,则表明当前中断服务程序结束后需要调用 portEND_SWITCHING_ISR 函数以切换到更高优先级的任务;如果变量被设置为 pdFALSE,则表明系统无需进行任务切换,可以继续执行当前任务。

当在 ISR 中完成队列操作或其他可能导致任务切换的操作时,应该及时更新 xHigherPriorityTaskWoken 变量的值,以确保任务调度器能够及时响应任务切换请求。同时,在结束 ISR 之前,也应该调用 portEND_SWITCHING_ISR 函数来通知任务调度器进行任务切换,以确保多任务处理的正确性和稳定性。

2.

 if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {uint8_t data = USART_ReceiveData(USART1);xQueueSendFromISR(rx_queue, &data, &xHigherPriorityTaskWoken);}

这段代码是在 USART1 的中断服务程序(IRQHandler)中处理 USART1 的接收中断(USART_IT_RXNE)的。

当 USART1 接收到新的数据时,会触发 USART1 的接收中断。在中断服务程序中,我们首先检查 RXNE(RX not empty)标志位是否被置位,如果被置位,则表明有新的数据已经接收到了。接着,我们调用 USART_ReceiveData 函数从数据缓冲区寄存器中读取一个字节的数据,并将其放入接收队列(rx_queue)中。

需要注意的是,在中断服务程序中,我们需要使用 xQueueSendFromISR 函数来往队列中发送数据,而不能使用常规的 xQueueSend 函数。这是因为在中断服务程序中调用队列操作函数时,需要确保线程安全性,以避免数据竞争和死锁等问题。同时,我们还需声明和初始化一个名为 xHigherPriorityTaskWoken 的变量,用来指示是否需要在中断服务程序结束后调用 portEND_SWITCHING_ISR 函数以切换到更高优先级的任务。

总之,这段代码用于在 USART1 中断服务程序中处理接收中断,其中涉及到了队列和线程安全相关的知识点。

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 的中断服务程序(IRQHandler)中处理 USART1 的发送中断(USART_IT_TXE)的。

当 USART1 发送数据缓冲区寄存器空(TXE=1)时,会触发 USART1 的发送中断。在中断服务程序中,我们首先检查 TXE 标志位是否被设置,如果被设置,则表明数据缓冲区寄存器已经准备好接受新的数据进行发送。

接着,我们尝试从发送队列(tx_queue)中取出一个字节的数据,并使用 USART_SendData 函数将其发送出去。如果队列中没有数据需要发送,则需要禁用 USART1 的 TXE 中断,以避免不必要的中断响应。

需要注意的是,在中断服务程序中,我们需要使用 xQueueReceiveFromISR 函数来从队列中获取数据,而不能使用常规的 xQueueReceive 函数。这是因为在中断服务程序中调用队列操作函数时,需要确保线程安全性,以避免数据竞争和死锁等问题。同时,我们还需声明和初始化一个名为 xHigherPriorityTaskWoken 的变量,用来指示是否需要在中断服务程序结束后调用 portEND_SWITCHING_ISR 函数以切换到更高优先级的任务。

总之,这段代码用于在 USART1 中断服务程序中处理发送中断,其中涉及到了队列和线程安全相关的知识点。

4.

portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

portEND_SWITCHING_ISR 是 FreeRTOS 提供的一个函数,用于结束 ISR 并检测是否需要任务切换。在中断服务程序结束时,我们需要根据变量 xHigherPriorityTaskWoken 的值来决定是否进行任务切换。

如果 xHigherPriorityTaskWoken 被设置为 pdTRUE,则表明当前中断服务程序结束后需要调用 portEND_SWITCHING_ISR 函数以切换到更高优先级的任务;如果 xHigherPriorityTaskWoken 被设置为 pdFALSE,则表明系统无需进行任务切换,可以继续执行当前任务。

当需要进行任务切换时,我们调用 portEND_SWITCHING_ISR 函数将控制权交回给任务调度器,并让调度器立即执行高优先级任务。具体来说,portEND_SWITCHING_ISR 函数会使用 pendSV 任务向处理器发送一个软件中断信号,触发任务切换流程。在此过程中,调度器会检查所有任务的状态,并根据任务的优先级、时间片和阻塞状态等因素来决定下一次执行哪个任务。

总之,portEND_SWITCHING_ISR 函数是在中断服务程序中用于结束 ISR 并检测是否需要任务切换的重要函数。它可以确保所有任务得到合理的调度和执行,从而实现高效、稳定和可靠的多任务处理。

http://www.lryc.cn/news/91143.html

相关文章:

  • 区间预测 | MATLAB实现基于QRCNN-LSTM-Multihead-Attention多头注意力卷积长短期记忆神经网络多变量时间序列区间预测
  • 递归--打印一个字符串的全部排列(java)
  • 【001 设备驱动】主设备号和次设备号的用途
  • 移动端PDF在线预览
  • 虚拟机两次寻址
  • DRF之JWT认证
  • 华为OD机试真题 Java 实现【放苹果】【2022Q4 100分】
  • 拼多多继续ALL IN
  • Unity的IPostprocessBuildWithReport:深入解析与实用案例
  • 九、Spring Cloud—gateway网关
  • ARM微架构与程序编写
  • Windows下利用Anaconda创建多个CUDA环境
  • C SS复习笔记
  • LeetCode 225 用队列实现栈
  • Java对象的共享
  • 漏洞概述-0day漏洞利用原理(0)
  • 交换机的4种网络结构方式:级联方式、堆叠方式、端口聚合方式、分层方式
  • firewall-cmd防火墙策略
  • 解决SQLException: Incorrect string value异常
  • 桂院校园导航 导入 与 配置教程
  • Linux上安装jdk Tomcat mysql redis
  • Postman中加url环境变量和token全局变量
  • 多线程事务回滚方法
  • java单元测试( Hamcrest 断言)
  • 讨论和总结 树模型 的三种序列化 方式的区别(模型存储大小、序列化所用内存、序列化速度)...
  • Halcon中的一些3D算子
  • Android:Selector + Layer-lists 实现 AppCompatCheckBox
  • TreeMap类型添加数据
  • iOS 16 UI 设计系统免费在线使用方法
  • 【接口测试】JMeter接口关联测试