2021-10-09 06:50 PM
I am using STM32F407ZGT6 and configured the TIM1_CH1 to trigger DMA2 to transmit data through GPIO. Since the protocol is using both raising and failing edges, I need to control both data pins and the clock pin. So the TIM1 is just generating PWM signals without output. I have put all pins in GPIOC so I can use a half-word data width on both MSIZE and PSIZE to directly control both data pins and the clock pin.
The target clock frequency is about 5 MHz, so I need to transmit data from SRAM to the GPIO peripheral every 50ns like:
GPIOC->ODR = tmpodr[0];
GPIOC->ODR = tmpodr[0]|CLK_Pin;
GPIOC->ODR = tmpodr[1]|CLK_Pin;
GPIOC->ODR = tmpdor[1];
GPIOC->ODR = tmpodr[2];
GPIOC->ODR = tmpodr[2]|CLK_Pin;
GPIOC->ODR = tmpodr[3]|CLK_Pin;
GPIOC->ODR = tmpdor[3];
it is one cycle.
But what I have got is just about 3.70 MHz from the oscilloscope. After I enabled the FIFO, the maximum is about 4 MHz. And memory burst mode has no effect. I thought it is caused by the bus race since if I do this by CPU, the maximum frequency may up to 7 MHz with for loop unroll. But it is hard to slow down since I cannot exceed the limitation of 5 MHz or the peripheral would not work correctly. And 3.7 Mhz is a bit slow since I hope it works at least 4.5+ MHz. So do you have any suggestion?
Solved! Go to Solution.
2021-10-10 11:48 PM
You can use two different timer channels to trigger DMA.
Or, maybe better, generate the clock using Toggle mode, and trigger DMA with a different channel or even using Update, in between.
JW
2021-10-09 07:23 PM
I can get an update rate of 8.4 MHz in direct mode and 16.8 MHz with a FIFO on the STM32F401 (at 84 MHz clock). I imagine the F407 will be the same.
This cripples the bus, so good luck doing anything else at the same time.
// set up the timer
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_TIM1_FORCE_RESET();
__HAL_RCC_TIM1_RELEASE_RESET();
TIM1->PSC = 0;
TIM1->ARR = 4;
TIM1->CNT = 0;
TIM1->DIER |= TIM_DIER_UDE;
uint32_t gpio_data[] = {
(1 << 10) | (1 << 11),
1 << 26,
(1 << 10) | (1 << 27),
1 << 26};
// set up the DMA channel
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_DMA2_FORCE_RESET();
__HAL_RCC_DMA2_RELEASE_RESET();
DMA_Stream_TypeDef * stream = DMA2_Stream5;
stream->M0AR = (uint32_t) gpio_data;
stream->NDTR = sizeof(gpio_data) / (sizeof(*gpio_data));
// disable direct mode
stream->FCR |= DMA_SxFCR_DMDIS;
//stream->FCR |= 0b11 << DMA_SxFCR_FTH_Pos;
// TIM1_UP is channel 6 on DMA2 stream 5
stream->CR |= 6 << DMA_SxCR_CHSEL_Pos;
stream->CR |= 0b10 << DMA_SxCR_MSIZE_Pos;
stream->CR |= 0b10 << DMA_SxCR_PSIZE_Pos;
stream->CR |= DMA_SxCR_MINC;
stream->CR |= DMA_SxCR_CIRC;
stream->CR |= 0b01 << DMA_SxCR_DIR_Pos;
stream->PAR = (uint32_t) &GPIOC->BSRR;
stream->CR |= 0b1 << DMA_SxCR_PL_Pos;
stream->CR |= DMA_SxCR_EN;
// start timer
TIM1->CR1 |= TIM_CR1_CEN;
2021-10-09 10:47 PM
Thanks for your reply. Yes I think it should be achievable with F407 168MHz. I am using STM32CubeMX and HAL to configure the system as follows:
static 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 = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 5;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 31;
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();
}
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_Init(&htim1, TIM_OPMODE_SINGLE) != 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_TIMING;
sConfigOC.Pulse = 1;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
__HAL_TIM_DISABLE_OCxPRELOAD(&htim1, TIM_CHANNEL_1);
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();
}
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
if(htim_base->Instance==TIM1)
{
/* Peripheral clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
/* TIM1 DMA Init */
/* TIM1_CH1 Init */
hdma_tim1_ch1.Instance = DMA2_Stream1;
hdma_tim1_ch1.Init.Channel = DMA_CHANNEL_6;
hdma_tim1_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim1_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim1_ch1.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim1_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_tim1_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_tim1_ch1.Init.Mode = DMA_NORMAL;
hdma_tim1_ch1.Init.Priority = DMA_PRIORITY_LOW;
hdma_tim1_ch1.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_tim1_ch1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_tim1_ch1.Init.MemBurst = DMA_MBURST_INC4;
hdma_tim1_ch1.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_tim1_ch1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC1],hdma_tim1_ch1);
}
/* ... */
}
I have compared the configuration roughly but did not find anything totally different. But after I changed the ARR less than 7, no improvement shown in the oscilloscope even with FIFO and memory burst. I will compare the details according to the reference to find the reason. Thank you again.
2021-10-09 10:54 PM
The simple differences are:
2021-10-10 02:56 AM
The main difference is that PZhan calls the whole 8-step sequence from the opening post as a "cycle".
I personally would generate the clock directly as PWM output of one of the TIM1's channels, that cuts down the DMA requirements to half. Timing may need fine-tuning, though.
JW
2021-10-10 06:19 PM
But as you can see in the pseudo-code, the protocol transmits two bits on both rising and falling edges. That is why the PWM clock doesn't work. But one thing you mentioned that DMA requested too fast may be the problem.
2021-10-10 11:48 PM
You can use two different timer channels to trigger DMA.
Or, maybe better, generate the clock using Toggle mode, and trigger DMA with a different channel or even using Update, in between.
JW