Basic implementation of Xbus message parsing and generation for embedded processors. The code has no dependencies and should also work for other MCU architectures than ARM provided a C99 compiler is available.

Dependents:   MTi-1_example_LPC1768 MTi-1_rikbeun MTi-1_example MTi-1_example

For an example of using the Xbus library to communicate with an MTi-1 series device using a full-duplex UART see:

Import programMTi-1_example

Example of using Xbus library to communicate with an MTi-1 series device using a full-duplex UART connection.

Committer:
tjerkhofmeijer
Date:
Fri Oct 02 16:22:33 2015 +0200
Revision:
1:c24f69a2eff4
Parent:
0:eb25b1785ee4
Xbus library is updated to support MTi 1-series' I2C and SPI interfaces

Who changed what in which revision?

UserRevisionLine numberNew contents of line
alexandery 0:eb25b1785ee4 1 /*!
alexandery 0:eb25b1785ee4 2 * \file
alexandery 0:eb25b1785ee4 3 * \copyright Copyright (C) Xsens Technologies B.V., 2015.
alexandery 0:eb25b1785ee4 4 *
alexandery 0:eb25b1785ee4 5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
alexandery 0:eb25b1785ee4 6 * use this file except in compliance with the License. You may obtain a copy
alexandery 0:eb25b1785ee4 7 * of the License at
alexandery 0:eb25b1785ee4 8 *
alexandery 0:eb25b1785ee4 9 * http://www.apache.org/licenses/LICENSE-2.0
alexandery 0:eb25b1785ee4 10 *
alexandery 0:eb25b1785ee4 11 * Unless required by applicable law or agreed to in writing, software
alexandery 0:eb25b1785ee4 12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
alexandery 0:eb25b1785ee4 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
alexandery 0:eb25b1785ee4 14 * License for the specific language governing permissions and limitations
alexandery 0:eb25b1785ee4 15 * under the License.
alexandery 0:eb25b1785ee4 16 */
alexandery 0:eb25b1785ee4 17
alexandery 0:eb25b1785ee4 18 #include "xbusparser.h"
alexandery 0:eb25b1785ee4 19 #include "xbusdef.h"
alexandery 0:eb25b1785ee4 20 #include "xbusutility.h"
alexandery 0:eb25b1785ee4 21 #include <stdlib.h>
alexandery 0:eb25b1785ee4 22 #include <string.h>
alexandery 0:eb25b1785ee4 23
alexandery 0:eb25b1785ee4 24 /*! \brief XbusParser states. */
alexandery 0:eb25b1785ee4 25 enum XbusParserState
alexandery 0:eb25b1785ee4 26 {
alexandery 0:eb25b1785ee4 27 XBPS_Preamble, /*!< \brief Looking for preamble. */
alexandery 0:eb25b1785ee4 28 XBPS_BusId, /*!< \brief Waiting for bus ID. */
alexandery 0:eb25b1785ee4 29 XBPS_MessageId, /*!< \brief Waiting for message ID. */
alexandery 0:eb25b1785ee4 30 XBPS_Length, /*!< \brief Waiting for length. */
alexandery 0:eb25b1785ee4 31 XBPS_ExtendedLengthMsb, /*!< \brief Waiting for extended length MSB*/
alexandery 0:eb25b1785ee4 32 XBPS_ExtendedLengthLsb, /*!< \brief Waiting for extended length LSB*/
alexandery 0:eb25b1785ee4 33 XBPS_Payload, /*!< \brief Reading payload. */
alexandery 0:eb25b1785ee4 34 XBPS_Checksum /*!< \brief Waiting for checksum. */
alexandery 0:eb25b1785ee4 35 };
alexandery 0:eb25b1785ee4 36
alexandery 0:eb25b1785ee4 37 /*!
alexandery 0:eb25b1785ee4 38 * \brief Xbus Parser state structure.
alexandery 0:eb25b1785ee4 39 */
alexandery 0:eb25b1785ee4 40 struct XbusParser
alexandery 0:eb25b1785ee4 41 {
alexandery 0:eb25b1785ee4 42 /*! \brief Callbacks for memory management, and message handling. */
alexandery 0:eb25b1785ee4 43 struct XbusParserCallback callbacks;
alexandery 0:eb25b1785ee4 44 /*! \brief Storage for the current message being received. */
alexandery 0:eb25b1785ee4 45 struct XbusMessage currentMessage;
alexandery 0:eb25b1785ee4 46 /*! \brief The number of bytes of payload received for the current message. */
alexandery 0:eb25b1785ee4 47 uint16_t payloadReceived;
alexandery 0:eb25b1785ee4 48 /*! \brief The calculated checksum for the current message. */
alexandery 0:eb25b1785ee4 49 uint8_t checksum;
alexandery 0:eb25b1785ee4 50 /*! \brief The state of the parser. */
alexandery 0:eb25b1785ee4 51 enum XbusParserState state;
alexandery 0:eb25b1785ee4 52 };
alexandery 0:eb25b1785ee4 53
alexandery 0:eb25b1785ee4 54 /*!
alexandery 0:eb25b1785ee4 55 * \brief Get the amount of memory needed for the XbusParser structure.
alexandery 0:eb25b1785ee4 56 */
alexandery 0:eb25b1785ee4 57 size_t XbusParser_mem(void)
alexandery 0:eb25b1785ee4 58 {
alexandery 0:eb25b1785ee4 59 return sizeof(struct XbusParser);
alexandery 0:eb25b1785ee4 60 }
alexandery 0:eb25b1785ee4 61
alexandery 0:eb25b1785ee4 62 /*!
alexandery 0:eb25b1785ee4 63 * \brief Create a new XbusParser object.
alexandery 0:eb25b1785ee4 64 * \param callback Pointer to callback structure containing callback functions
alexandery 0:eb25b1785ee4 65 * for memory management and handling received messages.
alexandery 0:eb25b1785ee4 66 * \returns Pointer the new XbusParser structure.
alexandery 0:eb25b1785ee4 67 *
alexandery 0:eb25b1785ee4 68 * Uses malloc to allocate the memory required for the parser.
alexandery 0:eb25b1785ee4 69 */
alexandery 0:eb25b1785ee4 70 struct XbusParser* XbusParser_create(struct XbusParserCallback const* callback)
alexandery 0:eb25b1785ee4 71 {
alexandery 0:eb25b1785ee4 72 void* mem = malloc(XbusParser_mem());
alexandery 0:eb25b1785ee4 73 if (mem)
alexandery 0:eb25b1785ee4 74 {
alexandery 0:eb25b1785ee4 75 return XbusParser_init(mem, callback);
alexandery 0:eb25b1785ee4 76 }
alexandery 0:eb25b1785ee4 77 return NULL;
alexandery 0:eb25b1785ee4 78 }
alexandery 0:eb25b1785ee4 79
alexandery 0:eb25b1785ee4 80 /*!
alexandery 0:eb25b1785ee4 81 * \brief Frees an XbusParser structure allocated by XbusParser_create().
alexandery 0:eb25b1785ee4 82 */
alexandery 0:eb25b1785ee4 83 void XbusParser_destroy(struct XbusParser* parser)
alexandery 0:eb25b1785ee4 84 {
alexandery 0:eb25b1785ee4 85 free(parser);
alexandery 0:eb25b1785ee4 86 }
alexandery 0:eb25b1785ee4 87
alexandery 0:eb25b1785ee4 88 /*!
alexandery 0:eb25b1785ee4 89 * \brief Initializes an XbusParser in the passed memory location.
alexandery 0:eb25b1785ee4 90 * \param parserMem Pointer to memory to use for storing parser state. Should
alexandery 0:eb25b1785ee4 91 * be at least as big as the value returned by XbusParser_mem().
alexandery 0:eb25b1785ee4 92 * \param callback Pointer to callback structure containing callback functions
alexandery 0:eb25b1785ee4 93 * for memory management and handling received messages.
alexandery 0:eb25b1785ee4 94 * \returns Initialized XbusParser structure.
alexandery 0:eb25b1785ee4 95 */
alexandery 0:eb25b1785ee4 96 struct XbusParser* XbusParser_init(void* parserMem, struct XbusParserCallback const* callback)
alexandery 0:eb25b1785ee4 97 {
alexandery 0:eb25b1785ee4 98 struct XbusParser* parser = (struct XbusParser*)parserMem;
alexandery 0:eb25b1785ee4 99 parser->state = XBPS_Preamble;
alexandery 0:eb25b1785ee4 100 parser->callbacks.allocateBuffer = callback->allocateBuffer;
alexandery 0:eb25b1785ee4 101 parser->callbacks.deallocateBuffer = callback->deallocateBuffer;
alexandery 0:eb25b1785ee4 102 parser->callbacks.handleMessage = callback->handleMessage;
alexandery 0:eb25b1785ee4 103 return parser;
alexandery 0:eb25b1785ee4 104 }
alexandery 0:eb25b1785ee4 105
alexandery 0:eb25b1785ee4 106 /*!
alexandery 0:eb25b1785ee4 107 * \brief Parse an XMID_DeviceId message to extract the device ID value.
alexandery 0:eb25b1785ee4 108
alexandery 0:eb25b1785ee4 109 * Replaces the raw Xbus message data with the device ID.
alexandery 0:eb25b1785ee4 110 */
alexandery 0:eb25b1785ee4 111 static void parseDeviceId(struct XbusParser* parser, uint8_t const* rawData)
alexandery 0:eb25b1785ee4 112 {
alexandery 0:eb25b1785ee4 113 uint32_t* deviceId = parser->callbacks.allocateBuffer(sizeof(uint32_t));
alexandery 0:eb25b1785ee4 114 if (deviceId)
alexandery 0:eb25b1785ee4 115 {
alexandery 0:eb25b1785ee4 116 XbusUtility_readU32(deviceId, rawData);
alexandery 0:eb25b1785ee4 117 parser->currentMessage.data = deviceId;
alexandery 0:eb25b1785ee4 118 parser->currentMessage.length = 1;
alexandery 0:eb25b1785ee4 119 }
alexandery 0:eb25b1785ee4 120 else
alexandery 0:eb25b1785ee4 121 {
alexandery 0:eb25b1785ee4 122 parser->currentMessage.data = NULL;
alexandery 0:eb25b1785ee4 123 }
alexandery 0:eb25b1785ee4 124 }
alexandery 0:eb25b1785ee4 125
alexandery 0:eb25b1785ee4 126 /*!
alexandery 0:eb25b1785ee4 127 * \brief Parse an XMID_OutputConfig message.
alexandery 0:eb25b1785ee4 128 *
alexandery 0:eb25b1785ee4 129 * Replaces the raw Xbus message data with an array of OutputConfiguration
alexandery 0:eb25b1785ee4 130 * structures.
alexandery 0:eb25b1785ee4 131 */
alexandery 0:eb25b1785ee4 132 static void parseOutputConfig(struct XbusParser* parser, uint8_t const* rawData)
alexandery 0:eb25b1785ee4 133 {
alexandery 0:eb25b1785ee4 134 uint8_t fields = parser->currentMessage.length / 4;
alexandery 0:eb25b1785ee4 135 struct OutputConfiguration* conf = parser->callbacks.allocateBuffer(fields * sizeof(struct OutputConfiguration));
alexandery 0:eb25b1785ee4 136 if (conf)
alexandery 0:eb25b1785ee4 137 {
alexandery 0:eb25b1785ee4 138 parser->currentMessage.data = conf;
alexandery 0:eb25b1785ee4 139 parser->currentMessage.length = fields;
alexandery 0:eb25b1785ee4 140
alexandery 0:eb25b1785ee4 141 for (int i = 0; i < fields; ++i)
alexandery 0:eb25b1785ee4 142 {
alexandery 0:eb25b1785ee4 143 rawData = XbusUtility_readU16((uint16_t*)&conf->dtype, rawData);
alexandery 0:eb25b1785ee4 144 rawData = XbusUtility_readU16(&conf->freq, rawData);
alexandery 0:eb25b1785ee4 145 ++conf;
alexandery 0:eb25b1785ee4 146 }
alexandery 0:eb25b1785ee4 147 }
alexandery 0:eb25b1785ee4 148 else
alexandery 0:eb25b1785ee4 149 {
alexandery 0:eb25b1785ee4 150 parser->currentMessage.data = NULL;
alexandery 0:eb25b1785ee4 151 }
alexandery 0:eb25b1785ee4 152 }
alexandery 0:eb25b1785ee4 153
alexandery 0:eb25b1785ee4 154 /*!
alexandery 0:eb25b1785ee4 155 * \brief Converts raw Xbus payload data to native structures if possible.
alexandery 0:eb25b1785ee4 156 *
alexandery 0:eb25b1785ee4 157 * Raw data payloads are converted to native data structures and the
alexandery 0:eb25b1785ee4 158 * message data pointer is changed to point to the native structure.
alexandery 0:eb25b1785ee4 159 * The raw data is automatically deallocated.
alexandery 0:eb25b1785ee4 160 */
alexandery 0:eb25b1785ee4 161 static void parseMessagePayload(struct XbusParser* parser)
alexandery 0:eb25b1785ee4 162 {
alexandery 0:eb25b1785ee4 163 uint8_t const* const rawData = parser->currentMessage.data;
alexandery 0:eb25b1785ee4 164 switch (parser->currentMessage.mid)
alexandery 0:eb25b1785ee4 165 {
alexandery 0:eb25b1785ee4 166 default:
alexandery 0:eb25b1785ee4 167 // Leave parsing and memory management to user code
alexandery 0:eb25b1785ee4 168 return;
alexandery 0:eb25b1785ee4 169
alexandery 0:eb25b1785ee4 170 case XMID_DeviceId:
alexandery 0:eb25b1785ee4 171 parseDeviceId(parser, rawData);
alexandery 0:eb25b1785ee4 172 break;
alexandery 0:eb25b1785ee4 173
alexandery 0:eb25b1785ee4 174 case XMID_OutputConfig:
alexandery 0:eb25b1785ee4 175 parseOutputConfig(parser, rawData);
alexandery 0:eb25b1785ee4 176 break;
alexandery 0:eb25b1785ee4 177 }
alexandery 0:eb25b1785ee4 178
alexandery 0:eb25b1785ee4 179 if (rawData)
alexandery 0:eb25b1785ee4 180 parser->callbacks.deallocateBuffer(rawData);
alexandery 0:eb25b1785ee4 181 }
alexandery 0:eb25b1785ee4 182
alexandery 0:eb25b1785ee4 183 /*!
alexandery 0:eb25b1785ee4 184 * \brief Prepare for receiving a message payload.
alexandery 0:eb25b1785ee4 185 *
alexandery 0:eb25b1785ee4 186 * Requests a memory area to store the received data to using the
alexandery 0:eb25b1785ee4 187 * registered callbacks.
alexandery 0:eb25b1785ee4 188 */
alexandery 0:eb25b1785ee4 189 void prepareForPayload(struct XbusParser* parser)
alexandery 0:eb25b1785ee4 190 {
alexandery 0:eb25b1785ee4 191 parser->payloadReceived = 0;
alexandery 0:eb25b1785ee4 192 parser->currentMessage.data = parser->callbacks.allocateBuffer(parser->currentMessage.length);
alexandery 0:eb25b1785ee4 193 }
alexandery 0:eb25b1785ee4 194
alexandery 0:eb25b1785ee4 195 /*!
alexandery 0:eb25b1785ee4 196 * \brief Parse a byte of data from a motion tracker.
alexandery 0:eb25b1785ee4 197 *
alexandery 0:eb25b1785ee4 198 * When a complete message is received the user will be notified by a call
alexandery 0:eb25b1785ee4 199 * to the handleMessage() callback function.
alexandery 0:eb25b1785ee4 200 */
alexandery 0:eb25b1785ee4 201 void XbusParser_parseByte(struct XbusParser* parser, const uint8_t byte)
alexandery 0:eb25b1785ee4 202 {
alexandery 0:eb25b1785ee4 203 switch (parser->state)
alexandery 0:eb25b1785ee4 204 {
alexandery 0:eb25b1785ee4 205 case XBPS_Preamble:
alexandery 0:eb25b1785ee4 206 if (byte == XBUS_PREAMBLE)
alexandery 0:eb25b1785ee4 207 {
alexandery 0:eb25b1785ee4 208 parser->checksum = 0;
alexandery 0:eb25b1785ee4 209 parser->state = XBPS_BusId;
alexandery 0:eb25b1785ee4 210 }
alexandery 0:eb25b1785ee4 211 break;
alexandery 0:eb25b1785ee4 212
alexandery 0:eb25b1785ee4 213 case XBPS_BusId:
alexandery 0:eb25b1785ee4 214 parser->checksum += byte;
alexandery 0:eb25b1785ee4 215 parser->state = XBPS_MessageId;
alexandery 0:eb25b1785ee4 216 break;
alexandery 0:eb25b1785ee4 217
alexandery 0:eb25b1785ee4 218 case XBPS_MessageId:
alexandery 0:eb25b1785ee4 219 parser->checksum += byte;
alexandery 0:eb25b1785ee4 220 parser->currentMessage.mid = (enum XsMessageId)byte;
alexandery 0:eb25b1785ee4 221 parser->state = XBPS_Length;
alexandery 0:eb25b1785ee4 222 break;
alexandery 0:eb25b1785ee4 223
alexandery 0:eb25b1785ee4 224 case XBPS_Length:
alexandery 0:eb25b1785ee4 225 parser->checksum += byte;
alexandery 0:eb25b1785ee4 226 if (byte == XBUS_NO_PAYLOAD)
alexandery 0:eb25b1785ee4 227 {
alexandery 0:eb25b1785ee4 228 parser->currentMessage.length = byte;
alexandery 0:eb25b1785ee4 229 parser->currentMessage.data = NULL;
alexandery 0:eb25b1785ee4 230 parser->state = XBPS_Checksum;
alexandery 0:eb25b1785ee4 231 }
alexandery 0:eb25b1785ee4 232 else if (byte < XBUS_EXTENDED_LENGTH)
alexandery 0:eb25b1785ee4 233 {
alexandery 0:eb25b1785ee4 234 parser->currentMessage.length = byte;
alexandery 0:eb25b1785ee4 235 prepareForPayload(parser);
alexandery 0:eb25b1785ee4 236 parser->state = XBPS_Payload;
alexandery 0:eb25b1785ee4 237 }
alexandery 0:eb25b1785ee4 238 else
alexandery 0:eb25b1785ee4 239 {
alexandery 0:eb25b1785ee4 240 parser->state = XBPS_ExtendedLengthMsb;
alexandery 0:eb25b1785ee4 241 }
alexandery 0:eb25b1785ee4 242 break;
alexandery 0:eb25b1785ee4 243
alexandery 0:eb25b1785ee4 244 case XBPS_ExtendedLengthMsb:
alexandery 0:eb25b1785ee4 245 parser->checksum += byte;
alexandery 0:eb25b1785ee4 246 parser->currentMessage.length = ((uint16_t)byte) << 8;
alexandery 0:eb25b1785ee4 247 parser->state = XBPS_ExtendedLengthLsb;
alexandery 0:eb25b1785ee4 248 break;
alexandery 0:eb25b1785ee4 249
alexandery 0:eb25b1785ee4 250 case XBPS_ExtendedLengthLsb:
alexandery 0:eb25b1785ee4 251 parser->checksum += byte;
alexandery 0:eb25b1785ee4 252 parser->currentMessage.length |= byte;
alexandery 0:eb25b1785ee4 253 prepareForPayload(parser);
alexandery 0:eb25b1785ee4 254 parser->state = XBPS_Payload;
alexandery 0:eb25b1785ee4 255 break;
alexandery 0:eb25b1785ee4 256
alexandery 0:eb25b1785ee4 257 case XBPS_Payload:
alexandery 0:eb25b1785ee4 258 parser->checksum += byte;
alexandery 0:eb25b1785ee4 259 if (parser->currentMessage.data)
alexandery 0:eb25b1785ee4 260 {
alexandery 0:eb25b1785ee4 261 ((uint8_t*)parser->currentMessage.data)[parser->payloadReceived] = byte;
alexandery 0:eb25b1785ee4 262 }
alexandery 0:eb25b1785ee4 263 if (++parser->payloadReceived == parser->currentMessage.length)
alexandery 0:eb25b1785ee4 264 {
alexandery 0:eb25b1785ee4 265 parser->state = XBPS_Checksum;
alexandery 0:eb25b1785ee4 266 }
alexandery 0:eb25b1785ee4 267 break;
alexandery 0:eb25b1785ee4 268
alexandery 0:eb25b1785ee4 269 case XBPS_Checksum:
alexandery 0:eb25b1785ee4 270 parser->checksum += byte;
alexandery 0:eb25b1785ee4 271 if ((parser->checksum == 0) &&
alexandery 0:eb25b1785ee4 272 ((parser->currentMessage.length == 0) ||
alexandery 0:eb25b1785ee4 273 parser->currentMessage.data))
alexandery 0:eb25b1785ee4 274 {
alexandery 0:eb25b1785ee4 275 parseMessagePayload(parser);
alexandery 0:eb25b1785ee4 276 parser->callbacks.handleMessage(&parser->currentMessage);
alexandery 0:eb25b1785ee4 277 }
alexandery 0:eb25b1785ee4 278 else if (parser->currentMessage.data)
alexandery 0:eb25b1785ee4 279 {
alexandery 0:eb25b1785ee4 280 parser->callbacks.deallocateBuffer(parser->currentMessage.data);
alexandery 0:eb25b1785ee4 281 }
alexandery 0:eb25b1785ee4 282 parser->state = XBPS_Preamble;
alexandery 0:eb25b1785ee4 283 break;
alexandery 0:eb25b1785ee4 284 }
alexandery 0:eb25b1785ee4 285 }
alexandery 0:eb25b1785ee4 286
alexandery 0:eb25b1785ee4 287 /*!
alexandery 0:eb25b1785ee4 288 * \brief Parse a buffer of data received from a motion tracker.
alexandery 0:eb25b1785ee4 289 */
alexandery 0:eb25b1785ee4 290 void XbusParser_parseBuffer(struct XbusParser* parser, uint8_t const* buf, size_t bufSize)
alexandery 0:eb25b1785ee4 291 {
alexandery 0:eb25b1785ee4 292 for (size_t i = 0; i < bufSize; ++i)
alexandery 0:eb25b1785ee4 293 {
alexandery 0:eb25b1785ee4 294 XbusParser_parseByte(parser, buf[i]);
alexandery 0:eb25b1785ee4 295 }
alexandery 0:eb25b1785ee4 296 }
alexandery 0:eb25b1785ee4 297