电子说
周立功教授新书《面向AMetal框架与接口的编程(上)》,对AMetal框架进行了详细介绍,通过阅读这本书,你可以学到高度复用的软件设计原则和面向接口编程的开发思想,聚焦自己的“核心域”,改变自己的编程思维,实现企业和个人的共同进步。
第六章为重用外设驱动代码,本文内容包含6.3 RTC 实时时钟前四个小节:
6.3.1 PCF85063
6.3.2 RTC 通用接口
6.3.3 闹钟通用接口
6.3.4 系统时间
6.3.5 特殊功能控制接口
6.3 RTC 实时时钟
本节将以PCF85063 为例,详细介绍RTC 通用接口,闹钟通用接口等。在本节的最后两小节,将介绍另外两款RTC 芯片:RX8025T 和DS1302,虽然它们与PCF85063 存在差异,但却可以使用同样的通用接口对其进行操作,实现了RTC 应用的跨平台复用。
>>>6.3.1 PCF85063
1. 器件简介
PCF85063 是一款低功耗实时时钟/日历芯片,它提供了实时时间的设置与获取、闹钟、可编程时钟输出、定时器/报警/半分钟/分钟中断输出等功能。
NXP 半导体公司的PCF85063 引脚封装详见图6.4,其中的SCL 和SDA 为I2C接口引脚,VDD 和VSS 分别为电源和地;OSCI 和OSCO 为32.768KHz 的晶振连接引脚,作为PCF85063 的时钟源;CLKOUT 为时钟信号输出,供其它外部电路使用;INT 为中断引脚,主要用于闹钟等功能。
图6.4 PCF85063 引脚定义
PCF85063 的7 位I2C从机地址为0x51,MicroPort-RTC 模块通过MicroPort 接口与AM824-Core 相连,SCL 和SDA 分别与PIO0_16 和PIO0_18 连接,详见图6.5。若焊接R1,则INT 与PIO0_1 相连;若焊接R3,则INT 与PIO0_8 相连;若焊接R2,则CLKOUT 与PIO0_24 相连。
图6.5 PCF85063 电路原理图
2. 器件初始化
在使用PCF85063 前,必须完成PCF85063 的初始化操作,以获取对应的操作句柄,进而才能使用PCF85063 的各种功能,初始化函数原型(am_pcf85063.h)为:
该函数意在获取PCF85063 器件的实例句柄,其中,p_dev 为指向am_pcf85063_dev_t类型实例的指针,int_pin 作为实例信息,指定PCF85063 的INT 与MCU 的连接引脚号。
(1)实例
定义am_pcf85063_dev_t 类型(am_pcf85063.h)实例如下:
其中,g_pcf85063_dev 为用户自定义的实例,其地址作为p_dev 的实参传递。
(2)实例信息
实例信息仅一个中断引脚信息,用于指定PCF85063 的INT 与MCU 的引脚号相连,便于使用闹钟等功能。假设使用PIO0_1,则PIO0_1 作为int_pin 的实参传递。
(3)I2C句柄i2c_handle
以I2C1 为例,其实例初始化函数am_lpc82x_i2c1_inst_init ()的返回值将作为实参传递给i2c_handle。即:
(4)实例句柄
PCF85063 初始化函数am_pcf85063_init ()的返回值,作为实参传递给其它功能接口函数的第一个参数(handle)。am_pcf85063_handle_t 类型的定义(am_pcf85063.h)如下:
若返回值为NULL,说明初始化失败;若返回值不为NULL,说明返回值handle 有效。
基于模块化编程思想,将初始化相关的实例、实例信息等的定义存放到对应的配置文件中,通过头文件引出实例初始化函数接口,源文件和头文件的程序范例分别详见程序清单6.39 和程序清单6.40。
程序清单6.39 实例初始化函数实现(am_hwconf_pcf85063.c)
程序清单6.40 实例初始化函数声明(am_hwconf_pcf85063.h)
后续只需要使用无参数的实例初始化函数,即可获取到PCF85063 的实例句柄。即:
>>>6.3.2 RTC 通用接口
PCF85063 作为一种典型的RTC 器件,可以使用RTC(Real-Time Clock)通用接口设置和获取时间,其函数原型详见表6.10。
表6.10 RTC 通用接口函数(am_rtc.h)
可见,这些接口函数的第一个参数均为am_rtc_handle_t 类型的RTC 句柄,显然,其并非前文通过PCF85063 实例初始化函数获取的am_pcf85063_handle_t 类型的句柄。
RTC 时间设置和获取只是PCF85063 提供的一个主要功能,PCF85063 还能提供闹钟等功能。PCF85063 的驱动提供了相应的接口用于获取PCF85063 的RTC 句柄,以便用户通过RTC 通用接口操作PCF85063,其函数原型为:
该函数意在获取RTC 句柄,其中,PCF85063 实例的句柄(pcf85063_handle)作为实参传递给handle,p_rtc 为指向am_rtc_serv_t 类型实例的指针,无实例信息。定义am_rtc_serv_t类型(am_rtc.h)实例如下:
其中,g_pcf85063_rtc 为用户自定义的实例,其地址作为p_rtc 的实参传递。
基于模块化编程思想,将初始化相关的实例定义存放到对应的配置文件中,通过头文件引出实例初始化函数接口,源文件和头文件分别详见程序清单6.41 和程序清单6.42。
程序清单6.41 新增PCF85063 的RTC 实例初始化函数(am_hwconf_pcf85063.c)
程序清单6.42 am_hwconf_pcf85063.h 文件内容更新(1)
后续只需要使用无参数的RTC 实例初始化函数,即可获取RTC 实例句柄。即:
1. 设置时间
该函数用于设置RTC 器件的当前时间值,其函数原型为:
其中,handle 为RTC 实例句柄,p_tm 为指向细分时间(待设置的时间值)的指针。返回AM_OK,表示设置成功,反之失败。其类型am_tm_t 是在am_time.h 中定义的细分时间结构体类型,用于表示年/月/日/时/分/秒等信息。即:
其中,tm_mon 表示月份,分别对应1~12 月。tm_year 表示年,1900 年至今的年数,其实际年为该值加上1900。tm_wday;表示星期,0~6 分别对应星期日~星期六。tm_yday 表示1 月1 日以来的的天数(0~365),0 对应1 月1 日。tm_isdst 表示夏令时,夏季将调快1 小时。如果不用,则设置为-1。设置年/月/日/时/分/秒的值详见程序清单6.43,星期等附加的一些信息无需用户设置,主要便于在获取时间时得到更多的信息。
程序清单6.43 设置时间范例程序
2. 获取时间
该函数用于获取当前时间值,其函数原型为:
其中,handle 为RTC 实例句柄,p_tm 为指向细分时间的指针,用于获取细分时间。返回AM_OK,表示获取成功,反之失败,范例程序详见程序清单6.44。
程序清单6.44 获取细分时间范例程序
基于RTC 通用接口,可以编写一个通用的时间显示应用程序:每隔1s 通过调试串口打印当前的时间值。应用程序的实现和接口声明分别详见程序清单6.45 和程序清单6.46。
程序清单6.45 RTC 时间显示应用程序(app_rtc_time_show.c)
程序清单6.46 RTC 时间显示接口声明(app_rtc_time_show.h)
为了启动该应用程序,必须提供一个RTC 实例句柄以指定设置时间和获取时间的RTC对象,若使用PCF85063,则RTC 实例句柄可通过实例初始化函数am_pcf85063_rtc_inst_init()获得,范例程序详见程序清单6.47。
程序清单6.47 启动RTC 应用程序(基于PCF85063)
>>>6.3.3 闹钟通用接口
PCF85063 除提供基本的RTC 功能外,还可以提供闹钟功能,可以使用闹钟通用接口设置使用闹钟,其函数原型详见表6.11。
表6.11 闹钟通用接口函数(am_alarm_clk.h)
由此可见,这些接口函数的第一个参数均为am_alarm_clk_handle_t 类型的闹钟句柄,PCF85063 的驱动提供了相应的接口用于获取PCF85063 的闹钟句柄,以便用户通过闹钟通用接口操作PCF85063,其函数原型为:
该函数意在获取闹钟句柄,其中,PCF85063 实例的句柄(pcf85063_handle)作为实参传递给handle,p_alarm_clk 为指向am_alarm_clk_serv_t 类型实例的指针,无实例信息。定义am_alarm_clk_serv_t 类型(am_alarm_clk.h)实例如下:
其中,g_pcf85063_alarm_clk 为用户自定义的实例,其地址作为p_alarm_clk 的实参传递。
基于模块化编程思想,将初始化相关的实例定义存放到对应的配置文件中,通过头文件引出实例初始化函数接口,源文件和头文件分别详见程序清单6.48 和程序清单6.49。
程序清单6.48 新增PCF85063 的闹钟实例初始化函数(am_hwconf_pcf85063.c)
程序清单6.49 am_hwconf_pcf85063.h 文件内容更新(2)
后续只需要使用无参数的闹钟实例初始化函数,即可获取闹钟实例句柄。即:
1. 设置闹钟时间
该函数用于设置闹钟时间,其函数原型为:
其中,handle 为闹钟实例句柄,p_tm 为指向闹钟时间(待设置的时间值)的指针。返回AM_OK,表示设置成功,反之失败。类型am_alarm_clk_tm_t 是在am_alarm_clk.h 中定义的闹钟时间结构体类型,用于表示闹钟时间信息。即:
其中,min 表示闹钟时间的分,hour 闹钟时间的小时,wdays 用于指定闹钟在周几有效,可以是周一至周日的任意一天或几天。其可用的值已经使用宏进行了定义,比如,AM_ALARM_CLK_SUNDAY 位星期日有效,AM_ALARM_CLK_MONDAY 为星期一有效,AM_ALARM_CLK_TUESDAY 为星期二有效,AM_ALARM_CLK_WEDNESDAY 为星期三有效,AM_ALARM_CLK_THURSDAY 为星期四有效,M_ALARM_CLK_FRIDAY 为星期五有效,AM_ALARM_CLK_SATURDAY 为星期六有效,AM_ALARM_CLK_WORKDAY为工作日有效,AM_ALARM_CLK_EVERYDAY 为每天均有效。
若需闹钟在多天同时有效,则可以将多个宏值使用“|”连接起来,比如,要使闹钟在星期一和星期二有效,则其值为:
AM_ALARM_CLK_MONDAY | AM_ALARM_CLK_TUESDAY。
若需闹钟在星期一至星期五有效(工作日有效),则其值为:
AM_ALARM_CLK_WORKDAY。
若需闹钟在每一天均有效,这其值为AM_ALARM_CLK_EVERYDAY,设置闹钟的范例程序详见程序清单6.50。
程序清单6.50 设置闹钟时间的范例程序
2. 设置闹钟回调函数
PCF85063 可以在指定的时间产生闹钟事件,当事件发生时,由于需要通知应用程序,因此需要由应用程序设置一个回调函数,在闹钟事件发生时自动调用应用程序设置的回调函数。设置闹钟回调函数原型为:
其中,handle 为闹钟实例句柄,pfn_callback 为指向实际回调函数的指针,p_arg 为回调函数的参数。若返回AM_OK,表示设置成功,反之失败。
函数指针的类型am_pfnvoid_t 在am_types.h 中定义,即:
当闹钟事件发生时,将自动调用pfn_callback 指向的回调函数,传递给该回调函数的void*类型的参数就是p_arg 设定值,范例程序详见程序清单6.51。
程序清单6.51 设置闹钟回调函数范例程序
3. 打开闹钟
该函数用于打开闹钟,以便当闹钟时间到时,自动调用用户设定的回调函数,其函数原型为:
其中,handle 为闹钟实例句柄。返回AM_OK,表示打开成功,反之失败,范例程序详见程序清单6.52。
程序清单6.52 打开闹钟范例程序
4. 关闭闹钟
该函数用于关闭闹钟,其函数原型为:
其中,handle 为闹钟实例句柄。返回AM_OK,表示关闭成功,反之失败,范例程序详见程序清单6.53。
程序清单6.53 关闭闹钟范例程序
基于闹钟通用接口,可以编写一个通用的闹钟测试应用程序:设定当前时间为09:32:30,闹钟时间为09:34,一分半后,达到闹钟时间,蜂鸣器鸣叫1 分钟。闹钟测试应用程序的实现和接口声明分别详见程序清单6.54 和程序清单6.55。
程序清单6.54 闹钟测试应用程序(app_alarm_clk_test.c)
程序清单6.55 闹钟测试应用程序接口声明(app_alarm_clk_test.h)
为了启动该应用程序,必须提供一个RTC 实例句柄以设置当前时间与一个闹钟实例句柄用于设置闹钟,若使用PCF85063,则RTC 实例句柄可通过am_pcf85063_rtc_inst_init()获得,闹钟实例句柄可通过am_pcf85063_alarm_clk_inst_init()获得,范例程序详见程序清单6.56。
程序清单6.56 启动闹钟测试应用程序(基于PCF85063)
>>>6.3.4 系统时间
AMetal 平台提供了一个系统时间,进行设置和获取系统时间的函数原型详见表6.12。
表6.12 系统时间接口函数(am_time.h)
1. 系统时间
系统时间的3 种表示形式分别为日历时间、精确日历时间、细分时间,细分时间前文已有介绍,这里仅介绍日历时间和精确日历时间。
日历时间
与标准C 的定义相同,日历时间表示从1970 年1 月1 日1 时0 分0 秒开始的秒数。其类型am_time_t 定义如下:
精确日历时间
日历时间精度为秒,精确日历时间的精度可以达到纳秒,精确日历时间只是在日历时间的基础上,增加了一个纳秒计数器,其类型am_timespec_t(am_time.h)定义如下:
当纳秒值达到1000000000 时,则秒值加1;当该值复位为0 时,则重新计数。
2. 初始化
使用系统时间前,必须初始化系统时间,其函数原型为:
其中,rtc_handle 用于指定系统时间使用的RTC,系统时间将使用该RTC 保存时间和获取时间。update_sysclk_ns 和 update_rtc_s 用以指定更新系统时间相关的参数。
RTC 句柄rtc_handle
获取RTC 句柄可通过RTC 实例初始化函数获取,以作为rtc_handle 的实参传递。即:
与系统时间更新相关的参数(update_sysclk_ns 和 update_rtc_s)
每个MCU 都有一个系统时钟,比如,LPC824,其系统时钟的频率为30MHz,常常称之为主频,在短时间内,该时钟的误差是很小的。由于直接读取MCU 中的数据要比通过I2C读取RTC 器件上的数据快得多,因此根据系统时钟获取时间值比直接从RTC 器件中获取时间值要快得多,完全可以在短时间内使用该时钟更新系统时间,比如,每隔1ms 将精确日历时间的纳秒值增加1000000。但长时间使用该时钟来更新系统时间,势必产生较大的误差,这就需要每隔一定的时间重新从RTC 器件中,读取精确的时间值来更新系统时间,以确保系统时间的精度。
update_sysclk_ns 为指定使用系统时钟更新系统时间的时间间隔,其单位为ns,通常设置为1~100ms,即1000000~100000000。update_rtc_s 为指定使用RTC 器件更新系统时间的时间间隔,若对精度要求特别高,将该值设置为1,即每秒都使用RTC 更新一次系统时间,通常设置为10~ 60 较为合理。
基于此,将初始化函数调用在添加到配置文件中,通过头文件引出系统时间的实例初始化函数接口,详见程序清单6.57 和程序清单6.58。
程序清单6.57 PCF85063 用作系统时间的实例初始化(am_hwconf_pcf85063.c)
程序清单6.58 am_hwconf_pcf85063.h 文件内容更新(2)
后续只需要简单的调用该无参函数,即可完成系统时间的初始化。即:
3. 设置系统时间
根据不同的时间表示形式,有2 种设置系统时间方式。
精确日历时间设置的函数原型为:
其中,p_tv 为指向精确日历时间(待设置的时间值)的指针。若返回AM_OK,表示设置成功,反之失败,范例程序详见程序清单6.59。
程序清单6.59 使用精确日历时间设置系统时间范例程序
将精确日历时间的秒值设置为了1472175150,该值是从1970 年1 月1 日0 时0 分0 秒至2016 年8 月26 日09 时32 分30 秒的秒数。即将时间设置为2016 年8 月26 日09 时32分30 秒。通常不会这样设置时间值,均是采用细分时间方式设置时间值。
细分时间设置的函数原型为:
其中,p_tm 为指向细分时间(待设置的时间值)的指针。若返回AM_OK,表示设置成功,反之失败,范例程序详见程序清单6.60。
程序清单6.60 使用细分时间设置系统时间范例程序
将时间设置为2016 年8 月26 日09:32:30,当使用细分时间设置时间值时,则细分时间的成员tm_wday, tm_yday 在调用后被更新。如果不使用夏令时,则设置为-1。
4. 获取系统时间
根据不同的时间表示形式,有3 种获取系统时间的方式。
获取日历时间的函数原型为:
其中,p_time 为指向日历时间的指针,用于获取日历时间。返回值同样为日历时间,若返回值为-1,表明获取失败,通过返回值获取日历时间的范例程序详见程序清单6.61。
程序清单6.61 通过返回值获取日历时间范例程序
也可以通过参数获得日历时间,范例程序详见程序清单6.62。
程序清单6.62 通过参数获取日历时间范例程序
获取精确日历时间的函数原型为:
其中,p_tv 为指向精确日历时间的指针,用于获取精确日历时间。若返回AM_OK,获取成功,反之失败,范例程序详见程序清单6.63。
程序清单6.63 读取精确日历时间范例程序
获取细分时间的函数原型为:
其中,p_tm 为指向细分时间的指针,用于获取细分时间。若返回AM_OK,表示获取成功,反之失败,范例程序详见程序清单6.64。
程序清单6.64 获取细分时间范例程序
基于系统时间相关接口,可以编写一个通用的系统时间测试应用程序:每隔1s 通过调试串口打印当前的系统时间值。应用程序的实现和接口声明分别详见程序清单6.65 和程序清单6.66。
程序清单6.65 系统时间测试应用程序(app_sys_time_show.c)
程序清单6.66 系统时间测试应用程序接口声明(app_sys_time_show.h)
由此可见,在应用程序中,不再使用到任何实例句柄,使得应用程序不与任何具体器件直接关联,系统时间的定义使得应用程序在使用时间时更加便捷。在启动应用程序前,必须完成系统时间的初始化,若使用PCF85063 为系统时间提供RTC 服务,则系统时间的初始化可以通过am_pcf85063_time_inst_init ()完成,范例程序详见程序清单6.67。
程序清单6.67 启动系统时间测试应用程序(基于PCF85063)
>>>6.3.5 特殊功能控制接口
对于PCF85063,除典型的时钟和闹钟功能外,还具有一些特殊功能,如定时器、时钟输出、1 字节RAM 等。这些功能由于不是通用功能,只能使用PCF85063 相应的接口进行操作。以读写1 字节RAM 为例,其相应的接口函数详见表6.13。
表6.13 读写RAM 接口函数(am_pcf85063.h)
1. 写入RAM
该函数用于写入1 字节数据到PCF85063 的RAM 中,其函数原型为:
其中,handle 为PCF85063 实例句柄,data 为写入的单字节数据。若返回AM_OK,表示数据写入成功,反之失败,写入0x55 至RAM 中的范例程序详见程序清单6.68。
程序清单6.68 写入RAM 范例程序
2. 读取RAM
该函数读取存于PCF85063 的单字节RAM 中的数据,其函数原型为:
其中,handle 为PCF85063 实例句柄,p_data 为输出参数,用于返回读取到的单字节数据。返回AM_OK,表示读取成功,反之失败,范例程序详见程序清单6.69。
程序清单6.69 读取范例程序
可以使用读写RAM 接口简单验证PCF85063 是否正常,详见程序清单6.70。
程序清单6.70 读写RAM 数据范例程序
全部0条评论
快来发表一下你的评论吧 !