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

2023-05-16

1.1 实现方法

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

  1. 分别将 rx、tx的数据缓存在队列中;
  2. 收发数据放在需要外部周期调用的周期函数中;
  3. 周期从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(使用前将#替换为@)

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

  • 140-基于stm32单片机智能晾衣杆控制系统Proteus仿真+源程序

    资料编号 140 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 ds1302时钟 光敏传感器 蜂鸣器 LED灯 制作一个基于stm32单片机智能晾衣杆控制系统Proteus仿真 2 通过光敏传感器
  • 138-基于stm32单片机汽车多功能仪表盘显示系统Proteus仿真+源程序

    资料编号 138 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 ds1302时钟 LED灯 蜂鸣器 电位器 制作一个基于stm32单片机汽车多功能仪表盘显示系统Proteus仿真 2 通过DHT1
  • 135-基于stm32单片机超声波非接触式感应水龙头控制系统Proteus仿真+源程序

    资料编号 135 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 电机 超声波传感器 制作一个基于stm32单片机超声波非接触式感应水龙头控制系统Proteus仿真 2 通过DHT11传感器检测当前
  • HAL库STM32常用外设教程(二)—— GPIO输入\输出

    HAL库STM32常用外设教程 二 GPIO输入 输出 文章目录 HAL库STM32常用外设教程 二 GPIO输入 输出 前言 一 GPIO功能概述 二 GPIO的HAl库驱动 三 GPIO使用示例 1 示例功能 四 代码讲解 五 总结
  • rt-thread studio中新建5.0不能用

    文章目录 一 版本对比 二 文件和文件夹打斜杠 在使用RT Thread studio创建新工程5 0版本的时候 结果发现新建完成之后程序不能正常运行 但是创建4 10版本的时候却能运行 那肯定是新版本出现了BUG 一 版本对比 首先对比了
  • STM32F4 通过软复位跳转到引导加载程序,无需 BOOT0 和 BOOT1 引脚

    我问这个问题是因为可以在这里找到类似问题的答案 通过应用程序跳转到 STM32 中的引导加载程序 即从用户闪存在引导模式下使用引导 0 和引导 1 引脚 用户 JF002 JF002回答 当我想跳转到引导加载程序时 我在其中一个备份寄存器中
  • 匹配 STM32F0 和 zlib 中的 CRC32

    我正在研究运行 Linux 的计算机和 STM32F0 之间的通信链路 我想对我的数据包使用某种错误检测 并且由于 STM32F0 有 CRC32 硬件 并且我在 Linux 上有带有 CRC32 的 zlib 所以我认为在我的项目中使用
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • STM32超声波——HC_SR04

    文章目录 一 超声波图片 二 时序图 三 超声波流程 四 单位换算 五 取余计算 六 换算距离 七 超声波代码 一 超声波图片 测量距离 2cm 400cm 二 时序图 1 以下时序图要先提供一个至少10us的脉冲触发信号 告诉单片机我准备
  • 物联网网关

    物联网网关是 连接物联网设备和互联网的重要桥梁 它负责将物联网设备采集到的数据进行处理 存储和转发 使其能够与云端或其它设备进行通信 物联网网关的作用是实现物联网设备与云端的无缝连接和数据交互 物联网网关功能 数据采集 物联网网关可以从物联
  • 最终启动顺序错误 - STM32L476 的 Eclipse System Workbench 调试

    我正在尝试调试和运行 STM32L476 的简单汇编代码 我已经设置了 Eclipse Oxygen 在 Eclipse 中安装了最新版本的 System Workbench 插件并安装了 ST Link 驱动程序 IDE 成功构建了程序
  • C、硬件抽象层中“extern”类型的变量

    我正在研究硬件抽象层 该 HAL 的目的是在 Linux 驱动程序和 MCU 驱动程序之间轻松切换 我正在研究SPI接口 下面是 打开 SPI接口的HAL函数的签名 哈尔 spi h spi handle t spi open spi po
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • 启用 DMA 的 UART Tx 模式

    我已经为 UART 在传输模式下编写了一个简单的设备驱动程序 并启用了 DMA 和中断 我使用的硬件是 omap 4460 pandaboard 其中加载了 Linux 3 4 下面我分享一下相关部分的代码 在开放阶段 dma map io
  • STM32的HAL中实现单按、长按和双按功能

    我正在尝试实现单击 双击和长按功能来执行不同的功能 到目前为止 我已经理解了单击和长按的逻辑 但我不知道如何检测双击 至于代码 我使用计数器实现了单击和长按 但代码仅停留在第一个 if 条件上 bool single press false
  • Arm:objcopy 如何知道 elf 中的哪些部分要包含在二进制或 ihex 中?

    我正在开发一个项目 其中涉及解析arm elf 文件并从中提取部分 显然 elf 文件中有很多部分没有加载到闪存中 但我想知道 objcopy 到底如何知道要在二进制文件中包含哪些部分以直接闪存到闪存中 以arm elf文件的以下reade
  • 嵌入式 C++11 代码 — 我需要 volatile 吗?

    采用 Cortex M3 MCU STM32F1 的嵌入式设备 它具有嵌入式闪存 64K MCU固件可以在运行时重新编程闪存扇区 这是由闪存控制器 FMC 寄存器完成的 所以它不像a b那么简单 FMC 获取缓冲区指针并将数据刻录到某个闪存
  • STM32 上的 ADC 单次转换

    我正在研究 STM32 F103x 上的 ADC 编程 并从最简单的情况 单次转换开始 测量内部温度传感器 连接到 ADC1 的值 并使用 USART 将其发送到 COM 端口 目标似乎很明确 但是当我尝试将源代码下载到闪存时 它不会向 C
  • 哪些变量类型/大小在 STM32 微控制器上是原子的?

    以下是 STM32 微控制器上的数据类型 http www keil com support man docs armcc armcc chr1359125009502 htm http www keil com support man d
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE

随机推荐