Использование Arduino для записи аналоговых сигналов с помощью SPI ADC + проблема с частотой дискретизации

Я использую Arduino UNO для чтения 8-канальных аналоговых сигналов с 24-битным разрешением, используя ADS1299 Analog-fornt-end. ADS1299 использует интерфейс SPI для связи с Arduino. В настоящее время я могу записывать аналоговый сигнал с помощью Arduino на основе протокола SPI и отправлять данные через последовательный порт на ПК.

ADS1299 имеет 8 каналов с 24-битным разрешением. Частоту дискретизации можно задать от 250 с/с до 16 тыс. с/с. Как вы можете видеть в приведенном ниже коде, когда данные готовы, я перевожу устройство в режим чтения данных и сохраняю 8 бит (1 байт) при каждом запуске и объединяю 3 байта для создания полной выборки. Я повторяю эти процедуры, чтобы получить полные выборки всех каналов. В конце я отправил образец одного канала на последовательный порт с помощью команды serial.print.

Вот моя проблема: если я хочу увеличить частоту дискретизации устройства, например, до 10 тыс. с/с, Arduino не сможет обеспечить эту частоту дискретизации из-за задержки, вызванной командой Serial.println. Можно ли сохранить данные в большой массив, а затем отправить весь массив через последовательный порт? Возможно ли, что другие типы микроконтроллеров, такие как типы ARM, решают эту проблему? Вообще как можно избежать задержки серийной печати?

    #include <SPI.h>
    #define WAKEUP 0x02
    #define STANDBY 0x04
    #define RESET 0x06
    #define START 0x08
    #define STOP 0x0a
    #define RDATAC 0x10
    #define SDATAC 0x11
    #define RDATA 0x12
    #define TESTaa 0xaa
    #define TEST55 0x55

    #define CONFIG1 0x01
    #define CONFIG2 0x02
    #define CONFIG3 0x03
    #define CH1SET 0x05
    #define CH2SET 0x06
    #define CH3SET 0x07
    #define CH4SET 0x08
    #define CH5SET 0x09
    #define CH6SET 0x0A
    #define CH7SET 0x0B
    #define CH8SET 0x0C




      const int PIN_START = 7;
      const int IPIN_DRDY = 9;
      const int PIN_CS = 10; 

      const int DOUT = 12;
      const int DIN = 11;
      const int PIN_SCLK = 13;  
      const int PIN_RESET = 8;


      byte chSet;
      byte chSet2;
      boolean flag = false;
      long t0, t;


    void setup(){


      // Вы можете использовать последовательную связь для отладки
      Serial.begin(2000000);
      Serial.flush();
      delayMicroseconds(100);
      // Вы должны установить связь SPI в соответствии с таблицей данных
      SPI.setDataMode(SPI_MODE1);
      //SPI.setClockDivider(SPI_CLOCK_DIV16);
      SPI.setBitOrder(MSBFIRST);
      SPI.begin();

      pinMode(DOUT, OUTPUT);
      pinMode(PIN_SCLK, OUTPUT);
      pinMode(PIN_CS, OUTPUT);
      pinMode(PIN_START, OUTPUT);
      pinMode(IPIN_DRDY, INPUT);
      pinMode(PIN_RESET, OUTPUT);

      //сбросить связь, см. техническое описание
        digitalWrite(PIN_SCLK, LOW);
        digitalWrite(DIN, LOW);
        digitalWrite(PIN_CS, HIGH);


      digitalWrite(PIN_CS, LOW); //От низкого до сообщенного
      SPI.transfer(RESET); 
      digitalWrite(PIN_CS, HIGH); //От низкого до сообщенного


      // Дождитесь запуска чипа TI
      delay(500);
      send_command(SDATAC);
      delay(10);
      chSet = read_byte(0x00);
      Serial.println("-- ID CHIP is:" + hex_to_char(chSet) );


      // Запись конфигурации 1 и 2: см. техническое описание на стр. 47
      // Конфигурация 1: Биты 7 6 5 4 3 2 1 0 --- 1 DAISY_EN=1 CLK_EN=0 1 0 DR[2:0] (110-- 250 Sample/S) ---> 0b11010110
      // Конфигурация 2: Биты 7 6 5 4 3 2 1 0 ---- 1 1 0 INT_CAL=1(Int/Ext test sig) 0 CAL_AMP=(амплитуда калибровочного сигнала)
      // CAL_FREQ[1:0] (частота калибровочного сигнала) -----> 0b11010100


      // для нормальной работы пишем в config1 0x02, config2 0xA0. Для тестового сигнала напишите 0xA3 в config2
      write_byte(CONFIG1, 0x96);   // Гирляндная цепочка включена: D6, гирляндная цепочка отключена: 96
      delay(10);

      write_byte(CONFIG2, 0xD1);   // Тестовый сигнал Включен: D0 Тестовый сигнал Отключен: C0
      delay(10);

      write_byte(CONFIG3, 0xE0);   // Ссылка на INT включена: E0, иначе ссылка на INT отключена: 60

      delay(1000);
      //эта часть предназначена только для проверки того, правильно ли вы отправляете и читаете данные
      Serial.println("Check Configs");
      chSet = read_byte(CONFIG1);
      Serial.println("CONFIG1: Received " + hex_to_char(chSet) );
      chSet = read_byte(CONFIG2);
      Serial.println("CONFIG2: Received "  + hex_to_char(chSet) );
      chSet = read_byte(CONFIG3);
      Serial.println("CONFIG3: Received "  + hex_to_char(chSet) );

      // помните, что ch1 используется для дыхания! используйте канал 2 для измерений ЭКГ

       // Настройка Ch1: Биты 7 6 5 4 3 2 1 0 --- PDn (режим мощности канала) GAINn[2:0] (усиление PGA -- 101: 12 110 : 24)
       //SRB2 (подключение SRB2) MUXn[2:0] (выбор входа канала: 000: нормальный вход электрода, 101: тестовый сигнал)

       /// Входной канал закорочен ---> 0b00000001 0X01; Тестовый сигнал активирован ---> 0b00000101 0X05;
      delay(1000);

      write_byte(CH1SET, 0x05); //
      write_byte(CH2SET, 0x05); //
      write_byte(CH3SET, 0x05); //
      write_byte(CH4SET, 0x05); //
      write_byte(CH5SET, 0x05); //
      write_byte(CH6SET, 0x05); //
      write_byte(CH7SET, 0x05); //
      write_byte(CH8SET, 0x05); //


      Serial.println("Check Channel Settings");
      chSet = read_byte(CH1SET);
      Serial.println("Ch1: Received " + hex_to_char(chSet) );
      chSet = read_byte(CH2SET);
      Serial.println("Ch2: Received  " +   hex_to_char(chSet) );
      chSet = read_byte(CH7SET);
      Serial.println("Ch7: Received  " +   hex_to_char(chSet) );
      chSet = read_byte(CH8SET);
      Serial.println("Ch8: Received  " +   hex_to_char(chSet) );

      // Запускаем связь, можно использовать RDATAC или RDATA согласно даташиту
      send_command(RDATAC);
      digitalWrite(PIN_START, LOW);
      delay(150);
      send_command(START);  
    // цифровая запись (PIN_CS, LOW); //От низкого до сообщенного
    // SPI.transfer(START);
    // digitalWrite(PIN_CS, HIGH); //От низкого до сообщенного

    }

    boolean gActiveChan [2];
    int nChannels = 1;
    int gMaxChan=1;



    void loop(){

    // На основе страницы. 36 Для 8-канального ADS1299 количество выходов данных равно
    //[(24 бита состояния + 24 бита × 8 каналов) = 216 бит].

      delayMicroseconds(10); 

         if(digitalRead(IPIN_DRDY) == LOW){
          t0 = micros();

            digitalWrite(PIN_CS, LOW);

            long output[9];
            long dataPacket;
            for(int i = 0; i<9; i++){
                for(int j = 0; j<3; j++){
                    byte dataByte = SPI.transfer(0x00);
                    dataPacket = (dataPacket<<8) | dataByte;
                }

                output[i] = dataPacket;
                dataPacket = 0;
            }

            digitalWrite(PIN_CS, HIGH);
            Serial.println(output[1]);

    // t = micros()-t0; // вычислить прошедшее время
   // Serial.print(1000000/float(t));
    //Serial.println("выборки в секунду");
    // задержка (200);


     }

            }


        String hex_to_char(int hex_in) {
      int precision = 2;
      char tmp[16];
      char format[128];
      sprintf(format, "0x%%.%dX", precision);
      sprintf(tmp, format, hex_in);
      //Serial.print(tmp);
      return(String(tmp));
    }

    // см. спецификацию 38
    int read_byte(int reg_addr){
      int out = 0;
      digitalWrite(PIN_CS, LOW);
      SPI.transfer(0x20 | reg_addr);
      delayMicroseconds(5);
      SPI.transfer(0x00);
      delayMicroseconds(5);
      out = SPI.transfer(0x00);
      delayMicroseconds(1);
      digitalWrite(PIN_CS, HIGH);


      return(out);
    }

    void send_command(uint8_t cmd) {
      digitalWrite(PIN_CS, LOW);
      delayMicroseconds(5); 
      SPI.transfer(cmd);
      delayMicroseconds(10);
      digitalWrite(PIN_CS, HIGH);
    }

    //см. стр. 38
    void write_byte(int reg_addr, int val_hex) {
      digitalWrite(PIN_CS, LOW);
      delayMicroseconds(5);
      SPI.transfer(0x40 | reg_addr);
      delayMicroseconds(5);
      SPI.transfer(0x00);
      delayMicroseconds(5);
      SPI.transfer(val_hex);
      delayMicroseconds(10);
      digitalWrite(PIN_CS, HIGH);

    }

, 👍0

Обсуждение

Да (хотя и ограничено небольшим объемом SRAM), да (более быстрые, чем Uno), и пробовали ли вы использовать более высокие скорости передачи данных? Какую скорость вы на самом деле используете? А может и не печатать, а написать в Serial, @chrisl

Пожалуйста, предоставьте полный код. И почему вы печатаете только второй элемент output?, @chrisl

@chris: не могли бы вы помочь мне, как я могу сохранить вывод [1] в цикле и последовательно напечатать большой массив через указанное время. в этом коде я просто печатаю один канал, но я могу распечатать весь канал, разделенный запятой, а затем получить их на хост-компьютере., @Abed

@chris Я пытался отправить весь код, но текст не очень хорош. Можно ли загрузить код отдельно?, @Abed

@chrisl Я выясняю и добавляю весь код. Я проверил с разной скоростью передачи данных, но не заметил явного влияния на частоту дискретизации., @Abed


1 ответ


-1

Попробуйте изменить их порядок:

  SPI.setDataMode(SPI_MODE1);
  //SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.setBitOrder(MSBFIRST);
  SPI.begin();

Я думаю, SPI.begin() нужно запускать до двух других вызовов.

Вместо digitalWrite для управления выбором микросхемы используйте Direct Port Manipulation, что намного быстрее.

Поддерживает ли АЦП тактовую частоту SPI 8 МГц? От 7,6 & 7.7 даташита, я бы сказал да. Вы также можете избавиться от всех delayMicroseconds(5), (1), (10), они ничего для вас не делают.

"Распаковать" это и просто выполнить 3 SPI.transfers подряд, чтобы считать данные, выполнение в цикле замедляет процесс:

        for(int i = 0; i<9; i++){
            for(int j = 0; j<3; j++){
                byte dataByte = SPI.transfer(0x00);
                dataPacket = (dataPacket<<8) | dataByte;
            }

Я бы зашел так далеко, что сделал бы все 27 строк четными, а затем выполнил бы манипуляцию с массивом/объединение данных, когда все 27 байтов были бы захвачены.

,

Спасибо, как я могу поместить все 27 байтов в массив?, @Abed

dataByte[0] = SPI.transfer(0x00); dataByte[1] = SPI.transfer(0x00); dataByte[2] = SPI.transfer(0x00); ... dataByte[25] = SPI.transfer(0x00); dataByte[26] = SPI.transfer(0x00);, @CrossRoads

Спасибо вам за вашу помощь. Мне удалось сохранить кучу байтов (например, 270 байт) в части чтения и отправить массив большого размера в следующей части. Но проблема в том, что при отправке большого массива я теряю новые входящие данные. Есть ли способ определить прерывание и выполнить многозадачность (отправка и получение)?, @Abed

ЗадержкаMicroсекунды предназначена для того, чтобы дать микросхеме АЦП время для загрузки своих регистров. 5uS может занять немного больше времени, но потребуется некоторая задержка. Вам следует обратиться к техническому описанию АЦП., @Delta_G

@Кровать. Последовательные данные уже управляются прерываниями. Когда вы вызываете Serial.print, все, что вы делаете, это загружаете буфер. Он становится блокирующим только в том случае, если буфер уже заполнен. Если это то, что происходит, то проблема в том, что у вас больше данных, чем вы успеваете отправить. Добавление большего количества прерываний не решит эту проблему., @Delta_G