| ;**********************************************************; ;                                                          ;
 ;               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
 
 ;----------------------------------------------------------
 |