STM32H7 DMA GPIO->memory - Page 1
Forum section: electronics / general

author: veso266
author_rank: Newbie
timestamp: May 20, 2023, 12:38:37 pm
content: 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/fileWhat I am doing is thisHere are my definitions and macrosCode:/* --- 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 end1. Setup TIM1 that will trigger DMA with 2Mhz interval (this works fine)Code: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: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 ADChdma_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, 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 workCode:static uint32_t samplingRam __attribute__ ((section(".RAM_D1"))) __attribute__ ((aligned (4)));/* Enable I-Cache---------------------------------------------------------*/SCB_EnableICache();/* Enable D-Cache---------------------------------------------------------*/SCB_EnableDCache();//Clean cachememset(&samplingRam, 0x00, sizeof(samplingRam)); //This buffer should have ... when DMA is working and 0x00 when DMA is not workingSCB_CleanDCache_by_Addr((uint32_t*)&samplingRam, sizeof(samplingRam));// DMA, circular peripheral-to-memory mode, half word (16 bit) transferif (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, sizeof(samplingRam));SCB_CleanDCache_by_Addr((uint32_t*)&samplingRam, 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));}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 lotand 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 fineSo 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 withCode:SCB_InvalidateDCache_by_Addr((uint32_t*)&samplingRam, sizeof(samplingRam));SCB_CleanDCache_by_Addr((uint32_t*)&samplingRam, sizeof(samplingRam));For some strange reason, even if I disable the cache, withCode:SCB_DisableICache();SCB_DisableDCache();it still doesn't workSo really not sure what the problem is hereI 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 workPS: 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 HALPPS: If anyone wants to play with my broken example, its here:https://www.mediafire.com/file/uxe7j1teqko0jxf/STM32_H7_DMA2SRAM_Example.zip/fileThanks for Answering and Best Regards

author: bugnate
author_rank: Regular Contributor
timestamp: May 20, 2023, 11:43:00 pm
content: Would you need a __DMB() before you read the "samplingRam" or alternatively declare it as 'volatile'?

author: Georgy.Moshkin
author_rank: Frequent Contributor
timestamp: May 21, 2023, 06:39:25 am
content: 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 + DMA2) Generate code3) 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:if (HAL_DMA_Start_IT(htim->hdma,(uint32_t)pData,(uint32_t)&htim->Instance->ARR, // beforeLength) != HAL_OK)if (HAL_DMA_Start_IT(htim->hdma,(uint32_t)pData,(uint32_t)&GPIOA->ODR, // afterLength) != 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 registerI 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)

author: wek
author_rank: Frequent Contributor
timestamp: May 21, 2023, 07:09:35 am
content: > So as you can see from the picture:No, I can't.JW

author: veso266
author_rank: Newbie
timestamp: May 22, 2023, 04:05:58 pm
content: Quote from: Georgy.Moshkin on May 21, 2023, 06:39:25 amThanksYou wouldn't it believe it but, the only thing I had to do for it to start working was to generate a fresh exampleSo yea, here is a working example that works:https://www.mediafire.com/file/5nkxud3n9532184/STM32_H7_DMA2SRAM_DELADELADELA.zip/fileQuote from: wek on May 21, 2023, 07:09:35 amSo sorry, I am not sure where did the picture go but here is a working one:https://imgur.com/a/bfqJIXM