stm32串口接收/發送+DMA內存到外設


簡要說一下實驗目的:上位機給單片機發送數據,單片機使用串口接收中斷接收。在接收中斷中,串口向DMA控制器發送請求,把內存中的數據發送到串口的DR寄存器(發送到上位機)

1.串口的基本配置配置略過,需要注意的是打開串口的接收中斷,編寫接收中斷函數

  串口接收中斷的NVIC配置

 1   /* 配置USART為中斷源 */
 2   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
 3   /* 搶斷優先級*/
 4   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
 5   /* 子優先級 */
 6   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
 7   /* 使能中斷 */
 8   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 9   /* 初始化配置NVIC */
10   NVIC_Init(&NVIC_InitStructure);
11   USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

  串口中斷函數

 1 void USART1_IRQHandler(void)
 2 {
 3   if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)//串口接收中斷
 4 { 
 5   USART_ClearITPendingBit(DEBUG_USARTx,USART_IT_RXNE);
 6   printf("usart\n");
 7  
 8   DMA_Cmd(DMA1_Channel4,ENABLE);//串口向dma發送請求
 9   DMA_ClearFlag(DMA1_FLAG_GL4);
10  
11   //DMA1_Channel4->CNDTR = SENDBUFF_SIZE;
12  
13 USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);
14 }
15 }

2.DMA 配置

 1 void USARTx_DMA_Config(void)
 2 {
 3         DMA_InitTypeDef DMA_InitStructure;
 4         NVIC_InitTypeDef NVIC_InitStructure;     /* Configure one bit for preemption priority */
 5     
 6         // 開啟DMA時鍾
 7         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 8         // 設置DMA源地址:串口數據寄存器地址*/
 9     DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
10         // 內存地址(要傳輸的變量的指針)
11         DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
12         // 方向:從內存到外設    
13         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
14         // 傳輸大小    
15         DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
16         // 外設地址不增        
17         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
18         // 內存地址自增
19         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
20         // 外設數據單位    
21         DMA_InitStructure.DMA_PeripheralDataSize = 
22       DMA_PeripheralDataSize_Byte;
23         // 內存數據單位
24         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     
25         // DMA模式,一次或者循環模式
26         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
27         //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;    
28         // 優先級:中    
29         DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
30         // 禁止內存到內存的傳輸
31         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
32         // 配置DMA通道           
33         DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);        
34         
35         
36         //DMA中斷的NVIC
37     //    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);     
38         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;     
39         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
40         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
41         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          
42         NVIC_Init(&NVIC_InitStructure);
43         DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  //配置DMA發送完成后產生中斷
44         DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);  //配置DMA發送完成后產生中斷
45 //        USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
46             //DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
47         // 使能DMA
48         DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
49 }

    DMA中斷函數

 1 void DMA1_Channel4_IRQHandler(void)
 2 {
 3     if(DMA_GetITStatus(DMA1_IT_TC4)==SET)
 4     {
 5         LED1(1);
 6             printf("d");
 7         DMA_ClearITPendingBit(DMA1_IT_TC4); //清除全部中斷標志
 8             DMA_ClearFlag(DMA1_FLAG_GL4);
 9             DMA_Cmd(DMA1_Channel4,DISABLE); 
10 //            DMA_Cmd(DMA1_Channel4,DISABLE);
11     }
12         if(DMA_GetITStatus(DMA1_IT_HT4)==SET)
13     {
14         LED1(1);
15             printf("h");
16         DMA_ClearITPendingBit(DMA1_IT_HT4); //清除全部中斷標志
17     }
18 }

3.結果

串口接收中斷中 向上位機發送“usart”,然后發送DMA請求,把內存中的數據“p”發送到串口(外設,由上位機接收)。DMA半傳輸完成中斷中向上位機發送“h”,DMA全部完成傳輸中斷中向上位機發送“d”。

下圖為實驗結果

 

經過多次調試,發現了幾個問題

問題1:如果DMA發送了N個數據,上位機接收過程中總會少2個數據。缺少的位置是h和d的位置。

問題2:在串口接收中斷中 使用USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE); 串口向DMA發送請求 發送數據時,只會在第一次進入接收中斷里發生DMA傳輸。第二次往后都只會進串口接收中斷,不會有DMA傳輸。DMA設置為normal模式,按照網上的說法,清中斷DMA_ClearITPendingBit(DMA1_IT_TC4),關DMA,以及在第二次傳輸前設置數據大小//DMA_SetCurrDataCounter(DMA1_Channel4,SENDBUFF_SIZE); (或者DMA1_Channel4->CNDTR = SENDBUFF_SIZE;)以及修改串口中斷和DMA中斷的NVIC優先級都不會有第二次傳輸。嘗試了這些方法都不能正常啟動DMA第二次傳輸。暫時先記錄一下,解決了再來寫。###:解決了,只用修改一下DMA_Mode即可。DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 之前是DMA_InitStructure.DMA_Mode = DMA_Mode_Normal。感謝一位熱心網友。

 其他問題和總結(自己總結的,理解的不到位的請大佬指正):

1.打開了串口接收和發送完成中斷。在串口接收中斷中 使用串口發送數據,有時候會出現接收到一個字符,多次進入接收中斷,查找資料,可能存在中斷嵌套,后來只在串口接收中斷中接收,添加了一個按鍵,用按鍵去輸出接收到的數據。這個沒有問題。(在使用printf時,一定要勾選microlib(MDK))

2.關於串口接收中斷和發送中斷的問題。經過查找啟動文件,發現在NVIC設置中斷時只能設置串口中斷(不區分時接收還是發送完成)NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;區分接收還是發送是在void USART1_IRQHandler(void) 中字節去判斷是哪個標志位。那可能串口的接收中斷和發送完成中斷的優先級是一樣的。

3.關於DMA的中斷問題。DMA不是不需要cpu參與嗎?為什么還要設置中斷。DMA的傳輸數據過程是不需要cpu的,但是DMA有3個中斷:半傳輸完成DMA1_IT_HT4、全部傳輸完成DMA1_IT_TC4、傳輸錯誤DMA1_IT_TE4。當DMA在傳輸數據完成一半、全部、出錯時,需要向cpu發送中斷。一樣的,這三個中斷也需要設置NVIC。和其他中斷是一樣的(比如:定時器中斷、串口中斷)。

 打開DMA中的函數:

DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  //配置DMA發送完成后產生中斷
DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);  //配置DMA發送完成后產生中斷

4.DMA1有7個通道,DMA2有5個,每個通道都是固定的和某個外設連接,比如要使用存儲器到外設,外設到存儲器模式,像上面的串口。要使用串口外設,該怎么選DMA通道,要查中文參考手冊。存儲器到存儲器之間通道不固定,自選任選。

 

 

 5. 關於設置順序,每個外設都有對應的通道,那到底是先設置外設呢,還是先設置通道。先設置通道,當需要開啟DMA時,用對應外設去發請求。設置通道的函數在DMA庫中,發送請求的函數在各個外設的庫中。也就是說當外設發送DMA請求時,該外設對應的DMA通道一定是配置好的。

__STM32F10x_DMA_H頭文件中:413行的函數 參數是 哪個通道的初始化和 通道初始化結構體

 

IIC頭文件中:537行的函數 參數是 哪個外設的DMA請求和ENABLE

 

 

串口頭文件中:372行的函數 參數是 哪個外設的DMA請求和ENABLE

 

 

6.每個通道都包括多個中斷比如:半傳輸完成DMA1_IT_HT4、全部傳輸完成DMA1_IT_TC4、傳輸錯誤DMA1_IT_TE4。那這3個中斷的優先級也不能單獨配置。因為每個通道的中函數名是void DMA1_Channel4_IRQHandler(void)這種類型的。從名字上能看出通道上的中斷是針對通道的,不針對具體中斷。就和串口中斷一樣,是接收中斷還是發送上完成中斷,自己在中斷函數中判斷去。

 7.第6條是同一通道中的不同中斷,那DMA1有7個通道,不同通道中的中斷能設置不同優先級嗎?DMA也是一個外設,如果DMA1的7個通道(7個外設)同時要請求DMA,那么DMA先處理哪個通道呢?通過觀看通道Init函數,發現DMA_InitStructure結構體中有個變量DMA_InitStructure.DMA_Priority是設置優先級的,優先級可選項有4種:DMA_Priority_VeryHigh、DMA_Priority_High、DMA_Priority_Medium、DMA_Priority_Low。那么就是DMA中的不同通道是可以設置不同的優先級的。如果兩個通道優先級一樣。那通道號越小優先級越高(通道0>通道1)

8.在使用DMA時只要初始化DMA_InitStructure結構體中的所有變量和NVIC即可

 1 DMA_InitTypeDef DMA_InitStructure;
 2         NVIC_InitTypeDef NVIC_InitStructure;     /* Configure one bit for preemption priority */
 3     
 4         // 開啟DMA時鍾
 5         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 6         // 設置DMA源地址:串口數據寄存器地址*/
 7     DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
 8         // 內存地址(要傳輸的變量的指針)
 9         DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
10         // 方向:從內存到外設    
11         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
12         // 傳輸大小    
13         DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
14         // 外設地址不增        
15         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
16         // 內存地址自增
17         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
18         // 外設數據單位    
19         DMA_InitStructure.DMA_PeripheralDataSize = 
20       DMA_PeripheralDataSize_Byte;
21         // 內存數據單位
22         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     
23         // DMA模式,一次或者循環模式
24         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
25         //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;    
26         // 優先級:中    
27         DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
28         // 禁止內存到內存的傳輸
29         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
30         // 配置DMA通道           
31         DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);        
32         
33         
34     
35     //    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);     
36         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;     
37         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
38         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
39         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          
40         NVIC_Init(&NVIC_InitStructure);
41         DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  //配置DMA發送完成后產生中斷
42         DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);  //配置DMA發送完成后產生中斷
43 //        USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
44             //DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
45         // 使能DMA
46         DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM