大家好,我是专注分享职业规划/技术科普/智能生活有关原创文章的allen康哥。嵌入式开发其实某种意义上来说就是传感器的使用,所以今天就总结分享7种常用的传感器应用场景以及单片机例程,仅供参看。
1.温湿度传感器 DHT11
应用场景
农业大棚:环境监控系统(阈值触发通风/喷淋)
冷链运输:药品/食品运输箱温湿度记录
STM32例程
#include "main.h"
#include <stdio.h>
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_PIN_0
TIM_HandleTypeDef htim2; // 需要配置一个基本定时器
// 微秒延时函数(需要配置一个基本定时器,比如TIM2)
void Delay_us(uint16_t us)
{
__HAL_TIM_SET_COUNTER(&htim2, 0);
HAL_TIM_Base_Start(&htim2);
while(__HAL_TIM_GET_COUNTER(&htim2) < us);
HAL_TIM_Base_Stop(&htim2);
}
// 初始化DHT11
void DHT11_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); // 拉高总线
}
// 读取DHT11数据
uint8_t DHT11_Read(float *temp, float *humi)
{
uint8_t data[5] = {0};
uint8_t retry = 0;
// 发送开始信号
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET);
HAL_Delay(18); // 拉低至少18ms
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET);
// 切换为输入模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
// 等待DHT11响应
Delay_us(40); // 等待20-40us
if(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_RESET)
{
// 等待80us低电平
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_RESET && retry < 100)
{
retry++;
Delay_us(1);
}
retry = 0;
// 等待80us高电平
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET && retry < 100)
{
retry++;
Delay_us(1);
}
// 开始接收数据
for(uint8_t i=0; i<40; i++)
{
retry = 0;
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_RESET && retry < 100)
{
retry++;
Delay_us(1);
}
uint32_t time = 0;
retry = 0;
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET && retry < 100)
{
retry++;
Delay_us(1);
time++;
}
data[i/8] <<= 1;
if(time > 30) // 高电平持续时间大于30us表示'1'
{
data[i/8] |= 1;
}
}
// 校验数据
if(data[4] == (data[0] + data[1] + data[2] + data[3]))
{
*humi = data[0] + data[1] * 0.1;
*temp = data[2] + data[3] * 0.1;
return0; // 成功
}
}
return1; // 失败
}
// 主函数示例
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_TIM2_Init(); // 初始化定时器
DHT11_Init();
float temperature, humidity;
while(1)
{
if(DHT11_Read(&temperature, &humidity) == 0)
{
printf("Temperature: %.1f Crn", temperature);
printf("Humidity: %.1f %%rn", humidity);
}
else
{
printf("Sensor read failed!rn");
}
HAL_Delay(2000); // 2秒读取一次
}
}
2.温度传感器 DS18B20
应用场景
医疗设备:血液存储箱温度监测
STM32例程(单总线协议)
#include "main.h"
#include <stdio.h>
#define DS18B20_PORT GPIOA
#define DS18B20_PIN GPIO_PIN_0
TIM_HandleTypeDef htim2; // 用于微秒延时的基础定时器
/* 微秒延时函数(需要配置定时器,例如TIM2) */
void Delay_us(uint16_t us)
{
__HAL_TIM_SET_COUNTER(&htim2, 0);
HAL_TIM_Base_Start(&htim2);
while(__HAL_TIM_GET_COUNTER(&htim2) < us);
HAL_TIM_Base_Stop(&htim2);
}
/* 单总线初始化 */
uint8_t DS18B20_Reset(void)
{
uint8_t status = 0;
// 配置为输出模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DS18B20_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
// 拉低总线480us
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET);
Delay_us(480);
// 释放总线,切换为输入模式
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET);
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
// 检测存在脉冲(60-240us低电平)
Delay_us(60);
if(!HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN)) status = 1;
Delay_us(240);
return status; // 返回1表示检测到设备
}
/* 写入1位数据 */
void DS18B20_WriteBit(uint8_t bit)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DS18B20_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET);
if(bit) {
Delay_us(5); // 1-15us低电平表示写1
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET);
Delay_us(60);
} else {
Delay_us(60); // 60us低电平表示写0
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET);
Delay_us(5);
}
}
/* 写入1字节数据 */
void DS18B20_WriteByte(uint8_t data)
{
for(uint8_t i=0; i<8; i++){
DS18B20_WriteBit(data & 0x01);
data >>= 1;
}
}
/* 读取1位数据 */
uint8_t DS18B20_ReadBit(void)
{
uint8_t bit = 0;
// 配置为输出模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DS18B20_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
// 产生读时隙
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET);
Delay_us(2);
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET);
// 切换为输入模式
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
Delay_us(10);
bit = HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN);
Delay_us(50);
return bit;
}
/* 读取1字节数据 */
uint8_t DS18B20_ReadByte(void)
{
uint8_t data = 0;
for(uint8_t i=0; i<8; i++){
data >>= 1;
if(DS18B20_ReadBit()) data |= 0x80;
}
return data;
}
/* 读取温度值 */
float DS18B20_ReadTemp(void)
{
if(DS18B20_Reset() == 0) return-999; // 设备未响应
DS18B20_WriteByte(0xCC); // 跳过ROM指令
DS18B20_WriteByte(0x44); // 启动温度转换
Delay_us(800000); // 等待转换完成(最大750ms)
if(DS18B20_Reset() == 0) return-999;
DS18B20_WriteByte(0xCC); // 跳过ROM指令
DS18B20_WriteByte(0xBE); // 读取暂存器
uint8_t temp_l = DS18B20_ReadByte();
uint8_t temp_h = DS18B20_ReadByte();
int16_t temp = (temp_h << 8) | temp_l;
return temp * 0.0625; // 12位精度时的分辨率
}
/* 主函数示例 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_TIM2_Init(); // 初始化定时器
while(1)
{
float temperature = DS18B20_ReadTemp();
if(temperature != -999) {
printf("Temperature: %.3f Crn", temperature);
} else {
printf("Sensor Error!rn");
}
HAL_Delay(1000); // 1秒读取一次
}
}
3.加速度传感器 MPU6050
应用场景
无人机飞控:姿态解算(大疆飞控IMU模块)
工业机器人:振动监测(库卡机械臂)
#include "main.h"
#include <stdio.h>
#include <math.h>
#define MPU6050_ADDR 0xD0 // I2C地址(AD0接地时为0x68,左移1位后为0xD0)
#define SMPLRT_DIV 0x19 // 采样率分频
#define CONFIG 0x1A // 配置寄存器
#define GYRO_CONFIG 0x1B // 陀螺仪配置
#define ACCEL_CONFIG 0x1C // 加速度计配置
#define PWR_MGMT_1 0x6B // 电源管理1
#define WHO_AM_I 0x75 // 设备ID寄存器
I2C_HandleTypeDef hi2c1; // 需要配置I2C外设
// 校准参数结构体
typedefstruct {
int16_t x_offset;
int16_t y_offset;
int16_t z_offset;
} MPU6050_Calib;
// 原始数据结构体
typedefstruct {
int16_t accel_x;
int16_t accel_y;
int16_t accel_z;
int16_t temp;
int16_t gyro_x;
int16_t gyro_y;
int16_t gyro_z;
} MPU6050_RawData;
// 初始化MPU6050
uint8_t MPU6050_Init(void)
{
uint8_t check;
HAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, WHO_AM_I, 1, &check, 1, 100);
if(check != 0x68) return1; // 检测设备ID
// 唤醒设备,选择时钟源
uint8_t data = 0x00;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, PWR_MGMT_1, 1, &data, 1, 100);
// 设置采样率分频(1kHz/(1+SMPLRT_DIV))
data = 0x07; // 125Hz
HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, SMPLRT_DIV, 1, &data, 1, 100);
// 设置低通滤波器带宽
data = 0x06; // 5Hz
HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, CONFIG, 1, &data, 1, 100);
// 设置加速度计量程 ±4g
data = 0x08; // ±4g (8192 LSB/g)
HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, ACCEL_CONFIG, 1, &data, 1, 100);
// 设置陀螺仪量程 ±500°/s
data = 0x08; // ±500°/s (65.5 LSB/°/s)
HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, GYRO_CONFIG, 1, &data, 1, 100);
return0;
}
// 读取原始数据
void MPU6050_ReadRaw(MPU6050_RawData *data)
{
uint8_t buffer[14];
HAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, 0x3B, 1, buffer, 14, 100);
data->accel_x = (buffer[0] <<8) | buffer[1];
data->accel_y = (buffer[2] <<8) | buffer[3];
data->accel_z = (buffer[4] <<8) | buffer[5];
data->temp = (buffer[6] <<8) | buffer[7];
data->gyro_x = (buffer[8] <<8) | buffer[9];
data->gyro_y = (buffer[10]<<8) | buffer[11];
data->gyro_z = (buffer[12]<<8) | buffer[12];
}
// 自动校准(传感器需保持水平静止)
void MPU6050_Calibrate(MPU6050_Calib *calib, uint32_t sample_num)
{
int32_t sum_gx = 0, sum_gy = 0, sum_gz = 0;
for(uint32_t i=0; i<sample_num; i++){
MPU6050_RawData data;
MPU6050_ReadRaw(&data);
sum_gx += data.gyro_x;
sum_gy += data.gyro_y;
sum_gz += data.gyro_z;
HAL_Delay(10);
}
calib->x_offset = sum_gx / sample_num;
calib->y_offset = sum_gy / sample_num;
calib->z_offset = sum_gz / sample_num;
}
// 姿态解算(互补滤波)
void MPU6050_GetAngle(float *pitch, float *roll, MPU6050_RawData *raw, float dt)
{
staticfloat angle_pitch = 0, angle_roll = 0;
staticfloat gyro_x_bias = 0, gyro_y_bias = 0;
// 加速度计角度计算
float accel_pitch = atan2(raw->accel_y, sqrt(raw->accel_x*raw->accel_x + raw->accel_z*raw->accel_z)) * 180/M_PI;
float accel_roll = atan2(-raw->accel_x, sqrt(raw->accel_y*raw->accel_y + raw->accel_z*raw->accel_z)) * 180/M_PI;
// 陀螺仪角速度(考虑校准偏移)
float gyro_x = (raw->gyro_x - gyro_x_bias) / 65.5; // ±500dps时灵敏度65.5 LSB/°/s
float gyro_y = (raw->gyro_y - gyro_y_bias) / 65.5;
// 互补滤波(系数0.96)
angle_pitch = 0.96*(angle_pitch + gyro_x*dt) + 0.04*accel_pitch;
angle_roll = 0.96*(angle_roll + gyro_y*dt) + 0.04*accel_roll;
*pitch = angle_pitch;
*roll = angle_roll;
}
// 主函数示例
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C1_Init(); // 初始化I2C
if(MPU6050_Init()) {
printf("MPU6050 Init Failed!rn");
while(1);
}
MPU6050_Calib calib;
MPU6050_Calibrate(&calib, 200); // 采集200个样本校准
MPU6050_RawData raw;
float pitch, roll;
uint32_t last_tick = HAL_GetTick();
while(1) {
// 读取原始数据并应用校准
MPU6050_ReadRaw(&raw);
raw.gyro_x -= calib.x_offset;
raw.gyro_y -= calib.y_offset;
raw.gyro_z -= calib.z_offset;
// 计算时间差(单位:秒)
uint32_t current_tick = HAL_GetTick();
float dt = (current_tick - last_tick) * 0.001f;
last_tick = current_tick;
// 姿态解算
MPU6050_GetAngle(&pitch, &roll, &raw, dt);
// 输出结果
printf("Pitch: %.2f° Roll: %.2f°rn", pitch, roll);
HAL_Delay(50); // 控制输出频率
}
}
4.压力传感器 BMP280
应用场景
气象设备:便携式气压计(如Garmin手持GPS)
无人机:高度计(大疆Phantom系列)
STM32例程
#include "main.h"
#include <stdio.h>
#include <math.h>
#define BMP280_I2C_ADDR 0x76 // 0x77 if SDO high
#define BMP280_CALIB_SIZE 24
I2C_HandleTypeDef hi2c1; // 需要配置I2C外设
// 校准参数结构体
typedefstruct {
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
} BMP280_Calib;
// 传感器配置结构体
typedefstruct {
uint8_t ctrl_meas;
uint8_t config;
} BMP280_Config;
// 初始化BMP280
uint8_t BMP280_Init(BMP280_Calib *calib, BMP280_Config *conf)
{
// 检测设备ID
uint8_t id;
HAL_I2C_Mem_Read(&hi2c1, BMP280_I2C_ADDR, 0xD0, 1, &id, 1, 100);
if(id != 0x58) return1;
// 读取校准参数
uint8_t calib_data[BMP280_CALIB_SIZE];
HAL_I2C_Mem_Read(&hi2c1, BMP280_I2C_ADDR, 0x88, 1, calib_data, BMP280_CALIB_SIZE, 100);
calib->dig_T1 = (calib_data[1] <<8) | calib_data[0];
calib->dig_T2 = (calib_data[3] <<8) | calib_data[2];
calib->dig_T3 = (calib_data[5] <<8) | calib_data[4];
calib->dig_P1 = (calib_data[7] <<8) | calib_data[6];
calib->dig_P2 = (calib_data[9] <<8) | calib_data[8];
calib->dig_P3 = (calib_data[11]<<8) | calib_data[10];
calib->dig_P4 = (calib_data[13]<<8) | calib_data[12];
calib->dig_P5 = (calib_data[15]<<8) | calib_data[14];
calib->dig_P6 = (calib_data[17]<<8) | calib_data[16];
calib->dig_P7 = (calib_data[19]<<8) | calib_data[18];
calib->dig_P8 = (calib_data[21]<<8) | calib_data[20];
calib->dig_P9 = (calib_data[23]<<8) | calib_data[22];
// 写入配置寄存器
HAL_I2C_Mem_Write(&hi2c1, BMP280_I2C_ADDR, 0xF4, 1, &conf->ctrl_meas, 1, 100);
HAL_I2C_Mem_Write(&hi2c1, BMP280_I2C_ADDR, 0xF5, 1, &conf->config, 1, 100);
return0;
}
// 读取原始数据
void BMP280_ReadRaw(int32_t *temp_raw, int32_t *press_raw)
{
uint8_t data[6];
HAL_I2C_Mem_Read(&hi2c1, BMP280_I2C_ADDR, 0xF7, 1, data, 6, 100);
*press_raw = (int32_t)((data[0] <<12) | (data[1] <<4) | (data[2] >>4));
*temp_raw = (int32_t)((data[3] <<12) | (data[4] <<4) | (data[5] >>4));
}
// 温度补偿计算
float BMP280_CompensateTemp(int32_t temp_raw, BMP280_Calib *calib)
{
int32_t var1, var2, T;
var1 = ((((temp_raw >>3) - ((int32_t)calib->dig_T1 <<1))) *
((int32_t)calib->dig_T2)) >>11;
var2 = (((((temp_raw >>4) - ((int32_t)calib->dig_T1)) *
((temp_raw >>4) - ((int32_t)calib->dig_T1))) >>12) *
((int32_t)calib->dig_T3)) >>14;
T = var1 + var2;
return (T * 5 + 128) / 256.0f / 100.0f; // 返回摄氏度
}
// 压力补偿计算
float BMP280_CompensatePress(int32_t press_raw, BMP280_Calib *calib, float t_fine)
{
int64_t var1, var2, p;
var1 = ((int64_t)t_fine) - 128000;
var2 = var1 * var1 * (int64_t)calib->dig_P6;
var2 = var2 + ((var1 * (int64_t)calib->dig_P5) <<17);
var2 = var2 + (((int64_t)calib->dig_P4) <<35);
var1 = ((var1 * var1 * (int64_t)calib->dig_P3) >>8) +
((var1 * (int64_t)calib->dig_P2) <<12);
var1 = (((((int64_t)1) <<47) + var1)) * ((int64_t)calib->dig_P1) >>33;
if(var1 == 0) return0;
p = 1048576 - press_raw;
p = (((p <<31) - var2) * 3125) / var1;
var1 = (((int64_t)calib->dig_P9) * (p >>13) * (p >>13)) >>25;
var2 = (((int64_t)calib->dig_P8) * p) >>19;
p = ((p + var1 + var2) >>8) + (((int64_t)calib->dig_P7) <<4);
return (float)p / 256.0f; // 返回Pa
}
// 计算海拔高度(国际标准大气模型)
float BMP280_CalcAltitude(float pressure, float sea_level_hpa=1013.25f)
{
return44330.0f * (1.0f - powf(pressure / 100.0f / sea_level_hpa, 0.1903f));
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C1_Init();
// 配置参数:温度x2采样,压力x16采样,正常模式,滤波器系数16
BMP280_Config conf = {
.ctrl_meas = 0b01010111, // osrs_t=x2, osrs_p=x16, mode=normal
.config = 0b00010100// t_sb=0.5ms, filter=16, spi3w_en=0
};
BMP280_Calib calib;
if(BMP280_Init(&calib, &conf)) {
printf("BMP280 Init Failed!rn");
while(1);
}
int32_t temp_raw, press_raw;
float temperature, pressure, altitude;
while(1) {
BMP280_ReadRaw(&temp_raw, &press_raw);
temperature = BMP280_CompensateTemp(temp_raw, &calib);
float t_fine = temperature * 100.0f; // 用于压力补偿
pressure = BMP280_CompensatePress(press_raw, &calib, t_fine);
altitude = BMP280_CalcAltitude(pressure);
printf("Temp: %.2f C Pressure: %.2f Pa Alt: %.2f mrn",
temperature, pressure, altitude);
HAL_Delay(1000);
}
}
5.红外测距 GP2Y0A21
应用场景
服务机器人:自动避障(如扫地机器人)
工业自动化:传送带物体位置检测
智能马桶:人体接近感应
STM32例程
#include "main.h"
#include <stdio.h>
#include <math.h>
#define IR_ADC hadc1 // 使用的ADC实例
#define SAMPLE_TIMES 5 // 采样次数(用于移动平均)
#define VALID_MIN_DISTANCE 10 // 有效测量范围10cm
#define VALID_MAX_DISTANCE 80 // 有效测量范围80cm
// ADC初始化(需在CubeMX中配置)
void MX_ADC1_Init(void);
// 非线性校准结构体
typedefstruct {
float adc_min; // 对应80cm的ADC值
float adc_max; // 对应10cm的ADC值
float voltage_ref;// 参考电压(3.3V或5V)
} IR_Calibration;
// 获取ADC原始值(12位分辨率)
uint32_t IR_GetRawADC(void)
{
uint32_t raw = 0;
HAL_ADC_Start(&IR_ADC);
for(int i=0; i<SAMPLE_TIMES; i++){
HAL_ADC_PollForConversion(&IR_ADC, 10);
raw += HAL_ADC_GetValue(&IR_ADC);
HAL_Delay(2);
}
HAL_ADC_Stop(&IR_ADC);
return raw / SAMPLE_TIMES; // 返回平均值
}
// 查表法计算距离(基于典型特性曲线)
float IR_CalcDistance_LUT(uint32_t adc_val, IR_Calibration *cal)
{
// 典型电压-距离对应表(需根据实测数据校准)
constfloat distance_table[] = {80.0, 70.0, 60.0, 50.0, 40.0, 30.0, 20.0, 10.0};
constuint16_t adc_table[] = {620, 720, 860, 1050, 1350, 1850, 2750, 3850}; // 3.3V参考电压时的典型值
if(adc_val <= adc_table[7]) return VALID_MIN_DISTANCE;
if(adc_val >= adc_table[0]) return VALID_MAX_DISTANCE;
// 线性插值
for(int i=0; i<7; i++){
if(adc_val >= adc_table[i+1] && adc_val <= adc_table[i]){
float ratio = (float)(adc_val - adc_table[i+1]) / (adc_table[i] - adc_table[i+1]);
return distance_table[i+1] + ratio*(distance_table[i] - distance_table[i+1]);
}
}
return0;
}
// 公式法计算距离(近似公式V = 1/(a*d + b))
float IR_CalcDistance_Formula(uint32_t adc_val, IR_Calibration *cal)
{
float voltage = (adc_val / 4095.0f) * cal->voltage_ref;
if(voltage < 0.4f) return VALID_MAX_DISTANCE;
if(voltage > 3.0f) return VALID_MIN_DISTANCE;
// 经验公式:1/d = (V - 0.35)/0.3 (单位:cm)
float distance = 1.0 / ((voltage - 0.35f)/0.3f);
return fmaxf(fminf(distance, VALID_MAX_DISTANCE), VALID_MIN_DISTANCE);
}
// 自动校准函数(需在已知距离下校准)
void IR_AutoCalibrate(IR_Calibration *cal, float known_distance)
{
uint32_t adc_val = IR_GetRawADC();
if(known_distance == 10.0f) cal->adc_min = adc_val;
if(known_distance == 80.0f) cal->adc_max = adc_val;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_ADC1_Init();
// 初始化校准参数(需根据实际测量校准)
IR_Calibration calib = {
.adc_min = 3850, // 10cm时的ADC值
.adc_max = 620, // 80cm时的ADC值
.voltage_ref = 3.3f// 参考电压
};
while(1)
{
uint32_t raw_adc = IR_GetRawADC();
// 两种计算方法
float distance_lut = IR_CalcDistance_LUT(raw_adc, &calib);
float distance_formula = IR_CalcDistance_Formula(raw_adc, &calib);
// 输出结果
printf("ADC: %4lu LUT: %.1fcm Formula: %.1fcmrn",
raw_adc, distance_lut, distance_formula);
HAL_Delay(200); // 控制采样频率
}
}
6.超声波测距 HC-SR04
应用场景
智能停车场:车位检测系统
液位监测:储油罐液位测量
安防系统:入侵检测报警
STM32例程
#include "main.h"
#include <stdio.h>
#include <math.h>
// 硬件引脚定义
#define TRIG_GPIO_PORT GPIOA
#define TRIG_GPIO_PIN GPIO_PIN_1
#define ECHO_GPIO_PORT GPIOA
#define ECHO_GPIO_PIN GPIO_PIN_0
// 定时器配置(用于测量高电平时间)
TIM_HandleTypeDef htim2;
// 测量参数
#define SOUND_SPEED_25C 34300.0f // 25℃时声速(cm/s)
#define MAX_DISTANCE_CM 400 // 最大有效距离
#define MIN_DISTANCE_CM 2 // 最小有效距离
#define TIMEOUT_US 60000 // 超时时间(对应400cm)
// 滤波器配置
#define MEDIAN_FILTER_SIZE 5 // 中值滤波窗口大小
#define MOVING_AVERAGE_SIZE 3 // 移动平均窗口大小
typedefstruct {
float temperature; // 环境温度(用于声速补偿)
float distance_cm; // 最终输出距离
uint32_t timeout; // 超时计数器
} Ultrasonic_State;
// 微秒延时函数
void Delay_us(uint16_t us)
{
__HAL_TIM_SET_COUNTER(&htim2, 0);
HAL_TIM_Base_Start(&htim2);
while(__HAL_TIM_GET_COUNTER(&htim2) < us);
HAL_TIM_Base_Stop(&htim2);
}
// 超声波模块初始化
void Ultrasonic_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置TRIG引脚为输出
GPIO_InitStruct.Pin = TRIG_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TRIG_GPIO_PORT, &GPIO_InitStruct);
// 配置ECHO引脚为输入
GPIO_InitStruct.Pin = ECHO_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(ECHO_GPIO_PORT, &GPIO_InitStruct);
}
// 触发测量
void Ultrasonic_Trigger(void)
{
HAL_GPIO_WritePin(TRIG_GPIO_PORT, TRIG_GPIO_PIN, GPIO_PIN_SET);
Delay_us(12); // 触发信号至少10us
HAL_GPIO_WritePin(TRIG_GPIO_PORT, TRIG_GPIO_PIN, GPIO_PIN_RESET);
}
// 获取高电平时间(微秒)
uint32_t Ultrasonic_GetPulseWidth(void)
{
uint32_t start_time = 0, end_time = 0;
// 等待ECHO变高
uint32_t timeout = 0;
while(HAL_GPIO_ReadPin(ECHO_GPIO_PORT, ECHO_GPIO_PIN) == GPIO_PIN_RESET){
if(++timeout > 1000) return0; // 1ms超时
Delay_us(1);
}
// 记录高电平开始时间
start_time = __HAL_TIM_GET_COUNTER(&htim2);
// 等待ECHO变低
timeout = 0;
while(HAL_GPIO_ReadPin(ECHO_GPIO_PORT, ECHO_GPIO_PIN) == GPIO_PIN_SET){
if(++timeout > TIMEOUT_US) break;
Delay_us(1);
}
end_time = __HAL_TIM_GET_COUNTER(&htim2);
return (end_time - start_time);
}
// 中值滤波
float MedianFilter(float new_value)
{
staticfloat buffer[MEDIAN_FILTER_SIZE];
staticuint8_t index = 0;
float temp[MEDIAN_FILTER_SIZE];
buffer[index] = new_value;
index = (index + 1) % MEDIAN_FILTER_SIZE;
// 复制数组并排序
for(uint8_t i=0; i<MEDIAN_FILTER_SIZE; i++) temp[i] = buffer[i];
for(uint8_t i=0; i<MEDIAN_FILTER_SIZE-1; i++){
for(uint8_t j=i+1; j<MEDIAN_FILTER_SIZE; j++){
if(temp[i] > temp[j]){
float swap = temp[i];
temp[i] = temp[j];
temp[j] = swap;
}
}
}
return temp[MEDIAN_FILTER_SIZE/2];
}
// 移动平均滤波
float MovingAverageFilter(float new_value)
{
staticfloat buffer[MOVING_AVERAGE_SIZE];
staticuint8_t index = 0;
staticfloat sum = 0;
sum -= buffer[index];
buffer[index] = new_value;
sum += new_value;
index = (index + 1) % MOVING_AVERAGE_SIZE;
return sum / MOVING_AVERAGE_SIZE;
}
// 计算距离(带温度补偿)
float CalcDistanceWithTemp(uint32_t pulse_us, float temp)
{
// 声速计算(331.5 + 0.6*T)m/s
float sound_speed = 33150.0f + 60.0f * temp;
float distance = (pulse_us * sound_speed) / 20000.0f; // 单位cm
// 限制有效范围
if(distance < MIN_DISTANCE_CM) return MIN_DISTANCE_CM;
if(distance > MAX_DISTANCE_CM) return MAX_DISTANCE_CM;
return distance;
}
// 主函数示例
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_TIM2_Init(); // 配置TIM2为1us计数
Ultrasonic_Init();
Ultrasonic_State state = {
.temperature = 25.0f, // 默认25℃
.distance_cm = 0,
.timeout = 0
};
while(1)
{
Ultrasonic_Trigger();
uint32_t pulse_us = Ultrasonic_GetPulseWidth();
if(pulse_us == 0){
state.timeout++;
if(state.timeout > 3) printf("Sensor Error!rn");
continue;
}
// 原始距离计算
float raw_distance = CalcDistanceWithTemp(pulse_us, state.temperature);
// 双重滤波处理
float median = MedianFilter(raw_distance);
float filtered = MovingAverageFilter(median);
state.distance_cm = filtered;
state.timeout = 0;
printf("Distance: %.1f cmrn", state.distance_cm);
HAL_Delay(100); // 最小测量间隔60ms
}
}
7.气体传感器 MQ-2
应用场景
智能家居:燃气泄漏报警(如海尔燃气报警器)
矿业安全:井下瓦斯浓度监测
车载设备:新能源汽车电池热失控预警
STM32例程
#include "main.h"
#include <stdio.h>
#include <math.h>
#define ADC_HANDLE hadc1
#define HEATER_GPIO GPIOA
#define HEATER_PIN GPIO_PIN_4
#define CALIBRATION_SAMPLES 50 // 校准采样次数
typedefenum {
GAS_LPG = 0,
GAS_SMOKE,
GAS_HYDROGEN,
GAS_TYPE_COUNT
} GasType;
// 传感器参数结构体
typedefstruct {
float Ro; // 干净空气中传感器电阻
float RL; // 负载电阻值
float VCC; // 供电电压
float temp_compensation; // 温度补偿系数
float hum_compensation; // 湿度补偿系数
float gas_curve[GAS_TYPE_COUNT][3]; // 气体曲线参数
} MQ2_Calibration;
// 初始化函数
void MQ2_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
HAL_ADC_ConfigChannel(&ADC_HANDLE, &sConfig);
// 加热器控制引脚初始化
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = HEATER_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(HEATER_GPIO, &GPIO_InitStruct);
}
// 预热传感器(建议首次使用前预热24小时)
void MQ2_Preheat(uint32_t minutes)
{
HAL_GPIO_WritePin(HEATER_GPIO, HEATER_PIN, GPIO_PIN_SET);
HAL_Delay(minutes * 60 * 1000);
HAL_GPIO_WritePin(HEATER_GPIO, HEATER_PIN, GPIO_PIN_RESET);
}
// 获取ADC原始值(12位分辨率)
float MQ2_ReadAnalog(void)
{
uint32_t raw = 0;
HAL_ADC_Start(&ADC_HANDLE);
for(int i=0; i<5; i++){ // 5次采样平均
HAL_ADC_PollForConversion(&ADC_HANDLE, 10);
raw += HAL_ADC_GetValue(&ADC_HANDLE);
}
HAL_ADC_Stop(&ADC_HANDLE);
return raw / 5.0f;
}
// 校准函数(需在干净空气中执行)
void MQ2_Calibrate(MQ2_Calibration *calib)
{
float avg = 0;
for(int i=0; i<CALIBRATION_SAMPLES; i++){
float Vout = MQ2_ReadAnalog() / 4095.0f * calib->VCC;
float Rs_air = (calib->VCC - Vout) / Vout * calib->RL;
avg += Rs_air;
HAL_Delay(100);
}
calib->Ro = avg / CALIBRATION_SAMPLES / 9.8f; // 清洁空气系数9.8
}
// 计算气体浓度(应用温度补偿)
float MQ2_GetGasPPM(MQ2_Calibration *calib, GasType gas_type)
{
float Vout = MQ2_ReadAnalog() / 4095.0f * calib->VCC;
float Rs = (calib->VCC - Vout) / Vout * calib->RL;
float Rs_Ro = Rs / calib->Ro;
/* 气体灵敏度曲线公式:Rs/Ro = a*(ppm)^b + c */
float a = calib->gas_curve[gas_type][0];
float b = calib->gas_curve[gas_type][1];
float c = calib->gas_curve[gas_type][2];
float ppm = powf(((Rs_Ro - c)/a), 1.0f/b);
return ppm * calib->temp_compensation * calib->hum_compensation;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_ADC1_Init();
// 初始化校准参数
MQ2_Calibration calib = {
.Ro = 10.0f, // 初始值(需校准)
.RL = 5.0f, // 分压电阻(单位:kΩ)
.VCC = 5.0f, // 传感器供电电压
.temp_compensation = 1.0f, // 默认补偿系数
.hum_compensation = 1.0f,
.gas_curve = { // 典型灵敏度曲线参数
[GAS_LPG] = {212.7, -0.5, 100}, // LPG
[GAS_SMOKE] = {300.0, -0.55, 120}, // Smoke
[GAS_HYDROGEN] = {150.0, -0.4, 90} // H2
}
};
MQ2_Init();
MQ2_Calibrate(&calib);
while(1)
{
float lpg_ppm = MQ2_GetGasPPM(&calib, GAS_LPG);
float smoke_ppm = MQ2_GetGasPPM(&calib, GAS_SMOKE);
float hydrogen_ppm = MQ2_GetGasPPM(&calib, GAS_HYDROGEN);
printf("LPG: %.1fppm Smoke: %.1fppm H2: %.1fppmrn",
lpg_ppm, smoke_ppm, hydrogen_ppm);
// 报警检测
if(lpg_ppm > 2000) printf("LPG Danger!rn");
if(smoke_ppm > 1000) printf("Fire Warning!rn");
if(hydrogen_ppm > 500) printf("H2 Leakage!rn");
HAL_Delay(1000);
}
}
阅读全文
2088