STM32入门笔记(03): ADC低通滤波(IIR)(SPL库函数版)(2)

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 (模数转换器) 的原始数值转换为电压值,通常需要进行以下步骤:

  1. 确定 ADC 的分辨率
    例如,对于 STM32F103C8T6,ADC 的分辨率是 12 位,因此其数值范围是 0 到 4095。

  2. 确定参考电压
    确定 ADC 的参考电压 Vref。在 STM32F103C8T6 中,默认情况下Vref 通常是 3.3V,但这取决于具体的硬件配置。

  3. 读取 ADC 值
    读取 ADC 转换后的数值,例如 ADC_value 。

  4. 使用公式转换为电压值
    使用公式计算实际的电压值:
    在这里插入图片描述

    其中 ( N ) 是 ADC 的分辨率。例如,对于 12 位 ADC,( N = 12 )。

具体步骤如下:

  1. 读取 ADC 值:

    uint16_t ADC_value = HAL_ADC_GetValue(&hadc1);
    
  2. 计算电压值:

    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 进行采样,计算出实际电压的方法如下:

  1. 分压电路公式
    分压电路的输出电压 Vout 可以用以下公式计算:
    在这里插入图片描述

    其中 Vin 是输入电压, R1 和 R2 是分压电阻。

  2. 具体分压电路参数

    在这里插入图片描述

    注意:在实际应用中,必须确保 ( V_{out} ) 不超过 ADC 的参考电压(例如 3.3V)。在这种情况下,分压比使得 Vout 超过了 ADC 允许的最大电压,需要调整分压电阻以确保 Vout 在安全范围内

  3. 假设安全分压电阻

在这里插入图片描述

  1. 通过 ADC 读取分压后的电压
    设定 ADC 的参考电压 ( V_{REF} = 3.3V )。
假设 ADC 读取的数值为 ( ADC_value ),分辨率为 12 位 (0 到 4095)。

  2. 计算实际电压

在这里插入图片描述

  1. 反向计算原始输入电压

在这里插入图片描述

假设使用 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 读取到的值有抖动是常见现象,可以通过以下几种方法来进行滤波:

  1. 平均值滤波:读取多次 ADC 值,取平均值,可以平滑抖动。
  2. 中值滤波:读取多次 ADC 值,取中间值,可以去除突发噪声。
  3. 低通滤波器:通过数字滤波算法,平滑高频噪声。

以下是每种方法的具体实现:

1. 平均值滤波

#define NUM_SAMPLES 10uint16_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 9uint16_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 到 1float 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.1float 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 低通滤波器)。

原理

指数加权移动平均滤波器的基本原理是对当前采样值和先前的滤波值进行加权平均。公式如下:

在这里插入图片描述

具体实现步骤

  1. 定义变量

    • 滤波系数 (\alpha)
    • 上一次的滤波输出值(初始为 0)
  2. 读取 ADC 值

    • 使用 HAL 库读取 ADC 值
  3. 计算滤波值

    • 应用低通滤波公式
  4. 重复进行

    • 在循环中持续读取 ADC 值并计算滤波值

代码实现

以下是一个详细的 C 语言代码示例,使用 STM32 HAL 库实现低通滤波器:


uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量
float Vin;          // 输入电压
#define ALPHA 0.1  // 滤波系数,范围在 0 到 1 之间static float previous_filtered_value = 0;  // 上一次的滤波输出值float Read_ADC_LowPass(float previous_filtered_value)
{ADValue = AD_GetValue();					//获取AD转换的值float current_filtered_value = ALPHA *(float)ADValue + (1 - ALPHA) * previous_filtered_value;return current_filtered_value;
}int main(void)
{AD_Init();				//AD初始化while (1) {previous_filtered_value = Read_ADC_LowPass(previous_filtered_value);Voltage = (float)previous_filtered_value / 4095 * 3.3;		// 公式:adc_value/4096=Vout/Vref ,Vref=3.3// R1=10k R2=1kVin = Voltage * 11.0; // 分压公式:Vout=Vin*(R2/(R1+R2)) 所以 Vin=Vout*((R1+R2)/R2)Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间}
}

详细说明

  1. 定义滤波系数和变量

    • #define ALPHA 0.1 定义滤波系数,决定新输入值的权重。
    • float previous_filtered_value = 0; 初始化上一次的滤波输出值。
  2. 读取 ADC 值并应用低通滤波公式

    • ADValue = AD_GetValue(); 获取 ADC 读取值。
    • float current_filtered_value = ALPHA *(float)ADValue + (1 - ALPHA) * previous_filtered_value; 应用低通滤波公式计算当前滤波值。
  3. 在主循环中持续滤波

    • previous_filtered_value = Read_ADC_LowPass(previous_filtered_value); 调用滤波函数,更新滤波值。
  4. 计算和使用滤波后的电压值

    • Voltage = (float)previous_filtered_value / 4095 * 3.3; 计算分压电路的输出电压。
    • Vin = Voltage * 11.0; 反向计算原始输入电压 Vin。

通过这个实现,可以有效地平滑 ADC 读取值的抖动,提高测量的稳定性。

在这里插入图片描述

在这里插入图片描述

程序下载

  • 测试程序:STM32_AD单通道_低通滤波IIR计算电压(20240704)

ADC - 电流

为了通过 ADC 采集电流,并根据 ADC 值计算出电流值,需要以下几个步骤:

  1. 选择电流传感器:通常使用分流电阻 (shunt resistor) 或霍尔效应电流传感器。
  2. 采样电压:通过分流电阻或传感器将电流转换为电压,然后用 ADC 采样该电压。
  3. 计算电流:根据电压值和已知的分流电阻或传感器的特性,计算出实际电流。

以下是详细步骤:

1. 分流电阻测量电流

使用分流电阻测量电流的方法如下:

在这里插入图片描述

2. ADC 采样电压

使用 ADC 采样分流电阻上的电压。假设 ADC 的参考电压 ( V_{REF} ) 为 3.3V,分辨率为 12 位 (0 到 4095)。

3. 计算电流

根据 ADC 采样到的电压值,计算实际电流:
[
I = \frac{V_{shunt}}{R_{shunt}} = \frac{ADC_{value} \times V_{REF}}{4095 \times R_{shunt}}
]

代码实现

以下是一个详细的 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)
{// 时钟配置代码
}

详细说明

  1. 定义分流电阻和 ADC 参数

    • #define R_SHUNT 0.01 定义分流电阻值,单位为欧姆。
    • #define V_REF 3.3 定义 ADC 参考电压,单位为伏特。
    • #define ADC_RESOLUTION 4095.0 定义 ADC 的分辨率。
  2. 读取 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; 计算实际电流。
  3. 在主循环中持续测量电流

    • float current = Read_Current(&hadc1, R_SHUNT); 调用测量电流的函数,获取当前电流值。

通过这个实现,可以有效地使用 ADC 采集电流,并根据 ADC 值计算出实际电流值。


ADC - 温度

使用查表法计算温度是一种常见且简单的方法,特别适合在微控制器上实现,因为它不需要复杂的数学运算。以下是具体步骤和实现方法:

步骤概述

  1. 建立查表:根据 NTC 热敏电阻的特性曲线,创建一个电阻值与温度对应的查表(数组)。
  2. 读取 ADC 值:使用 ADC 读取分压电路的电压。
  3. 计算 NTC 电阻值:根据分压公式计算 NTC 热敏电阻的电阻值。
  4. 查找温度:在查表中找到对应的温度值。

具体步骤

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)
{// 时钟配置代码
}

详细说明

  1. 定义常量和查表

    • #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 定义查表的大小。
  2. 读取 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 热敏电阻的电阻值。
  3. 查找温度值

    • 遍历查表,通过线性插值计算电阻值与温度值之间的关系,找到对应的温度。

通过这个实现,可以有效地使用 ADC 采集 NTC 热敏电阻的电阻值,并通过查表法将其转换为温度。线性插值可以提高温度计算的精度。查表数据可以根据具体的 NTC 热敏电阻特性曲线来建立。

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

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

相关文章

PLM系统:PLM系统如何重塑产品生命周期管理

PLM系统&#xff1a;重塑产品生命周期管理的未来 在当今快速变化的商业环境中&#xff0c;产品生命周期管理&#xff08;PLM&#xff09;系统正逐渐成为企业提升竞争力、加速创新并优化运营流程的关键工具。随着技术的不断进步和市场需求的日益复杂化&#xff0c;传统的手动或…

直播预告 | VMware大规模迁移实战,HyperMotion助力业务高效迁移

2006年核高基专项启动&#xff0c;2022年国家79号文件要求2027年央国企100%完成信创改造……国家一系列信创改造政策的推动&#xff0c;让服务器虚拟化软件巨头VMware在中国的市场份额迅速缩水。 加之VMware永久授权的取消和部分软件组件销售策略的变更&#xff0c;导致VMware…

昇思25天学习打卡营第16天|文本解码原理——以MindNLP为例

在大模型中&#xff0c;文本解码通常是指在自然语言处理&#xff08;NLP&#xff09;任务中使用的大型神经网络模型&#xff08;如Transformer架构的模型&#xff09;将编码后的文本数据转换回可读的原始文本的过程。这些模型在处理自然语言时&#xff0c;首先将输入文本&#…

长沙(市场调研公司)源点 企业如何决定是否需要开展市场调研?

长沙源点调研咨询认为&#xff1a;对于一个特定问题&#xff0c;管理者在面临几种解决问题的方案时&#xff0c;不应该凭直觉草率开展应用性市场调研。事实上&#xff0c;首先需要做的决策是是否需要开展调研。在下述情况下&#xff0c;最好不要做调研&#xff1a; *缺乏资源。…

重大更新来袭!!《植物大战僵尸杂交版V2.1+修改器+融合版》

大家好&#xff01;每个软件更新总是令人兴奋不已。前段时间介绍的《植物大战僵尸》系列以其独特的策略玩法和丰富的植物角色&#xff0c;赢得了很多玩家的喜爱。而在今天&#xff0c;这款经典游戏全网最新版本——《植物大战僵尸&#xff1a;杂交版V2.1》正式推出&#xff0c;…

ASUS/华硕枪神4 G532L G732L系列 原厂win10系统 工厂文件 带F12 ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;Windows10 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…

【Redis】真行,原来是这样啊! --Redis自动序列化和手动序列化的区别(存储结构、内存开销,实际写法)

对于Redis有两种序列化和反序列化的方式&#xff0c; 方式一&#xff1a; 一种是通过 注入RedisTemplate 对象&#xff0c;找个对象&#xff0c;通过配置类进行一定的配置&#xff0c;使得使用RedisTemplate 对象时&#xff0c;便会使用配置的那些键、值的序列化方式&#xff…

有哪些有效的策略可以提升独立站的外链数量?

有哪些有效的策略可以提升独立站的外链数量&#xff1f;提升独立站的外链数量并不难&#xff0c;难得是不被谷歌惩罚把你的网站判定为作弊&#xff0c;正因如此&#xff0c;了解并应用GNB自然外链策略是个不错的开始&#xff0c;GNB外链的核心价值在于它提高了网站外链资源的自…

opencv概念以及安装方法

#opencv相关概念介绍 Open Source Computer Vision Library 缩写 opencv 翻译&#xff1a;开源的计算机视觉库 &#xff0c;英特尔公司发起并开发&#xff0c;支持多种编程语言&#xff08;如C、Python、Java等&#xff09;&#xff0c;支持计算机视觉和机器学习等众多算法&a…

Android车载开发中调试app与bat结合的丝滑小妙招

项目场景&#xff1a; 做Android车载的小伙伴调试app的时候常年就是手动adb命令三连&#xff0c;例如我常用的adb推送apk的命令 adb root adb remount adb push D:\workspace_atc\XSP3-10A\AutoSystemUIPlugin\app\release\CarSystemUI.apk /system/priv-app/CarSystemUI …

【qt】如何获取网卡的IP地址?

网卡相当于是一个翻译官,可以将数据转换成网络信号. 同时也可以将网络信号转换成数据. 我们要用到网卡类QNetmorkInterface 我们获取网卡的所有地址用静态函数allAddresses() 返回的还是一个QhostAddress的容器. QList<QHostAddress> addrList QNetworkInterface::allA…

相关技术 太阳能热水器循环水泵制作技术

网盘 https://pan.baidu.com/s/1oAKwUEGkKnEgxE-F4znKow?pwdidxd 双温区蓄能供热型太阳能热水系统及其工作方法.pdf 双罐叠压节能恒温型太阳能热水机组.pdf 基于傅科电流的循环式风能热水器.pdf 基于太阳能利用的建筑冷热电联产系统及工作方法.pdf 基于太阳能和热泵的双蓄式热…

剪画小程序:自媒体工具推荐:视频文案提取!

各位小伙伴&#xff0c;你们好啊&#xff01; 上周五观看《歌手 2024》第八期时&#xff0c;我再次被何炅老师幽默风趣的主持风格所折服。他的每一句话都仿佛带着魔力&#xff0c;让现场气氛热烈非凡&#xff0c;实在令人羡慕不已&#xff01; 何炅老师的口才之所以如此出色&a…

7月6日 VueConf 技术大会即将在深圳举办

7月6日&#xff0c;VueConf 2024 即将在深圳召开&#xff0c;本次大会正值 Vue.js 十周年&#xff0c;旨在聚焦 Vue.js 社区的成员&#xff0c;分享最新的技术动态、经验以及创新实践。 本次参与 VueConf 大会的是来自全球 Vue.js 核心团队成员、行业专家及前端开发者。其中&a…

# Sharding-JDBC从入门到精通(7)- Sharding-JDBC 公共表 与 读写分离

Sharding-JDBC从入门到精通&#xff08;7&#xff09;- Sharding-JDBC 公共表 与 读写分离 一、Sharding-JDBC 公共表 1、公共表 公共表属于系统中数据量较小&#xff0c;变动少&#xff0c;而且属于高频联合查询的依赖表。参数表、数据字典表等属于此类型。可以将这类表在每…

芯片基识 | 掰开揉碎讲 FIFO(同步FIFO和异步FIFO)

文章目录 一、什么是FIFO二、为什么要用FIFO三、什么时候用FIFO四、FIFO分类五、同步FIFO1. 同步FIFO电路框图2. 同步FIFO空满判断3. 同步FIFO设计代码4. 同步FIFO仿真结果 六、异步FIFO1、异步FIFO的电路框图2 、亚稳态3、打两拍4、格雷码5、如何判断异步FIFO的空满&#xff0…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——4.预后相关外泌体基因确定单因素cox回归(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…

全端面试题15(canvas)

在前端开发领域&#xff0c;<canvas> 元素和相关的 API 是面试中经常被提及的主题。下面是一些常见的关于 HTML5 Canvas 的面试问题及解答示例&#xff1a; 1. 什么是 <canvas> 元素&#xff1f; <canvas> 是 HTML5 引入的一个用于图形渲染的标签。它本身并…

win11如何关闭自动更新,延长暂停更新时间

网上有很多关闭自动更新的方法&#xff0c;今天给大家带来另一种关闭win11自动更新的方法。 1.winR打开运行窗口&#xff0c;输入regedit打开注册表 2.定位到以下位置&#xff1a; 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 3.右键右边空白&…

昇思25天学习打卡营第13天|linchenfengxue

Diffusion扩散模型 关于扩散模型&#xff08;Diffusion Models&#xff09;有很多种理解&#xff0c;本文的介绍是基于denoising diffusion probabilistic model &#xff08;DDPM&#xff09;&#xff0c;DDPM已经在&#xff08;无&#xff09;条件图像/音频/视频生成领域取得…