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

STM32F4 SPI DMA

[复制链接]
STMCU小助手 发布时间:2021-10-12 13:31
STM32F4 SPI DMA
自己整理(存储器到外设模式)9 ~9 o1 y( `( B, t, L8 q
SPI结构体( B5 \. {4 \% I9 C" e  Y( x# j

- O3 ~" K* s7 i8 U0 J. k( e

  K5 B4 @$ f( Y1 _: e
  1. typedef struct
    # C$ G1 r! X& a& ?
  2. {
    * ~& i1 F; A6 e0 Q% d; v3 B5 t
  3. uint16_t SPI_Direction; /*设置SPI 的单双向模式 */
    8 s- M4 u1 Z- G& @/ l; [
  4. uint16_t SPI_Mode; /*设置SPI 的主/从机端模式 */
    / f8 W# z1 O, C3 N& q3 |. {
  5. uint16_t SPI_DataSize; /*设置SPI 的数据帧长度,可选8/16 位 */
    9 y3 ^/ u5 h: G. F" T5 H7 P6 a# S
  6. uint16_t SPI_CPOL; /*设置时钟极性CPOL,可选高/低电平*/* ]2 C) h$ S+ F% I& _
  7. uint16_t SPI_CPHA; /*设置时钟相位,可选奇/偶数边沿采样 */' q: L2 ?1 A. i8 [+ h2 ^( n
  8. uint16_t SPI_NSS; /*设置NSS 引脚由SPI 硬件控制还是软件控制*/( [" i. B& g) z3 E
  9. uint16_t SPI_BaudRatePrescaler; /*设置时钟分频因子,fpclk/分频数=fSCK */9 f$ b' r2 K- v" ]* C/ A6 _
  10. uint16_t SPI_FirstBit; /*设置MSB/LSB 先行 */
    1 V3 E5 Y5 J2 m
  11. uint16_t SPI_CRCPolynomial; /*设置CRC 校验的表达式 */
    . s$ }' Y4 g7 p! E3 n
  12. }SPI_InitTypeDef;
    2 O' U' A7 _/ W0 a. _1 y
复制代码
SPI引脚编号5 c$ U7 O7 U3 l5 F
14.png
15.png
16.png
SPI配置
  h/ ^$ M0 D; g* b/ m6 Z% P
  1. void SPI_Config(void)
    ( A! c0 n6 \% u! K$ H$ W
  2. {5 G$ k9 F! Y+ V; q- O
  3. GPIO_InitTypeDef GPIO_InitStructure;
    % D! t: S- {5 }+ C
  4. SPI_InitTypeDef SPI_InitStructure;5 k1 N- P' p  h6 A3 E) U4 m: w8 C1 d& [
  5. 2 l  R( ]' O: f0 F! u0 R
  6. //1.初始化GPIO
    * a; C% f* m4 M  Z+ z# q
  7. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB,ENABLE);
    * R6 q6 b& W4 X- `5 a7 Z4 f
  8. /* 连接 引脚源*/
    , f. k4 p. ]' \% C3 [0 T5 c4 T
  9. GPIO_PinAFConfig(GPIOA,GPIO_PinSource15,GPIO_AF_SPI1);
    ) z& I, N" |) [
  10. GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);
    ! W$ T7 K6 \  ~  k: R
  11. GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);
    ' q8 w* o" h& g+ f2 W8 A
  12. % f/ ?) e" ~6 l6 r* M: M+ N
  13. /* 使能 SPI 时钟 */
    ' a2 {1 {: X- t' k. a0 p
  14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    * ]/ H, j$ w( k: f$ @
  15. " C" _+ N( _/ G  d! G& R6 m
  16. /* GPIO初始化 */' _$ l& j& b6 |- a" E5 w: X
  17. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;       
    % V( n% [& r* L1 d
  18. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; ; i7 t4 m3 A7 g) y. `
  19. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;! V1 g8 c7 x3 v: @
  20. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    : J% \$ \8 f+ l9 R1 @2 f
  21. 3 p% Y2 {$ ^3 o! f0 O: G2 u0 b
  22. /* 配置SCK引脚为复用功能 */* `+ |: x7 s3 w
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ;
    5 g7 H6 ~- X  Q; @
  24. GPIO_Init(GPIOB, &GPIO_InitStructure);
    - `) ]& F3 R/ q" x
  25. 8 r: n5 i( V+ x0 \
  26. /* 配置MISO引脚为复用功能 */  c7 N9 |, Y5 t
  27. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    4 v$ a! V% z2 Q
  28. GPIO_Init(GPIOB, &GPIO_InitStructure);$ R+ c- ~% f8 S2 m" I4 H

  29. ! v! Y2 |4 ]( D& ?
  30. /* 配置MOSI引脚为复用功能 */
    ; v/ \) F( V/ n/ I. ]0 k1 j
  31. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    ( k" i9 t; ]6 I3 C% e' P- S: O6 ]0 k
  32. GPIO_Init(GPIOA, &GPIO_InitStructure); : g, ^% g0 m& \
  33. * V" r4 i6 V3 g( m$ j5 d% v
  34. /*CS引脚 */+ Q- \( W% a7 P1 c& }; q
  35. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;- e( Z5 Q" @1 k* J9 _0 @* `3 r
  36. " R: ?) [, j- n5 |; c/ h
  37. /* 配置CS(NSS,自动控制SPI的片选信号)引脚为复用功能 */6 c  W' t$ e; y, i( f8 y
  38. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 ; * l" Y) [- m) k2 h% s4 |
  39. GPIO_Init(GPIOA, &GPIO_InitStructure);7 a" H' i5 O, l1 e) m/ [
  40. * Z7 k* |: t0 G: h- x4 g2 k8 X9 p
  41. /* 停止信号: CS 引脚高电平 */3 t& T  `/ I' r
  42. GPIO_SetBits(GPIOA, GPIO_Pin_15);
    8 \; p" |" Y! \6 _! E9 B
  43. ) h) n4 |1 H5 k' s
  44. //2.配置SPI工作模式
    ( W+ ^5 `+ [% Y' B6 D% z& i
  45. // 分频
    0 A/ R6 D# C; x: v2 ^  O$ i2 f1 }
  46. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    2 U- J, J# [' {& W3 @" t% T
  47. // 数据捕获于第二个时钟沿" W# a; {" W  b5 }
  48. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    " M4 t$ [/ S+ y4 y
  49. // 时钟空闲idle时是低电平- x1 k4 b/ Y' B( b
  50. SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; / V" ]8 a: z, F! s8 a+ x
  51. // 不需要使用CRC校验" M- }" @2 A. Q+ b
  52. SPI_InitStructure.SPI_CRCPolynomial = 0; " V, |7 r: ^7 K( d( n
  53. // 数据帧长度为8位
    9 n4 B- j! S8 Z3 X) a
  54. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
      x! B; E+ ]2 t' W, v2 D
  55. // 双线全双工模式
    ) j, P5 V) s$ N6 N
  56. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    3 }6 `2 y$ _& c8 {- J
  57. //数据传输从 MSB 位开始: M3 r7 Z, V1 Q
  58. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; 9 s8 H" p, h( t' W2 @
  59. //NSS 信号由软件管理) E" S2 O/ v# B
  60. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;& @* e' M: w& `2 u
  61. //设置为主设备: V, y+ V7 U3 ?5 ?0 X9 c* }
  62. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       
    8 x- i9 H* i5 T: h% N
  63. SPI_Init(SPI1,&SPI_InitStructure);
      Q4 t0 U' g3 C; \

  64. * T( x+ ]: Y+ u( y+ F) R! l
  65. /* 使能 FLASH_SPI */        9 X% i  s# `  o1 A4 O# s
  66. SPI_Cmd(SPI1, ENABLE);
    $ B/ U& j3 r- b+ _
  67. }
    " r5 P2 [' q  s! [" U4 W3 F
复制代码
DMA结构体
4 p, z9 L5 M6 X2 e" z
  1. typedef struct {) X! z% K' O4 `0 w
  2.     uint32_t DMA_Channel;                                 //通道选择' v+ F: C% y, ^' j# ?
  3.     uint32_t DMA_PeripheralBaseAddr;         //外设地址
    # }7 R) ~6 ?* `/ E8 [% Y' m- i
  4.     uint32_t DMA_Memory0BaseAddr;                 //存储器0 地址
    ; c% X' {5 V1 t, j- T
  5.     uint32_t DMA_DIR;                                         //传输方向& K! q8 \$ H: D. w$ _* D
  6.     uint32_t DMA_BufferSize;                         //数据数目
    ' @+ _1 `" g; M- e) y. f7 N
  7.     uint32_t DMA_PeripheralInc;                 //外设递增& C* G( F8 [1 [
  8.     uint32_t DMA_MemoryInc;                         //存储器递增
    . R/ n: I7 m; K! [3 T+ o* G
  9.     uint32_t DMA_PeripheralDataSize;         //外设数据宽度" a3 c  q2 }$ P* d3 o
  10.     uint32_t DMA_MemoryDataSize;                 //存储器数据宽度4 `1 h7 N" |3 {
  11.     uint32_t DMA_Mode;                                         //模式选择
    7 I. Y0 A$ ]6 {4 D2 r! K! L9 k
  12.     uint32_t DMA_Priority;                                 //优先级
    5 W; J& B" F8 ]
  13.     uint32_t DMA_FIFOMode;                                 //FIFO 模式
    # |+ `" x5 W, M# i& Z% z' o2 R
  14.     uint32_t DMA_FIFOThreshold;                 //FIFO 阈值
    , A1 X6 v( {# j# w- M6 J4 y* p
  15.     uint32_t DMA_MemoryBurst;                         //存储器突发传输
    & |( t0 t1 ^+ F) R
  16.     uint32_t DMA_PeripheralBurst;                 //外设突发传输) t; z( Q) r, Y1 w. N6 @; \5 X) [. y
  17. } DMA_InitTypeDef;' n& U; m; U8 f. k
复制代码
6 X7 C' y+ H' W( {
DMA请求映射
( S. ?. B) @+ i" g  C: I/ _
17.png
18.png
DMA传输模式9 w, O; v/ q, w0 r7 X
19.png
SPI 发送DMA配置7 W* A  _/ R) U8 j, T! V/ \% D( j4 Z
  1. #define SENDBUFF_SIZE (1024*20)            // 一次发送的数据        ( h# P5 k+ E# k% I5 ?0 t' s
  2. uint8_t TX_Buff[SENDBUFF_SIZE];                // 发送缓存. A7 W; F; o9 O! q1 l# c
  3. void SPI2_TX_DMA_Config(void)
    , K& Q  e5 ~' J- ~# T; f0 b, ^/ Z
  4. {
    5 m3 J' d2 d9 `* v
  5.     // 中断结构体
    9 O! i7 t; z' {" r8 e
  6.     NVIC_InitTypeDef NVIC_InitStructure;                  ]- ?0 r+ M# Y7 k
  7.     // DMA结构体3 X& I* X. s" o" [6 I
  8.     DMA_InitTypeDef DMA_InitStructure;                 
    # O& L/ N: M+ ]$ Q* p
  9.     /* 使能DMA时钟 */                 
    & F) _' Q2 o- p; N" T- B8 `2 T
  10.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);       
    4 x, d2 W8 o+ Q" Q7 [2 C! _  g
  11.     /* 复位初始化DMA数据流 */  
      {# t- X" h4 ?2 A' a* |
  12.     DMA_DeInit(DMA2_Stream5);                                                                ! e. o& N2 G8 n7 G
  13.     /* 确保DMA数据流复位完成 */  
    " i# _' f3 z2 L8 ~
  14.     while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE);       
    ) W/ o% U; p; z/ s9 T$ m! ]

  15. , s1 |% @4 k2 l
  16.     /* 配置 DMA Stream */
    . i9 D9 Q$ G4 q) E0 k
  17.     /* 通道3,数据流5 */          
    + @1 T5 @8 ~% a, I
  18.     DMA_InitStructure.DMA_Channel = DMA_Channel_3;- y) S; n  f3 z# C& K( E$ O7 I7 F
  19.     /* 外设地址 */  
    6 }' x- x& T" y7 }( L+ i
  20.     DMA_InitStructure.DMA_PeripheralBaseAddr = (SPI1_BASE+0x0C);       
    $ F4 x8 U. F* P+ Y) X- p: |
  21.     /* 内存地址(要传输的变量的指针) ,DMA存储器0地址*/         
    * D5 ~' `' v2 {
  22.     DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TX_Buff;        3 v0 D6 t7 A6 I- P
  23.     /* 方向:存储器到外设 */                       
    ! t" c, \0 o8 R6 ~  A# e- P
  24.     DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    + I; D$ ]" C5 C8 q7 H+ n
  25.     /* 数据传输量 ,可设置为0, 实际发送时会重新设置*/            * J% N: T  F1 {) i3 c
  26.     DMA_InitStructure.DMA_BufferSize = (uint32_t)SENDBUFF_SIZE;                6 A0 y3 I0 [3 @& C' K- q6 [, }
  27.     /* 外设非增量模式 */               
    8 E+ f/ v* ]# z( k8 q
  28.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;+ o* N, H7 C- h0 s
  29.     /* 存储器增量模式 */         
    / }4 F  B( v. F/ V
  30.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;2 F- Q; _0 ]- H8 s
  31.     /* 外设数据长度:8位 */         ) G7 M9 A% o! s: V. Q5 X- k3 d
  32.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    * [! C; D  B& s7 o: ]. c/ ?' ]/ Z
  33.     /* 内存数据长度:8位 */
    0 z  v+ c; j. X) N/ ~% R3 r; ^
  34.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;, {9 ]5 ]8 N9 m' c3 g, C4 L
  35.     /* DMA模式:正常模式 */                  9 g; P7 i# S# Y+ l* C  y. e
  36.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;1 l* I* N; j) J3 q* X
  37.     /* 优先级:高 */                         " B$ F  Y! y' X" U" N
  38.     DMA_InitStructure.DMA_Priority = DMA_Priority_High;3 b4 s0 x7 A& ^# h/ u; ?
  39.     /* 禁用FIFO */: i3 W; u$ ~" ~8 Y1 Q
  40.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                       
    ; H( K, q( w. F! a" B( s
  41.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;   , ]) @7 e# K6 N
  42.     /* 外设突发单次传输 */  
    # G0 x4 X8 K8 R4 K2 l2 b
  43.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                   
    / f- T: Y; G  Z) x7 A
  44.     /* 存储器突发单次传输 */  ( q+ i+ V  V& W& F8 N
  45.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    ) X( \6 ~) U+ }4 D; x
  46. - P% p6 h  d; m7 i7 @
  47.     /* 初始化DMA Stream */               
    - {# W# q7 G8 ?5 Y2 I9 f
  48.     DMA_Init(DMA2_Stream5, &DMA_InitStructure);: W: j/ E6 u& S' o
  49.     /* 开启传输完成中断  */               
    ; P1 d7 E; b4 g1 g4 u2 E
  50.     DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);
    ( h: {! Z4 \/ E! }! h% ]
  51. 1 N) I1 M2 k! q6 Y0 w
  52.     // 中断初始化 - J6 v) i# Y- Y& n2 O* [
  53.     /* DMA发送中断源 */  
    . a' S, }4 u( G- [0 Y
  54.     NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream5_IRQn;        ) z& N" ^" U  r$ K& m& O6 `7 I3 F
  55.     /* 抢断优先级 */  5 j% o5 k& \' Y$ a  C
  56.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    0 [2 e3 m/ s; L3 U+ [9 g
  57.     /* 响应优先级 */  
    ( A. y7 N) @) |( p2 e9 L$ b
  58.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                               
    2 S* F: ^4 m$ o, t
  59.     /* 使能外部中断通道 */ / m# ?4 @. M$ N8 Q+ u, r5 E
  60.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                 
    / u5 w, P5 `2 q1 {
  61.     /* 配置NVIC */                5 @. {3 T  a1 o6 M5 X  J
  62.     NVIC_Init(&NVIC_InitStructure);
    * R& I% F/ |: k- g. Q% M; R
  63. }
    4 v5 w& C, s; P7 |7 g3 K5 K
复制代码
DMA发送中断服务函数. W! \* N6 N! }' c# R# h% c
  1. void DMA2_Stream5_IRQHandler(void)
    ! ^) Q: P" h  s% W
  2. {# N" M9 S# o0 o# B9 M
  3.     // DMA 发送完成
    7 \" I" p% t) F, ?0 z) ?5 [9 D5 O
  4.     if(DMA_GetITStatus(DMA2_Stream5, DMA_IT_TCIF5))        0 p- \( @: I2 y  f4 z
  5.     {
    5 H# ~6 }- F% U
  6.         // 清除DMA发送完成标志
    # ?, D4 h* j- Z
  7.         DMA_ClearITPendingBit(DMA2_Stream5, DMA_IT_TCIF5);       
    : F' M( d& \: o/ S( c5 U$ P; z
  8.         // 片选拉高,数据发送完毕        ' ?9 L0 p/ l9 v% P. o0 i0 i
  9.         GPIO_SetBits(GPIOA, GPIO_Pin_15);        ) E( `  }# }& V9 G/ @
  10.     }
    ) [4 h; G1 Y3 I: ~) X+ ~
  11. }0 W7 C# x" z( v8 N! _7 b- A
复制代码
SPI 接收DMA 配置
8 }" f  d& t' ~8 s4 _3 e
  1. #define RECEIVE_SIZE                  800          // 接收大小& H' R+ b2 K" q; Q
  2. uint8_t RX_Buff[RECEIVE_SIZE];                // 接收到缓存2 G1 t! s* v- B$ ^5 u
  3. void SPI2_RX_DMA_Config(void)
    2 I- U# Z3 U" Q3 N" I$ z
  4. {
    4 f. d* Y# b1 M9 A5 B+ g9 B( o/ S- A' Q$ ]
  5.     // 中断结构体
    + Z1 g, U8 c. b9 l& \6 `% u9 e
  6.     NVIC_InitTypeDef NVIC_InitStructure;        : s! S6 u( s6 W  a9 S
  7.     // DMA结构体  
    : D  m( X+ y9 r: A, h% e
  8.     DMA_InitTypeDef DMA_InitStructure;               
    7 U& z9 r2 U2 e4 l* c" }
  9.     /* 使能DMA时钟*/  
    ) N4 G2 L1 B! ~3 W+ H; S. L
  10.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);                                        /* 复位初始化DMA数据流 */ + l. l0 O) V# k4 t: ~& n# S
  11.     DMA_DeInit(DMA2_Stream2);                                                                                                /* 确保DMA数据流复位完成 */% T$ f; \  ^1 y: K. l
  12.     while(DMA_GetCmdStatus(DMA2_Stream2)!=DISABLE);; ?* y; w6 o6 |! \/ j  a

  13. ! G7 T) o" C5 D' }) v7 o* ]' ]+ \' s
  14.     /* 配置 DMA Stream */1 E8 L" {) m9 E: |$ Y* z7 a1 c
  15.     /* 通道3,数据流2*/          
    & e; c! F& H6 _
  16.     DMA_InitStructure.DMA_Channel = DMA_Channel_3;                          : e2 Q; ]7 O  l7 E1 v9 P
  17.     /* 设置DMA源:串口数据寄存器地址*/  4 q, i& }0 ?. k' n" @
  18.     DMA_InitStructure.DMA_PeripheralBaseAddr = (SPI1_BASE+0x0C)                ;6 a5 B/ U# P7 r- M% M. B$ O
  19.     /* 内存地址(要传输的变量的指针)*/  0 i4 }; V  C2 @8 T2 ^' a) T
  20.     DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RX_Buff;                        0 S3 ]" ^) m+ D6 z5 h3 [2 G! l
  21.     /* 方向:存储器到外设模式 */                        % V. `, n- `2 P3 |( K: ?+ p
  22.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;4 B, B, a* T. b6 a+ q4 ^! d0 K
  23.     /* 数据传输量 ,需要最大可能接受的数据量[不能为0],实际发送时会重新设置*/          
    , Z3 G* r' o- `9 [5 a2 s' o  O
  24.     DMA_InitStructure.DMA_BufferSize = (uint32_t)RECEIVE_SIZE;
    $ x" u7 J% \) q
  25.     /* 外设非增量模式 */          
    % X8 h- d) d. W' G9 `& l/ _6 r* W2 g" X
  26.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;           7 Z4 d* y% Q; w- v' [, g
  27.     /* 存储器增量模式 */    7 J# ^4 {/ u; J' n
  28.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;, f. J# ^4 h6 o/ i4 r$ K+ ~
  29.     /* 外设数据长度:8位 */          # Z! ?, R3 h% W8 Q4 V
  30.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    2 q  W6 b5 ~! R
  31.     /* 内存数据长度:8位 */3 d7 \+ X6 P' q- w5 D1 O6 P# ]
  32.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                " h8 T6 u" \3 l0 e# a7 ?( _
  33.     /* DMA模式:正常模式 */  
    $ V8 g" a& g4 B& Y% k
  34.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;+ v8 C$ Q. ]4 O
  35.     /* 优先级:高 */           , @! L. r2 F/ D; H
  36.     DMA_InitStructure.DMA_Priority = DMA_Priority_High;2 L& G5 _4 k" r% C2 d
  37.     /*禁用FIFO*/   
      S. `* ?  h" J# r6 ^4 L- n7 c
  38.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;1 o: Z! H' d; R
  39.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
    3 R7 S3 S) A2 b) m9 t+ O7 x
  40.     /* 外设突发单次传输 */  
    ) V) o6 H( b8 _: i( s  }6 Z
  41.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;7 e* h# S: s! ?4 q( U9 h' ?$ ^
  42.     /* 存储器突发单次传输 */  1 z7 L/ j/ m3 r* l* U. a
  43.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    . |; j* I' a7 b' |
  44.     /* 初始化DMA Stream */               
    ; I# C8 k/ v: b& k$ f
  45.     DMA_Init(DMA2_Stream2, &DMA_InitStructure);                                                           . j/ c: N1 Q, i* f! c' L
  46.     /* 开启传输完成中断  */# I5 f( w% c1 E* k
  47.     DMA_ITConfig(DMA2_Stream2,DMA_IT_TC,ENABLE);                                          6 c5 E) J( Z, r

  48. $ w6 T; o3 x9 O! Z) p& X6 C3 \
  49.     // 中断初始化
    - n! S6 b, a5 Y6 S6 p+ v
  50.     /* 配置 DMA接收为中断源 *// O( U' T0 O: q2 S6 {
  51.     NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;         
    0 q$ \: e" D# F8 w( J- t, s1 j
  52.     /* 抢断优先级 */  ! Z. l$ R. b) V1 f$ q; u0 Q
  53.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;               
    5 F( x; F% L, L6 @/ N
  54.     /* 响应优先级 */  7 x( y0 w9 m& q: G7 F. E
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                                : f. R  f) D+ V
  56.     /* 使能外部中断通道 */  9 g' Q7 a+ X* k2 N7 f3 r
  57.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    ' U9 L# _; [2 U7 L9 E
  58.     /* 配置NVIC */        : ~2 J, L' ~' y  L( H+ R; l, H, ~
  59.     NVIC_Init(&NVIC_InitStructure);
    % W$ `8 |  h! W
  60. }
    ; h1 M# C1 L( u. K7 v
复制代码
DMA接收中断服务函数
% p/ U2 Y2 ], _( L- a2 X9 r5 w
  1. void DMA2_Stream2_IRQHandler(void). `% K5 q. i6 ?% p5 `! I
  2. {                                % J/ A' x9 E6 Q+ b* b+ q
  3.     // DMA接收完成0 y2 M8 p% n0 `7 T5 ~7 @
  4.     if(DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2))       
    7 ]& S6 y" B5 N& J, E% x# \
  5.     {                4 e/ i2 c0 w& a8 n6 `, Z5 J. ^3 \) C
  6.         // 数据接收完成 拉高片选
    " m! b6 W/ {! g6 {1 [3 w4 M
  7.         GPIO_SetBits(GPIOA, GPIO_Pin_15);       
    5 _* f) w0 d! o: W, R
  8.         // 清除DMA接收完成标志位               
    / A0 S2 o# M9 p3 l3 }
  9.         DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);       
    ' r8 J8 I5 E* h( h# `6 N
  10.     }: y% ?% c" e" T' G( W
  11. }
    # w8 F: _5 N3 y$ b, E  e; G% U
复制代码
DMA请求使能) h) v; e; P$ }0 \/ J
  1. //SPI2 TX DMA请求使能. w4 y  U# K) S3 |$ J
  2. SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);       
    $ S% m! }/ v: O
  3. //SPI2 RX DMA请求使能
    " C5 `$ s- i* A/ `: P
  4. SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);       
复制代码
SPI_DMA 读写一个buf4 K$ u! r. E. g3 B3 L  a
  1. #define BufSize                800
    % H" F! Z3 \4 m1 K- p3 N0 ]
  2. void SPI_DMA_WRITE_READ_BUF(void)
    7 k. C! m6 d% t
  3. {   
    " w( l6 @# x9 {4 o; _- D( w! m
  4.     // 关闭发送 DMA       
    3 Z9 A6 o/ F2 [! _* F
  5.     DMA_Cmd(DMA2_Stream5, DISABLE);       
    6 j8 n7 x* `2 z6 T5 f; C+ V
  6.     // 关闭接收 DMA       
    # b1 g& @* \1 s8 c$ T- s  E- |3 ~
  7.     DMA_Cmd(DMA2_Stream2, DISABLE);7 P- L2 E' V6 E9 }
  8.     // 设置发送的数据量5 Z: x$ D( e# K" p
  9.     DMA_SetCurrDataCounter(DMA2_Stream5, BufSize);        0 j1 Y3 g; ]. a7 U% J# \; I
  10.     // 设置接收的数据量
    * }' E/ ~  k8 v6 |+ s( o
  11.     DMA_SetCurrDataCounter(DMA2_Stream2, BufSize);. W* o* r, L. r( F# C
  12.         // 清空数据
    2 Q4 b  Q) G& H, }2 @  F6 s, t+ _
  13.     SPI1->DR;                                  8 W3 r) ^8 O0 ^0 b6 d9 P. T
  14.     // 擦除DMA标志位
    , _/ N, r4 S* A6 V
  15.     DMA_ClearFlag(DMA2_Stream5, DMA_IT_TCIF5);       
    ) f7 U+ M4 V& f! B6 I, A0 X
  16.     DMA_ClearFlag(DMA2_Stream2, DMA_IT_TCIF5);
    $ R% l6 Q1 h% ~5 O+ d5 V- {
  17. + J+ T! w9 U( x6 f( @8 j1 i9 {. K
  18.     // 片选拉低,接收数据
    + k" v3 a& B7 N' ]/ \
  19.     GPIO_ResetBits(GPIOA, GPIO_Pin_15);        ! H) C, L, a& f
  20.     // 开启接收 DMA2 p  @+ ~  S6 J; S
  21.     DMA_Cmd(DMA2_Stream5, ENABLE);        - {0 X, O+ S( y: u4 e9 C+ n% X
  22.     DMA_Cmd(DMA2_Stream2, ENABLE);0 F1 }8 Z! H1 {8 l: }
  23. }
    * b4 V- p4 t8 L. p3 E
复制代码
SPI_DMA 发送一个buf: R8 N) l; n; w$ k: g4 f
  1. void DMA_Write_buf(uint32_t SizeLen)
    2 S' [7 n! Z* G3 n! H
  2. {   
    6 A. Y/ O/ w. J- `* Z
  3.     // 关闭发送 DMA               
    , G8 z7 }  m) S9 N9 U5 q6 K) D
  4.     DMA_Cmd(DMA2_Stream5, DISABLE);       
    / t/ ^, U4 ^# i5 |6 h% p  Q3 F
  5.     // 设置发送的数据量    2 k( `0 w3 N) g% T3 k
  6.     DMA_SetCurrDataCounter(DMA2_Stream5,SizeLen);4 h# Z5 p- E% [2 |$ ~! @
  7.     // 清空数据
    1 e" M5 [* ^7 V5 Q: g" J4 t9 l5 Z
  8.     SPI1->DR;               
    5 U2 g4 H9 Z: p9 n6 |+ U
  9.     // 擦除DMA标志位        % d8 O$ S' d7 }. Z$ D" N
  10.     DMA_ClearFlag(DMA2_Stream5,DMA_IT_TCIF5);; P* |2 b7 |* N2 _6 c6 W
  11.     // 片选拉低,接收数据" Q4 x! c$ Z8 B4 W' D
  12.     GPIO_ResetBits(GPIOA, GPIO_Pin_15);       
    & @) r7 S8 B. Y8 b" x: U
  13.     // 开启发送 DMA
    : K* w" A7 M$ q7 D' h# J. T
  14.     DMA_Cmd(DMA2_Stream5, ENABLE);       
    ) F0 N1 _' [, W. b0 _4 V
  15. }) h  P! E) V3 M% S
复制代码
张贴的代码显示如何配置我的GPIO,定时器,SPI, DMA和NVIC模块,以及一些系统如何工作的解释。
$ o( h# t+ O3 j- ]注意,我使用的是STM32F4标准外设库。
6 y0 l; @  T& E; [
1 A4 _: {3 p9 l( T% j. ~5 s

) i$ k% z  R- H网上内容/ e8 s6 r! n) T8 ^0 ]% W  b: F
通过RCC(复位和时钟控制)模块使时钟信号到达所需模块:# G( L* f5 ?' K" C# O

, t3 h0 ]: e, H+ P
& I( y, {6 D- |- m2 n9 _- {0 }
RCC. C% n2 I! Z6 Z5 C6 d
  1. // 为所需模块配置时钟
    0 m; Q: {5 E' z: k1 ^5 D2 D
  2.     // 启用GPIO外围时钟$ Q" [& b- a% X/ x8 A* S" x
  3.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    % H1 Z+ R0 g, _% A' `6 }. L- k
  4.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);$ f# B9 N- l$ C5 o) y8 {
  5.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);% H/ ~: t4 n2 t  n
  6.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);) d4 Y- J% ^" h3 m6 v# l
  7. / e/ T& h5 x& _0 B5 C6 a5 A
  8.     // 启用串行外围接口外围时钟
      M( ?. o5 v4 B( ^$ s8 k
  9.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    5 g6 W) C; K# I8 f

  10.   A% Y& J  S: j
  11.     // 启用直接内存访问外围时钟
    " f: e1 W7 g' [% \6 ^  y4 h
  12.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
    # e2 {8 V. i- {# `/ y9 Z9 ^
  13. 1 M! r; a3 u  ]; Y, f
  14.    // 启用计时器外围时钟
    - W5 t. Z; K! `5 ?
  15.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);. A+ x* t5 M% r7 J) Z& P
  16.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    + q. d: X" O4 Q' j4 o/ \) j3 [/ D
复制代码
GPIO
4 Y% d4 m4 G7 s+ t2 I' e7 `0 ?接下来,配置所需的GPIO引脚:0 W' |0 G$ b7 c' S2 ^9 F( U2 M
  1. #define GPIO_SCAN_PORT            GPIOB
    0 F' o9 V# j/ ]5 E
  2.   #define GPIO_SCAN_PIN                GPIO_Pin_7
    3 @/ h& ~' x6 M& F, u5 Z, j
  3.   #define GPIO_XLAT_PORT              GPIOA
    ) t. {+ e$ _, t3 b
  4.   #define GPIO_XLAT_PIN                 GPIO_Pin_5
    & k/ U' n6 h/ e( p
  5.   #define GPIO_BLANK_PORT           GPIOB
    ! D6 R- ?/ k7 ~' R" F8 o
  6.   #define GPIO_BLANK_PIN              GPIO_Pin_62 w( u) I6 [+ j. {  d0 c
  7. 0 s$ q- J: M, c. a
  8. // 配置GPIO引脚
    : f. K8 Q) b4 B3 {1 _- u$ _  c
  9.     GPIO_InitTypeDef GPIO_InitStructure;' w  F7 J' d6 b# z

  10. 4 T$ x0 @; c6 p; S
  11.     // Timer3&4 输出 (TLC5940 GSCLK and BLANK)# Y- n5 D( G$ O2 d1 ^
  12.     GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_4 | GPIO_Pin_6);: |2 O+ F6 J( T1 l3 Z' f: Q; r
  13.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    ( h' o% }* O" {& D$ g  e
  14.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    : O+ S9 N5 \' G4 Z
  15.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;: i! D+ o8 L% X- S, i$ ^; \
  16.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;. h7 E! }+ A8 v! y
  17.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    ' C$ I3 l/ A/ R" @, R. R0 |
  18.     // 连接计时器到GPIO引脚
    ; t- q9 s. M  F4 _
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);   // 将TIM3 OC1输出连接到PortB Pin4 (GSCLK)
    . M- u1 u, P' j+ W$ B+ d
  20.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);   // 将TIM4 OC1输出连接到PortB Pin6(BLANK)
    3 \5 Y" g1 i! a3 a
  21. # x  x; O' D9 j3 x! C- A6 Y
  22.     // TLC5940 XLAT 引脚
    , D9 z* E- N( e! m
  23.     GPIO_InitStructure.GPIO_Pin = GPIO_XLAT_PIN;
    ) Y8 E8 C" f1 B2 q0 f6 D' d* |- z+ e$ C
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    1 A2 j( e* D  D# H6 |& t
  25.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;' f* m/ Y+ d% j1 n( J9 `
  26.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
      \: k& o3 C/ I
  27.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    8 I9 U  f0 ]+ L1 K8 m8 {( X
  28.     GPIO_Init(GPIO_XLAT_PORT, &GPIO_InitStructure);$ U9 ]% c' O( `% R1 f

  29. 5 N1 \( @4 d; B' L4 ?' p( E
  30.     // 显示扫描 引脚- u9 F) Y8 q! W! X& n
  31.     GPIO_InitStructure.GPIO_Pin = GPIO_SCAN_PIN;0 Z/ ?' o! z# [$ j% ^3 ?/ D
  32.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;9 k) N% W) d' [; J' Y( J
  33.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    + c3 Y8 l* X3 Q7 ]3 R1 Q
  34.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;# z- L+ V  h/ i8 ^$ J8 d' \% s
  35.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;; S: W) e3 k8 i. O  q; K
  36.     GPIO_Init(GPIO_SCAN_PORT, &GPIO_InitStructure);  b- ]$ l  D/ }9 }- |
  37. + r5 q' w+ A; p' g- A
  38.     // SPI2 引脚, _5 H, U6 W# x- y0 ^% ^4 I
  39.     //    SCLK =    PB10) t$ C. e# h, n+ y0 S3 u
  40.     //    NSS =     PB9) E3 b" k( {, x% x% J" \! b- \
  41.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;( J4 a" K( f5 ?7 d
  42.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    ' P5 C2 H# z' Q" M
  43.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;4 Z8 q4 c$ o( H4 v8 k
  44.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 M  @- X7 r- k4 ]3 J/ N$ K
  45.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    ! q2 V$ b- o) o+ a
  46.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    7 O5 P& v3 ~) n) y
  47.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_SPI2);, Y) \  p0 J- w
  48.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2);& o8 r6 x5 V+ X/ G; o* G
  49. + r9 v* p4 c6 I' y
  50.     //    MISO =    PC23 L* Z5 C- P' s  b4 a
  51.     //    MOSI =    PC3
    / x% _1 E9 n( N% S7 M' \
  52.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
    % j" Y# |3 H% C% t
  53.     GPIO_Init(GPIOC, &GPIO_InitStructure);
    ; r- y0 s% R) m, n
  54.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_SPI2);. W% ~2 z0 T7 R
  55.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2); 1 O+ |2 G# Q2 z; j, Z8 s; N
  56. ————————————————
    / I1 i* V% c" o; K1 S5 F% o
复制代码
这里的要点是,我将把TIM3的OC1输出直接连接到GPIO引脚(用于GSCLK),把TIM4的OC1输出连接到BLANK信号。$ D1 z6 @' i; J
4 o* }5 |' ]) O& ]" ?4 y: M

* }* S$ Y1 r/ q& z* z( v  hSPI( M: V( _2 O* N% \9 p% O0 Y$ b' _
现在SPI模块可以初始化:
8 p9 c. c. x0 I/ L0 |
  1. // 初始化SPI模块
    ! ^  H! S- a! o- D+ K; @, J
  2.     SPI_InitTypeDef SPI_InitStructure;
    & z/ y; ]4 T% {
  3.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;       // SPI总线设置使用两条线,一条用于Rx,另一条用于Tx
    ( Q' r+ ~& H$ K( r
  4.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                // STM32是主服务,tlc5940作为从服务) f- M1 l9 G  |0 l- |
  5.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                             // 使用8位数据传输6 ?& b8 g/ c+ p# u/ e2 a( c1 Z
  6.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                                    // TLC5940时钟空闲时低
    4 }/ w3 T! {. H, \9 i0 L5 C
  7.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                                // TLC5940使用第一个时钟过渡作为“捕获边缘”2 N  ], c# n% v) \% D) G
  8.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                        // 软件slave-select操作
    . G% z' O1 q: ]+ \1 K
  9.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 设置预定标器
    * D7 f3 O% c% Y9 R* W9 t
  10.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                             // TLC5940数据先从MSB传输
    / O  u( v! T5 E% O5 l
  11.     SPI_InitStructure.SPI_CRCPolynomial = 0;                                          // 没有使用CRC
    / X6 e' ?; v8 T) C/ ~' e/ d

  12. 0 K) O( n* ?* u2 Y
  13.     SPI_Init(SPI2, &SPI_InitStructure);                                                    // 初始化SPI2外围设备
    " a1 v& b& R+ L8 D2 N: a
  14.     SPI_SSOutputCmd(SPI2, ENABLE);                                                 // 将SS Pin设置为输出(主模式)
    ) Z# Q; C: I/ i6 e
  15.     SPI_Cmd(SPI2, ENABLE);
    5 H  g' F4 N& Z5 E
复制代码
我选择的SPI时钟分频器是相当随意的,但这里的关键点是,我已经配置了时钟相位和极性,根据TLC5940数据表,所有传输都将是8位(稍后详细介绍)。; |$ @5 H) Q& Q5 O

1 M0 H: M. y# G3 j
  t; m7 ]7 D: j5 h; K
DMA
" b) v9 U! d4 U# V1 X1 h$ q) C9 v用于SPI传输的DMA模块如下:) g' P- e+ a1 F% U
  1. // 初始化用于SPI2_TX DMA访问的DMA1流4通道0
    $ i& w# H) A. k/ O1 h% g! }, ]

  2. , G& [1 k6 d$ c- e) m+ l) j% Y: Y
  3.     #define DISP_SCAN_DATA_CNT     (24 * 3 * 2)    // 初始化用于SPI2_TX DMA访问的DMA1流4通道0) \* a4 |: z7 e+ |8 x
  4.     volatile uint8_t dispData0[DISP_SCAN_DATA_CNT];( I# L9 Q" N4 q% q% F* t: O
  5.     volatile uint8_t dispData1[DISP_SCAN_DATA_CNT];
    8 h, C1 I: \  s3 `# \& \, A' ]
  6. 5 i: m4 d  c9 ~6 j7 R
  7.     DMA_InitTypeDef DMA_InitStructure;
    , [( J" z# M+ ^0 `/ S6 h

  8. . c3 [6 m! g0 t1 C: N9 F
  9.     DMA_InitStructure.DMA_Channel = DMA_Channel_0;                   // SPI2 Tx DMA是DMA1/Stream4/Channel0/ L5 I3 ], f: C: ^( V4 C- s
  10.     DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&(SPI2->DR);                // 设置SPI2 Tx
    & f2 }* ^* t8 i0 L& \
  11.     DMA_InitStructure.DMA_Memory0BaseAddr  = (uint32_t)&dispData0;                   // 设置内存位置$ ]2 w1 V1 w' F6 S8 }3 w4 x
  12.     DMA_InitStructure.DMA_DIR  = DMA_DIR_MemoryToPeripheral;                          // 从内存发送数据到外设的Tx寄存器
    8 Q5 @: \! f  |7 P" C6 c
  13.     DMA_InitStructure.DMA_BufferSize  = DISP_SCAN_DATA_CNT;                          // 定义要发送的字节数
    ( h! k) D! `- Y
  14.     DMA_InitStructure.DMA_PeripheralInc  = DMA_PeripheralInc_Disable;                  // 不要增加外围“内存”5 B+ v" x6 r  V# x$ ^
  15.     DMA_InitStructure.DMA_MemoryInc  = DMA_MemoryInc_Enable;                        // 增加内存位置/ [% V  g4 X5 r4 f* S, e/ Q, A
  16.     DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte;    // 字节大小内存传输  t1 L" a8 B3 s7 y
  17.     DMA_InitStructure.DMA_MemoryDataSize  = DMA_MemoryDataSize_Byte;         // 字节大小内存传输
    8 y0 ?* m- v% V4 T
  18.     DMA_InitStructure.DMA_Mode  = DMA_Mode_Normal;                           // 正常模式(非循环)
    / B5 [& k& T+ M( l! j& ?- p4 H
  19.     DMA_InitStructure.DMA_Priority  = DMA_Priority_High;                // 优先级高,以避免使FIFO饱和,因为我们是在直接模式
    * ^- Z/ h. p0 o% x( W7 h0 y
  20.     DMA_InitStructure.DMA_FIFOMode  = DMA_FIFOMode_Disable;                 // 操作在“直接模式”没有FIFO: C$ L* j3 g& s5 J' R) l4 ~8 b
  21.     DMA_Init(DMA1_Stream4, &DMA_InitStructure);
    3 a# C0 `+ J8 ]+ B
  22. 0 ^+ J2 M) f  S5 k
  23.     // 操作在“直接模式”没有FIFO/ I* M8 q- j: S$ G
  24.     DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE); 1 R  ~, Z; S0 f4 P6 V
复制代码
NVIC: v: l6 u" o. j9 d2 y
接下来,我已经为两个中断服务例程触发器配置了NVIC(嵌套矢量中断控制器):
) V; s' v: G4 n0 F5 q! C6 c
  1. // 初始化嵌套矢量中断控制器
      y: m& ]. N) Z9 \
  2.     NVIC_InitTypeDef NVIC_InitStructure;
    0 A7 b+ b/ E( m2 C* T3 h
  3. * |1 A: c8 a. T8 }
  4.     // 启用TIM4(BLANK)中断
    7 {1 z# h8 X  p! q: Q
  5.     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;5 {% b& i( e* C9 q! w5 S
  6.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;; @3 o8 H! [* ~0 U: g
  7.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    6 x) ~, I( v$ J4 e
  8.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    ; _" o" K' J7 C
  9.     NVIC_Init(&NVIC_InitStructure);
    " K% n, |: |8 L; J
  10. " }  E% R* m/ y9 Y$ y5 R
  11.     // 启用DMA1 Stream4 (SPI2_TX)中断9 d# R" |6 F% k
  12.     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;. o  \6 k/ v4 I% I
  13.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    9 u' i# ~+ v2 o5 _' z! E# D
  14.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    # R# S- i3 [) y6 a( T; T8 q
  15.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;8 B1 f8 D* O" ~& I
  16.     NVIC_Init(&NVIC_InitStructure);: H7 O" Z+ S3 V
复制代码
BLANK中断用于产生空白脉冲,初始化DMA传输,并在每个扫描周期后锁定先前传输的数据。) N) F5 o" W" h/ F" t
& D8 ~/ M  d& G8 U* |
0 I4 z4 T2 l" W( I
Timers
# }( e9 Y- [, M9 q7 @1 U4 R最后,定时器模块配置:
0 M! ^- \  Z& }1 \
  1.    #define TLC5940_GSCLK_COUNTS    256                   // GSCLK在BLANK 脉冲之间进行计数# N# p& t5 B# Z& H+ ~: ~; s
  2.    #define TLC5940_GSCLK_FREQ        1000000            // GSCLK频率
    & a  ]& p$ K! ]1 P9 q
  3.    #define TLC5940_BLANK_COUNT      50                     // 在切换到下一列之前,允许前一扫描列的正电源轨道关闭的填充
    4 W7 J. y0 H: ~3 f* A
  4.    #define TIM_APB1_FREQ                  84000000          // 内部时钟频率(CK_INT)' n- y3 |" k6 I. X  R  p
  5. 5 f& h5 z/ x- B! L8 R# d! N
  6. // 启动计时器模块' P+ }! C! c4 I" N2 b" W
  7.     TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;3 a6 q$ m' `, V4 a3 I7 ]2 ?
  8.     TIM_OCInitTypeDef TIM_OCInitStructure;7 ~3 Z, u6 q; z' z
  9. 5 x: O- I3 }. a# o2 f& r
  10.     // 反初始化计时器模块和初始化结构
    9 c' r# z& P5 _. n$ @3 z2 T
  11.     TIM_DeInit(TIM3);2 ^: q0 O3 b/ L- y- l
  12.     TIM_DeInit(TIM4);/ v8 G; h& A# p- k
  13.     TIM_TimeBaseStructInit(&TIM_BaseInitStructure);0 t; r! W3 @$ y( e2 b% K
  14.     TIM_OCStructInit(&TIM_OCInitStructure);4 B; w, q! t, u& G4 z/ @$ A6 }
  15. & Z1 g% \: T; n6 ^  @5 p7 m! F* {
  16.     // 设置TIM3来生成“主时钟”" J# f% v% {: p0 \/ ~+ Z) z( C
  17.     TIM_BaseInitStructure.TIM_Period = 1;
    # p1 J7 T# ?  p! c
  18.     TIM_BaseInitStructure.TIM_Prescaler = (uint16_t) (((TIM_APB1_FREQ / TLC5940_GSCLK_FREQ)/4) - 1);    // 请注意,4的除法因子是由OC1频率vs CK_INT频率引起的- P, v+ w" n) h2 ^
  19.     TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;* g9 R6 q' n1 x. K3 Y8 Q
  20.     TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;# L4 E& x4 V! W- M
  21.     TIM_TimeBaseInit(TIM3, &TIM_BaseInitStructure);3 w7 _  D6 t- _, g5 B0 h6 Q
  22.     // 配置通道1输出比较作为触发器输出(用于生成‘GSCLK’信号)- A2 x9 _# |* c
  23.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    4 f" e- \$ u2 d, U' t' W( r3 T2 Y$ e' G( k
  24.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    ; Y* h: ~$ d% `5 a3 ^
  25.     TIM_OCInitStructure.TIM_Pulse = 1;& m& l; v# J) i. h
  26.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    - {) R$ |" w3 M4 G
  27.     TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    6 B# r/ b4 h7 z7 U  P( ?1 r
  28.     TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    1 Y3 ]- r9 v) F* }, p

  29. + S, a, ?  z6 b% `; V2 O
  30.    // 为对称计数器设置TIM4基准,最大计数指定为“GSCLK计数”(实际上是TLC5940的灰度分辨率)
    ' L; Z- ]+ ]1 s' j! t
  31.     TIM_BaseInitStructure.TIM_Period = TLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT;                   // GSCLK溢出计数(对“阻塞”的空白信号额外加1)
    8 i; M$ a, M1 ~- M6 V' x3 a
  32.     TIM_BaseInitStructure.TIM_Prescaler = 0;( j0 T* }: q+ c( s& ^9 y2 n
  33.     TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
      t" d6 K* c3 Q9 F) Z. I, H
  34.     TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;( H, f( U8 @+ t: B$ [* {. {6 @0 O
  35.     TIM_TimeBaseInit(TIM4, &TIM_BaseInitStructure);
    * x2 I6 C  K+ I' p4 h
  36.     // 将Channel 1输出Compare配置为触发输出(TIM4用作时钟信号来生成'BLANK')
    ' H+ z. u; D+ o/ j
  37.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;: V' z, [3 `1 }6 H2 _3 {8 }: W5 ]
  38.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    ) |1 x/ v) O8 ]4 {4 D! N% _: D+ k
  39.     TIM_OCInitStructure.TIM_Pulse = TLC5940_BLANK_COUNT;
    & a5 o2 H( H( e- p
  40.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    1 n6 E5 X$ h) Z7 [
  41.     TIM_OC1Init(TIM4, &TIM_OCInitStructure);4 i. U  W9 @  r4 B8 L
  42.     TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);, N  h7 J% a5 R) v

  43. 7 R* n7 x& H% q) q+ h% ]& E
  44.     // 将TIM3配置为主计时器! n! C4 c2 w: u& e% s
  45.     TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);                 // TRGO与TIM3的更新绑定+ [! N+ E# g/ Z% }- h) C
  46.     TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);    // TIM3作为master启用
    ; |8 |) O' o1 @+ ?2 x
  47. % Q$ M( e' _" U1 m
  48.    // 将TIM4配置为从属! r, N  g& _8 M! O* ]
  49.     TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);                                     // 设置TIM4(从)触发TIM3(主)1 [. y( E/ T* d4 ]; [
  50.     TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_External1);                   // 使用主信号输入作为一个“外部时钟”1 m- |; B3 d7 a, N
  51. : j4 y+ E) d4 _; ~/ F) z9 W! D" _- f
  52.     // 将TIM4模块配置为在捕获/比较1个事件时中断(向上计数和向下计数都匹配)
    ' P+ g2 L2 \* _
  53.     TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);
    " o. x4 d* }/ i
  54. & Z' i- Y/ k' n! g' \
  55.     // 启用计时器3和4. K0 r' V) z3 ^9 P
  56.     TIM_Cmd(TIM4, ENABLE);) t: g# \+ u1 {- ?6 U9 {
  57.     TIM_Cmd(TIM3, ENABLE);, Z# |% y6 R7 s# v
  58. ————————————————2 t. e$ H4 e$ }& @, x
复制代码
在这里,定时器3被用作主时钟,在它的输出比较1线上产生GSCLK信号,和驱动定时器4被配置为一个中心对齐的PWM输出在OC1上。
' _7 Y7 \: n9 g- j5 W; vBLANK计数是有效的填充脉冲,允许:
, w% p' H5 C  g: ~, T1 Y( ?) Y1 Y# N4 d0 I* w$ W( t

! g( C* O& D4 n4 m- q7 K& k最小空白脉冲时间0 q8 r0 \7 n% K# v4 Q
XLAT和DMA传输触发" A( q- t- ?! l% ^1 n  k
MOSFET输出在之前的扫描列完全放电(我已经通过观察放电时间在我的示波器调谐)
$ ~4 T" L/ k% N6 C3 {$ K设置了GSCLK频率,并且在下降和上升的空白信号边缘之间的GSCLK脉冲数设置为256,因为我使用的是8位的颜色,而不是TLC5940芯片(12位)的全部功能。这意味着在空白脉冲之间将有256个GSCLK周期。7 j/ k: ^; f6 w, x: A. E+ w
外围设备现在已经完全配置好了,所以最后要做的是查看中断服务例程,并调查结果:/ p: ~" b3 S1 {& M7 W! s( d# P- |  e; c
' }/ {; M4 A) A
# H9 V% [  D- G- v5 h" e
ISRs0 i& H+ T5 N: H% ?# [; R
  1. #define DISP_SCAN_FREQ                 200                                                                                                                                                                           // 扫描信号的频率+ v% g3 O" R7 v4 q* W2 i) f* v
  2. #define DISP_BLANK_CYCLE_LIMIT    ((((TLC5940_GSCLK_FREQ / (TLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT)) / DISP_SCAN_FREQ) / 2) - 1)   // 扫描前要计数的BLANK循环数
    0 k, g( z5 Y7 G) Z9 E

  3. " x7 I& f# Z( ~7 Y: o5 k! B; c# _, x
  4.    void TIM4_IRQHandler(void)* h4 p) R  j6 A5 S. d- B0 u4 a
  5.     {
    + ^$ ^' G/ y+ [$ |* \
  6.         // TIM4 IRQ处理程序有几个任务:
    . W3 T, I) T8 A: B% {# Z
  7.         //    - 切换扫描信号; q" P3 U& d4 x! f" [( s
  8.        //    - 锁定新选择('扫描')列的先前传输的数据
    $ [" E% M6 t" u" U% ~
  9.         //    - 设置并启动SPI2 DMA流来传输下一列的数据) @7 G1 A& ]" }+ G. h
  10.         // 所有这些都应该在BLANK信号(TIM4 OC1)高的窗口内执行(而不是完整的SPI传输)。
    + T; ]: c% `7 J& Q% H2 w# C
  11. # p0 `* j$ F$ m3 r
  12.         // 检查所生成的中断是否为OC1更新: v6 Z  n2 b5 G: l$ y" R( |
  13.         if(TIM_GetFlagStatus(TIM4,TIM_IT_CC1)); w; u8 ?0 C8 ]- n3 W6 T9 o4 K
  14.         {
    ; ~" J4 c% N. u2 Y
  15.             // 清除TIM4 CC1中断位) p& @& J; ^  Z3 z* y) ^5 P
  16.             TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);2 [4 K" a+ @( q3 g. n5 Q" e" n
  17. ' Z# q: {' ^7 ]$ z$ y* D' }
  18.             // 仅在向下计数时执行事件(这确保在空白脉冲内触发XLAT脉冲、扫描更新和SPI传输)+ i3 o. c. T" L; a5 D
  19.             if(TIM4->CR1 & TIM_CR1_DIR)
    2 A8 ~* |, t/ v8 F. ]' F0 r
  20.             {
    4 w. y  a1 v0 U* H. h
  21.                 // 检查是否需要'SCAN'更新(XLAT脉冲,扫描切换,下一次传输触发)
    + d; e! V* K: f, R
  22.                 if(dispBlankCycleCnt++ >= DISP_BLANK_CYCLE_LIMIT)' p* z; M% {! s4 R
  23.                 {( g6 W$ d6 s$ L8 \+ c2 ?
  24.                     GPIO_SetBits(GPIO_XLAT_PORT, GPIO_XLAT_PIN);                  // 设置XLAT引脚3 Z2 M9 B2 u6 |: Q3 {7 O/ D+ L
  25.                     dispBlankCycleCnt = 0;                                        // 重置计数器
    3 X) h" z1 v1 s3 y4 z1 I! Q

  26. / @' h* d% a: ], O$ D- Z! U
  27.                     // 确定当前列,并相应地移动
    - _0 j3 z# E) t$ ]9 T; `
  28.                     if(dispCurrentCol), W' \7 s' b0 A/ {0 J3 e. @
  29.                     {
    * u- a/ u3 T0 k4 s! r
  30.                         dispCurrentCol = 0;                              // 更改为“0”列
    4 p6 {* v. E9 ]! W. {1 N
  31.                        GPIO_SetBits(GPIO_SCAN_PORT, GPIO_SCAN_PIN);      // 设置扫描引脚(注意列0是逻辑高位,列1是逻辑低位)
    $ P( T! H( m- j5 q* F( {
  32.                         DMA1_Stream4->M0AR = (uint32_t)&dispData1;       // Send *next*列的数据(由于当前列现在是'0',因此将发送(针对下一个循环)( W" M" ~4 |" A
  33.                    }
    ! E1 B1 r# Y! e% }
  34.                    else
    4 Y# r! l8 n. C9 |9 K( e! X+ `0 b
  35.                     {4 v" e5 a8 D; s  m! |0 B6 X: C& K
  36.                         dispCurrentCol = 1;                                    // 更改为“1”列3 y' t+ m. C  Y
  37.                         GPIO_ResetBits(GPIO_SCAN_PORT, GPIO_SCAN_PIN);      // 重置扫描引脚(注意列0是逻辑高位,列1是逻辑低位)
    3 x" |4 T% J7 j: B
  38.                         DMA1_Stream4->M0AR = (uint32_t)&dispData0;         // Send *next*列的数据(由于当前列现在是'1',因此发送了(对于下一个循环)" e8 t; J4 @- a  E- e. `
  39.                     }
    ) {8 K- U9 V% ~" n) s  P3 b# \5 c% |
  40. / e2 A7 z0 q' m+ ^9 a
  41.                     GPIO_ResetBits(GPIO_XLAT_PORT, GPIO_XLAT_PIN);             // 清除XLAT引脚$ A8 U8 m7 I+ g
  42. 7 R/ `0 n3 W6 E; F$ ^0 d9 I
  43.                     //Trigger the next transfer
    ) F0 S+ d" b% \% j- I6 u+ ?6 b! l
  44.                     SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);        // 启用DMA传输请求2 c& E1 t  x- Q, ^
  45.                     DMA_Cmd(DMA1_Stream4, ENABLE);                         // 启用分配给SPI2的DMA流
    3 w* p9 Y4 {, k& A! R
  46.                 }
    1 d/ l8 K) j1 Z% J6 `
  47.             }$ @7 C* \: N1 o; o* @6 Y; _9 W5 @) O
  48.         }" r! q6 f, }' d7 t- K8 Y
  49.     }9 \8 ~, b6 f+ N/ ^/ n2 S
  50. # _; {: w* v3 G; _# ?9 X/ p
  51.     void DMA1_Stream4_IRQHandler(void)
    ' e4 ?- Y5 a; o; f- F- L; C+ g
  52.     {
    ' {' x5 D9 R, a7 t
  53.         // 检查是否设置了传输完成中断标志! ^/ t; t8 N, d+ G
  54.         if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4) == SET)( \  C0 E4 X( x' e1 K
  55.         {4 h: M" r6 }2 {. ^  d5 j
  56.             // 清除DMA1 Stream4传输完成标志$ }/ z! n, ~$ s: R
  57.             DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);6 x8 V# x: |( s1 f+ S- e
  58.         }
    ! b; s* K/ i  [6 |: P
  59.     }2 j& y7 X2 L/ i9 e2 u0 m4 }' s
复制代码
DMA ISR目前没有被使用(我确实打算用它做一些无关的事情),但TIM4 ISR基本上控制了整个显示。# \1 `7 o- u& w; L+ U# i. a
空白脉冲的上升边缘(有效地)触发中断。在确定正确的ISR触发了事件之后,使用TIM_CR1_DIR位检查计数器是否正在减少计数。这确保了我们只在BLANK脉冲的上升边缘执行以下任务。5 r; k7 I; ?1 G% c: x
每当ISR运行时,我们增加一个计数器,如果这个计数器超过扫描显示所需的数量,我们就使用XLAT信号锁存前面的数据,切换扫描信号,并传输下一个数据(在disdata0[]或disdata1[]数组中找到)。* O4 A3 s) G$ j5 x# p  ]5 A2 ~! ~$ d
在dis_blank_cycle_limit中计算扫描前等待的空循环数,其中考虑到:- z- T. u' Y1 H* E; w, f

: b2 q* z$ n* F: m- e4 k
5 z  m0 I3 y2 l7 l$ m. B" @3 A
TLC5940_GSCLK_FREQ -灰度时钟频率
9 D' F& l  m  d+ K4 MTLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT——上升的空白边之间的GSCLK脉冲数- T- O  Y4 @5 K4 W8 r, P7 S% v
expec_scan_freq -我们希望扫描数组的频率(此处设置为200Hz)
& w, g5 f: N" B( V现在,更新disdatax[]数组中的数据将改变led上显示的内容。7 o, f9 n! O6 R$ ?: V# K
GSCLK频率为1MHz,扫描频率为200Hz,我没有明显的LED闪烁,即使我听到人们谈论使用>5MHz来避免它与他们的设置。3 o6 A( H7 U0 U% v/ ]4 O  {
% d# X8 W2 g+ s% P  x

- d2 Y1 a: h: p  DLogic Analysis
; b' [" [1 ?4 D9 J. O$ V/ Q我在STM32F407输出和TLC5940显示板输入之间附加了一个逻辑分析器,如下所示:1 W4 T2 @4 r, t$ r, S
20.png
现在我们知道GSCLK周期是预期的,我们可以研究空白时间来确定正在以8位分辨率记录的灰度数据。
+ K9 s0 \. u" \: z下降和上升空白边之间的时间是305.6 - 49.6 = 256us,这是预期的。: d4 ^$ S/ C) w. N+ k
我还调查了一个更接近的水平,以检查信号的相位是正确的2^8计数。) O8 N) V% W8 m) O& V" x
21.png
最后,检查扫描宽度,我们可以看到一个列启用了2.445ms。: z0 r) H! Y$ f
即扫描速率为409Hz;考虑到2。5us =不能被256us整除,这很好。% i. ^5 I% _" d* I6 w, B: R& P
上面的捕获还显示,当空白计数达到极限时,相关的ISR锁存前面的数据,7 m, x  d7 D7 W2 J
切换扫描行,然后触发SPI传输。
$ C! E; H8 U4 t% w3 s( H然后在锁定该数据之前计算所需的空白周期数(XLAT信号在2445ms光标所在的蓝色箭头右侧几乎不可见)。% u  B9 R& `3 b. s; w/ v5 k
  ~7 y$ A" g0 l! H# s
: I6 A# U# o$ X! J6 k  m4 O

6 e" f: J2 B0 C4 f9 o7 ]) m
收藏 评论0 发布时间:2021-10-12 13:31

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版