RK3066 实现LED闪烁的代码分析
实现LED灯的闪烁,须要在驱动里加入一个定时器函数,详细实现涉及到了LED
GPIO驱动。用户空间程序调用驱动程序。
1.首先来看LED设备驱动注冊过程,代码位于../kernel/drivers/leds/led-gpio.c中,
297
static int __init gpio_led_init(void)
298
{
299
return
platform_driver_register(&gpio_led_driver);
300
}
285
static struct platform_driver gpio_led_driver = {
286
.probe
=
gpio_led_probe,
287
.remove
=
__devexit_p(gpio_led_remove),
288
.driver
=
{
289
.name
= "leds-gpio",
//与platform_device结构体rk29_device_gpio_leds(见后面的加入过程)中定义的.name一致,platform总线通过name将两者关联。
290
.owner
= THIS_MODULE,
291
.of_match_table
= of_gpio_leds_match,
292
},
293
};
driver中的probe函数,当中pdev相应于
../kernel/arch/arm/mach-rk30/board-rk30sdk-box.c 中定义的platform_device结构体rk29_device_gpio_leds,当中.name = "leds-gpio"
235
static int __devinit gpio_led_probe(struct platform_device *pdev)
236
{
237
struct
gpio_led_platform_data *pdata = pdev->dev.platform_data;
238
struct
gpio_leds_priv *priv;
239
int
i, ret = 0;
240
241
if
(pdata && pdata->num_leds) {
242
priv
= kzalloc(sizeof_gpio_leds_priv(pdata->num_leds),
243
GFP_KERNEL);
244
if
(!priv)
245
return
-ENOMEM;
246
247
priv->num_leds
= pdata->num_leds;
248
for
(i = 0; i < priv->num_leds; i++) {
249
ret
= create_gpio_led(&pdata->leds[i],
250
&priv->leds[i],
251
&pdev->dev,
pdata->gpio_blink_set); //创建详细的设备,相应结构体rk29_leds中的LED1。LED2。HDMI-sw
252
if
(ret < 0) {
253
/*
On failure: unwind the led creations */
254
for
(i = i - 1; i >= 0; i--)
255
delete_gpio_led(&priv->leds[i]);
256
kfree(priv);
257
return
ret;
258
}
259
}
260
}
else {
261
priv
= gpio_leds_create_of(pdev);
262
if
(!priv)
263
return
-ENODEV;
264
}
265
266
platform_set_drvdata(pdev,
priv);
267
268
return
0;
269
}
93
static int __devinit create_gpio_led(const struct gpio_led *template,
94
struct
gpio_led_data *led_dat, struct device *parent,
95
int
(*blink_set)(unsigned, int, unsigned long *, unsigned long *))
96
{
97
int
ret, state;
98
99
led_dat->gpio
= -1;
100
101
/*
skip leds that aren't available */
102
if
(!gpio_is_valid(template->gpio)) {
103
printk(KERN_INFO
"Skipping unavailable LED gpio %d (%s)\n",
104
template->gpio,
template->name);
105
return
0;
106
}
107
/*
108
ret
= gpio_request(template->gpio, template->name);
109
if
(ret < 0)
110
return
ret;
111
*/
112
led_dat->cdev.name
= template->name;
113
led_dat->cdev.default_trigger
= template->default_trigger;
114
led_dat->gpio
= template->gpio;
115
led_dat->can_sleep
= gpio_cansleep(template->gpio);
116
led_dat->active_low
= template->active_low;
117
led_dat->blinking
= 0;
118
if
(blink_set) {
119
led_dat->platform_gpio_blink_set
= blink_set;
120
led_dat->cdev.blink_set
= gpio_blink_set;
121
}
122
led_dat->cdev.brightness_set
= gpio_led_set;
123
if
(template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
124
state
= !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
125
else
126
state
= (template->default_state == LEDS_GPIO_DEFSTATE_ON);
127
led_dat->cdev.brightness
= state ? LED_FULL : LED_OFF;
128
if
(!template->retain_state_suspended)
129
led_dat->cdev.flags
|= LED_CORE_SUSPENDRESUME;
130
131
ret
= gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
132
if
(ret < 0)
133
goto
err;
134
135
INIT_WORK(&led_dat->work,
gpio_led_work);
136
137
ret
= led_classdev_register(parent, &led_dat->cdev);
138
if
(ret < 0)
139
goto
err;
140
141
return
0;
142
err:
143
gpio_free(led_dat->gpio);
144
return
ret;
145
}
247
/**
248
* led_classdev_register - register a new object of led_classdev class.
249
* @parent: The device to register.
250
* @led_cdev: the led_classdev structure for this device.
251
*/
252
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
253
{
254
led_cdev->dev
= device_create(leds_class, parent, 0, led_cdev,
255
"%s",
led_cdev->name); //device_create - creates a device and registers it with sysfs;创建设备节点在/sys/class/leds下创建LED1 LED2 HDMI-sw
256
if
(IS_ERR(led_cdev->dev))
257
return
PTR_ERR(led_cdev->dev);
258
259
#ifdef CONFIG_LEDS_TRIGGERS
260
init_rwsem(&led_cdev->trigger_lock);
261
#endif
262
/*
add to the list of leds */
263
down_write(&leds_list_lock);
264
list_add_tail(&led_cdev->node,
&leds_list); //把设备节点加入到leds的链表中
265
up_write(&leds_list_lock);
266
267
if
(!led_cdev->max_brightness)
268
led_cdev->max_brightness
= LED_FULL;
269
270
led_update_brightness(led_cdev);
271
272
init_timer(&led_cdev->blink_timer);
//初始化一个定时器
273
led_cdev->blink_timer.function
= led_timer_function; //注冊一个定时器调用函数
274
led_cdev->blink_timer.data
= (unsigned long)led_cdev; //定时器调用函数參数
275
276
#ifdef CONFIG_LEDS_TRIGGERS
277
led_trigger_set_default(led_cdev);
278
#endif
279
280
printk(KERN_DEBUG
"Registered led device: %s\n",
281
led_cdev->name);
282
283
return
0;
284
}
定时器调用函数,定时器到时启动
131
static void led_timer_function(unsigned long data)
132
{
133
struct
led_classdev *led_cdev = (void *)data;
134
unsigned
long brightness;
135
unsigned
long delay;
136
137
if
(!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
138
led_set_brightness(led_cdev,
LED_OFF);
139
return;
140
}
141
142
brightness
= led_get_brightness(led_cdev);
143
if
(!brightness) {
144
/*
Time to switch the LED on. */
145
brightness
= led_cdev->blink_brightness;
146
delay
= led_cdev->blink_delay_on;
147
}
else {
148
/*
Store the current brightness value to be able
149
*
to restore it when the delay_off period is over.
150
*/
151
led_cdev->blink_brightness
= brightness;
152
brightness
= LED_OFF;
153
delay
= led_cdev->blink_delay_off;
154
}
155
156
led_set_brightness(led_cdev,
brightness); //运行gpio高低切换的函数
157
158
mod_timer(&led_cdev->blink_timer,
jiffies + msecs_to_jiffies(delay)); //能够改动定时器定时參数
159
}
加入平台设备rk29_device_gpio_leds,代码位于../kernel/arch/arm/mach-rk30/board-rk30-box.c中。
1061
static struct platform_device rk29_device_gpio_leds = {
1062
.name
= "leds-gpio",
1063
.id =
-1,
1064
.dev =
{
1065
.platform_data
= &rk29_leds_pdata,
1066
},
1067
};
1056
static struct gpio_led_platform_data rk29_leds_pdata = {
1057
.leds
= rk29_leds,
1058
.num_leds
= ARRAY_SIZE(rk29_leds),
1059
};
1060
972
static struct gpio_led rk29_leds[] = {
......
1030
{
1031
.name
= "LED1",
1032
.gpio
= RK30_PIN4_PC6,
1033
.active_low
= 0,
1034
.retain_state_suspended
= 0,
1035
.default_state
= LEDS_GPIO_DEFSTATE_OFF,
1036
},
1037
1038
{
1039
.name
= "LED2",
1040
.gpio
= RK30_PIN4_PC7,
1041
.active_low
= 0,
1042
.retain_state_suspended
= 0,
1043
.default_state
= LEDS_GPIO_DEFSTATE_ON,
1044
},
1045
1046
{
1047
.name
= "HDMI-sw",
1048
.gpio
= RK30_PIN4_PD2,
1049
.active_low
= 0,
1050
.retain_state_suspended
= 0,
1051
.default_state
= LEDS_GPIO_DEFSTATE_ON,
1052
},
1053
1054
};
2.
创建一个类,进入系统后能够看到 /sys/class/leds文件夹,驱动层代码位于../kernel/drivers/leds/led-class.c中。
339
static int __init leds_init(void)
340
{
341
leds_class
= class_create(THIS_MODULE, "leds"); //相应于 /sys/class/leds
342
if
(IS_ERR(leds_class))
343
return
PTR_ERR(leds_class);
344
leds_class->suspend
= led_suspend;
345
leds_class->resume
= led_resume;
346
leds_class->dev_attrs
= led_class_attrs; //属于类leds设备的属性
347
return
0;
348
}
属于类leds设备的属性的定义,用户空间程序通过读取设置这些属性来调用LED驱动
119
static struct device_attribute led_class_attrs[] = {
120
__ATTR(brightness,
0644, led_brightness_show, led_brightness_store),
121
__ATTR(max_brightness,
0444, led_max_brightness_show, NULL),
122
#ifdef CONFIG_LEDS_TRIGGERS
123
__ATTR(trigger,
0644, led_trigger_show, led_trigger_store),
124
#endif
125
__ATTR(blinkquick,
0644, NULL, led_blink_store_quick),
126
__ATTR(blinkslow,
0644, NULL, led_blink_store_slow),
127
__ATTR_NULL,
128
129
};
130
__ATTR的定义。属性名称,模式。读取函数。设置函数
70
#define __ATTR(_name,_mode,_show,_store) { \
71
.attr
= {.name = __stringify(_name), .mode = _mode }, \
72
.show
= _show, \
73
.store
= _store, \
74
}
led_blink_store_slow函数的定义,
static
ssize_t led_blink_store_slow(struct device *dev,
89 struct device_attribute *attr, const char *buf, size_t size)
90 {
91 struct led_classdev *led_cdev = dev_get_drvdata(dev);
92 ssize_t ret = -EINVAL;
93 char *after;
94 unsigned long state = simple_strtoul(buf, &after, 10);
95 size_t count = after - buf;
96 unsigned long delay_on = 500;
97 unsigned long delay_off = 500;
98
99 if (isspace(*after))
100 count++;
101
102 if (count == size) {
103 ret = count;
104 if(state == 0)
105 delay_on = 0;
106 }
107 led_blink_set(led_cdev,&delay_on,&delay_off);
108 return ret;
109 }
led_blink_set函数的定义。
313
void led_blink_set(struct led_classdev *led_cdev,
314
unsigned
long *delay_on,
315
unsigned
long *delay_off)
316
{
317
del_timer_sync(&led_cdev->blink_timer);
318
319
if
(led_cdev->blink_set &&
320
!led_cdev->blink_set(led_cdev,
delay_on, delay_off))
321
return;
322
323
/*
blink with 1 Hz as default if nothing specified */
324
if
(!*delay_on && !*delay_off)
325
*delay_on
= *delay_off = 500;
326
327
led_set_software_blink(led_cdev,
*delay_on, *delay_off);
328
}
led_set_software_blink函数的定义,
169
static void led_set_software_blink(struct led_classdev *led_cdev,
170
unsigned
long delay_on,
171
unsigned
long delay_off)
172
{
173
int
current_brightness;
174
175
current_brightness
= led_get_brightness(led_cdev);
176
if
(current_brightness)
177
led_cdev->blink_brightness
= current_brightness;
178
if
(!led_cdev->blink_brightness)
179
led_cdev->blink_brightness
= led_cdev->max_brightness;
180
181
if
(led_get_trigger_data(led_cdev) &&
182
delay_on
== led_cdev->blink_delay_on &&
183
delay_off
== led_cdev->blink_delay_off)
184
return;
185
186
led_stop_software_blink(led_cdev);
187
188
led_cdev->blink_delay_on
= delay_on;
189
led_cdev->blink_delay_off
= delay_off;
190
191
/*
never on - don't blink */
192
if
(!delay_on)
193
return;
194
195
/*
never off - just set to brightness */
196
if
(!delay_off) {
197
led_set_brightness(led_cdev,
led_cdev->blink_brightness);
198
return;
199
}
200
201
mod_timer(&led_cdev->blink_timer,
jiffies + 1); //将已初始化的定时器led_cdev->blink_timer加入到系统定时器链表中
202
}
3.
系统启动首先执行leds_init 创建了/sys/class/leds,将设备属性blinkslow与函数led_blink_store_slow相关连;而后执行gpio_led_init创建并注冊设备,创建设备节点在/sys/class/leds下创建LED1 LED2 HDMI-sw。初始化一个定时器led_cdev->blink_timer注冊一个定时器调用函数led_timer_function.
Android中的内核启动后,kernel会启动第一个用户级别的进程:init。它是一个由内核启动的用户级进程。
内核自行启动(已经被加载内存,开
始执行,并已初始化全部的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完毕引导进程。init始终是第一个进程。Android init.*.rc文件由系统第一个启动的init程序解析,在init.rk30board.rc设置例如以下:
53 write
/sys/class/leds/LED1/blinkslow 1
54 write
/sys/class/leds/HDMI-sw/blinkslow 1
blinkslow写1又是怎样
怎样调用函数led_blink_store_slow的呢?
4.
通过在led_blink_set函数中加入 WARN_ON(1), 能够追踪函数调用过程。详细例如以下:
[ 3.315763]
------------[ cut here ]------------
[ 3.315829]
WARNING: at /home/vichie/netcast/firefly-2.0/netcast/os/kernel/drivers/leds/led-class.c:315 led_blink_set+0x20/0x10c()
[ 3.315934]
[<c043e61c>] (unwind_backtrace+0x0/0xf8) from [<c046fe8c>] (warn_slowpath_common+0x4c/0x64)
[ 3.316004]
[<c046fe8c>] (warn_slowpath_common+0x4c/0x64) from [<c046fec0>] (warn_slowpath_null+0x1c/0x24)
[ 3.316081]
[<c046fec0>] (warn_slowpath_null+0x1c/0x24) from [<c0757a2c>] (led_blink_set+0x20/0x10c)
[ 3.316150]
[<c0757a2c>] (led_blink_set+0x20/0x10c) from [<c0757b94>] (led_blink_store_slow+0x7c/0x94)
[ 3.316219]
[<c0757b94>] (led_blink_store_slow+0x7c/0x94) from [<c0667590>] (dev_attr_store+0x18/0x24)
[ 3.316291]
[<c0667590>] (dev_attr_store+0x18/0x24) from [<c054b104>] (sysfs_write_file+0x168/0x198)
[ 3.316366]
[<c054b104>] (sysfs_write_file+0x168/0x198) from [<c05009a4>] (vfs_write+0xa0/0x144)
[ 3.316432]
[<c05009a4>] (vfs_write+0xa0/0x144) from [<c0500c30>] (sys_write+0x38/0x70)
[ 3.316492]
[<c0500c30>] (sys_write+0x38/0x70) from [<c0439140>] (ret_fast_syscall+0x0/0x30)
[ 3.316548]
---[ end trace fd8d711c10e99270 ]---
相同在led_timer_function中加入
WARN_ON(1), 能够追踪到每次LED开关时led_timer_function被调用的过程:
shell@rk30sdk:/
$ [ 2817.591119] ------------[ cut here ]------------
[
2817.591180] WARNING: at /home/vichie/netcast/firefly-2.0/netcast/os/kernel/drivers/leds/led-class.c:135 led_timer_function+0x14/0xd4()
[
2817.591282] [<c043e61c>] (unwind_backtrace+0x0/0xf8) from [<c046fe8c>] (warn_slowpath_common+0x4c/0x64)
[
2817.591351] [<c046fe8c>] (warn_slowpath_common+0x4c/0x64) from [<c046fec0>] (warn_slowpath_null+0x1c/0x24)
[
2817.591423] [<c046fec0>] (warn_slowpath_null+0x1c/0x24) from [<c075795c>] (led_timer_function+0x14/0xd4)
[
2817.591497] [<c075795c>] (led_timer_function+0x14/0xd4) from [<c047d7e0>] (run_timer_softirq+0x138/0x370)
[
2817.591566] [<c047d7e0>] (run_timer_softirq+0x138/0x370) from [<c0476430>] (__do_softirq+0xcc/0x238)
[
2817.591631] [<c0476430>] (__do_softirq+0xcc/0x238) from [<c0476a30>] (irq_exit+0x98/0xa0)
[
2817.591695] [<c0476a30>] (irq_exit+0x98/0xa0) from [<c0433274>] (do_local_timer+0x70/0x94)
[
2817.591756] [<c0433274>] (do_local_timer+0x70/0x94) from [<c0438c48>] (__irq_svc+0x48/0xe0)
[
2817.591812] Exception stack(0xd6c5ff48 to 0xd6c5ff90)
[
2817.591851] ff40: d6c5ff90
00000000 05737eba 00000290 04db99cd 00000290
[
2817.591909] ff60: 000003ff feb3cfff c160e0c0 00000001 c14fe06c c0b5a530 c0ae60c0 d6c5ff90
[
2817.591964] ff80: c0495564 c044d7d4 60000013 ffffffff
[
2817.592008] [<c0438c48>] (__irq_svc+0x48/0xe0) from [<c044d7d4>] (rk30_idle+0x44/0x74)
[
2817.592070] [<c044d7d4>] (rk30_idle+0x44/0x74) from [<c0745244>] (cpuidle_idle_call+0xac/0x200)
[
2817.592134] [<c0745244>] (cpuidle_idle_call+0xac/0x200) from [<c0439ef4>] (cpu_idle+0xbc/0xf8)
RK3066 实现LED闪烁的代码分析的更多相关文章
- [ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)
说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入 ...
- [stm32][ucos][ucgui] 2、LED闪烁、串口、滑块、文本编辑框简单例程
上一篇:[stm32][ucos] 1.基于ucos操作系统的LED闪烁.串口通信简单例程 * 内容简述: 本例程操作系统采用ucos2.86a版本, 建立了7个任务 任务名 ...
- STM32F103片外运行代码分析
STM32F103片外运行代码分析 STM32F103有三种启动方式: 1.从片内Flash启动: 2.从片内RAM启动: 3.从片内系统存储器启动,内嵌的自举程序,用于串口IAP. 无法直接在片外N ...
- led闪烁(时序输入输出,自定义变量,时钟仿真,执行顺序)
1.设计定义 设计一个以200ms亮,200ms暗交替闪烁的led灯,并且有一个复位按钮可以停止工作. 2.设计输入 2.1端口 以固定周期交替闪烁说明由时钟控制,需要一个时钟控制端口clk,要求复位 ...
- Blink Without Delay: 不使用 delay() 函数而使 LED 闪烁
不使用 delay() 函数而使 LED 闪烁 有些时候你需要同时做两件事.例如,你可能希望在读取按键按下状态同时让LED闪烁. 在这种情况下,你不能使用 delay(),因为Arduino程序会在d ...
- [stm32][ucos] 1、基于ucos操作系统的LED闪烁、串口通信简单例程
* 内容简述: 本例程操作系统采用ucos2.86a版本, 建立了5个任务 任务名 优先级 ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析
一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...
- uC/OS-III学习2::uC/OS-III LED闪烁实验
1 前言: 看完了uC/OS-III的基本介绍之后,大致对这个操作系统有了点了解,但真正的理解还是要通过不断的去使用,在使用中体验uC/OS-III的乐趣和更深的理解其工作原理是非常重要的.因此,我在 ...
- 基于Systick系统时钟延时的LED闪烁灯
1.回顾我们的51 单片机编程,当我们需要做系统延迟的时候,最常采用的一种方式就是使用for 循环的空语句等待来实现. 当然,在STM32 里面也可以这么实现.但是在STM32 的Cortex 内核里 ...
随机推荐
- vuex 闲置状态重置方案
前言 大型单页应用(后面都是指spa),我们往往会通过使用状态管理器 vuex 去解决组件间状态共享与状态传递等问题.这种应用少则几十个单页,多则上百个单页.随着路由的频繁切换,每个路由对应的 vue ...
- web——前后端通信原理
前端向后台传输数据: 传输方法:post get 区别: (1)get:用于从服务器获取数据,将参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看 ...
- 关于Github Pages
迁移Github Pages 我稍微有一点强迫症,实在是忍受不了整洁的界面有一些推广的广告.正所谓博客平台不重要,重要的是要有干货,CSDN首页满屏的广告也就忍受了,但是自己的文章的页面有广告看着实在 ...
- Codeforces Round #313 (Div. 2) 解题报告
A. Currency System in Geraldion: 题意:有n中不同面额的纸币,问用这些纸币所不能加和到的值的最小值. 思路:显然假设这些纸币的最小钱为1的话,它就能够组成随意面额. 假 ...
- Scala具体解释---------Scala是什么?可伸展的语言!
Scala是什么 Scala语言的名称来自于"可伸展的语言". 之所以这样命名,是由于他被设计成随着使用者的需求而成长.你能够把Scala应用在非常大范围的编程任务上.从写个小脚本 ...
- 【POJ 2195】 Going Home(KM算法求最小权匹配)
[POJ 2195] Going Home(KM算法求最小权匹配) Going Home Time Limit: 1000MS Memory Limit: 65536K Total Submiss ...
- Python数据可视化——散点图
PS: 翻了翻草稿箱. 发现竟然存了一篇去年2月的文章...尽管naive.还是发出来吧... 本文记录了python中的数据可视化--散点图scatter, 令x作为数据(50个点,每一个30维), ...
- 库:IO读写操作
在Java文件读取路径要注意的地方: 计算机:"D:\" 程序:"D://" 内存与硬盘之间进行文件的相互传输过程 以应用程序为参考点,应用程序从硬盘中读取数据 ...
- python requests请求卡住问题
最近经常接到别人反馈某个爬虫工具程序没有正常运行,需要下载的资讯数据也没有及时进行收录. 刚开始以为可能是机器的问题,偶尔机器会出现程序运行中途卡住的情况. 但随着异常的情况越来越频繁,我便只好去排查 ...
- 应对加密js的三种方法
经常遇到网页在登录后会对用户输入的帐号和密码通过js进行加密,导致模拟登录这类网站时受到阻碍 这里小记一下当前解决该问题的三种方法 1.利用python实现js同等加密. 2.利用selenium模拟 ...