4.3 GPIO驱动构件封装方法与驱动构件封装规范
4.3.1 设计GPIO驱动构件的必要性及GPIO驱动构件封装要点分析
1. 设计GPIO驱动构件的必要性
软件构件(software component)技术的出现,为实现软件构件的工业化生产提供了理论与技术基石。将软件构件技术应用到嵌入式软件开发中,可以大大提高嵌入式开发的效率与稳定性。软件构件的封装性、可移植性与可复用性是软件构件的基本特性,采用构件技术设计软件,可以使软件具有更好的开放性、通用性和适应性,特别是对于底层硬件的驱动编程,只有封装成底层驱动构件,才能减少重复工作,使广大MCU应用开发者专注于应用软件的稳定性与功能设计上。因此,必须把底层硬件驱动设计好、封装好。
以MSP432的GPIO为例,它有84个引脚分布在11个端口,不可能使用直接地址去操作相关寄存器,那样就无法实现软件的移植与复用。而是应该把对GPIO引脚的操作封装成构件,通过函数调用与传参的方式实现对引脚的干预与状态获取,这样的软件才便于维护与移植,因此设计GPIO驱动构件十分必要。同时,底层驱动构件的封装,也为在操作系统下对底层硬件的操作提供了基础。
2. GPIO驱动构件封装要点分析
同样以GPIO驱动构件为例进行封装要点分析,即分析应该设计哪几个函数及入口参数。GPIO引脚可以被定义成输入、输出两种情况:若是输入,程序需要获得引脚的状态(逻辑1或0);若是输出,程序可以设置引脚状态(逻辑1或0)。MCU的PORT模块分为许多端口,每个端口有若干引脚。GPIO驱动构件可以实现对所有GPIO引脚统一编程。GPIO驱动构件由gpio.h和gpio.c两个文件组成,如果要使用GPIO驱动构件,只需要将这两个文件加入到所建工程中,由此方便了对GPIO的编程操作。
1)模块初始化(gpio_init)
由于芯片引脚具有复用特性,应把引脚设置成GPIO功能,且定义成输入或输出,若是输出,还要给出初始状态。因此GPIO模块初始化函数gpio_init的参数为哪个引脚,是输入还是输出,若是输出其状态是什么,函数不必有返回值。其中引脚可用一个16位数据描述,高8位表示端口号,低8位表示端口内的引脚号。这样GPIO模块初始化函数原型可以设计为:
void gpio_init(uint_16 port_pin,uint_8 dir,uint_8 state)
其中,uint_8是无符号8位整型的别名,uint_16是无符号16位整型的别名,其定义在工程文件夹下的“..\05_SoftComponent\Common\common.h”文件中,后面不再特别说明。
2)设置引脚状态(gpio_set)
对于输出,希望通过函数设置引脚是高电平(逻辑1)还是低电平(逻辑0),入口参数应该是哪个引脚,其输出状态是什么,函数不必有返回值。这样设置引脚状态的函数原型可以设计为:
void gpio_set(uint_16 port_pin,uint_8 state)
3)获得引脚状态(gpio_get)
对于输入,希望通过函数获得引脚的状态是高电平(逻辑1)还是低电平(逻辑0),入口参数应该是哪个引脚,函数需要返回值引脚状态。这样设置引脚状态的函数原型可以设计为:
uint_8 gpio_get(uint_16 port_pin)
4)引脚状态反转(void gpio_reverse)
类似的分析,可以设计引脚状态反转函数的原型为:
void gpio_reverse(uint_16 port_pin)
5)引脚上下拉使能函数(void gpio_pull)
若引脚被设置成输入,还可以设定内部上下拉,MSP432内部上下拉电阻大小为20~50kΩ。引脚上下拉使能函数的原型为:
void gpio_pull(uint_16 port_pin,uint_8 pullselect)
这些函数基本满足了对GPIO操作的基本需求。还有中断使能与禁止、引脚驱动能力等函数(内容比较深,可暂时略过),使用或深入学习时参考GPIO构件即可。要实现GPIO驱动构件的这几个函数,并给出清晰的接口、良好的封装、简洁的说明与注释、规范的编程风格等,需要一些准备工作。
4.3.2 底层驱动构件封装规范概要与构件封装的前期准备
底层驱动构件封装规范见5.3节,本节给出概要与前期准备,以便在认识第一个构件前和开始设计构件时,少走弯路,做出来的构件符合基本规范,便于移植、复用、交流。
1. 底层驱动构件封装规范概要
1)底层驱动构件的组成、存放位置与内容
每个构件由头文件(.h)与源文件(.c)两个独立文件组成,放在以构件名命名的文件夹中。驱动构件头文件(.h)中仅包含对外接口函数的声明,是构件的使用指南,以构件名命名。例如,GIPO构件命名为gpio(使用小写,目的是与内部函数名前缀统一)。设计好的GPIO构件存放于“..\底层驱动构件\gpio”文件夹中,供复制时用。基本要求是调用者只看头文件即可使用构件。对外接口函数及内部函数的实现在构件源程序文件(.c)中。同时应注意,头文件声明对外接口函数的顺序与源程序文件实现对外接口函数的顺序应保持一致。源程序文件中内部函数的声明,放在外接口函数代码的前面,内部函数的实现放在全部外接口函数代码的后面,以便提高可阅读性与可维护性。
一个具体的工程中,在本书给出的标准框架下,所有底层驱动构件放在工程文件夹下的“..03_MCU\MCU_drivers”文件夹中,见第一个规范样例工程“..\ch04-Light\MSP432-Light(Component)”下的文件组织。
2)设计构件的最基本要求
下面摘要给出设计构件的最基本要求。
(1)考虑使用与移植方便。要对构件的共性与个性进行分析,抽取出构件的属性和对外接口函数。希望做到:使用同一芯片的应用系统,构件不更改,直接使用;同系列芯片的同功能底层驱动移植时,仅改动头文件;不同系列芯片的同功能底层驱动移植时,头文件与源程序文件的改动尽可能少。
(2)要有统一、规范的编码风格与注释。主要涉及:文件、函数、变量、宏及结构体类型的命名规范;空格与空行、缩进、断行等的排版规范;文件头、函数头、行及边等的注释规范。具体要求见5.3.2节。
(3)宏的使用限制。宏使用具有两面性,有提高可维护性的一面,也有降低阅读性的一面,不要随意使用宏。
(4)不使用全局变量。构件封装时,禁止使用全局变量。
2. 构件封装的前期准备——公共要素文件
把同一芯片所有工程均需使用的一些内容放在一个文件中,并命名为“公共要素文件”,该文件放在工程文件夹的“..\05_SoftComponent\common”文件夹下,名称为common.h。这里给出基本说明,其他内容见5.3.3节,部分内容有所重复,但侧重点不同。
1)MSP432芯片寄存器映射文件
#include"msp432.h" //包含芯片头文件
每个底层驱动构件都是以硬件模块的功能寄存器为操作对象,因此,在common.h文件中包含了描述芯片寄存器地址映射的头文件,当底层驱动构件引用common.h文件时,即可使用片内寄存器映射文件中的定义访问各自相关功能寄存器。
2)位操作宏函数
将编程时经常用到的寄存器位操作,定义成宏函数BSET、BCLR、BGET这些容易理解与记忆的标识,表示进行寄存器的置位、清位及获得寄存器某一位状态的操作。BSET、BCLR、BGET宏定义见5.3.3节。
3)重定义基本数据类型
给出基本类型的重定义(别名)有两层含义:一是为了便于移植,二是为了书写方便。对于构件公共要素文件中的其他内容将在5.3.3节中解释。
4.3.3 MSP432的GPIO驱动构件源码及解析
根据构件生产的基本要求设计的第一个构件——GPIO驱动构件,存放于网上教学资源“..\底层驱动构件\gpio”文件夹,供复制使用,各个工程文件夹下的“..03_MCU\MCU_drivers\gpio”文件夹中GPIO驱动构件与此一致。
1. GPIO驱动构件头文件(gpio.h)
在GPIO驱动构件的头文件(gpio.h)中包含的内容有:头文件说明;防止重复包含的条件编译代码结构“#ifndef…#define…#endif”。用宏定义方式统一了端口号地址偏移量(如PT1),为引脚描述量的高8位。给出11个对外服务函数的接口说明及声明,这些函数包括引脚初始化函数(gpio_init)、设定引脚状态函数(gpio_set)、获取引脚状态函数(gpio_get)3个主要函数,以及反转引脚状态函数(gpio_reverse)、引脚上下拉使能函数(gpio_pull)、使能引脚中断函数(gpio_enable_int)、禁用引脚中断函数(gpio_disable_int)、获取引脚GPIO中断状态(gpio_get_int)、引脚的驱动能力设置函数(gpio_drive_strength)6个功能函数。
2. GPIO驱动构件源程序文件(gpio.c)
GPIO驱动构件的源程序文件中实现的对外接口函数,主要是对相关寄存器进行配置,从而完成构件的基本功能。构件内部使用的函数也在构件源程序文件中定义。下面给出部分函数的源代码。
3. GPIO驱动构件源码解析
1)结构体类型
在工程文件夹的芯片头文件(“..\03_MCU\startup\msp432p401r.h”)中,有端口寄存器结构体,把端口模块的编程寄存器用结构体类型(DIO_PORT_Interruptable_Type)封装起来:
限于篇幅,还包括两个结构体,分别为偶数端口结构体和无中断结构体,可以在msp432p401r.h中查看。
2)端口模块及GPIO模块各口基地址
MSP432的GPIO模块各口基地址P1、P2、P3、P4、P5、P6、P7、P8、P9、P10、PJ也在芯片头文件(msp432p401r.h)中以宏常数方式给出,本程序直接作为指针常量。
3)编程与注释风格
希望仔细分析本构件的编程与注释风格,一开始就规范起来,这样就会逐步锻炼起良好的编程习惯。特别注意,不要编写令人难以看懂的程序,不要把简单问题复杂化,不要使用不必要的宏。