前言
在Cubieboard2裸机开发之(三)里用到了一个延时函数delay,它的延时时间是不精确的,因此为了能够精确延时,就需要定时器的配合。定时器可以精确延时的一个重要原因是它的计时时钟(或者说频率)是精确的,计时时钟越小,能实现的延时时间就越小。
A20的定时器模块比较强大,它不仅有6个普通的定时器,还有4个高速定时器,计时频率可达上百MHz,更重要的是它们操作起来非常简单、易懂。
一、目的
学习使用A20的普通定时器,实现精确延时。
二、源代码说明
start.S文件。首先禁止CPU的IRQ和FIQ,设置为管理模式,然后设置堆栈指针,最后调用C语言的main函数。
1 /* 2 * (C) Copyright 2014 conan liang3 * 4 */ 5 6 7 /* global entry point */ 8 .globl _start 9 _start: b reset 10 11 reset: 12 /* disable IRQ & FIQ, set the cpu to SVC32 mode */ 13 mrs r0, cpsr 14 and r1, r0, #0x1f 15 teq r1, #0x1a 16 bicne r0, r0, #0x1f 17 orrne r0, r0, #0x13 18 orr r0, r0, #0xc0 19 msr cpsr, r0 20 /* setup stack, so we can call C code */ 21 ldr sp, =(1024 * 10) 22 /* jump to main function */ 23 bl main 24 loop: 25 b loop
main.c文件。首先初始化LED所在IO管脚,设置为输出功能,并且输出低电平,即一开始两个LED是熄灭的,接着初始化定时器0,包括设置它的时钟,工作模式等。
1 #include "timer.h" 2 #include "io.h" 3 4 /* reg define for IO of LEDs */ 5 #define SUNXI_PIO_BASE (0x01C20800) 6 #define PH_CFG2 (SUNXI_PIO_BASE + 0x104) 7 #define PH_DAT (SUNXI_PIO_BASE + 0x10C) 8 9 /* set two LEDs on */ 10 static void set_led_on(void) 11 { 12 unsigned int tmp; 13 14 /* PH20 and PH21 output 1 */ 15 tmp = readl(PH_DAT); 16 tmp |= (0x1 << 20); 17 tmp |= (0x1 << 21); 18 writel(tmp, PH_DAT); 19 } 20 21 /* set two LEDs off */ 22 static void set_led_off(void) 23 { 24 unsigned int tmp; 25 26 /* PH20 and PH21 output 0 */ 27 tmp = readl(PH_DAT); 28 tmp &= ~(0x1 << 20); 29 tmp &= ~(0x1 << 21); 30 writel(tmp, PH_DAT); 31 } 32 33 static void led_init(void) 34 { 35 unsigned int tmp; 36 37 /* configure PH20 and PH21 output */ 38 tmp = readl(PH_CFG2); 39 tmp &= ~(0x7 << 16); 40 tmp &= ~(0x7 << 20); 41 tmp |= (0x1 << 16); 42 tmp |= (0x1 << 20); 43 writel(tmp, PH_CFG2); 44 /* set PH20 and PH21 output 0 */ 45 tmp = readl(PH_DAT); 46 tmp &= ~(0x1 << 20); 47 tmp &= ~(0x1 << 21); 48 writel(tmp, PH_DAT); 49 } 50 51 /* C code entry point */ 52 int main(void) 53 { 54 /* init led */ 55 led_init(); 56 /* init timer0 */ 57 sunxi_timer0_init(); 58 59 while (1) { 60 set_led_on(); 61 udelay(1000000); 62 set_led_off(); 63 udelay(1000000); 64 } 65 66 return 0; 67 }
timer.c文件。一个初始化函数,一个微妙延时函数,这里设置定时器的计时频率为6MHz。
1 #include "timer.h" 2 #include "io.h" 3 4 5 /* 6MHz for timer0 count freq*/ 6 #define TIMER0_HZ (6000000) 7 8 #if 0 9 static void sunxi_timer0_start(void) 10 { 11 unsigned int tmp; 12 13 tmp = readl(TMR0_CTRL_REG); 14 tmp |= (1 << TMR0_EN); 15 writel(tmp, TMR0_CTRL_REG); 16 } 17 18 static void sunxi_timer0_stop(void) 19 { 20 unsigned int tmp; 21 22 tmp = readl(TMR0_CTRL_REG); 23 tmp &= ~(1 << TMR0_EN); 24 writel(tmp, TMR0_CTRL_REG); 25 } 26 #endif 27 28 /* accurate delay */ 29 void udelay(unsigned int usec) 30 { 31 unsigned int count; 32 unsigned int tmp; 33 34 /* write interval value */ 35 count = (TIMER0_HZ / 1000000 ) * ((unsigned int)usec); 36 writel(count, TMR0_INTV_VALUE_REG); 37 38 /* reload and start timer0 must be operated at the same time */ 39 tmp = readl(TMR0_CTRL_REG); 40 tmp |= (1 << TMR0_RELOAD); 41 tmp |= (1 << TMR0_EN); 42 writel(tmp, TMR0_CTRL_REG); 43 /* wait for interrupt */ 44 while (!(readl(TMR_IRQ_STA_REG) & (1 << TMR0_IRQ_PEND))); 45 /* clear timer0 interrupt */ 46 tmp = readl(TMR_IRQ_STA_REG); 47 tmp |= (1 << TMR0_IRQ_PEND); 48 writel(tmp, TMR_IRQ_STA_REG); 49 } 50 51 void sunxi_timer0_init(void) 52 { 53 unsigned int tmp; 54 55 /* single mode, /4 divide, clock source is OSC24M, reload . so clk_freq = 24M / 4 = 6M*/ 56 tmp = (0x1 << TMR0_MODE) | (0x2 << TMR0_CLK_PRES) | (0x1 << TMR0_CLK_SRC) | (0x1 << TMR0_RELOAD); 57 writel(tmp, TMR0_CTRL_REG); 58 /* clear timer0 interrupt */ 59 tmp = readl(TMR_IRQ_STA_REG); 60 tmp |= (1 << TMR0_IRQ_PEND); 61 writel(tmp, TMR_IRQ_STA_REG); 62 /* enable timer0 interrupt */ 63 tmp = readl(TMR_IRQ_EN_REG); 64 tmp |= (1 << TMR0_IRQ_EN); 65 writel(tmp, TMR_IRQ_EN_REG); 66 }
三、验证
使用arm-linux-gnueabihf工具编译后生成timer.b文件,再使用mksunxiboot工具在timer.b文件前面加上一个头部,最终生成timer.bin文件,使用以下命令将timer.bin文件烧写到TF中:
#sudo dd if=./timer.bin of=/dev/sdb bs=1024 seek=8
将TF卡插入Cubieboard2,上电即可看到两个LED同时闪烁,并且闪烁周期为2秒(亮1秒,灭1秒),效果不好用图片展示,因此就不上图了。