Невозможно считывать данные с 16 последовательными тактами

Я пытаюсь считывать данные с датчика температуры AD7314, подключенного к Arduino Uno, и выводить это значение на 6 светодиодов, также подключенных к плате Arduino Uno. Из приведенного ниже изображения видно, что датчик показывает только выход 0011111111111111 во время потоков 16 тактов даже после того, как включение чипа переходит от низкого уровня к высокому перед преобразованием, чтобы инициировать новый (кажется, что преобразование застряло). В таблице данных говорится:

Во время операции чтения передаются ведущий ноль и 10 бит данных. Операции считывания происходят в потоках из 16 тактовых импульсов. Выходные данные обновляются на восходящем фронте SCLK. Доступ к последовательным данным осуществляется в количестве байтов, если считывается 10 бит данных. В конце операции чтения линия SDO остается в состоянии последнего бита данных, тактово переданного из AD7314, до тех пор, пока CE не вернется на низкий уровень

Я не уверен, где именно я ошибаюсь во время операции чтения:

Примечание: для изображения формы волны (сверху вниз):

  • форма волны 1: Включение чипа

  • форма волны 2: Последовательные часы

  • форма волны 3: Линия МИСО

  • форма волны 4: Линия MOSI

Мой код приведен ниже:

#include <SPI.h>

const int MOSI_PIN = 11;
const int MISO_PIN = 12;
const int SCK_PIN = 13;
const int Slave_PIN = A0;

// module data
uint16_t myValFromTempSensor = 0;

void setup () {
  SPI.begin ();
  SPI.beginTransaction(SPISettings(125000, MSBFIRST, SPI_MODE0));
  pinMode(5, OUTPUT); //LED 3
  pinMode(6, OUTPUT); //LED 4
  pinMode(7, OUTPUT); //LED 5
  pinMode(8, OUTPUT); //LED 6
  pinMode(9, OUTPUT); //LED 7
  pinMode(10, OUTPUT);//LED 8
  pinMode(MOSI_PIN, OUTPUT); // MOSI to SDI
  pinMode(MISO_PIN, INPUT); //MISO to SDO
  pinMode(SCK_PIN, OUTPUT); //CLOCK PIN to SCK
  pinMode(Slave_PIN,OUTPUT); //Slave pin

  digitalWrite(Slave_PIN, LOW);//Set slave low before communication
  for (int i = 5; i < 11; i++) {
    digitalWrite(i, LOW);
  }
  delay(100);
} // end of setup

void loop () {
  digitalWrite(Slave_PIN, HIGH);  //Set slave high to read data
  myValFromTempSensor = SPI.transfer(0x00); // Read Data from the device
  myValFromTempSensor |= (uint8_t)SPI.transfer(0x00) << 8;// Bitshift to read next 8 bits
  digitalWrite(Slave_PIN, LOW);
  if(( myValFromTempSensor>=0) && ( myValFromTempSensor<=196)) {
    //Range 0C to 60C is  0 to 196
    digitalWrite(5, bitRead(myValFromTempSensor,0));
    digitalWrite(6, bitRead(myValFromTempSensor,1));
    digitalWrite(7, bitRead(myValFromTempSensor,2));
    digitalWrite(8, bitRead( myValFromTempSensor,3));
    digitalWrite(9, bitRead( myValFromTempSensor,4));
    digitalWrite(10, bitRead( myValFromTempSensor,5));
  } else {
    digitalWrite(5, 0);
    digitalWrite(6, 0);
    digitalWrite(7, 0);
    digitalWrite(8, 0);
    digitalWrite(9, 0);
    digitalWrite(10, 0);
  }
}

SPI Waveform

, 👍2

Обсуждение

судя по осциллограммам, мне кажется, что датчик выдает 0b0011111111111111 = 0x3fff., @Edgar Bonet

Первый 0 это ведущий ноль в соответствии с таблицей данных который не добавляет к показаниям датчика, @user7187418

Я знаю, я читал спецификацию. Я просто говорю, что не согласен с вашим утверждением: “датчик показывает только выход”01000000000""., @Edgar Bonet

- Прошу прощения, - поправил я его., @user7187418


3 ответа


Лучший ответ:

1

Я вижу здесь несколько проблем.

Выход датчика

В соответствии с осциллограммами, показанными вашим логическим анализатором, датчик выдает 0b001111111111111111. Мы должны отбросить первый (заполняющий ноль) и последние пять битов (копии DB0). Затем мы получаем фактические биты данных 0111111111. Это должно быть интерпретировано как знаковое число с фиксированной точкой в формате s.7.2, которое дает 127,75 °C: максимально возможное значение, которое может выдать датчик, и значительно выше его максимальной рабочей температуры.

Я не могу сказать, что здесь происходит не так. Похоже, датчик либо сгорает заживо, либо пытается сказать вам, что он крайне несчастлив.

Режим SPI

Вы написали:

SPISettings(125000, MSBFIRST, SPI_MODE0)

SPI mode 0 кажется довольно распространенным явлением. В этом режиме строки данных обновляются на падающем фронте часов, а считываются на восходящем фронте. Однако, согласно спецификации вашего датчика, похоже , что этот датчик работает наоборот: он использует режим SPI 1. Ваши настройки должны отражать это.

Декодирование данных

Вы написали:

myValFromTempSensor = SPI.transfer(0x00);
myValFromTempSensor |= (uint8_t)SPI.transfer(0x00) << 8;

Здесь вы предполагаете, что наименее значимый байт передается первым. Это не так: вы получаете поток из 16 бит с самым значительным первым. Таким образом, с точки зрения байтов, это также MSB first.

Затем 6 из этих 16 битов должны быть отброшены, чтобы получить фактические 10 битов данных. Поскольку Arduino не имеет 10-битного типа данных, а число подписано, оно должно быть расширено до 16 бит. Все это можно сделать на C/C++ с помощью:

  • left-сдвиг на один бит влево (отбрасывание бита заполнения нуля)
  • приведение к 16-битному целому числу со знаком
  • сдвиг вправо (который gcc реализует с помощью расширения знака) шесть бит вправо

Или, в коде:

digitalWrite(Slave_PIN, HIGH);
uint16_t raw_data = SPI.transfer16(0);
digitalWrite(Slave_PIN, LOW);
int temperature_reading = (int16_t)(raw_data << 1) >> 6;

Edit: Вот более подробная информация о представлении данных и о преобразовании в 16-битное выражение (int16_t)(raw_data << 1) >> 6.

Согласно техническому описанию, датчик выдает температуру в виде 10-битной знаковое число в системе счисления двойки. Если вы не признаете тот факт, что результат подписан, и интерпретируете 10 бит как число без знака, то температура -1 °C будет интерпретироваться как +255 °C, что, очевидно, очень неправильно.

Поскольку это знаковое число, увеличение разрядности температуры до 16 бит должно быть сделано с использованием расширения знака, т. Е. Недостающие позиции битов должны быть заполнены копиями крайнего левого бита (“знаковый бит”).

В таблице данных также показано, что датчик посылает поток из 16 бит следующим образом:

0 db9 db8 db7 db6 db5 db4 db3 db2 db1 db0 db0 db0 db0 db0 db0

где первый бит – это заполняющий ноль, затем идут десять битов данных db9- db0, а затем пять дополнительных копий последнего бита данных. Эти дополнительные копии можно увидеть на рис.3 ( диграмма последовательного интерфейса), а также можно сделать вывод из предложения “линия SDO остается в состоянии последнего бита данных, синхронизированного с AD7314”.

Линия

uint16_t raw_data = SPI.transfer16(0);

помещает этот битовый шаблон в переменную raw_data, которая представляет собой 16- битное целое число без знака. Затем операция raw_data << 1 сдвигает этот шаблон на одну битовую позицию влево, отбрасывая крайний левый ноль и дополняя его другим нулем справа, что дает:

db9 db8 db7 db6 db5 db4 db3 db2 db1 db0 db0 db0 db0 db0 db0 0

Затем он преобразуется в знаковое число, которое не изменяет битовый шаблон, а только интерпретацию крайнего левого бита, который теперь является знаковым битом. Затем число сдвигается на 6 битных позиций вправо. Поскольку это арифметический сдвиг, который делает расширение знака, крайний левый бит (“знаковый бит”) реплицируется во время сдвига, что дает:

db9 db9 db9 db9 db9 db9 db9 db8 db7 db6 db5 db4 db3 db2 db1 db0

И это ожидаемый результат: то же самое числовое значение, которое первоначально хранилось в “регистре значений температуры” датчика, только представлено в виде 16-битного числа.

,

Я не уверен, откуда вы получаете 16 бит, а не 11 бит (включая ведущий ноль), если я правильно понял. В таблице данных упоминается, что 11 бит передаются во время потоков по 16 тактов., @user7187418

@user7187418: На аппаратном уровне порт SPI может обрабатывать только 8 бит, и он считывает входной бит на каждом такте. На уровне библиотеки метод transfer16 () посылает 16 тактовых импульсов и возвращает 16-битное число без знака., @Edgar Bonet

Итак, в строке кода : int temperature_reading = (int16_t)(raw_data << 1) >> 6; Означает ли это , что я получаю 10-битные подписанные данные, если да, то у меня есть 2 вопроса: 1) Если я правильно понял, transfer16() возвращает беззнаковое 16-битное число означает ли это, что 10 битовые данные температуры в коде, упомянутом выше, являются беззнаковым 10-битным чтением ? (мне нужно прочитать от 0 C до 60 C ) 2) Должен ли я выводить данные о температуре на 6 светодиодов, считывая только первые 6 бит с помощью функции bitRead (), как я делаю в настоящее время, или есть другой способ, о котором я в настоящее время не знаю ?, @user7187418

1) Вы, кажется, смущены представлением двоичных чисел. См.Мой расширенный ответ. 2) Только вы можете знать, как вы хотите отобразить температуру конечному пользователю. Я бы выводил данные через последовательный порт, по крайней мере, на этапе отладки., @Edgar Bonet


1

Это не ответ на ваш вопрос, но я не могу добавить его в качестве замечания, так как он включает в себя код.

Вы можете значительно сократить свой код, используя циклы for, например:

   pinMode(5, OUTPUT); //LED 3
   pinMode(6, OUTPUT); //LED 4
   pinMode(7, OUTPUT); //LED 5
   pinMode(8, OUTPUT); //LED 6
   pinMode(9, OUTPUT); //LED 7
   pinMode(10, OUTPUT);//LED 8  

Для

   int n;
   for (n = 5; n < 11; n++)
   {
      pinMode(n, OUTPUT);
   }

И

digitalWrite(5,bitRead( myValFromTempSensor,0));
digitalWrite(6,bitRead( myValFromTempSensor,1));
digitalWrite(7,bitRead( myValFromTempSensor,2));
digitalWrite(8,bitRead( myValFromTempSensor,3));
digitalWrite(9,bitRead( myValFromTempSensor,4));
digitalWrite(10,bitRead( myValFromTempSensor,5));

Для

int n = 0;
for (n = 0; n < 6; n++)
{
    digitalWrite(n + 5,bitRead( myValFromTempSensor, n));
}

И

  digitalWrite(5,0);
  digitalWrite(6,0);
  digitalWrite(7,0);
  digitalWrite(8,0);
  digitalWrite(9,0);
  digitalWrite(10,0);

Для

int n;
for (n = 5; n < 11; n++)
{
    digitalWrite(n, 0);
}

Вместо жестко закодированных значений 5 и 11 вы можете использовать определения, например:

#define LED_MIN 5
#define LED_MAX 11

и перемены

for (n = 5; n < 11; n++)

Для

for (n = LED_MIN; n < LED_MAX; n++)
,

1

Если кто-то наткнулся на эту тему позже, как я, может быть полезно понять, что двоичный ответ-это десятичное число, особенно с разрешением 0.25 c. Это означает, что целочисленное значение, которое даст вам (отличный) ответ Эдгара Бонета, нужно умножить на 0,25, чтобы получить фактическую температуру в градусах Цельсия.

Я предлагаю умножить на 0,25, потому что это даст тот же результат, что и деление на 4, но без риска ошибки деления на ноль. Кроме того, деление происходит чрезвычайно медленно на Arduino, так как базовый набор команд не имеет команды деления.

Я надеюсь, что это кому-то поможет в какой-то момент; если не извините за раздражение.

,