2015年(4)
分类: LINUX
2015-03-23 16:36:44
原文地址:Mini2440 LCD 点亮 作者:wangjianchangdx
启动的过程中,LCD变亮,并且如果console配置了tty0的话,可以看到LCD上,会有启动信息打印。然而随即LCD熄灭。此时按住复位键不放,发现LCD变量,其上的LOGO也显示出来:这说明了是背光的问题。
代码中,关于LCD的位置,最明显的在于,mini2440_init函数了。其中:
static void __init mini2440_init(void)
{
struct mini2440_features_t features = { 0 };
int i;
printk(KERN_INFO "MINI2440: Option string mini2440=%s\n",
mini2440_features_str);
/* Parse the feature string */
mini2440_parse_features(&features, mini2440_features_str); 默认的特性为”0tb”,其中’b’代表backlight,打开背光
/* turn LCD on */
s3c_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND); 配置GPIO,开启LCD
/* Turn the backlight early on */
WARN_ON(gpio_request(S3C2410_GPG(4), "backlight"));
gpio_direction_output(S3C2410_GPG(4), 1); 配置GPB4,也就是屏幕背光
/* remove pullup on optional PWM backlight -- unused on 3.5 and 7"s */ 这段注释看不懂,不过下面的代码,把GPB1,配置成了输入口,
s3c_gpio_setpull(S3C2410_GPB(1), S3C_GPIO_PULL_UP); 而非PWM timer1输出口。
s3c2410_gpio_setpin(S3C2410_GPB(1), 0);
s3c_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_INPUT);
/* Make sure the D+ pullup pin is output */
WARN_ON(gpio_request(S3C2410_GPC(5), "udc pup"));
gpio_direction_output(S3C2410_GPC(5), 0);
/* mark the key as input, without pullups (there is one on the board) */
for (i = 0; i < ARRAY_SIZE(mini2440_buttons); i++) {
s3c_gpio_setpull(mini2440_buttons[i].gpio, S3C_GPIO_PULL_UP);
s3c_gpio_cfgpin(mini2440_buttons[i].gpio, S3C2410_GPIO_INPUT);
}
if (features.lcd_index != -1) { 初始化LCD
int li;
mini2440_fb_info.displays =
&mini2440_lcd_cfg[features.lcd_index];
printk(KERN_INFO "MINI2440: LCD");
for (li = 0; li < ARRAY_SIZE(mini2440_lcd_cfg); li++)
if (li == features.lcd_index)
printk(" [%d:%dx%d]", li,
mini2440_lcd_cfg[li].width,
mini2440_lcd_cfg[li].height);
else
printk(" %d:%dx%d", li,
mini2440_lcd_cfg[li].width,
mini2440_lcd_cfg[li].height);
printk("\n");
s3c24xx_fb_set_platdata(&mini2440_fb_info);
}
s3c24xx_udc_set_platdata(&mini2440_udc_cfg);
s3c24xx_mci_set_platdata(&mini2440_mmc_cfg);
s3c_nand_set_platdata(&mini2440_nand_info);
s3c_i2c0_set_platdata(NULL);
i2c_register_board_info(0, mini2440_i2c_devs,
ARRAY_SIZE(mini2440_i2c_devs));
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
if (features.count) /* the optional features */
platform_add_devices(features.optional, features.count);
}
事实证明,LCD的配置是没有问题的,所以将焦点集中在背光之上。从上述代码的注释,以及在网上查询的资料中,都提到了PWM Backlight,于是乎搜寻PWM Backlight的资料;另外,检查配置,发现PWM Backlight配置正常,于是乎从代码入手,查看背光、PWM相关模块的代码。
第一个,对PWM背光的理解:
PWM控制LCD背光,LCD背光源有CCFL和LED。
PWM与LCD的关系并不大,PWM控制LED,只不过这个LED充当背光源的角色罢了。
PWM输出控制LED的亮暗程度原理?
利用改51单片机的定时器PWM输出控制LED灯的亮暗程度,通过一个参数调节其占空比,比如第一次0/10不亮;1/9 很暗;第二次2/8.....,第九次9/1很亮;第十次,10/0完全亮。 小弟不明白的是:LED的亮暗由其电流和电压决定,所以1/9时的电压比9/1的电压电流高,但是这个电压或者电流怎么计算的了?
1/9不是只是代表其高低电平的时间之比吗? 怎么与LED的电压联系起来呢?
把PWM的一个周期分成10块。
你说的1/9的意思是,这10块时间里,只有1块的时间让LED导通,剩下9块的时间里LED截止。假设PWM信号的幅值是5V,那么在导通的1块时间里LED电压的确是5V的,但是在剩下9块时间里,它的电压却是0V。也就是说从一个周期整体看来,LED的平均电压只有5*0.1+0*0.9=0.5V。
PWM信号频率很高的,我们无法通过肉眼来观察到每一个周期LED灯亮灭的变化过程,所以只好通过平均电压这样一种方式来决定这个LED的亮的程度了。
第二个,LCD相关的驱动代码在drivers/video,背光相关的在drivers/video/backlight。观察产生的.o文件,发现被编译了的有backlight.c/lcd.c/platform_lcd.c/pwm_bl.c。
File |
Init |
description |
Lcd.c |
postcore_initcall |
在sysfs下创建lcd目录,另外,还有lcd_device_regist等函数 |
Platform_lcd.c |
module_init |
注册LCD driver |
Backlight.c |
postcore_initcall |
在sysfs下创建backlight目录,其中还有背光设备注册函数、亮度的调整函数 |
Pwm_bl.c |
module_init |
注册pwm backlight驱动 |
查看postcore_initcall(include/linux/init.h)的定义:
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
#define __initcall(fn) device_initcall(fn)
******
#define module_init(x) __initcall(x);
按顺序,postcore_initcall定义的函数,被存放在使用module_init定义的函数之前。
查看内核链接时生成的System.map文件,
__initcall_start
********
__early_initcall_end
********
__initcall_consistent_init1
__initcall_s3c2442_core_init1
__initcall_s3c2440_core_init1
__initcall_s3c24xx_gpiolib_init1
__initcall_s3c24xx_dma_sysclass_init1
__initcall_sysctl_init1
__initcall_ksysfs_init1
__initcall_async_init1
********
__initcall_lcd_class_init2
********
__initcall_pwm_backlight_init6
这些都会在start_kernel->rest_init->kernel_init->do_basic_setup-> do_initcalls,其中按顺序调用__initcall_start, __initcall_end之间的init函数。
综上,并没有发现对backlight的初始化配置部分。
再搜寻PWM配置部分,在arch/arm/plat-samsung/pwm.c中,有arch_initcall(pwm_init);定义的pwm_init函数。其中有对pwm driver的注册,驱动的probe函数中,有对pwm相关寄存器的配置。
返回mini2440_init函数,该函数被定义在mach_desc数据结构中。在start_kernel->setup_arch中,有init_machine = mdesc->init_machine; 在全代码树中搜索”init_machine”发现,就在setu_arch函数上,有一个customize_machine函数,其中调用init_machine函数,而customize_machine是使用arch_initcall(customize_machine);定义的。
现在解释另外一个问题,关于probe函数的调用时机问题,在各模块的init函数中,只是注册sys文件系统或注册驱动数据结构,绝非调用probe函数。跟踪platform_drive_register函数发现,在其中有对autoprobe的判断,如果是autoprobe的话,就会便利platform_bus上已挂载的设备链表,亦即调用probe函数。注册设备时,亦是如此。
由以上分析可知:
① pwm已被配置,backlight没被配置
② Mini2440_init在pwm_bl的init函数之前被调用
③ Backlight被注册在sys目录下,也证实了网上搜集的资料
尝试着,修改mini2440_init中GPB1的配置,将其修改为pwm timer1的输出。
/* remove pullup on optional PWM backlight -- unused on 3.5 and 7"s */
/*s3c_gpio_setpull(S3C2410_GPB(1), S3C_GPIO_PULL_UP);
trace();
s3c2410_gpio_setpin(S3C2410_GPB(1), 0);
trace();
s3c_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_INPUT);*/
s3c_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPB1_TOUT1);
编译,烧写,运行
echo 1 > /sys/class/leds/backlight/brightness
LCD被点亮