当前位置:   article > 正文

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

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

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

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
  • 2
  • 3
  • 4
  • 5
  • 6

任务的具体实现

//任务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 */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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

通过对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);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

按键一任务控制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);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

按键二任务控制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);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

底层功能驱动函数实现

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);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

串口重定向

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

按键扫描功能函数的实现

/*
功能:   按键扫描
参数:   无
返回值: 对应按键按下宏
*/
//头文件宏定义
/* 读取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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/463964
推荐阅读
相关标签
  

闽ICP备14008679号