Continue to Site

### Welcome to our site!

#### augustinetez

##### Active Member
Not being very good in the math department, could someone point in the direction of solving an ADC scaling/conversion problem please.

Scenario (this carries across from my voltage leveling post a little while ago):-

Input to a PIC ADC using the 3.3V rails as the ADC reference.

The ADC raw values span 35 to 937

When the input voltage to the leveling circuit is at the center of it's range, the ADC value is 480

What I need to solve:-

A dead band of 5 ADC counts either side of 480 ie 475 - 485 = dead band, this could be increased slightly if it helps with calculations.

Above and below this dead band, I need to compute a number from 10 - 2500 in increments of 10 from the remaining counts (10 to 2500 above dead band and 10 to 2500 below) and also determine if it is above or below the dead band with an above/below dead band indicator bit.

The result from any calculation to = 0 when in the dead band.

This is to add/subtract from the frequency control word of a DDS chip using an existing analogue circuit.

I have spent several days trawling through this and various other forums without success.

And yes, of course, I'm still doing it all in assembler.

#### Pommie

##### Well-Known Member
This is my attempt in C,
Code:
  if(valADC<=480-5){
}else{
newVal=0;
}
BTW, your middle value is 486 (937+35)/2 if I'm understanding you correctly.

Mike.
P.S. I could put it in a spreadsheet if that helps.
Edit, the above isn't right as the range is wrong.

Last edited:

#### Pommie

##### Well-Known Member
If you want to play around with it in excel to get your head around it then the formula is,

I.E. =IF(A5<(B$1-5),((B$1-5)-A5)*(2500)/(B$2)+10,IF(A5>=(B$1+5),(A5-(B$1+5))*(2500)/(B$2)+10,0))

This is basically using nested IFs to do the same thing.

Mike.

#### Pommie

##### Well-Known Member
Terry, another possibility (probably more implementable in assembly) is to store a table of 226 words. Your chip (16F1827 I assume) has 4K of 14 bit words. A single page (255 words) could hold a 226 word table (amount above/below your mid point) and the flash read routines could retrieve the correct value. I'm not sure how you store words in assembly - it used to be DW but things have "progressed". Just a thought.

mike.

#### rjenkinsgb

##### Well-Known Member
My take on it:

It does not use floating point or division, which should make it very fast and compact compared to a floating point based solution.

It's also spread out a lot more than it needs to be, for clarity and comments..

C:
int16_t ADCResult; // for testing only - use your input value

int32_t tempval;

int16_t sign;

// Remove offset

// Extract the sign bit
sign = 0;
if ( adc_val & 0x8000 ) sign = 1;

// Move the absolute value to the 32 bit temp, also removing the dead zone
if ( adc_val < -5 )
else if ( adc_val > 5)
else
tempval = 0;

if ( tempval ) {
// Only do the rest of the calcs if there is a value to work with

// Scale it; to 0-210 using a fractional multiplication and only integers.
// ratio of input to output, * 2^20
//  to shift the result by 20 bits at the same time; scale factor 591000
tempval *= 591000;

// Remove the 20 bit scaling
tempval >>= 20;

// Add in the minimum output value
tempval += 1;
// And make it units of 10
tempval *= 10;

// Limit to 2500
if( tempval > 2500 )
tempval = 2500;

}

// tempval has the numeric result.
// sign is 1 if it's a negative result.

It's a slight compromise in that it is symmetrical around the zero offset point.
35 give 2500 (-)
34 gives 2490 (-)

However, at the top of the scale it reaches 2500 with an input of 925 rather than 937
If that's critical, try adding a couple of lines to use a scale of 580446 if the sign bit is clear, or the existing value if it's set.

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
augustinetez .. I think they missed the bit about ASM..
You may be better off in Oshonsoft... I wrote a Map() function some time back in basic.
I actuality did a little Math library for myself..

However!! The map function ONLY worked for positive numbers..

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
One other thing... I do find useful.. Write the routine in basic... Assemble then take that code to your assembly code. Math routines will be needed though... ( All in the assembled code )

#### augustinetez

##### Active Member
Thanks for the input so far, been out of the house for longer than I expected.

Re Ian's comment of doing it in the Oshonsoft compiler, that would work as I did the same with his bargraph code to see if it would translate back to asm - it worked fine.

Have a few things I have to get done, so will read through this all properly later on.

#### augustinetez

##### Active Member
BTW, your middle value is 486 (937+35)/2 if I'm understanding you correctly.
Possible - I wrote a small file to actually display the raw ADC values from the test jig, but I'm running a 5V LCD on 3.3V.
While the electronics run fine at 3.3V, the backlight not so much and is quite dim and it looked like 480 on the display.

is to store a table of 226 words. Your chip (16F1827 I assume)
Actually 250 values between 10 and 2500 and yes 16F1827.

However, at the top of the scale it reaches 2500 with an input of 925 rather than 937
If that's critical,
I do really wish my brain could absorb all this C stuff, I did sort of understand the flow of it.

It is not critical critical if that makes any sense - the input to all this is a regular pot which when centered is the 'dead band', all I need to make sure of is that the knob can be turned through the bulk of the distance either side of center rather than having a large bit at the end that does nothing.

Off to see if I can do a small mod to the test jig to run the backlight off 5V and play with Mike's formula in Excel to see if I can make sense of it.

As an aside, how easy/hard would it be to convert RJ's bit of C code across to Oshonsoft basic? I might have a play and see what happens.

#### rjenkinsgb

##### Well-Known Member
For Mike's routine you need floating point maths.
For mine you would need a 32 bit integer multiply, but no floating point.

Probably the simplest "precision" method in assembly is to mix the various approaches:

Remove the offset and get the absolute value, as in the first half of mine.

If it's non zero, then use a lookup table to convert the input range to 0-249 output
Add one and multiply by ten (shift left once, save that, shift left two more times and add the two parts).

Or if the end limits are not too critical - remove the offset & get the absolute value etc. then return zero if it's in the dead zone.

That should give you a value somewhere around 0-512; just set it to 498 if it's greater than that, add 2 (to get the minimum 10 output) and multiply by 5 (shift left twice and add the original).

That gives 10-2500 with a fractional dead zone at either extreme end, but next to no maths.

#### Buk

##### Active Member
Effectively you are converting an unsigned 10-bit value to a signed 9-bit and then multiplying by 10.
(Plus some fudge factors, which may or may not be important to your application.

And that can be done in 3 opcodes (depending on your flavour of cpu).

In pseudo asm:

Code:
in regN, $ADC shr regN, 1 sub regN, 256 mult regN, 10 This gives Code:  0 : -2560 1 : -2560 2 : -2550 3 : -2550 4 : -2540 5 : -2540 ... 32 : -2400 33 : -2400 34 : -2390 35 : -2390 36 : -2380 37 : -2380 ... 508 : -20 509 : -20 510 : -10 511 : -10 512 : 0 513 : 0 514 : 10 515 : 10 516 : 20 517 : 20 518 : 30 519 : 30 ... 934 : 2110 935 : 2110 936 : 2120 937 : 2120 938 : 2130 939 : 2130 ... 1019 : 2530 1020 : 2540 1021 : 2540 1022 : 2550 1023 : 2550 which might be close enough to your specification #### Nigel Goodwin ##### Super Moderator Most Helpful Member Possible - I wrote a small file to actually display the raw ADC values from the test jig, but I'm running a 5V LCD on 3.3V. While the electronics run fine at 3.3V, the backlight not so much and is quite dim and it looked like 480 on the display. It's not the backlight, that's just an LED that you can easily give more current, it's the LCD panel itself that doesn't like too low a drive voltage. I've tried to run them at 3.3V as well, some aren't too bad, but some are dreadful. #### augustinetez ##### Active Member Thanks Nigel, won't bother messing around with it then. #### augustinetez ##### Active Member Quick update - I've got exactly nowhere so far. Answered my own question about converting RJ's bit of C code to Basic - of course it won't, Basic can't do negative numbers (Basic for micro's that is). Yes, I know there are work-arounds but that just gets messy. Still to look at RJ's second suggestion (post #11) Mike's excel formula just gave me a headache and a load of spots before my eyes. And BUK's method doesn't condense the results to be within the actual ADC range of 35 - 937. Got to spend a load of time doing my day job (filling in paperwork for the Govt) over the next week, so will let this idle for a while and see if a miraculous brainwave strikes #### Buk ##### Active Member And BUK's method doesn't condense the results to be within the actual ADC range of 35 - 937. If you feed my method 237, you get -2380. If you feed it 937, you get 2120. If you want to get closer to your spec, add 37 to the input and multiply by 11 instead of 10. Ie: Perl:  sub x{ my$n = shift;
$n += 37;$n >>= 1;
$n -= 256;$n *= 11;
return $n } Which gives Code:  37 => -2409 38 => -2409 39 => -2398 ... 472 => -22 473 => -11 474 => -11 475 => 0 476 => 0 477 => 11 478 => 11 479 => 22 ... 934 => 2519 935 => 2530 936 => 2530 937 => 2541 #### Buk ##### Active Member If you want to get closer to your spec, add 37 to the input and multiply by 11 instead of 10. To get closer still, add a second fudge factor by subtracting 40 from the previous result and rounding to the nearest 10: Perl: sub x{ my$n = shift;
$n += 37;$n >>= 1;
$n -= 256;$n *= 11;
$n -= 40 int($n/10 ) * 10;
}

Gives:
Code:
 37 => -2440
38 => -2440
39 => -2430
40 => -2430
41 => -2420
...
478 =>  -20
479 =>  -10
480 =>  -10
481 =>    0
482 =>    0
483 =>    0
484 =>    0
485 =>   10
486 =>   10
487 =>   20
...
932 => 2460
933 => 2470
934 => 2470
935 => 2490
936 => 2490
937 => 2500

#### augustinetez

##### Active Member
Thanks BUK, I'll add it to the list of things to try.

#### Pommie

##### Well-Known Member
Terry,
A little simpler approach (I hope).
2. if above 491 calculate ADC-492 - this allows for the dead zone.
3. if below 483 calculate 482-ADC
4. if neither 2 or 3 true then calculated value=0
5. if not zero then lookup value in a table.

The table would ideally be 512 (14 bit) words in the last two pages of flash (I think mpasm had DW but might only have been for 18 series chips) but could be a huge table of retlw values which would require 4 pages of flash (1/4 of your memory). I can supply the table in either format if you want to try this path.

Mike.

#### Pommie

##### Well-Known Member
Terry, I just tried using DW in MPLAB 8.92 and it works.
I placed this in the code,
Code:
    org 0xe00
DW  5,11,16,22,27,33,39,44,50,55,61,67,72,78,83,89
DW  95,100,106,111,117,123,128,134,139,145,151,156,162,167,173,178
DW  184,190,195,201,206,212,218,223,229,234,240,246,251,257,262,268
DW  274,279,285,290,296,302,307,313,318,324,329,335,341,346,352,357
DW  363,369,374,380,385,391,397,402,408,413,419,425,430,436,441,447
DW  453,458,464,469,475,480,486,492,497,503,508,514,520,525,531,536
DW  542,548,553,559,564,570,576,581,587,592,598,604,609,615,620,626
DW  631,637,643,648,654,659,665,671,676,682,687,693,699,704,710,715
DW  721,727,732,738,743,749,755,760,766,771,777,782,788,794,799,805
DW  810,816,822,827,833,838,844,850,855,861,866,872,878,883,889,894
DW  900,906,911,917,922,928,934,939,945,950,956,961,967,973,978,984
DW  989,995,1001,1006,1012,1017,1023,1029,1034,1040,1045,1051,1057,1062,1068,1073
DW  1079,1085,1090,1096,1101,1107,1112,1118,1124,1129,1135,1140,1146,1152,1157,1163
DW  1168,1174,1180,1185,1191,1196,1202,1208,1213,1219,1224,1230,1236,1241,1247,1252
DW  1258,1263,1269,1275,1280,1286,1291,1297,1303,1308,1314,1319,1325,1331,1336,1342
DW  1347,1353,1359,1364,1370,1375,1381,1387,1392,1398,1403,1409,1414,1420,1426,1431
DW  1437,1442,1448,1454,1459,1465,1470,1476,1482,1487,1493,1498,1504,1510,1515,1521
DW  1526,1532,1538,1543,1549,1554,1560,1565,1571,1577,1582,1588,1593,1599,1605,1610
DW  1616,1621,1627,1633,1638,1644,1649,1655,1661,1666,1672,1677,1683,1689,1694,1700
DW  1705,1711,1717,1722,1728,1733,1739,1744,1750,1756,1761,1767,1772,1778,1784,1789
DW  1795,1800,1806,1812,1817,1823,1828,1834,1840,1845,1851,1856,1862,1868,1873,1879
DW  1884,1890,1895,1901,1907,1912,1918,1923,1929,1935,1940,1946,1951,1957,1963,1968
DW  1974,1979,1985,1991,1996,2002,2007,2013,2019,2024,2030,2035,2041,2046,2052,2058
DW  2063,2069,2074,2080,2086,2091,2097,2102,2108,2114,2119,2125,2130,2136,2142,2147
DW  2153,2158,2164,2170,2175,2181,2186,2192,2197,2203,2209,2214,2220,2225,2231,2237
DW  2242,2248,2253,2259,2265,2270,2276,2281,2287,2293,2298,2304,2309,2315,2321,2326
DW  2332,2337,2343,2348,2354,2360,2365,2371,2376,2382,2388,2393,2399,2404,2410,2416
DW  2421,2427,2432,2438,2444,2449,2455,2460,2466,2472,2477,2483,2488,2494,2500,2500
DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
DW  2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500,2500
and it assembled.
An inspection of the memory (in simulator) showed,

Which looks like it worked.
The assembly code to retrieve the values is on page 106 of the datasheet.
HTH.

Mike.
Edit, I assumed the default radix is decimal, if not I can produce the table anyway you like.

Replies
11
Views
858
Replies
14
Views
936
Replies
12
Views
1K
Replies
4
Views
2K
Replies
5
Views
1K