Ayuda a comprender el tiempo de ejecución de AVR

3

Estoy trabajando con un microcontrolador Atmel ATMEGA32U4 - hoja de datos aquí con un cristal de 16 MHz para el reloj del sistema.

A mi entender, este chip tiene un fusible 'Dividir reloj por 8' programado de fábrica, lo que hace que mi sistema sea de 2 MHz. (Página 348 de la hoja de datos. CKDIV8 (bit 7) el valor predeterminado es 0, programado).

Me gustaría confirmar la velocidad del reloj de mi sistema, así que escribí un código para generar un pin bajo, retrasar un ciclo de reloj y luego volver a colocar el pin alto. Medir el tiempo que el pin está bajo debería ser igual a un ciclo de reloj.

Aquí está el código que usé para lograr esto:

//Set PORT E as output
DDRE = 0xFF;

asm volatile("nop\n\t"::); 
PORTE |= 1<<2;  

asm volatile("nop\n\t"::); 
PORTE &=~ (1<<2);   

asm volatile("nop\n\t"::); 
PORTE |= 1<<2;  

asm volatile("nop\n\t"::); 
PORTE &=~ (1<<2);

Un 'nop' es igual a un ciclo de reloj, según el manual del conjunto de instrucciones AVR , página 108 .

En base a esta información, asumiría una instrucción 'nop' para tomar 500 nanosegundos. ¿Es esta suposición correcta? (16 MHz / 8 = 2 MHz. \ $ \ Frac1 {2MHz} \ $ = 500ns)

Aquí hay un diagrama de alcance de mis hallazgos:

Parece que el tiempo de ejecución 'nop' es de solo 200 ns (5 MHz) Yo esperaría que hubiera una sobrecarga adicional usando C para establecer el puerto alto / bajo, por lo que 500 ns es en realidad el tiempo mínimo que esperaría que el pin sea bajo. Pero como se puede ver en mis cursores de medición 'a' y 'b', ni siquiera está cerca de 500 ns.

¿Puede alguien explicar qué está pasando?
¿Hay algún error en mi método?
¿Me estoy perdiendo algo estúpido? : p
Gracias por cualquier ayuda!

    
pregunta dext0rb

3 respuestas

4

Este es un mal enfoque. Lo ideal sería medir una gran cantidad de comandos NOP, por ejemplo, un millón, en lugar de uno solo. Creo que es la primera cosa tonta que puedo encontrar. No me sorprendería mucho si su número cambia justo después de implementar ese cambio.

Además, ¿puede ver el desmontaje de su código C? Ni siquiera trataría de juzgar el tiempo sin ver exactamente qué código de ensamblaje se generó a partir de mi código C. Intentaría trabajar hacia atrás desde el ensamblaje para calcular cuál debería ser el tiempo de ejecución (a través del manual de instrucciones que encontró). De esa manera puede ver si la medición del alcance está desactivada por poco o por mucho. Un poco podría significar que cometió un error de matemáticas. Mucho puede significar que una de tus suposiciones es incorrecta.

    
respondido por el AngryEE
2

Hace un tiempo verifiqué varios ajustes de reloj de un ATtiny45 con el código C a continuación. Aunque no es perfecto con el bucle while, las rutinas de retardo vienen bastante bien optimizadas con las bibliotecas avr-gcc. Llamo al programa '1kHz.cpp'. Verifique en la hoja de datos qué pin exacto produce la onda de bloque.

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
  DDRB = 0x10;
  PORTB = 0x00;

  while (1)
  {
    PORTB = 0x00; _delay_us(500);  // pin low, then wait
    PORTB = 0x10; _delay_us(500);  // pin high, then wait
  }
}

El truco con el reloj es compilar el código con la configuración de reloj adecuada. En Linux se ve así, pero Windows debería ser razonablemente similar. Como ves, el ATtiny45 tiene un reloj predeterminado de fábrica de 1MHz.

freq=8000000/8
baud=19200
src=1kHz.cpp
avr=attiny45
port=/dev/ttyUSB1

# Compile
avr-gcc -g -DF_CPU=$freq -Wall -Os -mmcu=$avr -c -o tmp.o $src
# Link
avr-gcc -g -DF_CPU=$freq -Wall -Os -mmcu=$avr -o tmp.elf tmp.o
# Convert to Intel .hex (required for my programmer)
avr-objcopy -j .text -j .data -O ihex tmp.elf tmp.hex
# Program the device
avrdude -p $avr -c stk500v1 -P $port -b $baud -v -U flash:w:tmp.hex

BTW experimentar con la configuración del reloj puede bloquear su dispositivo, así que tenga cuidado con los fusibles que configura / desactiva. Unbricking solo se puede hacer con un programador de alto voltaje (que se puede hacer con un Arduino de repuesto).

    
respondido por el jippie
1

Si está realizando un análisis de tiempo en el nivel de las instrucciones individuales, debe mirar el desmontaje de su programa o, en primer lugar, instalarlo en el ensamblaje. Luego, puede indicar las duraciones exactas de la hoja de datos contando los ciclos de cada instrucción:

Sin embargo, si solo desea verificar que la frecuencia del reloj principal es correcta, hay una solución más sencilla: simplemente genere una salida de baja frecuencia. (0.1 Hz a algunos kHz, dependiendo de lo que use para medirlo, un LED parpadeante y un cronómetro podrían ser suficientes). Puede usar uno de los temporizadores de 16 bits para eso, o simplemente usar un bucle con un largo ocupado. espera, por ejemplo %código%. El tiempo dedicado a los saltos del bucle será despreciable.

    
respondido por el Stefan Paul Noack

Lea otras preguntas en las etiquetas