aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fw/can.c148
-rw-r--r--fw/can.h81
-rw-r--r--fw/types.h3
3 files changed, 193 insertions, 39 deletions
diff --git a/fw/can.c b/fw/can.c
index b311383..aef2706 100644
--- a/fw/can.c
+++ b/fw/can.c
@@ -1,5 +1,6 @@
#include <xc.h>
+#include <stdbool.h>
#include <stdint.h>
#include "system.h"
@@ -12,21 +13,21 @@
#define STARTUP_TIME 128u
// Register addresses
-enum {
- // Config
- REG_BFPCTRL = 0x0C, // pin control and status
+typedef enum {
+ // Config and status
REG_CANCTRL = 0x0F, // CAN control
REG_CANSTAT = 0x0E, // CAN status
+ REG_BFPCTRL = 0x0C, // pin control and status
+ REG_RXB0CTRL = 0x60, // receive buffer 0 control
+ REG_RXB1CTRL = 0x70, // receive buffer 1 control
+ REG_CANINTE = 0x2B, // CAN interrupt enable
+ REG_CANINTF = 0x2C, // CAN interrupt flags
// Bit timing
REG_CNF1 = 0x2A,
REG_CNF2 = 0x29,
REG_CNF3 = 0x28,
- // Interrupts
- REG_CANINTE = 0x2B, // CAN interrupt enable
- REG_CANINTF = 0x2C, // CAN interrupt flag
-
// Filter 0
REG_RXF0SIDH = 0x00, // standard ID high
REG_RXF0SIDL = 0x01, // standard ID low
@@ -76,7 +77,6 @@ enum {
REG_RXM1EID0 = 0x27, // extended ID low
// Receive buffer 0
- REG_RXB0CTRL = 0x60, // control
REG_RXB0SIDH = 0x61, // standard ID high
REG_RXB0SIDL = 0x62, // standard ID low
REG_RXB0EID8 = 0x63, // extended ID high
@@ -85,23 +85,13 @@ enum {
REG_RXB0DM = 0x66, // data byte <M> [66h+0, 66h+7]
// Receive buffer 1
- REG_RXB1CTRL = 0x70, // control
REG_RXB1SIDH = 0x71, // standard ID high
REG_RXB1SIDL = 0x72, // standard ID low
REG_RXB1EID8 = 0x73, // extended ID high
REG_RXB1EID0 = 0x74, // extended ID low
REG_RXB1DLC = 0x75, // data length code
REG_RXB1DM = 0x76, // data byte <M> [76h+0, 76h+7]
-};
-
-// Modes of operation
-typedef enum {
- MODE_NORMAL = 0x0,
- MODE_SLEEP = 0x1,
- MODE_LOOPBACK = 0x2,
- MODE_LISTEN_ONLY = 0x3,
- MODE_CONFIG = 0x4,
-} Mode;
+} Reg;
// Instructions
enum {
@@ -130,17 +120,17 @@ reset(void) {
CAN_CS = 1;
}
-// Read n registers into data, starting at the specified address.
-static void
-read(U8 addr, U8 *data, U8 n) {
+// Read a register.
+static U8
+read(Reg addr) {
+ U8 data;
+
CAN_CS = 0;
(void)spiTx(CMD_READ);
- (void)spiTx(addr);
- while (n--) {
- *data = spiTx(0x00);
- data++;
- }
+ (void)spiTx((U8)addr);
+ data = spiTx(0x00);
CAN_CS = 1;
+ return data;
}
// Read the DATA field of one of the RX buffers.
@@ -158,10 +148,10 @@ readRxData(RxBuf bufNum, U8 data[8u]) {
// Write to a register.
static void
-write(U8 addr, U8 data) {
+write(Reg addr, U8 data) {
CAN_CS = 0;
(void)spiTx(CMD_WRITE);
- (void)spiTx(addr);
+ (void)spiTx((U8)addr);
(void)spiTx(data);
CAN_CS = 1;
}
@@ -180,10 +170,10 @@ rxStatus(void) {
// Write individual bits of a register with the BIT MODIFY instruction.
static void
-bitModify(U8 addr, U8 mask, U8 data) {
+bitModify(Reg addr, U8 mask, U8 data) {
CAN_CS = 0;
(void)spiTx(CMD_BIT_MODIFY);
- (void)spiTx(addr);
+ (void)spiTx((U8)addr);
(void)spiTx(mask);
(void)spiTx(data);
CAN_CS = 1;
@@ -191,12 +181,106 @@ bitModify(U8 addr, U8 mask, U8 data) {
void
canInit(void) {
+ // Configure chip-select pin
CAN_CS_TRIS = OUT;
CAN_CS = 1;
+ // Send RESET command
_delay(STARTUP_TIME); // wait for MCP2515 to power on
reset(); // send RESET command
_delay(STARTUP_TIME); // wait to power on again
- // TODO
+ // Configure registers
+ canSetMode(CAN_MODE_CONFIG);
+ bitModify(REG_CANCTRL, 0x1F, 0x00); // disable one-shot mode and CLKOUT pin
+ write(REG_BFPCTRL, 0x00); // disable RXnBF interrupt pins
+ write(REG_RXB0CTRL, 0x00); // use filters, disable rollover
+ write(REG_RXB1CTRL, 0x00); // use filters
+}
+
+void
+canSetMode(CanMode mode) {
+ CanMode currentMode;
+
+ do {
+ // Set REQOP of CANCTRL to request mode
+ bitModify(REG_CANCTRL, 0xE0, (U8) (mode << 5u));
+ // Read OPMOD of CANSTAT
+ currentMode = read(REG_CANSTAT) >> 5u;
+ } while (currentMode != mode);
+}
+
+void
+canSetBitTiming(U8 cnf1, U8 cnf2, U8 cnf3) {
+ write(REG_CNF1, cnf1);
+ write(REG_CNF2, cnf2);
+ write(REG_CNF3, cnf3);
+}
+
+void
+canIE(bool enable) {
+ if (enable) {
+ write(REG_CANINTF, 0x00); // clear interrupt flags
+ write(REG_CANINTE, 0x03); // enable RX-buffer-full interrupts
+ } else {
+ write(REG_CANINTE, 0x00); // disable interrupts
+ }
+}
+
+static void
+writeIdToRegs(const CanId *id, Reg sidh, Reg sidl, Reg eid8, Reg eid0) {
+ switch (id->type) {
+ case CAN_ID_STD: // standard ID
+ write(sidh, (U8)(id->sid.hi << 5u) | (U8)(id->sid.lo >> 3u));
+ write(sidl, (U8) (id->sid.lo << 5u));
+ write(eid8, 0x00);
+ write(eid0, 0x00);
+ break;
+ case CAN_ID_EXT: // extended ID
+ write(sidh, (U8)(id->eid[1] << 5u) | (U8)(id->eid[0] >> 3u)); // sid[10:3]
+ write(sidl, (U8)(id->eid[0] << 5u) | (U8)0x08 | (U8)((id->eid[3] >> 3u) & 0x03)); // sid[2:0], exide, eid[28:27]
+ write(eid8, (U8)(id->eid[3] << 5u) | (U8)(id->eid[2] >> 3u)); // eid[26:19]
+ write(eid0, (U8)(id->eid[2] << 5u) | (U8)(id->eid[1] >> 3u)); // eid[18:11]
+ break;
+ }
+}
+
+void
+canSetMask0(const CanId *mask) {
+ writeIdToRegs(mask, REG_RXM0SIDH, REG_RXM0SIDL, REG_RXM0EID8, REG_RXM0EID0);
+}
+
+void
+canSetMask1(const CanId *mask) {
+ writeIdToRegs(mask, REG_RXM1SIDH, REG_RXM1SIDL, REG_RXM1EID8, REG_RXM1EID0);
+}
+
+void
+canSetFilter0(const CanId *filter) {
+ writeIdToRegs(filter, REG_RXF0SIDH, REG_RXF0SIDL, REG_RXF0EID8, REG_RXF0EID0);
+}
+
+void
+canSetFilter1(const CanId *filter) {
+ writeIdToRegs(filter, REG_RXF1SIDH, REG_RXF1SIDL, REG_RXF1EID8, REG_RXF1EID0);
+}
+
+void
+canSetFilter2(const CanId *filter) {
+ writeIdToRegs(filter, REG_RXF2SIDH, REG_RXF2SIDL, REG_RXF2EID8, REG_RXF2EID0);
+}
+
+void
+canSetFilter3(const CanId *filter) {
+ writeIdToRegs(filter, REG_RXF3SIDH, REG_RXF3SIDL, REG_RXF3EID8, REG_RXF3EID0);
+}
+
+void
+canSetFilter4(const CanId *filter) {
+ writeIdToRegs(filter, REG_RXF4SIDH, REG_RXF4SIDL, REG_RXF4EID8, REG_RXF4EID0);
+}
+
+void
+canSetFilter5(const CanId *filter) {
+ writeIdToRegs(filter, REG_RXF5SIDH, REG_RXF5SIDL, REG_RXF5EID8, REG_RXF5EID0);
}
diff --git a/fw/can.h b/fw/can.h
index 833a7c4..c9b3d4b 100644
--- a/fw/can.h
+++ b/fw/can.h
@@ -7,6 +7,7 @@
*
* Usage:
*
+ * #include <stdbool.h>
* #include <stdint.h>
* #include "types.h"
* #include "can.h"
@@ -17,14 +18,80 @@
#define CAN_CS LATAbits.LATA5
// Bit timings (CNF1, CNF2, CNF3)
-#define CAN_TIMINGS_10K 0xDE, 0xAD, 0x06 // BRP=30, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
-#define CAN_TIMINGS_20K 0xCF, 0xAD, 0x06 // BRP=15, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
-#define CAN_TIMINGS_50K 0xC6, 0xAD, 0x06 // BRP=6, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
-#define CAN_TIMINGS_100K 0xC3, 0xAD, 0x06 // BRP=3, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
-#define CAN_TIMINGS_125K 0xC4, 0x9A, 0x03 // BRP=4, PropSeg=3, PS1=4, PS2=4, SP=66.7%, SJW=4
-#define CAN_TIMINGS_250K 0xC2, 0x9A, 0x03 // BRP=2, PropSeg=3, PS1=4, PS2=4, SP=66.7%, SJW=4
-#define CAN_TIMINGS_500K 0xC1, 0x9A, 0x03 // BRP=1, PropSeg=3, PS1=4, PS2=4, SP=66.7, SJW=4
+#define CAN_TIMINGS_10K 0xDD, 0xAD, 0x06 // BRP=30, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
+#define CAN_TIMINGS_20K 0xCE, 0xAD, 0x06 // BRP=15, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
+#define CAN_TIMINGS_50K 0xC5, 0xAD, 0x06 // BRP=6, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
+#define CAN_TIMINGS_100K 0xC2, 0xAD, 0x06 // BRP=3, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4
+#define CAN_TIMINGS_125K 0xC3, 0x9A, 0x03 // BRP=4, PropSeg=3, PS1=4, PS2=4, SP=66.7%, SJW=4
+#define CAN_TIMINGS_250K 0xC1, 0x9A, 0x03 // BRP=2, PropSeg=3, PS1=4, PS2=4, SP=66.7%, SJW=4
+#define CAN_TIMINGS_500K 0xC0, 0x9A, 0x03 // BRP=1, PropSeg=3, PS1=4, PS2=4, SP=66.7%, SJW=4
#define CAN_TIMINGS_800K "800kbps unsupported" // not possible with 12MHz clock
#define CAN_TIMINGS_1M "1Mbps unsupported" // not possible with 12MHz clock
+// MCP2515 modes of operation
+typedef enum {
+ CAN_MODE_NORMAL = 0x0,
+ CAN_MODE_SLEEP = 0x1,
+ CAN_MODE_LOOPBACK = 0x2,
+ CAN_MODE_LISTEN_ONLY = 0x3,
+ CAN_MODE_CONFIG = 0x4,
+} CanMode;
+
+// CAN identifier
+typedef struct {
+ enum {
+ CAN_ID_STD, // standard
+ CAN_ID_EXT, // extended
+ } type;
+ union {
+ U16 sid; // 11-bit standard ID
+ U32 eid; // 29-bit extended ID
+ };
+} CanId;
+
+// Initialize the MCP2515.
+// Initial mode is Config.
void canInit(void);
+
+// Set the operating mode of the MCP2515.
+void canSetMode(CanMode mode);
+
+// Set the bit timing parameters (bitrate, etc.).
+// Pass one of the CAN_TIMINGS_x macros.
+// The MCP2515 must be in Config mode.
+void canSetBitTiming(U8 cnf1, U8 cnf2, U8 cnf3);
+
+// Enable/disable interrupts on the MCP2515's INT pin.
+void canIE(bool enable);
+
+// Set the message acceptance mask of RXB0.
+// The MCP2515 must be in Config mode.
+void canSetMask0(const CanId *mask);
+
+// Set the message acceptance mask of RXB1.
+// The MCP2515 must be in Config mode.
+void canSetMask0(const CanId *mask);
+
+// Set message acceptance filter 0 (RXB0).
+// The MCP2515 must be in Config mode.
+void canSetFilter0(const CanId *filter);
+
+// Set message acceptance filter 1 (RXB0).
+// The MCP2515 must be in Config mode.
+void canSetFilter1(const CanId *filter);
+
+// Set message acceptance filter 2 (RXB1).
+// The MCP2515 must be in Config mode.
+void canSetFilter2(const CanId *filter);
+
+// Set message acceptance filter 3 (RXB1).
+// The MCP2515 must be in Config mode.
+void canSetFilter3(const CanId *filter);
+
+// Set message acceptance filter 4 (RXB1).
+// The MCP2515 must be in Config mode.
+void canSetFilter4(const CanId *filter);
+
+// Set message acceptance filter 5 (RXB1).
+// The MCP2515 must be in Config mode.
+void canSetFilter5(const CanId *filter);
diff --git a/fw/types.h b/fw/types.h
index 392c7cf..ccdf0ae 100644
--- a/fw/types.h
+++ b/fw/types.h
@@ -18,6 +18,9 @@ typedef struct {
U8 hi, lo;
} U16;
+// Little-endian 32-bit unsigned integer.
+typedef U8 U32[4];
+
// *a = *a+b
void addU16(U16 *a, U8 b);