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

STM32F4 SPI DMA

[复制链接]
STMCU小助手 发布时间:2021-10-12 13:31
STM32F4 SPI DMA
自己整理(存储器到外设模式): D5 ?6 O/ ^" V/ ~5 ^
SPI结构体
4 s. T" {3 @3 U  F: s6 k& d; a8 H: h  H
9 {1 Y" s$ ]* |
  1. typedef struct
    ' R- s" _, U  }# D
  2. {& q8 n% I$ D; j/ x) z1 w! F
  3. uint16_t SPI_Direction; /*设置SPI 的单双向模式 */; O1 V$ }! G# v' x7 Y
  4. uint16_t SPI_Mode; /*设置SPI 的主/从机端模式 */. N1 P; u0 s; r$ P6 V3 |
  5. uint16_t SPI_DataSize; /*设置SPI 的数据帧长度,可选8/16 位 */2 x5 b1 ?0 A- ?$ d$ F; x* Q
  6. uint16_t SPI_CPOL; /*设置时钟极性CPOL,可选高/低电平*/
    & H5 d' y7 C4 c) a! l
  7. uint16_t SPI_CPHA; /*设置时钟相位,可选奇/偶数边沿采样 */
    6 Y; t( Q6 Z; v
  8. uint16_t SPI_NSS; /*设置NSS 引脚由SPI 硬件控制还是软件控制*/
    $ w/ _( y2 \- w% v4 ]
  9. uint16_t SPI_BaudRatePrescaler; /*设置时钟分频因子,fpclk/分频数=fSCK */
    5 B' N% d8 s& F4 m5 i" M* n2 r
  10. uint16_t SPI_FirstBit; /*设置MSB/LSB 先行 */: A2 d( {% `) e" J4 j/ t* y
  11. uint16_t SPI_CRCPolynomial; /*设置CRC 校验的表达式 */8 {/ \8 z9 c/ Q
  12. }SPI_InitTypeDef;
    0 v' m( \$ ]+ [6 Z; `
复制代码
SPI引脚编号
# S1 c) [0 m5 T
14.png
15.png
16.png
SPI配置7 y" j# U% i" b# l% ]
  1. void SPI_Config(void)1 n2 f0 B& |5 {( a+ d. `/ w
  2. {
    8 J  Z* I8 V/ A6 N: K
  3. GPIO_InitTypeDef GPIO_InitStructure;
    0 I: W. j$ v$ U& O* _& H6 z
  4. SPI_InitTypeDef SPI_InitStructure;. [9 M* M  L3 {

  5. & q( K3 K0 Y( d, S9 G
  6. //1.初始化GPIO
    7 q1 {0 C+ A1 @
  7. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB,ENABLE); 0 u3 u  ?1 K/ D( m) g6 C$ O4 ?5 K2 Z
  8. /* 连接 引脚源*/
    ! y8 v( b6 d3 V; Z7 g
  9. GPIO_PinAFConfig(GPIOA,GPIO_PinSource15,GPIO_AF_SPI1);9 E( P+ Z. x& T# r4 H. |' B
  10. GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); % Z3 _# \$ z! c# w% ?& k
  11. GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);0 A# L* B" z4 D% L+ p- }8 o& T0 |
  12. 5 o, k% @. j% M9 K, c' `3 x: y! h1 I
  13. /* 使能 SPI 时钟 */4 ]6 r: I4 V5 A- T1 V9 e
  14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); 8 l5 q/ X3 `3 R2 u, @: m
  15. / n/ m9 n) Z- e" F3 S% y' `
  16. /* GPIO初始化 */5 a) m( L4 _1 q+ a; J9 d
  17. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        " Q  n- ~; u# W3 I
  18. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 3 }/ X( ~5 _1 m
  19. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    % O" O$ R0 _9 L, ~6 a$ Z
  20. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;$ C/ u" H& _7 U0 o- C/ a
  21. , F$ y7 ^* l# Y# e
  22. /* 配置SCK引脚为复用功能 */
    ; R1 @5 z4 A. F- Z: W
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ;
    ( W3 ~* ^$ R0 D) Q! x( r' r
  24. GPIO_Init(GPIOB, &GPIO_InitStructure);
    ; h/ O6 S% i# u2 {( C

  25. 3 W9 y1 v9 v; S0 D1 M' }
  26. /* 配置MISO引脚为复用功能 */0 f/ ?+ }: A0 s" m
  27. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    - i7 P% q# P' U' x9 s5 j7 L' R
  28. GPIO_Init(GPIOB, &GPIO_InitStructure);) C5 c& s6 L; y4 {2 g5 W
  29. 6 j! o( `  X' H/ M' s8 o
  30. /* 配置MOSI引脚为复用功能 */% s7 [% f: Z! z
  31. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;3 q  j% N4 C8 j) _5 q: H
  32. GPIO_Init(GPIOA, &GPIO_InitStructure); , v! l' T- f% F9 W* [! J
  33. & N$ M8 D3 f" i
  34. /*CS引脚 */
    5 n) ?& ]2 T9 ~1 E/ D( W' q5 j3 J
  35. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    3 m% ~, K1 U" w3 Y3 A2 c
  36. 8 m9 u- B3 D3 S0 R: E1 g0 ~1 l
  37. /* 配置CS(NSS,自动控制SPI的片选信号)引脚为复用功能 */' z+ G& h; f9 ~/ r/ f/ y; M( Q$ L* y& S
  38. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 ;
    ' z, `0 P9 }: \5 P4 E
  39. GPIO_Init(GPIOA, &GPIO_InitStructure);
    / {: H) s4 ^( v1 O
  40. * n0 F7 l- w9 M7 d
  41. /* 停止信号: CS 引脚高电平 */
    6 Q# q( d! R+ e# @9 {5 O& K& ~/ Z- j  q
  42. GPIO_SetBits(GPIOA, GPIO_Pin_15);
    2 b% L7 S: l- b3 N4 d

  43. # v/ Q7 `0 V' C1 E) ?; h% l, w
  44. //2.配置SPI工作模式
    1 t+ M5 e& r$ R+ ?0 j( \# P
  45. // 分频
    0 Z8 ?9 n: E6 J# j! w& s; T
  46. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; # n" i* I4 f3 K* Y( \( l
  47. // 数据捕获于第二个时钟沿  ?2 k1 ]2 d1 B
  48. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; : \, ~! `' Q4 _
  49. // 时钟空闲idle时是低电平( _6 |$ f5 t3 _& u
  50. SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; 7 |5 a" Y3 }  w9 Q; a, H
  51. // 不需要使用CRC校验
    & U1 }! K( t& v2 v  l! C
  52. SPI_InitStructure.SPI_CRCPolynomial = 0; % W. [" e% ]+ ]! h. V0 t
  53. // 数据帧长度为8位( A3 J) ~! W0 v# P) d
  54. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; + _3 v# u3 j, `2 |
  55. // 双线全双工模式
    2 ?7 M; D3 a3 U- H, u; e$ Z1 c! v5 B
  56. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;3 @" c1 Z) C* h' [5 v; e% k
  57. //数据传输从 MSB 位开始
    8 H# K' l- C, k' v
  58. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; # \# W; \! U+ w- E3 ~! m/ K: A( o
  59. //NSS 信号由软件管理
    5 L, C4 f8 C2 p. N' q1 N* J/ r
  60. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;& j- E: d" L7 u" l  O) K7 j
  61. //设置为主设备  {' d7 u* C3 ?- o
  62. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       
    . c9 z+ w  N4 [, T$ t
  63. SPI_Init(SPI1,&SPI_InitStructure); . O, ^7 g  y4 ~1 B# _
  64. 3 B( n' E8 _: `" _
  65. /* 使能 FLASH_SPI */       
    8 p! D1 p5 T. K: {( m' W' N( b$ N# I
  66. SPI_Cmd(SPI1, ENABLE);
    # A- }9 Q1 G! B, r& X
  67. }! n/ E: Z8 k, m, j: n$ z+ J, k
复制代码
DMA结构体
$ X4 r9 [; a) w: V! ]
  1. typedef struct {9 \  W4 P+ c- a
  2.     uint32_t DMA_Channel;                                 //通道选择" o. ~# q, a6 O8 L9 B
  3.     uint32_t DMA_PeripheralBaseAddr;         //外设地址
    / P, h5 r* {1 p% d% l
  4.     uint32_t DMA_Memory0BaseAddr;                 //存储器0 地址
    0 R# c! R2 N7 I3 q5 O1 e  h- k7 }  s
  5.     uint32_t DMA_DIR;                                         //传输方向
    * [, ?; H! S) U) f* d
  6.     uint32_t DMA_BufferSize;                         //数据数目6 {9 j( k" L. f. K- H
  7.     uint32_t DMA_PeripheralInc;                 //外设递增3 A6 }" S, |) y3 W. |3 ?4 ~
  8.     uint32_t DMA_MemoryInc;                         //存储器递增
    . a; \: d" [9 N; w0 n3 r* B
  9.     uint32_t DMA_PeripheralDataSize;         //外设数据宽度
    ; x: z! ~" c' R9 q  d/ x- Y4 ~
  10.     uint32_t DMA_MemoryDataSize;                 //存储器数据宽度
    1 w* U8 F8 E. j6 r
  11.     uint32_t DMA_Mode;                                         //模式选择
    $ |6 r* P, I# |
  12.     uint32_t DMA_Priority;                                 //优先级
    & v# G( h" Z8 r/ G& o5 o8 m
  13.     uint32_t DMA_FIFOMode;                                 //FIFO 模式
    2 l; B7 U3 v" |. L& y" q
  14.     uint32_t DMA_FIFOThreshold;                 //FIFO 阈值
    6 a0 g% s6 }9 I% W
  15.     uint32_t DMA_MemoryBurst;                         //存储器突发传输  K' N; P0 A4 h9 I5 M! ~
  16.     uint32_t DMA_PeripheralBurst;                 //外设突发传输
    ! N" O, I" c6 T4 }6 K3 ^
  17. } DMA_InitTypeDef;; J4 {3 E0 i1 b8 D* a: u
复制代码

/ Q8 x; ^; Q& _- }; E  KDMA请求映射
7 R8 i9 m7 ]  _  V
17.png
18.png
DMA传输模式* Y9 P/ r* K; a4 ~# M
19.png
SPI 发送DMA配置
7 n3 R- z9 K& v/ E$ O0 S# v
  1. #define SENDBUFF_SIZE (1024*20)            // 一次发送的数据        ! y/ h5 u5 v6 e/ B
  2. uint8_t TX_Buff[SENDBUFF_SIZE];                // 发送缓存
    $ v. `- _8 J) K# Q6 ?
  3. void SPI2_TX_DMA_Config(void)# N& o* k3 f$ w4 v& D/ M
  4. {
    * P4 J4 ?, W" \& O
  5.     // 中断结构体
    + p, e- H" Z/ Y# w/ l- |
  6.     NVIC_InitTypeDef NVIC_InitStructure;               
    ( y, W% |2 v+ Z7 z, E
  7.     // DMA结构体; X0 R& z) h' N! ?
  8.     DMA_InitTypeDef DMA_InitStructure;                  7 }. B/ O# G0 L4 [+ g' i" d/ f
  9.     /* 使能DMA时钟 */                 
    5 Y/ I( l1 o1 {' G! a' x
  10.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);       
    ) ^( F: V7 `$ @. a
  11.     /* 复位初始化DMA数据流 */  
      }" L* D4 ]0 ^8 Z$ _! Q! T6 H# Y7 w
  12.     DMA_DeInit(DMA2_Stream5);                                                               
    : i( n6 t9 u  c6 I2 m( _
  13.     /* 确保DMA数据流复位完成 */  
    6 F' a2 G; }5 s, h( x
  14.     while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE);       
    4 [/ M/ S5 j0 c) Q" x! K

  15. ; [. L4 H3 w7 c7 h. c
  16.     /* 配置 DMA Stream */
    ! d( y0 O2 U, j: n
  17.     /* 通道3,数据流5 */          
    + @5 F# j: s' ?1 l0 J' F
  18.     DMA_InitStructure.DMA_Channel = DMA_Channel_3;; L& }9 B! K; X& g3 Z. k* o1 Q: W
  19.     /* 外设地址 */  
    , G$ o! C$ ~7 g& p/ T" e) h1 f
  20.     DMA_InitStructure.DMA_PeripheralBaseAddr = (SPI1_BASE+0x0C);        ) j0 G# P% i0 u2 o
  21.     /* 内存地址(要传输的变量的指针) ,DMA存储器0地址*/          6 R+ G$ V" x. `% w3 m; w. D; D
  22.     DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TX_Buff;        9 v& K! ]" D! R3 @8 p( H+ Z
  23.     /* 方向:存储器到外设 */                       
    & u4 `$ c# ^3 l8 J
  24.     DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;$ B- s) s0 g% X) R* C
  25.     /* 数据传输量 ,可设置为0, 实际发送时会重新设置*/            
    * r( y9 o" p) G9 p  b+ Q
  26.     DMA_InitStructure.DMA_BufferSize = (uint32_t)SENDBUFF_SIZE;               
    ! Z4 ~0 }: Z4 I% I) G& s
  27.     /* 外设非增量模式 */               
    8 n! N! `, w; ~( ?- g
  28.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;& q, Q6 [4 Z2 Q* T
  29.     /* 存储器增量模式 */          : v" I' f0 A  k9 L! n. m4 L
  30.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;7 `& A" j' H+ b) Z' M
  31.     /* 外设数据长度:8位 */         5 r% b( a5 G5 W' ~
  32.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    5 c$ n. ?3 ~& U; z
  33.     /* 内存数据长度:8位 */
    : [1 N& [! C  E3 d
  34.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    1 c. G- s) I" |- H
  35.     /* DMA模式:正常模式 */                  * n3 c  I8 H9 p' v# D- P
  36.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    * ]8 `0 V: `2 L9 D( S! W9 p
  37.     /* 优先级:高 */                         - o; ~1 p( `, Y+ O# w
  38.     DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    , {$ K5 ]  S" T
  39.     /* 禁用FIFO */
    3 h( y4 V7 @* @& S
  40.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                        # O$ S* `- b7 K, e7 V4 [! I
  41.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;   # J/ y1 h4 o/ M
  42.     /* 外设突发单次传输 */  
    6 O! a0 Q2 ]; Q' D, n3 w& ?
  43.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                    2 J4 z! R9 ]( k: a' h
  44.     /* 存储器突发单次传输 */  
    0 g  i. O5 |$ Q
  45.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 4 |* e) S' H% L1 e

  46. ) T* H, B9 D( _+ l0 s/ [
  47.     /* 初始化DMA Stream */               
    ' V% h$ L; O' a, B" X6 ^0 _
  48.     DMA_Init(DMA2_Stream5, &DMA_InitStructure);! M4 h" [! ]+ o5 o/ y& \2 u" q9 p
  49.     /* 开启传输完成中断  */               
    1 c( f" A/ E9 E3 l
  50.     DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);
    ' t3 w4 \8 @5 c* w& `
  51. 7 m$ P; {  ^8 E1 v
  52.     // 中断初始化
    3 d6 R9 V8 {; ^& H0 {2 ^
  53.     /* DMA发送中断源 */  
    8 a. T3 R+ {, v( m
  54.     NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream5_IRQn;        4 W- O9 f% M) b+ _" F. c* A
  55.     /* 抢断优先级 */  
    % N& u( @/ s; S5 \
  56.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;0 e! f" V" O8 R0 D1 `1 [$ Z
  57.     /* 响应优先级 */  9 D! Q0 H/ }+ L% W! z
  58.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                                ' x  L- F; r; ~0 e% Y2 ^; N7 e; z
  59.     /* 使能外部中断通道 */ 8 y' z" o1 K  l3 E! D6 R' z
  60.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                 - q" p) v* C0 G4 ~. N
  61.     /* 配置NVIC */                6 N5 _! O4 U: m! V4 `1 S
  62.     NVIC_Init(&NVIC_InitStructure);  b% M* u8 ~0 d! J" G7 w" D
  63. }( e8 @7 [4 j- ^0 {
复制代码
DMA发送中断服务函数
! F( C; S! i5 H/ e, i6 r( m" n
  1. void DMA2_Stream5_IRQHandler(void)
    ; U5 o, {# H- u) w& ^7 n
  2. {9 @  O; e. t" Y2 `
  3.     // DMA 发送完成0 @% O& S" d# b8 S6 i5 i9 ~
  4.     if(DMA_GetITStatus(DMA2_Stream5, DMA_IT_TCIF5))        & J% I) C& A" _" x; R& S! X+ j, V
  5.     {  K* L2 w$ s  b8 \2 I' m( l
  6.         // 清除DMA发送完成标志) e5 P! L0 f4 a0 }2 M7 H. t
  7.         DMA_ClearITPendingBit(DMA2_Stream5, DMA_IT_TCIF5);        * U( W4 `# [- g2 m4 k4 I
  8.         // 片选拉高,数据发送完毕        ( n4 \( I! x- @, _
  9.         GPIO_SetBits(GPIOA, GPIO_Pin_15);       
    1 L8 X/ S! O" [; l2 B% ~( {
  10.     }4 N% N. ~( O6 ^
  11. }
    ' h- I* a" G+ \( X4 f! L7 Q# o
复制代码
SPI 接收DMA 配置
* A& ]! |; j6 q" {8 V. N
  1. #define RECEIVE_SIZE                  800          // 接收大小. w: D  h; ?) O. I: ^# T
  2. uint8_t RX_Buff[RECEIVE_SIZE];                // 接收到缓存
    * F$ W: j3 k# l6 t0 I* D
  3. void SPI2_RX_DMA_Config(void)
    ( G1 Q% M3 V; q0 @) M
  4. {8 |3 g" G' Z+ L. M
  5.     // 中断结构体
    4 }) F' j4 w& k2 R$ }3 R
  6.     NVIC_InitTypeDef NVIC_InitStructure;        3 I! S' |/ K5 [" G% ]
  7.     // DMA结构体  & o4 S( h7 b% x  n8 j
  8.     DMA_InitTypeDef DMA_InitStructure;                & E) `9 `. u' _  R( W
  9.     /* 使能DMA时钟*/  2 S$ Z0 a3 E$ R2 G) @
  10.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);                                        /* 复位初始化DMA数据流 */
    % C2 ?( g/ _% s# U/ ^
  11.     DMA_DeInit(DMA2_Stream2);                                                                                                /* 确保DMA数据流复位完成 */
    $ x# @6 {7 x) R9 Y. h! {5 A) v
  12.     while(DMA_GetCmdStatus(DMA2_Stream2)!=DISABLE);1 a. ^# n+ D$ j, y% ]$ t

  13.   _  q* w2 L# ~6 ]
  14.     /* 配置 DMA Stream */6 |( Q- P3 O0 [3 U( l) i6 n! B2 J) ~9 F/ b
  15.     /* 通道3,数据流2*/          8 ?6 r' c" ?" L, A
  16.     DMA_InitStructure.DMA_Channel = DMA_Channel_3;                          : d1 D7 x  n; g* j1 y
  17.     /* 设置DMA源:串口数据寄存器地址*/  ; E; h7 e, P6 l8 g
  18.     DMA_InitStructure.DMA_PeripheralBaseAddr = (SPI1_BASE+0x0C)                ;
    1 T6 T% \2 W9 t4 M
  19.     /* 内存地址(要传输的变量的指针)*/  4 f7 A, ^7 R2 i9 W
  20.     DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RX_Buff;                        2 l2 ~4 W2 \3 e' w5 K5 i
  21.     /* 方向:存储器到外设模式 */                        - a6 R  f- w1 O
  22.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;4 S( Q0 a& b3 a5 ?' `* L
  23.     /* 数据传输量 ,需要最大可能接受的数据量[不能为0],实际发送时会重新设置*/            ?+ r  I5 H; c1 v# Y0 [" y. s
  24.     DMA_InitStructure.DMA_BufferSize = (uint32_t)RECEIVE_SIZE;" l8 W& ?  J" o4 q
  25.     /* 外设非增量模式 */          - d) Q/ R; F* o# _& [
  26.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;           
      B6 A+ k" k) N9 @0 t# d  b
  27.     /* 存储器增量模式 */   
    " d) D; \- T0 u* v) i
  28.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    ' M4 E! n. O% T+ c' m
  29.     /* 外设数据长度:8位 */          ) _5 d- O% u4 ?
  30.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;. l4 S& a4 L; i0 ?) a1 m
  31.     /* 内存数据长度:8位 */
    7 u& |! ~8 w0 G, h, C: L# \4 Z+ l
  32.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;               
    , i+ I5 F& ]/ ?- [/ R$ y4 b
  33.     /* DMA模式:正常模式 */  , U% a& e: f5 z; N$ D" v+ i
  34.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    % j7 I( E+ s' K: U% E
  35.     /* 优先级:高 */          
    ( b7 O! L; s5 A- N% x; h: A
  36.     DMA_InitStructure.DMA_Priority = DMA_Priority_High;! n8 f$ _( H. ]" @% u
  37.     /*禁用FIFO*/    ( I# z! G/ K& V3 K9 P1 M
  38.     DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;9 L$ I/ x' R3 d0 B; W
  39.     DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;; N5 M+ ]0 ^3 K- O4 x$ R
  40.     /* 外设突发单次传输 */  7 P7 i' o- j% E( Y$ t) k
  41.     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;- O! e( t' S" p( G+ m/ e4 c
  42.     /* 存储器突发单次传输 */  
    ; r. w. q) Y+ B) M
  43.     DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; $ V* Q: \% t; L) ]) R$ M0 a9 v1 s9 H
  44.     /* 初始化DMA Stream */                / m- J5 R0 |# M
  45.     DMA_Init(DMA2_Stream2, &DMA_InitStructure);                                                           $ E" {/ q3 c+ a, p. i% v) \$ x
  46.     /* 开启传输完成中断  */
    - T  T" |% F( H9 f. [
  47.     DMA_ITConfig(DMA2_Stream2,DMA_IT_TC,ENABLE);                                         
    + l1 S& [1 w# Q0 v3 S
  48. - |. S# P( H) q- P
  49.     // 中断初始化 , c1 J: s' q( `. r/ y# b
  50.     /* 配置 DMA接收为中断源 */
    8 V% t1 Z: V$ f* m8 }
  51.     NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;          . p  k* K3 C" L3 T
  52.     /* 抢断优先级 */  $ o4 B- v' q1 B! {: |* U# Q$ J
  53.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;                , e3 @8 V7 ]! f& v7 U9 u
  54.     /* 响应优先级 */  ! j) {8 ?7 Y6 }9 K+ P; |/ V9 q
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                               
    4 ?! n! L; W- ?( `5 z" a
  56.     /* 使能外部中断通道 */  
    ; W5 K$ u6 v3 L
  57.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    , s. M9 C' }3 p; L" |) K
  58.     /* 配置NVIC */        ' M3 U7 g! U1 D% H  l  y
  59.     NVIC_Init(&NVIC_InitStructure);
    & R+ n, a7 d3 ?* U" j. d
  60. }/ N% T; w7 q( y1 a  S
复制代码
DMA接收中断服务函数5 j; k! J! E/ n( `7 }
  1. void DMA2_Stream2_IRQHandler(void)" O. h9 ?9 a# [- ?! @/ D
  2. {                               
    $ k1 `, I9 B7 T- v
  3.     // DMA接收完成) S7 f; |$ {7 H5 s3 Q/ k2 V
  4.     if(DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2))        . l# o9 y8 \4 f- J8 q
  5.     {                * A5 p8 I. v1 M$ C% `
  6.         // 数据接收完成 拉高片选
    ; T0 D7 K+ Z5 {) {# R% S
  7.         GPIO_SetBits(GPIOA, GPIO_Pin_15);        : ^/ l4 D: V- M5 Z
  8.         // 清除DMA接收完成标志位                % N; G' ~( X8 j8 J. a
  9.         DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);       
    % F; N  l6 x4 D/ X+ H
  10.     }
    . K( p0 d6 W# L- b  ]
  11. }
    0 G/ `9 T5 O: t. o9 A
复制代码
DMA请求使能, s1 I* W( o/ _' X$ U+ \/ P( J
  1. //SPI2 TX DMA请求使能# e% ]  x/ N7 s! O, q# \0 r7 A) P
  2. SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);        # H, s  H1 |- r! z- P/ i
  3. //SPI2 RX DMA请求使能6 g9 {' V. k# j5 W2 ?1 `0 B
  4. SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);       
复制代码
SPI_DMA 读写一个buf
. ~* F, U+ P4 c
  1. #define BufSize                800
    : @; ~7 l# I! w7 |+ Q( m
  2. void SPI_DMA_WRITE_READ_BUF(void) . d" f* {6 E  ~+ v1 E% z! q
  3. {   
    & X$ X( \" _5 j5 k
  4.     // 关闭发送 DMA        - h0 j# `7 m7 G5 R, s
  5.     DMA_Cmd(DMA2_Stream5, DISABLE);       
    $ P- L7 \! O$ N9 A
  6.     // 关闭接收 DMA        " k9 J# g- P) x& J; V: a
  7.     DMA_Cmd(DMA2_Stream2, DISABLE);
    / S7 D% b( p& ~# ^
  8.     // 设置发送的数据量
    % H( [! \1 e7 b* L2 p3 p; l
  9.     DMA_SetCurrDataCounter(DMA2_Stream5, BufSize);        8 s4 `& y5 P. X2 f6 V
  10.     // 设置接收的数据量. Y/ m1 s2 ~4 n+ G
  11.     DMA_SetCurrDataCounter(DMA2_Stream2, BufSize);" _; u0 Y+ E! V( _: r
  12.         // 清空数据7 z) e$ W1 T+ T* c2 [' ^
  13.     SPI1->DR;                                  ! E$ v! C  K7 z
  14.     // 擦除DMA标志位* {$ \% ?* X  e5 M7 ?
  15.     DMA_ClearFlag(DMA2_Stream5, DMA_IT_TCIF5);        0 W8 x9 {, U  @, t0 z" j' ]' w
  16.     DMA_ClearFlag(DMA2_Stream2, DMA_IT_TCIF5);- T  h. \: ?0 ~/ C
  17. + y) k$ V4 x" d9 W- Z) l
  18.     // 片选拉低,接收数据: J& `6 [1 }2 }4 x
  19.     GPIO_ResetBits(GPIOA, GPIO_Pin_15);        2 |! R4 x$ y: |5 A& B2 }
  20.     // 开启接收 DMA
    4 i3 ?# ], E4 Z& o1 Q/ l
  21.     DMA_Cmd(DMA2_Stream5, ENABLE);       
    3 v3 b, |$ L/ w7 f2 }, @- b  T
  22.     DMA_Cmd(DMA2_Stream2, ENABLE);5 @+ i7 ?) `; n  j& N3 N+ P, ^
  23. }
    ( e1 D6 E7 K0 s2 Y' _
复制代码
SPI_DMA 发送一个buf* I) C6 s1 k: x' A
  1. void DMA_Write_buf(uint32_t SizeLen)* G( a1 B$ |$ o, e3 u! ?
  2. {   9 i' Z9 R3 v: ?" M2 \
  3.     // 关闭发送 DMA                $ I) N+ Q' I* v  r4 E
  4.     DMA_Cmd(DMA2_Stream5, DISABLE);       
    # p; S& ~1 t  N7 q8 Q# O, X6 N+ c
  5.     // 设置发送的数据量   
    - H- C) N1 i( [3 M0 o2 e3 |9 M
  6.     DMA_SetCurrDataCounter(DMA2_Stream5,SizeLen);
    : ]; |, h0 r7 J3 e% V5 y7 R
  7.     // 清空数据
    - C! Q8 a. Q. ]% w% a1 t
  8.     SPI1->DR;                0 ]1 |, F. e3 d$ T' ~2 E
  9.     // 擦除DMA标志位       
    $ {0 V. i; @, _  Q7 {/ a
  10.     DMA_ClearFlag(DMA2_Stream5,DMA_IT_TCIF5);
    7 Z% X# t* g4 B8 P
  11.     // 片选拉低,接收数据0 C7 |' R. c( `
  12.     GPIO_ResetBits(GPIOA, GPIO_Pin_15);        - [- f$ ~9 E: r* M2 c7 o# U
  13.     // 开启发送 DMA
    ; v9 i0 D3 N% u' J1 B6 V' Q, i( i) V8 x
  14.     DMA_Cmd(DMA2_Stream5, ENABLE);       
    - \. `5 @7 }) p
  15. }
      S4 G3 A( }1 \; H9 i  i8 j
复制代码
张贴的代码显示如何配置我的GPIO,定时器,SPI, DMA和NVIC模块,以及一些系统如何工作的解释。  Y$ a" L3 n! i- \+ _' }
注意,我使用的是STM32F4标准外设库。  f2 b0 p% Q8 F, n& o: |( {' w8 o
0 K7 L6 e: \& O5 |' r# {

  ]  P2 b9 `5 H% J6 B( |网上内容
( @, C' E2 x0 X# U* @通过RCC(复位和时钟控制)模块使时钟信号到达所需模块:
8 h  Y% g) Y& G9 Y- z! s0 C1 X
& V# O; B8 V  }7 ]

: n" N2 F; |" v' ?& Q! P: t% iRCC0 E4 R3 B5 D$ }4 G- L6 Q
  1. // 为所需模块配置时钟
    $ s: |5 g: I0 h
  2.     // 启用GPIO外围时钟
    + k- I6 e; n( r4 \8 ]- T: E' @+ V
  3.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    ) \2 A7 ]- E6 k* }/ Z5 Q
  4.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    ( U" l' Q: [1 [9 \) b
  5.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    , k) b" O6 k7 E0 u) v2 k7 M
  6.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    ; G/ H* m* F" K8 S1 {
  7. ; t  K2 C' H5 O  {+ q
  8.     // 启用串行外围接口外围时钟
    " F) \7 ~, T0 C8 H  r/ {
  9.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);/ [" E) w( `; G, v. v' Y  h9 _
  10. 7 f3 A) \( d. n- i' H; n
  11.     // 启用直接内存访问外围时钟2 k% h+ f: ~2 ^: e- g0 U0 {
  12.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);. D# e- O7 j# o% w7 v& u# F4 j! G

  13. % Q- L8 x' J7 ^
  14.    // 启用计时器外围时钟' _0 E7 ]9 |4 |* w9 i' j" K
  15.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    3 M6 F3 p, `. e6 n+ `0 {
  16.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    * C) i/ H5 I* ]
复制代码
GPIO
" A0 X# j8 U2 `: n接下来,配置所需的GPIO引脚:
' n9 X7 O5 K, K9 S( R  f# o
  1. #define GPIO_SCAN_PORT            GPIOB0 U; d. B  r+ i5 @8 i
  2.   #define GPIO_SCAN_PIN                GPIO_Pin_7% _# ]* a) {  P- w1 }4 t( R) S
  3.   #define GPIO_XLAT_PORT              GPIOA( [' z( O: }# t0 |. O
  4.   #define GPIO_XLAT_PIN                 GPIO_Pin_5' @, O  ?+ W" |8 l7 n" R
  5.   #define GPIO_BLANK_PORT           GPIOB7 z2 u* N" E3 @# I+ n
  6.   #define GPIO_BLANK_PIN              GPIO_Pin_63 ]+ ^) V' C) X/ n7 \, `/ t# {

  7.   H0 j* I6 e+ d: l" {9 P. ^
  8. // 配置GPIO引脚
      w0 G; d7 g8 F$ Y1 I
  9.     GPIO_InitTypeDef GPIO_InitStructure;4 g- S* T8 I1 r' {  [: p

  10. 0 I& s$ T4 \- z* v$ F9 x
  11.     // Timer3&4 输出 (TLC5940 GSCLK and BLANK); ?  J+ }3 Q# w- c% q
  12.     GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_4 | GPIO_Pin_6);
    ! f7 F/ ?0 @6 R8 L2 O6 g
  13.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;" [' c. j5 S2 r4 ~  q& J# w0 y
  14.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    1 N0 u; s) T" K5 ~( E- [' y% e+ j
  15.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;9 m$ N$ |  w9 E
  16.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;/ X6 A3 b, k0 C, H7 H. f, O
  17.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    & C8 o8 R5 J: d$ i
  18.     // 连接计时器到GPIO引脚/ P; C4 g) N0 d3 O4 S, u5 S
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);   // 将TIM3 OC1输出连接到PortB Pin4 (GSCLK)! q( m  h- |1 ~% Y6 g
  20.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);   // 将TIM4 OC1输出连接到PortB Pin6(BLANK)
    6 g& i( E- f7 b  d4 U
  21. ' C- N% m2 ?0 M6 Y
  22.     // TLC5940 XLAT 引脚* H  s$ P) ?4 ~+ ~- {1 i) _$ I2 s) h( Z
  23.     GPIO_InitStructure.GPIO_Pin = GPIO_XLAT_PIN;+ V$ H" C% M- E/ U+ ^
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;$ N: X0 J2 B6 r' G- \5 |- [
  25.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;4 e& J! i; D1 n( Y8 f$ o3 }
  26.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;5 f* p- Y# w* t; @, |# X+ x" W& b1 ~
  27.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;( F% ]6 J- Y- j( H4 a( D; Y
  28.     GPIO_Init(GPIO_XLAT_PORT, &GPIO_InitStructure);
    ! ]  j* ]5 b6 f0 p

  29. : A. `7 U: ^* F) ?" S
  30.     // 显示扫描 引脚2 |0 H% G1 b9 Z# v/ T8 u; K0 t
  31.     GPIO_InitStructure.GPIO_Pin = GPIO_SCAN_PIN;6 }% {% Y  m, U3 W' T7 Q! h. U
  32.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
      Z* V4 w) M, F' _+ w+ R4 X
  33.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    $ i/ N2 S; H1 d/ o# L
  34.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    ! \* b* Q0 K0 h/ Q5 `) c0 B. p
  35.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    ; `' i8 Y0 p* l# _4 e0 u+ o
  36.     GPIO_Init(GPIO_SCAN_PORT, &GPIO_InitStructure);8 b* R' r: R4 f
  37. 5 l1 k( @1 ~% u, e: c1 e
  38.     // SPI2 引脚& \4 U$ ^) a! {1 u; z+ |
  39.     //    SCLK =    PB10
    , L3 r6 |: f  H3 N+ P
  40.     //    NSS =     PB91 p' }& C8 \8 Y
  41.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    % ^- x8 m4 _/ A* _1 g" g9 Z$ m5 w
  42.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;8 d8 k( a; ^( A2 U
  43.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;4 c9 |+ k, t& v( ]# \" \
  44.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    * z0 |! P# a8 ^) ]4 Z; H- L! z
  45.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;6 N5 u6 t; ]( X1 n$ h2 |
  46.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    0 w/ `: l) N2 U- E7 w/ E3 [& B
  47.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_SPI2);. @0 p5 E# M' E. `$ H; O
  48.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2);
    , C0 |& G: Z: c, `4 }
  49. 1 p* P' U- o$ ~" j, ]
  50.     //    MISO =    PC21 z* `* b" K! d/ R1 v5 W# C4 R
  51.     //    MOSI =    PC3& s' X7 L' X" G9 W2 r
  52.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
    & _2 a+ a8 u" r. |# o3 f7 U. \1 s& S
  53.     GPIO_Init(GPIOC, &GPIO_InitStructure);
    . J0 l+ |2 k8 f, W4 h
  54.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_SPI2);* ?3 g( t9 Z& _* |" J# E0 g
  55.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2); ( |9 P% o' [6 N1 k' @
  56. ————————————————
    * J- k4 d4 V# o& m1 X0 ^7 l: ?4 }
复制代码
这里的要点是,我将把TIM3的OC1输出直接连接到GPIO引脚(用于GSCLK),把TIM4的OC1输出连接到BLANK信号。
" r9 G' V( e# s: o9 S: O
$ L" V$ I, [$ T% O- a  h: B
. @7 r: B3 @% k3 Q+ W, g1 A) M2 J) N
SPI2 ?/ ]1 q$ m" @' f  r* n
现在SPI模块可以初始化:
: V+ t/ S* w2 U- Y
  1. // 初始化SPI模块% Y* t3 B% m  h1 M2 l! e6 F9 T
  2.     SPI_InitTypeDef SPI_InitStructure;
    : U9 U4 f- z7 c' Q( N) A7 G6 K
  3.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;       // SPI总线设置使用两条线,一条用于Rx,另一条用于Tx
    * L1 W! {* q" b  Q. c
  4.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                // STM32是主服务,tlc5940作为从服务
    : O  F# v$ v8 Z: m' v0 }
  5.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                             // 使用8位数据传输/ b. k* x7 T4 v$ Z
  6.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                                    // TLC5940时钟空闲时低% I. T. G7 Q$ C6 K
  7.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                                // TLC5940使用第一个时钟过渡作为“捕获边缘”; a& a' S. J. f4 j% e
  8.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                        // 软件slave-select操作6 h. F7 d) K2 ^/ P' B+ r
  9.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 设置预定标器
    8 E: `: e0 L6 B: B2 d, ]
  10.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                             // TLC5940数据先从MSB传输0 h2 E0 Z' `0 C7 J7 a0 d
  11.     SPI_InitStructure.SPI_CRCPolynomial = 0;                                          // 没有使用CRC
    ( l# Y+ w7 N: Q$ S
  12. 6 h4 {: T6 g' M6 y
  13.     SPI_Init(SPI2, &SPI_InitStructure);                                                    // 初始化SPI2外围设备4 B7 h) q3 t0 n& r, O0 V# E
  14.     SPI_SSOutputCmd(SPI2, ENABLE);                                                 // 将SS Pin设置为输出(主模式)
    7 j! ?, b' L1 H" ]2 u5 s& U6 F
  15.     SPI_Cmd(SPI2, ENABLE); 6 E) B# L  l  P& p- W# v
复制代码
我选择的SPI时钟分频器是相当随意的,但这里的关键点是,我已经配置了时钟相位和极性,根据TLC5940数据表,所有传输都将是8位(稍后详细介绍)。, z5 q, B" I* f- h0 Y
/ a' I1 m2 }; @& h6 x* L: _

- \( s% W' c% D* dDMA
/ a7 c! ~7 _* H8 D; H& |用于SPI传输的DMA模块如下:4 Q  g, A; V1 f* q  g2 E1 R: V6 V
  1. // 初始化用于SPI2_TX DMA访问的DMA1流4通道07 I$ _6 t. ~* _
  2. ( N1 @; m$ n) Z3 h- W" G0 f6 d
  3.     #define DISP_SCAN_DATA_CNT     (24 * 3 * 2)    // 初始化用于SPI2_TX DMA访问的DMA1流4通道02 Z. ?5 v4 F* d, w0 M# `. z* Z
  4.     volatile uint8_t dispData0[DISP_SCAN_DATA_CNT];* \8 P& v/ @) [; I/ ?" k! N
  5.     volatile uint8_t dispData1[DISP_SCAN_DATA_CNT]; ( L9 n+ Y2 D* A- }* [
  6. & A9 C: h) F: t/ l
  7.     DMA_InitTypeDef DMA_InitStructure;* K, U: ?2 E. V8 E7 b& I7 u

  8.   W8 I# h$ Y8 h/ k! L; _
  9.     DMA_InitStructure.DMA_Channel = DMA_Channel_0;                   // SPI2 Tx DMA是DMA1/Stream4/Channel0
    ( o' @0 x- y9 j
  10.     DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&(SPI2->DR);                // 设置SPI2 Tx  Z; U0 m( {$ o+ u0 k: {2 ^# I  E
  11.     DMA_InitStructure.DMA_Memory0BaseAddr  = (uint32_t)&dispData0;                   // 设置内存位置
    : B9 E  v- u$ Z+ n: L
  12.     DMA_InitStructure.DMA_DIR  = DMA_DIR_MemoryToPeripheral;                          // 从内存发送数据到外设的Tx寄存器
    # w6 C! R% P% N) l: ]
  13.     DMA_InitStructure.DMA_BufferSize  = DISP_SCAN_DATA_CNT;                          // 定义要发送的字节数
    + c3 D, `# i/ N4 a
  14.     DMA_InitStructure.DMA_PeripheralInc  = DMA_PeripheralInc_Disable;                  // 不要增加外围“内存”
    % g4 ~9 ?7 }: I
  15.     DMA_InitStructure.DMA_MemoryInc  = DMA_MemoryInc_Enable;                        // 增加内存位置
    % h5 v- q% |; \7 M+ h
  16.     DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte;    // 字节大小内存传输
    $ i. C; r& A# U
  17.     DMA_InitStructure.DMA_MemoryDataSize  = DMA_MemoryDataSize_Byte;         // 字节大小内存传输% |! m0 p3 ~8 \( L7 L6 X
  18.     DMA_InitStructure.DMA_Mode  = DMA_Mode_Normal;                           // 正常模式(非循环)
    8 S2 Z* H& ^; R3 i1 n) g) f/ Z" ]
  19.     DMA_InitStructure.DMA_Priority  = DMA_Priority_High;                // 优先级高,以避免使FIFO饱和,因为我们是在直接模式
    ! H( p$ S! C1 y* W, @
  20.     DMA_InitStructure.DMA_FIFOMode  = DMA_FIFOMode_Disable;                 // 操作在“直接模式”没有FIFO
    ' i% h! s$ d0 F$ w
  21.     DMA_Init(DMA1_Stream4, &DMA_InitStructure);
    % F  N4 f& I: [+ o+ X4 z- }: s3 m

  22. 8 T) Y) m3 m# }5 \
  23.     // 操作在“直接模式”没有FIFO
    " B8 `' X( }, \& }' H% t) l
  24.     DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE); 9 ?1 o# Q, Z4 q1 U- H5 S$ v
复制代码
NVIC6 N& l- k0 z% `
接下来,我已经为两个中断服务例程触发器配置了NVIC(嵌套矢量中断控制器):
0 z) a* R9 X6 M4 [3 H
  1. // 初始化嵌套矢量中断控制器# M; c5 m4 a. z% k' H  U! F) T
  2.     NVIC_InitTypeDef NVIC_InitStructure;! H2 p# }9 s( ]+ y
  3. , T2 M4 A) i9 e: T5 X4 W$ y6 c
  4.     // 启用TIM4(BLANK)中断
    ; y/ y3 z% l' h
  5.     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    1 }5 x/ C. v& e0 K
  6.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;' Y% U+ F! v; {2 m6 r- M# Q; ?
  7.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;) n: c' y/ g2 ^) @5 b" K* @
  8.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    1 Z& H6 I( e9 ?2 s
  9.     NVIC_Init(&NVIC_InitStructure);# y' @4 G$ a8 g
  10. 7 m) k- c! {) ]) D& B1 a
  11.     // 启用DMA1 Stream4 (SPI2_TX)中断
    6 H8 A3 \7 S4 u6 h9 G4 {
  12.     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;
    7 \& k% M" |& z3 r
  13.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;6 g" a# W9 F' R( [) D
  14.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    * ?5 r# \1 U3 z6 s9 @* r( |
  15.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    : C' T3 c  w/ v( _0 |% c
  16.     NVIC_Init(&NVIC_InitStructure);
    / K" U( n! r& x; ~2 Z* ]& s1 T$ N
复制代码
BLANK中断用于产生空白脉冲,初始化DMA传输,并在每个扫描周期后锁定先前传输的数据。: c* q2 a' Y5 S# _! h5 @- l; j

2 M9 n  E6 _7 g& d, j' V
4 K5 K% t0 ?* V7 o) l( w' U2 Y. G6 s
Timers0 {5 Z3 W/ Q. N3 W0 d8 M
最后,定时器模块配置:
2 A! D) K+ x. O% Z2 d
  1.    #define TLC5940_GSCLK_COUNTS    256                   // GSCLK在BLANK 脉冲之间进行计数
    $ `& a% _, U% n# g/ n
  2.    #define TLC5940_GSCLK_FREQ        1000000            // GSCLK频率" w7 M. a& R/ v' h
  3.    #define TLC5940_BLANK_COUNT      50                     // 在切换到下一列之前,允许前一扫描列的正电源轨道关闭的填充! _  C. g' {, E
  4.    #define TIM_APB1_FREQ                  84000000          // 内部时钟频率(CK_INT)$ w: I8 B( @3 z& [5 {

  5. # g' Z* {$ A( i* C  w' j; j# t7 ^- A
  6. // 启动计时器模块% G+ c, C7 W! l5 u
  7.     TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
    9 E  I1 q# T/ L* Y- K) F2 n8 n
  8.     TIM_OCInitTypeDef TIM_OCInitStructure;
    8 G/ w7 _) ^$ I3 D
  9. - ?& f; x3 ~) K' A+ w
  10.     // 反初始化计时器模块和初始化结构
      e5 d8 S! a* l: @4 C* Z( b
  11.     TIM_DeInit(TIM3);
    ' I( ]6 {. e/ {* _) L$ K4 }
  12.     TIM_DeInit(TIM4);
    / ]' c" ^  M( F( O1 E$ S
  13.     TIM_TimeBaseStructInit(&TIM_BaseInitStructure);! t9 E. a4 Q% o/ v0 `
  14.     TIM_OCStructInit(&TIM_OCInitStructure);1 y& V; g2 s7 i! \: p
  15. & v. \2 w6 v3 S) _/ i5 Y- o. }( o
  16.     // 设置TIM3来生成“主时钟”8 ]/ s* Z8 R5 Q! S* |! T
  17.     TIM_BaseInitStructure.TIM_Period = 1;8 a  k* N" K3 L2 D) x( W9 |. u
  18.     TIM_BaseInitStructure.TIM_Prescaler = (uint16_t) (((TIM_APB1_FREQ / TLC5940_GSCLK_FREQ)/4) - 1);    // 请注意,4的除法因子是由OC1频率vs CK_INT频率引起的/ g7 y6 r5 n- x: I; i; E4 A
  19.     TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    / J1 x, B8 S/ o* Y. m" z
  20.     TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    8 e/ S4 E; I3 ]
  21.     TIM_TimeBaseInit(TIM3, &TIM_BaseInitStructure);
      d8 D. q9 u7 Q4 ^4 N* K
  22.     // 配置通道1输出比较作为触发器输出(用于生成‘GSCLK’信号)
    + O3 u1 M* Y0 x
  23.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    & y. z( w, h6 z
  24.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    " c  V8 D' R" u* I/ }2 \
  25.     TIM_OCInitStructure.TIM_Pulse = 1;
      W" z8 [  }% v* |
  26.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;0 R  J4 M, `' J$ P. k/ |' k( E
  27.     TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    " D3 {& I5 v% U: Z( T
  28.     TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);; [  k, Z5 D  j3 s$ V4 j" Y  m. m
  29. : |2 Y. _9 H& l0 {
  30.    // 为对称计数器设置TIM4基准,最大计数指定为“GSCLK计数”(实际上是TLC5940的灰度分辨率)
    7 `4 }/ s& H$ D; ]; |- h/ I0 x
  31.     TIM_BaseInitStructure.TIM_Period = TLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT;                   // GSCLK溢出计数(对“阻塞”的空白信号额外加1)2 w8 x6 k. }2 @6 S3 M/ Y9 _
  32.     TIM_BaseInitStructure.TIM_Prescaler = 0;
    % |9 i+ v: }% R
  33.     TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    - B# A& B4 F0 D$ C+ x( X# {( c
  34.     TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;( r7 g: L9 s# h! _; X+ \
  35.     TIM_TimeBaseInit(TIM4, &TIM_BaseInitStructure);. M/ B9 H( H( i# R) o
  36.     // 将Channel 1输出Compare配置为触发输出(TIM4用作时钟信号来生成'BLANK')
    / I9 @( _8 P" p0 r7 b( g$ D" a5 |
  37.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    / b; ], t/ D1 ~8 a: F) F0 E
  38.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    , b/ G# I  G4 t
  39.     TIM_OCInitStructure.TIM_Pulse = TLC5940_BLANK_COUNT;3 d. M; ?& ?& G7 N  x6 x- u
  40.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    7 ]; Y2 \  }4 s
  41.     TIM_OC1Init(TIM4, &TIM_OCInitStructure);
    / ^+ q4 p4 e& v
  42.     TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);, G/ i: J3 X4 `8 Y. Y3 r
  43. : L1 l0 ^/ _: p: |: z
  44.     // 将TIM3配置为主计时器
    5 k0 X- R4 F+ \3 t
  45.     TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);                 // TRGO与TIM3的更新绑定7 S7 u+ p5 V+ O% A
  46.     TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);    // TIM3作为master启用
    " M8 B. D0 W$ `5 Q

  47. 6 @  q/ u' v- J- E1 B
  48.    // 将TIM4配置为从属* e6 y1 }' E/ Q0 U; I5 _& q
  49.     TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);                                     // 设置TIM4(从)触发TIM3(主)
    , ^9 U% l$ k2 M! m. R
  50.     TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_External1);                   // 使用主信号输入作为一个“外部时钟”: v5 e0 a1 Y) {5 p' T# b

  51. 9 b& W; l# X( O: p& m/ E  \4 J6 K
  52.     // 将TIM4模块配置为在捕获/比较1个事件时中断(向上计数和向下计数都匹配)
    2 N1 D# S' q8 l* N7 {" k# b0 C
  53.     TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);
    ! R# k4 W# \( n1 O" e
  54. 9 @2 W0 y& q# \$ Z' j
  55.     // 启用计时器3和46 Z! ^5 `, `6 |
  56.     TIM_Cmd(TIM4, ENABLE);9 ~3 v3 @! B; Q& U0 x
  57.     TIM_Cmd(TIM3, ENABLE);
    : v7 l5 \1 }' ~" k$ R
  58. ————————————————
    & M8 ?- t2 u4 Y8 y7 }5 {
复制代码
在这里,定时器3被用作主时钟,在它的输出比较1线上产生GSCLK信号,和驱动定时器4被配置为一个中心对齐的PWM输出在OC1上。$ ^& T3 N$ X; I$ K8 v7 ]
BLANK计数是有效的填充脉冲,允许:  f7 q8 ^: m3 B. n+ ^, ]9 j

( I/ y( c- o1 I8 r: r1 w
% z$ e, o6 e, S: Q/ l" S
最小空白脉冲时间
. Z) W4 A. j/ V$ f7 o$ B- XXLAT和DMA传输触发
3 G5 h- `# e, j! V4 i, O) yMOSFET输出在之前的扫描列完全放电(我已经通过观察放电时间在我的示波器调谐)1 Q* r7 P$ n# Q0 F9 |% x/ g- l
设置了GSCLK频率,并且在下降和上升的空白信号边缘之间的GSCLK脉冲数设置为256,因为我使用的是8位的颜色,而不是TLC5940芯片(12位)的全部功能。这意味着在空白脉冲之间将有256个GSCLK周期。8 @. u+ d, v5 Y7 B% F
外围设备现在已经完全配置好了,所以最后要做的是查看中断服务例程,并调查结果:
" }+ f0 T) d  M0 G
9 I% q! w- r* a  P$ b9 n0 u, `
/ p; ~4 R8 I8 p! @- `9 ^
ISRs
8 T3 L7 p- a2 a: W4 q
  1. #define DISP_SCAN_FREQ                 200                                                                                                                                                                           // 扫描信号的频率
    # H2 i; u6 m0 {. t
  2. #define DISP_BLANK_CYCLE_LIMIT    ((((TLC5940_GSCLK_FREQ / (TLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT)) / DISP_SCAN_FREQ) / 2) - 1)   // 扫描前要计数的BLANK循环数
    $ i+ X0 z. I9 C& C2 V

  3. ! X" i4 X2 R1 e) V
  4.    void TIM4_IRQHandler(void)
    + N0 A7 m3 K+ v! k6 a
  5.     {
    5 A, f& |  W7 I8 k
  6.         // TIM4 IRQ处理程序有几个任务:
    + u7 S3 K) ~3 r7 U' W
  7.         //    - 切换扫描信号" {& Y* q6 r: f
  8.        //    - 锁定新选择('扫描')列的先前传输的数据
    ( s* {' z' S  J4 I
  9.         //    - 设置并启动SPI2 DMA流来传输下一列的数据
    : F' W2 n$ b8 Y7 I. j2 o" F; L5 @
  10.         // 所有这些都应该在BLANK信号(TIM4 OC1)高的窗口内执行(而不是完整的SPI传输)。, t- B) g  h$ m* P
  11. $ `/ ]9 J7 [' d6 }6 R, Y! A
  12.         // 检查所生成的中断是否为OC1更新$ @. r# l/ P, y" ~% u' b5 G0 J
  13.         if(TIM_GetFlagStatus(TIM4,TIM_IT_CC1))
    4 n1 ?% G  r. N- Q$ i0 S# S/ J0 O1 D6 i
  14.         {  f% u8 Y) T$ _( ?0 E: E5 e% ?  o- `
  15.             // 清除TIM4 CC1中断位
    & H; M( D2 l% l  q9 z
  16.             TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);" Y( A4 ?" y& j

  17. : k( v5 g8 F. Q( p/ i; E5 J$ h
  18.             // 仅在向下计数时执行事件(这确保在空白脉冲内触发XLAT脉冲、扫描更新和SPI传输)
    " b# p- d: K' V" f" s" T$ ~5 f* d
  19.             if(TIM4->CR1 & TIM_CR1_DIR)2 F8 z6 M) F+ Y0 t, _, j" h/ e
  20.             {. B, U3 J* l& u
  21.                 // 检查是否需要'SCAN'更新(XLAT脉冲,扫描切换,下一次传输触发)
    8 u% k% l; K* o  a; `
  22.                 if(dispBlankCycleCnt++ >= DISP_BLANK_CYCLE_LIMIT)& w8 T, t9 P! {9 R' m! y, }
  23.                 {
    $ z0 R4 t: A+ V% Y$ w% L. A
  24.                     GPIO_SetBits(GPIO_XLAT_PORT, GPIO_XLAT_PIN);                  // 设置XLAT引脚
    ) x, a# t2 P1 ~' k$ J' U) m5 J
  25.                     dispBlankCycleCnt = 0;                                        // 重置计数器
    * W) o" F4 A& u* ?: M0 M

  26. % U0 Q, ?5 ~8 Y) h% b4 U
  27.                     // 确定当前列,并相应地移动" j2 X, ?1 V6 t1 d
  28.                     if(dispCurrentCol); {! \1 y; m0 f% @
  29.                     {7 }5 ?, `' b1 G! F" e
  30.                         dispCurrentCol = 0;                              // 更改为“0”列1 t9 B( y) i3 S! V" J! w
  31.                        GPIO_SetBits(GPIO_SCAN_PORT, GPIO_SCAN_PIN);      // 设置扫描引脚(注意列0是逻辑高位,列1是逻辑低位)
    9 s+ w: S+ n/ O2 W, r
  32.                         DMA1_Stream4->M0AR = (uint32_t)&dispData1;       // Send *next*列的数据(由于当前列现在是'0',因此将发送(针对下一个循环)
      W9 E( e4 _1 F" V) l0 D
  33.                    }
      K; e: a$ I. p3 |7 c
  34.                    else
    . ^$ k2 N; m, b; I. ~/ p8 `4 [! }
  35.                     {" E3 H( R) q+ P
  36.                         dispCurrentCol = 1;                                    // 更改为“1”列
    # z' f+ P* w  N$ Q! t! J2 i
  37.                         GPIO_ResetBits(GPIO_SCAN_PORT, GPIO_SCAN_PIN);      // 重置扫描引脚(注意列0是逻辑高位,列1是逻辑低位)' c2 T) r) n% W% M6 c
  38.                         DMA1_Stream4->M0AR = (uint32_t)&dispData0;         // Send *next*列的数据(由于当前列现在是'1',因此发送了(对于下一个循环)) d5 ^1 K& \' ]4 |# V
  39.                     }9 @! r% `" ]( f! Z+ I! }& t

  40. 1 Z4 F8 U  I9 |9 G6 x
  41.                     GPIO_ResetBits(GPIO_XLAT_PORT, GPIO_XLAT_PIN);             // 清除XLAT引脚
    : o9 M$ r; P$ C

  42. 9 g; M0 k# p, z9 H
  43.                     //Trigger the next transfer
    ' s1 J: i& Z! M6 j. ^
  44.                     SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);        // 启用DMA传输请求1 m4 V/ v( Y: }  c9 m& r6 ?9 Z
  45.                     DMA_Cmd(DMA1_Stream4, ENABLE);                         // 启用分配给SPI2的DMA流
    # Q* ?0 R" X+ J% M3 l
  46.                 }0 ^& d$ G8 e, f& K: q
  47.             }% }6 U' E, Y9 Y
  48.         }
    + @9 t3 t- s" n2 O8 `
  49.     }
    ! w$ b/ v- C" j

  50. + Z. p4 R2 M2 N
  51.     void DMA1_Stream4_IRQHandler(void)! c' t1 i" m0 F1 ^! ^# G% n
  52.     {
    , S- G6 l$ J6 W& l% _
  53.         // 检查是否设置了传输完成中断标志
    $ B* i. t' j( J5 I' a8 J2 e0 k
  54.         if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4) == SET)
    / ]- \9 Q+ R  ]; u7 n
  55.         {% Z, F: t8 i3 t" ]/ U
  56.             // 清除DMA1 Stream4传输完成标志6 Y4 T  a, `$ ~# U6 O/ z% x) N
  57.             DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);
    7 K# O7 L1 ^; e5 B8 q& L6 G8 t
  58.         }
    ' o& h* E( W, ?% X6 d
  59.     }5 [& ^5 h0 d/ b8 R
复制代码
DMA ISR目前没有被使用(我确实打算用它做一些无关的事情),但TIM4 ISR基本上控制了整个显示。1 [9 _# e0 x# b1 C; h
空白脉冲的上升边缘(有效地)触发中断。在确定正确的ISR触发了事件之后,使用TIM_CR1_DIR位检查计数器是否正在减少计数。这确保了我们只在BLANK脉冲的上升边缘执行以下任务。
* O7 C6 Z) Z, M3 F1 D  P" Q1 s每当ISR运行时,我们增加一个计数器,如果这个计数器超过扫描显示所需的数量,我们就使用XLAT信号锁存前面的数据,切换扫描信号,并传输下一个数据(在disdata0[]或disdata1[]数组中找到)。# y3 h0 H( Z# h4 p
在dis_blank_cycle_limit中计算扫描前等待的空循环数,其中考虑到:, S/ X! v7 T9 `* D* S
) x! N$ C2 S8 R$ T
, B- m5 a$ X. K% `9 ~4 F3 d
TLC5940_GSCLK_FREQ -灰度时钟频率
, q1 L. G- y$ Z7 p& V& wTLC5940_GSCLK_COUNTS + TLC5940_BLANK_COUNT——上升的空白边之间的GSCLK脉冲数$ j, B0 h- e( O! V8 k
expec_scan_freq -我们希望扫描数组的频率(此处设置为200Hz)
7 Z, H$ l" Q' |6 X现在,更新disdatax[]数组中的数据将改变led上显示的内容。
+ `3 H+ o* Y( B% d6 Z4 B$ i  J4 BGSCLK频率为1MHz,扫描频率为200Hz,我没有明显的LED闪烁,即使我听到人们谈论使用>5MHz来避免它与他们的设置。9 X2 j) [3 l$ H
+ v# i0 x2 ?1 a2 t% w- k2 n

. Y5 x, B; }0 V3 m! lLogic Analysis: N8 q1 H5 e$ O& {( N
我在STM32F407输出和TLC5940显示板输入之间附加了一个逻辑分析器,如下所示:
; J4 A& k* y: C! n/ z4 {/ A1 s
20.png
现在我们知道GSCLK周期是预期的,我们可以研究空白时间来确定正在以8位分辨率记录的灰度数据。
- z* q, y2 N3 }& T1 t) {1 U; G下降和上升空白边之间的时间是305.6 - 49.6 = 256us,这是预期的。( b3 {$ t9 H) z4 t' ~9 X9 f
我还调查了一个更接近的水平,以检查信号的相位是正确的2^8计数。
4 T8 \) ^3 q$ O5 p9 g3 g0 J
21.png
最后,检查扫描宽度,我们可以看到一个列启用了2.445ms。  T4 z8 l* N5 G0 {; L( A( {
即扫描速率为409Hz;考虑到2。5us =不能被256us整除,这很好。
% \' u1 o1 T) M上面的捕获还显示,当空白计数达到极限时,相关的ISR锁存前面的数据,2 Q, i3 [5 y3 m* @0 Z* i0 y
切换扫描行,然后触发SPI传输。
) E) x" M3 w0 E然后在锁定该数据之前计算所需的空白周期数(XLAT信号在2445ms光标所在的蓝色箭头右侧几乎不可见)。# l; ^: A* F; F/ E7 S

# I: N7 F/ a( k- b! m3 v7 d- l) T6 t. u4 r" I& Z0 r& Z1 F' l; v

* E- E5 `; {0 [8 d8 e. w: a
收藏 评论0 发布时间:2021-10-12 13:31

举报

0个回答

所属标签

相似技术帖

官网相关资源

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