GD32F303调试小记(九)之FreeRTOS移植

前言

距离上一次更新GD32系列的文章已经过了一年有余。按照之前的想法,仅仅介绍到GD32中常用的模块就结束了。在后续的开发中,有幸再次能使用这颗IC作为主控。所以既为了自己做个随笔,也为方便各位同行或是同学借鉴,这段时间我会编写几篇文章主要以GD32F303为主控对FreeRTOSLVGLFreeModbusCJson的移植和简单使用方法。因为使用了RTOS,后面三个开源库就不再介绍单独的裸机移植方法。
接下来我们开始正式进入RTOS的移植。

FreeRTOS

  • 一款开源的实时操作系统,能方便的编写业务需求而较少的考虑模块间存在的耦合问题,相比于裸机对CPU的管理更有效,广泛运用于各类电子行业。
  • 这里不作盲目推荐,RTOS不是只有FreeRTOS,也不是所有的方向上的产品开发都需要使用RTOS,比如一些对实时性要求极高(包含极端保护功能)的产品用上RTOS基本会死的透透的。
  • 对RTOS的详细介绍可自行查阅网上资料,这里不再过多赘述。

移植准备

移植前请准备好以下内容:

  1. GD32F303包含其对应标准库的keil工程
    工程可使用官方的例程或可按照GD32F303调试小记(零)之工程创建与编译创建。
  2. FreeRTOS源码
    我使用的是V10.4.0,源码在GITHUB上。其内核源码我替换成了V10.4.3
  3. 一块由GD32F303主控的硬件板子,并包含对应的输入输出控件。

移植步骤

一、FreeRTOS并入KEIL工程

  1. 解压文档
    请添加图片描述 2. 重点关注Source目录下的文件
    请添加图片描述
  • FreeRTOS移植的核心源码就是include文件夹、7个C文件和portable文件夹中的部分文件
    请添加图片描述
  1. 在工程目录中新建FreeRTOS文件夹并分好类别
    请添加图片描述
  • 类别分好后,将源码中include文件夹和7个C文件无增减的分别复制到工程中FreeRTOS->include文件夹和FreeRTOS->Src文件夹

请添加图片描述
请添加图片描述
请添加图片描述

  1. 接下来还剩port文件夹和1个FreeRTOSConfig.h的头文件
  • 我们先解决头文件的问题,这里可以从之前下载的官方源码例子中复制,也可套用我下面的头文件
    请添加图片描述
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 
 *
 * See http://www.freertos.org/a00110.html
 *----------------------------------------------------------*/

#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			0
#define configCPU_CLOCK_HZ			( ( unsigned long ) 108000000 )	
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES		( 5 )
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 4 * 1024U ) )
#define configMAX_TASK_NAME_LEN		( 16 )
#define configUSE_TRACE_FACILITY	0
#define configUSE_16_BIT_TICKS		0

#define configUSE_PREEMPTION		1
#define configUSE_TIME_SLICING      1
#define configIDLE_SHOULD_YIELD		1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	0
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1

/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 		255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */


/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15

#define vPortSVCHandler		SVC_Handler
#define xPortPendSVHandler	PendSV_Handler
//#define xPortSysTickHandler SysTick_Handler

#endif /* FREERTOS_CONFIG_H */
  1. 还剩一个port文件夹,port文件夹需要解决两个问题,RTOS内部的内存管理以及跟我们实际用到的IC硬件上的接口
  • 内存管理部分,官方提供了5种方法,我们只使用第4种,不过我们还是都复制过来
    请添加图片描述
    请添加图片描述
  • IC硬件接口文件的选择又取决于两个问题:我们用的芯片内核是什么?对于这款IC我们使用的开发(编译)环境又是什么?
  • 对于上述两个问题可以好好思考下,理解了这两个,即使再换一颗你没用过的芯片想跑上FreeRTOS都不是问题。
  • GD32F303这款我直接说答案了,在FreeRTOS里的分类是ARM-CortexM4不带MPU的核,由于Keil工程里我使用的是V5编译器,移植的文件选择RVDS里的文件。
    请添加图片描述
    请添加图片描述
  1. 将所有需要的源码复制至工程里后,我们要在Keil工程同样配置关联这些文件
  • 先确认自己用的编译器版本是否是V5
    请添加图片描述
  • 创建工程目录里的文件夹并添加文件

请添加图片描述
请添加图片描述
请添加图片描述

  • 添加上述文件的路径

请添加图片描述
请添加图片描述

  1. 做完上述步骤,还剩下port.c中几个重要的接口函数
  • port.c中有3个比较重要的函数请添加图片描述

  • 我们需要将FreeRTOS与MCU相关的接口函数名关联起来,网上方法有很多,这里选择最便捷的方法通过宏对函数做个重命名
    请添加图片描述

  • 在gd32f30x_it.c中注释掉SVC_Handler();PendSV_Handler();

/*!
    \brief      this function handles PendSV exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
//void PendSV_Handler(void)
//{
    
    
//}

/*!
    \brief      this function handles PendSV exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
//void PendSV_Handler(void)
//{
    
    
//}
  • 在gd32f30x_it.c中的SysTick_Handler()添入xPortSysTickHandler();
  • 这里也建议让INCLUDE_xTaskGetSchedulerState 这个宏值为 1,判断RTOS是否先创建任务,再执行调度。
extern void xPortSysTickHandler(void);
/*!
    \brief      this function handles SysTick exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SysTick_Handler(void)
{
    
    
	if(0U != delay)
	delay--;

	#if (INCLUDE_xTaskGetSchedulerState  == 1 )
		if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
		{
    
    
	#endif   
			xPortSysTickHandler();
	#if (INCLUDE_xTaskGetSchedulerState  == 1 )
		}
	#endif  

}
  1. 调整启动文件中的堆栈大小
  • 这里我调的都比较大,实际可以再调小些,我用的这颗IC有96k的SRAM,所以可劲躁
    在这里插入图片描述
  1. 完成上述步骤后,添加对应的头文件,编译就能正常使用FreeRTOS了

请添加图片描述请添加图片描述请添加图片描述

二、板子验证

  1. 先创建3个任务
  • 创建LCD_BG_task任务,用于调整屏背光的亮灭,产生呼吸效果
  • lcd_backlight_breathe()用于调整背光的PWM
  • xSemaphoreGive()用与释放一个二值信号量,让任务同步
void LCD_BG_task(void * pvParameters)
{
    
      
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 1UL );
	for(;;)
	{
    
    		
		if(pdTRUE == lcd_backlight_breathe())
			xSemaphoreGive(xBinarySemaphore);
		vTaskDelay(xTicksToWait);
	}
}
  • 创建Usart_Send_task任务,用于定时发送串口信息
void Usart_Send_task(void * pvParameters)
{
    
    
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 1000UL );
	TickType_t xLastWakeTime;
    
	/* 获得当前的Tick Count */
	xLastWakeTime = xTaskGetTickCount();
	
	while(1)
	{
    
    
		Usartx_Transmit_DMA(USART1,(uint8_t *)"FreeRTOS_1S_Task!\n",18);
		
		xTaskDelayUntil(&xLastWakeTime,xTicksToWait);
	}	
}
  • 创建Lcd_Refresh_task任务,用于刷新屏幕显示内容
  • xSemaphoreTake()用于获取任务1给到的同步信息,在灭屏时刷新显示内容
  • LCD_Clear()用于刷屏,将屏变成指定的颜色
void Lcd_Refresh_task(void * pvParameters)
{
    
    
	static uint8_t i=0;
	
	while(1)
	{
    
    
		if( xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE )
		{
    
    
			if(i<2)	i++;
			else		i=0;
			
			if(i==0)		LCD_Clear(0xF800);	/* 红色   */ 
			else if(i==1)	LCD_Clear(0x001F);	/* 蓝色   */
			else if(i==2)	LCD_Clear(0xFFE0);	/* 黄色   */
		}
	}	
}
  1. 主函数
  • 创建任务并执行调度
#include "gd32f30x.h"
#include "gd32f30x_libopt.h"
#include "main.h"

FLAG_BIT Module;

extern uint32_t delay;

uint8_t lcd_backlight_breathe(void);

#define LCD_BG_TASK_PRIO  		( tskIDLE_PRIORITY + 2 )
#define UART_TASK_PRIO    		( tskIDLE_PRIORITY + 2 )
#define LCD_REFRESH_TASK_PRIO   ( tskIDLE_PRIORITY + 2 )

TaskHandle_t LCDBGTask_Handle;

void LCD_BG_task(void * pvParameters);
void Usart_Send_task(void * pvParameters);
void Lcd_Refresh_task(void * pvParameters);

/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
/* 二进制信号量句柄 */
SemaphoreHandle_t xBinarySemaphore;

int main()
{
    
    	
	
	SystemTick_Init();	
	SystemClock_Reconfig();	
		
	GPIO_Init();
	Timer3_Init();
	SPIx_Init();
	USARTx_Init();	
//	ADCx_Init();
	DMA_Init();
	NVIC_Init();
	LCD_Init();

	/* 创建二进制信号量 */
	xBinarySemaphore = xSemaphoreCreateBinary( );
	
	if( xBinarySemaphore != NULL )
	{
    
    
		xTaskCreate(LCD_BG_task, "LcdBG_Task", configMINIMAL_STACK_SIZE, NULL, LCD_BG_TASK_PRIO, (TaskHandle_t*  )&LCDBGTask_Handle);
		xTaskCreate(Usart_Send_task, "Uart_Task", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, NULL);
		xTaskCreate(Lcd_Refresh_task,"LCD_Refresh_Task",configMINIMAL_STACK_SIZE, NULL, LCD_REFRESH_TASK_PRIO, NULL);
	
		vTaskStartScheduler();
	}
	else
	{
    
    
	
	}

	while(1)
	{
    
    

	}

}
  1. 最终效果
  • 任务2发送的串口数据在上位机的效果如下:
    在这里插入图片描述
  • 任务1、3在实物板子上的显示效果如下:

GD32F303+FreeRTOS刷屏显示

  • 这里留个思考,视频里屏的颜色变化为绿->蓝->黄->红->蓝->黄->红,为什么不是绿->红->蓝->黄->红->蓝->黄->红?

工程文件

  • 这里也提供本文的工程源码,不免费提供,有需要的可以下载

请添加图片描述

猜你喜欢

转载自blog.csdn.net/qq_37554315/article/details/131060820