turn ftdi driver into echo server
[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 # WORKING COPY
7 # Currently being updated by Siege Technology's ENGS89 team
8 # Primary contact: Theodore Sumers <ted.sumers at gmail.com>
9 # ADDED FUNCTIONALITY:
10 #   10.4/41.6/83.3 kHz sniffing
11 #   setbitrate
12 #   debugs on TX and RX packet
13 #   int-based state change reqs for preserving state
14 #
15
16 # The MCP2515 is a CAN Bus to SPI adapter from Microchip Technology,
17 # as used in the Goodthopter series of boards.  It requires a separate
18 # chip for voltage level conversion, such as the MCP2551.
19
20 import sys, time, string, cStringIO, struct, glob, os;
21
22 from GoodFETSPI import GoodFETSPI;
23
24 class GoodFETMCPCAN(GoodFETSPI):
25     """This class uses the regular SPI app to implement a CAN Bus
26     adapter on the Goodthopter10 hardware."""
27     MCPMODES=["Normal","Sleep","Loopback","Listen-only","Configuration",
28               "UNKNOWN","UNKNOWN","PowerUp"];
29     
30     def MCPsetup(self):
31         """Sets up the ports."""
32         self.SPIsetup();
33         self.MCPreset(); #Reset the chip.
34         # We're going to set some registers, so we must be in config
35         # mode.
36         self.MCPreqstatConfiguration();
37         
38         # If we don't enable promiscous mode, we'll miss a lot of
39         # packets.  It can be manually disabled later.
40         #self.poke8(0x60,0xFF); #TODO Does this have any unpleasant side effects?
41         self.poke8(0x60,0x66); #Wanted FF, but some bits are reserved.
42
43     #Array of supported rates.
44     MCPrates=[10.4, 41.6, 83.3, 100, 125, 250, 500, 1000];
45     
46     def MCPreset(self):
47         """Reset the MCP2515 chip."""
48         self.SPItrans([0xC0]);
49     
50       
51     ################    SETTING BAUD RATE   ################
52     
53     
54     def MCPsetrate(self,rate=125):
55         """Sets the data rate in kHz."""
56         # Now we need to set the timing registers.  See chapter 5 of
57         # the MCP2515 datasheet to get some clue as to how this
58         # arithmetic of this works.
59
60         
61         #STORE the prior status
62         oldstatus=self.MCPcanstatint();
63         print "Setting rate of %i kHz." % rate;
64         #print "Current state is %s." % self.MCPcanstatstr();
65         self.MCPreqstatConfiguration();
66         # print "Switched to %s state." % self.MCPcanstatstr();
67         
68         
69         if rate>41 and rate<42:
70             # NOT CHECKED: based on kvaser website.
71             # Sets baud rate for 41.6 kbps
72             CNF1=0x8e;
73             CNF2=0xa3;
74             CNF3=0x05;
75         elif rate>10 and rate<11:
76             # NOT CHECKED: based on kvaser website.
77             # Sets baud rate for 10.4 kbps
78             CNF1=0xbb;
79             CNF2=0xa3;
80             CNF3=0x05;  
81         elif rate==125:
82             #125 kHz, 16 TQ, not quite as worked out above.
83             CNF1=0x04;
84             CNF2=0xB8;
85             CNF3=0x05;
86         elif rate==100:
87             #100 kHz, 20 TQ
88             CNF1=0x04;
89             CNF2=0xBA;
90             CNF3=0x07;
91         elif rate>83 and rate<83.5:
92             #83+1/3 kHz, 8 TQ
93             # 0.04% error from 83.30
94             CNF1=0x0E;
95             CNF2=0x90;
96             CNF3=0x02;
97         elif rate==250:
98             #256 kHz, 20 TQ
99             CNF1=0x01;
100             CNF2=0xBA;
101             CNF3=0x07;
102         elif rate==500:
103             #500 kHz, 20 TQ
104             CNF1=0x00;
105             CNF2=0xBA;
106             CNF3=0x07;
107         elif rate==1000:
108             #1,000 kHz, 10 TQ
109             CNF1=0x00;
110             CNF2=0xA0;
111             CNF3=0x02;
112             print "WARNING: Because of chip errata, this probably won't work."
113         else:
114             print "Given unsupported rate of %i kHz." % rate;
115             print "Defaulting to 125kHz.";
116             CNF1=0x04;
117             CNF2=0xB8;
118             CNF3=0x05;
119         self.poke8(0x2a,CNF1);
120         self.poke8(0x29,CNF2);
121         self.poke8(0x28,CNF3);
122
123         # and now return to whatever state we were in before
124         self.MCPreqstat(oldstatus);
125         #print "Reverted to %s." % self.MCPcanstatstr();
126
127     
128     #################   STATE MANAGEMENT   ##################
129
130     def MCPcanstat(self):
131         """Get the CAN Status."""
132         return self.peek8(0x0E);
133     def MCPcanstatstr(self):
134         """Read the present status as a string."""
135         opmod=self.MCPcanstatint();
136         return self.MCPMODES[opmod];
137     def MCPcanstatint(self):
138         """Read present status as an int."""
139         return self.MCPcanstat()>>5;
140
141             
142     def MCPreqstat(self, state):
143         """Set the CAN state."""
144         if state==0:
145             self.MCPreqstatNormal();
146         elif state==1:
147             self.MCPreqstatSleep();
148         elif state==2:
149             self.MCPreqstatLoopback();
150         elif state==3:
151             self.MCPreqstatListenOnly();
152         elif state==4:
153             self.MCPreqstatConfiguration();
154         else:
155             print "Invalid state entered; defaulting to Listen-Only"
156             self.MCPreqstatListenOnly();
157     def MCPreqstatNormal(self):
158         """Set the CAN state."""
159         state=0x0;
160         self.MCPbitmodify(0x0F,0xE0,(state<<5));
161     def MCPreqstatSleep(self):
162         """Set the CAN state."""
163         state=0x1;
164         self.MCPbitmodify(0x0F,0xE0,(state<<5));
165     def MCPreqstatLoopback(self):
166         """Set the CAN state."""
167         state=0x2;
168         self.MCPbitmodify(0x0F,0xE0,(state<<5));
169     def MCPreqstatListenOnly(self):
170         """Set the CAN state."""
171         state=0x3;
172         self.MCPbitmodify(0x0F,0xE0,(state<<5));
173     def MCPreqstatConfiguration(self):
174         """Set the CAN state."""
175         state=0x4;
176         self.MCPbitmodify(0x0F,0xE0,(state<<5));
177     
178     ####################     RX MANAGEMENT     #####################
179     
180     def MCPrxstatus(self):
181         """Reads the RX Status by the SPI verb of the same name."""
182         data=self.SPItrans([0xB0,0x00]);
183         return ord(data[1]);
184
185     def MCPreadstatus(self):
186         """Reads the Read Status by the SPI verb of the same name."""
187         #See page 69 of the datasheet for the flag names.
188         data=self.SPItrans([0xA0,0x00]);
189         return ord(data[1]);
190             
191     def readrxbuffer(self,packbuf=0):
192         """Reads the RX buffer.  Might have old data."""
193         data=self.SPItrans([0x90|(packbuf<<2),
194                             0x00,0x00, #SID
195                             0x00,0x00, #EID
196                             0x00,      #DLC
197                             0x00, 0x00, 0x00, 0x00,
198                             0x00, 0x00, 0x00, 0x00
199                             ]);
200         
201         return data[1:len(data)];
202     
203     def fastrxpacket(self):
204         return self.readrxbuffer(0);
205     
206     def rxpacket(self):
207         """Reads the next incoming packet from either buffer.
208             Returns None immediately if no packet is waiting."""
209         status=self.MCPrxstatus()&0xC0;
210         if status&0x40:
211             #Buffer 0 has higher priority.
212             return self.readrxbuffer(0);
213         elif status&0x80:
214             #Buffer 1 has lower priority.
215             return self.readrxbuffer(1);
216         else:
217             return None;
218
219     #################      TX MANAGEMENT    ##################
220             
221     def MCPrts(self,TXB0=False,TXB1=False,TXB2=False):
222         """Requests to send one of the transmit buffers."""
223         flags=0;
224         if TXB0: flags=flags|1;
225         if TXB1: flags=flags|2;
226         if TXB2: flags=flags|4;
227         
228         if flags==0:
229             print "Warning: Requesting to send no buffer.";
230         self.SPItrans([0x80|flags]);
231     
232     def writetxbuffer(self,packet,packbuf=0):
233         """Writes the transmit buffer."""
234         
235         self.SPItrans([0x40|(packbuf<<1)]+packet);
236
237    
238     def txpacket(self,packet):
239         """Transmits a packet through one of the outbound buffers.
240         As usual, the packet should begin with SIDH.
241         For now, only TXB0 is supported."""
242
243         self.writetxbuffer(packet,0);
244         self.MCPrts(TXB0=True);
245
246                 
247     ###############   UTILITY FUNCTIONS  #################
248      
249     def simpleParse(self,packet):
250         """ 
251         This will print a simple parsing of the data with the standard Id and the extended id parsed.
252         """
253         dataPt = ord(packet[0]);
254         dataPt2 = ord(packet[1]);
255         # check if we have a standard frame, the msb of the second
256         # nibble will be 1. otherwise it is an extended rame
257         stdCheck = dataPt2 & 0x0f
258         if( stdCheck == 16 ):
259             #arb id is first byte + 3 msb of the 2nd byte
260             dataPt = dataPt<<3 | dataPt2>>5
261             print "Standard Packet \n Arb ID: "+("%d"%dataPt)
262         else:
263             #arb id is first byte + 3 msb + 2 lsb of 2nd byte +
264             # 3rd byte + 4th byte
265             dataPt = dataPt<<3 | dataPt2>>5
266             dataPt = dataPt<<2 | (dataPt2 & 0x03)
267             dataPt = dataPt<<8 | ord(packet[2])
268             dataPt = dataPt<<8 | ord(packet[3])
269             print "Extended Data Frame \n Arb ID: "+("%d"%dataPt)
270         
271         #find the dataLength
272         dataPt5 = packet[4]
273         dataLength = dataPt5 & 0x0e
274         #print "Data Length: "+("%d"%dataLength)
275         # Print the data packet
276         #print "Data:"
277         # Temporary, until correct packets are read
278         if( dataLength > 8 ):
279             dataLength = 8
280         toprint = self.pcket2str(packet[5:12])
281         print toprint
282         # For reading correct packets
283        # if (dataLength > 8 ):
284         #    print "Acceptable Length Exceeded"
285             # Data length value should never exceed 8
286        # else:
287        # toprint =  self.pcket2str(packet[5:(5+dataLength)])
288        # print toprint
289
290             
291     def packet2str(self,packet):
292         """Converts a packet from the internal format to a string."""
293         toprint="";
294         for bar in packet:
295             toprint=toprint+("%02x "%ord(bar))
296         return toprint;
297     
298     
299     
300     def packet2parsed(self,data):
301         """
302         This method will parse the packet that was received via L{rxpacket}.
303         
304         @type data: List
305         @param data: data packet read off of the MCP2515 from the CAN bus. format will be
306                      14 bytes where each element is a character whose unicode integer value corresponds
307                      to the hex value of the byte (use the ord() method).
308         
309         @rtype: Dictionary
310         @return: Dictionary of the packet parsed into various components. The key values will be as follows
311                      
312                      1. ide : 1 if the message is an extended frame. 0 otherwise
313                      2. eID : extended ID. Not included if not an extended frame
314                      3. rtr : 1 if the message is a remote transmission request (RTR)
315                      4. sID : Standard ID. 
316                      5. length: packet length (between 0 and 8)
317                      6. db0 : Data byte 0 (not included if RTR message)
318                          
319                          ---
320                      7. db7 : Data byte 7 (not included if RTR message)
321         """
322         dp1 = ord(data[0])
323         dp2 = ord(data[1])
324         dp5 = ord(data[4])
325         
326         packet = {}
327         #get the ide bit. allows us to check to see if we have an extended
328         #frame
329         packet['ide'] = (dp2 & 0x0f)>>3
330         #we have an extended frame
331         if( packet['ide'] != 0):
332             #get lower nibble, last 2 bits
333             eId = dp2 & 0x03
334             eId = eId<<8 | ord(data[2])
335             eId = eId<<8 | ord(data[3])
336             rtr = dp5>>6 & 0x01
337             packet['eID'] = " eID: %06d" %(eId)
338             packet['rtr'] = " rtr: %d" % (rtr)
339     
340         else:
341             packet['rtr'] = dp2>>4 & 0x01
342             
343         
344         # Create the standard ID. from the message
345         packet['sID'] = dp1<<3 | dp2>>5
346         packet['length'] = dp5 & 0x0f
347         
348         #generate the data section
349         for i in range(0,packet['length']):
350             idx = 5 + i
351             dbidx = 'db%d'%i
352             packet[dbidx] = ord(data[idx])
353         return packet
354     
355     def packet2parsedstr(self,data):
356         """
357         This will return a string that is the parsed CAN message. The bytes will be parsed
358         for the standard ID, extended ID (if one is present), rtr, length and databytes (if present).
359         The message will be placed into a string as decimal integers not hex values. This method 
360         calls L{packet2parsed} to do the packet parsing.
361         
362         @type data: List
363         @param data: Data packet as returned by the L{rxpacket}
364         @rtype: String
365         @return: String that shows the data message in decimal format, parsed.
366         """
367         packet = self.packet2parsed(data)
368         msg = "sID: %04d" %packet['sId']
369         if( packetParsed.get('eID')):
370             msg += " eID: %d" %packetParsed.get('eID')
371         msg += " rtr: %d"%packetParsed['rtr']
372         length = packetParsed['length']
373         msg += " length: %d"%length
374         msg += " data:"
375         for i in range(0,length):
376             dbidx = 'db%d'%i
377             msg +=" %03d"% ord(packetParsed[dbidx])  # could change this to HEX
378         #msg = self.client.packet2parsedstr(packet)
379         return msg
380         
381         
382     def peek8(self,adr):
383         """Read a byte from the given address.  Untested."""
384         data=self.SPItrans([0x03,adr&0xFF,00]);
385         return ord(data[2]);
386     def poke8(self,adr,val):
387         """Poke a value into RAM.  Untested"""
388         self.SPItrans([0x02,adr&0xFF,val&0xFF]);
389         newval=self.peek8(adr);
390         if newval!=val:
391             print "Failed to poke %02x to %02x.  Got %02x." % (adr,val,newval);
392             print "Are you not in idle mode?";
393         return val;
394     def MCPbitmodify(self,adr,mask,data):
395         """Writes a byte with a mask.  Doesn't work for many registers."""
396         data=self.SPItrans([0x05,adr&0xFF,mask&0xFF,data&0xFF]);
397         return ord(data[2]);
398
399 ############### DEBUG UTILITIES: STATUS MESSAGES ##############
400
401 #    def MCPrxstatusmessage(self):
402 #        """Returns string indicating status of RX buffers"""
403 #        status = self.MCPrxstatus();
404 #
405 #        if status < 64:
406 #            print "No RX message";
407 #        elif status < 128:
408 #            print "Message in RXB0";
409 #        elif status < 192:
410 #            print "Message in RXB1";
411 #        else:
412 #            print "Messages in both buffers";
413
414
415
416 # TXB0TRL = 0x30
417 # RXB0CTRL = 0x60
418 # CNF3/2/1 = 0x28, 29, 2a
419 # CANINTE = 0x2b
420 # CANINTF = 0x2c
421 # TXRTSCTRL = x0D
422
423
424