基于STM32H563 USART演示DMA鏈表功能
這里選用STM32H563Nucleo開(kāi)發(fā)板,使用USART3外設(shè)演示DMA鏈表模式的實(shí)現(xiàn)過(guò)程。大致過(guò)程是這樣的:
STM32H563芯片上的USART3與板載STLINK的VCP相連,通過(guò)PC端串口助手軟件先后發(fā)送3串字符給MCU,MCU的USART3通過(guò)DMA以鏈表模式依次接收并存到相應(yīng)內(nèi)存,同時(shí)在相應(yīng)的調(diào)試界面的觀察窗口加以顯示。接收過(guò)程對(duì)應(yīng)3個(gè)DMA接收節(jié)點(diǎn),他們構(gòu)成1個(gè)DMA接收隊(duì)列。當(dāng)MCU接收到3串字符后,也以DMA 鏈表模式依次發(fā)送3串字符到PC端的串口助手并顯示出來(lái)。同樣,USART3的DMA 發(fā)送也用到3個(gè)節(jié)點(diǎn)以組成1個(gè)DMA發(fā)送隊(duì)列。
GPDMA1的2個(gè)通道CH0,CH1,配置在鏈表模式。其中CH0用于USART3 RX DMA隊(duì)列,CH1用于USART3 TX DMA隊(duì)列。這個(gè)通道安排可以在用戶代碼里調(diào)整。
(相關(guān)資料圖)
下面使用STM32CbueMx圖形化工具進(jìn)行基本配置后創(chuàng)建測(cè)試工程。【注:手機(jī)模式下文中圖片可放大查看】
3個(gè)USART3 DMA RX節(jié)點(diǎn)的配置如下【3個(gè)配置類似,只是源地址、目標(biāo)地址有差異】:
3個(gè)USART3 DMA TX節(jié)點(diǎn)的配置如下【3個(gè)配置類似,只是源地址、目標(biāo)地址有差異】:
相關(guān)的用戶參考代碼如下:
#define RXBUFFERSIZE (8)#define TXBUFFERSIZE (22)volatileuint8_tRxComplete=0;//DMA接收完成標(biāo)志ALIGN_32BYTES (uint8_t aTxBuffer0[])="STM32H5_DMA_LIST_0";ALIGN_32BYTES (uint8_t aTxBuffer1[])="STM32H5_DMA_LIST_1";ALIGN_32BYTES (uint8_t aTxBuffer2[])="STM32H5_DMA_LIST_2";ALIGN_32BYTES (uint8_t aRxBuffer0[RXBUFFERSIZE]);ALIGN_32BYTES (uint8_t aRxBuffer1[RXBUFFERSIZE]);ALIGN_32BYTES (uint8_t aRxBuffer2[RXBUFFERSIZE]);DMA_HandleTypeDef handle_GPDMA1_Channel1;DMA_HandleTypeDef handle_GPDMA1_Channel0;UART_HandleTypeDef huart3;int main(void){/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_GPDMA1_Init();//MX_ADC1_Init(); MX_ICACHE_Init(); MX_USART3_UART_Init();/* USER CODE BEGIN 2 */ MX_Uart_tx_queue_Config(); MX_Uart_rx_queue_Config();/* Link UART queue to DMA channel */ HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel0, &Uart_tx_queue);/* Associate the initialized GPDMA handle to the UART handle */ __HAL_LINKDMA(&huart3, hdmatx, handle_GPDMA1_Channel0);/* Link UART queue to DMA channel */ HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel1, &Uart_rx_queue);/* Associate the initialized GPDMA handle to the UART handle */ __HAL_LINKDMA(&huart3, hdmarx, handle_GPDMA1_Channel1);HAL_UART_Receive_DMA(&huart3, (uint8_t *)aRxBuffer0, RXBUFFERSIZE);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1) {if(RxComplete)//checkuartRxDMAtransfercomplete ornot { RxComplete=0; huart3.gState = HAL_UART_STATE_READY; HAL_UART_Transmit_DMA(&huart3, (uint8_t*)aTxBuffer0, TXBUFFERSIZE); } }}/*UART3DMA接收完成中斷回調(diào)函數(shù)*/void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){RxComplete++;//DMAtransfercompleted}
下面開(kāi)始運(yùn)行程序以驗(yàn)證結(jié)果。
PC串口終端先發(fā)送三串字符被MCU USART3通過(guò)DMA接收,然后MCU以DMA鏈表的形式發(fā)送三串字符到串口終端。下圖中右邊觀察串口的字符為USART3通過(guò)DMA以鏈表形式接收的來(lái)自串口助手的數(shù)據(jù)。下圖中綠色方框內(nèi)三串字符為STM32H563 USART3以DMA鏈表形式3次發(fā)送出來(lái)的。
在上面測(cè)試中,關(guān)于DMA傳輸完成事件的配置,我選擇的針對(duì)整個(gè)DMA傳輸鏈完成傳輸后才產(chǎn)生完成事件。以上面的USART3 RX DMA接收為例,當(dāng)3個(gè)節(jié)點(diǎn)都完成DMA接收時(shí)才觸發(fā)DMA傳輸完成事件。當(dāng)然,我們也可以基于每個(gè)DMA傳輸節(jié)點(diǎn)來(lái)產(chǎn)生DMA傳輸完成事件,比如按下面的配置修改。
如果這樣修改之后,其它地方不動(dòng),這時(shí)USART3 通過(guò)DMA每收到一串?dāng)?shù)據(jù)都會(huì)觸發(fā)傳輸完成事件,并在相應(yīng)中斷里設(shè)置完成事件標(biāo)志【RxComplete】,進(jìn)而啟動(dòng)USART3發(fā)送的DMA 鏈?zhǔn)絺鬏敗R?jiàn)下圖結(jié)果【褐紫色字符是USART3通過(guò)DMA接收到的數(shù)據(jù)】:
其實(shí),在前面驗(yàn)證過(guò)程中,發(fā)生了點(diǎn)小波折也順便分享出來(lái)。
當(dāng)我做完基本的配置,創(chuàng)建工程、添加用戶代碼。編譯、除錯(cuò)后,開(kāi)始運(yùn)行程序時(shí)并不順利。
我先使用串口助手發(fā)送字符串給MCU,可是在USART3的DMA接收隊(duì)列里,只有第一個(gè)節(jié)點(diǎn)有反應(yīng)。通過(guò)開(kāi)發(fā)環(huán)境的觀察窗口看到的接收數(shù)據(jù)跟我發(fā)出去的字符大相徑庭,其它2個(gè)DMA接收節(jié)點(diǎn)不論我發(fā)送多少數(shù)據(jù)就根本沒(méi)反應(yīng)。因?yàn)槲以O(shè)計(jì)的就是只有當(dāng)接收隊(duì)列即3個(gè)接收節(jié)點(diǎn)都正常接收后才會(huì)啟動(dòng)USART3的DMA發(fā)送隊(duì)列。所以此時(shí)USART3也就沒(méi)有任何數(shù)據(jù)發(fā)送出來(lái)。檢查配置和用戶代碼,但沒(méi)有很快發(fā)現(xiàn)問(wèn)題原因。
后來(lái)在檢查USART3的基本通信配置時(shí),無(wú)意中發(fā)現(xiàn)了一點(diǎn)異樣。就是有關(guān)接收數(shù)據(jù)是否需要翻轉(zhuǎn)的配置項(xiàng)不知何時(shí)變成Enable了。
顯然,這個(gè)地方不能翻轉(zhuǎn),不然發(fā)送的數(shù)據(jù)跟接收的數(shù)據(jù)肯定不一樣了。我清楚地記得這個(gè)地方我是沒(méi)動(dòng)的,不知是不是某個(gè)時(shí)候不小心碰著而改動(dòng)了。立即將其Disable掉后進(jìn)行測(cè)試驗(yàn)證。
測(cè)試結(jié)果發(fā)現(xiàn)第一個(gè)DMA接收節(jié)點(diǎn)的數(shù)據(jù)正常了。但奇怪的是第2個(gè)、第3個(gè)接收節(jié)點(diǎn)仍然接收不到數(shù)據(jù)。又是一番配置和代碼核對(duì),無(wú)果。
后來(lái),先干脆不管接收隊(duì)列,而試著直接啟動(dòng)USART3的DMA發(fā)送隊(duì)列。慶幸的是,發(fā)送很正常。看來(lái),USART3 TX DMA發(fā)送隊(duì)列的配置是靠譜的。其實(shí)USART3 RX DMA接收隊(duì)列的配置跟發(fā)送隊(duì)列配置很相似,只是請(qǐng)求源、傳輸端地址的配置差異。再次逐個(gè)核對(duì)DMA接收節(jié)點(diǎn)2和3的配置。功夫不負(fù)有心人,果真發(fā)現(xiàn)烏龍了。在DMA接收節(jié)點(diǎn)2的DMA請(qǐng)求配置那里,我竟然不小心選成USART1的了,一字之差,謬以千里。節(jié)點(diǎn)3的配置沒(méi)有問(wèn)題。看來(lái)問(wèn)題就出在接收節(jié)點(diǎn)2的那個(gè)誤選項(xiàng)。立馬驗(yàn)證,結(jié)果一切正常了。
為什么只是節(jié)點(diǎn)2配置有問(wèn)題,節(jié)點(diǎn)3也不能接收數(shù)據(jù)呢?因?yàn)檫@3個(gè)節(jié)點(diǎn)構(gòu)成了一條關(guān)于DMA傳輸?shù)臅r(shí)序、事件配合鏈,節(jié)點(diǎn)2沒(méi)有正常完成,節(jié)點(diǎn)3也沒(méi)法正常啟動(dòng)。
另外,還解釋一個(gè)變量。我在代碼里設(shè)置了RxComplete變量,是用來(lái)標(biāo)記DMA接收完成事件的,在主程序和中斷里都用到了。記得定義它時(shí)加上volatile,不然當(dāng)優(yōu)化等級(jí)調(diào)至較高時(shí),可能出現(xiàn)程序運(yùn)行異常情況。
OK,今天的DMA 鏈表功能的應(yīng)用演示就到分享這里,下次再聊。
審核編輯:湯梓紅
關(guān)鍵詞: