国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例

這篇具有很好參考價值的文章主要介紹了使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例

Date Author Version Note
2023.08.06 Dog Tao V1.0 1. 完成了文檔的撰寫。
2023.08.23 Dog Tao V1.1 1. 增加了STM32F103-USART2的DMA傳輸配置示例。 2. 增加了STM32F103與F407單片機的DMA控制器介紹并更改了第一章節(jié)的結(jié)構(gòu)。

背景介紹

應(yīng)用場景

在許多現(xiàn)實應(yīng)用場景中,例如工業(yè)自動化控制、嵌入式通信設(shè)備等領(lǐng)域,單片機需要實時地從一個串口讀取數(shù)據(jù),并轉(zhuǎn)發(fā)到另一個串口。如果使用常規(guī)的輪詢或中斷方法來完成這樣的任務(wù),會消耗大量的CPU資源,效率較低。此時如果采用DMA(直接存儲器訪問)進行串口數(shù)據(jù)轉(zhuǎn)發(fā)則可以具備很多優(yōu)勢,例如降低數(shù)據(jù)轉(zhuǎn)發(fā)延時、減輕CPU的運行負載、提高系統(tǒng)的實時性等。通過串口轉(zhuǎn)發(fā)也可以實現(xiàn)多個不同通訊形式(例如無線傳輸與有線傳輸)、不同通訊協(xié)議(例如自定協(xié)議與Modbus協(xié)議)、不同通訊參數(shù)(例如兩個設(shè)備分別具備不同波特率)的設(shè)備通訊中轉(zhuǎn)。

直接存儲器訪問(DMA,Direct Memory Access)是一種允許外設(shè)或內(nèi)存直接與其他外設(shè)或內(nèi)存交換數(shù)據(jù),而不需要通過CPU進行中介處理的技術(shù)。DMA可以有效提高整體系統(tǒng)效率,因為它允許數(shù)據(jù)傳輸?shù)耐瑫r,CPU仍可以執(zhí)行其他任務(wù)。STM32的DMA系統(tǒng)是一項強大的功能,允許高效的數(shù)據(jù)傳輸,同時減輕了CPU的負擔。其靈活的配置選項和與多種外設(shè)的兼容性使其適用于許多應(yīng)用,從簡單的數(shù)據(jù)復制到復雜的外設(shè)管理。正確使用DMA可以顯著提高STM32微控制器的性能和功能。

From STM32F103 datasheet:
The flexible 7-channel general-purpose DMA is able to manage memory-to-memory, peripheral-to-memory and memory-to-peripheral transfers. The DMA controller supports circular buffer management avoiding the generation of interrupts when the controller reaches the end of the buffer. Each channel is connected to dedicated hardware DMA requests, with support for software trigger on each channel. Configuration is made by software and transfer sizes between source and destination are independent.The DMA can be used with the main peripherals: SPI, I2C, USART, general-purpose and advanced-control timers TIMx and ADC.

From STM32F407 datasheet:
The devices feature two general-purpose dual-port DMAs (DMA1 and DMA2) with 8 streams each. They are able to manage memory-to-memory, peripheral-to-memory and memory-to-peripheral transfers. They feature dedicated FIFOs for APB/AHB peripherals, support burst transfer and are designed to provide the maximum peripheral bandwidth (AHB/APB). The two DMA controllers support circular buffer management, so that no specific code is needed when the controller reaches the end of the buffer. The two DMA controllers also have a double buffering feature, which automates the use and switching of two memory buffers without requiring any special code. Each stream is connected to dedicated hardware DMA requests, with support for software trigger on each stream. Configuration is made by software and transfer sizes between source and destination are independent. The DMA can be used with the main peripherals: SPI and I2S, I2C, USART, General-purpose, basic and advanced-control timers TIMx, DAC, SDIO, Camera interface (DCMI) and ADC.

STM32的DMA控制器

  • STM32F103的DMA控制器

對于大容量的STM32F103芯片有2個DMA控制器,DMA1有7個通道,DMA2有5個通道。每個通道都可以配置一些外設(shè)的地址。從外設(shè)TIMx[x=1、2、3、4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1、2]和USARTx[x=1、2、3]產(chǎn)生的7個DMA請求,通過邏輯或輸入到DMA1控制器。從外設(shè)TIMx[5、6、7、8]、ADC3、SPI/I2S3、UART4、DAC通道1、2和SDIO產(chǎn)生的5個請求,經(jīng)邏輯或輸入到DMA2控制器。

在同一個DMA模塊上,多個請求間的優(yōu)先權(quán)可以通過軟件編程設(shè)置(共有四級:很高、高、中等和低),優(yōu)先權(quán)設(shè)置相等時由硬件決定(請求0優(yōu)先于請求1,依此類推)

使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例,STM32-MCU,單片機,stm32,嵌入式硬件,modbus,通訊協(xié)議使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例,STM32-MCU,單片機,stm32,嵌入式硬件,modbus,通訊協(xié)議使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例,STM32-MCU,單片機,stm32,嵌入式硬件,modbus,通訊協(xié)議
使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例,STM32-MCU,單片機,stm32,嵌入式硬件,modbus,通訊協(xié)議

  • STM32F407的DMA控制器

每個DMA控制器有8個數(shù)據(jù)流,每個數(shù)據(jù)流都能夠提供源和目標之間的單向傳輸鏈路。每個DMA控制器可以同時配置多個數(shù)據(jù)流,但在某一時刻只允許有一個數(shù)據(jù)流使用DMA控制器。當多個數(shù)據(jù)流同時請求時,由仲裁器決定哪一個數(shù)據(jù)流優(yōu)先使用DMA控制器。仲裁器用于在多個數(shù)據(jù)流同時請求時,解決請求沖突的問題。在硬件上,數(shù)據(jù)流的編號越低,請求優(yōu)先級越高,仲裁器優(yōu)先響應(yīng)編號低的數(shù)據(jù)流。

仲裁器為實現(xiàn)更靈活的配置,數(shù)據(jù)流還可以設(shè)置軟件優(yōu)先級,軟件優(yōu)先級分為以下4個級別:非常高優(yōu)先級、高優(yōu)先級、中優(yōu)先級、低優(yōu)先級。如果兩個請求具有相同的軟件優(yōu)先級,則編號低的數(shù)據(jù)流優(yōu)先于編號高的數(shù)據(jù)流。

每個數(shù)據(jù)流有8個通道,每個通道映射到不同外設(shè),這有利于針對不同的產(chǎn)品配置不同的DMA外設(shè)請求。每個數(shù)據(jù)流只能配置為映射到一個通道,無法配置為映射到多個通道。即,與數(shù)據(jù)流不同,每個DMA控制器可以同時配置多個數(shù)據(jù)流(因為有仲裁器),但每個數(shù)據(jù)流不能同時配置多個通道(因為只有選擇器)。

使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例,STM32-MCU,單片機,stm32,嵌入式硬件,modbus,通訊協(xié)議
使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例,STM32-MCU,單片機,stm32,嵌入式硬件,modbus,通訊協(xié)議

實現(xiàn)流程

使用單片機實現(xiàn)串口轉(zhuǎn)發(fā)可以分為兩種主要的模式:直接轉(zhuǎn)發(fā)模式與選擇轉(zhuǎn)發(fā)模式。直接轉(zhuǎn)發(fā)模式是指單片機從一個串口中接收到的數(shù)據(jù)不經(jīng)CPU的判斷與處理,直接通過DMA傳輸從另個一串口發(fā)送出去。間接轉(zhuǎn)發(fā)模式是指單片機從一個串口中接收到的數(shù)據(jù)需經(jīng)過CPU的判斷與處理,選擇性的將部分數(shù)據(jù)或者修改后的數(shù)據(jù)通過DMA傳輸從另個一串口發(fā)送出去。

直接轉(zhuǎn)發(fā)模式的核心實現(xiàn)過程為:對于接收數(shù)據(jù)的DMA通道,將串口的數(shù)據(jù)寄存器地址設(shè)置為源地址,并設(shè)置一個內(nèi)存地址為目標地址。對于發(fā)送數(shù)據(jù)的DMA通道,將之前設(shè)置的內(nèi)存地址設(shè)置為源地址,將另一個串口的數(shù)據(jù)寄存器地址設(shè)置為目標地址。

間接轉(zhuǎn)發(fā)模式由于CPU的恰當介入而具備更好的靈活性與多場景的適應(yīng)性,因此得到更為廣泛的應(yīng)用。以USART1與USART3為例,間接轉(zhuǎn)發(fā)的主要實現(xiàn)流程為:

  1. 初始化串口:初始化USART1和USART3,配置波特率、數(shù)據(jù)位、停止位、奇偶校驗等。

  2. 配置USART1用于中斷接收和DMA轉(zhuǎn)發(fā):啟用USART1的接收中斷功能,并配置相關(guān)NVIC。選擇適當?shù)腄MA通道,關(guān)聯(lián)USART1的發(fā)送功能。設(shè)置DMA源地址(例如緩沖區(qū))和目標地址(USART3的數(shù)據(jù)發(fā)送寄存器)。配置DMA的大小、方向、優(yōu)先級、模式等。

  3. 配置USART3用于中斷接收和DMA轉(zhuǎn)發(fā):與USART1類似,配置USART3以使用中斷進行接收,并選擇適當?shù)腄MA通道用于發(fā)送。設(shè)置DMA源地址(例如緩沖區(qū))和目標地址(USART1的數(shù)據(jù)發(fā)送寄存器)。配置DMA的大小、方向、優(yōu)先級、模式等。

  4. 啟用USART和DMA:啟用USART1、USART3以及相關(guān)的DMA通道。

  5. 中斷服務(wù)程序處理:在USART1的中斷服務(wù)程序中,讀取接收到的數(shù)據(jù),并觸發(fā)與USART3關(guān)聯(lián)的DMA傳輸。在USART3的中斷服務(wù)程序中,讀取接收到的數(shù)據(jù),并觸發(fā)與USART1關(guān)聯(lián)的DMA傳輸。

  6. 錯誤處理和同步:監(jiān)視DMA和USART的錯誤標志,并采取適當措施響應(yīng)任何潛在問題。根據(jù)需要,實現(xiàn)緩沖區(qū)管理和同步機制,以確保數(shù)據(jù)的完整性和時序。

源碼示例

以STM32F407的USART1與USART3雙向互發(fā)為例,展示核心功能實現(xiàn)的源碼。其中部分自定外設(shè)配置函數(shù)(例如USART_ConfigNVIC, USART_ConfigPort等)來自筆者自定的HAL庫。

示例代碼中,本機為Modbus-RTU從機設(shè)備,其USART1為Modbus-RTU/無線433MHz通訊口,USART3為RS485通訊口(485總線上連接多臺Modbus-RTU從機設(shè)備)。單片機從USART1中接收到Modbus-RTU請求報文之后,會首先判斷從機地址是否為本機,如果從機地址為本機地址,則進行正常的報文回復處理。如果從機地址不是本機地址,則通過USART3/485端口進行數(shù)據(jù)轉(zhuǎn)發(fā)。接收到來自USART3/485端口上對應(yīng)從機的回復后,再通過USART1/無線433MHz通訊端口將報文進一步封裝后發(fā)送到主機。

通過此方法,可以實現(xiàn)一對一的無線通訊與一對多的Modbus/RS485的混合組網(wǎng)。其通訊系統(tǒng)示意圖如下所示:

使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例,STM32-MCU,單片機,stm32,嵌入式硬件,modbus,通訊協(xié)議

串口與中斷配置

void NVIC_Config()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

	USART_ConfigNVIC(1, 0, 0);
	USART_ConfigNVIC(2, 0, 0);
	USART_ConfigNVIC(3, 0, 0);

	NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
   	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   	NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
   	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   	NVIC_Init(&NVIC_InitStructure);
}

void USART_Config()
{
	USART_ConfigPort(1, 115200, WordLength_8b, StopBits_1, Parity_No);
	USART_ConfigPort(2, 115200, WordLength_8b, StopBits_1, Parity_No);
	USART_ConfigPort(3, 115200, WordLength_8b, StopBits_1, Parity_No);

	// 使能串口發(fā)送完成中斷
	// USART_ITConfig(USART1, USART_IT_TC, ENABLE);
	// USART_ITConfig(USART2, USART_IT_TC, ENABLE);
	// USART_ITConfig(USART3, USART_IT_TC, ENABLE);
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
	USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);

	USART_Config_DMA();

	// 初始化串口接收緩沖區(qū)
	USART_RevInitAll();
}

DMA外設(shè)配置

void USART_Config_DMA()
{
	//配置USART1_TX-Stream: DMA-2 Stream-7 Channel-4
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);    //開啟DMA時鐘 
    DMA_DeInit(DMA2_Stream7);
    while(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}   //等待stream可配置,即DMAy_SxCR.EN變?yōu)?
   
    DMA_InitStructure.DMA_Channel = DMA_Channel_4;          //從8個channel中選擇一個
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;            //外設(shè)地址
    DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff;      //存儲器0地址,雙緩存模式還要使用M1AR
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;            //存儲器到外設(shè)模式
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;                //數(shù)據(jù)傳輸量,以外設(shè)數(shù)據(jù)項為單位 
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        //外設(shè)地址保持不變
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 //存儲器地址遞增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外設(shè)數(shù)據(jù)位寬:8位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         //存儲器數(shù)據(jù)位寬:8位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                         //普通模式(與循環(huán)模式對應(yīng))
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   //中等優(yōu)先級
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                  //禁止FIFO模式         
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;             //單次傳輸
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     //單次傳輸

	DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
   	// DMA_ITConfig(DMA2_Stream7, DMA_IT_TE, ENABLE);
    DMA_Init(DMA2_Stream7, &DMA_InitStructure);


  	//配置USART3_TX-Stream: DMA-1 Stream-3 Channel-4
	// DMA_InitTypeDef DMA_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);    //開啟DMA時鐘 
    DMA_DeInit(DMA1_Stream3);
    while(DMA_GetCmdStatus(DMA1_Stream3) != DISABLE){}   //等待stream可配置,即DMAy_SxCR.EN變?yōu)?
   
    DMA_InitStructure.DMA_Channel = DMA_Channel_4;          //從8個channel中選擇一個
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART3->DR;            //外設(shè)地址
    DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff;      //存儲器0地址,雙緩存模式還要使用M1AR
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;            //存儲器到外設(shè)模式
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;                //數(shù)據(jù)傳輸量,以外設(shè)數(shù)據(jù)項為單位 
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        //外設(shè)地址保持不變
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 //存儲器地址遞增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外設(shè)數(shù)據(jù)位寬:8位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         //存儲器數(shù)據(jù)位寬:8位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                         //普通模式(與循環(huán)模式對應(yīng))
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   //中等優(yōu)先級
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                  //禁止FIFO模式         
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;             //單次傳輸
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     //單次傳輸

	DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);
   	// DMA_ITConfig(DMA1_Stream3, DMA_IT_TE, ENABLE);
    DMA_Init(DMA1_Stream3, &DMA_InitStructure);
}

DMA發(fā)送數(shù)據(jù)函數(shù)

由于USART3是RS485協(xié)議傳輸,需要選擇收發(fā)狀態(tài)。本文源碼中,通過RS485_CTRL_ADDR的值實現(xiàn)收發(fā)轉(zhuǎn)換。在發(fā)送數(shù)據(jù)前,先將RS485_CTRL_ADDR置1。在DMA中斷服務(wù)函數(shù)中(發(fā)送完成中斷),將RS485_CTRL_ADDR置0,恢復RS485的數(shù)據(jù)接收狀態(tài)。

void USART1_DMA_SendData(uint8_t *tx_buffer,uint16_t length)
{
    // DMA_InitTypeDef DMA_InitStructure;
    DMA_Cmd(DMA2_Stream7, DISABLE);                                      //關(guān)閉DMA通道
    DMA_SetCurrDataCounter(DMA2_Stream7, (uint16_t)length);              //設(shè)置傳輸字節(jié)數(shù)
    DMA2_Stream7->CR |= (1 << 10);                                       //發(fā)送DMA流的地址不自增
    DMA2_Stream7->M0AR = (uint32_t)tx_buffer;                            //設(shè)置接收和發(fā)送的內(nèi)存地址
    DMA_Cmd(DMA2_Stream7, ENABLE);                                       //打開DMA通道
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);                         //使能串口1的DMA發(fā)送
    // while( DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) == RESET);    //等待傳輸完成 
    // DMA_Cmd(DMA2_Stream7, DISABLE);                                      //關(guān)閉DMA通道  
    // DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);                         //清除DMA傳輸完成標志
}

void USART3_DMA_SendData(uint8_t *tx_buffer,uint16_t length)
{
    // DMA_InitTypeDef DMA_InitStructure;
    DMA_Cmd(DMA1_Stream3, DISABLE);                                      //關(guān)閉DMA通道
    DMA_SetCurrDataCounter(DMA1_Stream3, (uint16_t)length);              //設(shè)置傳輸字節(jié)數(shù)
    DMA1_Stream3->CR |= (1 << 10);                                       //發(fā)送DMA流的地址不自增
    DMA1_Stream3->M0AR = (uint32_t)tx_buffer;                            //設(shè)置接收和發(fā)送的內(nèi)存地址
    DMA_Cmd(DMA1_Stream3, ENABLE);                                       //打開DMA通道
    USART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE);                         //使能串口1的DMA發(fā)送
    // while( DMA_GetFlagStatus(DMA1_Stream3, DMA_FLAG_TCIF3) == RESET);    //等待傳輸完成 
    // DMA_Cmd(DMA1_Stream3, DISABLE);                                      //關(guān)閉DMA通道  
    // DMA_ClearFlag(DMA1_Stream3, DMA_FLAG_TCIF7);                         //清除DMA傳輸完成標志
}

void USART1_WSN32_SendData(uint8_t *tx_buffer, uint16_t length)
{
	static uint8_t data_temp[300];

	memcpy(data_temp, MB_CommParam.MB_PreTrans_Data, MB_CommParam.MB_PreTrans_Num);
	memcpy(data_temp + MB_CommParam.MB_PreTrans_Num, tx_buffer, length);

	USART1_DMA_SendData(data_temp, length + MB_CommParam.MB_PreTrans_Num);

	// USART_SendData(USART1, MB_CommParam.MB_PreTrans_Data, MB_CommParam.MB_PreTrans_Num);
	// USART_SendData(USART1, tx_buffer, length);
}

void USART3_RS485_SendData(uint8_t *tx_buffer, uint16_t length)
{
	if(tx_buffer[0] == MB_CommParam.MB_SlaveAddr)
	{
		// 本機地址,不發(fā)送
		return;
	}

	*RS485_CTRL_ADDR = 1;
	// delay_ms(1);
	USART3_DMA_SendData(tx_buffer, length);

	// 恢復RS485控制信號為接收狀態(tài)的操作放到DMA發(fā)送完成中斷中
	// vtaskDelay(100);
	// *RS485_CTRL_ADDR = 0;
}

串口中斷服務(wù)函數(shù)

示例代碼中,本機為Modbus-RTU從機設(shè)備,其USART1為Modbus-RTU/無線433MHz通訊串口,USART3為RS485通訊串口(485總線上連接多臺Modbus-RTU從機設(shè)備)。因此,MB_CommParam.MB_PortNum 的值為1。

USART1: 在串口接收中斷USART_IT_RXNE的服務(wù)函數(shù)中調(diào)用Modbus-RTU協(xié)議的數(shù)據(jù)接收函數(shù)pxMBFrameCBByteReceived。
USART3: 在串口接收中斷USART_IT_RXNE的服務(wù)函數(shù)中往FIFO隊列緩沖中添加接收到的數(shù)據(jù)。在串口空閑中斷USART_IT_IDLE的服務(wù)函數(shù)中判斷數(shù)據(jù)接收完成并實現(xiàn)數(shù)據(jù)轉(zhuǎn)發(fā)的操作。

void USART1_IRQHandler(void)
{
    /**
     * 如果使能串口接收中斷,那么ORE為1時也會產(chǎn)生中斷。
     * 在應(yīng)用中對ORE標志進行處理,當判斷發(fā)生ORE中斷的時候,
     * 我們再讀一次USART_DR的值,
     * 這樣如果沒有新的Overrun 溢出事件發(fā)生的時候,ORE會被清除,
     * 然后程序就不會因為ORE未被清除而一直不斷的進入串口中斷
     */
    if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
    {
        USART_ReceiveByte(USART1);
    }

    if (MB_CommParam.MB_PortNum == 1)
    {
        if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
        {
            pxMBFrameCBByteReceived();
            USART_ClearITPendingBit(USART1, USART_IT_RXNE);
        }
        else if (USART_GetITStatus(USART1, USART_IT_TC) != RESET)
        {
            pxMBFrameCBTransmitterEmpty();
            USART_ClearITPendingBit(USART1, USART_IT_TC);
        }
        else if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
        {
       		// 串口接收完數(shù)據(jù)后空閑必須清除空閑標志位。
       		// 通過讀串口DR寄存器里的值來清除IDLE標志位,否則將一直觸發(fā)空閑中斷
            uint16_t data_temp = USART1->DR; // 先讀取接收緩存中數(shù)據(jù),清除空閑標志位
            data_temp = USART1->SR;
        }
        else
        {
        }
    }
    else
    {
			/* 省略無關(guān)代碼 */
    }
}

void USART3_IRQHandler(void)
{
    if (USART_GetFlagStatus(USART3, USART_FLAG_ORE) != RESET)
    {
        USART_ReceiveByte(USART3);
    }

    if (MB_CommParam.MB_PortNum == 3)
    {
    	/* 省略無關(guān)代碼 */
    }
    else
    {
        if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
        {
            USART_ClearITPendingBit(USART3, USART_IT_RXNE);
            USART_WriteFIFO(2, USART_ReceiveByte(USART3));	// 將接收到的數(shù)據(jù)添加到FIFO緩沖區(qū)
        }
        else if (USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)	// 串口空閑(數(shù)據(jù)接收完成)時,轉(zhuǎn)發(fā)數(shù)據(jù)到USART1
        {
            uint16_t data_temp = USART3->DR; // 先讀取接收緩存中數(shù)據(jù),清除空閑標志位
            data_temp = USART3->SR;
            
            if (IsEnablePortForwarding != 0)
            {
                // USART3 數(shù)據(jù)接收完成后,轉(zhuǎn)發(fā)數(shù)據(jù)到串口1
                USART3_RevBuffer_Handler(USART1_WSN32_SendData);
                // USART3_RevBuffer_Handler(USART2_RS232_SendData);
            }
        }
        else if (USART_GetITStatus(USART3, USART_IT_TC) != RESET)
        {
            USART_ClearITPendingBit(USART3, USART_IT_TC);

            // do something
        }
        else
        {
        }
    }
}

DMA中斷服務(wù)函數(shù)

在DMA中斷服務(wù)函數(shù)中(發(fā)送完成中斷),將RS485_CTRL_ADDR置0,恢復RS485的數(shù)據(jù)接收狀態(tài)。

void DMA2_Stream7_IRQHandler(void) // USART-1-TX DMA
{
  // 判斷是否為DMA發(fā)送完成中斷
  if (DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) == SET)
  {
    DMA_Cmd(DMA2_Stream7, DISABLE); // 關(guān)閉DMA通道
    DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);
  }
}

void DMA1_Stream3_IRQHandler(void) // USART3-TX DMA
{
  // 判斷是否為DMA發(fā)送完成中斷
  if (DMA_GetFlagStatus(DMA1_Stream3, DMA_FLAG_TCIF3) == SET)
  {
    DMA_Cmd(DMA1_Stream3, DISABLE); // 關(guān)閉DMA通道
    DMA_ClearFlag(DMA1_Stream3, DMA_FLAG_TCIF3);

    // delay_ms(1);
    delay_us(500);
    *RS485_CTRL_ADDR = 0;		// 恢復RS485的數(shù)據(jù)接收狀態(tài)
  }
}

Modbus協(xié)議代碼

Modbus-RTU協(xié)議通過移植freemodbus庫實現(xiàn),筆者在此庫中增加了報文接收的函數(shù)指針:

/// @brief When modbus-RTU ADU received, this function will be called.
extern RTU_ADU_ReceivedHandler_Type RTU_ADU_ReceivedHandler;

因此,可以設(shè)計一個回調(diào)函數(shù)USART3_RS485_SendData(已在上文提供實現(xiàn)源碼)注冊給RTU_ADU_ReceivedHandler指針,實現(xiàn)非本機地址的modbus請求指令通過USART3轉(zhuǎn)發(fā)。

User_Init函數(shù)首先通過讀取兩個撥碼開關(guān)的值來判斷當前設(shè)備的功能設(shè)定,如果處于無線通訊狀態(tài)(主機與本機一對一)則使能串口轉(zhuǎn)發(fā)功能。通過將不是本機地址的Modbus報文通過RS485總線發(fā)送出去,再將接收到的RS485回復數(shù)據(jù)通過無線通訊轉(zhuǎn)發(fā)到主機,則可以實現(xiàn)多機通訊與無線/有線混合組網(wǎng)。文章來源地址http://www.zghlxwxcb.cn/news/detail-631886.html

void User_Init()
{
	// 讀取撥碼開關(guān)的撥碼值
	DevAddr_Val = Debug_GetDipSwitchValue(GPIO_Array_DevAddr, 4, 0);
	SigChan_Val = Debug_GetDipSwitchValue(GPIO_Array_SigChan, 4, 0);

	if ((DevAddr_Val != 0) && (SigChan_Val != 0)) // The current work mode is WSN32
	{
		IsEnablePortForwarding = 1;
	}
	else
	{
		IsEnablePortForwarding = 0;
	}

	// 初始化Modbus四種寄存器
	User_MB_InitRegs();

	if(IsEnablePortForwarding != 0)
	{
		RTU_ADU_ReceivedHandler = USART3_RS485_SendData;	// 注冊回調(diào)函數(shù),處理接收到Modbus報文事件
	}
}

其他

STM32F103 - USART2 DMA傳輸配置示例

uint8_t VFD_RequestCmd[20] = {0};

void USART_Config()
{
    // 配置串口
   	USART_ConfigGPIO(USART_485_TXD_PORT, USART_485_TXD_PIN, USART_485_RXD_PORT, USART_485_RXD_PIN);
	USART_ConfigNVIC(USART_485_NUM, 7, 0);
	USART_ConfigPort(USART_485_NUM, 9600, WordLength_8b, StopBits_1, Parity_No);
   	USART_ITConfig(USART_485,USART_IT_IDLE,ENABLE);

    // 配置DMA - STM32: DMA-Channel7 (USART2_TX)
    DMA_InitTypeDef DMA_InitStruct;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA傳輸
	DMA_DeInit(DMA1_Channel7);
	
    DMA_InitStruct.DMA_PeripheralBaseAddr=(u32)&USART2->DR;
	DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; 	//配置外設(shè)數(shù)據(jù)單位 8位字節(jié)傳輸
	DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable; 	//禁止外設(shè)地址遞增

    DMA_InitStruct.DMA_MemoryBaseAddr=(u32)VFD_RequestCmd;
	DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; 	//配置內(nèi)存數(shù)據(jù)單位 8位字節(jié)傳輸
	DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;	    //允許內(nèi)存地址是否遞增

	DMA_InitStruct.DMA_BufferSize=sizeof(VFD_RequestCmd);

	DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralDST;   // 配置數(shù)據(jù)傳輸方向 外設(shè)作為數(shù)據(jù)傳輸目的地
	DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;	        // 配置是否使能從內(nèi)存到內(nèi)存?zhèn)鬏?/span>
    DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;        // 配置DMA模式 單次傳輸 or 循環(huán)傳輸
	DMA_InitStruct.DMA_Priority=DMA_Priority_High;	// 配置DMA優(yōu)先級

	DMA_Init(DMA1_Channel7, &DMA_InitStruct);
	USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
    DMA_ITConfig(DMA1_Channel7, DMA_IT_TC, DISABLE);//使能DMA傳輸完成通道

	// DMA_ClearITPendingBit(DMA1_IT_TC4);
	// DMA_Cmd(DMA1_Channel7, ENABLE);  //使能USART2 TX DMA1 所指示的通道
}

void USART2_SendWithDMA(uint8_t *Data, uint16_t Count)
{
	memcpy(VFD_RequestCmd, Data, Count);

	DMA_Cmd(DMA1_Channel7, DISABLE);   // 關(guān)閉USART2 TX DMA1 所指示的通道      
 	DMA_SetCurrDataCounter(DMA1_Channel7, Count);//DMA通道的DMA緩存的大小
 	DMA_Cmd(DMA1_Channel7, ENABLE);     // 使能USART2 TX DMA1 所指示的通道 
}

到了這里,關(guān)于使用DMA傳輸實現(xiàn)單片機高效串口轉(zhuǎn)發(fā)——以STM32系列為例的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 51單片機串口使用

    51單片機串口使用

    今天將為大家講解51單片機的串口原理及代碼編寫。 51單片機串口是一種通信接口,它可以將51單片機與外部設(shè)備連接起來,實現(xiàn)數(shù)據(jù)的雙向傳輸。51單片機串口的原理是,51單片機的串口接口由兩個管腳組成,一個是RXD,另一個是TXD,RXD用于接收數(shù)據(jù),TXD用于發(fā)送數(shù)據(jù)。當5

    2024年02月05日
    瀏覽(29)
  • 【51單片機】串口通信&&使用串口通信控制LED燈

    【51單片機】串口通信&&使用串口通信控制LED燈

    ??專欄【51單片機】 ??喜歡的詩句:更喜岷山千里雪 三軍過后盡開顏。 ??音樂分享【Promise】 ??大一同學小吉,歡迎并且感謝大家指出我的問題?? 目錄 ??串口通信 ??代碼? ???串口初始化 ?分析? ??????確定T1的工作方式 ??TMOD=0X20;? ??????確定T1的初值 ??

    2024年02月17日
    瀏覽(58)
  • C51單片機串口發(fā)送數(shù)據(jù)的使用

    C51單片機串口發(fā)送數(shù)據(jù)的使用

    ?????????假如說電路板上沒有CH340芯片,我們就要使用TTL轉(zhuǎn)USB模塊來進行電平的轉(zhuǎn)換,然后將模塊的RX接單片機的TX,模塊的TX接單片機的RX,來進行接線連接。 ?????????在連接好連線后,我們打開計算機管理,來查看端口是否存在,如果沒有,我們需要下載CH340驅(qū)動,

    2024年02月03日
    瀏覽(98)
  • CH32V203 單片機串口使用

    以串口2為例,初始化代碼如下: 發(fā)送數(shù)據(jù)代碼如下: USART_SendData(USART2, 0x55); ?以上代碼使用 MounRiver Studio 開發(fā),單片機型號 :CH32V203C8T6

    2024年02月12日
    瀏覽(16)
  • Proteus8仿真:51單片機使用串口進行雙機通訊

    Proteus8仿真:51單片機使用串口進行雙機通訊

    元器件 名稱 電阻 RES 51單片機 AT89C51 電容 CAP 晶振 CRYSTAL BCD數(shù)碼管 7SEG-BCD 串口初始化: 首先根據(jù)相關(guān)的串口寄存器編寫對應(yīng)的初始化代碼: STC89C51單片機設(shè)有兩個定時器/計數(shù)器,因定時器1有4種工作方式,而常選用定時器1的工作方式2(8位自動重裝)作為波特率的溢出率。 串口

    2024年02月04日
    瀏覽(24)
  • 51單片機UART串口通信實現(xiàn)接收PC的字符串

    51單片機UART串口通信實現(xiàn)接收PC的字符串

    ????????基本思路是觸發(fā)串口接收中斷之后,在串口中斷服務(wù)函數(shù)中處理接收到的字節(jié)并將其連接成字符串存入全局變量中。 隱含的額外工作有: 1.區(qū)分是發(fā)送中斷還是接收中斷,兩者都會進入同一個中斷服務(wù)子函數(shù); 2.判斷已接收到了句末,暫停接收,并通過標志位告知

    2023年04月20日
    瀏覽(27)
  • Proteus基于51單片機利用虛擬串口實現(xiàn)主從機的通信

    Proteus基于51單片機利用虛擬串口實現(xiàn)主從機的通信

    最近在學校做單片機課程實驗時,需要在Proteus上實現(xiàn)串口的通信,具體要求如下: 在此記錄一下本人的解題方案,首先Proteus中的原理圖繪制如下 其中虛擬串口可在元件庫中搜索COMPIM獲得。 將原件按上圖連接完畢后,將各個虛擬串口的收發(fā)波特率設(shè)置為19200,并分別賦予CO

    2024年02月08日
    瀏覽(49)
  • 利用Web Serial API實現(xiàn)Vue與單片機串口通信

    利用Web Serial API實現(xiàn)Vue與單片機串口通信

    ????????Web Serial API 是一項 Web 技術(shù),用于在瀏覽器中訪問串行端口設(shè)備(如 Arduino、傳感器等)并與之通信。它提供了一組 JavaScript 接口,使得 Web 應(yīng)用程序可以通過 USB 串行端口連接到硬件設(shè)備,并進行數(shù)據(jù)發(fā)送和接收操作。 ????????瀏覽器版本:Google Chrome?版本

    2024年02月14日
    瀏覽(19)
  • 51單片機串口通信的原理及使用方法(附串口收發(fā)數(shù)據(jù)例程代碼)

    51單片機串口通信的原理及使用方法(附串口收發(fā)數(shù)據(jù)例程代碼)

    溫馨提示:本篇文章詳細介紹了串口的原理及使用方法,涉及的內(nèi)容較多,但也較為全面,學者若想充分掌握串口通信的原理和具體使用方法(部分限于51單片機),請靜下心來將其讀完,相信一定會受益匪淺!而且對32單片機的串口學習也有非常好的輔助! 一.串行通信的原

    2024年02月09日
    瀏覽(24)
  • 如何編寫一個可變參數(shù)函數(shù)?如何讓所有單片機的所有串口實現(xiàn)printf函數(shù)?

    (1)由于真的復習不下去,就想著寫一篇博客拉回自己的心思。于是想到了長期有疑惑,但是一直沒有進行深入了解的C語言可變參數(shù)函數(shù)。 (2)本人查閱了一些網(wǎng)上的資料,以及自己的理解寫出來了這一片博客。首先再次感謝肯哥的答疑。 (3)借鑒文章: C51單片機中如何

    2024年02月11日
    瀏覽(16)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包