Libraries to support working with GMLAN - General Motors CAN BUS network in most of their vehicles between 2007-present day. Please note this is a work in progress and not guaranteed to be correct, use at your own risk! Read commit logs / subscribe to see what has been added, it's a work in progress after all ;)
GMLAN.cpp@7:5ec65e6e8095, 2013-04-07 (annotated)
- Committer:
- foxdie
- Date:
- Sun Apr 07 18:20:01 2013 +0000
- Revision:
- 7:5ec65e6e8095
- Parent:
- 6:32592425aa57
- Child:
- 8:bc97fa5d306e
Corrected an issue where multi-frame packets that fit perfectly into whole frames were truncating the last byte
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
foxdie | 0:9266fbfbef88 | 1 | /* |
foxdie | 0:9266fbfbef88 | 2 | GMLAN.cpp - Source file for GMLAN Library |
foxdie | 0:9266fbfbef88 | 3 | |
foxdie | 0:9266fbfbef88 | 4 | GMLAN is a Controller Area Network Bus used in General Motors vehicles from |
foxdie | 0:9266fbfbef88 | 5 | roughly 2007-onwards. Its purpose is to allow various Electronic Control Units |
foxdie | 0:9266fbfbef88 | 6 | (aka ECUs) within a modern vehicle to share information and enact procedures. |
foxdie | 0:9266fbfbef88 | 7 | |
foxdie | 0:9266fbfbef88 | 8 | An example of this would be communication between the HU (Head unit) and the |
foxdie | 0:9266fbfbef88 | 9 | DIC (Dashboard Information Cluster), when you adjust the volume up / down, this |
foxdie | 0:9266fbfbef88 | 10 | is reported to the cluster to be displayed. |
foxdie | 0:9266fbfbef88 | 11 | |
foxdie | 0:9266fbfbef88 | 12 | It is the function of this library to "crack open" this world to allow anyone |
foxdie | 0:9266fbfbef88 | 13 | with only as little as a few hours of C++ programming under their belt to get |
foxdie | 0:9266fbfbef88 | 14 | started in what can sometimes seem a daunting world. |
foxdie | 0:9266fbfbef88 | 15 | |
foxdie | 0:9266fbfbef88 | 16 | Jason Gaunt, 18th Feb 2013 |
foxdie | 0:9266fbfbef88 | 17 | */ |
foxdie | 0:9266fbfbef88 | 18 | |
foxdie | 5:d0b067be6d44 | 19 | #include "mbed.h" |
foxdie | 0:9266fbfbef88 | 20 | #include "GMLAN.h" |
foxdie | 5:d0b067be6d44 | 21 | #include <vector> |
foxdie | 0:9266fbfbef88 | 22 | |
foxdie | 0:9266fbfbef88 | 23 | void CANHeader::decode(int _header) { |
foxdie | 3:09fdfec053cd | 24 | if (_header < 0x800) |
foxdie | 3:09fdfec053cd | 25 | { |
foxdie | 3:09fdfec053cd | 26 | // 11-bit header |
foxdie | 4:486fec88517e | 27 | arbitrationID = (_header >> 0) & 0x7FF; |
foxdie | 3:09fdfec053cd | 28 | } else { |
foxdie | 3:09fdfec053cd | 29 | // 29-bit header |
foxdie | 3:09fdfec053cd | 30 | priorityID = (_header >> 26) & 0x7; |
foxdie | 3:09fdfec053cd | 31 | arbitrationID = (_header >> 13) & 0x1FFF; |
foxdie | 3:09fdfec053cd | 32 | senderID = (_header >> 0) & 0x1FFF; |
foxdie | 3:09fdfec053cd | 33 | } |
foxdie | 0:9266fbfbef88 | 34 | } |
foxdie | 3:09fdfec053cd | 35 | int CANHeader::encode29bit(void) { |
foxdie | 0:9266fbfbef88 | 36 | long int buffer = 0; |
foxdie | 0:9266fbfbef88 | 37 | buffer = (buffer << 3) | 0x0; // 3 bit padding |
foxdie | 0:9266fbfbef88 | 38 | buffer = (buffer << 3) | priorityID; |
foxdie | 0:9266fbfbef88 | 39 | buffer = (buffer << 13) | arbitrationID; |
foxdie | 0:9266fbfbef88 | 40 | buffer = (buffer << 13) | senderID; |
foxdie | 0:9266fbfbef88 | 41 | return buffer; |
foxdie | 4:486fec88517e | 42 | } |
foxdie | 4:486fec88517e | 43 | int CANHeader::encode11bit(void) { |
foxdie | 4:486fec88517e | 44 | short int buffer = 0; |
foxdie | 4:486fec88517e | 45 | buffer = (buffer << 5) | 0x0; // 5 bit padding |
foxdie | 4:486fec88517e | 46 | buffer = (buffer << 11) | arbitrationID; |
foxdie | 4:486fec88517e | 47 | return buffer; |
foxdie | 5:d0b067be6d44 | 48 | } |
foxdie | 5:d0b067be6d44 | 49 | |
foxdie | 5:d0b067be6d44 | 50 | |
foxdie | 5:d0b067be6d44 | 51 | GMLAN_Message::GMLAN_Message(int _priority, int _arbitration, int _sender, |
foxdie | 5:d0b067be6d44 | 52 | int _b0, int _b1, int _b2, int _b3, int _b4, int _b5, int _b6, int _b7) { |
foxdie | 5:d0b067be6d44 | 53 | priority = _priority; |
foxdie | 5:d0b067be6d44 | 54 | arbitration = _arbitration; |
foxdie | 5:d0b067be6d44 | 55 | sender = _sender; |
foxdie | 5:d0b067be6d44 | 56 | if (_b0 != -1) data.push_back(_b0); |
foxdie | 5:d0b067be6d44 | 57 | if (_b1 != -1) data.push_back(_b1); |
foxdie | 5:d0b067be6d44 | 58 | if (_b2 != -1) data.push_back(_b2); |
foxdie | 5:d0b067be6d44 | 59 | if (_b3 != -1) data.push_back(_b3); |
foxdie | 5:d0b067be6d44 | 60 | if (_b4 != -1) data.push_back(_b4); |
foxdie | 5:d0b067be6d44 | 61 | if (_b5 != -1) data.push_back(_b5); |
foxdie | 5:d0b067be6d44 | 62 | if (_b6 != -1) data.push_back(_b6); |
foxdie | 5:d0b067be6d44 | 63 | if (_b7 != -1) data.push_back(_b7); |
foxdie | 5:d0b067be6d44 | 64 | } |
foxdie | 5:d0b067be6d44 | 65 | CANMessage GMLAN_Message::generate(void) { |
foxdie | 5:d0b067be6d44 | 66 | CANHeader hdr; |
foxdie | 5:d0b067be6d44 | 67 | hdr.priority(priority); |
foxdie | 5:d0b067be6d44 | 68 | hdr.arbitration(arbitration); |
foxdie | 5:d0b067be6d44 | 69 | hdr.sender(sender); |
foxdie | 5:d0b067be6d44 | 70 | |
foxdie | 5:d0b067be6d44 | 71 | char datatochars [data.size()]; |
foxdie | 5:d0b067be6d44 | 72 | for (int i = 0; i < data.size(); i++) datatochars[i] = data[i]; |
foxdie | 5:d0b067be6d44 | 73 | |
foxdie | 5:d0b067be6d44 | 74 | if (sender > 0x0) |
foxdie | 5:d0b067be6d44 | 75 | return CANMessage(hdr.encode29bit(), datatochars, data.size(), CANData, CANExtended); |
foxdie | 5:d0b067be6d44 | 76 | else |
foxdie | 5:d0b067be6d44 | 77 | return CANMessage(arbitration, datatochars, data.size(), CANData, CANStandard); |
foxdie | 6:32592425aa57 | 78 | } |
foxdie | 6:32592425aa57 | 79 | |
foxdie | 7:5ec65e6e8095 | 80 | GMLAN_11Bit_Request::GMLAN_11Bit_Request(int _id, vector<char> _request, bool _await_response) { |
foxdie | 6:32592425aa57 | 81 | id = _id; |
foxdie | 6:32592425aa57 | 82 | request_data = _request; |
foxdie | 7:5ec65e6e8095 | 83 | await_response = _await_response; |
foxdie | 6:32592425aa57 | 84 | tx_bytes = rx_bytes = 0; |
foxdie | 6:32592425aa57 | 85 | tx_frame_counter = rx_frame_counter = 1; |
foxdie | 6:32592425aa57 | 86 | const char _fp [8] = {0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA}; |
foxdie | 6:32592425aa57 | 87 | memcpy(frame_padding, _fp, 8); |
foxdie | 6:32592425aa57 | 88 | request_state = GMLAN_STATE_READY_TO_SEND; |
foxdie | 6:32592425aa57 | 89 | } |
foxdie | 6:32592425aa57 | 90 | CANMessage GMLAN_11Bit_Request::getNextFrame(void) { |
foxdie | 6:32592425aa57 | 91 | char datatochars [8]; |
foxdie | 6:32592425aa57 | 92 | memcpy(datatochars, frame_padding, 8); |
foxdie | 6:32592425aa57 | 93 | |
foxdie | 6:32592425aa57 | 94 | if (request_data.size() < 8) { |
foxdie | 6:32592425aa57 | 95 | // Unsegmented frame |
foxdie | 6:32592425aa57 | 96 | datatochars[0] = (GMLAN_PCI_UNSEGMENTED << 4) | (request_data.size() & 0xF); |
foxdie | 6:32592425aa57 | 97 | for (int i = 0; i < request_data.size(); i++) datatochars[i+1] = request_data[i]; |
foxdie | 6:32592425aa57 | 98 | request_state = GMLAN_STATE_AWAITING_REPLY; |
foxdie | 6:32592425aa57 | 99 | } else if (tx_bytes == 0) { |
foxdie | 6:32592425aa57 | 100 | // First segmented frame |
foxdie | 6:32592425aa57 | 101 | datatochars[0] = (GMLAN_PCI_SEGMENTED << 4) | ((request_data.size() >> 8) & 0xF); |
foxdie | 6:32592425aa57 | 102 | datatochars[1] = request_data.size() & 0xFF; |
foxdie | 6:32592425aa57 | 103 | for (int i = 0; i < 6; i++) { |
foxdie | 6:32592425aa57 | 104 | datatochars[i+2] = request_data[i]; |
foxdie | 6:32592425aa57 | 105 | tx_bytes++; |
foxdie | 6:32592425aa57 | 106 | } |
foxdie | 6:32592425aa57 | 107 | request_state = GMLAN_STATE_AWAITING_FC; |
foxdie | 6:32592425aa57 | 108 | } else if (tx_bytes <= request_data.size()) { |
foxdie | 6:32592425aa57 | 109 | // Additional segmented frame with data left to transmit |
foxdie | 6:32592425aa57 | 110 | datatochars[0] = (GMLAN_PCI_ADDITIONAL << 4) | (tx_frame_counter & 0xF); |
foxdie | 6:32592425aa57 | 111 | int old_tx_bytes = tx_bytes; |
foxdie | 6:32592425aa57 | 112 | for (int i = old_tx_bytes; i < old_tx_bytes + 7; i++) { |
foxdie | 6:32592425aa57 | 113 | if (i >= request_data.size()) break; |
foxdie | 6:32592425aa57 | 114 | datatochars[(i+1)-old_tx_bytes] = request_data[i]; |
foxdie | 6:32592425aa57 | 115 | tx_bytes++; |
foxdie | 6:32592425aa57 | 116 | } |
foxdie | 6:32592425aa57 | 117 | tx_frame_counter++; |
foxdie | 6:32592425aa57 | 118 | if (tx_frame_counter > 0xF) tx_frame_counter = 0x0; |
foxdie | 6:32592425aa57 | 119 | } |
foxdie | 6:32592425aa57 | 120 | |
foxdie | 7:5ec65e6e8095 | 121 | if (tx_bytes >= request_data.size()) { |
foxdie | 7:5ec65e6e8095 | 122 | if (await_response == true) request_state = GMLAN_STATE_AWAITING_REPLY; |
foxdie | 7:5ec65e6e8095 | 123 | else request_state = GMLAN_STATE_COMPLETED; |
foxdie | 7:5ec65e6e8095 | 124 | } |
foxdie | 6:32592425aa57 | 125 | |
foxdie | 6:32592425aa57 | 126 | return CANMessage(id, datatochars, 8, CANData, CANStandard); |
foxdie | 6:32592425aa57 | 127 | } |
foxdie | 6:32592425aa57 | 128 | CANMessage GMLAN_11Bit_Request::getFlowControl(void) { |
foxdie | 6:32592425aa57 | 129 | request_state = GMLAN_STATE_AWAITING_REPLY; |
foxdie | 6:32592425aa57 | 130 | GMLAN_Message buffer = GMLAN_Message(0x0, id, 0x0, 0x30, 0x0, 0x0); |
foxdie | 6:32592425aa57 | 131 | return buffer.generate(); |
foxdie | 6:32592425aa57 | 132 | } |
foxdie | 6:32592425aa57 | 133 | void GMLAN_11Bit_Request::processFrame(CANMessage msg) { |
foxdie | 6:32592425aa57 | 134 | if (((msg.id & 0xFF) == (id & 0xFF)) && (request_state == GMLAN_STATE_AWAITING_REPLY)) { |
foxdie | 6:32592425aa57 | 135 | // Only handle requests we've instigated |
foxdie | 6:32592425aa57 | 136 | char datatochars [8]; |
foxdie | 6:32592425aa57 | 137 | memcpy(datatochars, msg.data, 8); |
foxdie | 6:32592425aa57 | 138 | |
foxdie | 6:32592425aa57 | 139 | if (((datatochars[0] >> 4) & 0xF) == GMLAN_PCI_UNSEGMENTED) { |
foxdie | 6:32592425aa57 | 140 | // Unsegmented frame |
foxdie | 7:5ec65e6e8095 | 141 | rx_bytes = (datatochars[0] & 0xF); |
foxdie | 6:32592425aa57 | 142 | if (datatochars[1] == GMLAN_SID_ERROR) { |
foxdie | 6:32592425aa57 | 143 | // Error frame |
foxdie | 6:32592425aa57 | 144 | if ((rx_bytes == 3) && (datatochars[3] == 0x78)) return; // "Still processing request" message, ignore this one |
foxdie | 6:32592425aa57 | 145 | request_state = GMLAN_STATE_ERROR; |
foxdie | 6:32592425aa57 | 146 | } else request_state = GMLAN_STATE_COMPLETED; |
foxdie | 6:32592425aa57 | 147 | for (int i = 1; i < (rx_bytes+1); i++) response_data.push_back(datatochars[i]); |
foxdie | 6:32592425aa57 | 148 | } else if (((datatochars[0] >> 4) & 0xF) == GMLAN_PCI_SEGMENTED) { |
foxdie | 6:32592425aa57 | 149 | // First segmented frame |
foxdie | 7:5ec65e6e8095 | 150 | rx_bytes = (datatochars[0] & 0xF); |
foxdie | 6:32592425aa57 | 151 | rx_bytes = (rx_bytes << 8) | datatochars[1]; |
foxdie | 6:32592425aa57 | 152 | for (int i = 2; i < 8; i++) { |
foxdie | 6:32592425aa57 | 153 | if ((i - 2) >= rx_bytes) { |
foxdie | 6:32592425aa57 | 154 | // Safety net for incorrectly formatted packets |
foxdie | 6:32592425aa57 | 155 | request_state = GMLAN_STATE_COMPLETED; |
foxdie | 6:32592425aa57 | 156 | return; |
foxdie | 6:32592425aa57 | 157 | } |
foxdie | 6:32592425aa57 | 158 | response_data.push_back(datatochars[i]); |
foxdie | 6:32592425aa57 | 159 | } |
foxdie | 6:32592425aa57 | 160 | request_state = GMLAN_STATE_SEND_FC; |
foxdie | 6:32592425aa57 | 161 | } else if (((datatochars[0] >> 4) & 0xF) == GMLAN_PCI_ADDITIONAL) { |
foxdie | 6:32592425aa57 | 162 | // Additional segmented frame |
foxdie | 6:32592425aa57 | 163 | // TODO check for frame order |
foxdie | 6:32592425aa57 | 164 | for (int i = 1; i < 8; i++) { |
foxdie | 7:5ec65e6e8095 | 165 | if (response_data.size() >= rx_bytes) { |
foxdie | 6:32592425aa57 | 166 | request_state = GMLAN_STATE_COMPLETED; |
foxdie | 6:32592425aa57 | 167 | return; |
foxdie | 6:32592425aa57 | 168 | } |
foxdie | 6:32592425aa57 | 169 | response_data.push_back(datatochars[i]); |
foxdie | 6:32592425aa57 | 170 | } |
foxdie | 7:5ec65e6e8095 | 171 | if (response_data.size() >= rx_bytes) { |
foxdie | 7:5ec65e6e8095 | 172 | request_state = GMLAN_STATE_COMPLETED; |
foxdie | 7:5ec65e6e8095 | 173 | return; |
foxdie | 7:5ec65e6e8095 | 174 | } |
foxdie | 6:32592425aa57 | 175 | } else if (((datatochars[0] >> 4) & 0xF) == GMLAN_PCI_FLOW_CONTROL) { |
foxdie | 6:32592425aa57 | 176 | // Flow control frame |
foxdie | 6:32592425aa57 | 177 | request_state = GMLAN_STATE_SEND_DATA; |
foxdie | 6:32592425aa57 | 178 | } |
foxdie | 6:32592425aa57 | 179 | } |
foxdie | 0:9266fbfbef88 | 180 | } |