STM32标准库ADC和DMA知识点总结-1

简介: STM32标准库ADC和DMA知识点总结

前言

最近想重温一下32标准库的内容,所以打算写几篇博客梳理一遍之前学过的知识点,图片和代码都是参考江科大的,江科大32教程非常不错,不管是小白还是大佬想学习32标准库都可以看他b站的课程。

一、ADC模数转换器

ADC简介:

逐次逼近型ADC:

ADC框图:

ADC基本结构:

输入通道:

通道

ADC1

ADC2

ADC3

通道0

PA0

PA0

PA0

通道1

PA1

PA1

PA1

通道2

PA2

PA2

PA2

通道3

PA3

PA3

PA3

通道4

PA4

PA4

PF6

通道5

PA5

PA5

PF7

通道6

PA6

PA6

PF8

通道7

PA7

PA7

PF9

通道8

PB0

PB0

PF10

通道9

PB1

PB1

通道10

PC0

PC0

PC0

通道11

PC1

PC1

PC1

通道12

PC2

PC2

PC2

通道13

PC3

PC3

PC3

通道14

PC4

PC4

通道15

PC5

PC5

通道16

温度传感器

通道17

内部参考电压


单次转换,非扫描模式:

连续转换,非扫描模式:

单次转换,扫描模式:

连续转换,扫描模式:

触发控制:

数据对齐:

转换时间:

校准:

硬件电路:

(1)AD单通道

面包板接线图:

代码示例:

AD.c

#include "stm32f10x.h"                  // Device header
 
/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
  /*开启时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  //开启ADC1的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
  
  /*设置ADC时钟*/
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);           //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
  
  /*GPIO初始化*/
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);          //将PA0引脚初始化为模拟输入
  
  /*规则组通道配置*/
  ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);   //规则组序列1的位置,配置为通道0
  
  /*ADC初始化*/
  ADC_InitTypeDef ADC_InitStructure;            //定义结构体变量
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //模式,选择独立模式,即单独使用ADC1
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //数据对齐,选择右对齐
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   //连续转换,失能,每转换一次规则组序列后停止
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;     //扫描模式,失能,只转换规则组的序列1这一个位置
  ADC_InitStructure.ADC_NbrOfChannel = 1;         //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
  ADC_Init(ADC1, &ADC_InitStructure);           //将结构体变量交给ADC_Init,配置ADC1
  
  /*ADC使能*/
  ADC_Cmd(ADC1, ENABLE);                  //使能ADC1,ADC开始运行
  
  /*ADC校准*/
  ADC_ResetCalibration(ADC1);               //固定流程,内部有电路会自动执行校准
  while (ADC_GetResetCalibrationStatus(ADC1) == SET);
  ADC_StartCalibration(ADC1);
  while (ADC_GetCalibrationStatus(ADC1) == SET);
}
 
/**
  * 函    数:获取AD转换的值
  * 参    数:无
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);         //软件触发AD转换一次
  while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束
  return ADC_GetConversionValue(ADC1);          //读数据寄存器,得到AD转换的结果
}

AD.h

#ifndef __AD_H
#define __AD_H
 
void AD_Init(void);
uint16_t AD_GetValue(void);
 
#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
 
uint16_t ADValue;     //定义AD值变量
float Voltage;        //定义电压变量
 
int main(void)
{
  /*模块初始化*/
  OLED_Init();      //OLED初始化
  AD_Init();        //AD初始化
  
  /*显示静态字符串*/
  OLED_ShowString(1, 1, "ADValue:");
  OLED_ShowString(2, 1, "Voltage:0.00V");
  
  while (1)
  {
    ADValue = AD_GetValue();          //获取AD转换的值
    Voltage = (float)ADValue / 4095 * 3.3;    //将AD值线性变换到0~3.3的范围,表示电压
    
    OLED_ShowNum(1, 9, ADValue, 4);       //显示AD值
    OLED_ShowNum(2, 9, Voltage, 1);       //显示电压值的整数部分
    OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);  //显示电压值的小数部分
    
    Delay_ms(100);      //延时100ms,手动增加一些转换的间隔时间
  }
}

(2)AD多通道

面包板接线:

代码示例:

AD.c

#include "stm32f10x.h"                  // Device header
 
/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
  /*开启时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  //开启ADC1的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
  
  /*设置ADC时钟*/
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);           //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
  
  /*GPIO初始化*/
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);          //将PA0、PA1、PA2和PA3引脚初始化为模拟输入
  
  /*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*/
  
  /*ADC初始化*/
  ADC_InitTypeDef ADC_InitStructure;            //定义结构体变量
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //模式,选择独立模式,即单独使用ADC1
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //数据对齐,选择右对齐
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   //连续转换,失能,每转换一次规则组序列后停止
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;     //扫描模式,失能,只转换规则组的序列1这一个位置
  ADC_InitStructure.ADC_NbrOfChannel = 1;         //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
  ADC_Init(ADC1, &ADC_InitStructure);           //将结构体变量交给ADC_Init,配置ADC1
  
  /*ADC使能*/
  ADC_Cmd(ADC1, ENABLE);                  //使能ADC1,ADC开始运行
  
  /*ADC校准*/
  ADC_ResetCalibration(ADC1);               //固定流程,内部有电路会自动执行校准
  while (ADC_GetResetCalibrationStatus(ADC1) == SET);
  ADC_StartCalibration(ADC1);
  while (ADC_GetCalibrationStatus(ADC1) == SET);
}
 
/**
  * 函    数:获取AD转换的值
  * 参    数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
  ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); //在每次转换前,根据函数形参灵活更改规则组的通道1
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);         //软件触发AD转换一次
  while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束
  return ADC_GetConversionValue(ADC1);          //读数据寄存器,得到AD转换的结果
}

AD.h

#ifndef __AD_H
#define __AD_H
 
void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);
 
#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
 
uint16_t AD0, AD1, AD2, AD3;  //定义AD值变量
 
int main(void)
{
  /*模块初始化*/
  OLED_Init();        //OLED初始化
  AD_Init();          //AD初始化
  
  /*显示静态字符串*/
  OLED_ShowString(1, 1, "AD0:");
  OLED_ShowString(2, 1, "AD1:");
  OLED_ShowString(3, 1, "AD2:");
  OLED_ShowString(4, 1, "AD3:");
  
  while (1)
  {
    AD0 = AD_GetValue(ADC_Channel_0);   //单次启动ADC,转换通道0
    AD1 = AD_GetValue(ADC_Channel_1);   //单次启动ADC,转换通道1
    AD2 = AD_GetValue(ADC_Channel_2);   //单次启动ADC,转换通道2
    AD3 = AD_GetValue(ADC_Channel_3);   //单次启动ADC,转换通道3
    
    OLED_ShowNum(1, 5, AD0, 4);       //显示通道0的转换结果AD0
    OLED_ShowNum(2, 5, AD1, 4);       //显示通道1的转换结果AD1
    OLED_ShowNum(3, 5, AD2, 4);       //显示通道2的转换结果AD2
    OLED_ShowNum(4, 5, AD3, 4);       //显示通道3的转换结果AD3
    
    Delay_ms(100);      //延时100ms,手动增加一些转换的间隔时间
  }
}

HAL库实验可看:STM32 ADC介绍和应用_mq-4 adc-CSDN博客


STM32标准库ADC和DMA知识点总结-2

https://developer.aliyun.com/article/1508392

相关文章
|
12天前
使用STM32F103标准库实现定时器控制LED点亮和关闭
通过这篇博客,我们学习了如何使用STM32F103标准库,通过定时器来控制LED的点亮和关闭。我们配置了定时器中断,并在中断处理函数中实现了LED状态的切换。这是一个基础且实用的例子,适合初学者了解STM32定时器和中断的使用。 希望这篇博客对你有所帮助。如果有任何问题或建议,欢迎在评论区留言。
47 2
|
12天前
|
IDE 开发工具
使用STM32F103标准库实现自定义键盘
通过本文,我们学习了如何使用STM32F103标准库实现一个简单的自定义键盘。我们首先初始化了GPIO引脚,然后实现了一个扫描函数来检测按键状态。这个项目不仅能够帮助我们理解STM32的GPIO配置和按键扫描原理,还可以作为进一步学习中断处理和低功耗设计的基础。希望本文对你有所帮助,祝你在嵌入式开发的道路上不断进步!
45 4
|
12天前
|
存储 数据采集 数据安全/隐私保护
使用STM32F103读取TF卡并模拟U盘:使用标准库实现
通过以上步骤,你可以实现用STM32F103将TF卡内容变成U盘进行读取。这种功能在数据采集、便携式存储设备等应用中非常有用。如果你有更多的需求,可以进一步扩展此项目,例如添加文件管理功能、加密存储等。希望这篇博客能帮到你,如果有任何问题,欢迎在评论区留言讨论!
17 1
|
13天前
|
传感器
【经典案例】STM32F407使用HAL库配置I2C详解
STM32F407是一个强大的微控制器,广泛应用于嵌入式系统中。在许多应用中,我们需要使用I2C总线来与传感器、EEPROM、显示屏等外设进行通信。本文将详细介绍如何使用STM32 HAL库来配置和使用I2C接口。
26 2
|
13天前
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
30 1
|
1月前
【STM32】基于HAL库的360度编码器、摇杆代码编写
【STM32】基于HAL库的360度编码器、摇杆代码编写
|
1月前
|
传感器 算法
【STM32】I2C练习,HAL库读取MPU6050角度陀螺仪
【STM32】I2C练习,HAL库读取MPU6050角度陀螺仪
|
1月前
|
传感器 存储 缓存