一、问题描述
入门51没多久后就主攻32了,最近又搞起51,移植一个软定时器代码到STC8上,结果出现了奇怪的问题,而这种问题在各种32位单片机上都是不曾有的。
有如下代码,实现了软定时器。使用内部IRC,22.1184MHz
定义了一个32位变量,在定时器0的1ms中断里面自增,作为系统时基
static __IO uint32_t xdata SystemTimer = 0;
void timer0_int (void) interrupt TIMER0_VECTOR
{
SystemTimer++;
}
void UserTimer_Reset(uint32_t xdata *Timer)
{
*Timer = SystemTimer;
}
uint32_t UserTimer_Read(uint32_t xdata *Timer)
{
return (SystemTimer - *Timer);
}
然后在主循环里面定义一个变量与时基进行比较,大于等于1000ms则通过串口发送计数值
int main(void)
{
static __IO uint32_t xdata timer_test = 0;
static __IO uint8_t xdata run_step = 0;
__IO uint32_t xdata val;
while (1)
{
if ((val = UserTimer_Read(&timer_test)) >= TIMEOUT_1S)
{
uart_send_bytes(UART2, (uint8_t *)(&timer_test), 4);
UserTimer_Reset(&timer_test);
uart_send_bytes(UART2, (uint8_t *)(&SystemTimer), 4);
uart_send_bytes(UART2, (uint8_t *)(&val), 4);
}
}
}
得到如下结果,相减的结果和打印的结果不一致
二、问题猜想
1、以上图一为例,当指定到xdata区时,timer_test、SystemTimer和val三者的关系为val = SystemTimer - 0x100 - timer_test,即0x800-0x100-0x7D0 = 0xFFFFFF30。
而差值很大的时候均发生在SystemTimer 的低8位变为0x00的时候。
猜测是中断中自增时,自增前为0x7FF,自增后低8位溢出变为0x00,而在退出中断后并运行到UserTimer_Read()函数进行减法时,次低8位还未获得进位,于是减法算式拿到的值是0x700,于是就发生了SystemTimer(0x700) - *Timer(0x7D0) = 0xFFFFFF30的惨案。
2、以上图二为例,当指定到data区时timer_test、SystemTimer和val三者的关系为SystemTimer = val + timer_test - 0xFF,即0x437+0x7C3C8-0xFF=0x7C700。
同样是均发生在SystemTimer 的低8位变为0x00的时候。
猜测是中断中自增时,自增前0x07C6FF,自增后低8位溢出,应该变为0x00但内存中还是0xFF,而次低8位已经获得进位变为了0xC7。于是退出中断后,减法算式拿到的值是0x07C7FF,于是有SystemTimer(0x07C7FF) - *Timer(0x07C3C8) = 0x0437。
三、试图解决
这或许跟8位单片机一个指令只能处理32位数据其中的8位有关,因对汇编不熟悉,不了解编译器具体实现,只能瞎改一下。
1、修改自增语句,令其执行周期变长,即先加上1再覆盖SystemTimer 。 实测没用,可能被编译器优化掉了
void timer0_int (void) interrupt TIMER0_VECTOR
{
SystemTimer = SystemTimer + 1;
}
或者
void timer0_int (void) interrupt TIMER0_VECTOR
{
__IO uint32_t t = SystemTimer + 1;
SystemTimer = t;
}
2、再定义一个变量,进行如下操作。错误变少了,但还是有。有用但不多
static __IO uint32_t xdata SystemTimer_1 = 0;
void timer0_int (void) interrupt TIMER0_VECTOR
{
SystemTimer = SystemTimer_1;
SystemTimer_1++;
}
3、在2的基础上,将进中断的时间由1ms改为10ms,也不行
static __IO uint32_t xdata SystemTimer_1 = 0;
void timer0_int (void) interrupt TIMER0_VECTOR
{
SystemTimer = SystemTimer_1;
SystemTimer_1 += 10;
}
为什么在中断中自加,不是更新了RAM才出来的?确切原因有时间再深究,或者哪位大佬能给小弟指点一下。
这51单片机给我整出心理阴影了,还是避免我这个用法吧。