标题: 关于51单片机计脉冲程序问题 [打印本页]
作者: guhuawei19 时间: 2024-6-28 15:54
标题: 关于51单片机计脉冲程序问题
#include <reg51.h>
#include <intrins.h>
#include "display.h"
sbit LED=P1^0;
void Timer0Init(void);
void EX0_Init();
void EX1_Init();
void display_Service();
unsigned int numCnt=0; //用于存放要显示的脉冲数
unsigned char flag;
unsigned char temp0;
unsigned char temp1;
unsigned char temp2;
unsigned char temp3;
void main()
{
Timer0Init();
EX0_Init();
EX1_Init();
EA=1; //中断总开关
while(1)
{
}
}
void display_Service()
{
temp0=numCnt/1000;
temp1=numCnt/100%10;
temp2=numCnt/10%10;
temp3=numCnt%10;
if(numCnt<1000)
LEDBuf[0]=12;
else
LEDBuf[0]=temp0;
if(numCnt<100)
LEDBuf[1]=12;
else
LEDBuf[1]=temp1;
if(numCnt<10)
LEDBuf[2]=12;
else
LEDBuf[2]=temp2;
LEDBuf[3]=temp3;
}
void EX0_Init()
{
IT0=1;
EX0=1;
}
void EX1_Init()
{
IT1=1;
EX1=1;
}
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
ET0=1; //中断定时器0的开关
TR0 = 1; //定时器0开始计时
}
// void Timer1Init(void) //50毫秒@12.000MHz
// {
// TMOD &= 0x0F; //设置定时器模式
// TMOD |= 0x10; //设置定时器模式
// TL1 = 0xB0; //设置定时初值
// TH1 = 0x3C; //设置定时初值
// ET1=1;
// TF1 = 0; //清除TF1标志
// TR1 = 1; //定时器1开始计时
// }
/*******************************************
1、中断服务函数一定是一个没有返回值函数
2、中断服务函数一定是没有参数的函数
3、中断服务函数函数名后要跟关键字 interrupt n
4、interrupt n 0-4 8*n+0003H 0003H INT0
000BH T0 0013H INT1 001BH TI 0023H ES
5、中服务函数不能被主程序或其他程序调用
6、n 后面跟 using m(0-3)工作寄存器组
*******************************************/
void timer0_ISR(void) interrupt 1
{
TR0=0; //定时器0关闭
Display();
TL0 = 0x18; //重新定时初值
TH0 = 0xFC; //设置定时初值
TR0=1; //定时器0开始计时
}
void EX0_ISR() interrupt 0
{
EX0=0;
numCnt++;
if(numCnt>=9999)
numCnt=0;
EX0=1;
}
void EX1_ISR() interrupt 2
{
EX1=0;
flag=1;
display_Service();
numCnt=0; //实现数码管清零操作
EX1=1;
}
问题一:程序跑的频率比较低,超过5K的信号无显示
问题二:跑出来计的脉冲数不准确
作者: cedtek 时间: 2024-6-29 08:10
您的代码遇到了性能和脉冲计数准确性的问题,这是由于中断和定时器处理方式所致。以下是一些洞察和建议,以解决这些问题:
问题分析解决步骤1. 优化ISR执行时间尽量减少ISR中的代码,以确保它能够尽快执行。例如,在timer0_ISR中不要调用显示函数,而是在主循环中设置一个标志并处理显示更新。
bit displayUpdateFlag = 0;
void timer0_ISR(void) interrupt 1
{
TR0 = 0; // 停止Timer0
displayUpdateFlag = 1;
TL0 = 0x18; // 重置Timer0初始值
TH0 = 0xFC;
TR0 = 1; // 重新启动Timer0
}在主循环中检查这个标志,并在需要时调用显示更新函数:
void main()
{
Timer0Init();
EX0_Init();
EX1_Init();
EA = 1; // 启用全局中断
while (1)
{
if (displayUpdateFlag)
{
displayUpdateFlag = 0;
display_Service();
}
}
}2. 确保脉冲计数准确尽可能短暂地禁用和重新启用你的ISRs中的中断,以防错过脉冲。
void EX0_ISR() interrupt 0
{
numCnt++;
if (numCnt >= 9999)
numCnt = 0;
}注意,在其ISR中无需禁用EX0,因为进入ISR时中断标志会自动清除。这减少了中断被禁用的时间。
3. 高效使用Timer0定时器ISR应仅负责基于时间的任务,而不是处理复杂操作如更新显示。
最终代码调整以下是根据上述建议修改后的代码:
#include <reg51.h>
#include "display.h"
sbit LED = P1^0;
void Timer0Init(void);
void EX0_Init();
void EX1_Init();
void display_Service();
unsigned int numCnt = 0; // 用于存储要显示的脉冲数量
bit displayUpdateFlag = 0;
unsigned char temp0, temp1, temp2, temp3;
void main()
{
Timer0Init();
EX0_Init();
EX1_Init();
EA = 1; // 启用全局中断
while (1)
{
if (displayUpdateFlag)
{
displayUpdateFlag = 0;
display_Service();
}
}
}
void display_Service()
{
temp0 = numCnt / 1000;
temp1 = (numCnt / 100) % 10;
temp2 = (numCnt / 10) % 10;
temp3 = numCnt % 10;
LEDBuf[1] = (numCnt < 1000)? 12 : temp0;
LEDBuf[2] = (numCnt < 100)? 12 : temp1;
LEDBuf[3] = (numCnt < 10)? 12 : temp2;
LEDBuf[4] = temp3;
}
void EX0_Init()
{
IT0 = 1; // 边缘触发中断
EX0 = 1; // 启用EX0
}
void EX1_Init()
{
IT1 = 1; // 边缘触发中断
EX1 = 1; // 启用EX1
}
void Timer0Init(void) // 1ms @ 12.000MHz
{
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01; // 设置定时器模式
TL0 = 0x18; // 设置初始定时器值
TH0 = 0xFC; // 设置初始定时器值
TF0 = 0; // 清除TF0标志
ET0 = 1; // 启用Timer0中断
TR0 = 1; // 启动Timer0
}
void timer0_ISR(void) interrupt 1
{
TR0 = 0; // 停止Timer0
displayUpdateFlag = 1; // 设置标志以更新显示
TL0 = 0x18; // 重置Timer0初始值
TH0 = 0xFC;
TR0 = 1; // 重新启动Timer0
}
void EX0_ISR() interrupt 0
{
numCnt++;
if (numCnt >= 9999)
numCnt = 0;
}
void EX1_ISR() interrupt 2
{
flag = 1;
display_Service();
numCnt = 0; // 清除计数以重置显示
}这应该提高了嵌入式系统的性能和准确性,确保它能处理更高频率的信号并更准确地计数脉冲。
作者: tonny126 时间: 2024-6-29 09:47
如果要调整占空比,那就调整PWM4DUTY和占空比那高位的重载值 ,有一点要注意就是占空比的值不能大于TMR4的重载值,不然就是一直有效电平了。如果把TM34RH=0,PWM4DUTY=占空比了吗? 如果要调整频率,那就是调整分频比和TMR4重载值。 TMR3 重载值=100,向下计数的话,是不是我频率是1us,这里就是100us?但是我要调成红外发射频率38KHz,也就是TMR3=26us的话,又和上面的注意事项冲突了。
作者: xiaobendan001 时间: 2024-6-29 10:35
不知道你的准确意图,看起来脉冲是接外中断0,外中断1是清零。如果不清零就一直在0-9999之间循环。显示一直在更新,是啥意思,如果脉冲速度很快,可能都看不清了。超过5K的信号无显示是说的频率,还是计数值达到5000以后就不显示了?脉冲的频率是多少?外中断采集脉冲比较容易受到干扰,使计数值变大。但是你使用的是外中断0,是最高优先,大概率不会有丢脉冲的情况。
作者: lkc8210 时间: 2024-6-29 14:51
一:不要用外部中断,用定时器的计数模式
二: 同上
作者: guhuawei19 时间: 2024-6-29 17:00
程序的主要原理就是测编码器转一圈测试实际的脉冲数,有个零位是起始到终止的总脉冲数,然后在显示函数里显示,可能显示函数放到中断了造成显示过快,是不是要把显示函数放到MAIN函数下面?
作者: xiaobendan001 时间: 2024-6-29 18:14
你的显示数据只在外中断1里面更新一次的,就是计数过程看不到,清零时才能看到?
作者: xiaobendan001 时间: 2024-6-30 07:37
干嘛要抄啊,赚个积分不容易。删除几个字比较容易是吧
作者: guhuawei19 时间: 2024-7-1 07:56
不需要一直更新显示,只需要读出最后的脉冲数,然后显示在数码管上,比如500脉冲只要显示500,不需要显示增加的过程
作者: guhuawei19 时间: 2024-7-1 09:51
while(1)
{
if(flag==1)
{
DelayXms(100);
display_Service();
}
改成这样能否正常显示
作者: xiaobendan001 时间: 2024-7-1 11:41
DelayXms(100);的意图是什么啊?
作者: xiaobendan001 时间: 2024-7-1 13:03
这意思是外中断1是接在编码器的Z相了?
作者: guhuawei19 时间: 2024-7-1 16:26
不加延时的话,进一次中断就显示一次速度太快了吧?会显示不清楚吧
作者: guhuawei19 时间: 2024-7-1 16:27
是的,中断1接到Z相
作者: xiaobendan001 时间: 2024-7-2 07:16
无论调用多少次显示,都只是在Z相信号到来时的瞬间才更新显示,其余时间都是在现实之前的内容,所以从视觉上是看不到的。
比如我在研究TM1650的时候,显示在主循环,显示内容是定时器,1秒变一次,主循环在1秒里面不知道循环了多少次了,显示依旧稳定的很。
可惜1650的按键扫描太慢了,做不了编码器。
作者: guhuawei19 时间: 2024-7-2 10:45
那使用脉冲计数呢
欢迎光临 (http://www.51hei.com/bbs/) |
Powered by Discuz! X3.1 |