STM32使用DMA+空闲中断方式实现串口数据接收

欢迎入群共同学习交流

时间记录:2024/5/23

一、概念介绍

(1)DMA:直接存储器存取(DMA)(Direct Memory Access)也是一个挂载在AHB总线上的外设,用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。两个DMA控制器有12个通道(DMA1有7个通道,DMA2(只存在于大容量和互联网产品中)有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权

(2)STM32F103C8T6单片机通道介绍

二、串口接收数据代码示例

(1)头文件

c 复制代码
#ifndef __UART_H__
#define __UART_H__
#include "stm32f10x.h"

/**
 * DMA+空闲中断实现UART接收数据
*/

void vUart1Init(int baudrate);
void vUart1Send(u8 *data,int dataLen);
extern void (*vUart1Callback)(u8 *data,int dataLen);

#endif

(2)源文件

c 复制代码
#include "uart.h"
#include "string.h"

#define RX_BUF_SIZE 64
static u8 rx_buf[RX_BUF_SIZE];
void (*vUart1Callback)(u8 *data,int dataLen);

void vUart1Init(int baudrate)
{
    //1.初始化串口1GPIO口
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

    //2.初始化串口1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = baudrate;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART1,&USART_InitStruct);

    //3.初始化接收DMA通道
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    
    DMA_InitTypeDef DMA_InitStruct;
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR); //外设地址
    DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)rx_buf; //接收数据的内存地址
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //DMA方向,外设到内存
    DMA_InitStruct.DMA_BufferSize = RX_BUF_SIZE; //缓冲区大小
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不自增
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 移动数据的大小
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //模式,循环模式
    DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; //DMA优先级
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; // 禁用内存到内存
    DMA_Init(DMA1_Channel5,&DMA_InitStruct);

    //4.初始化NVIC
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    //5.使能串口、空闲中断、DMA接收
    USART_Cmd(USART1,ENABLE);
    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    DMA_Cmd(DMA1_Channel5,ENABLE);
}

void vUart1Send(u8 *data,int dataLen)
{
    for(int i=0;i<dataLen;i++)
    {
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
        USART_SendData(USART1,data[i]);
        while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
    }
}

//中断服务函数
void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
    {
        USART_ClearITPendingBit(USART1,USART_IT_IDLE);
        u8 temp = (u8)USART_ReceiveData(USART1);
        
        //处理接收数据
        DMA_Cmd(DMA1_Channel5,DISABLE);
        int recvLen = RX_BUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);
        vUart1Callback(rx_buf,recvLen);
        memset(rx_buf,0,sizeof(rx_buf));
        DMA_SetCurrDataCounter(DMA1_Channel5,RX_BUF_SIZE);
        DMA_Cmd(DMA1_Channel5,ENABLE);
    }
}
相关推荐
Joseph Cooper17 分钟前
STM32MP157 Linux驱动学习笔记(一):驱动基础与设备模型入门(同步互斥/LCD/I2C/Input)
linux·stm32·学习
Joseph Cooper20 分钟前
STM32MP157 Linux驱动学习笔记(二):硬件资源地基(Pinctrl/GPIO/Interrupt)
linux·stm32·学习
Z文的博客24 分钟前
FLASHDB实战详解 - 嵌入式KV/TSD数据库开发全攻略
stm32·单片机·嵌入式·flash·flashdb·w25q256
SUNNYSPY0011 小时前
120R016-ASEMI解锁电力电子的效率革命
单片机
芯希望1 小时前
芯伯乐XOPA340/XOPA2340/XOPA4340系列11MHz低噪声CMOS运放,高性能与低功耗的理想平衡
单片机·嵌入式硬件·dc-dc·工业控制·国产替代·电源管理·xblw芯伯乐
LCMICRO-133108477461 小时前
长芯微LCMDC8588完全P2P替代ADS8588,是一款16位、8通道同步采样的逐次逼近型(SAR)模数转换器
stm32·单片机·嵌入式硬件·fpga开发·硬件工程·模数转换器
VBsemi-专注于MOSFET研发定制2 小时前
面向车载冰箱高效可靠需求的功率器件选型策略与器件适配手册
单片机
进击的小头2 小时前
第17篇:嵌入式通用串行外设:UART_SPI_I2C接口原理与外设扩展应用
单片机·嵌入式硬件
LCG元2 小时前
STM32实战:基于FreeRTOS的LVGL嵌入式GUI移植(智能手表界面)
stm32·嵌入式硬件·智能手表
振浩微433射频芯片2 小时前
低功耗无线遥控新选择:深度解析VI520R ASK/OOK接收芯片与433MHz方案优势
网络·单片机·嵌入式硬件·物联网·智能家居