CircuitGizmos

Electronic Products for Creative Minds

  • Home
  • Gizmo Store/Products
  • Documentation/Datasheets
  • Projects/Libraries
  • GizmoBlog
  • Services/Contact
    • Design
    • Contact Gizmo!
  • Your Gizmo Cart
    • Your Account
You are here: Home / GizmoBlog / MMBasic Language / Input and Output / CAN (Controller Area Network)

CAN (Controller Area Network)

These commands provide access to the CAN hardware available on the CGCOLORMAX2.

A CAN module supports up to 32 channels, each channel can be configured to transmit or receive data. For receiving data the CAN module writes received messages into a different FIFO buffer for each channel. One entry in a FIFO buffer is 16 bytes long (4 address bytes, 4 length bytes, 8 data bytes).

These commands allow the user to specify the number of records in each FIFO buffer and the internals keep track of the memory requirements and automatically allocates memory from the available heap. A command is provided to free the allocated memory when the CAN interface is no longer required.

Note that CAN channels are numbered from 0 to 31 and that, per the Microchip documentation, CAN channels should be configured contiguously from 0 upwards. I.e. if 5 channels are required use channels 0, 1, 2, 3 & 4; and not 1, 2, 3, 4 & 5 or 3, 5, 10, 22, 31 or any other combination.

The CAN module is first configured by placing it in configuration mode and then issuing configuration commands. When the desired configuration is set up then the CAN module is enabled. Once enabled the RX and TX commands can be used to receive and send data on the bus. Once the CAN module is finished it should be disabled and the memory associated with the FIFO buffers should be freed (for example at the end of the application)

An RX channel can either have a filter for a specific id or be configured to receive all messages. In the latter case the user can request the id of the received command and then perform processing based on the id of the command. However, on a CAN bus with moderate to high utilization, more reliable operation will be achieved by setting up multiple channels with each channel filtering for one id of interest. This is because of the relatively small buffer sizes and the relatively slow operation of the BASIC interpreter – when trying to receive all messages into one buffer the buffer overruns and data is lost. Furthermore, when a channel is being filtered for a single ID it may be appropriate to set the FIFO buffer size to just 1 record, especially if the goal is to update a single value display. This will always give you the most up-to-date data.

The examples provided were designed for (and tested on) a second generation Toyota Prius (Model years 2004 – 2009).

Throughout this overview “CAN commands” have been referred to in the plural. In fact there is only one true CAN command – the other commands are “sub-commands” from this one master. By structuring the commands this way there is less impact on the limitation for the number of top-level/master commands that MMBasic can support.

EXAMPLE ONE – Minimal example of reading one channel

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
' (c) John Harding, 2012
' Example designed for Gen 2 Prius
' Configures connection speed to 500kbps and monitors for CAN id 52Ch when data is received 
' we calculate the ECT from the appropriate data bytes.
' Note that the period of this message is approximately 1Hz
Cls
Dim ok
Dim data(8)
Dim ect
CAN CONFIG ok
CAN SETSPEED 500000, ok
CAN ADDRXCHNL 0,&h52C,0,1,ok
CAN ENABLE ok
Timer = 0
 
Do
  If (Inkey$ = "q") Then Exit
  CAN RX 0,data(0),ok
  If (ok=1) Then
    Print Timer ": ECT = " (data(1)/2)
  EndIf
Loop
 
CAN FREE
End

EXAMPLE TWO – Example of reading all channels and displaying just one (but don’t do this!)

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
' (c) John Harding, 2012
' Example designed for Gen 2 Prius
' Configures connection speed to 500kbps and configures a channel to receive all messages into a FIFO buffer with 32 records.
' When a message with id 52Ch is received we calculate the ECT from the appropriate data bytes.
' Note that the period of this message is approximately 1Hz but that we receive many wrong ids before we get the message we want
' This example is provided to contrast with example 1. It is suggested to use example 1 as the basis for your code.
Dim ok
Dim data(8)
Dim id
Dim typ
Dim length
CAN CONFIG ok
CAN SETSPEED 500000, ok
CAN ADDRXCHNL 0, 0, 0, 32, ok
CAN ENABLE ok
Timer=0
 
Do
  q$ = Inkey$
  If (q$="q") Then Exit
  CAN RX 0, id, typ, length, data(0), ok
  If (ok=1) Then
    If (id=&H52C) Then
      Print " "
      Print Timer ": " Hex$(id) " : " length " : ECT= " data(1) / 2 " C"
    Else
      Print Timer ": " Hex$(id) " ";
    Endif
  EndIf
Loop
 
CAN FREE
End

EXAMPLE THREE – Reading manufacturer specific PID’s on a Toyota Prius 2005.

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
' Example to query PIDs for the battery ECU and convert the received data into engineering values
' JDH 10/8/12
Dim ok
Dim txID : Dim txData(8) : Dim txLen
Dim rxID : Dim rxData(8)
txData(0) = 0 : txData(1) = 0 : txData(2) = 0 : txData(3) = 0
txData(4) = 0 : txData(5) = 0 : txData(6) = 0 : txData(7) = 0
txLen = 8 ' always transmit 8 bytes even though payload may be less
txID = &H7E3 ' Battery ECU module
rxID = txID + 8 ' id of reply is 8 higher than module number
 
' Check the "ok" result, if it fails print a message and exit
Sub checkOK(okay, failed$, succeeded$, xit)
  If (okay=0) Then
    Print failed$
    If (xit=1) Then Exit
  Else
    Print succeeded$
  EndIf
End Sub
 
' print formatted hex numbers
Function toHex$(val)
  toHex$=""
  If (val<16) Then toHex$="0"
  toHex$ = toHex$ + Hex$(val)
End Function
 
' print timer : id : len : data
Sub PrintRawData
  Print Timer ": " Hex$(rxId) " : " rxLen " : " toHex$(rxData(0)) " ";
  Print toHex$(rxData(1)) " " toHex$(rxData(2)) " " toHex$(rxData(3)) " ";
  Print toHex$(rxData(4)) " " toHex$(rxData(5)) " " toHex$(rxData(6)) " ";
  Print toHex$(rxData(7))
End Sub
 
' make the PID request
Sub SendModeAndPID(mode, pid)
  Local txOK
  txData(0) = 3
  txData(1) = mode
  txData(2) = pid
  CAN TX 0,txID,0,txLen,txData(0),txOk
  checkOK(txOK, "FAILED TO SEND PID", "SENT PID " + Hex$(pid) + " TO " + Hex$(txId), 0)
End Sub
 
' Send the acknowledgement after receiving frame
Sub SendReadyForMore()
  Local txOK
  txData(0) = &H30
  txData(1) = 0
  txData(2) = 0
  CAN TX 0,txID,0,txLen,txData(0),txOk
  checkOK(txOK, "FAILED TO SEND PID", "SENT 0x30 TO " + Hex$(txId), 0)
End Sub
 
 
' Chains together the initial PID request and the acknowledgement
Sub RequestPID(mode, pid)
' Note, we're "cheating" here and simply waiting 10msec and sending the
' acknowlegement. Strictly speaking we should wait for the response
' header first (frame 0x10). This works because in the CAN setup (below)
' we've setup a big enough buffer to receive all the frames from the
' longest PID.
  SendModeAndPID(mode, pid)
  Pause 10
  SendReadyForMore
End Sub
 
Sub DisplayD0
' retrieve the data (see DisplayCE for commentary)
  RequestPID &H21,&HD0
  Do
    CAN RX 1, rxData(0), ok
  Loop Until (ok=1)
  PrintRawData
  Local b(rxData(1)+15)
  Local j
  Local i
  j=0
  For i=4 To 7
    b(j)=rxData(i)
    j=j+1
  Next i
  Do
    CAN RX 1, rxData(0), ok
    If (ok=1) Then
      PrintRawData
      For i=1 To 7
        b(j)=rxData(i)
        j=j+1
      Next i
    EndIf
  Loop Until (ok=0)
  
  ' Convert to engineering units (see DisplayCE for commentary)
  Print "Block Count = " b(0)
  Print "Time in LOW = " (256*b(1)+b(2))
  Print "Time in DC Inhibit = " (256*(b3)+b(4))
  Print "Time in HIGH = " (256*(b5)+b(6))
  Print "Time in HOT = " (256*(b7)+b(8))
  Print "Lowest Block = " b(9)
  Print "Lowest Voltage = " (2.56*b(10)+0.1*b(11)-327.68)
  Print "Highest Block = " b(12)  
  Print "Highest Voltage = " (2.56*b(13)+0.1*b(14)-327.68)
  For i=15 To 28
    Print "Block " toHex$(i-14) " Resistance = " (0.001*b(i)) " Ohms"
  Next i
End Sub
 
Sub DisplayCF
  ' retrieve the data (see DisplayCE for commentary)
  RequestPID &H21,&HCF
  Do
    CAN RX 1, rxData(0), ok
  Loop Until (ok=1)
  PrintRawData
  Local b(rxData(1)+15)
  Local j
  Local i
  j=0
  For i=4 To 7
    b(j)=rxData(i)
    j=j+1
  Next i
  Do
    CAN RX 1, rxData(0), ok
    If (ok=1) Then
      PrintRawData
      For i=1 To 7
        b(j)=rxData(i)
        j=j+1
      Next i
    EndIf
  Loop Until (ok=0)
 
  ' Convert to engineering units (see DisplayCE for commentary)
  Print "Air Intake Temp = " (4.608*b(0)+0.018*b(1)-557.824) " F"
  Print "Fan Motor Voltage = " (0.2*b(2) - 25.6) " V"
  Print "Aux Batt Voltage = " (0.2*b(3) - 25.6) " V"
  Print "Battery Charge = " (b(4) - 64)
  Print "Battery Discharge = " (b(5) - 64)
  Print "Delta SOC = " (0.01 * b(6)) " %"
  Print "Fan Speed = " b(8)
  Print "Batt Temp 1 = " (4.608*b(10)+0.018*b(11)-557.824) " F"
  Print "Batt Temp 2 = " (4.608*b(12)+0.018*b(13)-557.824) " F"
  Print "Batt Temp 3 = " (4.608*b(14)+0.018*b(15)-557.824) " F"
End Sub
 
Sub DisplayCE
  ' send the request
  RequestPID &H21,&HCE
  ' retrieve the first frame
  Do
    CAN RX 1, rxData(0), ok
  Loop Until (ok=1)
  PrintRawData
  ' create an array big enough to hold all the data
  ' the +15 is because there always seems to be 1 more frame than expecte
  ' and if the amount of data is 1 larger than a multiple of 8 we need 7
  ' bytes for the remainder of that frame + 8 bytes for the "extra" frame
  Local buffer(rxData(1)+15)
  ' Copy the data from the first frame into the buffer
  Local i
  Local j
  j=0
  For i=4 To 7
    buffer(j)=rxData(i)
    j=j+1
  Next i
  ' now process the remaining frames
  Do
    CAN RX 1, rxData(0), ok
    If (ok=1) Then
      PrintRawData
      For i=1 To 7
        buffer(j)=rxData(i)
        j=j+1
      Next i
    EndIf
  Loop Until (ok=0)
  
  ' We have the raw data, now convert it to engineering values
  ' These calculations are from USBSeaWolf's spreadsheet available on the
  ' PriusChat.com forum.
  Print "SOC = " (0.5 * buffer(0)) " %"
  Print "Current = " (2.56*buffer(1)+0.1*buffer(2)-327.68) " A"
  j=3
  For i=1 To 14
    Print "Block " toHex$(i) " = " (2.56*buffer(j)+0.1*buffer(j+1)-327.68) " V"
    j=j+2
  Next i
End Sub
 
''''''''''''''''''''''''''
' START HERE...
Cls
CAN CONFIG ok : checkOK(ok, "CAN CONFIG FAILED", "", 1)
CAN SETSPEED 500000, ok : checkOK(ok, "CAN SETSPEED FAILED", "", 1)
CAN ADDTXCHNL 0,1,ok : checkOK(ok, "CAN ADDTXCHNL FAILED", "", 1)
CAN ADDRXCHNL 1,rxId,0,10,ok : checkOK(ok, "CAN ADDRXCHNL FAILED", "", 1)
CAN ENABLE ok : checkOK(ok, "CAN ENABLE FAILED", "", 1)
CAN PRINTCONFIG
' Note the above configuration for channel 1
' (a) we're filtering on the response ID (which is the moduleID + 8)
' (b) we create a 10 record buffer this allows us to capture all the response
' frames in one go
Print "q:quit, t:0xCE, u:0xCF, v:0xD0 send pid to " Hex$(txID)
Timer = 0
Do
  k$ = Inkey$
  If (k$ = "q") Then Exit
  If (k$ = "t") Then DisplayCE
  If (k$ = "u") Then DisplayCF
  If (k$ = "v") Then DisplayD0
  CAN RX 1, rxData(0), ok
  If (ok=1) Then PrintRawData
Loop
CAN FREE

CAN-in-Automation connector pin out.

There are several types of connectors used with CAN. Illustrated here is a standard using a 9-pin D-sub connector. This follows the CiA DS-102 pin out.

 

Pin Signal Description
1
2 CAN_L CAN_L bus line
3 CAN_GND CAN ground
4
5 (CAN_SHLD) CAN shield (optional)
6 (GND) Ground (optional)
7 CAN_H CAN_H bus line
8
9 (CAN_V+) CAN positive supply (optional)

 

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

A Ton of Documentation

Open all | Close all

Projects made by Gizmo, Friends, and Members

open all | close all

Recent GizmoBlog Musings

  • Altair 8800 using a ColorMax!
  • Re-energizing the ColorMax, Pt. 3

Visit us!

  • Facebook

Electronic Products for Creative Minds

CircuitGizmos is your source for electronic products that help you create your embedded projects. Here at CircuitGizmos.com you will find a friendly store filled with creative products and all of the documentation that you need to use these gizmos.

We create devices that we believe make electronics fun, but we also know that our products are used for professional designs. For decades we have designed products for commercial, military, and medical industries. Our gizmos here are great for engineers and hobbyists alike.

Copyright © 2008+ CircuitGizmos, L.L.C. All rights reserved

Image already added

Recently Viewed Products

Copyright © 2021 · Generate Pro Theme on Genesis Framework · WordPress · Log in