STM32-单通道ADC采集(DMA读取)实验

news2024/11/27 13:48:56

关于ADC的一些原理和实验我们已经有了2篇笔记,链接如下:

关于ADC的笔记1_Mr_rustylake的博客-CSDN博客

STM32-ADC单通道采集实验_Mr_rustylake的博客-CSDN博客

实验要求:通过ADC1通道1(PA1)采集电位器的电压,并显示ADC转换的数字量和换算后的电压值。

我们通过下表可以知道DMA1通道1的外设对应的就是ADC1的读取。

 

首先确定我们的最小刻度,Vref = 3.3V,所以0V <= Vin <= 3.3V,所以最小刻度是3.3V / 4096(2^12)。

接下来确定转换时间。采样时间239.5个ADC时钟周期为例,可以得到转换时间为21us。

时间转换公式参考如下公式:Tcvtmin=(12.5+X)周期=(12.5 + X)/(12MHz)=21us。

 因为使用的是DMA读取,所以采取连续转换模式,因为使用的是单通道,所以不扫描。

接下来我们编写实验代码:

先编写函数代码adc.c:

#include "./BSP/ADC/adc.h"
 
ADC_HandleTypeDef g_adc_handle;
DMA_HandleTypeDef g_dma_handle;
uint8_t g_adc_dma_sta; //标志DMA的传输是否完成

void adc_dam_init(void){
 
    ADC_ChannelConfTypeDef adc_ch_conf;

    __HAL_RCC_DMA1_CLK_ENABLE();

    g_dma_handle.Instance = DMA1_Channel1;
    g_dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;  //外设到内存
    g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;  //因为选取的是DMA1的数据寄存器,选择不增量
    g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;  //对于存储器需要存储多个数据,所以选择增量模式
    g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据位宽,我们选择16位半字(全字可以理解为全角中文字符)
    g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;  //存储器数据位宽,我们也选择16位半字
    g_dma_handle.Init.Mode = DMA_NORMAL;   //选择普通模式,因为在传输完成之后我们需要进行进一步操作现实我们获取到的值,所以选择normal
    g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;   //只有1个DMA随便选

    HAL_DMA_Init(&g_dma_handle);
    //联系DMA和ADC的句柄
    __HAL_LINKDMA(&g_adc_handle, DMA_Handle, &g_dma_handle);  //第二个参数为第一个ADC句柄的第三个成员,指向对应的DMA句柄
 
    g_adc_handle.Instance = ADC1;
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; //不扫描
    g_adc_handle.Init.ContinuousConvMode = ENABLE; //连续模式
    g_adc_handle.Init.NbrOfConversion = 1; //转换通道数为1,单通道
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE; //不用间断模式
    g_adc_handle.Init.NbrOfDiscConversion = 0; //无间断模式则无间断通道
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //外部软件触发
 
    adc_ch_conf.Channel = ADC_CHANNEL_1;
    adc_ch_conf.Rank = ADC_REGULAR_RANK_1; //转换顺序
    adc_ch_conf.SamplingTime = ADC_SMAPLINGTIME_239CYCLES_5; //设置为最大值
    HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);

    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 2, 3);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    HAL_ADC_Init(&g_adc_dma_handle);
    HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);
}
 
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){
 
    if(hadc->Instance == ADC1){
        GPIO_InitTypeDef gpio_init_struct;
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
 
        __HAL_RCC_GPIOA_CLK_ENABLE();  //使能ADC时钟
        __HAL_RCC_ADC1_CLK_ENABLE();   //使能GPIO时钟
 
        gpio_init_struct.Pin = GPIO_PIN_1;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG; //模拟模式
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
 
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; //选择ADC外设时钟设置
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; //选择6分频,72/6=12MHz
 
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init, &g_adc_dma_handle);
    }
}
 
uint32_t adc_get_result(void){
 
    HAL_ADC_Start(&g_adc_handle);
    HAL_ADC_PollForConversion(&g_adc_handle, 10); //第二个参数比1大就行
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);
}
 
uint32_t adc_get_result_average(uint32_t ch, uint8_t times){
 
    uint32_t temp_val = 0;
    uint8_t t;
 
    for(t = 0; t < times; t++){
        temp_val += adc_get_result();
        delay_ms(5);
    }
 
    return temp_val / times;
}

void adc_dma_enable(uint16_t cndtr){

    ADC1->CR2 &= ~(1 << 0); //关闭ADC
    DMA1_Channel1->CCR &= ~(1 << 0);//关闭DMA
    while(DMA1_Channel1->CCR & (1 << 0));
    DMA1_Channel1->CNDTR = cndtr;
    DMA1_Channel1->CCR |= (1 << 0); //开启DMA
    ADC1->CR2 |= (1 << 0);  //开启ADC

    ADC1->CR2 |= (1 << 22);  //触发规则组转换
}

void DMA1_Channel1_IRQHandle(void){

    if(DMA1->ISR & (1 << 1)){
        g_adc_dma_sta = 1;
        DMA1->IECR |= 1 << 1;
    }
}

接下来编写函数头文件adc.h:

#ifndef __ADC_H
#define __ADC_H

#include "SYSTEM/sys/sys.h"
#include "BSP/DMA/dma.h"

extern ADC_HandleTypeDef g_adc_handle;
 
void adc_dma_init(void);
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc);
uint32_t adc_get_result(void);
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);
void adc_dma_enable(uint16_t cndtr);
void DMA1_Channel1_IRQHandle(void);
 
#endif

接下来编写主函数代码main.c:

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"


#define ADC_DMA_BUF_SIZE        100         /* ADC DMA采集 BUF大小 */
uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];   /* ADC DMA BUF */

extern uint8_t g_adc_dma_sta;               /* DMA传输状态标志, 0,未完成; 1, 已完成 */

int main(void)
{
    uint16_t i;
    uint16_t adcx;
    uint32_t sum;
    float temp;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */

    adc_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", 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); /* 先在固定位置显示小数点 */

    adc_dma_enable(ADC_DMA_BUF_SIZE);   /* 启动ADC DMA采集 */

    while (1)
    {
        if (g_adc_dma_sta == 1)
        {
            /* 计算DMA 采集到的ADC数据的平均值 */
            sum = 0;

            for (i = 0; i < ADC_DMA_BUF_SIZE; i++)   /* 累加 */
            {
                sum += g_adc_dma_buf[i];
            }

            adcx = sum / ADC_DMA_BUF_SIZE;           /* 取平均值 */

            /* 显示结果 */
            lcd_show_xnum(134, 110, adcx, 4, 16, 0, BLUE);      /* 显示ADCC采样后的原始值 */

            temp = (float)adcx * (3.3 / 4096);                  /* 获取计算后的带小数的实际电压值,比如3.1111 */
            adcx = temp;                                        /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
            lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);      /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

            temp -= adcx;                                       /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
            temp *= 1000;                                       /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
            lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);   /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */

            g_adc_dma_sta = 0;                                  /* 清除DMA采集完成状态标志 */
            adc_dma_enable(ADC_DMA_BUF_SIZE);                   /* 启动下一次ADC DMA采集 */
        }

        LED0_TOGGLE();
        delay_ms(100);
    }
}

到这里我们的实验代码编写就完成了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/524817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

专升本资料怎么找?可以通过哪些渠道找到?

统招专升本专科生每个人只有一次考试的机会&#xff0c;近年来越来越多的人注意到学历的重要性&#xff0c;报考的人也越来越多&#xff0c;竞争激烈。 所以想要成功上岸&#xff0c;光靠靠努力可不行&#xff0c;拥有一个好的复习资料你就成功了一半。 随着粉丝量越来越大&am…

让思维结构化: 美团四大名著之 <金字塔原理>

目录 什么是金字塔原理 纵向结构 横向结构 如何构建金字塔模型 纵向结构 自上而下法 自下而上法 总结 自下而上思考 自上而下表达&#xff0c;结论先行 横向结构 时间顺序 空间顺序 重要性顺序 演绎推理 SCQA方法 序言&#xff08;前言/引言&#xff09;&#x…

如何降低OBS推流直播延迟的问题?

使用推流OBS工具进行直播推流操作时&#xff0c;默认的推流关键帧间隔是10秒&#xff0c;而客户端在播放时&#xff0c;通常需要3个关键帧的数据才会开始播放&#xff0c;为了实现更低的延迟&#xff0c;您需要在推流时将关键帧的间隔设置的小一些&#xff0c;您可以逐步调整这…

Netty(4)

Netty 文章目录 Netty6 Netty 核心模块6.1 EventLoopGroup 与 NioEventLoopGroup6.2 Bootstrap/ServerBootstrap6.3 ChannelPipline、ChannelHandler、ChannelHandlerContext6.3.1 三者的关系6.3.2 ChannelPipline6.3.3 ChannelHandler6.3.4 ChannelHandlerContext6.3.5 三者创…

微服务---Redis实用篇-黑马头条项目-优惠卷秒杀功能(使用java阻塞队列对秒杀进行异步优化)

Redis实用篇-黑马头条项目-优惠卷秒杀功能(使用java阻塞队列对秒杀进行异步优化) 1、秒杀优化 1.1 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求&#xff0c;此时会请求nginx&#xff0c;nginx会访问到tomcat&#xff0c;而tomcat中的程序&#xff0c;会进…

LeetCode_Day1 | 关于数组双指针及二分法查询

LeetCode_数组 704.二分查找1.题目描述2. 做题前要想明白什么3. 左闭右闭代码4.左闭右开代码5. 关于中间值溢出问题6. 图解举例(左闭右闭) 27.移除元素1. 题目描述2. 暴力法3. 快慢指针法4. 双向双指针法 704.二分查找 1.题目描述 给定一个 n 个元素有序的&#xff08;升序&a…

15天学习MySQL计划(运维篇)分库分表-监控-第十四天

15天学习MySQL计划分库分表-监控-第十四天 1.介绍 1.问题分析 ​ 随着互联网及移动互联网的发展&#xff0c;应用系统的数据量也是成指数式增加&#xff0c;若采用但数据进行数据存储&#xff0c;存在以下性能瓶颈&#xff1a; IO瓶颈&#xff1a;热点数据太多&#xff0c;数…

【2023/05/14】Apple I

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第9天。 Share I cannot choose the best.The best choose me. 译文&#xff1a; 我不能选择那最好的。 是那最好的选择我。 They throw their shadows before them who carry their lantern on thei…

MySQL基础-多表查询

本文介绍MySQL的多表查询 文章目录 多表关系一对多多对多一对一 查询内连接外连接自连接联合查询子查询标量子查询列子查询行子查询表子查询 多表查询案例 多表关系 一对多&#xff08;多对一&#xff09;多对多一对一 一对多 案例&#xff1a;部门与员工 关系&#xff1a;一…

Go Wails Docker图形界面管理工具 (4)

文章目录 1. 前言2. 效果图3. 代码 1. 前言 接上篇&#xff0c;本次添加Docker日志查看功能 2. 效果图 3. 代码 直接调用官方库 app.go func (a *App) Log(ID string) (string, error) {reader, err : Cli.ContainerLogs(context.Background(), ID, types.ContainerLogsOption…

最快鉴别网工能力的方法,就三个字

大家好&#xff0c;我是老杨。 可以投个票&#xff0c;让我了解了解你的近况。 转眼2023年都过去3天了&#xff0c;我这的人事部门又开始找我问招聘画像&#xff0c;每年这时候&#xff0c;我都得头痛一阵子。 从技术转管理之后&#xff0c;最明显的差异&#xff0c;就是从“…

TLS反调试

一、TLS概念 线程局部存储&#xff08;Thread Local Storage&#xff0c;TLS&#xff09;是一种线程级别的存储机制&#xff0c;它允许每个线程在运行时都拥有自己的私有变量&#xff0c;这些变量只能被该线程访问&#xff0c;而不会被其他线程所共享。 1、TLS的出现是为了解…

【 Python 全栈开发 ⑭ 】数据操作方法

文章目录 一、运算符&#xff08;1&#xff09;&#xff1a;加号二、运算符&#xff08;2&#xff09;&#xff1a;乘号三、in 与 not in四、len()五、del六、max() 与 min()七、数据类型转换 一、运算符&#xff08;1&#xff09;&#xff1a;加号 运算符 “” 的作用是 “合…

〖Web全栈开发②〗—网络编程基础(下)

〖Web全栈开发②〗—网络编程基础&#xff08;下&#xff09; &#xff08;一&#xff09;TCP 网络应用程序开发流程1. TCP 网络应用程序开发流程的介绍2. TCP 客户端程序开发流程的介绍3. TCP 服务端程序开发流程的介绍4. 小结 &#xff08;二&#xff09;socket之send和recv原…

4.LiCTF NSSCTF WEB方向部分 WriteUp

文章目录 0x01、我FLAG呢&#xff1f;【源码信息泄露js信息泄露】0x02、导弹迷踪【js信息泄露】0x03、Follow me and hack me【hackbarburp备份文件】0x04、PHP是世界上最好的语言&#xff01;&#xff01;【代码执行 getshell】0x05、Vim yyds【命令执行 getshell】0x06、作业…

孙鑫VC++第一章 Windows内部运行原理

1. Windows应用程序&#xff0c;操作系统&#xff0c;计算机硬件之间的相互关系 1箭头表示操作系统控制输出设备2箭头表示操作系统可以得到输入设备信息3箭头表示应用程序通知操作系统执行具体操作 操作系统提供给应用程序的接口 API4箭头表示输入设备变化告诉应用程序 Window…

线性表之双向链表(详解)

&#x1f355;博客主页&#xff1a;️自信不孤单 &#x1f36c;文章专栏&#xff1a;数据结构与算法 &#x1f35a;代码仓库&#xff1a;破浪晓梦 &#x1f36d;欢迎关注&#xff1a;欢迎大家点赞收藏关注 文章目录 &#x1f365;前言&#x1f352;双向链表1. 带头双向循环链表…

【C++】通序录管理系统

1、缘起 最近&#xff08;2023-04-24&#xff09;学习完了 C 编程语言的 基础语法&#xff0c;然后将这些基础语法的知识点整合到一起&#xff0c;实现一个 通讯录管理系统。以此来巩固以前所学习过的知识点&#xff0c;以求在后续的学习中能够灵活应用。 2、系统需求 通讯录是…

ChatGPT结合本地数据_llamaindex

1 功能 大模型学习的主要是通用数据&#xff0c;而用户可能需要让ChatGPT在本地的知识库中寻找答案。 普通用户不太可能训练大模型&#xff1b;由于本地数据格式丰富&#xff0c;内容烦多&#xff0c;且考虑到使用成本和token大小限制&#xff0c;也不可能在每次提问时都将所有…

balenaEtcher v1.18.1 开源跨平台镜像文件快速刻录工具

balenaEtcher 是一款开源免费的跨平台镜像文件快速刻录工具&#xff0c;使用体验感觉比软碟通UltraISO好用多了&#xff0c;推荐使用。它可以帮助用户快速将 ISO 文件、IMG 文件或者其他格式的镜像文件刻录到 USB 驱动器、SD 卡或者其他可烧录介质上。它支持 Windows、macOS 和…