【FreeRTOS】使用CubeMX快速移植FreeRTOS工程到蓝桥杯开发板(STM32G431RBT6)

使用CubeMX快速创建FreeRTOS工程到蓝桥杯开发板(STM32G431RBT6)

  • CubeMX配置
    • CubeMX基础工程的配置
    • ☆FreeRTOS相关配置
    • FreeRTOS配置选项卡的解释
  • 软件工程架构与程序设计
  • 小综合:☆任务的创建删除、挂起与恢复设计
    • cubexMX配置创建任务
    • 软件程序设计
    • 底层功能驱动函数实现

  随着CubeMX软件的不断推广使用,STM32HAL库的使用也不断增加,并且某些系列开发板只支持HAL开发。最近参加完蓝桥杯后,继续深入学习FreeRTOS操作系统,但在移植RTOS的时候,大多数教程都是基于正点原子F系列开发板根据固件库或者HAL例程进行移植,移植过程相对比较复杂,因此特此整理一篇直接通过CubeMX生成的FreeRTOS工程教程到蓝桥杯的STM32G431RB系列的开发板中,方便后续通过蓝桥杯开发板深入学习RTOS操作系统。

  • 对于手动移植过程复杂且繁琐,对新手不友好。如有需要手动移植,可参照以下文章:
    1. FreeRTOS移植到STM32
    2. FreeRTOS移植stm32详细步骤介绍

CubeMX配置

对于CubeMX创建基础工程这里仅进行简单概述,详细步骤可参考:

1. STM32 CubeMX新建工程+GPIO的研究

2. Cubemx新建工程引脚配置与点亮LED

CubeMX基础工程的配置

使能外部高速时钟:

配置LED与按键引脚

配置开启调试串口

工程相关配置


☆FreeRTOS相关配置

更改/配置时钟源:

  1. 在 SYS 选项里,将 Debug 设为 Serial Wire,并且将 Timebase Source 设为TIM6(其它定时器也行)。
  2. 裸机的时钟源默认是 SysTick,但是开启FreeRTOS后,FreeRTOS会占用SysTick(用来生成1ms 定时,用于任务调度),所以需要需要为其他总线提供另外的时钟源。

使能FREERTOS选项
将 Interface 配置为CMSIS_V1,V2的内核版本更高,功能更多,在大多数情况下V1版本的内核完全够用

创建第二个任务

至此,FreeRTOS多任务工程已配置完毕,可直接生成工程代码!!!


FreeRTOS配置选项卡的解释

FreeRTOS 各配置选项卡的解释

  • Events:事件相关的创建
  • Task and Queues: 任务与队列的创建
  • Timers and Semaphores: 定时器和信号量的创建
  • Mutexes: 互斥量的创建
  • FreeRTOS Heap Usage: 用于查看堆使用情况
  • config parameters: 内核参数设置,用户根据自己的实际应用来裁剪定制FreeRTOS 内核
  • Include parameters: FreeRTOS 部分函数的使能
  • User Constants: 相关宏的定义,可以自建一些常量在工程中使用
  • Advanced settings:高级设置

内核配置、函数使能的一些翻译

查看用户堆的使用情况

FreeRTOS部分函数使能配置

内核参数设置,用户可根据自己的实际应用来裁剪定制FreeRTOS 内核

内核参数的理解内容非常多,可以参考以下文章:FreeRTOS内核配置说明

软件工程架构与程序设计

软件工程架构

主函数中初始化RTOS并且开启内核任务调度:

在app_freertos.c中创建任务,并实现对任务的具体实现

程序设计
通过CubeMX生成的FreeRTOS工程创建了2个任务,通过程序设计实现对两个LED灯分别以不同的时间周期进行闪烁。

LED翻转功能函数

void led_toggle(uint8_t led)
{
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8<<(led-1));
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

任务的具体实现

//任务1控制LED1小灯每秒闪烁一次
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  for(;;)
  {
	led_toggle(1);
    osDelay(250);
  }
  /* USER CODE END StartDefaultTask */
}

//任务2控制LED2小灯每500ms闪烁一次
void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
	led_toggle(2);
    osDelay(500);
  }
  /* USER CODE END StartTask02 */
}

小综合:☆任务的创建删除、挂起与恢复设计

通过对FreeRTOS的理论基础知识及其系统的移植,通过设计按键控制任务的创建与删除,挂起与恢复巩固系统的移植与相关理论知识。

  • 在此之前需掌握RTOS任务的调度、任务的状态以及任务的创建与删除相关理论基础知识:RTOS理论基础知识快速入门
  • 同时需要掌握通过CubeMX移植FreeRTOS工程,因为该实验建立在上述移植的工程上进行修改

设计要求描述:
创建 4 个任务:taskLED1,taskLED2,taskKEY1,taskKEY2,任务要求如下:
taskLED1:间隔 500ms 闪烁 LED1;
taskLED2:间隔 1000ms 闪烁 LED2;
taskKEY1:如果 taskLED1 存在,则按下 KEY1 后删除 taskLED1 ,否则创建 taskLED1 ;
taskKEY2:如果 taskLED2 正常运行,则按下 KEY2 后挂起 taskLED2 ,否则恢复 taskLED2

cubexMX配置创建任务

  通过设计要求分析,在tasks选项中创建四个任务,分别为taskLED01,taskLED02,taskKEY01,以及taskKEY02,将任务优先级均设置为相同的Normal优先级,代码创建定义设置为若定义,方便后续用户在user.c内对函数进行重写,其中创建方式如下图所示。
注意:由于创建的任务过多,会使得堆空间不够,因此这里将Interface设置为CMSIS_V2版本。

软件程序设计

任务一和任务二分别控制LED小灯以不同的时间周期进行闪烁

void taskLED01(void * argument)
{
	uint task1_num = 0;
	while(1)
	{
		led_toggle(1);
		osDelay(250);
		task1_num++;
		printf("task1 num:%d\r\n",task1_num);
	}
}
void taskLED02(void * argument)
{
	uint task2_num = 0;
	while(1)
	{
		led_toggle(2);
		osDelay(500);
		task2_num++;
		printf("task2 num:%d\r\n",task2_num);
	}
}

按键一任务控制LED1任务的创建与删除

void taskKEY01(void * argument)
{
	while(1)
	{
		//按下
		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == 0)
		{
			//消抖
			osDelay(10);
			if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == 0)
			{
				//等待抬起
				while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == 0);
				printf("key1 按下\r\n");
				if(taskLED1Handle != NULL)
				{
					//删除任务1
					osThreadTerminate(taskLED1Handle);
					taskLED1Handle = NULL;
					led_show(1,0);
					printf("taskLED01 删除成功\r\n");
				}
				else
				{
					//创建任务
					taskLED1Handle = osThreadNew(taskLED01, NULL, &taskLED1_attributes);
					if(taskLED1Handle != NULL)
						printf("taskLED01 创建成功\r\n");
					else
						printf("taskLED01 创建失败\r\n");
				}
			}
		}
		osDelay(10);
	}
}

按键二任务控制LED2任务的挂起与恢复

void taskKEY02(void * argument)
{
	static uchar key2_flag = 0;
	while(1)
	{
		//按下
		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == 0)
		{
			//消抖
			osDelay(10);
			if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == 0)
			{
				//等待抬起
				while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == 0);
				printf("key2 按下\r\n");
				//挂起
				if(key2_flag == 0)
				{
					osThreadSuspend(taskLED2Handle);
					printf("任务2已挂起\r\n");
				}
				//恢复
				else
				{
					osThreadResume(taskLED2Handle);
					printf("任务2已恢复\r\n");
				}
				key2_flag = !key2_flag;
			}
		}
		osDelay(10);
	}
}

底层功能驱动函数实现

LED功能函数的实现

/*****************LED与按键的底层驱动函数*****************************/
/*
功能: LED翻转
参数: LED编号  1~8
*/
void led_toggle(uint8_t led)
{
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8<<(led-1));
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

/*
功能:  LED亮灭控制
参数1: LED编号  1~8
参数2: LED亮灭模式  1:亮   0:灭
*/
void led_show(uint8_t led, uchar mode)
{
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	if(mode)
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(led-1),GPIO_PIN_RESET);
	else
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(led-1),GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

/*
功能: LED亮灭控制
参数: 无
*/
void led_offAll(void)
{
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

串口重定向

#include "stdio.h"
int fputc(int ch, FILE *f)
{
	unsigned char temp[1] = {ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}

按键扫描功能函数的实现

/*
功能:   按键扫描
参数:   无
返回值: 对应按键按下宏
*/
//头文件宏定义
/* 读取KEY引脚 */
#define KEY1    HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)     	
#define KEY2	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)     
#define KEY3	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)     
#define KEY4	HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)    

 /* KEY按下 */
#define KEY1_PRES    1           
#define KEY2_PRES    2             
#define KEY3_PRES    3
#define KEY4_PRES    4

uint8_t key_scan(void)
{
	static uint8_t key_up = 1;  /* 按键按松开标志 */
	uint8_t keyval = 0;
	
	/* 按键松开标志为1, 且有任意一个按键按下了 */
	if (key_up && (KEY1 == 0 || KEY2 == 0 || KEY3 == 0 || KEY4 == 0))  
	{
		//消抖
		osDelay(10);
		key_up = 0;

		if (KEY1 == 0)  
			keyval = KEY1_PRES;
		else if (KEY2 == 0)  
			keyval = KEY2_PRES;
		else if (KEY3 == 0)  
			keyval = KEY3_PRES;
		else if (KEY4 == 0)  
			keyval = KEY4_PRES;
	}
	
	/* 没有任何按键按下, 标记按键松开 */
	else if (KEY1 == 1 && KEY2 == 1 && KEY3 == 1 && KEY4 == 1)         
	{
			key_up = 1;
	}
	return keyval;
}

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

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

相关文章

高频前端面试题汇总之JavaScript篇(上)

一、数据类型 1. JavaScript有哪些数据类型&#xff0c;它们的区别&#xff1f; JavaScript共有八种数据类型&#xff0c;分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。 其中 Symbol 和 BigInt 是ES6 中新增的数据类型&#xff1a; Symbol 代…

关于 Windows10 计算机丢失 MSVCP120.dll 的解决方法

今天学长跟平时一样打开电脑开始发布文章需要用到Adobe Photoshop CC 2018的时候居然给我来个Photoshop.exe-系统错误、无法启动此程序&#xff0c;因为计算机中丢失MSVCP120.dll 尝试重新安装该程序以解决此问题&#xff0c;安装上面的说明重新安装了我的Photoshop CC 打开还是…

关于CAS

什么是CAS: CAS:Compare And Swap&#xff0c;比较且交换。 CAS中有三个参数&#xff1a;1.内存中原数据的值V 2.预期值A 3.修改后的数据B Compare&#xff1a;V与A会先比较是否一样 Swap&#xff1a;如果V与A一致&#xff0c;那么就将B写入V 返回操作是否成功 伪代码&…

椋鸟数据结构笔记#10:排序·中

文章目录 四、归并排序时间复杂度实现递归实现非递归实现 测试稳定性 五、非比较排序5.1 计数排序时间复杂度实现测试局限性 5.2 桶排序时间复杂度实现测试 5.3 基数排序时间复杂度实现测试局限性 萌新的学习笔记&#xff0c;写错了恳请斧正。 四、归并排序 归并排序是一种非常…

微服务使用SockJs+Stomp实现Websocket 前后端实例 | Vuex形式断开重连、跨域等等问题踩坑(一)

大家好&#xff0c;我是程序员大猩猩。 之前几篇文章&#xff0c;我们讲了Spring Cloud Gateway的轻量级实现&#xff0c;Nginx的配置概念与实现&#xff0c;如以下往期文章。 轻量级的Spring Cloud Gateway实践&#xff0c;实现api和websocket转发轻松实现Nginx的HTTP与WebS…

新产品成功的七大关键要素:理论解析与案例探讨

在激烈的市场竞争中&#xff0c;新产品的成功推出不仅关乎企业的生死存亡&#xff0c;更是企业持续发展的核心动力。那么&#xff0c;新产品如何能够脱颖而出&#xff0c;赢得市场的青睐呢&#xff1f;本文将深入探讨新产品成功的七大关键要素&#xff0c;并结合实际案例进行解…

中颖51芯片学习8. ADC模数转换

中颖51芯片学习8. ADC模数转换 一、ADC工作原理简介1. 概念2. ADC实现方式3. 基准电压 二、中颖芯片ADC功能介绍1. 中颖芯片ADC特性2. ADC触发源&#xff08;1&#xff09;**软件触发**&#xff08;2&#xff09;**TIMER4定时器触发**&#xff08;3&#xff09;**外部中断2触发…

洛谷P1057 [NOIP2008 普及组] 传球游戏

#include<iostream> using namespace std; int n;// n个人传球游戏 默认开始球在编号为1的位置 int m;// 传递m次球 int main(){cin>>n>>m;// 动态转方程&#xff1a;// 球传递到编号为k人的手中// 种类总数 传递到k-1编号种类总数 传递到k1编号种类总数//…

如何查看微信公众号发布文章的主图,如何看微信文章的主图,怎么才能拿到主图

如何查看&#xff0c;微信公众号发布文章的主图&#xff0c;如何看微信文章的主图 起因是这样的&#xff0c;当我看到一篇文章的时候&#xff0c;他的主图很漂亮&#xff0c;但是&#xff0c;正文里没有&#xff0c;而我又想看到&#xff0c;并且使用这张图片&#xff0c;该怎么…

代码随想录训练营Day 27|Python|Leetcode|122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能获…

同旺科技 USB TO SPI / I2C适配器读写24LC256--页写

所需设备&#xff1a; 1、USB 转 SPI I2C 适配器&#xff1b;内附链接 2、24LC256芯片 适应于同旺科技 USB TO SPI / I2C适配器升级版、专业版&#xff1b; 从00地址开始写入64个字节&#xff0c;然后再将64个字节读回&#xff1b; 页写时序&#xff1a; 读时序&#xff1a…

easyx库的介绍

前言 如果想要摆脱黑窗口的限制那么easyx图形库是一个好的选择 easyx的初认识 easyx是针对c的图形库&#xff0c;可以帮助c/c上手图形和游戏编程 所以要用easyx必须要用.cpp的后缀 1 easyx的原理 window的图形编程&#xff0c;最终都由window的底层API来实现 2 easyx的颜色 …

【Java笔记】第4章:深入学习循环结构

前言1. 循环的理解2. while循环3. do...while循环4. for循环5. 循环的控制语句6. 循环的嵌套结语 ↓ 上期回顾: 【Java笔记】第3章&#xff1a;深入学习分支结构 个人主页&#xff1a;C_GUIQU 归属专栏&#xff1a;【Java学习】 ↑ 前言 各位小伙伴大家好&#xff01;上期小编…

Mac下删除旧版本.net sdk

参照微软官网给的方法,Releases dotnet/cli-lab (github.com) 好像不能直接的解决问题,我做一下补充,希望对需要删除旧版本sdk的小伙伴们有所帮助 1:下载工具包 Releases dotnet/cli-lab (github.com) 2:打开终端,cd切换到该文件的制定目录 3:然后按照提示一步步执行…

2024上海国际半导体制造设备材料与核心部件展览会

2024上海国际半导体制造设备材料与核心部件展览会 2024 Shanghai International Semiconductor Manufacturing Equipment Materials and Core Components Exhibition 时间&#xff1a;2024年11月18日-20日 地点&#xff1a;上海新国际博览中心 详询主办方陆先生 I38&#…

【干货精品分享】Elasticsearch 6.7 Should 子语句的失效

在ES 使用多条件 查询&#xff0c;并且是多个条件只需要满足部分条件的时候&#xff0c;我们通常会使用到ES的should查询 GET /trademark_query_index/_search {"query":{"bool" : {"must":[{"match" : {"origin": {"…

PACS系统源码 新一代的医学图像管理系统 pacs 云影像,PACS云胶片,PACS影像工作站系统源码

PACS系统源码 新一代的医学图像管理系统 pacs 云影像,PACS云胶片,PACS影像工作站系统源码 三甲医院医学影像PACS系统源码&#xff0c;集成三维影像后处理功能&#xff0c;包括三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜、最大/小密度投影、心脏动脉钙化分…

SynchronousQueue

SynchronousQueue 解释&#xff1a; 同步队列 介绍 实现了BlockingQueue 和 Queue 其中每个插入操作必须等待另一个线程相应的删除操作 同步队列没有任何容量&#xff0c;甚至没有一个容量 如果你想放入一个元素&#xff0c;就必须有另外一个线程尝试从中移除元素 使用 …

软件行业中的蓝海领域有哪些?

什么是蓝海&#xff1f; 蓝海&#xff0c;指的是未知的市场空间。这个概念相对于“红海”而言&#xff0c;红海则是指已知的市场空间。 企业要启动和保持获利性增长&#xff0c;就必须超越产业竞争&#xff0c;开创全新市场&#xff0c;这其中包括两块&#xff1a;一块是突破…

Shader 渐变屏幕

渐变 前置工作&#xff0c;创建缓冲&#xff0c;对顶点着色器传递顶点数据 function main() {var canvas document.getElementById(webgl);var gl getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) returnvar n initVertexBuffers(gl); }fu…