Author Topic: STM32H7 DMA GPIO->memory  (Read 1641 times)

0 Members and 1 Guest are viewing this topic.

Offline veso266Topic starter

  • Newbie
  • Posts: 9
  • Country: si
STM32H7 DMA GPIO->memory
« on: May 20, 2023, 12:38:37 pm »
Hi there, I am playing with GPIOs on my STM32s and today wanted to create an example on how to read GPIO rail on my STM32H750B-DK like I already did for my STM32F769, which you can download here: ttps://www.mediafire.com/file/n8h3c617olsmqlu/STM32_F7_DMA2SRAM_Example.zip/file

What I am doing is this
Here are my definitions and macros
Code: [Select]
/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_SEPARATOR
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8               PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#define SAMPLING_PORT GPIOC
#define MAX_SAMPLING_RAM 16*2 // Adjust buffer size as needed
#define SAMPLES_TO_CAPTURE 16
#define TIM1_PERIOD 16-1 //2 MHz at the end
1. Setup TIM1 that will trigger DMA with 2Mhz interval (this works fine)
Code: [Select]
static void MX_TIM1_Init(void)
{
    /* Peripheral clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();

/* USER CODE BEGIN TIM2_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = TIM1_PERIOD;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}
2. Setup DMA1_Stream1 (in STM32H7 every DMA has access to GPIO ports exept DMA1_Stream0 (which is only for ADC)
Code: [Select]
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */

  /* USER CODE END TIM2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();

    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_tim1_up.Instance = DMA1_Stream1; //DMA1_Stream0 can only be used for reading from ADC, DMA1_Stream1 can be used for other things besides reading ADC
    hdma_tim1_up.Init.Request = DMA_REQUEST_TIM1_UP;
    //hdma_tim1_up.Init.Direction = DMA_MEMORY_TO_MEMORY;
    hdma_tim1_up.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_tim1_up.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim1_up.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim1_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_tim1_up.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_tim1_up.Init.Mode = DMA_CIRCULAR;
    //hdma_tim1_up.Init.Mode = DMA_NORMAL;
    hdma_tim1_up.Init.Priority = DMA_PRIORITY_HIGH;
    //hdma_tim1_up.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

    hdma_tim1_up.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_tim1_up.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_tim1_up.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_tim1_up.Init.PeriphBurst = DMA_PBURST_SINGLE;

    if (HAL_DMA_Init(&hdma_tim1_up) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_UPDATE], hdma_tim1_up);

  /* USER CODE BEGIN TIM2_MspInit 1 */

  /* USER CODE END TIM2_MspInit 1 */
  }

}
3. Read GPIOC->IDR and compare if its value is the same as in RAM, if it is, then it works, if its not, then it doesn't work
Code: [Select]
static uint32_t samplingRam[MAX_SAMPLING_RAM] __attribute__ ((section(".RAM_D1"))) __attribute__ ((aligned (4)));

  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();

  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();

  //Clean cache
  memset(&samplingRam[0], 0x00, sizeof(samplingRam)); //This buffer should have ... when DMA is working and 0x00 when DMA is not working
  SCB_CleanDCache_by_Addr((uint32_t*)&samplingRam[0], sizeof(samplingRam));



  // DMA, circular peripheral-to-memory mode, half word (16 bit) transfer
  if (HAL_DMA_Start_IT(&hdma_tim1_up, (uint32_t)(&SAMPLING_PORT->IDR), (uint32_t)(samplingRam), 16) != HAL_OK)
  {
    /* Starting Error */
    Error_Handler();
  }
  //Enable TIM to generate DMA events
  __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);

  /*##-2- Start the TIM Base generation in interrupt mode ####################*/
  /* Start Channel1 */
  if (HAL_TIM_Base_Start_IT(&htim1) != HAL_OK)
  {
    /* Starting Error */
    Error_Handler();
  }
  while (1)
  {
  //if (isTransfering)
  //{
    /* USER CODE END WHILE */
  SCB_InvalidateDCache_by_Addr((uint32_t*)&samplingRam[0], sizeof(samplingRam));
  SCB_CleanDCache_by_Addr((uint32_t*)&samplingRam[0], sizeof(samplingRam));
/* USER CODE BEGIN 3 */

  printf("---------------------\n");
  printf("RAM Adress is: %p\n", samplingRam);
  printf("---------------------\n");

  printf("---------------------\n");
  printf("Buffer (Direct): "
          PRINTF_BINARY_PATTERN_INT16 "\n",
          PRINTF_BYTE_TO_BINARY_INT16(SAMPLING_PORT->IDR));

  for (int i = 0; i < SAMPLES_TO_CAPTURE; i += 16)
  {
  printf("Buffer (  RAM ): " PRINTF_BINARY_PATTERN_INT16 "\n", PRINTF_BYTE_TO_BINARY_INT16(samplingRam[i]));
  }
  printf("---------------------\n");

  printf("---------------------\n");
  printf("TIM1 Counter: %d\n", TIM1->CNT);
  printf("DMA  Counter: %d\n", DMA1_Stream1->NDTR);
  printf("---------------------\n");
  //}

HAL_Delay(1000);
  }

Now unlike STM32F7 where everything works now, STM32H7 uses D and I cache, which complicated my GPIO read example a lot

and yes, I did try to copy data from one buffer that is located in D1 into another buffer, which is located in D2 RAM, and this works fine

So as you can see from the picture:


Content of GPIOC->IDR and my RAM is not the same (it looks like its cached, eve if I invalidated and cleaned the cache with

Code: [Select]
SCB_InvalidateDCache_by_Addr((uint32_t*)&samplingRam[0], sizeof(samplingRam));
SCB_CleanDCache_by_Addr((uint32_t*)&samplingRam[0], sizeof(samplingRam));

For some strange reason, even if I disable the cache, with
Code: [Select]
SCB_DisableICache();
SCB_DisableDCache();

it still doesn't work

So really not sure what the problem is here
I know DMA is doing something, because if I never start the timer or DMA, then content of my ram is 0 (just like I told it to be)

So I hope someone has some idea what would the problem be, because this seems to be so simple example (because the same example (-D and I cache, and using DMA2 there because only DMA2 has access to GPIO on STM32F7) work fine on my STM32F7 board, so what is different on STMH7 board that my example doesn't work

PS: I am using HAL, because I want my examples to be as portable and as easy to read as possible, so thats why if I can I always try to make them in HAL
PPS: If anyone wants to play with my broken example, its here: https://www.mediafire.com/file/uxe7j1teqko0jxf/STM32_H7_DMA2SRAM_Example.zip/file

Thanks for Answering and Best Regards
 

Offline bugnate

  • Regular Contributor
  • *
  • Posts: 58
  • Country: us
Re: STM32H7 DMA GPIO->memory
« Reply #1 on: May 20, 2023, 11:43:00 pm »
Would you need a __DMB() before you read the "samplingRam" or alternatively declare it as 'volatile'?
 

Offline Georgy.Moshkin

  • Regular Contributor
  • *
  • Posts: 177
  • Country: hk
  • R&D Engineer
    • How to become a Tech Sponsor
Re: STM32H7 DMA GPIO->memory
« Reply #2 on: May 21, 2023, 06:39:25 am »
I have this working on h750 and h7b0, try this and if it works, examine status registers and other differences with your current solution:
1) Configure timer + circular mode + DMA
2) Generate code
3) Find HAL_TIM_Base_Start_DMA() function, copy-paste it to user code with a different name, for example MY_TIM_Base_Start_DMA().
4)In renamed function find HAL_DMA_Start_IT() and replace TIMER register with GPIO register:
Code: [Select]
    if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE],
         (uint32_t)pData,
         (uint32_t)&htim->Instance->ARR, // before
         Length) != HAL_OK)
     
    if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE],
         (uint32_t)pData,
         (uint32_t)&GPIOA->ODR, // after
         Length) != HAL_OK)

Use MY_TIM_Base_Start_DMA to start timer before main loop. Also can be used with input capture + dma to provide external clock for data output, then you alter HAL_TIM_IC_Start_DMA(). Copy-paste it to user code, rename to MY_TIM_IC_Start_DMA and alter destination to GPIO register
I see you defined right memory foam hich is accesible by DMA, but also check linker .LD files generated by cube. I had some old project which not worked with newer cube firmware version because LD file memory arrangement was updated (different address)
Disappointed with crowdfunding projects? Make a lasting, meaningful impact by becoming a Tech Sponsor today. Visit TechSponsor.io to Start Your Journey!
 

Offline wek

  • Frequent Contributor
  • **
  • Posts: 536
  • Country: sk
Re: STM32H7 DMA GPIO->memory
« Reply #3 on: May 21, 2023, 07:09:35 am »
> So as you can see from the picture:

No, I can't.

JW
 

Offline veso266Topic starter

  • Newbie
  • Posts: 9
  • Country: si
Re: STM32H7 DMA GPIO->memory
« Reply #4 on: May 22, 2023, 04:05:58 pm »
I have this working on h750 and h7b0, try this and if it works, examine status registers and other differences with your current solution:
1) Configure timer + circular mode + DMA
2) Generate code
3) Find HAL_TIM_Base_Start_DMA() function, copy-paste it to user code with a different name, for example MY_TIM_Base_Start_DMA().
4)In renamed function find HAL_DMA_Start_IT() and replace TIMER register with GPIO register:
Code: [Select]
    if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE],
         (uint32_t)pData,
         (uint32_t)&htim->Instance->ARR, // before
         Length) != HAL_OK)
     
    if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE],
         (uint32_t)pData,
         (uint32_t)&GPIOA->ODR, // after
         Length) != HAL_OK)

Use MY_TIM_Base_Start_DMA to start timer before main loop. Also can be used with input capture + dma to provide external clock for data output, then you alter HAL_TIM_IC_Start_DMA(). Copy-paste it to user code, rename to MY_TIM_IC_Start_DMA and alter destination to GPIO register
I see you defined right memory foam hich is accesible by DMA, but also check linker .LD files generated by cube. I had some old project which not worked with newer cube firmware version because LD file memory arrangement was updated (different address)

Thanks
You wouldn't it believe it but, the only thing I had to do for it to start working was to generate a fresh example
So yea, here is a working example that works: https://www.mediafire.com/file/5nkxud3n9532184/STM32_H7_DMA2SRAM_DELADELADELA.zip/file

> So as you can see from the picture:

No, I can't.

JW
So sorry, I am not sure where did the picture go but here is a working one: https://imgur.com/a/bfqJIXM
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf