El uso de avr-gcc _delay_ms hace que el chip se congele

4

Tengo un chip ATmega328-PU que está configurado para usar el oscilador interno sin dividir la velocidad del reloj entre 8. Lo primero que hice fue usar un programa realmente simple que cargué; un programa que activaba y desactivaba todos los pines en PORTB. Al igual que:

#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/delay.h>

int main(void) {
    DDRB = 0xFF;
        PORTB = 0xFF;

    while(1) {
            PORTB = 0x00;
            _delay_ms(50);
            PORTB = 0xFF;
            _delay_ms(50);
    }

    return 0;
}
  • Actualización: He intentado lo que Jon L sugirió. Sigue con el mismo resultado.
  • Actualización 2: he intentado lo que sugirió Kevin Vermeer. El chip parece congelarse cuando entra en el primer bucle. Usando el temporizador con un valor < 65000 hace que el LED se encienda directamente, usando un valor > 65000 resultados en el LED nunca se encienden.
  • Actualización 3: he intentado seguir con un nuevo chip idéntico con los mismos resultados
  • Actualización 4: enchufé simulavr y avr-gdb para ver si encontré algo, esta fue la salida:

    memory.c: 267: ADVERTENCIA: * * Intente escribir io reg inválido: ADCL en 0x0024 memory.c: 267: ADVERTENCIA: * * Intente escribir io reg inválido: ADCH en 0x0025 memory.c: 267: ADVERTENCIA: * * Intente escribir io reg inválido: ADCH en 0x0025 decoder.h: 59: ADVERTENCIA: Opcode desconocido: 0xffff

Entonces el código de operación desconocido se repite para siempre

Sin embargo, cuando subí el programa, llega a la segunda instrucción en main y luego se congela. Dejando todos los pines de PORTB en ALTO. Hasta ahora lo he intentado:

  • Diferentes ms entre 10 y 1000 para ver si hay algunos valores que no se pueden usar
  • Se cambió F_CPU a 1000000UL en caso de que usara CKDIV8.
  • Looping _delay_ms (1) en una función separada hasta que se itere a la cantidad dada
  • Recompilado y subido varias veces
  • Se intentó restablecer varias veces
  • Utiliza diferentes pines PORTB
  • F_CPU definido a partir de los argumentos del compilador -DF_CPU=8000000

Por qué pregunto aquí y no por stackoverflow.com es porque creo que debería comenzar a eliminar los errores en el nivel más bajo de abstracción, es decir; hardware.

Entonces, ¿cuál podría ser el problema?

Aquí hay información sobre mi configuración:

  • Sistema operativo: OS X 10.7.3
  • Programador: AVRisp MKII
  • Uploader: avrdude
  • Compilador: avr-gcc
  • hex de Bin 2: avr-objcopy

Configuración de fusibles:

avrdude: safemode: lfuse reads as E2
avrdude: safemode: hfuse reads as D9
avrdude: safemode: efuse reads as 7

Entrada en la calculadora de fusibles AVR :

makefile:

main:
        avr-gcc -g -Os -Wall -mmcu=atmega328 -c ../src/example.c

hex:
        avr-objcopy -j .text -j .data -O ihex example.o example.hex

dump:
        avr-objdump -h -S example.o > example.lst

upload:
        avrdude -p m328 -c avrispmkII -P usb -U flash:w:example.hex

clean:
        rm -f *.o
        rm -f *.hex
        rm -f *.lst

Pines utilizados en el chip:

  • Pin 7 (VCC): suministro de 5 voltios
  • Pin 8 (GND): suelo
  • Pin 14 (PB0): resistencia y LED
pregunta rzetterberg

3 respuestas

3

Sospecho que el problema no está en tu código C sino en tu Makefile.

Las siguientes líneas en su Makefile producen un archivo de objeto example.o .

main:
    avr-gcc -g -Os -Wall -mmcu=atmega328 -c ../src/example.c

El archivo .o creado solo contiene los símbolos y el código de example.c , no la fuente adicional requerida para que realmente se ejecute en un sistema de destino como las tablas de salto de vector de interrupción y el código para inicializar el segmento de RAM BSS a ceros y cargue sus secciones de datos inicializadas.

Necesitará agregar una línea adicional como esta para ejecutar el enlazador y generar un objeto de salida adecuado para descargar en la parte AVR. Alternativamente, use avr-ld , pero tendrá que encontrar todas las opciones de vinculador requeridas.

main.elf: example.o
    avr-gcc example.o -o main.elf

Puedes usar avr-objdump --disassemble-all <filename> tanto en example.o como en main.elf para verificar el contenido diferente de cada archivo.

Siempre es una buena idea tratar de reducir su problema en pasos al ejemplo más simple posible. En este caso, probablemente significaría caer en el software AVR Studio y crear un proyecto que se ejecute en el simulador utilizando su proceso de construcción administrado. Desde allí, podría exportar el Makefile en uso por su proceso de construcción usando la opción de menú 'Exportar Makefile'. El makefile generado podría compararse con su versión.

En realidad, probablemente sea una buena idea usar un Makefile similar al generado por AVR Studio porque ya tiene definidas las reglas correctas, solo tiene que configurar algunas variables con respecto a qué objetos deben generarse y la Nombre final del archivo de destino.

    
respondido por el Austin Phillips
3

Para la velocidad de su CPU:

#define F_CPU 80000000UL

¿Tienes un 0 extra ahí? Eso lee 80 MHz. Creo que quieres 8 MHz.

EDITAR: después de verificar la hoja de datos, su valor lfuse parece estar bien si desea usar el oscilador interno con CKDIV8 deshabilitado.

    
respondido por el Jon L
3

Su programa debería poder usar _delay_ms() , pero hay otras formas de retrasar.

_delay_ms() simplemente llama a _delay_us() , que invoca a _delay_loop_1() en . _delay_loop_1() , como su nombre lo indica, simplemente hace un bucle. Utiliza 3 instrucciones por bucle (para comparaciones, incrementos y saltos), y el tiempo empleado para cada instrucción es constante y conocido, por lo que puede demorar un tiempo dado simplemente ejecutándose en un bucle.

Para eliminar cualquier confusión sobre la estructura de su programa y la ruta de búsqueda F_CPU , puede volver a crear la función (de una manera muy aproximada) en la función principal como esta:

#include <stdint.h>  // Typedefs 'unsigned long' or something to 'uint16_t',
                     // a 16-bit unsigned number
#include <avr/io.h>
int main(void) {
    uint16_t counter1, counter2;

    DDRB = 0xFF;
    PORTB = 0xFF;

    while(1) {
            PORTB ^= 0; // Use XOR to toggle
            for (counter1 = 0; counter1 < 10; counter1++) { 
                for (counter2 = 0; counter2 < 50000; counter2++) { /* Do nothing */ }
            }
    }

    return 0;
}

El otro método es usar los periféricos del temporizador a bordo, en lugar de implementar un temporizador con un bucle. Puede usar estos periféricos de muchas maneras, pero a continuación describiré las dos formas más simples. Primero, puede sondear el temporizador desde un bucle y hacer algo cuando el valor alcanza un cierto punto. En segundo lugar, puede hacer que el temporizador emita una interrupción cuando llegue a cierto punto y que su código no haga nada.

Para sondear desde un bucle, harías algo como esto:

#include <avr/io.h> 

int main (void) 
{ 
   DDRB = 0xFF; // Set port as output 
   TCCR1B |= _BV(CS10); // Set up timer 

   while(1)    { 
      // Check timer value in if statement, true when count matches 1/20th of a second 
      if (TCNT1 >= 49999) 
      { 
         PORTB ^= 0xFF; // Toggle the port 

         TCNT1 = 0; // Reset timer value 
      } 
   } 
}

Para emitir una interrupción:

#include <avr/io.h> 
#include <avr/interrupt.h> 

int main (void) 
{ 
   DDRB = 0xFF; // Set LED as output 
   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode 
   TIMSK |= (1 << OCIE1A); // Enable CTC interrupt 
   sei(); //  Enable global interrupts 
   OCR1A   = 10000; // Set CTC compare value, about 50ms.
   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64 

   while(1) { 
       // Do nothing, the interrupt is taking care of it.
   } 
} 

ISR(TIMER1_COMPA_vect) 
{ 
   PORTB ^= 0xFF; // Toggle the LED 
}

Los ejemplos de código anteriores se adaptaron del excelente tutorial de temporizador en AVR Freaks , también disponible en sitio web de Dean aquí en formato PDF .

Además, su código (y cada uno de los ejemplos anteriores) alterna todo el puerto. Su LED está en PB0; puede alternar solo este pin reemplazando cada asignación a PORTB con

PORTB ^= _BV(PB0);

que compara PORTB con solo 0x0000 0001 .

    
respondido por el Kevin Vermeer

Lea otras preguntas en las etiquetas