From 76adb3584c775a093ac21b54fb20bab5206084ac Mon Sep 17 00:00:00 2001 From: skiinder Date: Sat, 11 Apr 2026 13:41:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(board):=20=E6=B7=BB=E5=8A=A0=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=B1=8F=E5=92=8CPWM=E8=83=8C=E5=85=89=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在CMakeLists.txt中添加display_test_module.c源文件 - 在设备树配置中添加SPI3和PWM0引脚控制定义 - 配置MIPI DBI显示屏驱动,支持ST7789V控制器 - 添加PWM LED背光控制功能 - 启用GPIO复位功能并添加点击检测器配置 - 实现显示测试模块,支持彩色测试图案渲染 --- CMakeLists.txt | 1 + .../mini_keyboard/mini_keyboard-pinctrl.dtsi | 28 +++ .../atguigu/mini_keyboard/mini_keyboard.dts | 77 +++++- inc/click_detector_def.h | 15 ++ prj.conf | 12 + src/display_test_module.c | 230 ++++++++++++++++++ 6 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 inc/click_detector_def.h create mode 100644 src/display_test_module.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 69b8a66..7affda1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(app PRIVATE src/ble_adv_uuid16.c src/ble_bas_module.c src/ble_hid_module.c + src/display_test_module.c src/encoder_module.c src/hid_flowctrl_module.c src/keyboard_core_module.c diff --git a/boards/atguigu/mini_keyboard/mini_keyboard-pinctrl.dtsi b/boards/atguigu/mini_keyboard/mini_keyboard-pinctrl.dtsi index a3833a2..04e9863 100644 --- a/boards/atguigu/mini_keyboard/mini_keyboard-pinctrl.dtsi +++ b/boards/atguigu/mini_keyboard/mini_keyboard-pinctrl.dtsi @@ -30,4 +30,32 @@ bias-pull-up; }; }; + + spi3_default: spi3_default { + group1 { + psels = , + ; + }; + }; + + spi3_sleep: spi3_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + pwm0_default: pwm0_default { + group1 { + psels = ; + }; + }; + + pwm0_sleep: pwm0_sleep { + group1 { + psels = ; + low-power-enable; + }; + }; }; diff --git a/boards/atguigu/mini_keyboard/mini_keyboard.dts b/boards/atguigu/mini_keyboard/mini_keyboard.dts index 9cc465c..564aebe 100644 --- a/boards/atguigu/mini_keyboard/mini_keyboard.dts +++ b/boards/atguigu/mini_keyboard/mini_keyboard.dts @@ -2,6 +2,8 @@ #include #include "mini_keyboard-pinctrl.dtsi" #include +#include +#include / { model = "Mini keyboard"; @@ -11,11 +13,13 @@ zephyr,sram = &sram0; zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; + zephyr,display = &screen_lcd; }; aliases { led0 = &myled0; qdec0 = &qdec; + backlight = &backlight; }; hid_kbd: hid_kbd { @@ -36,12 +40,57 @@ in-polling-period-us = <1000>; }; - leds { - compatible = "gpio-leds"; - myled0: led_0 { - gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; - }; - }; + leds { + compatible = "gpio-leds"; + myled0: led_0 { + gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; + }; + }; + + pwm_leds { + compatible = "pwm-leds"; + status = "okay"; + + backlight: pwm_led_0 { + pwms = <&pwm0 0 PWM_MSEC(10) PWM_POLARITY_INVERTED>; + }; + }; + + mipi_dbi_screen: mipi_dbi_screen { + compatible = "zephyr,mipi-dbi-spi"; + spi-dev = <&spi3>; + dc-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + write-only; + #address-cells = <1>; + #size-cells = <0>; + + screen_lcd: st7789v@0 { + compatible = "sitronix,st7789v"; + status = "okay"; + reg = <0>; + mipi-max-frequency = <32000000>; + width = <320>; + height = <172>; + x-offset = <0>; + y-offset = <34>; + vcom = <0x34>; + gctrl = <0x00>; + mdac = <0x70>; + gamma = <0x01>; + colmod = <0x05>; + lcm = <0x2c>; + porch-param = [ 0c 0c 00 33 33 ]; + cmd2en-param = [ 5a 69 02 01 ]; + pwctrl1-param = [ a4 a1 ]; + pvgam-param = [ f0 04 08 0a 0a 05 25 33 3c 24 0e 0f 27 2f ]; + nvgam-param = [ f0 02 06 06 04 22 25 32 3b 3a 15 17 2d 37 ]; + ram-param = [ 00 f0 ]; + rgb-param = [ cd 08 14 ]; + ready-time-ms = <120>; + mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE"; + }; + }; vbatt: vbatt { compatible = "voltage-divider"; @@ -90,6 +139,7 @@ &uicr { nfct-pins-as-gpios; + gpio-as-nreset; }; &adc { @@ -148,6 +198,21 @@ status = "okay"; }; +&spi3 { + status = "okay"; + pinctrl-0 = <&spi3_default>; + pinctrl-1 = <&spi3_sleep>; + pinctrl-names = "default", "sleep"; + cs-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; +}; + +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-names = "default", "sleep"; +}; + &qdec { status = "okay"; pinctrl-0 = <&encoder_default>; diff --git a/inc/click_detector_def.h b/inc/click_detector_def.h new file mode 100644 index 0000000..3ff3c00 --- /dev/null +++ b/inc/click_detector_def.h @@ -0,0 +1,15 @@ +/* + * This configuration file is included only once from the CAF click detector + * module and defines the keys that should produce click events. + */ + +#include + +const struct {} click_detector_def_include_once; + +static const struct click_detector_config click_detector_config[] = { + { + .key_id = 0x180, + .consume_button_event = true, + }, +}; diff --git a/prj.conf b/prj.conf index 286418a..6512ef2 100644 --- a/prj.conf +++ b/prj.conf @@ -1,8 +1,13 @@ CONFIG_CAF=y CONFIG_CAF_BUTTONS=y CONFIG_CAF_BUTTONS_DEF_PATH="buttons_def.h" +CONFIG_CAF_CLICK_DETECTOR=y +CONFIG_CAF_CLICK_DETECTOR_DEF_PATH="click_detector_def.h" CONFIG_GPIO=y CONFIG_I2C=y +CONFIG_LED=y +CONFIG_PWM=y +CONFIG_SPI=y CONFIG_NRFX_RTC2=y CONFIG_NRFX_GPPI=y CONFIG_NRFX_QDEC=y @@ -10,6 +15,9 @@ CONFIG_PINCTRL_DYNAMIC=y CONFIG_REBOOT=y CONFIG_SENSOR=y CONFIG_ADC=y +CONFIG_DISPLAY=y +CONFIG_DISPLAY_LOG_LEVEL_DBG=y +CONFIG_MIPI_DBI_LOG_LEVEL_DBG=y CONFIG_SETTINGS=y CONFIG_SETTINGS_NVS=y CONFIG_FLASH=y @@ -78,6 +86,10 @@ CONFIG_CAF_BLE_ADV_FAST_ADV=y CONFIG_CAF_BLE_ADV_FILTER_ACCEPT_LIST=y CONFIG_CAF_BLE_ADV_MODULE_SUSPEND_EVENTS=y CONFIG_CAF_BLE_BOND=y +CONFIG_CAF_BLE_BOND_PEER_ERASE_CLICK=y +CONFIG_CAF_BLE_BOND_PEER_ERASE_CLICK_KEY_ID=0x180 +CONFIG_CAF_BLE_BOND_PEER_ERASE_CLICK_LONG=y +CONFIG_CAF_BLE_BOND_PEER_ERASE_CLICK_TIMEOUT=-1 CONFIG_CAF_MODULE_SUSPEND_EVENTS=y CONFIG_BT_ADV_PROV_FLAGS=y CONFIG_BT_ADV_PROV_GAP_APPEARANCE=y diff --git a/src/display_test_module.c b/src/display_test_module.c new file mode 100644 index 0000000..81dbb3a --- /dev/null +++ b/src/display_test_module.c @@ -0,0 +1,230 @@ +#include +#include + +#include + +#define MODULE display_test_module +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define DISPLAY_TEST_LINE_PIXELS 320U +#define COLOR_RED 0xF800U +#define COLOR_GREEN 0x07E0U +#define COLOR_BLUE 0x001FU +#define COLOR_YELLOW 0xFFE0U + +BUILD_ASSERT(DT_HAS_CHOSEN(zephyr_display), "Missing zephyr,display chosen node"); +BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_ALIAS(backlight), okay), + "Missing backlight alias"); + +static const struct device *const display_dev = + DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); +static const struct device *const backlight_dev = + DEVICE_DT_GET(DT_PARENT(DT_ALIAS(backlight))); +static const uint32_t backlight_idx = DT_NODE_CHILD_IDX(DT_ALIAS(backlight)); +static uint16_t line_buf[DISPLAY_TEST_LINE_PIXELS]; +static bool initialized; +static bool running; + +static void fill_test_line(size_t width) +{ + const uint16_t colors[] = { + COLOR_RED, + COLOR_GREEN, + COLOR_BLUE, + COLOR_YELLOW, + }; + size_t segment = MAX(width / ARRAY_SIZE(colors), 1U); + + for (size_t x = 0; x < width; x++) { + size_t color_idx = MIN(x / segment, ARRAY_SIZE(colors) - 1U); + + line_buf[x] = colors[color_idx]; + } +} + +static int draw_test_pattern(void) +{ + struct display_capabilities caps; + struct display_buffer_descriptor desc; + int err; + + display_get_capabilities(display_dev, &caps); + LOG_INF("Display caps: %ux%u fmt:%u orient:%u info:0x%x", + caps.x_resolution, caps.y_resolution, + caps.current_pixel_format, caps.current_orientation, + caps.screen_info); + + if (caps.x_resolution > ARRAY_SIZE(line_buf)) { + LOG_ERR("Display width %u exceeds line buffer", caps.x_resolution); + return -ENOMEM; + } + + if (caps.current_pixel_format != PIXEL_FORMAT_RGB_565) { + LOG_WRN("Unexpected pixel format %u", caps.current_pixel_format); + } + + fill_test_line(caps.x_resolution); + + desc.width = caps.x_resolution; + desc.height = 1U; + desc.pitch = caps.x_resolution; + desc.buf_size = caps.x_resolution * sizeof(line_buf[0]); + + for (uint16_t y = 0; y < caps.y_resolution; y++) { + err = display_write(display_dev, 0U, y, &desc, line_buf); + if (err) { + LOG_ERR("display_write failed at line %u (%d)", y, err); + return err; + } + } + + err = display_blanking_off(display_dev); + if (err) { + LOG_ERR("display_blanking_off failed (%d)", err); + return err; + } + + LOG_INF("Display test pattern rendered (%ux%u)", + caps.x_resolution, caps.y_resolution); + + return 0; +} + +static int module_init(void) +{ + int err; + + LOG_INF("Display test init on %s", display_dev->name); + + if (!device_is_ready(display_dev)) { + LOG_ERR("Display device %s not ready", display_dev->name); + return -ENODEV; + } + + if (!device_is_ready(backlight_dev)) { + LOG_ERR("Backlight device %s not ready", backlight_dev->name); + return -ENODEV; + } + + err = led_off(backlight_dev, backlight_idx); + if (err) { + LOG_ERR("Backlight off failed (%d)", err); + return err; + } + + LOG_INF("Backlight device %s channel %u ready", + backlight_dev->name, backlight_idx); + + return 0; +} + +static int module_start(void) +{ + int err; + + if (running) { + return 0; + } + + LOG_INF("Display test start"); + + err = led_on(backlight_dev, backlight_idx); + if (err) { + LOG_ERR("Backlight enable failed (%d)", err); + return err; + } + + LOG_INF("Backlight enabled"); + k_sleep(K_MSEC(50)); + + err = draw_test_pattern(); + if (err) { + (void)led_off(backlight_dev, backlight_idx); + return err; + } + + running = true; + + return 0; +} + +static void module_pause(void) +{ + if (!running) { + return; + } + + LOG_INF("Display test pause"); + (void)display_blanking_on(display_dev); + (void)led_off(backlight_dev, backlight_idx); + running = false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_module_state_event(aeh)) { + const struct module_state_event *event = cast_module_state_event(aeh); + int err; + + if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + LOG_INF("main READY received"); + if (!initialized) { + err = module_init(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + return false; + } + + initialized = true; + } + + err = module_start(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + if (is_power_down_event(aeh)) { + if (initialized) { + module_pause(); + module_set_state(MODULE_STATE_STANDBY); + } + + return false; + } + + if (is_wake_up_event(aeh)) { + if (initialized) { + int err = module_start(); + + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + return false; +} + +APP_EVENT_LISTENER(MODULE, app_event_handler); +APP_EVENT_SUBSCRIBE(MODULE, module_state_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);