Skip to content

ADC Driver

QMK 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.

This driver currently supports both AVR and a limited selection of ARM devices. The values returned are 10-bit integers (0-1023) mapped between 0V and VCC (usually 5V or 3.3V for AVR, 3.3V only for ARM), however on ARM there is more flexibility in control of operation through #defines if you need more precision.

Usage

To use this driver, add the following to your rules.mk:

make
ANALOG_DRIVER_REQUIRED = yes

Then place this include at the top of your code:

c
#include "analog.h"

Channels

AVR

ChannelAT90USB64/128ATmega16/32U4ATmega32AATmega328/P
0F0F0A0C0
1F1F1A1C1
2F2A2C2
3F3A3C3
4F4F4A4C4
5F5F5A5C5
6F6F6A6*
7F7F7A7*
8D4
9D6
10D7
11B4
12B5
13B6

* The ATmega328/P 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.

ARM

STM32

Note 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.

Also note that the F0 and F3 use different numbering schemes. The F0 has a single ADC and the channels are 0-indexed, whereas the F3 has 4 ADCs and the channels are 1-indexed. This is because the F0 uses the ADCv1 implementation of the ADC, whereas the F3 uses the ADCv3 implementation.

ADCChannelSTM32F0xxSTM32F1xxSTM32F3xxSTM32F4xx
10A0A0A0
11A1A1A0A1
12A2A2A1A2
13A3A3A2A3
14A4A4A3A4
15A5A5F4A5
16A6A6C0A6
17A7A7C1A7
18B0B0C2B0
19B1B1C3B1
110C0C0F2C0
111C1C1C1
112C2C2C2
113C3C3C3
114C4C4C4
115C5C5C5
116
20A0¹A0²
21A1¹A4A1²
22A2¹A5A2²
23A3¹A6A3²
24A4¹A7A4²
25A5¹C4A5²
26A6¹C0A6²
27A7¹C1A7²
28B0¹C2B0²
29B1¹C3B1²
210C0¹F2C0²
211C1¹C5C1²
212C2¹B2C2²
213C3¹C3²
214C4¹C4²
215C5¹C5²
216
30A0¹A0²
31A1¹B1A1²
32A2¹E9A2²
33A3¹E13A3²
34F6¹F6²
35F7¹B13F7²
36F8¹E8F8²
37F9¹D10F9²
38F10¹D11F10²
39D12F3²
310C0¹D13C0²
311C1¹D14C1²
312C2¹B0C2²
313C3¹E7C3²
314E10F4²
315E11F5²
316E12
41E14
42E15
43B12
44B14
45B15
46E8
47D10
48D11
49D12
410D13
411D14
412D8
413D9
414
415
416

¹ As of ChibiOS 20.3.4, the ADC driver for STM32F1xx devices supports only ADC1, therefore any configurations involving ADC2 or ADC3 cannot actually be used. In particular, pins F6F10, which are present at least on some STM32F103x[C-G] devices, cannot be used as ADC inputs because of this driver limitation.

² Not all STM32F4xx devices have ADC2 and/or ADC3, therefore some configurations shown in this table may be unavailable; in particular, pins F4F10 cannot be used as ADC inputs on devices which do not have ADC3. Check the device datasheet to confirm which pin functions are supported.

RP2040

RP2040 has only a single ADC (ADCD1 in ChibiOS); in the QMK API the index for that ADC is 0.

ChannelPin
0GP26
1GP27
2GP28
3GP29
4Temperature sensor*

* The temperature sensor is disabled by default and needs to be enabled by the RP2040-specific function: adcRPEnableTS(&ADCD1). The ADC must be initialized before calling that function; an easy way to ensure that is to perform a dummy conversion.

Functions

AVR

FunctionDescription
analogReference(mode)Sets the analog voltage reference source. Must be one of ADC_REF_EXTERNAL, ADC_REF_POWER or ADC_REF_INTERNAL.
analogReadPin(pin)Reads the value from the specified pin, eg. F6 for ADC6 on the ATmega32U4.
pinToMux(pin)Translates a given pin to a mux value. If an unsupported pin is given, returns the mux value for "0V (GND)".
adc_read(mux)Reads the value from the ADC according to the specified mux. See your MCU's datasheet for more information.

ARM

FunctionDescription
analogReadPin(pin)Reads the value from the specified 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.
analogReadPinAdc(pin, adc)Reads the value from the specified 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.
pinToMux(pin)Translates a given pin to a channel and ADC combination. If an unsupported pin is given, returns the mux value for "0V (GND)".
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.

Configuration

ARM

The 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. Please consult the corresponding hal_adc_lld.h in ChibiOS for your specific microcontroller for further documentation on your available options.

#defineTypeDefaultDescription
ADC_CIRCULAR_BUFFERboolfalseIf true, then the implementation will use a circular buffer.
ADC_NUM_CHANNELSint1Sets the number of channels that will be scanned as part of an ADC operation. The current implementation only supports 1.
ADC_BUFFER_DEPTHint2Sets the depth of each result. Since we are only getting a 10-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 an 8-bit or lower result.
ADC_SAMPLING_RATEintADC_SMPR_SMP_1P5Sets the sampling rate of the ADC. By default, it is set to the fastest setting.
ADC_RESOLUTIONintADC_CFGR1_RES_10BIT or ADC_CFGR_RES_10BITSThe resolution of your result. We choose 10 bit by default, but you can opt for 12, 10, 8, or 6 bit. Different MCUs use slightly different names for the resolution constants.