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);}
}
相关文章:

stm32——hal库学习笔记(ADC)
这里写目录标题 一、ADC简介(了解)1.1,什么是ADC?1.2,常见的ADC类型1.3,并联比较型工作示意图1.4,逐次逼近型工作示意图1.5,ADC的特性参数1.6,STM32各系列ADC的主要特性 …...

一周学会Django5 Python Web开发-Http请求HttpRequest请求类
锋哥原创的Python Web开发 Django5视频教程: 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计25条视频,包括:2024版 Django5 Python we…...

element el-date-picker 日期组件置灰指定日期范围、禁止日期范围日期选择
JS如何将当前日期或指定日期转时间戳_javascript技巧_脚本之家 小于指定日期前的日期置灰 比如这里 指定日期是 2024-02-20 10:48:15 disabledDate(time) time是一个函数提供的时间用于比较 他是一个时间戳↓ 理解为我们想要置灰的时间 time.getTime() < timeStamps- 1 *…...

202434读书笔记|《繁星·春水》——残花缀在繁枝上,鸟儿飞去了,撒得落红满地,生命也是这般的一瞥么?
202434读书笔记|《繁星春水》——残花缀在繁枝上,鸟儿飞去了,撒得落红满地,生命也是这般的一瞥么? 繁星春水 《繁星春水》冰心著,共300多首小诗,并不是惊艳,就那么平凡而朴实的看完了。 繁星 黑…...
Golang 关于 interface 接口的理解
package mainimport "fmt"// 定义一个存储器接口:支持mysql存储、redis存储 type StorageManager interface {insert(data string) int // 增加update(id int, data string) int // 更新 }// 实现一个Mysql存储器 type Mysql struct{}func (mysql…...

SQL注入漏洞解析--less-7
我们先看一下第七关 页面显示use outfile意思是利用文件上传来做 outfile是将检索到的数据,保存到服务器的文件内: 格式:select * into outfile "文件地址" 示例: mysql> select * into outfile f:/mysql/test/one f…...

java高级——反射
目录 反射概述反射的使用获取class对象的三种方式反射获取类的构造器1. 获取类中所有的构造器2. 获取单个构造器 反射获取构造器的作用反射获取成员变量反射变量赋值、取值获取类的成员方法反射对象类方法执行 反射简易框架案例案例需求实现步骤代码如下 反射概述 什么是反射 反…...

云计算新宠:探索Apache Doris的云原生策略
文章目录 Apache Doris 特性极简架构高效自运维高并发场景支持MPP 执行引擎明细与聚合模型的统一便捷数据接入 Apache Doris 极速 1.0 时代极速列式内存布局向量化的计算框架Cache 亲和度虚函数调用SIMD 指令集 稳定多源 关于 Apache Doris 开源社区基于云原生向量数据库Milvus…...
【PHP设计模式08】装饰模式
【装饰模式】 装饰模式,又称装饰器模式 或 装饰者模式 或 油漆工模式,通过创建一个“装饰对象”,在不改变原有类和使用继承的情况下,动态地扩展一个对象的功能,比直接生成子类继承更加灵活,可以通过多个不同的具体装饰类,创建多个不同的行为组合。 结构: 抽象构件…...

寒假作业Day 01
这个项目主要是为了复习博主之前关于C语言和数据结构的寒假作业,大家也可以根据这些题目自己进行填写并检查自己的知识点是否过关 博主也会有错误,所以如果大家看到错误,也希望大家能够进行指正,谢谢大家! Day 01 一…...
学习JAVA的第四天(基础)
目录 方法 方法的定义 方法的调用 参数 注意事项 方法的重载 练习 面向对象 类和对象 定义类的注意事项 封装 private关键字 this关键字 构造方法 标准的Javabean类 创建一个对象时,虚拟机做了什么? 方法 方法含义:方法是程序…...
拉美巴西阿根廷媒体宣发稿墨西哥哥伦比亚新闻营销如何助推跨境出海推广?
【本篇由言同数字科技有限公司原创】拉美地区是一个巨大的市场,其中包括了许多国家,如巴西、阿根廷、智利、哥伦比亚等。这些国家的消费者对品牌的认知度和忠诚度不同,而且市场环境也存在着很大的差异。因此,品牌需要通过跨境海外…...

SpringMVC 学习(九)之拦截器
目录 1 拦截器介绍 2 创建一个拦截器类 3 配置拦截器 1 拦截器介绍 在 SpringMVC 中,拦截器 (Interceptor) 是一种用于拦截 HTTP 请求并在请求处理之前或之后执行自定义逻辑的组件。拦截器可以用于实现以下功能: 权限验证:在请求处理之前…...

TCP/IP-常用网络协议自定义结构体
1、TCP/IP模型: 2、TCP/IP- 各层级网络协议(从下往上): 1)数据链路层: ARP: 地址解析协议,用IP地址获取MAC地址的协议,通过ip的地址获取mac地 …...
内部控制提纲
当然,以下是一个更详细的关于内部控制的论文提纲: 一、引言 1.1 内部控制的定义与重要性 解释内部控制的基本概念和它在企业管理中的作用阐述内部控制对企业风险管理和运营效率的影响 1.2 内部控制的目标与原则 列出内部控制的主要目标,…...

江科大stm32 定时器 TIM输出比较--学习笔记
这几天遇到输出比较相关的问题,于是来学习下TIM输出比较部分知识点! 输出比较简介 CNT是计数器的值,CCR寄存器是捕获/ 比较寄存器 简单的讲,输出比较就是用来输出PWM波形。 PWM简介 占空比:高电平占一个周期的比例。…...

VHDL-2008语言支持
VHDL-2008语言支持 介绍 AMD Vivado™合成支持VHDL-2008标准的可合成子集。这个以下部分介绍了支持的子集以及使用它的过程。将Vivado设置为使用VHDL-2008有几种方法可以使用Vivado运行VHDL-2008文件。您可以转到源文件属性窗口,并从可用文件类型的下拉列表中设置…...
linux系统git的安装和配置
安装和配置 安装gitYum安装Git编译安装 运行 Git 前的配置配置git命令集配置过程 获取帮助 安装git Yum安装Git yum install git -y编译安装 编译安装可以安装较新版本的git Git下载地址: https://github.com/git/git/releases # 安装依赖关系 yum install curl-d…...
oracle11g数据库 冷备份与冷恢复
我们在做备份时,究竟需要备份数据库的哪些文件呢? 其实只需要备份数据文件和控制文件就可以了,其他的参数文件,重做日志文件以及口令文件与数据文件相比都非常小,所以在一般情况下都会一起备份。 冷备份步骤…...

考研数据结构算法机试训练1
中南大学上机压轴题 测试数据: 3 500 0.6 100 0.8 200 0.7 100 输出 390首先要对输入的折扣进行排序,优先使用比率低的z进行支付。 然后用lowcost记录目前多少钱是打过折的。T-lowcost就是剩余没打折的。 每次循环用上一个人的折扣额度。若所有人折扣额…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...