Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
MCP2515Can.cxx
Go to the documentation of this file.
1
34#define _DEFAULT_SOURCE
35
36#include "MCP2515Can.hxx"
37
38#include <fcntl.h>
39#include <compiler.h>
40
42{
43 /* 20 MHz clock source
44 * TQ = (2 * BRP) / freq = (2 * 5) / 20 MHz = 500 nsec
45 * Baud = 125 kHz
46 * bit time = 1 / 125 kHz = 8 usec = 16 TQ
47 * SyncSeg = 1 TQ
48 * PropSeg = 7 TQ
49 * PS1 = 4 TQ
50 * PS2 = 4 TQ
51 * sample time = (1 TQ + 7 TQ + 4 TQ) / 16 TQ = 75%
52 * SJW = PS2 - 1 = 4 - 1 = 3
53 * SJW = 3 * 500 nsec = 1.5 usec
54 *
55 * Oscillator Tolerance:
56 * 4 / (2 * ((13 * 16) - 4)) = 0.980%
57 * 3 / (20 * 16) = 0.938%
58 * = 0.938%
59 */
60 {20000000, 125000, {(5 - 1), (3 - 1)},
61 {(7 - 1), (4 - 1), 0, 1},
62 {(4 - 1), 0, 0}},
63 /* 8 MHz clock source
64 * TQ = (2 * BRP) / freq = (2 * 2) / 8 MHz = 500 nsec
65 * Baud = 125 kHz
66 * bit time = 1 / 125 kHz = 8 usec = 16 TQ
67 * SyncSeg = 1 TQ
68 * PropSeg = 7 TQ
69 * PS1 = 4 TQ
70 * PS2 = 4 TQ
71 * sample time = (1 TQ + 7 TQ + 4 TQ) / 16 TQ = 75%
72 * SJW = PS2 - 1 = 4 - 1 = 3
73 * SJW = 3 * 500 nsec = 1.5 usec
74 *
75 * Oscillator Tolerance:
76 * 4 / (2 * ((13 * 16) - 4)) = 0.980%
77 * 3 / (20 * 16) = 0.938%
78 * = 0.938%
79 */
80 {8000000, 125000, {(2 - 1), (3 - 1)},
81 {(7 - 1), (4 - 1), 0, 1},
82 {(4 - 1), 0, 0}}
83};
84
85/*
86 * init()
87 */
88void MCP2515Can::init(const char *spi_name, uint32_t freq, uint32_t baud)
89{
90 spiFd_ = ::open(spi_name, O_RDWR);
91 HASSERT(spiFd_ >= 0);
92
93 ::ioctl(spiFd_, SPI_IOC_GET_OBJECT_REFERENCE, &spi_);
95
96 /* configure SPI bus settings */
97 uint8_t spi_mode = SPI_MODE_0;
98 uint8_t spi_bpw = 8;
99 uint32_t spi_max_speed_hz = freq / 2;
100 if (spi_max_speed_hz > SPI_MAX_SPEED_HZ)
101 {
102 spi_max_speed_hz = SPI_MAX_SPEED_HZ;
103 }
104 ::ioctl(spiFd_, SPI_IOC_WR_MODE, &spi_mode);
105 ::ioctl(spiFd_, SPI_IOC_WR_BITS_PER_WORD, &spi_bpw);
106 ::ioctl(spiFd_, SPI_IOC_WR_MAX_SPEED_HZ, &spi_max_speed_hz);
107
108 /* reset device */
109 reset();
110
111 /* wait until device is in configuration mode */
112 while ((register_read(CANSTAT) & 0xE0) != 0x80);
113
114 /* find valid timing settings for the requested frequency and buad rates */
115 for (size_t i = 0; i < ARRAYSIZE(BAUD_TABLE); ++i)
116 {
117 if (BAUD_TABLE[i].freq_ == freq && BAUD_TABLE[i].baud_ == baud)
118 {
119 register_write(CNF1, BAUD_TABLE[i].cnf1_.data_);
120 register_write(CNF2, BAUD_TABLE[i].cnf2_.data_);
121 register_write(CNF3, BAUD_TABLE[i].cnf3_.data_);
122
123 /* setup RX Buf 0 to receive any message, and if RX Buf 0 is full,
124 * the new message will roll over into RX Buf 1 */
125 register_write(RXB0CTRL, 0x64);
126 register_write(RXB1CTRL, 0x60);
127
128 /* setup TXnRTS and RXnBF pins as inputs and outputs respectively */
129 register_write(BFPCTRL, 0x0C | (gpoData_ << 4));
131 gpiData_ = (register_read(TXRTSCTRL) >> 3) & 0x7;
132
133 /* put the device into normal operation mode */
134 register_write(CANCTRL, 0x00);
135
136 /* wait until device is in normal mode */
137 while ((register_read(CANSTAT) & 0xE0) != 0x00);
138
139 return;
140 }
141 }
142
143 /* unsupported frequency */
144 HASSERT(0);
145}
146
147/*
148 * enable()
149 */
151{
152 /* there is a mutex lock above us, so the following sequence is atomic */
153 if (!is_created())
154 {
155 /* start the thread at the highest priority in the system */
156 start(name, get_priority_max(), 2048);
157 }
158
159 /* clear interrupt flags */
161
162 /* enable error and receive interrupts */
164
166}
167
168/*
169 * disable()
170 */
172{
174
175 /* disable all interrupt sources */
177
178 register_write(TXB0CTRL, 0x00);
179 register_write(TXB1CTRL, 0x00);
180 portENTER_CRITICAL();
181 /* flush out any transmit data in the pipleline */
182 txBuf->flush();
183 txPending_ = 0;
184 portEXIT_CRITICAL();
185}
186
187/*
188 * tx_msg_locked()
189 */
190__attribute__((optimize("-O3")))
191void MCP2515Can::tx_msg_locked()
192{
193#if MCP2515_NULL_TX
194 /* throw away the CAN frames after consuming them */
195 struct can_frame *can_frame;
196
197 portENTER_CRITICAL();
198 if (txBuf->data_read_pointer(&can_frame))
199 {
200 txBuf->consume(1);
201 txBuf->signal_condition();
202 }
203 portEXIT_CRITICAL();
204#else
205 /* the node lock_ will be locked by the caller */
206 if (txPending_ < 3)
207 {
208 struct can_frame *can_frame;
209
210 /* find an empty buffer */
211 int index = (txPending_ & 0x1) ? 1 : 0;
212
213 portENTER_CRITICAL();
214 if (txBuf->data_read_pointer(&can_frame))
215 {
216 /* build up a transmit BufferWrite structure */
217 BufferWrite buffer(index, can_frame);
218 txBuf->consume(1);
219 portEXIT_CRITICAL();
220
221 /* bump up priority of the other buffer so it will
222 * transmit first if it is pending
223 */
224 bit_modify(index == 0 ? TXB1CTRL : TXB0CTRL, 0x01, 0x03);
225
226 /* load the tranmit buffer */
227 buffer_write(&buffer, can_frame);
228
229 txPending_ |= (0x1 << index);
230
231 /* request to send at lowest priority */
232 bit_modify(index == 0 ? TXB0CTRL : TXB1CTRL, 0x08, 0x0B);
233 bit_modify(CANINTE, TX0I << index, TX0I << index);
234 txBuf->signal_condition();
235 }
236 else
237 {
238 portEXIT_CRITICAL();
239 }
240 }
241#endif
242}
243
244/*
245 * MCP2515Can::ioctl()
246 */
247int MCP2515Can::ioctl(File *file, unsigned long int key, unsigned long data)
248{
249 if (key == SIOCGCANSTATE)
250 {
251 *((can_state_t*)data) = state_;
252 return 0;
253 }
254 return -EINVAL;
255}
256
257/*
258 * entry()
259 */
260__attribute__((optimize("-O3")))
261void *MCP2515Can::entry()
262{
263 for ( ; /* forever */ ; )
264 {
265#if MCP2515_DEBUG
266 int result = sem_.timedwait(SEC_TO_NSEC(1));
267
268 if (result != 0)
269 {
270 lock_.lock();
271 spi_ioc_transfer xfer[2];
272 memset(xfer, 0, sizeof(xfer));
273 uint8_t wr_data[2] = {READ, 0};
274 xfer[0].tx_buf = (unsigned long)wr_data;
275 xfer[0].len = sizeof(wr_data);
276 xfer[1].rx_buf = (unsigned long)regs_;
277 xfer[1].len = sizeof(regs_);
278 xfer[1].cs_change = 1;
279 ::ioctl(spiFd_, SPI_IOC_MESSAGE(2), xfer);
280 lock_.unlock();
281 continue;
282 }
283#else
284 sem_.wait();
285#endif
286 lock_.lock();
287
288 /* read status flags */
289 uint8_t canintf = register_read(CANINTF);
290
291 if (UNLIKELY((canintf & ERRI)) || UNLIKELY((canintf & MERR)))
292 {
293 /* error handling, read error flag register */
294 uint8_t eflg = register_read(EFLG);
295
296 /* clear error status flag */
297 bit_modify(CANINTF, 0, ERRI | MERR);
298
299 if (eflg & (RX0OVR | RX1OVR))
300 {
301 /* receive overrun */
302 ++overrunCount;
303
304 /* clear error flag */
305 bit_modify(EFLG, 0, (RX0OVR | RX1OVR));
306 }
307 if (eflg & TXBO)
308 {
309 /* bus off */
310 ++busOffCount;
311 state_ = CAN_STATE_BUS_OFF;
312 }
313 if ((eflg & TXEP) || (eflg & RXEP))
314 {
315 /* error passive state */
316 ++softErrorCount;
317 state_ = CAN_STATE_BUS_PASSIVE;
318
319 /* flush out any transmit data in the pipleline */
320 register_write(TXB0CTRL, 0x00);
321 register_write(TXB1CTRL, 0x00);
322 bit_modify(CANINTE, 0, TX0I | TX1I);
323 bit_modify(CANINTF, 0, TX0I | TX1I);
324
325 portENTER_CRITICAL();
326 txBuf->flush();
327 portEXIT_CRITICAL();
328 txBuf->signal_condition();
329
330 txPending_ = 0;
331 }
332 }
333
334 if (canintf & RX0I)
335 {
336 /* receive interrupt active */
337 state_ = CAN_STATE_ACTIVE;
338 BufferRead buffer(0);
339 buffer_read(&buffer);
340 struct can_frame *can_frame;
341
342 portENTER_CRITICAL();
343 if (LIKELY(rxBuf->data_write_pointer(&can_frame)))
344 {
345 buffer.build_struct_can_frame(can_frame);
346 rxBuf->advance(1);
347 rxBuf->signal_condition();
348 ++numReceivedPackets_;
349 }
350 else
351 {
352 /* receive overrun occured */
353 ++overrunCount;
354 }
355 portEXIT_CRITICAL();
356 }
357
358 /* RX Buf 1 can only be full if RX Buf 0 was also previously full.
359 * It is extremely unlikely that RX Buf 1 will ever be full.
360 */
361 if (UNLIKELY(canintf & RX1I))
362 {
363 /* receive interrupt active */
364 state_ = CAN_STATE_ACTIVE;
365 BufferRead buffer(1);
366 buffer_read(&buffer);
367 struct can_frame *can_frame;
368
369 portENTER_CRITICAL();
370 if (LIKELY(rxBuf->data_write_pointer(&can_frame)))
371 {
372 buffer.build_struct_can_frame(can_frame);
373 rxBuf->advance(1);
374 rxBuf->signal_condition();
375 ++numReceivedPackets_;
376 }
377 else
378 {
379 /* receive overrun occured */
380 ++overrunCount;
381 }
382 portEXIT_CRITICAL();
383 }
384
385 if (txPending_)
386 {
387 /* transmit interrupt active and transmission complete */
388 if (canintf & TX0I)
389 {
390 state_ = CAN_STATE_ACTIVE;
391 txPending_ &= ~0x1;
392 bit_modify(CANINTE, 0, TX0I);
393 bit_modify(CANINTF, 0, TX0I);
394 ++numTransmittedPackets_;
395 }
396 if (canintf & TX1I)
397 {
398 state_ = CAN_STATE_ACTIVE;
399 txPending_ &= ~0x2;
400 bit_modify(CANINTE, 0, TX1I);
401 bit_modify(CANINTF, 0, TX1I);
402 ++numTransmittedPackets_;
403 }
404
405 while (txPending_ < 3)
406 {
407 struct can_frame *can_frame;
408
409 /* find an empty buffer */
410 int index = (txPending_ & 0x1) ? 1 : 0;
411
412 portENTER_CRITICAL();
413 if (txBuf->data_read_pointer(&can_frame))
414 {
415 /* build up a transmit BufferWrite structure */
416 BufferWrite buffer(index, can_frame);
417 txBuf->consume(1);
418 portEXIT_CRITICAL();
419
420 /* bump up priority of the other buffer so it will
421 * transmit first if it is pending
422 */
423 bit_modify(index == 0 ? TXB1CTRL : TXB0CTRL, 0x01, 0x03);
424
425 /* load the tranmit buffer */
426 buffer_write(&buffer, can_frame);
427
428 txPending_ |= (0x1 << index);
429
430 /* request to send at lowest priority */
431 bit_modify(index == 0 ? TXB0CTRL : TXB1CTRL, 0x08, 0x0B);
432 bit_modify(CANINTE, TX0I << index, TX0I << index);
433 txBuf->signal_condition();
434 }
435 else
436 {
437 portEXIT_CRITICAL();
438 break;
439 }
440 }
441 }
442
443 if (UNLIKELY(ioPending_))
444 {
445 ioPending_ = false;
446 /* write the latest GPO data */
447 register_write(BFPCTRL, 0x0C | (gpoData_ << 4));
448
449 /* get the latest GPI data */
450 gpiData_ = (register_read(TXRTSCTRL) >> 3) & 0x7;
451 }
452 lock_.unlock();
453
454 interruptEnable_();
455 }
456
457 return NULL;
458}
459
460/*
461 * interrupt_handler()
462 */
463__attribute__((optimize("-O3")))
464void MCP2515Can::interrupt_handler()
465{
466 int woken = false;
467 interruptDisable_();
468 sem_.post_from_isr(&woken);
469 os_isr_exit_yield_test(woken);
470}
int ioctl(int fd, unsigned long int key,...)
Request and ioctl transaction.
Definition Fileio.cxx:452
DeviceBuffer< struct can_frame > * txBuf
transmit buffer
void flush()
flush all the data out of the buffer and reset the buffer.
const char * name
device name
Definition Devtab.hxx:266
Setup a buffer read transfer structure.
Setup a buffer read transfer structure.
void build_struct_can_frame(struct can_frame *can_frame)
Take the contents of the Buffer and fill in a can_frame structure.
Specialization of CAN driver for Tiva CAN.
unsigned gpoData_
local copy of the I/O expansion output data
@ CNF2
configuration register 3
@ CANSTAT
status
@ CNF3
configuration register 3
@ CANCTRL
control
@ CANINTF
interrupt flags
@ CNF1
configuration register 3
@ CANINTE
interrupt enable
@ TXRTSCTRL
TX RTS I/O control.
@ ERRI
error interrupt bit
@ RX0I
receive buffer 0 interrupt bit
@ RX1I
receive buffer 1 interrupt bit
@ MERR
message error interrupt bit
int ioctl(File *file, unsigned long int key, unsigned long data) override
Request an ioctl transaction.
SPI * spi_
pointer to a SPI object instance
void register_write(Registers address, uint8_t data)
Write to a SPI register.
static constexpr uint32_t SPI_MAX_SPEED_HZ
maximum SPI clock speed in Hz
unsigned gpiData_
local copy of the I/O expansion input data
unsigned state_
present bus state
void(* interruptEnable_)()
enable interrupt callback
void init(const char *spi_name, uint32_t freq, uint32_t baud)
Initialize CAN device settings.
int spiFd_
SPI bus that accesses MCP2515.
void enable() override
function to enable device
void(* interruptDisable_)()
disable interrupt callback
void disable() override
function to disable device
uint8_t register_read(Registers address)
Read from a SPI register.
void reset()
Reset the device.
unsigned txPending_
transmission in flight
static const MCP2515Baud BAUD_TABLE[]
baud rate settings table
int open(File *, const char *, int, int) OVERRIDE
Open method.
bool is_created()
Definition OS.hxx:90
static int get_priority_max()
Get the maximum thread priority.
Definition OS.hxx:132
void start(const char *name, int priority, size_t stack_size)
Starts the thread.
Definition OS.hxx:78
#define CAN_STATE_BUS_OFF
CAN bus off.
#define CAN_STATE_ACTIVE
CAN bus active.
#define SIOCGCANSTATE
Read the CAN state.
uint32_t can_state_t
CAN state type.
#define CAN_STATE_BUS_PASSIVE
CAN bus error passive.
#define ARRAYSIZE(a)
Returns the number of elements in a statically defined array (of static size)
Definition macros.h:185
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define SEC_TO_NSEC(_sec)
Convert a second value to a nanosecond value.
Definition os.h:286
File information.
Definition Devtab.hxx:52
Baud rate table entry.