Atmel ISP MCU programmer ATmega. Programador AVR USBasp

De wikijoan
Salta a la navegació Salta a la cerca

Introducció

Avr usb asp.JPG

He comprat aquest programador:

Necessito aquest programador de xips Atmel per fer una interfície USB per al joistick i botons, en el projecte de MAME:

D'altra banda, amb aquest programador puc programar directament els xips Atmel, i no limitar-me a l'Arduino (amb els seus xips que tenen bootloader). És convenient no confondre fer un projecte amb Arduino que fer-lo directament amb el microcontrolador, que també es pot fer. L'avantatge de l'arduino és que ja ve la placa preparada i que té un bon IDE. Però si jo vull fer una configuració de l'arduino i fer-me jo mateix les plaques PCB, aleshores es pot fer directament el projecte amb el xip atmel corresponent.

NOTA quan a la denominació Atmel i AVR. Atmel és l'empresa, i AVR és la família de microcontroladors:

Desenvolupament

El primer de tot és tenir clar el pinout del connector de 10 pins. Amb aquesta imatge es veu clar:

Usbasp connector.jpg

El primer punt de partida és el projecte que vull realitzar de la interface joistick-botons/USP per al cabinet MAME. Tinc dos enllaços per començar a treballar, a fer proves i entendre conceptes:

Es pot programar els xips amb la comanda uisp, però el més general és programar amb avrdude:

$ sudo apt-get install avrdude

El primer de tot és tenir clar que estic programant per al Atmega168 (no pas el Atmega8 com en l'enllaç), i que per tant;

$ sudo apt-get install avrdude
$ avrdude -p list -P usb -c usbasp
o bé
$ man avrdude
...
  m168 = ATMEGA168       [/etc/avrdude.conf:8173]
...

Per veure de quin tipus és el meu programador:

$ lsusb
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 008: ID 16c0:05dc VOTI USBasp AVR Programmer

Veig que és de tipus usbasp, i aquest és el tipus que he de ficar en la línia de comanda. D'altra banda, també he de tenir clar quin és el port on tinc ficat el programador. En aquest cas és el Bus 004 Device 008:

$ sudo avrdude -p m168 -P /dev/bus/usb/004/008 -c usbasp -Uflash:w:usb_game12-1.0.hex -B 1.0

avrdude: set SCK frequency to 750000 Hz
avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: error: programm enable: target doesn't answer. 1
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.

He de fer servir sudo per un tema de permisos, Es pot no utilitzar sudo ficant la regla corresponent en l'arxiu udev. Com veig, intento gravar l'arxiu hex proporcionat, però em respon dient que target doesn't answer. Això vol dir que realment no hi ha comunicació entre el servidor i el progrmador, el programador no respon.

També podem utilitzar de forma genèrica el port -P usb:

$ sudo avrdude -p m168 -P usb -c usbasp -Uflash:w:usb_game12-1.0.hex -B 1.0

avrdude: set SCK frequency to 750000 Hz
avrdude: warning: cannot set sck period. please check for usbasp firmware update
...
Do not worry about the SCK message. Ignore any SCK messages. If you ever need to run 'slow' just use the -B# argument. -B5 is fine for normal programming of a 1MHz AVR. -B1 is fine for a 8MHz AVR.

Així doncs, encara no funciona, i he de revisar les connexions. El connector l'he revisat detingudament, i el problema no bé per aqui... El que està clar és que el xip que vull programar se l'ha d'alimentar correctament, i hi ha aspectes de connexió i electrònica que s'han de tenir en compte. Per tant he de mirar altres enllaços. Falta doncs la part de connexió del xip, que no té res a veure amb el programador. I és que el programador val per quasi tota la família de xips AVR/Atmel, que poden tenir pinouts diferents (el Atmel8 i el Atmel168 tenen el mateix pinout).

tutorial AVRdude:

I obtinc una sèrie de conclusions:

Might have heard wrong. AVCC is just another VCC pin that needs to be connected to 5V. All the VCC pins and GND pins must be connected, and you need a BFC on the breadboard, and some .1uf caps right on the vcc pins.

...

The F_CPU entry specifies the microcontroller clock speed and is used by the _delay_ms function. The ATmega8 out of the box runs on a 1MHz internal clock, but this can be changed if required.
En el tutorial de fuses:
By default, chips that come from the factory have the Internal 8 MHz clock with 14CK + 65ms Startup.

Però això no és una contradicció:
First you need to set the SCK The default clock of an atmega is usually shipped with internal RC oscillator at 8.0MHz and with the fuse CKDIV8 programmed, resulting in 1.0MHz system clock.

Note: The -B argument controls the ISP bit clock period (in microseconds). This frequency must not be higher than 1/4 of the MCU clock.
initialization failed, rc=-1 Double check connections and try again, or use -F to override this check

This means that the programmer couldn't talk to the chip. If you are using a "simple" programmer such as a serial or parallel port bitbang programmer, it could mean the programmer is at fault. Otherwise, it usually means the programmer is OK but it couldnt find the chip.

Check that the chip is powered, plugged into the socket or programmer properly, the programming cables are plugged in correctly, the header is wired correctly, etc. 99% of the time, it is a problem with wiring.

Per veure configuracions correctes i que funcionen es pot cercar al google imatges per atmega168 usbasp. I trec conclusions importants:

  • Reset. Important la resistència del Reset a Vcc, per ex 10K. Això significa que el reset està a Vcc, però de fer és un pin negat. No cal que implementi el botó de reset.
  • Condensador power-rails. Important ficar un condensador entre Vcc i GND, no importa massa el valor.
  • AVCC ha d'estar connectat a Vcc, i tots els GND a GND.

Tinc un dubte de si he de ficar un cristall extern, o de si no cal ficar-lo doncs funciona l'oscil.lador intern. Finalment per tal de què funcioni he ficat un cristall extern de 4MHz (i caldria ficar els dos condensador de 27pF, però no ho he fet doncs funciona igualment). Així doncs, ja funciona:

Gravant el xip


$ sudo avrdude -p m168 -P usb -c usbasp -Uflash:w:usb_game12-1.0.hex -B 1

avrdude: set SCK frequency to 750000 Hz
avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e9406
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: set SCK frequency to 750000 Hz
avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: reading input file "usb_game12-1.0.hex"
avrdude: input file usb_game12-1.0.hex auto detected as Intel Hex
avrdude: writing flash (2558 bytes):

Writing | ################################################## | 100% 1.47s

avrdude: 2558 bytes of flash written
avrdude: verifying flash memory against usb_game12-1.0.hex:
avrdude: load data flash data from input file usb_game12-1.0.hex:
avrdude: input file usb_game12-1.0.hex auto detected as Intel Hex
avrdude: input file usb_game12-1.0.hex contains 2558 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 1.37s

avrdude: verifying ...
avrdude: 2558 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Amb l'opció

-Uflash:w:usb_game12-1.0.hex

estic dient que vull escriure (w) a la memòria flash els fitxer usb_game12-1.0.hex.

Fuses

Fuse bits or bytes: The fuses bytes are used to configure some parameters in the microcontroler. For example, fuse bytes are used to enable the use of an external crystal. Typically, the fuse bytes are given as one or two hexadecimal values.

AVR Fuse Calculator:

Els fuses que es graven en l'enllaç original per a la interface joystick/USB és; -U lfuse:w:0x62:m -U hfuse:w:0xdf:m -U efuse:w:0xf9:m Però recordem que això és per a Atmega8. Serà el mateix per Atmega168?

Fuse Reset Disable: This fuse turns the Reset pin into a normal pin instead of a special pin. If you turn this fuse on you cant program the chip using ISP anymore. I would suggest you never set this fuse unless you really mean to. By default, chips that come from the factory have this turned off (that is, Reset is enabled)

i ara ja puc programar els fuses:

$ sudo avrdude -p m168 -P usb -c usbasp -Uhfuse:w:0xc9:m -Ulfuse:w:0x9f:m

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e9406
avrdude: reading input file "0xc9"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xc9:
avrdude: load data hfuse data from input file 0xc9:
avrdude: input file 0xc9 contains 1 bytes
avrdude: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.00s

avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: reading input file "0x9f"
avrdude: writing lfuse (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0x9f:
avrdude: load data lfuse data from input file 0x9f:
avrdude: input file 0x9f contains 1 bytes
avrdude: reading on-chip lfuse data:

Reading | ################################################## | 100% 0.00s

avrdude: verifying ...
avrdude: 1 bytes of lfuse verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

L'opció era

-Uhfuse:w:0xc9:m -Ulfuse:w:0x9f:m

que significa que gravo (w) uns bytes especials que són el fuse alt i el fuse baix.

i d'on trec aquests valors per al fuse? I quins valors de fuse he de ficar? En l'enllaç del projecte es comenta:

For this project, here are the appropriate fuse values:
high byte = 0xc9, low byte = 0x9f

Necessito una calculadora de fuses:

per trobar aquests valors la configuració és

Els valors que se'm proporciona tenen la configuració:

  • Atmega8
  • LOW: BODEN, SUT1
  • HIGH: SPIEN, CKOPT, BOOTSZ1, BOOTSZ0

Amb els mateixos paràmetres però escollint el Atmega168 els valors que obtinc són els mateixos, tot i que automàticament canvien alguns paràmetres.

  • Atmega168
  • LOW: Clock output on PORTB0; [CKOUT=0], SUT1 (ext crystal > 8MHz)
  • HIGH: Serial program downloading (SPI) enabled; [SPIEN=0] ; Watch-dog Timer always on; [WDTON=0] ; BODLEVEL2 ; BODLEVEL1

Els valors que s'obtenen són en principi els mateixos. Fixem-nos que el projecte utilitza un cristall de 12MHz, i això és coherent amb el paràmetre SUT1.

Està explicat en el codi font del projecte, en el Makefile:

# Fuse high byte:
# 0xc9 = 1 1 0 0   1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000)
#        ^ ^ ^ ^   ^ ^ ^------ BOOTSZ0
#        | | | |   | +-------- BOOTSZ1
#        | | | |   + --------- EESAVE (don't preserve EEPROM over chip erase)
#        | | | +-------------- CKOPT (full output swing)
#        | | +---------------- SPIEN (allow serial programming)
#        | +------------------ WDTON (WDT not always on)
#        +-------------------- RSTDISBL (reset pin is enabled)
# Fuse low byte:
# 0x9f = 1 0 0 1   1 1 1 1
#        ^ ^ \ /   \--+--/
#        | |  |       +------- CKSEL 3..0 (external >8M crystal)
#        | |  +--------------- SUT 1..0 (crystal osc, BOD enabled)
#        | +------------------ BODEN (BrownOut Detector enabled)  
#        +-------------------- BODLEVEL (2.7V)

Conclusions

Ara que ja sé programar els xips AVR ja estic disposat a fer el projecte de la interface USB/joystick. D'aquest projecte es proporciona el fitxer hex i també el codi font. De fet, ara seria interessant fer un projecte senzill (Led intermitent, per ex) estudiant el codi font amb C, generant el hex file, gravant el xip i testejant el funcionament correcte.

Aquí van tres enllaços per programar amb avr-gcc:

Programming the AVR microcontroller with GCC, libc 1.0.4

AVR Microcontrollers in Linux HOWTO

A Brief Tutorial on Programming the AVR without Arduino

Està documentat a:

Blink LED

IMG 20150602 133122805.jpg

NOTA. Les proves de fer pampallugues amb un LED i jugar amb diferents freqüències ja s'havien fet a Programació_dels_microcontroladors_AVR:_avr-gcc#Lesson_1:_A_blinking_LED.

Aquestes són dues proves que funcionen de l'hola món del ATMega168: fer pampallugues d'un LED de 1 segon. Funciona amb el rellotge intern a 8 MHz. El que és important és que a part de gravar el programa, també s'ha de gravar els fuses correctes per dir que funciona amb el rellotge intern. En el primer cas no es divideix internament per 8 (-U lfuse:w:0xE2:m, funciona a 8MHz); i en el segon cas sí que es divideix internament per 8 (-U lfuse:w:0xE2:m, funciona a 1 MHz).

També hi ha una diferència entre els dos codis. El primer codi és més fàcil de llegir. El segon codi és a més baix nivell, més difícil de llegir.

blink_led_v1.c:

// funciona correctament. Sense rellotge extern
// http://wiki.joanillo.org/index.php/Programaci%C3%B3_dels_microcontroladors_AVR:_avr-gcc
// cd /home/joan/projectes/rec_baga/avr

// el primer que hem de fer és cremar els fuses correctament, tenint en compte que faig servir el rellotge intern (http://www.eleccelerator.com/fusecalc/fusecalc.php?chip=atmega168) (de fet són els valors per defecte excepte que trec el divide clock by 8 internally).
// $ sudo avrdude -b19200 -P usb -c usbasp -p m168 -U lfuse:w:0xE2:m -U hfuse:w:0xDF:m

// $ avr-gcc -g -mmcu=atmega168 -c blink_led_v1.c -Wa,-alh,-L -Os -o blink_led_v1.o > blink_led_v1.asm
// $ avr-gcc -g -mmcu=atmega168 -Wl,-Map,blink_led_v1.map -o blink_led_v1.elf blink_led_v1.o
// $ avr-objdump -h -S blink_led_v1.elf > blink_led_v1.lst
// $ avr-objcopy -j .text -j .data -O ihex blink_led_v1.elf blink_led_v1.hex
// $ avr-size blink_led_v1.elf
// $ cat blink_led_v1.hex
// ja podem programar el xip:
// $ sudo avrdude -b19200 -P usb -c usbasp -p m168 -U flash:w:blink_led_v1.hex

// pinout del Atmel168: http://hackaday.com/2010/10/23/avr-programming-introduction/
// utilitzo el port D: pins 2,3,4,5,6,11,12,13
#ifndef F_CPU
   #define F_CPU 8000000UL //el rellotge intern va a 8MHz
#endif

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

/*
 * Assumptions:
 * 	- LED connected to PORTD
 * 	- F_CPU is defined to be your cpu speed (preprocessor define)
 */

int main (void)
{
	/* set PORTD for output*/
	DDRD = 0xFF;

	while (1) {
		/* set PORTD high */
		PORTD = 0xFF; //escric en tot els pins del port (es podria escriure només en un dels pins)
		_delay_ms(1000);

		/* set PORTD low */
		PORTD = 0x00;
		_delay_ms(1000);
	}
	return 0;
}

blink_led_v2.c:

// funciona correctament. Sense rellotge extern
//exemple extret de:
//http://hackaday.com/2010/11/05/avr-programming-03-reading-and-compiling-code/
// http://wiki.joanillo.org/index.php/Programaci%C3%B3_dels_microcontroladors_AVR:_avr-gcc
// cd /home/joan/projectes/rec_baga/avr
// el primer que hem de fer és cremar els fuses correctament, tenint en compte que faig servir el rellotge intern (http://www.eleccelerator.com/fusecalc/fusecalc.php?chip=atmega168) (de fet són els valors per defecte. Divideixo internament per 8: CKDIV8).
// $ sudo avrdude -b19200 -P usb -c usbasp -p m168 -U lfuse:w:0x62:m -U hfuse:w:0xDF:m

// $ avr-gcc -g -mmcu=atmega168 -c blink_led_v2.c -Wa,-alh,-L -Os -o blink_led_v2.o > blink_led_v2.asm
// $ avr-gcc -g -mmcu=atmega168 -Wl,-Map,blink_led_v2.map -o blink_led_v2.elf blink_led_v2.o
// $ avr-objdump -h -S blink_led_v2.elf > blink_led_v2.lst
// $ avr-objcopy -j .text -j .data -O ihex blink_led_v2.elf blink_led_v2.hex
// $ avr-size blink_led_v2.elf
// $ cat blink_led_v2.hex
// ja podem programar el xip:
// $ sudo avrdude -b19200 -P usb -c usbasp -p m168 -U flash:w:blink_led_v2.hex

// pinout del Atmel168: http://hackaday.com/2010/10/23/avr-programming-introduction/
// utilitzo el port D, pin 2 (D0)
/*
* Hackaday.com AVR Tutorial firmware
* written by: Mike Szczys (@szczys)
* 10/24/2010
*
* ATmega168
* Blinks one LED conneced to PD0
*
* http://hackaday.com/2010/10/25/avr-programming-02-the-hardware/
*/

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

int main(void)
{

  //Setup the clock
  cli();			//Disable global interrupts
  //el rellotge intern va a 8MHz / 8 divisió interna = 1MHz. Si ho divideixo per 64 són 15624 cicles/s.
  TCCR1B |= 1<<CS11 | 1<<CS10;	//Divide by 64
  OCR1A = 15624;		//Count 15624 cycles for 1 second interrupt
  TCCR1B |= 1<<WGM12;		//Put Timer/Counter1 in CTC mode
  TIMSK1 |= 1<<OCIE1A;		//enable timer compare interrupt
  sei();			//Enable global interrupts

  //Setup the I/O for the LED

  DDRD |= (1<<0);		//Set PortD Pin0 as an output
  PORTD |= (1<<0);		//Set PortD Pin0 high to turn on LED

  while(1) { }			//Loop forever, interrupts do the rest
}

ISR(TIMER1_COMPA_vect)		//Interrupt Service Routine
{
  PORTD ^= (1<<0);		//Use xor to toggle the LED
}

En les proves 3 i 4 faig servir un switch per tal d'activar les pampallugues del led. M'he liat bastant per un error de sintaxi, i perquè s'ha d'entendre com funcionen els registres del ATMEL, i també s'ha de tenir clara la notació binària.

Faig servir el PD2 (tercer pin del PORTD) com a entrada, i el PD3 (quart pin del PORTD) com a sortida. Mirant el pinout del ATMEGA168 (http://hackaday.com/2010/10/23/avr-programming-introduction/), es correspon als pins 4 i 5. Faig servir el rellotge intern (8MHz) sense divisió interna.

La configuració del switch és important, doncs el switch pot estar normalment a 1 o normalment a 0. L'esquema que faig servir és aprofitant el rec_vaga_v4, on es veu que el switch el que fa és connectar el pin directament a terra, tan fàcil com això. Però perquè això funciona vol dir que el switch quan està obert ha d'haver-hi un 1 en el pin, i això significa activar la resistència de pull-up interna que es fa posant a 1 el pin. Per entendre el funcionament dels registres és important llegir:

Bàsicament el PORTX (on X val A,B,C,D) són tres registres:

  • DDRX: la direcció del pin
  • PORTX: registre que controla el voltatge dels pins, i per tant és per escriure. També és per escriure el valor per defecte, per indicar si els pins són normalment 1 o 0 (activar o desactivar la pull-up resistor). No confondre el registre PORTX amb el nom genèric del port: PORTX
  • PINX: registre per llegir el valor del pin.
DDRD &= ~(1<<PD2);//Makes tercer pin of PORTD as Input
  • 1 << PD2 és el mateix que 00000100 (operació de shift a nivell binari. correm a l'esquerra un 1 tres posicions.
  • ~(1<<PD2) és la negació: 11111011
  • DDRD &= ~(1<<PD2); és el mateix que DDRD = DDRD & ~(1<<PD2); -> DDRD = DDRD & 11111011, i per tant clarament el que fa és posar el tercer bit a 0, independentment dels altres. Això fa que aquest pin sigui un input.

De manera similar fem el PD4 que sigui output posant-lo a 1:

DDRD |= (1<<PD3);
  • (1<<PD3) -> 00001000
  • DDRD |= (1<<PD3); -> DDRD = DDRD | (1<<PD3) -> DDRD = DDRD | 00001000 -> i pe tant el 4rt pin és un 1 (output)

NOTA: recordar que els operadors lògics a nivell de bit són & i | (no pas && i ||)

Enable pull-up for switch on PORTD bit 2:

PORTD |= (1 << PD2);

escribim un 1 en aquest pin del registre PORTD. Escriure un 1 o un 0 significa habilitar/deshabilitar la resistència de pull-up interna. En el cas del swwitch és necessari per tal de què el pin estigui normalment a 1. De la mateixa manera podrem encendre els pin del LED (que està definit com a output)

Les operacions es poden amb diverses nomenclatures. Per exemple, si volem posar els pins 1,3,5,7 del PORTC com a outputs: (el valor per defecte és input):

DDRC = (1<<DDRC1) | (1<<DDRC3) | (1<<DDRC5) | (1<<DDRC7);
DDRC = (1<<1) | (1<<3) | (1<<5) | (1<<7);
DDRC = 0xAA;
DDRC = 0b10101010;
DDRD = (1<<PD1) | (1<<PD3) | (1<<PD5) | (1<<PD7);

blink_led_v3.c:

//llegir: http://www.engineersgarage.com/contribution/expert/avr-io-ports#

//https://electrosome.com/push-button-switch-atmega32-microcontroller-atmel-studio/
/*
fuses:
sudo avrdude -b19200 -P usb -c usbasp -p m168 -U lfuse:w:0xE2:m -U hfuse:w:0xDF:m

avr-gcc -g -mmcu=atmega168 -c blink_led_v3.c -Wa,-alh,-L -Os -o blink_led_v3.o > blink_led_v3.asm
avr-gcc -g -mmcu=atmega168 -Wl,-Map,blink_led_v3.map -o blink_led_v3.elf blink_led_v3.o
avr-objdump -h -S blink_led_v3.elf > blink_led_v3.lst
avr-objcopy -j .text -j .data -O ihex blink_led_v3.elf blink_led_v3.hex
avr-size blink_led_v3.elf
cat blink_led_v3.hex
ja podem programar el xip:
sudo avrdude -b19200 -P usb -c usbasp -p m168 -U flash:w:blink_led_v3.hex
*/

#ifndef F_CPU
   #define F_CPU 8000000UL //el rellotge intern va a 8MHz
#endif

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

int main(void)
{
	//DDRD = 0x00; //Makes all pins of PORTD input
	DDRD &= ~(1<<PD2);//Makes tercer pin of PORTD as Input
	PORTD |= (1 << PD2);       // Enable pull-up for switch on PORTD bit 2 (important)
	DDRD |= (1<<PD3); //Makes quart pin of PORTD as Output. correcte

	 while(1) //infinite loop
	{
		if((PIND & (1<<PIND2)) == 0) //If switch is pressed
		{
			PORTD |= (1<<PD3); //Turns ON LED.
			_delay_ms(1000); //1 second delay.
			PORTD &= ~(1<<PD3); //Turns OFF LED.
			_delay_ms(1000); //1 second delay.
		}
	}
}

blink_led_v4.c:

//llegir: http://www.engineersgarage.com/contribution/expert/avr-io-ports#

//https://electrosome.com/push-button-switch-atmega32-microcontroller-atmel-studio/
/*
fuses:
sudo avrdude -b19200 -P usb -c usbasp -p m168 -U lfuse:w:0xE2:m -U hfuse:w:0xDF:m

avr-gcc -g -mmcu=atmega168 -c blink_led_v4.c -Wa,-alh,-L -Os -o blink_led_v4.o > blink_led_v4.asm
avr-gcc -g -mmcu=atmega168 -Wl,-Map,blink_led_v4.map -o blink_led_v4.elf blink_led_v4.o
avr-objdump -h -S blink_led_v4.elf > blink_led_v4.lst
avr-objcopy -j .text -j .data -O ihex blink_led_v4.elf blink_led_v4.hex
avr-size blink_led_v4.elf
cat blink_led_v4.hex
ja podem programar el xip:
sudo avrdude -b19200 -P usb -c usbasp -p m168 -U flash:w:blink_led_v4.hex
*/

#ifndef F_CPU
   #define F_CPU 8000000UL //el rellotge intern va a 8MHz
#endif

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

int main(void)
{
    //direcció dels pins
    //DDRD = 0b00001000; // PD2 as input, PD3 as output
    DDRD = 0x08;

    //valor per defecte dels pins (PORTx register)
    //el pin2 del switch ha de valdre 1 (activem el pull-up resistor intern). Quan cliquem el switch passarà a 0
    //el pin 3 del led val per defecte 0 (led apagat)
    //PORTD = 0b00000100;
    PORTD = 0x04;
    //PORTD = (1<<PD2);

    while (1) //infinite loop
    {
        if ((PIND & (0x04)) == 0 )  //If switch is pressed
        {
            //aquí el truco està en canviar només el pin que toca, doncs el pin2 és el d'entrada
            PORTD |= (1<<PD3); //Turns ON LED. correcte
            _delay_ms(1000); //1 second delay. correcte
            PORTD &= ~(1<<PD3); //Turns OFF LED. correcte
            _delay_ms(1000); //1 second delay. correcte
        } else {
            PORTD |= (1<<PD3); //Turns ON LED. correcte
            _delay_ms(200); //1 second delay. correcte
            PORTD &= ~(1<<PD3); //Turns OFF LED. correcte
            _delay_ms(200); //1 second delay. correcte
        }
    }
}

creat per Joan Quintana Compte, febrer 2014, juny 2015