Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maybe should use 7N1 serial, rather than 8N1? #1

Open
tonywestonuk opened this issue Oct 3, 2024 · 5 comments
Open

Maybe should use 7N1 serial, rather than 8N1? #1

tonywestonuk opened this issue Oct 3, 2024 · 5 comments

Comments

@tonywestonuk
Copy link

Hi,

I've just done a similar project myself, using an ESP32C3 mcu, and came to roughly the same solution as you....

However, I used 7N1 serial. ( which is 1 start bit, 7 data bits, 1 end bit), This is 9 bits in total, exactly the right size for 3 sets of WS2812 bits.

I just came across your blog, and I see you say 8N1 is the right size? I am confused, this would be 1 bit over?

Maybe I have overlooked something? or maybe you have 1 bit too many? But I'd like to find out If I am doing something wrong, so I can fix my code... But, I know it works... (see attached)

IMG_1179.mp4
@steveschnepp
Copy link
Owner

Hi!

Glad that at least someone else than me read my article 😉

As I pointed in my post, I'm using someone else's work on it and they used 8N1.

Yet, i agree that if one looks at the timing diagram :
serial stream

And, indeed, the START bit being already low, we should be able to skip the 1rst low bit and directly start with the raising edge of the 1rst neopixel bit.

So, good finding. I'll do some experiments and will eventually post an update if it works out.

@steveschnepp
Copy link
Owner

steveschnepp commented Oct 9, 2024

So, I had a look, and I don't actually use the same timing diagram as the blog article anymore, since I already leverage the START bit.

ws2812bify/ws2812bify.c

Lines 34 to 38 in e54f985

// A UART byte is
// 2 bits for 1st bit as the 1rst UART bit is the start bit
// 3 bits for 2nd bit
// 3 bits for 3rd bit
// .. repeat for all the remaining 21 bits.

And in that, you are fully right, 7N1 is enough as the STOP bit is always a LOW bit due to the inversion logic. I just always sent an explicit 0, but redundant, at the end in 8N1 mode.

Good news is that 7N1 is shorter, and therefore quicker as you gain 10% of the speed on the WS2812 wire.
Bad news is that the USB is still sending full bytes, even in 7N1 mode, so you don't have the speed gains on the USB wire.
Lastly, good news is that the ws2812bify code works unchanged in 7N1 and 8N1 mode, as the last 0 bit is simply discarded since UART is sending it in LSB mode.

I'll amend the post and will close the issue.

@tonywestonuk
Copy link
Author

Nice :-)

Also, something you might want to consider... Since ESP32 is very low powered compared to a real PC, I found a way of sending 6 bits at a time.....

First an array of specially crafted 64 shorts - The binary pattern of each of these numbers represent 6 bits of ws2812 protocol (when the UART start and stop bits taken into account)

For example, consider the first, element zero, which is 23387. Binary is: 1011011 1011011

With start and stop bits added: 110110110 110110110
... split into 3 bits: 110 110 110 110 110 110
.... Inverted: 001 001 001 001 001 001
... which is ws2812 protocol for 6 zeros:

static const short rgb2ws2812_map[] = {
  23387, 7003, 21339, 4955, 23131, 6747, 21083, 4699, 23323, 6939, 21275, 4891,
  23067, 6683, 21019, 4635, 23379, 6995, 21331, 4947, 23123, 6739, 21075, 4691,
  23315, 6931, 21267, 4883, 23059, 6675, 21011, 4627, 23386, 7002, 21338, 4954,
  23130, 6746, 21082, 4698, 23322, 6938, 21274, 4890, 23066, 6682, 21018, 4634,
  23378, 6994, 21330, 4946, 23122, 6738, 21074, 4690, 23314, 6930, 21266, 4882,
  23058, 6674, 21010, 4626
};

Then something like:

uint32_t packedColor = (red << 16) | (green<< 8) | blue;
uint16_t buffer[4]
buffer[0] = rgb2ws2812_map[(packedColor & 0b111111000000000000000000) >> 18];
buffer[1] = rgb2ws2812_map[(packedColor & 0b000000111111000000000000) >> 12];
buffer[2] = rgb2ws2812_map[(packedColor & 0b000000000000111111000000) >> 6];
buffer[3] = rgb2ws2812_map[(packedColor & 0b000000000000000000111111) >> 0];

This moves red, green, blue values into 8 bytes ready to be sent over uart.

This might be a way of speeding things up for you?

@tonywestonuk
Copy link
Author

This is my actual UART sending code:

struct RGB_Colour{
     union {
        struct {
            byte blue; 
            byte green; 
            byte red;
            byte metadata2;    
        };
        uint32_t packedColor;
    };
};

RGB_Colour sequence[LEDS_COUNT];


...

// Send RGB_Colour sequence to UART.
  uint16_t buffer[80];
  for (int i = 0; i < LEDS_COUNT; i++) {
      uint32_t packedColor = sequence[i].packedColor;
      buffer[page * 4 + 0] = rgb2ws2812_map[(packedColor & 0b111111000000000000000000) >> 18];
      buffer[page * 4 + 1] = rgb2ws2812_map[(packedColor & 0b000000111111000000000000) >> 12];
      buffer[page * 4 + 2] = rgb2ws2812_map[(packedColor & 0b000000000000111111000000) >> 6];
      buffer[page * 4 + 3] = rgb2ws2812_map[(packedColor & 0b000000000000000000111111) >> 0];

      page++;
      if (page == sizeof(buffer)/8) {
        uart_write_bytes(1, (const char *)buffer, sizeof(buffer));
        page = 0;
      }
    }
    uart_write_bytes(1, (const char *)buffer, page * 8);

@steveschnepp
Copy link
Owner

Indeed, that's actually a good idea!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants