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