Seed can implementation
Fork of SEEED_CAN by
Diff: seeed_can_api.cpp
- Revision:
- 0:f5d099885d3d
- Child:
- 1:ad71faa09868
diff -r 000000000000 -r f5d099885d3d seeed_can_api.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seeed_can_api.cpp Tue Nov 05 22:37:35 2013 +0000 @@ -0,0 +1,400 @@ +/* seeed_can_api.cpp + * Copyright (c) 2013 Sophie Dexter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "seeed_can_api.h" + +/** Initialise the MCP2515 and set the bit rate + */ +uint8_t mcpInit(can_t *obj, const uint32_t bitRate) +{ + union { // Access CANMsg as: + CANMsg x; // the organised struct + uint8_t y[]; // or contiguous memory array + }; + uint8_t maskFilt[8] = { MCP_RXM0SIDH, MCP_RXM1SIDH, MCP_RXF0SIDH, MCP_RXF1SIDH, MCP_RXF2SIDH, MCP_RXF3SIDH, MCP_RXF4SIDH, MCP_RXF5SIDH }; + uint8_t canBufCtrl[5] = { MCP_TXB0CTRL, MCP_TXB1CTRL, MCP_TXB2CTRL, MCP_RXB0CTRL, MCP_RXB1CTRL }; + uint8_t canBuffer[3] = { MCP_TXB0CTRL+1, MCP_TXB1CTRL+1, MCP_TXB2CTRL+1 }; + +#ifdef DEBUG + printf("Reseting MCP2515\r\n"); +#endif + mcpReset(obj); + for (uint32_t i = 0; i < 8; i++) { // Clear all CAN id masks and filters + mcpWriteId(obj, maskFilt[i], NULL, NULL); + } + for (uint32_t i = 0; i < 5; i++) { // Clear all CAN buffer control registers + mcpWrite(obj, canBufCtrl[i], NULL); + } + for (uint32_t i = 0; i < sizeof(x); i++) y[i] = NULL; // Initialise empty CAN message buffer + for (uint32_t i = 0; i < 3; i++) { // Clear all CAN TX buffers + mcpWriteMultiple(obj, canBuffer[i], y, sizeof(x) ); // using empty CAN message (as an array) + } + mcpWrite(obj, MCP_CANINTE, MCP_RX0IF | MCP_RX1IF); // RX buffers can generate a interrupt +#if (DEBUG_RXANY==1) + // enable both receive-buffers to receive any message and enable rollover + mcpBitModify(obj, MCP_RXB0CTRL, MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, MCP_RXB_RX_ANY | MCP_RXB_BUKT_MASK); + mcpBitModify(obj, MCP_RXB1CTRL, MCP_RXB_RX_MASK, MCP_RXB_RX_ANY); +#else + // enable both receive-buffers to receive messages with std. and ext. identifiers and enable rollover + mcpBitModify(obj, MCP_RXB0CTRL, MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, MCP_RXB_RX_STDEXT | MCP_RXB_BUKT_MASK ); + mcpBitModify(obj, MCP_RXB1CTRL, MCP_RXB_RX_MASK, MCP_RXB_RX_STDEXT); +#endif +#ifdef DEBUG + printf("Setting bit rate\r\n"); +#endif + return (mcpSetBitRate(obj, bitRate)) ? 1 : 0; // set baudrate and return +} + +/** set MCP2515 operation mode + * + * Configuration, Normal, Sleep, Listen-only or Loopback + */ +uint8_t mcpSetMode(can_t *obj, const uint8_t newmode) +{ + mcpBitModify(obj, MCP_CANCTRL, MODE_MASK, newmode); + for (uint32_t i = 0; i<10; i++) { + if ((mcpRead(obj, MCP_CANSTAT) & MODE_MASK) == newmode) { +#ifdef DEBUG + printf("Successfully entered mode: %02x time: %dms\r\n", newmode, i); + printf("CANCTRL:%02x CANSTAT:%02x TXB0:%02x TXB1:%02x TXB2:%02x\r\n", mcpRead(obj, MCP_CANCTRL), mcpRead(obj, MCP_CANSTAT), mcpRead(obj, MCP_TXB0CTRL), mcpRead(obj, MCP_TXB1CTRL), mcpRead(obj, MCP_TXB2CTRL)); +#endif + return 1; + } + wait_ms(1); + } +#ifdef DEBUG + printf("Failed to enter mode: %02x\r\n", newmode); + printf("CANCTRL:%02x CANSTAT:%02x TXB0:%02x TXB1:%02x TXB2:%02x\r\n", mcpRead(obj, MCP_CANCTRL), mcpRead(obj, MCP_CANSTAT), mcpRead(obj, MCP_TXB0CTRL), mcpRead(obj, MCP_TXB1CTRL), mcpRead(obj, MCP_TXB2CTRL)); +#endif + return 0; +} + +/** set the CAN bus bitrate + * + * Calculate suitable BTR register values. + * The Bit Rate Pre-scaler (BRP) can be in the range of 1-64. + * According to CANopen, Bit Time can be be between 25 and 8 Time Quanta (TQU). + * Bit Time = SyncSeg(1 TQU) + PropSeg(1-8 TQU) + PhaseSeg1(1-8 TQU) + PhaseSeg2(2-8 TQU). + * SyncSeg is always 1TQU, PhaseSeg2 must be at least 2TQU to be longer than the processing time. + * Opinions vary on when to take a sample but a point roughly 2/3 of Bit Time seems OK. + * Synchronisation Jump width can be 1-4 TQU, a value of 1 seems to be normal. + * + * All register values are -1, e.g. PropSeg can range from 1-8 TQU, so values are 0-7 (0-63 for BRP). + * + * This table has the sampling points as close to 2/3 (66.7%) as possible. + * The first value is PropSeg, 2nd PhaseSeg1. + * PhaseSeg2 will be the same as PhaseSeg1 when btlmode bit is initialised to 0. + */ +static const uint8_t timing_pts[18][2] = { + {0x0, 0x2}, // 8, 62.5% + {0x1, 0x2}, // 9, 66.7% + {0x2, 0x2}, // 10, 70.0% + {0x1, 0x3}, // 11, 63.6% + {0x2, 0x3}, // 12, 66.7% + {0x3, 0x3}, // 13, 69.2% + {0x2, 0x4}, // 14, 64.3% + {0x3, 0x4}, // 15, 66.7% + {0x4, 0x4}, // 16, 68.75% + {0x3, 0x5}, // 17, 64.7% + {0x4, 0x5}, // 18, 66.7% + {0x5, 0x5}, // 19, 63.2% + {0x4, 0x6}, // 20, 65.0% + {0x5, 0x6}, // 21, 66.7% + {0x6, 0x6}, // 22, 68.2% + {0x5, 0x7}, // 23, 65.2 + {0x6, 0x7}, // 24, 66.7% + {0x7, 0x7}, // 25, 68.0% +}; + +uint8_t mcpSetBitRate(can_t *obj, const uint32_t bitRate) +{ + union { // Access CANtiming as: + CANtiming x; // the organised struct + uint8_t y[]; // or contiguous memory array + }; + uint32_t bestBRP = 0; + uint32_t bestTQU = 0; + uint32_t bestCanRate = 0; + uint32_t minBRP = (MCP_CLOCK_FREQ / (2 * MCP_MAX_TIME_QUANTA * bitRate)); + uint32_t maxBRP = (MCP_CLOCK_FREQ / (2 * MCP_MIN_TIME_QUANTA * bitRate)); + + #ifdef DEBUG + printf("Setting configuration mode\r\n"); + #endif + if(!mcpSetMode(obj, MODE_CONFIG)) { // Go into configuration mode + return 0; + } + + for (uint32_t i = 0; i < sizeof(x); i++) y[i] = NULL; // Initialise CANtiming (btlmode, sjw and sam all = 0) + if ((bitRate < CAN_MIN_RATE) || (bitRate > CAN_MAX_RATE)) { +#ifdef DEBUG + printf("FAILED!! The requested Bit Rate is too high or too low: %d\r\n", bitRate); +#endif + return 0; // Cannot set the requested bit rate! + } + minBRP = (minBRP == 0) ? MCP_MIN_PRESCALER : minBRP; + maxBRP = (maxBRP > MCP_MAX_PRESCALER) ? MCP_MAX_PRESCALER : maxBRP; + for (uint32_t BRP = minBRP; BRP < (maxBRP + 1); BRP++) { + uint32_t timeQuanta = (MCP_CLOCK_FREQ / (2 * BRP * bitRate)); + if ((timeQuanta >= MCP_MIN_TIME_QUANTA) && (timeQuanta <= MCP_MAX_TIME_QUANTA)) { + for (uint32_t TQU = timeQuanta; TQU <= MCP_MAX_TIME_QUANTA; TQU++) { + uint32_t thisCanRate = MCP_CLOCK_FREQ / (2 * BRP * TQU); + if ( abs((int)bitRate - (int)thisCanRate) < abs((int)bitRate - (int)bestCanRate)) { + bestCanRate = thisCanRate; + bestBRP= BRP; + bestTQU= TQU; + } + } + } + } + x.brp = (bestBRP - 1); + x.prseg = (timing_pts[bestTQU - 8][0]); + x.phseg1 = (timing_pts[bestTQU - 8][1]); + mcpWriteMultiple(obj, MCP_CNF3, y, sizeof(x) ); // Copy CANtiming to the MCP2515 (as an array) +#ifdef DEBUG + printf("minBRP %d maxBRP %d\r\n", minBRP, maxBRP); + printf("Bitrate: %d\tactualBitRate: %d\t Error: %1.2f percent.\r\n", bitRate, bestCanRate, (100-(100*(float)bitRate/(float)bestCanRate))); + printf("TimeQuanta: %d\tbitRatePrescaler: %d\tSamplePoint: %2.2f percent\r\n", bestTQU, bestBRP, 100*(float)(3 + x.prseg + x.phseg1)/(float)bestTQU ) ; + printf("Syncseg: 1\tPropSeg: %d\tPhaseSeg1: %d\tPhaseSeg2: %d\r\n", (x.prseg+1), (x.phseg1+1), (x.phseg1+1)); + printf("Setting normal mode\r\n"); +#endif + return (mcpSetMode(obj, MODE_NORMAL)) ? 1 : 0; // desired bit rate set enter normal mode and return +} + +/** write a CAN id to a mask, filter or transmit buffer + */ +void mcpWriteId(can_t *obj, const uint8_t mcp_addr, const uint8_t ext, const uint32_t id ) +{ + union { // Access CANid as: + CANid x; // the organised struct + uint8_t y[]; // or contiguous memory array + }; + + for (uint32_t i = 0; i < sizeof(x); i++) y[i] = NULL; // Initialise CANid structure + x.ide = ext; // Extended Identifier Flag + if (x.ide == CANExtended) { + x.sid10_3 = (uint8_t) (id >> 21); // SID10..3 + x.sid2_0 = (uint8_t) (id >> 18) & 0x07; // SID2..0 + x.eid17_16 = (uint8_t) (id >> 16) & 0x03; // EID17..16 + x.eid15_8 = (uint8_t) (id >> 8); // EID15..8 + x.eid7_0 = (uint8_t) id; // EID7..0 + } else { + x.sid10_3 = (uint8_t) (id >> 3); // SID10..3 + x.sid2_0 = (uint8_t) (id & 0x07); // SID2..0 + } +#ifdef DEBUG + printf("sizeof CanIdStruct: %d bytes\r\n", sizeof(x)); + printf("sid10_3: %x\r\n", x.sid10_3); + printf("eid17_16: %x\r\n", x.eid17_16); + printf("ide: %x\r\n", x.ide); + printf("srtr: %x\r\n", x.srtr); + printf("sid2_0: %x\r\n", x.sid2_0); + printf("eid15_8: %x\r\n", x.eid15_8); + printf("eid7_0: %x\r\n", x.eid7_0); +#endif + mcpWriteMultiple(obj, mcp_addr, y, sizeof(x) ); // Copy CANid to the MCP2515 (as an array) +} + +/** write a CAN message to the MCP2515 + */ +uint8_t mcpCanWrite(can_t *obj, CAN_Message msg) +{ + union { // Access CANMsg as: + CANMsg x; // the organised struct + uint8_t y[]; // or contiguous memory array + }; + uint8_t bufferCommand[] = {MCP_WRITE_TX0, MCP_WRITE_TX1, MCP_WRITE_TX2}; + uint8_t rtsCommand[] = {MCP_RTS_TX0, MCP_RTS_TX1, MCP_RTS_TX2}; + uint8_t status = mcpStatus(obj); + uint32_t num = 0; +// Check if there is a free message buffer + if (!(status & MCP_STAT_TX0REQ)) { // TX Message Buffer 0 free? + num = 0; + } else if (!(status & MCP_STAT_TX1REQ)) { // TX Message Buffer 1 free? + num = 1; + } else if (!(status & MCP_STAT_TX2REQ)) { // TX Message Buffer 2 free? + num = 2; + } else { + return 0; // No free transmit buffers in the MCP2515 CAN controller chip + } +// populate CANMsg structure + for (uint32_t i = 0; i < sizeof(x); i++) y[i] = NULL; // Initialise CANMsg structure + x.id.ide = msg.format; // Extended Identifier Flag + if (x.id.ide == CANExtended) { + x.id.sid10_3 = (uint8_t) (msg.id >> 21); // SID10..3 + x.id.sid2_0 = (uint8_t) (msg.id >> 18) & 0x07; // SID2..0 + x.id.eid17_16 = (uint8_t) (msg.id >> 16) & 0x03; // EID17..16 + x.id.eid15_8 = (uint8_t) (msg.id >> 8); // EID15..8 + x.id.eid7_0 = (uint8_t) msg.id; // EID7..0 + } else { + x.id.sid10_3 = (uint8_t) (msg.id >> 3); // SID10..3 + x.id.sid2_0 = (uint8_t) (msg.id & 0x07); // SID2..0 + } + x.dlc = msg.len & 0x0f; // Number of bytes in can message + x.ertr = msg.type; // Data or remote message + memcpy(x.data,msg.data,x.dlc); // Get the Data bytes +// write CANmsg to the specified TX buffer 'num' + mcpWriteBuffer(obj, bufferCommand[num], y, sizeof(x)); // Write the message ,CANMsg, to the MCP2515's Tx buffer 'num' (as an array) + mcpBufferRTS(obj, rtsCommand[num]); + return 1; // Indicate that message has been transmitted +} + +/** read a CAN message from the MCP2515 + */ +uint8_t mcpCanRead(can_t *obj, CAN_Message *msg) +{ + union { // Access CANMsg as: + CANMsg x; // the organised struct + uint8_t y[]; // or contiguous memory array + }; + uint8_t bufferCommand[] = {MCP_READ_RX0, MCP_READ_RX1}; + uint8_t status = mcpReceiveStatus(obj); + bool num = 0; +// Check if there is a message the buffers + if (status & MCP_RXSTAT_RXB0) { // Msg in Buffer 0? + num = 0; + } else if (status & MCP_RXSTAT_RXB1) { // Msg in Buffer 1? + num = 1; + } else { + return 0; // No messages waiting + } + mcpReadBuffer(obj, bufferCommand[0], y, sizeof(x)); // Read the message into CANMsg (as an array) + mcpBitModify(obj, MCP_CANINTF, (!num ? MCP_RX0IF : MCP_RX1IF), 0); // Free the message buffer +#ifdef DEBUG + printf("sizeof CanMsgStruct: %d bytes\r\n", sizeof(x)); + printf("sizeof CanMsgArray: %d bytes\r\n", sizeof(y)); + printf("sid10_3: %x\r\n", x.id.sid10_3); + printf("eid17_16: %x\r\n", x.id.eid17_16); + printf("ide: %x\r\n", x.id.ide); + printf("srtr: %x\r\n", x.id.srtr); + printf("sid2_0: %x\r\n", x.id.sid2_0); + printf("eid15_8: %x\r\n", x.id.eid15_8); + printf("eid7_0: %x\r\n", x.id.eid7_0); + printf("dlc: %x\r\n", x.dlc); + printf("ertr: %x\r\n", x.ertr); + printf("data: "); + for (char i=0; i<8; i++) + printf("%02x,", x.data[i]); + printf("\r\n"); +#endif + msg->format = (status & MCP_RXSTAT_IDE) ? CANExtended : CANStandard;// Extended CAN id Flag + if (msg->format == CANExtended) { // Assemble the Extended CAN id + msg->id = (x.id.sid10_3 << 21) | + (x.id.sid2_0 << 18) | + (x.id.eid17_16 << 16) | + (x.id.eid15_8 << 8) | + (x.id.eid7_0); + } else { // Assemble the Standard CAN id + msg->id = (x.id.sid10_3 << 3) | + (x.id.sid2_0); + } + msg->len = x.dlc; // Number of bytes in CAN message + msg->type = (status & MCP_RXSTAT_RTR) ? CANRemote : CANData; // Determine if a Remote or Data message type + memcpy(msg->data,x.data,x.dlc); // Get the Data bytes + return 1; // Indicate that message has been retrieved +} + +/** initialise an Acceptance Mask + */ +uint8_t mcpInitMask(can_t *obj, uint8_t num, uint32_t ulData, bool ext) +{ + uint8_t mask[2] = { MCP_RXM0SIDH, MCP_RXM1SIDH }; + + if (num > 1) { +#ifdef DEBUG + printf("Trying to set an invalid Mask number: %d\r\n", num); +#endif + return 0; + } +#ifdef DEBUG + printf("Begin to set Mask!!\r\n"); +#endif + if(!mcpSetMode(obj, MODE_CONFIG)) { + return 0; + } + mcpWriteId(obj, mask[num], ext, ulData); + if(!mcpSetMode(obj, MODE_NORMAL)) { + return 0; + } +#ifdef DEBUG + printf("Successfully set Mask number: %d\r\n", num); +#endif + return 1; +} + +/** initialise an Acceptance Filter + */ +uint8_t mcpInitFilter(can_t *obj, uint8_t num, uint32_t ulData, bool ext) +{ + uint8_t filter[6] = { MCP_RXF0SIDH, MCP_RXF1SIDH, MCP_RXF2SIDH, MCP_RXF3SIDH, MCP_RXF4SIDH, MCP_RXF5SIDH }; + + if (num > 5) { +#ifdef DEBUG + printf("Trying to set an invalid Filter number: %d\r\n", num); +#endif + return 0; + } +#ifdef DEBUG + printf("Begin to set Filter!!\r\n"); +#endif + if(!mcpSetMode(obj, MODE_CONFIG)) { + return 0; + } + mcpWriteId(obj, filter[num], ext, ulData); + if(!mcpSetMode(obj, MODE_NORMAL)) { + return 0; + } +#ifdef DEBUG + printf("Successfully set Filter: %d\r\n", num); +#endif + return 1; +} + +/* Number of message reception errors + */ +uint8_t mcpReceptionErrorCount(can_t *obj) +{ + return (mcpRead(obj, MCP_REC)); +} + +/* Number of message transmission errors + */ +uint8_t mcpTransmissionErrorCount(can_t *obj) +{ + return (mcpRead(obj, MCP_TEC)); +} + +/* Select between monitor (silent = 1) and normal (silent = 0) modes + */ +void mcpMonitor(can_t *obj, const bool silent) +{ + silent ? mcpSetMode(obj, MODE_LISTENONLY) : mcpSetMode(obj, MODE_NORMAL); +} + +/* Change CAN operation to the specified mode + */ +uint8_t mcpMode(can_t *obj, const CANMode mode) +{ + uint8_t which[] = { MODE_NORMAL, MODE_SLEEP, MODE_LOOPBACK, MODE_LISTENONLY, MODE_CONFIG, MODE_CONFIG}; + + if (mode == _RESET) { + mcpReset(obj); + } + if (mcpSetMode(obj, which[mode])) { + return 1; + } + return 0; +}