2019-03-08 06: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
2019-03-08 06: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
2019-03-08 06:45 AM
What exactly do you mean by "parallel synchronous communication"? Can you post examples, diagrams?
JW
2019-03-08 07:09 AM
I don't have diagrams, but it's exactly what is mentioned in the AN4666
2019-03-08 07:22 AM
OK and which step of those mentioned in AN4666 is then not clear?
JW
2019-03-11 01: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
2019-03-11 02: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:
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
2019-03-13 04: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
2019-03-13 05: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
2019-03-14 03:47 AM
Thanks to you, I have succeeded to generate a PWM, close to 2.7MHz. This should enough for the future.