1. 程式人生 > >串列埠以DMA方式傳送(TX)資料

串列埠以DMA方式傳送(TX)資料

  實驗:控制串列埠一以DMA方式傳送(TX)資料

一、初始化DMA

       對STM32任何模組使用前都要對其初始化、首先就是初始化外設時鐘,檢視時鐘

數可知DMA時鐘由AHB得來。

初始化時鐘:RCC->AHBENR|=1<<0;

        在讀資料手冊可知:直接儲存器存取(DMA)用來提供在外設和儲存器之間或者儲存器和儲存器之間的高速資料傳輸。無須CPU干預,資料可以通過DMA快速地移動,這就節省了CPU的資源來做其他操作。 兩個DMA控制器有12個通道(DMA1有7個通道,DMA2有5個通道),每個通道專門用來管理來自於一個或多個外設對儲存器訪問的請求

。還有一個仲裁器來協調各個DMA請求的優先權。我們實驗用的是串列埠一、檢視外設與通道的對應關係如下:

所以我們初始化DMA1的第四通道。關於通道配置過程:

其中幾個簡單個人理解:

CPARx:就是串列埠傳送資料的暫存器地址;

CMARx:就是DMA傳輸的資料的地址;

CMDTRx:就是傳輸的資料大小 ,按位元組傳輸,傳輸後值遞減;

[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <span style="background-color: rgb(240, 240, 240);">void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)</span>{  
  2.     RCC->AHBENR|=1<<0;         //開啟DMA1時鐘
  3.     delay_ms(5);                //等待DMA時鐘穩定
  4.     DMA_CHx->CPAR=cpar;      //DMA1 外設地址 
  5.     DMA_CHx->CMAR=(u32)cmar;     //DMA1,儲存器地址
  6.     DMA1_MEM_LEN=cndtr;         //儲存DMA傳輸資料量
  7.     DMA_CHx->CNDTR=cndtr;        //DMA1,傳輸資料量
  8.     DMA_CHx->CCR=0X00000000; //復位
  9.     DMA_CHx->CCR|=1<<4;        //從儲存器讀
  10.     DMA_CHx->CCR|=0<<5;        //普通模式
  11.     DMA_CHx->CCR|=0<<6;        //外設地址非增量模式
  12.     DMA_CHx->CCR|=1<<7;        //儲存器增量模式
  13.     DMA_CHx->CCR|=0<<8;        //外設資料寬度為8位
  14.     DMA_CHx->CCR|=0<<10;       //儲存器資料寬度8位
  15.     DMA_CHx->CCR|=1<<12;       //中等優先順序
  16.     DMA_CHx->CCR|=0<<14;       //非儲存器到儲存器模式            
  17. }  
<span style="background-color: rgb(240, 240, 240);">void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)</span>{
	RCC->AHBENR|=1<<0;			//開啟DMA1時鐘
	delay_ms(5);				//等待DMA時鐘穩定
	DMA_CHx->CPAR=cpar; 	 	//DMA1 外設地址 
	DMA_CHx->CMAR=(u32)cmar; 	//DMA1,儲存器地址
	DMA1_MEM_LEN=cndtr;      	//儲存DMA傳輸資料量
	DMA_CHx->CNDTR=cndtr;    	//DMA1,傳輸資料量
	DMA_CHx->CCR=0X00000000;	//復位
	DMA_CHx->CCR|=1<<4;  		//從儲存器讀
	DMA_CHx->CCR|=0<<5;  		//普通模式
	DMA_CHx->CCR|=0<<6; 		//外設地址非增量模式
	DMA_CHx->CCR|=1<<7; 	 	//儲存器增量模式
	DMA_CHx->CCR|=0<<8; 	 	//外設資料寬度為8位
	DMA_CHx->CCR|=0<<10; 		//儲存器資料寬度8位
	DMA_CHx->CCR|=1<<12; 		//中等優先順序
	DMA_CHx->CCR|=0<<14; 		//非儲存器到儲存器模式		  	
}
[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. //開啟一次DMA傳輸
  2. void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)  
  3. {  
  4.     DMA_CHx->CCR&=~(1<<0);       //關閉DMA傳輸 
  5.     DMA_CHx->CNDTR=DMA1_MEM_LEN; //DMA1,傳輸資料量 
  6.     DMA_CHx->CCR|=1<<0;          //開啟DMA傳輸
  7. }  
//開啟一次DMA傳輸
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
	DMA_CHx->CCR&=~(1<<0);       //關閉DMA傳輸 
	DMA_CHx->CNDTR=DMA1_MEM_LEN; //DMA1,傳輸資料量 
	DMA_CHx->CCR|=1<<0;          //開啟DMA傳輸
}

初始化就基本完成。

在上面標識紅色字型可知,開始DMA傳輸必須要有外設請求:


那麼問題來了,請求訊號是什麼樣的呢?

我們檢視資料手冊串列埠找到如下內容:



請求訊號:就是配置USART_CR3,在實驗中配置USART1->CR3=1<<7;

主函式如下:

[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. #define SEND_BUF_SIZE 1200
  2. u8 SendBuff[SEND_BUF_SIZE]; //傳送資料緩衝區
  3. const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串列埠實驗"};      
  4. int main(void)  
  5. {     
  6.     u16 i;  
  7.     u8 t=0;  
  8.     u8 j,mask=0;  
  9.     Stm32_Clock_Init(9);    //系統時鐘設定
  10.     uart_init(72,115200);   //串列埠初始化為115200
  11.     delay_init(72);         //延時初始化 
  12.     KEY_Init();             //按鍵初始化         
  13.     MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA1通道4,外設為串列埠1,儲存器為SendBuff,長度SEND_BUF_SIZE.
  14.     j=sizeof(TEXT_TO_SEND);      
  15.     for(i=0;i<SEND_BUF_SIZE;i++)//填充資料到SendBuff
  16.     {  
  17.         if(t>=j)//加入換行符
  18.         {  
  19.             if(mask)  
  20.             {  
  21.                 SendBuff[i]=0x0a;  
  22.                 t=0;  
  23.             }else
  24.             {  
  25.                 SendBuff[i]=0x0d;  
  26.                 mask++;  
  27.             }     
  28.         }else//複製TEXT_TO_SEND語句
  29.         {  
  30.             mask=0;  
  31.             SendBuff[i]=TEXT_TO_SEND[t];  
  32.             t++;  
  33.         }            
  34.     }          
  35.     i=0;  
  36.     while(1)  
  37.     {  
  38.         t=KEY_Scan(0);  
  39.         if(t==KEY0_PRES)//KEY0按下
  40.         {  
  41.             USART1->CR3=1<<7;           //使能串列埠1的DMA傳送       
  42.             MYDMA_Enable(DMA1_Channel4);//開始一次DMA傳輸!      
  43.             //等待DMA傳輸完成,此時我們來做另外一些事,點燈
  44.             //實際應用中,傳輸資料期間,可以執行另外的任務
  45.             while(1)  
  46.             {  
  47.             <span style="color:#ff0000;"if(DMA1->ISR&(1<<13))//等待通道4傳輸完成
  48.                 {  
  49.                     DMA1->IFCR|=1<<13;//清除通道4傳輸完成標誌
  50.                     break;</span>   
  51.              }  
  52.         }  
  53.   }  
  54. }  
  55. }  
#define SEND_BUF_SIZE 1200

u8 SendBuff[SEND_BUF_SIZE];	//傳送資料緩衝區
const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串列埠實驗"};	 

int main(void)
{	
	u16 i;
	u8 t=0;
	u8 j,mask=0;
 	Stm32_Clock_Init(9);	//系統時鐘設定
	uart_init(72,115200);	//串列埠初始化為115200
	delay_init(72);	   	 	//延時初始化 
	KEY_Init();				//按鍵初始化		 	
 	MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA1通道4,外設為串列埠1,儲存器為SendBuff,長度SEND_BUF_SIZE.
	j=sizeof(TEXT_TO_SEND);	   
	for(i=0;i<SEND_BUF_SIZE;i++)//填充資料到SendBuff
    {
		if(t>=j)//加入換行符
		{
			if(mask)
			{
				SendBuff[i]=0x0a;
				t=0;
			}else 
			{
				SendBuff[i]=0x0d;
				mask++;
			}	
		}else//複製TEXT_TO_SEND語句
		{
			mask=0;
			SendBuff[i]=TEXT_TO_SEND[t];
			t++;
		}    	   
    }		 
	i=0;
	while(1)
	{
		t=KEY_Scan(0);
		if(t==KEY0_PRES)//KEY0按下
		{
		    USART1->CR3=1<<7;           //使能串列埠1的DMA傳送       
			MYDMA_Enable(DMA1_Channel4);//開始一次DMA傳輸!	  
		    //等待DMA傳輸完成,此時我們來做另外一些事,點燈
		    //實際應用中,傳輸資料期間,可以執行另外的任務
		    while(1)
		    {
			<span style="color:#ff0000;">	if(DMA1->ISR&(1<<13))//等待通道4傳輸完成
				{
					DMA1->IFCR|=1<<13;//清除通道4傳輸完成標誌
					break;</span> 
		     }

		}
  }
}
}
[cpp] view plain copy print?

如程式紅色部分 傳輸過程出現錯誤或者傳送完成我們可以設定標誌位來提示:


這些標誌位都在中斷暫存器中:

我們用的是第四通道,所以是13位完成標誌位,完成後再清掉中斷可以接受下一次中斷,清中斷暫存器DMA_IFCR與ISR的位對應,這裡就不解釋了