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

详解STM32在线IAP升级

[复制链接]
攻城狮Melo 发布时间:2022-11-13 14:06
简介
/ d$ @% N0 f! h" [本文主要讲解在线升级IAP的基础知识, 主要是针对IAP 从原理分析, 分区划分, 到代码编写和实验验证等过程阐述这一过程. 帮助大家加深对在线升级的认识.
1 b8 c- h! r: b6 H1 t+ V, {3 ~7 o, y" \' |0 L! R: I
1. 在线升级知识
. O4 t) `. T6 [3 [9 `7 t- Y什么是BootLoader?
0 ^3 o# w) R+ @% k1 U1 C3 }, N( o5 aBootLoader可以理解成是引导程序, 它的作用是启动正式的App应用程序. 换言之, BootLoader是一个程序, App也是一个程序,  BootLoader程序是用于启动App程序的.4 Y: x3 K8 J; c
4 H  C, P( z, b: X
STM32中的程序在哪儿?" S4 `  p/ i6 Q. C2 Y0 g" b/ w' o
正常情况下, 我们写的程序都是放在STM32片内Flash中(暂不考虑外扩Flash). 我们写的代码最终会变成二进制文件, 放进Flash中 感兴趣的话可以在Keil>>>Debug>>>Memory中查看, 右边Memory窗口存储的就是代码
  n6 w  k8 f# E. M2 O3 h7 P7 A: ]/ h8 H# @
微信图片_20221113140525.png
" [: k( c! Z  Y2 A% A
, ?) L9 N7 P1 x8 n接下来就可以进入正题了.
% @& t# A4 ~) w+ G0 D9 n. P7 w  t2 H7 x8 ^  |# b( u/ e
进行分区
" ~+ {5 A4 z9 X: a5 G; {- n既然我们写的程序都会变成二进制文件存放到Flash中, 那么我们就可以进一步对我们程序进行分区. 我使用的是F103RB-NUCLEO开发板,他的Flash一共128页, 每页1K.见下图:
6 q' ^, j! P: U0 x) ~5 M( d* w9 K# C+ X1 D* W2 |2 N6 H
微信图片_20221113134602.png + t! @! u" k% l, \: [4 e7 C
: R9 h% r! ~8 @2 K
以它为例, 我将它分为三个区.BootLoader区、 App1区、 App2区(备份区)具体划分如下图:9 ], N: g/ T2 U
BootLoader区存放启动代码
& k2 {2 S0 r* a4 s. FApp1区存放应用代码. c+ l5 }: z- X' y0 Q
App2区存放暂存的升级代码
, Z3 K8 Z+ u8 ?1 L1 a
% M1 v+ n! Q8 A, @4 s! D- y) B2 S! q 微信图片_20221113134552.png ( P$ t! c5 L) j' d. C7 j

# U! S$ s3 g5 b2 ^总体流程图
8 h% \( N' i4 c& W* ?先执行BootLoader程序, 先去检查APP2区有没有程序, 如果有就将App2区(备份区)的程序拷贝到App1区, 然后再跳转去执行App1的程序.8 K) @8 p; U9 A; x; k( F. f# d. a

$ C8 }/ b: f6 M/ }. h然后执行App1程序, 因为BootLoader和App1这两个程序的向量表不一样, 所以跳转到App1之后第一步是先去更改程序的向量表. 然后再去执行其他的应用程序.
: t- \3 S) w. A0 x8 n/ J8 f1 Q5 R1 A, i7 x- @/ p! }$ p: c9 z
在应用程序里面会加入程序升级的部分, 这部分主要工作是拿到升级程序, 然后将他们放到App2区(备份区), 以便下次启动的时候通过BootLoader更新App1的程序. 流程图如下图所示:; \$ z4 E) _& [  @! @( b
' p* c: B0 J: u7 A, Y
微信图片_20221113134529.png - o2 z2 L: N8 G! Z& A3 T6 ~
) k. A5 Y: m+ W# R( C( J
2. BootLoader的编写$ t% d6 s4 x0 V! v6 b
本节主要讲解在线升级(OTA)的BooLoader的编写,我将以我例程的BootLoader为例, 讲解BootLoader(文末会提供免费的代码下载链接),其他的大体上原理都差不多。2 j% q4 F  x3 O5 r! h

$ q; s/ @4 H: W9 X  {. H; E* e2 c流程图分析! V3 D% K) `/ n  f; D: C! I' T7 x
以我例程的BootLoader为例:
' J& y8 X  s# G+ A/ K2 c; Z' P6 ?1 {5 q8 k1 S
我将App2区的最后一个字节(0x0801FFFC)用来表示App2区是否有升级程序, STM32在擦除之后Flash的数据存放的都是0xFFFFFFFF, 如果有, 我们将这个地址存放0xAAAAAAAA. 具体的流程图见下图所示
7 [5 h2 `  y1 E% l5 [+ L1 B2 o6 k4 Q- G) `. D
微信图片_20221113134518.png
) ]" t: N9 Z( z
) I( u1 J3 Y0 Q. k程序编写和分析
' T- t: @8 D  l) l# G: N+ n+ s所需STM32的资源有:
' I" A% R& H" p( D, p0 m发送USART数据和printf重定向2 R" P9 I3 g# y9 j" y
Flash的读写
/ m' b. X( s7 W6 V3 k* e4 ~/ g" Q# V程序跳转指令,可以参考如下代码:
, D& E# b, c& H3 ^; }0 j/ W# |" s; g8 ?
  1. 1/* 采用汇编设置栈的值 */2 D5 z# t( t( M) l" I
  2. 2__asm void MSR_MSP (uint32_t ulAddr)+ H9 I' I. [- Z% ~2 W# i6 Y
  3. 3{
    0 i; ?* }, ]0 Y) N( k1 r+ Q3 q
  4. 4    MSR MSP, r0   //设置Main Stack的值) P, z8 V& I' q; [4 `0 V# v
  5. 5    BX r14# `0 ]. e# \6 h- L$ W0 n
  6. 6}: o: X2 \7 r' l4 y$ ?* T" i+ |
  7. 7
    ; h9 N  P1 S- j% l$ i
  8. 8
    8 ^$ ?' I) z& x0 r8 e$ w6 l+ j
  9. 9/* 程序跳转函数 */
    # N( i( n2 t! J
  10. 10typedef void (*Jump_Fun)(void);
    + H3 G1 u5 O8 i
  11. 11void IAP_ExecuteApp (uint32_t App_Addr)
    ) B/ f% M/ f+ y% C1 j1 [' s4 E
  12. 12{
    6 P# J: v" H1 F- k$ y0 w% v
  13. 13  Jump_Fun JumpToApp;
    / b. v8 z7 I5 o, v  P4 j
  14. 14: x/ Q5 o3 _1 Y# @3 Q
  15. 15  if ( ( ( * ( __IO uint32_t * ) App_Addr ) & 0x2FFE0000 ) == 0x20000000 )  //检查栈顶地址是否合法.- G# s; s1 ?4 j4 l6 k; K! ?' b
  16. 16  {
    + D4 T5 K# g& Z0 I8 ^4 Y5 H% A
  17. 17    JumpToApp = (Jump_Fun) * ( __IO uint32_t *)(App_Addr + 4);  //用户代码区第二个字为程序开始地址(复位地址)
    ; |% ?! l6 X: o$ v0 e
  18. 18    MSR_MSP( * ( __IO uint32_t * ) App_Addr );                  //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)& R4 T' |$ u0 m4 b& t8 c* G
  19. 19    JumpToApp();                                                //跳转到APP.) z5 N' x1 M" Z! A& w8 l# H9 u
  20. 20  }: T  o. I9 K- L5 U
  21. 21}
复制代码
* u" [3 f; ?8 C2 H. Y  w/ Z& f( q) w
在需要跳转的地方执行这个函数就可以了IAP_ExecuteApp(Application_1_Addr);
, g3 E+ f) D5 e/ W- p# e
9 r7 `# m" N5 Q& ], j其他的代码请参考BootLoader源代码8 i6 Q3 T% `+ H4 w' t

3 ]7 _! l: X0 `! V: v7 j2 s3. APP的编写

3 N  c# M) n/ e& Z  _" K/ v本节主要讲解在线升级(OTA)的App1的编写以及整个流程的说明,我将以我例程的App为例, 采用Ymodem协议进行串口传输,讲解App的编写(后面会提供免费的代码下载链接), 其他的协议原理大体上都差不多, 都是通过某种协议拿到升级的代码。5 B3 s1 H9 U, z6 R+ m

3 G7 h3 D8 X, _& X流程图分析
5 v2 C' T& b  T5 a& g以我例程的App1为例:
+ j& e+ X/ \8 I% \: ~先修改向量表, 因为本程序是由BootLoader跳转过来的, 不修改向量表后面会出现问题;
% O7 a$ w1 N. D* O- `. _打印版本信息, 方便查看不同的App版本;  i% u# Q) W" `& Q4 x5 g
本例程的升级程序采用串口的Ymoderm协议进行传输bin文件. 具体的流程图见下图所示:
, c" q& X7 v5 A* W
- A% }- s3 H. h3 U 微信图片_20221113134510.png ( X! z. H7 K2 ^" `9 T2 s) Q7 r

: I. f8 l3 M: U9 B- G* a程序编写和分析

2 H: }" E1 p% Q* X* P' ^- I所需STM32的资源有:% e" r5 V% A1 H
发送USART数据和printf重定向+ a0 z( L8 M. J7 a8 F- q
Flash的读写
+ W* X* }2 p+ [串口的DMA收发! {6 T4 H- h: ~* _
YModem协议相关
4 f& C4 g4 y1 ~5 q0 ^5 q7 z4 y& G$ s6 B# c6 s; Z
Ymodem协议# B% T1 N3 q/ J7 k
百度百科[Ymodem协议]2 z+ d( D" d: p+ O2 f& M7 U/ t
具体流程可自行查找相关文档, 这儿提供一个我找到的 XYmodem.pdf(文末和源码一起提供).7 N- s# n4 @$ i0 i# j
Ymodem协议相关介绍可参考我的这篇教程 YModem介绍
3 @1 \7 w( w5 u8 s$ g. f8 ^
; ^/ G6 c: F$ |( f代码分析8 Z6 S( S2 A7 ?; ]) T4 a
代码大多数都是通过串口实现Ymodem协议的接收, 这儿就不详细说明
. u+ u, Z* d! F: v/ m0 w后面放了我的源代码, 详情请参考我的源代码.
& F' R# W4 h" p/ a0 N2 O) E4 @主函数添加修改向量表的指令& ^7 V. J) V4 p9 ?/ W

; i# j; W, N/ w1 @7 C+ W) ^3 U: f" q 微信图片_20221113134445.png " H) \* \$ W1 x0 M# W+ e

4 K# I4 z5 |  [打印版本信息以及跳转指令 微信图片_20221113134440.png
3 g1 a$ s$ V% N# y  UYModem相关的文件接收部分' O0 `% R0 o/ \! @0 x7 Q
  1. 1/**. Z  y7 `- ~9 v8 l3 e6 y/ H
  2. 2 * @bieaf YModem升级
    - r# V8 @; I9 S
  3. 3 *# @7 F, C0 t! L% e* ?# M* A( G
  4. 4 * @param none
    / t3 \3 }5 x9 F' n' I* u4 l
  5. 5 * @return none# z7 B: U& A2 P0 U
  6. 6 */
    % z- l/ ]& ]+ M  v: m4 B6 _, G
  7. 7void ymodem_fun(void)
    4 n$ L1 {2 i' u) d- [
  8. 8{; M* w" A) x" o7 p. ?* E3 k3 q
  9. 9    int i;
    9 l% d+ g( S. @# I% Y7 i3 @0 ^
  10. 10    if(Get_state()==TO_START)7 w# U' m! q' o# u
  11. 11    {
    / X" t" D3 c) m6 L
  12. 12        send_command(CCC);
    # l8 K" j$ ^4 j/ Q
  13. 13        HAL_Delay(1000);+ w( a7 [5 K! O- Z, U) v2 G4 j
  14. 14    }
    & x3 a9 d  Y% P0 {# ]' {% p
  15. 15    if(Rx_Flag)     // Receive flag
    # Z( S. }# W; y7 S
  16. 16    {' l9 z8 W: {% ^% X# q
  17. 17        Rx_Flag=0;  // clean flag" z; J( W4 M+ i6 n+ [* f
  18. 18- A7 s0 _. \4 x1 ^
  19. 19        /* 拷贝 */7 h9 Z! n& K8 J
  20. 20        temp_len = Rx_Len;. t9 R9 g1 _9 u6 I$ E+ R- [
  21. 21        for(i = 0; i < temp_len; i++)
    - ~  g6 |- ?0 n8 g
  22. 22        {8 c" _% f7 N1 c" D* M# L
  23. 23            temp_buf= Rx_Buf;
    8 i; I1 v4 P  ?1 I% X
  24. 24        }" V( L1 K1 }% e5 H! [
  25. 25
    0 y: Z5 w4 h& O* h2 M
  26. 26        switch(temp_buf[0])
    * A4 H& S$ w: T
  27. 27        {* H) |& A2 B( P1 ?
  28. 28            case SOH:///<数据包开始" w! l" t/ k6 S
  29. 29            {
    " S) k7 z6 C6 N7 a) r6 |/ h. J* S
  30. 30                static unsigned char data_state = 0;; [8 @! x0 r  R$ P: ^2 Q
  31. 31                static unsigned int app2_size = 0;
    ; J8 Z1 b" l# n+ d/ ~' X
  32. 32                if(Check_CRC(temp_buf, temp_len)==1)///< 通过CRC16校验
    $ D% R( p4 d1 L3 ]8 ]- A
  33. 33                {                   ; \/ a! \1 s# t8 U! ]8 ^
  34. 34                    if((Get_state()==TO_START)&&(temp_buf[1] == 0x00)&&(temp_buf[2] == (unsigned char)(~temp_buf[1])))///< 开始
    , i# [' k5 [. t5 B$ E+ p: q5 n! ?
  35. 35                    {4 P- P6 l& e0 P8 G
  36. 36                        printf("> Receive start...\r\n");* u9 M. [/ X+ {% @+ l
  37. 37
    3 W. ^- E' n, m% G/ c: e4 z
  38. 38                        Set_state(TO_RECEIVE_DATA);
    . T* T% v% S. A) W# j
  39. 39                        data_state = 0x01;                     
    ! C/ N0 l" P0 [0 e( U
  40. 40                        send_command(ACK);
    9 p' T8 X/ j+ D9 a# o
  41. 41                        send_command(CCC);
    . A  M- I' s& j' ]# X) \
  42. 42
    , {: F0 f* I& A' x# }, j# m4 h
  43. 43                        /* 擦除App2 */                           
      w5 H1 h; @7 v% s4 ?. z
  44. 44                        Erase_page(Application_2_Addr, 40);4 e. I  ]  h+ S1 O/ |* }  m
  45. 45                    }
    7 z1 I9 Z) J* v, e
  46. 46                    else if((Get_state()==TO_RECEIVE_END)&&(temp_buf[1] == 0x00)&&(temp_buf[2] == (unsigned char)(~temp_buf[1])))///< 结束
    % L8 n; l+ ?7 ^; q& r8 Z
  47. 47                    {, q' q$ F( d4 \3 \6 p
  48. 48                        printf("> Receive end...\r\n");
    5 I% D& p7 z' r+ s! x
  49. 49
    # ^' @; l8 F# x9 [' [0 a6 I  i) T
  50. 50                        Set_Update_Down();                     
    % h* u8 B' U7 j+ r4 D
  51. 51                        Set_state(TO_START);1 |+ ?9 ]9 M! U* J
  52. 52                        send_command(ACK);$ |8 e" |- v# S  z! A6 q, T
  53. 53                        HAL_NVIC_SystemReset();6 H0 d9 g. Y" ]8 _
  54. 54                    }                   - z, ?; J! F6 D$ I' z1 a8 Z; L8 X
  55. 55                    else if((Get_state()==TO_RECEIVE_DATA)&&(temp_buf[1] == data_state)&&(temp_buf[2] == (unsigned char)(~temp_buf[1])))///< 接收数据7 |9 |2 X# p7 E2 R5 [/ o- P6 S
  56. 56                    {/ Z5 D: t" s9 U6 @
  57. 57                        printf("> Receive data bag:%d byte\r\n",data_state * 128);) a# i; z5 f  l% J4 j5 j2 D1 O; a
  58. 58# j, |/ s) \2 o9 b6 T' V# l; X6 v
  59. 59                        /* 烧录程序 */8 |# ^( c+ y7 O2 i9 C5 a9 y
  60. 60                        WriteFlash((Application_2_Addr + (data_state-1) * 128), (uint32_t *)(&temp_buf[3]), 32);
    1 K. O. s1 h2 p" e: `. ~1 X
  61. 61                        data_state++;7 y8 u% K( G: B2 m2 i
  62. 62
    2 Y5 F  A. C/ F6 o' K
  63. 63                        send_command(ACK);      
      ^( q1 e5 y3 H) i) r8 B0 F
  64. 64                    }
    4 Z& H- k# M. S) b) _
  65. 65                }/ K# D- H/ k. |" E
  66. 66                else, F, V; L$ H, J8 Y0 O0 _
  67. 67                {
    + o: e$ ~; ?1 G9 `$ {3 Y) B
  68. 68                    printf("> Notpass crc\r\n");
    5 ]: V' {" p* W- q! S3 k4 ]) F  d1 i
  69. 69                }0 ~( q) N$ {+ @9 k% V
  70. 70% F1 P5 k, M$ [2 F
  71. 71            }break;
    8 K# f' r. ], \% p: U/ ?
  72. 72            case EOT://数据包开始
    9 @5 M, F" N, k. X
  73. 73            {; O, H# q! N& d! ]$ A% d, E
  74. 74                if(Get_state()==TO_RECEIVE_DATA)
    1 N2 h2 W0 W: @. s) w- |. M/ E- H
  75. 75                {4 q. g2 T! T4 U* l1 Q
  76. 76                    printf("> Receive EOT1...\r\n");# h- H7 ~8 K9 r1 @) _& H
  77. 77! s' w4 }. I  m; f3 l" \
  78. 78                    Set_state(TO_RECEIVE_EOT2);                 # v! u8 H- A' l
  79. 79                    send_command(NACK);/ `6 B! B% A! I$ T. ], _
  80. 80                }
      N# i# J% A4 U5 t. u- u6 m/ N
  81. 81                else if(Get_state()==TO_RECEIVE_EOT2)
    * c! G+ M/ n/ S; w$ M
  82. 82                {
    ! @) D: ?2 D2 h" l
  83. 83                    printf("> Receive EOT2...\r\n");
    9 o- x8 F+ u& z
  84. 84, Z/ q. K1 _, k- u% L
  85. 85                    Set_state(TO_RECEIVE_END);                  
    6 V$ O( v5 O/ e- y0 o
  86. 86                    send_command(ACK);
    ; w9 T' h8 s) V( R( v7 {7 R0 l
  87. 87                    send_command(CCC);
    6 i$ m$ s$ @: {# m: c
  88. 88                }( s6 |! J# D8 m2 _
  89. 89                else7 V, r( V9 z" j
  90. 90                {
    8 ^# \6 e( ~% z2 A: \8 r, t  H
  91. 91                    printf("> Receive EOT, But error...\r\n");
    ( @$ w) s5 [3 U$ M( e
  92. 92                }4 X. Y% f6 x3 _1 c* A9 K+ p
  93. 93            }break; 3 m6 ~* v, K& E6 ~- U
  94. 94        }7 q/ u0 ~, ?& s: N! n7 S2 ~
  95. 95    }. ?: P' L$ s9 z9 v; x1 O# }" Z
  96. 96}
复制代码
' a8 e: U: C1 F& R8 z; A
其中部分函数未在以上代码中展现, 详情请参看文末给出的源码链接.0 i, H7 U/ G/ ^$ K) X

( O: j9 z8 ^) f; O4. 整体测试, w5 l! g+ E4 V* K/ e: L* l0 D
本节主要对前三节的教程做测试验证 BootLoader + App的升级功能。: V8 F2 `; T7 k. _/ q

5 c3 p( ?- d3 v5 J2 F$ m代码的下载
- x* x2 f6 |, |' D0 T/ y+ L由下图可知两份代码的下载区域是不一样的,所以他们「下载的区域也不一样」。
* V( ^! H, V3 T6 U! n& V( t# h( \5 I; `* w! [+ L5 g
微信图片_20221113134403.png
4 N$ I, r+ x" n% s) o$ b0 b/ K
9 Y" l9 [; o6 V2 v" T3 _* DBootLoader的下载: h3 `* _' i; Y/ e, C) |
BootLoader的代码默认是最开始的所以不需要特别设置代码的下载位置% v+ q* V; p. y  U
按照下图, 修改擦除方式为Erase Sectors, 大小限制在0X5000(20K)
( D, M1 z/ y' y0 X5 ~; V9 a: [; @  x$ C& O
微信图片_20221113134400.png
0 y5 L- j* X' S9 ~
$ |' `, @1 O8 u/ k+ \+ |烧录代码
: v/ x  Q% X3 Z% j- ^运行, 通过串口1打印输出, 会看到以下打印消息
7 e! J; T3 Z3 V8 `说明BootLoader已经成功运行
: ?& N3 i5 P- m; c5 q6 f
; i, }# `5 U! S$ N9 v& u 微信图片_20221113134354.png # P& r, N( z& d

- u5 Z9 s1 N: D( v- B/ q4 n: b) iApp1的下载
* L; n% f7 X5 ?. a8 W: @App1稍微复杂一点, 需要将代码的起始位置设置为0x08005000# d9 G& i; k% ]3 a, S, h! a0 j+ ^
同时也要修改擦除方式为Erase Sectors, 见下图) A5 @$ n5 J$ r% ~/ t3 {

/ m  l2 S/ c- a( s2 _ 微信图片_20221113134351.png
. L% s6 s6 z3 c
& ]: f! Y/ P, i0 X+ W" N* A6 u/ } 微信图片_20221113134347.png
$ W% u6 `3 b4 J; s3 E5 P. O3 E+ r1 u% H5 Z
烧录代码
! I! {: L' C. ]运行, 通过串口1打印输出, 会看到以下打印消息* m/ W) P6 n" C: p2 F+ M( _
说明BootLoader已经成功跳转到版本号为0.0.1的App13 s4 K( C) \, B

! _0 F2 f* w, q 微信图片_20221113134335.png + i% Y. B' m% {' s

: }9 x6 I% R( C3 P& w& H生成App2的.bin文件
% [) T9 c" Y) D! b* _8 H5 ^$ ?Keil如何生成.bin文件, 请参考这篇博文 Keil如何生成.bin文件
5 P0 N3 m1 Z. b& N修改代码, 把版本号改为0.0.2, 并且编译并且生成.bin文件
+ m/ P0 O( m$ H7 B7 i, ^- r生成好之后你会得到一个.bin结尾的文件, 这就是我们待会儿YModem要传输的文件/ @( S9 D$ J) t# {6 B) D
* x; w" y1 ?! S6 Z% U, e
微信图片_20221113134330.png 0 z: t# h: P& ~. _4 }0 z
% q! c4 h! F+ A: O) M
使用Xshell进行文件传输
( x. Z/ ]: Z! k9 F" J/ e+ ~打开Xshell
2 r1 m0 s8 c1 K0 \! b. o7 x代码中, 串口1进行调试信息的打印, 串口2进行YModem升级的
) r$ b$ g/ n9 B+ W: N所以使用Xshell打开串口2进行文件传输, 串口1则可以通过串口调试助手查看调试消息
$ W" k: K4 B% B$ }你会看到App的版本成功升级到0.0.2了.( ?2 h- x" ^; X. Y6 t" Y8 C  @
如果你到了这一步.5 c/ D! p) L4 ^9 w& p6 }
那么恭喜你! 你已经能够使用在线升级了!1 \# @" \) Z9 c3 V0 I
" \6 v- \+ e& {& f) j
5. 总结
6 W8 C- j) b2 M通过本几节的教程, 想必你已经会使用在线升级了, 只要原理知道了其他的问题都可以迎刃而解了, 除了使用YModem协议传输.bin文件, 你还可以通过蓝牙, WIFI,等其他协议传输, 只要能够将.bin文件传输过去, 那其他的部分原理都差不多。
+ ~: `4 z7 `" q8 J! P$ X8 g) Q# B4 q6 P0 H7 }" S+ M5 s
转载自:单片机与嵌入式
' G2 [2 C& R4 V3 N+ z# `% S; u8 L$ O/ J0 _/ ^; _
赞 收藏 评论0 发布时间:2022-11-13 14:06

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版