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.

Table Look-ups

Status
Not open for further replies.

jpanhalt

Well-Known Member
Most Helpful Member
This question relates to my tilt sensor project. I have decided to use a table look-up to convert a calculated value from the accelerometer (i.e., a whole number proportional to duty cycle) to tilt angle.

I have read about table look-ups and gone through some tutorials to get a tenuous grasp of what is done. These questions relate to setting up and reading the table. MCU=12F683 at 8 MHz.

Question 1: The calculated values range from 471 to 553. I plan to subtract 384 from each value to give 87 to 169. I chose 384 (256 +128) on a wild guess -- largest number less than 471 with only two 1's -- that it might speed the calculation. Would it be faster to subtract a one-byte number, say 235, twice?

Question 2: What are the largest values one can enter in a line of Assembly such as: "movlw <calculated number> - 1 ". Can I just subtract ".471" that way and be done with it?

Question 3: My spreadsheet program (QuattroPro) has a nice catenation ("catb") command so one can pluck just the desired bits from a larger binary number. Is there a similar one-step operator in MPAsm?

Question 4: Related to #3 above, do the shift commands (<< and >>) rotate through carry?

Thanks.

John
 
Last edited:
Nigel's tutorial's deal with lookup tables quite extensively.... However your numbers will be two byte... you will have to read them in sequence..... ASM language is as basic as it gets.... 16 bit arithmetic has to be implemented... I have math routines for a 12f683 ( as I expect, so does most on this forum ) that I would be glad to give you...

Unfortunately, they are at work....and I'm not at the moment....
 
Last edited:
WOW QuattroPro.
I have not used that in a long time. I liked it.
I now use excel because I have old copies and every one uses it and open office because I can not afford MSoffice..
 
Hi Ian,

Thanks for the advice. I have been camped out at PicList and have working multi-precision math routines already incorporated. I found the ones from Peter Hemsley were more easily understood at my level than some of the more optimized ones. That part is working.

As for my table, avoiding two-byte look-ups is the whole purpose of the offset. The actual range of values is less than 128. I can reduce that more, but I want reproducible 0.5° resolution near zero. My tilt range is only ±11.5°. At the extremes (e.g., 11°), I will have 2 or 3 values from the accelerometer mapping to the same value of tilt.

John
 
I assumed as you needed to subtract 471 - 551 you were talking word values..... If the biggest value stored will be 115 (11.5 degrees).Then a normal 1 byte array will sufice..

As regards to shifting RRF and RRL in am pretty sure they are with carry... if you require without you need to clear the LSB or the MSB before the shift.
 
Question 1: The calculated values range from 471 to 553. I plan to subtract 384 from each value to give 87 to 169. I chose 384 (256 +128) on a wild guess -- largest number less than 471 with only two 1's -- that it might speed the calculation. Would it be faster to subtract a one-byte number, say 235, twice?

I would suggest subtracting 471 from the value, so the index byte for the table starts at zero, requiring just 82 values. You also need to consider that it doesn't want to cross a 256 byte boundary - it 'can' but it makes it much more complicated (see my LED matrix tutorial for examples).
 
@Nigel,

I was definitely leaning in that direction of subtracting 471 and starting at zero steps in the table. I figured blank spots in the table (the first 86 positions) would only deduct from usable program memory space. I put the question here just in case there was some way to jump to a label instead of just taking a number of steps down the table.

As to the RLF and RRF versus the arithmetic operators of << and >> (Question #4). It is clear to me that the former rotate through carry. I could not find in the MPAsm User's Guide a definitive statement on whether the latter rotated through carry. MPAsm does refer to >> as a shift, not a rotation.

John
 
Wow, thank you. I was and still am confused. Here is a snippet of a delay macro I got here on ETO:
Code:
DelayCy macro   delay           ; 12..327690 cycle range
		movlw	high((delay-12)/5)+1
	    movwf	delayhi
    	movlw   low((delay-12)/5)+1
      	movwf   delaylo
      	call    uDelay-((delay-12)%5)
        endm

It appeared the math operators were being used with instructions. So, I went to my copy of Table 3-4 in MPAsm User's Guide (DS33014J, ©2005) and saw those operators and more. I thought I had hit it easy. There is a note that says, "These operators can only be used on a line by themselves; they cannot be embedded within other expression evaluations."

I just checked for another version of the user's guide and found, DS33014H(also ©2005) that has a big bolded box at the top:
Note: These operators cannot be used with program variables. They are for use with directives only.

That is a little clearer, but that version is "H" and my version was "J". Why would such an important message be deleted from a later version? Or, maybe, Microchip started at "z." ;)

Confusion is not rare at my age, so I will just accept it and go the long way to getting the result I need.

Thank you for explaining this detail I saw, but didn't understand.

John
 
Table for two?....

I second Nigel, suboff 471 and make the table base address as a $xx00. If table values are 1 byte, all good; otherwise Left Shift Logical as multiplier.
I use LUT (look up tables) a Lot. Most stuff I need to convert don't lend themselves to a simple assembler math routine. A spreadsheet to derive values then stuff the table... all good. G.H <<<)))
 
Last edited:
Thanks all. I was messing with it last night. All table entries will be one byte and include a sign bit. The range of this thing is only ±11.5 degrees tilt with 0.5 degree resolution. I will test it with an extra bit of precision at zero, but I suspect vibration and noise will make that addition meaningless.

John
 
John,

What is the data in the lookup table?

Also, since you pretty much have to setup PCLATH and PCL for the table read operation anyway, couldn't you simply take care of the table offset in that code? In other words, instead of subtracting 471 from your result and then adding that result to the table address, simply add 'result' (471..553) to table address minus 471. You'd save one arithmetic operation by letting the assembler pre-processor do some of the work for you.

Cheerful regards, Mike

Code:
        movlw   low(table-471)  ; 
        addwf   resultlo,F      ; 
        movlw   high(table-471) ; 
        skpnc                   ;
        addlw   1               ;
        addwf   resulthi,W      ; 
        movwf   PCLATH          ;
        movf    resultlo,W      ;
        addwf   PCL,F           ;
;
;
 
Last edited:
Hi Mike,

I am a bit slow in replying, because I had to go to the farm this morning -- I have skipped too many days having fun with programming.

1) What are the data? Background: The Memsic outputs a 100 Hz PWM. The duty cycle is proportional to g and is offset so at level, it is 50%. The datasheet shows the formula for calculating g and tilt (arcsin g). Initially, I thought I might get by with measuring the pulse width only and correcting for temperature. Unfortunately, the temperature correction for the total period is quite large. It amounts to several degrees of tilt. So, I stepped back from that plan and measured the duty cycle with TMR1 (16-bit). I capture counts for the pulse and total period and must then ratio them to get the duty cycle. I needed the remainder to be calculated to at least 3 significant figures.

I am pretty much a rank beginner with PIC math and am using the 32 bit by 16 bit division program from Peter Hemsley (Piclist). The high pulse counts (2 bytes) are put into the top two bytes of the dividend registers. The total counts go to the divisor registers. That part is working to give a 16 or 15 bit quotient. The rest of my analysis has been done only on a spreadsheet. I concatenate that number picking bits <15,5> to give a sequence that is d1105 to d942. Subtracting 896 gives an 8-bit sequence form d209 to d46. I spent the better part of Tuesday messing with a spreadsheet to find better multipliers and offsets. 8-bit would not give me the resolution I need. 12 bits works fine, as do some others. One of my criteria was to not have the ratio go through zero. I know I could mess with the table look-up to deal with that, but I find it easier to read if the sequence starts high and goes to zero. Simply put, the table is a sequence of about 200 entries (I can change that resolution easily) that map to tilts of +11.5° to -11.5°.

2)
...simply add 'result' (471..553) to table address minus 471. You'd save one arithmetic operation by letting the assembler pre-processor do some of the work for you.
I started this thread unaware that the arithmetic operators for the assembler could not be used with program instructions (See posts#8, 9 ) and have subsequently spent several hours reading about pre-processor routines (Olin Lathrop's PIC Development Tools looks interesting). I came back to Cleveland early just so I could play with them. One approach I might try with them is to get the concatenation done using shifts instead of rotates. Your suggestion for the look-up table is appreciated and will also be tried.

John
 
Last edited:
@Diver300

That is exactly what I searched for several days ago, but I didn't find anything that helped me with PIC assembly. Over the range of gravitational values I am working, the relationship between the number correlating with duty cycle and tilt is quite linear. Depending on the multiplier used and bits concatenated, I have about 4 to 8 counts per degree. That is, at the more sensitive setting, 4°=#28 and 5.2°=#36.

I can imagine a routine that would take an input, decide the lower limit of a range it is in (i.e., table offset), look up that value and the $+1 value, and then after returning to the program, assign some fraction of the returned values to the input number. However, that adds steps and time. I am kind of balancing complexity, my own inexperience, and the capabilities of my chip. As a last resort, there is almost no difference in price between the 12F683 and 12F1840, and the latter has more than double the program memory if I run out of space.

However, I don't want to discard your suggestion without looking into it more. Can you give me any lead to how it is really done?

BTW, Memsic finally got back to me. The MXC6202 has been replaced with the MXC6232MP. Memsic will sell direct in quantities on 1ea. in the US, since it has no authorized distributor here. However, since I am so deep into the MXD2020 project, I would like to complete it. After all, it is mostly for fun and education for me.

John
 
I've got an excel spread sheet that makes high/low tables. I'll have to add some instructions but if you want it just let me know.

This is what it produces,
Code:
TableHigh
		dt	high(.7),high(.54),high(.97),high(.139),high(.177)
		dt	high(.12),high(.53),high(.92),high(.128),high(.162)
		dt	high(.16),high(.53),high(.87),high(.119),high(.149)
		dt	high(.21),high(.53),high(.83),high(.111),high(.136)
		dt	high(.26),high(.53),high(.79),high(.103),high(.125)
		dt	high(.30),high(.54),high(.76),high(.97),high(.115)
		dt	high(.35),high(.55),high(.74),high(.91),high(.106)
		dt	high(.39),high(.56),high(.72),high(.86),high(.98)
		dt	high(.44),high(.57),high(.70),high(.81),high(.92)
TableLow
		dt	low(.7),low(.54),low(.97),low(.139),low(.177)
		dt	low(.12),low(.53),low(.92),low(.128),low(.162)
		dt	low(.16),low(.53),low(.87),low(.119),low(.149)
		dt	low(.21),low(.53),low(.83),low(.111),low(.136)
		dt	low(.26),low(.53),low(.79),low(.103),low(.125)
		dt	low(.30),low(.54),low(.76),low(.97),low(.115)
		dt	low(.35),low(.55),low(.74),low(.91),low(.106)
		dt	low(.39),low(.56),low(.72),low(.86),low(.98)
		dt	low(.44),low(.57),low(.70),low(.81),low(.92)

The asm to extract the word value is fairly straight forward as well.

Mike.
 
I don't know what the MXC6232MP is, and I can't find any reference to it on the MEMSIC website.

I have used the MXC6202, which has an I2C output, not a variable mark space ratio that the MXD2020 has.

Anyhow that's not really relevant to the look-up table.

The first thing that I noted about the application is that in the range +/- 11.5 degrees there is very little non-linearity. The cosine of 11.5 degrees is just about 0.98, so the slope of the sine graph could be taken as being 0.99 with less than 1% error.

I feel that this may be a problem with scaling rather than lookups.

For any lookup where the slope is always positive, the basic idea is to split the value into most significant bits and least significant bits. The most significant bits are used to lookup a lower value, then one is added to the most significant bits and an upper value is looked up.

The difference between the upper and lower values is the slope of the graph. The slope is multiplied by the least significant bits and then added to the lower value.

The result is a curve approximated by several straight lines. In the case of a sine curve over the range +/- 11.5 degrees, a lookup table with 5 entries, (at -11.5, -5.75, 0, 5.75 11.5) would give a maximum error of 0.12% of the result, or about 0.01 degrees. That is far less than the error from your tilt sensor.

I used a lookup like that for an arctan table over the range 0 - 45 deg. The code is written for an 18F processor, which has hardware multiply, and the commands are a bit different, but it should give you the idea.

The code was:-

Code:
;count_reg is now 0 - 0xff representing tan(angle) in the range 0 - 45 deg
;there is a lookup table for the values, 0x00, 0x40, 0x80, 0xC0 and 0x100
;the value looked up is 4 times the tan in degrees
;the values either side of the current tan are looked up
;the last 6 bits are multiplied by the difference in the lookup table
;we want the atan of the number /256

	clrf   	count1
	bcf    	status, carry
	rlcf   	count_reg, f
	rlcf   	count1, f
	rlcf   	count_reg, f		;collect the top 2 bits and 
	rlcf   	count1, f		;multiply what is left by 4, because it will later be divided by 4
	incf   	count1, f	

	rcall 	arc_tan_table		;Get a2=atan( (x>>6) + 1)
	movwf	count2			;Store temporarily in count2
	decf  	count1, f		;Get the saved index
	rcall  	arc_tan_table		;Get a1=atan( (x>>6) )
	subwf	count2, w		;W=a2-a1, This is always positive.
	subwf	count2, f		;a1 = a1 - (a1-W) = W	;now count2 is the lower value and w is the difference

	mulwf	count_reg		;the difference is multiplied, 
	movf 	prodh, w		;take the high byte because we want the atan of input/256

	addwf	count2, w
	mullw	d'64'
	movff	prodh, count_reg	;divide by 4 so angle is now in count_reg

;This is the end of the code. The lookup table follows.



arc_tan_table
	movlw	high(atan_start)
	movwf	pclath

	rlncf  	count1, w

	addlw	low (atan_start)
	btfsc 	status, carry
	incf   	pclath, f
	movwf	pcl
atan_start
	retlw 	d'3'
	retlw 	d'59'
	retlw 	d'110'
	retlw 	d'150'	;these are 4 times the arctan of the numbers
	retlw 	d'182'
atan_end
 
Last edited:
UPDATE

It hit me last night that my concern about having the look-up table offsets go through zero was not only unfounded, but that going through zero could be used to advantage. Since the counts are symmetrical about zero, I simply converted the negative counts to have the same offsets as the positive ones and kept track of the sign. I used Mike's 10F200 code for Bin2BCD for testing purposes. Here is the code for concatenating and "folding" the table values. divid0 and divid1 are the registers that contain the quotient from dividing the high count*2^16 by the count for the period. The concatenation is performed on those registers, which are then combined into register divid0 for calculating table offsets. The flag is stored as the carry bit.

Code:
Catenate				;Catenate quotient <12,5>
	
	movlw	b'00011111'	;mask off upper 3 bits of quotient high byte
	andwf	divid1,f
	clrc
	rlf	divid1,f
	rlf	divid1,f
	rlf	divid1,f
	movlw	b'11100000'	;mask off lower 5 bits of quotient low byte
	andwf	divid0,f	
	swapf	divid0,f	;exchange high and low nibbles
	clrc
	rrf	divid0,f
	movf	divid1,w
	addwf	divid0,f	;combine high and low bytes into divid0
	btfss  	divid0,7	;check if divid0 is >d.127
	goto	Bin2BCD	;No, call	Table for positive tilts
	bsf	status,1	;Yes, set carry bit as flag 
	bcf	divid0,7	;subtract 128 because comf is limited to 0<=f<=127
	comf	divid0,f 	;1's complement to "fold" table, only Z is affected by comf
	bcf	divid0,7
	goto	Bin2BCD	;call	Table for tilt

Using the Clock Stimulus for debugging, I set high to 5200 and low to 4800 (duty cycle >0.5). High count was 5199; the look-up offset was d.41.

With the periods reversed (high = 4800 , low = 5200) to give a duty cycle <0.5 (negative tilt), high count was 4800 and the look-up offset was d.40.

I have tested every possible duty cycle in my range of interest with a calculated spreadsheet, and the algorithm works. I need to add a branch on error for values outside that range. As for the table, I plan to just get it working. Then, I will look at saving more space by using linear interpolation as mentioned above.

Now to take a break. It seems I spend half my time correcting typing errors.

John
 
Last edited:
UPDATE1

I have it working now with a look-up table. It goes through the table, picks a value, converts to ASCII, and outputs to a 2X16 screen. The table has 90 values in it, which is more than twice what I will need, but I figure it is easier to cut twice than lengthen once. In the final embodiment, it will just output a "packed" binary that will be sent by xBee to the decoder and display. Since the PC was already at 200+, I put the Table on page 6.

The 16F887 seems to be popular for GLCD's. I will be using it just for the display, UART to the xBee, and a couple of pins for zeroing and reset. 40-pins seems like overkill, but the price difference compared to a 12F1840 is insignificant.

1) Is there another processor you would recommend?
2) Any recommendation for GLCD?

John
 
1) Is there another processor you would recommend?

Yes. Consider using one of the "enhanced mid-range" devices. Perhaps something like the 28 pin 16F1938 or the 40 pin 16F1939. Both of those "enhanced" devices will run at 32 MHz and they have double the amount of program memory (16 kwords) and almost three times the amount of RAM (1024 bytes) compared to the 16F887.

The "enhanced" devices also provide a relatively easy mechanism to store and retrieve 14-bit "word size" values in tables (const arrays) which you might use to represent three packed-BCD digits. Direct access to WREG and the stack is another plus. So is automatic interrupt context save and restore. Then there are the awesome new instructions, including but not limited to, "brw" for tables, "addwfc", and "subwfb". Don't forget the ability to access RAM in one continuous linear address range, and, the dual indirect register sets (FSR0 / FSR1) with pre/post inc/dec capability.

Have fun with your project. Regards, Mike

<added>

Caveat! If you're using a PICKIT2, please be aware that the "enhanced" devices are not supported within MPLAB for programming or debugging. You have to use the PICKIT2 stand-alone application to program these devices. This may be an important consideration for some people...
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top