思考再三:終究是要拿出一些干貨--單片機基礎核心代碼,串口的高效率使用請這里開始。--舉一反三,我只列出串口一的雙dma緩沖應用范例,剩下的自己擴展。並給與了我迄今覺得最好的串口配置架構-感謝野火的高質量代碼
#include "sys.h" #include "usart.h" #include "string.h" #include "stdio.h" #include "stdlib.h" //藍牙 ////////////////////////////////////////////////////////////////////////////////// uart_find uart1s;//定義串口一的接受處理結構體 //局部定義以便實現模塊化 //USART #define USART USART1 #define USART_CLK RCC_APB2Periph_USART1 #define USART_CLK_SET RCC_APB2PeriphClockCmd #define USART_GPIO_CLK_SET RCC_APB2PeriphClockCmd #define USART_RX_GPIO_PORT GPIOA #define USART_RX_GPIO_CLK RCC_APB2Periph_GPIOA #define USART_RX_PIN GPIO_Pin_10 #define USART_RX_IRQ USART1_IRQn #define USART_RX_IRQ_HANDLER USART1_IRQHandler //USART3_IRQHandler #define USART_TX_GPIO_PORT GPIOA #define USART_TX_GPIO_CLK RCC_APB2Periph_GPIOA #define USART_TX_PIN GPIO_Pin_9 #define USART_BAUDRATE 115200 //DMA tx #define USART_DR_BASE (USART1_BASE+0x04) // 0x40013800 + 0x04 = 0x40013804,串口數據寄存器地址 #define SEND_BUFF_SIZE 128 //發送的數據量,SEND_BUFF_SIZE * DMA_MemoryDataSize #define USART_TX_DMA_CLK RCC_AHBPeriph_DMA1 #define USART_TX_DMA_CHANNEL DMA1_Channel4 #define USART_TX_DMA_IRQ DMA1_Channel4_IRQn//中斷接口 #define USART_TX_DMA_IRQ_HANDLER DMA1_Channel4_IRQHandler//中斷接口 #define USART_Tx_DMA_FLAG DMA1_FLAG_GL4 //錯誤標志 #define USART_Tx_ERR_DMA_FLAG DMA1_FLAG_GL4 //DMA rx #define USART_DR_BASE (USART1_BASE+0x04) // 0x40013800 + 0x04 = 0x40013804,串口數據寄存器地址 #define REC_BUFF_SIZE 128 //發送的數據量,SEND_BUFF_SIZE * DMA_MemoryDataSize #define USART_RX_DMA_CLK RCC_AHBPeriph_DMA1 #define USART_RX_DMA_CHANNEL DMA1_Channel5 #define USART_RX_DMA_IRQ DMA1_Channel5_IRQn//中斷接口 #define USART_RX_DMA_IRQ_HANDLER DMA1_Channel5_IRQHandler//中斷接口 #define USART_Rx_DMA_FLAG DMA1_FLAG_GL5 //錯誤標志 #define USART_Rx_ERR_DMA_FLAG DMA1_FLAG_GL5 static uint8_t SendBuff[SEND_BUFF_SIZE];//發送測試緩沖 static uint8_t rec_by=0;//緩沖位置 static uint8_t recBuff[REC_BUFF_SIZE]; //接受測試緩沖 臨時緩沖 static uint8_t recBuff1[REC_BUFF_SIZE]; //接受測試緩沖 static uint8_t recBuff2[REC_BUFF_SIZE]; //接受測試緩沖 static u16 rec_counter=0; //static uint8_t *send_buff = SendBuff; //鎖定發送地址 //static uint8_t *rec_buff1 = recBuff1; //鎖定接收地址1 //static uint8_t *rec_buff2 = recBuff2; //鎖定接收地址2 static void uart_dma_send(u8 *buff,u8 len); static void USART_DMA_Config(void); static void user_heander(void); static void USART_Config(void); /********************************** 定義函數接口 ************************************/ void blue_uart_int(void) { uart1s.uart_send = uart_dma_send; uart1s.uart_rxbuf_len = &rec_counter; uart1s.uart_init = USART_Config; //初始化 uart1s.uart_init(); uart1s.uart_rxbuf1 = recBuff1; uart1s.uart_rxbuf2 = recBuff2; } /** * @brief USART GPIO 配置,工作模式配置。115200 8-N-1 * @param 無 * @retval 無 */ static void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /*配置使用DMA模式*/ USART_DMA_Config(); USART_GPIO_CLK_SET( USART_RX_GPIO_CLK|USART_TX_GPIO_CLK, ENABLE); /* Enable UART clock */ USART_CLK_SET(USART_CLK, ENABLE); //時鍾將會出現大問題 USART_DeInit(USART); //復位串口 /* Configure USART Tx as alternate function */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出 GPIO_InitStructure.GPIO_Pin = USART_TX_PIN ; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(USART_TX_GPIO_PORT, &GPIO_InitStructure); /* Configure USART Rx as alternate function */ GPIO_InitStructure.GPIO_Pin = USART_RX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入 GPIO_Init(USART_RX_GPIO_PORT, &GPIO_InitStructure); // /* Enable the DMA Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART_RX_IRQ; // 發送DMA通道的中斷配置 滿中斷 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; // 優先級設置 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* USART mode config */ USART_InitStructure.USART_BaudRate = USART_BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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(USART, &USART_InitStructure); USART_Cmd(USART, ENABLE); // USART_ITConfig(USART, USART_IT_RXNE, ENABLE);//開啟中斷 // USART_ITConfig(USART, USART_IT_IDLE, ENABLE); // 開啟 串口空閑IDEL 中斷 /* Enable USARTy DMA TX request */ USART_ITConfig(USART,USART_IT_TC,DISABLE); // USART_DMACmd(USART, USART_DMAReq_Tx, ENABLE); // 開啟串口DMA發送 這兩個不能隨便開啟 開啟就是發送 USART_DMACmd(USART, USART_DMAReq_Rx, ENABLE); // 開啟串口DMA接收 接受可以提前開啟 USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE); // 開啟串口DMA發送 printf("串口2初始化\r\n"); } /** * @brief USART1 TX DMA 配置,內存到外設(USART1->DR) * @param 無 * @retval 無 */ static DMA_InitTypeDef DMA_InitStructure1; static void USART_DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure; /*usart1 tx對應dma2,通道4,數據流7*/ /*開啟DMA時鍾*/ RCC_AHBPeriphClockCmd(USART_TX_DMA_CLK, ENABLE); DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE); // 關DMA通道 DMA_DeInit(USART_TX_DMA_CHANNEL); // 恢復缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART->DR);// 設置串口發送數據寄存器 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; // 設置發送緩沖區首地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 設置外設位目標,內存緩沖區 -> 外設寄存器 DMA_InitStructure.DMA_BufferSize = uart1s.uart_txbuf_len; // 需要發送的字節數,這里其實可以設置為0,因為在實際要發送的時候,會重新設置次值 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外設地址不做增加調整,調整不調整是DMA自動實現的 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 內存緩沖區地址增加調整 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外設數據寬度8位,1個字節 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 內存數據寬度8位,1個字節 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 單次傳輸模式 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // 優先級設置 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 關閉內存到內存的DMA模式 DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure); // 寫入配置 DMA_ClearFlag(USART_Tx_DMA_FLAG); // 清除DMA所有標志 DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE); // 關閉DMA DMA_ITConfig(USART_TX_DMA_CHANNEL, DMA_IT_TC, ENABLE); //中斷配置 DMA_ITConfig(USART_TX_DMA_CHANNEL, DMA_IT_TC, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; /* Enable the DMA Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART_TX_DMA_IRQ; // 發送DMA通道的中斷配置 滿中斷 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; // 優先級設置 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /**************************************************************/ //****************************配置接收 /*--- LUMMOD_UART_Rx_DMA_Channel DMA Config ---*/ //啟動DMA時鍾 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE); // 關DMA通道 DMA_DeInit(USART_RX_DMA_CHANNEL); // 恢復缺省值 DMA_InitStructure1.DMA_PeripheralBaseAddr = (uint32_t)(&USART->DR);// 設置串口接收數據寄存器 DMA_InitStructure1.DMA_MemoryBaseAddr = (uint32_t)recBuff1; // 設置接收緩沖區首地址 DMA_InitStructure1.DMA_DIR = DMA_DIR_PeripheralSRC; // 設置外設為數據源,外設寄存器 -> 內存緩沖區 DMA_InitStructure1.DMA_BufferSize = *uart1s.uart_rxbuf_len; // 需要最大可能接收到的字節數 不能為零 DMA_InitStructure1.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外設地址不做增加調整,調整不調整是DMA自動實現的 DMA_InitStructure1.DMA_MemoryInc = DMA_MemoryInc_Enable; // 內存緩沖區地址增加調整 DMA_InitStructure1.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外設數據寬度8位,1個字節 DMA_InitStructure1.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 內存數據寬度8位,1個字節 DMA_InitStructure1.DMA_Mode = DMA_Mode_Normal; // 單次傳輸模式 DMA_InitStructure1.DMA_Priority = DMA_Priority_VeryHigh; // 優先級設置 DMA_InitStructure1.DMA_M2M = DMA_M2M_Disable; // 關閉內存到內存的DMA模式 DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure1); // 寫入配置 DMA_ClearFlag(USART_Rx_DMA_FLAG); // 清除DMA所有標志 DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE); // 開啟接收DMA通道,等待接收數據 //DMA_ITConfig(LUMMOD_UART_Rx_DMA_Channel, DMA_IT_TC, ENABLE); // 開啟接收完成DMA通道中斷 /* Enable the DMA Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART_RX_DMA_IRQ; // 發送DMA通道的中斷配置 滿中斷 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; // 優先級設置 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /********************************* dma 發送 ***********************************/ static void uart_dma_send(u8 *buff,u8 len) { memset(SendBuff,0,SEND_BUFF_SIZE); memcpy(SendBuff,buff,len); DMA_SetCurrDataCounter(USART_TX_DMA_CHANNEL,len); //數據傳輸量 DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE); //開啟DMA傳輸 /* USART1 向 DMA發出TX請求 */ USART_DMACmd(USART, USART_DMAReq_Tx, ENABLE);//開始發送 } /*********************************************END OF FILE**********************/ /*下面代碼我們直接把中斷控制邏輯寫在中斷服務函數內部。*/ //DMA傳輸接收完成中斷 void USART_RX_DMA_IRQ_HANDLER(void) { if(DMA_GetITStatus(USART_Rx_DMA_FLAG) != RESET) { DMA_ClearITPendingBit(USART_Rx_DMA_FLAG); // Dma_FreeBuf_Ok = 1;//有准備好的數據了 //數據溢出會出現在這里 } else { DMA_ClearFlag(USART_Rx_ERR_DMA_FLAG); } } /********************* 參數說明: dma串口通道切換 **********************/ static void buf_chn2(void) { if(rec_by==0) { rec_by=1; DMA_InitStructure1.DMA_MemoryBaseAddr = (uint32_t)recBuff2; // 設置接收緩沖區首地址 DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure1); // 寫入配置 } else { rec_by=0; DMA_InitStructure1.DMA_MemoryBaseAddr = (uint32_t)recBuff1; // 設置接收緩沖區首地址 DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure1); // 寫入配置 } DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE); } //串口1中斷服務程序 void USART_RX_IRQ_HANDLER(void) { u8 res; if(USART_GetITStatus(USART, USART_IT_RXNE) != RESET)//接收到數據 { res=USART->DR; //讀取就是清空 // // while((USART1->SR&0X40)==0);//等待發送結束 // USART1->DR=res; } else if(USART_GetITStatus(USART, USART_IT_IDLE) != RESET)//空閑中斷 { u8 clear; clear=USART->SR;//讀SR寄存器 clear=USART->DR;//讀DR寄存器 清空數據 /*******************************/ DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE); // 關閉DMA ,防止干擾 DMA_ClearFlag( USART_Rx_ERR_DMA_FLAG ); // 清DMA標志位 rec_counter = REC_BUFF_SIZE - DMA_GetCurrDataCounter(USART_RX_DMA_CHANNEL); //獲得接收到的字節數 USART_RX_DMA_CHANNEL->CNDTR = REC_BUFF_SIZE; // 重新賦值計數值,必須大於等於最大可能接收到的數據幀數目 //開始切換buff buf_chn2(); // printf("當前是通道二:%s\r\n",recBuff1); DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE); //數據轉移 memset(recBuff,0,REC_BUFF_SIZE); if(rec_by==1) { memcpy(recBuff,recBuff1,REC_BUFF_SIZE); memset(recBuff1,0,REC_BUFF_SIZE); } else { memcpy(recBuff,recBuff2,REC_BUFF_SIZE); memset(recBuff2,0,REC_BUFF_SIZE); } user_heander(); //cc3200_rec(); //some things doing /* DMA 開啟,等待數據。注意,如果中斷發送數據幀的速率很快,MCU來不及處理此次接收到的數據,中斷又發來數據的話,這里不能開啟,否則數據會被覆蓋。有2種方式解決。 1. 在重新開啟接收DMA通道之前,將LumMod_Rx_Buf緩沖區里面的數據復制到另外一個數組中,然后再開啟DMA,然后馬上處理復制出來的數據。 2. 建立雙緩沖,在LumMod_Uart_DMA_Rx_Data函數中,重新配置DMA_MemoryBaseAddr 的緩沖區地址,那么下次接收到的數據就會保存到新的緩沖區中,不至於被覆蓋。*/ //注意雙緩沖:只需要重新配置,數據地址即可 } } //dma2 7 tx 發送完成中斷實驗 void USART_TX_DMA_IRQ_HANDLER(void) { if(DMA_GetITStatus(USART_Tx_DMA_FLAG)!=RESET) { DMA_ClearFlag(USART_Tx_DMA_FLAG); // 清除標志 DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE); // 關閉DMA通道 } else { DMA_ClearFlag(USART_Tx_ERR_DMA_FLAG); // 清除所有錯誤標志 } } /******************************** 用戶處理程序 **********************************/ static void user_heander(void) { // your code //rest printf("your printf : %s",recBuff); }
#ifndef __USART_H #define __USART_H #include "stdio.h" #include "sys.h" typedef struct { uint8_t * uart_rxbuf1;//接收緩存1 uint8_t * uart_rxbuf2;//接收緩存1 uint8_t * uart_rxbuf;//接收緩存 u16 *uart_rxbuf_len; //uint8_t rxbuf_counter;//接收位置 uint8_t *uart_txbuf;//接收緩存 uint8_t txbuf_counter;//接收位置 u16 uart_txbuf_len; u8 buff_by;//雙緩沖標志 uint8_t recby;//接收標志 uint8_t clear;//清除標志 void (* uart_init)(void); //初始化函數 void (* uart_send)(u8 *buff,u8 len); //發送函數 }uart_find; extern uart_find uart1s;//定義串口一的接受處理結構體 ////////////////////uart2 extern uart_find uart2s;//定義串口一的接受處理結構體 ///////////////////uart3////////////////////////////// extern uart_find uart3s;//定義串口一的接受處理結構體 void uart_int(void); void wifi_uart_int(void); void debug_uart_int(void); void blue_uart_int(void); //void USART_Config_test(void); u8 String_match(u8 *str_aim,u8 *str_buf,u8 len,u8 len1);//數據匹配函數 u8 String_collect(u8 * aim_buf,u8 *buff,u8 start); #endif
