Timings básicos con un STM32

Estoy buscando hacer tiempos muy básicos en un STM32. Por ejemplo, me gustaría programar mi STM32 para generar bytes en el UART durante 1 minuto. ¿Qué reloj/cronómetro debo usar?

Mirando a través del manual de referencia , hay muchos relojes disponibles. Parece que algunos de los relojes más apropiados serían el Reloj de Tiempo Real (RTC), un Temporizador de Propósito General o un Temporizador de Control Avanzado. ¿Qué reloj sería el más fácil de usar para hacer tiempos fijos básicos?

Un temporizador de propósito general suena menos complejo para su tarea
github.com/dwelch67 Tengo varios ejemplos de cortex-m. Tenga en cuenta que el cortex-m4 no tiene el temporizador sysstick que tiene el cortex-m3, no se acostumbre a que esté ahí. (o tal vez fue la corteza-m0 que no lo tiene). Sin embargo, la respuesta corta es simplemente elegir uno de los temporizadores, los de uso general suelen ser fáciles, necesitan uno que pueda contar hasta un segundo sin demasiados problemas. no es necesario usar interrupciones, demasiado complicado hasta más tarde, comience sin interrupciones.

Respuestas (3)

Para tareas periódicas, recomendaría usar una interrupción SysTick. Aquí hay una aplicación de ejemplo:

void SysTick_Handler(void) {
    static uint8_t tick   = 0;
    static uint8_t second = 0;
    static uint8_t minute = 0;

    switch (tick++) {
        case 0:
            // task 0 here
            break;
        case 1:
            // task 1 here
            break;
        // and so on
        case 99:
            tick = 0;
            second = (second < 59) ? (second + 1) : 0;
            minute = (second == 0) ? ((minute < 59) ? (minute + 1) : 0) : minute;
            break;  
    }
}

int main(void)
{
    // interrupt at 100Hz
    SysTick_Config(SystemCoreClock/100);

    while (1);
}
Este es un ejemplo básico, sí, pero no debe usar un número entero de 8 bits para su variable de marca. Dependiendo de la frecuencia de la interrupción del sysstick, debería ser al menos una variable de 16 bits. De lo contrario, es posible que nunca pueda contar lo suficientemente alto como para alcanzar un segundo, especialmente si la interrupción de su sysstick se ejecuta mediante un cristal de reloj típico de 32 KHz.
El contador de ticks solo llega a 99 y luego se reinicia, así que no estoy seguro de lo que quieres decir.
No sé sobre esta plataforma, pero; si SysTick_Handler()es una rutina de interrupción, ¿tendrá un desbordamiento de pila si coloca demasiado código en ella, o es una buena práctica hacerlo?
@abdullahkahraman Esas variables en el isr son estáticas, no están en la pila
Mi punto es que, para la mayoría de las circunstancias, he necesitado una mejor precisión de tiempo que un tic de 10 ms; por lo general, 1 ms funciona mejor para mí. Creo que es una mala práctica tener un valor de 8 bits para almacenar los ticks, ya que es muy probable que desee cambiar el período de interrupción de algo así como 10 ms a 1 ms. Una variable de 8 bits no puede contar hasta 1000, por lo que nunca llegará a su declaración de caso que incrementa los segundos. No es un miedo vago: HE HECHO esto antes, por eso lo llamo mejores prácticas.

Una limitación de SysTick es que su velocidad está ligada al reloj principal. Si ralentiza el reloj principal para reducir el consumo de energía, SysTick se ralentizará en consecuencia. Muchas (¿todas?) partes STM32 tienen algún tipo de función de "reloj en tiempo real" que puede ejecutarse independientemente del reloj principal; en algunos casos, incluso puede funcionar independientemente de la fuente de alimentación principal. Las funciones de reloj en tiempo real se pueden programar para activar la CPU en momentos específicos, aunque su precisión es bastante limitada (p. ej., algunas restringen las activaciones a incrementos de un segundo a menos que se utilicen algunos trucos bastante repulsivos). Tenga en cuenta también que algunos chips, por alguna razón que me desconcierta por completo, almacenan el tiempo de manera bastante molesta como (0-9) segundos + (0-5) decenas de segundos + (0-9) minutos + (0-5) decenas de minutos + (0-9) horas hasta 23, más (0-2) decenas de horas hasta 24, etc.

uint32_t hora_presente;
uint32_t última_lectura_del_reloj;
uint32_t última_lectura_sin procesar;

uint32_t get_time (vacío)
{
 // Captura segundos y minutos 0-9, en formato BCD
  uint32_t esta_lectura_del_reloj = (RTC->TR) & 0x0FFF;
  // Salida anticipada si no hay cambios
  if (this_clock_reading == last_raw_reading)
    volver hora_presente;
  last_raw_reading = this_clock_reading;
 // Cada diez minutos deben ser 600 segundos, no 4096
  esta_lectura_del_reloj -= (4096-600)*(esta_lectura_del_reloj & 0x0F00)
 // Cada minuto debe ser de 60 segundos, no de 256
  lectura_de_este_reloj -= (256-60)*(lectura_de_este_reloj & 0x0F00)
 // Cada diez segundos deben ser 10 segundos, no 16
  lectura_de_este_reloj -= (16-10)*(lectura_de_este_reloj & 0x00F0);
  // Actualizar la hora actual
  if (última_lectura_del_reloj > esta_lectura_del_reloj)
    hora_presente += 3600 + esta_lectura_del_reloj - última_lectura_del_reloj;
  demás
    hora_presente += esta_lectura_del_reloj - ultima_lectura_del_reloj;
  ultima_lectura_del_reloj = esta_lectura_del_reloj;

  volver hora_presente;
}
int set_alarm(uint32_t alarm_time) // Devuelve -1 si ya pasó la hora de la alarma
{
  int32_t temperatura;
  // ¿Qué tan lejos en el futuro (si lo hay) es la hora de la alarma?
  temp = (alarm_time - get_time());
  if (temp 3000) // Establecer alarma un máximo de 50 minutos en el futuro
    temperatura = 3000;
 // Calcular la lectura del reloj cuando deberíamos tener nuestra alarma
  temp += ultima_lectura_del_reloj;
  si (temperatura > 3600)
    temperatura -= 3600;
 // Convierte a BCD deshaciendo las correcciones que se harían al leer
  temperatura += (16-10)*(temperatura/10) + (256-60)*(temperatura/60) + (4096-60)*(temperatura/600);
 // Ahora almacene el valor de la alarma (primero habilite los registros para escritura)
  RTC->WPR = 0xCA;
  RTC->WPR = 0x53;
  RTC->ALARMAR = temperatura | 0x80800000; // Establecer alarma (solo coincide con minutos y segundos)
}

Usando rutinas como las anteriores, el tiempo usado por la aplicación se mantendrá en un buen valor de incremento uniforme de 32 bits. Se puede configurar una alarma con hasta 50 minutos de anticipación y luego hacer que el chip se duerma; el chip se despertará cuando llegue la hora de la alarma. Configurar una alarma con más de 50 minutos de anticipación hará que se configure con 50 minutos de anticipación; cuando la CPU se despierta, puede "volver a la cama" si la hora de la alarma aún no ha llegado.

En el STM32L151, si se desea, se puede configurar el reloj para que funcione a 2x, 4x, 16x o varios otros múltiplos de la velocidad normal. El código anterior no cambiaría cuando se haga esto; los únicos efectos serían (1) los valores en present_time y el parámetro para establecer_alarma representarían una unidad de tiempo más rápida que segundos, y (2) el tiempo máximo antes de la siguiente alarma se escalaría en consecuencia. Es algo feo trabajar con unidades BCD que representan, por ejemplo, 37,5 segundos, 3,75 segundos, 0,625 segundos y 0,0625 segundos (que es lo que representarían los "minutos" y los "segundos" a una velocidad de 16x), pero así es la vida.

Si desea enviar datos en un UART durante un período de tiempo, puede usar un método de temporización del sistema como otros han sugerido para decidir cuándo dejar de enviar, pero esto sería ligeramente asíncrono con la operación de envío, que en sí misma consume tiempo.

Personalmente, me sentiría tentado a utilizar la cantidad de tiempo necesaria para enviar los caracteres como fuente de tiempo. Siempre que nunca deje de escribir un nuevo carácter en el registro "listo para enviar" antes de que el carácter existente esté completamente marcado, puede mantener la línea de transmisión en serie en uso constante y transmitir (velocidad en baudios) / (longitud de carácter) caracteres por segundo.

En muchos casos, cualquiera de los dos métodos será lo suficientemente preciso.