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

stm32——hal库学习笔记(ADC)

这里写目录标题

  • 一、ADC简介(了解)
    • 1.1,什么是ADC?
    • 1.2,常见的ADC类型
    • 1.3,并联比较型工作示意图
    • 1.4,逐次逼近型工作示意图
    • 1.5,ADC的特性参数
    • 1.6,STM32各系列ADC的主要特性
  • 二、ADC工作原理(掌握)
    • 2.1,ADC框图简介(F1)
    • 2.2,参考电压/模拟部分电压(战舰为例)
    • 2.3,输入通道 ( F1为例)
    • 2.4,转换序列(F1为例)
    • 2.5,触发源(F1)
    • 2.6,转换时间(F1)
    • 2.7,数据寄存器(F1)
    • 2.8,中断
    • 2.9,单次转换模式和连续转换模式
    • 2.10,扫描模式
  • 三、单通道ADC采集实验(熟悉)
    • 3.1,实验简要(了解)
    • 3.2,ADC寄存器介绍(了解)
    • 3.3,单通道ADC采集实验配置步骤(掌握)
    • 3.4,编程实战:单通道ADC采集实验(掌握)
  • 四、单通道ADC采集(DMA读取)实验(熟悉)
    • 4.1,实验简要(了解)
    • 4.2,单通道ADC采集(DMA读取)实验配置步骤(掌握)
    • 4.3,编程实战:单通道ADC采集(DMA读取)实验(掌握)
  • 五、多通道ADC采集(DMA读取)实验(熟悉)
  • 六、单通道ADC过采样实验(熟悉)
    • 6.1,如何用过采样和求均值的方式提高ADC的分辨率?(熟悉)
    • 6.2,实验简要(了解)
    • 6.3,编程实战:单通道ADC过采样(16位分辨率)实验(掌握)
  • 七、内部温度传感器实验(熟悉)
    • 7.1,STM32内部温度传感器简介(了解)
    • 7.2,温度计算方法(熟悉)
    • 7.3,实验简要(了解)
    • 7.4,编程实战:内部温度传感器实验(掌握)
  • 八、光敏传感器实验(熟悉)
    • 8.1,光敏二极管简介(了解)
    • 8.2,实验原理(熟悉)
    • 8.3,实验简要(了解)
    • 8.4,编程实战:光敏传感器实验(掌握)

一、ADC简介(了解)

1.1,什么是ADC?

ADC,全称:Analog-to-Digital Converter,指模拟/数字转换器
在这里插入图片描述

1.2,常见的ADC类型

在这里插入图片描述

1.3,并联比较型工作示意图

优点:转换速度快
缺点:成本高、功耗高、分辨率低
在这里插入图片描述

1.4,逐次逼近型工作示意图

优点:结构简单、低功耗
缺点:转换速度较慢

特点:
分辨率和采样速度相互矛盾,
分辨率越高,采样速率越低
在这里插入图片描述

1.5,ADC的特性参数

在这里插入图片描述

1.6,STM32各系列ADC的主要特性

在这里插入图片描述

二、ADC工作原理(掌握)

2.1,ADC框图简介(F1)

①参考电压/模拟部分电压
②输入通道
③转换序列
④触发源
⑤转换时间
⑥数据寄存器
⑦中断
在这里插入图片描述

2.2,参考电压/模拟部分电压(战舰为例)

在这里插入图片描述

2.3,输入通道 ( F1为例)

在这里插入图片描述

2.4,转换序列(F1为例)

在这里插入图片描述
规则组和注入组执行优先级对比
在这里插入图片描述
规则序列和注入序列(F1为例)
在这里插入图片描述
在这里插入图片描述

2.5,触发源(F1)

在这里插入图片描述
在这里插入图片描述

2.6,转换时间(F1)

在这里插入图片描述
在这里插入图片描述

2.7,数据寄存器(F1)

在这里插入图片描述

2.8,中断

在这里插入图片描述

2.9,单次转换模式和连续转换模式

在这里插入图片描述

2.10,扫描模式

在这里插入图片描述
在这里插入图片描述

三、单通道ADC采集实验(熟悉)

3.1,实验简要(了解)

在这里插入图片描述

3.2,ADC寄存器介绍(了解)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3,单通道ADC采集实验配置步骤(掌握)

在这里插入图片描述

HAL_ADC_Init()HAL_ADCEx_Calibration_Start()

在这里插入图片描述

HAL_ADC_MspInit()     //配置NVIC、CLOCK、GPIO等

在这里插入图片描述

HAL_ADC_ConfigChannel()

在这里插入图片描述

HAL_ADC_Start()

在这里插入图片描述

HAL_ADC_PollForConversion()

在这里插入图片描述

HAL_ADC_GetValue()

相关HAL库函数介绍
在这里插入图片描述
关键结构体介绍(F1为例)
在这里插入图片描述
在这里插入图片描述

3.4,编程实战:单通道ADC采集实验(掌握)

(战舰为例)
在这里插入图片描述
adc.c

#include "./BSP/ADC/adc.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */
/*** @brief       ADC初始化函数*   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3*              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期*              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us* @param       无* @retval      无*/
void adc_init(void)
{g_adc_handle.Instance = ADC_ADCX;                        /* 选择哪个ADC */g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       /* 数据对齐方式:右对齐 */g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       /* 非扫描模式,仅用到一个通道 */g_adc_handle.Init.ContinuousConvMode = DISABLE;          /* 关闭连续转换模式 */g_adc_handle.Init.NbrOfConversion = 1;                   /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_handle.Init.DiscontinuousConvMode = DISABLE;       /* 禁止规则通道组间断模式 */g_adc_handle.Init.NbrOfDiscConversion = 0;               /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_handle);                             /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_handle);              /* 校准ADC */
}/*** @brief       ADC底层驱动,引脚配置,时钟使能此函数会被HAL_ADC_Init()调用* @param       hadc:ADC句柄* @retval      无*/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{if(hadc->Instance == ADC_ADCX){GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ADCX_CHY_CLK_ENABLE();                                /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                           /* 开启GPIO时钟 *//* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                 /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;             /* ADC通道IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                 /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);}
}/*** @brief       设置ADC通道采样时间* @param       adcx : adc句柄指针,ADC_HandleTypeDef* @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17* @param       stime: 采样时间  0~7, 对应关系为:*   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期* @param       rank: 多通道采集时需要设置的采集编号,假设你定义channle1的rank=1,channle2 的rank=2,那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 单通道DMA设置为 ADC_REGULAR_RANK_1*   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16* @retval      无*/
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{ADC_ChannelConfTypeDef adc_ch_conf;adc_ch_conf.Channel = ch;                            /* 通道 */adc_ch_conf.Rank = rank;                             /* 序列 */adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}/*** @brief       获得ADC转换后的结果* @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17* @retval      无*/
uint32_t adc_get_result(uint32_t ch)
{adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */HAL_ADC_Start(&g_adc_handle);                            /* 开启ADC */HAL_ADC_PollForConversion(&g_adc_handle, 10);            /* 轮询转换 */return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}/*** @brief       获取通道ch的转换值,取times次,然后平均* @param       ch      : 通道号, 0~17* @param       times   : 获取次数* @retval      通道ch的times次转换结果平均值*/
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++)     /* 获取times次数据 */{temp_val += adc_get_result(ch);delay_ms(5);}return temp_val / times;        /* 返回平均值 */
}

adc.h

#ifndef __ADC_H
#define __ADC_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 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{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_1                                 /* 通道Y,  0 <= Y <= 17 */ 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)    /* ADC1 时钟使能 *//******************************************************************************************/void adc_init(void);                                           /* ADC初始化函数 */
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);    /* 配置ADC通道 */
uint32_t adc_get_result(uint32_t ch);                          /* 获得某个通道值  */
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);   /* 得到某个通道给定次数采样的平均值 */#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"int main(void)
{uint16_t adcx;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_init();                             /* 初始化ADC */lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);lcd_show_string(30, 70, 200, 16, 16, "ADC 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){adcx = adc_get_result_average(ADC_ADCX_CHY, 10); /* 获取通道5的转换值,10次取平均 */lcd_show_xnum(134, 110, adcx, 5, 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. */LED0_TOGGLE();delay_ms(100);}
}

四、单通道ADC采集(DMA读取)实验(熟悉)

4.1,实验简要(了解)

在这里插入图片描述

4.2,单通道ADC采集(DMA读取)实验配置步骤(掌握)

在这里插入图片描述
相关HAL库函数介绍
在这里插入图片描述
关键结构体介绍
在这里插入图片描述

4.3,编程实战:单通道ADC采集(DMA读取)实验(掌握)

在这里插入图片描述

adc.c

#include "./BSP/ADC/adc.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */
/*** @brief       ADC初始化函数*   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3*              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期*              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us* @param       无* @retval      无*/
void adc_init(void)
{g_adc_handle.Instance = ADC_ADCX;                        /* 选择哪个ADC */g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       /* 数据对齐方式:右对齐 */g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       /* 非扫描模式,仅用到一个通道 */g_adc_handle.Init.ContinuousConvMode = DISABLE;          /* 关闭连续转换模式 */g_adc_handle.Init.NbrOfConversion = 1;                   /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_handle.Init.DiscontinuousConvMode = DISABLE;       /* 禁止规则通道组间断模式 */g_adc_handle.Init.NbrOfDiscConversion = 0;               /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_handle);                             /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_handle);              /* 校准ADC */
}/*** @brief       ADC底层驱动,引脚配置,时钟使能此函数会被HAL_ADC_Init()调用* @param       hadc:ADC句柄* @retval      无*/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{if(hadc->Instance == ADC_ADCX){GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ADCX_CHY_CLK_ENABLE();                                /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                           /* 开启GPIO时钟 *//* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                 /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;             /* ADC通道IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                 /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);}
}/*** @brief       设置ADC通道采样时间* @param       adcx : adc句柄指针,ADC_HandleTypeDef* @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17* @param       stime: 采样时间  0~7, 对应关系为:*   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期* @param       rank: 多通道采集时需要设置的采集编号,假设你定义channle1的rank=1,channle2 的rank=2,那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 单通道DMA设置为 ADC_REGULAR_RANK_1*   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16* @retval      无*/
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{ADC_ChannelConfTypeDef adc_ch_conf;adc_ch_conf.Channel = ch;                            /* 通道 */adc_ch_conf.Rank = rank;                             /* 序列 */adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}/*** @brief       获得ADC转换后的结果* @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17* @retval      无*/
uint32_t adc_get_result(uint32_t ch)
{adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */HAL_ADC_Start(&g_adc_handle);                            /* 开启ADC */HAL_ADC_PollForConversion(&g_adc_handle, 10);            /* 轮询转换 */return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}/*** @brief       获取通道ch的转换值,取times次,然后平均* @param       ch      : 通道号, 0~17* @param       times   : 获取次数* @retval      通道ch的times次转换结果平均值*/
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++)     /* 获取times次数据 */{temp_val += adc_get_result(ch);delay_ms(5);}return temp_val / times;        /* 返回平均值 */
}/***************************************单通道ADC采集(DMA读取)实验代码*****************************************/DMA_HandleTypeDef g_dma_adc_handle = {0};                                 /* 定义要搬运ADC数据的DMA句柄 */
ADC_HandleTypeDef g_adc_dma_handle = {0};                                 /* 定义ADC(DMA读取)句柄 */
uint8_t g_adc_dma_sta = 0;                                                /* DMA传输状态标志, 0,未完成; 1, 已完成 *//*** @brief       ADC DMA读取 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置* @param       par         : 外设地址* @param       mar         : 存储器地址* @retval      无*/
void adc_dma_init(uint32_t mar)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ChannelConfTypeDef adc_ch_conf = {0};ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                         /* 开启GPIO时钟 */if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                 /* 大于DMA1_Channel7, 则为DMA2的通道了 */{__HAL_RCC_DMA2_CLK_ENABLE();                                        /* DMA2时钟使能 */}else{__HAL_RCC_DMA1_CLK_ENABLE();                                        /* DMA1时钟使能 */}/* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                  /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                     /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                               /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;                           /* ADC通道对应的IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                               /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);/* 初始化DMA */g_dma_adc_handle.Instance = ADC_ADCX_DMACx;                             /* 设置DMA通道 */g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */g_dma_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */HAL_DMA_Init(&g_dma_adc_handle);__HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);         /* 将DMA与adc联系起来 */g_adc_dma_handle.Instance = ADC_ADCX;                                   /* 选择哪个ADC */g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                  /* 数据对齐方式:右对齐 */g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;                  /* 非扫描模式,仅用到一个通道 */g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;                      /* 使能连续转换模式 */g_adc_dma_handle.Init.NbrOfConversion = 1;                              /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;                  /* 禁止规则通道组间断模式 */g_adc_dma_handle.Init.NbrOfDiscConversion = 0;                          /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;            /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_dma_handle);                                        /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);                         /* 校准ADC *//* 配置ADC通道 */adc_ch_conf.Channel = ADC_ADCX_CHY;                                     /* 通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                  /* 序列 */adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                  /* 采样时间,设置最大采样周期:239.5个ADC周期 */HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);                 /* 通道配置 *//* 配置DMA数据流请求中断优先级 */HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);       /* 启动DMA,并开启中断 */HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar, 0);                          /* 开启ADC,通过DMA传输结果 */
}/*** @brief       使能一次ADC DMA传输*   @note      该函数用寄存器来操作,防止用HAL库操作对其他参数有修改,也为了兼容性* @param       ndtr: DMA传输的次数* @retval      无*/
void adc_dma_enable(uint16_t cndtr)
{ADC_ADCX->CR2 &= ~(1 << 0);                 /* 先关闭ADC */ADC_ADCX_DMACx->CCR &= ~(1 << 0);           /* 关闭DMA传输 */while (ADC_ADCX_DMACx->CCR & (1 << 0));     /* 确保DMA可以被设置 */ADC_ADCX_DMACx->CNDTR = cndtr;              /* DMA传输数据量 */ADC_ADCX_DMACx->CCR |= 1 << 0;              /* 开启DMA传输 */ADC_ADCX->CR2 |= 1 << 0;                    /* 重新启动ADC */ADC_ADCX->CR2 |= 1 << 22;                   /* 启动规则转换通道 */
}/*** @brief       ADC DMA采集中断服务函数* @param       无 * @retval      无*/
void ADC_ADCX_DMACx_IRQHandler(void)
{if (ADC_ADCX_DMACx_IS_TC()){g_adc_dma_sta = 1;                      /* 标记DMA传输完成 */ADC_ADCX_DMACx_CLR_TC();                /* 清除DMA1 数据流7 传输完成中断 */}
}

adc.h

#ifndef __ADC_H
#define __ADC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 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{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 *//* ADC单通道/多通道 DMA采集 DMA及通道 定义* 注意: ADC1的DMA通道只能是: DMA1_Channel1, 因此只要是ADC1, 这里是不能改动的*       ADC2不支持DMA采集*       ADC3的DMA通道只能是: DMA2_Channel5, 因此如果使用 ADC3 则需要修改*/
#define ADC_ADCX_DMACx                      DMA1_Channel1
#define ADC_ADCX_DMACx_IRQn                 DMA1_Channel1_IRQn
#define ADC_ADCX_DMACx_IRQHandler           DMA1_Channel1_IRQHandler#define ADC_ADCX_DMACx_IS_TC()              ( DMA1->ISR & (1 << 1) )    /* 判断 DMA1_Channel1 传输完成标志, 这是一个假函数形式,* 不能当函数使用, 只能用在if等语句里面 */
#define ADC_ADCX_DMACx_CLR_TC()             do{ DMA1->IFCR |= 1 << 1; }while(0) /* 清除 DMA1_Channel1 传输完成标志 *//******************************************************************************************/void adc_init(void);                                           /* ADC初始化函数 */
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);    /* 配置ADC通道 */
uint32_t adc_get_result(uint32_t ch);                          /* 获得某个通道值  */
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);   /* 得到某个通道给定次数采样的平均值 */void adc_dma_init(uint32_t mar);                               /* ADC DMA采集初始化 */
void adc_dma_enable( uint16_t cndtr);                          /* 使能一次ADC DMA采集传输 */#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);}
}

五、多通道ADC采集(DMA读取)实验(熟悉)

在这里插入图片描述
在这里插入图片描述
adc.c

#include "./BSP/ADC/adc.h"
#include "./SYSTEM/delay/delay.h"ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 *//*** @brief       ADC初始化函数*   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3*              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期*              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us* @param       无* @retval      无*/
void adc_init(void)
{g_adc_handle.Instance = ADC_ADCX;                        /* 选择哪个ADC */g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       /* 数据对齐方式:右对齐 */g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       /* 非扫描模式,仅用到一个通道 */g_adc_handle.Init.ContinuousConvMode = DISABLE;          /* 关闭连续转换模式 */g_adc_handle.Init.NbrOfConversion = 1;                   /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_handle.Init.DiscontinuousConvMode = DISABLE;       /* 禁止规则通道组间断模式 */g_adc_handle.Init.NbrOfDiscConversion = 0;               /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_handle);                             /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_handle);              /* 校准ADC */
}/*** @brief       ADC底层驱动,引脚配置,时钟使能此函数会被HAL_ADC_Init()调用* @param       hadc:ADC句柄* @retval      无*/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{if(hadc->Instance == ADC_ADCX){GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ADCX_CHY_CLK_ENABLE();                                /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                           /* 开启GPIO时钟 *//* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                 /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;             /* ADC通道IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                 /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);}
}/*** @brief       设置ADC通道采样时间* @param       adcx : adc句柄指针,ADC_HandleTypeDef* @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17* @param       stime: 采样时间  0~7, 对应关系为:*   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期* @param       rank: 多通道采集时需要设置的采集编号,假设你定义channle1的rank=1,channle2 的rank=2,那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 单通道DMA设置为 ADC_REGULAR_RANK_1*   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16* @retval      无*/
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{ADC_ChannelConfTypeDef adc_ch_conf;adc_ch_conf.Channel = ch;                            /* 通道 */adc_ch_conf.Rank = rank;                             /* 序列 */adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}/*** @brief       获得ADC转换后的结果* @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17* @retval      无*/
uint32_t adc_get_result(uint32_t ch)
{adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */HAL_ADC_Start(&g_adc_handle);                            /* 开启ADC */HAL_ADC_PollForConversion(&g_adc_handle, 10);            /* 轮询转换 */return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}/*** @brief       获取通道ch的转换值,取times次,然后平均* @param       ch      : 通道号, 0~17* @param       times   : 获取次数* @retval      通道ch的times次转换结果平均值*/
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++)     /* 获取times次数据 */{temp_val += adc_get_result(ch);delay_ms(5);}return temp_val / times;        /* 返回平均值 */
}/***************************************单通道ADC采集(DMA读取)实验代码*****************************************/DMA_HandleTypeDef g_dma_adc_handle = {0};                                   /* 定义要搬运ADC数据的DMA句柄 */
ADC_HandleTypeDef g_adc_dma_handle = {0};                                   /* 定义ADC(DMA读取)句柄 */
uint8_t g_adc_dma_sta = 0;                                                  /* DMA传输状态标志, 0,未完成; 1, 已完成 *//*** @brief       ADC DMA读取 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置* @param       par         : 外设地址* @param       mar         : 存储器地址* @retval      无*/
void adc_dma_init(uint32_t mar)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ChannelConfTypeDef adc_ch_conf = {0};ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                         /* 开启GPIO时钟 */if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                 /* 大于DMA1_Channel7, 则为DMA2的通道了 */{__HAL_RCC_DMA2_CLK_ENABLE();                                        /* DMA2时钟使能 */}else{__HAL_RCC_DMA1_CLK_ENABLE();                                        /* DMA1时钟使能 */}/* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                  /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                     /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                               /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;                           /* ADC通道对应的IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                               /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);/* 初始化DMA */g_dma_adc_handle.Instance = ADC_ADCX_DMACx;                             /* 设置DMA通道 */g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */g_dma_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */HAL_DMA_Init(&g_dma_adc_handle);__HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);         /* 将DMA与adc联系起来 */g_adc_dma_handle.Instance = ADC_ADCX;                                   /* 选择哪个ADC */g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                  /* 数据对齐方式:右对齐 */g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;                  /* 非扫描模式,仅用到一个通道 */g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;                      /* 使能连续转换模式 */g_adc_dma_handle.Init.NbrOfConversion = 1;                              /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;                  /* 禁止规则通道组间断模式 */g_adc_dma_handle.Init.NbrOfDiscConversion = 0;                          /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;            /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_dma_handle);                                        /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);                         /* 校准ADC *//* 配置ADC通道 */adc_ch_conf.Channel = ADC_ADCX_CHY;                                     /* 通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                  /* 序列 */adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                  /* 采样时间,设置最大采样周期:239.5个ADC周期 */HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);                 /* 通道配置 *//* 配置DMA数据流请求中断优先级 */HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);       /* 启动DMA,并开启中断 */HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar, 0);                          /* 开启ADC,通过DMA传输结果 */
}/*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************/DMA_HandleTypeDef g_dma_nch_adc_handle = {0};                               /* 定义要搬运ADC多通道数据的DMA句柄 */
ADC_HandleTypeDef g_adc_nch_dma_handle = {0};                               /* 定义ADC(多通道DMA读取)句柄 *//*** @brief       ADC N通道(6通道) DMA读取 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置*              另外,由于本函数用到了6个通道, 宏定义会比较多内容, 因此,本函数就不采用宏定义的方式来修改通道了,*              直接在本函数里面修改, 这里我们默认使用PA0~PA5这6个通道.**              注意: 本函数还是使用 ADC_ADCX(默认=ADC1) 和 ADC_ADCX_DMACx( DMA1_Channel1 ) 及其相关定义*              不要乱修改adc.h里面的这两部分内容, 必须在理解原理的基础上进行修改, 否则可能导致无法正常使用.** @param       mar         : 存储器地址 * @retval      无*/
void adc_nch_dma_init(uint32_t mar)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ChannelConfTypeDef adc_ch_conf = {0};ADC_ADCX_CHY_CLK_ENABLE();                                                /* 使能ADCx时钟 */__HAL_RCC_GPIOA_CLK_ENABLE();                                             /* 开启GPIOA时钟 */if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                   /* 大于DMA1_Channel7, 则为DMA2的通道了 */{__HAL_RCC_DMA2_CLK_ENABLE();                                          /* DMA2时钟使能 */}else{__HAL_RCC_DMA1_CLK_ENABLE();                                          /* DMA1时钟使能 */}/* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                                 /* 设置ADC时钟 *//* 设置ADC1通道0~5对应的IO口模拟输入AD采集引脚模式设置,模拟输入PA0对应 ADC1_IN0PA1对应 ADC1_IN1PA2对应 ADC1_IN2PA3对应 ADC1_IN3PA4对应 ADC1_IN4PA5对应 ADC1_IN5*/gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;  /* GPIOA0~5 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                                 /* 模拟 */HAL_GPIO_Init(GPIOA, &gpio_init_struct);/* 初始化DMA */g_dma_nch_adc_handle.Instance = ADC_ADCX_DMACx;                           /* 设置DMA通道 */g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;               /* 从外设到存储器模式 */g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                   /* 外设非增量模式 */g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                       /* 存储器增量模式 */g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  /* 外设数据长度:16位 */g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     /* 存储器数据长度:16位 */g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL;                              /* 外设流控模式 */g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                 /* 中等优先级 */HAL_DMA_Init(&g_dma_nch_adc_handle);__HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle);   /* 将DMA与adc联系起来 *//* 初始化ADC */g_adc_nch_dma_handle.Instance = ADC_ADCX;                                 /* 选择哪个ADC */g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                /* 数据对齐方式:右对齐 */g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;                 /* 使能扫描模式 */g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;                    /* 使能连续转换 */g_adc_nch_dma_handle.Init.NbrOfConversion = 6;                            /* 赋值范围是1~16,本实验用到6个规则通道序列 */g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;                /* 禁止规则通道组间断模式 */g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0;                        /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;          /* 软件触发 */HAL_ADC_Init(&g_adc_nch_dma_handle);                                      /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle);                       /* 校准ADC *//* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_0;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                    /* 采样序列里的第1个 */adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                    /* 采样时间,设置最大采样周期:239.5个ADC周期 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 通道配置 */adc_ch_conf.Channel = ADC_CHANNEL_1;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_2;                                    /* 采样序列里的第2个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_2;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_3;                                    /* 采样序列里的第3个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_3;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_4;                                    /* 采样序列里的第4个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_4;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_5;                                    /* 采样序列里的第5个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_5;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_6;                                    /* 采样序列里的第6个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 *//* 配置DMA数据流请求中断优先级 */HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0);     /* 启动DMA,并开启中断 */HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar, 0);                        /* 开启ADC,通过DMA传输结果 */
}/*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************//*** @brief       使能一次ADC DMA传输*   @note      该函数用寄存器来操作,防止用HAL库操作对其他参数有修改,也为了兼容性* @param       ndtr: DMA传输的次数* @retval      无*/
void adc_dma_enable(uint16_t cndtr)
{ADC_ADCX->CR2 &= ~(1 << 0);                 /* 先关闭ADC */ADC_ADCX_DMACx->CCR &= ~(1 << 0);           /* 关闭DMA传输 */while (ADC_ADCX_DMACx->CCR & (1 << 0));     /* 确保DMA可以被设置 */ADC_ADCX_DMACx->CNDTR = cndtr;              /* DMA传输数据量 */ADC_ADCX_DMACx->CCR |= 1 << 0;              /* 开启DMA传输 */ADC_ADCX->CR2 |= 1 << 0;                    /* 重新启动ADC */ADC_ADCX->CR2 |= 1 << 22;                   /* 启动规则转换通道 */
}/*** @brief       ADC DMA采集中断服务函数* @param       无 * @retval      无*/
void ADC_ADCX_DMACx_IRQHandler(void)
{if (ADC_ADCX_DMACx_IS_TC()){g_adc_dma_sta = 1;                      /* 标记DMA传输完成 */ADC_ADCX_DMACx_CLR_TC();                /* 清除DMA1 数据流7 传输完成中断 */}
}

adc.h

#ifndef __ADC_H
#define __ADC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 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{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 *//* ADC单通道/多通道 DMA采集 DMA及通道 定义* 注意: ADC1的DMA通道只能是: DMA1_Channel1, 因此只要是ADC1, 这里是不能改动的*       ADC2不支持DMA采集*       ADC3的DMA通道只能是: DMA2_Channel5, 因此如果使用 ADC3 则需要修改*/
#define ADC_ADCX_DMACx                      DMA1_Channel1
#define ADC_ADCX_DMACx_IRQn                 DMA1_Channel1_IRQn
#define ADC_ADCX_DMACx_IRQHandler           DMA1_Channel1_IRQHandler#define ADC_ADCX_DMACx_IS_TC()              ( DMA1->ISR & (1 << 1) )    /* 判断 DMA1_Channel1 传输完成标志, 这是一个假函数形式,* 不能当函数使用, 只能用在if等语句里面 */
#define ADC_ADCX_DMACx_CLR_TC()             do{ DMA1->IFCR |= 1 << 1; }while(0) /* 清除 DMA1_Channel1 传输完成标志 *//******************************************************************************************/void adc_init(void);                                           /* ADC初始化函数 */
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);    /* 配置ADC通道 */
uint32_t adc_get_result(uint32_t ch);                          /* 获得某个通道值  */
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);   /* 得到某个通道给定次数采样的平均值 */void adc_dma_init(uint32_t mar);                               /* ADC DMA采集初始化 */
void adc_dma_enable( uint16_t cndtr);                          /* 使能一次ADC DMA采集传输 */void adc_nch_dma_init(uint32_t mar);                           /* ADC多通道 DMA采集初始化 */#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        50 * 6      /* ADC DMA采集 BUF大小, 应等于ADC通道数的整数倍 */
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,j;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_nch_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 6CH DMA TEST", RED);lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(30, 110, 200, 12, 12, "ADC1_CH0_VAL:", BLUE);lcd_show_string(30, 122, 200, 12, 12, "ADC1_CH0_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 140, 200, 12, 12, "ADC1_CH1_VAL:", BLUE);lcd_show_string(30, 152, 200, 12, 12, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 170, 200, 12, 12, "ADC1_CH2_VAL:", BLUE);lcd_show_string(30, 182, 200, 12, 12, "ADC1_CH2_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 200, 200, 12, 12, "ADC1_CH3_VAL:", BLUE);lcd_show_string(30, 212, 200, 12, 12, "ADC1_CH3_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 230, 200, 12, 12, "ADC1_CH4_VAL:", BLUE);lcd_show_string(30, 242, 200, 12, 12, "ADC1_CH4_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 260, 200, 12, 12, "ADC1_CH5_VAL:", BLUE);lcd_show_string(30, 272, 200, 12, 12, "ADC1_CH5_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */adc_dma_enable(ADC_DMA_BUF_SIZE);   /* 启动ADC DMA采集 */while (1){if (g_adc_dma_sta == 1){/* 循环显示通道0~通道5的结果 */for(j = 0; j < 6; j++)  /* 遍历6个通道 */{sum = 0; /* 清零 */for (i = 0; i < ADC_DMA_BUF_SIZE / 6; i++)  /* 每个通道采集了10次数据,进行10次累加 */{sum += g_adc_dma_buf[(6 * i) + j];      /* 相同通道的转换数据累加 */}adcx = sum / (ADC_DMA_BUF_SIZE / 6);        /* 取平均值 *//* 显示结果 */lcd_show_xnum(108, 110 + (j * 30), adcx, 4, 12, 0, BLUE);   /* 显示ADCC采样后的原始值 */temp = (float)adcx * (3.3 / 4096);  /* 获取计算后的带小数的实际电压值,比如3.1111 */adcx = temp;                        /* 赋值整数部分给adcx变量,因为adcx为u16整形 */lcd_show_xnum(108, 122 + (j * 30), adcx, 1, 12, 0, BLUE);   /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */temp -= adcx;                       /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */temp *= 1000;                       /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */lcd_show_xnum(120, 122 + (j * 30), temp, 3, 12, 0X80, BLUE);/* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */}g_adc_dma_sta = 0;                      /* 清除DMA采集完成状态标志 */adc_dma_enable(ADC_DMA_BUF_SIZE);       /* 启动下一次ADC DMA采集 */}LED0_TOGGLE();delay_ms(100);}
}

六、单通道ADC过采样实验(熟悉)

6.1,如何用过采样和求均值的方式提高ADC的分辨率?(熟悉)

在这里插入图片描述

6.2,实验简要(了解)

在这里插入图片描述

6.3,编程实战:单通道ADC过采样(16位分辨率)实验(掌握)

在这里插入图片描述
adc.c

#include "./BSP/ADC/adc.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */
/*** @brief       ADC初始化函数*   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3*              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期*              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us* @param       无* @retval      无*/
void adc_init(void)
{g_adc_handle.Instance = ADC_ADCX;                        /* 选择哪个ADC */g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       /* 数据对齐方式:右对齐 */g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       /* 非扫描模式,仅用到一个通道 */g_adc_handle.Init.ContinuousConvMode = DISABLE;          /* 关闭连续转换模式 */g_adc_handle.Init.NbrOfConversion = 1;                   /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_handle.Init.DiscontinuousConvMode = DISABLE;       /* 禁止规则通道组间断模式 */g_adc_handle.Init.NbrOfDiscConversion = 0;               /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_handle);                             /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_handle);              /* 校准ADC */
}/*** @brief       ADC底层驱动,引脚配置,时钟使能此函数会被HAL_ADC_Init()调用* @param       hadc:ADC句柄* @retval      无*/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{if(hadc->Instance == ADC_ADCX){GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ADCX_CHY_CLK_ENABLE();                                /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                           /* 开启GPIO时钟 *//* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                 /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;             /* ADC通道IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                 /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);}
}/*** @brief       设置ADC通道采样时间* @param       adcx : adc句柄指针,ADC_HandleTypeDef* @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17* @param       stime: 采样时间  0~7, 对应关系为:*   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期* @param       rank: 多通道采集时需要设置的采集编号,假设你定义channle1的rank=1,channle2 的rank=2,那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 单通道DMA设置为 ADC_REGULAR_RANK_1*   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16* @retval      无*/
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{ADC_ChannelConfTypeDef adc_ch_conf;adc_ch_conf.Channel = ch;                            /* 通道 */adc_ch_conf.Rank = rank;                             /* 序列 */adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}/*** @brief       获得ADC转换后的结果* @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17* @retval      无*/
uint32_t adc_get_result(uint32_t ch)
{adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */HAL_ADC_Start(&g_adc_handle);                            /* 开启ADC */HAL_ADC_PollForConversion(&g_adc_handle, 10);            /* 轮询转换 */return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}/*** @brief       获取通道ch的转换值,取times次,然后平均* @param       ch      : 通道号, 0~17* @param       times   : 获取次数* @retval      通道ch的times次转换结果平均值*/
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++)     /* 获取times次数据 */{temp_val += adc_get_result(ch);delay_ms(5);}return temp_val / times;        /* 返回平均值 */
}/***************************************单通道ADC采集(DMA读取)实验代码*****************************************/DMA_HandleTypeDef g_dma_adc_handle = {0};                                   /* 定义要搬运ADC数据的DMA句柄 */
ADC_HandleTypeDef g_adc_dma_handle = {0};                                   /* 定义ADC(DMA读取)句柄 */
uint8_t g_adc_dma_sta = 0;                                                  /* DMA传输状态标志, 0,未完成; 1, 已完成 *//*** @brief       ADC DMA读取 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置* @param       par         : 外设地址* @param       mar         : 存储器地址* @retval      无*/
void adc_dma_init(uint32_t mar)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ChannelConfTypeDef adc_ch_conf = {0};ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                         /* 开启GPIO时钟 */if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                 /* 大于DMA1_Channel7, 则为DMA2的通道了 */{__HAL_RCC_DMA2_CLK_ENABLE();                                        /* DMA2时钟使能 */}else{__HAL_RCC_DMA1_CLK_ENABLE();                                        /* DMA1时钟使能 */}/* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                  /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                     /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                               /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;                           /* ADC通道对应的IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                               /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);/* 初始化DMA */g_dma_adc_handle.Instance = ADC_ADCX_DMACx;                             /* 设置DMA通道 */g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */g_dma_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */HAL_DMA_Init(&g_dma_adc_handle);__HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);         /* 将DMA与adc联系起来 */g_adc_dma_handle.Instance = ADC_ADCX;                                   /* 选择哪个ADC */g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                  /* 数据对齐方式:右对齐 */g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;                  /* 非扫描模式,仅用到一个通道 */g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;                      /* 使能连续转换模式 */g_adc_dma_handle.Init.NbrOfConversion = 1;                              /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;                  /* 禁止规则通道组间断模式 */g_adc_dma_handle.Init.NbrOfDiscConversion = 0;                          /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;            /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_dma_handle);                                        /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);                         /* 校准ADC *//* 配置ADC通道 */adc_ch_conf.Channel = ADC_ADCX_CHY;                                     /* 通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                  /* 序列 */adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                  /* 采样时间,设置最大采样周期:239.5个ADC周期 */HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);                 /* 通道配置 *//* 配置DMA数据流请求中断优先级 */HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);       /* 启动DMA,并开启中断 */HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar, 0);                          /* 开启ADC,通过DMA传输结果 */
}/*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************/DMA_HandleTypeDef g_dma_nch_adc_handle = {0};                               /* 定义要搬运ADC多通道数据的DMA句柄 */
ADC_HandleTypeDef g_adc_nch_dma_handle = {0};                               /* 定义ADC(多通道DMA读取)句柄 *//*** @brief       ADC N通道(6通道) DMA读取 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置*              另外,由于本函数用到了6个通道, 宏定义会比较多内容, 因此,本函数就不采用宏定义的方式来修改通道了,*              直接在本函数里面修改, 这里我们默认使用PA0~PA5这6个通道.**              注意: 本函数还是使用 ADC_ADCX(默认=ADC1) 和 ADC_ADCX_DMACx( DMA1_Channel1 ) 及其相关定义*              不要乱修改adc.h里面的这两部分内容, 必须在理解原理的基础上进行修改, 否则可能导致无法正常使用.** @param       mar         : 存储器地址 * @retval      无*/
void adc_nch_dma_init(uint32_t mar)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ChannelConfTypeDef adc_ch_conf = {0};ADC_ADCX_CHY_CLK_ENABLE();                                                /* 使能ADCx时钟 */__HAL_RCC_GPIOA_CLK_ENABLE();                                             /* 开启GPIOA时钟 */if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                   /* 大于DMA1_Channel7, 则为DMA2的通道了 */{__HAL_RCC_DMA2_CLK_ENABLE();                                          /* DMA2时钟使能 */}else{__HAL_RCC_DMA1_CLK_ENABLE();                                          /* DMA1时钟使能 */}/* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                                 /* 设置ADC时钟 *//* 设置ADC1通道0~5对应的IO口模拟输入AD采集引脚模式设置,模拟输入PA0对应 ADC1_IN0PA1对应 ADC1_IN1PA2对应 ADC1_IN2PA3对应 ADC1_IN3PA4对应 ADC1_IN4PA5对应 ADC1_IN5*/gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;  /* GPIOA0~5 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                                 /* 模拟 */HAL_GPIO_Init(GPIOA, &gpio_init_struct);/* 初始化DMA */g_dma_nch_adc_handle.Instance = ADC_ADCX_DMACx;                           /* 设置DMA通道 */g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;               /* 从外设到存储器模式 */g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                   /* 外设非增量模式 */g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                       /* 存储器增量模式 */g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  /* 外设数据长度:16位 */g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     /* 存储器数据长度:16位 */g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL;                              /* 外设流控模式 */g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                 /* 中等优先级 */HAL_DMA_Init(&g_dma_nch_adc_handle);__HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle);   /* 将DMA与adc联系起来 *//* 初始化ADC */g_adc_nch_dma_handle.Instance = ADC_ADCX;                                 /* 选择哪个ADC */g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                /* 数据对齐方式:右对齐 */g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;                 /* 使能扫描模式 */g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;                    /* 使能连续转换 */g_adc_nch_dma_handle.Init.NbrOfConversion = 6;                            /* 赋值范围是1~16,本实验用到6个规则通道序列 */g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;                /* 禁止规则通道组间断模式 */g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0;                        /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;          /* 软件触发 */HAL_ADC_Init(&g_adc_nch_dma_handle);                                      /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle);                       /* 校准ADC *//* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_0;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                    /* 采样序列里的第1个 */adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                    /* 采样时间,设置最大采样周期:239.5个ADC周期 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 通道配置 */adc_ch_conf.Channel = ADC_CHANNEL_1;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_2;                                    /* 采样序列里的第2个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_2;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_3;                                    /* 采样序列里的第3个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_3;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_4;                                    /* 采样序列里的第4个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_4;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_5;                                    /* 采样序列里的第5个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_5;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_6;                                    /* 采样序列里的第6个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 *//* 配置DMA数据流请求中断优先级 */HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0);     /* 启动DMA,并开启中断 */HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar, 0);                        /* 开启ADC,通过DMA传输结果 */
}/*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************//*** @brief       使能一次ADC DMA传输*   @note      该函数用寄存器来操作,防止用HAL库操作对其他参数有修改,也为了兼容性* @param       ndtr: DMA传输的次数* @retval      无*/
void adc_dma_enable(uint16_t cndtr)
{ADC_ADCX->CR2 &= ~(1 << 0);                 /* 先关闭ADC */ADC_ADCX_DMACx->CCR &= ~(1 << 0);           /* 关闭DMA传输 */while (ADC_ADCX_DMACx->CCR & (1 << 0));     /* 确保DMA可以被设置 */ADC_ADCX_DMACx->CNDTR = cndtr;              /* DMA传输数据量 */ADC_ADCX_DMACx->CCR |= 1 << 0;              /* 开启DMA传输 */ADC_ADCX->CR2 |= 1 << 0;                    /* 重新启动ADC */ADC_ADCX->CR2 |= 1 << 22;                   /* 启动规则转换通道 */
}/*** @brief       ADC DMA采集中断服务函数* @param       无 * @retval      无*/
void ADC_ADCX_DMACx_IRQHandler(void)
{if (ADC_ADCX_DMACx_IS_TC()){g_adc_dma_sta = 1;                      /* 标记DMA传输完成 */ADC_ADCX_DMACx_CLR_TC();                /* 清除DMA1 数据流7 传输完成中断 */}
}

adc.h

#ifndef __ADC_H
#define __ADC_H#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 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{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 *//* ADC单通道/多通道 DMA采集 DMA及通道 定义* 注意: ADC1的DMA通道只能是: DMA1_Channel1, 因此只要是ADC1, 这里是不能改动的*       ADC2不支持DMA采集*       ADC3的DMA通道只能是: DMA2_Channel5, 因此如果使用 ADC3 则需要修改*/
#define ADC_ADCX_DMACx                      DMA1_Channel1
#define ADC_ADCX_DMACx_IRQn                 DMA1_Channel1_IRQn
#define ADC_ADCX_DMACx_IRQHandler           DMA1_Channel1_IRQHandler#define ADC_ADCX_DMACx_IS_TC()              ( DMA1->ISR & (1 << 1) )    /* 判断 DMA1_Channel1 传输完成标志, 这是一个假函数形式,* 不能当函数使用, 只能用在if等语句里面 */
#define ADC_ADCX_DMACx_CLR_TC()             do{ DMA1->IFCR |= 1 << 1; }while(0) /* 清除 DMA1_Channel1 传输完成标志 */
/******************************************************************************************/
void adc_init(void);                                           /* ADC初始化函数 */
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);    /* 配置ADC通道 */
uint32_t adc_get_result(uint32_t ch);                          /* 获得某个通道值  */
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);   /* 得到某个通道给定次数采样的平均值 */void adc_dma_init(uint32_t mar);                               /* ADC DMA采集初始化 */
void adc_dma_enable( uint16_t cndtr);                          /* 使能一次ADC DMA采集传输 */void adc_nch_dma_init(uint32_t mar);                           /* ADC多通道 DMA采集初始化 */
#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"/* ADC过采样技术, 是利用ADC多次采集的方式, 来提高ADC精度, 采样速度每提高4倍* 采样精度提高 1bit, 同时, ADC采样速度降低4倍, 如提高4bit精度, 需要256次采集* 才能得出1次数据, 相当于ADC速度慢了256倍. 理论上只要ADC足够快, 我们可以无限* 提高ADC精度, 但实际上ADC并不是无限快的, 而且由于ADC性能限制, 并不是位数无限* 提高结果就越好, 需要根据自己的实际需求和ADC的实际性能来权衡.*/
#define ADC_OVERSAMPLE_TIMES    256                         /* ADC过采样次数, 这里提高4bit分辨率, 需要256倍采样 */
#define ADC_DMA_BUF_SIZE        ADC_OVERSAMPLE_TIMES * 10   /* 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, 已完成 */
extern ADC_HandleTypeDef g_adc_dma_handle;                  /* ADC(DMA读取)句柄 */int main(void)
{uint16_t i;uint32_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采集 */adc_channel_set(&g_adc_dma_handle, ADC_ADCX_CHY, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_1CYCLE_5); /* 设置ADCX对应通道采样时间为1.5个时钟周期, 已达到最高的采集速度 */lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);lcd_show_string(30,  70, 200, 16, 16, "ADC OverSample 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 / ADC_OVERSAMPLE_TIMES); /* 取平均值 */adcx >>= 4;   /* 除以2^4倍, 得到12+4位 ADC精度值, 注意: 提高 N bit精度, 需要 >> N *//* 显示结果 */lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE);      /* 显示ADCC采样后的原始值 */temp = (float)adcx * (3.3 / 65536);                 /* 获取计算后的带小数的实际电压值,比如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);}
}

七、内部温度传感器实验(熟悉)

7.1,STM32内部温度传感器简介(了解)

在这里插入图片描述

7.2,温度计算方法(熟悉)

在这里插入图片描述

7.3,实验简要(了解)

在这里插入图片描述

7.4,编程实战:内部温度传感器实验(掌握)

adc.c

#include "./BSP/ADC/adc.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */
/*** @brief       ADC初始化函数*   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3*              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期*              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us* @param       无* @retval      无*/
void adc_init(void)
{g_adc_handle.Instance = ADC_ADCX;                        /* 选择哪个ADC */g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       /* 数据对齐方式:右对齐 */g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       /* 非扫描模式,仅用到一个通道 */g_adc_handle.Init.ContinuousConvMode = DISABLE;          /* 关闭连续转换模式 */g_adc_handle.Init.NbrOfConversion = 1;                   /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_handle.Init.DiscontinuousConvMode = DISABLE;       /* 禁止规则通道组间断模式 */g_adc_handle.Init.NbrOfDiscConversion = 0;               /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_handle);                             /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_handle);              /* 校准ADC */
}/*** @brief       ADC底层驱动,引脚配置,时钟使能此函数会被HAL_ADC_Init()调用* @param       hadc:ADC句柄* @retval      无*/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{if(hadc->Instance == ADC_ADCX){GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ADCX_CHY_CLK_ENABLE();                                /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                           /* 开启GPIO时钟 *//* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                 /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;             /* ADC通道IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                 /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);}
}/*** @brief       设置ADC通道采样时间* @param       adcx : adc句柄指针,ADC_HandleTypeDef* @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17* @param       stime: 采样时间  0~7, 对应关系为:*   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期* @param       rank: 多通道采集时需要设置的采集编号,假设你定义channle1的rank=1,channle2 的rank=2,那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 单通道DMA设置为 ADC_REGULAR_RANK_1*   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16* @retval      无*/
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{ADC_ChannelConfTypeDef adc_ch_conf;adc_ch_conf.Channel = ch;                            /* 通道 */adc_ch_conf.Rank = rank;                             /* 序列 */adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}/*** @brief       获得ADC转换后的结果* @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17* @retval      无*/
uint32_t adc_get_result(uint32_t ch)
{adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */HAL_ADC_Start(&g_adc_handle);                            /* 开启ADC */HAL_ADC_PollForConversion(&g_adc_handle, 10);            /* 轮询转换 */return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}/*** @brief       获取通道ch的转换值,取times次,然后平均* @param       ch      : 通道号, 0~17* @param       times   : 获取次数* @retval      通道ch的times次转换结果平均值*/
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++)     /* 获取times次数据 */{temp_val += adc_get_result(ch);delay_ms(5);}return temp_val / times;        /* 返回平均值 */
}/***************************************单通道ADC采集(DMA读取)实验代码*****************************************/DMA_HandleTypeDef g_dma_adc_handle = {0};                                   /* 定义要搬运ADC数据的DMA句柄 */
ADC_HandleTypeDef g_adc_dma_handle = {0};                                   /* 定义ADC(DMA读取)句柄 */
uint8_t g_adc_dma_sta = 0;                                                  /* DMA传输状态标志, 0,未完成; 1, 已完成 *//*** @brief       ADC DMA读取 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置* @param       par         : 外设地址* @param       mar         : 存储器地址* @retval      无*/
void adc_dma_init(uint32_t mar)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ChannelConfTypeDef adc_ch_conf = {0};ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                         /* 开启GPIO时钟 */if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                 /* 大于DMA1_Channel7, 则为DMA2的通道了 */{__HAL_RCC_DMA2_CLK_ENABLE();                                        /* DMA2时钟使能 */}else{__HAL_RCC_DMA1_CLK_ENABLE();                                        /* DMA1时钟使能 */}/* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                  /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                     /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                               /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;                           /* ADC通道对应的IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                               /* 模拟 */HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);/* 初始化DMA */g_dma_adc_handle.Instance = ADC_ADCX_DMACx;                             /* 设置DMA通道 */g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */g_dma_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */HAL_DMA_Init(&g_dma_adc_handle);__HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);         /* 将DMA与adc联系起来 */g_adc_dma_handle.Instance = ADC_ADCX;                                   /* 选择哪个ADC */g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                  /* 数据对齐方式:右对齐 */g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;                  /* 非扫描模式,仅用到一个通道 */g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;                      /* 使能连续转换模式 */g_adc_dma_handle.Init.NbrOfConversion = 1;                              /* 赋值范围是1~16,本实验用到1个规则通道序列 */g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;                  /* 禁止规则通道组间断模式 */g_adc_dma_handle.Init.NbrOfDiscConversion = 0;                          /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;            /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc_dma_handle);                                        /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_dma_handle);                         /* 校准ADC *//* 配置ADC通道 */adc_ch_conf.Channel = ADC_ADCX_CHY;                                     /* 通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                  /* 序列 */adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                  /* 采样时间,设置最大采样周期:239.5个ADC周期 */HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);                 /* 通道配置 *//* 配置DMA数据流请求中断优先级 */HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);       /* 启动DMA,并开启中断 */HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar, 0);                          /* 开启ADC,通过DMA传输结果 */
}/*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************/DMA_HandleTypeDef g_dma_nch_adc_handle = {0};                               /* 定义要搬运ADC多通道数据的DMA句柄 */
ADC_HandleTypeDef g_adc_nch_dma_handle = {0};                               /* 定义ADC(多通道DMA读取)句柄 *//*** @brief       ADC N通道(6通道) DMA读取 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置*              另外,由于本函数用到了6个通道, 宏定义会比较多内容, 因此,本函数就不采用宏定义的方式来修改通道了,*              直接在本函数里面修改, 这里我们默认使用PA0~PA5这6个通道.**              注意: 本函数还是使用 ADC_ADCX(默认=ADC1) 和 ADC_ADCX_DMACx( DMA1_Channel1 ) 及其相关定义*              不要乱修改adc.h里面的这两部分内容, 必须在理解原理的基础上进行修改, 否则可能导致无法正常使用.** @param       mar         : 存储器地址 * @retval      无*/
void adc_nch_dma_init(uint32_t mar)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC_ChannelConfTypeDef adc_ch_conf = {0};ADC_ADCX_CHY_CLK_ENABLE();                                                /* 使能ADCx时钟 */__HAL_RCC_GPIOA_CLK_ENABLE();                                             /* 开启GPIOA时钟 */if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)                   /* 大于DMA1_Channel7, 则为DMA2的通道了 */{__HAL_RCC_DMA2_CLK_ENABLE();                                          /* DMA2时钟使能 */}else{__HAL_RCC_DMA1_CLK_ENABLE();                                          /* DMA1时钟使能 */}/* 设置ADC时钟 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;                    /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;                       /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                                 /* 设置ADC时钟 *//* 设置ADC1通道0~5对应的IO口模拟输入AD采集引脚模式设置,模拟输入PA0对应 ADC1_IN0PA1对应 ADC1_IN1PA2对应 ADC1_IN2PA3对应 ADC1_IN3PA4对应 ADC1_IN4PA5对应 ADC1_IN5*/gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;  /* GPIOA0~5 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                                 /* 模拟 */HAL_GPIO_Init(GPIOA, &gpio_init_struct);/* 初始化DMA */g_dma_nch_adc_handle.Instance = ADC_ADCX_DMACx;                           /* 设置DMA通道 */g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;               /* 从外设到存储器模式 */g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                   /* 外设非增量模式 */g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                       /* 存储器增量模式 */g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  /* 外设数据长度:16位 */g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     /* 存储器数据长度:16位 */g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL;                              /* 外设流控模式 */g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                 /* 中等优先级 */HAL_DMA_Init(&g_dma_nch_adc_handle);__HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle);   /* 将DMA与adc联系起来 *//* 初始化ADC */g_adc_nch_dma_handle.Instance = ADC_ADCX;                                 /* 选择哪个ADC */g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                /* 数据对齐方式:右对齐 */g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;                 /* 使能扫描模式 */g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;                    /* 使能连续转换 */g_adc_nch_dma_handle.Init.NbrOfConversion = 6;                            /* 赋值范围是1~16,本实验用到6个规则通道序列 */g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;                /* 禁止规则通道组间断模式 */g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0;                        /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;          /* 软件触发 */HAL_ADC_Init(&g_adc_nch_dma_handle);                                      /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle);                       /* 校准ADC *//* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_0;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_1;                                    /* 采样序列里的第1个 */adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;                    /* 采样时间,设置最大采样周期:239.5个ADC周期 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 通道配置 */adc_ch_conf.Channel = ADC_CHANNEL_1;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_2;                                    /* 采样序列里的第2个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_2;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_3;                                    /* 采样序列里的第3个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_3;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_4;                                    /* 采样序列里的第4个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_4;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_5;                                    /* 采样序列里的第5个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 */adc_ch_conf.Channel = ADC_CHANNEL_5;                                      /* 配置使用的ADC通道 */adc_ch_conf.Rank = ADC_REGULAR_RANK_6;                                    /* 采样序列里的第6个 */HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf);               /* 配置ADC通道 *//* 配置DMA数据流请求中断优先级 */HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3);HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn);HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0);     /* 启动DMA,并开启中断 */HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar, 0);                        /* 开启ADC,通过DMA传输结果 */
}/*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************//*** @brief       使能一次ADC DMA传输*   @note      该函数用寄存器来操作,防止用HAL库操作对其他参数有修改,也为了兼容性* @param       ndtr: DMA传输的次数* @retval      无*/
void adc_dma_enable(uint16_t cndtr)
{ADC_ADCX->CR2 &= ~(1 << 0);                 /* 先关闭ADC */ADC_ADCX_DMACx->CCR &= ~(1 << 0);           /* 关闭DMA传输 */while (ADC_ADCX_DMACx->CCR & (1 << 0));     /* 确保DMA可以被设置 */ADC_ADCX_DMACx->CNDTR = cndtr;              /* DMA传输数据量 */ADC_ADCX_DMACx->CCR |= 1 << 0;              /* 开启DMA传输 */ADC_ADCX->CR2 |= 1 << 0;                    /* 重新启动ADC */ADC_ADCX->CR2 |= 1 << 22;                   /* 启动规则转换通道 */
}/*** @brief       ADC DMA采集中断服务函数* @param       无 * @retval      无*/
void ADC_ADCX_DMACx_IRQHandler(void)
{if (ADC_ADCX_DMACx_IS_TC()){g_adc_dma_sta = 1;                      /* 标记DMA传输完成 */ADC_ADCX_DMACx_CLR_TC();                /* 清除DMA1 数据流7 传输完成中断 */}
}/*********************************************内部温度传感器实验代码***************************************************//*** @brief       ADC 内部温度传感器 初始化函数*   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置*              注意: STM32F103内部温度传感器只连接在ADC1的通道16上, 其他ADC无法进行转换.** @param       无* @retval      无*/
void adc_temperature_init(void)
{adc_init();                                            /* 先初始化ADC */SET_BIT(g_adc_handle.Instance->CR2, ADC_CR2_TSVREFE);  /* TSVREFE = 1, 启用内部温度传感器和Vrefint */
}/*** @brief       获取内部温度传感器温度值* @param       无* @retval      温度值(扩大了100倍,单位:℃.)*/
short adc_get_temperature(void)
{uint32_t adcx;short result;double temperature;adcx = adc_get_result_average(ADC_TEMPSENSOR_CHX, 20);  /* 读取内部温度传感器通道,10次取平均 */temperature = (float)adcx * (3.3 / 4096);               /* 转化为电压值 */temperature = (1.43 - temperature) / 0.0043 + 25;       /* 计算温度 */result = temperature *= 100;                            /* 扩大100倍. */return result;
}

adc.h

#ifndef __ADC_H
#define __ADC_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 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{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 *//* ADC单通道/多通道 DMA采集 DMA及通道 定义* 注意: ADC1的DMA通道只能是: DMA1_Channel1, 因此只要是ADC1, 这里是不能改动的*       ADC2不支持DMA采集*       ADC3的DMA通道只能是: DMA2_Channel5, 因此如果使用 ADC3 则需要修改*/
#define ADC_ADCX_DMACx                      DMA1_Channel1
#define ADC_ADCX_DMACx_IRQn                 DMA1_Channel1_IRQn
#define ADC_ADCX_DMACx_IRQHandler           DMA1_Channel1_IRQHandler#define ADC_ADCX_DMACx_IS_TC()              ( DMA1->ISR & (1 << 1) )    /* 判断 DMA1_Channel1 传输完成标志, 这是一个假函数形式,* 不能当函数使用, 只能用在if等语句里面 */
#define ADC_ADCX_DMACx_CLR_TC()             do{ DMA1->IFCR |= 1 << 1; }while(0) /* 清除 DMA1_Channel1 传输完成标志 *//* ADC 温度传感器通道 定义 */ #define ADC_TEMPSENSOR_CHX                  ADC_CHANNEL_16/******************************************************************************************/void adc_init(void);                                           /* ADC初始化函数 */
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);    /* 配置ADC通道 */
uint32_t adc_get_result(uint32_t ch);                          /* 获得某个通道值  */
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);   /* 得到某个通道给定次数采样的平均值 */void adc_dma_init(uint32_t mar);                               /* ADC DMA采集初始化 */
void adc_dma_enable( uint16_t cndtr);                          /* 使能一次ADC DMA采集传输 */void adc_nch_dma_init(uint32_t mar);                           /* ADC多通道 DMA采集初始化 */void adc_temperature_init(void);                               /* ADC温度采集初始化函数 */
short adc_get_temperature(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"int main(void)
{short 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_temperature_init();                     /* 初始化ADC内部温度传感器采集 */lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);lcd_show_string(30,  70, 200, 16, 16, "Temperature TEST", RED);lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(30, 120, 200, 16, 16, "TEMPERATE: 00.00C", BLUE);while (1){temp = adc_get_temperature();   /* 得到温度值 */if (temp < 0){temp = -temp;lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, "-", BLUE);   /* 显示负号 */}else{lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, " ", BLUE);   /* 无符号 */}lcd_show_xnum(30 + 11 * 8, 120, temp / 100, 2, 16, 0, BLUE);    /* 显示整数部分 */lcd_show_xnum(30 + 14 * 8, 120, temp % 100, 2, 16, 0X80, BLUE); /* 显示小数部分 */LED0_TOGGLE();  /* LED0闪烁,提示程序运行 */delay_ms(250);}
}

八、光敏传感器实验(熟悉)

8.1,光敏二极管简介(了解)

在这里插入图片描述

8.2,实验原理(熟悉)

在这里插入图片描述

8.3,实验简要(了解)

在这里插入图片描述

8.4,编程实战:光敏传感器实验(掌握)

adc3.c

#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc3_handle;         /* ADC句柄 *//*** @brief       ADC3初始化函数*   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3*              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期*              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us* @param       无* @retval      无*/
void adc3_init(void)
{GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init = {0};ADC3_CHY_GPIO_CLK_ENABLE();                                /* IO口时钟使能 */ADC3_CHY_CLK_ENABLE();                                     /* ADC时钟使能 */adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;     /* ADC外设时钟 */adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;        /* 分频因子6时钟为72M/6=12MHz */HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);                  /* 设置ADC时钟 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = ADC3_CHY_GPIO_PIN;                  /* ADC通道对应的IO引脚 */gpio_init_struct.Mode = GPIO_MODE_ANALOG;                  /* 模拟 */HAL_GPIO_Init(ADC3_CHY_GPIO_PORT, &gpio_init_struct);g_adc3_handle.Instance = ADC_ADCX;                         /* 选择哪个ADC */g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;        /* 数据对齐方式:右对齐 */g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;        /* 非扫描模式,仅用到一个通道 */g_adc3_handle.Init.ContinuousConvMode = DISABLE;           /* 关闭连续转换模式 */g_adc3_handle.Init.NbrOfConversion = 1;                    /* 1个转换在规则序列中 也就是只转换规则序列1 */g_adc3_handle.Init.DiscontinuousConvMode = DISABLE;        /* 禁止规则通道组间断模式 */g_adc3_handle.Init.NbrOfDiscConversion = 0;                /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;  /* 触发转换方式:软件触发 */HAL_ADC_Init(&g_adc3_handle);                              /* 初始化 */HAL_ADCEx_Calibration_Start(&g_adc3_handle);               /* 校准ADC */
}/*** @brief       设置ADC通道采样时间* @param       adcx : adc句柄指针,ADC_HandleTypeDef* @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17* @param       stime: 采样时间  0~7, 对应关系为:*   @arg       ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期        ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期     ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期     ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期*   @arg       ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期     ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期* @param       rank: 多通道采集时需要设置的采集编号,假设你定义channle1的rank=1,channle2 的rank=2,那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 单通道DMA设置为 ADC_REGULAR_RANK_1*   @arg       编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16* @retval      无*/
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{ADC_ChannelConfTypeDef adc_ch_conf;adc_ch_conf.Channel = ch;                            /* 通道 */adc_ch_conf.Rank = rank;                             /* 序列 */adc_ch_conf.SamplingTime = stime;                    /* 采样时间 */HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf);     /* 通道配置 */
}/*** @brief       获得ADC转换后的结果* @param       ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17* @retval      无*/
uint32_t adc3_get_result(uint32_t ch)
{adc3_channel_set(&g_adc3_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);    /* 设置通道,序列和采样时间 */HAL_ADC_Start(&g_adc3_handle);                            /* 开启ADC */HAL_ADC_PollForConversion(&g_adc3_handle, 10);            /* 轮询转换 */return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle);        /* 返回最近一次ADC1规则组的转换结果 */
}/*** @brief       获取通道ch的转换值,取times次,然后平均* @param       ch      : 通道号, 0~17* @param       times   : 获取次数* @retval      通道ch的times次转换结果平均值*/
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{uint32_t temp_val = 0;uint8_t t;for (t = 0; t < times; t++)     /* 获取times次数据 */{temp_val += adc3_get_result(ch);delay_ms(5);}return temp_val / times;        /* 返回平均值 */
}

adc.h

#ifndef __ADC_H
#define __ADC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* ADC及引脚 定义 */
#define ADC3_CHY_GPIO_PORT                  GPIOA
#define ADC3_CHY_GPIO_PIN                   GPIO_PIN_1 
#define ADC3_CHY_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)  /* PA口时钟使能 */#define ADC_ADCX                            ADC3 
#define ADC3_CHY                            ADC_CHANNEL_1                                /* 通道Y,  0 <= Y <= 17 */ 
#define ADC3_CHY_CLK_ENABLE()               do{ __HAL_RCC_ADC3_CLK_ENABLE(); }while(0)   /* ADC1 时钟使能 */
/******************************************************************************************/void adc3_init(void);                                          /* ADC3初始化 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);   /* ADC3通道设置 */
uint32_t adc3_get_result(uint32_t ch);                         /* 获得某个通道值  */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times);  /* 得到某个通道给定次数采样的平均值 */#endif 

lsens.c

#include "./BSP/ADC/adc3.h"
#include "./BSP/LSENS/lsens.h"
/*** @brief       初始化光敏传感器* @param       无* @retval      无*/
void lsens_init(void)
{GPIO_InitTypeDef gpio_init_struct;LSENS_ADC3_CHX_GPIO_CLK_ENABLE();   /* IO口时钟使能 *//* 设置AD采集通道对应IO引脚工作模式 */gpio_init_struct.Pin = LSENS_ADC3_CHX_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_ANALOG;HAL_GPIO_Init(LSENS_ADC3_CHX_GPIO_PORT, &gpio_init_struct);adc3_init();                       /* 初始化ADC */
}/*** @brief       读取光敏传感器值* @param       无* @retval      0~100:0,最暗;100,最亮*/
uint8_t lsens_get_val(void)
{uint32_t temp_val = 0;temp_val = adc3_get_result_average(LSENS_ADC3_CHX, 10);  /* 读取平均值 */temp_val /= 40;if (temp_val > 100)temp_val = 100;return (uint8_t)(100 - temp_val);
}

lsens.h

#ifndef __LSENS_H
#define __LSENS_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 光敏传感器对应ADC3的输入引脚和通道 定义 */#define LSENS_ADC3_CHX_GPIO_PORT            GPIOF
#define LSENS_ADC3_CHX_GPIO_PIN             GPIO_PIN_8
#define LSENS_ADC3_CHX_GPIO_CLK_ENABLE()    do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)   /* PF口时钟使能 */#define LSENS_ADC3_CHX                      ADC_CHANNEL_6       /* 通道Y,  0 <= Y <= 17 */ /******************************************************************************************/void lsens_init(void);          /* 初始化光敏传感器 */
uint8_t lsens_get_val(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/LSENS/lsens.h"int main(void)
{short adcx;HAL_Init();                                 /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */delay_init(72);                             /* 延时初始化 */usart_init(115200);                         /* 串口初始化为115200 */led_init();                                 /* 初始化LED */lcd_init();                                 /* 初始化LCD */lsens_init();                               /* 初始化光敏传感器 */lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);lcd_show_string(30,  70, 200, 16, 16, "LSENS TEST", RED);lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(30, 110, 200, 16, 16, "LSENS_VAL:", BLUE);while (1){ adcx = lsens_get_val();lcd_show_xnum(30 + 10 * 8, 110, adcx, 3, 16, 0, BLUE); /* 显示ADC的值 */LED0_TOGGLE();                                         /* LED0闪烁,提示程序运行 */delay_ms(250);}
}

相关文章:

  • PDF Expert for Mac v3.9.2中文激活版下载
  • v-rep--websocket接口
  • 中介者模式(Mediator Pattern)
  • Adobe Acrobat DC中如何合并pdf并生成目录
  • 云时代【4】—— 资源隔离与控制技术
  • 原码,反码,补码
  • 嵌入式烧录报错:板端IP与PC的IP相同
  • STM32控制max30102读取血氧心率数据(keil5工程)
  • js实现文件下载,并传给后端
  • Linux 软链接与硬链接
  • 【MySQL】_自连接与子查询
  • 小项目——学校宿舍楼系统
  • Nest.js权限管理系统开发(二)连接MySQL、Redis
  • Uniapp在IOS系统打包测试流程
  • leetcode 重复的子字符串
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • ES6系统学习----从Apollo Client看解构赋值
  • exif信息对照
  • Java方法详解
  • js正则,这点儿就够用了
  • Promise初体验
  • ReactNative开发常用的三方模块
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • 飞驰在Mesos的涡轮引擎上
  • 基于组件的设计工作流与界面抽象
  • 技术发展面试
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 算法之不定期更新(一)(2018-04-12)
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 智能合约Solidity教程-事件和日志(一)
  • ​ssh免密码登录设置及问题总结
  • ​力扣解法汇总946-验证栈序列
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • $forceUpdate()函数
  • (1)虚拟机的安装与使用,linux系统安装
  • (SpringBoot)第七章:SpringBoot日志文件
  • (多级缓存)缓存同步
  • (二十四)Flask之flask-session组件
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (免费领源码)python#django#mysql公交线路查询系统85021- 计算机毕业设计项目选题推荐
  • (三) diretfbrc详解
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)Windows2003安全设置/维护
  • .java 9 找不到符号_java找不到符号
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .NET Core引入性能分析引导优化
  • .net6 webapi log4net完整配置使用流程
  • .NetCore项目nginx发布
  • .Net组件程序设计之线程、并发管理(一)