STM32F7 DMA GPIO->memory triggered by timer - Page 1
Forum section: electronics / general

author: veso266
author_rank: Newbie
timestamp: March 21, 2023, 05:15:39 pm
content: 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 timerWhat 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 workMy 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 pinsbut 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 toggleNot sure whats wronghere 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 changedFor 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/fileI 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.cCode:#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 workuint32_t samplingRam;void ClearRAM(void){for (int i = 0; i < MAX_SAMPLING_RAM; i++){samplingRam = 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) transferHAL_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 timerHAL_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 & TimerDMA2->HIFCR = DMA_HIFCR_CTCIF5;//No half transfer event on stream 5DMA2_Stream5->CR |= DMA_SxCR_EN;//Enable DMATIM1->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 clockRCC->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);#elseRCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;uint32_t dmaSize = DMA_PDATAALIGN_HALFWORD;//TIM1_UP -> DMA2, Ch6, Stream5//DMA should be stopped before this pointDMA2_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.cCode:/* 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,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);/* 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 workI 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 simpleThanks for Anwsering and Best Regards

author: wek
author_rank: Frequent Contributor
timestamp: March 22, 2023, 05:39:32 am
content: 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 readinghereandhereand the "Cortex-M7 Programming Manual" which comes as part of the "normal" documentation to your STM32F7xx.JWPS. 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.

author: veso266
author_rank: Newbie
timestamp: March 22, 2023, 02:23:04 pm
content: Quote from: wek on March 22, 2023, 05:39:32 amThanks 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 worksas 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 happeningif 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 thatif I change it back, the project runs properly (thats wierd)

author: wek
author_rank: Frequent Contributor
timestamp: March 22, 2023, 02:50:33 pm
content: 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.QuoteYou appear to change all port's MODER - GPIOA contains the debugging pins SDWIO/SWCLK at PA13/PA14, so you must not touch those.JW

author: veso266
author_rank: Newbie
timestamp: March 24, 2023, 02:48:20 pm
content: Quote from: wek on March 22, 2023, 02:50:33 pmEven if I pre-fill the whole RAM array with 1s, the pattern still stays the sameAfter I shifted the bits to left in RAM by 5Code: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 sameSo I don't know what now, maybe it is working and I just don't know how to see that properlyQuote from: wek on March 22, 2023, 02:50:33 pmDon't even care if I sample the built in chips, at this point (will worry about that later) I just want to sample something

author: veso266
author_rank: Newbie
timestamp: May 20, 2023, 11:31:18 am
content: I managed to fix itthe problem was, that I printed the address of my RAM instead of its valueso this thing works nowCode: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", DMA2_Stream5->NDTR);printf("---------------------\n");