大家好,又见面了,我是你们的朋友全栈君。
在使用RT-Thread中的FinSH 命令时,除了系统默认的FinSH命令以外,我们还可以自定义FinSH命令。下面就来演示一下如何自定义FinSH命令。关于FinSH命令的详细用法请参考官方资料https://www.rt-thread.org/document/site/programming-manual/finsh/finsh/。
要使用FinSH 命令首先要添加FinSH组件,添加组件的详细过程见手把手教你移植RT-Thread系统,FinSH组件添加成后,打开建立好的工程,在工程中USER文件夹下新建一个test.c的文件,并将这个文件添加到工程中。
然后在test.c中间中添加一个简单的测试代码
代码语言:javascript复制#include "board.h"
void test_hello( void )
{
rt_kprintf( "this is test hello!n" );
}
MSH_CMD_EXPORT( test_hello, say hello to RT - Thread );
添加头文件board.h,这个文件中包含了项目中所用到的操作系统和外设头文件。
下面新建一个函数,这个函数实现的功能就是打印一句话,用它来简单的测试一下,我们自定义的命令。
最后一行代码是自定义FinSH 命令的关键代码,MSH_CMD_EXPORT()函数用于将用户自定义的命令导出。这个函数的原型在官网中有说明。
第一个参数是要导出命令的名字,在这里指的是自定义的函数名。这个名字必须和函数名要一样,否则命令就不会正确执行。同时要注意这个函数名不能和工程中其他函数同名,否则在执行命令时会出错。第二个命令是自定义命令的描述,实际上是一个字符串,可以随便写。
自定义FinSH 命令就完成了,就是这么简单。这个函数不需要在系统中初始化,也不需要在其他地方调用。只要用MSH_CMD_EXPORT()函数注册之后,这个函数在变系统编译的时候,就会自动添加到FinSH 命令的列表中。使用的时候可以在控制台上调用。下来打开控制台,编译下载程序。
控制台上打印出系统信息,下来按一下键盘上的Tab键,就会打印出当前支持的所有命令。
第一个命名就是test_hello命令,说明自定义的命令注册成功了。在控制台上输入test_hello,然后按回车键。
字符串打印成功,说明自定义的函数已经成功运行了。
用同样的方式,在test.c文件中再添加两个函数并注册测试一下。
代码语言:javascript复制void test_hello( void )
{
rt_kprintf( "this is test hello!n" );
}
MSH_CMD_EXPORT( test_hello, say hello to RT - Thread );
void test1( void )
{
rt_int8_t i;
for( i = 0; i < 3; i )
rt_kprintf( "test %d!n", i );
}
MSH_CMD_EXPORT( test1, test1 to RT - Thread );
void test2( void )
{
rt_int8_t i = 10;
while( i )
{
LED2_TOGGLE;
rt_thread_delay( 500 );
i--;
}
}
MSH_CMD_EXPORT( test2, test1 to RT - Thread );
又添加了test1和test2两个函数,test1循环打印3次,test2让LED2翻转10次。编译下载文件。
在控制台上可以看到自定义的3个命令都出现了,挨个测试下。
test_hello和test1打印数据成功,test2是翻转LED指示灯的状态,在控制台上看不到效果。但是在开发板上可以看到LED2闪烁。说明自定义的3个命令都成功了。
在这里要注意一个问题,自定义的函数不能是死循环,由于这个函数是有控制台调用的,如果自定义了一个死循环的话,控制台调用这个函数之后也就进入死循环了。如果这时候想要在控制台上输入其他命名的话,就输入不了了。
比如,这里将test2中的while循环设置为死循环。
编译下载代码,并在控制台上执行test2命令。
这时会发现控制台的光标会一直在那闪烁,键盘输入指令时没有任何反应。说明控制台程序已经进入死循环中了。所以在使用FinSH 自定义命令时函数体必须为有限的循环,不能为无限循环。
自定义命令还支持带参数的命令,下面再添加一个带参数的函数。在test.c中添加下面的代码。
代码语言:javascript复制static void test_cmd( int argc, char**argv )
{
if ( argc < 2 )
{
rt_kprintf( "Please input'test_cmd <cmd1|cmd2>'nn" );
return;
}
if ( !rt_strcmp( argv[1], "cmd1" ) )
{
rt_kprintf( "cmd1 command test!nn" );
}
else if ( !rt_strcmp( argv[1], "cmd2" ) )
{
rt_kprintf( "cmd2 command test!nn" );
}
else
{
rt_kprintf( "Please input'test_cmd <cmd1|cmd2>'n n" );
}
}
MSH_CMD_EXPORT( test_cmd, cmd sample:test_cmd < cmd1 | cmd2 > );
函数的入参为 int argc
和 char**argv
。
1、argc是命令行总的参数个数,argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数命令行后面跟的用户输入的参数。 2、char *argv[]是一个字符数组,其大小是int argc,主要用于命令行参数argv[]参数,数组里每个元素代表一个参数
最后使用MSH_CMD_EXPORT()函数注册带参数的命令函数。编译下载工程,然后在控制台上输入”test_cmd cmd1″给函数传递cmd1命令,然后在输入”test_cmd cmd2″给函数传递cmd2命令,控制台上打印出的字符串和测试函数中相同,说明带参数的命令注册也成功了。
通过这个带命令的功能可以用来调试代码,在程序运行过程中通过外部命令改变函数的某个参数,来控制程序执行的过程。这里就用LED闪烁的例子来演示,比如可以在控制台发送 statrt 命令,让LED灯开始闪烁,发送stop的命令让LED停止闪烁。LED的闪烁是在LED线程中执行的,那么如何通过命令去控制LED线程呢?这里可以使用一个标志位控制,在LED线程运行过程中实时监测标志位的值。当标志位为1时,闪烁 LED灯,当标志位值为0时,停止闪烁 LED 灯。然后在命令函数中只需要改变标志位的值就行了。
在test.c 中添加代码
代码语言:javascript复制extern rt_int8_t led_flag;
//在控制台上输入 test_cmd cmd1 就会打印出对应的输出
static void led_cmd( int argc, char**argv )
{
if ( argc < 2 )
{
rt_kprintf( "Please input'led_cmd <start|stop>'nn" );
return;
}
if ( !rt_strcmp( argv[1], "start" ) )
{
led_flag = 1;
rt_kprintf( "The LED starts flashing!nn" );
}
else if ( !rt_strcmp( argv[1], "stop" ) )
{
led_flag = 0;
rt_kprintf( "The LED stop flashing!nn" );
}
else
{
rt_kprintf( "Please input'led_cmd <start|stop>'n n" );
}
}
MSH_CMD_EXPORT( led_cmd, cmd sample:led_cmd < start | stop > );
通过start和stop的命令来设置标志位的值
在主函数的LED执行线程中添加标志位控制代码
代码语言:javascript复制static void led1_thread_entry( void* parameter )
{
while ( 1 )
{
if( led_flag )
{
LED1_ON;
rt_thread_delay( 500 ); /* 延时500个tick */
LED1_OFF;
rt_thread_delay( 500 ); /* 延时500个tick */
}
else
{
rt_thread_delay( 1000 ); /* 延时500个tick */
}
}
}
在LED线程中根据标志位来选则是否闪烁LED
编译下载代码,然后在控制台上输入命令
在控制台上输入 led_cmd 命令后,开发板上的LED灯开始闪烁,在控制台上输入led_cmd stop命令后,开发板上的LED灯停止闪烁。这样通过控制台命令,来控制程序的执行流程。这样在调试代码的时候,就可以实时改变系统某些参数,方便代码的调试与监控。
工程代码下载地址 在rt-thread实时系统上自定义Finsh命令
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/143237.html原文链接:https://javaforall.cn