Library for XBus servo (under construction)
Dependents: mbed_XBus_Test mbed_XBus_MotionTest XBusServoTest ControlYokutan2017_2 ... more
It's pre-opened page. it's still a little bit unstable to use command packet but mostly work. Tested only on KL25Z
暫定版ページです。 まだコマンドパケット使用時に時々不安定になりますが、概ね動作しています。 KL25Z上でのみ、動作確認しています
XBusServo.cpp
- Committer:
- sawa
- Date:
- 2014-10-30
- Revision:
- 17:3ffb2e3e3bec
- Parent:
- 16:e283810b53c3
- Child:
- 18:75ddf12d93b6
File content as of revision 17:3ffb2e3e3bec:
/** * @file XBusServo.cpp * @brief this header file will contain all required * definitions and basic utilities functions. * for controling XBus servo. * @author Zak Sawa * @note Copyright (c) 2014-2014 JR PROPO * @note Released under the MIT License: http://mbed.org/license/mit */ #include "XBusServo.h" #include "pinmap.h" #include "gpio_api.h" #define kXBusBaudrate 250000 // bps #define kPacketHeaderFooterSize 3 #define kPacketPreDataSize 3 #define kStartOffsetOfCHData 4 #define kCHDataSize 4 #define kCHDataPacketCommand 0 #define kCHDataPacketLength 1 #define kCHDataPacketKey 2 #define kCHDataPacketType 3 #define kCmdDataPacketSize 8 #define kCmdDataPacketCommand 0 #define kCmdDataPacketLength 1 #define kCmdDataPacketKey 2 #define kCmdDataPacketCH_ID 3 #define kCmdDataPacketOrder 4 #define kCmdDataPacketData1 5 #define kCmdDataPacketData2 6 #define kCmdDataPacketCRC 7 // XBus Command typedef enum { kXBusCmd_Set = 0x20, kXBusCmd_Get = 0x21, kXBusCmd_Status = 0x22, kXBusCmd_ModeA = 0xa4 } XBusCmd; // XBus device mode typedef enum { kXBusMode_Operate = 0x01, kXBusMode_IDSet = 0x02 } XBusMode; #define DEBUG #ifdef DEBUG #define DBG(fmt) printf(fmt) #define DBGF(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DBG(...) #define DBGF(...) #endif /**************************************************************************** XBusServo::XBusServo @param tx pin name for tx @param rx pin name for rx @param maxServoNum max number of servo that you want to connect. (limit 50) this does just to reserve the buffer. you need to add XBus servo at the beginning of your sketch @bref Constructor @note 2014/09/02 : move from Arduino lib by Sawa *****************************************************************************/ XBusServo::XBusServo(PinName tx, PinName rx, PinName sw, uint8_t maxServoNum) : XBusPort(tx, rx), TxSwitch(sw, 0) { DBG("XBusServo::XBusServo\n"); // initialize serial txPin = tx; txOnly = false; if (rx == NC) txOnly = true; maxServo = maxServoNum; // initialise vars numOfServo = 0; if (maxServo > kXBusMaxServoNum) maxServo = kXBusMaxServoNum; else if (maxServo == 0) maxServo = 1; dirty = false; serialCommandBusy = false; recieveBufferPointer = 0; modifyServosNow = false; sendLength = 0; need2ReadData = false; chPacketBuffer = NULL; sendBuffer = NULL; } //**************************************************************************** // XBusServo::start // return : error code // parameter : none // // start to use XBus // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::start() { int bufferSize; // channel data packet buffer size DBG("XBusServo::start\n"); // allocate my buffer bufferSize = kStartOffsetOfCHData + maxServo * kCHDataSize + 1; // add 1 for CRC if (bufferSize < kCmdDataPacketSize) bufferSize = kCmdDataPacketSize; chPacketBuffer = new uint8_t[bufferSize]; if (chPacketBuffer == NULL) { stop(); return kXBusError_MemoryFull; } sendBuffer = new uint8_t[bufferSize]; if (sendBuffer == NULL) { stop(); return kXBusError_MemoryFull; } // initialize channel packer buffer chPacketBuffer[kCHDataPacketCommand] = kXBusCmd_ModeA; chPacketBuffer[kCHDataPacketLength] = 0x00; chPacketBuffer[kCHDataPacketKey] = 0x00; chPacketBuffer[kCHDataPacketType] = 0x00; // initialize serial XBusPort.baud(kXBusBaudrate); XBusPort.format(8, Serial::None, 1); #if DEVICE_SERIAL_FC XBusPort.set_flow_control(RawSerial::Disabled, NC, NC); #endif #ifdef TARGET_KL25Z // do nothing here #else XBusPort.attach(this, &XBusServo::TxIrqHandler, RawSerial::TxIrq); #endif serial_pinout_tx(txPin); return kXBusError_NoError; } //**************************************************************************** // XBusServo::stop // return : none // parameter : none // // stop to use XBus // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** void XBusServo::stop() { DBG("XBusServo::stop\n"); delete[] chPacketBuffer; chPacketBuffer = NULL; delete[] sendBuffer; sendBuffer = NULL; } //**************************************************************************** // XBusServo::write // return : none // parameter : buffer data buffer to send // length data length on the buffer // // start to send all packet data // 2014/09/30 : first write by Sawa //**************************************************************************** void XBusServo::write(uint8_t* buffer, uint8_t length) { // DBG("XBusServo::write\n"); if (serialCommandBusy) return; serialCommandBusy = true; XBusServo::sendLength = length; sendBufferPointer = buffer; if (XBusPort.putc(*sendBufferPointer++) < 0) { serialCommandBusy = false; XBusServo::sendLength = 0; } else { XBusServo::sendLength--; } #ifdef TARGET_KL25Z XBusPort.attach(this, &XBusServo::TxIrqHandler, RawSerial::TxIrq); #endif } //**************************************************************************** // XBusServo::flush // return : none // parameter : none // // wait to send all packet data. never call from interrupt. // 2014/09/30 : first write by Sawa //**************************************************************************** void XBusServo::flush(void) { // DBG("XBusServo::flush\n"); while(serialCommandBusy) ; } //**************************************************************************** // XBusServo::TxSwitchHandler // return : none // parameter : none // // handler for Tx switch // 2014/10/29 : first write by Sawa //**************************************************************************** void XBusServo::TxSwitchHandler(void) { TxSwitch.write(1); } //**************************************************************************** // XBusServo::TxIrqHandler // return : none // parameter : none // // handler for Tx buffer empty // 2014/09/30 : first write by Sawa // 2014/10/24 : add to setup reading for command data packet //**************************************************************************** void XBusServo::TxIrqHandler(void) //void XBusServo::TxIrqHandler(MODSERIAL_IRQ_INFO *q) { int result = 0; // DBG("XBusServo::TxIrqHandler\n"); if (! serialCommandBusy) return; // Is it noise ? if (XBusServo::sendLength > 0) { result = XBusPort.putc(*sendBufferPointer++); if (result < 0) { // error occured XBusServo::sendLength = 0; serialCommandBusy = false; } else { XBusServo::sendLength--; return; } } #ifdef TARGET_KL25Z XBusPort.attach(NULL, RawSerial::TxIrq); #endif if (result < 0) return; if (need2ReadData) { #ifdef TARGET_KL25Z TxSwitchTimer.attach_us(this, &XBusServo::TxSwitchHandler, 27); #endif } serialCommandBusy = false; } //**************************************************************************** // XBusServo::RxIrqHandler // return : none // parameter : none // // handler for Rx buffer full // 2014/09/30 : first write by Sawa // 2014/10/21 : add to ignore the data myself // 2014/10/24 : modify to get the data from 1st byte of buffer //**************************************************************************** void XBusServo::RxIrqHandler(void) //void XBusServo::RxIrqHandler(MODSERIAL_IRQ_INFO *q) { // DBG("XBusServo::RxIrqHandler\n"); while (XBusPort.readable()) { recieveBuffer[recieveBufferPointer++] = XBusPort.getc(); if (recieveBufferPointer >= kRecieveBufferSize) recieveBufferPointer = 0; } } //**************************************************************************** // XBusServo::sendChannelDataPacket // return : none // parameter : none // // This should be called on the timer handler like MsTimer2 when you // use the XBus servo. // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** void XBusServo::sendChannelDataPacket(void) { // DBG("XBusServo::sendChannelDataPacket\n"); if (modifyServosNow) return; if (numOfServo > 0) { if (dirty) { memcpy(sendBuffer, chPacketBuffer, chPacketBuffer[kCHDataPacketLength] + kPacketHeaderFooterSize); dirty = false; } need2ReadData = false; write(sendBuffer, sendBuffer[kCHDataPacketLength] + kPacketHeaderFooterSize); } } //**************************************************************************** // XBusServo::sendCommandDataPacket // return : error code // parameter : command The commnad that you want to send // channelID The channel ID of the XBus servo that you want to set up // order The order that you want to set up // value The value that you want to set / get // valueSize The value size. 1 byte(char) or 2 byte(int) // // This should NOT be called on interrupt handler when you // setup the XBus servo. // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::sendCommandDataPacket(uint8_t command, uint8_t channelID, uint8_t order, int16_t* value, uint8_t valueSize) { int sendSize; int prevData = -1; uint8_t dummy; DBG("XBusServo::sendCommandDataPacket\n"); // setup command sendBuffer[kCmdDataPacketCommand] = command; sendBuffer[kCmdDataPacketLength] = valueSize + kPacketPreDataSize; sendBuffer[kCmdDataPacketKey] = 0x00; sendBuffer[kCmdDataPacketCH_ID] = channelID; sendBuffer[kCmdDataPacketOrder] = order; if (valueSize == 1) { // 1 byte value sendBuffer[kCmdDataPacketData1] = *value & 0x00FF; sendBuffer[kCmdDataPacketData2] = crc8(sendBuffer, sendBuffer[kCHDataPacketLength] + 2); } else { sendBuffer[kCmdDataPacketData1] = (*value >> 8) & 0x00FF; sendBuffer[kCmdDataPacketData2] = *value & 0x00FF; sendBuffer[kCmdDataPacketCRC] = crc8(sendBuffer, sendBuffer[kCHDataPacketLength] + 2); } // to recover channel data packet when you call sendChannelDataPacket after this dirty = true; // prepair recieve data XBusPort.attach(this, &XBusServo::RxIrqHandler, RawSerial::RxIrq); while (XBusPort.readable()) dummy = XBusPort.getc(); recieveBufferPointer = 0; // reset recieve buffer // send command sendSize = sendBuffer[kCmdDataPacketLength] + kPacketHeaderFooterSize; need2ReadData = ((! txOnly) && (channelID != 0)); recieveBuffer[sendSize + kCmdDataPacketLength] = 5; // dummy to get real packet size write(sendBuffer, sendSize); // DBG("XBusServo::sendCommandDataPacket 2\n"); // if it's tx only mode or ID=0, it done if (! need2ReadData) { flush(); return kXBusError_NoError; } // wait to read all packet while(recieveBufferPointer < (recieveBuffer[sendSize + kCmdDataPacketLength] + kPacketHeaderFooterSize + sendSize)) { if (prevData != recieveBufferPointer) { prevData = recieveBufferPointer; // DBGF("XBusServo::sendCommandDataPacket 3 %02d %02X \n", recieveBufferPointer, recieveBuffer[recieveBufferPointer - 1]); } // need to add time out } TxSwitch.write(0); // DBG("XBusServo::sendCommandDataPacket 4\n"); // change bus direction to Tx mode XBusPort.attach(NULL, RawSerial::RxIrq); need2ReadData = false; serialCommandBusy = false; // check CRC if (crc8(recieveBuffer, recieveBuffer[kCHDataPacketLength] + 3) != 0) { DBG("XBusServo::sendCommandDataPacket kXBusError_CRCError\n"); return kXBusError_CRCError; } // check unsupported if (recieveBuffer[kCmdDataPacketOrder] == kXBusOrder_1_Unsupported) { DBG("XBusServo::sendCommandDataPacket kXBusError_Unsupported\n"); return kXBusError_Unsupported; } // send back the value if (valueSize == 1) { // 1 byte value *value = recieveBuffer[sendSize + kCmdDataPacketData1]; if (*value & 0x0080) *value |= 0xFF00; } else { *value = recieveBuffer[sendSize + kCmdDataPacketData1]; *value <<= 8; *value |= recieveBuffer[sendSize + kCmdDataPacketData2]; } return kXBusError_NoError; } //**************************************************************************** // XBusServo::addServo // return : error code // parameter : channelID channel ID of the XBus servo that you want to use // initValue initial value of this XBus servo // use kXbusServoNeutral for center of the XBus servo // // add new servo to the buffer on this library // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::addServo(uint8_t channelID, uint16_t initValue) { uint16_t dataOffset; DBG("XBusServo::addServo\n"); // max check if (numOfServo >= maxServo) return kXBusError_ServoNumOverflow; // convert to servo ID channelID &= 0x3F; // scan servo ID that is already added if (numOfServo > 0) { uint16_t servoNo; for (servoNo = 0; servoNo < numOfServo; servoNo++) if (chPacketBuffer[kStartOffsetOfCHData + kCHDataSize * servoNo] == channelID) return kXBusError_AddWithSameID; // found same servo ID } // atomic flag on modifyServosNow = true; // add new servo dataOffset = kStartOffsetOfCHData + kCHDataSize * numOfServo; numOfServo++; chPacketBuffer[kCHDataPacketLength] = numOfServo * kCHDataSize + 2; // add 2 for key and type chPacketBuffer[dataOffset] = channelID; chPacketBuffer[dataOffset + 1] = 0x00; chPacketBuffer[dataOffset + 2] = (initValue >> 8) & 0x00FF; chPacketBuffer[dataOffset + 3] = initValue & 0x00FF; // calc CRC chPacketBuffer[dataOffset + 4] = crc8(chPacketBuffer, chPacketBuffer[kCHDataPacketLength] + 2); dirty = true; // atomic flag off modifyServosNow = false; return kXBusError_NoError; } //**************************************************************************** // XBusServo::removeServo // return : error code // parameter : channelID channel ID of the XBus servo that you want to remove // // remove the servo from the buffer on this library // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::removeServo(uint8_t channelID) { DBG("XBusServo::removeServo\n"); // min check if (numOfServo == 0) return kXBusError_ServoNumIsZero; // convert to servo ID channelID &= 0x3F; // scan servo ID that is already added if (numOfServo > 0) { uint16_t servoNo; for (servoNo = 0; servoNo < numOfServo; servoNo++) if (chPacketBuffer[kStartOffsetOfCHData + kCHDataSize * servoNo] == channelID) { // atomic flag on modifyServosNow = true; // copy data after that if (servoNo < (numOfServo - 1)) memcpy(&(chPacketBuffer[kStartOffsetOfCHData + kCHDataSize * servoNo]), &(chPacketBuffer[kStartOffsetOfCHData + kCHDataSize * (servoNo + 1)]), kCHDataSize * (numOfServo - servoNo - 1)); // update packet size numOfServo--; chPacketBuffer[kCHDataPacketLength] = numOfServo * kCHDataSize + 2; // add 2 for key and type // calc CRC chPacketBuffer[kStartOffsetOfCHData + kCHDataSize * numOfServo] = crc8(chPacketBuffer, chPacketBuffer[kCHDataPacketLength] + 2); dirty = true; // atomic flag off modifyServosNow = false; return kXBusError_NoError; } } return kXBusError_IDNotFound; } //**************************************************************************** // XBusServo::setServo // return : error code // parameter : channelID channel ID of the XBus servo that you want to set // value value of this XBus servo // use kXbusServoNeutral for center of the XBus servo // // set new angle to the servo // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::setServo(uint8_t channelID, uint16_t value) { uint16_t dataOffset; // DBG("XBusServo::setServo\n"); // convert to servo ID channelID &= 0x3F; // scan servo ID that is already added if (numOfServo > 0) { int servoNo; for (servoNo = 0; servoNo < numOfServo; servoNo++) { dataOffset = kStartOffsetOfCHData + kCHDataSize * servoNo; if (chPacketBuffer[dataOffset] == channelID) { // atomic flag on modifyServosNow = true; // set value chPacketBuffer[dataOffset + 2] = (value >> 8) & 0x00FF; chPacketBuffer[dataOffset + 3] = value & 0x00FF; // calc CRC chPacketBuffer[kStartOffsetOfCHData + kCHDataSize * numOfServo] = crc8(chPacketBuffer, chPacketBuffer[kCHDataPacketLength] + 2); dirty = 1; // atomic flag off modifyServosNow = false; return kXBusError_NoError; } } } return kXBusError_IDNotFound; } //**************************************************************************** // XBusServo::setChannelID // return : error code // parameter : oldChannelID channel IDof the XBus servo to change the ID // newChannelID new channel ID for the XBus servo // // set new channel ID to the XBus servo // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::setChannelID(uint8_t oldChannelID, uint8_t newChannelID) { XBusError result; int16_t value; DBG("XBusServo::setChannelID\n"); value = kXBusMode_IDSet; result = sendCommandDataPacket(kXBusCmd_Set, oldChannelID, kXBusOrder_1_Mode, &value, 1); if (result != kXBusError_NoError) return result; value = newChannelID; result = sendCommandDataPacket(kXBusCmd_Set, oldChannelID, kXBusOrder_1_ID, &value, 1); return result; } //**************************************************************************** // XBusServo::setChannelID // return : error code // parameter : newChannelID new channel ID for the XBus servo // // set new channel ID to the XBus servo // this is only for TX only mode // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::setChannelID(uint8_t newChannelID) { XBusError result; int16_t value; DBG("XBusServo::setChannelID\n"); if (txOnly) return kXBusError_OnlyForTxOnlyMode; value = kXBusMode_IDSet; result = sendCommandDataPacket(kXBusCmd_Set, 0, kXBusOrder_1_Mode, &value, 1); if (result != kXBusError_NoError) return result; value = newChannelID; result = sendCommandDataPacket(kXBusCmd_Set, 0, kXBusOrder_1_ID, &value, 1); return result; } //**************************************************************************** // XBusServo::getDataSize // return : data size for this order // parameter : order the order that you want to know // // get the data size of this order // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** uint8_t XBusServo::getDataSize(uint8_t order) { uint8_t dataSize = 1; DBG("XBusServo::getDataSize\n"); switch(order) { case kXBusOrder_2_Version: // only for get case kXBusOrder_2_Product: // only for get case kXBusOrder_2_Reset: // only for set case kXBusOrder_2_ParamWrite: // only for set case kXBusOrder_2_Reverse: case kXBusOrder_2_Neutral: case kXBusOrder_2_H_Travel: case kXBusOrder_2_L_Travel: case kXBusOrder_2_H_Limit: case kXBusOrder_2_L_Limit: case kXBusOrder_2_PowerOffset: case kXBusOrder_2_AlarmDelay: case kXBusOrder_2_CurrentPos: // only for get case kXBusOrder_2_MaxInteger: dataSize = 2; } return dataSize; } //**************************************************************************** // XBusServo::setCommand // return : error code // parameter : channelID channel ID of the XBus servo that you want to set to // order the order that you want // value the value that you want to set and return current value // // send set command to the XBus servo // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::setCommand(uint8_t channelID, uint8_t order, int16_t* value) { DBG("XBusServo::setCommand\n"); return sendCommandDataPacket(kXBusCmd_Set, channelID, order, value, getDataSize(order)); } //**************************************************************************** // XBusServo::getCommand // return : error code // parameter : channelID channel ID of the XBus servo that you want to get from // order the order that you want // value the value that you want to get from // // send get command to the XBus servo // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::getCommand(uint8_t channelID, uint8_t order, int16_t* value) { DBG("XBusServo::getCommand\n"); return sendCommandDataPacket(kXBusCmd_Get, channelID, order, value, getDataSize(order)); } //**************************************************************************** // XBusServo::setCommand // return : error code // parameter : order the order that you want // value the value that you want to set current value // // send set command to the XBus servo // this is only for TX only mode // 2014/09/02 : move from Arduino lib by Sawa //**************************************************************************** XBusError XBusServo::setCommand(uint8_t order, int16_t* value) { DBG("XBusServo::setCommand\n"); if (txOnly) return kXBusError_OnlyForTxOnlyMode; return sendCommandDataPacket(kXBusCmd_Set, 0, order, value, getDataSize(order)); } //**************************************************************************** // for CRC static uint8_t s_crc_array[256] = { 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc, 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62, 0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff, 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07, 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, 0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9, 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50, 0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee, 0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, 0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, 0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, 0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35, }; uint8_t XBusServo::crc_table(uint8_t data, uint8_t crc) { uint16_t index = (data ^ crc) & 0xff; crc = s_crc_array[index]; return crc; } uint8_t XBusServo::crc8(uint8_t* buffer, uint8_t length) { uint8_t crc = 0; while (length-- > 0) crc = crc_table(*buffer++, crc); return crc; }