lxiaogao

linux 支持spi及cpu屏framebuffer驱动的编写

手里有一块开发板,但显示控制器对应的引脚并未引出如lcd lvds,只预留了几个gpio及复用的spi接口,脑洞大开,可以类似UCGUI驱动spi屏或cpu屏(只是没用控制器显存硬件支持估计fps会比较低且fbcopy比较占cpu吧)。

搜了一下开源硬件 发现树莓派已经做了相同的工作了,而且集合了大部分tft屏iic显示芯片,好,芯片手册都不用读了。

提到树莓派,我这里提一下国内的智能开源硬件芯片提供商:全志科技,真良心,bsp代码是我看过写的最漂亮的(在公司看高通bsp代码,靠,各种宏各种回调。)另外全志提供的documents是最全面的包括各种linux驱动编写移植,及OS的DIY。而且全是中文的,方便英文不好及入门的嵌入式开发爱好者。

具体开源工程:

notro/fbtft,完整的实现了framebuffer驱动,让树莓派完美支持tft液晶,下面对移植过程进行一个简单说明

工程首页:https://github.com/notro
fbtft源码:https://github.com/notro/fbtft
编译好的固件(基于3.12.25+):https://github.com/notro/rpi-firmware

使用说明(wiki):https://github.com/notro/fbtft/wiki

好吧,那我就借鉴一下他的思想。

分析它的源码发现大致思想和我想的一样,只是我惊奇地发现linux3.5已经提供好相应定时异步刷新接口了。

总的思想是先开辟一段buffer,定时通过spi或i8080或uart刷新到屏幕。

具体接口如下:

著名的struct fb_info 里

struct fb_info {

....

#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;//不错这就是定时刷新的延时工作队列
struct fb_deferred_io *fbdefio;
#endif

....

}

static void fb_deferred_io_work(struct work_struct *work)
{
struct fb_info *info = container_of(work, struct fb_info,
deferred_work.work);
struct list_head *node, *next;
struct page *cur;
struct fb_deferred_io *fbdefio = info->fbdefio;

/* here we mkclean the pages, then do all deferred IO */
mutex_lock(&fbdefio->lock);
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
lock_page(cur);
page_mkclean(cur);
unlock_page(cur);
}

/* driver's callback with pagelist */
fbdefio->deferred_io(info, &fbdefio->pagelist);

/* clear the list */
list_for_each_safe(node, next, &fbdefio->pagelist) {
list_del(node);
}
mutex_unlock(&fbdefio->lock);
}

void fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;

BUG_ON(!fbdefio);
mutex_init(&fbdefio->lock);
info->fbops->fb_mmap = fb_deferred_io_mmap;
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
INIT_LIST_HEAD(&fbdefio->pagelist);
if (fbdefio->delay == 0) /* set a default of 1 s */
fbdefio->delay = HZ;
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);

现在我大概分析一下fbtft源码:

一 .实现向tft屏幕写图像数据的函数是write_vmem()

根据具体硬件总线带宽具体回调函数是

/* 16 bit pixel over 8-bit databus */

int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)

  二.定时刷新屏幕实现代码

标准frambuffer操作api->mkdirty->fbtft_mkdirty()->schedule_delayed_work(&info->deferred_work, fbdefio->delay);->deferred_io()->fbtft_deferred_io()->update_display()->fbtft_update_display()->write_vmem()->fbtft_write_vmem16_bus8()

故只要定期执行deferred_io()即可完成FPS。哪里实现定时呢?

别急,还记得之前说的linux已经实现定时异步刷新接口吗?

接着分析:

首先

struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,struct device *dev)

{

。。。。。

fb_deferred_io_init(info);//建立延定时工作队列 

。。。。。

}
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);-> fb_deferred_io_work(struct work_struct *work)->fbdefio->deferred_io(info, &fbdefio->pagelist);//回调执行屏幕update函数了吧-->void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)

评论