Mipi屏幕的驱动
mipi屏幕驱动步骤:
电路设计、确定时序、上电验证
一、电路设计
原理图:这里主要是R4电阻的确定,根据MP3302数据手册和屏幕的数据手册得到屏幕背光led的额定电流,用MP3302DJ这颗IC做恒流驱动。
PCB:唯一注意的地方就是mipi需要做100Ω的差分等长布线,预留测时序的接口P1。
二、时序确定
mipi接口的屏幕在初始化时需要根据厂家给的时序进行改写到设备树中,具体改写过程不做赘述,参考链接:
Mipi屏幕驱动1
Mipi屏幕驱动2
通常厂家给的格式如下:(伪代码)
SSD_SEND(0x01,0xE0,0xAB,0xBA);
SSD_SEND(0x01,0xE1,0xBA,0xAB);
SSD_SEND(0x01,0xB1,0x10,0x01,0x47,0xFF);
SSD_SEND(0x01,0xB2,0x0C,0x14,0x04,0x50,0x50,0x14);
SSD_SEND(0x01,0xB3,0x56,0x53,0x00);
SSD_SEND(0x01,0xB4,0x33,0x30,0x04);
SSD_SEND(0x01,0xB6,0xB0,0x00,0x00,0x10,0x00,0x10,0x00);
SSD_SEND(0x01,0xB8,0x05,0x12,0x29,0x49,0x48);
SSD_SEND(0x01,0xB9,0x7C,0x65,0x55,0x49,0x46,0x36,0x3B,0x24,0x3D,0x3C,0x3D,0x5C,0x4C,0x55,0x47,0x46,0x39,0x26,0x06,0x7C,0x65,0x55,0x49,0x46,0x36,0x3B,0x24,0x3D,0x3C,0x3D,0x5C,0x4C,0x55,0x47,0x46,0x39,0x26,0x06);
SSD_SEND(0x01,0xC0,0xFF,0x87,0x12,0x34,0x44,0x44,0x44,0x44,0x98,0x04,0x98,0x04,0x0F,0x00,0x00,0xC1);
SSD_SEND(0x01,0xC1,0x54,0x94,0x02,0x85,0x9F,0x00,0x7F,0x00,0x54,0x00);
SSD_SEND(0x01,0xC2,0x17,0x09,0x08,0x89,0x08,0x11,0x22,0x20,0x44,0xFF,0x18,0x00);
SSD_SEND(0x01,0xC3,0x86,0x46,0x05,0x05,0x1C,0x1C,0x1D,0x1D,0x02,0x1F,0x1F,0x1E,0x1E,0x0F,0x0F,0x0D,0x0D,0x13,0x13,0x11,0x11,0x00);
SSD_SEND(0x01,0xC4,0x07,0x07,0x04,0x04,0x1C,0x1C,0x1D,0x1D,0x02,0x1F,0x1F,0x1E,0x1E,0x0E,0x0E,0x0C,0x0C,0x12,0x12,0x10,0x10,0x00);
SSD_SEND(0x01,0xC6,0x2A,0x2A);
SSD_SEND(0x01,0xC8,0x21,0x00,0x31,0x42,0x34,0x16);
SSD_SEND(0x01,0xCA,0xCB,0x43);
SSD_SEND(0x01,0xCD,0x0E,0x4B,0x4B,0x20,0x19,0x6B,0x06,0xB3);
SSD_SEND(0x01,0xD2,0xE3,0x2B,0x38,0x00);
SSD_SEND(0x01,0xD4,0x00,0x01,0x00,0x0E,0x04,0x44,0x08,0x10,0x00,0x00,0x00);
SSD_SEND(0x01,0xE6,0x80,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF);
SSD_SEND(0x01,0xF0,0x12,0x03,0x20,0x00,0xFF);
SSD_SEND(0x01,0xF3,0x00);
DCS_Short_Write_NP(0x11);
Delay(750);//delay 150ms
DCS_Short_Write_NP(0x29);
Delay(100);//delay 20ms
目前的改写都是需要自己一一改写,本文借助Chatgpt写了一个自动转换的代码,实际上是一个文本处理程序,为了节省时间,仅实现多余三个参数的情况,1个参数和2个参数还有延时的情况需要自己添加。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void convert(const char *input) {
const char *ptr = input;
int arg_count = 0;
// Look for "SSD_SEND(" in the input
ptr = strstr(input, "SSD_SEND(");
if (ptr != NULL) {
// Move the pointer to the end of "SSD_SEND("
ptr += 8;
// Print the initial part (39 00)
printf("39 00 ");
// Count the number of arguments
while (*ptr != '\0' && *ptr != ')') {
if (*ptr == '0' && *(ptr + 1) == 'x') {
// Skip "0x" and the hexadecimal digits
ptr += 2;
while ((*ptr >= '0' && *ptr <= '9') || (*ptr >= 'A' && *ptr <= 'F')) {
ptr++;
}
arg_count++;
}
ptr++;
}
// Print the argument count as a hexadecimal character
printf("%02X", arg_count-1);
// Print a space
printf(" ");
} else {
printf("SSD_SEND not found in input.");
return;
}
// Parse and print the arguments
ptr = strstr(input, "0x");
int first = 1;
while (ptr != NULL) {
if (!first) {
// Convert the hexadecimal string to an integer
int value = (int)strtol(ptr, (char **)&ptr, 16);
// Print the value as a hexadecimal character
printf("%02X ", value);
} else {
first = 0;
// Skip "0x" and the hexadecimal digits
ptr += 2;
while ((*ptr >= '0' && *ptr <= '9') || (*ptr >= 'A' && *ptr <= 'F')) {
ptr++;
}
}
// Look for the next "0x" occurrence
ptr = strstr(ptr, "0x");
}
printf("\n");
}
void splitText(const char *text, char *line) {
const char *delimiter = ";"; // 按分号分割
const char *ptr = text;
while (*ptr != '\0') {
if (*ptr == ';') {
strcat(line, ";"); // 追加分号
strcat(line, "\n"); // 添加换行符
} else {
strncat(line, ptr, 1);
}
ptr++;
}
}
int main() {
const char *text = "SSD_SEND(0x01,0xE0,0xAB,0xBA);"
"SSD_SEND(0x01,0xE1,0xBA,0xAB);"
"SSD_SEND(0x01,0xB1,0x10,0x01,0x47,0xFF);"
"SSD_SEND(0x01,0xB2,0x0C,0x14,0x04,0x50,0x50,0x14);"
"SSD_SEND(0x01,0xB3,0x56,0x53,0x00);"
"SSD_SEND(0x01,0xB4,0x33,0x30,0x04);"
"SSD_SEND(0x01,0xB6,0xB0,0x00,0x00,0x10,0x00,0x10,0x00);"
"SSD_SEND(0x01,0xB8,0x05,0x12,0x29,0x49,0x48);"
"SSD_SEND(0x01,0xB9,0x7C,0x65,0x55,0x49,0x46,0x36,0x3B,0x24,0x3D,0x3C,0x3D,0x5C,0x4C,0x55,0x47,0x46,0x39,0x26,0x06,0x7C,0x65,0x55,0x49,0x46,0x36,0x3B,0x24,0x3D,0x3C,0x3D,0x5C,0x4C,0x55,0x47,0x46,0x39,0x26,0x06);"
"SSD_SEND(0x01,0xC0,0xFF,0x87,0x12,0x34,0x44,0x44,0x44,0x44,0x98,0x04,0x98,0x04,0x0F,0x00,0x00,0xC1);"
"SSD_SEND(0x01,0xC1,0x54,0x94,0x02,0x85,0x9F,0x00,0x7F,0x00,0x54,0x00);"
"SSD_SEND(0x01,0xC2,0x17,0x09,0x08,0x89,0x08,0x11,0x22,0x20,0x44,0xFF,0x18,0x00);"
"SSD_SEND(0x01,0xC3,0x86,0x46,0x05,0x05,0x1C,0x1C,0x1D,0x1D,0x02,0x1F,0x1F,0x1E,0x1E,0x0F,0x0F,0x0D,0x0D,0x13,0x13,0x11,0x11,0x00);"
"SSD_SEND(0x01,0xC4,0x07,0x07,0x04,0x04,0x1C,0x1C,0x1D,0x1D,0x02,0x1F,0x1F,0x1E,0x1E,0x0E,0x0E,0x0C,0x0C,0x12,0x12,0x10,0x10,0x00);"
"SSD_SEND(0x01,0xC6,0x2A,0x2A);"
"SSD_SEND(0x01,0xC8,0x21,0x00,0x31,0x42,0x34,0x16);"
"SSD_SEND(0x01,0xCA,0xCB,0x43);"
"SSD_SEND(0x01,0xCD,0x0E,0x4B,0x4B,0x20,0x19,0x6B,0x06,0xB3);"
"SSD_SEND(0x01,0xD2,0xE3,0x2B,0x38,0x00);"
"SSD_SEND(0x01,0xD4,0x00,0x01,0x00,0x0E,0x04,0x44,0x08,0x10,0x00,0x00,0x00);"
"SSD_SEND(0x01,0xE6,0x80,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF);"
"SSD_SEND(0x01,0xF0,0x12,0x03,0x20,0x00,0xFF);"
;
char all[50000] = ""; // 初始化line
splitText(text, all);
// printf("%s", all); // 输出分割后的文本
const char *delimiter = "\n";
char *line = strtok(all, delimiter);
while (line != NULL) {
convert(line);
line = strtok(NULL, delimiter);
}
return 0;
}
运行结果如下:
修改后的设备树节点:
&dsi {
status = "okay";
rockchip,lane-rate = <480>;
panel@0 {
compatible = "ilitek,ili9881d", "simple-panel-dsi";
reg = <0>;
backlight = <&backlight>;
power-supply = <&vcc18_lcd_n>;
prepare-delay-ms = <5>;
reset-delay-ms = <1>;
init-delay-ms = <80>;
disable-delay-ms = <10>;
unprepare-delay-ms = <5>;
width-mm = <68>;
height-mm = <121>;
dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
dsi,format = <MIPI_DSI_FMT_RGB888>;
dsi,lanes = <4>;
panel-init-sequence = [
39 00 03 E0 AB BA
39 00 03 E1 BA AB
39 00 05 B1 10 01 47 FF
39 00 07 B2 0C 14 04 50 50 14
39 00 04 B3 56 53 00
39 00 04 B4 33 30 04
39 00 08 B6 B0 00 00 10 00 10 00
39 00 06 B8 05 12 29 49 48
39 00 27 B9 7C 65 55 49 46 36 3B 24 3D 3C 3D 5C 4C 55 47 46 39 26 06 7C 65 55 49 46 36 3B 24 3D 3C 3D 5C 4C 55 47 46 39 26 06
39 00 11 C0 FF 87 12 34 44 44 44 44 98 04 98 04 0F 00 00 C1
39 00 0B C1 54 94 02 85 9F 00 7F 00 54 00
39 00 0D C2 17 09 08 89 08 11 22 20 44 FF 18 00
39 00 17 C3 86 46 05 05 1C 1C 1D 1D 02 1F 1F 1E 1E 0F 0F 0D 0D 13 13 11 11 00
39 00 17 C4 07 07 04 04 1C 1C 1D 1D 02 1F 1F 1E 1E 0E 0E 0C 0C 12 12 10 10 00
39 00 03 C6 2A 2A
39 00 07 C8 21 00 31 42 34 16
39 00 03 CA CB 43
39 00 09 CD 0E 4B 4B 20 19 6B 06 B3
39 00 05 D2 E3 2B 38 00
39 00 0C D4 00 01 00 0E 04 44 08 10 00 00 00
39 00 09 E6 80 01 FF FF FF FF FF FF
39 00 06 F0 12 03 20 00 FF
15 00 02 F3 00
05 FF 01 11
05 64 01 29
];
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <78000000>;
hactive = <800>;
vactive = <1280>;
hfront-porch = <80>;
hsync-len = <20>;
hback-porch = <80>;
vfront-porch = <20>;
vsync-len = <4>;
vback-porch = <12>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel_in_dsi: endpoint {
remote-endpoint = <&dsi_out_panel>;
};
};
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi_out_panel: endpoint {
remote-endpoint = <&panel_in_dsi>;
};
};
};
};