Синхронизация SPI с Arduino DUE и ADIS16364 IMU

Я пытаюсь использовать Arduino Due интерфейс с ADIS16364 IMU от Analog Devices (информация о продукте здесь) через SPI.

Существует два режима получения измерений из этого конкретного ИДУ (шесть 16-битных значений, 3 асс, 3 гироскопа):

  1. Первый режим-это, если я не ошибаюсь, стандартный режим с SPI, в котором каждая последующая команда передачи извлекает последние запрошенные данные и указывает, какие из них следует извлечь следующими.

  2. Вторая возможность - это так называемый "серийный режим". Для этого вам нужно запросить регистр 0x3E00. После этого ИДУ выдаст шесть измерений (плюс несколько других битов информации, таких как температура и напряжение питания). Из спецификации:

Проблема в том, что мне не удается заставить Arduino считывать данные, используя этот пакетный режим. Вот код, который я использую для проверки связи:

#include <SPI.h>
#define SerialBT Serial2

short supply, xgyro, ygyro, zgyro, xacc, yacc, zacc, xtemp, ytemp, ztemp, aux;
volatile byte fDataIMUtoRead = 0;
int pauseBetweenTransfers;

void setup() {

  SerialBT.begin(230400);

  pinMode(52, OUTPUT);

  // Настройка SPI
  SPI.begin(52);
  SPI.setClockDivider(52, 200); // 84e6/200 = 420 кГц
  SPI.setDataMode(52, SPI_MODE3);
  SPI.setBitOrder(52, MSBFIRST);

  // Заданная частота дискретизации
  float Fs = 1; // Целевая частота дискретизации [Гц]
  float tb = 0.61035 * (Fs >= 12.7009) + 18.921 * (Fs < 12.7009);
  unsigned long tbsel = 0 * (Fs >= 12.7009) + 1 * (Fs < 12.7009);
  unsigned long Ns = (uint16_t) round((1000.0 - tb * Fs) / (tb * Fs));
  float Feff = 1000 / (tb * (Ns + 1));
  SerialBT.print("Selected freq: ");
  SerialBT.println(Feff, DEC);

  digitalWrite(52, LOW);  // Похоже, в этом нет необходимости
  SPI.transfer16(52, (0L << 15) | (0x36L << 8));                       delayMicroseconds(100);
  SPI.transfer16(52, (1L << 15) | (0x36L << 8) | (tbsel << 7) | Ns );  delayMicroseconds(100);
  SPI.transfer16(52, (0L << 15) | (0x36L << 8));                       delayMicroseconds(100);
  zgyro = SPI.transfer16(52, 0x00);
  digitalWrite(52, HIGH);  

  SerialBT.print("SMPL_PRD = ");
  SerialBT.println(zgyro, HEX);

  attachInterrupt(3, availableIMUData, RISING);
}

void loop() {

if (fDataIMUtoRead) {

SerialBT.print("Loop... ");

// Считывание значений (стандартный режим)  
pauseBetweenTransfers = 100;
digitalWrite(52, LOW); // Похоже, в этом нет необходимости

        SPI.transfer16(52, 0x0400, SPI_CONTINUE); delayMicroseconds(pauseBetweenTransfers);
xgyro  = SPI.transfer16(52, 0x0600, SPI_CONTINUE); delayMicroseconds(pauseBetweenTransfers);
ygyro  = SPI.transfer16(52, 0x0800, SPI_CONTINUE); delayMicroseconds(pauseBetweenTransfers);
zgyro  = SPI.transfer16(52, 0x0A00, SPI_CONTINUE); delayMicroseconds(pauseBetweenTransfers);
xacc   = SPI.transfer16(52, 0x0C00, SPI_CONTINUE); delayMicroseconds(pauseBetweenTransfers);
yacc   = SPI.transfer16(52, 0x0E00, SPI_CONTINUE); delayMicroseconds(pauseBetweenTransfers);             
zacc   = SPI.transfer16(52, 0x0000, SPI_CONTINUE);
  
digitalWrite(SS, HIGH);    

/*
// Read values (burst mode)
pauseBetweenTransfers = 5;
digitalWrite(52, LOW);    // Похоже, в этом нет необходимости 
  
         SPI.transfer16(52, 0x3E00);    delayMicroseconds(pauseBetweenTransfers);  // Поставка пакета данных запроса
supply = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
xgyro  = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
ygyro  = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
zgyro  = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
xacc   = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
yacc   = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
zacc   = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
xtemp  = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
ytemp  = SPI.transfer16(52, 0x0000, SPI_CONTINUE);  delayMicroseconds(pauseBetweenTransfers);
ztemp  = SPI.transfer16(52, 0x0000);
    
digitalWrite(52, HIGH);     
*/

SerialBT.println(zgyro, DEC); // Печать показаний

fDataIMUtoRead = 0; // Reser the "есть новые данные"-флаг

}    

}

void availableIMUData() {
fDataIMUtoRead = 1; // Установите флаг "есть новые данные" }

Стандартный способ связи, кажется, работает без каких-либо проблем, при условии, что я устанавливаю правильные задержки после каждой команды transfer16. Но я не могу заставить Arduino считывать данные в пакетном режиме (прокомментировано в коде выше).

Итак, есть ли у кого-нибудь идеи о том, как заставить Arduino получать показания в этом серийном режиме? Я понимаю, что это сложно, если у вас нет (был) доступ к тому же оборудованию, но любое предложение будет приветствоваться.

Большое спасибо.

, 👍3

Обсуждение

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

Спасибо, маг времени. К сожалению, у меня нет возможности. Только дешевый логический анализатор, который не очень помог, @Guillermo Benito


1 ответ


3

Прежде всего, это может не иметь отношения к вашему вопросу, но вы используете старые методы SPI setClockDivider(), setDataMode ()и setBitOrder (), которые устарели с 2014 года. Рекомендуется использовать use transactional SPI API, которые у меня есть некоторые объяснения здесь.

Зачем мне на самом деле нужны эти задержки?

Если вы читаете страницу 6 спецификацииADIS16364, то между каждой передачей данных существует время tSTALL, которое может достигать 75uS (при работе в режиме низкого энергопотребления), это значение может быть намного ниже, если вы работаете в нормальном или спадающем режиме.

почему такая комбинация задержек с делителем часов 200?

Тактовое деление устанавливает часы, используемые для скорости SPI SCLK, например, тактовое деление 200 означает 84 000 000/200 = 420 000 Гц, почему именно эта скорость? опять же, вы должны заглянуть в таблицу данных на странице 6 таблицы 2, max fsck составляет 2 МГц для нормального режима и 1 МГц для режима бюста, и только 300 кГц в режиме низкой мощности.

Разве это не должно быть сделано автоматически библиотекой SPI?

Это неправильное восприятие, библиотека SPI не делает ничего, кроме обработки сообщения. Все требования к времени зависят от конкретного компонента/чипа, и поэтому, если вы хотите общаться с чипом на уровне SPI-связи, вам придется иметь дело со всеми требованиями к времени самостоятельно. Большинство людей в сообществе Arduino привыкли использовать какую-то заранее написанную библиотеку, которая была написана кем-то, кто хочет прочитать таблицу данных и предоставить абстрактный API высокого уровня, чтобы пользователям не нужно было иметь дело со всеми сроками.

Есть идеи о том, как заставить Arduino получать показания в этом серийном режиме?

Обновить

Как следует из таблицы данных, "Чтобы начать последовательность пакетного чтения, установите DIN = 0x3E00", это означает, что вам нужно сначала ЗАПИСАТЬ в регистр 0x3E00, чтобы настроить режим пакетного чтения. На рис. 11 таблицы данных указано, что операция ЗАПИСИ будет иметь значение 1, поэтому вам нужно будет отправить 0x3E00 | 0x8000 или 0xAE00 для записи в регистр 0x3E00. То, что вы сделали, - это в основном чтение формы 0x3E00, а не запись в 0x3E00.

Я перечитал таблицу данных, и вы правы, должно быть нормально просто отправить 0x3E00. Я никогда раньше не использовал Arduino Due, но понял, что SPI.transfer16() может сначала отправить 0x00 вместо 0x3E. Я изменил код, чтобы отправить два байта 0x3E и 0x00 отдельно, используя SPI.transfer()

У меня нет ИДУ, но я думаю, что следующий код должен работать для режима бюста:

SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));    //инициализирует тактовую частоту SPI, режим и порядок битов
digitalWrite(SS, LOW);              // отрегулируйте это в соответствии с вашим чипом select pin
SPI.transfer(0x3E);
SPI.transfer(0x00);
delayMicroseconds(1);               //режим бюста требуется только 1uS в соответствии с таблицей
xgyro_out = SPI.transfer16(0x0000);
delayMicroseconds(1);
ygyro_out = SPI.transfer16(0x0000);  
delayMicroseconds(1);
zgyro_out = SPI.transfer16(0x0000);  
delayMicroseconds(1);
xaccl_out = SPI.transfer16(0x0000);  
delayMicroseconds(1);
yaccl_out = SPI.transfer16(0x0000);  
delayMicroseconds(1);
zaccl_out = SPI.transfer16(0x0000);  
delayMicroseconds(1);
data16    = SPI.transfer16(0x0000);  
delayMicroseconds(1);
data16    = SPI.transfer16(0x0000);  
delayMicroseconds(1);
data16    = SPI.transfer16(0x0000);  
delayMicroseconds(1);
data16    = SPI.transfer16(0x0000);  
delayMicroseconds(1);
digitalWrite(SS, HIGH);   // отменить выбор ведомого
SPI.endTransaction();
,

Спасибо за подробный ответ, чхеунг. Ваше указание на ЗАПИСЬ в 0x3e00 казалось ключевым, но это не решило проблему. После повторной проверки спецификации на странице 12 конец первого абзаца второго столбца: "Чтение регистра GLOB_CMD (DIN = 0x3E00) запускает последовательность пакетного чтения". Таким образом, SPI.transfer16(0x3E00) действительно должен выполнить эту работу, если я все правильно понял. Есть еще какие-нибудь идеи?, @Guillermo Benito

Я использую старый синтаксис для связи SPI, потому что я просто не смог заставить новую команду SPI.beginTransaction работать. Понятия не имею, что я делаю не так. Однако старый, кажется, работает нормально (если вы не видите никаких причин, по которым это должно мешать работе серийного режима)., @Guillermo Benito

Только вчера я обнаружил, что при частоте дискретизации до 150 Гц ИДУ все еще работает в маломощном режиме, что объясняет высокие задержки, необходимые, как вы предполагаете., @Guillermo Benito

Вы правы, должно быть нормально просто отправить read cmd в 0x3E00. Пожалуйста, смотрите мой ответ на обновление, а также изменение кода для отправки двух байтов (0x3E, 0x00) отдельно. Пожалуйста, попробуйте, я думаю, это должно сработать., @hcheung

Старые API все еще работают, даже если они устарели, если на вашей шине SPI нет нескольких устройств SPI. SPI.begintransaction предназначен для работы нескольких устройств, каждое из которых имеет свои собственные параметры конфигурации., @hcheung

Я не думаю, что тебе нужно менять порядок этих двух байтов, хчеунг. Я пробовал, на всякий случай, но это не возымело никакого эффекта. Чтение в стандартном режиме отлично работает со строками типа {SPI.transfer16(52, 0x0A00, SPI_CONTINUE)}, @Guillermo Benito

Я отредактировал основной вопрос с полным кодом, который я использую для тестирования, как в стандартном, так и в серийном режимах, на всякий случай, если вы видите там что-то не так., @Guillermo Benito