STM32入門筆記(03): ADC(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 庫實現低通濾波器:

#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)
{// 時鐘配置代碼
}

詳細說明

  1. 定義濾波系數和變量

    • #define ALPHA 0.1 定義濾波系數,決定新輸入值的權重。
    • float previous_filtered_value = 0; 初始化上一次的濾波輸出值。
  2. 讀取 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; 應用低通濾波公式計算當前濾波值。
  3. 在主循環中持續濾波

    • previous_filtered_value = Read_ADC_LowPass(&hadc1, previous_filtered_value); 調用濾波函數,更新濾波值。
  4. 計算和使用濾波后的電壓值

    • float V_out = (previous_filtered_value / 4095.0) * V_REF; 計算分壓電路的輸出電壓。
    • float V_in = V_out * (R1 + R2) / R2; 反向計算原始輸入電壓。

通過這個實現,可以有效地平滑 ADC 讀取值的抖動,提高測量的穩定性。


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.pswp.cn/web/38994.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/38994.shtml
英文地址,請注明出處:http://en.pswp.cn/web/38994.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何學好自動化測試

1. 什么是自動化測試 自動化測試是使用腳本和工具來執行測試任務&#xff0c;以替代手工測試過程。它可以提高效率、減少人工錯誤&#xff0c;并增加測試覆蓋率。在軟件開發過程中&#xff0c;自動化測試已經成為了不可或缺的一部分。 自動化測試主要有以下好處&#xff1a; …

Amos結構方程模型---探索性分析

初級 第5講 探索性分析_嗶哩嗶哩_bilibili amos中基本操作&#xff1a; 橢圓潛變量&#xff0c;不可預測 數據導入 改變形狀 判定系數 方差估計和假設檢驗&#xff1a; 探索性分析&#xff1a; ses&#xff08;潛變量&#xff09;社會經濟指數 從考慮最大的MI開始&#xff0c;卡…

【Python畫圖-馴化seaborn】一文搞懂seaborn中的箱線圖實踐技巧

【Python畫圖-馴化seaborn】一文搞懂seaborn中的箱線圖實踐技巧 本次修煉方法請往下查看 &#x1f308; 歡迎蒞臨我的個人主頁 &#x1f448;這里是我工作、學習、實踐 IT領域、真誠分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免費獲取相關內容文檔關注&a…

2736 卡片重排

題目描述 Description 可可共有兩種卡片&#xff0c;一種卡片是數字0-9編號&#xff0c;一種卡片是字母A-Z編號&#xff0c;現在兩種卡片混在一起&#xff0c;可可想將它們歸類擺放&#xff0c;但是要求同類卡片中&#xff0c;它們相對位置不可以改變&#xff0c;原先在前的仍…

python把項目編譯成so文件

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、使用步驟1.引入庫 總結 前言 提示 例如&#xff1a; 提示&#xff1a;以下是本篇文章正文內容&#xff0c;下面案例可供參考 一、使用步驟 1.引入庫 代…

C語言兩個較大數字相加

C語言兩個較大數字相加 思路分析 由于C語言中的基本數據類型&#xff08;如int、long等&#xff09;有固定的大小&#xff0c;無法直接處理非常大的數字&#xff08;如數百位的數字&#xff09;。因此&#xff0c;我們需要采用字符串或數組來表示大數字&#xff0c;并逐位進行…

1.4 ROS2集成開發環境搭建

1.4.1 安裝VSCode VSCode全稱Visual Studio Code&#xff0c;是微軟推出的一款輕量級代碼編輯器&#xff0c;免費、開源而且功能強大。它支持幾乎所有主流的程序語言的語法高亮、智能代碼補全、自定義熱鍵、括號匹配、代碼片段、代碼對比Diff、GIT 等特性&#xff0c;支持插件…

導入第三方包Could not find a package configuration file provided by “demo msgs“ with

報錯代碼 Could not find a package configuration file provided by "demo msgs" with any of the following names: demo msgsConfig.cmake demo msgs-config.cmake Add the installation prefix of "demo msgs" tO CMAKE PREFIX PATH or set "dem…

7.3數據庫第一次作業

安裝MySQL 1.打開安裝包 2.選擇自定義安裝&#xff08;custom&#xff09;并點擊下一步 3.自定義安裝路徑 4.點擊執行 5.執行成功 6.默認選項點擊下一步 7.選擇新的授權方式并點擊下一步 8.配置密碼 9.默認配置并點擊下一步 10.點擊執行&#xff08;Execute&#xff09; 11.執…

python中的文件

1.什么是文件&#xff1f; 硬盤上存儲的數據都是以文件的形式來組織的~ 文件是數據在硬盤上的存儲形式&#xff0c;不同的數據在硬盤上的存儲形式是不同的&#xff0c; 2.文件路徑 文件夾/目錄。 文件夾&#xff0c;再包含文件夾的情況&#xff0c;這就是一個嵌套的關系&…

2024-2025年本田維修電路圖線路圖接線圖資料更新

此次更新了2024-2025年本田車系電路圖資料&#xff0c;覆蓋市面上99%車型&#xff0c;包括維修手冊、電路圖、新車特征、車身鈑金維修數據、全車拆裝、扭力、發動機大修、發動機正時、保養、電路圖、針腳定義、模塊傳感器、保險絲盒圖解對照表位置等等&#xff01; 汽修幫手汽…

Java中使用arima預測未來數據

看著已經存在的曲線圖數據&#xff0c;想預估下后面曲線圖的數據。 import java.util.Vector;public class AR {double[] stdoriginalData{};int p;ARMAMath armamathnew ARMAMath();/*** AR模型* param stdoriginalData* param p //p為MA模型階數*/public AR(double [] stdori…

你的硬盤知道的太多:你以為你的秘密真的被刪除了嗎?

某一天你收到了朋友發給你的一個秘密文件&#xff0c;在看完之后&#xff0c;為了不被別人發現&#xff0c;你決定將文件毀尸滅跡&#xff01; 你選中文件名稱 / 右鍵 / 刪除&#xff0c;好了&#xff0c;文件已經消失了。但你是懂電腦的&#xff0c;知道文件此時還在回收站里面…

Ozon/Noon/Temu/TK Shop如何多店鋪經營免受關聯封號?

許多商家和個人都面臨著多店鋪經營免受關聯封號的挑戰。特別是在像Ozon、Noon、TEMU以及TikTok Shop等平臺上&#xff0c;如何有效管理多個店鋪并避免關聯封號成為關鍵問題。 一、多店鋪經營防關聯封號的重要性&#xff1a; 在多店鋪經營過程中&#xff0c;如果平臺檢測到多個…

海外虛擬卡開卡平臺有哪些?無限開卡,無其他限制

隨著時代的發展很多小伙伴都需要海外虛擬卡&#xff0c;海外虛擬卡開卡平臺我這里用的是Fomepay的&#xff0c;他們比較人性化&#xff0c;有客服&#xff0c;隨時可咨詢 對于消費者而言&#xff0c;虛擬卡號提供了隱私&#xff0c;因此廣告商更難以跟蹤和定位購買行為&#x…

PyQt5入門教程:從安裝到構建簡單應用

PyQt5入門教程&#xff1a;從安裝到構建簡單應用 簡介 PyQt5是一個功能強大的Python綁定庫&#xff0c;用于Qt應用程序框架。它允許我們使用Python語言快速開發跨平臺的桌面應用程序。本教程將引導你完成PyQt5的安裝、配置&#xff0c;并帶你創建一個簡單的圖形用戶界面&…

《python程序語言設計》2018版第5章第50題利用turtle編程顯示三角形圖案

2024.06.18 05.50.01version 首先我覺得還是應該現從簡單陣列來進行。非常簡單。順便回憶一下我3月份做的5.19題里那些淘氣的數列 代碼成功 #將i從10設計成12打印的畢竟好看 for i in range(1,12):#這這里給結尾的i2效果并不好看for j in range(1,i):print(j,end" "…

【深度學習】Transformer

李宏毅深度學習筆記 https://blog.csdn.net/Tink1995/article/details/105080033 https://blog.csdn.net/leonardotu/article/details/135726696 https://blog.csdn.net/u012856866/article/details/129790077 Transformer 是一個基于自注意力的序列到序列模型&#xff0c;與基…

軟件測試與質量保證 | 云班課選擇題庫

目錄 第1章課后習題 第2章課后習題 第3章課后習題 第4章課后習題 第5章課后習題 第6章課后習題 第7章課后習題 第8章課后習題 第9章課后習題 第10章課后習題 第11章課后習題 第12章課后習題 第13章 測試相關未分類習題 第1章課后習題 1. 與質量相關的概念包括 &a…

51單片機第26步_單片機工作在空閑模式

重點學習51單片機工作在空閑模式。 1、進入空閑模式的方法 通過將PCON寄存器中的IDLE置1&#xff0c;CPU就會進入空閑模式。在空閑模式中&#xff0c;程序停止執行&#xff0c;RAM中的數據仍然保持&#xff0c;晶振繼續工作&#xff0c;但與CPU斷開&#xff0c;定時器和串行口…