STM32中的DMA

DMA介紹

什么是DMA?

????????DMA(Direct Memory Access,直接存儲器訪問)提供在外設與內存存儲器和存儲器之間的高速數據傳輸使用。它允許不同速度的硬件裝置來溝通,而不需要依賴于CPU在這個時間中,CPU對于內存的工作來說就無法使用。

????????簡單描述: 就是一個數據搬運工

DMA的意義

代替 CPU 搬運數據,為 CPU 減負。

1.數據搬運的工作比較耗時間;

2. 數據搬運工作時效要求高(有數據來就要搬走);

3. 沒啥技術含量(CPU 節約出來的時間可以處理更重要的事)。

搬運數據的方式

有三種方式:存儲器到存儲器、存儲器到外設、外設到存儲器

  • 存儲器存儲器(例如:復制某特別大的數據buf
  • 存儲器外設 (例如:將某數據buf寫入串口TDR寄存器
  • 外設存儲器 (例如:將串口RDR寄存器寫入某數據buf

????????這里的外設指的是spiusartiicadc 等基于APB1 APB2AHB時鐘的外設,而這里的存儲器包括自身的閃存(flash)或者內存(SRAM)以及外設的存儲設備都可以作為訪問地源或者目的地。

存儲器存儲器

存儲器外設?

外設存儲器?

?DMA框圖

說明:利用DMA進行外設的數據搬運,首先,外設需向DMA1進項請求,然后,經過DMA的仲裁之后,DMA訪問外設的數據進行搬運。?

DMA控制器?

STM32F103 2 DMA 控制器, DMA1 7 個通道 DMA 2 5 個通道。

注意:

一個通道每次只能搬運一個外設的數據!! 如果同時有多個外設的 DMA 請求,則按照優先級進行響應。 STM32F103C8T6 只有 DMA1
  • DMA17個通道:

  • DMA2 5 個通道:?

DMA優先級管理?

  • 優先級管理采用軟件+硬件

軟件: 每個通道的優先級可以在DMA_CCRx寄存器中設置,有4個等級:最高級>高級>中級>低級。

硬件: 如果2個請求,它們的軟件優先級相同,則較低編號的通道比較高編號的通道有較高的優先權。比如:如果軟件優先級相同,通道2優先于通道4。

?DMA傳輸方式與指針遞增模式

  • 傳輸方式
DMA_Mode_Normal (正常模式)

一次DMA數據傳輸完后,停止DMA傳送 ,也就是只傳輸一次。

DMA_Mode_Circular (循環傳輸模式)

當傳輸結束時,硬件自動會將傳輸數據量寄存器進行重裝,進行下一輪的數據傳輸。 也就是多次傳輸模式。

  • 指針遞增模式

????????外設和存儲器指針在每次傳輸后可以自動向后遞增或保持常量。當設置為增量模式時,下一個要傳輸的地址將是前一個地址加上增量值。

情況1:

情況2:(目標只有一個存儲數據的位置,例如:串口只有一個數據寄存器)

?DMA數據對齊方式

  • 數據寬度大的轉移到數據寬度小的時候,低位保留高位截斷

DMA寄存器?

  • ?DMA中斷狀態寄存器(DMA_ISR)

  • DMA中斷標志清除寄存器(DMA_IFCR)?

  • ?DMA通道x配置寄存器(DMA_CCRx)(x = 1…7)

  • ?DMA通道x傳輸數量寄存器(DMA_CNDTRx)(x = 1…7)

?16位寄存器,最多可以傳輸數量65536。

  • DMA通道x外設地址寄存器(DMA_CPARx)(x = 1…7)

  • DMA通道x存儲器地址寄存器(DMA_CMARx)(x = 1…7)?

DMA的庫函數

在hal.dma.c文件中的一些常用的函數:

打開dma1時鐘的函數:?

在hal.dma.h文件中的一些常用的宏函數:?

在hal_dma_ex.h文件中,獲取?傳輸完成標志位

在hal.def.h文件中還存在所需的下面的函數:?

?若要讀取外設的數據還需要相關串口的函數,如下:

?小實驗1:DMA內存到內存數據搬運

?實驗目的

使用DMA將一個大數組的數組搬運到另一個位置。

硬件清單

開發板、ST-Link、USB轉TTL

配置流程

文件代碼?

  • dma.c文件代碼
#include "dma.h"
#include "stdio.h"#define BUF_SIZE 16uint32_t src_buf[BUF_SIZE] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};uint32_t dst_buf[BUF_SIZE] = {0};/**
* @breif    DMA的初始化函數
* @note     打開時鐘,配置相關參數
* @param    無
* @retval   無
*/
DMA_HandleTypeDef dma_handle = {0};
void dma_init(void){__HAL_RCC_DMA1_CLK_ENABLE();dma_handle.Instance = DMA1_Channel1;dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;           /* 搬運數據的方式:內存到內存,外設到內存,內存到外設 *///內存相關的配置dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;     /* 源:內存數據對齊模式:一般是8位 */dma_handle.Init.MemInc = DMA_MINC_ENABLE;                   /* 源:內存數據指針遞增的方式 */dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;  /* 目標:外設數據對齊模式:一般是8位 */dma_handle.Init.PeriphInc = DMA_PINC_ENABLE;                /* 目標:外設數據指針遞增的方式 */dma_handle.Init.Mode = DMA_NORMAL;                          /* 傳輸的模式:循環,不循環。內存到內存不支持循環模式 */dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;             /* 通道的優先級設置:有四種*/HAL_DMA_Init(&dma_handle);}/**
* @breif    封裝一個函數進行數據的轉運
* @note     利用DMA_Start函數進行數據的搬運,當搬運完成后查看標志位是否置1,然后進行打印
* @note     在DMA_Start()函數中,數據的長度要寫成sizeof(uint32_t)*BUF_SIZE,不能寫成BUF_SIZE
* @param    無
* @retval   無
*/
void dma_transmit(void){HAL_DMA_Start(&dma_handle,(uint32_t)src_buf,(uint32_t)dst_buf,sizeof(uint32_t)*BUF_SIZE);while(__HAL_DMA_GET_FLAG(&dma_handle,DMA_FLAG_TC1) == RESET);for(uint16_t i = 0;i < BUF_SIZE;i++){printf("傳輸的數據是:%d \r\n",dst_buf[i]);}        
}
  • dma.h文件代碼?
#ifndef __DMA_H__
#define __DMA_H__
#include "stm32f1xx.h"void dma_init(void);
void dma_transmit(void);#endif
  • mian.c文件代碼?
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "dma.h"int main(void)
{HAL_Init();                         /* 初始化HAL庫 */stm32_clock_init(RCC_PLL_MUL9);     /* 設置時鐘, 72Mhz */led_init();                         /* LED初始化 */uart1_init(115200);printf("hello,world");dma_init();dma_transmit();while(1){ }
}

?注意事項:

  • 關于數據傳輸中最后一行出現異常值(如從15突然跳變到1073872904),如下所示:

原因:緩沖區溢出或內存越界

解決方式:將上面的等號去掉,就會正常。

  • *****關于dma.c文件中的while循環中的判斷條件:==RESET,而不能用!=SET*****

原因:!= SET 還可能代表其他未定義的狀態,例如:硬件錯誤、無效參數,導致條件判斷不準確。

因此,要直接使用官方推薦條件,可避免兼容性的問題。

小實驗2:內存到外設數據轉運

?實驗目的

使用DMA將一個大數據通過串口1發送

硬件清單

開發板、ST-Link、USB轉TTL

配置流程?

文件代碼?

  • ?dma.c文件代碼
#include "dma.h"
#include "stdio.h"
extern UART_HandleTypeDef uart1_handle;/**
* @breif    DMA的初始化函數
* @note     打開時鐘,配置相關參數
* @param    無
* @retval   無
*/
DMA_HandleTypeDef dma_handle = {0};
void dma_init(void){__HAL_RCC_DMA1_CLK_ENABLE();dma_handle.Instance = DMA1_Channel4;                        /* 查看表格:看所需的DMA通道:通道4, */dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;           /* 搬運數據的方式:內存到內存,外設到內存,內存到外設 *///內存相關的配置dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;     /* 源:內存數據對齊模式:一般是8位 */dma_handle.Init.MemInc = DMA_MINC_ENABLE;                   /* 源:內存數據指針遞增的方式 */dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;  /* 目標:外設數據對齊模式:一般是8位 */dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;               /* 目標:串口發送寄存器數據指針是不能遞增的 */dma_handle.Init.Mode = DMA_NORMAL;                          /* 傳輸的模式:循環,不循環。內存到內存不支持循環模式 */dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;             /* 通道的優先級設置:有四種*/HAL_DMA_Init(&dma_handle);__HAL_LINKDMA(&uart1_handle,hdmatx,dma_handle);             /* 將內存和外設的地址進行連接,注意:dma句柄前面不用加&*/   
}/**
* @breif    封裝一個函數進行數據的轉運
* @note     利用DMA_Start函數進行數據的搬運,當搬運完成后查看標志位是否置1,然后進行打印
* @note     在DMA_Start()函數中,數據的長度要寫成sizeof(uint32_t)*BUF_SIZE,不能寫成BUF_SIZE
* @param    無
* @retval   無
*/

?dma.h文件代碼

#ifndef __DMA_H__
#define __DMA_H__
#include "stm32f1xx.h"void dma_init(void);#endif
  • main.c文件代碼?
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "dma.h"extern UART_HandleTypeDef uart1_handle;
uint8_t send_buf[1000] = {0};int main(void)
{HAL_Init();                         /* 初始化HAL庫 */stm32_clock_init(RCC_PLL_MUL9);     /* 設置時鐘, 72Mhz */led_init();                         /* LED初始化 */uart1_init(115200);
//    printf("hello,world");dma_init();int i = 0;for(i = 0; i < 1000;i++){send_buf[i] = 'A';}HAL_UART_Transmit_DMA(&uart1_handle,send_buf,1000);while(1){ }
}

總結:

  • 本代碼是將內存中的數據轉運到串口的發送數據的寄存器中,利用的函數HAL_UART_Transmit_DMA()函數,然后通過串口打印到串口調試助手的界面上。
  • 與利用printf函數不同;
  • 在將數據由內存轉運到內存中時,利用的是HAL_DMA_Start()函數,通過判斷傳輸完成標志位的函數__HAL_DMA_GET_FLAG()函數 ==SET(表明數據傳輸完成)。,然后利用printf()函數將數據打印出來。
  • 在dma.c和main.c文件中用到串口初始化函數的句柄,所以,要注意利用extern聲明一下外部變量。
  • 實驗現象

小實驗3:DMA外設到寄存器內存數據搬運?

實驗目的

使用DMA接收串口的數據

硬件清單

開發板、ST-Link、USB轉TTL?

函數的意義:聲明一個接收緩沖區(數組);這個函數返回接收完數據后剩余的數組長度。

可以用來計算,傳輸數據的長度:?

例:

uart1_rx_len = UART1_RX_BUF_SIZE-__HAL_DMA_GET_COUNTER(&dma_handle);

配置流程?

文件代碼?

  • dma.c文件代碼
#include "dma.h"
#include "stdio.h"
extern UART_HandleTypeDef uart1_handle;extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];                                    /* UART1接收緩沖區 *//**
* @breif    DMA的初始化函數
* @note     打開時鐘,配置相關參數
* @param    無
* @retval   無
*/
DMA_HandleTypeDef dma_handle = {0};
void dma_init(void){__HAL_RCC_DMA1_CLK_ENABLE();dma_handle.Instance = DMA1_Channel5;                        /* 查看表格:UART1_RX的DMA通道:通道5 */dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;           /* 搬運數據的方式:內存到內存,外設到內存,內存到外設 *///內存相關的配置dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;     /* 源:內存數據對齊模式:一般是8位 */dma_handle.Init.MemInc = DMA_MINC_ENABLE;                   /* 源:內存數據指針遞增的方式 */dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;  /* 目標:外設數據對齊模式:一般是8位 */dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;               /* 目標:串口發送寄存器數據指針是不能遞增的 */dma_handle.Init.Mode = DMA_NORMAL;                          /* 傳輸的模式:循環,不循環。內存到內存不支持循環模式 */dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;             /* 通道的優先級設置:有四種*/HAL_DMA_Init(&dma_handle);__HAL_LINKDMA(&uart1_handle,hdmarx,dma_handle);             /* 將內存和外設的地址進行連接,注意:dma句柄前面不用加&*/  HAL_UART_Receive_DMA(&uart1_handle,uart1_rx_buf,UART1_RX_BUF_SIZE);  /* 打開串口的DMA數據轉運,*/
}
  • ?dma.h文件代碼
#ifndef __DMA_H__
#define __DMA_H__
#include "stm32f1xx.h"#define UART1_RX_BUF_SIZE            128
void dma_init(void);#endif
  • uart1.c文件代碼?

  • main.c文件代碼?
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "dma.h"int main(void)
{HAL_Init();                         /* 初始化HAL庫 */stm32_clock_init(RCC_PLL_MUL9);     /* 設置時鐘, 72Mhz */led_init();                         /* LED初始化 */uart1_init(115200);printf("hello,world\r\n");dma_init();while(1){ }
}

注意事項:

  • 注意在串口中斷函數中書寫的DMA轉運數據的流程。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80365.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80365.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80365.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

聊聊JetCache的緩存構建

序 本文主要研究一下JetCache的緩存構建 invokeWithCached com/alicp/jetcache/anno/method/CacheHandler.java private static Object invokeWithCached(CacheInvokeContext context)throws Throwable {CacheInvokeConfig cic context.getCacheInvokeConfig();CachedAnnoC…

c#隊列及其操作

可以用數組、鏈表實現隊列&#xff0c;大致與棧相似&#xff0c;簡要介紹下隊列實現吧。值得注意的是循環隊列判空判滿操作&#xff0c;在用鏈表實現時需要額外思考下出入隊列條件。 設計頭文件 #ifndef ARRAY_QUEUE_H #define ARRAY_QUEUE_H#include <stdbool.h> #incl…

開源項目實戰學習之YOLO11:12.3 ultralytics-models-sam-encoders.py源碼分析

?? 點擊關注不迷路 ?? 點擊關注不迷路 ?? 另外,前些天發現了一個巨牛的AI人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。感興趣的可以點擊相關跳轉鏈接。 點擊跳轉到網站。 ultralytics-models-sam 1.sam-modules-encoders.pyblocks.py: 定義模型中的各…

STM32 | FreeRTOS 消息隊列

01 一、概述 隊列又稱消息隊列&#xff0c;是一種常用于任務間通信的數據結構&#xff0c;隊列可以在任務與任務間、中斷和任務間傳遞信息&#xff0c;實現了任務接收來自其他任務或中斷的不固定長度的消息&#xff0c;任務能夠從隊列里面讀取消息&#xff0c;當隊列中的消…

Java 安全漏洞掃描工具:如何快速發現和修復潛在問題?

Java 安全漏洞掃描工具&#xff1a;如何快速發現和修復潛在問題&#xff1f; 在當今的軟件開發領域&#xff0c;Java 作為一種廣泛使用的編程語言&#xff0c;其應用的規模和復雜度不斷攀升。然而&#xff0c;隨著應用的拓展&#xff0c;Java 應用面臨的潛在安全漏洞風險也日益…

Python繪制克利夫蘭點圖:從入門到實戰

Python繪制克利夫蘭點圖&#xff1a;從入門到實戰 引言 克利夫蘭點圖&#xff08;Cleveland Dot Plot&#xff09;是一種強大的數據可視化工具&#xff0c;由統計學家William Cleveland在1984年提出。這種圖表特別適合展示多個類別的數值比較&#xff0c;比傳統的條形圖更直觀…

LVGL- Calendar 日歷控件

1 日歷控件 1.1 日歷背景 lv_calendar 是 LVGL&#xff08;Light and Versatile Graphics Library&#xff09;提供的標準 GUI 控件之一&#xff0c;用于顯示日歷視圖。它支持用戶查看某年某月的完整日歷&#xff0c;還可以實現點擊日期、標記日期、導航月份等操作。這個控件…

多指標組合策略

該策略(MultiConditionStrategy)是一種基于多種技術指標和市場條件的交易策略。它通過綜合考慮多個條件來生成交易信號,從而決定買入或賣出的時機。 以下是對該策略的詳細分析: 交易邏輯思路 1. 條件1:星期幾和價格變化判斷 - 該條件根據當前日期是星期幾以及價格的變化…

BC 范式與 4NF

接下來我們詳細解釋 BC 范式&#xff08;Boyce-Codd范式&#xff0c;簡稱 BCNF&#xff09;&#xff0c;并通過具體例子說明其定義和應用。 一、BC范式的定義 BC范式&#xff08;Boyce-Codd范式&#xff0c;BCNF&#xff09;是數據庫規范化理論中的一種范式&#xff0c;它比第…

基于 CSS Grid 的網頁,拆解頁面整體布局結構

通過以下示例拆解網頁整體布局結構&#xff1a; 一、基礎結構&#xff08;HTML骨架&#xff09; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

采購流程規范化如何實現?日事清流程自動化助力需求、采購、財務高效協作

采購審批流程全靠人推進&#xff0c;內耗嚴重&#xff0c;效率低下&#xff1f; 花重金上了OA&#xff0c;結果功能有局限、不靈活&#xff1f; 問題出在哪里&#xff1f;是我們的要求太多、太苛刻嗎&#xff1f;NO&#xff01; 流程名稱&#xff1a; 采購審批管理 流程功能…

全棧項目搭建指南:Nuxt.js + Node.js + MongoDB

全棧項目搭建指南&#xff1a;Nuxt.js Node.js MongoDB 一、項目概述 我們將構建一個完整的全棧應用&#xff0c;包含&#xff1a; 前端&#xff1a;Nuxt.js (SSR渲染)后端&#xff1a;Node.js (Express/Koa框架)數據庫&#xff1a;MongoDB后臺管理系統&#xff1a;集成在同…

NVMe簡介6之PCIe事務層

PCIe的事務層連接了PCIe設備核心與PCIe鏈路&#xff0c;這里主要基于PCIe事務層進行分析。事務層采用TLP傳輸事務&#xff0c;完整的TLP由TLPPrefix、TLP頭、Payload和TLP Digest組成。TLP頭是TLP中最關鍵的部分&#xff0c;一般由三個或四個雙字的長度&#xff0c;其格式定義如…

Python異常模塊和包

異常 當檢測到一個錯誤時&#xff0c;Python解釋器就無法繼續執行了&#xff0c;反而出現了一些錯誤的提示&#xff0c;這就是所謂的“異常”, 也就是我們常說的BUG 例如&#xff1a;以r方式打開一個不存在的文件。 f open(‘python1.txt’,‘r’,encoding‘utf-8’) 當我們…

匯編:循環程序設計

一、 實驗要求 熟練掌握循環程序設計的基本方法熟練掌握單片機外部存儲空間的訪問方法 二、 實驗設計 1.整體思路 先初始化一些寄存器和數據存儲位置&#xff0c;然后調用兩個子程序Procedure1和Procedure2&#xff0c;分別從SRC復制數據到DEST&#xff0c;一個從開頭到末尾&…

典籍知識問答模塊AI問答bug修改

一、修改流式數據處理問題 1.問題描述&#xff1a;由于傳來的數據形式如下&#xff1a; event:START data:350 data:< data:t data:h data:i data:n data:k data:> data: data: data: data: data:嗯 data:&#xff0c; 導致需要修改獲取正常的當前信息id并更…

【金倉數據庫征文】- 金融HTAP實戰:KingbaseES實時風控與毫秒級分析一體化架構

文章目錄 引言&#xff1a;金融數字化轉型的HTAP引擎革命一、HTAP架構設計與資源隔離策略1.1 混合負載物理隔離架構1.1.1 行列存儲分區策略1.1.2 四級資源隔離機制 二、實時流處理與增量同步優化2.1 分鐘級新鮮度保障2.1.1 WAL日志增量同步2.1.2 流計算優化 2.2 物化視圖實時刷…

季報中的FPGA行業:U型反轉,春江水暖

上周Lattice,AMD兩大廠商相繼發布2025 Q1季報,盡管恢復速度各異,但同時傳遞出FPGA行業整體回暖的復蘇信號。 5月5日,Lattice交出了“勉強及格”的答卷,報告季度營收1億2000萬,與華爾街的預期基本相符。 對于這家聚焦在中小規模器件的領先廠商而言,按照其CEO的預期,長…

使用 javap 深入理解 Java 字節碼

引言 Java 是一種廣泛使用的高級編程語言,其獨特之處在于編譯后的代碼不是直接的機器碼,而是一種稱為字節碼的中間表示形式。字節碼存儲在 .class 文件中,由 Java 虛擬機 (JVM) 解釋或即時編譯為特定平臺的機器碼。這種設計賦予了 Java 平臺無關性,即“一次編寫,到處運行…

LeetCode_sql刷題(3482.分析組織層級)

題目描述&#xff1a;3482. 分析組織層級 - 力扣&#xff08;LeetCode&#xff09; 表&#xff1a;Employees ------------------------- | Column Name | Type | ------------------------- | employee_id | int | | employee_name | varchar | | manager_id …