一、模数转换器(ADC)概述:
1、ADC:Analog to Digital Converter
2、将时间和幅值连续的模拟量转化为时间和幅值离散的数字量,A/D 转换一般要经过采样、保持、量化和编码 4 个过程。
3、常用 ADC:逐次逼近型、双积分型、Σ-Δ 型。
4、AD 转换器的几个技术指标:
- 量程:指 ADC 所能输入模拟信号的类型和电压范围,即参考电压。信号类型包括单极性和双极性。
- 转换位数:量化过程中的量化位数 n(A/D 转换后的输出结果用 n 位二进制数来表示)。例:10 位 ADC 的输出值是 0 ~ 1023
- 分辨率:ADC 能够分辨的模拟信号最小变化量(分辨率 = 量程 / 2n)。
- 转换时间:ADC 完成一次完整的 A/D 转换所需要的时间,包括采样、保持、量化、编码的全过程。
5、ADC 数据采用的计算应用:
有一个温度测控系统,已知温度传感器在 0℃ 到 100℃ 之间为线性输出,参考电压为 5V,采用 8 位的 A/D 转换器,0℃ 的时候测得电压为 1.8V,100℃ 的时候测得电压为 4.3V。
求:1、系统的分辨率。 2、若采集到数据 1001 0001B,表示的电压?表示的温度?
解:1、分辨率(电压)= 5V / 28 = 0.0195V = 1.95mV 分辨率(温度)= 0.0195 * 40 = 0.78℃,在采用电压 1.8V ~ 4.3V 之中,采样电压每上升 0.0195V,所代表的温度就上升 0.78℃。
2、温度 = 40 * ( 采样得到电压 - 1.8 ) 1001 0001B = 91H = 145 表示的电压 = 145 * 0.0195V ≈ 2.83V 表示的温度 = 40 * ( 2.83 - 1.8 ) = 41.2℃
二、STM32 的 ADC 资源概述:
STM32 拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC),这些 ADC 可以独立使用,也可以使用双重模式(提高采样率)。 STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个测量通道,可测量 16 个外部和 2 个内部信号源(内部温度和内部参考电压)。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。 ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。 STM32F103 系列最少都拥有 2 个 ADC。STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14Mhz(在 STM32CubeMX 中配置时钟树时应注意),否则将导致结果准确度下降。 STM32 将 ADC 的组织形式划分为 2 个通道组:规则通道组和注入通道组。规则通道组 ADC 可以对一组最多 16 个通道按照指定的顺序逐个进行转换,相当于正常运行的程序;在实际应用中,可能需要中断规则组的转换,临时对某些通道进行转换,称为注入通道组,最多由 4 个通道组成,就相当于中断,在你程序正常执行的时候,打断你的执行。注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。 A/D 转换结果有 2 种存储方式:左对齐、右对齐。
三、STM32CubeMX 中关于 ADC 的配置:
1、配置引脚复用为 ADC:
STM32F103RCT6 的 ADC1 通道 0 在 PA0 上。
2、配置 A/D 转换相关设置:
此处我们选择默认配置。
3、ADC 通道中断使能:
需要使用 ADC 通道中断则使能。
四、程序编写:
1、查询方式和中断方式的 HAL 库函数应用:
① 查询方式,阻塞式 A/D 转换:
uint_t ADC_Value = 0;
HAL_ADC_Start(&hadc1);
if(HAL_ADC_PollForConversion(&hadc1,10) == HAL_OK){
ADC_Value = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1);
② 中断方式,非阻塞式 A/D 转换:
uint_t ADC_Value = 0;
HAL_ADC_Start_IT(&hadc1);
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1){
ADC_Value = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop_IT(&hadc1);
}
}
2、程序编写:
示例:将ADC_IN0 设置为 12 位 ADC,右对齐,启用中断,分别用查询和中断 2 种方式,每隔 0.5 秒采样一次 ADC 的数据(使用定时器计时),将每次读取到的 ADC 采样值转换为对应的电压值,通过USART1 发送到上位机。
参考代码(查询方式):
#include "stdio.h"
#define LED0_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET)
#define LED0_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET)
uint16_t ADC_Value = 0;
float ADC_Volt = 0;
uint8_t Str_Buf[64] = {'\0'};
void ADC_UR1_Send()
{
sprintf((char*)Str_Buf, "采样值:%d,电压值:%fV。\r\n", ADC_Value, (ADC_Volt/1000));
HAL_UART_Transmit(&huart1, Str_Buf, sizeof(Str_Buf), 10000);
}
void ADC0_Get_Value()
{
HAL_ADC_Start(&hadc1);
if(HAL_ADC_PollForConversion(&hadc1,10) == HAL_OK){
ADC_Value = HAL_ADC_GetValue(&hadc1);
ADC_Volt = (ADC_Value * 3300 / 4096);
}
HAL_ADC_Stop(&hadc1);
ADC_UR1_Send();
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2){
ADC0_Get_Value();
}
}
HAL_TIM_Base_Start_IT(&htim2);
参考代码(中断方式):
#include "stdio.h"
#define LED0_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET)
#define LED0_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET)
uint16_t ADC_Value = 0;
float ADC_Volt = 0;
uint8_t Str_Buf[64] = {'\0'};
void ADC_UR1_Send()
{
sprintf((char*)Str_Buf, "采样值:%d,电压值:%fV。\r\n", ADC_Value, (ADC_Volt/1000));
HAL_UART_Transmit(&huart1, Str_Buf, sizeof(Str_Buf), 10000);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2){
HAL_ADC_Start_IT(&hadc1);
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1){
ADC_Value = HAL_ADC_GetValue(&hadc1);
ADC_Volt = (ADC_Value * 3300 / 4096);
HAL_ADC_Stop_IT(&hadc1);
ADC_UR1_Send();
}
}
HAL_TIM_Base_Start_IT(&htim2);
上位机串口:
|