采用stm32f103c8t6芯片的A0引脚连接热敏电阻模块。采用ADC1的通道0,使用标准库实现。
实现功能所使用的常量为:
//温度传感器参数
#define VREF 3.3f // ADC参考电压 3.3V
#define R1 10000.0f // 电压分压阻值为10KΩ,与热敏电阻串联,用于计算热敏电阻的阻值
#define B 3950.0f // 热敏电阻的 B 常数,单位为开尔文(K),用于计算热敏电阻的温度
#define T0 298.15f // 参考温度,单位为开尔文(K),298.15K即 25°C
#define R0 10000.0f // 热敏电阻在参考温度 T0 下的阻值,用于计算热敏电阻在当前温度下的阻值
// DMA内存存入位置
#define ADC_BUFFER_SIZE 10
uint16_t ADC_ConvertedValue[ADC_BUFFER_SIZE];
GPIO引脚配置:
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置PA0为模拟输入用于温度传感器
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
ADC配置:
void ADC_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
// 使能ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 配置 ADC1 的基本参数
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //配置为独立模式因芯片只有ADC1
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //启用连续扫描,这里因只开一个通道,是否启用连续扫描都不影响结果
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //启用连续转换,每次读入模拟量就进行转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不适用外部触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据选择右对齐,转换结果低12位有效
ADC_InitStructure.ADC_NbrOfChannel = 1; //转换通道数设置为1
ADC_Init(ADC1, &ADC_InitStructure);
// 配置 ADC1 的通道,设置通道在规则组中的转换顺序为第 1 个,设置采样时间为 239.5 个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
// 启用 ADC1 的 DMA 功能,可以通过DMA实现高速转换
ADC_DMACmd(ADC1, ENABLE);
// 启用 ADC1
ADC_Cmd(ADC1, ENABLE);
// 校准 ADC1,消除 ADC 的偏移误差
ADC_ResetCalibration(ADC1); //复位 ADC1 的校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准完成,完成置0
ADC_StartCalibration(ADC1); //启动 ADC1 的校准
while(ADC_GetCalibrationStatus(ADC1)); //等待完成
// 启动 ADC1 转换,使用软件触发转换功能,将结果通过DMA传输刀内存中
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
DMA模块实现:
void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
// 使能 DMA1 时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 配置 DMA1 通道1
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //配置DMA读取数据位置为ADC1的DR寄存器中(每次通过A0引脚读入的值都放在DR中所以DMA通过DR读数据)
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue; //配置DMA内存地址为ADC_ConvertedValue,即上方常量的地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向为外设到内存
DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE; //缓冲区大小为每次DMA传输的数据量,即上方常量中的值
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //禁用外设地址自增,ADC 数据寄存器地址是固定的
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //启用内存地址自增,数组的每个元素地址是连续的
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //设置外设数据大小为半字(16位)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //设置内存数据大小为半字(16位),与 ADC 数据寄存器一致
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //设置 DMA 模式为循环模式,即 DMA 传输完成后会自动重新开始
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //设置 DMA 通道的优先级为高(因为就一个通道,不设置也没关系)
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁用内存到内存的传输模式
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// 使能 DMA1 通道1
DMA_Cmd(DMA1_Channel1, ENABLE);
}
将模拟量转换为温度:
float GetTemperature(void)
{
uint32_t sum = 0;
uint8_t i;
float voltage, rt, temperature;
uint16_t adc_value;
// 计算10个样本的平均值 ADC_BUFFER_SIZE为10
for(i = 0; i < ADC_BUFFER_SIZE; i++) {
sum += ADC_ConvertedValue[i];
}
adc_value = sum / ADC_BUFFER_SIZE;
// 将ADC值转换为电压 12位为4096
voltage = (float)adc_value * VREF / 4096.0f;
// 计算热敏电阻阻值
rt = R1 * voltage / (VREF - voltage);
// 使用Steinhart-Hart公式计算温度
temperature = 1.0f / (1.0f/T0 + log(rt/R0)/B) - 273.15f;
return temperature;
}
温度T的计算公式:
$$
T=\frac{1}{\frac{1}{T_0}+\frac{\frac{ln(R_t)}{R_0}}{B}}
$$
公式中的参数
- T:参考温度,通常为 25°C(即 298.15K)。
- R0:热敏电阻在参考温度 T0T0 下的阻值,通常为 10kΩ。
- Rt:热敏电阻在当前温度下的阻值,通过分压电路和 ADC 测量得到。
- B:热敏电阻的 B 常数,通常由热敏电阻的规格书提供,例如 3950。
通过以上的函数调用即可实现热敏电阻读取温度,最后在main函数中使用OLED显示温度:
int main(void)
{
// 初始化
GPIO_Config();
DMA_Config();
ADC_Config();
// 初始化 OLED(OLED的代码需要自行去搜索获取)
OLED_Init();
OLED_Clear();
while(1)
{
uint32_t i;
// 获取温度
float temperature = GetTemperature();
char temp_str[16];
sprintf(temp_str, "Temp:%.1fC", temperature); //格式化字符串
OLED_ShowString(2, 1, temp_str); //OLED显示温度
}
}