-
Hello Mesh - RGB Colors Broadcast
03/22/2017 at 19:21 • 7 commentsDemo
This was the very first step in this project that shows how machines talk to each other in a way humans could perceive :
- Broadcast use case
Firmware
WS2812B bitbanging
- The challenge here was to make an 16MHz 8bit microcontroller namely the 1$ STM8S bread board talk to a WS2812B where the full fledged ones have to be using tricks and DMAs and timers and complex stuff.
- As you might know there Smart RGB Leds are controlled with pulses that generate the 24bits of reg green blue, the problem is that every bit a pulse short or long for 0 or 1, and the Time for the high pulse of a zero as an example is 400 ns, well at 16MHz, every intruction is 62,5 nano second, that leave us with 6 instruction for looping and deciding which bit to bang the next.
- The hack here was to jump down to assambler, yes in this world of javascript, if you really want to hack stuff that's the ultimate way.
- Let's first have a look at a piece of the code
void rgb_SendArray() { BYTE int_state = __get_interrupt_state(); __disable_interrupt(); asm( "lb_intiLoop: \n" "LDW X, #0xFFFF \n"// set -1 in X, so that first inc gets 0, as inc has to be in the beginning of the loop "LDW Y, L:nbLedsBytes \n"//2 "lb_begin_loop: \n" //--------------------------------------------------------------- //--------------- bit 0 ----------------------------------------- "INCW X \n"// L t+2 "lb_start_bit0: \n"// "LD A, (L:LedsArray,X)\n"// RGBLedPin_Set "AND A, #0x80 \n"// H t2 "JREQ L:lb_bit0_Send_0 \n"// H0 t3 t4 : 2 jump to Zero, 1 Stay in One + next nop "lb_bit0_Send_1: \n"//------Send 1 : 800ns High, 450ns Low (12,8) 750ns,500ns "nop \n"// H1 t5 "nop \n"// H1 t6 "nop \n"// H1 t7 "nop \n"// H1 t8 "nop \n"// H1 t9 "nop \n"// H1 t10 "nop \n"// H1 t11 "nop \n"// H1 t12 RGBLedPin_ReSet // L1 t1 "nop \n"// L1 t2 "JRA L:lb_start_bit1 \n"// L1 JRA:2 t4 // L1 NextBitTest:4 t8 "lb_bit0_Send_0: \n"//------Send 0 : 400ns High, 850ns Low (7,13) 375ns,875ns // H0 t4 RGBLedPin_ReSet // L0 t1 "nop \n"// L0 t1 "nop \n"// L0 t2
- rgb_sendArray is a C function, that starts by disabling the interrupts after saving the interrupts state to restitute them later on.
- of course any interrupt here would corrupt the bit banging, so expect a latency on your interrupts handling.
- The trick to have a multi line assembler that looks nice was to add the end line inside the asembler text.
- This function can bag as many LEDs as you have RAM, unfortunately with the STM8S I could not compile more than ~ 220 bytes of continuous memory, which is more than enough for series of Leds.
- The trick is to start by setting a PIO, which is a macro, as the pin is configurable, I did not manage to have text confurable variable inside the assembler, so that was the trick.
- You always have to set a PIO high either it is a zero or a one, then as they have different timings, if it is not a zero then keep some more nops to make the one pulse longer, otherwise jump directly to the reset instruction to make the pulse shorter.
- Now the problem was that looping instructions were not fast enough to cover all cases of second half of bit timings, that means that if we have to send the first bit, then loop, test and start sending the second bit, the loop would be so slow that it is not possible to set the new pulse soon enough. The only way to solve this was to unroll the complete 8 bits, yes unroll which means writing the same code 8 times, for bit one, bit two, bit three. I admit that sounds completely stupid from advanced programming perspective, but if anyone else could hold the challenge of banging WS2812B with the STM8S differently, I'd be very interested.
- Any way, it's working, it's not very much code consuming, and it is very memory friendly as every bit to send take only one bit out of the memory, so that we save the RGB values in memory first as three consecutive bytes.
Full source code part of the STM8S driver : github IoT_Frameworks WS2812B.c
Color Flashing
- Not much is left to flash colors, first the needed color is written in memory, then the assembler rgb_SendArray function is called.
- Now, I added additional routines to make more advanced functions easier, such as providing a counter, and making the color stick to the counter level. It is color shading.
void rgb_FlashColors(BYTE delay, RGBColor_t Color) { for(int iCount=0;iCount<255;iCount++)//0-10 { RGBColor_t Ci = rgb_ColorScale(iCount,255,BLACK,Color); rgb_SendColors(Ci); delay_ms(delay); } for(int iCount=0;iCount<255;iCount++)//0-10 { RGBColor_t Ci = rgb_ColorScale(iCount,255,Color,BLACK); rgb_SendColors(Ci); delay_ms(delay); } rgb_SwitchOff(); }
- So this is only following counter and scaling colors to them to go first up in the first loop and then down
- Note the color passed as argument is scaled between Black and the color itself to fade it in and then out, but the same function could be used to shade a temperature between cold and warm for example.
- The last magic step is the color scaling which is a simple interpolation of the R, G and B values, not worth showing here refer in the same driver source for details.
- So only important to keep in mind, you first create the colors in memory with any time consuming operation, shading, scaling, and then bit banging with the assembler function every new intermediate color, doing so consecutively show the result in the video above how LEDs flash slowly and nicely and not only stick on a new color.
RF Broadcast
- The rest is a simple broadcast and listen.
- The master flashes the color before sending the RF signals
- The slaves flash of course after reception, which give a better visual effect of slaves submission.
- The RF broadcast is not of significant importance here as more work has been done on the mesh protocol, but that'll be in posts to follow.
Voltage levels
- This is a weak spot of the design I'm struggling with, the WS2812B is not really TTL compliant and as the STM8S is running on 3.3V as per PCB design is required for the nRF24L01, the LEDs sometimes do not like the timing as they would not get the full pulse but a shorter top of it. In one LED, that's not a problem, but with a long LED series, there might be some issues, I'm trying some level shifters but high speed level shifting is more critical.