代码改变世界

一个完整的信号采集系统项目开发流程

2013-12-14 13:23  BreakChen  阅读(15218)  评论(39编辑  收藏  举报

一. 摘要

    这篇文章详细介绍了一个“多路信号采集系统”的开发过程。“多路信号采集系统”是一个可伸缩的信号采集系统,通道可以选择从0~100路不同的信号源。单个采集板都能够采集10路数据,用户可以根据自己的需求方便地扩展或者收缩信号通道数。本系统可以用于常见的民用或者工业现场监控、仪器仪表等数据采集场合。该系统基于Arm Context M3内核处理器实现,有基板和采集板两大部分组成,基板主要负责整个采集时序的控制,而采集板则完成真是的数据采集并将采集到的数据发送到数据总线,进而传输到主机端。数据传输采用了串口通信的方式(RS485),并采用Modbus协议实现,从而方便地实现了采集板地址的检索、数据量控制、以及CRC校验值确定等功能。软件系统则采用了固件库编程的方式,全程开发均使用C语言完成,从而为以后升级做好准备。开发使用了今日标企业工作平台以及Github代码托管平台相结合完成开发的方式,使用今日标企业工作平台管理项目开发流程,而使用Github则方便地实现了不同地区开发者协作开发的目的。而系统调试则选择了传统的调试方式,先进行单个功能模块测试,再测试系统功能,进而Burning实验。
 

二. 本文提纲

    1. 摘要
    2. 本文提纲
    3. 项目起始
    4. 开发方式选择
    5. 系统构架
    6. 硬件设计
    7. 软件设计
    8. 系统调试
    9. 总结
 

三. 项目起始

    该系统是应兰州交通大学自动化研究所(一下简称为研究所)的项目需求进行开发。项目开始的时候谈的最多的问题主要有两个,一个是“钱”的问题,还有一个就是项目需求了。至于钱最后的商定结果为,天佑电子有限公司(以下简称天佑)只负责产品研发,开发过程中所有的元器件、材料、以及测试系统的所有成本均由研究所承担,而天佑则只承担人力成本。最后研究所应该支付天佑XXX研发费用。由于以前有过很多次的合作经历了,所以谈判过程也比较顺利,也没有书面的合同约束双方。不过这也仅仅建立在双方已经有过很多次的合作经历,彼此都是很熟的人,项目本身技术上没有太大的复杂度,而且最终产品的应用场合也不会造成大的损失的基础之上。不过一般的项目合同是很重要的一个方面,合同不仅仅为双方的行为建立起了约束,而且如果项目失败等造成损失,双方也能够明确自己的责任。至于项目需求,则只有以下几点需求:
    1. 信号通道数必须在50~100路之间(可伸缩)
    2. 单路信号误差限定在0.03V之内
    3. 数据传输使用485协议
    4. 数据协议遵循Modbus协议
    5. 传送数据中必须包含数据量以及CRC校验值
    6. 上电或者数据传送过程都需要有相应的指示灯指示其状态
    项目本身是个很简单的项目,几乎没有技术上的难点。产品需求也一目了然,看了项目需求之后解决方案就已经呼之欲出了。经初步分析,整个设计过程中唯一可能需要仔细斟酌的也就是多个板子之间通信时的时序问题,事实证明,这部分到后面的确测试了挺长时间才实现功能,而且测试了好多边界条件才最终确定下来最后的最优化参数。
 

四. 开发方式选择

    项目的开发方式选择了“今日标企业工作平台”和Github结合进行开发的方式。
    在本次开发中使用金目标的目的在于管控项目开发流程,实现整个开发过程简单的文档化。虽然该开发过程与现在所倡导的敏捷开发模式相违背,但是考虑到目前我们的开发条件,最后还是决定使用传统的文档化开发模式。在本次开发中由于参与开发的主要人员在不同的地方(软件开发在江苏,而硬件开发在广东),造成了沟通上的诸多不便,而文档化则会改善一些沟通上的困难。将所有的开发过程都存放在一个公共平台上,大家可以随时查阅。这样也最大化地降低了沟通歧义所带来的项目风险。
    众所周知,Github是一个不仅强大而且简单易用的分布式代码托管平台。对于不同地域开发人员之间的协作开发来说,拥有一个强大的代码共享平台至关重要。而Github刚好满足这些需求:
    1. 多方开发人员同时参与一个工程的开发
    2. 平台必须是分布式的,开发人员同时必须能编辑同一个文件
    2. 整个调试过程中各个开发人员的代码必须要保持一致
    有关这两个平台的使用方法,这里就不做介绍了,在各自的官网上都有很详细的教程。在这里只提供两个平台分别的链接。金目标的链接为:http://www.jingoal.com ,Github的链接为:https://github.com 。
 

五. 系统架构

   
 
  系统架构
 
  系统设计时考虑到可伸缩性,以及采集通道要求太多,故而将数据采集任务分给多个采集板去完成。每个采集板各自完成自己的采集任务,并将采集到的数据传送到数据总线。而具体的何时采集数据,以及何时传送数据到数据总线并交给PC段,则有Base Board控制。在这里设计的时候并没有将采集到的数据由Base Board传送到PC上,而是放到数据总线上直接由PC提取。这种设计是基于以下考虑,如果所有采集到的数据全都交由Base Board向上传送,则相当于多了一级路由,这样会降低整个系统的数据采集速率。也加重了Base Board的工作任务,使其控制逻辑变得复杂,严重违背“简单即为美”的编程原则。
 

六. 硬件设计

    有关硬件设计的部分,由于我本人对于硬件不是很擅长,所以可能总结不好。故而请了我们团队中的另一位硬件工程师撰写。在这里我就只上两张设计的电路图。
采集板电路图
 
基板电路图

七. 软件设计

    在本系统中软件设计分为两个部分,采集板软件设计和基板软件设计。下面分别就这两方面介绍整个软件开发的过程。
    1. 采集板软件设计:
    在确定了采集板功能的情况下再开始软件设计。本次设计中采用了“从两端到中间”的软件开发模式。所谓“从两端到中间的开发模式”是指在软件开发过程中,首先按照软件的架构层次习惯,将软件分为前端界面、中间逻辑控制层、以及底层的模型层(这是PC软件的开发框架)。而对于我们嵌入式软件来说,也可以分为类似的层。我在这次开发过程中,则是将整个软件分为:业务逻辑、中间件、以及底层驱动模型。而从两端到中间的开发则是指先开发底层驱动,然后再确定 业务逻辑 ,然后通过中间件将两个部分衔接起来,并进行测试的方法。
    首先根据需求确定软件中需要使用到的底层硬件驱动分别有:AD、USART、DMA、GPIO、外部硬件中断。而在嵌入式系统中,一般还需要一些延时之类的功能,故而还需要提供一些延时函数的驱动。下图显示的是本次设计中用到的所有驱动,虽然开发过程选择了使用STM32官方提供的固件库编程方式,但是如果完全使用固件库中的函数,而没有经过封装。则主函数将会显得很臃肿,而且整个软件系统的业务逻辑和底层驱动也严重耦合在一起,不便于以后系统维护、升级,也会大大降低系统的可移植性。
    这里的驱动设计,其实从本质上来讲的话只是对固件库中的一些函数或者是宏定义进行进一步的封装。例如ADC部分的驱动,在~/ADC/adc.c文件中只需要系统一个Adc_Init函数,在该函数中完成对CPU ADC资源的初始化。而对应的USART设备。不仅要提供初始化函数,还需要提供一些发送数据以及接收数据等的函数。而且为了调试方便期间,一般按照我个人的习惯的话还会添加一部分代码,用以支持printf函数,利用该函数通过串口向PC端,打印出一些程序执行的信息,这样会大大方便嵌入式软件调试。对于STM32来说,要支持printf函数只需要加入以下代码:
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
int handle; 
 
}; 
 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
return ch;
}
#endif
View Code

驱动文件列表
 
    完成了驱动程序的设计之后,则设计业务逻辑。 业务逻辑 是指从用户需求的角度完成用户需要的功能的一部分代码。这部分代码一般都是指Main函数中的逻辑。在此次设计中主要的执行流程可以使用以下的图表示。
 
业务逻辑
 
    最后设计的是中间件,中间件的设计不仅要考虑到业务逻辑的需求,更要兼顾底层驱动模型的实现方式。只有将这两者比较好地结合起来,最终才能得到比较理想的产品。
    从底层往上层看,中间件是对底层驱动更高一级的封装,底层驱动只是实现独立的单个功能点。而通过中间件不仅实现了这些功能点,而且融入了一部分系统功能的成分在里面。从上层往底层看,中间件则可以理解成对于系统业务逻辑的细化。业务逻辑类似于将用户需求文本化或者“代码化”,从本质上来讲,它还是在用户需求一级。而通过中间件的作用,则将抽象的业务逻辑实现了具体化,成为对于处理器来说能够识别并执行的真实代码。
    以上就是采集板的整个软件开发思路以及开发过程,这里的程序从其流程来看,的确是几乎没有复杂度。但是在这里提出了一些软件开发过程中的框架,一种分层机制。框架的出现将系统级的软件分成各个不同的模块,或者分为不同的层。从而实现了软件良好的可维护性,也大大方便了后期系统升级。对于软件框架的理解,我也只是一个初学者,在这里提出权当是抛砖引玉了吧。
    1. 基板软件设计:
    从软件的角度来讲,基板是整个系统时序的控制者。就仿佛人的大脑一般,它可以发出各种指令让系统的各个部分能够各司其职,而不会导致系统紊乱。本次设计中,基板主要的工作流程如下:
基板的主要工作流程
 
    设计过程中基板中有一个唯一值得注意的地方就是板子复位后在线板的检测,在这个系统中挂在基板上的采集板的个数在0~10个之间是随意的,而控制板发送采集信号命令的时候应该只对当前系统中已经挂载的采集板发送。故而在设计之时,软件中设计了一个链表,大家知道链表是一个大小可以动态分配的数据结构,所以我们可以讲在线板的信息存储在这个链表当中,而每次需要发送控制命令的时候只要遍历整个链表,并发送控制命令即可。
 

八. 系统调试

    在完成了系统硬件设计以及软件设计之后,接下来的主要任务就是单个模组测试以及系统测试。测试过程主要地可以分为以下几个部分:
    1. 单个驱动功能点测试
    2. 采集板功能测试
    3. BaseBoard功能测试
    4. 系统功能测试
    5. 系统稳定性测试
 
    下面分别介绍此次开发过程中针对前面5个测试点的具体实现过程。
    1. 单个驱动功能点测试:
    单个驱动功能点测试是指在测试单个功能块,比如在系统中用到的串口通信模块、GPIO模块、中断模块等。包括硬件电路测试和软件驱动测试。这部分的测试对于整个系统测试来说是一个基础测试,但也是至关重要的部分。只有保证了所有单个功能点能够正确地工作才使得后续系统逻辑无误成为可能。根据前面的介绍,在本次设计过程中,用到的所有驱动全都封装在相应的驱动文件中,并且单个功能点都非常独立。前面所有的驱动都能够独立出来形成一个完整的测试用例。而我的做法则是先创建一个通用的测试模板(一个没有实现任何功能只包含main函数的工程)。在测试过程中只需要分别将需要测试的驱动移植到该测试工程中,并且在main函数中调用驱动中提供的函数进行测试即可。
    2. 采集板功能测试:
    在本系统中,采集板的主要任务是根据基板的控制命令,完成采集数据的动作,并将采集到的数据按照之前的协定进行整理,然后传送到基板数据总线。所以对于基板的测试,则需要再使用一个模拟基板控制流程的电路板,或信号发生器之类的控制器。而我的做法是使用了一个51板,该板只完成产生一个定时跳变的功能。
采集板功能测试
    3. BaseBoard功能测试
    对于基板而言,测试的关键有两个方面:
        1> 检测在线板,并将在线板信息保存在队列中
        2> 按照队列中的信息,将控制信息发送到采集板中
    其测试过程反而比较简单,只需要将测试的信息全部通过串口发送到PC段,根据PC端的显示信息即可判断是否实现了需要的功能。但是在测试的过程中发现了一个问题:串口要使用printf函数向上位机发送信息,则需要实现前面所示代码,但是这部分代码与工程中使用的malloc函数所在库stdlib冲突,故而产生了编译不过去的问题。也就是说在一个工程中这两个功能只能使用其中一个。而malloc函数是实现双向链表的关键,不能却去掉。所以我选择了自己实现一个向上位机发送字符串的方法。具体实现的代码如下:
void Send_Data(USART_TypeDef* usartx, uint16_t data)
{
USART_SendData(usartx, data);
}
 
void Send_String(USART_TypeDef* usartx, char *str)
{
while(*str != '\0')           //判断字符串是否发送完毕
{
Send_Data(usartx, *str);        //发送单个字符
str++;                 //字符地址加1,指向先下一个字符
delay_ms(5);
}
}
View Code

 

    4. 系统功能测试
    在完成了前面几个部分的测试之后,对于后面系统功能测试,其实需要做的工作就少了很多。基本上只需要处理好整个系统工作时的时序即可。
    测试的过程中,首先不是将所有的采集板全都挂载到基板上去测试,而是只挂载一个测试基板和采集板的协同工作是否正确。这部分的测试大概使用了4个小时的时间,期间调整了两个板子同时上电后的时序,这个时序是通过延时来控制的。基本的流程是上电之后采集板完成系统初始化之后,置位在线信号(置位这个信号之后基板就能够检测到采集板是否在线,如果采集板不在线,对应的端口呈现的状态应该是低电平输入),然后以个比较长的延时(我用了大概5S的时间),而基板则是完成初始化之后也是在一个比较长的延时(我选择2S)之后,再检测采集板的在线情况。使用这样的时序,就可以保证只要整个系统同时上电,能够安全采集到在线采集板的信息。这里虽然是一个比较简单的逻辑,但是设计之时由于忽略了两个板子上电之后完成初始化的过程所使用的时间相差比较大,在经过了很多的测试,并评估之后还是决定采用长延时这种安全的方式去处理上电后检测采集板在线信息的任务。
    完成了在线检测之后,就能够确定在线板的信息已经存在于前面设计的队列之中了。后面需要测试的就是能否根据采集板的在线信息发送了控制指令之后采集板是否能够采集到数据并传送上上位机上。经测试这部分的逻辑很容易实现,只要发送了控制信息,采集板就能够发送控制信息到上位机上,但是发送了信息之后对于基板而言,还需要做的工作是等待采集板给它发送闲信号。基板的设计使用了while等待的方式,发送完控制命令之后一直等待直到收到当前工作的采集板发送了闲信号给它之后才推出循环,再对下一块采集板发送控制指令。如此循环,便能实现一直轮询采集的任务。
    在完成了单个采集板的系统测试之后,就需要测试同时挂载多个采集板时系统的工作情况了。在测试的过程中发现了一个问题,采集板的个数比较少的时候系统才能正常工作,但是当个数超过三个的时候指示灯显示系统是在正常采集数据,但是485就是无法接收到数据。怀疑为所有模块都为发送使能,有可能是相互之间的干扰造成数据通讯异常,建议软体方面改变使能端口状态,当空闲时关断使能,将总线释放,用示波器实测AB输出波形发现,当单个模块介入系统,不进行数据通讯时A为“1”,B为“0”,当进行数据通讯时,A变为“0”时,B将变为“1”,高电平值3.3v,当接入2个模块,不进行数据通讯时A为“1”,B为“0”,当进行数据通讯时,A变为“0”时,B将变为“1”,但A的低电平值变为1.56v,而B的高电平值也变为了1.56V。从此可以推断,非通讯模块要将A上拉的3.3V,B要下拉的0V,而通讯的模块想让A变为0V,B变为3.3V,并联的模块越多,电平值越小,直至AB差分判断失效,造成相互干扰。切记一点:但不使用总线时要释放总线,防止相互干扰。
    经过以上的测试,就能够确定从功能的角度来讲,系统是没有问题了。稳定性还有待测试。
    5. 系统稳定性测试
    系统稳定性的测试,无外乎就是测试系统长时间的工作是否能够依然正常。因为系统长时间工作会造成系统发热,而如果热量过高,则有可能导致整个系统阻抗以及容抗等都发生变化,造成系统不能正常工作。经测试系统工作20小时以上功能依然能够满足,电源部分会有一点热。经过再三考虑,最后还是加了一个散热风扇(使用了普通PC机上的散热风扇)。再加了风扇之后发现电源部分的发热现象明显有了好转。最后经过48小时的烧机实验,确定系统无误!!!
 

九. 总结

    以上就是整个这次《信号采集系统》的开发过程。就如在前文所说,这个系统是一个很简单的系统,没有任何技术难点可言。写这篇博文,只是想抛砖引玉,希望大家以后可以多推出类似的关于开发流程管理等的文章,大家共同学习进步。在开发的过程中肯定还有很多不完善的地方,非常期待有这方面爱好的朋友能够提出宝贵的意见,以便我改进。最后再上几张产品的图片吧。