Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A Deep Dive into QtCanBus

1,560 views

Published on

These are the slides to my talk "A Deep Dive into QtCanBus" from Qt World Summit 2019. I show how to build the middleware between the CAN bus and the QML HMI.

Published in: Automotive
  • Login to see the comments

  • Be the first to like this

A Deep Dive into QtCanBus

  1. 1. Copyright 2019, Burkhard Stubert A Deep Dive into Qt CAN Bus Burkhard Stubert I help teams succeed with Qt embedded systems
  2. 2. Copyright 2019, Burkhard Stubert 2 Signals per second: 9 50-100 79 10-20 199 2-5 63 <= 2 5 main ECUs with • 350 signals • 1800 properties • 2500 errors Received in CAN module: • 1100 / 1700 fps avg / peak Received in GUI: • 50 / 300 fps avg / peak Home Screen of Sugar Beet Harvester
  3. 3. Copyright 2019, Burkhard Stubert Outline – A Deep Dive into Qt CAN Bus • Basic Concepts of CAN • Architecture of CAN Middleware • Connecting to the CAN Bus • Receiving CAN Frames • Sending CAN Frames • Generating Code for CAN Middleware 3
  4. 4. Copyright 2019, Burkhard Stubert CAN Bus Architecture 4 Engine 0x00 Transmission 0x03 Body 0x21 Steering 0x13 Joystick 0x2a Nav Keys 0x2b Climate 0x19 Camera 0x1c Header 1 0xf0 Header 2 0xf1 Terminal 0x28 can0 can1 Communication Principle • Publish-subscribe • Signal-slots
  5. 5. Copyright 2019, Burkhard Stubert CAN Frame = Frame ID # Payload 5 auto frameId{0x18ff3203U}; auto payload{QByteArray::fromHex("4cfc4305")}; auto frame{QCanBusFrame{frameId, payload}}; QCOMPARE(frame.frameId(), frameId); QCOMPARE(frame.payload(), payload); QVERIFY(frame.isValid()); QVERIFY(frame.hasExtendedFrameFormat()); QVERIFY(!frame.hasFlexibleDataRateFormat()); qDebug() << "frame = " << frame.toString(); // "18FF3203 [4] 4C FC 43 05" auto invalidType{QCanBusFrame::InvalidFrame}; auto frame2{QCanBusFrame{invalidType}}; QVERIFY(!frame2.isValid()); Extended Frame Format (EFF) • frame ID: 29 bits • payload length <= 8 bytes (if not flexible data rate) Base Frame Format (BFF) • frame ID: 11 bits • payload length <= 8 bytes (if not flexible data rate)
  6. 6. Copyright 2019, Burkhard Stubert Standardising CAN Frames: SAE J1939 6 Meaning of CAN frame 0cf00400 # a4503871150d031e • Can mean anything • Fully defined by OEM • Communication nightmare Meaning of J1939 frame 0cf00400 # a4503871150d031e • Frame EEC1 from Engine about engine speed and torque • Standardised communication • Tractor ó Implements • Harvester ó MAN or Volvo motor • Defines 1500+ standard frames, 100+ ECU IDs
  7. 7. Copyright 2019, Burkhard Stubert J1939 Broadcast Frames (PDU2 Format) 7 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 0 0 Priority 0 DP PDU Format (PF) PDU Specific (PS) Source Address (SA) Parameter Group Number (PGN) • Like Qt Signals • PGN determines payload • Data Page (DP) doubles #PGNs • PDU2 Format • 240 <= PF <= 255 • proprietary if PF == 255 Parameter Group (qint16): PR = 0x06 tiltAxle1 = 389 = 0x0185 PGN = 0xff10 tiltAxle2 = -813 = 0xfcd3 SA = 0x21 tiltAxle3 = 1034 = 0x040a padding = 0 = 0x0000 J1939Frame: 18 ff10 21 # 8501 d3fc 0a04 0000 QCanBusFrame{0x18ff1021U, QByteArray::fromHex("8501d3fc0a040000")} encode decode
  8. 8. Copyright 2019, Burkhard Stubert J1939 Peer-To-Peer Frames (PDU1 Format) 8 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 0 0 Priority 0 DP PDU Format (PF) Destination Addr. (DA) Source Address (SA) Parameter Group Number (PGN) • Like Qt Properties • payload[0] determines payload • Sent from ECU SA to ECU DA • PDU1 Format • 0 <= PF < 240 (0xF0) • proprietary if PF == 239 (0xEF) Write Property Request: PR = 0x06 Command = 0x02 (quint8) PGN = 0xef ID = 0x0203 (quint16) DA = 0x13 Value = 0x096C61B5 (qint32) SA = 0x28 Padding = 0x00 J1939Frame: 18 ef 13 28 # 02 0302 B5616C09 00 QCanBusFrame{0x18ef1328U, QByteArray::fromHex("020302B5616C0900")} encode decode
  9. 9. Copyright 2019, Burkhard Stubert Outline – A Deep Dive into Qt CAN Bus • Basic Concepts of CAN • Architecture of CAN Middleware • Connecting to the CAN Bus • Receiving CAN Frames • Sending CAN Frames • Generating Code for CAN Middleware 9
  10. 10. Copyright 2019, Burkhard Stubert CanBusRouter and ECU Proxies 10 Body 0x21 Steering 0x13 can0 Terminal 0x28 QCanBusDevice CanBusRouter BodyProxySteeringProxy QML HMI Number J1939Frame QCanBusFrame QCanBusFrame Route frames from ECUs to their proxies Reduce number of frames seen by HMI Avoid write buffer overflows Handle DM1 error frames Responsibilities
  11. 11. Copyright 2019, Burkhard Stubert Outline – A Deep Dive into Qt CAN Bus • Basic Concepts of CAN • Architecture of CAN Middleware • Connecting to the CAN Bus • Receiving CAN Frames • Sending CAN Frames • Generating Code for CAN Middleware 11
  12. 12. Copyright 2019, Burkhard Stubert Setting Up CAN Network Interface Systemd Service 12 [Unit] Description=can0 [Service] ExecStartPre=/sbin/ip link set can0 type can bitrate 250000 restart-ms 100 ExecStart=/sbin/ifconfig can0 up txqueuelen 64 Restart=on-failure RestartSec=1 [Install] WantedBy=multi-user.target Connect Hardware Terminal on ARM ECU on PC m m f f USB CAN Terminated 2-wire serial cable CAN
  13. 13. Copyright 2019, Burkhard Stubert Connecting To CAN Network Interface 13 // In canbusrouter.h QCanBusDevice *m_device; // In CanBusRouter constructor m_device = QCanBus::instance() ->createDevice("socketcan", "can0"); m_device->connectDevice(); // In CanBusRouter destructor if (m_device->state() == QCanBusDevice::ConnectedState) { m_device->disconnectDevice(); }
  14. 14. Copyright 2019, Burkhard Stubert Outline – A Deep Dive into Qt CAN Bus • Basic Concepts of CAN • Architecture of CAN Middleware • Connecting to the CAN Bus • Receiving CAN Frames • Sending CAN Frames • Generating Code for CAN Middleware 14
  15. 15. Copyright 2019, Burkhard Stubert Receiving CAN Frames (1) 16 QCanBusDevice EcuProxyBase BodyProxy CanBusRouter CanBusRouter framesReceived() onFramesReceived() framesReceived(saColl) onFramesReceived(saColl) receiveBroadcastFrame(jframe) void CanBusRouter::onFramesReceived() { auto frameColl{ m_device->readAllFrames() ); auto saColl{ m_frameCache.enqueueIncomingFrames( frameColl) }; emit framesReceived(saColl); } 11 12 13 14 15 16 17 0x00 0x21 0x13 * 11 12 14 17 * 13 16 * 15 0x00 0x21 0x13 frameColl saColl m_frameCache J1939Frames
  16. 16. Copyright 2019, Burkhard Stubert Receiving CAN Frames (2) 17 QCanBusDevice EcuProxyBase BodyProxy CanBusRouter CanBusRouter framesReceived() onFramesReceived() framesReceived(saColl) onFramesReceived(saColl) receiveBroadcastFrame(jframe) void EcuProxyBase::onFramesReceived(QSet<int> saColl) { if (!saColl.contains(ecuId())) return; for (const auto &jframe : m_router->takeReceivedFrame(ecuId())) { receiveBroadcastFrame(jframe) } } 0x00 * 11 12 14 0x13 * 15 ecuId() == 0x21 0x21 * 13 16
  17. 17. Copyright 2019, Burkhard Stubert Receiving CAN Frames (3) 18 QCanBusDevice EcuProxyBase BodyProxy CanBusRouter CanBusRouter framesReceived() onFramesReceived() framesReceived(saColl) onFramesReceived(saColl) receiveBroadcastFrame(jframe) void BodyProxy::receiveBroadcastFrame(J1939Frame jframe) { if (jframe.parameterGroupNumber() == A02AxleTilt::PGN) { auto payload { jframe.decode<A02AxleTilt::Payload>() }; // Do something with: // payload.tiltAxle1, // payload.tiltAxle2, // payload.tiltAxle3 }
  18. 18. Copyright 2019, Burkhard Stubert Decoding Payload of J1939 Frames 19 QCanBusFrame(0x18ff1021, "8501d3fc0a040000") J1939Frame{6, 0xff, 0x10, 0x28, "8501d3fc0a040000"} jframe.decode<A02AxleTilt::Payload>() // payload.tiltAxle1 == 389 == 0x0185 // payload.tiltAxle2 == -813 == 0xfcd3 // payload.tiltAxle3 == 1034 == 0x040a template <class P> P decode() const { auto dec{P{}}; qFromLittleEndian<qint64> (payload().data(), 1, &dec); return dec; } struct A02AxleTilt : public J1939Frame { static const quint32 PGN{0xff10U}; struct Payload { qint64 tiltAxle1 : 16; qint64 tiltAxle2 : 16; qint64 tiltAxle3 : 16; qint64 dummy0 : 16; }; Order of bit fields compiler-dependent
  19. 19. Copyright 2019, Burkhard Stubert Filtering in CAN Frame Cache • Forward fewer frames to HMI • CanBusRouter notifies Proxies only every 100-250ms – instead of instantly • Cache ignores older frames with same PGN: A1, B1 • Cache ignores frames not passing filter criterion: D, E 20 Received by CanBusRouter in 100 ms A1 B1 C A2 E D B2 C A2 B2 Filter Forwarded to ECU proxies and HMI
  20. 20. Copyright 2019, Burkhard Stubert Raw Filters in SocketCAN • Block CAN frames from entering SocketCAN module • See configuration parameter QCanBusDevice::RawFilterKey for implementation details 21 cframe.frameId() & filter.frameIdMask == filter.frameId & filter.frameIdMask • Set source address filter for every ECU with proxy • Every EcuProxy sets filter on creation • Block frames from ECUs without proxies 0x18ff1021 & 0x000000ff == 0x00000021 == 0x1fffff21 & 0x000000ff • Received CAN frame passes filter:
  21. 21. Copyright 2019, Burkhard Stubert Outline – A Deep Dive into Qt CAN Bus • Basic Concepts of CAN • Architecture of CAN Middleware • Connecting to the CAN Bus • Receiving CAN Frames • Sending CAN Frames • Generating Code for CAN Middleware 22
  22. 22. Copyright 2019, Burkhard Stubert WriteProperty Request and Response (1) 23 BodyProxy receivePeerToPeerFrame(jframe) void BodyProxy::writeProperty(quint16 pid, qint32 val) { m_router->writeFrame ( WritePropertyRequest(0x21, 0x28, pid, val) ); } BodyProxy writeProperty(pid, val) CanBusRouter writeFrame(jframe) void CanBusRouter::writeFrame(const J1939Frame &jframe) { m_device->writeFrame(jframe); } ECU 0x21 Body CanBusRouter
  23. 23. Copyright 2019, Burkhard Stubert WriteProperty Request and Response (2) 24 BodyProxy receivePeerToPeerFrame(jframe) BodyProxy readProperty(pid) CanBusRouter writeFrame(jframe) ECU 0x21 Body CanBusRouter Decode WritePropertyRequest(0x21, 0x28, pid, val) Write val of property pid to EEPROM Encode WritePropertyResponse(0x28, 0x21, pid, val) Decode WritePropertyResponse according to payload[0]
  24. 24. Copyright 2019, Burkhard Stubert Encoding Payload of J1939 Frames 25 WritePropertyRequest(0x21, 0x28, 0x0203, 0x096c61b5) J1939Frame{6, 0xef, 0x21, 0x28, encode(Payload{0x02, 0x0203, 0x096c61b5, 0x00})} QCanBusFrame(0x18ef2128, "020302b5616c0900") struct WritePropertyRequest : public J1939Frame { static const quint32 PGN{0xef}; struct Payload { qint64 groupFunction : 8; qint64 parameterId : 16; qint64 parameterValue : 32; qint64 dummy0 : 8; }; template <class Payload> static QByteArray encode(Payload payload) { auto enc{ QByteArray{8, 0x00} }; qToLittleEndian(payload, enc.data()); return enc; }
  25. 25. Copyright 2019, Burkhard Stubert TX Buffer Overflow in SocketCAN • Write more than txqueuelen frames to CAN bus without pause 26 • Common non-solutions • Wait for 20 ms between writes • Still fails for higher bus loads • Reduces bandwidth • Wait 20 ms for response frame • Not all writes have a response (e.g., firmware update) • Same problems as previous "solution" • Objectives for good solution • Adapts dynamically to bus load • Works for writes without responses // txqueuelen = 10 for (int i = 0; i < 25; ++i) { m_bodyProxy->readProperty(100 + i); } // Results in several errors: // No buffer space available // (QCanBusDevice::WriteError) Q1 P1Q2 Q25 P2 P25 Q1 P1 Q2 Q25P2 P25
  26. 26. Copyright 2019, Burkhard Stubert Receive Own Frame Before Next Write 27 P1 can0 Q1 Q1 Q2 Q11 Q2 Q20 Q2 Q25 Q2 Q3 Q25 Q2 P2 Q3 Q25 Q25 Q25 P25 Q25 CanFrameCache::m_outgoingCache m_device->setConfigurationParameter(QCanBusDevice::ReceiveOwnKey, true)
  27. 27. Copyright 2019, Burkhard Stubert Outline – A Deep Dive into Qt CAN Bus • Basic Concepts of CAN • Architecture of CAN Middleware • Connecting to the CAN Bus • Receiving CAN Frames • Sending CAN Frames • Generating Code for CAN Middleware 28
  28. 28. Copyright 2019, Burkhard Stubert Generating Code for Broadcast Frames 29 // From struct A02AxleTilt static const quint32 PGN{0xff10U}; A02AxleTilt(389, -813, 1034) J1939Frame{6U, 0xffU, 0x10, 0x21, "8501d3fc0a040000"} struct Payload { qint64 tiltAxle1 : 16; qint64 tiltAxle2 : 16; qint64 tiltAxle3 : 16; qint64 dummy0 : 16; }; [A02AxleTilt] ID=18FF1021h Type=Extended CycleTime=250 DLC=8 VAR=tiltAxle1 signed 0,16 /f:0.01 /o:0 /u:"°" /d:0 /min:-3100 /max:3100 VAR=tiltAxle2 signed 16,16 /f:0.01 /o:0 /u:"°" /d:0 /min:-2900 /max:2900 VAR=tiltAxle3 signed 32,16 /f:0.01 /o:0 /u:"°" /d:0 /min:-2900 /max:2900 hmiValue = factor * canValue + offset tiltAxle1 = (0.01 * 389 + 0)° = 3.89°
  29. 29. Copyright 2019, Burkhard Stubert Generating Code for Peer-To-Peer Frames 30 ReadPropertyRequest(0x21, 0x28, 313U) J1939Frame{6U, 0xefU, 0x21, 0x28, "0139010000000000"} static const quint32 PGN{0xef00U}; struct Payload { qint64 groupFunction : 8; qint64 parameterId : 16; qint64 parameterValue : 32; qint64 dummy0 : 8; }; // From file A02.dbm [VARIABLE] VAR_CODE_NAME=gridHeight VAR_TYPE=TUINT16 VAR_HST_COM_ID=313 VAR_HST_OFFSET=0 VAR_HST_FACTOR=0.001 VAR_HST_UNIT=m VAR_MIN=0 VAR_MAX=890 VAR_DFT_1=360 VAR_HST_ACC_PRO=RW VAR_HST_DESCRIPT_1=Hoehe Leitrost hmiValue = VAR_HST_FACTOR * canValue + VAR_HST_OFFSET gridHeight = (0.001 * 357 + 0) m = 0.357 m
  30. 30. This presentation is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright 2019, Burkhard Stubert Thank you J Mail: burkhard.stubert@embeddeduse.com Web: http://www.embeddeduse.com Code: https://github.com/bstubert/embeddeduse/tree/master/BlogPosts/CanComm (MIT)

×