нужно сравнить, если байт меньше 80 шестнадцатеричных
Я использую инфракрасный датчик OTI301. В его техническом паспорте говорится, что для получения значений температуры объекта и температуры окружающей среды мне нужно извлечь двоичную информацию из датчика и использовать двоичную информацию в заданных формулах. Я сделал все это, но по какой-то причине я не получаю правильных значений температуры объекта. Температура окружающей среды, кажется, работает хорошо, но когда я помещаю горячий объект, более 320 F, формула возвращает случайное отрицательное число. Кроме того, датчик может нагреваться до 780 F, поэтому я знаю, что температура не ограничена. Пожалуйста, смотрите фотографии для получения важной информации о технических характеристиках, или вот ссылки на полные технические характеристики. https://drive.google.com/file/d/1XMRDCNzY3fn0q6lkGejs5D-pqksfRuZ8/view? usp=совместное использование. https://drive.google.com/file/d/1mmdkHkNbwC5VgQxGMrq07XolajNvgBkN/view?usp=sharing. Наконец, вот часть кода, где я извлекаю информацию и применяю формулы.
void loop()
{
if (Wire.read()) // On success, read() will return 1, on fail 0.
{
buttonState = digitalRead(button);
//Serial.println(buttonState);
Wire.beginTransmission(DEVICE_ADDRESS);
Wire.write(0x80); // readout command
Wire.endTransmission(false);
Wire.requestFrom(DEVICE_ADDRESS, 6); // request 6 bytes
uint8_t data[6];
for (int i = 0; i < 6; i++) {
data[i] = Wire.read();
}
//Calculate Object Temperature
if(data[5] < 0x80) {
TA = (data[3]+ data[4]*256+data[5]*65536)/200;
}
else {
TA = (((data[3]+ data[4]*256+data[5]*65536)) - 16777216) /200;
}
//Calculate Ambient Temperature
if(data[2] < 0x80) {
TA = (data[0]+ data[1]*256+data[2]*65536)/200;
}
else {
TA = (((data[0]+ data[1]*256+data[2]*65536)) - 16777216) /200;
}
2 ответа
Вы сказали: "Я определил ТА как число с плавающей запятой". Это не имеет значения. В C/C++ выражения только "продвигаются" до более крупного/другого типа данных ПОСЛЕ их оценки. Ваши расчеты выполняются с использованием 16-битных целых чисел, которые переполняются и затем преобразуются в число с плавающей запятой.
(C/C++ оценивает выражения от самого внутреннего к внешнему, и если один из операндов оператора имеет более крупный тип, он "повысит" другой операнд до более крупного типа перед выполнением вычислений. Это не заметит, что вычисление переполнится и переместит операнды, чтобы решить эту проблему.)
Попробуйте это:
TA = (data[3]+ data[4]*256+uint64(data[5])*65536)/200;
В этой версии кода data[5] преобразуется в тип unsigned long перед умножением на 65536.
Проделайте то же самое со значением, которое вы умножаете на 65 536 во всех ваших выражениях, и вы должны избежать переполнения.
Спасибо вам всем. задача решена!, @Santiago Restrepo Serna
1. На Nano uint64
(я думаю, вы имеете в виду uint64_t
) не то же самое, что unsigned long
: последнее только 32-битное. 2. Переполнение происходит с data[4]
, а не с data[5]
. 3. Ваша формула дает очень плохие результаты для температур выше 163,84 °C (попробуйте данные = {0,0,0,0,128,0})., @Edgar Bonet
переполнение данных (4)? Данные - это целое число или байт?, @Duncan C
Правила целочисленной арифметики в C++ сложны. Есть правила, которые указать тип целочисленных литералов:
256
и200
имеют типint
, который является типом по умолчанию для целочисленные литералы. На Nano этот тип является 16-битным и охватывает диапазон от −32 768 до +32 767.65536
слишком большой для типаint
, он неявно считаетсядлинным целым числом
. Этот тип является 32-битным на Nano и охватывает примерно ±2,15 × 109.
Затем существуют правила для типов, используемых в арифметических операциях. Нет
вычисление всегда выполняется для типа, меньшего, чем int
: все, что меньше
повышается до int
. Тогда, если вы объедините два операнда разных
типы, более крупный тип «выигрывает», и компилятор неявно преобразует
другой операнд к большему типу. Обратите внимание, что если два типа имеют одинаковые
размер в битах, беззнаковый считается «больше».
Давайте рассмотрим вашу формулу и посмотрим на типы различных условия:
data[3] = uint8_t
data[4]*256 = uint8_t * int → int
data[5]*65536 = uint8_t * (long int) → long int
Теперь вы можете увидеть проблему. Ваш расчет в конечном итоге переполнится,
но переполнение не в data[5]*65536
, как можно наивно ожидать.
Что происходит, когда температура достигает 163,84 ° C (≈ 327 ° F),
data[4]
становится 128, а data[4]*256
— 32 768, слишком большим для
int
. Результат переполняется до -128, следовательно, отрицательные значения, которые вы
см.
Самое простое решение — заставить 256 быть long int
, добавив
суффикс L
к номеру. Тогда будет выполняться умножение
с типом long int
, и не будет переполнения:
TA = (data[3] + data[4]*256L + data[5]*65536) / 200;
В заключение стоит отметить, что числа 256, 65536 и 16777216, используемые в формуле, являются всеми степенями двойки. Даже степени 256. Вы можете заметить, что за исключением деления на 200, что эта формула описывает кодирование целого числа как 24-битного, с прямым порядком байтов, двойки дополнить номер. Этот формат (дополнение до двух с прямым порядком байтов) именно то, что Arduino использует внутри для представления чисел. Ты можно было бы избежать всех этих вычислений и получить результат, просто сообщая компилятору, что эти ячейки памяти на самом деле содержат представляет собой 24-битное целое число:
float TA = *(__int24*)&data[3] / 200.0;
Это работает как для положительных, так и для отрицательных температур, но имеет
недостатком нарушения некоторых правил C++ (правил алиасинга) и использования
нестандартный тип __int24
. Самый безопасный способ добиться того же —
построить 32-битное целое, поставив биты на место с помощью побитового
операции. Но обратите внимание, что тогда байт расширения знака необходим для
преобразовать 24-битное число в 32-битное:
uint8_t sign_extension = data[5] & 0x80 ? 0xff : 0x00;
int32_t reading = (uint32_t) data[3] << 0
| (uint32_t) data[4] << 8
| (uint32_t) data[5] << 16
| (uint32_t) sign_extension << 24;
float TA = reading / 200.0;
int32_t чтение = (int32_t)(int8_t)b[5] <<16 | (uint16_t)b[4] << 8 | б[3] ;
старший байт просто должен быть подписан, тогда это все еще сложно, но немного проще., @DataFiddler
- Использование массивов, двоичных данных и битового чтения
- Изменение одного бита в байтовом массиве
- Как изменить созданный байт?
- Передать массив байтов в Char и отправить на отдельный Arduino
- Как инициализировать сброс на цифровом термометре sht30?
- Вход Последовательного Монитора Arduino, шифрование 3DES
- DS18B20 работает ненадежно.
- Все ли эти битовые настройки означают одно и то же?
Какой тип данных у TA?, @Majenko
на нано расчеты int 16 битные. Умножение на *65536 не будет работать должным образом, если вы не введете его в 32-битную арифметику., @DataFiddler
@Majenko Я определил ТА как поплавок., @Santiago Restrepo Serna
Когда вы вычисляете «TA», вы выполняете целочисленную математику. Попробуйте, как написал DataFiddler, и приведите числа к длинным, чтобы результат не мог переполниться, @chrisl
@DataFiddler: умножение на 65536 будет работать, как и ожидалось, потому что 65536 — это 32-битное длинное целое число., @Edgar Bonet