aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/adc_driver.md105
-rw-r--r--drivers/arm/analog.c220
-rw-r--r--drivers/arm/analog.h60
3 files changed, 384 insertions, 1 deletions
diff --git a/docs/adc_driver.md b/docs/adc_driver.md
index 26e148add..d808a8215 100644
--- a/docs/adc_driver.md
+++ b/docs/adc_driver.md
@@ -2,7 +2,7 @@
2 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). 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 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). 5This driver currently supports both AVR and a limited selection of ARM devices. On AVR devices, the values returned are 10-bit integers (0-1023) mapped between 0V and VCC (usually 5V or 3.3V). On supported ARM devices, there is more flexibility in control of operation through `#define`s, but by default the values returned are 12-bit integers (0-4095) mapped between 0V and VCC (usually 3.3V).
6 6
7## Usage 7## Usage
8 8
@@ -20,6 +20,8 @@ Then place this include at the top of your code:
20 20
21## Channels 21## Channels
22 22
23### AVR
24
23|Channel|AT90USB64/128|ATmega16/32U4|ATmega32A|ATmega328P| 25|Channel|AT90USB64/128|ATmega16/32U4|ATmega32A|ATmega328P|
24|-------|-------------|-------------|---------|----------| 26|-------|-------------|-------------|---------|----------|
25|0 |`F0` |`F0` |`A0` |`C0` | 27|0 |`F0` |`F0` |`A0` |`C0` |
@@ -39,8 +41,84 @@ Then place this include at the top of your code:
39 41
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> 42<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 43
44### ARM
45
46Note that some of these pins are doubled-up on ADCs with the same channel. This is because the pins can be used for either ADC.
47
48Also note that the F0 and F3 use different numbering schemes. The F0 has a single ADC and the channels are 0-based, whereas the F3 has 4 ADCs and the channels are 1 based. This is because the F0 uses the `ADCv1` implementation of the ADC, whereas the F3 uses the `ADCv3` implementation.
49
50|ADC|Channel|STM32F0XX|STM32F3XX|
51|---|-------|---------|---------|
52|1 |0 |`A0` | |
53|1 |1 |`A1` |`A0` |
54|1 |2 |`A2` |`A1` |
55|1 |3 |`A3` |`A2` |
56|1 |4 |`A4` |`A3` |
57|1 |5 |`A5` |`F4` |
58|1 |6 |`A6` |`C0` |
59|1 |7 |`A7` |`C1` |
60|1 |8 |`B0` |`C2` |
61|1 |9 |`B1` |`C3` |
62|1 |10 |`C0` |`F2` |
63|1 |11 |`C1` | |
64|1 |12 |`C2` | |
65|1 |13 |`C3` | |
66|1 |14 |`C4` | |
67|1 |15 |`C5` | |
68|1 |16 | | |
69|2 |1 | |`A4` |
70|2 |2 | |`A5` |
71|2 |3 | |`A6` |
72|2 |4 | |`A7` |
73|2 |5 | |`C4` |
74|2 |6 | |`C0` |
75|2 |7 | |`C1` |
76|2 |8 | |`C2` |
77|2 |9 | |`C3` |
78|2 |10 | |`F2` |
79|2 |11 | |`C5` |
80|2 |12 | |`B2` |
81|2 |13 | | |
82|2 |14 | | |
83|2 |15 | | |
84|2 |16 | | |
85|3 |1 | |`B1` |
86|3 |2 | |`E9` |
87|3 |3 | |`E13` |
88|3 |4 | | |
89|3 |5 | | |
90|3 |6 | |`E8` |
91|3 |7 | |`D10` |
92|3 |8 | |`D11` |
93|3 |9 | |`D12` |
94|3 |10 | |`D13` |
95|3 |11 | |`D14` |
96|3 |12 | |`B0` |
97|3 |13 | |`E7` |
98|3 |14 | |`E10` |
99|3 |15 | |`E11` |
100|3 |16 | |`E12` |
101|4 |1 | |`E14` |
102|4 |2 | |`B12` |
103|4 |3 | |`B13` |
104|4 |4 | |`B14` |
105|4 |5 | |`B15` |
106|4 |6 | |`E8` |
107|4 |7 | |`D10` |
108|4 |8 | |`D11` |
109|4 |9 | |`D12` |
110|4 |10 | |`D13` |
111|4 |11 | |`D14` |
112|4 |12 | |`D8` |
113|4 |13 | |`D9` |
114|4 |14 | | |
115|4 |15 | | |
116|4 |16 | | |
117
42## Functions 118## Functions
43 119
120### AVR
121
44|Function |Description | 122|Function |Description |
45|----------------------------|-------------------------------------------------------------------------------------------------------------------| 123|----------------------------|-------------------------------------------------------------------------------------------------------------------|
46|`analogReference(mode)` |Sets the analog voltage reference source. Must be one of `ADC_REF_EXTERNAL`, `ADC_REF_POWER` or `ADC_REF_INTERNAL`.| 124|`analogReference(mode)` |Sets the analog voltage reference source. Must be one of `ADC_REF_EXTERNAL`, `ADC_REF_POWER` or `ADC_REF_INTERNAL`.|
@@ -48,3 +126,28 @@ Then place this include at the top of your code:
48|`analogReadPin(pin)` |Reads the value from the specified QMK pin, eg. `F6` for ADC6 on the ATmega32U4. | 126|`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)". | 127|`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. | 128|`adc_read(mux)` |Reads the value from the ADC according to the specified mux. See your MCU's datasheet for more information. |
129
130### ARM
131
132Note that care was taken to match all of the functions used for AVR devices, however complications in the ARM platform prevent that from always being possible. For example, the `STM32` chips do not have assigned Arduino pins. We could use the default pin numbers, but those numbers change based on the package type of the device. For this reason, please specify your target pins with their identifiers (`A0`, `F3`, etc.). Also note that there are some variants of functions that accept the target ADC for the pin. Some pins can be used for multiple ADCs, and this specified can help you pick which ADC will be used to interact with that pin.
133
134|Function |Description |
135|----------------------------|--------------------------------------------------------------------------------------------------------------------|
136|`analogReadPin(pin)` |Reads the value from the specified QMK pin, eg. `A0` for channel 0 on the STM32F0 and ADC1 channel 1 on the STM32F3. Note that if a pin can be used for multiple ADCs, it will pick the lower numbered ADC for this function. eg. `C0` will be channel 6 of ADC 1 when it could be used for ADC 2 as well.|
137|`analogReadPinAdc(pin, adc)`|Reads the value from the specified QMK pin and ADC, eg. `C0, 1` will read from channel 6, ADC 2 instead of ADC 1. Note that the ADCs are 0-indexed for this function.|
138|`pinToMux(pin)` |Translates a given QMK pin to a channel and ADC combination. If an unsupported pin is given, returns the mux value for "0V (GND)".|
139|`adc_read(mux)` |Reads the value from the ADC according to the specified pin and adc combination. See your MCU's datasheet for more information.|
140
141## Configuration
142
143## ARM
144
145The ARM implementation of the ADC has a few additional options that you can override in your own keyboards and keymaps to change how it operates.
146
147|`#define` |Type |Default |Description|
148|-------------------|------|---------------------|-----------|
149|ADC_CIRCULAR_BUFFER|`bool`|`false` |If `TRUE`, then the implementation will use a circular buffer.|
150|ADC_NUM_CHANNELS |`int` |`1` |Sets the number of channels that will be scanned as part of an ADC operation. The current implementation only supports `1`.|
151|ADC_BUFFER_DEPTH |`int` |`2` |Sets the depth of each result. Since we are only getting a 12-bit result by default, we set this to `2` bytes so we can contain our one value. This could be set to 1 if you opt for a 8-bit or lower result.|
152|ADC_SAMPLING_RATE |`int` |`ADC_SMPR_SMP_1P5` |Sets the sampling rate of the ADC. By default, it is set to the fastest setting. Please consult the corresponding `hal_adc_lld.h` in ChibiOS for your specific microcontroller for further documentation on your available options.|
153|ADC_RESOLUTION |`int` |`ADC_CFGR1_RES_12BIT`|The resolution of your result. We choose 12 bit by default, but you can opt for 12, 10, 8, or 6 bit. Please consult the corresponding `hal_adc_lld.h` in ChibiOS for your specific microcontroller for further documentation on your available options.|
diff --git a/drivers/arm/analog.c b/drivers/arm/analog.c
new file mode 100644
index 000000000..57f649a81
--- /dev/null
+++ b/drivers/arm/analog.c
@@ -0,0 +1,220 @@
1/* Copyright 2019 Drew Mills
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "analog.h"
18#include "quantum.h"
19
20
21/* User configurable ADC options */
22#ifndef ADC_CIRCULAR_BUFFER
23#define ADC_CIRCULAR_BUFFER FALSE
24#endif
25
26#ifndef ADC_NUM_CHANNELS
27#define ADC_NUM_CHANNELS 1
28#elif ADC_NUM_CHANNELS != 1
29#error "The ARM ADC implementation currently only supports reading one channel at a time."
30#endif
31
32#ifndef ADC_BUFFER_DEPTH
33#define ADC_BUFFER_DEPTH 2
34#endif
35
36// For more sampling rate options, look at hal_adc_lld.h in ChibiOS
37#ifndef ADC_SAMPLING_RATE
38#define ADC_SAMPLING_RATE ADC_SMPR_SMP_1P5
39#endif
40
41// Options are 12, 10, 8, and 6 bit.
42#ifndef ADC_RESOLUTION
43#define ADC_RESOLUTION ADC_CFGR1_RES_12BIT
44#endif
45
46
47
48static ADCConfig adcCfg = {};
49static adcsample_t sampleBuffer[ADC_NUM_CHANNELS * ADC_BUFFER_DEPTH];
50
51// Initialize to max number of ADCs, set to empty object to initialize all to false.
52#if defined(STM32F0XX)
53static bool adcInitialized[1] = {};
54#elif defined(STM32F3XX)
55static bool adcInitialized[4] = {};
56#else
57#error "adcInitialized has not been implemented for this ARM microcontroller."
58#endif
59
60
61
62static ADCConversionGroup adcConversionGroup = {
63 ADC_CIRCULAR_BUFFER,
64 (uint16_t)(ADC_NUM_CHANNELS),
65 NULL, // No end callback
66 NULL, // No error callback
67#if defined(STM32F0XX)
68 ADC_CFGR1_CONT | ADC_RESOLUTION,
69 ADC_TR(0, 0).
70 ADC_SAMPLING_RATE,
71 NULL, // Doesn't specify a default channel
72#elif defined(STM32F3XX)
73 ADC_CFGR_CONT | ADC_RESOLUTION,
74 ADC_TR(0, 4095),
75 {
76 ADC_SAMPLING_RATE,
77 ADC_SAMPLING_RATE,
78 },
79 {
80 0, // Doesn't specify a default channel
81 0,
82 0,
83 0,
84 },
85#endif
86};
87
88
89
90static inline ADCDriver* intToADCDriver(uint8_t adcInt) {
91
92 ADCDriver* target;
93
94 switch (adcInt) {
95 // clang-format off
96#if STM32_ADC_USE_ADC1
97 case 0: target = &ADCD1; break;
98#endif
99#if STM32_ADC_USE_ADC2
100 case 1: target = &ADCD2; break;
101#endif
102#if STM32_ADC_USE_ADC3
103 case 2: target = &ADCD3; break;
104#endif
105#if STM32_ADC_USE_ADC4
106 case 3: target = &ADCD4; break;
107#endif
108 default: target = NULL; break;
109 // clang-format on
110 }
111
112 return target;
113}
114
115static inline void manageAdcInitializationDriver(uint8_t adc, ADCDriver* adcDriver) {
116 if (!adcInitialized[adc]) {
117 adcStart(adcDriver, &adcCfg);
118 adcInitialized[adc] = true;
119 }
120}
121
122static inline void manageAdcInitialization(uint8_t adc) {
123 manageAdcInitializationDriver(adc, intToADCDriver(adc));
124}
125
126pin_and_adc pinToMux(pin_t pin) {
127 switch(pin) {
128 // clang-format off
129#if defined(STM32F0XX)
130 case A0: return (pin_and_adc){ ADC_CHANNEL_IN0, 0 };
131 case A1: return (pin_and_adc){ ADC_CHANNEL_IN1, 0 };
132 case A2: return (pin_and_adc){ ADC_CHANNEL_IN2, 0 };
133 case A3: return (pin_and_adc){ ADC_CHANNEL_IN3, 0 };
134 case A4: return (pin_and_adc){ ADC_CHANNEL_IN4, 0 };
135 case A5: return (pin_and_adc){ ADC_CHANNEL_IN5, 0 };
136 case A6: return (pin_and_adc){ ADC_CHANNEL_IN6, 0 };
137 case A7: return (pin_and_adc){ ADC_CHANNEL_IN7, 0 };
138 case B0: return (pin_and_adc){ ADC_CHANNEL_IN8, 0 };
139 case B1: return (pin_and_adc){ ADC_CHANNEL_IN9, 0 };
140 case C0: return (pin_and_adc){ ADC_CHANNEL_IN10, 0 };
141 case C1: return (pin_and_adc){ ADC_CHANNEL_IN11, 0 };
142 case C2: return (pin_and_adc){ ADC_CHANNEL_IN12, 0 };
143 case C3: return (pin_and_adc){ ADC_CHANNEL_IN13, 0 };
144 case C4: return (pin_and_adc){ ADC_CHANNEL_IN14, 0 };
145 case C5: return (pin_and_adc){ ADC_CHANNEL_IN15, 0 };
146#elif defined(STM32F3XX)
147 case A0: return (pin_and_adc){ ADC_CHANNEL_IN1, 0 };
148 case A1: return (pin_and_adc){ ADC_CHANNEL_IN2, 0 };
149 case A2: return (pin_and_adc){ ADC_CHANNEL_IN3, 0 };
150 case A3: return (pin_and_adc){ ADC_CHANNEL_IN4, 0 };
151 case A4: return (pin_and_adc){ ADC_CHANNEL_IN1, 1 };
152 case A5: return (pin_and_adc){ ADC_CHANNEL_IN2, 1 };
153 case A6: return (pin_and_adc){ ADC_CHANNEL_IN3, 1 };
154 case A7: return (pin_and_adc){ ADC_CHANNEL_IN4, 1 };
155 case B0: return (pin_and_adc){ ADC_CHANNEL_IN12, 2 };
156 case B1: return (pin_and_adc){ ADC_CHANNEL_IN1, 2 };
157 case B2: return (pin_and_adc){ ADC_CHANNEL_IN12, 1 };
158 case B12: return (pin_and_adc){ ADC_CHANNEL_IN2, 3 };
159 case B13: return (pin_and_adc){ ADC_CHANNEL_IN3, 3 };
160 case B14: return (pin_and_adc){ ADC_CHANNEL_IN4, 3 };
161 case B15: return (pin_and_adc){ ADC_CHANNEL_IN5, 3 };
162 case C0: return (pin_and_adc){ ADC_CHANNEL_IN6, 0 }; // Can also be ADC2
163 case C1: return (pin_and_adc){ ADC_CHANNEL_IN7, 0 }; // Can also be ADC2
164 case C2: return (pin_and_adc){ ADC_CHANNEL_IN8, 0 }; // Can also be ADC2
165 case C3: return (pin_and_adc){ ADC_CHANNEL_IN9, 0 }; // Can also be ADC2
166 case C4: return (pin_and_adc){ ADC_CHANNEL_IN5, 1 };
167 case C5: return (pin_and_adc){ ADC_CHANNEL_IN11, 1 };
168 case D8: return (pin_and_adc){ ADC_CHANNEL_IN12, 3 };
169 case D9: return (pin_and_adc){ ADC_CHANNEL_IN13, 3 };
170 case D10: return (pin_and_adc){ ADC_CHANNEL_IN7, 2 }; // Can also be ADC4
171 case D11: return (pin_and_adc){ ADC_CHANNEL_IN8, 2 }; // Can also be ADC4
172 case D12: return (pin_and_adc){ ADC_CHANNEL_IN9, 2 }; // Can also be ADC4
173 case D13: return (pin_and_adc){ ADC_CHANNEL_IN10, 2 }; // Can also be ADC4
174 case D14: return (pin_and_adc){ ADC_CHANNEL_IN11, 2 }; // Can also be ADC4
175 case E7: return (pin_and_adc){ ADC_CHANNEL_IN13, 2 };
176 case E8: return (pin_and_adc){ ADC_CHANNEL_IN6, 2 }; // Can also be ADC4
177 case E9: return (pin_and_adc){ ADC_CHANNEL_IN2, 2 };
178 case E10: return (pin_and_adc){ ADC_CHANNEL_IN14, 2 };
179 case E11: return (pin_and_adc){ ADC_CHANNEL_IN15, 2 };
180 case E12: return (pin_and_adc){ ADC_CHANNEL_IN16, 2 };
181 case E13: return (pin_and_adc){ ADC_CHANNEL_IN3, 2 };
182 case E14: return (pin_and_adc){ ADC_CHANNEL_IN1, 3 };
183 case E15: return (pin_and_adc){ ADC_CHANNEL_IN2, 3 };
184 case F2: return (pin_and_adc){ ADC_CHANNEL_IN10, 0 }; // Can also be ADC2
185 case F4: return (pin_and_adc){ ADC_CHANNEL_IN5, 0 };
186#else
187#error "An ADC pin-to-mux configuration has not been specified for this microcontroller."
188#endif
189 default: return (pin_and_adc){ 0, 0 };
190 // clang-format on
191 }
192}
193
194adcsample_t analogReadPin(pin_t pin) {
195 return adc_read(pinToMux(pin));
196}
197
198adcsample_t analogReadPinAdc(pin_t pin, uint8_t adc) {
199 pin_and_adc target = pinToMux(pin);
200 target.adc = adc;
201 return adc_read(target);
202}
203
204adcsample_t adc_read(pin_and_adc mux) {
205#if defined(STM32F0XX)
206 adcConversionGroup.sqr = ADC_CHSELR_CHSEL1;
207#elif defined(STM32F3XX)
208 adcConversionGroup.sqr[0] = ADC_SQR1_SQ1_N(mux.pin);
209#else
210#error "adc_read has not been updated to support this ARM microcontroller."
211#endif
212
213 ADCDriver* targetDriver = intToADCDriver(mux.adc);
214 manageAdcInitializationDriver(mux.adc, targetDriver);
215
216 adcConvert(targetDriver, &adcConversionGroup, &sampleBuffer[0], ADC_BUFFER_DEPTH);
217 adcsample_t* result = sampleBuffer;
218
219 return *result;
220}
diff --git a/drivers/arm/analog.h b/drivers/arm/analog.h
new file mode 100644
index 000000000..081d0c1e7
--- /dev/null
+++ b/drivers/arm/analog.h
@@ -0,0 +1,60 @@
1/* Copyright 2019 Drew Mills
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "quantum.h"
20#include "ch.h"
21#include <hal.h>
22
23
24#if !defined(STM32F0XX) && !defined(STM32F3XX)
25#error "Only STM23F0 and STM32F3 devices have ADC support in QMK at this time."
26#endif
27
28#if !HAL_USE_ADC
29#error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC."
30#endif
31
32#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4
33#error "You need to set one of the 'STM32_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC."
34#endif
35
36#if STM32_ADC_DUAL_MODE
37#error "STM32 ADC Dual Mode is not supported at this time."
38#endif
39
40#if STM32_ADCV3_OVERSAMPLING
41#error "STM32 ADCV3 Oversampling is not supported at this time."
42#endif
43
44
45
46typedef struct {
47 pin_t pin;
48 uint8_t adc;
49} pin_and_adc;
50#define PIN_AND_ADC(p,a) (pin_and_adc){p,a}
51
52
53// analogReference has been left un-defined for ARM devices.
54// void analogReference(uint8_t mode);
55
56adcsample_t analogReadPin(pin_t pin);
57adcsample_t analogReadPinAdc(pin_t pin, uint8_t adc);
58pin_and_adc pinToMux(pin_t pin);
59
60adcsample_t adc_read(pin_and_adc mux);