【正点原子STM32连载】第三十四章 单通道ADC采集(DMA读取)实验 摘自【正点原子】APM32E103最小系统板使用指南

本文主要是介绍【正点原子STM32连载】第三十四章 单通道ADC采集(DMA读取)实验 摘自【正点原子】APM32E103最小系统板使用指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1)实验平台:正点原子APM32E103最小系统板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第三十四章 单通道ADC采集(DMA读取)实验

本章介绍使用APM32E103的DMA进行单通道的ADC采集。通过本章的学习,读者将学习到DMA、ADC的使用。
本章分为如下几个小节:
34.1 硬件设计
34.2 程序设计
34.3 下载验证

34.1 硬件设计
34.1.1 例程功能

  1. ADC1采集通道1(PA1)上的电压,并在LCD上显示ADC转换后电压的数字量和换算后的模拟量
  2. LED0闪烁,指示程序正在运行
    34.1.2 硬件资源
  3. LED
    LED0 - PB5
  4. 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
  5. ADC1
    通道1 - PA1
  6. DMA1
    通道1 - Channel1
    34.1.3 原理图
    本章实验使用的ADC1为APM32E103的片上资源,因此没有对应的连接原理图。
    34.2 程序设计
    34.2.1 Geehy标准库的DMA驱动
    本章实验中使用DMA1通道1的外设到存储器模式,将ADC1的转换结果保存到指定的存储器中,其具体的步骤如下:
    ①:根据需求配置DMA1通道1
    ②:使能DMA1通道1的传输完成中断
    ③:使能DMA1中断,并配置其相关的中断优先级
    ④:使能DMA1通道1进行数据传输
    在Geehy标准库中对应的驱动函数如下:
    ①:配置DMA通道
    请见第32.2.1小节中配置DMA通道的相关内容。
    ②:使能DMA的指定中断
    该函数用于使能DMA的指定中断,其函数原型如下所示:
    void DMA_EnableInterrupt(DMA_Channel_T *channel, uint32_t interrupt);
    该函数的形参描述,如下表所示:
    形参 描述
    channel DMA1_channelx,x可以从1到7或DMA2_channely,y可以从1至5
    例如:DMA1_Channel1、DMA1_Channel2等
    (在apm32e10x.h文件中有定义)
    interrupt 指定使能的中断
    例如:DMA_INT_TC、DMA_INT_HT等
    (在apm32e10x_dma.h文件中有定义)
    表34.2.1.1 函数DMA_EnableInterrupt()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    无 无
    表34.2.1.2 函数DMA_EnableInterrupt()形参描述
    该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_dma.h"void example_fun(void)
{/* 使能DMA1通道1传输完成中断 */DMA_EnableInterrupt(DMA1_Channel1, DMA_INT_TC);
}

③:使能DMA中断
请见第12.2.3小节中配置中断的相关内容
④:使能DMA通道
请见第32.2.1小节中使能DMA通道的相关内容。
34.2.2 Geehy标准库的ADC驱动
本章实验与上一章实验一样,使用ADC1的通道1(PA1引脚)采集外部电压,不同之处在于,本章使用利用DMA自动将ADC1转换的结果保存到指定的存储器中,其具体的步骤如下:
①:使能ADC1和DMA2时钟
②:配置ADC1引脚
③:配置ADC1
④:使能ADC1发出DMA请求
⑤:配置ADC1通道1规则通道
⑥:使能ADC1
⑦:启动ADC1转换规则通道
在Geehy标准库中对应的驱动函数如下:
①:配置ADC
请见第33.2.1小节中配置ADC的相关内容。
②:使能ADC的DMA模式
该函数用于使能ADC的DMA模式,其函数原型如下所示:
void ADC_EnableDMA(ADC_T* adc);
该函数的形参描述,如下表所示:
形参 描述
adc 指向ADC外设结构体的指针
例如:ADC1、ADC2等(在apm32e10x.h文件中有定义)
表34.2.2.1 函数ADC_EnableDMA()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表34.2.2.2 函数ADC_EnableDMA()返回值描述
该函数的使用示例,如下所示:

#include "apm32e10x.h"
#include "apm32e10x_adc.h"void example_fun(void)
{/* 使能ADC1的DMA模式 */ADC_EnableDMA(ADC1);
}

③:配置ADC规则通道
请见第33.2.1小节中配置ADC规则通道的相关内容。
④:使能ADC
请见第33.2.1小节中使能ADC的相关内容。
⑤:启动转换规则通道
请见第33.2.1小节中启动转换规则通道的相关内容。
34.2.3 ADC驱动
本章实验的ADC驱动主要负责向应用层提供ADC初始化和启动ADC的DMA采集的函数,同时实现DMA的中断回调函数。本章实验中,ADC的驱动代码包括adc.c和adc.h两个文件。
ADC驱动中,对DMA、GPIO、ADC的相关宏定义,如下所示:

/* ADC通道及引脚定义 */
#define ADC_ADCX_CHY_GPIO_PORT          GPIOA
#define ADC_ADCX_CHY_GPIO_PIN           GPIO_PIN_1 
#define ADC_ADCX_CHY_GPIO_CLK_ENABLE()  do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA); }while(0)#define ADC_ADCX                        ADC1 
#define ADC_ADCX_CHY                    ADC_CHANNEL_1
#define ADC_ADCX_CHY_CLK_ENABLE()       do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1); }while(0)#define ADC_ADCX_DMAx                   DMA1_Channel1
#define ADC_ADCX_DMAx_IRQn              DMA1_Channel1_IRQn
#define ADC_ADCX_DMAx_IRQHandler        DMA1_Channel1_IRQHandler
ADC驱动中,ADC的初始化函数,如下所示:
/*** @brief   初始化ADC和DMA* @note    公式:TCONV=采样时间+12.5个周期*          采样时间由 SMPCYCCFGx[2:0]位控制,最小采样周期为1.5个,当*          ADCCLK=14MHz,采样时间为1.5周期:TCONV=1.5l,周期+12.5,周期=14,周期=1us。* @param   无* @retval  无*/
void adc_dma_init(uint32_t mar)
{ADC_Config_T adc_init_struct;GPIO_Config_T gpio_init_struct;DMA_Config_T dma_init_struct;/* 使能时钟 */ADC_ADCX_CHY_CLK_ENABLE();                      /* 使能ADC时钟 */
ADC_ADCX_CHY_GPIO_CLK_ENABLE();                 /* 使能GPIOA时钟 */ 
/* 置ADC分频因子6,120MHz/6=20MHz,ADC时间频率不能超过20MHz */RCM_ConfigADCCLK(RCM_PCLK2_DIV_6);if ((uint32_t)ADC_ADCX_DMAx > (uint32_t)DMA2)
{
/* 使能DMA2时钟 */RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA2);}else
{
/* 使能DMA1时钟 */RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);}/* 配置ADC引脚 */gpio_init_struct.pin = ADC_ADCX_CHY_GPIO_PIN;   /* ADC通道IO引脚 */
gpio_init_struct.mode = GPIO_MODE_ANALOG;       /* 模拟输入 */
/* 初始化ADC引脚 */ GPIO_Config(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct); ADC_Reset(ADC_ADCX);                            /* 复位ADC1 *//* 配置ADC */adc_init_struct.mode = ADC_MODE_INDEPENDENT;    /* ADC工作模式:独立模式 */adc_init_struct.scanConvMode = DISABLE;         /* 模数转换工作在单通道模式 */
adc_init_struct.continuosConvMode = ENABLE;     /* 模数转换工作在连续转换模式 */
/* 转换由软件而不是外部触发启动 */
adc_init_struct.externalTrigConv = ADC_EXT_TRIG_CONV_None;
/* ADC数据右对齐 */
adc_init_struct.dataAlign = ADC_DATA_ALIGN_RIGHT;
/* 顺序进行规则转换的ADC通道的数目 */
adc_init_struct.nbrOfChannel = 1; 
/* 初始化外设ADCx的寄存器 */ADC_Config(ADC_ADCX, &adc_init_struct); ADC_EnableDMA(ADC_ADCX);                        /* 使能ADC发出DMA请求 */ADC_Enable(ADC_ADCX);                           /* 使能指定的ADC1 */
ADC_ResetCalibration(ADC_ADCX);                 /* 使能复位校准 */
/* 读取指定的ADC校准重置状态 */while(ADC_ReadResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);                     /* 开始校验 */
/* 获取指定的ADC校准开始标志 */while(ADC_ReadCalibrationStartFlag(ADC1));/* 配置ADC通道 */ADC_ConfigRegularChannel(ADC_ADCX, ADC_ADCX_CHY, 1, ADC_SAMPLETIME_239CYCLES5);                       /* 配置指定ADC规则通道 *//* 复位DMA1 */
DMA_Reset(ADC_ADCX_DMAx);
/* 等待DMA可配置 */while (ADC_ADCX_DMAx->CHCFG_B.CHEN != RESET);/* 外设基地址 */
dma_init_struct.peripheralBaseAddr = (uint32_t)&ADC1->REGDATA; 
/* DMA内存基地址 */
dma_init_struct.memoryBaseAddr = mar;
/* 数据传输方向 */
dma_init_struct.dir = DMA_DIR_PERIPHERAL_SRC;
/* 传输的数据项数目 */
dma_init_struct.bufferSize = 0;
/* 外设增量模式 */
dma_init_struct.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
/* 存储器递增模式 */
dma_init_struct.memoryInc = DMA_MEMORY_INC_ENABLE;
/* 外设数据大小 */
dma_init_struct.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;
/* 存储器数据大小 */
dma_init_struct.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
/* 正常工作模式 */
dma_init_struct.loopMode = DMA_MODE_NORMAL;
/* 优先级 */
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
/* DMA通道x没有设置为内存到内存传输 */
dma_init_struct.M2M = DMA_M2MEN_DISABLE;
/* 配置DMA通道 */DMA_Config(ADC_ADCX_DMAx, &dma_init_struct);DMA_ClearStatusFlag(DMA1_FLAG_TC1);             /* 清除指定DMA通道的标志 */NVIC_EnableIRQRequest(ADC_ADCX_DMAx_IRQn, 3, 0);/* 配置DMA中断 */DMA_EnableInterrupt(ADC_ADCX_DMAx, DMA_INT_TC); /* 使能DMA通道传输完成中断 */
}

从上面的代码中可以看出,该ADC的初始化函数与上一章实验中的ADC初始化函数是十分类似的,不过本章实验配置的ADC使能了连续转换模式,同时使能了DMA,因此也完成了DMA的相关初始化配置,同时,函数的形参为DMA传输存储器的目的地址。
ADC驱动中,启动ADC的DMA采集的函数,如下所示:

/*** @brief       使能一次DMA传输ADC数据* @param       ndtr: DMA传输的数据量* @retval      无*/
void adc_dma_enable(uint16_t ndtr)
{ADC_Disable(ADC_ADCX);                       /* 禁用ADC */DMA_Disable(ADC_ADCX_DMAx);                  /* 禁止DMA通道 */while (ADC_ADCX_DMAx->CHCFG_B.CHEN != RESET);/* 等待DMA可配置 */DMA_ConfigDataNumber(ADC_ADCX_DMAx, ndtr);   /* 配置DMA通道传输的数据项数目 */DMA_Enable(ADC_ADCX_DMAx);                   /* 使能DMA通道 */ADC_Enable(ADC_ADCX);                        /* 使能ADC */ADC_EnableSoftwareStartConv(ADC_ADCX);       /* 启动ADC转换规则通道 */
}

从上面的代码中可以看到,启动ADC的DMA传输就是先后使能DMA和ADC,因为ADC初始化中使能了ADC的连续转换模式,因此开启转换后,ADC便会连续地进行转换,同时DMA也会连续地将转换结果传输到指定的目的存储器中去,直至DMA传输完毕。
ADC驱动代码中,DMA的中断回调函数,如下所示:
/**

  • @brief DMA中断服务函数
  • @param 无
  • @retval 无
    /
    void ADC_ADCX_DMAx_IRQHandler(void)
    {
    /
    判断DMA1_Channel1传输完成中断标志 /
    if (DMA_ReadStatusFlag(DMA1_FLAG_TC1) == SET)
    {
    g_adc_dma_sta = 1; /
    标记DMA1_Channel1传输完成 /
    DMA_ClearStatusFlag(DMA1_FLAG_TC1); /
    清除DMA1_Channel1传输完成标志 */
    }
    }
    DMA的中断回调函数中可以看到,就是在DMA传输完成后,将DMA传输完成的标志变量置1。
    34.2.4 实验应用代码
    本章实验的应用代码,如下所示:
#define ADC_DMA_BUF_SIZE 100                         /* ADC DMA缓冲区大小 */static uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];     /* ADC DMA缓冲区 */
extern uint8_t g_adc_dma_sta;                        /* DMA传输完成标志 */int main(void)
{uint16_t i;uint32_t sum;uint16_t adcdata;uint16_t voltage;NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4); /* 设置中断优先级分组为组4 */sys_apm32_clock_init(15);                        /* 配置系统时钟 */delay_init(120);                                 /* 初始化延时功能 */usart_init(115200);                              /* 初始化串口 */led_init();                                      /* 初始化LED */lcd_init();                                      /* 初始化LCD */adc_dma_init((uint32_t)g_adc_dma_buf);           /* 初始化ADC和DMA */adc_dma_enable(ADC_DMA_BUF_SIZE);                /* 使能一次DMA传输ADC数据 */lcd_show_string(30, 50, 200, 16, 16, "APM32", RED);lcd_show_string(30, 70, 200, 16, 16, "ADC DMA TEST", RED);lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH1_VAL:", BLUE);lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH1_VOL:0.000V", BLUE);while (1){if (g_adc_dma_sta == 1)                 /* 等待DMA传输结束 */{sum = 0;for (i=0; i < ADC_DMA_BUF_SIZE; i++)/* 对ADC的多次采样值进行均值滤波 */{sum += g_adc_dma_buf[i];}adcdata = sum / ADC_DMA_BUF_SIZE;
/* 显示原始值 */lcd_show_xnum(134, 110, adcdata, 5, 16, 0, BLUE);           voltage = (adcdata * 3300) / 4095;  /* 计算实际电压值(扩大1000倍) */
/* 显示电压值的整数部分 */lcd_show_xnum(134, 130, voltage / 1000, 1, 16, 0, BLUE);
/* 显示电压值的小数部分(保留三位小数) */lcd_show_xnum(150, 130, voltage % 1000, 3, 16, 0x80, BLUE); g_adc_dma_sta = 0;                  /* 清除DMA传输结束标志 */adc_dma_enable(ADC_DMA_BUF_SIZE);   /* 使能下一次DMA传输ADC数据 */}LED0_TOGGLE();delay_ms(100);}
}

从上面的代码中可以看出,应用代码中定义了一个数组作为DMA传输的目的存储器,随后在完成相关初始化后,就开启ADC的DMA传输,随后便等待DMA传输完成,DMA传输完成后对所有ADC转换的结果进行均值滤波后在LCD上进行显示,同时显示转换的电压模拟量。
34.3 下载验证
在完成编译和烧录操作后,可以看到LCD上不断地刷新显示ADC1通道1(PA1引脚)采集到电压的数字量和模拟量,此时可以通过杜邦线给PA1引脚接入不同的电压值(注意共地,且输入电压不能超过3.3V,否则可能损坏开发板),可以看到LCD上显示的电压数字量和模拟量也随之改变。

这篇关于【正点原子STM32连载】第三十四章 单通道ADC采集(DMA读取)实验 摘自【正点原子】APM32E103最小系统板使用指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/611142

相关文章

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想

2025最新版Python3.13.1安装使用指南(超详细)

《2025最新版Python3.13.1安装使用指南(超详细)》Python编程语言自诞生以来,已经成为全球最受欢迎的编程语言之一,它简单易学易用,以标准库和功能强大且广泛外挂的扩展库,为用户提供包罗... 目录2025最新版python 3.13.1安装使用指南1. 2025年Python语言最新排名2.

Linux系统之authconfig命令的使用解读

《Linux系统之authconfig命令的使用解读》authconfig是一个用于配置Linux系统身份验证和账户管理设置的命令行工具,主要用于RedHat系列的Linux发行版,它提供了一系列选项... 目录linux authconfig命令的使用基本语法常用选项示例总结Linux authconfi

Nginx配置系统服务&设置环境变量方式

《Nginx配置系统服务&设置环境变量方式》本文介绍了如何将Nginx配置为系统服务并设置环境变量,以便更方便地对Nginx进行操作,通过配置系统服务,可以使用系统命令来启动、停止或重新加载Nginx... 目录1.Nginx操作问题2.配置系统服android务3.设置环境变量总结1.Nginx操作问题

Python如何实现读取csv文件时忽略文件的编码格式

《Python如何实现读取csv文件时忽略文件的编码格式》我们再日常读取csv文件的时候经常会发现csv文件的格式有多种,所以这篇文章为大家介绍了Python如何实现读取csv文件时忽略文件的编码格式... 目录1、背景介绍2、库的安装3、核心代码4、完整代码1、背景介绍我们再日常读取csv文件的时候经常

CSS3 最强二维布局系统之Grid 网格布局

《CSS3最强二维布局系统之Grid网格布局》CS3的Grid网格布局是目前最强的二维布局系统,可以同时对列和行进行处理,将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,本文介... 深入学习 css3 目前最强大的布局系统 Grid 网格布局Grid 网格布局的基本认识Grid 网

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

Deepseek使用指南与提问优化策略方式

《Deepseek使用指南与提问优化策略方式》本文介绍了DeepSeek语义搜索引擎的核心功能、集成方法及优化提问策略,通过自然语言处理和机器学习提供精准搜索结果,适用于智能客服、知识库检索等领域... 目录序言1. DeepSeek 概述2. DeepSeek 的集成与使用2.1 DeepSeek API

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具