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

系统时钟的获取,调用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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值