我在ARM平台上用QEMU写了一个裸机定时器程序。这个平台是 versatilepb
. 计时器是 SP804 ARM 双定时器模块.
该 SP804
提供2个定时器模块。每个模块提供2个定时器。所以一共有4个定时器,分别是 Timer 0, 1, 2, 3
.
定时器01共享IRQ 4.
定时器23共享IRQ 5.
我注意到,如果我启动一个 单一计时器或2个定时器 不同 定时器模块,比如02或13。中断频率很正常。
但是,如果我将2个定时器从 一样 定时器模块,比如01或23。中断频率要高很多。
而当我从同一个定时器模块启动这2个定时器时,在这2个定时器模块中的 第一 定时器的IRQ频率比我开始使用的那个定时器的IRQ频率高得多。第二 一个,而后一个似乎有一个正常的IRQ频率。
下面是我的一些IRQ处理代码。
void IRQ_handler()
{
u32 vicstatus = VIC_STATUS;
// VIC status BITs: timer0,1=4, timer2,3=5
if (vicstatus & (1<<4))
{// bit4=1:timer0,1, handle timer 0 and 1 one by one
if (*(timer[0].base + TVALUE) == 0) // timer 0
timer_handler(0);
if (*(timer[1].base + TVALUE) == 0) // timer 1
timer_handler(1);
}
if (vicstatus & (1<<5))
{// bit5=1:timer2,3, handle timer 2 and 3 one by one
if (*(timer[2].base + TVALUE) == 0) // timer 2
timer_handler(2);
if (*(timer[3].base + TVALUE) == 0) // timer 3
timer_handler(3);
}
}
void timer_handler(u32 n)
{
TIMER *t = &timer[n];
t->tick++; // Assume 20 ticks per second. Need to calculate it for more accuracy.
if (t->tick == 20)
{
t->tick = 0;
t->ss++;
if (t->ss == 60)
{
t->ss = 0;
t->mm++;
if (t->mm == 60)
{
t->mm = 0;
t->hh++; // no 24 hour roll around
}
}
t->clock[7] = '0' + (t->ss % 10);
t->clock[6] = '0' + (t->ss / 10);
t->clock[4] = '0' + (t->mm % 10);
t->clock[3] = '0' + (t->mm / 10);
t->clock[1] = '0' + (t->hh % 10);
t->clock[0] = '0' + (t->hh / 10);
kprintf("Timer [%d]: %s\n", n, (u8 *)&t->clock[0]);
}
timer_clearInterrupt(n); // clear timer interrupt
}
截图:
单个定时器: (定时器有正常的频率)
2个不同模块的定时器13。(两个定时器都有正常频率)
同一模块的2个定时器23。(2号定时器先启动,频率比3号高得多,而且两个23号的整体频率也高得多) 而且两个23的整体频率都高得多)
谢谢 jcmvbkb的评论。我改用 Masked Interrupt Status
寄存器(偏移量= TMIS
)来检测是哪个定时器在发出中断。
根据规范,该值是由控制寄存器中的原始中断状态与定时器中断使能位的逻辑AND。
该值是原始中断状态与控制寄存器中的定时器中断使能位的逻辑AND值 也是传递给中断输出引脚TIMINTX的相同值。
现在看来,同一定时器模块的2个定时器的中断频率是正常的。
我还在思考,为什么以前的方法与? Current Value Register
不能工作。不过看起来很自然。
void IRQ_handler()
{
u32 vicstatus = VIC_STATUS;
//UART 0
if (vicstatus & UART0_IRQ_VIC_BIT)
{
uart_handler(&uart[0]);
}
//UART 1
if (vicstatus & UART1_IRQ_VIC_BIT)
{
uart_handler(&uart[1]);
}
// VIC status BITs: timer0,1=4, uart0=13, uart1=14
if (vicstatus & TIMER01_IRQ_VIC_BIT)
{// bit4=1:timer0,1, handle timer 0 and 1 one by one
if (*(timer[0].base + TMIS) == 1) // timer 0 <===== HERE changed to use TMIS
timer_handler(0);
if (*(timer[1].base + TMIS) == 1) // timer 1 <===== HERE changed to use TMIS
timer_handler(1);
}
if (vicstatus & TIMER23_IRQ_VIC_BIT)
{// bit5=1:timer2,3, handle timer 2 and 3 one by one
if (*(timer[2].base + TMIS) == 1) // timer 2 <===== HERE changed to use TMIS
timer_handler(2);
if (*(timer[3].base + TMIS) == 1) // timer 3 <===== HERE changed to use TMIS
timer_handler(3);
}
}
下面是从同一个定时器模块启动定时器23的截图。
IRQ_handler使用 Current Value Register
来检测哪个定时器引起了中断,这看起来不对。有 Masked Interrupt Status Register
显然是为了这个目的,我建议用它来代替。此外,检查IRQ源,记录检查结果,并通过写到 Interrupt Clear Register
必须防止重入,否则单个定时器IRQ仍可能被多次计算。
我猜测,原来观察到的问题是因为定时器IRQ被其他东西触发,或者当定时器计数器为零时,IRQ_handler被其他IRQ调用。