摘自: http://blog.csdn.net/hellowxwworld/article/details/11001739
如有侵权,敬请告知,谢谢。
GPIO 驱动的 LED 由于操作简单和可视化即可以在板上直接看到其闪烁时长和频率,我们可以通过LED这种特性用于调试开发过程当中各种情景, 如统计某个中断出发频率,某些在linux 内核比较难以调试的环境, 比如休眠唤醒模式, soc各种低功耗模式等环境, 这样即使在普通串口打印不能正常工作的环境,我们也可以利用LED辅助这些环境下的调试。
0 GPIO LED 设备驱动分析
区分于keyboard的led驱动(由input 子设备管理),GPIO LEDS使用led-clas驱动框架, 用户空间通过/sys/class/leds/board-led/ 访问led的各种属性,其中max_brightness 代表最大亮度,brightness代表亮度, 复杂的led 系统支持delay_{on, off} 用来控制led交替闪烁哦的时长, trigger 用来触发led事件,例如开启关闭
配置内核CONFIG
- CONFIG_LEDS_GPIO=y
- CONFIG_LEDS_TRIGGERS=y
1 重要数据结构
- o 描述gpio led 设备结构
- /* For the leds-gpio driver */
- struct gpio_led {
- const char *name; // 每个led的名字
- const char *default_trigger; // 指定默认触发源
- unsigned gpio; // 控制led 的io pin
- unsigned active_low : 1; // low 时为off
- unsigned retain_state_suspended : 1; // 休眠时是否保存状态,等到唤醒后恢复
- unsigned default_state : 2; // 默认状态, 0:开, 1:关, 2:保持
- /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
- };
- o int led_classdev_register(); gpio led注册时会创建sys接口,由以下结构体控制
- struct led_classdev {
- const char *name;
- int brightness; // 当前亮度
- int max_brightness; // 最大亮度
- int flags; // 反映led状态
-
- /* Lower 16 bits reflect status */
- #define LED_SUSPENDED (1 << 0)
- /* Upper 16 bits reflect control information */
- #define LED_CORE_SUSPENDRESUME (1 << 16)
-
- /* Set LED brightness level */
- /* Must not sleep, use a workqueue if needed */
- void (*brightness_set)(struct led_classdev *led_cdev,
- enum led_brightness brightness); // 亮度设置回调
- /* Get LED brightness level */
- enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); // 亮度获取回调
-
- /*
- * Activate hardware accelerated blink, delays are in milliseconds
- * and if both are zero then a sensible default should be chosen.
- * The call should adjust the timings in that case and if it can't
- * match the values specified exactly.
- * Deactivate blinking again when the brightness is set to a fixed
- * value via the brightness_set() callback.
- */
- int (*blink_set)(struct led_classdev *led_cdev,
- unsigned long *delay_on,
- unsigned long *delay_off); // 硬件加速闪烁回调, 毫秒级别
-
- struct device *dev;
- struct list_head node; /* LED Device list */ // 每个led驱动加入双向循环链表管理
- const char *default_trigger; /* Trigger to use */ // 默认trigger, 一般设置为dummy
-
- unsigned long blink_delay_on, blink_delay_off; // blink delay
- struct timer_list blink_timer; // 定时器实现 blink时长控制
- int blink_brightness; // 闪烁亮度
-
- #ifdef CONFIG_LEDS_TRIGGERS
- /* Protects the trigger data below */
- struct rw_semaphore trigger_lock; // 读写信号量处理竞态
-
- struct led_trigger *trigger; // 事件触发结构体
- struct list_head trig_list; // 触发源列表
- void *trigger_data; // 数据pointer
- #endif
- };
- o led事件触发回调
- struct led_trigger {
- /* Trigger Properties */
- const char *name; // 触发源
- void (*activate)(struct led_classdev *led_cdev); //亮灯回调
- void (*deactivate)(struct led_classdev *led_cdev); // 关灯回调
-
- /* LEDs under control by this trigger (for simple triggers) */
- rwlock_t leddev_list_lock; // 读写所,防止竟态
- struct list_head led_cdevs; // 双向循环链表控制每个led的触发处理handler
-
- /* Link to next registered trigger */
- struct list_head next_trig; // 管理同个led的不同触发处理handler
- }
2 注册GPIO LEDS设备
- static struct gpio_led __initdata gpio_leds[] = {
- [0] = {
- .name = "gpio-led0",
- .default_trigger = "cpuidle",
- .gpio = LED_ID1,
- .active_low = true,
- .retain_state_suspended = false,
- .default_state = LEDS_GPIO_DEFSTATE_OFF,
- },
- [1] = {
- .name = "gpio-led1",
- .default_trigger = "mmc",
- .gpio = LED_ID2,
- .active_low = true,
- .retain_state_suspended = true,
- .default_state = LEDS_GPIO_DEFSTATE_OFF,
- },
- ret = gpio_led_register_device(-1, &gpio_led); // 注册gpio leds设备
- -> ret = platform_device_register_resndata(); // 注册gpio leds设备
-
-
- driver probe
- gpio_led_probe() // 驱动加载后probe
- -> create_gpio_led() // 申请gpio, 初始化设备并且填充上述相关回调
- -> gpio_request()
- -> gpio_direction_output()
- -> INIT_WORK(&led_dat->work, gpio_led_work) // 使用工作队列处理例如闪烁,开关等事件
- -> led_classdev_register(parent, &led_dat->cdev); // 建立sys接口,提供用户空间控制度
2 调试时只需用到led的开关足以,无需blink闪烁的功能
o 用于调试低功耗
定义不同低功耗模式的各个led,在cpu进入idle, aftr和lpa模式后就会点亮对应的leds,退出以后就会关闭相应的leds,四个cpu每个对应一个led,aftr和lpa各用一个,一共6个。
根据上述驱动配置,具体的对应关系如下:
调试对象 LED灯编号
AFTR gpio-led0
LPA gpio-led5
CPU0 IDLE: gpio-led2
CPU1~3 IDLE 分别对应其他的三个LEDS
可以按照以下步骤利用led调试
o 以dummy为例,定义一个led trigger
- DEFINE_LED_TRIGGER(dummy_led_trigger)
o 绑定该led trigger到驱动中的那个leds灯
- led_trigger_register_simple("dummy", &cpuidle_led_trigger);
o 通过开、关led灯来定义某个dummy问题的原因
比如说,我们怀疑某个驱动中某一个操作有问题,那么可以在前后加上点灯和灭灯的动作,如果这个操作导致系统挂起,那led等点亮后就不会灭掉,因而可以用来确认问题的位置,例如:
- led_trigger_event(aftr_led_trigger, LED_FULL);
- /* Some potentially problematic operations */
- led_trigger_event(aftr_led_trigger, LED_OFF);
- -> led_set_brightness()
- -> gpio_set_value();
3 更简单用法
直接封装gpio的操作已达到操作led亮灭的目的
- void led1_on() {
- s3c_gpio_cfgpin(LED_ID1, S3C_GPIO_OUTPUT);
- s3c_gpio_setpull(LED_ID1, S3C_GPIO_PULL_NONE);
- gpio_set_value(LED_ID1, 1);
- }
- void led1_off() {
- s3c_gpio_cfgpin(LED_ID1, S3C_GPIO_OUTPUT);
- s3c_gpio_setpull(LED_ID1, S3C_GPIO_PULL_NONE);
- gpio_set_value(LED_ID1, 0);
- }
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。