首页 > 其他分享 >STM32-笔记36-ADC(模拟/数字转换器)

STM32-笔记36-ADC(模拟/数字转换器)

时间:2025-01-05 19:57:56浏览次数:8  
标签:init handle 36 STM32 Init adc HAL ADC

一、什么是ADC?

        全称:Analog-to-Digital Converter,指模拟/数字转换器。

        ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。

12 位 ADC 是一种逐次逼近型模拟数字转换器(0~4095(2^12))。它有多达 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。 ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。

模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。

ADC 的输入时钟不得超过 14MHz,它是由 PCLK2 经分频产生。

STM32F103C8T6 ADC资源:ADC1、ADC2,10 个外部输入通道

二、ADC工作原理(逐次逼近型)

三、ADC框图

规则组/注入组

四、转换顺序

        每个 ADC 规则通道只有一个数据寄存器16个通道一起共用这个寄存器,所以需要指定规则转换通道的转换顺序。
        规则通道中的转换顺序由三个寄存器控制:SQR1、SQR2、SQR3,它们都是32位寄存器。SQR寄存器控制着转换通道的数目和转换顺序,只要在对应的寄存器位SQx中写入相应的通道,这个通道就是第x个转换。

和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有一个JSQR寄存器 来控制,控制关系如下:

注入序列的转换顺序是从JSQx[ 4 : 0 ](x=4-JL[1:0])开始。只有当JL=4的时候,注入通道的转换顺序才会按照JSQ1、JSQ2、JSQ3、JSQ4的顺序执行。

五、触发转换方法

        1. 通过向控制寄存器 ADC-CR2 的 ADON 位写 1 来开启 ADC ,再将 SWSTART 位置 1 ,启动规则通道转换

        2. 也可以通过外部事件(如定时器)进行转换。

六、转化时间

计算最小的转换时间?

ADC 是挂载在 APB2 总线(PCLK2)上的,经过分频器得到 ADC 时钟(ADCCLK),最高 14 MHz。

72MHZ/2 = 36 MHZ > MAX14MHZ  (不行)

72MHZ/4 = 18 MHZ > MAX14MHZ  (不行)

72MHZ/6 = 12 MHZ < MAX14MHZ  (行)

经过分频器得到 ADC 时钟(ADCCLK)的时钟频率为12MHZ

转换时间=采样时间+12.5个周期

12.5个周期是固定的,一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M, 采样周期设置为 1.5 个周期,

转换时间 = 采样时间 (最小如上图所示:1.5个周期)+12.5个周期 = 14个周期/12MHZ = 1.17us

算出最短的转换时间为 1.17us。

七、ADC寄存器及库函数介绍

7.1 ADC状态寄存器(ADC_SR)

7.2 ADC控制寄存器 1(ADC_CR1)

7.3 ADC控制寄存器 2(ADC_CR2)

7.4 ADC采样时间寄存器 1(ADC_SMPR1)

7.5 ADC采样时间寄存器 2(ADC_SMPR2)

7.6 ADC注入通道数据偏移寄存器x (ADC_JOFRx)(x=1..4)

7.7 ADC规则序列寄存器 1(ADC_SQR1)

7.8 ADC注入序列寄存器(ADC_JSQR)

7.9 ADC 注入数据寄存器x (ADC_JDRx) (x= 1..4)

7.10 ADC规则数据寄存器(ADC_DR)

八、常用的函数

九、ADC单通道采集实验

使用 ADC1 采集通道 1 的电压值,通道 1 连接光敏电阻传感器。

复制项目文件夹19-串口打印功能

重命名为43-ADC单通道采集实验

打开项目文件

加载文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "adc.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* 初始化LED灯 */
    uart1_init(115200);
    adc_init();
    printf("hello world!\r\n");

    while(1)
    { 
        //换算成电压的形式输出
        printf("adc result: %f\r\n", (float)adc_get_result(ADC_CHANNEL_1) / 4096 * 3.3);
        delay_ms(500);
    }
}

adc.c

#include "adc.h"

ADC_HandleTypeDef adc_handle = {0};//ADC的句柄
//初始化ADC函数
void adc_init(void)
{
    adc_handle.Instance = ADC1;//基地址
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;//数据右对齐
    adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;//要不要扫描?不需要扫描
    adc_handle.Init.ContinuousConvMode = DISABLE;//连续模式?不连续转换模式
    adc_handle.Init.NbrOfConversion = 1;//转换个数?1个
    adc_handle.Init.DiscontinuousConvMode = DISABLE;//间断模式?不间断模式
    adc_handle.Init.NbrOfDiscConversion = 0;//间断个数0
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;//触发方式:软件触发
    HAL_ADC_Init(&adc_handle);
    
    HAL_ADCEx_Calibration_Start(&adc_handle);//ADC校准
}
//初始化msp函数
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)//判断是不是ADC1
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};//ADC时钟句柄
        GPIO_InitTypeDef gpio_init_struct = {0};//GPIO的句柄
        
        __HAL_RCC_ADC1_CLK_ENABLE();//打开ADC1的时钟
        __HAL_RCC_GPIOA_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分频
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);//ADC时钟初始化函数
    }
}
//通道配置的函数
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank, uint32_t stime)
{
    //
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    
    adc_ch_config.Channel = ch;//指定通道-外界传进来
    adc_ch_config.Rank = rank;//序列-外界传进来
    adc_ch_config.SamplingTime = stime;//取样时间-外界传进来
    HAL_ADC_ConfigChannel(hadc, &adc_ch_config);//通道配置
}
//ADC获取转换结果
uint32_t adc_get_result(uint32_t ch)
{
    //adc句柄,通道,指定的序列,采样时间
    adc_channel_config(&adc_handle, ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
    
    HAL_ADC_Start(&adc_handle);//开启ADC
    HAL_ADC_PollForConversion(&adc_handle, 10);//等待ADC转化完成
    //(ADC1转化的结果放在DR寄存器的低16位,这里强转为uint16_t就可以直接获取,
    //DR寄存器的高16位存放的是ACD2的转换结果)
    return (uint16_t)HAL_ADC_GetValue(&adc_handle);//获取ADC转化的结果
}

adc,h

#ifndef __ADC_H__
#define __ADC_H__

#include "sys.h"

void adc_init(void);
uint32_t adc_get_result(uint32_t ch);

#endif

十、ADC单通道采集实验+DMA读取

使用 ADC1 采集通道 1 的电压值+DMA读取,通道 1 连接光敏电阻传感器。

 复制项目文件43-ADC单通道采集实验

重命名为44-ADC单通道采集实验(DMA读取)

打开项目文件夹

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "adc.h"

uint16_t adc_result = 0;

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* 初始化LED灯 */
    uart1_init(115200);
    adc_dma_init((uint32_t *)&adc_result);
    printf("hello world!\r\n");

    while(1)
    { 
        //换算成电压的形式输出
        printf("adc result: %f\r\n", (float)adc_result / 4096 * 3.3);
        delay_ms(500);
    }
}

adc.c

#include "adc.h"

DMA_HandleTypeDef dma_handle = {0};//DMA的句柄
ADC_HandleTypeDef adc_handle = {0};//ADC的句柄
//初始化ADC函数
void adc_config(void)
{
    adc_handle.Instance = ADC1;//基地址
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;//数据右对齐
    adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;//要不要扫描?不需要扫描
    adc_handle.Init.ContinuousConvMode = ENABLE;//连续模式?连续转换模式
    adc_handle.Init.NbrOfConversion = 1;//转换个数?1个
    adc_handle.Init.DiscontinuousConvMode = DISABLE;//间断模式?不间断模式
    adc_handle.Init.NbrOfDiscConversion = 0;//间断个数0
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;//触发方式:软件触发
    HAL_ADC_Init(&adc_handle);
    
    HAL_ADCEx_Calibration_Start(&adc_handle);//ADC校准
}
//初始化msp函数
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)//判断是不是ADC1
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};//ADC时钟句柄
        GPIO_InitTypeDef gpio_init_struct = {0};//GPIO的句柄
        
        __HAL_RCC_ADC1_CLK_ENABLE();//打开ADC1的时钟
        __HAL_RCC_GPIOA_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分频
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);//ADC时钟初始化函数
    }
}
void dma_config(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();
    dma_handle.Instance = DMA1_Channel1;//通道1
    dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//外设到内存
    
    //内存相关配置
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;//半字对齐
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量失能模式
    
    //外设相关配置
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;//半字对其
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//内存增量失能模式
    
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级
    dma_handle.Init.Mode = DMA_CIRCULAR;//模式:循环搬运模式
    HAL_DMA_Init(&dma_handle);
    //这行代码的作用是将ADC的句柄(adc_handle)与DMA的句柄(dma_handle)关联起来
    __HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);//当ADC完成数据采集时,数据可以直接通过DMA传输到指定的内存地址,而不需要CPU的干预。
}
//通道配置的函数
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank, uint32_t stime)
{
    //
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    
    adc_ch_config.Channel = ch;//指定通道-外界传进来
    adc_ch_config.Rank = rank;//序列-外界传进来
    adc_ch_config.SamplingTime = stime;//取样时间-外界传进来
    HAL_ADC_ConfigChannel(hadc, &adc_ch_config);//通道配置
}
//adc dma 初始化函数
void adc_dma_init(uint32_t *mar)
{
    adc_config();
    //adc句柄,通道,序列号,ADC采样的时间
    adc_channel_config(&adc_handle, ADC_CHANNEL_1, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
    dma_config();
    //启动adc,ADC转化完的数据通过DMA搬运出来,从ADC外设传输到存储器的长度
    HAL_ADC_Start_DMA(&adc_handle, mar, 1);//mar是目标缓冲区的地址
}

adc.h

#ifndef __ADC_H__
#define __ADC_H__

#include "sys.h"

void adc_dma_init(uint32_t *mar);

#endif

十一、ADC多通道采集实验+DMA读取

使用 ADC1 采集通道 0 ~3 的电压值+DMA读取,通道 1 连接光敏电阻传感器。

复制项目文件44-ADC单通道采集实验(DMA读取)

重命名为:45-ADC多通道采集实验(DMA读取)

打开项目文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "adc.h"

uint16_t adc_result[4] = {0};

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* 初始化LED灯 */
    uart1_init(115200);
    adc_dma_init((uint32_t *)&adc_result);
    printf("hello world!\r\n");

    while(1)
    { 
        //换算成电压的形式输出
        printf("通道0电压: %f\r\n", (float)adc_result[0] / 4096 * 3.3);
        printf("通道1电压: %f\r\n", (float)adc_result[1] / 4096 * 3.3);
        printf("通道2电压: %f\r\n", (float)adc_result[2] / 4096 * 3.3);
        printf("通道3电压: %f\r\n\r\n", (float)adc_result[3] / 4096 * 3.3);
        delay_ms(500);
    }
}

adc.c

#include "adc.h"

DMA_HandleTypeDef dma_handle = {0};//DMA的句柄
ADC_HandleTypeDef adc_handle = {0};//ADC的句柄
//初始化ADC函数
void adc_config(void)
{
    adc_handle.Instance = ADC1;//基地址
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;//数据右对齐
    adc_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;//要不要扫描?需要扫描
    adc_handle.Init.ContinuousConvMode = ENABLE;//连续模式?连续转换模式
    adc_handle.Init.NbrOfConversion = 4;//转换个数?4个通道数目
    adc_handle.Init.DiscontinuousConvMode = DISABLE;//间断模式?不间断模式
    adc_handle.Init.NbrOfDiscConversion = 0;//间断个数0
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;//触发方式:软件触发
    HAL_ADC_Init(&adc_handle);
    
    HAL_ADCEx_Calibration_Start(&adc_handle);//ADC校准 
}
//初始化msp函数
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)//判断是不是ADC1
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};//ADC时钟句柄
        GPIO_InitTypeDef gpio_init_struct = {0};//GPIO的句柄
        
        __HAL_RCC_ADC1_CLK_ENABLE();//打开ADC1的时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();//打开GPIO口的时钟
        
        gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;//引脚
        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分频
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);//ADC时钟初始化函数
    }
}
void dma_config(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();
    dma_handle.Instance = DMA1_Channel1;//通道1
    dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//外设到内存
    
    //内存相关配置
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;//半字对齐
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量失能模式
    
    //外设相关配置
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;//半字对其
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//内存增量失能模式
    
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级
    dma_handle.Init.Mode = DMA_CIRCULAR;//模式:循环搬运模式
    HAL_DMA_Init(&dma_handle);
    //这行代码的作用是将ADC的句柄(adc_handle)与DMA的句柄(dma_handle)关联起来
    __HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);//当ADC完成数据采集时,数据可以直接通过DMA传输到指定的内存地址,而不需要CPU的干预。
}
//通道配置的函数
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank, uint32_t stime)
{
    //
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    
    adc_ch_config.Channel = ch;//指定通道-外界传进来
    adc_ch_config.Rank = rank;//序列-外界传进来
    adc_ch_config.SamplingTime = stime;//取样时间-外界传进来
    HAL_ADC_ConfigChannel(hadc, &adc_ch_config);//通道配置
}
//adc dma 初始化函数
void adc_dma_init(uint32_t *mar)
{
    adc_config();
    //adc句柄,通道,序列号,ADC采样的时间
    adc_channel_config(&adc_handle, ADC_CHANNEL_0, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
    adc_channel_config(&adc_handle, ADC_CHANNEL_1, ADC_REGULAR_RANK_2, ADC_SAMPLETIME_239CYCLES_5);
    adc_channel_config(&adc_handle, ADC_CHANNEL_2, ADC_REGULAR_RANK_3, ADC_SAMPLETIME_239CYCLES_5);
    adc_channel_config(&adc_handle, ADC_CHANNEL_3, ADC_REGULAR_RANK_4, ADC_SAMPLETIME_239CYCLES_5);
    dma_config();
    //启动adc,ADC转化完的数据通过DMA搬运出来,从ADC外设传输到存储器的长度
    HAL_ADC_Start_DMA(&adc_handle, mar, 4);//mar是目标缓冲区的地址,4个通道
}

adc.h

#ifndef __ADC_H__
#define __ADC_H__

#include "sys.h"

void adc_dma_init(uint32_t *mar);

#endif

标签:init,handle,36,STM32,Init,adc,HAL,ADC
From: https://blog.csdn.net/L_1068/article/details/144943430

相关文章

  • 每日一题洛谷B3655 [语言月赛202208] 天天爱跑步C语言
    #include<stdio.h>intmain(){ intn; scanf("%d",&n); intv1,v3,v7,v30,v120,v365; scanf("%d%d%d%d%d%d",&v1,&v3,&v7,&v30,&v120,&v365); intt=0; intcount=0; intsum=0; for......
  • springboot闲置物品交易系统-计算机毕业设计源码01364
    摘 要本项目是一个基于SpringBoot的闲置物品交易系统。该平台旨在为大学生提供一个便捷、高效的交易平台,使他们能够在校园内买卖闲置物品。通过该平台,学生们可以方便地找到自己需要的物品,同时也可以将自己不再使用的物品转让给其他有需求的同学。这样的"买卖同体"理念促......
  • (免费领源码)基于Java#SpringBoot#mysql#微信小程序的健身打卡平台的设计与实现13606-计
    摘 要随着人们健康意识的不断提高,健身已经成为一种流行的生活方式。然而,传统的健身方式往往受到时间和空间的限制,无法满足人们随时随地进行健身打卡的需求。为了解决这个问题,提出了一种基于SpringBoot微信小程序的健身打卡平台的设计与实现。本平台旨在提供一个便捷、实......
  • Python系列之例题100题(36-40题)
    Hello!友友们,话不多说直接干题!!!36:有序列表添加数据:有一个已经排好序的列表。现输入一个数,要求按原来的规律将它插入数组中。list=[1,3,8,20,40]n=int(input('请输入数据:'))list.append(n)list.sort()print(list)37:变量值互换:给定两个变量交换他们的值。a=5b=......
  • STM32烧写失败之Contents mismatch at: 0800005CH (Flash=FFH Required=29H) !
    一)问题:用ULINK2给STM32F103C8T6下载程序,下载方式设置如下:出现下面两个问题:1)下载问题界面如下:这个错误的信息大概可以理解为,在0x08000063地址上读取到flash存储为FF,但实际上应该写入08H,即校验时读取到数据与实际写入的不符。2)在DEBUG调试的时候,出现如下问题:调试的时候......
  • LeetCode136.只出现一次的数字
    题目给你一个非空整数数组nums,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。示例1:输入:nums=[2,2,1]输出:1示例2:输入:nums=[4,1,2,1,2]输出:4......
  • STM32-笔记33-Wi-Fi遥控风扇项目
    一、项目简介        电脑通过esp8266模块远程遥控风扇。PC端的网络调试助手(以服务端的模式连接客户端的esp8266)二、项目实现复制项目文件36-编程实现ESP8266连接TCP服务器重命名为:38-wifi控制风扇项目重命名为fan加载文件main.c#include"sys.h"#includ......
  • w136人口老龄化社区服务与管理平台
    ......
  • Bandizip(跨平台解压缩软件) v7.36 激活版
    虽然7-Zip是开源免费压缩工具中的佼佼者,但用得不是很顺手,今天试了下推荐的Bandizip却出人意料的好用!Bandizip是一款来自韩国的免费优秀的文件压缩/解压缩软件,支持Win与Mac,操作方式与WinRAR相似,支持压缩和解压rar、zip、7z等众多主流格式…解压支持的格式:7Z,AC......
  • Bandizip(跨平台解压缩软件) v7.36 激活版
    虽然7-Zip是开源免费压缩工具中的佼佼者,但用得不是很顺手,今天试了下推荐的Bandizip却出人意料的好用!Bandizip是一款来自韩国的免费优秀的文件压缩/解压缩软件,支持Win与Mac,操作方式与WinRAR相似,支持压缩和解压rar、zip、7z等众多主流格式…解压支持的格式:7Z,ACE,......