第五章 操作系统(RTOS)之shell
5.1 shell 介绍
在计算机发展的早期,图形系统出现之前,没有鼠标,甚至没有键盘。那时候人们如何与计算机交互呢?最早期的计算机使用打孔的纸条向计算机输入命令,编写程序。后来随着计算机的不断发展,显示器、键盘成为计算机的标准配置,但此时的操作系统还不支持图形界面,计算机先驱们开发了一种软件,它接受用户输入的命令,解释之后,传递给操作系统,并将操作系统执行的结果返回给用户。这个程序像一层外壳包裹在操作系统的外面,所以它被称为 shell。
嵌入式设备通常需要将开发板与 PC 机连接起来通讯,常见连接方式包括:串口、USB、以太网、Wi-Fi等。一个灵活的 shell 也应该支持在多种连接方式上工作。有了 shell,就像在开发者和计算机之间架起了一座沟通的桥梁,开发者能很方便的获取系统的运行情况,并通过命令控制系统的运行。特别是在调试阶段,有了 shell,开发者除了能更快的定位到问题之外,也能利用 shell 调用测试函数,改变测试函数的参数,减少代码的烧录次数,缩短项目的开发时间。FinSH 是 RT-Thread 的命令行组件shell),正是基于上面这些考虑而诞生的,FinSH 的发音为[ˈfɪnʃ]。读完本章,我们会对 FinSH 的工作方式以及如何导出自己的命令到 FinSH 有更加深入的了解。
FinSH 是 RT-Thread 的命令行组件,提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。它可以使用串口 / 以太网 / USB 等与 PC 机进行通信,硬件拓扑结构如下图所示:
当使用串口连接设备与控制终端时,FinSH 命令的执行流程,如下图所示:
跟linux的shell一样,RTOS也支持一下补全指令
5.2 FISH 输入模式
FinSH 支持两种输入模式,分别是传统命令行模式和 C 语言解释器模式。
5.2.1 命令行模式
此模式又称为 msh(module shell),msh 模式下,FinSH 与传统 shell(dos/bash)执行方式一致,例如,可以通过 cd / 命令将目录切换至根目录。
msh 通过解析,将输入字符分解成以空格区分开的命令和参数。其命令执行格式如下所示:
command [arg1] [arg2] […]
其中 command 既可以是 RT-Thread 内置的命令,也可以是可执行的文件。
5.2.2 解释器模式
C-Style 模式。C 语言解释器模式下,FinSH 能够解析执行大部分 C 语言的表达式,并使用类似 C 语言的函数调用方式访问系统中的函数及全局变量,此外它也能够通过命令行方式创建变量。在该模式下,输入的命令必须类似 C 语言中的函数调用方式,即必须携带 () 符号,例如,要输出系统当前所有线程及其状态,在 FinSH 中输入 list_thread() 即可打印出需要的信息。FinSH 命令的输出为此函数的返回值。对于一些不存在返回值的函数(void 返回值),这个打印输出没有意义。
如果在 RT-Thread 中同时使能了这两种模式,那它们可以动态切换,在 msh 模式下输入 exit 后回车,即可切换到 C-Style 模式。在 C-Style 模式输入 msh() 后回车,即可进入 msh 模式。两种模式的命令不通用,msh 命令无法在 C-Style 模式下使用,反之同理。
我们一般还是采用命令行模式。
5.3 Finsh 配置
5.3.1 加载Finsh的库
5.3.2 开启配置。
标黄的部分,删除模块。
5.3.3 配置串口函数
进入一下界面,配置以下代码。
串口配置
static int uart_init(void)
{
//#error "TODO 2: Enable the hardware uart and config baudrate."
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIO clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA , ENABLE); // PA9 & pa 10
/* Enable USART clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* Connect USART pins to AF7 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
/* Configure USART Tx and Rx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable the USART OverSampling by 8 */
USART_OverSampling8Cmd(USART1, ENABLE);
/* USARTx configuration ----------------------------------------------------*/
/* USARTx configured as follows:
- BaudRate = 5250000 baud
- Maximum BaudRate that can be achieved when using the Oversampling by 8
is: (USART APB Clock / 8)
Example:
- (USART3 APB1 Clock / 8) = (42 MHz / 8) = 5250000 baud
- (USART1 APB2 Clock / 8) = (84 MHz / 8) = 10500000 baud
- Maximum BaudRate that can be achieved when using the Oversampling by 16
is: (USART APB Clock / 16)
Example: (USART3 APB1 Clock / 16) = (42 MHz / 16) = 2625000 baud
Example: (USART1 APB2 Clock / 16) = (84 MHz / 16) = 5250000 baud
- Word Length = 8 Bits
- one Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 4800;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* NVIC configuration */
/* Configure the Priority Group to 2 bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable the Tx buffer empty interrupt */
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/* Enable USART */
USART_Cmd(USART1, ENABLE);
return 0;
}
}
输出配置
void rt_hw_console_output(const char *str)
{
//#error "TODO 3: Output the string 'str' through the uart."
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
u32 size = rt_strlen(str);
for (u32 i = 0; i < size; i++)
{
if(*str=='\n')
{
USART_SendData(USART1, '\r');
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{}
USART_SendData(USART1, '\n');
}
else
{
USART_SendData(USART1, (uint8_t) *(str));
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{}
str++;
}
}
5.3.3 配置接收缓存
输入配置,找到finsh_port.c文件,修改一下代码。
#include <rthw.h>
#include <rtconfig.h>
#include "stm32f4xx.h"
#ifndef RT_USING_FINSH
#error Please uncomment the line <#include "finsh_config.h"> in the rtconfig.h
#endif
#ifdef RT_USING_FINSH
#define BUFFERSIZE 28
u32 ubRxIndex=0;
u32 rbRxIndex=0;
char aRxBuffer[BUFFERSIZE];
RT_WEAK char rt_hw_console_getchar(void)
{
/* Note: the initial value of ch must < 0 */
int ch = -1;
//#error "TODO 4: Read a char from the uart and assign it to 'ch'."
if(rbRxIndex!=ubRxIndex)
{
ch=aRxBuffer[rbRxIndex++];
if(rbRxIndex>=BUFFERSIZE)
rbRxIndex=0;
}
return ch;
}
void USART1_IRQHandler(void)
{
/* USART in Receiver mode */
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
if (ubRxIndex < BUFFERSIZE)
{
/* Receive Transaction data */
aRxBuffer[ubRxIndex++] = USART_ReceiveData(USART1);
}
else
{
/* Disable the Rx buffer not empty interrupt */
ubRxIndex=0;
aRxBuffer[ubRxIndex++] = USART_ReceiveData(USART1);
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
#endif /* RT_USING_FINSH */
5.4 shell 常见命令
相关指令
version - show RT-Thread version information
list_thread - list thread
list_sem - list semaphore in system
list_event - list event in system
list_mutex - list mutex in system
list_mailbox - list mail box in system
list_msgqueue - list message queue in system
list_timer - list timer in system
list_device - list device in system
exit - return to RT-Thread shell mode
help - RT-Thread shell help.
ps - List threads in the system.
time - Execute command with time.
free - Show the memory usage in the system.
5.5 shell 应用
5.5.1 不带参数操作
代码
#include <rtthread.h>
void hello(void)
{
rt_kprintf("hello RT-Thread!\n");
}
MSH_CMD_EXPORT(hello , say hello to RT-Thread);
操作
msh />hello
hello RT_Thread!
msh />
5.5.2 带参数操作
配置函数
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>);
输出结果
msh >atcmd
Please input'atcmd <server|client>'
msh >
msh >atcmd server
AT server!