/* tutorial TinyAVR 1-Series * ADC1 - B) měření teploty pomocí MCP9701 * Výstup z MCP9701 na PA7 * 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ůi fci abs() #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) // parametry teplotního čidla MCP9701 (lze změnit k přesnější kalibraci) #define MCP_OFFSET 400 // mV #define MCP_SCALE 195 // jednotkou je 0.1*mV/°C void clock_20MHz(void); void init_adc(void); int16_t get_temperature(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í int16_t temp; int main(void){ clock_20MHz(); // taktujeme na plný výkon PORTA.PIN7CTRL = PORT_ISC_INPUT_DISABLE_gc; // vypnout vstupní buffer na PA7 (AIN7) init_adc(); // rozběhnout referenci a ADC usart_init(); // konfigurace UARTu stdout = &mystdout; // přesměrujeme Printf na UART while (1){ _delay_ms(1000); // každou sekundu temp=get_temperature(); // přečteme teplotu (v desetinnách °C) // pošleme část zprávy včetně znaménka teploty na terminál if(temp<0){printf_P(PSTR("t = -"));}else{printf_P(PSTR("t = +"));} // a dále pracujeme už jen s kladnou hodnotou teploty temp=abs(temp); // pošleme "desetinné" vyjádření teploty printf_P(PSTR("%i.%i °C\n\r"),temp/10,temp%10); } } // vrací teplotu v desetinách °C int16_t get_temperature(void){ int32_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 // "hodně" matematiky v jednom řádku - viz text tutoriálu tmp = (100*((tmp*V_REF+2*1023)/(4*1023) - MCP_OFFSET))/MCP_SCALE; return tmp; } void init_adc(void){ VREF.CTRLA = VREF_ADC0REFSEL_1V5_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ů // vzorkovací kondenzátor 5pF, prescaler 20M/64 = 312kHz, použít vnitřní referenci (1.5V) ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; ADC0.MUXPOS = ADC_MUXPOS_AIN7_gc; // zvolit vstup pro ADC (PA7) 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) }