logo elektroda
logo elektroda
X
logo elektroda

WS2812 Christmas animations on PIC12F683 - how many LEDs will 128 bytes of RAM handle?

p.kaczmarek2 201 6
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
📢 Listen (AI):
  • WS2812 LED strip powered by a microcontroller, waveform shown on a RIGOL oscilloscope.
    Can the tiny eight-bit PIC12F683 microcontroller handle the dynamic colour animation on the WS2812 LEDs? The MCU used here has just 2048 bytes of Flash memory, 128 bytes of RAM and is clocked at up to 20 MHz (8 MHz for the internal oscillator). Is this enough? Let's find out!

    The idea came from the fact that I have a dozen or so of these types of PICs left over from older projects and I decided to try to give them new life. Perhaps something for Christmas. We'll see if I still have time. At the time of publishing the first part (of this topic) I have only just started the second part.

    Here I will describe the first part of the adventure, that is, getting the WS2812 up and running and the limitations of this microcontroller.

    This topic is part of my series on PIC microcontrollers and the SDCC compiler. For other parts, please see the topics below:
    Part 1 - Setting up the operating environment
    https://www.elektroda.pl/rtvforum/topic3635522.html#18304424
    Part 2 - Blink LED, IO pins, digital inputs and outputs
    https://www.elektroda.pl/rtvforum/topic3647884.html#18389188
    Part 3 - Oscillator settings. Internal oscillator, external oscillator, quartz resonator, PLL
    https://www.elektroda.pl/rtvforum/topic3657704.html
    Part 4 - Timers, interrupts
    https://www.elektroda.pl/rtvforum/topic3676645.html#18580858
    Part 5 - Seven-segment display operation
    https://www.elektroda.pl/rtvforum/topic3676650.html#18580877
    Part 6 - MM5450 LED display driver
    https://www.elektroda.pl/rtvforum/topic3845301.html
    Related topic about PIC12F:
    PIC12F683 and SDCC - tutorial - we create a simple dimmer (read catalogue notes)


    Specification of WS2812
    The WS2812 is an individually addressable, colour LED that combines an RGB LED and a controller in a single housing. This allows each module to be controlled independently, enabling the creation of lighting effects, animations and dynamic colour changes in LED strips, arrays or DIY projects. The WS2812 uses a single-line protocol for data transfer, allowing multiple LEDs to be easily connected in series with minimal wiring. The way it works is that the WS2812s are connected in series - the DOUT input of one is connected to the DIN of the other, and their controllers themselves cleverly pass on the signal, taking only their colour described by three bytes.
    Data transmission and cascade connection diagram for WS2812 LEDs
    The bytes are encoded as a high state - low state pair, where the timing of a given state must meet a specification.
    Data transfer timing table for WS2812 LED, showing TH and TL durations.
    According to the table - we must be able to generate T0H (0.4 us) and T1H (0.8 us) times with a margin of error of up to 0.15 us (in practice slightly more).

    First attempts and instruction timing
    The first PIC that caught my eye was the PIC12F675, lying right next to the eponymous PIC12F683. However, it had even less RAM, at a mere 64 bytes:
    Comparison of PIC12F629 and PIC12F675 microcontroller specifications
    I then compared the notes and decided that the PIC12F683 was the better option after all:
    Specification table of the PIC12F683 microcontroller
    It's a tiny 8-bit MCU in a DIP8 enclosure, but it's more likely to be enough here?
    USB connector next to a PIC12F675 DIP8 microcontroller on a white background
    To start with, I got the PIC up and running. PICKIT2 sees it without external components:
    Breadboard with connected PICKIT2 programmer and DIP8 microcontroller
    For the PIC to work, however, you need a 10 kΩ resistor on the MCLR (pull up to the power supply) and you also need to give a 100 nF decoupling capacitor between power supply and ground.
    I started by trying the built-in oscillator - 8 MHz, faster is not possible. This is set in the OSCCON register:
    OSCCON register table from PIC12F683 datasheet showing oscillator frequency settings
    I wanted to illustrate how long it takes to do the GPIO state setting. This can of course be counted (from the Fosc/4 formula), but it is worthwhile to verify the result anyway. Example code:
    Code: C / C++
    Log in, to see the code

    We are interested here in the timing of the high state, because after the low state there is still a jump to the start of the loop, and this is also an instruction:
    Oscilloscope waveform showing a 520 ns pulse measured on Rigol screen
    It comes out 500 ns... and we need 400 ns - 800 ns, a bit low.


    Migrating to an external oscillator
    8 MHz is rather too slow. You need to switch to an external oscillator:
    Clock source block diagram of a PIC microcontroller with external oscillator highlighted
    I have just such a 20 MHz one:
    20 MHz quartz resonator held with tweezers, background box with other components
    All you need to do is connect it on the right pins:
    Pin diagram of PIC12F683 microcontroller in 8-pin PDIP/SOIC package
    You also need to enable it in the configuration word.
    Configuration word register of PIC12F with FOSC field highlighted in red
    New code - we are testing:
    Code: C / C++
    Log in, to see the code

    We are interested here in the timing of the low state, as there is still a spike after the high state:
    Rigol DS1054Z oscilloscope screen showing square wave data signal for LED
    It is better, 360 ns some, maybe less.


    First colours
    Since the time of one command is about 360 ns, two commands will give us about 720 ns. The second command can be completed with the command nop - processor passivity.
    The zeros and ones times with the WS2812 are not critical, you can deviate to some extent from the expected timings and the diodes can still potentially work. For this reason, I was hoping that simple bit-checking wouldn't fully mess up communication. Similarly with my WS_HIGH and WS_LOW macros, which I'm not sure about - you'd have to check in the generated listing what the compiler converts them to.
    Code: C / C++
    Log in, to see the code

    In the worst case scenario, I was planning to expand the byte send so that there is no loop, or write the whole bit in assembler. Another interesting trick is to load the whole byte into a GPIO register and shift it by one bit so as to expose its bits to a given pin sequentially. I have seen this done in the past with a PAL signal generator, but here it was not needed.
    The rest of the code speaks for itself - I rigidly prepared three pixels, or nine bytes. The colours in turn separately.
    Code: C / C++
    Log in, to see the code

    I then connected the LED strip to the chosen GPIO. Both the PIC and WS2812 run on 5 V, so no power supply problems. The LED strip has a separate data, power and ground line, so you don't have to worry about the current capacity of the PIC pin.
    Effect:
    WS2812 LED strip glowing in three colors with Pickit 2 programmer and DIP IC on white surface
    It appears to work, that the times obtained are within the WS2812 tolerances, but are we sure?


    Pseudo-animation - colour swapping
    To verify the operation, I prepared the simplest animation - a colour swap. Two arrays of data displayed with a gap so that you can see the change.
    Code: C / C++
    Log in, to see the code

    It appears that the transfer works reliably enough and alternates the two sets of colours displayed. No data is lost:
    Three glowing WS2812 LEDs in red, blue, and green colors


    First animation
    The next step is already authentic animation - just what kind? Maybe some smooth colour transitions based on a sine function. You can get a different coloured effect this way, as long as you introduce some offset between the numerical values of red, green and blue. For simplicity, I have introduced an array of sine values (in byte form), although this is largely a disastrous approach - I lose as much as 32 bytes for it alone!
    Code: C / C++
    Log in, to see the code

    The effect works, but there is one problem:
    Colorful WS2812 LEDs driven by microcontroller, oscilloscope in background
    No more pixels can be added because there is not enough RAM.
    Compilation error log for PIC12F683 showing relocation and memory allocation issues


    Optimisations and limitations
    I've tried various approaches, but even switching to dynamically counted values doesn't change much either - SDCC needs memory for them too, as it creates helper functions for them when the operation is not supported by the processor:
    warning: Relocation symbol "_cinit" [0x0022] has no section. (pass 0)
    warning: Relocation of section "UDL__moduint_0" failed, relocating to a shared memory location.
    error: No target memory available for section "UDL__moduint_0".
    error: Error while writing hex file.

    warning: Relocation of section "IDD_idata_0" failed, relocating to a shared memory location.
    error: No target memory available for section "IDD_idata_0".
    error: Error while writing hex file.

    warning: Relocation of section "UDL__mulint_0" failed, relocating to a shared memory location.
    error: No target memory available for section "UDL__mulint_0".
    error: Error while writing hex file.

    For best performance this would need to be written in assembler and limited to types that fit in registers, maybe then a bit more bytes could be racked up.
    The version below supports up to about 20 LEDs, so 60 bytes are allocated to pixels. This is less than 50% of the available RAM, as there are 128 bytes in total.
    Code: C / C++
    Log in, to see the code

    End result:



    Now you can implement already other animations, knight rider, chasing fireflies, fireworks, rainbows, of course in a given firmware compilation there will probably be only one of them, depends how much computation is required.

    Summary
    On the PIC12F683 we have a total of 128 bytes of RAM. One RGB pixel is classically assumed to be 3 bytes, so assuming half the RAM goes to pixels (because there are still variables, etc) then we have 64/3 = about 21 pixels. In practice I was able to run up to 20 pixels, although the code could probably still be slimmed down considerably.
    Potentially you could try to optimise this, but I don't see many options here:
    - there are unlikely to be any peripherals on the PIC12F, such as hardware SPI, that could send these bytes
    - The PIC12F is rather too slow to generate these bytes on the fly
    - an interesting idea would be to pack the pixels in fewer bytes, but then the animation and decoding of the results itself gets complicated
    Probably rewriting the whole thing to the assembler would help a bit, but at this stage I find that those 128 bytes of RAM are however largely an impassable obstacle in terms of animating more LEDs.
    The PIC12F is capable of controlling the WS2812, but only short strings, even more so if you want to add something else to it - button control or different modes there.
    Follow up in a separate topic.
    I am attaching the project (code + bat script for SDCC)

    Cool? Ranking DIY
    Helpful post? Buy me a coffee.
    About Author
    p.kaczmarek2
    Moderator Smart Home
    Offline 
    p.kaczmarek2 wrote 13505 posts with rating 11341, helped 617 times. Been with us since 2014 year.
  • ADVERTISEMENT
  • #2 21785710
    TechEkspert
    Editor
    An interesting Christmas surprise! Such optimisations may seem like logic puzzles that can be bypassed by using a more powerful microcontroller. However, I personally think it is worth overcoming such challenges in practice, they can come in handy where further increases in resources are not possible.
  • ADVERTISEMENT
  • #3 21785718
    p.kaczmarek2
    Moderator Smart Home
    I was also a bit taken aback by these optimisations. As I gave up on that lookup table for the sine function and tried to rewrite the effect to count on the fly, I quickly started to run into more problems. Dividing? Can't do it, requires extra memory. Modulo? Also no - No target memory available for section "UDL__moduint_0". . Multiplication of type int? Also no: No target memory available for section "UDL__mulint_0". .

    I guess seriously in the long run I'll have to write this in assembler, with full care to perform those operations that can be done by single instructions on this PIC. I just don't know how much I'll be able to count some nice effects in assembler. The good thing is at least that the ICSP is there and PICKIT2 has an "auto detect and write" function, so with each compilation of the batch PICKIT2 reloads it to the PIC itself. You can iterate fairly quickly.

    Up to 24 LEDs would be useful, as I have such rings:
    Colorful RGB LED ring with 24 WS2812 diodes arranged in a circle

    Ew. You can cheat the fun slightly, as I still found such a PIC:
    Three Microchip PIC microcontrollers on currency pattern, 12F1840 pointed by tweezers
    PIC12F1840
    Specification table of PIC12F1840 and PIC12LF1840 MCUs with 256-byte SRAM highlighted
    But again, I tested once, and my PICKIT2 doesn't seem to want to recognise it, despite the new Device File. I'd have to dust off the PICKIT3, I have one too, just where.
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #4 21785727
    TechEkspert
    Editor
    These rings allow you to generate nice effects more easily to give the impression of movement and cyclicity of the effect. Perhaps if linear changes were used instead of a sine, such increments would be easier, while I am not sure they would be similarly visually appealing. These rings can easily draw 500mA at higher brightness. What else would be unusual to use? An external PSRAM or EEPROM would be rather difficult to use due to the transmission times required to the WS2812. The advantage is that once the data is sent, the neopixel holds the settings, i.e. calculations can be performed between successive 'frames'. Perhaps for longer strings several synchronised PICs could be used and each would control its own ES2812 group.



  • ADVERTISEMENT
  • #5 21785747
    p.kaczmarek2
    Moderator Smart Home
    Yes, this holding of settings is useful, you don't have to "refresh non-stop every frame" as with, for example, PAL image generation, or there images for monitors in general, VGA the same.

    For the time being, I don't see the option to combine anything during the display, so RAM is limiting, you have to have the data already before the transmission starts. You can calculate to your heart's content, rather the frame rate (between successive frames) is not an issue here.

    What if two strips were connected to the PIC pin and somehow benefit from having two copies of the "mirror" effect?

    WS2812s in general have been discussed quite extensively on the forum already, but there is plenty to discuss as the effects are really good. Here are a handful of related topics from me:
    PIC18F45K50 as WS2812 LED strip driver (theory+library)
    ESP32 and Remote Control Transceiver (RMT) - tutorial, first steps, WS2812 control
    Controlling WS2812 diodes via SPI with DMA - using MOSI to generate timings
    Animations of OpenBeken WS2812B - new HTTP panel integration, PixelAnim driver
    An interesting alternative to the WS2812:
    LED strip with RS485 interface? DMX512 protocol - TM512C4 RGBW 24V
    How to compile and configure WLED to support DMX on LilyGo T-CAN485 ESP32?
    DIY on WS2812:
    House-built 7-segment colour display based on WS2812B
    Intelligent workshop organiser - WS2812B - illuminated drawers with database


    And your YT presentation mentioned (topic for discussion):
    WS2812B NeoPixel - #29 edu electroda.co.uk
    Helpful post? Buy me a coffee.
  • #7 21785779
    p.kaczmarek2
    Moderator Smart Home
    512 bytes of RAM the author had:
    ATtiny85 microcontroller specs with 512 bytes of RAM and 8 KB program memory
    Helpful post? Buy me a coffee.
📢 Listen (AI):
ADVERTISEMENT