Comprender la configuración de un temporizador en un STM32F10x

Estoy tratando de entender cómo configurar correctamente el temporizador en un STM32F10x.

La placa está funcionando con un cristal externo de 16 MHz y estoy usando el PLL:

/* PLLCLK = 16MHz / 2 * 9 = 72 MHz */
RCC_PLLConfig(RCC_PLLSource_HSE_Div2, RCC_PLLMul_9);

por lo que la placa está funcionando a 72Mhz. Estoy configurando el temporizador de la siguiente manera:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseInitTypeDef timerInitstructure;
timerInitstructure.TIM_Prescaler =72000-1;
timerInitstructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitstructure.TIM_Period=1;
timerInitstructure.TIM_ClockDivision = TIM_CKD_DIV1;
timerInitstructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&timerInitstructure);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3,ENABLE);

Para asegurarme de configurar la frecuencia correcta, estoy alternando un pin usando la interrupción, pero cuando mido con el osciloscopio obtengo 2.784 kHz. ¿Alguien puede decirme por qué obtengo esta frecuencia?

Respuestas (2)

Escribí una respuesta más larga, antes de darme cuenta de la solución simple. Incluyo la respuesta completa en caso de que sea útil.


La respuesta rápida: los valores pasados ​​a la función TIM_TimeBaseInit() son solo de 16 bits, por lo que 69999 no es un valor válido. Deberá elegir el período y el preescalador de modo que ambos estén por debajo de 65536.

Sin embargo, hay un segundo problema, así que continúa leyendo :)


La respuesta detallada:

¿Parece que está apuntando a un reloj de 1kHz (1ms)?

Aquí está la ecuación básica, donde PSC es el preescalador y ARR es el período ("Registro de recarga automática"):

(PSC+1)(ARR+1) = (EventTime)(ClkFreq)

Para 1 ms:

(PSC+1)(ARR+1) = (1ms)(72MHz) = 72000

Ahora, puede elegir cualquier conjunto de valores para que esto sea cierto, siempre que no superen los 16 bits cada uno. Elegí:

(PSC+1) = 2y (ARR+1) = 36000,

que da PSC = 1y ARR = 35999.

Luego termina de configurar el reloj como lo has hecho.

En tu caso, eliges ARR=1 y PSC=71999. Dando la vuelta a las ecuaciones:

EventTime = (PSC+1)(ARR+1)/ClkFreq

EventTime = (1+1)(71999+1)/72MHz = 2ms, que no es lo que estás buscando.

Por supuesto, esto tampoco es lo que está obteniendo, debido al problema mencionado anteriormente con el tamaño del preescalador.


Por cierto, como señala @ScottSeidman, a menudo el temporizador está configurado correctamente pero el reloj en sí no. ¡Los árboles del reloj son sorprendentemente complicados! Una forma de verificar los relojes es usar la RCC_MCOConfig()función (o ver el registro RCC_CFGR). Se pueden usar para enviar los relojes internos directamente al pin MCO.

¡Buena suerte!

2 ms produciría un temporizador de 500 Hz, y el OP se acerca a 5 kHz (una onda cuadrada de 2,7 kHz). ¿Es esto debido al número inválido pasado a la estructura?
@ScottSeidman Sí, escribí esa parte antes de darme cuenta del problema del tamaño de 16 bits. Acabo de editar mi respuesta un poco para mayor claridad. Gracias :)
Para completar el resto de la familia STM32-ARM Cortex, agregaré que a menudo el problema es exactamente lo contrario: ¡el temporizador está configurado correctamente y el reloj del sistema está mal! Esto es menos probable para los F1, donde el reloj es bastante sencillo, pero un problema real para los F4 y demás, donde se utilizan rutinas de configuración de reloj bastante arcanas basadas en Excel para proporcionar archivos de inicio. De hecho, a menudo compruebo la configuración de mi reloj cambiando un poco con un temporizador.
Bueno, no sé si podría estar lo suficientemente agradecido por esta gran respuesta. muchas gracias

@Bitsmack ha dado una gran respuesta, sin embargo, hay otro parámetro en juego que no se ha mencionado: TIM_ClockDivision. Es simplemente el divisor de reloj de hardware que se encuentra entre busclock y el contador, y tiene valores discretos de 1, 2 o 4 si la memoria sirve. En el ejemplo del OP es 1 (TIM_CKD_DIV1). Entonces, si por alguna razón necesita llevar ClkFreq a un rango en el que pueda usar valores de 16 bits para PSC y/o ARR, entonces este parámetro será útil. No tanto en este caso, pero si se trata de núcleos a frecuencias de reloj más altas, entonces tal vez sí. También si necesita una mayor duración del temporizador (período). Entonces, la fórmula de @bitsmack podría escribirse como:

(PSC+1)(ARR+1) = (EventTime)[(ClkFreq)/ClkDiv]

Una forma intuitiva de pensar en los temporizadores básicos es que la frecuencia del evento del temporizador (nota: no el tiempo del evento) es simplemente el reloj de entrada dividido por cada uno de estos parámetros:

EventFreq = ClkFreq/ClkDiv/(PSC+1)/(ARR+1), and
EventTime = 1/EventFreq

lo que nos lleva a la fórmula de bitsmack. Puede enmarcar su requisito como un requisito de intervalo de tiempo de evento (quiero un temporizador con un período de 1 ms) o como un requisito de frecuencia de evento (quiero una tasa de interrupción de 1 kHz), los cuales se cumplen con las fórmulas anteriores.