stm32f103硬件串口,dma收发
·
/* 当前使用 USART1:TX=PA9,RX=PA10 */
#define sys_usart USART1
#define sys_usart_IRQn USART1_IRQn
/* STM32F10x 固定映射:USART1_TX -> DMA1_Channel4,USART1_RX -> DMA1_Channel5 */
#define sys_usart_dma_tx DMA1_Channel4
#define sys_usart_dma_rx DMA1_Channel5
#define sys_TxGpio GPIOA
#define sys_TxPin GPIO_Pin_9
#define sys_RxGpio GPIOA
#define sys_RxPin GPIO_Pin_10
#define sys_usartClock() (RCC->APB2ENR |= RCC_APB2ENR_USART1EN)
#define sys_dmaClock() (RCC->AHBENR |= RCC_AHBENR_DMA1EN)
/* DMA 接收缓冲区大小;一次接收帧不能超过该长度 */
#define SYS_USART_RX_BUF_SIZE 128u
/* DMA 发送等待超时时间基数,按发送长度放大 */
#define SYS_USART_TX_TIMEOUT 0xFFFFu
/* 串口接收状态结构体 */
typedef struct {
uint8_t rxBuf[SYS_USART_RX_BUF_SIZE]; /* DMA 接收缓冲区 */
volatile uint8_t rxFrame; /* 接收到完整一帧标志 */
volatile uint16_t rxLen; /* 当前帧有效数据长度 */
} SYS_USART_DATA;
static SYS_USART_DATA sys_usart_data = {0};
static sys_usart_rx_callback_t sys_usart_rx_callback = 0;
/* 重新启动 DMA 接收,准备接收下一帧数据 */
static void sys_usartDmaRxRestart(void)
{
DMA_Cmd(sys_usart_dma_rx, DISABLE);
DMA_SetCurrDataCounter(sys_usart_dma_rx, SYS_USART_RX_BUF_SIZE);
DMA_Cmd(sys_usart_dma_rx, ENABLE);
}
/*
* 串口初始化
* bount:波特率,例如 115200
*/
void sys_usartInit(uint32_t bount)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
sys_usartClock();
sys_dmaClock();
/* PA9 配置为 USART1_TX,复用推挽输出 */
GPIO_InitStructure.GPIO_Pin = sys_TxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(sys_TxGpio, &GPIO_InitStructure);
/* PA10 配置为 USART1_RX,浮空输入 */
GPIO_InitStructure.GPIO_Pin = sys_RxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(sys_RxGpio, &GPIO_InitStructure);
/* 串口参数:8 位数据位、1 位停止位、无校验、无硬件流控 */
USART_InitStructure.USART_BaudRate = bount;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(sys_usart, &USART_InitStructure);
/* 配置 RX DMA:外设 USART_DR -> 内存 rxBuf */
DMA_DeInit(sys_usart_dma_rx);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&sys_usart->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sys_usart_data.rxBuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = SYS_USART_RX_BUF_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(sys_usart_dma_rx, &DMA_InitStructure);
/* 配置 TX DMA:内存发送缓冲区 -> 外设 USART_DR,每次发送前重新设置地址和长度 */
DMA_DeInit(sys_usart_dma_tx);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&sys_usart->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(sys_usart_dma_tx, &DMA_InitStructure);
/* 配置 USART1 空闲中断,用于判断一帧数据接收完成 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = sys_usart_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(sys_usart, USART_IT_IDLE, ENABLE);
USART_DMACmd(sys_usart, USART_DMAReq_Rx, ENABLE);
USART_DMACmd(sys_usart, USART_DMAReq_Tx, ENABLE);
USART_Cmd(sys_usart, ENABLE);
/* 开启第一次 DMA 接收 */
sys_usartDmaRxRestart();
}
/*
* 使用 DMA 发送一段数据
* data:待发送数据指针
* len :待发送数据长度
*/
void sys_usartSendData(uint8_t *data, uint16_t len)
{
uint32_t timeout;
if ((data == 0) || (len == 0)){
return;
}
/* 每次发送前重新配置 TX DMA 的内存地址和发送长度 */
DMA_Cmd(sys_usart_dma_tx, DISABLE);
sys_usart_dma_tx->CMAR = (uint32_t)data;
DMA_SetCurrDataCounter(sys_usart_dma_tx, len);
DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_TC4 | DMA1_FLAG_TE4);
DMA_Cmd(sys_usart_dma_tx, ENABLE);
/* 等待 DMA 发送完成,加入超时避免异常时死等 */
timeout = SYS_USART_TX_TIMEOUT * len;
while ((DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET) && (timeout != 0)) {
timeout--;
}
DMA_Cmd(sys_usart_dma_tx, DISABLE);
DMA_ClearFlag(DMA1_FLAG_TC4);
}
/*
* 获取接收到的一帧数据
* len:用于返回接收长度,可传入 0
* 返回值:有新数据时返回接收缓冲区指针,没有新数据时返回 0
* 注意:该缓冲区会被下一帧 DMA 接收复用,外部需要及时处理或拷贝
*/
uint8_t *sys_usartGetRxData(uint16_t *len)
{
if (sys_usart_data.rxFrame == 0) {
return 0;
}
sys_usart_data.rxFrame = 0;
if (len != 0) {
*len = sys_usart_data.rxLen;
}
return sys_usart_data.rxBuf;
}
/* 注册接收回调函数,收到一帧数据后会在 USART1 中断中调用 */
void sys_usartRegisterRxCallback(sys_usart_rx_callback_t callback)
{
sys_usart_rx_callback = callback;
}
/*
* 弱定义接收处理函数
* 外部可以在其他 C 文件中重新定义同名函数,实现自己的接收处理逻辑
*/
__weak void sys_usartRxDataHandle(uint8_t *data, uint16_t len)
{
(void)data;
(void)len;
}
/* USART1 中断服务函数:使用空闲中断判断 DMA 接收一帧结束 */
void USART1_IRQHandler(void)
{
volatile uint32_t tmp;
uint16_t len;
if (USART_GetITStatus(sys_usart, USART_IT_IDLE) != RESET) {
/* 清除 IDLE 标志:必须先读 SR,再读 DR */
tmp = sys_usart->SR;
tmp = sys_usart->DR;
(void)tmp;
/* 停止 DMA 后读取剩余计数,计算本帧长度 */
DMA_Cmd(sys_usart_dma_rx, DISABLE);
len = SYS_USART_RX_BUF_SIZE - DMA_GetCurrDataCounter(sys_usart_dma_rx);
if (len > 0) {
sys_usart_data.rxLen = len;
sys_usart_data.rxFrame = 1;
/* 预留外部处理入口:弱函数和注册回调二选一或同时使用 */
sys_usartRxDataHandle(sys_usart_data.rxBuf, len);
if (sys_usart_rx_callback != 0) {
sys_usart_rx_callback(sys_usart_data.rxBuf, len);
}
}
/* 重新开启 DMA,接收下一帧 */
sys_usartDmaRxRestart();
}
}
uint8_t *sys_rx = NULL;//用于串口接收
uint16_t sys_rtlen = 0;//接收数据长度
//main.c循环中调用
sys_rx = sys_usartGetRxData(&sys_rtlen);//接收到的数据
if((sys_rx != NULL) && (sys_rtlen > 0))
{
sys_usartSendData(sys_rx, sys_rtlen);//发送接收到的数据
}
更多推荐

所有评论(0)