;**********************************************************;
; ;
; Atmel 8-Bit RISC MCU ATmega8 ;
; ;
; PCF8563 RTC Driver ;
; ;
; Powered By SWUST SiChuan China JiangHaibo ;
; ;
; soundman@sohu.com (Not For Support) ;
; ;
; V1.20 2006-03-18 ;
; ;
; Warning : All Right Reserved ;
; ;
; HardWare Interface : ATmega8, PCF8563 V1.0 Driver ;
; SoftWare Interface : ICCAVR 6.28 ;
; ;
; Last Version : V1.20 ;
; Publish Day : 2006-08-19 ;
; ;
;程序名称: PCF8563 RTC驱动 ;
;程序开发: 西南科技大学 江海波 ;
;编写时间: 2005年7月5日 ;
;软件环境: ICCAVR上层C语言调用 ;
;硬件环境: 基于软I2C接口的PCF8563 ;
;MCU: ATmega8 @8M ;
;程序版本: V1.11 ;
;最后修订时间: 2006年08月19日 ;
;修订部分说明: V1.10修改了set_time函数的参数格式,底层不再 ;
; 需要对松散的BCD代码进行压缩 ;
; V1.11修正了V1.10中设置年及设置秒函数的错误 ;
; V1.20修正了使用R28的错误 ;
;**********************************************************;
;定义伪指令,当硬件结构改变时修改本位置上的指令即可完成程序修改
;----------------------------------------------------------
;I2C的SDA信号被设置在端口sda_port的sda_bit位上
.define sda_port PORTB
sda_bit =5
.define sda_rport PINB
;----------------------------------------------------------
;MAX7219的时钟CLK信号被设置在端口led_clk_port的led_clk_bit位上
.define scl_port PORTB
scl_bit =4
;----------------------------------------------------------
;SDA端口的方向寄存器为
.define sda_ddr DDRB
;----------------------------------------------------------
;为了程序调适方便及系统自检,硬件Debug由函数的返回值定义
;Debug 代码表:
i2c_ack =$01 ;PCF8563应答成功
i2c_nack =$02 ;PCF8563没有应答主机
RTC_pd =$03 ;PCF8563发生过掉电
;----------------------------------------------------------
;资源占用
;1.寄存器
; R0在设置年的函数中用于数据计算
; R16计数器,计算每次发送/接收的8个BIT,同时在
; 停止时作为延迟时间用计数器
; R17计数器,计算启动总线时读取ACK的次数
; 在本软件中,系统将尝试启动总线3次
; R17同时还作为接收,设置时间时的数据个数计数器用
; R18接收一个字节时用的临时变量
; R24全局变量,用于暂存最后向C的返回值
; R25接收,发送一个字节子程序传递数据用寄存器
; X指针,指向显示"rtc_buffer"数据传递区
; R27,接收一个字节时的临时寄存器
; Z指针,BCD编,解码时用的双指针
;2.RAM空间
; rtc_buff为首的7个字节
;----------------------------------------------------------
;函数使用说明:
;1._get_time: 带参数函数,将当前时间读入到指定的数组中,数据
; 共计13个字节长度
;例如
; debug_code=get_time(&time[0]);
;
; 其中debug_code表征本次调用的硬件错误代码
; 读到的13字节数据存放在time[0..13]中
; 数据为已经解码的松散BCD格式,数据格式见下
; 表所示
; time[0] = Seconds Low
; time[1] = Seconds High
; time[2] = Minutes Low
; time[3] = Minutes High
; time[4] = Hour Low
; time[5] = Hour High
; time[6] = Data Low
; time[7] = Data High
; time[8] = Week Low
; time[9] = Week High
; time[10] = Month Low
; time[11] = Month High
; time[12] = Year Low
; time[13] = Year High
;为了照顾格式,我们将"周"数据也扩展为2个字节,其高字节固定为"0"
;----------------------------------------------------------
;2._set_time: 带参数函数,调用前将数据以压缩BCD的格式放入
; 参数为首的数组中,数据的结构与PCF8563中的数据
; 结构相同
;例如
; set_time(&time[0]);
;----------------------------------------------------------
;3._init_rtc: 无参数函数,调用本函数可初始化RTC,该函数可在其
; 他应用中改写以适应其系统要求
;例如
; init_rtc();
;----------------------------------------------------------
;4._set_hhmm: 带参数函数,调用前将数据以松散BCD的格式放入
; 参数为首的数组中,数据的结构与上表相同
;例如
; set_hhmm(&time[2]);
;----------------------------------------------------------
;5._set_year: 带参数函数,调用前将数据以松散BCD的格式放入
; 参数为首的数组中,数据的结构与上表相同
;例如
; set_year(&time[12]);
;----------------------------------------------------------
;6._set_second: 无参数函数,调用本函数可将RTC的秒设置为"0"
; 调用本函数将清除掉电标志
;例如
; set_second();
;----------------------------------------------------------
;7._set_week: 带参数函数,调用前将数据以松散BCD的格式放入
; 参数为首的数组中,有效数据长度为2个字节
;例如
; set_week(&time[8]);
;----------------------------------------------------------
;8._set_ddmm: 带参数函数,调用前将数据以松散BCD的格式放入
; 参数为首的数组中,有效数据长度为4个字节
;例如
; set_ddmm(&time[6]);
;----------------------------------------------------------
;.area text
;-----------------------Delay For I2C----------------------
i2c_delay_5us: ;Delay 5uS
ldi R16,7 ;Counter For Delay
i2c_d_5_loop:
nop
subi R16,1
brne i2c_d_5_loop
ret
i2c_delay_1us3: ;Delay 1.3uS, e.x. 11 "nop"s
nop
nop
i2c_delay_0us6: ;Delay 0.6uS, e.x. 5 "nop"s
ret
;---------------------Start The I2C Bus--------------------
i2c_start: ;Start I2C Bus, The Slave Address Is Transd In R25
cbi sda_port,sda_bit ;SDA=0, Start The I2C Bus
rcall i2c_delay_0us6 ;Thd:STA=0.6uS
cbi scl_port,scl_bit ;SCL=0
rcall i2c_delay_0us6 ;Tlow=1.3uS
;Then Trans The I2C Slave Address, Write Direction To The Bus Using The Following Function
;-----------------------Trans One Byte---------------------
i2c_trans: ;---I2C TRANS 1 byte in R25
ldi R16,8 ;Counter, For 8 BIT
i2c_t_loop: ;---I2C Trans 1 byte LOOP
cbi sda_port,sda_bit ;Clear SDA, Default Trans '0'
rol R25
brcc i2c_t_l_0
sbi sda_port,sda_bit ;Trans BIT '1'
i2c_t_l_0: ;---I2C Trans Loop '0'
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_0us6 ;Thigh=0.6uS
cbi scl_port,scl_bit ;SCL=0
subi R16,1
brne i2c_t_loop ;Trans 8 BIT Loop
;-------------------------Get ACK--------------------------
i2c_ack_rd: ;---I2C ACKnowledge ReaD
rcall i2c_delay_0us6 ;Tlow=1.3uS
cbi sda_ddr,sda_bit ;SDA Port Become Input To Read ACK
sbi sda_port,sda_bit ;Enable Onchip Pull-up
sbi scl_port,scl_bit ;SCL=1, Start The Acknowledge Clock
ldi R24,i2c_nack ;Because The Onchip Pull-up Resistor Is Not Very Strong, We Placed A Delay Function
rcall i2c_delay_0us6 ;There To Immunity Parasitic Capacitance
sbis sda_rport,sda_bit ;Check SDA Line
ldi R24,i2c_ack ;Device Ackloaged
cbi scl_port,scl_bit ;SCL=0, Tlow=1.3uS
cbi sda_port,sda_bit ;To Combin With Re-Start, To Set The SDA Line '0'
sbi sda_ddr,sda_bit ;SDA Port Become Output After Read ACK
ret
;-------------------------ACK GEN--------------------------
i2c_nack_gen: ;---I2C Not-ACKnowledge GENerate
sbi sda_port,sda_bit ;SDA=1, To Not-Acknowledge The Slave
rjmp i2c_ack_trans
i2c_ack_gen: ;---I2C ACKnowledge GENerate
cbi sda_port,sda_bit ;SDA=0, To Acknowledge The Slave
i2c_ack_trans: ;Trans ACK/NACK To The Bus
nop ;Tlow=1.3uS
nop
nop
nop
nop
sbi scl_port,scl_bit ;SCL=1, Generate The Ack. Clock
rcall i2c_delay_0us6 ;Thigh=0.6uS
cbi scl_port,scl_bit ;SCL=0, End The Ack. Clock
ret
;-----------------------Receive One Byte-------------------
i2c_receive: ;---I2C RECEIVE 1 byte to R25
ldi R16,$80
mov R2,R16
ldi R16,8 ;Counter, For 8 BIT
cbi sda_ddr,sda_bit ;SDA Port Become Input To Read Data
sbi sda_port,sda_bit ;Enable Onchip Pull-up
clr R25 ;Clear R25 Register To Init. Receive
i2c_r_loop:
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_1us3 ;Thigh=0.6uS, For '1' Signal Stabilization
sbic sda_rport,sda_bit ;Sample SDA Line
or R25,R2 ;Received a '1'
lsr R2
cbi scl_port,scl_bit ;SCL=0
rcall i2c_delay_1us3 ;Tlow=1.3uS
subi R16,1
brne i2c_r_loop
sbi sda_ddr,sda_bit ;SDA Port Become Output After Read Data
ret
;--------------------------Decode--------------------------
decode: ;Decode Time Date
ld R25,Z+ ;Load Low Half
ld R16,Z+ ;Load High Half
swap R16
or R16,R25
st X+,R16
subi R17,1 ;The No. Of Date Is Passed In R17
brne decode ;Encode All dates
ret
;-------------------------Get Time-------------------------
_get_time:: ;Get Time Form The RTC
movw XL,R16 ;Init Pointer X
ldi R17,3 ;Counter, Count For At Most 3 Times Bus Start
get_t_st: ;Get Time Start The Bus
ldi R25,$A2 ;Start The I2C Bus Write Dir.
rcall i2c_start
cpi R24,i2c_ack
breq get_t_read
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_5us ;Tsu:STO=4uS
sbi sda_port,sda_bit ;SDA=1
rcall i2c_delay_5us ;Tbuf=4.7uS Accord To Philips I2C-Bus Specification V2.1
subi R17,1
brne get_t_st ;Re-try For 3 Times
mov R16,R24 ;Declare I2C Start Error
ret
get_t_read:
ldi R25,$02 ;Word Address
rcall i2c_trans
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_5us ;Tsu:STO=4uS
sbi sda_port,sda_bit ;SDA=1
rcall i2c_delay_5us ;Tbuf=4.7uS Accord To Philips I2C-Bus Specification V2.1
ldi R25,$A3 ;Start The I2C Bus Read Dir.
rcall i2c_start
ldi R17,6 ;Counter, Count For 6 Dates
get_t_loop:
rcall i2c_receive
st X+,R25
rcall i2c_ack_gen
subi R17,1
brne get_t_loop
rcall i2c_receive ;Receive The Last Date
rcall i2c_nack_gen
cbi sda_port,sda_bit ;SDA=0, Stop The Bus
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_5us ;Tsu:STO=4uS
sbi sda_port,sda_bit ;SDA=1
movw ZL,XL
adiw ZL,7
mov R16,R25 ;Decode Year
swap R25
andi R25,$0F
st Z,R25 ;High Half Of Year
andi R16,$0F
sbiw ZL,1
st Z,R16 ;Low Half Of Year
ld R25,-X
mov R16,R25 ;Decode Month
swap R25
andi R25,$01 ;High Half Of Month
st -Z,R25
andi R16,$0F ;Low Half Of Month
st -Z,R16
ldi R16,$00 ;High Half Of Week, Always $00
st -Z,R16 ;Decode Week
ld R25,-X
andi R25,$07 ;Low Half Of Week
st -Z,R25
ld R25,-X
mov R16,R25 ;Decode Day
swap R25
andi R25,$03 ;High Half Of Day
st -Z,R25
andi R16,$0F ;Low Half Of Day
st -Z,R16
ld R25,-X
mov R16,R25 ;Decode Hour
swap R25
andi R25,$03 ;High Half Of Hour
st -Z,R25
andi R16,$0F ;Low Half Of Hour
st -Z,R16
ld R25,-X
mov R16,R25 ;Decode Minute
swap R25
andi R25,$07 ;High Half Of Minute
st -Z,R25
andi R16,$0F ;Low Half Of Minute
st -Z,R16
ld R25,-X
sbrc R25,7 ;Test Bit.7 Of Seconds
ldi R24,RTC_pd ;If Bit.7='1', Indicate The Power Has Been Droped
mov R16,R25 ;Decode Seconds
swap R25
andi R25,$0F ;High Half Of Seconds
st -Z,R25
andi R16,$0F ;Low Half Of Seconds
st -Z,R16
; sei ;Enable Interrupt
mov R16,R24
ret
;-------------------------Set Time-------------------------
_set_time::
movw XL,R16 ;Init Pointer X
ldi R25,$A2 ;Start The I2C Bus Write Dir.
rcall i2c_start
ldi R25,$02 ;Word Address
rcall i2c_trans
ldi R17,7 ;Counter, Count For 7 Dates
set_t_loop:
ld R25,X+
rcall i2c_trans
subi R17,1
brne set_t_loop
rjmp i2c_stop
;-------------------------Init RTC-------------------------
_init_rtc::
ldi R25,$A2 ;Start The I2C Bus Write Dir.
rcall i2c_start
ldi R25,$00 ;Word Address
rcall i2c_trans
ldi R25,$00 ;Cont/Stat1, TEST1=Normal, STOP=Run, TESTC=No POR
rcall i2c_trans
ldi R25,$00 ;Cont/Stat2, AF/TF=Clear, AIE/TIE=Disable
rcall i2c_trans
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_5us ;Tsu:STO=4uS
sbi sda_port,sda_bit ;SDA=1
rcall i2c_delay_5us ;Tbuf=4.7uS Accord To Philips I2C-Bus Specification V2.1
ldi R25,$0D ;Word Address
rcall i2c_trans
ldi R25,$00 ;Clock Out, Clk=Stop, Freq.=32768
rcall i2c_trans
ldi R25,$03 ;Timer Reg, Timer=Disable, Freq.=1/60 For Power Saving
rcall i2c_trans
;---------------------Stop The I2C Bus---------------------
i2c_stop: ;Stop I2C Bus
cbi sda_port,sda_bit ;SDA=0
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_5us ;Tsu:STO=4uS
sbi sda_port,sda_bit ;SDA=1
mov R16,R24
ret
;-----------------------Set HH-MiMi------------------------
_set_hhmm:: ;Set Hour And Minute
movw XL,R16 ;Init Pointer X
movw ZL,XL
ldi R17,2
rcall decode ;Decode 2 Date
ldi R25,$A2 ;Start The I2C Bus Write Dir.
rcall i2c_start
ldi R25,$03 ;Word Address
rcall i2c_trans
sbiw XL,2
ld R25,X+ ;Minute
rcall i2c_trans
ld R25,X ;Hour
rcall i2c_trans
rjmp i2c_stop
;-----------------------Set Year---------------------------
_set_year:: ;Set Year
movw ZL,R16
ld R0,Z+
ld R16,Z
swap R16
or R0,R16 ;Year Date
ldi R16,$08
mov R1,R16 ;Word Address, Year
set_1b_trans: ;Set 1 Byte Time Date Trans
ldi R25,$A2 ;Start The I2C Bus Write Dir.
rcall i2c_start
mov R25,R1 ;Word Address
rcall i2c_trans
mov R25,R0 ;Date That Will Be Write To RTC
rcall i2c_trans
rjmp i2c_stop
;----------------------Set Second--------------------------
_set_second:: ;Set Second, Clear To "0"
clr R0 ;Second Date, Clear To "0"S
ldi R25,$02 ;Word Address, Second
mov R1,R25
rjmp set_1b_trans
;----------------------Set WeekDay-------------------------
_set_week:: ;Set WeekDay
movw ZL,R16
ld R0,Z ;Week Date
ldi R16,$06
mov R1,R16 ;Word Address, Year
rjmp set_1b_trans
;------------------------Set DD-MM-------------------------
_set_ddmm:: ;Set Day And Month
movw XL,R16 ;Init Pointer X
movw ZL,XL
ldi R17,3
rcall decode ;Decode 3 Date, Include WeekDay
ldi R25,$A2 ;Start The I2C Bus Write Dir.
rcall i2c_start
ldi R25,$05 ;Word Address, Day
rcall i2c_trans
sbiw XL,3
ld R25,X
rcall i2c_trans
cbi sda_port,sda_bit ;SDA=0, Stop The I2C Bus
sbi scl_port,scl_bit ;SCL=1
rcall i2c_delay_5us ;Tsu:STO=4uS
sbi sda_port,sda_bit ;SDA=1
adiw XL,2
ld R0,X
ldi R16,$07
mov R1,R16 ;Word Address, Month
rjmp set_1b_trans
;---------------------------------------------------------- |