• 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) ",

    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 ",

    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)


  • 相关阅读:
    js运动框架
    关闭树莓派的指示灯
    利用Windows系统自带的Powershell功能计算文件的MD5、SHA256等Hash值
    CentOS下Zabbix Server 安装
    STM32开发——bootloader跳转App执行的实现
    ESP8266或ESP32使用ESP-IDF开发读取DHT12温度湿度
    Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————结合三个部分工作
    Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————帧缓冲显示视频
    Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————V4L2捕捉画面
    树莓派上安装Qt5 MQTT支持
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/6944575.html
Copyright © 2020-2023  润新知