Megasquirt 2 CAN system

(Note: CAN is not available on the first batch of MS2 cards produced before August 2005 and is not available on MS1 at all.)

CAN node to CAN node (e.g. MS2 to GPIO communications)

The CAN system in Megasquirt 2 primarily allows the transport of data from a memory block in one device to a memory block in another device. An understanding of the concept of these memory blocks is therefore important and is described below. The CAN system also allows “pass through” of data where a serial “put” or “get” request is initiated by the tuning software and relayed out over the CAN network, this is described later in the document.

Within the Megasquirt 2 processor there a number of flash regions and a ram region. Within these areas are stored the firmware (program), the permanent copy of the tuning data, the working copy of the tuning data, a buffered copy of the realtime (gauge) data and internal variables for use by the program.

There is an array “tables[]” within the C code that defines the locations of certain core tables for tuning and also for use by the CAN system. These data blocks are also referred to as tables or pages - there is no special meaning attached to either term.

The block numbers presented here are correct for MS2 as at January 2008 and GPIO 1.041

In MS2 (+ MS2/Extra), block 7 is “outpc” (outputs-to-PC) the collection of realtime data that is displayed on the gauges in Megatune.

In GPIO 1.041, block 2 is “outpc” a similar collection of sensor data.

This worked example will show how MS2 code can send a request to the GPIO to get data from its ADC ports and store them into the MS2 memory.

The GPIO stores its adc data in block 2. This is determined as follows.

The data block / table / page descriptors are these:

const tableDescriptor tables[NO_TBLES] = {

{ (unsigned int *)&inpram, (unsigned int *)&in1flash, sizeof(inputs1) },

{ (unsigned int *)&txbuf, NULL, sizeof(txbuf) },

{ (unsigned int *)&outpc, NULL, sizeof(outpc) }, <--- block 2 is what we want to fetch from

{ (unsigned int *)msvar, NULL, sizeof(msvar) },

....

The outpc definition is:

typedef struct {

unsigned short seconds;

short adcval[NADC]; // adc values in engineering units <--- the data we want starts at offset 2

....

For MS2/Extra we want to store them into a space already reserved “outpc.gpioadc[]” which is in block 7.

// has a slightly different usage to MS2 2.8+

const tableDescriptor tables[NO_TBLES] = {

{

(unsigned int *)cltfactor_table,

(unsigned int *)cltfactor_table,

sizeof(cltfactor_table)

},

0

{

(unsigned int *)matfactor_table,

(unsigned int *)matfactor_table,

sizeof(matfactor_table)

},

1

{

(unsigned int *)egofactor_table,

(unsigned int *)egofactor_table,

sizeof(egofactor_table)

},

2

{

(unsigned int *)maffactor_table,

(unsigned int *)maffactor_table,

sizeof(maffactor_table)

},

3

{

(unsigned int *)&ram_data,

(unsigned int *)&flash4,

1024

},

4

{

(unsigned int *)&ram_data,

(unsigned int *)&flash5,

1024

},

5

{

(unsigned int *)&txbuf,

NULL,

sizeof(txbuf)

},

6

{

(unsigned int *)&outpc,

NULL,

sizeof(outpc)

},

7 <--- block 7 is what we want to deposit into

{

(unsigned int *)&ram_data,

(unsigned int *)&flash8,

1024

},

8

{

(unsigned int *)&ram_data,

(unsigned int *)&flash9,

1024

},

9

{

(unsigned int *)&ram_data,

(unsigned int *)&flash10,

1024

},

10

{

NULL,

NULL,

0

},

11

{

NULL,

NULL,

0

},

12

{

NULL,

NULL,

0

},

13

{

(unsigned int *)&Signature,

(unsigned int *)&Signature,

60

},

14

{

(unsigned int *)&RevNum,

(unsigned int *)&RevNum,

20

}

15



Diagrammatically we want to do this:




By default the MS2 will have CAN_ID of 0. The GPIO must have a different CAN_ID, this can be set in software but could typically be 1.

For the MS2 to collect remote data it sends a “MSG_REQ” command out on the CAN bus.


The data is packaged up into a CAN packet by the CAN hardware but for a basic user that “simply works” and need not be of great concern to the user or programmer on MC9S12 platforms. The real protocol sent down the wire includes checksums and other data, programmers porting to other processors should refer to http://www.megamanual.com/com/CAN.htm for the more technical details.

What is of interest to most is the data shown above and what it means.

cx_dest is the CAN id we are sending data to, in this case the GPIO, CAN id 1.

cx_destvarblk is the data block 2 as above

cx_destvaroff is the offset within that block which happens to also be 2 in this case.

cx_datbuf is not used in a request packet.

cx_myvarblk specifies what block the return packet should store data in. Block 7 is outpc on MS2.

cx_myvaroff specifies what offset within that block should be used. Offset 114 is where gpioadc[] appears within outpc (see megasquirt-ii.ini.ms2extra)

cx_varbyt is the number of bytes to be sent back. We want 8 which is 4 words i.e. the first four ADC channels.



What is critical to understand here is that the request packet contains the remote AND local addresses for the transfer. On receipt of the return packet the CAN receiving code stores the data where instructed. (The chances of having a “rogue” CAN device in your car is extremely unlikely, but this protocol allows the remote device to write into your memory map without authority.)

Having now sent this packet off on the network, hopefully the GPIO will pick it up, process it and issue a return packet.




cx_dest is the CAN id we are sending data to, in this case the MS2, CAN id 0.

cx_destvarblk is the block where data should be stored. It should be the original cv_myvarblk from the REQ packet, data block 7

cx_destvaroff is the offset where data should be stored. It should be the original cv_myvaroff from the REQ packet, offset 114

cx_datbuf contains the data requested. This will be stored direct into memory on receipt.

cx_myvarblk is not used in MSG_RSP

cx_myvaroff is not used in MSG_RSP

cx_varbyt is the number of bytes to be sent back. It should be the original cv_myvaroff from the REQ packet, 8

On receipt of this MSQ_RSP packet, the MS2 will then store the received data into memory as directed. In this case, because it was stored into outpc, it is then available for collection by Megatune down the serial line using conventional methods.

Example code

This code is taken from the mainloop of MS2/Extra at the end of December 2007. It performs the commands as above and fetches the first 4 ADC channels.

can[1].cx_msg_type[can[1].cxno_in] = MSG_REQ;

can[1].cx_dest[can[1].cxno_in] = flash4.can_poll_id; // send to GPIO device at user defined CAN id

can[1].cx_destvarblk[can[1].cxno_in] = 2; // fetch from block 2 - GPIO

can[1].cx_destvaroff[can[1].cxno_in] = 2; // fetch from adcs start at byte 2 - GPIO

can[1].cx_varbyt[can[1].cxno_in] = 8; // 8 bytes to be returned

// where should the resulting data be stored

can[1].cx_myvarblk[can[1].cxno_in] = 7; // store returned data in outpc

can[1].cx_myvaroff[can[1].cxno_in] = (unsigned short)(&outpc.gpioadc[0]) - (unsigned short)(&outpc); // this is offset of where to store it.

// For non C programmers, it takes the physical address of outpc.gpioadc[0] in memory and subtracts the address of outpc itself. This gives the offset within the block and works out to be 114 at the time of writing. Using the code here is more flexible than simply keying in the number.



// These next chunks of code need to be included to update counters and work with the other CAN routines. The same code section should be used for every mainloop usage of can.

// Where (in xmt ring buffer) to put next message

if(can[1].cxno_in < (NO_CANMSG - 1)) {

can[1].cxno_in++;

} else {

can[1].cxno_in = 0;

}

// increment counter

if(can[1].cxno < NO_CANMSG) {

can[1].cxno++;

} else {

can[1].cxno = NO_CANMSG;

}

if(!(CANTIER & 0x07)) {

// Following will cause entry to TxIsr without sending msg

// since when CANTIER = 0, CANTFLG left as buff empty(>0).

// If CANTIER has at least 1 int buf enabled, will enter

// TxIsr automatically.

CANTBSEL = CANTFLG;

CANTIER = CANTBSEL;

}



Weaknesses in protocol and possible solutions

One problem I observe in the protocol as it stands today is that the sender must know the hardware layout of the remote end, yet there isn't presently any way to exchange version information to determine what firmware lies at the other end. While this might not present any issues today as the CAN system is just about to to implemented, as time passes and new versions of software are created, if any of the structures such as outpc are re-arranged and a “well known” location is no-longer so then communications will fail. A proposal is to implement a pseudo table 255 as the format string. This is already used by tuning software on the serial port to determine the communications method. Adding this to the CAN system would allow similar version matching and at the very least incompatible firmware would be able to establish a problem instead of simply corrupting data.





CAN pass-through mode (e.g. laptop to GPIO communications via MS2)




In all recent MS2 codes, the serial communications are CAN-ready. The send and request messages include a CANid descriptor. In theory the serial connection may be made to any of the CAN devices in the network and it will handle remote or transparent accesses transparently. In practice we will usually be attaching the laptop to the serial port on the MS2 master and only making remote queries to other devices on the CAN network.

The key starting point is this line from the MS2 Megatune ini file.

pageIdentifier = "\x00\x04", "\x00\x05"

These identify the CANid and page numbers of the two tuning pages in MS2. The \x00 means send raw number 00, which is the CANid target. MS2 by default has CANid of zero, so this data is destined for MS2. The second number \x04 says that the first tuning page is block 04. The second tuning page is on CANid zero as well, but with block 05.

MS2/Extra looks like this:

pageIdentifier = "\x00\x04", "\x00\x05", "\x00\x07", "\x00\x08", "\x00\x09"

You will note that there are more tuning data blocks, but the principle is the same.



GPIO however, is similar to this:

pageIdentifier = "\x01\x00"

The \x01 means send raw number 01, which is the CANid target. GPIO by default has CANid of one, so this data is destined for GPIO. The second number \x00 says that the first tuning page is block 00.

A typical read tuning data request to MS2 would look like:

r 00 04 ........

and to GPIO:

r 01 00 ........

When the MS2 receives a serial command it checks the CANid. If this matches its own CANid then it processes the command locally, if it does not match then the CAN-pass-through mode is initiated. The message is relayed over the CAN network and the MS2 gets ready to receive the response. When the response comes it is then passed back over the serial line. Ideally, the tuning computer should be unaware whether the command was handled locally or remotely. However, there is one limitation of the CAN system that cannot be ignored - only eight data bytes can be transmitted at any one time. So the tuning software MUST know if this is a local or remote request and only ask or send a maximum of eight data bytes at one time for the remote transfers.

The realtime data (gauges) are handled in a similar way

From megasquirt-ii-mt250.ini

[OutputChannels]

nPages = 2

ochBlockSize = 112, 32

blockingFactor = 0, 8

pageIdentifier = "\x00\x06", "\x01\x00"

ochGetCommand = "a%2i", "r%2i%2o%2c"

A typical read tuning data request to MS2 would look like:

a 00 06 ........

and to GPIO:

r 01 02 ........

The “blockingFactor” line above recognises the requirement to only send/request eight bytes maximum via CAN.



At the time of writing, CAN pass-through is an area still under development, but it does work.





(c) James Murray 2007,2008. Last updated 13th Jan 08