Commit 41452792 authored by Martin Jäger's avatar Martin Jäger Committed by Carles Cufi
Browse files

drivers: dac: Add support for STM32L0 series



First implementation for STM32L0 series MCUs to be used for testing.

Signed-off-by: default avatarMartin Jäger <martin@libre.solar>
parent 33228f51
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2,4 +2,5 @@

zephyr_library()

zephyr_library_sources_ifdef(CONFIG_DAC_STM32		dac_stm32.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE		dac_handlers.c)
+2 −0
Original line number Diff line number Diff line
@@ -17,4 +17,6 @@ module = DAC
module-str = DAC
source "subsys/logging/Kconfig.template.log_config"

source "drivers/dac/Kconfig.stm32"

endif # DAC
+14 −0
Original line number Diff line number Diff line
# DAC configuration options

# Copyright (c) 2020 Libre Solar Technologies GmbH
#
# SPDX-License-Identifier: Apache-2.0

# Workaround for not being able to have commas in macro arguments
DT_COMPAT_ST_STM32_DAC := st,stm32-dac

config DAC_STM32
	bool "STM32 DAC driver"
	default $(dt_compat_enabled,$(DT_COMPAT_ST_STM32_DAC))
	help
	  Enable the driver implementation for the stm32xx DAC
+167 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2020 Libre Solar Technologies GmbH
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT st_stm32_dac

#include <errno.h>

#include <drivers/dac.h>
#include <device.h>
#include <kernel.h>
#include <init.h>
#include <soc.h>

#define LOG_LEVEL CONFIG_DAC_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(dac_stm32);

#include <drivers/clock_control/stm32_clock_control.h>

/* some low-end MCUs have DAC with only one channel */
#ifdef LL_DAC_CHANNEL_2
#define STM32_CHANNEL_COUNT		2
#else
#define STM32_CHANNEL_COUNT		1
#endif

/* first channel always named 1 */
#define STM32_FIRST_CHANNEL		1

#define CHAN(n)		LL_DAC_CHANNEL_##n
static const u32_t table_channels[] = {
	CHAN(1),
#ifdef LL_DAC_CHANNEL_2
	CHAN(2),
#endif
};

/* Read-only driver configuration */
struct dac_stm32_cfg {
	DAC_TypeDef *base;
	struct stm32_pclken pclken;
};

/* Runtime driver data */
struct dac_stm32_data {
	u8_t channel_count;
	u8_t resolution;
};

static int dac_stm32_write_value(struct device *dev,
					u8_t channel, u32_t value)
{
	struct dac_stm32_data *data = dev->driver_data;
	const struct dac_stm32_cfg *cfg = dev->config->config_info;

	if (channel - STM32_FIRST_CHANNEL >= data->channel_count ||
					channel < STM32_FIRST_CHANNEL) {
		LOG_ERR("Channel %d is not valid", channel);
		return -EINVAL;
	}

	if (data->resolution == 8) {
		LL_DAC_ConvertData8RightAligned(cfg->base,
			table_channels[channel - STM32_FIRST_CHANNEL], value);
	} else if (data->resolution == 12) {
		LL_DAC_ConvertData12RightAligned(cfg->base,
			table_channels[channel - STM32_FIRST_CHANNEL], value);
	}

	return 0;
}

static int dac_stm32_channel_setup(struct device *dev,
				   const struct dac_channel_cfg *channel_cfg)
{
	struct dac_stm32_data *data = dev->driver_data;
	const struct dac_stm32_cfg *cfg = dev->config->config_info;

	if ((channel_cfg->channel_id - STM32_FIRST_CHANNEL >=
			data->channel_count) ||
			(channel_cfg->channel_id < STM32_FIRST_CHANNEL)) {
		LOG_ERR("Channel %d is not valid", channel_cfg->channel_id);
		return -EINVAL;
	}

	if ((channel_cfg->resolution == 8) ||
			(channel_cfg->resolution == 12)) {
		data->resolution = channel_cfg->resolution;
	} else {
		LOG_ERR("Resolution not supported");
		return -ENOTSUP;
	}

	/* enable output buffer by default */
	LL_DAC_SetOutputBuffer(cfg->base,
		table_channels[channel_cfg->channel_id - STM32_FIRST_CHANNEL],
		LL_DAC_OUTPUT_BUFFER_ENABLE);

	LL_DAC_Enable(cfg->base,
		table_channels[channel_cfg->channel_id - STM32_FIRST_CHANNEL]);

	LOG_DBG("Channel setup succeeded!");

	return 0;
}

static int dac_stm32_init(struct device *dev)
{
	const struct dac_stm32_cfg *cfg = dev->config->config_info;

	/* enable clock for subsystem */
	struct device *clk =
		device_get_binding(STM32_CLOCK_CONTROL_NAME);

	if (clock_control_on(clk,
			     (clock_control_subsys_t *) &cfg->pclken) != 0) {
		return -EIO;
	}

	return 0;
}

static const struct dac_driver_api api_stm32_driver_api = {
	.channel_setup = dac_stm32_channel_setup,
	.write_value = dac_stm32_write_value
};


#define STM32_DAC_INIT(index)						\
									\
static const struct dac_stm32_cfg dac_stm32_cfg_##index = {		\
	.base = (DAC_TypeDef *)DT_INST_REG_ADDR(index),		\
	.pclken = {							\
		.enr = DT_INST_CLOCKS_CELL(index, bits),		\
		.bus = DT_INST_CLOCKS_CELL(index, bus),			\
	},								\
};									\
static struct dac_stm32_data dac_stm32_data_##index = {			\
	.channel_count = STM32_CHANNEL_COUNT				\
};									\
									\
DEVICE_AND_API_INIT(dac_##index, DT_INST_LABEL(index),			\
		    &dac_stm32_init, &dac_stm32_data_##index,		\
		    &dac_stm32_cfg_##index, POST_KERNEL,		\
		    CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,		\
		    &api_stm32_driver_api)

/* DT instance numbering starts from 0, STM DAC from 1 */

#if DT_HAS_DRV_INST(0)
STM32_DAC_INIT(0);
#endif

#if DT_HAS_DRV_INST(1)
STM32_DAC_INIT(1);
#endif

#if DT_HAS_DRV_INST(2)
STM32_DAC_INIT(2);
#endif

#if DT_HAS_DRV_INST(3)
STM32_DAC_INIT(3);
#endif
+5 −0
Original line number Diff line number Diff line
@@ -163,4 +163,9 @@
#define STM32L0_PINMUX_FUNC_PB1_ADC_IN9 \
	STM32_MODER_ANALOG_MODE

#define STM32L0_PINMUX_FUNC_PA4_DAC_OUT1 \
	STM32_MODER_ANALOG_MODE
#define STM32L0_PINMUX_FUNC_PA5_DAC_OUT2 \
	STM32_MODER_ANALOG_MODE

#endif /* ZEPHYR_DRIVERS_PINMUX_STM32_PINMUX_STM32L0_H_ */
Loading