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 内核里 ...
随机推荐
- caioj 1112 树形动态规划(TreeDP)7:战略游戏
这道题和上一道题非常相似 这道题是看边,上一道是看点. 但是状态定义不同 看边的话没有不放不安全这种状态 因为当前结点的父亲无法让这颗子树没有看到的边看到 所以这种状态不存在 而上一道题存在不放不安全 ...
- 【Codeforces Round #420 (Div. 2) C】Okabe and Boxes
[题目链接]:http://codeforces.com/contest/821/problem/C [题意] 给你2*n个操作; 包括把1..n中的某一个数压入栈顶,以及把栈顶元素弹出; 保证压入和 ...
- Qt之图形(转换)
简述 QTransform类指定坐标系的2D转换,可以指定平移.缩放.扭曲(剪切).旋转或投影坐标系.绘制图形时,通常会使用. QTransform与QMatrix的不同之处在于,它是一个真正的3x3 ...
- Qt中事件分发源码剖析
Qt中事件分发源码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,而且进行分发,这些都是在exec中进行的. 以下举例说明: 1)首先看看以下一段演示样例代码: ...
- BZOJ5029: 贴小广告 & BZOJ5168: [HAOI2014]贴海报
[传送门:BZOJ5029&BZOJ5168] 简要题意: 给出m段区间l[i],r[i],表示l[i]到r[i]的数全部变成i,求出最后有多少种不同的数 题解: 线段树+离散化 这是一道经典 ...
- LSTM入门学习——结合《LSTM模型》文章看
摘自:https://zybuluo.com/hanbingtao/note/581764 写得非常好 见原文 长短时记忆网络的思路比较简单.原始RNN的隐藏层只有一个状态,即h,它对于短期的输入非常 ...
- 关于在天机项目中遇到的常用git 命令
1. 本地分支和远程分支 1>我们在本地创建分支,第一次push到远程是没有分支存在,执行git push 会有提示,按照提示的内容操作即可,当然我们也可以 git push origin fe ...
- 洛谷P1200 [USACO1.1]你的飞碟在这儿
题目描述 众所周知,在每一个彗星后都有一只UFO.这些UFO时常来收集地球上的忠诚支持者.不幸的是,他们的飞碟每次出行都只能带上一组支持者.因此,他们要用一种聪明的方案让这些小组提前知道谁会被彗星带走 ...
- Atcoder ABC 069 C - 4-adjacent D - Grid Coloring
C - 4-adjacent Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statement We have ...
- js中运算符优先级问题
其实事情是这样的,最近看到不少朋友讨论一道据说不知道哪儿的笔试题目,题目如下: var a = {n:1}; var b = a; a.x = a = {n:2}; 请写出a.x的值. 当然通过运行, ...