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