Неправильные выходные данные на ЦАП DAC8830i

Возникла проблема с внешним ЦАП: задача принятия данных с АЦП, работа с ними на ардуино нано и дальнейшая передача на ЦАП по интерфейсу SPI
В подавляющем большинстве примеров для ЦАП на SPI, задержки между изменением состояния CS и MOSI отсутствуют, но в моем случае:
delay = ~0: напряжение постоянно равно либо 0, либо 4.10В
delay = ~5мс: напряжение стабильно только при 0 или 4.10В (0 и 65535), любое другое значение колеблется с амплитудами до вольта
delay = ~15мс: напряжение стабильно на всей шкале измерения, но нелинейно: при 65535 3.7В, а при 32000 уже 1.35В
Параллельно вывожу значение в Serial и вижу, что с тем, что должно передаваться, проблем нет, соответственно проблема в том, что ЦАП получает, в каком порядке и прочее, с этим не получается разобраться для получения приемлемого результата
При этом, даташит по датчику отрицает необходимость задержек:


разводку проверяй, как обычно сопли везде

Какие-то совершенно дикие значения. Если задержки и нужны, то они как правило измеряются нано-секундами, в крайнем случае микро
15 миллисекунд - это вечность по меркам электроники.
Это я все к тому, что микросхема наверняка таких задержек не требует. проблема в вашем коде или схеме

Да, кстати, только сейчас заметил - никакого вопроса в сообщении нет. Вы собственно, от форума-то что хотели?

2 лайка

Как вариант: внимательно изучить шину ЦАП. Возможно SCLK считается по фронту,а МК считает по спаду. И какой бит первый,младший или старший. Короче проверить соответствие настроек МК и ЦАП.

И ещё заметно, что нет ни кода, ни схемы))

Сколько притоков у Дуная?

Микросхема, конечно, таких задержек не требует, но при соответствии задержкам из даташита(= их отсутствию) не работает
Привожу код и схему, в реализации подключения соответствуют схеме, качество пайки откровенно плохое, так как приходилось паять smd компоненты на обычную макетную плату, но все подключения выполнены правильно
От форума хотел понять, почему в SPI набивается всякая ерунда и как бОльшая задержка способна это исправлять. Необходимо, конечно, от задержки избавиться, только тогда ЦАП не преобразовывает

#include <SPI.h>

#define weight_of_standard 220  // Калибровочный вес
#define power_down() \          
bitSet(_SFR_IO8(_Write), _clkPin); \
delayMicroseconds(64)
#define power_up() bitClear(_SFR_IO8(_Write), _clkPin)  // Макрос для включения питания АЦП
#define GAIN64A 2                                       // Установка Коэффициента усиления
#define REF_GR_DAC 100.00                                  // Референсное значение для ЦАП в граммах (=максимум шкалы измерения)
#define SPI_CLK 13
#define SPI_SDI 11

  class DAC {
public:
  DAC(int i = 1) {
  }
  void begin(uint8_t CS) {  // Инициализация ЦАП, Pins - пины SPI
    CLK_PIN = SPI_CLK;           // Запись пинов SPI
    SDI_PIN = SPI_SDI;
    CS_PIN = CS;
    digitalWrite(CS_PIN, HIGH);
    SPI.begin();                                                       // Запуск интерфейса SPI
    SPI.beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0));  // Начало транзакции, 20МГц, Старший байт первый, Мод0
    
  }
  void write(float value) {  // Запись значения в ЦАП
    if (value < 0) {          // Если значение опускается меньше нуля, записывать ноль
      _VAL = 0;
    } else if (value > REF_GR_DAC) {  // Если значение выше референсного, записывать максимальное
      _VAL = 65535;
    } else {
      _VAL = uint16_t(65535 * value / REF_GR_DAC);  // Расчет значения по пропорции
    }
    digitalWrite(CS_PIN, LOW);   // Запись в чипселект 0 для выбора ЦАП для передачи
    delay(15);
    SPI.transfer16(_VAL);         // передача ЦАП
    //delay(15);
    digitalWrite(CS_PIN, HIGH);  // Запись в чипселект 1 для окончания передачи
  }
  uint16_t get_value () {
    return _VAL;
  }
  //MSBFIRST в настройке spi = избегаем разделения значений по двум байтам, выдача начинается со старшего байта
private:
  uint16_t _VAL;
  uint8_t CS_PIN, CLK_PIN, SDI_PIN;
  int16_t REFERENCE_MV;
};

class SCALE {
public:
  /*Порядок записи в массив: DR, DW, DM, CW, CM, DP, CP*/
  SCALE(int i = 1) {
  }

  /*ИНИЦИАЛИЗАЦИЯ ДАТЧИКА*/
  void begin(byte Regs[3], byte Pins[2]) { // Инициализация АЦП
    _Read = Regs[0]; // Запись значений регистров и пинов
    _Write = Regs[1];
    _Mode = Regs[2];
    _dataPin = Pins[0];
    _clkPin = Pins[1];
    bitSet(_SFR_IO8(_Mode), _clkPin); // Настройка регистров
    bitClear(_SFR_IO8(_Mode), _dataPin);
    bitClear(_SFR_IO8(_Write), _clkPin);
    bitClear(_SFR_IO8(_Write), _dataPin);
    power_down(); //  Выключение и включение АЦП
    power_up();
    _chan = GAIN64A;
    _weight = 0;
    _value = 0;
    _offset = 0;
    _scale = 1;
  }

  /*УСТАНОВКА МАСШТАБА*/
  void set_scale(float scale) {
    _scale = 1.0 / scale;
  }

  /*КАЛИБРОВКА*/
  void calibrate_scale(uint8_t weight) {
    long read_av = 0;
    for (uint8_t i = 0; i < 15; i++) { // Калибровка на 15 значениях среднее арифметическое
      read_av += read();
    }
    read_av /= 15;
    _scale = (1.0 * weight) / (read_av - _offset); // получаем масштаб
  }

  /*ВЕРНУТЬ МАСШТАБ*/
  float get_scale() {
    return 1 / _scale;
  }

  /*ТАРИРОВАНИЕ*/
  void tare() {
    for (uint8_t i = 0; i < 3; i++) {  // тарируем на фильтрованном значении
      _offset += filt();
    }
    _offset = (_offset / 3) / _scale;
  }

  /*ЧТЕНИЕ СЫРОГО ЗНАЧЕНИЯ*/
  long read() {
    while (bitRead(_SFR_IO8(_Read), _dataPin) == HIGH) yield();
    _weight = 0;
    for (uint8_t i = 0; i < 24; i++) { // посылка сигналов тактирования
      bitSet(_SFR_IO8(_Write), _clkPin);  //digitalWrite(_clock, HIGH);
      delayMicroseconds(1);
      _weight <<= 1;
      if (bitRead(_SFR_IO8(_Read), _dataPin)) _weight |= 1;
      bitClear(_SFR_IO8(_Write), _clkPin);  //digitalWrite(_clock, LOW);
      delayMicroseconds(1);
    }
    for (uint8_t i = 0; i < _chan + 1; i++) {
      bitSet(_SFR_IO8(_Write), _clkPin);  //digitalWrite(_clock, 1);
      delayMicroseconds(1);
      bitClear(_SFR_IO8(_Write), _clkPin);  //digitalWrite(_clock, 0);
      delayMicroseconds(1);
    }
    if (_weight & 0x800000) _weight |= 0xFF000000;  // отрицательные
    return _weight;
  }

  /*ПЕРЕВОД СЫРОГО ЗНАЧЕНИЯ В ГРАММЫ*/
  float value() {
    read();
    _value = (_weight - _offset) * _scale;
    return _value;
  }

  /*ВЕРНУТЬ ЗНАЧЕНИЕ В ГРАММАХ*/
  float get_value() {
    return _value;
  }

  /*ФИЛЬТРАЦИЯ ЗНАЧЕНИЯ В ГРАММАХ*/
  float filt() {
    float newVal = value(); 
    byte k = 5;
    static float filt = newVal;
    if (abs(newVal - filt) <= 0.5) k = 30; // Выбор коэффициента для бегущего среднего в зависимости от величины изменения
    else if (abs(newVal - filt) >= 0.51 && abs(newVal - filt) < 2.7) k = 4;
    else if (abs(newVal - filt) >= 2.7 && abs(newVal - filt) < 3.5) k = 2;
    else if (abs(newVal - filt) >= 3.5 && abs(newVal - filt) < 5) k = 6;
    else if (abs(newVal - filt) >= 5 && abs(newVal - filt) < 7) k = 10;
    else if (abs(newVal - filt) >= 7) k = 20;

    filt += (newVal - filt) / k; // фильтрация по бегущему среднему
    return filt;
  }
  // float filt_raw() {
  //   float newVal = read();
  //   byte k = 7;
  //   static float filt = newVal;
  //   // if (abs(newVal - filt) <=0.5) k = 30;
  //   // else if(abs(newVal-filt) >= 0.51 && abs(newVal-filt)<=15) k = 3;
  //   // else if (abs(newVal-filt) > 10) k = 10;

  //   filt += (newVal - filt) / k;
  //   return filt;
  // }


private:
  byte _Read, _Write, _Mode;
  byte _dataPin, _clkPin, _chan;
  long _weight, _offset;
  float _scale, _value;
};

// Инициализация глобальных переменных
float values[4] = {0, 0, 0, 0};
bool tare_sig = false;
long timer = 0;
uint32_t t = 0;

/*Адресса регистров HX PIND, PORTD, DDRD  PORTB DDRB*/
byte D_Adresses[3] = { 0x09, 0x0B, 0x0A /*, 0x05, 0x04*/ };

// Пины CS для DAC      CS0 CS1 CS2 CS3
byte SPI_Adresses[4] = {14, 15, 16, 17};

// Пины данных и тактирования для HX711
byte D_Pins[4][2] = {
  { 1, 0 },
  { 2, 3 },
  { 4, 5 },
  { 6, 7 },
};





SCALE scale[4];  // создание объектов АЦП
DAC dac[4];      // Создание объектов ЦАП


// ISR(PCINT1_vect) {  // Обработчик прерывания на пине А4
//   scale[1].begin(D_Adresses, D_Pins[1]); // инициализация АЦП
//   scale[1].set_scale(386.54); // установка масштаба
//   scale[1].tare(); // тарирование
//   scale[1].set_scale(386.54);  // Вызов функции тарирование
//   tare_sig = true;  // Установка флага
// }

void setup() {
  Serial.begin(9600);
  // PCICR |= 1 << 1;
  // PCMSK1 |= 1 << 4;  // НАСТРОЙКА ПРЕРЫВАНИЯ НА ПИНЕ А4
  // SREG |= 1 << SREG_I;
// КОД ДЛЯ 4 ДАТЧИКОВ
  // for (int i = 0; i < 4; i++) {
  // scale[i].begin(D_Adresses, D_Pins[i]); // инициализация АЦП
  // scale[i].set_scale(386.54); // установка масштаба
  // scale[i].tare(); // тарирование
  // scale[i].set_scale(386.54);
  // dac[i].begin(SPI_Adresses); // инициализация ЦАП
  // }
  
  scale[1].begin(D_Adresses, D_Pins[1]); // инициализация АЦП
  scale[1].set_scale(386.54); // установка масштаба
  scale[1].tare(); // тарирование
  scale[1].set_scale(386.54);
  dac[1].begin(SPI_Adresses[1]); // инициализация ЦАП
}

void loop() {
  tare_sig = false; // Сброс флага
    
   while (tare_sig == false) { // Выполнение при активном флаге
      values[1] = scale[1].filt(); //запись фильтрованного значения
      //Serial.println(values[1]);
      //  исполняемый код для передачи на плк
      dac[1].write(values[1]); // запись в ЦАП
      uint16_t value = dac[1].get_value();
      Serial.print("DAC: ");
      Serial.print(value);
      Serial.print(" ; ");
      Serial.print("ACD: ");
      Serial.println(values[1]);
    }
}

Настройки SPI изменял в первую очередь, по отрывку из даташита видно, что это Мод 0 и MSBFIRST, менял частоту, но это лучше не делало

Не совсем понимаю, что может дать код, в котором важен только отрывок записи в ЦАП (= одна библиотечная строчка SPI) и схема, собранная по даташиту

Хотя бы элементарная вежливость. Мы ведь не знаем что у Вас , да как…

Понимаю, прошу прощения
Последовал логике Stackoverflow - коротко и по делу, наверное, неправ
В любом случае, код и схему выше прикрепил
Суть в том, что если бы были фундаментальные ошибки в них, макет бы не работал вообще, но основная функция ЦАП выполняется, только при задержке взятой эмпирически и непомерно большой, как уже заметили
При том, что по даташиту значения задержек измеряются порядком наносекунд, а относительно стабильная работа датчика (и то нелинейная), достигается только при задержке в 15 мс
UPD. Касательно кода, пробовал передачу и по байту не через transfer16, а через transfer, разницы нет

не вижу, где в классе DAC пин CS выходом назначается…

1 лайк

SPI_MODE3 пробовали?

Безумно глупо с моей стороны, да, в этом была проблема
Видимо, без назначения выхода, каждый раз при записи в чипселект ардуино выполняла какой-то свой код проверки/назначения/etc и часть данных в transfer кушалась
Спасибо большое за внимательность, вроде код проверял, а такую простую вещь не увидел
В следующий раз буду умнее, сразу прикреплять все исходники, а не следовать одной своей догадке

Да, оказалось, дело в важной мелочи, которую я упустил, зато начитался про SPI, режимы и проштудировал несколько раз даташит

1 лайк

Согласись,время с пользой провёл😁 Опыт набрал,что-то новое узнал.

1 лайк