stm32F30K oneplus DMA - Page 1
Forum section: electronics / general

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 07, 2025, 02:05:08 pm
content: Hi,I want to use timer 1 of stm32 to act as a slave triggered oneplus mode to generate 8 PWM pulses, so the pulses would be used to generate timer triggered DMA request and I want to use it to transfer the data to SPI data register, I have configured it like this, the PWM generation and 8 PWM pulses works fine, But the DMA request does not generate any SPI transactionsTIM1 configurationSPI configthe main codeCode:int main(void){/* USER CODE BEGIN 1 */uint8_t txbuf;uint8_t rxbuf;//DMA_HandleTypeDef/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_TIM1_Init();MX_SPI1_Init();/* USER CODE BEGIN 2 */HAL_SPI_TransmitReceive(&hspi1,txbuf,rxbuf,10,10);HAL_TIM_OnePulse_Start(&htim1,TIM_CHANNEL_1);// Enable TIM1 DMA request on update event__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);if(HAL_DMA_Start(&hdma_tim1_ch1, (uint32_t)txbuf, (uint32_t)&SPI1->DR, 10) != HAL_OK){Error_Handler();}/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///SPI1->DR =0x55;}/* USER CODE END 3 */}The stmcube generated code for the timerCode:void MX_TIM1_Init(void){/* USER CODE BEGIN TIM1_Init 0 *//* USER CODE END TIM1_Init 0 */TIM_SlaveConfigTypeDef sSlaveConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};/* USER CODE BEGIN TIM1_Init 1 *//* USER CODE END TIM1_Init 1 */htim1.Instance = TIM1;htim1.Init.Prescaler = 48-1;htim1.Init.CounterMode = TIM_COUNTERMODE_UP;htim1.Init.Period = 3750-1;htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim1.Init.RepetitionCounter = 7;htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim1) != 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();}sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;sSlaveConfig.InputTrigger = TIM_TS_ETRF;sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_NONINVERTED;sSlaveConfig.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;sSlaveConfig.TriggerFilter = 0;if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM2;sConfigOC.Pulse = 1875-1;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;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();}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();}/* USER CODE BEGIN TIM1_Init 2 *//* USER CODE END TIM1_Init 2 */HAL_TIM_MspPostInit(&htim1);}void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle){GPIO_InitTypeDef GPIO_InitStruct = {0};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();__HAL_RCC_GPIOA_CLK_ENABLE();/**TIM1 GPIO ConfigurationPA12 ------> TIM1_ETR*/GPIO_InitStruct.Pin = GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF2_TIM1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* TIM1 DMA Init *//* TIM1_CH1 Init */hdma_tim1_ch1.Instance = DMA1_Channel2;hdma_tim1_ch1.Init.Direction = DMA_PERIPH_TO_MEMORY;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_CIRCULAR;hdma_tim1_ch1.Init.Priority = DMA_PRIORITY_LOW;if (HAL_DMA_Init(&hdma_tim1_ch1) != HAL_OK){Error_Handler();}__HAL_LINKDMA(tim_baseHandle,hdma,hdma_tim1_ch1);/* USER CODE BEGIN TIM1_MspInit 1 *//* USER CODE END TIM1_MspInit 1 */}}and the output 8 pwm pulses that would successfully generate on the PWM output when the ETR triggers with an external clock, as you can see channel 1 which is connected to the SPI clk, dos not generate anything!Do you have any Idea what I'm doing wrong?

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 09, 2025, 11:13:11 am
content: Guys do you have any Idea what I have done wrong regarding not being able to DMA transfer by the timer update event?

author: hans
author_rank: Super Contributor
timestamp: July 09, 2025, 02:41:32 pm
content: Code:__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);Have you tried changing the DMA flag to CC1?TIM1_UP and TIM1_CH1 events are on different DMA channels/streams

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 09, 2025, 02:56:12 pm
content: Explain a bit better what are you trying to achieve...Are you trying to trigger a SPI transfer (8bits) on every pwm pulse?If so, yes, you must use CC flag.Timer update flag will only trigger when the repetition counter is done, so only once after the 8 pulses.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 10, 2025, 11:07:50 am
content: QuoteYes, I want to transfer SPI (16bits) every PWM pulse, how to use CC flag?QuoteI do not get even 1 SPI transfer after 8 pwm channels, why is that? you can see the attached Scope plot too.QuoteNo, How should I do that? which DMA channel /streams are connected to TIM1_UP or TIM1_CH1?

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 10, 2025, 01:20:03 pm
content: TIM_DMA_CC1

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 10, 2025, 02:20:11 pm
content: Thanks it's now working

author: hans
author_rank: Super Contributor
timestamp: July 10, 2025, 04:03:10 pm
content: Good to hear its working!But to teach a man to fish:Quote from: ali_asadzadeh on July 10, 2025, 11:07:50 amIts in the reference manual of the STM32. Up to recently, all STM32s had fixed channel allocations per DMA request. There is a "DMA request mapping" table in the RM which outlines them all. Newer STM32s have a DMAMUX peripheral which makes this a lot more flexible.It may also be useful to read up on the difference between the update and CH1 compare events. The timer has an DMA/interrupt enable register which is used to enable the signals to be sent to the DMA controller. The update event is typically generated by the counter value over/under flowing. The CH1 event can be generated on an input capture event or an output compare match.The TRGO signal from the timer can also be used by DMA, as its also the most flexible in setup. But not all timers have this request mapped.

author: gamalot
author_rank: Super Contributor
timestamp: July 10, 2025, 04:28:55 pm
content: What is STM32F30K? I have never heard of this model and I can't find any information about it.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 10, 2025, 08:26:30 pm
content: QuoteThanks hans for your inputs, Now is the time to figure out out to make the SPI CS pin low for the 8 SPI frames, I guess I should connect to timers, and I don't know how to exactly do that?QuoteIt was a type, it's stm32f030kt6 for testing, But I will use it on CH32V307 or STM32H series, since this puppy has a small PCB that can fit in my breadboard for testing.

author: hans
author_rank: Super Contributor
timestamp: July 11, 2025, 08:23:00 am
content: QuoteOh I didn't even notice lol, I just assumed it was somekind of STM32F3xx chip. It doesn't matter because most ST's peripherals are exactly the same, only 1 or 2 incremental updates over the various series.Up around STM32F7/STM32H7, they did start refreshing some peripherals though. I think timers are still kept the same, but things like UART, SPI and DMA controllers (e.g. DMAMUX) were redesigned.That may reflect in the HAL if you want to go forward with testing on this F0 chip. I personally don't use the HAL that much, I'm more familiar with using these chips at RTL.Quote from: ali_asadzadeh on July 10, 2025, 08:26:30 pmI think you could try to use the CH1 output compare signal for that. You could try change around the compare value and the polarity to get the signal aligned with when the SPI clock burst occurs. I guess you could tie the TIM signal back to the SPI_NSS pin of STM32, or simply tell it in the SPI CR1 you use software NSS which is always active.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 11, 2025, 07:40:13 pm
content: QuoteI want the CS line be low for the entire burst, in this way only for one SPI frame I have the CS active, and for the next one it would deactivate and reactive, which is not What I want, since the ADC would reset if the CS is inactive.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 11, 2025, 09:17:49 pm
content: Use timer Update ISR?How often do you run it? What freq?

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 12, 2025, 08:42:36 am
content: QuoteI want to push it to 50K samples per second, so Interrupts are not an option, only DMA and hardware.

author: hans
author_rank: Super Contributor
timestamp: July 12, 2025, 08:50:45 am
content: If you're planning to switch to a H7, then 50k interrupts per second is nothing. The interrupt entry/exit time is about a dozen cycles or so. Add an IRQ of say 50 cycles, [12+50+12] (guesstimate) / 480MHz = 154ns per IRQ.Obviously waiting for an SPI transfer to finish is going to take forever, so DMA is still useful. But gluing a IRQ in to toggle an I/O shouldn't be impossible even with a 8MSPS burst, although having the automata in hardware is still better of course.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 12, 2025, 12:02:40 pm
content: QuoteInterrupts are not acceptable, because I should do a ton of DSP (including FFT and FIR) and GUI on the incoming ADC data, so I should figure out a way of doing it all without any interrupt, the only interrupt that I need is the DMA complete of say 5000 samples are ready to process.

author: hans
author_rank: Super Contributor
timestamp: July 12, 2025, 01:15:19 pm
content: You could also use CH1 for DMA and CH2 for chip select, each with their own timer compare values to adjust their phase / duty cycle respectively.But just wanted to point out the possibility of adding some interrupt code, to avoid premature optimization. I get why you wouldn't want to initiate and wait on a SPI transfer inside the interrupt, as that will probably take a few us which is too much CPU time. But simply toggling a GPIO is not that much time. E.g. 50k IRQ/s x 250ns is 1.25% CPU time. It may cause annoying jitter in other timings, if they also rely on CPU attention and are asynchronous to this process.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 12, 2025, 02:44:13 pm
content: You can probably use another timer in PWM mode with a duty of 99%, just running 8 times slower.

author: Silenos
author_rank: Regular Contributor
timestamp: July 12, 2025, 04:06:21 pm
content: What's the ADC? What is thr waveform it talks with, anything unusual?Also if you want to run H7 consider using QSPI - it has a very different interface, it is controlled more like a transaction generator.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: July 13, 2025, 12:43:53 pm
content: QuoteThe ADC would be ADS131M08,