STM32 DMA +串口 收发数据(不用频繁进入中断) STM32G473 hal库

1.1 实现方法

利用DMA接收串口数据,在一定程度上会节省CPU 的消耗。大多数串口接收都是接收一个字节就中断一次,如果串口上需要实时接收大量的数据,这就会导致频繁进入中断,这样一来,对处理其它逻辑很不利。利用DMA收发串口数据可以做到收发都不进入中断。实现思路大致如下:

  1. 分别将 rx、tx的数据缓存在队列中;
  2. 收发数据放在需要外部周期调用的周期函数中;
  3. 周期从DMA获取接收数据存到队列中,同时将发送数据(队列)转到DMA中;

                                     

 

上代码,此处以STM32G473为例,其他系列需要修改DMA或串口的配置,理论上这种方法在大多数单片上都可以实现,(在华大单片F460 上面测试实现了的)

#include <../COM/MQueue.h>
#include <../HAL/MSerial0.h>  //
#include 
#include "sys.h"
struct MSerial0 gSerial0;
#define fa (gSerial0.base)
#define me (gSerial0)
#define my (gSerial0.pri)#define Serial0_BUFFER_SIZE (1024Ul)  // DMA 数据缓存最大长度
uint8_t USART1_R_data[Serial0_BUFFER_SIZE];
uint8_t USART1_T_data[Serial0_BUFFER_SIZE];struct MSerial0
{struct MSerial base;  //继承MSerialstruct{uint8_t       rxBuf[MSerial_BUFLEN];uint8_t       txBuf[MSerial_BUFLEN];struct MQueue rq;struct MQueue tq;} pri;
};UART_HandleTypeDef huart1;
uint8_t            bufrx[64];  //接收缓冲,最大64个字节.//串口1 初始化
void InitUsart1(uint32_t baudRate)
{// GPIO端口设置GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE();   //使能GPIOA时钟__HAL_RCC_USART1_CLK_ENABLE();  //使能USART1时钟GPIO_Initure.Pin       = GPIO_PIN_9 | GPIO_PIN_10;  // PA9,10GPIO_Initure.Mode      = GPIO_MODE_AF_PP;           //复用推挽输出GPIO_Initure.Pull      = GPIO_PULLUP;               //上拉GPIO_Initure.Speed     = GPIO_SPEED_FAST;           //高速GPIO_Initure.Alternate = GPIO_AF7_USART1;           //复用为USART1HAL_GPIO_Init(GPIOA, &GPIO_Initure);                //初始化PA9,10// USART 初始化设置huart1.Instance        = USART1;huart1.Init.BaudRate   = baudRate;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits   = UART_STOPBITS_1;huart1.Init.Parity     = UART_PARITY_NONE;huart1.Init.Mode       = UART_MODE_TX_RX;huart1.Init.HwFlowCtl  = UART_HWCONTROL_NONE;__HAL_UART_DISABLE_IT(&huart1, UART_IT_TC);if (HAL_UART_Init(&huart1) != HAL_OK){//        Error_Handler();}
}DMA_HandleTypeDef UART1TxDMA_Handler;  // DMA句DMA_HandleTypeDef UART1TxDMA_Handler; //DMA句
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;
// DMA 收发串口数据 初始化
void InitDMAforUsart1()
{__HAL_RCC_DMAMUX1_CLK_ENABLE();__HAL_RCC_DMA1_CLK_ENABLE();/* USART1 DMA Init *//* USART1_RX Init */hdma_usart1_rx.Instance                 = DMA1_Channel1;hdma_usart1_rx.Init.Request             = DMA_REQUEST_USART1_RX;hdma_usart1_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;hdma_usart1_rx.Init.PeriphInc           = DMA_PINC_DISABLE;hdma_usart1_rx.Init.MemInc              = DMA_MINC_ENABLE;hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_usart1_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;hdma_usart1_rx.Init.Mode                = DMA_CIRCULAR;hdma_usart1_rx.Init.Priority            = DMA_PRIORITY_MEDIUM;if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK){//   Error_Handler();}__HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);HAL_UART_Receive_DMA(&huart1, USART1_R_data, Serial0_BUFFER_SIZE);/* USART1_TX Init */hdma_usart1_tx.Instance                 = DMA1_Channel2;hdma_usart1_tx.Init.Request             = DMA_REQUEST_USART1_TX;hdma_usart1_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;hdma_usart1_tx.Init.PeriphInc           = DMA_PINC_DISABLE;hdma_usart1_tx.Init.MemInc              = DMA_MINC_ENABLE;hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_usart1_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;hdma_usart1_tx.Init.Mode                = DMA_NORMAL;hdma_usart1_tx.Init.Priority            = DMA_PRIORITY_MEDIUM;if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK){//   Error_Handler();}hdma_usart1_tx.Instance->CPAR = (uint32_t)&huart1.Instance->TDR;__HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx);
}uint32_t Cnum = 0;static void _Init(uint32_t baudRate)
{InitUsart1(baudRate);InitDMAforUsart1();MQueue(&my.rq, MSerial_BUFLEN);MQueue(&my.tq, MSerial_BUFLEN);
}static uint32_t _Recv(uint8_t* pData, uint32_t rxMaxLen)
{uint32_t max = rxMaxLen;while ((rxMaxLen) && (my.rq.deep > 0)){rxMaxLen--;*pData++ = my.rxBuf[my.rq.tail];my.rq.Del(&my.rq);}Cnum += (max - rxMaxLen);return max - rxMaxLen;
}static void _Send(uint8_t* dataflow, uint32_t num)
{num = (num < MSerial_BUFLEN) ? num : MSerial_BUFLEN;while (num--){my.txBuf[my.tq.head] = *dataflow++;my.tq.Add(&my.tq);}
}
static void _Period(void)
{static uint8_t  step = 1;int             len, i;static uint32_t last_add = 0;uint32_t        temp_add;temp_add = Serial0_BUFFER_SIZE - hdma_usart1_rx.Instance->CNDTR;if (last_add != temp_add){if (last_add < temp_add){len = temp_add - last_add;i   = last_add;while (len--){my.rxBuf[my.rq.head] = USART1_R_data[i++];my.rq.Add(&my.rq);  //队列增加}last_add = temp_add;}else{len = Serial0_BUFFER_SIZE - last_add;i = last_add;while (len--){my.rxBuf[my.rq.head] = USART1_R_data[i++];my.rq.Add(&my.rq);  //队列增加}i   = 0;len = temp_add;while (len--){my.rxBuf[my.rq.head] = USART1_R_data[i++];my.rq.Add(&my.rq);  //队列增加}last_add = temp_add;}}/**********step2 :处理发送**********/switch (step){case 0:if (__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TC2))  // DMA 发送完毕{__HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TC2);  //清除DMA2_Steam7传输完成标志hdma_usart1_tx.State = HAL_DMA_STATE_READY;hdma_usart1_tx.Lock  = HAL_UNLOCKED;step                 = 1;}hdma_usart1_rx.State = HAL_DMA_STATE_BUSY;break;case 1:len = 0;while ((my.tq.deep > 0)){USART1_T_data[len++] = my.txBuf[my.tq.tail];my.tq.Del(&my.tq);  //删除一个}if (len){HAL_DMA_Start(huart1.hdmatx, (uint32_t)USART1_T_data, (uint32_t)&huart1.Instance->TDR, len);huart1.Instance->CR3 |= USART_CR3_DMAT;  //使能串口DMA发送 breakstep = 0;}else{/* code */}break;}
}/** 构造函数* */
struct MSerial* MSerial0_Creat(void)  //
{memset(&me, 0, sizeof(struct MSerial0));fa.Init   = _Init;fa.Rx     = _Recv;fa.Tx     = _Send;fa.Period = _Period;return &fa;
}
#ifndef _BUSART0_H_
#define _BUSART0_H_#include "MSerial.h" //引用框架#define MSerial_BUFLEN   (1000)struct MSerial * MSerial0_Creat(void); //构造函数要重写#endif

 

#ifndef _M_SERIAL_H_
#define _M_SERIAL_H_/************************************* MSerial 对象模板************************************/
struct MSerial
{//MD_OBJ  parent;   //父类void     (* Init)(uint32_t baudRate);uint32_t (* Rx)( uint8_t *pData , uint32_t rxMaxLen);void     (* Tx)( uint8_t *pData , uint32_t txLen );void     (* Period)(void);};#endif

1.2使用方法:

 

 

  1. 创建对象  初始化pSerial0 = MSerial0_Creat();pSerial0->Init(115200)2.  滴答定时器可以产生系统时间,同时可以将串口的需要定时调用的函数放入,滴答定时器中void SysTick_Handler(void)
{uint8_t    reb[500];int        len;static int t = 0;//    SysTick_IncTick();t++;HAL_IncTick();if (t > 4){pSerial0->Period();t = 0;//        len = pSerial0->Rx(reb, 250);//        pSerial0->Tx(reb, len);}pCpu0->Period1ms();
}3.发送数据pSerial0->Tx((uint8_t*)"hello world", strlen("hello world"));4.接收数据uint8_t rxByte[128];pSerial0->Rx(rxByte, 128);

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部