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

【STM32U3评测】SPIDMA发送

[复制链接]
donatello1996 发布时间:2025-5-27 00:09
      SPIDMA发送/接收是非常常见的MCU应用,用于与高速SPI器件通信并提高效率,这个帖子是评测STM32U385RG的SPI1接口,并启用SPI1的DMA发送功能。
, Q% ^3 o- H, V# l9 ?" ]: I2 K$ t; w$ ?
       在上一个帖子中提到,STM32U3的DMA外设名为GPDMA,在ST新出的系列中,任意支持DMA的外设都可以绑定到DMA的任意通道/流/节点上,不像F103和F4系列那样规定通道号,来看看SPI1DMA的配置:
5 B7 M& B' l. q' L; Y* }$ @9 _2 ]
, M7 z$ r& `  W  ^ 8.JPG   h) {0 H; E3 y7 u* C; f6 C. c0 r9 M* U
* A7 N) ~! p* P. ]( F( W9 ]. {
Normal模式表示每次调用DMA发送函数只会发送一次,Circular模式表示只调用一次DMA发送函数就一直发;在外设所使用的DMA中一般配置外设地址非自增,内存地址可配置为自增或者非自增,内存地址非自增表示DMA发送的数据是同一个数据,也就是内存首地址的数据;源地址和目标地址的数据格式必须一致,都是8位或者16位;只要SPI使用了DMA进行发送或者接收,就必须要把SPI中断和DMA中断都打开,少打开一个都不会正常工作。
8 e, i" {( a1 V7 C6 }2 j; M
/ D1 m5 m& \% D7 E( D$ ?SPI初始化代码如下,在ST新系列中加入了一些不太常用的SPI机制,如CS片选引脚空闲等待节拍,Ready模式,因为在大部分场景中片选引脚都由GPIO进行控制,比较少用到SPI外设自带的NSS引脚功能,除非是双MCU通信:" ~1 A, w; H7 ]
  1. void SPI1_PA5_PA6_PA7_Init(void)
    0 Y2 W- E1 H* v5 d+ N
  2. {" ?  C' y/ T* C
  3.    
    * {5 \" O+ w# y$ o& L* n
  4.   __HAL_RCC_GPIOA_CLK_ENABLE();
    ' [" K7 `0 k8 y. c* @4 f! Z6 P
  5.     __HAL_RCC_SPI1_CLK_ENABLE();; j# s* e) z( b- S7 E: V
  6.     __HAL_RCC_GPDMA1_CLK_ENABLE();% U) S1 {" ?* b1 [
  7.     4 ]) X$ I9 B, Z8 M) g' r% O
  8.     RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    + J* o" x0 i% r
  9.     GPIO_InitTypeDef GPIO_InitStruct = {0};5 K$ B6 D  F9 e8 h+ A5 C
  10.   SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};
    " X. }; H/ u. o! N4 O. F
  11.    
    7 y9 U. x. T6 }  a/ e1 Z- X
  12.     PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SPI1;* C, A; N9 I. ?! H, F, R$ a
  13.   PeriphClkInit.Spi1ClockSelection = RCC_SPI1CLKSOURCE_PCLK2;. K8 l. X  q* [1 Y
  14.   HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
      Y/ a+ o( J1 N0 a) h
  15.     , y  h7 w- |0 f/ N/ ?+ b
  16.     GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;. v, k" S0 t; g/ \1 d
  17.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    6 e( c. K' ^0 Q. u: @7 f1 K/ q
  18.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    2 }5 e# ^5 P7 m: d. b# w* L- X
  19.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;6 ~/ V9 w( C+ T4 P4 @
  20.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;+ @. @, s' f& f" \3 ~
  21.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    ! M9 X6 X. a! L

  22. % v( T7 A6 _9 C2 ^
  23.   hspi1.Instance = SPI1;
    / H+ i: u. O2 @) p7 ?  c8 U
  24.   hspi1.Init.Mode = SPI_MODE_MASTER;
    % H, Q7 J! C, d% I
  25.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;! S7 n/ k' {" X0 F5 Z5 R/ S
  26.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    - `6 N0 B& ?/ d) ^7 F9 Y
  27.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;, u" u, I4 a4 S. D
  28.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    $ W8 H3 P, v2 `: c5 C
  29.   hspi1.Init.NSS = SPI_NSS_SOFT;( q; _, x6 S) R) J: a
  30.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
    ! h+ O; L: i2 U5 G' D  J" H. Y
  31.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    * p- p  r6 g, o
  32.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
      B7 ]! x) N8 K5 m
  33.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    : y2 [+ c2 }7 J; \3 D" [1 K
  34.   hspi1.Init.CRCPolynomial = 0x7;
    9 i+ r$ s/ q: U% H: l7 N  H
  35.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
    . `  `0 n5 |1 T  R7 C( C
  36.   hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;+ |" Y7 C/ Z# T: z( p5 ^7 P3 R
  37.   hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;! q. b" B6 _+ q2 [  B6 E6 @
  38.   hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
    6 [! Y  a3 b2 C7 A: ~
  39.   hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
      E& k  |9 z' y+ E. B: t( L
  40.   hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;& j! ^0 F5 K; a* S/ `
  41.   hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
    3 S, e% {& B# W
  42.   hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    : }; D0 k2 a5 e% S% `
  43.   hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
    # G4 }' ~6 n& u& H7 n/ X! I- V1 u
  44.   hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
    2 j& G0 y4 _- R. ?, h7 I5 J) G
  45.   HAL_SPI_Init(&hspi1);
    : y. D0 E: O/ O5 M( G
  46.     __HAL_SPI_ENABLE(&hspi1);/ X: r0 w* l7 C0 _7 @' N: U
  47.     & J; X3 O+ f+ V& y+ v4 M
  48.     HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);& N) B  m- r" I. x; D8 Z
  49.     HAL_NVIC_EnableIRQ(SPI1_IRQn);5 c6 D) T, F$ l; @( x4 `0 \4 J
  50.    
    2 T% j  q" g  c' e$ C& v
  51.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;
    0 T, p7 R8 |. p/ E7 a- T
  52.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;
    ; r9 r# u- ?: V- A! f
  53.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;) v0 t9 O" B( x+ B
  54.   HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct);
    5 l, Y4 B" W; u: C! i

  55. % Q$ D5 \- Y' K+ f, E. T6 b
  56.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;7 w5 _1 ^, R6 J
  57.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
    $ j  ]  [& [% j& @! S" F9 a
  58.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;$ {6 }. e) q( o
  59.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;7 _' s% P: t, s( p# C/ `
  60.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
    ) h- Z3 C. E! u8 S/ f) v
  61.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;- e7 C6 l" @; Y- w4 ?9 R
  62.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;( S4 |0 m, d: c$ z
  63.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;( ~+ g9 f+ F8 n0 V& V
  64.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;' M% Q; P* H' c7 @6 a0 \% l
  65.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    8 |# {- x6 j4 `: U6 N  q
  66.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    " A% S( l$ a; f, ]* g3 T8 T
  67.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;% G) Z6 V, A2 F* R$ d% ]% o4 w
  68.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    2 {" y1 b2 N' u6 ^9 x2 d9 C# h9 }
  69.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;; g* @- T+ E: I* V6 a" X0 u9 i3 ?
  70.     HAL_DMA_Init(&handle_GPDMA1_Channel1);* Y+ n2 j- C2 V) I* l$ l% @

  71. ; ]6 R& V( I# M; V
  72.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);; ]8 |2 n8 _1 m, z8 E& n
  73. ( e7 @0 Q/ {5 b2 M5 E- l5 c" |2 [
  74.     HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV);
    5 Y* K/ s4 Y& ?7 j/ t
  75.    
    5 L* O8 b- V% e% d9 `( c. _
  76.     HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);
    & W3 k+ ^6 ~# p( x# I. {- n; ^
  77.   HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
    0 t0 N0 I- ^5 D; H1 B/ ~- b
  78. . v; a/ o/ L" u9 F( @; L, t) j
  79. }! w' E% N1 v& \- M/ t. ?
  80. 3 J6 U! z9 ?  W9 A; l
  81. void GPDMA1_Channel1_IRQHandler(void)
    , Y0 i& w4 ]+ U' Y! N5 J
  82. {
    ! w7 V1 n7 _  Y: s  \
  83.   HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);
    2 M3 L- G) L; @# \. o0 g
  84. }
    6 ]) F* K7 U- x6 m  g

  85. 7 v0 I" q+ m5 J
  86. void SPI1_IRQHandler(void)
    % q& m8 U0 K! H5 V2 X
  87. {
    , H! s9 j1 f  D
  88.   HAL_SPI_IRQHandler(&hspi1);
    1 M- s4 z5 F- I" c) J
  89. }
复制代码
- ^3 c1 ~7 m$ o3 x; R
: x/ g+ g4 }( I! ?& p
SPI发送或者发送+接收代码如下:
9 e' A( C" \1 a' o; Y3 {
  1. uint8_t SPI1_PA5_PA6_PA7_Read_Write_Byte(uint8_t TxData)! Z+ @( w3 o: |  q7 ^
  2. {5 j7 i& ]" S5 Z- y) T
  3.     uint8_t Rxdata;, ?/ B0 q: `$ y* ?/ U% n
  4.     HAL_SPI_TransmitReceive(&hspi1 , &TxData , &Rxdata , 1 , 1000);       , h: g( o, Z: p5 Z3 q
  5.     return Rxdata;
    & R. X8 E- p: M! j9 ?
  6. }
    ; @, w. c5 x, x* c. {3 K
  7. + f3 _! r, V8 c9 s4 P/ A4 W3 e
  8. void SPI1_PA5_PA7_Write_Byte(uint8_t TxData)8 V  L+ k; s8 p/ H6 B/ M# S
  9. {, e; A# {/ R, m
  10.     HAL_SPI_Transmit(&hspi1 , &TxData , 1 , 1000);
    / L* _. x5 H5 c0 m/ |+ d& x% N
  11. }
复制代码

, M9 s+ d( H) Q% S4 x1 Y
6 a6 I. _$ V0 q) o6 G  g3 V' g) [  Y6 R1 M' D- c# _4 w; a
SPI随时需要调整数据位数为16位或者改回8位:
5 a1 ]/ e& [$ T2 N  [
  1. hspi1.Init.DataSize=SPI_DATASIZE_16BIT;
    # c3 C; G# E8 j* `+ t7 h8 h
  2. HAL_SPI_Init(&hspi1);  i; U! W+ O" a) o, W

  3. 4 L+ F4 J0 a. Z
  4. hspi1.Init.DataSize=SPI_DATASIZE_8BIT;( _# N- K4 B! ~; T* T% U& [6 j
  5. HAL_SPI_Init(&hspi1);
复制代码
' O  `; J8 V$ Y5 R
在实际应用中,常常需要修改DMA参数,最常见的就是内存地址自增或者非自增,数据是8位或者16位(Halfword半字)对齐:
2 ~! }( g- j0 L2 L7 s
  1. void SPI1_TX_DMA_Config_PID_MID_HalfWord_Normal()
    & a% o: S$ I3 G2 Y
  2. {6 P$ V7 T% R; p" E7 \9 l6 j
  3.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    " w- q* _2 j4 a+ Y
  4.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;4 Q' ]# {# s& u; ^% _' Q
  5.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    4 H% Q: m! ]$ l6 j* G1 q) v: y
  6.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    7 k. A2 T! h: b" @
  7.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;
    9 M/ T6 ?6 ^# `- r
  8.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;. _' _) L! K6 g2 z- X
  9.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;5 G+ g  f- E7 ~! z# I6 O; T* T
  10.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;" A# c) M' J0 x: I( _2 Y; O8 }
  11.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    / _. F9 ]8 F! c1 ^+ G8 z4 n0 g
  12.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;. C3 g% d# L* t2 ?0 V# q
  13.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;0 E; _, K( c0 q  e" L5 @% n
  14.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    6 }; q# w  ]( h! Q! p4 ^
  15.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;& A: u* }: D' v0 [# w
  16.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    , g; v( @7 e$ t$ ~% T/ f2 N

  17. ( F0 o/ V# K! o/ W8 P5 F
  18.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
    * K  x: o, Z4 g* r0 o7 X$ n5 G& e0 u
  19.     ( W7 m0 M9 L4 N: t* U' Z' s, ^- T
  20.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    ) f3 Y' s  J( A% J% ~
  21.    
    - A! b5 z& m4 B' D/ B$ |4 j2 _
  22. }
    * z# E& N0 V3 Z! v+ t3 ~# q
  23. " ~- i+ b8 s8 P( Y# c
  24. void SPI1_TX_DMA_Config_PID_MIE_Byte_Normal()  h3 ^2 n5 I; }3 p$ |8 ^
  25. {. x& `& _5 u( p( R
  26.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    2 V) F/ ?% r. K. L/ `: u
  27.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;$ U. U+ ~/ F, \( q: r# `3 W" }  l
  28.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    ' R3 \0 R! S0 q$ a7 w
  29.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    6 [: J' ^5 q& ]  T$ F
  30.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;$ V+ A' `7 x0 u1 y0 h
  31.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;* f, `; z3 ^0 _; W% J/ C4 @
  32.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    " u& ~, \; g2 M! U% o
  33.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;& @- \& s% z+ e3 }+ d( |0 {
  34.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    ) w6 F% ?5 g1 v$ |
  35.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    6 x  O  J4 E; h; }. ^9 B
  36.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    4 t* v% ~! B6 ?' g. W8 b
  37.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;/ _; f/ D; Y6 V: l/ f
  38.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    2 j( I. r( {2 g' T. j. s4 F
  39.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    & w9 `  ?; c" ^

  40. # g/ Q8 v# k/ ?& e2 ?
  41.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
    + X. d: \3 p0 H4 w/ ?  |0 F
  42.     & U4 ~7 v# U, _/ ^+ Y
  43.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
      Y( a1 e8 H! F; y' @
  44.     9 c& s4 e' i4 S. ]- c7 q
  45. }
复制代码
& Q4 [4 l; ]8 R" t& }3 u2 Y' i; v
我这里用SPI1去驱动ST7789的屏幕,使用SPI1的DMA发送功能,因此使用DMA传输函数:
; X& s) c# e5 k6 c# D
  1. HAL_SPI_Transmit_DMA(&hspi1 , pic , num1);
复制代码
, l0 f# ?. p" x4 b1 {
SPI不使用DMA的波形如图:
, f# ^! ?7 `6 x( A IMG_20250522_224448.jpg
  Z, M! c, K$ H4 Y. e$ U这里有个ST新系列的SPI波形问题,MOSI脚会在发送结束的时候拉低相当长的一段时间才会恢复高电平。
% g8 q- p! z, ^8 J# z- a
7 M+ O1 y2 B4 Q# Y' K
SPI使用DMA的波形如图:/ z9 l& Z- i& y: B' A$ K8 _
IMG_20250526_224854.jpg
; R4 Y2 ?  T; u& L4 l6 V1 t
& _# \( R5 s( r2 W4 k0 ]
使用SPI引脚驱动ST7789液晶彩屏:  \& g0 w" Q  b9 v6 \
u3.JPG
1 `6 s& {, d+ ]" A6 k+ e( W! F" m IMG_20250526_235555.jpg
' m- q" q% M3 E
; q! R8 u& P' W
收藏 评论0 发布时间:2025-5-27 00:09

举报

0个回答

所属标签

相似技术帖

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