http://blog.csdn.net/dndxhej/article/details/7727489
RTC
概述
在系统电源关掉时RTC可以在备份电池的支持下来工作.RTC可以使用STRB/LDRB指令传输8bit的BCD值到CPU.数据包括秒,分,时,日期,天,月和年.RTC工作在外部32.768KHz的晶振下,而且有报警功能.
属性
BCD:秒,分,时,日期,天,月和年
闰年产生器
报警功能:报警中断 从power-off模式唤醒
独立的电源管脚(RTCVDD)
为RTOS kernel time tick支持毫秒级的tick.
闰年产生器
闰年产生器通过BCDDATA,BCDMON和BCDYEAR来决定每个月最后一天的日期.一个8bit的计数器只能表示两个BCD码,所以无法决定'00'年是否是闰年.举个例子,它不能区分1900和2000.为了解决这个问题,s3c2440的RTC模块在硬件逻辑上支持闰年是2000.1900不是闰年而2000时闰年.因此s3c2440的00表示2000,而不是1900.
READ/WRITE REGISTERS
为了写BCD寄存器,RTCCON寄存器的第0位必须置高.为了显示秒,分,时,日期,月和年,CPU从BCDSEC,BCDMIN,BCDHOUR,BCDDAY,BCDDATE,BCDMON和BCDYEAR寄存器中读数据.然而,在读取多个寄存器时,可能会有1秒的偏移.比如,当user读数据的时候,假设结果是2059(Year),12(Month),31(Date),23(Hour)和59(Minute).当user读BCDSEC寄存器,值的范围是1--59s,到这没有问题,但是,如果这个值是0s,由于刚才提到的那一s的偏移,年月日时分就会变为2060(year),1(Month),1(Date),0(Hour)和0(Minute).在这种情况下,如果BCDSEC是0 user应该重读各个寄存器.
BACKUP BATTERY OPERATION
RTC由备份电池驱动,及时系统电源断了,备份电池可以通过RTCVDD管脚向RTC模块供电.当系统关闭,CPU和RTC逻辑之间的接口是封闭的,备份电池只是驱动晶振电路和BCD计数器来减少电源消耗.
ALARM FUNCTION
在power-off模式或者正常操作模式下,RTC可以在一个指定的时间产生一个报警信号.在正常操作模式下,报警中断(INT_RTC)是激活的.在power-off模式,电源管理唤醒信号(PMWKUP)和INT_RTC都是激活的.RTC报警寄存器(RTCALM)决定是否开启报警状态和报警时间的设置.
TICK TIME INTERRUPT
RTC的tick time用于中断请求.TICNT寄存器有中断使能位和中断的计数值.当tick time中断产生计数值为0.中断的周期:
Period = (n+1)/128 second
n:Tick time count value(1~127)
REAL TIME CLOCK SPECIAL REGISTERS
RTCCON
RTCCON控制4个bits,比如RTCEN控制BCD寄存器的读写使能,CLKSEL,CNTSEL和CLKRST是用于测试的.
RTCEN bit控制CPU与RTC之间的所有接口,所以在系统重启后应该在RTC控制程序中将其设为1来使能数据的读写.在电源关闭前,RTCEN应该清0来防止对RTC寄存器的不经意的写入.
Register Address R/W Description Reset Value
RTCCON 0x57000040(L)/0x57000043(B) R/W RTC control register 0x0
RTCCON Bit Description Initial State
RTCEN [0] RTC控制使能 0
注意:所有的RTC寄存器都要以字节为单位来访问,可以用STRB和LDRB汇编指令或者char类型的指针.
TICNT
Register Address R/W Description Reset Value
TICNT 0x57000044(L)/0x57000047(B) R/W(by byte) Tick time count register 0x0
TICNT BIT Description Initial State
TICK INT ENABLE [7] tick time 中断使能 0
TICK TIME COUNT [6:0] tick time 计数值(1~127) 000000
RTCALM
RTCALM决定alarm使能和alarm时间.在power-off模式下RTCALM寄存器通过INT_RTC和PWMKUP产生报警信号,而在正常模式下只通过INT_RTC.
Register Address R/W Description Reset Value
RTCALM 0x57000050(L)/0x57000053(B) R/W(by type) RTC报警控制寄存器 0x0
RTCALM Bit Description Initial State
ALMEN [6] 报警总开关 0
YEAREN [5] Year报警开关 0
MONREN [4] Month报警开关 0
DATEEN [3] Date报警开关 0
HOUREN [2] Hour报警开关 0
MINEN [1] Minute报警开关 0
SECEN [0] Second报警开关 0
ALMSEC
设置Second报警的具体秒数
ALMMIN
设置Minute报警的具体分钟数
ALMHOUR
设置Hour报警的具体小时数
ALMDATE
设置Date报警的具体日期
ALMMON
设置Mon报警的具体月份
ALMYEAR
设置Year报警的具体年份
BCDSEC BCDMIN BCDHOUR BCDDATE BCDMON BCDYEAR
用BCD码表示的秒 分 时 日期 月份 年
给出报警中断的rtc汇编和c代码如下,在报警中断时是调用PWM的蜂鸣器来做闹钟的:
start.S:
/* watchdog timer with disable reset copyleft@ [email protected] */ .equ NOINT, 0xc0 .equ GPBCON, 0x56000010 @led .equ GPBDAT, 0x56000014 @led .equ GPBUP, 0x56000018 @led .equ GPFCON, 0x56000050 @interrupt config .equ EINTMASK, 0x560000a4 .equ EXTINT0, 0x56000088 .equ EXTINT1, 0x5600008c .equ EXTINT2, 0x56000090 .equ INTMSK, 0x4A000008 .equ EINTPEND, 0x560000a8 .equ SUBSRCPND, 0x4a000018 .equ INTSUBMSK, 0x4a00001c .equ SRCPND, 0X4A000000 .equ INTPND, 0X4A000010 .equ GPHCON, 0x56000070 .equ GPHDAT, 0x56000074 .equ GPB5_out, (1<<(5*2)) .equ GPB6_out, (1<<(6*2)) .equ GPB7_out, (1<<(7*2)) .equ GPB8_out, (1<<(8*2)) .equ GPBVALUE, (GPB5_out | GPB6_out | GPB7_out | GPB8_out) .equ LOCKTIME, 0x4c000000 .equ MPLLCON, 0x4c000004 .equ UPLLCON, 0x4c000008 .equ M_MDIV, 92 .equ M_PDIV, 1 .equ M_SDIV, 1 .equ U_MDIV, 56 .equ U_PDIV, 2 .equ U_SDIV, 2 .equ CLKDIVN, 0x4c000014 .equ DIVN_UPLL, 0 .equ HDIVN, 1 .equ PDIVN, 1 @FCLK : HCLK : PCLK = 1:2:4 .equ WTCON, 0x53000000 .equ Pre_scaler, 249 .equ wd_timer, 1 .equ clock_select, 00 @316 .equ int_gen, 1 @开中断 .equ reset_enable, 0 @关掉重启信号 .equ WTDAT,0x53000004 .equ Count_reload,50000 @定时器定为2S PCLK = 100M PCLK/(Pre_scaler+1)/clock_select = 100M/(249+1)/16=25k 50000/25k=2s .equ WTCNT,0x53000008 .equ Count,50000 .equ TCFG0,0x51000000 .equ Prescaler1,0x00 @[15:8]Timer234 .equ Prescaler0,249 @[7:0]Timer01 .equ TCFG1,0x51000004 .equ DMA_MODE,0x0 @[23:20]no dma channal .equ MUX0,0x2 @[3:0] 1/8 @定时器输入时钟周期 = PCLK/(prescaler + 1)/(divider value) @clk = 100M/(249+1)/8=25k .equ TCON,0x51000008 .equ DZ_eable,0 @[4]关闭死区的操作 .equ auto_reload,1 @[3]auto_reload .equ inverter,1 @[2]打开电平反转 .equ man_update,1 @[1]手动更新 .equ clear_man_update,0 .equ start,1 @[0]开始 .equ stop,0 @[0]停止 .equ TCNTB0,0x5100000c .equ TCMPB0,0x51000010 .equ TCNTO0,0x51000014 .equ ULCON0, 0x50000000 .equ IR_MODE, 0x0 @[6]正常模式 .equ Parity_Mode, 0x0 @[5:3]无校验位 .equ Num_of_stop_bit, 0x0 @[2]一个停止位 .equ Word_length, 0b11 @[1:0]8个数据位 .equ UCON0, 0x50000004 .equ FCLK_Div, 0 @[15:12] 时钟源选择用PCLK,所以这里用默认值 .equ Clk_select, 0b00 @[11:10] 时钟源选择使用PCLK .equ Tx_Int_Type, 1 @[9] 中断请求类型为Level .equ Rx_Int_Type, 0 @1 @[8] 中断请求类型为Level .equ Rx_Timeout, 0 @[7] .equ Rx_Error_Stat_Int, 1 @[6] .equ Loopback_Mode, 0 @[5] 正常模式 .equ Break_Sig, 0 @[4] 不发送终止信号 .equ Tx_Mode, 0b01 @[3:2] 中断请求或轮循模式 .equ Rx_Mode, 0b01 @[1:0] 中断请求或轮循模式 .equ UFCON0, 0x50000008 .equ Tx_FIFO_Trig_Level, 0b00 @[7:6] .equ Rx_FIFO_Trig_Level, 0b00 @[5:4] .equ Tx_FIFO_Reset, 0b0 @[2] .equ Rx_FIFO_Reset, 0b0 @[1] .equ FIFO_Enable, 0b0 @[0] 非FIFO模式 .equ UMCON0, 0x5000000C @这个寄存器可以不管的 .equ UTRSTAT0, 0x50000010 .equ UERSTAT0, 0x50000014 .equ UFSTAT0, 0x50000018 .equ UMSTAT0, 0x5000001C .equ UTXH0, 0x50000020 @(L 小端) .equ URXH0, 0x50000024 @(L 小端) .equ UBRDIV0, 0x50000028 .equ UBRDIV, 0x35 @PCLK=400M/4=100M UBRDIV = (int)(100M/115200/16) - 1 = 53 = 0x35 .equ BCDMIN,0x57000074 .equ BCDSEC,0x57000070 //.global Buzzer_Freq_Set .global _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used @b irq ldr pc, _irq ldr pc, _fiq _undefined_instruction: .word undefined_instruction _software_interrupt: .word software_interrupt _prefetch_abort: .word prefetch_abort _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq .balignl 16,0xdeadbeef reset: ldr r3, =WTCON mov r4, #0x0 str r4, [r3] @ disable watchdog ldr r0, =GPBCON ldr r1, =0x15400 @这个时候暂不配置GPB0为TOUT0 str r1, [r0] ldr r2, =GPBDAT ldr r1, =0x160 str r1, [r2] bl clock_setup bl uart_init bl delay msr cpsr_c, #0xd2 @进入中断模式 ldr sp, =3072 @中断模式的栈指针定义 msr cpsr_c, #0xd3 @进入系统模式 ldr sp, =4096 @设置系统模式的栈指针 @-------------------------------------------- ldr r0, =GPBUP ldr r1, =0x03f0 str r1, [r0] ldr r0, =GPFCON ldr r1, =0x2ea@0x2 str r1, [r0] ldr r0, =EXTINT0 @ldr r1, =0x8f888@0x0@0x8f888 @~(7|(7<<4)|(7<<8)|(7<<16)) //低电平触发中断 ldr r1, =0xafaaa@0x0@0x8f888 //下降沿触发中断 str r1, [r0] ldr r0, =EINTPEND ldr r1, =0xf0@0b10000 str r1, [r0] ldr r0, =EINTMASK ldr r1, =0x00@0b00000 str r1, [r0] ldr r0, =SRCPND ldr r1, =0x3ff | (1<<30) @0x1@0b11111 str r1, [r0] ldr r0, =SUBSRCPND ldr r1, =0x1<<13 str r1, [r0] ldr r0, =INTPND ldr r1, =0x3ff | (1<<30) @0x1@0b11111 str r1, [r0] ldr r0, =INTSUBMSK ldr r1, =0x0<<13 str r1, [r0] ldr r0, =INTMSK ldr r1, =0x1ffff000@0b00000 str r1, [r0] MRS r1, cpsr BIC r1, r1, #0x80 MSR cpsr_c, r1 bl main irq: sub lr,lr,#4 stmfd sp!,{r0-r12,lr} bl irq_isr ldmfd sp!,{r0-r12,pc}^ irq_isr: ldr r2, =GPBDAT ldr r1, =0x0e0 str r1, [r2] ldr r3,=0xffffff delay2: sub r3,r3,#1 cmp r3,#0x0 bne delay2 //这上面的延时必须要,否则蜂鸣器的声音有问题 ldr r0,=EINTPEND ldr r1,=0xf0 str r1,[r0] ldr r0, =SRCPND ldr r1, =0x3ff | (1<<30) @0b11111 str r1, [r0] ldr r0, =SUBSRCPND ldr r1, =0x1<<13 str r1, [r0] ldr r0, =INTPND ldr r1, =0x3ff | (1<<30) @0b11111 str r1, [r0] ldr r2, =GPBCON ldr r1,[r2] ldr r1,[r1] //ldr r1, =0x15400 bic r1,r1,#0x3 orr r1,r1,#0x2 str r1,[r2] ldr r2, =GPBDAT ldr r1, =0x1a0 str r1, [r2] ldr r1,=TCFG0 ldr r2,=(Prescaler0<<0) str r2, [r1] ldr r1,=TCFG1 ldr r2,=(DMA_MODE<<20) | (MUX0<<0) str r2, [r1] // ldr r3,[r0] // str r3,[r2] //mov r2, r0 ldr r1,=TCNTB0 ldr r2,=10 str r2, [r1] //r0就是c调用汇编的传递参数 //mov r0,r0,LSR #2 ldr r1,=TCMPB0 ldr r2,=2 str r2, [r1] ldr r1,=TCON ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (start<<0) str r2, [r1] ldr r1,=TCON ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (clear_man_update<<1) | (start<<0) str r2, [r1] ldr r2, =GPBDAT ldr r1, =0x1a0 str r1, [r2] mov pc,lr delay: ldr r3,=0xffffff delay1: sub r3,r3,#1 cmp r3,#0x0 bne delay1 mov pc,lr clock_setup: ldr r0,=LOCKTIME ldr r1,=0xffffffff str r1, [r0] ldr r0,=CLKDIVN ldr r1,=(DIVN_UPLL<<3) | (HDIVN<<1) | (PDIVN<<0) str r1, [r0] ldr r0,=UPLLCON ldr r1,=(U_MDIV<<12) | (U_PDIV<<4) | (U_SDIV<<0) @Fin=12M UPLL=48M str r1, [r0] nop nop nop nop nop nop nop ldr r0,=MPLLCON ldr r1,=(M_MDIV<<12) | (M_PDIV<<4) | (M_SDIV<<0) @Fin=12M FCLK=400M str r1, [r0] mov pc,lr uart_init: ldr r0,=GPHCON ldr r1,=0x2aaaa @配置GPIO复用规则为串口 str r1, [r0] ldr r0,=ULCON0 ldr r1,=(IR_MODE<<6) | (Parity_Mode<<3) | (Num_of_stop_bit<<2) | (Word_length<<0) @ str r1, [r0] ldr r0,=UCON0 ldr r1,=(FCLK_Div<<12) | (Clk_select<<10) | (Tx_Int_Type<<9) | (Rx_Int_Type<<8) | (Rx_Timeout<<7) | (Rx_Error_Stat_Int<<6) |(Loopback_Mode<<5) | (Break_Sig<<4) | (Tx_Mode<<2) | (Rx_Mode<<0) str r1, [r0] ldr r0,=UFCON0 ldr r1,=(Tx_FIFO_Trig_Level<<6) | (Rx_FIFO_Trig_Level<<4) | (Tx_FIFO_Reset<<2) | (Rx_FIFO_Reset<<1) | (FIFO_Enable<<0) @ str r1, [r0] ldr r0,=UBRDIV0 ldr r1,=(UBRDIV<<0) str r1, [r0] mov pc,lr /* Buzzer_Freq_Set: //ldr r0, =GPBCON //ldr r1, =0x15400 @这个时候暂不配置GPB0为TOUT0,这时候只是配置GPB0为TOUT0 //str r1, [r0] ldr r2, =GPBCON ldr r1,[r2] ldr r1,[r1] //ldr r1, =0x15400 bic r1,r1,#0x3 orr r1,r1,#0x2 str r1,[r2] ldr r2, =GPBDAT ldr r1, =0x1a0 str r1, [r2] ldr r1,=TCFG0 ldr r2,=(Prescaler0<<0) str r2, [r1] ldr r1,=TCFG1 ldr r2,=(DMA_MODE<<20) | (MUX0<<0) str r2, [r1] // ldr r3,[r0] // str r3,[r2] //mov r2, r0 ldr r1,=TCNTB0 ldr r2,=10 str r2, [r1] //r0就是c调用汇编的传递参数 //mov r0,r0,LSR #2 ldr r1,=TCMPB0 ldr r2,=2 str r2, [r1] ldr r1,=TCON ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (start<<0) str r2, [r1] ldr r1,=TCON ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (clear_man_update<<1) | (start<<0) str r2, [r1] ldr r2, =GPBDAT ldr r1, =0x1a0 str r1, [r2] mov pc,lr */ main: ldr r2, =GPBDAT ldr r1, =0x1a0 str r1, [r2] bl delay //ldr r1,=TCON //ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (stop<<0) //str r2, [r1] /* ldr r2, =GPBCON ldr r1,[r2] ldr r1,[r1] //ldr r1, =0x15400 bic r1,r1,#0x3 orr r1,r1,#0x2 str r1,[r2] ldr r0,=TCFG0 ldr r1,=(Prescaler0<<0) str r1, [r0] ldr r0,=TCFG1 ldr r1,=(DMA_MODE<<20) | (MUX0<<0) str r1, [r0] ldr r0,=TCNTB0 ldr r1,=10 str r1, [r0] ldr r0,=TCMPB0 ldr r1,=2 str r1, [r0] ldr r0,=TCON ldr r1,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (start<<0) str r1, [r0] ldr r0,=TCON ldr r1,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (clear_man_update<<1) | (start<<0) str r1, [r0] */ ldr lr, =loop // ldr pc, _rtc_uart_test //_rtc_uart_test: .word rtc_uart_test bl rtc_uart_test ldr r2, =GPBDAT ldr r1, =0x1c0 str r1, [r2] bl delay loop: ldr r2, =BCDSEC @BCDMIN ldr r1,[r2] cmp r1, #0x06 bleq ledon b loop @ 死循环 ledon: ldr r2, =GPBDAT ldr r1, =0x160 str r1, [r2] ldr r3,=0xffffff delay3: sub r3,r3,#1 cmp r3,#0x0 bne delay3 mov pc,lr undefined_instruction: nop software_interrupt: nop prefetch_abort: nop data_abort: nop not_used: nop fiq: nop
#include <stdarg.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <ctype.h> #include "rtc_uart_test.h" extern void Buzzer_Freq_Set(int freq); //extern void Buzzer_Freq_Set( void ); char uart_GetByte(void) { while(!(rUTRSTAT0 & 0x1)); //Wait until THR is empty. return RdURXH0(); } void uart_GetString(char *pt) { while(*pt) uart_GetByte(); } void uart_SendByte(int data) { if(data=='\n') { while(!(rUTRSTAT0 & 0x2)); WrUTXH0('\r'); } while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty. WrUTXH0(data); } //==================================================================== void uart_SendString(char *pt) { while(*pt) uart_SendByte(*pt++); } void uart_Printf(char *fmt,...) { va_list ap; char string[256]; va_start(ap,fmt); //vsprintf(string,fmt,ap); uart_SendString(string); va_end(ap); } void uart_test(void) { char str[20] = "\nhello world\n"; int a = 97; //while(1) // uart_SendByte(a); uart_SendString(str); char s = uart_GetByte(); //if(s == 'a') if(s == 97) rGPBDAT = 0x1c0; //uart_SendByte(a); //uart_SendByte(97); //uart_SendByte('a'); uart_SendByte((int)s); uart_SendByte((int)'s'); } void rtc_uart_test(void) { rRTCCON = 0x1; rTICNT = 0x0; rRTCALM = 0x42; rBCDYEAR = 0x10 ; rBCDMON = 0x11 ; rBCDDATE = 0x07 ; rBCDDAY = 0x05 ; rBCDHOUR = 0x12 ; rBCDMIN = 0x03 ; rBCDSEC = 0x00 ; rALMMIN = 0x04; uart_SendString("begin\n"); //uart_Printf("year:%d\n",rBCDYEAR); } void pwm_uart_test(void) { int freq = 10; int i; for(i=0; i<100;i++) uart_SendString("app\n"); //Buzzer_Freq_Set( freq ) ; //Buzzer_Freq_Set( ) ; //uart_test(); uart_SendString("start\n"); /* int i; for(i=0;i<1000;i++) uart_SendString("wait\n"); while( 1 ) { char key = uart_GetByte(); uart_SendByte(key); if( key == 'a' || key == 'A' ) { if( freq < 2000 ) //lci 20000 freq += 10 ; uart_SendByte('a'); Buzzer_Freq_Set( freq ) ; } if( key == 'b' || key == 'B' ) { if( freq > 11 ) freq -= 10 ; uart_SendByte('b'); Buzzer_Freq_Set( freq ) ; } //uart_SendString( "\tFreq = %d\n", freq ) ; //if( key == ESC_KEY ) //{ // Buzzer_Stop() ; // return ; //} } */ }
ldr r2, =BCDSEC @BCDMIN
ldr r1,[r2]
cmp r1, #0x06
bleq ledon
通过这里的判断,可以明确时间有被设进去,可是时间不走.经过google,才确定是寄存器的设置问题.
到此,rtc的闹钟也实现了功能.明天就用这个做闹钟吧~~