diff --git a/firmware/ch32x035-usb-device-compositekm-c/keyboard/CMakeLists.txt b/firmware/ch32x035-usb-device-compositekm-c/keyboard/CMakeLists.txt index 728945568..23686bcf7 100644 --- a/firmware/ch32x035-usb-device-compositekm-c/keyboard/CMakeLists.txt +++ b/firmware/ch32x035-usb-device-compositekm-c/keyboard/CMakeLists.txt @@ -8,8 +8,10 @@ target_sources(keyboard PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/keyboard_split.c ${CMAKE_CURRENT_SOURCE_DIR}/keyboard_split_rx_dma.c ${CMAKE_CURRENT_SOURCE_DIR}/keyboard_split_tx_dma.c + ${CMAKE_CURRENT_SOURCE_DIR}/keyboard_touchkey.c ${CMAKE_CURRENT_SOURCE_DIR}/matrix/col_to_row.c ${CMAKE_CURRENT_SOURCE_DIR}/matrix/row_to_col.c + ${CMAKE_CURRENT_SOURCE_DIR}/matrix/touchkey_mutual.c ) target_include_directories(keyboard INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.c b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.c index 09261968f..eb894a55c 100644 --- a/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.c +++ b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.c @@ -4,11 +4,6 @@ GPIO_TypeDef *const GPIO_PORTS[] = {GPIOA, GPIOB, GPIOC}; -typedef struct { - GPIO_TypeDef *port; - uint32_t pin; -} ch32x_gpio_t; - ch32x_gpio_t to_ch32x_gpio(keyboard_gpio_t gpio_source) { ch32x_gpio_t result = { .port = GPIO_PORTS[gpio_source.port], diff --git a/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.h b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.h index 186aafc8a..f214b3c67 100644 --- a/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.h +++ b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_gpio.h @@ -3,6 +3,8 @@ #include #include +#include "ch32x035.h" + typedef struct { // Port Source uint8_t port; @@ -10,6 +12,13 @@ typedef struct { uint8_t pin; } keyboard_gpio_t; +typedef struct { + GPIO_TypeDef *port; + uint32_t pin; +} ch32x_gpio_t; + +ch32x_gpio_t to_ch32x_gpio(keyboard_gpio_t gpio_source); + void keyboard_gpio_configure_ipu(keyboard_gpio_t gpio); void keyboard_gpio_configure_output(keyboard_gpio_t gpio); diff --git a/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_touchkey.c b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_touchkey.c new file mode 100644 index 000000000..3c2eab243 --- /dev/null +++ b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_touchkey.c @@ -0,0 +1,52 @@ +#include "keyboard_touchkey.h" + +#include "ch32x035_adc.h" +#include "ch32x035_gpio.h" +#include "ch32x035_rcc.h" + +#include "keyboard_gpio.h" + +void keyboard_touchkey_configure_drive(keyboard_gpio_t gpio_source) { + keyboard_gpio_configure_output(gpio_source); + keyboard_gpio_reset(gpio_source); +} + +void keyboard_touchkey_configure_sense(keyboard_gpio_t gpio_source) { + ch32x_gpio_t gpio = to_ch32x_gpio(gpio_source); + GPIO_InitTypeDef gpio_init_value = { + .GPIO_Pin = gpio.pin, + .GPIO_Mode = GPIO_Mode_AIN, + }; + GPIO_Init(gpio.port, &gpio_init_value); +} + +void keyboard_touchkey_init(void) { + RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); + + ADC_CLKConfig(ADC1, ADC_CLK_Div6); + + ADC_InitTypeDef adc_init_value = { + .ADC_Mode = ADC_Mode_Independent, + .ADC_ScanConvMode = DISABLE, + .ADC_ContinuousConvMode = DISABLE, + .ADC_ExternalTrigConv = ADC_ExternalTrigConv_None, + .ADC_DataAlign = ADC_DataAlign_Right, + .ADC_NbrOfChannel = 1, + }; + ADC_Init(ADC1, &adc_init_value); + + ADC_Cmd(ADC1, ENABLE); + + TKey1->CTLR1 |= ADC_TKENABLE; +} + +uint16_t keyboard_touchkey_read(uint8_t ch) { + // TODO: allow configuration + ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_11Cycles); + TKey1->IDATAR1 = 0xFF; // Charging Time + TKey1->RDATAR = 0xFF; // Discharging Time; start read + while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) + ; + + return (uint16_t)TKey1->RDATAR; +} diff --git a/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_touchkey.h b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_touchkey.h new file mode 100644 index 000000000..75610dd70 --- /dev/null +++ b/firmware/ch32x035-usb-device-compositekm-c/keyboard/keyboard_touchkey.h @@ -0,0 +1,9 @@ +#pragma once + +#include "keyboard_gpio.h" + +void keyboard_touchkey_configure_drive(keyboard_gpio_t gpio); +void keyboard_touchkey_configure_sense(keyboard_gpio_t gpio); + +void keyboard_touchkey_init(void); +uint16_t keyboard_touchkey_read(uint8_t ch); diff --git a/firmware/ch32x035-usb-device-compositekm-c/keyboard/matrix/touchkey_mutual.c b/firmware/ch32x035-usb-device-compositekm-c/keyboard/matrix/touchkey_mutual.c new file mode 100644 index 000000000..143a481c7 --- /dev/null +++ b/firmware/ch32x035-usb-device-compositekm-c/keyboard/matrix/touchkey_mutual.c @@ -0,0 +1,143 @@ +#ifdef KEYBOARD_MATRIX_IMPL_TOUCHKEY_MUTUAL +#include "debug.h" + +#include "ch32x035_rcc.h" + +#include "keyboard_gpio.h" +#include "keyboard_matrix.h" +#include "keyboard_touchkey.h" + +uint16_t tkey_baselines[KEYBOARD_MATRIX_KEY_COUNT] = {0}; +uint16_t tkey_readings[KEYBOARD_MATRIX_KEY_COUNT] = {0}; + +// KLUDGE: hardcoded. Ideally, comes from codegen. +// for board: ch32x-tc-2x4 rev2025.1 +uint8_t adc_channel_map[4] = { + // per col + 4, // col 1 + 6, // col 2 + 8, // col 3 + 9, // col 4 +}; + +void init_column(keyboard_gpio_t col) { + keyboard_touchkey_configure_sense(col); +} + +void init_row(keyboard_gpio_t row) { keyboard_touchkey_configure_drive(row); } + +void keyboard_matrix_scan_raw(bool scan_buf[KEYBOARD_MATRIX_KEY_COUNT]); + +void keyboard_matrix_init(void) { + // NOTE: this implementation is for 'touchkeys' using mutual capacitance, + // driving on rows, sensing on columns. + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); + + // Sense Cols + for (uint8_t i = 0; i < KEYBOARD_MATRIX_COL_COUNT; i++) { + init_column(keyboard_matrix_cols[i]); + } + + // Drive Rows + for (uint8_t i = 0; i < KEYBOARD_MATRIX_ROW_COUNT; i++) { + init_row(keyboard_matrix_rows[i]); + keyboard_gpio_reset(keyboard_matrix_rows[i]); + } + + keyboard_touchkey_init(); + + // Scan once to set ADC values + bool current_raw_scan[KEYBOARD_MATRIX_KEY_COUNT] = {false}; + keyboard_matrix_scan_raw(current_raw_scan); +} + +void discharge_pin(keyboard_gpio_t col) { + // Ensure col is discharged + keyboard_gpio_configure_output(col); + keyboard_gpio_reset(col); + Delay_Us(5); + keyboard_touchkey_configure_sense(col); +} + +void keyboard_matrix_scan_column_for_row( + keyboard_matrix_coordinate_t index, + bool scan_buf[KEYBOARD_MATRIX_KEY_COUNT]) { + uint8_t column_index = index.column; + uint8_t row_index = index.row; + keyboard_gpio_t column = keyboard_matrix_cols[column_index]; + + discharge_pin(column); + + uint16_t adc_value = keyboard_touchkey_read(adc_channel_map[column_index]); + + uint8_t keymap_index = keymap_indices[row_index][column_index]; + + if (keymap_index >= 0) { + tkey_readings[keymap_index] = adc_value; + + // Initialize baseline if not set + if (tkey_baselines[keymap_index] == 0) { + tkey_baselines[keymap_index] = adc_value; + } + + uint16_t threshold = 150; // TODO: make configurable + if (adc_value < tkey_baselines[keymap_index] - threshold) { + scan_buf[keymap_index] = true; + } else { + scan_buf[keymap_index] = false; + + // Update baseline + uint8_t n = 64; + tkey_baselines[keymap_index] = + (tkey_baselines[keymap_index] * (n - 1) / n) + (adc_value * 1 / n); + } + } +} + +void keyboard_matrix_scan_row(uint8_t row_index, + bool scan_buf[KEYBOARD_MATRIX_KEY_COUNT]) { + keyboard_gpio_t row = keyboard_matrix_rows[row_index]; + + keyboard_gpio_set(row); + Delay_Us(5); + + for (uint8_t column_index = 0; column_index < KEYBOARD_MATRIX_COL_COUNT; + column_index++) { + keyboard_matrix_coordinate_t index = {.column = column_index, + .row = row_index}; + keyboard_matrix_scan_column_for_row(index, scan_buf); + } + + keyboard_gpio_reset(row); +} + +void keyboard_matrix_scan_raw(bool scan_buf[KEYBOARD_MATRIX_KEY_COUNT]) { + static uint16_t counter = 0; + + if ((++counter) > 1000) { + printf("base: \r\n %5d %5d\r\n %5d %5d\r\n", tkey_baselines[0], + tkey_baselines[1], tkey_baselines[2], tkey_baselines[3]); + printf("read: \r\n %5d %5d\r\n %5d %5d\r\n", tkey_readings[0], + tkey_readings[1], tkey_readings[2], tkey_readings[3]); + printf("delt: \r\n %5d %5d\r\n %5d %5d\r\n", + ((int32_t)(tkey_readings[0]) - (int32_t)(tkey_baselines[0])), + ((int32_t)(tkey_readings[1]) - (int32_t)(tkey_baselines[1])), + ((int32_t)(tkey_readings[2]) - (int32_t)(tkey_baselines[2])), + ((int32_t)(tkey_readings[3]) - (int32_t)(tkey_baselines[3]))); + printf("\r\n"); + counter = 0; + } + + for (uint8_t row_index = 0; row_index < KEYBOARD_MATRIX_ROW_COUNT; + row_index++) { + keyboard_matrix_scan_row(row_index, scan_buf); + } +} + +bool keyboard_matrix_is_sw_1_1_pressed() { + return false; // TBI +} +#endif diff --git a/firmware/ch32x035-usb-device-compositekm-c/ncl/boards/ch32x-tc-2x4.ncl b/firmware/ch32x035-usb-device-compositekm-c/ncl/boards/ch32x-tc-2x4.ncl new file mode 100644 index 000000000..7e977b107 --- /dev/null +++ b/firmware/ch32x035-usb-device-compositekm-c/ncl/boards/ch32x-tc-2x4.ncl @@ -0,0 +1,45 @@ +let C = import "keyboard/contracts.ncl" in + +{ + gpio_pins | C.GpioPins, + + board | C.Board = { + matrix = + let p = gpio_pins in + { + # Sense columns + cols = [p.A4, p.A6, p.A7, p.B1], + # Drive rows + rows = [ + p.A0, + p.A5, + ], + key_count = 8, + + implementation = "touchkey_mutual", + }, + + keymap_index_for_key = fun { column_index | Number, row_index | Number, .. } => + let [ + k00, k01, k02, k03, + k04, k05, k06, k07, + ] = std.array.generate (fun i => 'Key i) matrix.key_count in + let NO = 'NoKey in + let matrix = [ + [k00, k01, k02, k03], + [k04, k05, k06, k07], + ] + in + matrix + |> std.array.at row_index + |> std.array.at column_index, + + led = { + enabled = true, + pin = gpio_pins.A3, + }, + + # TX4 available if Col3 not used. + debug.tx = 1, + }, +}