本文描述基于mini6410平台的time.c中的相关代码,主要描述下对部分代码的理解。
先贴上代码:
1 /* we use the shifted arithmetic to work out the ratio of timer ticks
2 * to usecs, as often the peripheral clock is not a nice even multiple
3 * of 1MHz.
4 *
5 * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok
6 * for the current HZ value of 200 without producing overflows.
7 *
8 * Original patch by Dimitry Andric, updated by Ben Dooks
9 */
10
11 /* timer_mask_usec_ticks
12 *
13 * given a clock and divisor, make the value to pass into timer_ticks_to_usec
14 * to scale the ticks into usecs
15 */
16 static inline unsigned long
17 timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk)
18 {
19 unsigned long den = pclk / 1000;
20
21 return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den;
22 }
该函数主要的功能为换算出在某个时钟频率下,每个时钟周期所对应的微秒数,且该微秒数经过放大处理。
首先第19行代码将时钟频率由MHZ转换为KHZ,当然前提是pclk以MHZ为单位计算。
分析下一行代码前,先看下频率转换为微秒的计算公式:t = (10^6)/(pclk/scaler),上述公式表示在分频后的时钟频率下一个时钟周期对应的微秒数。显然上述公式可能出现小数部分,在计算机中我们用整型变量来保存数据时,为了不减小精度,一般会将数据放大处理,因此将上述公式放大一定的倍数,在得到最后结果前,再缩小相应的倍数得到最后结果。在这里将上述公式放大了2^16倍,得到公式:
t = (10^6)*(2^16)/(pclk/scaler),
对上述公式变量替换并推导:t = ((10^6)/1000)*(2^16)/((pclk/1000)/scaler)
= ((10^3)<<16)/(den/scaler)
= ((1000<<16)*scaler)/den。
为了减小误差,对上述公式进行四舍五入则得到公式:
((1000<<16)*scaler)/den + (1/2) = ((1000<<16)*scaler)/den + (den/2den)
= ((1000<<16)*scaler+den/2)/den
= ((1000<<16)*scaler+(den>>1))/den。
TIMER_USEC_SHIFT宏定义为16,由此第21行代码便不言而喻了。
关于第5、6行注释的理解:12MHZ转换为微秒为(10^6)/(12*10^6)=1/12=0.08333333333 将其放大保存在整型变量里,这里是说放大系数为2^15或2^14显得太低,精度不够。因此设置放大系数为2^16,并且2^16不会导致200HZ的频率上溢。200HZ频率表示5毫秒产生一个中断,假设时钟频率就是12M,则5毫秒需要12*(10^6)*(5/1000)=60000个时钟周期。((10^6)/(12*10^6))*(2^16)=5462(约等于), 5462*60000=0x13889C40,没有造成溢出。个人理解,如有错误之处,还请多多提点。
接下来再看另一段代码:
1 /* timer_ticks_to_usec
2 *
3 * convert timer ticks to usec.
4 */
5
6 static inline unsigned long timer_ticks_to_usec(unsigned long ticks)
7 {
8 unsigned long res;
9
10 res = ticks * timer_usec_ticks;
11
12 res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */
13
14 return res >> TIMER_USEC_SHIFT;
15 }
该函数的主要功能是将时钟周期数转换为微秒数,第10行代码中全局变量timer_usec_ticks为由timer_mask_usec_ticks函数返回得到的值。该值乘以具体时钟周期数便得到该时钟周期数对应的微秒数。后面第12行又加上了一个值,该值目的是进行向上取整处理,以提高精度。第14行代码右移处理便是将上面进行放大倍数处理后的值还原为实际的结果值。
1<<(TIMER_USEC_SHIFT - 4) = 2^12, (2^12/2^16) 小于 1,因此起到向上取整的作用。