购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.3 GPIO驱动构件封装方法与规范

4.3.1 设计GPIO驱动构件的必要性及GPIO驱动构件封装要点分析

1.设计GPIO驱动构件的必要性

软件构件(Software Component)技术的出现,为实现软件构件的工业化生产提供了理论与技术基石。将软件构件技术应用到嵌入式软件开发中,可以大大提高嵌入式开发的开发效率与稳定性。软件构件的封装性、可移植性与可复用性是软件构件的基本特性,采用构件技术设计软件,可以使软件具有更好的开放性、通用性和适应性。特别是对于底层硬件的驱动编程,只有封装成底层驱动构件,才能减少重复劳动,使 MCU 应用开发者专注于应用软件的稳定性与功能设计。因此,必须把底层硬件驱动设计好、封装好。

以S32K144的GPIO为例,它有89个引脚可以作为GPIO,分布在5个端口,不可能使用直接地址去操作相关寄存器,那样无法实现软件移植与复用。应该把对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)引脚状态反转(gpio_reverse)。类似地,可以设计引脚状态反转函数的原型为:

void gpio_reverse(uint_16 port_pin)

(5)引脚上拉/下拉使能函数(gpio_pull)。若引脚被设置成输入,还可以设定内部上拉/下拉电阻,S32K144内部上拉/下拉电阻大小为20~50 kΩ。引脚上拉/下拉使能函数的原型为:

void gpio_pull(uint_16 port_pin, uint_8 pullselect)

这些函数基本满足了对GPIO操作的基本需求。中断使能与禁止 、引脚驱动能力等函数比较复杂,可暂时略过,使用或深入学习时参考GPIO驱动构件即可。要实现 GPIO驱动构件的这几个函数,给出清晰的接口、良好的封装、简洁的说明与注释、规范的编程风格等,需要一些准备工作,4.3.2节给出了底层驱动构件封装规范概要与构件封装的前期准备。

4.3.2 底层驱动构件封装规范概要与构件封装的前期准备

底层驱动构件封装规范见5.3节,本节给出概要与前期准备,以便在认识第一个构件前,以及在开始设计构件时少走弯路,使构件符合基本规范,便于移植、复用、交流。

1.底层驱动构件封装规范概要

(1)底层驱动构件的组成、存放位置与内容。每个构件由头文件(.h)与源(程序)文件(.c)两个独立文件组成,放在以构件名命名的文件夹中。驱动构件的头文件(.h)中仅包含对外接口函数的声明,是构件的使用指南,通常以构件名命名,gpio.h(使用小写,目的是与内部函数名前缀统一)。设计好的GPIO驱动构件存放于“..S32K144共用驱动\S32K144底层驱动构件\gpio”文件夹中,供复制使用。对外接口函数及内部函数的实现在构件的源文件(.c)中,同时应注意,头文件声明对外接口函数的顺序与源文件实现对外接口函数的顺序应保持一致。源文件中内部函数的声明要放在对外接口函数代码的前面,内部函数的实现放在全部对外接口函数代码的后面,以便提高可阅读性与可维护性。

在一个具体的工程中,根据在本书给出的标准框架,所有底层驱动构件放在工程文件夹下的“03_MCU”中的“driver”,可参见第一个规范样例工程“..\ch04-Light\S32K144GPIO-LIGHT”下的文件组织。

(2)设计构件的最基本要求。这里简要地给出设计构件的最基本要求。

① 考虑使用与移植的方便。要对构件的共性与个性进行分析,抽取出构件的属性和对外接口函数。希望做到:对于同一芯片的应用系统,不更改构件就可直接使用;在同系列芯片的同功能底层驱动移植时,仅改动头文件即可;在不同系列芯片的同功能底层驱动移植时,头文件与源程序文件的改动尽可能少。

② 要有统一、规范的编码风格与注释。主要涉及文件、函数、变量、宏及结构体类型的命名规范;涉及空格与空行、缩进、断行等的排版规范;涉及文件头、函数头、行及边等的注释规范。具体要求见5.3.2节。

③ 宏的使用限制。宏使用具有两面性,既有提高可维护性的一面,也有降低可读性的一面,不要随意使用宏。

④ 不使用全局变量。在构件封装时,禁止使用全局变量。

2.构件封装的前期准备——公共要素文件

我们通常把同一芯片所有工程均需使用一些内容放在一个文件中,称这个文件为公共要素文件,该文件放在工程文件夹的“..\05_SoftComponent\common”文件夹下,名为common.h。这里给出基本说明,其他内容见5.3.3节,部分内容有所重复,但侧重点不同。

(1)S32K144芯片寄存器映射文件。

#include"S32K144.h"       //包含芯片头文件

每个底层驱动构件都是以硬件模块的功能寄存器为操作对象的,因此,在 common.h 文件中包含了描述芯片寄存器地址映射的头文件,当底层驱动构件引用 common.h 文件时,就可以使用片内寄存器映射文件中的定义来访问各自相关功能寄存器。

(2)位操作宏函数。将编程时经常用到的寄存器位操作定义成宏函数BSET、BCLR、BGET这些容易理解与记忆的标识,表示进行寄存器的置1、清0及获得寄存器某一位状态的操作,详见5.3.3节。

(3)重定义基本数据类型。给出基本类型的重定义(别名),有两层含义,一是为了便于移植,二是为了书写方便,见5.3.3节。对于构件公共要素文件中的其他内容也在5.3.3节中进行了解释。

4.3.3 S32K144的GPIO驱动构件源码及解析

根据构件的基本要求设计的第一个构件——GPIO 驱动构件,该构件存放于网上光盘“..\底层驱动构件\gpio”文件夹中,供复制使用,各个工程文件夹下的“..\ 03_MCU\MCU_drivers\gpio”文件夹中GPIO驱动构件与此一致。

1.GPIO驱动构件头文件(gpio.h)

在GPIO驱动构件的头文件(gpio.h)中包含的内容有:头文件说明;防止重复包含的条件编译代码结构“#ifndef ...#define ...#endif”;用宏定义方式统一了端口号地址偏移量(如PTA_NUM)为引脚描述量的高8位;给出了11个对外服务函数的接口说明及声明,这些函数包括引脚初始化函数(gpio_init)、设置引脚状态函数(gpio_set)、获取引脚状态函数(gpio_get)三个主要函数,以及引脚状态反转函数(gpio_reverse)、引脚上拉/下拉使能函数(gpio_pull)、使能引脚中断函数(gpio_enable_int)、禁止引脚中断函数(gpio_disable_int)、获取引脚GPIO中断状态(gpio_get_int)、清引脚GPIO中断(gpio_clear_int)、清所有引脚GPIO中断(gpio_clear_allint)、引脚驱动能力设置函数(gpio_drive_strength)八个功能函数。

2.GPIO驱动构件源文件(gpio.c)

GPIO驱动构件的源文件中实现的是对外接口函数(也称对外服务函数),主要对相关寄存器进行配置,从而完成构件的基本功能。构件内部使用的函数也在构件源文件中定义,下面给出部分函数的源代码。

3.GPIO驱动构件源码解析

(1)两个结构体类型。在工程文件夹的芯片头文件“..\03_MCU\startup\S32K144.h”中,有端口寄存器结构体,把端口模块的编程寄存器用一个结构体类型(PORT_Type)封装起来。

typedef struct {

__IO uint32_t PCR[32];    //引脚控制寄存器(0~31),偏移0x0,间隔0x4

__O uint32_t GPCLR;   //全局引脚控制寄存器(L),偏移0x80

__O uint32_t GPCHR;   //全局引脚控制寄存器(H),偏移0x84

uint8_t RESERVED_0[24];  //保留(占位)(0~23)

__IO uint32_t ISFR;     //中断状态标志寄存器,偏移0xA0

uint8_t RESERVED_1[28];

__IO uint32_t DFER;

__IO uint32_t DFCR;

__IO uint32_t DFWR;

} PORT_Type, *PORT_MemMapPtr;

同时定义了未优化的PORT模块寄存器结构体指针PORT_MemMapPtr,这样,只要给出端口基地址就可以使用该结构体的成员变量对各寄存器进行访问。类似地,给出了GPIO模块结构体类型(GPIO_Type)及其指针(GPIO_MemMapPtr)。

typedef struct{

__IO uint32_t  PDOR;   //数据输出寄存器,偏移0x0

__O uint32_t  PSOR;    //输出置1寄存器,偏移0x4

__O uint32_t  PCOR;   //输出清0寄存器,偏移0x8

__O uint32_t  PTOR;   //输出反转寄存器,偏移0xC

__I  uint32_t  PDIR;    //数据输入寄存器,偏移0x10

__IO uint32_t  PDDR;   //数据方向寄存器,偏移0x14

}GPIO_Type,*GPIO_MemMapPtr;

(2)端口模块及GPIO模块的各端口基地址。S32K144的端口(PORT)模块的各端口基地址PORTA_BASE_PTR、PORTB_BASE_PTR、PORTC_BASE_PTR、PORTD_BASE_PTR、PORTE_BASE_PTR 在芯片的头文件中以宏常数方式给出。S32K144的 GPIO 模块各端口基地址PTA_BASE_PTR、PTB_BASE_PTR、PTC_BASE_PTR、PTD_BASE_PTR、PTE_BASE_PTR也在芯片的头文件中以宏常数方式给出,本程序直接作为指针常量。

(3)编程与注释风格。希望仔细分析本构件的编程与注释风格,一开始就规范起来,这样就会逐步形成良好的编程习惯。特别要注意,不要编写令人难以看得懂的程序,不要把简单问题复杂化,不要使用不必要的宏。 aryHrn64YPo/wAddAQmDrkLSNFI9C5Z+f2u5DIVfQNlduQaazn9+brnkbTNOpuns

点击中间区域
呼出菜单
上一章
目录
下一章
×