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

【STM32U3评测】SPIDMA发送

[复制链接]
donatello1996 发布时间:2025-5-27 00:09
      SPIDMA发送/接收是非常常见的MCU应用,用于与高速SPI器件通信并提高效率,这个帖子是评测STM32U385RG的SPI1接口,并启用SPI1的DMA发送功能。
- q( D! o5 H7 p1 j' @
6 o, Y, Z/ w, u! h( u
       在上一个帖子中提到,STM32U3的DMA外设名为GPDMA,在ST新出的系列中,任意支持DMA的外设都可以绑定到DMA的任意通道/流/节点上,不像F103和F4系列那样规定通道号,来看看SPI1DMA的配置:
1 B2 M$ B: p5 N: q, p
4 K& r# f+ A4 o& U7 Y3 {$ T# Y 8.JPG
+ r4 p" o7 w- z

  q: Z9 w0 {0 I8 v6 TNormal模式表示每次调用DMA发送函数只会发送一次,Circular模式表示只调用一次DMA发送函数就一直发;在外设所使用的DMA中一般配置外设地址非自增,内存地址可配置为自增或者非自增,内存地址非自增表示DMA发送的数据是同一个数据,也就是内存首地址的数据;源地址和目标地址的数据格式必须一致,都是8位或者16位;只要SPI使用了DMA进行发送或者接收,就必须要把SPI中断和DMA中断都打开,少打开一个都不会正常工作。
9 D: O$ |6 e* P% ?, q# @% Y+ q
( \: g8 ^% U# \. G+ \9 u1 L( v! K7 ]SPI初始化代码如下,在ST新系列中加入了一些不太常用的SPI机制,如CS片选引脚空闲等待节拍,Ready模式,因为在大部分场景中片选引脚都由GPIO进行控制,比较少用到SPI外设自带的NSS引脚功能,除非是双MCU通信:
5 P" i1 T4 p! v6 G6 h. N% ]
  1. void SPI1_PA5_PA6_PA7_Init(void)( L9 Q% A7 U1 {8 e$ A
  2. {* M; f1 f8 O6 b
  3.     ; p$ e9 i6 Q" V9 D+ N. T
  4.   __HAL_RCC_GPIOA_CLK_ENABLE();
    1 G% c; q3 }4 u$ v$ d
  5.     __HAL_RCC_SPI1_CLK_ENABLE();! c5 N: a! r2 J( S- k
  6.     __HAL_RCC_GPDMA1_CLK_ENABLE();
    9 w: \1 g8 w3 {2 q% i+ S
  7.    
    % q% h, u& L( t4 K9 I# p
  8.     RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};/ D& \" V/ E$ x+ [
  9.     GPIO_InitTypeDef GPIO_InitStruct = {0};
    ) k4 x& F' k: D8 h
  10.   SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};, l4 m$ C9 V! F
  11.     # m, x1 J2 _! ?: w
  12.     PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SPI1;. p: l  @3 n" N  p) b4 l
  13.   PeriphClkInit.Spi1ClockSelection = RCC_SPI1CLKSOURCE_PCLK2;3 X: i; j# E! w
  14.   HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    ' U0 U% K: H0 Q. l
  15.     % K0 S; F& I( e4 q0 Y2 |8 ]8 y( ?
  16.     GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;* w6 I& r+ X2 g$ ~; L
  17.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    4 y. y/ }" k* q; f# n8 C* W
  18.     GPIO_InitStruct.Pull = GPIO_NOPULL;- @  |' m: X& R  a7 J; r
  19.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    + k2 U( A' @! G
  20.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;7 L" d  q3 U- u  M
  21.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);5 P% R& D3 [& L8 v" C- N

  22.   U7 r: v3 O. f: ?, ?; L( w  l3 K
  23.   hspi1.Instance = SPI1;
    7 T5 M3 i5 A; h8 `6 D# a  y
  24.   hspi1.Init.Mode = SPI_MODE_MASTER;* G' z) n! V) S! y% G5 O
  25.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    ' C8 i( v* r& i- x# F( N
  26.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;1 V$ B# Y9 C  p$ T  k) z' Q. c
  27.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    7 f; z0 f3 l. t6 w
  28.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    6 B! ~) F) G. F$ c8 F
  29.   hspi1.Init.NSS = SPI_NSS_SOFT;9 j. I' g3 n* [4 b
  30.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;" y2 D  _" E, }+ l, q9 p, X+ M
  31.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    + I5 }& ~: `9 N8 B  t
  32.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    & {% ~, Q' N+ y! {9 E7 F
  33.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    ! E( M4 z" n/ p- H  {
  34.   hspi1.Init.CRCPolynomial = 0x7;% I" a, z1 A: Q# I" d
  35.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
    7 ?% K5 |5 R' W: N% K! r
  36.   hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;7 v0 G, }8 L: [0 O2 n8 C
  37.   hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
    - [/ n, z- B* w8 f  W
  38.   hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
      |5 g: h# U/ D* J0 L! |' w
  39.   hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    1 c+ P6 o  ]" s) w
  40.   hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;3 j- @& r% [  {6 j" B
  41.   hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
    9 m  n3 I& g+ U# s0 X
  42.   hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    4 x+ [2 c) T2 }
  43.   hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;/ U7 T) H) t7 a; ^% P& }# R
  44.   hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;! q! o( M5 C' d' ^% a( k! e8 P
  45.   HAL_SPI_Init(&hspi1);
    " o8 U! p: b6 n1 h$ Q3 G
  46.     __HAL_SPI_ENABLE(&hspi1);
    ' O5 f5 @; z8 C  p6 {) d: I
  47.    
    , r+ \4 b; |+ |2 Q. d. ~" Z* @
  48.     HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
    ' A; [% U  m' X8 R5 d
  49.     HAL_NVIC_EnableIRQ(SPI1_IRQn);! Z+ Z0 ~5 \* n2 _$ q- P5 Y: F
  50.    
    * P1 h. o7 N: E
  51.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;
    & C( c+ w2 P5 S4 T
  52.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;
    * y! F2 i) ^. M
  53.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;
    2 z: _3 M0 D4 ?
  54.   HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct);
    & {/ y' N4 p; T. B, K* H6 u+ l, n
  55. ! T8 j, A  b- b# E1 G. e
  56.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;! e$ m! |( ^* B( n  q- [8 S2 |, @* h6 F
  57.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;, o3 `% N! o( S+ x& M1 i# O9 X
  58.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;; y/ V1 ?5 S* F# h$ ~
  59.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;# M' U1 n+ B' Y+ E
  60.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;' G+ S1 S% f8 q- j
  61.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;9 Z/ A' W0 ?1 Q" m; ]4 a
  62.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;+ @: ~/ I7 B) L, c8 G
  63.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;+ t& a! _+ J2 L# E7 @# c% `
  64.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;7 y  m% [/ A1 L) Z# C
  65.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;' B% w( V! X: J
  66.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    , h: W" J8 l6 x/ C( q0 ~
  67.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    $ `, ~5 q' o9 W+ S8 s4 a5 M
  68.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    / |7 j/ H" i& p
  69.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    4 {5 l0 V. h2 }7 i1 G$ Q) N
  70.     HAL_DMA_Init(&handle_GPDMA1_Channel1);. u- S% I/ R  P+ o# v
  71. , o# k8 q3 U4 T. A0 J- g7 G
  72.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    6 @1 |  t' J* U  M+ h$ ]6 ^1 E

  73. $ w( I, R: P8 V
  74.     HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV);
    4 c9 i: P' b# v5 C" j( S; J
  75.     9 k; k; F( Y1 [9 u8 l" s, d
  76.     HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);
    6 O4 o! L1 _& b$ {9 {& H1 j" V
  77.   HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
    8 i6 d2 g: _4 z9 Y; _

  78. 9 T5 s% ^2 y1 H) N  {6 r; k. W% N
  79. }
    & _6 K+ Y5 Z8 ?7 E( b
  80. 1 Z5 A- r4 f5 k. R9 e
  81. void GPDMA1_Channel1_IRQHandler(void)9 M8 ~6 q+ y% y% ?. q
  82. {/ Q3 I* k- D( Q% i
  83.   HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);* F; F7 L) Y) s. I
  84. }
    * ~  B# j* }9 C3 B  B
  85. ! l3 l5 ]5 K5 C5 p! x" K- Y
  86. void SPI1_IRQHandler(void)3 U- u& w' |4 _. E! A
  87. {  L$ y* P) e; p/ q  J4 ^4 u! r6 N
  88.   HAL_SPI_IRQHandler(&hspi1);
    + b3 a: [+ m2 Y0 J3 v
  89. }
复制代码

( B3 t1 E' }9 T4 k
- D+ h! }' c( y; aSPI发送或者发送+接收代码如下:' L' g: j+ {5 ]! B
  1. uint8_t SPI1_PA5_PA6_PA7_Read_Write_Byte(uint8_t TxData)
    8 I1 M. R' x0 h
  2. {
    * Y: Q) h% N* K0 y
  3.     uint8_t Rxdata;' I2 V& Q4 Q- s7 s" D: F& W' `: Q
  4.     HAL_SPI_TransmitReceive(&hspi1 , &TxData , &Rxdata , 1 , 1000);      
    4 k" \  P9 a( D- j* B
  5.     return Rxdata;
    9 W+ x6 v! s+ O# g/ \2 V' r
  6. }3 Y* [* ]# U; ^$ z8 d
  7. 3 o5 W2 N3 @0 n/ P2 y
  8. void SPI1_PA5_PA7_Write_Byte(uint8_t TxData)
    0 a+ S* Z4 @' m
  9. {
    - h( I; }7 ~& I- G* \. V! H( d* N
  10.     HAL_SPI_Transmit(&hspi1 , &TxData , 1 , 1000);  c3 a: \/ J7 e- [+ G. z1 R& Y
  11. }
复制代码

4 t- }- p7 _# R  ~; n. }: T) a% h4 ]4 ^. m1 `3 R+ D# @* R. }! M
6 Z) M6 L& |3 [
SPI随时需要调整数据位数为16位或者改回8位:. R, }3 Q: @- v* [; ~& I3 ^. j5 V" A
  1. hspi1.Init.DataSize=SPI_DATASIZE_16BIT;1 B0 ]! h( K+ ~8 K- O
  2. HAL_SPI_Init(&hspi1);
    ( T9 M# @! y0 L! _& q  G2 a8 N
  3. 2 |- @; ^  r- }) v  V& z' J
  4. hspi1.Init.DataSize=SPI_DATASIZE_8BIT;
    2 R/ l$ d  ?4 y: o$ ]0 M5 i6 A: B2 z
  5. HAL_SPI_Init(&hspi1);
复制代码
: z' h5 O# G8 d3 g; l$ ]
在实际应用中,常常需要修改DMA参数,最常见的就是内存地址自增或者非自增,数据是8位或者16位(Halfword半字)对齐:
2 h* f$ g( q+ Y% J
  1. void SPI1_TX_DMA_Config_PID_MID_HalfWord_Normal()
    7 T5 U  k8 k: R( ~- w* ]& [
  2. {' ^9 J9 q) i. ~; V; b( Z0 C9 r% t. b
  3.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;$ ^( ?3 ^. ^( u$ U. ]
  4.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;2 Y$ n! L2 k& C6 s$ b# K
  5.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    1 G* }/ C0 g4 V! y
  6.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    . B0 \$ |  O* {
  7.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;( O$ n! A& z6 F
  8.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
    , \+ o8 Y6 Z0 s6 K& i
  9.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;1 F" S# \0 K) f4 P
  10.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
    5 R2 b" h8 Y( ^
  11.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    % ^% U- _* m8 J8 B! z1 j
  12.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;* Q0 D% t9 {+ V$ n
  13.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    . ~, L9 t% y/ a# |: M' n: ]: N
  14.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;1 D% |$ h0 v7 W5 I$ x' K9 G/ j
  15.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    . b3 d# @$ a0 v& w5 E
  16.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    3 A; G8 `4 [2 B4 W' w3 H5 ?, `
  17. % L  ?" E$ }$ ~
  18.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
    ' m' m- l1 B& e( j9 E, u8 ?2 q3 a
  19.     3 ~# B$ O0 ^1 Z; Y
  20.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);; c+ ]+ L2 h2 t! A! S
  21.     9 m2 a' |( L2 E" i
  22. }
    " T2 a0 c  e! W5 U1 f
  23. ) q" g1 s- F/ q4 _% E
  24. void SPI1_TX_DMA_Config_PID_MIE_Byte_Normal()
    4 `0 l1 N, e* M5 G" g
  25. {; ^0 ?( o/ S4 w% w+ A
  26.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    1 ^7 f9 Z& w: v2 C; U
  27.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
    + E! H6 }# \( e* w
  28.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;3 r/ a4 I2 Z4 e, X3 Q! |
  29.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;1 }2 c4 a# U" w6 S% w
  30.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
    : B1 L) U" |' C
  31.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;5 A4 `, W* \  \* d  ?3 h& L
  32.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    ; D+ e% Y2 R% |/ c
  33.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;2 C: i+ x0 J8 e" ~6 g1 M) A8 C# O3 {6 F
  34.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;3 R: `/ z2 A0 F$ L7 S0 l
  35.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    ; t) H/ p. A* x' E6 w
  36.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    0 C1 q5 O- N: M! ?
  37.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    + o. I& }3 L! h4 ~
  38.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    9 i. [% l' L6 e# O" b  l% ]
  39.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;6 F9 ]7 I$ h" ^

  40. ( C; q% r3 Q) l
  41.   HAL_DMA_Init(&handle_GPDMA1_Channel1);7 n! t3 g9 L* n4 O% R
  42.    
      @: y  C7 R9 `% S  @" }. H7 z1 [
  43.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    . \! \0 b/ t; z0 ~- [. G
  44.    
    - B' t4 b2 A0 T2 \
  45. }
复制代码
. C- o! d) c* E6 q  i9 l
我这里用SPI1去驱动ST7789的屏幕,使用SPI1的DMA发送功能,因此使用DMA传输函数:
3 b/ M% ?6 y/ C0 `1 d& n5 ^+ N
  1. HAL_SPI_Transmit_DMA(&hspi1 , pic , num1);
复制代码

* y$ A$ ?8 F1 i' o) S' S6 Z
SPI不使用DMA的波形如图:* U- m9 T$ q. y" z1 T4 `2 y
IMG_20250522_224448.jpg 5 _- t7 V, b) @4 W3 n7 ~
这里有个ST新系列的SPI波形问题,MOSI脚会在发送结束的时候拉低相当长的一段时间才会恢复高电平。
% G4 d1 C+ Q0 F9 ~
+ z# m0 k- {4 X3 u2 d3 y: V
SPI使用DMA的波形如图:
9 c# ^. Q% \3 L" z# C IMG_20250526_224854.jpg
2 F! K1 \% ~% @
9 O9 W- x& r7 B+ U' o( L# |
使用SPI引脚驱动ST7789液晶彩屏:; r  S# a9 q  j- E2 r$ |0 ^
u3.JPG 8 x" p  b* `! t9 I" S6 Q( p( r% k
IMG_20250526_235555.jpg - e: |2 z( f) g8 Z6 N& z) g- Z

0 G7 j* C. o: }9 R& ]9 K/ J8 ]
收藏 评论0 发布时间:2025-5-27 00:09

举报

0个回答

所属标签

相似技术帖

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