1913|4

68

帖子

0

TA的资源

一粒金砂(中级)

[雅特力AT-START-F437]经nr_micro_shell加持的FreeRTOS真香 [复制链接]

各种RTOS比如zephyr、RT-Thread、Nuttx都带了自己shell,做demo或者各种功能测试的时候特别有用,那么能否给FreeRTOS也整一个shell呢?其实FreeRTOS官方有个仓库的目录中就有,所谓的FreeRTOS-Plus-CLI是也,但这玩意笔者玩过一段时间发现挺难用的,且依赖过多,所以最好是另外找一个移植简单、小巧玲珑的shell实现,笔者最后找到了nr_micro_shell和letter-shell,这两移植使用上基本差不多,选一个顺手的用用,笔者最后选择了nr_micro_shell。

注:nr_micro_shell也可以用于裸机环境,但结合RTOS会更加方便。
 

nr_micro_shell下载

git clone https://github.com/Nrusher/nr_micro_shell

然后把nr_micro_shell目录下的两子目录inc和src中的文件都copy到middlewares/nr_micro_shell/

 

nr_micro_shell在FreeRTOS中的使用

初始化的核心代码如下所示:

        shell_init();

        rx_queue = xQueueCreate(32, sizeof(uint8_t *));

        xTaskCreate((TaskFunction_t )task_shell,
                        (const char*    )"shell",
                        (uint16_t       )1024,
                        (void*          )NULL,
                        (UBaseType_t    )2,
                        (TaskHandle_t*  )NULL);

调用shell_init()初始化nr_micro_shell,然后创建一个queue用于串口中断中读出输入字符后传递给shell任务,然后就是创建名为shell的任务了,其中任务函数task_shell的代码如下:
 

static void task_shell(void const *argument)
{
        unsigned char ch;

        while (1) {
                if (xQueueReceive(rx_queue, &ch, portMAX_DELAY))
                        shell(ch);
        }
}

它的实现很简单,从队列中接收字符,然后作为参数传递给shell()函数,在队列为空即用户未输入时,shell任务会阻塞在队列接收上。接下来我们看看串口中断程序是怎么实现的,它是如何和shell任务交互的:

 

void USART1_IRQHandler(void)
{
        uint8_t rxdata;
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;

        if(usart_interrupt_flag_get(USART1, USART_RDBF_FLAG) != RESET) {
                /* read one byte from the receive data register */
                rxdata = usart_data_receive(USART1);
                xQueueSendFromISR(rx_queue, &rxdata, &xHigherPriorityTaskWoken);
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }
}

首先判断串口RDBF是否置位,如置位表明串口有数据可以读出,调用usart_data_receive()函数读出输入字符,用xQueueSendFromISR()函数发给队列,这时候阻塞在队列读取上的shell任务就会被唤醒,但能否拿到cpu执行需要等待任务切换机会,所以串口中断程序这里调用portYIELD_FROM_ISR()进行任务切换,方便已经就绪的shell任务及时运行。

 

至此,使用nr_micro_shell的框架已经搭好,接下来就可以实现各种各样的shell命令啦

 

实现几个nr_micro_shell命令
首先按nr_micro_shell的说明,向nr_micro_shell注册命令有两个方法:方法一,使用static_cmd[]全局大数组,把所有命令相关结构体都放在这个大数组里;方法二,定义NR_SHELL_USING_EXPORT_CMD宏>,然后NR_SHELL_CMD_EXPORT()导出每一个实现的命令。方法一每添加一个命令,都得改动static_cmd[]数组,从软件工程角度看不是太优美,所以方法一比较适合命令比较少或者很集中的场景;方法二比较优美,使用NR_SHELL_CMD_EXPORT()消除了全局数组的使用,特别适合各命令实现分布在很多文件中的场景,所以笔者采用了方法二。

 

然后利用FreeRTOS的支持,可以实现一些非常有意义的命令,比如uptime命令就可以用xTaskGetTickCount()来实现:

static void shell_uptime_cmd(char argc, char *argv)
{
        printf("%lu\r\n", xTaskGetTickCount());
}
NR_SHELL_CMD_EXPORT(uptime, shell_uptime_cmd);

 

msleep命令可以用vTaskDelay()函数实现:

static void shell_msleep_cmd(char argc, char *argv)
{
        unsigned int ms;

        ms = strtoul(&(argv[(int)argv[1]]), NULL, 0);
        vTaskDelay(pdMS_TO_TICKS(ms));
}
NR_SHELL_CMD_EXPORT(msleep, shell_msleep_cmd);

 

而利用vPortGetHeapStats()实现了free命令:

static void shell_free_cmd(char argc, char *argv)
{
        HeapStats_t heap_stats;
        vPortGetHeapStats(&heap_stats);

        // format the heap stats
        printf("Memory Statistics       Bytes\r\n"
            "------------------------------\r\n"
            "Total heap:\t\t%u\r\n"
            "Used heap:\t\t%u\r\n"
            "Available heap:\t\t%u\r\n"
            "Largest free block:\t%u\r\n"
            "Smallest free block:\t%u\r\n"
            "Num free blocks:\t%u\r\n"
            "Min ever heap:\t\t%u\r\n"
            "Num mallocs:\t\t%u\r\n"
            "Num frees:\t\t%u\r\n",
            configTOTAL_HEAP_SIZE,
            (configTOTAL_HEAP_SIZE - heap_stats.xAvailableHeapSpaceInBytes),
            heap_stats.xAvailableHeapSpaceInBytes,
            heap_stats.xSizeOfLargestFreeBlockInBytes,
            heap_stats.xSizeOfSmallestFreeBlockInBytes,
            heap_stats.xNumberOfFreeBlocks,
            heap_stats.xMinimumEverFreeBytesRemaining,
            heap_stats.xNumberOfSuccessfulAllocations,
            heap_stats.xNumberOfSuccessfulFrees
        );
}
NR_SHELL_CMD_EXPORT(free, shell_free_cmd);

 

任务相关的一些命令ps和top分别基于vTaskList()和vTaskGetRunTimeStats()实现:

static void shell_ps_cmd(char argc, char *argv)
{
        printf("                                Min\r\n"
                "Task            State   Pri     Stack   No\r\n"
                "------------------------------------------\r\n");
        int tasks_maxlen = 40 * uxTaskGetNumberOfTasks();
        char *ps_msg = pvPortMalloc(tasks_maxlen);

        vTaskList(ps_msg);
        printf("%s", ps_msg);
        vPortFree(ps_msg);
}
NR_SHELL_CMD_EXPORT(ps, shell_ps_cmd);

static void shell_top_cmd(char argc, char *argv)
{
        printf("Task            Runtime(us)     Percentage\r\n"
                "------------------------------------------\r\n");
        int tasks_maxlen = 40 * uxTaskGetNumberOfTasks();
        char *top_msg = pvPortMalloc(tasks_maxlen);

        vTaskGetRunTimeStats(top_msg);
        printf("%s", top_msg);
        vPortFree(top_msg);
}
NR_SHELL_CMD_EXPORT(top, shell_top_cmd);

 

当然基于CMSIS也可以实现一些有趣的命令,比如类似重启/重置的reset命令就是基于__NVIC_SystemReset()实现的,它的效果相当于按reset按钮或者调试器里重置了。

static void shell_reset_cmd(char argc, char *argv)
{
        __NVIC_SystemReset();
}
NR_SHELL_CMD_EXPORT(reset, shell_reset_cmd);

 

代码写完后修改CMakeLists.txt文件编译烧录,步骤不再赘述,请参考本测评系列前面文章。

 

美图欣赏

最后展示几个命令的运行截图供欣赏。
启动图:

1.jpg

 

 

uptime命令:

 
2.jpg

 

free命令:

5.jpg

 

 

ps命令:

3.jpg

 

 

top命令:

4.jpg

 

 

reset命令:

6.jpg

 

最新回复

我很菜的,别瞎说哈,只是刚好用那个功能   详情 回复 发表于 2025-2-10 09:36

赞赏

1

查看全部赞赏


回复
举报

7257

帖子

11

TA的资源

版主

nr_micro_shell这个shell的确非常好用。感谢大的无私分享!期待优秀作品继续!


回复

76

帖子

1

TA的资源

一粒金砂(中级)

爱用letter-shell的路过,我还给他pr过代码

点评

哇抓住大牛一枚  详情 回复 发表于 2025-2-8 19:51

回复

68

帖子

0

TA的资源

一粒金砂(中级)

cctv180 发表于 2025-2-8 15:47 爱用letter-shell的路过,我还给他pr过代码

哇抓住大牛一枚

点评

我很菜的,别瞎说哈,只是刚好用那个功能  详情 回复 发表于 2025-2-10 09:36

回复

76

帖子

1

TA的资源

一粒金砂(中级)

我很菜的,别瞎说哈,只是刚好用那个功能


回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 15

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表