mind-bending logic problem

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have been struggling with a logic problem that seems simple but turns into a mind-bender.

I have a free running counter cycling 0-255 and wrapping around back to zero. So it just keeps counting along, incremented by an interrupt.

I'd like to have a variable software timer generated from this as follows. I have a two bit number set to either 0,1,2,3

when the number is 0 the timer should react at every count (ie all free running counts are valid)
when the number is 1, the timer should react at every other free running count (eg 1,3,5,7,9,11...) or (2,4,6,8...)
when the number is 2, the timer should react every 4 counts-I don't care what the counts are, as long as it divides by4
when the number is 3 , the timer should react every 8 counts

I'd like to not have to reset the free running counter, or add a secondary counter of any sort. I'm hoping to take the freee running count & the two bit number and apply some logic (and,or,xor...) & maybe some shifting operations to determine if a valid time is present in the free running counter. Any thoughts are welcome.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

here is something I came up with...however, it seems quite complex:

;count=free runnning counter
;num=divider (0,1,2,3) (div by 1,2,4,8)

ldi temp1, count
ldi temp2, num
mov temp3, num
loop: cp temp2, 0
breq done
lsr temp1
sbc temp3, 0
dec temp2
rjmp loop
done: cp temp3, 0
breq time_valid
....time not valid...

this basically checks for 0,1,2, or 3 bits set in the LSB's depending upon num

I was hoping for something much more straightforward!! any thoughts??

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Edit: This code listing is broken :(

avrcandies wrote:
when the number is 0 the timer should react at every count (ie all free running counts are valid)
when the number is 1, the timer should react at every other free running count (eg 1,3,5,7,9,11...) or (2,4,6,8...)
when the number is 2, the timer should react every 4 counts-I don't care what the counts are, as long as it divides by4
when the number is 3 , the timer should react every 8 counts

Since you scale the timer with sequential powers of 2, you can use your 2-bit number (which I am calling "scale") to tell you which bit to check in the timer. First check if scale is 0. Your routine always fires if scale is 0. Then check bit (scale-1) in the timer. If it is 0, your routine should fire.

You didn't say if you are using C, assembly, or BASIC. Anyhow, I wrote a small piece of code that will branch to DoStuff when action should be taken, and will jump to Exit when no action should be taken. the RJMP to Exit could be replaced with RETI if you dont need to do anything else before leaving the ISR.

Note that this code is destructive to the "scale" and "timer" registers, so you should work on copies of your actual scale and timer registers

.def scale = r10  ; 2-bit number
.def timer = r17  ; 8-bit timer

   lsl timer      ; shift timer left
Loop:
   tst scale      ; check scale
   brne DecScale  ; only test bit 0 of timer if scale is 0
   sbrs timer, 0  ; check bit 0 of timer
   rjmp DoStuff   ; if bit 0 is clear, do stuff
   rjmp Exit      ; bit 0 is set, exit

DecScale:
   dec scale      ; decrement scale
   lsr timer      ; shift timer right
   rjmp Loop

/* John Butera */

Last Edited: Sat. Aug 7, 2004 - 06:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Edit: This code listing is broken too :(

If you don't want to sit in a loop, you could just check all four cases of scale.

This has the advantage that you don't need to make copies of your scale and timer registers.

tst  scale    ; if (scale == 0)
breq DoStuff  ;   DoStuff();

cpi  scale, 1 ; if (scale == 1)
brne Scale2   ; {
sbrs timer, 0 ;   if (timer.0 == 0)
rjmp DoStuff  ;     DoStuff();
              ; }
Scale2:
cpi  scale, 2 ; if (scale == 2)
brne Scale3   ; {
sbrs timer, 1 ;   if (timer.1 == 0)
rjmp DoStuff  ;     DoStuff();
              ; }
Scale3:
sbrs timer, 2 ; if (timer.2 == 0)
rjmp DoStuff  ;   DoStuff();

rjmp Exit

/* John Butera */

Last Edited: Sat. Aug 7, 2004 - 06:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can check it by doing logic AND with some mask:

lds r24,timer
lds r25,mask
and r24,r25
brne do_nothing

;do some action

do_nothing:

Or in C:

if ((timer&mask)==0) {do some action};

For every count mask=0,for every 2nd mask=1,for every 4th mask=3,for every 8th mask=7 and so on (mask=(2^n)-1,where n=0,1,2,3..8 ).Mask can be calculated by shifting 1 left n times.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for all of the help. Sorry to say pepsi/John, but I think both of your routines would have a problem (I ran into this same insane logic--thats why I called it a mind-bender).

Your routines only end up checking 1 bit in the counter (either bit none, 0, 1, or 2) depending on the scale. This is correct for checking none or 1 bit. Checking bit 1 does give a divide by two effect. HOWever, checking only counter bit 2 still gives an effective divide by two (rather than the desired divide by 4). In fact checking any single bit will give an effective divide by two---what is interesting is that the "do somethings" merely get lumped together differently. Depening on which single bit is used, the average rate stays at 50% & while the is "do something" frequncy is changed. Imagine checking only the MSB (bit 7). Nothing happens for the first 128 counts, then happens every time for the next 128 counts----rather than the desired alternatiing an action every other count.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

Is there any reason you don't want to use another variable or secondary counter? It would be a lot easier if you used another small variable as a three-bit counter or something.

Regards,

-Colin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I set this up to test for: nothing, 0, 00, or 000 depending on scale. Interestingly, would get the same (correct) results testing for: nothing, any 1 bit number, any two bit number, any three bit number depending on scale. Note the 1,2,3, bit numbers are right justified.
So you could, for example, check for nothing, 0, 10, 010

mov temp2, timer ;timer is free running isr counter
move temp1, scale ; two bit scaling factor (selects action every 1,2,4, or 8 counts)
loop: tst temp1
breq do_event ;proper time to do the action--if needed # of 0's detected
dec temp1 ;"reverse count" number of bits to check
lsr temp2 ;check for another 0 set
brcc loop ;if not enough zeros (or could use ones) set, drop out of check
skip event

I don't want to say/admit how many hours I spent messing with this!
Thanks to everyone who helped give me the food for thought to figure this out

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh, you're right, both of my code listings are broken :(

/* John Butera */

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually URRIZ's solution is correct. Since it is checking all the bits up to the divisor:

he is calculating the mask as follows:
(2^n)-1

for n=0
(2^0)-1
(1)-1
0 = 0b00000000
using this mask by AND'ing it with your count will always be zero, resulting in every count to be processed

for n=1
(2^1)-1
(2)-1
1 = 0b00000001
using this mask by AND'ing it with your count will always be zero for every other count

for n=2
(2^2)-1
(4)-1
3 = 0b00000011
using this mask by AND'ing it with your count will always be zero for every fourth count

for n=3
(2^3)-1
(8)-1
7 = 0b00000111
using this mask by AND'ing it with your count will always be zero for every eighth count

Here is how to generate the mask in assembler

; function called to change the prescale value
;   on call: r16 holds the passed prescale value
; on return: r16 holds the test mask
setmask:
        push    r17         ; preserve r17, as we will use it
        ldi     r17, $1     ; load in the starting value (2^0)
        tst     r16         ; set the flags for the loop
loop:   breq    done
        lsl     r17         ; shift the power
        dec     r16         ; decay the count
        rjmp    loop
done:   mov     r16, r17    ; copy result to r16
        subi    r16, $1     ; create the mask
        pop     r17         ; restore r17
        ret

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Depending on what you exactly you can also take a look at the Timer project in the academy, but it is written in GCC and not in ASM.