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

【STM32U3评测】SPIDMA发送

[复制链接]
donatello1996 发布时间:2025-5-27 00:09
      SPIDMA发送/接收是非常常见的MCU应用,用于与高速SPI器件通信并提高效率,这个帖子是评测STM32U385RG的SPI1接口,并启用SPI1的DMA发送功能。
4 j0 |' q* ~; [; n. T/ R5 P8 Q3 h2 z. U
       在上一个帖子中提到,STM32U3的DMA外设名为GPDMA,在ST新出的系列中,任意支持DMA的外设都可以绑定到DMA的任意通道/流/节点上,不像F103和F4系列那样规定通道号,来看看SPI1DMA的配置:
8 Y- G) a! Y1 y4 L5 l
" z( z8 t# t3 V: m4 M3 n/ D 8.JPG
& s. h9 N& P; x8 C
( G" f* ?% r+ ~" A. K( t% |4 {
Normal模式表示每次调用DMA发送函数只会发送一次,Circular模式表示只调用一次DMA发送函数就一直发;在外设所使用的DMA中一般配置外设地址非自增,内存地址可配置为自增或者非自增,内存地址非自增表示DMA发送的数据是同一个数据,也就是内存首地址的数据;源地址和目标地址的数据格式必须一致,都是8位或者16位;只要SPI使用了DMA进行发送或者接收,就必须要把SPI中断和DMA中断都打开,少打开一个都不会正常工作。0 v( z, x1 s' O4 U( o5 _% I4 B
# ~8 n: f3 m: N3 p
SPI初始化代码如下,在ST新系列中加入了一些不太常用的SPI机制,如CS片选引脚空闲等待节拍,Ready模式,因为在大部分场景中片选引脚都由GPIO进行控制,比较少用到SPI外设自带的NSS引脚功能,除非是双MCU通信:
' q, j6 W5 U0 f; C; p
  1. void SPI1_PA5_PA6_PA7_Init(void)# D' Z; T& Z6 G0 ~8 Z
  2. {# B  v$ w3 Q5 t! E3 d2 x
  3.    
    # K& g. U7 C$ C; {9 h7 }
  4.   __HAL_RCC_GPIOA_CLK_ENABLE();6 s/ o8 g, a" P
  5.     __HAL_RCC_SPI1_CLK_ENABLE();
    3 n) u4 i0 F4 ]. [0 t
  6.     __HAL_RCC_GPDMA1_CLK_ENABLE();0 f! m) @/ n6 d, f
  7.     ) s- a1 ~/ ?* f( {" t4 ?# N
  8.     RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    $ N* z6 Q2 u* @% e+ @& D7 T  n
  9.     GPIO_InitTypeDef GPIO_InitStruct = {0};* E* W( H* g5 w3 q( H
  10.   SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};$ }- i  Z& F, u) N
  11.     * V9 y6 m) E* E% Q  T
  12.     PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
    3 d8 [" [- O% c* _7 h! H3 w
  13.   PeriphClkInit.Spi1ClockSelection = RCC_SPI1CLKSOURCE_PCLK2;1 U4 T- N8 e. t5 h. P) z4 ?
  14.   HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    ! o" ~  r6 \# J) ]0 X- M6 b% y5 M
  15.     ' h4 s7 C% \! M& K
  16.     GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    ) Q" G3 b2 o* j1 s& e
  17.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;7 d2 F# O5 g' M
  18.     GPIO_InitStruct.Pull = GPIO_NOPULL;7 L* S% j5 Z8 g9 }/ S
  19.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    2 R8 A  ^& L4 g! q- ?0 P! E+ x8 i
  20.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;- l7 D5 ~7 S$ c% i, I3 l! P
  21.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    8 [- X% O# K7 L
  22. 8 }7 R1 L4 K) v5 V
  23.   hspi1.Instance = SPI1;
    & Q' I7 V/ W' k5 f9 P0 t
  24.   hspi1.Init.Mode = SPI_MODE_MASTER;
    " C" b, u8 l4 L
  25.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    - b  T' X7 }: G  r$ R
  26.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    9 q4 o0 T; A2 G: p# K; u7 g+ Y) {) F9 B
  27.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    : x& v2 g6 d  Z  w* g3 |" c& I! N
  28.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    8 i0 E2 [* r4 g* l  _+ e0 j& d/ N. b
  29.   hspi1.Init.NSS = SPI_NSS_SOFT;3 i9 A" N3 z$ l; v/ N% f6 x/ J
  30.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;! v! x: E8 u5 ]% q- q5 j
  31.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;# R% m' k; p$ V. F1 a/ t- v4 n' ~
  32.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;7 p: _( \* [6 F
  33.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    ; h& R; G4 l9 \" y
  34.   hspi1.Init.CRCPolynomial = 0x7;
    : T* r" `! j/ d
  35.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
    9 s( {7 \9 t7 a) w
  36.   hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;# ~* c4 _: U" z7 d% S' {
  37.   hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
    6 J, H+ f/ ^3 I8 N! N  D' L( v% Q  v9 b
  38.   hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;/ B* N2 I2 |6 {3 q
  39.   hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    ; z) C+ z+ F0 j
  40.   hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
    2 W/ ^  x: y" c
  41.   hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
      J) _4 F. J  S* R& q
  42.   hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    # n' I3 a2 a" s; e2 y
  43.   hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
    : o. v6 N+ e" S* \( w' T
  44.   hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;* X# w, I8 f5 c' z6 N% ]7 O
  45.   HAL_SPI_Init(&hspi1);
    8 v1 Y: c5 |2 n6 S- Q- ]- ]
  46.     __HAL_SPI_ENABLE(&hspi1);
    & c3 H  ]5 S% X% K( I
  47.     , v; M" V4 _& t: A" w- R6 _2 B
  48.     HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
    , ~$ E6 |: P6 U% ?$ f8 E
  49.     HAL_NVIC_EnableIRQ(SPI1_IRQn);& {0 u& n8 M3 ]
  50.     ' V& [+ x4 L# Z4 v9 `
  51.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;
    + f% `7 Q# R9 |
  52.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;
    9 ]" T+ B6 D: S7 y6 h
  53.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;- U8 p+ l* {9 Y: @2 M! Z
  54.   HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct);  V0 N! {3 |; r- g

  55. & D' P( a2 w0 e
  56.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    * N1 k  v8 n" W: _4 m% A* [9 n+ F! d
  57.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;! g/ C: \* Q4 b. U7 W1 n( M& ?+ g
  58.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;/ e# S1 ?% a) F
  59.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    $ S; I: |: E* g; i; k
  60.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
    " E  e+ v7 E* [# O
  61.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;: \4 r6 M, M* K" |7 h
  62.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;" S7 c! C- @; K: l
  63.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;7 D+ O" u+ o& }# f- X. e1 s
  64.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;' l# E" B: h9 M# O3 d& J7 n
  65.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    " M" m7 A* W1 s
  66.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;* b8 W5 h, t" F  T- F& {
  67.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;' H$ B+ O, ?8 }) t1 ]- l2 [5 B
  68.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;0 z1 W8 Y& y0 N9 h
  69.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    1 E! c' x- s8 W' M* [( m. _
  70.     HAL_DMA_Init(&handle_GPDMA1_Channel1);
    ! d# Z0 k+ W( b5 G
  71. 4 r* B6 y% J" K, j% z; ]( v
  72.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    $ T/ ^: _8 b0 S; n; U3 e6 p1 H/ b

  73.   o5 z* k8 C! t% |' @2 H0 R9 h
  74.     HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV);
    8 ^) F4 Y% K/ |6 c8 o! O  E, b
  75.    
    3 V1 `; r$ z$ s# e- k4 f
  76.     HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);
    + p' g3 K# }0 f+ l  C7 g
  77.   HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
    : `% ]6 I+ }$ g

  78. 5 S% Z) [1 Z) M4 T5 s8 q0 x- W
  79. }
    % J  h+ U4 k5 G

  80. & f# e# V7 v8 q
  81. void GPDMA1_Channel1_IRQHandler(void)% p4 g/ N2 v0 L/ j" L# I- b. }
  82. {3 {6 O% B% k/ b
  83.   HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);
    , ]7 \. _# p3 p5 K- T3 T, ?$ l1 V
  84. }/ k+ }5 j, Y( X- n4 Q. o5 ^+ {
  85. , \5 E+ s+ C3 O' X6 e) r: ?" T
  86. void SPI1_IRQHandler(void)
    3 S, ?& T, p- k' E  M2 |
  87. {, f! T# s; E8 h' h  r- a$ @
  88.   HAL_SPI_IRQHandler(&hspi1);1 }4 V( ?* d9 J) R# ]0 N
  89. }
复制代码
; O6 e+ g5 s! H8 P* S" S6 R
* d1 o- h4 H4 P+ }# J
SPI发送或者发送+接收代码如下:
1 m4 b$ T4 A7 q2 U+ k" k
  1. uint8_t SPI1_PA5_PA6_PA7_Read_Write_Byte(uint8_t TxData)! y+ F+ V6 S5 U# Y
  2. {
    * b$ {; \/ m  n
  3.     uint8_t Rxdata;+ L6 Z7 J% D9 e4 |4 C6 z. i* \7 o; V
  4.     HAL_SPI_TransmitReceive(&hspi1 , &TxData , &Rxdata , 1 , 1000);      
    . [$ Y/ Z1 H) W! P- I6 k/ C/ u
  5.     return Rxdata;
    + x$ n5 v9 K6 l6 k. X8 T
  6. }; b; U2 b6 }! T! ~0 y

  7. / w  x' J  F2 m  v. b
  8. void SPI1_PA5_PA7_Write_Byte(uint8_t TxData)* \- i, P: M" [3 u$ G7 h
  9. {
    7 x7 G3 F" e$ E: U' a0 M7 H
  10.     HAL_SPI_Transmit(&hspi1 , &TxData , 1 , 1000);, ]9 @& K, H% w5 [, [1 u
  11. }
复制代码

8 x& r& K  J; l! j# u1 ?! }/ p/ m, D7 x# G
8 B4 `3 R" {5 M: v
SPI随时需要调整数据位数为16位或者改回8位:
2 l% j" H7 W8 y& b0 n
  1. hspi1.Init.DataSize=SPI_DATASIZE_16BIT;$ H  M$ t# z% P; _
  2. HAL_SPI_Init(&hspi1);
    $ Y, M) W( ~( B: ]: ^9 [  c2 n
  3. 9 y/ m2 n) y- Q3 W, l1 \8 [
  4. hspi1.Init.DataSize=SPI_DATASIZE_8BIT;
    1 {' Z4 R% B  G
  5. HAL_SPI_Init(&hspi1);
复制代码
$ W( c4 n& ?* A# y4 g
在实际应用中,常常需要修改DMA参数,最常见的就是内存地址自增或者非自增,数据是8位或者16位(Halfword半字)对齐:; Q+ I/ x; k8 y' x: n5 K( b
  1. void SPI1_TX_DMA_Config_PID_MID_HalfWord_Normal()9 q6 F3 K% }" t6 H
  2. {; \6 y" j% K: ?  s' w) M: P
  3.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    ) F" e/ X) t3 U- L3 v6 P+ n
  4.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;. W. ]. k& R9 {1 F4 U
  5.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;$ R, b3 X' K# E
  6.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
      `$ s. i, Q- F9 Y5 Y8 \& n
  7.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;: |3 a& k; B% V. y0 N7 ]
  8.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;) V4 U0 c7 u+ Q6 d3 j! P
  9.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
    " @: d/ K1 l9 a8 [
  10.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;$ o9 F( a: s, }6 g
  11.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    7 L) b$ j  J% K  b2 s( u, m* w
  12.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    # l5 K) a" D" J7 ]$ H- y# f
  13.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    % n- S7 A" ~4 u& ]& c. ]
  14.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;+ p" ~# z7 L. {. n! [* G
  15.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;, F5 D- ?, t! z5 p
  16.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;9 v% D2 T4 B; R
  17. " @7 I( `* _5 b, x+ j
  18.   HAL_DMA_Init(&handle_GPDMA1_Channel1);# q/ A) [" i5 t; k4 x
  19.    
    : K7 u" G+ ~1 o
  20.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    4 u- \/ ~* G6 U* ~- a) H' t4 @
  21.     " I6 j( B3 |* M" I! O5 b
  22. }% E5 q( C& @5 F, z

  23. 9 i: o; Q# t& o; D1 {
  24. void SPI1_TX_DMA_Config_PID_MIE_Byte_Normal()
    " {; j) y7 \1 q6 Z8 a
  25. {
    1 y6 h; Z; ~- A! B7 m" w$ \, |
  26.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;$ |( Q. o% I6 m
  27.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
    8 l- Q, k% b  l
  28.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;8 V7 n2 z  w2 |3 S  G
  29.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    . i. l% J$ q7 ~  Y5 u
  30.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
    " h& t2 ^1 B5 @, v, h  e9 N
  31.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;" W" |8 I' X& D
  32.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    2 m* d- O: S) D) S
  33.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
    0 R: b8 v1 I; V( r) S# H" V
  34.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    1 _) t* S7 Q: q9 E9 Q1 i
  35.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    & ?# Q* I$ {1 |, f" }9 i5 i
  36.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    0 }# V3 L4 t: p' a
  37.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;6 s0 c# }( ^  Z, e: J  ?
  38.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    # _4 x0 h9 D8 v" {# h3 n0 g5 n3 X
  39.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    " B- s& ^, ~6 f
  40. . `6 z! W: @  o! z  p& ~) G+ s# o
  41.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
    7 x* R5 Y. H# r3 u, S9 u
  42.     7 c* ^# z# H; }4 a/ _
  43.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);; [" [9 g! _$ t4 l2 A" q- }2 p# L
  44.     % Q9 S; O) P4 L0 |! q
  45. }
复制代码

/ h4 ^$ m2 k) R4 C我这里用SPI1去驱动ST7789的屏幕,使用SPI1的DMA发送功能,因此使用DMA传输函数:7 h4 X% X2 Q1 Q' g3 A
  1. HAL_SPI_Transmit_DMA(&hspi1 , pic , num1);
复制代码

, f3 X" V# \5 O& C+ j! J
SPI不使用DMA的波形如图:) V( l8 l! C+ L6 X
IMG_20250522_224448.jpg ! W* Q% m7 D% _9 C
这里有个ST新系列的SPI波形问题,MOSI脚会在发送结束的时候拉低相当长的一段时间才会恢复高电平。, Q. @& D, [" }& o. S
9 k1 x9 i+ v" g, R: y2 |* x
SPI使用DMA的波形如图:) r3 S) S) \; ?' f) c
IMG_20250526_224854.jpg - `$ ?9 Z4 I# x: _  b4 V2 `9 i& N
7 M, ^! Q% @% M' l1 h
使用SPI引脚驱动ST7789液晶彩屏:
, i. H* E9 X5 O+ A6 a u3.JPG
! n6 C4 T0 x- l, f8 Z7 v4 G4 P IMG_20250526_235555.jpg 2 {% D2 d: `& c) T) G* o8 R( {
' z- I5 B, N% h3 J
收藏 评论0 发布时间:2025-5-27 00:09

举报

0个回答

所属标签

相似技术帖

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