Невозможно считывать данные с 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);
}
}
@user7187418, 👍2
Обсуждение3 ответа
Лучший ответ:
Я вижу здесь несколько проблем.
Выход датчика
В соответствии с осциллограммами, показанными вашим логическим анализатором, датчик выдает 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
Это не ответ на ваш вопрос, но я не могу добавить его в качестве замечания, так как он включает в себя код.
Вы можете значительно сократить свой код, используя циклы 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++)
Если кто-то наткнулся на эту тему позже, как я, может быть полезно понять, что двоичный ответ-это десятичное число, особенно с разрешением 0.25 c. Это означает, что целочисленное значение, которое даст вам (отличный) ответ Эдгара Бонета, нужно умножить на 0,25, чтобы получить фактическую температуру в градусах Цельсия.
Я предлагаю умножить на 0,25, потому что это даст тот же результат, что и деление на 4, но без риска ошибки деления на ноль. Кроме того, деление происходит чрезвычайно медленно на Arduino, так как базовый набор команд не имеет команды деления.
Я надеюсь, что это кому-то поможет в какой-то момент; если не извините за раздражение.
- Arduino UNO + Ethernet Shield + ЖК-дисплей + RFID, но RFID не работает
- AMIS30543 - Код библиотеки | Порядок pinDirection и pinOut
- АМИС30543 | Понимание таблицы данных Регистр состояния
- Как использовать SPI на Arduino?
- Библиотека DHT.h не импортируется
- OVF в последовательном мониторе вместо данных
- Как вызвать функции C из скетча ардуино?
- Как отправить строку с подчиненного устройства Arduino с помощью SPI?
судя по осциллограммам, мне кажется, что датчик выдает 0b0011111111111111 = 0x3fff., @Edgar Bonet
Первый 0 это ведущий ноль в соответствии с таблицей данных который не добавляет к показаниям датчика, @user7187418
Я знаю, я читал спецификацию. Я просто говорю, что не согласен с вашим утверждением: “датчик показывает только выход”01000000000""., @Edgar Bonet
- Прошу прощения, - поправил я его., @user7187418