PDA

View Full Version : Problem with ASM IRQ using FSR0 on PIC18F252



mytekcontrols
- 14th June 2005, 01:36
I have a 6 digit counter routine incorporated as an ASM IRQ on a PIC18F252 under MEL PicBasic Pro. It utilizes in-direct addressing of a 6 byte array to update and store the counter's count. The problem is that the part of the routine that is supposed to clear a digit as it rolls over from 9 doesn't always work. Here is the partial code showing only the UP counting module:



DEFINE INTHAND myint

Ecount var byte[6] bankA
fsave var word bankA system
loopcount var byte

Goto Start ' jump past interrupt routine

;================================================
asm
myint
; save the FSR value because it gets changed below
movf Low FSR0, W
movwf fsave
movf High FSR0, W
movwf fsave+1

;-------------------------------------------------------
;polling and calling routine for counter
;-------------------------------------------------------

; (code not shown)

;-------------------------------------------------------
;6 digit count up module - each entry increments counter
;-------------------------------------------------------

countup
clrf _loopcount

;set base address of array
movlw Low _Ecount
movwf FSR0L
movlw High _Ecount
movwf FSR0H

inc_loop
movlw d'9'
subwf INDF0,W
btfsc STATUS, Z ; Have we hit top for this digit?
goto nextdigit ; Yes, go to next!
incf INDF0,F ; else keep incrementing it, and...
goto restore ; leave!
nextdigit
clrf POSTINC0 ; (clear INDF and increment FSR)
incf _loopcount,F
movlw d'6'
subwf _loopcount,W
btfss STATUS, Z ; Have we checked all 6 digits?
goto inc_loop ; No go to next!

;restore registers
restore
movf fsave, W
movwf Low FSR0
movf fsave+1, W
movwf High FSR0
retfie FAST
endasm
;================================================

Start:
;-------------------------------------------------------
;PBP interrupt init and counter display
;-------------------------------------------------------

; (code not shown)

End

The problem when it manifests is that a given digit will continue to increment beyond the number 9. It is as if the array byte pointed to by the FSR has changed momentarily, thereby missing the check for zero after subtracting 9.

Is code generated by PBP able to interfere with the in-direct addressing? The reason I ask this is, that I originally coded the counter routine without using FSR and instead used Ecount+1, Ecount+2, ect. to access the array. I achieved reliable counting using this method, although at the cost of considerably more code.

The problem is very intermittent with the FSR routine. So this too tends to point to outside influences causing the glitch. Has anyone else encountered such a problem when using SR's and interrupts under PBP?

Darrel Taylor
- 14th June 2005, 06:12
Hey Michael,

This doesn't really explain your problem, but in this section...
; save the FSR value because it gets changed below
movf Low FSR0, W
movwf fsave
movf High FSR0, W
movwf fsave+1

It's really just saving address 00 instead of saving the contents of the FSR0 registers. (FE9 and FEA)

The symbol FSR0 equates to 0x0h. It's for use with LFSR etc.

I believe it should be like this.
; save the FSR value because it gets changed below
movf FSR0L, W
movwf fsave
movf FSR0H, W
movwf fsave+1
It's the same again when trying to restore it.

HTH,
   Darrel

Darrel Taylor
- 14th June 2005, 07:22
Speaking of LFSR ....

This
;set base address of array
movlw Low _Ecount
movwf FSR0L
movlw High _Ecount
movwf FSR0H
Could be this
LFSR FSR0, _Ecount

mytekcontrols
- 14th June 2005, 17:58
Darrel your solution worked great! Thank you for your great wisdom and speedy answers.

The glitch problem was caused by the fact that I am also running a low priority interrupt service routine which is using the same FSR. Neither of the routines was doing a proper context save of the FSR.

Here is the completed 6 digit up/down counter w/prescaler source code incorporating your suggestions (hopefully this is all accurate, since I cut and pasted it together out of several include files). The counter's signal source is a rotary encoder connected to 2 pins on PORTC.



'************************************************
'* Name : mod_ENCDRdisp.BAS
'* Author : Michael St. Pierre
'* Notice : Copyright (c) 2005 Mytek Controls
'* : Free For All To Use
'* Date : 06/14/2005
'* Version : 1.0
'* Notes : MEL PicBasic Pro Rotary Encoder Display Routine
'* : Includes 8-bit Prescaler
'************************************************

DEFINE OSC 40
DEFINE INTHAND myint
' Device = PIC18Fxxx

'================================================
' Equates
'================================================

ENCA var PORTC.x ' use any 2 schmidt trigger inputs with 10K pull-ups
ENCB var PORTC.x
encoder var byte
auxenc var byte
Ecount var byte[6] bankA
prescale var byte
psvalue var byte
direnc var bit
loopcount var byte
periodH con $FE
periodL con $0B
ascii var byte

'Variables for saving state in interrupt handler
fsave var word bankA system ' Saves FSR0

Goto Start ' Jump past interrupt routine

'================================================
' Interrupt Handler (High Priority)
' Rotary Encoder w/precaler and 6 digit up/down counter
'================================================
asm
myint

; save the FSR value because it gets changed below
movf FSR0L, W
movwf fsave
movf FSR0H, W
movwf fsave+1

; Main Interrupt routine begins here

; Timer1 set-up as 20 Khz counter (.00005 sec period)
bcf _TMR1ON ; turn OFF Timer1 for counter load
movlw _periodH
movwf TMR1H ; load high byte of period counter
movlw _periodL
movwf TMR1L ; load low byte of period counter
bcf _TMR1IF ; clear Timer1 IRQ Flag
bsf _TMR1ON ; turn Timer1 back ON

;check rotary encoder inputs (@20 Khz rate)
chk_encoder
clrf _auxenc
btfsc _ENCA
bsf _auxenc,0
btfsc _ENCB
bsf _auxenc,1

movf _auxenc, W
xorwf _encoder, F

rrf _auxenc, F
rlf _encoder, F
rrf _auxenc, F
rlf _encoder, F

btfsc _encoder,2
goto encoder_up
chk_down
btfsc _encoder,3
goto encoder_down
goto restore

encoder_up
btfss _encoder,3
bsf _direnc
btfsc _encoder,3
goto chk_down
goto countup

encoder_down
btfss _encoder,2
bcf _direnc
btfsc _encoder,2
goto restore
goto countdown

;Encoder 6 Digit Counter w/PreScaler - Count Up
countup
;prescaler
movf _psvalue, W ; retrieve prescaler value and...
subwf _prescale, W ; compare to prescale count
btfsc STATUS, Z ; Have we finished prescaling?
goto preupdone ; Yes! Go begin actual count
incf _prescale, F ; Else keep incrementing prescale count and
goto restore ; Leave!
preupdone
clrf _prescale ; Set-up for next prescale event
clrf _loopcount
LFSR FSR0, _Ecount ; set counter base address

inc_loop
movlw d'9'
subwf INDF0, W
btfsc STATUS, Z ; Have we hit top for this digit?
goto nextdigit ; Yes, go to next!
incf INDF0, F ; else keep incrementing it, and...
goto enc_exit ; Leave!
nextdigit
clrf POSTINC0 ; (clear INDF and increment FSR)
incf _loopcount, F
movlw d'6'
subwf _loopcount,W
btfss STATUS, Z ; Have we checked all 6 digits?
goto inc_loop ; No go to next!
goto enc_exit

;Encoder 6 Digit Counter w/PreScaler - Count Down
countdown
;prescaler
movf _prescale, W ; retrieve prescaler value
btfsc STATUS, Z ; Have we finished prescaling?
goto predowndone ; Yes! Go begin actual count
decf _prescale, F ; Else keep decrementing prescale count and
goto restore ; Leave!
predowndone
movf _psvalue, W
movwf _prescale ; Set-up for next prescale event
clrf _loopcount
LFSR FSR0, _Ecount ; set counter base address

dec_loop
movf INDF0, W ; retrieve digit[x]
bnz dec_digit ; Are we at the bottom for this digit?
movlw d'9' ; Yes, roll it over!
movwf POSTINC0 ; (move w = 9 to INDF and increment FSR)
incf _loopcount, F
movlw d'6'
subwf _loopcount, W
btfss STATUS, Z ; Have we checked all 6 digits?
goto dec_loop ; No go to next!
goto enc_exit
dec_digit
decf INDF0, F ; Not at bottom, keep decrementing it, and...
goto enc_exit ; leave!

enc_exit
bsf _AUTOdispNEW ; flag that encoder value has changed

;restore registers
restore
movf fsave, W
movwf FSR0L
movf fsave+1, W
movwf FSR0H
retfie FAST
endasm

'================================================
' Program Start
'================================================
Start:
Clear

' Set-up Interrupts and Timers
TMR1H = periodH ' Pre-load Timer1 High Byte
TMR1L = periodL ' Pre-load Timer1 Low Byte
T1CON = %000001 ' Set Timer1: IntCLK SRC,Pre 1:1, - Timer ON
TMR1IF = 0 ' Clear Timer1 IRQ Overflow Flag
TMR1IE = 1 ' Enable Timer1 IRQ on Overflow
TMR1IP = 1 ' Timer 1 set as high priority interrupt
IPEN = 1 ' Enable priority levels on interrupts
INTCON = %11000000 ' Enable all interrupts!

Pause 100
Gosub reset_enc ' initialize encoder

psvalue = 0 ' initialize prescaler

' psvalue = 0 - no prescaling -
' psvalue = 1 divide by 2
' psvalue = 2 divide by 3
' psvalue = 99 divide by 100

' Main Program Loop Begins Here

mainloop
If AUTOdispNEW = 1 Then Gosub ENCDRdisp

'--------------------------------------------
' your mainloop code goes here
'--------------------------------------------

Goto mainloop

ENCDRdisp:
' display direction indicator
If direnc = 1 Then
ascii = "+" 'direction = UP
Else
ascii = "-" 'direction = down
Endif
Gosub printascii

' display distance
For x = 5 To 0 Step-1
ascii = (Ecount DIG x) + $30
Gosub printascii
Next x
Return

' Reset "ZERO" Encoder Display
reset_enc:
AUTOdispNEW = 1
prescale = 0
For x = 0 To 5
Ecount[x] = 0
Next x
Return

' Print ascii characters
printascii:
' your ascii print/display routine goes here
Return

End

Please excuse the lack of comments on the chk_encoder part of the routine. It was a public domain example that I pasted into my code ( I wish I could remember the author).

One last question:
I implemented the following as you suggested


LFSR FSR0, _Ecount ; set counter base address

Can you explain how it works? What is LFSR?

Thanks,

Darrel Taylor
- 15th June 2005, 00:03
SWEET, I'm glad that worked for you.

Let's see here, LFSR. It stands for "Load File Select Register" or Load FSR.

It takes a 12-bit constant and loads the low byte in FSRxL and the highbyte in FSRxH. It does it in 2 instruction cycles, and doesn't care what bank of memory is selected by BSR at the time.

Using the "old way" movf / movwf you first need to select the proper bank (or use the "a" option to select the access bank) then use 4 instruction cycles to accomplish the same thing.

While your program wasn't large enough to worry about bank switching, sometimes is has to be handled manually.

The 18F's have several new Opcodes that can make life easier. MOVFF is one of the best. For a complete list, see the "Instruction Set Summary" towards the end of the datasheet for the device you are using. I know..., I hate being told to RTFM too, but sometimes ya gotta do it.

regards,
   Darrel

mytekcontrols
- 15th June 2005, 06:00
The 18F's have several new Opcodes that can make life easier. MOVFF is one of the best. For a complete list, see the "Instruction Set Summary" towards the end of the datasheet for the device you are using. I know..., I hate being told to RTFM too, but sometimes ya gotta do it.

Your right, I found the reference in the data sheet (Stupid me I guess I just got a litle lazy). Thanks again for all the help.

jmen
- 4th August 2005, 08:01
I have tried to use the code on a 18f452 - but it dont works... At the beginnig there were many error messages from the Compiler.
Therefore I´ve done the following modifications:
1.changed the rrf instructions to rrcf
2.changed the rlf instructions to rrcf
3.at "enc_exit" I changed
"bsf _AUTOdispNEW ; flag that encoder value has changed"
to
"bsf _AUTOdispNEW,0 ; flag that encoder value has changed"
4. I defined the following Variables:

TMR1ON VAR BIT
TMR1IF Var BIT
TMR1IE VAR BIT
TMR1IP VAR BIT
IPEN VAR BIT

TMR1ON = T1CON.0
TMR1IF = PIR1.0
TMR1IE = PIE1.0
TMR1IP = IPR1.0
IPEN = RCON.7
Now it compiles well, but do not work :-(
To tell the truth I have no expirience with assembler but I have to decode the quadencoder and with PBP I´m loosing counts.

Thanks in advance
Jan

Darrel Taylor
- 4th August 2005, 19:39
Hi Jan,

Those variables aren't working the way you think. For instance:

TMR1ON VAR BIT
TMR1ON = T1CON.0

Creates a separate BIT variable, and then puts the value of T1CON.0 in it. It doesn't assign T1CON.0 the the name TMR1ON.

Try doing it this way.

TMR1ON VAR T1CON.0
TMR1IF Var PIR1.0
TMR1IE VAR PIE1.0
TMR1IP VAR IPR1.0
IPEN VAR RCON.7<br><br>

jmen
- 5th August 2005, 15:57
I´m realy a fool! I changed the wrong variable declaration to aliases as you suggested - now it´s working!!
thanks a lot, you´ve saved my life!!
Another short question...
When i have declared a variable as word - how do I assign a value like 1000 to it or better a value from anoter variable or constant?

In PBP I would do:
<pre>resetValue CON 1000
counter = resetValue</pre>
or:
<pre>resetValue VAR WORD
resetValue = 1000
counter = resetValue</pre>
In ASM: ??
<pre>MOVFF _resetValue, _counter</pre>
You see I´m a real beginner in ASM :-(

regards,
Jan

Darrel Taylor
- 5th August 2005, 18:41
Hey, don't worry about it Jan. I think I made the same mistake in the beginning.

To move 1 word var to another using movff might look like this ...

MOVFF _resetValue, _counter
MOVFF _resetValue+1, _counter+1

but that will only work with 2 variables. movff won't work with constants.

The easiest way to copy things from 1 place to another is using the MOVE?xx macro's. Take a look at this post for more info.

2009

The MOVE?xx macro's will take care of all the bank switching (if required) the same way that PBP does it. <br><br>

mdaweb
- 11th March 2008, 20:48
Hello, please it sends for I the codigo that functions, I am not obtaining success. They thanked