First timer delay missing -- AN4776 wrong?
I'm new here but have already gotten lots of good advice, so I'll press my luck and ask another question. Can't believe it hasn't come up before, but search doesn't find anything.
Simplest possible timer example. Start counting down from ARR value, busy loop wait for finish. No interrupts, no capture/compare, no output ... nothing. The code is right out of AN4776 "General-purpose timer cookbook.
It works, except that the first time it's called the delay is always zero. The following code is for TIM6 on an STM32F767ZI Nucleo-144 board, but I've had the same problem forever, including an STM32L476 Discovery, and bare 32F103xx and 32L0x1 chips (and with many different timers).
I really want to use one-pulse mode, but it doesn't matter -- the problem happens with or without it. Also see commented-out lines in the code. Explicitly setting CNT to zero, setting the SR register UIF flag, setting the PSC prescaler every time instead of just once at program initialization -- nothing helps.
It's not a fatal problem because I usually work around it by doing a "fake" first delay and then all the later ones work correctly. Still, I'd like to understand what's going on. The demo code below should turn the LED on for 4 seconds, off for 2, and then blink at 1 Hz indefinitely. Instead, there's a tiny, brief flash, then 2 seconds delay, and then the 1 Hz blinking.
Anyone have time to build and run this code (should be super simple to port to any system) and tell me what I'm doing wrong?
// for STM32F767 Nucleo-144 evaluation board
#include <stm32f767xx.h>
// default STM32F767xx clock is 16MHz HSI
static const uint32_t MAIN_CLOCK_KHZ = 16000;
void delay(
uint32_t milliseconds)
{
// initialize
TIM6->SR = 0;
TIM6->ARR = milliseconds;
// TIM6->CNT = 0;
// TIM6->SR = TIM_SR_UIF;
// enable and start one-pulse timer
TIM6->CR1 = TIM_CR1_CEN; // | TIM_CR1_OPM
// wait for timer to finish
while (!(TIM6->SR & TIM_SR_UIF))
asm("nop");
}
int main()
{
// enable peripherals
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
// reset peripherals
RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIOBRST;
RCC->APB1RSTR |= RCC_APB1RSTR_TIM6RST;
RCC->AHB1RSTR = 0;
RCC->APB1RSTR = 0;
// set on-board green LED port to output
GPIOB->MODER = GPIO_MODER_MODER0_0;
// set prescaler for 1 count per millisecond
TIM6->PSC = MAIN_CLOCK_KHZ - 1;
// turn LED on for 4 seconds
GPIOB->BSRR = GPIO_BSRR_BS_0;
delay(4000);
// turn LED off for 2 seconds
GPIOB->BSRR = GPIO_BSRR_BR_0;
delay(2000);
// infinite loop, blink LED at 1 Hz, 10% duty cycle
while (1) {
// turn LED on for 0.1 seconds
GPIOB->BSRR = GPIO_BSRR_BS_0;
delay(100);
// turn LED off for 0.9 seconds
GPIOB->BSRR = GPIO_BSRR_BR_0;
delay(900);
}
}