#include \"esp_lcd_panel_ops.h\"\n#include \"freertos/FreeRTOS.h\"\n#include \"freertos/task.h\"\n#include \"freertos/queue.h\"\n#include \"driver/spi_master.h\"\n#include \"esp_lcd_panel_io.h\"\n#include \"esp_lcd_panel_vendor.h\"\n#include \"esp_lcd_panel_interface.h\"\n#include \"esp_log.h\"\n#include \"esp_timer.h\"\n#include \"usb/usb_host.h\"\n#include \"usb/hid_host.h\"\n#include \"usb/hid_usage_keyboard.h\"\n#include \u003Cstdlib.h>\n#include \u003Cstring.h>\n#include \u003Cstdbool.h>\n\n// Screen orientation (1=landscape, 0=portrait)\n#define SCREEN_ORIENTATION 1 // 1: landscape, 0: portrait\n\n// Hardware config\n#define LCD_HOST SPI2_HOST\n#define PIN_NUM_MOSI 11\n#define PIN_NUM_CLK 19\n#define PIN_NUM_CS 5\n#define PIN_NUM_DC 2\n#define PIN_NUM_RST 18\n\n// Size by orientation\n#if SCREEN_ORIENTATION == 1\n #define LCD_WIDTH 320\n #define LCD_HEIGHT 240\n#else\n #define LCD_WIDTH 240\n #define LCD_HEIGHT 320\n#endif\n\n#define CHAR_WIDTH 6 // character width (5 + 1 spacing)\n#define CHAR_HEIGHT 8 // character height (7 + 1 spacing)\n#define TEXT_LINE1_Y 30 // first line Y\n#define TEXT_LINE2_Y 50 // second line Y\n#define TEXT_INPUT_Y 80 // input area Y\n\nstatic const char *TAG = \"keyboard_display\";\n\n// 全局变量\nstatic esp_lcd_panel_handle_t lcd_panel = NULL;\nstatic QueueHandle_t hid_event_queue = NULL;\n\nstatic char display_text[100] = \"Input: \";\nstatic int text_length = 7;\n\n// Typing speed stats\nstatic int typed_chars = 0; // total typed characters\n\n// Practice target text\nstatic const char *target_text = \"hello world\";\nstatic const char *practice_list[] = {\n \"hello world\",\n \"practice typing fast\",\n \"education for everyone\",\n \"keyboard input on lcd\",\n \"typing trainer project\"\n};\nstatic int practice_index = 0; // current practice sentence index\n\n// Accuracy stats\nstatic int correct_chars = 0; // correct characters\nstatic int wrong_chars = 0; // wrong characters\nstatic int start_time_ms = 0; // start time (ms)\n\n// Colors (globals)\nstatic uint16_t bg_color = 0x0000;\nstatic uint16_t text_color = 0xFFFF;\n\n// 函数声明\nvoid draw_char(int x, int y, char c, uint16_t color, uint16_t bg);\nvoid draw_string(int x, int y, const char *str, uint16_t color, uint16_t bg_color);\nvoid clear_screen(uint16_t bg_color);\nvoid update_input_display(void);\nvoid set_target_text(const char *new_text);\nstatic void handle_keyboard_input_char(char input_char);\n\n\n// 5x7字体数据\nconst uint8_t font5x7[] = {\n 0x0E,0x11,0x11,0x1F,0x11,0x11,0x11, // A\n 0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E, // B\n 0x0E,0x11,0x10,0x10,0x10,0x11,0x0E, // C\n 0x1E,0x11,0x11,0x11,0x11,0x11,0x1E, // D\n 0x1F,0x10,0x10,0x1F,0x10,0x10,0x1F, // E\n 0x1F,0x10,0x10,0x1F,0x10,0x10,0x10, // F\n 0x0E,0x11,0x10,0x13,0x11,0x11,0x0F, // G\n 0x11,0x11,0x11,0x1F,0x11,0x11,0x11, // H\n 0x0E,0x04,0x04,0x04,0x04,0x04,0x0E, // I\n 0x07,0x02,0x02,0x02,0x02,0x12,0x0C, // J\n 0x11,0x12,0x14,0x18,0x14,0x12,0x11, // K\n 0x10,0x10,0x10,0x10,0x10,0x10,0x1F, // L\n 0x11,0x1B,0x15,0x15,0x11,0x11,0x11, // M\n 0x11,0x19,0x19,0x15,0x13,0x13,0x11, // N\n 0x0E,0x11,0x11,0x11,0x11,0x11,0x0E, // O\n 0x1E,0x11,0x11,0x1E,0x10,0x10,0x10, // P\n 0x0E,0x11,0x11,0x11,0x15,0x12,0x0D, // Q\n 0x1E,0x11,0x11,0x1E,0x14,0x12,0x11, // R\n 0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E, // S\n 0x1F,0x04,0x04,0x04,0x04,0x04,0x04, // T\n 0x11,0x11,0x11,0x11,0x11,0x11,0x0E, // U\n 0x11,0x11,0x11,0x11,0x11,0x0A,0x04, // V\n 0x11,0x11,0x11,0x15,0x15,0x15,0x0A, // W\n 0x11,0x11,0x0A,0x04,0x0A,0x11,0x11, // X\n 0x11,0x11,0x0A,0x04,0x04,0x04,0x04, // Y\n 0x1F,0x01,0x02,0x04,0x08,0x10,0x1F, // Z\n 0x0E,0x11,0x13,0x15,0x19,0x11,0x0E, // 0\n 0x04,0x0C,0x04,0x04,0x04,0x04,0x0E, // 1\n 0x0E,0x11,0x01,0x02,0x04,0x08,0x1F, // 2\n 0x1F,0x02,0x04,0x02,0x01,0x11,0x0E, // 3\n 0x02,0x06,0x0A,0x12,0x1F,0x02,0x02, // 4\n 0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E, // 5\n 0x06,0x08,0x10,0x1E,0x11,0x11,0x0E, // 6\n 0x1F,0x01,0x02,0x04,0x08,0x08,0x08, // 7\n 0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E, // 8\n 0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C, // 9\n 0x00,0x00,0x00,0x00,0x00,0x00,0x00 // 空格\n};\n\n// 键码到字符映射表\nconst uint8_t keycode2ascii[57][2] = {\n {0,0},{0,0},{0,0},{0,0},\n {'a','A'},{'b','B'},{'c','C'},{'d','D'},{'e','E'},{'f','F'},{'g','G'},{'h','H'},{'i','I'},{'j','J'},{'k','K'},{'l','L'},{'m','M'},{'n','N'},{'o','O'},{'p','P'},{'q','Q'},{'r','R'},{'s','S'},{'t','T'},{'u','U'},{'v','V'},{'w','W'},{'x','X'},{'y','Y'},{'z','Z'},\n {'1','!'},{'2','@'},{'3','#'},{'4','$'},{'5','%'},{'6','^'},{'7','&'},{'8','*'},{'9','('},{'0',')'},\n {'\\r','\\r'},{0,0},{'\\b',0},{0,0},\n {' ',' '},{'-','_'},{'=','+'},{'[','{'},{']','}'},{'\\\\','|'},{'\\\\','|'},{';',':'},{'\\'', '\"'},{'`','~'},{',','\u003C'},{'.','>'},{'/','?'}\n};\n\n// 获取字符在字体库中的索引\nint get_char_index(char c) {\n if (c >= 'A' && c \u003C= 'Z') return c - 'A';\n if (c >= 'a' && c \u003C= 'z') return c - 'a';\n if (c >= '0' && c \u003C= '9') return 26 + (c - '0');\n if (c == ' ') return 36;\n return -1;\n}\n\n// 绘制单个字符\nvoid draw_char(int x, int y, char c, uint16_t color, uint16_t bg) {\n if (x \u003C 0 || y \u003C 0 || x + 5 > LCD_WIDTH || y + 7 > LCD_HEIGHT) {\n return;\n }\n\n int char_index = get_char_index(c);\n if (char_index \u003C 0) {\n return;\n }\n\n const uint8_t *bitmap = &font5x7[char_index * 7];\n uint16_t pixels[5*7];\n\n for (int row = 0; row \u003C 7; row++) {\n for (int col = 0; col \u003C 5; col++) {\n pixels[row*5 + col] = (bitmap[row] & (1 \u003C\u003C (4 - col))) ? color : bg;\n }\n }\n\n esp_lcd_panel_draw_bitmap(lcd_panel, x, y, x+5, y+7, pixels);\n}\n\n// 绘制字符串\nvoid draw_string(int x, int y, const char *str, uint16_t color, uint16_t bg_color) {\n int cursor_x = x;\n int cursor_y = y;\n\n while (*str) {\n if (cursor_x + 5 > LCD_WIDTH) {\n cursor_x = x;\n cursor_y += CHAR_HEIGHT;\n if (cursor_y + 7 > LCD_HEIGHT) break;\n }\n\n draw_char(cursor_x, cursor_y, *str, color, bg_color);\n cursor_x += CHAR_WIDTH;\n str++;\n }\n}\n\n// 清屏函数\nvoid clear_screen(uint16_t bg_color) {\n const int block_size = 128;\n uint16_t *buffer = malloc(block_size * sizeof(uint16_t));\n if (!buffer) return;\n\n memset(buffer, bg_color, block_size * sizeof(uint16_t));\n\n for (int y = 0; y \u003C LCD_HEIGHT; y++) {\n for (int x = 0; x \u003C LCD_WIDTH; x += block_size) {\n int w = (x + block_size > LCD_WIDTH) ? (LCD_WIDTH - x) : block_size;\n esp_lcd_panel_draw_bitmap(lcd_panel, x, y, x + w, y + 1, buffer);\n }\n }\n free(buffer);\n}\n\n// Switch target text and reset stats\nvoid set_target_text(const char *new_text) {\n target_text = new_text;\n\n // Refresh target line on screen\n draw_string(10, TEXT_INPUT_Y + 20, \" \", text_color, bg_color);\n draw_string(10, TEXT_INPUT_Y + 20, target_text, text_color, bg_color);\n\n // Reset stats\n correct_chars = 0;\n wrong_chars = 0;\n typed_chars = 0;\n start_time_ms = 0;\n}\n\n\n// 处理键盘输入字符\n\nstatic void handle_keyboard_input_char(char input_char) {\n // 修改测试代码 - 让效果更明显\nstatic int test_x = 0;\ntest_x = (test_x + 10) % 300;\n\n// 绘制一个持久的彩色条纹\nuint16_t *test_stripe = malloc(20 * 50 * sizeof(uint16_t));\nfor (int i = 0; i \u003C 20 * 50; i++) {\n test_stripe[i] = 0xF81F; // 洋红色\n}\nesp_lcd_panel_draw_bitmap(lcd_panel, test_x, 50, test_x + 20, 100, test_stripe);\nfree(test_stripe);\n\n// 在固定位置显示测试文字\ndraw_string(10, 200, \"KEY PRESSED!\", 0x07E0, 0x0000); // 绿色文字\n if (input_char == 0) return;\n\n // Handle backspace\n if (input_char == '\\b') {\n if (text_length > 7) { // keep \"Input: \"\n text_length--;\n display_text[text_length] = '\\0';\n }\n update_input_display();\n return;\n }\n\n if (text_length >= (int)sizeof(display_text) - 1) return;\n\n // 追加字符\n display_text[text_length++] = input_char;\n display_text[text_length] = '\\0';\n\n // 正确率统计\n int pos = text_length - 7; // 从 \"Input: \" 后开始比对\n if (pos >= 0 && pos \u003C (int)strlen(target_text)) {\n if (input_char == target_text[pos]) {\n correct_chars++;\n } else {\n wrong_chars++;\n }\n int total = correct_chars + wrong_chars;\n int accuracy = (total > 0) ? (correct_chars * 100 / total) : 0;\n ESP_LOGI(TAG, \"Accuracy: Correct=%d Wrong=%d Accuracy=%d%%\", correct_chars, wrong_chars, accuracy);\n }\n\n // 统计速度\n typed_chars++;\n if (start_time_ms == 0) {\n start_time_ms = (int)(esp_timer_get_time() / 1000); // 记录第一次输入时间\n }\n int now_ms = (int)(esp_timer_get_time() / 1000);\n int elapsed_ms = now_ms - start_time_ms;\n if (elapsed_ms > 0) {\n int cpm = typed_chars * 60000 / elapsed_ms; // 每分钟字符数\n ESP_LOGI(TAG, \"Speed Stats: Typed %d Characters, CPM=%d\", typed_chars, cpm);\n }\n\n update_input_display();\n\n // 检查是否完成目标文本\n if (pos >= (int)strlen(target_text)) {\n ESP_LOGI(TAG, \"Target text completed, switching to next sentence\");\n practice_index = (practice_index + 1) % (sizeof(practice_list) / sizeof(practice_list[0]));\n set_target_text(practice_list[practice_index]);\n }\n}\n\n\n// 更新输入区域显示\nvoid update_input_display(void) {\n // 清空输入区域(红底)\n draw_string(80, 160, \" \", 0xF800, 0xF800);\n\n // 绘制当前输入内容\n draw_string(80, 160, display_text, 0x0000, 0xF800);\n\n // 显示速度和正确率\n int now_ms = (int)(esp_timer_get_time() / 1000);\n int elapsed_ms = now_ms - start_time_ms;\n int cpm = (elapsed_ms > 0) ? (typed_chars * 60000 / elapsed_ms) : 0;\n\n int total = correct_chars + wrong_chars;\n int accuracy = (total > 0) ? (correct_chars * 100 / total) : 0;\n\n char stats[64];\n snprintf(stats, sizeof(stats), \"Speed: %d CPM Accuracy: %d%%\", cpm, accuracy);\n\n // 在下一行显示统计信息\n draw_string(80, 180, \" \", 0xF800, 0xF800);\n draw_string(80, 180, stats, 0x0000, 0xF800);\n}\n\n// 键盘报告处理\nstatic void hid_host_keyboard_report_callback(const uint8_t *data, const int length) {\n if (length \u003C sizeof(hid_keyboard_input_report_boot_t)) return;\n\n hid_keyboard_input_report_boot_t *kb_report = (hid_keyboard_input_report_boot_t *)data;\n static uint8_t prev_keys[HID_KEYBOARD_KEY_MAX] = {0};\n static TickType_t last_key_time = 0;\n\n if (xTaskGetTickCount() - last_key_time \u003C 50 / portTICK_PERIOD_MS) {\n return;\n }\n last_key_time = xTaskGetTickCount();\n\n for (int i = 0; i \u003C HID_KEYBOARD_KEY_MAX; i++) {\n if (kb_report->key[i] > HID_KEY_ERROR_UNDEFINED) {\n bool is_new_key = true;\n for (int j = 0; j \u003C HID_KEYBOARD_KEY_MAX; j++) {\n if (prev_keys[j] == kb_report->key[i]) {\n is_new_key = false;\n break;\n }\n }\n\n if (is_new_key) {\n uint8_t keycode = kb_report->key[i];\n bool is_shift = (kb_report->modifier.val & (HID_LEFT_SHIFT | HID_RIGHT_SHIFT));\n uint8_t mod = is_shift ? 1 : 0;\n\n if (keycode >= HID_KEY_A && keycode \u003C= HID_KEY_SLASH) {\n char pressed_char = keycode2ascii[keycode][mod];\n\n handle_keyboard_input_char(pressed_char);\n ESP_LOGI(TAG, \"Key Press: '%c', Current Text: %s\", pressed_char, display_text);\n }\n }\n }\n }\n\n memcpy(prev_keys, kb_report->key, HID_KEYBOARD_KEY_MAX);\n}\n\n// HID接口回调\nvoid hid_host_interface_callback(hid_host_device_handle_t hid_device_handle,\n const hid_host_interface_event_t event,\n void *arg) {\n uint8_t data[64] = {0};\n size_t data_length = 0;\n hid_host_dev_params_t dev_params;\n ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));\n\n switch (event) {\n case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:\n ESP_ERROR_CHECK(hid_host_device_get_raw_input_report_data(hid_device_handle,\n data, sizeof(data), &data_length));\n if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class &&\n HID_PROTOCOL_KEYBOARD == dev_params.proto) {\n hid_host_keyboard_report_callback(data, data_length);\n }\n break;\n case HID_HOST_INTERFACE_EVENT_DISCONNECTED:\n ESP_LOGI(TAG, \"Keyboard Disconnected\");\n ESP_ERROR_CHECK(hid_host_device_close(hid_device_handle));\n break;\n default:\n break;\n }\n}\n\n// HID设备事件处理\nvoid hid_host_device_event(hid_host_device_handle_t hid_device_handle,\n const hid_host_driver_event_t event,\n void *arg) {\n hid_host_dev_params_t dev_params;\n ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));\n\n if (event == HID_HOST_DRIVER_EVENT_CONNECTED) {\n ESP_LOGI(TAG, \"Keyboard Connected\");\n const hid_host_device_config_t dev_config = {\n .callback = hid_host_interface_callback,\n .callback_arg = NULL\n };\n ESP_ERROR_CHECK(hid_host_device_open(hid_device_handle, &dev_config));\n if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) {\n ESP_ERROR_CHECK(hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT));\n ESP_ERROR_CHECK(hid_class_request_set_idle(hid_device_handle, 0, 0));\n }\n ESP_ERROR_CHECK(hid_host_device_start(hid_device_handle));\n }\n}\n\n\n// HID事件结构体\ntypedef struct {\n hid_host_device_handle_t handle;\n hid_host_driver_event_t event;\n void *arg;\n} hid_event_t;\n\n// HID设备回调\nvoid hid_host_device_callback(hid_host_device_handle_t hid_device_handle,\n const hid_host_driver_event_t event,\n void *arg) {\n hid_event_t hid_event = {\n .handle = hid_device_handle,\n .event = event,\n .arg = arg\n };\n\n if (hid_event_queue) {\n xQueueSend(hid_event_queue, &hid_event, 0);\n }\n}\n\n// USB主机任务\nstatic void usb_lib_task(void *arg) {\n const usb_host_config_t host_config = {\n .intr_flags = ESP_INTR_FLAG_LEVEL1,\n };\n ESP_ERROR_CHECK(usb_host_install(&host_config));\n\n while (1) {\n uint32_t event_flags;\n usb_host_lib_handle_events(portMAX_DELAY, &event_flags);\n if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {\n ESP_ERROR_CHECK(usb_host_device_free_all());\n }\n }\n}\n\n// 主函数\nvoid app_main(void) {\n ESP_LOGI(TAG, \"Keyboard Display Test Program Started\");\n\n // 初始化LCD屏幕\n spi_bus_config_t buscfg = {\n .sclk_io_num = PIN_NUM_CLK,\n .mosi_io_num = PIN_NUM_MOSI,\n .miso_io_num = -1,\n .quadwp_io_num = -1,\n .quadhd_io_num = -1,\n .max_transfer_sz = LCD_WIDTH * LCD_HEIGHT * 2 + 8\n };\n ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));\n\n esp_lcd_panel_io_handle_t io_handle = NULL;\n esp_lcd_panel_io_spi_config_t io_config = {\n .dc_gpio_num = PIN_NUM_DC,\n .cs_gpio_num = PIN_NUM_CS,\n .pclk_hz = 8 * 1000 * 1000,\n .lcd_cmd_bits = 8,\n .lcd_param_bits = 8,\n .spi_mode = 0,\n .trans_queue_depth = 5,\n };\n ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));\n\n esp_lcd_panel_dev_config_t panel_config = {\n .reset_gpio_num = PIN_NUM_RST,\n .color_space = ESP_LCD_COLOR_SPACE_RGB,\n .bits_per_pixel = 16,\n };\n ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &lcd_panel));\n\n esp_lcd_panel_reset(lcd_panel);\n esp_lcd_panel_init(lcd_panel);\n\n#if SCREEN_ORIENTATION == 1\n esp_lcd_panel_swap_xy(lcd_panel, true);\n#else\n esp_lcd_panel_swap_xy(lcd_panel, false);\n#endif\n\n esp_lcd_panel_mirror(lcd_panel, false, true);\n esp_lcd_panel_invert_color(lcd_panel, false);\n esp_lcd_panel_disp_on_off(lcd_panel, true);\n\n // 显示初始界面\n clear_screen(bg_color);\n draw_string(10, TEXT_LINE1_Y, \"USB KEYBOARD TEST\", text_color, bg_color);\n draw_string(10, TEXT_LINE2_Y, \"CONNECT KEYBOARD\", text_color, bg_color);\n draw_string(10, TEXT_INPUT_Y, \"Input: \", text_color, bg_color);\n\n // 初始化目标练习文本为数组的第一句\n practice_index = 0;\n set_target_text(practice_list[practice_index]);\n\n // 显示提示界面\n draw_string(10, TEXT_INPUT_Y + 40, \"Next Sentence Starts!\", 0x0000, 0xFFE0); // 黑字黄底\n vTaskDelay(1000 / portTICK_PERIOD_MS); // 延迟1秒\n\n // 清除提示\n draw_string(10, TEXT_INPUT_Y + 40, \" \", text_color, bg_color);\n\n // 显示目标练习文本\n draw_string(10, TEXT_INPUT_Y + 20, target_text, text_color, bg_color);\n\n // 初始化USB和HID\n hid_event_queue = xQueueCreate(10, sizeof(hid_event_t));\n xTaskCreate(usb_lib_task, \"usb_events\", 4096, NULL, 2, NULL);\n vTaskDelay(1000 / portTICK_PERIOD_MS);\n\n const hid_host_driver_config_t hid_host_driver_config = {\n .create_background_task = true,\n .task_priority = 5,\n .stack_size = 8192,\n .core_id = 0,\n .callback = hid_host_device_callback,\n .callback_arg = NULL\n };\n ESP_ERROR_CHECK(hid_host_install(&hid_host_driver_config));\n\n ESP_LOGI(TAG, \"System Init Complete, Please Connect USB Keyboard\");\n\n // 主事件循环\n while (1) {\n hid_event_t hid_event;\n if (xQueueReceive(hid_event_queue, &hid_event, portMAX_DELAY)) {\n hid_host_device_event(hid_event.handle, hid_event.event, hid_event.arg);\n }\n }\n}请帮我检查一下代码