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

【STM32U3评测】SPIDMA发送

[复制链接]
donatello1996 发布时间:2025-5-27 00:09
      SPIDMA发送/接收是非常常见的MCU应用,用于与高速SPI器件通信并提高效率,这个帖子是评测STM32U385RG的SPI1接口,并启用SPI1的DMA发送功能。: l: B" [( C+ M; m9 t; S" w

) H( W' c/ z2 e
       在上一个帖子中提到,STM32U3的DMA外设名为GPDMA,在ST新出的系列中,任意支持DMA的外设都可以绑定到DMA的任意通道/流/节点上,不像F103和F4系列那样规定通道号,来看看SPI1DMA的配置:5 g2 I2 W9 [4 r& {
6 `7 p) c  {0 q; v
8.JPG ; t9 Z- j2 E/ [: H: V8 H8 z: t

4 r+ K; z" A+ N3 _& j; J4 YNormal模式表示每次调用DMA发送函数只会发送一次,Circular模式表示只调用一次DMA发送函数就一直发;在外设所使用的DMA中一般配置外设地址非自增,内存地址可配置为自增或者非自增,内存地址非自增表示DMA发送的数据是同一个数据,也就是内存首地址的数据;源地址和目标地址的数据格式必须一致,都是8位或者16位;只要SPI使用了DMA进行发送或者接收,就必须要把SPI中断和DMA中断都打开,少打开一个都不会正常工作。
" U, ^& r" F9 A* J/ a2 X( a+ T: i  N) Y7 e7 H" @$ r7 ^
SPI初始化代码如下,在ST新系列中加入了一些不太常用的SPI机制,如CS片选引脚空闲等待节拍,Ready模式,因为在大部分场景中片选引脚都由GPIO进行控制,比较少用到SPI外设自带的NSS引脚功能,除非是双MCU通信:
; _5 i7 U5 i8 y! v8 A5 i# m1 F
  1. void SPI1_PA5_PA6_PA7_Init(void)- h2 \) _' ?( N& q- n7 ^8 P8 a$ W
  2. {
    # F9 r1 J, v" s- O4 K" I6 o
  3.    
    $ W+ C2 n# t8 _' m# U" u  n& \5 ?0 H
  4.   __HAL_RCC_GPIOA_CLK_ENABLE();
    0 J# D, f& i4 U! ~' n6 h, g
  5.     __HAL_RCC_SPI1_CLK_ENABLE();
    ) T& o5 Q1 m, ^+ N
  6.     __HAL_RCC_GPDMA1_CLK_ENABLE();# r2 t/ ?& v. T% F9 ~/ \' N( I
  7.     ' C  [$ Z- c, q
  8.     RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};" f+ _* A5 U, K9 C5 f2 {
  9.     GPIO_InitTypeDef GPIO_InitStruct = {0};; c2 y+ W1 u# z) }% v. d) k
  10.   SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};
    ' x6 R. \% g. @! M( G$ n: J: a
  11.     - q6 Z, c+ \9 |
  12.     PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
    # Y4 g+ I1 X0 Z5 \7 l
  13.   PeriphClkInit.Spi1ClockSelection = RCC_SPI1CLKSOURCE_PCLK2;
      s  ~8 h( G6 t$ `5 S9 ^/ l  t7 O9 D
  14.   HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);3 X/ E; b+ f7 F1 M8 X
  15.     + |& ?+ X$ t& v2 J9 L& i1 X
  16.     GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;' s% V4 X. T1 E; W& R
  17.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    ' Y* e9 s1 e2 c% M
  18.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    1 W0 i: |2 r* l/ u$ o- b
  19.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    6 k; k$ L: f2 S( V' k" p7 g
  20.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;! F4 o: B6 _9 }* u6 X
  21.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    ; n5 w6 l: z4 c

  22. , j( ~0 a. }3 u* l5 e: j2 I
  23.   hspi1.Instance = SPI1;
    , P0 M7 g' K8 l, f6 q- ?
  24.   hspi1.Init.Mode = SPI_MODE_MASTER;
    7 }1 U' J, C/ s
  25.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;6 A' F7 b* G3 }" p& Q' @
  26.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
      h5 k; Z) T! [3 _2 O+ e. h* h/ y
  27.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    3 I3 b$ a% Z! @* V- a
  28.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;. X$ i4 P8 E; l' R
  29.   hspi1.Init.NSS = SPI_NSS_SOFT;7 N* y1 _  _2 c; P& \' K
  30.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
    ) w2 c- Y2 s+ T  Y  I8 |
  31.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    7 a- v5 Y3 N! B0 b9 G+ G7 v
  32.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;" r6 q8 [6 t, l6 W$ [3 @
  33.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    - L9 y; L# _3 j4 D; ^* w& O+ F' {
  34.   hspi1.Init.CRCPolynomial = 0x7;9 w0 m3 R2 ?9 U3 S! L5 Q
  35.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;3 Y$ u( _" _) S6 {2 n
  36.   hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;+ Q5 Z. ^7 q3 o+ q& H& o
  37.   hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
    7 e& K8 P/ M: f9 u2 N
  38.   hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;1 y( w) y! [  T+ ]  u! H4 y9 N9 h
  39.   hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    ; _3 n6 @) w* E5 h/ O- H
  40.   hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;, _% _! d  `" m& ?
  41.   hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
    2 A8 _3 r5 n* o. i  P3 {% m
  42.   hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;: K2 w" w5 _  t7 y- v) r
  43.   hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;& [# J$ g8 e- b( X& d
  44.   hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;6 J2 p& p4 x( I3 w8 ~' t
  45.   HAL_SPI_Init(&hspi1);; J% _8 I# Q  h2 p. D, E& [
  46.     __HAL_SPI_ENABLE(&hspi1);/ G# d+ ?( j( Q  O3 z* f- {$ ]& G* J
  47.     $ j) V1 e, [6 I9 F, c
  48.     HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);: D0 P9 S# ?/ k9 y7 v  a
  49.     HAL_NVIC_EnableIRQ(SPI1_IRQn);
    ' a' l  E" J: @+ \9 v
  50.    
    ; k1 B2 Z5 l0 J6 r' [# z
  51.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;$ E- W( ~9 {7 E  w, e+ n2 R
  52.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;# L2 X$ y: ~$ o1 j) V7 `
  53.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;, T5 b8 ]( `0 e+ U2 U/ Z8 B7 K
  54.   HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct);  U" \/ l7 J: }2 e
  55. , H8 N+ d9 L5 _# s- q2 A" F& G8 ^
  56.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;9 J$ w( I* @7 _  O/ ^  i
  57.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
      t: X7 l8 f2 j& L
  58.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;1 F8 W( Y5 u- ]
  59.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    % y! x! }, k4 ~9 ~4 d9 {
  60.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
    1 ~; L+ w- a3 H) L6 q" g" }1 ~
  61.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
    1 W- v" k% _& X- z( c
  62.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    6 N; \8 T" I& U0 {3 `2 E
  63.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;% h; ?0 e+ H- f
  64.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    * \4 z5 M( [# Z' R& V6 c7 J
  65.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;- D! q2 j% g. ]% x) y# m
  66.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    0 Y  a8 m- {+ m9 R
  67.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    ; W, T' d- T8 A& j! F! g* l( x
  68.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    3 K* V$ B" d5 r8 H, N# H  V
  69.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    ! }" p3 s, r! z; A  ~
  70.     HAL_DMA_Init(&handle_GPDMA1_Channel1);
    " F* k9 j  v# a

  71. : [+ o  u1 T. l: V
  72.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);% w  a6 B# D: g* n2 ~: E. j! c
  73. + n9 a, l& ?0 k) a. S
  74.     HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV);
    6 v, M/ B1 F" u* j" z: x
  75.    
    , c8 a* m: v9 I/ p+ }5 x; O0 Y
  76.     HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);) T4 B+ L- G2 ~  l; I3 Y" B
  77.   HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
    # B( W4 |! `3 y  M, N2 K

  78. & ?2 Z* m0 t! Q& e( x
  79. }
    & q2 _# a& m+ ~. D, Z" ?

  80.   W* j+ @- o, M0 \
  81. void GPDMA1_Channel1_IRQHandler(void)* `* {" j/ ^- w0 D
  82. {$ y1 w  x" X$ A" }( R9 P0 w8 {
  83.   HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);! u0 m/ v) ~, s8 I& f- D
  84. }
    8 J5 i5 o& Z. }3 m7 q/ ^" c# D
  85. " k2 b) t4 w' E% j3 n
  86. void SPI1_IRQHandler(void)! f* O! H7 L- L" O7 I6 t
  87. {
    ; k+ Q0 s: D; M3 _" C" C/ W
  88.   HAL_SPI_IRQHandler(&hspi1);6 K2 O) T$ n: D3 E$ ~
  89. }
复制代码
6 }& g9 ^6 y. R! y

; K3 A' h2 |" Q# TSPI发送或者发送+接收代码如下:
) x0 _$ y! ^: V/ p. @) V/ z  ?
  1. uint8_t SPI1_PA5_PA6_PA7_Read_Write_Byte(uint8_t TxData)3 m, z$ i% I% N; P
  2. {$ B* b  q/ Y! ^) u
  3.     uint8_t Rxdata;. u: s8 ^9 }1 r: ?
  4.     HAL_SPI_TransmitReceive(&hspi1 , &TxData , &Rxdata , 1 , 1000);      
    ) M, n$ x! \, I# {0 p* i$ ^* Z. b: T
  5.     return Rxdata;9 ^/ A5 V! d6 K$ I' B* X5 v1 w: c& U
  6. }! W  S& b" G" z

  7. . p' I% V4 s: {" z4 v& a
  8. void SPI1_PA5_PA7_Write_Byte(uint8_t TxData), M1 d) p# r+ e8 L% c2 t
  9. {+ e5 T8 H/ N) H/ [$ \4 S6 p
  10.     HAL_SPI_Transmit(&hspi1 , &TxData , 1 , 1000);4 a5 Y0 P0 H- W1 ]0 N+ S
  11. }
复制代码

- a) d3 r$ T1 A) t# y" y6 r/ _# k3 D! H! D+ p( _4 s* f/ G8 P
3 C7 y) u: M7 T4 ?* Z! b9 K% O9 Y
SPI随时需要调整数据位数为16位或者改回8位:; |: @* {0 S5 P& V3 F
  1. hspi1.Init.DataSize=SPI_DATASIZE_16BIT;
    ' t7 V+ j' B9 \# q6 C% S% ]4 [$ V
  2. HAL_SPI_Init(&hspi1);0 u# S4 H, ]2 ?7 P- g: P& v
  3. 9 Y( V% k6 Z7 O5 D! [7 S0 f4 {
  4. hspi1.Init.DataSize=SPI_DATASIZE_8BIT;
    3 o: L; d) x0 P- B
  5. HAL_SPI_Init(&hspi1);
复制代码

! Y! L4 V8 @, H; V) k在实际应用中,常常需要修改DMA参数,最常见的就是内存地址自增或者非自增,数据是8位或者16位(Halfword半字)对齐:! S; F3 V& ~) }1 u0 p# y; P
  1. void SPI1_TX_DMA_Config_PID_MID_HalfWord_Normal()
    ( g, j' x0 P; r) ~
  2. {
    ! s8 u4 o5 `! f
  3.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    0 S; M+ V# P3 c1 X: A  l# @1 z8 E
  4.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;9 S! s6 i  Z, s) l2 p+ r& a) I) r, d
  5.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    7 L% G2 ]7 F# z* y. ^& {
  6.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;& N+ {) T/ J$ [/ L5 o) b! k1 g- c
  7.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;- b! p2 u+ u; b/ K: i; e7 d
  8.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;! ^6 @2 a( `  i' Y! r) v
  9.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
    - X) L8 l9 k7 q
  10.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
    6 i# e; O: [6 q1 _
  11.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;4 \$ p& e2 z* J
  12.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    - z/ `5 \; C/ [; _0 o2 {
  13.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    % I$ L% g- j3 ]5 [! t+ g
  14.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    7 R: I1 g* d5 h7 R
  15.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    $ X% C8 a4 }7 H7 v1 r5 O' }
  16.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    / `* U5 \" ^1 B7 E" B) e4 C
  17. " r2 v' w2 s/ O1 G
  18.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
    # `- p% y8 I. w* E- B
  19.     # W3 q6 Z2 E- |# R' t7 x) {; J
  20.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    4 W# i' J( `* M  e4 Q6 v6 D
  21.    
    : Q; J" l' r" D+ u
  22. }2 B, g9 d4 x8 Q. w

  23. . \+ ~+ a) V7 k. F
  24. void SPI1_TX_DMA_Config_PID_MIE_Byte_Normal()" f1 Q2 D2 y& X0 a; W1 @: O
  25. {* S0 |4 H/ E3 U
  26.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    2 q% q. ]. W4 f  n& I6 H
  27.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
    % G. z. c+ U$ ^( ?+ j/ ^2 X
  28.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    + F9 _7 u4 q7 X9 O
  29.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    ! w6 J( \/ G9 _3 P
  30.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;. S/ P$ k' B7 l( ]2 D1 u$ v
  31.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;8 y1 h- R# K# ]: M/ X
  32.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    ' q0 [5 X1 y% @/ }6 v
  33.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;2 n4 `$ @/ G; \
  34.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;& Y: \2 }8 u0 o* j
  35.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    4 D4 P% Z, v5 Z& J8 \& z7 p
  36.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;) x/ h" M) o; p; a0 @) w
  37.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;! |+ y& ~6 F$ i& \" q" w
  38.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    , ]6 ]. |* y5 `. W- u. I* h
  39.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    ! T7 H/ h( ]2 W  P1 s

  40. 8 I) {/ ^/ r/ |- e( g
  41.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
    8 i" R2 e3 g8 x1 ?* x
  42.     9 O6 A3 t: n8 h" D+ o8 U4 f
  43.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    0 E) T5 E& l+ v
  44.     : }3 R; f/ e+ C3 e( X, {
  45. }
复制代码
6 }7 d6 M! R7 L7 c" N
我这里用SPI1去驱动ST7789的屏幕,使用SPI1的DMA发送功能,因此使用DMA传输函数:
+ s- F4 |& I& t5 W% N' u# C
  1. HAL_SPI_Transmit_DMA(&hspi1 , pic , num1);
复制代码
0 V( R* f( i! ]7 {
SPI不使用DMA的波形如图:
% e& N" p4 G" U( k8 G IMG_20250522_224448.jpg & L+ [' g, Y- e
这里有个ST新系列的SPI波形问题,MOSI脚会在发送结束的时候拉低相当长的一段时间才会恢复高电平。
* D& |/ R: v0 r. O8 Z
# `  e( I% J! A( a9 H0 D+ W
SPI使用DMA的波形如图:5 @9 c: G  G$ N2 @  f: `; W! Z
IMG_20250526_224854.jpg
/ ^) S+ |  U" _7 z6 ^5 I% h4 O5 k: O: b: V- W5 s+ G: B6 ~! J, W& X
使用SPI引脚驱动ST7789液晶彩屏:6 P  O$ [7 L6 G# a6 [+ r
u3.JPG 8 i" d3 P0 s2 f  {2 J0 ~( o
IMG_20250526_235555.jpg & p' W5 g" ~4 S9 S

3 j: R9 v- F' Q- A! ^
赞 收藏 评论0 发布时间:2025-5-27 00:09

举报

0个回答

所属标签

相似技术帖

官网相关资源

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