cancel
Showing results for 
Search instead for 
Did you mean: 

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

VN.2
Associate III

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;

1 ACCEPTED SOLUTION

Accepted Solutions
VN.2
Associate III

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;
}

View solution in original post

7 REPLIES 7

Sounds really impractical

Can you perhaps use FMC + CPLD ?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

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.

The step between the registers doesn't really fit, does it?

Perhaps use two DMA channels

People doing stuff at wire speeds tend to do so with glue logic, not MCU's

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

> The step between the registers doesn't really fit, does it?

I have DMA setup to output, when EXTI0 goes low. When EXTI0 goes HI, I need the BUS back in input mode so I can read the next address.

> Perhaps use two DMA channels

Does the DMA to control direction need to be MEM to MEM (can I use MDMA) or does it need to be Mem to Peripheral?

> People doing stuff at wire speeds tend to do so with glue logic, not MCU's

Fair point suggesting FPGAs, I'm trying to save on electronics by utilizing software in this case. The MCU should be fast enough to support this specific interface. But the SoC architecture of the CM7 is making it more difficult than I expected.

S.Ma
Principal

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

VN.2
Associate III

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
Associate III

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;
}