当前位置: 首页 > article >正文

STM32单片机芯片与内部33 ADC 单通道连续DMA

目录

一、ADC DMA配置——标准库

1、ADC配置

2、DMA配置

二、ADC DMA配置——HAL库

1、ADC配置

2、DMA配置

三、用户侧

1、DMA开关

(1)、标准库

(2)、HAL库

2、DMA乒乓

(1)、标准库

(2)、HAL库


上文提到了当转换速度较高的时候需要由DMA进行搬运。

一、ADC DMA配置——标准库

1、ADC配置

        可以看到ADC配置几乎不用变。

	// 使能ADC DMA 请求
	ADC_DMACmd(ADCx, ENABLE);

2、DMA配置

        最重要的是源地址、目的地址、传输大小。如下配置为将每次ADC的数据从DR源地址搬运到ADC_ConvertedValue变量,因为只有一个大小长度,因此设定为1,大小设置为两个字节。

__IO uint16_t ADC_ConvertedValue;
	DMA_InitTypeDef DMA_InitStructure;

	// 打开DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	// 打开ADC时钟
	ADC_APBxClock_FUN ( ADC_CLK, ENABLE );
	
	// 复位DMA控制器
	DMA_DeInit(ADC_DMA_CHANNEL);
	
	// 配置 DMA 初始化结构体
	// 外设基址为:ADC 数据寄存器地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t ) ( & ( ADCx->DR ) );
	
	// 存储器地址,实际上就是一个内部SRAM的变量
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;
	
	// 数据源来自外设
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	
	// 缓冲区大小为1,缓冲区的大小应该等于存储器的大小
	DMA_InitStructure.DMA_BufferSize = 1;
	
	// 外设寄存器只有一个,地址不用递增
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

	// 存储器地址固定
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; 
	
	// 外设数据大小为半字,即两个字节
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	
	// 存储器数据大小也为半字,跟外设数据大小相同
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	
	// 循环传输模式
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	

	// DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	
	// 禁止存储器到存储器模式,因为是从外设到存储器
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	// 初始化DMA
	DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
	
	// 使能 DMA 通道
	DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);

        有人会好奇,DR寄存器32位,为什么定义的16位,不会丢失什么嘛,前面数据手册介绍过。高16位用于双ADC,单ADC仅用到低16位,且一般右端对齐,则为低12位有效。

二、ADC DMA配置——HAL库

1、ADC配置

        同样不修改配置。

    HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t*)&ADC_ConvertedValue, 1);

2、DMA配置

        可以看到源地址和目的地址和长度最重要的被放在了HAL_ADC_Start_DMA中,这样可以实现不修改初始化的情况下,进行地址、长度的修改。

DMA_HandleTypeDef hdma_adcx;
__IO uint16_t ADC_ConvertedValue;
    // 开启DMA时钟
    RHEOSTAT_ADC_DMA_CLK_ENABLE();
    // 数据传输通道
     hdma_adcx.Instance = RHEOSTAT_ADC_DMA_STREAM;
  
     hdma_adcx.Init.Direction=DMA_PERIPH_TO_MEMORY;;            //存储器到外设
     hdma_adcx.Init.PeriphInc=DMA_PINC_DISABLE;                 //外设非增量模式
     hdma_adcx.Init.MemInc=DMA_MINC_ENABLE;                     //存储器增量模式 
     hdma_adcx.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;//外设数据长度:16位
     hdma_adcx.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD;   //存储器数据长度:16位
     hdma_adcx.Init.Mode= DMA_CIRCULAR;                         //外设普通模式
     hdma_adcx.Init.Priority=DMA_PDATAALIGN_HALFWORD;               //中等优先级

    //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
    HAL_DMA_Init(&hdma_adcx); 

    __HAL_LINKDMA( &ADC_Handle,DMA_Handle,hdma_adcx);

三、用户侧

还是前面的问题,如果需要处理1000个点,该怎么办?

        如果不需要前1000和后1000连续,则可以进行DMA的开关或ADC的开关,如果要求连续则开启DMA乒乓切换。

1、DMA开关

        说明不需要DMA的连续转换,而是传输1000个点则停止,处理后再进行一次DMA传输。

(1)、标准库

        需要修改为单次的缓冲区大小、单次传输模式。

define max_size 1000
__IO uint16_t ADC_ConvertedValue[max_size ];
	// 存储器地址,实际上就是一个内部SRAM的数组
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;
	// 缓冲区大小为1,缓冲区的大小应该等于存储器的大小
	DMA_InitStructure.DMA_BufferSize = max_size ;
	// 单次传输模式
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;	
    // 存储器地址,实际上就是一个内部SRAM的变量
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
    // 存储器地址递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 

        DMA中断赋值flag,主函数用户检测到flag=1说明单次1000个点的数据好了,就可以进行处理,然后再次开启DMA和ADC。

        DMA进入中断,关闭ADC转换并将数据转换结果置1。当然也可以直接在主函数检测DMA的传输完成TCIF。

// DMA 完成后产生中断,停止 DMA,用户处理数据
void DMA1_Channel1_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC1))  // 检查 DMA 传输完成中断
    {
        // 清除 DMA 中断标志
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        // 关闭 ADC 转换
        ADC_SoftwareStartConvCmd(ADCx, DISABLE);
        flag=1;
    }
}

        主函数检测到flag置位1后,说明可以进行数据处理,处理完成后,就可以重启DMA和ADC转换了。

if(flag==1)
{
    data_process();
    flag=0;
    // 重新启动 DMA
    DMA_Cmd(ADC_DMA_CHANNEL, ENABLE);
    // 重新启动 ADC
    ADC_SoftwareStartConvCmd(ADCx, ENABLE);
}

(2)、HAL库

        需要修改为单次的缓冲区大小、单次传输模式。

define max_size 1000
__IO uint16_t ADC_ConvertedValue[max_size ];
     hdma_adcx.Init.MemInc=DMA_MINC_ENABLE;                     //存储器增量模式 
     hdma_adcx.Init.Mode= DMA_NORMAL;                         //外设普通模式
    HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t*)&ADC_ConvertedValue, max_size );

有StartDMA,自然也有StopDMA,在中断服务函数直接执行即可。

HAL_ADC_Stop_DMA(&ADC_Handle)

2、DMA乒乓

        每次中断后修改目的地址,并开启新的中断,只需要修改中断服务函数即可。

(1)、标准库

__IO uint16_t ADC_ConvertedValue[2][max_size];  // 定义两个缓冲区,双缓冲区
    // 存储器地址,实际上就是一个内部SRAM的变量
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue[currentBuffer];
// DMA 完成后产生中断,停止 DMA,用户处理数据
void DMA1_Channel1_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC1))  // 检查 DMA 传输完成中断
    {
        // 清除中断标志
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        
        // 切换到下一个缓冲区
        currentBuffer = (currentBuffer + 1) % 2;  // 切换到另一个缓冲区

        // 重新配置DMA传输目标地址
        DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
        DMA_Cmd(ADC_DMA_CHANNEL, ENABLE); // 重新启动DMA
    }
}

        在主函数中判断,如果currentBuffer为1,说明当前在向第二部分写入此时可以处理第一部分,如果为0,则说明在向第一部分写入此时可以处理第二部分。

(2)、HAL库

        可以看到,得力于源地址和目的地址和长度最重要的被放在了HAL_ADC_Start_DMA中,可以很方便实现。

// DMA 完成后产生中断,停止 DMA,用户处理数据
void DMA1_Channel1_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC1))  // 检查 DMA 传输完成中断
    {
        // 清除中断标志
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        // 切换到下一个缓冲区
        currentBuffer = (currentBuffer + 1) % 2;  // 切换到另一个缓冲区

        HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t)ADC_ConvertedValue[currentBuffer], max_size );
    }
}

        在主函数中判断,如果currentBuffer为1,说明当前在向第二部分写入此时可以处理第一部分,如果为0,则说明在向第一部分写入此时可以处理第二部分。


http://www.kler.cn/a/445857.html

相关文章:

  • 堆【Lecode_HOT100】
  • wxWidgets使用wxStyledTextCtrl(Scintilla编辑器)的正确姿势
  • 使用vcpkg安装opencv>=4.9后#include<opencv2/opencv.hpp>#include<opencv2/core.hpp>无效
  • 安卓环境配置及打开新项目教程,2024年12月20日最新版
  • Web3.0安全开发实践:探索比特币DeFi生态中的PSBT
  • 以腾讯混元模型为例,在管理平台上集成一个智能助手
  • 子域提取工具,子域名收集神器,支持多种数据源和枚举选项,域名发现工具,可以为任何目标枚举海量的有效子域名,安全侦察工具,利用证书透明原则监控部署的新子域
  • html在线转换工具集合大全
  • AFL-Fuzz 的使用
  • 五十个网络安全学习项目——(九)无线网络安全分析
  • windows 钉钉缓存路径不能修改 默认C盘解决方案
  • Python 【大模型】之 使用千问Qwen2-VL 大模型训练LaTeX数学公式图,并进行LaTeX图识别测试
  • 校园快领系统|Java|SSM|VUE| 前后端分离
  • 模型训练之优化器
  • #渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍05-基于堆叠查询的SQL注入(Stacked Queries SQL Injection)
  • java全栈day17--Web后端实战(java操作数据库)
  • springboot3访问第三方接口
  • GNU Octave:特性、使用案例、工具箱、环境与界面
  • PHP接入美团联盟推广
  • textfile类型小文件合并
  • Unity动态读取外部图片转Texture2D,内存过大问题解决方案
  • [ThinkPHP]5.0.23-Rce 1
  • Oracle/MySQL 到 OceanBase 数据库迁移的关键问题与解决方案
  • python学opencv|读取图像(十五)BGR图像和HSV图像通道合并
  • M3D: 基于多模态大模型的新型3D医学影像分析框架,将3D医学图像分析从“看图片“提升到“理解空间“的层次,支持检索、报告生成、问答、定位和分割等8类任务
  • 【蓝桥杯每日一题】扫雷——暴力搜索