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 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 低通濾波器)。
原理
指數加權移動平均濾波器的基本原理是對當前采樣值和先前的濾波值進行加權平均。公式如下:
具體實現步驟
-
定義變量:
- 濾波系數 (\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 熱敏電阻特性曲線來建立。