在使用STM32的UART的DMA功能總結如下:
首先上代碼,這里采用STM32 的USART1作為Demo,RX的DMA為DMA1_Channel5,TX的DMA為DMA1_Channel4.初始化如下,紅色的標記需要注意:
RX-DMA初始化
1 // DMA Rx 2 USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); 3 DMA_Cmd(DMA1_Channel5,DISABLE); 4 DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)(&USART1->DR); 5 DMA_InitStruct.DMA_MemoryBaseAddr = (u32)RxBuf0; 6 DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; 7 DMA_InitStruct.DMA_BufferSize = 10; 8 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 9 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; 10 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 11 DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; 12 DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; 13 DMA_InitStruct.DMA_Priority = DMA_Priority_High; 14 DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; 15 DMA_Init(DMA1_Channel5,&DMA_InitStruct); 16 DMA_Cmd(DMA1_Channel5,ENABLE);
中斷的初始化如下:
DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE); NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel5_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); //ENABLE DMA TX ISR DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel4_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct);
TX發送函數如下:
1 void USART1_SendDMA(uint8_t* buf,int len) 2 { 3 DMA_InitTypeDef DMA_InitStruct;
5 DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); 6 7 DMA_Cmd(DMA1_Channel4,DISABLE);
8 9 DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)(&USART1->DR); 10 DMA_InitStruct.DMA_MemoryBaseAddr = (u32)buf; 11 DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; 12 DMA_InitStruct.DMA_BufferSize = len; 13 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 14 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; 15 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 16 DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; 17 DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; 18 DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh; 19 DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; 20 DMA_Init(DMA1_Channel4,&DMA_InitStruct); 21 22 DMA_Cmd(DMA1_Channel4,ENABLE); 23 }
兩個服務函數如下:
RX-DMA中斷函數,實現RX的雙緩沖區功能。值得注意的是,你的DMA設置成Normal模式的時候,在中斷函數中就得設置DMABufferSize,應為Normal模式完成一次傳輸之后,BufferSize直接清零,設置成Circle模式就不會存在這個問題,下面的中斷就是設置成Circle模式的。
void DMA1_Channel5_IRQHandler() { if(DMA_GetITStatus(DMA1_IT_TC5)) { if(using_buf0 ==0) { DMA1_Channel5->CMAR = (u32)RxBuf0; using_buf0 = 1; } else { DMA1_Channel5->CMAR = (u32)RxBuf1; using_buf0 = 0; } recv_flag = 1; DMA_ClearITPendingBit(DMA1_IT_TC5); } }
TX-DMA中斷函數
void DMA1_Channel4_IRQHandler() { if(DMA_GetITStatus(DMA1_IT_TC4)) { //TODO:Add code here DMA_ClearITPendingBit(DMA1_IT_TC4); } }
幾點說明
1)這里采用雙緩沖區的方式,對於大數據量的時候是很有效的,在處理一個緩沖區數據的時候,另外一個緩沖區能夠正常接收數據
2)TX的DMA中斷,可以采用類似於操作系統中互斥量的操作,當一個數組還在發送的時候,另外一個數組如果檢測到一個在發送,則不能夠進行發送,不然這樣子數據會亂掉;
3)如果DMA接收想采用循環緩沖區的方式,可以直接將RX-DMA設置成Circle方式,然后數據就會硬件上自動實現環形緩沖區的功能,省了不少時間。
4)DMA在采用Normal模式的時候,當一次任務完成后,DMA->DMA_BufferSize自動清零,並且DMA自動停止。如果想再次設置DMA的BufferSize的話,必須要進行如下操作:
step1:DMA_CMD(DMAx_Channely,DISABLE);
step2: 設置DMA_BufferLen
step3:DMA_CMD(DMAx_Channely,ENABLE)
5)DMA采用Circle模式的時候,在發送或者接受完成之后,仍然保存着BufferSize,並且DMA還處於使能狀態,一直連續工作,直到用戶停止DMA