Reputation: 11
I am a rookie in assembly programming and I am currently doing a project using a temperature sensor on MPLAB and it's values to be displayed on a LCD.
In order to convert the raw values (0 to 255 based on the output voltage) from the sensor to the LCD I need to do an affine function (8x+1000)/100. But here is the problem, since my microprocessor is in 8 bit, I cannot divide by 100.
Here is the code :
MOVLW d'50'
MOVWF CAN_Val_Brut;----------------------------------MULTIPLICATION
clrf CAN_Val_Dec
clrf CAN_Val_Decp1
movlw .8
movwf mpy_cnt
movlw .8
bcf STATUS,C
mpy rrf CAN_Val_Brut,f
btfsc STATUS,C
addwf CAN_Val_Decp1,f
rrf CAN_Val_Decp1,f
rrf CAN_Val_Dec,f
decfsz mpy_cnt,f
goto mpy
;-------------------------------------------------------------ADDITION
;------Je met 1000 dans a et aplus1----
movlw .3;00000011
movwf aplus1
movlw .232;11101000
movwf a
movf a,w
addwf CAN_Val_Dec,f
btfsc STATUS,C
incf CAN_Val_Decp1,f
movf aplus1,w
addwf CAN_Val_Decp1,f
;-----------------------------------------------DIVISION
movlw .8
movwf dvy_cnt
movlw .100
bcf STATUS,C
dvy
btfsc STATUS,C
addwf CAN_Val_Decp1,f
rlf CAN_Val_Decp1,f
rlf CAN_Val_Dec,f
decfsz dvy_cnt,f
goto dvy
If someone have a code for 2x8bits division I am glad to take a look :)
Thank you for your help !
Upvotes: 1
Views: 433
Reputation: 37222
My 1st step would be to realize that (8x + 1000)/100
is the same as 8x/100 + 1000/100
, or x/12.5 + 10
or x*0.08 + 10
.
Of course integer maths doesn't like 0.08
. We can convert it to shifts and additions using integers. In binary, 0.08 is 0.00010100011110101110000101000111...b. This means that we can convert x*0.08
into something like x*0.0001b + x*0.000001b + x*0.0000000001 + ...
, where the multiplications can be done with right shifts. This gives us something like x>>4 + x>>6 + x>>10 + x>>11 + x>>12 + x>>13 + x>>15 ... + 10
.
There are 2 problems with this. The first problem is rounding - e.g. if x
is an 8-bit value, then x>>10
will be zero (all of the bits in x
will be shifted out), which will increase "error due to rounding". The second problem is that 0.08 in binary is an infinite number of bits, so you must choose where to stop and must have some precision loss.
For the 2nd problem, for your purposes (where x
is a relatively imprecise number to begin with) 0.000101001b (or 0.0001010001111 rounded up a little) is likely to be plenty of precision. This gives us x>>4 + x>>6 + x>>9 + 10
.
For the first problem, we can improve precision by doing addition before shifting. x>>9 + x>>6 + x>>4 + 10
is the same as x>>3>>6 + x>>6 + x>>4 + 10
or (x>>3 + x)>>6 + x>>4 + 10
or (x>>3 + x)>>2>>4 + x>>4 + 10
or ((x>>3 + x)>>2 + x)>>4 + 10
.
For an example (and to check that the results are almost identical to the original formula) we can test it with values of x
while being careful to do integer operations that truncate. For x = 255
we get (8*255 + 1000)/100 = 3040/100 = 30
and ((255>>3 + 255)>>2 + 255)>>4 + 10 = ((31 + 255)>>2 + 255)>>4 + 10 = (71 + 255)>>4 + 10 = 30 + 10 = 30
.
Note that the intermediate values will require 1 more bit than x
has; or if x
is 8 bits then you will need 9 bits to store something like x>>3 + x
. This is easily solved by using the carry flag as the extra (ninth) bit. Specifically, the formula does the addition of a pair of 8 bit values to get "carry + 8 bit" result, and then you can use an RRF instruction ("rotate right by 1 bit with carry" to reduce it back to 8 bits before using a normal shift for the rest of the bits (where normal shifts don't seem to exist in PIC assembly - I guess "shift right by N" becomes a "mask N-1 low bits, clear carry, then do N rotates with carry that's never set". You don't need any 16-bit maths (if x
is 8-bit).
Upvotes: 3