Commit 2615d168 authored by Clément Foucher's avatar Clément Foucher
Browse files

Add DAC driver module.

parent 9b4653fc
if(CONFIG_OWNTECH_DAC_DRIVER)
# Select directory to add to the include path
zephyr_include_directories(./public_include)
# Define the current folder as a Zephyr library
zephyr_library()
# Select source files to be compiled
zephyr_library_sources(
./src/stm32_dac_driver.c
./src/owntech_dac_configure.c
)
endif()
config OWNTECH_DAC_DRIVER
bool "Enable OwnTech DAC driver"
default y
name: owntech_dac_driver
build:
cmake: zephyr
kconfig: zephyr/Kconfig
/*
* Copyright (c) 2021 LAAS-CNRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGLPV2.1
*/
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*/
#ifndef DAC_H_
#define DAC_H_
// Zephyr
#include <zephyr.h>
#include <device.h>
#ifdef __cplusplus
extern "C" {
#endif
// Configuration types
typedef enum
{
dac_function_noise,
dac_function_triangle,
dac_function_sawtooth
} dac_function_t;
typedef struct
{
dac_function_t dac_function;
uint32_t trigger_source;
uint32_t step_trigger_source;
uint32_t polarity;
uint32_t reset_data;
uint32_t step_data;
} dac_function_config_t;
typedef struct
{
uint32_t pin_connect;
uint32_t pin_buffer_enable;
} dac_pin_config_t;
// API
typedef void (*dac_api_setconstvalue) (const struct device* dev, uint8_t channel, uint32_t value);
typedef void (*dac_api_setfunction) (const struct device* dev, uint8_t channel, const dac_function_config_t* config);
typedef void (*dac_api_pinconfigure) (const struct device* dev, uint8_t channel, const dac_pin_config_t* config);
typedef void (*dac_api_start) (const struct device* dev, uint8_t channel);
typedef void (*dac_api_stop) (const struct device* dev, uint8_t channel);
__subsystem struct dac_driver_api
{
dac_api_setconstvalue setconstvalue;
dac_api_setfunction setfunction;
dac_api_pinconfigure pinconfigure;
dac_api_start start;
dac_api_stop stop;
};
static inline void dac_set_const_value(const struct device* dev, uint8_t channel, uint32_t value)
{
const struct dac_driver_api* api = (const struct dac_driver_api*)(dev->api);
api->setconstvalue(dev, channel, value);
}
static inline void dac_set_function(const struct device* dev, uint8_t channel, const dac_function_config_t* function_config)
{
const struct dac_driver_api* api = (const struct dac_driver_api*)(dev->api);
api->setfunction(dev, channel, function_config);
}
static inline void dac_pin_configure(const struct device* dev, uint8_t channel, const dac_pin_config_t* pin_config)
{
const struct dac_driver_api* api = (const struct dac_driver_api*)(dev->api);
api->pinconfigure(dev, channel, pin_config);
}
static inline void dac_start(const struct device* dev, uint8_t channel)
{
const struct dac_driver_api* api = (const struct dac_driver_api*)(dev->api);
api->start(dev, channel);
}
static inline void dac_stop(const struct device* dev, uint8_t channel)
{
const struct dac_driver_api* api = (const struct dac_driver_api*)(dev->api);
api->stop(dev, channel);
}
/////
// Owntech-specific config
#define DAC1_LABEL DT_PROP(DT_NODELABEL(dac1), label)
#define DAC2_LABEL DT_PROP(DT_NODELABEL(dac2), label)
#define DAC3_LABEL DT_PROP(DT_NODELABEL(dac3), label)
void owntech_dac_dac2_constant_init();
void owntech_dac_dac1_dac3_current_mode_init();
#ifdef __cplusplus
}
#endif
#endif // DAC_H_
/*
* Copyright (c) 2021 LAAS-CNRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGLPV2.1
*/
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*/
// Zephyr
#include <zephyr.h>
// STM32LL
#include <stm32_ll_dac.h>
// Owntech driver
#include "dac.h"
// TODO: this function must *always* be run.
// Write a driver to load on startup without user action?
void owntech_dac_dac2_constant_init()
{
const struct device* dac2 = device_get_binding(DAC2_LABEL);
dac_pin_config_t pin_config =
{
.pin_connect = LL_DAC_OUTPUT_CONNECT_GPIO,
.pin_buffer_enable = LL_DAC_OUTPUT_BUFFER_ENABLE
};
dac_set_const_value(dac2, 1, 2048U);
dac_pin_configure(dac2, 1, &pin_config);
dac_start(dac2, 1);
}
void owntech_dac_dac1_dac3_current_mode_init()
{
const struct device* dac1 = device_get_binding(DAC1_LABEL);
const struct device* dac3 = device_get_binding(DAC3_LABEL);
dac_function_config_t function_config =
{
.dac_function = dac_function_sawtooth,
.trigger_source = LL_DAC_TRIG_EXT_HRTIM_RST_TRG1,
.step_trigger_source = LL_DAC_TRIG_EXT_HRTIM_STEP_TRG1,
.polarity = LL_DAC_SAWTOOTH_POLARITY_DECREMENT,
.reset_data = 4000,
.step_data = 200
};
dac_pin_config_t pin_config =
{
.pin_connect = LL_DAC_OUTPUT_CONNECT_INTERNAL,
.pin_buffer_enable = LL_DAC_OUTPUT_BUFFER_DISABLE
};
// DAC 1
dac_set_function(dac1, 1, &function_config);
dac_pin_configure(dac1, 1, &pin_config);
dac_start(dac1, 1);
// DAC 3
function_config.trigger_source = LL_DAC_TRIG_EXT_HRTIM_RST_TRG2;
function_config.step_trigger_source = LL_DAC_TRIG_EXT_HRTIM_STEP_TRG2;
dac_set_function(dac3, 1, &function_config);
dac_pin_configure(dac3, 1, &pin_config);
dac_start(dac3, 1);
}
/*
* Copyright (c) 2021 LAAS-CNRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGLPV2.1
*/
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*/
// Zephyr
#include <zephyr.h>
// STM32 LL
#include <stm32_ll_dac.h>
#include <stm32_ll_gpio.h>
#include <stm32_ll_bus.h>
// Current file header
#include "stm32_dac_driver.h"
/////
// Init function
static int dac_stm32_init(const struct device* dev)
{
DAC_TypeDef* dac_dev = ((struct stm32_dac_driver_data*)dev->data)->dac_struct;
if (dac_dev == DAC1)
{
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_DAC1);
}
else if (dac_dev == DAC2)
{
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_DAC2);
}
else if (dac_dev == DAC3)
{
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_DAC3);
}
return 0;
}
/////
// API
static const struct dac_driver_api dac_funcs =
{
.setconstvalue = dac_stm32_set_const_value,
.setfunction = dac_stm32_set_function,
.pinconfigure = dac_stm32_pin_configure,
.start = dac_stm32_start,
.stop = dac_stm32_stop
};
static void dac_stm32_set_const_value(const struct device* dev, uint8_t channel, uint32_t value)
{
struct stm32_dac_driver_data* data = (struct stm32_dac_driver_data*)dev->data;
DAC_TypeDef* dac_dev = data->dac_struct;
uint8_t dac_channel = __LL_DAC_DECIMAL_NB_TO_CHANNEL(channel);
if (data->dac_mode != dac_mode_constant)
{
data->dac_mode = dac_mode_constant;
LL_DAC_SetSignedFormat(dac_dev, dac_channel, LL_DAC_SIGNED_FORMAT_DISABLE);
LL_DAC_SetWaveAutoGeneration(dac_dev, dac_channel, LL_DAC_WAVE_AUTO_GENERATION_NONE);
LL_DAC_DisableTrigger(dac_dev, dac_channel);
LL_DAC_DisableDMADoubleDataMode(dac_dev, dac_channel);
}
LL_DAC_ConvertData12RightAligned(dac_dev, dac_channel, value);
}
static void dac_stm32_set_function(const struct device* dev, uint8_t channel, const dac_function_config_t* function_config)
{
struct stm32_dac_driver_data* data = (struct stm32_dac_driver_data*)dev->data;
DAC_TypeDef* dac_dev = data->dac_struct;
uint8_t dac_channel = __LL_DAC_DECIMAL_NB_TO_CHANNEL(channel);
data->dac_mode = dac_mode_function;
if (function_config->dac_function == dac_function_sawtooth)
{
LL_DAC_SetSignedFormat(dac_dev, dac_channel, LL_DAC_SIGNED_FORMAT_DISABLE);
LL_DAC_SetWaveAutoGeneration(dac_dev, dac_channel, LL_DAC_WAVE_AUTO_GENERATION_SAWTOOTH);
LL_DAC_SetWaveSawtoothResetTriggerSource(dac_dev, dac_channel, function_config->trigger_source);
LL_DAC_SetWaveSawtoothStepTriggerSource(dac_dev, dac_channel, function_config->step_trigger_source);
LL_DAC_SetWaveSawtoothPolarity(dac_dev, dac_channel, function_config->polarity);
LL_DAC_SetWaveSawtoothResetData(dac_dev, dac_channel, function_config->reset_data);
LL_DAC_SetWaveSawtoothStepData(dac_dev, dac_channel, function_config->step_data);
LL_DAC_EnableTrigger(dac_dev, dac_channel);
LL_DAC_DisableDMADoubleDataMode(dac_dev, dac_channel);
}
}
static void dac_stm32_pin_configure(const struct device* dev, uint8_t channel, const dac_pin_config_t* pin_config)
{
struct stm32_dac_driver_data* data = (struct stm32_dac_driver_data*)dev->data;
DAC_TypeDef* dac_dev = data->dac_struct;
uint8_t dac_channel = __LL_DAC_DECIMAL_NB_TO_CHANNEL(channel);
LL_DAC_ConfigOutput(dac_dev, dac_channel, LL_DAC_OUTPUT_MODE_NORMAL, pin_config->pin_buffer_enable, pin_config->pin_connect);
}
static void dac_stm32_start(const struct device* dev, uint8_t channel)
{
struct stm32_dac_driver_data* data = (struct stm32_dac_driver_data*)dev->data;
DAC_TypeDef* dac_dev = data->dac_struct;
uint8_t dac_channel = __LL_DAC_DECIMAL_NB_TO_CHANNEL(channel);
LL_DAC_Enable(dac_dev, dac_channel);
while (LL_DAC_IsReady(dac_dev, dac_channel) == 0)
{
// Wait
}
}
static void dac_stm32_stop(const struct device* dev, uint8_t channel)
{
struct stm32_dac_driver_data* data = (struct stm32_dac_driver_data*)dev->data;
DAC_TypeDef* dac_dev = data->dac_struct;
uint8_t dac_channel = __LL_DAC_DECIMAL_NB_TO_CHANNEL(channel);
LL_DAC_Disable(dac_dev, dac_channel);
}
/////
// Device definitions
// DAC 1
#if DT_NODE_HAS_STATUS(DAC1_NODELABEL, okay)
struct stm32_dac_driver_data dac1_data =
{
.dac_struct = DAC1,
.dac_mode = dac_mode_unset
};
DEVICE_DT_DEFINE(DAC1_NODELABEL,
dac_stm32_init,
device_pm_control_nop,
&dac1_data,
NULL,
PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&dac_funcs
);
#endif // DAC 1
// DAC 2
#if DT_NODE_HAS_STATUS(DAC2_NODELABEL, okay)
struct stm32_dac_driver_data dac2_data =
{
.dac_struct = DAC2,
.dac_mode = dac_mode_unset
};
DEVICE_DT_DEFINE(DAC2_NODELABEL,
dac_stm32_init,
device_pm_control_nop,
&dac2_data,
NULL,
PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&dac_funcs
);
#endif // DAC 2
// DAC 3
#if DT_NODE_HAS_STATUS(DAC3_NODELABEL, okay)
struct stm32_dac_driver_data dac3_data =
{
.dac_struct = DAC3,
.dac_mode = dac_mode_unset
};
DEVICE_DT_DEFINE(DAC3_NODELABEL,
dac_stm32_init,
device_pm_control_nop,
&dac3_data,
NULL,
PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&dac_funcs
);
#endif // DAC 3
/*
* Copyright (c) 2021 LAAS-CNRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGLPV2.1
*/
/**
* @author Clément Foucher <clement.foucher@laas.fr>
*/
#ifndef STM32_DAC_DRIVER_H_
#define STM32_DAC_DRIVER_H_
// Zephyr
#include <device.h>
// Public API
#include "dac.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DAC1_NODELABEL DT_NODELABEL(dac1)
#define DAC2_NODELABEL DT_NODELABEL(dac2)
#define DAC3_NODELABEL DT_NODELABEL(dac3)
typedef enum
{
dac_mode_unset,
dac_mode_constant,
dac_mode_function
} dac_mode_t;
struct stm32_dac_driver_data
{
DAC_TypeDef* dac_struct;
dac_mode_t dac_mode;
};
static int dac_stm32_init(const struct device* dev);
static void dac_stm32_set_const_value(const struct device* dev, uint8_t channel, uint32_t value);
static void dac_stm32_set_function(const struct device* dev, uint8_t channel, const dac_function_config_t* function_config);
static void dac_stm32_pin_configure(const struct device* dev, uint8_t channel, const dac_pin_config_t* pin_config);
static void dac_stm32_start(const struct device* dev, uint8_t channel);
static void dac_stm32_stop(const struct device* dev, uint8_t channel);
#ifdef __cplusplus
}
#endif
#endif // STM32_DAC_DRIVER_H_
......@@ -84,3 +84,19 @@
&dmamux1 {
status = "okay";
};
/*******/
/* DAC */
/*******/
&dac1 {
status = "okay";
};
&dac2 {
status = "okay";
};
&dac3 {
status = "okay";
};
......@@ -45,3 +45,4 @@ CONFIG_ASSERT=y
#CONFIG_OWNTECH_NGND_DRIVER=n
#CONFIG_OWNTECH_DATA_ACQUISITION=n
#CONFIG_OWNTECH_VREFBUF_DRIVER=n
#CONFIG_OWNTECH_DAC_DRIVER=n
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment