PIC Assembly Followup

So a while ago I posted a thread about looking for help with assembly programming for the PIC16F819, and despite not getting a whole lot of response, I learned a lot of assembly and I’m pretty far along, but now I need some help.

I have, as I’ve said, a PIC16F819 running on the internal 4MHz clock, and yet I can’t seem to get the thing to work timer interrupts properly. Though the code I’m writing is actually a lot more complex than what I’m showing here, this is just the part that I’m obviously making mistakes on. This code should activate timer2 and timer0. Timer2 should go off every .440 milliseconds for a rather complex bit of code that isn’t implemented in this example. Timer0 should go off every 5ms. I have 4 displays so that means that each display should flicker approximately fifty times a second. But, this doesn’t happen. Not even a little bit. It’s almost as bad as 1 display for every half-second.

The setup I gave Timer0 was prescaler 1:32 and offset of 100. I tried fiddling with these values, going so far down as a 1:2 ratio and it was still going too slowly. If anyway can give me a hand, the code is attached (the reason the file is so big is because it includes the PIC16F819 manual).

;**********************************************************************
;																	  *
;	This program is a template for later, more complex programs		  *
;	currently this should only use TIMER0 to display 0, 4, 8, and F	  *
;	in the appropriate displays.									  *
;																	  *
;**********************************************************************
;                                                                     *
;    Filename:	    timerplusdisplaytest.asm                          *
;    Date:          May 1st, 2007                                     *
;                                                                     *
;    Author:        Kit Sczudlo                                       *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Files required:                                                  *
;                    none                                             *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Notes:                                                           *
;           Currently will not properly fire the interrupt.           *
;           Goes at about 1/100 of the expected speed.                *
;                                                                     *
;                                                                     *
;**********************************************************************

	list      p=16F819           ; list directive to define processor
	#include <p16F819.inc>        ; processor specific variable definitions

	errorlevel  -302              ; suppress message 302 from list file


	cblock	h'20'			; Define our variables starting at valid ram
w_temp						; variable used for context saving 
status_temp					; variable used for context saving

curdisp						; Variable that contains the address of the current display routine
disp0						; Variable that has the sevent segment info for disp0
disp1						; Variable that has the sevent segment info for disp1
disp2						; Variable that has the sevent segment info for disp2
disp3						; Variable that has the sevent segment info for disp3

tmr2cnt						; current number of iterations since tmr2 was last reset

temp						; for calculating things in mid-signal retrieval
	endc

;**********************************************************************

	ORG 0						; Reset Vector
		goto    main        	; go to beginning of program
	ORG 4						; Interrupt Vector
		goto isr				; Run an interrupt

	ORG 8						; Interrupt Service routines
isr:
		movwf   w_temp			; save off current W register contents
		movf	STATUS,w		; move status register into W register
		movwf	status_temp		; save off contents of STATUS register
; isr code goes here

		btfsc INTCON,TMR0IF		; Check to make see if this was the timer 0 interrupt
			goto tmr0			; If so, jump up to the timer0 routine
		btfsc PIR1,TMR2IF		; Check to see if the Timer2 flag is set
			goto tmr2			; If this is the timer2 flag, goto timer 2 routine

		goto isrdone			; If we have no $@#$@#$@#$@#ing clue what interrupt it was, end the routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Timer 0 Code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
tmr0:
		bcf INTCON,TMR0IF		; Clear our interrupt request flag to avoid headache
		movlw d'100'			; Get our offset setup for timer0
		movwf TMR0				; And put it into the timer0 count
		clrf PORTA				; Turn off PORTA (no displays on)
		movf curdisp,W			; Grab the address of the next display to jump to
		movwf PCL				; Put that address into the instruction counter
display0:
		movf disp0,W			; grab the current display info for display 0
		movwf PORTB				; Set up PORTB so that our display works
		bsf PORTA,0				; Turn on the display
		movlw #display1			; grab the address of the next display routine
		movwf curdisp			; and put it into the current display
		goto isrdone			; Jump to the end
display1:
		movf disp1,W			; grab the current display info for display 1
		movwf PORTB				; Set up PORTB so that our display works
		bsf PORTA,1				; Turn on the display
		movlw #display2			; grab the address of the next display routine
		movwf curdisp			; and put it into the current display
		goto isrdone			; Jump to the end
display2:
		movf disp2,W			; grab the current display info for display 2
		movwf PORTB				; Set up PORTB so that our display works
		bsf PORTA,2				; Turn on the display
		movlw #display3			; grab the address of the next display routine
		movwf curdisp			; and put it into the current display
		goto isrdone			; Jump to the end
display3:
		movf disp3,W			; grab the current display info for display 3
		movwf PORTB				; Set up PORTB so that our display works
		bsf PORTA,3				; Turn on the display
		movlw #display0			; grab the address of the next display routine
		movwf curdisp			; and put it into the current display
		goto isrdone			; Jump to the end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; End Timer 0 Code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;**********************************************************************************************;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Timer 2 Code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
tmr2:
		bcf PIR1,TMR2IF			; Clear the timer 2 flag to avoid headaches
		incf tmr2cnt,F			; increment the timer2 count
		goto isrdone			; We've finished, so end the routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; End Timer 2 Code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


isrdone:
; end interrupt code section
		movf    status_temp,w	; retrieve copy of STATUS register
		movwf	STATUS			; restore pre-isr STATUS register contents
		swapf   w_temp,f		; To preserve status flags we use 2 swapf instructions instead of a movf
		swapf   w_temp,w		; restore pre-isr W register contents
		retfie					; return from interrupt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

lookup_7seg:				; lookup table for 7 segment display
	addwf PCL,F				; Jump to the value specified in W (add to instruction counter)
	retlw b'01110111'		; Return the correct seven segment setup for '0'
	retlw b'01000010'		; for '1'
	retlw b'00111011'		; for '2'
	retlw b'01101011'		; for '3'
	retlw b'01001110'		; for '4'
	retlw b'01101101'		; for '5'
	retlw b'01111101'		; for '6'
	retlw b'01000111'		; for '7'
	retlw b'01111111'		; for '8'
	retlw b'01001111'		; for '9'
	retlw b'01011111'		; for 'A'
	retlw b'01111100'		; for 'B'
	retlw b'00110101'		; for 'C'
	retlw b'01111010'		; for 'D'
	retlw b'00111101'		; for 'E'
	retlw b'00011101'		; for 'F'

main:
;;;;;;;;;;;;;;;;;;;;;;;; Setup PIC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	BANKSEL PORTA			; move back to the regular bank
	bcf ADCON0,ADON			; turn off the analog to digital converter
	clrf PORTA				; Clear output data latches
	clrf PORTB				; Clear output data latches

	BANKSEL ADCON1			; move to the right bank
	movlw b'00000110'		; Prepare to mess with porta (set all as digital i/o ports)
	iorwf ADCON1,F			; Load the configuration data, but leave everything else alone

	BANKSEL TRISA			; move to the right bank
	clrf TRISA				; make all of porta outputs
	BANKSEL PORTA			; move back to the regular bank

	BANKSEL TRISB			; move to the right bank
	movlw b'10000000'		; Set up W with the correct values for a pin 8 being an input
	movwf TRISB				; make all PORTB pins outputs, except for pin 8

	BANKSEL INTCON			; We need to be in bank 0 for everything else
;;;;;;;;;;;;;;;;;;;;;;;; End PIC Setup ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;  Initialization   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	clrf PORTA			; Set all ports on PORTA 'off'
	clrf PORTB			; Set all ports on PORTB 'off'
	movlw #display0		; get the address for the first display function
	movwf curdisp		; and put it into the current display
	clrw				; Make W = 0
	call lookup_7seg	; And get the correct 7 segment display for W
	movwf disp0			; and put that display info into disp0,
	movlw h'4'			; Put 5 in W
	call lookup_7seg	; And get the correct 7 segment display for W
	movwf disp1			; disp1,
	movlw h'8'			; Put 9 in W
	call lookup_7seg	; And get the correct 7 segment display for W
	movwf disp2			; disp2,
	movlw h'F'			; Put F in W
	call lookup_7seg	; And get the correct 7 segment display for W
	movwf disp3			; and disp3

	bsf T2CON,2			; Enable our interrupt
; Setup our interrupt configuration information
	movlw b'01000000'	; Enable peripheral interrupts
						; But turn off timer0, rb0, and portb port change interrupts
	movwf INTCON		; Store those new values in the interrupt configuration bits
						; and blow out all registered interrupts
	BANKSEL PIE1		; Jump to the right bank
	movlw b'00000010'	; Turn off all peripheral interrupts except Timer2
	movwf PIE1			; And store those values into the peripheral config bits
	BANKSEL PIR1		; Jump back to bank0
	clrf PIR1			; To destory all frivilous interrupt info
	clrf PIR2			; To destory all frivilous interrupt info
; Setup Timer2 to go off ~ every .440 milliseconds (a tiny bit less than half our expected ir signal time)
	movlw d'220'		; Get our PR2 value set up in W
	movwf PR2			; and put it into PR2
	movlw b'00001100'	; bits 6-3: Set Timer2 postscaler to 1:2,
						; bit  2  : and turn it on
						; bits 1-0: prescaler to 1:1,
	movwf T2CON			; Put the configuration bits in Timer2 config
	bcf PIR1,1			; Clear the interrupt bit
; Setup Timer0 to go off every approx. 5 ms.  This provides a good refresh rate for our displays (approximately 28 times a second)
	clrwdt				; Clear the watchdogtimer (should be off, but it never hurts to check)
	BANKSEL OPTION_REG	; We need to edit option_reg to mess with timer0, so get in the right bank
	movlw b'11000000'	; We want to set up timer0 so 
	andwf OPTION_REG,F	; we mask everything off that we want off, and leave bits 6 and 7 alone
	bsf OPTION_REG,PS2	; Set the prescaler to 1:32 on Timer0
	BANKSEL INTCON		; We need to be back in BANK0, so let's get there
	movlw d'100'		; Get our offset setup for timer0
	movwf TMR0			; And put it into the timer0 count
	bsf INTCON,TMR0IE	; Everything else in INTCON was set up earlier, so just turn on TIMER0

	bsf INTCON,GIE		; Now activate the global interrupt info


	goto $				; Make an infinite loop here to double check display status

	END                       ; directive 'end of program'
```<br><br><a class='attachment' href='/uploads/default/original/3X/c/2/c224ef466e53104f15474ad2c3595b3144ac10a4.zip'>testdisplay.zip</a> (1.73 MB)<br><br><br><a class='attachment' href='/uploads/default/original/3X/c/2/c224ef466e53104f15474ad2c3595b3144ac10a4.zip'>testdisplay.zip</a> (1.73 MB)<br>

Kit,

I haven’t taken a good look at your code, but I was wondering how you know that your processor is running at a 4mhz clock speed? Specifically, what are your configuration bits set for and if you are using the internal clock, what is the setting for the oscillator post scaler. In the manual, this would be chapter 12.1 and chapter 4.5.3 (oscillator configuration). It appears that the default bit setup for OSCCON is for a 31.25 khz frequency, not 4mhz. That would make your 5 millisecond interrupt actually more like 640 milliseconds. While I am certainly no expert in assembly language PIC programming, I did play around with it a little last year. Code that I developed usually had a line such as the following at the top of the code to ensure the configuration bits were set correctly

__config	_INTOSCIO & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF

These flags are usually found in the .h file for the specific processor (this is for a PIC 12F683). If this doesn’t help, let me know and I’ll take a closer look at your code.

Mike

Thanks! Sure enough it was that I hadn’t configured the oscillator properly. Though it didn’t have to do with the __config block. If you don’t use the linker then you just have to manually set the config block in your programmer. Anyway, thanks for the tip!

–Kit

Can you see the light blinking if your having it blink 50 times a second? I thought it was just look dimmer.

Also if you need any help with Pic programming e-mail me at [email protected] . I’ve programmed a bunch of Pics in assembly to do a bunch of hard projects.

Kit didn’t describe the hardware in detail, but the code makes it look like there are multiple seven-segment displays, each enabled in turn. The light isn’t simply blinking; it’s moving from one display to the other to make it possible to show several numbers with only one set of outputs.

Yes, that’s exactly what I’m doing. Now I’m trying to use the IR recievers from a few years ago to use the four seven-segment displays to show the information sent by remote controls (the kind you use with your TV). Anyway, the code is being written now. I’m not really too sure I know what the best way to go about it is though, or what protocol to code for. The main apprehension I’m having is that I know the internal oscillator is only accurate to about 15-20% of the calculated frequency, and I’m trying to figure out a good way to base the calculations I’m doing based on probability, rather than accuracy.

Anyway, I’ll post some code later when I have the chance, but here’s a video of the hardware doing it’s thing with some very simple scrolling text:
Thanks Mike!

Kit,

Looks great! Glad I could help. While I haven’t been able to find your reference to the 15-20% oscillator accuracy (I’m sure it’s in the manual somewhere), you’ll probably find that the accuracy is much better than that. I would expect 15-20% to be the absolute maximum. Also, if you have the testing equipment necessary to measure the actual frequency, you can adjust the internal frequency using the OSCTUNE register. A more crude measurement would be set up a timer to blink an led and count the number of times it turns on/off over a set interval (for example, a 1 second blink rate over a 100 second period would give you a good idea if your oscillator is way off - or in your case, a 1 second timer with the number of blinks outputted to your LEDs!)

Let me know how it works out.

Mike