Что может привести к тому, что данные будут напечатаны правильно, затем неправильно, а затем повторятся? CAN-шина/общая передача

Я пытаюсь отправить данные с MPU6050, подключенного к Arduino Uno, на другой Uno через шину CAN, однако я не думаю, что это проблема, специфичная для шины CAN.

Короче, я отправляю два пакета по 8 байт в следующих форматах

Packet 1 - (uint32_t/ int16_t/ int16_t)
Packet 2 - (int16_t/ int16_t/ int16_t/ int16_t)

все преобразовано в байты таким образом;

int16_t byte2Int(int16_t output, int pos, uint8_t data[2]){
      for (int i = 0; i < 2; i++)
    {
      output += ((int16_t)data[i + pos]) << (i * 8);
    }
    return output;
}

где все должно иметь смысл, кроме части «int pos», которая находится в том месте буфера, где я хочу начать помещать данные, поскольку пакеты 1 и 2 имеют разные структуры.

Моя проблема в том, что с помощью всего лишь одного пакета я могу нормально распечатать значения, а с добавлением второго пакета я получаю следующее на последовательном мониторе;

AccX    AccY    AccZ  GyX    GyY     GyZ

16832   -816    928 -440    16832   -816
16984   -940    992 -449    16984   -940
16896   -804    988 -462    16896   -804
209 55  0   0   209 55
16900   -852    1080    -471    16900   -852
16900   -852    1080    -471    16900   -852
16984   -760    948 -481    16984   -760
16880   -824    896 -459    16880   -824
230 102 0   0   230 102
16928   -860    996 -422    16928   -860
16928   -860    996 -422    16928   -860
17004   -856    864 -466    17004   -856
17004   -856    864 -466    17004   -856
214 105 0   0   214 105
16964   -828    944 -446    16964   -828
16964   -828    944 -446    16964   -828

Как вы можете видеть, я получаю некоторые связные сообщения, затем некоторые зашифрованные, которые не имеют смысла, затем снова связные, и цикл продолжается. Не уверен в актуальности, но когда я впервые запускаю последовательный монитор, этого не происходит, затем, после изменения времени, примерно через 5 секунд, все начинает идти не так.

Я считаю, что это связано с моим программированием и тем, что я не могу надежно запрограммировать код, чтобы дождаться завершения первого сообщения, а затем отправить следующее, и они будут прочитаны правильно. Ниже приведена моя неловкая попытка отправить отправителю сообщение ACK, чтобы сообщить, что сообщение получено, и перейти к следующему сообщению. Я не программист по натуре, и выкладываю этот дрянной код, который потратил несколько дней, пытаясь разобраться, в надежде, что кто-нибудь укажет мне правильное направление.

Заранее извиняюсь.

// Пример отправки CAN
//

#include <mcp_can.h>
#include <SPI.h>
#include <Wire.h>

const int MPU_addr = 0x68; // I2C-адрес MPU-6050
uint32_t TimeMicro;
int16_t AccX, AccY, AccZ, Tmp, GyX, GyY, GyZ; 
int8_t data1[8], data2[8], rxBuf[8];
int Switch = 7;
long unsigned int rxId;
unsigned char len = 0;

#define CAN0_INT 2                              // Установите INT на контакт 2
MCP_CAN CAN0(10);     // Устанавливаем CS на контакт 10

void setup()
{
  Serial.begin(115200);

  // Инициализируем MCP2515, работающий на частоте 16 МГц, со скоростью передачи данных 500 Кбит/с и отключенными масками и фильтрами.
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!");
  else Serial.println("Error Initializing MCP2515...");

  CAN0.setMode(MCP_NORMAL);   // Переходим в обычный режим, чтобы разрешить передачу сообщений

  pinMode(Switch, OUTPUT);
  digitalWrite(Switch, HIGH);

  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // регистр PWR_MGMT_1
  Wire.write(0);     // устанавливаем на ноль (пробуждает MPU-6050)
  Wire.endTransmission(true);


}

void loop()
{

  TimeMicro = millis();

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // начиная с регистра 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true); // запрашиваем всего 14 регистров
  AccX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AccY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AccZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp = Wire.read() << 8 | Wire.read();
  GyX = Wire.read() << 8 | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (ГИРО_XOUT_L)
  GyY = Wire.read() << 8 | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ = Wire.read() << 8 | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (ГИРО_ZOUT_L)

  double2Byte(TimeMicro, 0, data1);
  int2Byte(AccX, 4, data1);
  int2Byte(AccY, 6, data1);

  int2Byte(AccZ, 0, data2);
  int2Byte(GyX, 2, data2);
  int2Byte(GyY, 4, data2);
  int2Byte(GyZ, 6, data2);  

  // отправка данных: ID = 0x100, стандартный кадр CAN, длина данных = 8 байт, 'data' = массив байтов данных для отправки
  byte sndStat = CAN0.sendMsgBuf(0x100, 0, 8, data1);

  if(!digitalRead(CAN0_INT))                         // Если на выводе CAN0_INT низкий уровень, читаем буфер приема
  {
    CAN0.readMsgBuf(&rxId, &len, rxBuf);      // Чтение данных: len = длина данных, buf = байт(ы) данных

    while(rxBuf[0] != 0x0A)
    {
      continue;
    }

  }
  delay(10);
  byte sndStat2 = CAN0.sendMsgBuf(0x100, 0, 8, data2);

    while(rxBuf[0] != 0x0A)
    {
      continue;
    }

  for(int i = 0; i < 8; i++)
  {
    data1[i] = 0;
    data2[i] = 0;
  }

}

//============================================== ==================================
// ФУНКЦИИ
//============================================== ==================================
uint8_t int2Byte(int16_t input, int pos, uint8_t data[2]){
  for (int i = 0; i < 2; i++)
  {
    data[i+ pos] = ((input >> (i * 8)) & 0xff);
  }
return data[2];
}

uint8_t double2Byte(uint32_t input, int pos, uint8_t data[4]){
  for (int i = 0; i < 4; i++)
  {
    data[i+ pos] = ((input >> (i * 8)) & 0xff);
  }
return data[4];
}



/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

Получатель

// Пример приема CAN
//

#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf1[8];
unsigned char rxBuf2[8];
unsigned char txBuf[8];
char msgString[128];                        // Массив для хранения последовательной строки
uint32_t TimeMicro;
int16_t AccX, AccY, AccZ, GyX, GyY, GyZ;

#define CAN0_INT 2                              // Установите INT на контакт 2
MCP_CAN CAN0(10);                               // Устанавливаем CS на контакт 10


void setup()
{
  Serial.begin(115200);

  // Инициализируем MCP2515, работающий на частоте 16 МГц, со скоростью передачи данных 500 Кбит/с и отключенными масками и фильтрами.
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK)
    Serial.println("MCP2515 Initialized Successfully!");
  else
    Serial.println("Error Initializing MCP2515...");

  CAN0.setMode(MCP_NORMAL);                     // Установите нормальный режим работы, чтобы MCP2515 отправлял подтверждения на полученные данные.

  pinMode(CAN0_INT, INPUT);                            // Настройка вывода для входа /INT

  Serial.println("MCP2515 Library Receive Example...");
}

void loop()
{
  if(!digitalRead(CAN0_INT))                         // Если на выводе CAN0_INT низкий уровень, читаем буфер приема
  {
    CAN0.readMsgBuf(&rxId, &len, rxBuf1);      // Чтение данных: len = длина данных, buf = байт(ы) данных
  }

  for(int i; i < 8; i++){
    txBuf[i] = 0x0A;
  }

  byte sndStat = CAN0.sendMsgBuf(0x101, 0, 8, txBuf);

  if(!digitalRead(CAN0_INT))                         // Если на выводе CAN0_INT низкий уровень, читаем буфер приема
  {
  CAN0.readMsgBuf(&rxId, &len, rxBuf1);      // Чтение данных: len = длина данных, buf = байт(ы) данных
  }

  byte sndStat2 = CAN0.sendMsgBuf(0x101, 0, 8, txBuf);


  TimeMicro = byte2Double(TimeMicro, 0, rxBuf1);
  AccX = byte2Int(AccX, 4, rxBuf1);
  AccY = byte2Int(AccY, 6, rxBuf1);
  AccZ = byte2Int(AccZ, 0, rxBuf2);
  GyX = byte2Int(GyX, 2, rxBuf2);
  GyY = byte2Int(GyY, 4, rxBuf2);
  GyZ = byte2Int(GyZ, 6, rxBuf2);


  Serial.print(TimeMicro);
  Serial.print("\t");
  Serial.print(AccX);
  Serial.print("\t");
  Serial.print(AccY);
  Serial.print("\t");
  Serial.print(AccZ);
  Serial.print("\t");
  Serial.print(GyX);
  Serial.print("\t");
  Serial.print(GyY);
  Serial.print("\t");
  Serial.println(GyZ);



  TimeMicro = 0;
  AccX = 0;
  AccY = 0;
  AccZ = 0;
  GyX = 0;
  GyY = 0;
  GyZ = 0;


}

//============================================== ==================================
// ФУНКЦИИ
//============================================== ==================================

uint32_t byte2Double(uint32_t output, int pos, uint8_t data[4]){
      for (int i = 0; i < 4; i++)
    {
      output += ((uint32_t)rxBuf1[i + pos]) << (i * 8);
    }
    return output;
}

int16_t byte2Int(int16_t output, int pos, uint8_t data[2]){
      for (int i = 0; i < 2; i++)
    {
      output += ((int16_t)rxBuf1[i + pos]) << (i * 8);
    }
    return output;
}
/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

Я пробовал отладить это следующим образом:

  • проверьте, что MPU отправляет правильно (хорошо)
  • проверьте, происходит ли это только с одним пакетом (без проблем)
  • проверьте, правильно ли собраны пакеты на отправителе (получили данные, разбил его на байты, собрал и распечатал результат. сделал то, что я ожидается)
  • убрано время из отправки, если это вызывало проблему (та же проблема как показано выше)
  • удалены все серийные отпечатки и задано только одно значение (проблема все еще существует)

Поэтому я считаю, что это связано с ужасной и неэффективной попыткой отправить сообщение с подтверждением. Я пробовал исследовать, но не смог найти пример для CAN-шины, а все остальные использовали процедуры прерываний, специфичные для протокола, которые я пытался реализовать, но не сработало. очевидно.

Спасибо за терпение

, 👍0

Обсуждение

Вы проверяли, что у вас всегда достаточно доступной оперативной памяти? У меня были какие-то сумасшедшие, казалось бы, случайные проблемы, связанные с периодическим нехваткой оперативной памяти., @Edgar

отправлять фиксированные байты для обоих пакетов.... у нас нет возможности узнать, действительно ли данные повреждены при передаче..... гироскоп может выдавать поврежденные значения, @jsotola


2 ответа


Лучший ответ:

1

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Хорошо, я никогда не работал с CAN на Arduino, поэтому могу сильно ошибаться, и у меня нет необходимых плат для проверки этого, поэтому я могу допустить много ошибок; если вы обнаружите какие-либо проблемы, укажите их, чтобы я мог их исправить (или, если вы их исправили, сообщите мне, чтобы я мог обновить ответ для будущих читателей).

Хорошо, насколько я понимаю, у вас есть два пакета, но только один кадр CAN. Мне это кажется огромной неэффективностью, потому что как узнать, какой кадр вы только что получили?

Чтобы это исправить, вы можете использовать два кадра или использовать что-то в полезных данных, которое сообщит вам, какой кадр вы получили. На мой взгляд, поскольку бесплатных идентификаторов много, можно использовать первый подход.

Более того, вы выполняете некоторые проверки в процессе обработки (например, «получил ли я подтверждение?»), Но если это не помогло, вы продолжаете без дальнейшей проверки. Например, когда в отправителе вы пишете:

byte sndStat = CAN0.sendMsgBuf(0x100, 0, 8, data1);
if(!digitalRead(CAN0_INT))
{
  ...
}
delay(10);
byte sndStat2 = CAN0.sendMsgBuf(0x100, 0, 8, data2);

Что происходит, когда CAN0_INT все еще высокий? Да, данные отправляются немедленно.

Я не смог понять часть while(rxBuf[0] != 0x0A); это навсегда заблокирует вашу программу...

Наконец, когда вы получаете сообщение, вы никогда не проверяете его идентификатор; это означает, что вы будете проверять все сообщения по CAN, и когда вы добавите другое устройство в сеть, вы не сможете отправлять какие-либо другие сообщения.

Хорошо, вот что я бы попробовал для ваших нужд:

  • Плата TX всегда передает данные, а плата RX всегда их принимает. Никаких кадров подтверждения не требуется.
  • В сети будет два кадра: кадр 0x100 будет содержать пакет 1, а кадр 0x101 будет содержать пакет 2.
  • Плата TX будет отправлять одно сообщение каждые 100 мс; это можно уменьшить, но укажите время, а не заставляйте часть отправлять кадры «непрерывно».
  • Плата приема будет считать данные действительными только после получения пакета 0x101 и пакета 0x100, полученного ранее.

Итак, вот код. Обратите внимание, что я провел рефакторинг некоторых частей для облегчения понимания (поместил некоторый код в функции, чтобы иметь меньшие по размеру функции настройки «более высокого уровня» и циклы).

Кстати, что такое «переключатель» в отправителе? Он никогда не используется...

Отправитель:

// Пример отправки CAN
//

#include <mcp_can.h>
#include <SPI.h>
#include <Wire.h>

const int MPU_addr = 0x68; // I2C-адрес MPU-6050

uint32_t lastTimeSent;
uint32_t currMillis;
const uint32_t intervalSendMs = 100; // Период отправки в миллисекундах

int16_t AccX, AccY, AccZ, Tmp, GyX, GyY, GyZ; 
uint8_t data1[8], data2[8];
int Switch = 7;

#define CAN0_INT 2    // Установите INT на контакт 2
MCP_CAN CAN0(10);     // Устанавливаем CS на контакт 10

void setup()
{
    Serial.begin(115200);

    initializeMCP2515();

    pinMode(Switch, OUTPUT);
    digitalWrite(Switch, HIGH);

    initializeMPU6050();
}

void loop()
{
    currMillis = millis();

    if ((currMillis - lastTimeSent) >= intervalSendMs)
    {
        lastTimeSent += intervalSendMs;

        readFromMPU6050();

        convertDataToPackets();

        // Отправляем два кадра; если первая отправка успешна, отправьте и вторую, в противном случае прервите
        // отправка кадра данных 1: ID = 0x100, стандартный кадр CAN, длина данных = 8 байт, 'data' = массив байтов данных для отправки
        // отправка кадра данных 2: ID = 0x101, стандартный кадр CAN, длина данных = 8 байт, 'data' = массив байтов данных для отправки
        if (CAN0.sendMsgBuf(0x100, 0, 8, data1) == CAN_OK)
            CAN0.sendMsgBuf(0x101, 0, 8, data2);
    }
}

//============================================== ==================================
// ФУНКЦИИ
//============================================== ==================================
void initializeMCP2515()
{
    // Инициализируем MCP2515, работающий на частоте 16 МГц, со скоростью передачи данных 500 Кбит/с и отключенными масками и фильтрами.
    if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK)
        Serial.println("MCP2515 Initialized Successfully!");
    else
        Serial.println("Error Initializing MCP2515...");

    CAN0.setMode(MCP_NORMAL);   // Переходим в обычный режим, чтобы разрешить передачу сообщений
}

void initializeMPU6050()
{
    Wire.begin();
    Wire.beginTransmission(MPU_addr);
    Wire.write(0x6B);  // регистр PWR_MGMT_1
    Wire.write(0);     // устанавливаем на ноль (пробуждает MPU-6050)
    Wire.endTransmission(true);
}

void readFromMPU6050()
{
    Wire.beginTransmission(MPU_addr);
    Wire.write(0x3B);  // начиная с регистра 0x3B (ACCEL_XOUT_H)
    Wire.endTransmission(false);
    Wire.requestFrom(MPU_addr, 14, true); // запрашиваем всего 14 регистров
    AccX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
    AccY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
    AccZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    Tmp = Wire.read() << 8 | Wire.read();
    GyX = Wire.read() << 8 | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (ГИРО_XOUT_L)
    GyY = Wire.read() << 8 | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
    GyZ = Wire.read() << 8 | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (ГИРО_ZOUT_L)
}

void convertDataToPackets()
{
    long2Byte(currMillis, 0, data1);
    int2Byte(AccX, 4, data1);
    int2Byte(AccY, 6, data1);

    int2Byte(AccZ, 0, data2);
    int2Byte(GyX, 2, data2);
    int2Byte(GyY, 4, data2);
    int2Byte(GyZ, 6, data2);
}

void int2Byte(int16_t input, int pos, uint8_t *data)
{
    for (int i = 0; i < 2; i++)
    {
        data[i + pos] = ((input >> (i * 8)) & 0xff);
    }
}

void long2Byte(uint32_t input, int pos, uint8_t *data)
{
    for (int i = 0; i < 4; i++)
    {
        data[i + pos] = ((input >> (i * 8)) & 0xff);
    }
}

/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

Получатель:

// Пример приема CAN
//

#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
uint8_t receivedFirstFrame;

char msgString[128];                        // Массив для хранения последовательной строки
uint32_t TimeMicro;
int16_t AccX, AccY, AccZ, GyX, GyY, GyZ;

#define CAN0_INT 2  // Установите INT на контакт 2
MCP_CAN CAN0(10);   // Устанавливаем CS на контакт 10


void setup()
{
    Serial.begin(115200);

    initializeMCP2515();

    Serial.println("MCP2515 Library Receive Example...");
    receivedFirstFrame = 0;
}

void loop()
{
    if(!digitalRead(CAN0_INT))
    {
        // Получено сообщение
        if (CAN0.readMsgBuf(&rxId, &len, rxBuf) == CAN_OK)
        {
            // Примечание: строка выше не такая, как в примерах, но если посмотреть на код, то
            // функция может вернуться, если в очереди не было сообщения, поэтому я думаю, что это безопаснее

            // Проверим, является ли это одним из известных сообщений
            switch (rxId)
            {
            case 0x100:
                { // Получен первый кадр
                    // Декодируем содержимое и сохраняем, что был получен первый кадр
                    decodeFirstFrame(rxBuf);
                    receivedFirstFrame = 1;
                }
                break;
            case 0x101:
                if (receivedFirstFrame)
                { // Получен второй кадр после первого кадра
                    // Декодируем содержимое
                    decodeSecondFrame(rxBuf);

                    printDataToSerial();

                    receivedFirstFrame = 0;
                }
                break;
            }
        }
    }
}

//============================================== ==================================
// ФУНКЦИИ
//============================================== ==================================

void initializeMCP2515()
{
    // Инициализируем MCP2515, работающий на частоте 16 МГц, со скоростью передачи данных 500 Кбит/с и отключенными масками и фильтрами.
    if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK)
        Serial.println("MCP2515 Initialized Successfully!");
    else
        Serial.println("Error Initializing MCP2515...");

    CAN0.setMode(MCP_NORMAL); // Переходим в обычный режим, чтобы разрешить передачу сообщений

    pinMode(CAN0_INT, INPUT); // Настройка вывода для входа /INT
}

void decodeFirstFrame(uint8_t *buffer)
{
    TimeMicro = byte2Double(0, buffer);
    AccX = byte2Int(4, buffer);
    AccY = byte2Int(6, buffer);
}

void decodeSecondFrame(uint8_t *buffer)
{
    AccZ = byte2Int(0, buffer);
    GyX = byte2Int(2, buffer);
    GyY = byte2Int(4, buffer);
    GyZ = byte2Int(6, buffer);
}

void printDataToSerial()
{
    Serial.print(TimeMicro);
    Serial.print("\t");
    Serial.print(AccX);
    Serial.print("\t");
    Serial.print(AccY);
    Serial.print("\t");
    Serial.print(AccZ);
    Serial.print("\t");
    Serial.print(GyX);
    Serial.print("\t");
    Serial.print(GyY);
    Serial.print("\t");
    Serial.println(GyZ);
}

uint32_t byte2Double(int pos, uint8_t *data)
{
    uint32_t output = 0;
    for (int i = 0; i < 4; i++)
    {
      output += ((uint32_t)data[i + pos]) << (i * 8);
    }
    return output;
}

int16_t byte2Int(int pos, uint8_t *data)
{
    int16_t output = 0;
    for (int i = 0; i < 2; i++)
    {
      output += ((int16_t)data[i + pos]) << (i * 8);
    }
    return output;
}
/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

Дайте мне знать, если у вас есть какие-либо комментарии или вы обнаружите какие-либо проблемы по этому поводу.

,

Код работает отлично после нескольких проблем с компиляцией. Интересно только то, что пришлось сериалить. Распечатайте данные непосредственно после функции «CAN0. readMsgBuf». Может ли это быть потому, что данные перезаписываются в rxBuf после «decodeSecondFrame»? Теперь у меня есть файл для работы. Спасибо, что нашли время помочь мне научить. И что мне делать с кодом, который работает? Поместить это в конец вопроса?, @Ross Hanna

@RossHanna Если вы расскажете мне о проблемах с компиляцией, я смогу исправить код, чтобы будущим читателям не приходилось выяснять, что не так;) Что касается вашей проблемы, это моя вина. Мне следовало научиться не вызывать переменные подобным образом. Пожалуйста, исправьте код с помощью двух функций декодирования, которые я отредактировал выше, и верните печать в прежнее положение (после того, как readMsgBuf неверен, поскольку он печатает не действительные данные, а также частичные). Что касается кода, то достаточно принять этот ответ, как вы это сделали, @frarugi87

1 — изменены данные 1 на тип переменной на uint8_t. 2 — определите currentMillis в «convertDataToPackets». 3 - удалить размер массива данных "[2][i + pos]". 4 — установите все переменные в ноль в конце основного цикла, иначе они просто увеличиваются. затем переместите последовательные отпечатки в функции после декодирования., @Ross Hanna

Ради исправления кода: функции byte2Double и byte2Int в эскизе приемника по-прежнему содержат исходный код с ошибкой, как указано в моем ответе., @oh.dae.su

@RossHanna Мне следовало исправить код для проблем 1, 2 и (если я правильно понял) 3. Пункт 3 был связан с data[4] и data[2] в функциях byte2Double и byte2Int? Пункт 4 тоже решен, но не так, как вы думаете (старые функции были неправильными, я их исправил "правильным" - ИМХО - способом), @frarugi87

@oh.dae.su Спасибо, я упустил этот момент (исправил в отправителе, а не в получателе); Я интегрировал это в код в ответе, @frarugi87


1

В вашем эскизе действительно есть несколько проблем с кодированием, особенно в части приемника. Я постараюсь их перечислить и предложить изменения.

  1. Если вы внимательно посмотрите на полученные данные, вы увидите, что AccX==GyY и AccY==GyZ в каждой строке вашего вывода. Это связано с тем, что вы используете глобальную переменную rxBuf1 в функциях byte2Int и byte2Double на стороне получателя. Это приводит к тому, что выходные данные используют rxBuf1 также для второго кадра CAN. Вместо этого вам следует использовать указатель на данные, которые вы подготовили во входных данных для этой цели.
  2. В эскизе приемника оба раза, когда вы читаете сообщение CAN с помощью CAN0.readMsgBuf, вы записываете результат в rxBuf1, хотя для второго чтения вы, скорее всего, предназначен для записи в rxBuf2. Теперь вы просто перезаписываете rxBuf1, и поэтому содержимое первого полученного сообщения CAN в цикле никогда не оценивается.
  3. Переключение выходов через каждые несколько строк, по моему мнению, скорее всего, связано со смешанным порядком обоих сообщений на стороне приемника, когда вы получаете сообщение CAN 2 раньше сообщения CAN 1. Чтобы избежать этого, следуйте инструкциям хорошее предложение от @frarugi87 отправлять оба сообщения с отдельными идентификаторами CAN и проверять получение обоих перед их печатью.
  4. Я также согласен, что вы можете избавиться от подтверждения ответа от получателя. Протокол can уже содержит механизм подтверждения на приемопередатчике CAN, который уведомит отправителя о том, что хотя бы один прослушивающий узел правильно получил сообщение. Если отправитель не получит этот индикатор подтверждения, он будет повторите, чтобы отправить сообщение в течение некоторого времени.
,

Я использовал изменения кода frarugi87, и они отлично работают. Мой единственный оставшийся вопрос. Использование двух отдельных идентификаторов для каждого пакета? так ли это будет сделано в реальной системе? я пытался получить все данные за одну передачу, поскольку думал, что именно так это будет сделано в реальной системе?, @Ross Hanna

Обычно один CAN-ID используется для фиксированной полезной нагрузки размером до 8 байт. Поэтому обычно при повторной передаче CAN-ID он содержит те же переменные (однако с обновленными значениями). Если у вас есть более 8 байт информации, которую вы хотите передавать регулярно, вы либо разделяете ее на несколько CAN-ID, либо используете транспортный протокол, такой как SAE J1939, поверх обычного CAN-протокола на канальном уровне. ., @oh.dae.su