Prop1 SPIN: Binary manipulation 101

in Propeller 1
I am updating a test fixture that I had originally developed in 2014 from this old thread. While I have already updated the firmware for the new requirements this past weekend, I have an idea to make another change to the tester to make it more visually appealing since the pass/fail is determined by an operator viewing the 56 LEDs. The tester Prop1 chip is powered up after the DUT is inserted, so the program starts from scratch at each test and my program is designed that way.
Some background: The PCBA being tested has 4 Left/Right banks of VU Meter style LEDs, 4 green, 2 yellow, and 1 red that are driven by MAX7313 ICs. The two banks are referred to the Low bank (L) and High bank (H). See the original thread for more details, but what I would like to do is get a little more "proper" with my binary data that is being sent to the MAX7313s so that it is easier to show rolling/sequential patterns across the banks. I stripped down my code to explain what I am currently doing:
Main Program Code (this version would cycle the MAX7313 at address $20 from only red on to full off repeatedly, but always leaving the unused bit off)
MAX7313 Object (key portions only!)
So, with all that said, the pattern for each bank of LEDs is below. Remember, these are Left/Right banks of 7 LEDs each, with one output of each output bank not used (the "-" in the pattern definition):
Here is a pattern for just the two yellows on:
The Left (Low) and Right (High) bank positions of LEDs are inverted in relation to the MAX7313 outputs, so, 3 Greens on for Low and High would be %1000_0111 for the Low bank and %1111_0000 for the High bank. The first bit of "1" is for the unused output so it is always "1" in any pattern so that it remains off
The dilemma that is mocking my lack of binary manipulation knowledge is as follows:
Here is what I want to do: Rotate a single "on" LED through the bank, somewhat fast. So visually, an operator can see that each one lights individually. I know that to do that, I should be able to do some straightforward binary manipulation so I don't need a CON list of 8 patterns. What is blocking my train of thought is the challenge with dealing with the unused output between the channels.
Is there an easy way to flip the Low bank value, shift over by one and then make the first bit a 1 to get the High bank value of the same pattern?
I am thinking using a mask to OR with the original value to set the first "1" each time. Is that wrong?
I believe I can do a basic shift operation to move the single "0" through the pattern to light one at a time, but the issue of aligning it against that one unused inverted bit is confusing.
My dream pseudocode would be
Any and all help is appreciated.
Some background: The PCBA being tested has 4 Left/Right banks of VU Meter style LEDs, 4 green, 2 yellow, and 1 red that are driven by MAX7313 ICs. The two banks are referred to the Low bank (L) and High bank (H). See the original thread for more details, but what I would like to do is get a little more "proper" with my binary data that is being sent to the MAX7313s so that it is easier to show rolling/sequential patterns across the banks. I stripped down my code to explain what I am currently doing:
Main Program Code (this version would cycle the MAX7313 at address $20 from only red on to full off repeatedly, but always leaving the unused bit off)
MAX7313.init_A(i2cSDA, i2cSCL,false)
MAX7313.Setup_A($20)
repeat
MAX7313.LED_BLANK_A($20)
waitcnt(clkfreq/5 + cnt)
MAX7313.LED_RED_A($20)
waitcnt(clkfreq/5 + cnt)
MAX7313 Object (key portions only!)
CON
BLANK_L = %1111_1111
BLANK_H = %1111_1111
RED_L = %1011_1111
RED_H = %1111_1110
PUB LED_BLANK_A(devaddress) | dev_addr
dev_addr := devaddress << 1 | 0
i2cObject.i2cStart
i2cObject.i2cWrite(dev_addr | 0,8)
i2cObject.i2cWrite(PORTS7to0 | 0,8) '0x06
i2cObject.i2cWrite(BLANK_L | 0,8) ' Send Pattern
i2cObject.i2cWrite(BLANK_H | 0,8) ' Send Pattern (auto increments to 0x07)
i2cObject.i2cStop
PUB LED_RED_A(devaddress) | dev_addr
dev_addr := devaddress << 1 | 0
i2cObject.i2cStart
i2cObject.i2cWrite(dev_addr | 0,8)
i2cObject.i2cWrite(PORTS7to0 | 0,8) '0x06
i2cObject.i2cWrite(RED_L | 0,8) ' Send Pattern
i2cObject.i2cWrite(RED_H | 0,8) ' Send Pattern (auto increments to 0x07)
i2cObject.i2cStop
So, with all that said, the pattern for each bank of LEDs is below. Remember, these are Left/Right banks of 7 LEDs each, with one output of each output bank not used (the "-" in the pattern definition):
LEDs P7-0 "_L" %1000_0000
-RYY_GGGG
LEDs P15-8 "_H" %1000_0000
-GGG_GYYR
Here is a pattern for just the two yellows on:
YELLOW_L = %1100_1111
' -RYY_GGGG
YELLOW_H = %1111_1001
' -GGG_GYYR
The Left (Low) and Right (High) bank positions of LEDs are inverted in relation to the MAX7313 outputs, so, 3 Greens on for Low and High would be %1000_0111 for the Low bank and %1111_0000 for the High bank. The first bit of "1" is for the unused output so it is always "1" in any pattern so that it remains off
The dilemma that is mocking my lack of binary manipulation knowledge is as follows:
Here is what I want to do: Rotate a single "on" LED through the bank, somewhat fast. So visually, an operator can see that each one lights individually. I know that to do that, I should be able to do some straightforward binary manipulation so I don't need a CON list of 8 patterns. What is blocking my train of thought is the challenge with dealing with the unused output between the channels.
Is there an easy way to flip the Low bank value, shift over by one and then make the first bit a 1 to get the High bank value of the same pattern?
I am thinking using a mask to OR with the original value to set the first "1" each time. Is that wrong?
I believe I can do a basic shift operation to move the single "0" through the pattern to light one at a time, but the issue of aligning it against that one unused inverted bit is confusing.
My dream pseudocode would be
Init I2c
Setup/7313
Call a routine that repeatedly sends the same variable as the pattern and that variable that gets manipulated each cycle to rotate the "0" through the pattern appropriately.
Any and all help is appreciated.
Comments
%1111_1011 XOR %0000_1100 = %1111_0111
-Phil
EDIT: Ah! ROL works with longs... Thx
dgately
If you have an 8 bit value 1, 000000001, you can shift that one bit left by multiplying it by 2, 00000010. The pattern continues until the values roll over to 0, so you will need to check every iteration if the value = 0 and if it does, add 1 instead of multiplying by 2.
00000000 + 00000001 = 00000001 #add 1 because it equals zero 00000001 * 00000010 = 00000010 #multiply by two otherwise 00000010 * 00000010 = 00000100 00000100 * 00000010 = 00001000 00001000 * 00000010 = 00010000 00010000 * 00000010 = 00100000 00100000 * 00000010 = 01000000 01000000 * 00000010 = 10000000 10000000 * 00000010 = 00000000 #next step would be to add 1
pattern = $0000_0001 do repeat 7x { SHL pattern, 1 (delay) } ROL pattern, 26 (delay) loop
Something similar to this will work (I forget the ROL function so I have just used SHL instead)
pattern << 1 if pattern & $100 == 0 pattern := $FFFF_FFFE
Just re-read your original post. So here is a routine using 16 bits of the pattern long where lower 8 bits for one LED group and next 8 bits for the next.
pattern << 1 if pattern & $10000 == 0 pattern := $FFFF_FFFE ‘now skip over the two missing LEDs if (pattern & $80 == 0) or (pattern & $8000 == 0) pattern << 1 outa := pattern ‘assumes only p0-15 are outputs (to drive LEDs and p7 and p15 are unused)
pub rol8(byteVal, bits) : result result.byte[1] := result.byte[0] := byteVal.byte[0] result << (bits & %111) result := result.byte[1]
That seems inconsistent with the patterns for 2 yellows. Does a 0 bit signify on or off?
Phil, so it looks like I would just need to change the value of what I XOR with the pattern each time to keep pushing the 0 across the pattern. Sounds fairly straightforward and at least makes sense to me.
JRoark, JonnyMac stated that ROL only works with longs, so not sure how to change yours for that
Cluso, That makes some sense to me, but I need to learn why you are using $80 and $8000 so I fully understand. I haven't touched SPIN for nerly a yer, so a little rusty.
JonnyMac, I think I am following your code correctly, so I can see how that could be useful, IF I am able to understand it well enough
mpark, a 0 is on and 1 is off. The first bit is always 1 because that port isn't used, thus remains off all the time.
I won't be able to get the tester back in my hands until Thursday, but I am updating some code for testing variations.
(the rest deleted, I misread "-" for "_"!)
Perhaps I miss-read the requirement.
High Low 111111 54321098 76543210 -------------------- 0 11111111 11111110 1 11111111 11111101 2 11111111 11111011 3 11111111 11110111 4 11111111 11101111 5 11111111 11011111 6 11111111 10111111 11111111 01111111 <--- invalid, so skip this state 7 11111110 11111111 8 11111101 11111111 9 11111011 11111111 10 11110111 11111111 11 11101111 11111111 12 11011111 11111111 13 10111111 11111111 01111111 11111111 <--- invalid, so skip back to state 0 ^--------^---------no LEDs and repeat...
Cluso, the pattern I am after is simply a single LED starting at green and going to the red, then repeating. To my understanding, I can't send Low and High bits together as a single data segment, they have to be sent separately, but I could be wrong. I will play around with it as a Long, rather than two Words.
Thanks for the pattern example, I used that method to show what I have been trying to explain:
Low High 0 11111110 10111111 1 11111101 11011111 2 11111011 11101111 3 11110111 11110111 4 11101111 11111011 5 11011111 11111101 6 10111111 11111110 7 11111111 11111111 -RYYGGGG -GGGGYYR
I realized that I can actually make use of the unused LED and just cycle the "ON" right through it rather than trying to figure out how to work around it. (that pin is not connected to anything on the PCBA)
So, my pattern can be:
Low High 0 11111110 10111111 1 11111101 11011111 2 11111011 11101111 3 11110111 11110111 4 11101111 11111011 5 11011111 11111101 6 10111111 11111110 7 01111111 01111111
Production finished the current batch of boards so I will run some new code through it over the weekend.
I tried several different options from the different suggestions and while doing so, I realized that the idea of a Knight Rider chasing LED pattern would actually meet my goals. I also realized that the reason I was having so much trouble was due to the inverted nature (1 is off and 0 is on) so I simply used a starting pattern that was opposite to make use of the shifting commands easily and then inverted it when I sent it to the MAX7313. With all that said, my Main code looks like this:
pub Main | dev_addr MAX7313.init(i2cSDA, i2cSCL,false) ' Initialize I2C Start_L := %0000_0001 ' Set pattern start (inverted) Start_H := %0100_0000 Repeat dev_addr from $20 to $23 ' Initialize MAX7313 from address $20 to $23 MAX7313.Setup(dev_addr) Repeat 'Main Repeat loop of program Repeat 6 'Repeat loop for LEDs going up Repeat dev_addr from $20 to $23 MAX7313.LED_BINARY(dev_addr, Start_L, Start_H) ' Send pattern to Binary method Start_L <<= 1 ' Shift low bank pattern bits to the left Start_H >>= 1 ' Shift high bank pattern bits to the right waitcnt(clkfreq/8 + cnt) Repeat 6 'Repeat loop for LEDs going down Repeat dev_addr from $20 to $23 MAX7313.LED_BINARY(dev_addr, Start_L, Start_H) ' Send pattern to Binary method Start_L >>= 1 ' Shift low bank pattern bits to the left Start_H <<= 1 ' Shift high bank pattern bits to the right waitcnt(clkfreq/8 + cnt)
And my LED_BINARY method looks like this:
PUB LED_BINARY(devaddress, Start_L, Start_H) | dev_addr, Pattern_L, Pattern_H dev_addr := devaddress << 1 | 0 Pattern_L := !Start_L Pattern_H := !Start_H i2cObject.i2cStart i2cObject.i2cWrite(dev_addr | 0,8) i2cObject.i2cWrite(PORTS7to0 | 0,8) '0x06 i2cObject.i2cWrite(Pattern_L | 0,8) ' Send Pattern i2cObject.i2cWrite(Pattern_H | 0,8) ' Send Pattern (auto increments to 0x07) i2cObject.i2cStop
Everything looks good and it is now very easy to verify all LEDs in a few seconds.