在利用51定时器定时中断方式使用IO口产生一个PWM波时,遇到了一些bug:
问题
这段代码main里没有while1,导致定时器中断产生的PMW并没有输出。见下图
void main()
{
uchar x = 66;
Init_LCD1602(); //LCD1602初始化
Timer0Init();
L293D_IN2 = 0;
L293D_EN = 1;
// while(1)
// {
LCD1602_Dis_OneChar(0,0,pwm_t/100+'0');
LCD1602_Dis_OneChar(1,0,pwm_t%100/10+'0');
LCD1602_Dis_OneChar(2,0,pwm_t%10+'0');
// }
}
//中断函数
void TIM0_INTR() interrupt 1
{
pwm_t++;
if(pwm_t == 250)
{
pwm_t = 0;
L293D_IN1 = 0;
}
if(pwm_t == pwm_value)
L293D_IN1 = 1;
// if(pwm_t == 250)
// pwm_t = 0;
//
// if(pwm_t <= pwm_value)
// L293D_IN1 = 1;
// else if(pwm_t <= 250)
// L293D_IN1 = 0;
LED = !LED;
}
修改
加入while(1)《内容不重要》
void main()
{
uchar x = 66;
Init_LCD1602(); //LCD1602初始化
Timer0Init();
L293D_IN2 = 0;
L293D_EN = 1;
while(1)
{
LCD1602_Dis_OneChar(0,0,pwm_t/100+'0');
LCD1602_Dis_OneChar(1,0,pwm_t%100/10+'0');
LCD1602_Dis_OneChar(2,0,pwm_t%10+'0');
}
}
//中断函数
void TIM0_INTR() interrupt 1
{
pwm_t++;
if(pwm_t == 250)
{
pwm_t = 0;
L293D_IN1 = 0;
}
if(pwm_t == pwm_value)
L293D_IN1 = 1;
// if(pwm_t == 250)
// pwm_t = 0;
//
// if(pwm_t <= pwm_value)
// L293D_IN1 = 1;
// else if(pwm_t <= 250)
// L293D_IN1 = 0;
LED = !LED;
}
可以看到正常输出。
原因分析
如果不加while循环,程序跑完之后会被软复位,重新进入到main函数,即重复启动->运行->结束-> 软复位
因为在嵌入式中,main函数是不能返回的,而虽然我们所编写的单片机程序是用的是C语言,可最终下载到单片机里运行的程序包含两个部分:一是我们编写的程序代码,另一部分是编译器自动生成的代码,例如在keil里,观察我们所写的C语言程序在转换成汇编语言后,单片机的代码区,没有写程序的部分,例如全1或全0区域,程序运行到该区域后就会造成意料不到的结果,在没有while(1)的去情况下运行到最后一行的时候,会自动跳转到main函数的第一行,就造成了main()函数的返回。不同的C语言实现的单片机初始化代码会有不同的表现,例如:在cal_main后jmp,或者是在call_main后jmp 0,这些将会导致不可预料的结果。
验证分析–不严谨
使用LED的闪烁(因为之前的程序调试正好用到了)
在我最开始的验证过程中,可以从之前的程序中看到我加了一个LED,最开始两种情况都有LED闪烁,都符合程序预设的结果,不过频率不一样,但是没太在意;
但实际上就是闪烁频率不一样,才显示出了问题所在:
如果加了while(1),那么main会卡在while(1)处,此时定时器中断正常,如果设置LED没1s闪一次,那确实就会如此。
但是如果没加while(1),第一次执行LED翻转一次,然后马上返回main 的第一句,重新开始执行,又一次从暗到亮,也会有一个proteus中肉眼看见的翻转,但是频率很快,几乎看不见灭,这是因为重复执行main,只能看到第一次的效果。
所以我们可以确定,main函数不加while就会重复执行,有一个软复位的过程。
未解决疑惑点:
在上面的代码中,定时器中断里还有另一段代码,在之前的过程中,如果我用这段产生PWM,并且不加while1,他的占空比是变化的,也就是说,他是自动PWM调制了,类似呼吸灯的效果
以上两张图片就是仿真时截图,可以看到占空比不断变化,但是实在是不懂为什么会这样。。。。。