STM32 FreeRTOS 任务创建和删除实验(动态方法)

目录

实验目标

 CubeMX环境准备

SysMode配置

RCC配置 

​编辑LED1引脚配置 

LED2引脚配置

KEY1引脚配置

串口USART1配置

NVIC配置

 项目管理

代码生成配置

生成代码

Keil配置

打开项目:

配置使用微库

配置每次烧录后“复位并运行”

FreeRTOS移植

移植配置完成后的包含目录如下

工程对象窗口如下

编译

创建外设及测试程序文件

在项目根目录下创建Int目录

在Int目录下创建Key.c、Key.h、Led.c、Led.h四个文件

在项目根目录下的Core/Inc目录下创建FreeRTOS_demo.h

在Core/Src目录下创建FreeRTOS_demo.c

在Keil中将Int添加为头文件包含目录

在项目对象中添加Int组 ,并在该组下添加Key.c和Led.c

在Application/User/Core组下添加FreeRTOS_demo.c

项目导入Keil Assistant

通过Code打开工作目录

选择VSCode配置文件

确保该配置下包含Keil Assistant插件。

 切换后可以看到KEIL UVISION PROJECT,如下

导入项目

自动导入失败时手动导入

单击导入项目按钮

在弹出的窗口中选择打开项目文件即可

此时会提示是否切换工作区,选择OK即可

展开项目对象列表

在项目中显示头文件

在FreeRTOS_demo.c中写入以下内容

Key.c写入内容

Led.c写入内容

重新编译

展开源文件可以看到引用的头文件列表

处理“without a newline”警告

重定向printf

usart.h代码清单

usart.c代码清单

外设文件代码清单

Key.h

key.c

Led.h

Led.c

FreeRTOSConfig.h代码清单

FreeRTOS_demo.h代码清单

FreeRTOS_demo.c代码清单

引入头文件

任务设置

入口函数

启动任务函数

task1函数

task2函数

task3函数

main.c代码清单

引入头文件

调用入口函数

测试

编译并烧录后,可以看到LED1和LED2同时闪烁

打开串口工具可以看到以下内容

运行日志

按下KEY1,可以看到LED1不再闪烁

此外,task1不再输出日志

代码逻辑


动态创建,堆栈是在FreeRTOS管理的堆内存里,注意任务不要重复创建。

xxxxx_STACK_SIZE 128

uxTaskGetStackHighWaterMark()获取指定任务的任务栈的历史剩余最小值,根据这个结果适当调整启动任务的大小。

实验目标

学会 xTaskCreate( ) 和 vTaskDelete( ) 的使用:

  • start_task:用来创建其他的三个任务。
  • task1:实现LED1每500ms闪烁一次。
  • task2:实现LED2每500ms闪烁一次。
  • task3:判断按键KEY1是否按下,按下则删掉task1。

 CubeMX环境准备

SysMode配置

HAL和FreeRTOS都依赖SysTick,保险起见,所有项目都将HAL库的时钟源替换为TIM7。 

RCC配置 

LED1引脚配置 

LED2引脚配置

KEY1引脚配置

串口USART1配置

NVIC配置

将TIM7调整为HAL时钟源后,其中断默认开启,且不能关闭,为了避免使用HAL库时钟源时被FreeRTOS调度中断导致卡死,TIM7的中断优先级配置为1,下文同理。

 项目管理

代码生成配置

生成代码

Keil配置

打开项目:

生成代码后,在弹出的串口选择“Open Project”,用Keil打开项目

或者点击“Open Folder”打开目录

 

也可以直接在文件资源管理器中找到CubeMX中指定的项目文件路径。

双击MDK-ARM目录下后缀为.uvprojx的文件即可在Keil中打开生成的项目

配置使用微库

我们要重定向printf(),需要调用微库实现。

配置每次烧录后“复位并运行”

打开Target选项窗口

打开ST-Link Debugger的配置

配置每次烧录后“复位并运行”

确定

 OK

FreeRTOS移植

按照STM32 FreeRTOS移植-CSDN博客

配置即可,需要注意的是,最后一步建议将时钟源由SysTick替换为其它定时器,此处可以不做。

移植配置完成后的包含目录如下

工程对象窗口如下

编译

编译后出现如下结果则移植成功

创建外设及测试程序文件

在项目根目录下创建Int目录

在Int目录下创建Key.c、Key.h、Led.c、Led.h四个文件

在项目根目录下的Core/Inc目录下创建FreeRTOS_demo.h

在Core/Src目录下创建FreeRTOS_demo.c

在Keil中将Int添加为头文件包含目录

确认即可

在项目对象中添加Int组 ,并在该组下添加Key.c和Led.c

确认即可

在Application/User/Core组下添加FreeRTOS_demo.c

项目导入Keil Assistant

通过Code打开工作目录

进入项目根目录下的MDK-ARM目录,右键空白处

选择VSCode配置文件

确保该配置下包含Keil Assistant插件。

 切换后可以看到KEIL UVISION PROJECT,如下

导入项目

切换配置文件后稍待片刻点击KEIL UVISION PROJECT标签即可看到以下内容

 如果项目没有导入,可以多次点击KEIL UVISION PROJECT标签,若此法不奏效,可以手动导入,步骤如下。

自动导入失败时手动导入
单击导入项目按钮

在弹出的窗口中选择打开项目文件即可

此时会提示是否切换工作区,选择OK即可

展开项目对象列表

有时某些项目对象未被加载,如Int/,此时可以在Keil中重新编译项目即可加载。

在项目中显示头文件

在FreeRTOS_demo.c中写入以下内容
#include "FreeRTOS_demo.h"
Key.c写入内容
#include "Key.h"
Led.c写入内容
#include "LED.h"
重新编译

展开源文件可以看到引用的头文件列表

Led.h和Key.h同理。

处理“without a newline”警告

上述警告是因为编译器要求每行以\n结尾,因此,文件末尾要有空行

添加空行并重新编译,即可消除警告。

重定向printf

usart.h代码清单
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

要注意,用户自定义的头文件应该在USER CODE BEGIN Includes注释标签对之间,这样在CubeMX重新生成代码时,这部分内容才不会被清除。

usart.c代码清单
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f) {
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
  return 0;
}
/* USER CODE END 0 */

同理,重写的fputc()函数代码应置于USER CODE BEGIN标签之间。

外设文件代码清单

Key.h
#ifndef __KEY_H
#define __KEY_H

#include "main.h"

#define KEY1        HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)   
/* 读取KEY1引脚状态(上拉输入) */

#define KEY1_PRESS    1              

uint8_t Key_Detect(void);

#endif
key.c
#include "Key.h"

/**
 * @description: 检测按键
 * @return {*} 按下的按键值
 */
uint8_t Key_Detect(void)
{
    uint8_t res = 0;
    if (KEY1 == GPIO_PIN_RESET)  
    {
        HAL_Delay(10);           /* 去抖动 */
        /* 按这个顺序,如果多个按键同时按,优先级:KEY4>KEY3>KEY2>KEY1 */
        if (KEY1 == GPIO_PIN_RESET)  res = KEY1_PRESS;
    }
    return res;
}
Led.h
#ifndef __LED_H
#define __LED_H

#include "gpio.h"

#define LED uint16_t
void LED_Turn_On(LED led);
void LED_Turn_Off(LED led);
void LED_Toggle(LED led);

void LED_Turn_Off_All(LED led[], uint8_t len);

#endif
Led.c
#include "Led.h"

/**
 * @description: 点亮LED
 * @param {LED} led
 */
void LED_Turn_On(LED led)
{
    HAL_GPIO_WritePin(GPIOA, led, GPIO_PIN_RESET);
}

/**
 * @description: 熄灭LED
 * @param {LED} led
 */
void LED_Turn_Off(LED led)
{
    HAL_GPIO_WritePin(GPIOA, led, GPIO_PIN_SET);
}

/**
 * @description: 翻转LED的状态
 * @param {LED} led
 */
void LED_Toggle(LED led)
{
    HAL_GPIO_TogglePin(GPIOA, led);
}

/**
 * @description: 关闭所有LED
 * @param {LED} led
 * @param {uint8_t} len
 * @return {*}
 */
void LED_Turn_Off_All(LED led[], uint8_t len)
{
    uint8_t i;
    for (i = 0; i < len; i++)
    {
        LED_Turn_Off(led[i]);
    }
}

FreeRTOSConfig.h代码清单

#define configSUPPORT_DYNAMIC_ALLOCATION                1

实际上,配置项configSUPPORT_DYNAMIC_ALLOCATION的默认值为1,上述代码可以省略。在FreeRTOS.h的959-962行有如下代码

#ifndef configSUPPORT_DYNAMIC_ALLOCATION
    /* Defaults to 1 for backward compatibility. */
    #define configSUPPORT_DYNAMIC_ALLOCATION    1
#endif

由此可知,configSUPPORT_DYNAMIC_ALLOCATION默认值为1。

FreeRTOS_demo.h代码清单

#ifndef __FREERTOS_DEMO_H__
#define __FREERTOS_DEMO_H__

void FreeRTOS_Start(void);

#endif

FreeRTOS_demo.c代码清单

引入头文件
#include "FreeRTOS_demo.h"
#include "FreeRTOS.h"
#include "task.h"
#include "Key.h"
#include "Led.h"
#include <stdio.h>
任务设置
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

/* Task2 任务 配置 */
#define TASK2_PRIORITY 3
#define TASK2_STACK_DEPTH 128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);

/* Task3 任务 配置 */
#define TASK3_PRIORITY 4
#define TASK3_STACK_DEPTH 128
TaskHandle_t task3_handler;
void Task3(void *pvParameters);
入口函数
/**
 * @description: FreeRTOS入口函数:创建任务函数并开始调度
 * @return {*}
 */
void FreeRTOS_Start(void)
{
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}
启动任务函数
void Start_Task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   Task1,
                (char *                 )   "Task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIORITY,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   Task2,
                (char *                 )   "Task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIORITY,
                (TaskHandle_t *         )   &task2_handler );
                
    xTaskCreate((TaskFunction_t         )   Task3,
                (char *                 )   "Task2",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIORITY,
                (TaskHandle_t *         )   &task3_handler );
    vTaskDelete(NULL);                  
    taskEXIT_CRITICAL();                /* 退出临界区 */
}
task1函数
/**
 * @description: LED1每500ms翻转一次
 * @param {void *} pvParameters
 * @return {*}
 */
void Task1(void * pvParameters)
{
    while(1)
    {
        printf("task1运行....\r\n");
        LED_Toggle(LED1_Pin);
        vTaskDelay(500);
    }
}
task2函数
/**
 * @description: LED2每500ms翻转一次
 * @param {void *} pvParameters
 * @return {*}
 */
void Task2(void * pvParameters)
{
    while(1)
    {
        printf("task2运行....\r\n");
        LED_Toggle(LED2_Pin);
        vTaskDelay(500);
    }
}
task3函数
/**
 * @description: 按下KEY1删除task1
 * @param {void *} pvParameters
 * @return {*}
 */
void Task3(void * pvParameters)
{
    uint8_t key = 0;
    while(1)
    {
        printf("task3正在运行...\r\n");
        key = Key_Detect();
        if(key == KEY1_PRESS)
        {
            if(task1_handler != NULL)
            {
                printf("删除task1任务...\r\n");
                vTaskDelete(task1_handler);
                task1_handler = NULL;
            }
        }
        vTaskDelay(10);
    }

}

main.c代码清单

引入头文件
/* USER CODE BEGIN Includes */
#include "FreeRTOS_demo.h"
/* USER CODE END Includes */
调用入口函数
  /* USER CODE BEGIN 2 */
  FreeRTOS_Start();
  /* USER CODE END 2 */

测试

编译并烧录后,可以看到LED1和LED2同时闪烁
打开串口工具可以看到以下内容

运行日志

task1和task2约半秒执行一次,task3约10ms执行一次,每隔半秒可以看到task2和task1运行日志,如下。

按下KEY1,可以看到LED1不再闪烁
此外,task1不再输出日志

代码逻辑

主体代码逻辑,首先启动任务task---创建任务开始调度---创建task1会抢占task,执行task1---在task1里进行阻塞,在阻塞过程会让出cpu---task继续执行---创建task2---task2抢占task---task2也有延迟---task继续执行---创建task3---task3同样有延迟---task继续执行---删除task---task3因为延迟低优先级高会先刷屏---然后等task1和task2阻塞结束后,根据优先级task2比task1高先执行task2---再执行task1---按下按键---删除task1---只剩下task2和task3运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/955259.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Docker】——安装Docker以及解决常见报错

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

Ubuntu22.04安装paddle GPU版本

文章目录 确立版本安装CUDA与CUDNN安装paddle 确立版本 查看官网信息&#xff0c;确立服务版本&#xff1a;https://www.paddlepaddle.org.cn/documentation/docs/zh/2.6/install/pip/linux-pip.html 安装CUDA与CUDNN 通过nvidia-smi查看当前显卡驱动版本&#xff1a; 通过…

【MySQL 的数据目录】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、MySQL 的数据目录 1、数据库文件的存放路径2、相关命令目录3、配置文件目录 二、数据库和文件系统的关系 1、查看默认数据库2、数据库在文件系统中的表示3、表…

【安卓开发】【Android】总结:安卓技能树

【持续更新】 对笔者在安卓开发的实践中认为必要的知识点和遇到的问题进行总结。 一、基础知识部分 1、Android Studio软件使用 软件界面 最新的版本是瓢虫&#xff08;Ladybug&#xff09;&#xff0c;bug的确挺多。笔者更习惯使用电鳗&#xff08;Electric Eel&#xff0…

openharmony电源管理子系统

电源管理子系统 简介目录使用说明相关仓 简介 电源管理子系统提供如下功能&#xff1a; 重启服务&#xff1a;系统重启和下电。系统电源管理服务&#xff1a;系统电源状态管理和休眠运行锁管理。显示相关的能耗调节&#xff1a;包括根据环境光调节背光亮度&#xff0c;和根…

SQL Server 导入Excel数据

1、选中指定要导入到哪个数据库&#xff0c;右键选择 》任务 》导入数据 2、数据源 选择Excel&#xff0c;点击 下一步(Next) 3、目前 选择OLE DB Provider &#xff0c;点击 下一步&#xff08;Next&#xff09; 4、默认 &#xff0c;点击 下一步&#xff08;Next&#xff09;…

MySQL安装-Windows

目录 官网下载地址 下载MySQL安装包或者压缩包 安装 / 添加Path环境变量 初始化mysql 无密码初始化 随机密码初始化 注册MySQL服务 / 移除已经注册过的服务 启动MySQL服务 登录mysql前修改密码 登录mysql 登录mysql后修改密码 停止MqSQL服务 企业中使用的场景 官网下…

OSPF的LSA的学习研究

OSPF常见1、2、3、4、5、7类LSA的研究 1、拓扑如图&#xff0c;按照地址表配置&#xff0c;激活OSPF划分相关区域并宣告相关网段 2、1类LSA&#xff0c;每台运行了OSPF的路由器都会产生&#xff0c;描述了路由器的直连接口状况和cost 可以看到R1产生了一条router lsa&#xff0…

ZNS SSD垃圾回收优化方案解读-1

本文解读的论文《Optimizing Garbage Collection for ZNS SSDs via In-storage Data Migration and Address Remapping》是由重庆大学相关研究团队撰写&#xff0c;发表于2024年11月。本文小编将结合论文内容进行学习解读&#xff0c;以供各位读者参考&#xff01;由于水平有限…

生产管理看板助力节能科技公司实现数据自动化管理

在节能科技公司的生产过程中&#xff0c;数据管理的自动化是提高生产效率和产品质量的关键。然而&#xff0c;许多公司在数据记录、展示、对比和存档方面仍面临诸多痛点&#xff0c;如产品检测数据无法自动记录、缺乏直观的产线状态展示、检测数据对比繁琐耗时&#xff0c;以及…

JavaScript笔记基础篇03——函数

黑马程序员视频地址&#xff1a;黑马程序员前端JavaScript入门到精通全套视频教程https://www.bilibili.com/video/BV1Y84y1L7Nn?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes 目录 函数 函数的使用 1.函数的声明语法 2.函数的…

在PyCharm中使用Anaconda中的虚拟环境

1、在File菜单中找到Settings 2、Settings中搜索interpreter&#xff0c;找到Python Interpreter&#xff0c;再点击Add 3、选择第一个local interpreter 4、如图&#xff1a; 5、找到anaconda安装位置中的envs文件夹&#xff0c;在里面选择需要添加的python环境&#xff0c;如…

Windows系统安装 Rust 及其配置

1、在Rust官网下载Rust安装程序 &#xff08;1&#xff09;官网链接&#xff1a;Rust中文官网 &#xff08;2&#xff09;下载链接&#xff1a;下载Rust安装程序 &#xff08;3&#xff09;下载好后的rust安装程序&#xff0c;如下图&#xff1a; 2、运行 rustup-init.exe 安…

Python大数据可视化:基于Python对B站热门视频的数据分析与研究_flask+hive+spider

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 排行榜界面 系统管理界面 看板展示 摘要 本项目以对B站热…

MySQL 安装配置(完整教程)

文章目录 一、MySQL 简介二、下载 MySQL三、安装 MySQL四、配置环境变量五、配置 MySQL 5.1 初始化 MySQL5.2 启动 MySQL 服务 六、修改 MySQL 密码七、卸载 MySQL八、结语 一、MySQL 简介 MySQL 是一款广泛使用的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&am…

Navicat Premium 原生支持阿里云 PolarDB 数据库

近日&#xff0c;我司旗下的 Navicat Premium 软件通过了阿里云 PolarDB 数据库产品生态集成认证&#xff0c;这标志着 Navicat 通过原生技术全面实现了对秒级弹性、高性价比、稳定可靠的PolarDB 数据库三大引擎&#xff08;PolarDB MySQL版、PolarDB PostgreSQL版和 PolarDB f…

基于SpringBoot的装修公司管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

cuda从零开始手搓PB神经网络

cuda实现PB神经网络 基于上一篇的矩阵点乘&#xff0c;实现了矩阵的加减乘除、函数调用等。并且复用之前元编程里面写的梯度下降、Adam、NAdam优化方法。实现PB神经网络如下&#xff1a; #ifndef __BP_NETWORK_HPP__ #define __BP_NETWORK_HPP__ #include "matrix.hpp&quo…

Django多线程爬虫:突破数据抓取瓶颈

Django框架以其高效、安全、可扩展性强等特点&#xff0c;在Web开发领域得到了广泛应用。同时&#xff0c;Python语言的多线程支持和丰富的库也为开发多线程爬虫提供了便利。将Django与多线程技术相结合&#xff0c;不仅可以利用Django的强大功能进行项目管理和数据存储&#x…

RabbitMQ前置概念

文章目录 1.AMQP协议是什么&#xff1f;2.rabbitmq端口介绍3.消息队列的作用和使用场景4.rabbitmq工作原理5.整体架构核心概念6.使用7.消费者消息推送限制&#xff08;work模型&#xff09;8.fanout交换机9.Direct交换机10.Topic交换机&#xff08;推荐&#xff09;11.声明队列…