面向应用对象的嵌入式终端构件设计方法研究*

2018-12-07 08:32,,
单片机与嵌入式系统应用 2018年11期
关键词:原理图嵌入式构件

,,

(1.昆山鑫盛盟创科技有限公司,昆山 215300;2.苏州大学;3.武夷学院)

引 言

嵌入式系统终端构件是具有通用功能的嵌入式外围设备的软硬件实体总称[1]。它们负责实现嵌入式系统的信息采集、显示、交互、控制等具体应用功能。这类应用对象具有一定的通用性,因此终端构件可复用可移植性显得相当重要。当前终端构件的复用和移植效率还处于较低的水平。这其中有嵌入式系统本身特性的因素,也有设计规范化的问题。目前市场上终端构件销售商通常是将工程师提供的一系列零碎的源码、文档、参考手册、工具等十几甚至几十个文件打包和硬件一起提供给用户,用户面对一堆杂乱无章的文件,通常需要花费大量的时间来寻找、梳理与自己需求相关的线索。从这侧面可以看出大多数嵌入式工程师的目的只是开发出产品实现功能,构件设计过程往往是依经验行事,对于底层驱动程序设计没有彻底贯彻软件工程思想,开发过程缺少规范化的文档管理等。这些因素都直接导致了终端构件可复用性和可移植性较差。

近些年来人们参考通用计算机软件工程体系,将基于构件化的软件开发(Component-Based Software Development,CBSD)思想引入到嵌入式系统设计当中。Kopetz等人提出了嵌入式软件组合构建理论,明确了构件化设计、组合构造的思想和原则,并建立构件可组合设计的理论基础[2-3]。参考文献[4]提出了一种嵌入式系统代码合成技术,通过增加中间层,屏蔽硬件和平台特性。本文针对嵌入式系统终端构件设计可移植性差等问题,从面向应用对象的角度,厘清终端构件共性和个性知识要素,建立终端构件概念模型,从硬件原理图绘制到驱动程序设计一系列过程,提出一套符合构件化思想、层次清晰、封装合理且与MCU无关的终端构件一般性设计方法,并通过一个液晶屏终端构件设计实例来阐述并验证其合理性和有效性。

1 嵌入式终端构件基本概念

图1 嵌入式系统构件层次模型

构件是系统中模块化、可部署和可替换的部件,构件封装对外提供一组接口[5]。嵌入式终端构件也称底层外设构件或应用构件,在嵌入式系统中属于最外层次的构件,按照生产者和消费者关系,终端构件只有需求接口而没有供给接口。需求接口接受其中间构件或者核心构件提供的服务,如键盘模块、LED或LCD显示模块等。终端构件可以调用底层内部构件,如LCD可调用GPIO、SPI等。终端构件通常对应着一种或多种具体应用功能,如嵌入式系统的信息采集、显示、交互、控制等功能。嵌入式系统构件层次模型图如图1所示。

2 面向应用对象的终端硬件构件原理图绘制

2.1 终端硬件构件概念模型

嵌入式硬件构件(Hardware Component,HwC)是指将一个或者多个硬件功能模块、支撑电路及其功能描述封装在一起,提供一系列规范的输入/输出接口的可重用的硬件实体[6]。一个完整硬件构件通常包含三大部分:构件描述、构件实体、构件接口[7]。构件描述是一个硬件构件的说明文档,主要内容包括硬件名称、主要功能、引脚说明及注意事项等。一个终端硬件构件可表述为如下模型[8]:

HwC={构件实体,构件描述,接口描述};

构件实体={封装的硬件实现};

构件描述={构件的功能描述,构件注意事项};

接口描述={接口标识,接口网标,接口含义,调用说明}。

2.2 硬件原理图绘制基本规则

硬件构件的概念模型可通过硬件原理图进行表述。为了清晰表达硬件构件概念模型,在绘制原理图时,需遵循以下基本规则:①同类型元器件命名时以相同字母为前缀并进行编号,如电阻名称为R1、R2,电容名称C1、C2等;②应给出详细的文字描述,包括构件中英文名称、功能描述、接口描述以及注意事项等,以提升硬件构件的可读性;③用虚框封装硬件构件的电路及文字描述,虚框之内为硬件构件的实体;④标明硬件构件的对外输入/输出接口,接口标识分为接口注释和接口网标两种,接口注释用来描述接口功能,标于虚框之内,接口网标则标于虚框之外,用来表示电路连接特性。

对于终端构件而言,因其只有需求接口,硬件设计时首要考虑的问题就是该构件需要什么样的信号才能工作。因此绘制终端构件原理图时,没有对外接口网标而只有接口注释。图2给出一个典型LCD终端构件的原理图。一个遵循规范绘制的原理图,可读性强,与之相对应的驱动构件修改方便,可有效提高移植效率。

图2 LCD构件原理图

2.3 驱动构件与原理图的对应关系

构件是软硬件综合体,硬件引脚连接只是最基本前提,要实现构件的正常功能,必须有构件驱动程序进行配合。引脚是硬件构件对外的唯一接口,与之对应驱动构件要有相应的代码表达出原理图的含义。从终端驱动构件可移植角度出发,驱动构件应面向应用对象也就是构件本身进行编程,实现这一目的必须在驱动程序的头文件中添加引脚映射功能。通过引脚宏定义映射,可以屏蔽不同MCU引脚的差异性,驱动程序编程只需针对终端构件的引脚展开即可,无需关心不同型号MUC引脚的区别。构件复用到相同型号的MCU不需要更改任何内容即可直接复用,如果移植到不同型号MCU为核心的应用系统当中,只需改动头文件引脚的映射关系,对于驱动程序来说,则可做到最小的改动即可实现高效移植。例如图2中原理图中基于恩智浦KL25微控制器的引脚映射,定义如下:

#define LCD_CLK (PTA_NUM|15) //LCD时钟

#define LCD_SDI (PTA_NUM|16) //LCD主出从入

#define LCD_RS (PTA_NUM|4) //LCD复位

#define LCD_DC (PTA_NUM|12) //LCD数据/命令

#define LCD_SDO (PTA_NUM|17) //LCD主入从出

3 面向应用对象的终端驱动构件设计

3.1 终端驱动构件概念模型

终端构件是包含硬件构件和驱动构件的完整软硬件实体。硬件构件生产通常都会遵循一定的标准规范。从某种意义上说,驱动构件设计更大程度地决定了该终端构件的可复用和可移植性的高低。驱动构件是直接面向硬件操作的软件程序及相应的说明文档。驱动构件直接和硬件打交道,从软件工程角度来看,驱动程序必须与核心构件具体型号的MCU无关,而是面向终端构件本身,这样才具有复用和移植价值。终端驱动构件设计的关键问题是如何对这类构件的共性和具体应用系统的特性进行分析,抽象出该构件对象的属性和对外接口[9]。

终端驱动构件(Terminal Drive Component,TDC)包括头文件(.h)、源程序文件(.c)文件、文件描述、接口描述等,其概念模型可表述为:

TDC={头文件,源程序文件,文件描述,接口描述}

头文件={具体硬件引脚连接映射关系,功能函数声明,全局常量定义}

源程序文件={函数功能实现}

文件描述={头文件标注,源程序文件标注,函数头注}

函数头注={函数名称,函数功能描述,返回值类型,参数描述,注意事项}

参数描述={参数列表,参数方向,参数含义}

用户在使用构件时,可以通过头文件描述的构件完整信息进行接口调用,而不必关心封装在构件源程序中的服务实现细节。一个设计良好的构件被复用或者移植到不同嵌入式系统中,通常只需修改少量头文件,对于源程序尽量做到少改甚至不改。

3.2 终端驱动构件封装基本原则

为了便于复用和移植,驱动构件设计应遵循以下基本原则和规范:

① 命名一致性原则。驱动构件的头文件和源程序文件的主文件名必须与驱动构件名一致,构件的属性和内外部函数命名统一以构件名+属性名/操作名形式,命名应有见名知义的效果。

② 通信内聚性和分层内聚性原则。内聚性指的是构件内部函数功能清晰单一明确,只负责一组相关的操作。通信内聚指的要有专门负责构件的底层通信函数;分层内聚指的是函数设计应有层次化,高层函数能调用底层函数,底层函数不能调用高层函数[10]。

③ 内外有别原则。构件函数分为内部函数和外部函数,内部函数仅限构件内部调用,外部接口函数是外界操作构件的接口,外部程序访问构件属性必须借助外部接口函数进行,不可直接访问构件属性。

④ 松耦合原则。构件应严格限制全局变量的使用,且所有数据传递都要通过函数的形参进行,这样编码既简洁高效又安全可靠。

4 LCD终端构件设计实例

TFTLCD即薄膜晶体管液晶显示器,是一种常用液晶显示器件。下面我们利用上文所述的设计思想和基本原则从原理图绘制、公共要素分析及头文件设计、驱动要素分析及函数设计等方面给出一个面向应用对象的TFTLCD构件分析和设计完整过程。

4.1 LCD构件原理图绘制

LCD的硬件对外唯一接口是其连接引脚。因此原理图的绘制用虚线封装其内部结构,虚线内部给出引脚的基本含义,虚线外部给出对应的接口网标,本例LCD引脚连接的网标为恩智浦公司的Kinetis 系列KL25微控制器[11]。同时在虚拟框内部给出详细的构件说明,包括构件中英文名称、各引脚功能和使用说明以及注意事项等,如图3所示。这样在系统移植过程中就可快速进行引脚的连接。

图3 LCD构件与KL25系统连接原理图

4.2 LCD构件公共要素分析及头文件设计

要提高LCD构件的可移植性,就必须实现面向应用对象的设计和编程,屏蔽不同型号MCU的差异性。因此须在LCD构件的头文件lcd.h中对给出构件的引脚连接的MCU的引脚进行定义,使用宏定义描述硬件连接线,且每个接线都单独宏定义,更具普适性。

除此之外,构件的头文件应给出文件头注,包括文件名称、功能概要、版本号、编写者或修改者姓名、最后修改时间等信息。头文件包含(#include)为LCD构件提供服务的核心构件或者中间构件头文件。LCD驱动构件的函数声明都放在lcd.h头文件。LCD驱动构件内部函数和外部接口函数分开声明。LCD构件使用的一些全局常量,如屏幕宽高以及颜色类型都在lcd.h头文件中进行定义。

4.3 LCD构件源程序文件设计

LCD构件源程序文件lcd.c和头文件一样,给出文件头注释,包含内容与头文件的头注类似,每个函数也都必须给出函数头注,函数内部代码关键语句也应有行注释或边注释,提高了程序可读性。终端驱动构件函数从属于驱动构件,驱动函数的命名除要体现函数功能之外,还需要体现其从属构件不同的实现方式,如LCD_Init(LCD初始化)、LCD_ShowImage(LCD显示图片)等,避免构件函数出现同名现象,同时函数名也要能够“顾名思义”。

4.4 LCD构件驱动要素分析及函数层次化设计

(1)驱动要素分析及通信函数设计

LCD构件能够运行的基础是LCD与系统核心MCU正常通信。TFTLCD采用的驱动控制器型号为ILI9341。构件驱动程序主要工作就是和ILI9341芯片打交道,MCU与ILI9341之间采用串行外设接口(Serial Peripheral Interface,SPI)方式通信。SPI通信需要考虑驱动芯片各引脚数据传输的时序问题,对ILI9341驱动芯片的初始化涉及对不同寄存器进行操作,是一系列的复杂过程。对于一般嵌入式开发人员,并不希望了解其复杂内部机制,而是只要少量驱动代码修改便能移植使用。

LCD构件的中上层函数如初始化、显示等上层功能部函数的运行均需建立在通信函数正常运行的前提之下。根据构件的通信内聚性和分层内聚性原则,给出了构件专用底层通信函数代码如下(按照命名一致性原则,函数命名统一采用LCD_操作名):

//发送8位数据

void LCD_wr_data(uint_8 data){

GPIO_set(LCD_DC,1);

//LCD_DC高电平表示发送数据

SPI_send(0,data);

//使用0号SPI模块发送8位数据

}

//发送8位寄存器命令

void LCD_ wr_reg (uint_8 data){

GPIO_set(LCD_DC,0);

//LCD_DC低电平表示写寄存器

SPI_send1(0,data);

//使用0号SPI模块发送8位寄存器命令

}

可以看出,数据通信函数调用了GPIO 构件和SPI通信构件。GPIO 构件用来实现MCU引脚的初始化和状态设置;SPI通信的初始化必须按照LCD驱动芯片ILI9341设定的时序进行。根据ILI9341参考手册[12],该芯片使用的是数据发送低电平空闲,上升沿取数模式,对应的时钟极性CPOL及相位CPHA选择均为0,因此将引脚初始化和SPI构件初始化如下:

GPIO_init(LCD_RS,1,0);

GPIO_init(LCD_DC,1,0);

SPI_init(SPI_0,1,6000,0,0);

说明:GPIO_init是GPIO构件初始化函数,分别将LCD_RS和LCD_DC设置为输出引脚,默认值为低电平。SPI_init 为构件初始化函数,参数SPI_0为0号SPI模块;1为MCU为通信主机;6000为通信波特率;最后两个参数 0、0分别为时钟极性及相位。这些通信的关键要素必须在文档中交代清楚,以便移植时参考。终端构件在移植时,除了引脚连接和宏定义映射外,作为功能实现的基础,驱动构件的通信函数是极为关键的,必须予以高度重视。

(2)中上层功能函数设计

有了底层通信函数作为基础,其他中上层功能函数就可实现具体功能,例如LCD的初始化函数、LCD显示区域设置函数等都是对ILI9341寄存器一系列的设置,过程相对比较复杂,但是每一种模式通常都遵循固定的顺序步骤,需根据ILI9341参考手册调用底层通信函数LCD_wr_reg等对寄存器进行一系列的设置,设计时将具体的步骤封装,对外屏蔽其复杂的写寄存器操作,必要时通过参数设置入口,开发人员只需调用即可,该类函数借助通信函数实现其操作与构件底层通信无关,易于复用和移植。下面是两个常用功能函数样例:

void LCD_Init(void); //LCD构件初始化

void LCD_AreaSet(uint_16 x1,uint_16 y1,uint_16 x2,uint_16 y2);

//显示区域设置

其中,LCD_AreaSet函数中x1、y1和x2、y2是确定显示矩形区域的对角线两点坐标值。

LCD具体应用功能(如画点、画线、画圈、显示文字、显示图像等)需要综合考虑,如何进行封装和设计,方便用户使用。鉴于篇幅,下面以显示图像函数LCD_ShowImage为例进行说明。显示图像通常需要知道显示位置、图像尺寸和图像的点阵信息,因此LCD_ShowImage函数可设计如下:

void LCD_ShowImage (uint_16 x,uint_16 y,uint_16 width,uint_16 height,uint_8 *image);

其中,x、y是图片显示的起始坐标,width 和height分别为图像的宽度和高度,image则为存储图像的点阵信息数组首地址。函数内部也是通过调用通信函数来实现图像的显示,用户无需知道细节,只需知道参数意义即可进行调用。

在LCD驱动构件函数层次中,中上层功能函数在移植时基本无需改动,可看作驱动构件的通用层。移植的关键在于MCU头文件和构件的底层通信函数,因此这部分可看作是LCD构件的适配层。LCD构件驱动程序层次及函数关系如图4所示。

图4 LCD构件驱动程序层次模型

4.5 LCD构件可移植性度量分析

一个构件的可移植性高低可以采用软件可移植性度量进行分析。下面分别利用基于移植成本和移植工作量两种可移植性度量模式[13]对LCD构件进行可移植度量分析。

(1)基于移植成本度量

该度量模式基于软件的可移植性和开发成本之间的关系来衡量可移植性程度,可使用下式:

其中,DP表示可移植性大小,COSTport表示软件的移植成本,COSTredevelop表示软件重新开发的成本,DP越大表示软件的可移植性越高。从复用和移植步骤可以看出,在新系统移植时,LCD构件只需修改适配层的引脚定义和通信函数,其他功能则基本不需改动,反映到成本上,COSTport远远小于COSTredevelop。因此,LCD构件从成本角度看可移植性较高。

(2)基于移植工作量度量

该度量模式通过与移植环境相关的静态指标(如代码修改量、移植耗时等)来衡量可移植性高低。如上文所述,LCD构件严格按照构件概念模型设计,代码具有模块化、层次化的特征。移植新系统时,需要修改的仅仅是头文件的引脚定义和源文件中通信函数的重写,除特殊情况外,其他部分基本不需改动,而且LCD构件有规范的注释和移植说明、注意事项等文档作为移植参考。经实验室测试,在新购的TFTLCD构件代码零乱、没有相关参考文档的情况下,移植成功花费了大约两天时间,而经过本文封装形成规范的TFTLCD构件,交付给另一个没有参与设计且技术水平相当的人员,在基于不同型号的核心构件系统中进行移植花费时间仅约3小时,大大提高了效率,且运行更加稳定可靠。测试说明按照本文提出规范设计面向应用对象的终端构件具有良好的可复用可移植性。

结 语

猜你喜欢
原理图嵌入式构件
浅谈STM32核心板原理图设计
电路原理图自动布图系统的设计与实现
基于Protel DXP 2004层次原理图的设计
搭建基于Qt的嵌入式开发平台
嵌入式软PLC在电镀生产流程控制系统中的应用
建筑构件
建筑构件
建筑构件
建筑构件
关于EDA教学中Quartus II原理图输入法的探讨