/* tutorial TinyAVR 1-Series * ADC1 - C) Analog watchdog * PIT skrze event systém pravidelně spouští ADC převod * ADC v režimu okénkového komparátoru hlídá napětí na PA5 * pokud napětí překročí horní hranici, nebo podkročí dolní hranici * volá se přerušení a aplikace při překročení horní hranice rozsvítí LED * při podkročení dolní hranice zhasíná LED * hlídání napětí probíhá na pozadí, program reaguje jen při překročení/podkročení */ /* Ve fuses zvolen 20MHz oscilátor */ #define F_CPU 20000000UL #include #include #include #define V_REF 2507 // napětí vnitřní 2.5V reference (empiricky určeno) //#define V_REF 1105 // napětí vnitřní 1.1V reference (empiricky určeno) //#define V_REF 555 // napětí vnitřní 0.55V reference (empiricky určeno) //#define V_REF 4354 // napětí vnitřní 4.34V reference (empiricky určeno) //#define V_REF 1505 // napětí vnitřní 1.5V reference (empiricky určeno) #define LOW_LIMIT_VOLTAGE 1000UL //mV #define HIGH_LIMIT_VOLTAGE 1500UL // mV #define AKUMULACE 4 // počet vzorků které ADC akumuluje během převodu // vypočtené komparační hladiny pro výsledky převodu (i s akumulací) #define LOW_LIMIT (LOW_LIMIT_VOLTAGE*1023*AKUMULACE)/V_REF #define HIGH_LIMIT (HIGH_LIMIT_VOLTAGE*1023*AKUMULACE)/V_REF // indikační LED (PB5 zapojená proti VCC) #define LED_ON PORTB.OUTCLR = PIN5_bm; #define LED_OFF PORTB.OUTSET = PIN5_bm; void clock_20MHz(void); void init_adc(void); void init_pit(void); // přerušení od Window comparatoru ADC ISR(ADC0_WCOMP_vect){ uint16_t tmp; tmp = ADC0.RES; // čtení výsledku převodu zároveň maže vlajku perušení // (vlajku lze mazat i klasicky zápisem ADC0.INTFLAGS = ADC_WCMP_bm;) // pokud jsme přes horní hranici, rozsvítit LED, jinak jsme pod dolní hranicí - zhasnout LED if(tmp>=HIGH_LIMIT){LED_ON;}else{LED_OFF;} } int main(void){ clock_20MHz(); // taktujeme na plný výkon LED_OFF; // LED ze začátku zhasnutá PORTB.DIRSET = PIN5_bm; // LED (PB5) výstup PORTA.PIN5CTRL = PORT_ISC_INPUT_DISABLE_gc; // vypnout vstupní buffer na PA5 (AIN5) init_adc(); // rozběhnout referenci a ADC init_pit(); // spustit PIT (generuje pravidelné eventy pro ADC) // do event kanálu ASYNCCH3 zavést výstup PITu (1k/256 = ~ 4Hz) EVSYS.ASYNCCH3 = EVSYS_ASYNCCH3_PIT_DIV256_gc; // Asyncuser1 = ADC0, bere eventy z ASYNCCH3 (tedy z PITu) EVSYS.ASYNCUSER1 = EVSYS_ASYNCUSER1_ASYNCCH3_gc; sei(); // povolit globálně přerušení (ADC bude volat) while (1){ // ... není co dělat } } void init_pit(void){ RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; // nastavím zdroj clocku pro PIT - vnitřní 1kHz z ULP oscilátoru while(RTC.PITSTATUS & RTC_CTRLABUSY_bm){} // čekej dokud probíhá předchozí zápis do PITCTRLA RTC.PITCTRLA = RTC_PERIOD_OFF_gc | RTC_PITEN_bm; // spustí "naprázdno" PIT (bez povoleného přerušení) } void init_adc(void){ VREF.CTRLA = VREF_ADC0REFSEL_2V5_gc; // vybrat referenci pro ADC ADC0.CTRLA = ADC_RESSEL_10BIT_gc; // zvolit rozlišení 10bit ADC0.CTRLB = ADC_SAMPNUM_ACC4_gc; // akumulujeme výsledek 4 převodů ADC0.WINLT = LOW_LIMIT; // nastavíme dolní hladinu okénkového komparátoru ADC0.WINHT = HIGH_LIMIT; // nastavíme horní hladinu okénkového komparátoru ADC0.CTRLE = ADC_WINCM_OUTSIDE_gc; // okénkový komparátor hlídá vystoupení ze stanoveného pásma ADC0.INTCTRL = ADC_WCMP_bm; // povolíme přerušení od okénkového komparátoru ADC0.EVCTRL = ADC_STARTEI_bm; // převod spouštěn Eventem // vzorkovací kondenzátor 5pF, prescaler 20M/64 = 312kHz, použít vnitřní referenci (2.5V) ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; ADC0.MUXPOS = ADC_MUXPOS_AIN5_gc; // zvolit vstup pro ADC (PA5) ADC0.SAMPCTRL = 3; // 2+3 = 5 period (16us) vzorkovací čas ADC0.CTRLA |= ADC_ENABLE_bm; // spustit ADC _delay_us(25); // počkat na stabilizaci reference } // Nastaví clock 20MHz (z interního 20MHz bez děličky) void clock_20MHz(void){ // v případě potřeby zde dočasně vypněte přerušení CCP = CCP_IOREG_gc; // odemyká zápis do chráněného registru CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSC20M_gc; // vybírá 20MHz oscilátor CCP = CCP_IOREG_gc; // odemyká zápis do chráněného registru CLKCTRL.MCLKCTRLB = 0; // vypne prescaler (děličku) }