深入浅出AMetal之接口与实现

第五章为深入浅出ametal,本文内容为5.1 接口与实现。
本章导读:
对于初学者来说,要想实现一个温度采集是很难的,但ametal 可以做到。ametal 构建了一套抽象度更高的标准化接口,封装了各种mcu 底层的变化,为应用软件提供了更稳定的抽象服务,延长了软件系统的生命周期。因此无论你选择什么mcu,只要支持ametal,开发者无需阅读用户手册,甚至不需要知道什么是ametal,就可以高度复用原有的代码。
尽管你已经得心应手地使用ametal 编写了很多的程序,但还是想深入了解更多的接口是如何实现的?那么我们不妨从这里开始ametal 的神奇之旅!
5.1 接口与实现
>>> 5.1.1 gpio 接口函数
ametal 提供了操作gpio 的标准接口函数,所有gpio 的标准接口函数原型位于ametal_am824_core_1.00\ametal\common\interface\am_gpio.h 文件中,其中包括宏定义和提供给用户操作gpio 的函数原型的声明。即:
配置引脚功能和模式:int am_gpio_pin_cfg(int pin, uint32_t flags)
获取引脚电平:int am_gpio_get(int pin)
设置引脚电平:int am_gpio_set(int pin, int value)
翻转引脚电平:int am_gpio_toggle(int pin)
1. 配置引脚功能和模式
其中的pin 为引脚编号,格式为piox_x,比如,pio0_0 用于指定配置相应引脚。在gpio标准接口层中,所有函数的第一个参数均为pin,用于指定具体操作的引脚,相关的宏定义在ametal_easy_arm_lpc8xx\ametal\lpc8xx\drivers\include\lpc8xx_pin.h 中定义。
flags 为配置标志,由“通用功能 | 通用模式 | 平台功能 | 平台模式”(‘|’就是c 语言中的按位或)组成。通用功能和模式在am_gpio.h 文件中定义,是从标准接口层抽象出来的gpio 最通用的功能和模式,格式为am_gpio_*。通用功能相关宏定义与含义详见表5.1,通用模式相关宏定义与含义详见表5.2。
表5.1 引脚通用功能
表5.2 引脚通用模式
平台功能和模式与具体芯片相关,会随着芯片的不同而不同,平台功能和模式相关的宏定义在lpc8xx_pin.h 文件中定义,芯片引脚的复用功能和一些特殊的模式都定义这个文件中,格式为pio*_*_*,比如,pio0_4_uart0_tx,pio0_4 的串口0 发送。
如果需要找到pio0_0 相关的平台功能和平台模式,可以打开lpc8xx_pin.h 这个文件,找到pio0_0为前缀的宏定义,pio0_0 相关的平台功能详见表5.3,平台模式详见表5.4。
表5.3 pio0_0 平台功能
表5.4 pio0_0 平台模式
在这里,读者可能会问,为什么要将功能分为通用功能和平台功能呢?各自相关的宏存放在各自的文件中,文件数目多了,会不会使用起来更加复杂呢?
通用功能定义在标准接口层中,不会随芯片的改变而改变。而gpio 复用功能等,会随着芯片的不同而不同,这些功能是由具体芯片决定的,因此必须放在平台定义的文件中。如果这部分也放到标准接口层文件中,就不能保证所有芯片标准接口的一致性,从而也就失去了标准接口的意义。这样分开使用让使用者更清楚,哪些代码是全部使用标准接口层实现的,全部使用标准接口层的代码与具体芯片是无关的,是可跨平台复用的。
如果返回am_ok,说明配置成功;如果返回- am_enotsup,说明配置的功能不支持,配置失败,配置引脚为gpio 功能详见程序清单5.1。
程序清单5.1 配置管脚为gpio 功能
配置引脚为ad 模拟输入功能详见程序清单5.2。
程序清单5.2 配置引脚为ad 模拟输入功能
配置引脚为uart 功能详见程序清单5.3。
程序清单5.3 配置引脚为uart 功能
2. 获取引脚电平
其中的pin 为引脚编号,格式为piox_x,比如pio0_0,用于获取引脚的电平状态。使用范例详见程序清单5.4。
程序清单5.4 am_gpio_get()范例程序
3. 设置引脚电平
其中的pin 为引脚编号,格式为piox_x。比如,pio0_0 用于设置pio0.0 引脚的电平。value 为设置的引脚状态,0-低电平,1-高电平。如果返回am_ok,说明操作成功,使用范例详见程序清单5.5。
程序清单5.5 am_gpio_set()范例程序
4. 翻转引脚电平
翻转 gpio 引脚的输出电平,如果gpio 当前输出低电平,当调用该函数后,gpio 翻转输出高电平,反之则翻转为低电平。
其中的pin 为引脚编号,格式为piox_x。比如,pio0_0 用于翻转pio0.0 引脚的电平状态。如果返回am_ok,说明操作成功,使用范例详见程序清单5.6。
程序清单5.6 am_gpio_toggle()范例程序
5. 范例
显然,控制led0 点亮或熄灭是通过gpio 输出0 或1 实现的,因此需要先调用am_gpio_pin_cfg()函数将gpio 配置为输出模式,并初始化为高电平,确保初始时led0 处于确定的熄灭状态,接着调用am_gpio_set()函数,使pio0_20 输出低电平点亮led0,其相应的代码详见程序清单5.7。
程序清单5.7 点亮led 范例程序
led 不停地闪烁就是让一个i/o 不断翻转的过程,详见程序清单5.8。
程序清单5.8 单个led 闪烁范例程序(1)
ametal 针对i/o 提供了引脚电平翻转函数am_gpio_toggle(),详见程序清单5.9。
程序清单5.9 单个led 闪烁范例程序(2)
假如使蜂鸣器发出1khz 频率的声音,1khz 频率对应的周期为:t=1/1000(s)=1(ms),由于一个周期是低电平(接通)时间和高电平(断开)时间的总和,因此在一个周期内,高、低电平保持的的时间分别为500us。由此可见,要使蜂鸣器不间断地发声,只要以500us 的时间间隔不断的翻转引脚的输出电平即可,详见程序清单5.10。
程序清单5.10 蜂鸣器发声范例程序
>>> 5.1.2 led 接口与实现
下面将使用这些程序设计的概念实现通用函数接口,比如,家用电器的某个动作完成时,或工业现场数据采集的上下限报警,都会通过led 提醒操作者。显然led 驱动软件应用非常广泛,完全有必要编写一个通用函数作为接口以便复用。编写通用函数应该建立一个“.h”文件和一个“.c”文件,.h 文件用于提供接口,告知调用者提供了哪些接口,.c 文件用于实现各个接口函数,所以需要建立一个led.c 文件和led.h 文件,并将.c 文件添加到工程中。
显然led 只有点亮、熄灭和翻转3 种操作,如果我们不在乎抽象性的话,则可以直接调用ametal 函数实现。抽象的方法在操作led 的实现代码和使用操作led 的代码之间添加一个函数层,创建一个定义明确的接口,正确的抽象性是将对象的实现和它的接口分离。即将操作led 的方法“声明”函数原型如下:
其中的led_id 对应的led 编号,为了方便调用者以后不用再查看原理图,则将led 与gpio 的对应关系定义在一个数组中,其相应的代码详见程序清单5.11。
程序清单5.11 定义led 对应的gpio 口
那么调用者只要将索引号传入数组即可。由于i/o 口的数量只有8 个,则led_id 的有效值是0 ~ 7,所以需要判定led_id 是否合法防止数组越界,其相应的代码详见程序清单5.12。
程序清单5.12 通用接口函数(led.c)的实现(1)
编程到这里貌似已经很完善了,但led 还是不能工作,因为还没有将gpio 配置为输出模式,其相应的代码详见程序清单5.13。
程序清单5.13 添加初始化函数
这里并没有简单地将gpio 初始化为输出,而是在配置为输出模式的同时,初始化gpio为高电平,以保证led 处于熄灭状态。此时编程完毕,则将相关的函数接口声明封装到led.h文件中,详见程序清单5.14。当后续需要调用时,只需要 #include led.h就可以了。
程序清单5.14 在led.h 中添加函数声明
在实际的使用中,接口函数都应添加详细的描述,告诉调用者应该如何调用这些接口。为了方便调用,可以在led.h 中将led 的编号与实际数组中的索引号的对应关系使用宏定义出来。那么在调用led 接口函数时,就不再需要关心led_id 的具体数值,直接使用宏即可,其相应的代码详见程序清单5.15。
程序清单5.15 led_id 的定义
此时,如果要点亮led0,则调用led_on(led0)即可。这个接口是否已经做到很通用了呢?虽然led 对应的gpio 信息中包含了i/o 信息,却没有包括对应的电平信息。如果仅仅看数组,而不看硬件原理图,还是不知道点亮或熄灭led 究竟是高电平还是低电平?
由于led 对应的管脚信息和相应的电平信息分别属于不同的数据类型,显然只有使用结构体,才能将不同类型的数据放在一起作为一个整体来对待。同时注意在声明结构体时给出typedef 定义,且在定义的类型名称后面追加标签,比如,led_info,其相应的数据结构详见程序清单5.16(3)~ (8)通用接口函数的实现。
程序清单5.16 通用接口函数(led.c)的实现(2)
如果我们需要改变处理数据的方法,则只需要在一个地方进行修改就可以了,而不必改动程序中所有直接访问数据的地方。正确的封装机制,不仅鼓励而且强迫隐藏实现细节。它使你的代码更可靠,而且更容易维护。文件led.h 仅包含了相应的接口函数的声明,而在led.c中对它们进行定义,实际上用户是看不到led.c 的。
在实际的应用中,用户使用led 有两种情况,绝大部分情况都是使用am824-core 板载的两个led,但在流水灯实验中,使用的是miniport-led 上的8 个led,它们对应的引脚是不同的,基于此,可以在led.h 文件中定义一个宏use_miniport_led,默认值为0,使用板载led,为1 时使用miniport-led。引脚信息数组g_led_info 的定义修改如下:
显然,根据抽象定义的接口操作对象,将极大地减少了子系统实现之间的相互依赖关系,也产生了可复用的程序设计的原则:只针对接口编程而不是针对实现编程。因为针对接口编程的组件不需要知道对象的具体类型和实现,只需要知道抽象类定义了哪些接口,从而减少了实现上的依赖关系。
实际上,这些接口并不妨碍你将一个对象和其它对象一起使用,因而对象只能通过接口来访问,所以并不会破坏封装性。
>>> 5.1.3 i/o 接口与中断
gpio 触发部分主要是使gpio 工作在中断状态的相关操作接口,详见表5.5。
表5.5 gpio 触发相关接口函数
1. 配置引脚触发条件函数
配置引脚触发函数原型如下:
其中的pin 为引脚编号,格式为piox_x,比如pio0_0,配置相应引脚的触发条件。flag为触发条件,所有可选的触发条件详见表5.6。
表5.6 gpio 触发条件配置宏
注意,这些触发条件并不一定每个gpio 口都支持,当配置触发条件时,应检测返回值,确保相应引脚支持所配置的触发条件。细心的人可能会发现,这里的参数flag 为单数形式,而am_gpio_pin_cfg()函数的参数flag 为复数形式。当参数为单数形式时,则表明只能从可选宏中选择一个具体的宏值作为实参;当参数为复数形式时,则表明可以选多个宏值的或值(c 语言中的“|”运算符)作为实参。
如果返回am_ok,说明配置成功,如果返回-am_enotsup,说明引脚不支持该触发条件,配置失败,使用范例详见程序清单5.17。
程序清单5.17 am_gpio_trigger_cfg ()范例程序
2. 连接引脚触发回调函数
连接一个回调函数到触发引脚,当相应引脚触发事件产生时,则会调用本函数连接的回调函数。其函数原型为:
其中的pin 为引脚编号,格式为piox_x,比如pio0_0,将函数与相应引脚关联。pfn_callback 为回调函数,类型为am_pfnvoid_t (void (*)(void *) ),即无返回值,参数为void*型的函数。p_arg 为回调函数的参数为void *型,这个参数就是当回调函数调用时,传递给回调函数的参数。如果返回am_ok,说明连接成功,使用范例详见程序清单5.18。
程序清单5.18 am_gpio_trigger_connect()范例程序
3. 断开引脚触发回调函数
与am_gpio_trigger_connect()函数的功能相反,当不需要使用一个引脚中断时,应该断开引脚与回调函数的连接;或者当需要将一个引脚的回调函数重新连接到另外一个函数时,应该先断开当前连接的回调函数,再重新连接到新的回调函数。其函数原型为:
其中的pin 为引脚编号,格式为piox_x,比如pio0_0,断开相应引脚的连接函数;pfn_callback 为回调函数,应该与连接函数对应的回调函数一致;p_arg 为回调函数的参数为void *型,应该与连接函数对应的回调函数参数一致。如果返回am_ok,说明断开连接成功,使用范例详见程序清单5.19。
程序清单5.19 am_gpio_trigger_disconnect()范例程序
4. 打开引脚触发
打开引脚触发,只有打开引脚触发后,引脚触发才开始工作。在打开引脚触发之前,应该确保正确连接了回调函数并设置了相应的触发条件。其函数原型为:
其中的pin 为引脚编号,格式为piox_x,比如pio0_0,打开相应引脚的触发。如果返回am_ok,说明打开成功,使用范例详见程序清单5.20。
程序清单5.20 am_gpio_trigger_on ()范例程序
5. 关闭引脚触发
关闭后,引脚触发将停止工作,即相应触发条件满足后,不会调用引脚相应的回调函数。如需引脚触发继续工作,可以使用am_gpio_trigger_on()重新打开引脚触发。其函数原型为:
其中的pin 为引脚编号,格式为piox_x,比如,pio0_0,关闭相应引脚的触发。如果返回am_ok,说明关闭引脚触发成功,使用范例详见程序清单5.21。
程序清单5.21 am_gpio_trigger_off()范例程序

联想两款Tab 4系列平板电脑开启预售:899元起你会买吗?
全新蓝牙标准优化远程患者监护
光模块DSP内部的光层测试与OSNR测试的区别
爱立信正在关注的x是什么
点焊机引起电流变化的主要原因有什么
深入浅出AMetal之接口与实现
鸿海科技宣布正式进军电动车领域
浅谈导热石墨片的包边处理
三星新一代Artik开发平台 新增脸部辨识功能
为什么运算放大器两个输入端放上相等的阻抗
蓝牙耳机排行榜_降噪TWS蓝牙耳机品牌排行
污染源视频监控的基本架构和解决方案
74ls20逻辑功能及真值表
5G模式和5G频段的问题
电子设备雷击浪涌抗扰度试验标准及防护解析
STM32 SPI读写W25Q64(二)
光学谐振器的结构和作用
基于单片机的蓝牙控制小车设计
Wild宣布对OculusQuest支持 将提高工作效率
电容在电路板中的作用是什么