-
Notifications
You must be signed in to change notification settings - Fork 2.1k
cpu/atmega_common: add adc driver #7227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -155,9 +155,27 @@ extern "C" { | |
| #define ARDUINO_PIN_A14 ARDUINO_PIN_68 | ||
| #define ARDUINO_PIN_A15 ARDUINO_PIN_69 | ||
| #endif | ||
|
|
||
| /** @ */ | ||
|
|
||
| #define ARDUINO_A0 ADC_LINE(0) | ||
| #define ARDUINO_A1 ADC_LINE(1) | ||
| #define ARDUINO_A2 ADC_LINE(2) | ||
| #define ARDUINO_A3 ADC_LINE(3) | ||
| #define ARDUINO_A4 ADC_LINE(4) | ||
| #define ARDUINO_A5 ADC_LINE(5) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Code duplication here. You can omit the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Atmega328p (arduino nano) has also lines A6 and A7, please move the following |
||
| #define ARDUINO_A6 ADC_LINE(6) | ||
| #define ARDUINO_A7 ADC_LINE(7) | ||
| #ifdef CPU_ATMEGA2560 | ||
| #define ARDUINO_A8 ADC_LINE(8) | ||
| #define ARDUINO_A9 ADC_LINE(9) | ||
| #define ARDUINO_A10 ADC_LINE(10) | ||
| #define ARDUINO_A11 ADC_LINE(11) | ||
| #define ARDUINO_A12 ADC_LINE(12) | ||
| #define ARDUINO_A13 ADC_LINE(13) | ||
| #define ARDUINO_A14 ADC_LINE(14) | ||
| #define ARDUINO_A15 ADC_LINE(15) | ||
| #endif | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| /* | ||
| * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen | ||
| * 2016 Laurent Navet <[email protected]> | ||
| * 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 | ||
|
|
@@ -17,6 +18,7 @@ | |
| * @author Hinnerk van Bruinehsen <[email protected]> | ||
| * @author Laurent Navet <[email protected]> | ||
| * @author Hauke Petersen <[email protected]> | ||
| * @author Dimitri Nahm <[email protected]> | ||
| */ | ||
|
|
||
| #ifndef PERIPH_CONF_H | ||
|
|
@@ -136,9 +138,33 @@ extern "C" { | |
| /** @} */ | ||
|
|
||
| /** | ||
| * @brief I2C configuration | ||
| * @name I2C configuration | ||
| * @{ | ||
| */ | ||
| #define I2C_NUMOF 1 | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name ADC configuration | ||
| * | ||
| * The number of ADC channels of the atmega328p depends on the package: | ||
| * - 6-channel 10-bit ADC in PDIP package | ||
| * - 8-channel 10-bit ADC in TQFP and QFN/MLF package | ||
| * Arduino UNO / Duemilanove has thereby 6 channels. But only 5 channels can be | ||
| * used for ADC, because the pin of ADC5 emulate a software triggered interrupt. | ||
| * -> boards -> arduino-atmega-common -> include -> board_common.h | ||
| * @{ | ||
| */ | ||
| #if defined (CPU_ATMEGA328P) | ||
| #define ADC_NUMOF (8) | ||
| #elif defined (CPU_ATMEGA2560) | ||
| #define ADC_NUMOF (16) | ||
| #endif | ||
|
|
||
| #ifdef CPU_ATMEGA1281 | ||
| #define ADC_NUMOF (8) | ||
| #endif | ||
| /** @} */ | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,9 +104,27 @@ extern "C" { | |
| /** @} */ | ||
|
|
||
| /** | ||
| * @brief I2C configuration | ||
| * @name I2C configuration | ||
| * @{ | ||
| */ | ||
| #define I2C_NUMOF 1 | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name ADC configuration | ||
| * @{ | ||
| */ | ||
| #define ADC_NUMOF 8 | ||
| /* ADC Channels */ | ||
| #define ADC_PIN_0 0 | ||
| #define ADC_PIN_1 1 | ||
| #define ADC_PIN_2 2 | ||
| #define ADC_PIN_3 3 | ||
| #define ADC_PIN_4 4 | ||
| #define ADC_PIN_5 5 | ||
| #define ADC_PIN_6 6 | ||
| #define ADC_PIN_7 7 | ||
| /** @} */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is no Arduino board. So I will remove ADC channel configuration. |
||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| /* | ||
| * Copyright (C) 2016 Laurent Navet <[email protected]> | ||
| * 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 <[email protected]> | ||
| * @author Dimitri Nahm <[email protected]> | ||
| * | ||
| * @} | ||
| */ | ||
|
|
||
| #include "cpu.h" | ||
| #include "mutex.h" | ||
| #include "assert.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) | ||
| { | ||
| uint32_t clk_div = 1; | ||
|
|
||
| /* check if the line is valid */ | ||
| if (line >= ADC_NUMOF) { | ||
| return -1; | ||
| } | ||
|
|
||
| prep(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ADC doesn't need to be enabled here
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it must be locked |
||
|
|
||
| /* Disable corresponding Digital input */ | ||
| #if defined (CPU_ATMEGA328P) || defined (CPU_ATMEGA1281) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indention if of by one tab for all |
||
| DIDR0 |= (1 << line); | ||
| #elif defined (CPU_ATMEGA2560) | ||
| if (line < 8) | ||
| DIDR0 |= (1 << line); | ||
| else | ||
| DIDR2 |= (1 << (line - 8)); | ||
| #endif | ||
|
|
||
| /* Set ADC-pin as input */ | ||
| #if defined (CPU_ATMEGA328P) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not simply calling if (line < 8) {
gpio_init(GPIO_PIN(PORT_C, line), GPIO_IN);
}
#ifndef CPU_ATMEGA328P
else {
gpio_init(GPIO_PIN(PORT_K, line - 8), GPIO_IN);
}
#endif |
||
| DDRC &= ~(1 << line); | ||
| PORTC &= ~(1 << line); | ||
| #elif defined (CPU_ATMEGA2560) | ||
| if (line < 8) { | ||
| DDRF &= ~(1 << line); | ||
| PORTF &= ~(1 << line); | ||
| } | ||
| else { | ||
| DDRK &= ~(1 << (line-8)); | ||
| PORTK &= ~(1 << (line-8)); | ||
| } | ||
| #elif defined (CPU_ATMEGA1281) | ||
| DDRF &= ~(1 << line); | ||
| PORTF &= ~(1 << line); | ||
| #endif | ||
|
|
||
| /* set clock prescaler to get the maximal possible ADC clock value */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idea: since prescalers just depends in the coreclock, can be set by the pre-processor
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in #7502 its the other way around, in general I'd say the compiler should optimise that out anyway. But to be sure, you could make a size comparison. However, I would leave as is for now. |
||
| for (clk_div = 1; clk_div < 8; clk_div++) { | ||
| if ((CLOCK_CORECLOCK / (1 << clk_div)) <= ADC_MAX_CLK) { | ||
| break; | ||
| } | ||
| } | ||
| ADCSRA |= clk_div; | ||
|
|
||
| /* Ref Voltage is Vcc(5V) */ | ||
| ADMUX |= (1 << REFS0); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't force it to 5V, it' might be interesting to have VREF as reference voltage. Plus in the 1281/2560 it can be set to differential measurements which might be quite useful. I suggest introduce a configuration struct, which allows to configure it for each channel. Then set the reference when doing the triggering the sample. typedef struct {
gpio_t pin;
adc_ref_t ref;
uint8_t ch;
} adc_conf_t;
...
static const adc_conf_t adc_conf[] = {
{ GPIO_PIN(ADC_PORT, 0), ADC_REF_AVCC, 0 },
{ GPIO_PIN(ADC_PORT, 1), ADC_REF_AVCC, 1 },
{ GPIO_PIN(ADC_PORT, 2), ADC_REF_AVCC, 2 },
...
}This also simplifies the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general a good idea, specifically to have the reference voltage configurable. What I don't like is the duplication in
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but maybe we should get general ADC support first and fix this later on. A first step could also be to make the ref voltage configurable via define macro (though it applies to all pins then). |
||
|
|
||
| done(); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| int adc_sample(adc_t line, adc_res_t res) | ||
| { | ||
| int sample = 0; | ||
|
|
||
| /* check if resolution is applicable */ | ||
| assert(res == ADC_RES_10BIT); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be a return. Documentation states: |
||
|
|
||
| prep(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it has to be enabled after setting the other registers otherwise some of the registers of the 328p can't be written |
||
|
|
||
| /* set conversion channel */ | ||
| #if defined (CPU_ATMEGA328P) || defined (CPU_ATMEGA1281) | ||
| ADMUX &= 0xf0; | ||
| ADMUX |= line; | ||
| #endif | ||
| #ifdef CPU_ATMEGA2560 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not use a straight forward |
||
| if(line < 8) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. coding style, missing space after |
||
| 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; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Result is 10 bits, you need to read two registers (in the correct order to get the result)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both registers (ADCL, ADCH) are read out via ADC register |
||
|
|
||
| /* Clear the ADIF flag */ | ||
| ADCSRA |= (1 << ADIF); | ||
|
|
||
| done(); | ||
|
|
||
| return sample; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,5 +82,15 @@ int digitalRead(int pin); | |
| */ | ||
| void delay(unsigned long msec); | ||
|
|
||
| /** | ||
| * @brief Read the current value of the given analog pin | ||
| * | ||
| * @param[in] pin pin to read | ||
| * | ||
| * @return a value between 0 to 1023 that is proportionnal | ||
| * to the voltage applied to the pin | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. according the above, it may also return |
||
| */ | ||
| int analogRead(int pin); | ||
|
|
||
| #endif /* ARDUINO_H */ | ||
| /** @} */ | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you shoud re-add the
ARDUINO_Axdefines!