forked from sejo/compudanzas
348 lines
6.5 KiB
Plaintext
348 lines
6.5 KiB
Plaintext
# tomatimer
|
|
|
|
a small pomodoro timer on an attiny85, programmed with {avr-asm}.
|
|
|
|
part of the {s-camino} practice.
|
|
|
|
=> ./img/foto_tomatimer.png photo of the tomatimer: a small circuit with two leds, a buzzer, the attiny85, all of them over a plastic card.
|
|
|
|
# about
|
|
|
|
tomatimer works as a 25 and 5 minutes timer, cycling between them.
|
|
|
|
a couple of LEDs indicate via blinking which one of the two timers is active at a given moment.
|
|
|
|
at the end of each timer, a buzzer plays an alarm during some seconds.
|
|
|
|
all the durations can be reconfigured via the source code if needed.
|
|
|
|
# connections
|
|
|
|
```diagrama de pines
|
|
┌──────┐
|
|
│1 8│ VCC
|
|
│2 7│ PB2: LED state 1
|
|
│3 6│ PB1: LED state 0
|
|
GND │4 5│ PB0: Buzzer
|
|
└──────┘
|
|
```
|
|
|
|
LEDs are connected such that an output of "high" will turn them on.
|
|
|
|
# source code
|
|
|
|
with billingual comments
|
|
|
|
```
|
|
; tomatimer.S
|
|
#include <avr/io.h>
|
|
|
|
; TIMER1 tiene su reloj a clk/512 (~1953 Hz)
|
|
; contando 244 pulsos se va a 8Hz (frame freq)
|
|
; i.e. 8 frames son 1 segundo
|
|
; r16 y r17 se usan como registros auxiliares
|
|
|
|
; duraciones en minutos de los timers
|
|
#define DUR_TIMER0 25
|
|
#define DUR_TIMER1 5
|
|
; duraciones en segundos de la alarma
|
|
#define DUR_ALARMA0 3
|
|
#define DUR_ALARMA1 5
|
|
|
|
; más chico es más agudo
|
|
#define WAVE_PERIOD0 3
|
|
#define WAVE_PERIOD1 6
|
|
|
|
; teórico: 244
|
|
#define TIMER1_PERIOD 240
|
|
|
|
#define rFrames r20
|
|
#define rSegundos r21
|
|
#define rMinutos r22
|
|
#define rFlags r23
|
|
|
|
#define fAlarmOn 0
|
|
#define fAlarmLevel 1
|
|
#define fCualTimer 2
|
|
|
|
#define pinAlarm PB0
|
|
#define pinState0 PB1
|
|
#define pinState1 PB2
|
|
|
|
; para hacer fast modulo 8
|
|
#define MASK_FRAMES 0x07
|
|
|
|
; ***********************
|
|
; vectores de interrupts:
|
|
; ***********************
|
|
; Reset
|
|
.org 0x0000
|
|
rjmp main
|
|
|
|
; dirección 0x0003 * 2
|
|
.org 0x0006
|
|
rjmp timer1compareA_isr
|
|
|
|
; dirección 0x0009 * 2
|
|
.org 0x0012
|
|
rjmp timer1compareB_isr
|
|
|
|
; ***********************
|
|
; Main
|
|
; ***********************
|
|
.org 0x001E
|
|
main:
|
|
;----------------------------------
|
|
; configuración general
|
|
;----------------------------------
|
|
|
|
; habilita interrupciones
|
|
sei
|
|
|
|
; pin PB0 como pin de salida
|
|
ldi r16, (1<<DDB2) | (1<<DDB1) | (1<<DDB0) ; pins de salida
|
|
sts DDRB, r16
|
|
|
|
;----------------------------------
|
|
; configuración TIMER0 para buzzer
|
|
;---------------------------------
|
|
; CS0: 0b011, clock select clk/64
|
|
; WGM0: 0b010, Clear timer on compare match (CTC mode)
|
|
; COM0A: 0b01, toggle OCOA on compare match (0b00 para apagar)
|
|
ldi r16, (0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00)
|
|
sts TCCR0B, r16
|
|
ldi r16, (0<<COM0A1) | (1<<COM0A0) | (1<<WGM01) | (0<<WGM00)
|
|
sts TCCR0A, r16
|
|
|
|
; el TOP es el valor en OCR0A
|
|
; más pequeño es más agudo
|
|
ldi r16, WAVE_PERIOD0
|
|
sts OCR0A, r16
|
|
|
|
;----------------------------------
|
|
; configuración TIMER1 para frames
|
|
;----------------------------------
|
|
; CS1 en 0b1010 es CK/512
|
|
ldi r16, (1<<CTC1) | (1<<CS13) | (0<<CS12) | (1<<CS11) | (0<<CS10)
|
|
sts TCCR1, r16
|
|
|
|
; establece el valor A al que cuenta el timer1 para interrupt
|
|
ldi r16, TIMER1_PERIOD
|
|
sts OCR1A, r16
|
|
|
|
; establece el valor C al que cuenta el timer1 para resetear
|
|
sts OCR1C, r16
|
|
|
|
; establece el valor B para mitad de periodo
|
|
lsr r16 ; divide entre 2
|
|
sts OCR1B, r16
|
|
|
|
; set timer output compare A interrupt enable 1
|
|
; y timer output compare B interrupt enable
|
|
ldi r16, (1<<OCIE1A) | (1<<OCIE1B)
|
|
sts TIMSK, r16
|
|
|
|
;enciende alarma
|
|
ldi rFlags, (1<<fAlarmOn)
|
|
|
|
; time count
|
|
ldi rFrames, 0 ; frames
|
|
ldi rSegundos, 0 ; seconds
|
|
ldi rMinutos, 0 ; minutes
|
|
|
|
; enciede state 0
|
|
ldi r16, (1<<pinState0)
|
|
sts PORTB, r16
|
|
|
|
loop:
|
|
sleep
|
|
rjmp loop
|
|
|
|
; -----------------------
|
|
; interrupts
|
|
|
|
; main time counting
|
|
timer1compareA_isr:
|
|
; apaga alarm level
|
|
clt
|
|
bld rFlags, fAlarmLevel
|
|
|
|
; apaga pwm output
|
|
lds r16, TCCR0A
|
|
andi r16, ~(1<<COM0A0)
|
|
sts TCCR0A, r16
|
|
|
|
; incrementa frames
|
|
inc rFrames
|
|
andi rFrames, MASK_FRAMES
|
|
breq incrementsec ; salta si rFrames%8 == 0
|
|
reti
|
|
|
|
incrementsec:
|
|
inc rSegundos ; cuenta segundos
|
|
|
|
lds r16, PORTB
|
|
; en qué timer estamos?
|
|
bst rFlags, fCualTimer
|
|
brts checksegundos1 ; salta si es el 1
|
|
|
|
checksegundos0:
|
|
bst rSegundos, 0
|
|
brts ledstate0off
|
|
|
|
ledstate0on:
|
|
ori r16, (1<<pinState0)
|
|
rjmp comparesegundos0
|
|
|
|
ledstate0off:
|
|
andi r16, ~(1<<pinState0)
|
|
|
|
comparesegundos0:
|
|
sts PORTB, r16
|
|
cpi rSegundos, DUR_ALARMA0 ; time == DUR_ALARMA0?
|
|
brne updateminutes
|
|
rjmp turnalarmoff
|
|
|
|
checksegundos1:
|
|
bst rSegundos,0
|
|
brts ledstate1off
|
|
|
|
ledstate1on:
|
|
ori r16, (1<<pinState1)
|
|
rjmp comparesegundos1
|
|
|
|
ledstate1off:
|
|
andi r16, ~(1<<pinState1)
|
|
|
|
comparesegundos1:
|
|
sts PORTB, r16
|
|
cpi rSegundos, DUR_ALARMA1 ; time == DUR_ALARMA0?
|
|
brne updateminutes
|
|
|
|
turnalarmoff: ; si es la duración correspondiente, apaga alarma
|
|
clt
|
|
bld rFlags, fAlarmOn
|
|
|
|
updateminutes:
|
|
cpi rSegundos,60 ; segundos == 60?
|
|
breq minute
|
|
reti
|
|
|
|
minute:
|
|
ldi rSegundos, 0 ; reset seconds
|
|
inc rMinutos ; increment minutes
|
|
|
|
bst rFlags, fCualTimer
|
|
brts checkminutes1
|
|
|
|
checkminutes0:
|
|
cpi rMinutos, DUR_TIMER0
|
|
breq firealarm
|
|
reti
|
|
|
|
checkminutes1:
|
|
cpi rMinutos, DUR_TIMER1
|
|
breq firealarm
|
|
reti
|
|
|
|
firealarm:
|
|
ldi rMinutos, 0 ; reset minutes
|
|
; inicia alarma
|
|
set
|
|
bld rFlags, fAlarmOn
|
|
; invierte timer
|
|
bst rFlags, fCualTimer
|
|
brts settimer0 ; salta si el timer es 1
|
|
|
|
settimer1:
|
|
set
|
|
bld rFlags,fCualTimer
|
|
|
|
; cambia tono de alarma
|
|
ldi r16, WAVE_PERIOD1
|
|
sts OCR0A, r16
|
|
|
|
; apaga otro led
|
|
lds r16, PORTB
|
|
andi r16, ~(1<<pinState0)
|
|
sts PORTB, r16
|
|
reti
|
|
settimer0:
|
|
clt
|
|
bld rFlags,fCualTimer
|
|
|
|
; cambia tono de alarma
|
|
ldi r16, WAVE_PERIOD0
|
|
sts OCR0A, r16
|
|
|
|
; apaga otro led
|
|
lds r16, PORTB
|
|
andi r16, ~(1<<pinState1)
|
|
sts PORTB, r16
|
|
reti
|
|
|
|
|
|
; enciende pin de alarma
|
|
timer1compareB_isr:
|
|
bst rFlags, fAlarmOn ; bit store
|
|
|
|
; branch if T bit is set
|
|
brts turnonalarmlevel
|
|
reti
|
|
|
|
turnonalarmlevel:
|
|
; set alarmlevel on
|
|
set
|
|
bld rFlags, fAlarmLevel
|
|
|
|
; enciende pwm output
|
|
lds r16, TCCR0A
|
|
ori r16, (1<<COM0A0)
|
|
sts TCCR0A, r16
|
|
reti
|
|
```
|
|
|
|
# makefile
|
|
|
|
to assemble:
|
|
|
|
```
|
|
make hex
|
|
```
|
|
|
|
to flash:
|
|
|
|
```
|
|
make flash
|
|
```
|
|
|
|
## makefile
|
|
|
|
```
|
|
# Makefile
|
|
# nombre del programa sin .S :
|
|
PROG = tomatimer
|
|
# config hardware
|
|
BOARD = attiny85
|
|
PROGRAMMER = usbasp
|
|
|
|
# ensambla programa .S 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)
|
|
```
|