uboot中timer定時器的設置,uboot匯編部分沒有對timer初始化,而是在C中。在C入口函數void start_armboot(void)中,首先是通過如下代碼方式調用: [cpp] typedef int (init_fnc_t) (void); [html] init_fnc_t *init_sequence[] = { a/a/l/board.c #if defined(CONFIG_ARCH_CPU_INIT) arch_cpu_init, /* basic arch cpu dependent setup */ #endif board_init, /* basic board dependent setup */ #if defined(CONFIG_USE_IRQ) interrupt_init, /* set up exceptions */ #endif timer_init, /* initialize timer */ #ifdef CONFIG_FSL_ESDHC get_clocks, #endif env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) init_func_i2c, #endif dram_init, /* configure available RAM banks */ #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI) arm_pci_init, #endif display_dram_config, NULL, }; 這裡是u-boot的C代碼部分的入口: [cpp] void start_armboot (void) { init_fnc_t **init_fnc_ptr; 。 。 。 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } 上面的函數調用很復雜,不是討論這個所以暫時不深究,直接看它調用arch/arm/cpu/arm920t/s3c24x0/timer.c文件中的函數int timer_init (void),代碼如下: [cpp] int timer_init(void) { struct s3c24x0_timers *timers = s3c24x0_get_base_timers(); ulong tmr; /* use PWM Timer 4 because it has no output */ /* prescaler for Timer 4 is 16 */ writel(0x0f00, &timers->TCFG0); if (timer_load_val == 0) { /* * for 10 ms clock period @ PCLK with 4 bit divider = 1/2 * (default) and prescaler = 16. Should be 10390 * @33.25MHz and 15625 @ 50 MHz */ timer_load_val = get_PCLK() / (2 * 16 * 100); timer_clk = get_PCLK() / (2 * 16); } /* load value for 10 ms timeout */ lastdec = timer_load_val; writel(timer_load_val, &timers->TCNTB4); /* auto load, manual update of Timer 4 */ tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000; writel(tmr, &timers->TCON); /* auto load, start Timer 4 */ tmr = (tmr & ~0x0700000) | 0x0500000; writel(tmr, &timers->TCON); timestamp = 0; return (0); } 以下是對函數的詳細分析: 1、先看數據結構s3c24x0_timers [cpp] struct s3c24x0_timers { u32 TCFG0; u32 TCFG1; u32 TCON; struct s3c24x0_timer ch[4]; u32 TCNTB4; u32 TCNTO4; }; 有引出來了另一個結構s3c24x0_timer [cpp] struct s3c24x0_timer { u32 TCNTB; u32 TCMPB; u32 TCNTO; }; 看下這兩個結構是不是涵蓋了所有timer的寄存器,對照s3c2440的datasheet得出結論是:timer寄存器地址空間從(0x51000000~0x51000040)依次對應的寄存器名是卻是是上面的結構體中的寄存器,總共是17個寄存器. 為什麼會有4個結構s3c24x0_timer呢?因為2440有5個timer,其中前四個timer0~timer3工作方式類似,都有外部引腳引出,而第五個timer4不同與他們,第一個就是它沒有TCMPB寄存器,具體的區別可以看看這個文檔或是datasheet: 2、s3c24x0_get_base_timers()函數返回的就是timer寄存器空間的起始地址0x51000000 [cpp] static inline struct s3c24x0_timers *s3c24x0_get_base_timers(void) { return (struct s3c24x0_timers *)S3C24X0_TIMER_BASE; } 3、 [cpp] /* use PWM Timer 4 because it has no output */ /* prescaler for Timer 4 is 16 */ 這個注釋是不是有問題呢,我怎麼查不到timer4預分頻器是16位的,都是8位。是不是要表達計數器是16位的呢? 4、writel(0x0ff,&timers->TCFG0)的作用是什麼呢?在arch/arm/include/asm/io.h文件中既然有3處定義了這個函數,考察了一下只有下面被編譯: [cpp] #define writel(v,a) __arch_putl(v,a) [cpp] #define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v)) 分析:就是往a寄存器寫入v. 這裡就是往寄存器TCFG0寫入0x0ff,先看下TCFG0寄存器信息: 將timer0、1的預分頻器的值寫ff就是255, 其值N為: 0~255 輸出頻率為:PCLK ÷(N+1) 這時候N = 255,結合這篇文檔http://blog.csdn.net/sonbai/article/details/8687858知道PCLK的頻率是101.25MHz,輸出頻率就出來了,這時候最低,同時沒有用到他們,所以不考慮了。t只考慮imer4,這時候N = 0,輸出頻率最大就是PCLK。為什麼要設最大,先放著 5、timer_load_val是一個全局變量,顧名思義它是裝入TCNTB4寄存器的值。看下面的注釋: [cpp] /* * for 10 ms clock period @ PCLK with 4 bit divider = 1/2 * (default) and prescaler = 16. Should be 10390 * @33.25MHz and 15625 @ 50 MHz */ 它的意思是如果要將timer4的時鐘周期設置為10ms的話,要通過PCLK、分配器divider默認的值1/2和prescaler = 16來設置如何設置(?)來看一下下面這個函數 [cpp] /* return PCLK frequency */ ulong get_PCLK(void) { struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); return (readl(&clk_power->CLKDIVN) & 1) ? get_HCLK() / 2 : get_HCLK(); } 看看結構s3c24x0_clock_power [cpp] /* CLOCK & POWER MANAGEMENT (see S3C2400 manual chapter 6) */ /* (see S3C2410 manual chapter 7) */ struct s3c24x0_clock_power { u32 LOCKTIME; u32 MPLLCON; u32 UPLLCON; u32 CLKCON; u32 CLKSLOW; u32 CLKDIVN; #if defined(CONFIG_S3C2440) U32 CAMDIVN; #endif }; 涵蓋了所有時鐘設置的寄存器,s3c24x0_get_base_clock_power();返回的是(struct s3c24x0_clock_power *)0x4C000000也就是時鐘設置寄存器的起始地址 [cpp] return (readl(&clk_power->CLKDIVN) & 1) ? get_HCLK() / 2 : get_HCLK(); 判斷一下CLKDIVN是不是被設置成了1,如果是說明PCLK = 1/2HCLK,不在去看get_HCLK了。前面我已經說過PCLK = 101.25MHz,至於timer_load_val = 101.25MHz / (2*16*100);這個公式,分析如下: 我們要設置timer4的時鐘周期是T = 10ms; 又 T = [1 /(PCLK / (預分頻器的值(prescaler)*分頻器的值(div)))] *timer_load_val 已知T、PCLK、perscaler、div,就可以求的 timer_load_val = PCLK / (perscaler * div) 但是這裡為什麼會有100呢?(暫時不清楚,望大家給我一個解釋) timer_clk下面暫時沒有遇到,先不考慮。 6、 [cpp] /* load value for 10 ms timeout */ lastdec = timer_load_val; writel(timer_load_val, &timers->TCNTB4); 然後將timer_load_val的值寫入TCNTB4寄存器中 7、下面是要配置TCON寄存器,也就是timer控制寄存器 [cpp] /* auto load, manual update of Timer 4 */ tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000; writel(tmr, &timers->TCON); 先來看看TCON寄存器信息: TCON現實與(~0x0700000)與運算,即TCON = TCON & F8FFFFF,也就是將TCON的20、21、21這幾個位清零,然後TCON = TCON | 0X0600000將位21、20設置為1,對照datasheet知道將timer4設置為自動裝載,和手動更新.這就有一個矛盾既然自動轉載干嘛還要手動更新呢?看下8就知道了。writel()函數已經介紹過了。 8、你看下面這個代碼就很有趣,他和上面的那個代碼很類似 [cpp] /* auto load, start Timer 4 */ tmr = (tmr & ~0x0700000) | 0x0500000; writel(tmr, &timers->TCON); 起始2440定時器第一次往TCONTB裝入初始值是要手動更新的,如果沒有更新初值自動更新那更新什麼呢,他不知道,TCONTB默認是0,更新0有什麼用呢。所以需要手動更新,然後關掉手動更新,設置好後,再啟動timer4就完成了timer4的設置和運行。 9、timestamp = 0,這句我們暫時還沒涉及到,他是後面要用的。 10、然後返回 [cpp] return (0);