页次: 1
插个眼,最近也是在调试屏幕,在st7789的手册时序里面也看到了8080。
8080的接口虽然古老,但是用着还行,自己也有RGB屏幕,但不想看着8080屏幕吃灰就用起来了。
自从用F1C200S调试8080接口16位并行LCD屏幕成功后,就迫不及待的用T113-S3做实验,本来想着挺简单的,但是被一条指令搞了一天,就是它:
if (info->lcd_cpu_mode == LCD_CPU_AUTO_MODE)
{
DBG_INFO(" cpu auto mode.\n");
sunxi_lcd_cpu_set_auto_mode(sel);
}
不知道是不是我的LCD特殊的原因,看着别人的屏幕不用这条指令也能点亮,无语中,LCD屏幕是ST7796SV芯片,背光PWM用的是PWM4(PE10),屏幕RESET管脚是PG10,未开启“TE”模式。写一下配置,为后面的兄弟避坑了。
board.dts
&pio {
......
rgb16_pins_a: rgb16@0 {
allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", \
"PD6", "PD7", "PD8", "PD10", "PD11", "PD12", \
"PD13", "PD14", "PD15", "PD16", "PD17", \
"PD18", "PD19", "PD20", "PD21";
allwinner,pname = "lcdb0", "lcdb1", "lcdb2", "lcdb3", "lcdb4", \
"lcdg0", "lcdg1", "lcdg2", "lcdg3", "lcdg4", "lcdg5", \
"lcdr0", "lcdr1", "lcdr2", "lcdr3", "lcdr4", \
"lcdwr", "lcdrs", "lcdrd", "lcdcs";
allwinner,function = "lcd0";
allwinner,muxsel = <2>;
allwinner,drive = <3>;
allwinner,pull = <0>;
};
rgb16_pins_b: rgb16@1 {
allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", \
"PD6", "PD7", "PD8", "PD10", "PD11", "PD12", \
"PD13", "PD14", "PD15", "PD16", "PD17", \
"PD18", "PD19", "PD20", "PD21";
allwinner,pname = "lcdb0", "lcdb1", "lcdb2", "lcdb3", "lcdb4", \
"lcdg0", "lcdg1", "lcdg2", "lcdg3", "lcdg4", "lcdg5", \
"lcdr0", "lcdr1", "lcdr2", "lcdr3", "lcdr4", \
"lcdwr", "lcdrs", "lcdrd", "lcdcs";
allwinner,function = "io_disabled";
allwinner,muxsel = <7>;
allwinner,drive = <3>;
allwinner,pull = <0>;
};
}
/* 8080i 16bit */
&lcd0 {
// part 1
lcd_used = <1>;
lcd_driver_name = "st7796sv_cpu";
// part 2
lcd_if = <1>;
lcd_cpu_if = <8>;
// part 3
lcd_x = <480>;
lcd_y = <320>;
lcd_width = <84>;
lcd_height = <56>;
lcd_dclk_freq = <24>;
lcd_hbp = <100>;
lcd_ht = <618>;
lcd_hspw = <50>;
lcd_vbp = <8>;
lcd_vt = <336>;
lcd_vspw = <4>;
// part 4
lcd_pwm_used = <1>;
lcd_pwm_ch = <4>;
lcd_pwm_freq = <50000>;
lcd_pwm_pol = <1>;
lcd_pwm_max_limit = <255>;
lcd_backlight = <150>;
// part 5
lcd_cpu_mode = <0>;
lcd_cpu_te = <0>;
// part 6
lcd_frm = <0>;
lcd_gamma_en = <0>;
lcd_cmap_en = <0>;
lcd_rb_swap = <0>;
// part 7
// reset pin
lcd_gpio_0 = <&pio PG 10 1 0 3 1>;
pinctrl-0 = <&rgb16_pins_a>;
pinctrl-1 = <&rgb16_pins_b>;
};
st7796sv_cpu.c
#include "st7796sv_cpu.h"
// #define CPU_TRI_MODE
#define DBG_INFO(format, args...) (printk("[ST7796SV LCD INFO] LINE:%04d-->%s:"format, __LINE__, __func__, ##args))
#define DBG_ERR(format, args...) (printk("[ST7796SV LCD ERR] LINE:%04d-->%s:"format, __LINE__, __func__, ##args))
#define panel_reset(val) sunxi_lcd_gpio_set_value(sel, 0, val)
// #define lcd_cs(val) sunxi_lcd_gpio_set_value(sel, 1, val)
#define LCD_WIDTH 480
#define LCD_HEIGHT 320
#define MADCTL_MY 0x80 // 纵向交换
#define MADCTL_MX 0x40 // 横向交换
#define MADCTL_MV 0x20 // 纵横交换
#define MADCTL_ML 0x10 // 从下到上刷新
#define MADCTL_RGB 0x00 // RGB
#define MADCTL_BGR 0x08 // BGR
#define MADCTL_MH 0x04 // 从右到左刷新
typedef enum
{
LCD_DIRECTION_0,
LCD_DIRECTION_90,
LCD_DIRECTION_180,
LCD_DIRECTION_270
} eLcdDirection;
static void lcd_panel_st7796sv_init(u32 sel, struct disp_panel_para *info);
static void LCD_power_on(u32 sel);
static void LCD_power_off(u32 sel);
static void LCD_bl_open(u32 sel);
static void LCD_bl_close(u32 sel);
static void LCD_panel_init(u32 sel);
static void LCD_panel_exit(u32 sel);
static void LCD_cfg_panel_info(struct panel_extend_para *info)
{
}
static s32 LCD_open_flow(u32 sel)
{
LCD_OPEN_FUNC(sel, LCD_power_on, 120);
#ifdef CPU_TRI_MODE
LCD_OPEN_FUNC(sel, LCD_panel_init, 100);
LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 50);
#else
LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 100);
LCD_OPEN_FUNC(sel, LCD_panel_init, 50);
#endif
LCD_OPEN_FUNC(sel, LCD_bl_open, 0);
return 0;
}
static s32 LCD_close_flow(u32 sel)
{
LCD_CLOSE_FUNC(sel, LCD_bl_close, 20);
#ifdef CPU_TRI_MODE
LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 10);
LCD_CLOSE_FUNC(sel, LCD_panel_exit, 50);
#else
LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10);
LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 10);
#endif
LCD_CLOSE_FUNC(sel, LCD_power_off, 0);
return 0;
}
static void LCD_power_on(u32 sel)
{
/*config lcd_power pin to open lcd power0 */
sunxi_lcd_power_enable(sel, 0);
sunxi_lcd_pin_cfg(sel, 1);
}
static void LCD_power_off(u32 sel)
{
/*lcd_cs, active low */
// lcd_cs(1);
sunxi_lcd_delay_ms(10);
/*lcd_rst, active hight */
panel_reset(1);
sunxi_lcd_delay_ms(10);
sunxi_lcd_pin_cfg(sel, 0);
/*config lcd_power pin to close lcd power0 */
sunxi_lcd_power_disable(sel, 0);
}
static void LCD_bl_open(u32 sel)
{
sunxi_lcd_pwm_enable(sel);
/*config lcd_bl_en pin to open lcd backlight */
sunxi_lcd_backlight_enable(sel);
}
static void LCD_bl_close(u32 sel)
{
/*config lcd_bl_en pin to close lcd backlight */
sunxi_lcd_backlight_disable(sel);
sunxi_lcd_pwm_disable(sel);
}
/*static int bootup_flag = 0;*/
static void LCD_panel_init(u32 sel)
{
struct disp_panel_para *info =
kmalloc(sizeof(struct disp_panel_para), GFP_KERNEL);
DBG_INFO("\n");
bsp_disp_get_panel_info(sel, info);
lcd_panel_st7796sv_init(sel, info);
kfree(info);
return;
}
static void LCD_panel_exit(u32 sel)
{
sunxi_lcd_cpu_write_index(sel, 0x28);
sunxi_lcd_cpu_write_index(sel, 0x10);
}
void lcdSetWindows(u32 sel, u16 x, u16 y, u16 width, u16 height)
{
u16 temp;
// 横向
temp = x + width - 1;
if (temp >= LCD_WIDTH) {
temp = LCD_WIDTH - 1;
}
sunxi_lcd_cpu_write_index(sel, 0x2a);
sunxi_lcd_cpu_write_data(sel, (x >> 8) & 0xFF);
sunxi_lcd_cpu_write_data(sel, x & 0xFF);
sunxi_lcd_cpu_write_data(sel, (temp >> 8) && 0xFF);
sunxi_lcd_cpu_write_data(sel, temp & 0xFF);
// 纵向
temp = y + height - 1;
if (temp >= LCD_HEIGHT) {
temp = LCD_HEIGHT - 1;
}
sunxi_lcd_cpu_write_index(sel, 0x2b);
sunxi_lcd_cpu_write_data(sel, (y >> 8) && 0xFF);
sunxi_lcd_cpu_write_data(sel, y & 0xFF);
sunxi_lcd_cpu_write_data(sel, (temp >> 8) && 0xFF);
sunxi_lcd_cpu_write_data(sel, temp & 0xFF);
// 开始写入屏幕
sunxi_lcd_cpu_write_index(sel, 0x2c);
}
void lcdSetDirection(u32 sel, eLcdDirection dir)
{
u8 temp = 0;
switch (dir) {
case LCD_DIRECTION_0:
temp = MADCTL_MX;
break;
case LCD_DIRECTION_90:
temp = MADCTL_MV;
break;
case LCD_DIRECTION_180:
temp = MADCTL_MY;
break;
case LCD_DIRECTION_270:
temp = MADCTL_MY | MADCTL_MX | MADCTL_MV;
break;
default:
break;
}
temp |= MADCTL_BGR;
sunxi_lcd_cpu_write_index(sel, 0x36);
sunxi_lcd_cpu_write_data(sel, temp);
lcdSetWindows(sel, 0, 0, LCD_WIDTH, LCD_HEIGHT);
}
static void lcd_panel_st7796sv_init(u32 sel, struct disp_panel_para *info)
{
u8 initCmd[] = {
0x81, 0x11,
2, 0xfb, 0xc3,
2, 0xfb, 0x96,
2, 0x36, 0x48,
2, 0x3a, 0x55,
2, 0xb4, 0x01,
2, 0xb7, 0xc6,
9, 0xe8, 0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33,
2, 0xc1, 0x06,
2, 0xc2, 0xa7,
2, 0xc5, 0x18,
15, 0xe0, 0x0F, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0X54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b,
15, 0xe1, 0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b,
2, 0xeb, 0x3c,
0x82, 0xeb, 0x69,
1, 0x29,
0x00
};
u8* cmdIndex = initCmd;
u8 count, temp;
u16 index;
DBG_INFO(" %d, %d\n", info->lcd_x, info->lcd_y);
/*lcd_cs, active low */
// lcd_cs(0);
// reset
panel_reset(1);
sunxi_lcd_delay_ms(10);
panel_reset(0);
sunxi_lcd_delay_ms(20);
panel_reset(1);
sunxi_lcd_delay_ms(150);
// init lcd
while (*cmdIndex) {
temp = *cmdIndex++;
count = (temp & 0x7F) - 1;
sunxi_lcd_cpu_write_index(sel, *cmdIndex++);
for (index = 0; index < count; ++index)
{
sunxi_lcd_cpu_write_data(sel, *cmdIndex++);
}
if (temp >= 0x80) {
sunxi_lcd_delay_ms(150);
}
}
#if defined(CPU_TRI_MODE)
/* enable te, mode 0 */
sunxi_lcd_cpu_write_index(0, 0x35);
sunxi_lcd_cpu_write_data(0, 0x00);
sunxi_lcd_cpu_write_index(0, 0x44);
sunxi_lcd_cpu_write_data(0, 0x00);
sunxi_lcd_cpu_write_data(0, 0x80);
#endif
lcdSetDirection(sel, LCD_DIRECTION_90);
sunxi_lcd_cpu_write_index(sel, 0x2c);
if (info->lcd_cpu_mode == LCD_CPU_AUTO_MODE)
{
DBG_INFO(" cpu auto mode.\n");
sunxi_lcd_cpu_set_auto_mode(sel);
}
}
/* panel driver name, must mach the name of lcd_drv_name in sys_config.fex */
struct __lcd_panel st7796sv_cpu_panel = {
.name = "st7796sv_cpu",
.func = {
.cfg_panel_info = LCD_cfg_panel_info,
.cfg_open_flow = LCD_open_flow,
.cfg_close_flow = LCD_close_flow,
},
};
有天逛gitee时发现有个叫“黑白菜”的UP的仓库里有F1C200S的一个库,看了下介绍,说是参照一些大佬的作品做出来的,感兴趣就试了试,的确挺好用的,地址在这里F1C200S,不知道这位兄弟在不在这个论坛,献上我对他和其他大佬们的敬意。
用了2天“黑白菜”的库后,我发现里面有初始化8080屏幕接口的函数,还有写入TCON0的0x64寄存器的函数。熟悉的小伙伴看见这个地址就知道这是专门发送8080命令给LCD用的,但是怎么用却没有提供例程,没办法自己摸索呗,以下是我这个初学者这几天摸索的结果,说的不对的地方请各位大佬指正。
一、TCON是自动按照时序发送命令给LCD的。
我第一次调通的时候也是很惊讶,当然这和我没有用过太多的SOC有关,初学者一枚。也就是说F1C200S和8080屏幕相连时,几个重要的管脚顺序如下(以RGB屏幕类比):
CS = VSYNC。
RD = HSYNC。
RS = DE。
WR = CLK。
这也就是用户手册中TCON0的0x68寄存器中提到的IO3~IO0几个管脚。其实对照T113或者D1H的手册中关于屏幕接线的图就不难发现的,估价大佬们都知道的,我是无意间发现,再通过实验证实的,这里就不长篇大论了,直接说结论:
举个例子我想发送一条指令给LCD,比如是设置屏幕横向尺寸的0x2A指令,一般情况下的指令序列用伪指令应该是。
CS = 0
WR = 0
RS = 0 // 发送指令为低,数据为高
D0~D15 = 0x2A
WR = 1
CS = 1
但是现在只需要给TCON0的0x64寄存器发送这个0x2A指令,接着设置0x60寄存器的第25位为低或者高,告诉TCON我们发送的是“命令”还是“数据”,那么TCON会按照上面伪指令的时序自动调整CS、WR管脚(读取的时候是RD管脚),比我们自己写指令方便多了,这在F1C200S的用户手册里没写,我是参照T113的用户手册看的,用“黑白菜”库里的代码如下:
void de_lcd_8080_write(uint16_t data, bool is_cmd) {
while(read32(TCON_BASE + TCON0_CPU_INTF) & 0x00C00000)
;
if(is_cmd) {
clear32(TCON_BASE + TCON0_CPU_INTF, (1 << 25));
} else {
set32(TCON_BASE + TCON0_CPU_INTF, (1 << 25));
}
while(read32(TCON_BASE + TCON0_CPU_INTF) & 0x00C00000)
;
uint32_t reg_data = ((data & 0xfc00) << 8) | ((data & 0x0300) << 6) | ((data & 0x00e0) << 5) | ((data & 0x001f) << 3);
write32(TCON_BASE + TCON0_CPU_WR_DAT, reg_data);
}
我手头只有一台简单的示波器,没有逻辑分析仪,慢慢的一点点试出来的。
基于此,初始化屏幕就变得异常简单了,代码如下:
void lcdSetWindows(uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
uint16_t temp;
// 横向
temp = x + width - 1;
if (temp >= LCD_WIDTH)
{
temp = LCD_WIDTH - 1;
}
de_lcd_8080_write(0x2a, true);
de_lcd_8080_write(x >> 8, false);
de_lcd_8080_write(x & 0xFF, false);
de_lcd_8080_write(temp >> 8, false);
de_lcd_8080_write(temp & 0xFF, false);
// 纵向
temp = y + height - 1;
if (temp >= LCD_HIGHT)
{
temp = LCD_HIGHT - 1;
}
de_lcd_8080_write(0x2b, true);
de_lcd_8080_write(y >> 8, false);
de_lcd_8080_write(y & 0xFF, false);
de_lcd_8080_write(temp >> 8, false);
de_lcd_8080_write(temp & 0xFF, false);
// 开始写入屏幕
de_lcd_8080_write(0x2c, true);
}
// 寄存器0x36掩码
#define MADCTL_MY 0x80 // 纵向交换
#define MADCTL_MX 0x40 // 横向交换
#define MADCTL_MV 0x20 // 纵横交换
#define MADCTL_ML 0x10 // 从下到上刷新
#define MADCTL_RGB 0x00 // RGB
#define MADCTL_BGR 0x08 // BGR
#define MADCTL_MH 0x04 // 从右到左刷新
void lcdSetDirection(eLcdDirection dir)
{
uint8_t temp = 0;
switch (dir)
{
case LCD_DIRECTION_0:
temp = MADCTL_MX;
break;
case LCD_DIRECTION_90:
temp = MADCTL_MV;
break;
case LCD_DIRECTION_180:
temp = MADCTL_MY;
break;
case LCD_DIRECTION_270:
temp = MADCTL_MY | MADCTL_MX | MADCTL_MV;
break;
default:
break;
}
temp |= MADCTL_BGR;
de_lcd_8080_write(0x36, true);
de_lcd_8080_write(temp, false);
lcdSetWindows(0, 0, LCD_WIDTH, LCD_HIGHT);
}
void lcdFill(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)
{
lcdSetWindows(x, y, width, height);
for (int y = 0; y < width; y++)
{
for (int x = 0; x < height; x++)
{
de_lcd_8080_write(color, false);
}
}
}
void lcdInit()
{
// 屏幕RESET端口为PE7
gpio_pin_init(GPIOE, 7, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_DRV_3);
gpio_pin_set(GPIOE, 7);
delay_soft(2); // 2 * 7 = 14ms
gpio_pin_clear(GPIOE, 7);
delay_soft(3); // 3 * 7 = 21ms
gpio_pin_set(GPIOE, 7);
delay_soft(20); // 20 * 7 = 140ms
// 发送初始化命令
uint8_t initCmd[] = {
0x81, 0x11,
2, 0xfb, 0xc3,
2, 0xfb, 0x96,
2, 0x36, 0x48,
2, 0x3a, 0x55,
2, 0xb4, 0x01,
2, 0xb7, 0xc6,
9, 0xe8, 0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33,
2, 0xc1, 0x06,
2, 0xc2, 0xa7,
2, 0xc5, 0x18,
15, 0xe0, 0x0F, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0X54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b,
15, 0xe1, 0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b,
2, 0xeb, 0x3c,
0x82, 0xeb, 0x69,
1, 0x29,
0x00};
const uint8_t *cmdIndex = initCmd;
uint8_t count, temp;
while (*cmdIndex)
{
temp = *cmdIndex++;
count = (temp & 0x7F) - 1;
de_lcd_8080_write(*cmdIndex++, true);
for (size_t index = 0; index < count; ++index)
{
de_lcd_8080_write(*cmdIndex++, false);
}
if (temp >= 0x80)
{
delay_soft(20); // 20 * 7 = 140ms
}
}
// 设置屏幕方向为横屏
lcdSetDirection(LCD_DIRECTION_90);
// 清除屏幕
lcdFill(0, 0, LCD_WIDTH, LCD_HIGHT, COLOR565_BLACK);
}
二、TCON、DEFE、DEBE的关系。
这个很好理解,手册里也说了,DEFE和DEBE可以对送过来的图像进行处理,然后送给TCON发送到LCD,今天先说TCON和DEBE。
三、屏幕的颜色模式怎么选择。
我的屏幕是一块普通的8080接口的4寸屏幕,最大16位传输(18位被厂家屏蔽了),RGB565格式,当然通过屏幕的最后三个管脚M0~M2可以让它成为8位或者9位传输模式(RGB666或者RGB888模式)。为了最大传输效率,我选择了16位RGB565模式。
开发板是自己设计并焊接的,用起来效果还行吧,原本开发板搭载的是RGB屏幕,但是为了8080屏幕,自己又设计了块转接板。
那么如何让TCON知道我的屏幕模式呢?请看TCON0的0x60寄存器的第31~29位,这些位就是用来设置屏幕格式的,其中的18、9、8位模式都好理解,为啥16位要有4种颜色模式呢,我的屏幕到底是哪种呢?
这个问题也是无意间解决的,就在第二天我无意间翻阅了T113的“LCD开发指南”的43页,猜我发现了什么?
18bit/1cycle (RGB666)
16bit/3cycle (RGB666)
16bit/2cycle (RGB666)
16bit/2cycle (RGB666)
16bit/1cycle (RGB565)
9bit/1cycle (RGB666)
8bit/3cycle (RGB666)
8bit/2cycle (RGB565)
是不是和TCON0的0x60寄存器的第31~29位对应上了,其中cycle的意思是写入数据需要几个时钟周期,比如我的屏幕是一次可以写入16位数据,所以是“16bit/1cycle (RGB565)”这个模式,按照F1C200S的用户手册相关内容写入“100”给第31~29位,这样颜色模式的问题就解决了。幸好显示接口变化不大,要不然还真不好找呢。
四、TCON和DEBE。
不知道大伙还记不记得,DEBE有个设置图层(Layer)的功能,一共有4个图层,我们一般用Layer0即第一层,通过DEBE的相关功能可以设置这个图层的大小、位置等,最后再用它的0x850寄存器绑定一个我们自己定义的数组给它(暂时叫它Buffer0)。这样我们修改自己的数组Buffer0的内容,等于间接修改了Layer0的内容。但是到这里我就不太明白了,修改了图层后,图层怎么传给TCON呢?RGB屏幕的话我们就不用管理了,TCON会自动处理。到了8080接口这里是不是也是这样呢?通过实验,我发现TCON0的0x60寄存器的第28、27位比较可疑,当然手册里面也写了,但是写的很模糊。第28位是启动自动刷新,第27位则是启动直接刷新,抱着试试看的态度,我将Buffer0的所有数据填充成某种颜色(比如红色),然后通过TCON发送指令0x2A、0x2B和0x2C指令给LCD(即设置写入窗口大小并打开LCD的数据写入功能),再设置TCON0的0x60寄存器的第28或27位设为1时,TCON自动的将Buffer0里面的数据传送给了LCD。我继续让程序每秒动态修改Buffer0的里面的颜色值,随后在LCD上显示出来的也就是写入的颜色了,而且速度很快(不知道是不是内部用了DMA),超过了LCD的液晶的反应速度(不知道这样说对不对),以至于我不得不加入了每帧7ms的延时,就这样也超过了每秒百帧以上的速度(不用延时帧率更高)。不过出来的图像因为液晶速度的原因会产生“Tear Effect”现象,幸好我的这个屏幕提供了一个FMARK管脚,用于同步处理TE信号,这都是后面要研究的了,代码片段如下:
void lcdDirectFlush(bool enabled)
{
if (enabled)
{
set32(TCON_BASE + TCON0_CPU_INTF, (1 << 27));
}
else
{
clear32(TCON_BASE + TCON0_CPU_INTF, (1 << 27));
}
}
...
lcdSetWindows(0, 0, LCD_WIDTH, LCD_HIGHT);
// de_lcd_8080_auto_mode(true);
lcdDirectFlush(true);
uint16_t rectX, rectY, rectWidth, rectHeight;
for(index = 0; index < testCount; ++index)
{
rectX = rand() % (LCD_WIDTH >> 1);
rectY = rand() % (LCD_HIGHT >> 1);
rectWidth = rand() % LCD_WIDTH;
if(rectX + rectWidth >= LCD_WIDTH)
{
rectWidth = LCD_WIDTH - rectX - 1;
}
rectHeight = rand() % LCD_HIGHT;
if(rectY + rectHeight >= LCD_HIGHT)
{
rectHeight = LCD_HIGHT - rectY - 1;
}
disp_fill(rectX, rectY, rectWidth, rectHeight, colorList[index % colorCount]);
delay_soft(1); // 1 * 7 = 7ms
}
这就是我这几天的一点感受,虽然很累但是觉得很值得。再次感谢“黑白菜”和论坛里各位大佬的不懈努力。不知道我以上的表述的清楚不清楚,请各位大佬批评指正。
PS:我已经整理好例程,向“黑白菜”提出了Pull requests。如果着急的话可以直接下载如下的代码压缩包,代码使用方法,先下载“黑白菜”的库,烧写它的BootLoader,然后将我的代码解压到库的“examples”文件夹里,进入文件夹后make download即可,我用的是串口0,需要改变可以修改system.c文件,记得让开发板提前进入FEL模式。
display8080.tar.gz
1、你的配置中的ht和vt算下来差不多12mhz的,是不是按照V821的手册照抄的,这个应该按照你的lcd的手册配置的。lcd_cpu_mode=<0>是为了自动刷屏,lcd_cpu_te=<0>是不用te功能,因为你说没有引出te管脚。
2、cat /sys/class/disp/disp/attr/sys多次,看看显示的irq数字变化了没有,如果没有变化还是时序不对。
3、建议找找本论坛里的“Linux_LCD_开发指南”,我是在100ask下载的。
lcd_cpu_mode和lcd_cpu_te都改为0。如果还是不行,lcd_dclk_freq设为12试试。
可以分享一下你的驱动吗?你用的内核版本是多少?
裸机跑的,没用linux,等我整理一下再发出来。
nes的声音和图像基本同步吧。
@nTliang
在值班,明天晚上回去发nes的视频。
@nTliang
这个我倒是没有测试,你的帖子我看了,但手头没有工具测试。不过我试了一下nes,声音和图像同步的。整屏发送后是让硬件去处理framebuffer,写到frambuffer的东西tcon会自动送给lcd,不知道硬件有没有用到dma,我觉得速度还可以。
不是每帧的间隔,是每行的间隔。我这边的现象是发送完一行数据后会有几十us的空闲
是整屏发送,不是每行发送。
你这个传输数据的时候中间会有间隔吗?
每帧大概停了7ms左右,要不然一下子就传过去图像屏幕还没有反应过来。
现在类似于f1c200s的芯片很多了吧,也很便宜,这芯片还有优势么?还没资料
手头还剩下几片,放着也是浪费,用起来呗。
手里有块4寸的16位8080接口并行LCD屏幕,想用F1C200S芯片驱动起来。但在网上找了一圈没有相关显示并行屏幕的例子,gitee上有位叫“黑白菜”的兄台出了个裸机程序,虽然有8080初始化部分,只有rgb屏幕的程序和例程。索性就自己研究一下呗,经过几天的努力,今天终于可以显示内容了,速度方面还算可以,终于可以休息了。
页次: 1