mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #4430 from haukepetersen/opt_periph_adc
drivers/periph: reworked the ADC driver
This commit is contained in:
commit
d66625b6ca
@ -95,19 +95,13 @@ static const timer_conf_t timer_config[] = {
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* The configuration consists simply of a list of channels that should be used
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 4
|
||||
|
||||
/* ADC 0 device configuration */
|
||||
#define ADC_0_CHANNELS 4
|
||||
#define ADC_0_CH0 ADC_CONFIG_PSEL_AnalogInput3
|
||||
#define ADC_0_CH1 ADC_CONFIG_PSEL_AnalogInput4
|
||||
#define ADC_0_CH2 ADC_CONFIG_PSEL_AnalogInput5
|
||||
#define ADC_0_CH3 ADC_CONFIG_PSEL_AnalogInput6
|
||||
#define ADC_CONFIG {3, 4, 5, 6}
|
||||
#define ADC_NUMOF (4)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -93,12 +93,11 @@ extern "C" {
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
* @brief ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF 1
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 12
|
||||
#define ADC_NUMOF (12)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -104,6 +104,13 @@ static const uart_conf_t uart_config[] = {
|
||||
#define UART_NUMOF (sizeof(uart_config) / sizeof(uart_config[0]))
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (0)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -20,7 +20,7 @@
|
||||
#ifndef PERIPH_CONF_H
|
||||
#define PERIPH_CONF_H
|
||||
|
||||
#include "cpu_conf.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -45,6 +45,7 @@ extern "C"
|
||||
#define KINETIS_MCG_PLL_FREQ 60000000
|
||||
|
||||
#define CLOCK_CORECLOCK KINETIS_MCG_PLL_FREQ
|
||||
#define CLOCK_BUSCLOCK (CLOCK_CORECLOCK / 2)
|
||||
/** @} */
|
||||
|
||||
|
||||
@ -102,47 +103,17 @@ extern "C"
|
||||
* @name ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 6
|
||||
static const adc_conf_t adc_config[] = {
|
||||
/* dev, pin, channel */
|
||||
{ ADC0, GPIO_PIN(PORT_B, 10), 14 },
|
||||
{ ADC0, GPIO_PIN(PORT_B, 11), 15 },
|
||||
{ ADC0, GPIO_PIN(PORT_C, 11), 7 },
|
||||
{ ADC0, GPIO_PIN(PORT_C, 10), 6 },
|
||||
{ ADC0, GPIO_PIN(PORT_C, 8), 4 },
|
||||
{ ADC0, GPIO_PIN(PORT_C, 9), 5 },
|
||||
};
|
||||
|
||||
/* ADC 0 configuration */
|
||||
#define ADC_0_DEV ADC1
|
||||
#define ADC_0_MODULE_CLOCK CLOCK_CORECLOCK
|
||||
#define ADC_0_CHANNELS 6
|
||||
#define ADC_0_CLKEN() (SIM->SCGC3 |= (SIM_SCGC3_ADC1_MASK))
|
||||
#define ADC_0_CLKDIS() (SIM->SCGC3 &= ~(SIM_SCGC3_ADC1_MASK))
|
||||
#define ADC_0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK))
|
||||
/* ADC 0 channel 0 pin config */
|
||||
#define ADC_0_CH0_PORT PORTB
|
||||
#define ADC_0_CH0_PIN 10
|
||||
#define ADC_0_CH0_PIN_AF 0
|
||||
#define ADC_0_CH0 14
|
||||
/* ADC 0 channel 1 pin config */
|
||||
#define ADC_0_CH1_PORT PORTB
|
||||
#define ADC_0_CH1_PIN 11
|
||||
#define ADC_0_CH1_PIN_AF 0
|
||||
#define ADC_0_CH1 15
|
||||
/* ADC 0 channel 2 pin config */
|
||||
#define ADC_0_CH2_PORT PORTC
|
||||
#define ADC_0_CH2_PIN 11
|
||||
#define ADC_0_CH2_PIN_AF 0
|
||||
#define ADC_0_CH2 7
|
||||
/* ADC 0 channel 3 pin config */
|
||||
#define ADC_0_CH3_PORT PORTC
|
||||
#define ADC_0_CH3_PIN 10
|
||||
#define ADC_0_CH3_PIN_AF 0
|
||||
#define ADC_0_CH3 6
|
||||
/* ADC 0 channel 4 pin config */
|
||||
#define ADC_0_CH4_PORT PORTC
|
||||
#define ADC_0_CH4_PIN 8
|
||||
#define ADC_0_CH4_PIN_AF 0
|
||||
#define ADC_0_CH4 4
|
||||
/* ADC 0 channel 5 pin config */
|
||||
#define ADC_0_CH5_PORT PORTC
|
||||
#define ADC_0_CH5_PIN 9
|
||||
#define ADC_0_CH5_PIN_AF 0
|
||||
#define ADC_0_CH5 5
|
||||
#define ADC_NUMOF (sizeof(adc_config) / sizeof(adc_config[0]))
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -100,25 +100,17 @@ extern "C" {
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
*
|
||||
* We need to define the following fields:
|
||||
* PIN, device (ADCx), channel
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 2
|
||||
#define ADC_CONFIG { \
|
||||
{GPIO_PIN(PORT_B, 0), 0, 8}, \
|
||||
{GPIO_PIN(PORT_B, 1), 0, 9} \
|
||||
}
|
||||
|
||||
/* ADC 0 configuration */
|
||||
#define ADC_0_DEV ADC1
|
||||
#define ADC_0_CHANNELS 2
|
||||
#define ADC_0_CLKEN() (RCC->APB2ENR |= RCC_APB2ENR_ADC1EN)
|
||||
#define ADC_0_CLKDIS() (RCC->APB2ENR &= ~(RCC_APB2ENR_ADC1EN))
|
||||
#define ADC_0_PORT GPIOB
|
||||
#define ADC_0_PORT_CLKEN() (RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN)
|
||||
/* ADC 0 channel 0 pin config */
|
||||
#define ADC_0_CH0 8
|
||||
#define ADC_0_CH0_PIN 0
|
||||
/* ADC 0 channel 1 pin config */
|
||||
#define ADC_0_CH1 9
|
||||
#define ADC_0_CH1_PIN 1
|
||||
#define ADC_NUMOF (2)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,8 @@
|
||||
#ifndef MULLE_PERIPH_CONF_H_
|
||||
#define MULLE_PERIPH_CONF_H_
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -50,6 +52,9 @@ extern "C"
|
||||
#define CPU_INT_FAST_CLK_HZ 4000000u
|
||||
/** Default System clock value */
|
||||
#define DEFAULT_SYSTEM_CLOCK (CPU_XTAL32k_CLK_HZ * 2929u)
|
||||
|
||||
/* bus clock for the peripherals */
|
||||
#define CLOCK_BUSCLOCK (DEFAULT_SYSTEM_CLOCK / 2)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -135,89 +140,19 @@ extern "C"
|
||||
* @name ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (2U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_1_EN 1
|
||||
#define ADC_MAX_CHANNELS 6
|
||||
static const adc_conf_t adc_config[] = {
|
||||
/* dev, pin, channel */
|
||||
{ ADC0, GPIO_UNDEF, 26 }, /* internal: temperature sensor */
|
||||
{ ADC0, GPIO_UNDEF, 27 }, /* internal: band gap */
|
||||
{ ADC0, GPIO_UNDEF, 29 }, /* internal: V_REFSH */
|
||||
{ ADC0, GPIO_UNDEF, 30 }, /* internal: V_REFSL */
|
||||
{ ADC1, GPIO_UNDEF, 0 }, /* connected to Mulle Vbat/2 on PGA1_DP */
|
||||
{ ADC1, GPIO_UNDEF, 19 }, /* connected to Mulle Vchr/2 on PGA1_DM */
|
||||
};
|
||||
|
||||
/* ADC 0 configuration */
|
||||
#define ADC_0_DEV ADC0
|
||||
#define ADC_0_CHANNELS 4
|
||||
#define ADC_0_CLKEN() (BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_ADC0_SHIFT) = 1)
|
||||
#define ADC_0_CLKDIS() (BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_ADC0_SHIFT) = 0)
|
||||
#define ADC_0_PORT_CLKEN() /* no PORT pins configured */
|
||||
#define ADC_0_MODULE_CLOCK SystemBusClock
|
||||
/* ADC 0 channel 0 pin config */
|
||||
#define ADC_0_CH0 26 /* Temp sensor channel */
|
||||
#define ADC_0_CH0_PORT 0 /* this channel is not part of the pin mux on this CPU */
|
||||
#define ADC_0_CH0_PIN 0
|
||||
#define ADC_0_CH0_PIN_AF 0
|
||||
/* ADC 0 channel 1 pin config */
|
||||
#define ADC_0_CH1 27 /* Band gap channel */
|
||||
#define ADC_0_CH1_PORT 0 /* this channel is not part of the pin mux on this CPU */
|
||||
#define ADC_0_CH1_PIN 0
|
||||
#define ADC_0_CH1_PIN_AF 0
|
||||
/* ADC 0 channel 2 pin config */
|
||||
#define ADC_0_CH2 29 /* V_REFSH */
|
||||
#define ADC_0_CH2_PORT 0 /* this channel is not part of the pin mux on this CPU */
|
||||
#define ADC_0_CH2_PIN 0
|
||||
#define ADC_0_CH2_PIN_AF 0
|
||||
/* ADC 0 channel 3 pin config */
|
||||
#define ADC_0_CH3 30 /* V_REFSL */
|
||||
#define ADC_0_CH3_PORT 0 /* this channel is not part of the pin mux on this CPU */
|
||||
#define ADC_0_CH3_PIN 0
|
||||
#define ADC_0_CH3_PIN_AF 0
|
||||
/* ADC 0 channel 4 pin config */
|
||||
#define ADC_0_CH4 4
|
||||
#define ADC_0_CH4_PORT 0
|
||||
#define ADC_0_CH4_PIN 0
|
||||
#define ADC_0_CH4_PIN_AF 0
|
||||
/* ADC 0 channel 5 pin config */
|
||||
#define ADC_0_CH5 5
|
||||
#define ADC_0_CH5_PORT 0
|
||||
#define ADC_0_CH5_PIN 0
|
||||
#define ADC_0_CH5_PIN_AF 0
|
||||
|
||||
/* ADC 1 configuration */
|
||||
#define ADC_1_DEV ADC1
|
||||
#define ADC_1_CHANNELS 2
|
||||
#define ADC_1_CLKEN() (BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_ADC1_SHIFT) = 1)
|
||||
#define ADC_1_CLKDIS() (BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_ADC1_SHIFT) = 0)
|
||||
#define ADC_1_PORT_CLKEN() /* no PORT pins configured */
|
||||
#define ADC_1_MODULE_CLOCK SystemBusClock
|
||||
/* ADC 1 channel 0 pin config */
|
||||
#define ADC_1_CH0 0 /* DADP0, connected externally to Mulle Vbat/2 on PGA1_DP */
|
||||
#define ADC_1_CH0_PORT 0 /* this channel is not part of the pin mux on this CPU */
|
||||
#define ADC_1_CH0_PIN 0
|
||||
#define ADC_1_CH0_PIN_AF 0
|
||||
/* ADC 1 channel 1 pin config */
|
||||
#define ADC_1_CH1 19 /* AD19, connected externally to Mulle Vchr/2 on PGA1_DM */
|
||||
#define ADC_1_CH1_PORT 0 /* this channel is not part of the pin mux on this CPU */
|
||||
#define ADC_1_CH1_PIN 0
|
||||
#define ADC_1_CH1_PIN_AF 0
|
||||
/* ADC 1 channel 2 pin config */
|
||||
#define ADC_1_CH2 12
|
||||
#define ADC_1_CH2_PIN 0
|
||||
#define ADC_1_CH2_PIN_AF 0
|
||||
#define ADC_1_CH2_PORT 0
|
||||
/* ADC 1 channel 3 pin config */
|
||||
#define ADC_1_CH3 13
|
||||
#define ADC_1_CH3_PIN 0
|
||||
#define ADC_1_CH3_PIN_AF 0
|
||||
#define ADC_1_CH3_PORT 0
|
||||
/* ADC 1 channel 4 pin config */
|
||||
#define ADC_1_CH4 14
|
||||
#define ADC_1_CH4_PIN 0
|
||||
#define ADC_1_CH4_PIN_AF 0
|
||||
#define ADC_1_CH4_PORT 0
|
||||
/* ADC 1 channel 5 pin config */
|
||||
#define ADC_1_CH5 15
|
||||
#define ADC_1_CH5_PIN 0
|
||||
#define ADC_1_CH5_PIN_AF 0
|
||||
#define ADC_1_CH5_PORT 0
|
||||
#define ADC_NUMOF (sizeof(adc_config) / sizeof(adc_config[0]))
|
||||
/** @} */
|
||||
|
||||
|
||||
/**
|
||||
* @name PWM configuration
|
||||
* @{
|
||||
|
@ -80,6 +80,15 @@ static const timer_conf_t timer_config[] = {
|
||||
#define UART_PIN_CTS 10
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* The configuration consists simply of a list of channels that should be used
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (0)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Radio device configuration
|
||||
*
|
||||
|
@ -118,6 +118,16 @@ static const timer_conf_t timer_config[] = {
|
||||
#define SPI_1_PIN_MOSI 18
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* The configuration consists simply of a list of channels that should be used
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (0)
|
||||
/** @} */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -19,6 +19,8 @@
|
||||
#ifndef PERIPH_CONF_H_
|
||||
#define PERIPH_CONF_H_
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -100,6 +102,13 @@ extern "C" {
|
||||
#define RTC_NUMOF (1U)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (0)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -161,6 +161,12 @@ static const uart_conf_t uart_config[] = {
|
||||
#define I2C_0_SDA_CLKEN() (RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (0)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014-2016 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
@ -22,7 +22,7 @@
|
||||
#ifndef PERIPH_CONF_H_
|
||||
#define PERIPH_CONF_H_
|
||||
|
||||
#include "cpu_conf.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -47,6 +47,7 @@ extern "C"
|
||||
#define KINETIS_MCG_PLL_FREQ 48000000
|
||||
|
||||
#define CLOCK_CORECLOCK KINETIS_MCG_PLL_FREQ
|
||||
#define CLOCK_BUSCLOCK CLOCK_CORECLOCK
|
||||
/** @} */
|
||||
|
||||
|
||||
@ -115,55 +116,23 @@ extern "C"
|
||||
#define UART_1_AF 3
|
||||
/** @} */
|
||||
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 6
|
||||
static const adc_conf_t adc_config[] = {
|
||||
/* dev, pin, channel */
|
||||
{ ADC0, GPIO_PIN(PORT_E, 2), 1 },
|
||||
{ ADC0, GPIO_PIN(PORT_E, 3), 1 },
|
||||
{ ADC0, GPIO_PIN(PORT_D, 7), 22 },
|
||||
{ ADC0, GPIO_PIN(PORT_D, 5), 6 },
|
||||
{ ADC0, GPIO_PIN(PORT_E, 0), 10 },
|
||||
{ ADC0, GPIO_PIN(PORT_E, 1), 11 },
|
||||
};
|
||||
|
||||
/* ADC 0 configuration */
|
||||
#define ADC_0_DEV ADC0
|
||||
#define ADC_0_MODULE_CLOCK CLOCK_CORECLOCK
|
||||
#define ADC_0_CHANNELS 6
|
||||
#define ADC_0_CLKEN() (SIM->SCGC6 |= (SIM_SCGC6_ADC0_MASK))
|
||||
#define ADC_0_CLKDIS() (SIM->SCGC6 &= ~(SIM_SCGC6_ADC0_MASK))
|
||||
#define ADC_0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTE_MASK))
|
||||
/* ADC 0 channel 0 pin config */
|
||||
#define ADC_0_CH0 1
|
||||
#define ADC_0_CH0_PIN 2
|
||||
#define ADC_0_CH0_PIN_AF 0
|
||||
#define ADC_0_CH0_PORT PORTE
|
||||
/* ADC 0 channel 1 pin config */
|
||||
#define ADC_0_CH1 1 /* PTE3 uses the same ADC_CH as PTE2, in single channel mode only one of them can be selected */
|
||||
#define ADC_0_CH1_PIN 3
|
||||
#define ADC_0_CH1_PIN_AF 0
|
||||
#define ADC_0_CH1_PORT PORTE
|
||||
/* ADC 0 channel 2 pin config */
|
||||
#define ADC_0_CH2 22
|
||||
#define ADC_0_CH2_PIN 7
|
||||
#define ADC_0_CH2_PIN_AF 0
|
||||
#define ADC_0_CH2_PORT PORTD
|
||||
/* ADC 0 channel 3 pin config */
|
||||
#define ADC_0_CH3 6
|
||||
#define ADC_0_CH3_PIN 5
|
||||
#define ADC_0_CH3_PIN_AF 0
|
||||
#define ADC_0_CH3_PORT PORTD
|
||||
/* ADC 0 channel 4 pin config */
|
||||
#define ADC_0_CH4 10
|
||||
#define ADC_0_CH4_PIN 0
|
||||
#define ADC_0_CH4_PIN_AF 0
|
||||
#define ADC_0_CH4_PORT PORTE
|
||||
/* ADC 0 channel 5 pin config */
|
||||
#define ADC_0_CH5 11
|
||||
#define ADC_0_CH5_PIN 1
|
||||
#define ADC_0_CH5_PIN_AF 0
|
||||
#define ADC_0_CH5_PORT PORTE
|
||||
#define ADC_NUMOF (sizeof(adc_config) / sizeof(adc_config[0]))
|
||||
/** @} */
|
||||
|
||||
|
||||
/**
|
||||
* @name PWM configuration
|
||||
* @{
|
||||
|
@ -82,6 +82,15 @@ static const timer_conf_t timer_config[] = {
|
||||
#define UART_PIN_CTS 10
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* The configuration consists simply of a list of channels that should be used
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (0)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Radio device configuration
|
||||
*
|
||||
|
@ -102,19 +102,13 @@ static const timer_conf_t timer_config[] = {
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* The configuration consists simply of a list of channels that should be used
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 4
|
||||
|
||||
/* ADC 0 device configuration */
|
||||
#define ADC_0_CHANNELS 4
|
||||
#define ADC_0_CH0 ADC_CONFIG_PSEL_AnalogInput0
|
||||
#define ADC_0_CH1 ADC_CONFIG_PSEL_AnalogInput1
|
||||
#define ADC_0_CH2 ADC_CONFIG_PSEL_AnalogInput2
|
||||
#define ADC_0_CH3 ADC_CONFIG_PSEL_AnalogInput3
|
||||
#define ADC_CONFIG {0, 1, 2, 3}
|
||||
#define ADC_NUMOF (4)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -19,6 +19,8 @@
|
||||
#ifndef PERIPH_CONF_H_
|
||||
#define PERIPH_CONF_H_
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -89,38 +91,22 @@ extern "C" {
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* We need to configure the following values:
|
||||
* [ pin, channel ]
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 6
|
||||
#define ADC_CONFIG { \
|
||||
{ GPIO_PIN(PORT_C, 0), 10 },\
|
||||
{ GPIO_PIN(PORT_C, 1), 11 },\
|
||||
{ GPIO_PIN(PORT_C, 2), 12 },\
|
||||
{ GPIO_PIN(PORT_C, 3), 13 },\
|
||||
{ GPIO_PIN(PORT_C, 4), 14 },\
|
||||
{ GPIO_PIN(PORT_C, 5), 15 } \
|
||||
}
|
||||
|
||||
/* ADC 0 configuration */
|
||||
#define ADC_0_DEV ADC1
|
||||
#define ADC_0_CHANNELS 6
|
||||
#define ADC_0_CLKEN() (RCC->APB2ENR |= RCC_APB2ENR_ADCEN)
|
||||
#define ADC_0_CLKDIS() (RCC->APB2ENR &= ~(RCC_APB2ENR_ADCEN))
|
||||
#define ADC_0_PORT GPIOC
|
||||
#define ADC_0_PORT_CLKEN() (RCC->AHBENR |= RCC_AHBENR_GPIOCEN)
|
||||
/* ADC 0 channel 0 pin config */
|
||||
#define ADC_0_CH0 10
|
||||
#define ADC_0_CH0_PIN 0
|
||||
/* ADC 0 channel 1 pin config */
|
||||
#define ADC_0_CH1 11
|
||||
#define ADC_0_CH1_PIN 1
|
||||
/* ADC 0 channel 2 pin config */
|
||||
#define ADC_0_CH2 12
|
||||
#define ADC_0_CH2_PIN 2
|
||||
/* ADC 0 channel 3 pin config */
|
||||
#define ADC_0_CH3 13
|
||||
#define ADC_0_CH3_PIN 3
|
||||
/* ADC 0 channel 4 pin config */
|
||||
#define ADC_0_CH4 14
|
||||
#define ADC_0_CH4_PIN 4
|
||||
/* ADC 0 channel 5 pin config */
|
||||
#define ADC_0_CH5 15
|
||||
#define ADC_0_CH5_PIN 5
|
||||
#define ADC_NUMOF (6)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -116,42 +116,20 @@ static const uart_conf_t uart_config[] = {
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* We need to define the following fields:
|
||||
* PIN, device (ADCx), channel
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (2U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_1_EN 1
|
||||
#define ADC_MAX_CHANNELS 2
|
||||
#define ADC_CONFIG { \
|
||||
{GPIO_PIN(PORT_A, 1), 0, 1}, \
|
||||
{GPIO_PIN(PORT_A, 4), 0, 4}, \
|
||||
{GPIO_PIN(PORT_C, 1), 1, 11}, \
|
||||
{GPIO_PIN(PORT_C, 2), 1, 12} \
|
||||
}
|
||||
|
||||
/* ADC 0 configuration */
|
||||
#define ADC_0_DEV ADC1
|
||||
#define ADC_0_CHANNELS 2
|
||||
#define ADC_0_CLKEN() (RCC->APB2ENR |= RCC_APB2ENR_ADC1EN)
|
||||
#define ADC_0_CLKDIS() (RCC->APB2ENR &= ~(RCC_APB2ENR_ADC1EN))
|
||||
#define ADC_0_PORT GPIOA
|
||||
#define ADC_0_PORT_CLKEN() (RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN)
|
||||
/* ADC 0 channel 0 pin config */
|
||||
#define ADC_0_CH0 1
|
||||
#define ADC_0_CH0_PIN 1
|
||||
/* ADC 0 channel 1 pin config */
|
||||
#define ADC_0_CH1 4
|
||||
#define ADC_0_CH1_PIN 4
|
||||
|
||||
/* ADC 1 configuration */
|
||||
#define ADC_1_DEV ADC2
|
||||
#define ADC_1_CHANNELS 2
|
||||
#define ADC_1_CLKEN() (RCC->APB2ENR |= RCC_APB2ENR_ADC2EN)
|
||||
#define ADC_1_CLKDIS() (RCC->APB2ENR &= ~(RCC_APB2ENR_ADC2EN))
|
||||
|
||||
#define ADC_1_PORT GPIOC
|
||||
#define ADC_1_PORT_CLKEN() (RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN)
|
||||
/* ADC 1 channel 0 pin config */
|
||||
#define ADC_1_CH0 11
|
||||
#define ADC_1_CH0_PIN 1
|
||||
/* ADC 1 channel 1 pin config */
|
||||
#define ADC_1_CH1 12
|
||||
#define ADC_1_CH1_PIN 2
|
||||
#define ADC_NUMOF (4)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -266,15 +266,6 @@ extern "C" {
|
||||
#define PWM_1_CH2_AF 0x81
|
||||
/* @} */
|
||||
|
||||
/**
|
||||
* @brief ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 8
|
||||
/* @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -100,19 +100,13 @@ static const timer_conf_t timer_config[] = {
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
* @brief ADC configuration
|
||||
*
|
||||
* The configuration consists simply of a list of channels that should be used
|
||||
* @{
|
||||
*/
|
||||
#define ADC_NUMOF (1U)
|
||||
#define ADC_0_EN 1
|
||||
#define ADC_MAX_CHANNELS 4
|
||||
|
||||
/* ADC 0 device configuration */
|
||||
#define ADC_0_CHANNELS 4
|
||||
#define ADC_0_CH0 ADC_CONFIG_PSEL_AnalogInput4
|
||||
#define ADC_0_CH1 ADC_CONFIG_PSEL_AnalogInput5
|
||||
#define ADC_0_CH2 ADC_CONFIG_PSEL_AnalogInput6
|
||||
#define ADC_0_CH3 ADC_CONFIG_PSEL_AnalogInput7
|
||||
#define ADC_CONFIG {4, 5, 6, 7}
|
||||
#define ADC_NUMOF (4)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "cpu.h"
|
||||
#include "mcg.h"
|
||||
#include "cpu_conf.h"
|
||||
|
||||
#define SIM_CLKDIV1_60MHZ (SIM_CLKDIV1_OUTDIV1(0) | \
|
||||
|
@ -32,8 +32,6 @@
|
||||
#error "undefined CPU_MODEL"
|
||||
#endif
|
||||
|
||||
#include "mcg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
|
@ -100,7 +100,6 @@
|
||||
#ifndef __MCG_CPU_H
|
||||
#define __MCG_CPU_H
|
||||
|
||||
#include "cpu_conf.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#if KINETIS_CPU_USE_MCG
|
||||
|
@ -109,6 +109,31 @@ enum {
|
||||
GPIO_PORTS_NUMOF /**< overall number of available ports */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Override default ADC resolution values
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = (0xfe), /**< not supported */
|
||||
ADC_RES_8BIT = ADC_CFG1_MODE(0), /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT = ADC_CFG1_MODE(2), /**< ADC resolution: 10 bit */
|
||||
ADC_RES_12BIT = ADC_CFG1_MODE(1), /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT = (0xff), /**< ADC resolution: 14 bit */
|
||||
ADC_RES_16BIT = ADC_CFG1_MODE(3) /**< ADC resolution: 16 bit */
|
||||
} adc_res_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CPU specific ADC configuration
|
||||
*/
|
||||
typedef struct {
|
||||
ADC_Type *dev; /**< ADC device */
|
||||
gpio_t pin; /**< pin to use, set to GPIO_UNDEF for internal
|
||||
* channels */
|
||||
uint8_t chan; /**< ADC channel */
|
||||
} adc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief CPU internal function for initializing PORTs
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014-2016 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
* Copyright (C) 2015 Eistec AB
|
||||
*
|
||||
@ -25,18 +25,68 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard in case that no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
/**
|
||||
* @brief Maximum clock speed
|
||||
*
|
||||
* The ADC clock speed must be configured to be >2MHz and <12MHz in order for
|
||||
* the ADC to work. For ADC calibration to work as intended, the ADC clock speed
|
||||
* must further no be >4MHz, so we target a ADC clock speed > 2 and <4MHz.
|
||||
*/
|
||||
#define ADC_MAX_CLK (4000000U)
|
||||
|
||||
typedef struct {
|
||||
int max_value;
|
||||
int bits;
|
||||
} adc_config_t;
|
||||
static mutex_t locks[] = {
|
||||
MUTEX_INIT,
|
||||
#ifdef ADC1
|
||||
MUTEX_INIT,
|
||||
#endif
|
||||
};
|
||||
|
||||
adc_config_t adc_config[ADC_NUMOF];
|
||||
static inline ADC_Type *dev(adc_t line)
|
||||
{
|
||||
return adc_config[line].dev;
|
||||
}
|
||||
|
||||
static inline int dev_num(adc_t line)
|
||||
{
|
||||
if (dev(line) == ADC0) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef ADC1
|
||||
else if (dev(line) == ADC1) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void prep(adc_t line)
|
||||
{
|
||||
if (dev(line) == ADC0) {
|
||||
BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_ADC0_SHIFT) = 1;
|
||||
}
|
||||
#ifdef ADC1
|
||||
else {
|
||||
BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_ADC1_SHIFT) = 1;
|
||||
}
|
||||
#endif
|
||||
mutex_lock(&locks[dev_num(line)]);
|
||||
}
|
||||
|
||||
static inline void done(adc_t line)
|
||||
{
|
||||
if (dev(line) == ADC0) {
|
||||
BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_ADC0_SHIFT) = 0;
|
||||
}
|
||||
#ifdef ADC1
|
||||
else {
|
||||
BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_ADC1_SHIFT) = 0;
|
||||
}
|
||||
#endif
|
||||
mutex_unlock(&locks[dev_num(line)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform ADC calibration routine.
|
||||
@ -44,34 +94,31 @@ adc_config_t adc_config[ADC_NUMOF];
|
||||
* This is a recipe taken straight from the Kinetis K60 reference manual. It has
|
||||
* been tested on MK60DN512VLL10.
|
||||
*
|
||||
* @param[in] ADC_ptr Pointer to ADC device to operate on.
|
||||
* @param[in] dev Pointer to ADC device to operate on.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on errors
|
||||
*/
|
||||
int kinetis_adc_calibrate(ADC_Type *ADC_ptr)
|
||||
int kinetis_adc_calibrate(ADC_Type *dev)
|
||||
{
|
||||
uint16_t cal;
|
||||
|
||||
ADC_ptr->SC3 |= ADC_SC3_CAL_MASK;
|
||||
dev->SC3 |= ADC_SC3_CAL_MASK;
|
||||
|
||||
while (ADC_ptr->SC3 & ADC_SC3_CAL_MASK); /* wait for calibration to finish */
|
||||
while (dev->SC3 & ADC_SC3_CAL_MASK) {} /* wait for calibration to finish */
|
||||
|
||||
while (!(ADC_ptr->SC1[0] & ADC_SC1_COCO_MASK));
|
||||
while (!(dev->SC1[0] & ADC_SC1_COCO_MASK)) {}
|
||||
|
||||
if (ADC_ptr->SC3 & ADC_SC3_CALF_MASK) {
|
||||
if (dev->SC3 & ADC_SC3_CALF_MASK) {
|
||||
/* calibration failed for some reason, possibly SC2[ADTRG] is 1 ? */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Following the steps in the reference manual:
|
||||
*/
|
||||
/* From the reference manual */
|
||||
/* 1. Initialize or clear a 16-bit variable in RAM. */
|
||||
/* 2. Add the plus-side calibration results CLP0, CLP1, CLP2, CLP3, CLP4, and
|
||||
* CLPS to the variable. */
|
||||
cal = ADC_ptr->CLP0 + ADC_ptr->CLP1 + ADC_ptr->CLP2 + ADC_ptr->CLP3 +
|
||||
ADC_ptr->CLP4 + ADC_ptr->CLPS;
|
||||
/* 2. Add the plus-side calibration results CLP0, CLP1, CLP2, CLP3, CLP4,
|
||||
* and CLPS to the variable. */
|
||||
cal = dev->CLP0 + dev->CLP1 + dev->CLP2 + dev->CLP3 + dev->CLP4 + dev->CLPS;
|
||||
/* 3. Divide the variable by two. */
|
||||
cal /= 2;
|
||||
/* 4. Set the MSB of the variable. */
|
||||
@ -82,345 +129,88 @@ int kinetis_adc_calibrate(ADC_Type *ADC_ptr)
|
||||
* We ignore the above optimization suggestion, we most likely only perform
|
||||
* this calibration on startup and it will only save nanoseconds. */
|
||||
/* 6. Store the value in the plus-side gain calibration register PG. */
|
||||
ADC_ptr->PG = cal;
|
||||
|
||||
dev->PG = cal;
|
||||
/* 7. Repeat the procedure for the minus-side gain calibration value. */
|
||||
cal = ADC_ptr->CLM0 + ADC_ptr->CLM1 + ADC_ptr->CLM2 + ADC_ptr->CLM3 +
|
||||
ADC_ptr->CLM4 + ADC_ptr->CLMS;
|
||||
cal = dev->CLM0 + dev->CLM1 + dev->CLM2 + dev->CLM3 + dev->CLM4 + dev->CLMS;
|
||||
cal /= 2;
|
||||
cal |= (1 << 15);
|
||||
ADC_ptr->MG = cal;
|
||||
dev->MG = cal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
ADC_Type *adc = 0;
|
||||
PORT_Type *port[ADC_MAX_CHANNELS];
|
||||
uint8_t pins[ADC_MAX_CHANNELS];
|
||||
uint8_t af[ADC_MAX_CHANNELS];
|
||||
int channels = 0;
|
||||
uint32_t mode = 0;
|
||||
uint32_t div = 0;
|
||||
int i = 0;
|
||||
uint32_t clock = 0;
|
||||
|
||||
adc_poweron(dev);
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
port[0] = ADC_0_CH0_PORT;
|
||||
port[1] = ADC_0_CH1_PORT;
|
||||
port[2] = ADC_0_CH2_PORT;
|
||||
port[3] = ADC_0_CH3_PORT;
|
||||
port[4] = ADC_0_CH4_PORT;
|
||||
port[5] = ADC_0_CH5_PORT;
|
||||
pins[0] = ADC_0_CH0_PIN;
|
||||
pins[1] = ADC_0_CH1_PIN;
|
||||
pins[2] = ADC_0_CH2_PIN;
|
||||
pins[3] = ADC_0_CH3_PIN;
|
||||
pins[4] = ADC_0_CH4_PIN;
|
||||
pins[5] = ADC_0_CH5_PIN;
|
||||
af[0] = ADC_0_CH0_PIN_AF;
|
||||
af[1] = ADC_0_CH1_PIN_AF;
|
||||
af[2] = ADC_0_CH2_PIN_AF;
|
||||
af[3] = ADC_0_CH3_PIN_AF;
|
||||
af[4] = ADC_0_CH4_PIN_AF;
|
||||
af[5] = ADC_0_CH5_PIN_AF;
|
||||
channels = ADC_0_CHANNELS;
|
||||
clock = ADC_0_MODULE_CLOCK;
|
||||
ADC_0_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
port[0] = ADC_1_CH0_PORT;
|
||||
port[1] = ADC_1_CH1_PORT;
|
||||
port[2] = ADC_1_CH2_PORT;
|
||||
port[3] = ADC_1_CH3_PORT;
|
||||
port[4] = ADC_1_CH4_PORT;
|
||||
port[5] = ADC_1_CH5_PORT;
|
||||
pins[0] = ADC_1_CH0_PIN;
|
||||
pins[1] = ADC_1_CH1_PIN;
|
||||
pins[2] = ADC_1_CH2_PIN;
|
||||
pins[3] = ADC_1_CH3_PIN;
|
||||
pins[4] = ADC_1_CH4_PIN;
|
||||
pins[5] = ADC_1_CH5_PIN;
|
||||
af[0] = ADC_1_CH0_PIN_AF;
|
||||
af[1] = ADC_1_CH1_PIN_AF;
|
||||
af[2] = ADC_1_CH2_PIN_AF;
|
||||
af[3] = ADC_1_CH3_PIN_AF;
|
||||
af[4] = ADC_1_CH4_PIN_AF;
|
||||
af[5] = ADC_1_CH5_PIN_AF;
|
||||
channels = ADC_1_CHANNELS;
|
||||
clock = ADC_1_MODULE_CLOCK;
|
||||
ADC_1_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (channels > ADC_MAX_CHANNELS) {
|
||||
/* make sure the given line is valid */
|
||||
if (line >= ADC_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set precision, these numbers are valid for the K60 */
|
||||
switch (precision) {
|
||||
case ADC_RES_6BIT:
|
||||
/* Not supported by hardware, use 8 bit mode. */
|
||||
mode = ADC_CFG1_MODE(0);
|
||||
adc_config[dev].bits = 8;
|
||||
break;
|
||||
/* prepare the device: lock and power on */
|
||||
prep(line);
|
||||
|
||||
case ADC_RES_8BIT:
|
||||
mode = ADC_CFG1_MODE(0);
|
||||
adc_config[dev].bits = 8;
|
||||
break;
|
||||
|
||||
case ADC_RES_10BIT:
|
||||
mode = ADC_CFG1_MODE(2);
|
||||
adc_config[dev].bits = 10;
|
||||
break;
|
||||
|
||||
case ADC_RES_12BIT:
|
||||
mode = ADC_CFG1_MODE(1);
|
||||
adc_config[dev].bits = 12;
|
||||
break;
|
||||
|
||||
case ADC_RES_14BIT:
|
||||
/* Not supported by hardware, use 16 bit mode. */
|
||||
mode = ADC_CFG1_MODE(3);
|
||||
adc_config[dev].bits = 16;
|
||||
break;
|
||||
|
||||
case ADC_RES_16BIT:
|
||||
mode = ADC_CFG1_MODE(3);
|
||||
adc_config[dev].bits = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
adc_config[dev].max_value = (1 << adc_config[dev].bits) - 1;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
/* Check whether we need to set the pin mux for this channel. */
|
||||
if (port[i] != NULL) {
|
||||
port[i]->PCR[pins[i]] = PORT_PCR_MUX(af[i]);
|
||||
}
|
||||
/* configure the connected pin mux */
|
||||
if (adc_config[line].pin != GPIO_UNDEF) {
|
||||
gpio_init_port(adc_config[line].pin, GPIO_AF_ANALOG);
|
||||
}
|
||||
|
||||
/* The ADC requires at least 2 MHz module clock for full accuracy, and less
|
||||
* than 12 MHz */
|
||||
/* For the calibration it is important that the ADC clock is <= 4 MHz */
|
||||
if (clock > 4000000 * 8) {
|
||||
/* Need to divide by 16, we set the clock input to BusClock/2 and
|
||||
* divide clock by 8 (the maximum divider) */
|
||||
div = ADC_CFG1_ADIV(3) | ADC_CFG1_ADICLK(1);
|
||||
}
|
||||
else if (clock > 4000000 * 4) {
|
||||
/* divide clock by 8 */
|
||||
div = ADC_CFG1_ADIV(3);
|
||||
}
|
||||
else if (clock > 4000000 * 2) {
|
||||
/* divide clock by 4 */
|
||||
div = ADC_CFG1_ADIV(2);
|
||||
}
|
||||
else if (clock > 4000000 * 1) {
|
||||
/* divide clock by 2 */
|
||||
div = ADC_CFG1_ADIV(1);
|
||||
uint32_t adiv;
|
||||
int i = 4;
|
||||
if (CLOCK_BUSCLOCK > (ADC_MAX_CLK * 8)) {
|
||||
adiv = ADC_CFG1_ADIV(3) | ADC_CFG1_ADICLK(1);
|
||||
}
|
||||
else {
|
||||
/* no clock divider */
|
||||
div = ADC_CFG1_ADIV(0);
|
||||
while ((i > 0) && (CLOCK_BUSCLOCK < (ADC_MAX_CLK * i))) {
|
||||
i--;
|
||||
}
|
||||
adiv = ADC_CFG1_ADIV(i - 1);
|
||||
}
|
||||
|
||||
/* set configuration register 1: clocking and precision */
|
||||
/* Set long sample time */
|
||||
adc->CFG1 = ADC_CFG1_ADLSMP_MASK | mode | div;
|
||||
|
||||
dev(line)->CFG1 = ADC_CFG1_ADLSMP_MASK | adiv;
|
||||
/* select ADxxb channels, longest sample time (20 extra ADC cycles) */
|
||||
adc->CFG2 = ADC_CFG2_MUXSEL_MASK | ADC_CFG2_ADLSTS(0);
|
||||
|
||||
dev(line)->CFG2 = ADC_CFG2_MUXSEL_MASK | ADC_CFG2_ADLSTS(0);
|
||||
/* select software trigger, external ref pins */
|
||||
adc->SC2 = ADC_SC2_REFSEL(0);
|
||||
|
||||
dev(line)->SC2 = ADC_SC2_REFSEL(0);
|
||||
/* select hardware average over 32 samples */
|
||||
adc->SC3 = ADC_SC3_AVGE_MASK | ADC_SC3_AVGS(3);
|
||||
|
||||
dev(line)->SC3 = ADC_SC3_AVGE_MASK | ADC_SC3_AVGS(3);
|
||||
/* set an (arbitrary) input channel, single-ended mode */
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH0);
|
||||
adc->SC1[1] = ADC_SC1_ADCH(ADC_0_CH0);
|
||||
dev(line)->SC1[0] = ADC_SC1_ADCH(0);
|
||||
|
||||
/* perform calibration routine */
|
||||
if (kinetis_adc_calibrate(adc) != 0) {
|
||||
int res = kinetis_adc_calibrate(dev(line));
|
||||
done(line);
|
||||
return res;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
int sample;
|
||||
|
||||
/* check if resolution is applicable */
|
||||
if (res > 0xf0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
{
|
||||
ADC_Type *adc = 0;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
|
||||
/* start single conversion on corresponding channel */
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH2);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH3);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH4);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH5);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
|
||||
/* start single conversion on corresponding channel */
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH2);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH3);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH4);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH5);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
/* lock and power on the device */
|
||||
prep(line);
|
||||
|
||||
/* set resolution */
|
||||
dev(line)->CFG1 &= ~(ADC_CFG1_MODE_MASK);
|
||||
dev(line)->CFG1 |= (res);
|
||||
/* select the channel that is sampled */
|
||||
dev(line)->SC1[0] = adc_config[line].chan;
|
||||
/* wait until conversion is complete */
|
||||
/* TODO: Use interrupts and yield the thread */
|
||||
while (!(adc->SC1[0] & ADC_SC1_COCO_MASK));
|
||||
|
||||
while (!(dev(line)->SC1[0] & ADC_SC1_COCO_MASK)) {}
|
||||
/* read and return result */
|
||||
return (int)adc->R[0];
|
||||
sample = (int)dev(line)->R[0];
|
||||
|
||||
/* power off and unlock the device */
|
||||
done(line);
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
ADC_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
ADC_1_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
ADC_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
ADC_1_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
int scale = max - min;
|
||||
|
||||
/* NB: There is additional room for the multiplication result if using the
|
||||
* actual precision setting of the ADC as the limit in this if statement: */
|
||||
if (scale < (1 << 16)) {
|
||||
/* The ADCs on these CPUs are limited to 16 bits, the result of
|
||||
* value x scale will be 31 bits long or less. We use the upper part
|
||||
* of a 32 bit word when scaling */
|
||||
int32_t tmp = value * scale;
|
||||
/* Divide by ADC range to get the scaled result */
|
||||
return (tmp / (1 << adc_config[dev].bits));
|
||||
}
|
||||
else {
|
||||
/* Target scale is too large to use int32_t as an in between result */
|
||||
int64_t tmp = scale;
|
||||
/* Make sure the compiler does not generate code which will truncate the
|
||||
* result. */
|
||||
tmp *= value;
|
||||
/* Divide by ADC range to get the scaled result */
|
||||
tmp /= (1 << adc_config[dev].bits);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
{
|
||||
return ((max - min) / ((float)adc_config[dev].max_value)) * value;
|
||||
}
|
||||
|
||||
#endif /* ADC_NUMOF */
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "cpu.h"
|
||||
#include "mcg.h"
|
||||
#include "cpu_conf.h"
|
||||
|
||||
#define FLASH_BASE (0x00000000)
|
||||
|
@ -37,8 +37,6 @@
|
||||
#error "undefined CPU_MODEL"
|
||||
#endif
|
||||
|
||||
#include "mcg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
|
@ -52,7 +52,6 @@ typedef enum {
|
||||
} gpio_pp_t;
|
||||
/** @} */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Override values for pin direction configuration
|
||||
* @{
|
||||
@ -88,6 +87,19 @@ enum {
|
||||
PORT_F = 5, /**< port F */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Override resolution options
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = 0xa00, /**< not supported by hardware */
|
||||
ADC_RES_8BIT = 0xb00, /**< not supported by hardware */
|
||||
ADC_RES_10BIT = ADC_RES_10BIT_S, /**< ADC resolution: 10 bit */
|
||||
ADC_RES_12BIT = ADC_RES_12BIT_S, /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT = 0xc00, /**< not supported by hardware */
|
||||
ADC_RES_16BIT = 0xd00, /**< not supported by hardware */
|
||||
} adc_res_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -13,6 +13,8 @@
|
||||
* @file
|
||||
* @brief Low-level ADC driver implementation
|
||||
*
|
||||
* The current ADC driver implementation only supports ADC0.
|
||||
*
|
||||
* @author Marc Poulhiès <dkm@kataplop.net>
|
||||
*
|
||||
* @}
|
||||
@ -22,152 +24,109 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#if ADC_NUMOF
|
||||
/*
|
||||
* @brief ADC sequence used by this driver and oversampling settings
|
||||
* @{
|
||||
*/
|
||||
#define SEQ (3)
|
||||
#define OVERSAMPLE (64)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief pin configuration parameters
|
||||
*/
|
||||
struct adc_gpio_cfg_s {
|
||||
unsigned long gpio_base;
|
||||
unsigned long gpio_sysctl;
|
||||
unsigned short gpio_pin;
|
||||
};
|
||||
|
||||
static const struct adc_gpio_cfg_s adc0_gpio[ADC_MAX_CHANNELS] = {
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_3 }, // AIN0
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_2 }, // AIN1
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_1 }, // AIN2
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_0 }, // AIN3
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_3 }, // AIN4
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_2 }, // AIN5
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_1 }, // AIN6
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_0 }, // AIN7
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_5 }, // AIN8
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_4 }, // AIN9
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOB, GPIO_PIN_4 }, // AIN10
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOB, GPIO_PIN_5 }, // AIN11
|
||||
/**
|
||||
* @brief Fixed ADC pin configuration
|
||||
*/
|
||||
static const struct adc_gpio_cfg_s adc0_gpio[ADC_NUMOF] = {
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_3 }, /**< AIN0 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_2 }, /**< AIN1 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_1 }, /**< AIN2 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_0 }, /**< AIN3 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_3 }, /**< AIN4 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_2 }, /**< AIN5 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_1 }, /**< AIN6 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOD, GPIO_PIN_0 }, /**< AIN7 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_5 }, /**< AIN8 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOE, GPIO_PIN_4 }, /**< AIN9 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOB, GPIO_PIN_4 }, /**< AIN10 */
|
||||
{ GPIO_PORTE_BASE, SYSCTL_PERIPH_GPIOB, GPIO_PIN_5 }, /**< AIN11 */
|
||||
};
|
||||
|
||||
/*
|
||||
* We only support ADC0 for now
|
||||
/**
|
||||
* @brief Lock to prevent concurrent access to the ADC
|
||||
*/
|
||||
#define ADC_0_SYSCTL SYSCTL_PERIPH_ADC0
|
||||
#define ADC_0_GPIO adc0_gpio
|
||||
#define ADC_0_BASE ADC0_BASE
|
||||
#define ADC_0_SEQ 3
|
||||
static mutex_t lock = MUTEX_INIT;
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
static inline void prep(void)
|
||||
{
|
||||
unsigned long adc_base;
|
||||
unsigned long adc_seq;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc_base = ADC_0_BASE;
|
||||
adc_seq = ADC_0_SEQ;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
ROM_SysCtlADCSpeedSet(SYSCTL_ADCSPEED_125KSPS);
|
||||
adc_poweron(dev);
|
||||
|
||||
ROM_ADCHardwareOversampleConfigure(adc_base, 64);
|
||||
ROM_ADCSequenceDisable(adc_base, adc_seq);
|
||||
|
||||
return 0;
|
||||
mutex_lock(&lock);
|
||||
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
static inline void done(void)
|
||||
{
|
||||
int value[2];
|
||||
unsigned long adc_base;
|
||||
unsigned long adc_seq;
|
||||
unsigned long adc_gpio_port = 0;
|
||||
unsigned long adc_gpio_pin = 0;
|
||||
unsigned long adc_gpio_sysctl = 0;
|
||||
ROM_SysCtlPeripheralDisable(SYSCTL_PERIPH_ADC0);
|
||||
mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
if (channel >= ADC_MAX_CHANNELS) {
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
/* make sure the given ADC line is valid */
|
||||
if (line >= ADC_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc_base = ADC_0_BASE;
|
||||
adc_seq = ADC_0_SEQ;
|
||||
prep();
|
||||
|
||||
adc_gpio_port = ADC_0_GPIO[channel].gpio_base;
|
||||
adc_gpio_sysctl = ADC_0_GPIO[channel].gpio_sysctl;
|
||||
adc_gpio_pin = ADC_0_GPIO[channel].gpio_pin;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
ROM_SysCtlPeripheralEnable(adc_gpio_sysctl);
|
||||
ROM_GPIOPinTypeADC(adc_gpio_port, adc_gpio_pin);
|
||||
ROM_SysCtlADCSpeedSet(SYSCTL_ADCSPEED_125KSPS);
|
||||
ROM_ADCHardwareOversampleConfigure(ADC0_BASE, OVERSAMPLE);
|
||||
|
||||
ROM_ADCSequenceDisable(adc_base, adc_seq);
|
||||
ROM_ADCIntClear(adc_base, adc_seq);
|
||||
ROM_SysCtlPeripheralEnable(adc0_gpio[line].gpio_sysctl);
|
||||
ROM_GPIOPinTypeADC(adc0_gpio[line].gpio_base, adc0_gpio[line].gpio_pin);
|
||||
|
||||
ROM_ADCSequenceConfigure(adc_base, adc_seq, ADC_TRIGGER_PROCESSOR, 0);
|
||||
done();
|
||||
|
||||
ROM_ADCSequenceStepConfigure(adc_base, adc_seq, 0, channel | ADC_CTL_IE | ADC_CTL_END);
|
||||
ROM_ADCSequenceEnable(adc_base, adc_seq);
|
||||
|
||||
ROM_ADCIntClear(adc_base, adc_seq);
|
||||
|
||||
ROM_ADCProcessorTrigger(adc_base, adc_seq);
|
||||
while (!ROM_ADCIntStatus(adc_base, adc_seq, false)) {
|
||||
}
|
||||
ROM_ADCIntClear(adc_base, adc_seq);
|
||||
ROM_ADCSequenceDataGet(adc_base, adc_seq, (unsigned long *) value);
|
||||
|
||||
return value[0];
|
||||
}
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
unsigned long adc_sysctl;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc_sysctl = ADC_0_SYSCTL;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
ROM_SysCtlPeripheralEnable(adc_sysctl);
|
||||
}
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
unsigned long adc_sysctl;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc_sysctl = ADC_0_SYSCTL;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
ROM_SysCtlPeripheralDisable(adc_sysctl);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Currently not supported
|
||||
*/
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently not supported
|
||||
*/
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
int adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
int value[2];
|
||||
|
||||
#endif /* ADC_NUMOF */
|
||||
if ((res != ADC_RES_10BIT) && (res != ADC_RES_12BIT)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
prep();
|
||||
|
||||
/* set channel */
|
||||
ROM_ADCSequenceConfigure(ADC0_BASE, SEQ, ADC_TRIGGER_PROCESSOR, 0);
|
||||
ROM_ADCSequenceStepConfigure(ADC0_BASE, SEQ, 0, line | ADC_CTL_IE | ADC_CTL_END);
|
||||
/* set resolution */
|
||||
ROM_ADCResolutionSet(ADC0_BASE, (unsigned long)res);
|
||||
|
||||
/* start conversion and wait for results */
|
||||
ROM_ADCSequenceEnable(ADC0_BASE, SEQ);
|
||||
ROM_ADCIntClear(ADC0_BASE, SEQ);
|
||||
ROM_ADCProcessorTrigger(ADC0_BASE, SEQ);
|
||||
while (!ROM_ADCIntStatus(ADC0_BASE, SEQ, false)) {}
|
||||
|
||||
/* get results */
|
||||
ROM_ADCSequenceDataGet(ADC0_BASE, SEQ, (unsigned long *) value);
|
||||
|
||||
/* disable device again */
|
||||
ROM_ADCSequenceDisable(ADC0_BASE, SEQ);
|
||||
done();
|
||||
|
||||
return value[0];
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin
|
||||
* Copyright (C) 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
|
||||
@ -14,6 +14,7 @@
|
||||
* @brief CPU specific definitions for internal peripheral handling
|
||||
*
|
||||
* @author Paul RATHGEB <paul.rathgeb@skynet.be>
|
||||
* @author Hauke Petersen<hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef PERIPH_CPU_H_
|
||||
@ -39,6 +40,28 @@ extern "C" {
|
||||
*/
|
||||
#define CPUID_LEN (16U)
|
||||
|
||||
/**
|
||||
* @brief Define number of available ADC lines
|
||||
*
|
||||
* TODO: check this value
|
||||
*/
|
||||
#define ADC_NUMOF (10U)
|
||||
|
||||
/**
|
||||
* @brief Override the ADC resolution settings
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = 0, /**< ADC resolution: 6 bit */
|
||||
ADC_RES_8BIT, /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT, /**< ADC resolution: 10 bit */
|
||||
ADC_RES_12BIT, /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT, /**< ADC resolution: 14 bit */
|
||||
ADC_RES_16BIT, /**< ADC resolution: 16 bit */
|
||||
} adc_res_t;
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin
|
||||
* Copyright (C) 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,114 +13,91 @@
|
||||
* @file
|
||||
* @brief Low-level ADC driver implementation
|
||||
*
|
||||
* @author Paul RATHGEB <paul.rathgeb@skynet.be>
|
||||
* @author Paul Rathgeb <paul.rathgeb@skynet.be>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard in case that no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
/**
|
||||
* @brief Mutex to synchronize ADC access from different threads
|
||||
*/
|
||||
static mutex_t lock = MUTEX_INIT;
|
||||
|
||||
static inline uint32_t *_adc_channel_to_pin(int channel)
|
||||
static inline uint32_t *pincfg_reg(adc_t line)
|
||||
{
|
||||
return (channel < 6) ? ((uint32_t*)(LPC_IOCON) + (11 + channel))
|
||||
: ((uint32_t*)(LPC_IOCON) + (16 + channel));
|
||||
int offset = (line < 6) ? (11 + line) : (16 + line);
|
||||
return ((uint32_t *)(LPC_IOCON) + offset);
|
||||
}
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
static inline void prep(void)
|
||||
{
|
||||
adc_poweron(dev);
|
||||
mutex_lock(&lock);
|
||||
LPC_SYSCON->PDRUNCFG &= ~(1 << 4);
|
||||
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 13);
|
||||
}
|
||||
|
||||
/* Set resolution */
|
||||
switch (precision) {
|
||||
case ADC_RES_6BIT:
|
||||
LPC_ADC->CR |= (0x04 << 17);
|
||||
break;
|
||||
case ADC_RES_8BIT:
|
||||
LPC_ADC->CR |= (0x02 << 17);
|
||||
break;
|
||||
case ADC_RES_10BIT:
|
||||
LPC_ADC->CR |= (0x0 << 17);
|
||||
break;
|
||||
case ADC_RES_12BIT:
|
||||
case ADC_RES_14BIT:
|
||||
case ADC_RES_16BIT:
|
||||
/* Resolution > 10 bits unsupported */
|
||||
adc_poweroff(dev);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
static inline void done(void)
|
||||
{
|
||||
LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 13);
|
||||
LPC_SYSCON->PDRUNCFG |= (1 << 4);
|
||||
mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
uint32_t *pincfg;
|
||||
|
||||
prep();
|
||||
|
||||
/* ADC frequency : 3MHz */
|
||||
LPC_ADC->CR |= (15 << 8);
|
||||
LPC_ADC->CR = (15 << 8);
|
||||
/* configure the connected pin */
|
||||
pincfg = pincfg_reg(line);
|
||||
/* Put the pin in its ADC alternate function */
|
||||
if (line < 5) {
|
||||
*pincfg |= 2;
|
||||
}
|
||||
else {
|
||||
*pincfg |= 1;
|
||||
}
|
||||
/* Configure ADMODE in analog input */
|
||||
*pincfg &= ~(1 << 7);
|
||||
|
||||
done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
int adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
if (channel > ADC_MAX_CHANNELS) {
|
||||
int sample;
|
||||
|
||||
/* check if resolution is valid */
|
||||
if (res < 0xff) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Compute the IOCON register for the channel */
|
||||
uint32_t *cfg = _adc_channel_to_pin(channel);
|
||||
/* Put the pin in its ADC alternate function */
|
||||
if (channel < 5) {
|
||||
*cfg |= 2;
|
||||
}
|
||||
else {
|
||||
*cfg |= 1;
|
||||
}
|
||||
/* Configure ADMODE in analog input */
|
||||
*cfg &= ~(1 << 7);
|
||||
/* prepare the device */
|
||||
prep();
|
||||
|
||||
/* set resolution */
|
||||
LPC_ADC->CR &= ~(0x7 << 17);
|
||||
LPC_ADC->CR |= res;
|
||||
/* Start a conversion */
|
||||
LPC_ADC->CR |= (1 << channel) | (1 << 24);
|
||||
LPC_ADC->CR |= (1 << line) | (1 << 24);
|
||||
/* Wait for the end of the conversion */
|
||||
while (!(LPC_ADC->DR[channel] & (1 << 31)));
|
||||
while (!(LPC_ADC->DR[line] & (1 << 31))) {}
|
||||
/* Read and return result */
|
||||
return (LPC_ADC->DR[channel] >> 6);
|
||||
}
|
||||
sample = (LPC_ADC->DR[line] >> 6);
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
LPC_SYSCON->PDRUNCFG &= ~(1 << 4);
|
||||
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 13);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
done();
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
LPC_SYSCON->PDRUNCFG |= (1 << 4);
|
||||
LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 13);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif /* ADC_NUMOF */
|
||||
|
@ -1,10 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2015 Ludwig Knüpfer
|
||||
* Copyright (C) 2014-2016 Freie Universität Berlin
|
||||
* 2015 Ludwig Knüpfer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -12,7 +12,7 @@
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level UART driver implementation
|
||||
* @brief Low-level ADC driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
||||
@ -21,114 +21,73 @@
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard file in case no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
/**
|
||||
* @brief Load the ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#ifdef ADC_CONFIG
|
||||
static const uint8_t adc_config[] = ADC_CONFIG;
|
||||
#else
|
||||
static const uint8_t adc_config[] = {};
|
||||
#endif
|
||||
|
||||
/* save the maximum value configured for the ADC */
|
||||
int adc_max_value;
|
||||
/**
|
||||
* @brief Lock to prevent concurrency issues when used from different threads
|
||||
*/
|
||||
static mutex_t lock;
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
static inline void prep(void)
|
||||
{
|
||||
/* the NRF51822 only supports one ADC... */
|
||||
if (dev != ADC_0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* power on ADC */
|
||||
mutex_lock(&lock);
|
||||
NRF_ADC->POWER = 1;
|
||||
NRF_ADC->ENABLE = 1;
|
||||
}
|
||||
|
||||
/* disable ADC interrupts */
|
||||
NRF_ADC->INTENSET = (ADC_INTENSET_END_Disabled << ADC_INTENSET_END_Pos);
|
||||
static inline void done(void)
|
||||
{
|
||||
NRF_ADC->ENABLE = 0;
|
||||
NRF_ADC->POWER = 0;
|
||||
mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
/* configure ADC, set precision, internal reference to VBG */
|
||||
switch (precision) {
|
||||
case ADC_RES_8BIT:
|
||||
adc_max_value = 255;
|
||||
NRF_ADC->CONFIG = (ADC_CONFIG_RES_8bit << ADC_CONFIG_RES_Pos);
|
||||
break;
|
||||
case ADC_RES_10BIT:
|
||||
adc_max_value = 1023;
|
||||
NRF_ADC->CONFIG = (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos);
|
||||
break;
|
||||
default:
|
||||
NRF_ADC->POWER = 0;
|
||||
return -1;
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
if (line >= ADC_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* select the reference voltage / prescaler */
|
||||
NRF_ADC->CONFIG |= (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
|
||||
NRF_ADC->CONFIG |= (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos);
|
||||
NRF_ADC->CONFIG |= (ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos);
|
||||
|
||||
/* enable the ADC */
|
||||
NRF_ADC->ENABLE = (ADC_ENABLE_ENABLE_Enabled << ADC_ENABLE_ENABLE_Pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
int adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
if (dev != ADC_0) {
|
||||
return -2;
|
||||
int val;
|
||||
|
||||
/* check if resolution is valid */
|
||||
if (res > 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set channel */
|
||||
NRF_ADC->CONFIG &= ~(ADC_CONFIG_PSEL_Msk);
|
||||
NRF_ADC->CONFIG |= (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos);
|
||||
switch (channel) {
|
||||
case 0:
|
||||
NRF_ADC->CONFIG |= (ADC_0_CH0 << ADC_CONFIG_PSEL_Pos);
|
||||
break;
|
||||
case 1:
|
||||
NRF_ADC->CONFIG |= (ADC_0_CH1 << ADC_CONFIG_PSEL_Pos);
|
||||
break;
|
||||
case 2:
|
||||
NRF_ADC->CONFIG |= (ADC_0_CH2 << ADC_CONFIG_PSEL_Pos);
|
||||
break;
|
||||
case 3:
|
||||
NRF_ADC->CONFIG |= (ADC_0_CH3 << ADC_CONFIG_PSEL_Pos);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
/* prepare device */
|
||||
prep();
|
||||
|
||||
/* set resolution, line, and use 1/3 input and ref voltage scaling */
|
||||
NRF_ADC->CONFIG = ((ADC_CONFIG_REFSEL_SupplyOneThirdPrescaling << 5) |
|
||||
(ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling << 2) |
|
||||
(1 << (adc_config[line] + 8)) |
|
||||
res);
|
||||
/* start conversion */
|
||||
NRF_ADC->TASKS_START = 1;
|
||||
|
||||
/* wait for conversion to be complete */
|
||||
while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {}
|
||||
NRF_ADC->EVENTS_END = 1;
|
||||
while (NRF_ADC->BUSY == 1) {}
|
||||
/* get result */
|
||||
val = (int)NRF_ADC->RESULT;
|
||||
|
||||
/* return result */
|
||||
return (int)NRF_ADC->RESULT;
|
||||
/* free device */
|
||||
done();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
if (dev == ADC_0) {
|
||||
NRF_ADC->POWER = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
if (dev == ADC_0) {
|
||||
NRF_ADC->POWER = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
return (int)adc_mapf(dev, value, (float)min, (float)max);
|
||||
}
|
||||
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
{
|
||||
(void) dev;
|
||||
return ((max - min) / ((float)adc_max_value)) * value;
|
||||
}
|
||||
|
||||
#endif /* ADC_NUMOF */
|
||||
|
@ -77,6 +77,21 @@ typedef enum {
|
||||
} gpio_flank_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Override ADC resolution values
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = 0xf0, /**< ADC resolution: 6 bit */
|
||||
ADC_RES_8BIT = 0x00, /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT = 0x02, /**< ADC resolution: 10 bit */
|
||||
ADC_RES_12BIT = 0xf1, /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT = 0xf2, /**< ADC resolution: 14 bit */
|
||||
ADC_RES_16BIT = 0xf3 /**< ADC resolution: 16 bit */
|
||||
} adc_res_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Timer configuration options
|
||||
*/
|
||||
|
@ -77,6 +77,29 @@ typedef enum {
|
||||
GPIO_AF3, /**< use alternate function 3 */
|
||||
} gpio_af_t;
|
||||
|
||||
/**
|
||||
* @brief Override ADC resolution values
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = (0x3 << 3), /**< ADC resolution: 6 bit */
|
||||
ADC_RES_8BIT = (0x2 << 3), /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT = (0x1 << 3), /**< ADC resolution: 10 bit */
|
||||
ADC_RES_12BIT = (0x0 << 3), /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT = (0xfe), /**< not applicable */
|
||||
ADC_RES_16BIT = (0xff) /**< not applicable */
|
||||
} adc_res_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC line configuration values
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_t pin; /**< pin to use */
|
||||
uint8_t chan; /**< internal channel the pin is connected to */
|
||||
} adc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Configure the alternate function for the given pin
|
||||
*
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -18,164 +18,90 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard in case that no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
/**
|
||||
* @brief Maximum allowed ADC clock speed
|
||||
*/
|
||||
#define MAX_ADC_SPEED (12000000U)
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t precision;
|
||||
} adc_config_t;
|
||||
|
||||
|
||||
adc_config_t config[ADC_NUMOF];
|
||||
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
{
|
||||
ADC_TypeDef *adc = 0;
|
||||
|
||||
adc_poweron(dev);
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
ADC_0_PORT_CLKEN();
|
||||
ADC_0_PORT->MODER |= ((3 << ADC_0_CH0_PIN) | (3 << ADC_0_CH1_PIN) |
|
||||
(3 << ADC_0_CH2_PIN) | (3 << ADC_0_CH3_PIN) |
|
||||
(3 << ADC_0_CH4_PIN) | (3 << ADC_0_CH5_PIN));
|
||||
break;
|
||||
/**
|
||||
* @brief Load the ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#ifdef ADC_CONFIG
|
||||
static const adc_conf_t adc_config[] = ADC_CONFIG;
|
||||
#else
|
||||
static const adc_conf_t adc_config[] = {};
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
|
||||
/**
|
||||
* @brief Allocate locks for all three available ADC device
|
||||
*
|
||||
* All STM32F0 CPUs we support so far only come with a single ADC device.
|
||||
*/
|
||||
static mutex_t lock = MUTEX_INIT;
|
||||
|
||||
static inline void prep(void)
|
||||
{
|
||||
mutex_lock(&lock);
|
||||
RCC->APB2ENR |= RCC_APB2ENR_ADCEN;
|
||||
}
|
||||
|
||||
static inline void done(void)
|
||||
{
|
||||
RCC->APB2ENR &= ~(RCC_APB2ENR_ADCEN);
|
||||
mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
/* make sure the given line is valid */
|
||||
if (line >= ADC_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* reset control registers */
|
||||
adc->CR = 0;
|
||||
adc->CFGR1 = 0;
|
||||
adc->CFGR2 = 0;
|
||||
|
||||
/* set resolution */
|
||||
config[dev].precision = (6 + (2 * precision));
|
||||
|
||||
switch (precision) {
|
||||
case ADC_RES_6BIT:
|
||||
adc->CFGR1 |= ADC_CFGR1_RES_0 | ADC_CFGR1_RES_1;
|
||||
break;
|
||||
case ADC_RES_8BIT:
|
||||
adc->CFGR1 |= ADC_CFGR1_RES_1;
|
||||
break;
|
||||
case ADC_RES_10BIT:
|
||||
adc->CFGR1 |= ADC_CFGR1_RES_0;
|
||||
break;
|
||||
case ADC_RES_12BIT:
|
||||
break;
|
||||
case ADC_RES_14BIT:
|
||||
case ADC_RES_16BIT:
|
||||
adc_poweroff(dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* configure sampling time to 41.5 cycles */
|
||||
adc->SMPR = 4;
|
||||
|
||||
/* enable the ADC module */
|
||||
adc->CR = ADC_CR_ADEN;
|
||||
/* lock and power on the device */
|
||||
prep();
|
||||
/*configure the pin */
|
||||
gpio_init_analog(adc_config[line].pin);
|
||||
/* reset configuration */
|
||||
ADC1->CFGR2 = 0;
|
||||
/* enable device */
|
||||
ADC1->CR = ADC_CR_ADEN;
|
||||
/* configure sampling time to save value */
|
||||
ADC1->SMPR = 0x3; /* 28.5 ADC clock cycles */
|
||||
/* power off an release device for now */
|
||||
done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
int adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
ADC_TypeDef *adc = 0;
|
||||
int sample;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->CHSELR = (1 << ADC_0_CH0);
|
||||
break;
|
||||
case 1:
|
||||
adc->CHSELR = (1 << ADC_0_CH1);
|
||||
break;
|
||||
case 2:
|
||||
adc->CHSELR = (1 << ADC_0_CH2);
|
||||
break;
|
||||
case 3:
|
||||
adc->CHSELR = (1 << ADC_0_CH3);
|
||||
break;
|
||||
case 4:
|
||||
adc->CHSELR = (1 << ADC_0_CH4);
|
||||
break;
|
||||
case 5:
|
||||
adc->CHSELR = (1 << ADC_0_CH5);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
/* check if resolution is applicable */
|
||||
if (res > 0xf0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start single conversion */
|
||||
adc->CR |= ADC_CR_ADSTART;
|
||||
/* wait until conversion is complete */
|
||||
while (!(adc->ISR & ADC_ISR_EOC)) {}
|
||||
/* read and return result */
|
||||
return (int)adc->DR;
|
||||
}
|
||||
/* lock and power on the ADC device */
|
||||
prep();
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
ADC_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
ADC_1_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* set resolution and channel */
|
||||
ADC1->CFGR1 = res;
|
||||
ADC1->CHSELR = (1 << adc_config[line].chan);
|
||||
/* start conversion and wait for results */
|
||||
ADC1->CR |= ADC_CR_ADSTART;
|
||||
while (!(ADC1->ISR & ADC_ISR_EOC)) {}
|
||||
/* read result */
|
||||
sample = (int)ADC1->DR;
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
ADC_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
ADC_1_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* unlock and power off device again */
|
||||
done();
|
||||
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
return 0;
|
||||
return sample;
|
||||
}
|
||||
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif /* ADC_NUMOF */
|
||||
|
@ -25,6 +25,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Available number of ADC devices
|
||||
*/
|
||||
#if defined(CPU_MODEL_STM32F401RE)
|
||||
#define ADC_DEVS (1U)
|
||||
#elif defined(CPU_MODEL_STM32F407VG) || defined(CPU_MODEL_STM32F415RG)
|
||||
#define ADC_DEVS (3U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Overwrite the default gpio_t type definition
|
||||
* @{
|
||||
@ -43,6 +52,30 @@ typedef uint32_t gpio_t;
|
||||
*/
|
||||
#define GPIO_PIN(x, y) ((GPIOA_BASE + (x << 10)) | y)
|
||||
|
||||
/**
|
||||
* @brief declare needed generic SPI functions
|
||||
* @{
|
||||
*/
|
||||
#define PERIPH_SPI_NEEDS_TRANSFER_BYTES
|
||||
#define PERIPH_SPI_NEEDS_TRANSFER_REG
|
||||
#define PERIPH_SPI_NEEDS_TRANSFER_REGS
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Override the ADC resolution configuration
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = 0x03000000, /**< ADC resolution: 6 bit */
|
||||
ADC_RES_8BIT = 0x02000000, /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT = 0x01000000, /**< ADC resolution: 10 bit */
|
||||
ADC_RES_12BIT = 0x00000000, /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT = 1, /**< ADC resolution: 14 bit (not supported) */
|
||||
ADC_RES_16BIT = 2 /**< ADC resolution: 16 bit (not supported)*/
|
||||
} adc_res_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Available ports on the STM32F4 family
|
||||
*/
|
||||
@ -95,6 +128,16 @@ typedef struct {
|
||||
} uart_conf_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC channel configuration data
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_t pin; /**< pin connected to the channel */
|
||||
uint8_t dev; /**< ADCx - 1 device used for the channel */
|
||||
uint8_t chan; /**< CPU ADC channel connected to the pin */
|
||||
uint8_t rcc; /**< bit in the RCC APB2 enable register */
|
||||
} adc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Configure the alternate function for the given pin
|
||||
*
|
||||
@ -105,6 +148,13 @@ typedef struct {
|
||||
*/
|
||||
void gpio_init_af(gpio_t pin, gpio_af_t af);
|
||||
|
||||
/**
|
||||
* @brief Configure the given pin to be used as ADC input
|
||||
*
|
||||
* @param[in] pin pin to configure
|
||||
*/
|
||||
void gpio_init_analog(gpio_t pin);
|
||||
|
||||
/**
|
||||
* @brief Power on the DMA device the given stream belongs to
|
||||
*
|
||||
|
@ -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
|
||||
@ -19,167 +19,111 @@
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard in case that no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
/**
|
||||
* @brief Maximum allowed ADC clock speed
|
||||
*/
|
||||
#define MAX_ADC_SPEED (12000000U)
|
||||
|
||||
typedef struct {
|
||||
int max_value;
|
||||
} adc_config_t;
|
||||
/**
|
||||
* @brief Load the ADC configuration
|
||||
* @{
|
||||
*/
|
||||
#ifdef ADC_CONFIG
|
||||
static const adc_conf_t adc_config[] = ADC_CONFIG;
|
||||
#else
|
||||
static const adc_conf_t adc_config[] = {};
|
||||
#endif
|
||||
|
||||
adc_config_t adc_config[ADC_NUMOF];
|
||||
/**
|
||||
* @brief Allocate locks for all three available ADC devices
|
||||
*/
|
||||
static mutex_t locks[] = {
|
||||
#if ADC_DEVS > 1
|
||||
MUTEX_INIT,
|
||||
#endif
|
||||
#if ADC_DEVS > 2
|
||||
MUTEX_INIT,
|
||||
#endif
|
||||
MUTEX_INIT
|
||||
};
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
static inline ADC_TypeDef *dev(adc_t line)
|
||||
{
|
||||
ADC_TypeDef *adc = 0;
|
||||
return (ADC_TypeDef *)(ADC1_BASE + (adc_config[line].dev << 8));
|
||||
}
|
||||
|
||||
adc_poweron(dev);
|
||||
static inline void prep(adc_t line)
|
||||
{
|
||||
mutex_lock(&locks[adc_config[line].dev]);
|
||||
RCC->APB2ENR |= (RCC_APB2ENR_ADC1EN << adc_config[line].dev);
|
||||
}
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
ADC_0_PORT_CLKEN();
|
||||
ADC_0_PORT->MODER |= (3 << (ADC_0_CH0_PIN * 2) | 3 << (ADC_0_CH1_PIN * 2));
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
ADC_1_PORT_CLKEN();
|
||||
ADC_1_PORT->MODER |= (3 << (ADC_1_CH0_PIN * 2) | 3 << (ADC_1_CH1_PIN * 2));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
static inline void done(adc_t line)
|
||||
{
|
||||
RCC->APB2ENR &= ~(RCC_APB2ENR_ADC1EN << adc_config[line].dev);
|
||||
mutex_unlock(&locks[adc_config[line].dev]);
|
||||
}
|
||||
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
uint32_t clk_div = 2;
|
||||
|
||||
/* check if the line is valid */
|
||||
if (line >= ADC_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* reset control registers */
|
||||
adc->CR1 = 0;
|
||||
adc->CR2 = 0;
|
||||
adc->SQR1 = 0;
|
||||
/* lock and power-on the device */
|
||||
prep(line);
|
||||
|
||||
/* set precision */
|
||||
|
||||
switch (precision) {
|
||||
case ADC_RES_6BIT:
|
||||
adc->CR1 |= ADC_CR1_RES_0 | ADC_CR1_RES_1;
|
||||
adc_config[dev].max_value = 0x3f;
|
||||
break;
|
||||
case ADC_RES_8BIT:
|
||||
adc->CR1 |= ADC_CR1_RES_1;
|
||||
adc_config[dev].max_value = 0xff;
|
||||
break;
|
||||
case ADC_RES_10BIT:
|
||||
adc->CR1 |= ADC_CR1_RES_0;
|
||||
adc_config[dev].max_value = 0x3ff;
|
||||
break;
|
||||
case ADC_RES_12BIT:
|
||||
adc_config[dev].max_value = 0xfff;
|
||||
break;
|
||||
case ADC_RES_14BIT:
|
||||
case ADC_RES_16BIT:
|
||||
adc_poweroff(dev);
|
||||
return -1;
|
||||
/* configure the pin */
|
||||
gpio_init_analog(adc_config[line].pin);
|
||||
/* set sequence length to 1 conversion and enable the ADC device */
|
||||
dev(line)->SQR1 = 0;
|
||||
dev(line)->CR2 = ADC_CR2_ADON;
|
||||
/* set clock prescaler to get the maximal possible ADC clock value */
|
||||
for (clk_div = 2; clk_div < 8; clk_div += 2) {
|
||||
if ((CLOCK_CORECLOCK / clk_div) <= MAX_ADC_SPEED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ADC->CCR = ((clk_div / 2) - 1) << 16;
|
||||
|
||||
/* set clock prescaler */
|
||||
ADC->CCR = (3 << 16); /* ADC clock = 10,5MHz */
|
||||
|
||||
/* enable the ADC module */
|
||||
adc->CR2 |= ADC_CR2_ADON;
|
||||
|
||||
/* free the device again */
|
||||
done(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
int adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
ADC_TypeDef *adc = 0;
|
||||
int sample;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SQR3 = ADC_0_CH0 & 0x1f;
|
||||
break;
|
||||
case 1:
|
||||
adc->SQR3 = ADC_0_CH1 & 0x1f;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SQR3 = ADC_1_CH0 & 0x1f;
|
||||
break;
|
||||
case 1:
|
||||
adc->SQR3 = ADC_1_CH1 & 0x1f;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
/* check if resolution is applicable */
|
||||
if (res < 0xff) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start single conversion */
|
||||
adc->CR2 |= ADC_CR2_SWSTART;
|
||||
/* wait until conversion is complete */
|
||||
while (!(adc->SR & ADC_SR_EOC)) {}
|
||||
/* read and return result */
|
||||
return (int)adc->DR;
|
||||
}
|
||||
/* lock and power on the ADC device */
|
||||
prep(line);
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
ADC_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
ADC_1_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* wait for any ongoing conversions to finish */
|
||||
while (dev(line)->SR & ADC_SR_STRT) {}
|
||||
/* set resolution and conversion channel */
|
||||
dev(line)->CR1 = res;
|
||||
dev(line)->SQR3 = adc_config[line].chan;
|
||||
/* start conversion and wait for results */
|
||||
dev(line)->CR2 |= ADC_CR2_SWSTART;
|
||||
while (!(dev(line)->SR & ADC_SR_EOC)) {}
|
||||
/* finally read sample and reset the STRT bit in the status register */
|
||||
sample = (int)dev(line)->DR;
|
||||
dev(line)->SR &= ~ADC_SR_STRT;
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
ADC_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
ADC_1_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* power off and unlock device again */
|
||||
done(line);
|
||||
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
return (int)adc_mapf(dev, value, (float)min, (float)max);
|
||||
return sample;
|
||||
}
|
||||
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
{
|
||||
return ((max - min) / ((float)adc_config[dev].max_value)) * value;
|
||||
}
|
||||
|
||||
#endif /* ADC_NUMOF */
|
||||
|
@ -148,6 +148,14 @@ void gpio_init_af(gpio_t pin, gpio_af_t af)
|
||||
port->AFR[(pin_num > 7) ? 1 : 0] |= (af << ((pin_num & 0x07) * 4));
|
||||
}
|
||||
|
||||
void gpio_init_analog(gpio_t pin)
|
||||
{
|
||||
/* enable clock */
|
||||
RCC->AHB1ENR |= (RCC_AHB1ENR_GPIOAEN << _port_num(pin));
|
||||
/* set to analog mode */
|
||||
_port(pin)->MODER |= (0x3 << (2 * _pin_num(pin)));
|
||||
}
|
||||
|
||||
void gpio_irq_enable(gpio_t pin)
|
||||
{
|
||||
EXTI->IMR |= (1 << _pin_num(pin));
|
||||
|
@ -33,8 +33,7 @@ extern "C" {
|
||||
* @brief device descriptor for a MQ-3 sensor
|
||||
*/
|
||||
typedef struct {
|
||||
adc_t adc_dev; /**< the used ADC device */
|
||||
int adc_chan; /**< used channel of the ADC */
|
||||
adc_t adc_line; /**< the used ADC line */
|
||||
} mq3_t;
|
||||
|
||||
/**
|
||||
@ -47,12 +46,11 @@ typedef struct {
|
||||
*
|
||||
* @param[out] dev device descriptor of an MQ-3 sensor
|
||||
* @param[in] adc the ADC device the sensor is connected to
|
||||
* @param[in] channel the channel of the ADC device used
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int mq3_init(mq3_t *dev, adc_t adc, int channel);
|
||||
int mq3_init(mq3_t *dev, adc_t adc_line);
|
||||
|
||||
/**
|
||||
* @brief Read the RAW sensor value, can be between 0 and MQ3_MAX_RAW_VALUE
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014-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
|
||||
@ -9,7 +9,29 @@
|
||||
/**
|
||||
* @defgroup drivers_periph_adc ADC
|
||||
* @ingroup drivers_periph
|
||||
* @brief Low-level ADC peripheral driver
|
||||
* @brief Low-level ADC peripheral driver interface
|
||||
*
|
||||
* This is a very simple ADC interface to allow platform independent access to
|
||||
* a MCU's ADC unit(s). This interface is intentionally designed as simple as
|
||||
* possible, to allow for very easy implementation and maximal portability.
|
||||
*
|
||||
* As of now, the interface does not allow for any advanced ADC concepts (e.g.
|
||||
* continuous mode, scan sequences, injections). It is to be determined, if
|
||||
* these features will ever be integrated in this interface, or if it does make
|
||||
* more sense to create a second, advanced ADC interface for this.
|
||||
*
|
||||
* The ADC driver interface is built around the concept of ADC lines. An ADC
|
||||
* line in this context is a tuple consisting out of a hardware ADC device (an
|
||||
* ADC functional unit on the MCU) and an ADC channel connected to pin.
|
||||
*
|
||||
* If a MCU has more than one hardware ADC unit, the ADC lines can be mapped in
|
||||
* a way, that it is possible to sample multiple lines in parallel, given that
|
||||
* the ADC implementation allows for interruption of the program flow while
|
||||
* waiting for the result of a conversion (e.g. through putting the calling
|
||||
* thread to sleep while waiting for the conversion results).
|
||||
*
|
||||
* @todo Extend interface for continuous mode?
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
@ -18,128 +40,99 @@
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef ADC_H
|
||||
#define ADC_H
|
||||
#ifndef PERIPH_ADC_H
|
||||
#define PERIPH_ADC_H
|
||||
|
||||
#include "periph_cpu.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* guard file in case no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
|
||||
/**
|
||||
* @brief Definition available ADC devices
|
||||
*
|
||||
* Each ADC device is based on a hardware ADC which can have one or more
|
||||
* multiplexed channels.
|
||||
* @brief Make sure the number of available ADC lines is defined
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
#if ADC_0_EN
|
||||
ADC_0 = 0, /**< ADC device 0 */
|
||||
#ifndef ADC_NUMOF
|
||||
#error "ADC_NUMOF undefined"
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
ADC_1, /**< ADC device 1 */
|
||||
#endif
|
||||
#if ADC_2_EN
|
||||
ADC_2, /**< ADC device 2 */
|
||||
#endif
|
||||
#if ADC_3_EN
|
||||
ADC_3, /**< ADC device 3 */
|
||||
#endif
|
||||
} adc_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Possible ADC precision settings
|
||||
* @brief Define default ADC type identifier
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = 0, /**< ADC precision: 6 bit */
|
||||
ADC_RES_8BIT, /**< ADC precision: 8 bit */
|
||||
ADC_RES_10BIT, /**< ADC precision: 10 bit */
|
||||
ADC_RES_12BIT, /**< ADC precision: 12 bit */
|
||||
ADC_RES_14BIT, /**< ADC precision: 14 bit */
|
||||
ADC_RES_16BIT, /**< ADC precision: 16 bit */
|
||||
} adc_precision_t;
|
||||
#ifndef HAVE_ADC_T
|
||||
typedef unsigned int adc_t;
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Initialization of a given ADC device
|
||||
* @brief Default ADC undefined value
|
||||
* @{
|
||||
*/
|
||||
#ifndef ADC_UNDEF
|
||||
#define ADC_UNDEF (0xffff)
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Default ADC line access macro
|
||||
* @{
|
||||
*/
|
||||
#ifndef ADC_LINE
|
||||
#define ADC_LINE(x) (x)
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Possible ADC resolution settings
|
||||
* @{
|
||||
*/
|
||||
#ifndef HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = 0, /**< ADC resolution: 6 bit */
|
||||
ADC_RES_8BIT, /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT, /**< ADC resolution: 10 bit */
|
||||
ADC_RES_12BIT, /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT, /**< ADC resolution: 14 bit */
|
||||
ADC_RES_16BIT, /**< ADC resolution: 16 bit */
|
||||
} adc_res_t;
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Initialize the given ADC line
|
||||
*
|
||||
* The ADC will be initialized in synchronous, blocking mode, so no callbacks for finished
|
||||
* conversions are required as conversions are presumed to be very fast (somewhere in the
|
||||
* range of some us).
|
||||
* The ADC line is initialized in synchronous, blocking mode.
|
||||
*
|
||||
* @param[in] dev the device to initialize
|
||||
* @param[in] precision the precision to use for conversion
|
||||
* @param[in] line line to initialize
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on precision not available
|
||||
* @return -1 on invalid ADC line
|
||||
*/
|
||||
int adc_init(adc_t dev, adc_precision_t precision);
|
||||
int adc_init(adc_t line);
|
||||
|
||||
/**
|
||||
* @brief Start a new conversion by using the given channel.
|
||||
* @brief Sample a value from the given ADC line
|
||||
*
|
||||
* If a conversion on any channel on the given ADC core is in progress, it is aborted.
|
||||
* This function blocks until the conversion has finished. Please note, that if
|
||||
* more than one line share the same ADC device, and if these lines are sampled
|
||||
* at the same time (e.g. from different threads), the one called secondly waits
|
||||
* for the first to finish before its conversion starts.
|
||||
*
|
||||
* @param[in] dev the ADC device to use for the conversion
|
||||
* @param[in] channel the channel to convert from
|
||||
* @param[in] line line to sample
|
||||
* @param[in] resolution resolution to use for conversion
|
||||
*
|
||||
* @return the converted value
|
||||
* @return -1 on invalid channel
|
||||
* @return the sampled value on success
|
||||
* @return -1 if resolution is not applicable
|
||||
*/
|
||||
int adc_sample(adc_t dev, int channel);
|
||||
|
||||
/**
|
||||
* @brief Enable the power for the given ADC device
|
||||
*
|
||||
* @param[in] dev the ADC device to power up
|
||||
*/
|
||||
void adc_poweron(adc_t dev);
|
||||
|
||||
/**
|
||||
* @brief Disable the power for the given ADC device
|
||||
*
|
||||
* @param[in] dev the ADC device to power down
|
||||
*/
|
||||
void adc_poweroff(adc_t dev);
|
||||
|
||||
/**
|
||||
* @brief Helper function to map a converted value to the given integer range.
|
||||
*
|
||||
* This function is useful for converting sampled ADC values into their physical representation.
|
||||
*
|
||||
* The min value is asserted to be smaller than the max value.
|
||||
*
|
||||
* @param[in] dev the ADC device to read the precision value from (as input interval)
|
||||
* @param[in] value the value to map
|
||||
* @param[in] min the lower bound of the target interval
|
||||
* @param[in] max the upper bound of the target interval
|
||||
*
|
||||
* @return the mapped value
|
||||
*/
|
||||
int adc_map(adc_t dev, int value, int min, int max);
|
||||
|
||||
/**
|
||||
* @brief Helper function to map a converted value to the given float range
|
||||
*
|
||||
* @see adc_map
|
||||
*
|
||||
* @param[in] dev the ADC device to read the precision value from (as input interval)
|
||||
* @param[in] value the value to map
|
||||
* @param[in] min the lower bound of the target interval
|
||||
* @param[in] max the upper bound of the target interval
|
||||
*
|
||||
* @return the mapped value
|
||||
*/
|
||||
float adc_mapf(adc_t dev, int value, float min, float max);
|
||||
|
||||
#endif /* ADC_NUMOF */
|
||||
int adc_sample(adc_t line, adc_res_t res);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ADC_H */
|
||||
#endif /* PERIPH_ADC_H */
|
||||
/** @} */
|
||||
|
@ -24,16 +24,15 @@
|
||||
#define MIN (100U) /* TODO: calibrate to useful value */
|
||||
#define FACTOR (2.33f) /* TODO: calibrate to useful value */
|
||||
|
||||
int mq3_init(mq3_t *dev, adc_t adc, int channel)
|
||||
int mq3_init(mq3_t *dev, adc_t adc_line)
|
||||
{
|
||||
dev->adc_dev = adc;
|
||||
dev->adc_chan = channel;
|
||||
return adc_init(dev->adc_dev, PRECISION);
|
||||
dev->adc_line = adc_line;
|
||||
return adc_init(dev->adc_line);
|
||||
}
|
||||
|
||||
int mq3_read_raw(mq3_t *dev)
|
||||
{
|
||||
return adc_sample(dev->adc_dev, dev->adc_chan);
|
||||
return adc_sample(dev->adc_line, PRECISION);
|
||||
}
|
||||
|
||||
int mq3_read(mq3_t *dev)
|
||||
|
1
sys/adc_util/Makefile
Normal file
1
sys/adc_util/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
41
sys/adc_util/adc_util.c
Normal file
41
sys/adc_util/adc_util.c
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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_adc_util
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief ADC utility function implementation
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "adc_util.h"
|
||||
|
||||
/* keep a max value to ADC resolution mapping for quick access in the ROM */
|
||||
static const int val_max[] = {
|
||||
[ADC_RES_6BIT] = 0x003f,
|
||||
[ADC_RES_8BIT] = 0x00ff,
|
||||
[ADC_RES_10BIT] = 0x03ff,
|
||||
[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)
|
||||
{
|
||||
return ((((max - min) * sample) / val_max[res]) + min);
|
||||
}
|
||||
|
||||
float adc_util_mapf(int sample, adc_res_t res, float min, float max)
|
||||
{
|
||||
return ((((max - min) * sample) / val_max[res]) + min);
|
||||
}
|
67
sys/include/adc_util.h
Normal file
67
sys/include/adc_util.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_adc_util ADC utilities
|
||||
* @ingroup sys
|
||||
* @brief Utility functions for handling ADC samples
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
* @brief ADC utility function interfaces
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef ADC_UTIL_H
|
||||
#define ADC_UTIL_H
|
||||
|
||||
#include "periph/adc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Map a sampled ADC value to a given range
|
||||
*
|
||||
* This function is useful for converting sampled ADC values into their physical
|
||||
* representation.
|
||||
*
|
||||
* The min value is asserted to be smaller than the max value.
|
||||
*
|
||||
* @param[in] sample sampled ADC value
|
||||
* @param[in] res ADC resolution
|
||||
* @param[in] min the lower bound of the target interval
|
||||
* @param[in] max the upper bound of the target interval
|
||||
*
|
||||
* @return the mapped value
|
||||
*/
|
||||
int adc_util_map(int sample, adc_res_t res, int min, int max);
|
||||
|
||||
/**
|
||||
* @brief Map a sampled ADC value to a given range (using floating point
|
||||
* arithmetics)
|
||||
*
|
||||
* @see adc_util_map
|
||||
*
|
||||
* @param[in] sample sampled ADC value
|
||||
* @param[in] res ADC resolution
|
||||
* @param[in] min the lower bound of the target interval
|
||||
* @param[in] max the upper bound of the target interval
|
||||
*
|
||||
* @return the mapped value
|
||||
*/
|
||||
float adc_util_mapf(int sample, adc_res_t res, float min, float max);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ADC_UTIL_H */
|
||||
/** @} */
|
@ -7,11 +7,9 @@ USEMODULE += mq3
|
||||
USEMODULE += xtimer
|
||||
|
||||
# set default device parameters in case they are undefined
|
||||
MQ3_ADC ?= ADC_0
|
||||
MQ3_CHAN ?= 0
|
||||
MQ3_ADC_LINE ?= ADC_LINE\(0\)
|
||||
|
||||
# export parameters
|
||||
CFLAGS += -DMQ3_ADC=$(MQ3_ADC)
|
||||
CFLAGS += -DMQ3_CHAN=$(MQ3_CHAN)
|
||||
CFLAGS += -DMQ3_ADC_LINE=$(MQ3_ADC_LINE)
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
@ -25,11 +25,8 @@
|
||||
#include "periph_conf.h"
|
||||
#include "periph/adc.h"
|
||||
|
||||
#ifndef MQ3_ADC
|
||||
#error "MQ3_ADC is not specified"
|
||||
#endif
|
||||
#ifndef MQ3_CHAN
|
||||
#error "MQ3_CHAN is not specified"
|
||||
#ifndef MQ3_ADC_LINE
|
||||
#error "MQ3_ADC_LINE is not specified"
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
@ -38,8 +35,8 @@ int main(void)
|
||||
int res;
|
||||
|
||||
puts("MQ-3 alcohol sensor test application\n");
|
||||
printf("Initializing MQ-3 sensor at ADC_%i, channel %i... ", MQ3_ADC, MQ3_CHAN);
|
||||
res = mq3_init(&dev, MQ3_ADC, MQ3_CHAN);
|
||||
printf("Initializing MQ-3 sensor at ADC_LINE(%i)... ", (int)MQ3_ADC_LINE);
|
||||
res = mq3_init(&dev, MQ3_ADC_LINE);
|
||||
if (res == 0) {
|
||||
puts("[ok]\n");
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
Expected result
|
||||
===============
|
||||
When running this test, you should see the samples of all configured ADC channels
|
||||
When running this test, you should see the samples of all configured ADC lines
|
||||
continuously streamed to std-out.
|
||||
|
||||
Background
|
||||
==========
|
||||
This test application will initialize each configured ADC device to sample with
|
||||
This test application will initialize each configured ADC lines to sample with
|
||||
10-bit accuracy. Once configured the application will continuously convert each
|
||||
available channel and print the conversion results to std-out.
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014-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.
|
||||
* 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 tests
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Test case for the low-level ADC driver
|
||||
* @brief Test application for peripheral ADC drivers
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
@ -20,64 +20,43 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "board.h"
|
||||
#include "xtimer.h"
|
||||
#include "periph/adc.h"
|
||||
|
||||
#if ADC_NUMOF < 1
|
||||
#error "Please enable at least 1 ADC device to run this test"
|
||||
#endif
|
||||
|
||||
#define RES ADC_RES_10BIT
|
||||
#define DELAY (100 * 1000U)
|
||||
|
||||
static int values[ADC_NUMOF][ADC_MAX_CHANNELS];
|
||||
|
||||
int main(void)
|
||||
{
|
||||
puts("\nRIOT ADC peripheral driver test\n");
|
||||
puts("This test simply converts each available ADC channel about every 10ms\n\n");
|
||||
uint32_t last = xtimer_now();
|
||||
int sample = 0;
|
||||
|
||||
puts("\nRIOT ADC peripheral driver test\n");
|
||||
puts("This test will sample all available ADC lines once every 100ms with\n"
|
||||
"a 10-bit resolution and print the sampled results to STDIO\n\n");
|
||||
|
||||
/* initialize all available ADC lines */
|
||||
for (int i = 0; i < ADC_NUMOF; i++) {
|
||||
/* initialize result vector */
|
||||
for (int j = 0; j < ADC_MAX_CHANNELS; j++) {
|
||||
values[i][j] = -1;
|
||||
}
|
||||
/* initialize ADC device */
|
||||
printf("Initializing ADC_%i @ %i bit resolution", i, (6 + (2* RES)));
|
||||
if (adc_init(i, RES) == 0) {
|
||||
puts(" ...[ok]");
|
||||
}
|
||||
else {
|
||||
puts(" ...[failed]");
|
||||
if (adc_init(ADC_LINE(i)) < 0) {
|
||||
printf("Initialization of ADC_LINE(%i) failed\n", i);
|
||||
return 1;
|
||||
} else {
|
||||
printf("Successfully initialized ADC_LINE(%i)\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
puts("\n");
|
||||
|
||||
while (1) {
|
||||
/* convert each channel for this ADC device */
|
||||
for (int i = 0; i < ADC_NUMOF; i++) {
|
||||
for (int j = 0; j < ADC_MAX_CHANNELS; j++) {
|
||||
values[i][j] = adc_sample(i, j);
|
||||
sample = adc_sample(ADC_LINE(i), RES);
|
||||
if (sample < 0) {
|
||||
printf("ADC_LINE(%i): 10-bit resolution not applicable\n", i);
|
||||
} else {
|
||||
printf("ADC_LINE(%i): %i\n", i, sample);
|
||||
}
|
||||
}
|
||||
|
||||
/* print the results */
|
||||
printf("Values: ");
|
||||
for (int i = 0; i < ADC_NUMOF; i++) {
|
||||
for (int j = 0; j < ADC_MAX_CHANNELS; j++) {
|
||||
if (values[i][j] >= 0) {
|
||||
printf("ADC_%i-CH%i: %4i ", i, j, values[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
/* sleep a little while */
|
||||
xtimer_usleep(DELAY);
|
||||
xtimer_usleep_until(&last, DELAY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#endif
|
||||
|
||||
#define RES DAC_RES_10BIT
|
||||
#define ADC_RES ADC_RES_10BIT
|
||||
#define DELAY (1000 * 1000U)
|
||||
#define MAX_VALUE_STEPS 1000
|
||||
|
||||
@ -60,9 +61,9 @@ int main(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if ADC_NUMOF > 0
|
||||
printf("Initializing ADC_0 @ %i bit resolution", (6 + (2* RES)));
|
||||
if (adc_init(0, RES) == 0) {
|
||||
#ifdef ADC_NUMOF
|
||||
printf("Initializing ADC_LINE(0)");
|
||||
if (adc_init(ADC_LINE(0)) == 0) {
|
||||
puts(" ...[ok]");
|
||||
}
|
||||
else {
|
||||
@ -82,9 +83,9 @@ int main(void)
|
||||
printf("%i: Something went wrong writing DAC\n", status_dac_write);
|
||||
return -1;
|
||||
}
|
||||
#if ADC_NUMOF > 0
|
||||
#ifdef ADC_NUMOF
|
||||
/* Read values from ADC */
|
||||
int sample = adc_sample(ADC_0, 0);
|
||||
int sample = adc_sample(ADC_LINE(0), ADC_RES);
|
||||
if (sample < 0) {
|
||||
printf("%i: Something went wrong sampling ADC\n", sample);
|
||||
return -1;
|
||||
|
Loading…
Reference in New Issue
Block a user