aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfauxpark <fauxpark@gmail.com>2019-12-08 12:11:29 +1100
committerGitHub <noreply@github.com>2019-12-08 12:11:29 +1100
commita8320f20f76782789b274f7a8c3e3ad4278a075c (patch)
treeb86b8ae732cb995dcccf5de1effa6af32149d98c
parentf275ffbdfc1cbd1965cd3546b45a7838012321da (diff)
downloadqmk_firmware-a8320f20f76782789b274f7a8c3e3ad4278a075c.tar.gz
qmk_firmware-a8320f20f76782789b274f7a8c3e3ad4278a075c.zip
Improve support and docs for ADC driver (#7191)
* Improve support and docs for ADC driver * Comment ADC channels * Move to Makers and Modders section, and fix usage instructions * Flesh out intro * Superscript 328P note * Fix pin_to_mux LUT * Support USB64/1287 as well * analogReadPin() defaults to 0V mux on invalid pin * Update pinToMux() function documentation * Dot * Accept (some of) the `qmk cformat` changes * Do clang-format properly * More wording tweaks * Link to encoder docs
-rw-r--r--docs/_summary.md1
-rw-r--r--docs/adc_driver.md50
-rw-r--r--drivers/avr/analog.c110
-rw-r--r--drivers/avr/analog.h37
4 files changed, 159 insertions, 39 deletions
diff --git a/docs/_summary.md b/docs/_summary.md
index b6ee4a923..f6b03867f 100644
--- a/docs/_summary.md
+++ b/docs/_summary.md
@@ -101,6 +101,7 @@
101 * [Hand Wiring Guide](hand_wire.md) 101 * [Hand Wiring Guide](hand_wire.md)
102 * [ISP Flashing Guide](isp_flashing_guide.md) 102 * [ISP Flashing Guide](isp_flashing_guide.md)
103 * [ARM Debugging Guide](arm_debugging.md) 103 * [ARM Debugging Guide](arm_debugging.md)
104 * [ADC Driver](adc_driver.md)
104 * [I2C Driver](i2c_driver.md) 105 * [I2C Driver](i2c_driver.md)
105 * [WS2812 Driver](ws2812_driver.md) 106 * [WS2812 Driver](ws2812_driver.md)
106 * [GPIO Controls](internals_gpio_control.md) 107 * [GPIO Controls](internals_gpio_control.md)
diff --git a/docs/adc_driver.md b/docs/adc_driver.md
new file mode 100644
index 000000000..26e148add
--- /dev/null
+++ b/docs/adc_driver.md
@@ -0,0 +1,50 @@
1# ADC Driver
2
3QMK can leverage the Analog-to-Digital Converter (ADC) on supported MCUs to measure voltages on certain pins. This can be useful for implementing things such as battery level indicators for Bluetooth keyboards, or volume controls using a potentiometer, as opposed to a [rotary encoder](feature_encoders.md).
4
5This driver is currently AVR-only. The values returned are 10-bit integers (0-1023) mapped between 0V and VCC (usually 5V or 3.3V).
6
7## Usage
8
9To use this driver, add the following to your `rules.mk`:
10
11```make
12SRC += analog.c
13```
14
15Then place this include at the top of your code:
16
17```c
18#include "analog.h"
19```
20
21## Channels
22
23|Channel|AT90USB64/128|ATmega16/32U4|ATmega32A|ATmega328P|
24|-------|-------------|-------------|---------|----------|
25|0 |`F0` |`F0` |`A0` |`C0` |
26|1 |`F1` |`F1` |`A1` |`C1` |
27|2 |`F2` | |`A2` |`C2` |
28|3 |`F3` | |`A3` |`C3` |
29|4 |`F4` |`F4` |`A4` |`C4` |
30|5 |`F5` |`F5` |`A5` |`C5` |
31|6 |`F6` |`F6` |`A6` |* |
32|7 |`F7` |`F7` |`A7` |* |
33|8 | |`D4` | | |
34|9 | |`D6` | | |
35|10 | |`D7` | | |
36|11 | |`B4` | | |
37|12 | |`B5` | | |
38|13 | |`B6` | | |
39
40<sup>\* The ATmega328P possesses two extra ADC channels; however, they are not present on the DIP pinout, and are not shared with GPIO pins. You can use `adc_read()` directly to gain access to these.</sup>
41
42## Functions
43
44|Function |Description |
45|----------------------------|-------------------------------------------------------------------------------------------------------------------|
46|`analogReference(mode)` |Sets the analog voltage reference source. Must be one of `ADC_REF_EXTERNAL`, `ADC_REF_POWER` or `ADC_REF_INTERNAL`.|
47|`analogRead(pin)` |Reads the value from the specified Arduino pin, eg. `4` for ADC6 on the ATmega32U4. |
48|`analogReadPin(pin)` |Reads the value from the specified QMK pin, eg. `F6` for ADC6 on the ATmega32U4. |
49|`pinToMux(pin)` |Translates a given QMK pin to a mux value. If an unsupported pin is given, returns the mux value for "0V (GND)". |
50|`adc_read(mux)` |Reads the value from the ADC according to the specified mux. See your MCU's datasheet for more information. |
diff --git a/drivers/avr/analog.c b/drivers/avr/analog.c
index 1a8da4261..abe478b71 100644
--- a/drivers/avr/analog.c
+++ b/drivers/avr/analog.c
@@ -14,24 +14,31 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */ 15 */
16 16
17// Simple analog to digitial conversion
18
19#include <avr/io.h> 17#include <avr/io.h>
20#include <avr/pgmspace.h> 18#include <avr/pgmspace.h>
21#include <stdint.h> 19#include <stdint.h>
22#include "analog.h" 20#include "analog.h"
23 21
24static uint8_t aref = (1 << REFS0); // default to AREF = Vcc 22static uint8_t aref = ADC_REF_POWER;
25 23
26void analogReference(uint8_t mode) { aref = mode & 0xC0; } 24void analogReference(uint8_t mode) { aref = mode & (_BV(REFS1) | _BV(REFS0)); }
27 25
28// Arduino compatible pin input 26// Arduino compatible pin input
29int16_t analogRead(uint8_t pin) { 27int16_t analogRead(uint8_t pin) {
30#if defined(__AVR_ATmega32U4__) 28#if defined(__AVR_ATmega32U4__)
31 static const uint8_t PROGMEM pin_to_mux[] = {0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20}; 29 // clang-format off
30 static const uint8_t PROGMEM pin_to_mux[] = {
31 //A0 A1 A2 A3 A4 A5
32 //F7 F6 F5 F4 F1 F0
33 0x07, 0x06, 0x05, 0x04, 0x01, 0x00,
34 //A6 A7 A8 A9 A10 A11
35 //D4 D7 B4 B5 B6 D6
36 0x20, 0x22, 0x23, 0x24, 0x25, 0x21
37 };
38 // clang-format on
32 if (pin >= 12) return 0; 39 if (pin >= 12) return 0;
33 return adc_read(pgm_read_byte(pin_to_mux + pin)); 40 return adc_read(pgm_read_byte(pin_to_mux + pin));
34#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) 41#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega328P__)
35 if (pin >= 8) return 0; 42 if (pin >= 8) return 0;
36 return adc_read(pin); 43 return adc_read(pin);
37#else 44#else
@@ -39,20 +46,87 @@ int16_t analogRead(uint8_t pin) {
39#endif 46#endif
40} 47}
41 48
42// Mux input 49int16_t analogReadPin(pin_t pin) { return adc_read(pinToMux(pin)); }
50
51uint8_t pinToMux(pin_t pin) {
52 switch (pin) {
53 // clang-format off
54#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
55 case F0: return 0; // ADC0
56 case F1: return _BV(MUX0); // ADC1
57 case F2: return _BV(MUX1); // ADC2
58 case F3: return _BV(MUX1) | _BV(MUX0); // ADC3
59 case F4: return _BV(MUX2); // ADC4
60 case F5: return _BV(MUX2) | _BV(MUX0); // ADC5
61 case F6: return _BV(MUX2) | _BV(MUX1); // ADC6
62 case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
63 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
64#elif defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
65 case F0: return 0; // ADC0
66 case F1: return _BV(MUX0); // ADC1
67 case F4: return _BV(MUX2); // ADC4
68 case F5: return _BV(MUX2) | _BV(MUX0); // ADC5
69 case F6: return _BV(MUX2) | _BV(MUX1); // ADC6
70 case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
71 case D4: return _BV(MUX5); // ADC8
72 case D6: return _BV(MUX5) | _BV(MUX0); // ADC9
73 case D7: return _BV(MUX5) | _BV(MUX1); // ADC10
74 case B4: return _BV(MUX5) | _BV(MUX1) | _BV(MUX0); // ADC11
75 case B5: return _BV(MUX5) | _BV(MUX2); // ADC12
76 case B6: return _BV(MUX5) | _BV(MUX2) | _BV(MUX0); // ADC13
77 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
78#elif defined(__AVR_ATmega32A__)
79 case A0: return 0; // ADC0
80 case A1: return _BV(MUX0); // ADC1
81 case A2: return _BV(MUX1); // ADC2
82 case A3: return _BV(MUX1) | _BV(MUX0); // ADC3
83 case A4: return _BV(MUX2); // ADC4
84 case A5: return _BV(MUX2) | _BV(MUX0); // ADC5
85 case A6: return _BV(MUX2) | _BV(MUX1); // ADC6
86 case A7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
87 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
88#elif defined(__AVR_ATmega328P__)
89 case C0: return 0; // ADC0
90 case C1: return _BV(MUX0); // ADC1
91 case C2: return _BV(MUX1); // ADC2
92 case C3: return _BV(MUX1) | _BV(MUX0); // ADC3
93 case C4: return _BV(MUX2); // ADC4
94 case C5: return _BV(MUX2) | _BV(MUX0); // ADC5
95 // ADC7:6 not present in DIP package and not shared by GPIO pins
96 default: return _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
97#endif
98 // clang-format on
99 }
100}
101
43int16_t adc_read(uint8_t mux) { 102int16_t adc_read(uint8_t mux) {
44#if defined(__AVR_AT90USB162__)
45 return 0;
46#else
47 uint8_t low; 103 uint8_t low;
48 104
49 ADCSRA = (1 << ADEN) | ADC_PRESCALER; // enable ADC 105 // Enable ADC and configure prescaler
50 ADCSRB = (1 << ADHSM) | (mux & 0x20); // high speed mode 106 ADCSRA = _BV(ADEN) | ADC_PRESCALER;
51 ADMUX = aref | (mux & 0x1F); // configure mux input 107
52 ADCSRA = (1 << ADEN) | ADC_PRESCALER | (1 << ADSC); // start the conversion 108#if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
53 while (ADCSRA & (1 << ADSC)) 109 // High speed mode and ADC8-13
54 ; // wait for result 110 ADCSRB = _BV(ADHSM) | (mux & _BV(MUX5));
55 low = ADCL; // must read LSB first 111#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
56 return (ADCH << 8) | low; // must read MSB only once! 112 // High speed mode only
113 ADCSRB = _BV(ADHSM);
114#endif
115
116 // Configure mux input
117#if defined(MUX4)
118 ADMUX = aref | (mux & (_BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));
119#else
120 ADMUX = aref | (mux & (_BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));
57#endif 121#endif
122
123 // Start the conversion
124 ADCSRA |= _BV(ADSC);
125 // Wait for result
126 while (ADCSRA & _BV(ADSC))
127 ;
128 // Must read LSB first
129 low = ADCL;
130 // Must read MSB only once!
131 return (ADCH << 8) | low;
58} 132}
diff --git a/drivers/avr/analog.h b/drivers/avr/analog.h
index 1b773d82c..058882450 100644
--- a/drivers/avr/analog.h
+++ b/drivers/avr/analog.h
@@ -14,45 +14,40 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */ 15 */
16 16
17#ifndef _analog_h_included__ 17#pragma once
18#define _analog_h_included__
19 18
20#include <stdint.h> 19#include <stdint.h>
20#include "quantum.h"
21 21
22#ifdef __cplusplus 22#ifdef __cplusplus
23extern "C" { 23extern "C" {
24#endif 24#endif
25void analogReference(uint8_t mode); 25void analogReference(uint8_t mode);
26int16_t analogRead(uint8_t pin); 26int16_t analogRead(uint8_t pin);
27
28int16_t analogReadPin(pin_t pin);
29uint8_t pinToMux(pin_t pin);
30
27int16_t adc_read(uint8_t mux); 31int16_t adc_read(uint8_t mux);
28#ifdef __cplusplus 32#ifdef __cplusplus
29} 33}
30#endif 34#endif
31 35
32#define ADC_REF_POWER (1 << REFS0) 36#define ADC_REF_EXTERNAL 0 // AREF, Internal Vref turned off
33#define ADC_REF_INTERNAL ((1 << REFS1) | (1 << REFS0)) 37#define ADC_REF_POWER _BV(REFS0) // AVCC with external capacitor on AREF pin
34#define ADC_REF_EXTERNAL (0) 38#define ADC_REF_INTERNAL (_BV(REFS1) | _BV(REFS0)) // Internal 2.56V Voltage Reference with external capacitor on AREF pin (1.1V for 328P)
35 39
36// These prescaler values are for high speed mode, ADHSM = 1 40// These prescaler values are for high speed mode, ADHSM = 1
37#if F_CPU == 16000000L 41#if F_CPU == 16000000L || F_CPU == 12000000L
38# define ADC_PRESCALER ((1 << ADPS2) | (1 << ADPS1)) 42# define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS1)) // /64
39#elif F_CPU == 8000000L 43#elif F_CPU == 8000000L
40# define ADC_PRESCALER ((1 << ADPS2) | (1 << ADPS0)) 44# define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS0)) // /32
41#elif F_CPU == 4000000L 45#elif F_CPU == 4000000L
42# define ADC_PRESCALER ((1 << ADPS2)) 46# define ADC_PRESCALER (_BV(ADPS2)) // /16
43#elif F_CPU == 2000000L 47#elif F_CPU == 2000000L
44# define ADC_PRESCALER ((1 << ADPS1) | (1 << ADPS0)) 48# define ADC_PRESCALER (_BV(ADPS1) | _BV(ADPS0)) // /8
45#elif F_CPU == 1000000L 49#elif F_CPU == 1000000L
46# define ADC_PRESCALER ((1 << ADPS1)) 50# define ADC_PRESCALER _BV(ADPS1) // /4
47#else 51#else
48# define ADC_PRESCALER ((1 << ADPS0)) 52# define ADC_PRESCALER _BV(ADPS0) // /2
49#endif
50
51// some avr-libc versions do not properly define ADHSM
52#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
53# if !defined(ADHSM)
54# define ADHSM (7)
55# endif
56#endif
57
58#endif 53#endif