当前位置:   article > 正文

STM32在STM32CubeIDE平台下的RT-Thread Nano移植_rtthread nano tim

rtthread nano tim

一、软硬件版本

STM32:STM32F103C8T6
RT-Thread:RT-Thread Nano 3.15
STM32CubeIDE:v1.6.0

RT-Thread Nano
RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于家电、消费电子、医疗设备、工控等领域大量使用的 32 位 ARM 入门级 MCU 的场合。

在这里插入图片描述

RT-Thread Nano 以软件包的方式集成在 Keil MDK 与 CubeMX 中,可以直接在软件中下载 Nano 软件包获取源码。

与 RT-Thread 完整版不同的是,Nano 不含 Scons 构建系统,不需要 Kconfig 以及 Env 配置工具,也去除了完整版特有的 device 框架和组件,仅是一个纯净的内核。

由于 Nano 的极简特性,使 Nano 的移植过程变得极为简单。添加 Nano 源码到工程,就已完成 90% 的移植工作。在 Keil MDK 与 Cube MX 中还提供了 Nano 的软件包,可以一键下载加入到工程。另外,在 RT-Thread Studio 中可以基于 Nano 创建工程直接使用。

CubeMax下的Nano移植比较多,但我习惯在STM32CubeIDE下编程,但存在一个问题,那就是通过软件包的方式移植rtthread Nano后,修改过rtthread相关的配置文件后再配置ioc文件,rtthread相关的配置又会初始化,所以本例不以软件包的形式移植rtthread Nano。

二、基本配置

2.1 时钟配置

本例使用外部高速时钟(HSE),并配置好时钟树。
在这里插入图片描述
在这里插入图片描述

2.2 打开调SYS中的Debug选项

在这里插入图片描述

2.3 时基修改

本例修改为了TIM3
SYS Timebase Source 是STM32的HAL库中的新增部分,主要用于实现 HAL_Delay() 以及作为各种 timeout 的时钟基准。
在使用了OS(操作系统)之后,OS的运行也需要一个时钟基准(简称“时基”),来对任务和时间等进行管理。而OS的这个时基一般也都是通过 SysTick(滴答定时器) 来维护的,这时就需要考虑 “HAL的时基” 和 “OS的时基” 是否要共用 SysTick(滴答定时器) 了。

由于共用时基可能出现未知问题,加上STM32有多个时钟源资源,故选用TIM3作为HAL时基。

选用TIM3作为HAL时基后,会在工程下生成一个stm32f1xx_hal_timebase_tim.c文件
在这里插入图片描述
该文件下的HAL_InitTick会覆盖掉库文件里的弱函数HAL_InitTick,从而使得TIM3替代掉原来的SysTick

/**
  * @brief  This function configures the TIM3 as a time base source.
  *         The time source is configured  to have 1ms time base with a dedicated
  *         Tick interrupt priority.
  * @note   This function is called  automatically at the beginning of program after
  *         reset by HAL_Init() or at any time when clock is configured, by HAL_RCC_ClockConfig().
  * @param  TickPriority: Tick interrupt priority.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.4 串口配置

选用usart1作为rtt的console 输出口
在这里插入图片描述

2.5 配置适配 RT-Thread Nano

中断与异常处理
RT-Thread 操作系统重定义 HardFault_Handler、PendSV_Handler、SysTick_Handler 中断函数,为了避免重复定义的问题,在生成工程之前,需要在中断配置中,代码生成的选项中,取消选择三个中断函数(对应注释选项是 Hard fault interrupt, Pendable request, Time base :System tick timer),最后点击生成代码。
在这里插入图片描述

三、RT-Thread Nano移植

3.1 下载源码

https://github.com/RT-Thread/rtthread-nano

3.2 整理源码

  • bsp:只需保留bsp/_template/cubemx_config/下的board.crtconfig.h
  • compnents:用到的组件,目前不需要,可以不用,后续可以把finsh组件添加进来
  • include:全部包含
  • libcpu:只选用arm下的cortex-m3
  • src:全部包含
    在这里插入图片描述

3.3 文件添加到工程

在这里插入图片描述
在这里插入图片描述

3.4 配置 board.c

#include <rthw.h>
#include <rtthread.h>

#include "main.h"
#include "usart.h"
#include "gpio.h"

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
/*
 * Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP
 * the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes
 */
#define RT_HEAP_SIZE (10*1024)
static rt_uint8_t rt_heap[RT_HEAP_SIZE];

RT_WEAK void *rt_heap_begin_get(void)
{
    return rt_heap;
}

RT_WEAK void *rt_heap_end_get(void)
{
    return rt_heap + RT_HEAP_SIZE;
}
#endif

void SysTick_Handler(void)
{
    rt_interrupt_enter();
    
    rt_tick_increase();

    rt_interrupt_leave();
}

/**
 * This function will initial your board.
 */
void rt_hw_board_init(void)
{
	extern void SystemClock_Config(void);

	//第一部分:系统初始化,系统时钟配置等
	//系统时钟是给各个硬件模块提供工作时钟的基础,在 rt_hw_board_init() 函数中完成,可以调用库函数实现配置,也可以自行实现
	HAL_Init(); 						// 一些系统层初始化,若需要则增加此部分
	SystemClock_Config();				// 配置系统时钟
	SystemCoreClockUpdate();			// 更新系统时钟频率 SystemCoreClock

	//第二部分:配置 OS Tick的频率,实现 OS节拍(并在中断服务例程中实现 OS Tick 递增)
	HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND);

    //第三部分:初始化硬件外设,若有需要,则放在此处调用 ,注意,uart_init() 或者其他的外设初始化函数,若已经使用了宏 INIT_BOARD_EXPORT() 进行初始化,则不需要在此进行显式调用。两种初始化方法选择一种即可。
	MX_GPIO_Init();
	MX_USART1_UART_Init();

    /* 第四部分:系统动态内存堆初始化 */
	#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
		rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
	#endif

    /* 第五部分:使用 INIT_BOARD_EXPORT() 进行的初始化 Call components board initial (use INIT_BOARD_EXPORT())  */
	#ifdef RT_USING_COMPONENTS_INIT
		rt_components_board_init();
	#endif

    /* 第六部分:其他初始化 */

}


#ifdef RT_USING_CONSOLE
	void rt_hw_console_output(const char *str)
	{
		rt_size_t i = 0, size = 0;
		char a = '\r';

		__HAL_UNLOCK(&huart1);

		size = rt_strlen(str);

		for (i = 0; i < size; i++)
		{
			if (*(str + i) == '\n')
			{
				HAL_UART_Transmit(&huart1, (uint8_t *)&a, 1, 1);
			}
			HAL_UART_Transmit(&huart1, (uint8_t *)(str + i), 1, 1);
		}
	}
#endif

#ifdef RT_USING_FINSH
	char rt_hw_console_getchar(void)
	{
		/* Note: the initial value of ch must < 0 */
		int ch = -1;

		if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
		{
			ch = huart1.Instance->DR & 0xff;
		}
		else
		{
	        if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) != RESET)
	        {
	            __HAL_UART_CLEAR_OREFLAG(&huart1);
	        }
			rt_thread_mdelay(10);
		}
		return ch;
	}
#endif
  • 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
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

系统时钟配置说明

需要在 board.c 中实现 系统时钟配置(为 MCU、外设提供工作时钟)与 OS Tick 的配置 (为操作系统提供心跳 / 节拍)。

  • HAL_Init() 初始化 HAL 库,
  • SystemClock_Config() 配置了系统时钟,
  • SystemCoreClockUpdate() 对系统时钟进行更新,
  • HAL_SYSTICK_Config 配置了 OS Tick。此处 OS Tick 使用滴答定时器 systick 实现,需要用户在 board.c 中实现 SysTick_Handler() 中断服务例程,调用 RT-Thread 提供的 rt_tick_increase()。

uart_init( )说明

默认的写法如下:

static UART_HandleTypeDef UartHandle;
static int uart_init(void)
{
    /* TODO: Please modify the UART port number according to your needs */
    UartHandle.Instance = USART2;
    UartHandle.Init.BaudRate = 115200;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits = UART_STOPBITS_1;
    UartHandle.Init.Parity = UART_PARITY_NONE;
    UartHandle.Init.Mode = UART_MODE_TX_RX;
    UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;

    if (HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        while (1);
    }
    return 0;
}
INIT_BOARD_EXPORT(uart_init);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

串口的初始化有两种,官方默认使用宏 INIT_BOARD_EXPORT() 进行自动初始化,自己改下串口号也可以用。

也可以使用显式调用,需要在 board.c 中的 rt_hw_board_init() 函数中调用uart_init() 。

由于CubeIDE已自动生成了串口的初始化函数,所以本例采用的是直接自己初始化,不去写uart_init() 函数,个人觉得更简单易读。

	MX_USART1_UART_Init();
  • 1

rt_hw_console_output() 是rrt的 rt_kprintf() 函数会调用到的,实现控制台字符输出。

#ifdef RT_USING_CONSOLE
	void rt_hw_console_output(const char *str)
	{
		rt_size_t i = 0, size = 0;
		char a = '\r';

		__HAL_UNLOCK(&huart1);

		size = rt_strlen(str);

		for (i = 0; i < size; i++)
		{
			if (*(str + i) == '\n')
			{
				HAL_UART_Transmit(&huart1, (uint8_t *)&a, 1, 1);
			}
			HAL_UART_Transmit(&huart1, (uint8_t *)(str + i), 1, 1);
		}
	}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

动态内存堆说明

开启 RT_USING_HEAP 将可以使用动态内存功能,即可以使用 rt_malloc、rt_free 以及各种系统动态创建对象的 API。动态内存堆管理功能的初始化是通过 rt_system_heap_init() 函数完成的,动态内存堆的初始化需要指定堆内存的起始地址和结束地址,函数原型如下:

void rt_system_heap_init(void *begin_addr, void *end_addr)
  • 1

开启 RT_USING_HEAP 后,系统默认使用数组作为 heap,heap 的起始地址与结束地址作为参数传入 heap 初始化函数,heap 初始化函数 rt_system_heap_init() 将在 rt_hw_board_init() 中被调用。

开启 heap 后,系统中默认使用数组作为 heap(heap 默认较小,实际使用时请根据芯片 RAM 情况改大),获得的 heap 的起始地址与结束地址,作为参数传入 heap 初始化函数:

#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE];
RT_WEAK void *rt_heap_begin_get(void)
{
    return rt_heap;
}

RT_WEAK void *rt_heap_end_get(void)
{
    return rt_heap + RT_HEAP_SIZE;
}

void rt_hw_board_init(void)
{
    ....
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());    //传入 heap 的起始地址与结束地址
#endif
    ....
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.5 startup()的跳转设置

系统的运行应该是先去启动rtos,然后再在os里去执行生成的main函数,所以涉及到这个设置;

芯片在 KEIL MDK 与 IAR 下的启动文件一般不用做修改,会自动转到 RT-Thread 系统启动函数 rtthread_startup() 。

而GCC 下的启动文件则需要修改,让其跳转到 RT-Thread 提供的 entry() 函数。

修改 startup_stm32f103c8tx.s文件,将main改为entry。

/* Call the application's entry point.*/
  bl main
  bx lr
  • 1
  • 2
  • 3
/* Call the application's entry point.*/
  bl entry
  bx lr
  • 1
  • 2
  • 3

3.6 main函数的修改

包含 rtthread.h

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "rtthread.h"
  • 1
  • 2
  • 3

编写main

int main(void)
{
  /* USER CODE BEGIN 1 */
#ifndef RT_USING_USER_MAIN
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
#endif

	MX_RT_Thread_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

	  rt_kprintf("main_test\r\n");
	  rt_thread_mdelay(500);
  }
  /* USER CODE END 3 */
}
  • 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

注意: 由于相关的初始化工作已在board文件中初始化完毕,故在此处使用RT_USING_USER_MAIN宏来取消这部分的代码的编译,该宏默认开启,后续即使对图形化配置更新后,也不会覆盖掉此设定。

3.7 再建立一个线程

建立文件app_rt_thread.c

#include "rtthread.h"
#include "main.h"
#include "stdio.h"

/* 定义线程控制块 */
void MX_RT_Thread_Init(void);
//添加LED闪烁线程
static struct rt_thread led_thread;
static char led_thread_stack[256];
static void led_thread_entry(void *parameter);

void MX_RT_Thread_Init(void)
{
	//初始化线程
	rt_err_t rst;
	rst = rt_thread_init(&led_thread,
						(const char *)"ledshine",  /* 线程名字 */
						led_thread_entry,  /* 线程入口函数 */
						RT_NULL,           /* 线程入口函数参数 */
						&led_thread_stack[0],
						sizeof(led_thread_stack),   /* 线程栈大小 */
						RT_THREAD_PRIORITY_MAX-2,  /* 线程的优先级 */
						20); /* 线程时间片 */
	if(rst == RT_EOK)
	{///* 启动线程,开启调度 */
		rt_thread_startup(&led_thread);
	}
}

static void led_thread_entry(void *parameter)
{
	while(1)
	{
		rt_kprintf("led_test\r\n");
		rt_thread_mdelay(500);
	}
}
//MSH_CMD_EXPORT(led_thread_entry,run_led_thread);
  • 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

在main函数中while之前初始化

int main(void)
{
    ......
	MX_RT_Thread_Init();
	while (1)
  	{
	  rt_kprintf("main_test\r\n");
	  rt_thread_mdelay(500);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

编译下载代码,打开串口助手,可以在串口助手看到交替打印2个线程的rt_kprintf信息;

四、添加Finsh组件

RT-Thread FinSH 是 RT-Thread 的命令行组件(shell),提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。它可以使用串口 / 以太网 / USB 等与 PC 机进行通信,使用 FinSH 组件基本命令的效果图如下所示:
在这里插入图片描述
在 RT-Thread Nano 上添加 FinSH 组件,以串口 UART 作为 FinSH 的输入输出端口与 PC 进行通信,实现 FinSH 功能的步骤主要如下:

4.1 添加 FinSH 源码到工程

把finsh的源码拷贝到components下。
在这里插入图片描述

4.2 更改配置

然后在 rtconfig.h 中打开 finsh 相关选项

#include "finsh_config.h"
  • 1

finsh_port.c 中去掉2个error

#include <rthw.h>
#include <rtconfig.h>

#ifndef RT_USING_FINSH
//#error Please uncomment the line <#include "finsh_config.h"> in the rtconfig.h
#endif

#ifdef RT_USING_FINSH

RT_WEAK char rt_hw_console_getchar(void)
{
    /* Note: the initial value of ch must < 0 */
    int ch = -1;

//#error "TODO 4: Read a char from the uart and assign it to 'ch'."

    return ch;
}

#endif /* RT_USING_FINSH */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

确保之前的 board.crt_hw_console_getchar函数已设置

要实现 FinSH 组件功能:既可以打印也能输入命令进行调试,控制台已经实现了打印功能,现在还需要在 board.c 中对接控制台输入函数,实现字符输入。

#ifdef RT_USING_FINSH
	char rt_hw_console_getchar(void)
	{
		/* Note: the initial value of ch must < 0 */
		int ch = -1;

		if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
		{
			ch = huart1.Instance->DR & 0xff;
		}
		else
		{
	        if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) != RESET)
	        {
	            __HAL_UART_CLEAR_OREFLAG(&huart1);
	        }
			//rt_thread_mdelay(10);
		}
		return ch;
	}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.3 链接脚本设置

由于finsh也是作为线程,在系统初始化时自动初始化,但CubeIDE出现过未初始化成功的情况,后发现需要在STM32F103C8TX_FLASH.ld 链接脚本下添加如下内容:

    /* section information for finsh shell */
	 . = ALIGN(4);
	 __fsymtab_start = .;
	 KEEP(*(FSymTab))
	 __fsymtab_end = .;
	 . = ALIGN(4);
	 __vsymtab_start = .;
	 KEEP(*(VSymTab))
	 __vsymtab_end = .;
	 . = ALIGN(4);
	 
	 /* section information for initial. */
	 . = ALIGN(4);
	 __rt_init_start = .;
	 KEEP(*(SORT(.rti_fn*)))
	 __rt_init_end = .;
	 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

位置如下:

  /* The program code and other data into "FLASH" Rom type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
    
    /* section information for finsh shell */
	 . = ALIGN(4);
	 __fsymtab_start = .;
	 KEEP(*(FSymTab))
	 __fsymtab_end = .;
	 . = ALIGN(4);
	 __vsymtab_start = .;
	 KEEP(*(VSymTab))
	 __vsymtab_end = .;
	 . = ALIGN(4);
	 
	 /* section information for initial. */
	 . = ALIGN(4);
	 __rt_init_start = .;
	 KEEP(*(SORT(.rti_fn*)))
	 __rt_init_end = .;
	 
  } >FLASH
  • 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

4.4 程序更改

由于既存在着打印信息又需要输入命令,这里先把main下列代码注释掉

MX_RT_Thread_Init();

rt_kprintf("main_test\r\n");
  • 1
  • 2
  • 3

将led_thread线程添加至终端,去掉app_rt_thread.c文件MSH_CMD_EXPORT的注释:

int main(void)
{
	//MX_RT_Thread_Init();
	while (1)
  	{
	  //rt_kprintf("main_test\r\n");
	  rt_thread_mdelay(500);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
#include "rtthread.h"
#include "main.h"
#include "stdio.h"

/* 定义线程控制块 */
void MX_RT_Thread_Init(void);
//添加LED闪烁线程
static struct rt_thread led_thread;
static char led_thread_stack[256];
static void led_thread_entry(void *parameter);

void MX_RT_Thread_Init(void)
{
	//初始化线程
	rt_err_t rst;
	rst = rt_thread_init(&led_thread,
						(const char *)"ledshine",  /* 线程名字 */
						led_thread_entry,  /* 线程入口函数 */
						RT_NULL,           /* 线程入口函数参数 */
						&led_thread_stack[0],
						sizeof(led_thread_stack),   /* 线程栈大小 */
						RT_THREAD_PRIORITY_MAX-2,  /* 线程的优先级 */
						20); /* 线程时间片 */
	if(rst == RT_EOK)
	{///* 启动线程,开启调度 */
		rt_thread_startup(&led_thread);
	}
}

static void led_thread_entry(void *parameter)
{
	while(1)
	{
		rt_kprintf("led_test\r\n");
		rt_thread_mdelay(500);
	}
}
MSH_CMD_EXPORT(led_thread_entry,run_led_thread);
  • 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

编译下载代码,打开串口助手,可以在串口助手中打印输入 help 命令,回车查看系统支持的命令。

在这里插入图片描述
可以发现led_thread_entry函数已经可以作为命令了,可通过终端执行。

五、部分问题

5.1 问题1:低优先级的线程不运行

低优先级的线程不运行,可能是使用Finsh造成的;

查看shell.c文件可发现 finsh_thread_entry中有while(1)循环,其中的finsh_getchar 会调用到rt_hw_console_getchar函数。finsh需要适当的让出CPU资源。

void finsh_thread_entry(void *parameter)
{
    ....
     while (1)
    {
        ch = finsh_getchar();
        if (ch < 0)
        {
            continue;
        }
     ....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

方法1:把rt_hw_console_getchar函数里的rt_thread_mdelay(10)加上,但这有可能造成终端输入命令时不连续。

#ifdef RT_USING_FINSH
	char rt_hw_console_getchar(void)
	{
		/* Note: the initial value of ch must < 0 */
		int ch = -1;

		if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
		{
			ch = huart1.Instance->DR & 0xff;
		}
		else
		{
	        if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) != RESET)
	        {
	            __HAL_UART_CLEAR_OREFLAG(&huart1);
	        }
			rt_thread_mdelay(10);//在未获取到字符时,需要让出 CPU
		}
		return ch;
	}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

方法2:rt_hw_console_getchar函数采用中断得方式实现。原理是,在 uart 接收到数据时产生中断,在中断中把数据存入 ringbuffer 缓冲区,然后释放信号量,tshell 线程接收信号量,然后读取存在 ringbuffer 中的数据。这部分官方有说明。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/67495
推荐阅读
相关标签
  

闽ICP备14008679号