实现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. caioj 1112 树形动态规划(TreeDP)7:战略游戏

    这道题和上一道题非常相似 这道题是看边,上一道是看点. 但是状态定义不同 看边的话没有不放不安全这种状态 因为当前结点的父亲无法让这颗子树没有看到的边看到 所以这种状态不存在 而上一道题存在不放不安全 ...

  2. 【Codeforces Round #420 (Div. 2) C】Okabe and Boxes

    [题目链接]:http://codeforces.com/contest/821/problem/C [题意] 给你2*n个操作; 包括把1..n中的某一个数压入栈顶,以及把栈顶元素弹出; 保证压入和 ...

  3. Qt之图形(转换)

    简述 QTransform类指定坐标系的2D转换,可以指定平移.缩放.扭曲(剪切).旋转或投影坐标系.绘制图形时,通常会使用. QTransform与QMatrix的不同之处在于,它是一个真正的3x3 ...

  4. Qt中事件分发源码剖析

    Qt中事件分发源码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,而且进行分发,这些都是在exec中进行的. 以下举例说明: 1)首先看看以下一段演示样例代码: ...

  5. BZOJ5029: 贴小广告 & BZOJ5168: [HAOI2014]贴海报

    [传送门:BZOJ5029&BZOJ5168] 简要题意: 给出m段区间l[i],r[i],表示l[i]到r[i]的数全部变成i,求出最后有多少种不同的数 题解: 线段树+离散化 这是一道经典 ...

  6. LSTM入门学习——结合《LSTM模型》文章看

    摘自:https://zybuluo.com/hanbingtao/note/581764 写得非常好 见原文 长短时记忆网络的思路比较简单.原始RNN的隐藏层只有一个状态,即h,它对于短期的输入非常 ...

  7. 关于在天机项目中遇到的常用git 命令

    1. 本地分支和远程分支 1>我们在本地创建分支,第一次push到远程是没有分支存在,执行git push 会有提示,按照提示的内容操作即可,当然我们也可以 git push origin fe ...

  8. 洛谷P1200 [USACO1.1]你的飞碟在这儿

    题目描述 众所周知,在每一个彗星后都有一只UFO.这些UFO时常来收集地球上的忠诚支持者.不幸的是,他们的飞碟每次出行都只能带上一组支持者.因此,他们要用一种聪明的方案让这些小组提前知道谁会被彗星带走 ...

  9. 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 ...

  10. js中运算符优先级问题

    其实事情是这样的,最近看到不少朋友讨论一道据说不知道哪儿的笔试题目,题目如下: var a = {n:1}; var b = a; a.x = a = {n:2}; 请写出a.x的值. 当然通过运行, ...