Author Topic: STM32F7 DMA GPIO->memory triggered by timer  (Read 1233 times)

0 Members and 1 Guest are viewing this topic.

Offline veso266Topic starter

  • Newbie
  • Posts: 9
  • Country: si
STM32F7 DMA GPIO->memory triggered by timer
« on: March 21, 2023, 05:15:39 pm »
Hello there, I have STMF769i Discovery 0 and I am trying to read GPIOa (it seams it has a nice patern I can observe, and has blue user button on it so I can see a bit change if I click it) into RAM with DMA triggered by a timer

What I do is setup a timer1 to trigger DMA2 (because TIM1 and TIM8 can only trigger DMA, and only DMA2 can read GPIOs) and I setup DMA to work in circular mode (DMA should read samples and I will take them out of RAM when I feal like it)

But for some reason it does not work
My timer 1 works, DMA2 Stream5 also work (I verified that by TIM1->CNT register which increase (with 2Mhz) and DMA2_Stream5->NDTR which does decrease from 16 to 0 (I want to read 16 bytes, because GPIOA has 16 pins

but when I look into GPIOA->IDR and my RAM the content should be the same but its not, its different. RAM has some random value in it (even if I clean it) while GPIO->IDR does show my User button toggle

Not sure whats wrong

here is a picture: (it seams that DMA2_Stream5->NDTR does not decrease at all on the picture, but it does, its just the I have HAL_Delay(1000); to slow the output down a bit and it seams that DMA2_Stream5->NDTR decreases to fast)



I use PRINTF_BYTE_TO_BINARY_INT16 to convert my buffer to 0 and 1 so I can see if a bit has changed

For those of you who want to quickly test it, here is a complete project: https://www.mediafire.com/file/d2ukau99f22f6hv/STM32_F7_DMA2SRAM_Example.zip/file

I did tried to use HAL (if you define USE_REGISTERS u use registers, if you don't define this, u use HAL) but with HAL its even worse (content of DMA2_Stream5->NDTR always stays at 16)


here is the relevant part of main.c
Code: [Select]

#define SAMPLING_PORT GPIOA
#define MAX_SAMPLING_RAM 16 // Adjust buffer size as needed
#define TIM1_PERIOD 16-1 //2 MHz at the end

/* --- 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 --- */

TIM_HandleTypeDef htim1;
DMA_HandleTypeDef hdma_tim1_up;

//If I have it declared as array, then my macro does not work
uint32_t samplingRam[MAX_SAMPLING_RAM];

void ClearRAM(void)
{
for (int i = 0; i < MAX_SAMPLING_RAM; i++)
{
samplingRam[i] = 0;
}
}
ClearRAM();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_DMA_Init();
  /* USER CODE BEGIN 2 */
  MX_USART1_UART_Init();
  printf("Booting Program\n");

#ifndef USE_REGISTERS

  //reading with DMA does not.....
  //HAL_TIM_Base_Start(&htim1);

  // DMA, circular peripheral-to-memory mode, half word (16 bit) transfer
  HAL_DMA_Start(&hdma_tim1_up, (uint32_t)(&SAMPLING_PORT->IDR), (uint32_t)(samplingRam), 16);

  //Enable TIM to generate DMA events
  __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);

  //Start the timer
  HAL_TIM_Base_Start(&htim1);
  //HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);
  TIM1->DIER |= (1 << 8);   // set UDE bit (update dma request enable)
#else
  //Start DMA & Timer
  DMA2->HIFCR = DMA_HIFCR_CTCIF5; //No half transfer event on stream 5
  DMA2_Stream5->CR |= DMA_SxCR_EN; //Enable DMA
  TIM1->CR1 |= TIM_CR1_CEN; //Start Timer
#endif




  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    //if (TIM1->CNT > 0)
  //   printf("Timer 1 is working\n");

  //Blue B1 User button is on GPIOA->0 PIN (User manual page 32)
    printf("---------------------\n");
    printf("Buffer (Direct): "
        PRINTF_BINARY_PATTERN_INT16 "\n",
        PRINTF_BYTE_TO_BINARY_INT16(SAMPLING_PORT->IDR));
    printf("Buffer (  RAM ): "
        PRINTF_BINARY_PATTERN_INT16 "\n",
        PRINTF_BYTE_TO_BINARY_INT16((uint32_t)samplingRam));
    printf("---------------------\n");

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

HAL_Delay(1000);
  }


/**
  * @brief TIM1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM1_Init(void)
{
#ifndef USE_REGISTERS
/* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

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

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  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_ENABLE;
  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();
  }
  if (HAL_TIM_OC_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */
#else
  //Init clock
  RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
  TIM1->DIER = 0;
  TIM1->SR &= ~TIM_SR_UIF;
  TIM1->CNT = 0;
  TIM1->PSC = 0;
  TIM1->CR1 = TIM_CR1_URS;
  TIM1->ARR = TIM1_PERIOD;
  TIM1->CR2 = 0;
  TIM1->DIER = TIM_DIER_UDE;
  TIM1->EGR = TIM_EGR_UG;
#endif

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

#ifndef USE_REGISTERS
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
#else
  RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
  uint32_t dmaSize = DMA_PDATAALIGN_HALFWORD;

  //TIM1_UP -> DMA2, Ch6, Stream5
  //DMA should be stopped before this point
  DMA2_Stream5->CR = (DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_2) | dmaSize | DMA_SxCR_MINC | DMA_SxCR_CIRC;
  DMA2_Stream5->M0AR = (uint32_t)samplingRam;//samplingRam;
  DMA2_Stream5->PAR  = (uint32_t)&(SAMPLING_PORT->IDR);
  DMA2_Stream5->NDTR = 16;//transferCount;// / transferSize;
  DMA2_Stream5->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH;

  /* DMA interrupt init */
  /* DMA1_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
#endif

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

GPIO_InitTypeDef GPIO_InitStruct = {0};

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();

/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_All;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(SAMPLING_PORT, &GPIO_InitStruct); //User Button is on PIOA PIN 0

}


and here is relevant part of stm32f7xx_hal_msp.c
Code: [Select]

/* USER CODE BEGIN 1 */
#ifndef USE_REGISTERS
/**
* @brief TIM_Base MSP Initialization
* This function configures the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
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();

    /* TIM2 DMA Init */
    /* TIM2_UP Init */
    hdma_tim1_up.Instance = DMA2_Stream5;
    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_PDATAALIGN_HALFWORD;
    hdma_tim1_up.Init.Mode = DMA_CIRCULAR;
    hdma_tim1_up.Init.Priority = DMA_PRIORITY_HIGH;
    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 */
  }

}
/**
* @brief TIM_Base MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM2_MspDeInit 0 */

  /* USER CODE END TIM2_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM1_CLK_DISABLE();

    /* TIM2 DMA DeInit */
    HAL_DMA_DeInit(htim_base->hdma[TIM_DMA_ID_UPDATE]);
  /* USER CODE BEGIN TIM2_MspDeInit 1 */

  /* USER CODE END TIM2_MspDeInit 1 */
  }

}
#endif
/* USER CODE END 1 */


I mean it doesn't relay matter what mode (HAL or registers) I get to work, would just like this to work :)
I do prefer to use HAL whenever possible (code is more portable and readable)

I hope someone of you can figure out whats the problem, it seams souch a simple thing to do, but its proven to be anything but simple :)

Thanks for Anwsering and Best Regards
« Last Edit: March 21, 2023, 05:19:05 pm by veso266 »
 

Offline wek

  • Frequent Contributor
  • **
  • Posts: 495
  • Country: sk
Re: STM32F7 DMA GPIO->memory triggered by timer
« Reply #1 on: March 22, 2023, 05:39:32 am »
The answer's easy: you use cached RAM. DMA writes into the physical RAM and you are reading from cache which does not know that the physical RAM changed behind its back, so it returns you its old content.

The answer is not simple, because the 'F7 (Cortex-M7) - thus mcus built around it - are very complicated. You can disable data cache entirely which comes with significant performance penalty; you can disable caching of portion of RAM in MPU which you have to learn how to do properly and may not be the top performance solution; you can perform cache maintenance operations whenever you need to read/write DMA data, which you have to learn how to do properly and is not that simple; or you can try to use non-cacheable TCM which may be not that easy and not top performance either.

In some cases, you may be surprisingly better off using a "lower performance" 'F4.

Some reading here and here and the "Cortex-M7 Programming Manual" which comes as part of the "normal" documentation to your STM32F7xx.

JW


PS. I see you've already gone registers for the DMA. I personally recommend to get rid of Cube/HAL entirely; you probably already understand, why.
« Last Edit: March 22, 2023, 05:41:21 am by wek »
 

Offline veso266Topic starter

  • Newbie
  • Posts: 9
  • Country: si
Re: STM32F7 DMA GPIO->memory triggered by timer
« Reply #2 on: March 22, 2023, 02:23:04 pm »
The answer's easy: you use cached RAM. DMA writes into the physical RAM and you are reading from cache which does not know that the physical RAM changed behind its back, so it returns you its old content.

The answer is not simple, because the 'F7 (Cortex-M7) - thus mcus built around it - are very complicated. You can disable data cache entirely which comes with significant performance penalty; you can disable caching of portion of RAM in MPU which you have to learn how to do properly and may not be the top performance solution; you can perform cache maintenance operations whenever you need to read/write DMA data, which you have to learn how to do properly and is not that simple; or you can try to use non-cacheable TCM which may be not that easy and not top performance either.

In some cases, you may be surprisingly better off using a "lower performance" 'F4.

Some reading here and here and the "Cortex-M7 Programming Manual" which comes as part of the "normal" documentation to your STM32F7xx.

JW


PS. I see you've already gone registers for the DMA. I personally recommend to get rid of Cube/HAL entirely; you probably already understand, why.

Thanks I think I will go with disabling the cache (since this should be a simple example, as I just want it to work (don't relaly care for speed right not)

I tried not calliing CPU_CACHE_Enable(); (thinking if I manualy enable the cache, by calling this function, if I don't call it, chache should be disabled), but sadly no change, still nothing works :(

as for the usage of HAL (since this is an example, would like it to be as portable and readable as possible (so it would be best if I get it working with registers and HAL (so when I later forgot about it and see the example, I can quickly adapt it to any other project), also if its using HAL, its easier to port to something like H7 for instance (I have STM32H750B-DK at my disposal as well, and example for reading GPIO with DMA would be also be usefull for H7 device)


BTW: Funny thing started happening

if I change my sampling port from GPIOE to GPIOA (with changing the clock of course)

I get Target is not responding, retrying... when I want run my project, not sure what up with that :)

if I change it back, the project runs properly (thats wierd :) )
« Last Edit: March 22, 2023, 02:25:37 pm by veso266 »
 

Offline wek

  • Frequent Contributor
  • **
  • Posts: 495
  • Country: sk
Re: STM32F7 DMA GPIO->memory triggered by timer
« Reply #3 on: March 22, 2023, 02:50:33 pm »
Okay so I was wrong and it's not easy and I don't know what's the problem.

If you pre-fill the array in memory by some fixed pattern and then run DMA once as noncircular, do the vales change? Are they sane?

Note, that the Disco board contains on-board chips which may be connected to the pins you are sampling. Have a look at the schematics.

Quote
BTW: Funny thing started happening
if I change my sampling port from GPIOE to GPIOA (with changing the clock of course)
I get Target is not responding

You appear to change all port's MODER - GPIOA contains the debugging pins SDWIO/SWCLK at PA13/PA14, so you must not touch those.

JW
« Last Edit: March 22, 2023, 02:52:55 pm by wek »
 

Offline veso266Topic starter

  • Newbie
  • Posts: 9
  • Country: si
Re: STM32F7 DMA GPIO->memory triggered by timer
« Reply #4 on: March 24, 2023, 02:48:20 pm »
Okay so I was wrong and it's not easy and I don't know what's the problem.

If you pre-fill the array in memory by some fixed pattern and then run DMA once as noncircular, do the vales change? Are they sane?

Even if I pre-fill the whole RAM array with 1s, the pattern still stays the same

After I shifted the bits to left in RAM by 5
Code: [Select]
        PRINTF_BINARY_PATTERN_INT16 "\n",
        PRINTF_BYTE_TO_BINARY_INT16(((uint32_t)samplingRam) << 5));

The pattern almost suggeest its working, but sadly only half ot the bits are the same


So I don't know what now, maybe it is working and I just don't know how to see that properly

Note, that the Disco board contains on-board chips which may be connected to the pins you are sampling. Have a look at the schematics.

Don't even care if I sample the built in chips, at this point (will worry about that later) I just want to sample something :)
« Last Edit: March 24, 2023, 02:51:10 pm by veso266 »
 

Offline veso266Topic starter

  • Newbie
  • Posts: 9
  • Country: si
Re: STM32F7 DMA GPIO->memory triggered by timer
« Reply #5 on: May 20, 2023, 11:31:18 am »
I managed to fix it
the problem was, that I printed the address of my RAM instead of its value  :)
so this thing works now
Code: [Select]
  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", DMA2_Stream5->NDTR);
  printf("---------------------\n");
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf