diff --git a/firmware/Makefile b/firmware/Makefile index aa57a076..5508c0f2 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -113,6 +113,7 @@ endif # Configure libfirmware Paths/Includes RUSEFI_LIB = libfirmware include $(RUSEFI_LIB)/util/util.mk +include $(RUSEFI_LIB)/sent/sent.mk # Licensing files. include $(CHIBIOS)/os/license/license.mk @@ -151,6 +152,7 @@ CPPSRC = $(ALLCPPSRC) \ can.cpp \ can_helper.cpp \ can_aemnet.cpp \ + sent.cpp \ fault.cpp \ lambda_conversion.cpp \ pwm.cpp \ diff --git a/firmware/boards/f1_dual_rev1/chconf.h b/firmware/boards/f1_dual_rev1/chconf.h index 8ffc63c2..96fe4c7b 100644 --- a/firmware/boards/f1_dual_rev1/chconf.h +++ b/firmware/boards/f1_dual_rev1/chconf.h @@ -343,7 +343,7 @@ typedef int pid_t; * @note Requires @p CH_CFG_USE_SEMAPHORES. */ #if !defined(CH_CFG_USE_MAILBOXES) -#define CH_CFG_USE_MAILBOXES FALSE +#define CH_CFG_USE_MAILBOXES TRUE #endif /** diff --git a/firmware/boards/f1_dual_rev1/halconf.h b/firmware/boards/f1_dual_rev1/halconf.h index 0c9dae8b..6f14d14c 100644 --- a/firmware/boards/f1_dual_rev1/halconf.h +++ b/firmware/boards/f1_dual_rev1/halconf.h @@ -100,7 +100,7 @@ * @brief Enables the ICU subsystem. */ #if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) -#define HAL_USE_ICU FALSE +#define HAL_USE_ICU TRUE #endif /** diff --git a/firmware/boards/f1_dual_rev1/mcuconf.h b/firmware/boards/f1_dual_rev1/mcuconf.h index 4e386c57..77fccf59 100644 --- a/firmware/boards/f1_dual_rev1/mcuconf.h +++ b/firmware/boards/f1_dual_rev1/mcuconf.h @@ -129,7 +129,7 @@ */ #define STM32_ICU_USE_TIM1 FALSE #define STM32_ICU_USE_TIM2 FALSE -#define STM32_ICU_USE_TIM3 FALSE +#define STM32_ICU_USE_TIM3 TRUE #define STM32_ICU_USE_TIM4 FALSE #define STM32_ICU_USE_TIM5 FALSE #define STM32_ICU_USE_TIM8 FALSE @@ -194,7 +194,7 @@ * ST driver system settings. */ #define STM32_ST_IRQ_PRIORITY 8 -#define STM32_ST_USE_TIMER 3 +#define STM32_ST_USE_TIMER 2 /* * UART driver system settings. diff --git a/firmware/boards/f1_dual_rev1/wideband_board_config.h b/firmware/boards/f1_dual_rev1/wideband_board_config.h index 0203e16d..201d4c71 100644 --- a/firmware/boards/f1_dual_rev1/wideband_board_config.h +++ b/firmware/boards/f1_dual_rev1/wideband_board_config.h @@ -95,3 +95,8 @@ // Indication: per channel status LED + communication LED // ******************************* #define ADVANCED_INDICATION + +// ******************************* +// SENT protocol receiver on analog inputs +// ******************************* +#define SENT_INPUT_COUNT 1 // Number of sent channels diff --git a/firmware/libfirmware b/firmware/libfirmware index 39b3a04d..db2d33a9 160000 --- a/firmware/libfirmware +++ b/firmware/libfirmware @@ -1 +1 @@ -Subproject commit 39b3a04dab617e7ff24577e99d0d7fac2ce44c9f +Subproject commit db2d33a91800c457943a2f67199325b0824e5b58 diff --git a/firmware/main.cpp b/firmware/main.cpp index 6430b455..2bf6f074 100644 --- a/firmware/main.cpp +++ b/firmware/main.cpp @@ -14,6 +14,7 @@ #include "port.h" #include "tunerstudio.h" #include "indication.h" +#include "sent.h" #include "wideband_config.h" @@ -46,6 +47,10 @@ int main() { InitUart(); InitIndication(); +#if (SENT_INPUT_COUNT > 0) + initSent(); +#endif + #if (EGT_CHANNELS > 0) StartEgt(); #endif diff --git a/firmware/sent.cpp b/firmware/sent.cpp new file mode 100644 index 00000000..e2d90ce6 --- /dev/null +++ b/firmware/sent.cpp @@ -0,0 +1,367 @@ +/* + * sent.cpp + * + * SENT protocol decoder + * + * @date Dec 13, 2024 + * @author Andrey Gusakov , (c) 2024 + */ + +#include "ch.h" +#include "hal.h" +#include "chprintf.h" + +#include "wideband_config.h" + +#if (SENT_INPUT_COUNT > 0) + +#include "sent.h" + +static sent_channel channels[SENT_INPUT_COUNT]; + +/*==========================================================================*/ +/* ICU driver. */ +/*==========================================================================*/ +/* This SENT HW driver is based on ChibiOS ICU driver */ +#if (HAL_USE_ICU == TRUE) + +/* TODO: do we care about scaling abstract timer ticks to some time base? */ +/* TODO: get at runtime */ +/* Max timer clock for most timers on STM32 is CPU clock / 2 */ +#define SENT_TIMER_CLOCK_DIV 2 +/* TODO: not all timers are clocked from STM32_TIMCLK1 */ +#define SENT_ICU_FREQ (STM32_TIMCLK1 / SENT_TIMER_CLOCK_DIV) + +static uint16_t lastPulse[SENT_INPUT_COUNT]; +static bool overcapture[SENT_INPUT_COUNT]; + +static void icuperiodcb(ICUDriver *icup, size_t index) +{ + uint16_t clocks; + uint8_t flags = 0; + const ICUConfig *icucfg = icup->config; + + if ((icucfg->channel == ICU_CHANNEL_1) || (icucfg->channel == ICU_CHANNEL_2)) { + /* channel 1 and channel 2 supports period measurements */ + clocks = icuGetPeriodX(icup); + } else { + /* this is freerunnig timer and we need to calculate period using just captured timer value and previous one */ + /* TODO: support 32 bit timers too? */ + uint16_t val = icuGetWidthX(icup); + + /* can overflow */ + clocks = val - lastPulse[index]; + + lastPulse[index] = val; + } + + if (overcapture[index]) { + flags |= SENT_FLAG_HW_OVERFLOW; + overcapture[index] = false; + } + + SENT_ISR_Handler(index, clocks, flags); +} + +//static void icuovercapture(ICUDriver *icup, size_t index) +//{ +// overcapture[index] = true; +//} + +/* ICU callbacks */ +static void icuperiodcb_in1(ICUDriver *icup) +{ + icuperiodcb(icup, 0); +} + +//static void icuovercapture_in1(ICUDriver *icup) +//{ +// icuovercapture(icup, 0); +//} + +static void icuperiodcb_in2(ICUDriver *icup) +{ + icuperiodcb(icup, 1); +} + +//static void icuovercapture_in2(ICUDriver *icup) +//{ +// icuovercapture(icup, 1); +//} + +/* ICU configs */ +static /* const */ ICUConfig icucfg[SENT_INPUT_COUNT] = +{ +#if (STM32_ICU_USE_TIM3 == TRUE) + /* P1.A5 -> R_AUX -> R_AUX_ADC -> PA6 */ + { + .mode = ICU_INPUT_ACTIVE_LOW, + .frequency = SENT_ICU_FREQ, + .width_cb = NULL, + .period_cb = icuperiodcb_in1, + .overflow_cb = NULL, + .channel = ICU_CHANNEL_1, + .dier = 0U, + .arr = 0xFFFFFFFFU, + //.overcapture_cb = icuovercapture_in1, + }, +#if 0 + /* P1.A4 -> L_AUX -> L_AUX_ADC -> PA7 */ + { + .mode = ICU_INPUT_ACTIVE_LOW, + .frequency = SENT_ICU_FREQ, + .width_cb = NULL, + .period_cb = icuperiodcb_in2, + .overflow_cb = NULL, + .channel = ICU_CHANNEL_2, + .dier = 0U, + .arr = 0xFFFFFFFFU, + //.overcapture_cb = icuovercapture_in2, + } +#endif +#endif +}; + +static ICUDriver *icudrivers[SENT_INPUT_COUNT] = +{ +#if (STM32_ICU_USE_TIM3 == TRUE) + &ICUD3, + //&ICUD3, +#else + nullptr, + nullptr, +#endif +}; + +void startSent() { +#if (STM32_ICU_USE_TIM3 == TRUE) + palSetPadMode(GPIOA, 6, PAL_MODE_INPUT); + palSetPadMode(GPIOA, 7, PAL_MODE_INPUT); +#endif + + for (int i = 0; i < SENT_INPUT_COUNT; i++) { + ICUConfig *cfg = &icucfg[i]; + ICUDriver *icu = icudrivers[i]; + + if (icu == nullptr) { + continue; + } + + icuStart(icu, cfg); + icuStartCapture(icu); + icuEnableNotifications(icu); + } +} +#endif //HAL_USE_ICU + +/*==========================================================================*/ +/* Forward declarations. */ +/*==========================================================================*/ + +static int GmPressureDecode(size_t index); +static void GmPressureDebug(void); + +/*==========================================================================*/ +/* Debug. */ +/*==========================================================================*/ +extern BaseSequentialStream *chp; + +void sent_channel::Info() { + uint8_t stat; + uint16_t sig0, sig1; + + chprintf(chp, "Unit time %lu timer ticks\r\n", tickPerUnit); + chprintf(chp, "Pause pulse detected %s\r\n", pausePulseReceived ? "Yes" : "No"); + chprintf(chp, "Total pulses %lu\r\n", pulseCounter); + + if (GetSignals(&stat, &sig0, &sig1) == 0) { + chprintf(chp, "Last valid fast msg Status 0x%01x Sig0 0x%03x Sig1 0x%03x\r\n", stat, sig0, sig1); + } + + chprintf(chp, "Slow channels:\r\n"); + for (int i = 0; i < SENT_SLOW_CHANNELS_MAX; i++) { + if (scMsg[i].valid) { + chprintf(chp, " %d: ID %d: %d (0x%x)\r\n", i, scMsg[i].id, scMsg[i].data, scMsg[i].data); + } + } + + #if SENT_STATISTIC_COUNTERS + static int aliveCounter = 0; + chprintf(chp, "HW overflows %lu\r\n", statistic.hwOverflowCnt); + + chprintf(chp, "Pause pulses %lu\r\n", statistic.PauseCnt); + chprintf(chp, "Restarts %lu\r\n", statistic.RestartCnt); + chprintf(chp, "Interval errors %lu short, %lu long\r\n", statistic.ShortIntervalErr, statistic.LongIntervalErr); + chprintf(chp, "Total frames %lu with CRC error %lu (%d%%)\r\n", statistic.FrameCnt, statistic.CrcErrCnt, 100 * statistic.CrcErrCnt / statistic.FrameCnt); + chprintf(chp, "Total slow channel messages %lu + %lu with crc6 errors %lu (%d%%)\r\n", statistic.sc12, statistic.sc16, statistic.scCrcErr, 100 * statistic.scCrcErr / (statistic.sc12 + statistic.sc16)); + chprintf(chp, "Sync errors %lu alive=%d\r\n", statistic.SyncErr, aliveCounter++); + #endif + GmPressureDebug(); +} + +void sentDebug(void) +{ + for (int i = 0; i < SENT_INPUT_COUNT; i++) { + if (icudrivers[i] == nullptr) + continue; + + sent_channel &channel = channels[i]; + + chprintf(chp, "---- SENT input %d ----\r\n", i); + channel.Info(); + chprintf(chp, "--------------------\r\n"); + } +} + +/*==========================================================================*/ +/* Decoder feed handler and mailbox */ +/*==========================================================================*/ +/* 4 per channel should be enough */ +#define SENT_MB_SIZE (4 * SENT_INPUT_COUNT) + +static msg_t sent_mb_buffer[SENT_MB_SIZE]; +static MAILBOX_DECL(sent_mb, sent_mb_buffer, SENT_MB_SIZE); + +static THD_WORKING_AREA(waSentDecoderThread, 256); + +void SENT_ISR_Handler(uint8_t channel, uint16_t clocks, uint8_t flags) { + /* encode to fit msg_t */ + msg_t msg = (flags << 24) | (channel << 16) | clocks; + + /* called from ISR */ + chSysLockFromISR(); + chMBPostI(&sent_mb, msg); + chSysUnlockFromISR(); +} + +/*==========================================================================*/ +/* Decoder thread. */ +/*==========================================================================*/ +static void SentDecoderThread(void*) { + while (true) { + msg_t ret; + msg_t msg; + + ret = chMBFetchTimeout(&sent_mb, &msg, TIME_INFINITE); + + if (ret == MSG_OK) { + uint16_t tick = msg & 0xffff; + uint8_t n = (msg >> 16) & 0xff; + uint8_t flags = (msg >> 24) & 0xff; + + if (n < SENT_INPUT_COUNT) { + sent_channel &channel = channels[n]; + + if (channel.Decoder(tick, flags) > 0) { + /* Call high level decoder from here */ + GmPressureDecode(n); + } + } + } + } +} + +/* Should be called once */ +void initSent(void) { + /* init interval mailbox */ + chMBObjectInit(&sent_mb, sent_mb_buffer, SENT_MB_SIZE); + + chThdCreateStatic(waSentDecoderThread, sizeof(waSentDecoderThread), NORMALPRIO, SentDecoderThread, nullptr); + + /* Start HW layer */ + startSent(); +} + +int getSentValues(size_t index, uint16_t *sig0, uint16_t *sig1) { + if (index < SENT_INPUT_COUNT) { + sent_channel &channel = channels[index]; + + return channel.GetSignals(NULL, sig0, sig1); + } + + /* invalid channel */ + return -1; +} + +int getSentSlowChannelValue(size_t index, size_t id) +{ + if (index < SENT_INPUT_COUNT) { + sent_channel &channel = channels[index]; + + return channel.GetSlowChannelValue(id); + } + + /* invalid channel */ + return -1; +} + +/*==========================================================================*/ +/* Sensor handler(s) */ +/*==========================================================================*/ + +static bool gm_valid = false; +static float gm_pres = 0.0; +static float gm_temp = -273.0; + +static int GmPressureDecode(size_t index) +{ + int t1, t2; + + /* Slow channels 16 and 22 transmit temperature */ + t1 = getSentSlowChannelValue(index, 16); + t2 = getSentSlowChannelValue(index, 22); + + if ((t1 < 0) || (t2 < 0)) { + /* this is not GM pressure sensor or slow channels is not received yet */ + return 0; + } + + /* TODO: this is just a guess */ + /* average, 8 per degree C, 65 offset */ + gm_temp = (((float)t1 + (float)t2) / 2.0 - 512.0) / 8.0; + + /* + * Sig0 shows about 197..198 at 1 Atm (open air) and 282 at 1000 KPa (9.86 Atm) + * Sig1 shows about 202..203 at 1 Atm (open air) and 283 at 1000 KPa (9.86 Atm) + * So for 8.86 Atm delta there are: + * 84..85 units for sig0 + * 80..81 units for sig1 + * Measurements are not ideal, so lets ASSUME 10 units per 1 Atm + * Offset is 187..188 for Sig0 and 192..193 for Sig1. + * Average offset is 190 for 0 Atm. + */ + uint16_t sig0, sig1; + getSentValues(index, &sig0, &sig1); + + gm_pres = (((float)sig0 + (float)sig1) / 2 - 190) / 10.0; + + /* TODO: timestamp value */ + + gm_valid = true; + + return 1; +} + +static void GmPressureDebug(void) +{ + if (!gm_valid) + return; + + /* We can not print float */ + int pres = gm_pres * 1000; + int temp = gm_temp * 1000; + + chprintf(chp, "GM: press %d.%03d, temp %d.%03d\r\n", pres / 1000, pres % 1000, temp / 1000, temp % 1000); +} + +float GmPressureGetPressure(void) +{ + return gm_pres; +} + +float GmPressureGetTemperature(void) +{ + return gm_temp; +} + +#endif // SENT_INPUT_COUNT diff --git a/firmware/sent.h b/firmware/sent.h new file mode 100644 index 00000000..4a117b3f --- /dev/null +++ b/firmware/sent.h @@ -0,0 +1,26 @@ +/* + * sent.h + * + * SENT protocol decoder header + * + * @date Dec 13, 2024 + * @author Andrey Gusakov , (c) 2024 + */ + +#pragma once + +#include "sent_decoder.h" + +/* SENT decoder init */ +void initSent(); + +/* decoder feed hook */ +void SENT_ISR_Handler(uint8_t channels, uint16_t clocks, uint8_t flags); + +int getSentValues(size_t index, uint16_t *sig0, uint16_t *sig1); + +void sentDebug(void); + +/* sensor decoders */ +float GmPressureGetPressure(void); +float GmPressureGetTemperature(void); diff --git a/firmware/uart.cpp b/firmware/uart.cpp index 8d87668a..9efcd4f6 100644 --- a/firmware/uart.cpp +++ b/firmware/uart.cpp @@ -8,6 +8,7 @@ #include "max3185x.h" #include "fault.h" #include "uart.h" +#include "sent.h" #include "tunerstudio.h" #include "tunerstudio_io.h" @@ -67,6 +68,9 @@ static void UartThread(void*) (int)getEgtDrivers()[ch].coldJunctionTemperature); } #endif /* EGT_CHANNELS > 0 */ +#if (SENT_INPUT_COUNT > 0) + sentDebug(); +#endif /* SENT_INPUT_COUNT > 0 */ chThdSleepMilliseconds(100); } diff --git a/firmware/util/timer.cpp b/firmware/util/hw_timer.cpp similarity index 100% rename from firmware/util/timer.cpp rename to firmware/util/hw_timer.cpp diff --git a/firmware/wideband.mk b/firmware/wideband.mk index 59969761..65e4d2f1 100644 --- a/firmware/wideband.mk +++ b/firmware/wideband.mk @@ -2,4 +2,4 @@ WIDEBANDSRC = \ $(FIRMWARE_DIR)/pid.cpp \ $(FIRMWARE_DIR)/sampling.cpp \ $(FIRMWARE_DIR)/heater_control.cpp \ - $(FIRMWARE_DIR)/util/timer.cpp \ + $(FIRMWARE_DIR)/util/hw_timer.cpp \ diff --git a/firmware/wideband_config.h b/firmware/wideband_config.h index e9e01f1f..a93b3b82 100644 --- a/firmware/wideband_config.h +++ b/firmware/wideband_config.h @@ -10,6 +10,10 @@ #define EGT_CHANNELS 0 #endif +#ifndef SENT_INPUT_COUNT + #define SENT_INPUT_COUNT 0 +#endif + // ******************************* // Nernst voltage & ESR sense // *******************************