1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-24 22:13:52 +01:00

Merge pull request #4431 from haukepetersen/opt_periph_dac

drivers/periph: updated DAC driver interface
This commit is contained in:
DipSwitch 2016-03-15 09:14:18 +01:00
commit c446091d86
13 changed files with 241 additions and 337 deletions

View File

@ -111,6 +111,13 @@ static const uart_conf_t uart_config[] = {
#define ADC_NUMOF (0)
/** @} */
/**
* @brief DAC configuration
* @{
*/
#define DAC_NUMOF (0)
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -114,23 +114,18 @@ extern "C" {
/** @} */
/**
* @name DAC configuration
* @brief DAC configuration
*
* We need to define the following fields:
* PIN, DAC channel
* @{
*/
#define DAC_NUMOF (1U)
#define DAC_0_EN 1
#define DAC_MAX_CHANNELS 2
#define DAC_CONFIG { \
{GPIO_PIN(PORT_A, 4), 0}, \
{GPIO_PIN(PORT_A, 5), 1} \
}
/* DAC 0 configuration */
#define DAC_0_DEV DAC
#define DAC_0_CHANNELS 2
#define DAC_0_CLKEN() (RCC->APB1ENR |= (RCC_APB1ENR_DACEN))
#define DAC_0_CLKDIS() (RCC->APB1ENR &= ~(RCC_APB1ENR_DACEN))
#define DAC_0_PORT GPIOA
#define DAC_0_PORT_CLKEN() (RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN)
/* DAC 0 channel config */
#define DAC_0_CH0_PIN 4
#define DAC_0_CH1_PIN 5
#define DAC_NUMOF (2)
/** @} */
/**

View File

@ -168,6 +168,13 @@ static const uart_conf_t uart_config[] = {
#define ADC_NUMOF (0)
/** @} */
/**
* @brief DAC configuration
* @{
*/
#define DAC_NUMOF (0)
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -133,23 +133,19 @@ static const uart_conf_t uart_config[] = {
/** @} */
/**
* @name DAC configuration
* @brief DAC configuration
*
* We need to define the following fields:
* PIN, DAC channel
* @{
*/
#define DAC_NUMOF (1U)
#define DAC_0_EN 1
#define DAC_MAX_CHANNELS 2
#define DAC_CONFIG { \
{GPIO_PIN(PORT_A, 4), 0}, \
{GPIO_PIN(PORT_A, 5), 1} \
}
/* DAC 0 configuration */
#define DAC_0_DEV DAC
#define DAC_0_CHANNELS 2
#define DAC_0_CLKEN() (RCC->APB1ENR |= (RCC_APB1ENR_DACEN))
#define DAC_0_CLKDIS() (RCC->APB1ENR &= ~(RCC_APB1ENR_DACEN))
#define DAC_0_PORT GPIOA
#define DAC_0_PORT_CLKEN() (RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN)
/* DAC 0 channel config */
#define DAC_0_CH0_PIN 4
#define DAC_0_CH1_PIN 5
#define DAC_NUMOF (2)
/** @} */
/**
* @name PWM configuration

View File

@ -137,6 +137,14 @@ typedef struct {
uint8_t chan; /**< CPU ADC channel connected to the pin */
} adc_conf_t;
/**
* @brief DAC line configuration data
*/
typedef struct {
gpio_t pin; /**< pin connected to the line */
uint8_t chan; /**< DAC device used for this line */
} dac_conf_t;
/**
* @brief Configure the alternate function for the given pin
*

View File

@ -1,9 +1,10 @@
/*
* Copyright (C) 2014 Simon Brummer
* 2015-2016 Freie Universität 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.
* 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.
*/
/**
@ -13,7 +14,8 @@
* @file
* @brief Low-level DAC driver implementation
*
* @author Simon Brummer<simon.brummer@haw-hamburg.de>
* @author Simon Brummer <simon.brummer@haw-hamburg.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
@ -22,145 +24,55 @@
#include "periph/dac.h"
#include "periph_conf.h"
/* guard in case that no DAC device is defined */
#if DAC_NUMOF
/* only compile this, if the CPU has a DAC */
#ifdef DAC
#define DAC_MAX_12BIT 0x0fff
typedef struct {
uint8_t shift_mod;
} dac_config_t;
dac_config_t dac_config[DAC_NUMOF];
int8_t dac_init(dac_t dev, dac_precision_t precision)
{
DAC_TypeDef *dac = 0;
dac_poweron(dev);
switch (dev) {
#if DAC_0_EN
case DAC_0:
dac = DAC_0_DEV;
DAC_0_PORT_CLKEN();
/* Set Mode to analoge out, disable Pullup Pulldown Resistors for both channels */
DAC_0_PORT->MODER |= (3 << (DAC_0_CH0_PIN * 2) | 3 << (DAC_0_CH1_PIN * 2));
DAC_0_PORT->PUPDR &= ~(3 << (DAC_0_CH0_PIN * 2) | 3 << (DAC_0_CH1_PIN * 2));
break;
/**
* @brief Get the DAC configuration from the board (if configured)
* @{
*/
#ifdef DAC_CONFIG
static const dac_conf_t dac_config[] = DAC_CONFIG;
#else
static const dac_conf_t dac_config[] = {};
#endif
default:
/* Unknown Device */
return -1;
/** @} */
int8_t dac_init(dac_t line)
{
if (line >= DAC_NUMOF) {
return -1;
}
/* Select Shift value to normalize given Value */
switch(precision) {
case DAC_RES_6BIT:
dac_config[dev].shift_mod = 0x06; /* 2^6 << 6 = 2^12 */
break;
case DAC_RES_8BIT:
dac_config[dev].shift_mod = 0x04; /* 2^8 << 4 = 2^12 */
break;
case DAC_RES_10BIT:
dac_config[dev].shift_mod = 0x02; /* 2^10 << 2 = 2^12 */
break;
case DAC_RES_12BIT:
dac_config[dev].shift_mod = 0x00; /* 2^12 << 0 = 2^12 */
break;
/* Not Supported Resolutions */
case DAC_RES_14BIT:
case DAC_RES_16BIT:
default:
dac_poweroff(dev);
return -2;
break;
}
/* Enable Channels, Clear Output */
dac->CR = 0;
dac->CR |= (DAC_CR_EN1 | DAC_CR_EN2);
dac->DHR12R1 = 0;
dac->DHR12R2 = 0;
/* configure pin */
gpio_init_analog(dac_config[line].pin);
/* enable the DAC's clock */
RCC->APB1ENR |= RCC_APB1ENR_DACEN;
/* reset output and enable the line's channel */
dac_set(line, 0);
dac_poweron(line);
return 0;
}
int8_t dac_write(dac_t dev, uint8_t channel, uint16_t value)
void dac_set(dac_t line, uint16_t value)
{
DAC_TypeDef* __attribute__((unused)) dac = 0;
uint16_t __attribute__((unused)) val = value << dac_config[dev].shift_mod;
switch(dev){
#if DAC_0_EN
case DAC_0:
dac = DAC_0_DEV;
if( DAC_MAX_12BIT < val ){
/* Value out of Range */
return -3;
}
switch(channel){
case 0:
dac->DHR12R1 = val;
break;
case 1:
dac->DHR12R2 = val;
break;
/* Invalid Channel */
default:
return -2;
}
break;
#endif
/* Unknown Device */
default:
return -1;
value = (value >> 4); /* scale to 12-bit */
if (dac_config[line].chan) {
DAC->DHR12R2 = value;
}
return 0;
}
int8_t dac_poweron(dac_t dev)
{
switch (dev){
#if DAC_0_EN
case DAC_0:
DAC_0_CLKEN();
break;
#endif
default:
/* Unknown Device */
return -1;
else {
DAC->DHR12R1 = value;
}
return 0;
}
int8_t dac_poweroff(dac_t dev)
void dac_poweron(dac_t line)
{
switch (dev) {
#if DAC_0_EN
case DAC_0:
DAC_0_CLKDIS();
break;
#endif
default:
/* Unknown Device */
return -1;
}
return 0;
DAC->CR |= (1 << (16 * dac_config[line].chan));
}
uint16_t dac_map(dac_t dev, int value, int min, int max)
void dac_poweroff(dac_t line)
{
return dac_mapf(dev, (int) value, (int) min, (int) max);
DAC->CR &= ~(1 << (16 * dac_config[line].chan));
}
uint16_t dac_mapf(dac_t dev, float value, float min, float max)
{
uint16_t val_12_bit = ((value - min) * DAC_MAX_12BIT)/(max-min);
return val_12_bit >> dac_config[dev].shift_mod;
}
#undef DAC_MAX_12BIT
#endif /* DAC_NUMOF */
#endif /* DAC */

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Simon Brummer
* 2015-2016 Freie Universität 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
@ -9,144 +10,116 @@
/**
* @defgroup drivers_periph_dac DAC
* @ingroup drivers_periph
* @brief Low-level DAC peripheral driver
* @brief DAC peripheral driver interface
*
* Similar to the ADC driver interface (@ref drivers_periph_adc), the DAC
* interface uses the concept of lines, corresponds to a tuple of a DAC device
* and a DAC output channel.
*
* The DAC interface expects data to be served as a 16-bit unsigned integer,
* independent of the actual resolution of the hardware device. It is up to the
* DAC driver, to scale the given value to the maximal width that can be
* handled. The device driver should, on the other hand, implement the DAC in a
* way, that it will use the bit width, that comes closest to 16-bit.
*
* This kind of 'auto-scaling' is quite sufficient for this interface, as
* standard DAC peripherals use a fixed conversion resolution internally anyway,
* so that any particular bit-width configuration on this driver level would not
* have much effect...
*
* @{
* @file
* @brief Low-level DAC peripheral driver interface definitions
* @brief DAC peripheral driver interface definition
*
* @author Simon Brummer <simon.brummer@haw-hamburg.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef DAC_H
#define DAC_H
#ifndef PERIPH_DAC_H
#define PERIPH_DAC_H
#include <stdint.h>
#include <limits.h>
#include "periph_cpu.h"
#include "periph_conf.h"
#ifdef __cplusplus
extern "C" {
#endif
/* guard file in case no DAC device is defined */
#if DAC_NUMOF
/**
* @brief Definition avialable DAC devices
*
* Each DAC device is based on a hardware DAC which can have one or more
* independet channels.
* @brief Make sure the number of available DAC lines is defined
* @{
*/
typedef enum {
#if DAC_0_EN
DAC_0 = 0, /**< DAC device 0 */
#ifndef DAC_NUMOF
#define "DAC_NUMOF undefined"
#endif
#if DAC_1_EN
DAC_1 = 1, /**< DAC device 1 */
#endif
#if DAC_2_EN
DAC_2 = 2, /**< DAC device 2 */
#endif
#if DAC_3_EN
DAC_3 = 3, /**< DAC device 3 */
#endif
} dac_t;
/** @} */
/**
* @brief Possilbe DAC precision settings
* @brief Define default DAC type identifier
* @{
*/
typedef enum {
DAC_RES_6BIT = 0, /**< DAC precision: 6 bit */
DAC_RES_8BIT, /**< DAC precision: 8 bit */
DAC_RES_10BIT, /**< DAC precision: 10 bit */
DAC_RES_12BIT, /**< DAC precision: 12 bit */
DAC_RES_14BIT, /**< DAC precision: 14 bit */
DAC_RES_16BIT, /**< DAC precision: 16 bit */
} dac_precision_t;
#ifndef HAVE_DAC_T
typedef unsigned int dac_t;
#endif
/** @} */
/**
* @brief Initialization of a given DAC device
* @brief Default DAC undefined value
* @{
*/
#ifndef DAC_UNDEF
#define DAC_UNDEF (UINT_MAX)
#endif
/** @} */
/**
* @brief Default DAC access macro
* @{
*/
#ifndef DAC_LINE
#define DAC_LINE(x) (x)
#endif
/** @} */
/**
* @brief Initialize the given DAC line
*
* The DAC will be initialized with all possilble channels active.
* On each channel will be initialized with a Zero on it.
*
* @param[in] dev the device to initialize
* @param[in] precision the precision to use for conversion
* @param[in] line DAC line to initialize
*
* @return 0 on success
* @return -1 on unknown DAC Device
* @return -2 on precision not available
* @return -1 on invalid DAC line
*/
int8_t dac_init(dac_t dev, dac_precision_t precision);
int8_t dac_init(dac_t line);
/**
* @brief Write a value onto DAC Device on a given Channel.
*
* @param[in] dev the DAC device to use
* @param[in] channel the channel used for Digital/Analoge conversion
* @param[in] value the value to write onto DAC.
*
* @return 0 on success
* @return -1 on unknown DAC Device
* @return -2 on invalid channel
* @return -3 if value is out of range.
* @param[in] line DAC line to set
* @param[in] value value to set @p line to
*/
int8_t dac_write(dac_t dev, uint8_t channel, uint16_t value);
void dac_set(dac_t line, uint16_t value);
/**
* @brief Enable power for the given DAC Device
* @brief Enable the given DAC line
*
* @param[in] dev the DAC device to power up.
*
* @return 0 on success
* @return -1 on unknown DAC Device
* @param[in] line DAC line to power on
*/
int8_t dac_poweron(dac_t dev);
void dac_poweron(dac_t line);
/**
* @brief Disable power for the given DAC Device
* @brief Disable the given DAC line
*
* @param[in] dev the DAC device to power down
*
* @return 0 on success
* @return -1 on unknown DAC Device
* @param[in] line DAC line to power off
*/
int8_t dac_poweroff(dac_t dev);
void dac_poweroff(dac_t line);
/**
* @brief Helper function to map a given integer range to a valid DAC value.
*
* This function is useful for converting ranges of values to a valid DAC output value.
*
* The min value is assumed to be smaller than max value and value is assumed
* to be between min and max.
*
* @param[in] dev the DAC Device to read precision value from
* @param[in] value the value to map onto min and max
* @param[in] min the lower bound of the target interval
* @param[in] max the upper bound of the target interval
*
* @return the mapped value, in valid DAC range
*/
uint16_t dac_map(dac_t dev, int value, int min, int max);
/**
* @brief Helper function to map a given float value range to a valid DAC value.
*
* @see dac_map
*
* @param[in] dev the DAC Device to read precision value from
* @param[in] value the value to map onto min and max
* @param[in] min the lower bound of the target interval
* @param[in] max the upper bound of the target interval
*
* @return the mapped value, in valid DAC range
*/
uint16_t dac_mapf(dac_t dev, float value, float min, float max);
#endif/* DAC_NUMOF */
#ifdef __cplusplus
}
#endif
#endif /* DAC_H */
#endif /* PERIPH_DAC_H */
/** @} */

View File

@ -7,7 +7,7 @@
*/
/**
* @ingroup sys_adc_util
* @ingroup sys_analog_util
* @{
*
* @file
@ -18,7 +18,7 @@
* @}
*/
#include "adc_util.h"
#include "analog_util.h"
/* keep a max value to ADC resolution mapping for quick access in the ROM */
static const int val_max[] = {
@ -28,7 +28,7 @@ static const int val_max[] = {
[ADC_RES_12BIT] = 0x0fff,
[ADC_RES_14BIT] = 0x3fff,
[ADC_RES_16BIT] = 0xffff
}
};
int adc_util_map(int sample, adc_res_t res, int min, int max)
{

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2015 Freie Universität 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 sys_analog_util
* @{
*
* @file
* @brief DAC utility function implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include <limits.h>
#include "analog_util.h"
uint16_t dac_util_map(int value, int min, int max)
{
return (uint16_t)((value - min) * UINT16_MAX) / (max - min);
}
uint16_t dac_util_mapf(float value, float min, float max)
{
return (uint16_t)(((value - min) * UINT16_MAX) / (max - min));
}

View File

@ -7,19 +7,19 @@
*/
/**
* @defgroup sys_adc_util ADC utilities
* @defgroup sys_analog_util Analog data conversion utilities
* @ingroup sys
* @brief Utility functions for handling ADC samples
* @brief Utility functions for converting analog data samples
*
* @{
* @file
* @brief ADC utility function interfaces
* @brief Analog utility function interfaces
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef ADC_UTIL_H
#define ADC_UTIL_H
#ifndef ANALOG_UTIL_H
#define ANALOG_UTIL_H
#include "periph/adc.h"
@ -59,9 +59,36 @@ int adc_util_map(int sample, adc_res_t res, int min, int max);
*/
float adc_util_mapf(int sample, adc_res_t res, float min, float max);
/**
* @brief Map a value out of the given range to a 16-bit unsigned int
*
* The min value is assumed to be smaller than max value and value is assumed
* to be between min and max.
*
* @param[in] value value to map to a DAC set value
* @param[in] min the lower bound of the source interval
* @param[in] max the upper bound of the source interval
*
* @return the mapped value
*/
uint16_t dac_util_map(int value, int min, int max);
/**
* @brief Helper function to map a given float value range to a valid DAC value.
*
* @see dac_util_map
*
* @param[in] value value to map to a DAC set value
* @param[in] min the lower bound of the source interval
* @param[in] max the upper bound of the source interval
*
* @return the mapped value
*/
uint16_t dac_util_mapf(float value, float min, float max);
#ifdef __cplusplus
}
#endif
#endif /* ADC_UTIL_H */
#endif /* ANALOG_UTIL_H */
/** @} */

View File

@ -3,20 +3,12 @@ About
This is a test application for a digital to analog converter (DAC).
This test application will initialize each configured DAC and one ADC (ADC_O)
device to sample with 10-bit accuracy. The ADC is only initialized if there is
one available on your board.
After initialization, values from 0 to 1000 are converted through the DACs in a
loop. Shortly after the digital to analog conversion of one number, the ADC_0
samples its input signal. The numbers that are given to the DACs and the
numbers that are sampled by the ADC were printed to the STDOUT.
This test application tries to initialize every available DAC line. If
successful, it will output a saw tooth pattern with a period of about 10Hz on
each of the available lines.
Usage
=====
a) Connect an oscilloscope to the DAC pins and look at the ten iteration signal levels
or
b) Connect the ADC input to the DAC outputs successively and compare if the sampled input value correlates with the printed output value at each DAC port.
Connect an oscilloscope to the DAC pins and look at the signal levels (you
should be able to clearly see the saw tooth signal in your scope)

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2014-2016 Freie Universität 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
@ -7,7 +7,7 @@
*/
/**
* @ingroup tests
* @ingroup tests
* @{
*
* @file
@ -21,88 +21,42 @@
#include <stdio.h>
#include "cpu.h"
#include "board.h"
#include "xtimer.h"
#include "periph/dac.h"
#include "periph/adc.h"
#if DAC_NUMOF < 1
#error "Please enable at least 1 DAC device to run this test"
#endif
#define RES DAC_RES_10BIT
#define ADC_RES ADC_RES_10BIT
#define DELAY (1000 * 1000U)
#define MAX_VALUE_STEPS 1000
static int values[DAC_NUMOF][DAC_MAX_CHANNELS];
#define DELAY (100U)
#define STEPS (1000U)
int main(void)
{
uint16_t write_value = 0;
int8_t status_dac_write;
uint32_t last = xtimer_now();
uint16_t val = 0;
uint16_t step = 0xffff / STEPS;
puts("\nRIOT DAC peripheral driver test\n");
puts("This test simply sets each available DAC channel about every 100ms\n\n");
puts("This test application produces a saw tooth signal on each available\n"
"DAC line. The period of the signal should be around 100ms\n");
/* initialize all DAC lines */
for (int i = 0; i < DAC_NUMOF; i++) {
/* initialize result vector */
for (int j = 0; j < DAC_MAX_CHANNELS; j++) {
values[i][j] = -1;
}
/* initialize DAC device */
printf("Initializing DAC_%i @ %i bit resolution", i, (6 + (2* RES)));
if (dac_init(i, RES) == 0) {
puts(" ...[ok]");
}
else {
puts(" ...[failed]");
if (dac_init(DAC_LINE(i)) < 0) {
printf("Error initializing DAC_LINE(%i)\n", i);
return 1;
}
#ifdef ADC_NUMOF
printf("Initializing ADC_LINE(0)");
if (adc_init(ADC_LINE(0)) == 0) {
puts(" ...[ok]");
}
else {
puts(" ...[failed]");
return 1;
printf("Successfully initialized DAC_LINE(%i)\n", i);
}
#endif
}
puts("\n");
puts("");
/* create saw tooth signal */
while (1) {
for (int i = 0; i < DAC_NUMOF; i++) {
for (int j = 0; j < DAC_MAX_CHANNELS; j++) {
/* Write values to DAC */
status_dac_write = dac_write(i, j, write_value);
if (status_dac_write < 0) {
printf("%i: Something went wrong writing DAC\n", status_dac_write);
return -1;
}
#ifdef ADC_NUMOF
/* Read values from ADC */
int sample = adc_sample(ADC_LINE(0), ADC_RES);
if (sample < 0) {
printf("%i: Something went wrong sampling ADC\n", sample);
return -1;
}
printf("Wrote %i Read %i using DAC %i Channel %i and ADC_0 Channel 0\n", write_value, sample, i, j);
#else
printf("Wrote %i to DAC %i Channel %i\n", write_value, i, j);
#endif
}
dac_set(DAC_LINE(i), val);
}
puts("\n");
write_value+=100;
if (write_value >= MAX_VALUE_STEPS) {
write_value = 0;
}
/* sleep a little while */
xtimer_usleep(DELAY);
val += step;
xtimer_usleep_until(&last, DELAY);
}
return 0;