/* Programm zum Auslesen eines KTY81 oder PT1000 mit ATtiny25/45/85 und Ausgabe der Messwerte mit 9600 Bd im Format 8N1. Der TxD-Ausgangspegel (PB1) passt zu RS232 Empfängern. Jede Sekunde wird per WDT eine ISR aufgerufen, die den Spannungsteiler und den ADC (PB3) aktiviert, einen dummy-ADC-Zyklus startet und die eff. Spannungen an ADC_REF (PB3) und PTC_IN (PB4) einliest. Die Ergebnisse ADC_max und ADC_werden anschliessend in der Funktion 'berechne_celsius()' per Tabelle ausgewertet und umgerechnet. Die Routine 'sende_werte()' erledigt die Ausgabe als ASCII Zeichenkette abgeschlossen mit CR. Wegen ratiometrischer Messung arbeitet die Schaltung im Versorgungsspannungsbereich 2 - 5 V. Die eff. Stromaufnahme liegt im unteren zweistelligen µA Bereich und betraegt für 3 V VCC grob 10 µA. Spannungsteiler aktiv fuer 150 µs mit ca. 1,5 mA: etwa 0,23 µA Messdauer mit Datenausgabe aktiv fuer 7 ms mit ca. 550 µA: etwa 3,9 µA Power-Down Ruhestrom permanent: etwa 5 µA Dies sind grobe Angaben zur Einschätzung beim Raumtemperatur 20 °C. Michael Nowak 2025-11-02 www.mino-elektronik.de Alle Angaben ohne Gewaehr */ //#define PT1000 // Tabelle passend zum PT1000 aktivieren //#define DBG // mit Rohdatenausgabe zur Kontrolle //#define FLOAT // braucht je nach Compiler ein paar Bytes mehr Code #include #include #include #include #define BIT(x) (1< UEBERLAUF) KTY_x = UEBERLAUF; index = 0; #ifdef FLOAT while(KTY_x > ptc_tab[index+1][RX]) index++; // Bereich zu KTY_x finden temperatur = ptc_tab[index][GRAD]; // minimale Temperatur des gefundenen Bereiches KTY_x = (KTY_x - ptc_tab[index][RX]) / (float) (ptc_tab[index+1][RX] - ptc_tab[index][RX]); // KTY_x wird zum Bruchteil im Bereich temperatur = temperatur + KTY_x * (ptc_tab[index+1][GRAD] - ptc_tab[index][GRAD]); // linear interpolierten Offset addieren #else while(KTY_x > (ptc_tab[index+1][RX])) index++; // Bereich zu KTY_x finden temperatur = (ptc_tab[index][GRAD]); // minimale Temperatur des gefundenen Bereiches KTY_x = (KTY_x - (ptc_tab[index][RX])) * PTC_SKAL_FAKTOR / ((ptc_tab[index+1][RX]) - (ptc_tab[index][RX])); // KTY_x wird zum Bruchteil im Bereich temperatur = temperatur + KTY_x * ((ptc_tab[index+1][GRAD]) - (ptc_tab[index][GRAD])) / PTC_SKAL_FAKTOR; // linear interpolierten Offset addieren #endif return temperatur; } // dauert ca. 150 µs @ 1 MHz ISR (WDT_vect) // Aufruffrequenz ca. 1 Hz { cli(); // ser. Ausgabe nicht blockieren PORTB |= BIT(ADC_REF); // Ausgang setzen ADCSRA = BIT(ADEN) | 0x01; // ADC einschalten mit Teiler /1 ADMUX = ADC_REF; // dummy ADCSRA |= BIT(ADSC); // starten while(ADCSRA & BIT(ADSC)); // warten ADMUX = ADC_REF; // PORTB3 als Referenz, rechtsbuendig ADCSRA |= BIT(ADSC); // starten while(ADCSRA & BIT(ADSC)); // warten ADC_max = ADC; ADMUX = PTC_IN; // TTC lesen, rechtsbuendig ADCSRA |= BIT(ADSC); // starten while(ADCSRA & BIT(ADSC)); // warten ADC_wert = ADC; PORTB &= ~BIT(ADC_REF); // und Ausgang wieder loeschen sende_flag = 1; ADCSRA = 0; // ADC abschalten } // ser. Ausgabe synchron per Timer1 und OCR1A-Register ISR (TIM1_COMPA_vect) { static char txd_cnt, txd_buf; OCR1A += T1_NACHLADEWERT; // fuer 9600 Baud switch(txd_cnt) { case 1: txd_cnt--; TXD_AUF_1; // Stoppbit setzen break; // und naechstes Zeichen laden case 0: txd_cnt = TXD_BITS; TXD_INT_OFF; // fertig mit interrupts break; case TXD_BITS: txd_buf = txd_data; // neues Zeichen txd_cnt--; TXD_AUF_0; // Startbit setzen break; default: txd_cnt--; if(txd_buf & 1) { TXD_AUF_1; // '1' bit } else { TXD_AUF_0; // '0' bit } txd_buf >>= 1; // LSB -> MSB } } void sende_byte(char c) { while(TXD_AKTIV); // warten, bis txd frei ist txd_data = c; // neues Zeichen TCNT1 = OCR1A = 0; // Timer + Comp-Reg. loeschen TXD_INT_ON; // Ausgabe starten } // direkte ser. Ausgabe des Wertes void sende_int(int16_t l) { uint16_t tab[] = {10000, 1000, 100, 10, 1, 0}; uint16_t temp; int i=0,j; if(l) { if(l < 0) { l = -l; sende_byte('-'); } while(tab[i] > l) i++; do { temp = tab[i]; j = '0'; if(temp) { while(l >= temp) { j++; l -= temp; } sende_byte(j); i++; } } while(temp); } else sende_byte('0'); } // Ausgabe bei Bedarf mit Rohwerten des ADCs void sende_werte() { // init_serio(); // T1 starten TCNT1 = OCR1A = 0; TCCR1 |= 1; // Timer1 ohne Vorteiler TXD_AUF_1; // Ruhepegel invertiert TXD_FLAG_OFF; #ifdef DBG // ggf. Rohwerte ausgeben sende_int(ADC_max); sende_byte(','); sende_int(ADC_wert); sende_byte(','); #endif sende_int(berechne_celsius()); sende_byte(CR); while(TXD_AKTIV); // vollstaendige Ausgabe abwarten TCCR1 = 0; // Timer1 abschalten } // Beispielprogramm int main(void) { CLKPR = 0x80; CLKPR = 0x03; // Vorteiler / 8, CPU-Takt = 1MHz erzwingen DDRB = BIT(ADC_REF) | BIT(TXD) | BIT(0) | BIT(2); // als Ausgaenge PRR = BIT(PRTIM0) | BIT(PRUSI); // Timer0 und USI abschalten WDTCR = BIT(WDIE) | BIT(WDP2) | BIT(WDP1); // Watchdog-Intervall auf ca. 1 s sei(); while(1) { MCUCR = BIT(SE) + BIT(SM1); // Power-Down sleep_cpu(); // aktivieren if(sende_flag) { // neues Eregbnis ausgeben sende_werte(); // dauert ca. 7 ms sende_flag = 0; } } }