ARM - ADC cleanup (#8385)
authorJoel Challis <git@zvecr.com>
Tue, 17 Mar 2020 00:29:52 +0000 (00:29 +0000)
committerGitHub <noreply@github.com>
Tue, 17 Mar 2020 00:29:52 +0000 (00:29 +0000)
* Update switch to array to allow custom values

* Add adc keymap

* update docs to reflect alignment of default 10 bit

* start conversion to USE_ADCVn

* samplerate is hella wrong...stub out for now

* basic f1 and f4 functionality

* Tidy up current changes

* Restore old pinToMux function

* Add back sample rate for supported platforms

* F0 compile fixes

* wordsmithery

Co-Authored-By: Ryan <fauxpark@gmail.com>
* Remove reference to avr only function

Co-authored-by: Ryan <fauxpark@gmail.com>
docs/adc_driver.md
drivers/arm/analog.c
drivers/arm/analog.h
keyboards/handwired/onekey/keymaps/adc/config.h [new file with mode: 0644]
keyboards/handwired/onekey/keymaps/adc/keymap.c [new file with mode: 0644]
keyboards/handwired/onekey/keymaps/adc/rules.mk [new file with mode: 0644]

index d808a82..7c4e05e 100644 (file)
@@ -2,7 +2,7 @@
 
 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](feature_encoders.md).
 
-This 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).
+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 `#define`s if you need more precision.
 
 ## Usage
 
dissimilarity index 72%
index 427381f..a7e6d25 100644 (file)
-/* Copyright 2019 Drew Mills
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "analog.h"
-#include "quantum.h"
-
-/* User configurable ADC options */
-#ifndef ADC_CIRCULAR_BUFFER
-#    define ADC_CIRCULAR_BUFFER FALSE
-#endif
-
-#ifndef ADC_NUM_CHANNELS
-#    define ADC_NUM_CHANNELS 1
-#elif ADC_NUM_CHANNELS != 1
-#    error "The ARM ADC implementation currently only supports reading one channel at a time."
-#endif
-
-#ifndef ADC_BUFFER_DEPTH
-#    define ADC_BUFFER_DEPTH 2
-#endif
-
-// For more sampling rate options, look at hal_adc_lld.h in ChibiOS
-#ifndef ADC_SAMPLING_RATE
-#    define ADC_SAMPLING_RATE ADC_SMPR_SMP_1P5
-#endif
-
-// Options are 12, 10, 8, and 6 bit.
-#ifndef ADC_RESOLUTION
-#    define ADC_RESOLUTION ADC_CFGR1_RES_12BIT
-#endif
-
-static ADCConfig   adcCfg = {};
-static adcsample_t sampleBuffer[ADC_NUM_CHANNELS * ADC_BUFFER_DEPTH];
-
-// Initialize to max number of ADCs, set to empty object to initialize all to false.
-#if defined(STM32F0XX)
-static bool adcInitialized[1] = {};
-#elif defined(STM32F3XX)
-static bool adcInitialized[4] = {};
-#else
-#    error "adcInitialized has not been implemented for this ARM microcontroller."
-#endif
-
-static ADCConversionGroup adcConversionGroup = {
-    ADC_CIRCULAR_BUFFER,
-    (uint16_t)(ADC_NUM_CHANNELS),
-    NULL,  // No end callback
-    NULL,  // No error callback
-#if defined(STM32F0XX)
-    ADC_CFGR1_CONT | ADC_RESOLUTION,
-    ADC_TR(0, 0).ADC_SAMPLING_RATE,
-    NULL,  // Doesn't specify a default channel
-#elif defined(STM32F3XX)
-    ADC_CFGR_CONT | ADC_RESOLUTION,
-    ADC_TR(0, 4095),
-    {
-        ADC_SAMPLING_RATE,
-        ADC_SAMPLING_RATE,
-    },
-    {
-        0,  // Doesn't specify a default channel
-        0,
-        0,
-        0,
-    },
-#endif
-};
-
-static inline ADCDriver* intToADCDriver(uint8_t adcInt) {
-    ADCDriver* target;
-
-    switch (adcInt) {
-        // clang-format off
-#if STM32_ADC_USE_ADC1
-        case 0: target = &ADCD1; break;
-#endif
-#if STM32_ADC_USE_ADC2
-        case 1: target = &ADCD2; break;
-#endif
-#if STM32_ADC_USE_ADC3
-        case 2: target = &ADCD3; break;
-#endif
-#if STM32_ADC_USE_ADC4
-        case 3: target = &ADCD4; break;
-#endif
-        default: target = NULL; break;
-            // clang-format on
-    }
-
-    return target;
-}
-
-static inline void manageAdcInitializationDriver(uint8_t adc, ADCDriver* adcDriver) {
-    if (!adcInitialized[adc]) {
-        adcStart(adcDriver, &adcCfg);
-        adcInitialized[adc] = true;
-    }
-}
-
-static inline void manageAdcInitialization(uint8_t adc) { manageAdcInitializationDriver(adc, intToADCDriver(adc)); }
-
-pin_and_adc pinToMux(pin_t pin) {
-    switch (pin) {
-        // clang-format off
-#if defined(STM32F0XX)
-        case A0:  return (pin_and_adc){ ADC_CHANNEL_IN0,  0 };
-        case A1:  return (pin_and_adc){ ADC_CHANNEL_IN1,  0 };
-        case A2:  return (pin_and_adc){ ADC_CHANNEL_IN2,  0 };
-        case A3:  return (pin_and_adc){ ADC_CHANNEL_IN3,  0 };
-        case A4:  return (pin_and_adc){ ADC_CHANNEL_IN4,  0 };
-        case A5:  return (pin_and_adc){ ADC_CHANNEL_IN5,  0 };
-        case A6:  return (pin_and_adc){ ADC_CHANNEL_IN6,  0 };
-        case A7:  return (pin_and_adc){ ADC_CHANNEL_IN7,  0 };
-        case B0:  return (pin_and_adc){ ADC_CHANNEL_IN8,  0 };
-        case B1:  return (pin_and_adc){ ADC_CHANNEL_IN9,  0 };
-        case C0:  return (pin_and_adc){ ADC_CHANNEL_IN10, 0 };
-        case C1:  return (pin_and_adc){ ADC_CHANNEL_IN11, 0 };
-        case C2:  return (pin_and_adc){ ADC_CHANNEL_IN12, 0 };
-        case C3:  return (pin_and_adc){ ADC_CHANNEL_IN13, 0 };
-        case C4:  return (pin_and_adc){ ADC_CHANNEL_IN14, 0 };
-        case C5:  return (pin_and_adc){ ADC_CHANNEL_IN15, 0 };
-#elif defined(STM32F3XX)
-        case A0:  return (pin_and_adc){ ADC_CHANNEL_IN1,  0 };
-        case A1:  return (pin_and_adc){ ADC_CHANNEL_IN2,  0 };
-        case A2:  return (pin_and_adc){ ADC_CHANNEL_IN3,  0 };
-        case A3:  return (pin_and_adc){ ADC_CHANNEL_IN4,  0 };
-        case A4:  return (pin_and_adc){ ADC_CHANNEL_IN1,  1 };
-        case A5:  return (pin_and_adc){ ADC_CHANNEL_IN2,  1 };
-        case A6:  return (pin_and_adc){ ADC_CHANNEL_IN3,  1 };
-        case A7:  return (pin_and_adc){ ADC_CHANNEL_IN4,  1 };
-        case B0:  return (pin_and_adc){ ADC_CHANNEL_IN12, 2 };
-        case B1:  return (pin_and_adc){ ADC_CHANNEL_IN1,  2 };
-        case B2:  return (pin_and_adc){ ADC_CHANNEL_IN12, 1 };
-        case B12: return (pin_and_adc){ ADC_CHANNEL_IN2,  3 };
-        case B13: return (pin_and_adc){ ADC_CHANNEL_IN3,  3 };
-        case B14: return (pin_and_adc){ ADC_CHANNEL_IN4,  3 };
-        case B15: return (pin_and_adc){ ADC_CHANNEL_IN5,  3 };
-        case C0:  return (pin_and_adc){ ADC_CHANNEL_IN6,  0 }; // Can also be ADC2
-        case C1:  return (pin_and_adc){ ADC_CHANNEL_IN7,  0 }; // Can also be ADC2
-        case C2:  return (pin_and_adc){ ADC_CHANNEL_IN8,  0 }; // Can also be ADC2
-        case C3:  return (pin_and_adc){ ADC_CHANNEL_IN9,  0 }; // Can also be ADC2
-        case C4:  return (pin_and_adc){ ADC_CHANNEL_IN5,  1 };
-        case C5:  return (pin_and_adc){ ADC_CHANNEL_IN11, 1 };
-        case D8:  return (pin_and_adc){ ADC_CHANNEL_IN12, 3 };
-        case D9:  return (pin_and_adc){ ADC_CHANNEL_IN13, 3 };
-        case D10: return (pin_and_adc){ ADC_CHANNEL_IN7,  2 }; // Can also be ADC4
-        case D11: return (pin_and_adc){ ADC_CHANNEL_IN8,  2 }; // Can also be ADC4
-        case D12: return (pin_and_adc){ ADC_CHANNEL_IN9,  2 }; // Can also be ADC4
-        case D13: return (pin_and_adc){ ADC_CHANNEL_IN10, 2 }; // Can also be ADC4
-        case D14: return (pin_and_adc){ ADC_CHANNEL_IN11, 2 }; // Can also be ADC4
-        case E7:  return (pin_and_adc){ ADC_CHANNEL_IN13, 2 };
-        case E8:  return (pin_and_adc){ ADC_CHANNEL_IN6,  2 }; // Can also be ADC4
-        case E9:  return (pin_and_adc){ ADC_CHANNEL_IN2,  2 };
-        case E10: return (pin_and_adc){ ADC_CHANNEL_IN14, 2 };
-        case E11: return (pin_and_adc){ ADC_CHANNEL_IN15, 2 };
-        case E12: return (pin_and_adc){ ADC_CHANNEL_IN16, 2 };
-        case E13: return (pin_and_adc){ ADC_CHANNEL_IN3,  2 };
-        case E14: return (pin_and_adc){ ADC_CHANNEL_IN1,  3 };
-        case E15: return (pin_and_adc){ ADC_CHANNEL_IN2,  3 };
-        case F2:  return (pin_and_adc){ ADC_CHANNEL_IN10, 0 }; // Can also be ADC2
-        case F4:  return (pin_and_adc){ ADC_CHANNEL_IN5,  0 };
-#else
-#error "An ADC pin-to-mux configuration has not been specified for this microcontroller."
-#endif
-        default:  return (pin_and_adc){ 0, 0 };
-            // clang-format on
-    }
-}
-
-adcsample_t analogReadPin(pin_t pin) { return adc_read(pinToMux(pin)); }
-
-adcsample_t analogReadPinAdc(pin_t pin, uint8_t adc) {
-    pin_and_adc target = pinToMux(pin);
-    target.adc         = adc;
-    return adc_read(target);
-}
-
-adcsample_t adc_read(pin_and_adc mux) {
-#if defined(STM32F0XX)
-    adcConversionGroup.sqr = ADC_CHSELR_CHSEL1;
-#elif defined(STM32F3XX)
-    adcConversionGroup.sqr[0] = ADC_SQR1_SQ1_N(mux.pin);
-#else
-#    error "adc_read has not been updated to support this ARM microcontroller."
-#endif
-
-    ADCDriver* targetDriver = intToADCDriver(mux.adc);
-    manageAdcInitializationDriver(mux.adc, targetDriver);
-
-    adcConvert(targetDriver, &adcConversionGroup, &sampleBuffer[0], ADC_BUFFER_DEPTH);
-    adcsample_t* result = sampleBuffer;
-
-    return *result;
-}
+/* Copyright 2019 Drew Mills
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "quantum.h"
+#include "analog.h"
+#include "ch.h"
+#include <hal.h>
+
+#if !HAL_USE_ADC
+#    error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC."
+#endif
+
+#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4
+#    error "You need to set one of the 'STM32_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC."
+#endif
+
+#if STM32_ADC_DUAL_MODE
+#    error "STM32 ADC Dual Mode is not supported at this time."
+#endif
+
+#if STM32_ADCV3_OVERSAMPLING
+#    error "STM32 ADCV3 Oversampling is not supported at this time."
+#endif
+
+// Otherwise assume V3
+#if defined(STM32F0XX) || defined(STM32L0XX)
+#    define USE_ADCV1
+#elif defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX)
+#    define USE_ADCV2
+#endif
+
+// BODGE to make v2 look like v1,3 and 4
+#ifdef USE_ADCV2
+#    if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_3)
+#        define ADC_SMPR_SMP_1P5 ADC_SAMPLE_3
+#        define ADC_SMPR_SMP_7P5 ADC_SAMPLE_15
+#        define ADC_SMPR_SMP_13P5 ADC_SAMPLE_28
+#        define ADC_SMPR_SMP_28P5 ADC_SAMPLE_56
+#        define ADC_SMPR_SMP_41P5 ADC_SAMPLE_84
+#        define ADC_SMPR_SMP_55P5 ADC_SAMPLE_112
+#        define ADC_SMPR_SMP_71P5 ADC_SAMPLE_144
+#        define ADC_SMPR_SMP_239P5 ADC_SAMPLE_480
+#    endif
+
+#    if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_1P5)
+#        define ADC_SMPR_SMP_1P5 ADC_SAMPLE_1P5
+#        define ADC_SMPR_SMP_7P5 ADC_SAMPLE_7P5
+#        define ADC_SMPR_SMP_13P5 ADC_SAMPLE_13P5
+#        define ADC_SMPR_SMP_28P5 ADC_SAMPLE_28P5
+#        define ADC_SMPR_SMP_41P5 ADC_SAMPLE_41P5
+#        define ADC_SMPR_SMP_55P5 ADC_SAMPLE_55P5
+#        define ADC_SMPR_SMP_71P5 ADC_SAMPLE_71P5
+#        define ADC_SMPR_SMP_239P5 ADC_SAMPLE_239P5
+#    endif
+
+// we still sample at 12bit, but scale down to the requested bit range
+#    define ADC_CFGR1_RES_12BIT 12
+#    define ADC_CFGR1_RES_10BIT 10
+#    define ADC_CFGR1_RES_8BIT 8
+#    define ADC_CFGR1_RES_6BIT 6
+#endif
+
+/* User configurable ADC options */
+#ifndef ADC_COUNT
+#    if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX)
+#        define ADC_COUNT 1
+#    elif defined(STM32F3XX)
+#        define ADC_COUNT 4
+#    else
+#        error "ADC_COUNT has not been set for this ARM microcontroller."
+#    endif
+#endif
+
+#ifndef ADC_NUM_CHANNELS
+#    define ADC_NUM_CHANNELS 1
+#elif ADC_NUM_CHANNELS != 1
+#    error "The ARM ADC implementation currently only supports reading one channel at a time."
+#endif
+
+#ifndef ADC_BUFFER_DEPTH
+#    define ADC_BUFFER_DEPTH 1
+#endif
+
+// For more sampling rate options, look at hal_adc_lld.h in ChibiOS
+#ifndef ADC_SAMPLING_RATE
+#    define ADC_SAMPLING_RATE ADC_SMPR_SMP_1P5
+#endif
+
+// Options are 12, 10, 8, and 6 bit.
+#ifndef ADC_RESOLUTION
+#    define ADC_RESOLUTION ADC_CFGR1_RES_10BIT
+#endif
+
+static ADCConfig   adcCfg = {};
+static adcsample_t sampleBuffer[ADC_NUM_CHANNELS * ADC_BUFFER_DEPTH];
+
+// Initialize to max number of ADCs, set to empty object to initialize all to false.
+static bool adcInitialized[ADC_COUNT] = {};
+
+// TODO: add back TR handling???
+static ADCConversionGroup adcConversionGroup = {
+    .circular     = FALSE,
+    .num_channels = (uint16_t)(ADC_NUM_CHANNELS),
+#if defined(USE_ADCV1)
+    .cfgr1 = ADC_CFGR1_CONT | ADC_RESOLUTION,
+    .smpr  = ADC_SAMPLING_RATE,
+#elif defined(USE_ADCV2)
+#    if !defined(STM32F1XX)
+    .cr2   = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without...
+#    endif
+    .smpr2 = ADC_SMPR2_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN9(ADC_SAMPLING_RATE),
+    .smpr1 = ADC_SMPR1_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN15(ADC_SAMPLING_RATE),
+#else
+    .cfgr = ADC_CFGR_CONT | ADC_RESOLUTION,
+    .smpr = {ADC_SMPR1_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN9(ADC_SAMPLING_RATE), ADC_SMPR2_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN15(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN16(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN17(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN18(ADC_SAMPLING_RATE)},
+#endif
+};
+
+// clang-format off
+__attribute__((weak)) adc_mux pinToMux(pin_t pin) {
+    switch (pin) {
+#if defined(STM32F0XX)
+        case A0:  return TO_MUX( ADC_CHSELR_CHSEL0,  0 );
+        case A1:  return TO_MUX( ADC_CHSELR_CHSEL1,  0 );
+        case A2:  return TO_MUX( ADC_CHSELR_CHSEL2,  0 );
+        case A3:  return TO_MUX( ADC_CHSELR_CHSEL3,  0 );
+        case A4:  return TO_MUX( ADC_CHSELR_CHSEL4,  0 );
+        case A5:  return TO_MUX( ADC_CHSELR_CHSEL5,  0 );
+        case A6:  return TO_MUX( ADC_CHSELR_CHSEL6,  0 );
+        case A7:  return TO_MUX( ADC_CHSELR_CHSEL7,  0 );
+        case B0:  return TO_MUX( ADC_CHSELR_CHSEL8,  0 );
+        case B1:  return TO_MUX( ADC_CHSELR_CHSEL9,  0 );
+        case C0:  return TO_MUX( ADC_CHSELR_CHSEL10, 0 );
+        case C1:  return TO_MUX( ADC_CHSELR_CHSEL11, 0 );
+        case C2:  return TO_MUX( ADC_CHSELR_CHSEL12, 0 );
+        case C3:  return TO_MUX( ADC_CHSELR_CHSEL13, 0 );
+        case C4:  return TO_MUX( ADC_CHSELR_CHSEL14, 0 );
+        case C5:  return TO_MUX( ADC_CHSELR_CHSEL15, 0 );
+#elif defined(STM32F3XX)
+        case A0:  return TO_MUX( ADC_CHANNEL_IN1,  0 );
+        case A1:  return TO_MUX( ADC_CHANNEL_IN2,  0 );
+        case A2:  return TO_MUX( ADC_CHANNEL_IN3,  0 );
+        case A3:  return TO_MUX( ADC_CHANNEL_IN4,  0 );
+        case A4:  return TO_MUX( ADC_CHANNEL_IN1,  1 );
+        case A5:  return TO_MUX( ADC_CHANNEL_IN2,  1 );
+        case A6:  return TO_MUX( ADC_CHANNEL_IN3,  1 );
+        case A7:  return TO_MUX( ADC_CHANNEL_IN4,  1 );
+        case B0:  return TO_MUX( ADC_CHANNEL_IN12, 2 );
+        case B1:  return TO_MUX( ADC_CHANNEL_IN1,  2 );
+        case B2:  return TO_MUX( ADC_CHANNEL_IN12, 1 );
+        case B12: return TO_MUX( ADC_CHANNEL_IN2,  3 );
+        case B13: return TO_MUX( ADC_CHANNEL_IN3,  3 );
+        case B14: return TO_MUX( ADC_CHANNEL_IN4,  3 );
+        case B15: return TO_MUX( ADC_CHANNEL_IN5,  3 );
+        case C0:  return TO_MUX( ADC_CHANNEL_IN6,  0 ); // Can also be ADC2
+        case C1:  return TO_MUX( ADC_CHANNEL_IN7,  0 ); // Can also be ADC2
+        case C2:  return TO_MUX( ADC_CHANNEL_IN8,  0 ); // Can also be ADC2
+        case C3:  return TO_MUX( ADC_CHANNEL_IN9,  0 ); // Can also be ADC2
+        case C4:  return TO_MUX( ADC_CHANNEL_IN5,  1 );
+        case C5:  return TO_MUX( ADC_CHANNEL_IN11, 1 );
+        case D8:  return TO_MUX( ADC_CHANNEL_IN12, 3 );
+        case D9:  return TO_MUX( ADC_CHANNEL_IN13, 3 );
+        case D10: return TO_MUX( ADC_CHANNEL_IN7,  2 ); // Can also be ADC4
+        case D11: return TO_MUX( ADC_CHANNEL_IN8,  2 ); // Can also be ADC4
+        case D12: return TO_MUX( ADC_CHANNEL_IN9,  2 ); // Can also be ADC4
+        case D13: return TO_MUX( ADC_CHANNEL_IN10, 2 ); // Can also be ADC4
+        case D14: return TO_MUX( ADC_CHANNEL_IN11, 2 ); // Can also be ADC4
+        case E7:  return TO_MUX( ADC_CHANNEL_IN13, 2 );
+        case E8:  return TO_MUX( ADC_CHANNEL_IN6,  2 ); // Can also be ADC4
+        case E9:  return TO_MUX( ADC_CHANNEL_IN2,  2 );
+        case E10: return TO_MUX( ADC_CHANNEL_IN14, 2 );
+        case E11: return TO_MUX( ADC_CHANNEL_IN15, 2 );
+        case E12: return TO_MUX( ADC_CHANNEL_IN16, 2 );
+        case E13: return TO_MUX( ADC_CHANNEL_IN3,  2 );
+        case E14: return TO_MUX( ADC_CHANNEL_IN1,  3 );
+        case E15: return TO_MUX( ADC_CHANNEL_IN2,  3 );
+        case F2:  return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2
+        case F4:  return TO_MUX( ADC_CHANNEL_IN5,  0 );
+#elif defined(STM32F4XX) // TODO: add all pins
+        case A0:  return TO_MUX( ADC_CHANNEL_IN0,  0 );
+        //case A1:  return TO_MUX( ADC_CHANNEL_IN1,  0 );
+#elif defined(STM32F1XX) // TODO: add all pins
+        case A0:  return TO_MUX( ADC_CHANNEL_IN0,  0 );
+#endif
+    }
+
+    // return an adc that would never be used so intToADCDriver will bail out
+    return TO_MUX(0, 0xFF);
+}
+// clang-format on
+
+static inline ADCDriver* intToADCDriver(uint8_t adcInt) {
+    switch (adcInt) {
+#if STM32_ADC_USE_ADC1
+        case 0:
+            return &ADCD1;
+#endif
+#if STM32_ADC_USE_ADC2
+        case 1:
+            return &ADCD2;
+#endif
+#if STM32_ADC_USE_ADC3
+        case 2:
+            return &ADCD3;
+#endif
+#if STM32_ADC_USE_ADC4
+        case 3:
+            return &ADCD4;
+#endif
+    }
+
+    return NULL;
+}
+
+static inline void manageAdcInitializationDriver(uint8_t adc, ADCDriver* adcDriver) {
+    if (!adcInitialized[adc]) {
+        adcStart(adcDriver, &adcCfg);
+        adcInitialized[adc] = true;
+    }
+}
+
+int16_t analogReadPin(pin_t pin) {
+    palSetLineMode(pin, PAL_MODE_INPUT_ANALOG);
+
+    return adc_read(pinToMux(pin));
+}
+
+int16_t analogReadPinAdc(pin_t pin, uint8_t adc) {
+    palSetLineMode(pin, PAL_MODE_INPUT_ANALOG);
+
+    adc_mux target = pinToMux(pin);
+    target.adc     = adc;
+    return adc_read(target);
+}
+
+int16_t adc_read(adc_mux mux) {
+#if defined(USE_ADCV1)
+    // TODO: fix previous assumption of only 1 input...
+    adcConversionGroup.chselr = 1 << mux.input; /*no macro to convert N to ADC_CHSELR_CHSEL1*/
+#elif defined(USE_ADCV2)
+    adcConversionGroup.sqr3 = ADC_SQR3_SQ1_N(mux.input);
+#else
+    adcConversionGroup.sqr[0] = ADC_SQR1_SQ1_N(mux.input);
+#endif
+
+    ADCDriver* targetDriver = intToADCDriver(mux.adc);
+    if (!targetDriver) {
+        return 0;
+    }
+
+    manageAdcInitializationDriver(mux.adc, targetDriver);
+    if (adcConvert(targetDriver, &adcConversionGroup, &sampleBuffer[0], ADC_BUFFER_DEPTH) != MSG_OK) {
+        return 0;
+    }
+
+#ifdef USE_ADCV2
+    // fake 12-bit -> N-bit scale
+    return (*sampleBuffer) >> (12 - ADC_RESOLUTION);
+#else
+    // already handled as part of adcConvert
+    return *sampleBuffer;
+#endif
+}
index ab592ad..2818e9d 100644 (file)
 
 #pragma once
 
+#include <stdint.h>
 #include "quantum.h"
-#include "ch.h"
-#include <hal.h>
 
-#if !defined(STM32F0XX) && !defined(STM32F3XX)
-#    error "Only STM23F0 and STM32F3 devices have ADC support in QMK at this time."
-#endif
-
-#if !HAL_USE_ADC
-#    error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC."
-#endif
-
-#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4
-#    error "You need to set one of the 'STM32_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC."
-#endif
-
-#if STM32_ADC_DUAL_MODE
-#    error "STM32 ADC Dual Mode is not supported at this time."
-#endif
-
-#if STM32_ADCV3_OVERSAMPLING
-#    error "STM32 ADCV3 Oversampling is not supported at this time."
+#ifdef __cplusplus
+extern "C" {
 #endif
 
 typedef struct {
-    pin_t   pin;
+    uint16_t input;
     uint8_t adc;
-} pin_and_adc;
-#define PIN_AND_ADC(p, a) \
-    (pin_and_adc) { p, a }
+} adc_mux;
+#define TO_MUX(i, a) \
+    (adc_mux) { i, a }
 
-// analogReference has been left un-defined for ARM devices.
-// void analogReference(uint8_t mode);
+int16_t analogReadPin(pin_t pin);
+int16_t analogReadPinAdc(pin_t pin, uint8_t adc);
+adc_mux pinToMux(pin_t pin);
 
-adcsample_t analogReadPin(pin_t pin);
-adcsample_t analogReadPinAdc(pin_t pin, uint8_t adc);
-pin_and_adc pinToMux(pin_t pin);
+int16_t adc_read(adc_mux mux);
 
-adcsample_t adc_read(pin_and_adc mux);
+#ifdef __cplusplus
+}
+#endif
diff --git a/keyboards/handwired/onekey/keymaps/adc/config.h b/keyboards/handwired/onekey/keymaps/adc/config.h
new file mode 100644 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
diff --git a/keyboards/handwired/onekey/keymaps/adc/keymap.c b/keyboards/handwired/onekey/keymaps/adc/keymap.c
new file mode 100644 (file)
index 0000000..c5294bb
--- /dev/null
@@ -0,0 +1,38 @@
+#include QMK_KEYBOARD_H
+#include "analog.h"
+#include <stdio.h>
+
+#ifndef ADC_PIN
+#    define ADC_PIN A0
+#endif
+
+enum custom_keycodes {
+    ADC_SAMPLE = SAFE_RANGE,
+};
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+    LAYOUT(ADC_SAMPLE)  //
+};
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+    switch (keycode) {
+        case ADC_SAMPLE:
+            if (record->event.pressed) {
+                int16_t val = analogReadPin(ADC_PIN);
+
+                char buffer [50];
+                sprintf(buffer, "ADC:%u\n", val);
+#ifdef CONSOLE_ENABLE
+                    printf(buffer);
+#else
+                    SEND_STRING(buffer);
+#endif
+            }
+            break;
+    }
+    return false;
+};
+
+// adc_mux pinToMux(pin_t pin) {
+//     return TO_MUX( ADC_CHANNEL_IN1,  0 );
+// };
diff --git a/keyboards/handwired/onekey/keymaps/adc/rules.mk b/keyboards/handwired/onekey/keymaps/adc/rules.mk
new file mode 100644 (file)
index 0000000..a691d54
--- /dev/null
@@ -0,0 +1,3 @@
+SRC += analog.c
+
+CONSOLE_ENABLE = yes