最近重新開始學STM32單片機,搞到串口 DMA 的時候, 數據讀取卡了很長一段時間,最終,功夫不負有心人終于搞出來了。在此以記錄一下,方便以后查詢使用。
在調試的過程中也遇到了很多bug,有些簡直就是低級問題,但是還是卡了很久,在此寫出來給自己加深印象,同時已給后來者一個前車之鑒
1、在32的程序編寫中,若使用到了中斷部分,特別是中斷函數一定要注意,查詢清除中斷標志位到底是什么。
獲取中斷標志位 是 USART_GetITStatus 函數,筆者就是沒弄清 用成了 USART_ GetFlagStatus 函數,
USART_GetITStatus 檢查指定的 USART 中斷發生與否
USART_ GetFlagStatus 檢查指定的 USART 標志位設置與否
就是這個問題卡了筆者很久,甚至筆者檢查了很多次都沒注意到
2、中斷函數的使用, 這個在使用時若不注意就會用錯,比如 中斷串口2 ,卻使用成了 中斷串口1
關于中斷函數的查詢可以通過kile5 Startup文件夾 下的 startup_stm32f10x_md.s 進行查詢
3、代碼功能獨立
這部分主要就是要將各函數功能進行,獨立。方便理清邏輯,可能很多人覺得很麻煩,且很多人沒有此習慣,但是在學習和工作中稍微花點時間,將其進行整理還是很有必要的。
筆者在調試這部分時,就出現了邏輯混亂的情況,將 GPIO, 串口,DMA 全部寫在一起,導致最后混亂。最終每次只會搬運最后一個字符。具體原因筆者也沒搞清楚。若有大佬能夠了解,望指點一二。
—————————————————————分割線———————————————
以上廢話,下為正文
DMA 串口數據讀取主要就是 將串口的數據搬移到內存中
在實際代碼中,筆者定義了一個char 數組進行存儲。并通過重定義printf 的方式將 char 數組內的內容輸出出來(后面看大家需不需要,也可以將DMA 數據發送的代碼 分享出來 嘿嘿嘿)。
主要步驟如下
1、選擇使用的串口(串口1)
2、查詢該串口對應的引腳及DMA通道(A9,A10, DMA1chanel4, DMA1chanel5)Tip:可以通過STM32參考手冊查詢,后面會放的(感謝正點原子)
3、配置串口1 GPIO
4、配置串口1 USART 及 中斷
5、配置串口1 對應 DMA
著重需要注意的是:這里使用的是 串口1 的 空閑中斷?臻e中斷在設備接收到數據后 一個字節內的時間還是為接收到數據就會產生,在清除之后,當下一次接收到數據之后的1個字節時間內才會再次產生。 說人話就是,正常情況下不會產生,只有在接收數據后,沒有數據了,才會再次產生。
此處使用串口 的空閑中斷可以實現 接收不定長數據,若使用的是 定長數據 就可以使用DMA 的接收完成中斷(接收完成是當前數據接收完成,還需要判斷接收次數是否為 0)
筆者設備 STM32F103RCT6 + window10,若其他設備可根據提供的資料進行查詢對應的引腳和通道
—————————————分割線———————————————
開始放代碼
配置引腳
- void USART1_GPIO_Init(void){//初始化 串口 1 GPIO
- //GPIO引腳功能初始化結構體
- GPIO_InitTypeDef GPIO_InitStruct;
- //1、使能串口引腳,復用功能
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
-
- //2、初始化引腳功能 輸入 懸空(輸入高為高, 輸入低為低), 輸出 復用推挽
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//初始化引腳為 A9
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//初始化引腳功能為 復用推挽模式
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//設置引腳傳輸速率為 10MHz
- GPIO_Init(GPIOA, & GPIO_InitStruct);//初始化 A9引腳 (發送)
-
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//初始化引腳為 A10
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//初始化引腳功能為 懸空輸入
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//設置引腳傳輸速率為 10MHz
- GPIO_Init(GPIOA, & GPIO_InitStruct);//初始化 A1引腳 (接收)
- }
復制代碼 配置串口
- void USART1_Init(uint32_t BaudRate){
- //串口初始化結構體
- USART_InitTypeDef USART_InitStruct;
- //串口優先級結構體
- NVIC_InitTypeDef NVIC_InitStruct;
- //1、使能串口引腳,復用功能
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
- //2、串口初始化
- //2.1 初始化波特率
- USART_InitStruct.USART_BaudRate = BaudRate;//初始化串口波特率(使用形參確定)
- //2.2 初始化數據位
- USART_InitStruct.USART_WordLength = USART_WordLength_8b;//初始化串口數據位為 8 位
- //2.3 初始化停止位
- USART_InitStruct.USART_StopBits = USART_StopBits_1;//初始化串口數據位為 0 位
- //2.4 初始化奇偶校驗位
- USART_InitStruct.USART_Parity = USART_Parity_No;//初始化串口奇偶校驗位
- //2.5 初始化流控
- USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//初始化串口控制為無流控
- //2.6 初始化串口工作模式
- USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
- USART_Init(USART1, & USART_InitStruct);
- //3、設置串口 接收中斷 (空閑中斷-搬運數據完畢 或 未搬運數據 當開始搬運數據后 一段時間未搬運下一個數據,發出此中斷)
- //3.1 配置串口中斷優先級通道
- NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
- //3.2 使能命令
- NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
- //3.3 設置搶占優先級
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
- //3.4 設置優先級
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
- NVIC_Init( & NVIC_InitStruct);
-
- //5、配置串口接收中斷
- USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//串口1 空閑中斷
- //5、中斷處理
- USART_DMACmd(USART1,USART_DMAReq_Rx, ENABLE);//開啟串口1 DMA 接收請求
- //6、使能串口
- USART_Cmd(USART1,ENABLE);//若重載 printf ,就無需使用 DMA 進行數據搬移。(故此處應該無需使用重載)
- }
復制代碼
配置 DMA
- void USART1_DMA_Init(void){
- //DMA初始化結構體
- DMA_InitTypeDef DMA_InitStruct;
- //1、使能DMA時鐘
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
-
- //2、接收數據 DMA通道5初始化
- //2.1 初始化外設地址
- DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)& USART1->DR;//設置串口發送數據寄存器
- //2.2 初始化內存地址
- DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Get_Date;//接收內存地址
- //2.3 初始化 DMA 方向
- DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//外設為源地址
- //2.4 初始化 發送數據大小 ,6.2內存大小
- DMA_InitStruct.DMA_BufferSize = BUFFSIZE;//此處的接收數據大小設置一個固定的,較大的
- //2.5 初始化外設地址增量(向發送端寫入數據,無需使用增量)
- DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//無增量
- //2.6 初始化內存地址增量(讀取內存地址,寫入外設,有增量)
- DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//有增量
- //2.7 初始化外設數據寬度
- DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//無增量的話應該不用設置
- //2.8 初始化內存數據寬度
- DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//增量為全字
- //2.9 初始化DMA工作模式
- DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;//正常循環模式
- //2.10 初始化DMA通道優先級
- DMA_InitStruct.DMA_Priority = DMA_Priority_High;//通道優先級 高
- //2.11 初始化DMA存儲器to存儲器(內存寫入外設,無需使用)
- DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;//不使用 內存到內存
- DMA_Init(DMA1_Channel5, & DMA_InitStruct);//通道5 串口1 讀數據
- DMA_Cmd(DMA1_Channel5, ENABLE);//開啟通道5 使能位
- }
復制代碼
中斷和調用函數部分
- void Mov_DmaDate_To_Buffer(void){//搬移函數,將DMA寄存器中的數據搬移到另一個寄存器
- DMA_Cmd(DMA1_Channel5, DISABLE);//關閉 DMA 通道 5 (關閉串口1 讀)
-
- DMA1_Channel5->CNDTR = BUFFSIZE;//重新設置 通道 待傳輸數據大小
-
- DMA_Cmd(DMA1_Channel5, ENABLE);//開啟 DMA 通道 5 (開啟串口1 讀)
-
- printf("%s\n",Get_Date);//輸出接收的數據
- }
- void USART1_IRQHandler(void){//串口1 中斷處理函數
- if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET){//若中斷標志位為 空閑中斷
- //調用搬移函數
- Mov_DmaDate_To_Buffer();//調用空閑中斷處理函數
- USART_ReceiveData(USART1);//通過讀取數據實現關閉 空閑中斷
-
- //清除標志位
- Recevie_State = true;//設置接收數據完畢標志位
- }
- }
復制代碼
Keil代碼下載:
代碼STM32_Study_Serial_DMA_Read.7z
(193.55 KB, 下載次數: 52)
2023-4-8 16:57 上傳
點擊文件名下載附件
代碼
|