8 votos

Embedded C: la forma más elegante de insertar un retraso

Estoy trabajando en un proyecto que involucra un cortex-m4 mcu (LPC4370). Y necesito insertar un retraso al activar la optimización del compilador. Hasta ahora, mi solución era mover hacia arriba y hacia abajo una salida digital dentro de un ciclo for:

 for (int i = 0; i < 50000; i++)
{
     LPC_GPIO_PORT->B[DEBUGPIN_PORT][DEBUG_PIN1] = TRUE;
     LPC_GPIO_PORT->B[DEBUGPIN_PORT][DEBUG_PIN1] = FALSE;
}
 

Pero me pregunto si hay una mejor manera de engañar a GCC.

22voto

JukesOnYou Puntos 398

El contexto de este en línea no-dependencia de la demora es que falta aquí. Pero estoy asumiendo que usted necesita de un breve retraso durante la inicialización o de otra parte del código donde está permitido el bloqueo.

Su pregunta no debería ser ¿cómo engañar a GCC. Usted debe decirle a GCC lo que usted desea.

#pragma GCC push_options
#pragma GCC optimize ("O0")   

for(uint i=0; i<T; i++){__NOP()}

#pragma GCC pop_options

Desde la parte superior de mi cabeza, este bucle será de aproximadamente 5*T relojes.

(fuente)


Comentario justo por Colin en otra respuesta. Un NOP no está garantizado para tener ciclos en un M4. Si desea frenar las cosas, tal vez ISB (ras pipeline) es una mejor opción. Ver el Genérico de la Guía del Usuario.

12voto

shanyu Puntos 4775

El uso de un temporizador si tiene uno disponible. El SysTick es muy sencillo de configurar, con la documentación en el Cortex M4 guía del Usuario (o M0 si usted está en el M0 parte). Incremento de un número en su interrupción, y en su función de retardo puede bloquear hasta que el número se ha incrementado un cierto número de pasos.

Su parte contiene muchos temporizadores si el systick ya está en uso, y el principio sigue siendo el mismo. Si es diferente del temporizador se puede configurar como un contador, y basta con ver su registro de cuenta para evitar una interrupción.

Si usted realmente quiere hacer en el software, entonces usted puede poner asm("nop"); el interior del bucle. nop no tiene que tomar tiempo, el procesador puede eliminarlos de su cartera, sin ejecutarla, pero el compilador debe generar el bucle.

10voto

John Burger Puntos 648

No en detrimento de otras respuestas aquí, pero, exactamente, ¿qué longitud de retardo necesita? Algunas de las hojas de datos de mencionar nanosegundos; otros microsegundos; y otros milisegundos.

  • Nanosegundos retrasos generalmente son mejor servidos mediante la adición de "pérdida de tiempo" instrucciones. De hecho, a veces la velocidad misma de la microcontrolador significa que el retraso ha sido satisfechos entre el "conjunto de los pines de alta, a continuación, establecer el pin de baja" instrucciones que se muestran. De lo contrario, uno o más NOP, JMP-para-la próxima instrucción, u otros perder el tiempo-las instrucciones son suficientes.
  • Corto microsegundo retrasos podría ser realizado por una for de bucle (dependiendo de la CPU velocidad), pero no puede justificar la espera de que en un temporizador;
  • Milisegundo retrasos generalmente son mejor servidos por hacer algo completamente mientras se espera que el proceso se complete, entonces va de nuevo para asegurarse de que se haya completado antes de continuar.

En definitiva, todo depende de la periférica.

3voto

Neil Foley Puntos 1313

La mejor forma es usar el chip de temporizadores. Systick, RTC o periférica de los temporizadores. Estos tienen la ventaja de que el tiempo es preciso, determinista y puede ser adaptada fácilmente si velocidad de reloj de CPU se cambia. Opcionalmente, usted puede incluso dejar que la CPU de sueño y el uso de una señal de interrupción.

Sucio "ocupado-delay" bucles por otro lado, rara vez son precisas y vienen con diferentes problemas, tales como "acoplamiento" para un determinado conjunto de instrucciones de la CPU y el reloj.

Algunas cosas de la nota:

  • Alternar un pin GPIO repetidamente, es una mala idea, puesto que esta corriente innecesariamente, y potencialmente también causa problemas de EMC si la patilla está conectada a las huellas.
  • El uso de instrucciones NOP no podría funcionar. Muchas de las arquitecturas (como la Corteza M, si mal no recuerdo) son libres para saltar NOP sobre el nivel del CPU y en realidad no ejecutarlas.

Si desea insistir en la generación de un sucio ocupado-loop, entonces es suficiente sólo con volatile califica el bucle de iteración. Por ejemplo:

void dirty_delay (void)
{
  for(volatile uint32_t i=0; i<50000u; i++)
    ;
}

Esto está garantizado para generar diversos mierda de código. Por ejemplo ARM gcc -O3 -ffreestanding le da:

dirty_delay:
        mov     r3, #0
        sub     sp, sp, #8
        str     r3, [sp, #4]
        ldr     r3, [sp, #4]
        ldr     r2, .L7
        cmp     r3, r2
        bhi     .L1
.L3:
        ldr     r3, [sp, #4]
        add     r3, r3, #1
        str     r3, [sp, #4]
        ldr     r3, [sp, #4]
        cmp     r3, r2
        bls     .L3
.L1:
        add     sp, sp, #8
        bx      lr
.L7:
        .word   49999

Desde allí usted puede, en teoría, calcular la cantidad de garrapatas que cada instrucción tarda y cambiar el número mágico de 50000 en consecuencia. De la canalización, de la rama de predicción etc significa que el código se puede ejecutar más rápido que la suma de los ciclos de reloj, aunque. Ya que el compilador se decidió involucrar a la pila de almacenamiento de datos en caché podría también desempeñar un papel.

Mi punto aquí es que calcular con exactitud el tiempo que este código va a tomar realmente es difícil. Ensayo y error de benchmarking con un ámbito de aplicación es probablemente una de las más sensata idea de intentar cálculos teóricos.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X