2
\$\begingroup\$

I have designed an assembler program for PIC 12F675, but it gives wrong timing. Datasheet says that internal oscillator is 4 MHz, and its instruction cycle is 4 clocks. Thus single instruction executes 1 us. Thus for timer 0, looping from 0 to FF with prescaler 1:256 I expect to get 131 ms per period (2 times with output value inverted), and for timer 1 and loaded counter value of B8FB I expect time of 18.18 ms.

I get:

TMR0 -> 108 ms (instead of 131 ms)

TMR1 -> 30 ms (instead of 18 ms).

What do I do wrong?

Code is below. It does simple things: for TMR0 it just checks for overflow bit and toggles LED state, for TMR1 it checks for overflow, and if it did, it toggles LED and resets counter back to B8FB.

CONF1   equ _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF
CONF2   equ _MCLRE_OFF & _BODEN_OFF & _CP_OFF & _CPD_OFF
__CONFIG    CONF1 & CONF2


START
    ; in bank 0
    bcf     STATUS,RP0  ; bank 0
    movlw   02h
    movwf   GPIO        ; init GPIO
    movlw   07h         ; comparator off: set GP<2:0> to
    movwf   CMCON       ; digital IO
    clrwdt              ; clear watchdog
    clrf    TMR0        ; clear timer 0 value (mut be performed before
                        ; writing option register
    ; in bank 1
    bsf     STATUS,RP0  ; bank 1
    clrf    PIE1        ; disable peripheral interrupts
    movlw   07h         ; pull-ups enabled,
                        ; GP2/INT pin interrupt on falling edge,
                        ; TMR0 is clocked by internal oscillator,
                        ; prescaler for TMR0 is 1:256
    movwf   OPTION_REG
    clrf    ANSEL       ; all pins are digital I/O
    movlw   02ch        ; (1=input, 0=output)
    movwf   TRISIO      ; and set GP0/GP1/GP4 as outputs,
                        ; GP2/GP3/GP5 as input
;    movlw   034h       ; (1=enabled, 0=disabled)
    movwf   WPU         ; set weak pull-ups on all input pins

    ; initialize TMR1FLG value
    bcf     STATUS,RP0  ; change back to bank 0
    movlw   01h
    movwf   TMR1FLG     ; starting as NTSC mode until first interrupt

    ; finished configuring, now enabling interrupts
;    movlw   090h       ; interrupts enabled from INT pin,
;               ; TMR0 interrupts are disabled
;    movwf   INTCON     ; set interrupts reg

; test code

    ; start the timer, 18181 = 04705h, and -04705h=0b8fbh (timer counts up)
    clrf    T1CON       ; stop timer to prevent contention
    movlw   0b8h
    movwf   TMR1H
    movlw   0fbh
    movwf   TMR1L       ; set up TMR1 counter to 0b8fbh
    bcf     PIR1,TMR1IF ; clear timer 1 interrupt flag
    incf    T1CON,F     ; start timer with 4MHz/4 (T1CON is 01h now)

    ; clearing interrupt
    bcf     INTCON,INTF


test_loop
    btfss   INTCON,T0IF ; Timer 0 overflew?
    goto    no_tmr0     ; no, continue to main loop

    bcf     INTCON,T0IF ; reset timer 0 flag
    movf    W_TEMP,W
    xorlw   02h
    movwf   W_TEMP
    movwf   GPIO        ; alternate green LED

no_tmr0
    btfss   PIR1,TMR1IF
    goto    test_loop

    ; TMR1 overflew
    clrf    T1CON       ; stop timer to prevent contention
    movlw   0b8h
    movwf   TMR1H
    movlw   0fbh
    movwf   TMR1L       ; set up TMR1 counter to 0b8fbh
    bcf     PIR1,TMR1IF ; clear timer 1 interrupt flag
    incf    T1CON,F     ; start timer with 4MHz/4 (T1CON is 01h now)

    ; clearing interrupt
    bcf     INTCON,INTF

    movf    W_TEMP,W
    xorlw   010h
    movwf   W_TEMP
    movwf   GPIO        ; alternate red LED
    goto    test_loop
\$\endgroup\$
  • 1
    \$\begingroup\$ DS41190C-page 70 of the data sheet show nearly all of the RISC processor instructions are 1 cycle. There are a few exceptions, five are 2 cycles and 4 are 1 or 2 cycles depending on use. ww1.microchip.com/downloads/en/devicedoc/41190c.pdf \$\endgroup\$ – Wendall Jun 5 at 21:06
  • \$\begingroup\$ It must not matter how many instruction cycles specific instruction takes. Datasheet says "In Timer mode, the Timer0 module will increment every instruction cycle". Instruction takes two instruction cycles? Then timer must increment by 2. Of course there will be small slack between interrupt flag set and program checking it, but it is a matter not microseconds, not tens of milliseconds. In overall, if timer is incremented in variable time steps then it has no right to be called TIMER, it is just counter of something. \$\endgroup\$ – Anonymous Jun 5 at 21:12
  • \$\begingroup\$ Hmm, I have written many assembly language programs for PIC, Atmel, NEC and other micro controllers. Once you understand the timers and interrupts and how to set them up they are very, very accurate. I prefer assembly. \$\endgroup\$ – Wendall Jun 5 at 21:21
  • \$\begingroup\$ I used timers in the past with PIC16 and PIC18, had no problem. Here I designed the whole application (using interrupts) to find out that it does not work properly. Digging deeper revealed that my timing calculations are not followed by the microcontroller. And the error is not a microsecond, and not even millisecond (which I can tolerate out of 18 ms), but error is 100%! \$\endgroup\$ – Anonymous Jun 5 at 21:27
  • \$\begingroup\$ Interrupts are enabled, but I don't see interrupt service routine @ location 0x0004 \$\endgroup\$ – glen_geek Jun 5 at 22:47
2
\$\begingroup\$

I agree with your calculations and your code looks right. There may just be a couple of oversights resulting in your observations:

  1. Timer1's red LED is alternated on overflow, so like TMR0, its period should be twice the overflow time of the timer: 2*18.18ms = 36.36ms.
  2. That means the actual timings are both 17% faster than calculated, suggesting common cause.
  3. The internal oscillator has a stated tolerance of 1%, though over temperature it can be 2% faster. That doesn't explain it.
  4. But the oscillator does have configuration and calibration registers which could explain the discrepancy. In your preamble you specify the CONFIG setting of _INTRC_OSC_NOCLKOUT but is that sufficient to set it? I suggest changing to the INTOSC with CLKOUT, and measuring the CLKOUT pin. If it is not 4MHz as desired, look at adjusting the OSCCAL register. The Microchip document DS33023A may help here.
\$\endgroup\$
  • \$\begingroup\$ Changed config to CLKOUT, and see 1.22 MHz on GPIO4 :( Will dig further. I am afraid I corrupt BG bits, but it is not clear what exactly do they affect. Also, programmer says "OSCCAL: 341C", not sure what that means and where this value came from. \$\endgroup\$ – Anonymous Jun 5 at 21:51
  • 2
    \$\begingroup\$ Relevant info is on page 2-16 of DS33023A. To load the calibration value you must do call 0x3ff followed by movwf OSCCAL in your startup code (in bank 1). \$\endgroup\$ – Bruce Abbott Jun 5 at 21:55
  • \$\begingroup\$ It seems have improved the situation: now it outputs 132.93 ms instead of 131.07 ms, and 36.91 ms instead of 36.36 ms. Well 1 ms is 1000 us, equal to 1000 instruction cycles, and this amount is not there for sure. Thus clock is still not much accurate. Is it normal? I used hair dryer to heat the chips, and frequency increased just a little, I do not see much drift. \$\endgroup\$ – Anonymous Jun 5 at 22:52
  • 1
    \$\begingroup\$ Ok, I think it is the best I can get out of this chip. I have three: one has calibration byte 1C, another 28, and last one 24. \$\endgroup\$ – Anonymous Jun 5 at 23:10
  • \$\begingroup\$ 1.5% error sounds okay, if not great, for an internal oscillator. To get better than that I think you'd have to move to an external crystal. \$\endgroup\$ – Heath Raftery Jun 6 at 6:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.