diff --git a/keyboards/nuphy/air60_v2/ansi/ansi.c b/keyboards/nuphy/air60_v2/ansi/ansi.c
new file mode 100644
index 000000000000..745397f3139e
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/ansi.c
@@ -0,0 +1,444 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#include "user_kb.h"
+#include "ansi.h"
+#include "mcu_pwr.h"
+
+/* qmk pre-process record */
+bool pre_process_record_kb(uint16_t keycode, keyrecord_t *record) {
+ no_act_time = 0;
+ rf_linking_time = 0;
+
+
+ // wake up immediately
+ if (f_wakeup_prepare) {
+ exit_light_sleep(false);
+ // f_wakeup_prepare = 0;
+ }
+
+ if (!pre_process_record_user(keycode, record)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* qmk process record */
+bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+
+ if (!process_record_user(keycode, record)) {
+ return false;
+ }
+
+ switch (keycode) {
+ case SIDE_VAI:
+ case SIDE_VAD:
+ case SIDE_HUI:
+ case NUMLOCK_IND:
+ if (game_mode_enable) { break; }
+ call_update_eeprom_data(&user_update);
+ break;
+
+ case SIDE_MOD:
+ case SIDE_SPI:
+ case SIDE_SPD:
+ case SIDE_1:
+ case SLEEP_MODE:
+ case CAPS_WORD:
+ if (game_mode_enable) { return false; }
+ call_update_eeprom_data(&user_update);
+ break;
+
+ case BAT_SHOW:
+ case SLEEP_NOW:
+ if (game_mode_enable) { return false; }
+ break;
+
+ case RGB_VAI:
+ case RGB_VAD:
+ if (game_mode_enable) { break; }
+ call_update_eeprom_data(&rgb_update);
+ break;
+
+ case RGB_MOD:
+ case RGB_HUI:
+ case RGB_SPI:
+ case RGB_SPD:
+ if (game_mode_enable) { return false; }
+ call_update_eeprom_data(&rgb_update);
+ break;
+ }
+
+ switch (keycode) {
+ case RF_DFU:
+ if (record->event.pressed) {
+ if (dev_info.link_mode != LINK_USB) { return false; }
+ uart_send_cmd(CMD_RF_DFU, 10, 20);
+ }
+ return false;
+
+ case LNK_USB:
+ if (record->event.pressed) {
+ break_all_key();
+ } else {
+ dev_info.link_mode = LINK_USB;
+ uart_send_cmd(CMD_SET_LINK, 10, 10);
+ }
+ return false;
+
+ case LNK_RF ... LNK_BLE3:
+ if (record->event.pressed) {
+ if (dev_info.link_mode != LINK_USB) {
+ rf_sw_temp = keycode - 2;
+ f_rf_sw_press = 1;
+ break_all_key();
+ }
+ } else {
+ if (f_rf_sw_press) {
+ if (rf_sw_press_delay < MEDIUM_PRESS_DELAY) {
+ set_link_mode();
+ uart_send_cmd(CMD_SET_LINK, 10, 20);
+ }
+ }
+ rf_sw_press_delay = 0;
+ }
+ return false;
+
+ case GAME_MODE:
+ if (record->event.pressed) {
+ game_mode_enable = !game_mode_enable;
+ game_mode_tweak();
+ }
+ return false;
+
+ case CAPS_WORD:
+ f_caps_word_tg = record->event.pressed;
+ return false;
+
+ case WIN_LOCK:
+ if (record->event.pressed) {
+ keymap_config.no_gui = !keymap_config.no_gui;
+ signal_rgb_led(!keymap_config.no_gui, WIN_LED, WIN_LED, 3000);
+ // eeconfig_update_keymap(keymap_config.raw);
+ }
+ return false;
+
+ case KC_LSFT:
+ if (!record->event.pressed) {
+ if ((!user_config.caps_word_enable || game_mode_enable) && is_caps_word_on()) { caps_word_off(); }
+ }
+ return true;
+
+ case MAC_VOICE:
+ if (record->event.pressed) {
+ if (dev_info.sys_sw_state == SYS_SW_MAC) {
+ host_consumer_send(0xcf);
+ } else {
+ tap_code(KC_F5);
+ }
+ } else if (dev_info.sys_sw_state == SYS_SW_MAC) {
+ host_consumer_send(0);
+ }
+ return false;
+
+ case MAC_DND:
+ if (record->event.pressed) {
+ host_system_send(0x9b);
+ } else {
+ host_system_send(0);
+ }
+ return false;
+
+ case TASK:
+ if (record->event.pressed) {
+ if (dev_info.sys_sw_state == SYS_SW_MAC) {
+ tap_code(KC_MCTL);
+ } else {
+ tap_code(KC_CALC);
+ }
+ }
+ return false;
+
+ case SEARCH:
+ if (record->event.pressed) {
+ if (dev_info.sys_sw_state == SYS_SW_MAC) {
+ register_code(KC_LGUI);
+ register_code(KC_SPACE);
+ wait_ms(TAP_CODE_DELAY);
+ unregister_code(KC_LGUI);
+ unregister_code(KC_SPACE);
+ } else {
+ register_code(KC_LCTL);
+ register_code(KC_F);
+ wait_ms(TAP_CODE_DELAY);
+ unregister_code(KC_F);
+ unregister_code(KC_LCTL);
+ }
+ }
+ return false;
+
+ case PRT_SCR:
+ if (record->event.pressed) {
+ if (dev_info.sys_sw_state == SYS_SW_MAC) {
+ register_code(KC_LGUI);
+ register_code(KC_LSFT);
+ register_code(KC_3);
+ wait_ms(TAP_CODE_DELAY);
+ unregister_code(KC_3);
+ unregister_code(KC_LSFT);
+ unregister_code(KC_LGUI);
+ } else {
+ tap_code(KC_PSCR);
+ }
+ }
+ return false;
+
+ case PRT_AREA:
+ if (record->event.pressed) {
+ if (dev_info.sys_sw_state == SYS_SW_MAC) {
+ register_code(KC_LGUI);
+ register_code(KC_LSFT);
+ register_code(KC_4);
+ wait_ms(TAP_CODE_DELAY);
+ unregister_code(KC_4);
+ unregister_code(KC_LSFT);
+ unregister_code(KC_LGUI);
+ }
+ else {
+ tap_code(KC_PSCR);
+ }
+ }
+ return false;
+
+ case SIDE_VAI:
+ if (record->event.pressed) {
+ side_light_control(1);
+ }
+ return false;
+
+ case SIDE_VAD:
+ if (record->event.pressed) {
+ side_light_control(0);
+ }
+ return false;
+
+ case SIDE_MOD:
+ if (record->event.pressed) {
+ side_mode_control(1);
+ }
+ return false;
+
+ case SIDE_HUI:
+ if (record->event.pressed) {
+ side_colour_control(1);
+ }
+ return false;
+
+ case SIDE_SPI:
+ if (record->event.pressed) {
+ side_speed_control(1);
+ }
+ return false;
+
+ case SIDE_SPD:
+ if (record->event.pressed) {
+ side_speed_control(0);
+ }
+ return false;
+
+ case SIDE_1:
+ if (record->event.pressed) {
+ side_one_control(1);
+ }
+ return false;
+
+ case RGB_VAI:
+ if (record->event.pressed) {
+ rgb_matrix_increase_val_noeeprom();
+ }
+ return false;
+
+ case RGB_VAD:
+ if (record->event.pressed) {
+ rgb_matrix_decrease_val_noeeprom();
+ }
+ return false;
+
+ case RGB_MOD:
+ if (record->event.pressed) {
+ rgb_matrix_step_noeeprom();
+ }
+ return false;
+
+ case RGB_HUI:
+ if (record->event.pressed) {
+ rgb_matrix_increase_hue_noeeprom();
+ }
+ return false;
+
+ case RGB_SPI:
+ if (record->event.pressed) {
+ rgb_matrix_increase_speed_noeeprom();
+ }
+ return false;
+
+ case RGB_SPD:
+ if (record->event.pressed) {
+ rgb_matrix_decrease_speed_noeeprom();
+ }
+ return false;
+
+ case DEV_RESET:
+ if (record->event.pressed) {
+ f_dev_reset_press = 1;
+ break_all_key();
+ } else {
+ f_dev_reset_press = 0;
+ }
+ return false;
+
+ case SLEEP_MODE:
+ if (record->event.pressed) {
+ user_config.sleep_mode++;
+ if (user_config.sleep_mode < 3) {
+ link_timeout = (NO_ACT_TIME_MINUTE * user_config.sleep_mode);
+ sleep_time_delay = (NO_ACT_TIME_MINUTE * (4 * user_config.sleep_mode - 2));
+ } else { user_config.sleep_mode = 0; }
+ sleep_show_timer = timer_read32();
+ }
+ return false;
+
+ case BAT_SHOW:
+ if (record->event.pressed) {
+ f_bat_hold = !f_bat_hold;
+ }
+ return false;
+
+ case BAT_NUM:
+ f_bat_num_show = record->event.pressed;
+ return false;
+
+ case RGB_TEST:
+ f_rgb_test_press = record->event.pressed;
+ return false;
+
+ case NUMLOCK_INS:
+ if (record->event.pressed) {
+ if (get_mods() & MOD_MASK_CSA) {
+ tap_code(KC_INS);
+ f_numlock_press = 0;
+ } else { f_numlock_press = 1; }
+ } else {
+ if (f_numlock_press) {
+ f_numlock_press = 0;
+ tap_code(KC_INS);
+ }
+ }
+ return false;
+
+ case NUMLOCK_IND:
+ if (record->event.pressed) {
+ if (user_config.numlock_state < 2 - game_mode_enable) { user_config.numlock_state++; }
+ else { user_config.numlock_state = 0; }
+ }
+ return false;
+
+ case SLEEP_NOW:
+ if (record->event.pressed) {
+ wait_ms(100);
+ } else {
+ if (user_config.sleep_mode == 0) { return true; }
+ else {
+ f_goto_sleep = 1;
+ f_goto_deepsleep = 1;
+ }
+ }
+ return true;
+
+ default:
+ return true;
+ }
+ return true;
+}
+
+bool rgb_matrix_indicators_kb(void) {
+ if (!rgb_matrix_indicators_user()) {
+ return false;
+ }
+
+ if(f_bat_num_show) {
+ bat_num_led();
+ }
+
+ // power down unused LEDs
+ led_power_handle();
+ return true;
+}
+
+/* qmk keyboard post init */
+void keyboard_post_init_kb(void) {
+ gpio_init();
+ rf_uart_init();
+ wait_ms(500);
+ rf_device_init();
+
+ break_all_key();
+ load_eeprom_data();
+ dial_sw_fast_scan();
+#ifndef NO_DEBUG
+ debug_enable = false;
+ // debug_matrix = true;
+ // debug_keyboard = true;
+ // debug_mouse = true;
+#endif
+ keyboard_post_init_user();
+}
+
+/*
+void rgb_process_record_helper(const rgb_func_pointer rgb_func_noeeprom) {
+ rgb_func_noeeprom();
+ eeprom_update_timer = 0;
+ rgb_update = 1;
+}
+*/
+
+/* qmk housekeeping task */
+void housekeeping_task_kb(void) {
+ timer_pro();
+
+ uart_receive_pro();
+
+ uart_send_report_repeat();
+
+ dev_sts_sync();
+
+ custom_key_press();
+
+ led_show();
+
+#ifndef NO_DEBUG
+ user_debug();
+#endif
+
+ if (game_mode_enable) { return; }
+
+ delay_update_eeprom_data();
+
+ sleep_handle();
+
+}
diff --git a/keyboards/nuphy/air60_v2/ansi/ansi.h b/keyboards/nuphy/air60_v2/ansi/ansi.h
new file mode 100644
index 000000000000..7a03eabab15b
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/ansi.h
@@ -0,0 +1,56 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#pragma once
+
+#include "quantum.h"
+
+enum custom_keycodes {
+ RF_DFU = QK_KB_0,
+ LNK_USB,
+ LNK_RF,
+ LNK_BLE1,
+ LNK_BLE2,
+ LNK_BLE3,
+
+ MAC_VOICE, // F5
+ MAC_DND,
+ TASK, // KC_CALC
+ SEARCH, // KC_LCTL + KC_F
+ PRT_SCR, // PrintScreen
+ PRT_AREA, // PrintScreen
+
+ SIDE_VAI,
+ SIDE_VAD,
+ SIDE_MOD,
+ SIDE_HUI,
+ SIDE_SPI,
+ SIDE_SPD,
+ SIDE_1,
+
+ DEV_RESET,
+ SLEEP_MODE,
+ BAT_SHOW,
+ BAT_NUM,
+ RGB_TEST,
+ NUMLOCK_INS,
+ NUMLOCK_IND,
+ SLEEP_NOW,
+ GAME_MODE,
+ CAPS_WORD,
+ WIN_LOCK
+};
diff --git a/keyboards/nuphy/air60_v2/ansi/config.h b/keyboards/nuphy/air60_v2/ansi/config.h
new file mode 100644
index 000000000000..93fe346f711e
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/config.h
@@ -0,0 +1,87 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#pragma once
+
+#define DEV_MODE_PIN C0
+#define SYS_MODE_PIN C1
+#define DC_BOOST_PIN C2
+#define NRF_RESET_PIN B4
+#define NRF_BOOT_PIN B5
+#define NRF_WAKEUP_PIN C4
+#define RGB_DRIVER_SDB1 C6
+#define RGB_DRIVER_SDB2 C7
+
+#define SERIAL_DRIVER SD1
+#define SD1_TX_PIN B6
+#define SD1_TX_PAL_MODE 0
+#define SD1_RX_PIN B7
+#define SD1_RX_PAL_MODE 0
+
+#if !defined(NO_DEBUG) && !defined(CONSOLE_ENABLE)
+#define NO_DEBUG
+#endif // !NO_DEBUG
+
+#if !defined(NO_PRINT) && !defined(CONSOLE_ENABLE)
+#define NO_PRINT
+#endif // !NO_PRINT
+
+#if !defined(NO_DEBUG) && defined(CONSOLE_ENABLE)
+#define DEBUG_MATRIX_SCAN_RATE
+#endif // MATRIX_SCAN_RATE
+
+
+#define DYNAMIC_KEYMAP_MACRO_DELAY 8
+
+// This is a 7-bit address, that gets left-shifted and bit 0
+// set to 0 for write, 1 for read (as per I2C protocol)
+// The address will vary depending on your wiring:
+// 0b1110100 AD <-> GND
+// 0b1110111 AD <-> VCC
+// 0b1110101 AD <-> SCL
+// 0b1110110 AD <-> SDA
+#define DRIVER_ADDR_1 0b1010000
+#define DRIVER_ADDR_2 0b1010011
+
+#define ISSI_TIMEOUT 1
+
+#define I2C_DRIVER I2CD1
+#define I2C1_SCL_PIN B8
+#define I2C1_SDA_PIN B9
+#define I2C1_CLOCK_SPEED 1000000
+
+#define I2C1_SCL_PAL_MODE 1
+#define I2C1_SDA_PAL_MODE 1
+
+#define I2C1_TIMINGR_PRESC 0U
+#define I2C1_TIMINGR_SCLDEL 0U
+#define I2C1_TIMINGR_SDADEL 0U
+#define I2C1_TIMINGR_SCLH 0U
+#define I2C1_TIMINGR_SCLL 0U
+#define I2C1_DUTY_CYCLE FAST_DUTY_CYCLE_16_9
+
+#define DRIVER_COUNT 2
+#define DRIVER_1_LED_TOTAL 48
+#define DRIVER_2_LED_TOTAL (16 + 10)
+#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+
+#define EECONFIG_KB_DATA_SIZE 12
+#define MCU_SLEEP_ENABLE 1
+#define GPIO_INPUT_PIN_DELAY 6
+#define RF_QUEUE_SIZE 64
+
+#define RGB_MATRIX_DISABLE_KEYCODES
diff --git a/keyboards/nuphy/air60_v2/ansi/customizations.md b/keyboards/nuphy/air60_v2/ansi/customizations.md
new file mode 100644
index 000000000000..b0ea5a9e1139
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/customizations.md
@@ -0,0 +1,68 @@
+# Customizations & Fixes
+
+The following customizations were applied on top of the stock firmware.
+
+- Fn + M + O Changes the side light to display on either left side or right side or alternating sides every 1 minute.
+- Numlock indicator using side leds while color
+- Keyboard has 3 sleep levels:
+ 1. Leds off, RF module on, MCU on
+ 2. Leds off, RF module off, MCU on
+ 3. Leds off, RF module off, MCU standby/sleep
+
+ By default keyboard will go to sleep level 1 in 2 minutes and sleep level 2 in 6 minutes (when using 2.4G dongle) or in 30 minutes (when using BT connection)
+ If 2.4G is selected and there is no connectivity in 15 seconds keyboard will enter level 3 sleep.
+ If BT is selected and there is no connectivity in 60 seconds keyboard will enter level 3 sleep.
+
+ To change sleep mode you can use Fn + ]:
+ When on default mode (explained above, the right side leds will flash 4 times green)
+
+ The next mode will flash 4 times orange (This is actually the default sleep policy for Nuphy firmware version 1.1.3).
+ In this mode the following applies:
+ Keyboard will go to sleep level 1 in 6 minutes
+ If 2.4G or BT is selected and there is no connectivity in 120 seconds(BT) or 30 seconds (RF 2.4G) keyboard will enter level 2 sleep.
+
+ On the last sleep mode the right side leds will flash 4 times red.
+ In this mode the keyboard will never sleep.
+
+ Side indicators will flash blue for 0.3s when board enters level 3 sleep mode, as an indicator.
+
+- Fn + B will show current battery levels using numbers from 1 to 0 (0 means 100%). The color will be red, yellow, light green, dark green) based on battery level.
+ Last led will have a different color based on battery percentage as well.
+ Example:
+ if 1 to 4 lights up and the last led that lights up 5 is dark green color you will have between 47 and 50% battery
+ if 1 to 4 lights up and the last led that lights up 5 is light green color you will have between 44 and 46 % battery
+ if 1 to 4 lights up and the last led that lights up 5 is red color you will have between 41 and 43 % battery
+
+- The custom MAC keys now have double functionality:
+ MAC_VOICE if Mac mode selected and F5 if Win mode is selected
+ TASK if Mac mode selected and KC_CALC if Win mode is selected
+ SEARCH if Mac mode selected and KC_LCTL + KC_F if Win mode is selected
+ PRT_SCR if Mac mode selected and PrintScreen if Win mode is selected
+ PRT_AREA if Mac mode selected and PrintScreen if Win mode is selected
+
+- New custom key: NUMLOCK_INS. On longpress it will act as NUMLOCK and on short press as INS.
+- New algorithm to prevent unnecessary EEPROM writes. When you change RGB modes or side lights it will only save the setting to EEPROM after 30 seconds.
+ If for instance your need to press mode button for 5 times to reach your favourite RGB effect, normally you will get 1 EEPROM save for each press (5 EEPROM writes).
+ With the new algorithm you will get 1 write to EEPROM after 40 seconds from last press. As a side efect if you change something and you shutdown the keyboard immediately you will lose the change.
+
+- Bluetooth and RF will signal on corresponding number keys when trying to connect (Blue color and keys 1 to 3 for BT_1 to BT_3 or Green color and key 4 when it is trying to connect using 2.4G)
+
+- Side light brightness levels are now 7 (instead of 6) from 0 (disabled) to 6 (full). A new lower step has been introduced. Default level after reset is 1 (the lowest ilumination possible for side leds)
+
+- The system indicators now have 3 levels of brightness (before there was only 1 level) that are dependend on the side light brightness. Thus for example capslock will no longer light at full brightness if side leds have a lower brightness selected.
+
+- Default startup LED brightness is set to half of max. Default RGB effect is RGB_MATRIX_CYCLE_LEFT_RIGHT
+- Default tapping has been reduced to 6ms (from 8ms default) and debounce decreased to 1 ms
+- Deep sleep algorithm (level 3 sleep) is applied using code from nuphy / jincao1
+- Fix keyboard randomly crashing/freezing (algorithm from jincao1 with small modifications)
+- Fix LED lights not powering down when not used. Because of how the keyboard is build, both rgb and side leds need to be off, they can't be controlled individually (algorithm from jincao1)
+- Fix keystrokes being lost on wake. Wake keystrokes will appear after a very short delay while board re-establishes connection. BT may not be as reliable as the dongle.
+ This is achieved through a buffer of 64 key actions (key down and key up are 2 actions). The buffer is cleared if connection is not established within 1s after the last action.
+ Key events after the buffer is full will also be dropped. (Buffer algorithm taken from jincao1)
+- Enhance keyboard reports transmission logic to greatly reduce stuck/lost key strokes. It may still occasionally drop/repeat keys but it's rare. (algorithm from jincao1 with small modifications)
+- Side light algorithms are modified in order to reduce firmware size and to make animations more smooth. On the lowest level of brightness for side leds you might see some jerkiness on some of the breath algorithm. This is because the lowest level of brightness is lower than standard Nuphy.
+
+## Author
+
+[@adi4086](https://github.com/adi4086)
+
diff --git a/keyboards/nuphy/air60_v2/ansi/halconf.h b/keyboards/nuphy/air60_v2/ansi/halconf.h
new file mode 100644
index 000000000000..27125fb9f0d4
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/halconf.h
@@ -0,0 +1,26 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#pragma once
+
+#include_next
+
+#undef HAL_USE_SERIAL
+#define HAL_USE_SERIAL TRUE
+
+#undef HAL_USE_I2C
+#define HAL_USE_I2C TRUE
diff --git a/keyboards/nuphy/air60_v2/ansi/keyboard.json b/keyboards/nuphy/air60_v2/ansi/keyboard.json
new file mode 100644
index 000000000000..9995a2208fdf
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/keyboard.json
@@ -0,0 +1,255 @@
+{
+ "manufacturer": "NuPhy",
+ "keyboard_name": "NuPhy Air60 V2",
+ "processor": "STM32F072",
+ "bootloader": "stm32-dfu",
+ "build": {
+ "debounce_type": "asym_eager_defer_pk",
+ "lto": true
+ },
+ "debounce": 3,
+ "features": {
+ "bootmagic": true,
+ "command": false,
+ "console": true,
+ "extrakey": true,
+ "key_lock": false,
+ "mousekey": true,
+ "nkro": true,
+ "rgb_matrix": true
+ },
+ "caps_word": {
+ "double_tap_shift_turns_on": true,
+ "enabled": true,
+ "idle_timeout": 5000
+ },
+ "usb":{
+ "vid": "0x19F5",
+ "pid": "0x3265",
+ "polling_interval": 1,
+ "device_version": "0.0.1",
+ "suspend_wakeup_delay": 50,
+ "no_startup_check": true
+ },
+ "qmk": {
+ "tap_keycode_delay": 8
+ },
+ "tapping": {
+ "term": 300
+ },
+ "dynamic_keymap": {
+ "layer_count": 8
+ },
+ "matrix_pins": {
+ "cols": ["A4", "A5", "A6", "A7", "B0", "B1", "B10", "B11", "B12", "B13", "B14", "B15", "A8", "A9", "A10", "A15", "B3"],
+ "rows": ["C14", "C15", "A0", "A1", "A2", "A3"],
+ "io_delay": 20
+ },
+ "diode_direction": "COL2ROW",
+ "rgb_matrix": {
+ "driver": "is31fl3733",
+ "center_point": [70, 20],
+ "max_brightness": 255,
+ "val_steps": 52,
+ "speed_steps": 52,
+ "sleep": true,
+ "led_process_limit": 12,
+ "led_flush_limit": 18,
+ "default": {
+ "animation": "cycle_left_right",
+ "hue": 0,
+ "val": 156,
+ "speed": 151
+ },
+ "animations": {
+ "gradient_up_down": true,
+ "gradient_left_right": true,
+ "breathing": true,
+ "band_sat": true,
+ "band_val": true,
+ "band_pinwheel_sat": true,
+ "band_pinwheel_val": true,
+ "band_spiral_sat": true,
+ "band_spiral_val": true,
+ "cycle_all": true,
+ "cycle_left_right": true,
+ "cycle_up_down": true,
+ "rainbow_moving_chevron": true,
+ "cycle_out_in": true,
+ "cycle_out_in_dual": true,
+ "cycle_pinwheel": true,
+ "cycle_spiral": true,
+ "dual_beacon": true,
+ "rainbow_beacon": true,
+ "rainbow_pinwheels": true,
+ "flower_blooming": true,
+ "raindrops": true,
+ "jellybean_raindrops": true,
+ "hue_breathing": true,
+ "hue_pendulum": true,
+ "hue_wave": true,
+ "typing_heatmap": true,
+ "digital_rain": true,
+ "solid_reactive_simple": true,
+ "solid_reactive": true,
+ "solid_reactive_wide": true,
+ "solid_reactive_multiwide": true,
+ "solid_reactive_cross": true,
+ "solid_reactive_multicross": true,
+ "solid_reactive_nexus": true,
+ "solid_reactive_multinexus": true,
+ "splash": true,
+ "multisplash": true,
+ "solid_splash": true,
+ "solid_multisplash": true
+ },
+ "layout": [
+ {"matrix": [0, 0], "x": 0, "y": 0, "flags": 4},
+ {"matrix": [1, 1], "x": 10, "y": 0, "flags": 4},
+ {"matrix": [1, 2], "x": 20, "y": 0, "flags": 4},
+ {"matrix": [1, 3], "x": 30, "y": 0, "flags": 4},
+ {"matrix": [1, 4], "x": 40, "y": 0, "flags": 4},
+ {"matrix": [1, 5], "x": 50, "y": 0, "flags": 4},
+ {"matrix": [1, 6], "x": 60, "y": 0, "flags": 4},
+ {"matrix": [1, 7], "x": 70, "y": 0, "flags": 4},
+ {"matrix": [1, 8], "x": 80, "y": 0, "flags": 4},
+ {"matrix": [1, 9], "x": 90, "y": 0, "flags": 4},
+ {"matrix": [1, 10], "x": 100, "y": 0, "flags": 4},
+ {"matrix": [1, 11], "x": 110, "y": 0, "flags": 4},
+ {"matrix": [1, 12], "x": 120, "y": 0, "flags": 4},
+ {"matrix": [1, 13], "x": 130, "y": 0, "flags": 4},
+
+ {"matrix": [2, 0], "x": 0, "y": 10, "flags": 4},
+ {"matrix": [2, 1], "x": 15, "y": 10, "flags": 4},
+ {"matrix": [2, 2], "x": 25, "y": 10, "flags": 4},
+ {"matrix": [2, 3], "x": 35, "y": 10, "flags": 4},
+ {"matrix": [2, 4], "x": 45, "y": 10, "flags": 4},
+ {"matrix": [2, 5], "x": 55, "y": 10, "flags": 4},
+ {"matrix": [2, 6], "x": 65, "y": 10, "flags": 4},
+ {"matrix": [2, 7], "x": 75, "y": 10, "flags": 4},
+ {"matrix": [2, 8], "x": 85, "y": 10, "flags": 4},
+ {"matrix": [2, 9], "x": 95, "y": 10, "flags": 4},
+ {"matrix": [2, 10], "x": 105, "y": 10, "flags": 4},
+ {"matrix": [2, 11], "x": 115, "y": 10, "flags": 4},
+ {"matrix": [2, 12], "x": 125, "y": 10, "flags": 4},
+ {"matrix": [2, 13], "x": 135, "y": 10, "flags": 4},
+
+ {"matrix": [3, 0], "x": 0, "y": 20, "flags": 4},
+ {"matrix": [3, 1], "x": 17.5, "y": 20, "flags": 4},
+ {"matrix": [3, 2], "x": 27.5, "y": 20, "flags": 4},
+ {"matrix": [3, 3], "x": 37.5, "y": 20, "flags": 4},
+ {"matrix": [3, 4], "x": 47.5, "y": 20, "flags": 4},
+ {"matrix": [3, 5], "x": 57.5, "y": 20, "flags": 4},
+ {"matrix": [3, 6], "x": 67.5, "y": 20, "flags": 4},
+ {"matrix": [3, 7], "x": 77.5, "y": 20, "flags": 4},
+ {"matrix": [3, 8], "x": 87.5, "y": 20, "flags": 4},
+ {"matrix": [3, 9], "x": 97.5, "y": 20, "flags": 4},
+ {"matrix": [3, 10], "x": 107.5, "y": 20, "flags": 4},
+ {"matrix": [3, 11], "x": 117.5, "y": 20, "flags": 4},
+ {"matrix": [3, 13], "x": 127.5, "y": 20, "flags": 4},
+
+ {"matrix": [4, 0], "x": 0, "y": 30, "flags": 4},
+ {"matrix": [4, 2], "x": 20, "y": 30, "flags": 4},
+ {"matrix": [4, 3], "x": 30, "y": 30, "flags": 4},
+ {"matrix": [4, 4], "x": 40, "y": 30, "flags": 4},
+ {"matrix": [4, 5], "x": 50, "y": 30, "flags": 4},
+ {"matrix": [4, 6], "x": 60, "y": 30, "flags": 4},
+ {"matrix": [4, 7], "x": 70, "y": 30, "flags": 4},
+ {"matrix": [4, 8], "x": 80, "y": 30, "flags": 4},
+ {"matrix": [4, 9], "x": 90, "y": 30, "flags": 4},
+ {"matrix": [4, 10], "x": 100, "y": 30, "flags": 4},
+ {"matrix": [4, 11], "x": 110, "y": 30, "flags": 4},
+ {"matrix": [4, 13], "x": 120, "y": 30, "flags": 4},
+ {"matrix": [4, 15], "x": 130, "y": 30, "flags": 4},
+ {"matrix": [2, 14], "x": 140, "y": 30, "flags": 4},
+
+ {"matrix": [5, 0], "x": 0, "y": 40, "flags": 4},
+ {"matrix": [5, 1], "x": 12.5, "y": 40, "flags": 4},
+ {"matrix": [5, 2], "x": 25, "y": 40, "flags": 4},
+ {"matrix": [5, 6], "x": 65.5, "y": 40, "flags": 4},
+ {"matrix": [5, 9], "x": 100, "y": 40, "flags": 4},
+ {"matrix": [5, 10], "x": 110, "y": 40, "flags": 4},
+ {"matrix": [5, 14], "x": 120, "y": 40, "flags": 4},
+ {"matrix": [5, 15], "x": 130, "y": 40, "flags": 4},
+ {"matrix": [5, 16], "x": 140, "y": 40, "flags": 4},
+
+
+ ]
+ },
+ "layouts": {
+ "LAYOUT_60_ansi": {
+ "layout": [
+ {"label": "Esc", "matrix": [0, 0], "x": 0, "y": 0},
+ {"label": "!1", "matrix": [1, 1], "x": 1, "y": 0},
+ {"label": "@2", "matrix": [1, 2], "x": 2, "y": 0},
+ {"label": "#3", "matrix": [1, 3], "x": 3, "y": 0},
+ {"label": "$4", "matrix": [1, 4], "x": 4, "y": 0},
+ {"label": "%5", "matrix": [1, 5], "x": 5, "y": 0},
+ {"label": "^6", "matrix": [1, 6], "x": 6, "y": 0},
+ {"label": "&7", "matrix": [1, 7], "x": 7, "y": 0},
+ {"label": "*8", "matrix": [1, 8], "x": 8, "y": 0},
+ {"label": "(9", "matrix": [1, 9], "x": 9, "y": 0},
+ {"label": ")0", "matrix": [1, 10], "x": 10, "y": 0},
+ {"label": "_-", "matrix": [1, 11], "x": 11, "y": 0},
+ {"label": "+=", "matrix": [1, 12], "x": 12, "y": 0},
+ {"label": "Backsp", "matrix": [1, 13], "x": 13, "y": 0, "w": 2},
+
+ {"label": "Tab", "matrix": [2, 0], "x": 0, "y": 1, "w": 1.5},
+ {"label": "Q", "matrix": [2, 1], "x": 1.5, "y": 1},
+ {"label": "W", "matrix": [2, 2], "x": 2.5, "y": 1},
+ {"label": "E", "matrix": [2, 3], "x": 3.5, "y": 1},
+ {"label": "R", "matrix": [2, 4], "x": 4.5, "y": 1},
+ {"label": "T", "matrix": [2, 5], "x": 5.5, "y": 1},
+ {"label": "Y", "matrix": [2, 6], "x": 6.5, "y": 1},
+ {"label": "U", "matrix": [2, 7], "x": 7.5, "y": 1},
+ {"label": "I", "matrix": [2, 8], "x": 8.5, "y": 1},
+ {"label": "O", "matrix": [2, 9], "x": 9.5, "y": 1},
+ {"label": "P", "matrix": [2, 10], "x": 10.5, "y": 1},
+ {"label": "{[", "matrix": [2, 11], "x": 11.5, "y": 1},
+ {"label": "}]", "matrix": [2, 12], "x": 12.5, "y": 1},
+ {"label": "|\\", "matrix": [2, 13], "x": 13.5, "y": 1, "w": 1.5},
+
+ {"label": "Caps", "matrix": [3, 0], "x": 0, "y": 2, "w": 1.75},
+ {"label": "A", "matrix": [3, 1], "x": 1.75, "y": 2},
+ {"label": "S", "matrix": [3, 2], "x": 2.75, "y": 2},
+ {"label": "D", "matrix": [3, 3], "x": 3.75, "y": 2},
+ {"label": "F", "matrix": [3, 4], "x": 4.75, "y": 2},
+ {"label": "G", "matrix": [3, 5], "x": 5.75, "y": 2},
+ {"label": "H", "matrix": [3, 6], "x": 6.75, "y": 2},
+ {"label": "J", "matrix": [3, 7], "x": 7.75, "y": 2},
+ {"label": "K", "matrix": [3, 8], "x": 8.75, "y": 2},
+ {"label": "L", "matrix": [3, 9], "x": 9.75, "y": 2},
+ {"label": ":", "matrix": [3, 10], "x": 10.75, "y": 2},
+ {"label": "\"", "matrix": [3, 11], "x": 11.75, "y": 2},
+ {"label": "Enter", "matrix": [3, 13], "x": 12.75, "y": 2, "w": 2.25},
+
+ {"label": "Shift", "matrix": [4, 0], "x": 0, "y": 3, "w": 2},
+ {"label": "Z", "matrix": [4, 2], "x": 2, "y": 3},
+ {"label": "X", "matrix": [4, 3], "x": 3, "y": 3},
+ {"label": "C", "matrix": [4, 4], "x": 4, "y": 3},
+ {"label": "V", "matrix": [4, 5], "x": 5, "y": 3},
+ {"label": "B", "matrix": [4, 6], "x": 6, "y": 3},
+ {"label": "N", "matrix": [4, 7], "x": 7, "y": 3},
+ {"label": "M", "matrix": [4, 8], "x": 8, "y": 3},
+ {"label": "<,", "matrix": [4, 9], "x": 9, "y": 3},
+ {"label": ">.", "matrix": [4, 10], "x": 10, "y": 3},
+ {"label": "?/", "matrix": [4, 11], "x": 11, "y": 3},
+ {"label": "Shift", "matrix": [4, 13], "x": 12, "y": 3},
+ {"label": "↑", "matrix": [4, 15], "x": 13, "y": 3},
+ {"label": "Del", "matrix": [2, 14], "x": 14, "y": 3},
+
+ {"label": "Ctrl", "matrix": [5, 0], "x": 0, "y": 4, "w": 1.25},
+ {"label": "Opt", "matrix": [5, 1], "x": 1.25, "y": 4, "w": 1.25},
+ {"label": "Cmd", "matrix": [5, 2], "x": 2.5, "y": 4, "w": 1.25},
+ {"label": "Space", "matrix": [5, 6], "x": 3.75, "y": 4, "w": 6.25},
+ {"label": "Cmd", "matrix": [5, 9], "x": 10, "y": 4},
+ {"label": "Fn", "matrix": [5, 10], "x": 11, "y": 4},
+ {"label": "←", "matrix": [5, 14], "x": 12, "y": 4},
+ {"label": "↓", "matrix": [5, 15], "x": 13, "y": 4},
+ {"label": "→", "matrix": [5, 16], "x": 14, "y": 4}
+
+ ]
+ }
+ }
+}
+
diff --git a/keyboards/nuphy/air60_v2/ansi/keymaps/nuphy-air60-v2-via_custom_v2.json b/keyboards/nuphy/air60_v2/ansi/keymaps/nuphy-air60-v2-via_custom_v2.json
new file mode 100644
index 000000000000..8bdd8ef50404
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/keymaps/nuphy-air60-v2-via_custom_v2.json
@@ -0,0 +1,253 @@
+{
+ "name": "NuPhy Air60 V2",
+ "vendorId": "0x19F5",
+ "productId": "0x3265",
+ "matrix": {
+ "rows": 6,
+ "cols": 17
+ },
+ "layouts": {
+ "keymap": [
+ ["0,0","1,1","1,2","1,3","1,4","1,5","1,6","1,7","1,8","1,9","1,10","1,11","1,12",{"w":2},"1,13"],
+ [{"w":1.5},"2,0","2,1","2,2","2,3","2,4","2,5","2,6","2,7","2,8","2,9","2,10","2,11","2,12",{"w":1.5},"2,13"],
+ [{"w": 1.75},"3,0","3,1","3,2","3,3","3,4","3,5","3,6","3,7","3,8","3,9","3,10","3,11",{"w":2.25},"3,13"],
+ [{"w":2},"4,0","4,2","4,3","4,4","4,5","4,6","4,7","4,8","4,9","4,10","4,11","4,13","4,15","2,14"],
+ [{"w":1.25},"5,0",{"w":1.25},"5,1",{"w":1.25},"5,2",{"w":6.25},"5,6","5,9","5,10","5,14","5,15","5,16"]
+ ]
+ },
+ "menus": [
+ {
+ "label": "Lighting",
+ "content": [
+ {
+ "label": "Backlight",
+ "content": [
+ {
+ "label": "Brightness",
+ "type": "range",
+ "options": [0, 255],
+ "content": ["id_qmk_rgb_matrix_brightness", 3, 1]
+ },
+ {
+ "label": "Effect",
+ "type": "dropdown",
+ "content": ["id_qmk_rgb_matrix_effect", 3, 2],
+ "options": [
+ "All Off",
+ "Solid Color",
+ "Gradient Up/Down",
+ "Gradient Left/Right",
+ "Breathing",
+ "Band Sat.",
+ "Band Val.",
+ "Pinwheel Sat.",
+ "Pinwheel Val.",
+ "Spiral Sat.",
+ "Spiral Val.",
+ "Cycle All",
+ "Cycle Left/Right",
+ "Cycle Up/Down",
+ "Rainbow Moving Chevron",
+ "Cycle Out/In",
+ "Cycle Out/In Dual",
+ "Cycle Pinwheel",
+ "Cycle Spiral",
+ "Dual Beacon",
+ "Rainbow Beacon",
+ "Rainbow Pinwheels",
+ "Flower Blooming",
+ "Raindrops",
+ "Jellybean Raindrops",
+ "Hue Breathing",
+ "Hue Pendulum",
+ "Hue Wave",
+ "Typing Heatmap",
+ "Digital Rain",
+ "Reactive Simple",
+ "Reactive",
+ "Reactive Wide",
+ "Reactive Multiwide",
+ "Reactive Cross",
+ "Reactive Multicross",
+ "Reactive Nexus",
+ "Reactive MultiNexus",
+ "Splash",
+ "MultiSplash",
+ "Solid Splash",
+ "Solid MultiSplash"
+ ]
+ },
+ {
+ "showIf": "{id_qmk_rgb_matrix_effect} != 0",
+ "label": "Effect Speed",
+ "type": "range",
+ "options": [0, 255],
+ "content": ["id_qmk_rgb_matrix_effect_speed", 3, 3]
+ },
+ {
+ "showIf": "{id_qmk_rgb_matrix_effect} != 0",
+ "label": "Color",
+ "type": "color",
+ "content": ["id_qmk_rgb_matrix_color", 3, 4]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+"keycodes": [
+ "qmk_lighting"
+],
+"customKeycodes": [
+ {
+ "name": "RF\nDFU",
+ "title": "RF DFU",
+ "shortName": "RF_DFU"
+ },
+ {
+ "name": "Link\nUSB",
+ "title": "Link USB",
+ "shortName": "LnkUSB"
+ },
+ {
+ "name": "Link\nRF",
+ "title": "Link RF",
+ "shortName": "LnkRF"
+ },
+ {
+ "name": "Link\nBLE_1",
+ "title": "Link BLE_1",
+ "shortName": "LnkBLE1"
+ },
+ {
+ "name": "Link\nBLE_2",
+ "title": "Link BLE_2",
+ "shortName": "LnkBLE2"
+ },
+ {
+ "name": "Link\nBLE_3",
+ "title": "Link BLE_3",
+ "shortName": "LnkBLE3"
+ },
+ {
+ "name": "Mac\nVoice",
+ "title": "Mac Voice | F5",
+ "shortName": "Voice"
+ },
+ {
+ "name": "Mac\nDND",
+ "title": "Mac DND",
+ "shortName": "Dnd"
+ },
+ {
+ "name": "Task",
+ "title": "Mac Task | Calc",
+ "shortName": "Task"
+ },
+ {
+ "name": "Search",
+ "title": "Search",
+ "shortName": "Search"
+ },
+ {
+ "name": "Print\n Whole",
+ "title": "Print Whole | PrintScreen",
+ "shortName": "PrtScr"
+ },
+ {
+ "name": "Print\n Area",
+ "title": "Print Area | PrintScreen",
+ "shortName": "PrtAr"
+ },
+ {
+ "name": "Side\nLight+",
+ "title": "Side Light +",
+ "shortName": "Sd_L+"
+ },
+ {
+ "name": "Side\nLight-",
+ "title": "Side Light -",
+ "shortName": "Sd_L-"
+ },
+ {
+ "name": "Side\nMode",
+ "title": "Side Next Mode",
+ "shortName": "Sd_Md"
+ },
+ {
+ "name": "Side\nColor",
+ "title": "Side Next Color",
+ "shortName": "Sd_Col"
+ },
+ {
+ "name": "Side\nFast",
+ "title": "Side Speed +",
+ "shortName": "Sd_Fst"
+ },
+ {
+ "name": "Side\nSlow",
+ "title": "Side Speed -",
+ "shortName": "Sd_Slw"
+ },
+ {
+ "name": "Side\nOne",
+ "title": "Show One Side",
+ "shortName": "Sd_One"
+ },
+ {
+ "name": "Dev\nReset",
+ "title": "Device Reset",
+ "shortName": "DvRst"
+ },
+ {
+ "name": "Sleep\nMode",
+ "title": "Sleep Mode",
+ "shortName": "Sleep"
+ },
+ {
+ "name": "Bat\nShow",
+ "title": "Battery Show",
+ "shortName": "Bat_Show"
+ },
+ {
+ "name": "Bat\nNumeric",
+ "title": "Battery Show Numeric",
+ "shortName": "Bat_Nr"
+ },
+ {
+ "name": "RGB\nTest",
+ "title": "RGB Test",
+ "shortName": "RGB_Tst"
+ },
+ {
+ "name": "NumLock\nIns",
+ "title": "NumLock on hold | Ins",
+ "shortName": "Num_Ins"
+ },
+ {
+ "name": "NumLock\nIndicator",
+ "title": "NumLock Indicator",
+ "shortName": "NumlockInd"
+ },
+ {
+ "name": "Sleep\nTrigger",
+ "title": "Trigger Sleep",
+ "shortName": "Sleep_Trg"
+ },
+ {
+ "name": "Game\nMode",
+ "title": "Trigger Game Mode",
+ "shortName": "Game_Mode"
+ },
+ {
+ "name": "Caps_Word\nTrigger",
+ "title": "Trigger Caps_Word",
+ "shortName": "Caps_Word"
+ },
+ {
+ "name": "Win_Lock\nTrigger",
+ "title": "Win Lock",
+ "shortName": "Win_Lock"
+ }
+ ]
+}
diff --git a/keyboards/nuphy/air60_v2/ansi/keymaps/via/keymap.c b/keyboards/nuphy/air60_v2/ansi/keymaps/via/keymap.c
new file mode 100644
index 000000000000..acfcc850c870
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/keymaps/via/keymap.c
@@ -0,0 +1,167 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#include QMK_KEYBOARD_H
+
+enum layers{
+ MAC_BASE,
+ MAC_FN,
+ MAC_FN_SFT,
+ WIN_BASE,
+ WIN_FN,
+ WIN_FN_SFT,
+ M_LAYER
+};
+
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
+// layer 0 Mac
+[MAC_BASE] = LAYOUT_60_ansi(
+ KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC,
+ KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS,
+ KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT,
+ KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_DEL,
+ KC_LCTL, KC_LALT, KC_LGUI, KC_SPC, KC_RGUI, MO(1), KC_LEFT, KC_DOWN, KC_RGHT),
+
+// layer 1 Mac fn
+[MAC_FN] = LAYOUT_60_ansi(
+ KC_GRV, KC_BRID, KC_BRIU, TASK, SEARCH, MAC_VOICE, MAC_DND, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______,
+ _______, LNK_BLE1, LNK_BLE2, LNK_BLE3, LNK_RF, _______, _______, _______, _______, _______, _______, DEV_RESET, SLEEP_MODE, BAT_SHOW,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ MO(2), _______, _______, _______, _______, _______, _______, MO(6), RGB_SPD, RGB_SPI, _______, MO(2), RGB_VAI, _______,
+ _______, _______, _______, _______, _______, _______, RGB_MOD, RGB_VAD, RGB_HUI),
+
+// layer 2 Mac Fn+shift
+[MAC_FN_SFT] = LAYOUT_60_ansi(
+ _______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, RGB_TEST, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______),
+
+// layer 3 Win
+[WIN_BASE] = LAYOUT_60_ansi(
+ KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC,
+ KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS,
+ KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT,
+ KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_DEL,
+ KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(4), KC_LEFT, KC_DOWN, KC_RGHT),
+
+// layer 4 win fn
+[WIN_FN] = LAYOUT_60_ansi(
+ KC_GRV, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______,
+ _______, LNK_BLE1, LNK_BLE2, LNK_BLE3, LNK_RF, _______, _______, _______, _______, _______, _______, DEV_RESET, SLEEP_MODE, BAT_SHOW,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ MO(5), _______, _______, _______, _______, _______, _______, MO(6), RGB_SPD, RGB_SPI, _______, MO(5), RGB_VAI, _______,
+ _______, _______, _______, _______, _______, _______, RGB_MOD, RGB_VAD, RGB_HUI),
+
+// layer 5 win fn+shift
+[WIN_FN_SFT] = LAYOUT_60_ansi(
+ _______, KC_BRID, KC_BRIU, _______, _______, _______, _______, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, RGB_TEST, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______),
+
+// layer 6 function
+[M_LAYER] = LAYOUT_60_ansi(
+ QK_REBOOT, DB_TOGG, RF_DFU, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, SLEEP_NOW, _______,
+ CAPS_WORD, _______, _______, _______, GAME_MODE, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, NUMLOCK_IND, _______, _______, SIDE_SPD, SIDE_SPI, _______, _______, SIDE_VAI, _______,
+ _______, WIN_LOCK, _______, _______, _______, _______, SIDE_MOD, SIDE_VAD, SIDE_HUI)
+};
+
+const is31fl3733_led_t PROGMEM g_is31fl3733_leds[RGB_MATRIX_LED_COUNT] = {
+ {0, A_16, B_16, C_16}, // "Esc"
+ {0, A_2, B_2, C_2}, // "!1"
+ {0, A_3, B_3, C_3}, // "@2"
+ {0, A_4, B_4, C_4}, // "#3"
+ {0, A_5, B_5, C_5}, // "$4"
+ {0, A_6, B_6, C_6}, // "%5"
+ {0, A_7, B_7, C_7}, // "^6"
+ {0, A_8, B_8, C_8}, // "&7"
+ {0, A_9, B_9, C_9}, // "*8"
+ {0, A_10, B_10, C_10}, // "(9"
+ {0, A_11, B_11, C_11}, // ")0"
+ {1, D_1, E_1, F_1}, // "_-"
+ {1, D_2, E_2, F_2}, // "+="
+ {1, D_3, E_3, F_3}, // "Backsp"
+ {0, D_1, E_1, F_1}, // "Tab"
+ {0, D_2, E_2, F_2}, // "Q"
+ {0, D_3, E_3, F_3}, // "W"
+ {0, D_4, E_4, F_4}, // "E"
+ {0, D_5, E_5, F_5}, // "R"
+ {0, D_6, E_6, F_6}, // "T"
+ {0, D_7, E_7, F_7}, // "Y"
+ {0, D_8, E_8, F_8}, // "U"
+ {0, D_9, E_9, F_9}, // "I"
+ {0, D_10, E_10, F_10}, // "O"
+ {0, D_11, E_11, F_11}, // "P"
+ {1, G_1, H_1, I_1}, // "{["
+ {1, G_2, H_2, I_2}, // "}]"
+ {1, G_3, H_3, I_3}, // "|\\"
+ {0, G_1, H_1, I_1}, // "Caps"
+ {0, G_2, H_2, I_2}, // "A"
+ {0, G_3, H_3, I_3}, // "S"
+ {0, G_4, H_4, I_4}, // "D"
+ {0, G_5, H_5, I_5}, // "F"
+ {0, G_6, H_6, I_6}, // "G"
+ {0, G_7, H_7, I_7}, // "H"
+ {0, G_8, H_8, I_8}, // "J"
+ {0, G_9, H_9, I_9}, // "K"
+ {0, G_10, H_10, I_10}, // "L"
+ {0, G_11, H_11, I_11}, // ":"
+ {1, G_16, H_16, I_16}, // "\""
+ {1, G_14, H_14, I_14}, // "Enter"
+ {0, J_1, K_1, L_1}, // "Shift"
+ {0, J_3, K_3, L_3}, // "Z"
+ {0, J_4, K_4, L_4}, // "X"
+ {0, J_5, K_5, L_5}, // "C"
+ {0, J_6, K_6, L_6}, // "V"
+ {0, J_7, K_7, L_7}, // "B"
+ {0, J_8, K_8, L_8}, // "N"
+ {0, J_9, K_9, L_9}, // "M"
+ {0, J_10, K_10, L_10}, // "<,"
+ {0, J_11, K_11, L_11}, // ">."
+ {1, J_1, K_1, L_1}, // "?/"
+ {1, J_3, K_3, L_3}, // "Shift"
+ {1, J_4, K_4, L_4}, // "↑"
+ {1, G_4, H_4, I_4}, // "Del"
+ {0, J_16, K_16, L_16}, // "Ctrl"
+ {0, J_15, K_15, L_15}, // "Opt"
+ {0, J_14, K_14, L_14}, // "Cmd"
+ {0, J_13, K_13, L_13}, // "Space"
+ {0, J_12, K_12, L_12}, // "Cmd"
+ {1, J_16, K_16, L_16}, // "Fn"
+ {1, J_13, K_13, L_13}, // "←"
+ {1, J_12, K_12, L_12}, // "↓"
+ {1, J_11, K_11, L_11}, // "→"
+
+ {1, A_5, B_5, C_5}, // logo left
+ {1, A_4, B_4, C_4}, //
+ {1, A_3, B_3, C_3}, //
+ {1, A_2, B_2, C_2}, //
+ {1, A_1, B_1, C_1}, //
+
+ {1, A_6, B_6, C_6}, // logo right
+ {1, A_7, B_7, C_7}, //
+ {1, A_8, B_8, C_8}, //
+ {1, A_9, B_9, C_9}, //
+ {1, A_10, B_10, C_10} //
+};
diff --git a/keyboards/nuphy/air60_v2/ansi/keymaps/via/rules.mk b/keyboards/nuphy/air60_v2/ansi/keymaps/via/rules.mk
new file mode 100644
index 000000000000..1e5b99807cb7
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/keymaps/via/rules.mk
@@ -0,0 +1 @@
+VIA_ENABLE = yes
diff --git a/keyboards/nuphy/air60_v2/ansi/matrix.c b/keyboards/nuphy/air60_v2/ansi/matrix.c
new file mode 100644
index 000000000000..106a00ad9acb
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/matrix.c
@@ -0,0 +1,97 @@
+/*
+Copyright 2024 SHVD3x
+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 .
+
+*/
+
+#include
+#include
+#include "wait.h"
+#include "util.h"
+#include "matrix.h"
+#include "debounce.h"
+#include "quantum.h"
+
+#define rowA_bits (PAL_PORT_BIT(PAL_PAD(A0)) | PAL_PORT_BIT(PAL_PAD(A1)) | PAL_PORT_BIT(PAL_PAD(A2)) | PAL_PORT_BIT(PAL_PAD(A3)))
+#define rowC_bits (PAL_PORT_BIT(PAL_PAD(C14)) | PAL_PORT_BIT(PAL_PAD(C15)))
+#define colA_bits (PAL_PORT_BIT(PAL_PAD(A4)) | PAL_PORT_BIT(PAL_PAD(A5)) | PAL_PORT_BIT(PAL_PAD(A6)) | PAL_PORT_BIT(PAL_PAD(A7)) | PAL_PORT_BIT(PAL_PAD(A8)) | PAL_PORT_BIT(PAL_PAD(A9)) | PAL_PORT_BIT(PAL_PAD(A10)) | PAL_PORT_BIT(PAL_PAD(A15)))
+#define colB_bits (PAL_PORT_BIT(PAL_PAD(B0)) | PAL_PORT_BIT(PAL_PAD(B1)) | PAL_PORT_BIT(PAL_PAD(B3)) | PAL_PORT_BIT(PAL_PAD(B10)) | PAL_PORT_BIT(PAL_PAD(B11)) | PAL_PORT_BIT(PAL_PAD(B12)) | PAL_PORT_BIT(PAL_PAD(B13)) | PAL_PORT_BIT(PAL_PAD(B14)) | PAL_PORT_BIT(PAL_PAD(B15)))
+
+#ifndef MATRIX_DEBOUNCE
+# define MATRIX_DEBOUNCE 5
+#endif
+
+/* matrix state(1:on, 0:off) */
+extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
+extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
+
+// matrix code
+// ultra fast read_cols code
+static inline matrix_row_t read_cols(void) {
+ uint16_t portA_pin_state = palReadPort(PAL_PORT(A0));
+ uint16_t portB_pin_state = palReadPort(PAL_PORT(B0));
+ //uint16_t portC_pin_state = palReadPort(PAL_PORT(C0));
+ return ((((portA_pin_state & 0b11110000) ^ 0b11110000) >> 4) | (((portB_pin_state & 0b11) ^ 0b11) << 4) | (((portB_pin_state & 0b1111110000000000) ^ 0b1111110000000000) >> 4) | (((portA_pin_state & 0b11100000000) ^ 0b11100000000) << 4) | ((portA_pin_state & 0b1000000000000000) ^ 0b1000000000000000) | (((portB_pin_state & 0b1000) ^ 0b1000) << 13));
+}
+
+static inline void unselect_rows(void) {
+ palSetPort(PAL_PORT(A0), rowA_bits);
+ palSetPort(PAL_PORT(C0), rowC_bits);
+}
+
+static inline void select_row(uint8_t row) {
+ if (row > 1) {
+ palClearPort(PAL_PORT(A0), PAL_PORT_BIT(row - 2));
+ } else {
+ palClearPort(PAL_PORT(C0), PAL_PORT_BIT(row + 14));
+ }
+}
+
+void matrix_init_custom(void) {
+ // initialize key pins
+ palSetGroupMode(PAL_PORT(A0), rowA_bits, 0U, (PAL_STM32_MODE_OUTPUT | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST));
+ palSetGroupMode(PAL_PORT(C0), rowC_bits, 0U, (PAL_STM32_MODE_OUTPUT | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST));
+ palSetPort(PAL_PORT(A0), rowA_bits);
+ palSetPort(PAL_PORT(C0), rowC_bits);
+ palSetGroupMode(PAL_PORT(A0), colA_bits, 0U, (PAL_STM32_MODE_INPUT | PAL_STM32_PUPDR_PULLUP | PAL_STM32_OSPEED_HIGHEST));
+ palSetGroupMode(PAL_PORT(B0), colB_bits, 0U, (PAL_STM32_MODE_INPUT | PAL_STM32_PUPDR_PULLUP | PAL_STM32_OSPEED_HIGHEST));
+}
+
+// Only need to scan the result into current_matrix, and return changed.
+uint8_t matrix_scan_custom(matrix_row_t current_matrix[]) {
+ bool changed = false;
+
+ // Set col, read rows
+
+ for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
+ uint8_t stable_threshold = MATRIX_DEBOUNCE;
+ while (stable_threshold > 0) { // Wait for all Col signals to go HIGH
+ stable_threshold = ((((palReadPort(PAL_PORT(A0)) & colA_bits) ^ colA_bits) | ((palReadPort(PAL_PORT(B0)) & colB_bits) ^ colB_bits)) == 0) ? (stable_threshold - 1) : MATRIX_DEBOUNCE;
+ }
+
+ select_row(current_row); // set row pin to LOW
+ matrix_output_select_delay();
+
+ matrix_row_t cols = read_cols();
+ unselect_rows(); // set row pin to HIGH
+
+ changed |= (current_matrix[current_row] != cols);
+
+ matrix_output_unselect_delay(current_row, changed); // wait for all Row signals to go HIGH
+
+ current_matrix[current_row] = cols;
+ }
+
+ return changed;
+}
+
diff --git a/keyboards/nuphy/air60_v2/ansi/mcu_pwr.c b/keyboards/nuphy/air60_v2/ansi/mcu_pwr.c
new file mode 100644
index 000000000000..04976a3a85ef
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/mcu_pwr.c
@@ -0,0 +1,443 @@
+/*
+Copyright 2023 NuPhy, Persama (@Persama) & jincao1
+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 .
+*/
+#include "user_kb.h"
+#include "mcu_stm32f0xx.h"
+#include "mcu_pwr.h"
+#include "i2c_master.h"
+#include "hal_usb.h"
+#include "usb_main.h"
+#include "hal_lld.h"
+
+//------------------------------------------------
+
+static bool f_usb_deinit = 0;
+static bool rgb_led_on = 0;
+
+void clear_report_buffer_and_queue(void);
+void clear_report_buffer(void);
+
+#if (MCU_SLEEP_ENABLE)
+
+// Pin definitions
+static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
+static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+
+
+/** ================================================================
+ * @brief Turn off USB
+ *
+ ================================================================*/
+void m_deinit_usb_072(void) {
+ GPIO_InitTypeDef GPIO_InitStructure = {0};
+
+#if (0)
+ // call qmk library to turn off USB
+ void close_usb(void);
+ close_usb();
+#endif
+
+ // Reset USB register
+ RCC_APB1PeriphResetCmd(RCC_APB1RSTR_USBRST, ENABLE);
+ RCC_APB1PeriphResetCmd(RCC_APB1RSTR_USBRST, DISABLE);
+ wait_ms(10);
+
+ // Turn off USB clock
+ RCC_APB1PeriphClockCmd(RCC_APB1ENR_USBEN, DISABLE);
+
+ // GPIO to suspended state
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+ GPIO_Init((GPIO_TypeDef *)GPIOA, &GPIO_InitStructure);
+}
+
+/** ================================================================
+ * @brief Low Power mode
+ *
+ ================================================================*/
+// #include "hal_usb.h"
+// #include "usb_main.h"
+void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex) {
+ uint32_t tmp = 0x00;
+
+ tmp = ((uint32_t)0x0F) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03));
+ SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] &= ~tmp;
+ SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] |= (((uint32_t)EXTI_PortSourceGPIOx) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03)));
+}
+
+// #include "hal_lld.h"
+#define EXTI_PortSourceGPIOA ((uint8_t)0x00)
+#define EXTI_PortSourceGPIOB ((uint8_t)0x01)
+#define EXTI_PortSourceGPIOC ((uint8_t)0x02)
+#define EXTI_PortSourceGPIOD ((uint8_t)0x03)
+
+#endif
+
+/**
+ * @brief Light sleep by powering off LEDs.
+ * @note This is Nuphy's "open sourced" sleep logic. It's not deep sleep.
+ */
+void enter_light_sleep(void) {
+ uart_send_cmd(CMD_SET_CONFIG, 5, 5);
+ if ((dev_info.link_mode == LINK_RF_24 && f_rf_sleep) || dev_info.rf_state != RF_CONNECT) {
+ uart_send_cmd(CMD_SLEEP, 5, 5);
+ }
+
+ led_pwr_sleep_handle();
+ // break_all_key();
+ clear_report_buffer_and_queue();
+}
+
+/**
+ * @brief Enter deep sleep
+ * @note This is Nuphy's un-released logic with some cleanup/refactoring
+ * The MCU is put on a low power mode.
+ */
+void enter_deep_sleep(void) {
+ //------------------------ RF to sleep
+ enter_light_sleep();
+
+#if (MCU_SLEEP_ENABLE)
+ //------------------------ Turn off USB if not used
+ if (dev_info.link_mode != LINK_USB) {
+ f_usb_deinit = 1;
+ m_deinit_usb_072();
+ }
+
+ // Close timer
+ // if (tim6_enabled) TIM_Cmd(TIM6, DISABLE);
+
+ //------------------------ Configure WakeUp Key
+
+ for (uint8_t i = 0; i < MATRIX_COLS; ++i) {
+ gpio_set_pin_output(col_pins[i]);
+ gpio_write_pin_high(col_pins[i]);
+ }
+ for (uint8_t i = 0; i < MATRIX_ROWS; ++i) {
+ gpio_set_pin_input_low(row_pins[i]);
+ }
+
+ // Configure interrupt source - all 5 rows of the keyboard.
+ SYSCFG_EXTILineConfig(EXTI_PORT_R0, EXTI_PIN_R0);
+ SYSCFG_EXTILineConfig(EXTI_PORT_R1, EXTI_PIN_R1);
+ SYSCFG_EXTILineConfig(EXTI_PORT_R2, EXTI_PIN_R2);
+ SYSCFG_EXTILineConfig(EXTI_PORT_R3, EXTI_PIN_R3);
+ SYSCFG_EXTILineConfig(EXTI_PORT_R4, EXTI_PIN_R4);
+ SYSCFG_EXTILineConfig(EXTI_PORT_R5, EXTI_PIN_R5);
+
+ EXTI_InitTypeDef m_exti;
+ EXTI_StructInit(&m_exti);
+ m_exti.EXTI_Line = 0XFFFF; // GPIO 0-15
+ m_exti.EXTI_LineCmd = ENABLE;
+ m_exti.EXTI_Mode = EXTI_Mode_Interrupt;
+ m_exti.EXTI_Trigger = EXTI_Trigger_Falling;
+ EXTI_Init(&m_exti);
+
+ NVIC_InitTypeDef NVIC_InitStructure;
+ NVIC_InitStructure.NVIC_IRQChannel = EXTI4_15_IRQn;
+ NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
+ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
+ NVIC_Init(&NVIC_InitStructure);
+ NVIC_InitStructure.NVIC_IRQChannel = EXTI0_1_IRQn;
+ NVIC_Init(&NVIC_InitStructure);
+ NVIC_InitStructure.NVIC_IRQChannel = EXTI2_3_IRQn;
+ NVIC_Init(&NVIC_InitStructure);
+
+
+ // led_pwr_sleep_handle();
+
+ gpio_set_pin_output(DEV_MODE_PIN);
+ gpio_write_pin_low(DEV_MODE_PIN);
+
+ gpio_set_pin_output(SYS_MODE_PIN);
+ gpio_write_pin_low(SYS_MODE_PIN);
+
+ gpio_set_pin_output(A11);
+ gpio_write_pin_low(A11);
+ gpio_set_pin_output(A12);
+ gpio_write_pin_low(A12);
+
+ // removed because we want to skip the RF module sleep for now
+ /*
+ gpio_set_pin_input_high(NRF_BOOT_PIN);
+
+ gpio_set_pin_output(NRF_WAKEUP_PIN);
+ gpio_write_pin_high(NRF_WAKEUP_PIN);
+ */
+
+ // Enter low power mode and wait for interrupt signal
+ PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
+#endif
+}
+
+/**
+ * @brief Power back up LEDs on exiting light sleep.
+ * @note This is Nuphy's "open sourced" wake logic with some modifications. It's not deep sleep.
+ */
+void exit_light_sleep(bool stm32_init) {
+
+ // Resume normal operations
+ no_act_time = 0;
+ f_rf_sleep = 0;
+ f_wakeup_prepare = 0;
+
+ // Power on LEDs
+ led_pwr_wake_handle();
+
+#if (MCU_SLEEP_ENABLE)
+ // Reinitialize the system clock
+ if (stm32_init) { stm32_clock_init(); }
+#endif
+ // Handshake send to wake RF
+ // uart_send_cmd(CMD_HAND, 0, 1);
+ // uart_send_cmd(CMD_RF_STS_SYSC, 1, 1);
+
+ if (f_usb_deinit || dev_info.link_mode == LINK_USB) {
+ usb_lld_wakeup_host(&USB_DRIVER);
+ restart_usb_driver(&USB_DRIVER);
+ f_usb_deinit = 0;
+ }
+
+}
+
+void matrix_scan_repeat(uint8_t repeat) {
+ do {
+ matrix_scan();
+ __asm__ __volatile__("nop;nop;nop;\n\t" ::: "memory"); //nop nop
+ } while (repeat--);
+}
+
+/**
+ * @brief Wake up from deep sleep
+ * @note This is triggered by an interrupt event.
+ * This is mostly Nuphy's unreleased logic with cleanup/refactoring by me.
+ */
+void exit_deep_sleep(void) {
+
+ // Matrix initialization & Scan
+ // extern void matrix_init_pins(void);
+ // matrix_init_pins();
+ extern void matrix_init_custom(void);
+ matrix_init_custom();
+
+ matrix_scan_repeat(2);
+
+ // m_uart_gpio_set_low_speed();
+
+ // Restore IO to working status
+ gpio_set_pin_input_high(DEV_MODE_PIN); // PC0
+ gpio_set_pin_input_high(SYS_MODE_PIN); // PC1
+
+ /* set RF module boot pin high */
+ // gpio_set_pin_input_high(NRF_BOOT_PIN);
+
+ /* Wake RF module */
+ gpio_set_pin_output(NRF_WAKEUP_PIN);
+ gpio_write_pin_high(NRF_WAKEUP_PIN);
+
+ // Flag for RF state.
+ clear_report_buffer();
+ dev_info.rf_state = RF_LINKING;
+ // dev_info.rf_state = RF_DISCONNECT;
+ rf_disconnect_delay = 0xff;
+ rf_link_show_time = 250;
+ rf_linking_time = 0;
+
+ // wait_us(1);
+
+ exit_light_sleep(true);
+}
+
+void led_pwr_sleep_handle(void) {
+ pwr_led_off();
+}
+
+void led_pwr_wake_handle(void) {
+ pwr_led_on();
+}
+
+void pwr_led_off(void) {
+ if (!rgb_led_on) { return; }
+ // LED power supply off
+ gpio_set_pin_output(DC_BOOST_PIN);
+ gpio_write_pin_low(DC_BOOST_PIN);
+ gpio_set_pin_input(RGB_DRIVER_SDB1);
+ gpio_set_pin_input(RGB_DRIVER_SDB2);
+ rgb_led_on = 0;
+#if !defined(NO_DEBUG)
+ dprint("RGB LED State: OFF\n");
+#endif
+}
+
+void pwr_led_on(void) {
+ if (rgb_led_on) { return; }
+ // LED power supply on
+ gpio_set_pin_output(DC_BOOST_PIN);
+ gpio_write_pin_high(DC_BOOST_PIN);
+ gpio_set_pin_output(RGB_DRIVER_SDB1);
+ gpio_write_pin_high(RGB_DRIVER_SDB1);
+ gpio_set_pin_output(RGB_DRIVER_SDB2);
+ gpio_write_pin_high(RGB_DRIVER_SDB2);
+ rgb_matrix_set_color(RGB_MATRIX_LED_COUNT, 1, 1, 1);
+ rgb_led_on = 1;
+#if !defined(NO_DEBUG)
+ dprint("RGB LED State: ON\n");
+#endif
+}
+
+#if (MCU_SLEEP_ENABLE)
+/* Nuphy's implementations. */
+
+/**
+ * @brief Clears the EXTI's line pending flags.
+ * @param EXTI_Line: specifies the EXTI lines flags to clear.
+ * This parameter can be any combination of EXTI_Linex where x can be (0..19)
+ * @retval None
+ */
+void EXTI_ClearFlag(uint32_t EXTI_Line) {
+ EXTI->PR = EXTI_Line;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+#define STM32_EXTI_0_1_HANDLER Vector54
+OSAL_IRQ_HANDLER(STM32_EXTI_0_1_HANDLER) {
+ EXTI->PR = 0xffff;
+}
+
+#define STM32_EXTI_2_3_HANDLER Vector58
+OSAL_IRQ_HANDLER(STM32_EXTI_2_3_HANDLER) {
+ EXTI->PR = 0xffff;
+}
+
+#define STM32_EXTI_4_15_HANDLER Vector5C
+OSAL_IRQ_HANDLER(STM32_EXTI_4_15_HANDLER) {
+ EXTI->PR = 0xffff;
+}
+
+void EXTI_DeInit(void) {
+ EXTI->IMR = 0x0F940000;
+ EXTI->EMR = 0x00000000;
+ EXTI->RTSR = 0x00000000;
+ EXTI->FTSR = 0x00000000;
+ EXTI->PR = 0x006BFFFF;
+}
+
+void EXTI_Init(EXTI_InitTypeDef *EXTI_InitStruct) {
+ uint32_t tmp = 0;
+
+ tmp = (uint32_t)EXTI_BASE;
+
+ if (EXTI_InitStruct->EXTI_LineCmd != DISABLE) {
+ /* Clear EXTI line configuration */
+ EXTI->IMR &= ~EXTI_InitStruct->EXTI_Line;
+ EXTI->EMR &= ~EXTI_InitStruct->EXTI_Line;
+
+ tmp += EXTI_InitStruct->EXTI_Mode;
+
+ *(__IO uint32_t *)tmp |= EXTI_InitStruct->EXTI_Line;
+
+ /* Clear Rising Falling edge configuration */
+ EXTI->RTSR &= ~EXTI_InitStruct->EXTI_Line;
+ EXTI->FTSR &= ~EXTI_InitStruct->EXTI_Line;
+
+ /* Select the trigger for the selected interrupts */
+ if (EXTI_InitStruct->EXTI_Trigger == EXTI_Trigger_Rising_Falling) {
+ /* Rising Falling edge */
+ EXTI->RTSR |= EXTI_InitStruct->EXTI_Line;
+ EXTI->FTSR |= EXTI_InitStruct->EXTI_Line;
+ } else {
+ tmp = (uint32_t)EXTI_BASE;
+ tmp += EXTI_InitStruct->EXTI_Trigger;
+
+ *(__IO uint32_t *)tmp |= EXTI_InitStruct->EXTI_Line;
+ }
+ } else {
+ tmp += EXTI_InitStruct->EXTI_Mode;
+
+ /* Disable the selected external lines */
+ *(__IO uint32_t *)tmp &= ~EXTI_InitStruct->EXTI_Line;
+ }
+}
+
+/**
+ * @brief Fills each EXTI_InitStruct member with its reset value.
+ * @param EXTI_InitStruct: pointer to a EXTI_InitTypeDef structure which will
+ * be initialized.
+ * @retval None
+ */
+void EXTI_StructInit(EXTI_InitTypeDef *EXTI_InitStruct) {
+ EXTI_InitStruct->EXTI_Line = 0;
+ EXTI_InitStruct->EXTI_Mode = EXTI_Mode_Interrupt;
+ EXTI_InitStruct->EXTI_Trigger = EXTI_Trigger_Falling;
+ EXTI_InitStruct->EXTI_LineCmd = DISABLE;
+}
+
+#endif
+
+#if (0)
+void mcu_timer6_init(void) {
+ NVIC_InitTypeDef NVIC_InitStructure;
+ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
+
+ /* TIM6 clock enable */
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
+
+ /* TIM6 interrupt design */
+ NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
+ NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
+ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
+ NVIC_Init(&NVIC_InitStructure);
+
+ /* Time Basic timer settings */
+ /* This configuration results in 1ms intervals: https://deepbluembedded.com/stm32-timer-interrupt-hal-example-timer-mode-lab/
+ Tout = (ARR + 1)(PSC + 1) / Fclk (48mhz for STM32F072)
+ 0.001 = (1000)(48)/(48_000_000) = 1ms
+ */
+ TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
+ TIM_TimeBaseStructure.TIM_Prescaler = 48 - 1;
+ TIM_TimeBaseStructure.TIM_ClockDivision = 0;
+ TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
+ TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
+
+ TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
+
+ /* TIM6 enable */
+ TIM_Cmd(TIM6, ENABLE);
+
+ tim6_enabled = true;
+}
+
+/*
+ TIM6 handler is triggered by the timer above which is used as an interrupt.
+ This is every 1ms so it effectively puts the CPU sleep mode only for 1s.
+*/
+volatile uint8_t idle_sleep_cnt = 0;
+OSAL_IRQ_HANDLER(STM32_TIM6_HANDLER) {
+ if (TIM_GetFlagStatus(TIM6, TIM_FLAG_Update) != ST_RESET) {
+ TIM_ClearFlag(TIM6, TIM_FLAG_Update);
+ idle_sleep_cnt++;
+ }
+}
+
+// For this to work you need to call m_timer6_init() in ansi.c post kb init user.
+// That enables the STM32_TIM6_HANDLER but I think it runs on the interrupt interval of the TIM6 timer.
+void idle_enter_sleep(void) {
+ TIM6->CNT = 0;
+ idle_sleep_cnt = 0;
+ while (idle_sleep_cnt < 1) {
+ PWR_EnterSleepMode(PWR_SLEEPEntry_WFI);
+ }
+}
+#endif
diff --git a/keyboards/nuphy/air60_v2/ansi/mcu_pwr.h b/keyboards/nuphy/air60_v2/ansi/mcu_pwr.h
new file mode 100644
index 000000000000..b9f42cbe3560
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/mcu_pwr.h
@@ -0,0 +1,108 @@
+/*
+Copyright 2023 NuPhy & jincao1
+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 .
+*/
+
+#pragma once
+
+// key matrix pins
+/*
+#define KROW_0 C14
+#define KROW_1 C15
+#define KROW_2 A0
+#define KROW_3 A1
+#define KROW_4 A2
+#define KROW_5 A3
+*/
+
+#define EXTI_PORT_R0 EXTI_PortSourceGPIOC
+#define EXTI_PORT_R1 EXTI_PortSourceGPIOC
+#define EXTI_PORT_R2 EXTI_PortSourceGPIOA
+#define EXTI_PORT_R3 EXTI_PortSourceGPIOA
+#define EXTI_PORT_R4 EXTI_PortSourceGPIOA
+#define EXTI_PORT_R5 EXTI_PortSourceGPIOA
+
+#define EXTI_PIN_R0 14 // C14
+#define EXTI_PIN_R1 15 // C15
+#define EXTI_PIN_R2 0 // A0
+#define EXTI_PIN_R3 1 // A1
+#define EXTI_PIN_R4 2 // A2
+#define EXTI_PIN_R5 3 // A3
+
+//------------------------
+/*
+#define KCOL_0 A4
+#define KCOL_1 A5
+#define KCOL_2 A6
+#define KCOL_3 A7
+#define KCOL_4 B0
+#define KCOL_5 B1
+#define KCOL_6 B10
+#define KCOL_7 B11
+#define KCOL_8 B12
+#define KCOL_9 B13
+#define KCOL_10 B14
+#define KCOL_11 B15
+#define KCOL_12 A8
+#define KCOL_13 A9
+#define KCOL_14 A10
+#define KCOL_15 A15
+#define KCOL_16 B3
+*/
+
+#define EXTI_PORT_C0 EXTI_PortSourceGPIOA // A4
+#define EXTI_PORT_C1 EXTI_PortSourceGPIOA // A5
+#define EXTI_PORT_C2 EXTI_PortSourceGPIOA // A6
+#define EXTI_PORT_C3 EXTI_PortSourceGPIOA // A7
+#define EXTI_PORT_C4 EXTI_PortSourceGPIOB // B0
+#define EXTI_PORT_C5 EXTI_PortSourceGPIOB // B1
+#define EXTI_PORT_C6 EXTI_PortSourceGPIOB // B10
+#define EXTI_PORT_C7 EXTI_PortSourceGPIOB // B11
+#define EXTI_PORT_C8 EXTI_PortSourceGPIOB // B12
+#define EXTI_PORT_C9 EXTI_PortSourceGPIOB // B13
+#define EXTI_PORT_C10 EXTI_PortSourceGPIOB // B14
+#define EXTI_PORT_C11 EXTI_PortSourceGPIOB // B15
+#define EXTI_PORT_C12 EXTI_PortSourceGPIOA // A8
+#define EXTI_PORT_C13 EXTI_PortSourceGPIOA // A9
+#define EXTI_PORT_C14 EXTI_PortSourceGPIOA // A10
+#define EXTI_PORT_C15 EXTI_PortSourceGPIOA // A15
+#define EXTI_PORT_C16 EXTI_PortSourceGPIOB // B3
+
+#define EXTI_PIN_C0 4 // A4
+#define EXTI_PIN_C1 5 // A5
+#define EXTI_PIN_C2 6 // A6
+#define EXTI_PIN_C3 7 // A7
+#define EXTI_PIN_C4 0 // B0
+#define EXTI_PIN_C5 1 // B1
+#define EXTI_PIN_C6 10 // B10
+#define EXTI_PIN_C7 11 // B11
+#define EXTI_PIN_C8 12 // B12
+#define EXTI_PIN_C9 13 // B13
+#define EXTI_PIN_C10 14 // B14
+#define EXTI_PIN_C11 15 // B15
+#define EXTI_PIN_C12 8 // A8
+#define EXTI_PIN_C13 9 // A9
+#define EXTI_PIN_C14 10 // A10
+#define EXTI_PIN_C15 15 // A15
+#define EXTI_PIN_C16 3 // B3
+
+void enter_light_sleep(void);
+void exit_light_sleep(bool stm32_init);
+void enter_deep_sleep(void);
+void exit_deep_sleep(void);
+
+void pwr_led_off(void);
+void pwr_led_on(void);
+
+void led_pwr_sleep_handle(void);
+void led_pwr_wake_handle(void);
+void m_uart_gpio_set_low_speed(void);
diff --git a/keyboards/nuphy/air60_v2/ansi/mcu_stm32f0xx.h b/keyboards/nuphy/air60_v2/ansi/mcu_stm32f0xx.h
new file mode 100644
index 000000000000..6fc97dd70298
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/mcu_stm32f0xx.h
@@ -0,0 +1,1755 @@
+/*
+Copyright 2023 NuPhy & jincao1
+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 .
+*/
+
+#pragma once
+
+#include "hal.h"
+#include "stm32f0xx.h"
+// ================================================================================
+// ================================================================================
+
+typedef enum { ST_RESET = 0,
+ ST_SET = !ST_RESET } ST_FlagStatus,
+ ST_ITStatus;
+
+//////////////////////////////////////////////////////////////////////////////
+// PWR
+
+/** @defgroup PWR_PVD_detection_level
+ * @{
+ */
+
+#define PWR_PVDLevel_0 PWR_CR_PLS_LEV0
+#define PWR_PVDLevel_1 PWR_CR_PLS_LEV1
+#define PWR_PVDLevel_2 PWR_CR_PLS_LEV2
+#define PWR_PVDLevel_3 PWR_CR_PLS_LEV3
+#define PWR_PVDLevel_4 PWR_CR_PLS_LEV4
+#define PWR_PVDLevel_5 PWR_CR_PLS_LEV5
+#define PWR_PVDLevel_6 PWR_CR_PLS_LEV6
+#define PWR_PVDLevel_7 PWR_CR_PLS_LEV7
+
+#define IS_PWR_PVD_LEVEL(LEVEL) (((LEVEL) == PWR_PVDLevel_0) || ((LEVEL) == PWR_PVDLevel_1) || \
+ ((LEVEL) == PWR_PVDLevel_2) || ((LEVEL) == PWR_PVDLevel_3) || \
+ ((LEVEL) == PWR_PVDLevel_4) || ((LEVEL) == PWR_PVDLevel_5) || \
+ ((LEVEL) == PWR_PVDLevel_6) || ((LEVEL) == PWR_PVDLevel_7))
+/**
+ * @}
+ */
+
+/** @defgroup PWR_WakeUp_Pins
+ * @{
+ */
+
+#define PWR_WakeUpPin_1 PWR_CSR_EWUP1
+#define PWR_WakeUpPin_2 PWR_CSR_EWUP2
+#define IS_PWR_WAKEUP_PIN(PIN) (((PIN) == PWR_WakeUpPin_1) || \
+ ((PIN) == PWR_WakeUpPin_2))
+/**
+ * @}
+ */
+
+/** @defgroup PWR_Regulator_state_is_Sleep_STOP_mode
+ * @{
+ */
+
+#define PWR_Regulator_ON ((uint32_t)0x00000000)
+#define PWR_Regulator_LowPower ((uint32_t)0x00000001)
+#define IS_PWR_REGULATOR(REGULATOR) (((REGULATOR) == PWR_Regulator_ON) || \
+ ((REGULATOR) == PWR_Regulator_LowPower))
+/**
+ * @}
+ */
+
+/** @defgroup PWR_SLEEP_mode_entry
+ * @{
+ */
+
+#define PWR_SLEEPEntry_WFI ((uint8_t)0x01)
+#define PWR_SLEEPEntry_WFE ((uint8_t)0x02)
+#define IS_PWR_SLEEP_ENTRY(ENTRY) (((ENTRY) == PWR_SLEEPEntry_WFI) || ((ENTRY) == PWR_SLEEPEntry_WFE))
+
+/**
+ * @}
+ */
+
+/** @defgroup PWR_STOP_mode_entry
+ * @{
+ */
+
+#define PWR_STOPEntry_WFI ((uint8_t)0x01)
+#define PWR_STOPEntry_WFE ((uint8_t)0x02)
+#define IS_PWR_STOP_ENTRY(ENTRY) (((ENTRY) == PWR_STOPEntry_WFI) || ((ENTRY) == PWR_STOPEntry_WFE))
+
+/**
+ * @}
+ */
+
+/** @defgroup PWR_Flag
+ * @{
+ */
+
+#define PWR_FLAG_WU PWR_CSR_WUF
+#define PWR_FLAG_SB PWR_CSR_SBF
+#define PWR_FLAG_PVDO PWR_CSR_PVDO
+#define PWR_FLAG_VREFINTRDY PWR_CSR_VREFINTRDYF
+
+#define IS_PWR_GET_FLAG(FLAG) (((FLAG) == PWR_FLAG_WU) || ((FLAG) == PWR_FLAG_SB) || \
+ ((FLAG) == PWR_FLAG_PVDO) || ((FLAG) == PWR_FLAG_VREFINTRDY))
+
+#define IS_PWR_CLEAR_FLAG(FLAG) (((FLAG) == PWR_FLAG_WU) || ((FLAG) == PWR_FLAG_SB))
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+/* Function used to set the PWR configuration to the default reset state ******/
+void PWR_DeInit(void);
+
+/* Backup Domain Access function **********************************************/
+void PWR_BackupAccessCmd(FunctionalState NewState);
+
+/* PVD configuration functions ************************************************/
+void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel);
+void PWR_PVDCmd(FunctionalState NewState);
+
+/* WakeUp pins configuration functions ****************************************/
+void PWR_WakeUpPinCmd(uint32_t PWR_WakeUpPin, FunctionalState NewState);
+
+/* Low Power modes configuration functions ************************************/
+void PWR_EnterSleepMode(uint8_t PWR_SLEEPEntry);
+void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry);
+void PWR_EnterSTANDBYMode(void);
+
+/* Flags management functions *************************************************/
+FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG);
+void PWR_ClearFlag(uint32_t PWR_FLAG);
+
+//////////////////////////////////////////////////////////////////////////////
+// EXTI
+/**
+ * @brief EXTI mode enumeration
+ */
+
+typedef enum {
+ EXTI_Mode_Interrupt = 0x00,
+ EXTI_Mode_Event = 0x04
+} EXTIMode_TypeDef;
+
+#define IS_EXTI_MODE(MODE) (((MODE) == EXTI_Mode_Interrupt) || ((MODE) == EXTI_Mode_Event))
+
+/**
+ * @brief EXTI Trigger enumeration
+ */
+
+typedef enum {
+ EXTI_Trigger_Rising = 0x08,
+ EXTI_Trigger_Falling = 0x0C,
+ EXTI_Trigger_Rising_Falling = 0x10
+} EXTITrigger_TypeDef;
+
+#define IS_EXTI_TRIGGER(TRIGGER) (((TRIGGER) == EXTI_Trigger_Rising) || \
+ ((TRIGGER) == EXTI_Trigger_Falling) || \
+ ((TRIGGER) == EXTI_Trigger_Rising_Falling))
+/**
+ * @brief EXTI Init Structure definition
+ */
+
+typedef struct
+{
+ uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled.
+ This parameter can be any combination of @ref EXTI_Lines */
+
+ EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
+ This parameter can be a value of @ref EXTIMode_TypeDef */
+
+ EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
+ This parameter can be a value of @ref EXTIMode_TypeDef */
+
+ FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines.
+ This parameter can be set either to ENABLE or DISABLE */
+} EXTI_InitTypeDef;
+
+/* Exported constants --------------------------------------------------------*/
+
+/** @defgroup EXTI_Exported_Constants
+ * @{
+ */
+/** @defgroup EXTI_Lines
+ * @{
+ */
+
+#define EXTI_Line0 ((uint32_t)0x00000001) /*!< External interrupt line 0 */
+#define EXTI_Line1 ((uint32_t)0x00000002) /*!< External interrupt line 1 */
+#define EXTI_Line2 ((uint32_t)0x00000004) /*!< External interrupt line 2 */
+#define EXTI_Line3 ((uint32_t)0x00000008) /*!< External interrupt line 3 */
+#define EXTI_Line4 ((uint32_t)0x00000010) /*!< External interrupt line 4 */
+#define EXTI_Line5 ((uint32_t)0x00000020) /*!< External interrupt line 5 */
+#define EXTI_Line6 ((uint32_t)0x00000040) /*!< External interrupt line 6 */
+#define EXTI_Line7 ((uint32_t)0x00000080) /*!< External interrupt line 7 */
+#define EXTI_Line8 ((uint32_t)0x00000100) /*!< External interrupt line 8 */
+#define EXTI_Line9 ((uint32_t)0x00000200) /*!< External interrupt line 9 */
+#define EXTI_Line10 ((uint32_t)0x00000400) /*!< External interrupt line 10 */
+#define EXTI_Line11 ((uint32_t)0x00000800) /*!< External interrupt line 11 */
+#define EXTI_Line12 ((uint32_t)0x00001000) /*!< External interrupt line 12 */
+#define EXTI_Line13 ((uint32_t)0x00002000) /*!< External interrupt line 13 */
+#define EXTI_Line14 ((uint32_t)0x00004000) /*!< External interrupt line 14 */
+#define EXTI_Line15 ((uint32_t)0x00008000) /*!< External interrupt line 15 */
+#define EXTI_Line16 ((uint32_t)0x00010000) /*!< External interrupt line 16 \
+ Connected to the PVD Output */
+#define EXTI_Line17 ((uint32_t)0x00020000) /*!< Internal interrupt line 17 \
+ Connected to the RTC Alarm \
+ event */
+#define EXTI_Line19 ((uint32_t)0x00080000) /*!< Internal interrupt line 19 \
+ Connected to the RTC Tamper \
+ and Time Stamp events */
+#define EXTI_Line21 ((uint32_t)0x00200000) /*!< Internal interrupt line 21 \
+ Connected to the Comparator 1 \
+ event */
+#define EXTI_Line22 ((uint32_t)0x00400000) /*!< Internal interrupt line 22 \
+ Connected to the Comparator 2 \
+ event */
+#define EXTI_Line23 ((uint32_t)0x00800000) /*!< Internal interrupt line 23 \
+ Connected to the I2C1 wakeup \
+ event */
+#define EXTI_Line25 ((uint32_t)0x02000000) /*!< Internal interrupt line 25 \
+ Connected to the USART1 wakeup \
+ event */
+#define EXTI_Line27 ((uint32_t)0x08000000) /*!< Internal interrupt line 27 \
+ Connected to the CEC wakeup \
+ event */
+
+#define IS_EXTI_LINE(LINE) ((((LINE) & (uint32_t)0xF5140000) == 0x00) && ((LINE) != (uint16_t)0x00))
+
+#define IS_GET_EXTI_LINE(LINE) (((LINE) == EXTI_Line0) || ((LINE) == EXTI_Line1) || \
+ ((LINE) == EXTI_Line2) || ((LINE) == EXTI_Line3) || \
+ ((LINE) == EXTI_Line4) || ((LINE) == EXTI_Line5) || \
+ ((LINE) == EXTI_Line6) || ((LINE) == EXTI_Line7) || \
+ ((LINE) == EXTI_Line8) || ((LINE) == EXTI_Line9) || \
+ ((LINE) == EXTI_Line10) || ((LINE) == EXTI_Line11) || \
+ ((LINE) == EXTI_Line12) || ((LINE) == EXTI_Line13) || \
+ ((LINE) == EXTI_Line14) || ((LINE) == EXTI_Line15) || \
+ ((LINE) == EXTI_Line16) || ((LINE) == EXTI_Line17) || \
+ ((LINE) == EXTI_Line19) || ((LINE) == EXTI_Line21) || \
+ ((LINE) == EXTI_Line22))
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+/* Function used to set the EXTI configuration to the default reset state *****/
+void EXTI_DeInit(void);
+
+/* Initialization and Configuration functions *********************************/
+void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
+void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
+void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
+
+/* Interrupts and flags management functions **********************************/
+FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
+void EXTI_ClearFlag(uint32_t EXTI_Line);
+ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
+void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
+
+//////////////////////////////////////////////////////////////////////////////
+// RCC
+static __I uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};
+
+#define RCC_AHBPeriph_GPIOA RCC_AHBENR_GPIOAEN
+#define RCC_AHBPeriph_GPIOB RCC_AHBENR_GPIOBEN
+#define RCC_AHBPeriph_GPIOC RCC_AHBENR_GPIOCEN
+#define RCC_AHBPeriph_GPIOD RCC_AHBENR_GPIODEN
+#define RCC_AHBPeriph_GPIOF RCC_AHBENR_GPIOFEN
+#define RCC_AHBPeriph_TS RCC_AHBENR_TSEN
+#define RCC_AHBPeriph_CRC RCC_AHBENR_CRCEN
+#define RCC_AHBPeriph_FLITF RCC_AHBENR_FLITFEN
+#define RCC_AHBPeriph_SRAM RCC_AHBENR_SRAMEN
+#define RCC_AHBPeriph_DMA1 RCC_AHBENR_DMA1EN
+
+#define RCC_APB2Periph_SYSCFG RCC_APB2ENR_SYSCFGEN
+#define RCC_APB2Periph_ADC1 RCC_APB2ENR_ADC1EN
+#define RCC_APB2Periph_TIM1 RCC_APB2ENR_TIM1EN
+#define RCC_APB2Periph_SPI1 RCC_APB2ENR_SPI1EN
+#define RCC_APB2Periph_USART1 RCC_APB2ENR_USART1EN
+#define RCC_APB2Periph_TIM15 RCC_APB2ENR_TIM15EN
+#define RCC_APB2Periph_TIM16 RCC_APB2ENR_TIM16EN
+#define RCC_APB2Periph_TIM17 RCC_APB2ENR_TIM17EN
+#define RCC_APB2Periph_DBGMCU RCC_APB2ENR_DBGMCUEN
+
+#define RCC_APB1Periph_TIM2 RCC_APB1ENR_TIM2EN
+#define RCC_APB1Periph_TIM3 RCC_APB1ENR_TIM3EN
+#define RCC_APB1Periph_TIM6 RCC_APB1ENR_TIM6EN
+#define RCC_APB1Periph_TIM14 RCC_APB1ENR_TIM14EN
+#define RCC_APB1Periph_WWDG RCC_APB1ENR_WWDGEN
+#define RCC_APB1Periph_SPI2 RCC_APB1ENR_SPI2EN
+#define RCC_APB1Periph_USART2 RCC_APB1ENR_USART2EN
+#define RCC_APB1Periph_I2C1 RCC_APB1ENR_I2C1EN
+#define RCC_APB1Periph_I2C2 RCC_APB1ENR_I2C2EN
+#define RCC_APB1Periph_PWR RCC_APB1ENR_PWREN
+#define RCC_APB1Periph_DAC RCC_APB1ENR_DACEN
+#define RCC_APB1Periph_CEC RCC_APB1ENR_CECEN
+
+#define HSI_VALUE ((uint32_t)8000000)
+#define HSE_VALUE ((uint32_t)8000000)
+#define LSE_VALUE ((uint32_t)32768)
+
+#define RCC_CFGR_PLLMULL ((uint32_t)0x003C0000) /*!< PLLMUL[3:0] bits (PLL multiplication factor) */
+// #define RCC_CFGR_PLLSRC ((uint32_t)0x00010000) /*!< PLL entry clock source */
+
+/******************* Bit definition for RCC_CFGR2 register ******************/
+/*!< PREDIV1 configuration */
+#define RCC_CFGR2_PREDIV1 ((uint32_t)0x0000000F) /*!< PREDIV1[3:0] bits */
+#define RCC_CFGR2_PREDIV1_0 ((uint32_t)0x00000001) /*!< Bit 0 */
+#define RCC_CFGR2_PREDIV1_1 ((uint32_t)0x00000002) /*!< Bit 1 */
+#define RCC_CFGR2_PREDIV1_2 ((uint32_t)0x00000004) /*!< Bit 2 */
+#define RCC_CFGR2_PREDIV1_3 ((uint32_t)0x00000008) /*!< Bit 3 */
+
+/******************* Bit definition for RCC_CFGR3 register ******************/
+/*!< USART1 Clock source selection */
+// #define RCC_CFGR3_USART1SW ((uint32_t)0x00000003) /*!< USART1SW[1:0] bits */
+// #define RCC_CFGR3_USART1SW_0 ((uint32_t)0x00000001) /*!< Bit 0 */
+// #define RCC_CFGR3_USART1SW_1 ((uint32_t)0x00000002) /*!< Bit 1 */
+/*!< I2C1 Clock source selection */
+// #define RCC_CFGR3_I2C1SW ((uint32_t)0x00000010) /*!< I2C1SW bits */
+// #define RCC_CFGR3_CECSW ((uint32_t)0x00000040) /*!< CECSW bits */
+#define RCC_CFGR3_ADCSW ((uint32_t)0x00000100) /*!< ADCSW bits */
+
+#define HSI14_VALUE ((uint32_t)14000000) /*!< Value of the Internal High Speed oscillator for ADC in Hz. \
+ The real value may vary depending on the variations \
+ in voltage and temperature. */
+
+typedef struct
+{
+ uint32_t SYSCLK_Frequency;
+ uint32_t HCLK_Frequency;
+ uint32_t PCLK_Frequency;
+ uint32_t ADCCLK_Frequency;
+ uint32_t CECCLK_Frequency;
+ uint32_t I2C1CLK_Frequency;
+ uint32_t USART1CLK_Frequency;
+} RCC_ClocksTypeDef;
+
+/**
+ * @brief Forces or releases AHB peripheral reset.
+ * @param RCC_AHBPeriph: specifies the AHB peripheral to reset.
+ * This parameter can be any combination of the following values:
+ * @arg RCC_AHBPeriph_GPIOA: GPIOA clock
+ * @arg RCC_AHBPeriph_GPIOB: GPIOB clock
+ * @arg RCC_AHBPeriph_GPIOC: GPIOC clock
+ * @arg RCC_AHBPeriph_GPIOD: GPIOD clock
+ * @arg RCC_AHBPeriph_GPIOF: GPIOF clock
+ * @arg RCC_AHBPeriph_TS: TS clock
+ * @param NewState: new state of the specified peripheral reset.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void RCC_AHBPeriphResetCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ RCC->AHBRSTR |= RCC_AHBPeriph;
+ } else {
+ RCC->AHBRSTR &= ~RCC_AHBPeriph;
+ }
+}
+
+/**
+ * @brief Forces or releases High Speed APB (APB2) peripheral reset.
+ * @param RCC_APB2Periph: specifies the APB2 peripheral to reset.
+ * This parameter can be any combination of the following values:
+ * @arg RCC_APB2Periph_SYSCFG: SYSCFG clock
+ * @arg RCC_APB2Periph_ADC1: ADC1 clock
+ * @arg RCC_APB2Periph_TIM1: TIM1 clock
+ * @arg RCC_APB2Periph_SPI1: SPI1 clock
+ * @arg RCC_APB2Periph_USART1: USART1 clock
+ * @arg RCC_APB2Periph_TIM15: TIM15 clock
+ * @arg RCC_APB2Periph_TIM16: TIM16 clock
+ * @arg RCC_APB2Periph_TIM17: TIM17 clock
+ * @arg RCC_APB2Periph_DBGMCU: DBGMCU clock
+ * @param NewState: new state of the specified peripheral reset.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ RCC->APB2RSTR |= RCC_APB2Periph;
+ } else {
+ RCC->APB2RSTR &= ~RCC_APB2Periph;
+ }
+}
+
+/**
+ * @brief Forces or releases Low Speed APB (APB1) peripheral reset.
+ * @param RCC_APB1Periph: specifies the APB1 peripheral to reset.
+ * This parameter can be any combination of the following values:
+ * @arg RCC_APB1Periph_TIM2: TIM2 clock
+ * @arg RCC_APB1Periph_TIM3: TIM3 clock
+ * @arg RCC_APB1Periph_TIM6: TIM6 clock
+ * @arg RCC_APB1Periph_TIM14: TIM14 clock
+ * @arg RCC_APB1Periph_WWDG: WWDG clock
+ * @arg RCC_APB1Periph_SPI2: SPI2 clock
+ * @arg RCC_APB1Periph_USART2: USART2 clock
+ * @arg RCC_APB1Periph_I2C1: I2C1 clock
+ * @arg RCC_APB1Periph_I2C2: I2C2 clock
+ * @arg RCC_APB1Periph_PWR: PWR clock
+ * @arg RCC_APB1Periph_DAC: DAC clock
+ * @arg RCC_APB1Periph_CEC: CEC clock
+ * @param NewState: new state of the specified peripheral clock.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ RCC->APB1RSTR |= RCC_APB1Periph;
+ } else {
+ RCC->APB1RSTR &= ~RCC_APB1Periph;
+ }
+}
+
+/**
+ * @brief Enables or disables the AHB peripheral clock.
+ * @note After reset, the peripheral clock (used for registers read/write access)
+ * is disabled and the application software has to enable this clock before
+ * using it.
+ * @param RCC_AHBPeriph: specifies the AHB peripheral to gates its clock.
+ * This parameter can be any combination of the following values:
+ * @arg RCC_AHBPeriph_GPIOA: GPIOA clock
+ * @arg RCC_AHBPeriph_GPIOB: GPIOB clock
+ * @arg RCC_AHBPeriph_GPIOC: GPIOC clock
+ * @arg RCC_AHBPeriph_GPIOD: GPIOD clock
+ * @arg RCC_AHBPeriph_GPIOF: GPIOF clock
+ * @arg RCC_AHBPeriph_TS: TS clock
+ * @arg RCC_AHBPeriph_CRC: CRC clock
+ * @arg RCC_AHBPeriph_FLITF: (has effect only when the Flash memory is in power down mode)
+ * @arg RCC_AHBPeriph_SRAM: SRAM clock
+ * @arg RCC_AHBPeriph_DMA1: DMA1 clock
+ * @param NewState: new state of the specified peripheral clock.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ RCC->AHBENR |= RCC_AHBPeriph;
+ } else {
+ RCC->AHBENR &= ~RCC_AHBPeriph;
+ }
+}
+
+void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ RCC->APB2ENR |= RCC_APB2Periph;
+ } else {
+ RCC->APB2ENR &= ~RCC_APB2Periph;
+ }
+}
+
+/**
+ * @brief Enables or disables the Low Speed APB (APB1) peripheral clock.
+ * @note After reset, the peripheral clock (used for registers read/write access)
+ * is disabled and the application software has to enable this clock before
+ * using it.
+ * @param RCC_APB1Periph: specifies the APB1 peripheral to gates its clock.
+ * This parameter can be any combination of the following values:
+ * @arg RCC_APB1Periph_TIM2: TIM2 clock
+ * @arg RCC_APB1Periph_TIM3: TIM3 clock
+ * @arg RCC_APB1Periph_TIM6: TIM6 clock
+ * @arg RCC_APB1Periph_TIM14: TIM14 clock
+ * @arg RCC_APB1Periph_WWDG: WWDG clock
+ * @arg RCC_APB1Periph_SPI2: SPI2 clock
+ * @arg RCC_APB1Periph_USART2: USART2 clock
+ * @arg RCC_APB1Periph_I2C1: I2C1 clock
+ * @arg RCC_APB1Periph_I2C2: I2C2 clock
+ * @arg RCC_APB1Periph_PWR: PWR clock
+ * @arg RCC_APB1Periph_DAC: DAC clock
+ * @arg RCC_APB1Periph_CEC: CEC clock
+ * @param NewState: new state of the specified peripheral clock.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ RCC->APB1ENR |= RCC_APB1Periph;
+ } else {
+ RCC->APB1ENR &= ~RCC_APB1Periph;
+ }
+}
+
+/**
+ * @brief Returns the frequencies of the System, AHB and APB busses clocks.
+ * @note The frequency returned by this function is not the real frequency
+ * in the chip. It is calculated based on the predefined constant and
+ * the source selected by RCC_SYSCLKConfig():
+ *
+ * @note If SYSCLK source is HSI, function returns constant HSI_VALUE(*)
+ *
+ * @note If SYSCLK source is HSE, function returns constant HSE_VALUE(**)
+ *
+ * @note If SYSCLK source is PLL, function returns constant HSE_VALUE(**)
+ * or HSI_VALUE(*) multiplied by the PLL factors.
+ *
+ * (*) HSI_VALUE is a constant defined in stm32f0xx.h file (default value
+ * 8 MHz) but the real value may vary depending on the variations
+ * in voltage and temperature, refer to RCC_AdjustHSICalibrationValue().
+ *
+ * (**) HSE_VALUE is a constant defined in stm32f0xx.h file (default value
+ * 8 MHz), user has to ensure that HSE_VALUE is same as the real
+ * frequency of the crystal used. Otherwise, this function may
+ * return wrong result.
+ *
+ * - The result of this function could be not correct when using fractional
+ * value for HSE crystal.
+ *
+ * @param RCC_Clocks: pointer to a RCC_ClocksTypeDef structure which will hold
+ * the clocks frequencies.
+ *
+ * @note This function can be used by the user application to compute the
+ * baudrate for the communication peripherals or configure other parameters.
+ * @note Each time SYSCLK, HCLK and/or PCLK clock changes, this function
+ * must be called to update the structure's field. Otherwise, any
+ * configuration based on this function will be incorrect.
+ *
+ * @retval None
+ */
+void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)
+{
+ uint32_t tmp = 0, pllmull = 0, pllsource = 0, prediv1factor = 0, presc = 0;
+
+ /* Get SYSCLK source -------------------------------------------------------*/
+ tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+ switch (tmp) {
+ case 0x00: /* HSI used as system clock */
+ RCC_Clocks->SYSCLK_Frequency = HSI_VALUE;
+ break;
+ case 0x04: /* HSE used as system clock */
+ RCC_Clocks->SYSCLK_Frequency = HSE_VALUE;
+ break;
+ case 0x08: /* PLL used as system clock */
+ /* Get PLL clock source and multiplication factor ----------------------*/
+ pllmull = RCC->CFGR & RCC_CFGR_PLLMULL;
+ pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
+ pllmull = (pllmull >> 18) + 2;
+
+ if (pllsource == 0x00) {
+ /* HSI oscillator clock divided by 2 selected as PLL clock entry */
+ RCC_Clocks->SYSCLK_Frequency = (HSI_VALUE >> 1) * pllmull;
+ } else {
+ prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1;
+ /* HSE oscillator clock selected as PREDIV1 clock entry */
+ RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE / prediv1factor) * pllmull;
+ }
+ break;
+ default: /* HSI used as system clock */
+ RCC_Clocks->SYSCLK_Frequency = HSI_VALUE;
+ break;
+ }
+ /* Compute HCLK, PCLK clocks frequencies -----------------------------------*/
+ /* Get HCLK prescaler */
+ tmp = RCC->CFGR & RCC_CFGR_HPRE;
+ tmp = tmp >> 4;
+ presc = APBAHBPrescTable[tmp];
+ /* HCLK clock frequency */
+ RCC_Clocks->HCLK_Frequency = RCC_Clocks->SYSCLK_Frequency >> presc;
+
+ /* Get PCLK prescaler */
+ tmp = RCC->CFGR & RCC_CFGR_PPRE;
+ tmp = tmp >> 8;
+ presc = APBAHBPrescTable[tmp];
+ /* PCLK clock frequency */
+ RCC_Clocks->PCLK_Frequency = RCC_Clocks->HCLK_Frequency >> presc;
+
+ /* ADCCLK clock frequency */
+ if ((RCC->CFGR3 & RCC_CFGR3_ADCSW) != RCC_CFGR3_ADCSW) {
+ /* ADC Clock is HSI14 Osc. */
+ RCC_Clocks->ADCCLK_Frequency = HSI14_VALUE;
+ } else {
+ if ((RCC->CFGR & RCC_CFGR_ADCPRE) != RCC_CFGR_ADCPRE) {
+ /* ADC Clock is derived from PCLK/2 */
+ RCC_Clocks->ADCCLK_Frequency = RCC_Clocks->PCLK_Frequency >> 1;
+ } else {
+ /* ADC Clock is derived from PCLK/4 */
+ RCC_Clocks->ADCCLK_Frequency = RCC_Clocks->PCLK_Frequency >> 2;
+ }
+ }
+
+ /* CECCLK clock frequency */
+ if ((RCC->CFGR3 & RCC_CFGR3_CECSW) != RCC_CFGR3_CECSW) {
+ /* CEC Clock is HSI/256 */
+ RCC_Clocks->CECCLK_Frequency = HSI_VALUE / 244;
+ } else {
+ /* CECC Clock is LSE Osc. */
+ RCC_Clocks->CECCLK_Frequency = LSE_VALUE;
+ }
+
+ /* I2C1CLK clock frequency */
+ if ((RCC->CFGR3 & RCC_CFGR3_I2C1SW) != RCC_CFGR3_I2C1SW) {
+ /* I2C1 Clock is HSI Osc. */
+ RCC_Clocks->I2C1CLK_Frequency = HSI_VALUE;
+ } else {
+ /* I2C1 Clock is System Clock */
+ RCC_Clocks->I2C1CLK_Frequency = RCC_Clocks->SYSCLK_Frequency;
+ }
+
+ /* USART1CLK clock frequency */
+ if ((RCC->CFGR3 & RCC_CFGR3_USART1SW) == 0x0) {
+ /* USART1 Clock is PCLK */
+ RCC_Clocks->USART1CLK_Frequency = RCC_Clocks->PCLK_Frequency;
+ } else if ((RCC->CFGR3 & RCC_CFGR3_USART1SW) == RCC_CFGR3_USART1SW_0) {
+ /* USART1 Clock is System Clock */
+ RCC_Clocks->USART1CLK_Frequency = RCC_Clocks->SYSCLK_Frequency;
+ } else if ((RCC->CFGR3 & RCC_CFGR3_USART1SW) == RCC_CFGR3_USART1SW_1) {
+ /* USART1 Clock is LSE Osc. */
+ RCC_Clocks->USART1CLK_Frequency = LSE_VALUE;
+ } else if ((RCC->CFGR3 & RCC_CFGR3_USART1SW) == RCC_CFGR3_USART1SW) {
+ /* USART1 Clock is HSI Osc. */
+ RCC_Clocks->USART1CLK_Frequency = HSI_VALUE;
+ }
+}
+
+#define CR_DS_MASK ((uint32_t)0xFFFFFFFC)
+#define CR_PLS_MASK ((uint32_t)0xFFFFFF1F)
+/**
+ * @brief Enters STOP mode.
+ * @note In Stop mode, all I/O pins keep the same state as in Run mode.
+ * @note When exiting Stop mode by issuing an interrupt or a wakeup event,
+ * the HSI RC oscillator is selected as system clock.
+ * @note When the voltage regulator operates in low power mode, an additional
+ * startup delay is incurred when waking up from Stop mode.
+ * By keeping the internal regulator ON during Stop mode, the consumption
+ * is higher although the startup time is reduced.
+ * @param PWR_Regulator: specifies the regulator state in STOP mode.
+ * This parameter can be one of the following values:
+ * @arg PWR_Regulator_ON: STOP mode with regulator ON
+ * @arg PWR_Regulator_LowPower: STOP mode with regulator in low power mode
+ * @param PWR_STOPEntry: specifies if STOP mode in entered with WFI or WFE instruction.
+ * This parameter can be one of the following values:
+ * @arg PWR_STOPEntry_WFI: enter STOP mode with WFI instruction
+ * @arg PWR_STOPEntry_WFE: enter STOP mode with WFE instruction
+ * @retval None
+ */
+void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)
+{
+ uint32_t tmpreg = 0;
+
+ /* Check the parameters */
+
+ /* Select the regulator state in STOP mode ---------------------------------*/
+ tmpreg = PWR->CR;
+ /* Clear PDDS and LPDSR bits */
+ tmpreg &= CR_DS_MASK;
+
+ /* Set LPDSR bit according to PWR_Regulator value */
+ tmpreg |= PWR_Regulator;
+
+ /* Store the new value */
+ PWR->CR = tmpreg;
+
+ /* Set SLEEPDEEP bit of Cortex-M0 System Control Register */
+ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
+
+ /* Select STOP mode entry --------------------------------------------------*/
+ if (PWR_STOPEntry == PWR_STOPEntry_WFI) {
+ /* Request Wait For Interrupt */
+ __WFI();
+ } else {
+ /* Request Wait For Event */
+ __WFE();
+ }
+ /* Reset SLEEPDEEP bit of Cortex System Control Register */
+ SCB->SCR &= (uint32_t) ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
+}
+
+/**
+ * @brief Enters Sleep mode.
+ * @note In Sleep mode, all I/O pins keep the same state as in Run mode.
+ * @param PWR_SLEEPEntry: specifies if SLEEP mode in entered with WFI or WFE instruction.
+ * This parameter can be one of the following values:
+ * @arg PWR_SLEEPEntry_WFI: enter SLEEP mode with WFI instruction
+ * @arg PWR_SLEEPEntry_WFE: enter SLEEP mode with WFE instruction
+ * @retval None
+ */
+void PWR_EnterSleepMode(uint8_t PWR_SLEEPEntry)
+{
+ /* Clear SLEEPDEEP bit of Cortex-M0 System Control Register */
+ SCB->SCR &= (uint32_t) ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
+
+ /* Select SLEEP mode entry -------------------------------------------------*/
+ if (PWR_SLEEPEntry == PWR_SLEEPEntry_WFI) {
+ /* Request Wait For Interrupt */
+ __WFI();
+ } else {
+ /* Request Wait For Event */
+ __WFE();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// GPIO
+
+/** @defgroup Configuration_Mode_enumeration
+ * @{
+ */
+typedef enum {
+ GPIO_Mode_IN = 0x00, /*!< GPIO Input Mode */
+ GPIO_Mode_OUT = 0x01, /*!< GPIO Output Mode */
+ GPIO_Mode_AF = 0x02, /*!< GPIO Alternate function Mode */
+ GPIO_Mode_AN = 0x03 /*!< GPIO Analog In/Out Mode */
+} GPIOMode_TypeDef;
+
+#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_IN) || ((MODE) == GPIO_Mode_OUT) || \
+ ((MODE) == GPIO_Mode_AF) || ((MODE) == GPIO_Mode_AN))
+/**
+ * @}
+ */
+
+/** @defgroup Output_type_enumeration
+ * @{
+ */
+typedef enum {
+ GPIO_OType_PP = 0x00,
+ GPIO_OType_OD = 0x01
+} GPIOOType_TypeDef;
+
+#define IS_GPIO_OTYPE(OTYPE) (((OTYPE) == GPIO_OType_PP) || ((OTYPE) == GPIO_OType_OD))
+
+/**
+ * @}
+ */
+
+/** @defgroup Output_Maximum_frequency_enumeration
+ * @{
+ */
+typedef enum {
+ GPIO_Speed_Level_1 = 0x01, /*!< Medium Speed */
+ GPIO_Speed_Level_2 = 0x02, /*!< Fast Speed */
+ GPIO_Speed_Level_3 = 0x03 /*!< High Speed */
+} GPIOSpeed_TypeDef;
+
+#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_Level_1) || ((SPEED) == GPIO_Speed_Level_2) || \
+ ((SPEED) == GPIO_Speed_Level_3))
+/**
+ * @}
+ */
+
+/** @defgroup Configuration_Pull-Up_Pull-Down_enumeration
+ * @{
+ */
+typedef enum {
+ GPIO_PuPd_NOPULL = 0x00,
+ GPIO_PuPd_UP = 0x01,
+ GPIO_PuPd_DOWN = 0x02
+} GPIOPuPd_TypeDef;
+
+#define IS_GPIO_PUPD(PUPD) (((PUPD) == GPIO_PuPd_NOPULL) || ((PUPD) == GPIO_PuPd_UP) || \
+ ((PUPD) == GPIO_PuPd_DOWN))
+/**
+ * @}
+ */
+
+/** @defgroup Bit_SET_and_Bit_RESET_enumeration
+ * @{
+ */
+typedef enum {
+ Bit_RESET = 0,
+ Bit_SET
+} BitAction;
+
+/**
+ * @brief GPIO Init structure definition
+ */
+typedef struct
+{
+ uint32_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
+ This parameter can be any value of @ref GPIO_pins_define */
+
+ GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
+ This parameter can be a value of @ref GPIOMode_TypeDef */
+
+ GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
+ This parameter can be a value of @ref GPIOSpeed_TypeDef */
+
+ GPIOOType_TypeDef GPIO_OType; /*!< Specifies the operating output type for the selected pins.
+ This parameter can be a value of @ref GPIOOType_TypeDef */
+
+ GPIOPuPd_TypeDef GPIO_PuPd; /*!< Specifies the operating Pull-up/Pull down for the selected pins.
+ This parameter can be a value of @ref GPIOPuPd_TypeDef */
+} GPIO_InitTypeDef;
+
+/* Exported constants --------------------------------------------------------*/
+
+/** @defgroup GPIO_Exported_Constants
+ * @{
+ */
+
+/** @defgroup GPIO_pins_define
+ * @{
+ */
+#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
+#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
+#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
+#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
+#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
+#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
+#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
+#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
+#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
+#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
+#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
+#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
+#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
+#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
+#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
+#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
+#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
+
+#define IS_GPIO_PIN(PIN) ((PIN) != (uint16_t)0x00)
+
+#define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || \
+ ((PIN) == GPIO_Pin_1) || \
+ ((PIN) == GPIO_Pin_2) || \
+ ((PIN) == GPIO_Pin_3) || \
+ ((PIN) == GPIO_Pin_4) || \
+ ((PIN) == GPIO_Pin_5) || \
+ ((PIN) == GPIO_Pin_6) || \
+ ((PIN) == GPIO_Pin_7) || \
+ ((PIN) == GPIO_Pin_8) || \
+ ((PIN) == GPIO_Pin_9) || \
+ ((PIN) == GPIO_Pin_10) || \
+ ((PIN) == GPIO_Pin_11) || \
+ ((PIN) == GPIO_Pin_12) || \
+ ((PIN) == GPIO_Pin_13) || \
+ ((PIN) == GPIO_Pin_14) || \
+ ((PIN) == GPIO_Pin_15))
+
+/**
+ * @}
+ */
+
+/** @defgroup GPIO_Pin_sources
+ * @{
+ */
+#define GPIO_PinSource0 ((uint8_t)0x00)
+#define GPIO_PinSource1 ((uint8_t)0x01)
+#define GPIO_PinSource2 ((uint8_t)0x02)
+#define GPIO_PinSource3 ((uint8_t)0x03)
+#define GPIO_PinSource4 ((uint8_t)0x04)
+#define GPIO_PinSource5 ((uint8_t)0x05)
+#define GPIO_PinSource6 ((uint8_t)0x06)
+#define GPIO_PinSource7 ((uint8_t)0x07)
+#define GPIO_PinSource8 ((uint8_t)0x08)
+#define GPIO_PinSource9 ((uint8_t)0x09)
+#define GPIO_PinSource10 ((uint8_t)0x0A)
+#define GPIO_PinSource11 ((uint8_t)0x0B)
+#define GPIO_PinSource12 ((uint8_t)0x0C)
+#define GPIO_PinSource13 ((uint8_t)0x0D)
+#define GPIO_PinSource14 ((uint8_t)0x0E)
+#define GPIO_PinSource15 ((uint8_t)0x0F)
+
+#define IS_GPIO_PIN_SOURCE(PINSOURCE) (((PINSOURCE) == GPIO_PinSource0) || \
+ ((PINSOURCE) == GPIO_PinSource1) || \
+ ((PINSOURCE) == GPIO_PinSource2) || \
+ ((PINSOURCE) == GPIO_PinSource3) || \
+ ((PINSOURCE) == GPIO_PinSource4) || \
+ ((PINSOURCE) == GPIO_PinSource5) || \
+ ((PINSOURCE) == GPIO_PinSource6) || \
+ ((PINSOURCE) == GPIO_PinSource7) || \
+ ((PINSOURCE) == GPIO_PinSource8) || \
+ ((PINSOURCE) == GPIO_PinSource9) || \
+ ((PINSOURCE) == GPIO_PinSource10) || \
+ ((PINSOURCE) == GPIO_PinSource11) || \
+ ((PINSOURCE) == GPIO_PinSource12) || \
+ ((PINSOURCE) == GPIO_PinSource13) || \
+ ((PINSOURCE) == GPIO_PinSource14) || \
+ ((PINSOURCE) == GPIO_PinSource15))
+/**
+ * @}
+ */
+
+/** @defgroup GPIO_Alternate_function_selection_define
+ * @{
+ */
+
+/**
+ * @brief AF 0 selection
+ */
+#define GPIO_AF_0 ((uint8_t)0x00) /* WKUP, EVENTOUT, TIM15, SPI1, TIM17, \
+ MCO, SWDAT, SWCLK, TIM14, BOOT, \
+ USART1, CEC, IR_OUT, SPI2 */
+/**
+ * @brief AF 1 selection
+ */
+#define GPIO_AF_1 ((uint8_t)0x01) /* USART2, CEC, Tim3, USART1, USART2, \
+ EVENTOUT, I2C1, I2C2, TIM15 */
+/**
+ * @brief AF 2 selection
+ */
+#define GPIO_AF_2 ((uint8_t)0x02) /* TIM2, TIM1, EVENTOUT, TIM16, TIM17 */
+/**
+ * @brief AF 3 selection
+ */
+#define GPIO_AF_3 ((uint8_t)0x03) /* TS, I2C1, TIM15, EVENTOUT */
+
+/**
+ * @brief AF 4 selection
+ */
+#define GPIO_AF_4 ((uint8_t)0x04) /* TIM14 */
+/**
+ * @brief AF 5 selection
+ */
+#define GPIO_AF_5 ((uint8_t)0x05) /* TIM16, TIM17 */
+
+/**
+ * @brief AF 6 selection
+ */
+#define GPIO_AF_6 ((uint8_t)0x06) /* EVENTOUT */
+/**
+ * @brief AF 7 selection
+ */
+#define GPIO_AF_7 ((uint8_t)0x07) /* COMP1 OUT and COMP2 OUT */
+
+#define IS_GPIO_AF(AF) (((AF) == GPIO_AF_0) || ((AF) == GPIO_AF_1) || \
+ ((AF) == GPIO_AF_2) || ((AF) == GPIO_AF_3) || \
+ ((AF) == GPIO_AF_4) || ((AF) == GPIO_AF_5) || \
+ ((AF) == GPIO_AF_6) || ((AF) == GPIO_AF_7))
+
+/**
+ * @}
+ */
+
+/** @defgroup GPIO_Speed_Legacy
+ * @{
+ */
+
+#define GPIO_Speed_10MHz GPIO_Speed_Level_1 /*!< Fast Speed:10MHz */
+#define GPIO_Speed_2MHz GPIO_Speed_Level_2 /*!< Medium Speed:2MHz */
+#define GPIO_Speed_50MHz GPIO_Speed_Level_3 /*!< High Speed:50MHz */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @brief Deinitializes the GPIOx peripheral registers to their default reset
+ * values.
+ * @param GPIOx: where x can be (A, B, C, D or F) to select the GPIO peripheral.
+ * @retval None
+ */
+void GPIO_DeInit(GPIO_TypeDef* GPIOx)
+{
+ if (GPIOx == (GPIO_TypeDef*)GPIOA) {
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOA, ENABLE);
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOA, DISABLE);
+ } else if (GPIOx == (GPIO_TypeDef*)GPIOB) {
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOB, ENABLE);
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOB, DISABLE);
+ } else if (GPIOx == (GPIO_TypeDef*)GPIOC) {
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOC, ENABLE);
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOC, DISABLE);
+ } else if (GPIOx == (GPIO_TypeDef*)GPIOD) {
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOD, ENABLE);
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOD, DISABLE);
+ } else {
+ if (GPIOx == (GPIO_TypeDef*)GPIOF) {
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOF, ENABLE);
+ RCC_AHBPeriphResetCmd(RCC_AHBPeriph_GPIOF, DISABLE);
+ }
+ }
+}
+
+/**
+ * @brief Initializes the GPIOx peripheral according to the specified
+ * parameters in the GPIO_InitStruct.
+ * @param GPIOx: where x can be (A, B, C, D or F) to select the GPIO peripheral.
+ * @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that contains
+ * the configuration information for the specified GPIO peripheral.
+ * @note The configured pins can be: GPIO_Pin_0 -> GPIO_Pin_15 for GPIOA, GPIOB and GPIOC,
+ * GPIO_Pin_0 -> GPIO_Pin_2 for GPIOD, GPIO_Pin_0 -> GPIO_Pin_3 for GPIOF.
+ * @retval None
+ */
+void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
+{
+ uint32_t pinpos = 0x00, pos = 0x00, currentpin = 0x00;
+
+ /*-------------------------- Configure the port pins -----------------------*/
+ /*-- GPIO Mode Configuration --*/
+ for (pinpos = 0x00; pinpos < 0x10; pinpos++) {
+ pos = ((uint32_t)0x01) << pinpos;
+
+ /* Get the port pins position */
+ currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
+
+ if (currentpin == pos) {
+ if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF)) {
+ /* Speed mode configuration */
+ GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));
+ GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));
+
+ /* Output mode configuration */
+ GPIOx->OTYPER &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos));
+ GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));
+ }
+
+ GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (pinpos * 2));
+
+ GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));
+
+ /* Pull-up Pull down resistor configuration */
+ GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));
+ GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));
+ }
+ }
+}
+
+/**
+ * @brief Fills each GPIO_InitStruct member with its default value.
+ * @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure which will
+ * be initialized.
+ * @retval None
+ */
+void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
+{
+ /* Reset GPIO init structure parameters values */
+ GPIO_InitStruct->GPIO_Pin = GPIO_Pin_All;
+ GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN;
+ GPIO_InitStruct->GPIO_Speed = GPIO_Speed_Level_2;
+ GPIO_InitStruct->GPIO_OType = GPIO_OType_PP;
+ GPIO_InitStruct->GPIO_PuPd = GPIO_PuPd_NOPULL;
+}
+
+/**
+ * @brief Sets the selected data port bits.
+ * @param GPIOx: where x can be (A, B, C, D or F) to select the GPIO peripheral.
+ * @param GPIO_Pin: specifies the port bits to be written.
+ * @note This parameter can be GPIO_Pin_x where x can be:(0..15) for GPIOA,
+ * GPIOB or GPIOC,(0..2) for GPIOD and(0..3) for GPIOF.
+ * @retval None
+ */
+void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
+{
+ GPIOx->BSRR = GPIO_Pin;
+}
+
+/**
+ * @brief Clears the selected data port bits.
+ * @param GPIOx: where x can be (A, B, C, D or F) to select the GPIO peripheral.
+ * @param GPIO_Pin: specifies the port bits to be written.
+ * @note This parameter can be GPIO_Pin_x where x can be: (0..15) for GPIOA,
+ * GPIOB or GPIOC,(0..2) for GPIOD and(0..3) for GPIOF.
+ * @retval None
+ */
+void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
+{
+ GPIOx->BRR = GPIO_Pin;
+}
+
+/**
+ * @brief Sets or clears the selected data port bit.
+ * @param GPIOx: where x can be (A, B, C, D or F) to select the GPIO peripheral.
+ * @param GPIO_Pin: specifies the port bit to be written.
+ * @param BitVal: specifies the value to be written to the selected bit.
+ * This parameter can be one of the BitAction enumeration values:
+ * @arg Bit_RESET: to clear the port pin
+ * @arg Bit_SET: to set the port pin
+ * @note The GPIO_Pin parameter can be GPIO_Pin_x where x can be: (0..15) for GPIOA,
+ * GPIOB or GPIOC,(0..2) for GPIOD and(0..3) for GPIOF.
+ * @retval None
+ */
+void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
+{
+ if (BitVal != Bit_RESET) {
+ GPIOx->BSRR = GPIO_Pin;
+ } else {
+ GPIOx->BRR = GPIO_Pin;
+ }
+}
+
+/**
+ * @brief Writes data to the specified GPIO data port.
+ * @param GPIOx: where x can be (A or B) to select the GPIO peripheral.
+ * @param GPIO_PinSource: specifies the pin for the Alternate function.
+ * This parameter can be GPIO_PinSourcex where x can be (0..15).
+ * @param GPIO_AF: selects the pin to used as Alternate function.
+ * This parameter can be one of the following value:
+ * @arg GPIO_AF_0:WKUP, EVENTOUT, TIM15, SPI1, TIM17,MCO, SWDAT, SWCLK, TIM14,
+ * BOOT,USART1, CEC, IR_OUT, SPI2
+ * @arg GPIO_AF_1:USART2, CEC, Tim3, USART1, USART2,EVENTOUT, I2C1, I2C2, TIM15
+ * @arg GPIO_AF_2:TIM2, TIM1, EVENTOUT, TIM16, TIM17.
+ * @arg GPIO_AF_3:TS, I2C1, TIM15, EVENTOUT
+ * @arg GPIO_AF_4:TIM14.
+ * @arg GPIO_AF_5:TIM16, TIM17.
+ * @arg GPIO_AF_6:EVENTOUT.
+ * @arg GPIO_AF_7:COMP1 OUT, COMP2 OUT
+ * @note The pin should already been configured in Alternate Function mode(AF)
+ * using GPIO_InitStruct->GPIO_Mode = GPIO_Mode_AF
+ * @note Refer to the Alternate function mapping table in the device datasheet
+ * for the detailed mapping of the system and peripherals'alternate
+ * function I/O pins.
+ * @retval None
+ */
+void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF)
+{
+ uint32_t temp = 0x00;
+ uint32_t temp_2 = 0x00;
+
+ temp = ((uint32_t)(GPIO_AF) << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4));
+ GPIOx->AFR[GPIO_PinSource >> 0x03] &= ~((uint32_t)0xF << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4));
+ temp_2 = GPIOx->AFR[GPIO_PinSource >> 0x03] | temp;
+ GPIOx->AFR[GPIO_PinSource >> 0x03] = temp_2;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ????
+typedef struct
+{
+ uint32_t USART_BaudRate; /*!< This member configures the USART communication baud rate.
+ The baud rate is computed using the following formula:
+ - IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))
+ - FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 16) + 0.5 */
+
+ uint16_t USART_WordLength; /*!< Specifies the number of data bits transmitted or received in a frame.
+ This parameter can be a value of @ref USART_Word_Length */
+
+ uint16_t USART_StopBits; /*!< Specifies the number of stop bits transmitted.
+ This parameter can be a value of @ref USART_Stop_Bits */
+
+ uint16_t USART_Parity; /*!< Specifies the parity mode.
+ This parameter can be a value of @ref USART_Parity
+ @note When parity is enabled, the computed parity is inserted
+ at the MSB position of the transmitted data (9th bit when
+ the word length is set to 9 data bits; 8th bit when the
+ word length is set to 8 data bits). */
+
+ uint16_t USART_Mode; /*!< Specifies wether the Receive or Transmit mode is enabled or disabled.
+ This parameter can be a value of @ref USART_Mode */
+
+ uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled
+ or disabled.
+ This parameter can be a value of @ref USART_Hardware_Flow_Control */
+} USART_InitTypeDef;
+
+#define USART_WordLength_8b ((uint32_t)0x00000000)
+
+#define USART_StopBits_1 ((uint32_t)0x00000000)
+#define USART_StopBits_2 ((uint32_t)USART_CR2_STOP_1)
+#define USART_StopBits_1_5 ((uint32_t)USART_CR2_STOP_0 | USART_CR2_STOP_1)
+
+#define USART_Parity_No ((uint32_t)0x00000000)
+#define USART_Parity_Even ((uint32_t)USART_CR1_PCE)
+#define USART_Parity_Odd ((uint32_t)USART_CR1_PCE | USART_CR1_PS)
+
+#define USART_HardwareFlowControl_None ((uint32_t)0x00000000)
+#define USART_HardwareFlowControl_RTS ((uint32_t)USART_CR3_RTSE)
+#define USART_HardwareFlowControl_CTS ((uint32_t)USART_CR3_CTSE)
+#define USART_HardwareFlowControl_RTS_CTS ((uint32_t)USART_CR3_RTSE | USART_CR3_CTSE)
+
+#define USART_Mode_Rx USART_CR1_RE
+#define USART_Mode_Tx USART_CR1_TE
+
+#define USART_FLAG_REACK USART_ISR_REACK
+#define USART_FLAG_TEACK USART_ISR_TEACK
+#define USART_FLAG_WU USART_ISR_WUF
+#define USART_FLAG_RWU USART_ISR_RWU
+#define USART_FLAG_SBK USART_ISR_SBKF
+#define USART_FLAG_CM USART_ISR_CMF
+#define USART_FLAG_BUSY USART_ISR_BUSY
+#define USART_FLAG_ABRF USART_ISR_ABRF
+#define USART_FLAG_ABRE USART_ISR_ABRE
+#define USART_FLAG_EOB USART_ISR_EOBF
+#define USART_FLAG_RTO USART_ISR_RTOF
+#define USART_FLAG_nCTSS USART_ISR_CTS
+#define USART_FLAG_CTS USART_ISR_CTSIF
+#define USART_FLAG_LBD USART_ISR_LBD
+#define USART_FLAG_TXE USART_ISR_TXE
+#define USART_FLAG_TC USART_ISR_TC
+#define USART_FLAG_RXNE USART_ISR_RXNE
+#define USART_FLAG_IDLE USART_ISR_IDLE
+#define USART_FLAG_ORE USART_ISR_ORE
+#define USART_FLAG_NE USART_ISR_NE
+#define USART_FLAG_FE USART_ISR_FE
+#define USART_FLAG_PE USART_ISR_PE
+
+#define USART_IT_WU ((uint32_t)0x00140316)
+#define USART_IT_CM ((uint32_t)0x0011010E)
+#define USART_IT_EOB ((uint32_t)0x000C011B)
+#define USART_IT_RTO ((uint32_t)0x000B011A)
+#define USART_IT_PE ((uint32_t)0x00000108)
+#define USART_IT_TXE ((uint32_t)0x00070107)
+#define USART_IT_TC ((uint32_t)0x00060106)
+#define USART_IT_RXNE ((uint32_t)0x00050105)
+#define USART_IT_IDLE ((uint32_t)0x00040104)
+#define USART_IT_LBD ((uint32_t)0x00080206)
+#define USART_IT_CTS ((uint32_t)0x0009030A)
+#define USART_IT_ERR ((uint32_t)0x00000300)
+#define USART_IT_ORE ((uint32_t)0x00030300)
+#define USART_IT_NE ((uint32_t)0x00020300)
+#define USART_IT_FE ((uint32_t)0x00010300)
+
+/*!< USART CR1 register clear Mask ((~(uint32_t)0xFFFFE6F3)) */
+#define CR1_CLEAR_MASK ((uint32_t)(USART_CR1_M | USART_CR1_PCE | \
+ USART_CR1_PS | USART_CR1_TE | \
+ USART_CR1_RE))
+
+/*!< USART CR2 register clock bits clear Mask ((~(uint32_t)0xFFFFF0FF)) */
+#define CR2_CLOCK_CLEAR_MASK ((uint32_t)(USART_CR2_CLKEN | USART_CR2_CPOL | \
+ USART_CR2_CPHA | USART_CR2_LBCL))
+
+/*!< USART CR3 register clear Mask ((~(uint32_t)0xFFFFFCFF)) */
+#define CR3_CLEAR_MASK ((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE))
+
+/*!< USART Interrupts mask */
+#define IT_MASK ((uint32_t)0x000000FF)
+
+/**
+ * @brief Initializes the USARTx peripheral according to the specified
+ * parameters in the USART_InitStruct .
+ * @param USARTx: where x can be 1 or 2 to select the USART peripheral.
+ * @param USART_InitStruct: pointer to a USART_InitTypeDef structure
+ * that contains the configuration information for the specified USART peripheral.
+ * @retval None
+ */
+void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
+{
+ uint32_t tmpreg = 0, apbclock = 0;
+ uint32_t integerdivider = 0;
+ uint32_t fractionaldivider = 0;
+ RCC_ClocksTypeDef RCC_ClocksStatus;
+
+ /* Disable USART */
+ USARTx->CR1 &= (uint32_t) ~((uint32_t)USART_CR1_UE);
+
+ /*---------------------------- USART CR2 Configuration -----------------------*/
+ tmpreg = USARTx->CR2;
+ /* Clear STOP[13:12] bits */
+ tmpreg &= (uint32_t) ~((uint32_t)USART_CR2_STOP);
+
+ /* Configure the USART Stop Bits, Clock, CPOL, CPHA and LastBit ------------*/
+ /* Set STOP[13:12] bits according to USART_StopBits value */
+ tmpreg |= (uint32_t)USART_InitStruct->USART_StopBits;
+
+ /* Write to USART CR2 */
+ USARTx->CR2 = tmpreg;
+
+ /*---------------------------- USART CR1 Configuration -----------------------*/
+ tmpreg = USARTx->CR1;
+ /* Clear M, PCE, PS, TE and RE bits */
+ tmpreg &= (uint32_t) ~((uint32_t)CR1_CLEAR_MASK);
+
+ /* Configure the USART Word Length, Parity and mode ----------------------- */
+ /* Set the M bits according to USART_WordLength value */
+ /* Set PCE and PS bits according to USART_Parity value */
+ /* Set TE and RE bits according to USART_Mode value */
+ tmpreg |= (uint32_t)USART_InitStruct->USART_WordLength | USART_InitStruct->USART_Parity |
+ USART_InitStruct->USART_Mode;
+
+ /* Write to USART CR1 */
+ USARTx->CR1 = tmpreg;
+
+ /*---------------------------- USART CR3 Configuration -----------------------*/
+ tmpreg = USARTx->CR3;
+ /* Clear CTSE and RTSE bits */
+ tmpreg &= (uint32_t) ~((uint32_t)CR3_CLEAR_MASK);
+
+ /* Configure the USART HFC -------------------------------------------------*/
+ /* Set CTSE and RTSE bits according to USART_HardwareFlowControl value */
+ tmpreg |= USART_InitStruct->USART_HardwareFlowControl;
+
+ /* Write to USART CR3 */
+ USARTx->CR3 = tmpreg;
+
+ /*---------------------------- USART BRR Configuration -----------------------*/
+ /* Configure the USART Baud Rate -------------------------------------------*/
+ RCC_GetClocksFreq(&RCC_ClocksStatus);
+
+ if (USARTx == USART1) {
+ apbclock = RCC_ClocksStatus.USART1CLK_Frequency;
+ } else {
+ apbclock = RCC_ClocksStatus.PCLK_Frequency;
+ }
+ /* Determine the integer part */
+ if ((USARTx->CR1 & USART_CR1_OVER8) != 0) {
+ /* Integer part computing in case Oversampling mode is 8 Samples */
+ integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate)));
+ } else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */
+ {
+ /* Integer part computing in case Oversampling mode is 16 Samples */
+ integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate)));
+ }
+ tmpreg = (integerdivider / 100) << 4;
+
+ /* Determine the fractional part */
+ fractionaldivider = integerdivider - (100 * (tmpreg >> 4));
+
+ /* Implement the fractional part in the register */
+ if ((USARTx->CR1 & USART_CR1_OVER8) != 0) {
+ tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07);
+ } else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */
+ {
+ tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);
+ }
+
+ /* Write to USART BRR */
+ USARTx->BRR = (uint16_t)tmpreg;
+}
+
+/**
+ * @brief Enables or disables the specified USART peripheral.
+ * @param USARTx: where x can be 1 or 2 to select the USART peripheral.
+ * @param NewState: new state of the USARTx peripheral.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ /* Enable the selected USART by setting the UE bit in the CR1 register */
+ USARTx->CR1 |= USART_CR1_UE;
+ } else {
+ /* Disable the selected USART by clearing the UE bit in the CR1 register */
+ USARTx->CR1 &= (uint32_t) ~((uint32_t)USART_CR1_UE);
+ }
+}
+
+/**
+ * @brief Checks whether the specified USART flag is set or not.
+ * @param USARTx: where x can be 1 or 2 to select the USART peripheral.
+ * @param USART_FLAG: specifies the flag to check.
+ * This parameter can be one of the following values:
+ * @arg USART_FLAG_REACK: Receive Enable acknowledge flag.
+ * @arg USART_FLAG_TEACK: Transmit Enable acknowledge flag.
+ * @arg USART_FLAG_WUF: Wake up flag.
+ * @arg USART_FLAG_RWU: Receive Wake up flag.
+ * @arg USART_FLAG_SBK: Send Break flag.
+ * @arg USART_FLAG_CMF: Character match flag.
+ * @arg USART_FLAG_BUSY: Busy flag.
+ * @arg USART_FLAG_ABRF: Auto baud rate flag.
+ * @arg USART_FLAG_ABRE: Auto baud rate error flag.
+ * @arg USART_FLAG_EOBF: End of block flag.
+ * @arg USART_FLAG_RTOF: Receive time out flag.
+ * @arg USART_FLAG_nCTSS: Inverted nCTS input bit status.
+ * @arg USART_FLAG_CTS: CTS Change flag.
+ * @arg USART_FLAG_LBD: LIN Break detection flag.
+ * @arg USART_FLAG_TXE: Transmit data register empty flag.
+ * @arg USART_FLAG_TC: Transmission Complete flag.
+ * @arg USART_FLAG_RXNE: Receive data register not empty flag.
+ * @arg USART_FLAG_IDLE: Idle Line detection flag.
+ * @arg USART_FLAG_ORE: OverRun Error flag.
+ * @arg USART_FLAG_NE: Noise Error flag.
+ * @arg USART_FLAG_FE: Framing Error flag.
+ * @arg USART_FLAG_PE: Parity Error flag.
+ * @retval The new state of USART_FLAG (SET or RESET).
+ */
+ST_FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint32_t USART_FLAG)
+{
+ ST_FlagStatus bitstatus = ST_RESET;
+
+ if ((USARTx->ISR & USART_FLAG) != (uint16_t)ST_RESET) {
+ bitstatus = ST_SET;
+ } else {
+ bitstatus = ST_RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Clears the USARTx's pending flags.
+ * @param USARTx: where x can be 1 or 2 to select the USART peripheral.
+ * @param USART_FLAG: specifies the flag to clear.
+ * This parameter can be any combination of the following values:
+ * @arg USART_FLAG_WUF: Wake up flag.
+ * @arg USART_FLAG_CMF: Character match flag.
+ * @arg USART_FLAG_EOBF: End of block flag.
+ * @arg USART_FLAG_RTOF: Receive time out flag.
+ * @arg USART_FLAG_CTS: CTS Change flag.
+ * @arg USART_FLAG_LBD: LIN Break detection flag.
+ * @arg USART_FLAG_TC: Transmission Complete flag.
+ * @arg USART_FLAG_IDLE: IDLE line detected flag.
+ * @arg USART_FLAG_ORE: OverRun Error flag.
+ * @arg USART_FLAG_NE: Noise Error flag.
+ * @arg USART_FLAG_FE: Framing Error flag.
+ * @arg USART_FLAG_PE: Parity Errorflag.
+ *
+ * @note RXNE pending bit is cleared by a read to the USART_RDR register
+ * (USART_ReceiveData()) or by writing 1 to the RXFRQ in the register
+ * USART_RQR (USART_RequestCmd()).
+ * @note TC flag can be also cleared by software sequence: a read operation
+ * to USART_SR register (USART_GetFlagStatus()) followed by a write
+ * operation to USART_TDR register (USART_SendData()).
+ * @note TXE flag is cleared by a write to the USART_TDR register (USART_SendData())
+ * or by writing 1 to the TXFRQ in the register USART_RQR (USART_RequestCmd()).
+ * @note SBKF flag is cleared by 1 to the SBKRQ in the register USART_RQR
+ * (USART_RequestCmd()).
+ * @retval None
+ */
+void USART_ClearFlag(USART_TypeDef* USARTx, uint32_t USART_FLAG)
+{
+ USARTx->ICR = USART_FLAG;
+}
+
+/**
+ * @brief Enables or disables the specified USART interrupts.
+ * @param USARTx: where x can be 1 or 2 to select the USART peripheral.
+ * @param USART_IT: specifies the USART interrupt sources to be enabled or disabled.
+ * This parameter can be one of the following values:
+ * @arg USART_IT_WU: Wake up interrupt.
+ * @arg USART_IT_CM: Character match interrupt.
+ * @arg USART_IT_EOB: End of block interrupt.
+ * @arg USART_IT_RTO: Receive time out interrupt.
+ * @arg USART_IT_CTS: CTS change interrupt.
+ * @arg USART_IT_LBD: LIN Break detection interrupt.
+ * @arg USART_IT_TXE: Tansmit Data Register empty interrupt.
+ * @arg USART_IT_TC: Transmission complete interrupt.
+ * @arg USART_IT_RXNE: Receive Data register not empty interrupt.
+ * @arg USART_IT_IDLE: Idle line detection interrupt.
+ * @arg USART_IT_PE: Parity Error interrupt.
+ * @arg USART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
+ * @param NewState: new state of the specified USARTx interrupts.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void USART_ITConfig(USART_TypeDef* USARTx, uint32_t USART_IT, FunctionalState NewState)
+{
+ uint32_t usartreg = 0, itpos = 0, itmask = 0;
+ uint32_t usartxbase = 0;
+
+ usartxbase = (uint32_t)USARTx;
+
+ /* Get the USART register index */
+ usartreg = (((uint16_t)USART_IT) >> 0x08);
+
+ /* Get the interrupt position */
+ itpos = USART_IT & IT_MASK;
+ itmask = (((uint32_t)0x01) << itpos);
+
+ if (usartreg == 0x02) /* The IT is in CR2 register */
+ {
+ usartxbase += 0x04;
+ } else if (usartreg == 0x03) /* The IT is in CR3 register */
+ {
+ usartxbase += 0x08;
+ } else /* The IT is in CR1 register */
+ {
+ }
+ if (NewState != DISABLE) {
+ *(__IO uint32_t*)usartxbase |= itmask;
+ } else {
+ *(__IO uint32_t*)usartxbase &= ~itmask;
+ }
+}
+
+/**
+ * @brief Returns the most recent received data by the USARTx peripheral.
+ * @param USARTx: where x can be 1 or 2 to select the USART peripheral.
+ * @retval The received data.
+ */
+uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
+{
+ /* Receive Data */
+ return (uint16_t)(USARTx->RDR & (uint16_t)0x01FF);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// NVIC
+
+typedef struct
+{
+ uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
+ This parameter can be a value of @ref IRQn_Type
+ (For the complete STM32 Devices IRQ Channels list,
+ please refer to stm32f0xx.h file) */
+
+ uint8_t NVIC_IRQChannelPriority; /*!< Specifies the priority level for the IRQ channel specified
+ in NVIC_IRQChannel. This parameter can be a value
+ between 0 and 3. */
+
+ FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
+ will be enabled or disabled.
+ This parameter can be set either to ENABLE or DISABLE */
+} NVIC_InitTypeDef;
+
+/**
+ * @brief Initializes the NVIC peripheral according to the specified
+ * parameters in the NVIC_InitStruct.
+ * @note To configure interrupts priority correctly, the NVIC_PriorityGroupConfig()
+ * function should be called before.
+ * @param NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure that contains
+ * the configuration information for the specified NVIC peripheral.
+ * @retval None
+ */
+void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
+{
+ uint32_t tmppriority = 0x00;
+
+ if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE) {
+ /* Compute the Corresponding IRQ Priority --------------------------------*/
+ tmppriority = NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel >> 0x02];
+ tmppriority &= (uint32_t)(~(((uint32_t)0xFF) << ((NVIC_InitStruct->NVIC_IRQChannel & 0x03) * 8)));
+ tmppriority |= (uint32_t)((((uint32_t)NVIC_InitStruct->NVIC_IRQChannelPriority << 6) & 0xFF) << ((NVIC_InitStruct->NVIC_IRQChannel & 0x03) * 8));
+
+ NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel >> 0x02] = tmppriority;
+
+ /* Enable the Selected IRQ Channels --------------------------------------*/
+ NVIC->ISER[0] = (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
+ } else {
+ /* Disable the Selected IRQ Channels -------------------------------------*/
+ NVIC->ICER[0] = (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// TIMER
+
+#define TIM_PSCReloadMode_Update ((uint16_t)0x0000)
+#define TIM_PSCReloadMode_Immediate ((uint16_t)0x0001)
+
+#define TIM_FLAG_Update ((uint16_t)0x0001)
+#define TIM_FLAG_CC1 ((uint16_t)0x0002)
+#define TIM_FLAG_CC2 ((uint16_t)0x0004)
+#define TIM_FLAG_CC3 ((uint16_t)0x0008)
+#define TIM_FLAG_CC4 ((uint16_t)0x0010)
+#define TIM_FLAG_COM ((uint16_t)0x0020)
+#define TIM_FLAG_Trigger ((uint16_t)0x0040)
+#define TIM_FLAG_Break ((uint16_t)0x0080)
+#define TIM_FLAG_CC1OF ((uint16_t)0x0200)
+#define TIM_FLAG_CC2OF ((uint16_t)0x0400)
+#define TIM_FLAG_CC3OF ((uint16_t)0x0800)
+#define TIM_FLAG_CC4OF ((uint16_t)0x1000)
+
+#define TIM_CounterMode_Up ((uint16_t)0x0000)
+#define TIM_CounterMode_Down ((uint16_t)0x0010)
+#define TIM_CounterMode_CenterAligned1 ((uint16_t)0x0020)
+#define TIM_CounterMode_CenterAligned2 ((uint16_t)0x0040)
+#define TIM_CounterMode_CenterAligned3 ((uint16_t)0x0060)
+
+#define TIM_IT_Update ((uint16_t)0x0001)
+#define TIM_IT_CC1 ((uint16_t)0x0002)
+#define TIM_IT_CC2 ((uint16_t)0x0004)
+#define TIM_IT_CC3 ((uint16_t)0x0008)
+#define TIM_IT_CC4 ((uint16_t)0x0010)
+#define TIM_IT_COM ((uint16_t)0x0020)
+#define TIM_IT_Trigger ((uint16_t)0x0040)
+#define TIM_IT_Break ((uint16_t)0x0080)
+
+typedef struct
+{
+ uint16_t TIM_Prescaler; /*!< Specifies the prescaler value used to divide the TIM clock.
+ This parameter can be a number between 0x0000 and 0xFFFF */
+
+ uint16_t TIM_CounterMode; /*!< Specifies the counter mode.
+ This parameter can be a value of @ref TIM_Counter_Mode */
+
+ uint32_t TIM_Period; /*!< Specifies the period value to be loaded into the active
+ Auto-Reload Register at the next update event.
+ This parameter must be a number between 0x0000 and 0xFFFF. */
+
+ uint16_t TIM_ClockDivision; /*!< Specifies the clock division.
+ This parameter can be a value of @ref TIM_Clock_Division_CKD */
+
+ uint8_t TIM_RepetitionCounter; /*!< Specifies the repetition counter value. Each time the RCR downcounter
+ reaches zero, an update event is generated and counting restarts
+ from the RCR value (N).
+ This means in PWM mode that (N+1) corresponds to:
+ - the number of PWM periods in edge-aligned mode
+ - the number of half PWM period in center-aligned mode
+ This parameter must be a number between 0x00 and 0xFF.
+ @note This parameter is valid only for TIM1. */
+} TIM_TimeBaseInitTypeDef;
+
+/**
+ * @brief Initializes the TIMx Time Base Unit peripheral according to
+ * the specified parameters in the TIM_TimeBaseInitStruct.
+ * @param TIMx: where x can be 1, 2, 3, 6, 14, 15, 16 and 17 to select the TIM
+ * peripheral.
+ * @param TIM_TimeBaseInitStruct: pointer to a TIM_TimeBaseInitTypeDef
+ * structure that contains the configuration information for
+ * the specified TIM peripheral.
+ * @retval None
+ */
+void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
+{
+ uint16_t tmpcr1 = 0;
+
+ tmpcr1 = TIMx->CR1;
+
+ if ((TIMx == TIM1) || (TIMx == TIM2) || (TIMx == TIM3)) {
+ /* Select the Counter Mode */
+ tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
+ tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
+ }
+
+ if (TIMx != TIM6) {
+ /* Set the clock division */
+ tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
+ tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
+ }
+
+ TIMx->CR1 = tmpcr1;
+
+ /* Set the Autoreload value */
+ TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period;
+
+ /* Set the Prescaler value */
+ TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
+
+ if ((TIMx == TIM1) || (TIMx == TIM15) || (TIMx == TIM16) || (TIMx == TIM17)) {
+ /* Set the Repetition Counter value */
+ TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
+ }
+
+ /* Generate an update event to reload the Prescaler and the Repetition counter
+ values immediately */
+ TIMx->EGR = TIM_PSCReloadMode_Immediate;
+}
+
+/**
+ * @brief Enables or disables the specified TIM interrupts.
+ * @param TIMx: where x can be 1, 2, 3, 6, 14, 15, 16 or 17 to select the TIMx peripheral.
+ * @param TIM_IT: specifies the TIM interrupts sources to be enabled or disabled.
+ * This parameter can be any combination of the following values:
+ * @arg TIM_IT_Update: TIM update Interrupt source
+ * @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
+ * @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
+ * @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
+ * @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
+ * @arg TIM_IT_COM: TIM Commutation Interrupt source
+ * @arg TIM_IT_Trigger: TIM Trigger Interrupt source
+ * @arg TIM_IT_Break: TIM Break Interrupt source
+ * @note
+ * - TIM6 can only generate an update interrupt.
+ * - TIM15 can have only TIM_IT_Update, TIM_IT_CC1,
+ * TIM_IT_CC2 or TIM_IT_Trigger.
+ * - TIM14, TIM16 and TIM17 can have TIM_IT_Update or TIM_IT_CC1.
+ * - TIM_IT_Break is used only with TIM1 and TIM15.
+ * - TIM_IT_COM is used only with TIM1, TIM15, TIM16 and TIM17.
+ * @param NewState: new state of the TIM interrupts.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ /* Enable the Interrupt sources */
+ TIMx->DIER |= TIM_IT;
+ } else {
+ /* Disable the Interrupt sources */
+ TIMx->DIER &= (uint16_t)~TIM_IT;
+ }
+}
+
+/**
+ * @brief Checks whether the specified TIM flag is set or not.
+ * @param TIMx: where x can be 1, 2, 3, 6, 14, 15, 16 or 17 to select the TIM peripheral.
+ * @param TIM_FLAG: specifies the flag to check.
+ * This parameter can be one of the following values:
+ * @arg TIM_FLAG_Update: TIM update Flag
+ * @arg TIM_FLAG_CC1: TIM Capture Compare 1 Flag
+ * @arg TIM_FLAG_CC2: TIM Capture Compare 2 Flag
+ * @arg TIM_FLAG_CC3: TIM Capture Compare 3 Flag
+ * @arg TIM_FLAG_CC4: TIM Capture Compare 4 Flag
+ * @arg TIM_FLAG_COM: TIM Commutation Flag
+ * @arg TIM_FLAG_Trigger: TIM Trigger Flag
+ * @arg TIM_FLAG_Break: TIM Break Flag
+ * @arg TIM_FLAG_CC1OF: TIM Capture Compare 1 overcapture Flag
+ * @arg TIM_FLAG_CC2OF: TIM Capture Compare 2 overcapture Flag
+ * @arg TIM_FLAG_CC3OF: TIM Capture Compare 3 overcapture Flag
+ * @arg TIM_FLAG_CC4OF: TIM Capture Compare 4 overcapture Flag
+ * @note
+ * - TIM6 can have only one update flag.
+ * - TIM15 can have only TIM_FLAG_Update, TIM_FLAG_CC1, TIM_FLAG_CC2 or
+ * TIM_FLAG_Trigger.
+ * - TIM14, TIM16 and TIM17 can have TIM_FLAG_Update or TIM_FLAG_CC1.
+ * - TIM_FLAG_Break is used only with TIM1 and TIM15.
+ * - TIM_FLAG_COM is used only with TIM1 TIM15, TIM16 and TIM17.
+ * @retval The new state of TIM_FLAG (SET or RESET).
+ */
+ST_FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
+{
+ ST_FlagStatus bitstatus = ST_RESET;
+
+ if ((TIMx->SR & TIM_FLAG) != (uint16_t)ST_RESET) {
+ bitstatus = ST_SET;
+ } else {
+ bitstatus = ST_RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Clears the TIMx's pending flags.
+ * @param TIMx: where x can be 1, 2, 3, 6, 14, 15, 16 or 17 to select the TIM peripheral.
+ * @param TIM_FLAG: specifies the flag bit to clear.
+ * This parameter can be any combination of the following values:
+ * @arg TIM_FLAG_Update: TIM update Flag
+ * @arg TIM_FLAG_CC1: TIM Capture Compare 1 Flag
+ * @arg TIM_FLAG_CC2: TIM Capture Compare 2 Flag
+ * @arg TIM_FLAG_CC3: TIM Capture Compare 3 Flag
+ * @arg TIM_FLAG_CC4: TIM Capture Compare 4 Flag
+ * @arg TIM_FLAG_COM: TIM Commutation Flag
+ * @arg TIM_FLAG_Trigger: TIM Trigger Flag
+ * @arg TIM_FLAG_Break: TIM Break Flag
+ * @arg TIM_FLAG_CC1OF: TIM Capture Compare 1 overcapture Flag
+ * @arg TIM_FLAG_CC2OF: TIM Capture Compare 2 overcapture Flag
+ * @arg TIM_FLAG_CC3OF: TIM Capture Compare 3 overcapture Flag
+ * @arg TIM_FLAG_CC4OF: TIM Capture Compare 4 overcapture Flag
+ * @note
+ * - TIM6 can have only one update flag.
+ * - TIM15 can have only TIM_FLAG_Update, TIM_FLAG_CC1,TIM_FLAG_CC2 or
+ * TIM_FLAG_Trigger.
+ * - TIM14, TIM16 and TIM17 can have TIM_FLAG_Update or TIM_FLAG_CC1.
+ * - TIM_FLAG_Break is used only with TIM1 and TIM15.
+ * - TIM_FLAG_COM is used only with TIM1, TIM15, TIM16 and TIM17.
+ * @retval None
+ */
+void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
+{
+ /* Clear the flags */
+ TIMx->SR = (uint16_t)~TIM_FLAG;
+}
+
+/**
+ * @brief Enables or disables the specified TIM peripheral.
+ * @param TIMx: where x can be 1, 2, 3, 6, 14, 15, 16 and 17to select the TIMx
+ * peripheral.
+ * @param NewState: new state of the TIMx peripheral.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
+{
+ if (NewState != DISABLE) {
+ /* Enable the TIM Counter */
+ TIMx->CR1 |= TIM_CR1_CEN;
+ } else {
+ /* Disable the TIM Counter */
+ TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
+ }
+}
diff --git a/keyboards/nuphy/air60_v2/ansi/mcuconf.h b/keyboards/nuphy/air60_v2/ansi/mcuconf.h
new file mode 100644
index 000000000000..5ccf0a3fc40f
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/mcuconf.h
@@ -0,0 +1,29 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#pragma once
+
+#include_next
+
+#undef STM32_SERIAL_USE_USART1
+#define STM32_SERIAL_USE_USART1 TRUE
+
+#undef STM32_I2C_USE_I2C1
+#define STM32_I2C_USE_I2C1 TRUE
+
+#undef STM32_I2C_USE_DMA
+#define STM32_I2C_USE_DMA FALSE
diff --git a/keyboards/nuphy/air60_v2/ansi/readme.md b/keyboards/nuphy/air60_v2/ansi/readme.md
new file mode 100644
index 000000000000..f0f0fe2d8afb
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/readme.md
@@ -0,0 +1,30 @@
+# NuPhy Air60 V2
+
+*NuPhy Air60 V2 is a standard 84 key keyboard.*
+![NuPhy Air60 V2](https://i.imgur.com/R7jS2JC.jpeg)
+
+* Keyboard Maintainer: [nuphy](https://github.com/nuphy-src)
+* Hardware Supported: NuPhy Air60 V2 PCB
+* Hardware Availability: Private
+
+Make example for this keyboard (after setting up your build environment):
+
+ make nuphy/air60_v2/ansi:via
+
+Flashing example for this keyboard:
+
+ make nuphy/air60_v2/ansi:via:flash
+
+See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
+
+## Bootloader
+
+Enter the bootloader:
+
+* **Bootmagic reset**: Hold down the key at (0,0) in the matrix (usually the top left key or Escape) and plug in the keyboard
+
+* **Hardware reset**: Remove the capslock keycap, hold the little button beneath and plug in the keyboard.
+
+## Customizations and Fixes
+
+* [customizations.md](customizations.md)
diff --git a/keyboards/nuphy/air60_v2/ansi/rf.c b/keyboards/nuphy/air60_v2/ansi/rf.c
new file mode 100644
index 000000000000..c40496562b93
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/rf.c
@@ -0,0 +1,618 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#include "user_kb.h"
+#include "uart.h" // qmk uart.h
+#include "ansi.h"
+#include "rf_queue.h"
+
+USART_MGR_STRUCT Usart_Mgr;
+#define RX_SBYTE Usart_Mgr.RXDBuf[0]
+#define RX_CMD Usart_Mgr.RXDBuf[1]
+#define RX_ACK Usart_Mgr.RXDBuf[2]
+#define RX_LEN Usart_Mgr.RXDBuf[3]
+#define RX_DAT Usart_Mgr.RXDBuf[4]
+
+bool f_uart_ack = 0;
+bool f_rf_read_data_ok = 0;
+bool f_rf_sts_sysc_ok = 0;
+bool f_rf_new_adv_ok = 0;
+bool f_rf_reset = 0;
+bool f_rf_hand_ok = 0;
+bool f_goto_sleep = 0;
+bool f_goto_deepsleep = 0;
+bool f_wakeup_prepare = 0;
+bool f_rf_sleep = 0;
+
+uint8_t func_tab[32] = {0};
+uint8_t sync_lost = 0;
+uint8_t rf_disconnect_delay = 0;
+uint32_t uart_rpt_timer = 0;
+
+report_buffer_t byte_report_buff = {0};
+report_buffer_t bit_report_buff = {0};
+
+extern host_driver_t * m_host_driver;
+extern host_driver_t rf_host_driver;
+extern rf_queue_t rf_queue;
+extern uint8_t host_mode;
+extern bool f_send_channel;
+extern bool f_dial_sw_init_ok;
+
+void uart_init(uint32_t baud); // qmk uart.c
+void uart_send_report(uint8_t report_type, uint8_t *report_buf, uint8_t report_size);
+void uart_send_bytes(uint8_t *Buffer, uint32_t Length);
+uint8_t get_checksum(uint8_t *buf, uint8_t len);
+void uart_receive_pro(void);
+void break_all_key(void);
+
+/**
+ * @brief Get variable uart key send repeat interval.
+ */
+static uint8_t get_repeat_interval(void) {
+ uint8_t interval = MAX(byte_report_buff.repeat, bit_report_buff.repeat);
+
+ if (interval < 10) { return 20; }
+ return 50;
+
+}
+
+/**
+ * @brief Reset report buffers and clear the queue
+ */
+void clear_report_buffer(void) {
+ if (byte_report_buff.cmd) { memset(&byte_report_buff.cmd, 0, sizeof(report_buffer_t)); }
+ if (bit_report_buff.cmd) { memset(&bit_report_buff.cmd, 0, sizeof(report_buffer_t)); }
+}
+
+void clear_report_buffer_and_queue(void) {
+ clear_report_buffer();
+ rf_queue.clear();
+}
+
+/**
+ * @brief Repeating reports from queue.
+ */
+void uart_send_repeat_from_queue(void) {
+ static uint32_t dequeue_timer = 0;
+ static report_buffer_t report_buff = {0};
+
+ if (timer_elapsed32(dequeue_timer) > 5 && !rf_queue.is_empty()) { //50
+ rf_queue.dequeue(&report_buff);
+ dequeue_timer = timer_read32();
+ }
+
+ // queue is empty, continue sending from standard process.
+ if (rf_queue.is_empty()) {
+ clear_report_buffer_and_queue();
+ if (report_buff.length > 6) { byte_report_buff = report_buff; }
+ }
+
+ if (report_buff.repeat < 24) {
+ wait_us(1);
+ uart_send_report(report_buff.cmd, report_buff.buffer, report_buff.length);
+ report_buff.repeat++;
+ }
+}
+
+/**
+ * @brief Uart send keys report.
+ * @note Repeats the last sent key reports to reduce stuck keys once every 50ms if no activity.
+ */
+void uart_send_report_repeat(void) {
+ if (dev_info.link_mode == LINK_USB) { return; }
+
+ if (dev_info.rf_state != RF_CONNECT) {
+ if (no_act_time > 600) { clear_report_buffer_and_queue(); }
+ return;
+ }
+
+ // queue is not empty, send from queue.
+ if (!rf_queue.is_empty()) {
+ uart_send_repeat_from_queue();
+ return;
+ }
+
+ if (no_act_time > 200) { return; }
+
+ uint8_t interval = get_repeat_interval();
+
+ if (timer_elapsed32(uart_rpt_timer) >= interval) {
+ if (no_act_time <= 75) { // increments every 10ms, 50 = 500ms
+ if (byte_report_buff.cmd) {
+ uart_send_report(byte_report_buff.cmd, byte_report_buff.buffer, byte_report_buff.length);
+ byte_report_buff.repeat++;
+ if (bit_report_buff.cmd) { wait_us(100); }
+ }
+
+ if (bit_report_buff.cmd) {
+ uart_send_report(bit_report_buff.cmd, bit_report_buff.buffer, bit_report_buff.length);
+ bit_report_buff.repeat++;
+ }
+
+ } else {
+ clear_report_buffer_and_queue();
+ }
+ uart_rpt_timer = timer_read32();
+ }
+}
+
+/**
+ * @brief Parsing the data received from the RF module.
+ */
+void rf_protocol_receive(void) {
+ uint8_t i, check_sum = 0;
+
+ if (Usart_Mgr.RXDState == RX_Done) {
+ sync_lost = 0;
+
+ if (RX_LEN >= UART_MAX_LEN - 4) {
+ Usart_Mgr.RXDState = RX_DATA_ERR;
+ return;
+ } else if (Usart_Mgr.RXDLen > 4) {
+ for (i = 0; i < RX_LEN; i++) {
+ check_sum += Usart_Mgr.RXDBuf[4 + i];
+ }
+ if (check_sum != Usart_Mgr.RXDBuf[4 + i]) {
+ Usart_Mgr.RXDState = RX_SUM_ERR;
+ return;
+ }
+ } else if (Usart_Mgr.RXDLen == 3) {
+ if (Usart_Mgr.RXDBuf[2] == 0xA0) {
+ f_uart_ack = 1;
+ }
+ }
+
+ Usart_Mgr.RXCmd = RX_CMD;
+
+ switch (RX_CMD) {
+ case CMD_HAND: {
+ f_rf_hand_ok = 1;
+ break;
+ }
+
+ case CMD_24G_SUSPEND: {
+ if (!USB_ACTIVE) { f_goto_sleep = 1; }
+ break;
+ }
+
+ case CMD_NEW_ADV: {
+ f_rf_new_adv_ok = 1;
+ break;
+ }
+
+ case CMD_RF_STS_SYSC: {
+ static uint8_t error_cnt = 0;
+ static uint8_t bat_per_debounce = 0;
+
+ if (dev_info.link_mode == Usart_Mgr.RXDBuf[4]) {
+ error_cnt = 0;
+
+ dev_info.rf_state = Usart_Mgr.RXDBuf[5];
+
+ if ((dev_info.rf_state == RF_CONNECT) && ((Usart_Mgr.RXDBuf[6] & 0xf8) == 0)) {
+ dev_info.rf_led = Usart_Mgr.RXDBuf[6];
+ }
+
+ dev_info.rf_charge = Usart_Mgr.RXDBuf[7];
+ if (Usart_Mgr.RXDBuf[8] <= 100 && bat_per_debounce > 4) {
+ dev_info.rf_battery = Usart_Mgr.RXDBuf[8];
+ bat_per_debounce = 0;
+ }
+ bat_per_debounce++;
+ if (dev_info.rf_charge & 0x01) { dev_info.rf_battery = 100; }
+ } else {
+ if (dev_info.rf_state != RF_INVAILD) {
+ if (error_cnt >= 5) {
+ error_cnt = 0;
+ f_send_channel = 1;
+ } else {
+ error_cnt++;
+ }
+ }
+ }
+
+ f_rf_sts_sysc_ok = 1;
+ break;
+ }
+
+ case CMD_READ_DATA: {
+ memcpy(func_tab, &Usart_Mgr.RXDBuf[4], 32);
+
+ if (func_tab[4] <= LINK_USB) {
+ dev_info.link_mode = func_tab[4];
+ }
+
+ if (func_tab[5] < LINK_USB) {
+ dev_info.rf_channel = func_tab[5];
+ }
+
+ if ((func_tab[6] <= LINK_BT_3) && (func_tab[6] >= LINK_BT_1)) {
+ dev_info.ble_channel = func_tab[6];
+ }
+
+ f_rf_read_data_ok = 1;
+ break;
+ }
+ }
+
+ Usart_Mgr.RXDLen = 0;
+ Usart_Mgr.RXDState = RX_Idle;
+ Usart_Mgr.RXDOverTime = 0;
+ }
+}
+
+/**
+ * @brief Uart send cmd.
+ * @param cmd: cmd.
+ * @param wait_ack: wait time for ack after sending.
+ * @param delayms: delay before sending.
+ */
+uint8_t uart_send_cmd(uint8_t cmd, uint8_t wait_ack, uint8_t delayms) {
+ wait_ms(delayms);
+
+ memset(&Usart_Mgr.TXDBuf[0], 0, UART_MAX_LEN);
+
+ Usart_Mgr.TXDBuf[0] = UART_HEAD;
+ Usart_Mgr.TXDBuf[1] = cmd;
+ Usart_Mgr.TXDBuf[2] = 0x00;
+
+ switch (cmd) {
+ case CMD_SLEEP: {
+ Usart_Mgr.TXDBuf[3] = 1;
+ Usart_Mgr.TXDBuf[4] = 0;
+ Usart_Mgr.TXDBuf[5] = 0;
+ break;
+ }
+
+ case CMD_HAND: {
+ Usart_Mgr.TXDBuf[3] = 1;
+ Usart_Mgr.TXDBuf[4] = 0;
+ Usart_Mgr.TXDBuf[5] = 0;
+ break;
+ }
+
+ case CMD_RF_STS_SYSC: {
+ Usart_Mgr.TXDBuf[3] = 1;
+ Usart_Mgr.TXDBuf[4] = dev_info.link_mode;
+ Usart_Mgr.TXDBuf[5] = dev_info.link_mode;
+ break;
+ }
+
+ case CMD_SET_LINK: {
+ dev_info.rf_state = RF_LINKING;
+ Usart_Mgr.TXDBuf[3] = 1;
+ Usart_Mgr.TXDBuf[4] = dev_info.link_mode;
+ Usart_Mgr.TXDBuf[5] = dev_info.link_mode;
+
+ rf_linking_time = 0;
+ rf_disconnect_delay = 0xff;
+ break;
+ }
+
+ case CMD_NEW_ADV: {
+ dev_info.rf_state = RF_PAIRING;
+ Usart_Mgr.TXDBuf[3] = 2;
+ Usart_Mgr.TXDBuf[4] = dev_info.link_mode;
+ Usart_Mgr.TXDBuf[5] = 1;
+ Usart_Mgr.TXDBuf[6] = dev_info.link_mode + 1;
+
+ rf_linking_time = 0;
+ rf_disconnect_delay = 0xff;
+ f_rf_new_adv_ok = 0;
+ break;
+ }
+
+ case CMD_CLR_DEVICE: {
+ Usart_Mgr.TXDBuf[3] = 1;
+ Usart_Mgr.TXDBuf[4] = 0;
+ Usart_Mgr.TXDBuf[5] = 0;
+ break;
+ }
+
+ case CMD_SET_CONFIG: {
+ Usart_Mgr.TXDBuf[3] = 1;
+ Usart_Mgr.TXDBuf[4] = RF_POWER_DOWN_DELAY;
+ Usart_Mgr.TXDBuf[5] = RF_POWER_DOWN_DELAY;
+ break;
+ }
+
+ case CMD_SET_NAME: {
+ Usart_Mgr.TXDBuf[3] = 17;
+ Usart_Mgr.TXDBuf[4] = 1;
+ Usart_Mgr.TXDBuf[5] = 15;
+ Usart_Mgr.TXDBuf[6] = 'N';
+ Usart_Mgr.TXDBuf[7] = 'u';
+ Usart_Mgr.TXDBuf[8] = 'P';
+ Usart_Mgr.TXDBuf[9] = 'h';
+ Usart_Mgr.TXDBuf[10] = 'y';
+ Usart_Mgr.TXDBuf[11] = ' ';
+ Usart_Mgr.TXDBuf[12] = 'A';
+ Usart_Mgr.TXDBuf[13] = 'i';
+ Usart_Mgr.TXDBuf[14] = 'r';
+ Usart_Mgr.TXDBuf[15] = '6';
+ Usart_Mgr.TXDBuf[16] = '0';
+ Usart_Mgr.TXDBuf[17] = ' ';
+ Usart_Mgr.TXDBuf[18] = 'V';
+ Usart_Mgr.TXDBuf[19] = '2';
+ Usart_Mgr.TXDBuf[20] = '-';
+ Usart_Mgr.TXDBuf[21] = get_checksum(Usart_Mgr.TXDBuf + 4, Usart_Mgr.TXDBuf[3]);
+ break;
+ }
+
+ case CMD_READ_DATA: {
+ Usart_Mgr.TXDBuf[3] = 2;
+ Usart_Mgr.TXDBuf[4] = 0x00;
+ Usart_Mgr.TXDBuf[5] = FUNC_VALID_LEN;
+ Usart_Mgr.TXDBuf[6] = FUNC_VALID_LEN;
+ break;
+ }
+
+ case CMD_RF_DFU: {
+ Usart_Mgr.TXDBuf[3] = 1;
+ Usart_Mgr.TXDBuf[4] = 0;
+ Usart_Mgr.TXDBuf[5] = 0;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ f_uart_ack = 0;
+ uart_send_bytes(Usart_Mgr.TXDBuf, Usart_Mgr.TXDBuf[3] + 5);
+
+ if (wait_ack) {
+ while (wait_ack--) {
+ wait_ms(1);
+ uart_receive_pro();
+ if (f_uart_ack || Usart_Mgr.RXCmd == cmd) { return TX_OK; }
+ }
+ } else {
+ return TX_OK;
+ }
+
+ return TX_TIMEOUT;
+}
+
+/**
+ * @brief RF module state sync.
+ */
+void dev_sts_sync(void) {
+ static uint32_t interval_timer = 0;
+ static uint8_t link_state_temp = RF_DISCONNECT;
+
+ if (timer_elapsed32(interval_timer) < 200) { return; }
+ interval_timer = timer_read32();
+
+ if (f_rf_reset) {
+ f_rf_reset = 0;
+ wait_ms(100);
+ gpio_write_pin_low(NRF_RESET_PIN);
+ wait_ms(50);
+ gpio_write_pin_high(NRF_RESET_PIN);
+ wait_ms(50);
+ } else if (f_send_channel) {
+ f_send_channel = 0;
+ uart_send_cmd(CMD_SET_LINK, 10, 10);
+ }
+
+ if (dev_info.link_mode == LINK_USB) {
+ if (host_mode != HOST_USB_TYPE) {
+ host_mode = HOST_USB_TYPE;
+ break_all_key();
+ host_set_driver(m_host_driver);
+ }
+ rf_blink_cnt = 0;
+ } else {
+ if (host_mode != HOST_RF_TYPE) {
+ host_mode = HOST_RF_TYPE;
+ break_all_key();
+ host_set_driver(&rf_host_driver);
+ }
+
+ if (dev_info.rf_state != RF_CONNECT) {
+ if (rf_disconnect_delay >= 15) {
+ rf_blink_cnt = 3;
+ if (rf_link_show_time == RF_LINK_SHOW_TIME) { rf_link_show_time = 0; }
+ link_state_temp = dev_info.rf_state;
+ } else {
+ rf_disconnect_delay++;
+ }
+ } else if (dev_info.rf_state == RF_CONNECT) {
+ rf_linking_time = 0;
+ rf_disconnect_delay = 0;
+ rf_blink_cnt = 0;
+
+ if (link_state_temp != RF_CONNECT) {
+ link_state_temp = RF_CONNECT;
+ if (rf_link_show_time == RF_LINK_SHOW_TIME) { rf_link_show_time = 0; }
+ }
+ }
+ }
+
+ /** This is called in house keeping with 1ms delay and
+ * 1ms wait time originally. Set to 0 the wait time to not hold up housekeeping
+ * if RF is sleeping we don't want to sync and wakeup the RF
+ */
+ if (f_wakeup_prepare && f_rf_sleep) { return; }
+ uart_send_cmd(CMD_RF_STS_SYSC, 1, 1);
+ uart_rpt_timer = timer_read32();
+
+ if (dev_info.link_mode != LINK_USB) {
+ if (++sync_lost >= 5) {
+ sync_lost = 0;
+ f_rf_reset = 1;
+ }
+ }
+}
+
+/**
+ * @brief Uart send bytes.
+ * @param Buffer data buf
+ * @param Length data length
+ */
+void uart_send_bytes(uint8_t *Buffer, uint32_t Length) {
+ Usart_Mgr.RXCmd = CMD_NULL;
+ gpio_write_pin_low(NRF_WAKEUP_PIN);
+ wait_us(50);
+
+ uart_transmit(Buffer, Length);
+
+ wait_us(50 + Length * 30);
+ gpio_write_pin_high(NRF_WAKEUP_PIN);
+
+ wait_us(800 - Length * 30);
+}
+
+/**
+ * @brief get checksum.
+ * @param buf data buf
+ * @param len data length
+ */
+uint8_t get_checksum(uint8_t *buf, uint8_t len) {
+ uint8_t i;
+ uint8_t checksum = 0;
+
+ for (i = 0; i < len; i++) {
+ checksum += *buf++;
+ }
+
+ checksum ^= UART_HEAD;
+
+ return checksum;
+}
+
+/**
+ * @brief Uart send report.
+ * @param report_type report_type
+ * @param report_buf report_buf
+ * @param report_size report_size
+ */
+void uart_send_report(uint8_t report_type, uint8_t *report_buf, uint8_t report_size) {
+ if (f_dial_sw_init_ok == 0) { return; }
+ if (dev_info.link_mode == LINK_USB) { return; }
+ if (dev_info.rf_state != RF_CONNECT) { return; }
+
+ Usart_Mgr.TXDBuf[0] = UART_HEAD;
+ Usart_Mgr.TXDBuf[1] = report_type;
+ Usart_Mgr.TXDBuf[2] = 0x01;
+ Usart_Mgr.TXDBuf[3] = report_size;
+
+ memcpy(&Usart_Mgr.TXDBuf[4], report_buf, report_size);
+ Usart_Mgr.TXDBuf[4 + report_size] = get_checksum(&Usart_Mgr.TXDBuf[4], report_size);
+
+ uart_send_bytes(&Usart_Mgr.TXDBuf[0], report_size + 5);
+ uart_rpt_timer = timer_read32(); // reset uart repeat timer.
+ // wait_us(200);
+}
+
+/**
+ * @brief Uart receives data and processes it after completion,.
+ */
+void uart_receive_pro(void) {
+ static bool rcv_start = false;
+ static uint32_t rcv_timer = 0;
+
+ // Process at most once every millisecond.
+ if (timer_elapsed32(rcv_timer) < 1) { return; }
+
+ // If there's data, wait a bit first then process it all.
+ // If you don't do this, you may lose sync.
+ if (uart_available()) {
+ wait_us(200);
+ // Receiving serial data from RF module
+ while (uart_available()) {
+ uint8_t byte = uart_read();
+ if (byte == UART_HEAD) { // valid UART transaction always begins with 0x5A
+ rcv_start = true;
+ }
+ // only read in what's valid. and drop the rest.
+ if (rcv_start && Usart_Mgr.RXDLen < UART_MAX_LEN) {
+ Usart_Mgr.RXDBuf[Usart_Mgr.RXDLen++] = byte;
+ }
+ }
+
+ // Processing received serial port protocol
+ if (rcv_start) {
+ rcv_start = false;
+ Usart_Mgr.RXDState = RX_Done;
+ rf_protocol_receive();
+ Usart_Mgr.RXDLen = 0;
+ }
+ }
+ rcv_timer = timer_read32();
+}
+
+void m_uart_gpio_set_low_speed(void) {
+ GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7);
+ GPIOB->PUPDR |= (GPIO_PUPDR_PUPDR6_0 | GPIO_PUPDR_PUPDR7_0);
+}
+
+/**
+ * @brief RF uart initial.
+ */
+void rf_uart_init(void) {
+ /* set uart buad as 460800 */
+ uart_init(460800);
+
+ /* Enable parity check */
+ USART1->CR1 &= ~((uint32_t)USART_CR1_UE);
+ USART1->CR1 |= USART_CR1_M0 | USART_CR1_PCE;
+ USART1->CR1 |= USART_CR1_UE;
+
+ /* set Rx and Tx pin pull up */
+ m_uart_gpio_set_low_speed();
+}
+
+/**
+ * @brief RF module initial.
+ */
+void rf_device_init(void) {
+ uint8_t timeout = 10;
+
+ f_rf_hand_ok = 0;
+ while (timeout--) {
+ uart_send_cmd(CMD_HAND, 0, 20);
+ wait_ms(5);
+ uart_receive_pro(); // receive data
+ // uart_receive_pro(); // parsing data
+ if (f_rf_hand_ok) { break; }
+ }
+
+ timeout = 10;
+ f_rf_read_data_ok = 0;
+ while (timeout--) {
+ uart_send_cmd(CMD_READ_DATA, 0, 20);
+ wait_ms(5);
+ uart_receive_pro();
+ // uart_receive_pro();
+ if (f_rf_read_data_ok) { break; }
+ }
+
+ timeout = 10;
+ f_rf_sts_sysc_ok = 0;
+ while (timeout--) {
+ uart_send_cmd(CMD_RF_STS_SYSC, 0, 20);
+ wait_ms(5);
+ uart_receive_pro();
+ // uart_receive_pro();
+ if (f_rf_sts_sysc_ok) { break; }
+ }
+
+ uart_send_cmd(CMD_SET_NAME, 10, 20);
+}
diff --git a/keyboards/nuphy/air60_v2/ansi/rf_driver.c b/keyboards/nuphy/air60_v2/ansi/rf_driver.c
new file mode 100644
index 000000000000..0a15fcd87cd2
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/rf_driver.c
@@ -0,0 +1,156 @@
+/*
+Copyright 2023 @ Nuphy (Source from jincao1, extended by Nuphy)
+
+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 .
+*/
+
+#include "host_driver.h"
+#include "host.h"
+#include "user_kb.h"
+#include "rf_queue.h"
+
+/* Variable declaration */
+extern report_buffer_t byte_report_buff;
+extern report_buffer_t bit_report_buff;
+extern rf_queue_t rf_queue;
+
+/* Host driver */
+static uint8_t rf_keyboard_leds(void);
+static void rf_send_keyboard(report_keyboard_t *report);
+static void rf_send_nkro(report_nkro_t *report);
+static void rf_send_mouse(report_mouse_t *report);
+static void rf_send_extra(report_extra_t *report);
+
+const host_driver_t rf_host_driver = {rf_keyboard_leds, rf_send_keyboard, rf_send_nkro, rf_send_mouse, rf_send_extra};
+
+/* defined in rf.c */
+void clear_report_buffer(void);
+void uart_send_report(uint8_t report_type, uint8_t *report_buf, uint8_t report_size);
+
+/**
+ * @brief Send or queue the RF report.
+ *
+ */
+static void send_or_queue(report_buffer_t *report) {
+ if (dev_info.rf_state == RF_CONNECT && rf_queue.is_empty()) {
+ uart_send_report(report->cmd, report->buffer, report->length);
+ } else {
+ rf_queue.enqueue(report);
+ }
+}
+
+static report_buffer_t make_report_buffer(uint8_t cmd, uint8_t *buff, uint8_t len) {
+ report_buffer_t report = {.cmd = cmd, .length = len};
+ memcpy(report.buffer, buff, len);
+ return report;
+}
+
+/**
+ * @brief Uart auto nkey send
+ */
+static void uart_auto_nkey_send(uint8_t *now_bit_report, uint8_t size) {
+ static uint8_t bytekb_report_buf[8] = {0};
+ static uint8_t bitkb_report_buf[16] = {0};
+ static uint8_t pre_bit_report[16] = {0};
+
+ uint8_t i, j, byte_index;
+ uint8_t change_mask, offset_mask;
+ uint8_t key_code = 0;
+
+ bool f_byte_send = 0;
+ bool f_bit_send = 0;
+
+ if (pre_bit_report[0] ^ now_bit_report[0]) {
+ bytekb_report_buf[0] = now_bit_report[0];
+ f_byte_send = 1;
+ }
+
+ for (i = 1; i < size; i++) {
+ change_mask = pre_bit_report[i] ^ now_bit_report[i];
+ offset_mask = 1;
+ for (j = 0; j < 8; j++) {
+ if (change_mask & offset_mask) {
+ if (now_bit_report[i] & offset_mask) {
+ for (byte_index = 2; byte_index < 8; byte_index++) {
+ if (bytekb_report_buf[byte_index] == 0) {
+ bytekb_report_buf[byte_index] = key_code;
+ f_byte_send = 1;
+ break;
+ }
+ }
+ if (byte_index >= 8) {
+ bitkb_report_buf[i] |= offset_mask;
+ f_bit_send = 1;
+ }
+ } else {
+ for (byte_index = 2; byte_index < 8; byte_index++) {
+ if (bytekb_report_buf[byte_index] == key_code) {
+ bytekb_report_buf[byte_index] = 0;
+ f_byte_send = 1;
+ break;
+ }
+ }
+ if (byte_index >= 8) {
+ bitkb_report_buf[i] &= ~offset_mask;
+ f_bit_send = 1;
+ }
+ }
+ }
+ key_code++;
+ offset_mask <<= 1;
+ }
+ }
+ memcpy(pre_bit_report, now_bit_report, 16);
+
+ if (f_byte_send) {
+ report_buffer_t rpt_byte = make_report_buffer(CMD_RPT_BYTE_KB, &bytekb_report_buf[0], 8);
+ send_or_queue(&rpt_byte);
+ byte_report_buff = rpt_byte;
+ }
+
+ if (f_bit_send) {
+ report_buffer_t rpt_bit = make_report_buffer(CMD_RPT_BIT_KB, &bitkb_report_buf[0], 16);
+ send_or_queue(&rpt_bit);
+ bit_report_buff = rpt_bit;
+ }
+}
+
+static uint8_t rf_keyboard_leds(void) {
+ return dev_info.rf_led;
+}
+
+static void rf_send_keyboard(report_keyboard_t *report) {
+ report->reserved = 0;
+ report_buffer_t rpt = make_report_buffer(CMD_RPT_BYTE_KB, &report->mods, 8);
+ send_or_queue(&rpt);
+ byte_report_buff = rpt;
+}
+
+static void rf_send_nkro(report_nkro_t *report) {
+ // clear current reports
+ clear_report_buffer();
+ uart_auto_nkey_send(&nkro_report->mods, 16); // only need 1 byte mod + 15 byte keys
+}
+
+static void rf_send_mouse(report_mouse_t *report) {
+ report_buffer_t rpt = make_report_buffer(CMD_RPT_MS, &report->buttons, 5);
+ send_or_queue(&rpt);
+}
+
+static void rf_send_extra(report_extra_t *report) {
+ uint8_t cmd_rpt = report->report_id == REPORT_ID_CONSUMER ? CMD_RPT_CONSUME : CMD_RPT_SYS;
+ report_buffer_t rpt = make_report_buffer(cmd_rpt, (uint8_t *)(&report->usage), 2);
+ send_or_queue(&rpt);
+}
+
diff --git a/keyboards/nuphy/air60_v2/ansi/rf_queue.c b/keyboards/nuphy/air60_v2/ansi/rf_queue.c
new file mode 100644
index 000000000000..9d76a1d26fdb
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/rf_queue.c
@@ -0,0 +1,60 @@
+/*
+Copyright 2024 @ jincao1, inspired by Keychron
+
+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 .
+*/
+
+#include "rf_queue.h"
+
+static uint8_t queue_head = 0;
+static uint8_t queue_tail = 0;
+
+static report_buffer_t buffer_queue[RF_QUEUE_SIZE] = {0};
+
+static bool rf_queue_is_empty(void) {
+ return queue_head == queue_tail;
+}
+
+static void clear_rf_queue(void) {
+ queue_head = 0;
+ queue_tail = 0;
+}
+
+static bool enqueue_rf_report(report_buffer_t *report) {
+ uint8_t next = (queue_head + 1) % RF_QUEUE_SIZE;
+ if (next == queue_tail) { // queue is full.
+ return false;
+ }
+ buffer_queue[queue_head] = *report;
+ queue_head = next;
+ return true;
+}
+
+static bool dequeue_rf_report(report_buffer_t *report) {
+ if (rf_queue_is_empty()) {
+ return false; // queue empty
+ }
+ *report = buffer_queue[queue_tail];
+ queue_tail = (queue_tail + 1) % RF_QUEUE_SIZE; // set tail to next index
+ return true;
+}
+
+// clang-format off
+const rf_queue_t rf_queue = {
+ .enqueue = enqueue_rf_report,
+ .dequeue = dequeue_rf_report,
+ .is_empty = rf_queue_is_empty,
+ .clear = clear_rf_queue,
+};
+// clang-format on
diff --git a/keyboards/nuphy/air60_v2/ansi/rf_queue.h b/keyboards/nuphy/air60_v2/ansi/rf_queue.h
new file mode 100644
index 000000000000..d149d6720c2d
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/rf_queue.h
@@ -0,0 +1,38 @@
+/*
+Copyright 2024 @ jincao1
+
+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 .
+*/
+
+#pragma once
+
+#include "quantum.h"
+
+#ifndef RF_QUEUE_SIZE
+#define RF_QUEUE_SIZE 64
+#endif
+
+typedef struct {
+ uint8_t cmd;
+ uint8_t length;
+ uint8_t repeat; // track number of times it's been resent
+ uint8_t buffer[16]; // set to max length used
+} report_buffer_t;
+
+typedef struct {
+ bool (*enqueue)(report_buffer_t *report);
+ bool (*dequeue)(report_buffer_t *report);
+ bool (*is_empty)(void);
+ void (*clear)(void);
+} rf_queue_t;
diff --git a/keyboards/nuphy/air60_v2/ansi/rgb.c b/keyboards/nuphy/air60_v2/ansi/rgb.c
new file mode 100644
index 000000000000..522f1704f3bf
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/rgb.c
@@ -0,0 +1,795 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#include "user_kb.h"
+#include "ansi.h"
+#include "rgb_table.h"
+#include "mcu_pwr.h"
+#include "is31fl3733.h"
+
+/* side rgb mode */
+enum {
+ SIDE_WAVE = 0,
+ SIDE_MIX,
+ SIDE_STATIC,
+ SIDE_BREATH,
+ SIDE_OFF,
+};
+
+uint8_t side_play_point = 0;
+uint32_t side_one_timer = 0;
+
+bool rgb_state = 0;
+uint8_t rgb_start_led = 0;
+uint8_t rgb_end_led = 0;
+uint16_t rgb_show_time = 0;
+uint32_t rgb_indicator_timer = 0;
+
+uint8_t sys_light = 3;
+
+HSV hsv = { .h = 0, .s = 255, .v = 255};
+RGB current_rgb = {.r = 0x00, .g = 0x00, .b = 0x00};
+
+const uint8_t side_speed_table[5][5] = {
+ [SIDE_WAVE] = {14, 19, 25, 32, 40}, // [SIDE_WAVE] = {10, 14, 20, 28, 38},
+ [SIDE_MIX] = {14, 19, 25, 32, 40}, // [SIDE_MIX] = {10, 14, 20, 28, 38},
+ [SIDE_STATIC] = {50, 50, 50, 50, 50}, // [SIDE_STATIC] = {50, 50, 50, 50, 50},
+ [SIDE_BREATH] = {14, 19, 25, 32, 40}, // [SIDE_BREATH] = {10, 14, 20, 28, 38},
+ [SIDE_OFF] = {50, 50, 50, 50, 50}, // [SIDE_OFF] = {50, 50, 50, 50, 50},
+};
+
+const uint8_t side_light_table[7] = {
+ 0,
+ 42,
+ 84,
+ 128,
+ 168,
+ 210,
+ 255,
+};
+
+const uint8_t side_led_index_tab[SIDE_LINE][2] =
+ {
+ {SIDE_INDEX + 4, SIDE_INDEX + 5},
+ {SIDE_INDEX + 3, SIDE_INDEX + 6},
+ {SIDE_INDEX + 2, SIDE_INDEX + 7},
+ {SIDE_INDEX + 1, SIDE_INDEX + 8},
+ {SIDE_INDEX + 0, SIDE_INDEX + 9},
+};
+
+void set_sys_light(void) {
+ sys_light = user_config.ee_side_light > 5 ? 1 : (3 - user_config.ee_side_light / 2);
+}
+
+static void side_off_mode_show(void);
+void rgb_matrix_update_pwm_buffers(void);
+
+typedef struct is31fl3733_driver_t {
+ uint8_t pwm_buffer[192];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[24];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3733_driver_t;
+
+extern is31fl3733_driver_t driver_buffers[DRIVER_COUNT];
+
+/**
+ * @brief is_side_rgb_off
+ */
+
+bool is_side_rgb_off(void)
+{
+ is31fl3733_led_t led;
+ for (int i = SIDE_INDEX; i < SIDE_INDEX + 10; i++) {
+ memcpy_P(&led, (&g_is31fl3733_leds[i]), sizeof(led));
+ if (driver_buffers[led.driver].pwm_buffer[led.r] != 0 || driver_buffers[led.driver].pwm_buffer[led.g] != 0 || driver_buffers[led.driver].pwm_buffer[led.b] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void side_rgb_refresh(void) {
+ if (!is_side_rgb_off() || user_config.ee_side_light > 0) {
+ pwr_led_on(); // power on side LED before refresh
+ set_sys_light();
+ }
+}
+
+/**
+ * @brief Adjusting the brightness of side lights.
+ * @param bright: 0 - decrease, 1 - increase.
+ * @note save to eeprom.
+ */
+void side_light_control(uint8_t bright) {
+ if (bright) {
+ if (user_config.ee_side_light < SIDE_BRIGHT_MAX) { user_config.ee_side_light++; }
+ } else {
+ if (user_config.ee_side_light > 0) { user_config.ee_side_light--; }
+ }
+ // set_sys_light();
+#ifndef NO_DEBUG
+ dprintf("side matrix light_control [NOEEPROM]: %d\n", user_config.ee_side_light);
+#endif
+}
+
+/**
+ * @brief Adjusting the speed of side lights.
+ * @param fast: 0 - decrease, 1 - increase.
+ * @note save to eeprom.
+ */
+void side_speed_control(uint8_t fast) {
+ if (fast) {
+ if (user_config.ee_side_speed > 0) { user_config.ee_side_speed--; }
+ } else {
+ if (user_config.ee_side_speed < SIDE_SPEED_MAX) { user_config.ee_side_speed++; }
+ }
+#ifndef NO_DEBUG
+ dprintf("side matrix speed_control [NOEEPROM]: %d\n", user_config.ee_side_speed);
+#endif
+}
+
+/**
+ * @brief Switch to the next color of side lights.
+ * @param color: 0 - prev, 1 - next.
+ * @note save to eeprom.
+ */
+void side_colour_control(uint8_t color) {
+ if (user_config.ee_side_mode != SIDE_WAVE) {
+ if (user_config.ee_side_rgb) {
+ user_config.ee_side_rgb = 0;
+ user_config.ee_side_colour = game_mode_enable;
+ }
+ }
+
+ if (user_config.ee_side_rgb) {
+ user_config.ee_side_rgb = 0;
+ user_config.ee_side_colour = color ? game_mode_enable : SIDE_COLOUR_MAX - 1;
+ } else {
+ if (color) { user_config.ee_side_colour++; }
+ else { user_config.ee_side_colour--; }
+
+ if (user_config.ee_side_colour >= SIDE_COLOUR_MAX) {
+ user_config.ee_side_rgb = 1;
+ user_config.ee_side_colour = game_mode_enable;
+ }
+ }
+#ifndef NO_DEBUG
+ dprintf("side matrix colour_control [NOEEPROM]: %d rgb: %d\n", user_config.ee_side_colour, user_config.ee_side_rgb);
+#endif
+}
+
+/**
+ * @brief Change the color mode of side lights.
+ * @param dir: 0 - prev, 1 - next.
+ * @note save to eeprom.
+ */
+void side_mode_control(uint8_t dir) {
+ if (dir) {
+ user_config.ee_side_mode++;
+ if (user_config.ee_side_mode > SIDE_OFF) { user_config.ee_side_mode = 0; }
+ } else {
+ if (user_config.ee_side_mode > 0) { user_config.ee_side_mode--; }
+ else { user_config.ee_side_mode = SIDE_OFF; }
+ }
+ side_play_point = 0;
+#ifndef NO_DEBUG
+ dprintf("side matrix mode_control [NOEEPROM]: %d\n", user_config.ee_side_mode);
+#endif
+}
+
+void set_side_rgb(uint8_t side, uint8_t r, uint8_t g, uint8_t b) {
+ // side = 1 => left
+ // side = 2 => right
+ // side = 3 => both
+ uint8_t start = 0;
+ uint8_t end = SIDE_LINE * 2;
+ if (side > SYS_MARK) {
+ r = r / sys_light;
+ g = g / sys_light;
+ b = b / sys_light;
+ }
+ if (side % SYS_MARK == LEFT_SIDE) { end = end - SIDE_LINE; }
+ if (side % SYS_MARK == RIGHT_SIDE) { start = start + SIDE_LINE; }
+
+ for (uint8_t i = start; i < end; i++) {
+ rgb_matrix_set_color(SIDE_INDEX + i, r, g, b);
+ }
+}
+
+/**
+ * @brief Visual cue for sleep on side LED.
+*/
+void signal_sleep(uint8_t r, uint8_t g, uint8_t b) {
+ pwr_led_on();
+ wait_ms(50); // give some time to ensure LED powers on.
+ set_side_rgb(LEFT_SIDE + RIGHT_SIDE, r, g, b);
+ rgb_matrix_update_pwm_buffers();
+ wait_ms(300);
+}
+
+/**
+ * @brief system switch led show
+ */
+void sys_sw_led_show(void) {
+ if (sys_show_timer != 0) {
+ if (dev_info.sys_sw_state == SYS_SW_MAC) {
+ current_rgb.r = SIDE_BLINK_LIGHT;
+ current_rgb.g = SIDE_BLINK_LIGHT;
+ current_rgb.b = SIDE_BLINK_LIGHT;
+ } else {
+ current_rgb.r = 0x00;
+ current_rgb.g = 0x00;
+ current_rgb.b = SIDE_BLINK_LIGHT;
+ }
+ if (timer_elapsed32(sys_show_timer) >= 2900) {
+ sys_show_timer = 0;
+ }
+ if ((timer_elapsed32(sys_show_timer) / 500) % 2 == 0) {
+ set_side_rgb(RIGHT_SIDE + SYS_MARK, current_rgb.r, current_rgb.g, current_rgb.b);
+ } else {
+ set_side_rgb(RIGHT_SIDE, RGB_OFF);
+ }
+ }
+}
+
+/**
+ * @brief sleep_sw_led_show
+ */
+void sleep_sw_led_show(void) {
+ if (sleep_show_timer != 0) {
+ current_rgb.r = 0x00, current_rgb.g = 0x00, current_rgb.b = 0x00;
+ switch (user_config.sleep_mode) {
+ case 0:
+ current_rgb.r = SIDE_BLINK_LIGHT;
+ break;
+ case 1:
+ current_rgb.g = SIDE_BLINK_LIGHT;
+ break;
+ case 2:
+ current_rgb.r = SIDE_BLINK_LIGHT;
+ current_rgb.g = SIDE_BLINK_LIGHT;
+ break;
+ }
+ if (timer_elapsed32(sleep_show_timer) >= 2900) {
+ sleep_show_timer = 0;
+ }
+ if ((timer_elapsed32(sleep_show_timer) / 500) % 2 == 0) {
+ set_side_rgb(RIGHT_SIDE + SYS_MARK, current_rgb.r, current_rgb.g, current_rgb.b);
+ } else {
+ set_side_rgb(RIGHT_SIDE, RGB_OFF);
+ }
+ }
+}
+
+/**
+ * @brief host system led indicate.
+ */
+void sys_led_show(void) {
+ current_rgb.g = SIDE_BLINK_LIGHT;
+ current_rgb.b = SIDE_BLINK_LIGHT;
+ current_rgb.r = 0x00;
+ uint8_t led_side = LEFT_SIDE;
+
+ if (host_keyboard_led_state().caps_lock) {
+ led_side = RIGHT_SIDE;
+ set_side_rgb(LEFT_SIDE + SYS_MARK, current_rgb.r, current_rgb.g, current_rgb.b);
+ }
+
+ if (user_config.numlock_state != 1) { return; }
+
+ if (host_keyboard_led_state().num_lock) {
+ current_rgb.r = SIDE_BLINK_LIGHT;
+ set_side_rgb(led_side + SYS_MARK, current_rgb.r, current_rgb.g, current_rgb.b);
+ }
+}
+
+/**
+ * @brief light_point_playing.
+ * @param trend:
+ * @param step:
+ * @param len:
+ * @param point:
+ */
+static void light_point_playing(uint8_t trend, uint8_t step, uint8_t len, uint8_t *point) {
+ if (trend) {
+ *point += step;
+ if (*point > 254 && len == 0) { *point = 0; }
+ else if (*point >= len) { *point -= len; }
+ } else {
+ *point -= step;
+ if (*point < 1 && len == 0) { *point = 255; }
+ else if (*point >= len) { *point = len - (255 - *point) - 1; }
+ }
+}
+
+
+/**
+ * @brief count_rgb_light.
+ * @param light_temp:
+ */
+static void count_rgb_light(uint8_t light_temp) {
+ uint16_t temp;
+
+ temp = (light_temp)*current_rgb.r + current_rgb.r;
+ current_rgb.r = temp >> 8;
+
+ temp = (light_temp)*current_rgb.g + current_rgb.g;
+ current_rgb.g = temp >> 8;
+
+ temp = (light_temp)*current_rgb.b + current_rgb.b;
+ current_rgb.b = temp >> 8;
+}
+
+/**
+ * @brief side_wave_mode_show.
+ */
+static void side_wave_mode_show(void) {
+ uint8_t play_index;
+
+ if (user_config.ee_side_rgb) {
+ light_point_playing(0, 6, 0, &side_play_point);
+ } else {
+ light_point_playing(0, 2, BREATHE_TAB_LEN, &side_play_point);
+ }
+
+ play_index = side_play_point;
+ for (uint8_t i = 0; i < SIDE_LINE; i++) {
+ if (user_config.ee_side_rgb) {
+ hsv.h = play_index;
+ current_rgb = hsv_to_rgb_nocie(hsv);
+
+ light_point_playing(1, 32, 0, &play_index);
+ } else {
+ current_rgb.r = colour_lib[user_config.ee_side_colour][0];
+ current_rgb.g = colour_lib[user_config.ee_side_colour][1];
+ current_rgb.b = colour_lib[user_config.ee_side_colour][2];
+ light_point_playing(1,12,BREATHE_TAB_LEN,&play_index);
+ count_rgb_light(breathe_data_tab[play_index]);
+ }
+ count_rgb_light(side_light_table[user_config.ee_side_light]);
+
+ for (uint8_t j = 0; j < 2; j++) {
+ rgb_matrix_set_color(side_led_index_tab[i][j], current_rgb.r, current_rgb.g, current_rgb.b);
+ }
+ }
+}
+
+/**
+ * @brief side_spectrum_mode_show.
+ */
+static void side_spectrum_mode_show(void) {
+ light_point_playing(0, 2, 0, &side_play_point);
+ hsv.h = side_play_point;
+ current_rgb = hsv_to_rgb_nocie(hsv);
+
+ count_rgb_light(side_light_table[user_config.ee_side_light]);
+ set_side_rgb(LEFT_SIDE + RIGHT_SIDE, current_rgb.r, current_rgb.g, current_rgb.b);
+}
+
+
+/**
+ * @brief side_breathe_mode_show.
+ */
+static void side_breathe_mode_show(void) {
+ light_point_playing(1,1,BREATHE_TAB_LEN,&side_play_point);
+
+ current_rgb.r = colour_lib[user_config.ee_side_colour][0];
+ current_rgb.g = colour_lib[user_config.ee_side_colour][1];
+ current_rgb.b = colour_lib[user_config.ee_side_colour][2];
+
+ count_rgb_light(breathe_data_tab[side_play_point]);
+ count_rgb_light(side_light_table[user_config.ee_side_light]);
+ set_side_rgb(LEFT_SIDE + RIGHT_SIDE, current_rgb.r, current_rgb.g, current_rgb.b);
+}
+
+/**
+ * @brief side_static_mode_show.
+ */
+static void side_static_mode_show(void) {
+ current_rgb.r = colour_lib[user_config.ee_side_colour][0];
+ current_rgb.g = colour_lib[user_config.ee_side_colour][1];
+ current_rgb.b = colour_lib[user_config.ee_side_colour][2];
+
+ count_rgb_light(side_light_table[user_config.ee_side_light]);
+ set_side_rgb(LEFT_SIDE + RIGHT_SIDE, current_rgb.r, current_rgb.g, current_rgb.b);
+}
+
+/**
+ * @brief side_off_mode_show.
+ */
+static void side_off_mode_show(void) {
+ set_side_rgb(LEFT_SIDE + RIGHT_SIDE, RGB_OFF);
+}
+
+/**
+ * @brief side_one_control
+ */
+void side_one_control(uint8_t adjust)
+{
+ if (adjust) {
+ if (user_config.ee_side_one == LEFT_SIDE + RIGHT_SIDE) {
+ user_config.ee_side_one = 0;
+ } else {
+ user_config.ee_side_one++;
+ side_one_timer = 1;
+ }
+ }
+#ifndef NO_DEBUG
+ dprintf("side matrix side_control [NOEEPROM]: %d\n", user_config.ee_side_one);
+#endif
+}
+
+/**
+ * @brief side_one_show
+ */
+static void side_one_show(void)
+{
+ static uint8_t my_side = 2;
+ if (user_config.ee_side_one == 0) { return; }
+
+ if (user_config.ee_side_one != LEFT_SIDE + RIGHT_SIDE) {
+ set_side_rgb(user_config.ee_side_one, RGB_OFF);
+ } else {
+ if (side_one_timer == 1) {
+ my_side = 2;
+ }
+ if (side_one_timer <= 1) {
+ side_one_timer = timer_read32();
+ }
+
+ if (timer_elapsed32(side_one_timer) < 500 ) {
+ set_side_rgb(LEFT_SIDE + RIGHT_SIDE, RGB_OFF);
+ return;
+ } else if (timer_elapsed32(side_one_timer) >= (1000 * 60 * 1)) {
+ side_one_timer = 0;
+ my_side = my_side == 2 ? 1 : 2;
+ }
+ set_side_rgb(my_side, RGB_OFF);
+ }
+}
+
+/**
+ * @brief rf state indicate
+ */
+void rf_led_show(void) {
+ static uint32_t rf_blink_timer = 0;
+ uint16_t rf_blink_period = 0;
+
+ if (rf_blink_cnt || (rf_link_show_time < RF_LINK_SHOW_TIME)) {
+ current_rgb.r = dev_info.link_mode == LINK_USB ? SIDE_BLINK_LIGHT : 0x00; // LINK_USB
+ current_rgb.g = dev_info.link_mode % LINK_USB == 0 ? SIDE_BLINK_LIGHT : 0x00; // LINK_USB || LINK_RF_24
+ current_rgb.b = dev_info.link_mode % LINK_USB != 0 ? SIDE_BLINK_LIGHT : 0x00; // LINK_BT
+ } else {
+ rf_blink_timer = timer_read32();
+ return;
+ }
+
+ if (rf_blink_cnt) {
+ if (dev_info.rf_state == RF_PAIRING) {
+ rf_blink_period = RF_LED_PAIR_PERIOD;
+ } else {
+ rf_blink_period = RF_LED_LINK_PERIOD;
+ }
+
+ if (timer_elapsed32(rf_blink_timer) > (rf_blink_period >> 1)) {
+ current_rgb.r = 0x00, current_rgb.g = 0x00, current_rgb.b = 0x00;
+ }
+
+ if (timer_elapsed32(rf_blink_timer) >= rf_blink_period) {
+ rf_blink_cnt--;
+ rf_blink_timer = timer_read32();
+ }
+ }
+
+ set_side_rgb(LEFT_SIDE + SYS_MARK, current_rgb.r, current_rgb.g, current_rgb.b);
+
+ // light up corresponding BT/RF key
+ if (dev_info.link_mode <= LINK_BT_3) {
+ uint8_t my_pos = dev_info.link_mode == LINK_RF_24 ? 23 : (19 + dev_info.link_mode);
+ rgb_required = 1;
+ rgb_matrix_set_color(my_pos, current_rgb.r, current_rgb.g, current_rgb.b);
+ }
+}
+
+/**
+ * @brief bat_num_led.
+ */
+void bat_num_led(void) {
+ uint8_t bat_percent = dev_info.rf_battery;
+ uint8_t bat_pct = bat_percent / 10;
+ uint8_t r, g, b;
+
+ rgb_required = 1;
+
+ // set color
+ if (bat_percent <= 15) {
+ r = 0xff; g = 0x00; b = 0x00;
+ }
+ else if (bat_percent <= 50) {
+ r = 0xff; g = 0x40; b = 0x00;
+ }
+ else if (bat_percent <= 80) {
+ r = 0xff; g = 0xff; b = 0x00;
+ } else {
+ r = 0x00; g = 0xff; b = 0x00;
+ }
+
+ if (bat_percent % 10 == 0) { bat_pct--; }
+
+ for(uint8_t i=0; i < bat_pct; i++) {
+ rgb_matrix_set_color(20 + i, r, g, b);
+ }
+ // set percent
+
+ if (bat_percent % 10 == 0) {
+ r = 0x00; g = 0xff; b = 0x00;
+ } else if (bat_percent % 10 <= 3) {
+ r = 0xff; g = 0x00; b = 0x00;
+ } else if (bat_percent % 10 <= 6) {
+ r = 0xff; g = 0x40; b = 0x00;
+ } else {
+ r = 0x00; g = 0xff; b = 0x00;
+ }
+
+ rgb_matrix_set_color(20 + bat_pct, r, g, b);
+
+}
+
+/**
+ * @brief Battery level indicator
+ */
+void bat_percent_led(void)
+{
+ uint8_t bat_percent = dev_info.rf_battery;
+ uint8_t bat_end_led = 4;
+ current_rgb.r = SIDE_BLINK_LIGHT, current_rgb.g = SIDE_BLINK_LIGHT / 2, current_rgb.b = 0x00;
+
+ if (bat_percent <= 30) {
+ bat_end_led = 1;
+ current_rgb.r = SIDE_BLINK_LIGHT, current_rgb.g = 0x00, current_rgb.b = 0x00;
+ } else if (bat_percent <= 40) {
+ bat_end_led = 1;
+ } else if (bat_percent <= 60) {
+ bat_end_led = 2;
+ } else if (bat_percent <= 75) {
+ bat_end_led = 3;
+ } else if (bat_percent <= 90) {
+ bat_end_led = 4;
+ } else {
+ current_rgb.r = 0x00, current_rgb.g = SIDE_BLINK_LIGHT, current_rgb.b = 0x00;
+ }
+
+ set_side_rgb(RIGHT_SIDE + SYS_MARK, current_rgb.r, current_rgb.g, current_rgb.b);
+ for (uint8_t i = bat_end_led + 1; i < SIDE_LINE; i++) {
+ rgb_matrix_set_color(SIDE_INDEX + 9 - i, RGB_OFF);
+ }
+}
+
+/**
+ * @brief battery state indicate
+ */
+void bat_led_show(void)
+{
+ static bool bat_show_flag = 1;
+ static bool bat_show_breath = 0;
+ static bool f_init = 1;
+ static uint8_t bat_play_point = 0;
+ static uint32_t bat_play_timer = 0;
+ static uint32_t bat_show_time = 0;
+
+ static uint32_t bat_sts_debounce = 0;
+ static uint8_t charge_state = 0;
+
+ if (f_init) {
+ f_init = 0;
+ bat_show_time = timer_read32();
+ charge_state = dev_info.rf_charge;
+ }
+
+ if (charge_state != dev_info.rf_charge) {
+ if (timer_elapsed32(bat_sts_debounce) > 1000) {
+ if ((charge_state & 0x01) == 0 && (dev_info.rf_charge & 0x01) != 0) {
+ bat_show_flag = true;
+ bat_show_breath = true;
+ bat_show_time = timer_read32();
+ }
+ charge_state = dev_info.rf_charge;
+ }
+ } else {
+ bat_sts_debounce = timer_read32();
+ if (timer_elapsed32(bat_show_time) > 5000) {
+ bat_show_flag = false;
+ bat_show_breath = false;
+ }
+ if (charge_state == 0x03) {
+ bat_show_breath = true;
+ } else if (charge_state & 0x01) {
+ dev_info.rf_battery = 100;
+ }
+ }
+
+ if (dev_info.rf_battery < 15) {
+ bat_show_flag = true;
+ bat_show_breath = true;
+ bat_show_time = timer_read32();
+ }
+
+ if (f_bat_hold || bat_show_flag) {
+
+ if (bat_show_breath) {
+
+ if (timer_elapsed32(bat_play_timer) > 15) {
+ bat_play_timer = timer_read32();
+ light_point_playing(0, 1, BREATHE_TAB_LEN, &bat_play_point);
+ }
+ current_rgb.r = SIDE_BLINK_LIGHT, current_rgb.g = dev_info.rf_battery < 15 ? 0x00 : SIDE_BLINK_LIGHT / 2, current_rgb.b = 0x00;
+ count_rgb_light(breathe_data_tab[bat_play_point]);
+ set_side_rgb(RIGHT_SIDE + SYS_MARK, current_rgb.r, current_rgb.g, current_rgb.b);
+ } else {
+ bat_percent_led();
+ }
+ }
+}
+
+/**
+ * @brief device_reset_show.
+ */
+void device_reset_show(void)
+{
+ pwr_led_on();
+ for (uint8_t blink_cnt = 0; blink_cnt < 3; blink_cnt++) {
+ rgb_matrix_set_color_all(RGB_WHITE);
+ rgb_matrix_update_pwm_buffers();
+ wait_ms(200);
+
+ rgb_matrix_set_color_all(RGB_OFF);
+ rgb_matrix_update_pwm_buffers();
+ wait_ms(200);
+ }
+}
+
+/**
+ * @brief device_reset_init.
+ */
+void device_reset_init(void)
+{
+ side_play_point = 0;
+ game_mode_enable = 0;
+ f_bat_hold = false;
+
+ rgb_matrix_enable_noeeprom();
+ user_config_reset();
+}
+
+/**
+ * @brief rgb test
+ */
+
+void rgb_test_show(void) {
+ // open power control
+ pwr_led_on();
+
+ for (uint8_t i = 0; i < SIDE_COLOUR_MAX; i++) {
+ rgb_matrix_set_color_all(colour_lib[i][0], colour_lib[i][1], colour_lib[i][2]);
+ set_side_rgb(LEFT_SIDE + RIGHT_SIDE, colour_lib[i][0], colour_lib[i][1], colour_lib[i][2]);
+ rgb_matrix_update_pwm_buffers();
+ wait_ms(500);
+ }
+}
+
+void signal_rgb_led(uint8_t state, uint8_t start_led, uint8_t end_led, uint16_t show_time) {
+ rgb_state = state > 0 ? 1 : 0;
+ rgb_start_led = start_led;
+ rgb_end_led = end_led;
+ rgb_show_time = show_time;
+ rgb_indicator_timer = 0;
+}
+
+void rgb_led_indicator(void) {
+ if (rgb_show_time == 0) { return; }
+ if (rgb_indicator_timer == 0) { rgb_indicator_timer = timer_read32(); }
+ if (timer_elapsed32(rgb_indicator_timer) < rgb_show_time) {
+ current_rgb.r = 0xFF;
+ current_rgb.g = 0x00;
+ current_rgb.b = 0x00;
+ if (rgb_state) {
+ current_rgb.r = 0x00;
+ current_rgb.g = 0xFF;
+ }
+
+ rgb_required = 2;
+ for (uint8_t i = rgb_start_led; i <= rgb_end_led; i++) {
+ rgb_matrix_set_color(i, current_rgb.r, current_rgb.g, current_rgb.b);
+ }
+ } else {
+ rgb_indicator_timer = 0;
+ rgb_show_time = 0;
+ }
+}
+
+void caps_word_show(void) {
+ if (game_mode_enable || !user_config.caps_word_enable) { return; }
+ if (is_caps_word_on()) {
+ rgb_required = 2;
+ rgb_matrix_set_color(CAPS_LED, RGB_CYAN);
+ }
+}
+
+void numlock_rgb_show(void) {
+ if (user_config.numlock_state != 2) { return; }
+ if (host_keyboard_led_state().num_lock) {
+ rgb_required = 2;
+ rgb_matrix_set_color(NUMLOCK_LED, RGB_WHITE);
+ }
+}
+
+/**
+ * @brief side_led_show.
+ */
+void normal_led_process(void) {
+ static uint32_t side_update_time = 0;
+ // side_mode & side_speed should always be valid...
+ // refresh side LED animation based on speed.
+
+ uint32_t update_interval = game_mode_enable ? 500 : side_speed_table[user_config.ee_side_mode][user_config.ee_side_speed];
+
+ if (timer_elapsed32(side_update_time) < update_interval) { return; }
+ side_update_time = timer_read32();
+
+ switch (user_config.ee_side_mode) {
+ case SIDE_WAVE:
+ side_wave_mode_show();
+ break;
+ case SIDE_MIX:
+ side_spectrum_mode_show();
+ break;
+ case SIDE_BREATH:
+ side_breathe_mode_show();
+ break;
+ case SIDE_STATIC:
+ side_static_mode_show();
+ break;
+ case SIDE_OFF:
+ side_off_mode_show();
+ break;
+ }
+
+ if (!game_mode_enable) {
+ side_one_show();
+ bat_led_show();
+ sleep_sw_led_show();
+ } else if (dev_info.rf_battery < 15 && !USB_ACTIVE) { set_side_rgb(RIGHT_SIDE, 0x40, 0x00, 0x00); }
+
+ sys_sw_led_show();
+ sys_led_show();
+}
+
+void realtime_led_process(void) {
+ rf_led_show();
+ rgb_led_indicator();
+ caps_word_show();
+ numlock_rgb_show();
+}
+
+void led_show(void) {
+ if (f_wakeup_prepare) { return; }
+ side_rgb_refresh();
+ normal_led_process();
+ realtime_led_process();
+}
diff --git a/keyboards/nuphy/air60_v2/ansi/rgb_table.h b/keyboards/nuphy/air60_v2/ansi/rgb_table.h
new file mode 100644
index 000000000000..08556dfbf168
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/rgb_table.h
@@ -0,0 +1,80 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#pragma once
+
+#define BREATHE_TAB_LEN 128
+
+#define SIDE_BRIGHT_MAX 6
+#define SIDE_SPEED_MAX 4
+#define SIDE_COLOUR_MAX 10
+
+#define SIDE_LINE 5
+#define LEFT_SIDE 1
+#define RIGHT_SIDE 2
+#define SYS_MARK 10
+
+#define SIDE_BLINK_LIGHT 128
+#define SIDE_INDEX 64
+
+#define RF_LED_LINK_PERIOD 500
+#define RF_LED_PAIR_PERIOD 250
+
+
+//----------------------------------------------------------------
+// breathe data table
+//----------------------------------------------------------------
+
+const uint8_t breathe_data_tab[BREATHE_TAB_LEN] =
+{
+ 0, 1, 2, 3, 4, 5, 6, 7,
+// 8, 9, 10, 12, 14, 16, 18, 20,
+// 22, 24, 27, 30, 33, 36, 39, 42,
+// 45, 49, 53, 57, 61, 65, 69, 73,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 25, 27, 29, 31, 33, 36, 39, 42,
+ 45, 49, 53, 57, 61, 65, 69, 73,
+ 77, 81, 85, 89, 94, 99, 104, 109,
+ 114, 119, 124, 129, 134, 140, 146, 152,
+ 158, 164, 170, 176, 182, 188, 194, 200,
+ 206, 213, 220, 227, 234, 241, 248, 255,
+
+ 255, 248, 241, 234, 227, 220, 213, 206,
+ 200, 194, 188, 182, 176, 170, 164, 158,
+ 152, 146, 140, 134, 129, 124, 119, 114,
+ 109, 104, 99, 94, 89, 85, 81, 77,
+ 73, 69, 65, 61, 57, 53, 49, 45,
+ 42, 39, 36, 33, 30, 27, 24, 22,
+// 20, 18, 16, 14, 12, 10, 9, 8,
+ 7, 6, 5, 4, 3, 2, 1, 0,
+};
+
+
+const uint8_t colour_lib[SIDE_COLOUR_MAX][3] =
+{
+ {RGB_RED},
+ {RGB_ORANGE},
+ {RGB_YELLOW},
+ {RGB_GREEN},
+ {RGB_SPRINGGREEN},
+ {RGB_CYAN},
+ {RGB_BLUE},
+ {RGB_PURPLE},
+ {RGB_MAGENTA},
+ {RGB_WHITE}
+};
diff --git a/keyboards/nuphy/air60_v2/ansi/rules.mk b/keyboards/nuphy/air60_v2/ansi/rules.mk
new file mode 100644
index 000000000000..457cb5558b7a
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/rules.mk
@@ -0,0 +1,10 @@
+SRC += user_kb.c
+SRC += rf.c
+SRC += rgb.c
+
+SRC += mcu_pwr.c sleep.c rf_driver.c rf_queue.c
+UART_DRIVER_REQUIRED = yes
+
+CUSTOM_MATRIX = lite
+SRC += matrix.c
+
diff --git a/keyboards/nuphy/air60_v2/ansi/sleep.c b/keyboards/nuphy/air60_v2/ansi/sleep.c
new file mode 100644
index 000000000000..1fcf4e312de1
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/sleep.c
@@ -0,0 +1,102 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#include "user_kb.h"
+#include "ansi.h"
+#include "hal_usb.h"
+#include "usb_main.h"
+#include "mcu_pwr.h"
+#include "rf_queue.h"
+
+void deep_sleep_handle(void) {
+ signal_sleep(0x00, dev_info.link_mode == LINK_RF_24 ? 0x80 : 0x00, dev_info.link_mode == LINK_RF_24 ? 0x00 : 0x80);
+ // Sync again before sleeping
+ dev_sts_sync();
+ enter_deep_sleep(); // puts the board in WFI mode and pauses the MCU
+#if (MCU_SLEEP_ENABLE)
+ exit_deep_sleep(); // This gets called when there is an interrupt (wake) event.
+#endif
+}
+
+/**
+ * @brief Sleep Handle.
+ */
+void sleep_handle(void) {
+ static uint32_t delay_step_timer = 0;
+ static uint8_t usb_suspend_debounce = 0;
+ static uint32_t rf_disconnect_time = 0;
+
+ if (user_config.sleep_mode == 0 || USB_ACTIVE) { return; }
+ if (timer_elapsed32(delay_step_timer) > (60 * 1000)) { no_act_time = 0; }
+
+ /* 500ms interval */
+ if (timer_elapsed32(delay_step_timer) < 500) { return; }
+ delay_step_timer = timer_read32();
+
+ if (user_config.sleep_mode != 1 || f_rf_sleep) {
+ f_goto_deepsleep = 0;
+ } else if (no_act_time >= deep_sleep_delay) {
+ f_goto_deepsleep = 1;
+ }
+
+ if (f_goto_deepsleep != 0) {
+ f_goto_deepsleep = 0;
+ f_goto_sleep = 0;
+ f_wakeup_prepare = 1;
+ f_rf_sleep = 1;
+ deep_sleep_handle();
+ return;
+ }
+
+ // sleep process
+ if (f_goto_sleep) {
+ f_goto_sleep = 0;
+ enter_light_sleep();
+ f_wakeup_prepare = 1;
+ }
+
+ // sleep check
+ if (f_goto_sleep || f_wakeup_prepare) {
+ return;
+ }
+
+ if (dev_info.link_mode == LINK_USB) {
+ if (USB_DRIVER.state == USB_SUSPENDED) {
+ usb_suspend_debounce++;
+ if (usb_suspend_debounce >= 2) {
+ f_goto_sleep = 1;
+ }
+ } else {
+ usb_suspend_debounce = 0;
+ }
+ } else if (no_act_time >= sleep_time_delay) {
+ f_goto_sleep = 1;
+ } else if (rf_linking_time >= (dev_info.link_mode == LINK_RF_24 ? (link_timeout / 4) : link_timeout)) {
+ f_goto_deepsleep = 1;
+ f_goto_sleep = 1;
+ rf_linking_time = 0;
+ } else if (dev_info.rf_state == RF_DISCONNECT) {
+ rf_disconnect_time++;
+ if (rf_disconnect_time > 5 * 2) {
+ f_goto_deepsleep = 1;
+ f_goto_sleep = 1;
+ rf_disconnect_time = 0;
+ }
+ } else if (dev_info.rf_state == RF_CONNECT) {
+ rf_disconnect_time = 0;
+ }
+}
diff --git a/keyboards/nuphy/air60_v2/ansi/user_kb.c b/keyboards/nuphy/air60_v2/ansi/user_kb.c
new file mode 100644
index 000000000000..d8312a7d4bc4
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/user_kb.c
@@ -0,0 +1,494 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#include "user_kb.h"
+#include "ansi.h"
+#include "mcu_pwr.h"
+#include "color.h"
+
+user_config_t user_config;
+DEV_INFO_STRUCT dev_info = {
+ .rf_battery = 100,
+ .link_mode = LINK_USB,
+ .rf_state = RF_IDLE,
+};
+bool f_bat_hold = 0;
+bool game_mode_enable = 0;
+bool f_send_channel = 0;
+bool f_dial_sw_init_ok = 0;
+bool f_rf_sw_press = 0;
+bool f_dev_reset_press = 0;
+bool f_rgb_test_press = 0;
+bool f_bat_num_show = 0;
+bool f_caps_word_tg = 0;
+bool f_numlock_press = 0;
+
+uint8_t rgb_required = 0;
+uint8_t rf_blink_cnt = 0;
+uint8_t rf_sw_temp = 0;
+uint8_t host_mode = 0;
+uint16_t rf_linking_time = 0;
+uint16_t rf_link_show_time = 0;
+uint32_t no_act_time = 0;
+uint16_t dev_reset_press_delay = 0;
+uint16_t rf_sw_press_delay = 0;
+uint16_t rgb_test_press_delay = 0;
+uint16_t caps_word_tg_delay = 0;
+uint16_t numlock_press_delay = 0;
+uint32_t sys_show_timer = 0;
+uint32_t sleep_show_timer = 0;
+
+host_driver_t *m_host_driver = 0;
+
+uint16_t link_timeout = NO_ACT_TIME_MINUTE;
+uint16_t sleep_time_delay = NO_ACT_TIME_MINUTE * 2;
+uint32_t deep_sleep_delay = NO_ACT_TIME_MINUTE * 6;
+uint32_t eeprom_update_timer = 0;
+bool user_update = 0;
+bool rgb_update = 0;
+
+extern host_driver_t rf_host_driver;
+
+/**
+ * @brief gpio initial.
+ */
+void gpio_init(void) {
+ /* power on all LEDs */
+ pwr_led_on();
+ /* config RF module pin */
+ gpio_set_pin_output(NRF_WAKEUP_PIN);
+ gpio_write_pin_high(NRF_WAKEUP_PIN);
+ /* set RF module boot pin high */
+ gpio_set_pin_input_high(NRF_BOOT_PIN);
+ /* reset RF module */
+ gpio_set_pin_output(NRF_RESET_PIN);
+ gpio_write_pin_low(NRF_RESET_PIN);
+ wait_ms(50);
+ gpio_write_pin_high(NRF_RESET_PIN);
+ /* config dial switch pin */
+ gpio_set_pin_input_high(DEV_MODE_PIN);
+ gpio_set_pin_input_high(SYS_MODE_PIN);
+}
+
+void set_link_mode(void) {
+ f_rf_sw_press = 0;
+
+ dev_info.link_mode = rf_sw_temp;
+ dev_info.rf_channel = rf_sw_temp;
+ dev_info.ble_channel = rf_sw_temp;
+}
+
+/**
+ * @brief long press key process.
+ */
+void custom_key_press(void) {
+ static uint32_t long_press_timer = 0;
+
+ if (timer_elapsed32(long_press_timer) < 100) { return; }
+ long_press_timer = timer_read32();
+
+ dial_sw_scan();
+
+ // Open a new RF device
+ if (f_rf_sw_press) {
+ rf_sw_press_delay++;
+ if (rf_sw_press_delay >= MEDIUM_PRESS_DELAY) {
+ set_link_mode();
+ uint8_t timeout = 5;
+ while (timeout--) {
+ uart_send_cmd(CMD_NEW_ADV, 0, 1);
+ wait_ms(20);
+ uart_receive_pro();
+ if (f_rf_new_adv_ok) { break; }
+ }
+ }
+ }
+
+ // The device is restored to factory Settings
+ if (f_dev_reset_press) {
+ dev_reset_press_delay++;
+ if (dev_reset_press_delay >= MEDIUM_PRESS_DELAY) {
+ f_dev_reset_press = 0;
+
+ if (dev_info.link_mode != LINK_RF_24) {
+ dev_info.ble_channel = LINK_BT_1;
+ dev_info.rf_channel = LINK_BT_1;
+ }
+
+ if (dev_info.link_mode != LINK_USB) {
+ if (dev_info.link_mode != LINK_RF_24) {
+ dev_info.link_mode = LINK_BT_1;
+ }
+ }
+
+ uart_send_cmd(CMD_SET_LINK, 10, 10);
+ wait_ms(500);
+ uart_send_cmd(CMD_CLR_DEVICE, 10, 10);
+
+ void device_reset_show(void);
+ void device_reset_init(void);
+
+ eeconfig_init();
+ device_reset_show();
+ device_reset_init();
+ eeconfig_update_rgb_matrix_default();
+
+ dev_info.sys_sw_state = 0;
+ dial_sw_fast_scan();
+
+ }
+ } else {
+ dev_reset_press_delay = 0;
+ }
+
+ // Enter the RGB test mode
+ if (f_rgb_test_press) {
+ rgb_test_press_delay++;
+ if (rgb_test_press_delay >= MEDIUM_PRESS_DELAY) {
+ f_rgb_test_press = 0;
+ rgb_test_show();
+ }
+ } else {
+ rgb_test_press_delay = 0;
+ }
+
+ // Trigger NumLock
+ if (f_numlock_press) {
+ numlock_press_delay++;
+ if (numlock_press_delay >= MICRO_PRESS_DELAY) {
+ tap_code(KC_NUM);
+ f_numlock_press = 0;
+ }
+ } else {
+ numlock_press_delay = 0;
+ }
+
+ if (f_caps_word_tg) {
+ caps_word_tg_delay++;
+ if (caps_word_tg_delay >= SMALL_PRESS_DELAY) {
+ user_config.caps_word_enable = !user_config.caps_word_enable;
+ f_caps_word_tg = 0;
+#ifndef NO_DEBUG
+ dprintf("caps_word_state: %s\n", user_config.caps_word_enable ? "ON" : "OFF");
+#endif
+ signal_rgb_led(user_config.caps_word_enable, CAPS_LED, CAPS_LED, CAPS_WORD_IDLE_TIMEOUT);
+ }
+ }
+ else {
+ caps_word_tg_delay = 0;
+ }
+
+}
+
+/**
+ * @brief Release all keys, clear keyboard report.
+ */
+void break_all_key(void) {
+ clear_keyboard();
+ void clear_report_buffer_and_queue(void);
+ clear_report_buffer_and_queue();
+}
+
+/**
+ * @brief switch device link mode.
+ * @param mode : link mode
+ */
+void switch_dev_link(uint8_t mode) {
+ if (mode > LINK_USB) { return; }
+ no_act_time = 0;
+ break_all_key();
+
+ dev_info.link_mode = mode;
+
+ dev_info.rf_state = RF_IDLE;
+ f_send_channel = 1;
+
+ if (mode == LINK_USB) {
+ host_mode = HOST_USB_TYPE;
+ host_set_driver(m_host_driver);
+ rf_link_show_time = 0;
+ } else {
+ host_mode = HOST_RF_TYPE;
+ host_set_driver(&rf_host_driver);
+ }
+}
+
+/**
+ * @brief read dial values
+ */
+uint8_t dial_read(void) {
+ uint8_t dial_scan = 0;
+ gpio_set_pin_input_high(DEV_MODE_PIN);
+ gpio_set_pin_input_high(SYS_MODE_PIN);
+
+ if (gpio_read_pin(DEV_MODE_PIN)) { dial_scan |= 0X01; }
+ if (gpio_read_pin(SYS_MODE_PIN)) { dial_scan |= 0X02; }
+
+ return dial_scan;
+}
+
+/**
+ * @brief set dial values based on input
+ * @param dial_scan : current dial input value
+ * @param led_sys_show : show system led
+ */
+void dial_set(uint8_t dial_scan, bool led_sys_show) {
+
+ if (dial_scan & 0x01) {
+ if (dev_info.link_mode != LINK_USB) {
+ switch_dev_link(LINK_USB);
+ }
+ } else {
+ if (dev_info.link_mode != dev_info.rf_channel) {
+ switch_dev_link(dev_info.rf_channel);
+ }
+ }
+
+ if (dial_scan & 0x02) {
+ if (dev_info.sys_sw_state != SYS_SW_MAC) {
+ if (led_sys_show) { sys_show_timer = timer_read32(); }
+ default_layer_set(1 << 0);
+ dev_info.sys_sw_state = SYS_SW_MAC;
+ keymap_config.nkro = 0;
+ }
+ } else {
+ if (dev_info.sys_sw_state != SYS_SW_WIN) {
+ if (led_sys_show) { sys_show_timer = timer_read32(); }
+ default_layer_set(1 << 3);
+ dev_info.sys_sw_state = SYS_SW_WIN;
+ keymap_config.nkro = 1;
+ }
+ }
+}
+
+/**
+ * @brief scan dial switch.
+ */
+void dial_sw_scan(void) {
+ uint8_t dial_scan = 0;
+ static uint8_t dial_save = 0xf0;
+ static uint8_t debounce = 0;
+
+ dial_scan = dial_read();
+
+ if (dial_save != dial_scan) {
+ break_all_key();
+
+ no_act_time = 0;
+ rf_linking_time = 0;
+ f_wakeup_prepare = 0;
+
+ dial_save = dial_scan;
+ debounce = 5;
+ f_dial_sw_init_ok = 0;
+ return;
+ } else if (debounce) {
+ debounce--;
+ return;
+ }
+
+ dial_set(dial_scan, true);
+
+ if (f_dial_sw_init_ok == 0) {
+ f_dial_sw_init_ok = 1;
+
+ if (dev_info.link_mode != LINK_USB) {
+ host_set_driver(&rf_host_driver);
+ }
+ }
+}
+
+/**
+ * @brief power on scan dial switch.
+ */
+void dial_sw_fast_scan(void) {
+ uint8_t dial_scan = 0;
+ uint8_t dial_check = 0xf0;
+ uint8_t debounce = 0;
+
+ // Debounce to get a stable state
+ for (debounce = 0; debounce < 10; debounce++) {
+ dial_scan = dial_read();
+ if (dial_check != dial_scan) {
+ dial_check = dial_scan;
+ debounce = 0;
+ }
+ wait_ms(1);
+ }
+
+ dial_set(dial_scan, false);
+}
+
+/**
+ * @brief timer process.
+ */
+void timer_pro(void) {
+ static uint32_t interval_timer = 0;
+ static bool f_first = true;
+
+ if (f_first) {
+ f_first = false;
+ interval_timer = timer_read32();
+ m_host_driver = host_get_driver();
+ }
+
+ // step 10ms
+ if (timer_elapsed32(interval_timer) < 10) { return; }
+ interval_timer = timer_read32();
+
+ if (rf_link_show_time < RF_LINK_SHOW_TIME) { rf_link_show_time++; }
+
+ if (no_act_time < 0xffffffff) { no_act_time++; }
+
+ if (rf_linking_time < 0xffff) { rf_linking_time++; }
+
+}
+
+/**
+ * @brief load eeprom data.
+ */
+void load_eeprom_data(void) {
+ eeconfig_read_kb_datablock(&user_config);
+ if (user_config.init_layer < 100) { user_config_reset(); }
+}
+
+void call_update_eeprom_data(bool* eeprom_update_init) {
+ *eeprom_update_init = 1;
+ eeprom_update_timer = 0;
+}
+
+/**
+ * @brief User config update to eeprom with delay
+ */
+void delay_update_eeprom_data(void) {
+ if (eeprom_update_timer == 0) {
+ if (user_update || rgb_update) { eeprom_update_timer = timer_read32(); }
+ return;
+ }
+ if (timer_elapsed32(eeprom_update_timer) < (1000 * 30)) { return; }
+ if (user_update) {
+#ifndef NO_DEBUG
+ dprint("Updating EEPROM: user_config\n");
+#endif
+ eeconfig_update_kb_datablock(&user_config);
+ user_update = 0;
+ }
+ if (rgb_update) {
+#ifndef NO_DEBUG
+ dprint("Updating EEPROM: rgb_config\n");
+#endif
+ eeconfig_update_rgb_matrix();
+ rgb_update = 0;
+ }
+ eeprom_update_timer = 0;
+}
+
+void game_mode_tweak(void)
+{
+ if (game_mode_enable) {
+ rgb_matrix_mode_noeeprom(RGB_MATRIX_GAME_MODE);
+ user_config.ee_side_mode = 2;
+ user_config.ee_side_rgb = 0;
+ user_config.ee_side_colour = SIDE_MATRIX_GAME_MODE;
+ if (user_config.numlock_state != 0) { user_config.numlock_state = 1; }
+ } else {
+ rgb_matrix_reload_from_eeprom();
+ eeconfig_read_kb_datablock(&user_config);
+ }
+ pwr_led_on();
+ signal_rgb_led(game_mode_enable, G_LED, G_LED, 2000);
+}
+
+#ifndef NO_DEBUG
+void user_debug(void) {
+ static uint32_t last_print = 0;
+ if (no_act_time == 0 || no_act_time == last_print) { return; }
+ if (no_act_time % 3000 == 0) {
+ if (!USB_ACTIVE && debug_enable) {
+ debug_enable = false;
+ print("DEBUG: disabled.\n");
+ }
+
+ last_print = no_act_time;
+ dprintf("no_act_time: %lds\n", no_act_time / 100);
+ }
+}
+#endif
+
+/**
+ * @brief User config to default setting.
+ */
+void user_config_reset(void) {
+ /* first power on, set rgb matrix brightness at middle level*/
+ // rgb_matrix_sethsv(RGB_HUE_INIT, 255, RGB_MATRIX_MAXIMUM_BRIGHTNESS - RGB_MATRIX_VAL_STEP * 2);
+
+ user_config.init_layer = 100;
+ user_config.ee_side_mode = 0;
+ user_config.ee_side_light = 1;
+ user_config.ee_side_speed = 2;
+ user_config.ee_side_rgb = 1;
+ user_config.ee_side_colour = 0;
+ user_config.ee_side_one = 0;
+ user_config.sleep_mode = 1;
+ user_config.caps_word_enable = 1;
+ user_config.numlock_state = 1;
+ keymap_config.no_gui = 0;
+ eeconfig_update_kb_datablock(&user_config);
+}
+
+/**
+ * @brief Handle LED power
+ * @note Turn off LEDs if not used to save some power. This is ported
+ * from older Nuphy leaks.
+ */
+
+void matrix_io_delay(void) {
+#if (MCU_SLEEP_ENABLE)
+ if (MATRIX_IO_DELAY == 0 || game_mode_enable == 1 || f_rf_sleep) {
+#else
+ if (MATRIX_IO_DELAY == 0 || game_mode_enable == 1) {
+#endif
+ __asm__ __volatile__("nop;nop;nop;nop;nop;nop;\n\t" ::: "memory"); // sleep 0.3125 us (312.5 ns)
+ return;
+ }
+
+ if (no_act_time > 3000) { wait_us(1200); }
+ else if (no_act_time > 1000) { wait_us(200); }
+ wait_us(MATRIX_IO_DELAY);
+}
+
+void led_power_handle(void) {
+ static uint32_t interval = 0;
+ static uint8_t led_debounce = 4;
+
+ if ((timer_elapsed32(interval) < 500 || f_wakeup_prepare || game_mode_enable) && rgb_required != 1) { // only check once in a while, less flickering for unhandled cases
+ return;
+ }
+
+ if ((rgb_matrix_is_enabled() && rgb_matrix_get_val() != 0) || !is_side_rgb_off() || rgb_required > 0) {
+ rgb_required = 0;
+ led_debounce = 4;
+ pwr_led_on();
+ } else if (led_debounce--) {
+ interval = timer_read32();
+ return;
+ } else if (timer_elapsed32(interval) > 500) { // brightness is 0 or RGB off.
+ pwr_led_off();
+ }
+
+ interval = timer_read32();
+}
diff --git a/keyboards/nuphy/air60_v2/ansi/user_kb.h b/keyboards/nuphy/air60_v2/ansi/user_kb.h
new file mode 100644
index 000000000000..7cac10d91fe5
--- /dev/null
+++ b/keyboards/nuphy/air60_v2/ansi/user_kb.h
@@ -0,0 +1,237 @@
+/*
+Copyright 2023 @ Nuphy
+
+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 .
+*/
+
+#pragma once
+
+#include "quantum.h"
+#include "usb_main.h"
+
+typedef enum {
+ RX_Idle,
+ RX_Receiving,
+ RX_Done,
+ RX_Fail,
+ RX_OV_ERR,
+ RX_SUM_ERR,
+ RX_CMD_ERR,
+ RX_DATA_ERR,
+ RX_DATA_OV,
+ RX_FORMAT_ERR,
+
+ TX_OK = 0XE0,
+ TX_DONE,
+ TX_BUSY,
+ TX_TIMEOUT,
+ TX_DATA_ERR,
+
+} TYPE_RX_STATE;
+
+
+#define RF_IDLE 0
+#define RF_PAIRING 1
+#define RF_LINKING 2
+#define RF_CONNECT 3
+#define RF_DISCONNECT 4
+#define RF_SLEEP 5
+#define RF_SNIF 6
+#define RF_INVAILD 0XFE
+#define RF_ERR_STATE 0XFF
+
+#define CMD_POWER_UP 0XF0
+#define CMD_SLEEP 0XF1
+#define CMD_HAND 0XF2
+#define CMD_SNIF 0XF3
+#define CMD_24G_SUSPEND 0XF4
+#define CMD_IDLE_EXIT 0XFE
+
+#define CMD_RPT_MS 0XE0
+#define CMD_RPT_BYTE_KB 0XE1
+#define CMD_RPT_BIT_KB 0XE2
+#define CMD_RPT_CONSUME 0XE3
+#define CMD_RPT_SYS 0XE4
+
+#define CMD_SET_LINK 0XC0
+#define CMD_SET_CONFIG 0XC1
+#define CMD_GET_CONFIG 0XC2
+#define CMD_SET_NAME 0XC3
+#define CMD_GET_NAME 0XC4
+#define CMD_CLR_DEVICE 0XC5
+#define CMD_NEW_ADV 0XC7
+#define CMD_RF_STS_SYSC 0XC9
+#define CMD_SET_24G_NAME 0XCA
+#define CMD_GO_TEST 0XCF
+#define CMD_RF_DFU 0XB1
+#define CMD_NULL 0X00
+
+#define CMD_WRITE_DATA 0X80
+#define CMD_READ_DATA 0X81
+
+#define LINK_RF_24 0
+#define LINK_BT_1 1
+#define LINK_BT_2 2
+#define LINK_BT_3 3
+#define LINK_USB 4
+
+#define UART_HEAD 0x5A
+#define FUNC_VALID_LEN 32
+#define UART_MAX_LEN 64
+
+#define SYS_SW_WIN 0xa1
+#define SYS_SW_MAC 0xa2
+
+#define RF_LINK_SHOW_TIME 300
+
+#define HOST_USB_TYPE 0
+#define HOST_BLE_TYPE 1
+#define HOST_RF_TYPE 2
+
+#define RF_POWER_DOWN_DELAY (30)
+
+#define MICRO_PRESS_DELAY 5
+#define SMALL_PRESS_DELAY 10
+#define MEDIUM_PRESS_DELAY 30
+#define LONG_PRESS_DELAY 50
+
+#define NO_ACT_TIME_MINUTE (60 * 100)
+
+#define RGB_MATRIX_GAME_MODE RGB_MATRIX_GRADIENT_LEFT_RIGHT
+#define SIDE_MATRIX_GAME_MODE 4
+
+#define CAPS_LED 28
+#define LSHIFT_LED 41
+#define WIN_LED 56
+#define NUMLOCK_LED 47
+#define G_LED 33
+
+#define USB_ACTIVE ((dev_info.link_mode == LINK_USB && USB_DRIVER.state != USB_SUSPENDED) || (dev_info.link_mode != LINK_USB && dev_info.rf_charge == 0x03))
+
+typedef struct
+{
+ uint8_t RXDState;
+ uint8_t RXDLen;
+ uint8_t RXDOverTime;
+ uint8_t TXDLenBack;
+ uint8_t TXDOffset;
+ uint8_t RXCmd;
+ uint8_t TXDBuf[UART_MAX_LEN];
+ uint8_t RXDBuf[UART_MAX_LEN];
+} USART_MGR_STRUCT;
+
+typedef struct
+{
+ uint8_t link_mode;
+ uint8_t rf_channel;
+ uint8_t ble_channel;
+ uint8_t rf_state;
+ uint8_t rf_charge;
+ uint8_t rf_led;
+ uint8_t rf_battery;
+ uint8_t sys_sw_state;
+} DEV_INFO_STRUCT;
+
+typedef struct
+{
+ uint8_t init_layer;
+ uint8_t ee_side_mode;
+ uint8_t ee_side_light;
+ uint8_t ee_side_speed;
+ uint8_t ee_side_rgb;
+ uint8_t ee_side_colour;
+ uint8_t ee_side_one;
+ uint8_t sleep_mode;
+ uint8_t caps_word_enable;
+ uint8_t numlock_state;
+ uint8_t retain1;
+ uint8_t retain2;
+} user_config_t;
+
+_Static_assert(sizeof(user_config_t) == EECONFIG_KB_DATA_SIZE, "Mismatch in user EECONFIG stored data");
+
+extern DEV_INFO_STRUCT dev_info;
+extern user_config_t user_config;
+
+extern uint8_t rf_blink_cnt;
+extern uint16_t rf_link_show_time;
+extern uint8_t rf_disconnect_delay;
+
+extern bool f_bat_hold;
+extern bool game_mode_enable;
+extern uint32_t sys_show_timer;
+extern uint32_t sleep_show_timer;
+extern bool f_rf_sw_press;
+extern bool f_dev_reset_press;
+extern bool f_bat_num_show;
+extern bool f_rgb_test_press;
+extern bool f_caps_word_tg;
+extern bool f_numlock_press;
+
+extern uint8_t rf_sw_temp;
+extern uint16_t rf_sw_press_delay;
+extern uint16_t rf_linking_time;
+extern bool f_rf_new_adv_ok;
+
+extern uint32_t no_act_time;
+extern uint16_t sleep_time_delay;
+extern uint32_t deep_sleep_delay;
+extern uint16_t link_timeout;
+extern bool f_rf_sleep;
+extern bool f_wakeup_prepare;
+extern bool f_goto_sleep;
+extern bool f_goto_deepsleep;
+
+
+extern uint32_t eeprom_update_timer;
+extern bool rgb_update;
+extern bool user_update;
+extern uint8_t rgb_required;
+
+extern bool is_side_rgb_off(void);
+
+void dev_sts_sync(void);
+void rf_uart_init(void);
+void rf_device_init(void);
+void uart_send_report_repeat(void);
+void uart_receive_pro(void);
+void uart_send_report(uint8_t report_type, uint8_t *report_buf, uint8_t report_size);
+void signal_sleep(uint8_t r, uint8_t g, uint8_t b);
+void side_speed_control(uint8_t dir);
+void side_light_control(uint8_t dir);
+void side_colour_control(uint8_t dir);
+void side_mode_control(uint8_t dir);
+void side_one_control(uint8_t dir);
+void led_show(void);
+void sleep_handle(void);
+void bat_num_led(void);
+void rgb_test_show(void);
+void gpio_init(void);
+void custom_key_press(void);
+void break_all_key(void);
+void switch_dev_link(uint8_t mode);
+void dial_sw_scan(void);
+void dial_sw_fast_scan(void);
+void timer_pro(void);
+void load_eeprom_data(void);
+void delay_update_eeprom_data(void);
+void user_config_reset(void);
+void led_power_handle(void);
+void set_link_mode(void);
+void matrix_io_delay(void);
+void game_mode_tweak(void);
+void user_debug(void);
+void call_update_eeprom_data(bool* eeprom_update_init);
+void signal_rgb_led(uint8_t state, uint8_t start_led, uint8_t end_led, uint16_t show_time);
+uint8_t uart_send_cmd(uint8_t cmd, uint8_t ack_cnt, uint8_t delayms);