找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2475|回复: 28
收起左侧

51单片机用定时器实现准确的延时程序 求思路

[复制链接]
ID:988065 发表于 2021-12-4 20:59 来自手机 | 显示全部楼层 |阅读模式
用定时器实现准确的延时,准确地控制对应的灯闪烁的时间间
隔,并通过按键改变时间间隔。比如:开始时,第一个灯闪烁,时间
间隔为1s。当按下按键后,前两个灯闪烁,时间间隔为2s,如此循环,
若在时间间隔为8s时按下按键,则返回开始时的状态。
这道题我实在不会做,我写的程序不能做到实时扫描按键次数,通过按键次数改变灯闪烁的频率和闪烁的个数,这些我分开写能写,但是功能组到一起就不会了,求助思路,求助大佬们
IMG_20211204_202919.jpg
回复

使用道具 举报

ID:624769 发表于 2021-12-4 22:09 | 显示全部楼层
你多多少少写点出来,那么人家根据你的代码,评估一下你的水平,适当的帮你改一改,也不是不可能。
你现在是出题?
然后,人家答题?
人家随便扔个代码上来,你确定你就能用了么?确定能看懂么?
回复

使用道具 举报

ID:332444 发表于 2021-12-5 08:09 | 显示全部楼层
现在网络提供的代码较多了,可能是为了防止新生不学无术,题目都稍有改动新生就不能即刻复制粘贴了事,老师也是被逼无奈,为了新生的苦心!所以我现在看到稍有不同的题目直接不去回复,新生应该通过理解稍加修改即可。本题随意设定定时器时间都只是基本的数学计算问题。比如定时50毫秒乘10就是半秒闪烁就是一秒,之后10乘以2就是2秒闪烁,如此类推,按键也是很简单的控制一个量值即可实现循环控制。
回复

使用道具 举报

ID:332444 发表于 2021-12-5 10:41 | 显示全部楼层
本帖最后由 xianfajushi 于 2021-12-5 11:08 编辑
  1. 升级板+静态数码管显示挡位
  2. unsigned int Js=0;
  3. void main()
  4. {
  5.         unsigned char Xd=0,xz=1,cs=1;
  6.         unsigned int wei=0,ss=10;
  7.         P0=1;
  8.         ZhongDuanSheZhi();
  9.         while(1)
  10.         {
  11.                 if(K1==0&&++Xd==0)
  12.                 {
  13.                         ss*=2;
  14.                         if(++cs>8){cs=1;ss=10}
  15.                         while(!K1);
  16.                 }
  17.                 P2=SmZiFu[cs];
  18.                 if(Js==ss)//设置定时器50毫秒
  19.                 {
  20.                         P0=~P0;
  21.                         Js=0;
  22.                 }
  23.         }
  24. }
复制代码
回复

使用道具 举报

ID:139866 发表于 2021-12-5 12:31 | 显示全部楼层
首先你需要知道什么功能要放在while循环中让他不断重复,很明显你这个按键检测是必需的,灯的闪烁是根据按键的值被选择的,至于定时器只起到了一个调整间隔的作用,你可以先写按键检测,在这个基础上加入灯,每个模式封装成一个函数,按键扫描的值存到一个变量中,根据这个变量来调用不同的模式,在不同模式下调整计时器
回复

使用道具 举报

ID:160500 发表于 2021-12-5 13:15 | 显示全部楼层
1、定时器定时1ms中断,中断程序里面用一个变量来记录中断次数。变量类型要能存储足够大的数据,能够满足你的要求。
2、主程序判断这个变量,到1S(1000ms)做什么,到2S做什么......以此类推。
3、到最后把变量清零,重新开始。如果不清零变量就要特殊处理一下也可以。
回复

使用道具 举报

ID:988065 发表于 2021-12-5 16:07 来自手机 | 显示全部楼层
188610329 发表于 2021-12-4 22:09
你多多少少写点出来,那么人家根据你的代码,评估一下你的水平,适当的帮你改一改,也不是不可能。
你现在 ...

第一次提问,没想到这个问题
回复

使用道具 举报

ID:988065 发表于 2021-12-5 16:15 来自手机 | 显示全部楼层
天ノ忆 发表于 2021-12-5 12:31
首先你需要知道什么功能要放在while循环中让他不断重复,很明显你这个按键检测是必需的,灯的闪烁是根据按 ...

我是想在主函数中用while死循环一直扫描按键按钮次数并记录,然后根据Switch还根据按键次数选择灯的个数,在case里用个for循环来让灯闪,然后用定时器写个延时函数来控制灯闪的时间间隔,但是我写不出来
IMG_20211205_161205.jpg
mmexport1638691943110.png
回复

使用道具 举报

ID:988065 发表于 2021-12-5 16:16 来自手机 | 显示全部楼层
188610329 发表于 2021-12-4 22:09
你多多少少写点出来,那么人家根据你的代码,评估一下你的水平,适当的帮你改一改,也不是不可能。
你现在 ...

我是想在主函数中用while死循环一直扫描按键按钮次数并记录,然后根据Switch还根据按键次数选择灯的个数,在case里用个for循环来让灯闪,然后用定时器写个延时函数来控制灯闪的时间间隔,但是我写不出来
IMG_20211205_161205.jpg
mmexport1638691943110.png
回复

使用道具 举报

ID:988065 发表于 2021-12-5 16:17 来自手机 | 显示全部楼层
liuzx66 发表于 2021-12-5 13:15
1、定时器定时1ms中断,中断程序里面用一个变量来记录中断次数。变量类型要能存储足够大的数据,能够满足你 ...

这跟我思路不一样,我试试,感谢思路
回复

使用道具 举报

ID:988065 发表于 2021-12-5 16:18 来自手机 | 显示全部楼层
xianfajushi 发表于 2021-12-5 08:09
现在网络提供的代码较多了,可能是为了防止新生不学无术,题目都稍有改动新生就不能即刻复制粘贴了事,老师 ...

我是想在主函数中用while死循环一直扫描按键按钮次数并记录,然后根据Switch还根据按键次数选择灯的个数,在case里用个for循环来让灯闪,然后用定时器写个延时函数来控制灯闪的时间间隔,但是我写不出来,
IMG_20211205_161205.jpg
mmexport1638691943110.png
回复

使用道具 举报

ID:988065 发表于 2021-12-5 16:27 来自手机 | 显示全部楼层
xianfajushi 发表于 2021-12-5 08:09
现在网络提供的代码较多了,可能是为了防止新生不学无术,题目都稍有改动新生就不能即刻复制粘贴了事,老师 ...

我是想在主函数中用while死循环一直扫描按键按钮次数并记录,然后根据Switch还根据按键次数选择灯的个数,在case里用个for循环来让灯闪,然后用定时器写个延时函数来控制灯闪的时间间隔,但是我卡在用定时器写个延时函数了,所以想求助一下其他思路
mmexport1638691943110.png
IMG_20211205_161205.jpg
回复

使用道具 举报

ID:988065 发表于 2021-12-5 16:31 来自手机 | 显示全部楼层
xianfajushi 发表于 2021-12-5 08:09
现在网络提供的代码较多了,可能是为了防止新生不学无术,题目都稍有改动新生就不能即刻复制粘贴了事,老师 ...

我其实没有想复制粘贴,交这个作业还要讲解思路,而且我也是真心想学单片机
回复

使用道具 举报

ID:624769 发表于 2021-12-5 16:35 | 显示全部楼层
莉雅丝 发表于 2021-12-5 16:18
我是想在主函数中用while死循环一直扫描按键按钮次数并记录,然后根据Switch还根据按键次数选择灯的个数 ...

如果,你用delay 延时来让灯闪烁,你的按键,就必须用   中断来检测, 如果按键不想用 中断,那么闪烁就必须用 定时器中断来控时,总之, 要达到你的最终目的, 必须要有 1个以上的中断。
回复

使用道具 举报

ID:624769 发表于 2021-12-5 17:15 | 显示全部楼层
本帖最后由 188610329 于 2021-12-5 20:30 编辑
莉雅丝 发表于 2021-12-5 16:27
我是想在主函数中用while死循环一直扫描按键按钮次数并记录,然后根据Switch还根据按键次数选择灯的个数 ...

代码如下,你要觉得能用就用,不能用就算了。


#include <STC89C5xRC.H>
typedef        unsigned char        u8;

#define Mainforse        24000000L
#define Timer_Base        (65536 - (Mainforse / 12 / 256))
u8        T0_Count,Sec_Count,Mode,Old_Mode,New_Mode;
sbit        Key = P3^2;

void main()
{
        TMOD        = 0x01;
        TL0        = Timer_Base % 256;
        TH0        = Timer_Base / 256;
        TCON        = 0x11;                //启动TR0, INT0 下降沿中断
        IE        = 0x83;  //EA = 1, EX0 =1 , ET0 = 1
        while(1)
        {
                New_Mode = Mode;
                if(New_Mode != Old_Mode)
                {
                        Old_Mode = New_Mode;
                        P1        = 0xff;
                }
                if(Sec_Count > Old_Mode)
                {
                        Sec_Count = Sec_Count - Old_Mode - 1;
                        switch(Old_Mode)
                        {
                                case 0:        P1 ^= 0x01;        break;
                                case 1:        P1 ^= 0x03;        break;
                                case 2:        P1 ^= 0x07;        break;
                                case 3:        P1 ^= 0x0F;        break;
                                case 4:        P1 ^= 0x1F;        break;
                                case 5:        P1 ^= 0x3F;        break;
                                case 6:        P1 ^= 0x7F;        break;
                                case 7:        P1 ^= 0xFF;        break;
                                default:        break;
                        }
                }
        }
}

void INT0(void) interrupt 0
{
        while(!Key);   //等待按键抬起, 如IT0 = 1, 可以不需要
        Mode++;
        Mode &= 0x07;  // Mode 0~7
}

void T0_INT(void) interrupt 1
{
        TL0        = Timer_Base % 256;
        TH0        = Timer_Base / 256;
        T0_Count++;
        if(T0_Count == 0)        Sec_Count++;
}
回复

使用道具 举报

ID:139866 发表于 2021-12-5 17:28 | 显示全部楼层
你这程序按键那个地方就不对,你随便找本书,关于按键地方肯定会写消抖,你这程序里并没有消抖,这会导致数据出错,当然也不是一定会出错,只是出错的几率大大增加。
定时器设置直接抄书就可以,但是最好是自己理解清楚。
延时函数可以去烧录那个软件生成一个。
switch()里面为什么要这么写,先说P1 = 0xFE;之后紧跟一句P1 = 0XFF;你这个写法意思是人眼视觉停留时间比单片机运行一条指令的时间还短吗,你中间不加个延时怎么用眼睛看到IO口变化,
其次,你这for循环也起不到改变间隔的作用,因为你if成立之后程序还要跑完剩下的for,加起来间隔是一样的,
如果按你这个想法倒不如这样写
  1. while(1){
  2.        
  3.         keyscan();
  4.        
  5.         for(uchar i = 0; i < num; i++) {
  6.                 delay_ms(1000);
  7.         }
  8.        
  9.         switch(num){
  10.         case 1:
  11.                 P1 = 0xEE;
  12.                 delay_ms(100);
  13.                 P1 = 0xFF;
  14.         break;
  15. //后面自己写吧
  16.         }
  17. }
复制代码
但是这样写又不需要定时器了,或者说直接设置定时器1s中断,for里面的延时改成这样
  1. void main()
  2. {
  3.         while(1){
  4.                
  5.                 keyscan();
  6.                
  7.                 for(uchar i = 0; i < num; i++) {
  8.                         T0_count = 1;
  9.                         TR0 = 1;                // 启动定时器T0
  10.                         while(T0_count);
  11.                 }
  12.         }
  13. }
  14. void userTimer0(void) interrupt 1 //假设这是1s,我没算
  15. {
  16.     TR0 = 0;
  17.     T0_count = 0;
  18.     TH0 = 0xFC;               
  19.     TL0 = 0X4B;
  20. }

复制代码
在中断里加一个标志位就好。
好像这样不知不觉就写完了,最后,最重要的一点就是,你代码的排版简直烂透了,不要为了图省事就随便写,代码排版好看点对人对己都有好处
回复

使用道具 举报

ID:988065 发表于 2021-12-5 18:35 来自手机 | 显示全部楼层
188610329 发表于 2021-12-5 16:35
如果,你用delay 延时来让灯闪烁,你的按键,就必须用   中断来检测, 如果按键不想用 中断,那么闪烁就 ...

不太懂中断这一块,我能用定时器0写一个延时函数,然后在case里调用吗
回复

使用道具 举报

ID:624769 发表于 2021-12-5 19:36 | 显示全部楼层
本帖最后由 188610329 于 2021-12-6 02:02 编辑
莉雅丝 发表于 2021-12-5 16:27
我是想在主函数中用while死循环一直扫描按键按钮次数并记录,然后根据Switch还根据按键次数选择灯的个数 ...

自己改 Mainforse 值为,你的晶振频率

#include <STC89C5xRC.H>
typedef        unsigned char        u8;
#define Mainforse        24000000L
#define Timer_Base        (65536 - (Mainforse / 12 / 256))
u8        T0_Count,Sec_Count,Mode,Old_Mode,New_Mode;
sbit        Key = P3^2;

void main()
{
        TMOD        = 0x01;
        TL0        = Timer_Base % 256;
        TH0        = Timer_Base / 256;
        TCON        = 0x11;                //INT0 下降沿中断
        IE        = 0x83;
        while(1)
        {
                New_Mode = Mode;
                if(New_Mode != Old_Mode)
                {
                        Old_Mode = New_Mode;
                        P1        = 0xff;
                }
                if(Sec_Count > Old_Mode)
                {
                        Sec_Count = Sec_Count - Old_Mode - 1;
                        switch(Old_Mode)
                        {
                                case 0:        P1 ^= 0x01;        break;
                                case 1:        P1 ^= 0x03;        break;
                                case 2:        P1 ^= 0x07;        break;
                                case 3:        P1 ^= 0x0F;        break;
                                case 4:        P1 ^= 0x1F;        break;
                                case 5:        P1 ^= 0x3F;        break;
                                case 6:        P1 ^= 0x7F;        break;
                                case 7:        P1 ^= 0xFF;        break;
                                default:        break;
                        }
                }
        }
}

void ET0_INT(void) interrupt 0
{
        while(!Key);   //等待按键抬起, 可以不需要
        Mode++;
        Mode &= 0x07;  // Mode 0~7
}

void T0_INT(void) interrupt 1
{
        TL0        = Timer_Base % 256;
        TH0        = Timer_Base / 256;
        T0_Count++;
        if(T0_Count == 0)        Sec_Count++;
}


回复

使用道具 举报

ID:988065 发表于 2021-12-5 21:42 来自手机 | 显示全部楼层
天ノ忆 发表于 2021-12-5 17:28
你这程序按键那个地方就不对,你随便找本书,关于按键地方肯定会写消抖,你这程序里并没有消抖,这会导致数 ...

谢谢指导,帮助很多,刚初学没有注意这个排版,辛苦了,买的板子还没到,用仿真做的,就没写消抖了,以后一定注意排版π_π。
回复

使用道具 举报

ID:332444 发表于 2021-12-6 08:36 | 显示全部楼层
莉雅丝 发表于 2021-12-5 16:31
我其实没有想复制粘贴,交这个作业还要讲解思路,而且我也是真心想学单片机

真想学很好,会有人帮助的,我也不是专门说你,只是一时感慨而已,论坛上真想学的人和只想复制粘贴的人都遇到过,前天就有人向我索要图片中的代码,我问老婆她都不同意直接给代码,不想学的人连图片拿去识别都不做,
我有代码,但回复大部分只给代码图片,为的就是不给直接复制了事,真想学的人要么就参考我的思路写法,要么就自己照着动手打代码,要么就去识别图片,不管别人如何看待我用代码图片回复,只做认为该做的,当然不包括我博客发布的代码可以复制,我学习时基本就是看别人的思路与写法,不需要复制粘贴.
回复

使用道具 举报

ID:548551 发表于 2021-12-6 09:16 | 显示全部楼层
定时器假如1MS? 那么是不是可以在定时中断函数里面给个标志位?比如F_1MS=1? WHILE(1){}里面我们把所有的程序都放在这个if(F_1MS){}里面可以不? 这就是前后台程序架构。 对付8位单片机足矣。
回复

使用道具 举报

ID:332444 发表于 2021-12-6 09:18 | 显示全部楼层
1.gif
回复

使用道具 举报

ID:332444 发表于 2021-12-6 09:25 | 显示全部楼层
2.gif
回复

使用道具 举报

ID:988065 发表于 2021-12-10 16:36 来自手机 | 显示全部楼层
xianfajushi 发表于 2021-12-6 08:36
真想学很好,会有人帮助的,我也不是专门说你,只是一时感慨而已,论坛上真想学的人和只想复制粘贴的人都遇到 ...

您好,我想再请教一个问题,就是关于串口通信串口发送数据的问题,如题,我通过串口接收中断,设置一个中断标志位,然后在主函数根据标志位给计算机回复内容,但是题目要求回复中文,这个点我在网上没找到太多的相关资料,好像是有关计算机内码的内容,但是我找不到学习的教程等,想请您指个学习途径。
IMG_20211210_163036.jpg
回复

使用道具 举报

ID:988065 发表于 2021-12-10 17:11 来自手机 | 显示全部楼层
xianfajushi 发表于 2021-12-6 09:25

我好像找到解决方案了,好像包括一个stdio.h库文件就能用里面的函数输出字符串了,不知道有没有其他方法
回复

使用道具 举报

ID:887371 发表于 2021-12-13 10:21 | 显示全部楼层
本帖最后由 datouyuan 于 2021-12-13 10:57 编辑
莉雅丝 发表于 2021-12-10 17:11
我好像找到解决方案了,好像包括一个stdio.h库文件就能用里面的函数输出字符串了,不知道有没有其他方法

printf("已点亮%bd个灯\n",x);//x为8bit数
用stdio.h的printf函数是最容易做的方法。你只要完成char putchar (char c) 这个函数就行。

void send(char* c);//串口发送字符
void sends(char* str,unsigned char len);//串口发送字符串
假如不用stdio.h,你需要完成上面2个函数。

sends("已点亮",6);
send(x/10+'0');send(x%10+'0');//x范围00~99
sends("个灯\n",5);//一个中文字符算2个字节,ascii算1个字节,所以长度为5
回复

使用道具 举报

ID:883242 发表于 2021-12-13 11:27 | 显示全部楼层
莉雅丝 发表于 2021-12-10 17:11
我好像找到解决方案了,好像包括一个stdio.h库文件就能用里面的函数输出字符串了,不知道有没有其他方法

stdio.h解决不了问题,要重定向putch才可以。
回复

使用道具 举报

ID:401564 发表于 2021-12-13 16:30 | 显示全部楼层
莉雅丝 发表于 2021-12-10 17:11
我好像找到解决方案了,好像包括一个stdio.h库文件就能用里面的函数输出字符串了,不知道有没有其他方法

串口输出字符串用sprintf函数最省事
sprintf可以自动输出负号和小数点,也可以不输出小数点,对于数据处理最省事,至少占用内存什么的,那是单片机累,我不累就行
char   k[10];                                                         //这里不能加code限定符,否则计算不正确,数组长度随意,不出错就可以
        char a;
        sprintf(k,"%.1f",Data_Send);                        //把浮点数据分割成字符串,并存放在k数组中,"%.1f"的2f表示小数点后再显示1位数,不显示小数就是0f
                for(a=0;(k[a]!='\0');a++)
                {
                        Send_Out_COM(k[a]);                                //发送(串口发送函数),发送的是ASCII码,避免出现0x00数据
                }



void Send_Out_COM(unsigned char COM)        //发送一个字节数据(串口发送函数)
{
        while(Busy);                                                //等待上次发送完成,Busy在串口2完成发送后清除       
        Busy=1;
        S2BUF=COM;       
}

回复

使用道具 举报

ID:988065 发表于 2021-12-14 18:45 来自手机 | 显示全部楼层
datouyuan 发表于 2021-12-13 10:21
printf("已点亮%bd个灯\n",x);//x为8bit数
用stdio.h的printf函数是最容易做的方法。你只要完成char put ...

学到了很多,谢谢大佬
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表