技术专栏
VO自定义时序计算以及MIPI屏幕调试
前言
海思平台VO自定义时序说明,下面以
海鸥派SS928为例自定义MIPI屏时序。MIPI屏幕型号:M101WXB40-01A(MIPI 800x1280@60fps)。
文章中MIPI屏幕时序计算可通过海思SDK提供的
ReleaseDoc\zh\02.only for reference\software\RGB_MIPI屏幕时钟时序计算器.xlsx计算。
一、定义时序信息
参考文档MPP 媒体处理软件 V5.0 开发参考.pdf, ot_vo_sync_info。
根据上面的公式计算出像素时钟:(1280+24+20)x(800+40+40)x 60 = 69907200Hz ≈ 69908KHz。
二、用户接口时序信息
参考文档MPP 媒体处理软件 V5.0 开发参考.pdf, ot_vo_user_sync_info。
使用下面的python脚本计算:
import itertools
#pixel_clk = (hbb+hact+hfb)*(vbb+vact+vfb) * frame_rate /1000000
pixel_clk = 70
FREF = 24 # FREF PLL 输入参考时钟 固定输入24MHz
# 设定范围
g_fb_div = range(0, 4095) # fb_div PLL 整数倍频系数,数值范围:[0,0xfff]。
g_frac = range(0, 16777215) # frac PLL 小数分频系数,数值范围:[0,0xffffff]。
g_ref_div = [1] # ref_div PLL 参考时钟分频系数,SS928V100/SS626V100只能为1。
g_post_div1 = range(1, 7) # post_div1 PLL 第一级输出分频系数,数值范围:(0,0x7]。
g_post_div2 = range(1, 7) # post_div2 PLL 第二级输出分频系数,数值范围:(0,0x7]。
g_pre_div = [1] # pre_div 设备前置分频,数值范围[1, 32],只有在SS528V100/SS625V100/SS524V100/SS522V100采用HDMI(含HDMI同源)接口输出时设备前置分频属性需要进行配置,其他接口输出时前置分频属性配置为1(不分频)
g_dev_div = [1] # dev_div 设备的时钟分频比,数值范围:[1, 4], SS928V100 HDMI/VGA/BT1120/MIPI 固定为1
#要求
#FOUTVCO = FREF x ( fb_div + frac/2^24) / ref_div #PLL 工作频率,要求大于等于800MHz,且小于等于3.2GHz
#FOUTPOSTDIV = FOUTVCO / (post_div1 xpost_div2) #post_div1 >= post_div2 [16.326531,594]MHz。
#pixel_clk = FOUTPOSTDIV / (pre_div × dev_div)
def find_pll_parameters():
"""查找PLL参数配置"""
results = []
# 遍历所有可能的组合
for ref_div, post_div1, post_div2, pre_div, dev_div in itertools.product(g_ref_div, g_post_div1, g_post_div2, g_pre_div, g_dev_div):
# 检查 post_div1 >= post_div2
if post_div1 < post_div2:
continue
# 计算总的分频比
total_div = ref_div * post_div1 * post_div2 * pre_div * dev_div
# 计算需要的FVCO
required_fvco = pixel_clk * total_div
# 检查FVCO范围 [800, 3200] MHz
if not (800 <= required_fvco <= 3200):
continue
# 计算FOUTPOSTDIV并检查范围 [16.326531, 594] MHz
required_foutpostdiv = pixel_clk * pre_div * dev_div
if not (16.326531 <= required_foutpostdiv <= 594):
continue
# 计算分频比
div_ratio = required_fvco / FREF
g_fb_div = int(div_ratio)
g_frac = int((div_ratio - g_fb_div) * (2 ** 24))
# 检查参数范围
if 1 <= g_fb_div < 4095 and 0 <= g_frac < 16777215:
# 验证实际频率
actual_fvco = FREF * (g_fb_div + g_frac / (2 ** 24))
actual_foutpostdiv = actual_fvco / (post_div1 * post_div2)
actual_pixel_clk = actual_foutpostdiv / (pre_div * dev_div)
# 检查误差
error = abs(actual_pixel_clk - pixel_clk)
if error < 0.001: # 允许1kHz误差
results.append({
'fb_div': g_fb_div,
'frac': g_frac,
'ref_div': ref_div,
'post_div1': post_div1,
'post_div2': post_div2,
'pre_div': pre_div,
'dev_div': dev_div,
'total_div': total_div,
'fvco': actual_fvco,
'foutpostdiv': actual_foutpostdiv,
'pixel_clk': actual_pixel_clk,
'error': actual_pixel_clk - pixel_clk,
'error_ppm': (actual_pixel_clk - pixel_clk) / pixel_clk * 1e6
})
return results
# 更高效的搜索版本,按总分频比排序
def find_pll_parameters_sorted():
"""查找并排序PLL参数配置"""
results = find_pll_parameters()
# 按总分频比排序(较小的分频比通常更好)
results.sort(key=lambda x: x['total_div'])
return results
def select_best_solution(solutions):
"""选择最佳方案"""
if not solutions:
return None
# 评分函数
def score_solution(sol):
score = 0
# 1. 分频比最小化(权重最高)
total_div = sol['total_div']
score -= total_div * 1000 # 分频比越小越好
# 2. 整数分频优先(frac=0)
if sol['frac'] == 0:
score += 5000 # 整数分频大幅加分
# 3. FVCO接近理想值2000MHz
fvco_ideal = 2000
fvco_diff = abs(sol['fvco'] - fvco_ideal)
score -= fvco_diff * 10 # 越接近理想值越好
# 4. 分频系数简化
# post_div1和post_div2接近
div_ratio = sol['post_div1'] / sol['post_div2']
if div_ratio == 1: # 相等最好
score += 100
elif div_ratio == 2 or div_ratio == 0.5: # 2倍关系较好
score += 50
# 5. 使用标准分频值加分
standard_divs = [1, 2, 4, 8, 16]
if sol['ref_div'] in standard_divs:
score += 20
if sol['post_div1'] in standard_divs:
score += 20
if sol['post_div2'] in standard_divs:
score += 20
return score
# 按评分排序
scored_solutions = [(sol, score_solution(sol)) for sol in solutions]
scored_solutions.sort(key=lambda x: x[1], reverse=True)
return scored_solutions[0][0] # 返回评分最高的方案
def analyze_solutions(solutions):
"""分析并推荐最佳方案"""
if not solutions:
print("未找到合适的解决方案")
return
# 方案1:总分频比最小
min_div_solution = min(solutions, key=lambda x: x['total_div'])
# 方案2:整数分频优先
integer_solutions = [sol for sol in solutions if sol['frac'] == 0]
if integer_solutions:
best_integer = min(integer_solutions, key=lambda x: x['total_div'])
else:
best_integer = None
# 方案3:综合评分最佳
best_overall = select_best_solution(solutions)
# 显示各候选方案
print("\n\n=== 候选方案分析 ===")
print(f"1. 最小分频比方案 (total_div={min_div_solution['total_div']}):")
print_solution_details(min_div_solution)
if best_integer:
print(f"\n2. 整数分频最佳方案 (frac=0):")
print_solution_details(best_integer)
else:
print("\n2. 无整数分频方案")
print(f"\n3. 综合最佳方案:")
print_solution_details(best_overall)
def print_solution_details(sol):
"""打印方案详情"""
print(f" fb_div = {sol['fb_div']} (0x{sol['fb_div']:03x})")
print(f" frac = {sol['frac']} (0x{sol['frac']:06x})")
print(f" ref_div = {sol['ref_div']}")
print(f" post_div1 = {sol['post_div1']}")
print(f" post_div2 = {sol['post_div2']}")
print(f" pre_div = {sol['pre_div']}")
print(f" dev_div = {sol['dev_div']}")
print(f" 总分频比 = {sol['total_div']}")
print(f" FVCO = {sol['fvco']:.6f} MHz")
print(f" FOUTPOSTDIV = {sol['foutpostdiv']:.6f} MHz")
print(f" 像素时钟 = {sol['pixel_clk']:.6f} MHz")
print(f" 误差 = {sol['error']:.6f} MHz")
print(f" 相对误差 = {sol['error_ppm']:.2f} ppm")
def main():
# 执行搜索
print("搜索PLL参数配置...")
print(f"目标像素时钟: {pixel_clk} MHz")
print(f"计算公式: 像素时钟 = FOUTPOSTDIV / (pre_div × dev_div)")
print()
solutions = find_pll_parameters_sorted()
print(f"找到 {len(solutions)} 个解决方案:")
for i, sol in enumerate(solutions):
print(f"\n方案 {i + 1}:")
print_solution_details(sol)
# 验证计算
calculated_foutpostdiv = sol['fvco'] / (sol['post_div1'] * sol['post_div2'])
calculated_pixel_clk = calculated_foutpostdiv / (sol['pre_div'] * sol['dev_div'])
print(f" 验证计算 = {calculated_pixel_clk:.6f} MHz")
analyze_solutions(solutions)
if __name__ == '__main__':
main()
选择其中一个结果:
三、MIPI屏幕代码配置
1. sample_vo_mipi_tx_cfg 配置
/* VO: USER 800x1280_60, TX: USER 800x1280 */
static const sample_vo_mipi_tx_cfg g_vo_tx_cfg_800x1280_user = {
.vo_config = {
.vo_dev = SAMPLE_VO_DEV_UHD,
.vo_intf_type = OT_VO_INTF_MIPI,
.intf_sync = OT_VO_OUT_USER,
.bg_color = COLOR_RGB_BLACK,
.pix_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420,
.disp_rect = {0, 0, 800, 1280},
.image_size = {800, 1280},
.vo_part_mode = OT_VO_PARTITION_MODE_SINGLE,
.dis_buf_len = 3, /* 3: def buf len for single */
.dst_dynamic_range = OT_DYNAMIC_RANGE_SDR8,
.vo_mode = VO_MODE_1MUX,
.compress_mode = OT_COMPRESS_MODE_NONE,
.sync_info = {0, 1, 1, 1280, 24, 20, 800, 40, 40, 1, 1, 1, 1, 20, 4, 0, 0, 0},
.user_sync = {
.user_sync_attr = {
.clk_src = OT_VO_CLK_SRC_PLL,
.vo_pll = { /* if hdmi, set it by pixel clk and div mode */
.fb_div = 105, /* 105 fb div */
.frac = 0,
.ref_div = 1, /* 1 ref div */
.post_div1 = 6, /* 6 post div1 */
.post_div2 = 6, /* 6 post div2 */
},
},
.pre_div = 1, /* if hdmi, set it by pixel clk */
.dev_div = 1, /* if rgb, set it by serial mode */
.clk_reverse_en = TD_FALSE,
},
.dev_frame_rate = 60,
},
.tx_config = {
/* for combo dev config */
.intf_sync = OT_MIPI_TX_OUT_USER,
/* for screen cmd */
.cmd_count = CMD_COUNT_800X1280,
.cmd_info = g_cmd_info_800x1280,
/* for user sync */
.combo_dev_cfg = {
.devno = 0,
.lane_id = {0, 1, 2, 3},
.out_mode = OUT_MODE_DSI_VIDEO,
.out_format = OUT_FORMAT_RGB_24BIT,
.video_mode = BURST_MODE,
.sync_info = {
.hsa_pixels = 20, /* 20 pixel */
.hbp_pixels = 20, /* 20 pixel */
.hact_pixels = 800, /* 800 pixel */
.hfp_pixels = 40, /* 40 pixel */
.vsa_lines = 4, /* 4 line */
.vbp_lines = 20, /* 20 line */
.vact_lines = 1280, /* 1280 line */
.vfp_lines = 20, /* 20 line */
},
.phy_data_rate = 999, /* 999 Mbps */
.pixel_clk = 70000, /* 70000 KHz */
},
},
};
其中g_cmd_info_800x1280为mipi屏幕初始化控制,注意在最后加上下面两行:
{{0, 0, 0, 0x23, 0x0011, NULL}, USLEEP_120000}, #退出睡眠模式
{{0, 0, 0, 0x23, 0x0029, NULL}, USLEEP_60000} #开启显示
2. vo配置mipi屏出图
参考例程sample_vdec.c中的sample_start_vo, 新增start_vo_mipi_tx。
static td_s32 sample_start_vo(sample_vo_cfg *vo_config, td_u32 vpss_grp_num)
{
td_s32 ret;
vo_config->vo_dev = SAMPLE_VO_DEV_UHD;
vo_config->vo_intf_type = g_vdec_display_cfg.intf_type;
vo_config->intf_sync = g_vdec_display_cfg.intf_sync;
vo_config->pic_size = g_vdec_display_cfg.pic_size;
vo_config->bg_color = COLOR_RGB_BLUE;
vo_config->dis_buf_len = 3; /* 3:buf length */
vo_config->dst_dynamic_range = OT_DYNAMIC_RANGE_SDR8;
vo_config->vo_mode = VO_MODE_1MUX;
vo_config->pix_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
vo_config->disp_rect.x = 0;
vo_config->disp_rect.y = 0;
vo_config->disp_rect.width = 1920;
vo_config->disp_rect.height = 1080;
vo_config->image_size.width = 1920;
vo_config->image_size.height = 1080;
vo_config->vo_part_mode = OT_VO_PARTITION_MODE_SINGLE;
vo_config->compress_mode = OT_COMPRESS_MODE_NONE;
ret = sample_comm_vo_start_vo(vo_config);
if (ret != TD_SUCCESS) {
sample_print("start VO fail for %#x!\n", ret);
sample_comm_vo_stop_vo(vo_config);
return ret;
}
ret = sample_vpss_bind_vo(*vo_config, vpss_grp_num);
if (ret != TD_SUCCESS) {
sample_vpss_unbind_vo(vpss_grp_num, *vo_config);
sample_comm_vo_stop_vo(vo_config);
}
return ret;
}
static td_s32 start_vo_mipi_tx(const sample_vo_mipi_tx_cfg *vo_tx_cfg, td_u32 vpss_grp_num)
{
td_s32 ret;
const sample_vo_cfg *vo_config = &vo_tx_cfg->vo_config;
const sample_mipi_tx_config *tx_config = &vo_tx_cfg->tx_config;
ret = sample_comm_vo_start_vo(vo_config);
if (ret != TD_SUCCESS) {
sample_print("start vo failed with 0x%x!\n", ret);
return ret;
}
printf("start vo dhd%d.\n", vo_config->vo_dev);
ret = sample_vpss_bind_vo(*vo_config, vpss_grp_num);
if (ret != TD_SUCCESS) {
sample_vpss_unbind_vo(vpss_grp_num, *vo_config);
sample_comm_vo_stop_vo(vo_config);
}
if ((vo_config->vo_intf_type & OT_VO_INTF_MIPI) ||
(vo_config->vo_intf_type & OT_VO_INTF_MIPI_SLAVE)) {
ret = sample_comm_start_mipi_tx(tx_config);
if (ret != TD_SUCCESS) {
sample_print("start mipi tx failed with 0x%x!\n", ret);
return ret;
}
}
return TD_SUCCESS;
}
3. 板端测试
测试前注意检查mipi_tx管脚复用以及mipi屏幕背光和复位控制。
管脚复用一般在sysconfig.ko驱动代码里已经写好,load脚本加载时传递参数即可。
VO_INTF=mipi_tx
# sys config
insmod $ko_path/sys_config.ko sensors=sns0=$SNS_TYPE0,sns1=$SNS_TYPE1,sns2=$SNS_TYPE2,sns3=$SNS_TYPE3 vo_intf=$VO_INTF
可参考ReleaseDoc\zh\01.software\board\MPP\SYS_CONFIG 配置指南.pdf。
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包
2
收藏
评论
打赏
- 分享
- 举报
评论
0个
手气红包
暂无数据相关专栏
-
浏览量:3348次2020-05-06 15:52:54
-
浏览量:2452次2020-08-03 12:02:37
-
浏览量:4161次2020-09-20 21:19:24
-
浏览量:15653次2020-11-12 21:55:56
-
浏览量:6577次2020-09-23 23:07:37
-
浏览量:2129次2020-08-03 12:01:28
-
浏览量:5136次2021-06-28 15:59:34
-
浏览量:35913次2021-03-03 17:25:19
-
浏览量:12325次2020-12-19 13:31:48
-
浏览量:15946次2020-12-18 17:44:33
-
浏览量:20742次2020-08-25 11:16:34
-
浏览量:2476次2020-08-14 18:33:44
-
浏览量:4767次2021-09-13 13:47:51
-
浏览量:2297次2020-08-03 13:33:48
-
浏览量:14956次2021-08-13 16:08:47
-
浏览量:3040次2020-08-14 18:40:18
-
浏览量:5136次2021-09-08 16:03:36
-
浏览量:1623次2023-09-07 09:50:10
-
浏览量:1712次2023-12-19 17:38:07
置顶时间设置
结束时间
删除原因
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
打赏作者
Sunshine
您的支持将鼓励我继续创作!
打赏金额:
¥1
¥5
¥10
¥50
¥100
支付方式:
微信支付
举报反馈
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明
审核成功
发布时间设置
发布时间:
请选择发布时间设置
是否关联周任务-专栏模块
审核失败
失败原因
请选择失败原因
备注
请输入备注

微信扫码分享
QQ好友