View previous topic :: View next topic |
Author |
Message |
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Wed Oct 03, 2007 11:50 pm Post subject: maths modulo question |
|
|
hi there
I am trying to measure a battery pack voltage (0-20V) and display the result in the form xx.yy where xx = units and yy = decimals, ie 15.65V would give xx = 15 and yy = 65.
I have a 1:4 resistor divider to scale the battery voltage, so I am measuring 0-5V against the internal ADC ref of 5V, meaning in theory that
vbatt = (vadc /1024 * 5) * 4
ie
vbatt = (vadc * 5) / 256
OK so I want to measure vadc, and then get the result in a form of 2 variables whereby xx (units) and yy (decimals).
I know I can do this using Single variables and then using INT and FRAC but I tried this and it uses HEAPS of code space and it should be easy enough to do it using bytes or words.
Can anyone spot the problem with the following code, also is there perhaps a more simple (code-efficient) way of performing this type of calculation?
My problem is, the following code gives 0 whenever I use the arithmetic operator "*" This means that all subsequent calculations are useless and result in 0. Is this a bug in the compiler or something? I am using 1.11.8.7. Even if I write a trivial code like:
dim x as byte
X = 5
X = X * 10
print X
I get X = 0...?
thanks in advance...
Code: |
' ****************************************************************************************
$regfile = "attiny44.dat" 'regfile must match the chip
$crystal = 8000000 'crystal must match too
Config Adc = Single , Prescaler = 16 ', Reference = vcc
Ddra = &B10001111 ' Use pin 0..3 as ops, 4..6 as ips, 7 as op
Ddrb = &B0100 ' Use pin 0..1 as ips for SWs, 2 as pwm op, 3 is /Res
Start ADC
Dim Vadc As Word '10-bit ADC reading fullscale = 1024 ie 5V
Dim Vbatt As Word 'actual battery voltage to measure/display
Dim Units As Word 'units of measurement
Dim Remainder As Word 'MODulo
Dim Decimals As Word 'decimals to 2 places
Progstart:
Open "comb.0:9600,8,n,1" For Output As #1 'to display results
Vadc = Getadc(5) 'use ADC(5) input on pin 8
Print #1 , "vadc = " ; vadc 'check the value
'first work out units:
Vbatt = Vadc * 5 'start with the x5 multiplier for 5V ref
Units = Vbatt \ 256 'integer division first for units
Print #1 , "vbatt (units) = " ; Units
'now work out decimals:
'first use MOD to get remainder:
Vbatt = Vadc * 5 'use original ADC value again
Remainder = Vbatt Mod 256 'remainder for decimals
Print #1 , "voltage remainder = " ; Remainder
'now work out decimals to 2 places:
Remainder = Remainder * 100 'going to work out the 2 decimal places now
Decimals = Remainder / 256 'this will give us integer result ie first 2 decimal places
Print #1 , "vbatt (decimals) = " ; Decimals
Close #1
Goto Progstart
End |
I am sure the maths should work, ie assume
vbatt(actual) = 14.6V
Vadc = 14.6V / 4 = 3.65V at ADC input
Vadc conversion result = 747 (out of 1024)
vbatt = 747 * 5 = 3735
Units = vbatt/256 = 14.59 (this is integer maths so Units = 14)
now for decimals:
vbatt = 747 * 5 = 3735
remainder = vbatt MOD 256 = 151
remainder = remainder * 100 = 15100
decimals = remainder / 256 = 58.99 (this is integer maths so Remainder = 58 )
ie
Units = 14, Remainder = 58
(the maths is not 100% perfect cos the decimals are not rounded, but this is fine for my application) |
|
Back to top |
|
|
Jonas
Joined: 06 Jul 2006 Posts: 109
|
Posted: Thu Oct 04, 2007 1:16 am Post subject: |
|
|
Hi
Why not using a lookup table. Because the result will always be the same.
Like the ADC value 511 will always be equal 10.00V and 255 always 05.00V
You will end up with much faster code too.
/Jonas |
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Thu Oct 04, 2007 1:19 am Post subject: |
|
|
Hi
thanks for the reply - I thought of that, but it seems a bit tedious to create a table with that many entries for a problem that should be fairly simple to solve mathematically.
The code does not need to be run often, so speed is not a big problem. It would be nice to keep it compact though.
Still can't work out why "*" operator always = 0 ....? |
|
Back to top |
|
|
kimmi
Joined: 24 Feb 2006 Posts: 1922 Location: Denmark
|
Posted: Thu Oct 04, 2007 3:43 am Post subject: |
|
|
Hi,
I just testet your code on mega8 & 16 it works fine
can it be the attiny44.dat that dont work or your 1.11.8.7 ? _________________ / Kim |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Thu Oct 04, 2007 7:17 am Post subject: |
|
|
I cant see what is wrong with your code, but there is a built-in way of scaling an integer to display a fixed number of decimals. Look up the Format command in Bascom Help.
Essentially you calculate a number scaled up by a factor 100 ( make sure it fits in a Word, or use a Long ), then use Format to show it with 2 decimal places. _________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Thu Oct 04, 2007 8:19 am Post subject: |
|
|
AdrianJ wrote: | I cant see what is wrong with your code, but there is a built-in way of scaling an integer to display a fixed number of decimals. Look up the Format command in Bascom Help.
Essentially you calculate a number scaled up by a factor 100 ( make sure it fits in a Word, or use a Long ), then use Format to show it with 2 decimal places. |
I did look at using Format but I'm not so interested in adjusting the decimal places to 2, rather in extracting the parts of the number, ie units (before decimal place) into one variable, and decimals (after decimal place) into another. I don't think Format will help with this?
thanks for the input |
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Thu Oct 04, 2007 8:21 am Post subject: |
|
|
kimmi wrote: | Hi,
I just testet your code on mega8 & 16 it works fine
can it be the attiny44.dat that dont work or your 1.11.8.7 ? |
hmm that's interesting.. I'm assuming there must be a compiler bug then if the "*" operator always results in 0...? |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Thu Oct 04, 2007 9:19 am Post subject: |
|
|
You are correct, Format wont do that. But you could as easily use the Left and Mid or Right string operations to chop the numbers up, after converting to strings. I suspect the string operations are at least as fast as the numeric manipulations. _________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
Luciano
Joined: 29 Nov 2004 Posts: 3149 Location: Italy
|
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Thu Oct 04, 2007 2:34 pm Post subject: |
|
|
Ah thanks I will try that, I should have noticed myself... I assume this means I can still use "*" but the compiler will generate code to do it in software?
I had noticed your many helpful posts on this forum Luciano, it is good to see someone who is ready to share experience and expertise in this generous way
Any comments or advice on my code, by the way? |
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Thu Oct 04, 2007 2:40 pm Post subject: |
|
|
Luciano, as I suspected, you do have a tendency to genius, changing the dat file got it working I spent 4 hours yesterday trying to get this to work |
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Thu Oct 04, 2007 5:17 pm Post subject: |
|
|
AdrianJ wrote: | You are correct, Format wont do that. But you could as easily use the Left and Mid or Right string operations to chop the numbers up, after converting to strings. I suspect the string operations are at least as fast as the numeric manipulations. |
thanks I'll try that too. The code is working perfectly now once I edited the dat file but I'm interested to see whether there's a more code-efficient way of doing it. |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Fri Oct 05, 2007 12:06 am Post subject: |
|
|
You have 2 multiplies by 5, two divisions by 256, and a mult by 100 to do this numerically. If you did it in strings it could look something like this:
Vbatt = Vadc * 500 'scale up to voltage * 100
strBatt = str(Vbatt) 'convert to string
bLen = len(strBatt) 'get its length, should check that len > 2
strDecimal = right(strBatt,2)
Decimal = val(strDecimal) 'if you need it as a numeric
bLen = bLen - 2
strUnit = left(strBatt,blen)
Unit = val(strUnit) _________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Fri Oct 05, 2007 12:13 pm Post subject: |
|
|
AdrianJ wrote: | You have 2 multiplies by 5, two divisions by 256, and a mult by 100 to do this numerically. If you did it in strings it could look something like this:
Vbatt = Vadc * 500 'scale up to voltage * 100
strBatt = str(Vbatt) 'convert to string
bLen = len(strBatt) 'get its length, should check that len > 2
strDecimal = right(strBatt,2)
Decimal = val(strDecimal) 'if you need it as a numeric
bLen = bLen - 2
strUnit = left(strBatt,blen)
Unit = val(strUnit) |
aha but you forgot the /256
I did try that and although the code is a little neater it actually compiles to around 20% more code than my original. This is probably partly because Vbatt has to be dimensioned as Long to accomodate the x500 calculation. The string operations also seem to use a fair bit of code. The calulation may well be faster but that is not critical in this application (only required on power-up).
thanks again for all the advice! |
|
Back to top |
|
|
jasreb
Joined: 03 Dec 2004 Posts: 41
|
Posted: Fri Oct 05, 2007 12:32 pm Post subject: |
|
|
this is the neatest/most compact compiled I have come up with so far:
Code: |
Config Adc = Single , Prescaler = 16 ', Reference = vcc
Start Adc
Dim Vadc As Word '10-bit ADC reading fullscale = 1024 ie 5V
Dim Vbatt As Word 'actual battery voltage to measure/display
Dim Units As Byte 'units of measurement
Dim Decimals As Byte 'decimals to 2 places
Progstart:
Open "comb.0:9600,8,n,1" For Output As #1 'to display results
Vadc = Getadc(5) 'use ADC(5) input on pin 8
Vbatt = Vadc * 5 'start with the x5 multiplier for 5V ref
Shift Vbatt , Right , 8 'divide by 256, result is integer
Units = Vbatt 'assign word Vbatt to byte Units
Print #1 , "vbatt (units) = " ; Units
'now work out decimals, first use MOD to get remainder:
Vbatt = Vadc * 5 'use original ADC value again with the x5 multiplier for 5V ref
Vbatt = Vbatt Mod 256 'divide by 256, result is remainder for decimals
Vbatt = Vbatt * 100 'going to work out the 2 decimal places now
Shift Vbatt , Right , 8 'divide by 256 again to get decimals (integer result)
Decimals = Vbatt
Print #1 , "vbatt (decimals) = " ; Decimals
Close #1
Wait 3
Goto Progstart
End |
seems to work nicely. Changing the " * 100 " to " * 10 " gives simple way of getting 1 decimal place instead of 2 for variable 'decimals' |
|
Back to top |
|
|
|