你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32MP1嵌入式Linux驱动开发指南V1.7——Linux自带的LED灯驱动实验

[复制链接]
STMCU小助手 发布时间:2022-9-27 11:32
Linux自带的LED灯驱动实验$ i3 [! {+ C/ s+ |: k+ D3 e
前面我们都是自己编写LED灯驱动,其实像LED灯这样非常基础的设备驱动,Linux内核已经集成了。Linux内核的LED灯驱动采用platform框架,因此我们只需要按照要求在设备树文件中添加相应的LED节点即可,本章我们就来学习如何使用Linux内核自带的LED驱动来驱动正点原子的STM32MP1开发板上的LED0和LED1这两个LED灯。- z6 \3 `& e  I5 ?! P) K

, _% D+ D  Q4 m4 [' \36.1 Linux内核自带LED驱动使能: K4 a1 i% m2 E: z
上一章节我们编写基于设备树的platform LED灯驱动,其实Linux内核已经自带了LED灯驱动,要使用Linux内核自带的LED灯驱动首先得先配置Linux内核,使能自带的LED灯驱动,输入如下命令打开Linux配置菜单:
/ W  t& E% M, f" b2 S: umake menuconfig
* A2 t* S2 R1 l9 E# @( E# j# v7 ~按照如下路径打开LED驱动配置项:
6 ]5 |) {0 U) M, |2 mDevice Drivers
9 A. N$ x1 e% o, L4 o, g4 eLED Support (NEW_LEDS [=y])* c6 I* M7 r$ ~( {  }
LED Support for GPIO connected LEDs2 G6 t8 m3 j, C. {! l" U
按照上述路径,选择“LED Support for GPIO connected LEDs”,将其编译进Linux内核,也即是在此选项上按下“Y”键,使此选项前面变为“<*>”,如图36.1.1所示:
. m$ j4 C+ ]" u1 A  S8 A
5 Z4 j# V! e8 E9 q 086508567a564fa2ae508e61a941daf7.png
" `+ x( a" Y& w2 y7 M6 j. v- ]. D3 o
9 f0 L8 Q/ a6 Q, s图36.1.1 使能LED灯驱动7 Q" V- c4 u5 `" w
在“LED Support for GPIO connected LEDs”上按下“?”健可以打开此选项的帮助信息,如图36.1.2所示:' B; Z& N# ^: E+ I4 k
0 r( r$ A- B5 U; `  B9 ]
4ee5650acacf43198d5210cca9213131.png
! L* z4 a9 S2 x+ B, }
/ q2 e$ i' a1 b' Z- o6 x4 j图36.1.2 内部LED灯驱动帮助信息" u8 e- W% {! ?& Q
从图36.1.2可以看出,把Linux内部自带的LED灯驱动编译进内核以后,CONFIG_LEDS_GPIO就会等于‘y’,Linux会根据CONFIG_LEDS_GPIO的值来选择如何编译LED灯驱动,如果为‘y’就将其编译进Linux内核。# g& _7 ?8 z6 h; a* W
配置好Linux内核以后退出配置界面,打开.config文件,会找到“CONFIG_LEDS_GPIO=y”这一行,如图36.1.3所示:  |0 U2 ?* v3 z
, a+ \+ B( M, Y8 d: _. W
ee3bf5aa48154498863fdc3195eb3938.png
. d! M! `8 S' V0 p; `! A: Q  z! d! T# d  p: F) p/ j
图36.1.3 .config文件内容
- I( @* N: @! N- n8 e" ]6 J8 `) d重新编译Linux内核,然后使用新编译出来的zImage镜像启动开发板。
# ~2 Y. @! \4 B8 W+ _, o36.2 Linux内核自带LED驱动简介
; @# @. b0 Q; |7 K7 t36.2.1 LED灯驱动框架分析
. @8 a  ~6 q9 G: e/ I, ELED灯驱动文件为/drivers/leds/leds-gpio.c,大家可以打开/drivers/leds/Makefile这个文件,找到如下所示内容:
; Z! U& ~& e9 ?+ d5 b; H" C! r4 s$ j) T' }: B) m. U2 u
  1. 示例代码36.2.1.1 /drivers/leds/Makefile文件代码段
    ( `0 E: M- _* k* m
  2. 1   # SPDX-License-Identifier: GPL-2.03 C4 r9 e" t6 I3 |
  3. 2 & M% y8 `" H% H+ m
  4. 3   # LED Core
    + \* x; H% f# a; A" [
  5. 4   obj-$(CONFIG_NEW_LEDS)                          += led-core.o  H# h/ c( B3 X! t) q
  6. ......& x; v- b: P7 f4 Q. s
  7. 31  obj-$(CONFIG_LEDS_PCA9532)                      += leds-pca9532.o
    5 S% |) J! h  S: q* q
  8. 32  obj-$(CONFIG_LEDS_GPIO_REGISTER)          += leds-gpio-register.o
    8 R& S" i. C) n0 s/ r, Z5 z3 M/ C
  9. 33  obj-$(CONFIG_LEDS_GPIO)                         += leds-gpio.o1 ~' j# R2 b6 P8 H
  10. 34  obj-$(CONFIG_LEDS_LP3944)                       += leds-lp3944.o
    $ M1 k3 b/ m5 u) A  ^/ C* j0 a
  11. ......
复制代码
6 e) b3 E8 s0 O0 c
第33行,如果定义了CONFIG_LEDS_GPIO的话就会编译leds-gpio.c这个文件,在上一小节我们选择将LED驱动编译进Linux内核,在.config文件中就会有“CONFIG_LEDS_GPIO=y”这一行,因此leds-gpio.c驱动文件就会被编译。
& _7 f$ Q3 c& P" y# Z' Z1 f  T7 G接下来我们看一下leds-gpio.c这个驱动文件,找到如下所示内容:
4 ~+ Y0 Y; b. s) |9 r: n" l# S' x0 b
) C8 L( t7 x4 ~' [' `
  1. 示例代码36.2.1.2 leds-gpio.c文件代码段
    & Q% s  l6 |1 p- b
  2. 203 static const struct of_device_id of_gpio_leds_match[] = {
    7 t3 J8 w9 U7 _
  3. 204     { .compatible = "gpio-leds", },, x# u/ K- I4 P6 v; j
  4. 205     {},
    $ k( f9 m* m. e4 f# B2 U. G
  5. 206 };
    ) ?# D. n) y4 ^/ b2 z
  6. 2070 i5 u& u7 O0 f' O. K  ]6 ]
  7. ......
    0 e( O2 _0 y) k2 j% h
  8. 316 static struct platform_driver gpio_led_driver = {0 p% c; B7 h+ z* U, p) y3 Y" j3 A
  9. 317     .probe           = gpio_led_probe,) H. {" N9 R$ r$ o
  10. 318     .shutdown        = gpio_led_shutdown,% ^: w! Z! Q& I
  11. 319     .driver          = {
    4 u& P  t9 |* C6 g. v+ N: u
  12. 320         .name         = "leds-gpio",
    ! ?8 C; m$ a5 {6 I  n
  13. 321         .of_match_table = of_gpio_leds_match,6 P3 c/ [1 J1 P- x. H5 w
  14. 322     },
    ! f, n: @1 q! D
  15. 323 };
    ! Y4 T2 p7 o3 U. v; r9 j* O
  16. 324# A5 p( E7 w( {5 i
  17. 325 module_platform_driver(gpio_led_driver);
复制代码

8 r  ^) \) C; }& }% \) H第203~206行,LED驱动的匹配表,此表只有一个匹配项,compatible内容为“gpio-leds”,因此设备树中的LED灯设备节点的compatible属性值也要为“gpio-leds”,否则设备和驱动匹配不成功,驱动就没法工作。1 c* j5 P# B6 E$ T. d9 p
第316~323行,platform_driver驱动结构体变量,可以看出,Linux内核自带的LED驱动采用了platform框架。第317行可以看出probe函数为gpio_led_probe,因此当驱动和设备匹配成功以后gpio_led_probe函数就会执行。从320行可以看出,驱动名字为“leds-gpio”,因此会在/sys/bus/platform/drivers目录下存在一个名为“leds-gpio”的文件,如图36.2.1.1所示:$ A6 w8 W  u% s( z: x6 v
$ J$ y) Y8 E+ q6 _* d, U
966557407f394a889a2ed7da7ed130bd.png 5 v! K8 O* D  g# u0 J1 ?
" C% C3 V' [0 Z/ d
图36.2.1.1 leds-gpio驱动文件1 w: w$ I( t9 _6 x* h, f6 G
第326行通过module_platform_driver函数向Linux内核注册gpio_led_driver这个platform驱动。/ N+ N/ s0 C" A' Z) R
36.2.2 module_platform_driver函数简析
' `; q! e. W3 Y; q% O/ ~在上一小节中我们知道LED驱动会采用module_platform_driver函数向Linux内核注册platform驱动,其实在Linux内核中会大量采用module_platform_driver来完成向Linux内核注册platform驱动的操作。module_platform_driver定义在include/linux/platform_device.h文件中,为一个宏,定义如下:! c" O1 f! X4 |6 @( g" Q& ?9 [
  1. 示例代码36.2.2.1 module_platform_driver函数  ]8 O4 C% {( D5 A. Q& k8 |
  2. 237 #define module_platform_driver(__platform_driver) \9 \5 |$ r  j9 t# D+ D, e6 r8 W
  3. 238     module_driver(__platform_driver, platform_driver_register, \6 H% |3 \% {6 R9 i1 E+ i/ c& o0 [% [
  4. 239             platform_driver_unregister)
复制代码
. |) w" _5 [: u) F! u2 z& S8 O
可以看出,module_platform_driver依赖module_driver,module_driver也是一个宏,定义在include/linux/device.h文件中,内容如下:5 G/ @. E2 m$ i) U. [9 N; b+ F
- N& f+ B+ ^0 P+ y  `  B
  1. 示例代码36.2.2.2 module_driver函数
    4 B5 F4 B" I, w2 b3 d% A
  2. 1898 #define module_driver(__driver, __register, __unregister, ...) \% B% z- u1 n# x7 ?7 W. D
  3. 1899 static int __init __driver##_init(void) \; V0 S! {# e4 X: f4 v
  4. 1900 { \
    * `% W( h3 p' f( L1 O8 V& @6 k9 s
  5. 1901    return __register(&(__driver) , ##__VA_ARGS__); \
    : V$ C6 \# E/ ^6 g8 {9 G  S
  6. 1902 } \
      o( Q* g( `' ^% w) I
  7. 1903 module_init(__driver##_init); \
    1 `) v& O# N+ H( |+ X
  8. 1904 static void __exit __driver##_exit(void) \
    ( m. d; |( d% L  d
  9. 1905 { \% M, h+ Z: N2 a( v) S9 {
  10. 1906    __unregister(&(__driver) , ##__VA_ARGS__); \
    ! t. B, N3 I$ Y6 \
  11. 1907 } \
    4 }5 l6 c6 a/ M
  12. 1908 module_exit(__driver##_exit);
复制代码
6 X5 A2 N/ M2 K& i! W
借助示例代码36.2.2.1和示例代码36.2.2.2,将:! S! ?5 M8 L4 F" \- C! {2 c
4 C7 P: R0 ~* x$ S
module_platform_driver(gpio_led_driver)  V0 B  v, d9 i2 B3 e) {$ Z4 Z
展开以后就是:
( j' x: h% L: y3 T+ y, }6 \static int __init gpio_led_driver_init(void)
& o1 _' q6 S3 ]: P  I- \. I: B{
. J- V3 s; B( _! J( z, H8 F0 Q& _  Yreturn platform_driver_register (&(gpio_led_driver));
" l. {& a6 s2 I& k9 ^$ ~}4 `$ e' A; i* }) h) ~
module_init(gpio_led_driver_init);7 S6 V" H- R0 A. c0 P
. j# ?& @1 W# ^8 Y  [3 f3 [, k
static void __exit gpio_led_driver_exit(void)
4 _; ^8 v2 z0 ~. ^! `+ K{
; x# C  r1 n3 K  ?; w8 Vplatform_driver_unregister (&(gpio_led_driver) );
$ l7 Z# D8 I- r}% M  u1 O# t2 n1 f4 y& b4 K
module_exit(gpio_led_driver_exit);  D& w2 I6 A$ f: e( b& S2 |
上面的代码不就是标准的注册和删除platform驱动吗?因此module_platform_driver函数的功能就是完成platform驱动的注册和删除。: g) B5 ?" V* u1 h2 P
% M) a: X' I. \. H# X! N
36.2.3 gpio_led_probe函数简析! u8 S/ A9 s/ B3 q+ h* S' r; S1 j# d( R
当驱动和设备匹配以后gpio_led_probe函数就会执行,此函数主要是从设备树中获取LED灯的GPIO信息,缩减后的函数内容如下所示:3 x  R+ S* c; i- d! I

8 b/ c; o  u2 S: g
  1. 示例代码36.2.3.1 gpio_led_probe函数5 d' a# K- Y" N( \" @- U
  2. 256 static int gpio_led_probe(struct platform_device *pdev)) d4 z; V: v; C8 l3 z
  3. 257 {, V7 p' O, B$ _7 G: H1 S: a
  4. 258     struct gpio_led_platform_data *pdata =
    1 `# B' i% S. w* @4 X
  5. dev_get_platdata(&pdev->dev);5 \; d0 G/ f( Z5 L! u- D
  6. 259     struct gpio_leds_priv *priv;8 s$ T, n1 ]9 y( J7 Z
  7. 260     int i, ret = 0;! D7 p7 \* |0 a/ x" u
  8. 261
    7 ]- F1 p. s4 M& Z9 D; x8 J. q/ S0 H; T, P
  9. 262     if (pdata && pdata->num_leds) {         /* 非设备树方式                 */$ d" ^1 l% C8 d+ p+ r8 l) D
  10.             /* 获取platform_device信息 */
    4 t9 P+ d# n' \" a3 j% R2 y
  11. .....7 t3 a4 M+ g" P' E* \
  12. 292     } else {                                        /* 采用设备树              */
    2 X6 x$ W8 ?) G/ z
  13. 293         priv = gpio_leds_create(pdev);+ @  {* ]5 K1 T& k: w( I. ~( K) y; k
  14. 294         if (IS_ERR(priv))
    # w9 B, F: c1 a+ o/ _
  15. 295             return PTR_ERR(priv);
    5 e  a2 v8 E( \/ n, D
  16. 296     }: j+ i0 A9 b6 Z
  17. 2979 ]9 x5 n0 }) o
  18. 298     platform_set_drvdata(pdev, priv);
    6 ?2 E% Z# E8 y) Q8 a
  19. 299
    % u" T$ }8 c/ Q) Q$ K5 S- Z
  20. 300     return 0;
    2 D+ X' s: t( y. k  f
  21. 301 }
复制代码
! \0 E9 t+ A7 s5 }% N
第293~295行,如果使用设备树的话,使用gpio_leds_create函数从设备树中提取设备信息,获取到的LED灯GPIO信息保存在返回值中,gpio_leds_create函数内容如下:' p2 I6 p  }% Z! e& q

: U" I. W; x7 ^" n+ \# r) U
  1. 示例代码36.2.3.2 gpio_leds_create函数
    4 e$ }: j  Q# r2 w! F6 O% }
  2. 134 static struct gpio_leds_priv *gpio_leds_create(struct        platform_device *pdev)) R, \9 q8 L" s' s- H5 v# I. ~
  3. 135 {7 n1 O! c3 E! U4 o: x( J
  4. 136     struct device *dev = &pdev->dev;: C6 W& H* p! a
  5. 137     struct fwnode_handle *child;$ \: ]% M  n( v; b0 n2 [
  6. 138     struct gpio_leds_priv *priv;
    5 d+ P0 n" A8 n5 w" z- a
  7. 139     int count, ret;& j2 Y% [( D" x0 Z" v& y" ]7 r
  8. 140/ x1 o1 n* }: B, ^5 V
  9. 141     count = device_get_child_node_count(dev);  \2 w% e: x+ F# {2 o/ Q
  10. 142     if (!count)! e0 M  ?, y5 z2 {3 k
  11. 143         return ERR_PTR(-ENODEV);4 y4 \7 F9 A) t( N/ v( X
  12. 144
    ) m. w. @( Z% _8 c3 `; A
  13. 145     priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), ' x1 z( P( t0 T: F5 C
  14. GFP_KERNEL);
    0 ~9 b* y* C" b, T, q: b" k
  15. 146     if (!priv). B" P8 j% D. d9 A3 W) v
  16. 147         return ERR_PTR(-ENOMEM);3 {( ^- j; r% Z. V0 g, l# u
  17. 148  R9 ]  ~) M- z5 r; c1 W
  18. 149     device_for_each_child_node(dev, child) {* L9 q6 W9 g& d) _, a
  19. 150         struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];. N2 k* C5 v% ~+ b
  20. 151         struct gpio_led led = {};
    3 ^- l" D( d) _3 I8 i7 N) g7 f
  21. 152         const char *state = NULL;
    $ F# y0 X# ^' c% X# q
  22. 153
    1 N. d5 O+ u. M0 T" i
  23. 154         /*
    8 a# C  m7 x" J4 f
  24. 155          * Acquire gpiod from DT with uninitialized label, which5 }9 {/ {; \9 j8 u5 _( l
  25. 156          * will be updated after LED class device is registered,
    $ L6 F/ j" c2 @3 \. w
  26. 157          * Only then the final LED name is known.
    . a8 _( k6 B+ i$ F8 C8 Q6 A
  27. 158          */: V- o1 j+ \9 F4 q! k
  28. 159         led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, + g  r% I, {, A
  29. child,: ], B, a4 d( Z) M6 L$ d
  30. 160                                  GPIOD_ASIS,
    - [& ~# Z1 m. [6 `
  31. 161                                  NULL);
    3 ]! R. {1 h0 K( a% [7 P4 U
  32. 162         if (IS_ERR(led.gpiod)) {
      D* c4 d2 f3 r. V
  33. 163             fwnode_handle_put(child);, r3 o' T, _4 \  ]& `+ M" i
  34. 164             return ERR_CAST(led.gpiod);
    8 W& g; C: ]2 e4 J
  35. 165         }% n+ u  C$ ~/ w6 z
  36. 166
    6 i6 e% s. V7 p' W+ t
  37. 167         led_dat->gpiod = led.gpiod;
    0 a4 L: N' i( v& A
  38. 168: N! E% ^9 @/ s
  39. 169         fwnode_property_read_string(child, "linux,default-trigger",: K& `4 `. a- H" e+ Q
  40. 170                         &led.default_trigger);
    ) E9 t" s: p: r" B- V/ E) H& ?
  41. 171
    2 I! |5 _' S' t% V5 {* u
  42. 172         if (!fwnode_property_read_string(child, "default-state",
    2 \8 n7 u0 g. j: z" [1 k
  43. 173                          &state)) {: }8 {, S, d+ S$ R( e
  44. 174             if (!strcmp(state, "keep"))8 v* Q# Q' @! w5 q, ?
  45. 175                 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
    ; I/ u- L+ S# I" d
  46. 176             else if (!strcmp(state, "on"))$ J- K$ Y+ k7 e, Q# T" I: e! T: l
  47. 177                 led.default_state = LEDS_GPIO_DEFSTATE_ON;
    9 H+ n7 z* D* ]$ n8 {2 t: G$ I
  48. 178             else3 O# u% G4 s* |# z" _& d2 n
  49. 179                 led.default_state = LEDS_GPIO_DEFSTATE_OFF;4 I$ U. s* x; ~; J4 d4 d4 ^! o
  50. 180         }: D( v- q' q3 f0 F6 R* E# R
  51. 181
    4 }2 w( t8 m/ E, P6 C3 U4 O
  52. 182         if (fwnode_property_present(child, "retain-state-suspended"))* v% `% h* c# A1 K( h+ P
  53. 183             led.retain_state_suspended = 1;
    + S4 ]$ A" n* \  C$ Z
  54. 184         if (fwnode_property_present(child, "retain-state-shutdown"))$ V" F) O2 \6 p0 N
  55. 185             led.retain_state_shutdown = 1;2 r1 {  N6 a0 n! q* p% K# [6 Z
  56. 186         if (fwnode_property_present(child, "panic-indicator"))
      @3 u/ n1 @1 N4 d* w
  57. 187             led.panic_indicator = 1;
    + N6 u+ R: T( ^0 _( V6 I
  58. 188
    7 Y; O8 q1 B- c$ o
  59. 189         ret = create_gpio_led(&led, led_dat, dev, child, NULL);3 G" o. J( I- P  F0 T
  60. 190         if (ret < 0) {
    8 p( r; n" o* ]0 I) y
  61. 191             fwnode_handle_put(child);
    ) B+ J+ t/ g7 {# {0 z6 W: I
  62. 192             return ERR_PTR(ret);, Y! |" A) j8 Q5 v. |* U
  63. 193         }
    : k3 Z2 D" ^$ K5 m5 P1 D
  64. 194         /* Set gpiod label to match the corresponding LED name. */
    / q: ^2 t" N' o( I8 k) p  Q
  65. 195         gpiod_set_consumer_name(led_dat->gpiod,
    2 k# @5 }! _4 M$ f6 D, R! I( Q
  66. 196                     led_dat->cdev.dev->kobj.name);
    ! M, v/ M& z' I3 S3 T" f8 E1 B
  67. 197         priv->num_leds++;
    2 H* F. O9 \4 Y/ \! \$ v$ \
  68. 198     }
    - s' p! n: A, e2 W6 b
  69. 199- p7 a! \; E3 r* C) s4 F2 f
  70. 200     return priv;
    ' m$ V9 T- ]5 w2 l! K9 f8 ~# f
  71. 201 }
复制代码

! |' p/ y( L+ `2 r第141行,调用device_get_child_node_count函数统计子节点数量,一般在设备树中创建一个节点表示LED灯,然后在这个节点下面为每个LED灯创建一个子节点。因此子节点数量也是LED灯的数量。
' d3 O/ p# K( q6 J9 s, y第149行,遍历每个子节点,获取每个子节点的信息。
7 \5 q6 u6 r' C8 H第159行,获取LED灯所使用的GPIO信息。
& F9 A. N- t; M6 E$ z1 c第169~170行,获取“linux,default-trigger”属性值,可以通过此属性设置某个LED灯在Linux系统中的默认功能,比如作为系统心跳指示灯等等。
8 R2 _$ M/ Q& k: `) P! t; w* ^& F- ~% d第172~173行,获取“default-state”属性值,也就是LED灯的默认状态属性。( q5 h4 }7 X& C* w8 W  R3 E, [. s
第189行,调用create_gpio_led函数创建LED相关的io,其实就是设置LED所使用的io为输出之类的。create_gpio_led函数主要是初始化led_dat这个gpio_led_data结构体类型变量,led_dat保存了LED的操作函数等内容。* Z: P1 O: H: s
第195~196行,使用label属性作为LED的名字,led_dat->cdev.dev->kobj.name指向设备树里的LED灯节点下的label属性。
  Q! z" E5 L2 m1 {/ j! ?$ C关于gpio_led_probe函数就分析到这里,gpio_led_probe函数主要功能就是获取LED灯的设备信息,然后根据这些信息来初始化对应的IO,设置为输出等。
  D$ M8 b$ h) i" M# B3 m' c4 E% r8 P) M9 A& x+ B9 u
36.3 设备树节点编写" R- h5 L9 [  s% A& @! l
打开文档Documentation/devicetree/bindings/leds/leds-gpio.txt,此文档详细的讲解了Linux自带驱动对应的设备树节点该如何编写,我们在编写设备节点的时候要注意以下几点:4 I' l+ u2 d, H9 J3 U
①、创建一个节点表示LED灯设备,比如dtsleds,如果板子上有多个LED灯的话每个LED灯都作为dtsleds的子节点。
- l4 `- o+ q( S4 x; u+ B②、dtsleds节点的compatible属性值一定要为“gpio-leds”。* O0 Y1 W. t  @$ s( L
③、设置label属性,此属性为可选,每个子节点都有一个label属性,label属性一般表示LED灯的名字,比如以颜色区分的话就是red、green等等。# a4 S7 |9 y+ r% k+ W* i  i2 E
④、每个子节点必须要设置gpios属性值,表示此LED所使用的GPIO引脚!
; @2 p% k& _/ h' C⑤、可以设置“linux,default-trigger”属性值,也就是设置LED灯的默认功能,查阅Documentation/devicetree/bindings/leds/common.txt这个文档来查看可选功能,比如:
- p7 }, |: p# k+ W4 B, ^backlight:LED灯作为背光。
4 f6 I& _1 ?1 Wdefault-on:LED灯打开。
0 I* G% S# x# V& ]  _; ~heartbeat:LED灯作为心跳指示灯,可以作为系统运行提示灯。
, ]9 ^4 T. P- N: ldisk-activity:LED灯作为磁盘活动指示灯。
9 K# B. q! I$ z" R' iide-disk:LED灯作为硬盘活动指示灯。: _+ o# ~/ I% {4 }
timer:LED灯周期性闪烁,由定时器驱动,闪烁频率可以修改。
; Y+ }3 T& ^1 Y⑥、可以设置“default-state”属性值,可以设置为on、off或keep,为on的时候LED灯默认打开,为off的话LED灯默认关闭,为keep的话LED灯保持当前模式。
* S- H; d0 u* `6 r' d9 s另外还有一些其他的可选属性,比如led-sources、color、function等属性,这些属性的用法在Documentation/devicetree/bindings/leds/common.txt里面有详细的讲解,大家自行查阅。
7 _2 u+ S- w1 T% ^2 O本节实验我们把STM32MP1开发板上的2个LED灯都用上,其中LED0(红色)连接到PI0引脚上,LED1(绿色)连接到PF3。首先是创建这两个LED灯对应的pinctrl节点,直接在示例代码35.1.2.1上修改即可,修改完成以后如下所示:' S! ]6 X, @- o- e. ^+ k, G$ l6 J

! U- b2 E7 f* o0 A% r% B
  1. 示例代码36.3.1 pinctrl子节点7 T% a# z6 ^$ q& i7 D0 h
  2. 1  led_pins_a: gpioled-0 {
    2 A+ X0 C; B9 k2 _5 H$ K! T; i
  3. 2           pins {
    + A( A/ |" |; s2 W' W5 B1 m5 c% g3 G
  4. 3                       pinmux = <STM32_PINMUX('I', 0, GPIO)>,  /* LED0         */
    2 ~/ ]( v% b6 v7 a6 W8 ?6 Q+ h
  5. 4                          <STM32_PINMUX('F', 3, GPIO)>;  /* LED1         */
    8 e. a% m; _" m5 S! w+ m: k
  6. 5                       drive-push-pull;
    * p9 N' j* _" R
  7. 6                       bias-pull-up;
    ) `8 A% m( [( @2 p. w
  8. 7                       output-high;6 v/ Y5 z4 [: M1 o/ h! d- A
  9. 8                       slew-rate = <0>;
    " p7 z8 w7 k* I" i, T
  10. 9           };
    , t& }6 u# W+ e4 x) Q% W
  11. 10 };
复制代码

7 {/ p9 N( V4 B上述代码仅仅只是在示例代码35.1.2.1只添加了第4行,也就是LED1对应的PF3引脚,其他的属性配置没变。/ o3 q7 K% p3 E. c- c" a
最后,根据前面的绑定文档要求添加LED设备子节点,打开stm32mp157d-atk.dts,在“/”根节点下添加如下所示LED灯设备子节点:
# {2 Q( Q8 h4 S& x1 \
& k3 P- ^6 W5 P: y6 X, _7 _
  1. 示例代码36.3.2 dtsleds设备节点+ j" N6 Z/ r5 Y; j
  2. 1  dtsleds {
    6 c0 ?1 _% y9 O3 [1 g. t$ T
  3. 2      compatible = "gpio-leds";
    2 A( i# w& L) A+ x
  4. 3             pinctrl-0 = <&led_pins_a>;8 B8 M: O( \9 r8 W1 G' @
  5. 4
    ! L, \, |# S1 o6 h4 t. h& c
  6. 5           led0 {5 ?( `$ V2 t! |: b
  7. 6                       label = "red";
    + D* \& P0 B7 J  r4 o4 ^: o
  8. 7                       gpios = <&gpioi 0 GPIO_ACTIVE_LOW>;
    " S: A# j1 Z, u3 s9 H) r6 x
  9. 8                       default-state = "off";# F. l- L0 O" |, d* |
  10. 9           };
    : @# Z7 t. t4 E8 [4 a( w
  11. 10   % g2 m# t; X8 S% B# s6 A7 T
  12. 11          led1 {$ e" R+ A# q: m( v; F5 n
  13. 12              label = "green";
    8 F" ]( k! u& ^2 P' l  I, Q
  14. 13              gpios = <&gpiof 3 GPIO_ACTIVE_LOW>;' K  J: s0 f/ l0 H7 m/ h
  15. 14              default-state = "off";
    , a1 c% E! f9 d! [4 l9 B
  16. 15          };5 H: @2 r8 e* {  w. k  J2 ?
  17. 16 };
复制代码
" ?# w8 G/ U7 v& n6 d
第3行,设置LED的pinctrl节点为led_pins_a,也就是示例代码36.3.1。第4~8行是开发板上的LED0,第10~13行是开发板上的LED1。修改完成以后保存并重新编译设备树,然后用新的设备树启动开发板。
8 `% Q& f& \3 x; x
- [9 n; i: `% S; W4 ^- @* E36.4 运行测试  K7 W% d8 k4 u2 f4 P5 b' p/ U
用新的uImage和stm32mp157d-atk.dtb启动开发板,启动以后查看/sys/bus/platform/devices/dtsleds这个目录是否存在,如图36.4.1所示:% D4 k' S* H' {. l

2 o- O& W: @2 i. G 9d6b0d081ca2411bba6e1de13b269d35.png
4 M8 S  t! g" w2 G  o+ [
+ ]# ?1 f7 Y& E( n- }图36.4.1 dtsleds目录
, {8 B  T. b0 r4 ~" I进入到dtsleds/leds目录中,此目录中的内容如图36.4.2所示:+ i! X& S3 m7 o: @, L. z

+ E* `" z( C* l: ] ebbcacad862c42288a90f4e2e13eea2c.png
7 h- t0 z. U) T
; p6 w* _0 S# N; n1 J1 R图36.4.2 leds目录内容
) X; J% c# l' u. {9 V2 K从图36.4.2可以看出,在leds目录下有两个子目录,分别为:green和red,其中green就是LED1,red就是LED0,这两个子目录的名字就是我们在示例代码36.3.2中第5行和第11行设置的label属性值。2 q7 e) V- I: I( w6 W2 D/ d9 u
我们的设置究竟有没有用,最终是要通过测试才能知道的,首先查看一下系统中有没有“sys/class/leds/red/brightness”和“sys/class/leds/green/brightness”这两个文件,这两个文件分别对应LED0和LED1,通过操作这两个文件即可实现LED0和LED1的打开和关闭。
9 F2 A; P* t1 l& r6 j* @% G3 ~& f7 Q% K% e( K* n* K9 ~- t" q
果有的话就输入如下命令打开LED0(RED)和LED1(GREEN)这两个LED灯:2 |( }6 a( \  c; \* Q& q
echo 1 > /sys/class/leds/red/brightness //打开LED0( P$ K8 I' a) E3 W9 ?
echo 1 > /sys/class/leds/green/brightness //打开LED1
* J7 Y. w$ h. }8 N4 }4 c关闭这两个LED灯的命令如下:
6 `- U5 K! p7 Wecho 0 > /sys/class/leds/red/brightness //关闭LED0' L7 X. e+ m5 `% R, n9 N
echo 0 > /sys/class/leds/green/brightness //关闭LED1
, ]- ~$ x! A8 h$ k% \: y/ D如果能正常的打开和关闭两个LED灯话就说明我们Linux内核自带的LED灯驱动工作正常。我们一般会将一个LED灯作为系统指示灯,系统运行正常的话这个LED指示灯就会一闪一闪的。里我们设置LED0作为系统指示灯,在dtsleds/led0这个设备节点中加入“linux,default-trigger”属性信息即可,属性值为“heartbeat”,修改完以后的dtsleds节点内容如下:
" Z9 d; Z* F$ ^1 M; O4 ~" G& @9 ]6 T- H$ G3 a* P) m
  1. 示例代码36.4.1 dtsleds设备节点
    * [: q) i2 |% k0 f
  2. 1  dtsleds {
    7 J9 J. O, {- a2 V/ \4 x: {
  3. 2                  compatible = "gpio-leds";
    6 ]2 J. f& T# z# e
  4. 3                    pinctrl-0 = <&led_pins_a>;
    ) `4 t* P. }. h* ^+ J
  5. 48 [" @. l: S  p$ w/ ^
  6. 5           led0 {' r8 l8 i& `1 P3 E
  7. 6                       label = "red";
    7 _- }4 t; m7 C# V' {
  8. 7                       gpios = <&gpioi 0 GPIO_ACTIVE_LOW>;0 m$ w+ Z  ]9 \. w/ }
  9. 8                       linux,default-trigger = "heartbeat";1 \) L% u' X# V4 V2 h
  10. 9                       default-state = "on";
    $ f  T7 N% A9 b* I
  11. 10           };1 M3 c' f2 }! [$ D7 O/ i" J+ o+ a. B
  12. 11  1 b: a6 R9 a7 v- |7 k
  13. 12          led1 {
      g' J. k4 y8 P; G9 p4 a& M* I+ U
  14. 13              label = "green";( r/ V# \+ g3 x/ \( O8 w, f
  15. 14              gpios = <&gpiof 3 GPIO_ACTIVE_LOW>;
    2 V4 A' j$ M" ^- j% Z% ]+ j
  16. 15              default-state = "off";  a8 }" `# \% F- O8 _
  17. 16          };" ^& l8 E# y+ y" r4 s! A. G% [
  18. 17 };
复制代码

. |' K" @8 x, g! M- W9 n" @第8行,设置LED0为heartbeat。
  g9 x, r2 ?9 `4 T7 x. m; c第9行,默认打开LED0。
" r6 G1 e" F# [2 n. [) \! m, o6 j重新编译设备树并且使用新的设备树启动Linux系统,启动以后LED0就会闪烁,作为系统心跳指示灯,表示系统正在运行。
% _8 z" K& ?# ^0 g5 T2 t: _0 Y; [————————————————7 @! {/ u8 i4 ]/ a2 z. {
版权声明:正点原子  t, o4 p! ]) F2 H& L. A

8 l+ _( n4 B6 v6 s
! y6 q9 d) k2 [. W! s  M" p
收藏 评论0 发布时间:2022-9-27 11:32

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版