lotus: Add ADC reading code

Trigger with:

```
hidapitester --vidpid 32ac/0012 --usagePage 0xFF60 --usage 0x0061 \
  --open -l32 --send-output 0,11,3
```

Signed-off-by: Daniel Schaefer <git@danielschaefer.me>
This commit is contained in:
Daniel Schaefer
2023-01-04 13:54:35 +08:00
parent 5ae444a395
commit 42db647ce6
6 changed files with 343 additions and 151 deletions

4
.gitmodules vendored
View File

@@ -4,8 +4,8 @@
branch = master branch = master
[submodule "lib/chibios-contrib"] [submodule "lib/chibios-contrib"]
path = lib/chibios-contrib path = lib/chibios-contrib
url = https://github.com/qmk/ChibiOS-Contrib url = https://github.com/sigprof/ChibiOS-Contrib
branch = master branch = rp2040-adc-fix-start
[submodule "lib/googletest"] [submodule "lib/googletest"]
path = lib/googletest path = lib/googletest
url = https://github.com/qmk/googletest url = https://github.com/qmk/googletest

View File

@@ -3,11 +3,13 @@
#include "quantum.h" #include "quantum.h"
#include "raw_hid.h" #include "raw_hid.h"
#include "matrix.h"
enum factory_commands { enum factory_commands {
f_bootloader = 0x00, f_bootloader = 0x00,
f_emu_keypress = 0x01, // Next byte is keycode f_emu_keypress = 0x01, // Next byte is keycode
f_backlight = 0x02, // Next byte is on/off boolean f_backlight = 0x02, // Next byte is on/off boolean
f_adc = 0x03, // ADC trigger
}; };
void handle_factory_command(uint8_t *data) { void handle_factory_command(uint8_t *data) {
@@ -35,6 +37,9 @@ void handle_factory_command(uint8_t *data) {
backlight_enable(); backlight_enable();
} }
break; break;
case f_adc:
trigger_adc();
break;
default: default:
uprintf("Unknown factory command: %u\n", factory_command_id); uprintf("Unknown factory command: %u\n", factory_command_id);
break; break;

View File

@@ -8,4 +8,6 @@
// For single-zone backlight // For single-zone backlight
#define HAL_USE_PWM TRUE #define HAL_USE_PWM TRUE
#define HAL_USE_ADC TRUE
#include_next <halconf.h> #include_next <halconf.h>

View File

@@ -5,17 +5,151 @@
#include <stdint.h> #include <stdint.h>
#include "debug.h" #include "debug.h"
#include "matrix.h" #include "matrix.h"
#include "analog.h"
#include "print.h" #include "print.h"
#include "quantum.h" #include "quantum.h"
#include "hal_adc.h"
#include "chprintf.h"
#include "matrix.h"
#define ADC_THRESHOLD 3 #define ADC_THRESHOLD 3
#define LED_GREEN_PIN 25U
#define ADC_RESOLUTION 10
#define ADC_GRP_NUM_CHANNELS 2
#define ADC_GRP_BUF_DEPTH 2
adcsample_t samples[CACHE_SIZE_ALIGN(adcsample_t, ADC_GRP_NUM_CHANNELS * 2)];
const float CONV_FACTOR = 3.3f / (1<<12);
#define CALC_DIGITS 12
char calc_result[CALC_DIGITS+1] = "";
/*
* Print two digits
*/
void print_float(float temp) {
// dtostrf doesn't seem to be available
//dtostrf(temp, CALC_DIGITS, 2, calc_result);
//uprintf("temp: %s\n", calc_result);
int digits = (int)temp;
int decimals = (int)(temp * 100) % 100;
uprintf("temp: %d.%d\n", digits, decimals);
}
/*
* Call back function which called when ADC is finished.
*/
void adc_end_callback(ADCDriver *adcp) {
(void)adcp;
uprintf("ADC end cb samples: %d, %d, %d, %d\n", samples[0], samples[1], samples[2], samples[3]);
uprintf("temp sample: %d\n", samples[1]);
float raw_temp = (float)samples[1] * CONV_FACTOR;
double temp = 27.0 - (raw_temp - 0.706)/0.001721;
print_float(temp);
uint16_t val = (*samples) >> (12 - ADC_RESOLUTION);
uprintf("val: %d\n", val);
print("\n");
}
/*
* Call back function which called when ADC gives some error.
*/
void adc_error_callback(ADCDriver *adcp, adcerror_t err) {
(void)adcp;
uprintf("error: %ld\n", err);
}
void trigger_adc(void) {
print("Triggered ADC\n");
const ADCConfig adcConfig = {
0, // div_int
0, // div_frac
false, // shift
};
const ADCConversionGroup adcConvGroup = {
false, // circular
2, // num_channels
&adc_end_callback, // end_cb
&adc_error_callback, // error_cb
RP_ADC_CH0 | RP_ADC_CH4, // channel_mask
};
adcStart(&ADCD1, &adcConfig);
/* Enable temperature sensor. */
adcRPEnableTS(&ADCD1);
//adcsample_t buf[4] = {0, 0, 0, 0};
print("--\n");
chThdSleepMilliseconds(100);
print("adcStartConversion\n");
adcStartConversion(&ADCD1, &adcConvGroup, (adcsample_t *)&samples, 2);
/*
* Normal main() thread activity, in this demo it does nothing except
* sleeping in a loop.
*/
while (true) {
chThdSleepMilliseconds(500);
uprintf("samples: %d, %d, %d, %d\n", samples[0], samples[1], samples[2], samples[3]);
if (!adcConvGroup.circular) {
print("adcStartConversion\n");
adcStartConversion(&ADCD1, &adcConvGroup, (adcsample_t *)&samples, 2);
}
}
}
/*
* Green LED blinker thread, times are in milliseconds.
*/
//static CH_SYS_CORE0_MEMORY THD_WORKING_AREA(waThread1, 128);
//static THD_FUNCTION(Thread1, arg) {
// (void)arg;
// chRegSetThreadName("blinker");
// while (true) {
// backlight_enable();
// chThdSleepMilliseconds(500);
// backlight_disable();
// chThdSleepMilliseconds(500);
// }
//}
//
///*
// * Call back function which called when ADC is finished.
// */
//void adc_end_callback(ADCDriver *adcp) {
// (void)adcp;
// backlight_enable();
// uprintf("ADC end cb\n");
//}
//
///*
// * Call back function which called when ADC gives some error.
// */
//void adc_error_callback(ADCDriver *adcp, adcerror_t err) {
// (void)adcp;
// uprintf("error: %ld\n", err);
//}
//
//const ADCConversionGroup adcConvGroup = {
// .circular = false,
// .num_channels = ADC_GRP_NUM_CHANNELS,
// .end_cb = &adc_end_callback,
// .error_cb = &adc_error_callback,
// // CH0 is the keyboard matrix ADC, CH4 is the temp sensor
// .channel_mask = RP_ADC_CH0 | RP_ADC_CH4,
//};
/** /**
* Tell RP2040 ADC controller to initialize a specific GPIO for ADC input * Tell RP2040 ADC controller to initialize a specific GPIO for ADC input
*/ */
void adc_gpio_init(int gpio) { void adc_gpio_init(int gpio) {
assert(gpio >= 26 && gpio <= 29); assert(gpio >= 26 && gpio <= 29);
// TODO: Implement palSetLineMode(gpio, PAL_MODE_INPUT_ANALOG);
} }
/** /**
@@ -34,178 +168,221 @@ void adc_select_input(int adc_channel) {
// Mux output // Mux output
#define ADC_IN 28 #define ADC_IN 28
#define ADC_CH0_PIN 26U
#define ADC_CH1_PIN 27U
#define ADC_CH2_PIN 28U
/** /**
* Tell the mux to select a specific column * Tell the mux to select a specific column
* *
* Splits the positive integer (<=7) into its three component bits. * Splits the positive integer (<=7) into its three component bits.
*/ */
static void mux_select_row(int row) { //static void mux_select_row(int row) {
assert(col >= 0 && col <= 7); // assert(col >= 0 && col <= 7);
//
// Not in order - need to remap them // // Not in order - need to remap them
// X0 - KSI1 // // X0 - KSI1
// X1 - KSI2 // // X1 - KSI2
// X2 - KSI0 // // X2 - KSI0
// X3 - KSI3 // // X3 - KSI3
// X4 - KSI4 // // X4 - KSI4
// X5 - KSI5 // // X5 - KSI5
// X6 - KSI6 // // X6 - KSI6
// X7 - KSI7 // // X7 - KSI7
int index = 0; // int index = 0;
switch (row) { // switch (row) {
case 0: // case 0:
index = 2; // index = 2;
case 1: // case 1:
index = 0; // index = 0;
case 2: // case 2:
index = 1; // index = 1;
default: // default:
index = row; // index = row;
} // }
//
int bits[] = { // int bits[] = {
(index & 0x1) > 0, // (index & 0x1) > 0,
(index & 0x4) > 0, // (index & 0x4) > 0,
(index & 0x8) > 0 // (index & 0x8) > 0
}; // };
writePin(MUX_A, bits[0]); // writePin(MUX_A, bits[0]);
writePin(MUX_B, bits[1]); // writePin(MUX_B, bits[1]);
writePin(MUX_C, bits[2]); // writePin(MUX_C, bits[2]);
} //}
static uint16_t adc_read(void) { return 0; } //static uint16_t adc_read(void) { return 0; }
/** /**
* Based on the adc value, update the matrix for this column * Based on the adc value, update the matrix for this column
* */ * */
static bool interpret_adc_row(matrix_row_t cur_matrix[], uint16_t adc_value, int col, int row) { //static bool interpret_adc_row(matrix_row_t cur_matrix[], uint16_t adc_value, int col, int row) {
bool changed = false; // bool changed = false;
//
// // TODO: Convert adc value to voltage
// uint16_t voltage = adc_value;
//
// // By default the voltage is high (3.3V)
// // When a key is pressed it causes the voltage to go down.
// // But because every key is connected in a matrix, pressing multiple keys
// // changes the voltage at every key again. So we can't check for a specific
// // voltage but need to have a threshold.
// uint8_t key_state = 0;
// if (voltage < ADC_THRESHOLD) {
// key_state = 1;
// }
//
// uprintf("Col %d - Row %d - ADC value:%04X, Voltage: %d\n", col, row, adc_value, voltage);
// cur_matrix[row] |= key_state ? 0 : (1 << col);
//
// return changed;
//}
// TODO: Convert adc value to voltage //void drive_col(int col, bool high) {
uint16_t voltage = adc_value; // assert(col >= 0 && col <= MATRIX_COLS);
// int gpio = 0;
// By default the voltage is high (3.3V) // switch (col) {
// When a key is pressed it causes the voltage to go down. // case 0:
// But because every key is connected in a matrix, pressing multiple keys // gpio = 8;
// changes the voltage at every key again. So we can't check for a specific // break;
// voltage but need to have a threshold. // case 1:
uint8_t key_state = 0; // gpio = 9;
if (voltage < ADC_THRESHOLD) { // break;
key_state = 1; // case 2:
} // gpio = 10;
// break;
printf("Col %d - Row %d - ADC value:%04X, Voltage: %d\n", col, row, adc_value, voltage); // case 3:
cur_matrix[row] |= key_state ? 0 : (1 << col); // gpio = 11;
// break;
return changed; // case 4:
} // gpio = 12;
// break;
void drive_col(int col, bool high) { // case 5:
assert(col >= 0 && col <= MATRIX_COLS); // gpio = 13;
int gpio = 0; // break;
switch (col) { // case 6:
case 0: // gpio = 14;
gpio = 8; // break;
break; // case 7:
case 1: // gpio = 15;
gpio = 9; // break;
break; // case 8:
case 2: // gpio = 21;
gpio = 10; // break;
break; // case 9:
case 3: // gpio = 20;
gpio = 11; // break;
break; // case 10:
case 4: // gpio = 19;
gpio = 12; // break;
break; // case 11:
case 5: // gpio = 18;
gpio = 13; // break;
break; // case 12:
case 6: // gpio = 17;
gpio = 14; // break;
break; // case 13:
case 7: // gpio = 16;
gpio = 15; // break;
break; // default:
case 8: // // Not supposed to happen
gpio = 21; // assert(false);
break; // return;
case 9: // }
gpio = 20; //
break; // if (high) {
case 10: // // TODO: Could set up the pins with `setPinOutputOpenDrain` instead
gpio = 19; // writePinHigh(gpio);
break; // } else {
case 11: // writePinLow(gpio);
gpio = 18; // }
break; //}
case 12:
gpio = 17;
break;
case 13:
gpio = 16;
break;
default:
// Not supposed to happen
assert(false);
return;
}
if (high) {
// TODO: Could set up the pins with `setPinOutputOpenDrain` instead
writePinHigh(gpio);
} else {
writePinLow(gpio);
}
}
/** /**
* Overriding behavior of matrix_scan from quantum/matrix.c * Overriding behavior of matrix_scan from quantum/matrix.c
*/ */
bool matrix_scan_custom(matrix_row_t current_matrix[]) { //bool matrix_scan_custom(matrix_row_t current_matrix[]) {
bool changed = false; // bool changed = false;
//
for (int col = 0; col < MATRIX_COLS; col++) { // chThdSleepMilliseconds(500);
// Drive column low so we can measure the resistors on each row in this column // //chprintf((BaseSequentialStream *)&SIOD0, "MERRY CHRISTMAS!");
drive_col(col, false); // uprintf("hi :)))\n");
for (int row = 0; row <= MATRIX_ROWS; row++) { // uprintf(" samples: %d, %d, %d, %d\n", samples[0], samples[1], samples[2], samples[3]);
// uprintf(" hello???\n");
// Read ADC for this row // //if (!adcConvGroup.circular) {
mux_select_row(row); // // uprintf(" before adcStartConversion\n");
// // //adcStartConversion(&ADCD1, &adcConvGroup, (adcsample_t *)&samples, 2);
wait_us(30); // Wait for column select and ADC to settle // // uprintf(" after adcStartConversion\n");
// //}
uint16_t adc_value = adc_read(); // uprintf(" end of matrix_scan_custom\n");
//
// Interpret ADC value as rows // for (int col = 0; col < MATRIX_COLS; col++) {
changed |= interpret_adc_row(current_matrix, adc_value, col, row); // break;
} // // Drive column low so we can measure the resistors on each row in this column
// drive_col(col, false);
// Drive column high again // for (int row = 0; row <= MATRIX_ROWS; row++) {
drive_col(col, true); //
} // // Read ADC for this row
// mux_select_row(row);
return changed; //
} // wait_us(30); // Wait for column select and ADC to settle
//
// uint16_t adc_value = 0;//adc_read();
//
// // Interpret ADC value as rows
// changed |= interpret_adc_row(current_matrix, adc_value, col, row);
// }
//
// // Drive column high again
// drive_col(col, true);
// }
//
// return changed;
//}
/** /**
* Enable the ADC MUX * Enable the ADC MUX
* *
* TODO: Do we need a de-init? Probably not. * TODO: Do we need a de-init? Probably not.
*/ */
static void adc_mux_init(void) { //static void adc_mux_init(void) {
writePinHigh(MUX_ENABLE); // writePinHigh(MUX_ENABLE);
} //}
/** /**
* Overriding behavior of matrix_init from quantum/matrix.c * Overriding behavior of matrix_init from quantum/matrix.c
*/ */
void matrix_init_custom(void) { void matrix_init_custom(void) {
print("Initializing Lotus\n"); print("Initializing Lotus\n");
adc_mux_init(); //adc_mux_init();
// Make sure GPIO is high-impedance, no pullups etc ////setPinOutput(LED_GREEN_PIN);
adc_gpio_init(26); ////writePinHigh(LED_GREEN_PIN);
// Select ADC input 0 (GPIO26)
adc_select_input(0); ///*
//* Creates the blinker thread.
//*/
//chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
//// Make sure GPIO is high-impedance, no pullups etc
adc_gpio_init(ADC_CH0_PIN);
////// Handled by adc_read and analogReadPin
//const ADCConfig adcConfig = {
// .div_int = 0,
// .div_frac = 0,
// .shift = false,
//};
//adcStart(&ADCD1, &adcConfig);
////// For testing enable temp sensor
//adcRPEnableTS(&ADCD1);
//backlight_enable();
//chThdSleepMilliseconds(100);
////adcStartConversion(&ADCD1, &adcConvGroup,
//// samples, ADC_GRP_BUF_DEPTH);
//// TODO: Not sure we ever need to stop. Perhaps to save power.
//// adcStopConversion(&ADCD1);
} }

8
keyboards/lotus/matrix.h Normal file
View File

@@ -0,0 +1,8 @@
// Copyright 2022 Framework Computer
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "quantum.h"
void trigger_adc(void);