CIU32L051系列 DMA串口無阻塞性收發的實現

1.CIU32L051 DMA的通道映射

? ? ? ? 由于華大CIU32L051的DMA外設資源有限,DMA只有兩個通道可供使用,對應的通道映射圖如下:

2.UART對應的引腳分布及其復用映射

? ? ? ??CIU32L051對應的UART對應的引腳映射圖如下,這里博主為了各位方便查找,就直接全拿進來了:

3.USART1作為無阻塞性收發的串口

? ? ? ? 根據第二章的圖片可以看到,串口1對應的IO口為PA1,PA2,PA11,PA12等等,這里為了方便,博主直接拿usart的例程中的PA11,PA12分別作為USART1_TX,USART1_RX。

? ? ? ? 對應串口的配置程序如下:

//串口1    GPIO的配置
static void UART1_GPIO_Configure(void)
{/* GPIO外設時鐘使能 */  std_rcc_gpio_clk_enable(RCC_PERIPH_CLK_GPIOA);std_gpio_init_t usart_gpio_init={0};/* GPIO引腳配置  PA12    ------> RX  PA11    ------> TX  */    usart_gpio_init.pin = GPIO_PIN_11|GPIO_PIN_12;usart_gpio_init.mode = GPIO_MODE_ALTERNATE;//io復用模式usart_gpio_init.output_type = GPIO_OUTPUT_PUSHPULL;//復用推挽輸出usart_gpio_init.pull = GPIO_PULLUP;//上拉輸入usart_gpio_init.alternate = GPIO_AF1_USART1;//復用串口1std_gpio_init(GPIOA, &usart_gpio_init);NVIC_SetPriority(USART1_IRQn, 0);//這里配置串口1的中斷配置器,這里可以不需要使用NVIC_EnableIRQ(USART1_IRQn);
}//串口1 結構體的配置
static void usart1_init(uint32_t baud)
{/* USART1時鐘使能 */std_rcc_apb2_clk_enable(RCC_PERIPH_CLK_USART1);std_usart_init_t usart_init={0};usart_init.direction = USART_DIRECTION_SEND_RECEIVE;//這里是使能了串口的發送和接收usart_init.baudrate = baud;//波特率usart_init.wordlength = USART_WORDLENGTH_8BITS;//數據長度usart_init.stopbits = USART_STOPBITS_1;//停止位usart_init.parity = USART_PARITY_NONE;//奇偶校驗usart_init.hardware_flow = USART_FLOWCONTROL_NONE;//無硬件流使能/* USART初始化 */   if(STD_OK != std_usart_init(USART1,&usart_init)){/* 波特率配置不正確處理代碼 */while(1);}std_usart_enable(USART1);
}

4.串口1無阻塞性發送的實現及代碼實現

? ? ? ? 根據第一章的內容可以得知,串口1的TX和RX分別對應的是DMA的通道1和通道0。具體如下:

4.1配置串口1 DMA無阻塞性發送的實現

? ? ? ? 配置代碼具體如下:

? ? ? ? 此處有配置DMA的傳輸模式為DMA_BLOCK_TRANSFER,具體的解釋如下:

/**
* @brief  DMA通道1初始化
* @retval 無
*/
static void dma_init(void)
{std_dma_init_t dma_init_param={0};/* DMA外設時鐘使能 */std_rcc_ahb_clk_enable(RCC_PERIPH_CLK_DMA);/* dma_init_param 結構體初始化 */dma_init_param.dma_channel = DMA_CHANNEL_1;dma_init_param.dma_req_id = DMA_REQUEST_USART1_TX;//這里是指DMA請求的觸發條件dma_init_param.transfer_type = DMA_BLOCK_TRANSFER;//配置DMA 的傳輸模式dma_init_param.src_addr_inc = DMA_SRC_INC_ENABLE;//使能源地址遞增dma_init_param.dst_addr_inc = DMA_DST_INC_DISABLE;//DMA目的地址自增使能或禁止dma_init_param.data_size = DMA_DATA_SIZE_BYTE;//DMA傳輸數據寬度,字節、半字或字dma_init_param.mode = DMA_MODE_NORMAL;//DMA工作模式為單次傳輸/* DMA初始化 */std_dma_init(&dma_init_param);/* 使能傳輸完成中斷 */std_dma_interrupt_enable(dma_init_param.dma_channel,DMA_INTERRUPT_TF);//這里使能的傳輸完成中斷,當DMA將對應目標的數據搬運完成后會將傳輸完成的標記置為1/* NVIC初始化 */NVIC_SetPriority(DMA_Channel1_IRQn, 0);NVIC_EnableIRQ(DMA_Channel1_IRQn); 
}/**
* @brief  DMA配置函數    目的是更新DMA傳輸的數據源和目標地址
* @param  source DMA 傳輸源地址    指的是發送緩沖區的地址
* @param  number DMA 傳輸字符數    這個參數就是指的是你發送緩沖區對應的大小
* @retval 無
*/
void bsp_usart_dma_config(uint8_t *source,uint32_t number)
{std_dma_config_t dma_config = {0};/* 配置DMA 源地址、目的地址和傳輸數據大小,并使能DMA */dma_config.src_addr = (uint32_t)source;dma_config.dst_addr = (uint32_t)&USART1->TDR;//目標地址為串口1的發送數據寄存器dma_config.data_number = number;dma_config.dma_channel = DMA_CHANNEL_1;std_dma_start_transmit(&dma_config); 
}void En_Dma_Channel(void)//這里在進行對DMA通道的使能
{std_dma_enable(DMA_CHANNEL_1);
}void Dis_Dma_Channel(void)//這里在進行對DMA通道的失能
{std_dma_disable(DMA_CHANNEL_1);
}

? ? ? ? 上述代碼中,DMA的通道1觸發傳輸的條件是觸發了串口1的發送,即當發送緩沖區的數據非空時,DMA將會把發送緩沖區的數據搬運至串口1的發送數據寄存器中,串口則會通過發送數據寄存器將數據轉發。

? ? ? ? 由于,DMA的工作模式設置的是單次傳輸的模式,由此當DMA傳輸第一次完成后,需要對對DMA的數據源和目的地址進行更新,并重新使能對應的DMA通道,具體示例如下:

//使用DMA通道1	實現串口非阻塞性發送    這個函數的構造,就相當于是無阻塞性的UART_SENDDATA("DEBUG",strlen("DEBUG"))這種發送函數,只不過原本串口的發送函數是阻塞性的
void UART1_DMA_Send_Buf(uint8_t *buf,size_t len)
{Dis_Dma_Channel();//更新數據源前需要先關閉DMA對應的通道bsp_usart_dma_config(buf,len);//此函數則是更新了DMA對應通道的數據源,此函數在之前的配置代碼中有對應的詳細內容std_dma_interrupt_enable(DMA_CHANNEL_1,DMA_INTERRUPT_TF); //這里開啟對應的傳輸完成中斷,若是對應的中斷服務函數中沒有關閉傳輸完成中斷,此處內容可以省略En_Dma_Channel();//重新開啟DMA對應的通道
}//DMA通道1對應的中斷服務函數
/*-------------------------------------------functions------------------------------------------*/
/**
* @brief  DMA通道中斷服務函數
* @retval 無
*/
//傳輸完成中斷
void DMA_Channel1_IRQHandler(void)
{if(std_dma_get_flag(DMA_FLAG_TF1)){std_dma_interrupt_disable(DMA_CHANNEL_1,DMA_INTERRUPT_TF); //這個可以不用關閉SEGGER_RTT_printf(0, "DMA DATA SEND FINISH\r\n");//這就是RRT的輸出的一個調試信息std_dma_clear_flag(DMA_FLAG_TF1);//清除傳輸完成的標記}
}

? ? ? ? 上述內容是對應的串口 DMA無阻塞性的發送的實現。

4.2串口1 無阻塞性接收的實現

? ? ? ? 無阻塞性接收數據的實現,需要用到DMA雙緩沖區備份。為什么要用雙緩沖區備份呢?

理由如下:

? ? ? ? 由于串口接收到的數據是不定長的數據,使用的DMA傳輸數據時容易造成可能由于數據過長導致緩沖區溢出的情況,以及在接收完成數據后由于數據處理花費的時間導致數據丟失。

? ? ? ? DMA提供了一個傳輸完成一半的中斷提示,由此可以通過數據傳輸完成一半的時,在中斷服務函數中更換DMA的接收緩沖區,將其余的數據轉存至到備份區域,這就避免了傳輸過程中造成的數據丟失問題。

? ? ? ? 此方式花費了原本雙倍的內存換來了更高性能的傳輸,也避免了傳輸過程中的數據丟失問題。

? ? ? ? 具體配置如下:

/**
* @brief  DMA配置函數    目的是更新串口1RX對應DMA的數據源和目的地址
* @param  distination DMA傳輸目的地址
* @param  number DMA傳輸字符數
* @retval 無
*/
void USART1_DMA_RX_config(uint8_t *distination,uint32_t number)
{std_dma_config_t dma_config = {0};if(distination == buf_hu1)//此處內容用于中斷服務函數中更新對應的緩沖區{Set_USART1_Buf_Flag();}else if(distination == buf_hu2){Clear_USART1_Buf_Flag();}/* 配置DMA 源地址、目的地址和傳輸數據大小,并使能DMA */dma_config.src_addr = (uint32_t)&USART1->RDR;//串口數據接收寄存器dma_config.dst_addr = (uint32_t)distination;dma_config.data_number = number;dma_config.dma_channel = DMA_CHANNEL_0; std_dma_start_transmit(&dma_config);   
}/**
* @brief  DMA通道0初始化
* @retval 無
*/
void UART1_Dma_RX_init(void)
{std_dma_init_t dma_init_param = {0};/* DMA外設時鐘使能 */std_rcc_ahb_clk_enable(RCC_PERIPH_CLK_DMA);/* dma_init_param 結構體初始化 */dma_init_param.dma_channel = DMA_CHANNEL_0;//通道0對應串口1的RXdma_init_param.dma_req_id = DMA_REQUEST_USART1_RX;//dma請求是串口1的接收,意思是當串口1    的RDR寄存器不為空時將RDR寄存器的數據搬運置,緩沖區中dma_init_param.transfer_type = DMA_BLOCK_TRANSFER;dma_init_param.src_addr_inc = DMA_SRC_INC_DISABLE;//使能數據源地址遞增dma_init_param.dst_addr_inc = DMA_DST_INC_ENABLE;//使能目標地址遞增dma_init_param.data_size = DMA_DATA_SIZE_BYTE;//一次傳輸1bytedma_init_param.mode = DMA_MODE_CIRCULAR;//循環接收,不需要對DMA進行重新配置/* DMA初始化 */std_dma_init(&dma_init_param);USART1_DMA_RX_config(buf_hu1,256);//這設置了串口1對應DMA通道0的緩沖區/* 使能傳輸完成中斷 */std_dma_interrupt_enable(DMA_CHANNEL_0,DMA_INTERRUPT_TF);//使能dma通道0對應的接收完成中斷  std_dma_interrupt_enable(DMA_CHANNEL_0,DMA_INTERRUPT_TH);  //使能dma通道0對應的接收一半中斷  /* NVIC初始化 */NVIC_SetPriority(DMA_Channel0_IRQn, 0);    NVIC_EnableIRQ(DMA_Channel0_IRQn);
}void Set_USART1_Buf_Flag(void)
{ATdevs.uart1_buf_Flag = 1;
}uint8_t Get_USART1_Buf_Flag(void)
{return ATdevs.uart1_buf_Flag;
}void Clear_USART1_Buf_Flag(void)
{ATdevs.uart1_buf_Flag = 0;
}/*DMA 雙緩沖區實現串口DMA的無阻塞性接收數據配合DMA的傳輸一半的中斷進行使用當觸發傳輸一半的中斷后,此時需要更換對應的緩沖區,再下次傳輸一半數據前,是我們處理原本數據的有效時間要求是MCU的處理速度要比DMA的傳輸速度要快一半
*/
void DMA_USART_RX_CallBlack(void)
{std_dma_disable(DMA_CHANNEL_0);if(Get_USART1_Buf_Flag()){memset(buf_hu2,0,USART1_BUF_SIZE);//更換緩沖區前清除需要更換的緩沖區數據USART1_DMA_RX_config(buf_hu2,USART1_BUF_SIZE);//更新數據源SEGGER_RTT_printf(0,"dma buf is modify buf_hu2 success\r\n");}else{memset(buf_hu1,0,USART1_BUF_SIZE);USART1_DMA_RX_config(buf_hu1,USART1_BUF_SIZE);SEGGER_RTT_printf(0,"dma buf is modify buf_hu1 success\r\n");}std_dma_enable(DMA_CHANNEL_0);
}

? ? ? ? ?上述內容是配置了USART1_RX對應的DMA通道,以及更換數據源的實現。

? ? ? ? 關于DMA通道0對應的中斷服務函數如下:

/*-------------------------------------------functions------------------------------------------*/
/**
* @brief  DMA通道中斷服務函數
* @retval 無
*/
//傳輸完成中斷
void DMA_Channel0_IRQHandler(void)
{if(std_dma_get_flag(DMA_FLAG_TF0)){std_dma_clear_flag(DMA_FLAG_TF0);        }if(std_dma_get_flag(DMA_INTERRUPT_TH))//DMA傳輸一半完成的中斷{std_dma_clear_flag(DMA_INTERRUPT_TH);DMA_USART_RX_CallBlack();//傳輸一半后更換目的地址}
}

USART1對應的無阻塞性收發初始化內容具體如下:

void Configure_UART1_DMA0_TX_AND_RX(void)
{dma_init();//這是DMA結構體相關的初始化UART1_Dma_RX_init();//此處是關于DMA實現的無阻塞性接收的配置UART1_GPIO_Configure();usart1_init(115200);std_usart_dma_tx_enable(USART1);//這里必須要使能std_usart_dma_rx_enable(USART1);//這里必須要使能
}

5.測試效果

? ? ? ? 測試環境:

? ? ? ? ? ? ? ? RT-thread NANO系統下,開辟了一個周期為2秒的周期定時器,在周期定時器的回調中進行無阻塞性的發送。

? ? ? ? ? ? ? ? 另外,通過STM32F103的最小系統板一直給CIU32L051發送數據。

? ? ? ? 效果圖如下:

? ? ? ? 由此可見,基于DMA實現的串口無阻塞性收發已經實現。

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

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

相關文章

飛算 JavaAI 體驗:重塑 Java 開發的智能新范式

飛算 JavaAI 體驗:重塑 Java 開發的智能新范式引言:正文:一、工程化代碼生成:從 "片段拼接" 到 "模塊交付"1.1 傳統工具的局限與突破1.2 代碼質量驗證二、智能重構引擎:從 "問題修復" 到…

深入理解JVM的垃圾收集(GC)機制

引言首先我們來介紹垃圾收集的概念,什么是垃圾收集?垃圾收集 (Garbage Collection,GC),顧名思義就是釋放垃圾占用的空間,防止內存爆掉。有效的使用可以使用的內存,對內存堆中已經死亡…

【筆記】國標-機動車輛及掛車分類

源于:GB/T 15089-2001機動車輛及掛車分類 1.L類:兩輪或三輪車輛2.M類:四輪載客車輛3.N類:四輪載貨車輛4.O類:掛車5.G類:其他

VLLM部署DeepSeek-LLM-7B-Chat 模型

一、部署環境準備1. 基礎環境要求操作系統:Linux(推薦歐拉系統、Ubuntu 等)Python 版本:3.8 及以上依賴工具:pip、git、curl可選依賴:GPU 環境:NVIDIA GPU(支持 CUDA 11.7&#xff0…

翱翔的智慧之翼:Deepoc具身智能如何賦能巡檢無人機“讀懂”工業現場

翱翔的智慧之翼:Deepoc具身智能如何賦能巡檢無人機“讀懂”工業現場在百米高的風力發電機葉片頂端,在蜿蜒數十公里的高壓輸電線旁,在油氣管道穿越的崇山峻嶺之上,一架四旋翼無人機正精準地懸停著,它的“眼睛”&#xf…

Java大廠面試實錄:謝飛機的電商場景技術問答(Spring Cloud、MyBatis、Redis、Kafka、AI等)

Java大廠面試實錄:謝飛機的電商場景技術問答(Spring Cloud、MyBatis、Redis、Kafka、AI等)本文模擬知名互聯網大廠Java后端崗位面試流程,以電商業務為主線,由嚴肅面試官與“水貨”程序員謝飛機展開有趣的對話&#xff…

Kotlin基礎

前言 Decrement(遞減) → 將一個值減 1 的操作 Predicate(謂詞) → 返回布爾值(邏輯值)的函數 Reference(引用) → 允許使用自定義名稱與對象交互 Runtime(運行時&…

預防DNS 解析器安全威脅

DNS 是互聯網的重要基礎,例如 Web 訪問、email 服務在內的眾多網絡服務都和 DNS 息息相關,DNS 的安全則直接關系到整個互聯網應用能否正常使用。 DNS 解析器的作用是將用戶輸入的域名轉換為對應的 IP 地址,以便計算機能夠準確地定位并連接到…

Windows下VScode配置FFmpeg開發環境保姆級教程

相關準備 提前在本地開發環境中配置好mingw64或者msys2開發工具集。 安裝VScode軟件。 下載Windows版本的FFmpeg相關庫 下載地址:https://ffmpeg.org/download.html 下載步驟:如下圖。 下載后的文件:包含了可執行文件ffmpeg、ffpl…

Lecture #19 : Multi-Version Concurrency Control

CMU15445課程筆記多版本并發控制 多版本并發控制講的是Mvcc。 即維護單個邏輯對象的多個物理版本, 這樣當一個事務讀取某個對象的時候不會阻塞其他事務寫入該對象; 反之亦然。 但是Mvcc不保護寫寫沖突, 對于這種情況, 可能需要其兩…

imx6ul Qt運行qml報錯This plugin does not support createPlatformOpenGLContext!

imx6ul運行qml的Qt程序報錯This plugin does not support createPlatformOpenGLContext!1、開發環境2、問題復現3、解決辦法第一種方法第二種方法4、結論1、開發環境 主板:imx6ul Qt版本:5.9.6 文件系統:buildroot 問題描述:現需…

軟考中項系統集成第 5 章:軟件工程全流程考點拆解,備考邏輯清晰

備考系統集成項目管理工程師的小伙伴們,福利來啦!今天開始為大家帶來《系統集成項目管理工程師(第 3 版)》考點的思維導圖,今天帶來的是第5章。第 5 章聚焦軟件工程,涵蓋軟件工程定義、軟件需求、軟件設計、…

ICLR 2025 | InterpGN:時間序列分類的透明革命,Shapelet+DNN雙引擎驅動!

在Rensselaer理工學院、Stony Brook大學與IBM Research的合作下,本文聚焦于如何在時間序列分類任務中兼顧性能與可解釋性。傳統深度學習模型雖然準確率高,卻常被詬病為“黑盒”,難以贏得如醫療等高風險領域的信任。為此,作者提出了…

使用ENO將您的JSON對象生成HTML顯示

ENO 是簡單易用,性能卓越,自由靈活開源的 WEB 前端組件;實現 JSON 與 HTML 互操作的 JavaScript 函數庫。沒有任何其它依賴,足夠輕量。 WEBPack NPM 工程安裝。 npm install joyzl/eno 然后在JS中引用 import "joyzl/eno…

7.12 卷積 | 最小生成樹 prim

lc1900.模擬比賽算出兩個指定選手最早和最晚能在第幾輪碰到。還是建議dfs捏模擬比賽,找出兩個特定選手(firstPlayer和secondPlayer)最早和最晚相遇的輪次。1. 定義了一個“選手”結構體,包含兩個屬性a(戰斗力&#xff…

LVS-NAT模式配置

目錄 1、負載調度器配置 配置IP地址 安裝ipvsadm 開啟路由轉發功能 加載ip_vs模塊 啟動ipvsadm服務 配置負載分配策略 查看驗證 2、web節點配置 3、測試 1、負載調度器配置 配置IP地址 增加一塊網卡 cd /etc/sysconfig/network-scripts/ cp ifcfg-ens192 ifcfg-ens…

中國銀聯豪擲1億采購海光C86架構服務器

近日,中國銀聯國產服務器采購大單正式敲定,基于海光C86架構的服務器產品中標,項目金額超過1億元。接下來,C86服務器將用于支撐中國銀聯的虛擬化、大數據、人工智能、研發測試等技術場景,進一步提升其業務處理能力、用戶…

web網頁,在線%食譜推薦系統%分析系統demo,基于vscode,uniapp,vue,java,jdk,springboot,mysql數據庫

經驗心得兩業務單,項目前端在VSCode、HBuilder環境下整合Uniapp、Vue。后端使用Java、SpringBoot和MySQL,使用這些技術棧咱們就可以搭建在線食譜推薦與分析功能的系統,技術棧雖涉及前后端及數據庫跨度不小,但咱們拆分模塊去開發它難度就會變小…

MCP架構:AI時代的標準化上下文交互協議

本文深入解析Model Context Protocol(MCP)架構的創新設計,這是一種由Anthropic提出的標準化協議,旨在解決大型語言模型(LLM)與外部工具和數據源交互的碎片化問題。MCP采用客戶端-服務器架構,通過…

機器學習數據集加載全攻略:從本地到網絡

目錄 一、加載內置數據集 1.1 Iris鳶尾花數據集 1.2 其他常用內置數據集 二、加載網絡數據集 2.1 20 Newsgroups數據集 三、加載本地數據集 3.1 使用pandas加載CSV文件 3.2 處理常見問題 四、數據加載最佳實踐 五、總結 在機器學習項目中,數據的加載是第一…