Exynos4412裸机开发系列教程--TICK机制

来源:互联网 发布:05~06赛季科比数据 编辑:程序博客网 时间:2024/06/05 15:33

Tick信号对于任何一款操作系统而言,就类似于人的心脏脉搏,关键性不言而寓,其本质上就是操作系统的激励源,各种调度算法,时间片等概念,包括具体的任务,可以理解为一个巨大的状态机,在激励源的激励下,按部就班执行,一切都是可预测的,只不过复杂度比较高而已。

同样,对于裸机而言,虽没有多任务执行的能力,但是实现了tick机制,可以编写出比较复杂的裸机软件,而且结构清新,可读性强,扩展简单。当然,如果我们真的需要在裸机下实现多种任务的执行,比如在后台下载http网页的时候,同时显示下载进度,并可以接收用户的任何点击操作,比如停止按钮等。我们可以通过协程就可以实现多任务了,协程的本质就是由程序员来控制各种任务的轮换,而不是由操作系统本身按照一定的算法进行调度。当然也可以用传统的MCU编程方法,前后台系统来实现。方法很多,但都异曲同工,条条大路通罗马。

Tick其实很简单,就是一周期性执行的一段代码,不管cpu当前在执行什么任务,时间到了,这段代码必须运行。在这个周期性的代码里,我们可以将一个全局变量加一,用于记录流逝的时间,在具体的应用软件里,我们可以依据此变量实现精确延时。

了解到这里,我想大家已经明白该怎么做了,就是申请一个周期性触发中断的定时器,然后在中断处理函数里,添加相应的代码而已。

Exynos4412裸机开发系列教程中使用了一个100HZ的定时器,也就最大精度为10ms,小于10ms的延时都是不太准确的,当然我们可以提高到1000HZ,这样精度就可以到1ms了。

好了,来看具体的实现。首先,我们需要初始化定时器,设置相应的寄存器,并注册中断处理函数:

void exynos4412_tick_initial(void){u64_t pclk;if(!clk_get_rate("pclk", &pclk))return;if(!request_irq("TIMER4", timer_interrupt, 0))return;/* Using pwm timer 4, prescaler for timer 4 is 16 */writel(EXYNOS4412_TCFG0, (readl(EXYNOS4412_TCFG0) & ~(0xff<<8)) | (0x0f<<8));/* Select mux input for pwm timer4 is 1/2 */writel(EXYNOS4412_TCFG1, (readl(EXYNOS4412_TCFG1) & ~(0xf<<16)) | (0x01<<16));/* Load value for 10 ms timeout */writel(EXYNOS4412_TCNTB4, (u32_t)(pclk / (2 * 16 * 100)));/* Auto load, manaual update of timer 4 and stop timer4 */writel(EXYNOS4412_TCON, (readl(EXYNOS4412_TCON) & ~(0x7<<20)) | (0x06<<20));/* Enable timer4 interrupt and clear interrupt status bit */writel(EXYNOS4412_TINT_CSTAT, (readl(EXYNOS4412_TINT_CSTAT) & ~(0x1<<4)) | (0x01<<4) | (0x01<<9));/* Start timer4 */writel(EXYNOS4412_TCON, (readl(EXYNOS4412_TCON) & ~(0x7<<20)) | (0x05<<20));/* initial system tick */tick_hz = 100;jiffies = 0;}

中断处理函数里,只让一个全局变量jiffies简单的加一,并清中断标志:

static void timer_interrupt(void * data){/* tick count */jiffies++;/* Clear interrupt status bit */writel(EXYNOS4412_TINT_CSTAT, (readl(EXYNOS4412_TINT_CSTAT) & ~(0x1f<<5)) | (0x01<<9));}
当然这里还有些辅助函数及变量,这里一遍贴出

volatile u32_t jiffies = 0;static u32_t tick_hz = 0;u32_t get_system_hz(void){return tick_hz;}u64_t clock_gettime(void){if(get_system_hz() > 0)return (u64_t)jiffies * 1000000 / get_system_hz();return 0;}
有了以上的tick机制,我们就可以实现一些比较使用的功能了,比如延时函数,具体实现如下。

static volatile u32_t loops_per_jiffy = 0;void __attribute__ ((noinline)) __delay(volatile u32_t loop){for(; loop > 0; loop--);}void udelay(u32_t us){u32_t hz = get_system_hz();if(hz)__delay(us * loops_per_jiffy / (1000000 / hz));else__delay(us);}void mdelay(u32_t ms){u32_t hz = get_system_hz();if(hz)__delay(ms * loops_per_jiffy / (1000 / hz));else__delay(ms * 1000);}static void calibrate_delay(void){u32_t ticks, loopbit;s32_t lps_precision = 8;u32_t hz = get_system_hz();if(hz > 0){loops_per_jiffy = (1<<12);while((loops_per_jiffy <<= 1) != 0){/* wait for "start of" clock tick */ticks = jiffies;while (ticks == jiffies);/* go ... */ticks = jiffies;__delay(loops_per_jiffy);ticks = jiffies - ticks;if(ticks)break;}loops_per_jiffy >>= 1;loopbit = loops_per_jiffy;while(lps_precision-- && (loopbit >>= 1)){loops_per_jiffy |= loopbit;ticks = jiffies;while(ticks == jiffies);ticks = jiffies;__delay(loops_per_jiffy);/* longer than 1 tick */if(jiffies != ticks)loops_per_jiffy &= ~loopbit;}}else{loops_per_jiffy = 0;}}void exynos4412_tick_delay_initial(void){calibrate_delay();}
这里有个关键的函数calibrate_delay,这个是用于计算loops_per_jiffy,计算方法为逐次逼近,这个是linux内核里计算bogomips的方法,即每秒种运行多少条指令,是一个相对量,同平台,同环境比较才有意义。

计算出此变量后,我们就可以实现精确延时了,比如mdelay及udelay函数,大家可以自行做实验,看是否准确。

恩,需要源码的朋友,可以直接在本博客寻找链接,或者直接加QQ8192542


0 0
原创粉丝点击