2020-11-12 11:07 AM
I have a very basic 8-bit parallel line on pins PC0-PC7 with its clock on PB6. The MCU is a 411RE, I'm using STM32CubeIDE and HAL libraries.
I've read AN4666 but the examples provided are written using an obsolete library, not HAL. But I understood that I have to use a Timer in Input Capture mode to activate the DMA transfer. To do this, in CubeMX I configured TIM4 as follows:
Then in the DMA Settings tab I added one line:
Does this configuration make any sense? My idea is to transfer the contents of register GPIOC->IDR (dimension: Word 32bit) to a uint8_t buffer (dimension Byte 8bit).
I also need an interrupt to count how many transfers were done or simply when the destination buffer is full. This is because I have to receive N 8bit frames before I can do anything with the data.
If the configuration is correct, I don't have any idea on how to use the HAL library to configure the source and destination registers and interrupts.
I have prepared a minimal setup with a button and the parallel lines connected to DIP switches.
Any help or hint is appreciated, it's a couple of days I am messing around trying to adapt stuff from examples written with the old library and from examples which use other DMA configurations, like moving data from ADC. But I the moment I did reach any kind of success.
Thanks
Solved! Go to Solution.
2020-11-21 01:25 AM
"Peripheral" and "memory" are misleading terms in case of DMA, chosen for simplicity. P2M and M2P modes in fact mean, that transfers are started by requests from peripherals; M2M means that transfers are performed without any request, as soon as stream/channel is enabled, at the maximum possible pace - that's what you experienced, isn't it?
JW
2020-11-12 02:41 PM
> Does this configuration make any sense?
This is possible, but I don't think this is something supported by HAL. 90% sure.
> I also need an interrupt to count how many transfers were done or simply when the destination buffer is full.
The NDTR register counts the number of transfers remaining. You can trigger an interrupt when this reaches zero.
> Any help or hint is appreciated
This code will move data from memory to GPIO. It should be relatively straightforward to modify it operate in the opposite direction:
// set up the timer
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_TIM1_FORCE_RESET();
__HAL_RCC_TIM1_RELEASE_RESET();
TIM1->PSC = 0;
TIM1->ARR = 400;
TIM1->CNT = 0;
TIM1->DIER |= TIM_DIER_UDE;
uint32_t gpio_data[] = {
(1 << 10) | (1 << 11),
1 << 26,
(1 << 10) | (1 << 27),
1 << 26};
// set up the DMA channel
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_DMA2_FORCE_RESET();
__HAL_RCC_DMA2_RELEASE_RESET();
DMA_Stream_TypeDef * stream = DMA2_Stream5;
stream->M0AR = (uint32_t) gpio_data;
stream->NDTR = sizeof(gpio_data) / (sizeof(*gpio_data));
// disable direct mode
stream->FCR |= DMA_SxFCR_DMDIS;
//stream->FCR |= 0b11 << DMA_SxFCR_FTH_Pos;
// TIM1_UP is channel 6 on DMA2 stream 5
stream->CR |= 6 << DMA_SxCR_CHSEL_Pos;
stream->CR |= 0b10 << DMA_SxCR_MSIZE_Pos;
stream->CR |= 0b10 << DMA_SxCR_PSIZE_Pos;
stream->CR |= DMA_SxCR_MINC;
stream->CR |= DMA_SxCR_CIRC;
stream->CR |= 0b01 << DMA_SxCR_DIR_Pos;
stream->PAR = (uint32_t) &GPIOC->BSRR;
stream->CR |= 0b1 << DMA_SxCR_PL_Pos;
stream->CR |= DMA_SxCR_EN;
// start timer
TIM1->CR1 |= TIM_CR1_CEN;
2020-11-15 03:20 AM
By the way, for lines 30-39 a temporary variable for CR modification would generate much less instructions.
2020-11-17 01:05 AM
Or just a single write ORing the values together.
stream->CR = 0
| (6 << DMA_SxCR_CHSEL_Pos)
| (0b10 << DMA_SxCR_MSIZE_Pos)
etc.
Any reasonably optimizing compiler would throw the temporary variable away, anyway; but I consider it unnecessary noise.
But this is just style and a relatively minor inefficiency; the point remains the same.
JW
2020-11-19 10:03 AM
Hi and thanks for the response
I experimented for another 2/3 days on using CubeMX to configure all the stuff, but it's not possible. It just does not let you configure the TIM1 to trigger DMA2 , which is the only one of the two DMA controllers capable of doing memory-to-memory transfers like this one.
BTW I was wrong about the AN4666, actually it is written using HAL. But it is not a CubeMX generated project and that got me confused. I lost a lot of time trying to adapt that code for my case, but the underlying problem is that the L476 used in the AN is a more advanced MCU than the F411 that I am using. Its DMA controllers can do whatever they want, so you have complete flexibility in the configuration.
In the end I surrendered and abandoned myself to the idea of directly manipulating the registers. I managed to obtain the desired transfer, but I have a problem I don't understand.
Once setting
stream->CR |= DMA_SxCR_EN;
the transfer immediately starts, the Half and Complete transfer interrupts are generated. This coherent with what the reference manual says in Section 9.3.15, memory-to-memory subsection. What I don't understand it that when the transfer is complete, the EN bit is cleared. I've set the TIM1 CC1DE bit in the DIER register to trigger DMA requests on Input Capture Event, with no luck. I've set also the CC1IE and the 'classic' Input Capture interrupts are correctly generated.
Am I missing something? In there anything else I should check? I'm starting to think that there is a reason why this configuration is not possible in CubeMX, that is that memory-to-memory transfer can be triggered only by software, setting the EN bit and that's it. But I would be happier if someone could prove me wrong.
Thanks again for your time
2020-11-19 02:18 PM
For timer-triggered transfers, don't use M2M but P2M.
JW
2020-11-19 03:09 PM
But I don’t need mem-to-mem for transferring from GPIOC->IDR to a buffer in memory?
2020-11-19 03:27 PM
GPIO is a peripheral.
2020-11-21 01:25 AM
"Peripheral" and "memory" are misleading terms in case of DMA, chosen for simplicity. P2M and M2P modes in fact mean, that transfers are started by requests from peripherals; M2M means that transfers are performed without any request, as soon as stream/channel is enabled, at the maximum possible pace - that's what you experienced, isn't it?
JW
2020-11-22 02:14 PM
> that's what you experienced, isn't it?
Yes absolutely, I read again the reference manual with this concept in mind and everything makes more sense. Especially I missed the first sentence of section 9.3.6, which says that source and destination can be any address in the 32bit.
Anyway thanks a lot for your help. I managed to obtain what I needed. Also using CubeMX and HAL, which was preferred for the project. I will share in this thread my solution when I will have some time.
One error that took me quite a while to catch is that I was writing as source address just (uint32_t)GPIOC->IDR instead of (uint32_t)&GPIOC->IDR. Since just a plain uint32_t is expected, instead of a pointer, the compiler didn't complain. Not a great design choice of the HAL library IMHO.
Still, I have one last problem: my configuration seems to work only with TIM1, which on the F411RE can trigger DMA2 Str1 Ch6.
If I regenerate the project with for example TIM4 triggering DMA1 Str0 Ch2, or TIM5 with DMA1 Str2 Ch6, the DMA transfer raises a transfer error interrupt.
My code is left untouched between the CubeMX regenerations, I just change the references to the timer handle, otherwise it wouldn't even compile. The Input Capture interrupt works fine.
I can't understand if I'm doing something wrong when using the other timers, or again I am misinterpreting the documentation and it can't be done. Thanks again for your time.