How to configure a parallel synchronous communication through the GPIO interface (using the embedded DMA IP) with cube MX
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-08 6:14 AM
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
- Labels:
-
DMA
-
GPIO-EXTI
-
STM32F4 Series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-08 6:30 AM
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-08 6:45 AM
What exactly do you mean by "parallel synchronous communication"? Can you post examples, diagrams?
JW
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-08 7:09 AM
I don't have diagrams, but it's exactly what is mentioned in the AN4666
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-08 7:22 AM
OK and which step of those mentioned in AN4666 is then not clear?
JW
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-11 1:01 AM
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-11 2:31 PM
> 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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-13 4:13 AM
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-13 5:40 AM
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-03-14 3:47 AM
Thanks to you, I have succeeded to generate a PWM, close to 2.7MHz. This should enough for the future.
