ADC系列文章

【ADC】电压采集实验(1){STM32F4 ADC 功能框图剖析 + 12 位电压采集公式与实现}
【ADC】电压采集实验(2){STM32F4 ADC 独立模式 + 中断服务函数:电位器电压采集}
【ADC】电压采集实验(3){STM32 ADC 扫描模式 + DMA 循环传输:多通道采集实现}
【ADC】电压采集实验(4){STM32 三个 ADC 交叉采样:单通道高速采集(DMA 循环传输)}

前言

上篇文章我们已经学习了有关ADC的基本定义,对应的框图和结构体成员的讲解。接下来本文我们将完成《独立模式单通道采集实验》,该实验可完成以下目的:1.采集开发板上电位器的动触点输出引脚电压;2.并将采集的结果通过串口打印到PC端串口调试助手;3.本实验不使用DMA传输,AD转换完成在中断服务函数读取数据。

一、硬件设计

开发板板载一个贴片滑动变阻器 。贴片滑动变阻器的动触点通过一个跳帽连接至 STM32 芯片的 ADC 通道引脚。当我们使用旋转滑动变阻器调节旋钮时,其动触点电压也会随之改变,电压变化范围为 0~3.3V,亦是开发板默认的 ADC 电压采集范围。
在这里插入图片描述

二、软件设计

两个 ADC 驱动文件, bsp_adc.h 和 bsp_adc.c,用来存放 ADC 所用 IO 引脚的初始化函数以及 ADC 配置相关函数。

编程要点

  • 配置ADC目标引脚为模拟输入模式
  • 使能ADC时钟
  • 配置通用ADC为独立模式,4分频
  • 设置目标ADC的分辨率为12位,1通道连续转换,不需要外部触发
  • 设置ADC转换通道顺序和采样时间间隔
  • 配置使能ADC转换完成中断,在中断服务函数中读取转换完成的数据
  • 启动ADC转换
  • 使能软件触发ADC转换

代码分析

ADC宏定义

使用宏定义方便硬件电路改动时进行移植


// ADC GPIO 宏定义
#define RHEOSTAT_ADC_GPIO_PORT    GPIOB
#define RHEOSTAT_ADC_GPIO_PIN     GPIO_Pin_0
#define RHEOSTAT_ADC_GPIO_CLK     RCC_AHB1Periph_GPIOB

// ADC 序号宏定义
#define RHEOSTAT_ADC              ADC1
#define RHEOSTAT_ADC_CLK          RCC_APB2Periph_ADC1
#define RHEOSTAT_ADC_CHANNEL      ADC_Channel_8


// ADC 中断宏定义
#define Rheostat_ADC_IRQ            ADC_IRQn
#define Rheostat_ADC_INT_FUNCTION   ADC_IRQHandler

ADC GPIO 初始化函数

配置ADC目标引脚为模拟输入模式


static void Rheostat_ADC_GPIO_Config(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
	
	// 使能 GPIO 时钟
	RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK, ENABLE);
		
	// 配置 IO
	GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	    
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
	GPIO_Init(RHEOSTAT_ADC_GPIO_PORT, &GPIO_InitStructure);		
}

配置 ADC 工作模式

STM32 ADC 外设针对 “电位器(Rheostat)” 采集的专用初始化函数,核心是配置 ADC 为 “12 位分辨率、连续转换、软件触发、独立模式”,并开启转换完成中断,专门用于实时采集电位器的模拟电压值


static void Rheostat_ADC_Mode_Config(void)
{
	ADC_InitTypeDef ADC_InitStructure;
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
	
  // 开启ADC时钟
	RCC_APB2PeriphClockCmd(RHEOSTAT_ADC_CLK , ENABLE);

  // -------------------ADC Common 结构体 参数 初始化------------------------
	// 独立ADC模式
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
  // 时钟为fpclk x分频	
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
  // 禁止DMA直接访问模式	
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
  // 采样时间间隔	
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;  
  ADC_CommonInit(&ADC_CommonInitStructure);
	
  // -------------------ADC Init 结构体 参数 初始化--------------------------
	ADC_StructInit(&ADC_InitStructure);
  // ADC 分辨率
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  // 禁止扫描模式,多通道采集才需要	
  ADC_InitStructure.ADC_ScanConvMode = DISABLE; 
  // 连续转换	
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; 
  //禁止外部边沿触发
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
  //外部触发通道,本例子使用软件触发,此值随便赋值即可
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
  //数据右对齐	
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  //转换通道 1个
  ADC_InitStructure.ADC_NbrOfConversion = 1;                                    
  ADC_Init(RHEOSTAT_ADC, &ADC_InitStructure);
  //---------------------------------------------------------------------------
	
  // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为3个时钟周期
  ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL, 1, ADC_SampleTime_56Cycles);
	// ADC 转换结束产生中断,在中断服务程序中读取转换值
	

	ADC_ITConfig(RHEOSTAT_ADC, ADC_IT_EOC, ENABLE);
  // 使能ADC
  ADC_Cmd(RHEOSTAT_ADC, ENABLE);  
  //开始adc转换,软件触发
  ADC_SoftwareStartConv(RHEOSTAT_ADC);
}

采样周期越短, ADC 转换数据输出周期就越短但数据精度也越低;
采样周期越长, ADC 转换数据输出周期就越长同时数据精度越高。

ADC 中断配置

STM32 电位器 ADC 中断的优先级配置函数,核心是通过 NVIC(嵌套向量中断控制器)设置 ADC 中断的分组、抢占优先级和子优先级,确保 ADC 中断能被正确响应,且优先级合理(不抢占核心业务,也不被低优先级中断阻塞)

// 配置中断优先级
static void Rheostat_ADC_NVIC_Config(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
  NVIC_InitStructure.NVIC_IRQChannel = Rheostat_ADC_IRQ;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
	
  NVIC_Init(&NVIC_InitStructure);
}

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

  • 如果电机过流中断触发,不管 ADC / 按键中断是否在执行,都会立即打断,先处理保护;
  • 如果按键和 ADC 中断同时触发(都抢占 1),先处理按键(子 0),再处理 ADC(子 1);
  • ADC 中断执行时,只有电机过流中断(抢占 0)能打断它,按键中断(同抢占 1)不能打断,只能等 ADC 执行完再响应。

ADC 中断服务函数

// ADC 转换完成中断服务程序
void ADC_IRQHandler(void)
{
	if(ADC_GetITStatus(RHEOSTAT_ADC,ADC_IT_EOC)==SET)
	{
  // 读取ADC的转换值
		ADC_ConvertedValue = ADC_GetConversionValue(RHEOSTAT_ADC);

	}
	ADC_ClearITPendingBit(RHEOSTAT_ADC,ADC_IT_EOC);

}	

中断服务函数一般定义在 stm32f4xx_it.c 文件内,我们只使能了 ADC 转换完成中断,在 ADC 转换完成后就会进入中断服务函数,我们在中断服务函数内直接读取 ADC 转换结果保存在变量ADC_ConvertedValue(在 main.c 中定义) 中。ADC_GetConversionValue 函数是获取 ADC 转换结果值的库函数,只有一个形参为 ADC 外设,可选为 ADC1、 ADC2 或 ADC3,该函数还返回一个 16 位的 ADC 转换结果值。

主函数

初始化串口和变阻器 ADC 后,在死循环中持续读取 ADC 转换值、将其换算为电压值,并通过串口打印输出,同时加入延时控制打印频率。

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{	
  /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
    Debug_USART_Config();
	Rheostat_Init();	
	
    while (1)
    {
    	
	 ADC_Vol =(float) ADC_ConvertedValue/4096*(float)3.3; // 读取转换的AD值

      printf("\r\n The current AD value = 0x%04X \r\n", ADC_ConvertedValue); 
      printf("\r\n The current AD value = %f V \r\n",ADC_Vol);     

      Delay(0xffffee);  
    }
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐