Получить float по COM

Тема в разделе "Arduino & Shields", создана пользователем Danil_2002, 17 июн 2019.

  1. Danil_2002

    Danil_2002 Нерд

    Добрый день! Нужна помощь с получением float через USART. Написал вот такие программки:

    С# на ПК. Она отправляет float и принимает это же число от ардуино уно

    Код (C++):
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO.Ports;

    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                SerialPort port = new SerialPort("COM4", 9600, Parity.None, 8, StopBits.One); // Настраиваем COM-порт
                port.Open();
                float number = 45.5f;
                float m = 0;
                byte[] message_t = new byte[4];
                for (int start = 0; start < 10000; start++)
                {
                    byte[] message = BitConverter.GetBytes(number); // Float в 4 байта
                    port.Write(message, 0, message.Length);  // Отправляем массив байтов
                    port.Read(message_t, 0, message_t.Length); // Читаем массив байтов
                    m = BitConverter.ToSingle(message_t, 0); // Превращаем массив байтов в float
                    Console.WriteLine(m);
                 }
            }
        }
    }
     
    Код на ардуинке:
    Код (C++):

    float number = 0;
    union
    {
      uint32_t asByte;
      float asFloat;
    } message_float;
    void setup()
    {
      Serial.begin(9600);
      pinMode(2, OUTPUT);
    }
    void return_float_number()
    {
      uint32_t  s = (message_float.asByte >> 31) ? -1 : 1; // Определяем знак
      uint32_t e = (message_float.asByte >> 23) & 0xFF; // Порядок
      uint32_t m =                          
      e ?
        ( message_float.asByte & 0x7FFFFF ) | 0x800000 :
        ( message_float.asByte & 0x7FFFFF ) << 1;
       number = s * (m*2^ -23)*(2^(e-127));
     
    }
    void loop()
    {
      for(uint8_t start = 0; start < 5; start++)
      {
        uint8_t simvol_read = Serial.read();
        while(simvol_read == -1) {simvol_read = Serial.read();}
        message_float.asByte = simvol_read >> 8;
        //if (simvol_read != -1) {message_float.asByte = simvol_read << 8;}
        if (start == 4)
        {
          return_float_number();
          Serial.println(number);
          //Serial.println(number);
          if (number == 45.5) {digitalWrite(2, HIGH);}
        }
      }
    }
    Вот то что я получаю(отрывок):
    8,035656E-09
    4,120368E-11
    6,797966E-33
    6,825459E-07
    5,429218E-31
    1,097891E-05
    6,409409E-10
    4,244787E-05
    6,337645E-10
    8,035656E-09

    а нужно получить обратно свои 45.5

    P.S код получился запутанным потому что я много как пробовал. Помогите разобраться
     
  2. Igor68

    Igor68 Гуру

    Доброго времени суток!
    Сам уже давно убежал от C# и им пользуюсь только оформления окон. А работа вся на C/C++ в виде DLL. Именно потому как не разобрался с указателями в C#,но может он это и не умеет. Данные таскаю по ETHERNET, MODBUS ит.п. по правилу (ведь float это uint32_t с точки зрения размещения и передачи)
    Код (C++):

    float x;
    uint32_t y;
    y = (*(uint32_t*)(&x));
     
    Тут мы разместили значение типа float в размерность uint32_t. А это 4 байта. А можно и так:
    Код (C++):

    float x;
    uint8_t y[4];
    memcpy(&y[0], &x, sizeof(float));
     
    В любом случае это подготовлено для передачи - то, что есть y
    Принимаем эти байты и делаем всё обратно:
    Код (C++):

    uint8_t x[4];
    float y;
    y=(*(float*)(&x[0]));
     
    Ну или применив memcpy.
    В шарпе я не стал изучать - овчинка выделки не стоит. Примените к шарпу DLL на Си. И из него вызывайте функцию.
    Это чисто мой подход. Вы как хотите.
     
    parovoZZ и Daniil нравится это.
  3. Danil_2002

    Danil_2002 Нерд

    Сейчас попробую
     
  4. Igor68

    Igor68 Гуру

    Вам ОБЯЗАТЕЛЬНО надо не применять программные преобразования типов данных float в байты. Размерность float 4 байта. И ничего делать не надо. Представляйте для приёма и передачи эти 4 байта как есть. Не думаю что надо преобразовывать значение в стоку - это лишние действия как при передаче, так и приёме. Хотя говорят это модно и наглядно, но занимает ресурсы.
     
  5. Danil_2002

    Danil_2002 Нерд

    Вот код для ардуины:
    [
    Код (C++):
    uint8_t arr[4];
    uint8_t pointer_arr = 0;
    float number = 0;
    float* pointer_float;
    ISR(USART_RX_vect)
    {
      arr[pointer_arr] = UDRE0;
      pointer_arr++;
      if (pointer_arr == 4) // Получили все байты
      {
        pointer_arr = 0;
        number = (*(float*)(&arr[0])); // Превратили во float
        for(uint8_t start = 0; start <= 4; start++) // Вернули float по байтам
        {
          USART_TransmitByte(pointer_float + start);
        }
      }
    }
    void USART_init(void) // Настраеваем скорость передачи данных
    {
      UBRR0L = 103;
      UBRR0H = 0;
      UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
      UCSR0C = (1<<USBS0)|(3<<UCSZ00);
    }
    void USART_TransmitByte( unsigned char data )
    {
      while ( !( UCSR0A & (1<<UDRE0)) );
      UDR0 = data;
    }
    int main()
    {
      sei();
      USART_init();
      pointer_float = (float*)&number;
      while(1)
      {

      }
    }
    Все тоже! Может я тут ошибся где-то или все таки программа на C# не правильная?
     
    Последнее редактирование: 17 июн 2019
  6. Danil_2002

    Danil_2002 Нерд

    То есть то что я сейчас написал неправильно?
     
  7. Danil_2002

    Danil_2002 Нерд

    Вообщем проблема именно в ардуинке(в моем коде) я посмотрел какие байты отправляет и принимает моя программа, байты которые принимает постоянно разные
     
  8. Daniil

    Daniil Гуру

    А на С# не часто ли из порта читаете?
    Там есть ожидание 4ех байт?
     
  9. DetSimen

    DetSimen Гуру

    а у C# тип данных float точно есть? А он точно 4 байта?

    PS. Да, есть, паматрел
     
    Последнее редактирование: 18 июн 2019
  10. DetSimen

    DetSimen Гуру

    используй лучше явно System.Single
     
  11. SergeiL

    SergeiL Гуру

    Ну смотри:
    Косяки здесь:
    Код (C++):
    float* pointer_float;
        for(uint8_t start = 0; start <= 4; start++) // Вернули float по байтам
        {
          USART_TransmitByte(pointer_float + start);
        }
    Что мы отправляем в UART?
    Указатель на float! (не значение по указателю, а адрес). Это раз.
    Что будет если к указателю на float прибавить 1? Будет указатель на следующий float. А он есть? Нет. Это два.

    Ну и синхронизации тут нет.
    Нужно определять начало передачи.
    Если проскочит байт - получится фигня.
     
    Danil_2002, arkadyf, Daniil и ещё 1-му нравится это.
  12. Igor68

    Igor68 Гуру

    По ходу тут предлагается не только идея синхронизации, но и контрольной величины. Как раз пятый байт. Ну допустим XOR. В аккурат он передаётся последним формируя через XOR с неким байтом, побайтно. А при проверке (приёме) данные проверяются и ещё через XOR с пятым байтом. Дожен быть 0x00. Иначе ошибка. Для 4-х байт данных думаю с лихвой. Ну не Хемминга же прикручивать?!
     
    Danil_2002 нравится это.
  13. SergeiL

    SergeiL Гуру

    Согласен, но для того, чтобы получить правильную последовательность, нужно хотя бы таймер прикрутить.
    Получили первый байт - взвели таймер. Получили последний - обработали.
    Таймер отсчитал, - сбросили все, что получили и перешли в начальное состояние.
     
    Danil_2002 и arkadyf нравится это.
  14. AlexU

    AlexU Гуру

    Может для начала лучше воспользоваться штатными средствами класса Serial? Например, функцией 'parseFloat()'. А уже потом углубляться в дебри прерываний.

    Что касается обработчика прерывания:
    Код (C++):
    ISR(USART_RX_vect)
    {
      arr[pointer_arr] = UDRE0;
      pointer_arr++;
      if (pointer_arr == 4) // Получили все байты
      {
        pointer_arr = 0;
        number = (*(float*)(&arr[0])); // Превратили во float
        for(uint8_t start = 0; start <= 4; start++) // Вернули float по байтам
        {
          USART_TransmitByte(pointer_float + start);
        }
      }
    }
    Зачем ждёте окончания передачи (USART_TransmitByte)? Вы рискуете пропустить символ при приёме.
     
    Danil_2002 и DetSimen нравится это.
  15. Igor68

    Igor68 Гуру

    Согласен... но тогда лучше принять идеологию Modbus, где между посылками есть некий интервал. И интервал между байтами/символами не более такого-то значения. Но это уже протокол некий. Тут же вопрос в обмене величиной, да ещё между разными платформами. Надеюсь Автор получит то, что ему надо. Со стороны ардуины, я сам не очень вникал (или очень не вникал). Была необходимость отвязать от малины всё многообразие устройств, через ардуину конечно. И только для этого и не более.
     
    Danil_2002 нравится это.
  16. Daniil

    Daniil Гуру

    Еще нюанс, в ардуино float реализован программно. Я не знаю как это сделано внутри мк, но если нестандартно, то нужно быть готовым к проблемам.
    Для проверки можно собрать инт из 4ех байтов и преобразовать его в флоат.
     
    Danil_2002 нравится это.
  17. parovoZZ

    parovoZZ Гуру

    Поясню - не сам метод хранения float (он у всех процев одинаковый), а операции с ним и только на тех процах, где нет математического модуля.
     
    Danil_2002, Igor68 и Daniil нравится это.
  18. Igor68

    Igor68 Гуру

    Вопрос не в том, как обрабатывается (ПаровоЗЗ сказал уже), а в том как храниться. Так же как и uint32_t/int32_t ну или 4 байта. Так и передавать/принимать. Было дело самому надо было и если заинтересует вот как пример:
    testfloat.zip
    Собран под Linux, но файл то ведь есть для компиляции. Так в полторы строчки.
    Для входа (как параметр) значение float, а на выходе 4 байта в HEX.
     

    Вложения:

    • testfloat.zip
      Размер файла:
      4,2 КБ
      Просмотров:
      21
    Danil_2002 нравится это.
  19. Danil_2002

    Danil_2002 Нерд

    Спасибо большое всем за ответы, я думаю того что вы мне наотвечали будет вполне достаточно для решения моей проблемы. Но к сожалению сейчас нет времени с этим разбираться, у меня научная выставка 29 числа и нужно проект закончить успеть, поэтому и тему создал, чтобы разобраться побыстрей. Решил пока округлять числа и передавать их как uint8_t, точность особой роли не сыграет, но лишь бы заработало, а потом допилить как надо.

    P.S Если у меня все заработает рабочий код выложу где-то после 29

    И кстати,
    Почему у меня нет указателя?
    Код (C++):
    int main()
    {
      sei();
      USART_init();
      pointer_float = (float*)&number; // ВОТ ОН КАК БЫ или нет?
      while(1)
      {

      }
    }
     
  20. b707

    b707 Гуру

    потому что в том твоем коде, о котором пишет SergeiL
    ты пытаешься инкрементировать указатель на флоат в цикле, и делаешь это 4 раза. Хотя флоат у тебя всего один.
    Вот про эти лишние три флоата Сергей и пишет. что у тебя их нет.