The reason I am speculating this is that it is a standard ASAM spec, I have implemented it on some of our motorsports ECU's for the boxes that do not have ethernet comms.
Here is a sample header for a CCP protocol
- Code: Select all
//Command Codes sent from Master
#define CMD_CONNECT 0x01
#define CMD_SET_MTA 0x02
#define CMD_DNLOAD 0x03
#define CMD_UPLOAD 0x04
#define CMD_TEST 0x05
#define CMD_START_STOP 0x06
#define CMD_DISCONNECT 0x07
#define CMD_START_STOP_ALL 0x08
#define CMD_GET_ACTIVE_CAL_PAGE 0x09
#define CMD_SET_S_STATUS 0x0C
#define CMD_GET_S_STATUS 0x0D
#define CMD_BUILD_CHKSUM 0x0E
#define CMD_SHORT_UP 0x0F
#define CMD_CLEAR_MEMORY 0x10
#define CMD_SELECT_CAL_PAGE 0x11
#define CMD_GET_SEED 0x12
#define CMD_UNLOCK 0x13
#define CMD_GET_DAQ_SIZE 0x14
#define CMD_SET_DAQ_PTR 0x15
#define CMD_WRITE_DAQ 0x16
#define CMD_EXHANGE_ID 0x17
#define CMD_PROGRAM 0x18
#define CMD_MOVE 0x19
#define CMD_GET_CCP_VERSION 0x1B
#define CMD_DIAG_SERVICE 0x20
#define CMD_ACTION_SERVICE 0x21
#define CMD_PROGRAM_6 0x22
#define CMD_DNLOAD_6 0x23
The thing of interest is that current mode "23" seems to share the same hex id for CCP command CMD_DNLOAD_6, which I use in my tools to fetch up to 6 bytes worth of data.
- Code: Select all
void ccp_download6(U8 mbi)
{
U8 i=0;
if(ccp_state.connected==0)
return;
while(i<6)
{
*ccp_state.mta_addr_0++ = message[mbi].data[2+i++];
}
ccp_return_ack_mTA0(ccp_state.ctr, CRC_ACK, (U32)ccp_state.mta_addr_0);
}
Being that this is wrapped on top of the 15676 Layer, I am going to assume that I may be able to try some of the other commands.
The openport 2.0 logger is currently spamming a request for data values using command 23. I have heard that the data limitation rate is held up because the EVO's ECU only services the can command on a periodic 10ms task ( Which is a great way of gating the user from sending too many commands, and making the RTOS suffer due to a long list of ISR's to handle)
This is where I wish to suggest a better way for gathering this data. Most all CCP programs have a model called DAQ, in that during the start of a session, you specify a bin, pointer, and data size. This is done to completion until you build your "DAQ" header. There are usally 4-5 different rates, the ecu holds the list, and once after setup you have completly filled everything that you need, you set the daq to "RUN" and watch the CAN messages fly by. There are periodic respsonse, which will come bundles at a much faster rate than waiting for mode 23 to return data on its periodic task.
- Code: Select all
void ccp_set_daq_ptr(U8 mbi)
{
if(ccp_state.connected==0)
return;
if((message[mbi].data[2]<=ODT_COUNT) & (message[mbi].data[3]<ODT_SIZE) & (message[mbi].data[4]<7))
{
ccp_state.n_write_daq = message[mbi].data[2];
ccp_state.n_write_odt = message[mbi].data[3];
ccp_state.n_write_odt_element = message[mbi].data[4];
ccp_return_ack(ccp_state.ctr,CRC_ACK);
}
else
ccp_return_ack(ccp_state.ctr,CRC_PARAM_OUT_OF_RANGE);
}
void ccp_write_daq(U8 mbi)
{
struct ccp_state_struct
{
U8 * mta_addr_0;
U8 * mta_addr_1;
U32 station_addr;
U8 connected;
union {
U8 R;
struct {
U8 RUN:1;
U8 STORE:1;
U8 reserved_1:3;
U8 RESUME:1;
U8 DAQ:1;
U8 CAL:1;
} B;
} status;
U8 ctr;
U8 cpp_v_maj;
U8 cpp_v_min;
U8 start_daq;
U8 protection_status;
U8 protection_removed_que;
U8 n_write_daq;
U8 n_write_odt;
U8 n_write_odt_element;
union {
U32 R;
struct {
U32 S3:8;
U32 S2:8;
U32 S1:8;
U32 S0:8;
}B;
}seed;
union {
U8 R;
struct {
U8 reserved_2:1;
U8 PGM:1;
U8 reserved_1:4;
U8 DAQ:1;
U8 CAL:1;
} B;
} res_avail;
union {
U8 R;
struct {
U8 reserved_2:1;
U8 PGM:1;
U8 reserved_1:4;
U8 DAQ:1;
U8 CAL:1;
} B;
} res_prot;
};
struct odt_list_struct
{
struct odt_struct
{
U32 ptr[7];
U8 len[7];
U8 used;
} odt[ODT_SIZE];
U8 event;
U8 start_stop;
U16 prescaler;
U8 list_used;
U32 eid_can;
};
#ifdef E1_INCA_CCP_ODP_ENABLED
U32 addr;
U8 len=0;
len = message[mbi].data[2];
addr = ((U32)message[mbi].data[4]<<24) + ((U32)message[mbi].data[5]<<16) + ((U32)message[mbi].data[6]<<8) + ((U32)message[mbi].data[7]);
if((addr>=0x40000000 ) & (addr<=0x40020000 ))
{
CCP_ODT[ccp_state.n_write_daq].odt[ccp_state.n_write_odt].len[ccp_state.n_write_odt_element] = len;
CCP_ODT[ccp_state.n_write_daq].odt[ccp_state.n_write_odt].ptr[ccp_state.n_write_odt_element] = addr;
//if(ccp_state.n_write_odt_element>CCP_ODT[ccp_state.n_write_daq].odt[ccp_state.n_write_odt].used)
// {
// CCP_ODT[ccp_state.n_write_daq].odt[ccp_state.n_write_odt].used = ccp_state.n_write_odt_element;
//}
CCP_ODT[ccp_state.n_write_daq].odt[ccp_state.n_write_odt].used++;
ccp_return_ack(ccp_state.ctr,CRC_ACK);
}
else
ccp_return_ack(ccp_state.ctr,CRC_PARAM_OUT_OF_RANGE);
#endif
}
void ccp_start_stop_all(U8 mbi)
{
if(message[mbi].data[2]==CCP_DAQ_START)
{
#ifdef E1_INCA_CCP_ODP_ENABLED
ccp_state.start_daq = CCP_DAQ_START;
CCP_ODT[0].start_stop = CCP_DAQ_START;
CCP_ODT[1].start_stop = CCP_DAQ_START;
CCP_ODT[2].start_stop = CCP_DAQ_START;
CCP_ODT[3].start_stop = CCP_DAQ_START;
#endif
ccp_return_ack(ccp_state.ctr, CRC_ACK);
}
else if(message[mbi].data[2]==CCP_DAQ_STOP)
{
ccp_state.start_daq = CCP_DAQ_STOP;
#ifdef E1_INCA_CCP_ODP_ENABLED
CCP_ODT[0].start_stop = CCP_DAQ_STOP;
CCP_ODT[1].start_stop = CCP_DAQ_STOP;
CCP_ODT[2].start_stop = CCP_DAQ_STOP;
CCP_ODT[3].start_stop = CCP_DAQ_STOP;
#endif
ccp_return_ack(ccp_state.ctr, CRC_ACK);
}
else
ccp_return_ack(ccp_state.ctr, CRC_PARAM_OUT_OF_RANGE);
}
I LOVE what you have done, but I am going to write some software this weekend and see if I can successfully interface with the DAQ setup , and try to get a session running
For some people, having their data @ 10HZ is fine, but in motorsports we like to run closer to the 100-500Hz range on some channels.
I am also close to getting my Methanl Pump Controller running on my car. It runs of CAN's and currently mode 23's in to the car to pick up Boost Pressure. If anyone is willing to test it on their setup, please PM and maybee we can get some more experience with it.