diff --git a/firmware/.cproject b/firmware/.cproject new file mode 100644 index 0000000..a4ef735 --- /dev/null +++ b/firmware/.cproject @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/firmware/.project b/firmware/.project new file mode 100644 index 0000000..f555db8 --- /dev/null +++ b/firmware/.project @@ -0,0 +1,27 @@ + + + firmware_wb + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/firmware/.settings/com.atollic.truestudio.debug.hardware_device.prefs b/firmware/.settings/com.atollic.truestudio.debug.hardware_device.prefs new file mode 100644 index 0000000..f5492de --- /dev/null +++ b/firmware/.settings/com.atollic.truestudio.debug.hardware_device.prefs @@ -0,0 +1,10 @@ +BOARD=None +CODE_LOCATION=FLASH +ENDIAN=Little-endian +MCU=STM32F042K6 +MCU_VENDOR=STMicroelectronics +MODEL=Pro +PROJECT_FORMAT_VERSION=2 +TARGET=STM32 +VERSION=9.3.0 +eclipse.preferences.version=1 diff --git a/firmware/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/firmware/.settings/org.eclipse.cdt.managedbuilder.core.prefs new file mode 100644 index 0000000..17bed3e --- /dev/null +++ b/firmware/.settings/org.eclipse.cdt.managedbuilder.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +environment/buildEnvironmentInclude/com.atollic.truestudio.exe.debug.toolchain.2055441200/CPATH/delimiter=; +environment/buildEnvironmentInclude/com.atollic.truestudio.exe.debug.toolchain.2055441200/CPATH/operation=remove +environment/buildEnvironmentInclude/com.atollic.truestudio.exe.debug.toolchain.2055441200/C_INCLUDE_PATH/delimiter=; +environment/buildEnvironmentInclude/com.atollic.truestudio.exe.debug.toolchain.2055441200/C_INCLUDE_PATH/operation=remove +environment/buildEnvironmentInclude/com.atollic.truestudio.exe.debug.toolchain.2055441200/append=true +environment/buildEnvironmentInclude/com.atollic.truestudio.exe.debug.toolchain.2055441200/appendContributed=true +environment/buildEnvironmentLibrary/com.atollic.truestudio.exe.debug.toolchain.2055441200/LIBRARY_PATH/delimiter=; +environment/buildEnvironmentLibrary/com.atollic.truestudio.exe.debug.toolchain.2055441200/LIBRARY_PATH/operation=remove +environment/buildEnvironmentLibrary/com.atollic.truestudio.exe.debug.toolchain.2055441200/append=true +environment/buildEnvironmentLibrary/com.atollic.truestudio.exe.debug.toolchain.2055441200/appendContributed=true diff --git a/firmware/Makefile b/firmware/Makefile index 393340d..6f8675f 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -108,6 +108,7 @@ include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk # Auto-build files in ./source recursively. include $(CHIBIOS)/tools/mk/autobuild.mk # Other files (optional). +include $(CHIBIOS)/os/hal/lib/streams/streams.mk # Define linker script file here LDSCRIPT= $(STARTUPLD)/STM32F042x6.ld @@ -124,6 +125,8 @@ CPPSRC = $(ALLCPPSRC) \ can_helper.cpp \ lambda_lookup.cpp \ pwm.cpp \ + pump_dac.cpp \ + sampling.cpp \ main.cpp # List ASM source files here. @@ -150,7 +153,8 @@ CPPWARN = -Wall -Wextra -Wundef # # List all user C define here, like -D_DEBUG=1 -UDEFS = +UDEFS = -DCHPRINTF_USE_FLOAT=1 + # Define ASM defines here UADEFS = diff --git a/firmware/analog_input.cpp b/firmware/analog_input.cpp index cb2b2fa..c6ff389 100644 --- a/firmware/analog_input.cpp +++ b/firmware/analog_input.cpp @@ -2,14 +2,18 @@ #include "hal.h" -#define ADC_OVERSAMPLE 8 +#define ADC_CHANNEL_COUNT 3 +#define ADC_OVERSAMPLE 16 -static adcsample_t adcBuffer[3 * ADC_OVERSAMPLE]; +static adcsample_t adcBuffer[ADC_CHANNEL_COUNT * ADC_OVERSAMPLE]; ADCConversionGroup convGroup = { - false, 3, nullptr, nullptr, - 0, // CFGR1 + false, + ADC_CHANNEL_COUNT, + nullptr, + nullptr, + ADC_CFGR1_CONT | ADC_CFGR1_RES_12BIT, // CFGR1 ADC_TR(0, 0), // TR ADC_SMPR_SMP_28P5, // SMPR ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL2 @@ -22,10 +26,10 @@ static float AverageSamples(adcsample_t* buffer, size_t idx) for (size_t i = 0; i < ADC_OVERSAMPLE; i++) { sum += buffer[idx]; - idx += ADC_OVERSAMPLE; + idx += ADC_CHANNEL_COUNT; } - constexpr float scale = 3.3f / (2048 * ADC_OVERSAMPLE); + constexpr float scale = 3.3f / (4095 * ADC_OVERSAMPLE); return (float)sum * scale; } @@ -34,9 +38,11 @@ AnalogResult AnalogSample() { adcConvert(&ADCD1, &convGroup, adcBuffer, ADC_OVERSAMPLE); + constexpr float nernstInputGain = 1 / 2.7f; + return { - .NernstVoltage = AverageSamples(adcBuffer, 0), + .NernstVoltage = AverageSamples(adcBuffer, 0) * nernstInputGain, .VirtualGroundVoltage = AverageSamples(adcBuffer, 1), .PumpCurrentVoltage = AverageSamples(adcBuffer, 2), }; diff --git a/firmware/can.cpp b/firmware/can.cpp index ad44ba1..f8c8bef 100644 --- a/firmware/can.cpp +++ b/firmware/can.cpp @@ -5,8 +5,8 @@ static const CANConfig canConfig500 = { - CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP, - CAN_BTR_SJW(0) | CAN_BTR_BRP(5) | CAN_BTR_TS1(12) | CAN_BTR_TS2(1), + CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP | CAN_MCR_NART, + CAN_BTR_SJW(0) | CAN_BTR_BRP(5) | CAN_BTR_TS1(12) | CAN_BTR_TS2(1) | CAN_BTR_LBKM, }; void InitCan() diff --git a/firmware/cfg/board.h b/firmware/cfg/board.h index caedb9f..1da82ba 100644 --- a/firmware/cfg/board.h +++ b/firmware/cfg/board.h @@ -282,6 +282,8 @@ * PB3 - SPI SCK LED_GREEN (alternate 0). * PB4 - SPI MISO (alternate 0). * PB5 - SPI MOSI (alternate 0). + * PB6 - Debug UART TX (alternate 0). + * PB7 - Debug UART RX (alternate 0). * PB8 - Nernst ESR driver (GPIO) (output pushpull). */ #define VAL_GPIOB_MODER (PIN_MODE_INPUT(GPIOB_PIN0) | \ @@ -290,8 +292,8 @@ PIN_MODE_ALTERNATE(GPIOB_PIN3) | \ PIN_MODE_ALTERNATE(GPIOB_PIN4) | \ PIN_MODE_ALTERNATE(GPIOB_PIN5) | \ - PIN_MODE_INPUT(GPIOB_PIN6) | \ - PIN_MODE_INPUT(GPIOB_PIN7) | \ + PIN_MODE_ALTERNATE(GPIOB_PIN6) | \ + PIN_MODE_ALTERNATE(GPIOB_PIN7) | \ PIN_MODE_OUTPUT(GPIOB_PIN8) | \ PIN_MODE_INPUT(GPIOB_PIN9) | \ PIN_MODE_INPUT(GPIOB_PIN10) | \ diff --git a/firmware/cfg/halconf.h b/firmware/cfg/halconf.h index 29dc364..f0f8e3b 100644 --- a/firmware/cfg/halconf.h +++ b/firmware/cfg/halconf.h @@ -100,7 +100,7 @@ * @brief Enables the UART subsystem. */ #if !defined(HAL_USE_UART) || defined(__DOXYGEN__) -#define HAL_USE_UART FALSE +#define HAL_USE_UART TRUE #endif /** diff --git a/firmware/cfg/mcuconf.h b/firmware/cfg/mcuconf.h index fa69762..14d0fc2 100644 --- a/firmware/cfg/mcuconf.h +++ b/firmware/cfg/mcuconf.h @@ -112,7 +112,7 @@ /* * PWM driver system settings. */ -#define STM32_PWM_USE_ADVANCED FALSE +#define STM32_PWM_USE_ADVANCED TRUE #define STM32_PWM_USE_TIM1 TRUE #define STM32_PWM_USE_TIM2 FALSE #define STM32_PWM_USE_TIM3 TRUE @@ -145,7 +145,7 @@ /* * UART driver system settings. */ -#define STM32_UART_USE_USART1 FALSE +#define STM32_UART_USE_USART1 TRUE #define STM32_UART_USE_USART2 FALSE #define STM32_UART_USART1_DMA_PRIORITY 0 #define STM32_UART_USART2_DMA_PRIORITY 0 diff --git a/firmware/main.cpp b/firmware/main.cpp index 806e80e..f4b9c72 100644 --- a/firmware/main.cpp +++ b/firmware/main.cpp @@ -1,16 +1,33 @@ #include "ch.h" #include "hal.h" +#include "chprintf.h" -#include "analog_input.h" #include "can.h" #include "pwm.h" +#include "pump_dac.h" +#include "sampling.h" // 400khz / 1024 = 390hz PWM // TODO: this is wired to an inverted output, what do? -Pwm heaterPwm(PWMD1, 1, 400000, 1024); +Pwm heaterPwm(PWMD1, 0, 400'000, 1024); -// 48MHz / 1024 = 46.8khz PWM -Pwm pumpDac(PWMD3, 1, 48000000, 1024); +static const UARTConfig uartCfg = +{ + .txend1_cb = nullptr, + .txend2_cb = nullptr, + .rxend_cb = nullptr, + .rxchar_cb = nullptr, + .rxerr_cb = nullptr, + .timeout_cb = nullptr, + + .timeout = 0, + .speed = 500000, + .cr1 = 0, + .cr2 = 0, + .cr3 = 0, +}; + +char strBuffer[200]; /* * Application entry point. @@ -19,19 +36,36 @@ int main() { halInit(); chSysInit(); + StartSampling(); + + InitPumpDac(); + InitCan(); + uartStart(&UARTD1, &uartCfg); + heaterPwm.Start(); - pumpDac.Start(); heaterPwm.SetDuty(0.2f); - pumpDac.SetDuty(0.4f); - while (true) { + + /*for (int i = 0; i < 500; i++) { + SetPumpCurrentTarget(current); + chThdSleepMilliseconds(50); + auto result = AnalogSample(); - // dummy data - SendCanData(0.5f, 300); - chThdSleepMilliseconds(10); + //size_t writeCount = chsnprintf(strBuffer, 200, "I: %d\t\tVM: %.3f\tIp: %.3f\n", current, result.VirtualGroundVoltage, result.PumpCurrentVoltage); + size_t writeCount = chsnprintf(strBuffer, 200, "%d\t%.4f\n", current, result.PumpCurrentVoltage); + uartStartSend(&UARTD1, writeCount, strBuffer); + + //current += 10; + }*/ + + while(1) { + size_t writeCount = chsnprintf(strBuffer, 200, "%.4f\t%.2f\n", GetNernstDc() * 1000, GetSensorInternalResistance()); + uartStartSend(&UARTD1, writeCount, strBuffer); + + chThdSleepMilliseconds(5); } } diff --git a/firmware/pump_dac.cpp b/firmware/pump_dac.cpp new file mode 100644 index 0000000..4ec2568 --- /dev/null +++ b/firmware/pump_dac.cpp @@ -0,0 +1,29 @@ +#include "pump_dac.h" +#include "pwm.h" + +#include "hal.h" + +// 48MHz / 1024 = 46.8khz PWM +static Pwm pumpDac(PWMD3, 0, 48'000'000, 1024); + +void InitPumpDac() +{ + pumpDac.Start(); + + // Set zero current to start - sensor can be damaged if current flowing + // while warming up + SetPumpCurrentTarget(0); +} + +void SetPumpCurrentTarget(int32_t microampere) +{ + // 47 ohm resistor + // 0.147 gain + // effective resistance of 317 ohms + float volts = 0.000321162f * microampere; + + // offset by + volts += 1.65f; + + pumpDac.SetDuty(volts / 3.3f); +} diff --git a/firmware/pump_dac.h b/firmware/pump_dac.h new file mode 100644 index 0000000..c5123d7 --- /dev/null +++ b/firmware/pump_dac.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void InitPumpDac(); +void SetPumpCurrentTarget(int32_t microamperes); diff --git a/firmware/pwm.cpp b/firmware/pwm.cpp index db7a224..2df18f4 100644 --- a/firmware/pwm.cpp +++ b/firmware/pwm.cpp @@ -5,7 +5,7 @@ Pwm::Pwm(PWMDriver& driver, uint8_t channel, uint32_t counterFrequency, uint32_t counterPeriod) : m_driver(&driver) , m_channel(channel) - , m_counterFrequency(m_counterFrequency) + , m_counterFrequency(counterFrequency) , m_counterPeriod(counterPeriod) { } @@ -17,10 +17,10 @@ void Pwm::Start() m_counterPeriod, nullptr, { - {PWM_OUTPUT_ACTIVE_HIGH, nullptr}, - {PWM_OUTPUT_ACTIVE_HIGH, nullptr}, - {PWM_OUTPUT_ACTIVE_HIGH, nullptr}, - {PWM_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 diff --git a/firmware/sampling.cpp b/firmware/sampling.cpp new file mode 100644 index 0000000..9a6f833 --- /dev/null +++ b/firmware/sampling.cpp @@ -0,0 +1,81 @@ +#include "sampling.h" + +#include "ch.h" +#include "hal.h" + +#include "analog_input.h" + +// Stored results +float nernstAc = 0; +float nernstDc = 0; +volatile float pumpCurrentSenseVoltage = 0; + +constexpr float f_abs(float x) +{ + return x > 0 ? x : -x; +} + +static THD_WORKING_AREA(waSamplingThread, 256); + +static void SamplingThread(void*) +{ + float r_2 = 0; + float r_3 = 0; + + while(true) + { + // First toggle the pin + palTogglePad(GPIOB, 8); + auto result = AnalogSample(); + + float r_1 = result.NernstVoltage; + + // r2_opposite_phase estimates where the previous sample would be had we not been toggling + // AKA the absolute value of the difference between r2_opposite_phase and r2 is the amplitude + // of the AC component on the nernst voltage. We have to pull this trick so as to use the past 3 + // samples to cancel out any slope in the DC (aka actual nernst cell output) from the AC measurement + // See firmware/sampling.png for a drawing of what's going on here + float r2_opposite_phase = (r_1 + r_3) / 2; + + // Compute AC (difference) and DC (average) components + nernstAc = f_abs(r2_opposite_phase - r_2); + nernstDc = (r2_opposite_phase + r_2) / 2; + + pumpCurrentSenseVoltage = 0.8f * pumpCurrentSenseVoltage + 0.2f * (result.PumpCurrentVoltage - 1.65f); + + // Shift history over by one + r_3 = r_2; + r_2 = r_1; + } +} + +void StartSampling() +{ + adcStart(&ADCD1, nullptr); + chThdCreateStatic(waSamplingThread, sizeof(waSamplingThread), NORMALPRIO + 5, SamplingThread, nullptr); +} + +float GetNernstAc() +{ + return nernstAc; +} + +float GetSensorInternalResistance() +{ + // Sensor is the lowside of a divider, top side is 22k, and 3.3v AC pk-pk is injected + return 22000 / (3.3f / GetNernstAc() - 1); +} + +float GetNernstDc() +{ + return nernstDc; +} + +float GetPumpNominalCurrent() +{ + // Gain is 10x, then a 61.9 ohm resistor + // Effective resistance with the gain is 619 ohms + // 1000 is to convert to milliamperes + constexpr float ratio = 1000 / 619.0f; + return pumpCurrentSenseVoltage * ratio; +} diff --git a/firmware/sampling.h b/firmware/sampling.h new file mode 100644 index 0000000..037d9a4 --- /dev/null +++ b/firmware/sampling.h @@ -0,0 +1,9 @@ +#pragma once + +void StartSampling(); + +float GetNernstAc(); +float GetSensorInternalResistance(); +float GetNernstDc(); + +float GetPumpNominalCurrent(); diff --git a/firmware/sampling.png b/firmware/sampling.png new file mode 100644 index 0000000..f479657 Binary files /dev/null and b/firmware/sampling.png differ