нужно сравнить, если байт меньше 80 шестнадцатеричных

arduino-nano temperature bit bytes

Я использую инфракрасный датчик 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;
     }

, 👍1

Обсуждение

Какой тип данных у TA?, @Majenko

на нано расчеты int 16 битные. Умножение на *65536 не будет работать должным образом, если вы не введете его в 32-битную арифметику., @DataFiddler

@Majenko Я определил ТА как поплавок., @Santiago Restrepo Serna

Когда вы вычисляете «TA», вы выполняете целочисленную математику. Попробуйте, как написал DataFiddler, и приведите числа к длинным, чтобы результат не мог переполниться, @chrisl

@DataFiddler: умножение на 65536 будет работать, как и ожидалось, потому что 65536 — это 32-битное длинное целое число., @Edgar Bonet


2 ответа


1

Вы сказали: "Я определил ТА как число с плавающей запятой". Это не имеет значения. В 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


1

Правила целочисленной арифметики в 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