基于demo测试的基础,我们已经完成了基本工程建立,同时包含GPIO小灯闪烁的驱动,本测试为完成串口驱动,及基于串口的shell终端的移植。
为了方便起见,串口就采用程序更新的USB转串口位置。根据硬件原理图可以知道,该串口连接到了控制器的P110和P109。
配置System:DEBUG,将Operation Mode 调整为 SWD。
配置 Connectivity:SCI 的 SCI9,调整Pin Group Selection为Mixed,Operation Mode为Asynchronous UART,并检查TXD9为P109,RXD9为P110。
切换到Stacks选项页,选择New Stack->Connectivity->UART(r_sci_uart),添加一个串口g_uart0
UART(r_sci_uart)。
点击新增的串口控件,编辑Properties,如下标所示。
letter-shell,一个功能强大的嵌入式shell ,是一个开源项目。letter
shell 3.0是一个C语言编写的,可以嵌入在程序中的嵌入式shell,通俗一点说就是一个串口终端,可以通过命令行调用、运行程序中的函数。支持的功能有:
移植比较简单,主要分为三个步骤,一个是shell功能的配置,一个是shell_port的移植,一个是基于FreeRTOS的shellTask任务创建。
请大家自己下载letter shell的源码,我这里下载时3.0的源码。下载后,将目录中的src复制到工程中,然后在demo目录下stm32-freertos复制到工程中,并将目录修改为shell_port。
1) Shell配置
Shell_cfg.h文件中包含shell执行的特性,具体的配置加下面的说明。
/**
* @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义
*
使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
* 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
*/
#define SHELL_TASK_WHILE 1
/**
*
@brief 是否使用命令导出方式
*
使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令
*
定义shell命令,关闭此宏的情况下,需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
/** *
@brief 是否使用shell伴生对象
*
一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象
*/
#define SHELL_USING_COMPANION 0
/**
*
@brief 支持shell尾行模式
*/
#define SHELL_SUPPORT_END_LINE 1
/** *
@brief 是否在输出命令列表中列出用户
*/
#define SHELL_HELP_LIST_USER 0
/**
*
@brief 是否在输出命令列表中列出变量
*/
#define SHELL_HELP_LIST_VAR 0
/**
*
@brief 是否在输出命令列表中列出按键
*/
#define SHELL_HELP_LIST_KEY 0
/**
*
@brief 是否在输出命令列表中展示命令权限
*/
#define SHELL_HELP_SHOW_PERMISSION 1
/**
*
@brief 使用LF作为命令行回车触发
*
可以和SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_LF 0
/**
*
@brief 使用CR作为命令行回车触发
*
可以和SHELL_ENTER_LF同时开启
*/
#define SHELL_ENTER_CR 1
/**
*
@brief 使用CRLF作为命令行回车触发
*
不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_CRLF 0
/**
*
@brief 使用执行未导出函数的功能
*
启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数
*
@attention 如果地址错误,可能会直接引起程序崩溃
*/
#define SHELL_EXEC_UNDEF_FUNC 0
/**
*
@brief shell命令参数最大数量
*
包含命令名在内,超过8个参数并且使用了参数自动转换的情况下,需要修改源码
*/
#define SHELL_PARAMETER_MAX_NUMBER 8
/**
*
@brief 历史命令记录数量
*/
#define SHELL_HISTORY_MAX_NUMBER 5
/**
*
@brief 双击间隔(ms)
*
使能宏`SHELL_LONG_HELP`后此宏生效,定义双击tab补全help的时间间隔
*/
#define SHELL_DOUBLE_CLICK_TIME 200
/**
*
@brief 管理的最大shell数量
*/
#define SHELL_MAX_NUMBER 1
/** *
@brief shell格式化输出的缓冲大小
*
为0时不使用shell格式化输出
*/
#define SHELL_PRINT_BUFFER 128
/**
*
@brief 获取系统时间(ms)
*
定义此宏为获取系统Tick,如`HAL_GetTick()` *
@note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定
*/
#define SHELL_GET_TICK() xTaskGetTickCount()
/**
*
@brief shell内存分配
*
shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_MALLOC(size) 0
/**
*
@brief shell内存释放
*
shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_FREE(obj) 0
/**
*
@brief 是否显示shell信息
*/
#define SHELL_SHOW_INFO 1
/**
* @brief
是否在登录后清除命令行
*/
#define SHELL_CLS_WHEN_LOGIN 1
/**
*
@brief shell默认用户
*/
#define SHELL_DEFAULT_USER "jy"
/**
*
@brief shell默认用户密码
*
若默认用户不需要密码,设为""
*/
#define SHELL_DEFAULT_USER_PASSWORD ""
/**
*
@brief shell自动锁定超时
*
shell当前用户密码有效的时候生效,超时后会自动重新锁定shell
*
设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位
*
@note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
*/
#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
2) Shell_port移植
Shell_port的移植时一个难点,为了兼容freertos操作系统,采用队列的方式传递所接收到的串口数据。
增加freertos队列,在Stacks选下卡中,增加一个Objects队列对象,队列配置为Item
Size 为1,Queue Length 为 100 ,如下图所示。
增加串口回调函数和接收及发送处理。
void user_uart_callback(uart_callback_args_t * p_args){
if (p_args->event == *UART_EVENT_TX_COMPLETE* )
{
uart_send_complete_flag = true;
}
if (p_args->event == *UART_EVENT_RX_CHAR* )
{
char RxBuff = (char )(p_args->data); xQueueSendFromISR(g_uart0rx_queue0,&RxBuff, pdTRUE );
}
}
串口数据发送处理函数
/**
*[url=home.php?mod=space&uid=2666770]@Brief[/url] 用户shell写
*
* [url=home.php?mod=space&uid=3142012]@param[/url] data 数据
*/
void userShellWrite(char data)
{
fsp_err_t err = FSP_SUCCESS;
err = R_SCI_UART_Write(&g_uart0_ctrl, (unsigned char *)&data, 1);
if(FSP_SUCCESS != err) __BKPT();
while(uart_send_complete_flag == false){}
uart_send_complete_flag = false;
}
串口数据接收处理函数,函数中读取队列存储的串口输入数据,并反馈给shell任务。
/* @brief 用户shell读
*
*@param data 数据
*[url=home.php?mod=space&uid=1141835]@Return[/url] char 状态
*/
signed char userShellRead(char *data)
{
BaseType_t xStatus;
/* 读队列
* xQueue: 读哪个队列
* &lReceivedValue: 读到的数据复制到这个地址
* xTicksToWait: 如果队列为空, 阻塞一会
*/
xStatus = xQueueReceive( g_uart0rx_queue0, data, portMAX_DELAY );
if( xStatus == pdPASS )
{
/* 读到了数据 */
return 0;
}
else
{
/* 没读到数据 */
return -1;
}
}
3) ShellTask任务创建
创建一个任务,完成shellTask信息的处理过程,如下图所示,在Stacks中,创建一个shellt任务,完成shellTask()的调用。
在创建的shellt_entry函数中,去掉原有的代码,增加shell处理和串口初始化代码。
/*pvParameters contains TaskHandle_t */
void shellt_entry (void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
/* **TODO** : add your own code here */
fsp_err_t err = *FSP_SUCCESS* ;
/* Open the transfer instance with initial configuration.*/
err = R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
assert(*FSP_SUCCESS* == err);
userShellInit();
shellTask(&shell);
}
我创建一个简单的shell命令,观察一下执行效果。
int hello_test (int argc, char *argv[])
{
if (argc <= 2)
{
shellPrint(&shell, "please input string same as
hello 1 2 3\n\r");
return -1;
}
shellPrint(&shell,"hello %s,%s,%s:\r\n",argv[1],argv[2],argv[3]);
return 0;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE( *SHELL_TYPE_CMD_MAIN* ), hello_test, hello_test,
hello_test);
启动后,打印了Letter shell 图案,命令提示中,也出现了我们新增的hello_test命令,执行后结果负荷我们的设计,letter shell成功移植完成。
更多回帖