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

【STM32U3评测】SPIDMA发送

[复制链接]
donatello1996 发布时间:2025-5-27 00:09
      SPIDMA发送/接收是非常常见的MCU应用,用于与高速SPI器件通信并提高效率,这个帖子是评测STM32U385RG的SPI1接口,并启用SPI1的DMA发送功能。
: u8 z* ~& i9 G- j/ b( ?; [- D) w5 V
       在上一个帖子中提到,STM32U3的DMA外设名为GPDMA,在ST新出的系列中,任意支持DMA的外设都可以绑定到DMA的任意通道/流/节点上,不像F103和F4系列那样规定通道号,来看看SPI1DMA的配置:: j: A( l, p# f9 C+ X
* Y' a  t1 |  I9 D  Y  F2 o# h* E
8.JPG
" C0 G3 ]& ]) [5 I

  C0 |8 P8 o; F) `Normal模式表示每次调用DMA发送函数只会发送一次,Circular模式表示只调用一次DMA发送函数就一直发;在外设所使用的DMA中一般配置外设地址非自增,内存地址可配置为自增或者非自增,内存地址非自增表示DMA发送的数据是同一个数据,也就是内存首地址的数据;源地址和目标地址的数据格式必须一致,都是8位或者16位;只要SPI使用了DMA进行发送或者接收,就必须要把SPI中断和DMA中断都打开,少打开一个都不会正常工作。
0 O2 E% Y& H6 O0 C( E7 [7 K
% a9 I* C8 k$ O) {SPI初始化代码如下,在ST新系列中加入了一些不太常用的SPI机制,如CS片选引脚空闲等待节拍,Ready模式,因为在大部分场景中片选引脚都由GPIO进行控制,比较少用到SPI外设自带的NSS引脚功能,除非是双MCU通信:
& y( e( d/ K4 V5 e5 V9 z
  1. void SPI1_PA5_PA6_PA7_Init(void)
    * c: g% e, n8 U9 k$ {( ]
  2. {0 _* q; N$ s  G- I! d
  3.     7 M, h. _4 K, N7 {% o2 I& q
  4.   __HAL_RCC_GPIOA_CLK_ENABLE();
    - C. O2 P7 a# C0 |: E; u& c
  5.     __HAL_RCC_SPI1_CLK_ENABLE();3 E* X" M* ]. m, q- X3 a" ^
  6.     __HAL_RCC_GPDMA1_CLK_ENABLE();
    # o8 O! a# @9 L' \8 ?% f
  7.     : E! h$ C2 u  Z6 O$ w( J$ |
  8.     RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};3 Y2 {4 e7 D5 l- e. v, V
  9.     GPIO_InitTypeDef GPIO_InitStruct = {0};
    & m. g) K: i8 x: B
  10.   SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};( }6 i: |" _- Q% {) J; Q
  11.    
    - L7 F7 |( g( u
  12.     PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
    ) `1 i2 N' M* _, K
  13.   PeriphClkInit.Spi1ClockSelection = RCC_SPI1CLKSOURCE_PCLK2;' }) Z. |1 x& g( K) U
  14.   HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);7 m4 q4 `" W1 K( C
  15.     ( T2 [1 P! q0 p$ j
  16.     GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;3 h+ A. M: i* T* p$ N+ B
  17.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;7 m: T; ]; Q) y- q  H6 o
  18.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    - w! h( @  ~. _/ I( m( ~- L
  19.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    + U8 E, u, n4 I: f, Z
  20.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;- m. t1 W  ^3 \9 S0 ~- W# ]. E/ C
  21.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    / X% e+ }* H/ A7 T. @$ N" [6 m& o" {

  22. 6 V4 b3 k# y1 v( C- ~
  23.   hspi1.Instance = SPI1;' T. G2 X+ C. U5 w! q4 M3 F& f
  24.   hspi1.Init.Mode = SPI_MODE_MASTER;
    % S+ Z. \$ g! m" M9 y& b
  25.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;! i! M/ @7 G. y; U) }& Z6 m
  26.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    + L: \7 g% M4 O
  27.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    3 Y, H  p0 O8 v' ^. {8 |
  28.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;, l2 C% f$ H$ S/ V4 G. Y
  29.   hspi1.Init.NSS = SPI_NSS_SOFT;; i5 G, `$ i2 l( e6 f
  30.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
    ; ~. B( Q. P% N1 [8 f
  31.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    1 I: c' W. M2 d* x
  32.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;8 f, v. a" Z8 P0 f
  33.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    , t, s6 C1 X, y7 x7 H8 X4 U
  34.   hspi1.Init.CRCPolynomial = 0x7;
    8 X' a) o- o/ p/ i7 Y$ O& Y5 m
  35.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;0 y! ?4 t" h- T; b
  36.   hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;2 }" [1 ~* r/ q( M% z0 X1 F
  37.   hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;- z* S( V* z7 o6 U0 M
  38.   hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;/ r( x1 o  _; C! s; m# e1 ?+ ^
  39.   hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    ' }  ]' w; z$ K- b, m
  40.   hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
    ! x5 y! |# m6 N  L# w
  41.   hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
    & w6 M/ p0 r. v  G1 K: r* T
  42.   hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    / j% y7 u1 R  b1 Q
  43.   hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;2 h( E" w! S+ M, d
  44.   hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
    8 _! y2 K) k# n' ], D  N% M3 t! G7 a
  45.   HAL_SPI_Init(&hspi1);
    + g+ P4 C  ^1 k1 z) s. Z$ H, H
  46.     __HAL_SPI_ENABLE(&hspi1);
    0 P# r  p, v4 |. u0 l
  47.    
    + {$ F$ ~  ~, s+ [  J. v
  48.     HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
    6 Y+ s2 A1 O9 w) t: ?. @
  49.     HAL_NVIC_EnableIRQ(SPI1_IRQn);. F& ~/ H/ k' G1 w. I: c
  50.    
    - K* I$ i. ~# t1 L$ j: R
  51.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;: z3 [8 t) O. ^+ }  _; w8 t0 h+ B( x" C
  52.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;
    2 Y1 U5 C/ y( t+ {0 o: G8 c6 j
  53.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;' @1 _/ P; h8 }* B9 z& _: t
  54.   HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct);
    . ]- a* D2 v0 @

  55. ( ~  E( i2 v/ S* X9 K; {) O
  56.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;; I/ I$ A2 S' x1 D0 g
  57.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
    $ S: l6 G9 E9 q4 o% |
  58.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;3 O/ e0 d) y; ]7 }- h
  59.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    . {8 C) d2 F0 u4 j
  60.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;& z$ y9 U- E3 @$ G# E6 Y
  61.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;. h+ n/ C+ G2 {, i! Z
  62.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
    ) V  t- r4 J3 s9 K- W3 J
  63.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;3 T8 E' F* [5 J# i3 O% \  D- n
  64.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    9 n- G& P% q( L; z
  65.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;+ {" B0 T. H8 h
  66.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    . o: Z. H+ v7 F- A5 }- [+ S3 B
  67.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    : ]8 J2 t7 J& v
  68.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;/ @$ A, s% J5 t( n- k
  69.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    6 ^+ C( r( v- M+ y4 b' Y# i0 i
  70.     HAL_DMA_Init(&handle_GPDMA1_Channel1);
    : V" b& `- s$ b- F$ w
  71. 0 w* {, `6 T6 m0 z
  72.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);5 n5 V2 V" h8 u. R7 P' E
  73. 1 S# j* U: _! G9 m
  74.     HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV);
    4 \4 _% B0 _, B, h# d. N6 B
  75.     : R* H: p" N# D9 Y7 \% L; U! s
  76.     HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);5 W: E& ]! H% o1 i9 b
  77.   HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);( ~+ M" m7 {  l5 r; a8 e9 I/ ~
  78. $ D5 p2 H  c& w% {3 x" c" c( J$ i
  79. }! g' M5 d! a9 i3 ?
  80. 4 A! K# J+ V( _) D+ T# B
  81. void GPDMA1_Channel1_IRQHandler(void)
    + z  e! U* s$ _1 s$ r* x
  82. {
    & x  o3 x0 A' V" {
  83.   HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);: ]1 v' j0 u, i& ]- {) b- J
  84. }
    2 K8 W3 c+ l. C+ C+ ^5 U: E

  85. 3 M3 D; u+ c, Z& o) E
  86. void SPI1_IRQHandler(void)
    1 t0 G. J; O, O5 P
  87. {
    ' r& C, B4 y  V) s; E
  88.   HAL_SPI_IRQHandler(&hspi1);% A$ b) K) ^; T' ]" @
  89. }
复制代码
# k% r* A& o+ G" {) f- H

2 `/ B* {0 a$ {- g$ [0 L& OSPI发送或者发送+接收代码如下:8 B% ?% O, R' ~  E5 R
  1. uint8_t SPI1_PA5_PA6_PA7_Read_Write_Byte(uint8_t TxData)
    / a' x1 k. Q5 x; F. J
  2. {
    ! y$ \* S* d! e  Z+ l6 p4 l
  3.     uint8_t Rxdata;
      S3 g, G: a" {7 ?/ c' \3 Z, _
  4.     HAL_SPI_TransmitReceive(&hspi1 , &TxData , &Rxdata , 1 , 1000);      
    + w( G8 S' ^1 J9 B! }
  5.     return Rxdata;
    0 Y& s: C, ]( P( s0 k7 Z
  6. }
    ! s; X: I7 b6 P6 h. e4 M4 u" B& n( e
  7. , {9 F4 o. ]& @4 X0 g1 a  I
  8. void SPI1_PA5_PA7_Write_Byte(uint8_t TxData)1 M3 O, K7 v" _8 L  Y2 l
  9. {
    ! G3 c& ^. Y& g0 \! t* m& W& l, O* A) {6 K
  10.     HAL_SPI_Transmit(&hspi1 , &TxData , 1 , 1000);' H  s8 Z& P* V
  11. }
复制代码
" t9 c9 u( z$ A' {) B9 |$ C, j

3 T* r4 H# |  o- _# N0 }7 v4 \. q$ _
0 c* ?- o- \# ESPI随时需要调整数据位数为16位或者改回8位:
& \8 s; F( n/ g9 M& O2 V
  1. hspi1.Init.DataSize=SPI_DATASIZE_16BIT;. U) e, o7 j' N6 R2 \
  2. HAL_SPI_Init(&hspi1);
    6 H% ~# R( a# ~' ^2 X2 P

  3. , T! d' f3 X9 W7 l; O3 G3 b
  4. hspi1.Init.DataSize=SPI_DATASIZE_8BIT;
    ! P: J7 V- {: w* z; _; A" z
  5. HAL_SPI_Init(&hspi1);
复制代码

0 g( t+ U5 D9 p" q5 {4 i在实际应用中,常常需要修改DMA参数,最常见的就是内存地址自增或者非自增,数据是8位或者16位(Halfword半字)对齐:+ \/ A2 d* w5 ~# F0 P" k8 D
  1. void SPI1_TX_DMA_Config_PID_MID_HalfWord_Normal()
    ; G: `( i5 v5 V. _
  2. {1 F; c7 Y6 Q1 E5 C0 u0 P6 j
  3.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;3 S5 R; A# p! ^; G) D( y* j: h
  4.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;/ s( h& U8 d* a6 ]3 Z1 r2 \# T
  5.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    . y; J) B# l6 n/ \, ~3 ?7 O/ K
  6.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;/ k, ]1 p' P  |2 C3 q
  7.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;( M$ {5 }, l9 ]* B* E7 S* g5 C
  8.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
    9 U  o1 K# z! s
  9.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
    8 @4 Z0 K; R0 g  v& H; u
  10.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;0 \, F  t0 A% q& {9 q# ]9 x
  11.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;5 I3 I. w  B8 Z8 ~6 I' E; }
  12.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;) ?3 H$ {6 M* b6 G2 R. M: Q8 {" C
  13.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    9 W- I( x+ v0 L0 l! l5 E' {. A( l+ u
  14.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;  ?' V) p  h8 c! h
  15.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;2 G% Y7 ~7 w% E0 {- Q: y
  16.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    ; S) y. _# h4 y+ K

  17. 7 \' c& K& m9 A) A' p6 E5 Y: l+ E8 S
  18.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
    / Y* T. j, Q+ }- _; }9 M8 j3 I6 d
  19.     : l1 Z0 B7 _! [" e- y3 |
  20.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);& y, {; I6 X* ^% }( ?. {* R6 q: ^% t8 k/ |
  21.     5 X8 V" C0 p1 k& n
  22. }
    / F3 U5 g; @) d: E2 y. ^1 \

  23. 5 F# n2 D& Y. I- e" M* a0 Z8 ?
  24. void SPI1_TX_DMA_Config_PID_MIE_Byte_Normal()
    ; P8 u" X6 ]9 ], P0 H8 ]
  25. {5 ~, |* v- ?/ y, v
  26.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
    8 J" D# I, m: p8 `
  27.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;8 H- d! a' W5 w' |) W+ N; F& \
  28.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    4 Z' G9 u1 y, l2 c, d
  29.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;' B, w3 h) q9 `
  30.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;6 ~+ p. c* a+ {; ?, l
  31.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;8 r+ n; f! Z% [- @( l4 p- Y
  32.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;2 h0 F# A& l; ]' d! c! e8 m# E
  33.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
    2 O" W* G: Y0 J3 v6 `
  34.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
    % Z" r8 U: m% K9 o6 f
  35.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
    ' T! u5 _: C) |4 e8 m& f
  36.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
    1 u- ^) i% x& {% ~
  37.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
    2 T( T4 S! B3 w* s
  38.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;, I8 r  {, T1 A8 e5 a' k
  39.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
    - O- h0 @# C! A2 i4 n+ H+ {
  40. $ V! Q9 o. p! i0 Z
  41.   HAL_DMA_Init(&handle_GPDMA1_Channel1);' w( T& X7 [# P! a. q  D
  42.     5 Z5 l4 u$ T4 \
  43.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
    2 F+ c3 A0 J/ S
  44.     + p" s2 y9 p; Y9 r+ i4 I  Q
  45. }
复制代码

, w/ }" l* k4 x" ~1 l我这里用SPI1去驱动ST7789的屏幕,使用SPI1的DMA发送功能,因此使用DMA传输函数:
6 L1 C) C* E' F2 t
  1. HAL_SPI_Transmit_DMA(&hspi1 , pic , num1);
复制代码

+ f' _* o1 @! o( ?  r5 m4 ?# d1 q
SPI不使用DMA的波形如图:
$ Z0 ]) E& h* s! g1 x6 N IMG_20250522_224448.jpg ' C& T; L, a% l  W' w
这里有个ST新系列的SPI波形问题,MOSI脚会在发送结束的时候拉低相当长的一段时间才会恢复高电平。
  m5 B  x! J2 g$ J8 G$ k/ e3 j$ ^6 |. J9 T5 P' D) s4 k
SPI使用DMA的波形如图:: _" @. s" Y) R( B( ~
IMG_20250526_224854.jpg
+ A( y0 n) o2 w: E- x" \2 @4 R# o2 i- J' J5 ?6 ?! n  m0 }- X
使用SPI引脚驱动ST7789液晶彩屏:* S6 y4 \0 @/ k5 K% w
u3.JPG 6 x/ M% z/ E1 D6 @' {- {
IMG_20250526_235555.jpg 3 Y! D$ |9 A# s' P: s3 _
5 B; P1 s! F8 w* d
赞 收藏 评论0 发布时间:2025-5-27 00:09

举报

0个回答

所属标签

相似技术帖

官网相关资源

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