A/D转换的常用技术有逐次逼近式、双积分式、并行式和跟踪比较式等。目前用的较多的是前3种。
A/D转换器的主要技术指标
转换时间
分辨率
例如,8位A/D转换器的数字输出量的变化范围为0~255,当输入电压的满刻度为5V时,数字量每变化一个数字所对应输入模拟电压的值为5V/255=19.6mV,其分辨能力为19.6mV
转换精度
转换精度指的是转换后所得的结果相对于实际值的准确度,可以用满量程的百分比这一相对误差来表示,如±0.05%
ADC应用设计深入讨论尽管STM32内部集成了12位ADC,但在实际应用中,要想真正实现12位精度且比较稳定的ADC并不简单,需要进一步从硬件、软件方面进行综合、细致地考虑。下面介绍一些在ADC应用设计中应该考虑的几个要点:
工作电压的稳定性
AVCC是提供给ADC工作的电源,如果AVCC不稳定,就会影响ADC的转换精度
参考电压的确定
ADC的参考电压应稍大于输入电压的最高值。ADC的参考电压VREF可以选择为AVCC,或外接参考电压源,外接的参考电压源应该稳定。
采样时钟的选择
ADC时钟频率最大为14MHz。如果STM32系统时钟频率为56MHz时,一般为4分频,ADC时钟频率为14MHz,如果系统时钟频率为72MHz时,一般为6分频,ADC时钟频率为12MHz。
ADC通道的输入信号频率带宽取决于ADC时钟频率。把ADC通道配置为55.5个周期,若ADCCLK的时钟频率配置为12MHz,则ADC采样的时间计算公式如下。Tcovn=采样时间+12.5个周期
其中:Tcovn为总转换时间,采样时间是根据每个通道的ADC_SampleTime的值来决定的。后面的12.5个周期是ADC转换时量化所需要的
固定的周期,ADC的一次转换所需要的时间是Tcovn=(55.5+12.5)×(1/12),大约是5.67μs。STM32的ADC输入阻抗典型值为50kΩ,为了保证测量准确,被测信号源的输出阻抗要尽可能低。
模拟噪声的抑制
器件外部和内部的数字电路会产生电磁干扰,并会影响模拟测量的精度。如果对ADC的转换精度要求很高,则可以采用以下的技术来降低噪声的影响。
使模拟信号的通路尽可能地短,模拟信号连线应从模拟地的布线盘上通过,并使它们尽可能远离高速开关数字信号线。
STM32的AVCC引脚应该通过LC网络与数字端电源VCC相连。
如果某些ADC引脚作为通用数字输出口,那么在ADC转换过程中,不要改变这些引脚的状态。
校准
ADC - 电压
要将 ADC (模数转换器) 的原始数值转换为电压值,通常需要进行以下步骤:
-
确定 ADC 的分辨率:
例如,对于 STM32F103C8T6,ADC 的分辨率是 12 位,因此其数值范围是 0 到 4095。 -
确定参考电压:
确定 ADC 的参考电压 Vref。在 STM32F103C8T6 中,默认情况下Vref 通常是 3.3V,但这取决于具体的硬件配置。 -
读取 ADC 值:
读取 ADC 转换后的数值,例如 ADC_value 。 -
使用公式转换为电压值:
使用公式计算实际的电压值:
其中 ( N ) 是 ADC 的分辨率。例如,对于 12 位 ADC,( N = 12 )。
具体步骤如下:
-
读取 ADC 值:
uint16_t ADC_value = HAL_ADC_GetValue(&hadc1);
-
计算电压值:
float V_REF = 3.3; // 假设参考电压为 3.3V float voltage = (ADC_value / 4095.0) * V_REF;
以下是一个完整的例子,假设你使用 STM32 HAL 库:
#include "main.h"
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
while (1)
{
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint16_t ADC_value = HAL_ADC_GetValue(&hadc1);
float V_REF = 3.3;
float voltage = (ADC_value / 4095.0) * V_REF;
// 在这里使用计算出的电压值
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void SystemClock_Config(void)
{
// 时钟配置代码
}
这个例子展示了如何初始化 ADC1 并读取通道 1 (PA1) 的 ADC 值,然后将其转换为电压值。
如果将100V电压经过串联电阻分压,电阻R1为66K,R2为3K,这样如何通过ADC采样计算出电压?
将 100V 电压经过分压电路并通过 ADC 进行采样,计算出实际电压的方法如下:
-
分压电路公式:
分压电路的输出电压 Vout 可以用以下公式计算:
其中 Vin 是输入电压, R1 和 R2 是分压电阻。
-
具体分压电路参数:
注意:在实际应用中,必须确保 ( V_{out} ) 不超过 ADC 的参考电压(例如 3.3V)。在这种情况下,分压比使得 Vout 超过了 ADC 允许的最大电压,需要调整分压电阻以确保 Vout 在安全范围内 。
-
假设安全分压电阻:
-
通过 ADC 读取分压后的电压:
-
计算实际电压:
- 反向计算原始输入电压:
假设使用 STM32 和 HAL 库,代码示例如下:
#include "main.h"
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
while (1)
{
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint16_t ADC_value = HAL_ADC_GetValue(&hadc1);
float V_REF = 3.3;
float V_out = (ADC_value / 4095.0) * V_REF;
float R1 = 96000.0; // 96KΩ
float R2 = 3000.0; // 3KΩ
float V_in = V_out * (R1 + R2) / R2;
// 使用计算出的 V_in
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void SystemClock_Config(void)
{
// 时钟配置代码
}
这个代码展示了如何读取 ADC 值并将其转换为实际电压,再通过分压公式计算出原始输入电压。注意在实际电路中,请确保所有电压和电阻值在安全范围内,以防止损坏设备。
ADC 软件滤波
ADC 读取到的值有抖动是常见现象,可以通过以下几种方法来进行滤波:
- 平均值滤波:读取多次 ADC 值,取平均值,可以平滑抖动。
- 中值滤波:读取多次 ADC 值,取中间值,可以去除突发噪声。
- 低通滤波器:通过数字滤波算法,平滑高频噪声。
以下是每种方法的具体实现:
1. 平均值滤波
#define NUM_SAMPLES 10
uint16_t Read_ADC_Averaged(ADC_HandleTypeDef* hadc)
{
uint32_t sum = 0;
for (int i = 0; i < NUM_SAMPLES; i++)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
sum += HAL_ADC_GetValue(hadc);
}
return sum / NUM_SAMPLES;
}
2. 中值滤波
#define NUM_SAMPLES 9
uint16_t Read_ADC_Median(ADC_HandleTypeDef* hadc)
{
uint16_t samples[NUM_SAMPLES];
for (int i = 0; i < NUM_SAMPLES; i++)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
samples[i] = HAL_ADC_GetValue(hadc);
}
// 排序
for (int i = 0; i < NUM_SAMPLES - 1; i++)
{
for (int j = 0; j < NUM_SAMPLES - i - 1; j++)
{
if (samples[j] > samples[j + 1])
{
uint16_t temp = samples[j];
samples[j] = samples[j + 1];
samples[j + 1] = temp;
}
}
}
return samples[NUM_SAMPLES / 2];
}
3. 低通滤波器 (IIR 滤波器)
#define ALPHA 0.1 // 滤波系数,取值范围为 0 到 1
float Read_ADC_LowPass(ADC_HandleTypeDef* hadc, float previous_filtered_value)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
uint16_t raw_value = HAL_ADC_GetValue(hadc);
float current_filtered_value = ALPHA * raw_value + (1 - ALPHA) * previous_filtered_value;
return current_filtered_value;
}
综合代码示例
将这些滤波方法整合到你的代码中,可以如下:
#include "main.h"
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
#define NUM_SAMPLES 10
#define ALPHA 0.1
float previous_filtered_value = 0;
uint16_t Read_ADC_Averaged(ADC_HandleTypeDef* hadc)
{
uint32_t sum = 0;
for (int i = 0; i < NUM_SAMPLES; i++)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
sum += HAL_ADC_GetValue(hadc);
}
return sum / NUM_SAMPLES;
}
uint16_t Read_ADC_Median(ADC_HandleTypeDef* hadc)
{
uint16_t samples[NUM_SAMPLES];
for (int i = 0; i < NUM_SAMPLES; i++)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
samples[i] = HAL_ADC_GetValue(hadc);
}
for (int i = 0; i < NUM_SAMPLES - 1; i++)
{
for (int j = 0; j < NUM_SAMPLES - i - 1; j++)
{
if (samples[j] > samples[j + 1])
{
uint16_t temp = samples[j];
samples[j] = samples[j + 1];
samples[j + 1] = temp;
}
}
}
return samples[NUM_SAMPLES / 2];
}
float Read_ADC_LowPass(ADC_HandleTypeDef* hadc, float previous_filtered_value)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
uint16_t raw_value = HAL_ADC_GetValue(hadc);
float current_filtered_value = ALPHA * raw_value + (1 - ALPHA) * previous_filtered_value;
return current_filtered_value;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
while (1)
{
uint16_t ADC_value_averaged = Read_ADC_Averaged(&hadc1);
uint16_t ADC_value_median = Read_ADC_Median(&hadc1);
previous_filtered_value = Read_ADC_LowPass(&hadc1, previous_filtered_value);
float V_REF = 3.3;
float V_out_averaged = (ADC_value_averaged / 4095.0) * V_REF;
float V_out_median = (ADC_value_median / 4095.0) * V_REF;
float V_out_lowpass = (previous_filtered_value / 4095.0) * V_REF;
float R1 = 96000.0;
float R2 = 3000.0;
float V_in_averaged = V_out_averaged * (R1 + R2) / R2;
float V_in_median = V_out_median * (R1 + R2) / R2;
float V_in_lowpass = V_out_lowpass * (R1 + R2) / R2;
// 使用计算出的 V_in
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void SystemClock_Config(void)
{
// 时钟配置代码
}
这个代码示例展示了如何分别使用平均值滤波、中值滤波和低通滤波来处理 ADC 抖动。根据实际需求选择合适的滤波方法,可以有效减小 ADC 读数的抖动。
低通滤波器(IIR)
低通滤波器是一种常用的信号处理技术,用于平滑信号并去除高频噪声。最常见的低通滤波器之一是指数加权移动平均滤波器(IIR 低通滤波器)。
原理
指数加权移动平均滤波器的基本原理是对当前采样值和先前的滤波值进行加权平均。公式如下:
具体实现步骤
-
定义变量:
- 滤波系数 (\alpha)
- 上一次的滤波输出值(初始为 0)
-
读取 ADC 值:
- 使用 HAL 库读取 ADC 值
-
计算滤波值:
- 应用低通滤波公式
-
重复进行:
- 在循环中持续读取 ADC 值并计算滤波值
代码实现
以下是一个详细的 C 语言代码示例,使用 STM32 HAL 库实现低通滤波器:
#include "main.h"
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
#define ALPHA 0.1 // 滤波系数,范围在 0 到 1 之间
float previous_filtered_value = 0; // 上一次的滤波输出值
float Read_ADC_LowPass(ADC_HandleTypeDef* hadc, float previous_filtered_value)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
uint16_t raw_value = HAL_ADC_GetValue(hadc);
float current_filtered_value = ALPHA * raw_value + (1 - ALPHA) * previous_filtered_value;
return current_filtered_value;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
while (1)
{
previous_filtered_value = Read_ADC_LowPass(&hadc1, previous_filtered_value);
float V_REF = 3.3;
float V_out = (previous_filtered_value / 4095.0) * V_REF;
float R1 = 96000.0; // 96KΩ
float R2 = 3000.0; // 3KΩ
float V_in = V_out * (R1 + R2) / R2;
// 在这里使用计算出的 V_in
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void SystemClock_Config(void)
{
// 时钟配置代码
}
详细说明
-
定义滤波系数和变量:
#define ALPHA 0.1
定义滤波系数,决定新输入值的权重。float previous_filtered_value = 0;
初始化上一次的滤波输出值。
-
读取 ADC 值并应用低通滤波公式:
HAL_ADC_Start(hadc);
启动 ADC。HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
等待 ADC 转换完成。uint16_t raw_value = HAL_ADC_GetValue(hadc);
获取 ADC 读取值。float current_filtered_value = ALPHA * raw_value + (1 - ALPHA) * previous_filtered_value;
应用低通滤波公式计算当前滤波值。
-
在主循环中持续滤波:
previous_filtered_value = Read_ADC_LowPass(&hadc1, previous_filtered_value);
调用滤波函数,更新滤波值。
-
计算和使用滤波后的电压值:
float V_out = (previous_filtered_value / 4095.0) * V_REF;
计算分压电路的输出电压。float V_in = V_out * (R1 + R2) / R2;
反向计算原始输入电压。
通过这个实现,可以有效地平滑 ADC 读取值的抖动,提高测量的稳定性。
ADC - 电流
为了通过 ADC 采集电流,并根据 ADC 值计算出电流值,需要以下几个步骤:
- 选择电流传感器:通常使用分流电阻 (shunt resistor) 或霍尔效应电流传感器。
- 采样电压:通过分流电阻或传感器将电流转换为电压,然后用 ADC 采样该电压。
- 计算电流:根据电压值和已知的分流电阻或传感器的特性,计算出实际电流。
以下是详细步骤:
1. 分流电阻测量电流
使用分流电阻测量电流的方法如下:
2. ADC 采样电压
使用 ADC 采样分流电阻上的电压。假设 ADC 的参考电压 ( V_{REF} ) 为 3.3V,分辨率为 12 位 (0 到 4095)。
3. 计算电流
根据 ADC 采样到的电压值,计算实际电流:
代码实现
以下是一个详细的 C 语言代码示例,使用 STM32 HAL 库通过分流电阻测量电流:
#include "main.h"
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
#define R_SHUNT 0.01 // 分流电阻值 (单位: 欧姆)
#define V_REF 3.3 // ADC 参考电压 (单位: 伏特)
#define ADC_RESOLUTION 4095.0 // ADC 分辨率 (12 位)
float Read_Current(ADC_HandleTypeDef* hadc, float R_shunt)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
uint16_t ADC_value = HAL_ADC_GetValue(hadc);
float V_shunt = (ADC_value / ADC_RESOLUTION) * V_REF;
float current = V_shunt / R_shunt;
return current;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
while (1)
{
float current = Read_Current(&hadc1, R_SHUNT);
// 在这里使用计算出的电流值 current
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void SystemClock_Config(void)
{
// 时钟配置代码
}
详细说明
-
定义分流电阻和 ADC 参数:
#define R_SHUNT 0.01
定义分流电阻值,单位为欧姆。#define V_REF 3.3
定义 ADC 参考电压,单位为伏特。#define ADC_RESOLUTION 4095.0
定义 ADC 的分辨率。
-
读取 ADC 值并计算电流:
HAL_ADC_Start(hadc);
启动 ADC。HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
等待 ADC 转换完成。uint16_t ADC_value = HAL_ADC_GetValue(hadc);
获取 ADC 读取值。float V_shunt = (ADC_value / ADC_RESOLUTION) * V_REF;
计算分流电阻上的电压。float current = V_shunt / R_shunt;
计算实际电流。
-
在主循环中持续测量电流:
float current = Read_Current(&hadc1, R_SHUNT);
调用测量电流的函数,获取当前电流值。
通过这个实现,可以有效地使用 ADC 采集电流,并根据 ADC 值计算出实际电流值。
ADC - 温度
使用查表法计算温度是一种常见且简单的方法,特别适合在微控制器上实现,因为它不需要复杂的数学运算。以下是具体步骤和实现方法:
步骤概述
- 建立查表:根据 NTC 热敏电阻的特性曲线,创建一个电阻值与温度对应的查表(数组)。
- 读取 ADC 值:使用 ADC 读取分压电路的电压。
- 计算 NTC 电阻值:根据分压公式计算 NTC 热敏电阻的电阻值。
- 查找温度:在查表中找到对应的温度值。
具体步骤
1. 建立查表
NTC 热敏电阻的电阻值随温度变化。可以根据热敏电阻的数据手册或特性曲线建立一个电阻值与温度对应的查表。例如:
const uint32_t resistance_table[] = {3380, 3300, 3220, /* ... */ 100}; // 阻值 (单位: 欧姆)
const float temperature_table[] = {0, 1, 2, /* ... */ 100}; // 温度值 (单位: 摄氏度)
const int table_size = sizeof(resistance_table) / sizeof(resistance_table[0]);
2. 使用 ADC 读取电压
使用 STM32 的 ADC 采样 Vout。
3. 计算 NTC 电阻值
4. 查找温度
在查表中找到对应的温度值,可以使用线性插值法提高精度。
代码实现
以下是一个详细的 C 语言代码示例,使用 STM32 HAL 库实现以上步骤:
#include "main.h"
#include <stdio.h>
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
#define R1 10000 // 固定电阻值 (单位: 欧姆)
#define V_REF 3.3 // ADC 参考电压 (单位: 伏特)
#define ADC_RESOLUTION 4095.0 // ADC 分辨率 (12 位)
// 查表 (电阻值单位: 欧姆, 温度单位: 摄氏度)
const uint32_t resistance_table[] = {3380, 3300, 3220, /* ... */ 100};
const float temperature_table[] = {0, 1, 2, /* ... */ 100};
const int table_size = sizeof(resistance_table) / sizeof(resistance_table[0]);
float Read_NTC_Temperature(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Start(hadc);
HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
uint16_t ADC_value = HAL_ADC_GetValue(hadc);
float Vout = (ADC_value / ADC_RESOLUTION) * V_REF;
float R_ntc = R1 * (V_REF / Vout - 1);
// 查表法找到温度值
for (int i = 0; i < table_size - 1; i++)
{
if (R_ntc >= resistance_table[i+1] && R_ntc <= resistance_table[i])
{
// 线性插值计算温度
float R1 = resistance_table[i];
float R2 = resistance_table[i+1];
float T1 = temperature_table[i];
float T2 = temperature_table[i+1];
float temperature = T1 + (R_ntc - R1) * (T2 - T1) / (R2 - R1);
return temperature;
}
}
// 如果未找到匹配的电阻值,返回一个默认值
return -273.15; // 表示错误
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
while (1)
{
float temperature = Read_NTC_Temperature(&hadc1);
// 使用计算出的温度值 temperature
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void SystemClock_Config(void)
{
// 时钟配置代码
}
详细说明
-
定义常量和查表:
#define R1 10000
定义固定电阻 ( R1 ) 的值,单位为欧姆。#define V_REF 3.3
定义 ADC 参考电压,单位为伏特。#define ADC_RESOLUTION 4095.0
定义 ADC 的分辨率。const uint32_t resistance_table[]
和const float temperature_table[]
分别定义电阻值和温度值的查表。const int table_size
定义查表的大小。
-
读取 ADC 值并计算电阻和温度:
HAL_ADC_Start(hadc);
启动 ADC。HAL_ADC_PollForConversion(hadc, HAL_MAX_DELAY);
等待 ADC 转换完成。uint16_t ADC_value = HAL_ADC_GetValue(hadc);
获取 ADC 读取值。float Vout = (ADC_value / ADC_RESOLUTION) * V_REF;
计算分压电路的输出电压。float R_ntc = R1 * (V_REF / Vout - 1);
计算 NTC 热敏电阻的电阻值。
-
查找温度值:
- 遍历查表,通过线性插值计算电阻值与温度值之间的关系,找到对应的温度。
通过这个实现,可以有效地使用 ADC 采集 NTC 热敏电阻的电阻值,并通过查表法将其转换为温度。线性插值可以提高温度计算的精度。查表数据可以根据具体的 NTC 热敏电阻特性曲线来建立。