#ifndef __ADC_H__ #define __ADC_H__ #ifdef __cplusplus extern "C" { #endif /* Includes ------------------------------------------------------------------*/ #include "main.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ extern ADC_HandleTypeDef hadc1; /* USER CODE BEGIN Private defines */ /* 水位传感器ADC值变量声明 */ extern uint32_t water_level_adc_value; extern float calibration_coefficient; // 校准系数 /* USER CODE END Private defines */ void MX_ADC1_Init(void); /* USER CODE BEGIN Prototypes */ /* 水位传感器相关函数声明 */ void Water_Level_Sensor_Init(void); // 水位传感器初始化函数 uint16_t Get_Water_Level_ADC_Value(void); // 获取水位传感器的ADC值 void Calibrate_Water_Level_Sensor(float reference_water_level, uint16_t reference_adc_value); // 校准函数 float Get_Calibrated_Water_Level(uint16_t adc_value); // 获取校准后的水位高度 /* USER CODE END Prototypes */ 这是头文件,我要求显示修改后的头文件和完整的c文件代码
首先,用户提供的头文件adc.h
中包含了一些基本的配置,比如ADC的初始化函数MX_ADC1_Init
,以及一些用于水位传感器的变量和函数声明,比如校准系数calibration_coefficient
和获取ADC值的函数Get_Water_Level_ADC_Value
。用户之前得到的优化建议涉及模块化设计、DMA传输、滤波处理等,所以需要将这些优化点整合到用户的现有代码中。
接下来,我需要考虑如何将之前的优化结构适配到用户的头文件中。之前的优化建议使用了结构体来封装ADC配置和水位传感器的参数,比如ADC_Config
和WaterSensor
结构体。这可能与用户现有的全局变量(如water_level_adc_value
和calibration_coefficient
)有所冲突,所以需要调整结构体以包含这些变量,或者替换原有的全局变量。
另外,用户现有的函数声明如Calibrate_Water_Level_Sensor
和Get_Calibrated_Water_Level
可能需要重新设计,以符合模块化的结构。例如,校准函数可能需要接收结构体参数,而不是全局变量,这样更利于多实例支持。
然后,考虑到用户可能使用的是STM32CubeMX生成的代码,需要保留MX_ADC1_Init
函数,同时将新的初始化函数ADC_Init
整合进去。可能需要将CubeMX生成的初始化代码与自定义的模块化代码结合,确保硬件配置正确。
在C文件部分,需要实现头文件中声明的所有函数,并加入DMA配置、滤波处理等功能。例如,ADC_ReadRaw
函数应使用DMA传输来获取ADC值,而水位传感器的获取函数需要加入移动平均滤波,如之前示例中的filter_buffer
。
同时,需要注意现有的全局变量如water_level_adc_value
是否仍然必要。在优化后的设计中,ADC值可能存储在结构体的成员变量中,因此可能需要移除原有的全局变量,改为通过结构体访问。
还需要考虑校准函数的实现方式。用户原有的校准函数可能基于一个参考水位和对应的ADC值来设置校准系数,而优化后的代码可能需要在WaterSensor
结构体中存储零点和满量程电压,从而进行线性插值计算。因此,校准函数可能需要调整为设置这两个电压值,而不是单一的校准系数。
此外,错误处理机制也需要考虑进去,比如在ADC读取值超出预期范围时,返回错误或进行相应的处理,提高系统的鲁棒性。
最后,确保代码符合STM32F103C8T6的硬件限制,比如可用的ADC通道、DMA通道等,避免配置错误。同时,代码需要保持可维护性和可读性,添加必要的注释,方便用户后续的修改和调试。
总结来说,步骤可能包括:
- 修改头文件,引入结构体封装ADC和水位传感器的配置参数。
- 替换原有的全局变量,将其纳入结构体中。
- 更新函数声明,使用结构体指针作为参数,增强模块化。
- 实现DMA传输和滤波算法,提升采样效率和稳定性。
- 调整校准函数,使用零点和满量程电压进行线性插值。
- 确保与CubeMX生成的代码兼容,保留必要的初始化函数。
- 添加错误处理和安全检查,如断言或范围限制。
- 编写完整的C文件代码,实现所有声明的函数,并添加详细注释。
在实现过程中,需要不断检查代码的语法正确性,确保没有遗漏的依赖项或配置步骤,比如DMA的初始化、ADC校准等。同时,保持代码的简洁和高效,避免不必要的计算或内存占用,特别是在中断服务例程或高频调用的函数中。
可能遇到的挑战包括DMA配置与现有CubeMX生成的代码的兼容性,需要仔细检查DMA通道和ADC实例的关联是否正确。此外,滤波算法的实现需要注意缓冲区索引的管理,避免越界或数据覆盖的问题。
最后,测试和验证是关键。需要建议用户在硬件上测试优化后的代码,观察ADC采样是否稳定,水位计算是否准确,并根据实际测量结果调整滤波窗口大小或校准参数。根据您的需求,我将为您提供优化后的头文件和对应的完整C文件实现。以下是基于模块化设计和稳定性优化的代码方案:
优化后的adc.h头文件
#ifndef __ADC_H__
#define __ADC_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include <stdbool.h>
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern ADC_HandleTypeDef hadc1;
/* USER CODE BEGIN Private defines */
// 模块化配置结构体
typedef struct {
ADC_HandleTypeDef* hadc;
uint32_t channel;
float vref; // 参考电压(V)
uint16_t zero_adc; // 空水位ADC值
uint16_t full_adc; // 满水位ADC值
bool calibrated; // 校准标志
} WaterSensor_TypeDef;
// 滤波配置
#define FILTER_WINDOW_SIZE 10 // 移动平均窗口大小
/* USER CODE END Private defines */
void MX_ADC1_Init(void);
/* USER CODE BEGIN Prototypes */
// 初始化函数
void WaterSensor_Init(WaterSensor_TypeDef* sensor, ADC_HandleTypeDef* hadc, uint32_t channel, float vref);
// 核心功能函数
bool WaterSensor_Calibrate(WaterSensor_TypeDef* sensor, uint16_t zero_adc, uint16_t full_adc);
float WaterSensor_GetLevel(WaterSensor_TypeDef* sensor);
uint16_t WaterSensor_GetRawADC(WaterSensor_TypeDef* sensor);
// 诊断函数
bool WaterSensor_SelfTest(WaterSensor_TypeDef* sensor);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __ADC_H__ */
完整的adc.c实现文件
#include "adc.h"
#include <string.h>
/* USER CODE BEGIN PV */
// 私有变量定义
static uint16_t adc_buffer[FILTER_WINDOW_SIZE];
static uint8_t filter_index = 0;
/* USER CODE END PV */
ADC_HandleTypeDef hadc1;
/* USER CODE BEGIN 0 */
void WaterSensor_Init(WaterSensor_TypeDef* sensor,
ADC_HandleTypeDef* hadc,
uint32_t channel,
float vref)
{
// 参数校验
if(sensor == NULL || hadc == NULL) return;
memset(sensor, 0, sizeof(WaterSensor_TypeDef));
sensor->hadc = hadc;
sensor->channel = channel;
sensor->vref = vref;
sensor->calibrated = false;
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = channel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
HAL_ADC_ConfigChannel(hadc, &sConfig);
// 启动DMA连续转换
HAL_ADC_Start_DMA(hadc, (uint32_t*)adc_buffer, FILTER_WINDOW_SIZE);
}
bool WaterSensor_Calibrate(WaterSensor_TypeDef* sensor,
uint16_t zero_adc,
uint16_t full_adc)
{
if(sensor == NULL) return false;
if(full_adc <= zero_adc) return false;
sensor->zero_adc = zero_adc;
sensor->full_adc = full_adc;
sensor->calibrated = true;
return true;
}
float WaterSensor_GetLevel(WaterSensor_TypeDef* sensor)
{
if(sensor == NULL || !sensor->calibrated) return -1.0f;
// 获取滤波后的ADC值
uint32_t sum = 0;
for(uint8_t i=0; i<FILTER_WINDOW_SIZE; i++){
sum += adc_buffer[i];
}
uint16_t avg_adc = sum / FILTER_WINDOW_SIZE;
// 范围限制
if(avg_adc <= sensor->zero_adc) return 0.0f;
if(avg_adc >= sensor->full_adc) return 100.0f;
// 线性转换
return ((float)(avg_adc - sensor->zero_adc) /
(sensor->full_adc - sensor->zero_adc)) * 100.0f;
}
uint16_t WaterSensor_GetRawADC(WaterSensor_TypeDef* sensor)
{
if(sensor == NULL) return 0;
return adc_buffer[filter_index % FILTER_WINDOW_SIZE];
}
bool WaterSensor_SelfTest(WaterSensor_TypeDef* sensor)
{
if(sensor == NULL) return false;
// 检查ADC值是否在合理范围内
uint16_t raw = WaterSensor_GetRawADC(sensor);
if(raw > 4095) return false; // 12位ADC最大值检查
// 检查参考电压范围
if(sensor->vref < 2.8f || sensor->vref > 3.6f) return false;
return true;
}
/* USER CODE END 0 */
/**
* @brief ADC1 Initialization Function
* @param None
* @retval None
*/
void MX_ADC1_Init(void)
{
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;
HAL_ADC_Init(&hadc1);
}
/* USER CODE BEGIN 1 */
// DMA完成回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
// 更新滤波索引
filter_index = (filter_index + 1) % FILTER_WINDOW_SIZE;
}
/* USER CODE END 1 */
主要优化点说明:
模块化设计:
- 使用
WaterSensor_TypeDef
结构体封装所有传感器参数 - 分离硬件配置(ADC通道)和应用参数(校准值)
- 使用
稳定性增强:
// 增加参数校验 if(sensor == NULL || hadc == NULL) return; // 添加自检函数 bool WaterSensor_SelfTest(WaterSensor_TypeDef* sensor);
实时滤波优化:
// 使用DMA循环模式实现硬件级滤波 HAL_ADC_Start_DMA(hadc, (uint32_t*)adc_buffer, FILTER_WINDOW_SIZE); // 在转换完成回调中管理索引 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { filter_index = (filter_index + 1) % FILTER_WINDOW_SIZE; }
校准机制改进:
// 双点校准(空/满量程) bool WaterSensor_Calibrate(WaterSensor_TypeDef* sensor, uint16_t zero_adc, uint16_t full_adc);
使用方法:
// 在main.c中
WaterSensor_TypeDef my_sensor;
int main(void) {
// 硬件初始化
HAL_Init();
MX_ADC1_Init();
// 水位传感器初始化
WaterSensor_Init(&my_sensor, &hadc1, ADC_CHANNEL_0, 3.3f);
// 校准(实际使用时需要物理测量)
WaterSensor_Calibrate(&my_sensor, 820, 3800); // 示例校准值
while(1) {
float level = WaterSensor_GetLevel(&my_sensor);
if(level >= 0) {
printf("水位: %.1f%%\n", level);
} else {
printf("传感器故障!\n");
}
HAL_Delay(1000);
}
}
新增功能:
- 自动DMA循环采样
- 硬件级滤波(无需软件干预)
- 双点校准机制
- 传感器自诊断功能
- 参数范围校验
- 故障安全返回(-1表示错误)
推荐配置参数:
// 在adc.h中可调整:
#define FILTER_WINDOW_SIZE 20 // 根据噪声情况调整
#define ADC_SAMPLETIME ADC_SAMPLETIME_239CYCLES_5 // 高阻抗信号时增加采样时间
请根据实际硬件连接修改ADC_CHANNEL
参数,并确保:
- 在CubeMX中启用ADC的DMA连续模式
- 配置正确的DMA通道(根据具体型号)
- 校准值通过实际测量获取
- 参考电压与实际电路匹配(3.3V或使用外部基准)
相关推荐


















