qmk_firmware/docs/adc_driver.md

12 KiB

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:

SRC += analog.c

Then place this include at the top of your code:

#include "analog.h"

Channels

AVR

Channel AT90USB64/128 ATmega16/32U4 ATmega32A ATmega328/P
0 F0 F0 A0 C0
1 F1 F1 A1 C1
2 F2 A2 C2
3 F3 A3 C3
4 F4 F4 A4 C4
5 F5 F5 A5 C5
6 F6 F6 A6 *
7 F7 F7 A7 *
8 D4
9 D6
10 D7
11 B4
12 B5
13 B6

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

ADC Channel STM32F0xx STM32F1xx STM32F3xx STM32F4xx
1 0 A0 A0 A0
1 1 A1 A1 A0 A1
1 2 A2 A2 A1 A2
1 3 A3 A3 A2 A3
1 4 A4 A4 A3 A4
1 5 A5 A5 F4 A5
1 6 A6 A6 C0 A6
1 7 A7 A7 C1 A7
1 8 B0 B0 C2 B0
1 9 B1 B1 C3 B1
1 10 C0 C0 F2 C0
1 11 C1 C1 C1
1 12 C2 C2 C2
1 13 C3 C3 C3
1 14 C4 C4 C4
1 15 C5 C5 C5
1 16
2 0 A0¹ A0²
2 1 A1¹ A4 A1²
2 2 A2¹ A5 A2²
2 3 A3¹ A6 A3²
2 4 A4¹ A7 A4²
2 5 A5¹ C4 A5²
2 6 A6¹ C0 A6²
2 7 A7¹ C1 A7²
2 8 B0¹ C2 B0²
2 9 B1¹ C3 B1²
2 10 C0¹ F2 C0²
2 11 C1¹ C5 C1²
2 12 C2¹ B2 C2²
2 13 C3¹ C3²
2 14 C4¹ C4²
2 15 C5¹ C5²
2 16
3 0 A0¹ A0²
3 1 A1¹ B1 A1²
3 2 A2¹ E9 A2²
3 3 A3¹ E13 A3²
3 4 F6¹ F6²
3 5 F7¹ B13 F7²
3 6 F8¹ E8 F8²
3 7 F9¹ D10 F9²
3 8 F10¹ D11 F10²
3 9 D12 F3²
3 10 C0¹ D13 C0²
3 11 C1¹ D14 C1²
3 12 C2¹ B0 C2²
3 13 C3¹ E7 C3²
3 14 E10 F4²
3 15 E11 F5²
3 16 E12
4 1 E14
4 2 E15
4 3 B12
4 4 B14
4 5 B15
4 6 E8
4 7 D10
4 8 D11
4 9 D12
4 10 D13
4 11 D14
4 12 D8
4 13 D9
4 14
4 15
4 16

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

Channel Pin
0 GP26
1 GP27
2 GP28
3 GP29
4 Temperature 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

Function Description
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

Function Description
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.

#define Type Default Description
ADC_CIRCULAR_BUFFER bool false If true, then the implementation will use a circular buffer.
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.
ADC_BUFFER_DEPTH int 2 Sets 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_RATE int ADC_SMPR_SMP_1P5 Sets the sampling rate of the ADC. By default, it is set to the fastest setting.
ADC_RESOLUTION int ADC_CFGR1_RES_10BIT or ADC_CFGR_RES_10BITS The 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.