前言linux
在Cubieboard2裸機開發之(三)裏用到了一個延時函數delay,它的延時時間是不精確的,所以爲了可以精確延時,就須要定時器的配合。定時器能夠精確延時的一個重要緣由是它的計時時鐘(或者說頻率)是精確的,計時時鐘越小,能實現的延時時間就越小。ide
A20的定時器模塊比較強大,它不只有6個普通的定時器,還有4個高速定時器,計時頻率可達上百MHz,更重要的是它們操做起來很是簡單、易懂。函數
1、目的工具
學習使用A20的普通定時器,實現精確延時。oop
2、源代碼說明學習
start.S文件。首先禁止CPU的IRQ和FIQ,設置爲管理模式,而後設置堆棧指針,最後調用C語言的main函數。spa
1 /* 2 * (C) Copyright 2014 conan liang <lknlfy@163.com> 3 * 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。code
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 }
3、驗證blog
使用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秒),效果很差用圖片展現,所以就不上圖了。