老董 发表于 2024-7-7 16:42 不知道为啥旧帖子被推上来了 问题已经解决很久了 就是8楼wulin 说的原因 8位机要避免同时在中断和主循环运算和比较多于8位的变量 |
引脚设置了 吗?P0M0 P0M1--P7M0 P7M1 |
大神过招,我只能看来看去,啥也看不懂!! |
受教了,还以为是13位的方式0呢! |
sunny118 发表于 2022-1-5 22:23 因为是 自动重装 |
怎么后面的几个程序中定时器中断响应后没有重复初值了? |
188610329 发表于 2021-10-26 17:59 受教了 ![]() |
lkc8210 发表于 2021-10-26 01:14 虽然,你这个修正解决了你目前的问题,但是,这个方案是有缺陷的:如果你程序比较长,定时器定时比较短,会出现掉帧的问题,即:当定时器把 INT0_EN 置1之后,你的主程序还没有来的及判断 INT0_EN 是否为1,你的定时器再次触发,又一次把 INT0_EN 置一, 这个时候,你的主程序 才开始第一次判断,那么就会少减一次(那么频率就会不对)。所以这时候,通常会用传char来避免这样的情况发生。 代码如下: unsigned char INT0_Times,Temp; void timer0_int (void) interrupt 1 { INT0_Times++; } void main() { Timer0Init(); ET0 = 1; EA = 1; while(1) { if(INT0_Times != 0) { Temp = INT0_Times; INT0_Times -= Temp; //防止这个时候中断触发又改写了 INT0_Times 所以不直接 = 0,而用减法 TMR_01_OT -= Temp; if(CY) //有借位则反转LED以及赋新值 { TMR_01_OT +=270; LED1 = !LED1; } TMR_02_OT -= Temp; if(CY) { TMR_02_OT +=273; LED2 = !LED2; } TMR_03_OT -= Temp; if(CY) { TMR_03_OT +=277; LED3 = !LED3; } TMR_04_OT -= Temp; if(CY) { TMR_04_OT +=281; LED4 = !LED4; } } } } 这样,就能避免掉帧的问题,并且如果这次少了1拍,下次会补回来,总的频率不会发生变化,你可以参考一下。 |
npn 发表于 2021-10-25 19:38 有点覆杂 借鉴了你, 188610329总和wulin总的回覆 得出改善的代码如下
|
yzwzfyz 发表于 2021-10-25 17:31 你是不认真看贴子的结果 就算是用了13位定时器也不会出现闪烁不均匀的情况 |
Jiang_YY 发表于 2021-10-25 16:07 不懂如何插入汇编 试了赋值两次,都是重覆那个问题 已找到解决方法 |
188610329 发表于 2021-10-25 13:56 因为一个TMR_XX就加一个传标志 如果有几个TMR_XX就要加几个传标志 借鉴了你的代码 已找到解决方法 |
你遇到的问题和这个程序一样,很多新人都会遇到:
|
都是不认真读手册的结果: 15. TMOD &= 0xF0; //设置定时器模式 在89C52中,这是13位定时器模式(注意不是16位),TH0TL0=FC18,中只有13位起作用,即时常数不是真正的FC18! 在STC中,设计者将定时器做了改进,增加了一个TH0TL0,的影子TH0TL0’,它们共用一个地址,这样做的好处是,可以进行16自动重装,所以在 在STC中,它是16位自动重装模式,时常数是真正的16位:FC18! |
lkc8210 发表于 2021-10-25 11:22 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 你在关闭定时器后清除一下定时器中断标志,然后加2个NOP,看看。 按你10楼回复,你把TMR_XX.Dat_i=270;改成插入汇编,然后先赋值低字节的,然后赋值高字节的, MOV 0x09,#0x0E MOV TMR_XX(0x08),#0x01 或者直接TMR_XX.Dat_i=270;TMR_XX.Dat_i=270;写2次, 看看是什么情况? |
lkc8210 发表于 2021-10-25 11:12 说了,不是赋值的问题,是判断的问题, 你要关定时器的话,得在 if 之前关,然后,整个 if 结束之后开才能彻底杜绝这个问题,而且,在if之前关,因为是15系列,要在ET0=0 之后放一个NOP, 其实,一个简单的传标志搞定的事情,不知道为啥你非要传short…… |
TTQ001 发表于 2021-10-25 08:50 在STC89上可以正常运行 应该不会是代码或逻辑的问题 |
wulin 发表于 2021-10-24 21:16 10楼有新发现但审批迟了 我试过停止定时器再赋值再运行定时器 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 已经确保定时器中断不会打断变量的赋值 也会出现这种情况 停止运行定时器和禁止定时中断在防止打断赋值的功能上是等价的吧? |
188610329 发表于 2021-10-25 00:24 10楼有新发现但审批迟了 我试过停止定时器再赋值再运行定时器 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 已经确保定时器中断不会打断变量的赋值 也会出现这种情况 |
应该是代码的原因。 请仔细检查程序。 |
lkc8210 发表于 2021-10-24 21:38 这跟什么T的没有关系 12T能正常只是运气好而已,程序本身就是错误的 |
lkc8210 发表于 2021-10-24 21:38 不是 1T 的问题, 12T也会有这个问题,只是相比1T不容易出现(或者说不容易显现出来)。而且你原程序,不是那么简单的几句吧?应该有更多的内容,这次是为了测试才变那么“迷你”的吧?这是好事,以后程序复杂了,你没有注意这点的话,发生“诡异”事件,反而更麻烦。 其实道理很简单,中断和主程序 或者 高级中断和低级中断之间传参的话,8位机(51属于8位机)必须避免16位传参,因为不可必免会发生这样的事情(程序越简单发生概率越高):主程序在处理16位的变量,处理了其中的8位,这个时候中断打断,改写了整个16位,这个时候返回主程序,主程序继续处理剩下没处理的8位(这8位已经是新的数据了),那么这个结果会错的离谱。 所以,一旦有16位甚至32位的数据在中断里累加累减计数,达到你预期值的时候,必须转换成一个标志(bit),然后让主程序按这个标志去执行。 |
本帖最后由 lkc8210 于 2021-10-24 23:01 编辑 改用联合体又正常了
编译出来发现if(!TMR_XX_OT)是用ORL
if(!TMR_XX.Dat_c[0] && !TMR_XX.Dat_c[1]) 是用两个JNZ
是ORL有问题吗? |
188610329 发表于 2021-10-24 21:08 改了后可以是可以,但是为什么1T会这样,12T就正常? |
从逻辑上看程序没有问题,但同一个16位变量在主函数和中断中都可以操作容易出错,有前辈就此问题详细阐述过。择录如下: /* 注释二: * ET0=0;uiTimeCnt=0;ET0=1;----在清零 uiTimeCnt 之前,为什么要先禁止定时中断? * 因为 uiTimeCnt 是 unsigned int 类型,本质上是由两个字节组成。 * 在 C 语言中 uiTimeCnt=0 看似一条指令,实际上经过编译之后它不只一条汇编指令。 * 由于定时中断函数里也对这个变量进行累加操作,如果不禁止定时中断, * 那么 uiTimeCnt 这个变量在 main()函数中还没被完全清零的时候,如果这个时候 * 突然来一个定时中断,并且在中断里又更改了此变量,这种情况在某些要求高的 * 项目上会是一个不容易察觉的漏洞,为项目带来隐患。当然,大部分的普通项目, * 都可以不用那么严格,可以不用禁止定时中断。在这里只是提醒各位初学者有这种情况。 */ 建议改写为: void timer0_int (void) interrupt 1 { TL0 = 0x18; //设置定时初始值 TH0 = 0xFC; //设置定时初始值 if(--TMR_XX_OT==0) { TMR_XX_OT=270; flag=1; } } void main() { Timer0Init(); IE = 0; ET0 = 1; EA = 1; while(1) { if(flag) { flag=0; LED = !LED; } } } |
你按我写的改了之后,不就知道了? |
188610329 发表于 2021-10-24 19:25 为什么? |
lkc8210 发表于 2021-10-24 19:06 你的问题不是 定时器赋值,而是那个TMR_XX_OT 的判断,必须放到定时器里判断是否为0,不能放在外面判断。 |
npn 发表于 2021-10-24 18:15 不太可能 我试过停止定时器再赋值再运行定时器都会出现这种情况 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 反而在while(1)底加上4个_nop_()就可以正常运作 是因为1T单片机太快了吗? |
TMOD = 0x00; //16位自动重载模式 bit T_OT_Flag; //增加一个标志位 void timer0_int (void) interrupt 1 { // TL0 = 0x18; //设置定时初始值 // TH0 = 0xFC; //设置定时初始值 自动重载不需要设置 if(--TMR_XX_OT==0) { TMR_XX_OT = 270; T_OT_Flag = 1; } } while 里面这么写: while(1) { if(T_OT_Flag) { T_OT_Flag = 0; LED = !LED; } } 然后试试看。 |
8位单片机向16位变量赋值,是分成两条指令完成的。 先赋值高8位,此时中断触发改变了该变量的值。 中断返回后再去赋值低8位,导致程序出错。 |