Inhaltsverzeichnis

OID zur Temperaturmessung mit einem NTC Widerstand

In diesem Artikel soll die Vorgehensweise zur Temperaturmessung auf Basis des Raven Boards mithilfe des Analog Digital Converters (ADC) sowie einem NTC Widerstand beschrieben werden. Es wird hierbei absichtlich auf den bereits vorhanden NTC, welcher im Raven Board verbaut ist und über den 3290PV ereichbar ist verzichtet und ein neuer NTC eingebaut. Dies soll eine spätere Portierung auf einen anderen Mikrocontroller ermöglichen. Der 1284P des AVR Raven besitzt einen 10Bit Analog Digital Wandler, welcher wahlweise über die Pins J203-1 – J203-8 gemultiplext werden können. Diese Pins entsprechen dem Port PA0-PA7 des 1284p. Hier wird der Pin PA3, dh. Pin J203-4 genutzt. Der Pin Aref, welcher die Referenzspannung für den ADC bereitstellt, ist intern über einen Widerstand mit dem Pin AVCC verbunden. Die Referenzspannung beträgt somit 3,26V.

Aufbau

Über Pin J203-10 kann die Referenzspannung abgegriffen werden. Über Pin J203-9 ist die Masse verfügbar. Der NTC verringert nun bei steigender Temperatur seinen Widerstand, dadurch verschiebt sich auch das Spannungsverhältnis zwischen R1 und R2(PTC). Mit steigender Temperatur sinkt also die Eingangsspannung an Pin J203-4.

OID erstellen

Siehe dazu Erstellung einer eigenen MIB für den Contiki 2.5 SNMP Agent Abschnitt OID

/*Temperaturerfassung*/
static u8t ber_oid_temp_int[] PROGMEM     = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xac, 0x5d, 0x66, 0x01};
static ptr_t oid_temp_int PROGMEM         = {ber_oid_temp_int, 10};

Get Funktion

Die Abfrage der Temperatur erfolgt mithilfe der Get Funktion, wie auch beschrieben in Erstellung einer eigenen MIB für den Contiki 2.5 SNMP Agent. Bevor jedoch die eigentliche get-Funktion ausgeführt werden kann, sind folgende Schritte notwendig:

Initialisieren des ADC

Über das ADMUX Register wird die Referenzspannung gewählt. Durch das Setzen von REFS0 auf 1 wird die externe Referenzspannung AVCC gewählt. Außerdem sind über das Register ADCSRA durch das Bit ADEN der ADC aktiviert und über ADSP0, ADPS1 und ADSP2 der Frequenzvorteiler gewählt. Details siehe Datenblatt 1284p. Durch die Funktion adc_init() kann nun der ADC initialisiert werden.

void adc_init()
{
    // AREF = AVcc
    ADMUX = (1<<REFS0);
 
    // ADC Enable and prescaler of 128
    // 16000000/128 = 125000
    ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

Abfrage des ADC Werts

Um nun eine Messung zu starten und den Wert auszulesen, muss zuerst der Kanal festgelegt werden. Anschließend kann durch setzen des Bits ADSC im Register ADCSRA die Messung gestartet werden. Dieses Bit bleibt solange 1 bis die Messung abgeschlossen ist. Ist ADSC wieder 0 kann der ADC Wert übergeben werden.

u16t adc_read(u8t ch)
{
    // select the corresponding channel 0~7
    // ANDing with '7' will always keep the value
    // of 'ch' between 0 and 7
    ch &= 0b00000111;  // AND operation with 7
    ADMUX = (ADMUX & 0xF8)|ch;     // clears the bottom 3 bits and sets channel
 
    // start single convertion
    // write '1' to ADSC
    ADCSRA |= (1<<ADSC);
 
    // wait for conversion to complete
    // ADSC becomes '0' again
    // till then, run loop continuously
    while(ADCSRA & (1<<ADSC));
 
    return (ADC);
}

Bestimmung des aktuellen NTC Widerstands

Mit den beiden Funktionen void adc_init() und u16t adc_read(u8t ch) kann nun der aktuelle ADC Wert eingelesen werden. Der im 1284p verwendete ADC besitzt eine Auflösung von 10Bit, es sind also Werte von 0 bis 1024 möglich. Da die Referenzspannung 3,26V beträgt, entspricht ein ADC Wert von 1:

Somit ist es also möglich die aktuelle Spannung am NTC zu bestimmen. Zur Bestimmung des aktuellen Widerstandswerts kann nun die Spannungsteilerformel angewandt werden:

Wird nun durch und durch ersetzt, so erhält man:

Diese Berechnung wurde in der folgenden Funktion adcToOhm() umgesetzt.

float adcToOhm(u16t adc_value)
{
	float one_adc=0.00318359375;
	float r_div=10000;
	float u_ges=3.26;
	float r_ntc;
	r_ntc=(((float)adc_value)*one_adc*r_div)/(u_ges-(((float)adc_value)*one_adc));
	printf("RNTC=%f\n",r_ntc);
	return r_ntc;
}

Bestimmung der Temperatur

Leider besitzt der verwendete NTC keine lineare Kennlinie für das Widerstands-Temperaturverhältnis. Laut Datenblatt erfolgt die Berechnung mithilfe der Formel:

Diese ist jedoch zu Aufwendig um sie im 1284p zu implementieren, es wird deshalb ein Array mit berechneten Werten genutzt. Glücklichweise liefert das Datenblatt die berechneten Werte im 5° Abstand in der Form . Um eine genauere Skalierung von 1° zu erreichen wurden die Werte dazwischen interpoliert und in ein u16t Array eingetragen. (Werte wurden mit 100 multipliziert um u16t nutzen zu können)

u16t temp_array[] PROGMEM ={               325, 311, 296, 282, 267, 
                                           253, 242, 231, 220, 209, 
				           198, 189, 182, 173, 165, 
				           157, 151, 144, 138, 131, 
				           125, 120, 115, 110, 105, 
				           100, 96, 92, 88, 84,
				           80, 77, 74, 71, 68, 
				           65, 63, 60, 58, 55,
				           53, 51, 49, 47, 45,
				           43, 42, 40, 39, 37,
				           36, 35, 33, 32, 30,
				           29, 28, 27, 27, 26,
				           25, 24, 23, 23, 22,
				           21, 20, 19, 19, 18,
				           17, 17, 16, 16, 15,
				           15, 14, 14, 13, 13,
				           12, 12, 12, 11, 11,
				           11, 11, 10, 10, 9,
				           9, 9, 8, 8, 7,
				           7, 7, 7, 6, 6,
				           6};
//Werte laut Datenblatt in 5grad Schritten Zwischenwerte gemittelt Beginn bei 0°

Um Speicherplatz zu sparen, wurde das Array in das Programm Memory verschoben. Für den Zugriff muss deshalb eine weitere Funktion genutzt werden.

u16t getTempArray(u8t index) {
    return pgm_read_dword(&temp_array[index]);
}

Im nächsten Schritt kann nun der aktuelle Widerstandswert mit dem im Array verglichen werden und so die aktuelle Temperatur bestimmt werden. Das Array enthält Werte im Bereich von 0° bis 100°. Die Variable i liefert so den aktuellen Temperaturwert in °C zurück.

u8t find_temp_celsius(float r_ntc)
{	
	float rt_r25;
	float r_div=10000;
	rt_r25=(r_ntc/r_div)*100;
	u8t i=0;
	while((u16t)rt_r25 <= getTempArray(i))
	{
		i++;
	}
	return (i-1);
}

Die eigentliche get-Funktion

Im nächsten Schritt kann nun die eigentliche get-Funktion geschrieben werden, diese initialisiert zuerst den ADC, liest dann den aktuellen Wert ein, wandelt den ADC Wert in den aktuellen Widerstandswert und bestimmt anschließend die aktuelle Temperatur.

s8t getTempValue(mib_object_t* object, u8t* oid, u8t len)
{
	adc_init();
	u16t adc_value;
	u8t adc_channel;
	float r_ntc;
	adc_channel=3;
	adc_value=adc_read(adc_channel);
	r_ntc=adcToOhm(adc_value);
    object->varbind.value.i_value = find_temp_celsius(r_ntc);
	printf("Get temperature Value ausgeführt, ADC Value int %d RNTC=%f Ohm\n",adc_value,r_ntc);
	printf("Get temperature Value ausgeführt, iValue Ergebnis in Grad Celsius: %d\n",object->varbind.value.i_value);
    return 0;
}

Aufruf in mib_init()

Am Ende fehlt jetzt nur noch die Initialisierung der neuen OID in der Funktion mib_init().

if (add_scalar(&oid_temp_int, 0, BER_TYPE_INTEGER, 0, &getTempValue, 0) == -1) 
{
        return -1;
}

Die aktuelle (21.05.2012) funktionsfähige Raven Board MIB kann unter mib_210512_source betrachtet werden.