1. 程式人生 > >imx6ul linux4.1.15 LED驅動配置及heartbeat原始碼分析【轉】

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

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

1)檢視核心配置
[email protected]:~/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相關設定
[email protected]:~/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)查詢驅動
[email protected]:~/freescale/linux-imxgrep−rn“gpio−leds”driversdrivers/leds/leds−gpio.c:237:.compatible=“gpio−leds”,,觸發方式原始碼在trigger目錄下[email protected]−vBox: /freescale/linux−imx/drivers/ledsgrep−rn“gpio−leds”driversdrivers/leds/leds−gpio.c:237:.compatible=“gpio−leds”,,觸發方式原始碼在trigger目錄下

[email protected]−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支援的觸發器和目前設定的觸發器
[email protected]:~# cat /sys/class/leds/user/trigger
[none] rc-feedback nand-disk mmc0 mmc1 timer oneshot heartbeat backlight gpio
[email protected]:~# 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燈也設定為心跳燈。
[email protected]:~# echo heartbeat > /sys/class/leds/user/trigger
此時user燈與cpu燈同時閃爍。不過不是同步的。

再次檢視使用者燈的觸發方式
[email protected]:~# cat /sys/class/leds/user/trigger
none rc-feedback nand-disk mmc0 mmc1 timer oneshot [heartbeat] backlight gpio
已經設定成心跳燈了。

(5)心跳燈控制原始碼
因為比較好奇心跳燈閃的頻率,查看了驅動原始碼。
[email protected]:~/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可以從下面路徑找到定義
[email protected]:~/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];三個長整型資料。
[email protected]:~/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也可以看到
[email protected]:~/freescale/linux-imx$ uptime
11:44:08 up 21:23, 1 user, load average: 0.00, 0.02, 0.00

這三個值是從/proc/loadavg中獲得的。
[email protected]:~/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控制都是選擇灌電流。這樣邏輯上是反向的,低電平亮,高電平滅。
上圖