I2C: Network support
parent
13ec2bca98
commit
9817887523
|
|
@ -9,3 +9,4 @@ third-party/concurrentqueue/benchmarks
|
|||
third-party/concurrentqueue/tests
|
||||
*.bak
|
||||
.vs
|
||||
.cache
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ set(SRC_FILES
|
|||
communication/packet/ethphyregpacket.cpp
|
||||
communication/packet/logicaldiskinfopacket.cpp
|
||||
communication/packet/wivicommandpacket.cpp
|
||||
communication/packet/i2cpacket.cpp
|
||||
communication/packet/scriptstatuspacket.cpp
|
||||
communication/decoder.cpp
|
||||
communication/encoder.cpp
|
||||
|
|
@ -389,6 +390,7 @@ if(LIBICSNEO_BUILD_TESTS)
|
|||
test/diskdriverwritetest.cpp
|
||||
test/eventmanagertest.cpp
|
||||
test/ethernetpacketizertest.cpp
|
||||
test/i2cencoderdecodertest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(libicsneo-tests gtest gtest_main)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "icsneo/communication/message/scriptstatusmessage.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
|
|
@ -21,6 +22,7 @@
|
|||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||
#include "icsneo/communication/packet/logicaldiskinfopacket.h"
|
||||
#include "icsneo/communication/packet/wivicommandpacket.h"
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
#include "icsneo/communication/packet/scriptstatuspacket.h"
|
||||
#include <iostream>
|
||||
|
||||
|
|
@ -122,6 +124,20 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
|||
iso.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::I2C: {
|
||||
if(packet->data.size() < sizeof(HardwareI2CPacket)) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
result = HardwareI2CPacket::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; //malformed packet indicated by a nullptr return
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case Network::Type::Internal: {
|
||||
switch(packet->network.getNetID()) {
|
||||
case Network::NetID::Reset_Status: {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include "icsneo/communication/packet/canpacket.h"
|
||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||
#include "icsneo/communication/message/ethphymessage.h"
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
|
|
@ -68,6 +70,18 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
|||
// packets to the device. This function just encodes them back to back into `result`
|
||||
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
|
||||
} // End of Network::Type::ISO9141
|
||||
case Network::Type::I2C: {
|
||||
auto i2cmsg = std::dynamic_pointer_cast<I2CMessage>(message);
|
||||
if(!i2cmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareI2CPacket::EncodeFromMessage(*i2cmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
std::shared_ptr<Message> HardwareI2CPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream)
|
||||
{
|
||||
auto msg = std::make_shared<I2CMessage>();
|
||||
const I2CHeader* packet = reinterpret_cast<const I2CHeader*>(bytestream.data());
|
||||
const size_t numPayloadBytes = packet->length;
|
||||
const size_t numControlBytes = packet->CoreMiniBitsI2C.CBLen;
|
||||
const size_t numDataBytes = numPayloadBytes - numControlBytes;
|
||||
if( (numPayloadBytes == 0) || (numDataBytes > I2CMaxLength) ||
|
||||
(sizeof(I2CHeader) != (bytestream.size() - numPayloadBytes)) )
|
||||
{ return nullptr; }
|
||||
|
||||
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
|
||||
msg->address = (packet->CoreMiniBitsI2C.ID & 0x3FFu);
|
||||
msg->deviceMode = static_cast<I2CMessage::DeviceMode>(packet->CoreMiniBitsI2C.CT);
|
||||
msg->direction = static_cast<I2CMessage::Direction>(packet->CoreMiniBitsI2C.DIR);
|
||||
|
||||
msg->isExtendedID = static_cast<bool>(packet->CoreMiniBitsI2C.EID & 0x01u);
|
||||
msg->isTXMsg = static_cast<bool>(packet->CoreMiniBitsI2C.TXMsg & 0x01u);
|
||||
msg->txTimeout = static_cast<bool>(packet->CoreMiniBitsI2C.TXTimeout & 0x01u);
|
||||
msg->txNack = static_cast<bool>(packet->CoreMiniBitsI2C.TXNack & 0x01u);
|
||||
msg->txAborted = static_cast<bool>(packet->CoreMiniBitsI2C.TXAborted & 0x01u);
|
||||
msg->txLostArb = static_cast<bool>(packet->CoreMiniBitsI2C.TXLostArb & 0x01u);
|
||||
msg->txError = static_cast<bool>(packet->CoreMiniBitsI2C.TXError & 0x01u);
|
||||
//We don't care about 0xTRB0Dx in this case...
|
||||
//copy 0xTRB0STAT even though we likely won't use it either
|
||||
msg->stats = packet->stats;
|
||||
msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull));
|
||||
|
||||
//The device will combine the 'control' bytes and data bytes into one payload
|
||||
//The control bytes will always come before the data
|
||||
auto cbStart = bytestream.begin() + sizeof(I2CHeader);
|
||||
auto dataStart = cbStart + numControlBytes;
|
||||
std::copy(cbStart, dataStart, std::back_inserter(msg->controlBytes));
|
||||
std::copy(dataStart, bytestream.end(), std::back_inserter(msg->dataBytes));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool HardwareI2CPacket::EncodeFromMessage(const I2CMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
|
||||
{
|
||||
const size_t numControlBytes = message.controlBytes.size();
|
||||
const size_t numDataBytes = message.dataBytes.size();
|
||||
if(I2CMaxLength < numDataBytes)
|
||||
{
|
||||
report(APIEvent::Type::I2CMessageExceedsMaxLength, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(message.controlBytes.empty() || message.dataBytes.empty())
|
||||
{
|
||||
//You'll need to provide a target R/W register in controlBytes
|
||||
//alternatively, you're expecting to read without providing a dataBytes payload
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytestream.push_back(static_cast<uint8_t>(numControlBytes & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>((numControlBytes) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>(numDataBytes & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>((numDataBytes) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>((message.stats) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>(message.stats & 0xFFu));
|
||||
|
||||
if(message.isExtendedID)
|
||||
{
|
||||
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>(((message.address) >> 8) & 0x03u) | 0x04u);
|
||||
} else {
|
||||
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>(0x00u));
|
||||
}
|
||||
|
||||
if(I2CMessage::Direction::Read == message.direction)
|
||||
{ bytestream.back() |= static_cast<uint8_t>(0x10u); }
|
||||
|
||||
std::copy(message.controlBytes.begin(), message.controlBytes.end(), std::back_inserter(bytestream));
|
||||
std::copy(message.dataBytes.begin(), message.dataBytes.end(), std::back_inserter(bytestream));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -89,6 +89,7 @@ public:
|
|||
AtomicOperationCompletedNonatomically = 0x2035,
|
||||
WiVIStackRefreshFailed = 0x2036,
|
||||
WiVIUploadStackOverflow = 0x2037,
|
||||
I2CMessageExceedsMaxLength = 0x2038,
|
||||
|
||||
// Transport Events
|
||||
FailedToRead = 0x3000,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef __I2CMESSAGE_H_
|
||||
#define __I2CMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class I2CMessage : public Frame {
|
||||
public:
|
||||
enum class DeviceMode : uint8_t {
|
||||
Target = 0,
|
||||
Controller = 1
|
||||
};
|
||||
|
||||
enum class Direction : uint8_t {
|
||||
Write = 0,
|
||||
Read = 1
|
||||
};
|
||||
|
||||
bool isExtendedID = false;
|
||||
bool isTXMsg = false;
|
||||
bool txError = false;
|
||||
bool txLostArb = false;
|
||||
bool txAborted = false;
|
||||
bool txNack = false;
|
||||
bool txTimeout = false;
|
||||
uint16_t stats = static_cast<uint16_t>(0x0000u);
|
||||
uint16_t address;
|
||||
Direction direction;
|
||||
DeviceMode deviceMode;
|
||||
|
||||
//Must contain the target register address to read or write
|
||||
std::vector<uint8_t> controlBytes;
|
||||
|
||||
//The device expects a dataBytes payload even if you're reading
|
||||
//In the case of a read these bytes aren't interesting, but they have to be there
|
||||
//Add bytes to write, or the same number of junk bytes you expect the device send back
|
||||
std::vector<uint8_t> dataBytes;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef __I2CPACKET_H__
|
||||
#define __I2CPACKET_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include "icsneo/communication/network.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
static constexpr size_t I2CMaxLength = 1024u;
|
||||
|
||||
#pragma pack(push, 2)
|
||||
|
||||
struct I2CHeader {
|
||||
struct {
|
||||
// C1xI2C
|
||||
uint16_t ID : 10;// i2c address, 7-bit or 10-bit
|
||||
uint16_t EID : 1;// using extended addressing, i.e. 10-bit
|
||||
uint16_t CT : 1;// Controller/Target
|
||||
uint16_t DIR : 1;// read/write
|
||||
uint16_t RESERVED_0 : 3;
|
||||
|
||||
// C2xI2C
|
||||
uint16_t TXMsg: 1;
|
||||
uint16_t CBLen: 11;
|
||||
uint16_t RESERVED_1: 4;
|
||||
|
||||
// C3xI2C
|
||||
uint16_t TXTimeout : 1;
|
||||
uint16_t TXNack : 1;
|
||||
uint16_t TXAborted : 1;
|
||||
uint16_t TXLostArb : 1;
|
||||
uint16_t TXError : 1;
|
||||
uint16_t RESERVED_2: 11;
|
||||
} CoreMiniBitsI2C;
|
||||
|
||||
uint8_t coreMiniMessageData[8];
|
||||
uint16_t stats;
|
||||
|
||||
uint64_t timestamp;
|
||||
|
||||
uint16_t networkID;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
struct HardwareI2CPacket {
|
||||
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
|
||||
static bool EncodeFromMessage(const I2CMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //_cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -28,7 +28,10 @@ public:
|
|||
Network::NetID::LIN,
|
||||
|
||||
Network::NetID::A2B1,
|
||||
Network::NetID::A2B2
|
||||
Network::NetID::A2B2,
|
||||
|
||||
Network::NetID::I2C,
|
||||
Network::NetID::I2C2
|
||||
};
|
||||
return supportedNetworks;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,12 @@ protected:
|
|||
|
||||
Network::NetID::LIN,
|
||||
|
||||
Network::NetID::FlexRay
|
||||
Network::NetID::FlexRay,
|
||||
|
||||
Network::NetID::I2C,
|
||||
Network::NetID::I2C2,
|
||||
Network::NetID::I2C3,
|
||||
Network::NetID::I2C4,
|
||||
};
|
||||
rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "icsneo/communication/message/iso9141message.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
#include "icsneo/communication/message/ethphymessage.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/communication/encoder.h"
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <vector>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
class I2CEncoderDecoderTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
report = [](APIEvent::Type, APIEvent::Severity) {
|
||||
// Unless caught by the test, the packetizer should not throw errors
|
||||
EXPECT_TRUE(false);
|
||||
};
|
||||
packetizer.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
|
||||
report(t, s);
|
||||
});
|
||||
packetEncoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
|
||||
report(t, s);
|
||||
});
|
||||
packetDecoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
|
||||
report(t, s);
|
||||
});
|
||||
}
|
||||
device_eventhandler_t report;
|
||||
std::optional<Encoder> packetEncoder;
|
||||
std::optional<Packetizer> packetizer;
|
||||
std::optional<Decoder> packetDecoder;
|
||||
//Read request to the device
|
||||
//Control length 1, control bytes 0x12 (I2C register to read from)
|
||||
//data length 1: blank bytes padded in that the device will fill in the reply
|
||||
std::vector<uint8_t> testBytes =
|
||||
{0xaa, 0x0c, 0x11, 0x00, 0x58, 0x00, 0x01, 0x00,
|
||||
0x01, 0x00, 0x00, 0x01, 0x68, 0x10, 0x12, 0x00};
|
||||
|
||||
std::vector<uint8_t> recvBytes =
|
||||
{0xaa, 0x0c, 0x24,0x00, 0x58, 0x00, 0x68, 0x18,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x97, 0x29,
|
||||
0xe6, 0xfb, 0xc1, 0xfc, 0xb0, 0x80, 0x35, 0x00,
|
||||
0x02, 0x00, 0x12, 0x80};
|
||||
};
|
||||
|
||||
TEST_F(I2CEncoderDecoderTest, PacketEncoderTest) {
|
||||
std::vector<uint8_t> bytestream;
|
||||
auto message = std::make_shared<icsneo::I2CMessage>();
|
||||
message->network = icsneo::Network::NetID::I2C;
|
||||
message->controlBytes.push_back(static_cast<uint8_t>(0x12u)); //Product ID register address
|
||||
message->dataBytes.push_back(static_cast<uint8_t>(0x00u));
|
||||
message->address = 0x68u; //7 bit addressing, BASE_ADDR
|
||||
message->stats = static_cast<uint16_t>(0x0001u);
|
||||
message->direction = I2CMessage::Direction::Read;
|
||||
message->isTXMsg = true;
|
||||
packetEncoder->encode(*packetizer, bytestream, message);
|
||||
EXPECT_EQ(bytestream, testBytes);
|
||||
}
|
||||
|
||||
TEST_F(I2CEncoderDecoderTest, PacketDecoderTest) {
|
||||
std::shared_ptr<icsneo::Message> decodeMsg;
|
||||
std::shared_ptr<icsneo::I2CMessage> message = std::make_shared<icsneo::I2CMessage>();
|
||||
|
||||
message->network = icsneo::Network::NetID::I2C;
|
||||
message->controlBytes.push_back(static_cast<uint8_t>(0x12u)); //Product ID register address
|
||||
message->dataBytes.push_back(static_cast<uint8_t>(0x80u));
|
||||
message->address = 0x68u; //7 bit addressing, BASE_ADDR
|
||||
message->stats = static_cast<uint16_t>(0x0002u);
|
||||
message->direction = I2CMessage::Direction::Read;
|
||||
message->deviceMode = I2CMessage::DeviceMode::Controller;
|
||||
message->isTXMsg = true;
|
||||
message->timestamp = static_cast<uint64_t>(0xB0FCC1FBE62997);
|
||||
|
||||
EXPECT_TRUE(packetizer->input(recvBytes));
|
||||
auto packets = packetizer->output();
|
||||
if(packets.empty()) { EXPECT_TRUE(false); }
|
||||
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
|
||||
auto testMessage = std::dynamic_pointer_cast<icsneo::I2CMessage>(decodeMsg);
|
||||
EXPECT_EQ(message->network, testMessage->network);
|
||||
EXPECT_EQ(message->controlBytes, testMessage->controlBytes);
|
||||
EXPECT_EQ(message->dataBytes, testMessage->dataBytes);
|
||||
EXPECT_EQ(message->address, testMessage->address);
|
||||
EXPECT_EQ(message->stats, testMessage->stats);
|
||||
EXPECT_EQ(message->direction, testMessage->direction);
|
||||
EXPECT_EQ(message->deviceMode, testMessage->deviceMode);
|
||||
EXPECT_EQ(message->isTXMsg, testMessage->isTXMsg);
|
||||
EXPECT_EQ(message->timestamp, testMessage->timestamp);
|
||||
}
|
||||
Loading…
Reference in New Issue