实用STM32的串口控制平台的实现(建议收藏)
扫描二维码
随时随地手机看文章
来源:CSDN
1. 前言
玩过Linux的朋友, 是不是对Linux无所不能的串口Shell命令控制台羡慕不已, 要是自己做的STM32F系列低档次的MCU也有这种控制交互能力, 会给调试/维护和配置省下多少麻烦事呀, 比如启动/关闭调试或自检模式, 打印调试信息, 配置系统参数, 传输文件等等, 也有相当多的朋友凭借自己出色的编程能力可以实现这些功能, 这里提出我的这个解决方案, 以作交流.本平台(xc_shell)具备以下性能特点:- 大量主要代码, 和具体硬件无关, 移植性强,代码文件少.
- 只有在处理用户的输入命令时, 才占用CPU资源, 且代码可裁剪到1KB SRAM和4KB Flash;
- 用户可以非常灵活的添加按模板编写的命令脚本文件, 自定义扩张能力强.
- 支持操作系统和非操作系统两种场景应用.
- 支持Ymodem文件传输协议
- 支持将Flash的扇区开辟为参数区, 可实现本地/远程升级。
- 实用Led灯信号管理, 可将65535虚拟信号灯选择输出到1个实体LED灯上, 调试时序和状态非常有用
- 拥有基础的LED管理, 调试模式设置, 命令帮助指令, 复位指令等基础功能
2. xc_shell平台介绍
2.1 如何实现硬件无关
类比Linux会发现, 设备的硬件接口往往会被虚拟成一个文件(驱动), 而Linux内核完全与硬件系统无任何字节关联, 不同平台驱动不同而已, 故而本xc_shell的串口驱动也采用了相似的思路:1) 串口驱动用一个结构体描述, 这样只需在xc_shell.c中用指针指向这个TTYx_HANDLE结构体对象就可以将串口(tty)硬件与内核联系在一起, 聪明的朋友可能会想到, 假如我将带网络的开发板按此结构体,虚拟一个TTY对象, 岂不是就可以实现一个网络远程控制台了! 这点确实是可以的!2) 当然诸如多TTY串口实现接口互换等, 都是一个指针和step2中的注入回调处理交换的问题。3)用户在使用api_TxdFrame或api_TxdByte时”bsp_ttyX.c“,会驱动具体MCU的串口将数据发送出去, 收到一帧数据后,若用户设置了inj_RcvFrame回调处理方法,则会在中断中执行用户的回调处理。/*---------------------*
* 指正函数定义
*----------------------*/
typedef void (*pvFunDummy)(void);
//输入整行,输出逻辑
typedef void (*pvFunVoid) (void);
typedef void (*pvFunBool) (bool bVal);
typedef void (*pvFunChar) (uint8_t cVal);
typedef void (*pvFunShort)(uint16_t sVal);
typedef void (*pvFunWord) (uint32_t wVal);
//输入整行,输出逻辑
typedef bool (*pbFunVoid) (void);
typedef bool (*pbFunBool) (bool bVal);
typedef bool (*pbFunChar) (uint8_t cVal);
typedef bool (*pbFunShort)(uint16_t sVal);
typedef bool (*pbFunWord) (uint32_t wVal);
//输入整形指针,输出逻辑
typedef bool (*pbFun_pVoid) (void * pVoid);
typedef bool (*pbFun_pChar) (uint8_t * pStr);
typedef bool (*pbFun_pShort)(uint16_t * pShor);
typedef bool (*pbFun_pWord) (uint32_t * pWord);
//输入数据帧,输出逻辑
typedef bool (*pbFun_Buffx)(void * pcBuff, uint16_t len );
typedef bool (*pbFun_Bytex)(uint8_t * pcByte, uint16_t len );
/*---------------------*
* TTYx 句柄结构
*----------------------*/
typedef struct TTYx_HANDLE_STRUCT
{
const char * const name; //驱动器名
const uint16_t rxSize; //接收大小
const uint16_t txSize; //发送大小
//------------------------------------------------------
//step1: 用户可用API
const pvFunWord init; //初始化.
const pbFun_Bytex api_TxdFrame; //发送数据帧. (发送帧)
const pbFunChar api_TxdByte; //发送数据字节
//------------------------------------------------------
//step2: 注入回调函数
pbFun_Bytex inj_RcvFrame; //(ISR)接收数据帧. (接收帧)
pvFunDummy inj_TxdReady; //(ISR)发送完毕回调
//------------------------------------------------------
//step3: 接收回调函数
struct TTYx_HANDLE_STRUCT * pvNext; //连接到下一个指令
}TTYx_HANDLE;
- 可注入的命令脚本(CLI)实现
/*---------------------*
* CLI指令
*----------------------*/
typedef struct
{
const char * const pcCmdStr; //指令字符串(只能为小写字母)
const char * const pcHelpStr; //指令描述,必须以:"\r\n结束". 比如:"help: Returns a list\r\n".
const pFunHook pxCmdHook; //指向回调函数的指针,处理成功返回真否者返回0;
uint8_t ucExpParam; //指令期望的参数个数
const MEDIA_HANDLE *phStorage; //指向存储介质,没有的话填充NULL
}Cmd_Typedef_t;
各位朋友可能会使用到非常多的自定义CLI命令, 格式诸如这个网卡的命令:
const Cmd_Typedef_t CLI_WizMsg=
{
//识别关键字
.pcCmdStr = "wiz",
//帮助内容
.pcHelpStr =
"[WIZ contorls]\r\n"
" wiz help\r\n"
" wiz rd info\r\n"
" wiz reset\r\n"
" wiz wr ip ...\r\n"
" wiz wr mask ...\r\n"
" wiz wr way ...\r\n"
" wiz wr mac -----\r\n"
" wiz wr port \r\n"
" wiz wr sip ... \r\n"
" wiz wr cip ... \r\n"
" wiz load default\r\n"
"[WIZ Test mode]\r\n"
" wiz loop open\r\n"
" wiz loop close\r\n"
"\r\n",
//处理函数
.pxCmdHook =