锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

ARM外设 cortex-a8

时间:2022-08-23 01:00:01 阻抗式颗粒传感器

文章目录

  • SYSTEM
    • 1.GPIO
    • 2.clock
    • 3.power manager
    • 4.energy manager
    • 5.booting sequence
  • BUS
  • INTERRUPT
  • MEMORY
  • DMA
  • TIMRE
    • 1.watchdog
    • 2.RTC
    • 3.PWM
  • CONNECTIVITY STORAGE
    • 1.UART
    • 2.IIC
    • 3.SPI
    • 4.USB
    • 5.SD/MMC
  • ADC & DAC
    • 1.ADC
    • 2.触摸屏
    • 3.DAC
  • 番外:和CORTEX-M3的区别
    • gpio
    • clock
    • uart
    • interrupt
    • DMA
    • TIM


SYSTEM

1.GPIO

IO口分为通用口(gpio),和专用口,还有ADC,DAC口等

GPIO可以通过control每个寄存器配置工作模式IO口支持的工作模式不同:
? GPA0: 8 in/out port - 2xUART with flow control
? GPA1: 4 in/out port - 2xUART without flow control or 1xUART with flow control
? GPB: 8 in/out port - 2x SPI
? GPC0: 5 in/out port - I2S, PCM, AC97
? GPC1: 5 in/out port - I2S, SPDIF, LCD_FRM
? GPD0: 4 in/out port - PWM
? GPD1: 6 in/out port - 3xI2C, PWM, IEM
? GPE0,1: 13 in/out port - Camera I/F
? GPF0,1,2,3: 30 in/out port - LCD I/F
? GPG0,1,2,3: 28 in/out port - 4xMMC channel (Channel 0 and 2 support 4-bit and 8-bit mode, but channel 1, and channel 3 support only 4-bit mode)
? GPH0,1,2,3: 32 in/out port - Key pad, External Wake-up (up-to 32-bit). (GPH* groups are in Alive region)
? GPI: Low Power I2S, PCM (in/out port is not used), PDN configuration for power down is controlled by AUDIO_SS PDN Register.
? GPJ0,1,2,3,4: 35 in/out port - Modem IF, CAMIF, CFCON, KEYPAD, SROM ADDR[22:16]
? MP0_1,2,3: 20 in/out port - Control signals of EBI (SROM, NF, OneNAND)
? MP0_4,5,6,7: 32 in/out memory port - EBI (For more information about EBI configuration, refer to Chapter 5, and 6)
? MP1_0~8: 71 DRAM1 ports (in/out port is not used)
? MP2_0~8: 71 DRAM2 ports (in/out port is not used)
? ETC0, ETC1, ETC2, ETC4: 28 in/out ETC ports - JTAG, Operating Mode, RESET, CLOCK (ETC3 is reserved)

中断模式设置:
GPA0_INT_CON前三名寄存器:
在这里插入图片描述

2.clock

外部晶振 内部时钟发生器 内部PLL产生高频时钟 内部分频器分频
三个区域:

MSYS域:(main system) CPU(Cortex-A8内核)、DRAM控制器(DMC0和DMC1) 、IRAM&IROM······
DSYS域: (Display System)与视频显示、编解码等相关的模块
PSYS域:( Peripheral System)与各种内部外设时钟有关,如串口,SD接口,I2C,AC97,USB.
MSYS域:
ARMCLK: 给cpu内核工作时钟,即所谓的主频。
HCLK_MSYS: MSYS给域高频时钟DMC0和DMC1使用
PCLK_MSYS: MSYS域的低频时钟
HCLK_IMEM:给iROM和iRAM(合称iMEM)使用
DSYS域:
HCLK_DSYS:DSYS域高频时钟
PCLK_DSYS:DSYS域的低频时钟
PSYS域:
HCLK_PSYS:PSYS域的高频时钟
PCLK_PSYS:PSYS域的低频时钟
SCLK_ONENAND:

典型的时钟值:
? freq(ARMCLK) = 1000 MHz
? freq(HCLK_MSYS) = 200 MHz
? freq(HCLK_IMEM) = 100 MHz
? freq(PCLK_MSYS) = 100 MHz
? freq(HCLK_DSYS) = 166 MHz
? freq(PCLK_DSYS) = 83 MHz
? freq(HCLK_PSYS) = 133 MHz
? freq(PCLK_PSYS) = 66 MHz
? freq(SCLK_ONENAND) = 133 MHz, 166 MHz

外晶振有四种来源:

如上图所示,晶振用于RTC、system timer、clock
四倍频电路:
APLL:Cortex-A8内核 MSYS域
MPLL&EPLL:DSYS PSYS
VPLL:Video视频相关模块

倍频分频图:

初始化clock
第一步:首先选择不使用PLL。让外部24MHz原始时钟直接过去,绕过APLL那条路
第二步:设置定时间。默认值为0x0FFF,保险起见我们设置为0xFFFF
第三步:设置分频系统,决定PLL如何获得每分钟的最高时钟?
第4步:设置PLL,主要是设置PLL输入端24MHz能获得多少原始频率?的输出频率。我们按照默认设置值设置输出为ARMCLK为1GHz
第5步:打开PLL。前面4步已经设置好了所有的开关和分频系数,本步骤打开PLL后PLL开始工作,锁定频率后输出,然后经过分频得到各个频率。
时钟相关寄存器:

// 时钟控制器基地址
#define ELFIN_CLOCK_POWER_BASE 0xE0100000 

// 时钟相关的寄存器相对时钟控制器基地址的偏移值
#define APLL_LOCK_OFFSET 0x00 
#define MPLL_LOCK_OFFSET 0x08

#define APLL_CON0_OFFSET 0x100
#define APLL_CON1_OFFSET 0x104
#define MPLL_CON_OFFSET 0x108

#define CLK_SRC0_OFFSET 0x200
#define CLK_SRC1_OFFSET 0x204
#define CLK_SRC2_OFFSET 0x208
#define CLK_SRC3_OFFSET 0x20c
#define CLK_SRC4_OFFSET 0x210
#define CLK_SRC5_OFFSET 0x214
#define CLK_SRC6_OFFSET 0x218
#define CLK_SRC_MASK0_OFFSET 0x280
#define CLK_SRC_MASK1_OFFSET 0x284

#define CLK_DIV0_OFFSET 0x300
#define CLK_DIV1_OFFSET 0x304
#define CLK_DIV2_OFFSET 0x308
#define CLK_DIV3_OFFSET 0x30c
#define CLK_DIV4_OFFSET 0x310
#define CLK_DIV5_OFFSET 0x314
#define CLK_DIV6_OFFSET 0x318
#define CLK_DIV7_OFFSET 0x31c

c:

#define rREG_CLK_SRC0 (*(volatile unsigned int *)(ELFIN_CLOCK_POWER_BASE + CLK_SRC0_OFFSET)) 
#define rREG_APLL_LOCK (*(volatile unsigned int *)(ELFIN_CLOCK_POWER_BASE + APLL_LOCK_OFFSET)) 
#define rREG_MPLL_LOCK (*(volatile unsigned int *)(ELFIN_CLOCK_POWER_BASE + MPLL_LOCK_OFFSET)) 
#define rREG_APLL_CON0 (*(volatile unsigned int *)(ELFIN_CLOCK_POWER_BASE + APLL_CON0_OFFSET)) 
#define rREG_MPLL_CON (*(volatile unsigned int *)(ELFIN_CLOCK_POWER_BASE + MPLL_CON_OFFSET)) 
#define rREG_CLK_DIV0 (*(volatile unsigned int *)(ELFIN_CLOCK_POWER_BASE + CLK_DIV0_OFFSET))

#define APLL_MDIV 0X7d
#define APLL_PDIV 0X3
#define APLL_SDIV 0X1

#define CLK_DIV0_MASK 0x7fffffff

#define MPLL_MDIV 0X29b
#define MPLL_PDIV 0Xc
#define MPLL_SDIV 0X1

#define set_pll(mdiv,pdiv,sdiv) (1<<31|mdiv<<16|pdiv<<8|sdiv)
#define APLL_VAL set_pll(APLL_MDIV,APLL_PDIV,APLL_SDIV)
#define MPLL_VAL set_pll(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)

void clock_init(void)
{ 
        
    rREG_CLK_SRC0 = 0x0;

    rREG_APLL_LOCK = 0x0000FFFF;
    rREG_MPLL_LOCK = 0x0000FFFF;

    rREG_CLK_DIV0 =0x14131440;
    
    rREG_APLL_CON0 = APLL_VAL;
    rREG_MPLL_CON = MPLL_VAL;

    rREG_CLK_SRC0 = 0x10001111;
}

汇编:

.global clock_init
clock_init:
	ldr	r0, =ELFIN_CLOCK_POWER_BASE
	
	// 1 设置各种时钟开关,暂时不使用PLL
	ldr	r1, =0x0
	// 芯片手册P378 寄存器CLK_SRC:Select clock source 0 (Main)
	str	r1, [r0, #CLK_SRC0_OFFSET] 

	// 2 设置锁定时间,使用默认值即可
	// 设置PLL后,时钟从Fin提升到目标频率时,需要一定的时间,即锁定时间
	ldr	r1,	=0x0000FFFF					
	str	r1,	[r0, #APLL_LOCK_OFFSET] 
	str r1, [r0, #MPLL_LOCK_OFFSET] 

	// 3 设置分频
	// 清bit[0~31]
	ldr r1, [r0, #CLK_DIV0_OFFSET] 
	ldr	r2, =CLK_DIV0_MASK					
	bic	r1, r1, r2
	ldr	r2, =0x14131440						
	orr	r1, r1, r2
	str	r1, [r0, #CLK_DIV0_OFFSET]

	// 4 设置PLL
	// FOUT = MDIV*FIN/(PDIV*2^(SDIV-1))=0x7d*24/(0x3*2^(1-1))=1000 MHz
	ldr	r1, =APLL_VAL						
	str	r1, [r0, #APLL_CON0_OFFSET]
	// FOUT = MDIV*FIN/(PDIV*2^SDIV)=0x29b*24/(0xc*2^1)= 667 MHz
	ldr	r1, =MPLL_VAL						
	str	r1, [r0, #MPLL_CON_OFFSET]

	// 5 设置各种时钟开关,使用PLL
	ldr	r1, [r0, #CLK_SRC0_OFFSET]
	ldr	r2, =0x10001111
	orr	r1, r1, r2
	str	r1, [r0, #CLK_SRC0_OFFSET]

	mov	pc, lr

3.power manager

4.energy manager

5.booting sequence


BUS

1.AXI:
S5PV210由12个高性能AXI互连组成。作用是将总线
主设备互连到总线从设备。
寄存器:ASYNC_CONFIG0~10,ASYNC_CONFIG0的地址:0xE0F0_0000。只有第0位可设置:HALF_SYNC_SEL,决定同步器是使用半同步还是完全同步,它分隔了两个不同的时钟域。将此字段设置为“高”将选择半同步器,它比全同步器具有更好的性能。相反,由于跨时钟域,完全同步器具有更好的MTBF(平均无故障时间)。为了稳定运行,建议使用完全同步。

2.coresight:
debug相关

3.access controller(TZPC):
TZPC为信任区设计的安全系统中的保护位提供软件接口。它提供了系统灵活性,允许将不同的内存区域配置为安全或非安全。S5PV210由四个TZPC组成。有保护位和安全区域位:
保护位:这使您能够将最多32个内存区域编程为安全或非安全
安全区域位:这使您能够将内部RAM区域分为安全区域和非安全区域
此内存不是SRAM概念的内存,而是CPU的内存空间,4GB。我们可以规定不同的内存空间的安全区间来控制外设的安全性。4个TZCP负责不同的内存空间:

寄存器(以TZPC0为例):
TZPCR0SIZE(5位有效,最高128kb可设,0x20设置所有RAM空间为安全模式)、
TZPCDECPROT0Stat、TZPCDECPROT0Set、TZPCDECPROT0Clr、
TZPCDECPROT1Stat、TZPCDECPROT1Set、TZPCDECPROT1Clr、
TZPCDECPROT2Stat、TZPCDECPROT2Set、TZPCDECPROT2Clr、
TZPCDECPROT3Stat、TZPCDECPROT3Set、TZPCDECPROT3Clr、
TZPCPERIPHID0、TZPCPERIPHID1、TZPCPERIPHID2、TZPCPERIPHID3、
TZPCPCELLID0、TZPCPCELLID1、TZPCPCELLID2、TZPCPCELLID3


INTERRUPT

SoC对中断的实现机制:异常向量表,此文讲过
(1)异常向量表是CPU中某些特定地址的特定定义。当中断发生的时候,中断要想办法通知CPU去处理中断,怎么做到?这就要靠异常向量表。
(2)硬件已经决定了发生什么异常CPU自动跳转PC到哪个地址去执行,软件需要做的就是把处理这个异常的代码的首地址填入这个异常向量地址
(3)异常的定义就是突发事件,打断了CPU的正常常规业务,CPU不得不跳转到异常向量表中去执行异常处理程序;中断是异常的一种,一般特指SoC内的内部外设产生的打断SoC常规业务,或者外部中断(SoC的GPIO引脚传回来的中断)。
(4)S5PV210的异常向量表可以改变(在CP15协处理器中),以适应操作系统的需求。但是目前系统刚启动时,此时DRAM尚未初始化,程序都在SRAM中运行。210在iRAM中设置了异常向量表,供暂时性使用。
(5)像内存一样去访问异常向量表

中断处理要先在汇编中进行,为什么?
(1)中断处理要注意保护现场(中断从SVC模式来,则保存SVC模式下的必要寄存器的值)和恢复现场(中断处理完成后,准备返回SVC模式前,要将保存的SVC模式下的必要寄存器的值恢复回去)
(2)保存现场包括:第一:设置IRQ栈;第二,保存LR;第三,保存R0~R12
(iram只有两个模式下的栈,其他模式下的栈目前还不知道在哪里,开机时,BL0会帮我们设置SVC模式下的栈,将栈指针指向SVC_STACK的地址,但是其他模式下的栈需要我们自己设置)
(3)为什么要保存LR寄存器?要考虑中断返回的问题。中断ISR执行完后如何返回SVC模式下去接着执行原来的代码。中断返回其实取决于我们进入中断时如何保存现场。中断返回时关键的2个寄存器就是PC和CPSR。所以我们在进入IRQ模式时,应该将SVC模式下的下一句指令的地址(中断返回地址)和CPSR保存起来,将来恢复时才可以将中断返回地址给PC,将保存的CPSR给SPSR。
(4)中断返回地址就保存在LR中,而CPSR(自动)保存在(IRQ模式下的)SPSR中

总结
将现场保存至哪里?  irq模式的栈中
怎么设置irq模式栈?   首先要进入irq模式,之后将sp指向IRQ_STACK就行
IRQ_STACK地址?    sram的内存图中有,为0xd0037f80
怎么进入irq模式?    中断发生后自动进入

控制器
和其他外设一样,中断也有其控制器,硬件帮我们做了很多事情。
1.怎么找到具体是哪个中断:S5PV210中因为支持的中断源很多,所以直接设计了4个中断寄存器,每个32位,每位对应一个中断源。(理论上210最多支持128个中断,实际支持93个,有些位是空的);当中断发生时,在irq_handler中依次去查询4个中断源寄存器,看哪一个的哪一位被置1,则这个位对应的寄存器就发生了中断,即找到了中断编号。
2.怎么找到对应的isr的问题:210提供了很多寄存器来解决每个中断源对应isr的寻找问题,当发生相应中断时,硬件会自动的将相应isr推入一定的寄存器中,我们软件只要去这个寄存器中执行函数就行了。

控制器有VIC0、VIC1、VIC2、VIC3,其能支持的中断类型如下:
VIC3中的中断源:multimedia、audio、security、(ADC、TSI、MMC)
VIC2中的中断源:multimedia、audio、security、(LCD、JPEG、LCD)
VIC1中的中断源:ARM、power、memory、conncetivity storage(onenand、hsmmc、otg、i2c、spi、uart)
VIC0中的中断源:system、DMA、TIMER(RTC、WDT、timer、EINT0~31)

寄存器
VICnINTENABLE、VICnINTENCLEAR
使能,禁止寄存器
VICnINTSELECT
中断模式选择寄存器
VICnIRQSTATUS、VICnFIQSTATUS
中断状态寄存器
VICnVECTPRIORITY0~VICnVECTPRIORITY31
优先级设置
VICnVECTADDR0~VICnVECTADDR31、VICnADDR
存放isr函数地址,程序员在设置中断的时候,把这个中断的isr地址直接放入这个中断对应的VECTADDR寄存器即可
VICnADDR这个寄存器是只需要读的,它里面的内容是由硬件自动设置的。当发生了相应中断时,硬件会自动识别中断编号,并且会自动找到这个中断的VECTADDR寄存器,然后将其读出复制到VICnADDR中。
VICnIRQSTATUS、VICnFIQSTATUS
中断状态寄存器,只读。
当发生了中断时,硬件会自动将该寄存器的对应位置为1。

中断控制器初始化
主要工作有:第一阶段绑定异常向量表到异常处理程序;禁止所有中断源;选择所有中断类型为IRQ;清理VICnADDR寄存器为0.
中断的使能与禁止:
思路是先根据中断号判断这个中断属于VIC几,然后在用中断源减去这个VIC的偏移量(不是地址偏移量,而是中断号偏移量),得到这个中断号在本VIC中的偏移量,然后1<

真正的中断处理程序如何获取isr
(1)当发生中断时,硬件会自动把相应中断源的isr地址从VICnVECTADDR寄存器中推入VICnADDR寄存器中,所以我们第二阶段的第二阶段isr_handler中,只需要到相应的VICnADDR中去拿出isr地址,调用执行即可。
总结:第4步绑定isr地址到VICnVECTADDR和第5步中断发生时第二阶段的第二阶段如何获取isr地址,这两步是相关的。这两个的结合技术,就是我们一直在说的210的硬件自动寻找isr的机制。

整个中断的流程梳理:
整个中断的工作分为2部分:
第一部分是我们为中断响应而做的预备工作:
1. 初始化中断控制器
2. 绑定写好的isr到中断控制器
3. 相应中断的所有条件使能
第二部分是当硬件产生中断后如何自动执行isr:
1. 第一步,经过异常向量表跳转入IRQ/FIQ的入口
2. 第二步,做中断现场保护(在start.S中),然后跳入isr_handler
3. 第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR寄存器中取isr来执行即可。
4. 第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。

外部中断
ext属于外部中断,其主要寄存器为:EXT_CON、EXT_PEND、EXT_MASK

EXT_PEND寄存器是中断挂起寄存器。这个寄存器中每一位对应一个外部中断,平时没有中断时值为0。当发生了中断后,硬件会自动将这个寄存器中该中断对应的位置1,我们去处理完这个中断后应该手工将该位置0。这个PEND寄存器的位就相当于是一个标志,如果发生了中断但是我们暂时忙来不及去处理时,这个位一直是1(这就是挂起),直到去处理了这个中断才会手工清除(写代码清除)这个挂起位表示这个中断被我处理了。
EINT编号是和GPIO绑定的,所以EINT需要查GPIO

外部中断代码:(int.h、int.c是通用的,我们只需要设置GPIO中断寄存器,绑定相应中断函数即可)
uart中断代码:(设置uart的寄存器为相应中断模式)


MEMORY


DMA

框架:

由于DMA_peri仅作为非安全设备运行,因此必须在信任区保护控制器(TZPC)模块中将所有外围设备设置为非安全设备。
PL330的总线接口是AXI,因此DMA_mem和DMA_peri分别连接到AXI_B0和AXI_B1。
特性:

Key Features DMA_mem DMA_peri
Supports Data Size Up to double word (64-bit) Up to word (32-bit)
Supports Burst Size Up to 16 burst Word transfer: Up to 8 burst 、Byte or word transfer: Up to 16 burst
Supports Channel 8 channels at the same time 16 channels at the same time

三个DMA控制器:DMA_mem、DMA_peri0、DMA_peri1


TIMRE

(1)定时器计时其实是通过计数来实现的。定时器内部有一个计数器,这个计数器根据一个时钟(这个时钟源来自APB总线,然后经过时钟模块内部的分频器来分频得到)来工作。每隔一个时钟周期,计数器就计数一次,定时器的时间就是计数器计数值×时钟周期。
(2)定时器内部有1个寄存器TCNT,计时开始时我们会把一个总的计数值(譬如说300)放入TCNT寄存器中,然后每隔一个时钟周期(假设为1ms)TCNT中的值会自动减1(硬件自动完成,不需要CPU软件去干预),直到TCNT中减为0的时候,TCNT就会触发定时器中断。
(3)定时时间是由2个东西共同决定的:一个是TCNT中的计数值,一个是时钟周期。譬如上例中,定时周期就为300×1ms = 300ms。

1.watchdog

看门狗其实就是一个定时器,只不过定时时间到了之后不只是中断,还可以复位CPU
(1)PCLK_PSYS经过两级分频后生成WDT(watchdog timer)的时钟周期,然后把要定的时间写到WTDAT寄存器中,刷到WTCNT寄存器中去减1,减到0时(定时时间到)产生复位信号或中断信号。
(2)典型应用中是配置为产生复位信号,我们应该在WTCNT寄存器减到0之前给WTDAT寄存器中重新写值以喂狗。
7.3、主要寄存器:WTCON WTDAT WTCNT WTCLRINT
wdt.c:

#define WTCON (0xE2700000)
#define WTDAT (0xE2700004)
#define WTCNT (0xE2700008)
#define WTCLRINT (0xE270000C)

#define rWTCON (*(volatile unsigned int *)WTCON)
//...


// 初始化WDT使之可以产生中断
void wdt_init_interrupt(void)
{ 
        
	// 第一步,设置好预分频器和分频器,得到时钟周期是128us
	rWTCON &= ~(0xff<<8);
	rWTCON |= (65<<8);				// 1MHz
	
	rWTCON &= ~(3<<3);
	rWTCON |= (3<<3);				// 1/128 MHz, T = 128us
	
	// 第二步,设置中断和复位信号的使能或禁止
	rWTCON |= (1<<2);				// enable wdt interrupt
	rWTCON &= ~(1<<0);				// disable wdt reset
	
	// 第三步,设置定时时间
	// WDT定时计数个数,最终定时时间为这里的值×时钟周期
	//rWTDAT = 10000; // 定时1.28s
	//rWTCNT = 10000; // 定时1.28s
	
	// 其实WTDAT中的值不会自动刷到WTCNT中去,如果不显式设置WTCON中的值,它的值就是
	// 默认值,然后以这个默认值开始计数,所以这个时间比较久。如果我们自己显式的
	// 设置了WTCNT和WTDAT一样的值,则第一次的定时值就和后面的一样了。
	rWTDAT = 1000;					// 定时0.128s
	//rWTCNT = 1000; // 定时0.128s
	
	// 第四步,先把所有寄存器都设置好之后,再去开看门狗
	rWTCON |= (1<<5);				// enable wdt
}

// wdt的中断处理程序
void isr_wdt(void)
{ 
        
	static int i = 0;
	// 看门狗定时器时间到了时候应该做的有意义的事情
	printf("wdt interrupt, i = %d...", i++);	
	// 清中断
	intc_clearvectaddr();
	rWTCLRINT = 1;
}

2.RTC

(2)RTC是SoC中一个内部外设,RTC有自己独立的晶振提供RTC时钟源(32.768KHz),内部有一些寄存器用来记录时间(年月日时分秒星期)。一般情况下为了在系统关机时时间仍然在走,还会给RTC提供一个电池供电。
图中的每个时间框都用一个寄存器控制

闹钟发生器
(1)可以定闹钟时间,到时间会产生RTC alarm interrupt,通知系统闹钟定时到了。
(2)闹钟定时是定的时间点,而timer定时是定的时间段。
实时时钟的主要寄存器
(1)INTP 中断挂起寄存器
(2)RTCCON RTC控制寄存器
(3)RTCALM ALMxxx 闹钟功能有关的寄存器

BCD码:
(1)RTC中所有的时间(年月日时分秒星期,包括闹钟)都是用BCD码编码的。
(2)BCD码本质上是对数字的一种编码。用来解决这种问题:由56得到0x56(或者反过来)。也就是说我们希望十进制的56可以被编码成56(这里的56不是十进制56,而是两个数字5和6).
(3)BCD码的作用在于可以将十进制数拆成组成这个十进制数的各个数字的编码,变成编码后就没有位数的限制了。譬如我有一个很大的数123456789123456789,如果这个数纯粹当数字肯定超出了int的范围,计算机无法直接处理。要想让计算机处理这个数,计算机首先得能表达这个数,表达的方式就是先把这个数转成对应的BCD码(123456789123456789)
(4)BCD码在计算机中可以用十六进制的形式来表示。也就是说十进制的56转成BCD码后是56,在计算机中用0x56来表达(暂时存储与运算)。
(5)需要写2个函数,一个是bcd转十进制,一个是十进制转bcd。当我们要设置时间时(譬如要设置为23分),我们需要将这个23转成0x23然后再赋值给相应的寄存器BCDMIN;当我们从寄存器BCDMIN中读取一个时间时(譬如读取到的是0x59),需要将之当作BCD码转成十进制再去显示(0x59当作BCD码就是59,转成十进制就是59,所以显示就是59分)。

设置时间与读取显示时间
(1)为了安全,默认情况下RTC读写是禁止的,此时读写RTC的时间是不允许的;当我们要更改RTC时间时,应该先打开RTC的读写开关,然后再进行读写操作,操作完了后立即关闭读写开关。
(2)读写RTC寄存器时,一定要注意BCD码和十进制之间的转换。
(3)年的问题。S5PV210中做了个设定,BCDYEAR寄存器存的并不是完整的年数(譬如今年2015年),而是基于2000年的偏移量来存储的,譬如今年2015年实际存的就是15(2015-2000).还有些RTC芯片是以1970年(貌似)为基点来记录的。
rtc.c:

struct rtc_time
{ 
        
	unsigned int year;
	unsigned int month;
	unsigned int date;			// 几号
	unsigned int hour;			
	unsigned int minute;
	unsigned int second;
	unsigned int day;			// 星期几
};

#define RTC_BASE (0xE2800000)
#define rINTP (*((volatile unsigned long *)(RTC_BASE + 0x30)))

#define rRTCCON (*((volatile unsigned long *)(RTC_BASE + 0x40)))

#define rTICCNT (*((volatile unsigned long *)(RTC_BASE + 0x44)))

#define rRTCALM (*((volatile unsigned long *)(RTC_BASE + 0x50)))

#define rALMSEC (*((volatile unsigned long *)(RTC_BASE + 0x54)))
#define rALMMIN (*((volatile unsigned long *)(RTC_BASE + 0x58)))
#define rALMHOUR (*((volatile unsigned long *)(RTC_BASE + 0x5c)))
#define rALMDATE (*((volatile unsigned long *)(RTC_BASE + 0x60)))
#define rALMMON (*((volatile unsigned long *)(RTC_BASE + 0x64)))
#define rALMYEAR (*((volatile unsigned long *)(RTC_BASE + 0x68)))

#define rRTCRST (*((volatile unsigned long *)(RTC_BASE + 0x6c))) //数据手册没有此寄存器

#define rBCDSEC (*((volatile unsigned long *)(RTC_BASE + 0x70)))
#define rBCDMIN (*((volatile unsigned long *)(RTC_BASE + 0x74)))
#define rBCDHOUR (*((volatile unsigned long *)(RTC_BASE + 0x78)))
#define rBCDDATE (*((volatile unsigned long *)(RTC_BASE + 0x7c)))
#define rBCDDAY (*((volatile unsigned long *)(RTC_BASE + 0x80)))
#define rBCDMON (*((volatile unsigned long *)(RTC_BASE + 0x84)))
#define rBCDYEAR (*((volatile unsigned long *)(RTC_BASE + 0x88)))

# 

相关文章