AD7793 + термопара + термометр сопротивления (Arduino)

AD7793 — малошумящий 24-разрядный сигма-дельта АЦП с тремя дифференциальными аналоговыми входами. АЦП AD7793 предназначен для высокоточного измерения постоянного напряжения, может применяться для измерения напряжения термопар и других датчиков с выходным напряжением в несколько единиц или десятков мВ.

Перед прочтением статьи рекомендую ознакомится с двумя первыми статьями про АЦП AD7793:

В статье AD7793 + термопара (Arduino) был рассмотрен пример создания простого но высокоточного измерителя-регулятора температуры с применением в качестве датчика термопару. Большая разрядность АЦП AD7793 и линейность характеристики позволяет весьма точно измерять постоянное напряжение которое не превышает единиц мВ. Ранее описанный измеритель-регулятор преобразует ЭДС термопары в температуру с погрешностью не более ±0,2…±0,3 °С на всем диапазоне, а погрешность измерения ЭДС термопары не превышает 5…10 мкВ.

В этой статье будет рассмотрен пример использования АЦП AD7793 в качестве измерителя-регулятора с использованием датчика термометра сопротивлений.

Для примера была выбрана номинальная статическая характеристика (НСХ) 100П, при желании можно добавить в измеритель-регулятор еще несколько номинальных статических характеристик (НСХ).

Измеритель-регулятор с датчиком термосопротивления не будет отдельным уст-вом, он дополнит уже ранее описанный в термометр-регулятор с использованием в качестве датчиков термопары ТХА и ТХК.

АЦП AD7793 содержит два программируемых источника тока номиналами 10 мкА, 210 мкА, 1 мА. Токовые выходы IOT1 и IOT2 могут независимо работать друг от друга, а источники тока могут быть направлены в один токовый выход.

IO REGISTER управляет работой токовых выходов и источника тока.

Биты IEXCDIR1 IEXCDIR0 позволяю выбрать режим работы токовых выходов, а биты IEXCEN1 IEXCEN0 задают номиналы тока.

В рассматриваемом примере оба источника тока в 1 мА перенаправлены на выход IOT1, что в сумме дает ток 2 мА. Ток подается на термосопротивление, на котором вход АЦП IN2 измеряет падение напряжения, исходя из полученного значения вычисляется сопротивление термометра сопротивления и программно высчитывается температура.

Для уменьшения влияния измерительных проводов используется 4-х проводная схема подключения, при этом пары измерительных проводов соединяются непосредственно на клеммах термосопротивления.

Управление измерителем-регулятором очень простое, кнопка энкодера переключает тип датчика (ХА, ХК, 100П), а поворот ручки энкодера изменяет температуру регулирования. Выбранный последним тип датчика и температура регулирования заносятся в энергонезависимую память.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>   // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=45&download=1
#include <Encoder.h>             // http://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip 
#include <OneWire.h>             // http://rcl-radio.ru/wp-content/uploads/2018/07/OneWire.zip
#include <EEPROM.h>
#include <MsTimer2.h>            // http://rcl-radio.ru/wp-content/uploads/2018/11/MsTimer2.zip 
 
 LiquidCrystal_I2C lcd(0x27,16,2);  // Устанавливаем дисплей
 OneWire  ds(6); // Вход датчика 18b20
 Encoder myEnc(8, 9);// DT, CLK
 
 
// AD7793 x16 Umax=73mV CH1_DIFF Fads=4.17Hz
#define SCLK   2
#define CS     3
#define DIN    4
#define DOUT   5
 
#define AVDD_MONITOR 0b111 // CONF
#define TEMP_SENSOR  0b110 // CONF
#define CH1          0b000 // CONF
#define CH2          0b001 // CONF
#define CH3          0b011 // CONF
#define BIPOLAR      0b00000000 // CONF
#define UNIPOLAR     0b00010000 // CONF
#define GAIN_1       0b000      // CONF
#define GAIN_2       0b001      // CONF
#define GAIN_4       0b010      // CONF
#define GAIN_8       0b011      // CONF
#define GAIN_16      0b100      // CONF
#define GAIN_32      0b101      // CONF
#define GAIN_64      0b110      // CONF
#define GAIN_128     0b111      // CONF
 
#define F_ADC_500    0b0001 // MODE 
#define F_ADC_250    0b0010 // MODE 
#define F_ADC_125    0b0011 // MODE 
#define F_ADC_62_5   0b0100 // MODE 
#define F_ADC_50_0   0b0101 // MODE 
#define F_ADC_39_2   0b0110 // MODE
#define F_ADC_33_3   0b0111 // MODE 
#define F_ADC_19_6   0b1000 // MODE 
#define F_ADC_16_7   0b1001 // MODE 
#define F_ADC_12_5   0b1011 // MODE 
#define F_ADC_10_0   0b1100 // MODE 
#define F_ADC_8_33   0b1101 // MODE 
#define F_ADC_6_25   0b1110 // MODE  
#define F_ADC_4_17   0b1111 // MODE       
 
// XA K
 const float a[]{0,2.5173462*10,-1.1662878,-1.08336338,-8.9773540/10,-3.7342377/10,-8.6632643/100,-1.0450598/100,-5.1920577/10000};
 const float a1[]{0,2.508355*10,7.860106/100,-2.503131/10,8.315270/100,-1.228034/100,9.804036/10000,-4.413030/100000,1.057734/1000000,-1.052755*pow(10,-8)};
 const float a2[]{-1.318058*100,4.830222*10,-1.646031,5.464731/100,-9.6550715/10000,8.802193/1000000,-3.110810/100000000};
 // XK L
 const float b[]{1.1573067/10000,1.5884573*10,4.0458554/100,0.3170064,0.1666128,5.146958/100,9.5288883/1000,1.0301283/1000,6.0654431/100000,1.5131878/1000000};
 const float b1[]{7.2069422/1000,1.5775525*10,-0.2261183,9.4286756/1000,-3.5394655/10000,1.0050886/100000,-1.9323678/10000000,2.3816891/1000000000,-1.7130654*pow(10,-11),5.4857331*pow(10,-14)};
 
float temp_ds,u,u0,t_iz;
unsigned long times,times_enc,times_eeprom,oldPosition  = -999,newPosition;;
int menu,reg;
bool w,start=1;
/// калибровка
float gis=0.2;
float i_1ma = 1.0137;
float r_0_kall = 0.29;
 
void setup(){ 
  Serial.begin(9600);
  MsTimer2::set(1, to_Timer);MsTimer2::start();
  pinMode(SCLK,OUTPUT);pinMode(CS,OUTPUT);pinMode(DIN,OUTPUT);pinMode(DOUT,INPUT);
  digitalWrite(SCLK,HIGH);digitalWrite(CS,HIGH);digitalWrite(DIN,HIGH);
  delay(100);
  reset();
  write_byte(0x08,(0x20<<8)|F_ADC_4_17, 16);// MODE REGISTER
  write_byte(0x10,((UNIPOLAR|GAIN_1)<<8)|0x90|AVDD_MONITOR, 16); // CONFIGURATION REGISTER 
  write_byte(0x08,(0xC0<<8)|0x0A, 16);delay(10);// MODE REGISTER System Zero-Scale Calibration
  write_byte(0x08,(0xE0<<8)|0x0A, 16);delay(10);// MODE REGISTER System Full-Scale Calibration
  Serial.print("Stat ");Serial.println(read_byte(0x40,8),BIN);// STATUS REGISTER
  Serial.print("ID   ");Serial.println(read_byte(0x60,8),BIN);// ID REGISTER 
  Serial.print("Ofst ");Serial.println(read_byte(0x70,24));//OFFSET REGISTER
  Serial.print("ful  ");Serial.println(read_byte(0x78,24));//OFULL-SCALE REGISTER 
  write_byte(0x28,0b00001011, 8);// IO REGISTER 2mA
  pinMode(10,INPUT);
  pinMode(13,OUTPUT);
  menu = EEPROM.read(0);reg = EEPROM.read(1)*256 + EEPROM.read(2);
  lcd.init();lcd.backlight();
  lcd.setCursor(5, 0);lcd.print(F("AD7793"));
  delay(1000);lcd.clear();
}
 
void loop(){ 
// ENCODER ///////////////////////////////////////////////////////////////////
 if(newPosition != oldPosition){oldPosition = newPosition;
  reg=reg+newPosition;myEnc.write(0);newPosition=0;times_enc=millis();times_eeprom=millis();w=1;
  if(menu==0&&reg>1350){reg=1350;}if(menu==0&&reg<-270){reg=-270;}
  if(menu==1&&reg>800){reg=800;}if(menu==1&&reg<-200){reg=-200;}
  if(menu==2&&reg>850){reg=850;}if(menu==1&&reg<-200){reg=-200;}
  }
 
/// termopara ///////////////////  
// READ ADC //////////////////////////////////////////////////////////////////
if((millis()-times_enc>1000||start==1)&&menu<2){  
  write_byte(0x08,(0x20<<8)|F_ADC_4_17, 16);// MODE REGISTER
  write_byte(0x10,((BIPOLAR|GAIN_16)<<8)|0x90|CH1, 16); // CONFIGURATION REGISTER 
  long adc = read_byte(0x58,24);
  u = (1170.00/0x800000*(adc-0x800000))/16;
// BIPOLAR  
  Serial.print("Data b_B ");Serial.println(adc-0x800000);//DATA REGISTER
  Serial.print("Data V_B ");Serial.println((1170.00/0x800000*(adc-0x800000))/16,3);//DATA REGISTER 
// DS18B20 
  if(millis()-times>10000||start==1){temp_ds = dsRead(0);times=millis();}
  start=0;
}
/// end termopara
 
// READ ADC //////////////////////////////////////////////////////////////////
if(millis()-times_enc>1000&&menu>1){  
  write_byte(0x08,(0x20<<8)|F_ADC_4_17, 16);// MODE REGISTER
  write_byte(0x10,((UNIPOLAR|GAIN_1)<<8)|0x90|CH2, 16); // CONFIGURATION REGISTER 
  long adc = read_byte(0x58,24);
  u = (1170.00/0xFFFFFF*adc)/i_1ma/2 + r_0_kall;// 2mA
// UNIPOLAR
  Serial.print("Data b_U ");Serial.println(adc);//DATA REGISTER
  Serial.print("Data V_U ");Serial.println((1170.00/0xFFFFFF*adc)/1,3);//DATA REGISTER 
 
 
}
 
// LCD ///////////////////////////////////////////////////////////////////////  
  if(digitalRead(10)==LOW){menu++;EEPROM.update(0,menu);}
  if(menu>2){menu=0;}
 
  lcd.setCursor(0, 1);
  if(menu<2){lcd.print(u,3);lcd.print("mV  ");}else{lcd.print(u,2);lcd.print(" Om ");}
  lcd.setCursor(10, 1);lcd.print(reg); lcd.print(" ");lcd.setCursor(14, 1);lcd.print(char(223)); lcd.print("C  ");
  if(menu==0){txa(); lcd.setCursor(12,0);lcd.print(" TXA");}
  if(menu==1){txk(); lcd.setCursor(12,0);lcd.print(" TXK");}
  if(menu==2){p100(); lcd.setCursor(12,0);lcd.print("100P");}
  lcd.setCursor(0, 0);
  if(t_iz==-1000){lcd.print("LOW      ");}
  else if(t_iz==10000){lcd.print("HIGH     ");}
  else{lcd.print(t_iz,2);lcd.print(char(223)); lcd.print("C "); }
// rele + gis ////////////////////////
  if(reg >= t_iz + gis){digitalWrite(13,HIGH);lcd.setCursor(10,0);lcd.print("H");}
  if(reg <= t_iz - gis){digitalWrite(13,LOW);lcd.setCursor(10,0);lcd.print("L");} 
 
////////////////////// eeprom ////////////////////////////////////////  
  if(w==1&&millis()-times_eeprom>10000){ 
   EEPROM.update(1,highByte(reg)); EEPROM.update(2,lowByte(reg));w=0;}   
  delay(10);  
}
 
void write_byte(byte reg, long data, int bit_){
     digitalWrite(CS,LOW); 
  for(int i = 7; i >= 0; i--){
     digitalWrite(SCLK,LOW);
     digitalWrite(DIN, (reg >> i) & 1);
     digitalWrite(SCLK,HIGH);
     }
  for(int i = bit_-1; i >= 0; i--){
     digitalWrite(SCLK,LOW);
     digitalWrite(DIN, (data >> i) & 1);
     digitalWrite(SCLK,HIGH); 
     }
     digitalWrite(CS,HIGH);
     }
 
 
  long read_byte(byte reg, int bit_){
     digitalWrite(CS,LOW);
  while(digitalRead(DOUT)!=LOW); 
  for(int i = 7; i >= 0; i--){
     digitalWrite(SCLK,LOW); 
     digitalWrite(DIN, (reg >> i) & 1);
     digitalWrite(SCLK,HIGH);
     }
  while(digitalRead(DOUT)!=LOW); 
  long data_out=0;
  long dat;
  for(int i = bit_-1; i >= 0; i--){
     digitalWrite(SCLK,LOW);
     digitalWrite(SCLK,HIGH);
     dat = digitalRead(DOUT);
     data_out |= (dat<<i); 
     }
     digitalWrite(CS,HIGH);
     return data_out; 
  }
 
void reset(){
     digitalWrite(CS,LOW);   
  for(int i = 31; i >= 0; i--){
     digitalWrite(SCLK,LOW);
     digitalWrite(DIN, HIGH);
     digitalWrite(SCLK,HIGH);
     }
     digitalWrite(CS,HIGH);
  }
 
float dsRead(byte x) {
  byte data[2], addr[8][8], kol = 0;
  while (ds.search(addr[kol])) {  // поиск датчиков, определение адреса и кол-ва датчиков
    kol++;
  } 
  ds.reset_search();  // Сброс поиска датчика
  ds.reset();         // Инициализация, выполняется сброс шины
  ds.select(addr[x]); // Обращение к датчику по адресу
  ds.write(0x44, 0);  // Измерение температуры с переносом данных в память
  ds.reset();         // Инициализация, выполняется сброс шины
  ds.select(addr[x]); // Обращение к датчику по адресу
  ds.write(0xBE);     // Обращение памяти
  data[0] = ds.read();// Чтение памяти byte low
  data[1] = ds.read();// Чтение памяти byte high
  float value = ((data[1] << 8) | data[0]) / 16.0; return (float)value; // Расчет температуры и вывод
} 
 
void txa(){u0=u+temp_ds*0.0400;// окружающая температура 0.0400 мВ на 1 гр.Цельсия от 0 до 40 гр.Цельсия для TXA(K)
if(u0<0){t_iz=(a[0]*pow(u0,0))+(a[1]*pow(u0,1))+(a[2]*pow(u0,2))+(a[3]*pow(u0,3))+(a[4]*pow(u0,4))+(a[5]*pow(u0,5))+(a[6]*pow(u0,6))+(a[7]*pow(u0,7))+(a[8]*pow(u0,8));}
if(u0>=0&&u0<=20.64){t_iz=(a1[0]*pow(u0,0))+(a1[1]*pow(u0,1))+(a1[2]*pow(u0,2))+(a1[3]*pow(u0,3))+(a1[4]*pow(u0,4))+(a1[5]*pow(u0,5))+(a1[6]*pow(u0,6))+(a1[7]*pow(u0,7))+(a1[8]*pow(u0,8))+(a1[9]*pow(u0,9));}
if(u0>20.64){t_iz=(a2[0]*pow(u0,0))+(a2[1]*pow(u0,1))+(a2[2]*pow(u0,2))+(a2[3]*pow(u0,3))+(a2[4]*pow(u0,4))+(a2[5]*pow(u0,5))+(a2[6]*pow(u0,6));}
if(u<-6.35){t_iz=-1000;} if(u>54.9){t_iz=10000;}}
 
void txk(){u0=u+temp_ds*0.06476;// окружающая температура 0.06476 мВ на 1 гр.Цельсия от 0 до 40 гр.Цельсия для TXK(L)
if(u0<0){t_iz=(b[0]*pow(u0,0))+(b[1]*pow(u0,1))+(b[2]*pow(u0,2))+(b[3]*pow(u0,3))+(b[4]*pow(u0,4))+(b[5]*pow(u0,5))+(b[6]*pow(u0,6))+(b[7]*pow(u0,7))+(b[8]*pow(u0,8))+(b[9]*pow(u0,9));}
if(u0>=0){t_iz=(b1[0]*pow(u0,0))+(b1[1]*pow(u0,1))+(b1[2]*pow(u0,2))+(b1[3]*pow(u0,3))+(b1[4]*pow(u0,4))+(b1[5]*pow(u0,5))+(b1[6]*pow(u0,6))+(b1[7]*pow(u0,7))+(b1[8]*pow(u0,8))+(b1[9]*pow(u0,9));}
if(u<-9.49){t_iz=-1000;} if(u>66.47){t_iz=10000;}
}
 
void p100(){if(u>=100.00){t_iz=(sqrt(0.000015752961+0.0000023364*(1-u/100.00))-0.003969 )/-0.0000011682;}
if(u<100.00){t_iz=251.903*(pow(u/100.00-1,1)) + 8.80035*(pow(u/100.00-1,2)) - 2.91506 *(pow(u/100.00-1,3)) + 1.67611*(pow(u/100.00-1,4));}
if(u<17.24){t_iz=-1000;} if(u>395.16){t_iz=10000;}
  }
 
void to_Timer(){newPosition = myEnc.read()/4;}

Внутренние источники тока имею отличную стабильность, но не очень высокую точность выходного тока, поэтому для получения высокой точности измерения температуры при использовании термосопротивления необходимо произвести калибровку.

/// калибровка

  • float gis=0.2;
  • float i_1ma = 1.0137;
  • float r_0_kall = 0.29;

Гистерезис gis общий для всех датчиков, i_1ma калибровочный коэффициент источника тока, фактически это точное значение тока который выдает один источник тока, настраивать этот коэффициент необходимо при подключении ко входу сопротивления 300…400 Ом, меняя коэффициент необходимо добиться максимально точных показаний омметра измерителя-регулятора. r_0_kall — калибровка нуля, для его определения необходимо ко входу подключить сопротивление номиналом 1…5 Ом, а r_0_kall это погрешность измерения. Например, если Вы подключили ко входу сопротивление 1 Ом, а на дисплее Вы видите показание 0,71 Ом, то калибровочный коэффициент нуля будет 0,29. Для точного определения калибровочных значений необходимо повторить ранее описанную процедуру калибровки 2-3 раза.

  1. Температура измеренная термометром сопротивления
  2. Омметр
  3. Индикатор нагрева
  4. Тип датчика
  5. Температура регулирования

Результаты измерений

На вход измерителя регулятора подключен магазин сопротивлений МСР-60М (0,02%)

300 Ом

200 Ом

100 Ом

50 Ом

Тест точности измерения сопротивления и точности преобразования сопротивления в температуру

Температура, °С Значение номинальной статической характеристики соответствующее заданной температуре, Ом Измеренной значение температуры, °С Измеренное значение сопротивления, Ом
-150 38,79 -150,00 38,83
-50 80,00 -49,87 80,02
0 100,00 0,09 100,03
200 177,04 199,96 177,03
400 249,41 399,91 249,38
600 317,11 599,89 317,07
800 380,14 799,91 380,11

Как видно из таблицы максимальная погрешность при измерении температуры не превышает 0,13 °С при диапазоне 1050 °С (-200…+850 °С), что дает относительную погрешность в 0,0123%. При измерении сопротивления в диапазоне от 0 до 400 Ом относительная погрешность не превысила 0,01%. Полученная погрешность будет немного выше, так как магазин сопротивлений имеет класс точности 0,02%.

Форум — http://forum.rcl-radio.ru/viewtopic.php?id=505

Даташит — ad7793.pdf

Добавить комментарий

Войти с помощью: