imx6ul linux4.1.15 LED驅動配置及heartbeat源碼分析【轉】


本文轉載自:https://blog.csdn.net/u010444107/article/details/78328807

1)查看內核配置
wujun@wj-vBox:~/freescale/linux-imx$ cat arch/arm/configs/imx_v7_defconfig | grep LEDS
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_ONESHOT=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_GPIO=y
已經開啟了LED並且使能了幾種觸發模式:timer oneshot heartbeat backlight gpio

(2)設備樹增加LED相關設定
wujun@wj-vBox:~/freescale/linux-imx$ vi arch/arm/boot/dts/imx6ul-14x14-evk.dts
leds {
compatible = “gpio-leds”;
pinctrl-names = “default”;
pinctrl-0 = <&pinctrl_leds>;

led0: user {
label = "user";
gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
default-state = "off";
};

led1: cpu {
label = "cpu";
gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;
default-state = "on";
linux,default-trigger = "heartbeat";
};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
pinctrl放在 &iomuxc下
pinctrl_leds: ledgrp {
fsl,pins = <
MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x1b0b0
MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1b0b0

;
};

共設定兩個燈,
一個是GPIO5 IO01 用戶燈
一個是GPIO5 IO02 CPU心跳燈

(3)查找驅動
wujun@wj-vBox:~/freescale/linux-imxgrep−rn“gpio−leds”driversdrivers/leds/leds−gpio.c:237:.compatible=“gpio−leds”,,觸發方式源碼在trigger目錄下wujun@wj−vBox: /freescale/linux−imx/drivers/ledsgrep−rn“gpio−leds”driversdrivers/leds/leds−gpio.c:237:.compatible=“gpio−leds”,,觸發方式源碼在trigger目錄下wujun@wj−vBox: /freescale/linux−imx/drivers/leds ls trigger/
built-in.o ledtrig-cpu.c ledtrig-heartbeat.c ledtrig-timer.o
Kconfig ledtrig-cpu.o ledtrig-heartbeat.o ledtrig-transient.c
ledtrig-backlight.c ledtrig-default-on.c ledtrig-ide-disk.c ledtrig-transient.o
ledtrig-backlight.o ledtrig-default-on.o ledtrig-oneshot.c Makefile
ledtrig-camera.c ledtrig-gpio.c ledtrig-oneshot.o modules.builtin
ledtrig-camera.o ledtrig-gpio.o ledtrig-timer.c modules.order
這些的編譯時通過內核配置項來確定的。

(4)啟動系統
通過查看LED的trigger文件,就能知道當前LED支持的觸發器和目前設定的觸發器
root@imx6ulevk:~# cat /sys/class/leds/user/trigger
[none] rc-feedback nand-disk mmc0 mmc1 timer oneshot heartbeat backlight gpio
root@imx6ulevk:~# cat /sys/class/leds/cpu/trigger
none rc-feedback nand-disk mmc0 mmc1 timer oneshot [heartbeat] backlight gpio

user led沒有設定trigger
cpu led設定為heartbeat trigger

設置觸發器也很簡單,使用echo將觸發器名稱寫入trigger文件即可。
寫入的字符串一定是trigger文件已經存在的,否則會提示參數非法。
這里將user燈也設定為心跳燈。
root@imx6ulevk:~# echo heartbeat > /sys/class/leds/user/trigger
此時user燈與cpu燈同時閃爍。不過不是同步的。

再次查看用戶燈的觸發方式
root@imx6ulevk:~# cat /sys/class/leds/user/trigger
none rc-feedback nand-disk mmc0 mmc1 timer oneshot [heartbeat] backlight gpio
已經設定成心跳燈了。

(5)心跳燈控制源碼
因為比較好奇心跳燈閃的頻率,查看了驅動源碼。
wujun@wj-vBox:~/freescale/linux-imx/drivers/leds$ vi trigger/ledtrig-heartbeat.c
找到了控制閃燈時序的代碼。

static void led_heartbeat_function(unsigned long data)
{
struct led_classdev led_cdev = (struct led_classdev ) data;
struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
unsigned long brightness = LED_OFF;
unsigned long delay = 0;

if (unlikely(panic_heartbeats)) {
led_set_brightness(led_cdev, LED_OFF);
return;
}

/* acts like an actual heart beat -- ie thump-thump-pause... */
switch (heartbeat_data->phase) {
case 0:
/*
* The hyperbolic function below modifies the
* heartbeat period length in dependency of the
* current (1min) load. It goes through the points
* f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
*/
heartbeat_data->period = 300 +
(6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
heartbeat_data->period =
msecs_to_jiffies(heartbeat_data->period);
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
brightness = led_cdev->max_brightness;
break;
case 1:
delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
heartbeat_data->phase++;
break;
case 2:
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
brightness = led_cdev->max_brightness;
break;
default:
delay = heartbeat_data->period - heartbeat_data->period / 4 -
msecs_to_jiffies(70);
heartbeat_data->phase = 0;
break;
}

led_set_brightness_async(led_cdev, brightness);
mod_timer(&heartbeat_data->timer, jiffies + delay);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
}

代碼比較簡單,不過里面有個細節
/* acts like an actual heart beat – ie thump-thump-pause… */
這個意思是說,模擬人的心跳,從這點上看,寫這段代碼的工程師是具有追求極致的精神。心跳燈都要模擬心跳的頻率。
另外一個點就是周期的計算方法。
/*
* The hyperbolic function below modifies the
* heartbeat period length in dependency of the
* current (1min) load. It goes through the points
* f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
*/
heartbeat_data->period = 300 +
(6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
從代碼的意思上看,是根據當前的系統load來決定周期。
如果系統load是0,周期就是1260,其他類推。
300 + (6270 << 11) / (5 * 0) + (7 << 11) = 300 + 6270 / 7 = 300 + 960 = 1260;

其中FSHIFT可以從下面路徑找到定義
wujun@wj-vBox:~/freescale/linux-imx/include/linux$ grep -rn “FSHIFT” .
./sched.h:153:#define FSHIFT 11 /* nr of bits of precision */
這個是因為linux內核避免使用浮點運算所以用整數來計算。

linux內核定義了unsigned long avenrun[3];三個長整型數據。
wujun@wj-vBox:~/freescale/linux-imx$ vi ./kernel/sched/proc.c +61
61 unsigned long avenrun[3];
在32位的cpu上,long對應的就是32bit,其中低11位用於存放負載的小數部分,高21位用於存放整數部分。
之所以是三個變量,是因為內核要記錄cpu的load有三個變量,分別是1min,5min和15min。
運行top的時候能夠看到這三個變量的值。
load average: 0.00, 0.02, 0.00

運行uptime也可以看到
wujun@wj-vBox:~/freescale/linux-imx$ uptime
11:44:08 up 21:23, 1 user, load average: 0.00, 0.02, 0.00

這三個值是從/proc/loadavg中獲得的。
wujun@wj-vBox:~/freescale/linux-imx$ cat /proc/loadavg
0.00 0.02 0.00 1/167 5038

得出結論是在cpu load為0的情況下,閃燈周期是1260ms。
總共閃兩次,每次亮70ms,兩次亮之間的時間差是周期的1/4.
也就是在cpu load = 0情況下:
|70 | |70 |
| 415ms |
| 415ms | 415ms | 415ms | 415ms |

(6)最后用示波器看一下波形是否如預期。
因為GPIO的驅動能力有限,一般LED控制都是選擇灌電流。這樣邏輯上是反向的,低電平亮,高電平滅。
上圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM