dm3730平台oled显示时钟——系统时钟的获取和刷新

来源:互联网 发布:linux telnet测试端口 编辑:程序博客网 时间:2024/06/06 03:01

系统时钟的获取,调用do_gettimeofday函数,时间格式的转换用rtc_time_to_tm函数。
时间怎么刷新?
方案一:在内核起一个定时器,每隔30秒,定时器处理函数刷新。
看起来很美好,然而事实并非如此。简单来说,中断上下文不能睡眠,定时器处理函数在中断上下文,而定时器处理函数中如果调用会进入睡眠的函数,内核就panic了。
到底是怎么回事呢?
内核定时器是在时钟中断发生后,作为软中断在下半部中执行的。所有的定时器结构都以链表的形式存储。时钟中断发生后,内核按
链表顺序依次执行。一般来说,定时器在超时后会立即执行,但是也有可能被推迟到下一个时钟节拍才能运行,所以不能用定时器来
实现硬实时的操作。又因为内核定时器发生在软中断中,因此,定时器执行函数不能够睡眠,也不能够持有信号量。如果对硬件的访
问需要使用信号量同步,或者可能睡眠(比如需要调用kmalloc内存分配,但是由于某种原因不能使用GFP_ATOMIC标志),就不能
直接通过定时器来实现了。不巧,我们确实调用了会进入睡眠的函数。

这种情况怎样处理?
方案二:工作队列通过定时器处理函数自动调度工作 
一个变通的做法是在内核定时器执行函数里调用工作队列,在工作队列处理函数中解决这个问题。
为啥工作队列能耐就这么大呢?
《Linux内核设计与实现》的作者Robert Love是这么说的:“如果你需要一个可以重新调度的实体来执行你的下班部处理,你应该使
用工作队列。它是唯一能在进程上下文中运行的下半部机制,也只有它才可以睡眠。”
由于工作队列是工作在内核线程上的,因此其工作环境为进程上下文,从而工作函数可以休眠任意时间。

通常在内核中使用工作队列有两种方式:
1.共享队列
2.自定义队列
采用共享工作队列会有一个弊端,因为毕竟共享队列采用的是kevent线程,系统里面的其它工作也会使用到该共享队列。如果我们在
该工作队列加入太多耗时的程序,无疑会降低系统性能,因此一般在驱动程序中,我们会偏向于使用自定义工作队列,采用自定义工
作队列也比较简单,相对于共享工作队列,这里多了一个创建自定义工作的函数,即:create_queue函数(注意这个函数会在每一个
CPU上都创建一个一个工作队列和相应的线程,这未免太过于消耗资源,因此我们还可以采用在某一指定的CPU上创建一个工作队
列,例如采用create_singlethread_workqueue函数,就会在编号为第一个的CPU上创建内核线程和工作队列。)对于自定义的工作
队列,在这里我们不能使用schedule_struct函数将work_struct添加进工作队列了,这是因为schedule_struct函数只能往共享工作队列
上添加工作节点(work_struct),所以我们必须要采用queue_work 函数。

示例

void get_current_time(void){struct timex txc;struct rtc_time tm;static int index = 0;do_gettimeofday(&(txc.time));rtc_time_to_tm(txc.time.tv_sec,&tm);oled_post_word(3, 12, 11, 18, font_num[tm.tm_hour/10], FALSE);oled_post_word(17, 12, 11, 18, font_num[tm.tm_hour%10], FALSE);oled_post_word(36, 12, 11, 18, font_num[tm.tm_min/10], FALSE);oled_post_word(50, 12, 11, 18, font_num[tm.tm_min%10], FALSE);if (index == 0) {index = 1;oled_post_word(28, 12, 8, 20, font_num_colon[0], FALSE);} else {index = 0;oled_post_word(28, 12, 8, 20, font_num_colon[2], FALSE);}mod_timer(&timer, jiffies+HZ/2);//重新激活定时器queue_work(queue, &work);//采用自定义工作队列}

static int __devinit ssd1306_probe(struct i2c_client *client,    const struct i2c_device_id *id){ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);SSD_1306_INFO(" ssd1306 probe");if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {SSD_1306_INFO("i2c_check_functionality failed");return -EIO;}ssd1306_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);ssd1306_client->addr= SSD1306_SLAVE_ADDR;ssd1306_client->adapter = adapter;strcpy(ssd1306_client->name, "ssd1306");INIT_WORK(&work,oled_display_screen);init_timer(&timer);timer.expires = jiffies + HZ;timer.function = get_current_time;timer.data = 0;add_timer(&timer);ssd1306_init();ssd1306_clear_screen(0x00);ssd1306_display_off();oled_display_screen();ssd1306_display_on();return 0;}


static int ssd1306_module_init(void){queue = create_singlethread_workqueue("oled thread");i2c_add_driver(&ssd1306_driver);return 0;}

原创粉丝点击