STM32 SPI DMA 的使用
一是想總結一下SPI總線的特點與注意點,二是總結一下SPI DMA的使用
一、SPI信號線說明
通常SPI通過4個引腳與外部器件相連:
- MISO:主設備輸入/從設備輸出引腳。該引腳在從模式下發送數據,在主模式下接收數據。
- MOSI:主設備輸出/從設備輸入引腳。該引腳在主模式下發送數據,在從模式下接收數據。
- SCK:串口時鍾,作為主設備的輸出,從設備的輸入
- NSS:從設備選擇。這是一個可選的引腳,用來選擇主/從設備。它的功能是用來作為“片選引腳”,讓主設備可以單獨地與特定從設備通訊,避免數據線上的沖突。
二、原理
MOSI腳相互連接,MISO腳相互連接。這樣,數據在主和從之間串行地傳輸(MSB位在前)。
通信總是由主設備發起。主設備通過MOSI腳把數據發送給從設備,從設備通過MISO引腳回傳數據。這意味全雙工通信的數據輸出和數據輸入是用同一個時鍾信號同步的;時鍾信號由主設備通過SCK腳提供。
DMA說明
DMA是AMBA的先進高性能總線(AHB)上的設備,它有2個AHB端口:
一個是從端口,用於配置DMA,另一個是主端口,使得DMA可以在不同的從設備之間傳輸數據。
DMA的作用是在沒有Cortex-M3核心的干預下,在后台完成數據傳輸。在傳輸數據的過程中,
主處理器可以執行其它任務,只有在整個數據塊傳輸結束后,需要處理這些數據時才會中斷主處理器的操作。
它可以在對系統性能產生較小影響的情況下,實現大量數據的傳輸。
SPI_DMA的通信過程
- 設置外設地址
- 設置存儲器地址
- 設置傳輸數據量
- 設置通道的配置信息
- 使能DMA通道,啟動傳輸
- 發送時,在每次TXE被設置為’1’時發出DMA請求,DMA控制器則寫數據至SPI_DR寄存器,TXE標志因此而被清除。
- 接收時,在每次RXNE被設置為’1’時發出DMA請求,DMA控制器則從SPI_DR寄存器讀出數據,RXNE標志因此而被清除。
相關代碼 這里使用的是SPI1 SPI_DMA配置
/******************************************************************************* * Function Name : SPI1_DMA_Configuration * Description : 配置SPI1_RX的DMA通道2,SPI1_TX的DMA通道3 * Input : None * Output : None * Return : None * Attention : *******************************************************************************/ void SPI1_DMA_Configuration( void ) { DMA_InitTypeDef DMA_InitStructure; /* DMA1 Channel2 (triggered by SPI1 Rx event) Config */ DMA_DeInit(DMA1_Channel2); DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; //設置 SPI1 發送外設(0x4001300C) 地址(目的地址) DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff; //設置 SRAM 存儲地址(目的地址) DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //傳輸方向 外設-內存 DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize; //設置 SPI1 發送長度 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel2, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE); /* Enable SPI1 DMA RX request */ SPI1->CR2 |= 1<<0; //接收緩沖區DMA使能 DMA_Cmd(DMA1_Channel2, ENABLE); /* DMA1 Channel3 (triggered by SPI1 Tx event) Config */ DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; //設置 接收外設(0x4001300C) 地址(源地址) DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff; //設置 SRAM 存儲地址(源地址) DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //傳輸方向 內存-外設 DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize; //設置 SPI1 接收長度 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設地址增量(不變) DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內存地址增量(變化) DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外設傳輸寬度(字節) DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //內存傳輸寬度(字節) DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //傳輸方式,一次傳輸完停止,不重新加載 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //中斷方式-高(三級) DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //內存到內存方式禁止 DMA_Init(DMA1_Channel3, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE); //開啟 DMA1_Channel3 傳輸完成中斷 DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE); //開啟 DMA1_Channel3 傳輸錯誤中斷 /* Enable SPI1 DMA TX request */ SPI1->CR2 |= 1<<1; //發送緩沖區DMA使能 DMA_Cmd(DMA1_Channel3, DISABLE); //開啟 DMA 通道 DMA1_Channel3 }
SPI發送
/******************************************************************************* * Function Name : SPI1_Send * Description : SPI1的DMA方式發送 * Input : SPI1_TX_Buff[SPI1_SendBufferSize] * Output : None * Return : None * Attention : 關閉DMA通道3之前必須等待TXE為1,等待忙標志為0 *******************************************************************************/ void SPI1_Send( u8 *buff, u32 len ) { DMA1_Channel3->CPAR = SPI1_DR_Addr; //外設地址 DMA1_Channel3->CMAR = (u32) buff; //mem地址 DMA1_Channel3->CNDTR = len ; //傳輸長度 DMA1_Channel3->CCR = (0 << 14) | // 非存儲器到存儲器模式 (2 << 12) | // 通道優先級高 (0 << 11) | // 存儲器數據寬度8bit (0 << 10) | // 存儲器數據寬度8bit (0 << 9) | // 外設數據寬度8bit (0 << 8) | // 外設數據寬度8bit (1 << 7) | // 存儲器地址增量模式 (0 << 6) | // 外設地址增量模式(不增) (0 << 5) | // 非循環模式 (1 << 4) | // 從存儲器讀 (1 << 3) | // 允許傳輸錯誤中斷 (0 << 2) | // 允許半傳輸中斷 (1 << 1) | // 允許傳輸完成中斷 (1); // 通道開啟 }
SPI接收
/******************************************************************************* * Function Name : SPI1_Recive * Description : SPI1的DMA方式接收 * Input : None * Output : SPI1_RX_Buff[SPI1_ReciveBufferSize] * Return : None * Attention : 必須要先關閉通道2,然后再配置通道2的參數 *******************************************************************************/ void SPI1_Recive( u8 *buff, u32 len ) { DMA1_Channel2->CCR &= ~( 1 << 0 ); //關閉DMA通道2 DMA1_Channel2->CPAR = SPI1_DR_Addr; //外設地址 DMA1_Channel2->CMAR = (uint32_t)buff; //mem地址 DMA1_Channel2->CNDTR = len ; //傳輸長度 DMA1_Channel2->CCR = (0 << 14) | // 非存儲器到存儲器模式 (2 << 12) | // 通道優先級高 (0 << 11) | // 存儲器數據寬度8bit (0 << 10) | // 存儲器數據寬度8bit (0 << 9) | // 外設數據寬度8bit (0 << 8) | // 外設數據寬度8bit (1 << 7) | // 存儲器地址增量模式 (0 << 6) | // 外設地址增量模式(不增) (0 << 5) | // 非循環模式 (0 << 4) | // 傳輸方向 外設-內存 (0 << 3) | // 允許傳輸錯誤中斷 (0 << 2) | // 允許半傳輸中斷 (1 << 1) | // 允許傳輸完成中斷 (1); // 通道開啟 }