STM32學習筆記之SPI_DMA寄存器級操作
一、實驗目標
學會配置STM32的SPI寄存器和DMA寄存器,實現STM32的SPI1與SPI2通信功能,每次發送一字節數據,并可多次發送,如果接收的數據正確,則點亮LED燈。
本文引用地址:http://www.104case.com/article/201611/318033.htm二、實驗目的
加入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)正常模式的第二次發送:DMA發送的時候只需使能DMA就可以開始傳送,但是第二次傳送之前,需要進行以下步驟:
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;
評論