1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/sam0_common: implement periph/dac

The sam0 MCUs all have a DAC peripheral.
The DAC has a resulution of 10 or 12 bits and can have one or two
output channels.

The output pins are always hard-wired to PA2 for DAC0 and PA5 for DAC1
if it exists.

On the same54-xpro I would only get a max value of ~1V when using the
internal reference, so I configured it to use an external voltage reference.

The external reference pin is hard-wired to PA3, so you'll have to connect
that to 3.3V to get results.
This commit is contained in:
Benjamin Valentin 2020-04-28 00:05:59 +02:00
parent 096996ee22
commit bfb3d52a63
5 changed files with 218 additions and 0 deletions

View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2020 Beuth Hochschule für Technik Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_sam0_common
* @{
*
* @file
* @brief Low-level DAC driver implementation
*
* @author Benjamin Valentin <benpicco@beuth-hochschule.de>
*
* @}
*/
#include "cpu.h"
#include "periph/dac.h"
#include "periph/gpio.h"
#define DAC_VAL(in) (in >> (16 - DAC_RES_BITS))
static void _dac_init_clock(dac_t line)
{
sam0_gclk_enable(DAC_CLOCK);
/* GCLK Setup */
#ifdef GCLK_PCHCTRL_CHEN
GCLK->PCHCTRL[DAC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN
| GCLK_PCHCTRL_GEN(DAC_CLOCK);
#else
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN
| GCLK_CLKCTRL_GEN(DAC_CLOCK)
| GCLK_CLKCTRL_ID(DAC_GCLK_ID);
#endif
dac_poweron(line);
}
static inline bool _ext_vref(void)
{
#ifdef DAC_CTRLB_REFSEL_VREFP
return DAC_VREF == DAC_CTRLB_REFSEL_VREFP;
#endif
#ifdef DAC_CTRLB_REFSEL_VREFPU
return (DAC_VREF == DAC_CTRLB_REFSEL_VREFPU) ||
(DAC_VREF == DAC_CTRLB_REFSEL_VREFPB);
#endif
}
static inline void _sync(void)
{
#ifdef DAC_SYNCBUSY_MASK
while (DAC->SYNCBUSY.reg) {}
#else
while (DAC->STATUS.bit.SYNCBUSY) {}
#endif
}
#ifdef DAC_DACCTRL_CCTRL_Msk
static uint32_t _get_CCTRL(uint32_t freq)
{
if (freq < 1200000) {
return DAC_DACCTRL_CCTRL_CC100K;
}
if (freq < 6000000) {
return DAC_DACCTRL_CCTRL_CC1M;
}
if (freq < 12000000) {
return DAC_DACCTRL_CCTRL_CC12M;
}
assert(0);
return 0;
}
#endif
int8_t dac_init(dac_t line)
{
switch (line) {
case 0:
/* DAC0 is always connected to PA2 */
gpio_init(GPIO_PIN(PA, 2), GPIO_OUT);
gpio_init_mux(GPIO_PIN(PA, 2), GPIO_MUX_B);
break;
#ifdef PIN_PA05B_DAC_VOUT1
case 1:
/* DAC1 is always connected to PA5 */
gpio_init(GPIO_PIN(PA, 5), GPIO_OUT);
gpio_init_mux(GPIO_PIN(PA, 5), GPIO_MUX_B);
break;
#endif
default:
return DAC_NOLINE;
}
if (_ext_vref()) {
/* PA3 is external reference voltage */
gpio_init_mux(GPIO_PIN(PA, 3), GPIO_MUX_B);
}
_dac_init_clock(line);
/* Settings can only be changed when DAC is disabled */
DAC->CTRLA.bit.ENABLE = 0;
_sync();
#ifdef DAC_DACCTRL_ENABLE
DAC->DACCTRL[line].reg = DAC_DACCTRL_ENABLE
| _get_CCTRL(sam0_gclk_freq(DAC_CLOCK));
#endif
/* Set Reference Voltage & enable Output if needed */
DAC->CTRLB.reg = DAC_VREF
#ifdef DAC_CTRLB_EOEN
| DAC_CTRLB_EOEN
#endif
;
DAC->CTRLA.bit.ENABLE = 1;
_sync();
return DAC_OK;
}
void dac_set(dac_t line, uint16_t value)
{
#ifdef DAC_SYNCBUSY_DATA1
/* DAC has multiple outputs */
const uint32_t mask = (1 << (DAC_SYNCBUSY_DATA_Pos + line));
while (DAC->SYNCBUSY.reg & mask) {}
DAC->DATA[line].reg = DAC_VAL(value);
#else
/* DAC has only one output */
(void) line;
_sync();
DAC->DATA.reg = DAC_VAL(value);
#endif
}
void dac_poweron(dac_t line)
{
(void) line;
#ifdef PM_APBCMASK_DAC
PM->APBCMASK.reg |= PM_APBCMASK_DAC;
#endif
#ifdef MCLK_APBCMASK_DAC
MCLK->APBCMASK.reg |= MCLK_APBCMASK_DAC;
#endif
#ifdef MCLK_APBDMASK_DAC
MCLK->APBDMASK.reg |= MCLK_APBDMASK_DAC;
#endif
}
void dac_poweroff(dac_t line)
{
(void) line;
#ifdef PM_APBCMASK_DAC
PM->APBCMASK.reg &= ~PM_APBCMASK_DAC;
#endif
#ifdef MCLK_APBCMASK_DAC
MCLK->APBCMASK.reg &= ~MCLK_APBCMASK_DAC;
#endif
#ifdef MCLK_APBDMASK_DAC
MCLK->APBDMASK.reg &= ~MCLK_APBDMASK_DAC;
#endif
}

View File

@ -115,6 +115,17 @@ typedef enum {
} adc_res_t;
/** @} */
#endif /* ndef DOXYGEN */
/**
* @brief The MCU has a 10 bit DAC
*/
#define DAC_RES_BITS (10)
/**
* @brief The MCU has one DAC Output.
*/
#define DAC_NUMOF (1)
#ifdef __cplusplus
}
#endif

View File

@ -69,6 +69,16 @@ enum {
*/
#define SPI_HWCS(x) (UINT_MAX - 1)
/**
* @brief The MCU has a 12 bit DAC
*/
#define DAC_RES_BITS (12)
/**
* @brief The MCU has two DAC outputs.
*/
#define DAC_NUMOF (2)
#ifdef __cplusplus
}
#endif

View File

@ -62,6 +62,16 @@ typedef enum {
#endif /* ndef DOXYGEN */
/** @} */
/**
* @brief The MCU has a 10 bit DAC
*/
#define DAC_RES_BITS (10)
/**
* @brief The MCU has one DAC Output.
*/
#define DAC_NUMOF (1)
#ifdef __cplusplus
}
#endif

View File

@ -62,6 +62,16 @@ typedef enum {
/** @} */
#endif /* ndef DOXYGEN */
/**
* @brief The MCU has a 12 bit DAC
*/
#define DAC_RES_BITS (12)
/**
* @brief The MCU has two DAC outputs.
*/
#define DAC_NUMOF (2)
#ifdef __cplusplus
}
#endif