Table lookup

From MARS Wiki

Jump to: navigation, search

Contents

What is a table?

No, not the one with four legs that you eat off of. A table in microcontroller speak is a series of values hardcoded in nonvolatile memory. You can think of it as an array that you cannot (easily) change while the code is running. Suppose we had a table containing the following values:

1
1
2
3
5
8
13
21
34
.
.
.

Assuming we adopt C's zero-referenced indexing (that is, the first element of the array is referred to by index 0), then we have the following:

table[4]=5
table[7]=21
...etc

Why use tables?

You can see here that rather than calculate the appropriate entry of the Fibonacci Sequence at runtime, we simply look up the appropriate value from a table. Computing each entry, while not terribly difficult, can take a long time especially on a microcontroller that tries to use as little power as possible. By using table lookup, we can both speed up the application and make it more deterministic (meaning each operation takes a predictable and/or uniform amount of time).

Another commonly-seen table is a sine lookup table. Calculating trigonometric functions in particular can be processor-intensive, so it is much more efficient to simply place precomputed values in memory.

General form of a lookup table

To use a lookup table, one generally places an offset in W and then calls the table code. The table subroutine simply adds the offset in W to the program counter, which causes the microcontroller to skip all the instructions before the retlw statement containing the desired return value. The addwf pcl,f instruction is the one that performs this modification of the program counter.

Limitations

Since we only modify the lowest eight bits of the program counter, we can only skip 2^8=256 instructions; therefore, the size of the table is limited to 255 entries (subtracting one for the addwf pcl,f instruction). On the PIC18F, we have to multiply by two to generate the correct offset, so the table is limited to 127 entries.

Larger tables on the PIC18F

It turns out that there is a convenient way to access program memory on the PIC18F that expands addressable table range to the entire program memory space. Consider the following code:

	movlw	upper ADDRESS
	movwf	tblptru
	movwf	high ADDRESS
	movwf	tblptrh
	movlw	low ADDRESS
	movwf	tblptrl
	
	;repeat the following lines as necessary
	tblrd	*+		;use TBLPTRU:TBLPTRH:TBLPTRL as 24-bit pointer to memory
	movf	tablat,w	;place value pointed to in W

The first six lines form a pointer to the location given by ADDRESS. The next two lines read the value and place the result in W. Note the *+ operand given after tblrd -- this specifies a post-increment. This means that after reading the appropriate memory location, the 24-bit pointer is incremented to point to the next location. This makes it pretty easy to large continuous sections out of memory.

To create a table at a given memory location, use something like the following:

ADDRESS	
        de	"Put stuff here..."
        de	"...and more stuff here..."
	.
	.
	.

The de directive places the remainder of the line into program memory with no processing at all. This allows you to place arbitrary bit patterns into memory without necessarily requiring them to be meaningful to the processor. There is also less command overhead this way, since each retlw instruction uses several bits of memory.

Examples of table use

Binary-to-hex conversion

While it is possible to convert binary to hex without using a table, it is the author's opinion that using a table makes the process much more straightforward. Here's how a conversion might go on a member of the PIC16F family. The code assumes that a variable 'temp' has been defined appropriately, and that there is a subroutine 'transmit' that takes a single-byte argument in W.

tohex	movwf	temp		;save the number to be converted
	swapf	temp,w		;isolate the high nibble (highest 4 bits)
	andlw	0x0F
	call	table		;look up the ASCII value
	call	transmit	;transmit it
	movf	temp,w		;isolate the low nibble
	andlw	0x0F		;look up the ASCII value
	call	table
	call	transmit	;transmit it
	return			;all done!

table	addwf	pcl,f
	dt	"0123456789ABCDEF"

Note that in MPASM, the dt directive is a handy way to build a lookup table. The directive generates a series of retlw data commands just prior to assembly. The above table code then becomes

table	addwf   pcl,f
	retlw	'0'
	retlw	'1'
	retlw	'2'
	retlw	'3'
	.	
	.
	.

You can see that for large tables, particularly those involving ASCII values, this can save you lots of typing.

Driving a seven-segment LED display

This is a small modification of the above code. We retain the binary-to-hex conversion, except we only look at the upper nibble of the byte to be displayed. Instead of transmitting the result, we place it on a seven-segment LED display. This code segment is for the PIC18F2420, but it would run with minor modification on nearly any other 8-bit PIC microcontroller. Initialization code is omitted for the sake of compactness, but a full source code listing can be found here.

conv	bsf	adcon0,go_done	;start a conversion
conv1	btfsc	adcon0,go_done	;wait for the conversion to finish
	bra	conv1
	movff	adresh,result	;copy the result to the "result" register
	swapf	result,w
	call	table
	xorlw	b'11111111'     ;invert the bit pattern for common-anode LED module
	movwf	portc
	call	delay		;wait some more
	bra	conv		;do this forever

table	andlw	.15
	rlncf	wreg,f
	addwf	pcl,f
	retlw	b'01111110'	;0
	retlw	b'00110000'	;1
	retlw	b'01101101'	;2
	retlw	b'01111001'	;3
	retlw	b'00110011'	;4
	retlw	b'01011011'	;5
	retlw	b'01011111'	;6
	retlw	b'01110000'	;7
	retlw	b'01111111'	;8
	retlw	b'01111011'	;9
	retlw	b'01110111'	;A
	retlw	b'00011111'	;B
	retlw	b'01001110'	;C
	retlw	b'00111101'	;D
	retlw	b'01001111'	;E
	retlw	b'01000111'	;F

This program will repeatedly sample the analog input connected to RA0 (Pin 2) and display the high nibble of the conversion result in hex on a display connected to Port C. RC6 connects to segment A, RC5 to segment B, RC4 to segment C, and so on.

Personal tools