Использование 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);
}
@Abed, 👍0
Обсуждение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
- Взаимодействие ADS8319 с Arduino UNO
- Получение неправильного вывода от АЦП через SPI в Arduino Uno
- Библиотека AD7768-1 "Ардуино"
- Регистр ADCH Arduino Uno завис на значении 255 при чтении из ISR
- Если я использую схему смещения для сигнала, поступающего на аналоговый вход, повлияет ли это на работу АЦП?
- Максимальная частота, которую можно преобразовать с помощью Arduino Uno?
- Как использовать SPI на Arduino?
- OVF в последовательном мониторе вместо данных
Да (хотя и ограничено небольшим объемом SRAM), да (более быстрые, чем Uno), и пробовали ли вы использовать более высокие скорости передачи данных? Какую скорость вы на самом деле используете? А может и не печатать, а написать в Serial, @chrisl
Пожалуйста, предоставьте полный код. И почему вы печатаете только второй элемент
output
?, @chrisl@chris: не могли бы вы помочь мне, как я могу сохранить вывод [1] в цикле и последовательно напечатать большой массив через указанное время. в этом коде я просто печатаю один канал, но я могу распечатать весь канал, разделенный запятой, а затем получить их на хост-компьютере., @Abed
@chris Я пытался отправить весь код, но текст не очень хорош. Можно ли загрузить код отдельно?, @Abed
@chrisl Я выясняю и добавляю весь код. Я проверил с разной скоростью передачи данных, но не заметил явного влияния на частоту дискретизации., @Abed