compudanzas

avr-asm

explorando programación de microcontroladores avr a través de asm, en attiny85. s-camino

herramientas

en debian-based:

sudo apt install gcc-avr avrdude avr-libc

attiny85

datasheet

diagrama de pines

     ┌──────┐
PB5  │1    8│  VCC
PB3  │2    7│  PB2
PB4  │3    6│  PB1
GND  │4    5│  PB0
     └──────┘

además, cada pin es:

para flashear nos interesan los pines desde el punto de vista de SPI:

       ┌──────┐
~RESET │1    8│  VCC
       │2    7│  SCK
       │3    6│  MISO
GND    │4    5│  MOSI
       └──────┘

programador

USBasp - USB programmer for Atmel AVR controllers (web)

udev rule

possible udev rule for e.g. /etc/udev/rules.d/80-usbasp.rules, por si las dudas. en principio no se debería necesitar.

ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="16c0",
 ATTRS{idProduct}=="05dc", OWNER="user", GROUP="dialout",
 MODE="0777"

makefile

para ensamblar y flashear

# Makefile
# nombre del programa sin .S :
PROG = test
# config hardware
BOARD = attiny85
PROGRAMMER = usbasp

# ensambla programa a .hex
hex:
	avr-gcc -Os -DF_CPU=8000000 -mmcu=$(BOARD) -c $(PROG).S
	avr-ld -o $(PROG).elf $(PROG).o
	avr-objcopy $(PROG).elf -O ihex $(PROG).hex
	rm $(PROG).o $(PROG).elf

# flashea
flash:
	avrdude -c $(PROGRAMMER) -p $(BOARD) -U flash:w:$(PROG).hex:i

# lee la memoria flash a read.hex
read:
	avrdude -c $(PROGRAMMER) -p $(BOARD) -U flash:r:read.hex:i

# prueba conexión con programador y micro
test:
	avrdude -c $(PROGRAMMER) -p $(BOARD)

programas

software experimental, compartido como referencia y sin garantía de ningún tipo :)

test.S

enciende un el pin PB0 — conecta un led

; test.S
; enciende un pin
#include <avr/io.h>
.org 0x0000

        ldi r17, (1<<DDB0)      ; configura al PB0 como salida
        sts DDRB, r17

        ldi r16, (1<<PB0)       ; enciende el bit correspondiente al PB0
        sts PORTB, r16          ; actualiza el puerto

        sleep                   ; duerme por siempre (?)

blink.S

parpadea el pin PB0 usando el timer/counter0 — conecta un led

; blink.S
; parpadea el pin PB0 (OC0A)!
; r16 y r17 se usan como registros auxiliares

; el programa usa el timer/counter 0 con:
; * waveform generation mode (WGM0): 2, que es CTC (clear timer on compare match)
; * compare match output A mode (COM0A): 1, que es toggle en compare match
; * clock select (CS0): 5, que utiliza el reloj i/o dividido entre 1024

; notas sobre el reloj:
; por default el attiny85 va a 8MHz, y el reloj i/o va a 1MHz
; 1MHz/1024 ~= 967 Hz

#include <avr/io.h>
; ***********************
; vectores de interrupts:
; ***********************
; Reset
.org 0x0000
	rjmp main

; ***********************
; Main
; ***********************
.org 0x0010
main:
	; pin PB0 (OC0A) como pin de salida
	ldi r16, (1<<DDB0) ; pin de salida
	sts DDRB, r16

	; togglea OC0A en Compare match (1 en COM0A[1:0])
	; y usa modo Clear Timer on Compare Match (2 en WGM0[2:0])
	ldi r16, (0<<COM0A1) | (1<<COM0A0) | (1<<WGM01) | (0<<WGM00)
	sts TCCR0A, r16

	; completa el modo WGM (waveform generaton mode, bit 2)
	; establece el tipo de reloj: 0b101 en CS0 es clk/1024
	ldi r16, (0<<WGM02) | (1<<CS02) | (0<<CS01) | (1<<CS00)
	sts TCCR0B, r16

	; el TOP es el valor en OCR0A
	ldi r16, 0x80
	sts OCR0A, r16

loop:
	sleep
	rjmp loop

buzz.S

zumba el pin PB0 — conecta un buzzer

; buzz.S
; haz zumbar el pin PB0 (OC0A)!
; el código es igual a blink.S, pero con diferentes frecuencias
; r16 y r17 se usan como registros auxiliares

; el programa usa el timer/counter 0 con:
; * waveform generation mode (WGM0): 2, que es CTC (clear timer on compare match)
; * compare match output A mode (COM0A): 1, que es toggle en compare match
; * clock select (CS0): 4, que utiliza el reloj i/o dividido entre 256

; notas sobre el reloj:
; por default el attiny85 va a 8MHz, y el reloj i/o va a 1MHz
; 1MHz/256 ~= 3906 Hz (/2 para el periodo completo ~=1953Hz)

#include <avr/io.h>
; ***********************
; vectores de interrupts:
; ***********************
; Reset
.org 0x0000
	rjmp main

; ***********************
; Main
; ***********************
.org 0x0010
main:
	; pin PB0 (OC0A) como pin de salida
	ldi r16, (1<<DDB0) ; pin de salida
	sts DDRB, r16

	; togglea OC0A en Compare match (1 en COM0A[1:0])
	; y usa modo Clear Timer on Compare Match (2 en WGM0[2:0])
	ldi r16, (0<<COM0A1) | (1<<COM0A0) | (1<<WGM01) | (0<<WGM00)
	sts TCCR0A, r16

	; completa el modo WGM (waveform generaton mode, bit 2)
	; establece el tipo de reloj: 0b100 en CS0 es clk/256
	ldi r16, (0<<WGM02) | (1<<CS02) | (0<<CS01) | (0<<CS00)
	sts TCCR0B, r16

	; el TOP es el valor en OCR0A
	ldi r16, 0x02
	sts OCR0A, r16

loop:
	sleep
	rjmp loop

alarm.S

zumbido intermitente en el pin PB0 — conecta un buzzer

; alarm.S
; zumbido intermitente en el pin PB0 (OC0A)!
; r16 y r17 se usan como registros auxiliares
; r20 tiene el valor de TCCR0A para apagar sonido
; r21 tiene el valor de TCCR0A para encenderlo

; el programa usa el timer/counter 0 con:
; * waveform generation mode (WGM0): 2, que es CTC (clear timer on compare match)
; * compare match output A mode (COM0A): 1, que es toggle en compare match
; * clock select (CS0): 3, que utiliza el reloj i/o dividido entre 64

; y el timer/counter 1 con:
; * clock select (CS1): 0b1010, que es clk/512
; * interrupciones de overflow y de compare match A:
;	- en compare match A el zumbido se apaga
;	- en overflow el zumbido inicia

; notas sobre el reloj:
; por default el attiny85 va a 8MHz, y el reloj i/o va a 1MHz
; 1MHz/256 ~= 3906 Hz (/2 para el periodo completo ~=1953Hz)

#include <avr/io.h>
; ***********************
; vectores de interrupts:
; ***********************
; Reset
.org 0x0000
	rjmp main

; dirección 0x0003 * 2
.org 0x0006
	rjmp timer1compareA_isr

; dirección 0x0004 * 2
.org 0x0008
	rjmp timer1overflow_isr

; ***********************
; Main
; ***********************
.org 0x001E
main:
	;----------------------------------
	; configuración general
	;----------------------------------

	; habilita interrupciones
	sei

	; pin PB0 (OC0A) como pin de salida
	ldi r16, (1<<DDB0) ; pin de salida
	sts DDRB, r16

	; valores de TCCR0A para encender o apagar sonido
	ldi r20, (0<<COM0A1) | (0<<COM0A0) | (1<<WGM01) | (0<<WGM00)
	ldi r21, (0<<COM0A1) | (1<<COM0A0) | (1<<WGM01) | (0<<WGM00)

	;----------------------------------
	; configuración TIMER0 para buzzer
	;----------------------------------
	; togglea OC0A en Compare match (1 en COM0A[1:0])
	; y usa modo Clear Timer on Compare Match (2 en WGM0[2:0])
	sts TCCR0A, r21

	; completa el modo WGM (waveform generaton mode, bit 2)
	; establece el tipo de reloj: 0b011 en CS0 es clk/64
	ldi r16, (0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00)
	sts TCCR0B, r16

	; el TOP es el valor en OCR0A
	; más pequeño es más agudo
	ldi r16, 0x06
	sts OCR0A, r16

	;----------------------------------
	; configuración TIMER1 para alternar entre sonido on/off
	;----------------------------------
	; CS1 en 0b1010 es CK/512
	ldi r16, (1<<CS13) | (0<<CS12) | (1<<CS11) | (0<<CS10)
	sts TCCR1, r16

	; establece el valor A al que cuenta el timer1 antes de apagar sonido
	; menor valor, menor tiempo de sonido (duty cycle)
	ldi r16, 0x30
	sts OCR1A, r16

	; set Timer overflow interrupt enable 1
	; y timer output compare A interrupt enable 1
	ldi r16, (1<<TOIE1) | (1<<OCIE1A)
	sts TIMSK, r16

loop:
	sleep
	rjmp loop

timer1compareA_isr:
	; apaga la salida
	sts TCCR0A, r20
	reti

timer1overflow_isr:
	; enciende la salida
	sts TCCR0A, r21
	; regresa de la interrupción
	reti

enlaces entrantes