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.

How to handle a 16bit number and get average?

Status
Not open for further replies.

bsodmike

New Member
Right. I have the need to say add 2 numbers, 255 + 255 (the highest value from a A/D read) and get the average / 2.

Basically I want to data log say about 20 - 100 samples and get an average.

Thanks!

Mike
 
Okay, Bob just told me that RRF 'var' does a div by 2, makes sense ;) So all I need now is how do I add stuff to 2bytes, then RRF them 2 bytes and take the answer back into a single byte (8bits)??
 
As you will be going over an 8 bit result (in some cases) you need 16 bit maths routines, or at least 8 to 16 bit maths routines.

If you look on the PICList there are loads of maths routines for you to examine or use 'as is'. A 16 bit addition followed by a 16 bit shift is quite simple to do.
 
Code:
c_set	rrf	w
	addlw	b'10000000'
	goto	c_done


sample	movfw	avg
	movwf	sample1

	movfw	_adres		;sample 2
	
	addfw	sample1,w	;add sample1 to sample2, store result in w.

	btfsc	_status,0	;if carry is set...
	goto	c_set

	rrf	w	

c_done	movwf 	avg

TSample
	movlw	d'100'		; load value 5
	movwf	Cnt		; into file Cnt
TS1
	call	Sample		; waits 1 second
	decfsz	Cnt		; does this 5 times
	goto	TS1		; loop till Cnt=0

Hello there Nigel! I was just asking your Bob (of Modasylum) for assistance. As the max value of this 16bit is 255+255, bob noticed it's only 9bits, and to do something along these lines...

Tell me what you think ;)

edit: bob = fluba from robotbuilder
 
Nicer code:

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

c_set	rrf	w,w		; rotate accu right 1 (div by 2), put in w
	addlw	b'10000000'	; add to existing value of w (due to carry)
	goto	c_done		; by-pass 'rrf' if c isn't set.

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

measure	movlw	d'100'		; 
	movwf	cnt		; 

	movfw	_adres		; take the very first sample
	movwf	avg		; put it in avg

loop	movfw	avg
	movwf	sample1

	movfw	_adres		; sample 2

	addwf	sample1,w	; add sample1 to sample2, store result in w.

	btfsc	_status,0	; if carry is set...
	goto	c_set
	rrf	w,w	

c_done	movwf 	avg

	decfsz	cnt,f		; 
	goto	loop		; loop till cnt=0

	movfw	avg		; copy the final avg sample to accu

	return			; return without altering accu value
 
How about something simpler?, you don't need to do all the carry testing:

Code:
movwf   sample1 

   movfw   _adres      ; sample 2 

   addwf   _adres, f   ; add sample1 to sample2, store result in _adres. 
   rrf   _adres, f        ; rotate right, including carry, result in _adres

The rrf instruction rotates right including the carry, so it automatically inserts the carry for you.

I haven't checked or tested this, but I would expect it to work fine - assuming I've mad no mistakes of course!.
 
Yep.

How it works:

loop:
Take sample1
Take sample2
sample1 + 2 /2 = avg

take sample3
sample 3 + avg/2 = avg
goto loop.

I return from the main 'loop' without clearing the accu so the final avg will still be in it...
 
Oversampling with 8bits and A/D

Dear Nigel,

Thanks for the tip. I'm using sample1/2 for clarity....could do it your way of course.

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

measure	movlw	d'10'		; This defines the number of samples taken
	movwf	cnt		; 10 * 8µs (sample time) = 96µs (inc +20%)

	movfw	_adres		; take the very first sample
	movwf	avg		; put it in avg

loop	movfw	avg		; current/previous avg stored in sample1
	movwf	sample1

	movfw	_adres		; take new sample and store in accu

	addwf	sample1,w	; add sample1 to sample2, store result in w

	rrf	w,f		; rotate right including carry, result in w

	movwf 	avg		; avg is used as sample1 when cnt != 0

	decfsz	cnt,f		; perform cnt - 1, skip next if result is 0
	goto	loop		; cnt != 0 so take another reading and avg

	movfw	avg		; copy the final avg sample to accu

	return			; return without altering accu value

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
Hey bsodmike! that won't give you an 'average' result of many numbers!!!
Take numbers for example...
15, 12, 37
Average equals 15+12+37 = 64 / 3 = 21.33* Av.

Now the way you are considering...

15+12/2 = 13.5+37/2 = 25.25!!!!

This is only a small set of small numbers, any larger and you might as well get a random number generator guess the value!

When I first designed a bit of code to work out average I also fell for this trap, until I actually tested it out on paper and realised it wouldn't work so I had to rethink my stratagy a little!
 
Ouch. You are correct.

right now I've dropped the oversampling to 5 readings.

Say I take the following data set:

5v, 4v, 5v, 2v, 3v = avg is 3.8, so say 4v

the wrong way as I've attempted: 5+4 = 9/2 = 4+5 = 9/2 = 4+2 = 6/2 = 3+3 = 6/2 = 3v

(I'm rounding off as this is how it would work in binary...) a complete 1v error

How would I do this then?

[edit] Thought of a silly way but seems to work for 1set I tried:

Consider:

(5+4/2) = 4 but make it 5
(5+5/2) = 5 but make it 6
(6+2/2) = 4 but make it 5
(5+3/2) = 4v

hrm...not given this much thought but then again it *is* 1:39am :p

Any help would be greatly appreciated!
 
I don't really want to tell you cause I think half the fun of programming is in working the problems out :wink:
here's a clue though, try to think of how you could 'divide' the total without actually using a division command. You may have to keep a tally of 'somthing' that you haven't considered doing or required so far....
 
Well I could keep a tally of the number of samples I take, or I can even take that for granted as I know I'm only taking 5 readings... but each rrf would result in a div by 2.....

bah.... :(
 
but each rrf would result in a div by 2.....
forget rotate commands - in the sense of using them for the actal averaging maths that is!
You are right about the first suggestion, and getting there but you should also know by now processors don't always take things for granted in quite the same way us humans do!
 
Something like this:

Procedure: Double the divisor until it is just less than the divi-
dend. Then try to subtract the doubled divisors, starting with the
largest, from the dividend. Note a 1 if the subtraction is possible
otherwise, note a zero and do not perform the subtraction.
The 1s and Os constitute the binary form of the quotient. To ob-
tain the decimal form, multiply the latter digits with the corre-
sponding terms in a power of 2 series, arranged in reverse order.
The quotient is the sum of the resultant terms.

To obtain decimal accuracy, multiply the dividend initially by an
Nth power of 10. Then, after the division is complete, divide the
quotient by the same power of 10 (moving the decimal point N
places).

Code:
Example: 2246/51 Counter 
Double: 51
   0 
 102
   1 
 204
   2 
 408
   3 
 816
   4 
 1632
   5 
Subtract: 2246
    
 -1632
    
 614
 1 X 32 = 32 5 
 -816
    
  0 x 16 = 0 4 
 614
    
 -408
    
 206
 1 x 8 = 8 3 
 -204
    
 2
 1 x 4 = 4 2 
 -102
    
  0 x 2 = 0 1 
 2
    
 -51
    
  0 x 1 = O 0 
Remainder: 2
    
Quotient:   101100 = 44

?!?
I will attempt to code this..ohh boy...
 
I took a more simplistic approach, but bear in mind I wasn't dealing with decimal places so to speak, but did manage to round up or down to the nearest correct figure with a little routine at the end of each calculation. That 'proceedure' does seem a little heavy to digest, and isn't the way I approached the problem!
 
Bah!

Okay, as you can see the solution is simply evading me. Once you tell me i'll be like 'D'oh! why didn't I think of that!'... but that is if you ever will. I need to test this code asap, so I'll over sample by and extra sample simply as a+b, rrf w,f

Please tell me as I would (at least in the future) like to oversample by 5 - 10 sets of readings (10 - 20 samples and their average).

Ohh something I didn't tell you. I'm not just dealing with '5' etc, I'm dealing with the 8bit A/D unit.

So the max reading is 255, so if I were to add up 5 samples we are already in the land of a 16bit number - and for the life of me I can not see how I can divide this through.

As you said a rounded off value is all I need ; nothing with floating points.
 
bsodmike said:
So the max reading is 255, so if I were to add up 5 samples we are already in the land of a 16bit number - and for the life of me I can not see how I can divide this through.

As you said a rounded off value is all I need ; nothing with floating points.

As already suggested, simply treat them as pairs of numbers, add two together, them divide the result by two (by right shifting). Then add the next number to that result, and divide by two again. Keep doing that until all the numbers are processed - the result is tha average of all the numbers.

To divide a 16 bit number you need maths routines, you can find plenty of those on the PICList - but for this simple problem all you need is ADDWF and RRF.
 
Had another chat with Bob and we came up with this :

This is pretty much what nigel suggested but a little different...

Code:
measure	movlw	d'3'		; This defines the number of samples taken
	movwf	cnt		; excluding the 'first sample'

	movfw	_adres		; read the very first sample and store it
	movwf	sum		; in the variable 'sum'

loop	movfw	_adres		; read the second sample into accu	
	addwf	sum,f		; add both together, put answer back in sum
	btfsc	_status,0	; check carry bit for overflow
	incf 	overflow	; if carry bit is set, inc overflow by 1

	decfsz	cnt,f		; perform cnt - 1, skip next if result is 0
	goto	loop		; cnt != 0 so loop back for another sample

	rrf 	overflow	; shift the overflow
	rrf	sum		; rrf carry (div by 2)
	rrf 	overflow	; shift the overflow
	rrf 	sum		; rrf carry (div by 2)

	movfw	sum		; copy the average to accu

	return			; return without altering accu value

This is how it works:

**broken link removed**
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top