help

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
Excuse the stupid title, but apparently I am the victim of the notorious "too long title" bug. I wanted the title to be, "PBP ADC to voltage conversion help."

I want to read a pot, and convert its ADC10 digital value to voltage. Each step represents .004V. My conversion routine works up through a digital value of 999, but is wrong at 1000 and above. For example a digital value of 903 converts to a voltage of 3.612 (correct), but a digital value of 1007 converts to 4.24V when it should be 4.024V. The PicBasic Pro code follows; please tell me how to correct it. Thanks.

Rich (BB code):
start:
ADCIN 0, left_pot 'Reads analog voltage on AN0, converts it to 10-bit
    'digital value, and stores it as left_pot.
y = (left_pot*4)
whole = y/1000
decimal = y//1000              
LCDOUT $FE,1,"Left Pot = ", DEC left_pot 'Clears LCD screen, displays
    '"Left Pot = " and the decimal value of left_pot.
lcdout $FE,$C0,"Voltage = ", #whole, ".", #decimal
 

tshuck

Joined Oct 18, 2012
3,534
Decimal is only two digits - the leading zero is lopped off.

Either lcdout a zero before printing the remainder if decimal < 100, or divide for each digit value and print it out for each decimal place...
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
Decimal is only two digits - the leading zero is lopped off.

...or divide for each digit value and print it out for each decimal place...
Thanks. Would you show me how to do that division? I can figure out how to print it.
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
Okay, it is working as I had hoped. I need to study the MOD operator in an effort to understand exactly what's happening.

On a side note, I have my external reference quite steady at 4.096V as measured with my Fluke 115. When I use that same meter to measure the voltage out of the pot, it is consistently different by .006V from the calculated voltage. Do you have any idea why that is?

Thanks again for the code help.
 

tshuck

Joined Oct 18, 2012
3,534
Okay, it is working as I had hoped. I need to study the MOD operator in an effort to understand exactly what's happening.
Sorry, I shouldn't assume - it is commonly called a mod operator(like the mod counter) and, like the mod counter, is actually the modulus operation (or taking the modulo) - essentially the "//" division you were doing in your code.

On a side note, I have my external reference quite steady at 4.096V as measured with my Fluke 115. When I use that same meter to measure the voltage out of the pot, it is consistently different by .006V from the calculated voltage. Do you have any idea why that is?
I'm afraid I'm not familiar with your setup, but assuming you are just using the potentiometer as a voltage divider, I would suspect the difference to be from the quantization error of each of the measuring devices.
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
I want to be sure I understand how the modulus operation works in my code.

Rich (BB code):
ADCIN 0, left_pot 'Reads analog voltage on AN0, converts it to 10-bit digital value, and stores it as left_pot.
lpot = (left_pot*4)
lthousands = (lpot//10000)/1000
lhundreds = (lpot//1000)/100
ltens = (lpot//100)/10
lunits = (lpot//10)
If lpot = 3456, then lthousands is calculated as follows.

3456 is divided by 10000
quotient is 0
remainder is 3456
remainder is divided by 1000
quotient is 3 (because .456 is truncated)
lthousands is therefore 3

Correct?

Thanks.
 

tshuck

Joined Oct 18, 2012
3,534
I want to be sure I understand how the modulus operation works in my code.

Rich (BB code):
ADCIN 0, left_pot 'Reads analog voltage on AN0, converts it to 10-bit digital value, and stores it as left_pot.
lpot = (left_pot*4)
lthousands = (lpot//10000)/1000
lhundreds = (lpot//1000)/100
ltens = (lpot//100)/10
lunits = (lpot//10)
If lpot = 3456, then lthousands is calculated as follows.

3456 is divided by 10000
quotient is 0
remainder is 3456
remainder is divided by 1000
quotient is 3 (because .456 is truncated)
lthousands is therefore 3

Correct?

Thanks.
Yup, you just need to be careful that the number is an integer type, or is at least truncated properly.

You don't need to do the modulus on the thousands place since it will never be in the ten thousands digit - you can just divide by 1000 (I put it in for consistency). Of course, leaving it in won't hurt anything but cycle time, but the modulus on the highest digit position doesn't give you any new information.
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
Yup, you just need to be careful that the number is an integer type, or is at least truncated properly.

You don't need to do the modulus on the thousands place since it will never be in the ten thousands digit - you can just divide by 1000 (I put it in for consistency). Of course, leaving it in won't hurt anything but cycle time, but the modulus on the highest digit position doesn't give you any new information.
Thanks for the help. It's really nice to know how code works (even if I didn't write it myself.)

Here's a photo of my test setup. The little assembly toward the top left is my TL431A 4.096V reference circuit.
 

Attachments

tshuck

Joined Oct 18, 2012
3,534
Thanks for the help. It's really nice to know how code works (even if I didn't write it myself.)

Here's a photo of my test setup. The little assembly toward the top left is my TL431A 4.096V reference circuit.
Wow, you really enjoy making modular setups!:)

I had assumed you wrote it, had I not, I would have been more thorough in my initial explanation, sorry.

The double dabble approach (the alternative I linked to before) is faster, but not as clear when looking at the code. It doesn't seem like you are too concerned with speed now, but a good thing to know for future endeavors...
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
I had assumed you wrote it, had I not, I would have been more thorough in my initial explanation, sorry.
I did write the original that I posted, if one can really say he wrote any code. It's more accurate to say that I cobbled it together from bits and pieces. That's pretty much the way all my code comes together...a bit at a time...with help from people like you.
 

tshuck

Joined Oct 18, 2012
3,534
I did write the original that I posted, if one can really say he wrote any code. It's more accurate to say that I cobbled it together from bits and pieces. That's pretty much the way all my code comes together...a bit at a time...with help from people like you.
From my stint as a software engineer, I'd say that's how all code is written!:p

The key is understanding what the Frankencode means...
 

THE_RB

Joined Feb 11, 2008
5,438
Doesn't the compiler already have a function to convert a unsigned 16bit variable into an ascii text number?

In C you just use itoa() or WordToString() etc.
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
Doesn't the compiler already have a function to convert a unsigned 16bit variable into an ascii text number?

In C you just use itoa() or WordToString() etc.
I don't know, but I just posted the question over on the picbasic pro forum. I'll post the answer if/when it comes. Thanks for wording the question for me. :)
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
Here is what appears to be the shortest way to what I needed. Thanks to all for your input.

Rich (BB code):
' Aliases and Modifiers:

' Program Code:
PAUSE 1000 'Pauses 1 second to allow LCD to setup.
start:
ADCIN 0, left_pot 'Reads analog voltage on AN0, converts it to 10-bit digital
    'value, and stores it as left_pot.
lpot = (left_pot*4) 'Multiplies left_pot times 4 to get 1000x voltage.

ADCIN 1, right_pot 'Reads analog voltage on AN1, converts it to 10-bit digital
    'value and stores it as right_pot.
rpot = (right_pot*4) 'Multiplies right_pot times 4 to get 1000x voltage.

LCDOUT $FE,1,"Left Pot  = ", DEC left_pot 'Clears LCD screen, displays
    '"Left Pot = " and the decimal ADC of left_pot.
LCDOUT $FE,$C0, "Voltage = ", DEC lpot/1000, ".", DEC3 lpot//1000
    'Displays result to 3 decimal places.

LCDOUT $FE,$94,"Right Pot = ", DEC right_pot 'Sets LCD to beginning of
    'second line and displays "Right Pot = " and the decimal ADC of
    'right_pot.
LCDOUT $FE,$D4, "Voltage = ", DEC rpot/1000, ".", DEC3 rpot//1000
    'Displays result to 3 decimal places.
 
Last edited:

THE_RB

Joined Feb 11, 2008
5,438
Nicely done! :)

You've used the ability of the LCDOUT function to display multiple strings in sequence. Neat and clean.

If I was to find something to criticise, what are the "magic hex numbers" after LCDOUT?
LCDOUT $FE,$D4

I assume the second one is the line and column indexing, is the first one a move command? ie; FE=move D4=whereto?

It would be a nice fix to replace those with text constants if possible. That makes the code easier to read and more importantly easier to write. ;)
 

Thread Starter

tracecom

Joined Apr 16, 2010
3,944
The $FE indicates that what follows is a control code; the next $XX is the line indicator for the display. Thanks for the compliment, but I got a lot of help. The upside is that I know how to do it now!
 
Top