Как перевести float в четыре байта?
Я пытаюсь сохранить GPS-координаты в EEPROM.
В этом примере я передаю ему широту 56060066 в качестве аргумента с плавающей запятой x
void writeFloat(unsigned int addr, float x)
{
byte seriesOfBytes[3];
*((float *)seriesOfBytes) = x;
// Записываем все четыре байта.
for(int i = 0; i < 4; i++)
{
i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Запись байта в EEPROM
Serial.println(seriesOfBytes[i],BIN); // Строка отладки
}
}
Я ожидаю получить следующие четыре байта от серийной печати:
00000011
01010111
01101000
10100010
Вместо этого я получаю следующее:
00101000
11011010
01010101
01001100
Я пробовал менять всевозможные параметры, но не могу найти проблему. Кто-нибудь может определить проблему?
@blarg, 👍0
4 ответа
Лучший ответ:
Если бы вы включили отображение предупреждений, вы, скорее всего, увидели бы, что он жалуется на "разыменование указателя с символами типа нарушит строгие правила псевдонимов", что вы здесь и делаете. р>
По сути, вы пытаетесь преобразовать массив из четырех 8-битных значений, которые могут иметь любое выравнивание, которое им нравится (выравнивание по байтам), в 32-битное значение с плавающей запятой, для которого требуется выравнивание по 4 байтам. И они просто не сочетаются друг с другом.
Вместо этого вам нужно действовать наоборот — приводить тип с меньшими требованиями к выравниванию по сравнению с типом с более высокими требованиями.
Таким образом, вместо того, чтобы получать 4 байта и пытаться заполнить их как число с плавающей запятой, вы получаете число с плавающей запятой и читаете его как 4 байта:
byte *b = (byte *)&floatVal;
Тогда вы можете получить доступ от b[0] до b[3].
Еще один способ сделать это с объединением:
union {
float fval;
byte bval[4];
} floatAsBytes;
floatAsBytes.fval = floatVal;
EEPROM.write(0, floatAsBytes.bval[0]);
EEPROM.write(1, floatAsBytes.bval[1]);
EEPROM.write(2, floatAsBytes.bval[2]);
EEPROM.write(3, floatAsBytes.bval[3]);
Кроме того, в вашем коде вы выделили достаточно места только для 3 байтов, а не для 4...
Вы получаете именно то, что хотели. 4 байта, представляющие число в формате с плавающей запятой.
Вы ожидаете получить 4 байта, представляющих число в формате целого числа с обратным порядком байтов.
Используя решение Majenkos для приведения типов, вам нужно изменить типы и обратить цикл for
для большей конечности.
Я также использовал int32_t
, потому что размер <code>int</code> является 16-битным (2 байта) на Arduino Uno (и других платах на базе ATMega), а на Arduino Due — 32-битным (4 байта). В этом случае лучше определить точный int
.
union {
int32_t ival;
byte bval[4];
} int32AsBytes;
void writeInt32(unsigned int addr, int32_t x)
{
int32AsBytes.ival = x;
for(int i = 3; i >= 0; i--) // обратный порядок байтов
{
i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Запись байта в EEPROM
Serial.println(int32AsBytes.bval[i], BIN); // Строка отладки
}
}
Эта возня с приведениями и массивами слишком сложна.
См.: Чтение и запись структур данных в EEPROM
Для этого подойдет библиотека EEPROMAnything.h.
EEPROMAnything.h
Библиотека состоит только из этого файла. Сохраните в папку «библиотеки» под именем папки EEPROMAnything
.
#include <Arduino.h> // для определений типов
#include <EEPROM.h>
template <typename T> unsigned int EEPROM_writeAnything (int ee, const T& value)
{
const byte* p = (const byte*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
return i;
}
template <typename T> unsigned int EEPROM_readAnything (int ee, T& value)
{
byte* p = (byte*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
Пример кода
#include <EEPROM.h>
#include <EEPROMAnything.h>
void setup ()
{
Serial.begin (115200);
Serial.println ();
float foo = 56060066;
EEPROM_writeAnything (0, foo);
float bar;
EEPROM_readAnything (0, bar);
Serial.println (bar);
} // end of setup
void loop ()
{
} // end of loop
Вывод сверху
56060064.00
Обратите внимание, что вы не получите обратно точно такой же номер. Это связано с точностью 4-байтовых чисел с плавающей запятой.
После повторного прочтения вопроса я вижу, что вы используете I2C EEPROM. Это немного меняет ответ, но вы можете увидеть адаптацию этой библиотеки для I2C здесь. . Однако общая идея та же.
i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Запись байта в EEPROM
Похоже, ваш код записывает все байты на один и тот же адрес, я не знаю, как это может работать.
union Float {
float m_float;
uint8_t m_bytes[sizeof(float)];
};
float pi,pi1;
uint8_t bytes[sizeof(float)];
uint8_t bytes1[sizeof(float)];
Float myFloat;
#include <Wire.h>
#define disk1 0x50 //Адрес чипа 24LC256 eeprom
void setup(void)
{
unsigned int address = 0;
Serial.begin(9600);
Wire.begin();
pi = 300.78;
Serial.println("*******************************************");
Serial.println("pi = "+ String(pi));
Serial.println("***** Conversion by using type casting *****");
*(float*)(bytes) = pi;
for(int i=0;i<4;i++) Serial.println( bytes[i]);
Serial.println("*******************************************");
for( int i=0;i<4;i++){
writeEEPROM(disk1, address, bytes[i]);
address=address+sizeof(bytes[i]);
}
address=0;
for( int i=0;i<4;i++){
bytes1[i]=(readEEPROM(disk1, address));
address++;
Serial.println(bytes1[i]);
}
Serial.println("************Conver Byte to Float*************************");
pi1 = *(float*)(bytes1);
Serial.println("pi = "+String( pi1));
Serial.println("********* Conversion by using union *********");
myFloat.m_float = pi1; // присваиваем float объединению
Serial.println("myFloat.m_Float = "+String( myFloat.m_float));
Serial.println("myFloat.m_Bytes = "+String(myFloat.m_bytes[0])+String(myFloat.m_bytes[1])+String(myFloat.m_bytes[2])+String(myFloat.m_bytes[3])); // получаем байты
}
void loop(){}
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data )
{
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // старший бит
Wire.write((int)(eeaddress & 0xFF)); // LSB Wire.send(data);
Wire.write(data);
Wire.endTransmission();
delay(5);
}
byte readEEPROM(int deviceaddress, unsigned int eeaddress )
{
Надеюсь, это может помочь. Только что сделал несколько минут назад. удачи, @Nisit Wattanasri
Пожалуйста, добавьте несколько предложений, чтобы сопровождать код. Это может помочь тем, кто пытается понять это., @MichaelT
- Float печатается только 2 десятичных знака после запятой
- Отправка и получение различных типов данных через I2C в Arduino
- Проблемы с преобразованием byte[] в String
- Отправка числа с плавающей запятой из python в arduino
- Шестнадцатеричное/Байтовое реверсирование и преобразование
- Как отобразить переменные с плавающей запятой на OLED-дисплее (0,96 дюйма)
- Избегайте математических вычислений с плавающей запятой, чтобы ускорить Arduino
- Преобразование числа с плавающей запятой в шестнадцатеричное значение
Спасибо. Я только что попробовал союз, который вы предложили, но он показывает тот же результат! Компилятор не выдает никаких предупреждений. Ожидаете ли вы, что последовательная печать с параметром BIN покажет правильный результат?, @blarg
@blarg Думали ли вы, что, возможно, вы ожидаете неправильные значения? Откуда вы взяли эти значения?, @Majenko