相关代码已上传到个人github仓库https://github.com/flyingcys/RT-Thread-Nano-GD32F150
之前已经完成GD32F150上RT-Thread Nano的内核运行,为了在产品中方便调试除错,RT-Thread的shell是非常实用且必不可少的一个功能的。其实RT-Thread Nano的keil pack也是可以支持shell中的msh功能的,只需要完成一些配置及串口驱动编写等工作就可以正常使用msh。
同时RT-Thread官方也在STM32L0及LPC824这2个芯片上提供了“bink”和“msh”示例来演示Nano的基本使用和msh的使用。这次先学习example示例工程安装、学习,而后完成GD32F150上Nano的msh功能开发。
2. 在“Devices”栏中的“Search”中输入“STM32L0”或“LPC824”,在右侧“Examples”中可以找到“bink”和“msh”示例,如未发现,请点击“Packs”->“Check For Updates”.
STM32L0 example
LPC824 example
点击“copy”按钮即可安装。这次我们选择的是STM32L0的“rtthread msh example”。
如按钮为灰色“Install”,请先右键点击“rtthread msh example”并选择“cancel actions”即可点击“Install”进行安装。
下载的example已完成全部配置可正常编译运行。example工程含有msh功能必需的串口驱动“uart.c”和“uart.h”,在后面将按照这个uart驱动模版来完成自己芯片的串口驱动。
要使用msh,除了上次使用的kernel外,还必须加载Pack中的shell和device drivers。
选中全部3个功能后,可以在Project栏中看到已加载shell及device drivers相关文件。
在keil中要正确使用RT-Thread的shell功能,必须在keil的“Options”->“Linker”页面下的“Misc controls”栏中加入--keep *.o(.rti_fn.*) --keep *.o(FSymTab)
。
msh功能必须依赖串口,串口驱动编写可参考example中的uart.c和uart.h模板来完成。uart主要用到了RT-Thread的device驱动组件和ringbuffer组件。和RT-Thread标准版bsp下的serial驱动有一些区别,具体可参照第5部分.相关阅读
RT-Thread常使用rt_hw_usart_init()
函数,也可自定义函数名。同时为了让Nano msh能正确使用,必须使用INIT_BOARD_EXPORT(rt_hw_usart_init)
;来完成串口驱动初始化。
int rt_hw_usart_init(void)
{
struct gd32_uart *uart;
#ifdef RT_USING_UART1
uart = &uart1_device;
uart->parent.type = RT_Device_Class_Char;
uart->uart_base = USART1;
uart->uart_irq = USART1_IRQn;
rt_ringbuffer_init(&uart->rx_rb, uart->rx_buffer, sizeof(uart->rx_buffer));
uart->parent.init = rt_uart_init;
uart->parent.open = rt_uart_open;
uart->parent.close = rt_uart_close;
uart->parent.read = rt_uart_read;
uart->parent.write = rt_uart_write;
uart->parent.control = RT_NULL;
uart->parent.user_data = RT_NULL;
rt_device_register(&uart->parent, "uart1", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
#endif
return 0;
}
INIT_BOARD_EXPORT(rt_hw_usart_init);
在该函数内用到了自定义gd32_uart的结构体指针变量, 该结构体变量内包含rt_device设备控制块、环形队列、gd32串口设备号、中断号、串口接收buffer等。
#define UART_RX_BUFSZ 8
struct gd32_uart
{
struct rt_device parent;
struct rt_ringbuffer rx_rb;
USART_TypeDef* uart_base;
IRQn_Type uart_irq;
rt_uint8_t rx_buffer[UART_RX_BUFSZ];
};
串口驱动需完成init/open/close/read/write函数,其中:
1. init函数内完成GPIO配置、USART使能、NVIC中断使能等工作。
2. 在open/close函数内主要是对NVIC中断进行了开/关操作,如不需要对中断操作,这2个函数可不实现。
3. 在write函数内实现串口数据发送操作。
4. 在read函数内实现串口ringbuffer数据读取。
具体代码实现可参照example
中断处理依然使用的是GD32的中断处理函数,在中断处理函数中完成ringbuffer的串口数据写入。同时需要在中断处理函数中插入rt_interrupt_enter();/rt_interrupt_leave()
;通知RT-Thread内核当前处于中断模式。
static gd32_uart_recv_handler(struct gd32_uart *uart)
{
if(USART_GetIntBitState(uart->uart_base, USART_INT_RBNE) != RESET)
{
rt_ringbuffer_putchar_force(&(uart->rx_rb), (rt_uint8_t)USART_DataReceive(uart->uart_base));
if(uart->parent.rx_indicate != RT_NULL)
{
uart->parent.rx_indicate(&uart->parent, rt_ringbuffer_data_len(&uart->rx_rb));
}
}
}
void USART1_IRQHandler(void)
{
rt_interrupt_enter();
gd32_uart_recv_handler(&uart1_device);
rt_interrupt_leave();
}
由于我使用的是GD32F150的usart1作为msh串口,还需要将rtconfig.h文件第186行的#define RT_USING_UART2
修改为#define RT_USING_UART1
。
同时将rtconfig.h文件第148行的#define RT_CONSOLE_DEVICE_NAME "uart2"
修改为#define RT_CONSOLE_DEVICE_NAME "uart1"
。
msh命令与linux下bash类似,在msh命令行下输入help
命令可获取当前已支持的全部msh命令行
同时我们如需将自己编写的函数导出到msh命令行,只需要加入MSH_CMD_EXPORT
如RT-Thread内置的version指令
#include "finsh.h"
int hello_world(void)
{
rt_kprintf("%s\n", "hello world!");
return 0;
}
MSH_CMD_EXPORT(hello_world, printf hello world);
只使用Nano Kernel的资源占用:FLASH:6.08K,RAM:1.305K
增加msh功能后的资源占用:FLASH:14.48K,RAM:2.375K
通过对比可以发现,RT-Thread Nano添加了UART及msh功能后,资源占用依然比较小的,FLASH增大了8.4K,RAM只增大了1.07K。