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

这篇具有很好参考价值的文章主要介绍了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     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 函数初始化 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 并检测是否需要任务切换的重要函数。它可以确保所有任务得到合理的调度和执行,从而实现高效、稳定和可靠的多任务处理。文章来源地址https://www.toymoban.com/news/detail-605266.html

到了这里,关于STM32F103RCT6 -- 基于FreeRTOS 的USART1 串口通讯的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于STM32F103RCT6之手把手教你写智能家居项目(2)

            上一节我们简述了智能家居项目,实现了点灯的相关代码编写,还有WIFI模块的固件烧录。 连接什么平台:         我们想要远程控制家具的开关和获取家中的状态,少不了一个可以传输数据的云平台。我认为易监控是一个简单好用的云平台。 怎么连接平台:

    2024年02月20日
    浏览(163)
  • stm32f103rct6引脚功能表格

    脚号 引脚名称 主功能 默认复用 重定义 备注 1             VBAT VBAT -- -- 说明1 2             PC13-TAMPER-RTC PC13 TAMPER-RTC -- 说明3 3             PC14-OSC32_IN PC14 OSC32_IN -- 说明3 4             PC15-OSC32_OUT PC15 OSC32_OUT -- 说明3 5             OSC_IN OSC_IN -- CAN_RX 晶振

    2024年02月06日
    浏览(48)
  • STM32F103RCT6——定时器简单用法

    STM32F10xx参考手册英文和中文版 百度网盘:https://pan.baidu.com/s/1Z2nB0WVJIxvm3VOI9MQiiw 提取码:lxlx STM32F103RCT6数据手册 链接:https://pan.baidu.com/s/1tRchgf-5C1MN4W58vQ9zPg 提取码:lxlx STM32F103RCT6包括,高级控制定时器TIM1和TIM8,通用定时器TIM2-5,基本定时器TIM6和TIM7. 高级控制定时器TIM1,TI

    2024年02月08日
    浏览(62)
  • 从零开始制作STM32F103RCT6小车(一)

            仅以此系列给实验室的学弟学妹作为小车制作教程来使用,后续的内容我会在这个暑假陆续更新出来,本篇的内容是新建一个适用于STM32F103RCT6的工程         接下来的操作几乎是基于STM32F1xx系列的固件库,这里我给大家列出链接 STM32F1xx系列固件库               

    2023年04月08日
    浏览(63)
  • stm32f103rct6使用内部晶振作为时钟源

    stm32f103rct6(库函数版例程)使用内部晶振8M,倍频64M 参考 https://blog.csdn.net/oHuanCheng/article/details/105112884 http://www.openedv.com/forum.php?mod=viewthreadtid=286233 https://blog.csdn.net/ll148305879/article/details/106138302 修改system_stm32f10x.c文件中的void SystemInit(void)函数 内部时钟用到了FLASH,需要添加s

    2024年02月07日
    浏览(186)
  • stm32F103RCT6使用FFT运算分析波形详解(细致教学)

    最近学校电赛队伍招新,出的招新题就是低频示波器的。之前一直没有弄懂FFT,借着这次机会实现了一下,做了一个小示波器 FFT原理简述 FFT,就是快速傅里叶变换,这个操作能够将时域信号转化成频域信号,然后对信号进行分析 这样说可能有点抽象。讲细点就是指能够直观

    2024年02月14日
    浏览(52)
  • STM32F103RCT6驱动SG90舵机-完成正反转角度控制

    SG90是一种微型舵机,也被称为伺服电机。它是一种小型、低成本的直流电机,通常用于模型和机器人控制等应用中。SG90舵机可以通过电子信号来控制其精确的位置和速度。它具有体积小、重量轻、响应快等特点,因此在各种小型机械设备上得到了广泛应用。 SG90舵机通常用于

    2024年02月03日
    浏览(66)
  • STM32F103RCT6电路设计及绘制方法和学习体会

    目录 1电路图各部分原理分析 1.1通信-下载板块接口电路 1.1.1 通信模块 1.1.2 显示灯 1.2电源转化电路 1.2.1 电源转化 1.3晶振电路 1.4 OLED显示屏接口电路 1.5 独立按键电路 1.6 JTAG/SWD调试接口电路 1.7 STM32微控制器电路 1.7.1 副芯片原理 1.7.2 主芯片原理 1.8 外扩引脚 1.9 显示灯 2图纸的

    2024年02月14日
    浏览(62)
  • 设计分享 | STM32F103RCT6利用ULN2003驱动步进电机正反转

    https://mp.weixin.qq.com/s?__biz=Mzg4Mzc3NDUxOQ==mid=2247484170idx=1sn=3fa68d0fbf30cf614e8779abf38c3e2bchksm=cf430652f8348f44e4b95bb7b22ce7f9f5a86e13f421741aefd08cedb169708309c79566ab1ftoken=1923877603lang=zh_CN#rd https://mp.weixin.qq.com/s?__biz=Mzg4Mzc3NDUxOQ==mid=2247484170idx=1sn=3fa68d0fbf30cf614e8779abf38c3e2bchksm=cf430652f8348f44e4b95bb7b22ce7f9f5a8

    2023年04月17日
    浏览(58)
  • STM32F103RCT6开发板M3单片机教程06--定时器中断

    除非特别说明,本章节描述的模块应用于整个 STM32F103xx 微控制器系列,因为我们使用是 STM32F103RCT6开发板是mini最小系统板。 本教程使用是( 光明谷SUN_STM32mini开发板 )   首先了解一下是STM32F10X定时器(Timer)   注: 小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F101

    2024年02月04日
    浏览(67)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包