#include #include #include #include #include #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. // Alternativa je nastavit bit ADSC a ADIE, delat neco jineho, // a pockat az prijde ADC interrupt (viz firmware "step-up" na strance // tinyboard). 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; } // vyber kanalu A/D prevodniku (datasheet tabulka 17-4 v sekci 17.13.1) typedef enum { ADC1, ADC3, ADC4 } adc_channel; static void select_adc_channel(adc_channel c) { switch (c) { case ADC1: ADMUX = _BV(REFS1) | _BV(MUX0); // ADC1 (PB2), 1.1V ref break; case ADC3: ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0); // ADC3 (PB3), 1.1V ref break; case ADC4: ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0) // ADC4 (temperature), 1.1V ref break ; } } // 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) select_adc_channel(ADC4); // do the first conversion, drop the result read_adc_sync(); } // priklad vselijakych moznosti, co tenhle HW umi int main(void) { unsigned char i; init_log(); // viz logging.c - detekce poctu resetu, atd. 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 (lze misto tohoto pouzit jako PWM) PORTB |= _BV(PB0); // PB0 interni pull-up (aby tady byla 1, pokud je plovak rozepnuty) 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 select_adc_channel(ADC4); // interni teplomer na cipu log_word(read_adc_sync()); // nacteme teplotu cipu, zalogujeme // interpretace vysledku: datasheet sekce 17.12 log_flush(); // ve smycce budeme merit jak se meni hodnota na ADC3 (PB3) select_adc_channel(ADC3); while(1) { // PB4 (motor 2) zapínáme podle sepnutí plováku PB0 if (PINB & _BV(PINB0)) { // rozepnuto (1) PORTB &= ~_BV(PORTB4); } else { // sepnuto (0) PORTB |= _BV(PORTB4); } // hodnota 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); i++; i &= 0xF; log_word(read_adc_sync()); // nacteme hodnotu, zalogujeme log_flush(); } }