2025-09-05 3:08 AM - edited 2025-09-05 3:10 AM
I'm try to generate some pulses to PB0 using STM32F103C8T6 with the following code
#include <stdint.h>
// STM32F103C8 is a medium density device, hence we use the xB header
#include <stm32f103xb.h>
#if !defined(__SOFT_FP__) && defined(__ARM_FP)
#warning \
"FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif
void enable_clock() {
RCC->CR |= RCC_CR_HSION;
while ((RCC->CR & RCC_CR_HSIRDY) == 0);
}
// @formatter:off
#define HI 6
#define LO 3
uint16_t buffer[] = {
0, LO, HI, LO
};
// @formatter:on
void blink_dma_to_gpio() {
enable_clock();
// GPIO B configuration
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // Enable IO port B
GPIOB->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0); // GPIO B: Clear pin 6 configuration
GPIOB->CRL |= GPIO_CRL_CNF0_1 | GPIO_CRL_MODE0_0; // GPIO B: Set pin 6 to alternate function push-pull 10MHz output
// TIM3 configuration
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Enable TIM3
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // Enable alternate function
TIM3->PSC = 1 - 1;
TIM3->ARR = 10 - 1;
TIM3->CR1 |= TIM_CR1_ARPE;
TIM3->BDTR |= TIM_BDTR_MOE;
TIM3->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 // PWM mode 1
| TIM_CCMR2_OC3PE // Enable preload
;
TIM3->CCER |= TIM_CCER_CC3E; // Enable capture compare 3
TIM3->CCMR2 |= TIM_CCMR2_OC4M_0; // Active on match
TIM3->CCER |= TIM_CCER_CC4E; // Enable capture compare 4
TIM3->DIER |= TIM_DIER_CC4DE; // Enable DMA1 request
TIM3->CCR4 = 7;
// DMA1 configuration
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable DMA1
DMA1_Channel3->CPAR = (uint32_t) &TIM3->CCR3;
DMA1_Channel3->CMAR = (uint32_t) &buffer;
DMA1_Channel3->CNDTR = sizeof(buffer) / sizeof(buffer[0]);
DMA1_Channel3->CCR = DMA_CCR_PL // Very high priority
| DMA_CCR_MSIZE_0 // 16 bit peripheral
| DMA_CCR_PSIZE_0 // 16 bit peripheral
| DMA_CCR_MINC // Increment memory
| DMA_CCR_CIRC // Circular memory
| DMA_CCR_DIR // Memory to peripheral
| DMA_CCR_HTIE // Half transfer interrupt
| DMA_CCR_TCIE // Transfer complete interrupt
;
NVIC_EnableIRQ(DMA1_Channel3_IRQn);
// Enable peripherals
DMA1_Channel3->CCR |= DMA_CCR_EN;
TIM3->CR1 |= TIM_CR1_CEN;
}
int main() {
blink_dma_to_gpio();
for (;;);
}
void DMA1_Channel3_IRQHandler() {
if (DMA1->ISR & DMA_ISR_HTIF3) {
DMA1->IFCR = DMA_IFCR_CHTIF3;
}
if (DMA1->ISR & DMA_ISR_TCIF3) {
DMA1->IFCR = DMA_IFCR_CTCIF3;
}
}
I expect there to be only 3 pulses from the beginning to the end of the DMA buffer. However, when hooking to an oscilloscope, the pulses output are inconsistent.
Some pulses are right with short, long, and short pulses, but some pulses just have random long or short pulse in them. I can't see what's wrong with my code. Please let me know if there is anything potentially wrong with my code.
Solved! Go to Solution.
2025-09-05 4:23 AM
The DMA is pretty fast, but it isn't instantaneous. If it can't transfer the new compare value within a single TIM cycle (10 clocks in your case) the next cycle will re-use the stale compare value, resulting in a duplicate pulse.
I don't recall seeing a specification (for any STM32) for how many bus clocks a DMA transfer takes, but 6-10 is not unusual for other MCUs. Judging by the pattern in your trace I'm guessing it's about 12.
A quick experiment would be to slow down the timer by e.g. doubling the PSC (->(2-1)) to see if the symptom still appears.
2025-09-05 4:23 AM
The DMA is pretty fast, but it isn't instantaneous. If it can't transfer the new compare value within a single TIM cycle (10 clocks in your case) the next cycle will re-use the stale compare value, resulting in a duplicate pulse.
I don't recall seeing a specification (for any STM32) for how many bus clocks a DMA transfer takes, but 6-10 is not unusual for other MCUs. Judging by the pattern in your trace I'm guessing it's about 12.
A quick experiment would be to slow down the timer by e.g. doubling the PSC (->(2-1)) to see if the symptom still appears.
2025-09-05 4:37 AM
Yes, putting (12 -1) to the auto reload register actually makes it much more stable and fixes the issue. Thank you very much.
2025-09-05 6:08 AM
For future reference, it seems like ST does have a document about DMA latency at https://www.st.com/resource/en/application_note/cd00160362-using-the-stm32f0-f1-f3-g0-lx-series-dma-controller-stmicroelectronics.pdf
2025-09-05 6:21 AM
I hadn't seen this before. Thanks!