在爱板上看到有《我与开发的那些事》的活动时,就想着要写点什么。寒假在家搞毕业论文,利用调程序的间隙消遣一下,写了个小软件UartShell,实际上是一个跑在单片机上的模块程序,用来实现通过串口模拟超级终端调试单片机上的程序的,以前写程序的时候,用到类似的功能就临时做一下,也没考虑编程框架和实现效率的问题。这次静下心来,根据这几年算不上长的开发经验,好好规划了一些编程思路,花了两天的时间,终于搞出来这么个东西。写程序的时候突然有点感触,可能是要准备跟毕业有关的事情,要准备着结束一个阶段,又或是一年到头,好不容易撑了下来,来了点总结的味道。在此,我想跟同我一样热爱电子这一行的战友们分享我的一点经验,谈谈关于“积累”的事情。
像UartShell这种实用的程序模块,在开发的过程中能够为我们带来非常大的便利,所以,在日常的学习和开发过程中,我们要多注意这样模块程序的积累。在学习过程中,常会遇到一些实用的模块程序,它们能够独立完成一些常用的功能,本身又有很高的可移植性,只要稍做修改,就可以添加到我们自己的应用中。很多开源项目做的就是这些工作,并且还提供充分的程序(src)和文档(doc),这些就是我们的积累对象。久而久之,我们就可以拥有一个自己的实用工具代码库,当在开发过程中需要用到某项功能时,直接将积累的程序添加到应用中,就如同搭积木一样。
另外,对文档的积累也是非常重要的,一方面是搜集实用的应用笔记,大的芯片公司的网站上会提供丰富的官方资源不过大部分都是英文的,虽说也能看懂,不过实在有点费劲。中文的应用笔记材料多来自论坛,以前搞飞思卡尔的智能车比赛时,在智能车论坛上学到了很多,爱板网也是非常好的平台(呵呵,植入广告)。
还有就是,程序与文档不分家,只有程序没有文档,时间长了就忘了程序的功能和用法,再用的时候还得重新研究程序,浪累时间;只有文档没有程序,叫扯淡。我自己积累实用的模块程序时,尽量都在自己熟悉的平台上移植一遍,有时间的话,还要将自己的应用笔记记录下来,毕竟看自己写的东西比看别人写的更方便。
借鉴别人的同时,或是用得不爽,或是心血来潮,自己有时候也会点东西。自己写程序时多考虑一下功能的抽象,考虑独立性和可移植性,毕竟以后我们不会就只用某一款芯片,只维护某一个项目。提取和验证自己写的程序模块是否具好用的最好方法,就是亲自动手将这个功能从现有的功能移植到一个新的芯片上,让程序在不同的平台上运行出同样的效果,实际体验移植的过程,只说不做是没有人信的。实际上,本文提到的UartShell最开始就是在51单片机上写的,后来才移植到K60芯片上。
分享也是积累的一个重要内容,BT下载就是一个典型的例子,分享的人越多,积累得越快。在分享的过程中,还会遇到志同道合的伙伴。这是一个很有意思的事情,我接触到的工程师很多都是比较闷骚的性格,不可否认,我自己也是这个人群中的一员。这些人在陌生人的面前通常表现得比较木讷,不太愿意说话,但千万不要被这些假象骗到,一旦你提到最近在做的项目并且愿意同他们分享做项目的点滴时,他们就会告诉你类似的经历,没聊上几句就产生了相见恨晚、惺惺相惜的感觉,不多久,晚饭可能就有着落了,搞电路写程序的人也是很可爱的。
大家都说,搞我们这一行的是越老越值钱,但我们自己要明白,值钱的不是我们已经耗费的青春,而是用这些青春换来的积累和沉淀。
OK,上面是扯淡的部分,下面,我将奉上我自己的小作品——UartShell。
UartShell是一个我一直想写的小程序,但在过去的一年中,各种各样的事情缠身,就被拖了下来。在这一年将要结束的时候,终于了却了这一桩心愿,也算是让我这一年虚度的青春“死而瞑目”了。
总之,是高兴的事情,分享给大家。提供程序+文档的下载。
一、概述UartShell是一个应用于嵌入式开发中,运行在下位机的,可通过Uart串口进行人机交互的软件。类似于在下位机运行U-boot软件时,通过上位机的“超级终端”模拟命令行的操作方式。通过将UartShell软件的源码包含在下位机的程序中,使下位机通过UartShell软件识别来自上位机的命令,并根据命令执行相应的处理。软件本身使用C语言编写,代码量极少,结构清晰,便于直接移植应用或作为模块框架进行二次开发。 可使用任何一款运行在上位机的“串口调试工具”软件与UartShell进行通信。在通信过程中以字符串表示帧信息,使用直观、方便。 UartShell软件在开发过程中,使用Uart串口作为通信端口,但是UartShell本身并不局限与Uart串口,可适用于任何采用流式通信模式(stream)的通信手段场景的应用。 二、软件简介UartShell软件包含三个主要功能模块:帧接收函数、命令集和帧处理函数。 2.1 帧接收函数帧接收函数用于从Uart串口通信端口获得字节流,并进行组帧检测,其原型为: void ShellGetChar(uint_8 ch); 其中,通过ch参数向UartShell软件传入通过Uart串口获得的字节流。在函数内部进行组帧。通信帧为不定长帧,有帧首字符和帧尾字符确定帧的起止,其格式如表1所示: 在UartShell.h文件中与帧格式相关的宏定义如表2所示。 帧格式可由用户自行订制。由于在帧接收函数内部以有限状态机的形式实现帧检测,对于高级程序员来说,也非常便于深层次地订制通信帧格式。 只有符合帧格式的数据流片段才能作为信息帧被UartShell接收。 2.2 命令集UartShell将有效帧数据作为命令,从而启动下位机的对应功能。用户需在应用程序中自定义识别命令及其响应函数,供UartShell识别及调用。在UartShell.h文件中定义命令类型如下: typedef structshell_cmd_struct { uint_8 *CMD; uint_8 *INFO; void (*FUNC)(uint_8* param); uint_8 *PARAM; }SHELL_CMD_STRUCT, *SHELL_CMD_PTR; 各字段功能如表3所示。 表3 命令类型 | | | | | | 解释命令功能的字符串,识别命令后将通过通信端口输出提示信息 | | 响应函数(指针),识别命令后调用该函数执行相应功能 | | |
命令集是一组命令的组合,使用数组的形式定义,以“SHELL_CMD_END”宏作为结束标识。通过将命令集传入给帧处理函数,供UartShell进行帧匹配并执行响应处理。在样例工程中,定义命令集如下: (main.c) SHELL_CMD_STRUCTShellCmdTBL[] = { {"exit", "exit - exitshell\r\n", NULL, NULL}, {"help", "help - list allcmd\r\n", ShCmdFunc_Help, NULL}, {"fuck", "fuck - you know it^v^\r\n", NULL, NULL}, SHELL_CMD_END }; 其中ShCmdFunc_Help就是识别到“help”命令后执行的函数,其原型是: voidShCmdFunc_Help(uint_8 *param); 功能是打印输出所有的命令及提示信息,具体见样例工程。 用户可自定义多组命令集,通过将不同的命令集传入帧处理函数进行命令集检测的切换。 2.3 帧处理函数帧处理函数将接收帧与指定命令集中的命令进行匹配,当识别出帧信息中的命令时,触发命令的响应函数。其函数原型如下: intShellExcute(SHELL_CMD_STRUCT mShellCmdTBL[]); 其中,mShellCmdTBL是传入的命令集数组,进行帧处理的同时,将命令在命令集中的命令序号(数组编号)返回,用户可以根据这个信息进行下一步的处理。 建议:在命令响应函数中尽量不要执行太多的操作。命令响应函数跟中断服务函数的性质是一样的,如果响应函数执行的过长,将会影响到后续命令的响应。所以帧处理函数还返回了同样能够代表命令的命令序号,供应用程序进一步处理。 三、将UartShell添加到应用中本节将介绍如何在UartShell添加到用户自己的应用工程中。 3.1 添加相关代码UartShell软件主要包含两个文件UartShell.h和UartShell.c。另外,样例工程中,在common.h文件中定义了基本的数据类型。在使用UartShell前要确保这些与UartShell相关的内容被包含在应用工程中,在main.c文件中添加”#include “UartShell.h”使得在应用程序中可以使用UartShell的功能。 3.2 向UartShell中传送数据流将ShellGetChar函数插入到应用程序中处理Uart串口接收数据的代码中。建议在Uart串口中断服务例程中,例如在样例工程中有: (isr.c) voidisr_uart3_re(void) { uint8 ch; uint8 flag = 1; // 串口是否成功获得数据 ch = hw_uart_re1(UART_3, &flag); // 串口接收数据 if (0 == flag) { //hw_uart_send1(UART_3, ch); ShellGetChar(ch); // 将接收数据传入UartShell } } 3.3 自定义命令集及编写命令响应函数在应用程序中自定义命令集,指定所要识别的命令相关信息:命令名、命令提示信息、命令响应函数、命令响应函数参数。实现命令响应函数。 例如,在样例工程中定义的命令集为: (main.c) SHELL_CMD_STRUCTShellCmdTBL[] = { {"exit", "exit - exitshell\r\n", NULL, NULL}, {"help", "help - list allcmd\r\n", ShCmdFunc_Help, NULL}, {"fuck", "fuck - you know it^v^\r\n", NULL, NULL}, SHELL_CMD_END }; 其中实现了“help”命令为打印该命令集的命令提示信息: voidShCmdFunc_Help(uint_8 *param) { SHELL_CMD_PTR cmdPtr =(SHELL_CMD_PTR)ShellCmdTBL; printf_arg("\r\n/*---- SHELL HELP ---------------------------------*/\r\n"); while (cmdPtr->CMD) { printf_arg(" * "); printf_arg(cmdPtr->INFO); cmdPtr++; } printf_arg("/*-------------------------------------------------*/\r\n"); } 3.4 在main函数中添加帧处理函数在main函数中可执行的某个死循环中添加ShellExcute函数,代码如下所示: while(1) { ShellExcute(ShellCmdTBL); } 若整个应用中只有这一个while(1),则UartShell在整个程序中一直起作用。 另一种推荐的使用方式是使用两个while(1),在第一个while(1)中进行帧处理,并设计“exit”命令,在检测到exit命令后跳出第一个while(1),转入第二个while(1),执行正式的程序。 程序框架如下: main() { int ret = -1; // 第一个while while (1) { ret = ShellExcute(ShellCmdTBL); if (ret == 0) // 识别exit命令 { break; // 跳出UartShell } } // 第二个while while(1) { // 正式的运行程序 } } 采用第二种使用方式,在第一个while中进行调试与配置,在第二个while中运行正式的程序时,不再执行UartShell的程序,减少不必要的干扰。 四、样例工程随本文档提供了样例工程,演示了UartShell的基本使用方法。 编译平台:IAR 6.3 for ARM 硬件平台:FreescaleK60N512 MCU 使用在上位机运行“串口调试工具”演示效果如图1所示。
file:///C:%5CUsers%5CSuYong%5CAppData%5CLocal%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_image001.png | 图1 演示UartShell | 后记写这么个小软件的想法在心里压了一年多,呵呵,是真的。只是这一年多有太多的事情,写文档的时间多,坐车的时间多,写程序的时间少,所以对UartShell的需求不是很强烈,也就这么拖下来了。好不容易等到了放寒假,虽然仍然忙得要死,但在家里至少能静下心来安排一些事情。在为毕业论文调程序的时候碰到的通信帧的问题,终于下定决心花上两天时间搞定这个东西。 目前UartShell仅仅是个初级版本,实现了基本功能之后就没有再做多的规范化,其中的很多工程文件还是来自“野火”的工程模板,所以整体看来比较粗糙,不够专业。毕竟不是商业化的产品,有代码,有文档,足够说明问题了。如果以后有需要,再包装吧。 写UartShell的时候在网上找了一些资料,其中参考文献[1]是西北工业大学的同学写的,很务实的一篇文章,可以说是帮我把之前零星的相关编程经验系统地整理了一下,使我受益颇深;参考文献[2]是我研读了N多遍的大作,讲的是嵌入式开发中的面向对象的概念,在UartShell中命令的设计上就借鉴了面向对象的思想。而使用字符串进行识别命令的的思想则受益于我读研期间对操作系统源码的研究(在很多操作系统中,是使用名字字符串来识别对象的)。 这个小程序除了能够完成功能之外,主要的闪光点在于软件框架的设计,具有比较好的扩展性,其中主要体现有两点: (1)基于有限状态机的方式进行帧检测。在参考文献[1]中对具体的方法进行了非常详细的说明。学过《数电》的人对状态机都是非常熟悉的,再结合《编译原理》中关于状态机的阐述,就会发现基于状态机的设计具有很好的框架性,几乎可以解决任何跟文字有关的问题。 (2)基于面向对象的设计思想实现命令集。面向对象不受编程语言的限制,并不是用Java写出来的程序就是面向对象的,也不是说纯C写的程序就是面向过程的,在嵌入式编程中用上面向对象,那不要太臭屁啦。 参考文献[1] 周奋,王婷. 嵌入式系统中串口通信帧的同步方法[J]. 单片机与嵌入式系统应用,2006年,第10期. [2] 宋宝华. C语言嵌入式系统编程修炼之道[DB/OL]. 网上广为流传的大作.^,^
|