[РЕШЕНО] АЦП AD7714 по SPI

Тема в разделе "Схемотехника, компоненты, модули", создана пользователем Daniil, 1 ноя 2014.

  1. Daniil

    Daniil Гуру

    Здравствуйте!​
    Пытаюсь подружиться с внешним АЦП AD7714 через SPI.
    Схему как я его подключил к ардуино на рисунке.(цифры в зелёных полях, это пины на отладочной плате Arduino)
    Снимок.PNG Надо добавить, что выводы AIN2, AIN4 и AIN6 соединены с землёй, а выводы AIN1, AIN3 и AIN5 к среднему выводу потенциометра(делитель напряжения) (т.е. каждый канал должен показывать одно и тоже). Максимум +5 В, минимум 0 В. Кварц на 2,4576 МГц с кондёрами 100 нФ.
    К письму приложил даташит этого АЦП, подскажите, что делают выводы POL, BUFFER, REF IN(+) и REF IN (-)?

    В даташите есть пример алгоритма, как работать с этим АЦП.
    Всего 8 регистров:
    Имеются 5 регистров, по 8 бит.
    1. Communication Register - настройка следующей операции
    2. Mode Register - настройка режима работы
    3. Filter High Register - настройка скорости работы АЦП, разрядности
    4. Filter Low Register - продолжение старшего регистра
    5. Test Register - из даташита понял, что он нужен чтобы с ним играться, но его менять нельзя
    Один регистр от 16 до 24 бит.
    6. Data Register - регистр, в который записываются отцифрованные данные
    Два регистра по 24 бит.
    7 и 8. Calibration Registers - как я понял регистр с калибровочными данными.

    Работа с АЦП начинается через Communication Register (CR). Посылаем ему байт:
    0х27 = 0010 01111
    1ый бит должен быть нулём всегда
    2-4 биты выбор регистра в который будет осуществляться запись или из которого будем читать данные.(010 - Filter High Register)
    5ый бит если "0", то записываем данные, если 1, то читаем.
    6-8 биты это выбор аналогового канала, с которым будет выполняться считывание данных(или настройка)(т.к. сейчас с каналами не работаем).

    Следующая посылаемая команда 0х4F = 0100 0111, она записывается в Filter High Register.
    1ый бит "0" - однополярный режим.(для чего это?)
    2ой бит "1" - включает 24 разрядность(можно 16)
    3ий бит "0" он должен быть, если тактируется на частоте 2,4576 МГц
    4ый бит "0" тактирование от кварца
    5-8 биты понадобятся когда будем работать с Filter Low Register.

    Командой "0x37" обращяемся к Filter Low Register и записываем в него то, что придет следующей командой "0xA0" = 1010 0000.
    Если соединить 5-8 биты Filter High Register со всеми битами Filter Low Register, то получим
    0111 1010 0000 переведем в DEC и получим 4000, это делитель, он задаёт скорость работы АЦП.

    Далее командой "0х17" обращяемся к Mode Register и записываем в него "0x20", т.е. 0010 0000 это запуск самокалибровки и после неё АЦП начнёт свою работу.

    Вот код, который написан в Arduino IDE для UNO, ATmega 328p.

    В подпрограмме setup я конфигурирую вывод 2, UART, SPI и АЦП.
    В цикле я проверяю наличие нуля на выводе 2, т.е. жду данные, а когда дождусь, то запрашиваю данные и посылаю их на экран мониторинга com-порта.

    Код (Text):
    #include <SoftwareSerial.h> //для передачи данных на ПК
    #include <SPI.h>            //для передачи данных между arduino и ad7714

    #define DRDYpin 2

    byte bt; //бит данных

    void setup() {      
      pinMode(DRDYpin, INPUT);
      Serial.begin(9600);
      SPI.setClockDivider(SPI_CLOCK_DIV128);
      SPI.begin();
      SPI.transfer(0x27);  // обращение к регистру High Filter
      SPI.transfer(0x4F);  // Настройка его
      SPI.transfer(0x37);  // обращение к регистру Low Filter
      SPI.transfer(0xA0);  // настройка его
      SPI.transfer(0x17);  // обращение к регистру Mode Register
      SPI.transfer(0x20);  // настройка его
    }

    void loop() {
      if (digitalRead(DRDYpin)==0)   // если данные готовы, то на выводе DRDY должен
      {                              // быть ноль
        bt=SPI.transfer(0x5F);       // чтение из регистра данных
        Serial.println(bt,BIN);      // вывод на ПК
      }
    }
     
    На экране мониторинга COM-порта происходит не совсем понятное:
    добавил log.txt.
    Иногда(очень часто) на выводе DRDY "1", т.е. АЦП не готово, а если приходят данные, то я не могу их идентифицировать.

    Где может сидеть моя ошибка? Меня очень сильно удивляет то, что она так долго отцифровывает, вроде бы по расчётам отцифровка занимает менее 50 мс, а данные не повляются минутами, а если появляются, то очень странные.
     

    Вложения:

    • AD7714.pdf
      Размер файла:
      292,2 КБ
      Просмотров:
      986
    • log.txt
      Размер файла:
      3,7 КБ
      Просмотров:
      816
    Последнее редактирование: 3 ноя 2014
    Voha888 нравится это.
  2. Daniil

    Daniil Гуру

    Прошу, расскажите или укажите какие должны быть требования к кварцу и емкостям? Я заметил, что когда его задеваю, то данные начинают приходить.
     
  3. acos

    acos Официальный гик Администратор

  4. Unixon

    Unixon Оракул Модератор

    REFIN задают шкалу выходных напряжений, при этом REF IN (-) всегда AGND, а REF IN (+) это 1.25V или 2.5V в зависимости от напряжения питания;

    BUFFER управляет входным сопротивлением АЦП, в "1" оно больше;

    POL определяет по какому фронту - переднему или заднему - сигнала CLK будут передаваться данные.
     
  5. Daniil

    Daniil Гуру

    Спасибо, за разъяснения.
    Про кварц я нашёл в даташите к кварцу. Т.к. в даташите АТмеги328р написаны про частоты выше, чем 2457,6 кГц.

    Заметил, что иногда (вчера вечером) вывод AGND, земля аналоговой цепи, выставляет на своём выводе 5 В.

    Правильно ли я делаю прием данных по SPI, которые больше чем 8 бит?

    Код (Text):
    #include "test_SPI_ADC_main.h"
    #include <SoftwareSerial.h> // для передачи данных на ПК
    #include <SPI.h>            // для передачи данных между arduino и ad7714
    #define DRDY_pin 2
    //#define MOSI_pin 11
    //#define MISO_pin 12
    //#define SCK_pin  13
    #define SS  10

    unsigned long int bt; // 32 бит, без знака от 0 до 4 294 968 295 (так как на нужно 24 и более)
    byte k=51;              // количество строчек, которые поммещяются на экране

    void setup() {        
      pinMode(DRDY_pin , INPUT);  // 2ой пин - ввод
      pinMode(SS      , OUTPUT); // 10ый пин - вывод
      digitalWrite(SS  , LOW);    // низкий уровень на 10ый пин, сразу выбор АЦП
      SPI.begin();                            // Начало работы с SPI
      SPI.setClockDivider(SPI_CLOCK_DIV128); // делитель тактового сигнала
      SPI.setBitOrder(MSBFIRST);            // левый бит - первый бит LSBFIRST
      SPI.setDataMode(SPI_MODE3);            // полярность приёма
      Serial.begin(9600);                    // инициализация СОМ-порта
      delay(100);
    }

    // процедура конфигурации АЦП и вывода на экран "разметки таблицы"
    void my_init() {
    /* 0 010 0 000 = 0x20
    *  Filter High Register
    *      Запись
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); SPI.transfer(0b00100000);
    /* 0 1 0 1 1111 = 0x4f
    * Биполярный режим
    *  24 бит
    *      Ток Burnout on
    *            Для 4000 */
      while (digitalRead(DRDY_pin)!=0); SPI.transfer(0b01011111);
    /* 0 011 0 000 = 0x30
    *  Filter Low Register
    *      Запись
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); SPI.transfer(0b00110000);
    /* 1010 0000 = 0xA0
    * Для 4000 */
      while (digitalRead(DRDY_pin)!=0); SPI.transfer(0b10100000);
    /* 0 001 0 000 = 0x10
    *  Mode Register
    *      Запись
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); SPI.transfer(0b01110000);
    /* 001 000 0 0 = 0x20
    * Самокалибровка
    *    Усиление 1
    *        Отключение Burnout Токов
    *          Отключена синхронизация */
      while (digitalRead(DRDY_pin)!=0); SPI.transfer(0b00100000);


      Serial.print("HFR");    Serial.print("\t"); // Filter High Register
      Serial.print("LFR");    Serial.print("\t"); // Filter Low Register
      Serial.print("TR");    Serial.print("\t"); // Test Register
      Serial.print("ZSCR");    Serial.print("\t"); // Zero-Scale Calibration Register
      Serial.print("FSCR");    Serial.print("\t"); // Full-Scale Calibration Register
      Serial.print("DR");  Serial.print("\t"); // Data Register
      Serial.print("ArADC");Serial.print("\n"); // Arduino ADC
    }

    // главный цикл
    void loop() {
    // каждые 50 строк выводим разметку таблицу и конфигурируем АЦП
      if (k>50)
      {    delay(10);
        my_init();
        k=0;    }    k++;
    /* 0 010 1 000 = 0x28
    *  Filter High Register
    *      Чтение
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); bt=SPI.transfer(0b00101000);  Serial.print(bt,HEX); Serial.print("\t");
    /* 0 011 1 000 = 0x38
    *  Filter Low Register
    *      Чтение
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); bt=SPI.transfer(0b00111000);  Serial.print(bt,HEX); Serial.print("\t");
    /* 0 100 1 000 = 0x48
    *  Test Register
    *      Чтение
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); bt=SPI.transfer(0b01001000);  Serial.print(bt,HEX); Serial.print("\t");
    /* 0 110 1 000 = 0x68
    *  Zero-Scale Calibration Register
    *      Чтение
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); bt=SPI.transfer(0b01101000);  Serial.print(bt,HEX); Serial.print("\t");
    /* 0 111 1 000 = 0x78
    *  Full-Scale Calibration Register
    *      Чтение
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); bt=SPI.transfer(0b01111000);  Serial.print(bt,HEX); Serial.print("\t");
    /* 0 101 1 000 = 0x58
    *  Data Register
    *      Чтение
    *        AIN1 - AIN6 */
      while (digitalRead(DRDY_pin)!=0); bt=SPI.transfer(0b01011000);  Serial.print(bt,HEX); Serial.print("\t");
    // данные с АЦП Ардуино
                                                          Serial.print(analogRead(A5),HEX); Serial.print("\n");
      delay(100);
    }
     
    Результат я вижу такой
    Снимок.PNG
    Хотя в каждый момент времени хочу видеть одни и те же значения регистров(Я ручку потенциометра не кручу).
    Схема (только я не нашёл 24 пиновый корпус).
    2 резистора = делитель, чтобы 2,5 В подать.
    Потенциометр, чтобы хоть что то измерять, диод - для удобства восприятия.
    Снимок.PNG
    Если долго принимать данные, то после выдергивания +5 В из Ардуины, т.е. отключение Breadboard, то светодиод горит) если отключить AGND, то потухнет))
    P41103-141748.jpg
    Отключаю питание (рыжий провод(слева на фото) от pin 5V, к "+" breadborad)
    P41103-141808.jpg
    Отключаю AGND (синий провод, справа на фото)
    P41103-141820.jpg
    подключаю питание (рыжий, слева на фото)
    На breadboard не кучкую провода, т.к. когда их на одной линии больше двух, то они начинают толкаться, а breadboard странная штука, не всегда контачит.
     
    Последнее редактирование: 3 ноя 2014
    Voha888 нравится это.
  6. Daniil

    Daniil Гуру

    Прошу прощения, напишу для тех кто сталкнётся.

    В самом начале кода есть строчки в блоке setup:
    Код (Text):
    ...
    pinMode(SS      , OUTPUT); // 10ый пин - вывод
      digitalWrite(SS  , LOW);    // низкий уровень на 10ый пин, сразу выбор АЦП
    ...
    Когда я замучился и не знал куда копать, то начал тыкать во все ноги AD7714 проводом подключённый к внутреннему АЦП Arduino. И обнаружил, что SS, т.е. Slave Select линия, имеет уровень логической единицы, т.е. AD7714 не выбран как Slave, т.е. SPI не работает.

    Почему без питания диод горит не разобрался ещё.
    так же обнаружил, что строка:
    Код (Text):
    ...
    while (digitalRead(DRDY_pin)!=0); bt=SPI.transfer(0b00111000);  Serial.print(bt,HEX); Serial.print("\t");
    ...
    выдаёт на экран через COM-порт "111000", что не то, что нужно. Это команда для AD7714 выдать содержимое Filter Low Register. т.е. bt=SPI.transfer(0b00111000); выдаёт аргумент на выход. Буду дальше разбираться.
     
    Voha888 нравится это.
  7. Daniil

    Daniil Гуру

    Код (Text):
    bt=SPI.transfer(0b00111000);
    Данная команда передаёт к Slave биты (провод MOSI) и одновременно принимает по MISO. Т.к. для того, чтобы данный АЦП отработал команду, ему нужно, чтобы для начала пришла вся команда, а потом он будет отдавать информацию.
    Для этого я сделал так:
    Код (Text):
    byte my_transfer(byte b)
    { byte btl;
    while (digitalRead(DRDY_pin)!=0);
    btl=SPI.transfer(b);
    return btl;
    }
    ...
    my_transfer(B00111111); bt=my_transfer(0xFF);  ...
    Т.е. после команды на чтение посылаю команду "болванку"(у неё первый бит "1", АЦП такие команды игнорирует), тактирование происходит и я вынимаю, то что он подготовил для отдачи.
    Для съёма данных более одного байта, повторяю эту операцию столько сколько байтов нужно достать.

    Вопрос.
    Является критичным то, что цифровая земля и аналоговая земля это один и тот же пин ардуины (и для питания цифровой и аналоговой частей)?

    Пока не очень понятно как сделать 5 аналоговых входов. Т.к. пока чтобы я ни подавал с потенциометра, то приходит шум. (предпоследний столбец "DR")
    Снимок.PNG
     
    Voha888 нравится это.
  8. Daniil

    Daniil Гуру

    Первые данные сняты успешно!=)
    В чём то разобрался, в чём то разберусь, если есть вопросы пишите в личку.
    Предстоит разбор калибровок и таймингов.
    Было интересно воевать.
    Только в конце разбора я решил вбить в гугл "ad7714 рус" и нашёл статью 1999 года.
    http://www.kit-e.ru/articles/dac/1999_1_38.php
     
    acos нравится это.
  9. Daniil

    Daniil Гуру

    Здравствуйте.
    Мне начали задавать вопросы. Я решил, что будет лучше если я выложу и поясню работающий код.
    К сожалению мой код сейчас напичкан дополнительными функциями, поэтому тут приведу вырезанный кусок работы с АЦП.
    Процедура my_transfer отличается от процедуры SPI.transfer только тем, что ждёт появление логического нуля на выводе DRDY АЦП. Конечно тип для неё выбран избыточный, но всё же пока отладка я его не менял.
    Процедура my_init хорошо описал в комментариях. Обратите внимание, что я выбрал коэффициент деления 20, т.к. при 19 у меня ничего не работало, хотя по даташиту можно использовать 19. (Выходные потрачены на поиск этой ошибки)
    В процедуре Setup обратите внимание, что я SS_pin обнуляю сразу, т.е. у меня только АЦП выбрано и выбрано всегда активным. Так же грабли были в том, что я обнулял перед SPI.begin, выяснил, что после выполнения SPI.begin() вывод 10 (SS_pin) автоматически становится логической "1".
    SPI_mode3 выбран потому, что на вывод POL_pin подаётся логическая "1".
    Задержки до и после вызова my_init обусловлены тем, что иногда не выполнялась эта процедура, пока предположение то, что я писал всё в одну строку (я пишу программу в MariaMole, то ли баг, то ли фича) иногда если несколько команд в одной строке, то они не выполняются.
    fast_not_main_main_loop() - процедура приёма данных с псевдо-дифференциального канала AIN1-AIN6 и нормировка на 5000 мВ.
    Вывод REF(+) подключён к 2,5 В. Через делитель из 2ух резисторов.
    Вывод REF(-) подключён к земле.
    Вывод AIN6 подключён к 2,5 В. Через делитель из 2ух резисторов.
    Коэффициент усиления по-умолчанию равен 1. (K_yc = 1)
    Режим измерений биполярный.

    В таком случае диапазон измерений находится в интервале напряжений от AIN6 - (REF(+)-REF(-))/K_yc до AIN6 + (REF(+)-REF(-))/K_yc, подставляя цифры получается от 0 В до 5 В.

    Если режим однополярный то диапазон будет от AIN6 до AIN6 + (REF(+)-REF(-))/K_yc, т.е. от 2,5 В до 5 В.

    Такие REF(+) и REF(-) выбраны потому что даташит рекомендует чтобы их разность была 2,5 В. Мне было удобно это реализовать, т.к. делитель напряжений один, а пригодился 2 раза.

    Код:
    Код (Text):
    #include "test_SPI_ADC_main.h"
    #include <SoftwareSerial.h> // для передачи данных на ПК
    #include <SPI.h>            // для передачи данных между arduino и ad7714

    #define DRDY_pin 2
    #define BUFFER_pin 6
    #define STANDBY_pin 7
    #define RESET_pin 8
    #define POL_pin 9
    #define SS_pin 10
    #define MOSI_pin 11
    #define MISO_pin 12
    #define SCK_pin 13

    int spi_mode=SPI_MODE3;
    int bit_FIRST=MSBFIRST;
    int spi_clk_div=SPI_CLOCK_DIV2;
    unsigned long int bt; // 32 бит, без знака от 0 до 4 294 968 295 (так как нам нужно 24)

    unsigned long int my_transfer(byte b)
    { unsigned long int btl;
    while (digitalRead(DRDY_pin)!=0);
    btl=SPI.transfer(b);
    return btl;
    }

    // процедура конфигурации АЦП
    void my_init() {
    my_transfer(B00100111);
    /* 0 010 0 000
    *  Filter High Register
    *      Запись
    *        AIN1 - AIN6 */
    my_transfer(B01100000);
    /* 0 1 1 0 0000
    * Биполярный режим
    *  24 бит
    *    Ток Boost on т.к. работаем от кварца на 2,4576 МГц
    *      От кварца
    *          Для 20 */
    my_transfer(B00110000);
    /* 0 011 0 000
    *  Filter Low Register
    *      Запись
    *          AIN1 - AIN6 */
    my_transfer(B00010100);
    /* 0001 0100
    * Для 20 */
    }

    void setup() {          
      pinMode(DRDY_pin, INPUT);        // 2ой пин - ввод
      pinMode(BUFFER_pin, OUTPUT);      // 6ой пин - вывод
      digitalWrite(BUFFER_pin, LOW);    // Низкий уровень на 6ой пин, чтобы в обычный режим
      pinMode(STANDBY_pin, OUTPUT);    // 7ой пин - вывод
      digitalWrite(STANDBY_pin, HIGH);  // высокий уровень на 7ой пин, чтобы в обычный режим
      pinMode(RESET_pin, OUTPUT);      // 8ой пин - вывод
      digitalWrite(RESET_pin, HIGH);    // высокий уровень на 8ой пин, чтобы не было сброса
      pinMode(POL_pin, OUTPUT);        // 9ый пин - вывод
      digitalWrite(POL_pin, HIGH);      // высокий уровень на 9ой пин, выбор полярности
      pinMode(SS_pin, OUTPUT);          // 10ый пин - вывод
      SPI.begin();                        // Начало работы с SPI
      SPI.setClockDivider(spi_clk_div); // делитель тактового сигнала
      SPI.setBitOrder(bit_FIRST);        // левый бит - первый бит LSBFIRST
      SPI.setDataMode(spi_mode);        // полярность приёма
      digitalWrite(SS_pin, LOW);        // низкий уровень на 10ый пин, сразу выбор АЦП
      Serial.begin(115200);                // инициализация СОМ-порта
      delay(100);
      my_init();
      delay(100);
    }

    void fast_not_main_main_loop() {
    int bt1;
    int bt2;
    int bt3;
    my_transfer(B01011000);
    bt1=my_transfer(0xFF);
    bt1=bt1<<16;
    bt2=my_transfer(0xFF);
    bt2=bt2<<8;
    bt3=my_transfer(0xFF);
    bt=bt1 | bt2 | bt3;
    Serial.print(float(bt) * 50000 / 16777215,DEC); Serial.print("\t");
    }

    // главный цикл
    void loop() {
    if (digitalRead(STANDBY_pin) && digitalRead(RESET_pin) && !digitalRead(SS_pin)) { fast_not_main_main_loop(); }
    }
    Если есть вопросы, то спрашивайте. Надеюсь смогу помочь.
     
    Voha888 и acos нравится это.
  10. Vyacheslav.07

    Vyacheslav.07 Нерд

    Daniil , pls , не могли бы Вы помочь чайнику с сопряжением AD7714 и Atmega 128 по SPI ? Хотелось бы данные с ADC закидывать в переменную , 16 разрядов вполне достаточно , фильтр на 50 гц , а дальше эту переменную выведу сам на LCD . Спасибо за любой ответ .
     
  11. Daniil

    Daniil Гуру

    Атмега 128? Т.е. не ардуина? Т.е ассемблер нужен?
     
  12. Vyacheslav.07

    Vyacheslav.07 Нерд

    Daniil , спасибо за ответ , я пользуюсь mikroBoard for AVR 64-pin , доделал ее путем добавки 2.5 V ADR381 источника опорного напяжения для ADC контроллера . Собрал схему омметра на инструментальном OP ,
    все классно работает , но , к сожалению , 10 разрядный ADC Atmega 128 не дает достаточного разрешения .
    Пользуюсь как ASM , так и С , но в последнее время склоняюсь все же к С на Сode Vision . Проблемка сейчас найти библиотеку для AD7714 .
     
  13. Daniil

    Daniil Гуру

    К сожалению код ни на С, ни на Асме дать не могу, я так глубоко не влезал (я написал простейшую программу, а все алгоритмические проблемы решаю на ПК), но выше приведён код с использованием библиотек arduino IDE. Его пытались разобрать? Там есть неточность, но она сейчас не фатальная. Может пояснить, что я там написал? Может на псевдокоде написать?
     
  14. Vyacheslav.07

    Vyacheslav.07 Нерд

    Дени , не проблема , просмотрел Ваш код , все очень понятно , и кстати , рационально .
    Жду кварц на 2,4576 Mhz , затем отпишусь . Спасибо за внимание .
     
  15. Daniil

    Daniil Гуру

    Хорошо. Тогда сразу напишу, что если важна скорость, тогда частые запросы к чтению с аналоговых каналов надо разделять задержками.
    Я по формулам из даташита рассчитал себе задержку 1,5 мс если канал не меняется и 4,5 мс если обращаюсь к другому каналу. Жду хороших новостей!
     
  16. Vyacheslav.07

    Vyacheslav.07 Нерд

    Привет Дени !
    Вот простенькая программка вольтметра на CV AVR . Пока только на 16-ти битном режиме , доделаю входные цепи как положено ( экранировка , заземления и т.д.) , выложу на 24-ре бита .

    #include <mega128.h>
    // Alphanumeric LCD Module functions
    #asm
    .equ __lcd_port=0x1B ;PORTA
    #endasm
    #include <lcd.h>
    #include <spi.h>
    #include <stdio.h>
    #include <delay.h>
    float result;
    // LCD display buffer
    char lcd_buffer[33];
    unsigned read_adc(void)
    {
    unsigned int bt;
    unsigned int bt1;
    unsigned int bt2;
    if(!PINB.4) // ждем низкого уровня на DRDY (готовность АЦП к выводу данных)
    {
    spi(0x58); // запрос к AIN1/AIN6 каналам для вывода данных
    bt1=spi(0xFF); // посылка импульсов синхронизации и прием старшего байта
    bt1=bt1<<8; // двигаем старший байт влево
    bt2=spi(0xFF); // посылка импульсов синхронизации и прием младшего байта
    bt=bt1|bt2; // получение двух байтов данных с АЦП
    return;
    };
    }


    void main(void)
    {

    // Port B initialization
    // Func7=In Func6=In Func5=Out Func4=In Func3=In Func2=Out Func1=Out Func0=Out
    // State7=T State6=T State5=1 State4=P State3=P State2=0 State1=0 State0=1
    PORTB=0x33;
    DDRB=0x27;
    // SPI initialization
    // SPI Type: Master
    // SPI Clock Rate: 78,125 kHz
    // SPI Clock Phase: Cycle Start
    // SPI Clock Polarity: High
    // SPI Data Order: MSB First
    SPCR=0x5F;
    SPSR=0x00;
    // инициализация АЦП

    spi(0x20); // подготовка Communication Register к записи в High Filter Register выбраных каналов АЦП (AIN1/AIN6)
    spi(0x81); // запись установок в High Filter (unipolar=1,16 bit word,BST=0,CLKDIS=0,F8=1 в High Register Bits - Code 312 для 25гц )
    spi(0x30); // подготовка Communication Register к записи в Low Filter выбранных каналов АЦП (AIN1/AIN6)
    spi(0x38); // запись Сode 312 для 25 гц в Low Filter Register
    spi(0x10); // подготовка к записи в Mode Register его установок и выбор каналов АЦП (AIN1/AIN6)
    spi(0x20); // запись установок в Mode Register (self calibration on , gain=1,BO=0,FSYNC=0)

    // LCD module initialization
    lcd_init(33);
    while (1)
    {
    result=(5.00*read_adc())/65535; // собственно , формула полученного результата в вольтах (VREF=2.5VDC)
    sprintf(lcd_buffer,"Uadc=%.3fV",result); // вывод результата с тремя знаками после запятой (плавающая точка)
    lcd_clear();
    lcd_puts(lcd_buffer);
    delay_ms(500);
    };
    }
     
    Daniil нравится это.
  17. Vyacheslav.07

    Vyacheslav.07 Нерд

    Дени , вот программка на 24-х битовый режим , четыре нуля после запятой стоят себе как вкопанные при нормальном экранировании и соблюдения соответствубщих правил монтажа , ну и выбора компонентов , таких , например , как источник опорного ( у меня REF43 на 2.5 VDC - он позволяет делать небольшой трим +/- 98mV).
    C этого АЦП можно выжать и больше , но я не заморачивался . Что не доделал , это программный сброс АЦП путем подачи 32-х SCK импульсов при единице на DIN входе , но , это как показала практика , вроде и не совсем нужно .
     
  18. Vyacheslav.07

    Vyacheslav.07 Нерд

    #include <mega128.h>
    // Alphanumeric LCD Module functions
    #asm
    .equ __lcd_port=0x1B ;PORTA
    #endasm
    #include <lcd.h>
    #include <spi.h>
    #include <delay.h>
    #include <stdio.h>
    #define f32 float;
    // LCD display buffer
    char lcd_buffer[33];

    f32 float result;
    // Declare your global variables here
    unsigned long int read_adc(void)
    {
    unsigned long int bt;
    unsigned long int bt1;
    unsigned int bt2;
    unsigned int bt3;
    if(!PINB.4) // ждем низкого уровня на DRDY (готовность АЦП к выводу данных)
    {
    spi(0x58); // запрос к AIN1/AIN6 каналам для вывода данных
    bt1=spi(0xFF); // посылка импульсов синхронизации и прием старшего байта
    bt1=bt1<<16; // двигаем старший байт влево на два байта
    bt2=spi(0xFF); // посылка импульсов синхронизации и прием среднего байта
    bt2=bt2<<8; // двигаем средний байт влево на один байт

    bt3=spi(0xFF); // посылка импульсов синхронизации и прием младшего байта
    bt=bt1|bt2|bt3; // собираем три байта данных с АЦП
    return ;
    };
    }

    void main(void)
    {
    // Port B initialization
    // Func7=In Func6=In Func5=Out Func4=In Func3=In Func2=Out Func1=Out Func0=Out
    // State7=T State6=T State5=1 State4=P State3=P State2=0 State1=0 State0=1
    PORTB=0x39;
    DDRB=0x27;
    // SPI initialization
    // SPI Type: Master
    // SPI Clock Rate: 78,125 kHz
    // SPI Clock Phase: Cycle Start
    // SPI Clock Polarity: High
    // SPI Data Order: MSB First
    SPCR=0x5F;
    SPSR=0x00;
    // инициализация АЦП

    spi(0x20); // подготовка Communication Register к записи в High Filter Register и выбор каналов АЦП (AIN1/AIN6)

    spi(0x41); // запись установок в High Filter Register(bipolar,24bit word,BST=0,CLKDIS=0,FS11=0,FS10=0,FS9=0,F8=1 в High Register Bits - Code 312 для 25гц )

    spi(0x30); // подготовка Communication Register к записи в Low Filter кода фильтра для выбранных каналов АЦП (AIN1/AIN6)

    spi(0x38); // запись Сode 312( 0b00111000 ) для 25 гц в Low Filter Register
    spi(0x10); // подготовка к записи в Mode Register его установок для выбранных каналов АЦП (AIN1/AIN6)
    spi(0x20); // запись установок в Mode Register (self calibration , gain=1,BO=0,FSYNC=0)

    // LCD module initialization
    lcd_init(16);
    while (1)
    {

    result=(5.00*read_adc())/16777215; // собственно , формула полученного результата в вольтах (VREF=2.5VDC)
    sprintf(lcd_buffer,"Uadc = %.4f V",result); // вывод результата с четырмя знаками после запятой (плавающая точка)
    lcd_clear();
    lcd_puts(lcd_buffer);
    delay_ms(500);
    };
    }
     
    Последнее редактирование: 16 мар 2015
  19. Daniil

    Daniil Гуру

    Сейчас у вас данные считываются с предыдущего шага, сейчас это не критично, но после spi(0x58) в read_adc я рекомендую поставить задержку в соответсвии с выбранной частотой в регистрах фильтра. Когда (и если) вы перейдете к чтению данных с разных каналов, то это приведет к ужасным проблемам. Будете ждать ответ с одного канала, а получать будете с другого. Кстати задержка изменения канала отличается от задержки измерения с одного и того же канала.
    У вас хороший результат. Поздравляю!
     
  20. Vyacheslav.07

    Vyacheslav.07 Нерд

    Спасибо , Данил . Вы были правы насчет задержки , она должна быть даже с одним каналом , иначе спад импульса DRDY иногда цепляет начало выборки данных АЦП . Если можно , поделитесь , как его просчитать в зависимости от частоты фильтра . Я немного не догнал из PDF , время фильтра должно быть не менее в четыре раза выше периода частоты фильтра , так ли это ? Ниже еще один вариант программки c выводом на терминал через USART , может , кому пригодится .


    #include <mega128.h>
    #ifndef RXB8
    #define RXB8 1
    #endif
    #ifndef TXB8
    #define TXB8 0
    #endif
    #ifndef UPE
    #define UPE 2
    #endif
    #ifndef DOR
    #define DOR 3
    #endif
    #ifndef FE
    #define FE 4
    #endif
    #ifndef UDRE
    #define UDRE 5
    #endif
    #ifndef RXC
    #define RXC 7
    #endif
    #define FRAMING_ERROR (1<<FE)
    #define PARITY_ERROR (1<<UPE)
    #define DATA_OVERRUN (1<<DOR)
    #define DATA_REGISTER_EMPTY (1<<UDRE)
    #define RX_COMPLETE (1<<RXC)
    // Standard Input/Output functions
    #include <stdio.h>
    #define _ALTERNATE_PUTCHAR_ /* ВАЖНО ! если не указать _ALTERNATE_PUTCHAR_ , то компилятор засунет
    USART0 по дефолту , а мне нужен USART1 */
    #include <delay.h>
    #define f32 float;
    #include <string.h>
    #define SS_pin PORTB.0
    // Write a character to the USART1 Transmitter
    #pragma used+
    void putchar(char c)
    {
    while ((UCSR1A & DATA_REGISTER_EMPTY)==0);
    UDR1=c;
    }
    #pragma used-
    // SPI functions
    #include <spi.h>
    f32 float result;
    unsigned long int read_adc(void)
    {
    unsigned long bt;

    delay_ms(1000); // периодичность вывода на терминал
    SS_pin=0; // активируем АЦП

    if(!PINB.4) // ждем низкого уровня на DRDY (готовность АЦП к выводу данных)
    delay_ms(5); /* отстройка от постоянной времени фильтра (без нее даже и с одним каналом вылетает
    периодическая ошибка), считать было лень , поставил от фонаря */
    {

    spi(0x5E); // запрос к AIN5/AIN6 каналам для вывода данных
    spi(0xFF); // посылка импульсов синхронизации для приема старшего байта
    bt=(unsigned long)SPDR<<16; // принимаем и двигаем старший байт влево на два байта
    spi(0xFF); // посылка импульсов синхронизации для приема среднего байта
    bt|=(unsigned long)SPDR<<8; // принимаем и двигаем средний байт влево на байт и собираем его со старшим байтом
    spi(0xFF); // посылка импульсов синхронизации для приема младшего байта
    bt|=(unsigned long)SPDR; // принимаем младший байт и собираем его со средним и старшим байтами
    return bt;
    SS_pin=1; // отключаемся от АЦП
    };

    }

    void main(void)
    {
    // Port B initialization
    // Func7=In Func6=In Func5=Out Func4=In Func3=In Func2=Out Func1=Out Func0=Out
    // State7=T State6=T State5=1 State4=P State3=P State2=0 State1=0 State0=1
    PORTB=0x39;
    DDRB=0x27;
    // USART0 initialization
    // USART0 disabled
    UCSR0B=0x00;
    // USART1 initialization
    // Communication Parameters: 8 Data, 1 Stop, No Parity
    // USART1 Receiver: Off
    // USART1 Transmitter: On
    // USART1 Mode: Asynchronous
    // USART1 Baud Rate: 9600
    UCSR1A=0x00;
    UCSR1B=0x08;
    UCSR1C=0x06;
    UBRR1H=0x00;
    UBRR1L=0x40;
    // SPI initialization
    // SPI Type: Master
    // SPI Clock Rate: 78,125 kHz
    // SPI Clock Phase: Cycle Half
    // SPI Clock Polarity: High
    // SPI Data Order: MSB First
    SPCR=0x5F;
    SPSR=0x00;
    SS_pin=0; // активируем АЦП
    // инициализация АЦП

    spi(0x26); // подготовка Communication Register к записи в High Filter Register и выбор каналов АЦП (AIN5/AIN6)
    spi(0xD1); // запись установок в High Filter (unipolar,24 bit word,BST=0,CLKDIS=1,FS11=0,FS10=0,FS9=0,F8=1 в High Register Bits - Code 312 для 25гц )
    spi(0x36); // подготовка Communication Register к записи в Low Filter кода фильтра для выбранных каналов АЦП (AIN5/AIN6)
    spi(0x38); // запись Сode 312( 0b00111000 ) для 25 гц в Low Filter Register
    spi(0x16); // подготовка к записи в Mode Register его установок для выбранных каналов АЦП (AIN5/AIN6)
    spi(0x20); // запись установок в Mode Register (self calibration , gain=1,BO=0,FSYNC=0)
    SS_pin=1; // отключаемся от АЦП

    while(1)
    {

    result=(2.50*read_adc())/16777215; // собственно , формула полученного результата в вольтах (VREF=2.5VDC)
    printf("Uadc=%.6fmV",result); // вывод результата на терминал с шестью знаками после запятой
    printf("\n\r");

    }
    }
     
    Последнее редактирование: 23 мар 2015
    Daniil нравится это.