Keil下初次玩AT89C51,被定时器搞吐血了!特来求救!

KiteGirl 2008-01-14 03:50:15
原本用循环延时,见下帖子:
http://topic.csdn.net/u/20080113/22/a42c9278-e38d-4d0f-af0b-b7834b301f40.html

后来发现C编译后的循环不是精确到时钟周期的。于是改用定时器(代码如下)。
据说89C51定时器是1/12分频的,应该是1us一个记数,1ms理应是1000次记数(定时器初始值65536-1000=64536)。
可实际把程序写出来Keil下仿真发现明显过快。后来把中断记数周期改成12000次,才够1ms,彻底吐血。
由于手头没有仿真器和单片机,程序只在Keil下软件仿真,实在不知道究竟怎么回事,特来求救。
谁能帮我搞清楚这个问题,此帖及上面那帖子总共200分全给他。最好哪位老兄有单片机实际帮我测试一下。
此程序应该是以500ms的间隔依次点亮连接在P1、P2的16盏灯。


#include <C:\Keil51\C51\INC\REG51.h>

//
unsigned int pubTimerInt;
unsigned int pubTimerValue;

void SetTimerout(unsigned int pTimeout)
{
EA=1;
ET0=1;
TMOD=0x01;
pubTimerValue=65535-pTimeout;
TH0=pubTimerValue/256;
TL0=pubTimerValue%256;
TR0=1;
}

void intserv1(void) interrupt 1 using 1
{
if (pubTimerInt>0)
{
pubTimerInt=pubTimerInt-1;
}
TH0=pubTimerValue/256;
TL0=pubTimerValue%256;
}

void WhileTimeout(unsigned int pTimeout)
//WhileTimeout函数
//语法:WhileTimeout(pTimeout);
//说明:产生pTimeout指定延时,单位为ms。
//参数:unsigned int pTimeout 单位为ms的延时参数。
{
TR0=1;
TH0=pubTimerValue/256;
TL0=pubTimerValue%256;
pubTimerInt=pTimeout;
TR0=1;
while(pubTimerInt)
{
}
}

void ProtDevice_LO(unsigned char pConByte, unsigned char pProt)
//ProtDevice函数
//语法:ProtDevice(pConByte, pProt);
//说明:驱动端口(低电平开启)。
//参数:unsigned char pConByte 端口控制字节(高电平开启)。
// unsigned char pProt 端口选择(0-3)
{
switch(pProt)
{
case 0: P0=~pConByte;break;
case 1: P1=~pConByte;break;
case 2: P2=~pConByte;break;
case 3: P3=~pConByte;break;
}
}

void LoopOpen_LO(unsigned int pOpenTimeout, unsigned char pProt)
//LoopOpen_LO函数
//语法:LoopOpen_LO(pOpenTimeout, pProt);
//说明:循环驱动端口(低电平开启)。
//参数:unsigned int pOpenTimeout 开启时间设置(单位ms)
// unsigned char pProt 端口选择(0-3)
{
unsigned char tConByte;
unsigned int tLoopIndex;
unsigned int tLoopIndex_Length;

tLoopIndex_Length=7;

//开关0号。
tConByte=1;
ProtDevice_LO(tConByte, pProt); //驱动端口
WhileTimeout(pOpenTimeout);

//开关1-7号。
for (tLoopIndex=0; tLoopIndex<tLoopIndex_Length; tLoopIndex++)
{
tConByte=tConByte<<1;
ProtDevice_LO(tConByte, pProt); //驱动端口
WhileTimeout(pOpenTimeout);
}
}

void main()
{

unsigned char tProtA;
unsigned char tProtB;
unsigned char tConByte;
unsigned int tOpenTimeout;
unsigned int tWhileTimeout;
unsigned int tTimeoutValue;
tProtA=1;
tProtB=2;
tOpenTimeout=500;
tWhileTimeout=3000;
tTimeoutValue=12000; //按道理应该是1000us=1ms,可这里一定要设置12000才能保证中断是1ms。
tConByte=0;
ProtDevice_LO(tConByte, tProtA);
ProtDevice_LO(tConByte, tProtB);
SetTimerout(tTimeoutValue);
while(1)
{
//第一端口
LoopOpen_LO(tOpenTimeout, tProtA);
tConByte=0;
ProtDevice_LO(tConByte, tProtA);
//第二端口
LoopOpen_LO(tOpenTimeout, tProtB);
tConByte=0;
ProtDevice_LO(tConByte, tProtB);
//等待
WhileTimeout(tWhileTimeout);
}
}


编译后的HEX文件内容:

:1000E200D2AFD2A9758901C374FF9FF51274FF9E26
:0A00F200F511F58C85128AD28C22DC
:03000B0002009D53
:10009D00C0E0C0D0D3E5149400E51394004008E50A
:1000AD0014151470021513E511F58C85128AD0D034
:0300BD00D0E0325E
:1000FC00AD07AC06D28CE511F58C85128A8C138D6C
:0A010C0014D28CE514451370FA229A
:1000C000ED14600F14601114601324037013EFF427
:1000D000F58022EFF4F59022EFF4F5A022EFF4F58D
:0200E000B0224C
:060062008D0EAB07AA069B
:10006800750F0075100779017F011200C0AF0312E8
:1000780000FC900000C3E5829510E583950F5014AD
:10008800E925E0F9FFAD0E1200C0AF03AE02120081
:04009800FCA380E164
:01009C002241
:10000E00750801750902750A01750BF4750C0B75EF
:10001E000DB87BE07A2EE4F9FFAD081200C0AD09F1
:10002E001200C07FE07E2E1200E2AD08AF0BAE0ACA
:10003E00120062E4F9FFAD081200C0AD09AF0BAEBD
:10004E000A120062E4FFAD091200C0AF0DAE0C1231
:04005E0000FC80D64C
:03000000020116E4
:0C011600787FE4F6D8FD75811402000E1D
:00000001FF
...全文
809 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhujinqiang 2008-01-23
  • 打赏
  • 举报
回复
哦也!

KiteGirl 2008-01-22
  • 打赏
  • 举报
回复
好了!解决了。另外一个帖子我等等yanglin5131421去接分,如果他不接的话三天之内我会结的。:)

我讨厌被人用枪指着后脑勺结帖子!!!>(
MBWQ 2008-01-22
  • 打赏
  • 举报
回复
哈哈!今天我用面包板把单片机搭出来了,实验成功了!果然烧到AT89C51上是0.5s,而不是仿真时候那么快。

感谢MBWQ、zhujinqiang、yanglin5131421三位。请你们同时到下面帖子里接另外的100分。

http://topic.csdn.net/u/20080113/22/a42c9278-e38d-4d0f-af0b-b7834b301f40.html


----------------------------------------------------------------------------------------
言必行,行必果,请楼主及时结贴
lbing7 2008-01-22
  • 打赏
  • 举报
回复
哈哈哈哈哈

小仙妹怎么说也是水源一大镁铝哈

嘿嘿嘿

KiteGirl 2008-01-22
  • 打赏
  • 举报
回复
不会结贴?
呵呵,

方法:
管理帖子--> 输入密码(1楼右上角)--> 给各人分--> 结贴(1楼右上角)
=====================================================================
没搞错吧!我会这么弱智吗?想催我结贴就明说嘛……:)
zhujinqiang 2008-01-22
  • 打赏
  • 举报
回复
不会结贴?
呵呵,

方法:
管理帖子--> 输入密码(1楼右上角)--> 给各人分--> 结贴(1楼右上角)
KiteGirl 2008-01-14
  • 打赏
  • 举报
回复
TF0响应后自动清0的,因此在中断处理程序里TF0总是0。加上TF0=0还是一样飞快。
我手头没有仿真器和单片机,所以上述结果是在Keil里测试发现的。还不知道真正烧到片里什么样子。
计数器周期从1000改成12000是我猜的(因为我感觉它的速度是12倍),于是我把计数器周期改成12000之后,将定时脉冲从500改成1000,发现刚好是一秒。也就是说12000次计数才是1秒。似乎压根就没有1/12分频。彻底精神崩溃了。
zhujinqiang 2008-01-14
  • 打赏
  • 举报
回复
晕,是TF0=0
zhujinqiang 2008-01-14
  • 打赏
  • 举报
回复
void intserv1(void) interrupt 1 using 1
{
if (pubTimerInt>0)
{
pubTimerInt=pubTimerInt-1;
}
TH0=pubTimerValue/256;
TL0=pubTimerValue%256;
TF01=0; /*漏了这个--计数溢出标志位清0*/
}
zhujinqiang 2008-01-14
  • 打赏
  • 举报
回复
80C51单片机的1个机器周期有12个振荡周期组成。12MHZ,它的时钟周期是1/12us。

当80C5l内部的定时器/计数器被选定为定时器工作模式时,计数输入信号是内部时钟脉冲,每个机器周期产生一个脉冲位,计数器增l,因此定时器/计数器的输入脉冲的周期与机器周期一样,为时钟振荡频率的l/12。当采用12MHz频率的晶体时,计数速率为 1MHz,输入脉冲的周期间隔为1us。

void SetTimerout(unsigned int pTimeout)
{
EA=1;
ET0=1; /*设置T0为定时方式1*/
TMOD=0x01;
pubTimerValue=65535-pTimeout;
TH0=pubTimerValue/256;
TL0=pubTimerValue%256;
TR0=1;
}
/*上面正常。*/
void intserv1(void) interrupt 1 using 1
{
if (pubTimerInt>0)
{
pubTimerInt=pubTimerInt-1;
}
TH0=pubTimerValue/256;
TL0=pubTimerValue%256;

TF1=0; /*计数溢出标志位清0*,这个漏了!/
}


yanglin5131421 2008-01-14
  • 打赏
  • 举报
回复

LZ来看看我前几天转载的一个帖子,希望对你有帮助
http://topic.csdn.net/u/20080110/11/6be2a051-b997-4d78-a9bd-3957fdf86414.html
yanglin5131421 2008-01-14
  • 打赏
  • 举报
回复

LZ来看看我前几天转载的一个帖子,希望对你有帮助
http://topic.csdn.net/u/20080110/11/6be2a051-b997-4d78-a9bd-3957fdf86414.html
KiteGirl 2008-01-14
  • 打赏
  • 举报
回复
哈哈!今天我用面包板把单片机搭出来了,实验成功了!果然烧到AT89C51上是0.5s,而不是仿真时候那么快。

感谢MBWQ、zhujinqiang、yanglin5131421三位。请你们同时到下面帖子里接另外的100分。

http://topic.csdn.net/u/20080113/22/a42c9278-e38d-4d0f-af0b-b7834b301f40.html
yanglin5131421 2008-01-14
  • 打赏
  • 举报
回复
呵呵,小仙妹 您好,你是搞单片机的吗?总在水源听说您的大名,从来为得一见,呵呵
zhujinqiang 2008-01-14
  • 打赏
  • 举报
回复
晶体频率注意看看。
zhujinqiang 2008-01-14
  • 打赏
  • 举报
回复
有处理中断的时间在内,
所以实际计数值是调整的。

中断记数周期改成12000次,才够1ms,那么就用12000。
KiteGirl 2008-01-14
  • 打赏
  • 举报
回复
我选的是AT89C51。
zhujinqiang 2008-01-14
  • 打赏
  • 举报
回复
AT89LP2052,AT89LP4052就是单周期高速低功耗51内核单片机
zhujinqiang 2008-01-14
  • 打赏
  • 举报
回复
keil仿真,keil里面选的什么类型的芯片?
有的厂家的51的是单周期的。
KiteGirl 2008-01-14
  • 打赏
  • 举报
回复
根你试验了,你的程序没问题,是keil仿真出了问题
===========================================
试验的时候main函数的tTimeoutValue=12000是否改成了tTimeoutValue=1000呢?
我自己编译的HEX是12000的设置,这在仿真时是正常的,但理论上不对。
应当改成1000后保持0.5s滚动才对。

不过现在看来keil仿真的速度真的有点不大对头。
加载更多回复(2)

27,373

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧