I am trying to control two lines of digital LEDs (WS2812b) with my STM32F103 using Timers with DMA and PWM. LED strips data lines are connected to PB5 and PB6 of the MC. I used STM32CumeMX to create the inital code for my project.

I click channel 1 and set it to "PWM Generation CH1". PB6 tuns green and in DMA settings it says TIM4_CH1 and DMA1 Channel1. Looks OK, and it works. enter image description here

But when I try to configure PB5 weird stuff happens: I select TIM3 and Channel2. PA7 turns green(!?). I click PB5 and select TIM3_CH2 manually. The black pin appears next to it. In DMA settings it says TIM3_CH4/UP(!??) and DMA1 channel 3(!???). What is the logic here? What does /UP even mean? Where are all possible combinations of Timer/DMA channel are listed?

As the result PB5 does not work. enter image description here

Also I noticed that CubeMX produces this:

/* Several peripheral DMA handle pointers point to the same DMA handle.
     Be aware that there is only one channel to perform all the requested DMAs. */
    __HAL_LINKDMA(tim_baseHandle, hdma[TIM_DMA_ID_CC4], hdma_tim3_ch4_up);
    __HAL_LINKDMA(tim_baseHandle, hdma[TIM_DMA_ID_UPDATE], hdma_tim3_ch4_up);

I deleted the last line, but it didn't help. I just want to configure PB5 similarly to PB6. What am I missing here?

Is it possible to use DMA on PB5 at all?

  • \$\begingroup\$ A timer is by itself sufficient to generate a PWM signal. What is the DMA supposed to be providing in your use case (What data is stored in the DMA buffer? Intensity of the LED?) A timer can trigger DMA, and a timer can control PWM, but you wouldn't want the same timer to run both, since the two purposes ordinarily need very different frequencies. \$\endgroup\$
    – Ben Voigt
    Commented Dec 6, 2019 at 21:54
  • \$\begingroup\$ I have a buffer of color data stored as pulse widths for the whole LED strip. I use DMA to transfer this buffer from memory to the MC pin \$\endgroup\$
    – George
    Commented Dec 6, 2019 at 22:02

1 Answer 1


TIM3_CH2 can be connected to one of two pins: PA7 or PB5. When you select TIM3 & PWM Generation CH2 in CubeMX, it assigns it to PA7. You can CTRL+Click (and hold both) to PA7 and see that PB5 highlighted. Then you can drag & drop it while holding the CTRL key. The black pin means that it's manually assigned and CubeMX won't move it if you try to enable another peripheral which uses the same pin.

If you refer to the Table 78 in the reference manual, you see that DMA1 Channel 3 is shared by TIM3_CH4 and TIM3_UP (update event, basically timer overflow). That's why it's named that way in CubeMX. When a DMA channel is shared by more than one sources, you shouldn't enable more than one at a time.


After the OP's comment, I realized that I overlooked Table 78. It appears that TIM3_CH2 is unable to make a Capture & Compare DMA request as it's not present in the table.

However, I also realized that what you actually need is a DMA request tied to the update event. This allows you to update CCRx registers every time TIM3 overflows. You can even update all the CCRx registers at once by using the DMA Burst Mode of the timer.

BTW, DMA capabilities are not related to the pins that peripherals use. So, it doesn't matter if it's PA7 or PB5. CubeMX shows TIM3_CH4/UP option because the update event is not related to any timer channel and that DMA request is available even if no CH pin is used.

  • \$\begingroup\$ Ok, but if PB5 is connected to TIM3_CH2 how can I use it as an output of DMA? Why CubeMX offers to use TIM3_CH4/UP if it is not possible connect to PB5? Am I wrong? \$\endgroup\$
    – George
    Commented Dec 7, 2019 at 17:17
  • \$\begingroup\$ You are right. It seems that I overlooked Table 78. I updated my answer. \$\endgroup\$
    – Tagli
    Commented Dec 7, 2019 at 19:42
  • \$\begingroup\$ Thanks for making it clear. I really need to update CCRx every time the timer overflows. Previously I used HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1, (uint32_t *)&BUF_DMA, ARRAY_LEN); HAL command. It updated the CCR1 automatically every timer overflow. So how can I achieve this with DMA burst mode? \$\endgroup\$
    – George
    Commented Dec 7, 2019 at 20:06
  • \$\begingroup\$ Burst mode allows you to update more than one register of a given timer with a single DMA request. For example, you can update CCR1 to CCR4 of TIM3 every time it overflows using DMA. It works like as if a single update event causes the TIM module to make 4 DMA requests. Of course, all registers must belong to the same timer. \$\endgroup\$
    – Tagli
    Commented Dec 7, 2019 at 21:16
  • \$\begingroup\$ thanks for the update. I was able to write to CCR2 of TIM3 with burst mode, but turned out that burst mode can't handle large buffers of data. HAL_TIM_PWM_Start_DMA takes buffer size as the last argument and transfers the whole buffer. HAL_TIM_DMABurst_WriteStart takes BurstLength as the last argument, but this is completely different. It's maximum value is 18 which is too little for my needs. Am I missing something here? \$\endgroup\$
    – George
    Commented Dec 9, 2019 at 23:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.