简体   繁体   English

PIC16F877A timer1中断时间不符合预期

[英]PIC16F877A timer1 interrupt time is not as expected

Implemented interrupt function on TIMER1 on PIC16F877A MCU on PIC-DIP40 development board.PIC-DIP40开发板上的 PIC16F877A MCU 上的 TIMER1 上实现了中断 function。 Configured the timer Prescaler to 1 and auto preload value to 55536 so that the interrupt time is 0.01s .将定时器预分频器配置为1并将自动预加载值配置为55536 ,以便中断时间为0.01s Using a counter of 100 to count 1s interval.使用100的计数器来计算1s的间隔。 The Fosc is 4Mhz . Fosc 是4Mhz So my calculation is:所以我的计算是:

interrupt time = (4 / Fosc) * (65536 - 55536) = (4/4000000) * (65536 - 55536) = 0.01 s

And used a counter of 100 to generate a 1s interval.并使用100的计数器来生成1s的间隔。 Currently, I have no oscilloscope to test the actual 1s interval so, I am blinking an LED (LED2) on the timer interrupt and another LED (LED1) on the same time interval 1s using __delay_ms(1000);目前,我没有示波器来测试实际的 1s 间隔,所以我在定时器中断上闪烁一个 LED (LED2),在同一时间间隔1s上使用__delay_ms(1000); function. function。

So as expected the two LEDs will blink synchronously (Turn ON and OFF at the same Time).因此,正如预期的那样,两个 LED 将同步闪烁(同时打开和关闭)。 But for some first iterations, they blink synchronously.但是对于某些第一次迭代,它们会同步闪烁。 After some iterations, there is a clear difference in time between their blinking time (Turning ON and OFF time).经过一些迭代后,它们的闪烁时间(打开和关闭时间)之间存在明显的时间差异。 After several minutes the difference is almost 1s.几分钟后,差异几乎是 1 秒。 So the timer interrupt is not working as expected.所以定时器中断没有按预期工作。

So is my calculation wrong for interrupt time or I am missing something in the timer1 configuration?那么我的中断时间计算是错误的还是我在 timer1 配置中遗漏了什么?

The overall goal is to generate a 1s time interval and test the validity without using an oscilloscope.总体目标是在不使用示波器的情况下生成 1s 时间间隔并测试有效性。

Here is my code:这是我的代码:

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#include <xc.h>
#include <pic16f877a.h>
#define _XTAL_FREQ  4000000

#define LED1_ON PORTDbits.RD7 = 0
#define LED1_OFF PORTDbits.RD7 = 1 
#define LED2_ON PORTDbits.RD6 = 0
#define LED2_OFF PORTDbits.RD6 = 1 

#define LED2_TOGGLE PORTDbits.RD6 = ~PORTDbits.RD6

uint16_t preloadValue  = 55536 ; 
uint16_t counter  = 0 ; 
uint16_t secCounter1 = 100 ; 

void io_config() {
    TRISD  &= ~((1 << _PORTD_RD7_POSITION) | (1 << _PORTD_RD6_POSITION)) ; //RD7 and RD6 are output LEDs
}

void timer1_init(){
    TMR1 = preloadValue ; //loading the preload value
    T1CON &= ~((1 << _T1CON_T1CKPS1_POSN) | (1 << _T1CON_T1CKPS0_POSN) | (1 << _T1CON_TMR1CS_POSN)) ; //prescalar is 1 clock is Fosc
    T1CONbits.TMR1ON = 1 ; //timer 1 is ON
    LED2_ON ; 
}

void interrupt_en_configure(){
    INTCON |=  (1 << _INTCON_GIE_POSITION) | (1 << _INTCON_PEIE_POSITION) ; //global and peripheral interrupt on
    PIE1 |= _PIE1_TMR1IE_MASK ;  //timer 1 interrupt enable
    TMR1IF = 0 ; //clearing interupt flag
}

void __interrupt() ISR(){
    if(TMR1IF){
        counter ++  ; 
        if (counter == secCounter1){
            counter = 0 ;
            LED2_TOGGLE ; 
        }
        
        TMR1 = preloadValue ;
        TMR1IF = 0 ; 
    }
}

void main(void) {
    io_config();
    interrupt_en_configure() ; 
    timer1_init() ;
    
    while (1) {
        LED1_ON ; 
        __delay_ms(1000);
        LED1_OFF ;
        __delay_ms(1000);
    }
}

You should not expect them to operate synchronously for the following reasons:由于以下原因,您不应期望它们同步运行:

  • First you do not know how __delay_ms() is implemented or any "promises" of precision it may make - it is certainly not using TIMER1, because you are controlling that.首先,您不知道__delay_ms()是如何实现的,也不知道它可能做出的任何精度“承诺”——它肯定没有使用 TIMER1,因为您正在控制它。 In fact the documentation gives some implementation details, and you really cannot expect precision.事实上文档给出了一些实现细节,你真的不能指望精确。

  • Secondly, even if __delay_ms() were both accurate and synchronous, you are invoking it in a loop with the software overhead of the loop, function call and whatever you are doing to toggle the LED.其次,即使__delay_ms()既准确又同步,您还是在循环中调用它,循环的软件开销、function 调用以及您为切换 LED 所做的任何操作。 That is a few cycles on every iteration that do not affect the interrupt interval which is locked to the hardware, and independent of the software timing.这是每次迭代的几个周期,不会影响锁定到硬件的中断间隔,并且与软件时序无关。

The issue of precision of __delay_ms() is in fact addressed in this Microchip support article where it starts: __delay_ms()的精度问题实际上在这篇 Microchip 支持文章中得到了解决:

If an accurate delay is required, or if there are other tasks that can be performed during the delay, then using a timer to generate an interrupt is the best way to proceed.如果需要精确的延迟,或者在延迟期间有其他任务可以执行,那么使用定时器产生中断是最好的处理方式。

In this case you should trust your code over the library provided delay which is intentionally crude (because it does not use up a valuable H/W timer resource).在这种情况下,您应该信任您的代码,而不是库提供的延迟,这是故意粗略的(因为它不会耗尽宝贵的 H/W 计时器资源)。

__delay_ms() delays by running an empty loop, but it commonly cannot be exact. __delay_ms()通过运行一个空循环来延迟,但它通常不准确。 You would need to look into the actual machine code that is run to calculate the real delay.您需要查看运行的实际机器代码以计算实际延迟。 BTW, this is not rocket science and a great learning task.顺便说一句,这不是火箭科学,也不是一项伟大的学习任务。 (Been there, done that.) (去过也做过。)

Now the rest of your loop (LED switching, looping) adds to this.现在,您的循环(LED 开关、循环)的 rest 添加到此。 Therefore, your pure software driven blinker is not exact.因此,您的纯软件驱动信号灯并不准确。

However, your interrupt driven blinker is not, too.但是,您的中断驱动信号灯也不是。 You reset the timer at the end of the ISR, after several clock cycles have passed.在经过几个时钟周期后,您在 ISR 结束时重置定时器。 You need to take this into account, and don't forget the interrupt latency.您需要考虑到这一点,并且不要忘记中断延迟。 Even worse, depending on the conditional statement, the reset happens at different times after the timer overflow.更糟糕的是,根据条件语句,重置发生在定时器溢出后的不同时间。

Producing exact timing is difficult, especially with such a simple device.产生准确的时间是很困难的,尤其是对于这样一个简单的设备。

The solution is to avoid software at all for the reset of the timer.解决方案是完全避免使用软件来重置定时器。 Please read chapter 8 of the data sheet and use the capture/compare/PWM module to reset the timer on the appropriate value.请阅读数据表第 8 章,并使用捕获/比较/PWM 模块将定时器重置为适当的值。

The worst thing that could still happen is some jitter, just because the ISR might have different latencies.仍然可能发生的最糟糕的事情是一些抖动,只是因为 ISR 可能有不同的延迟。 But the timer runs as exactly as your system's crystal.但是计时器的运行与系统的晶体一样准确。 In average your LED will blink correctly.平均而言,您的 LED 会正确闪烁。

Anyway, if your timing requirements are not that hard, consider to live with some inaccuracy.无论如何,如果您的时间要求不那么严格,请考虑接受一些不准确的情况。 Then use the most simple solution you like best.然后使用您最喜欢的最简单的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM