关于51单片机中断的一些问题,求大神帮忙看看。

laexl 2017-07-30 02:34:12
问题:代码L41-43,对定时器1赋值,并且打开定时器1的运行控制位。但是除了这三行,没有看到其他地方调用定时器1,不知道这三行有什么作用。我尝试把这三行备注掉,程序仍能正常运行,但是板子上的数码管不能正常工作,将一直显示000。同时串口助手也将无法接收到任何数据。求大神解释一下其中缘由。



单片机上电后等待从上位机串口发送来的命令,同时在数码管前三位以十进制方式显示A/D菜鸡的数值,在未收到上位机发送来的启动A/D转换命令之前数码管始终显示000。
当收到上位机以十六进制发送来的01后,想上位机发送字符串“Turn on ad!”,同时间隔一秒读取一次A/D的值,然后把A/D采集回来的8位二进制数转换成十进制数表示的实际电压浮点数,并且从串口发送给上位机,形式如“The voltage is 3.398438V”,发送周期也是一秒一次,同时在数码管上也要每秒刷新显示的数值。
当收到上位机以十六进制发送来的02后,向上位机发送字符串“Turn off ad!”,然后停止发送电压值,数码管上显示上次结束时保持的值。
当收到上位机发来的其他任何数时,向上位机发送字符串“Error”。

#include<reg52.h>
#include<intrins.h>
#include<stdio.h>
#define uchar unsigned char
#define uint unsigned int
sbit dula=P2^6;//液晶显示屏上的显示
sbit wela=P2^7;//液晶显示屏上的显示
sbit adwr=P3^6;//控制ad转换器的写
sbit adrd=P3^7;//控制ad转换器的读
uchar flag;
uchar a;
uchar flag_uart;//中断4的标志位
uchar flag_time;//中断1 的标志位
uchar flag_on ;//在中断4中赋值,通过此变量与switch可以控制选择工作模式
uchar i;
uchar t0_num;
uchar ad_val;//储存ad转换后得到的结果
float ad_vo;//储存电压值

uchar code table []= //数码管的固定编制
{
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};

void delayms(uint xms) //固定的延时函数
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}

void init() //初始化函数,是串行口这一部分的设置
{
TMOD=0x21;//0010 0001 定时器工作方式寄存器
//SCON=0x50; 设置串行口控制寄存器。常用的SM0、SM1、REN、TI都可以在这里设置,此处吧SCON设置为0101 0000,SM0 SM1 SM2 REN TB8 RB8 TI RI.即通过SM1SM0设置工作方式为10位异步收发(8位数据)波特率可变。通过REN设置允许串行口接受数据。
TH0=(65536-50000)/256;//装填初值
TL0=(65536-50000)%256;//装填初值
TH1=0xfd;//1111 1101 十进制254,装填初值 ???
TL1=0xfd;//1111 1101 十进制254,装填初值 ???
TR1=1;//定时器1运行控制位:启动定时器1
ET0=1;//定时器0中断允许位:置1时允许定时器1发生中断。
SM0=0;//设定串行口控制寄存器的工作方式选择位
SM1=1;//设定串行口控制寄存器的工作方式选择位
REN=1;//允许串行接收位
EA=1;//全局中断允许位
ES=1;//串行口中断允许位
}

void display(uchar value)//显示函数,分解成三个数,分别显示
{
uchar bai,shi,ge;
bai=value/100;
shi=value%100/10;
ge=value%10;

dula=1;
P0=table[bai];
dula=0;
P0=0xff;
wela=1;
P0=0x7e;
wela=0;

delayms(5);

dula=1;
P0=table[shi];
dula=0;
P0=0xff;
wela=1;
P0=0x7d;
wela=0;

delayms(5);

dula=1;
P0=table[ge];
dula=0;
P0=0xff;
wela=1;
P0=0x7b;
wela=0;

delayms(5);
}

uchar get_ad()//获取ad转换后的值
{
uchar num;//定义一个临时变量num对读取倒的数进行储存
adwr=1;//关闭ad转换
_nop_();//延时函数,延时一个机器周期
adwr=0;//打开ad写入
_nop_();
adwr=1;//关闭ad写入
P1=0xff;//消影
adrd=1;//关闭ad读取
_nop_();
adrd=0;//打开ad读取
_nop_();
num=P1;//把读取到的数据从P1口取出,赋值给变量num
adrd=1;//关闭ad读取
return num;//把读取到的返回值返回。
}

void main()
{
init();//先把文件初始化,即运行一次init中的东西。
wela=1;//打开控制电脑屏幕的wela设置
P0=0x7f;//ADCS,片选信号输入端
wela=0;//关闭位选
while(1)//始终执行while循环内的语句。
{
if(flag_uart==1)//如果发生串行口中断(达到一秒)
{
flag_uart=0;//中断重置
ES=0;//关闭串行口中断允许位,暂时不允许发生串行口中断
TI=1;//发送中断标志位
switch(flag_on)
{
case 0:puts("Turn on ad!\n");
TR0=1;//定时器0运行控制位
break;
case 1:printf("Turn off ad!\n");
TR0=0;//定时器0运行控制位
break;
case 2:puts("Error!\n");
break;
}
while(!TI);//TI=0时,才能跳出循环,这是中断标志位,
TI=0;
ES=1;//打开串行口中断允许位
}
if(flag_time==1)//如果发生中断1(定时器0),满一定时间发生中断。
{
flag_time=0;
ad_val=get_ad();//利用get_ad()函数获得ad转换之后的结果,赋值给ad_val
ad_vo=(float)ad_val*5.0/256.0;//把获得的模拟量转化成电压值
ES=0;//关闭串行口中断允许位
TI=1;
printf("The voltage is %fV\n",ad_vo);
while(!TI);
TI=0;//利用软件将TI发送中断标志位置0
ES=1;//打开串行口中断允许位
}
display(ad_val);//显示模拟量的大小。。。
}
}

void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
t0_num++;
if(t0_num==20)
{
t0_num=0;
flag_time=1;
}
}

void ser() interrupt 4
{
RI=0;
a=SBUF;
flag_uart=1;
if(a==1)
{
flag_on=0;
}
else if(a==2)
{
flag_on=1;
}
else
{
flag_on=2;
}
}
...全文
176 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
worldy 2017-08-02
  • 打赏
  • 举报
回复
定时器1 在这里是做串口波特率发生器的,你去掉等于定时器1没有初始化,没有打开,波特率就错误
zgl7903 2017-07-31
  • 打赏
  • 举报
回复
定时器1 在这里是做串口波特率发生器的
1.uC/OS是个什么鬼?在一些朋友的留言和大神的文章中多次提到了uC/OS,相信很多朋友看到之后都是蒙B的“这是个什么鬼?”。uC/OS(MicroControlOperaTIonSystem)翻译过来就是微控制器操作系统,最初版本是在1992年发布,现在已经发展到uC/OSIII了。嗯,那它有什么用呢?接触过单片机或编程的朋友一定知道main()函数,mian()又叫主函数或者入口函数,顾名思义就是程序开始执行的地方(其实这是不严谨的,但是为了照顾小白可以这样简单的理解),而一般的裸机程序只有一个main(),从程序的开头到结尾跑一次就完了,而为了让程序能不停的跑往往会在main()中加一个while(true)让其不断的循环。uC/OS因为其处理方式理论上可以模拟无数个“main()函数”(任务),让这些任务并发运行,就像在一个单片机中有多个main()函数一样,让原本单线程的单片机能有多线程的效果。那uC/OS是变出无限个main函数呢,答案就是时钟节拍,时钟节拍就是系统以固定的频率产生中断(时基中断),并在中断中处理与时间相关的事件,推动所有任务向前运行。简单的来说就是高频率的切换任务来实现类似多线程的效果,这个时钟节拍是可调的,频率越快越浪费cpu,相应的在多个任务间切换的速度也就越快。那么什么是中断呢?从本质上来讲,中断是一种电信号的变化,当设备有某种事件发生时(产生电平变化),它就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理。(就好比小明现在正在看我写的这篇文章,突然有快递来敲门他就会先放下手机去取快递然后回来接着看,既拿了快递又读了文章)如上图所示电信号从低电平跳转高电平的中断称之为上升沿中断,反之称之为下降沿中断。因为篇幅的限制以上只是简单的介绍了一下uC/OS,当然它还有很多其它的优点,例如内存分配,任务消息队列,等等。这些都是这个系统多年迭代累积下来的“车轮”,如果感兴趣的朋友可以深入的了解一下。uC/OS固然是好,但是真的有必要什么都用uC/OS么?我看未必,杀鸡何用宰牛刀。以下引用一位知友的留言:“2以前没学过操作系统,用单片机裸机写程序。有一次做一个功能特别复杂的东西,发现中断都快用完了,并且用中断有些地方实现的特别勉强,冗余,复杂。当时根本就不知道有多任务,也以那时的知识根本想不到如何优化。如果当时学过操作系统,用过ucos,这个问题就很好解决了。不信,你看能不能用裸机实现一个简单的平板电脑。6以后想去大公司,做平板,做手机,不会嵌入式linux估计不行吧,这个依旧包括了上述所有知识。”(这位朋友表达的意思我懂,在此只是引用这一段话,只是引用!)(此段纯吐槽)可能是现在中国手机行业很赚钱。是个大一点的公司就要做平板,做手机,大公司都需要这样的人才,一窝蜂的往里钻。虽然arm理论上也算是单片机的一种,但是我更喜欢将其归入微处理器的行列,做手机做平板要是我肯定不会选stm32上uC/OS来做。如果是为了好找工作,为了做手机那还是学嵌入式linux比较靠谱。我是学自动化的,在我的感觉里单片机(stm32,avr,飞思卡尔,51等等)这类是属于微控器,最适合的就是用来做控制的,不是为了做手机而生的。和做人一样首先要清楚自己的定位,清楚自己能干什么,不能干什么。汽车芯片,3d打印机,数控机床,手环,液晶显示器,四轴,录像机,洗衣机,玩具,飞机仪表板等等等等,都有单片机的用武之地,我给它的定位就是简单重复高效的控制器。有朋友留言说“一句话想赚钱不要学这个。”我只能引用这样一句话“中国不是实体经济不行了,而是你的实体经济不行了。
1.首先是蓝牙APP, 易安卓编写的,说编写其实我只是修改了其中的一些内容,主要的部分都是易锦老师视频里的那个软件,不过我已经懂得了那些命令,(后面有工程文件,如果不 懂,可以去找易锦老师的视频来看,如果找不到,我这存的有),两张界面和代码截图,非常简单,功能也很简单,程序前后修改了两次主要地方,主要原因是测试 的时候发现第一种程序会出现错误,在单片机哪里会仔细说明! 操作界面,很简单,打开之后打开蓝牙,然后点击搜索设备,找到你的模块名字,点击之后就可以连接了,连接之后下面会显示蓝牙的名称和地址信息 2.单片机程序,这个程序也很简单,只要学过一些单片机程序的人应该都知道吧,串口通信,设置好通信的波特率,初始化工作做好,然后在串口中断程序里写上你要做的事情就可 以了,这里虽然说11.0592的晶振定时器初值为fd,但是如果用12m的晶振也是可以的,差距不多,没有问题。(说的不怎么专业,我也不是很专业的 人,所以请大神误喷,见笑了!)这里是修改前后的程序不一样的地方,前面的程序是单片机没接收到数据之后读取前一次的IO状态,然后改变其状态,但是测试 的时候发现读取状态有错误,估计是我的电路有问题,第一个继电器可以正常工作,第二个和第三个都有问题,当第一个关闭的时候可以打开,但是当打开的了却不 能关闭,只能用关闭所有的命令来关闭,(找了一下午也没发现问题,元件换了几个都没找到,后来放弃了),后来就换了后面程序,直接发送状态命令,不用判断 当前的状态了,我觉得后面这种可能更好!而且实际测试的时候也可以,没有问题。(补充一下,我发现12M的晶振不能用11.0592M的数据,原因是定时 器计数产生的波特率与9600差距有点大,误差到达了8.5%左右,理论上误差要小于4%才能正常通信,所以通信有错误,虽然能通信,但是数据不对,后来 我把晶振换回来就可以了,看来要实践才知道真理。) 第 一、二张是修改之前的程序,有问题,最后一张是修改之后的,没有问题,后来仔细想了一下,后面一种才是正确的,前面一种的改变状态可能会出现错误,就是手 机上显示的开关是关的,然而实际电路中的电路是开着的(这也是没有数据回传的原因吧,现在只是单向的手机发,模块接的形式,以后再研究)!,但是后面一种 不会出现这个问题! 3.实物电路连接,我也是在测试,所以先用LED 等来代替继电器输出,然后才用到继电器上面。单片机直接放在我做的最小系统版上面,然后用导线来连接到蓝牙开关的小板子上,等测试无误之后再安装在上面, 不然不好写程序上去。输出接的是一个小电机,用的一个12v蓄电池代替220V电源,如果要用220V的电源,要注意安全了!提醒一下,绝缘一定要做好, 毕竟不是开玩笑的。简单说一下电路连接,首先你得需要焊接一个51单片机最小系统板,(如果这个都不会,那你需要先学习一下,不然肯定是没办法做的)然后是由三极管驱动继电器的电路,记得加二极管,不然三极管很有几率被击穿,最后是蓝牙模块与单片机的连接,电源接好,一般蓝牙模块都是宽电压的,所以直接接到5V电源上,与单片机共用电源,不用什么电压转换,很方便的,把蓝牙模块的TX与单片机的RX连接,就是P3.0那个引脚,RX接单片机的TX,就是P3.1那个引脚,至于继电器哪里你需要接成常开还是常闭的模式就你自己决定了,当然还要加一点录滤波的,因为继电器启动的一瞬间电流很大,担心是单片机死机!这些就是主要的东西了!) 前一张是之前测试用的,后面一张是后来直接把单片机装上去的,看着没有那么乱了,可以看到,当手机上的开关23打开时,电路板上的灯23也是两的,表明继电器已经被打开了。 4.打完收工,作品完成好 了至此最简单的蓝牙开关就做好了,可以躺在床上遥控在远处的风扇了(好吧你们都用的是空调,当我没说!),定时关机(这个功能没做,不过原理都一样,自由 发挥了),其他神马的!感兴趣的同学可以试一下,比如说高级一点的外网控制的,把电脑作为服务器,把蓝牙模块接好,和控制器连接起来,然后让手机与电脑通 过互联网通信,用手机给电脑发送指令,再通过电脑给蓝牙模块发送指令,比如提前开个空调什么的(提前开风扇没用,还是开你们的空调吧),然后其他什么的东 西就自己发挥了!我想这个应该是属于传说中的物联网吧,虽然没有那么高大上,但是原理是一样的。
我要实现的功能就是能用我手机控制电源的通、对家里的电器的开关进行控制。(目前只是内网操作) 1.首先是蓝牙APP 易安卓编写的,说编写其实我只是修改了其中的一些内容,主要的部分都是通过视频学习的,不过我已经懂得了那些命令,(后面有工程文件,如果不懂,可以去找易锦老师的视频来看,如果找不到,我这存的有),两张界面和代码截图,非常简单,功能也很简单,程序前后修改了两个主要地方,主要原因是测试的时候发现第一种程序会出现错误,在单片机哪里会仔细说明! 操作界面,很简单,打开之后打开蓝牙,然后点击搜索设备,找到你的模块名字,点击之后就可以连接了,连接之后下面会显示蓝牙的名称和地址信息。这个是编程软件中显示的界面,可能和在手机上面运行的不一样,因为有些东西是非可视的,不过不影响,这反而能让我们知道更多的细节。 2.单片机程序 这个程序也很简单,只要学过一些单片机程序的人应该都知道吧,串口通信,设置好通信的波特率,初始化工作做好,然后在串口中断程序里写上你要做的事情就可 以了,这里虽然说11.0592的晶振定时器初值为fd,但是如果用12m的晶振也是可以的,差距不多,没有问题。(说的不怎么专业,我也不是很专业的 人,所以请大神误喷,见笑了!)这里是修改前后的程序不一样的地方,前面的程序是单片机没接收到数据之后读取前一次的IO状态,然后改变其状态,但是测试 的时候发现读取状态有错误,估计是我的电路有问题,第一个继电器可以正常工作,第二个和第三个都有问题,当第一个关闭的时候可以打开,但是当打开的了却不 能关闭,只能用关闭所有的命令来关闭,(找了一下午也没发现问题,元件换了几个都没找到,后来放弃了),后来就换了后面程序,直接发送状态命令,不用判断 当前的状态了,我觉得后面这种可能更好!而且实际测试的时候也可以,没有问题。(补充一下,我发现12M的晶振不能用11.0592M的数据,原因是定时 器计数产生的波特率与9600差距有点大,误差到达了8.5%左右,理论上误差要小于4%才能正常通信,所以通信有错误,虽然能通信,但是数据不对,后来 我把晶振换回来就可以了,看来要实践才知道真理。) 第 一、二张是修改之前的程序,有问题,最后一张是修改之后的,没有问题,后来仔细想了一下,后面一种才是正确的,前面一种的改变状态可能会出现错误,就是手 机上显示的开关是关的,然而实际电路中的电路是开着的(这也是没有数据回传的原因吧,现在只是单向的手机发,模块接的形式,以后再研究)!,但是后面一种 不会出现这个问题! 3.实物电路连接 我也是在测试,所以先用LED 等来代替继电器输出,然后才用到继电器上面。单片机直接放在我做的最小系统版上面,然后用导线来连接到蓝牙开关的小板子上,等测试无误之后再安装在上面,不然不好写程序上去。输出接的是一个小电机,用的一个12v蓄电池代替220V电源,如果要用220V的电源,要注意安全了!提醒一下,绝缘一定要做好,毕竟不是开玩笑的。简单说一下电路连接,首先你得需要焊接一个51单片机最小系统板,(如果这个都不会,那你需要先学习一下,不然肯定是没办法做的)然后是由三极管驱动继电器的电路,记得加二极管,不然三极管很有几率被击穿,最后是蓝牙模块与单片机的连接,电源接好,一般蓝牙模块都是宽电压的,所以直接接到5V电源上,与单片机共用电源,不用什么电压转换,很方便的,把蓝牙模块的TX与单片机的RX连接,就是P3.0那个引脚,RX接单片机的TX,就是P3.1那个引脚,至于继电器哪里你需要接成常开还是常闭的模式就你自己决定了,当然还要加一点录滤波的,因为继电器启动的一瞬间电流很大,担心是单片机死机!这些就是主要的东西了!) 前一张是之前测试用的,后面一张是后来直接把单片机装上去的,看着没有那么乱了,可以看到,当手机上的开关23打开时,电路板上的灯23也是两的,表明继电器已经被打开了。 4.打完收工,作品完成 好了至此最简单的蓝牙开关就做好了,可以躺在床上遥控在远处的风扇了(好吧你们都用的是空调,当我没说!),定时关机(这个功能没做,不过原理都一样,自由发挥了),其他神马的!感兴趣的同学可以试一下,比如说高级一点的外网控制的,把电脑作为服务器,把蓝牙模块接好,和控制器连接起来,然后让手机与电脑通过互联网通信,用手机给电脑发送指令,再通过电脑给蓝牙模块发送指令,比如提前开个空调什么的(提前开风扇没用,还是开你们的空调吧),然后其他什么的东西就自己发挥了!我想这个应该是属于传说中的最简单的物联网吧,虽然没有那么高大上,但是原理是一样的。虽然是手动控制的,不过可以发挥你聪明大脑,让他自动控制啊!

27,382

社区成员

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

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