测试在HDMI屏幕上显示LVGL
根据前面的步骤,这一步开始测试lvgl,看了sdk,已经移植好lvgl进来,用的lvgl8.0版本,我最早用的还是5.3,不用说,改动的应该很大,不过在测试lvgl之前,先尝试在fb上输出看看,毕竟驱屏从画点开始
1. 用fb驱屏
在串口终端中输入:ls /dev/f 这里加个TAB键,能看到有fb0,那么后面测试就是在fb0上输出了
输出参考了这里linux笔记(7):东山哪吒D1H使用framebuffer画直线(HDMI输出)
先在虚拟机将d1-h的工具链设置到系统环境
d1-h工具链路径tina-d1-h/prebuilt/gcc/linux-x86/riscv/toolchain-thead-glibc/riscv64-glibc-gcc-thead_20200702/bin
我这里sdk是放在桌面的,所以这里全路径是/home/allwinner/Desktop/tina-d1-h/prebuilt/gcc/linux-x86/riscv/toolchain-thead-glibc/riscv64-glibc-gcc-thead_20200702/bin
然后打开~/.bashrc,用什么都可以,不过要加sudo打开,不然保存不了,我用的gedit
sudo gedit ~/.bashrc
在最后加入:
PATH=$PATH:/home/allwinner/Desktop/tina-d1-h/prebuilt/gcc/linux-x86/riscv/toolchain-thead-glibc/riscv64-glibc-gcc-thead_20200702/bin
保存退出,然后执行source ~/.bashrc,这样在终端中就可以直接使用riscv64-linux-gcc了,可以测试下
在shell里,输入risv然后tab键,如果提示riscv64-linux-gcc,说明设置成功
下面是我的测试代码,在前面网友的基础上加了定点画色块,主要能验证是一个颜色问题,和坐标问题(做mcu的习惯),测试代码如下:
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <linux/fb.h>
#include <unistd.h>
static int fd_fb;
static struct fb_var_screeninfo var; /* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;
void lcd_put_pixel(int x, int y, unsigned int color)
//传入的 color 表示颜色,它的格式永远是 0x00RRGGBB,即 RGB888。
//当 LCD 是 16bpp 时,要把 color 变量中的 R、 G、 B 抽出来再合并成 RGB565 格式。
{
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
//计算(x,y)坐标上像素对应的 Framebuffer 地址。
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
//对于 8bpp, color 就不再表示 RBG 三原色了,这涉及调色板的概念, color 是调色板的值。
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
// R5 G6 B5
//先从 color 变量中把 R、 G、 B 抽出来。
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
//把 red、 green、 blue 这三种 8 位颜色值,根据 RGB565 的格式,
//只保留 red 中的高 5 位、 green 中的高 6 位、 blue 中的高 5 位,
//组合成一个新的 16 位颜色值。
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
//把新的 16 位颜色值写入 Framebuffer
*pen_16 = color;
break;
}
case 32:
{
//对于 32bpp,颜色格式跟 color 参数一致,可以直接写入Framebuffer
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n",var.bits_per_pixel);
break;
}
}
}
void lcd_fill_rect(int x, int y, int w, int h, unsigned int color){
for(int i=0; i<h; i++){
for(int j=0; j<w; j++){
lcd_put_pixel(x+j,y+i,color);
}
}
}
void blushScreen2(unsigned int color)
{// 这里只考虑24色,也就是HDMI的情况
unsigned int * p32=(unsigned int *)fb_base;
unsigned int h,w;
for(h=0;h<var.yres;h++)
for(w=0;w<var.xres;w++){
*p32= color;
p32++;
}
}
int main(int argc,int **argv)
{
int i;
printf("1.open /dev/fb0\n");
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
printf("2.get V Screen INFO\n");
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
printf("width:%d,hight:%d,bits_per_pixel:%d\n",var.xres,var.yres,var.bits_per_pixel);
printf("3.get framebuffer base address\n");
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fb_base == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}else{
printf("fb_base:0x%8x\n",fb_base);
}
/* 清屏: 全部设为白色 */
//memset(fb_base, 0xff, screen_size);
unsigned char colorData[10]={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xff};
unsigned int cData[30] = {0x00FF0000,0xFF00FF00,0xFF0000FF,
0xFFFFFF00,0xFFFF00FF,0xFF00FFFF,
0xFF7F7F7F,0xFF007F7F,0xFF7F007F,0x007F7F00,// 0-9 FF开头
0x7FFF0000,0x7F00FF00,0x7F0000FF,
0x00FFFF00,0x7FFF00FF,0x7F00FFFF,
0x7F7F7F7F,0x7F007F7F,0x7F7F007F,0x007F7F00,// 10-19 7F开头
0xFFFF0000,0x0000FF00,0x000000FF,
0x00FFFF00,0x00FF00FF,0x0000FFFF,
0x007F7F7F,0x00007F7F,0x007F007F,0xFF7F7F00};// 20-29 00开头
printf("4.flush screen in different colors\n");
for(i = 0;i<10;i++)
{
memset(fb_base,colorData[i],screen_size );
//sleep(1);//usleep(100*1000);
}
printf("5.draw 30 lines in different colors\n");
for (int j = 0; j < 30;j++){
for (i = 0; i < 100; i++) {
lcd_put_pixel(var.xres/3+i, var.yres/4+20*j+0, cData[j] );
lcd_put_pixel(var.xres/3+i, var.yres/4+20*j+1, cData[j] );
lcd_put_pixel(var.xres/3+i, var.yres/4+20*j+2, cData[j] );
lcd_put_pixel(var.xres/3+i, var.yres/4+20*j+3, cData[j] );
}
}
lcd_fill_rect(0,0,100,100,0xFF00FF00);
lcd_fill_rect(1800,0,100,100,0xFF0000FF);
lcd_fill_rect(0,900,100,100,0xFF00FFFF);
lcd_fill_rect(1800,900,100,100,0xFFFF0000);
munmap (fb_base, screen_size);
close(fd_fb);
return 0;
}
保存为main.c,随便吧,然后在shell下编译:
riscv64-unknown-linux-gnu-gcc main.c -o hdmitest
hdmitest即为测试程序,这里要放到板子的系统里运行,我之前在物理机的设备管理器看到板子被识别为安卓设备,可以用adb来发送,在虚拟机(virtualbox)的usb设备里将Allwinner开头的adb设备选中即可,之后在shell下执行
sudo adb push hdmitest .
我是直接放到根目录,方便测试,如果前面步骤没问题,这里应该能传输成功
在串口终端的shell下,ls能看到hdmitest,执行./hdmitest,这是就可以看到屏幕上的色块,如图
fb这么驱动没有问题,后面就可以上lvgl了
2. 测试sdk的lvgl测试程序
先在串口终端查看/usb/bin下有没有lv的测试程序,有的话可以跳过这步,运行测试下,没有就继续后面的步骤
在虚拟机shell执行make menuconfig
配置如下
.config - Tina Configuration
> Gui > Littlevgl ───────────────────────────────────────────────────────────────────────────────────────────────────────────────
┌──────────────────────────────────────────────────────── Littlevgl ─────────────────────────────────────────────────────────┐
│ Arrow keys navigate the menu. <Enter> selects submenus ---> (or empty submenus ----). Highlighted letters are hotkeys. │
│ Pressing <Y> includes, <N> excludes, <M> modularizes features. Press <Esc><Esc> to exit, <?> for Help, </> for Search. │
│ Legend: [*] built-in [ ] excluded <M> module < > module capable │
│ │
│ ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ <*> lv_examples................................. lvgl examples use lvgl-8.0.1 │ │
│ │ [*] lvgl-8.0.1 use sunxifb double buffer │ │
│ │ [ ] lvgl-8.0.1 use sunxifb cache │ │
│ │ [ ] lvgl-8.0.1 use sunxifb g2d │ │
│ │ [ ] lvgl-8.0.1 use sunxifb g2d rotate │ │
│ │ <*> lv_monitor................................... lvgl monitor use lvgl-8.0.1 │ │
然后保存退出,切换到lvgl的目录,路径:tina-d1-h/package/gui/littlevgl-8/,然后执行mm
然后croot切回tina根目录,执行make,后面打包烧录就可以了
此时在串口终端的shell就可以看到/usr/bin下有lv_examples和lv_monitor,运行下,就可以看到屏幕上的lvgl测试程序
这里有个奇怪的地方,lv_example里有5个测试demo
root@TinaLinux:/# lv_examples
lv_examples 0, is lv_demo_widgets
lv_examples 1, is lv_demo_music
lv_examples 2, is lv_demo_benchmark
lv_examples 3, is lv_demo_keypad_encoder
lv_examples 4, is lv_demo_stress
root@TinaLinux:/#
有个benchmark,我运行后,测试fps不到5……,这个有点奇怪
看图
另一个lv_monitor,界面包含cpu_test,HARDWARE_TEST,DDR_TEST,Video_TEST,camera_TEST,network_TEST
搭建LVGL项目
因为sdk里有现成的lvgl,就直接用了
- 在虚拟机shell切换到tina-d1-h/package/gui/littlevgl-8$目录,改目录下有4个文件夹
lv_demos lv_drivers lv_examples lvgl lv_monitor
lv_demos是之前测试的lv_examples的实际demo部分
lv_drivers是各种驱动
lv_examples是之前测试的lv_examples的入口程序
lvgl是lvgl的源代码
lv_monitor是之前那个有cpu的综合测试程序
- 在该目录,创建lv_ir_test目录,作为测试lvgl和ir的项目目录
- 参考lv_examples的Makefile,创建lv_ir_test/Makefile,内容如下
include $(TOPDIR)/rules.mk
include $(BUILD_DIR)/package.mk
PKG_NAME:=lv_ir_test
PKG_VERSION:=8.0.1
PKG_RELEASE:=1
PKG_BUILD_DIR := $(COMPILE_DIR)/$(PKG_NAME)
define Package/$(PKG_NAME)
SECTION:=gui
SUBMENU:=Littlevgl
CATEGORY:=Gui
DEPENDS:=+LVGL8_USE_SUNXIFB_G2D:libuapi +LVGL8_USE_SUNXIFB_G2D:kmod-sunxi-g2d
TITLE:=lvgl lv_ir_test use lvgl-8.0.1
MAINTAINER:=anruliu <anruliu@allwinnertech.com>
endef
PKG_CONFIG_DEPENDS := \
CONFIG_LVGL8_USE_SUNXIFB_DOUBLE_BUFFER \
CONFIG_LVGL8_USE_SUNXIFB_CACHE \
CONFIG_LVGL8_USE_SUNXIFB_G2D \
CONFIG_LVGL8_USE_SUNXIFB_G2D_ROTATE
# define Package/$(PKG_NAME)/config
# config LVGL8_USE_SUNXIFB_DOUBLE_BUFFER
# bool "lvgl-8.0.1 use sunxifb double buffer"
# default y
# help
# Whether use sunxifb double buffer
# config LVGL8_USE_SUNXIFB_CACHE
# bool "lvgl-8.0.1 use sunxifb cache"
# select LVGL8_USE_SUNXIFB_DOUBLE_BUFFER
# default y
# help
# Whether use sunxifb cache
# config LVGL8_USE_SUNXIFB_G2D
# bool "lvgl-8.0.1 use sunxifb g2d"
# select LVGL8_USE_SUNXIFB_DOUBLE_BUFFER
# default n
# help
# Whether use sunxifb g2d
# config LVGL8_USE_SUNXIFB_G2D_ROTATE
# bool "lvgl-8.0.1 use sunxifb g2d rotate"
# select LVGL8_USE_SUNXIFB_G2D
# default n
# help
# Whether use sunxifb g2d rotate
# endef
define Package/$(PKG_NAME)/Default
endef
define Package/$(PKG_NAME)/description
a lvgl lv_ir_test v8.0.1
endef
define Build/Prepare
$(INSTALL_DIR) $(PKG_BUILD_DIR)/
$(CP) ./src $(PKG_BUILD_DIR)/
$(CP) ./../lvgl $(PKG_BUILD_DIR)/src/
endef
define Build/Configure
endef
TARGET_CFLAGS+=-I$(PKG_BUILD_DIR)/src
ifeq ($(CONFIG_LVGL8_USE_SUNXIFB_DOUBLE_BUFFER),y)
TARGET_CFLAGS+=-DUSE_SUNXIFB_DOUBLE_BUFFER
endif
ifeq ($(CONFIG_LVGL8_USE_SUNXIFB_CACHE),y)
TARGET_CFLAGS+=-DUSE_SUNXIFB_CACHE
endif
ifeq ($(CONFIG_LVGL8_USE_SUNXIFB_G2D),y)
TARGET_CFLAGS+=-DUSE_SUNXIFB_G2D
TARGET_LDFLAGS+=-luapi
endif
ifeq ($(CONFIG_LVGL8_USE_SUNXIFB_G2D_ROTATE),y)
TARGET_CFLAGS+=-DUSE_SUNXIFB_G2D_ROTATE
endif
ifeq ($(CONFIG_LINUX_5_4),y)
TARGET_CFLAGS+=-DCONF_G2D_VERSION_NEW
endif
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR)/src\
ARCH="$(TARGET_ARCH)" \
AR="$(TARGET_AR)" \
CC="$(TARGET_CC)" \
CXX="$(TARGET_CXX)" \
CFLAGS="$(TARGET_CFLAGS)" \
LDFLAGS="$(TARGET_LDFLAGS)" \
INSTALL_PREFIX="$(PKG_INSTALL_DIR)" \
all
endef
#define Build/InstallDev
# $(INSTALL_DIR) $(1)/usr/lib
#endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/$(PKG_NAME) $(1)/usr/bin/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
用不到的,我注掉了
4. 在lv_ir_test目录下,创建src目录,作为源代码存放目录
5. 在src目录下,创建如下文件
disp_fb.c irdev.c lv_conf.h main.c test.c
disp_fb.h irdev.h lv_drv_conf.h Makefile test.h
最终呢,lv_ir_test结构如下:
.
├── Makefile
└── src
├── disp_fb.c
├── disp_fb.h
├── irdev.c
├── irdev.h
├── lv_conf.h
├── lv_drv_conf.h
├── main.c
├── Makefile
├── test.c
└── test.h
文件说明:
文件 | 说明 |
---|---|
disp_fb.c | lvgl显示驱动,使用fb驱动 |
disp_fb.h | disp_fb.c的头文件 |
irdrv.c | 红外驱动,这个备用,后面测试ir后完善 |
irdev.h | irdrv.c的头文件 |
lv_conf.h | lvgl的配置文件 |
lv_drv_conf.h | lvgl的驱动配置文件 |
main.c | lvgl的入口程序 |
test.c | 测试程序,测试lvgl |
test.h | test.c的头文件 |
src/Makefile | 项目的makefile |
Makefile(src同级目录的) | tina的package的makefile |
我这里src下的makefile也直接参考lv_examples/src的Makefile,内容如下 |
#
# Makefile
#
CC ?= gcc
LVGL_DIR_NAME ?= lvgl
LVGL_DIR ?= ${shell pwd}
CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/ -Wall -Wshadow -Wundef -Wmissing-prototypes -Wno-discarded-qualifiers -Wall -Wextra -Wno-unused-function -Wno-error=strict-prototypes -Wpointer-arith -fno-strict-aliasing -Wno-error=cpp -Wuninitialized -Wmaybe-uninitialized -Wno-unused-parameter -Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess -Wno-format-nonliteral -Wno-cast-qual -Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wformat-security -Wno-ignored-qualifiers -Wno-error=pedantic -Wno-sign-compare -Wno-error=missing-prototypes -Wdouble-promotion -Wclobbered -Wdeprecated -Wempty-body -Wtype-limits -Wshift-negative-value -Wstack-usage=2048 -Wno-unused-value -Wno-unused-parameter -Wno-missing-field-initializers -Wuninitialized -Wmaybe-uninitialized -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess -Wno-format-nonliteral -Wpointer-arith -Wno-cast-qual -Wmissing-prototypes -Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wno-discarded-qualifiers -Wformat-security -Wno-ignored-qualifiers -Wno-sign-compare
LDFLAGS ?= -lm
BIN = lv_ir_test
#Collect the files to compile
SRCDIRS = $(shell find . -maxdepth 1 -type d)
MAINSRC = $(foreach dir,$(SRCDIRS),$(wildcard $(dir)/*.c))
include $(LVGL_DIR)/lvgl/lvgl.mk
OBJEXT ?= .o
AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))
MAINOBJ = $(MAINSRC:.c=$(OBJEXT))
SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)
OBJS = $(AOBJS) $(COBJS)
## MAINOBJ -> OBJFILES
all: default
%.o: %.c
@$(CC) $(CFLAGS) -c $< -o $@
@echo "CC $<"
default: $(AOBJS) $(COBJS) $(MAINOBJ)
$(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS)
clean:
rm -f $(BIN) $(AOBJS) $(COBJS) $(MAINOBJ)
main.c内容如下:
#include "lvgl/lvgl.h"
#include "disp_fb.h"
#include "irdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include "test.h"
int main(void)
{
/*LittlevGL init*/
lv_init();
/*Linux frame buffer device init*/
fb_init();
/*A buffer for LittlevGL to draw the screen's content*/
static uint32_t width=0, height=0;
fb_get_sizes(&width, &height);
printf("Screen: %dx%d\r\n",width,height);
static lv_color_t *buf;
buf = (lv_color_t*) malloc(width * height * sizeof (lv_color_t));
if (buf == NULL) {
fb_exit();
printf("malloc draw buffer fail\n");
return 0;
}
/*Initialize a descriptor for the buffer*/
static lv_disp_draw_buf_t disp_buf;
lv_disp_draw_buf_init(&disp_buf, buf, NULL, width * height);
/*Initialize and register a display driver*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = fb_flush;
disp_drv.hor_res = width;
disp_drv.ver_res = height;
// disp_drv.rotated = rotated;
lv_disp_drv_register(&disp_drv);
irdev_init();
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type =LV_INDEV_TYPE_BUTTON; /*See below.*/
indev_drv.read_cb = irdev_read; /*See below.*/
/*Register the driver in LVGL and save the created input device object*/
lv_indev_t * irdev_indev = lv_indev_drv_register(&indev_drv);
/*Create a Demo*/
test(irdev_indev);
// lv_color_t c;
// lv_area_t a1={0,0,99,99};
// lv_color_t cc[10000] = {0};
// c.ch.blue=255;
// c.ch.alpha=255;
// lcd_fill_rect(700,0,100,100,c.full);
// for(int i=0; i<10000; i++)
// cc[i] = c;
// fb_flush(NULL,&a1,cc);
// c.full=0;
// c.ch.red=255;
// c.ch.alpha=255;
// lcd_fill_rect(1200,0,100,100,c.full);
// lv_area_t a2={1800,0,1899,99};
// for(int i=0; i<10000; i++)
// cc[i] = c;
// fb_flush(NULL,&a2,cc);
// c.full=0;
// c.ch.green=255;
// c.ch.alpha=255;
// lcd_fill_rect(700,600,100,100,c.full);
// lv_area_t a3={0,900,99,999};
// for(int i=0; i<10000; i++)
// cc[i] = c;
// fb_flush(NULL,&a3,cc);
// c.full=0;
// c.ch.alpha=255;
// lcd_fill_rect(1200,600,100,100,c.full);
// lv_area_t a4={1800,900,1899,999};
// for(int i=0; i<10000; i++)
// cc[i] = c;
// fb_flush(NULL,&a4,cc);
/*Handle LitlevGL tasks (tickless mode)*/
while(1) {
lv_timer_handler();
usleep(5000);
}
return 0;
}
/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
static uint64_t start_ms = 0;
if(start_ms == 0) {
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
}
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
uint64_t now_ms;
now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;
uint32_t time_ms = now_ms - start_ms;
return time_ms;
}
其中注释部分呢,是打色块测试,一个是测试lvgl的rgba,一个是测试坐标,跟前面fb测试色块一样
显示驱动部分disp_fb.c,如下
#include "disp_fb.h"
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
static struct fb_var_screeninfo var; /* screen var */
static struct fb_fix_screeninfo fix;
static uint16_t linewidth,pixelwidth;
static int disp_fb;
static uint8_t* dbuf = NULL;
void fb_init(void){
disp_fb = open(FBDEV_PATH,O_RDWR);
if(disp_fb>0){
printf("fb open succ\r\n");
if(!ioctl(disp_fb,FBIOGET_VSCREENINFO, (void*)&var)){
pixelwidth = var.bits_per_pixel/8;
linewidth = var.xres*pixelwidth;
printf("pixel:%d,lw:%d\r\n",pixelwidth,linewidth);
printf("Size: %dx%d,offset:(%d,%d)\r\n",var.xres,var.yres,var.xoffset,var.yoffset);
}
if(!ioctl(disp_fb,FBIOGET_FSCREENINFO,(void*)&fix)){
printf("lw:%d,m:%d\r\n",fix.line_length,fix.mmio_len);
}
fix.mmio_len = fix.line_length*var.yres;
dbuf = (uint8_t*)mmap(0,fix.mmio_len,PROT_READ|PROT_WRITE,MAP_SHARED,disp_fb,0);
if((intptr_t)dbuf == -1){
perror("dbuf err");
}
memset(dbuf,0,fix.mmio_len);
}
}
void fb_exit(void){
close(disp_fb);
disp_fb =0;
}
void fb_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p){
// uint16_t x=area->x1,y=area->y1;
if(dbuf==NULL || area->x1<0 || area->x2>var.xres-1 || area->y1<0 || area->y2>var.yres-1){
lv_disp_flush_ready(drv);
return;
}
int w = area->x2-area->x1+1;
for(int y=area->y1; y<=area->y2; y++){
memcpy((uint32_t*)(dbuf+(area->x1+var.xoffset)*pixelwidth+(y+var.yoffset)*linewidth),(uint32_t*)color_p,w*pixelwidth);
color_p+=w;
}
lv_disp_flush_ready(drv);
}
void fb_get_sizes(uint32_t *width, uint32_t *height){
*width = var.xres;
*height = var.yres;
}
void lcd_put_pixel(int x, int y, uint32_t color)
//传入的 color 表示颜色,它的格式永远是 0x00RRGGBB,即 RGB888。
//当 LCD 是 16bpp 时,要把 color 变量中的 R、 G、 B 抽出来再合并成 RGB565 格式。
{
unsigned char *pen_8 = dbuf+y*linewidth+x*pixelwidth;
//计算(x,y)坐标上像素对应的 Framebuffer 地址。
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
//对于 8bpp, color 就不再表示 RBG 三原色了,这涉及调色板的概念, color 是调色板的值。
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
// R5 G6 B5
//先从 color 变量中把 R、 G、 B 抽出来。
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
//把 red、 green、 blue 这三种 8 位颜色值,根据 RGB565 的格式,
//只保留 red 中的高 5 位、 green 中的高 6 位、 blue 中的高 5 位,
//组合成一个新的 16 位颜色值。
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
//把新的 16 位颜色值写入 Framebuffer
*pen_16 = color;
break;
}
case 32:
{
//对于 32bpp,颜色格式跟 color 参数一致,可以直接写入Framebuffer
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n",var.bits_per_pixel);
break;
}
}
}
void lcd_fill_rect(int x, int y, int w, int h, uint32_t color){
for(int i=0; i<h; i++){
for(int j=0; j<w; j++){
lcd_put_pixel(x+j,y+i,color);
}
}
}
上面flush部分呢,因为前面fb_init里读到的pixelwidth是4,所以用就直接u32
lcd_put_pixel就单纯测试下色块,验证rgpa和坐标,mcu的习惯,soc的别人怎么做,不清楚哈哈
实际上在调显示的时候,遇到点不是问题的问题,大意了,搞了点时间,
- 一个就是main.c里屏蔽那部分,我用memset来填充lv_color_t(因为full是32位的),我直接取的cc.full,结果导致显示异常,块花屏
- 另一个呢,是fb_flush里,用到fb的缓冲区dbuf,前面是uint_8*的,在fb_flush里忘记转了,导致显示异常,色块分离,启动时整体颜色正常,但有刷新呢,颜色分层,且位置不对,但没出现段错误哈哈,不过好在后面可以了
到这里,fb_flush刷色块也没问题,就可以加入test代码了
test.c代码如下:
void test(lv_indev_t* d){
base = lv_tabview_create(lv_scr_act(),LV_DIR_TOP, 80);
lv_obj_set_size(base,1920,1080);
lv_obj_set_pos(base,0,0);
tabs[0] = lv_tabview_add_tab(base,"Item1");
tabs[1] = lv_tabview_add_tab(base,"Item2");
tabs[2] = lv_tabview_add_tab(base,"Item3");
static lv_style_t style;
lv_style_init(&style);
lv_style_set_bg_opa(&style, LV_OPA_100);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 1));
lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER);
lv_style_set_text_color(&style, lv_color_white());
list = lv_list_create(tabs[0]);
lv_obj_set_size(list,200,400);
lv_obj_set_pos(list,0,0);
for(int i=0; i<6; i++){
char t[5] = {0};
sprintf(t,"T%d",i+1);
cur = lv_list_add_btn(list,NULL,t);
lv_obj_add_style(cur,&style,LV_STATE_FOCUSED);
}
lv_obj_add_state(cur,LV_STATE_FOCUSED);
curinput = d;
timer = lv_timer_create(checkkey,5,NULL);
}
void test_quit(){
if(base){
lv_obj_clean(base);
lv_obj_del(base);
}
}
上面呢,定时器后面给红外用的,我没用有导航模式,觉得不好用,如果要响应键多的话,就加不上了,这个后面红外按键时说
至于用tabview和list,容易做切换展示,哈哈
上面h文件简单,就不贴了,编译下包看看先
编译包开始测试
回到前面的shell,切换到lv_ir_test的根目录,执行mm
lv_ir_test$ mm
过个几分钟应该就可以了,编译后的可执行程序呢,在tina的根目录下,路径是
tina-d1-h/out/d1-h-nezha/compile_dir/target/lv_ir_test/ipkg-sunxi/lv_ir_test/usr/bin/lv_ir_test
将板子的adb加到虚拟机下,在tina根目录新建shell,执行sudo adb push out/d1-h-nezha/compile_dir/target/lv_ir_test/ipkg-sunxi/lv_ir_test/usr/bin/lv_ir_test .
执行成功,在串口终端执行ls,应该能看到lv_ir_test,执行./lv_ir_test,就可以看到上面的test的界面了,如图