|
|
|
![]() |
|
|||||||
|
||||||||
![]() |
|
|
Thread Tools | Rate Thread | Display Modes |
|
|
|
#1
|
||||
|
||||
|
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). Code:
;********************************************************************** ; * ; 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' Last edited by kitscuzz : 19-05-2007 at 13:54. |
|
#2
|
|||
|
|||
|
Re: PIC Assembly Followup
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 Last edited by Mike Bortfeldt : 19-05-2007 at 14:56. |
|
#3
|
||||
|
||||
|
Re: PIC Assembly Followup
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 |
|
#4
|
|||||
|
|||||
|
Re: PIC Assembly Followup
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 FirstJerseyKid@comcast.net . I've programmed a bunch of Pics in assembly to do a bunch of hard projects. |
|
#5
|
|||||
|
|||||
|
Re: PIC Assembly Followup
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.
|
|
#6
|
||||
|
||||
|
Re: PIC Assembly Followup
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! Last edited by kitscuzz : 26-05-2007 at 18:03. |
|
#7
|
|||
|
|||
|
Re: PIC Assembly Followup
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 |
![]() |
| Thread Tools | |
| Display Modes | Rate This Thread |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| pic: Mecanum Frame Assembly 2D | Rob2713g | Extra Discussion | 6 | 18-12-2006 13:41 |
| pic: Swerve Assembly | CD47-Bot | Extra Discussion | 3 | 23-01-2004 20:02 |
| Importing assembly into an assembly... | James Green | Inventor | 8 | 22-01-2004 22:17 |