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

STM32F4 SPI DMA

[复制链接]
STMCU小助手 发布时间:2021-10-12 13:31
STM32F4 SPI DMA
自己整理(存储器到外设模式), x8 T4 q3 Y8 x/ [+ d& o
SPI结构体
- p& b4 n2 A. l' X8 t$ R2 d9 D; I
9 Z. C4 p2 R6 ~- p8 H; _- ?
. \* ^7 _  j  m0 v
  1. typedef struct
    % ?9 [1 w$ Z/ M* [2 t/ @+ s0 N6 w
  2. {
    ' e6 M3 n( C9 l1 }+ _" [0 _/ b
  3. uint16_t SPI_Direction; /*设置SPI 的单双向模式 */
    ; }. d! \) l- J  F
  4. uint16_t SPI_Mode; /*设置SPI 的主/从机端模式 */! n) {( E) b" P1 ^5 R$ P
  5. uint16_t SPI_DataSize; /*设置SPI 的数据帧长度,可选8/16 位 */) s( O* l9 T, y: x4 i" e* x: g
  6. uint16_t SPI_CPOL; /*设置时钟极性CPOL,可选高/低电平*/; I& S- v/ s! |' G# x) t, e# O; c9 J
  7. uint16_t SPI_CPHA; /*设置时钟相位,可选奇/偶数边沿采样 */; _/ r: n, p& |
  8. uint16_t SPI_NSS; /*设置NSS 引脚由SPI 硬件控制还是软件控制*/
    4 G* k, z$ w' F2 K* S, H
  9. uint16_t SPI_BaudRatePrescaler; /*设置时钟分频因子,fpclk/分频数=fSCK */
    - ?$ e; @% e# Z4 @7 [
  10. uint16_t SPI_FirstBit; /*设置MSB/LSB 先行 */
    8 L% x7 V* D: Z3 b
  11. uint16_t SPI_CRCPolynomial; /*设置CRC 校验的表达式 */! O2 e1 O! k6 Z5 [3 ]
  12. }SPI_InitTypeDef;
    5 b8 e3 k, H  Y# s) a
复制代码
SPI引脚编号) h7 G% W7 d- [( A
14.png
15.png
16.png
SPI配置! F% R( N( n2 O. h  C: r) }& L
  1. void SPI_Config(void)
    : f, i3 O' L* g7 E' t# b4 \
  2. {
    " Y( }: Z7 s/ V% e% C& v2 }5 C9 V% Y
  3. GPIO_InitTypeDef GPIO_InitStructure;, c( x1 x; C. ]9 ~
  4. SPI_InitTypeDef SPI_InitStructure;* A& V8 y3 l9 x+ }
  5. " v* `0 i* r8 n" P
  6. //1.初始化GPIO
    ; F6 i1 x3 a9 p- ]$ `6 T1 S7 f. J$ A
  7. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB,ENABLE); % A  V8 _3 a# R5 C4 z2 r/ N" U" Q" j
  8. /* 连接 引脚源*/; g- Z" b0 x- f2 @
  9. GPIO_PinAFConfig(GPIOA,GPIO_PinSource15,GPIO_AF_SPI1);
    + e! ~1 E5 G" \" r7 k
  10. GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); 1 k/ W1 r- [# |) T4 V, Q- M1 E  r* o
  11. GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);
    7 u" I. A* a$ A

  12. 2 A7 ^7 k) Q. Z' T' y7 B
  13. /* 使能 SPI 时钟 */
    9 |. E4 {+ s; D0 U5 y
  14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    . [4 Z' q, G; L7 l! o( t, h
  15. ) K0 b% J. B/ N; A. l3 E
  16. /* GPIO初始化 */. v& M3 X. p- }9 J) E3 A
  17. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;       
    0 E( }: ~, ]. [. D" t4 u
  18. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; ; F3 Y: j) D1 E. J
  19. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 g& r4 J, ]; ^+ s
  20. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;# L* c) f# [; B! L) ~& h8 ]% B) H

  21. ) g5 y% h( ~+ z, s
  22. /* 配置SCK引脚为复用功能 */* K* ~" f% T3 o( H" Y% w( i' p% r
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ; ' \) c/ t5 a5 B, v7 o
  24. GPIO_Init(GPIOB, &GPIO_InitStructure);
    / z7 \; n3 {8 W7 N4 o
  25. 0 k+ M; |3 C) a/ d/ C
  26. /* 配置MISO引脚为复用功能 */+ }3 {+ ^5 C# {$ r( X4 _
  27. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;# X& w. m. ]- s2 _3 [- V- O; Y
  28. GPIO_Init(GPIOB, &GPIO_InitStructure);; F! \$ l- ~! Q( ^
  29. , l8 u* R; V3 u+ `- C: Z( N
  30. /* 配置MOSI引脚为复用功能 */
      w" @6 W7 |- \# u8 J6 D" S3 s
  31. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;1 F# J6 c0 b: ], P
  32. GPIO_Init(GPIOA, &GPIO_InitStructure); - m7 T, ~' n( p& ~$ |) R' h3 w

  33. 1 u, s' m3 @: l4 A
  34. /*CS引脚 */! e) @0 P  x- h
  35. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    8 L' `, o3 F& p/ u
  36. 3 w! A( O( V) r% y9 @# F
  37. /* 配置CS(NSS,自动控制SPI的片选信号)引脚为复用功能 */- g9 p) Q, V5 I4 R. A
  38. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 ;
    7 n6 n8 z/ x( R, G/ m/ D6 m7 m
  39. GPIO_Init(GPIOA, &GPIO_InitStructure);
    1 b2 K) O* J% K+ B
  40. 8 Y  Q! O2 L" j4 ?4 ]+ X
  41. /* 停止信号: CS 引脚高电平 */, K8 Y) Y' g: P
  42. GPIO_SetBits(GPIOA, GPIO_Pin_15);- J% B9 w! d7 \8 Y
  43. ( \) D. S" y+ j; T! R& P
  44. //2.配置SPI工作模式
    # q$ |/ R3 d* X/ D/ p1 q/ a2 B
  45. // 分频
    : J- l  C+ S) A# ?, T
  46. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    * O1 f8 i$ b/ p- r# l( E/ j
  47. // 数据捕获于第二个时钟沿) k9 D/ ~3 _/ ~5 U% O
  48. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; 5 y/ O9 B; @( H9 b* V
  49. // 时钟空闲idle时是低电平
    % q7 D8 }3 R+ W% I2 n9 H0 N! y0 [. z
  50. SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; 8 N( b3 p  v' a& b" d3 [' ~1 |% W
  51. // 不需要使用CRC校验8 ?; b& j6 N8 m; b/ E
  52. SPI_InitStructure.SPI_CRCPolynomial = 0;   m0 ]7 I$ d- J  W' Q1 @; {/ E- U3 Y
  53. // 数据帧长度为8位
      G4 z. ]+ }! |/ ]0 J% Q2 Z
  54. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    ' f* K2 C- h, Q
  55. // 双线全双工模式
    4 B9 X8 j% N9 @4 Z5 K
  56. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    - k6 H5 ~) J# I, M2 j3 \$ Q1 Q; z# I
  57. //数据传输从 MSB 位开始, w7 s7 b( L$ b
  58. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; 1 d& D; {& w* u0 X7 f
  59. //NSS 信号由软件管理
    & Q8 R; ]) }; D% Z: m7 E
  60. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    . U' `- A: m4 U/ ?* f8 [' S3 o
  61. //设置为主设备+ u- X3 M3 ]' n1 w1 k) A  i+ C0 \
  62. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;        6 [, _1 f3 e% p8 |5 G/ k, b
  63. SPI_Init(SPI1,&SPI_InitStructure); 6 g' X$ n5 n+ P2 t. f

  64. 0 F0 ]% v, B& i( Q/ `: \) Q- e4 j
  65. /* 使能 FLASH_SPI */       
    9 e7 m# U% |( U9 a, I, F
  66. SPI_Cmd(SPI1, ENABLE); . X5 y/ m! x9 Y# z
  67. }
    ) i  Z& D$ \( v& ?4 L4 G
复制代码
DMA结构体6 E5 s" J' ?0 A
  1. typedef struct {
    ! @7 H, H, S5 g& k* V) B2 Q
  2.     uint32_t DMA_Channel;                                 //通道选择
    0 K" X2 F" q; D7 g% A
  3.     uint32_t DMA_PeripheralBaseAddr;         //外设地址2 x: {- [2 A9 ?! D0 G% q& ^$ o
  4.     uint32_t DMA_Memory0BaseAddr;                 //存储器0 地址) N/ a1 |3 V/ s) T/ |1 N5 Q. g
  5.     uint32_t DMA_DIR;                                         //传输方向
    & [& H+ O8 t$ S( s) C$ F
  6.     uint32_t DMA_BufferSize;                         //数据数目* v  N5 [% I6 m0 O. u6 R6 p; O
  7.     uint32_t DMA_PeripheralInc;                 //外设递增- A- i! H% F  ?9 ?
  8.     uint32_t DMA_MemoryInc;                         //存储器递增" z2 ?  r& L+ T! Q+ `3 F4 n
  9.     uint32_t DMA_PeripheralDataSize;         //外设数据宽度
    * J0 n: K+ ?/ Z0 R; P( O4 P; S
  10.     uint32_t DMA_MemoryDataSize;                 //存储器数据宽度! e! d/ U; P) R6 B$ B
  11.     uint32_t DMA_Mode;                                         //模式选择
    9 @: U" S) w: [% K" q3 J
  12.     uint32_t DMA_Priority;                                 //优先级
    9 z) F$ F. u* f& z0 `1 w0 a
  13.     uint32_t DMA_FIFOMode;                                 //FIFO 模式
    8 A) h% Z0 L+ ^: _7 n
  14.     uint32_t DMA_FIFOThreshold;                 //FIFO 阈值- ~, s- i+ K2 v' ]& W' U! S
  15.     uint32_t DMA_MemoryBurst;                         //存储器突发传输: O- o% q6 S0 U: o* a% e: k$ M( U; V
  16.     uint32_t DMA_PeripheralBurst;                 //外设突发传输
    8 _5 ?9 R9 C2 a& k
  17. } DMA_InitTypeDef;8 A9 T* @$ J8 A- I" R# S
复制代码

8 h9 A0 `' n$ ZDMA请求映射
# T8 u: @6 S5 I5 Y* }9 A7 q* A
17.png
18.png
DMA传输模式6 A4 ~0 J& v( N. A
19.png
SPI 发送DMA配置- h$ q, n9 x# ~
  1. #define SENDBUFF_SIZE (1024*20)            // 一次发送的数据       
    - h9 c" M) l/ J  C  V8 c( G
  2. uint8_t TX_Buff[SENDBUFF_SIZE];                // 发送缓存
    $ }0 B" I7 q% x' D& b
  3. void SPI2_TX_DMA_Config(void); L* T+ f5 R6 h  [% P1 o
  4. {2 A/ s5 u: c; l6 X% d
  5.     // 中断结构体
    4 W0 C0 N3 u: U
  6.     NVIC_InitTypeDef NVIC_InitStructure;               
    , _7 m$ h. n7 y% D+ Z' x3 b& q& o
  7.     // DMA结构体
    # p' Q- @8 \; g( s9 x2 x+ ]
  8.     DMA_InitTypeDef DMA_InitStructure;                 
    ) l0 K* a$ ]# }
  9.     /* 使能DMA时钟 */                  ) g2 ?. L: g1 I0 ]1 E6 O
  10.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);       
    5 e6 ~1 C# \  g- p3 Q) S1 V7 y' N
  11.     /* 复位初始化DMA数据流 */  , e% |' I+ g: F% ?
  12.     DMA_DeInit(DMA2_Stream5);                                                               
    # x( Z9 C3 j5 M4 y# S, r8 \& L
  13.     /* 确保DMA数据流复位完成 */  
    * p0 K  ?0 v& t- g
  14.     while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE);        / Q5 p9 t2 }+ j& ]' K

  15. $ U9 R' ^. `4 G
  16.     /* 配置 DMA Stream */
      m5 e) e# c2 T- a2 s
  17.     /* 通道3,数据流5 */          
    2 R* w& V8 Y% e
  18.     DMA_InitStructure.DMA_Channel = DMA_Channel_3;; i4 d7 N! H( c
  19.     /* 外设地址 */  
    ; j1 t; g" R3 L2 i) m$ `
  20.     DMA_InitStructure.DMA_PeripheralBaseAddr = (SPI1_BASE+0x0C);       
    " t, f5 Y7 A0 O4 [3 f/ u1 l* S
  21.     /* 内存地址(要传输的变量的指针) ,DMA存储器0地址*/         
    ) p% S4 x, c- ]! r+ M' y
  22.     DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TX_Buff;        + ^  a6 U7 [' [, v& o' s
  23.     /* 方向:存储器到外设 */                        $ v! R& @2 u+ w: ~  d5 `6 F3 ^) Z
  24.     DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;" }  s5 T6 j6 `! V$ K
  25.     /* 数据传输量 ,可设置为0, 实际发送时会重新设置*/            
    " N9 v9 B' r. c" O0 S0 D+ e- \
  26.     DMA_InitStructure.DMA_BufferSize = (uint32_t)SENDBUFF_SIZE;               
    , E/ E+ H! f$ Z6 R1 }+ i$ H/ L/ s
  27.     /* 外设非增量模式 */                : `  w& H% |/ g/ i5 }
  28.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;& }  `4 n0 b, d1 k: G! l+ k
  29.     /* 存储器增量模式 */          : U' c* _" ^( q/ _
  30.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;: V8 j" d5 c; _( i7 m( @* R
  31.     /* 外设数据长度:8位 */         
    & b3 ]  v- f# o0 ^; q
  32.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    9 b  P% u( [$ g) ]
  33.     /* 内存数据长度:8位 */
    / d) c3 y$ K5 U% G1 t7 Y- j" g+ M, Q
  34.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    / Y' q4 X: q4 J0 q; T
  35.     /* DMA模式:正常模式 */                  , U( n% h2 i" p2 W. y
  36.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    3 H( A1 ]; o2 T. E# Z1 D  ?" k- ^  V
  37.     /* 优先级:高 */                        
    ) Z+ i8 R% u9 x- W3 a+ r5 d/ G
  38.     DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    ' ?. B' `4 J: q4 b& V% \  p
  39.     /* 禁用FIFO */2 U* }) V% Q6 H% Z# ?
  40.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                       
    / x8 e, w/ [% X; T1 k; M/ u
  41.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;   % H2 ?! B( e+ H7 [' m
  42.     /* 外设突发单次传输 */  2 j" f  F, |& u8 Y& F" ]1 q2 w' b* @$ a
  43.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                    7 u. k/ p# o( o' g# ~+ u
  44.     /* 存储器突发单次传输 */  7 t* C# H) W* {% `
  45.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; - G  Q2 T) g% A# W% o6 K- Z: Z  i
  46. : ]9 \  `: W" `, j" c( {, S5 |- L
  47.     /* 初始化DMA Stream */                1 ~, H- z9 z1 }  t' v$ H9 n. x
  48.     DMA_Init(DMA2_Stream5, &DMA_InitStructure);- h; z+ @! @" j5 c8 D# U
  49.     /* 开启传输完成中断  */               
    0 O: i+ }# l6 n* f- q5 i; ]4 V+ j0 g! ^
  50.     DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);
    : h! x$ N1 B" `& {% I1 }

  51. 2 b3 H  c$ l- i8 K' a1 X
  52.     // 中断初始化
    " n4 v$ ^+ K2 a2 G+ G( g  E! F7 C, }8 l
  53.     /* DMA发送中断源 */  ; w5 L4 z8 z3 ~' O% Q# c
  54.     NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream5_IRQn;       
    $ {5 o3 K# M+ D: W0 E; A1 O
  55.     /* 抢断优先级 */  & i# f4 A- @* \& c" \
  56.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;$ E& M8 G) Y# A$ p
  57.     /* 响应优先级 */  
    * i$ L! p, Y$ \9 Q$ i
  58.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                               
    7 G7 x* \) v4 O1 j* r4 Q
  59.     /* 使能外部中断通道 */   q! L7 J% R8 Q
  60.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                 
    ! M" H! n: \4 p5 s
  61.     /* 配置NVIC */                4 }" r) `# {1 Y7 b
  62.     NVIC_Init(&NVIC_InitStructure);
    5 @8 @5 ?0 P4 k8 e. t
  63. }
    7 I6 I3 D4 J/ S: q
复制代码
DMA发送中断服务函数
. k1 c0 w4 f: i) m" n; w/ d
  1. void DMA2_Stream5_IRQHandler(void)
    ' m! m0 W" D, O8 j5 Y  f
  2. {9 I- F- S9 W$ Z% l- L3 l/ O
  3.     // DMA 发送完成& I" ^2 p+ A4 d9 a' e/ r! o
  4.     if(DMA_GetITStatus(DMA2_Stream5, DMA_IT_TCIF5))       
    , V- I$ P3 c# S) l1 b1 ~) [! D
  5.     {/ Y! @% U  ~" B# d- P- @4 [4 A- `
  6.         // 清除DMA发送完成标志& a( H! K9 F: ]8 {' W3 T
  7.         DMA_ClearITPendingBit(DMA2_Stream5, DMA_IT_TCIF5);        # J6 S- i4 L/ U6 ]( A- @
  8.         // 片选拉高,数据发送完毕       
    . Z0 ]0 s. H$ a7 {' ~0 T: D- x' R/ O
  9.         GPIO_SetBits(GPIOA, GPIO_Pin_15);        & f$ H" r# E! \% [+ Z3 l
  10.     }
    / Q; o4 K" K$ N) `3 a, |; K
  11. }- B# C+ Q( h  ~& P9 g
复制代码
SPI 接收DMA 配置* q0 ~" ]! R0 {0 {
  1. #define RECEIVE_SIZE                  800          // 接收大小
    ( t6 m- C9 k; |( h; R  x% c
  2. uint8_t RX_Buff[RECEIVE_SIZE];                // 接收到缓存; O" u/ z! y8 o) B# w; Y
  3. void SPI2_RX_DMA_Config(void)
    0 W6 ^) i5 X/ m/ k4 g
  4. {
    ( q# \% w- O: J  f; v+ ?8 s# X
  5.     // 中断结构体
    $ h0 q" R& I- Q: }
  6.     NVIC_InitTypeDef NVIC_InitStructure;        " q. X* t  o8 b* n4 r. K. L
  7.     // DMA结构体  6 W  D; h% _3 `$ s5 c5 w$ ^
  8.     DMA_InitTypeDef DMA_InitStructure;                0 i, m4 ^& ]! h3 O. ^7 i
  9.     /* 使能DMA时钟*/  
    7 }& V: J/ n6 \2 _
  10.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);                                        /* 复位初始化DMA数据流 */
    3 `8 G$ [! F% \( g2 k
  11.     DMA_DeInit(DMA2_Stream2);                                                                                                /* 确保DMA数据流复位完成 */8 @" Q0 S/ o- N- t# C
  12.     while(DMA_GetCmdStatus(DMA2_Stream2)!=DISABLE);# W( E$ E. z0 k9 K' k6 J
  13. # h" n" X3 d! V, ~2 V
  14.     /* 配置 DMA Stream */3 M2 I: g& L- i. f
  15.     /* 通道3,数据流2*/          % I, X& X  |& W/ [- |  Q
  16.     DMA_InitStructure.DMA_Channel = DMA_Channel_3;                         
    5 Z3 K5 H6 h) @/ `# e
  17.     /* 设置DMA源:串口数据寄存器地址*/  
    * `. ~( j* X* }) }6 H. s
  18.     DMA_InitStructure.DMA_PeripheralBaseAddr = (SPI1_BASE+0x0C)                ;  {1 [2 m- T7 ]
  19.     /* 内存地址(要传输的变量的指针)*/  1 p' v$ `3 ]! L8 K, u* V: M
  20.     DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RX_Buff;                        8 m: X, E% |( D  C2 X
  21.     /* 方向:存储器到外设模式 */                       
    + B! k) k: K, i  Y9 x' |
  22.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
      j* g( ?4 z* B0 O/ ]
  23.     /* 数据传输量 ,需要最大可能接受的数据量[不能为0],实际发送时会重新设置*/          
    % o. B% n- C5 w1 C% ^  q( _; S
  24.     DMA_InitStructure.DMA_BufferSize = (uint32_t)RECEIVE_SIZE;- o- u5 M& Z( v
  25.     /* 外设非增量模式 */          
    3 @5 G0 c# B' `7 k- D' x8 A
  26.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;           
    & ?' e! |& _$ k  i% D! |
  27.     /* 存储器增量模式 */    * C  g1 l$ I# o4 {/ p+ `8 c' \
  28.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;8 j# y3 g( n; E% b/ ]
  29.     /* 外设数据长度:8位 */          
    9 h0 j0 K/ v& a+ S6 u- B7 x
  30.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;9 S; |+ J. y; M- ?1 _, l: x
  31.     /* 内存数据长度:8位 */9 r% `5 p+ B% x% j
  32.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;               
    ' D+ g9 P, ]0 s
  33.     /* DMA模式:正常模式 */  1 i! i# e, Q3 u* a! K
  34.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    + b+ s. ~  x) K8 s- a% r1 {
  35.     /* 优先级:高 */          
    & L( D. P  V3 n2 [  ]
  36.     DMA_InitStructure.DMA_Priority = DMA_Priority_High;
      O6 P1 y3 n$ z$ ~
  37.     /*禁用FIFO*/   
    3 z% }9 ?1 I, E( V
  38.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;# e# }% I; a8 d6 O
  39.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
    ' v+ P6 R+ Z, o" D8 L2 e# P# Q
  40.     /* 外设突发单次传输 */  3 ?6 N3 v# ^- a4 r4 ^- B( O, k
  41.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    . F! h7 t! b& X& g! K6 a
  42.     /* 存储器突发单次传输 */  1 C! B+ v( k6 A
  43.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; $ I0 Q* V" p9 }; G4 o- h
  44.     /* 初始化DMA Stream */                6 M1 l) a5 p$ h+ h1 x4 v: G* ?
  45.     DMA_Init(DMA2_Stream2, &DMA_InitStructure);                                                          
    0 s1 ]+ W4 D+ ]7 C  f2 x2 d$ l  g
  46.     /* 开启传输完成中断  */+ k4 [; L+ o) r# W) g, r& L! w( K
  47.     DMA_ITConfig(DMA2_Stream2,DMA_IT_TC,ENABLE);                                         
    & a* m* Q6 Q/ u3 I

  48. % d% T  }% L; H: z- h& H
  49.     // 中断初始化
    ! [% ?1 l& w0 {$ _0 G
  50.     /* 配置 DMA接收为中断源 */- ]% G4 ~. K8 o& z' e. l
  51.     NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;          # W1 Z& a8 D, a9 }# `
  52.     /* 抢断优先级 */  
    ; m2 [' [; f" I, _2 a. C2 t  e
  53.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;               
    % a8 o& I) y7 }. v1 ~4 [
  54.     /* 响应优先级 */  
    ; u( J/ v* J8 c/ B
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                               
    1 f5 [, V; Y7 \* F
  56.     /* 使能外部中断通道 */  
    5 u8 O8 N7 X; g, r6 Q& n
  57.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    1 q7 |' f% [% k0 c6 L! U
  58.     /* 配置NVIC */       
      R9 H, d8 i7 ~$ y# I
  59.     NVIC_Init(&NVIC_InitStructure);' e) j0 H  q& i+ _4 n
  60. }8 P" k3 G* P5 k4 W# w
复制代码
DMA接收中断服务函数* Y5 s) v/ Z# A; \1 J+ v0 ]
  1. void DMA2_Stream2_IRQHandler(void), v9 ?  X+ t; g/ i
  2. {                               
    ( \) y, F4 M! n
  3.     // DMA接收完成
    . y/ V+ r2 }# `: B/ \
  4.     if(DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2))       
    2 ]+ [" `6 R! ?  h9 e
  5.     {                ! f. e4 C1 Z/ t" E5 i' t+ H
  6.         // 数据接收完成 拉高片选
    . \# y; A- _* Z. @
  7.         GPIO_SetBits(GPIOA, GPIO_Pin_15);        * }9 M% f; v8 _9 u& }
  8.         // 清除DMA接收完成标志位               
    & u: R) a- @5 Y. n, U
  9.         DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);        ! g5 X% i+ d" L) w( `( ^
  10.     }% s# t' b' P1 t& J0 w
  11. }$ j5 {; `# Q% x7 ~
复制代码
DMA请求使能: Q3 s9 l2 ^8 A  ~
  1. //SPI2 TX DMA请求使能9 P- y8 z; s) H- x( y5 v* a9 W7 h
  2. SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);       
    " \8 n' C2 h9 v5 Y7 [! P# K
  3. //SPI2 RX DMA请求使能5 _7 R* N! z  o! p) K
  4. SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);       
复制代码
SPI_DMA 读写一个buf/ X( F/ q; B. i' X9 x8 |# O' H
  1. #define BufSize                800
      B' o/ K) L- G, @' g! i* \
  2. void SPI_DMA_WRITE_READ_BUF(void) $ z! n6 O7 Q* n" N# b1 `
  3. {   
    1 [. T, w( b  r4 q: r& [6 W
  4.     // 关闭发送 DMA       
      S; v0 ~: T  _0 n6 D* P+ \) a
  5.     DMA_Cmd(DMA2_Stream5, DISABLE);        * k; w1 u* K( n
  6.     // 关闭接收 DMA        % A) I3 Q3 N! q. f
  7.     DMA_Cmd(DMA2_Stream2, DISABLE);1 i3 @0 j  `1 d# H$ Q, _# l
  8.     // 设置发送的数据量' s4 A8 W1 q3 Q' X9 d' i' ^
  9.     DMA_SetCurrDataCounter(DMA2_Stream5, BufSize);        * p1 F! h$ b( [2 ]5 i
  10.     // 设置接收的数据量- T( s$ n1 i) b
  11.     DMA_SetCurrDataCounter(DMA2_Stream2, BufSize);
    4 m% j( O- t- u; R, u& z
  12.         // 清空数据
    1 A3 J) T) n; @. Z* @
  13.     SPI1->DR;                                  4 \( S, {/ a  X1 I" U7 f  R4 C7 K
  14.     // 擦除DMA标志位1 c; g* m5 u8 t2 J  F* H
  15.     DMA_ClearFlag(DMA2_Stream5, DMA_IT_TCIF5);       
    . p- s0 q% k$ @8 g6 J. A$ B/ X
  16.     DMA_ClearFlag(DMA2_Stream2, DMA_IT_TCIF5);* v# ~/ i4 u) E6 ?4 p
  17. 6 E& A5 n( a) j# q
  18.     // 片选拉低,接收数据1 u& V8 s2 K- u7 C, c6 B- E1 D
  19.     GPIO_ResetBits(GPIOA, GPIO_Pin_15);        ) ], M; N! a" J, |3 h9 N4 d# w( s
  20.     // 开启接收 DMA
    1 B' |0 O9 O3 ?4 U: P$ r& O  f& k
  21.     DMA_Cmd(DMA2_Stream5, ENABLE);        * W# i9 s3 y- F7 w2 ~, d
  22.     DMA_Cmd(DMA2_Stream2, ENABLE);6 ^: }! F1 G& {8 [4 n
  23. }
    ) \* y( R# `! v4 G
复制代码
SPI_DMA 发送一个buf
" r' r2 C8 E5 t& ~' {. I) a; G% Z
  1. void DMA_Write_buf(uint32_t SizeLen)
    ; }) P- @# h9 Z
  2. {     a! ]; E1 G% n1 x& |  B$ |
  3.     // 关闭发送 DMA                : ^2 ^( y) V+ E
  4.     DMA_Cmd(DMA2_Stream5, DISABLE);       
    - E& z$ Q) {) g6 H5 `2 g
  5.     // 设置发送的数据量   
    # y0 X2 A& o# j9 A
  6.     DMA_SetCurrDataCounter(DMA2_Stream5,SizeLen);* p7 Q# U3 ]4 a
  7.     // 清空数据5 a% \+ R; `, p; y8 E8 u, L. n
  8.     SPI1->DR;               
    * I, C* T1 h3 k8 \% b# w7 ^, k
  9.     // 擦除DMA标志位        * t$ V1 t8 [: _$ C9 t6 g7 s8 W
  10.     DMA_ClearFlag(DMA2_Stream5,DMA_IT_TCIF5);
    6 @' o8 C* I% _4 O
  11.     // 片选拉低,接收数据
    . D7 I5 U: F! M7 h5 o4 O' o/ F
  12.     GPIO_ResetBits(GPIOA, GPIO_Pin_15);       
    8 W) r" W' @) m6 g3 J4 h8 }
  13.     // 开启发送 DMA: T) S7 f& s$ d% T+ x
  14.     DMA_Cmd(DMA2_Stream5, ENABLE);        4 ]7 |* Z1 \3 a4 J% h4 z- m4 F
  15. }" b% g( p# \; R& C, o
复制代码
张贴的代码显示如何配置我的GPIO,定时器,SPI, DMA和NVIC模块,以及一些系统如何工作的解释。
( h9 H6 G+ J: B7 }1 C0 o6 A注意,我使用的是STM32F4标准外设库。8 m( K3 d4 Z- a: H

! \* H& z/ E0 N" m9 y( i
& i6 k; t; Y4 L( H
网上内容
& H+ x3 u% B. q, n  |8 D通过RCC(复位和时钟控制)模块使时钟信号到达所需模块:
6 _4 U2 |7 s# S9 j5 N% B8 o5 h, c+ V6 w7 _4 ]7 c) o
% G3 ~9 f$ D1 H$ ]* @' C
RCC6 w+ Y  b, a6 s/ r3 L: O& L
  1. // 为所需模块配置时钟+ ]$ y  l8 I4 N4 ]4 K0 U
  2.     // 启用GPIO外围时钟% W& {7 w2 a0 c) ~' G; X
  3.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);. U8 T: d) U$ D8 m: k2 ]: v
  4.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);4 R% V# I$ q% h0 U9 s$ ~* Z( Q
  5.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    % f' g1 Y5 E# s" x: J7 I
  6.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);3 Q: _+ u& J* x" _: x! J, _

  7. + ~2 O* ?2 B  c9 s2 [5 D
  8.     // 启用串行外围接口外围时钟! e( I  z, ~# ]. j7 Q
  9.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);8 E0 m" k2 U& c$ {0 U) U
  10. - `' v+ Y0 F2 C4 J6 e7 G2 M
  11.     // 启用直接内存访问外围时钟
    9 f9 Z0 R- ~  S! Z4 n4 [) b
  12.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);2 ?! k! ?6 F9 l2 B( Z

  13. , M9 j! G5 j3 ^. b, o# T
  14.    // 启用计时器外围时钟4 K, I; P! m& r
  15.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    2 }/ r8 c* }; x2 Y" ^! p& B
  16.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    . H% {. V- K- M0 c; c( [
复制代码
GPIO
: x- J# y; G5 ?( [8 `$ \5 X接下来,配置所需的GPIO引脚:, |6 z' H9 B* G6 u3 w+ m- Y8 z
  1. #define GPIO_SCAN_PORT            GPIOB6 T6 D) H( t1 ^- X% _4 H3 I
  2.   #define GPIO_SCAN_PIN                GPIO_Pin_7
    : ?# h' |% p! a. M' p8 Y' _
  3.   #define GPIO_XLAT_PORT              GPIOA
      [3 G7 ^0 |2 e' `9 F6 D; y
  4.   #define GPIO_XLAT_PIN                 GPIO_Pin_5
    ; T5 k! P3 j3 r0 d5 Y+ i; X4 M
  5.   #define GPIO_BLANK_PORT           GPIOB  A6 G7 J' n( l4 ~  @; W, y
  6.   #define GPIO_BLANK_PIN              GPIO_Pin_6
    9 n+ @' r; R2 l7 V# M0 J

  7. % l% V% P% t  H# N1 f: j, p
  8. // 配置GPIO引脚7 C$ Q0 h) Y1 U- Y/ j9 z, o, p7 `. Z# L
  9.     GPIO_InitTypeDef GPIO_InitStructure;
    / |# ~! U: ]7 ~, m  }

  10. 5 t' n8 u9 ^- o6 @  ~' m8 j9 [0 D
  11.     // Timer3&4 输出 (TLC5940 GSCLK and BLANK)
    " N& E9 e7 B0 o. v' ]+ ~/ d$ Y
  12.     GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_4 | GPIO_Pin_6);
    - X7 L. S0 _1 p+ U/ i
  13.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;( z" b, D# ]9 {: C0 D3 \1 F0 L% T; E# t
  14.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    : Q) i( y* x8 C# O% i' Y0 F
  15.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;- V" n1 l6 }& ]: n4 y8 T
  16.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;5 }2 g! L1 P! J, t: E- k* j! B; Y
  17.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    6 d" n: K- ~0 U& a0 ~/ Z
  18.     // 连接计时器到GPIO引脚
    ) I. v$ _6 F: }6 ~* c/ r
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);   // 将TIM3 OC1输出连接到PortB Pin4 (GSCLK)
    1 m  d: J0 v* k3 i9 y
  20.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);   // 将TIM4 OC1输出连接到PortB Pin6(BLANK)2 o" h- B. h- i! ]

  21. ; \) w  X$ |4 V# g* f
  22.     // TLC5940 XLAT 引脚1 {/ m2 q; a0 R5 v$ ~; y+ K/ |
  23.     GPIO_InitStructure.GPIO_Pin = GPIO_XLAT_PIN;5 c; k; O! x* I! b3 Y5 J7 ]
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    6 H! s! g. t; P8 c. P; p
  25.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    % R1 k+ p0 G( ^% t
  26.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;, |/ c# o+ f+ F, m: J! s
  27.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;* B3 ?$ v2 V4 p0 Z: f; C4 S" c7 ^
  28.     GPIO_Init(GPIO_XLAT_PORT, &GPIO_InitStructure);$ v  A" o0 K  x9 r4 o# }7 Y* v

  29. ) i: ?  R5 e( H
  30.     // 显示扫描 引脚! Q" o4 I- q. n: D% o: y- q& K
  31.     GPIO_InitStructure.GPIO_Pin = GPIO_SCAN_PIN;
    , \  M4 h7 m3 \0 |: S. b% S
  32.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    : }" V% L$ P( W% `( m
  33.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    4 N6 }! o, u0 Y0 w
  34.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;3 j  h% z  W/ O3 \1 e
  35.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    9 c8 T% K' g& L
  36.     GPIO_Init(GPIO_SCAN_PORT, &GPIO_InitStructure);
    $ V' m+ b( t2 L' H: _: ^, B' [

  37. 3 @0 k2 E7 |0 S. _7 u5 z2 J
  38.     // SPI2 引脚
    , t# Q( U) E2 i" u' _$ x  T
  39.     //    SCLK =    PB10% L9 i9 I: Z6 D) D. x/ N- ?$ @% E; j
  40.     //    NSS =     PB99 O# e( r% I& o7 p/ f+ z/ j! Z
  41.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;3 A: g# a; Q' d/ t# z; n
  42.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    $ o5 T3 n7 ^' R$ }: ~' `
  43.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    ) r. m7 P; ]# N
  44.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 ]  F6 u5 ~; |
  45.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;& C! x4 |$ v& c
  46.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    7 A# E; F9 a$ O5 U% N1 ~, {
  47.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_SPI2);
    $ Q& u+ B. c/ M$ y2 u7 m2 m8 C0 p2 E
  48.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2);
    ( w$ ^+ F% A6 _& q8 _

  49. 1 Z1 r0 L: c/ U: l
  50.     //    MISO =    PC2
    ; c% C" @3 @9 S
  51.     //    MOSI =    PC3
    7 t+ F4 U8 R8 }
  52.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
    ! B- B7 x5 W- q+ Z/ P! H
  53.     GPIO_Init(GPIOC, &GPIO_InitStructure);
    ; \& T% g9 h/ F
  54.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_SPI2);4 }# W: j# `6 G7 L( i2 F
  55.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2);
    4 X: d" V( \3 Z, K( [# R
  56. ————————————————; \  }5 @8 u1 q. ]3 o* m
复制代码
这里的要点是,我将把TIM3的OC1输出直接连接到GPIO引脚(用于GSCLK),把TIM4的OC1输出连接到BLANK信号。
4 C7 I* o" L5 u; K* l5 }
5 P1 T# l) y5 a

2 H! D7 h, Q  b- D9 W) i' J2 xSPI6 P! O' u* V* ^! n/ T) }0 f
现在SPI模块可以初始化:3 A  X/ J' g! ?7 a7 G6 O; l4 T( q
  1. // 初始化SPI模块
    2 e' I3 j# Z4 r0 q' P# G# \0 v
  2.     SPI_InitTypeDef SPI_InitStructure;
    + H2 D+ D$ s: Y. G- {/ ~, r% R
  3.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;       // SPI总线设置使用两条线,一条用于Rx,另一条用于Tx' Y. j' j0 q* u3 m8 G
  4.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                // STM32是主服务,tlc5940作为从服务
    ; J5 n! A  I$ C9 l
  5.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                             // 使用8位数据传输
    $ l* a: e# H9 B/ s" s
  6.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                                    // TLC5940时钟空闲时低+ f) C1 c2 t3 L/ \3 U
  7.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                                // TLC5940使用第一个时钟过渡作为“捕获边缘”- g# i$ w( U3 ]6 ~* X; n, v
  8.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                        // 软件slave-select操作. V4 O9 D* o3 u7 @0 v: S' L
  9.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 设置预定标器3 u1 B8 L% ~; ~) z
  10.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                             // TLC5940数据先从MSB传输
    1 S& I4 [4 l2 D6 `" E8 M1 O4 ~
  11.     SPI_InitStructure.SPI_CRCPolynomial = 0;                                          // 没有使用CRC: ?+ `% d6 O# D  _
  12. - v0 F; u1 P6 F9 }$ A
  13.     SPI_Init(SPI2, &SPI_InitStructure);                                                    // 初始化SPI2外围设备( a; L. D& l& e1 R+ c0 f
  14.     SPI_SSOutputCmd(SPI2, ENABLE);                                                 // 将SS Pin设置为输出(主模式)
    4 I* X# ^( @( ~' P) v! K( \
  15.     SPI_Cmd(SPI2, ENABLE); + L, |0 Y$ v6 ?5 o4 k  u
复制代码
我选择的SPI时钟分频器是相当随意的,但这里的关键点是,我已经配置了时钟相位和极性,根据TLC5940数据表,所有传输都将是8位(稍后详细介绍)。0 t4 m% T; R- c
6 }( P$ Q, R/ d$ O7 J

5 [5 F9 V+ k) DDMA
! X* J  Q! u7 R用于SPI传输的DMA模块如下:- R4 K# Q% ^$ P/ D  @3 H  G
  1. // 初始化用于SPI2_TX DMA访问的DMA1流4通道00 s5 H, N7 k5 ^7 O7 j' Y& g

  2. ! _% k# o' b( G0 R
  3.     #define DISP_SCAN_DATA_CNT     (24 * 3 * 2)    // 初始化用于SPI2_TX DMA访问的DMA1流4通道0; c  N7 Y  r9 q4 Z
  4.     volatile uint8_t dispData0[DISP_SCAN_DATA_CNT];
    7 }6 P4 G. j: _1 t* `: [, C" l. ]
  5.     volatile uint8_t dispData1[DISP_SCAN_DATA_CNT];
    ' j" j8 r' I! A) `! d
  6. - G1 v, R! j8 W0 T& {- I. `$ M
  7.     DMA_InitTypeDef DMA_InitStructure;' N: i/ _+ k8 P, j4 z* u" {
  8. # L- G1 S- u# i7 Y1 _8 e
  9.     DMA_InitStructure.DMA_Channel = DMA_Channel_0;                   // SPI2 Tx DMA是DMA1/Stream4/Channel0& ~' l( y( n& V$ m! w0 `
  10.     DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&(SPI2->DR);                // 设置SPI2 Tx
    7 Y! o- s; M* Z& q! @# Y& g
  11.     DMA_InitStructure.DMA_Memory0BaseAddr  = (uint32_t)&dispData0;                   // 设置内存位置
    0 e. u- ?' ^+ L# |2 k3 ~$ }% ~
  12.     DMA_InitStructure.DMA_DIR  = DMA_DIR_MemoryToPeripheral;                          // 从内存发送数据到外设的Tx寄存器
    3 M: |: w8 r$ @  W! o0 I
  13.     DMA_InitStructure.DMA_BufferSize  = DISP_SCAN_DATA_CNT;                          // 定义要发送的字节数
    ) H3 B, r! i' m2 |9 I
  14.     DMA_InitStructure.DMA_PeripheralInc  = DMA_PeripheralInc_Disable;                  // 不要增加外围“内存”
    $ d% i8 B1 w) K* E: _0 S
  15.     DMA_InitStructure.DMA_MemoryInc  = DMA_MemoryInc_Enable;                        // 增加内存位置
    . T( U. j# V+ z* Q" y8 Q) U
  16.     DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte;    // 字节大小内存传输3 _! Q# [( d$ U% u& x8 m1 c7 W
  17.     DMA_InitStructure.DMA_MemoryDataSize  = DMA_MemoryDataSize_Byte;         // 字节大小内存传输/ M  [' H/ e7 |* N
  18.     DMA_InitStructure.DMA_Mode  = DMA_Mode_Normal;                           // 正常模式(非循环)
    1 @+ v' ~7 c! J' D6 F. ]+ A
  19.     DMA_InitStructure.DMA_Priority  = DMA_Priority_High;                // 优先级高,以避免使FIFO饱和,因为我们是在直接模式4 _- c, k) n- c" ~' Q& h1 Y
  20.     DMA_InitStructure.DMA_FIFOMode  = DMA_FIFOMode_Disable;                 // 操作在“直接模式”没有FIFO
    " _/ V" Q0 |; Q
  21.     DMA_Init(DMA1_Stream4, &DMA_InitStructure);& [7 p7 M% X# Q2 x
  22. 7 L  ~+ M# s  r8 z6 J- Z
  23.     // 操作在“直接模式”没有FIFO
    $ Y+ t, F  t. s5 I1 e
  24.     DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE); $ V& f) T0 O/ J/ j
复制代码
NVIC
- \: @; t; N) U* ^: x) B接下来,我已经为两个中断服务例程触发器配置了NVIC(嵌套矢量中断控制器):% r: W# J& g% t" ]
  1. // 初始化嵌套矢量中断控制器# }( u3 c2 q; f0 Z* A# [/ R" k
  2.     NVIC_InitTypeDef NVIC_InitStructure;
      O7 s/ l3 d3 P: E

  3. 1 T' U# S+ K3 ]6 ?
  4.     // 启用TIM4(BLANK)中断
    . I/ S0 ~: n9 D' i* E. |3 I3 H7 k4 C
  5.     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    # h4 A0 M! u' C
  6.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    8 _: Q  R2 i/ k- a/ A
  7.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    ( E) u) u: F. g" U" W+ g  m
  8.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;6 M9 p$ t! ~! w7 R6 e$ a2 \1 y
  9.     NVIC_Init(&NVIC_InitStructure);
    / L6 _2 w! Q4 P. D) M

  10. ( T6 ~8 \: ^7 ~; a) y
  11.     // 启用DMA1 Stream4 (SPI2_TX)中断  J. \) _9 {4 {$ ?( A
  12.     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;5 D6 b+ p8 W1 ^4 J
  13.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;" s! z* e5 e# j" w
  14.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    7 E! r4 U, X, Q' L
  15.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    & E! D$ z' K* ~$ Q" V' ]6 `6 u
  16.     NVIC_Init(&NVIC_InitStructure);1 s. U. X4 F$ H9 [3 q
复制代码
BLANK中断用于产生空白脉冲,初始化DMA传输,并在每个扫描周期后锁定先前传输的数据。2 K* X- K9 Q3 e& o" u7 D( Y5 ?

7 i( ^2 u& |6 E+ F2 F- r1 s
, W: ]" ~" G" X' q" l3 K2 ?
Timers
# E: Z2 u/ ~% K0 G" F最后,定时器模块配置:
- [. p8 Q4 G) b) }
  1.    #define TLC5940_GSCLK_COUNTS    256                   // GSCLK在BLANK 脉冲之间进行计数& O( j- e6 I6 i2 [3 l
  2.    #define TLC5940_GSCLK_FREQ        1000000            // GSCLK频率
    & Z& K$ N$ [1 A* Z. Q9 L
  3.    #define TLC5940_BLANK_COUNT      50                     // 在切换到下一列之前,允许前一扫描列的正电源轨道关闭的填充) h" C4 k0 O! g. H3 z/ Z
  4.    #define TIM_APB1_FREQ                  84000000          // 内部时钟频率(CK_INT)
    8 z2 ?+ O# Q1 }2 C+ c, z

  5. 7 Z) f0 |6 [2 O- }! D( p
  6. // 启动计时器模块
      V% R. G% {. Y
  7.     TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
    ! U2 o- |" c$ F) G
  8.     TIM_OCInitTypeDef TIM_OCInitStructure;- [; ?! W) F6 y9 E8 c' U: Z' o0 P

  9. 9 G* S' r  R# S
  10.     // 反初始化计时器模块和初始化结构) @6 G: X( m6 u
  11.     TIM_DeInit(TIM3);
    , a& V) t0 r0 D7 y; g- P
  12.     TIM_DeInit(TIM4);' q6 k2 `; [# o$ T4 G
  13.     TIM_TimeBaseStructInit(&TIM_BaseInitStructure);
    0 o7 I# U) p9 x7 ^1 d! L5 [7 z
  14.     TIM_OCStructInit(&TIM_OCInitStructure);9 ?; L8 D2 M9 p$ n1 Q6 A& G
  15. ! w$ F3 }% H0 V
  16.     // 设置TIM3来生成“主时钟”# F2 R* l# E& w& X5 ^7 a: v
  17.     TIM_BaseInitStructure.TIM_Period = 1;" @, p- K4 Z9 g
  18.     TIM_BaseInitStructure.TIM_Prescaler = (uint16_t) (((TIM_APB1_FREQ / TLC5940_GSCLK_FREQ)/4) - 1);    // 请注意,4的除法因子是由OC1频率vs CK_INT频率引起的
    1 b  o/ c0 E  `% t+ o
  19.     TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    3 ?0 B7 m  ]& Y( J" O
  20.     TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    # W9 e; X# ?+ P: G
  21.     TIM_TimeBaseInit(TIM3, &TIM_BaseInitStructure);! B5 q( B, V4 c
  22.     // 配置通道1输出比较作为触发器输出(用于生成‘GSCLK’信号)
    + p8 `! w1 ?; J
  23.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;1 d* d# E. M( U; `  o
  24.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    7 e+ h* e  B" n$ j& P+ @
  25.     TIM_OCInitStructure.TIM_Pulse = 1;
    8 ^* W8 F; ^7 B$ v1 H1 |
  26.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;' \+ [" a' E- T" J0 V
  27.     TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    ( O! `8 n0 }; @2 u. H- E: d3 s( F' d9 T
  28.     TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    * G8 G' M7 q! N

  29. ; s% Q, E& {, B" l6 i
  30.    // 为对称计数器设置TIM4基准,最大计数指定为“GSCLK计数”(实际上是TLC5940的灰度分辨率)# `& z+ g8 M0 q) z4 D, F
  31.     TIM_BaseInitStructure.TIM_Period = TLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT;                   // GSCLK溢出计数(对“阻塞”的空白信号额外加1)8 e- |' l) h: C* w- b
  32.     TIM_BaseInitStructure.TIM_Prescaler = 0;* k% f. n. u0 Q) O2 q) R
  33.     TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    * z8 J# j  N0 ~$ b  B+ L1 z
  34.     TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
    5 {$ c3 U* u2 r0 Y
  35.     TIM_TimeBaseInit(TIM4, &TIM_BaseInitStructure);  d9 `0 G' d3 e4 D, m0 R% _! o
  36.     // 将Channel 1输出Compare配置为触发输出(TIM4用作时钟信号来生成'BLANK')
    , J5 t8 g: s% f" w, \% a
  37.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  f( K5 r" k0 [. |
  38.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      ?" c+ I5 K/ x" v' X
  39.     TIM_OCInitStructure.TIM_Pulse = TLC5940_BLANK_COUNT;
    ( t( F- W. b( \% H2 y" T' K
  40.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;* o: N( K" g- G; l! z4 B
  41.     TIM_OC1Init(TIM4, &TIM_OCInitStructure);
    . V( c6 w6 {  C! X
  42.     TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);4 Y4 j& v; d& E$ S  U6 l5 ?
  43. ; q+ \% {: m  P; {% ~1 x# V( w
  44.     // 将TIM3配置为主计时器" D( w8 {, ^) u& t- T1 [  K
  45.     TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);                 // TRGO与TIM3的更新绑定! {# t! |4 E, v# Y2 p
  46.     TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);    // TIM3作为master启用
    % l( {# r$ o4 {. O2 l8 v( ~
  47.   r, G; }% w# z) }) j1 I
  48.    // 将TIM4配置为从属
    8 p& F/ m8 n( C! |2 I
  49.     TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);                                     // 设置TIM4(从)触发TIM3(主)' F& Z* z$ H! z/ C9 N- r1 m3 M
  50.     TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_External1);                   // 使用主信号输入作为一个“外部时钟”7 ^, n, d9 \7 s1 h8 M, T4 E
  51. * z. j6 z8 x3 G
  52.     // 将TIM4模块配置为在捕获/比较1个事件时中断(向上计数和向下计数都匹配)
    4 \) p. k/ l0 v: B% |( Y2 c& V
  53.     TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);
    0 V& E" z$ W5 {1 y

  54. , V3 b& S; y  ?; ]0 u$ H: b
  55.     // 启用计时器3和47 ?6 y' B8 Z8 U9 d7 x% g
  56.     TIM_Cmd(TIM4, ENABLE);& [# Q$ O7 N  A$ l
  57.     TIM_Cmd(TIM3, ENABLE);* e1 q6 K8 \  ]5 k9 t
  58. ————————————————& W/ h+ ~' T5 @$ |6 x6 {- a' L
复制代码
在这里,定时器3被用作主时钟,在它的输出比较1线上产生GSCLK信号,和驱动定时器4被配置为一个中心对齐的PWM输出在OC1上。
7 K5 A: \  b5 k; s4 d3 sBLANK计数是有效的填充脉冲,允许:
4 X# a- {7 ]& [" J; X% _! _/ G( e( i9 R

3 W# x+ S. g) K* C最小空白脉冲时间$ B1 U( U) e6 \% h% e" R% ~% e
XLAT和DMA传输触发5 t0 I6 j  e, R5 x$ k2 [( ]4 `
MOSFET输出在之前的扫描列完全放电(我已经通过观察放电时间在我的示波器调谐)  g6 k9 l# @0 T3 F3 D' D9 A: n
设置了GSCLK频率,并且在下降和上升的空白信号边缘之间的GSCLK脉冲数设置为256,因为我使用的是8位的颜色,而不是TLC5940芯片(12位)的全部功能。这意味着在空白脉冲之间将有256个GSCLK周期。
) H5 z: \2 L9 f3 z* u3 r8 s* Z; K) E外围设备现在已经完全配置好了,所以最后要做的是查看中断服务例程,并调查结果:0 \  G4 G) X, r# Q/ r0 v
2 e  q) ]" f* ]

  j" z/ l! N6 mISRs0 L. @0 m" X/ Y
  1. #define DISP_SCAN_FREQ                 200                                                                                                                                                                           // 扫描信号的频率
    6 B% {  U" _, M7 Z  w
  2. #define DISP_BLANK_CYCLE_LIMIT    ((((TLC5940_GSCLK_FREQ / (TLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT)) / DISP_SCAN_FREQ) / 2) - 1)   // 扫描前要计数的BLANK循环数/ G# G2 |2 t4 [% X) J( I% B
  3. , k9 k- h% j2 r
  4.    void TIM4_IRQHandler(void)
    2 x5 b7 T. x5 b7 d% ]. s
  5.     {
    - ^4 M- n  X5 `2 s
  6.         // TIM4 IRQ处理程序有几个任务:
    ; {! K( W. p! h- k4 a# y
  7.         //    - 切换扫描信号
    / s8 c5 V) Y- d5 f( n
  8.        //    - 锁定新选择('扫描')列的先前传输的数据7 U8 q2 w2 I$ y8 j+ b* _# C8 t+ ^
  9.         //    - 设置并启动SPI2 DMA流来传输下一列的数据' i0 B1 ^4 y+ b( }( s
  10.         // 所有这些都应该在BLANK信号(TIM4 OC1)高的窗口内执行(而不是完整的SPI传输)。
    " d( ]$ V5 h  v4 b7 c( [2 O- W

  11. $ l5 ?: f$ e. C8 K- x" o5 e
  12.         // 检查所生成的中断是否为OC1更新. s) z9 O- i6 n. E' A: W
  13.         if(TIM_GetFlagStatus(TIM4,TIM_IT_CC1))- u5 S) e& v' ]
  14.         {  Y* A% f8 J+ j3 s, x
  15.             // 清除TIM4 CC1中断位6 R5 \, Z7 S+ q3 p( z6 p, q& M# X& z, I
  16.             TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
    : w+ T/ I' ~  W) M
  17. ( Y8 d- i2 l; H1 G
  18.             // 仅在向下计数时执行事件(这确保在空白脉冲内触发XLAT脉冲、扫描更新和SPI传输)
    6 S( u# k$ P" M- w8 x/ S
  19.             if(TIM4->CR1 & TIM_CR1_DIR)3 c7 X' ]1 j5 ^5 Q6 _, h; L
  20.             {7 B: U5 W  {. |6 }2 G5 F$ x2 u9 V
  21.                 // 检查是否需要'SCAN'更新(XLAT脉冲,扫描切换,下一次传输触发): J: a/ v" k( ?- `/ l* K
  22.                 if(dispBlankCycleCnt++ >= DISP_BLANK_CYCLE_LIMIT)$ _; g6 [% ^8 U1 Y" |! @" r; N# H9 t
  23.                 {
    9 w' _& @! ^4 J- I
  24.                     GPIO_SetBits(GPIO_XLAT_PORT, GPIO_XLAT_PIN);                  // 设置XLAT引脚
    , \; A& ^, m) \
  25.                     dispBlankCycleCnt = 0;                                        // 重置计数器+ q( o  P( f& M& v7 L
  26. $ }$ d( e% ~7 D+ z) w7 @' B
  27.                     // 确定当前列,并相应地移动7 j# Q5 x# Z) Z4 A6 m; s) w
  28.                     if(dispCurrentCol)
    ' b# e  p& A0 Z( f9 A
  29.                     {
    # z* X% N4 |0 x; P9 j( N
  30.                         dispCurrentCol = 0;                              // 更改为“0”列
    2 U( X& v. J7 h( b! U" @- v
  31.                        GPIO_SetBits(GPIO_SCAN_PORT, GPIO_SCAN_PIN);      // 设置扫描引脚(注意列0是逻辑高位,列1是逻辑低位)" J/ U/ K0 a/ Q
  32.                         DMA1_Stream4->M0AR = (uint32_t)&dispData1;       // Send *next*列的数据(由于当前列现在是'0',因此将发送(针对下一个循环)  X9 q- m9 E- F9 B& T! c+ _. {
  33.                    }
    $ O  g- I' [& k, S% Z  e) Y
  34.                    else+ P( |/ P" k: f; ?
  35.                     {
    0 F+ y# ]& E% a
  36.                         dispCurrentCol = 1;                                    // 更改为“1”列
    1 t; P8 F0 d2 `. x4 D4 [; E
  37.                         GPIO_ResetBits(GPIO_SCAN_PORT, GPIO_SCAN_PIN);      // 重置扫描引脚(注意列0是逻辑高位,列1是逻辑低位)1 u1 v$ ^5 b/ ^
  38.                         DMA1_Stream4->M0AR = (uint32_t)&dispData0;         // Send *next*列的数据(由于当前列现在是'1',因此发送了(对于下一个循环)& T  c! K% u) W2 z% k7 e
  39.                     }
    * g) ^, h, W9 _5 C; f
  40. . ^1 I+ D6 m( L' w+ l" _9 n
  41.                     GPIO_ResetBits(GPIO_XLAT_PORT, GPIO_XLAT_PIN);             // 清除XLAT引脚) @! w/ {4 g2 P
  42. % l3 ]  T) ]5 U& ^! ]
  43.                     //Trigger the next transfer
    - X* D8 d2 c+ y+ }5 v
  44.                     SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);        // 启用DMA传输请求3 S0 [$ @$ a/ J1 Z/ c' p/ f; }! q, Y
  45.                     DMA_Cmd(DMA1_Stream4, ENABLE);                         // 启用分配给SPI2的DMA流
    7 C: f3 ?8 z/ i2 M) j& ~$ `  |
  46.                 }
    6 D2 N) q' K( f7 L" K
  47.             }/ w  S8 H: J/ N/ Y2 J
  48.         }
    * Z1 T  ?4 g  z. |( ]9 l  e4 s
  49.     }
    0 H$ q7 Z6 I- G) D
  50. % M6 W5 a) T: O9 t2 S
  51.     void DMA1_Stream4_IRQHandler(void)7 C# k* Z7 d5 K- M% _, Q$ f2 `
  52.     {
    " W: b- K# H- o8 a0 c: Z' M
  53.         // 检查是否设置了传输完成中断标志
    & G4 u, n' `/ |' z
  54.         if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4) == SET)
    0 @' r* E2 p$ m, W# _
  55.         {
    4 Z+ P" z& l1 r4 Y3 {+ k& i
  56.             // 清除DMA1 Stream4传输完成标志% N1 k8 D3 z& s1 A+ G9 ?' Q
  57.             DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);
    $ b* V+ w/ o4 |# l& E
  58.         }
    ) v6 B4 U; ^0 p1 d$ e7 _
  59.     }/ V# ^9 n; |" w6 B/ @: g* ~
复制代码
DMA ISR目前没有被使用(我确实打算用它做一些无关的事情),但TIM4 ISR基本上控制了整个显示。+ l2 e- ^1 b: n0 J7 X) i. y
空白脉冲的上升边缘(有效地)触发中断。在确定正确的ISR触发了事件之后,使用TIM_CR1_DIR位检查计数器是否正在减少计数。这确保了我们只在BLANK脉冲的上升边缘执行以下任务。9 x; e6 _/ h! W9 i3 h: m
每当ISR运行时,我们增加一个计数器,如果这个计数器超过扫描显示所需的数量,我们就使用XLAT信号锁存前面的数据,切换扫描信号,并传输下一个数据(在disdata0[]或disdata1[]数组中找到)。
$ ]( z% m' ?. P$ B. F在dis_blank_cycle_limit中计算扫描前等待的空循环数,其中考虑到:
  F( P9 \: ?$ s1 I  z% ?1 b2 T* l& H% m& @1 s

" h% M/ G2 _5 f8 N9 tTLC5940_GSCLK_FREQ -灰度时钟频率
$ R/ ]  ]' i/ u. u) mTLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT——上升的空白边之间的GSCLK脉冲数
$ Q, G6 b" L% Iexpec_scan_freq -我们希望扫描数组的频率(此处设置为200Hz)
5 Q3 M) z5 a9 I- i: ?/ e现在,更新disdatax[]数组中的数据将改变led上显示的内容。, P, i& H/ d: M: e$ W
GSCLK频率为1MHz,扫描频率为200Hz,我没有明显的LED闪烁,即使我听到人们谈论使用>5MHz来避免它与他们的设置。9 c# L: q7 m6 T* c
' d' \# F: M7 ^

4 m& |. k2 ~3 f( R! V7 oLogic Analysis6 _, m) f7 ^5 U' d6 ~4 p5 I: d
我在STM32F407输出和TLC5940显示板输入之间附加了一个逻辑分析器,如下所示:
# T9 X: u6 l. x
20.png
现在我们知道GSCLK周期是预期的,我们可以研究空白时间来确定正在以8位分辨率记录的灰度数据。' _& E- M  {/ a. R% N5 @* D6 f
下降和上升空白边之间的时间是305.6 - 49.6 = 256us,这是预期的。9 a6 f* x  l( S) w% v
我还调查了一个更接近的水平,以检查信号的相位是正确的2^8计数。
) |8 J6 c1 R" M6 y" y6 {' d3 i1 X
21.png
最后,检查扫描宽度,我们可以看到一个列启用了2.445ms。1 ]" B2 o2 `" s5 N1 X
即扫描速率为409Hz;考虑到2。5us =不能被256us整除,这很好。$ y' J6 S! p! M( b/ `2 u5 ~
上面的捕获还显示,当空白计数达到极限时,相关的ISR锁存前面的数据,
8 f; z1 C3 i  v+ F切换扫描行,然后触发SPI传输。
6 H* B# Y9 x5 w  B9 M. m1 U. Q然后在锁定该数据之前计算所需的空白周期数(XLAT信号在2445ms光标所在的蓝色箭头右侧几乎不可见)。5 k+ P; x' Z, L8 X

$ }; I7 I( Y6 }5 m
3 V$ E- D' w. O8 z; m0 ^. {! b/ ]) |1 _! h" i3 |/ J) {
赞 收藏 评论0 发布时间:2021-10-12 13:31

举报

0个回答

所属标签

相似技术帖

官网相关资源

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