Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

New to 18F.... Memory & Look Up Table Questions

Status
Not open for further replies.
Hello,

I've just got my first 18F series chip - 18F4520 in fact (and it's humungous, think I'll have to get to grips with this SMT business if I'm ever to use it on a pcb). Previously I have only used a 16F690.

I've ported over the 2x16 LCD program from Nigels' tutorial having got it properley working yesterday on my 16f690 but am having a few problems getting it properley functioning on this new chip.

The first hurdle encountered has been with regard to user memory; Is it correct to place my user registers starting at 0x80 with this new chip?

The other problem encounterd is with the use of a look up table. I've now learnt that the 18F program counter increments 'in two's' and this has caused me a headache. So what is the 'standard' way to use a LUT with the 18F chips, are there some tips you can give me?

Thanks in advance for any help.

Code:
;A project to drive a 16 x 2 LCD alphanumeric module (Powertip PC1602 from Rapid Electronics). 11 June 2009
;Most of this code is copied from Nigel Goodwins' tutorial. I've used 8 bit mode rather than 4 bit.
;
;CHANGED THE PIC FROM 16F690 TO 18F4520 AFTER GETTING IT WORKING ON THE '690
;
    list      p=18F4520			
	#include <p18F4520.inc>
	errorlevel  -302            ; suppress message 302 from list file
	errorlevel  -305

	
    CONFIG  OSC = INTIO7, FCMEN = OFF, IESO = OFF, PWRT = OFF, BOREN = OFF, WDT = OFF, MCLRE = ON, LPT1OSC = OFF,            PBADEN = OFF, STVREN = OFF, LVP = OFF, XINST = OFF, DEBUG = ON
        
        
;DATA SECTION		
   UDATA 0x80        ;without this variables won't show in MPLAB Watch window

count       RES 1   
count1      RES 1
counta      RES 1
countb      RES 1

#define     LCD_PORT    PORTD
#define     LCD_RS        PORTB,1
#define     LCD_E          PORTB,0  

;END OF DATA SECTION
    org 0
;____________________________________________________________________________________________________________________ 
    
Start
    clrf    TRISB
    clrf    TRISD
    clrf    PORTB
    clrf    PORTD
 
    clrf    count
    clrf    count1
    clrf    counta
    clrf    countb
    
    movlw   b'01100010' ;4mhz
    movwf   OSCCON
    
;____________________________________________________________________________________________________________________ 
       
Main
    call    Delay100        ;LCD settling time
    call    Initialise_LCD

	clrf	count			;set counter register to zero
	
Message		
        movf	count,w		    ;put counter value in W
        call   Text			    ;get a character from the text table
	xorlw	0x00			    ;is it a zero?
	btfsc	STATUS, Z
	goto	NextMessage
	call	LCD_Char
	call	Delay255
	incf	count
	incf    count
	goto	Message
	
;____________________________________________________________________________________________________________________

NextMessage	
    call	LCD_Line2		;move to 2nd row, first column
	
	clrf	count			;set counter register to zero
Message2
       movf	count, w		;put counter value in W
	call	Text2			;get a character from the text table
	xorlw	0x00			;is it a zero?
	btfsc	STATUS, Z
	goto	EndMessage
	call	LCD_Char
	call    Delay255
	incf	count
	incf    count
	goto	Message2

EndMessage	
		
Stop		
        goto	Stop	    ;endless loop  
;____________________________________________________________________________________________________________________ 
    
Initialise_LCD
    movlw   b'00111100'     ;8 bit mode / 2 lines / 5x11
    call    LCD_CMD
    movlw   b'00011100'     ;display shift / right shift. 
    call    LCD_CMD
    movlw   b'00000110'     ;Character entry mode
    call    LCD_CMD
    movlw   b'00001111'     ;display on / cursor off / cursor position on
    call    LCD_CMD
    call    LCD_Clr
    retlw   .0
;____________________________________________________________________________________________________________________ 
    
LCD_CMD
    movwf   LCD_PORT
    bcf     LCD_RS
    call    Pulse_E
    call    Delay5
    retlw   .0    
;____________________________________________________________________________________________________________________    
    
LCD_Char
	movwf	LCD_PORT
	bsf	    LCD_RS	        ;RS line to 1
	call	Pulse_E			;Pulse the E line high
	call 	Delay5
	retlw	0x00
;____________________________________________________________________________________________________________________

LCD_Line2
    movlw	0xc0			;move to 2nd row, first column
	call	LCD_CMD
	retlw	0x00

LCD_Clr
    movlw	0x01			;Clear display
	call	LCD_CMD
	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_E
        nop
        bcf     LCD_E
        retlw   .0
;____________________________________________________________________________________________________________________  
  
Text  		
        addwf	PCL,f
        nop
		retlw	'H'
		retlw	'e'
		retlw	'l'
		retlw	'l'
		retlw	'o'
		retlw	0x00
		
Text2		
        addwf   PCL,f
        nop
        retlw   'r'
        retlw   'e'
        retlw   'a'
        retlw   'd'
        retlw   'y'
        retlw   '.'
        retlw   '.'
        retlw   '.'
        retlw   0x00 
 end   
 ;__________________________________________________________________________________________________________
 
Greetings Angry Badger,

You have a couple different ways to use tables on 18F' devices. You can still use 'retlw' type tables but as you noted you have to multiply the index in WREG by 2 (which also limits your strings to 128 characters). Please note the use of assembler dt directive in the second listing which creates the individual 'retlw' instructions for you;

Code:
Text
        rlncf   WREG,W          ; WREG = WREG * 2
        addwf   PCL,F           ; effect the jump into table
        retlw   'H'             ;
        retlw   'e'             ;
        retlw   'l'             ;
        retlw   'l'             ;
        retlw   'o'             ;
        retlw   0x00            ;
Code:
Text
        rlncf   WREG,W          ; WREG = WREG * 2
        addwf   PCL,F           ; effect the jump into table
        dt      "Hello\0"       ;
You can also use the 18F' table read mechanism to read tables. One benefit of this method is that you can store characters in each 8-bit byte of each 16-bit memory word. If you have an odd number of characters in the table the assembler will pad the last word in the table with an extra 0 byte.

Code:
PutStr1
        movlw   low(Text)       ; setup TBLPTR registers
        movwf   TBLPTRL         ;
        movlw   high(Text)      ;
        movwf   TBLPTRH         ;
        clrf    TBLPTRU         ; (assume <64KB memory)
lp1     tblrd   *+              ; table read, post increment pointer
        movf    TABLAT,W        ; end-of-table (0)?
        btfsc   STATUS,Z        ; no, skip, else
        return                  ; exit
        rcall   LCD_Char        ; send char to LCD
        bra     lp1             ; loop

Text    dw      "Hello\0"       ;
 
Last edited:
Hi Mike,

Thanks very much for your help. I've used the second code example you provided and it works fine. I have applied the same code to the second, following LUT but there is a problem in that the table causes the PC to go past 255 so the prog' crashes. I can remedy this by moving the tables away from the 'end' of the program but would like to know how to avoid a crash if it were not feasible to relocate the tables. Can you help?

Thanks in advance....:)

Just thinking...is it something to do with looking at the high byte of the PC if the lower byte rolls over?

**broken link removed**
 
Last edited:
Code:
	movlw	high (lookup_start)
	movwf	pclath
	
	movlw	(lookup_end - lookup_start)/2
	subwf 	index, w
	bc		error_handling

	rlncf   	index, w

	addlw        low(lookup_start)             ; index into table
        btfsc 	status, carry                  ; overflow?
        incf           pclath, f                        ; yes, bump PCLATH
	movwf	pcl
lookup_start
	bra		code_for_0			;0
	bra		code_for_1  		;1
	bra		code_for_2          	;2
lookup_end

That is the lookup that I have used for an 18F pic.

The jump to error_handling occurs if the index is too big and the jump would be outside the lookup range.
 
Thanks Diver300,

Thanks for your reply.

I found some code from a pickit2 tutorial and have used it (when combined with some of Mikes' code) - it works fine. Still don't fully understand how it works but will study it tomorrow.

**broken link removed**
 
Hi

Find this code works well for the old style tables - best if you Org the code at a high address.


Code:
	movf	TEST,W
	call	temptble




	org 0x7000
	
temptble movwf	TABWORK			; sets system for 18F lookups - 2 byte calls
	bcf	STATUS,C
	rlcf	TABWORK,F
	movlw	HIGH(table2)
	btfsc	STATUS,C
	incf	WREG,W
	movwf	PCLATH
	movlw	LOW(table2)
	addwf	TABWORK,W
	btfsc	STATUS,C
	incf	PCLATH,F
	movwf	PCL  
					
					 
table2 retlw 0x10	; 00
	retlw	0x11	; 01
	retlw	0x12	; 02
	retlw	0x13	; 03
	ETC
	ETC


Your earlier comment about the RAM memory of the 18F - can be a bit difficult to understand Microchips explanation.

If you simply code the 1 line command, movlb D'1', as show then you will have 128 bytes in Bank 0 and 256 bytes in Bank 1 - all accessible without any further code changes.

Code:
;	Bank0 variables 0x000 to 0x07F

	cblock  0x000
					;  LAST ADDRESS PAGE 0 7F

	endc


;	Bank1 variables 0x100 to 0x1FF

	cblock	0x100		; BANK1			
		
					;  last address entered  FF
	endc				










ramclr	movlb	D'1'			; define bank1, so it and bank 0 (access) 
						; can be addressed
 
 		
 

 		clrf	FSR1H		; FSR routine to clear ram banks 0 -4 only
		clrf	FSR1L		
clrram	        clrf    POSTINC1       ; clear location and inc
 		movlw	0x05		; now in bank5 ?
 		subwf	FSR1H,W
 		bnz	clrram	       ; carry on clearing if not at bank5


hth
 
Angry Badger,

Yes, the 'retlw' type table method requires additional instructions to adjust PCLATH when a string table straddles a 256 byte boundary. This isn't a problem when using the 18F' "table read" method.

Could I recommend a PutString subsystem that uses the 18F' "table read" method which allows you to store string tables in-line with your code?

Code:
;
;  PutStr macro
;
PutStr  macro   string
        call    PutString       ;
        dw      string,0        ; null terminated in-line string table
        endm
Code:
;
;  example program using the macro
;
Main
        call    Delay100        ; LCD settling time
        call    Initialize_LCD  ;
        PutStr  "Hello"         ;
        call    LCD_Line2       ; move to row 2, column 0
        PutStr  "ready..."      ;
Stop
        goto    Stop            ; loop forever
Code:
;
;  example program (without macro)
;
Main
        call    Delay100        ; LCD settling time
        call    Initialize_LCD  ;
        call    PutString       ;
        dw      "Hello",0       ;
        call    LCD_Line2       ; move to row 2, column 0
        call    PutString       ;
        dw      "ready...",0    ;
Stop
        goto    Stop            ; loop forever
Code:
;
;  PutString - print in-line string via Stack and TBLPTR
;
;  string must be terminated with a 0 byte and does not need
;  to be word aligned
;
PutString
        movff   TOSL,TBLPTRL    ; copy return address into TBLPTR
        movff   TOSH,TBLPTRH    ;
        clrf    TBLPTRU         ; assume PIC with < 64-KB
PutNext
        tblrd   *+              ; get in-line string character
        movf    TABLAT,W        ; last character (0)?
        bz      PutExit         ; yes, exit, else
        rcall   LCD_Char        ; print character
        bra     PutNext         ; and do another
PutExit
        btfsc   TBLPTRL,0       ; odd address?
        tblrd   *+              ; yes, make it even (fix PC)
        movf    TBLPTRH,W       ; setup new return address
        movwf   TOSH            ; 
        movf    TBLPTRL,W       ;
        movwf   TOSL            ;
        return                  ; to 1st instruction after the string table
The subsystem looks more complex then it really is. Basically when the "call PutString" instruction is executed the return address on the stack points to the first character of the in-line string table. The subroutine copies that address into the TBLPTR registers and sends each string table character, up to the null terminator, to your LCD_Char subroutine. Then the subroutine sets the return address on the stack to the address of the instruction following the in-line string table and 'returns' there.

Good luck with your project.

Kind regards, Mike
 
Last edited:
Angry Badger,

Just following up. How are things going? Did we help at all?

Mike
 
Last edited:
Hello Mike,

Yes, you all most certaintly did help (don't you always?) :), especially your good self.

I studied the code snippet you supplied, understood it and I'm using it. I have an LM35 temp sensor and I'm working on a little prog to read the output from it, turn the A2D value into BCD and drive the LCD display. Have been working on the BCD routine for the last couple of days - I found the 'left shift and add 3 if 5' method described somewhere so 'transcribed' it, crudely, to asm. I'm happy, it works even if the code is cumbersome. I saw a very compact routine you posted on Piclist but think maybe that's a bit too sophisticated for me at the moment.

Take care.

Code:
B2BCD
    rlncf   No.2Convert,F       ;left shift original number
    btfsc   No.2Convert,0       ;is the shifted out bit a '0' or a '1'?
    goto    LSB_1               ;it's a '1'         

LSB_0
    rlcf    bcd,F               ;shift bcd left, a '0' will be shifted in to bit 0
    goto    Split_byte           
      
LSB_1
    rlcf    bcd,F               ;shift bcd left, a '1' will be 'shifted' in to bit 0
    bsf     bcd,0               
;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 
Split_byte
   
Low_nibble
    movf    bcd,W
    andlw   b'00001111'         ;bits <7:4> all go to '0' <3:0> unchanged
    movwf   bcd_units
    
High_nibble    
    movf    bcd,W
    swapf   bcd,W
    andlw   b'00001111'
    movwf   bcd_tens

    dcfsnz  shift_count         ;count 8 left shifts
    goto    Check_for_200       ;finished shifting     
;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''     
Check_for_5

check_units
    movlw   .5
    subwf   bcd_units,W
    btfss   STATUS,C             ;units =>5?
    goto    check_tens           ;no
units_plus_3                     ;yes
    movlw   .3                          
    addwf   bcd_units,F    
;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''     
check_tens    
    movlw   .5
    subwf   bcd_tens,W
    btfss   STATUS,C             ;tens <=5?
    goto    join_byte    
tens_plus_3   
    movlw   .3
    addwf   bcd_tens,F 
  
join_byte
    swapf   bcd_tens,W
    iorwf   bcd_units,W
    movwf   bcd
    goto    Main 
;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''  
Check_for_200
    movlw    .200   
    subwf   No.2Convert,w
    btfss   STATUS,C            ;=>200?
    goto    Check_for_100       ;no
    bsf     bcd_hundreds,1      ;yes     
    goto    done
  
Check_for_100   
    movlw   .100
    subwf   No.2Convert,W
    btfsc   STATUS,C            ;=>100?
    bsf     bcd_hundreds,0      ;yes
    
done    
    return
 
Sounds like you're havin' fun. Keep us posted...

Regards, Mike

<added>

If you just need to convert an 8-bit binary number to bcd I would use a routine similar to the one below instead of trying to implement that "Double Dabble" algorithm.
Code:
;******************************************************************
;
;  8-bit Binary to 3 digit half-packed BCD (isochronous)
;
;   input: WREG, 0x00..0xFF, 0..255
;  output: tens, 0x00..0x25, packed bcd hundreds and tens
;          ones, 0x00..0x09
;
;  26 words/cycles (isochronous), not including call and return
;
        radix    dec
bin2bcd
        clrf    tens            ;
        addlw   -200            ; W = W - 200
        rlf     tens,F          ; pick up Carry result
        btfss   tens,0          ; borrow? no, skip, else
        addlw   200             ; add 200 back
        addlw   -100            ; subtract 100
        rlf     tens,F          ; pick up Carry result
        btfss   tens,0          ; borrow? no, skip, else
        addlw   100             ; add 100 back
        addlw   -80             ;
        rlf     tens,F          ;
        btfss   tens,0          ;
        addlw   80              ;
        addlw   -40             ;
        rlf     tens,F          ;
        btfss   tens,0          ;
        addlw   40              ;
        addlw   -20             ;
        rlf     tens,F          ;
        btfss   tens,0          ;
        addlw   20              ;
        addlw   -10             ;
        rlf     tens,F          ;
        btfss   tens,0          ;
        addlw   10              ;
        movwf   ones            ;
        return                  ;
 
Last edited:
Angry Badger,

Yes, the 'retlw' type table method requires additional instructions to adjust PCLATH when a string table straddles a 256 byte boundary. This isn't a problem when using the 18F' "table read" method.

Could I recommend a PutString subsystem that uses the 18F' "table read" method which allows you to store string tables in-line with your code?

Code:
;
;  PutStr macro
;
PutStr  macro   string
        call    PutString       ;
        dw      string,0        ; null terminated in-line string table
        endm
Code:
;
;  example program using the macro
;
Main
        call    Delay100        ; LCD settling time
        call    Initialize_LCD  ;
        PutStr  "Hello"         ;
        call    LCD_Line2       ; move to row 2, column 0
        PutStr  "ready..."      ;
Stop
        goto    Stop            ; loop forever
Code:
;
;  example program (without macro)
;
Main
        call    Delay100        ; LCD settling time
        call    Initialize_LCD  ;
        call    PutString       ;
        dw      "Hello",0       ;
        call    LCD_Line2       ; move to row 2, column 0
        call    PutString       ;
        dw      "ready...",0    ;
Stop
        goto    Stop            ; loop forever
Code:
;
;  PutString - print in-line string via Stack and TBLPTR
;
;  string must be terminated with a 0 byte and does not need
;  to be word aligned
;
PutString
        movff   TOSL,TBLPTRL    ; copy return address into TBLPTR
        movff   TOSH,TBLPTRH    ;
        clrf    TBLPTRU         ; assume PIC with < 64-KB
PutNext
        tblrd   *+              ; get in-line string character
        movf    TABLAT,W        ; last character (0)?
        bz      PutExit         ; yes, exit, else
        rcall   LCD_Char        ; print character
        bra     PutNext         ; and do another
PutExit
        btfsc   TBLPTRL,0       ; odd address?
        tblrd   *+              ; yes, make it even (fix PC)
        movf    TBLPTRH,W       ; setup new return address
        movwf   TOSH            ;
        movf    TBLPTRL,W       ;
        movwf   TOSL            ;
        return                  ; to 1st instruction after the string table
The subsystem looks more complex then it really is. Basically when the "call PutString" instruction is executed the return address on the stack points to the first character of the in-line string table. The subroutine copies that address into the TBLPTR registers and sends each string table character, up to the null terminator, to your LCD_Char subroutine. Then the subroutine sets the return address on the stack to the address of the instruction following the in-line string table and 'returns' there.

Good luck with your project.

Kind regards, Mike

Hi Mike, I know this is a very old post but I am working on using a PIC18F and need to send text strings to a LCD, after searching the web I found this post with your "macro" method, I tried running in my code and it seems to be in a loop resetting the PIC and will only reset when writing to the TOSL & TOSH registers... Any ideas? your post is exactly what I'm looking for -Ryan
 
Perhaps you could give us a peek at your macro, subroutine, and calling statements?

Hi Mike, thanks for responding back on such n old post, hope this helps, I never messed with PIC18 TBL's and altered your code to work with rest of my code...

Marco:
PrintLCD macro string
dw string,0 ; null terminated in-line string table
call PutString ;
endm


; PutString - print in-line string via Stack and TBLPTR
;
; string must be terminated with a 0 byte and does not need
; to be word aligned
;
PutString
movff TOSL,TBLPTRL ; copy return address into TBLPTR
movff TOSH,TBLPTRH ;
clrf TBLPTRU ; assume PIC with < 64-KB
PutNext
tblrd *+ ; get in-line string character
movf TABLAT,W ; last character (0)?
bz PutExit ; yes, exit, else
movwf DispChar
call LCD_Chr ; print character
bra PutNext ; and do another
PutExit
btfsc TBLPTRL,0 ; odd address?
tblrd *+ ; yes, make it even (fix PC)
movf TBLPTRH,W ; setup new return address
movwf TOSH ;
movf TBLPTRL,W ;
movwf TOSL ;
return ; to 1st instruction after the string table

When I need to send a message string to LCD I would insert: ( anywhere in code )
PrintLCD "Any message"

as far as I can tell the uC will reset as soon as PutExit routine writes to TOSH & TOSL registers, at first I thought the problem was that I had Stack Full/Underflow Reset enable bit set in config byte(s) so I disabled STVREN with no luck, same thing..... -Ryan

Edit- also you may notice I changed rcall instruction to call because when I use rcall instruction I get build error ( Symbol 'LCD_CHR' out of range of relative branch instruction )
 
Last edited:
Your macro is in the wrong order.
PrintLCD macro string
dw string,0 ; null terminated in-line string table
call PutString ;
endm

Should be,

PrintLCD macro string
call PutString;
dw string,0 ; null terminated in-line string table
endm

Mike
 
Your macro is in the wrong order.
PrintLCD macro string
dw string,0 ; null terminated in-line string table
call PutString ;
endm

Should be,

PrintLCD macro string
call PutString;
dw string,0 ; null terminated in-line string table
endm

Mike
Thank you for reply, yes I know its backwards, I failed to mention that I tried both ways
 
UPDATE: Got it working.... Some reason their is a problem with movff instruction, I changed to movf TOSL,w then movwf TBLPTRL on all three bytes and code now works great, thanks for the macro mike! -Ryan
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top