I think I've got the CAN mode running at 125kHz. Not yet sure.
[goodfet] / client / GoodFETMCPCAN.py
1 #!/usr/bin/env python
2 # GoodFET MCP2515 CAN Bus Client
3
4 # (C) 2012 Travis Goodspeed <travis at radiantmachines.com>
5 #
6 # This code is being rewritten and refactored.  You've been warned!
7 #
8
9 # The MCP2515 is a CAN Bus to SPI adapter from Microchip Technology,
10 # as used in the Goodthopter series of boards.  It requires a separate
11 # chip for voltage level conversion, such as the MCP2551.
12
13 import sys, time, string, cStringIO, struct, glob, os;
14
15 from GoodFETSPI import GoodFETSPI;
16
17 class GoodFETMCPCAN(GoodFETSPI):
18     """This class uses the regular SPI app to implement a CAN Bus
19     adapter on the Goodthopter10 hardware."""
20     MCPMODES=["Normal","Sleep","Loopback","Listen-only","Configuration",
21               "UNKNOWN","UNKNOWN","PowerUp"];
22     def MCPsetup(self):
23         """Sets up the ports."""
24         self.SPIsetup();
25         self.MCPreset(); #Reset the chip.
26         
27         # We're going to set some registers, so we must be in config
28         # mode.
29         self.MCPreqstatConfiguration();
30         
31         # Now we need to set the timing registers.  See chapter 5 of
32         # the MCP2515 datasheet to get some clue as to how this
33         # arithmetic of this works, as my comments here will likely be
34         # rambling, incoherent, and unchanged after I get the infernal
35         # thing working.
36         
37         # First, we must chose a Time Quanta (QT) which is used to
38         # define the durations of these segments.  Section 5.3
39         # suggests setting BRP<5:0> to 0x04 to get a TQ=500ns, as a 20
40         # MHz crystal gives a clock period of 50ns.  This way, for 125
41         # kHz communication, the bit time must be 16 TQ.
42         
43         # A bit consists of four parts:
44         # 1: SyncSeg             1 TQ
45         # 2: PropSeg             2 TQ
46         # 3: PhaseSeg1 (PS1)     7 TQ
47         # 4: PhaseSeg2 (PS2)     6 TQ
48         
49         # CNF1 with a prescaler of 4 and a SJW of 1 TQ.  SJW of 4
50         # might be more stable.
51         self.poke8(0x2a,0x04);
52         
53         # CNF2 with a BLTMODE of 1, SAM of 0, PS1 of 7TQ, and PRSEG of 2TQ
54         self.poke8(0x29,
55                    0x80   |  # BTLMODE=1
56                    (6<<3) |  # 6+1=7TQ for PHSEG1
57                    (1)       # 1+1=2TQ for PRSEG
58                    );
59         
60         #CNF3 with a PS2 length of 6TQ.
61         self.poke8(0x28,
62                    5      #5+1=6TQ
63                    );
64     def MCPreset(self):
65         """Reset the MCP2515 chip."""
66         self.SPItrans([0xC0]);
67     def MCPcanstat(self):
68         """Get the CAN Status."""
69         return self.peek8(0x0E);
70     def MCPreqstatNormal(self):
71         """Set the CAN state."""
72         state=0x0;
73         self.MCPbitmodify(0x0F,0xE0,(state<<5));
74     def MCPreqstatSleep(self):
75         """Set the CAN state."""
76         state=0x1;
77         self.MCPbitmodify(0x0F,0xE0,(state<<5));
78     def MCPreqstatLoopback(self):
79         """Set the CAN state."""
80         state=0x2;
81         self.MCPbitmodify(0x0F,0xE0,(state<<5));
82     def MCPreqstatListenOnly(self):
83         """Set the CAN state."""
84         state=0x3;
85         self.MCPbitmodify(0x0F,0xE0,(state<<5));
86     def MCPreqstatConfiguration(self):
87         """Set the CAN state."""
88         state=0x4;
89         self.MCPbitmodify(0x0F,0xE0,(state<<5));
90     
91     def MCPcanstatstr(self):
92         """Read the present status as a string."""
93         status=self.MCPcanstat();
94         opmod=(status&0xE0)>>5;
95         return self.MCPMODES[opmod];
96     def MCPrxstatus(self):
97         """Reads the RX Status by the SPI verb of the same name."""
98         data=self.SPItrans([0xB0,0x00]);
99         return ord(data[1]);
100
101     def MCPreadstatus(self):
102         """Reads the Read Status by the SPI verb of the same name."""
103         #See page 67 of the datasheet for the flag names.
104         data=self.SPItrans([0xA0,0x00]);
105         return ord(data[1]);
106
107     def MCPrts(self,TXB0=False,TXB1=False,TXB2=False):
108         """Requests to send one of the transmit buffers."""
109         flags=0;
110         if TXB0: flags=flags|1;
111         if TXB1: flags=flags|2;
112         if TXB2: flags=flags|4;
113         
114         if flags==0:
115             print "Warning: Requesting to send no buffer.";
116         
117         self.SPItrans([0x80|flags]);
118     def readrxbuffer(self,packbuf=0):
119         """Reads the RX buffer.  Might have old data."""
120         data=self.SPItrans([0x90|(packbuf<<2),
121                        0x00,0x00, #SID
122                        0x00,0x00, #EID
123                        0x00,      #DLC
124                        0x00, 0x00, 0x00, 0x00,
125                        0x00, 0x00, 0x00, 0x00
126                        ]);
127         return data[1:len(data)];
128     def writetxbuffer(self,packet,packbuf=0):
129         """Writes the transmit buffer."""
130         self.SPItrans([0x40|(packbuf<<1)]+packet);
131         
132     def rxpacket(self):
133         """Reads the next incoming packet from either buffer.
134         Returns None immediately if no packet is waiting."""
135         status=self.MCPrxstatus()&0xC0;
136         if status&0x40:
137             #Buffer 0 has higher priority.
138             return self.readrxbuffer(0);
139         elif status&0x80:
140             #Buffer 1 has lower priority.
141             return self.readrxbuffer(1);
142         else:
143             return None;
144     def txpacket(self,packet):
145         """Transmits a packet through one of the outbound buffers.
146         As usual, the packet should begin with SIDH.
147         For now, only TXB0 is supported."""
148         self.writetxbuffer(packet,0);
149         self.MCPrts(TXB0=True);
150     def packet2str(self,packet):
151         """Converts a packet from the internal format to a string."""
152         toprint="";
153         for bar in packet:
154             toprint=toprint+("%02x "%ord(bar))
155         return toprint;
156     def peek8(self,adr):
157         """Read a byte from the given address.  Untested."""
158         data=self.SPItrans([0x03,adr&0xFF,00]);
159         return ord(data[2]);
160     def MCPbitmodify(self,adr,mask,data):
161         """Writes a byte with a mask.  Doesn't work for many registers."""
162         data=self.SPItrans([0x05,adr&0xFF,mask&0xFF,data&0xFF]);
163         return ord(data[2]);
164     def poke8(self,adr,val):
165         """Poke a value into RAM.  Untested"""
166         self.SPItrans([0x02,adr&0xFF,val&0xFF]);
167         return val;