--- /dev/null
+eeprom.raw
+kolektor.eep
+kolektor.elf
+kolektor.hex
+*.o
--- /dev/null
+PROGRAM=kolektor
+SRC=main.c logging.c
+OBJ=$(SRC:.c=.o)
+
+
+MCU=attiny25
+AVRDUDE_MCU=$(MCU)
+AVRDUDE_PROGRAMMER=usbasp
+
+CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=1000000UL -std=gnu99
+LDFLAGS=
+AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -B 4 -c $(AVRDUDE_PROGRAMMER)
+
+FORMAT=ihex
+
+CC=avr-gcc
+OBJCOPY=avr-objcopy
+OBJDUMP=avr-objdump
+AVRDUDE=avrdude
+
+all: $(PROGRAM).hex $(PROGRAM).eep
+
+program: $(PROGRAM).hex $(PROGRAM).eep
+ $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i -U eeprom:w:$(PROGRAM).eep:i
+
+program_flash: $(PROGRAM).hex
+ $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i
+
+program_eeprom: $(PROGRAM).eep
+ $(AVRDUDE) $(AVRDUDE_FLAGS) eeprom:w:$(PROGRAM).eep:i
+
+dump_eeprom:
+ $(AVRDUDE) $(AVRDUDE_FLAGS) -U eeprom:r:eeprom.raw:r
+ od -tx1 eeprom.raw
+
+objdump: $(PROGRAM).elf
+ $(OBJDUMP) --disassemble $<
+
+.PRECIOUS : $(OBJ) $(PROGRAM).elf
+
+%.hex: %.elf
+ $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
+
+%.eep: %.elf
+ $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+ --change-section-lma .eeprom=0 -O $(FORMAT) $< $@
+
+%.elf: $(OBJ)
+ $(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
+
+%.o: %.c Makefile
+ $(CC) -c $(CFLAGS) $< -o $@
+
+%.s: %.c Makefile
+ $(CC) -S -c $(CFLAGS) $< -o $@
+
+%.o: %.S
+ $(CC) -c $(CFLAGS) $< -o $@
+
+clean:
+ rm -f $(PROGRAM).hex $(PROGRAM).eep $(PROGRAM).elf *.o *.s eeprom.raw
+
+.PHONY: all clean dump_eeprom program program_flash program_eeprom objdump
+
--- /dev/null
+Dokumentace k desce
+===================
+(schéma, layout plošného spoje):
+http://www.fi.muni.cz/~kas/tinyboard/
+
+Osazení desky:
+==============
+
+U1: ATtiny25-20SSU
+U2: MCP1703T-500 regulátor napětí 5V, 250 mA
+Q20, Q50: N-MOSFET IRLML6344TRPBF, 30V, 5A
+C1, C3: 10uF keramické, typ nevím :-)
+C2, R10, R30, R40: _kondenzátory_ 220nF keramické X7R
+R12, R20, R32, R42, R50: odpory 15KOhm
+R34, R44: odpory 300KOhm (podle nich dimenzovat termistory)
+D50, R15, R21, R23, R51, R53: propjky 0 Ohm (nebo dráty)
+
+Nepoužito:
+R2, R3, R4, R11, R13, R14, R22, R24, R25, R31, R33, R41, R43, R45, R52, R54, R55
+C11, C51, D10, L10, L50, Q10, Q1, Q2, Q30, Q40, U3
+
+Možno ještě osadit kontrolku napájení (D1, R1)
+
+Přiřazení pinů ATtiny:
+======================
+PB0: plovák nebo jiný spínač, zapojit mezi piny 1 a 2 konektoru PB0
+PB1: ovládání motoru, zapojit mezi piny 2 a 3 konektoru PB1
+PB2: termistor, zapojit mezi piny 1 a 2+3 konektoru PB2
+PB3: termistor, zapojit mezi piny 1 a 2+3 konektoru PB3
+PB4: ovládání motoru, zapojit mezi piny 2 a 3 konektoru PB4
+
+Motory lze řídit i přes PWM (viz dokumentace Timer/Counter 1,
+pin PB1 je pak OC1A, pin PB4 je OC1B. Doporučuju T/C1 časovat z PLL clock
+na 32 MHz, čímž se získá PWM o frekvenci 256 kHz, což by mělo na plynulé
+řízení motoru stačit.
+
+Termistory jsou čitelné přes A/D převodník (PB2 je ADC1, PB3 je ADC2).
+Podle typu termistoru použít vhodnou napěťovou referenci ADC (asi interní 1.1V).
+Před vstupem PB0, PB2 a PB3 je low-pass filtr z 220nF kondenzátoru
+a 15k odporu, což by mělo ořezávat frekvence vyšší než cca 50 Hz.
+Tentýž kanál ADC tedy nemá smysl vyhodnocovat častěji.
+
+Možná půjde využít i vestavěný teploměr (viz dokumentace A/D převodníku).
+
+Programování:
+=============
+Před programováním odpojit piny PB0-PB2 od zbytku desky vyndáním jumperů J1-J3.
+
+make program # přeloží firmware a nakopíruje do CPU (flash+eeprom)
+make program_flash # totéž, uploaduje jen programovou paměť (flash)
+make program_eeprom # totéž, uploaduje jen eeprom
+make dump_eeprom # výpis eeprom, například logovacích dat
+
+Programová flash umožňuje 10_000 přepsání (OK), EEPROM umožňuje
+"jen" 100_000 přepsání (pozor na to při logování do EEPROM, ať se
+příliš často nepřepisuje ta stejná adresa, například při zacyklení programu).
+
--- /dev/null
+#include <avr/io.h>
+#include <avr/eeprom.h>
+
+#include "logging.h"
+
+#define LOG_BUFFER 32
+static unsigned char log_buffer_ee[LOG_BUFFER] EEMEM;
+static unsigned char log_buffer_count;
+static unsigned char log_buffer[LOG_BUFFER];
+static unsigned char log_state EEMEM;
+/* Upper 4 bits are reset count, lower 4 bits are reset reason from MCUSR */
+static unsigned char reboot_count EEMEM = 0;
+static unsigned char can_write_eeprom = 0;
+static uint16_t flushed_end;
+
+void log_set_state(unsigned char val)
+{
+ if (can_write_eeprom)
+ eeprom_write_byte(&log_state, val);
+}
+
+void init_log()
+{
+ unsigned char r_count;
+
+ r_count = eeprom_read_byte(&reboot_count);
+ r_count >>= 4;
+
+ if (r_count < 5) {
+ r_count++;
+ eeprom_write_byte(&reboot_count,
+ (r_count << 4) | (MCUSR & 0xF));
+ MCUSR = 0;
+ can_write_eeprom = 1;
+ } else {
+ //eeprom_write_byte(&log_state, 0xFF);
+ can_write_eeprom = 0;
+ }
+
+ log_set_state(1);
+ log_buffer_count = 0;
+ flushed_end = 0;
+}
+
+void log_byte(unsigned char byte) {
+ if (log_buffer_count >= LOG_BUFFER)
+ return;
+
+ // eeprom_write_word(&log_buffer[log_buffer_count], word);
+ log_buffer[log_buffer_count++] = byte;
+
+ if (log_buffer_count == LOG_BUFFER)
+ log_flush();
+}
+
+void log_word(uint16_t word) {
+ log_byte(word & 0xFF);
+ log_byte(word >> 8);
+}
+
+void log_flush() {
+ unsigned char i;
+
+ if (!can_write_eeprom)
+ return;
+
+ for (i=flushed_end; i < log_buffer_count; i++) {
+ eeprom_write_byte(&log_buffer_ee[i],
+ log_buffer[i]);
+ }
+
+ flushed_end = i;
+
+ if (flushed_end == LOG_BUFFER)
+ log_set_state(0x42);
+}
+
--- /dev/null
+#ifndef LOGGING_H__
+#define LOGGING_H__ 1
+
+void init_log();
+void log_set_state(unsigned char val);
+void log_flush();
+void log_byte(unsigned char byte);
+void log_word(uint16_t word);
+
+#endif
--- /dev/null
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+#include <avr/power.h>
+
+#include "logging.h"
+
+// init PLL clock for timer/counter 1
+// datasheet sekce 12.2.1
+static void enable_pll_clock()
+{
+ // async clock
+ PLLCSR = _BV(PLLE) | _BV(LSM); // low-speed mode (32 MHz)
+
+ // Synchronize to the phase lock
+ _delay_us(100);
+ while((PLLCSR & _BV(PLOCK)) == 0)
+ ;
+ PLLCSR |= _BV(PCKE);
+}
+
+// Timer/Counter 1 initialization
+// datasheet sekce 12
+static void init_tc1()
+{
+ power_timer1_enable();
+
+ TCCR1 = _BV(CTC1) // clear on compare match
+ | _BV(CS10) // clock = PCK (no divisor)
+ | _BV(COM1A1) // clear output line on compare match
+ | _BV(PWM1A); // PWM mode
+
+ OCR1C = 255; // TOP value
+
+ DDRB |= _BV(PB1);
+ OCR1A = 0; // stride
+
+ // Tohle pripadne povoli PWM i na OC1B (PB4)
+ // GTCCR = _BV(COM1B1) | _BV(PWM1B);
+ // DDRB |= _BV(PB4);
+ // OCR1B = 0; // stride
+}
+
+// precte synchronne (busy-waitem) jednu hodnotu z A/D prevodniku
+static uint16_t read_adc_sync()
+{
+ uint16_t rv;
+
+ ADCSRA |= _BV(ADSC); // start the conversion
+
+ // wait for the conversion to finish
+ while((ADCSRA & _BV(ADIF)) == 0)
+ ;
+
+ rv = ADCW;
+ ADCSRA |= _BV(ADIF); // clear the IRQ flag
+
+ return rv;
+}
+
+// analog to digital converter
+// datasheet sekce 17
+static void init_adc()
+{
+ power_adc_enable();
+ ACSR |= _BV(ACD); // enable ADC, disable AC
+
+ ADCSRA = _BV(ADEN) // enable
+ | _BV(ADPS1) | _BV(ADPS0) // CLK/8 = 125 kHz
+ ;
+
+ DIDR0 = _BV(ADC1D) | _BV(ADC3D); // disable dig. input on ADC1, ADC3 (PB2, PB3)
+ ADMUX = _BV(REFS1) // 1.1V internal reference
+ | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0) // ADC4 (temperature)
+ ;
+
+ // do the first conversion, drop the result
+ read_adc_sync();
+}
+
+int main(void)
+{
+ unsigned char i;
+
+ init_log();
+
+ log_byte(0x42); // zkouska logovani
+ log_flush(); // nezapomenout vylit buffer
+
+ power_all_disable(); /* vypneme cele I/O, pak zapneme co bude potreba */
+
+ DDRB |= _BV(DDB4); // PB4 as output
+
+ enable_pll_clock();
+ init_tc1();
+ init_adc();
+
+ // po skonceni inicializace muzeme i zapnout preruseni,
+ // pokud je budeme potrebovat
+ // cli();
+
+ // priklad prace s A/D prevodnikemo
+ // init_adc() necha ADMUX nastaveny na ADC4 (teplomer), jinak
+ // tady lze ADMUX prestavit na neco jineho (pozor na napetovou
+ // referenci REFS)
+ log_word(read_adc_sync()); // nacteme teplotu, zalogujeme
+ // interpretace vysledku: datasheet sekce 17.12
+ log_flush();
+
+ while(1) {
+ for (i = 0; i < 16; i++) {
+ // zapiname/vypiname portb4 v sudem/lichem kroku
+ if (i & 1) {
+ PORTB |= _BV(PORTB4);
+ } else {
+ PORTB &= ~_BV(PORTB4);
+ }
+
+ // hodnopta PWM pro PB1 (OC1A)
+ // jdeme od 128 do 248, napriklad:
+ OCR1A = 0x80 + (i << 3);
+
+ // busy-wait, nebo se muzeme nechat casovat
+ // pomoci watchdogu (WDT, datasheet sekce 7.4.5)
+ // nebo pomoci Timer/Counter 0 (datasheet sekce 11)
+ _delay_ms(1500);
+ }
+ }
+}