|
我们在做SPI应用时,有时希望通过定时器定时地触发SPI的收发,并利用DMA完成数据的传输。这里,我基于STM32L476芯片来做个演示,以供参考【为什么选用32L476,其实没啥特别原因,只是顺手找了块Nucleo板】。 # t0 K3 j+ L9 d) c' |: ^# p 本示例的大致过程是这样的:4 P* J) h* j. X5 | ' N6 x9 n& @- W1 I$ D$ [ * c; l+ w) T2 N, T; ]3 q 片内SPI1做Master,SPI2做Slave,均工作在全双工模式。 , \7 j! o& g( U& T/ Q 5 ^3 j& D2 T' w
" j/ D' b, w2 w) }/ y; C 这里使用片内定时器TIM3,借助它的更新事件触发DMA请求,通过DMA将数据给到SPI1的数据寄存器并发送出去,同时也开启SPI1接收事件的DMA传输。SPI2的收、发事件都启用DMA传输来实现数据收发操作。 TIM3的更新事件周期控制两个SPI的收发节奏,即定时器每产生一次更新事件,SPI1/SPI2这两个主从通信模块就进行一个数据的收发。我们可以通过调整定时器的计时周期来调整数据收发的快慢节奏。 % E# X" I2 U/ j2 ]3 j$ Q 好,先基于STM32CubeMx进行初始化配置。 1、对TIM3做基本配置。选择时钟源,先大致估算个定时器计时周期,调试时我们可以自行灵活调整。, |, A ?) Z9 s7 Q/ V' m. y. i ' \; E6 n' q; L & @7 I. R/ @; P; [6 L
开启基于TIM3更新事件的DMA配置。传输方向是从Memory到外设SPI1,即将内存数据传输到SPI1的数据寄存器进行数据发送,这里选用循环模式。) l3 h' X" |9 H2 z( X/ Q 4 |' U; c# ~+ ~/ ]
2、对SPI1/SPI2进行基本配置。细节请参看下面三幅截图。7 L+ B) x# U% W% a& Z8 K* a5 t, z # L3 o7 F+ t6 S# P
) C- d; C8 C4 L* U3 L
5 F0 Q4 s x# J- j- f4 ?' x9 r
8 U7 V" I0 ]1 ^1 a6 X" b % C) @7 V ^- n1 Z2 ?" g) y 3、DMA的配置情况。" D3 G0 l- m" h2 H1 c5 K! i7 Q 在TIM3和SPI1/SPI2外设配置中,开启了相关事件的DMA请求,汇总如下图。! P: a: u& K' H: z! b ; t( q# P# O# ] x9 o5 ^* F6 P+ D 2 V& b; m; i. m. Q) P- A, a; W" p
: k' G: v3 d8 n5 k' [ 4、准备用户代码。 当完成基于STM32CubeMx的初始化配置并生产初始化代码后,我们准备相应的用户代码。 1 i7 I m6 S! \9 e, U* x) ]0 u 3 N0 Q' g4 {4 K) D 这里准备了4个内存数组,分别存放SPI1/SPI2的收发数据。/ _/ Y. I; v7 z( v+ |5 w 3 v& r4 U5 [% @& p( b0 J 在定时器的触发下,主SPI1逐字的向从SPI2发送“Hello! I AM STM32!”,从SPI2也逐字的向主SPI1回应“HI,MASTER,ME TOO!”,就这样循环操作。7 ^# a" W- C- o " r* ]* R& [( Y0 C2 w 下面两幅图是本示例中使用到的用户代码截图,是基于STM32Cube固件库而编写的。应该说简单明了,无须多做解释。* l1 O; _5 g( X/ v7 y; O$ U
' \8 C$ |4 t e0 o# v5 e% E& T# N
( W: T; [- [2 P8 j Q, M 5、结果验证。; I" u- x( h J/ K 1 R7 q: s( n9 w * m# L8 f+ l) E, P! S5 P6 ~8 B 下面的截图是两个不同时刻SPI通信时的信号时序波形图。其中,紫色的是时钟信号,绿色、黄色是数据信号。两个数据信号间的间隔由定时器的更新周期决定。 0 z0 ]# `* v" r% z; _. ]$ i, Q1 x% [
. p- \' s, o0 s6 G1 ~, S, e 下面的截图是在调试状态下的通过观察窗口得到的SPI1/SPI2分别从对方收到的内容。 - p0 Y/ t* T3 Y' w6 j$ P+ e F % P% T; [ |; ]' X
5 j- W+ f$ Z6 v. H8 C 最后小结下。整体上讲上述应用的实现不难,稍微有点综合性。 A9 V$ {- d. m% P9 v, ?; p 要实现上述应用,首先要求我们对DMA传输的原理有清晰的了解,触发事件,传输源、传输目标几个概念及关系要弄清楚。另外,即使我们基于STM32固件库开发,不一定能找到完完整整的现存代码,我们可能需要基于现有驱动代码自行组织用户程序。 还有,在上面示例代码中,我没有开启DMA的中断事件,我们在具体应用中可以根据情况来决定是否启用DMA中断,比方开启传输完成中断等。顺便提醒下,这里我们基于定时器事件自行指定DMA的源端和目的端,一定要保证是该触发事件所请求的DMA可以到达的地方,编程设计前最好查看下相关芯片数据手册里的芯片模块及总线框架图。 ; w6 }9 L. p0 t. B5 N5 r ( P1 o2 z2 W; e |
微信公众号
手机版