01
FinSH简介
FinSH是RT-Thread的命令行组件(shell),FinSH完全采用ANSI C编写,具有极好的移植性;内存占用少,支持权限验证、自动补全、查看历史命令等功能。
FinSH支持两种输入模式,分别是传统命令行模式和C语言解释器模式,这两个模式可以相互切换。传统模式又称为msh模式,在传统模式下输入exit后回车,即可切换到C语言解释器模式;在C语言解释器模式下输入msh()后回车即可切换到传统模式。
使用Shell,特别是在开发调试阶段,极大的方便了开发者快速调试功能、定位问题、调用测试函数、修改测试参数、减少代码反复烧录次数等操作,从而缩短项目开发周期,是开发过程中不可或缺的调试工具。
02
FinSH执行流程
FinSH给用户提供了一套基于命令行调用的操作接口,用户在控制终端输入命令,控制终端通过串口、USB、网络等形式将命令传给设备里的FinSH,FinSH会读取设备输入命令,解析并自动扫描内部函数表,寻找对应函数名,执行函数后输出回应,回应通过原路返回,将结果显示在控制终端上。
当使用串口连接设备与控制终端时,FinSH命令的执行流程如下图所示:
图 1 FinSH命令执行流程
03
FinSH移植
我们使用串口作为接控制台的交互接口,将FinSH的移植分为两个部分:
第一部分是添加UART控制台功能;第二部分是实现FinSH组件的移植,实现对控制台输入命令的解析和执行。
添加UART控制台 …
UART的初始化配置和接收中断处理:struct rt_semaphore shell_rx_sem;
sta
tic int UART2_Configure(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
UART_InitTypeDef UART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* initialize a semaphore for shell */
rt_sem_init(&shell_rx_sem, "shell_rx", 0, RT_IPC_FLAG_FIFO);
/* Enable GPIO and SYSCFG clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* Enable UART2 clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART2, ENABLE);
/* UART Configuration ad follow */
UART_StructInit(&UART_InitStructure);
UART_InitStructure.UART_BaudRate = 115200;
UART_InitStructure.UART_WordLength = UART_WordLength_8b;
UART_InitStructure.UART_StopBits = UART_StopBits_1;
UART_InitStructure.UART_Parity = UART_Parity_No;
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(UART2, &UART_InitStructure);
/* Enable UART2 Rx Interrupt */
UART_ITConfig(UART2, UART_IT_RXIEN, ENABLE);
/* Enable UART */
UART_Cmd(UART2, ENABLE);
/* Enable PA2 & PA3 alternate function */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);
/* Configure UART Tx as alternate function push-pull */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure UART Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Config UART2 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = UART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return 0;
}
INIT_BOARD_EXPORT(UART2_Configure);
void UART2_IRQHandler(void)
{
if(UART_GetITStatus(UART2, UART_IT_RXIEN) != RESET)
{
/* Clear UART2 Rx Interrupt Flag */
UART_ClearITPendingBit(UART2, UART_IT_RXIEN);
/* semaphore release */
rt_sem_release(&shell_rx_sem);
}
}
实现rt_hw_console_output函数:让FinSH组件输出一个字符,即在该函数中实现UART字符输出: void rt_hw_console_output(const char *str)
{
while(*str)
{
if(*str == 'n')
{
/* Send a character to the UART */
UART_SendData(UART2, 'r');
/* Loop until the end of transmission */
while(!UART_GetFlagStatus(UART2, UART_FLAG_TXEPT));
}
/* Send a character to the UART */
UART_SendData(UART2, *str++);
/* Loop until the end of transmission */
while(!UART_GetFlagStatus(UART2, UART_FLAG_TXEPT));
}
}
添加FinSH组件 …
将FinSH组件的源代码复制到目标工程目录下:
图 2 工程目录
将FinSH添加到软件工程中,使能RT_USING_FINSH宏定义,如下图以MDK-
ARM为例:
图 3 使能RT_USING_FINSH宏定义
实现rt_hw_console_getchar函数:控制台获取一个字符,即在该函数中实现UART字符获取:char rt_hw_console_getchar(void)
{
rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER);
/* Receive UART2 Data */
return UART_ReceiveData(UART2);
}
04
结果验证
编译软件工程,无误后下载代码;在串口终端工具中输入TAB按键或者help命令可以查看当前系统支持的命令信息:
图 4 操作结果窗口