当前位置: 首页 > news >正文

怎样写 Linux LCD 驱动程序

怎样写 Linux LCD 驱动程序
作者: 刘鹏
日期: 2008-12-27
本文分析了frame buffer 设备驱动的主要数据结构,在此基础上介绍了LCD驱动程序的开发。
<!-- Page -->

基本原理

  • 通过 framebuffer ,应用程序用 mmap 把显存映射到应用程序虚拟地址空间,将要显示的数据写入这个内存空间就可以在屏幕上显示出来;
  • 驱动程序分配系统内存作为显存;实现 file_operations 结构中的接口,为应用程序服务;实现 fb_ops 结构中的接口,控制和操作 LDC 控制器;
  • 驱动程序将显存的起始地址和长度传给 LCD 控制器的寄存器 (一般由 fb_set_var 完成) , LDC 控制器会自动的将显存中的数据显示在 LCD 屏上。

写 framebuffer 驱动程序要做什么

  • 简单的讲,framebuffer 驱动的功能就是分配一块内存作显存,然后对 LCD 控制器的寄存器作一些设置。
  • 具体来说:
    1. 填充一个 fbinfo 结构
    2. 用 reigster_framebuffer (fbinfo*) 将 fbinfo 结构注册到内核
  • 对于 fbinfo 结构,最主要的是它的 fs_ops 成员,需要针对具体设备实现 fs_ops 中的接口
  • 考虑是否使用中断处理
  • 考虑内存访问方式
    1. 显卡不自带显存的,分配系统内存作为显存
    2. 显卡自带显存的,用 I/O 内存接口进行访问 (request_mem_region / ioremap),
  • 关于如何写驱动的参考资料,在网站 http: /linux-fbdev.sourceforge.net/HOWTO/index.html 可以找到 "Linux Frame buffer Driver Writing HOWTO"

LCD 模块 \ 驱动程序 \ 控制器

关于LCD 设备资料可参考如下资料:

  • Datasheet of LCD device
  • 书:液晶显示技术
  • 书:液晶显示器件

什么是 frame buffer 设备

frame buffer 设备是图形硬件的抽象,它代表了图形硬件的侦缓冲区,允许应用程序通过指定的接口访问图形硬件。因此,应用程序不必关心底层硬件细节。

设备通过特定的设备节点访问,通常在 /dev 目录下,如 /dev/fb*。

更多关于 frame buffer device 的资料可以在以下两个文件中找到: linux /Documentation/fb/framebuffer.txt 和 linux /Documentation/fb /interal.txt,但这些资料内容不多,还需要看看结合代码具体分析。

Linux Frame Buffer 驱动程序层次结构

Frame Buffer 设备驱动可以从三个层次来看:

  1. 应用程序与系统调用;
  2. 适用于所有设备的通用代码,避免重复,包括 file_operations 结构、register/unregister framebuffer 接口等;
  3. 操作具体硬件的代码,主要是 fs_ops 结构。

在 Linux 内核中,Frame Buffer 设备驱动的源码主要在以下两个文件中,它们处于 frame buffer 驱动体系结构的中间层,它为上层的用户程序提供系统调用,也为底层特定硬件驱动提供了接口:

  1. linux/inlcude/fb.h
  2. linux/drivers/video/fbmem.c
数据结构

头文件 fb.h 定义了所有的数据结构:

  • fb_var_screeninfo:描述了一种显卡显示模式的所有信息,如宽、高、颜色深度等,不同的显示模式对应不同的信息;
  • fb_fix_screeninfo:定义了显卡信息,如 framebuffer 内存的起始地址,地址长度等;
  • fb_cmap:设备独立的 colormap 信息,可以通过 ioctl 的 FBIOGETCMAP 和 FBIOPUTCMAP 命令设置 colormap;
  • fb_info:包含当前 video card 的状态信息,只有 fb_info 对内核可见;
  • fb_ops : 应用程序使用 ioctl 系统调用操作底层的 LCD 硬件,fb_ops 结构中定义的方法用于支持这些操作;
  • 这些结构相互之间的关系如下所示:
framebuffer 驱动主要数据结构
接口

fbmem.c 实现了所有驱动使用的通用代码,避免了重复。

全局变量:

     struct fb_info *registered_fb [FB_MAX]
     int num_registered_fb;

这个两个变量用于记录正在使用的 fb_info 结构实例。fb_info 代表 video card 的当前状态,所有的 fb_info 结构都放在数组中。当一个 frame buffer 在内核中登记时,一个新的 fb_info 结构被加入该数组,num_registered_fb 加 1。

fb_drivers 数组:

static struct {
    const char *name;
    int (*init)(void);
    int (*setup)(void);
} fb_drivers[] __initdata= { ....};

若 frame buffer 驱动程序是静态链接到内核中,一个新的 entry 必须要加到这个表中。 若该驱动程序是使用 insmod/rmmod 动态加载到内核,则不必关心这个结构。

static struct file_operations fb_ops ={
    owner: THIS_MODULE,
    read: fb_read,
    write: fb_write,
    ioctl: fb_ioctl,
    mmap: fb_mmap,
    open: fb_open,
    release: fb_release
};

这是用户应用程序的接口,fbmem.c 实现了这些函数。

register/unregister framebuffer:

 register_framebuffer(struct fb_info *fb_info)
 unregister_framebuffer(struct fb_info *fb_info)

这是底层 frame buffer 设备驱动程序的接口。驱动程序使用这对函数实现注册和撤销操作。底层驱动程序的工作基本上是填充 fb_info 结构,然后注册它。

一个 LCD controller 驱动程序

实现一个 LCD controller 驱动程序主要做如下两步:

  • 分配系统内存作显存
  • 根据具体的硬件特性,实现 fb_ops 的接口
  • 在 linux/drivers/fb/skeletonfb.c 中有一个 frame buffer 驱动程序的框架,它示例了怎样用很少的代码实现一个 frame buffer 驱动程序。
分配系统内存作为显存

由于大多数 LDC controller 没有自己的显存,需要分配一块系统内存作为显存。这块系统内存的起始地址和长度之后会被存放在 fb_fix_screeninfo 的 smem_start 和 smem_len 域中。该内存应该是物理上连续的。

对于带独立显存的显卡,使用 request_mem_region 和 ioremap 将显卡外设内存映射到处理器虚拟地址空间。

实现 fb_ops 结构

目前还没有讨论的 file_operations 方法是 ioctl ()。用户应用程序使用 ioctrl 系统调用操作 LCD 硬件。fb_ops 结构中定义的方法为这些操作提供支持。注意, fb_ops 结构不是 file_operations 结构。fb_ops 是底层操作的抽象,而 file_operations 为上层系统调用接口提供支持。

下面考虑需要实现哪些方法。ioctl 命令和 fb_ops 结构中的接口之间的关系如下所示:

    FBIOGET_VSCREENINFO fb_get_var
    FBIOPUT_VSCREENINFO fb_set_var
    FBIOGET_FSCREENINFO fb_get_fix
    FBIOPUTCMAP fb_set_cmap
    FBIOGETCMAP fb_get_cmap
    FBIOPAN_DISPLAY fb_pan_display

只要我们实现了那些 fb_XXX 函数,那么用户应用程序就可以使用 FBIOXXXX 宏来操作 LDC 硬件了。那怎么实现那些接口呢?可以参考下 linux/drivers/video 目录下的驱动程序。

在众多接口中, fb_set_var 是最重要的。它用于设置 video mode 等信息。下面是实现 fb_set_var 函数的通用步骤:

  1. 检查是否有必要设置 mode
  2. 设置 mode
  3. 设置 colormap
  4. 根据上面的设置重新配置 LCD controller 寄存器

其中第四步是底层硬件操作。

相关文章:

  • LeetCode -- 帕斯卡三角形
  • SQL2005CLR函数扩展-正则表达式
  • LeetCode - Merge Intervals
  • LeetCode -- Three Sum
  • SQL2005CLR函数扩展-字符串函数
  • 算法面试题-- 连接树的所有兄弟节点
  • 怀念穆大叔
  • LeetCode -- Flatten 二叉树
  • [IE编程] WebBrowser控件的多页面浏览(Tabbed Browsing)开发接口
  • LeetCode -- 查找最小公共祖先
  • 8位程序员对Oracle收购Sun的担忧与期待
  • LeetCode -- 顺时针旋转图片90度
  • LeetCode -- Path Sum ||
  • 35岁IT“老人”的随笔
  • LeetCode -- Decode Ways
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • git 常用命令
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • Java到底能干嘛?
  • mysql外键的使用
  • vuex 笔记整理
  • vue数据传递--我有特殊的实现技巧
  • 从零开始的无人驾驶 1
  • 对超线程几个不同角度的解释
  • 精彩代码 vue.js
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 前端路由实现-history
  • 前端之Sass/Scss实战笔记
  • 线性表及其算法(java实现)
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (一)插入排序
  • (译) 函数式 JS #1:简介
  • (转)C#调用WebService 基础
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • **PHP分步表单提交思路(分页表单提交)
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET 的静态构造函数是否线程安全?答案是肯定的!
  • .net访问oracle数据库性能问题
  • .sys文件乱码_python vscode输出乱码
  • @JsonFormat与@DateTimeFormat注解的使用
  • @RequestParam,@RequestBody和@PathVariable 区别
  • [2021 蓝帽杯] One Pointer PHP
  • [boost]使用boost::function和boost::bind产生的down机一例
  • [BUG]vscode插件live server无法自动打开浏览器
  • [BZOJ 2142]礼物(扩展Lucas定理)
  • [C++] cout、wcout无法正常输出中文字符问题的深入调查(1):各种编译器测试
  • [C++]AVL树怎么转
  • [C++]模板与STL简介
  • [CSS]浮动