实现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闪烁的代码分析的更多相关文章

  1. [ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)

    说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入 ...

  2. [stm32][ucos][ucgui] 2、LED闪烁、串口、滑块、文本编辑框简单例程

    上一篇:[stm32][ucos] 1.基于ucos操作系统的LED闪烁.串口通信简单例程 * 内容简述: 本例程操作系统采用ucos2.86a版本, 建立了7个任务            任务名   ...

  3. STM32F103片外运行代码分析

    STM32F103片外运行代码分析 STM32F103有三种启动方式: 1.从片内Flash启动: 2.从片内RAM启动: 3.从片内系统存储器启动,内嵌的自举程序,用于串口IAP. 无法直接在片外N ...

  4. led闪烁(时序输入输出,自定义变量,时钟仿真,执行顺序)

    1.设计定义 设计一个以200ms亮,200ms暗交替闪烁的led灯,并且有一个复位按钮可以停止工作. 2.设计输入 2.1端口 以固定周期交替闪烁说明由时钟控制,需要一个时钟控制端口clk,要求复位 ...

  5. Blink Without Delay: 不使用 delay() 函数而使 LED 闪烁

    不使用 delay() 函数而使 LED 闪烁 有些时候你需要同时做两件事.例如,你可能希望在读取按键按下状态同时让LED闪烁. 在这种情况下,你不能使用 delay(),因为Arduino程序会在d ...

  6. [stm32][ucos] 1、基于ucos操作系统的LED闪烁、串口通信简单例程

    * 内容简述: 本例程操作系统采用ucos2.86a版本, 建立了5个任务            任务名                                             优先级 ...

  7. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  8. uC/OS-III学习2::uC/OS-III LED闪烁实验

    1 前言: 看完了uC/OS-III的基本介绍之后,大致对这个操作系统有了点了解,但真正的理解还是要通过不断的去使用,在使用中体验uC/OS-III的乐趣和更深的理解其工作原理是非常重要的.因此,我在 ...

  9. 基于Systick系统时钟延时的LED闪烁灯

    1.回顾我们的51 单片机编程,当我们需要做系统延迟的时候,最常采用的一种方式就是使用for 循环的空语句等待来实现. 当然,在STM32 里面也可以这么实现.但是在STM32 的Cortex 内核里 ...

随机推荐

  1. MySql系列之单表查询

    单表查询的语法 SELECT 字段1,字段2... FROM 表名 WHERE 条件 GROUP BY field HAVING 筛选 ORDER BY field LIMIT 限制条数 关键字的执行 ...

  2. 八 rowkey设计 几种方法

    简单来讲,rowkey就是 KeyValue 中的key     rowkey设计之 尽量散列设计 RowKey         如第三部分第六中讲到,如果数据都是有序的存储到一个特定的范围内,将会存 ...

  3. URAL 1513. Lemon Tale(简单的递推)

    写几组数据就会发现规律了啊. .但是我是竖着看的.. .还找了半天啊... 只是要用高精度来写,水题啊.就当熟悉一下java了啊. num[i] = 2*num[i-1]-num[i-2-k]. 15 ...

  4. UVa 112 树求和

    题意:给定一个数字,以及一个描写叙述树的字符序列,问存不存在一条从根到某叶子结点的路径使得其和等于那个数. 难点在于怎样处理字符序列.由于字符间可能有空格.换行等. 思路:本来想着用scanf的(后发 ...

  5. Pretty UI Design For Android -- 滑动背景、透明列表

    本文是从国外一个网上看到的效果.感觉非常不错.就简化了一下代码.拿来用了,先看下效果图: 效果还是非常不错的,以下让我们看看是如何实现的: 看看文字来源,非常easy,是一个数组: <?xml ...

  6. BZOJ1685: [Usaco2005 Oct]Allowance 津贴

    [传送门:BZOJ1685] 简要题意: 贝西工作勤勤恳恳,她每月向约翰索要C 元钱作为工资.约翰手上有不少钱,他一共有N 种面 额的钞票.第i 种钞票的面额记作Vi,约翰有Ki 张.钞票的面额设定是 ...

  7. JavaWeb简单介绍

    服务器端编程 技术种类 Servlet JSP Struts Spring Hibernate EJB Web Service Web服务器 IIS Apache Tomcat (提供对JSP和Ser ...

  8. 启动springboot

    新建一个springboot项目,idea的做法:一般直接next就行 填写项目使用到的技术,上面的Spring Boot版本建议选择最新的稳定版,主要勾选上Web就可以了,如下图: 新建之后< ...

  9. Gallery滑动一页(一个Item)效果

    本文主要介绍如何使用Gallery只滑动一页以及其实现原理. Demo APK 可以方便的查看效果,在各大应用商店搜索 trinea android 下载即可,如:Google Play. 可运行代码 ...

  10. MySQL中好用的GROUP_CONCAT函数

    今天看到同事的代码,又学到了一个有用的函数,刚看的时候不太懂,就搜了下用法,看下面这篇文章讲的挺详细就直接转载了,我也写不那么好,呵呵,感谢作者的无私奉献. http://blog.sina.com. ...