前言
前面我们进行了串口的测试,参考
https://bbs.eeworld.com.cn/thread-1247060-1-1.html
https://bbs.eeworld.com.cn/thread-1247447-1-1.html
为了方便后续调试,我们现在来实现简单SHELL命令行。
过程
首先需要实现串口收发接口
int uart_read(uint8_t* buffer, uint32_t len)
{
if (cyhal_uart_getc(&cy_retarget_io_uart_obj, buffer, len)
== CY_RSLT_SUCCESS)
{
return len;
}
else
{
return 0;
}
}
int uart_write(uint8_t* buffer, uint32_t len)
{
while(len--)
{
cyhal_uart_putc(&cy_retarget_io_uart_obj, *buffer++);
}
}
Shell.c中实现和putchar,putchar已经重定向实现了所以不需要实现,否则可以改为uart_write
/**
*****************************************************************************
* \fn static int shell_getchar(unsigned char *data)
* \brief 读取一个字符.
* \note .
* \param[out] data 存储读到的数据.
* retval 0 成功.
* retval -1 失败.
*****************************************************************************
*/
static int shell_getchar(unsigned char *data)
{
int erro=0;
//driver_uart_recv(getstdiouart(), data, 1,10,&erro);
if(0 == uart_read(data, 1))
{
erro = 1;
}
else
{
uart_write(data,1);
}
if(erro!=0)
{
return -1;
}
return 0;
}
核心数据结构
typedef void ( * CommandFunc )( void *); /**< shell命令回调函数 */
/**
* \struct shell_cmd_cfg
* SHELL命令结构体.
*/
typedef struct
{
unsigned char const* name; /**< shell命令名 */
CommandFunc func; /**< shell命令回调函数 */
char const* helpstr; /**< 帮助字符串 */
}shell_cmd_cfg;
定义表格
const shell_cmd_cfg shell_cmd_list[ ] =
{
/*1.帮助相关*/
{ (const uint8_t*)"help", HelpFun, "help"}, /*打印帮助信息*/
{ 0, 0 },
};
接收到命令行时搜索shell_cmd_list比对name命令名,如果比对上则调用函数func执行。
读一行命令关键代码
static unsigned int shell_read_line(int get(unsigned char* tmp))
{
unsigned char ch = '\r';
unsigned int count;
unsigned char tmp;
/*开始打印一次"sh>"*/
if(cmd_buf[0]=='\r')
{
printf("sh>\r\n");
memset(cmd_buf,0x00,sizeof(cmd_buf));
}
/*如果接收到字符往下走,否则返回*/
if(get(&tmp)==0)
{
ch = tmp;
}
else
{
return 0;
}
/*如果接收到非打印字符且当前接收缓冲区不为0则认为接收到一帧否则打印"SH>"*/
if((ch == '\r' || ch == '\n' || ch < ' ' || ch > '~') && (ch != '\b'))
{
if(cmd_buf_index==0)
{
printf("sh>\r\n");
}
else
{
count = cmd_buf_index;
cmd_buf[cmd_buf_index]=0;
cmd_buf_index =0;
printf("\r\n");
return count;
}
}
else
{
if(ch == '\b')
{
if(cmd_buf_index != 0)
{
cmd_buf_index--;
putchar('\b');
putchar(' ');
putchar('\b');
cmd_buf[cmd_buf_index]= '\0';
}
}
else
{
/*如果接收到打印字符且当前接收缓冲区满则认为接收完一帧"*/
putchar(ch);
cmd_buf[cmd_buf_index++] = ch;
if(cmd_buf_index>=(sizeof(cmd_buf)-1))
{
count = cmd_buf_index;
cmd_buf[cmd_buf_index]=0;
cmd_buf_index =0;
printf("\r\n");
return count;
}
}
}
return 0;
}
测试
编译运行,可以输入help查看命令列表
代码
shell.c
/**
*****************************************************************************
* \brief 平台相关(PLATFORM)SHELL模块(SHELL)相关接口实现.
* \details
* All rights reserved.
* \file shell.c
* \author
* \version 1.0
* \date
* \note 使用前请参考注释.\n
* \since 新建
* \par 修订记录
* - 初始版本
* \par 资源说明
* - RAM:
* - ROM:
*****************************************************************************
*/
#include <stdio.h>
#include <string.h>
#include "shell.h"
#include "cy_retarget_io.h"
extern int uart_read(uint8_t* buffer, uint32_t len);
extern int uart_write(uint8_t* buffer, uint32_t len);
/*****************************************************************************
*
* 内部数据
*
****************************************************************************/
#define SHELL_CMD_LEN 64 /**< shell命令缓冲区长度 */
extern const shell_cmd_cfg shell_cmd_list[ ]; /**< shell_fun中定义 */
extern const shell_cmd_cfg shell_cmd_list_app[ ]; /**< app_shellfun中定义 */
static int shellpassed = 1; /**< shell密码是否校验 0已经校验 1未校验 */
static unsigned char cmd_buf[SHELL_CMD_LEN]="\r";
static int cmd_buf_index=0; /**< 当前接收到的命令字符数 */
/*****************************************************************************
*
* 内部接口函数实现
*
****************************************************************************/
/**
*****************************************************************************
* \fn static int shell_getchar(unsigned char *data)
* \brief 读取一个字符.
* \note .
* \param[out] data 存储读到的数据.
* retval 0 成功.
* retval -1 失败.
*****************************************************************************
*/
static int shell_getchar(unsigned char *data)
{
int erro=0;
//driver_uart_recv(getstdiouart(), data, 1,10,&erro);
if(0 == uart_read(data, 1))
{
erro = 1;
}
else
{
uart_write(data,1);
}
if(erro!=0)
{
return -1;
}
return 0;
}
/**
*****************************************************************************
* \fn static unsigned int shell_cmd_len(char const *cmd)
* \brief 获取命令字符长度.
* \note 空格或者\0结束.
* \param[in] cmd 字符串
* return unsigned int 命令字符的长度
*****************************************************************************
*/
static unsigned int shell_cmd_len(unsigned char const *cmd)
{
unsigned char const *p = cmd;
unsigned int len = 0;
while((*p != ' ') && (*p != 0))
{
p++;
len++;
}
return len;
}
/**
*****************************************************************************
* \fn static void shell_check_passwd(void)
* \brief shell密码确认.
* \note 空格或者\0结束.
*****************************************************************************
*/
static void shell_check_passwd(void)
{
unsigned int i;
unsigned char ch[6] = {0};
unsigned char pw[7] = SHELL_PASSWORD_STR;
while(1)
{
printf("\r\nPassword:");
memset(ch, 0, sizeof(ch));
for (i = 0; i < sizeof(ch); i++)
{
ch = getchar();
putchar('\b');
putchar('*');
}
if (memcmp(pw, ch, sizeof(ch)) == 0)
{
printf("\r\n");
break;
}
printf("\r\nAccess denied\r\n");
}
}
/**
*****************************************************************************
* \fn static int shell_cmd_check(unsigned char *cmd, char const *str)
* \brief 匹配命令字符.
* \note .
* \param[in] cmd 字符串
* \param[in] str 匹配字符
* retval 0 匹配成功
* retval 1 匹配失败
*****************************************************************************
*/
static int shell_cmd_check(unsigned char *cmd, unsigned char const *str)
{
unsigned int len1 = shell_cmd_len((unsigned char const *)cmd);
unsigned int len2 = shell_cmd_len(str);
if(len1 != len2)
{
return 1;
}
return memcmp(cmd, str, len1);
}
/**
*****************************************************************************
* \fn static unsigned int shell_read_line(int get(void), unsigned int len)
* \brief shell读命令行.
* \note 回车结束,可以使用Backspace键清除输入.
* \param[in] get 输入接口函数
* \param[in] p 接收缓冲区
* \param[in] len 需要接收的长度
* return unsigned int 实际接收的长度
*****************************************************************************
*/
static unsigned int shell_read_line(int get(unsigned char* tmp))
{
unsigned char ch = '\r';
unsigned int count;
unsigned char tmp;
/*开始打印一次"sh>"*/
if(cmd_buf[0]=='\r')
{
printf("sh>\r\n");
memset(cmd_buf,0x00,sizeof(cmd_buf));
}
/*如果接收到字符往下走,否则返回*/
if(get(&tmp)==0)
{
ch = tmp;
}
else
{
return 0;
}
/*如果接收到非打印字符且当前接收缓冲区不为0则认为接收到一帧否则打印"SH>"*/
if((ch == '\r' || ch == '\n' || ch < ' ' || ch > '~') && (ch != '\b'))
{
if(cmd_buf_index==0)
{
printf("sh>\r\n");
}
else
{
count = cmd_buf_index;
cmd_buf[cmd_buf_index]=0;
cmd_buf_index =0;
printf("\r\n");
return count;
}
}
else
{
if(ch == '\b')
{
if(cmd_buf_index != 0)
{
cmd_buf_index--;
putchar('\b');
putchar(' ');
putchar('\b');
cmd_buf[cmd_buf_index]= '\0';
}
}
else
{
/*如果接收到打印字符且当前接收缓冲区满则认为接收完一帧"*/
putchar(ch);
cmd_buf[cmd_buf_index++] = ch;
if(cmd_buf_index>=(sizeof(cmd_buf)-1))
{
count = cmd_buf_index;
cmd_buf[cmd_buf_index]=0;
cmd_buf_index =0;
printf("\r\n");
return count;
}
}
}
return 0;
}
/**
*****************************************************************************
* \fn int shell_exec_cmdlist(unsigned char* cmd)
* \brief 搜索命令列表并执行.
* retval 0 成功
* retval -1 失败
* \note .
*****************************************************************************
*/
int shell_exec_cmdlist(unsigned char* cmd)
{
int i;
/*平台相关命令*/
for (i=0; shell_cmd_list.name != 0; i++)
{
if (shell_cmd_check(cmd, shell_cmd_list.name) == 0)
{
shell_cmd_list.func(cmd);
return 0;
}
}
// /*应用相关命令*/
// for (i=0; shell_cmd_list_app.name != 0; i++)
// {
// if (shell_cmd_check(cmd, shell_cmd_list_app.name) == 0)
// {
// shell_cmd_list_app.func(cmd);
// return 0;
// }
// }
if(shell_cmd_list.name == NULL)
{
printf("unkown command\r\n");
return -1;
}
return 0;
}
/*****************************************************************************
*
* 对外接口函数实现
*
****************************************************************************/
/**
*****************************************************************************
* \fn void shell_exec_shellcmd(void)
* \brief 执行shell命令.
* \note 任务周期调用该函数.
*****************************************************************************
*/
void shell_exec_shellcmd(void)
{
if (!shellpassed)
{
shell_check_passwd();
shellpassed = 1;
}
if(shell_read_line(shell_getchar))
{
shell_exec_cmdlist(cmd_buf);
}
}
shell.h
/**
*****************************************************************************
* \brief 平台相关(PLATFORM)SHELL模块(SHELL)相关数据结构和接口描述.
* \details
* All rights reserved.
* \file shell.h
* \author
* \version 1.0
* \date
* \note 使用前请参考注释.\n
* \since 新建
* \par 修订记录
* - 初始版本
* \par 资源说明
* - RAM:
* - ROM:
*****************************************************************************
*/
#ifndef _SHELL_H_
#define _SHELL_H_
#ifdef __cplusplus
extern "C" {
#endif
/** \addtogroup PLATFORM PLATFORM
* \{
*/
/** \addtogroup PLATFORM_SHELL PLATFORM_SHELL
* \{
*/
/*****************************************************************************
* *
* 数据结构描述 *
* *
****************************************************************************/
/** \defgroup PLATFORM_SHELL_data PLATFORM_SHELL_data
* \{
*/
typedef void ( * CommandFunc )( void *); /**< shell命令回调函数 */
/**
* \struct shell_cmd_cfg
* SHELL命令结构体.
*/
typedef struct
{
unsigned char const* name; /**< shell命令名 */
CommandFunc func; /**< shell命令回调函数 */
char const* helpstr; /**< 帮助字符串 */
}shell_cmd_cfg;
#define SHELL_PASSWORD_STR "DSWYBS" /**< shell密码固定6个字符 */
#define SHELL_CMDBUF_SIZE 64 /**< shell命令缓冲区大小 */
/**
* \}
*/
/*****************************************************************************
*
* 接口函数描述
*
****************************************************************************/
/** \defgroup PLATFORM_SHELL_if PLATFORM_SHELL_if
* \{
*/
/**
*****************************************************************************
* \fn void shell_exec_shellcmd(void)
* \brief 执行shell命令.
* \note 任务周期调用该函数.
*****************************************************************************
*/
void shell_exec_shellcmd(void);
/**
*****************************************************************************
* \fn int shell_exec_cmdlist(unsigned char* cmd)
* \brief 搜索命令列表并执行.
* retval 0 成功
* retval -11 失败
* \note .
*****************************************************************************
*/
int shell_exec_cmdlist(unsigned char* cmd);
/**
* \}
*/
/**
* \}
*/
/**
* \}
*/
#ifdef __cplusplus
}
#endif
#endif
shell_func.c
/**
*****************************************************************************
* \brief 平台层(PLATFORM)SHELL命令模块(SHELL_FUN)相关接口实现.
* \details
* All rights reserved.
* \file shell_fun.c
* \author
* \version 1.0
* \date
* \note 平台相关命令.\n
* \since 新建
* \par 修订记录
* - 初始版本
* \par 资源说明
* - RAM:
* - ROM:
*****************************************************************************
*/
#include <stdio.h>
#include <time.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include "shell_fun.h"
#include "shell.h"
/*****************************************************************************
*
* 内部数据
*
****************************************************************************/
const shell_cmd_cfg shell_cmd_list[ ] =
{
/*1.帮助相关*/
{ (const uint8_t*)"help", HelpFun, "help"}, /*打印帮助信息*/
{ 0, 0 },
};
/*****************************************************************************
*
* 内部接口函数实现
*
****************************************************************************/
/*****************************************************************************
*
* 对外接口函数实现
*
****************************************************************************/
/*****************************************************************************
*
* 帮助相关
*
****************************************************************************/
void HelpFun(void* param)
{
unsigned int i;
printf("\r\n");
printf("**************\r\n");
printf("* SHELL *\r\n");
printf("* V1.0 *\r\n");
printf("**************\r\n");
printf("\r\n");
for (i=0; shell_cmd_list.name != 0; i++)
{
printf("%02d.",i);
printf("%-16s",shell_cmd_list.name);
printf("%s\r\n",shell_cmd_list.helpstr);
}
}
shell_func.h
/**
*****************************************************************************
* \brief 平台层(PLATFORM)SHELL命令模块(SHELL_FUN)相关数据结构和接口描述.
* \details
* All rights reserved.
* \file shell_fun.h
* \author
* \version 1.0
* \date
* \note 平台相关命令.\n
* \since 新建
* \par 修订记录
* -
* \par 资源说明
* - RAM:
* - ROM:
*****************************************************************************
*/
#ifndef __SHELL_FUN_H
#define __SHELL_FUN_H
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************************
*
* 帮助相关
*
****************************************************************************/
void HelpFun(void* param);
void DeadTimeFun(void* param);
void DirFun(void* param);
void SpeedFun(void* param);
#ifdef __cplusplus
}
#endif
#endif
总结
以上实现了简单的eSHELL命令行,后面就可以基于此进行交互了。