2025-10-25 9:22 AM - edited 2025-10-25 9:27 AM
Hi,
I want to use LPTIMx to output a PWM signal that changes duty cycle on each period with DMA. The HAL driver implementation for LPTIM PWM is completely wrong, so I tried to find out how to do this from TRM etc.
LPTIMx has 3 DMA request events:
2xCCRx: Can only be used in input mode for input capture, not with output.
1xUP: Update event, which is exactly what I need here. However, it is not a regular single/burst transfer but some sort of hw block mode (DMA channel needs to set BREQ=1 to connect to this request line).
LPTIM has another special thing: It only requests a new DMA request once the ARR register is written.
So I can write a single register (it does not support burst), and it needs to be ARR or I will never get another DMA request again (with dest address set to ARR register):
/* GPDMA2_REQUEST_LPTIM1_UE Init */
NodeConfig.NodeType = DMA_GPDMA_2D_NODE;
NodeConfig.Init.Request = GPDMA2_REQUEST_LPTIM1_UE;
NodeConfig.Init.BlkHWRequest = DMA_BREQ_BLOCK;
NodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
NodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
NodeConfig.Init.DestInc = DMA_DINC_FIXED;
NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
NodeConfig.Init.SrcBurstLength = 1;
NodeConfig.Init.DestBurstLength = 1;
NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1|DMA_DEST_ALLOCATED_PORT0;
NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
NodeConfig.Init.Mode = DMA_NORMAL;
NodeConfig.RepeatBlockConfig.RepeatCount = 1;
NodeConfig.RepeatBlockConfig.SrcAddrOffset = 0;
NodeConfig.RepeatBlockConfig.DestAddrOffset = 0;
NodeConfig.RepeatBlockConfig.BlkSrcAddrOffset = 0;
NodeConfig.RepeatBlockConfig.BlkDestAddrOffset = 0;
NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;This works in the sense that it does update ARR on each period (i.e. period length). But I want to keep period length constant and update the duty cycle instead (CCR1). Now if I change the destination address to CCR1, it will write it once but never generate another DMA request because I did not write ARR.
Linked lists won't help here as I would need to generate a list with two entries: one for writing CCR1, one for ARR. Then I would need to update the linked list on each DMA request in an interrupt, which defies the whole point of using DMA in the first place.
I also cannot use two DMA channels with the same request (one would write ARR, another one CCR1), as this is not supported either if they are available at the same time.
The technical reference manual is very quiet about BREQ, it just says that it needs to be used with LPTIMx UP requests, but does not describe how or what functionality it offers.
I found an application note that hints at what I need but also does not document how it is done (https://www.st.com/resource/en/application_note/an5593-how-to-use-the-gpdma-for-stm32-mcus-stmicroelectronics.pdf)
The second point is exactly what I need. But how do I achieve that? There seems to be the option of "updating up to two registers from CCRx/RCR plus ARR", so 1-3 registers at once, and somehow it can be selected which ones.
How do I transfer such a "hw block"? It is not a burst (bursts are not supported by LPTIM), not a linked list and not a 2d/repeated block, the same application says a non-2d channel shall be used and the BREQ bit is present in all channels, so it cannot be the 2d/repeated block feature.
Is there another hidden undocumented mode to achieve that? Is that even possible on this silicon?