一、试验方针
学会装备STM32的SPI寄存器和DMA寄存器,完结STM32的SPI1与SPI2通讯功用,每次发送一字节数据,并可屡次发送,假如接纳的数据正确,则点亮LED灯。
二、试验意图
参加DMA的SPI通讯相关于一般SPI通讯有什么优点?ST给SPI加了DMA功用出于什么意图?我觉得这是很重要的一个问题,一向边学习边想。以下是我的观点:
削减CPU负荷?我想这应该是DMA最主要的功用,可是关于SPI通讯来说,其实大部分时分咱们需求依据发送的指令->方针器材的应对来决议下一个指令,所以此刻CPU仍是需求一向等候每次通讯的完毕。并且像SD卡的操作,是一个次序流的指令操作进程,用中止也不容易操控。那究竟参加了DMA有什么优点?细心查看了STM32F10xxx的用户手册,发现这么一行字“接连和非接连传输:当在主形式下发送数据时,假如软件足够快,能够在检测到每次TXE的上升沿(或TXE中止),并立即在正在进行的传输完毕之前写入SPI_DR寄存器,则能够完结接连的通讯;此刻,在每个数据项的传输之间的SPI时钟坚持接连,一起BSY位不会被铲除。假如软件不够快,则会导致不接连的通讯;这时,在每个数据传输之间会被铲除”以及
也便是说假如接连传输而不运用DMA的话,需求CPU不断检测TXE并很快地置入SPI->DR的值,关于杂乱程序的话这是很难到达的,而假如运用DMA,就能够容易完结接连传输,CPU只需等候其完结就好。我想到的一个运用便是在写SD卡的时分,每次写一个块512字节,就能够用到,能进步SD卡的写入数据速率。
其次还能够降低功耗,记住数字%&&&&&%老师说过一句话“软件上下降数字电路功耗的一个办法便是削减电平转化。”那么接连通讯的时分,像SPI的BSY电平转化会大大削减!
最终一点,尽管作用不大,便是假如不是用DMA,那么CPU的作业便是搬运工,把SPI->DR的内容搬到内存存储起来,而假如运用DMA,就省掉了这个环节!
我想,为什么完结同一个功用,有的履行起来很流通,有的却很卡,应该和这些小细节的减载有关吧。
这次先把SPI根本通讯写出来,然后再写SPI的接连通讯,并看能不能用到SD卡读写上。
三、SPI&DMA剖析
1、这儿先阐明一下SPI的全双工通讯(高手略过哈)
SPI全双工通讯的特色:一边发送一边接纳,硬件上只需一个SPI->DR寄存器和两个缓冲器(发送缓冲器和接纳缓冲器),主形式(从形式相似):SPI->DR会先读发送缓冲器,并经过MOSI管脚(Master output Slave Input)一位一位地发送出去,在发送的进程中,SPI->DR的数据会左移(假如是高位先发送),并且会从MISO(Master input Slave output)读入数据添补SPI->DR左移后的空缺。传完8比特后,SPI->DR再把数据并行写入接纳缓冲寄存器。所以,SPI1与SPI2的通讯进程如下:
装备SPI寄存器的时分,需求留意以下几点:
(1)nss的装备:假如是单主单从,运用nss软件办理,除了用MSTR装备主从设备,还要设置SSM和SSI,只需在SSM位为1时,SSI位才有含义。
(2)主从设备的数据帧格局,时钟沿读写形式要共同;
(3)SPI的寄存器也需求敞开DMA使能;
(4)SPI尽管能够发送16bit数据,可是只支撑8bitDMA!
2、再说一下DMA
DMA——Direct Memory Access,直接内存存取,作用是独立于CPU,直接树立内存与外设的通讯通道。
SPI的DMA操作,便是在SPI->TXE为1时,会向对应的DMA通道宣布恳求,DMA通道会宣布应对信号,SPI收到应对信号后吊销恳求信号,DMA吊销应对信号,并把内存值置入发送缓冲,SPI传送开端。接纳进程与上面相似。
DMA装备的部分阐明:
(1)需求使能RCC寄存器的SPI和DMA时钟,至于辅佐时钟,查过网上的评论,有人说一些外设假如没有敞开辅佐时钟会不能用,但SPI不需求;
(2)DMA的存储器地址(memorybaseaddr):即变量地址。咱们在程序中界说的每个变量,都有对应的内存地址,你想把SPI的接纳发送数据存在哪个变量,就将对应变量的地址赋给DMA存储器地址寄存器。如u8 SPI1_TX_Buff的地址是(u32)&SPI1_TX_Buff;u8 SPI1_TX_Buff[512]的地址是(u32)SPI1_TX_Buff。
(3)DMA循环形式:有些材料会译为DMA的循环缓存形式,我觉得不太精确,这儿循环的意思是指DMA的传输数量计数器会重置初值,因为DMA每传一个数据,传输数量计数器减一,只需在传输数量计数器的值不为零时,才会呼应恳求。在循环形式下,当传输计数器的值减为0后,会从头装载;而内存(缓存)地址则不论循环非循环形式,都会在每次传输完结后重置为基地址。所以,假如咱们把DMA设置会正常形式,那么在下次传输前,只需对DMA的传输数量计数器从头写入就行。
循环形式一般用于数据更新,比方ADC选用需求不断更新数据。
(4)DMA的外设地址:正点原子的串口DMA试验中,在写外设地址时,都会用一个变量缓存再写入,不然程序就运转不正确,他也不知道为什么,而ST库函数的example中关于外设地址也都是从头define的,所以外设地址最好仍是选用#define SPI1_DR_Addr ( (u32)0x4001300C )界说的好。
至于外设地址,能够先从STM32的用户手册“2.3存储器映像”得到开端地址+对应外设地点目录的“寄存器地址映像”标识的偏移地址。例如:从“2.3存储器映像”得到SPI1开端地址0x40013000,从SPI地点目录的“寄存器地址映像”得到SPI->DR的偏移量为0x0C,那么SPI1_DR_Addr便是0x4001300C;
(5)DMA通道敞开次序:依照下图的数字序号顺次敞开,才干保证数据正确发送。比方①的SPI2_TX_Buff对应的是DMA通道5.
(6)正常形式的第2次发送:DMA发送的时分只需使能DMA就能够开端传送,可是第2次传送之前,需求进行以下过程:
1、封闭DMA通道;
2、铲除DMA传输完结标志以及重置CNDTR传输数量计数器;
3、敞开DMA通道,等候传输完结。
四、试验成果
运用SPI1和SPI2进行两次数据传输,并比较SPI1_RX与SPI2_TX,SPI2_RX与SPI1_TX,数据相同点亮LED灯。
在某个论坛看到有人说把SPI的速度设置为2分频传输数据不正确,剖析原因是DMA反响不过来。我也试了一下,传输正常,数据正确。(SPI传输速率是用JLINK仿真查看寄存器的)
哦,对了,期间还吃过一个亏,害我调了良久,便是下面的句子:
while( ( DMA1->ISR & (1<<17) ) == 0 ) ; //等候通道5传输完结
我写成:
while( DMA1->ISR & (1<<17) == 0 ) ; //等候通道5传输完结
因为“==”的优先级比“&”高,所以会先履行“(1<<17) == 0”,成果是0,再与上DMA1->ISR,那么相当于while直接跳过了,读不到数据!很初级的过错!所以提示后来者,看起来可加可不加的括号,仍是要加上去的好!
还有一个问题,一向在想DMA传输,那么硬件怎样以为一次传输的完毕而中止以及怎样才干敞开新一次的传输。我觉得最要害便是DMA的传输数量计数器以及DMA的传输完结标志。只需DMA的计数器不为零,就能呼应恳求传输,此刻就算传输完结标志置位,也能进行DMA呼应,只不过你不知道什么时分完结算了。所以每次传输开端前,程序需求铲除标志位并检测到该标志方位位,才知道一次传输是否完结!
续:总算把SPI的DMA弄完了,完结了接连发送和读取的功用,DMA拓荒512字节的数组作为内存存储数据(所以接连发送最大的数据量也是512,当然能够在宏界说里边更改),经过num操控要写入或读入的数据量,源代码中有3个函数,一个函数是读写一体的,一个函数是只发送形式,一个函数是只接纳形式,都经过测验。仅有的缺点便是没有进行过错检测,特别阐明一下,我把清标志位是放在函数前面而不是函数后边,便是想函数履行完,标志位仍然还在,咱们能够以此来判别是否有过错。在这儿和我们共享一下小经验。
(1)怎样测验?最好的测验办法我觉得便是双机通讯了,因为试验室资源比较好,所以我得以有两个STM32(非MiniSTM32,用的是AG嵌入式开发板)进行测验,所以以上代码都是经过双击测验的,不过我只整理了SPI1主机源代码,需求的自己略微改一下就能够,程序中有注释!
(2)用双机测验的时分,刚开端我没有共地,导致数据能够接纳,可是数据过错!所以紧记,当你运用两个器材通讯或交互时,一定要先查看两个器材是否共地,乃至共源!
(3)假如只需一个STM32其实也能够测验,便是把MISO和MOSI短接,但这个测验办法,用来测验SPI1_ReceiveSendByte(u16 num)就比较便利,用来测验只发送和只接纳形式就需求改一下函数咯。
(4)弄了这么久的SPI_DMA,也不知道用途大不大,总归弄完了,呵呵,也算比较了解SPI总线和DMA了,接下来想试试原子哥的新的SD卡函数,本来AG嵌入式开发板也是移植原子哥的旧版,也是有些卡初始化失利,我还以为是我的卡有问题呢?还有便是文件体系,前阵子只弄了根本的读写,预备把FATFS文件体系写得完善一点~
最终,附上源代码。(运用的不是MiniSTM32,所以我们在测验时只需求改一下LED驱动。)
第一个源代码是根底的,完结一个字节在SPI1&SPI2的传送;
第二个是函数化的代码咯,发送随意数量的8bit数据,数量小于512;