Prop1 SPIN: Binary manipulation 101

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.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

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,757
    edited 2020-09-15 - 01:01:20
    You can shift an individual bit by XORing an otherwise blank pattern with %__11__ at the bit position. For example,

    %1111_1011 XOR %0000_1100 = %1111_0111

    -Phil
  • dgatelydgately Posts: 1,304
    edited 2020-09-16 - 00:22:15
    So, "ROL %1111_1011, 8" won't give the same result? Just curious...

    EDIT: Ah! ROL works with longs... Thx
    dgately
  • JonnyMacJonnyMac Posts: 7,008
    edited 2020-09-15 - 03:04:20
    I don't think so, because rotate instructions work with longs.
  • Coming here with no spin1 knowledge and minimal binary knowledge.

    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
    
  • Possible answer: Dont fight the 32-bit nature. Embrace it. Do a loop of 7 shifts followed by a rotate to get your upper bit back into the LSB. Janky pseudocode looks like this (bcuz I'm stuck in traffic on my phone):
    pattern = $0000_0001
    do
       repeat 7x
          {
          SHL pattern, 1
          (delay)
          }
        ROL pattern, 26
         (delay)
    loop
    
  • Cluso99Cluso99 Posts: 16,676
    edited 2020-09-15 - 21:06:15
    Along the lines of previous comments, provided you only have one 0, you can rotate left and then check if the 0 is in the 8th slot and if so rotate left 24 bits.
    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)
    
  • One could create a ROL8 instruction like this
    pub rol8(byteVal, bits) : result
    
      result.byte[1] := result.byte[0] := byteVal.byte[0]
      result << (bits & %111)
      result := result.byte[1]
    
  • ...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

    That seems inconsistent with the patterns for 2 yellows. Does a 0 bit signify on or off?
  • WBA ConsultingWBA Consulting Posts: 2,918
    edited 2020-09-16 - 08:56:03
    Thanks everyone for all the input!
    You can shift an individual bit by XORing an otherwise blank pattern with %__11__ at the bit position. For example,

    %1111_1011 XOR %0000_1100 = %1111_0111

    -Phil

    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.
  • You’re ANDing the immediate with the parameter long to extract the specific bit position. This is b7 and b25 because you need to skip these positions as there is no led in those positions. If at either position then we need to do another shift.
  • mparkmpark Posts: 1,230
    edited 2020-09-16 - 21:31:44
    @WBA Consulting, I still think your 3 greens example has Low and High swapped.

    (the rest deleted, I misread "-" for "_"!)
  • Cluso99Cluso99 Posts: 16,676
    edited 2020-09-16 - 20:57:36
    I thought this was the aim - a "KIT" car like moving single led on rotating pattern.
    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...
    
  • WBA ConsultingWBA Consulting Posts: 2,918
    edited 2020-09-18 - 06:23:18
    mpark, now I see my error, the patterns I put there would be for 4 greens, not 3. When I originally started that post, I was going to do 4 in my example, but then changed the pattern to 3 in the text, but didn't update the bits.

    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.



  • Update:
    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.
  • Gona' make a video or is the product proprietary?
  • WBA ConsultingWBA Consulting Posts: 2,918
    edited 2020-09-24 - 06:04:51
    Publison, yes, proprietary and NDA are in the way for a full video showing details. However, I was able to make a snippet so that my final code can be seen in action. Hopefully it attaches well to the forum. The red LEDs look brighter simply because you are also seeing the light from the side of the LED, not just the top, like the others.
Sign In or Register to comment.