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

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

  1. Daniil

    Daniil Гуру

    Здравствуйте.
    Ох, как я намучился с этой задержкой=) рад, что помог.
    На счёт цифр:
    Для расчёта задержки нужно.
    1. Выбрать тактовую частоту f_clk - это то, что подаётся на MCLK IN(OUT)
    2. Конфигурировать фильтр с учётом формулы времени измерения:
    t_изм = 1/f_ср,
    где f_cp - частота среза конфигурируемого фильтрa:
    f_cp = (f_clk/128) / k,
    где k - это код, который Вы вписали в регистры фильтра.
    3. Расчитать t_изм, желательно коруглять в большую сторону.

    Далее в pdf говорится о том, что если вы делаете калибровку, то нужно подождать 4*t_изм, но если вы пользуетесь синхронизацией, то можно ждать 3*t_изм.(я аппаратной калибровкой не пользуюсь, я на ПК слежу за числами).
    В том же абзаце говорится о том, что при изменении канала всегда надо ждать 3*t_изм.
    К сожалению, я это подтвердить не могу.
    Я использую f_clk = 2,4576 МГц (китайский, частота может меньше, возьму 2,45 МГц). С кодом k = 19 не работало ничего, поэтому взял 20 (может быть мои первые опыты неправильные были, но сейчас пока этим не занимаюсь).
    f_cp = 2450 кГц /128 / 20 = 0,957 кГц => t_изм = 1,045 мс округлю до 1.1 мс => мне нужно ждать при чтении одного канала 1,1 мс, а при смене канала 3,3 мс, на практике я жду 1,5 мс и 4,5 мс. Причину такого эффекта не нашёл, такие числа подбирал опытным путём.
     
  2. Daniil

    Daniil Гуру

    Для меня это новинка, не судите строго.
    На страницах 11-12 даташита есть таблицы Ia(b) и IIa(b). В них указано, что скорость работы АЦП и усиление влияет на шумы, а значит на "количество эффективных бит", т.е. это количество (в таблице указаны в скобках цифры) бит, на которые шум не действует.
    Пример:
    Я выше рассмотрел k=19, т.е. частота первого нуля фильтра равна 1 кГц (табл Iа), усиление я выбрал равное 1, а значит количество значащих бит равно 11 из 24ёх доступных.
    Ia.png
    Было весело, я не знал откуда шумы при быстрой работе АЦП, поэтому соорудил страшную схему с большой землёй и старался поставить по-больше фильтрующих емкостей... В итоге заметил, получилось так, что я включал АЦП в 2 этапа - для начала подавал питание на цифровую часть (DVDD) (это же питание идёт на ATmega), а потом тумблером включал аналоговую часть (AVDD) и шума не было) и думал что я молодец.
    Оказалось, что при включении 1ого питания МК программировал АЦП, но АЦП не работал в полную меру, т.к. аналоговое питание отключено (я думал, что при включённом цифровом питании работать будет), а значит не программировался и все биты были сконфигурированы по-умолчанию (кстати FS11-FS0 какие имеет значения по-умолчанию? В даташите молчат и точно не нули), поэтому у меня работало.
    Так же, ради быстродействия я плюнул на DRDY и вынимал данные через 4,5 мс)


    Теперь же вынужден изменить скорость работы на 100 Гц, но при этом получается сдвиг по абсолютному значению преобразованной величины.

    Сейчас я не очень понимаю как это k влияет на сам преобразованный сигнал. Если я медленно преобразую, то вижу, к примеру, 860 мВ, а если скорость выкручиваю на максимум, то вижу 600 мВ.
    Так же пока проблема - я вынужден запрашивать данные с одного канала 2 раза, иначе происходит сдвиг в значениях, т.е. запрашивал с первого канала, получил с 5ого, запросил с 2ого получил с первого. (я запрашиваю последовательно, не пробовал в разнобой), т.е. при первом запросе с нового канала данные приходят с предыдущего.
     
    Последнее редактирование: 28 май 2015
  3. Vyacheslav.07

    Vyacheslav.07 Нерд

    Привет ,
    При включении питания выполняется самокалибровка , поэтому нужно также присутствие аналогового питания . Частота фильтра ... по большому счету , нас имеет частота сети ... 50 или 60 Гц . По моим понятиям , чем ниже частота фильтра , тем больше помехозащищенность , а так же соглассно дата щиту , максимальные реджекции помех кратны 10 Герцам ... Исходя из всего выше сказанного , количество полезных битов будет зависеть от частоты фильтра и усиления .... чем они ниже , тем ниже шумов ... но ... время преобразования будет большим ... примерно , до 3-х секуднд ... а если учесть , что преобразуется не один канал , то намного выше .
    Сигнал DRDY необходимо также учитывать , так как он появляется первый с того канала , с которого Вы делаете запрос ... иначе , если поставить задержку 4,5 мс , то будет непонятно , с какого канала Вы снимаете преобразование .
    С уважением , Вячеслав
     
  4. Daniil

    Daniil Гуру

    Здравствуйте.

    Спасибо.
    Я немного переписал алгоритм.

    Процедура считывания (записи) с (в) АЦП.
    Код (Text):
    unsigned char my_transfer(byte b)
    {
    unsigned char btl;
    //начало бесконечного цикла ожидания DRDY
    boolean DRDY_flag = true;
    while (DRDY_flag){
        if (digitalRead(DRDY_pin)==0){
            DRDY_flag=false;}}
    //конец бесконечного цикла ожидания DRDY
    digitalWrite(SS_pin,LOW); //выбор АЦП
    btl=SPI.transfer(b); //передача данных
    //начало бесконечного цикла ожидания DRDY
    DRDY_flag = true;
    while (DRDY_flag){
        if (digitalRead(DRDY_pin)==0){
            DRDY_flag=false;}}
    //конец бесконечного цикла ожидания DRDY
    digitalWrite(SS_pin,HIGH); //выбор АЦП
    return btl;
    }
    Я ожидаю DRDY до и после отправки команды, хоть и получается избыточно.

    А процедура запроса данных выглядит так:
    Код (Text):
    void getEout()
    {
    //1ый запрос "перепутывается" с предыдущим, поэтому в холостую его запускаем
    my_transfer(B01011000);
    my_transfer(0xFF);
    my_transfer(0xFF);
    //2ой запрос
    my_transfer(B01011000);
    //программа на ЭВМ требует начинать посылку с ":" и номера (буквы) канала
    Serial.print(":B");
    getValue();
    }

    void getValue()
    {
    unsigned long int bt1;
    unsigned long int bt2;
    unsigned long int bt3;
    bt1=my_transfer(0xFF);
       Serial.print((bt1>>4)&0x0F, HEX);
       Serial.print((bt1)&0x0F, HEX);
    bt2=my_transfer(0xFF);
       Serial.print((bt2>>4)&0x0F, HEX);
       Serial.print((bt2)&0x0F, HEX);
    //программа на ЭВМ требует 6 цифр в числе и char(13), поэтому ниже 3 строчки не лишние
       Serial.print(0, HEX);
       Serial.print(0, HEX);
       Serial.print(char(13));
    }
     
    Весь код выглядит так (на всякий случай) в том виде в каком зашит в МК:
    Код (Text):
    #include "adc_main.h"
    #include <SoftwareSerial.h> // для передачи данных на ПК
    #include <SPI.h>            // для передачи данных между arduino и ad7714

    #define DRDY_pin 5
    #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 char my_transfer(byte b)
    {
    unsigned char btl;
    boolean DRDY_flag = true;
    while (DRDY_flag){
        if (digitalRead(DRDY_pin)==0){
            DRDY_flag=false;}}
    digitalWrite(SS_pin,LOW);
    btl=SPI.transfer(b);
    DRDY_flag = true;
    while (DRDY_flag){
        if (digitalRead(DRDY_pin)==0){
            DRDY_flag=false;}}
    digitalWrite(SS_pin,HIGH);
    return btl;
    }

    // процедура конфигурации АЦП и вывода на экран "разметки таблицы"
    void my_init() {
    //my_transfer(0x27);delay(100);
    /* 0 010 0 000
    *  Filter High Register
    *      Запись
    *        AIN1 - AIN6 */
    //my_transfer(0x00);delay(100);
    /* 0 0 1 0 0000
    * Биполярный режим
    *  16 бит
    *    Ток Boost on т.к. работаем от кварца на 2,4576 МГц и Кус=1
    *      От кварца
    *          Для 20 */
    //my_transfer(0x37);delay(100);
    /* 0 011 0 111
    *  Filter Low Register
    *      Запись
    *          AIN1 - AIN6 */
    //my_transfer(0xC3);delay(100);
    /* 0010100
    100 - 0x64
    20 - 0x14
    * Для 20 */
    my_transfer(0x17);delay(100);
    /* 0 001 0 000
    *  Mode Register
    *      Запись
    *          AIN1 - AIN6 */
    my_transfer(0x20);delay(100);
    /* 000 000 0 0
    * Нормальный режим
    *    Кус=1
    *          Выключить какие то токи
    *              Не надо синхронизации    */
    }

    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, LOW);    // высокий уровень на 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);
      digitalWrite(RESET_pin, HIGH);
      my_init();
    }

    void getValue()
    {
    unsigned long int bt1;
    unsigned long int bt2;
    unsigned long int bt3;
    bt1=my_transfer(0xFF);
        Serial.print((bt1>>4)&0x0F, HEX);
        Serial.print((bt1)&0x0F, HEX);
    bt2=my_transfer(0xFF);
        Serial.print((bt2>>4)&0x0F, HEX);
        Serial.print((bt2)&0x0F, HEX);
        Serial.print(0, HEX);
        Serial.print(0, HEX);
        Serial.print(char(13));
    }

    void getEout()
    {
    my_transfer(B01011000);
    my_transfer(0xFF);
    my_transfer(0xFF);
    my_transfer(B01011000);
    Serial.print(":B");
    getValue();
    }

    void getT1()
    {
    my_transfer(B01011001);
    my_transfer(0xFF);
    my_transfer(0xFF);
    my_transfer(B01011001);
    Serial.print(":C");
    getValue();
    }

    void getT2()
    {
    my_transfer(B01011010);
    my_transfer(0xFF);
    my_transfer(0xFF);
    my_transfer(B01011010);
    Serial.print(":D");
    getValue();
    }

    void getVs()
    {
    my_transfer(B01011011);
    my_transfer(0xFF);
    my_transfer(0xFF);
    my_transfer(B01011011);
    Serial.print(":E");
    getValue();
    }

    void getEin()
    {
    my_transfer(B01011110);
    my_transfer(0xFF);
    my_transfer(0xFF);
    my_transfer(B01011110);
    Serial.print(":A");
    getValue();
    }

    // главный цикл
    void loop()
    {
        if (Serial.available())
        {
            char str=Serial.read();
                switch (str)
                {  
                    //case '!':    menu();    break;    //menu
                    case 'A':    getEin();    break;    //Ein
                    case 'B':    getEout();    break;    //Eout
                    case 'C':    getT1();    break;    //T1
                    case 'D':    getT2();    break;    //T2
                    case 'E':    getVs();    break;    //Vs
                }
        }
    }
    Проблема именно с 2мя запросами, т.к. с шумами вроде разобрались. А то что при увеличении скорости появляется не шум, а искажение данных это ставит в тупик, ведь по таблицам скорости работы и усиления я могу позволить себе скорость выше, чем она есть сейчас.
     
  5. Panigo

    Panigo Нуб

    Daniil, подскажите пожалуйста почему у Вас в // процедуре конфигурации АЦП и вывода на экран "разметки таблицы", закомментированы строки настройки High и Low регистров? Никак не могу с этим АЦП разобраться.... Я правда использую Bascom для программирования, но это не важно. Спасибо.
     
  6. Daniil

    Daniil Гуру

    Здравствуйте!
    Мне нужна была высокая скорость работы и высокое разрешение, но я не знал что у АЦП есть таблица, про которую я писал выше (сообщение #22), поэтому мне пришлось удовлетвориться значениями по-умолчанию, я особо не вдавался тут в конкретные значения, поэтому в итоге в коде я закомментировал строчки конфигурации фильтра.
    На сколько я помню Filter High Register при включении АЦП имеет значение 0b0010хххх (последние 4 бита как раз и не знаю), а раз то, что мне нужно совпадает с даташитом, то я и не перезаписываю. (да, скорость работы так и остаётся неизвестной для меня)
     
  7. Panigo

    Panigo Нуб

    Да в том то и дело...Мне нужна маленькая скорость(10Гц-за глаза), но большое разрешение. Никак у меня каменный цветок не выходит с этой АЦП. Пытаюсь переписать прогу старого прибора, но под новые входные данные. Всю башку себе сломал. Переделывать старое хуже чем все с начала написать. Для получения данных вы используете прерывания на линии DRDY ? Да и почему у вас в подпрограммах считывания данных идет пропуск одного преобразования?
    my_transfer(B01011000);
    my_transfer(0xFF);
    my_transfer(0xFF);

    my_transfer(B01011000);
     
  8. Daniil

    Daniil Гуру

    Вам нужно в регистр High записать code равный 20 или 19 (19 у меня плохо работало)
    20 = 0000 0001 0100
    19 = 0000 0001 0011 - в двоичной системе
    (Я надеюсь тактирование, полярность и т.п. у нас совпадают)
    Код (C++):
    High:
    "1 1 1 0 0000"
    7ой бит - "1" - для однополярного сигнала
    6ой бит - "1" - 24 бит
    5ый бит - "1" - Ток Boost включён (т.к. работаем от кварца на 2,4576 МГц и Кус=1)
    4ый бит - "0" - От кварца
    3ий-0ой биты - "0000" - для 20 или 19
    Low:
    "0001 0100" для 20
    "0001 0011" для 19
    Для чтения DRDY прерывание НЕ использовал, т.к. у меня всё должно выполняться последовательно. В прерываниях нет нужды было.
    В процедуре my_transfer это продемонстрировано:
    Код (C++):
    unsigned char my_transfer(byte b)
    {
    unsigned char btl;
    boolean DRDY_flag = true;
    while (DRDY_flag){
        if (digitalRead(DRDY_pin)==0){
            DRDY_flag=false;}}
    digitalWrite(SS_pin,LOW);
    btl=SPI.transfer(b);
    DRDY_flag = true;
    while (DRDY_flag){
        if (digitalRead(DRDY_pin)==0){
            DRDY_flag=false;}}
    digitalWrite(SS_pin,HIGH);
    return btl;
    }
    Я перед каждой отправкой данных или приёмом данных жду когда DRDY станет "0" (вывод инвертированный, поэтому жду "0").
    Пока я не разобрался с DRDY я просто ждал 5 мс, у Вас же из-за другого делителя (code) я бы поставил 210 мс (1 / 4.8 Гц) (если у Вас тактируется от 2,4576 МГц).

    А вот 2 считывания - это самое слабое место в моей программе(
    Я не разобрался почему, но если я буду анализировать данные после первого считывания, то обнаружу, что эти данные принадлежат каналу использованному ранее. То есть я по очереди считываю с 1ого канала, потом со 2ого, потом с 3его... потом с 5ого, потом с 1ого... и тд и если я буду смотреть данные на 1ом канале, то после первого считывания у меня будут данные с 5ого.
    Поэтому я запрашиваю данные с 1ого канала,
    Код (C++):
    my_transfer(B01011000);
    считываю 2 байта (16 бит), их никак не использую,
    Код (C++):
    my_transfer(0xFF);
    my_transfer(0xFF);
    а потом опять запрашиваю данные с 1ого канала
    Код (C++):
    my_transfer(B01011000);
    и начинается обработка.
    Код (C++):
    Serial.print(":B");
    getValue();
     
  9. Panigo

    Panigo Нуб

    Собрал отдельно АЦП. Все подключил как у Вас указано в скетче. Подскажите плиз что это за библиотека "adc_main.h" и с чем ЕЕ Едят(где взять?).
     
  10. Daniil

    Daniil Гуру

    Это не библиотека. Я программировал в другой среде, там этот файл создавался автоматически. Можете удалить эту строчку.
     
  11. Panigo

    Panigo Нуб

    Понял Вас. Дальше будем репу чесать.
     
  12. Panigo

    Panigo Нуб

    ААААААА...... Помогите. Не пойму почему не работает. По моему попробовал уже все что можно. Посмотрите по возможности мой MAIN.c, у кого может какие догадки будут. Заранее спасибо. У меня на DRDY ноге низкий уровень не появляется, что это значит, НЕ проходит инициализация АЦП. Или еще что-то может быть?
     

    Вложения:

    • main.c
      Размер файла:
      11,9 КБ
      Просмотров:
      811
    Последнее редактирование: 17 дек 2015
  13. logic777

    logic777 Нуб

    daniil, Если не сложно подскажите по паре пунктов.
    Первое. Какое фактическое кол-во рабочих байтов удалось достичь на низких частотах(хоть 1 Гц)?
    Второе. Я так понял все напряжения Вы брали прямо от друинки без всяких прецизионных опорных и пр прелестей?

    Заранее благодарен.
     
  14. Daniil

    Daniil Гуру

    Прошу прощения, что никак не отреагировал на Ваше сообщение( Не приходило уведомление на почту. За такой долгий срок, я думаю поздно уже помогать=)Вы решили свою проблему? Если да, то как?
    По 1ому пункту ничего не скажу, т.к. в моей задаче приоритет ставился скорости, я не работал на такой низкой частоте, поэтому тут я не подскажу.
    По 2ому пункту - если вы про мою конфигурацию, то сразу скажу, что у ардуины нет 2,5 В, поэтому без доп. прелестей не обойтись.
    На деле же, я использовал отдельное питание (Vdd > 5-6 В). Использовал 2 стабилизатора на 5 В (не прецизионных, т.к. я так и не вычитал из даташита какой стабильностью должно обладать питание для аналоговой части). После стабилизатора на 5 В для аналоговой части стоит стабилизатор на 2,5 В, тут уже в даташите есть рекомендация AD780.
    Также было уделено не мало времени схемотехнике => аналоговую часть держал по-дальше от всего и старался не пересекать ни на верхнем слое, ни на нижнем слое.
    Ардуинка и плата АЦП имеют общую землю.
     
  15. logic777

    logic777 Нуб

    Благодарю. будем пилить :)
     
  16. logic777

    logic777 Нуб

    Таки дошли руки до гравицапы.

    Мне нужен вольтметр с максимальной точностью, частота в 1-5 грц норма. Канал нужен один.
    По поводу сдвижки есть прикол с входными аналоговыми фильтрами. Вначале думал что сделаю фильтр на постоянную времени 100мс и все гут. В даташите настоятельно не рекомендуют так делать. Если посмотрите, там постоянная времени около 1 мкс еще и от коэф. усиления зависит.

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