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?
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);
}
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?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.
m.alin
viejo contador de tiempo