STM32 + RGB LEDs Firmware Tutorial (TIM + DMA) - Phil's Lab

Поділитися
Вставка
  • Опубліковано 22 лис 2024

КОМЕНТАРІ • 71

  • @daflamingfox
    @daflamingfox 6 місяців тому +8

    Hey Phil, great video as usual, however, something I noticed with your driver is that I believe that the bit order is wrong. it should be g7..g0, r7..r0, b7..b0, in MSB order. I have a feeling you didn't notice any issues since you were using 255 to test, so all the values for each bit were high anyway, but using any other value gives the incorrect brightness. I used the following and it is working for me with WS2812B leds:
    uint16_t bufIndex = 0;
    for (uint8_t led = 0; led < NUM_LEDS; led++)
    { // for each LED
    for (uint8_t bits = 0; bits < BITS_PER_LED; bits++, bufIndex++)
    { // loop through all 24 bits
    uint8_t byte = (bits / 8) * 8;
    uint8_t bit = 7 - (bits % 8);
    uint8_t bitIndex = byte + bit;
    if ((LED_DATA[led].data >> bitIndex) & 0x01) // If bit set
    DMA_BUF[bufIndex] = T1H; // set pulse high
    else
    DMA_BUF[bufIndex] = T0H; // set pulse low
    }
    }

    • @PhilsLab
      @PhilsLab  6 місяців тому +3

      Thank you! You are completely right - sorry about that!

    • @daflamingfox
      @daflamingfox 6 місяців тому +1

      @@PhilsLab no worries, it happens! I thought I was losing my mind at first. Appreciate everything you do!

    • @kanyethegoatnocap
      @kanyethegoatnocap 3 місяці тому

      @@daflamingfox can you please send me e link where i can find a full code for this led's driver (sk6805) thank you

    • @gionag
      @gionag 2 місяці тому

      for (uint8_t ledIdx = 0; ledIdx < SK_NUM_LEDS; ledIdx++)
      {
      for (uint8_t bitIdx = 0; bitIdx < SK_LED_BITS; bitIdx++)
      {
      if ( (skLedData[ledIdx].data >> (SK_LED_BITS - 1 - bitIdx)) & 0x01 )
      skDmaBuffer[dmaBufIdx] = SK_H_VAL;
      else
      skDmaBuffer[dmaBufIdx] = SK_L_VAL;
      dmaBufIdx++;
      }
      }
      this should do the trick

    • @gionag
      @gionag 2 місяці тому

      also in the struct the order of rgb is wrong due to memory endiannes
      should be
      typedef union
      {
      struct
      {
      uint8_t blue;
      uint8_t red;
      uint8_t green;
      } color;
      uint32_t data;
      } sk6805dataRgb;
      instead of
      typedef union
      {
      struct
      {
      uint8_t green;
      uint8_t red;
      uint8_t blue;
      } color;
      uint32_t data;
      } sk6805dataRgb;

  • @triplebig
    @triplebig 6 місяців тому +28

    I'll never understand why Altium sponsors a hobby channel but doesn't offer a hobby license -_-

    • @tgirard123
      @tgirard123 6 місяців тому +7

      It's really sad. I guess they really think that all these hobbyists are going to love their tools so much that they'll pay. Good luck with that. I'll just stay with KiCad thank you...

    • @ashwin372
      @ashwin372 6 місяців тому

      Even fusion 360 is free for hobbyist.

    • @tarasaurus24
      @tarasaurus24 6 місяців тому

      It’s actually insane desperately need a hobby license.

  • @bencemarta5222
    @bencemarta5222 6 місяців тому +4

    I love these kind of elegant solutions. At work, I always take the time to write libraries that take advantage of the hardware on this level. One thing I don't really like though is the fact that each bit needs a byte of buffer space to be sent out. This may limit the capabilities of lower end microcontrollers with less memory (And I love the G0 series from ST). I have recently given some thought about using the SPI peripheral with DMA and a timer with some minimal external hardware so that I can control a lot of these LEDs basically without any CPU and memory overhead. It didn't get far but I figured that by using 2 timer channels to generate low and high pulses selecting the right one using MOSI and resetting the timer using the SPI clock, it can be achieved by purely using the SPI peripheral in DMA mode. It would of course be a trade-off but in projects where memory is a constraint, it could be useful. I will certainly do it during summer.

    • @PhilsLab
      @PhilsLab  6 місяців тому +2

      That's very true regarding the space needed for the DMA buffer. Interesting idea you have there with SPI + the two timer channels - looking forward to hearing about the results!

    • @javaguru7141
      @javaguru7141 6 місяців тому

      Wow, that sounds awesome. I would love to see a reference implementation!

  • @sundinmikael
    @sundinmikael 6 місяців тому +7

    If you want you can optimize the color DMA update byt using a lookup table of 16x32bits values (LED 4bits).
    Then you only need 6 writes for RGB values. This works due to you using 1byte for the PWM.

  • @sigmaxi7822
    @sigmaxi7822 6 місяців тому +3

    The STM32U5 seems to handle the DMA/TIM transfer quite differently than other STM32s. I once implemented a WS2812B driver with 4 channels of 128+ LEDs each on an STM32F30, using the same principle. The key to making it work there was to 1) trigger DMA transfers via the TIM CC event (not at the end of the period) and 2) enable AutoReload preload, such that the new value is written to the register before the end of the PWM period. Otherwise it would output each bit value twice because the AR value was not updated fast enough.
    In this video for the STM32U5, it seems that DMA (or the HAL?) appropriately times the transfer such that there is a new AR value ready for each PWM period, without having to enable AR preload.

    • @PhilsLab
      @PhilsLab  6 місяців тому +1

      Interesting to hear that that's the case for that MCU, thanks!

  • @werelwolf
    @werelwolf 6 місяців тому +4

    These videos are SO good, thank you for producing them!

    • @PhilsLab
      @PhilsLab  6 місяців тому +2

      Thank you, I'm glad to hear that!

  • @AlejandroMironov
    @AlejandroMironov 6 місяців тому +1

    Absolutely fantastic video. I’m not new to C but I get lost pretty heavily when I see functions passing pointers as arguments or by reference. It would help a lot if you could make someday a video on why you use them or how is the thought process in the context of microcontrollers. Fantastic work Phil!

  • @GaborGubicza
    @GaborGubicza 6 місяців тому +3

    Thanks Phil, I'm just working on my first Addressable LED project: 50+ LEDs per row.

    • @PhilsLab
      @PhilsLab  6 місяців тому

      Very cool, good luck with your project!

  • @SmashCatRandom
    @SmashCatRandom 6 місяців тому +1

    I used the WS28xx LEDs in a previous project, and just used DMA=>SPI peripheral; expanding out the bits in a frame-buffer in RAM so that a high bit = 110, and a low bit = 100 in the stream from the MOSI port (CLK not used). Those LEDs don't really need precise timing, so it was reliable and pretty simple to implement really - although a little bit wasteful of RAM, as it obviously needed 9 bytes for each 24bit colour value. 😁

    • @szymon4602
      @szymon4602 6 місяців тому

      By additionally using a down count timer in capture mode and directing the MOSI and MISO data to a port (preferably a port that has only the least significant two bits, PORTF - in many MCUs), and using one more DMA channel from overflow, this can be done completely in hardware, using only as many bits (RAM) as SPI needs for such sending.

  • @smaroukis
    @smaroukis 6 місяців тому +1

    Perfect timing! I was just wondering how to use DMA to update PWM/brightness levels for a custom seven segment video display wall I'm building. Good concise demo. I'm guessing this is how libraries like SmartLED (nee SmartMatrix) work under the hood?

    • @PhilsLab
      @PhilsLab  6 місяців тому

      Thank you, hope it's useful! From what I've seen, many openly available libraries have a very similar foundation to this. There are however a number of improvements that could be made (memory requirements, speed, ..).

  • @ruilianglin
    @ruilianglin 6 місяців тому +1

    Great video as usual!
    I tried your implementation and found a quirk When running at a slower clock speed (64MHz) where the driver didn't work properly.
    After sending the PWM data frame once, *_SK6805_DMA_COMPLETE_FLAG_* would get stuck at 0 forever.
    Turned out, when the clock speed is low, *_HAL_TIM_PWM_Start_DMA_* would finish transfering PWM data to the timer so quickly that *_SK6805_Callback()_* is called BEFORE the code enters *_if (halStatus == HAL_OK)_* in order to clear *_SK6805_DMA_COMPLETE_FLAG_*.
    Basically, *_HAL_TIM_PWM_Start_DMA_* is called, then *_SK6805_DMA_COMPLETE_FLAG_* is set to 1 by *_SK6805_Callback()_***, but immediatly after ***_SK6805_DMA_COMPLETE_FLAG_* is set to 0 when we finally run *_if (halStatus == HAL_OK)_*.
    My quick and dirty solution is to ditch *_if (halStatus == HAL_OK)_* and clear *_SK6805_DMA_COMPLETE_FLAG_* before *_HAL_TIM_PWM_Start_DMA_*. There's for sure a more elegant solution.

    • @PhilsLab
      @PhilsLab  6 місяців тому

      That's very interesting - thanks for sharing! Oddly enough, I haven't come across that myself (even on a G0 MCU running at 64MHz). Another (not particularly elegant) approach would be to check the complete flag in the while(1) loop in main.

  • @robby091000
    @robby091000 6 місяців тому

    I was literally looking into how to do this for a set of leds last week and finally figure out how to configurate the DMA with the timer after not being able to find a good guide that use the GPDMA!!! Now im looking into options that allow me to do a similar function with simpler microcontrollers without DMA to Peripheral interfaces that dont rely on CPU nop instructions to delays pulses

    • @PhilsLab
      @PhilsLab  6 місяців тому +1

      Yeah I haven't found a good guide on setting up GPDMA either :(

  • @AngryMosfet
    @AngryMosfet 6 місяців тому

    Hey Phil, your symbols are looking clean! Could you go over your current libraries in Altium? How you structure them, how you put text in the center of the symbol aka caps, res, ic, and any other useful tips you believe are worth mentioning. Thank you!

  • @ankurnaidu
    @ankurnaidu 6 місяців тому +5

    great video there Phil! May i ask from where did you learn Embedded Coding and Firmware coding since your codes are immaculate.

    • @PhilsLab
      @PhilsLab  6 місяців тому +2

      Thank you, Ankur! Mainly by working on various projects (personal and for work). It's predominantly self-taught via online resources, books, example code, etc..

  • @Kageni32
    @Kageni32 6 місяців тому +1

    Love your videos, the next step in my project happened to be designing a ws2812 driver for those exact led chips for a stm32u0 lol. You didn't need to use logic voltage translator? I have run into problems int the past when I didn't use on. Although with that project, I was using a bq24075 as my battery charger which outputs constant 5V no matter the battery charge, so that may be the difference.

  • @tgirard123
    @tgirard123 6 місяців тому

    These videos are so darn good! I don't mind having to go through them multiple times. There is so much to learn. Just subscribed And became a patreon.

    • @PhilsLab
      @PhilsLab  6 місяців тому

      Thank you very much for your support, I really appreciate it!

  • @chadkrause6574
    @chadkrause6574 6 місяців тому +1

    Love your videos! How do you figure this stuff out in the first place? Keep up the good work!

    • @PhilsLab
      @PhilsLab  6 місяців тому +1

      Thank you! Mainly via the datasheet, past experience/knowing the peripherals, looking at open-source implementations, etc..

  • @JoelCranmer
    @JoelCranmer 6 місяців тому +1

    Great video! Just curious why you chose C34/35 to be 220nF as opposed to the value shown in section 16 of the datasheet of 104 (which corresponds to 100nF).

    • @PhilsLab
      @PhilsLab  6 місяців тому +3

      Thanks! That's for BoM consolidation. I needed a specific 220n cap for a pin of the magnetometer, but the actual decoupling value for the rest of these parts is not a precise, required quantity.

  • @emielv7677
    @emielv7677 6 місяців тому +1

    The protocol is very simple but getting the timing right without sacrificing a lot of processing time is quite difficult in my experience 😅

  • @ksbs2036
    @ksbs2036 6 місяців тому

    Really nice presentation. Thanks

    • @PhilsLab
      @PhilsLab  6 місяців тому +1

      Thank you!

  • @cakeforcat
    @cakeforcat 6 місяців тому +1

    Nicely put together! What's your opinion on hijacking the SPI peripheral for this purpose? (sorry if this is mentioned in the video, can't watch it right now)

    • @PhilsLab
      @PhilsLab  6 місяців тому

      Thank you! Haven't actually tried with SPI yet, but my first thought is that would by default enable more pins than needed on the MCU. Question is how to repurposes the unused SPI lines (CLK) if needed. The timer approach is quite straightforwardy (also peripheral/pinning-wise), so I don't really see a reason to not use it - unless free timers aren't available.

  • @fritzbender5916
    @fritzbender5916 6 місяців тому

    Awesome video and really interested in the FC

    • @PhilsLab
      @PhilsLab  6 місяців тому +1

      Thank you, Fritz - more to come on that!

  • @d1zz3l
    @d1zz3l 6 місяців тому

    Best guide how to control adressed leds

    • @PhilsLab
      @PhilsLab  6 місяців тому +1

      Thank you!

  • @ianlee6416
    @ianlee6416 6 місяців тому

    I prefer the sk9822 led using the spi peripheral or simply bit-bang it. Just takes one more pin.

  • @kiprof4350
    @kiprof4350 6 місяців тому

    Thank you so much Phil! nice Video!

    • @PhilsLab
      @PhilsLab  6 місяців тому

      Thanks for watching!

  • @hippie-io7225
    @hippie-io7225 6 місяців тому

    Thanks. I really enjoyed how you abstracted your code from low-level (hardware) to high-level (firmware).
    I have not used a STM IDE since 2010. Any opinions on this current IDE and a beginners (for the IDE) learning curve?

    • @PhilsLab
      @PhilsLab  6 місяців тому +2

      Thank you! Despite some occasional bugs, CubeIDE is my favourite (Eclipse-based) IDE. The whole pin-out, peripheral, clock, etc.. config makes set-up far more straightforward than many other system, in my eyes. Plus there's a large community behind all of this.

    • @hippie-io7225
      @hippie-io7225 6 місяців тому

      @@PhilsLab Thanks!

  • @mwafakaljabi9611
    @mwafakaljabi9611 6 місяців тому

    Thank you for the great Video as always.
    Is it professionally ok to always use HAL libraries instead of writing our own bare metal code?

    • @PhilsLab
      @PhilsLab  6 місяців тому +3

      Thank you! Sure, I've seen HALs used in development for many products.

  • @alaeddinekh3716
    @alaeddinekh3716 6 місяців тому

    very nice video as usual. a little question, how can i detect if there are some abnormalities concerning the functionality of the rgb led (e.g. LED Strip ) Danke.

    • @PhilsLab
      @PhilsLab  6 місяців тому

      Thank you! Not that I'm aware of with a single-wire implementation.

  • @perceptron9834
    @perceptron9834 6 місяців тому

    very interesting and informative video, thank you :)

  • @casualnoises2808
    @casualnoises2808 6 місяців тому

    This is a very elegant solution!!! Thanks for the detailed explanation Phil!!!

  • @rallymax2
    @rallymax2 6 місяців тому

    Nice job, especially going through the schematic, which a lot of people struggle with.

  • @christiankoch4627
    @christiankoch4627 6 місяців тому

    Due to the nested for loop the set function is really slow.. Not suitable for fast led driving

    • @PhilsLab
      @PhilsLab  6 місяців тому

      Can be yes, but for my needs in this case this is sufficient and for a tutorial this is a simple demo to get the basics across.

  • @tamaseduard5145
    @tamaseduard5145 6 місяців тому +1

    👍🙏❤️

  • @blanchehermine
    @blanchehermine 6 місяців тому +1

    HAL is hideous.

  • @WhoAmI-g2o
    @WhoAmI-g2o 6 місяців тому +1

    Are the bits order reversed in your code. Bit-7 of the color code is to be sent first, and not bit 0. ua-cam.com/video/MqbJTj0Cw6o/v-deo.html. This code works for me
    //Fill DMA buf with LED data
    int bufIndex = 0;
    for (int i=0; i=0; b--) { // reverse
    if ( (RGBLED_DATA[i].data>>b)&0x01)
    RGBLED_DMA_BUF[bufIndex] = RGBLED_HI_PULSE;
    else
    RGBLED_DMA_BUF[bufIndex] = RGBLED_LO_PULSE;
    bufIndex++;
    }
    }
    struct needs to be redefined
    typedef union {
    //RGBLED data order is GGGGGGGGRRRRRRRRBBBBBBBB
    struct {
    uint8_t b;
    uint8_t r;
    uint8_t g;
    } colour;
    uint32_t data;
    } RGBLED_DATA_TYPE;

    • @WhoAmI-g2o
      @WhoAmI-g2o 6 місяців тому

      ignore this. I just noticed somebody has asked similar question

    • @kanyethegoatnocap
      @kanyethegoatnocap 3 місяці тому

      @@WhoAmI-g2o can you please send me e link where i can find a full code for this led's driver thank you