1.1 实现方法
利用DMA接收串口数据,在一定程度上会节省CPU 的消耗。大多数串口接收都是接收一个字节就中断一次,如果串口上需要实时接收大量的数据,这就会导致频繁进入中断,这样一来,对处理其它逻辑很不利。利用DMA收发串口数据可以做到收发都不进入中断。实现思路大致如下:
- 分别将 rx、tx的数据缓存在队列中;
- 收发数据放在需要外部周期调用的周期函数中;
- 周期从DMA获取接收数据存到队列中,同时将发送数据(队列)转到DMA中;
上代码,此处以STM32G473为例,其他系列需要修改DMA或串口的配置,理论上这种方法在大多数单片上都可以实现,(在华大单片F460 上面测试实现了的)
#include <../COM/MQueue.h>
#include <../HAL/MSerial0.h> //
#include <string.h>
#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; //继承MSerial
struct
{
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,10
GPIO_Initure.Mode = GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; //上拉
GPIO_Initure.Speed = GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate = GPIO_AF7_USART1; //复用为USART1
HAL_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发送 break
step = 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);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)