树莓派MIPI/DSI接口LCD屏的调试

树莓派MIPI/DSI接口LCD屏的调试

1. 问题:

树莓派CM4模块提供了两个HDMI, 两个DSI接口, 方便用户外接显示屏。 如果将树莓派当做电脑使用,可能使用HDMI接口,会比较方面和省心。但是在嵌入式设备上,我们希望能够使用DSI接口外接LCD屏幕。

如果你Google过raspberry DSI, 你就会知道针对DSI接口的调试,工程师们碰到的问题还是蛮多的。 这里我记录我自己的调试过程。

2. 硬件

本人使用的硬件有:

2.1 树莓派CM4模块

它的链接:

https://www.raspberrypi.com/products/compute-module-4/?variant=raspberry-pi-cm4001000

2.2 树莓派IO底板

它的链接:

https://www.raspberrypi.com/products/compute-module-4-io-board/

2.3 LCD屏

本人选择了一款3.5 英寸, 640 x 480像素,带触摸屏的LCD。 它的价格还比较合理。

LCD的链接:

https://newhavendisplay.com/3-5-inch-ips-tft-mipi-interface-capacitive-touchscreen/

2.4 适配板

适配板是自己画的。

电路图非常简单,就不晒了。这里直接上PCB图

因为适配板非常简单, 距离很短,不考虑阻抗匹配应该也能工作。

但是为了性能好些,最好做一下匹配:单端50欧,差分100欧。

(如果对这板子感兴趣的话,可以通过以下链接购买

MIPI/DSI adaptor board for Raspberry Pi from Industrial electronics on Tindie)

2.4 硬件链接图

最后系统的连接如下:

因为LCD的背光需要16.8~20.4V的电压,考虑到我们没有使用PCIe插槽,所以直接给IO板上电18V,然后通过J20接口给LCD背光供电。 上图中的黑白两根线为18v。

3. 软件

Linux内核版本:

Linux raspberrypidylan 6.6.60-v8+

源代码地址: GitHub - raspberrypi/linux: Kernel source tree for Raspberry Pi-provided kernel builds. Issues unrelated to the linux kernel should be posted on the community forum at https://forums.raspberrypi.com/

4. 一些其他资源

LCD驱动芯片:FL7703NI

触摸屏芯片:FT5426

LCDx芯片驱动相关: https://support.newhavendisplay.com/hc/en-us/community/posts/24147208520727-NHD-3-5-640480EF-MSXP-FL7703NI-Linux-driver

LCD屏幕的一些参数:

https://support.newhavendisplay.com/hc/en-us/community/posts/23239667440151-Porch-values-of-NHD-3-5-640480EF-MSXP-CTP

5. 调试过程

5.1 屏幕的驱动

因为当前Linux源码不直接支持FL7703芯片,所以需要自己移植。

通过直接咨询newhavendisplay,他们告知该款芯片和Sitronix ST7703 非常类似。

所以FL7703的驱动可以在Sitronix ST7703 的基础上修改。

ST7703驱动源码的位置:

linux/drivers/gpu/drm/panel/panel-sitronix-st7703.c at master · torvalds/linux · GitHub

5.2 添加compatible和data数据 

static const struct of_device_id st7703_of_match[] = {

      { .compatible = "anbernic,rg353v-panel-v2", .data = &rg353v2_desc },

      { .compatible = "rocktech,jh057n00900", .data = &jh057n00900_panel_desc },

      { .compatible = "xingbangda,xbd599", .data = &xbd599_desc },

      { .compatible = "forcelead,fl7703", .data = &NHD_35_MSXP_desc },

      { /* sentinel */ }

};

compatible的字符串内容后续要和dts对应上,这里是“forcelead,fl7703”

data数据是一个结构体NHD_35_MSXP_desc,用于描述屏幕的一些设置,下节定义。

5.3 添加屏幕的描述结构体

static const struct st7703_panel_desc NHD_35_MSXP_desc= {
        .mode = &NHD_35_MSXP_mode,
        .lanes = 4,
        .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
                     MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_LPM,  
        .format = MIPI_DSI_FMT_RGB888,
        .init_sequence = NHD_35_MSXP_init_sequence,
}; 

NHD_35_MSXP_mode 也是一个结构体,用于定义LCD的屏幕同步信息以及时钟等等,后续5.4节添加。   

这里lanes设置为4通道。开始调试的时候, lanes可以设置为1,即单通道, 方便测试和调式。

flag要包含MIPI_DSI_MODE_LPM

NHD_35_MSXP_init_sequence()即是屏幕初始化的函数名。

5.4 添加drm_display_mode

drm_display_mode 用于设置屏幕的一个行同步,场同步的信息,基本上都是来自于产家,见上面第四节的链接。注意时钟的单位是kHz

static const struct drm_display_mode NHD_35_MSXP_mode = {
    .hdisplay       = 640,
    .hsync_start    = 640 + 120,
    .hsync_end      = 640 + 120 + 120,
    .htotal         = 640 + 120 + 120 + 120,
    .vdisplay       = 480,
    .vsync_start    = 480 + 25,
    .vsync_end      = 480 + 25 + 5,
    .vtotal         = 480 + 25 + 5 + 12, 
    .clock      = 31200,
    .flags      = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,  
    .width_mm       = 56,
    .height_mm      = 78,
};

5.5 添加初始化代码NHD_35_MSXP_init_sequence()

NHD_35_MSXP_init_sequence() 主要是做一些寄存器的初始化工作,代码如下:

static int NHD_35_MSXP_init_sequence(struct st7703 *ctx)
{   
	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);  
	msleep(50);
	fl7703ni_read_id(ctx);
	/*
	* Init sequence was supplied by the panel vendor.
	*/
	dev_err(ctx->dev, "NHD_35_MSXP_init_sequence dl!! 5\n");
	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xF1, 0x12, 0x87);  //0xB9  //last is 0x87 from NH
	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP, 0x78, 0x14, 0x70); //0xB2
	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF, 0x10, 0x10, 0x28,  //0xB3
			      0x28, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80);   //0xB4

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP, 0x0A, 0x0A);  //0xB5

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM, 0x70, 0x70); //0xB6

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT, 0x26);//0xB8	      

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI, 0x33, 0x81, 0x05,  //0xBA  0x30 1 lane, 31 2 lanes 
		              0xF9, 0x0E, 0x0E, 0x20, 0x00, 0x00, 0x00, 0x00,
		              0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x91, 0x0A,
		              0x00, 0x00, 0x01, 0x4F, 0x01, 0x00, 0x00, 0x37);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x47);   //0xBC

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00);  //0xBF		                     

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50, //0xC0
		              0x00, 0x00, 0x12, 0x73, 0x00);		              

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER, 0x54, 0x00, 0x32, //0xC1
		              0x32, 0x77, 0xF4, 0x77, 0x77, 0xCC, 0xCC, 0xFF,
		              0xFF, 0x11, 0x11,0x00,0x00,0x31);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETIO, 0x10, 0x00, 0x0A, 0x00, //0xC7
		              0x00, 0x00,0x00,0x00, 0xED, 0xC7, 0x00, 0xA5);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETCABC, 0x10, 0x40, 0x1e, //0xC8
		              0x03);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B);  //0xCC 

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA, 0x00, 0x05, 0x09, //0xE0
		              0x29, 0x3C, 0x3F, 0x3B, 0x37, 0x05, 0x0A, 0x0C,
		              0x10, 0x13, 0x10, 0x13, 0x12, 0x1A, 0x00, 0x05,
		              0x09, 0x29, 0x3C, 0x3f, 0x3B, 0x37, 0x05, 0x0A,
		              0x0C, 0x10, 0x13, 0x10, 0x13, 0x12, 0x1A);

	mipi_dsi_dcs_write_seq(dsi, 0xE1, 0x11, 0x11, 0x91,0x00, 0x00, 0x00, 0x00 );//0xE1 dylan, ONLY FN7703

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ, 0x07, 0x07, 0x0B, 0x0B, //0xE3
		              0x03, 0x0B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x04,
		              0xC0, 0x10);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1, 0x01, 0x00, 0x0E, 0x00,//0xE9
		              0x00, 0xB0, 0xB1, 0x11, 0x31, 0x23, 0x28, 0x10,
		              0xB0, 0xB1, 0x27, 0x08, 0x00, 0x04, 0x02, 0x00,
		              0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00,
		              0x88, 0x88, 0xBA, 0x60, 0x24, 0x08, 0x88, 0x88,
		              0x88, 0x88, 0x88, 0x88, 0x88, 0xBA, 0x71, 0x35,
		              0x18, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00,
		              0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		              0x00, 0x00, 0x00);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2, 0x97, 0x0A, 0x82, 0x02,
		              0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		              0x81, 0x88, 0xBA, 0x17, 0x53, 0x88, 0x88, 0x88,
		              0x88, 0x88, 0x88, 0x80, 0x88, 0xBA, 0x06, 0x42,
		              0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00,
		              0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
		              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		              0x00);

	mipi_dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_EF, 0xFF, 0xFF, 0x01);

	msleep(50);

	dev_err(ctx->dev, "NHD_35_MSXP_init_sequence Done\n");

	return 0;

}

需要注意的是  对寄存器ST7703_CMD_SETMIPI (地址0xBA)设置的lanes必须和5.3节的一致。

至此,驱动代码添加完毕。

https://blog.csdn.net/dylanZheng/article/details/144114763

但是本人为了调试的方便, 添加了一个读ID的函数,以此来判定DSI总线开始时是否是工作的。

5.6 读ID函数fl7703ni_read_id()

static int fl7703ni_read_id(struct st7703 *ctx)
{
	u8 id1, id2, id3;
	int ret;
	 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);

	ret = mipi_dsi_dcs_read(dsi, 0xDA, &id1, 1);
	if (ret < 0) {
		dev_err(ctx->dev, "could not read ID1  11\n");
		return ret;
	}

	ret = mipi_dsi_dcs_read(dsi, 0xDB, &id2, 1);
	if (ret < 0) {
		dev_err(ctx->dev,  "could not read ID2  22\n");
		return ret;
	}

	ret = mipi_dsi_dcs_read(dsi, 0xDC, &id3, 1);
	if (ret < 0) {
		dev_err(ctx->dev, "could not read ID3\n");
		return ret;
	}

	dev_info(ctx->dev,
		     "manufacturer: %02x version: %02x driver: %02x\n",
		     id1, id2, id3);
	return 0;
}

该函数在初始化代码函数NHD_35_MSXP_init_sequence()中被调用。

如果设备工作正常,能够读取到ID是: 38, 21和1f

6. 设备树overlay

仿照类似设备,编写如下overlay dts文件

/*

 * vc4-kms-dsi-fl7703-nhd35-overlay.dts  

 */


/dts-v1/;

/plugin/;


#include <dt-bindings/gpio/gpio.h>

/ {

        compatible = "brcm,bcm2835";

        fragment@0 {

                target = <&dsi1>;

                __overlay__  {

                        #address-cells = <1>;

                        #size-cells = <0>;

                        status = "okay";


                        port {

                                dsi_out: endpoint {

                                        remote-endpoint = <&panel_in>;

                                };
                        };

                        dsi_panel: dsi_panel@0 {                                                        

                                compatible = "forcelead,fl7703";

                                status        = "okay";

                                reg = <0>;                                

                                reset-gpios = <&gpio 21 1>; /* GPIO PIN 21, active Low */

                                //backlight = <&display_mcu>;

                                port {

                                        panel_in: endpoint {

                                                remote-endpoint = <&dsi_out>;

                                        };

                                };

                        };

                };

        };

};

上面的dts文件中compatible需要和5.2节的一致,即都是“forcelead,fl7703”, 否则linux会找不到对应的驱动。

reset-gpios 根据自己的需要修改,本人使用的是GPIO21。

7. config文件的修改

在config.txt文件中添加

dtoverlay=vc4-kms-dsi-fl7703-nhd35

当然同时要使能kms

dtoverlay=vc4-kms-v3d

8. 测试

测试结果直接上图

附上正常工作的一些log信息:

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值