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);}
}

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

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

相关文章

LeetCode 0106.从中序与后序遍历序列构造二叉树:分治(递归)——五彩斑斓的题解(若不是彩色的可以点击原文链接查看)

【LetMeFly】106.从中序与后序遍历序列构造二叉树&#xff1a;分治&#xff08;递归&#xff09;——五彩斑斓的题解&#xff08;若不是彩色的可以点击原文链接查看&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/construct-binary-tree-from-inorder-an…

HarmonyOS Stage模型基本概念讲解

本文 我们来说harmonyos中的一种应用模型 Stage模型 官方提供了两种模型 一种是早期的 FA模型 另一种就是就是 harmonyos 3.1才开始的新增的一种模型 Stage模型 目前来讲 Stage 会成为现在乃至将来 长期推进的一种模型 也就是 无论是 现在的harmonyos 4.0 乃至 之后要发布的 …

五、分类算法 总结

代码&#xff1a; from sklearn.datasets import load_iris, fetch_20newsgroups from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.naive_bayes import MultinomialNB from s…

unity-firebase-Analytics分析库对接后数据不显示原因,及最终解决方法

自己记录一下unity对接了 FirebaseAnalytics.unitypackage&#xff08;基于 firebase_unity_sdk_10.3.0 版本&#xff09; 库后&#xff0c;数据不显示的原因及最终显示解决方法&#xff1a; 1. 代码问题&#xff08;有可能是代码写的问题&#xff0c;正确的代码如下&#xff…

React 事件处理 ( this问题 参数传递 ref)

React事件的命名采用小驼峰方式&#xff08;cameCase&#xff09;,而不是小写 使用JSX语法时你需要传入一个函数作为事件处理函数&#xff0c;而不是一个字符串 你不能通过返回false 的方式阻止默认行为。你必须显示式的使用preventDefault 1 this 需要谨慎对待JSX回调函数中的…

Flink双流(join)

一、介绍 Join大体分类只有两种&#xff1a;Window Join和Interval Join Window Join有可以根据Window的类型细分出3种&#xff1a;Tumbling(滚动) Window Join、Sliding(滑动) Window Join、Session(会话) Widnow Join。 &#x1f338;Window 类型的join都是利用window的机制…

ETL快速拉取物流信息

我国作为世界第一的物流大国&#xff0c;但是在目前的物流信息系统还存在着几大的痛点。主要包括以下几个方面&#xff1a; 数据孤岛&#xff1a;有些物流企业各个部门之间的数据标准不一致&#xff0c;难以实现数据共享和协同&#xff0c;容易导致信息孤岛。 操作繁琐&#x…

如何在 CentOS 上安装 ONLYOFFICE 文档 8.0

使用社区版&#xff0c;您可以在本地服务器上安装 ONLYOFFICE 文档&#xff0c;并将在线编辑器与 ONLYOFFICE 协作平台或其他热门系统集成在一起。 ONLYOFFICE 文档是什么 ONLYOFFICE 文档是一个功能强大的文档编辑器&#xff0c;支持处理文本文档、电子表格、演示文稿、可填写…

django自定义后端过滤

​ DRF自带的过滤 第一个 DjangoFilterBackend 是需要安装三方库见[搜索&#xff1a;多字段筛选]两外两个是安装注册了rest_framework就有。 如上图&#xff0c;只要配置了三个箭头所指的方向&#xff0c;就能使用。 第一个单字段过滤 用户视图集中加上filterset_fields …

Edting While Playing 瓦片地图编辑器开发整合导入自定义贴图 DEVC++ VS2022都可复制粘贴运行

接 多种类型图片模块读取-CSDN博客 与 Editing While Playing 使用 Easyx 开发的 RPG 地图编辑器 tilemap eaitor-CSDN博客 整合实现平面贴图纹理自定义 操作同上 导入步骤&#xff1a; 先运行程序&#xff0c;然后关闭&#xff0c;同目录下有四个文件夹&#xff0c; 把…

高维数据的中介效应【中介分析】《R包:HIMA》

允许基于高级中介筛选和惩罚回归技术来估计和测试高维中介效应 Hima包浏览 高维中介示意图 图1. 在暴露和结果之间有高维中介的情况 本包的作用 在确定独立筛选和极小极大凹惩罚技术的基础上&#xff0c;采用联合显著性检验方法对调解效果进行检验。使用蒙特卡罗模拟研究来展…

LabVIEW多通道压力传感器实时动态检测

LabVIEW多通道压力传感器实时动态检测 介绍了一种基于LabVIEW的多通道压力传感器实时动态检测系统&#xff0c;解决压阻式压力传感器温度补偿过程的复杂度&#xff0c;提高测量的准确性。通过自动轮询检测方法&#xff0c;结合硬件检测模型和多通道检测系统设计&#xff0c;本…

Netty-核心组件

核心组件 1.Bootstrap和ServerBootstrap2.Future和ChannelFuture3.Channel4.Selector5.NioEventLoop6.NioEventLoopGroup7.ByteBuf8.ChannelHandler9.ChannelHandlerContext10.ChannelPipeline 1.Bootstrap和ServerBootstrap Bootstrap是Netty的启动程序&#xff0c;⼀个Netty…

Redis之缓存穿透问题解决方案实践SpringBoot3+Docker

文章目录 一、介绍二、方案介绍三、Redis Docker部署四、SpringBoot3 Base代码1. 依赖配置2. 基本代码 五、缓存优化代码1. 校验机制2. 布隆过滤器3. 逻辑优化 一、介绍 当一种请求&#xff0c;总是能越过缓存&#xff0c;调用数据库&#xff0c;就是缓存穿透。 比如当请求一…

【开源】JAVA+Vue.js实现超市账单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统设计3.1 总体设计3.2 前端设计3.3 后端设计在这里插入图片描述 四、系统展示五、核心代码5.1 查询供应商5.2 查询商品5.3 新增超市账单5.4 编辑超市账单5.5 查询超市账单 六、免责说明 一、摘要 1.1 项目介绍 基于…

Linux-目录I/O-004

学习重点&#xff1a; 1.目录I/O的函数接口 2.目录的遍历&#xff0c;目录的递归遍历1.【mkdir】 1.1函数原型 【int mkdir(const char *pathname, mode_t mode);】1.2函数功能 创建目录文件1.3函数参数 1.3.1【pathname】 文件路径1.3.2【mode】 文件的权限1.4返回值 …

6.BFC

BFC 经典真题 介绍下 BFC 及其应用介绍下 BFC、IFC、GFC 和 FFC 搞懂各种 FC 一看到 BFC、IFC、GFC 和 FFC&#xff0c;大家可能会想到 KFC。 然而这里所说的 xFC 和 KFC 没有任何关系。 那么这些 FC 究竟是啥呢&#xff1f; 不着急&#xff0c;我们先搞懂一个&#xff0…

MT8788|MTK8788安卓核心板参数_4G联发科MTK模块

MT8788核心板是一款功能强大的4G全网通安卓智能模块。该模块采用了联发科AIOT芯片平台&#xff0c;具有长达8年的生命周期。MT8788模块内置了12nm制程的八核处理器&#xff0c;包括4个Cortex A73和4个Coretex A53&#xff0c;主频最高可达2.0GHZ。标配内存为4GB64GB&#xff0c…

LLM之RAG实战(二十七)| 如何评估RAG系统

有没有想过今天的一些应用程序是如何看起来几乎神奇地智能的&#xff1f;这种魔力很大一部分来自于一种叫做RAG和LLM的东西。把RAG&#xff08;Retrieval Augmented Generation&#xff09;想象成人工智能世界里聪明的书呆子&#xff0c;它会挖掘大量信息&#xff0c;准确地找到…