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

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

[复制链接]
STMCU小助手 发布时间:2022-9-27 11:32
Linux自带的LED灯驱动实验
4 ?5 i3 ?- G' x: f# k  j- G前面我们都是自己编写LED灯驱动,其实像LED灯这样非常基础的设备驱动,Linux内核已经集成了。Linux内核的LED灯驱动采用platform框架,因此我们只需要按照要求在设备树文件中添加相应的LED节点即可,本章我们就来学习如何使用Linux内核自带的LED驱动来驱动正点原子的STM32MP1开发板上的LED0和LED1这两个LED灯。: ^; z3 C8 c3 D' z3 p+ ~! T

, k+ W8 Y1 H9 r2 {36.1 Linux内核自带LED驱动使能& c' p0 W6 y: y" Z  R
上一章节我们编写基于设备树的platform LED灯驱动,其实Linux内核已经自带了LED灯驱动,要使用Linux内核自带的LED灯驱动首先得先配置Linux内核,使能自带的LED灯驱动,输入如下命令打开Linux配置菜单:
& F4 I# l9 F" cmake menuconfig' I5 ]8 o" L+ @* B; ?
按照如下路径打开LED驱动配置项:
1 }- U: E: h8 J2 Q+ jDevice Drivers
! `0 o- O; y5 W  ALED Support (NEW_LEDS [=y])
/ k2 E* @  R* b: Y# NLED Support for GPIO connected LEDs$ R- z0 z1 f* N  ]3 z* W
按照上述路径,选择“LED Support for GPIO connected LEDs”,将其编译进Linux内核,也即是在此选项上按下“Y”键,使此选项前面变为“<*>”,如图36.1.1所示:
$ R. _2 B8 @+ F) Q. O- M
+ j4 ]% j* ]- q& x3 J0 u; D 086508567a564fa2ae508e61a941daf7.png + u7 L3 G: H1 B1 m/ U
! K/ Q! `, Y0 S! e; J; Z( w
图36.1.1 使能LED灯驱动4 `7 e0 W4 {' _4 e3 g! Z  {
在“LED Support for GPIO connected LEDs”上按下“?”健可以打开此选项的帮助信息,如图36.1.2所示:
- b2 A0 @; f1 s( H$ W
0 x/ T9 U  O" X6 h0 x/ W 4ee5650acacf43198d5210cca9213131.png
4 U! G6 O* v3 y6 W* _- r7 V) K- X5 Y, D; B8 q% ]. f
图36.1.2 内部LED灯驱动帮助信息1 [& R/ d+ `1 ?; G; }; x- e
从图36.1.2可以看出,把Linux内部自带的LED灯驱动编译进内核以后,CONFIG_LEDS_GPIO就会等于‘y’,Linux会根据CONFIG_LEDS_GPIO的值来选择如何编译LED灯驱动,如果为‘y’就将其编译进Linux内核。4 `( ?7 Y& D' _) ]2 a
配置好Linux内核以后退出配置界面,打开.config文件,会找到“CONFIG_LEDS_GPIO=y”这一行,如图36.1.3所示:
4 A& r! P4 v0 V- D, Z( L
3 V: V+ ~' q$ S6 f3 E ee3bf5aa48154498863fdc3195eb3938.png
0 O% S$ b4 J  w0 w0 v$ \' L* Q0 l
  T: p4 m' X" I) G2 B图36.1.3 .config文件内容
. h8 t, u  Z5 H# t重新编译Linux内核,然后使用新编译出来的zImage镜像启动开发板。
2 Z: b- z. `6 J" T36.2 Linux内核自带LED驱动简介
0 Z0 s7 p8 s3 e# L/ X# W) k9 r7 i36.2.1 LED灯驱动框架分析1 f4 B8 X9 m. I) H* ]
LED灯驱动文件为/drivers/leds/leds-gpio.c,大家可以打开/drivers/leds/Makefile这个文件,找到如下所示内容:' l! E) Q) W& i, w0 v( z( l" |
2 X* R, X& w( z- l; G* O- y( g
  1. 示例代码36.2.1.1 /drivers/leds/Makefile文件代码段
    9 C- ]/ M$ r" |7 ^
  2. 1   # SPDX-License-Identifier: GPL-2.07 W6 H  k- T$ a* h  K
  3. 2 0 |8 G3 m/ o+ x$ e  ~. M) o: y
  4. 3   # LED Core* @% C( o7 ^: [) I( x; S6 _
  5. 4   obj-$(CONFIG_NEW_LEDS)                          += led-core.o
    # `5 T1 u/ {: W( }) P. l& C
  6. ......
    % J5 y) A  S! X7 H+ o6 c( d
  7. 31  obj-$(CONFIG_LEDS_PCA9532)                      += leds-pca9532.o
    3 ?4 o5 y% p; L0 G* ?1 O, a' J
  8. 32  obj-$(CONFIG_LEDS_GPIO_REGISTER)          += leds-gpio-register.o
    8 C5 m/ q; }/ Z6 i! J' L
  9. 33  obj-$(CONFIG_LEDS_GPIO)                         += leds-gpio.o
    9 b8 k# v$ i/ r% g
  10. 34  obj-$(CONFIG_LEDS_LP3944)                       += leds-lp3944.o! p, _& a. a6 |: J: E9 }7 @
  11. ......
复制代码
3 h. ?5 Y& m% c& ^
第33行,如果定义了CONFIG_LEDS_GPIO的话就会编译leds-gpio.c这个文件,在上一小节我们选择将LED驱动编译进Linux内核,在.config文件中就会有“CONFIG_LEDS_GPIO=y”这一行,因此leds-gpio.c驱动文件就会被编译。2 P% @3 ^1 |8 \
接下来我们看一下leds-gpio.c这个驱动文件,找到如下所示内容:2 P/ e, w6 }/ W0 I5 K% k, e

; z# ~& Z! d3 I; ~. O
  1. 示例代码36.2.1.2 leds-gpio.c文件代码段
    1 {0 \6 D8 I9 Q5 D  J5 Z6 v8 t
  2. 203 static const struct of_device_id of_gpio_leds_match[] = {, `/ v. |2 q) \* Y  J$ `- a: u
  3. 204     { .compatible = "gpio-leds", },
    ; Q- i8 T2 i; G" F; X
  4. 205     {},5 y. V; u/ P! M7 M  {' W
  5. 206 };
    " m9 T4 S8 Q/ i, _
  6. 2077 R! R7 z! O" u6 j. C* _+ Y
  7. ......
    " y  K& B- u5 f& |; F0 r2 u
  8. 316 static struct platform_driver gpio_led_driver = {7 b) t* `" M% C
  9. 317     .probe           = gpio_led_probe,
    3 l9 S3 L% q8 ?( S% _, ?+ N, n! S
  10. 318     .shutdown        = gpio_led_shutdown,
    7 W% R& B; G# A6 Y
  11. 319     .driver          = {2 F4 y, ?# r3 P
  12. 320         .name         = "leds-gpio",
    # v' `& L- s2 Q6 j6 J
  13. 321         .of_match_table = of_gpio_leds_match,6 P) U$ G$ W. J$ c$ T' Z- ~. t8 U
  14. 322     },
    5 I6 n  \9 x/ w. C" a
  15. 323 };) Z' `+ I0 z$ E( Q8 e: b4 h, B
  16. 324
    9 g& i) f1 R. O7 i
  17. 325 module_platform_driver(gpio_led_driver);
复制代码
# w2 f! ?( k8 w2 S4 e" v+ K9 L
第203~206行,LED驱动的匹配表,此表只有一个匹配项,compatible内容为“gpio-leds”,因此设备树中的LED灯设备节点的compatible属性值也要为“gpio-leds”,否则设备和驱动匹配不成功,驱动就没法工作。* d. P/ z9 I; V- [; r) Q/ d0 u
第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所示:5 y4 j4 \4 E4 i* v' f

: Y1 w/ j, V0 E( |4 D' K 966557407f394a889a2ed7da7ed130bd.png ) K% b; I3 q& l2 F. G: _: \
- D% c) v' E) O6 J: X/ M  L
图36.2.1.1 leds-gpio驱动文件
9 U/ X$ M' y( E, ?6 n3 O第326行通过module_platform_driver函数向Linux内核注册gpio_led_driver这个platform驱动。
- p, C) ?% \2 x/ g- j3 U36.2.2 module_platform_driver函数简析
# L' Q, x  {6 j在上一小节中我们知道LED驱动会采用module_platform_driver函数向Linux内核注册platform驱动,其实在Linux内核中会大量采用module_platform_driver来完成向Linux内核注册platform驱动的操作。module_platform_driver定义在include/linux/platform_device.h文件中,为一个宏,定义如下:) |0 S5 ]" S$ o' e
  1. 示例代码36.2.2.1 module_platform_driver函数
    " z7 @+ x, c8 i0 O
  2. 237 #define module_platform_driver(__platform_driver) \
    7 y" s2 H, Y; G/ l1 z
  3. 238     module_driver(__platform_driver, platform_driver_register, \
    ' l* \  b4 V. p. p) B
  4. 239             platform_driver_unregister)
复制代码

4 g% b4 W* m' k: L& p$ e可以看出,module_platform_driver依赖module_driver,module_driver也是一个宏,定义在include/linux/device.h文件中,内容如下:
! J) O* b. K9 x' z* _
0 U8 b1 P8 y7 x& r9 d# c
  1. 示例代码36.2.2.2 module_driver函数7 G  v2 J$ B8 h1 X: }( B8 z
  2. 1898 #define module_driver(__driver, __register, __unregister, ...) \
    # a8 d4 H' P2 Z6 `! w4 R8 F
  3. 1899 static int __init __driver##_init(void) \
    2 A! d5 d4 |  }1 F7 Z4 c- l
  4. 1900 { \
    2 `1 U, J$ g9 u! {1 r
  5. 1901    return __register(&(__driver) , ##__VA_ARGS__); \
    - W1 ]3 i; A5 s" K  Z
  6. 1902 } \
    9 S; o+ c1 z2 d
  7. 1903 module_init(__driver##_init); \
    1 o) e- g; m( x* k2 o1 _. \
  8. 1904 static void __exit __driver##_exit(void) \
    ; l7 a0 G+ F) Z+ t# V
  9. 1905 { \
    . ^$ H5 w3 ~% ]2 F& h
  10. 1906    __unregister(&(__driver) , ##__VA_ARGS__); \7 c: {3 t$ G% z. z  W5 l
  11. 1907 } \
    , \3 Y# Z2 v. P: r8 Y0 Y
  12. 1908 module_exit(__driver##_exit);
复制代码
* C$ v( q' J0 I8 V! Y5 k
借助示例代码36.2.2.1和示例代码36.2.2.2,将:9 L4 S0 A) Y  z$ k. J# Y
0 M5 S8 H/ R$ I9 U2 Z
module_platform_driver(gpio_led_driver)
: X# w6 R2 T% k& V5 J展开以后就是:$ P( [$ _% C1 n( }' ~/ N
static int __init gpio_led_driver_init(void)
3 F1 \7 w# a; f; X  c2 Y{
4 e9 H0 ^9 y0 f8 Greturn platform_driver_register (&(gpio_led_driver));" I# U3 q' ~7 z$ ^4 i% I# I- p
}
- h, @: E0 h$ V1 c  V' j! F) Vmodule_init(gpio_led_driver_init);- u: b; f5 d2 g
% Q0 T/ N) I! O6 D
static void __exit gpio_led_driver_exit(void), q" E. u/ V% l  T' v
{
: m8 ~8 ?6 I- z" L7 Iplatform_driver_unregister (&(gpio_led_driver) );3 `/ r  R. z$ S+ P. |
}0 E7 v, @) z7 M5 K
module_exit(gpio_led_driver_exit);
' K% L& G2 L, U0 N上面的代码不就是标准的注册和删除platform驱动吗?因此module_platform_driver函数的功能就是完成platform驱动的注册和删除。
6 t( T2 @3 d. y. d& \% \$ z6 O( l2 T
36.2.3 gpio_led_probe函数简析
2 V* x) @% S5 ?+ @. _4 K当驱动和设备匹配以后gpio_led_probe函数就会执行,此函数主要是从设备树中获取LED灯的GPIO信息,缩减后的函数内容如下所示:
) C) ~: m+ i( q+ {' j& f' p
  X1 m, |6 U. C, r
  1. 示例代码36.2.3.1 gpio_led_probe函数4 a3 f( p" {. ^
  2. 256 static int gpio_led_probe(struct platform_device *pdev)& s- ^* _/ N1 l
  3. 257 {
    8 i- Y/ \# @( u. B
  4. 258     struct gpio_led_platform_data *pdata = 7 t# i9 q* q1 @$ x
  5. dev_get_platdata(&pdev->dev);
    . ~) b7 P3 B, r' b! n7 v
  6. 259     struct gpio_leds_priv *priv;# F  x5 v1 g9 W8 ~6 L- @/ A) |
  7. 260     int i, ret = 0;
      b3 ]' o8 O. ?+ ?
  8. 261. J- o+ q0 e# H) g, x
  9. 262     if (pdata && pdata->num_leds) {         /* 非设备树方式                 */2 f6 J1 z6 D. E5 D9 `% ^
  10.             /* 获取platform_device信息 */
    7 P0 z/ v6 h, M0 s+ R
  11. .....+ D  h( n0 h: d' C
  12. 292     } else {                                        /* 采用设备树              */
    8 H# r+ m. B( V8 F7 |& R! U7 S
  13. 293         priv = gpio_leds_create(pdev);
    ! X9 O% h! S5 z' D
  14. 294         if (IS_ERR(priv))
    " y2 Z7 W6 \. b# H! ?
  15. 295             return PTR_ERR(priv);
    : q' l/ N" O3 L. e
  16. 296     }
      {/ W9 H: G, i4 k" b* k) ~
  17. 297
    + Q9 K' _+ l7 r$ Y8 ^3 l/ E& E
  18. 298     platform_set_drvdata(pdev, priv);
    % G0 K  D3 B- M8 n! Y# b
  19. 299( W5 c& o- [0 a( b( J
  20. 300     return 0;# ]9 m( U1 V# P9 ?! I
  21. 301 }
复制代码

5 G1 ]4 B- S3 @0 s. W第293~295行,如果使用设备树的话,使用gpio_leds_create函数从设备树中提取设备信息,获取到的LED灯GPIO信息保存在返回值中,gpio_leds_create函数内容如下:/ F, M. L, n, F" }8 z! V9 C9 I
* [  G8 n" r7 P% L* a. r9 a, p
  1. 示例代码36.2.3.2 gpio_leds_create函数. N6 ~) T3 ]2 K2 f' h
  2. 134 static struct gpio_leds_priv *gpio_leds_create(struct        platform_device *pdev)1 [% E6 f3 m8 `# X7 c. o2 _5 f
  3. 135 {
    + \$ @4 E+ D) B  d) }2 e3 `  F& E0 X+ K
  4. 136     struct device *dev = &pdev->dev;! G) r6 C# h7 M: X
  5. 137     struct fwnode_handle *child;& t0 n7 f. _9 w5 Z
  6. 138     struct gpio_leds_priv *priv;
    ; k5 p3 @! m) k6 f
  7. 139     int count, ret;
    + k  Z$ Z. P: i$ ]- m: F2 R
  8. 1408 ?6 T' x5 Q0 i8 t: D
  9. 141     count = device_get_child_node_count(dev);
      {$ q! a8 L! h9 h
  10. 142     if (!count)
    6 O7 [& O# z  J4 t, T7 m% n
  11. 143         return ERR_PTR(-ENODEV);
      p: F+ `6 ?# L8 X& W
  12. 144. t; ^7 @# e, L% H; M
  13. 145     priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), ! y7 S0 ]8 `' \$ p. D9 r9 P3 o
  14. GFP_KERNEL);
    ' f2 J: x* n8 ?+ H* ?# V
  15. 146     if (!priv)
    3 }& D' I6 b% j5 l0 |& I& _! ]1 [
  16. 147         return ERR_PTR(-ENOMEM);
    5 ^0 Q# b5 ]1 i$ H* `/ j
  17. 148
    6 X. o/ H+ Q. L: @1 c- v4 K0 u
  18. 149     device_for_each_child_node(dev, child) {
    ) e  ^( ^- u! V( G
  19. 150         struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
    " T, U+ d7 K; _0 Y( L  D" H; B
  20. 151         struct gpio_led led = {};( u* u1 A! I  L7 W4 q4 \
  21. 152         const char *state = NULL;
    ) E8 P- Q6 t- j9 t4 d4 c6 k( q
  22. 1539 o8 t2 V3 `* P( J# `
  23. 154         /*
    ' W1 f5 ?  ~& Z9 X
  24. 155          * Acquire gpiod from DT with uninitialized label, which
    & x' p2 {, h& _4 d
  25. 156          * will be updated after LED class device is registered,
    9 |5 g1 ]" g2 D( [2 ]+ r
  26. 157          * Only then the final LED name is known.$ y: a: X5 |" w
  27. 158          */
    1 {7 d% B" g% e$ A
  28. 159         led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, 9 P/ n$ p3 }5 t2 e, L6 C) R2 F
  29. child,
    2 h! g2 @5 o/ o) f
  30. 160                                  GPIOD_ASIS,
    ! g8 h! D, R3 P8 R5 m  W
  31. 161                                  NULL);" [: n$ X: [0 }: b
  32. 162         if (IS_ERR(led.gpiod)) {
    ' t* J7 c! [$ S$ u" {
  33. 163             fwnode_handle_put(child);* h7 ~4 ^7 y% ?, v
  34. 164             return ERR_CAST(led.gpiod);
    - B' V7 m/ t8 }" c( G( l; c
  35. 165         }  j) Y2 S0 F3 ?) @4 s% J
  36. 166" W( G/ F2 T7 m9 A4 S# ?
  37. 167         led_dat->gpiod = led.gpiod;4 W# }: S) j5 _* e+ e
  38. 168/ q% z9 ]# o7 x" O$ `2 v) U
  39. 169         fwnode_property_read_string(child, "linux,default-trigger",; }; C! ?4 g+ ~
  40. 170                         &led.default_trigger);+ a4 w# r  w, _' d, f, ?/ W, K
  41. 171
    - J" Q- S. R- N2 U  p" \
  42. 172         if (!fwnode_property_read_string(child, "default-state",; o; E- @1 G" _0 Z
  43. 173                          &state)) {8 r! F' x7 V# P6 |, U# d
  44. 174             if (!strcmp(state, "keep"))
    - K2 `3 H- \7 w1 M( {) z$ @
  45. 175                 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
    + Y" G& L: F, _  w$ x+ i- P
  46. 176             else if (!strcmp(state, "on"))/ P, L7 l! Q( c  _2 ^
  47. 177                 led.default_state = LEDS_GPIO_DEFSTATE_ON;; P+ y1 [& f9 r) g
  48. 178             else! h' ~5 i# m9 a, k; e! }$ f& `
  49. 179                 led.default_state = LEDS_GPIO_DEFSTATE_OFF;5 H' ^. K2 E" D4 u8 |
  50. 180         }- k& d, L- [/ w9 c1 s5 D; |) [+ I
  51. 181
    ( g7 O; U" b# ~* i6 N
  52. 182         if (fwnode_property_present(child, "retain-state-suspended")): J! ^; R0 D0 ~& d2 w7 b' j, l
  53. 183             led.retain_state_suspended = 1;" \0 B4 q3 G. i, T
  54. 184         if (fwnode_property_present(child, "retain-state-shutdown"))  C  }! _3 I+ i% e& e$ t
  55. 185             led.retain_state_shutdown = 1;+ m1 j! o" m& H  I3 u6 E
  56. 186         if (fwnode_property_present(child, "panic-indicator"))
    1 n% I# j0 \) S3 m
  57. 187             led.panic_indicator = 1;  b! |  }- u& D2 f) Y
  58. 188
    , w& C2 [4 p( U" l$ k  M: I
  59. 189         ret = create_gpio_led(&led, led_dat, dev, child, NULL);
    . a; ?- n9 z6 [: i5 U! K4 j
  60. 190         if (ret < 0) {
      K+ r6 |" H0 R# Y
  61. 191             fwnode_handle_put(child);
    " b' T% ^: q% w; y3 g8 F( |
  62. 192             return ERR_PTR(ret);
    4 M+ E& c. e  z) q' T
  63. 193         }
    - x* W. d2 D) R6 n+ @. @
  64. 194         /* Set gpiod label to match the corresponding LED name. */
    1 w4 B7 `0 V8 g* L
  65. 195         gpiod_set_consumer_name(led_dat->gpiod,& P$ a* G: e' ~/ g
  66. 196                     led_dat->cdev.dev->kobj.name);' O9 l# X  T& A, q3 W5 d% W
  67. 197         priv->num_leds++;
    & N) U, `; Y) R" r( q
  68. 198     }
    % T# l1 j+ R7 d; w4 }$ _6 W; z
  69. 199% }7 H: d0 ]. ^; R( i7 ^
  70. 200     return priv;2 V2 X. r' |! n. P( ~* S0 }
  71. 201 }
复制代码
: I5 l& Q7 N3 L
第141行,调用device_get_child_node_count函数统计子节点数量,一般在设备树中创建一个节点表示LED灯,然后在这个节点下面为每个LED灯创建一个子节点。因此子节点数量也是LED灯的数量。
  H! s* F& p1 x7 G第149行,遍历每个子节点,获取每个子节点的信息。. A6 r3 B/ N! b! S; W8 H
第159行,获取LED灯所使用的GPIO信息。2 L* g4 S% G, q; P' ?. l4 O5 i. y
第169~170行,获取“linux,default-trigger”属性值,可以通过此属性设置某个LED灯在Linux系统中的默认功能,比如作为系统心跳指示灯等等。/ r  d% O3 E2 ^1 V( _. P7 F
第172~173行,获取“default-state”属性值,也就是LED灯的默认状态属性。
% |4 P- v4 @. `! f, u2 R第189行,调用create_gpio_led函数创建LED相关的io,其实就是设置LED所使用的io为输出之类的。create_gpio_led函数主要是初始化led_dat这个gpio_led_data结构体类型变量,led_dat保存了LED的操作函数等内容。- V# n+ B- k8 t" }
第195~196行,使用label属性作为LED的名字,led_dat->cdev.dev->kobj.name指向设备树里的LED灯节点下的label属性。0 R& [) c; _# ?3 `  K5 c
关于gpio_led_probe函数就分析到这里,gpio_led_probe函数主要功能就是获取LED灯的设备信息,然后根据这些信息来初始化对应的IO,设置为输出等。
+ _  ~2 L$ \( m: X; t# ^
! l3 b7 P; b$ k) E+ d4 C  O36.3 设备树节点编写6 q" w9 k# u( P4 P7 H6 n& S# W; S
打开文档Documentation/devicetree/bindings/leds/leds-gpio.txt,此文档详细的讲解了Linux自带驱动对应的设备树节点该如何编写,我们在编写设备节点的时候要注意以下几点:
; x) w- N6 U3 s) @+ p①、创建一个节点表示LED灯设备,比如dtsleds,如果板子上有多个LED灯的话每个LED灯都作为dtsleds的子节点。! n* ]+ s3 n) I$ q  |
②、dtsleds节点的compatible属性值一定要为“gpio-leds”。
$ y$ D9 {# X( Q③、设置label属性,此属性为可选,每个子节点都有一个label属性,label属性一般表示LED灯的名字,比如以颜色区分的话就是red、green等等。
# K3 E5 Z" D( U; \& R④、每个子节点必须要设置gpios属性值,表示此LED所使用的GPIO引脚!$ r; f* m" b) B) p  u1 F! a  `
⑤、可以设置“linux,default-trigger”属性值,也就是设置LED灯的默认功能,查阅Documentation/devicetree/bindings/leds/common.txt这个文档来查看可选功能,比如:, g4 z/ T& z2 e, R$ O1 c8 i: ?
backlight:LED灯作为背光。
+ n+ v7 q( p# X1 n8 l) K2 {" e. Tdefault-on:LED灯打开。$ n. |$ S8 a; C9 K
heartbeat:LED灯作为心跳指示灯,可以作为系统运行提示灯。# l5 K: a# T3 w" K% c
disk-activity:LED灯作为磁盘活动指示灯。
4 H& ^$ a  A' i% R0 E6 Fide-disk:LED灯作为硬盘活动指示灯。5 S/ ~" |& P* ?- f$ }6 _
timer:LED灯周期性闪烁,由定时器驱动,闪烁频率可以修改。; X$ }5 F. L1 P& q& V/ v# \
⑥、可以设置“default-state”属性值,可以设置为on、off或keep,为on的时候LED灯默认打开,为off的话LED灯默认关闭,为keep的话LED灯保持当前模式。
3 l. p3 @# t- k% g" |另外还有一些其他的可选属性,比如led-sources、color、function等属性,这些属性的用法在Documentation/devicetree/bindings/leds/common.txt里面有详细的讲解,大家自行查阅。& G$ k1 D) V  N. n( x+ ]' n
本节实验我们把STM32MP1开发板上的2个LED灯都用上,其中LED0(红色)连接到PI0引脚上,LED1(绿色)连接到PF3。首先是创建这两个LED灯对应的pinctrl节点,直接在示例代码35.1.2.1上修改即可,修改完成以后如下所示:: h, _3 C8 F" k5 T' f& W: t
* h0 N4 ~5 `$ r( W- j5 B
  1. 示例代码36.3.1 pinctrl子节点
    6 I  \4 L. c: \
  2. 1  led_pins_a: gpioled-0 {- K$ @/ Y! U0 K" @
  3. 2           pins {$ L6 L$ p8 w  [- r, E$ e
  4. 3                       pinmux = <STM32_PINMUX('I', 0, GPIO)>,  /* LED0         */$ ?5 w3 x0 B: y5 J% T
  5. 4                          <STM32_PINMUX('F', 3, GPIO)>;  /* LED1         */+ @9 Y! L4 a9 J! V
  6. 5                       drive-push-pull;
    / n0 h2 i3 q% c# o8 c# m
  7. 6                       bias-pull-up;
    0 {& F# I& Q/ x9 H$ w7 `+ q
  8. 7                       output-high;
    7 g1 s) F( m- y9 Q2 \
  9. 8                       slew-rate = <0>;% ]4 F9 K  k, D! n4 P: b
  10. 9           };
    6 q) x0 P* b! t9 K& M
  11. 10 };
复制代码
. I" T/ e% p) _4 t+ ]+ D0 j7 h" [6 f
上述代码仅仅只是在示例代码35.1.2.1只添加了第4行,也就是LED1对应的PF3引脚,其他的属性配置没变。
1 U/ H$ `4 `* h8 D" F最后,根据前面的绑定文档要求添加LED设备子节点,打开stm32mp157d-atk.dts,在“/”根节点下添加如下所示LED灯设备子节点:
9 Q. Q$ R6 K* l, J4 o: B
/ k. x" t* @( j- n; C1 e
  1. 示例代码36.3.2 dtsleds设备节点5 k) ?& a% [( ^& C8 }
  2. 1  dtsleds {- o# \6 _! t! p2 g- N
  3. 2      compatible = "gpio-leds";  Q/ c3 `. \3 s. M/ ?
  4. 3             pinctrl-0 = <&led_pins_a>;
    2 Q% X& d3 Y+ K7 T2 S' H' b2 J6 L7 O4 D
  5. 4& L# p1 r4 p7 ~
  6. 5           led0 {
    ) U' S# s2 K, A9 t  h6 F+ c
  7. 6                       label = "red";
    ! p8 V7 \5 @$ v5 F$ q9 w- V
  8. 7                       gpios = <&gpioi 0 GPIO_ACTIVE_LOW>;
    * H2 |5 y8 q3 v% k9 V2 `
  9. 8                       default-state = "off";
    4 \  c! g* u) S% @/ s$ u6 _
  10. 9           };+ F5 ?% s9 @3 Q* L$ C
  11. 10   
    ( `% Z6 |* j  }9 d
  12. 11          led1 {
    + T& N/ N2 o( C
  13. 12              label = "green";
    ; m8 s, f) X8 V! Q! @) T2 d6 z
  14. 13              gpios = <&gpiof 3 GPIO_ACTIVE_LOW>;
    ) V) `5 I9 T7 T" y( Q
  15. 14              default-state = "off";
    4 S% j  C" C% d' W9 i
  16. 15          };# U/ }! J) @0 {2 f, [
  17. 16 };
复制代码
+ M- ?7 o+ Q; y; X! ~* X
第3行,设置LED的pinctrl节点为led_pins_a,也就是示例代码36.3.1。第4~8行是开发板上的LED0,第10~13行是开发板上的LED1。修改完成以后保存并重新编译设备树,然后用新的设备树启动开发板。
% R  f- C# f3 u4 x1 N! ?' d3 O5 X, I2 S
36.4 运行测试
3 t' }  k. C1 m& ]- n+ P3 ~用新的uImage和stm32mp157d-atk.dtb启动开发板,启动以后查看/sys/bus/platform/devices/dtsleds这个目录是否存在,如图36.4.1所示:- T! [4 f1 x% O, N
' s7 v3 l+ z8 z# X8 B: p2 j
9d6b0d081ca2411bba6e1de13b269d35.png
  E0 P# z) c8 K0 o# R  R$ P! b7 B7 e+ {- B4 `' S, O
图36.4.1 dtsleds目录
8 w9 Y. M: C9 @& p( _! Y9 P. {! o进入到dtsleds/leds目录中,此目录中的内容如图36.4.2所示:
+ H  ?3 z" O7 |9 N" P4 U4 t9 I7 m! S, M% n7 b% u& {
ebbcacad862c42288a90f4e2e13eea2c.png 0 |8 l; u& f/ {+ Q  W1 ^) g

+ g( V8 F: d% g2 N图36.4.2 leds目录内容% c( S0 K# e% R- j$ S
从图36.4.2可以看出,在leds目录下有两个子目录,分别为:green和red,其中green就是LED1,red就是LED0,这两个子目录的名字就是我们在示例代码36.3.2中第5行和第11行设置的label属性值。- I, w/ I, h3 U; T8 F& Q9 j4 U( h
我们的设置究竟有没有用,最终是要通过测试才能知道的,首先查看一下系统中有没有“sys/class/leds/red/brightness”和“sys/class/leds/green/brightness”这两个文件,这两个文件分别对应LED0和LED1,通过操作这两个文件即可实现LED0和LED1的打开和关闭。
- ~7 x; F% {/ C* @
) m9 o0 |0 d" D4 p' M$ H果有的话就输入如下命令打开LED0(RED)和LED1(GREEN)这两个LED灯:) V3 {2 k, G$ {- s  h: n+ D
echo 1 > /sys/class/leds/red/brightness //打开LED0: E0 ]( L8 F) s' x& E
echo 1 > /sys/class/leds/green/brightness //打开LED1- q& r) q0 k4 [. u# @8 C- L! q( D$ ~
关闭这两个LED灯的命令如下:
  t4 D/ Y# V' [echo 0 > /sys/class/leds/red/brightness //关闭LED09 u( f; A, o) o% H& `( `8 m
echo 0 > /sys/class/leds/green/brightness //关闭LED1; F2 b0 U, J! c8 ^- r1 N8 S8 W3 D8 t
如果能正常的打开和关闭两个LED灯话就说明我们Linux内核自带的LED灯驱动工作正常。我们一般会将一个LED灯作为系统指示灯,系统运行正常的话这个LED指示灯就会一闪一闪的。里我们设置LED0作为系统指示灯,在dtsleds/led0这个设备节点中加入“linux,default-trigger”属性信息即可,属性值为“heartbeat”,修改完以后的dtsleds节点内容如下:
$ D% M/ {' y8 `+ \! V% r/ w! Q- G9 l
# l3 L8 T) m/ y% k2 o  ~2 t
  1. 示例代码36.4.1 dtsleds设备节点& ]# F( `/ c  M2 n$ T; r% w
  2. 1  dtsleds {& b. @! p5 u' W4 h% n2 y
  3. 2                  compatible = "gpio-leds";3 s+ |, i  v! h+ I5 ~3 \* u6 ~
  4. 3                    pinctrl-0 = <&led_pins_a>;5 T* y+ ?( j+ x4 k; H1 `
  5. 45 \# ~* u1 C+ }5 S* F1 P! {
  6. 5           led0 {
    & [% ~0 a8 l$ b$ Y5 ?
  7. 6                       label = "red";
    / T. y8 b5 f- k: h! N4 m+ g4 U& G
  8. 7                       gpios = <&gpioi 0 GPIO_ACTIVE_LOW>;4 K+ |- ?7 G$ A, X. X3 ]
  9. 8                       linux,default-trigger = "heartbeat";' E0 e) h% z# X) I* j8 B3 ~
  10. 9                       default-state = "on";
    9 L  _$ Z1 G( d- ~# h0 D, [
  11. 10           };/ ]/ T: F: i8 X- z) ~
  12. 11  * @" `2 Y2 x. H# V$ F4 y8 h
  13. 12          led1 {9 U, G$ L$ J4 m, A7 ~. _+ e1 N
  14. 13              label = "green";- q. l& \: i) E0 t( Z, z
  15. 14              gpios = <&gpiof 3 GPIO_ACTIVE_LOW>;
    , D4 ~2 N/ C/ R3 j( T9 ~+ L
  16. 15              default-state = "off";& W1 ]* X4 \4 Q  I
  17. 16          };
    4 d* @" U- A/ g. P
  18. 17 };
复制代码

; i# m1 @+ K( t4 X第8行,设置LED0为heartbeat。
1 z7 _4 v8 h( }# q$ c/ h0 L6 A第9行,默认打开LED0。7 J. D) `! @( I2 V& r
重新编译设备树并且使用新的设备树启动Linux系统,启动以后LED0就会闪烁,作为系统心跳指示灯,表示系统正在运行。
+ f+ r. t5 z% ^# e: U, g& N————————————————. G/ c) Z# Y2 H6 X3 o- p
版权声明:正点原子# V3 N1 F# z6 m* |, a. f( P/ v5 {
& g& J; d5 H5 A+ z) ?

1 ^0 W0 p& g0 O# {$ {
收藏 评论0 发布时间:2022-9-27 11:32

举报

0个回答

所属标签

相似分享

官网相关资源

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