杂项函数在 BSP 中也是非常重要的。我为了图省事,全部都放置在了:c/src/lib/libcpu/arm/at91sam9260/pmc/pmc.c中。
主要是获取CPU当前的频率和系统主时钟的频率。
前几篇连载中,我们在串口和时钟驱动里都是用了获取主时钟频率的函数。
另外一个就是 udelay 函数。udelay函数原来是posix的标准函数,但是rtems并没有实现,
我们不可能用时钟节拍去做微秒级别的时间延时。
许多驱动程序必须要延迟一定的时间才能工作,这里我给出了一个简单的实现方式。
启动定时器1,采用计数方式,选择一个合适的时钟平率,我选择的是主时钟的 32 分之一,一个脉冲大约是300ns。然后
给定时器一个初值,测量一段空循环的时间。得出循环多少次正好是1微秒。
这是通过 calc_delay 来实现的。实际实现中,为了防止除法的精度不够,还做了一下微调。
我是在c/src/lib/libbsp/arm/at9260/startup/bspstart.c中的bsp_start_default函数中调用这个函数,bsp_start_default会在bootcard函数一开始就调用。不用担心定时器1会和别的驱动冲突。只调用calc_delay一次,不要再调用,就绝对不会冲突。
闲话少说,看pmc.c的全部代码:
mckr_css = (AT91C_BASE_PMC->PMC_MCKR) & AT91C_PMC_CSS;
if (mckr_css == AT91C_PMC_CSS_SLOW_CLK) {
return (BSP_SLCK_FREQ);
}
if (mckr_css == AT91C_PMC_CSS_MAIN_CLK) {
return (BSP_MAIN_FREQ);
}
if (mckr_css == AT91C_PMC_CSS_PLLA_CLK) {
pll_mult = ((AT91C_BASE_PMC->PMC_PLLAR & AT91C_CKGR_MULA) >> 16);
pll_div = (AT91C_BASE_PMC->PMC_PLLAR & AT91C_CKGR_DIVA) >> 0;
} else {
pll_mult = ((AT91C_BASE_PMC->PMC_PLLBR & AT91C_CKGR_MULB) >> 16);
pll_div = (AT91C_BASE_PMC->PMC_PLLBR & AT91C_CKGR_DIVB) >> 0;
}
if (pll_div == 0) {
return (BSP_MAIN_FREQ); /* If the PLL mult is 0, then the PLL is disabled */
}
/* Read the Master Clock divider */
pclk_div = (AT91C_BASE_PMC->PMC_MCKR >> 2) & 0x07;
pclk_div = 1 << pclk_div; /* Convert 0-7 into 1, 2, 4, 8, 16, 32, or 64 */
if (pclk_div >= 128) { /* Divider pattern for 128 is reserved */
return (BSP_MAIN_FREQ);
}
cpu_freq = ((BSP_MAIN_FREQ / pll_div) / pclk_div) * (pll_mult + 1);
return (cpu_freq);
}
uint32_t at91sam9260_get_mck(void)
{
uint32_t mclk_div;
uint32_t cpu_freq;
cpu_freq = at91sam9260_get_cpuclk();
mclk_div = (AT91C_BASE_PMC->PMC_MCKR >> 8) & 0x03; /* Read the Master Clock divider */
mclk_div = 1 << mclk_div; /* Convert 0-3 into 1, 2, 4 */
cpu_freq = cpu_freq / mclk_div;
return (cpu_freq);
}
static unsigned int _gLoopsPerMicrosec = 0;
#define TIMER_INT_VALUE 0xFFFFFFFFL
#define INIT_DELAY_TIMES (1 << 10)
static inline void __const_delay(unsigned int value)
{
for ( ; value > 0; value--)
{
__asm__ __volatile__ ("nop;/n");
}
}
#define START_TIMER() /
do {/
AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | AT91C_TC_WAVE;/
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS | AT91C_TC_SWTRG;/
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;/
} while (0)
#define STOP_TIMER() /
(AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS)
#define READ_TIMER(clock) /
((uint64_t) (32 * 1000000) * (AT91C_BASE_TC2->TC_CV) / clock)
void calc_delay(void)
{
uint32_t time;
uint32_t mck;
mck = at91sam9260_get_mck();
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC2;
START_TIMER();
__const_delay(INIT_DELAY_TIMES);
STOP_TIMER();
time = READ_TIMER(mck);
_gLoopsPerMicrosec = INIT_DELAY_TIMES / time;
while (1)
{
START_TIMER();
udelay(1000);
STOP_TIMER();
time = READ_TIMER(mck);
if (time < 1000)
{
_gLoopsPerMicrosec++;
}
else
break;
}
AT91C_BASE_PMC->PMC_PCDR = 1 << AT91C_ID_TC2;
}
void udelay(unsigned int usecs)
{
unsigned int loops = usecs * _gLoopsPerMicrosec;
__const_delay(loops);
}
这里,杂项函数我们就搞定了。