summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--keyboards/annepro2/annepro2.c202
-rw-r--r--keyboards/annepro2/annepro2.h65
-rw-r--r--keyboards/annepro2/annepro2_ble.c170
-rw-r--r--keyboards/annepro2/annepro2_ble.h27
-rw-r--r--keyboards/annepro2/ap2_led.c134
-rw-r--r--keyboards/annepro2/ap2_led.h84
-rw-r--r--keyboards/annepro2/boards/ANNEPRO2_C15/board.c103
-rw-r--r--keyboards/annepro2/boards/ANNEPRO2_C15/board.h39
-rw-r--r--keyboards/annepro2/boards/ANNEPRO2_C15/board.mk5
-rw-r--r--keyboards/annepro2/boards/ANNEPRO2_C18/board.c103
-rw-r--r--keyboards/annepro2/boards/ANNEPRO2_C18/board.h39
-rw-r--r--keyboards/annepro2/boards/ANNEPRO2_C18/board.mk5
-rw-r--r--keyboards/annepro2/c15/config.h47
-rw-r--r--keyboards/annepro2/c15/readme.md1
-rw-r--r--keyboards/annepro2/c15/rules.mk37
-rw-r--r--keyboards/annepro2/c18/config.h45
-rw-r--r--keyboards/annepro2/c18/readme.md1
-rw-r--r--keyboards/annepro2/c18/rules.mk37
-rw-r--r--keyboards/annepro2/chconf.h31
-rw-r--r--keyboards/annepro2/halconf.h28
-rw-r--r--keyboards/annepro2/info.json330
-rw-r--r--keyboards/annepro2/keymaps/default-full-caps/config.h20
-rw-r--r--keyboards/annepro2/keymaps/default-full-caps/keymap.c120
-rw-r--r--keyboards/annepro2/keymaps/default-layer-indicators/config.h20
-rw-r--r--keyboards/annepro2/keymaps/default-layer-indicators/keymap.c147
-rw-r--r--keyboards/annepro2/keymaps/default/config.h20
-rw-r--r--keyboards/annepro2/keymaps/default/keymap.c106
-rw-r--r--keyboards/annepro2/ld/HT32F52342_ANNEPRO2.ld91
-rw-r--r--keyboards/annepro2/matrix.c63
-rw-r--r--keyboards/annepro2/mcuconf.h62
-rw-r--r--keyboards/annepro2/protocol.c116
-rw-r--r--keyboards/annepro2/protocol.h111
-rw-r--r--keyboards/annepro2/readme.md50
33 files changed, 2459 insertions, 0 deletions
diff --git a/keyboards/annepro2/annepro2.c b/keyboards/annepro2/annepro2.c
new file mode 100644
index 0000000000..37489defff
--- /dev/null
+++ b/keyboards/annepro2/annepro2.c
@@ -0,0 +1,202 @@
+/* Copyright 2021 OpenAnnePro community
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hal.h"
+#include "annepro2.h"
+#include "annepro2_ble.h"
+#include "spi_master.h"
+#include "ap2_led.h"
+#include "protocol.h"
+
+#define RAM_MAGIC_LOCATION 0x20001ffc
+#define IAP_MAGIC_VALUE 0x0000fab2
+
+static const SerialConfig ledUartInitConfig = {
+ .speed = 115200,
+};
+
+#ifndef LED_UART_BAUD_RATE
+# define LED_UART_BAUD_RATE 115200
+#endif // LED_UART_BAUD_RATE
+
+static const SerialConfig ledUartRuntimeConfig = {
+ .speed = LED_UART_BAUD_RATE,
+};
+
+static const SerialConfig bleUartConfig = {
+ .speed = 115200,
+};
+
+static uint8_t ledMcuWakeup[11] = {0x7b, 0x10, 0x43, 0x10, 0x03, 0x00, 0x00, 0x7d, 0x02, 0x01, 0x02};
+
+ble_capslock_t BLECapsLock = {._dummy = {0}, .caps_lock = false};
+
+void bootloader_jump(void) {
+ // Send msg to shine to boot into IAP
+ annepro2SetIAP();
+
+ // wait for shine to boot into IAP
+ wait_ms(15);
+
+ // Load ble into IAP
+ annepro2_ble_bootload();
+ wait_ms(15);
+
+ // Magic key to set keyboard to IAP
+ // It’s from reversing original boot loader
+ // If value is that it stays in boot loader aka IAP
+ *((uint32_t *)RAM_MAGIC_LOCATION) = IAP_MAGIC_VALUE;
+
+ // Load the main MCU into IAP
+ __disable_irq();
+ NVIC_SystemReset();
+}
+
+void keyboard_pre_init_kb(void) {
+ // Start LED UART
+ sdStart(&SD0, &ledUartInitConfig);
+ /* Let the LED chip settle a bit before switching the mode.
+ * That helped at least one person. */
+ wait_ms(15);
+ sdWrite(&SD0, ledMcuWakeup, sizeof(ledMcuWakeup));
+
+ // wait to receive response from wakeup
+ wait_ms(15);
+
+ protoInit(&proto, ledCommandCallback);
+
+ // loop to clear out receive buffer from shine wakeup
+ while (!sdGetWouldBlock(&SD0)) sdGet(&SD0);
+
+ sdStart(&SD0, &ledUartRuntimeConfig);
+ keyboard_pre_init_user();
+}
+
+void keyboard_post_init_kb(void) {
+ // Start BLE UART
+ sdStart(&SD1, &bleUartConfig);
+ annepro2_ble_startup();
+
+ // Give the send uart thread some time to
+ // send out the queue before we read back
+ wait_ms(100);
+
+ // loop to clear out receive buffer from ble wakeup
+ while (!sdGetWouldBlock(&SD1)) sdGet(&SD1);
+
+ annepro2LedGetStatus();
+
+ keyboard_post_init_user();
+}
+
+void matrix_scan_kb() {
+ // if there's stuff on the ble serial buffer
+ // read it into the capslock struct
+ while (!sdGetWouldBlock(&SD1)) {
+ sdReadTimeout(&SD1, (uint8_t *)&BLECapsLock, sizeof(ble_capslock_t), 10);
+ }
+
+ /* While there's data from LED keyboard sent - read it. */
+ while (!sdGetWouldBlock(&SD0)) {
+ uint8_t byte = sdGet(&SD0);
+ protoConsume(&proto, byte);
+ }
+
+ matrix_scan_user();
+}
+
+bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+ if (record->event.pressed) {
+ if (annepro2LedStatus.matrixEnabled && annepro2LedStatus.isReactive) {
+ annepro2LedForwardKeypress(record->event.key.row, record->event.key.col);
+ }
+
+ const annepro2Led_t blue = {
+ .p.blue = 0xff,
+ .p.red = 0x00,
+ .p.green = 0x00,
+ .p.alpha = 0xff,
+ };
+
+ switch (keycode) {
+ case KC_AP2_BT1:
+ annepro2_ble_broadcast(0);
+ /* FIXME: This hardcodes col/row position */
+ annepro2LedBlink(0, 1, blue, 8, 50);
+ return false;
+
+ case KC_AP2_BT2:
+ annepro2_ble_broadcast(1);
+ annepro2LedBlink(0, 2, blue, 8, 50);
+ return false;
+
+ case KC_AP2_BT3:
+ annepro2_ble_broadcast(2);
+ annepro2LedBlink(0, 3, blue, 8, 50);
+ return false;
+
+ case KC_AP2_BT4:
+ annepro2_ble_broadcast(3);
+ annepro2LedBlink(0, 4, blue, 8, 50);
+ return false;
+
+ case KC_AP2_USB:
+ annepro2_ble_disconnect();
+ return false;
+
+ case KC_AP2_BT_UNPAIR:
+ annepro2_ble_unpair();
+ return false;
+
+ case KC_AP_LED_OFF:
+ annepro2LedDisable();
+ break;
+
+ case KC_AP_LED_ON:
+ if (annepro2LedStatus.matrixEnabled) {
+ annepro2LedNextProfile();
+ } else {
+ annepro2LedEnable();
+ }
+ annepro2LedResetForegroundColor();
+ break;
+
+ case KC_AP_LED_NEXT_PROFILE:
+ annepro2LedNextProfile();
+ annepro2LedResetForegroundColor();
+ break;
+
+ case KC_AP_LED_PREV_PROFILE:
+ annepro2LedPrevProfile();
+ annepro2LedResetForegroundColor();
+ break;
+
+ case KC_AP_LED_NEXT_INTENSITY:
+ annepro2LedNextIntensity();
+ annepro2LedResetForegroundColor();
+ return false;
+
+ case KC_AP_LED_SPEED:
+ annepro2LedNextAnimationSpeed();
+ annepro2LedResetForegroundColor();
+ return false;
+
+ default:
+ break;
+ }
+ }
+ return process_record_user(keycode, record);
+}
diff --git a/keyboards/annepro2/annepro2.h b/keyboards/annepro2/annepro2.h
new file mode 100644
index 0000000000..b08f8c5352
--- /dev/null
+++ b/keyboards/annepro2/annepro2.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 Yaotian Feng
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include "quantum.h"
+#include <stdint.h>
+#include "ap2_led.h"
+
+typedef struct __attribute__((__packed__)) {
+ uint8_t _dummy[10];
+ bool caps_lock;
+} ble_capslock_t;
+extern ble_capslock_t BLECapsLock;
+
+// Matrix keymap
+// clang-format off
+#define LAYOUT( \
+ K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, \
+ K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, \
+ K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, \
+ K30, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, \
+ K40, K42, K43, K46, K49, K4A, K4B, K4C \
+) { \
+ /* COL1 COL2 COL3 COL4 COL5 COL6 COL7 COL8 COL9 COL10 COL11 COL12 COL13 COL14*/ \
+ /* ROW1 */ { K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D }, \
+ /* ROW2 */ { K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D }, \
+ /* ROW3 */ { K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, KC_NO}, \
+ /* ROW4 */ { K30, KC_NO, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, KC_NO}, \
+ /* ROW5 */ { K40, KC_NO, K42, K43, KC_NO, KC_NO, K46, KC_NO, KC_NO, K49, K4A, K4B, K4C, KC_NO}, \
+}
+// clang-format on
+
+enum AP2KeyCodes {
+ KC_AP2_BT1 = SAFE_RANGE,
+ KC_AP2_BT2,
+ KC_AP2_BT3,
+ KC_AP2_BT4,
+ KC_AP2_BT_UNPAIR,
+ KC_AP2_USB,
+ KC_AP_LED_ON,
+ KC_AP_LED_OFF,
+ KC_AP_LED_NEXT_PROFILE,
+ KC_AP_LED_PREV_PROFILE,
+ KC_AP_LED_NEXT_INTENSITY,
+ KC_AP_LED_SPEED,
+ AP2_SAFE_RANGE,
+};
+
+#undef SAFE_RANGE
+#define SAFE_RANGE AP2_SAFE_RANGE
+
diff --git a/keyboards/annepro2/annepro2_ble.c b/keyboards/annepro2/annepro2_ble.c
new file mode 100644
index 0000000000..72cbb68016
--- /dev/null
+++ b/keyboards/annepro2/annepro2_ble.c
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 2020 Yaotian Feng, Codetector<codetector@codetector.cn>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "annepro2_ble.h"
+#include "ch.h"
+#include "hal.h"
+#include "host.h"
+#include "host_driver.h"
+#include "report.h"
+
+/* -------------------- Static Function Prototypes -------------------------- */
+static uint8_t ap2_ble_leds(void);
+static void ap2_ble_mouse(report_mouse_t *report);
+static void ap2_ble_system(uint16_t data);
+static void ap2_ble_consumer(uint16_t data);
+static void ap2_ble_keyboard(report_keyboard_t *report);
+
+static void ap2_ble_swtich_ble_driver(void);
+
+/* -------------------- Static Local Variables ------------------------------ */
+static host_driver_t ap2_ble_driver = {
+ ap2_ble_leds, ap2_ble_keyboard, ap2_ble_mouse, ap2_ble_system, ap2_ble_consumer,
+};
+
+static uint8_t bleMcuWakeup[11] = {0x7b, 0x12, 0x53, 0x00, 0x03, 0x00, 0x01, 0x7d, 0x02, 0x01, 0x02};
+
+static uint8_t bleMcuStartBroadcast[11] = {
+ 0x7b, 0x12, 0x53, 0x00, 0x03, 0x00, 0x00, 0x7d, 0x40, 0x01, 0x00 // Broadcast ID[0-3]
+};
+
+static uint8_t bleMcuConnect[11] = {
+ 0x7b, 0x12, 0x53, 0x00, 0x03, 0x00, 0x00, 0x7d, 0x40, 0x04, 0x00 // Connect ID [0-3]
+};
+
+static uint8_t bleMcuSendReport[10] = {
+ 0x7b, 0x12, 0x53, 0x00, 0x0A, 0x00, 0x00, 0x7d, 0x10, 0x04,
+};
+
+static uint8_t bleMcuSendConsumerReport[10] = {
+ 0x7b, 0x12, 0x53, 0x00, 0x06, 0x00, 0x00, 0x7d, 0x10, 0x08,
+};
+
+static uint8_t bleMcuUnpair[10] = {
+ 0x7b, 0x12, 0x53, 0x00, 0x02, 0x00, 0x00, 0x7d, 0x40, 0x05,
+};
+
+static uint8_t bleMcuBootload[11] = {0x7b, 0x10, 0x51, 0x10, 0x03, 0x00, 0x00, 0x7d, 0x02, 0x01, 0x01};
+
+static host_driver_t *lastHostDriver = NULL;
+#ifdef NKRO_ENABLE
+static bool lastNkroStatus = false;
+#endif // NKRO_ENABLE
+
+/* -------------------- Public Function Implementation ---------------------- */
+
+void annepro2_ble_bootload(void) { sdWrite(&SD1, bleMcuBootload, sizeof(bleMcuBootload)); }
+
+void annepro2_ble_startup(void) { sdWrite(&SD1, bleMcuWakeup, sizeof(bleMcuWakeup)); }
+
+void annepro2_ble_broadcast(uint8_t port) {
+ if (port > 3) {
+ port = 3;
+ }
+ // sdPut(&SD1, 0x00);
+ sdWrite(&SD1, bleMcuStartBroadcast, sizeof(bleMcuStartBroadcast));
+ sdPut(&SD1, port);
+ static int lastBroadcast = -1;
+ if (lastBroadcast == port) {
+ annepro2_ble_connect(port);
+ }
+ lastBroadcast = port;
+}
+
+void annepro2_ble_connect(uint8_t port) {
+ if (port > 3) {
+ port = 3;
+ }
+ sdWrite(&SD1, bleMcuConnect, sizeof(bleMcuConnect));
+ sdPut(&SD1, port);
+ ap2_ble_swtich_ble_driver();
+}
+
+void annepro2_ble_disconnect(void) {
+ /* Skip if the driver is already enabled */
+ if (host_get_driver() != &ap2_ble_driver) {
+ return;
+ }
+
+ clear_keyboard();
+#ifdef NKRO_ENABLE
+ keymap_config.nkro = lastNkroStatus;
+#endif
+ host_set_driver(lastHostDriver);
+}
+
+void annepro2_ble_unpair(void) {
+ // sdPut(&SD1, 0x0);
+ sdWrite(&SD1, bleMcuUnpair, sizeof(bleMcuUnpair));
+}
+
+/* ------------------- Static Function Implementation ----------------------- */
+static void ap2_ble_swtich_ble_driver(void) {
+ if (host_get_driver() == &ap2_ble_driver) {
+ return;
+ }
+ clear_keyboard();
+ lastHostDriver = host_get_driver();
+#ifdef NKRO_ENABLE
+ lastNkroStatus = keymap_config.nkro;
+#endif
+ keymap_config.nkro = false;
+ host_set_driver(&ap2_ble_driver);
+}
+
+static uint8_t ap2_ble_leds(void) {
+ return 0; // TODO: Figure out how to obtain LED status
+}
+
+static void ap2_ble_mouse(report_mouse_t *report) {}
+
+static void ap2_ble_system(uint16_t data) {}
+
+static inline uint16_t CONSUMER2AP2(uint16_t usage) {
+ switch (usage) {
+ case AUDIO_VOL_DOWN:
+ return 0x04;
+ case AUDIO_VOL_UP:
+ return 0x02;
+ case AUDIO_MUTE:
+ return 0x01;
+ case TRANSPORT_PLAY_PAUSE:
+ return 0x08;
+ case TRANSPORT_NEXT_TRACK:
+ return 0x10;
+ case TRANSPORT_PREV_TRACK:
+ return 0x20;
+ default:
+ return 0x00;
+ }
+}
+
+static void ap2_ble_consumer(uint16_t data) {
+ sdPut(&SD1, 0x0);
+ sdWrite(&SD1, bleMcuSendConsumerReport, sizeof(bleMcuSendConsumerReport));
+ sdPut(&SD1, CONSUMER2AP2(data));
+ static const uint8_t dummy[3] = {0};
+ sdWrite(&SD1, dummy, sizeof(dummy));
+}
+
+/*!
+ * @brief Send keyboard HID report for Bluetooth driver
+ */
+static void ap2_ble_keyboard(report_keyboard_t *report) {
+ sdPut(&SD1, 0x0);
+ sdWrite(&SD1, bleMcuSendReport, sizeof(bleMcuSendReport));
+ sdWrite(&SD1, &report->raw[0], KEYBOARD_REPORT_SIZE);
+}
diff --git a/keyboards/annepro2/annepro2_ble.h b/keyboards/annepro2/annepro2_ble.h
new file mode 100644
index 0000000000..0cfb68e071
--- /dev/null
+++ b/keyboards/annepro2/annepro2_ble.h
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 2020 Yaotian Feng, Codetector<codetector@codetector.cn>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#pragma once
+
+#include "annepro2.h"
+#include "quantum.h"
+
+void annepro2_ble_bootload(void);
+void annepro2_ble_startup(void);
+void annepro2_ble_broadcast(uint8_t port);
+void annepro2_ble_connect(uint8_t port);
+void annepro2_ble_disconnect(void);
+void annepro2_ble_unpair(void);
diff --git a/keyboards/annepro2/ap2_led.c b/keyboards/annepro2/ap2_led.c
new file mode 100644
index 0000000000..9969fcd02a
--- /dev/null
+++ b/keyboards/annepro2/ap2_led.c
@@ -0,0 +1,134 @@
+/* Copyright 2021 OpenAnnePro community
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "hal.h"
+#include "annepro2.h"
+#include "ap2_led.h"
+#include "protocol.h"
+
+annepro2Led_t ledMask[KEY_COUNT];
+annepro2LedStatus_t annepro2LedStatus;
+
+void ledCommandCallback(const message_t *msg) {
+ switch (msg->command) {
+ case CMD_LED_STATUS:
+ annepro2LedStatus.amountOfProfiles = msg->payload[0];
+ annepro2LedStatus.currentProfile = msg->payload[1];
+ annepro2LedStatus.matrixEnabled = msg->payload[2];
+ annepro2LedStatus.isReactive = msg->payload[3];
+ annepro2LedStatus.ledIntensity = msg->payload[4];
+ annepro2LedStatus.errors = msg->payload[5];
+ break;
+
+#ifdef CONSOLE_ENABLE
+ case CMD_LED_DEBUG:
+ /* TODO: Don't use printf. */
+ printf("LED:");
+ for (int i = 0; i < msg->payloadSize; i++) {
+ printf("%02x ", msg->payload[i]);
+ }
+ for (int i = 0; i < msg->payloadSize; i++) {
+ printf("%c", msg->payload[i]);
+ }
+ printf("\n");
+ break;
+#endif
+ }
+}
+
+void annepro2SetIAP(void) { protoTx(CMD_LED_IAP, NULL, 0, 3); }
+
+void annepro2LedDisable(void) { protoTx(CMD_LED_OFF, NULL, 0, 3); }
+
+void annepro2LedEnable(void) { protoTx(CMD_LED_ON, NULL, 0, 3); }
+
+void annepro2LedSetProfile(uint8_t prof) { protoTx(CMD_LED_SET_PROFILE, &prof, sizeof(prof), 3); }
+
+void annepro2LedGetStatus() { protoTx(CMD_LED_GET_STATUS, NULL, 0, 3); }
+
+void annepro2LedNextProfile() { protoTx(CMD_LED_NEXT_PROFILE, NULL, 0, 3); }
+
+void annepro2LedNextIntensity() { protoTx(CMD_LED_NEXT_INTENSITY, NULL, 0, 3); }
+
+void annepro2LedNextAnimationSpeed() { protoTx(CMD_LED_NEXT_ANIMATION_SPEED, NULL, 0, 3); }
+
+void annepro2LedPrevProfile() { protoTx(CMD_LED_PREV_PROFILE, NULL, 0, 3); }
+
+void annepro2LedMaskSetKey(uint8_t row, uint8_t col, annepro2Led_t color) {
+ uint8_t payload[] = {row, col, color.p.blue, color.p.green, color.p.red, color.p.alpha};
+ protoTx(CMD_LED_MASK_SET_KEY, payload, sizeof(payload), 1);
+}
+
+/* Push a whole local row to the shine */
+void annepro2LedMaskSetRow(uint8_t row) {
+ uint8_t payload[NUM_COLUMN * sizeof(annepro2Led_t) + 1];
+ payload[0] = row;
+ memcpy(payload + 1, &ledMask[ROWCOL2IDX(row, 0)], sizeof(*ledMask) * NUM_COLUMN);
+ protoTx(CMD_LED_MASK_SET_ROW, payload, sizeof(payload), 1);
+}
+
+/* Synchronize all rows */
+void annepro2LedMaskSetAll(void) {
+ for (int row = 0; row < 5; row++) annepro2LedMaskSetRow(row);
+}
+
+/* Set all keys to a given color */
+void annepro2LedMaskSetMono(const annepro2Led_t color) { protoTx(CMD_LED_MASK_SET_MONO, (uint8_t *)&color, sizeof(color), 1); }
+
+void annepro2LedBlink(uint8_t row, uint8_t col, annepro2Led_t color, uint8_t count, uint8_t hundredths) {
+ uint8_t payload[] = {row, col, color.p.blue, color.p.green, color.p.red, color.p.alpha, count, hundredths};
+ protoTx(CMD_LED_KEY_BLINK, payload, sizeof(payload), 1);
+}
+
+void annepro2LedSetForegroundColor(uint8_t red, uint8_t green, uint8_t blue) {
+ annepro2Led_t color = {.p.red = red, .p.green = green, .p.blue = blue, .p.alpha = 0xff};
+ annepro2LedMaskSetMono(color);
+}
+
+void annepro2LedResetForegroundColor() {
+ annepro2Led_t color = {
+ .p.red = 0,
+ .p.green = 0,
+ .p.blue = 0,
+ .p.alpha = 0,
+ };
+ annepro2LedMaskSetMono(color);
+}
+
+/*
+ * Currently keypresses are unified with other messages, still with single 1
+ * byte payload. Transfer is normally fast enough for that to not be a problem -
+ * especially with asynchronous message reading.
+ *
+ *
+ * Previous description:
+ * If enabled, this data is sent to LED MCU on every keypress.
+ * In order to improve performance, both row and column values
+ * are packed into a single byte.
+ * Row range is [0, 4] and requires only 3 bits.
+ * Column range is [0, 13] and requires 4 bits.
+ *
+ * In order to differentiate this command from regular commands,
+ * the leftmost bit is set to 1 (0b10000000).
+ * Following it are 3 bits of row and 4 bits of col.
+ * 1 + 3 + 4 = 8 bits - only a single byte is sent for every keypress.
+ */
+void annepro2LedForwardKeypress(uint8_t row, uint8_t col) {
+ const uint8_t payload = row << 4 | col;
+ protoTx(CMD_LED_KEY_DOWN, &payload, 1, 1);
+}
diff --git a/keyboards/annepro2/ap2_led.h b/keyboards/annepro2/ap2_led.h
new file mode 100644
index 0000000000..23712a2555
--- /dev/null
+++ b/keyboards/annepro2/ap2_led.h
@@ -0,0 +1,84 @@
+ /* Copyright 2021 OpenAnnePro community
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "protocol.h"
+
+// Struct defining an LED and its RGB color components
+// Compatible with Shine firmware.
+typedef union {
+ struct {
+ /* Little endian ordering to match uint32_t */
+ uint8_t blue, green, red;
+ /* Used in mask; nonzero means - use color from mask. */
+ uint8_t alpha;
+ } p; /* parts */
+ /* Parts vector access: 0 - blue, 1 - green, 2 - red */
+ uint8_t pv[4];
+ /* 0xrgb in mem is b g r a */
+ uint32_t rgb;
+} annepro2Led_t;
+
+#define ROWCOL2IDX(row, col) (NUM_COLUMN * (row) + (col))
+#define NUM_COLUMN 14
+#define NUM_ROW 5
+#define KEY_COUNT 70
+
+/* Local copy of ledMask, used to override colors on the board */
+extern annepro2Led_t ledMask[KEY_COUNT];
+
+/* Handle incoming messages */
+extern void ledCommandCallback(const message_t *msg);
+
+void annepro2SetIAP(void);
+void annepro2LedDisable(void);
+void annepro2LedEnable(void);
+void annepro2LedSetProfile(uint8_t prof);
+void annepro2LedGetStatus(void);
+void annepro2LedNextProfile(void);
+void annepro2LedPrevProfile(void);
+void annepro2LedNextIntensity(void);
+void annepro2LedNextAnimationSpeed(void);
+void annepro2LedForwardKeypress(uint8_t row, uint8_t col);
+
+/* Set single key to a given color; alpha controls which is displayed */
+void annepro2LedMaskSetKey(uint8_t row, uint8_t col, annepro2Led_t color);
+/* Push a whole local row to the shine */
+void annepro2LedMaskSetRow(uint8_t row);
+/* Synchronize all rows */
+void annepro2LedMaskSetAll(void);
+
+/* Set all keys to a given color */
+void annepro2LedMaskSetMono(annepro2Led_t color);
+
+/* Blink given key `count` times by masking it with a `color`. Blink takes `hundredths` of a second */
+void annepro2LedBlink(uint8_t row, uint8_t col, annepro2Led_t color, uint8_t count, uint8_t hundredths);
+
+/* Kept for compatibility, but implemented using masks */
+void annepro2LedSetForegroundColor(uint8_t red, uint8_t green, uint8_t blue);
+void annepro2LedResetForegroundColor(void);
+
+typedef struct {
+ uint8_t amountOfProfiles;
+ uint8_t currentProfile;
+ uint8_t matrixEnabled;
+ uint8_t isReactive;
+ uint8_t ledIntensity;
+ uint8_t errors;
+} annepro2LedStatus_t;
+
+extern annepro2LedStatus_t annepro2LedStatus;
diff --git a/keyboards/annepro2/boards/ANNEPRO2_C15/board.c b/keyboards/annepro2/boards/ANNEPRO2_C15/board.c
new file mode 100644
index 0000000000..60c1826155
--- /dev/null
+++ b/keyboards/annepro2/boards/ANNEPRO2_C15/board.c
@@ -0,0 +1,103 @@
+/*
+ Copyright (C) 2020 Yaotian Feng, Codetector<codetector@codetector.cn>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "hal.h"
+
+/* ============ Private Defines ===================== */
+
+/* ============ Function Prototypes ================== */
+
+#define PBIT(PORT, LINE) ((PAL_PORT(LINE) == PORT) ? (1 << PAL_PAD(LINE)) : 0)
+#define PAFIO_L(PORT, LINE, AF) (((PAL_PORT(LINE) == PORT) && (PAL_PAD(LINE) < 8)) ? (AF << (PAL_PAD(LINE) << 2)) : 0)
+#define PAFIO_H(PORT, LINE, AF) (((PAL_PORT(LINE) == PORT) && (PAL_PAD(LINE) >= 8)) ? (AF << ((PAL_PAD(LINE) - 8) << 2)) : 0)
+#define PAFIO(PORT, N, LINE, AF) ((N) ? PAFIO_H(PORT, LINE, AF) : PAFIO_L(PORT, LINE, AF))
+
+#define OUT_BITS(PORT) (PBIT(PORT, C2) | PBIT(PORT, C1) | PBIT(PORT, B5) | PBIT(PORT, B4) | PBIT(PORT, C3) | 0)
+
+#define IN_BITS(PORT) (PBIT(PORT, C4) | PBIT(PORT, C5) | PBIT(PORT, B10) | PBIT(PORT, B11) | PBIT(PORT, C0) | PBIT(PORT, A15) | PBIT(PORT, A8) | PBIT(PORT, A10) | PBIT(PORT, A11) | PBIT(PORT, A12) | PBIT(PORT, A13) | PBIT(PORT, A14) | PBIT(PORT, B2) | PBIT(PORT, B3) | 0)
+
+// Alternate Functions
+#define AF_BITS(PORT, N) (PAFIO(PORT, N, LINE_UART_RX, AFIO_USART) | PAFIO(PORT, N, LINE_UART_TX, AFIO_USART) | PAFIO(PORT, N, LINE_BT_UART_TX, AFIO_USART) | PAFIO(PORT, N, LINE_BT_UART_RX, AFIO_USART) | PAFIO(PORT, N, C2, AFIO_GPIO) | PAFIO(PORT, N, C1, AFIO_GPIO) | PAFIO(PORT, N, B5, AFIO_GPIO) | PAFIO(PORT, N, B4, AFIO_GPIO) | PAFIO(PORT, N, C3, AFIO_GPIO) | PAFIO(PORT, N, C4, AFIO_GPIO) | PAFIO(PORT, N, C5, AFIO_GPIO) | PAFIO(PORT, N, B10, AFIO_GPIO) | PAFIO(PORT, N, B11, AFIO_GPIO) | PAFIO(PORT, N, C0, AFIO_GPIO) | PAFIO(PORT, N, A15, AFIO_GPIO) | PAFIO(PORT, N, A8, AFIO_GPIO) | PAFIO(PORT, N, A10, AFIO_GPIO) | PAFIO(PORT, N, A11, AFIO_GPIO) | PAFIO(PORT, N, A12, AFIO_GPIO) | PAFIO(PORT, N, A13, AFIO_GPIO) | PAFIO(PORT, N, A14, AFIO_GPIO) | PAFIO(PORT, N, B2, AFIO_GPIO) | PAFIO(PORT, N, B3, AFIO_GPIO) | 0)
+
+/**
+ * @brief PAL setup.
+ * @details Digital I/O ports static configuration as defined in @p board.h.
+ * This variable is used by the HAL when initializing the PAL driver.
+ */
+const PALConfig pal_default_config = {
+ // GPIO A
+ .setup[0] =
+ {
+ .DIR = OUT_BITS(IOPORTA),
+ .INE = IN_BITS(IOPORTA),
+ .PU = IN_BITS(IOPORTA),
+ .PD = 0x0000,
+ .OD = 0x0000,
+ .DRV = 0x0000,
+ .LOCK = 0x0000,
+ .OUT = 0x0000,
+ .CFG[0] = AF_BITS(IOPORTA, 0),
+