Skip to main content
VN.2
Associate III
September 27, 2022
Solved

Toggle GPIO port input/output direction (MODER) through DMA? STM32H750

  • September 27, 2022
  • 3 replies
  • 2699 views

I would like to remove the CM7 from having to be involved in this toggle. Should it be possible to toggle the direction through DMA? Is there anything special that needs to happen for this to work?

 // Switch to output.
 GPIOA->MODER = 0xABFF5555;
 GPIOB->MODER = 0x5CF555B3;
 
 //... Write to the port.
 
 // WFI
 
 // Switch to Input.
 GPIOB->MODER = 0x0CF000B3;
 GPIOA->MODER = 0xABFF0000;

This topic has been closed for replies.
Best answer by VN.2

Answering my own question. Did an experiment. It is no issue, at all, to let BDMA write to GPIOx->MODER, as Memory to Pheriperal. By Chaining that DMA through an event a bidirectional interface can be created. The MODER write adds about 30ns to the actual DMA that I wanted. OUTPUT_OD still is not working for me for some reason, but would equate to the same code below as I would still need to write a 1 to all pins that need to switch to input.

Here is some example code, that triggers 2 different DMA operations from EXTI0, which are chained through an event with the DMAMuxer.

int InitializeDmaChannels(void)
{
 HAL_DMA_MuxRequestGeneratorConfigTypeDef dmamux_ReqGenParams = {0};
 __HAL_RCC_BDMA_CLK_ENABLE();
 // Setup the MODE switching DMA that should execute before the other channels start.
 {
 DMA_Handle_Channel2.Instance = BDMA_Channel2;
 DMA_Handle_Channel2.Init.Request = BDMA_REQUEST_GENERATOR0;
 DMA_Handle_Channel2.Init.Direction = DMA_MEMORY_TO_PERIPH;
 DMA_Handle_Channel2.Init.PeriphInc = DMA_PINC_DISABLE;
 DMA_Handle_Channel2.Init.MemInc = DMA_MINC_DISABLE;
 DMA_Handle_Channel2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
 DMA_Handle_Channel2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
 DMA_Handle_Channel2.Init.Mode = DMA_CIRCULAR;
 DMA_Handle_Channel2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
 DMA_Handle_Channel2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 DMA_Handle_Channel2.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
 DMA_Handle_Channel2.Init.MemBurst = DMA_MBURST_SINGLE;
 DMA_Handle_Channel2.Init.PeriphBurst = DMA_PBURST_SINGLE;
 // Initialize the DMA for Transmission.
 HAL_StatusTypeDef dmares = HAL_OK;
 dmares = HAL_DMA_Init(&DMA_Handle_Channel2);
 // Select Callbacks functions called after Transfer complete and Transfer error.
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel2, HAL_DMA_XFER_CPLT_CB_ID, NULL);
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel2, HAL_DMA_XFER_ERROR_CB_ID, HAL_TransferError);
 // NVIC configuration for DMA transfer complete interrupt.
 HAL_NVIC_SetPriority(BDMA_Channel2_IRQn, 1, 0);
 HAL_NVIC_DisableIRQ(BDMA_Channel2_IRQn);
 // Configure and enable the DMAMUX Request generator.
 dmamux_ReqGenParams.SignalID = HAL_DMAMUX2_REQ_GEN_EXTI0;
 dmamux_ReqGenParams.Polarity = HAL_DMAMUX_REQ_GEN_RISING_FALLING;
 dmamux_ReqGenParams.RequestNumber = 1;
 dmares = HAL_DMAEx_ConfigMuxRequestGenerator(&DMA_Handle_Channel2, &dmamux_ReqGenParams);
 HAL_DMA_MuxSyncConfigTypeDef Sync;
 Sync.SyncSignalID = HAL_DMAMUX2_SYNC_EXTI0;
 Sync.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
 Sync.SyncEnable = FunctionalState::DISABLE;
 Sync.EventEnable = FunctionalState::ENABLE;
 Sync.RequestNumber = 1;
 dmares = HAL_DMAEx_ConfigMuxSync(&DMA_Handle_Channel2, &Sync);
 // NVIC configuration for DMAMUX request generator overrun errors
 HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);
 dmares = HAL_DMAEx_EnableMuxRequestGenerator(&DMA_Handle_Channel2);
 dmares = HAL_DMA_Start_IT(&DMA_Handle_Channel2, (uint32_t)&(MODERDMA[1]), (uint32_t)&(GPIOB->MODER), 1);
 }
 MODERDMA[0] = 0xABFF0000;
 MODERDMA[1] = 0x0CF000BB;
 SCB_CleanDCache_by_Addr(MODERDMA, 8);
 // Setup the Address capture DMA.
 {
 DMA_Handle_Channel0.Instance = BDMA_Channel0;
 DMA_Handle_Channel0.Init.Request = BDMA_REQUEST_GENERATOR1;
 DMA_Handle_Channel0.Init.Direction = DMA_PERIPH_TO_MEMORY;
 DMA_Handle_Channel0.Init.PeriphInc = DMA_PINC_DISABLE;
 DMA_Handle_Channel0.Init.MemInc = DMA_MINC_ENABLE;
 DMA_Handle_Channel0.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
 DMA_Handle_Channel0.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
 DMA_Handle_Channel0.Init.Mode = DMA_CIRCULAR;
 DMA_Handle_Channel0.Init.Priority = DMA_PRIORITY_HIGH;
 DMA_Handle_Channel0.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
 DMA_Handle_Channel0.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
 DMA_Handle_Channel0.Init.MemBurst = DMA_MBURST_SINGLE;
 DMA_Handle_Channel0.Init.PeriphBurst = DMA_PBURST_SINGLE;
 // Initialize the DMA for Transmission
 HAL_StatusTypeDef dmares = HAL_OK;
 dmares = HAL_DMA_Init(&DMA_Handle_Channel0);
 // Select Callbacks functions called after Transfer complete and Transfer error.
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel0, HAL_DMA_XFER_CPLT_CB_ID, NULL);
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel0, HAL_DMA_XFER_ERROR_CB_ID, HAL_TransferError);
 // NVIC configuration for DMA transfer complete interrupt.
 HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn);
 // Configure and enable the DMAMUX Request generator.
 dmamux_ReqGenParams.SignalID = HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH2_EVT;
 dmamux_ReqGenParams.Polarity = HAL_DMAMUX_REQ_GEN_RISING;
 dmamux_ReqGenParams.RequestNumber = 1;
 dmares = HAL_DMAEx_ConfigMuxRequestGenerator(&DMA_Handle_Channel0, &dmamux_ReqGenParams);
 // NVIC configuration for DMAMUX request generator overrun errors
 HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);
 dmares = HAL_DMAEx_EnableMuxRequestGenerator (&DMA_Handle_Channel0);
 dmares = HAL_DMA_Start_IT(&DMA_Handle_Channel0, (uint32_t)&(GPIOB->IDR), (uint32_t)(PortBBuffer), 2);
 }
 return 0;
}

3 replies

Tesla DeLorean
Guru
September 27, 2022

Sounds really impractical

Can you perhaps use FMC + CPLD ?

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
VN.2
VN.2Author
Associate III
September 27, 2022

Thanks for the answer, I've updated the question, for some reason the detail part was missing. Not sure if you are referring to the CM7 as CLPD?

I would like to remove the CM7 from having to do the toggle, mostly because the interrupts from EXTI arrive so late compared to the speed of EXTI0->BDMA.

S.Ma
Principal
September 28, 2022

Too bad output open drain with pull up resistor crossed out: MODER in this case would have been static.

VN.2
VN.2Author
Associate III
September 28, 2022

I have tried putting those pins into OD mode + pullups, but I have not fully understood why that did not work. I saw my device put data on the bus, but when the MCU needs to communicate the lines just stay low, even if I put 0xFF on both port A and B. Not sure if the device has pulldown resistors active that overpower the pullups from the MCU (I'm still new to this).

Also observed that toggling the OD pins seems to be slightly slower than PP(I have OD + PU working for another signal which uses a bi-directional 1line protocol).

VN.2
VN.2AuthorBest answer
Associate III
October 20, 2022

Answering my own question. Did an experiment. It is no issue, at all, to let BDMA write to GPIOx->MODER, as Memory to Pheriperal. By Chaining that DMA through an event a bidirectional interface can be created. The MODER write adds about 30ns to the actual DMA that I wanted. OUTPUT_OD still is not working for me for some reason, but would equate to the same code below as I would still need to write a 1 to all pins that need to switch to input.

Here is some example code, that triggers 2 different DMA operations from EXTI0, which are chained through an event with the DMAMuxer.

int InitializeDmaChannels(void)
{
 HAL_DMA_MuxRequestGeneratorConfigTypeDef dmamux_ReqGenParams = {0};
 __HAL_RCC_BDMA_CLK_ENABLE();
 // Setup the MODE switching DMA that should execute before the other channels start.
 {
 DMA_Handle_Channel2.Instance = BDMA_Channel2;
 DMA_Handle_Channel2.Init.Request = BDMA_REQUEST_GENERATOR0;
 DMA_Handle_Channel2.Init.Direction = DMA_MEMORY_TO_PERIPH;
 DMA_Handle_Channel2.Init.PeriphInc = DMA_PINC_DISABLE;
 DMA_Handle_Channel2.Init.MemInc = DMA_MINC_DISABLE;
 DMA_Handle_Channel2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
 DMA_Handle_Channel2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
 DMA_Handle_Channel2.Init.Mode = DMA_CIRCULAR;
 DMA_Handle_Channel2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
 DMA_Handle_Channel2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 DMA_Handle_Channel2.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
 DMA_Handle_Channel2.Init.MemBurst = DMA_MBURST_SINGLE;
 DMA_Handle_Channel2.Init.PeriphBurst = DMA_PBURST_SINGLE;
 // Initialize the DMA for Transmission.
 HAL_StatusTypeDef dmares = HAL_OK;
 dmares = HAL_DMA_Init(&DMA_Handle_Channel2);
 // Select Callbacks functions called after Transfer complete and Transfer error.
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel2, HAL_DMA_XFER_CPLT_CB_ID, NULL);
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel2, HAL_DMA_XFER_ERROR_CB_ID, HAL_TransferError);
 // NVIC configuration for DMA transfer complete interrupt.
 HAL_NVIC_SetPriority(BDMA_Channel2_IRQn, 1, 0);
 HAL_NVIC_DisableIRQ(BDMA_Channel2_IRQn);
 // Configure and enable the DMAMUX Request generator.
 dmamux_ReqGenParams.SignalID = HAL_DMAMUX2_REQ_GEN_EXTI0;
 dmamux_ReqGenParams.Polarity = HAL_DMAMUX_REQ_GEN_RISING_FALLING;
 dmamux_ReqGenParams.RequestNumber = 1;
 dmares = HAL_DMAEx_ConfigMuxRequestGenerator(&DMA_Handle_Channel2, &dmamux_ReqGenParams);
 HAL_DMA_MuxSyncConfigTypeDef Sync;
 Sync.SyncSignalID = HAL_DMAMUX2_SYNC_EXTI0;
 Sync.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
 Sync.SyncEnable = FunctionalState::DISABLE;
 Sync.EventEnable = FunctionalState::ENABLE;
 Sync.RequestNumber = 1;
 dmares = HAL_DMAEx_ConfigMuxSync(&DMA_Handle_Channel2, &Sync);
 // NVIC configuration for DMAMUX request generator overrun errors
 HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);
 dmares = HAL_DMAEx_EnableMuxRequestGenerator(&DMA_Handle_Channel2);
 dmares = HAL_DMA_Start_IT(&DMA_Handle_Channel2, (uint32_t)&(MODERDMA[1]), (uint32_t)&(GPIOB->MODER), 1);
 }
 MODERDMA[0] = 0xABFF0000;
 MODERDMA[1] = 0x0CF000BB;
 SCB_CleanDCache_by_Addr(MODERDMA, 8);
 // Setup the Address capture DMA.
 {
 DMA_Handle_Channel0.Instance = BDMA_Channel0;
 DMA_Handle_Channel0.Init.Request = BDMA_REQUEST_GENERATOR1;
 DMA_Handle_Channel0.Init.Direction = DMA_PERIPH_TO_MEMORY;
 DMA_Handle_Channel0.Init.PeriphInc = DMA_PINC_DISABLE;
 DMA_Handle_Channel0.Init.MemInc = DMA_MINC_ENABLE;
 DMA_Handle_Channel0.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
 DMA_Handle_Channel0.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
 DMA_Handle_Channel0.Init.Mode = DMA_CIRCULAR;
 DMA_Handle_Channel0.Init.Priority = DMA_PRIORITY_HIGH;
 DMA_Handle_Channel0.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
 DMA_Handle_Channel0.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
 DMA_Handle_Channel0.Init.MemBurst = DMA_MBURST_SINGLE;
 DMA_Handle_Channel0.Init.PeriphBurst = DMA_PBURST_SINGLE;
 // Initialize the DMA for Transmission
 HAL_StatusTypeDef dmares = HAL_OK;
 dmares = HAL_DMA_Init(&DMA_Handle_Channel0);
 // Select Callbacks functions called after Transfer complete and Transfer error.
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel0, HAL_DMA_XFER_CPLT_CB_ID, NULL);
 dmares = HAL_DMA_RegisterCallback(&DMA_Handle_Channel0, HAL_DMA_XFER_ERROR_CB_ID, HAL_TransferError);
 // NVIC configuration for DMA transfer complete interrupt.
 HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn);
 // Configure and enable the DMAMUX Request generator.
 dmamux_ReqGenParams.SignalID = HAL_DMAMUX2_REQ_GEN_DMAMUX2_CH2_EVT;
 dmamux_ReqGenParams.Polarity = HAL_DMAMUX_REQ_GEN_RISING;
 dmamux_ReqGenParams.RequestNumber = 1;
 dmares = HAL_DMAEx_ConfigMuxRequestGenerator(&DMA_Handle_Channel0, &dmamux_ReqGenParams);
 // NVIC configuration for DMAMUX request generator overrun errors
 HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);
 dmares = HAL_DMAEx_EnableMuxRequestGenerator (&DMA_Handle_Channel0);
 dmares = HAL_DMA_Start_IT(&DMA_Handle_Channel0, (uint32_t)&(GPIOB->IDR), (uint32_t)(PortBBuffer), 2);
 }
 return 0;
}