如何为MAXQ610微控制器增加DS1904实时时钟

Abstract

MAXQ610微控制器不具备电池供电的实时时钟(RTC),但是利用1-Wire®网络的灵活性,可方便地为任何基于MAXQ610的应用直接增加一个DS1904 RTC iButton®。MAXQ610能够与DS1904通信、设定时钟和控制,并且能够相互转换时间值与原始的秒计数值,即使采用汇编语言也能支持这些功能。本应用笔记介绍如何为基于MAXQ610的应用增加RTC功能,提供了实现这一功能的例程。本文介绍的原理和方法同样适用于其它基于MAXQ®、具有1-Wire驱动通信协议的通用I/O (GPIO)的微控制器。


本应用笔记正在准备公开,您的申请将自动批准,您会收到该篇应用笔记。

概述

MAXQ610是一款灵活的低功耗微控制器,非常适合便携应用。但是,MAXQ610并未集成电池供电的实时时钟(RTC)。

本应用笔记介绍如何在基于MAXQ610的应用中增加电池供电的DS1904 RTC iButton。应用笔记中的例程采用基于汇编的MAX-IDE环境为MAXQ610编写,设计运行于MAXQ610-KIT评估(EV)板。演示应用的源代码、工程文件以及其它文档可从网站下载

尽管本应用笔记讨论的程序代码专门针对MAXQ610微控制器编写,但本文讨论的原理和方法也能很好地适用于其它基于MAXQ、具有1-Wire驱动通信协议通用I/O (GPIO)的微控制器。

关于Maxim提供的iButton及其它1-Wire器件的更多信息,请参考iButton产品1-Wire产品网站。

硬件和软件要求

运行演示程序需要以下硬件支持。

  • MAXQ610-KIT评估板
  • 5V直流电源
  • 串行至JTAG或USB至JTAG接口板
  • JTAG编程电缆(2 × 5扁平电缆,带0.100in引脚连接器)
  • DB9直通串口电缆
  • PC机提供空闲的COM口(或USB至串口转换器)
  • DS1904L-F5# RTC iButton
  • DS9094F+通孔装配iButton夹
程序设计运行在带有iButton夹(DS9094F+)的MAXQ610评估板,iButton夹安装在原型区,DS1904L-F5# RTC iButton插入iButton夹子内。通过iButton夹连接:
  • iButton夹的GROUND引脚(夹子顶端引脚位置标有“+”,连接DS1904的背面/无标签面)连接到MAXQ610评估板的一个GND测试点。
  • iButton夹子的DATA引脚(夹子的内侧引脚,连接DS1904的前面/标签面)连接到MAXQ610评估板的端口引脚P2.0 (插头引脚P3.1)。
运行本应用笔记提供的例程需要以下软件。
  • 用于MAXQ的MAX-IDE汇编语言开发环境
  • Microcontroller Tool Kit (MTK)或其它具有“哑终端”模式的终端仿真器
可从MAXQ微控制器网页下载最新的MAX-IDE环境安装包和文档。

DS1904特性

DS1904是一款坚固的RTC iButton模块,占用最少的硬件资源。数据采用1-Wire协议串行传输,仅需1根数据线和1根地回路。DS1904具有唯一的64位工厂激光刻制ROM ID,包含被作为二进制计数器的RTC/日历。坚固耐用的MicroCan封装对恶劣环境具有极高的耐受能力,例如灰尘、潮湿和振动。DS1904借助附件几乎可以安装在任何物体表面,包括:印制电路板(PCB)和塑料钥匙扣。DS1904能够为任何基于微控制器的电子装置或嵌入式应用添加日历、时间和日期标签、秒表、计时表、计时器及日志功能。

DS1904 RTC由一个分辨率为1s的32位计数器组成,可提供大约136年的计时日历。维持时钟运行的所有硬件(包括32kHz晶振和电池)都密封在F5 MicroCan封装内,DS1904能够支持10年以上的工作时间。室温(+25°C)下,时钟精度大约为每月±2分钟。工作模式(暂停或运行状态)及时钟计数值通过1-Wire接口进行读写。这意味着仅需占用MAXQ610的1个GPIO引脚即可与DS1904进行通信。

设计目标

本例程设计的主要目标是利用DS1904的1-Wire接口进行以下操作。

  • 读取DS1904的64位ROM ID。
  • 启动和停止RTC。
  • 读取RTC的当前值。
  • 将RTC设定为一个新值。
该示例还能够以直接读取格式显示当前的RTC时间,即将原始的秒计数值转换为年/月/日/时间格式。允许用户对不同的转换数据(例如:年、月、日)进行递增控制,修改时钟值,不需要用户计算新的秒数并将其输入到器件内。

与其它任何保存原始秒计数值的时钟设计相同,必须选择一个“年份”基线,将原始的秒计数值转换成年/月/日/时间格式。本应用中,所选基线为2000年1月1日12:00:00 am,为原始秒计数值为00000000h时对应的日期/时间。

1-Wire网络驱动

Maxim已经开发了各种1-Wire接口传感器等元件。该接口利用一根数据线和地线进行供电和传输数据,意味着微控制器可通过单个端口与1-Wire传感器通信。

1-Wire工作时采用一个主控制器和多个从器件架构,即多点配置。定时要求非常灵活,允许所有从器件以高达16kbps的通信速率与主控制器同步工作。每个1-Wire传感器都有一个全球唯一的64位ROM ID,所以,1-Wire主控制器可独立、准确地选择从器件,而与网络的物理位置无关。

1-Wire数据线工作在开漏模式:主控制器(以及从器件,请求它们输出数据时)将数据拉低至地电位表示“0”,数据线浮空在高电平表示“1”。通常,这种工作方式采用安装在数据线和VCC之间的分立上拉电阻实现。但在使用MAXQ610等端口引脚具有弱上拉的微控制器时,仅需将端口引脚切换至该模式并使数据线浮空在高电平即可,无需外部上拉电阻。由于主控制器和从器件只用于拉低数据线,从来不会主动将数据线拉高,所以,1-Wire网络工作在“线或”配置下。这种方法可防止多个从器件试图同时在1-Wire总线上传输数据时发生冲突。

为了驱动1-Wire网络,MAXQ610必须在软件控制下通过单个引脚产生下列时隙。由于所有时隙均由1-Wire主控制器启动,MAXQ610不与从器件通信时无需监测1-Wire数据线。关于1-Wire通信的定时要求,请参考DS1904的数据资料。

  • 复位时隙宽度约为1ms,该时隙的前半部分,主控制器(也就是MAXQ610)将1-Wire线保持在低电平。经过一半时隙后,主控制器释放1-Wire总线,使其悬浮空在高电平。总线出现的任何1-Wire从器件都通过自身复位并在时隙的后半部分拉低总线进行响应。从器件产生一个应答脉冲,向主控制器表示线上存在一个或多个1-Wire从器件,并已准备就绪进行通信。

  • 时隙的长度大约为60µs至120µs,由主控制器向一个或多个1-Wire从器件发送“0”或“1”。两种写时隙都由主控制器在开始时将数据线拉低至少一个微秒。若发送“1”,主控制器则在时隙的剩余时间内释放1-Wire总线(即悬浮在高电平);若传输“0”,主控制器则继续将数据线保持在低电平,直到时隙结束。

  • 时隙长度大约为60µs至120µs,由主控制器从某个从器件读取“0”或“1”。时隙开始时,由主控制器将数据线拉低至少一个微秒。然后主控制器释放数据线,使从器件能够拉低数据线(表示“0”)或使数据线悬浮在高电平(表示“1”)。时隙期间,主控制器对数据线进行采样,读取从器件的数据位。
由于MAXQ610工作速率大约为每微秒12个指令周期(12MHz),软件可利用一个端口引脚(P2.0)轻松执行标准的1-Wire协议。
#define OWIN       M0[0Ah].0   ; PI2.0
#define OWOUT      M0[02h].0   ; PO2.0
#define OWDIR      M0[12h].0   ; PD2.0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  Function    : Reset1Wire
;;  Description : Sends a standard-speed 1-Wire reset pulse
;;                and checks for a presence pulse reply.
;;  Inputs      : None
;;  Outputs     : C - Cleared on success; set on error (no presence
;;                    pulse detected)
;;  Destroys    : PSF, LC[0]
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Reset1Wire:
   move  OWDIR, #1           ; Output mode
   move  OWOUT, #0           ; Drive low
   move  LC[0], #RESET_LOW
   djnz  LC[0], $

   move  OWOUT, #1           ; Snap high
   move  LC[0], #SNAP
   djnz  LC[0], $

   move  OWDIR, #0           ; Change to weak pullup input
   move  LC[0], #RESET_PRESAMPLE
   djnz  LC[0], $

   move  C, OWIN             ; Check for presence detect

   move  LC[0], #RESET_POSTSAMPLE
   djnz  LC[0], $

   ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  Function    : Write1Wire
;;  Description : Writes a standard-speed 1-Wire output byte.
;;  Inputs      : GRL - Byte to write to 1-Wire.
;;  Outputs     : None.
;;  Destroys    : PSF, AP, APC, A[0], LC[0], LC[1]
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Write1Wire:
   move  APC, #080h          ; Standard mode, select A[0] as Acc
   move  Acc, GRL
   move  OWDIR, #1           ; Output drive mode
   move  LC[1], #8           ; 8 bits to write

Write1Wire_slot:
   move  OWOUT, #0           ; Drive low for start of write slot

   move  LC[0], #WRITE_PREBIT
   djnz  LC[0], $

   rrc                       ; Get the next bit
   jump  C, Write1Wire_one
Write1Wire_zero:
   move  OWOUT, #0           ; Keep the line low (zero bit)
   jump  Write1Wire_next
Write1Wire_one:
   move  OWOUT, #1
Write1Wire_next:
   move  LC[0], #WRITE_POSTBIT
   djnz  LC[0], $            ; Finish the time slot

   move  OWOUT, #1           ; Drive back high (end of slot)
   move  LC[0], #WRITE_RECOVERY
   djnz  LC[0], $            ; Recovery time period
   
   djnz  LC[1], Write1Wire_slot

   ret
发送读时隙的功能与此类似。注意,1-Wire总线的所有数据字节均为最低有效位(LSB)在前。

另外,还需要注意利用MAXQ610实现1-Wire总线时隙的特殊性。根据1-Wire网络器件数量的不同,1-Wire总线的上拉电阻会发生变化,典型值大约为4kΩ至5kΩ。而MAXQ610端口引脚的弱上拉电阻接近于15kΩ至40kΩ,取决于工作电压。为避免1-Wire总线从低电平跳变到高电平时花费过长时间,例程中在将端口引脚设定为标称弱上拉模式之前,在P2.0端口驱动一个标准的高电平并维持一个简短时间,从而使总线“快速”拉至高电平状态。只要该操作没有在器件拉低总线期间进行,就不会在1-Wire总线上引起任何问题。一种替代方案是在1-Wire总线上安装一个外部上拉电阻,将端口引脚驱动为标准低电平时表示“0”状态,三态模式表示高电平状态。

启动、停止和设置时钟

在与1-Wire总线器件进行通信时,数据传输涉及两个独立阶段。因为总线上可能挂接了不止一个1-Wire器件,需要通过两个过程执行操作。在第一阶段,总线主控制器选择需要通信的1-Wire器件;在第二阶段,总线主控制器与该器件通信。

关于下列命令的详细信息,请参考DS1904数据资料。

选择1-Wire从器件

一旦总线主控制器发送1-Wire复位脉冲,1-Wire总线上的所有从器件均复位至默认的未选中状态。在第一阶段,总线主控制器可利用几条命令选择在第二阶段需要读取或写入的1-Wire器件。所有1-Wire器件都支持这组与每个从器件64位ROM ID相关的命令集。

Skip ROM [CCh]

该单字节命令用于激活总线上的所有从器件。如果总线上只有一个1-Wire器件,或总线主控制器需要向总线上所有1-Wire器件发送相同命令,即可采用该ROM命令快速激活主控制器需要通信的器件。

由于示例中在总线上只有一个器件(DS1904),所以MAXQ610在绝大多数情况下都可使用该命令激活DS1904,进行后续的读、写操作。

Read ROM [33h]

该单字节命令用于激活总线上的所有从器件,并使其发送64位ROM ID至总线的主控制器。由于该命令激活全部从器件,只有当总线上仅有单个器件时才可使用。若在不止一个从器件的总线上使用该命令,不同器件都将尝试把各自的ROM ID发送至总线主控制器,将导致数据冲突。

由于示例中只在总线上挂接了一个从器件(DS1904),MAXQ610可以在示例开始利用该命令读取DS1904的ROM ID。

Match ROM [55h]

该命令用于从1-Wire总线的多个从器件中选择一个从器件。总线主控制器发送该命令后,紧接着发送需要选择的从器件的64位ROM ID。ROM ID与之相匹配的从器件将进入开启状态,响应总线主控制器,而总线上的其它从器件则保持禁止状态,并等待总线主控制器的下一次1-Wire复位。

该示例没有使用该命令。

Search ROM [F0h]

总线主控制器通过该命令采用迭代查找算法确定1-Wire总线上一个或多个从器件的ROM ID,详细信息请参考DS1904数据资料。

本示例中没有使用该命令。

DS1904时钟和控制的读、写操作

一旦总线主控制器(MAXQ610)通过Skip ROM命令或Read ROM命令选择了1-Wire从器件(DS1904),DS1904即准备就绪接收发给自己的1-Wire命令。以下详细介绍了这些命令,参见图1

图1. DS1904的时钟功能命令(摘自DS1904数据资料)
图1. DS1904的时钟功能命令(摘自DS1904数据资料)

Read Clock [66h]

总线主控制器利用该命令从DS1904读取器件控制字节及4字节(32位) RTC数值。器件控制字节决定了32kHz振荡器(驱动RTC)处于运行状态还是停止状态。

如以下代码所示,只用一条命令读取器件控制字节和时钟值。即使不需要两个数值,也必须在器件输出时钟数据之前先读取器件的控制字节。

ds1904_get_control:
   push    Acc

   call    Reset1Wire

   move    Acc, #OW_SKIP_ROM
   call    Write1Wire
   move    Acc, #OW_READ_CLOCK
   call    Write1Wire

   call    Read1Wire         ; Device control byte
   move    A[2], Acc

   call    Reset1Wire

   pop     Acc
   ret


ds1904_get_clock:
   push    Acc
   push    GR

   call    Reset1Wire

   move    Acc, #OW_SKIP_ROM
   call    Write1Wire
   move    Acc, #OW_READ_CLOCK
   call    Write1Wire

   call    Read1Wire         ; Device control byte (ignore)
   call    Read1Wire         ; RTC[7:0]
   move    GRL, Acc
   call    Read1Wire         ; RTC[15:8]
   move    GRH, Acc
   move    A[2], GR
   call    Read1Wire         ; RTC[23:16]
   move    GRL, Acc
   call    Read1Wire         ; RTC[31:24]
   move    GRH, Acc
   move    A[3], GR

   call    Reset1Wire

   pop     GR
   pop     Acc
   ret

Write Clock [99h]

该命令是读时钟命令的补充,它使总线主控制器能够将DS1904的器件控制字节和4字节时钟计数器设定到一个新的数值。注意,只有当5个字节全部写入并且发送一个1-Wire复位脉冲之后,新的数值才生效。

该示例提供了一个分别设置器件控制字节和时钟的例程。代码首先从DS1904读取5个字节(1个器件控制字节和4字节时钟计数器)的数据,然后进行适当改变后再将数据写回器件。

ds1904_set_clock:
   push    Acc
   push    GR

   call    Reset1Wire

   move    Acc, #OW_SKIP_ROM
   call    Write1Wire
   move    Acc, #OW_READ_CLOCK
   call    Write1Wire

   call    Read1Wire         ; Device control byte (save)
   move    GR, Acc
   call    Read1Wire         
   call    Read1Wire         
   call    Read1Wire         
   call    Read1Wire         

   call    Reset1Wire

   move    Acc, #OW_SKIP_ROM
   call    Write1Wire
   move    Acc, #OW_WRITE_CLOCK
   call    Write1Wire

   move    Acc, GR           ; Device control byte
   call    Write1Wire

   move    GR, A[2]
   move    Acc, GRL
   call    Write1Wire        ; New clock LSB
   move    Acc, GRH
   call    Write1Wire

   move    GR, A[3]
   move    Acc, GRL
   call    Write1Wire        
   move    Acc, GRH
   call    Write1Wire        ; New clock MSB

   call    Reset1Wire

   pop     GR
   pop     Acc
   ret



ds1904_set_control:
   push    Acc
   push    GR
   push    A[3]
   push    A[4]

   call    Reset1Wire

   move    Acc, #OW_SKIP_ROM
   call    Write1Wire
   move    Acc, #OW_READ_CLOCK
   call    Write1Wire

   call    Read1Wire         ; Device control byte 

   call    Read1Wire         ; Clock LSB
   move    GRL, Acc
   call    Read1Wire         
   move    GRH, Acc
   move    A[3], GR

   call    Read1Wire         
   move    GRL, Acc
   call    Read1Wire         
   move    GRH, Acc          ; Clock MSB
   move    A[4], GR

   call    Reset1Wire

   move    Acc, #OW_SKIP_ROM
   call    Write1Wire
   move    Acc, #OW_WRITE_CLOCK
   call    Write1Wire

   move    Acc, A[2]         ; New device control byte
   call    Write1Wire

   move    GR, A[3]
   move    Acc, GRL
   call    Write1Wire        ; New clock LSB
   move    Acc, GRH
   call    Write1Wire

   move    GR, A[4]
   move    Acc, GRL
   call    Write1Wire        
   move    Acc, GRH
   call    Write1Wire        ; New clock MSB

   call    Reset1Wire

   pop     A[4]
   pop     A[3]
   pop     GR
   pop     Acc
   ret

转换时间和日期值

为了把原始的秒计数值转换成打印格式,需要应用程序分别确定每个日期和时间字段的数值(例如年、月、日、时、分和秒)。通常,实现这一功能的程序首先从最大字段(年)开始,然后逐步向下转换。

  1. 当秒计数值≥ (每年秒数)时,从秒计数值中减去(每年秒数)并递增年份。
  2. 当秒计数值≥ (每月秒数)时,从秒计数值中减去(每月秒数)并递增月份。
  3. 当秒计数值≥ (每天秒数)时,从秒计数值中减去(每天秒数)并递增日期。
  4. 当秒计数值≥ (每小时秒数)时,从秒计数值中减去(每小时秒数)并递增小时值。
  5. 当秒计数值≥ 60时,从秒数中减去60并递增分钟值。
  6. 剩下的秒计数值即为秒字段数值。
即使MAXQ610硬件支持除法运算,一次简单的除法运算也不能够计算前两个字段的数值(年和月)。考虑到闰年(涉及到年和月)和每月天数(仅涉及月)的不同,每个字段的秒数是变化的。

例如,从2000年开始(该年为闰年),我们计算如下:

(2000年一年的秒数) = 366 (天数) × 24 (小时/日) × 60 (分钟/小时) × 60 (秒/分钟) = 31,622,400秒
而对于标准年份,一年的天数减少一天(365天),这就意味着(秒数/年)变为:(31,622,400 - 86,400) = 31,536,000。由于每4年有一个闰年,所以我们可计算年份如下。
  1. 如果秒计数值≥ (闰年秒数),从秒计数值中减去(每个闰年的秒数)并递增年份,否则停止。
  2. 如果秒计数值≥ (每年秒数),从秒计数值中减去(每年秒数)并递增年份,否则停止。
  3. 如果秒计数值≥ (每年秒数),从秒计数值中减去(每年秒数)并递增年份,否则停止。
  4. 如果秒计数值≥ (每年秒数),从秒计数值中减去(每年秒数)并递增年份,否则停止。
  5. 返回至第1行。
月份的计算方法类似。
  1. 如果秒计数值≥ (一月秒数),从秒计数值中减去(一月秒数)并递增月份,否则停止。
  2. 如果秒计数值≥ (二月秒数),从秒计数值中减去(二月秒数)并递增月份,否则停止。
  3. 如果秒计数值≥ (三月秒数),从秒计数值中减去(三月秒数)并递增月份,否则停止。
  4. 依次类推。
注意,二月份的秒数也受当年是否为闰年的影响。
;; A[6]  - Number of days in current month
;; A[7]  - Leap-year flag (leap year if 1, regular year if 0)
;; A[8]  - RTC value (low 16 bits)
;; A[9]  - RTC value (high 16 bits)
;; A[10] - Seconds  (00-59)
;; A[11] - Minutes  (00-59)
;; A[12] - Hours    (00-23)
;; A[13] - Days     (01-31, depending on month)
;; A[14] - Months   (01-12)
;; A[15] - Years    (00-99)

convToFields:
   push    A[8]
   push    A[9]

   call    zeroFields

convToFields_year:
   move    A[3], #SEC_IN_LEAPYEAR_H
   move    A[2], #SEC_IN_LEAPYEAR_L
   call    Sub32
   jump    C, convToFields_month
   call    incYear

   move    A[3], #SEC_IN_YEAR_H
   move    A[2], #SEC_IN_YEAR_L
   call    Sub32
   jump    C, convToFields_month
   call    incYear

   move    A[3], #SEC_IN_YEAR_H
   move    A[2], #SEC_IN_YEAR_L
   call    Sub32
   jump    C, convToFields_month
   call    incYear

   move    A[3], #SEC_IN_YEAR_H
   move    A[2], #SEC_IN_YEAR_L
   call    Sub32
   jump    C, convToFields_month
   call    incYear
   jump    convToFields_year

convToFields_month:
   move    A[3], #SEC_IN_MON_31_H      ; >Jan
   move    A[2], #SEC_IN_MON_31_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_28_H      ; >Feb
   move    A[2], #SEC_IN_MON_28_L
   move    Acc, A[7]
   jump    Z, convToFields_month_noLeap
   move    A[3], #SEC_IN_MON_29_H      ; >Feb
   move    A[2], #SEC_IN_MON_29_L
   

convToFields_month_noLeap:
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_31_H      ; >Mar
   move    A[2], #SEC_IN_MON_31_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_30_H      ; >Apr
   move    A[2], #SEC_IN_MON_30_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_31_H      ; >May
   move    A[2], #SEC_IN_MON_31_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_30_H      ; >Jun
   move    A[2], #SEC_IN_MON_30_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_31_H      ; >Jul
   move    A[2], #SEC_IN_MON_31_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_31_H      ; >Aug
   move    A[2], #SEC_IN_MON_31_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_30_H      ; >Sep
   move    A[2], #SEC_IN_MON_30_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_31_H      ; >Oct
   move    A[2], #SEC_IN_MON_31_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth

   move    A[3], #SEC_IN_MON_30_H      ; >Nov
   move    A[2], #SEC_IN_MON_30_L
   call    Sub32
   jump    C, convToFields_day
   call    incMonth


convToFields_day:
   move    A[3], #SEC_IN_DAY_H
   move    A[2], #SEC_IN_DAY_L
   call    Sub32
   jump    C, convToFields_hour
   call    incDay
   jump    convToFields_day

convToFields_hour:
   move    A[3], #SEC_IN_HOUR_H
   move    A[2], #SEC_IN_HOUR_L
   call    Sub32
   jump    C, convToFields_minute
   call    incHour
   jump    convToFields_hour

convToFields_minute:
   move    A[3], #0
   move    A[2], #60
   call    Sub32
   jump    C, convToFields_second
   call    incMinute
   jump    convToFields_minute

convToFields_second:
   move    A[10], A[8]

   pop     A[9]
   pop     A[8]
   ret
示例应用程序按照类似的方式将字段转换成秒数。此时,代码将每个字段累计值增加至秒数(而不是减)。

运行示例

运行例程时,请加载并运行应用程序。然后利用DB9串行电缆将MAXQ610评估板的J1 SKT连接至PC的COM1。启动MTK (或其它终端仿真器)并以38400波特率打开COM1。例程的初始化输出类似如下:

@
ID: 24B91231000000B2  AC  18F83065

+   18F83065   Apr 10 2013, 02:15:01 pm
+   18F83066   Apr 10 2013, 02:15:02 pm
+   18F83067   Apr 10 2013, 02:15:03 pm
+   18F83068   Apr 10 2013, 02:15:04 pm
+   18F83069   Apr 10 2013, 02:15:05 pm
代码的第二行含有DS1904的ROM ID (24B91231000000B2)、器件控制字节(AC)和当前的时钟(18F83065)。在随后几行中,“+”表示时钟正在运行。时间在发生变化后即进行刷新并显示,应该为每秒一次。

按下“-”停止时钟。此时,即可通过按下其它键修改时钟值,如下所示。

+ 再次启动时钟并开始自动更新。
Y 增加年份(将月份和日期复位为01/01)。
M 增加月份(将日期复位为01)。
D 增加日期(根据当前的月份循环)。
h 增加小时。
m 增加分钟。
s 将秒计数值复位为00。
Z 将计数器归零,将时间复位至2001年1月1日12:00:00 am。

结论

尽管MAXQ610不带电池供电的RTC,但是利用1-Wire网络的灵活性,可采用DS1904 RTC iButton方便地为任何基于MAXQ610的应用增加一个RTC。MAXQ610能够与DS1904通信、设定时钟和控制,并且可以转换时间和原始的秒计数值,使用汇编语言即可实现。

参考电路

1 Refer to the DS1904 RTC data sheet for more details on 1-Wire timing requirements.

2 For more details on the following commands, please refer to the DS1904 data sheet.

3 Ibid., https://www.analog.com/en/products/ds1904.html