Linux下LCD 10分钟自动关屏的问题总结

来源:互联网 发布:数据分析的方法有哪些 编辑:程序博客网 时间:2024/05/16 04:45

Linux下的LCD驱动默认10分钟后会自动关闭屏幕,我们可以修改一下代码让其不自动关屏

在有一个 drivers/char/vt.c 文件其中有一个变量(blankinterval)可以设置它来修改自动关屏的时间,也可以在函数(blank_screen_t)开头直接返回,这样就永远不会关屏了


  在用LCD显示的时候,发现10分钟LCD就不再工作了。确实,Linux下有一个参数blankinterval的值就是10*60*Hz。

它决定了LCD只显示10分钟,然后LCD控制器就被关掉了。

具体细节请参考http://blog.csdn.net/dongliqiang2006/article/details/4262950

但是这篇文章最后给出的程序是不能用的,有一处错误,在write那行应该是\0,原文是/0。

#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
int main(int argc ,char *argv[])
{
 int f0;
 f0 = open("/dev/tty0", O_RDWR);
 write(f0, "\033[9;0]", 8);
 close(f0);
 return 0;
}

1.程序存为display_time.c
2 交叉编译 arm-linux-gcc -o display_time  display_time.c
3.display_time复制到根目录下
4 运行  ./display_time
如果要开机自动启动,复制到根目录后,在/etc/init.d/rcS里加上一句  /display_time 。
原理请看下一篇博文Linux驱动和应用程序的开机自启动


Linux启动之后,只要一段时间不动键盘(开发板上用IO扩展出来的键盘),LCD就会自动关闭(黑屏、显示慢慢消失之类),只要按下键盘就能恢复。
 
这个问题让我花了一天多的时间。其实如果是手持设备,这样也没有什么。但是我们公司的产品是要一直显示东西的,必须解决这个问题。我看了很多论 坛,有不少人也遇到了这个问题,但是我刚才是搜索的时候,关键词不对,总找不到正确的答案。如果你遇到了同样的问题,而且不想看我的三脚猫分析,那么就在 百度上搜索“blankinterval”、“setterm -blank 0”之类的,马上你就能找到简单的解决方案。
 
这个问题很容易让人想到屏幕保护和电源管理。的确,这是一种电源管理。但是,你却无法从Linux内核选项的电源管理中解决这个问题。我们一步步来。
 
首先,我测量到LCD的PCLK时钟消失了,这意味着内核把LCD控制器关掉了。于是,从LCD驱动程序着手。我用的是S3C2440,这是 2410的升级版,但是LCD控制器是一样的,在我拿到的开发板厂商给我做好驱动的内核里,驱动的位置在/drivers/video /s3c2410fb.c。为什么后面有个fb呢?这是Framebuffer的缩写,百度下你能找到很多关于它的解释。Framebuffer是所有 Linux下GUI程序对硬件操作的设备接口,位于/dev中,一般为fb0。在s3c2410fb.c中可以找到一个类似 s3c2410_disable_controller()这样名称的函数,我的驱动里叫pxafb_disable_controller(),可以看 出这个驱动是从pxa处理器改的,当然厂家不一样名字也叫得不一样。里面有一句类似这样写的 __raw_writel(fbi->reg.lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);,把这句话删掉LCD就不会关掉了。这是第一个层次,我也看到有人是这样做的。但是,这有问题,按键盘恢复后,原本显 示在屏幕上的东西如果你不重画会消失,就算你重画了,也会看到屏幕的某些部分先黑了下,然后恢复了。当然如果你可以接受,那么就这样吧。
 
然后,可以很自然的想到是谁调用了这个函数,从源头把这个问题消除掉。但是情况却不是这样的。我搜索这个函数名,找到了一个 set_ctrlr_state()的函数调用了pxafb_disable_controller(),搜索set_ctrlr_state(),发现 有个pxafb_task()调用了set_ctrlr_state(),但是到了pxafb_task()就没有办法再往上找了,因为这是提供给内核的 一个任务,以指针传递函数入口。我对内核了解太不够了,花了很多时间看了很多论坛上的文章,机缘巧合之下,我找到了/drivers/char/vt.c 这个文件。vt.c我感觉应该是2.4内核的console.c和vt.c的结合体,应为它集成了console基本上所有功能函数,就ioctl在 vt_ioctl.c这个文件里。这个文件的主要作用是负责管理控制台,如控制台的模式(图形、字符)、向控制台输出等等。其中能找到一些如 do_blank_screen(),blank_screen_t()这样的函数,就是这些函数关闭了LCD控制器,修改任意一个都可以起作用。网上的 一个解决方案是把blank_screen_t()变成空函数,但是我没有这样试过,我觉得已经来到了问题的根源附近,应该能从根本上解决。
 
我们先看下屏幕关闭问题的真正起因,看这个控制台初始化函数
static int __init con_init(void)
{
 const char *display_desc = NULL;
 struct vc_data *vc;
 unsigned int currcons = 0;
 acquire_console_sem();
 if (conswitchp)
  display_desc = conswitchp->con_startup();
 if (!display_desc) {
  fg_console = 0;
  release_console_sem();
  return 0;
 }
 init_timer(&console_timer);
 console_timer.function = blank_screen_t;
 
 if (blankinterval) {
  blank_state = blank_normal_wait;
  mod_timer(&console_timer, jiffies + blankinterval);
 }
// 这是对控制台定时器的初始化,定时器事件函数被连接到了blank_screen_t()
 /*
  * kmalloc is not running yet - we use the bootmem allocator.
  */
 for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
  vc_cons[currcons].d = vc = alloc_bootmem(sizeof(struct vc_data));
  visual_init(vc, currcons, 1);
  vc->vc_screenbuf = (unsigned short *)alloc_bootmem(vc->vc_screenbuf_size);
  vc->vc_kmalloced = 0;
  vc_init(vc, vc->vc_rows, vc->vc_cols,
   currcons || !vc->vc_sw->con_save_screen);
 }
 currcons = fg_console = 0;
 master_display_fg = vc = vc_cons[currcons].d;
 set_origin(vc);
 save_screen(vc);
 gotoxy(vc, vc->vc_x, vc->vc_y);
 csi_J(vc, 0);
 update_screen(vc);
 printk("Console: %s %s %dx%d",
  vc->vc_can_do_color ? "colour" : "mono",
  display_desc, vc->vc_cols, vc->vc_rows);
 printable = 1;
 printk("/n");
 release_console_sem();
#ifdef CONFIG_VT_CONSOLE
 register_console(&vt_console_driver);
#endif
 return 0;
}

其中引用了一个叫blankinterval的全局变量和一个console_time,我不知道内核的定时器是具体是怎么工作,但是 这样的代码已经很明显了。这个定时器和电源管理宏PM_CONFIG没有任何关系,它是控制台的一部分。再看下blank_screen_t():
 
static void blank_screen_t(unsigned long dummy)
{
 blank_timer_expired = 1;
 schedule_work(&console_work);
}
 
可以发现vt.c开头的宏,static DECLARE_WORK(console_work, console_callback, NULL);,找到了console_callback()这个函数:
 
static void console_callback(void *ignored)
{
 acquire_console_sem();
 if (want_console >= 0) {
  if (want_console != fg_console &&
      vc_cons_allocated(want_console)) {
   hide_cursor(vc_cons[fg_console].d);
   change_console(vc_cons[want_console].d);
   /* we only changed when the console had already
      been allocated - a new console is not created
      in an interrupt routine */
  }
  want_console = -1;
 }
 if (do_poke_blanked_console) { /* do not unblank for a LED change */
  do_poke_blanked_console = 0;
  poke_blanked_console();
 }
 if (scrollback_delta) {
  struct vc_data *vc = vc_cons[fg_console].d;
  clear_selection();
  if (vc->vc_mode == KD_TEXT)
   vc->vc_sw->con_scrolldelta(vc, scrollback_delta);
  scrollback_delta = 0;
 }
 if (blank_timer_expired) {
  do_blank_screen(0);
  blank_timer_expired = 0;
 }
 release_console_sem();
}
 
再看do_blank_screen(),随着struct vc_data中的与fops类似指针跟踪下去,就可以找到驱动里面的相应代码了。写出来太麻烦,让我偷懒把。
 
小总结下,其实在控制台内部就有一个定时器,它负责在一定时间之后将显示关闭,而无视是否打开了电源管理功能。那这和Framebuffer有什么关系呢?我从一个很弱智的角度解释,内核刚启动的时候有这样一句输出:
Console: colour dummy device 80x30
在初始化LCD控制器(Framebuffer)之后,有这样一句输出:
Console: switching to colour frame buffer device 80x30
我就理解:这时候,内核把控制台(也不知道是console还是tty)切换到了Framebuffer上,大虾们赶快跳出来批判我吧,呵呵。
 
回到正题,从代码可以发现,根本的解决之道是让blankinterval = 0,blank_state就不会是blank_off之外的值,也就不会关闭屏幕了。
但是问题到这里还是没有完全解决,如果用户程序希望改变blankinterval来实现屏保(当然在我的系统上用不着);另外,一些程序改变 了blankinterval,程序退出之后,屏幕在一段时间之后还是会关闭的。怎么才能在用户程序那头解决这个问题呢,这又耗费了我很多时间。
 
我在追查代码的过程中走了个弯路,认为修改控制台的模式可以不让黑屏现象出现,但是后来发现,这样可能会使控制台没有办法画图,不知道对不对。
忽略弯路,直接正解。vt.c中不是有很多操作控制台的函数么?看看是谁修改了blankinterval。于是搜索 blankinterval,发现setterm_command()修改了它,然后搜索setterm_command,找到了 do_con_trol()函数,搜索do_con_trol,找到了do_con_write()函数,搜索do_con_write,终于最终 BOSS现身了:con_write()函数。为什么说它是最终BOSS呢?看看这段:
static struct tty_operations con_ops = {
 .open = con_open,
 .close = con_close,
 .write = con_write,
 .write_room = con_write_room,
 .put_char = con_put_char,
 .flush_chars = con_flush_chars,
 .chars_in_buffer = con_chars_in_buffer,
 .ioctl = vt_ioctl,
 .stop = con_stop,
 .start = con_start,
 .throttle = con_throttle,
 .unthrottle = con_unthrottle,
};
熟悉fops的话你就能看出来了,这是对tty设备的文件操作函数的表。也就是说,在用户程序里,通过open函数打开/dev/tty,然后 再用write函数就可以修改blankinterval了。原理是找到了,实践上有很大困难,那么多重函数调用,再看看do_con_trol()里面 的switch语句,正常人都要发晕。好在伟大的百度为我们提供了很多信息:在命令行下,可以使用setterm -blank 0指令来设置blankinterval。哈哈,救星来了,赶快看看setterm的源代码。setterm属于util-linux包,搜索一下很容易 找到。其中的perform_sequence()函数里有这样一段:
 
 /* -blank [0-60]. */
 if (opt_blank && vcterm) 
  printf("/033[9;%d]", opt_bl_min);
 
真得很神奇啊,用个printf就可以在用户程序里解决这个问题,本来我是打算只说用printf解决的,看到原理我想会更舒服一些;况且,在我的系统上用printf是不行的。
但是!问题还没有完,往往在我们的系统中,LCD的虚拟控制台和控制台TTY不是同一个设备,也就是说,如果在程序里单纯的printf是不行的!这样只能修改你正在使用的TTY的blankinterval,而你用的却是文本方式的设备,不存在黑屏问题。
于是,就需要仔细比较/dev/console、/dev/tty、/dev/ttyn的设备号,在我的系统里,用户程序里/dev /console和/dev/tty都是5,说明他们是一个东西,/dev/ttyn是4,这才是FB上的虚拟控制台。但是/dev/ttyn不是正在使 用的TTY,那么怎么printf呢?只好用write函数来解决了。
 
写这样一段代码:
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
 
void some_function()
{
 int f;
 f = open("/dev/tty0", O_RDWR);
 write(f, "/033[9;0]", 8);
 close(f);
}
问题终于解决了。
 
总结下,第二个问题有很多种解决方法:
1.修改LCD驱动,把关闭LCD控制器的函数变为空(不推荐)
2.修改vt.c中的blank_screen_t()函数,让其为空(在系统不需要使用关闭显示功能时推荐)
3.修改vt.c中的blankinterval,让其为0(系统可能需要使用关闭显示功能,而且希望系统上电后正常状态下不会关闭显示时推荐)
4.修改用户程序,加入设置blankinterval的代码(推荐)

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 宝宝一写字就哭怎么办 4岁宝宝不爱学习怎么办 4岁宝宝不爱写字怎么办 孩子调皮老师不让上学了怎么办 小学生写字握笔握出剪子来怎么办 儿子6岁不会写字怎么办 宝宝在幼儿园不说话怎么办 孩子上幼儿园不说话怎么办 幼儿写数字不写怎么办 小孩不吃饭怎么办 十个月 十个月小孩不爱吃饭怎么办 十个月的小孩不吃饭怎么办 家长要调幼儿园监控怎么办 自己带孩子婆婆生气怎么办 婆婆老是觉得我奶水不够怎么办 1岁小儿特别懒怎么办 17的孩子很懒怎么办 上大班的孩子不愿写字怎么办 快上中班的小朋友不爱学习怎么办 小学生两边肩膀不平应该怎么办 写字右肩膀疼是怎么办 开车久了肩膀疼怎么办 3岁宝宝撕书怎么办 孩子上幼儿园不爱写字怎么办 一年级小孩不爱做作业怎么办 小孩不愿多做作业怎么办 小孩一年级不自觉做作业怎么办 小孩会读不会写怎么办 好多字都不会写怎么办 写作业怎么办才能写快 五周宝宝爱玩不写字怎么办 爱玩手机的小孩怎么办? 一年级学生记不住生字怎么办 一年级小孩记不住生字怎么办 配镜度数高了怎么办 宝宝两岁半不肯坐马桶拉臭臭怎么办 儿子字写得不好 怎么办 小孩不听话不爱读书和写字怎么办 两岁宝宝不愿意穿衣服怎么办 做题粗心不认真怎么办 5岁宝宝不会写字怎么办