1. 程式人生 > >STM32串列埠USART用法的進階(標準庫函式版本)空閒中斷+DMA

STM32串列埠USART用法的進階(標準庫函式版本)空閒中斷+DMA

任務:配置串列埠,完成資料的收發。

方法1:普通操作----直接傳送&中斷接收

第0步:printf的準備

//加入以下程式碼,支援printf函式,而不需要選擇use MicroLIB	  百度搜索:半主機模式
#if 1 方法1
#pragma import(__use_no_semihosting)  //標準庫需要的支援函式 
                
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;      

int _sys_exit(int x) //定義_sys_exit()以避免使用半主機模式    
{ 
	x = x; 
} 

int fputc(int ch, FILE *f)//重定義fputc函式 
{      	
	USART1->DR = (u8) ch;  
	while((USART1->SR&0X40)==0){};//迴圈傳送,直到傳送完畢     
	return ch;
}


#else

/*使用microLib的方法*/ 需要KEIL打鉤!!!!
  
int fputc(int ch, FILE *f)
{
	 USART_SendData(USART1, (uint8_t) ch);
	 while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	   
         return ch;
}
#endif

第一步 :初始化

void uart_init(u32 bound)
{
GPIO_InitTypeDef  GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef  NVIC_InitStructure;
	 
//1時鐘	
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);

  
//2GPIO USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//複用推輓輸出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
		
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //3中斷  NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶佔優先順序3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子優先順序3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
  NVIC_Init(&NVIC_InitStructure);	//根據指定的引數初始化VIC暫存器

   //4配置 USART設定
  USART_InitStructure.USART_BaudRate = bound;//串列埠波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位資料格式
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體資料流控制
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收發模式
  USART_Init(USART1, &USART_InitStructure); //初始化串列埠1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串列埠接受中斷---------------------中斷
  USART_Cmd(USART1, ENABLE);                    //使能串列埠1 --------------------------使能
}

第二部:接收中斷

void USART1_IRQHandler(void)                	//串列埠1中斷服務程式
{
	u8 Res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
	{
	  Res =USART_ReceiveData(USART1);	//讀取接收到的資料
	  printf("%d ",Res );			 //傳送出去
	} 
} 

此時用PC和STM32可以串列埠實驗了。


方法2:進階操作----直接傳送&DMA+串列埠空閒中斷

問題引入:晚上看一篇文章https://mp.weixin.qq.com/s/2rjGwqQcbpDmeXaI1z0ULg

我的問題是:微控制器接收資料是串列埠中斷一個一個接收的,不會說我先拿到了20個100個再去解析,而是來一個解析一個,比如過來1 2 5 6我就不處理直接丟棄,而來了A我就馬克,啟動陣列把後面的裝起來。 我想問一下 你這種應用場合

刪除

作者你這種很明顯不適合大的資料,太過於佔用資源了,頻繁進入中斷沒必要,物聯網的話,一般是用dma+空閒中斷,在一系列資料提取我們需要的資料。

自己在查了查:https://blog.csdn.net/u011388550/article/details/49965117確實微信文章比較好

第0步不用改,

第1步需要修改中斷的配置

  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串列埠接受中斷
  USART_Cmd(USART1, ENABLE);                    //使能串列埠1 
////////////////////上面的刪除,修改為下面的/////////////

   USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);  
   USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);//-------USART_DMACmd(LUMMOD_UART, USART_DMAReq_Rx, ENABLE); // 開啟串列埠DMA接收
   USART_Cmd(USART1,ENABLE);
   DMA_init();//-------------------------------初始化DMA

完成這個函式 u8 receive_data[128];

void DMA_init(void)
{
   DMA_InitTypeDef    DMA_Initstructure;
   NVIC_InitTypeDef   NVIC_Initstructure;	
   /*開啟DMA時鐘*/
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
   	
   /*DMA配置*/
   DMA_Initstructure.DMA_PeripheralBaseAddr =  (u32)(&USART1->DR);;
   DMA_Initstructure.DMA_MemoryBaseAddr     = (u32)receive_data;
   DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC;
   DMA_Initstructure.DMA_BufferSize = 128;
   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_High;
   DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;
   DMA_Init(DMA1_Channel5,&DMA_Initstructure);
   
   //啟動DMA
   //DMA_Cmd(DMA1_Channel5,ENABLE);   
}
上面的意思是:外設USART的資料,搬運到記憶體也就是我定義的陣列receive_data,普通模式。

解釋DMA_Mode_Normal,當通道配置為非迴圈模式時,傳輸結束後(即傳輸計數變為0)將不再產生DMA操作。要開始新的DMA傳輸,需要3個步驟:在關閉DMA通道的情況下,在DMA_CNDTRx暫存器中重新寫入傳輸數目,然後重新開啟DMA。

第二部:重寫中斷服務函式

void USART1_IRQHandler(void)
{
    unsigned char num=0;
    if(USART_GetITStatus(USART1,USART_IT_IDLE) == SET)
    {
       num = USART1->SR;
       num = USART1->DR; //清USART_IT_IDLE標誌
       DMA_Cmd(DMA1_Channel5,DISABLE);    //關閉DMA
       num = 128 -  DMA_GetCurrDataCounter(DMA1_Channel5);      //得到真正接收資料個數  
       receive_data[num] = '\0';
       DMA1_Channel5->CNDTR=128;       //重新設定接收資料個數        DMA_Cmd(DMA1_Channel5,ENABLE);  //開啟DMA
       DMA_Cmd(DMA1_Channel5,ENABLE);  //開啟DMA
       printf("%s ",receive_data );		
    }
}

實驗效果


https://blog.csdn.net/u010001130/article/details/77816020

方法3:高階操作----上面的思路是PC傳送到STM32的USART,一旦資料停止USART就產生空閒中斷,於是DMA開始工作,把資料搬運到陣列中。我拿陣列處理完畢也就是傳送出來吧,就再次使能DMA。---在高階一點,DMA傳送完成中斷。(這個思路或許就不行 看看硬石那個5個PWM波)

遇到問題 :中斷進不去http://www.openedv.com/posts/list/15771.htm


但那是我的問題跟別人不同。

工程程式碼:https://pan.baidu.com/s/1-_dDc_l5dMI50Yc-c2vMbw

結論:我的思路就不對,過程是:PC傳送資料到STM32的USART,一旦資料到達RX的暫存器,DMA就會自動開始傳輸,而我的是DMA普通模式(記得硬石科技的5個PWM波嗎 就是DMA設計好的 CNT變成0的時候DMA中斷就來了),比如我設定的DMA緩衝陣列是128,而串列埠收到的報文是20,那麼現象就是你來一個我搬走一個,你再來一個我再搬走一個,(所以要求128>20),因為串列埠的波特率是固定的,所以20個以後就表示串列埠空閒了,即使再來報文也很難完成速率一樣,進入到空閒中斷服務函式,此時RX的資料早就已經到DMA搬運的終點啦!資料早就放置妥當了。所以,DMA中斷是不會觸發的,因為在128減少到0的時候才會觸發。(也可以留著,比如一個報文過來,長度是150,我在USART中斷之前先進去到DMA完成中斷)

++++有空研究傳送https://blog.csdn.net/MoWang_CZ/article/details/51459219