AD7793 — малошумящий 24-разрядный сигма-дельта АЦП с тремя дифференциальными аналоговыми входами. АЦП AD7793 предназначен для высокоточного измерения постоянного напряжения, может применяться для измерения напряжения термопар и других датчиков с выходным напряжением в несколько единиц или десятков мВ.
Перед прочтением статьи рекомендую ознакомится с тремя первыми статьями про АЦП AD7793:
На базе AD7793 с использованием Arduino Nano можно собрать миллиомметр с диапазоном измерения от 0,0000 до 32,000 Ом.
Схема миллиомметра
Вход АЦП сконфигурирован как несимметричный вход, предусилитель АЦП имеет коэффициент усиления равный 1, частота опроса 4,17 Гц.
Для точного измерения необходимо иметь эталонное сопротивление 100 Ом (манганин) через которое подается ток на измеряемое сопротивление, от точности эталонного сопротивления зависит точность прибора. Если нет возможности очень точно изготовить (намотать из манганиновой проволоки) эталонное сопротивление, то откалибровать прибор можно при помощи другого эталонного сопротивления 1…30 Ом с классом точности не менее 0.02 %.
Для упрощения конструкции прибора напряжение на эталонное сопротивление 100 Ом подается от источника питания +5 В Arduino, которое также является аналоговым напряжением питания АЦП AD7793, а так как в АЦП имеется возможность контроля этого напряжения, то оно становится опорным, так как перед каждым замером сопротивления производится измерения аналогового напряжения питания АЦП которое также подается на эталонное сопротивление.
#include <Wire.h> #include <LiquidCrystal_I2C.h> // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=45&download=1 LiquidCrystal_I2C lcd(0x27,16,2); // Устанавливаем дисплей // AD7793 DVDD = 5V | AVDD = 5V | R = 0...32 Om #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 #define R_100 99.969 #define K_0 0.0015 #define K_AVDD 6.065 byte v1[8] = {0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}; byte v2[8] = {0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00}; byte v3[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F}; byte v4[8] = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x1F,0x1F}; byte v5[8] = {0x1C,0x1C,0x00,0x00,0x00,0x00,0x1C,0x1C}; byte v6[8] = {0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C}; byte v7[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07}; byte v8[8] = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00}; byte d1,d2,d3,d4,d5,d6,e1,e2,e3,a[6],x; void setup(){ Serial.begin(9600); 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(100);// MODE REGISTER System Zero-Scale Calibration write_byte(0x08,(0xE0<<8)|0x0A, 16);delay(100);// 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,0x00, 8);// IO REGISTER lcd.init();lcd.backlight(); lcd.createChar(1, v1);lcd.createChar(2, v2);lcd.createChar(3, v3);lcd.createChar(4, v4); lcd.createChar(5, v5);lcd.createChar(6, v6);lcd.createChar(7, v7);lcd.createChar(8, v8); lcd.setCursor(5, 0);lcd.print(F("AD7793")); delay(1000);lcd.clear(); } void loop(){ // READ ADC ////////////////////////////////////////////////////////////////// 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); float u = 1170.00/0xFFFFFF*adc/1; Serial.print("DATA: ");Serial.println(adc); delay(1); 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 adc = read_byte(0x58,24); float u_100 = 1170.00/0xFFFFFF*adc*K_AVDD; delay(1); float r = ((R_100*u)/(u_100-u))-K_0; long number = r*10000; Serial.print("R_NUM: ");Serial.println(number); Serial.print("AVDD: ");Serial.println(u_100); Serial.println(); /// R < 10 if(r<10){ a[0]=number/10000; a[1]=number/1000%10; a[2]=number/100%10; a[3]=number/10%10; a[4]=number%10; for(x=0;x<5;x++){ switch(x){ case 0: e1=0;e2=1,e3=2;break; case 1: e1=4,e2=5,e3=6;break; case 2: e1=7,e2=8,e3=9;break; case 3: e1=10,e2=11,e3=12;break; case 4: e1=13,e2=14,e3=15;break; }digit();} lcd.setCursor(3,1);lcd.print(".");lcd.setCursor(3,0);lcd.print(" "); } /// R > 10 if(r>=10){ if(number>319999){number=319999;lcd.setCursor(6,0);lcd.print("*");} else{lcd.setCursor(6,0);lcd.print(" ");} a[0]=number/100000; a[1]=number/10000%10; a[2]=number/1000%10; a[3]=number/100%10; a[4]=number/10%10; for(x=0;x<5;x++){ switch(x){ case 0: e1=0;e2=1,e3=2;break; case 1: e1=3,e2=4,e3=5;break; case 2: e1=7,e2=8,e3=9;break; case 3: e1=10,e2=11,e3=12;break; case 4: e1=13,e2=14,e3=15;break; }digit();} lcd.setCursor(6,1);lcd.print("."); } } void digit(){ switch(a[x]){ case 0: d1=1,d2=8,d3=6,d4=1,d5=3,d6=6;break; case 1: d1=32,d2=2,d3=6,d4=32,d5=32,d6=6;break; case 2: d1=2,d2=8,d3=6,d4=1,d5=4,d6=5;break; case 3: d1=2,d2=4,d3=6,d4=7,d5=3,d6=6;break; case 4: d1=1,d2=3,d3=6,d4=32,d5=32,d6=6;break; case 5: d1=1,d2=4,d3=5,d4=7,d5=3,d6=6;break; case 6: d1=1,d2=4,d3=5,d4=1,d5=3,d6=6;break; case 7: d1=1,d2=8,d3=6,d4=32,d5=32,d6=6;break; case 8: d1=1,d2=4,d3=6,d4=1,d5=3,d6=6;break; case 9: d1=1,d2=4,d3=6,d4=7,d5=3,d6=6;break; case 10:d1=150,d2=150,d3=150,d4=150,d5=150,d6=150;break; } lcd.setCursor(e1,0);lcd.write((uint8_t)d1); lcd.setCursor(e2,0);lcd.write((uint8_t)d2);lcd.setCursor(e3,0);lcd.write((uint8_t)d3);lcd.setCursor(e1,1); lcd.write((uint8_t)d4);lcd.setCursor(e2,1);lcd.write((uint8_t)d5);lcd.setCursor(e3,1);lcd.write((uint8_t)d6);} 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); }
Для получения большой точности при измерении сопротивлений миллиомметр нуждается в калибровке:
#define R_100 99.969
#define K_0 0.0015
#define K_AVDD 6.065
K_AVDD — калибровочный коэффициент напряжения подаваемого на эталонное сопротивление, так как оно является аналоговым напряжением питания АЦП и имеется возможность его измерять, то необходимо очень точно произвести калибровку результата его измерения. Для этого измерьте напряжение подаваемое на эталонное сопротивление, откройте монитор порта и сравните его с измеренным значением.
AVDD — напряжением аналогового питания АЦП (выделено красным)
Измените K_AVDD таким образом, чтобы напряжение подаваемое на эталонное сопротивление соответствовало напряжению AVDD в мониторе порта.
R_100 — точное значение эталонного сопротивления.
K_0 — калибровка нуля. Замкните вход омметра, если показания не равны нулю, то укажите в K_0 погрешность нуля.
Результаты измерений
0 Ом
R0 магазина сопротивлений МСР-60М (0,02%), это значение необходимо вычитать в последующих измерениях
0,01 Ом (измеренное значение 0,009 Ом)
0,05 Ом (измеренное значение 0,0541 Ом)
0,1 Ом (измеренное значение 0,0997 Ом)
0,5 Ом (измеренное значение 0,5047 Ом)
1,0 Ом (измеренное значение 1,0001 Ом)
5,0 Ом (измеренное значение 4,9999 Ом)
10,0 Ом (измеренное значение 9,997 Ом)
15,0 Ом (измеренное значение 14,998 Ом)
20,0 Ом (измеренное значение 19,995 Ом)
25,0 Ом (измеренное значение 24,995 Ом)
30,0 Ом (измеренное значение 29,996 Ом)
Р310 катушка электрического сопротивления (мера) 0,001 Ом
Форум — http://forum.rcl-radio.ru/viewtopic.php?pid=6549#p6549