FR8008G-U 蓝牙和LVGL同时运行 LVGL使用外部PSRAM运行比较卡



  • FR8008G-U 蓝牙和LVGL同时跑,由于蓝牙协议栈基本上把内部RAM用完了,所以LVGL只能在PSRAM上运行,但是在PSRAM上运行比在内部RAM运行慢,我测试了下慢了将近6倍,请问PSRAM还有没有提升空间?或者还有其他办法解决上面LVGL和蓝牙同时跑内部RAM用完的问题,以下是我的PSRAM配置pmu_ioldosw_ctrl(true);

    __SYSTEM_GPIO_CLK_ENABLE();
    system_regs->mdm_qspi_cfg.qspi_ref_128m_en = 1; // configure qspi reference clock to 128MHz
    system_regs->mdm_qspi_cfg.qspi_ref_clk_sel = 1; // qspi is used for internal flash, set its reference clock to 96MHz
    // system_enable_internal_flash_q_read(0x01);
    system_set_internal_flash_clock_div(0);
    
    // configure PSRAM pin and init PSRAM
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_0, PORTC0_FUNC_QSPI0_IO3);
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_1, PORTC1_FUNC_QSPI0_SCLK0);
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_2, PORTC2_FUNC_QSPI0_CSN0);
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_3, PORTC3_FUNC_QSPI0_IO1);
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_4, PORTC4_FUNC_QSPI0_IO2);
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_5, PORTC5_FUNC_QSPI0_IO0);
    psram_init(PSRAM_CLK_SEL_COREH_96M);
    

    #include <stdint.h>
    #include <string.h>

    #include "plf.h"
    #include "driver_qspi.h"
    #include "driver_psram.h"
    #include "driver_system.h"
    #include "driver_pmu.h"
    #include "driver_cache.h"

    #include "co_printf.h"

    #ifdef INLINE
    #undef INLINE
    #endif
    #define INLINE

    #define PSRAM_ENABLE_Q_MODE 1
    #define PSRAM_CLK_DIV_SEL QSPI_BAUDRATE_DIV_2
    #define PSRAM_BASE QSPI1_DAC_ADDRESS

    #define PSRAM_READ_IDENTIFICATION 0x9F

    #define PSRAM_READ_OPCODE 0x03
    #define PSRAM_FAST_READ_OPCODE 0x0B
    #define PSRAM_FAST_QUAL_READ_OPCODE 0xEB

    #define PSRAM_PAGE_PROGRAM_OPCODE 0x02
    #define PSRAM_PAGE_QUAL_PROGRAM_OPCODE 0x38

    #define PSRAM_ENTER_QUAD_MODE_OPCODE 0x35
    #define PSRAM_EXIT_QUAD_MODE_OPCODE 0xF5

    #define PSRAM_RESET_ENABLE_OPCODE 0x66
    #define PSRAM_RESET_OPCODE 0x99

    #define QSPI0_STIG_MAX_SINGLE_LEN 8
    #define QSPI0_STIG_BANK_DEPTH 128

    #define QSPI0_HIGH_SPEED 1
    uint8_t psram_delay = 0xFF;
    static const struct qspi_stig_reg_t psram_read_id_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 3,
    .enable_read = 1,
    .opcode = PSRAM_READ_IDENTIFICATION,
    };

    static const struct qspi_stig_reg_t enter_quad_mode_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 0,
    .enable_read = 0,
    .opcode = PSRAM_ENTER_QUAD_MODE_OPCODE,
    };

    static const struct qspi_stig_reg_t reset_enable_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 0,
    .enable_read = 0,
    .opcode = PSRAM_RESET_ENABLE_OPCODE,
    };

    static const struct qspi_stig_reg_t reset_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 0,
    .enable_read = 0,
    .opcode = PSRAM_RESET_OPCODE,
    };

    //static const struct qspi_stig_reg_t exit_quad_mode_cmd = {
    // .enable_bank = 0,
    // .dummy_cycles = 0,
    // .write_bytes = 0,
    // .enable_write = 0,
    // .addr_bytes = 0,
    // .enable_mode = 0,
    // .enable_cmd_addr = 0,
    // .read_bytes = 0,
    // .enable_read = 0,
    // .opcode = PSRAM_EXIT_QUAD_MODE_OPCODE,
    //};

    volatile struct qspi_regs_t *qspi0_ctrl = (volatile struct qspi_regs_t *)QSPI0_APB_BASE;

    INLINE void qspi0_cfg_set_enable(uint8_t en)
    {
    qspi0_ctrl->config.enable = en;
    }

    INLINE void qspi0_cfg_set_cpol(uint8_t high)
    {
    qspi0_ctrl->config.cpol = high;
    }

    INLINE void qspi0_cfg_set_cpha(uint8_t rising)
    {
    qspi0_ctrl->config.cpha = rising;
    }

    INLINE void qspi0_cfg_set_hold(uint8_t hold)
    {
    qspi0_ctrl->config.hold_pin = hold;
    }

    INLINE void qspi0_cfg_set_reset(uint8_t reset)
    {
    qspi0_ctrl->config.reset_pin = reset;
    }

    INLINE void qspi0_cfg_set_reset_sel(uint8_t dedicated)
    {
    qspi0_ctrl->config.reset_pin_conf = dedicated;
    }

    INLINE void qspi0_cfg_set_enable_dac(uint8_t en)
    {
    qspi0_ctrl->config.enable_DAC = en;
    }

    INLINE void qspi0_cfg_set_enable_legacy(uint8_t en)
    {
    qspi0_ctrl->config.enable_legacy = en;
    }

    INLINE void qspi0_cfg_set_write_protect(uint8_t wp)
    {
    qspi0_ctrl->config.write_en_pin = wp;
    }

    INLINE void qspi0_cfg_set_enable_remap(uint8_t en)
    {
    qspi0_ctrl->config.enable_AHB_remap = en;
    }

    static void qspi0_cfg_set_baudrate(uint8_t baudrate);

    INLINE void qspi0_cfg_set_enable_AHB_decoder(uint8_t en)
    {
    qspi0_ctrl->config.enable_AHB_decoder = en;
    }

    INLINE int qspi0_is_busy(void)
    {
    return (qspi0_ctrl->config.status == 0);
    }

    INLINE void qspi0_read_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->read_conf.opcode_no_XIP = opcode;
    }

    INLINE void qspi0_read_set_instruction_type(uint8_t type)
    {
    qspi0_ctrl->read_conf.instruction_type = type;
    }

    INLINE void qspi0_read_set_address_type(uint8_t type)
    {
    qspi0_ctrl->read_conf.addr_type = type;
    }

    INLINE void qspi0_read_set_data_type(uint8_t type)
    {
    qspi0_ctrl->read_conf.data_type = type;
    }

    INLINE void qspi0_read_set_mode_en(uint8_t en)
    {
    qspi0_ctrl->read_conf.enable_mode = en;
    }

    INLINE void qspi0_read_set_dummy_cycles(uint8_t cycles)
    {
    qspi0_ctrl->read_conf.dummy_cycles = cycles;
    }

    INLINE void qspi0_write_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->write_conf.opcode = opcode;
    }

    INLINE void qspi0_write_set_wel_dis(uint8_t disable)
    {
    qspi0_ctrl->write_conf.disable_WEL = disable;
    }

    INLINE void qspi0_write_set_address_type(uint8_t type)
    {
    qspi0_ctrl->write_conf.addr_type = type;
    }

    INLINE void qspi0_write_set_data_type(uint8_t type)
    {
    qspi0_ctrl->write_conf.data_type = type;
    }

    INLINE void qspi0_write_set_dummy_cycles(uint8_t cycles)
    {
    qspi0_ctrl->write_conf.dummy_cycles = cycles;
    }

    INLINE void qspi0_set_remap_address(uint32_t address)
    {
    qspi0_ctrl->remap_address = address;
    }

    INLINE void qspi0_set_mode_bit(uint8_t mode)
    {
    qspi0_ctrl->mode_bits = (uint32_t)mode;
    }

    INLINE void qspi0_poll_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->poll_cfg.opcode_poll = opcode;
    }

    INLINE void qspi0_poll_set_bit_index(uint8_t index)
    {
    qspi0_ctrl->poll_cfg.poll_bit_index = index;
    }

    INLINE void qspi0_poll_set_polarity(uint8_t pol)
    {
    qspi0_ctrl->poll_cfg.poll_polarity = pol;
    }

    INLINE void qspi0_poll_set_disable(uint8_t dis)
    {
    qspi0_ctrl->poll_cfg.disable_poll = dis;
    }

    INLINE void qspi0_poll_set_expire(uint8_t en, uint32_t duration)
    {
    qspi0_ctrl->poll_cfg.enable_expiration = en;
    if(en) {
    qspi0_ctrl->poll_expiration = duration;
    }
    }

    INLINE void qspi0_poll_set_poll_count(uint8_t count)
    {
    qspi0_ctrl->poll_cfg.poll_count = count;
    }

    INLINE void qspi0_poll_set_poll_delay(uint8_t delay)
    {
    qspi0_ctrl->poll_cfg.poll_repetition_delay = delay;
    }

    INLINE void qspi0_set_cmd_addr(uint32_t addr)
    {
    qspi0_ctrl->cmd_address = addr;
    }

    INLINE void qspi0_stig_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->cmd_ctrl.opcode = opcode;
    }

    INLINE void qspi0_stig_set_read_en(uint8_t en)
    {
    qspi0_ctrl->cmd_ctrl.enable_read = en;
    }

    INLINE void qspi0_stig_set_read_bytes(uint8_t bytes)
    {
    qspi0_ctrl->cmd_ctrl.read_bytes = bytes;
    }

    INLINE void qspi0_stig_set_addr(uint8_t en, uint8_t bytes, uint32_t addr)
    {
    qspi0_ctrl->cmd_ctrl.enable_cmd_addr = en;
    qspi0_ctrl->cmd_ctrl.addr_bytes = bytes;
    if(en) {
    qspi0_ctrl->cmd_address = addr;
    }
    }

    INLINE void qspi0_stig_set_mode_en(uint8_t en)
    {
    qspi0_ctrl->cmd_ctrl.enable_mode = en;
    }

    INLINE void qspi0_stig_set_write_en(uint8_t en)
    {
    qspi0_ctrl->cmd_ctrl.enable_write = en;
    }

    INLINE void qspi0_stig_set_write_bytes(uint8_t bytes)
    {
    qspi0_ctrl->cmd_ctrl.write_bytes = bytes;
    }

    INLINE void qspi0_stig_set_dummy_cycles(uint8_t cycles)
    {
    qspi0_ctrl->cmd_ctrl.dummy_cycles = cycles;
    }

    INLINE void qspi0_stig_set_mem_bank(uint8_t en, uint8_t bytes)
    {
    qspi0_ctrl->cmd_ctrl.enable_bank = en;
    if(en) {
    qspi0_ctrl->cmd_ctrl_mem.mem_bank_req_bytes = bytes;
    }
    }

    static void qspi0_cfg_set_baudrate(uint8_t baudrate)
    {
    qspi0_ctrl->config.baud_rate = baudrate;
    }

    static int qspi0_stig_cmd(struct qspi_stig_reg_t cmd, enum qspi_stig_cmd_type_t type, int len, uint8_t *buffer)
    {
    uint32_t tmp_u32[2];
    uint8_t *tmp_u8 = (uint8_t *)tmp_u32;

    if(type == QSPI_STIG_CMD_BANK_READ) {
        if(QSPI0_STIG_BANK_DEPTH < len) {
            return -1;
        }
    }
    else {
        if(QSPI0_STIG_MAX_SINGLE_LEN < len) {
            return -1;
        }
    }
    
    while(qspi0_is_busy());
    
    if(type == QSPI_STIG_CMD_EXE) {
        qspi0_ctrl->cmd_ctrl = cmd;
        qspi0_ctrl->cmd_ctrl.execute = 1;
        while(qspi0_ctrl->cmd_ctrl.progress_status);
    }
    else {
        if(type == QSPI_STIG_CMD_WRITE) {
            memcpy(tmp_u8, buffer, len);
            qspi0_ctrl->write_data_L = tmp_u32[0];
            qspi0_ctrl->write_data_H = tmp_u32[1];
            cmd.write_bytes = len - 1;
            qspi0_ctrl->cmd_ctrl = cmd;
            qspi0_ctrl->cmd_ctrl.execute = 1;
            while(qspi0_ctrl->cmd_ctrl.progress_status);
        }
        else {
            cmd.read_bytes = len - 1;
            qspi0_ctrl->cmd_ctrl = cmd;
            qspi0_ctrl->cmd_ctrl.execute = 1;
            while(qspi0_ctrl->cmd_ctrl.progress_status);
            if(type == QSPI_STIG_CMD_READ) {
                tmp_u32[0] = qspi0_ctrl->read_data_L;
                tmp_u32[1] = qspi0_ctrl->read_data_H;
                //co_printf("READ_L: 0x%08x, READ_H: 0x%08x.\r\n", tmp_u32[0], tmp_u32[1]);
                memcpy(buffer, tmp_u8, len);
            }
            else {
                //TBD, BANK READ
            }
        }
    }
    
    return 0;
    

    }

    void psram_enter_quad(void)
    {
    qspi0_stig_cmd(enter_quad_mode_cmd, QSPI_STIG_CMD_EXE, 0, NULL);
    }

    uint32_t psram_read_id(void)
    {
    uint32_t flash_id;
    qspi0_stig_cmd(psram_read_id_cmd, QSPI_STIG_CMD_READ, 3, (uint8_t *)&flash_id);
    return (flash_id&0xffffff);
    }

    static void psram_controller_init(uint16_t page_boundary)
    {
    while(qspi0_is_busy());

    #if PSRAM_ENABLE_Q_MODE == 1
    qspi0_read_set_opcode(PSRAM_FAST_READ_OPCODE);
    qspi0_read_set_instruction_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_dummy_cycles(4);
    qspi0_read_set_mode_en(0);
    qspi0_set_mode_bit(0);//8 bits data after addr

    qspi0_write_set_opcode(PSRAM_PAGE_QUAL_PROGRAM_OPCODE);
    qspi0_write_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_dummy_cycles(0);
    

    #else
    qspi0_read_set_opcode(PSRAM_FAST_QUAL_READ_OPCODE);
    qspi0_read_set_instruction_type(QSPI_WIRE_TYPE_STAND);
    qspi0_read_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_dummy_cycles(6);
    qspi0_read_set_mode_en(0);
    qspi0_set_mode_bit(0);//8 bits data after addr

    qspi0_write_set_opcode(PSRAM_PAGE_QUAL_PROGRAM_OPCODE);
    qspi0_write_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_dummy_cycles(0);
    

    #endif

    //init configuration register
    qspi0_cfg_set_cpol(0);
    qspi0_cfg_set_cpha(0);
    qspi0_cfg_set_enable_dac(1);
    qspi0_cfg_set_enable_legacy(0);
    qspi0_cfg_set_enable_remap(1);
    

    #if QSPI0_HIGH_SPEED
    qspi0_cfg_set_baudrate(PSRAM_CLK_DIV_SEL);
    #else
    qspi0_cfg_set_baudrate(QSPI_BAUDRATE_DIV_32);
    #endif
    qspi0_cfg_set_enable_AHB_decoder(1);
    qspi0_write_set_wel_dis(1);
    qspi0_poll_set_disable(1);

    #if QSPI0_HIGH_SPEED
    qspi0_ctrl->read_cap.delay_capture = 3;
    #else
    qspi0_ctrl->read_cap.delay_capture = 0;
    #endif
    qspi0_ctrl->read_cap.enable_loopback_clk = 1;
    qspi0_ctrl->delay.sel_start_offset = 2;
    qspi0_ctrl->delay.sel_end_offset = 2;
    qspi0_ctrl->delay.sel_dessert = 2;
    qspi0_ctrl->cs_ctrl.rd_brk_en = 1;
    qspi0_ctrl->cs_ctrl.disable_cs_after_first_byte = 1;
    qspi0_ctrl->cs_ctrl.page_boundary_protect_en = 1;
    qspi0_ctrl->cs_ctrl.page_boundary = page_boundary;
    qspi0_set_remap_address(QSPI1_DAC_ADDRESS);
    qspi0_cfg_set_enable(1);
    }

    void psram_cache_enable(void)
    {
    __CACHE_FLUSH(CACHE);
    __CACHE_WR_MODE_SET(CACHE, CACHE_WR_WRITE_THROUGH);
    __CACHE_ADDR_RANGEx_BANK_SET(CACHE, 0, 0x0000);
    __CACHE_ADDR_RANGEx_MASK_SET(CACHE, 0, 0x0000);
    __CACHE_ADDR_RANGEx_POL_SET(CACHE, 0, CACHE_POL_CACHABLE);
    __CACHE_ADDR_RANGEx_ENABLE(CACHE, 0);
    __CACHE_ENABLE(CACHE);
    }

    bool psram_init(enum psram_clk_sel_t clk_sel)
    {
    uint32_t ref_clk;

    if (clk_sel >= PSRAM_CLK_SEL_MAX) {
        return false;
    }
    
    switch (clk_sel) {
        case PSRAM_CLK_SEL_COREH_48M:
            system_regs->mdm_qspi_cfg.qspi0_ref_clk_sel = 0;
            ref_clk = 48000000;
            break;
        case PSRAM_CLK_SEL_COREH_96M:
            system_regs->mdm_qspi_cfg.qspi0_ref_clk_sel = 1;
            ref_clk = 96000000;
            break;      
        default:
            return false;
    }
    system_regs->mdm_qspi_cfg.qspi0_ref_clk_en=1;
    system_regs->mdm_qspi_cfg.qspi0_hclk_en=1;
    system_regs->mdm_qspi_cfg.qspi0_io_ctl_oen=0;
    
    if(psram_delay == 0xFF)
    {
        qspi0_stig_cmd(reset_enable_cmd, QSPI_STIG_CMD_EXE, 0, NULL);
        qspi0_stig_cmd(reset_cmd, QSPI_STIG_CMD_EXE, 0, NULL);
    

    #if PSRAM_ENABLE_Q_MODE == 1
    psram_enter_quad();
    #endif // PSRAM_ENABLE_Q_MODE == 1
    }

    /* about 100 bytes can be transmitted during 8us when QSPI clock is 24MHz */
    ref_clk /= ((PSRAM_CLK_DIV_SEL+1) * 2);
    uint32_t bytes = ref_clk / 240000;
    uint32_t boundary_cfg = 1 << (31 - __CLZ(bytes));
    if (boundary_cfg >= 0x100) {
        boundary_cfg = 0x100;
    }
    psram_controller_init(boundary_cfg);
    
    if(psram_delay != 0xFF)
    {
        qspi0_ctrl->read_cap.delay_capture = psram_delay;
    }
    else
    {
        /* detect read capture delay configuration */
        uint8_t delay_lower, delay_upper, index;
        *(volatile uint32_t *)(PSRAM_BASE) = 0x5a5a5a5a;
        while(qspi0_is_busy());
    
        for (index = 0; index < 16; index++) {
            while(qspi0_is_busy());
            qspi0_ctrl->read_cap.delay_capture = index;
            if (*(volatile uint32_t *)(PSRAM_BASE) == 0x5a5a5a5a) {
                break;
            }
        }
        if (index == 16) {
            system_regs->mdm_qspi_cfg.qspi0_ref_clk_en=0;
            return false;
        }
        else {
            delay_lower = index;
        }
        for (; index < 16; index++) {
            while(qspi0_is_busy());        
            qspi0_ctrl->read_cap.delay_capture = index;
            delay_upper = index;
            if (*(volatile uint32_t *)(PSRAM_BASE) != 0x5a5a5a5a) {
                break;
            }
        }
        
        psram_delay = ((delay_lower + delay_upper)>>1);
        while(qspi0_is_busy());
        qspi0_ctrl->read_cap.delay_capture = psram_delay;      
    }
    
    psram_cache_enable();
    
    return true;
    

    }


  • Global Moderator

    @hejun
    0_1787217931959_5a69fe4f-99d3-40e2-95d9-622129e4d627-图片.png

    最高可以192m



  • #include <stdint.h>
    #include <string.h>

    #include "plf.h"
    #include "driver_qspi.h"
    #include "driver_psram.h"
    #include "driver_system.h"
    #include "driver_pmu.h"
    #include "driver_cache.h"

    #include "co_printf.h"

    #ifdef INLINE
    #undef INLINE
    #endif
    #define INLINE

    #define PSRAM_ENABLE_Q_MODE 1
    #define PSRAM_CLK_DIV_SEL QSPI_BAUDRATE_DIV_2
    #define PSRAM_BASE QSPI1_DAC_ADDRESS

    #define PSRAM_READ_IDENTIFICATION 0x9F

    #define PSRAM_READ_OPCODE 0x03
    #define PSRAM_FAST_READ_OPCODE 0x0B
    #define PSRAM_FAST_QUAL_READ_OPCODE 0xEB

    #define PSRAM_PAGE_PROGRAM_OPCODE 0x02
    #define PSRAM_PAGE_QUAL_PROGRAM_OPCODE 0x38

    #define PSRAM_ENTER_QUAD_MODE_OPCODE 0x35
    #define PSRAM_EXIT_QUAD_MODE_OPCODE 0xF5

    #define PSRAM_RESET_ENABLE_OPCODE 0x66
    #define PSRAM_RESET_OPCODE 0x99

    #define QSPI0_STIG_MAX_SINGLE_LEN 8
    #define QSPI0_STIG_BANK_DEPTH 128

    #define QSPI0_HIGH_SPEED 1
    uint8_t psram_delay = 0xFF;
    static const struct qspi_stig_reg_t psram_read_id_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 3,
    .enable_read = 1,
    .opcode = PSRAM_READ_IDENTIFICATION,
    };

    static const struct qspi_stig_reg_t enter_quad_mode_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 0,
    .enable_read = 0,
    .opcode = PSRAM_ENTER_QUAD_MODE_OPCODE,
    };

    static const struct qspi_stig_reg_t reset_enable_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 0,
    .enable_read = 0,
    .opcode = PSRAM_RESET_ENABLE_OPCODE,
    };

    static const struct qspi_stig_reg_t reset_cmd = {
    .enable_bank = 0,
    .dummy_cycles = 0,
    .write_bytes = 0,
    .enable_write = 0,
    .addr_bytes = 0,
    .enable_mode = 0,
    .enable_cmd_addr = 0,
    .read_bytes = 0,
    .enable_read = 0,
    .opcode = PSRAM_RESET_OPCODE,
    };

    //static const struct qspi_stig_reg_t exit_quad_mode_cmd = {
    // .enable_bank = 0,
    // .dummy_cycles = 0,
    // .write_bytes = 0,
    // .enable_write = 0,
    // .addr_bytes = 0,
    // .enable_mode = 0,
    // .enable_cmd_addr = 0,
    // .read_bytes = 0,
    // .enable_read = 0,
    // .opcode = PSRAM_EXIT_QUAD_MODE_OPCODE,
    //};

    volatile struct qspi_regs_t *qspi0_ctrl = (volatile struct qspi_regs_t *)QSPI0_APB_BASE;

    INLINE void qspi0_cfg_set_enable(uint8_t en)
    {
    qspi0_ctrl->config.enable = en;
    }

    INLINE void qspi0_cfg_set_cpol(uint8_t high)
    {
    qspi0_ctrl->config.cpol = high;
    }

    INLINE void qspi0_cfg_set_cpha(uint8_t rising)
    {
    qspi0_ctrl->config.cpha = rising;
    }

    INLINE void qspi0_cfg_set_hold(uint8_t hold)
    {
    qspi0_ctrl->config.hold_pin = hold;
    }

    INLINE void qspi0_cfg_set_reset(uint8_t reset)
    {
    qspi0_ctrl->config.reset_pin = reset;
    }

    INLINE void qspi0_cfg_set_reset_sel(uint8_t dedicated)
    {
    qspi0_ctrl->config.reset_pin_conf = dedicated;
    }

    INLINE void qspi0_cfg_set_enable_dac(uint8_t en)
    {
    qspi0_ctrl->config.enable_DAC = en;
    }

    INLINE void qspi0_cfg_set_enable_legacy(uint8_t en)
    {
    qspi0_ctrl->config.enable_legacy = en;
    }

    INLINE void qspi0_cfg_set_write_protect(uint8_t wp)
    {
    qspi0_ctrl->config.write_en_pin = wp;
    }

    INLINE void qspi0_cfg_set_enable_remap(uint8_t en)
    {
    qspi0_ctrl->config.enable_AHB_remap = en;
    }

    static void qspi0_cfg_set_baudrate(uint8_t baudrate);

    INLINE void qspi0_cfg_set_enable_AHB_decoder(uint8_t en)
    {
    qspi0_ctrl->config.enable_AHB_decoder = en;
    }

    INLINE int qspi0_is_busy(void)
    {
    return (qspi0_ctrl->config.status == 0);
    }

    INLINE void qspi0_read_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->read_conf.opcode_no_XIP = opcode;
    }

    INLINE void qspi0_read_set_instruction_type(uint8_t type)
    {
    qspi0_ctrl->read_conf.instruction_type = type;
    }

    INLINE void qspi0_read_set_address_type(uint8_t type)
    {
    qspi0_ctrl->read_conf.addr_type = type;
    }

    INLINE void qspi0_read_set_data_type(uint8_t type)
    {
    qspi0_ctrl->read_conf.data_type = type;
    }

    INLINE void qspi0_read_set_mode_en(uint8_t en)
    {
    qspi0_ctrl->read_conf.enable_mode = en;
    }

    INLINE void qspi0_read_set_dummy_cycles(uint8_t cycles)
    {
    qspi0_ctrl->read_conf.dummy_cycles = cycles;
    }

    INLINE void qspi0_write_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->write_conf.opcode = opcode;
    }

    INLINE void qspi0_write_set_wel_dis(uint8_t disable)
    {
    qspi0_ctrl->write_conf.disable_WEL = disable;
    }

    INLINE void qspi0_write_set_address_type(uint8_t type)
    {
    qspi0_ctrl->write_conf.addr_type = type;
    }

    INLINE void qspi0_write_set_data_type(uint8_t type)
    {
    qspi0_ctrl->write_conf.data_type = type;
    }

    INLINE void qspi0_write_set_dummy_cycles(uint8_t cycles)
    {
    qspi0_ctrl->write_conf.dummy_cycles = cycles;
    }

    INLINE void qspi0_set_remap_address(uint32_t address)
    {
    qspi0_ctrl->remap_address = address;
    }

    INLINE void qspi0_set_mode_bit(uint8_t mode)
    {
    qspi0_ctrl->mode_bits = (uint32_t)mode;
    }

    INLINE void qspi0_poll_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->poll_cfg.opcode_poll = opcode;
    }

    INLINE void qspi0_poll_set_bit_index(uint8_t index)
    {
    qspi0_ctrl->poll_cfg.poll_bit_index = index;
    }

    INLINE void qspi0_poll_set_polarity(uint8_t pol)
    {
    qspi0_ctrl->poll_cfg.poll_polarity = pol;
    }

    INLINE void qspi0_poll_set_disable(uint8_t dis)
    {
    qspi0_ctrl->poll_cfg.disable_poll = dis;
    }

    INLINE void qspi0_poll_set_expire(uint8_t en, uint32_t duration)
    {
    qspi0_ctrl->poll_cfg.enable_expiration = en;
    if(en) {
    qspi0_ctrl->poll_expiration = duration;
    }
    }

    INLINE void qspi0_poll_set_poll_count(uint8_t count)
    {
    qspi0_ctrl->poll_cfg.poll_count = count;
    }

    INLINE void qspi0_poll_set_poll_delay(uint8_t delay)
    {
    qspi0_ctrl->poll_cfg.poll_repetition_delay = delay;
    }

    INLINE void qspi0_set_cmd_addr(uint32_t addr)
    {
    qspi0_ctrl->cmd_address = addr;
    }

    INLINE void qspi0_stig_set_opcode(uint8_t opcode)
    {
    qspi0_ctrl->cmd_ctrl.opcode = opcode;
    }

    INLINE void qspi0_stig_set_read_en(uint8_t en)
    {
    qspi0_ctrl->cmd_ctrl.enable_read = en;
    }

    INLINE void qspi0_stig_set_read_bytes(uint8_t bytes)
    {
    qspi0_ctrl->cmd_ctrl.read_bytes = bytes;
    }

    INLINE void qspi0_stig_set_addr(uint8_t en, uint8_t bytes, uint32_t addr)
    {
    qspi0_ctrl->cmd_ctrl.enable_cmd_addr = en;
    qspi0_ctrl->cmd_ctrl.addr_bytes = bytes;
    if(en) {
    qspi0_ctrl->cmd_address = addr;
    }
    }

    INLINE void qspi0_stig_set_mode_en(uint8_t en)
    {
    qspi0_ctrl->cmd_ctrl.enable_mode = en;
    }

    INLINE void qspi0_stig_set_write_en(uint8_t en)
    {
    qspi0_ctrl->cmd_ctrl.enable_write = en;
    }

    INLINE void qspi0_stig_set_write_bytes(uint8_t bytes)
    {
    qspi0_ctrl->cmd_ctrl.write_bytes = bytes;
    }

    INLINE void qspi0_stig_set_dummy_cycles(uint8_t cycles)
    {
    qspi0_ctrl->cmd_ctrl.dummy_cycles = cycles;
    }

    INLINE void qspi0_stig_set_mem_bank(uint8_t en, uint8_t bytes)
    {
    qspi0_ctrl->cmd_ctrl.enable_bank = en;
    if(en) {
    qspi0_ctrl->cmd_ctrl_mem.mem_bank_req_bytes = bytes;
    }
    }

    static void qspi0_cfg_set_baudrate(uint8_t baudrate)
    {
    qspi0_ctrl->config.baud_rate = baudrate;
    }

    static int qspi0_stig_cmd(struct qspi_stig_reg_t cmd, enum qspi_stig_cmd_type_t type, int len, uint8_t *buffer)
    {
    uint32_t tmp_u32[2];
    uint8_t *tmp_u8 = (uint8_t *)tmp_u32;

    if(type == QSPI_STIG_CMD_BANK_READ) {
        if(QSPI0_STIG_BANK_DEPTH < len) {
            return -1;
        }
    }
    else {
        if(QSPI0_STIG_MAX_SINGLE_LEN < len) {
            return -1;
        }
    }
    
    while(qspi0_is_busy());
    
    if(type == QSPI_STIG_CMD_EXE) {
        qspi0_ctrl->cmd_ctrl = cmd;
        qspi0_ctrl->cmd_ctrl.execute = 1;
        while(qspi0_ctrl->cmd_ctrl.progress_status);
    }
    else {
        if(type == QSPI_STIG_CMD_WRITE) {
            memcpy(tmp_u8, buffer, len);
            qspi0_ctrl->write_data_L = tmp_u32[0];
            qspi0_ctrl->write_data_H = tmp_u32[1];
            cmd.write_bytes = len - 1;
            qspi0_ctrl->cmd_ctrl = cmd;
            qspi0_ctrl->cmd_ctrl.execute = 1;
            while(qspi0_ctrl->cmd_ctrl.progress_status);
        }
        else {
            cmd.read_bytes = len - 1;
            qspi0_ctrl->cmd_ctrl = cmd;
            qspi0_ctrl->cmd_ctrl.execute = 1;
            while(qspi0_ctrl->cmd_ctrl.progress_status);
            if(type == QSPI_STIG_CMD_READ) {
                tmp_u32[0] = qspi0_ctrl->read_data_L;
                tmp_u32[1] = qspi0_ctrl->read_data_H;
                //co_printf("READ_L: 0x%08x, READ_H: 0x%08x.\r\n", tmp_u32[0], tmp_u32[1]);
                memcpy(buffer, tmp_u8, len);
            }
            else {
                //TBD, BANK READ
            }
        }
    }
    
    return 0;
    

    }

    void psram_enter_quad(void)
    {
    qspi0_stig_cmd(enter_quad_mode_cmd, QSPI_STIG_CMD_EXE, 0, NULL);
    }

    uint32_t psram_read_id(void)
    {
    uint32_t flash_id;
    qspi0_stig_cmd(psram_read_id_cmd, QSPI_STIG_CMD_READ, 3, (uint8_t *)&flash_id);
    return (flash_id&0xffffff);
    }

    static void psram_controller_init(uint16_t page_boundary)
    {
    while(qspi0_is_busy());

    #if PSRAM_ENABLE_Q_MODE == 1
    qspi0_read_set_opcode(PSRAM_FAST_READ_OPCODE);
    qspi0_read_set_instruction_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_dummy_cycles(4);
    qspi0_read_set_mode_en(0);
    qspi0_set_mode_bit(0);//8 bits data after addr

    qspi0_write_set_opcode(PSRAM_PAGE_QUAL_PROGRAM_OPCODE);
    qspi0_write_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_dummy_cycles(0);
    

    #else
    qspi0_read_set_opcode(PSRAM_FAST_QUAL_READ_OPCODE);
    qspi0_read_set_instruction_type(QSPI_WIRE_TYPE_STAND);
    qspi0_read_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_read_set_dummy_cycles(6);
    qspi0_read_set_mode_en(0);
    qspi0_set_mode_bit(0);//8 bits data after addr

    qspi0_write_set_opcode(PSRAM_PAGE_QUAL_PROGRAM_OPCODE);
    qspi0_write_set_address_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_data_type(QSPI_WIRE_TYPE_QIO);
    qspi0_write_set_dummy_cycles(0);
    

    #endif

    //init configuration register
    qspi0_cfg_set_cpol(0);
    qspi0_cfg_set_cpha(0);
    qspi0_cfg_set_enable_dac(1);
    qspi0_cfg_set_enable_legacy(0);
    qspi0_cfg_set_enable_remap(1);
    

    #if QSPI0_HIGH_SPEED
    qspi0_cfg_set_baudrate(PSRAM_CLK_DIV_SEL);
    #else
    qspi0_cfg_set_baudrate(QSPI_BAUDRATE_DIV_32);
    #endif
    qspi0_cfg_set_enable_AHB_decoder(1);
    qspi0_write_set_wel_dis(1);
    qspi0_poll_set_disable(1);

    #if QSPI0_HIGH_SPEED
    qspi0_ctrl->read_cap.delay_capture = 3;
    #else
    qspi0_ctrl->read_cap.delay_capture = 0;
    #endif
    qspi0_ctrl->read_cap.enable_loopback_clk = 1;
    qspi0_ctrl->delay.sel_start_offset = 2;
    qspi0_ctrl->delay.sel_end_offset = 2;
    qspi0_ctrl->delay.sel_dessert = 2;
    qspi0_ctrl->cs_ctrl.rd_brk_en = 1;
    qspi0_ctrl->cs_ctrl.disable_cs_after_first_byte = 0;
    qspi0_ctrl->cs_ctrl.page_boundary_protect_en = 1;
    qspi0_ctrl->cs_ctrl.page_boundary = page_boundary;
    qspi0_set_remap_address(QSPI1_DAC_ADDRESS);
    qspi0_cfg_set_enable(1);
    }

    void psram_cache_enable(void)
    {
    __CACHE_FLUSH(CACHE);
    __CACHE_WR_MODE_SET(CACHE, CACHE_WR_WRITE_THROUGH);
    __CACHE_ADDR_RANGEx_BANK_SET(CACHE, 0, 0x0000);
    __CACHE_ADDR_RANGEx_MASK_SET(CACHE, 0, 0x0000);
    __CACHE_ADDR_RANGEx_POL_SET(CACHE, 0, CACHE_POL_CACHABLE);
    __CACHE_ADDR_RANGEx_ENABLE(CACHE, 0);
    __CACHE_ENABLE(CACHE);
    }

    bool psram_init(enum psram_clk_sel_t clk_sel)
    {
    uint32_t ref_clk;

    if (clk_sel >= PSRAM_CLK_SEL_MAX) {
        return false;
    }
    
    switch (clk_sel) {
        case PSRAM_CLK_SEL_COREH_48M:
            system_regs->mdm_qspi_cfg.qspi0_ref_clk_sel = 0;
            ref_clk = 48000000;
            break;
        case PSRAM_CLK_SEL_COREH_96M:
            system_regs->mdm_qspi_cfg.qspi0_ref_clk_sel = 1;
            ref_clk = 96000000;
            break;      
        default:
            return false;
    }
    system_regs->mdm_qspi_cfg.qspi0_ref_clk_en=1;
    system_regs->mdm_qspi_cfg.qspi0_hclk_en=1;
    system_regs->mdm_qspi_cfg.qspi0_io_ctl_oen=0;
    
    if(psram_delay == 0xFF)
    {
        qspi0_stig_cmd(reset_enable_cmd, QSPI_STIG_CMD_EXE, 0, NULL);
        qspi0_stig_cmd(reset_cmd, QSPI_STIG_CMD_EXE, 0, NULL);
    

    #if PSRAM_ENABLE_Q_MODE == 1
    psram_enter_quad();
    #endif // PSRAM_ENABLE_Q_MODE == 1
    }

    /* about 100 bytes can be transmitted during 8us when QSPI clock is 24MHz */
    ref_clk /= ((PSRAM_CLK_DIV_SEL+1) * 2);
    uint32_t bytes = ref_clk / 240000;
    uint32_t boundary_cfg = 1 << (31 - __CLZ(bytes));
    if (boundary_cfg >= 0x100) {
        boundary_cfg = 0x100;
    }
    psram_controller_init(boundary_cfg);
    
    if(psram_delay != 0xFF)
    {
        qspi0_ctrl->read_cap.delay_capture = psram_delay;
    }
    else
    {
        /* detect read capture delay configuration */
        uint8_t delay_lower, delay_upper, index;
        *(volatile uint32_t *)(PSRAM_BASE) = 0x5a5a5a5a;
        while(qspi0_is_busy());
    
        for (index = 0; index < 16; index++) {
            while(qspi0_is_busy());
            qspi0_ctrl->read_cap.delay_capture = index;
            if (*(volatile uint32_t *)(PSRAM_BASE) == 0x5a5a5a5a) {
                break;
            }
        }
        if (index == 16) {
            system_regs->mdm_qspi_cfg.qspi0_ref_clk_en=0;
            return false;
        }
        else {
            delay_lower = index;
        }
        for (; index < 16; index++) {
            while(qspi0_is_busy());        
            qspi0_ctrl->read_cap.delay_capture = index;
            delay_upper = index;
            if (*(volatile uint32_t *)(PSRAM_BASE) != 0x5a5a5a5a) {
                break;
            }
        }
        
        psram_delay = ((delay_lower + delay_upper)>>1);
        while(qspi0_is_busy());
        qspi0_ctrl->read_cap.delay_capture = psram_delay;      
        co_printf("psram_delay:%d\r\n", psram_delay);
    }
    
    psram_cache_enable();
    
    return true;
    

    }
    1、当前SDK没有192M的配置 确认是可以直接改成192M?
    2、 uint32_t bytes = ref_clk / 240000; 请问这个240000是什么意思,是否可以调整?



  • @zr #include "lvgl.h"
    #include "stdlib.h"
    #include "stdio.h"
    #include "gui_guider.h"
    #include "ui_manager.h"
    #include "ble_handle.h"
    #include "key_handle.h"
    #include "func.h"
    #include <math.h>

    #define BTN_HEIGHT 32 // 按键高度(固定,可根据需求调整)
    #define BTN_WIDTH 200 // 按键宽度
    #define PAGE_MAX_BTN 5 // 页面最大按钮数
    #define BTN_MAX_NUM 5 // 总共按钮数

    static const char *btn_text[] = {
    "device",
    "Relief",
    "20min",
    "Favorite",
    "Start",
    };

    static void update_channel_info(void);

    static uint8_t mainChoose_start_index = 0; // 主界面起始选项起始下标

    static lv_coord_t btn_y[BTN_MAX_NUM] = {45, 79, 114, 149, 184};
    static lv_coord_t btn_width[BTN_MAX_NUM] = {164, 200, 195, 149, 124};

    static lv_style_t choose_btn;
    static lv_style_t nochoose_btn;
    static lv_style_t startnochoose_btn;

    static lv_style_t style_arrow;
    static lv_style_t nostyle_arrow;
    static lv_obj_t *btn[BTN_MAX_NUM];

    static lv_obj_t *btn_num_label[BTN_MAX_NUM] = {NULL}; // 数字标签指针
    static lv_obj_t *btn_text_label[BTN_MAX_NUM] = {NULL}; // 文本标签指针(全部按钮)
    static lv_obj_t *sym[BTN_MAX_NUM] = {NULL};

    //static lv_obj_t *label;

    static const lv_img_dsc_t * line_img[4] = {&_01_alpha_196x4, &_02_alpha_210x4, &_03_alpha_202x4, &_04_alpha_160x4};
    const lv_img_dsc_t * device_icon[3] = {&_menu_device1_alpha_27x27, &_menu_device2_alpha_27x27, &_device_all_alpha_64x27};
    static uint8_t last_index = 0;
    static void btn_eventKey_callback(lv_event_t *e)
    {
    lv_obj_t *btn = lv_event_get_target(e);
    lv_event_code_t code = lv_event_get_code(e);
    uint32_t key = lv_event_get_key(e);
    uint8_t connect_status, num = (uint8_t)(uintptr_t)lv_obj_get_user_data(btn);
    lv_ui *ui = lv_event_get_user_data(e);
    struct DEVINFO *dev = get_dev();
    struct OUTINFO * current_out = get_out(dev->currentch);

    if (ui->choose_page == NULL)
        return;
    
    if (code == LV_EVENT_KEY)
    {
        switch(key)
        {
            case LV_KEY_ENTER:
                switch(num)
                {
                    case 1: // 小模式
                    {
                        last_index = num;
                        ui_manager_switch(SLAVEMODE_SCREEN, UI_ANIM_NONE, NULL);
                        break;
                    }
                    case 3: // 喜爱模式
                    {
                        last_index = num;
                        ui_manager_switch(FAVORITE_CHOOSE_SCREEN, UI_ANIM_NONE, NULL);
                        break;
                    }
                    case 4: // 启动
                    {
                        last_index = num;
                        ui_manager_switch(CONTROL_SCREEN, UI_ANIM_NONE, NULL);
                        break;
                    }
                    default:
                        break;
                }
                break;
            case LV_KEY_LEFT:
                switch(num)
                {
                    case 0: // 设备
                    {
                        connect_status = get_hosts_status(STATUS_TYPE_CONNECT);
                        if (connect_status == 0x03)
                        {
                            dev->currentch = get_nextDeviceId(dev->currentch, 1);
                            update_channel_info();
                        }
                        else if(connect_status && (dev->currentch == MAX_CHANNEL))
                        {
                            if (connect_status == 0x01)
                            {
                                dev->currentch = 0;
                            }
                            else
                            {
                                dev->currentch = 1;
                            }
                            update_channel_info();
                        }
                        break;
                    }
                    case 1: // 小模式
                    {
                        if (current_out->slave_mode > MIN_SLAVE_MODE)
                            current_out->slave_mode--;
                        else
                            current_out->slave_mode = MAX_SLAVE_MODE - 1;
                        device_set_mode(current_out, current_out->master_mode, current_out->slave_mode);
                        mainPage_slavemode_update(current_out->master_mode, current_out->slave_mode);
                        // mainPage_strength_update(current_out->strength);
                        packet_ble_txdata(RUN_DPID, get_dev()->currentch == MAX_CHANNEL ? SYNC_DEVICE_ID : current_out->channel);
                        break;
                    }
                    case 2: // 时间
                    {
                        // ========== 减少5分钟(结果对齐到5的倍数) ==========
                        if (current_out->worktime > MIN_WORK_TIME)
                        {
                            // 先转成分钟(向上取整),再对齐到5的倍数,最后减5
                            uint8_t minutes = (current_out->worktime + 59) / 60;
                            minutes = ((minutes + 4) / 5) * 5;   // 向上对齐到5的倍数
                            if (minutes > 5)
                            {
                                current_out->setworktime = current_out->worktime = (minutes - 5) * 60;
                            }
                            else
                            {
                                current_out->setworktime = current_out->worktime = MAX_WORK_TIME;  // 回绕
                            }
                        }
                        else
                        {
                            current_out->setworktime = current_out->worktime = MAX_WORK_TIME;
                        }
                        mainPage_worktime_update(current_out->worktime);
                        packet_ble_txdata(TIME_DPID, get_dev()->currentch == MAX_CHANNEL ? SYNC_DEVICE_ID : current_out->channel);
                        break;
                    }
                    // case 4: // 强度
                    // {
                    //     if (device_dec_strength(current_out))
                    //     {
                    //         mainPage_strength_update(current_out->strength);
                    //         packet_ble_txdata(RUN_DPID, get_dev()->currentch == MAX_CHANNEL ? SYNC_DEVICE_ID : current_out->channel);
                    //     }
                    //     else    // 没有处理任何操作直接return,防止调用sync_setChannelInfo
                    //         return;
                    //     break;
                    // }
                    default:
                        break;
                }
                break;
            case LV_KEY_RIGHT:
            {
                switch(num)
                {
                    case 0: // 设备
                    {
                        connect_status = get_hosts_status(STATUS_TYPE_CONNECT);
                        if (connect_status == 0x03)
                        {
                            dev->currentch = get_nextDeviceId(dev->currentch, 0);
                            update_channel_info();
                        }
                        else if(connect_status && (dev->currentch == MAX_CHANNEL))
                        {
                            if (connect_status == 0x01)
                            {
                                dev->currentch = 0;
                            }
                            else
                            {
                                dev->currentch = 1;
                            }
                            update_channel_info();
                        }
                        break;
                    }
                    case 1: // 小模式
                    {
                        current_out->slave_mode++;
                        current_out->slave_mode = current_out->slave_mode % MAX_SLAVE_MODE;
                        device_set_mode(current_out, current_out->master_mode, current_out->slave_mode);
                        mainPage_slavemode_update(current_out->master_mode, current_out->slave_mode);
                        // mainPage_strength_update(current_out->strength);
                        packet_ble_txdata(RUN_DPID, get_dev()->currentch == MAX_CHANNEL ? SYNC_DEVICE_ID : current_out->channel);
                        break;
                    }
                    case 2: // 时间
                    {
                        // ========== 增加5分钟(结果对齐到5的倍数) ==========
                        if (current_out->worktime < MAX_WORK_TIME)
                        {
                            // 先转成分钟(向上取整),再对齐到5的倍数,再加5
                            uint8_t minutes = (current_out->worktime + 59) / 60;
                            minutes = (minutes / 5) * 5;   // 向上对齐到5的倍数
                            if (minutes <= (MAX_WORK_TIME / 60 - 5))
                            {
                                current_out->setworktime = current_out->worktime = (minutes + 5) * 60;
                            }
                            else
                            {
                                current_out->setworktime = current_out->worktime = MIN_WORK_TIME;  // 回绕
                            }
                        }
                        else
                        {
                            current_out->setworktime = current_out->worktime = MIN_WORK_TIME;
                        }
                        mainPage_worktime_update(current_out->worktime);
                        packet_ble_txdata(TIME_DPID, get_dev()->currentch == MAX_CHANNEL ? SYNC_DEVICE_ID : current_out->channel);
                        break;
                    }
                    // case 4: // 强度
                    // {
                    //     if (device_add_strength(current_out))
                    //     {
                    //         mainPage_strength_update(current_out->strength);
                    //         packet_ble_txdata(RUN_DPID, get_dev()->currentch == MAX_CHANNEL ? SYNC_DEVICE_ID : current_out->channel);
                    //     }
                    //     else    // 没有处理任何操作直接return,防止调用sync_setChannelInfo
                    //         return;
                    //     break;
                    // }
                    default:
                        break;
                }
                break;
            }
            case LV_KEY_DOUBLE:
            {
                current_out->pause = !current_out->pause;
                packet_ble_txdata(RUN_DPID, get_dev()->currentch == MAX_CHANNEL ? SYNC_DEVICE_ID : current_out->channel);
                mainPage_pause_update(current_out->pause);
                break;
            }
            default:
                return;
        }
        if (dev->currentch == MAX_CHANNEL && (num != 0 || key == LV_KEY_DOUBLE))  // 同步通道下调整了设置,自动进入同步模式(设备切换除外)
            sync_setChannelInfo();
        else if (dev->currentch != MAX_CHANNEL && (num != 0 || key == LV_KEY_DOUBLE))  // 独立通道下调整了设置,自动退出同步模式(设备切换除外)
            exit_syncMode();
    }
    

    }

    void mainPage_device_update(uint8_t channel)
    {
    if (guider_ui.choose_page == NULL)
    return;
    lv_img_set_src(sym[0], device_icon[channel]);
    }

    void mainPage_slavemode_update(uint8_t masterMode, uint8_t slaveMode)
    {
    if (guider_ui.choose_page == NULL)
    return;
    lv_label_set_text(btn_text_label[1], slave_btn_text[slaveMode]);
    }

    void mainPage_worktime_update(uint16_t time)
    {
    uint8_t min = (time - 1) / 60 + 1;
    if (guider_ui.choose_page == NULL || btn_num_label[2] == NULL)
    return;
    lv_label_set_text_fmt(btn_num_label[2], "%02d", min);
    }

    void mainPage_mode_update(uint8_t slave_mode)
    {
    if (guider_ui.choose_page == NULL)
    return;
    mainPage_slavemode_update(0, slave_mode);
    }

    // void mainPage_strength_update(uint16_t strength)
    // {
    // if (guider_ui.choose_page == NULL)
    // return;
    // lv_label_set_text_fmt(btn_num_label[4], "%02d", strength);
    // }

    void mainPage_pause_update(uint8_t pause)
    {
    if (guider_ui.choose_page == NULL)
    return;
    if (pause)
    {
    lv_img_set_src(sym[2], &_pause_alpha_18x18);
    lv_obj_set_style_img_recolor_opa(sym[2], LV_OPA_0, 0);
    }
    else
    {
    lv_img_set_src(sym[2], &_time_alpha_18x18);
    lv_obj_set_style_img_recolor_opa(sym[2], LV_OPA_COVER, 0);
    }
    }

    // CHOOSE_MENU的style初始化
    static void style_init(void)
    {
    lv_style_init(&choose_btn);

    lv_style_set_radius(&choose_btn, 50);
    lv_style_set_bg_color(&choose_btn, lv_color_hex(UI_MAIN_COLOR));
    lv_style_set_bg_opa(&choose_btn, 255);
    lv_style_set_height(&choose_btn, BTN_HEIGHT);
    lv_style_set_text_align(&choose_btn, LV_TEXT_ALIGN_CENTER);
    lv_style_set_text_color(&choose_btn, lv_color_hex(0x000000));
    lv_style_set_shadow_opa(&choose_btn, LV_OPA_TRANSP); // 阴影透明
    lv_style_set_shadow_width(&choose_btn, 0);           // 阴影宽度0
    lv_style_set_border_opa(&choose_btn, 0);
    // 关闭过渡动画(关键)
    lv_style_set_transition(&choose_btn, NULL);
    
    lv_style_init(&nochoose_btn);
    
    lv_style_set_radius(&nochoose_btn, 50);
    lv_style_set_bg_color(&nochoose_btn, lv_color_hex(0x000000));
    lv_style_set_bg_opa(&nochoose_btn, 0);
    lv_style_set_height(&nochoose_btn, BTN_HEIGHT);
    lv_style_set_text_align(&nochoose_btn, LV_TEXT_ALIGN_CENTER);
    lv_style_set_text_color(&nochoose_btn, lv_color_hex(UI_MAIN_COLOR));
    lv_style_set_shadow_opa(&nochoose_btn, LV_OPA_TRANSP); // 阴影透明
    lv_style_set_shadow_width(&nochoose_btn, 0);           // 阴影宽度0
    lv_style_set_border_opa(&nochoose_btn, 0);
    // 关闭过渡动画(关键)
    lv_style_set_transition(&nochoose_btn, NULL);
    
    lv_style_init(&startnochoose_btn);
    
    lv_style_set_radius(&startnochoose_btn, 50);
    lv_style_set_bg_color(&startnochoose_btn, lv_color_hex(0x000000));
    lv_style_set_bg_opa(&startnochoose_btn, 0);
    lv_style_set_height(&startnochoose_btn, BTN_HEIGHT);
    lv_style_set_text_align(&startnochoose_btn, LV_TEXT_ALIGN_CENTER);
    lv_style_set_text_color(&startnochoose_btn, lv_color_hex(UI_MAIN_COLOR));
    lv_style_set_shadow_opa(&startnochoose_btn, LV_OPA_TRANSP); // 阴影透明
    lv_style_set_shadow_width(&startnochoose_btn, 0);           // 阴影宽度0
    lv_style_set_border_color(&startnochoose_btn, lv_color_hex(UI_MAIN_COLOR));
    lv_style_set_border_width(&startnochoose_btn, 2);
    lv_style_set_border_opa(&startnochoose_btn, 255);
    // 关闭过渡动画(关键)
    lv_style_set_transition(&startnochoose_btn, NULL);
    
    lv_style_init(&style_arrow);
    lv_style_set_text_opa(&style_arrow, 255);
    // 关闭过渡动画(关键)
    lv_style_set_transition(&style_arrow, NULL);
    
    lv_style_init(&nostyle_arrow);
    lv_style_set_text_opa(&nostyle_arrow, 0);
    // 关闭过渡动画(关键)
    lv_style_set_transition(&nostyle_arrow, NULL);
    

    }

    static void refresh_all_btn_pos(lv_ui *ui)
    {
    uint8_t index = 0;
    // 重置按钮位置
    for (index = 0; index < BTN_MAX_NUM; index++)
    {
    if (index >= (mainChoose_start_index + PAGE_MAX_BTN) || index < mainChoose_start_index)
    { // 超出屏幕显示数量的按钮放到屏幕外面
    lv_obj_set_size(btn[index], btn_width[index], 33);
    lv_obj_align(btn[index], LV_ALIGN_TOP_MID, 0, 0);
    lv_obj_set_y(btn[index], 250);
    }
    else
    {
    lv_obj_set_size(btn[index], btn_width[index - mainChoose_start_index], 33);
    lv_obj_align(btn[index], LV_ALIGN_TOP_MID, 0, 0);
    lv_obj_set_y(btn[index], btn_y[index - mainChoose_start_index]);
    }
    }
    }

    // 更新子对象状态
    static void sync_focus_state_to_children(lv_obj_t *parent, lv_ui *ui) {
    // 判断父对象是否处于聚焦状态
    uint8_t index = 0;
    uint8_t num = 0;
    bool is_focused = lv_obj_has_state(parent, LV_STATE_FOCUSED);
    co_printf("sync_focus_state_to_children\r\n");
    // 遍历父对象的所有子对象(箭头、文字等)
    lv_obj_t *child = lv_obj_get_child(parent, 0);
    num = (uint8_t)(uintptr_t)lv_obj_get_user_data(parent);
    while(child != NULL) {
    if(is_focused) {
    // 子对象添加聚焦状态
    lv_obj_add_state(child, LV_STATE_FOCUSED);
    last_index = num;
    if (sym[num] != NULL)
    lv_obj_set_style_img_recolor(sym[num], lv_color_hex(0x000000), 0);
    } else {
    // 子对象清除聚焦状态
    lv_obj_clear_state(child, LV_STATE_FOCUSED);
    if (sym[num] != NULL)
    lv_obj_set_style_img_recolor(sym[num], lv_color_hex(UI_MAIN_COLOR), 0);
    }
    // 切换到下一个子对象
    child = lv_obj_get_child(parent, lv_obj_get_child_id(child) + 1);
    }
    if (is_focused)
    { // 获取当前焦点下标
    num = (uint8_t)(uintptr_t)lv_obj_get_user_data(parent);
    // 根据当前焦点的下标判断是否移动起始下标
    if (num < mainChoose_start_index)
    {
    mainChoose_start_index--;
    refresh_all_btn_pos(ui);
    }
    else if (num >= (mainChoose_start_index + PAGE_MAX_BTN))
    {
    mainChoose_start_index++;
    refresh_all_btn_pos(ui);
    }
    }
    for (index = 0; index < 3; index++)
    {
    // 隐藏当前选项的上一线条
    if ((num - mainChoose_start_index) == index)
    lv_obj_add_flag(ui->choose_page_img[index], LV_OBJ_FLAG_HIDDEN);
    else
    lv_obj_clear_flag(ui->choose_page_img[index], LV_OBJ_FLAG_HIDDEN);
    }
    }

    // 按钮状态变化事件回调
    static void btn_state_change_cb(lv_event_t *e) {
    lv_obj_t *btn = lv_event_get_target(e);
    lv_event_code_t code = lv_event_get_code(e);
    lv_ui *ui = lv_event_get_user_data(e);
    if (ui->choose_page == NULL)
    return;
    sync_focus_state_to_children(btn, ui);
    }

    static void update_channel_info(void)
    {
    struct OUTINFO * current_out = get_out(get_dev()->currentch);
    uint8_t min = (current_out->worktime - 1) / 60 + 1;

    lv_img_set_src(sym[0], device_icon[get_dev()->currentch]);
    lv_label_set_text(btn_text_label[1], slave_btn_text[current_out->slave_mode]);
    mainPage_pause_update(current_out->pause);
    lv_label_set_text_fmt(btn_num_label[2], "%02d", min);
    lv_label_set_text(btn_text_label[2], "min");
    

    }

    void create_mainChoose_page(lv_ui *ui)
    {
    uint8_t index = 0;
    struct OUTINFO * current_out = get_out(get_dev()->currentch);
    uint8_t min = (current_out->worktime - 1) / 60 + 1;

    const lv_coord_t line_x[4] = {23, 16, 20, 41}; 
    const lv_coord_t line_y[4] = {78, 112, 147, 182};
    
    co_printf("enter create_mainChoose_page\r\n");
    style_init();
    
    //Write codes choose_page
    ui->choose_page = lv_obj_create(lv_scr_act());
    lv_obj_remove_style_all(ui->choose_page);
    lv_obj_set_size(ui->choose_page, 240, 240);
    lv_obj_set_scrollbar_mode(ui->choose_page, LV_SCROLLBAR_MODE_OFF);
    
    //Write style for choose_page, Part: LV_PART_MAIN, State: LV_STATE_DEFAULT.
    lv_obj_set_style_bg_opa(ui->choose_page, 255, LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_set_style_bg_color(ui->choose_page, lv_color_hex(0x000000), LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_set_style_bg_grad_dir(ui->choose_page, LV_GRAD_DIR_NONE, LV_PART_MAIN|LV_STATE_DEFAULT);
    
    //Write codes choose_page_img
    for (index = 0; index < 3; index++)
    {
        ui->choose_page_img[index] = lv_img_create(ui->choose_page);
        lv_obj_add_flag(ui->choose_page_img[index], LV_OBJ_FLAG_CLICKABLE);
        lv_img_set_src(ui->choose_page_img[index], line_img[index]);
        lv_img_set_pivot(ui->choose_page_img[index], 50,50);
        lv_img_set_angle(ui->choose_page_img[index], 0);
        lv_obj_set_pos(ui->choose_page_img[index], line_x[index], line_y[index]);
        lv_obj_set_size(ui->choose_page_img[index], line_img[index]->header.w, line_img[index]->header.h);
    
        //Write style for choose_page_img, Part: LV_PART_MAIN, State: LV_STATE_DEFAULT.
        lv_obj_set_style_img_recolor_opa(ui->choose_page_img[index], 0, LV_PART_MAIN|LV_STATE_DEFAULT);
        lv_obj_set_style_img_opa(ui->choose_page_img[index], 255, LV_PART_MAIN|LV_STATE_DEFAULT);
        lv_obj_set_style_radius(ui->choose_page_img[index], 0, LV_PART_MAIN|LV_STATE_DEFAULT);
        lv_obj_set_style_clip_corner(ui->choose_page_img[index], true, LV_PART_MAIN|LV_STATE_DEFAULT);
    }
    lv_obj_clear_flag(ui->choose_page, LV_OBJ_FLAG_SCROLLABLE);
    
    for (index = 0; index < BTN_MAX_NUM; index++)
    {
        /**************
        ** 按键初始化(保持不变)
        **************/
        btn[index] = lv_btn_create(ui->choose_page);
        lv_obj_set_style_outline_width(btn[index], 0, LV_STATE_FOCUS_KEY);
        lv_obj_set_size(btn[index], btn_width[index - mainChoose_start_index], 33);
        lv_obj_align(btn[index], LV_ALIGN_TOP_MID, 0, 0);
        lv_obj_remove_style(btn[index], NULL, LV_STATE_PRESSED);    // 移除按下时的缩放动画
        
        if (index >= (mainChoose_start_index + PAGE_MAX_BTN) || index < mainChoose_start_index)
            lv_obj_set_y(btn[index], 250);
        else
            lv_obj_set_y(btn[index], btn_y[index - mainChoose_start_index]);
        
        lv_obj_set_user_data(btn[index], (void *)(uintptr_t)index);
        // lv_obj_add_style(btn[index], &choose_btn, LV_STATE_FOCUSED); // 不能在这里设置样式,切换界面时选项会闪烁
        if (index == 4)
        {
            lv_obj_add_style(btn[index], &startnochoose_btn, LV_STATE_DEFAULT);
        }
        else
        {
            lv_obj_add_style(btn[index], &nochoose_btn, LV_STATE_DEFAULT);
        }
        
        // 设置flex布局(所有按钮统一用 SPACE_BETWEEN,保证左右箭头贴边)
        lv_obj_set_flex_flow(btn[index], LV_FLEX_FLOW_ROW);
        lv_obj_set_flex_align(btn[index], LV_FLEX_ALIGN_SPACE_BETWEEN, 
                              LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
        lv_obj_set_style_pad_left(btn[index], 5, LV_STATE_DEFAULT);
        lv_obj_set_style_pad_right(btn[index], 5, LV_STATE_DEFAULT);
        
        // 添加事件回调(保持不变)
        lv_obj_add_event_cb(btn[index], btn_state_change_cb, LV_EVENT_FOCUSED, ui);
        lv_obj_add_event_cb(btn[index], btn_state_change_cb, LV_EVENT_DEFOCUSED, ui);
        lv_obj_add_event_cb(btn[index], btn_eventKey_callback, LV_EVENT_KEY, ui);
    
        /**************
        ** 左箭头(所有按钮都有)
        **************/
        lv_obj_t *arrow_left = lv_label_create(btn[index]);
        lv_label_set_text(arrow_left, "\xE2\x97\x80");
        lv_obj_set_style_text_font(arrow_left, &lv_font_SourceHanSerifSC_Regular_16, LV_STATE_DEFAULT);
        lv_obj_add_style(arrow_left, &style_arrow, LV_STATE_FOCUSED);
        lv_obj_add_style(arrow_left, &nostyle_arrow, LV_STATE_DEFAULT);
        if ((index == 3) || (index == 4))
        {
            lv_obj_add_style(arrow_left, &nostyle_arrow, LV_STATE_FOCUSED);
        }
    
        /**************
        ** 中间内容区(区分处理)
        **************/
        if (index == 0)
        {
            lv_obj_t *mid = lv_obj_create(btn[index]);
            lv_obj_remove_style_all(mid);           // 去掉默认边框、背景
            lv_obj_set_size(mid, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
            lv_obj_set_flex_grow(mid, 1);           // 占据中间所有剩余空间
            lv_obj_set_flex_flow(mid, LV_FLEX_FLOW_ROW);
            lv_obj_set_flex_align(mid, LV_FLEX_ALIGN_CENTER,   // 水平居中
                                  LV_FLEX_ALIGN_CENTER,         // 垂直居中
                                  LV_FLEX_ALIGN_CENTER);
            
            sym[index] = lv_img_create(mid);
            lv_img_set_src(sym[index], device_icon[get_dev()->currentch]);
            // 重新着色
            set_img_recolor(sym[index], UI_MAIN_COLOR);
    

    // lv_obj_set_style_img_recolor(sym[index], lv_color_hex(UI_MAIN_COLOR), 0);
    // lv_obj_set_style_img_recolor_opa(sym[index], LV_OPA_COVER, 0);
    }
    else if (index == 2)
    {
    // 特殊按钮:左箭头 | [符号 数字 文本] | 右箭头
    // 中间整体用一个容器包裹,内部居中对齐
    lv_obj_t *mid = lv_obj_create(btn[index]);
    lv_obj_remove_style_all(mid); // 去掉默认边框、背景
    lv_obj_set_size(mid, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
    lv_obj_set_flex_grow(mid, 1); // 占据中间所有剩余空间
    lv_obj_set_flex_flow(mid, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(mid, LV_FLEX_ALIGN_CENTER, // 水平居中
    LV_FLEX_ALIGN_CENTER, // 垂直居中
    LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_column(mid, 3, 0); // 符号/数字/文本之间的间距

            // 1) 符号
            sym[index] = lv_img_create(mid);//lv_label_create(mid);
            // 重新着色
            set_img_recolor(sym[index], UI_MAIN_COLOR);
            if ((index == 2))
            {
                mainPage_pause_update(current_out->pause);
            }
    

    // lv_obj_set_style_img_recolor(sym[index], lv_color_hex(UI_MAIN_COLOR), 0);
    // lv_obj_set_style_img_recolor_opa(sym[index], LV_OPA_COVER, 0);

            // 2) 数字(可变,保存指针方便后续更新)
            if (index == 2) // 时间初始值
            {
                btn_num_label[index] = lv_label_create(mid);
                lv_obj_set_style_text_font(btn_num_label[index], &lv_font_arialbd_20, 0);
                lv_label_set_text_fmt(btn_num_label[index], "%02d", min);
            }
            
            // 3) 单位文本
            if ((index == 2))
            {
                btn_text_label[index] = lv_label_create(mid);
                lv_obj_set_style_text_font(btn_text_label[index], &lv_font_Arial_16, 0);
                lv_label_set_text(btn_text_label[index], "min");
            }
        }
        else
        {
            // 普通按钮:左箭头 | 文本 | 右箭头
            btn_text_label[index] = lv_label_create(btn[index]);
            switch(index)
            {
                case 1:
                    lv_label_set_text(btn_text_label[index], slave_btn_text[current_out->slave_mode]);
                    break;
                default:
                    lv_label_set_text(btn_text_label[index], btn_text[index]);
                    break;
            }
            lv_obj_set_style_text_font(btn_text_label[index], &lv_font_arialbd_20, LV_STATE_DEFAULT);
            
            if (index != 4)
            {   // 除最后一项收藏按钮只有文本外,其余选项均采用flex布局
                lv_obj_set_flex_grow(btn_text_label[index], 1); // 文本标签占据按钮内所有剩余空间
    

    // lv_label_set_long_mode(btn_text_label[index], LV_LABEL_LONG_SCROLL);
    // 普通按钮文本在flex_grow后自然占据中间,配合SPACE_BETWEEN即可
    }
    }

        /**************
        ** 右箭头(所有按钮都有)
        **************/
        lv_obj_t *arrow_right = lv_label_create(btn[index]);
        lv_label_set_text(arrow_right, "\xE2\x96\xB6");
        lv_obj_set_style_text_font(arrow_right, &lv_font_SourceHanSerifSC_Regular_16, LV_STATE_DEFAULT);
        lv_obj_add_style(arrow_right, &style_arrow, LV_STATE_FOCUSED);
        lv_obj_add_style(arrow_right, &nostyle_arrow, LV_STATE_DEFAULT);
        // 收藏按钮不需要左右箭头直接隐藏
        if ((index == 3) || (index == 4))
        {
            lv_obj_add_style(arrow_right, &nostyle_arrow, LV_STATE_FOCUSED);
        }
        if ((index == 3) || (index == 4))
        {
            lv_obj_add_style(arrow_right, &nostyle_arrow, LV_STATE_FOCUSED);
        }
    }
    lv_group_set_wrap(lv_group_get_default(), false);
    lv_group_focus_obj(btn[last_index]);
    for (index = 0; index < BTN_MAX_NUM; index++)
    {
        lv_obj_add_style(btn[index], &choose_btn, LV_STATE_FOCUSED);
    }
    co_printf("exit create_mainChoose_page\r\n");
    lv_obj_update_layout(ui->choose_page);
    

    }

    /// 创建开机页面UI
    static lv_obj_t *ui_mainChoose_create(void)
    {
    create_mainChoose_page(&guider_ui);

    return guider_ui.choose_page;
    

    }

    /// 进入开机页面
    static void ui_mainChoose_enter(void* param)
    {
    (void)param;
    }

    /// 退出开机页面
    static void ui_mainChoose_exit(void)
    {
    uint8_t i = 0;
    get_guiderUi_struct()->choose_page = NULL; //切换页面,置为NULL后续此界面的回调函数全停止处理防止读取到以及释放的资源
    lv_style_reset(&choose_btn);
    lv_style_reset(&nochoose_btn);
    lv_style_reset(&style_arrow);
    lv_style_reset(&nostyle_arrow);
    for (i = 0; i < BTN_MAX_NUM; i++)
    {
    if (sym[i] != NULL)
    {
    lv_obj_del(sym[i]);
    sym[i] = NULL;
    }
    if (btn_num_label[i] != NULL)
    {
    lv_obj_del(btn_num_label[i]);
    btn_num_label[i] = NULL;
    }
    if (btn_text_label[i] != NULL)
    {
    lv_obj_del(btn_text_label[i]);
    btn_text_label[i] = NULL;
    }
    if (btn[i] != NULL)
    {
    lv_obj_del(btn[i]);
    btn[i] = NULL;
    }
    }
    }

    /// 销毁开机页面
    static void ui_mainChoose_destroy(void)
    {
    // lv_obj_del(label);
    }

    /// 开机页面操作函数表
    const ui_page_ops_t ui_mainChoose_ops = {
    .create = ui_mainChoose_create,
    .enter = ui_mainChoose_enter,
    .exit = ui_mainChoose_exit,
    .destroy = ui_mainChoose_destroy
    };
    [16:11:18.388]收←◆enter create_mainChoose_page

    [16:11:18.771]收←◆sync_focus_state_to_children

    [16:11:18.995]收←◆sync_focus_state_to_children

    [16:11:19.043]收←◆exit create_mainChoose_page
    LVGL生成一帧内部耗时太长,请问可以怎么改进优化,芯片FR800GP-U