cancel
Showing results for 
Search instead for 
Did you mean: 

How to configure a parallel synchronous communication through the GPIO interface (using the embedded DMA IP) with cube MX

NNico.11
Associate II

Hi,

I don't find how to configure this DMA mode with CubeMx. This mode seems not mentioned in AN4031 (application note for DMA controler).

I want to implement it on a nucleo F429ZI board. I'm working with CubeMx + TrueStudio.

Best

12 REPLIES 12
NNico.11
Associate II

In fact, I'm not sure to be able to generate a clock at 2 or 3 Mhz in this mode, with a sysclk at 180Mhz

What exactly do you mean by "parallel synchronous communication"? Can you post examples, diagrams?

JW

NNico.11
Associate II

I don't have diagrams, but it's exactly what is mentioned in the AN4666

OK and which step of those mentioned in AN4666 is then not clear?

JW

NNico.11
Associate II

It's not fully clear for the moment, not a detail in particular, I've to lean on all the register used. Normal.

I discover STM32 and cubeMx specially for providing a training for industrial computer scientist.They discover just now C, embeded, .... and I prefer they start by an "high level" setting for this transfer mode, with CubeMx. And for me too. But I don't find how to do it with CubeMx.

As far as I'm concerned, I need to play more to determine the limit of this transfer mode (frequency and eventually dead-time between each word transmited) and check if it correspond to my specs.

In summary, with a nucleo 429ZI, I need to transfer approximatively a frame composed by 16 000 bits at a period around 300ns, from memory to a GPIO. 8 different frames to 8 different GPIO (massive parallel transfer)

Best

> training for industrial computer scientist

I'm not sure this is a suitable topic for them at all.

And I don't use CubeMX so can't help with that one.

But basically the steps are simple, for the Rx:

  • set up the data GPIO as inputs
  • set up the clock pin as TIMx_CHy (in 'F4, only TIM1 and TIM8 can be used for this purpose, as only these timers can trigger DMA2 transfers, and only DMA2 can access GPIO)
  • in the timer TIMx_CMMRz, set TIMx_CHy to input capture (you don't need to set the timer/counter at all), enable it in TIMx_CCER (and maybe change polarity if needed)
  • in TIMx_DIER set the appropriate CCyDE to allow triggering the DMA
  • in the DMA Stream respective to TIMx_CHy, set up the control register as appropriate for channel number respective to given timer, transfer size and direction, required mode; set up number of transfers in NDTR and the address of respective GPIOx_IDR and memory buffer address in the respective address registers.

Tx differs in that the timer must run and the TIMx_CHy must be set as output compare (PWM). And of course the data GPIO pins have to be set as output and the DMA transfer direction has to be the opposite, using address of respective GPIOx_ODR.

I believe, much of this can be clicked in CubeMX and the rest be written afterwards; but as I've said, I don't use Cube nor CubeMX.

> period around 300ns

Well, that might be tricky.

The maximum AHB clock around 180MHz converts to a cycle time of cca 6ns, so 300ns is some 50 AHB cycles. That sounds like quite a lot.

The minimum GPIO-to-memory (or the other way around) transfer cycle time itself is determined by the DMA, see AN4031 - maybe a little less than a dozen of cycles end-to-end. But then there will be some latency from the input signal to the moment where the DMA is triggered (given by delays in the timer, resampling, maybe resynchronization between timer and DMA?), and there will be also some jitter (maybe 2-3 AHB cycles, a few cycles more if there's activity on the buses from the processor or other bus-matrix masters). So you need to experiment with inversion and delaying (using the filter) of the trigger signal so that DMA reads GPIO in around the middle of the data cycle, or in similar appropriate moment. All this assuming no other DMA2 load - there's still some cycles left, so other DMA2 processes are not excluded, but certainly require care. So, all in all, IMO that might be manageable but it requires some insight and certainly some experimentation and benchmarking; and also some insight so that adding some more program won't spoil it.

JW

NNico.11
Associate II

Hi,

> I'm not sure this is a suitable topic for them at all.

 I think supply them this part ready-to-use

But for the moment, I've got some difficulties to use the TIM1 in simply PWM mode on the PE11 pin, without consider DMA for the moment.

I use that to init, config and launch it, with no success on the PIN PE11 (zero signal), from the main, before a "while(1);" and nothing more.

/* TIM1 init function */
void MX_TIM1_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
 
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 1; //TIM clock = internal clock divided by
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;	//upcount
  htim1.Init.Period = 1000;	//not use (only when auto reload register used)
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;	//no matter for the moment
  htim1.Init.RepetitionCounter = 1000;	//used in PWM only when  RCR downcounter  reaches zero (number of PWM period) Not used in upcounter mode?
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;	//TIMx_ARR register is not buffered
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;	//internal clock
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;	//no triger output (later for DMA???)
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;	//no master-slave mode
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;	//PWM1 mode
  sConfigOC.Pulse = 500;	//??duty cycle??
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;  //no matter for the moment, I'll see later
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;	//no matter for the moment, I'll see later
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;	//no matter for the moment, I'll see later
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;	//no matter for the moment, I'll see later
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  //not used for the moment
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim1); //connect PWM to PE11
  HAL_TIM_Base_MspInit(&htim1);	//enable TIM1 clock
 
}
 
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
 
  if(tim_baseHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspInit 0 */
 
  /* USER CODE END TIM1_MspInit 0 */
    /* TIM1 clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();
  /* USER CODE BEGIN TIM1_MspInit 1 */
 
  /* USER CODE END TIM1_MspInit 1 */
  }
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspPostInit 0 */
 
  /* USER CODE END TIM1_MspPostInit 0 */
  
    __HAL_RCC_GPIOE_CLK_ENABLE();
    /**TIM1 GPIO Configuration    
    PE11     ------> TIM1_CH2 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
  /* USER CODE BEGIN TIM1_MspPostInit 1 */
 
  /* USER CODE END TIM1_MspPostInit 1 */
  }
 
}

before, the clock is setting as this:

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

do you see an obvious mistake?

Best

As I've said I don't use Cube/CubeMX so don't know, but generally I recomment do read out the relevant registers and check if they are set as expected. Here, I would look at GPIOE_MODER if for pin 11 is set to AF, GPIOE_AFRH if for pin 11 is set to the AF appropriate for TIM1; TIM1_CR1 if CEN is set, TIM1_ARR if nonzero, TIM1_CNT should change upon each reread, in TIM1_CCMRy the appropriate channel should be set as output compare and one of the PWM modes, in TIM1_CCER the appropriate channel should be enabled, in TIM1_BDTR MOE should be set. That should be all.

JW

NNico.11
Associate II

Thanks to you, I have succeeded to generate a PWM, close to 2.7MHz. This should enough for the future.