1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
|
#include <xc.h>
#include <stdbool.h>
#include <stdint.h>
#include "system.h"
#include "types.h"
#include "spi.h"
#include "can.h"
// Oscillator startup timeout
#define STARTUP_TIME 128u
// Register addresses
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,
// Filter 0
REG_RXF0SIDH = 0x00, // standard ID high
REG_RXF0SIDL = 0x01, // standard ID low
REG_RXF0EID8 = 0x02, // extended ID high
REG_RXF0EID0 = 0x03, // extended ID low
// Filter 1
REG_RXF1SIDH = 0x04, // standard ID high
REG_RXF1SIDL = 0x05, // standard ID low
REG_RXF1EID8 = 0x06, // extended ID high
REG_RXF1EID0 = 0x07, // extended ID low
// Filter 2
REG_RXF2SIDH = 0x08, // standard ID high
REG_RXF2SIDL = 0x09, // standard ID low
REG_RXF2EID8 = 0x0A, // extended ID high
REG_RXF2EID0 = 0x0B, // extended ID low
// Filter 3
REG_RXF3SIDH = 0x10, // standard ID high
REG_RXF3SIDL = 0x11, // standard ID low
REG_RXF3EID8 = 0x12, // extended ID high
REG_RXF3EID0 = 0x13, // extended ID low
// Filter 4
REG_RXF4SIDH = 0x14, // standard ID high
REG_RXF4SIDL = 0x15, // standard ID low
REG_RXF4EID8 = 0x16, // extended ID high
REG_RXF4EID0 = 0x17, // extended ID low
// Filter 5
REG_RXF5SIDH = 0x18, // standard ID high
REG_RXF5SIDL = 0x19, // standard ID low
REG_RXF5EID8 = 0x1A, // extended ID high
REG_RXF5EID0 = 0x1B, // extended ID low
// Mask 0
REG_RXM0SIDH = 0x20, // standard ID high
REG_RXM0SIDL = 0x21, // standard ID low
REG_RXM0EID8 = 0x22, // extended ID high
REG_RXM0EID0 = 0x23, // extended ID low
// Mask 1
REG_RXM1SIDH = 0x24, // standard ID high
REG_RXM1SIDL = 0x25, // standard ID low
REG_RXM1EID8 = 0x26, // extended ID high
REG_RXM1EID0 = 0x27, // extended ID low
// Receive buffer 0
REG_RXB0SIDH = 0x61, // standard ID high
REG_RXB0SIDL = 0x62, // standard ID low
REG_RXB0EID8 = 0x63, // extended ID high
REG_RXB0EID0 = 0x64, // extended ID low
REG_RXB0DLC = 0x65, // data length code
REG_RXB0DM = 0x66, // data byte <M> [66h+0, 66h+7]
// Receive buffer 1
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]
// Transmit buffer 0
REG_TXB0CTRL = 0x30, // control
REG_TXB0SIDH = 0x31, // standard ID high
REG_TXB0SIDL = 0x32, // standard ID low
REG_TXB0EID8 = 0x33, // extended ID high
REG_TXB0EID0 = 0x34, // extended ID low
REG_TXB0DLC = 0x35, // data length code
REG_TXB0DM = 0x36, // data byte <M> [36h+0, 36h+7]
} Reg;
// Masks
enum {
// TXBnCTRL
TXREQ = 0x08,
TXERR = 0x10,
// TXBnDLC
RTR = 0x40,
EXIDE = 0x08, // extended identifier enable bit in SIDL registers
};
// Instructions
enum {
CMD_RESET = 0xC0,
CMD_READ = 0x03,
CMD_READ_RX = 0x90,
CMD_WRITE = 0x02,
CMD_LOAD_TX = 0x40,
CMD_RTS = 0x80,
CMD_READ_STATUS = 0xA0,
CMD_RX_STATUS = 0xB0,
CMD_BIT_MODIFY = 0x05,
};
// Receive buffer identifiers
typedef enum {
RXB0 = 0,
RXB1 = 1,
} RxBuf;
// Send RESET instruction.
static void
reset(void) {
CAN_CS = 0;
(void)spiTx(CMD_RESET);
CAN_CS = 1;
}
// Read a register.
static U8
read(Reg addr) {
U8 data;
CAN_CS = 0;
(void)spiTx(CMD_READ);
(void)spiTx((U8)addr);
data = spiTx(0x00);
CAN_CS = 1;
return data;
}
// Write to a register.
static void
write(Reg addr, U8 data) {
CAN_CS = 0;
(void)spiTx(CMD_WRITE);
(void)spiTx((U8)addr);
(void)spiTx(data);
CAN_CS = 1;
}
U8
canRxStatus(void) {
U8 status;
CAN_CS = 0;
(void)spiTx(CMD_RX_STATUS);
status = spiTx(0x00);
CAN_CS = 1;
return status;
}
// Write individual bits of a register with the BIT MODIFY instruction.
static void
bitModify(Reg addr, U8 mask, U8 data) {
CAN_CS = 0;
(void)spiTx(CMD_BIT_MODIFY);
(void)spiTx((U8)addr);
(void)spiTx(mask);
(void)spiTx(data);
CAN_CS = 1;
}
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
// 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
readRxbnData(U8 n, U8 data[8u]) {
CAN_CS = 0;
n &= 0x01; // n in {0,1}
(void)spiTx(CMD_READ_RX | (U8)(n << 2u) | 0x02); // start at RXBnD0
for (n = 0u; n < 8u; n++) {
data[n] = spiTx(0x00);
}
CAN_CS = 1;
}
void
canReadRxb0Data(U8 data[8u]) {
readRxbnData(0u, data);
}
void
canReadRxb1Data(U8 data[8u]) {
readRxbnData(1u, data);
}
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) | EXIDE | (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;
}
}
Status
canTx(const CanFrame *frame) {
U8 k, ctrl;
// Set ID, DLC, and RTR
writeIdToRegs(&frame->id, REG_TXB0SIDH, REG_TXB0SIDL, REG_TXB0EID8, REG_TXB0EID0);
write(REG_TXB0DLC, (frame->dlc & 0x0F) | ((frame->rtr) ? RTR : 0));
// Copy data to registers
for (k = 0u; k < frame->dlc; k++) {
write(REG_TXB0DM+k, frame->data[k]);
}
// Send
bitModify(REG_TXB0CTRL, TXREQ, TXREQ);
do {
ctrl = read(REG_TXB0CTRL);
if (ctrl & TXERR) {
// Error
bitModify(REG_TXB0CTRL, TXREQ, 0); // cancel transmission
return FAIL;
}
} while (ctrl & TXREQ); // transmission in progress
return OK;
}
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);
}
|