diff --git a/application/main/src/keyboard/keyboard_matrix.c b/application/main/src/keyboard/keyboard_matrix.c index 74f4542e8..73b9ecb98 100644 --- a/application/main/src/keyboard/keyboard_matrix.c +++ b/application/main/src/keyboard/keyboard_matrix.c @@ -49,10 +49,6 @@ static uint8_t debouncing = DEBOUNCE_RELOAD; static matrix_row_t matrix[MATRIX_ROWS]; static matrix_row_t matrix_debouncing[MATRIX_ROWS]; -static matrix_row_t read_cols(void); -static void select_row(uint8_t row); -static void unselect_rows(uint8_t row); - #ifdef ROW_IN #define READ_COL(pin) (!nrf_gpio_pin_read(pin)) #else @@ -63,7 +59,7 @@ static void unselect_rows(uint8_t row); * @brief 初始化键盘阵列 * */ -void matrix_init(void) +__attribute__((weak)) void matrix_init(void) { for (uint_fast8_t i = MATRIX_COLS; i--;) { #ifdef ROW_IN @@ -74,7 +70,7 @@ void matrix_init(void) } } /** read all rows */ -static matrix_row_t read_cols(void) +__attribute__((weak)) matrix_row_t read_cols(void) { matrix_row_t result = 0; @@ -86,7 +82,7 @@ static matrix_row_t read_cols(void) return result; } -static void select_row(uint8_t row) +__attribute__((weak)) void select_row(uint8_t row) { if ((uint32_t)row_pin_array[row] > 31 ) return; @@ -108,7 +104,7 @@ nrf_gpio_cfg( #endif } -static void unselect_rows(uint8_t row) +__attribute__((weak)) void unselect_rows(uint8_t row) { if ((uint32_t)row_pin_array[row] > 31 ) return; @@ -133,7 +129,7 @@ static inline void delay_us(void) } } -uint8_t matrix_scan(void) +__attribute__((weak)) uint8_t matrix_scan(void) { for (uint8_t i = 0; i < MATRIX_ROWS; i++) { select_row(i); @@ -165,7 +161,7 @@ uint8_t matrix_scan(void) return 1; } -bool matrix_is_modified(void) +__attribute__((weak)) bool matrix_is_modified(void) { if (debouncing) return false; @@ -245,7 +241,7 @@ uint8_t matrix_key_count(void) * @brief 禁用所有阵列针脚 * */ -void matrix_deinit(void) +__attribute__((weak)) void matrix_deinit(void) { for (uint8_t i = 0; i < MATRIX_COLS; i++) { nrf_gpio_cfg_default(column_pin_array[i]); @@ -259,7 +255,7 @@ void matrix_deinit(void) * @brief 阵列准备睡眠 * */ -void matrix_wakeup_prepare(void) +__attribute__((weak)) void matrix_wakeup_prepare(void) { // 这里监听所有按键作为唤醒按键,所以真正的唤醒判断应该在main的初始化过程中 #ifdef ROW_IN diff --git a/application/main/src/keyboard/keyboard_matrix.h b/application/main/src/keyboard/keyboard_matrix.h index 4722bf6cb..357fdf4af 100644 --- a/application/main/src/keyboard/keyboard_matrix.h +++ b/application/main/src/keyboard/keyboard_matrix.h @@ -1,6 +1,13 @@ #pragma once #include +#include "matrix.h" +void matrix_init(void); +matrix_row_t read_cols(void); +void select_row(uint8_t row); +void unselect_rows(uint8_t row); +uint8_t matrix_scan(void); +bool matrix_is_modified(void); void matrix_deinit(void); void matrix_wakeup_prepare(void); diff --git a/keyboard/magnet/Makefile b/keyboard/magnet/Makefile new file mode 100644 index 000000000..10da15203 --- /dev/null +++ b/keyboard/magnet/Makefile @@ -0,0 +1,29 @@ +# 此工程的根目录 +ROOT_DIR := ../.. + +# 特殊目录控制 +SDK_ROOT := $(ROOT_DIR)/SDK +TEMPLATE_PATH := $(ROOT_DIR)/template +APP_MAIN_DIR := $(ROOT_DIR)/application/main +TMK_CORE_DIR := $(ROOT_DIR)/tmk/tmk_core +USB_SOURCE_DIR := $(ROOT_DIR)/usb +APP_PROJ_DIR := $(APP_MAIN_DIR)/project +APP_SRC_DIR := $(APP_MAIN_DIR)/src +INC_FOLDERS += . + +# 配置文件文件名 +CONFIG_H = config.h +CONFIG_H_DIR = . +SRC_FILES += matrix_analog.c keymap_plain.c + +include ./rules.mk + +purge: + -rm -rf $(OUTPUT_DIR) + +go: purge ch554 package merge_bootloader_all + +all: default ch554 + +include $(APP_PROJ_DIR)/kbd.mk +include $(USB_SOURCE_DIR)/usb.mk diff --git a/keyboard/magnet/README.md b/keyboard/magnet/README.md new file mode 100644 index 000000000..8444f7438 --- /dev/null +++ b/keyboard/magnet/README.md @@ -0,0 +1 @@ +# magnet diff --git a/keyboard/magnet/config.h b/keyboard/magnet/config.h new file mode 100644 index 000000000..2a53a73b5 --- /dev/null +++ b/keyboard/magnet/config.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +/* Device */ +#define VENDOR_ID 0xDE29 +#define PRODUCT_ID 0xD707 +#define CONF_VENDOR_ID 0x9A29 +#define CONF_PRODUCT_ID 0x9707 +#define DEVICE_VER 0x0001 +#define MANUFACTURER "Leo Deng" +#define PRODUCT "Magnet" +#define DEVICE_BLE_APPEARANCE BLE_APPEARANCE_HID_KEYBOARD +#define MACADDR_SEPRATOR '_' + +/* Key Matrix */ +// MUX: RS2522XS16; 1 enable pin, 2 channel pins, 2 analog output pins, 8 analog input pins +#define MUX_COUNT 1 +static const uint8_t enable_pin_array[MUX_COUNT] = { 6 }; //mux enable, digital output +#define MUX_CHANNELS 2 +static const uint8_t channel_pin_array[MUX_CHANNELS] = { 7, 8 }; // mux channel, digital output +#define MATRIX_ROWS 4 // Used mux channels, max = 2 ^ MUX_CHANNELS +#define MATRIX_COLS 2 // Total mux sig pins, MUX_COUNT * MUX_OUTPUT +static const uint8_t row_pin_array[MATRIX_ROWS] = { 0 }; // fake +static const uint8_t column_pin_array[MATRIX_COLS] = { 4, 5 }; // mux output, analog input +#define DEBOUNCE 5 +#define MATRIX_SCAN_DELAY_CYCLE 36 +#define WAKE_UP_PIN 12 + +/* Magnet Switch Value Range */ +#define MAGNET_THRESHOLD_TOP 486 // stay still +#define MAGNET_THRESHOLD_LIGHT 430 +#define MAGNET_THRESHOLD_DEFAULT 380 +#define MAGNET_THRESHOLD_HEAVY 330 +#define MAGNET_THRESHOLD_BOTTOM 270 // fully push down + +/* Command Key */ +#define IS_COMMAND() (keyboard_report->mods == (MOD_BIT(KC_LEFT) | MOD_BIT(KC_RGHT))) + +/* Power Saving */ +#define SLEEP_SLOW_TIMEOUT 15 // 键盘闲置多久后转入慢速扫描模式 (s) +#define SLEEP_OFF_TIMEOUT 600 // 键盘闲置多久后转入自动关机 (s) +#define KEYBOARD_SCAN_INTERVAL 1 // 键盘最小时间单位TICK (ms) +#define KEYBOARD_FAST_SCAN_INTERVAL 10 // 通常模式下,多久扫描一次键盘 (ms) +#define KEYBOARD_SLOW_SCAN_INTERVAL 100 // 慢速模式下,多久扫描一次键盘 (ms) +#define LED_AUTOOFF_TIME 60 // LED自动熄灭时长(s),设为0则不自动熄灭 +#define DYNAMIC_TX_POWER /* 启用自动发射功率调整 */ +// #define HIGH_TX_POWER /* 更改发射功率到+4dBm */ + +/* Extra Features */ +#define ENABLE_WATCHDOG /* 启用看门狗 */ +#define KEYMAP_STORAGE /* 启用keymap存储 */ +#define MACRO_STORAGE /* 启用宏存储功能 */ +#define CONFIG_STORAGE /* 启用配置存储功能 */ +#define BUTTONLESS_DFU /* 启用免按钮DFU */ + +/* USB HID */ +#define KEYBOARD_EPSIZE 8 /* 键盘上传端点大小,请不要修改 */ +#define NKRO_EPSIZE 28 /* 键盘NKRO端点大小,请不要修改 */ + +/* USB UART */ +#define HAS_USB // 启用与CH554的通信支持 +#define UART_RXD 26 // UART_RX IO +#define UART_TXD 27 // UART_TX IO +#define UART_BAUDRATE NRF_UART_BAUDRATE_115200 // 通信波特率,请不要修改 + +/* Battery */ +#define BATTERY_ADC_PIN NRF_SAADC_INPUT_AIN0 // 电量检测引脚 +#define PIN_CHARGING !UCC1 // CH554的充电检测。当UCC1拉低时表示正在充电 +#define PIN_STANDBY !UCC2 // CH554的充电检测。当UCC2拉低时表示充电完成。若不配置则只使用PIN_CHARGING作为是否充电的检测标志 diff --git a/keyboard/magnet/keymap_common.h b/keyboard/magnet/keymap_common.h new file mode 100644 index 000000000..3ee79b35c --- /dev/null +++ b/keyboard/magnet/keymap_common.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "keymap.h" +#include "config.h" + +extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +extern const action_t fn_actions[]; + +#define KEYMAP( \ + K00, K01, \ + K10, K11, \ + K20, K21, \ + K30, K31 \ +) { \ + { K00, K01 }, \ + { K10, K11 }, \ + { K20, K21 }, \ + { K30, K31 } \ +} diff --git a/keyboard/magnet/keymap_plain.c b/keyboard/magnet/keymap_plain.c new file mode 100644 index 000000000..cb7532452 --- /dev/null +++ b/keyboard/magnet/keymap_plain.c @@ -0,0 +1,21 @@ +#include "keymap_common.h" +#include "keyboard_fn.h" + +const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + KEYMAP( + KC_1, KC_2, + KC_Q, KC_W, + KC_A, KC_S, + KC_Z, KC_FN0 + ), + KEYMAP( + KC_5, KC_6, + KC_T, KC_Y, + KC_G, KC_H, + KC_B, KC_TRNS + ), +}; + +const action_t fn_actions[] = { + ACTION_LAYER_MOMENTARY(1), +}; diff --git a/keyboard/magnet/matrix_analog.c b/keyboard/magnet/matrix_analog.c new file mode 100644 index 000000000..1840dd8b6 --- /dev/null +++ b/keyboard/magnet/matrix_analog.c @@ -0,0 +1,198 @@ +#include +#include + +#include "nrf.h" +#include "nrf_delay.h" +#include "nrf_gpio.h" + +#include "keyboard_config.h" +#include "adc_convert.h" +#include "ble_keyboard.h" +#include "debug.h" +#include "keyboard_matrix.h" +#include "matrix.h" +#include "print.h" +#include "util.h" +#include "wait.h" + +// 行程触发阈值 +// TODO 先这样,动态调节的功能后面再加 +static uint16_t magnet_threshold = MAGNET_THRESHOLD_DEFAULT; + +/* matrix state(1:on, 0:off) */ +static matrix_row_t matrix[MATRIX_ROWS]; + +void init_adc_col_pin(uint8_t pin) { + uint8_t channel_index; + nrf_saadc_input_t analog_input_pin; + // NRF_SAADC_INPUT_AIN0 (P0.02) has been used to detect battery voltage + switch (pin) { + case 3: + channel_index = 1; + analog_input_pin = NRF_SAADC_INPUT_AIN1; + break; + case 4: + channel_index = 2; + analog_input_pin = NRF_SAADC_INPUT_AIN2; + break; + case 5: + channel_index = 3; + analog_input_pin = NRF_SAADC_INPUT_AIN3; + break; + case 28: + channel_index = 4; + analog_input_pin = NRF_SAADC_INPUT_AIN4; + break; + case 29: + channel_index = 5; + analog_input_pin = NRF_SAADC_INPUT_AIN5; + break; + case 30: + channel_index = 6; + analog_input_pin = NRF_SAADC_INPUT_AIN6; + break; + case 31: + channel_index = 7; + analog_input_pin = NRF_SAADC_INPUT_AIN7; + break; + default: + channel_index = -1; // 理论上不可能走到这个分支? + analog_input_pin = NRF_SAADC_INPUT_DISABLED; + } + + nrf_saadc_channel_config_t channel_config = { + .resistor_p = NRF_SAADC_RESISTOR_DISABLED, + .resistor_n = NRF_SAADC_RESISTOR_DISABLED, + .gain = NRF_SAADC_GAIN1_2, + .reference = NRF_SAADC_REFERENCE_INTERNAL, + .acq_time = NRF_SAADC_ACQTIME_10US, + .mode = NRF_SAADC_MODE_SINGLE_ENDED, + .burst = NRF_SAADC_BURST_DISABLED, + .pin_p = (nrf_saadc_input_t)(analog_input_pin), + .pin_n = NRF_SAADC_INPUT_DISABLED + }; + + ret_code_t err_code; + err_code = nrfx_saadc_channel_init(channel_index, &channel_config); + APP_ERROR_CHECK(err_code); +} + +/* 初始化键盘阵列 */ +void matrix_init(void) { + // 拉低mux使能引脚,启用mux + for (uint_fast8_t i = 0; i < MUX_COUNT; i++) { + nrf_gpio_cfg_output(enable_pin_array[i]); + nrf_gpio_pin_clear(enable_pin_array[i]); + } + + // 全部拉高,先选中最后一个通道 + for (uint_fast8_t i = 0; i < MUX_CHANNELS; i++) { + nrf_gpio_cfg_output(channel_pin_array[i]); + nrf_gpio_pin_set(channel_pin_array[i]); + } + + // 初始化模拟引脚 + for (uint_fast8_t i = 0; i < MATRIX_COLS; i++) { + init_adc_col_pin(column_pin_array[i]); + } + + // 正常使用时禁用唤醒引脚 + nrf_gpio_cfg_default(WAKE_UP_PIN); +} + +/* Read all mux sig pins */ +matrix_row_t read_cols(void) { + matrix_row_t result = 0; + + for (uint_fast8_t c = 0; c < MATRIX_COLS; c++) { + nrf_saadc_value_t adc_val = adc_read_sync(column_pin_array[c]); + + if (adc_val < magnet_threshold) { + // Pressed + result |= (1 << c); + } else { + // Released + result &= ~(1 << c); + } + } + + return result; +} + +/* Select mux channel */ +void select_row(uint8_t channel) { + for (uint_fast8_t i = 0; i < MUX_CHANNELS; i++) { + uint8_t state = (channel >> i) & 1; + if (state) { + // set high + nrf_gpio_cfg_output(channel_pin_array[i]); + nrf_gpio_pin_set(channel_pin_array[i]); + } else { + // set low + nrf_gpio_cfg_output(channel_pin_array[i]); + nrf_gpio_pin_clear(channel_pin_array[i]); + } + } +} + +static inline void delay_us(void) { +#ifdef __GNUC__ +#define __nop() __asm("NOP") +#endif + +#ifndef MATRIX_SCAN_DELAY_CYCLE +#define MATRIX_SCAN_DELAY_CYCLE 36 +#endif + for (int i = 0; i < MATRIX_SCAN_DELAY_CYCLE; i++) { + __nop(); // 64mhz, 64 cycles = 1us + } +} + +uint8_t matrix_scan(void) { + for (uint_fast8_t i = 0; i < MATRIX_ROWS; i++) { + select_row(i); + delay_us(); // wait stable + matrix_row_t cols = read_cols(); + matrix[i] = cols; + } + + return 1; +} + +bool matrix_is_modified(void) { + return true; +} + +/* 禁用所有阵列针脚 */ +void matrix_deinit(void) { + for (uint_fast8_t i = 0; i < MUX_COUNT; i++) { + nrf_gpio_cfg_default(enable_pin_array[i]); + } + for (uint_fast8_t i = 0; i < MUX_CHANNELS; i++) { + nrf_gpio_cfg_default(channel_pin_array[i]); + } + for (uint_fast8_t i = 0; i < MATRIX_COLS; i++) { + nrf_gpio_cfg_default(column_pin_array[i]); + } +} + +/* 阵列准备睡眠 */ +void matrix_wakeup_prepare(void) { + // 拉高mux使能引脚,禁用mux + for (uint_fast8_t i = 0; i < MUX_COUNT; i++) { + nrf_gpio_cfg_output(enable_pin_array[i]); + nrf_gpio_pin_set(enable_pin_array[i]); + } + + // 禁用其他引脚 + for (uint_fast8_t i = 0; i < MUX_CHANNELS; i++) { + nrf_gpio_cfg_default(channel_pin_array[i]); + } + for (uint_fast8_t i = 0; i < MATRIX_COLS; i++) { + nrf_gpio_cfg_default(column_pin_array[i]); + } + + // 只保留一个唤醒引脚,上拉即唤醒。 + // TODO 考虑用振动开关,或者TTP223电容触摸 + nrf_gpio_cfg_sense_input(WAKE_UP_PIN, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH); +} diff --git a/keyboard/magnet/rules.mk b/keyboard/magnet/rules.mk new file mode 100644 index 000000000..7a74b91c3 --- /dev/null +++ b/keyboard/magnet/rules.mk @@ -0,0 +1,15 @@ +# 主控芯片类型:可选 nrf52810 或 nrf52832 +NRF_CHIP := nrf52832 + +# 功能选项:更多可配置项目,请参考doc目录下的相应文档 +BOOTMAGIC_ENABLE = no # 启用Bootmagic +BOOTCHECK_ENABLE = no # 启用Bootcheck +EXTRAKEY_ENABLE = yes # 启用媒体键功能 +MOUSEKEY_ENABLE = yes # 启用鼠标键功能 +USB_6KRO_ENABLE = yes # 启用USB的六键无冲功能 +NKRO_ENABLE = yes # 启用USB的全键无冲功能 +COMMAND_ENABLE = yes # 启用调试和配置的命令 +RGBLIGHT_ENABLE = no # 启用RGB灯 +THREE_LED_STATUS = no # 启用键盘运行状态灯 +ONBOARD_CMSIS_DAP = yes # 启用板载调试器 +NRF52_DISABLE_FPU = yes # 禁用FPU