/* tutorial TinyAVR 1-Series * ADC1 - B) jednoduchý převod s oversamplingem * Na PA5 je výstup odporového děliče (10k trimr + 100nF filtrace) z TL431 (2.495V) * Změří napětí na PA5 (AIN5) proti vnitřní 2.5V referenci * a výsledek pošle na UART (skrze mEDBG do terminálu PC) */ /* Ve fuses zvolen 20MHz oscilátor */ #define F_CPU 20000000UL #include #include #include // kvůli Printf_P (formátovací řetězec je ve Flash) #include // kvůli makru PSTR (v printf_P) #define BAUDVAL 8334 // 9600 baud při 20MHz #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) void clock_20MHz(void); void init_adc(void); uint16_t adc_get(void); void usart_init(void); int usart_putchar(char var, FILE *stream); // kouzelná formule, pro přesměrování printf na UART static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar, NULL, _FDEV_SETUP_WRITE); uint16_t adc_val, volt; // výsledek převodu a přepočítaná hodnota napětí int main(void){ clock_20MHz(); // taktujeme na plný výkon PORTA.PIN5CTRL = PORT_ISC_INPUT_DISABLE_gc; // vypnout vstupní buffer na PA5 (AIN5) init_adc(); // rozběhnout referenci a ADC usart_init(); // konfigurace UARTu stdout = &mystdout; // přesměrujeme Printf na UART while (1){ adc_val = adc_get(); // změřit napětí // trocha matematiky která převede akumulovaný výsledek na mV se správným zaokrouhlováním volt = (uint16_t)(((uint32_t)adc_val*V_REF+32736UL)/(64UL*1023UL)); // pošleme výsledek do PC printf_P(PSTR("ADC = %u (%u mV)\n\r"),adc_val,volt); _delay_ms(1000); // ... každou sekundu } } uint16_t adc_get(void){ uint16_t tmp; ADC0.COMMAND = ADC_STCONV_bm; // spustit převod while(ADC0.COMMAND & ADC_STCONV_bm){} // počkat na dokončení převodu tmp = ADC0.RES; // vyčíst výsledek převodu // vracíme akumulovaný výsledek (něco jako 16bit ADC) return tmp; } 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_ACC64_gc; // akumulujeme výsledek na maximum (64 převodů = "16bit") // 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) } // odešle jeden znak (formát pro "printf" implementaci) int usart_putchar(char var, FILE *stream){ while(!(USART0.STATUS & USART_DREIF_bm)){} // počkat než bude místo v odesílacím bufferu USART0.TXDATAL = var; // naložit data k odeslání return 0 ; } void usart_init(void){ // využijeme toho že po startu/restartu je UART nastaven ... // ... jako 8bit, 1stop bit, žádná parita, asynchronní režim // část konfigurace si tedy můžu odpustit PORTA.OUTSET = PIN1_bm; // log.1 PA1 (Tx) - neutrální hodnota na UARTu PORTA.DIRSET = PIN1_bm; // PA1 (Tx) - je výstup PORTMUX.CTRLB = PORTMUX_USART0_bm; // Remapujeme Tx na PA1 USART0.BAUD = BAUDVAL; // nastavit baudrate USART0.CTRLB |= USART_TXEN_bm; // povolit vysílač (od teď přebere UART kontrolu nad PA1) }