Skip to content
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ target_sources(${PROJECT_NAME} PRIVATE
src/core/io/ADC.cpp
src/core/io/CAN.cpp
src/core/io/CANopen.cpp
src/core/io/DAC.cpp
src/core/io/GPIO.cpp
src/core/io/I2C.cpp
src/core/io/PWM.cpp
Expand All @@ -54,6 +55,7 @@ if(COMPDEFS MATCHES "(.*)STM32F3xx(.*)")
src/core/platform/f3xx/stm32f302x8.cpp
src/core/io/platform/f3xx/ADCf3xx.cpp
src/core/io/platform/f3xx/CANf3xx.cpp
src/core/io/platform/f3xx/DACf3xx.cpp
src/core/io/platform/f3xx/GPIOf3xx.cpp
src/core/io/platform/f3xx/I2Cf3xx.cpp
src/core/io/platform/f3xx/PWMf3xx.cpp
Expand All @@ -67,6 +69,7 @@ elseif(COMPDEFS MATCHES "(.*)STM32F4xx(.*)")
src/core/platform/f4xx/stm32f446xx.cpp
src/core/io/platform/f4xx/ADCf4xx.cpp
src/core/io/platform/f4xx/CANf4xx.cpp
src/core/io/platform/f4xx/DACf4xx.cpp
src/core/io/platform/f4xx/GPIOf4xx.cpp
src/core/io/platform/f4xx/I2Cf4xx.cpp
src/core/io/platform/f4xx/PWMf4xx.cpp
Expand Down
63 changes: 63 additions & 0 deletions include/core/io/DAC.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef _EVT_DAC_
#define _EVT_DAC_

#include <stdint.h>

namespace core::io {

// Forward declarations:
// The different pins are hardware specific. Forward declarations to allow
// at compilation time the decision of which pins should be used.
enum class Pin;
enum class DACPeriph;

class DACBase {

public:
/**
* Setup the given pin for DAC (digital to analog) usage
*
* @param[in] pin The pin to setup for DAC
* @param[in] dacPeriph The DAC peripheral being used
*/
DACBase(Pin pin, DACPeriph dacPeriph);

/**
* Set the DAC output value
* @param value The digital value to convert (0-4095 for 12-bit DAC)
*/
virtual void setValue(uint32_t value) = 0;

/**
* Get the current DAC output value
* @return The current digital value being output
*/
virtual uint32_t getValue() const = 0;

/**
* Set the DAC output voltage directly
* @param voltage The desired output voltage in volts
*/
virtual void setVoltage(float voltage) = 0;

/**
* Get the current DAC output voltage
* @return The current output voltage in volts
*/
virtual float getVoltage() const = 0;

protected:
/// The pin the DAC is attached to
Pin pin;
/// The internal DAC being used
DACPeriph dacPeriph;
};

} // namespace core::io

// Convenience typedef - use different name to avoid macro conflict
namespace core::io {
using DigitalToAnalogConverter = DACBase;
}

#endif // _EVT_DAC_
92 changes: 92 additions & 0 deletions include/core/io/platform/f3xx/DACf3xx.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#ifndef _EVT_DACF3XX_H
#define _EVT_DACF3XX_H

#include <HALf3/stm32f3xx.h>
#include <HALf3/stm32f3xx_hal.h>

#include <core/io/DAC.hpp>

namespace core::io {

enum class DACPeriph {
ONE,
TWO
};

class DACf3xx : public DACBase {
public:
/**
* Setup the given pin for DAC usage
*
* @param[in] pin The pin to setup for DAC
* @param[in] dacPeriph The DAC peripheral being used
*/
DACf3xx(Pin pin, DACPeriph dacPeriph);

void setValue(uint32_t value) override;
uint32_t getValue() const override;
void setVoltage(float voltage) override;
float getVoltage() const override;

/// HAL DAC handle for STM32 operations (public for interrupt access)
DAC_HandleTypeDef halDac;

private:
/// Maximum raw DAC value (12-bit resolution)
static constexpr uint32_t MAX_RAW = 4095;
/// Reference voltage for DAC output calculation
static constexpr float VREF_POS = 3.3;
/// Current DAC output value
uint32_t currentValue = 0;
/// DAC channel for this instance
uint32_t channel = 0;

/**
* Bit packed struct to contain the channel along with the DAC peripherals the channel supports
*
* dac1: 1 bit. Support for DAC1 peripheral. 1 for supported, 0 for not supported.
* dac2: 1 bit. Support for DAC2 peripheral. 1 for supported, 0 for not supported.
* channel: 5 bits. The STM32 DAC channel value with said supported DAC peripherals
*/
struct Channel_Support {
uint8_t dac1 : 1;
uint8_t dac2 : 1;
uint8_t channel : 5;
};

/**
* Check if the channel that is being initialized supports the DAC peripheral that it is being initialized on.
*
* @param periph the DAC peripheral being used
* @param channelStruct the struct of the channel with supports to test
* @return true if channel is supported by the DAC peripheral, false otherwise
*/
static bool checkSupport(DACPeriph periph, Channel_Support channelStruct);

/**
* Initialize the DAC peripheral with proper configuration
*/
void initDAC();

/**
* Initialize GPIO pins for DAC functionality
*/
void initGPIO();

/**
* Determine the DAC channel based on the pin
* @return The STM32 DAC channel value
*/
uint32_t getChannelFromPin();

/**
* Get channel support information for a given pin
* @param pin The pin to get support information for
* @return Channel_Support struct with DAC peripheral support information
*/
static Channel_Support getChannelSupport(Pin pin);
};

} // namespace core::io

#endif // _EVT_DACF3XX_H
92 changes: 92 additions & 0 deletions include/core/io/platform/f4xx/DACf4xx.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#ifndef _EVT_DACF4XX_H
#define _EVT_DACF4XX_H

#include <HALf4/stm32f4xx.h>
#include <HALf4/stm32f4xx_hal.h>

#include <core/io/DAC.hpp>

namespace core::io {

enum class DACPeriph {
ONE,
TWO
};

class DACf4xx : public DACBase {
public:
/**
* Setup the given pin for DAC usage
*
* @param[in] pin The pin to setup for DAC
* @param[in] dacPeriph The DAC peripheral being used
*/
DACf4xx(Pin pin, DACPeriph dacPeriph);

void setValue(uint32_t value) override;
uint32_t getValue() const override;
void setVoltage(float voltage) override;
float getVoltage() const override;

/// HAL DAC handle for STM32 operations (public for interrupt access)
DAC_HandleTypeDef halDac;

private:
/// Maximum raw DAC value (12-bit resolution)
static constexpr uint32_t MAX_RAW = 4095;
/// Reference voltage for DAC output calculation
static constexpr float VREF_POS = 3.3;
/// Current DAC output value
uint32_t currentValue = 0;
/// DAC channel for this instance
uint32_t channel = 0;

/**
* Bit packed struct to contain the channel along with the DAC peripherals the channel supports
*
* dac1: 1 bit. Support for DAC1 peripheral. 1 for supported, 0 for not supported.
* dac2: 1 bit. Support for DAC2 peripheral. 1 for supported, 0 for not supported.
* channel: 5 bits. The STM32 DAC channel value with said supported DAC peripherals
*/
struct Channel_Support {
uint8_t dac1 : 1;
uint8_t dac2 : 1;
uint8_t channel : 5;
};

/**
* Check if the channel that is being initialized supports the DAC peripheral that it is being initialized on.
*
* @param periph the DAC peripheral being used
* @param channelStruct the struct of the channel with supports to test
* @return true if channel is supported by the DAC peripheral, false otherwise
*/
static bool checkSupport(DACPeriph periph, Channel_Support channelStruct);

/**
* Initialize the DAC peripheral with proper configuration
*/
void initDAC();

/**
* Initialize GPIO pins for DAC functionality
*/
void initGPIO();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add descriptions for these methods

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chat these methods are like not worth documenting

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am with Diego on this, add at least a little description.


/**
* Determine the DAC channel based on the pin
* @return The STM32 DAC channel value
*/
uint32_t getChannelFromPin();

/**
* Get channel support information for a given pin
* @param pin The pin to get support information for
* @return Channel_Support struct with DAC peripheral support information
*/
static Channel_Support getChannelSupport(Pin pin);
};

} // namespace core::io

#endif // _EVT_DACF4XX_H
25 changes: 25 additions & 0 deletions include/core/manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <core/io/ADC.hpp>
#include <core/io/CAN.hpp>
#include <core/io/DAC.hpp>
#include <core/io/GPIO.hpp>
#include <core/io/I2C.hpp>
#include <core/io/PWM.hpp>
Expand All @@ -14,6 +15,7 @@
#ifdef STM32F3xx
#define ADC_SUPPORTED
#define CAN_SUPPORTED
#define DAC_SUPPORTED
#define GPIO_SUPPORTED
#define I2C_SUPPORTED
#define IWDG_SUPPORTED
Expand All @@ -29,6 +31,7 @@
#include <core/dev/platform/f3xx/Timerf3xx.hpp>
#include <core/io/platform/f3xx/ADCf3xx.hpp>
#include <core/io/platform/f3xx/CANf3xx.hpp>
#include <core/io/platform/f3xx/DACf3xx.hpp>
#include <core/io/platform/f3xx/GPIOf3xx.hpp>
#include <core/io/platform/f3xx/I2Cf3xx.hpp>
#include <core/io/platform/f3xx/PWMf3xx.hpp>
Expand All @@ -40,6 +43,7 @@
#ifdef STM32F4xx
#define ADC_SUPPORTED
#define CAN_SUPPORTED
#define DAC_SUPPORTED
#define GPIO_SUPPORTED
#define I2C_SUPPORTED
#define MCU_SUPPORTED
Expand All @@ -54,6 +58,7 @@
#include <core/dev/platform/f4xx/Timerf4xx.hpp>
#include <core/io/platform/f4xx/ADCf4xx.hpp>
#include <core/io/platform/f4xx/CANf4xx.hpp>
#include <core/io/platform/f4xx/DACf4xx.hpp>
#include <core/io/platform/f4xx/GPIOf4xx.hpp>
#include <core/io/platform/f4xx/I2Cf4xx.hpp>
#include <core/io/platform/f4xx/PWMf4xx.hpp>
Expand Down Expand Up @@ -160,6 +165,26 @@ ADC& getADC() {
}
#endif

/**
* Get an instance of a DAC channel
*
* @param[in] pin The pin to use with the DAC
* @param[in] dacPeriph The DAC peripheral to use
*/
#ifdef DAC_SUPPORTED
template<Pin pin, DACPeriph dacPeriph = DACPeriph::ONE>
DigitalToAnalogConverter& getDAC() {
#ifdef STM32F4xx
static DACf4xx dac(pin, dacPeriph);
return dac;
#endif
#ifdef STM32F3xx
static DACf3xx dac(pin, dacPeriph);
return dac;
#endif
}
#endif

/**
* Get an instance of a CAN interface.
*
Expand Down
1 change: 1 addition & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_subdirectory(button)
add_subdirectory(button_debounce)
add_subdirectory(can)
add_subdirectory(canopen)
add_subdirectory(dac)
add_subdirectory(echo)
add_subdirectory(eeprom)
add_subdirectory(encoder)
Expand Down
3 changes: 3 additions & 0 deletions samples/dac/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/evt-core_build.cmake)

make_exe(dac main.cpp)
Loading