Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
MCP2515Can.hxx
Go to the documentation of this file.
1
34#ifndef _FREERTOS_DRIVERS_COMMON_MCP2515CAN_HXX_
35#define _FREERTOS_DRIVERS_COMMON_MCP2515CAN_HXX_
36
37#include <unistd.h>
38#include <cstddef>
39
40#include "Can.hxx"
41#include "SPI.hxx"
42
43#include "os/Gpio.hxx"
44#include "os/OS.hxx"
45
46#include "can_ioctl.h"
47
48#define MCP2515_DEBUG 0
49#define MCP2515_NULL_TX 0
50
51class MCP2515GPO;
52class MCP2515GPI;
53
56class MCP2515Can : public Can, public OSThread
57{
58public:
64 MCP2515Can(const char *name,
65 void (*interrupt_enable)(), void (*interrupt_disable)())
66 : Can(name)
67 , OSThread()
68 , interruptEnable_(interrupt_enable)
69 , interruptDisable_(interrupt_disable)
71 , txPending_(0)
72 , gpoData_(0x0)
73 , gpiData_(0x7)
74 , ioPending_(false)
75 , spiFd_(-1)
76 , spi_(nullptr)
77 , sem_()
78 {
79 }
80
84 {
85 }
86
93 void init(const char *spi_name, uint32_t freq, uint32_t baud);
94
97 void interrupt_handler();
98
104 {
105 return &lock_;
106 }
107
108private:
110 static constexpr uint32_t SPI_MAX_SPEED_HZ = 10000000;
111
114 {
115 RXF0SIDH = 0,
116 RXF0SIDL,
117 RXF0SEID8,
118 RXF0EID0,
119 RXF1SIDH,
120 RXF1SIDL,
121 RXF1SEID8,
122 RXF1EID0,
123 RXF2SIDH,
124 RXF2SIDL,
125 RXF2SEID8,
126 RXF2EID0,
127 BFPCTRL,
132 RXF3SIDH = 16,
133 RXF3SIDL,
134 RXF3EID8,
135 RXF3EID0,
136 RXF4SIDH,
137 RXF4SIDL,
138 RXF4EID8,
139 RXF4EID0,
140 RXF5SIDH,
141 RXF5SIDL,
142 RXF5EID8,
143 RXF5EID0,
147 RXM0SIDH = 32,
148 RXM0SIDL,
149 RXM0EID8,
150 RXM0EID0,
151 RXM1SIDH,
152 RXM1SIDL,
153 RXM1EID8,
154 RXM1EID0,
162 TXB0CTRL = 48,
163 TXB0SIDH,
164 TXB0SIDL,
165 TXB0EID8,
166 TXB0EID0,
167 TXB0DLC,
168 TXB0D0,
169 TXB0D1,
170 TXB0D2,
171 TXB0D3,
172 TXB0D4,
173 TXB0D5,
174 TXB0D6,
175 TXB0D7,
176
177 TXB1CTRL = 64,
178 TXB1SIDH,
179 TXB1SIDL,
180 TXB1EID8,
181 TXB1EID0,
182 TXB1DLC,
183 TXB1D0,
184 TXB1D1,
185 TXB1D2,
186 TXB1D3,
187 TXB1D4,
188 TXB1D5,
189 TXB1D6,
190 TXB1D7,
191
192 TXB2CTRL = 80,
193 TXB2SIDH,
194 TXB2SIDL,
195 TXB2EID8,
196 TXB2EID0,
197 TXB2DLC,
198 TXB2D0,
199 TXB2D1,
200 TXB2D2,
201 TXB2D3,
202 TXB2D4,
203 TXB2D5,
204 TXB2D6,
205 TXB2D7,
206
207 RXB0CTRL = 96,
208 RXB0SIDH,
209 RXB0SIDL,
210 RXB0EID8,
211 RXB0EID0,
212 RXB0DLC,
213 RXB0D0,
214 RXB0D1,
215 RXB0D2,
216 RXB0D3,
217 RXB0D4,
218 RXB0D5,
219 RXB0D6,
220 RXB0D7,
221
222 RXB1CTRL = 112,
223 RXB1SIDH,
224 RXB1SIDL,
225 RXB1EID8,
226 RXB1EID0,
227 RXB1DLC,
228 RXB1D0,
229 RXB1D1,
230 RXB1D2,
231 RXB1D3,
232 RXB1D4,
233 RXB1D5,
234 RXB1D6,
235 RXB1D7,
236 };
237
240 {
241 CLKPRE = 0x03,
242 CLKEN = 0x04,
243 OSM = 0x08,
244 ABAT = 0x10,
245 REQOP = 0xE0,
246 };
247
250 {
251 RX0I = 0x01,
252 RX1I = 0x02,
253 TX0I = 0x04,
254 TX1I = 0x08,
255 TX2I = 0x10,
256 ERRI = 0x20,
257 WAKI = 0x40,
258 MERR = 0x80,
259 };
260
263 {
264 EWARN = 0x01,
265 RXWARN = 0x02,
266 TXWARN = 0x04,
267 RXEP = 0x08,
268 TXEP = 0x10,
269 TXBO = 0x20,
270 RX0OVR = 0x40,
271 RX1OVR = 0x80,
272 };
273
276 {
277 RESET = 0xC0,
278 READ = 0x03,
279 READ_RX_BUF = 0x90,
280 WRITE = 0x02,
281 LOAD_TX_BUF = 0x40,
282 STATUS = 0xA0,
283 RX_STATUS = 0xB0,
284 RTS = 0x80,
285 BIT_MODIFY = 0x05,
286 };
287
289 struct Config1
290 {
295 Config1(uint8_t brp, uint8_t sjw)
296 : brp_(brp)
297 , sjw_(sjw)
298 {
299 }
300
301 union
302 {
303 uint8_t data_;
304 struct
305 {
306 uint8_t brp_ : 6;
307 uint8_t sjw_ : 2;
308 };
309 };
310 };
311
313 struct Config2
314 {
321 Config2(uint8_t prseg, uint8_t prseg1, uint8_t sam, uint8_t bltmode)
322 : prseg_(prseg)
323 , prseg1_(prseg1)
324 , sam_(sam)
325 , bltmode_(bltmode)
326 {
327 }
328
329 union
330 {
331 uint8_t data_;
332 struct
333 {
334 uint8_t prseg_ : 3;
335 uint8_t prseg1_ : 3;
336 uint8_t sam_ : 1;
337 uint8_t bltmode_ : 1;
338 };
339 };
340 };
341
343 struct Config3
344 {
350 Config3(uint8_t phseg2, uint8_t wakfil, uint8_t sof)
351 : phseg2_(phseg2)
352 , unused_(0)
353 , wakfil_(wakfil)
354 , sof_(sof)
355 {
356 }
357
358 union
359 {
360 uint8_t data_;
361 struct
362 {
363 uint8_t phseg2_ : 3;
364 uint8_t unused_ : 3;
365 uint8_t wakfil_ : 1;
366 uint8_t sof_ : 1;
367 };
368 };
369 };
370
380
382 class Buffer
383 {
384 public:
386 static const size_t TRANSFER_SIZE = 14;
387
391 __attribute__((optimize("-O3")))
392 void build_struct_can_frame(struct can_frame *can_frame)
393 {
394 can_frame->can_eff = exide_;
395 if (LIKELY(exide_))
396 {
397 /* extended frame */
398 can_frame->can_rtr = rtr_;
399 can_frame->can_id = ((uint32_t)eid0_ << 0) +
400 ((uint32_t)eid8_ << 8) +
401 ((uint32_t)eid_ << 16) +
402 ((uint32_t)sid_ << 18) +
403 ((uint32_t)sidh_ << 21);
404 }
405 else
406 {
407 /* standard frame */
408 can_frame->can_rtr = srr_;
409 can_frame->can_id = ((uint32_t)sid_ << 0) +
410 ((uint32_t)sidh_ << 3);
411 }
412 can_frame->can_err = 0;
413 can_frame->can_dlc = dlc_;
414 static_assert(offsetof(Buffer, data_) == 8, "data_ misaligned");
415 can_frame->data64 = data64_;
416 }
417
422 {
423 return &command_;
424 }
425
426 protected:
431 __attribute__((optimize("-O3")))
432 Buffer(struct can_frame *can_frame, uint8_t command)
433 : command_(command)
434 , dlc_(can_frame->can_dlc)
435 , rtr_(can_frame->can_rtr)
436 {
437 if (LIKELY(can_frame->can_eff))
438 {
439 sidh_ = (can_frame->can_id & 0x1FE00000) >> 21;
440 eid_ = (can_frame->can_id & 0x00030000) >> 16;
441 exide_ = 1;
442 sid_ = (can_frame->can_id & 0x001C0000) >> 18;
443 eid8_ = (can_frame->can_id & 0x0000FF00) >> 8;
444 eid0_ = (can_frame->can_id & 0x000000FF) >> 0;
445 }
446 else
447 {
448 sidh_ = (can_frame->can_id & 0x000007F8) >> 3;
449 eid_ = 0;
450 exide_ = 0;
451 sid_ = (can_frame->can_id & 0x000007F8) >> 3;
452 eid8_ = 0;
453 eid0_ = 0;
454 }
455 static_assert(offsetof(Buffer, data_) == 8, "data_ misaligned");
456 data64_ = can_frame->data64;
457 }
458
462 Buffer( uint8_t command)
463 : command_(command)
464 {
465 }
466
467 uint8_t pad_[2];
468 uint8_t command_;
469 uint8_t sidh_;
470 struct
471 {
472 uint8_t eid_ : 2;
473 uint8_t unused1_ : 1;
474 uint8_t exide_ : 1;
475 uint8_t srr_ : 1;
476 uint8_t sid_ : 3;
477 };
478 uint8_t eid8_;
479 uint8_t eid0_;
480 struct
481 {
482 uint8_t dlc_ : 4;
483 uint8_t unused3_ : 2;
484 uint8_t rtr_ : 1;
485 uint8_t unused4_ : 1;
486 };
487 union
488 {
489 uint64_t data64_;
490 uint8_t data_[8];
491 };
492 };
493
496 class BufferRead : public Buffer
497 {
498 public:
502 BufferRead(int index)
503 : Buffer(READ_RX_BUF | (index == 0 ? 0x00 : 0x04))
504 {
505 }
506 };
507
510 class BufferWrite : public Buffer
511 {
512 public:
517 BufferWrite(int index, struct can_frame *can_frame)
518 : Buffer(can_frame, LOAD_TX_BUF + (index << 1))
519 {
520 }
521 };
522
529 int ioctl(File *file, unsigned long int key, unsigned long data) override;
530
534 void *entry() override;
536 void enable() override;
537 void disable() override;
540 __attribute__((optimize("-O3")))
541 void tx_msg() override
542 {
543 /* The caller has us in a critical section, we need to exchange a
544 * critical section lock for a mutex lock since event handling happens
545 * in thread context and not in interrupt context like it would in a
546 * typical CAN device driver.
547 */
548 portEXIT_CRITICAL();
549 lock_.lock();
551 lock_.unlock();
552 portENTER_CRITICAL();
553 }
554
556 void tx_msg_locked();
557
560 void reset()
561 {
562 spi_ioc_transfer xfer;
563 uint8_t reset = RESET;
564
565 xfer.tx_buf = (unsigned long)&reset;
566 xfer.rx_buf = 0;
567 xfer.len = sizeof(reset);
568
570 }
571
576 __attribute__((optimize("-O3")))
577 uint8_t register_read(Registers address)
578 {
579 spi_ioc_transfer xfer;
580 uint8_t data[3] = {READ, address, 0};
581
582 xfer.tx_buf = (unsigned long)data;
583 xfer.rx_buf = (unsigned long)data;
584 xfer.len = sizeof(data);
585
587
588 return data[2];
589 }
590
595 __attribute__((optimize("-O3")))
596 void register_write(Registers address, uint8_t data)
597 {
598 spi_ioc_transfer xfer;
599 uint8_t payload[] = {WRITE, address, data};
600
601 xfer.tx_buf = (unsigned long)payload;
602 xfer.rx_buf = 0;
603 xfer.len = sizeof(payload);
604
606 }
607
613 void bit_modify(Registers address, uint8_t data, uint8_t mask)
614 {
615 spi_ioc_transfer xfer;
616 uint8_t payload[] = {BIT_MODIFY, address, mask, data};
617
618 xfer.tx_buf = (unsigned long)payload;
619 xfer.rx_buf = 0;
620 xfer.len = sizeof(payload);
622 }
623
628 {
629 spi_ioc_transfer xfer;
630 xfer.tx_buf = (unsigned long)buf->get_payload();
631 xfer.rx_buf = (unsigned long)buf->get_payload();
632 xfer.len = buf->TRANSFER_SIZE;
634 }
635
640 void buffer_write(BufferWrite *buf, struct can_frame *can_frame)
641 {
642 spi_ioc_transfer xfer;
643 xfer.tx_buf = (unsigned long)buf->get_payload();
644 xfer.rx_buf = 0;
645 xfer.len = buf->TRANSFER_SIZE;
646 spi_->SPI::transfer_with_cs_assert_polled(&xfer);
647 }
648
652 {
653 if (!ioPending_)
654 {
655 ioPending_ = true;
656 sem_.post();
657 }
658 }
659
662 unsigned state_ : 4;
663 unsigned txPending_ : 2;
664 unsigned gpoData_ : 2;
665 unsigned gpiData_ : 3;
666 unsigned ioPending_ : 1;
667 int spiFd_;
670#if MCP2515_DEBUG
671 volatile uint8_t regs_[128];
672#endif
673
675 static const MCP2515Baud BAUD_TABLE[];
676
680
682 friend class MCP2515GPO;
683
685 friend class MCP2515GPI;
686
688};
689
692class MCP2515GPO : public Gpio
693{
694public:
699 MCP2515GPO(MCP2515Can *instance, uint8_t bit)
700 : Gpio()
701 , instance_(instance)
702 , bit_(bit)
703 {
704 HASSERT(bit < 2);
705 }
706
710 void write(Value new_state) const override
711 {
712 new_state ? set() : clr();
713 }
714
718 Value read() const override
719 {
720 return instance_->gpoData_ & (0x1 << bit_) ? Gpio::SET : Gpio::CLR;
721 }
722
725 void set() const override
726 {
727 if (!(instance_->gpoData_ & (0x1 << bit_)))
728 {
729 portENTER_CRITICAL();
730 instance_->gpoData_ |= 0x1 << bit_;
731 portEXIT_CRITICAL();
733 }
734 }
735
738 void clr() const override
739 {
740 if ((instance_->gpoData_ & (0x1 << bit_)))
741 {
742 portENTER_CRITICAL();
743 instance_->gpoData_ &= ~(0x1 << bit_);
744 portEXIT_CRITICAL();
746 }
747 }
748
752 void set_direction(Gpio::Direction dir) const override
753 {
754 HASSERT(dir == Gpio::Direction::DOUTPUT);
755 }
756
760 Direction direction() const override
761 {
762 return Gpio::Direction::DOUTPUT;
763 }
764
765private:
768
770 uint8_t bit_;
771
773};
774
777class MCP2515GPI : public Gpio
778{
779public:
784 MCP2515GPI(MCP2515Can *instance, uint8_t bit)
785 : Gpio()
786 , instance_(instance)
787 , bit_(bit)
788 {
789 HASSERT(bit < 3);
790 }
791
795 Value read() const override
796 {
798 return instance_->gpiData_ & (0x1 << bit_) ? Gpio::SET : Gpio::CLR;
799 }
800
804 void set_direction(Gpio::Direction dir) const override
805 {
806 HASSERT(dir == Gpio::Direction::DINPUT);
807 }
808
812 Direction direction() const override
813 {
814 return Gpio::Direction::DINPUT;
815 }
816
817private:
820
822 uint8_t bit_;
823
825};
826
827#endif /* _FREERTOS_DRIVERS_COMMON_MCP2515CAN_HXX_ */
Base class for a CAN device for the Arduino environment.
const char * name
device name
Definition Devtab.hxx:266
OS-independent abstraction for GPIO.
Definition Gpio.hxx:43
Value
Defines the options for GPIO level.
Definition Gpio.hxx:62
Direction
Defines the options for GPIO direction.
Definition Gpio.hxx:73
Setup a buffer read transfer structure.
BufferRead(int index)
Constructor.
Setup a buffer read transfer structure.
BufferWrite(int index, struct can_frame *can_frame)
Constructor.
CAN TX and RX buffer structure.
uint8_t eid_
extended identifier bits 16 and 17
uint8_t unused4_
unused bit
void * get_payload()
Get a pointer to the buffer payload.
Buffer(uint8_t command)
Constructor.
uint8_t pad_[2]
force the data to 32/64-bit allign
uint8_t eid8_
extended identifier high byte
uint8_t exide_
extended identifer enable
uint8_t unused3_
unused bits
uint8_t eid0_
extended identifier low byte
uint8_t sidh_
standard identifier high byte
uint64_t data64_
64-bit data
uint8_t sid_
standard identifier low bits
uint8_t dlc_
data length code
uint8_t unused1_
unused bit
static const size_t TRANSFER_SIZE
The SPI transfer size in bytes of a buffer transfer.
uint8_t data_[8]
all 8 data bytes
uint8_t rtr_
remote transmit request bit
void build_struct_can_frame(struct can_frame *can_frame)
Take the contents of the Buffer and fill in a can_frame structure.
uint8_t command_
the transaction command
uint8_t srr_
standard frame RTR
Specialization of CAN driver for Tiva CAN.
unsigned gpoData_
local copy of the I/O expansion output data
MCP2515Can()
Default constructor.
Registers
SPI registers.
@ EFLG
error flags
@ CNF2
configuration register 3
@ CANSTAT
status
@ TEC
transmit error counter
@ CNF3
configuration register 3
@ CANCTRL
control
@ CANINTF
interrupt flags
@ RED
receive error counter
@ CNF1
configuration register 3
@ CANINTE
interrupt enable
@ TXRTSCTRL
TX RTS I/O control.
InterruptFlags
interrupt flag masks
@ TX0I
transmit buffer 0 interrupt bit
@ ERRI
error interrupt bit
@ RX0I
receive buffer 0 interrupt bit
@ RX1I
receive buffer 1 interrupt bit
@ WAKI
wake-up interrupt bit
@ TX2I
transmit buffer 2 interrupt bit
@ TX1I
transmit 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.
void request_gpio_operation()
Request that the GPIO cache be refreshed.
SPI * spi_
pointer to a SPI object instance
void buffer_read(BufferRead *buf)
Read a message to into a receive buffer.
MCP2515Can(const char *name, void(*interrupt_enable)(), void(*interrupt_disable)())
Constructor.
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
OSSem sem_
semaphore for posting events
void tx_msg() override
Function to try and transmit a message.
unsigned state_
present bus state
void(* interruptEnable_)()
enable interrupt callback
void bit_modify(Registers address, uint8_t data, uint8_t mask)
Bit modify to a SPI register.
void tx_msg_locked()
Function to try and transmit a message while holding a lock.
void buffer_write(BufferWrite *buf, struct can_frame *can_frame)
Write a message to a transmit buffer.
void init(const char *spi_name, uint32_t freq, uint32_t baud)
Initialize CAN device settings.
ControlFields
Fields of the control register.
@ REQOP
request operation mode
@ CLKPRE
clockout pin prescaler
@ CLKEN
clockout pin enable
@ OSM
one-shot mode
@ ABAT
abort all pending transmissions
unsigned ioPending_
true if an I/O update is pending
OSMutex * get_spi_bus_lock()
Return a mutex that can be used by another SPI driver instance sharing the same bus as its bus lock.
int spiFd_
SPI bus that accesses MCP2515.
void enable() override
function to enable device
ErrorFlags
interrupt flag masks
@ TXBO
set when TEC reaches 255 (bus-off)
@ TXWARN
set when TEC is equal to or greater than 96
@ RXWARN
set when REC is equal to or greater than 96
@ RXEP
set when REC is equal to or greater than 128
@ EWARN
set when TEC or REC is equal to or greater than 96
@ RX0OVR
receiver buffer 0 overflow flag
@ TXEP
set when TEC is equal to or greater than 128
@ RX1OVR
receiver buffer 1 overflow flag
void(* interruptDisable_)()
disable interrupt callback
void disable() override
function to disable device
~MCP2515Can()
Destructor.
void interrupt_handler()
Handle an interrupt.
Instructions
SPI transaction instructions.
@ READ_RX_BUF
read a receive buffer
@ RX_STATUS
read rx status
@ READ
read data from register at selected address
@ RESET
resets internal registers to default state
@ LOAD_TX_BUF
load a transmit buffer
@ STATUS
read status
@ WRITE
write data to register at selected address
@ BIT_MODIFY
perform a bit manipulation
@ RTS
request to send a tramsmit buffer
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
void * entry() override
User entry point for the created thread.
General Purpose Input (GPI) instance on the MCP2515.
uint8_t bit_
bit number representative of the bit
MCP2515Can * instance_
reference to chip instance
Value read() const override
Retrieves the current Value of a GPI input pin.
void set_direction(Gpio::Direction dir) const override
Sets the GPI direction (does nothing).
Direction direction() const override
Gets the GPI direction.
MCP2515GPI(MCP2515Can *instance, uint8_t bit)
Constructor.
General Purpose Output (GPO) instance on the MCP2515.
MCP2515Can * instance_
reference to chip instance
Value read() const override
Retrieves the current Value of a GPO output sate (requested).
void set() const override
Sets the GPO pin to high.
MCP2515GPO(MCP2515Can *instance, uint8_t bit)
Constructor.
Direction direction() const override
Gets the GPO direction.
void clr() const override
Clears the GPO pin to low.
void set_direction(Gpio::Direction dir) const override
Sets the GPO direction (does nothing).
void write(Value new_state) const override
Writes a GPO pin (set or clear to a specific state).
uint8_t bit_
bit number representative of the bit
OSMutex lock_
protects internal structures.
Definition Devtab.hxx:588
This class provides a mutex API.
Definition OS.hxx:427
void lock()
Lock a mutex.
Definition OS.hxx:446
void unlock()
Unlock a mutex.
Definition OS.hxx:453
This class provides a counting semaphore API.
Definition OS.hxx:243
void post()
Post (increment) a semaphore.
Definition OS.hxx:260
This class provides a threading API.
Definition OS.hxx:46
Private data for an SPI device.
Definition SPI.hxx:53
int transfer_with_cs_assert_polled(struct spi_ioc_transfer *msgs, int num=1)
Method to transmit/receive the data.
Definition SPI.hxx:80
#define CAN_STATE_STOPPED
CAN bus stopped.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Removes default copy-constructor and assignment added by C++.
Definition macros.h:171
File information.
Definition Devtab.hxx:52
Configuration resgister 1 structure.
uint8_t sjw_
synchronization jump width length bits
uint8_t data_
raw data for register
uint8_t brp_
baud rate prescaler bits
Config1(uint8_t brp, uint8_t sjw)
Constructor.
Configuration resgister 2 structure.
uint8_t bltmode_
PS2 bit time length bit.
uint8_t sam_
sample point configuration bit
uint8_t data_
raw data for register
uint8_t prseg1_
PS1 length bits.
uint8_t prseg_
propagation segment length bits
Config2(uint8_t prseg, uint8_t prseg1, uint8_t sam, uint8_t bltmode)
Constructor.
Configuration resgister 3 structure.
uint8_t phseg2_
PS2 length bits.
Config3(uint8_t phseg2, uint8_t wakfil, uint8_t sof)
Constructor.
uint8_t wakfil_
wake-up filter bit
uint8_t sof_
start of frame signal bit
uint8_t unused_
unused bits
uint8_t data_
raw data for register
Baud rate table entry.
uint32_t baud_
target baud rate
Config3 cnf3_
Configuration registers CNF3.
Config2 cnf2_
Configuration registers CNF2.
uint32_t freq_
incoming frequency
Config1 cnf1_
Configuration registers CNF1.