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; }
第一步 :初始化
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
自己在查了查: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