Внешний АЦП
- Войдите на сайт для отправки комментариев
Привет!
Появилась необходимость более точнее считывать напряжение на аналоговом входе. Купил 2 внешних АЦП:
1) AD7705 (16 bit / http://www.analog.com/static/imported-files/data_sheets/AD7705_7706.pdf)
Пытался подключить по SPI:
Более наглядно:
т.к. максимальный уровень 65535 соответствует 2.5В, подключая на аналог #1 1.2В я должен получить что-то похожее на 65535/2, однако на монитор выводятся только нули.
Скетчи я испробовал разные, например:
http://playground.arduino.cc/Code/MCP3208 | http://forum.arduino.cc/index.php/topic,14450.0.html | http://www.edaboard.com/thread251037-2.html | http://forum.arduino.cc/index.php/topic,18893.0.html
Пробовал использовать готовую библиотеку для данного ацп — http://www.kerrywong.com/2012/04/18/ad7705ad7706-library-revisited/ .
Ничего не помогло.
2) AD7714 (24 bit / http://www.analog.com/static/imported-files/data_sheets/AD7714.pdf)

Более наглядно:

Аналогичено предыдущему примеру, на мониторе одни нули.
Скетчи пробывал изменять как и для 16 бит, так и пытался использовать:
http://forum.arduino.cc/index.php?topic=193723.0 | http://forum.arduino.cc/index.php?topic=57873.0;wap2 | http://forum.arduino.cc/index.php?topic=57873.0 | http://interface.khm.de/index.php/lab/experiments/connect-a-ltc2400-high... | http://dangerousprototypes.com/forum/viewtopic.php?t=4247&p=42053 | http://forums.netduino.com/index.php?/topic/1831-netduino-with-24bit-adc...
Подключения стандартные:


DATAOUT 11 // MOSI
DATAIN 12 // MISO
Если DATAOUT это выход АЦП (и вроде-бы АЦП у нас слейвом работает, а Ардуина мастером)
то MOSI это MacterOUT - SlaveIN для Ардуины.
Может я ошибаюсь, но мне кажется что MOSI MISO перепутаны...
Может я ошибаюсь, но мне кажется что MOSI MISO перепутаны...
Да, Вы правы.
Подсоеденил по следующей схеме:
Для конкретного разбора взял ad7705.
Скетч:
#include <AD770X.h> #define DRDY (7) //set reference voltage to 2.5 V AD770X ad7706(4.8); double v; void setup() { //initializes channel 1 ad7706.reset(); ad7706.init(AD770X::CHN_AIN1); Serial.begin(9600); } void loop() { //read the converted results (in volts) while( !digitalRead(DRDY) ); v = ad7706.readADResult(AD770X::CHN_AIN1); Serial.println(v); delay(100); }Используемая библиотека — http://www.kerrywong.com/2012/04/18/ad7705ad7706-library-revisited/ .
Иногда работает правильно, но потом появляются не понятные мне цифры, например, опорное 4.8V, на аналог (1) идет 2.5, кварц - 2MHz
Жму кнопку reset на плате, затем в пор идут первые 2-е адекватные цифры, потом пауза в секунды 2 и снова 2048:
Поставьте кондёрчики на входы для проверки.
Хотя-бы 0.1 мкф, если сигнал не слишком быстро меняется.
Или подайте на входы 3.3 Вольта прямо с платы. И тогда смотрите что будет.
И ещё возможно вы читаете когда этого нельзя делать, там есть интересный выход DRDY (Data Ready).
Он выставляется в "0" при готовности данных.
Вешайте на прерывание (я бы поставил на FALLING) и читайте правильные данные.
Или в цикле через digitalRead...
Наверное железка не успевает отдавать данные. Кроме того, в даташите что-то говорится про Master clock frequency = 400 KHz... Вобщем, я бы, для начала, сделал паузу между считыванием в секунду-две, а потом уменьшал бы ее.
Вот эти ссылки, надеюсь, уже изучили?
http://www.gaw.ru/html.cgi/txt/ic/Analog_Devices/adc/delta/ad7705-06.htm
http://forum.qrz.ru/inzhenernyy-forum-radiolyubitelskie-tehnologii/18813...
Автор, разобраться таки получилось?
Заглохло?..
Не так давно пытался запустить эту микросхему... Нужна была высокая скорость обработки АЦП. Не взлетело на 2 микросхемах... т.е. иногда данные шли, но непонятно какие. Грешу на кварц (ставил 4 МГц), так как частота не соответсвует даташиту.
В итоге заказал китайский аналаг распаенный на платке. Если кому интересно, когда придет отпишусь как заработало. Тока напомните...
Если кому интересно, заработало со следующим кодом. ВАЖНО используется пин dataReadyPin, который DRDY
#include <SPI.h> const int dataReadyPin = 9; const int chipSelectPin = 10; #define pinCS chipSelectPin #define pinDR dataReadyPin //register selection //RS2 RS1 RS0 static const byte REG_CMM = 0x0; //communication register 8 bit static const byte REG_SETUP = 0x1; //setup register 8 bit static const byte REG_CLOCK = 0x2; //clock register 8 bit static const byte REG_DATA = 0x3; //data register 16 bit, contains conversion result static const byte REG_TEST = 0x4; //test register 8 bit, POR 0x0 static const byte REG_NOP = 0x5; //no operation static const byte REG_OFFSET = 0x6; //offset register 24 bit static const byte REG_GAIN = 0x7; // gain register 24 bit //channel selection for AD7706 (for AD7705 use the first two channel definitions) //CH1 CH0 static const byte CHN_AIN1 = 0x0; //AIN1; calibration register pair 0 static const byte CHN_AIN2 = 0x1; //AIN2; calibration register pair 1 static const byte CHN_COMM = 0x2; //common; calibration register pair 0 static const byte CHN_AIN3 = 0x3; //AIN3; calibration register pair 2 //output update rate //CLK FS1 FS0 static const byte UPDATE_RATE_20 = 0x0; // 20 Hz static const byte UPDATE_RATE_25 = 0x1; // 25 Hz static const byte UPDATE_RATE_100 = 0x2; // 100 Hz static const byte UPDATE_RATE_200 = 0x3; // 200 Hz static const byte UPDATE_RATE_50 = 0x4; // 50 Hz static const byte UPDATE_RATE_60 = 0x5; // 60 Hz static const byte UPDATE_RATE_250 = 0x6; // 250 Hz static const byte UPDATE_RATE_500 = 0x7; // 500 Hz //operating mode options //MD1 MD0 static const byte MODE_NORMAL = 0x0; //normal mode static const byte MODE_SELF_CAL = 0x1; //self-calibration static const byte MODE_ZERO_SCALE_CAL = 0x2; //zero-scale system calibration, POR 0x1F4000, set FSYNC high before calibration, FSYNC low after calibration static const byte MODE_FULL_SCALE_CAL = 0x3; //full-scale system calibration, POR 0x5761AB, set FSYNC high before calibration, FSYNC low after calibration //gain setting static const byte GAIN_1 = 0x0; static const byte GAIN_2 = 0x1; static const byte GAIN_4 = 0x2; static const byte GAIN_8 = 0x3; static const byte GAIN_16 = 0x4; static const byte GAIN_32 = 0x5; static const byte GAIN_64 = 0x6; static const byte GAIN_128 = 0x7; void setNextOperation(byte reg, byte channel, byte readWrite) { byte r = 0; r = reg << 4 | readWrite << 3 | channel; digitalWrite(pinCS, LOW); SPI.transfer(r); digitalWrite(pinCS, HIGH); } //Clock Register // 7 6 5 4 3 2 1 9 //ZERO(0) ZERO(0) ZERO(0) CLKDIS(0) CLKDIV(0) void writeClockRegister(byte CLKDIS, byte CLKDIV, byte outputUpdateRate) { byte r = CLKDIS << 4 | CLKDIV << 3 | outputUpdateRate; digitalWrite(pinCS, LOW); SPI.transfer(r); digitalWrite(pinCS, HIGH); } //Setup Register // 7 6 5 4 3 2 1 0 //MD10) MD0(0) G2(0) G1(0) G0(0) B/U(0) BUF(0) FSYNC(1) void writeSetupRegister(byte operationMode, byte gain, byte unipolar, byte buffered, byte fsync) { byte r = operationMode << 6 | gain << 3 | unipolar << 2 | buffered << 1 | fsync; digitalWrite(pinCS, LOW); SPI.transfer(r); digitalWrite(pinCS, HIGH); } unsigned int readDataRegister() { digitalWrite(pinCS, LOW); byte b1 = SPI.transfer(0xff); byte b2 = SPI.transfer(0xff); digitalWrite(pinCS, HIGH); unsigned int r = b1 << 8 | b2; return r; } void getReady() { while (digitalRead(pinDR)); } void init(byte channel, byte gain, byte updRate) { setNextOperation(REG_CLOCK, channel, 0); writeClockRegister(0, 1, updRate); setNextOperation(REG_SETUP, channel, 0); writeSetupRegister(MODE_SELF_CAL, gain, 0, 0, 1); setNextOperation(REG_SETUP, channel, 0); writeSetupRegister(MODE_SELF_CAL, gain, 0, 0, 0); getReady(); } unsigned int readADResult(byte channel) { setNextOperation(REG_DATA, channel, 1); getReady(); return readDataRegister(); } double readResult(byte channel) { double VRef = 2.5; return (readADResult(channel) - 32768) * 1.0 / 32768.0 * VRef; } void setup() { SPI.begin(); SPI.setDataMode(SPI_MODE3); SPI.setClockDivider(SPI_CLOCK_DIV2); Serial.begin(115200); Serial.println("start"); pinMode(chipSelectPin, OUTPUT); pinMode(dataReadyPin, INPUT); init(CHN_AIN1, GAIN_1, UPDATE_RATE_500); init(CHN_AIN2, GAIN_1, UPDATE_RATE_500); } void loop() { unsigned long currentMillis = millis(); Serial.print(readResult(CHN_AIN1)); Serial.print("\t"); Serial.print(readResult(CHN_AIN2)); Serial.print("\t"); Serial.println(millis() - currentMillis); }Сколько реально получилось выборок в сек. ? Там по даташиту вроде не менее 19,2кГц (38кГц. на усилении =1)
Для одного канала, сколько укажете в UPDATE_RATE столько и будет. Чем выше частота, тем больше плавает значение
на 500 кГц у меня сложилось впечатление что 3 - 4 разряда плавают
Если переключать каналы, то частота опроса падает в 3 раза на 1 канал (т.е. при переключении канала он автоматически берет первые 3 значения на фильтр и только потом выдает результат). Т.е. если при частоте 50 кГц один канал опрашивается за 20 мС, то 2 канала опрашиваютя за (20 * 3) * 2 = 120 мС
Автор, разобраться таки получилось?
Заглохло?..
5 лет прошло :).
Да, мне удалось запустить его (AD7705), уже и не помню как и что делал. Если надо, могу покапаться на диске и поискать скетчи.
Здравствуйте, товарищи!
Я тоже сейчас заморочился с внешним ацп AD7707. Сразу повылезали проблемы: 1. Длительная смена канала. 2. При смене канала - почему-то данные были устаревшие. Решил я эту проблему вот таким скетчем:
#include <OneWire.h> #include <SPI.h> OneWire ds(48); // Создаем объект OneWire для шины 1-Wire, с помощью которого будет осуществляться работа с датчиком const int dataReadyPin = 49; const int chipSelectPin = 53; double pH; double bR; double rX; unsigned int redox_mv; double pHcal1; double pHcal2; double pHcaltemp1; double pHcaltemp2; #define pinCS chipSelectPin #define pinDR dataReadyPin //register selection //RS2 RS1 RS0 static const byte REG_CMM = 0x0; //communication register 8 bit static const byte REG_SETUP = 0x1; //setup register 8 bit static const byte REG_CLOCK = 0x2; //clock register 8 bit static const byte REG_DATA = 0x3; //data register 16 bit, contains conversion result static const byte REG_TEST = 0x4; //test register 8 bit, POR 0x0 static const byte REG_NOP = 0x5; //no operation static const byte REG_OFFSET = 0x6; //offset register 24 bit static const byte REG_GAIN = 0x7; // gain register 24 bit //channel selection for AD7706 (for AD7705 use the first two channel definitions) //CH1 CH0 static const byte CHN_AIN1 = 0x0; //AIN1; calibration register pair 0 static const byte CHN_AIN2 = 0x1; //AIN2; calibration register pair 1 static const byte CHN_COMM = 0x2; //common; calibration register pair 0 static const byte CHN_AIN3 = 0x3; //AIN3; calibration register pair 2 //output update rate //CLK FS1 FS0 static const byte UPDATE_RATE_20 = 0x0; // 20 Hz static const byte UPDATE_RATE_25 = 0x1; // 25 Hz static const byte UPDATE_RATE_100 = 0x2; // 100 Hz static const byte UPDATE_RATE_200 = 0x3; // 200 Hz static const byte UPDATE_RATE_50 = 0x4; // 50 Hz static const byte UPDATE_RATE_60 = 0x5; // 60 Hz static const byte UPDATE_RATE_250 = 0x6; // 250 Hz static const byte UPDATE_RATE_500 = 0x7; // 500 Hz //operating mode options //MD1 MD0 static const byte MODE_NORMAL = 0x0; //normal mode static const byte MODE_SELF_CAL = 0x1; //self-calibration static const byte MODE_ZERO_SCALE_CAL = 0x2; //zero-scale system calibration, POR 0x1F4000, set FSYNC high before calibration, FSYNC low after calibration static const byte MODE_FULL_SCALE_CAL = 0x3; //full-scale system calibration, POR 0x5761AB, set FSYNC high before calibration, FSYNC low after calibration //gain setting static const byte GAIN_1 = 0x0; static const byte GAIN_2 = 0x1; static const byte GAIN_4 = 0x2; static const byte GAIN_8 = 0x3; static const byte GAIN_16 = 0x4; static const byte GAIN_32 = 0x5; static const byte GAIN_64 = 0x6; static const byte GAIN_128 = 0x7; void setNextOperation(byte reg, byte channel, byte readWrite) { byte r = 0; r = reg << 4 | readWrite << 3 | channel; digitalWrite(pinCS, LOW); SPI.transfer(r); digitalWrite(pinCS, HIGH); } //Clock Register // 7 6 5 4 3 2 1 9 //ZERO(0) ZERO(0) ZERO(0) CLKDIS(0) CLKDIV(0) void writeClockRegister(byte CLKDIS, byte CLKDIV, byte outputUpdateRate) { byte r = CLKDIS << 4 | CLKDIV << 3 | outputUpdateRate; digitalWrite(pinCS, LOW); SPI.transfer(r); digitalWrite(pinCS, HIGH); } //Setup Register // 7 6 5 4 3 2 1 0 //MD10) MD0(0) G2(0) G1(0) G0(0) B/U(0) BUF(0) FSYNC(1) void writeSetupRegister(byte operationMode, byte gain, byte unipolar, byte buffered, byte fsync) { byte r = operationMode << 6 | gain << 3 | unipolar << 2 | buffered << 1 | fsync; digitalWrite(pinCS, LOW); SPI.transfer(r); digitalWrite(pinCS, HIGH); } unsigned int readDataRegister() { digitalWrite(pinCS, LOW); byte b1 = SPI.transfer(0xff); byte b2 = SPI.transfer(0xff); digitalWrite(pinCS, HIGH); unsigned int r = b1 << 8 | b2; return r; } void getReady() { while (digitalRead(pinDR)== 0); } boolean DRDY_REDY() { boolean testresult = digitalRead(pinDR); while(digitalRead(pinDR) != 0){ testresult = digitalRead(pinDR); } return testresult; } void init(byte channel, byte gain, byte updRate) { setNextOperation(REG_CLOCK, channel, 0); writeClockRegister(0, 1, updRate); setNextOperation(REG_SETUP, channel, 0); writeSetupRegister(MODE_SELF_CAL, gain, 0, 0, 1); setNextOperation(REG_SETUP, channel, 0); writeSetupRegister(MODE_SELF_CAL, gain, 0, 0, 0); getReady(); } unsigned int readADResult(byte channel) { setNextOperation(REG_DATA, channel, 1); getReady(); return readDataRegister(); } double readResult(byte channel) { double VRef = 2.50000; return (readADResult(channel) - 32768)/ 32768.00000 * VRef; } byte spiTransfer(volatile byte data) { SPDR = data; while (!(SPSR & _BV(SPIF))); return SPDR; } void AD770Xreset() { digitalWrite(pinCS, LOW); for (int i = 0; i < 100; i++) spiTransfer(0xff); digitalWrite(pinCS, HIGH); } void setup() { SPI.begin(); SPI.setDataMode(SPI_MODE3); SPI.setClockDivider(SPI_CLOCK_DIV8); AD770Xreset(); Serial.begin(9600); Serial.println("start"); pinMode(chipSelectPin, OUTPUT); pinMode(dataReadyPin, INPUT); digitalWrite(dataReadyPin,HIGH); } float temperature = 20; void tempread(){ byte data[2]; ds.reset(); ds.write(0xCC); ds.write(0x44); ds.reset(); ds.write(0xCC); ds.write(0xBE); data[0] = ds.read(); data[1] = ds.read(); temperature = ((data[1] << 8) | data[0]) * 0.0625; } void spi_ch1_read(){ init(CHN_AIN1, GAIN_1, UPDATE_RATE_20); DRDY_REDY(); if(DRDY_REDY() == 0){ bR = 1000*readResult(CHN_AIN1)-201.46; } } void spi_ch2_read(){ init(CHN_AIN2, GAIN_1, UPDATE_RATE_20); DRDY_REDY(); if(DRDY_REDY() == 0){ pH = ((readResult(CHN_AIN2)*1000) -(temperature-10.2)*0.8165 + 18.456)*-0.02 + 7.1052; }} void spi_ch3_read(){ init(CHN_AIN2, GAIN_1, UPDATE_RATE_20); DRDY_REDY(); if(DRDY_REDY() == 0){ redox_mv = round(readResult(CHN_AIN3)*1000 + 224) ; } } void loop() { delay(10000); spi_ch1_read(); Serial.println("bR"); Serial.println(bR,4); spi_ch2_read(); Serial.println("pH"); Serial.println(pH,3); spi_ch3_read(); Serial.println("Redox:"); Serial.println(redox_mv); tempread(); Serial.println("temp"); Serial.println(temperature,1); }Добавил функцию
void getReady() { while (digitalRead(pinDR)== 0); } boolean DRDY_REDY() { boolean testresult = digitalRead(pinDR); while(digitalRead(pinDR) != 0){ testresult = digitalRead(pinDR); }И ура - все заработало без всяких millis и delay. Правда на одно измерение мне надо реально 300-450мс. Измерения очень стабильные.
Вопрос - можно ли как нибудь оптимизировать время чтения по SPI при выборе канала? Я сделал так, что в момент когда DRDY == LOW происходит считывание. В остальное время система просто ждет в цикле while. Правда если у меня в программе прилетит прерывание вероятно может это все испортить. Это я проверю чуть позже. Может есть какой нибудь более оптимальный вариант кода ?
По-моему тут не время чтения по SPI нужно оптимизировать, а процедуру ожидания готовности.
Вопрос только в том - есть ли куда тратить сэкономленные такты?
Конечно есть. У меня же не только эта программа исполняется на МК. Мне кажется очень глупо тратить по 500мс на одно измерение. Схема то используется в промышленности - я не думаю, что там по пол секунды тратят
Кто ж знает, где она у вас используется. В том случае, если хотите получить свои 500мс в рабочий цикл - запускайте конверсию, выставляйте флаг "работют датчики" и проверяйте каждый луп (или по таймеру) состояние INT-входа. Как только он упал - инициируйте процедуру считывания результата.
В целом так. Применимо к приведенному коду - я бы его переделал на какой-нить конечный автомат.
спасибо конечно, с таймерам я само собой сделал. опрос датчиков мне пока нужен раз в 8 секунд, и как под конечный автомат сделать я знаю. я интересуюсь как можно оптимизировать время выбора канала. Вот это интересно - неужели она такая медленная ?
Как я вижу - после команды инита канала еще определенное время мотается while(). Т.е. опять же - инит пустили, мигаете светодиодом, щелкаете релюшками, проверяете не выставился ли INT. Выставился - начинаете читать инициализированный канал. Вот и вся оптимизация.
На даташит глянул - там написано, что DRDY появляется, если есть новые данные. Скорее всего сразу после вместе с инитом запускается долгая конверсия/калибровка. Решение то же - пуск -> свои дела, проверка INT -> работа с каналом по обнаружению INT.
Призадумаешься, использовалить ли этот аналог девайс.
Интересный опыт https://digibird1.wordpress.com/arduino-as-a-5m-sample-osciloscope/
На базе внешнего AЦП на базе микрочипа CA3306.
5.000.000 выборок в секунду (5Mhz)!