STM32 SPI DMA 的使用
一、SPI信號線說明
本文引用地址:http://www.ex-cimer.com/article/201611/318283.htm通常SPI通過4個引腳與外部器件相連:
- MISO:主設備輸入/從設備輸出引腳。該引腳在從模式下發(fā)送數(shù)據(jù),在主模式下接收數(shù)據(jù)。
- MOSI:主設備輸出/從設備輸入引腳。該引腳在主模式下發(fā)送數(shù)據(jù),在從模式下接收數(shù)據(jù)。
- SCK:串口時鐘,作為主設備的輸出,從設備的輸入
- NSS:從設備選擇。這是一個可選的引腳,用來選擇主/從設備。它的功能是用來作為“片選引腳”,讓主設備可以單獨地與特定從設備通訊,避免數(shù)據(jù)線上的沖突。
二、原理
MOSI腳相互連接,MISO腳相互連接。這樣,數(shù)據(jù)在主和從之間串行地傳輸(MSB位在前)。
通信總是由主設備發(fā)起。主設備通過MOSI腳把數(shù)據(jù)發(fā)送給從設備,從設備通過MISO引腳回傳數(shù)據(jù)。這意味全雙工通信的數(shù)據(jù)輸出和數(shù)據(jù)輸入是用同一個時鐘信號同步的;時鐘信號由主設備通過SCK腳提供。
NSS分為內部引腳和外部引腳。
NSS外部引腳可以作為輸入信號或者輸出信號,輸入信號一般用作硬件方式從機的片選,而輸出信號一般用于主SPI去片選與之相連的從SPI。
NSS從設備選擇有兩種模式:
1、軟件模式
可以通過設置SPI_CR1寄存器的SSM位來使能這種模式,當它為1時,NSS引腳上的電平由SSI決定。在這種模式下NSS外部引腳可以用作它用,而內部NSS信號電平可以通過寫SPI_CR1的SSI位來驅動?!?/p>
2、硬件模式
兩種方式:
(1)對于主SPI,NSS可以直接接高電平,對于從SPI,可以直接接低電平。
(2)當STM32F10xxx工作為主SPI,并且NSS輸出已經(jīng)通過SPI_CR2寄存器的SSOE位使能,這時主機的NSS講作為輸出信號,引腳信號被拉低,所有NSS引腳與這個主SPI的NSS引腳相連并配置為硬件NSS的SPI設備,將自動變成從SPI設備。
此時兩個的NSS信號線可以接個上拉電阻直連。
四、DMA說明
DMA是AMBA的先進高性能總線(AHB)上的設備,它有2個AHB端口:一個是從端口,用于配置DMA,另一個是主端口,使得DMA可以在不同的從設備之間傳輸數(shù)據(jù)。
DMA的作用是在沒有Cortex-M3核心的干預下,在后臺完成數(shù)據(jù)傳輸。在傳輸數(shù)據(jù)的過程中,主處理器可以執(zhí)行其它任務,只有在整個數(shù)據(jù)塊傳輸結束后,需要處理這些數(shù)據(jù)時才會中斷主處理器的操作。它可以在對系統(tǒng)性能產(chǎn)生較小影響的情況下,實現(xiàn)大量數(shù)據(jù)的傳輸。
五、SPI_DMA的通信過程
- 設置外設地址
- 設置存儲器地址
- 設置傳輸數(shù)據(jù)量
- 設置通道的配置信息
- 使能DMA通道,啟動傳輸
- 發(fā)送時,在每次TXE被設置為’1’時發(fā)出DMA請求,DMA控制器則寫數(shù)據(jù)至SPI_DR寄存器,TXE標志因此而被清除。
- 接收時,在每次RXNE被設置為’1’時發(fā)出DMA請求,DMA控制器則從SPI_DR寄存器讀出數(shù)據(jù),RXNE標志因此而被清除。
六、相關代碼
這里使用的是SPI1
SPI_DMA配置
1 /*******************************************************************************2 * Function Name : SPI1_DMA_Configuration3 * Description : 配置SPI1_RX的DMA通道2,SPI1_TX的DMA通道34 * Input : None5 * Output : None6 * Return : None7 * Attention : 8 *******************************************************************************/9 void SPI1_DMA_Configuration( void )10 {11 DMA_InitTypeDef DMA_InitStructure;12 13 /* DMA1 Channel2 (triggered by SPI1 Rx event) Config */14 DMA_DeInit(DMA1_Channel2); 15 DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; //設置 SPI1 發(fā)送外設(0x4001300C) 地址(目的地址)16 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff; //設置 SRAM 存儲地址(目的地址)17 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //傳輸方向 外設-內存18 DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize; //設置 SPI1 發(fā)送長度19 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;20 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;21 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;22 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;23 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;24 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;25 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;26 DMA_Init(DMA1_Channel2, &DMA_InitStructure);27 28 DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);29 /* Enable SPI1 DMA RX request */30 SPI1->CR2 = 1<<0; //接收緩沖區(qū)DMA使能31 DMA_Cmd(DMA1_Channel2, ENABLE);32 33 34 /* DMA1 Channel3 (triggered by SPI1 Tx event) Config */35 DMA_DeInit(DMA1_Channel3); 36 DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; //設置 接收外設(0x4001300C) 地址(源地址)37 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff; //設置 SRAM 存儲地址(源地址)38 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //傳輸方向 內存-外設39 DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize; //設置 SPI1 接收長度40 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設地址增量(不變)41 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內存地址增量(變化)42 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外設傳輸寬度(字節(jié))43 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //內存?zhèn)鬏攲挾?字節(jié))44 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //傳輸方式,一次傳輸完停止,不重新加載45 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //中斷方式-高(三級)46 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //內存到內存方式禁止47 DMA_Init(DMA1_Channel3, &DMA_InitStructure);48 49 DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE); //開啟 DMA1_Channel3 傳輸完成中斷50 DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE); //開啟 DMA1_Channel3 傳輸錯誤中斷51 /* Enable SPI1 DMA TX request */52 SPI1->CR2 = 1<<1; //發(fā)送緩沖區(qū)DMA使能53 DMA_Cmd(DMA1_Channel3, DISABLE); //開啟 DMA 通道 DMA1_Channel354 }
SPI發(fā)送
1 /*******************************************************************************2 * Function Name : SPI1_Send3 * Description : SPI1的DMA方式發(fā)送4 * Input : SPI1_TX_Buff[SPI1_SendBufferSize]5 * Output : None6 * Return : None7 * Attention : 關閉DMA通道3之前必須等待TXE為1,等待忙標志為08 *******************************************************************************/9 void SPI1_Send( u8 *buff, u32 len )10 {11 DMA1_Channel3->CPAR = SPI1_DR_Addr; //外設地址12 DMA1_Channel3->CMAR = (u32) buff; //mem地址13 DMA1_Channel3->CNDTR = len ; //傳輸長度14 DMA1_Channel3->CCR = (0 << 14) // 非存儲器到存儲器模式15 (2 << 12) // 通道優(yōu)先級高16 (0 << 11) // 存儲器數(shù)據(jù)寬度8bit17 (0 << 10) // 存儲器數(shù)據(jù)寬度8bit18 (0 << 9) // 外設數(shù)據(jù)寬度8bit19 (0 << 8) // 外設數(shù)據(jù)寬度8bit20 (1 << 7) // 存儲器地址增量模式21 (0 << 6) // 外設地址增量模式(不增)22 (0 << 5) // 非循環(huán)模式23 (1 << 4) // 從存儲器讀24 (1 << 3) // 允許傳輸錯誤中斷25 (0 << 2) // 允許半傳輸中斷26 (1 << 1) // 允許傳輸完成中斷27 (1); // 通道開啟28 }
SPI接收
1 /*******************************************************************************2 * Function Name : SPI1_Recive3 * Description : SPI1的DMA方式接收4 * Input : None5 * Output : SPI1_RX_Buff[SPI1_ReciveBufferSize]6 * Return : None7 * Attention : 必須要先關閉通道2,然后再配置通道2的參數(shù)8 *******************************************************************************/9 void SPI1_Recive( u8 *buff, u32 len )10 {11 DMA1_Channel2->CCR &= ~( 1 << 0 ); //關閉DMA通道212 13 DMA1_Channel2->CPAR = SPI1_DR_Addr; //外設地址14 DMA1_Channel2->CMAR = (uint32_t)buff; //mem地址15 DMA1_Channel2->CNDTR = len ; //傳輸長度16 DMA1_Channel2->CCR = (0 << 14) // 非存儲器到存儲器模式17 (2 << 12) // 通道優(yōu)先級高18 (0 << 11) // 存儲器數(shù)據(jù)寬度8bit19 (0 << 10) // 存儲器數(shù)據(jù)寬度8bit20 (0 << 9) // 外設數(shù)據(jù)寬度8bit21 (0 << 8) // 外設數(shù)據(jù)寬度8bit22 (1 << 7) // 存儲器地址增量模式23 (0 << 6) // 外設地址增量模式(不增)24 (0 << 5) // 非循環(huán)模式25 (0 << 4) // 傳輸方向 外設-內存26 (0 << 3) // 允許傳輸錯誤中斷27 (0 << 2) // 允許半傳輸中斷28 (1 << 1) // 允許傳輸完成中斷29 (1); // 通道開啟30 }
評論