189 lines
5.5 KiB
C++
189 lines
5.5 KiB
C++
/**
|
|
* @file serial_sensor.cpp
|
|
*
|
|
*
|
|
* @date Mar 19, 2020
|
|
* @author Konstantin Smola, (c) 2020
|
|
*/
|
|
|
|
#include "pch.h"
|
|
|
|
#if EFI_AUX_SERIAL
|
|
#include "serial.h"
|
|
#include "serial_sensor.h"
|
|
|
|
#define NUM_INNOVATE_O2_SENSORS 1
|
|
#define AFR_MULTIPLIER 147
|
|
|
|
volatile float InnovateLC2AFR = AFR_ERROR;
|
|
|
|
typedef enum
|
|
{
|
|
NO_ERROR = 0,
|
|
HEATER_SHORTED = 1,
|
|
HEATER_OPEN = 2,
|
|
PUMPCELL_SHORTED = 3,
|
|
PUMPCELL_OPEN = 4,
|
|
REFCELL_SHORTED = 5,
|
|
REFCELL_OPEN = 6,
|
|
SYSTEM_ERROR = 7,
|
|
SENSOR_TIMING_ERR = 8,
|
|
SUPP_V_LOW = 9
|
|
} sensor_error_code_t;
|
|
|
|
struct sensor_data_t {
|
|
int function_code;
|
|
float AFR;
|
|
float AFR_multiplier;
|
|
float lambda;
|
|
float warmup;
|
|
sensor_error_code_t error_code;
|
|
};
|
|
|
|
static sensor_data_t innovate_o2_sensor[NUM_INNOVATE_O2_SENSORS];
|
|
|
|
static size_t tmsglen;
|
|
|
|
void IdentifyInnovateSerialMsg() { //this identifies an innovate LC1/LC2 o2 sensor by it's first word (header)
|
|
if (engineConfiguration->enableInnovateLC2) {
|
|
if ((((ser_buffer[0]) & lc2_header_mask) != lc2_header_mask) && innovate_serial_id_state == IDENTIFIED) { //not serial header word
|
|
innovate_serial_id_state = UNKNOWN;
|
|
innovate_msg_len = 1;
|
|
sb = 0;
|
|
}
|
|
|
|
switch (innovate_serial_id_state) {
|
|
case UNKNOWN:
|
|
InnovateLC2AFR = AFR_ERROR;
|
|
// read one byte, identify with mask, advance and read next byte
|
|
if (((ser_buffer[0]) & lc2_header_mask) == lc2_header_mask) { // check if it's the first byte of header
|
|
// first byte identified, now continue reading and advance statemachine
|
|
innovate_serial_id_state = HEADER_FOUND;
|
|
innovate_msg_len = 1;
|
|
sb = 1;
|
|
} else {
|
|
innovate_serial_id_state = UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case HEADER_FOUND:
|
|
// now we should have both header bytes in array, and we can read the total packet length
|
|
tmsglen = (((ser_buffer[0] << 8) | ser_buffer[1]) & lc2_pcklen_mask); //0000000101111111 mask
|
|
|
|
if (tmsglen) {
|
|
tmsglen += 1; // length in words including header (2 bytes)
|
|
tmsglen *= 2; // length in bytes (incl header)
|
|
innovate_msg_len = tmsglen - 2;
|
|
sb = 2;
|
|
innovate_serial_id_state = IDENTIFIED; //advance state machine
|
|
} else {
|
|
innovate_serial_id_state = UNKNOWN;
|
|
}
|
|
|
|
break;
|
|
|
|
case IDENTIFIED:
|
|
innovate_msg_len = tmsglen;
|
|
sb = 0;
|
|
// serial packet fully identified
|
|
ParseInnovateSerialMsg(); //takes about 570ns
|
|
clear_ser_buffer = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParseInnovateSerialMsg() {
|
|
float raw_afr;
|
|
//get error code and afr
|
|
|
|
// 000 Lambda valid and Aux data valid, normal operation.
|
|
// 001 Lambda value contains O2 level in 1/10%
|
|
// 010 Free air Calib in progress, Lambda data not valid
|
|
// 011 Need Free air Calibration Request, Lambda data not valid
|
|
// 100 Warming up, Lambda value is temp in 1/10% of operating temp.
|
|
// 101 Heater Calibration, Lambda value contains calibration countdown.
|
|
// 110 Error code in Lambda value
|
|
// 111 reserved
|
|
|
|
for (size_t i = 0; i < ((tmsglen - 2) / 4) && i < NUM_INNOVATE_O2_SENSORS; i++) {
|
|
innovate_o2_sensor[i].function_code = (ser_buffer[2 + i * 4] >> 2 & 0x7);
|
|
// innovate_o2_sensor[i].AFR_multiplier = ((ser_buffer[2 + i * 4] << 7 | ser_buffer[3 + i * 4]) & 0xFF);
|
|
innovate_o2_sensor[i].AFR_multiplier = AFR_MULTIPLIER;
|
|
|
|
switch (innovate_o2_sensor[i].function_code) {
|
|
case 0: //Lambda valid and aux data valid, normal operation
|
|
case 1: //Lambda value contains o2 level in 1/10%
|
|
innovate_o2_sensor[i].lambda = ((ser_buffer[4 + i * 4] << 7 | ser_buffer[5 + i * 4]) & 0x1FFF);
|
|
raw_afr = ((innovate_o2_sensor[i].lambda + 500) * innovate_o2_sensor[i].AFR_multiplier);
|
|
|
|
if (innovate_o2_sensor[i].function_code) {//case 1
|
|
innovate_o2_sensor[i].AFR = raw_afr * 0.001;
|
|
} else { // case 0
|
|
innovate_o2_sensor[i].AFR = raw_afr * 0.0001;
|
|
}
|
|
|
|
if (innovate_o2_sensor[i].AFR > AFRMAX) {
|
|
innovate_o2_sensor[i].AFR = AFRMAX;
|
|
} else if (innovate_o2_sensor[i].AFR < AFRMIN) {
|
|
innovate_o2_sensor[i].AFR = AFRMIN;
|
|
}
|
|
|
|
InnovateLC2AFR = innovate_o2_sensor[0].AFR; //only using one sensor right now
|
|
|
|
break;
|
|
// this is invalid o2 data, so we can ignore it:
|
|
// case 2: // Free air Calib in progress, Lambda data not valid
|
|
// break;
|
|
// case 3: // Need Free air Calibration Request, Lambda data not valid
|
|
// break;
|
|
case 4: // Warming up, Lambda value is temp in 1/10% of operating temp
|
|
InnovateLC2AFR = AFR_ERROR;
|
|
innovate_o2_sensor[i].warmup = ((ser_buffer[4 + i * 4] << 7 | ser_buffer[5 + i * 4]) & 0x1FFF);
|
|
//catch potential overflow:
|
|
if (innovate_o2_sensor[i].warmup >= 1023) {
|
|
innovate_o2_sensor[i].warmup = 1023;
|
|
} else if (innovate_o2_sensor[i].warmup <= 0) {
|
|
innovate_o2_sensor[i].warmup = 0;
|
|
}
|
|
break;
|
|
// case 5: // Heater Calibration, Lambda value contains calibration countdown
|
|
// break;
|
|
case 6: // Error code in Lambda value
|
|
InnovateLC2AFR = AFR_ERROR;
|
|
innovate_o2_sensor[i].error_code = (sensor_error_code_t)((ser_buffer[4 + i * 4] << 7 | ser_buffer[5 + i * 4]) & 0x1FFF);
|
|
//catch potential overflow:
|
|
if (innovate_o2_sensor[i].error_code >= (sensor_error_code_t)1023) {
|
|
innovate_o2_sensor[i].error_code = (sensor_error_code_t)1023;
|
|
} else if (innovate_o2_sensor[i].error_code <= 0) {
|
|
innovate_o2_sensor[i].error_code = (sensor_error_code_t)0;
|
|
}
|
|
break;
|
|
// case 7: // reserved
|
|
// break;
|
|
default:
|
|
InnovateLC2AFR = AFR_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ResetSerialSensor() {
|
|
ClearSerialBuffer();
|
|
ParseSerialData();
|
|
}
|
|
|
|
void ClearSerialBuffer() {
|
|
memset(ser_buffer, 0, sizeof(ser_buffer));
|
|
}
|
|
|
|
void ParseSerialData() {
|
|
if (engineConfiguration->enableInnovateLC2)
|
|
IdentifyInnovateSerialMsg();
|
|
}
|
|
|
|
#endif
|