From ecef7cdd6184b5717c8eb8141a284d0f1a443172 Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Sat, 20 Dec 2025 11:27:34 +0800 Subject: [PATCH] feat(dsi_lcd): pixel clock frequency can be a float number --- .../esp_hal_lcd/include/hal/mipi_dsi_hal.h | 13 +-- components/esp_hal_lcd/mipi_dsi_hal.c | 82 ++++++++++++------- components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c | 7 +- components/esp_lcd/dsi/esp_lcd_panel_dpi.c | 5 +- .../esp_lcd/dsi/include/esp_lcd_mipi_dsi.h | 4 +- 5 files changed, 67 insertions(+), 44 deletions(-) diff --git a/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h b/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h index d7f3737f52..3c1692383b 100644 --- a/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h +++ b/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -30,8 +30,9 @@ typedef struct dsi_brg_dev_t *mipi_dsi_bridge_soc_handle_t; typedef struct { mipi_dsi_host_soc_handle_t host; /*!< Pointer to the host controller registers */ mipi_dsi_bridge_soc_handle_t bridge; /*!< Pointer to the bridge controller registers */ - uint32_t lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */ - uint32_t dpi_clock_freq_mhz; /*!< DPI clock frequency in MHz */ + float lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */ + float expect_dpi_clock_freq_mhz; /*!< Expected DPI clock frequency in MHz */ + float real_dpi_clock_freq_mhz; /*!< Real DPI clock frequency in MHz */ } mipi_dsi_hal_context_t; /** @@ -39,7 +40,7 @@ typedef struct { */ typedef struct { int bus_id; /*!< MIPI DSI bus ID, index from 0 */ - uint32_t lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */ + float lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */ uint8_t num_data_lanes; /*!< Number of data lanes */ } mipi_dsi_hal_config_t; @@ -67,7 +68,7 @@ void mipi_dsi_hal_deinit(mipi_dsi_hal_context_t *hal); * @param phy_clk_src_freq_hz PHY clock source frequency in Hz * @param lane_bit_rate_mbps Lane bit rate in Mbps */ -void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, uint32_t lane_bit_rate_mbps); +void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, float lane_bit_rate_mbps); /** * @brief Write a value to a PHY register via internal bus (so-called test interface) @@ -175,7 +176,7 @@ void mipi_dsi_hal_host_dpi_set_vertical_timing(mipi_dsi_hal_context_t *hal, uint * @param expect_dpi_clk_mhz Expected DPI clock frequency in MHz * @return Divider value */ -uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, uint32_t clk_src_mhz, uint32_t expect_dpi_clk_mhz); +uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, float clk_src_mhz, float expect_dpi_clk_mhz); #ifdef __cplusplus diff --git a/components/esp_hal_lcd/mipi_dsi_hal.c b/components/esp_hal_lcd/mipi_dsi_hal.c index 73acc609cd..565cb80c64 100644 --- a/components/esp_hal_lcd/mipi_dsi_hal.c +++ b/components/esp_hal_lcd/mipi_dsi_hal.c @@ -1,11 +1,13 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include +#include #include +#include #include "hal/mipi_dsi_hal.h" #include "hal/mipi_dsi_ll.h" #include "hal/assert.h" @@ -40,28 +42,28 @@ void mipi_dsi_hal_deinit(mipi_dsi_hal_context_t *hal) hal->bridge = NULL; } -void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, uint32_t lane_bit_rate_mbps) +void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, float lane_bit_rate_mbps) { // Formula: f_vco = M/N * f_ref // where the M is Feedback Multiplication Ratio, N is Input Frequency Division Ratio - uint32_t ref_freq_mhz = phy_clk_src_freq_hz / 1000 / 1000; - uint32_t vco_freq_mhz = lane_bit_rate_mbps; + float ref_freq_mhz = (float)phy_clk_src_freq_hz / 1000.0f / 1000.0f; + float vco_freq_mhz = lane_bit_rate_mbps; uint8_t pll_N = 1; uint16_t pll_M = 0; // 5MHz <= f_ref/N <= 40MHz - uint8_t min_N = MAX(1, ref_freq_mhz / 40); - uint8_t max_N = ref_freq_mhz / 5; - uint16_t min_delta = UINT16_MAX; + uint8_t min_N = MAX(1, (uint8_t)(ref_freq_mhz / 40.0f)); + uint8_t max_N = (uint8_t)(ref_freq_mhz / 5.0f); + float min_delta = INFINITY; for (uint8_t n = min_N; n <= max_N; n++) { - uint16_t m = vco_freq_mhz * n / ref_freq_mhz; + uint16_t m = (uint16_t)(vco_freq_mhz * n / ref_freq_mhz); // M must be even number if ((m & 0x01) == 0) { - uint16_t delta = vco_freq_mhz - ref_freq_mhz * m / n; + float delta = fabsf(vco_freq_mhz - ref_freq_mhz * m / n); if (delta < min_delta) { min_delta = delta; pll_M = m; pll_N = n; - if (min_delta == 0) { + if (min_delta < 0.01f) { break; } } @@ -86,9 +88,9 @@ void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_cl mipi_dsi_hal_phy_write_register(hal, 0x18, ((pll_M - 1) & 0x1F)); mipi_dsi_hal_phy_write_register(hal, 0x18, 0x80 | (((pll_M - 1) >> 5) & 0x0F)); // update the real lane bit rate - hal->lane_bit_rate_mbps = ref_freq_mhz * pll_M / pll_N; - HAL_LOGD(TAG, "phy pll: ref=%" PRIu32 "Hz, lane_bit_rate=%" PRIu32 "Mbps, M=%" PRId16 ", N=%" PRId8 ", hsfreqrange=%" PRId8, - phy_clk_src_freq_hz, hal->lane_bit_rate_mbps, pll_M, pll_N, hs_freq_sel); + hal->lane_bit_rate_mbps = ref_freq_mhz * (float)pll_M / (float)pll_N; + HAL_LOGD(TAG, "phy pll: ref=%" PRIu32 "Hz, lane_bit_rate=%.2f Mbps, M=%" PRId16 ", N=%" PRId8 ", hsfreqrange=%" PRId8, + phy_clk_src_freq_hz, (double)hal->lane_bit_rate_mbps, pll_M, pll_N, hs_freq_sel); } void mipi_dsi_hal_phy_write_register(mipi_dsi_hal_context_t *hal, uint8_t reg_addr, uint8_t reg_val) @@ -118,7 +120,7 @@ void mipi_dsi_hal_host_gen_write_dcs_command(mipi_dsi_hal_context_t *hal, uint8_ uint32_t payload_size = command_bytes + param_size; // merge the command and some bytes of parameters into one 32-bit word - uint32_t temp = command & ((1 << (8 * command_bytes)) - 1); + uint32_t temp = command & ((1U << (8 * command_bytes)) - 1); uint16_t merged_size = MIN(4 - command_bytes, param_size); for (int i = 0; i < merged_size; i++) { temp |= payload[i] << (8 * (i + command_bytes)); @@ -133,15 +135,17 @@ void mipi_dsi_hal_host_gen_write_dcs_command(mipi_dsi_hal_context_t *hal, uint8_ payload += merged_size; uint32_t remain_size = param_size - merged_size; while (remain_size >= 4) { - temp = *(uint32_t *)payload; + // use memcpy to avoid unaligned memory access + memcpy(&temp, payload, sizeof(uint32_t)); while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host)); mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp); payload += 4; remain_size -= 4; } if (remain_size) { - temp = *(uint32_t *)payload; - temp &= (1 << (8 * remain_size)) - 1; + temp = 0; + // use memcpy to avoid unaligned memory access and buffer over-read + memcpy(&temp, payload, remain_size); while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host)); mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp); } @@ -178,15 +182,17 @@ void mipi_dsi_hal_host_gen_write_long_packet(mipi_dsi_hal_context_t *hal, uint8_ uint32_t remain_size = buffer_size; uint32_t temp = 0; while (remain_size >= 4) { - temp = *(uint32_t *)payload; + // use memcpy to avoid unaligned memory access + memcpy(&temp, payload, sizeof(uint32_t)); while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host)); mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp); payload += 4; remain_size -= 4; } if (remain_size) { - temp = *(uint32_t *)payload; - temp &= (1 << (8 * remain_size)) - 1; + temp = 0; + // use memcpy to avoid unaligned memory access and buffer over-read + memcpy(&temp, payload, remain_size); while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host)); mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp); } @@ -227,19 +233,30 @@ void mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_ void mipi_dsi_hal_host_gen_read_dcs_command(mipi_dsi_hal_context_t *hal, uint8_t vc, uint32_t command, uint32_t command_bytes, void *ret_param, uint16_t param_buf_size) { - uint16_t header_data = command & ((1 << (8 * command_bytes)) - 1); + uint16_t header_data = command & ((1U << (8 * command_bytes)) - 1); mipi_dsi_hal_host_gen_read_short_packet(hal, vc, MIPI_DSI_DT_DCS_READ_0, header_data, ret_param, param_buf_size); } void mipi_dsi_hal_host_dpi_set_horizontal_timing(mipi_dsi_hal_context_t *hal, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp) { - float dpi2lane_clk_ratio = (float)hal->lane_bit_rate_mbps / hal->dpi_clock_freq_mhz / 8; - mipi_dsi_host_ll_dpi_set_horizontal_timing(hal->host, - hsw * dpi2lane_clk_ratio, - hbp * dpi2lane_clk_ratio, - active_width * dpi2lane_clk_ratio, - hfp * dpi2lane_clk_ratio); - mipi_dsi_brg_ll_set_horizontal_timing(hal->bridge, hsw, hbp, active_width, hfp); + // set the horizontal timing based on user's expectation + uint32_t htotal = hsw + hbp + active_width + hfp; + float dpi2lane_clk_ratio = hal->lane_bit_rate_mbps / hal->expect_dpi_clock_freq_mhz / 8.0f; + uint32_t host_hsw = (uint32_t)roundf(hsw * dpi2lane_clk_ratio); + uint32_t host_hbp = (uint32_t)roundf(hbp * dpi2lane_clk_ratio); + uint32_t host_act = (uint32_t)roundf(active_width * dpi2lane_clk_ratio); + uint32_t host_hfp = (uint32_t)roundf(hfp * dpi2lane_clk_ratio); + uint32_t host_htotal = (uint32_t)roundf(htotal * dpi2lane_clk_ratio); + int compensation = (int)host_htotal - (int)(host_hsw + host_hbp + host_act + host_hfp); + mipi_dsi_host_ll_dpi_set_horizontal_timing(hal->host, host_hsw, host_hbp, host_act + compensation, host_hfp); + HAL_LOGD(TAG, "host video timing: hsw=%" PRIu32 ", hbp=%" PRIu32 ", act=%" PRIu32 ", hfp=%" PRIu32, + host_hsw, host_hbp, host_act + compensation, host_hfp); + + // compensate the video timing to achieve the same refresh rate as expected + compensation = (int)roundf(hal->real_dpi_clock_freq_mhz / hal->expect_dpi_clock_freq_mhz * htotal) - (int)htotal; + mipi_dsi_brg_ll_set_horizontal_timing(hal->bridge, hsw, hbp, active_width, hfp + compensation); + HAL_LOGD(TAG, "brg video timing: hsw=%" PRIu32 ", hbp=%" PRIu32 ", act=%" PRIu32 ", hfp=%" PRIu32, + hsw, hbp, active_width, hfp + compensation); } void mipi_dsi_hal_host_dpi_set_vertical_timing(mipi_dsi_hal_context_t *hal, uint32_t vsw, uint32_t vbp, uint32_t active_height, uint32_t vfp) @@ -248,9 +265,12 @@ void mipi_dsi_hal_host_dpi_set_vertical_timing(mipi_dsi_hal_context_t *hal, uint mipi_dsi_brg_ll_set_vertical_timing(hal->bridge, vsw, vbp, active_height, vfp); } -uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, uint32_t clk_src_mhz, uint32_t expect_dpi_clk_mhz) +uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, float clk_src_mhz, float expect_dpi_clk_mhz) { - uint32_t div = clk_src_mhz / expect_dpi_clk_mhz; - hal->dpi_clock_freq_mhz = clk_src_mhz / div; + uint32_t div = (uint32_t)roundf(clk_src_mhz / expect_dpi_clk_mhz); + hal->expect_dpi_clock_freq_mhz = expect_dpi_clk_mhz; + hal->real_dpi_clock_freq_mhz = clk_src_mhz / (float)div; + HAL_LOGD(TAG, "dpi clk: src=%.2f MHz, expect=%.2f MHz, div=%" PRIu32 ", real=%.2f MHz", + clk_src_mhz, expect_dpi_clk_mhz, div, hal->real_dpi_clock_freq_mhz); return div; } diff --git a/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c b/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c index c51046ccdb..cb98febf53 100644 --- a/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c +++ b/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c @@ -3,6 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ +#include #include "esp_lcd_mipi_dsi.h" #include "esp_clk_tree.h" #include "mipi_dsi_priv.h" @@ -19,7 +20,7 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc ESP_ERR_INVALID_ARG, TAG, "invalid number of data lanes %d", bus_config->num_data_lanes); ESP_RETURN_ON_FALSE(bus_config->lane_bit_rate_mbps >= MIPI_DSI_LL_MIN_PHY_MBPS && bus_config->lane_bit_rate_mbps <= MIPI_DSI_LL_MAX_PHY_MBPS, ESP_ERR_INVALID_ARG, TAG, - "invalid lane bit rate %"PRIu32, bus_config->lane_bit_rate_mbps); + "invalid lane bit rate %.2f", bus_config->lane_bit_rate_mbps); // we don't use an bus allocator here, because different DSI bus uses different PHY. // And each PHY has its own associated PINs, which is not changeable. @@ -114,9 +115,9 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc mipi_dsi_host_ll_enable_tx_eotp(hal->host, true, false); // Set the divider to get the Time Out clock, clock source is the high-speed byte clock - mipi_dsi_host_ll_set_timeout_clock_division(hal->host, bus_config->lane_bit_rate_mbps / 8 / MIPI_DSI_DEFAULT_TIMEOUT_CLOCK_FREQ_MHZ); + mipi_dsi_host_ll_set_timeout_clock_division(hal->host, (uint32_t)roundf(bus_config->lane_bit_rate_mbps / 8.0f / MIPI_DSI_DEFAULT_TIMEOUT_CLOCK_FREQ_MHZ)); // Set the divider to get the TX Escape clock, clock source is the high-speed byte clock - mipi_dsi_host_ll_set_escape_clock_division(hal->host, bus_config->lane_bit_rate_mbps / 8 / MIPI_DSI_DEFAULT_ESCAPE_CLOCK_FREQ_MHZ); + mipi_dsi_host_ll_set_escape_clock_division(hal->host, (uint32_t)roundf(bus_config->lane_bit_rate_mbps / 8.0f / MIPI_DSI_DEFAULT_ESCAPE_CLOCK_FREQ_MHZ)); // set the timeout intervals to zero, means to disable the timeout mechanism mipi_dsi_host_ll_set_timeout_count(hal->host, 0, 0, 0, 0, 0, 0, 0); // DSI host will wait indefinitely for a read response from the DSI device diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index d3983d9dad..9351058490 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c @@ -186,7 +186,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_ esp_lcd_dpi_panel_t *dpi_panel = NULL; ESP_RETURN_ON_FALSE(bus && panel_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(panel_config->virtual_channel < 4, ESP_ERR_INVALID_ARG, TAG, "invalid virtual channel %d", panel_config->virtual_channel); - ESP_RETURN_ON_FALSE(panel_config->dpi_clock_freq_mhz, ESP_ERR_INVALID_ARG, TAG, "invalid DPI clock frequency %"PRIu32, panel_config->dpi_clock_freq_mhz); + ESP_RETURN_ON_FALSE(panel_config->dpi_clock_freq_mhz > 0, ESP_ERR_INVALID_ARG, TAG, "invalid DPI clock frequency %.2f", panel_config->dpi_clock_freq_mhz); size_t num_fbs = panel_config->num_fbs; // if the user doesn't specify the number of frame buffers, then fallback to use one frame buffer @@ -250,7 +250,8 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_ ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(dpi_clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &dpi_clk_src_freq_hz), err, TAG, "get clock source frequency failed"); // divide the source clock to get the final DPI clock - uint32_t dpi_div = mipi_dsi_hal_host_dpi_calculate_divider(hal, dpi_clk_src_freq_hz / 1000 / 1000, panel_config->dpi_clock_freq_mhz); + float dpi_clk_src_freq_mhz = (float)dpi_clk_src_freq_hz / 1000.0f / 1000.0f; + uint32_t dpi_div = mipi_dsi_hal_host_dpi_calculate_divider(hal, dpi_clk_src_freq_mhz, panel_config->dpi_clock_freq_mhz); ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)dpi_clk_src, true), err, TAG, "clock source enable failed"); // set the clock source, set the divider, and enable the dpi clock PERIPH_RCC_ATOMIC() { diff --git a/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h b/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h index 57e1981aa0..91ad8d41f3 100644 --- a/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h +++ b/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h @@ -24,7 +24,7 @@ typedef struct { int bus_id; /*!< Select which DSI controller, index from 0 */ uint8_t num_data_lanes; /*!< Number of data lanes, if set to 0, the driver will fallback to use maximum number of lanes */ mipi_dsi_phy_pllref_clock_source_t phy_clk_src; /*!< The clock source for the PHY PLL */ - uint32_t lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */ + float lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */ } esp_lcd_dsi_bus_config_t; /** @@ -83,7 +83,7 @@ esp_err_t esp_lcd_new_panel_io_dbi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_d typedef struct { uint8_t virtual_channel; /*!< Virtual channel ID, index from 0 */ mipi_dsi_dpi_clock_source_t dpi_clk_src; /*!< MIPI DSI DPI clock source */ - uint32_t dpi_clock_freq_mhz; /*!< DPI clock frequency in MHz */ + float dpi_clock_freq_mhz; /*!< Pixel clock frequency in MHz */ lcd_color_format_t in_color_format; /*!< Format of the input data (color space and pixel format), which is the format stored in the frame buffer */ lcd_color_format_t out_color_format; /*!< Format of the output data (color space and pixel format),