Paso 2: Nuestro primer AVR C proyecto: Hola mundo, LED!
El "Hola mundo" de la programación del microcontrolador es el LED que parpadea. Así que esto es justo lo que intentaremos lograr con nuestro primer programa!
Nota: Puede obtener el código completo de estos programas en mi repositorio de github. También es mucho mejor ver el código porque github ha resaltado de la sintaxis correcta.
No queremos hacer todos los pasos de compilación, enlaces, etc. a mano así que lo primero que necesitamos es un Makefile. Si utilizas Crosspack AVR en os x, puede utilizar el comando avr-proyecto en el Terminal que crea automáticamente un archivo make para nosotros (y para un proyecto de XCode que no necesitamos por lo que se puede borrar). De lo contrario, puede utilizar la siguiente plantilla para su Makefile. Tenga en cuenta que debe modificar las primeras líneas de esta plantilla para su propia configuración (es decir, establecer las variables de dispositivo y programador).
--
#
# Plantilla Makefile para ATtiny45
# Derivados de AVR Crosspack plantilla
#
DISPOSITIVO = attiny45 # ver avr-ayuda para todos los dispositivos posibles
RELOJ = 1000000 # 1Mhz
Programador = - c usbtiny -P usb # para el uso de Adafruit USBtiny
OBJETOS = main.o # añadir más objetos para cada archivo .c aquí
FUSIBLES = - U lfuse:w:0x62:m - U hfuse:w:0xdf:m - U efuse:w:0xff:m # configuración como tomado de http://www.engbedded.com/fusecalc/
AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE)
COMPILAR = avr-gcc-pared -Os-DF_CPU=$(CLOCK)-mmcu=$(DEVICE)
objetivos simbólicos #:
todos: main.hex
. co:
$(COMPILE) - c $< -o $@
. S.o:
$(COMPILE) - x ensamblador con cpp - c $< -o $@
. CS:
$(COMPILE) -S $< -o $@
Flash: todos
Flash:w:main.hex:i - U $(AVRDUDE)
fusible:
$(AVRDUDE) $(FUSES)
instalar: fusible de flash
# Si utilizas un gestor de arranque, cambiar adecuadamente el siguiente comando:
carga: todos
bootloadHID main.hex
limpiar:
RM -f main.hex main.elf $(OBJECTS)
objetivos de archivo #:
Main.ELF: $(OBJECTS)
$(COMPILE) -o main.elf $(OBJECTS)
Main.hex: main.elf
RM -f main.hex
avr-objcopy - j .text -j. Data - O ihex main.elf main.hex
AVR-tamaño - formato = avr--mcu=$(DEVICE) main.elf
# Si tiene una sección EEPROM, también debe crear un archivo hex para el
# EEPROM y agregarlo en el destino del "flash".
# Metas para depuración de código y análisis:
disasm: main.elf
avr-objdump main.elf -d
CPP:
$(COMPILE) -E main.c
--
Usted puede preguntarse, ¿qué es esto "Fusiona"? Al programar el microcontrolador con un programador establecerá algunos bits en el chip que son algún tipo de configuración inicial. Utilice esta calculadora para configurar los bits de la derecha. Con esta configuración se puede saber por ejemplo el microcontrolador a utilizar el pin RESET como pines de I/O normal (que es útil cuando tiene sólo 8 pins!).
A continuación, necesita un archivo main.c mínima que tiene este aspecto:
--
#include < avr/io.h >
int Main {}
for(;;) {
bucle principal
}
return 0; nunca llegó a
}
--
Este programa no hace realmente nada, pero vamos a intentar compilar sin embargo. Tipo de hacer en el terminal y compilar y enlazar el programa. Incluso muestra el tamaño de su programa y cuánto ocupará en el dispositivo.
hacer llamará todos blanco de su Makefile. Hay otros objetivos importantes en su Makefile:
- limpia eliminar archivos binarios todos generados y que compile todo nuevo
- instalar transmitirá su programa al microcontrolador mediante un programador. más sobre eso más adelante
Ahora deja paso a paso crea un programa completo para dejar dos LEDs parpadean alternadamente:
En primer lugar, debemos incluir algunos encabezados para las funciones comunes de C de AVR. Se incluyeron ya avr/io.h para la dirección de I/O, que significa leer y escribir desde y hacia nuestros pins. También necesitaremos una función de "retraso" porque queremos que los LEDs parpadear durante un tiempo especificado. Esta función se incluye en util/delay.h. A continuación definimos los pines que utilizamos y el tiempo de retardo. Así las primeras líneas de nuestro programa este aspecto:
--
#include < avr/io.h >
#include < util/delay.h >
Definir pines
#define PIN_LED1 PB0
#define PIN_LED2 PB1
Definir el tiempo de retardo en ms
#define DELAY_MS 500
--
Necesitamos un ayudante macros y funciones que utilizamos a menudo (en el futuro). En AVR-C, se establece "alta" a un pin específico por escrito un registro de puerto específico usando operaciones bit a bit un bit «1». En nuestro ATtiny, sólo tenemos "PORTB" como/O-port. Cuando queremos configurar un pin "PB0" a "alto" escribimos: PORTB | = (1 << PB0);
Ajuste a "bajo" trabaja asimismo con una operación bit a bit, colocando la broca en esta posición "0" (lógico, no 1): PORTB & = ~ (1 << PB0);
Utilizamos esta información para escribir dos macros para configurar un pin en un determinado puerto "baja" o "alta". Además definimos una función que nos permite crear tiempos de demora. Es el problema con tiempos de demora, que el temporizador interno (o reloj contador) registrar voluntad desbordamiento muy rápido ya que generalmente es sólo un contador de 8 bits. Por lo tanto, definimos una función que divide nuestro retraso en trozos de 10 ms y esperar X tiempo 10 ms para obtener la larga espera.
--
escritura digital "alta" al perno < pn > Puerto < prt >
#define DIGIWRITE_H (prt, pn) prt | = (1 << pn)
escritura digital "baja" al perno < pn > Puerto < prt >
#define DIGIWRITE_L (prt, pn) prt & = ~ (1 << pn)
Definir la función de retardo largo
void long_delay_ms (uint16_t ms) {}
para (ms = 10; em > 0; ms-) _delay_ms(10);
}
--
Ahora vamos a pasar a la función main() . En primer lugar, necesitamos modificar el "registro de dirección de datos" para el puerto B, que se encuentra en la variable DDRB. Este registro indica el chip, que pines pueden obtener datos de entrada y que pines producirá un voltaje de salida. De forma predeterminada, todos los pines se establecen en la "entrada". Cuando nosotros queremos fijar ciertos pines a "salida" necesitamos establecer su registro bit a "1"
El resto es muy fácil: nosotros ahora solo llame al DIGIWRITE_L() y DIGIWRITE_H() para los pines respectivos y alternan el estado mediante el uso de un variable de palanca. Luego agregamos un tiempo de retraso.
Observe el uso de uint8_t para la variable de la palanca . Al escribir el código para chips con muy una pequeña cantidad de memoria flash, es fundamental utilizar siempre el tipo de datos más pequeño posible. Usted puede cambiar a, por ejemplo, int32_t y verás que el consumo de memoria aumenta ligeramente.
Este es el código completo de nuestro bucle principal:
--
punto de entrada del programa
int Main {}
DDRB es el "registro de dirección de datos" para el puerto B
la ATtinyX5 solo tiene puerto B con pines utilizables
Ponemos los pines del LED para la "salida"
DDRB | = (1 << PIN_LED1) | (1 << PIN_LED2);
configuración inicial de los pines a "baja"
DIGIWRITE_L (PORTB, PIN_LED1);
DIGIWRITE_L (PORTB, PIN_LED2);
bucle principal
palanca de uint8_t = 0;
for(;;) {
alternar entre los LEDs que se deje de parpadear
¿Digiwrite_l (PORTB, (palanca == 0? PIN_LED1: PIN_LED2));
¿Digiwrite_h (PORTB, (palanca == 0? PIN_LED2: PIN_LED1));
Alternave la variable palanca
palanca =! cambiar;
hacer un largo retraso
long_delay_ms(DELAY_MS);
}
return 0; / * nunca * /
}
--
Tipo hacer para compilar el código. Generará un .hex-archivo. En el siguiente paso, voy a explicar cómo nos sube esto a nuestro ATtiny.