mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
146 lines
3.1 KiB
C
146 lines
3.1 KiB
C
/*
|
|
* Copyright (C) 2016 Laurent Navet <laurent.navet@gmail.com>
|
|
* 2017 HAW Hamburg, Dimitri Nahm
|
|
*
|
|
* 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 drivers_periph
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Low-level ADC driver implementation for ATmega family
|
|
*
|
|
* @author Laurent Navet <laurent.navet@gmail.com>
|
|
* @author Dimitri Nahm <dimitri.nahm@haw-hamburg.de>
|
|
* @author Sebastian Meiling <s@mlng.net>
|
|
* @}
|
|
*/
|
|
|
|
#include "cpu.h"
|
|
#include "mutex.h"
|
|
#include "periph/adc.h"
|
|
#include "periph_conf.h"
|
|
|
|
#define ADC_MAX_CLK (200000U)
|
|
|
|
static mutex_t lock = MUTEX_INIT;
|
|
|
|
static inline void _prep(void)
|
|
{
|
|
mutex_lock(&lock);
|
|
/* Enable ADC */
|
|
ADCSRA |= (1 << ADEN);
|
|
}
|
|
|
|
static inline void _done(void)
|
|
{
|
|
/* Disable ADC */
|
|
ADCSRA &= ~(1 << ADEN);
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
int adc_init(adc_t line)
|
|
{
|
|
/* check if the line is valid */
|
|
if (line >= ADC_NUMOF) {
|
|
return -1;
|
|
}
|
|
|
|
_prep();
|
|
|
|
/* Disable corresponding Digital input */
|
|
if (line < 8) {
|
|
DIDR0 |= (1 << line);
|
|
}
|
|
#if defined(CPU_ATMEGA2560)
|
|
else {
|
|
DIDR2 |= (1 << (line - 8));
|
|
}
|
|
#endif
|
|
|
|
/* Set ADC-pin as input */
|
|
#if defined(CPU_ATMEGA328P)
|
|
DDRC &= ~(1 << line);
|
|
PORTC &= ~(1 << line);
|
|
#elif defined(CPU_ATMEGA1284P)
|
|
DDRA &= ~(1 << line);
|
|
PORTA &= ~(1 << line);
|
|
#elif defined(CPU_ATMEGA2560) || defined(CPU_ATMEGA1281)
|
|
if (line < 8) {
|
|
DDRF &= ~(1 << line);
|
|
PORTF &= ~(1 << line);
|
|
}
|
|
#if defined(CPU_ATMEGA2560)
|
|
else {
|
|
DDRK &= ~(1 << (line-8));
|
|
PORTK &= ~(1 << (line-8));
|
|
}
|
|
#endif /* CPU_ATMEGA2560 */
|
|
#endif /* CPU_ATMEGA328P */
|
|
|
|
/* set clock prescaler to get the maximal possible ADC clock value */
|
|
for (uint32_t clk_div = 1; clk_div < 8; ++clk_div) {
|
|
if ((CLOCK_CORECLOCK / (1 << clk_div)) <= ADC_MAX_CLK) {
|
|
ADCSRA |= clk_div;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Ref Voltage is Vcc(5V) */
|
|
ADMUX |= (1 << REFS0);
|
|
|
|
_done();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t adc_sample(adc_t line, adc_res_t res)
|
|
{
|
|
int sample = 0;
|
|
|
|
/* check if resolution is applicable */
|
|
if (res != ADC_RES_10BIT) {
|
|
return -1;
|
|
}
|
|
|
|
_prep();
|
|
|
|
/* set conversion channel */
|
|
#if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA1281) || defined(CPU_ATMEGA1284P) || defined(CPU_ATMEGA32U4)
|
|
ADMUX &= 0xf0;
|
|
ADMUX |= line;
|
|
#elif defined(CPU_ATMEGA2560) || defined(CPU_ATMEGA128RFA1) || defined(CPU_ATMEGA256RFR2)
|
|
if (line < 8) {
|
|
ADCSRB &= ~(1 << MUX5);
|
|
ADMUX &= 0xf0;
|
|
ADMUX |= line;
|
|
}
|
|
else {
|
|
ADCSRB |= (1 << MUX5);
|
|
ADMUX &= 0xf0;
|
|
ADMUX |= (line-8);
|
|
}
|
|
#endif
|
|
|
|
/* Start a new conversion. By default, this conversion will
|
|
be performed in single conversion mode. */
|
|
ADCSRA |= (1 << ADSC);
|
|
|
|
/* Wait until the conversion is complete */
|
|
while (ADCSRA & (1 << ADSC)) {}
|
|
|
|
/* Get conversion result */
|
|
sample = ADC;
|
|
|
|
/* Clear the ADIF flag */
|
|
ADCSRA |= (1 << ADIF);
|
|
|
|
_done();
|
|
|
|
return sample;
|
|
}
|