PIC Microcontroller Memory Methods: Tables

Conditionally replaceing one value with another

Big Table Lookups (example for sound playback)

Compressing large tables with linear interpolation by Peter Hemsley "

SIMM interface

Ken Websters PIC16C74 serial port multiplexer. This includes a complete SRAM interface and memory test code.

Table lookups

This article discusses reading constant values from program memory (Flash).

See "File Select Register" fsr.htm and "Moving data blocks" memmove.htm for reading and writing values to RAM.

See "Electrically Eraseable Programmable Read Only Memory" for reading and writing values to EEPROM.

On SX chips, you can access program memory via the "IREAD" instruction. See SX tables /techref/scenix/lib/mem/tables_sx.htm .

Douglas Wood [dbwood at kc.rr.com] says:

On the 17Cxxx and 18Cxxx PICs, you can access program memory via the 'table read' instructions.

On the 16Fxxx PICs, you can access program memory via a set of SFR (special function registers).

On all other PICs, you'll have to use a 'computed GOTO/RETW-style' table.

Drew Vassallo says:

More specifically, typically you access data tables through a call and modification of a program counter. For Microchip's MPLAB or MPASM, the instruction for "data" that is used is "dt" followed by your data, which will automatically be assembled into a data table, one line for each byte of data.
;;Call with byte to retrieve in W (in this case, W in range of 0-3)
SimpleTable
      addwf PCL
dt 0x01, 0x02, 0x03, 0x04

The dts are actually assembled as RETW #'s one after another. But watch out for one little "gottcha": Any instruction, other than a jump, that modifies the PCL register, also clears the 9th bit (bit 8). So addwf PCL only works if the target is in the first half of any 512 address page. See paging

Mike Keitz says:

...There are two situations where "paging" of the program memory is an issue. For CALL or GOTO, the pages are 800h instructions long [on the more advanced PIC processors]. Most short programs don't need to be concerned with them. [HOWEVER] For writing to PCL, the pages are only 100h instructions long. So [any] 256-value table definitely crosses one of those pages. And the program will crash when the index into the table gets large enough if your table-access code doesn't set up PCLATH properly.

[Here is some code that sets up PCLATH correctly]

   movlw  high   (TABLE_START)
   movwf  pclath
   movf   index,w
   addlw  TABLE_START
   skpnc
    incf  pclath,f
   movwf  pcl
TABLE_START
   retlw  ...

If the table is called such that W is the index:

   addlw  TABLE_START
   movwf  temp
   rlf    known_zero,w
   addlw  high(TABLE_START)
   movwf  pclath
   movf   temp,w
   movwf  pcl
TABLE_START
   retlw ...

Andrew Warren of Fast Forward Engineering - San Diego, California http://www.geocities.com/SiliconValley/2499 says:

...write table-lookup code [with] automatic page-crossing checks built in.

I generally do it like this:

        ADDWF   PCL

    TABLE1:

        DT      "Test"

        IF ((HIGH ($)) != (HIGH (TABLE1)))
            ERROR "TABLE1 CROSSES PAGE BOUNDARY!"
        ENDIF

P.S. By the way, another common way that a newly-inserted CALL can screw up previously-working code is if the called routine changes the PCLATH register... Or if your previosuly-working code expected the W register or STATUS flags to remain unchanged across the portion of the program where you inserted your CALL.

Rich Leggitt says:

...something like this should work for arbitrary table of any length located anywhere in the program space.
                ...
                movlw high string       ; point to a string
                movwf look_hi           ; in reality, a macro...
                movlw low string
                movwf look_lo
                call process            ; go process it
                ...

; subroutine to process string at look_hi/look_lo
process         call lookup             ; get a byte (this is the magic)

                ; here, do something with byte in W
                ; also, return if end of string
                ; otherwise...

                goto process            ; do it again

; Jump to address in look_hi/look_lo, which presumably is an RETLW.
; Note pointer post increment.
; Equivalent to: W=*look_ptr++
lookup          movf look_hi,w          ; set PCLATH
                movwf PCLATH
                movf look_lo,w          ; and get PCL
                incf look_lo,f          ; but post inc
                skpnz
                incf look_hi,f
                movwf PCL               ; ok, now jump


Germain Morbe [GMorbe at CDSGMBH.COM] says:

...while still working on my cipher routine i need to inline read a short table without using ram. Here is what i found. Could be of interrest to some. Any shorter forms known?
Enter with pointer in w
Exit with Value1 to Value4 in w
...
addwf    pcl,f
xorlw    ( 0 xor VALUE1 xor VALUE2 xor 1 )
xorlw    ( 1 xor VALUE2 xor VALUE3 xor 2 )
xorlw    ( 2 xor VALUE3 xor VALUE4 xor 3 )
xorlw    ( 3 xor VALUE4 )
...


Rubenflc@yahoo asks:

Hello, I,m Rubén. I,m doing a project which I have to use a PIC16F873 and I,ve got a problem, I would like you tell me the bits of the PCLATH and PCL register. I,m doing the main program in page0 and where there is a table, but have to take a second table from page1 to compare and either of them has 128 positions.

Thanks

Dear Rubén, your email bounced.

Dear Ruben,

I think you will enjoy reading Table lookups http://www.piclist.com/techref/microchip/tables.htm . Once you figure it out, it's pretty simple. Please tell me what you find confusing. Can you write a simpler explanation ?

Have you seen page http://www.piclist.com/techref/piclist/begin.htm ?

Have you seen the example program on page http://www.dontronics.com/psbpix/lookdown.html ?

I use the fcall macro. Then I don't have to ever deal with PCLATH. Perhaps something like this:

    ; (warning: untested code)

;(macros go here)

; the fcall macro
; by Roger Froud of Amytech Ltd.
fcall	macro subroutine_name
	local here
	lcall subroutine_name ; set PCLATH correctly
	pagesel here ; set PCLATH correctly
here:
	endm


    ;(initialization code goes here)

    ;...

main_loop:
    ;...
    movlw 3
    fcall string_table
    movwf temp1
    ;...
    movlw 4
    fcall offset_table
    movwf temp2
    ;...
    goto main_loop

;...
;(subroutines go here)
;...
; doesn't matter if this is in page 0 or 1, as long as it's entirely within a 0x100 boundary
string_table: ADDWF pcl
             RETLW 'D'
             RETLW 'E'
             RETLW 'F'
             RETLW 'G'
        IF ((HIGH ($)) != (HIGH (string_table+1)))
            ERROR "string_table CROSSES PAGE BOUNDARY!"
        ENDIF


;...
; doesn't matter if this is in page 0 or 1, as long as it's entirely within a 0x100 boundary
offset_table: ADDWF pcl
             RETLW -3
             RETLW -3
             RETLW -2
             RETLW -1
             RETLW -0
             RETLW -0
             RETLW 1
        IF ((HIGH ($)) != (HIGH (offset_table+1)))
            ERROR "offset_table CROSSES PAGE BOUNDARY!"
        ENDIF


;...

Does that help ?

Some people prefer the RETLW data method because it works in all Microchip PIC processors.

Alternatively, you could use EEDATA and related registers instead of the RETLW instruction to read a table of data in FLASH.

Some people prefer the EEDATA method because it allows them to pack more information into each FLASH location (14 bits rather than 8 bits), although it only works with the PIC16F87X processors

The data sheet for the PIC16F87XA from http://www.microchip.com/1000/pline/picmicro/ describes this alternative method in section "3.5 Reading FLASH Program Memory"

----
	; EXAMPLE 3-3: FLASH PROGRAM READ
	banksel MS_PROG_EE_ADDR
	MOVLW MS_PROG_EE_ADDR ;
	MOVWF EEADRH ; MS Byte of Program Address to read
	MOVLW LS_PROG_EE_ADDR ;
	MOVWF EEADR ; LS Byte of Program Address to read
	banksel EECON1
	BSF EECON1, EEPGD ; Point to PROGRAM memory
	BSF EECON1, RD ; EE Read
	NOP ; Any instructions here are ignored as program ...
	NOP ; ... memory is read in second cycle after BSF EECON1,RD
	banksel EEDATA
	MOVF EEDATA, W ; W = LS Byte of Program EEDATA
	MOVWF DATAL ;
	MOVF EEDATH, W ; W = MS Byte of Program EEDATA
	MOVWF DATAH ;
----

More information than you need about the interaction between PCLATH and PC is at http://www.piclist.com/techref/microchip/pages.htm which lists the fcall macro. -- David Cary


See Also:

David A Cary of Motorguide Pinpoint Says:

Bob Ammerman of Ram Systems says:
Here is a trick I use to avoid problems with PCLATH/PCLATU and interrupts on 18C chips:

given:

rcall    lookup_func

instead of:

;align to page start if needed
lookup_func:
addwf    PCL,F,A
retlw      1
retlw      2

I have used:

;align to page start if needed
lookup_func:
rcall    lookup_index
retlw    1
retlw    2

lookup_index:
addwf    TOSL,F,A
return

; For large tables you can:

lookup_index:
addwf    TOSL,F,A
btfsc      STATUS,C
incf        TOSH,F,A
return

In the program where I use this, I consider PCLATH/PCLATU to be resources usable _only_ in my interrupt handler. I use the above trick for code at task level. This seems to work quite well. It saves me the requirement of saving PCLATH in my interrupt handler (which is _very_ pressed for time).

I have _not_ seen the pattern of MOVFF somehow being interrupted in the middle. Are you saying that the instruction is basically skipped when this happens (ie: it doesn't execute before entering the interrupt and it doesn't execute on return from the interrupt). I'd love more particulars here if possible.

Also, in regard to the LFSR instruction. As far as I know, all silicon currently available is broken. Just say _no_ to the instruction. It is a shame, because this would be a _very_ useful instruction.

Archive:

Comments:

See:

Comments:

Questions:

Interested:

:

Hello,

I have PIC18F4550

I worked with computed goto for driving 7-seg LEDs and it's OK.

When I read the MPASM about data directives, most of them are for PIC12/PIC16.

So, what're the directives for PIC18?

If I want to change computed goto method, how to develop sets of bytes, because now I want to drive dot matrix display. And each character is a set of 8-bytes.

For example, char 'A' is 0x3C,0x7E,0x66,0xC3,0xFF,0xC3,0xC3,0xC3

With the char generator.

1. Now, if I want to call multiple chars, then how to call multiple tables?
2. How to move from char to another one?
3. Which directive to apply?

Thank you,