Cómo implementar funciones de retraso en Keil ARM MDK [cerrado]

¿Cómo implementar la función de retraso en Keil ARM MDK que espera un tiempo determinado en microsegundos que se puede volver a compilar a cualquier velocidad de reloj razonable definida en la configuración del proyecto Keil? Y todo esto sin usar temporizadores.

¿Por qué no usas un temporizador?
@Leon: he tenido casos en los que no había más temporizadores disponibles. También he tenido casos en los que la latencia de interrupción fue mayor que el tiempo de retraso deseado.
Usar un temporizador no significa necesariamente usar una interrupción; por lo general, solo puede leer el temporizador directamente para tiempos cortos, y eso es mucho mejor que un ciclo cronometrado por software. Pero, obviamente, si te has quedado sin temporizadores, entonces esa no es una opción.
Anteriormente usaba IAR Embedded Workbench para AVR. Tiene un tipo de función de macro __delay_cycles() intrínseco muy agradable que el compilador sustituyó con el retraso de bloqueo del tiempo exacto de los ciclos del procesador. Usando #define FCPU 5000000UL, por ejemplo, podría implementar retrasos fijos independientes del reloj como este compilador __delay_cycles (FCPU * 1e-3) sustituido por el retraso correcto de 1 ms. Si necesitaba que esa parte del código funcionara para otra velocidad de reloj, simplemente cambié FCPU y volví a compilar. AVR-GCC tiene delay.h con funciones _delay_us() y _delay_ms() con funcionalidad similar pero con varias limitaciones.
Así que estoy buscando una forma similar de hacer pequeños retrasos de bloqueo en KEIL como IAR o AVR-GCC.
@x4mer, lo que he escrito es lo que están haciendo en segundo plano. Pueden estar haciéndolo en ensamblador, pero es casi lo mismo en C. Hay un poco de trabajo decente para agregar algo como esto y hacer que funcione perfectamente, este no es un comando simple que tiene C.
Los bucles de retardo no son algo que se transmita fácilmente, son específicos del núcleo de la CPU y la velocidad del reloj. Por lo tanto, mantiene una gran colección de #ifdefs con bucles de retardo calibrados y calculados. O bien, hace lo que sugiere @supercat y construye un temporizador de hardware.
@Roh: no agregue etiquetas que sean meramente incidentales pero que no tengan una relación real con el problema, y ​​​​especialmente no las agregue a preguntas de hace 6 años que no eran lo suficientemente específicas para el sitio para empezar: el cartel se confundió con plantee esto como una pregunta de la cadena de herramientas, ya que antes puede ser que tiene que ser una MCU y una pila de software de tiempo de ejecución, ninguno de los cuales se ha especificado, por lo que no se puede responder.
@ChrisStratton Hola, Chris, lo agregué solo porque aquí hay algunas respuestas que contienen códigos C/C++. así como Keil es un compilador de C y C++. Parte de tu argumento me resulta extraño. Voy a hablar de ti con los moderadores.
Las etiquetas son para lo que trata una pregunta , no para lo que simplemente contiene. Esta pregunta no es sobre C, y de hecho, aparece muy poco C real en esta página; en su mayoría, ve directivas y ensamblaje, y el verdadero tema y objetivo no es la cadena de herramientas sino el hardware.

Respuestas (4)

En mi humilde opinión, el mejor enfoque es tener un temporizador de hardware que nunca se escribe, sino que simplemente se ejecuta libremente. Esto le permitirá a uno emular fácilmente un número arbitrario de temporizadores de sondeo para duraciones de hasta la mitad de la duración del temporizador de hardware. Para "iniciar" un temporizador, simplemente calcule el valor del temporizador de hardware cuando debe expirar. Para ver si un temporizador ya ha expirado, reste el tiempo de expiración esperado del tiempo actual (considerando ambas cantidades como sin firmar) y vea si el resultado (sin firmar) excede la mitad del valor máximo del temporizador. Si es así, el temporizador no ha expirado.

Alternativamente, si se desea un temporizador para medir el tiempo desde que ocurrió un determinado evento, se puede bloquear el valor del temporizador cuando ocurrió el evento y luego calcular la diferencia entre el valor bloqueado y el tiempo presente.

Keil da un ejemplo de esto en su documentación keil.com/support/man/docs/gsac/gsac_gpio.htm . Creo que el temporizador de hardware SysTick es común a todos los Cortex-M0 y Cortex-M3, por lo que el enfoque es portátil.
Un problema con el uso de SysTick para ese propósito es que a menudo se usa con un valor de período programable que a menudo será más corto que los intervalos que uno quisiera cronometrar, y no es una potencia de dos. Por otro lado, si la tasa que uno tiene asignada para el temporizador SysTick representaría una precisión aceptable para un temporizador a más largo plazo, uno puede simplemente hacer que SysTick mantenga un contador de tics de 32 bits de funcionamiento libre y usarlo para cronometrar otros cosas.

Tendrá que determinar la duración de un NOP y, a continuación, usar #Defines para que, en función de la velocidad del reloj, inserte el número correcto.

Específicamente, desea saber la cantidad exacta de ciclos de reloj que se necesitan para completar 1 bucle del estilo:

for(long i=0;i<NUMBER_OF_LOOPS_REQUIRED;i++)
{
  __NOP();
}

Al hacer #define para el número de bucles requeridos, se reconfigura para que sea automático en función del retraso solicitado y la velocidad del reloj a la que se está ejecutando actualmente. Todo esto se puede envolver en una macro.

En ese sentido, cuando obtiene una indiferencia total de la velocidad del reloj en un módulo, alguien lo escribió porque pasó una gran cantidad de tiempo diseñando para que usted tenga la función.
Esto dependerá mucho del compilador, si cambia de versión, por ejemplo, la demora también puede cambiar.
esto probablemente funcionaría mejor si también codificara el ciclo en ensamblador. Como es posible que el optimizador no siempre elija las mismas instrucciones de bifurcación
@Thomas O, Sí, cuando actualiza las versiones de un compilador, muchas otras cosas también pueden desaparecer, normalmente este tipo de cosas se mantienen constantes, pero el primer paso después de actualizar los compiladores (que necesitaría una razón para hacerlo por un proyecto) probando que el sistema aún funciona.
@JobyTaffey, esa es una opción, la mayoría de los mejores compiladores, como iar.com, por ejemplo, le permiten cambiar la configuración de optimización por archivo. Los deshabilitaría para esta función. Si lo macro, tendrá que asegurarse de que su compilador no lo optimice. IAR cuando se establece en la optimización máxima detectará este bucle y lo eliminará. La mejor manera de medir el tiempo es una función de temporizador, ya sea que el autor de la pregunta quisiera evitarlo por alguna razón.
#if _Main_Crystal == 25000000
   #define LOOP_DELAY 400
#elif  _Main_Crystal == 16000000
   #define LOOP_DELAY 256
#else
   #error microsecond delay must be adjusted!
#endif
void usDelay( void )
{
   for (int i = 0; i < LOOP_DELAY; i++)
   {
      SERVICE_WATCH_DOG();
   }
}

donde SERVICE_WATCH_DOG()hay una macro para dar servicio al temporizador de vigilancia. Esto también se puede reemplazar con un archivo NOP.

Use una función de prueba para averiguar cuáles deben ser sus constantes LOOP_DELAY para cada frecuencia de reloj:

void TestDelay( void )
{
   SetIOPin();    // Use a scope to start measuring elapsed time here.
   usDelay();
   ResetIOPin();  // use a scope to end measuring elapsed time here.
}
Gracias, pero es obvio e inconveniente si está escribiendo un módulo de interfaz para compilarlo para velocidades de reloj desconocidas y variadas en el futuro.
@x4mer ¿No podría simplemente usar: #Definir LOOP_DELAY (_Main_Crystal / 1000000)

Se prefiere una referencia de temporizador de hardware porque un buen optimizador podría eliminar el bucle por completo o modificarlo en función de la configuración. El otro problema con los bucles de tiempo que no se menciona aquí es que si ocurre una interrupción durante su bucle de tiempo, podría llevar mucho más tiempo. Las interrupciones deben ser cortas de todos modos, pero no todos los sistemas siguen ese mantra.