完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
刚起步,学习下!
|
|
|
|
|
|
感谢楼主分享。请问,假如某程序中需用二三十个定时器,怎样通过软件实现这么多的定时器?楼主是否可以提供这样的例程?谢谢。
|
|
|
|
|
|
我不会专门针对某个网友的问题而专门出一篇例程。我写的技术贴主要是以灌输为主,不要奢望我能做太多。我写的例程仅仅是站在我个人的角度把我认为有价值的东西分享出去。你提到的问题其实我已经在第二十六节里面给出了解答,只是你没注意而已。我在第二十六节的定时中断里用了2个定时器,一个定时器用来控制蜂鸣器的声音长短,一个用来控制数码管的闪烁时间。既然我能在1个定时中断里可以实现2个定时器,那么要编写23个定期器还会困难吗?直接参考复制就可以了,要30个都没问题。不过这种定时器的时间精度要校正一下,而且校正了之后,精度也不能要求太高,比如10分钟以内可以控制到2秒左右的误差。至于怎么校正时间精度?你先自己想想办法。等我下一节讲到时间篇的时候,我也会分享我校正时间精度的办法。 |
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 17:25 编辑
d:核心.bmp |
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 17:25 编辑
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 17:26 编辑
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|
|
|
|
|
这几天看了鸿哥的帖子收益匪浅,虽然现在工作不做单片机这快,但决定把单片机当***好来学习,给自己一个目标: 把鸿哥PIC源代码全部移植到C51内核中,每天搞定一节.
这几天比较有空,第一二节已近移植完毕,uart通讯的六七八也移植ok,目前正在看液晶驱动三大定律!以上移植全部亲测可用.通过移植的过程,深刻发现鸿哥的 switch 语句,lock 变量太经典了. 最后真心感谢鸿哥的无私分享!若干年后,鸿哥你定会发现你的分享对中国的单片机业的启蒙作用. 也希望更多的单片机初学者来好好研读&吸收鸿哥的文章,定会有意想不到的收获. |
|
|
|
|
|
|
|
|
|
|
|
谢谢,,大牛的分享
|
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 17:26 编辑
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|
|
|
|
|
本帖最后由 jianhong_wu 于 2013-5-5 16:01 编辑
第二十七节:时间篇--单片机定时中断实现倒计时 (1) 开场白: 在单片机系统中,时间是一个非常重要的功能,而且也是最常用。有很多种实现时间功能的方式,纯粹靠单片机编程或者使用专用的时钟芯片都可以达到目的。鸿哥见多识广,该玩的花样都玩过,大家不用急,接下来我准备花六七个章节专门讲常用的几种实现时间功能的方式。今天这一节先从最简单的开始,利用单片机内部定时中断实现倒计时。 这一节重点要知道怎么样校正时间的精度。第一步,在定时中断里,先凭感觉写入1秒钟大概会需要1160次定时中断。第二步,烧录程序进单片机上电开始测试,手上同步打开手机里的秒表。如果单片机从99倒计时到00时,手机秒表才走了85秒,那么经过校正后的结果是,一秒钟需要的中断次数等于1351=(1160*99)/85。当然,如果想让校正的时间精度更高,可以考虑让手机秒表的时间走更加长一点,比如以手机实际跑了5分钟为基准来校正,当然,如果用这种方法,由于数码管只能显示两位,需要专门编写一小段测试程序来达到实验的目的。 另外,希望利用这个小程序,加深了解吴坚鸿的程序框架,如果读者能够坚持看我写的程序范例,你一定会发现我的程序很有规律,而且很简单清晰。 (2) 功能需求: (a) 利用1个独立按键,作为暂停和启动的按键。按一下就表示启动,再按一下就表示暂停,此按键仅仅在启动和暂停这两种状态之间轮流切换。 (b) 利用另外1个独立按键,作为复位的按键。在任何状态下,按一下复位键,将恢复刚开机上电的状态。 (c) 利用两位数码管,一开机就显示“99”,代表99秒。按下启动按键后,开始倒计时,时间到“00”时,蜂鸣器长鸣一声,就停止。这个时候按一下复位键,时间马上显示“99”,表示复位。此时再按启动键,则重新开始。在运行中,再按暂停键,时间会暂停。在暂停中,按启动键,会继续倒计时。这里的启动键和暂停键是指同一个键。 (3) 硬件原理: (a) 独立按键的电路请参考第二节的内容。 (b) 动态扫描两位数码管的电路请参考第二十一节。 (4)源码适合的单片机: PIC16F73,晶振为3.579545MHz。 (5)源代码讲解如下: #include #define cnt_delay_cnt1 40 //按键去抖动延时阀值 #define cnt_voice_time 150 //按键声音的持续时间 #define cnt_long_voice_time 750 //倒计时结束时长鸣一声的声音的持续时间 #define cnt_1s 1351 //一秒钟需要多少次定时中断。时间的精度校正就是靠修改cnt_1s这个数据。具体方法请看本节开场白的介绍。 #define seg_0_dr RB6 //任意7个IO口接数码管的seg引脚 #define seg_1_dr RB5 #define seg_2_dr RB4 #define seg_3_dr RB3 #define seg_4_dr RC7 #define seg_5_dr RB0 #define seg_6_dr RB1 #define com_left_dr RB2 //任意2个IO口接数码管的com引脚 #define com_right_dr RB7 #define beep_dr RA2 //蜂鸣器输出 #define key_sr1 RC4 //独立按键输入<启动和暂停>键 #define key_sr2 RC5 //独立按键输入<复位>键 void initial();//初始化 void delay1(unsigned int de) ;//小延时程序,时间不宜太长,因为内部没有喂看门狗 void display_drive(); //数码管驱动程序,放在定时中断里 void display_seg(unsigned char seg); //编码转换程序,放在display_drive里 void key_scan(); //按键扫描函数,放在定时中断里,有人说不应该把子程序放在中断里,别听他们,信鸿哥无坎坷。 void key_service(); //按键服务函数,放在main函数循环里 unsigned char count_down_time=99; //正在显示的倒计时的时间,开机默认显示99 unsigned char number_left=0; //左边数码管显示的内容 unsigned char number_right=0; //右边数码管显示的内容 unsigned char dis_step=1; //扫描的步骤 unsigned char key_lock1=0; //按键自锁标志 unsigned char key_lock2=0; //按键自锁标志 unsigned int delay_cnt1=0; //延时计数器的变量 unsigned int delay_cnt2=0; //延时计数器的变量 unsigned int voice_time_cnt; //蜂鸣器响的声音长短的计数延时 unsigned char key_sec=0; //哪个按键被触发 unsigned int interrupt_cnt=0; //1秒钟累加的定时中断次数 unsigned char start_flag=0; //开始与暂停的标志。1表示开始,0表示暂停 main() //主程序 { initial(); //初始化 while(1) { CLRWDT(); //喂单片机内部自带的看门狗,大家可以不管它 key_service(); //按键服务函数 } } void interrupt timer1rbint(void) //中断程序入口 { if(TMR1IE==1&&TMR1IF==1) //定时中断程序 { TMR1IF=0; TMR1ON=0; if(voice_time_cnt) //控制蜂鸣器声音的长短 { beep_dr=1; //蜂鸣器响 --voice_time_cnt; //蜂鸣器响的声音长短的计数延时 } else { asm("nop"); //没别的意思,纯粹为了保持if和else各自括号内的队伍对称。不是鸿哥有"对称控"的洁癖,我只是感觉这样可以起到耗时平衡的小作用。 beep_dr=0; //蜂鸣器停止 } if(start_flag==1&&count_down_time!=0) //开始倒计时的两个条件。开始标志为1并且倒计时还不到0 { ++interrupt_cnt; //1秒钟需要的中断次数累加 if(interrupt_cnt>=cnt_1s) //一秒钟需要多少次定时中断。时间的精度校正就是靠修改cnt_1s这个数据。具体方法请看本节开场白的介绍。 { interrupt_cnt=0; count_down_time--; //倒计时每一秒减去1 if(count_down_time==0) //倒计时时间到 { start_flag=0; //开始的标志清零 voice_time_cnt= cnt_long_voice_time; //蜂鸣器长鸣一声,表示倒计时结束 } } } key_scan(); //按键扫描函数 display_drive(); //数码管驱动程序,放在定时中断里 TMR1H=0xFF; TMR1L=0xC8; TMR1ON=1; } } void initial()//初始化 { ADCON0=0x41; //设置AD模式 ADCON1=0x04; //RA0作为AD输入通道 TRISB=0x00; //数码管IO口设置成输出 TRISC7=0; TRISA2=0; //蜂鸣器输出 TRISC4=1; //按键输入 TRISC5=1; T1CON=0x24; //定时中断配置 TMR1H=0xFE; TMR1L=0xEF; INTCON=0xC0; TMR1IF=0; TMR1IE=1; PEIE=1; //外围中断允许 GIE=1; TMR1ON=1; } void display_drive() //数码管驱动程序,放在定时中断里 { number_left=count_down_time/10; //把倒计时的时间分解出十位 number_right=count_down_time%10; //把倒计时的时间分解出个位 seg_0_dr=0; seg_1_dr=0; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=0; seg_6_dr=0; com_left_dr=1; com_right_dr=1; //在即将更换下一位数码管时,先把让它两个什么都不显示,让显示过度更加平稳 asm("nop"); //空指令延时 asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); switch(dis_step) { case 1: //扫描左边的数码管 display_seg(number_left); //如果不是任意IO口,可以直接用查表的方式取代此子程序 com_left_dr=0; //选中左数码管 com_right_dr=1; break; case 2: //扫描右边的数码管 display_seg(number_right); //如果不是任意IO口,可以直接用查表的方式取代此子程序 com_left_dr=1; com_right_dr=0; //选中右数码管 break; } delay1(50); //每一位数码管显示的停留延时时间,有疑问的朋友请自己尝试改成计数延时的方式, //鸿哥认为在此种环境下,在定时中断里用死延时delay1(50)是最佳的方式 ++dis_step; //下一次中断扫描另外一位的数码管,轮流扫描 if(dis_step>2) { dis_step=1; } } //不是鸿哥不懂爱,如果不是用任意IO口,而是直接用一个并口(比如51单片机中的P1口),那么就不用那么费力, //直接用查数组(俗称查表)的方式就可以替代display_seg这个编码转换程序, void display_seg(unsigned char seg) //编码转换程序,,放在display_drive里 { switch(seg) //switch指令,单片机中的战斗机,鸿哥的最爱! { case 0: //显示"0" seg_0_dr=1; seg_1_dr=1; seg_2_dr=1; seg_3_dr=1; seg_4_dr=0; seg_5_dr=1; seg_6_dr=1; break; case 1: //显示"1" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=0; seg_6_dr=0; break; case 2: //显示"2" seg_0_dr=1; seg_1_dr=0; seg_2_dr=1; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=0; break; case 3: //显示"3" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=0; break; case 4: //显示"4" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=0; seg_4_dr=1; seg_5_dr=0; seg_6_dr=1; break; case 5: //显示"5" seg_0_dr=0; seg_1_dr=1; seg_2_dr=0; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 6: //显示"6" seg_0_dr=0; seg_1_dr=1; seg_2_dr=1; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 7: //显示"7" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=1; seg_6_dr=0; break; case 8: //显示"8" seg_0_dr=1; seg_1_dr=1; seg_2_dr=1; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 9: //显示"9" seg_0_dr=1; seg_1_dr=1; seg_2_dr=0; seg_3_dr=1; seg_4_dr=1; seg_5_dr=1; seg_6_dr=1; break; case 10: //什么也不显示,空 seg_0_dr=0; seg_1_dr=0; seg_2_dr=0; seg_3_dr=0; seg_4_dr=0; seg_5_dr=0; seg_6_dr=0; break; } } void delay1(unsigned int de) { unsigned int t; for(t=0;t void key_scan() //按键扫描函数 { if(key_sr1==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位 { key_lock1=0; //按键自锁标志清零 delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙 } else if(key_lock1==0) //有按键按下,且是第一次被按下 { ++delay_cnt1; //延时计数器 if(delay_cnt1>cnt_delay_cnt1) { delay_cnt1=0; key_lock1=1; //自锁按键置位,避免一直触发 key_sec=1; //触发1号键 } } if(key_sr2==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位 { key_lock2=0; //按键自锁标志清零 delay_cnt2=0; //按键去抖动延时计数器清零,此行非常巧妙 } else if(key_lock2==0) //有按键按下,且是第一次被按下 { ++delay_cnt2; //延时计数器 if(delay_cnt2>cnt_delay_cnt1) { delay_cnt2=0; key_lock2=1; //自锁按键置位,避免一直触发 key_sec=2; //触发2号键 } } } void key_service() //按键服务函数 { switch(key_sec) //按键服务状态切换 { case 1:// 1号键《启动和暂停》键 if(count_down_time!=0) //在倒计时还没等于0时,也就是还没结束时 { if(start_flag==0) //在暂停时就切换到运行 { start_flag=1; //启动 } else //在运行时切换到暂停 { start_flag=0; //暂停 } voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停 } key_sec=0; //相应完按键处理程序之后,把按键选择变量清零, break; case 2:// 2号键《复位》键 start_flag=0; //暂停 interrupt_cnt=0; //中断计数器清零 count_down_time=99; //恢复到99刚上电开机的状态 voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停 key_sec=0; //相应完按键处理程序之后,把按键选择变量清零, break; } } (6)下集预告: 时间篇—在单片机主函数的while(1)循环里实现倒计时。 (未完待续,下节更精彩,不要走开哦) |
|
|
|
|
|
|
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
求解外围电路实现的是4脚给持续低电平复位并正常工作,高电平不工作的原因
2077 浏览 1 评论
3505 浏览 3 评论
PIC1946程序有一个变量在运行过程中恢复初始值其他变量保持不变
2333 浏览 2 评论
2760 浏览 0 评论
PIC16F1825的RC5引脚,在主程序中操作无效,在中断中可以改变是为什么?
4015 浏览 5 评论
967浏览 0评论
用XC8编译PIC18F25K80时提示下面Error,求怎么解决这个问题
6350浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-28 08:26 , Processed in 0.991625 second(s), Total 89, Slave 79 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号