显示屏按其显示原理大致可分为CRT(显像管)、LCD(液晶)及OLED三类,从市场应用看,手机中使用的显示屏主流是LCD,OLED只在翻盖机的小屏中占有少量份额,而CRT在手机中没有用到。
LCD本身不会发光,要想让其显示所要数据和图像,就必需使用白光背光源,手机中的白光背光源一般由数个侧发光白色LED灯组成,LED灯的个数由屏的大小尺寸决定,一般由2~6个不等。
【1】LCD 背光控制原理
在 fl2440开发板中,LCD 背光是通过CPU 的LCD_PWR 引脚来控制的,从原理图中可以看出,它对应于GPG4,如图
当LCD_PWR 输出为高电平“1”时,将打开背光;当输出为低电平“0”时,将关闭背光(注意:这里只是打开和关闭背光,而并没有背光亮度的调节作用)。
【2】 在内核中添加背光驱动程序
现在,我们需要增加一个简单的背光驱动,以便能够通过软件便可简单的控制背光的开关。我们要达到的目的是:在命令终端通过向背光设备发送偶数比如“0”便可关闭背光,发送奇数比如“1”便可打开背光,这样使用起来就方便多了,而不需要专门的应用程序控制它,正如在用户手册中所描述的方法(2.5.10 控制LCD 的背光):
提示:LCD 背光设备文件:/dev/backlight
在命令行种输入:echo 0 > /dev/backlight 可以关闭LCD 背光。
在命令行种输入:echo 1 > /dev/backlight 可以打开LCD 背光。
为了实现这点,我们在drivers/video/backlight 目录增加一个fl2440_backlight.c文件,内容如下:
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#include <mach/gpio.h>
#define DEVICE_NAME "backlight" //设备名称
#define DEVICE_MINOR 255 //次设备号自动申请,这里我们将设备注册为misc设备,这种设备的主设备号都为10
#define S3C2410_GPG4 S3C2410_GPG(4)
#define S3C2410_GPG4_OUTP (0x01 << 8)
static long fl2440_backlight_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd)
{
case 0:
//当接收的命令为0时,就将GPG4引脚设为低电平,关闭背光
s3c2410_gpio_setpin(S3C2410_GPG4, 0);
printk(DEVICE_NAME " turn off!\n");
return 0;
case 1:
//当接收的命令为1时,就将GPG4引脚设为高电平,开启背光
s3c2410_gpio_setpin(S3C2410_GPG4, 1);
printk(DEVICE_NAME " turn on!\n");
return 0;
default:
return -EINVAL;
}
}
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.unlocked_ioctl = fl2440_backlight_ioctl, //这里只使用控制IO口的方式来控制背光
};
static struct miscdevice misc =
{
.minor = DEVICE_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
ret = misc_register(&misc); //注册成misc设备
if(ret < 0)
{
printk("Register misc device fiald!");
return ret;
}
//将GPG4口配置成输出口,默认为点亮
s3c2410_gpio_cfgpin(S3C2410_GPG4, S3C2410_GPG4_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG4, 1);
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc); //注销该misc设备
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("smart.zhao");
MODULE_DESCRIPTION("Backlight control for fl2440");
【3】把背光配置选项加入内核配置菜单
打开drivers/video/backlight/Kconfig,定位到1929行附近,加入如下代码:
#
# Backlight & LCD drivers configuration
#
menuconfig BACKLIGHT_LCD_SUPPORT
bool "Backlight & LCD device support"
help
Enable this to be able to choose the drivers for controlling the
backlight and the LCD panel on some platforms, for example on PDAs.
if BACKLIGHT_LCD_SUPPORT
config BACKLIGHT_FL2440
tristate "Backlight support for FL2440 from FriendlyARM"
depends on BACKLIGHT_LCD_SUPPORT
help
backlight driver for FL2440 from FriendlyARM
#
# LCD
#
config LCD_CLASS_DEVICE
再打开 drivers/video/backlight/Makefile,定位到文件末尾,根据配置定义加入驱动目标文件,如下:
obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o
obj-$(CONFIG_BACKLIGHT_FL2440) += fl2440_backlight.o
【4】在内核配置菜单中确认 "Backlight support for mini2440 from FriendlyARM",被选中。
在内核源代码根目录执行:
make menuconfig,依次选择如下子菜单:
Device Drivers --->
Graphics support --->
[*] Backlight & LCD device support --->
就可以找到该配置选项,如图
![linux 3.1内核的驱动路(5)--添加LCD背光驱动 - Smart_zhao - 军惠博客](http://img3.ph.126.net/PxP6A9ny62EHZsBaVDe1NA==/627407723105185703.jpg)
在这里,如果没有被选中,按空格选中我们刚刚加入的mini2440 配置项,然后退出时保存内核配置菜单,在命令行执行:make uImage
将生成的arch/arm/boot/uImage复制到/nfsboot目录下,然后启动开发板,可以在启动时看到LCD屏显示的企鹅图像,这说明我们已经点亮了背光。
【5】编写测试程序linux-test/codetest/backlight_test.c,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
int turn;
int fd;
//检测命令后面带的参数
if(argc == 1 || argc > 2)
{
printf("Usage: backlight_test on|off!\n");
exit(1);
}
//打开背光设备
fd = open("/dev/backlight", O_RDWR);
if(fd < 0)
{
printf("Open Backlight Device Faild!\n");
exit(1);
}
//判断输入的参数
if(strcmp(argv[1], "on") == 0)
{
turn = 1;
}
else if(strcmp(argv[1], "off") == 0)
{
turn = 0;
}
else
{
printf("Usage: backlight_test on|off!\n");
exit(1);
}
//进行IO控制
ioctl(fd, turn);
//关闭背光设备
close(fd);
return 0;
}
编译程序,测试结果如下:
![linux 3.1内核的驱动路(5)--添加LCD背光驱动 - Smart_zhao - 军惠博客](http://img0.ph.126.net/-ISEQrvAsQ_yZiWJPKOXgg==/2758454771781568100.jpg)
ANDROID里,自带的背光亮度调节,会调用哪个接口?
会调用
/sys/class/leds/lcd-backlight/brightness
而这个brightness节点,是如何声明的呢
请看LED驱动里的这么一句
static DEVICE_ATTR(brightness, /*NAME*/
0644, /*权限*/
led_brightness_show, /*Read Point*/
led_brightness_store); /*Write Point*/
这就是声明brightness的。
这里调用了led_brightness_store做为写背光函数,也就是调节时用到,它最终会调用到显示驱动里的PWM操作函数,实现背光调节。
分析:
在LED驱动中,led_classdev_register ()函数,调用device_create()创建了操作结构体空间leds_class,
led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,"%s", led_cdev->name);
关键是填充led_cdev结构体。
在led_brightness_store函数里,调用led_set_brightness设置背光亮度,最终是调用了
led_cdev->brightness_set(led_cdev, value);实现操作,这个操作,会调用哪里的函数呢?它是显示驱动里的brightness_set()函数。
请注意看,在显示驱动里(S3cfb_lte480wv.c),有这么一段代码,
static struct led_classdev smdk_backlight_led = {
.name = "lcd-backlight",
.brightness = SMDK_DEFAULT_BACKLIGHT_BRIGHTNESS,
.brightness_set = smdk_brightness_set,
};
把brightness_set指向了smdk_brightness_set()函数。这就有戏看了,终于找到真正操作函数了。
当然,LED驱动是如何与显示驱动关联起来的,这一套机制,得看一下
static int smdk_bl_probe(struct platform_device *pdev)
{
led_classdev_register(&pdev->dev, &smdk_backlight_led);
return 0;
}
原来使用了led_classdev_register(),它是由LEDS驱动提供的函数,
#include 传过来的。
这就把smdk_backlight_led 结构体指针传给了LEDS驱动,里面就有smdk_brightness_set函数的指针,所以,LEDS里的led_cdev->brightness_set能调用到这边来,呵呵。原来是这样。
注册背光的控制节点
/sys/class/leds/lcd-backlight/brightness
是在这个函数 platform_driver_register() à
smdk_bl_probe() à led_classdev_register(&pdev->dev, &smdk_backlight_led);
在smdk_backlight_led结构体里,设置了节点名称lcd-backlight和控制接口等。
而led_classdev_register()是led-class里的函数。最终调用了device_createp 这样的函数实现创建节点。