2024-06-16 10:45 AM - edited 2024-07-09 04:13 AM
Hello ST,
Today, in two separate threads, I've had to walk two separate newcomers to STM32 through the pitfalls of trying to trigger a DMA to an arbitrary peripheral from a timer event. The threads are:
what-are-gpio-events-how-do-i-connect-an-event-to-a-peripheral
and
spi-dma-request-triggered-by-timer-compare
I myself had to struggle (using the G431) not that long ago to make sense of what the CubeMX generated code does, and why it isn't usable.
HAL functions HAL_TIM_IC_Start_DMA/HAL_TIM_OC_Start_DMA hardcode the DMA peripheral register to which read/write is performed, thus they are not usable. They do however perform a lot of prep work, which it is not trivial
for beginners to reconstruct on their own (which they tend to do since they can't figure out what CUbeMX is actually
generating code to do). This is evident by the two threads above as well as my own experience.
There is no HAL API (that I know of) that allows the developer to specify the peripheral-side register address.
I strongly encourage the CubeMX developers to address this shortcoming in an upcoming release, for the following reasons:
1. It's a common use-case which currently isn't addressed.
2. It's not obvious to a newcomer what the existing API functions (such as HAL_TIM_OC_Start_DMA) are intended for. It takes quite a lot of spleunking to figure out what the HAL functions are tailored. In particular, I think it's confusing to most people that the API ties the *source* of the DMA *trigger* (e.g. timer event X), to the nature (src/target) of the DMA request (e.g. streaming capture values to memory). The hardware doesn't force this,
so no wonder people are baffled when the HAL API does.
3. It seems like this would be relatively simple fix. What is requires is just a generalized version of HAL_TIM_IC_Start_DMA/HAL_TIM_OC_Start_DMA, which takes the source/target register address as an argument. perhaps HAL_TIM_IC_Start_DMA/HAL_TIM_OC_Start_DMA can even be rewritten to simply call the generic function to eliminate some code duplication.
4. Finally, an separately, I think there's an inconsistency in the way CubeMX handles DMA to different peripherals,
and how this is integrated with the HAL. Consistency is important in order to simplify learning. In my experience, users expect the flow to be "choose a DMA trigger, choose a DMA channel, choose dest/src". In fact, support for triggering DMA from a timer is different between peripherals, and between events.
You can bind a channel with TIMx_CHy or TIMx_UP (for example) from the timer periph's DMA tab, and in the GUI this looks consistent. But the HAL doesn't treat these two equally. Using TIMx_UP gets no dedicated API (you have to call Base_Start and DMA_start yourself), while the IC/OC case gets a dedicated API, which silently assumes that since you want to use the IC/OC as a trigger you must also want to use them as a data src/target (for writing capture values to memory, or reprogramming the compare value for waveform generation). And, if you want to use a timer to do DMA->DAC, it's different again. the DAC peripheral GUI lets you specify the timer as a (conversion) trigger, and if you assign a DMA channel (and call the associated HAL API), the DAC itself will trigger the DMA (AFAICT). For each of these the users need to figure out the specifics of each as a special case, and this makes the learning curve so much steeper. I've been through each of these cases myself, and I can attest that It takes a lot of effort to find your way though this maze of special cases.
I hope you're listening,
Barry
Update 2024-07-09: Another thread
2024-06-20 07:04 AM
Hello @BarryWhit
Thanks for you feedback and for taking time to report this with many details.
I escalated this post internally via internal Ticket ID 184627 and will be back to you with update.
2024-06-21 01:47 AM - edited 2024-06-22 02:22 AM
Dear @Imen.D,
I've updated my post with more details as I gradually understand more about the issue. It is partly metamorphosing into a rant, I realize. To keep thing concrete, my hope is that the software dev team will add a missing function to the HAL, a function which specifically allows users to perform DMA with *any* timer event configured as a trigger, to/from an *arbitrary* peripheral address. The deeper issues of how to architect the CubeMX GUI in cooperation with the HAL are complex, and I realize it might not be feasible to make deep changes at this point. I hope whomever you routed this to internally at least reads the updated post.
Thanks,
Barry
2024-07-02 07:30 AM
Hello @BarryWhit
The proposed pseudocode introduces a new function, HAL_TIM_Flexible_DMA_Start, which allows developers to specify any timer event as a trigger and define an arbitrary peripheral address for the DMA transfer.
// Pseudocode for a flexible DMA triggering function in the STM32 HAL API
// Define a structure to hold the configuration parameters for the DMA trigger
struct DMA_Trigger_Config {
TIM_HandleTypeDef *htim; // Handle to the timer
uint32_t Channel; // Timer channel acting as the DMA request source
uint32_t TimerEvent; // Timer event used to trigger the DMA
uint32_t *PeripheralAddress; // Address of the peripheral data register
uint32_t *MemoryAddress; // Address of the memory location
uint32_t DataLength; // Number of data items to transfer
};
// Define the flexible DMA triggering function
HAL_StatusTypeDef HAL_TIM_Flexible_DMA_Start(DMA_Trigger_Config *config) {
// Validate the input parameters
if (config == NULL || config->htim == NULL || config->PeripheralAddress == NULL || config->MemoryAddress == NULL) {
return HAL_ERROR;
}
// Configure the timer to generate a DMA request on the specified event
// This step involves setting up the correct timer registers and modes based on the TimerEvent
ConfigureTimerForDMA(config->htim, config->Channel, config->TimerEvent);
// Configure the DMA channel to transfer data from the specified memory address to the peripheral address
// This step involves setting up the DMA controller with the source and destination addresses, data length, and other relevant parameters
ConfigureDMAChannel(config->htim->hdma[config->Channel], config->PeripheralAddress, config->MemoryAddress, config->DataLength);
// Enable the DMA stream/channel
HAL_DMA_Start(config->htim->hdma[config->Channel], (uint32_t)config->MemoryAddress, (uint32_t)config->PeripheralAddress, config->DataLength);
// Enable the timer DMA request
__HAL_TIM_ENABLE_DMA(config->htim, config->Channel);
// Start the timer
HAL_TIM_Base_Start(config->htim);
return HAL_OK;
}
// Example usage of the flexible DMA triggering function
void ExampleUsage() {
// Create a configuration structure and populate it with the desired settings
DMA_Trigger_Config dmaConfig;
dmaConfig.htim = &htim2; // Assume htim2 is already initialized
dmaConfig.Channel = TIM_CHANNEL_1;
dmaConfig.TimerEvent = TIM_EVENT_UPDATE; // For example, use the update event
dmaConfig.PeripheralAddress = (uint32_t*)&(DAC->DHR12R1); // DAC data holding register
dmaConfig.MemoryAddress = (uint32_t*)audioBuffer; // Buffer containing audio data
dmaConfig.DataLength = AUDIO_BUFFER_SIZE; // Size of the audio buffer
// Call the flexible DMA triggering function with the configuration
if (HAL_TIM_Flexible_DMA_Start(&dmaConfig) != HAL_OK) {
// Handle error
}
}
This is a high-level representation intended to illustrate the concept and is not a complete implementation. It is designed to be a starting point for discussion.
I would greatly appreciate your feedback on this approach. Does this pseudocode align with the solution you are looking for? Are there any specific aspects you would like to see adjusted to better fit your use case?
2024-07-02 07:49 AM - edited 2024-07-02 07:52 AM
Omar, Hurray Hurray Hurray!
I will look at this and give you any useful feedback I can.
2024-07-02 07:50 AM - edited 2024-07-02 07:50 AM
@Andrew Neil, I'd value your input. We're actually, like, changing the system, man! :face_with_tears_of_joy:
2024-07-02 02:04 PM - edited 2024-07-04 08:56 AM
@Saket_Om , I have looked at the pseudocode and pondered and here are my thoughts:
First, Does ST has an internal procedure for "experimental API"? I can't be sure I'm not missing anything,
and neither can you. Do you have a way to test out an API and getting wider feedback from users before
you cast it in concrete?
As for the code itself, the following issues came to mind:
That's all I have for now, perhaps more to come.
Thank you for considering the issue I raised seriously, it's a wonderful thing for to see that you really do respond to feedback from your community about pain points. I appreciate it very much.
2024-07-03 02:27 AM
Hello @BarryWhit
I appreciate your diligence in ensuring that the API meets your needs. To clarify, the pseudo code I provided serves as an example to confirm the functionality you require. This is an essential step, as it helps to communicate your requirement effectively to our development team.
>> Does ST has an internal procedure for "experimental API"?
Yes, we do have an internal process for validating and testing our APIs. This process is designed to ensure that the APIs we develop are robust and meet the needs of our users.
2024-07-03 02:58 AM
@BarryWhit wrote:@Andrew Neil, I'd value your input.
I don't tend to use DMA much, so nothing really to say.
But a big trouble with the HAL (and it's documentation), I find, is that it treats "DMA" entirely separately and in isolation from the peripherals - there is nothing about how you use the DMA with the peripherals.
The examples don't help here, because they just give a fait accompli: they just show you what it looks like when it's all done and finished - they don't take you through the process of actually getting to that point.
The coverage of interrupts is similar - with "NVIC" being entirely separate from the peripheral support.
But I think that's all a bit of a side-track from your point here?
2024-07-03 03:00 AM
@Saket_Om wrote:Yes, we do have an internal process for validating and testing our APIs. This process is designed to ensure that the APIs we develop are robust and meet the needs of our users.
my emphasis - how is that bit ensured ?
:thinking_face: