• IIANews微官网
    扫描二维码 进入微官网
    IIANews微信
    扫描二维码 关注微信
    移动客户端
  • English
2026全景工博会 菲尼克斯电气二级-以太网
I/O 设备

系统硬件参数提取可以实现最大限度的设计复用

  2001年04月29日  

软件设计复用是许多公司多年来追求的崇高目标。事实上,要在较高水平上真正实现设计复用还存在下列障碍:微控制器的差异、在一个项目中会有多个微控制器以及硬件的修改等。数据源的改变通常迫使代码也要有较大的改变,遵循本文概述的设计技巧将有助于增加设计复用的程度。

Joseph Lemieux

高级应用专家

EDS Embedded Solutions公司

E-mail:x@

从软件应用程序中实现硬件参数提取有三种方法。第一种方法,称之为函数名方法(function name method),这是一种直接I/O访问方法,它对于每个输入和输出都使用一种不同的函数。这种方法最快而且最容易使用,但当硬件发生变化时也最难于改变。第二种方法对于直接I/O使用API(应用编程接口)。这种方法更灵活并且更容易端接不同的硬件,但对资源的消耗会有所增加。最后一种方法称之为消息(message)方法,这种方法是间接I/O的一个范例,其中,I/O通过使用消息系统来获得,如在OSEK/VDX中建立的消息系统。在数据既来自于硬件也来自于其他器件的复杂环境中,这种消息方法运行良好,其代价是增加资源消耗。I/O层是本文的基础,在消息方法的讨论中,将通过消息系统把I/O层从应用任务中分离出来。

函数名方法

在函数名方法中,为每个输入或输出都定义了一个或一组函数,这种方法的名称也就来源于此。这些函数提供一个输入的值或设定一个输出的值。另外,需要为输入编写采样例程,需要为输出编写驱动例程。使用这种方法需要如下步骤:

  1. 在应用程序中定义所有的输入和输出;

  2. 为每一个输入和输出定义一个访问函数,也可以选择定义一个宏;

  3. 为所有类型的输入创建其所需的采样例程。理论上每个例程可以对很多输入采样;

  4. 为所有类型的输出创建其所需的驱动例程。同样,理论上每个例程可以驱动很多输出;

  5. 创建支持例程,例如去抖动例程、滤波例程、缩放例程等等。这些例程也可能已存于一个库之中。

这个方法将允许应用程序源代码的设计复用。然而,I/O层将使用借用的代码而不是复用代码,对于每个应用程序借用代码将被重新编写。

a) 定义输入和输出

应用这个方法的第一步是定义系统所有的输入和输出。这些输入和输出可能会包括数字输入和输出、模拟输入和输出以及时基输入和输出等。这些输入和输出应该与所有的需求一起列在一张表中,这些需求包括范围、去抖动、滤波、缺省值等。

然后,为每一个输入和输出定义一个访问函数,该函数或宏将一直采用函数的原型,如:type inputfunction(void) 或 void outputfunction(type),其中,type是包含输入的全部范围和粒度(granularity)的类型。对于数字输入,这通常是布尔型,在大多数应用中,它等同于无符号字符型(unsigned character)。它可能是C语言中的任何整数或浮点类型,也可能是一个结构。函数的返回值总是与输入或输出的值有关。如果其他应用任务需要读取当前的输出值,输出可能有两个函数。以下是一些访问函数名的例子,对于每个输入和输出需要用某个名字来替换其中的“NAME”。

定义了函数之后,就需要创建函数了。如果定义的是一个宏,定义和创建是同时的。以下的程序清单提供了前面每个例子的例程范例,其中包括了宏。然而,在最终产品中将会只有函数或只有宏。

b) 创建输入采样例程

下一步,创建用于确定输入值的采样例程和中断处理例程。在一个函数调用中,每个例程应该能对尽可能多的输入采样。以下的程序清单描述了一些输入类型和如何处理这些输入的范例。程序清单5中演示了采样一个8位宽数字输入端口并去抖动的范例。如果设计了硬件来优化这个算法,一次就能对8个输入去抖动。DebounceDigital例程将从采样端口和结构中得到信息以确定去抖动的值。该结构中包括8个定时器,每个输入端口一个。

c) 创建输出驱动例程

当直接写输出不可行时,就可以用输出驱动例程来输出数据,例如对于必须在每个边界产生中断的计时器输出,或者对于需要周期性刷新的输出。程序清单7中演示了在Intel 87C196处理器中的一个频率输出驱动程序。这个中断例程调用可设定下一个中断记时的频率处理例程。结构中的元素output->time_register是一个指向实际比较寄存器的指针,元素output->period是切换输出的时间间隔。只要进行了比较,就必须设置EPA0使其切换输出,这里假设输出是一个占空比为50%的频率输出。因此,output->period是输出信号的周期。的值由以前的SetFreqOutputValue()函数来设定。

d) 创建支持例程

最后,创建用于去抖动输入、滤波输入和缩放输入或输出的支持例程。这些例程实际上都很普通,所以这里就不予讨论了。然而,应当关注不使用任何全局变量的类例程。在进行函数调用时,所需的全部信息都会被传递给这些类例程。

函数名方法的优缺点

函数名方法的优点是访问数据的速度很快。读包括直接读全局存储器,或者通过快速单函数调用。写包括直接写端口、写全局存储器,或者通过快速单函数调用。编写读和写函数很直接也很简单。

其缺点是难于配置、代码设计复用能力较差、容易出错。当输入源改变时,许多代码段可能需要重写或者需要定义新的宏。定义新的宏还需要重新编译。调试宏也比较困难。

API调用方法需要开发类API,它可被使用包含I/O信息结构的应用程序调用。实际为每个新应用编写的代码数量很少或根本就没有。实现API调用方法有两个步骤:一次代码开发和与应用程序集成。

一次代码开发包括:

  1. 对你们公司所用的I/O类型进行分类;

  2. 对每种类型的配置进行定义。把会改变的数据定义为RAM类型,将常量数据定义为ROM类型。

  3. 为每个I/O定义用于创建ROM和RAM变量的宏;

  4. 创建用于访问、采样和驱动I/O的例程。

与应用程序集成包括:

  1. 在配置文件中创建输入和输出的每个实例;

  2. 在你建立的脚本或make文件中包含I/O文件;

  3. 在所有访问I/O的应用程序模块中包括io.h文件;

  4. 在每个应用程序模块中使用在配置文件中定义的名字访问I/O;

  5. 在系统中调度采样和驱动任务。如果I/O函数不能被RTOS直接调用或不能包装在ISR(中断服务程序)中,这是唯一可能需要编写的代码。

在这一步中,需要对你们公司所使用的每种类型的I/O进行定义,并对参数进行分类。例如,离散输入可能被定义成来源于数字或模拟端口,将会被去抖动,并在上电时有一个缺省值。其他类型的I/O可能包括离散输出、模拟输入/输出、PWM输入/输出、频率输入/输出、多状态输入/输出等。

对于上述的每种类型,定义一个变量(RAM)和常量(ROM)结构,该结构将用于某种输入类型的每个实例中。

API函数调用中将会用到IOHANDLE定义。在RAM结构中,rawvalue是输入的最后采样值;debouncedvalue是输入的去抖动后的状态;time是每个rawvalue为了去抖动而保持不变的时间。每个RAM结构可以保留8个数字输入。ROM结构中包括:digitalport,它是指向数字端口的指针;mask,它是在端口中对应于输入的屏蔽位掩码;debouncetime,它是在采样时用于输入的去抖动时间;ramlocation,它是在用于保持特定输入的变量DigitalInputRam数组中的偏移量; rammask,它是用于抽取数字输入RAM位置的掩码;defaultstate,它是输入的缺省状态。同样地,还要创建以前定义的每个输入和输出类型定义。

此外,要定义能够创建每个输入和输出的实例的宏。这些宏封装了结构的定义,因此允许修改结构以适应API中的改变。

ROM宏为每个数字输入定义了常数结构。每个数字输入都有一个该宏的范例,并会有一个唯一的标识名。结构中的元素port是在别处定义的全局名,并且它对所用的微处理器是确定的。debouncetime参数是用于去抖动输入的采样数。position和rammask是在成组的RAM中相对于RAM数组的偏移量以及数字输入的掩码。position和rammask的组合在配置文件中只能出现一次。最后,default是将用于输入RAM数组中的缺省值。

创建访问、采样和驱动例程

访问函数应该是类属(generic)的并允许用同样的界面访问多个输入和输出,只要在访问时采用不同的参数。为了扩展基于模拟的数字输入,例程应该能确定送到参数表中的参数类型。这需要额外的参数或包含输入类型的结构。为简单计,假设GetDiscreteState将只返回数字输入的数据。

在配置文件中创建每个实例

需要为应用程序创建一个名为iocfg.h的配置文件。该文件被包含在所有应用程序模块都使用的io.h文件中。然而,宏CREATE_MEMORY必须只能在io.h文件中定义,否则连接时会出错。配置文件中将包括所有宏,这些宏用于定义应用程序中的所有输入和输出。

最后一步就是把采样和驱动例程集成到RTOS中。周期性采样输入的每个采样例程都需要调度。这些例程的优点是可以不同的频率分别对单独的输入进行采样。在函数名方法的例程中要求以相同的频率对所有的输入和输出进行采样。这可能需要编写一个shell(命令解释程序)例程多次调用该函数,每个端口调用一次。通过使用一个终止条件为空的while() 循环可以使其更有效。还必须开发应用程序特定的ISR,并调用API例程。

API调用的优点是当填加I/O时几乎不需要编写、删除或修改代码。只有配置文件中使用宏的常量需要修改。因为每个函数接收一个空指针,而且引用该指针的结构改变时不会影响生成的应用程序代码,所以这种方法具有较高的可复用性和可移植性。其缺点是轻微增加对ROM和CPU的使用。然而,由于没有用基准程序来测试其差别,这种方法也有可能会减少总的ROM使用,因为将函数集成到API中比创建多个函数更为有效。

消息方法及其优缺点

除了消息方法使用IPC(RTOS的进程间通讯)系统传递数据的状态,它几乎和API调用方法一样。COM规范中的OSEK/VDX无队列消息就是一例。(参见有关OSEK/VDX的论文“SEK/VDX Standard: Operating System and Communication”,2000年3月,第90页;以及“SEK/VDX Network Manager and Implementation Language”,2000年4月,第96页)。无队列消息保留了最后值,并且可以在不丢失该值的条件下“接收”多次。

消息方法和API调用方法的不同之处如下:

  1. 类型定义改变较少;

  2. 用ReceiveMessage代替了API函数Get;

  3. 用SendMessage代替了API函数Set;

  4. 需要为所有输入定义消息。

消息方法能够清楚地提取数据源,无论该数据源是真正的硬件输入或输出,还是从网络上接收来的数据,复杂的消息如结构或串易于接收,而如果所有的数据都是简单的类型(如U8、U16、浮点,等等),则API调用工作得最好。这种方法的缺点是需要使用更多的RAM、ROM和吞吐量,因为增加了另一层消息提取。

本文总结

硬件参数提取是为了实现应用软件代码的可维护性。如果存在一种访问硬件的标准方法,那么,某一部门中的所有软件工程师就都可以维护代码。当硬件发生改变时,硬件参数提取只需在一处进行改动,而不是在每一个源代码模块中都要进行改动。通过复用代码和硬件参数提取,项目中新增的或修改的代码行数减少了,因而可以减少缺陷的数目。


最新视频
以AI赋能仿真技术 金发科技携手西门子提速汽车材料研发   
教程 | 和利时脱硫工况异常处置Agent   
绿电直连 算力新局——AIDC算电协同线上沙龙
工业 AI 赋能电子/半导体产业 智能制造升级
PTC Creo 13 设计未来型产品
PTC Codebeamer
专题报道
《我们的回答》ABB电气客户故事
《我们的回答》ABB电气客户故事 ABB以电气问题解决专家之志,回答未来之问。讲述与中国用户携手开拓创新、引领行业发展、推动绿色转型的合作故事,共同谱写安全、智慧和可持续的电气化未来。
企业通讯
2026全景工博会
2026全景工博会

2026中国国际工业博览会(简称“中国工博会”)定档于2026年10月12日-16日在国家会展中心(上海)举办。

探索工业AI未来——研祥集团智能物联业务新品发布会
探索工业AI未来——研祥集团智能物联业务新品发布会

新型工业化是当前我国制造业发展的核心战略方向,作为工业AI赛道头部企业,研祥通过旗下研祥智能、研祥金码、亿万克三大品牌,

在线会议
热门标签

社区

null /ca/_01-ABC00000000000096534.shtml /ca/_01-ABC00000000000096534.shtml