1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/atmega_common/periph/adc.c
2020-10-22 11:13:09 +02:00

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;
}