本文接着上一篇 LVGL linux arm平台上的详细移植过程 开始讲解驱动部分和main.c demo部分的移植和编写。
一、显示驱动和触摸屏驱动添加
驱动部分我们需要将 src\lvgl\examples\porting 底下的这几个文件
src
├── lvgl
├── examples
└── porting
├── lv_port_disp_template.c
├── lv_port_disp_template.h
├── lv_port_indev_template.c
└── lv_port_indev_template.h
重命名后放置到 src\lv_drivers 目录下
└── src
├── lv_drivers
├── lv_port_disp.c #9
├── lv_port_disp.h #10
├── lv_port_indev.c #11
└── lv_port_indev.h #12
1.1 显示驱动
#9 lv_port_disp.c 显示相关实现 lv_port_disp_init 里的disp_init实现及修改,修改见下面中文注释部分;可以参考lv_drivers\display\fbdev.c 中通用的linux fb初始化及绘图过程。我们以rgb565 为例。 lv_port_disp.h 中 extern void lv_port_disp_init(void);导出初始化函数接口。
void lv_port_disp_init(void)
{
disp_init();
static lv_disp_buf_t draw_buf_dsc_3;
static lv_color_t draw_buf_3_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];
static lv_color_t draw_buf_3_2[LV_HOR_RES_MAX * LV_VER_RES_MAX];
lv_disp_buf_init(&draw_buf_dsc_3, draw_buf_3_1, draw_buf_3_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = 1024;
disp_drv.ver_res = 600;
disp_drv.flush_cb = disp_flush;
disp_drv.buffer = &draw_buf_dsc_3;
#if LV_USE_GPU
disp_drv.gpu_blend_cb = gpu_blend;
disp_drv.gpu_fill_cb = gpu_fill;
#endif
lv_disp_drv_register(&disp_drv);
}
打开及初始化framebuffer
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static char *fbp = 0;
static long int screensize = 0;
static int fbfd = 0;
static void disp_init(void)
{
fbfd = open(FBDEV_PATH, O_RDWR);
if(fbfd == -1) {
perror("Error: cannot open framebuffer device");
return;
}
printf("The framebuffer device was opened successfully.\n");
if(ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
perror("Error reading fixed information");
return;
}
if(ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
perror("Error reading variable information");
return;
}
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
screensize = finfo.smem_len;
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
if((intptr_t)fbp == -1) {
perror("Error: failed to map framebuffer device to memory");
return;
}
memset(fbp, 0, screensize);
printf("The framebuffer device was mapped to memory successfully.\n");
}
实现画图函数:
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(fbp == NULL ||
area->x2 < 0 ||
area->y2 < 0 ||
area->x1 > (int32_t)vinfo.xres - 1 ||
area->y1 > (int32_t)vinfo.yres - 1) {
lv_disp_flush_ready(disp_drv);
return;
}
int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
int32_t act_x2 = area->x2 > (int32_t)vinfo.xres - 1 ? (int32_t)vinfo.xres - 1 : area->x2;
int32_t act_y2 = area->y2 > (int32_t)vinfo.yres - 1 ? (int32_t)vinfo.yres - 1 : area->y2;
lv_coord_t w = (act_x2 - act_x1 + 1);
long int location = 0;
long int byte_location = 0;
unsigned char bit_location = 0;
if(vinfo.bits_per_pixel == 32 || vinfo.bits_per_pixel == 24) {
uint32_t * fbp32 = (uint32_t *)fbp;
int32_t y;
for(y = act_y1; y <= act_y2; y++) {
location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 4;
memcpy(&fbp32[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 4);
color_p += w;
}
}
else if(vinfo.bits_per_pixel == 16) {
uint16_t * fbp16 = (uint16_t *)fbp;
int32_t y;
for(y = act_y1; y <= act_y2; y++) {
location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 2;
memcpy(&fbp16[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 2);
color_p += w;
}
}
else if(vinfo.bits_per_pixel == 8) {
uint8_t * fbp8 = (uint8_t *)fbp;
int32_t y;
for(y = act_y1; y <= act_y2; y++) {
location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length;
memcpy(&fbp8[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1));
color_p += w;
}
}
else if(vinfo.bits_per_pixel == 1) {
uint8_t * fbp8 = (uint8_t *)fbp;
int32_t x;
int32_t y;
for(y = act_y1; y <= act_y2; y++) {
for(x = act_x1; x <= act_x2; x++) {
location = (x + vinfo.xoffset) + (y + vinfo.yoffset) * vinfo.xres;
byte_location = location / 8;
bit_location = location % 8;
fbp8[byte_location] &= ~(((uint8_t)(1)) << bit_location);
fbp8[byte_location] |= ((uint8_t)(color_p->full)) << bit_location;
color_p++;
}
color_p += area->x2 - act_x2;
}
} else {
}
lv_disp_flush_ready(disp_drv);
}
#10 lv_port_disp.h 导出初始化函数main.c中需要先初始化
extern void lv_port_disp_init(void);
1.2 触摸屏驱动
在讲解触摸屏驱动前请先移植好tslib 触摸屏的坐标事件读取将采用tslib的接口 ,根据上篇将so和头文件放到相应的位置。
#11 lv_port_indev.c
#include "lv_port_indev.h"
#include "../lv_drv_conf.h"
#include "../tslib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/kd.h>
#include <linux/input.h>
#include <time.h>
static struct tsdev *ts = NULL;
void lv_port_indev_init(void)
{
lv_indev_drv_t indev_drv;
touchpad_init();
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
static void touchpad_init(void)
{
ts = ts_setup(NULL, 1);
if (!ts) {
perror("ts_open");
exit(1);
}
}
实现触摸屏事件的读取
static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
struct ts_sample samp;
int ret;
ret = ts_read(ts, &samp, 1);
if (ret < 0) {
perror("ts_read");
ts_close(ts);
exit(1);
}
if (ret != 1)
return true;
if (samp.pressure > 0) {
data->state = LV_INDEV_STATE_PR;
}
else{
data->state = LV_INDEV_STATE_REL;
}
data->point.x = samp.x;
data->point.y = samp.y;
return false;
}
#12 lv_port_indev.h 导出初始化函数
extern void lv_port_indev_init(void);
二、演示demo导入main.c的编写
#include "lvgl.h"
#include <stdio.h>
#include "lv_port_disp.h"
#include "lv_port_indev.h"
#include "lv_demo_widgets.h"
void lvgl_first_demo_start(void);
int main(void)
{
lv_init();
lv_port_disp_init();
lv_port_indev_init();
lv_demo_widgets();
printf("start demo \n");
while(1)
{
lv_task_handler();
}
}
void tim_lv_tick()
{
lv_tick_inc(1);
}
static void btn_event_cb(lv_obj_t * btn, lv_event_t event)
{
if(event == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;
lv_obj_t * label = lv_obj_get_child(btn, NULL);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}
void lvgl_first_demo_start(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL);
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_set_event_cb(btn, btn_event_cb);
lv_obj_t * label = lv_label_create(btn, NULL);
lv_label_set_text(label, "Button");
lv_obj_t * label1 = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(label1, "Hello world!");
lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_align(btn, label1, LV_ALIGN_OUT_TOP_MID, 0, -10);
}
main.c函数的作用就是编译出来看效果的,在这里要包含驱动的#include “lv_port_disp.h” #include “lv_port_indev.h” 两个头文件并初始化驱动。lv_demos如何使用呢? 1、打开lv_ex_conf.h
#define LV_USE_DEMO_WIDGETS 1
2、包含你要运行的lv_demos的相应demo的头文件#include “lv_demo_widgets.h” 3、在完成初始化后调用demo入口函数lv_demo_widgets(); 由于前面我们已经用cmake将demo代码编译成为库,故这里直接引用即可。 4、最后记得在src 的CMakeLists.txt下添加include_directories(./lv_demos/src/lv_demo_widgets/) #设置引用demo头文件路径不然会报找不到头文件的错误。
三、自定义tick
然后还有比较重要的systick的实现
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM == 1
#define LV_TICK_CUSTOM_INCLUDE "../../../sys_tick.h"
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get())
#endif
#13 sys_tick.c
#include "sys_tick.h"
#include <sys/time.h>
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;
}
#14 sys_tick.h
#ifndef __SYS_TICK__H
#define __SYS_TICK__H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
extern uint32_t custom_tick_get(void);
#endif
分析tick的使用: 在main中while 1 里面lv_task_handler 这里是处理tasklist的循环
uint32_t handler_start = lv_tick_get();
return LV_TICK_CUSTOM_SYS_TIME_EXPR;
LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get())
四、编译
好了现在工程已经移植完成,接下来去到工程根目录下
cd lvgl_prj cmake . //这里有个 . 哈,表示当前文件这步之后会生成Makefile make
编译。关于错误,一般都是头文件包含路径的错误,你可以加 …/lvgl/lvgl.h 以定位到src目录再索引到lvgl的头文件,或者你可以在cmakelists.txt中去导出这个头文件路径。 关于字体宏的一些错误,请根据错误相应的打开lv_conf.h 中的 #define LV_FONT_MONTSERRAT_10 1 #define LV_FONT_MONTSERRAT_12 1 以消除错误。 至此,lvgl的移植已完成。
Enjoy The LVGL For Arm Linux!
|