diff --git a/firmware/ini/wideband_dual.ini b/firmware/ini/wideband_dual.ini index dbb5377e..c4a0bc38 100644 --- a/firmware/ini/wideband_dual.ini +++ b/firmware/ini/wideband_dual.ini @@ -125,11 +125,13 @@ AFR1_heater = scalar, U08, 93, "", 1, 0 EGT0_temp = scalar, F32, 96, "C", 1, 0 EGT0_coldJunction = scalar, F32, 100, "C", 1, 0 EGT0_state = scalar, U08, 104, "", 1, 0 +EGT0_commErrors = scalar, U32, 108, "n", 1, 0 ; EGT1 EGT1_temp = scalar, F32, 112, "C", 1, 0 EGT1_coldJunction = scalar, F32, 116, "C", 1, 0 EGT1_state = scalar, U08, 120, "", 1, 0 +EGT1_commErrors = scalar, U32, 124, "n", 1, 0 ; TODO: something is wrong with these Aux0InputSig = { (Aux0InputSel == 0) ? AFR0_lambda : ((Aux0InputSel == 1) ? AFR1_lambda : ((Aux0InputSel == 2) ? EGT0_temp : EGT1_temp)) } @@ -205,6 +207,7 @@ gaugeCategory = EGT channel 0 EGT0_Gauge = EGT0_temp, "0: EGT", "C", 0.0, 1600.0, 100.0, 250.0, 900.0, 1000.0, 0, 0 EGT0_ColdJunctionGauge = EGT0_coldJunction, "0: EGT CJ", "C", 0.0, 130.0, -55.0, -20.0, 95.0, 105.0, 1, 1 EGT0_StateGauge = EGT0_state, "0: EGT state", "", 0.0, 5.0, 0.0, 0.0, 0.5, 0.5, 0, 0 +EGT0_CommErrorsGauge = EGT0_commErrors,"0: EGT comm errors", "", 0.0, 10, 0.0, 0.0, 1, 1, 0, 0 ; EGT1 gaugeCategory = EGT channel 1 @@ -212,6 +215,7 @@ gaugeCategory = EGT channel 1 EGT1_Gauge = EGT1_temp, "1: EGT", "C", 0.0, 1600.0, 100.0, 250.0, 900.0, 1000.0, 0, 0 EGT1_ColdJunctionGauge = EGT1_coldJunction, "1: EGT CJ", "C", 0.0, 130.0, -55.0, -20.0, 95.0, 105.0, 1, 1 EGT1_StateGauge = EGT1_state, "1: EGT state", "", 0.0, 5.0, 0.0, 0.0, 0.5, 0.5, 0, 0 +EGT1_CommErrorsGauge = EGT1_commErrors,"1: EGT comm errors", "", 0.0, 10, 0.0, 0.0, 1, 1, 0, 0 ; AUX outputs Aux0InputGauge = { (Aux0InputSel == 0) ? AFR0_AfrGauge : ((Aux0InputSel == 1) ? AFR1_AfrGauge : ((Aux0InputSel == 2) ? EGT0_Gauge : EGT1_Gauge)) } @@ -280,11 +284,13 @@ entry = AFR1_esr, "1: ESR", float, "%.1f" entry = EGT0_temp, "EGT 0: EGT", int, "%d" entry = EGT0_coldJunction, "EGT 0: CJ", int, "%d" entry = EGT0_state, "EGT 0: State", int, "%d" +entry = EGT0_commErrors, "EGT 0: comm errors", int, "%d" ; EGT1 entry = EGT1_temp, "EGT 1: EGT", int, "%d" entry = EGT1_coldJunction, "EGT 1: CJ", int, "%d" entry = EGT1_state, "EGT 1: State", int, "%d" +entry = EGT1_commErrors, "EGT 1: comm errors", int, "%d" [Menu] diff --git a/firmware/max3185x.cpp b/firmware/max3185x.cpp index 27478314..1f2432af 100644 --- a/firmware/max3185x.cpp +++ b/firmware/max3185x.cpp @@ -9,7 +9,7 @@ #if (EGT_CHANNELS > 0) -static SPIConfig spi_config[2] = +static const SPIConfig spi_config[EGT_CHANNELS] = { { .circular = false, @@ -43,11 +43,11 @@ static SPIConfig spi_config[2] = } }; -static Max3185x instances[] = {&spi_config[0], &spi_config[1]}; +static Max3185x instances[EGT_CHANNELS] = {Max3185x(&spi_config[0]), Max3185x(&spi_config[1])}; static Max3185xThread EgtThread(instances); -int Max3185x::spi_txrx(uint8_t tx[], uint8_t rx[], size_t n) +int Max3185x::spi_txrx(const uint8_t tx[], uint8_t rx[], size_t n) { /* Acquire ownership of the bus. */ spiAcquireBus(EGT_SPI_DRIVER); @@ -58,6 +58,8 @@ int Max3185x::spi_txrx(uint8_t tx[], uint8_t rx[], size_t n) spiExchange(EGT_SPI_DRIVER, n, tx, rx); /* Slave Select de-assertion. */ spiUnselect(EGT_SPI_DRIVER); + /* Bus deinit */ + spiStop(EGT_SPI_DRIVER); /* Ownership release. */ spiReleaseBus(EGT_SPI_DRIVER); @@ -85,152 +87,198 @@ int Max3185x::spi_rx32(uint32_t *data) return 0; } -int Max3185x::detect() +// bits D17 and D3 are always expected to be zero +#define MAX31855_RESERVED_BITS 0x20008 + +Max3185xType Max3185x::detect() { + int ret; uint8_t rx[4]; - /* read MASK, CJHF, CJLF */ - uint8_t tx[4] = {0x02, 0x00, 0x00, 0x00}; - uint32_t data; + uint8_t tx[4]; + + /* try to apply settings to max31956 and then read back settings */ + // Wr, register 0x00 + tx[0] = 0x00 | BIT(7); + // CR0: 50Hz mode + // Change the notch frequency only while in the "Normally Off" mode - not in the Automatic + tx[1] = BIT(0); + // CR1: 4 samples average, K type + // The Thermocouple Voltage Conversion Averaging Mode settings should not be changed while + // conversions are taking place. + tx[2] = (2 << 4) | (3 << 0); + + // Stop any conversion + ret = spi_txrx(tx, rx, 2); + if (ret) { + return UNKNOWN_TYPE; + } - int ret = spi_txrx(tx, rx, 4); - if (ret) - return ret; - data = (rx[0] << 24) | - (rx[1] << 16) | - (rx[2] << 8) | - (rx[3] << 0); - /* MASK, CJHF, CJLF defaults: 0xff, 0x7f, 0xc0 */ - if ((data & 0x00ffffff) == 0x00ff7fc0) { - /* configure */ - /* CR0: 50 Hz mode - * Change the notch frequency only while in the "Normally Off" mode - not in the Automatic - * Conversion mode.*/ - tx[0] = 0x80; - tx[1] = 0x01; - spi_txrx(tx, rx, 2); - /* CR0: Automatic Conversion mode, OCFAULT = 2, 50Hz mode */ - tx[1] = BIT(7) | BIT(0) | 2 << 4; - /* CR1: 4 samples average, K type */ - tx[2] = (2 << 4) | (3 << 0); - spi_txrx(tx, rx, 3); - type = MAX31856_TYPE; - return 0; + // Apply notch frequency and averaging + ret = spi_txrx(tx, rx, 3); + if (ret) { + return UNKNOWN_TYPE; + } + + // Start Automatic Conversion mode + // CR0: Automatic Conversion mode, OCFAULT = 2, 50Hz mode + tx[1] = BIT(7) | BIT(0) | (2 << 4); + // CR1: 4 samples average, K type + tx[2] = (2 << 4) | (3 << 0); + ret = spi_txrx(tx, rx, 3); + if (ret) { + return UNKNOWN_TYPE; } - if (data != 0xffffffff) { - type = MAX31855_TYPE; - return 0; + + /* Now readback settings */ + tx[0] = 0x00; + ret = spi_txrx(tx, rx, 4); + if ((rx[1] == tx[1]) && (rx[2] == tx[2])) { + return MAX31856_TYPE; + } + + /* in case of max31855 we get standart reply with few reserved, always zero bits */ + uint32_t data = (rx[0] << 24) | + (rx[1] << 16) | + (rx[2] << 8) | + (rx[3] << 0); + + /* MISO is constantly low or high */ + if ((data == 0xffffffff) || (data == 0x0)) { + return UNKNOWN_TYPE; + } + + if ((data & MAX31855_RESERVED_BITS) == 0x0) { + return MAX31855_TYPE; } livedata.state = MAX3185X_NO_REPLY; - type = UNKNOWN_TYPE; - return -1; + + return UNKNOWN_TYPE; } -int Max3185x::readPacket31855() +Max3185xState Max3185x::readPacket31855() { uint32_t data; + #define MAX33855_FAULT_BIT BIT(16) + #define MAX33855_OPEN_BIT BIT(0) + #define MAX33855_GND_BIT BIT(1) + #define MAX33855_VCC_BIT BIT(2) + int ret = spi_rx32(&data); - /* TODO: also check for 0x00000000? */ - if ((ret) || (data == 0xffffffff)) { - livedata.state = MAX3185X_NO_REPLY; - - ret = -1; - } else if (data & BIT(16)) { - if (data & BIT(0)) { - livedata.state = MAX3185X_OPEN_CIRCUIT; - } else if (data & BIT(1)) { - livedata.state = MAX3185X_SHORT_TO_GND; - } else if (data & BIT(2)) { - livedata.state = MAX3185X_SHORT_TO_VCC; + if ((ret) || + ((data & MAX31855_RESERVED_BITS) != 0) || + (data == 0x0) || + (data == 0xffffffff)) { + return MAX3185X_NO_REPLY; + } else if (data & MAX33855_FAULT_BIT) { + if (data & MAX33855_OPEN_BIT) { + return MAX3185X_OPEN_CIRCUIT; + } else if (data & MAX33855_GND_BIT) { + return MAX3185X_SHORT_TO_GND; + } else if (data & MAX33855_VCC_BIT) { + return MAX3185X_SHORT_TO_VCC; } - - ret = -1; } - if (ret) { - coldJunctionTemperature = NAN; - livedata.coldJunctionTemperature = 0; - temperature = NAN; - livedata.temperature = 0; - } else { - /* D[15:4] */ - int16_t tmp = (data >> 4) & 0xfff; - /* extend sign */ - tmp = tmp << 4; - tmp = tmp >> 4; /* shifting right signed is not a good idea */ - coldJunctionTemperature = (float)tmp * 0.0625; - - /* D[31:18] */ - tmp = (data >> 18) & 0x3fff; - /* extend sign */ - tmp = tmp << 2; - tmp = tmp >> 2; /* shifting right signed is not a good idea */ - temperature = (float) tmp * 0.25; - - /* update livedata: float to int */ - livedata.coldJunctionTemperature = coldJunctionTemperature; - livedata.temperature = temperature; - - livedata.state = MAX3185X_OK; - } - - return ret; + /* D[15:4] */ + int16_t tmp = (data >> 4) & 0xfff; + /* extend sign */ + tmp = tmp << 4; + tmp = tmp >> 4; /* shifting right signed is not a good idea */ + coldJunctionTemperature = (float)tmp * 0.0625; + + /* D[31:18] */ + tmp = (data >> 18) & 0x3fff; + /* extend sign */ + tmp = tmp << 2; + tmp = tmp >> 2; /* shifting right signed is not a good idea */ + temperature = (float) tmp * 0.25; + + return MAX3185X_OK; } -int Max3185x::readPacket31856() +Max3185xState Max3185x::readPacket31856() { - uint8_t rx[7]; - /* read Cold-Junction temperature MSB, LSB, Linearized TC temperature 3 bytes and Fault Status */ - uint8_t tx[7] = {0x0a}; + uint8_t rx[1 + 6]; + /* read one dummy byte, Cold-Junction temperature MSB, LSB, Linearized TC temperature 3 bytes and Fault Status */ + const uint8_t tx[1 + 6] = {0x0a}; - int ret = spi_txrx(tx, rx, 7); + int ret = spi_txrx(tx, rx, sizeof(rx)); - if (rx[6] & BIT(0)) { - livedata.state = MAX3185X_OPEN_CIRCUIT; - ret = -1; + if (ret) { + return MAX3185X_NO_REPLY; + } else if (rx[6] & BIT(0)) { + return MAX3185X_OPEN_CIRCUIT; } else if (rx[6] & BIT(1)) { - livedata.state = MAX3185X_SHORT_TO_VCC; - ret = -1; + return MAX3185X_SHORT_TO_VCC; } - if (ret) { - coldJunctionTemperature = NAN; - livedata.coldJunctionTemperature = 0; - temperature = NAN; - livedata.temperature = 0; - } else { - /* update livedata: float to int */ - coldJunctionTemperature = (float)(rx[1] << 8 | rx[2]) / 256.0; - temperature = (float)((rx[3] << 11) | (rx[4] << 3) | (rx[5] >> 5)) / 128.0; - livedata.coldJunctionTemperature = coldJunctionTemperature; - livedata.temperature = temperature; + // Paranoid check. + bool allZero = true; + for (int i = 1; i < 6; i++) { + if (rx[i] != 0x00) { + allZero = false; + break; + } + } + if (allZero) { + return MAX3185X_NO_REPLY; + } - livedata.state = MAX3185X_OK; + if (1) { + // 10 bit before point and 7 bits after + int32_t tmp = (rx[3] << 11) | (rx[4] << 3) | (rx[5] >> 5); + /* extend sign: move top bit 18 to 31 */ + tmp = tmp << 13; + tmp = tmp >> 13; /* shifting right signed is not a good idea */ + temperature = ((float)tmp) / 128.0; + } + if (1) { + int16_t tmp = (rx[1] << 6) | (rx[2] >> 2); + /* extend sign */ + tmp = tmp << 2; + tmp = tmp >> 2; /* shifting right signed is not a good idea */ + coldJunctionTemperature = ((float)tmp) / 64.0; } - return ret; + return MAX3185X_OK; } -int Max3185x::readPacket() +Max3185xState Max3185x::readPacket() { - int ret; - if (type == UNKNOWN_TYPE) { - ret = detect(); - if (ret < 0) { - return ret; + type = detect(); + if (type == UNKNOWN_TYPE) { + livedata.state = MAX3185X_NO_REPLY; } } if (type == MAX31855_TYPE) { - return readPacket31855(); + livedata.state = readPacket31855(); } else if (type == MAX31856_TYPE) { - return readPacket31856(); + livedata.state = readPacket31856(); + } + + if (livedata.state == MAX3185X_OK) { + /* update livedata: float to int */ + livedata.coldJunctionTemperature = coldJunctionTemperature; + livedata.temperature = temperature; + } else { + coldJunctionTemperature = NAN; + livedata.coldJunctionTemperature = 0; + temperature = NAN; + livedata.temperature = 0; + } + + /* in case of communication problems - reinit */ + if (livedata.state == MAX3185X_NO_REPLY) { + livedata.commErrors++; + type = UNKNOWN_TYPE; } - return -1; + return MAX3185X_OK; } void Max3185xThread::ThreadTask() { diff --git a/firmware/max3185x.h b/firmware/max3185x.h index 433eba53..c94012d1 100644 --- a/firmware/max3185x.h +++ b/firmware/max3185x.h @@ -26,6 +26,8 @@ struct livedata_egt_s { float temperature; float coldJunctionTemperature; uint8_t state; + uint8_t pad0[3]; + uint32_t commErrors; } __attribute__((packed)); uint8_t pad[16]; }; @@ -41,22 +43,22 @@ const struct livedata_egt_s * getEgtLiveDataStructAddr(const int ch); class Max3185x { public: - Max3185x(SPIConfig *spi) { + Max3185x(const SPIConfig *spi) { this->spi = spi; } livedata_egt_s livedata; /* do we need float temperatures? */ float coldJunctionTemperature; float temperature; - Max3185xType type; - int readPacket(); + Max3185xType type = UNKNOWN_TYPE; + Max3185xState readPacket(); private: - SPIConfig *spi; - int detect(); - int readPacket31855(); - int readPacket31856(); + const SPIConfig *spi; + Max3185xType detect(); + Max3185xState readPacket31855(); + Max3185xState readPacket31856(); int spi_rx32(uint32_t *data); - int spi_txrx(uint8_t tx[], uint8_t rx[], size_t n); + int spi_txrx(const uint8_t tx[], uint8_t rx[], size_t n); }; class Max3185xThread : public ThreadController { diff --git a/firmware/pump_dac.cpp b/firmware/pump_dac.cpp index 58c23aaa..861d1c4d 100644 --- a/firmware/pump_dac.cpp +++ b/firmware/pump_dac.cpp @@ -17,10 +17,10 @@ static const PWMConfig pumpDacConfig = { 1024, nullptr, { - {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH, nullptr}, - {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH, nullptr}, - {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH, nullptr}, - {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH, nullptr} + {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW, nullptr}, + {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW, nullptr}, + {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW, nullptr}, + {PWM_OUTPUT_ACTIVE_HIGH | PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW, nullptr} }, 0, 0, @@ -108,6 +108,8 @@ void SetPumpCurrentTarget(int ch, int32_t microampere) } #endif + // microampere = 2000; + state[ch].curIpump = microampere; // 47 ohm resistor diff --git a/firmware/uart.cpp b/firmware/uart.cpp index fb9b0cb2..1bbc8bec 100644 --- a/firmware/uart.cpp +++ b/firmware/uart.cpp @@ -8,6 +8,7 @@ #include "max3185x.h" #include "fault.h" #include "uart.h" +#include "pump_dac.h" #include "tunerstudio.h" #include "tunerstudio_io.h" @@ -56,10 +57,11 @@ static void UartThread(void*) int lambdaIntPart = lambda; int lambdaThousandths = (lambda - lambdaIntPart) * 1000; int heaterVoltageMv = GetSampler(ch).GetInternalHeaterVoltage() * 1000; - int duty = GetHeaterDuty(ch) * 100; + int heaterDuty = GetHeaterDuty(ch) * 100; + int pumpDuty = GetPumpOutputDuty(ch) * 100; size_t writeCount = chsnprintf(printBuffer, sizeof(printBuffer), - "[AFR%d]: %d.%03d DC: %4d mV AC: %4d mV ESR: %5d T: %4d C Ipump: %6d uA Vheater: %5d heater: %s (%d)\tfault: %s\r\n", + "[AFR%d]: %d.%03d DC: %4d mV AC: %4d mV ESR: %5d T: %4d C Ipump: %6d uA PumpDac: %3d Vheater: %5d heater: %s (%d)\tfault: %s\r\n", ch, lambdaIntPart, lambdaThousandths, (int)(GetSampler(ch).GetNernstDc() * 1000.0), @@ -67,8 +69,9 @@ static void UartThread(void*) (int)GetSampler(ch).GetSensorInternalResistance(), (int)GetSampler(ch).GetSensorTemperature(), (int)(GetSampler(ch).GetPumpNominalCurrent() * 1000), + pumpDuty, heaterVoltageMv, - describeHeaterState(GetHeaterState(ch)), duty, + describeHeaterState(GetHeaterState(ch)), heaterDuty, describeFault(GetCurrentFault(ch))); chnWrite(&SD1, (const uint8_t *)printBuffer, writeCount); }