Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/core/io/ADC.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ class ADC {
*/
virtual float readPercentage() = 0;

/**
* Set the reference voltage for the ADC
*
* @param[in] vref The reference voltage in volts (e.g., 3.3, 5.0)
*/
virtual void setVref(float vref) = 0;

/**
* Get the current reference voltage for the ADC
*
* @return The reference voltage in volts
*/
virtual float getVref() const = 0;

protected:
/// The pin the ADC is attached to
Pin pin;
Expand Down
10 changes: 8 additions & 2 deletions include/core/io/platform/f3xx/ADCf3xx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ class ADCf3xx : public ADC {

float readPercentage();

void setVref(float vref);

float getVref() const;

private:
// Max number of channels supported by the ADC
static constexpr uint8_t MAX_CHANNELS = 15;
// Positive reference voltage of the ADC. Needs to be updated based on the hardware configuration
static constexpr float VREF_POS = 3.3;
// Default positive reference voltage of the ADC. Can be updated via setVref()
Comment thread
oleglazari marked this conversation as resolved.
Outdated
static constexpr float DEFAULT_VREF_POS = 3.3;
// Max value for a 12 bit ADC reading (2^12 - 1)
static constexpr uint32_t MAX_RAW = 4095;
// Current reference voltage (can be modified at runtime)
static float vref_voltage;
/// This is static since the STM32F3xx only has a single ADC which
/// supports multiple channels. The ADC will be initialized once then
/// each channel will be added on.
Expand Down
10 changes: 8 additions & 2 deletions include/core/io/platform/f4xx/ADCf4xx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,21 @@ class ADCf4xx : public ADC {

float readPercentage();

void setVref(float vref);

float getVref() const;

private:
// Max number of channels supported by the ADC
static constexpr uint8_t MAX_CHANNELS = 16;
// Number of supported ADC Peripherals
static constexpr uint8_t NUM_ADCS = 3;
// Positive reference voltage of the ADC. Needs to be updated based on the hardware configuration
static constexpr float VREF_POS = 3.3;
// Default positive reference voltage of the ADC. Can be updated via setVref()
Comment thread
oleglazari marked this conversation as resolved.
Outdated
static constexpr float DEFAULT_VREF_POS = 3.3;
// Max value for a 12 bit ADC reading (2^12 - 1)
static constexpr uint32_t MAX_RAW = 4095;
// Current reference voltage (can be modified at runtime)
static float vref_voltage;
// Flag to indicate if the timer has been initialized
static bool timerInit;
// Timer handle for TIM8, used to configure and control the timer instance
Expand Down
1 change: 1 addition & 0 deletions samples/adc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/evt-core_build.cmake)

make_exe(single_adc single_adc.cpp)
make_exe(multi_adc multi_adc.cpp)
make_exe(vref_demo vref_demo.cpp)
4 changes: 4 additions & 0 deletions samples/adc/multi_adc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ int main() {
io::ADC& adc0 = io::getADC<io::Pin::PA_0, io::ADCPeriph::ONE>();
io::ADC& adc1 = io::getADC<io::Pin::PC_4, io::ADCPeriph::ONE>();

// Optional: Set custom VREF voltage (default is 3.3V)
// adc0.setVref(5.0f); // For 5V reference
Comment thread
oleglazari marked this conversation as resolved.
Outdated
// adc1.setVref(1.8f); // For 1.8V reference

while (1) {
core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "--------------------");
core::log::LOGGER.log(
Expand Down
42 changes: 31 additions & 11 deletions samples/adc/single_adc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <core/io/ADC.hpp>
#include <core/io/UART.hpp>
#include <core/manager.hpp>
#include <core/utils/log.hpp>
#include <core/utils/time.hpp>

namespace io = core::io;
Expand All @@ -18,24 +17,45 @@ int main() {

io::UART& uart = io::getUART<io::Pin::UART_TX, io::Pin::UART_RX>(9600);

// Set up the logger to catch errors in ADC creation
core::log::LOGGER.setUART(&uart);
core::log::LOGGER.setLogLevel(core::log::Logger::LogLevel::INFO);
// Logger setup removed - using direct UART prints instead
Comment thread
oleglazari marked this conversation as resolved.
Outdated

uart.printf("Starting ADC test\r\n");

time::wait(500);

io::ADC& adc0 = io::getADC<io::Pin::PA_0, io::ADCPeriph::ONE>();

// Optional: Set custom VREF voltage (default is 3.3V)

while (1) {
core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "--------------------");
core::log::LOGGER.log(
core::log::Logger::LogLevel::INFO, "ADC0 : %d mV", static_cast<uint32_t>(adc0.read() * 1000));
core::log::LOGGER.log(
core::log::Logger::LogLevel::INFO, "ADC0: %d%%", static_cast<uint32_t>(adc0.readPercentage() * 100));
core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "ADC0 raw: %d", adc0.readRaw());
core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "--------------------\r\n");
adc0.setVref(1.8f); // For 1.8V reference
uart.printf("1.8V ADC Reference\r\n");
uint32_t voltage_mv = static_cast<uint32_t>(adc0.read() * 1000);
uint32_t percentage = static_cast<uint32_t>(adc0.readPercentage() * 100);
uart.printf("ADC0 : %d mV\r\n", voltage_mv);
uart.printf("ADC0: %d%%\r\n", percentage);
uart.printf("ADC0 raw: %d\r\n", adc0.readRaw());
uart.printf("--------------------\r\n");
time::wait(500);

adc0.setVref(5.0f); // For 5V reference
uart.printf("5V ADC Reference\r\n");
voltage_mv = static_cast<uint32_t>(adc0.read() * 1000);
percentage = static_cast<uint32_t>(adc0.readPercentage() * 100);
uart.printf("ADC0 : %d mV\r\n", voltage_mv);
uart.printf("ADC0: %d%%\r\n", percentage);
uart.printf("ADC0 raw: %d\r\n", adc0.readRaw());
uart.printf("--------------------\r\n");
time::wait(500);

adc0.setVref(3.3f); // For 3.3V reference
uart.printf("3.3V ADC Reference\r\n");
voltage_mv = static_cast<uint32_t>(adc0.read() * 1000);
percentage = static_cast<uint32_t>(adc0.readPercentage() * 100);
uart.printf("ADC0 : %d mV\r\n", voltage_mv);
uart.printf("ADC0: %d%%\r\n", percentage);
uart.printf("ADC0 raw: %d\r\n", adc0.readRaw());
uart.printf("--------------------\r\n");
time::wait(500);
}
}
102 changes: 102 additions & 0 deletions samples/adc/vref_demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* This example demonstrates the VREF functionality of the ADC.
* It shows how to set a custom reference voltage and how it affects
* the voltage readings. It also demonstrates ADC calibration for
* improved accuracy.
*/
#include <core/io/ADC.hpp>
#include <core/io/UART.hpp>
#include <core/manager.hpp>
#include <core/utils/log.hpp>
#include <core/utils/time.hpp>

namespace io = core::io;
namespace time = core::time;

int main() {
// Initialize system
core::platform::init();

io::UART& uart = io::getUART<io::Pin::UART_TX, io::Pin::UART_RX>(9600);

// Set up the logger to catch errors in ADC creation
core::log::LOGGER.setUART(&uart);
core::log::LOGGER.setLogLevel(core::log::Logger::LogLevel::INFO);
Comment thread
oleglazari marked this conversation as resolved.
Outdated

uart.printf("Starting ADC VREF Demo\r\n");

time::wait(500);

io::ADC& adc0 = io::getADC<io::Pin::PA_0, io::ADCPeriph::ONE>();

// Display initial VREF setting
uint32_t vref_mv = static_cast<uint32_t>(adc0.getVref() * 1000);
uart.printf("Initial VREF: %d mV\r\n", vref_mv);

time::wait(1000);

// Test with default VREF (3.3V)
uart.printf("\r\n=== Testing with default VREF (3.3V) ===\r\n");
for (int i = 0; i < 5; i++) {
float voltage = adc0.read();
float percentage = adc0.readPercentage();
uint32_t raw = adc0.readRaw();

uart.printf("Reading %d: %.3f V (%.1f%%, raw: %d)\r\n", i + 1, voltage, percentage * 100, raw);
Comment thread
oleglazari marked this conversation as resolved.
Outdated
time::wait(200);
}

// Change VREF to 5.0V (common for many sensors)
uart.printf("\r\n=== Changing VREF to 5.0V ===\r\n");
adc0.setVref(5.0f);
uart.printf("New VREF: %.2f V\r\n", adc0.getVref());

// Test with new VREF
uart.printf("\r\n=== Testing with new VREF (5.0V) ===\r\n");
for (int i = 0; i < 5; i++) {
float voltage = adc0.read();
float percentage = adc0.readPercentage();
uint32_t raw = adc0.readRaw();

uart.printf("Reading %d: %.3f V (%.1f%%, raw: %d)\r\n", i + 1, voltage, percentage * 100, raw);
time::wait(200);
}

// Change VREF to 1.8V (common for low-voltage systems)
uart.printf("\r\n=== Changing VREF to 1.8V ===\r\n");
adc0.setVref(1.8f);
uart.printf("New VREF: %.2f V\r\n", adc0.getVref());

// Test with new VREF
uart.printf("\r\n=== Testing with new VREF (1.8V) ===\r\n");
for (int i = 0; i < 5; i++) {
float voltage = adc0.read();
float percentage = adc0.readPercentage();
uint32_t raw = adc0.readRaw();

uart.printf("Reading %d: %.3f V (%.1f%%, raw: %d)\r\n", i + 1, voltage, percentage * 100, raw);
time::wait(200);
}

// Demonstrate that raw values don't change, only voltage calculations do
uart.printf("\r\n=== Demonstrating that raw values are independent of VREF ===\r\n");
uart.printf("Note: Raw ADC values should remain the same regardless of VREF setting\r\n");

float test_vrefs[] = {1.0f, 2.5f, 3.3f, 5.0f};
for (float vref : test_vrefs) {
adc0.setVref(vref);
uint32_t raw = adc0.readRaw();
float voltage = adc0.read();
uart.printf("VREF: %.1fV -> Raw: %d, Voltage: %.3fV\r\n", vref, raw, voltage);
time::wait(100);
}

uart.printf("\r\n=== VREF Demo Complete ===\r\n");
uart.printf("The same raw ADC value produces different voltage readings\r\n");
uart.printf("based on the VREF setting, allowing for accurate measurements\r\n");
uart.printf("with different reference voltages.\r\n");

while (1) {
time::wait(1000);
}
}
17 changes: 14 additions & 3 deletions src/core/io/platform/f3xx/ADCf3xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ADC_HandleTypeDef ADCf3xx::halADC = {0};
Pin ADCf3xx::channels[MAX_CHANNELS];
uint16_t ADCf3xx::buffer[MAX_CHANNELS];
DMA_HandleTypeDef ADCf3xx::halDMA = {0};
float ADCf3xx::vref_voltage = DEFAULT_VREF_POS;

ADCf3xx::ADCf3xx(Pin pin, ADCPeriph adcPeriph) : ADC(pin, adcPeriph) {
// Flag representing if the ADC has been configured yet
Expand Down Expand Up @@ -64,7 +65,7 @@ ADCf3xx::ADCf3xx(Pin pin, ADCPeriph adcPeriph) : ADC(pin, adcPeriph) {

float ADCf3xx::read() {
float percentage = readPercentage();
return percentage * VREF_POS;
return percentage * vref_voltage;
}

uint32_t ADCf3xx::readRaw() {
Expand All @@ -81,11 +82,19 @@ float ADCf3xx::readPercentage() {
return static_cast<float>(raw / MAX_RAW);
}

void ADCf3xx::setVref(float vref) {
if (vref > 0.0f) {
vref_voltage = vref;
}
}

float ADCf3xx::getVref() const {
return vref_voltage;
}

void ADCf3xx::initADC(uint8_t num_channels) {
halADC.Instance = ADC1; // Only ADC the F3 supports

// TODO: Figure out ADC calibration

halADC.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // Use AHB clock (8MHz) w/ division for ADC clock
halADC.Init.Resolution = ADC_RESOLUTION_12B;
halADC.Init.DataAlign = ADC_DATAALIGN_RIGHT;
Expand Down Expand Up @@ -213,6 +222,8 @@ bool ADCf3xx::checkSupport(ADCPeriph periph, Channel_Support channelStruct) {
switch (periph) {
case ADCPeriph::ONE:
return channelStruct.adc1;
default:
return false;
}
}

Expand Down
13 changes: 12 additions & 1 deletion src/core/io/platform/f4xx/ADCf4xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ constexpr uint8_t ADC3_SLOT = 2;
ADCf4xx::ADC_State core::io::ADCf4xx::adcArray[NUM_ADCS];
bool core::io::ADCf4xx::timerInit = false;
TIM_HandleTypeDef core::io::ADCf4xx::htim8;
float core::io::ADCf4xx::vref_voltage = DEFAULT_VREF_POS;

ADCf4xx::ADCf4xx(Pin pin, ADCPeriph adcPeriph)
: ADC(pin, adcPeriph), adcState(ADCf4xx::adcArray[getADCNum(adcPeriph)]), adcNum(getADCNum(adcPeriph)) {
Expand Down Expand Up @@ -96,7 +97,7 @@ ADCf4xx::ADCf4xx(Pin pin, ADCPeriph adcPeriph)

float ADCf4xx::read() {
float percentage = readPercentage();
return percentage * VREF_POS;
return percentage * vref_voltage;
}

uint32_t ADCf4xx::readRaw() {
Expand All @@ -113,6 +114,16 @@ float ADCf4xx::readPercentage() {
return static_cast<float>(raw / MAX_RAW);
}

void ADCf4xx::setVref(float vref) {
if (vref > 0.0f) {
vref_voltage = vref;
}
}

float ADCf4xx::getVref() const {
return vref_voltage;
}

void ADCf4xx::initADC(uint8_t num_channels) {
/** Configure the global features of the ADC (Clock, Resolution, Data
* Alignment and number of conversion)
Expand Down