Maker Pro
Maker Pro

Two Wire Serial bit-bang Dyslexia

Fish4Fun

So long, and Thanks for all the Fish!
My Dyslexia is killing me and I need some help .... I can't remember the relationship between time and left-right orientation in the timing diagram ... I know this seems ridiculous, but it is the world I live in. In an effort to make clear what I am asking here are the two most likely scenarios as I see them:

****************************************************************************************************
One:

With Respect To the diagram below, assuming I want to send the 32-bit word $F1020408 (11110001 00000010 00000100 00001000) via a bit-bang routine do I send the lowest bit in the lowest byte first like so:

Code:
;Assume io Pins are Initialized

Tx_F1020408:
sbi   PORT_io, Pin_cki     ;Set Clock Line High
call TxLSB
call TxByte2
call TxByte3
call TxMSB
ret

Tx_LSB:     ;00001000
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_One
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
ret

Tx_Byte2:          ;00000100
call   Tx_Zero
call   Tx_Zero
call   Tx_One
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
ret

Tx_Byte3:          ;00000010
call   Tx_Zero
call   Tx_One
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
ret

Tx_MSB:          ;11110001
call   Tx_One
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_One
call   Tx_One
call   Tx_One
call   Tx_One
ret

Tx_Zero:
;Tx 0
cbi   PORT_io, Pin_sdi      ;Set sdi = 0
cbi   PORT_io, Pin_cki      ;Clock = Low
sbi   PORT_io, Pin_cki      ;Clock = High
ret

Tx_One:
sbi   PORT_io, Pin_sdi      ;Set sdi = 0
cbi   PORT_io, Pin_cki      ;Clock = Low
sbi   PORT_io, Pin_cki      ;Clock = High
ret

****************************************************************************************************
Two:
With Respect To the diagram below, assuming I want to send the 32-bit word $F1020408 (11110001 00000010 00000100 00001000) via a bit-bang routine do I send the Highest bit in the lowest byte first like so:


Code:
;Assume io Pins are Initialized

Tx_F1020408:
sbi   PORT_io, Pin_cki     ;Set Clock Line High
call TxLSB
call TxByte2
call TxByte3
call TxMSB
ret

Tx_LSB:     ;00001000
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_One
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
ret

Tx_Byte2:          ;00000100
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_One
call   Tx_Zero
call   Tx_Zero
ret

Tx_Byte3:          ;00000010
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_One
call   Tx_Zero
ret

Tx_MSB:          ;11110001
call   Tx_One
call   Tx_One
call   Tx_One
call   Tx_One
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_One
ret

Tx_Zero:
;Tx 0
cbi   PORT_io, Pin_sdi      ;Set sdi = 0
cbi   PORT_io, Pin_cki      ;Clock = Low
sbi   PORT_io, Pin_cki      ;Clock = High
ret

Tx_One:
sbi   PORT_io, Pin_sdi      ;Set sdi = 0
cbi   PORT_io, Pin_cki      ;Clock = Low
sbi   PORT_io, Pin_cki      ;Clock = High
ret
****************************************************************************************************

Of course the other two permutations (not detailed above) involve using one of the above two scenarios except sending the High Byte First, but I am 99.99% sure these are NOT the case ....

***AND NO, this is NOT the actual code, just didactic attempts to clarify the order of operations to comply with the protocol in the below diagram.***

Here is the Two-Wire serial timing Diagram (taken from the SK9822 data sheet):

TWI.jpg


I know this seems silly, and I could certainly figure it out experimentally; however, it would be a kindness if someone could simply tell me the answer .... (The irony of asking for the answer to **Time** is not lost on me.)

If anyone is still reading ... the bit-bang routine I am working on is going to be used to control arrays of SK9822 RGB LEDs .... The SK9822's arrays are "daisy chained" together the "Start Sequence" is 32 zeros followed by 32 bits of data for each LED in the array and then terminated by 32 ones ....

I am **assuming** that a routine optimized for refresh rate would immediately begin sending the next "frame" of data immediately following the termination bits .... ie:

Assuming 1024 LEDs in an array ...

Frame.0:
Start = $00000000
Data = 1024 * $xxxxxxxx data bytes
Termination = $FFFFFFFF

Frame.1:
Start = $00000000
Data = 1024 * $xxxxxxxx data bytes
Termination = $FFFFFFFF
.
.
.
.
Frame.N
Start = $00000000
Data = 1024 * $xxxxxxxx data bytes
Termination = $FFFFFFFF
.
.
etc
.
etc

(8256 Total Bits/Frame ... ~4.128mS/Frame (~240 Frames/Sec) @ 2mbps)


But to send a "Static" Frame one would do the following:

Start = $00000000
Data = 1024 * $xxxxxxxx data bytes
Termination = 1024 * $FFFFFFFF
(16416 Total Bits ... ~8.2mS @ 2mbps)

I have three of these 32x8 arrays on order: ( https://www.ebay.com/itm/WS2812B-RG...var=454012843559&_trksid=p2057872.m2749.l2649 ) to experiment with .... I will start a new thread specifically about these ... for now I just wanted to make sure I get the protocol straight in my head so I can get the bit-bang code written before the panels arrive ...

Thanks!

Fish
 

Harald Kapp

Moderator
Moderator
First of all, this more detailed explanation may be of more help than the sparse datasheet.
The interface used is of the SPI type which is explained e.g. in the Wikipedia.

I can't remember the relationship between time and left-right orientation in the timing diagram
Understandable. That's because this may be confusing.
Usually, time advances from left to right, showing earlier events to the left, later events to the right. As e.g. in a historical timeline. This can be interpreted as the events being static and time moving from left to right (my interpretation, at least). This is what is shown in the datasheet of the SK9822.
BUT the diagrams shown on e.g. the Wikipedia site show a different interpretation: time being static and the signals (events) flowing from left to right.Like this:
upload_2020-8-17_8-45-26.png
The red line indicates the static time, the signals are marching waveforms. This interpretation matches the hardware setup also shown on the same page where Bit 7 is shifted out forst from the shift register, followed by bits 6 5, 4 etc.:
1920px-SPI_8-bit_circular_transfer.svg.png


Coming back to your code: Both codes are invalid because both codes work with this sequence:
Code:
call TxLSB
call TxByte2
call TxByte3
call TxMSB
So they will send the LSB first, MSB last whereas the specs require MSB first, LSB last. But here the confusion may be on my side as it is not totally clear what LSB, Byte2, Byte3 and MSB are meant to be. From my point of view it were more meaningful to name thebytes according to function, e.g. for an LED frame:
Code:
call TxGlobal
call TxBlue
call TxGreen
call TxRed
But the naming is totally up to you, of course.

Within the subroutines for the bytes the code from your 2nd example is correct:
Code:
Tx_LSB:     ;00001000
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
call   Tx_One
call   Tx_Zero
call   Tx_Zero
call   Tx_Zero
ret
as it will output the MSB first, LSB last.
 

Fish4Fun

So long, and Thanks for all the Fish!
Thanks Harald!

I truly appreciate you taking the time to detail this for me. I cannot begin to explain how frustrating it is to look @ "dab" and read "bad", or "21" and see "12". While I understand MSB/LSB first conceptually, translating that to a time line with no actual data was killing me.

Again, Thank You!

I am aware there is existing Open Source C code specifically for driving small SK9822 arrays using an Arduino, but I have a really hard time sussing out the nuances of the low-level functions from C source written to use an AVR USART. The project I am exploring will require I have a mastery of the low-level mechanics in order to determine feasibility of the overall project ... and I was failing utterly at step one.

Your naming convention (TxGlobal, TxBlue, TxGreen, TxRed) combined with pointing out the correct sequence for sending "00001000" gives me the building blocks to get the byte oriented routines written correctly ... perhaps even in a single try!

One final question ... **if you happen to know the answer** (please do not waste any of your time researching, a few minutes testing will provide the answer) ...

With a Single SK9822 LED (not an array)
To light it Blue, I would send:
$00000000 ;Start Sequence
[Global Byte] [Blue Byte = 80] [Green Byte = 0] [Red Byte = 0]
$FFFFFFFF ; End Sequence


Now, assume an array of 256 x SK9822 LEDs .... LED(0) to LED(255)
If I want to set the LED(0) data to a static value of:
[Global Byte] [Blue Byte = 80 ] [Green Byte = 00] [Red Byte = 00]
And all the other LEDs <OFF>
I would send:
$00000000 ;Start Sequence
LED(255) = [Global Byte] [Blue Byte = 00] [Green Byte = 00] [Red Byte = 00]
LED(254) = [Global Byte] [Blue Byte = 00] [Green Byte = 00] [Red Byte = 00]
LED(253) = [Global Byte] [Blue Byte = 00] [Green Byte = 00] [Red Byte = 00]
.
.
.
.
LED(2) = [Global Byte] [Blue Byte = 00] [Green Byte = 00] [Red Byte = 00]
LED(1) = [Global Byte] [Blue Byte = 00] [Green Byte = 00] [Red Byte = 00]
LED(0) = [Global Byte] [Blue Byte = 80] [Green Byte = 00] [Red Byte = 00]
$FFFFFFFF ;End Sequence (0)
$FFFFFFFF ;End Sequence (1)
.
.
.
$FFFFFFFF ;End Sequence (254)
$FFFFFFFF ;End Sequence (255)

To simplify the next question ==>> If ....
Frame Data() =
LED(255) = [Global Byte = XX] [Blue Byte = XX] [Green Byte = XX] [Red Byte = XX]
LED(254) = [Global Byte = XX] [Blue Byte = XX] [Green Byte = XX] [Red Byte = XX]
.
.
.
LED(1) = [Global Byte = XX] [Blue Byte = XX] [Green Byte = XX] [Red Byte = XX]
LED(0) = [Global Byte = XX] [Blue Byte = XX] [Green Byte = XX] [Red Byte = XX]

With XX = Associate Values of RGB for LED(0) to LED(255) ....

I am ***assuming*** that in the more general case of "refreshing all the LEDs" that the typical refresh process would be:

$00000000 ;Start Frame(0)
Frame Data(0)
$FFFFFFFF ;End Sequence
$00000000 ;Start Frame(1)
Frame Data(1)
$FFFFFFFF ;End Sequence
.
.
.
$00000000 ;Start Frame(N)
Frame Data(N)
$FFFFFFFF ;End Sequence
$00000000 ;Start Frame(N + 1)
Frame Data(N + 1)
$FFFFFFFF ;End Sequence

Where the Start and End Sequences are propagated From LED(0) to LED(255) .... and the update of LED(255) in Frame(0) occurs after the data for LED(0) to LED(254) Frame(1) has been loaded, but just before LED(0) has Rxd the End Sequence?

It probably took longer to write that out than it will to actually test it, LoL, and it seems logical that my assumption would be correct from a bandwidth perspective ... but if you happen to know the answer ... ;-)

Thanks Again!

Fish
 
Top