本帖最后由 lemonhub 于 2025-1-19 19:22 编辑
极海半导体-G32A1465开发板测评 03-RT-ThreadRT-Thread线程管理测试
实验目的准备目标硬件(开发板/芯片/模组)本教程将使用极海G32A1465开发板进行示例移植。调试ARM Cortex M核还需要仿真器,如果您的开发板或者芯片模组没有板载仿真器,就需要连接外置的仿真器,如DAPLink]、JLINK之类的。 FINSH命令行控制工具在官方提供的RT-Thread工程模板中,已经移植好了finsh组件,直接使用即可,如果需要移植Finsh组件请参考官方教程。
FinSH是RT-Thread的命令行组件,提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。用户在控制终端输入命令,控制终端通过串口、USB、网络等方式将命令传给设备里的FinSH,FinSH会读取设备输入命令,解析并自动扫描内部函数表,寻找对应函数名,执行函数后输出回应,回应通过原路返回,将结果显示在控制终端上。 FinSH内置命令在RT-Thread中默认内置了一些FinSH命令,在FinSH中输入help后回车或者直接按下Tab键,就可以打印当前系统支持的所有命令。常用的命令如下:
自定义FinSH命令FinSH功能配置FinSH功能可以裁剪,宏配置选项在rtconfig.h文件中定义,具体配置项如下表所示。在实践开发中可以根据需求进行配置。 自定义FinSH命令除了FinSH自带的命令,FinSH还也提供了多个宏接口来导出自定义命令,导出的命令可以直接在FinSH中执行。 RT-Thread同时支持自定义msh命令和自定义C-Style命令,自定义msh命令使用的更加广泛。 自定义的FinSH命令,可以在msh模式下运行,将一个命令导出到msh模式可以使用如下宏接口如下: MSH_CMD_EXPORT:这个命令可以导出有参数的命令,也可以导出无参数的命令。 /************************************************************************************************
* 名称:MSH_CMD_EXPORT
* 功能:自定义msh命令导出
* 参数:name表示要导出的命令
* 参数:desc表示导出命令的描述
************************************************************************************************/
MSH_CMD_EXPORT(name, desc);
导出无参数命令时,函数的入参为void,示例如下:
#include <rtthread.h>
void hello(void)
{
rt_kprintf("hello RT-Thread!\n");
}
MSH_CMD_EXPORT(hello , say hello to RT-Thread);
导出有参数的命令时,函数的入参为int argc和char**argv。argc表示参数的个数,argv表示命令行参数字符串指针数组指针。导出有参数命令示例如下: #include <rtthread.h>
static void atcmd(int argc, char**argv)
{
if (argc < 2)
{
rt_kprintf("Please input'atcmd <server|client>'\n");
return;
}
if (!rt_strcmp(argv[1], "server"))
{
rt_kprintf("AT server!\n");
}
else if (!rt_strcmp(argv[1], "client"))
{
rt_kprintf("AT client!\n");
}
else
{
rt_kprintf("Please input'atcmd <server|client>'\n");
}
}
MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd <server|client>);
自定义FINSH命令测试案例 要实现自定义FinSH命令,第一,要了解如何定义命令并将命令添加到FinSH命令列表;第二,为了让命令效果更加明显,使用命令控制信号灯。将自定义的FinSH命令和信号灯控制结合起来可以实现联动控制,从而达到项目效果。 首先自定义FinSH命令,实现对信号灯的开关控制。然后在主线程中进行信号灯引脚初始化,使用FinSH控制台输入命令之后,进行命令解析并控制相关的信号灯。 程序设计流程如下: 1)首先进行LED引脚初始化,配置相关GPIO引脚的模式和初始电平; 2)然后编写自定义FinSH命令函数,当在MobaXterm串口终端FinSH控制台输入: “ledON r” 时点亮红色灯,“ledON g” 时点亮绿色灯,“ledON b” 时点亮蓝色灯,输入“ledON all”时亮所有灯,输入“ledOFF”时熄灭灯。 输入“led_flow”开始流水灯效果,输入“led_flow_stop”停止流水灯效果。
测试函数 // LED 控制函数
int ledON(int argc, char **argv)
{
if (argc < 2)
{
rt_kprintf("Usage: ledON <r|g|b>\n");
return -1;
}
if (!rt_strcmp(argv[1], "r")) // 打开红灯
{
LED_On(LED_RED);
LED_Off(LED_BLUE);
LED_Off(LED_GREEN);
rt_kprintf("Red LED ON!\n");
}
else if (!rt_strcmp(argv[1], "g")) // 打开绿灯
{
LED_On(LED_GREEN);
LED_Off(LED_BLUE);
LED_Off(LED_RED);
rt_kprintf("Green LED ON!\n");
}
else if (!rt_strcmp(argv[1], "b")) // 打开蓝灯
{
LED_On(LED_BLUE);
LED_Off(LED_RED);
LED_Off(LED_GREEN);
rt_kprintf("Blue LED ON!\n");
}
else if(!rt_strcmp(argv[1], "all"))
{
LED_On(LED_BLUE);
LED_On(LED_RED);
LED_On(LED_GREEN);
rt_kprintf("All LEDs ON!\n");
}
else
{
rt_kprintf("Invalid option! Usage: ledON <r|g|b>\n");
return -1;
}
return 0;
}
// 关闭所有 LED 的函数
int ledOFF(int argc, char **argv)
{
board_led_turnoff(); // 关闭所有 LED
rt_kprintf("All LEDs OFF!\n");
return 0;
}
// 导出命令到 RT-Thread Shell
MSH_CMD_EXPORT(ledON, Turn on specific LED (r: red, g: green, b: blue));
MSH_CMD_EXPORT(ledOFF, Turn off all LEDs);
// 流水灯状态
static int led_flow_state = 0; // 当前点亮的 LED 状态
static rt_timer_t flow_timer = RT_NULL; // 定时器句柄
// 流水灯定时器回调函数
static void led_flow_timer_callback(void *parameter)
{
// 根据当前状态切换 LED
switch (led_flow_state)
{
case 0:
LED_On(LED_RED); // 点亮红灯
LED_Off(LED_GREEN); // 关闭绿灯
LED_Off(LED_BLUE); // 关闭蓝灯
led_flow_state = 1; // 切换到下一个状态
break;
case 1:
LED_On(LED_GREEN); // 点亮绿灯
LED_Off(LED_RED); // 关闭红灯
LED_Off(LED_BLUE); // 关闭蓝灯
led_flow_state = 2; // 切换到下一个状态
break;
case 2:
LED_On(LED_BLUE); // 点亮蓝灯
LED_Off(LED_RED); // 关闭红灯
LED_Off(LED_GREEN); // 关闭绿灯
led_flow_state = 0; // 切换到下一个状态
break;
default:
led_flow_state = 0; // 重置状态
break;
}
}
// 启动流水灯效果
int led_flow(int argc, char **argv)
{
// 检查是否已经存在定时器
if (flow_timer != RT_NULL)
{
return -1; // 流水灯效果已经在运行
}
// 创建定时器
flow_timer = rt_timer_create(
"led_flow", // 定时器名称
led_flow_timer_callback, // 回调函数
RT_NULL, // 回调函数参数
500, // 定时器周期(500ms)
RT_TIMER_FLAG_PERIODIC // 周期性定时器
);
// 检查定时器是否创建成功
if (flow_timer == RT_NULL)
{
return -1; // 定时器创建失败
}
// 启动定时器
rt_timer_start(flow_timer);
return 0; // 流水灯效果启动成功
}
// 停止流水灯效果
int led_flow_stop(int argc, char **argv)
{
// 检查定时器是否存在
if (flow_timer == RT_NULL)
{
return -1; // 流水灯效果未运行
}
// 停止并删除定时器
rt_timer_stop(flow_timer);
rt_timer_delete(flow_timer);
flow_timer = RT_NULL;
// 关闭所有 LED
LED_Off(LED_RED);
LED_Off(LED_GREEN);
LED_Off(LED_BLUE);
return 0; // 流水灯效果停止成功
}
// 导出命令到 RT-Thread Shell
MSH_CMD_EXPORT(led_flow, Start LED flow effect);
MSH_CMD_EXPORT(led_flow_stop, Stop LED flow effect);
测试终端效果 测试实物效果
Thread 线程管理线程管理简介 在多线程操作系统中,也同样需要开发人员把一个复杂的应用分解成多个小的、可调度的、序列化的程序单元,当合理地划分任务并正确地执行时,这种设计能够让系统满足实时系统的性能及时间的要求。例如让嵌入式系统执行这样的任务,系统通过传感器采集数据,并通过显示屏将数据显示出来,在多线程实时系统中,可以将这个任务分解成两个子任务,如下图所示,一个子任务不间断地读取传感器数据,并将数据写到共享内存中,另外一个子任务周期性的从共享内存中读取数据,并将传感器数据输出到显示屏上。 在RT-Thread中,与上述子任务对应的程序实体就是线程,线程是实现任务的载体,它是RT-Thread中最基本的调度单位,它描述了一个任务执行的运行环境,也描述了这个任务所处的优先等级,重要的任务可设置相对较高的优先级,非重要的任务可以设置较低的优先级,不同的任务还可以设置相同的优先级,轮流运行。 线程调度机制RT-Thread线程管理的主要功能是对线程进行管理和调度,系统中总共存在两类线程,分别是系统线程和用户线程,系统线程是由RT-Thread内核创建的线程,用户线程是由应用程序创建的线程,这两类线程都会从内核对象容器中分配线程对象,当线程被删除时,也会被从对象容器中删除,每个线程都有重要的属性,如线程控制块、线程栈、入口函数等。 RT-Thread的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到CPU的使用权。 当一个运行着的线程使一个比它优先级高的线程满足运行条件,当前线程的CPU使用权就被剥夺了,或者说被让出了,高优先级的线程立刻得到了CPU的使用权。 如果是中断服务程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行。 当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。 线程管理方式
创建线程一个线程要成为可执行的对象,就必须由操作系统的内核来为它创建一个线程。可以通过如下的接口创建一个动态线程: /************************************************************************************************
* 名称:rt_thread_create()
* 功能:创建线程
* 参数:name表示线程的名称
* 参数:entry表示线程入口函数
* 参数:parameter表示线程入口函数参数
* 参数:stack_size表示线程栈大小,单位是字节
* 参数:priority表示线程的优先级
* 参数:tick表示线程的时间片大小
* 返回:thread表示线程创建成功,返回线程句柄;RT_NULL表示线程创建失败
************************************************************************************************/
rt_thread_t rt_thread_create(const char* name,
void (*entry)(void* parameter),
void* parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick);
删除线程对于一些使用rt_thread_create()创建出来的线程,当不需要使用,或者运行出错时,我们可以使用下面的函数接口来从系统中把线程完全删除掉: /************************************************************************************************
* 名称:rt_thread_delete()
* 功能:删除线程
* 参数:thread表示要删除的线程句柄
* 返回:RT_EOK表示删除线程成功,-RT_ERROR表示删除线程失败
************************************************************************************************/
rt_err_t rt_thread_delete(rt_thread_t thread);
启动线程创建(初始化)的线程状态处于初始状态,并未进入就绪线程的调度队列,我们可以在线程初始化创建成功后调用下面的函数接口让该线程进入就绪态: /************************************************************************************************
* 名称:rt_thread_startup()
* 功能:启动线程
* 参数:thread表示线程句柄
* 返回:RT_EOK表示线程启动成功,-RT_ERROR表示线程启动失败
************************************************************************************************/
rt_err_t rt_thread_startup(rt_thread_t thread);
挂起线程当线程调用rt_thread_delay()时,线程将主动挂起;当调用rt_sem_take(),rt_mb_recv()等函数时,资源不可使用也将导致线程挂起。处于挂起状态的线程,如果其等待的资源超时(超过其设定的等待时间),那么该线程将不再等待这些资源,并返回到就绪状态;或者,当其他线程释放掉该线程所等待的资源时,该线程也会返回到就绪状态。 /************************************************************************************************
* 名称:rt_thread_suspend()
* 功能:挂起线程
* 参数:thread表示线程句柄
* 返回:RT_EOK表示线程挂起成功,-RT_ERROR表示线程挂起失败
************************************************************************************************/
rt_err_t rt_thread_suspend (rt_thread_t thread);
恢复线程恢复线程就是让挂起的线程重新进入就绪状态,并将线程放入系统的就绪队列中;如果被恢复线程在所有就绪态线程中,位于最高优先级链表的第一位,那么系统将进行线程上下文的切换。线程恢复使用下面的函数接口: /************************************************************************************************
* 名称:rt_thread_resume()
* 功能:恢复线程
* 参数:thread表示线程句柄
* 返回:RT_EOK表示线程恢复成功,-RT_ERROR表示线程恢复失败
************************************************************************************************/
rt_err_t rt_thread_resume (rt_thread_t thread);
线程管理API-测试案例 首先进行LED线程初始化工作,创建线程,并设置线程入口函数,使用MobaXterm串口终端FinSH控制台输入命令之后,进行命令解析并执行相关的线程管理。 1)首先创建LED线程,并且在main函数中完成线程初始化工作; 2)然后编写线程入口函数,在线程入口中完成LED引脚初始化等工作; 3)当在MobaXterm串口终端FinSH控制台输入“threadManage start”命令时,启动LED线程,并进入到线程入口函数中循环点亮LED灯。输入“threadManage suspend”命令时,挂起线程,LED灯循环点亮停止;输入“threadManage resume”命令时,恢复线程,LED灯继续循环点亮;输入“threadManage delete”命令时,删除线程,LED循环点亮停止,并且在系统中移除LED线程。程序流程图如下图所示: 测试函数代码 //FINSH CMD命令行测试
int threadManage(int argc, char **argv)
{
rt_err_t result = RT_EOK;
if(led_thread->type == 0) // 判断LED线程是否被删除,避免程序异常
{
rt_kprintf("led thread have been removed!\n");
return 0;
}
if(!rt_strcmp(argv[1], "start"))
{
result = rt_thread_startup(led_thread); // 启动LED线程
if(result == RT_EOK)
rt_kprintf("led thread starting success!\n");
else
rt_kprintf("led thread failed to start, errCode:%d\n", result);
}
else if(!rt_strcmp(argv[1], "suspend"))
{
if(suspendFlag == 0)
suspendFlag = 1; // 置位挂起线程标志位
if(result == RT_EOK)
rt_kprintf("led thread suspend success!\n");
else
rt_kprintf("led thread suspend failed, errCode:%d\n", result);
}
else if(!rt_strcmp(argv[1], "resume"))
{
result = rt_thread_resume(led_thread); // 恢复线程
if(result == RT_EOK)
rt_kprintf("led thread resume success!\n");
else
rt_kprintf("led thread resume failed, errCode:%d\n", result);
}
else if(!rt_strcmp(argv[1], "delete"))
{
result = rt_thread_delete(led_thread); // 删除线程
if(result == RT_EOK)
rt_kprintf("led thread deletion success!\n");
else
rt_kprintf("led thread fail to delete, errCode:%d\n", result);
}
else
rt_kprintf("Please input 'threadManage <start|suspend|resume|delete>'\n");
return 0;
}
MSH_CMD_EXPORT(threadManage, thread Manage sample);
app_led任务函数 /*********************************************************************************************
* 文件:app_led.h
* 作者:CoderEnd 2025.01.19
* 描述:LED线程源文件
* 修改:
*
* 注释:
*********************************************************************************************/
#include "main.h"
rt_thread_t led_thread = RT_NULL;
unsigned char suspendFlag = 0;
/*********************************************************************************************
* 名称:led_ctrl()
* 功能:LED控制
* 参数:cmd -> 控制值
* 返回:无
* 修改:
* 注释:
*********************************************************************************************/
void led_ctrl(unsigned char cmd)
{
PINS_WritePin(LED_GPIO, LED1_PIN_NUM, !(cmd & LED1_NUM));
PINS_WritePin(LED_GPIO, LED2_PIN_NUM, !(cmd & LED2_NUM));
PINS_WritePin(LED_GPIO, LED3_PIN_NUM, !(cmd & LED3_NUM));
}
/*********************************************************************************************
* 名称:led_thread_entry()
* 功能:LED线程入口函数
* 参数:*parameter -> 入口参数
* 返回:无
* 修改:
* 注释:
*********************************************************************************************/
static void led_thread_entry(void *parameter)
{
(void)parameter;
unsigned char ledState = 0x01;
// led_pin_init(); // LED引脚初始化
while(1)
{
if(suspendFlag == 1)
{
rt_thread_suspend(led_thread); // 挂起线程
rt_schedule();
suspendFlag = 0;
}
led_ctrl(ledState);
if(ledState < 0x08)
ledState <<= 1;
else
ledState = 0x01;
rt_thread_mdelay(500); // 延时500ms
}
}
/*********************************************************************************************
* 名称:led_thread_init()
* 功能:LED线程初始化函数
* 参数:无
* 返回: -1 -> led线程创建失败
* 0 -> led线程创建成功
* 修改:
* 注释:
*********************************************************************************************/
int led_thread_init(void)
{
led_thread = rt_thread_create("app_led", // 线程名称
led_thread_entry, // 线程入口函数
RT_NULL, // 入口函数传入参数
256, // 线程堆栈大小
10, // 线程优先级
20); // 线程时间片
if(led_thread == RT_NULL) return -1;
return 0;
}
/*********************************************************************************************
* 文件:app_led.h
* 作者:CoderEnd 2025.01.19
* 描述:LED驱动及线程头文件
* 修改:
* 注释:
*********************************************************************************************/
#ifndef __APL_LED_H_
#define __APL_LED_H_
#include <rtthread.h>
// LED灯引脚对应序号,请参考 drv_gpio.c 文件
#define LED1_PIN_NUM 0 // PD0
#define LED2_PIN_NUM 15 // PD15
#define LED3_PIN_NUM 16 // PD16
#define LED1_NUM 0x01
#define LED2_NUM 0x02
#define LED3_NUM 0x04
extern rt_thread_t led_thread;
extern unsigned char suspendFlag;
int led_thread_init(void);
#endif
测试终端效果 测试实物效果
|