![]() |
![]() |
![]() |
|
|
|||||||
| Micro Controllers Discuss all aspects of micro controllers - building them, coding them, etc. All controllers are welcome - PIC, BASIC, Z8 Encore!, etc. |
|
|
Thread Tools | Display Modes |
|
|
(permalink) |
|
I have put together a simple timer from code I have downloaded. It runs on a timer0 interrupt and displays elapsed time on a 16 X 2 LCD. I am trying to run the code on a 16f88. It will run for a few seconds, zero up, run for a few seconds, zero up and maybe even run for a few minutes and zero up. Eventually it quits some times leaving numbers in the display other time leaving the display blank. This code with the minor necessary changes will run flawlessly for days on a 16f627a. I am setup on a breadboard using only the PIC, a voltage regulator and a LCD. I am using the internal clock on the PIC. I am inclosing the code. Maybe someone will look at the code and tell me what the problem is.
;************************************************* ***************************** ; ZERO-ERROR ONE SECOND TIMER ; (Roman Black 2001, public domain, use it as you like) ; ; ;************************************************* ***************************** ;================================================= ============================= ; processor defined ; LIST p=16F88 ;tell assembler what chip we are using include "P16F88.inc" ;include the defaults for the chip ERRORLEVEL 0, -302 ;suppress bank selection messages ;================================================= ============================= ; MPLAB stuff here LIST b=5, n=97, t=ON, st=OFF ; absolute listing tabs=5, lines=97, trim long lines=ON, symbol table=OFF __CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_IO ;Program Configuration Register 2 __CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF LCD_PORT Equ PORTB LCD_TRIS Equ TRISB LCD_RS Equ 0x04 ;LCD handshake lines LCD_RW Equ 0x06 LCD_E Equ 0x07 ;================================================= ============================= ; Variables here CBLOCK 0x20 sec sec10 min min10 hr hr10 bres_hi ; hi byte of our 24bit variable bres_mid ; mid byte bres_lo ; lo byte ; (we only need 3 bytes for this system) status_temp ; used for interrupt servicing w_temp ; used for interrupt servicing templcd ;temp store for 4 bit mode templcd2 count ;used in looping routines count1 ;used in delay routine counta ;used in delay routine countb ;used in delay routine oldtime ENDC ;================================================= ============================= ; Code here org 0x000 ; Set program memory base at reset vector 0x000 reset goto setup ; set up ints and port stuff org 0x004 ; Interrupt vector, int handler code comes next. ;================================================= ============================= ;************************************************* ***************************** ; INTERRUPT HANDLER (runs this code each timer0 interrupt) ;************************************************* ***************************** ; ;------------------ int_handler ; ;------------------ ;------------------------------------------------- ; first we preserve w and status register 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 tstf bres_mid ; first test for mid==0 skpnz ; nz = no underflow needed decf bres_hi,f ; z, so is underflow, so dec the msb decfsz bres_mid,f ; dec the mid byte (subtract 256) goto int_exit ; nz, so definitely not one second yet. tstf bres_hi ; test hi for zero too skpz ; z = both hi and mid are zero, is one second! goto int_exit ; nz, so not one second yet. movlw 0x0F ; get msb value movwf bres_hi ; load in msb movlw 0x42 ; get mid value movwf bres_mid ; load in mid movlw 0x40 ; lsb value to add addwf bres_lo,f ; add it to the remainder already in lsb skpnc ; nc = no overflow, so mid is still ok incf bres_mid,f ; c, so lsb overflowed, so inc mid ; movlw b'00001000' ; mask for bit 3 ; xorwf PORTB,f ; toggle PORTA,bit3 (toggle the led) movlw sec ; point at sec register movwf FSR newdigit: incf INDF, f ; current digit up one movlw sec ; get difference between sec and FSR subwf FSR, W call sethi ; use to get high limit + 1 subwf INDF, W ; reached that number yet? btfss STATUS, Z ; skip over if yes goto int_exit ; else exit isr clrf INDF ; set current digit to 0 incf FSR, f ; point at next digit goto newdigit ; no, increment the next digit int_exit BCF INTCON,2 ; reset the tmr0 interrupt flag bcf STATUS,Z movf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ; restore pre-isr W register contents retfie ; return from interrupt ;------------------------------------------------------------------------------ ;-------------------------------------------------------------------------; ; High limit + 1 of digits at position W ; ;-------------------------------------------------------------------------; sethi: addwf PCL, f dt H'A',H'6',H'A',H'6',H'A',H'A' ;************************************************* ***************************** ; SETUP (runs this only once at startup) ;************************************************* ***************************** ; ;------------------ setup ; goto label ;------------------ movlw B'01100000' ; 4Mhz = B'01100000' banksel OSCCON movwf OSCCON banksel 0 ;Wait until osc stabilizes lp: btfss OSCCON, 2 goto lp ; OPTION setup movlw b'00001000' ; ; -------x ; ; Note! We set the prescaler to the wdt, so timer0 ; has NO prescaler and will overflow every 256 ; instructions and make an interrupt. ; banksel OPTION_REG ; go proper reg bank movwf OPTION_REG ; load data into OPTION_REG banksel 0 ; back to normal bank 0 ;------------------------------------------------- ; PORTB pins direction setup ; 1=input, 0=output clrf PORTB ; ; movlw b'00000000' ; all 8 portb are outputs ; banksel TRISB ; go proper reg bank movwf TRISB ; send mask to portb banksel 0 ; back to normal reg bank ;------------------------------------------------- ; PORTA pins direction setup ; 1=input, 0=output clrf PORTA ; ; movlw b'00000000' ; all 5 porta are outputs, ; (with 16F84 porta only has lower 5 bits) ; banksel TRISA ; go proper reg bank movwf TRISA ; send mask to porta banksel 0 ; back to normal reg bank ;------------------------------------------------- movlw b'11100000' ; GIE=on TOIE=on (timer0 overflow int) ; bsf INTCON,7 ; bsf INTCON,6 ; bsf INTCON,5 banksel INTCON movwf INTCON banksel 0 clrf sec clrf sec10 clrf min clrf min10 clrf hr clrf hr10 movlw 0x0F ; get msb value movwf bres_hi ; put in hi movlw 0x42 +1 ; get mid value (note we added 1 to it) movwf bres_mid ; put in mid movlw 0x40 ; get lsb value movwf bres_lo ; put in mid ; now setup is complete, we can start execution. ;------------------------------------------------- goto main ; start main program ;------------------------------------------------------------------------------ ;Initialise LCD LCD_Init call Delay100 ;wait for LCD to settle movlw 0x20 ;Set 4 bit mode call LCD_Cmd movlw 0x28 ;Set display shift call LCD_Cmd movlw 0x06 ;Set display character mode call LCD_Cmd movlw 0x0c ;Set display on/off and cursor command call LCD_Cmd ;Set cursor off call LCD_Clr ;clear display retlw 0x00 ; command set routine LCD_Cmd movwf templcd swapf templcd, w ;send upper nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bcf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high movf templcd, w ;send lower nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bcf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high call Delay5 retlw 0x00 LCD_CharD addlw 0x30 ;add 0x30 to convert to ASCII LCD_Char movwf templcd swapf templcd, w ;send upper nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bsf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high movf templcd, w ;send lower nibble andlw 0x0f ;clear upper 4 bits of W movwf LCD_PORT bsf LCD_PORT, LCD_RS ;RS line to 1 call Pulse_e ;Pulse the E line high call Delay5 retlw 0x00 LCD_Line1 movlw 0x80 ;move to 1st row, first column call LCD_Cmd retlw 0x00 LCD_Line2 movlw 0xc0 ;move to 2nd row, first column call LCD_Cmd retlw 0x00 LCD_Line1W addlw 0x80 ;move to 1st row, column W call LCD_Cmd retlw 0x00 LCD_Line2W addlw 0xc0 ;move to 2nd row, column W call LCD_Cmd retlw 0x00 LCD_CurOn movlw 0x0d ;Set display on/off and cursor command call LCD_Cmd retlw 0x00 LCD_CurOff movlw 0x0c ;Set display on/off and cursor command call LCD_Cmd retlw 0x00 LCD_Clr movlw 0x01 ;Clear display call LCD_Cmd retlw 0x00 ;LCD_HEX movwf tmp1 ; swapf tmp1, w ; andlw 0x0f ; call HEX_Table ; call LCD_Char ; movf tmp1, w ; andlw 0x0f ; call HEX_Table ; call LCD_Char ; retlw 0x00 Delay255 movlw 0xff ;delay 255 mS goto d0 Delay100 movlw d'100' ;delay 100mS goto d0 Delay50 movlw d'50' ;delay 50mS goto d0 Delay20 movlw d'20' ;delay 20mS goto d0 Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock) d0 movwf count1 d1 movlw 0xC7 ;delay 1mS movwf counta movlw 0x01 movwf countb Delay_0 decfsz counta, f goto $+2 decfsz countb, f goto Delay_0 decfsz count1 ,f goto d1 retlw 0x00 Pulse_e bsf LCD_PORT, LCD_E nop bcf LCD_PORT, LCD_E retlw 0x00 ;end of LCD routines DispTime MOVLW 0x04 CALL LCD_Line1W movf hr10,w CALL LCD_CharD movf hr,w CALL LCD_CharD MOVLW ":" CALL LCD_Char movf min10,w CALL LCD_CharD movf min,w CALL LCD_CharD MOVLW ":" CALL LCD_Char movf sec10,10 CALL LCD_CharD movf sec,w CALL LCD_CharD RETURN ;************************************************* **************************** ;************************************************* ***************************** ; MAIN (main program loop) ;************************************************* ***************************** ; ;------------------ main ; goto label ;------------------ call LCD_Init main_loop ; movf oldtime, W ; is oldtime the same as sec? subwf sec, W btfsc STATUS, Z ; if not, skip over next instruction goto main_loop ; else continue checking call DispTime ; sec has changed, display the time movf sec, W ; make sec and oldsec the same movwf oldtime ; or calls to the main program pieces. ; The interrupt does all the one second timer stuff. ; goto main_loop ; keep running the main code. ;------------------------------------------------------------------------------ ;================================================= ============================= end ; no code after this point. ;================================================= ============================= |
|
|
|
|
|
|
(permalink) |
|
Can you edit your post and put code tags around your code. You'll find the code tags by clicking the go advanced button.
From your description, it sounds like your circuit is getting randomly reset. Try adding decoupling capacitors. Mike. |
|
|
|
|
|
|
(permalink) |
|
Thanks for your reply Pommie.
The code I posted will compile and run (a little while). I would like for someone to run the code. I will edit and repost if it must be done. I got advise from another forum to add caps at VSS and VDD. I added these caps with no results. I might have installed them wrong. I am attaching a sketch to show their insstallation. Any help will be appreciated. |
|
|
|
|
|
|
(permalink) |
|
"simple" code, eh?
your diagram is completely meaningless, by the way. you don't apply -5V to a PIC. maybe you meant the - ternminal of a battery? sounds like the watchdog timer though you do disable it. do you have a pull-up on /mclr? Last edited by philba; 21st November 2006 at 04:36 PM. |
|
|
|
|
|
|
(permalink) |
|
philba,
I am sorry my diagram was completely meaningless to you. I don't know much about electronics. I was only trying to show a cap connected in parallel to both the positive and negative sides of the power supply. If I am using the wrong terms again please excuse my political incorrectness I don't know any better. I would like to get back to my problem now. Something is resetting the PIC. The Watchdog timer is disable. I don't think it has to do with the power supply because the transition to zero's on the display is too smooth and I have run other applications without problems. I think there is a timer or some setting that I don't know about causing the problem. This device seems to have many more registers that can be set than the 84 or the 628 as partly evidenced by the 2 Config statements. Again, any help will be appreciated. |
|
|
|
|
|
|
(permalink) |
|
no political correctness involved - schematics should be clear and show connections. I'd spend a bit of time studying how other schematics are done and try to follow what they do.
It is possible it is your code but frankly, there was so much and it is unformated, it's not worth dredging through. I would suggest you verify your hardware first. Get an LED with a 470 ohm resistor. Connect it to PortB.0 (resistor to LED, LED to gnd) and try blinking it with a 1 second period. If the blinking is steady, then your hardware is probably OK. |
|
|
|
|
|
|
(permalink) |
|
Subtle bug in interrupt routine, on where the content of w_temp is saved.
At the end of interrupt handling, near the end of restoring context, the STATUS register is restored. The physical effect of this is to switch the current BANK back to the bank where the interrupt occurs. If interrupt occurs when PIC is running codes in BANK1,2 or 3, then the value of w_temp, which is saved originally in BANK0, will not be accessible and will be wrong after restore. Therefore, the w_temp must be saved to register with address starting at 0x70 on 16F88. Try this: Code:
CBLOCK 0x20 sec sec10 min min10 hr hr10 bres_hi ; hi byte of our 24bit variable bres_mid ; mid byte bres_lo ; lo byte ; (we only need 3 bytes for this system) status_temp ; used for interrupt servicing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;w_temp ; used for interrupt servicing templcd ;temp store for 4 bit mode templcd2 count ;used in looping routines count1 ;used in delay routine counta ;used in delay routine countb ;used in delay routine oldtime ENDC CBLOCK 0x70 w_temp ;saving W content during interrupt ENDC
__________________
L.Chung |
|
|
|
|
|
|
(permalink) |
|
Thanks LC
I made the changes you suggested. It did not fix the problem. But it did run a little past 7 mins once. I also installed the cap right that might have helped. I don't know. |
|
|
|
|
|
|
(permalink) |
|
If your display update code attempts to update the display and the registers for it have their contents changed by your interrupt routine, you will have no end of junk on the display...
The temporary registers used for context saving during your interrupts, as LC suggested, must reside in an area of the register file that is accessable to all banks, otherwise unexpected problems may creep in as your program grows in size and function. My head hurts trying to read through the code you have posted, perhaps re-post it as an attached but formatted asm file might help. |
|
|
|
|
|
|
(permalink) |
|
I have cleaned up the code and I will attemp to upload it as a zipped .asm file. Maybe it will be a little easier to follow.
Concerning the interrupt during the display procedure I have never really worried about that because I knew I had 250+- instruction cycles to get it displayed. Maybe these are getting eaten up during the interrupt service and the display routine. I guess I will have to try to count how many instructions are used. Thanks again |
|
|
|
|
|
|
(permalink) |
|
Now I can read it a little clearer, I have spotted what maybe the problem. In your context save code you have,
Code:
movwf w_temp movf STATUS,w movwf status_temp Code:
movwf w_temp swapf STATUS,w movwf status_temp Code:
int_exit BCF INTCON,2 bcf STATUS,Z ; this isn't needed swapf status_temp,w ;<--changed movwf STATUS swapf w_temp,f swapf w_temp,w retfie Mike. |
|
|
|
|
|
|
(permalink) |
|
Mike,
I made the suggested changes. It ran 00:03:44 and zeroed the display. Thanks anyway Steve |
|
|
|
|
|
|
(permalink) |
|
The movf isn't traditional, but will still work, the status register is set based on the result of the opcode execution, so the status register contents are copied into W prior to being altered, although I would use a clrf after storing it in any case to ensure I knew which bank etc I'm starting from within the interrupt routine.
like this.. movwf w_temp movf status,w movwf status_temp clrf status Be careful that you are not violating the correct set-up and hold times for the data and strobe lines for the display module you are using. Also your code is able to respond to interrupts before you have even finished initialising your display module and variables. Set up everything before you allow your clock to run. Your code still doesn't put your vital register contents in an area of the register file that is accessible across all banks, you are only storing the W register in an area accessible from all banks. PCLath, Status, FSR & W plus what ever else you regard as vital should be in your context saving in the interrupt service routine. I'm not saying that you need to store anything more than W at the moment, but it's one of those things that you will forget about later as your program grows, and will lead you into trouble. You are using all those banksels to initialise everything, why not set the bank bits once, set up all your ports and all other options etc, then clr the bank bits to get you back to bank 0 and save yourself some code space for future use. One bit doesn't make sense to me, why split your initialsation code...and use a goto to get to your interrupt service routine ? Something along the lines of this makes much more sense to me personally... org h'00' goto init org h'04' context saving interrupt routine context restoring return from interrupt init: set bank bits initialise all ports, options, prescaler etc etc clr bank bits set/clr variables clr junk on ports initialse lcd module set timer set interrupts active and clr flags loop: test for roll-over, if so then update display if not goto loop and try again Also, you mention 250 +/- instruction cycles, it's actually clock cycles because the timer is driven by the internal clocking scheme, some instructions use two cycles etc, I don't think that's where your problem lies in this case though, actually, now that I have re-read the code you posted, it looks close enough that I would be counting on my fingers just to be sure... Anyway, I hope I'm helping you rather than confusing you further Last edited by tunedwolf; 22nd November 2006 at 04:31 AM. |
|
|
|
|
|
|
(permalink) | |
|
Quote:
Mike. |
||
|
|
|
|
|
(permalink) |
|
IMSlo,
Can you increase the clock speed to 8 meg to see if it is timing related. I expect it to either run correctly (only double speed) or crash consistently. Mike. |
|
|
|
|
| Bookmarks |
| Thread Tools | |
| Display Modes | |
|
|
|
|
||||
| Thread | Thread Starter | Forum | Replies | Latest |
| 16F88 Code protected question | RonnyBone | Micro Controllers | 6 | 30th October 2006 08:30 AM |
| 3.2V to 5V TTL interface problems | GraveYard_Killer | Micro Controllers | 9 | 12th March 2005 01:29 PM |
| 16F88 internal oscillator problems | Jimbob's Ma | Micro Controllers | 1 | 1st March 2004 09:27 PM |
| Channel coding for digital wireless.. | Blueteeth | General Electronics Chat | 0 | 22nd January 2004 10:50 PM |
| Report in problems of 3 phase rectifiers | Ziyad_noel | General Electronics Chat | 0 | 11th October 2003 06:05 PM |