wzyhg adc_driver.c

wzyhg adc_driver.c /********************************************************************************* file adc_driver.c* brief STM32F1 ADC常用采集与数据处理封装实现。** 该文件以CubeMX生成的adc.c/adc.h为基础adc.c负责ADC时钟、PA2等模拟GPIO、* 规则组和触发源本文件仅使用生成的hadc句柄调用HAL不修改生成代码。*******************************************************************************/#include adc_driver.h/*** brief 判断ADC对象是否已绑定有效CubeMX句柄和换算参数。*/static uint8_t ADC_Driver_IsReady(const ADC_Driver_t *device){/* 检查对象地址、初始化标志、HAL句柄、ADC寄存器基地址、参考电压和量程。 */return (uint8_t)((device ! NULL) (device-initialized ! 0U) (device-hadc ! NULL) (device-hadc-Instance ! NULL) (device-vref_voltage 0.0f) (device-full_scale ! 0U));}ADC_Status_t ADC_Driver_Init(ADC_Driver_t *device,ADC_HandleTypeDef *hadc,float vref_voltage,uint32_t full_scale){/* 参数检查放在任何成员写入前防止对象只初始化一半。 */if ((device NULL) ||(hadc NULL) ||(hadc-Instance NULL) ||!(vref_voltage 0.0f) ||(full_scale 0U)){return ADC_STATUS_INVALID_ARGUMENT;}/* 保存CubeMX生成的hadc1地址后续HAL调用均使用该句柄。 */device-hadc hadc;/* 保存VDDA/参考电压RawToVoltage()用它将码值转换为伏特。 */device-vref_voltage vref_voltage;/* 保存满量程码值STM32F1 12位右对齐通常填写4095。 */device-full_scale full_scale;/* 此时尚未调用HAL_ADC_Start_DMA()故DMA状态明确为停止。 */device-dma_running 0U;/* 最后置位表示对象完整可用。 */device-initialized 1U;return ADC_STATUS_OK;}ADC_Status_t ADC_Driver_Calibrate(ADC_Driver_t *device){if (ADC_Driver_IsReady(device) 0U){/* 未绑定hadc时不能调用HAL校准函数。 */return ADC_STATUS_NOT_INITIALIZED;}/* STM32F1 HAL执行ADC复位校准和校准序列期间不要启动转换。 */if (HAL_ADCEx_Calibration_Start(device-hadc) ! HAL_OK){return ADC_STATUS_HAL_ERROR;}return ADC_STATUS_OK;}ADC_Status_t ADC_Driver_ReadRaw(ADC_Driver_t *device,uint32_t timeout_ms,uint16_t *raw_value){HAL_StatusTypeDef hal_status;if ((ADC_Driver_IsReady(device) 0U) || (raw_value NULL)){/* 输出地址为空会导致读取结果无处保存因此拒绝开始转换。 */return ADC_STATUS_INVALID_ARGUMENT;}/* DMA连续转换拥有ADC数据寄存器禁止再用轮询方式抢占同一ADC。 */if (device-dma_running ! 0U){return ADC_STATUS_BUSY;}/* 按CubeMX当前ExternalTrigConv配置启动一次规则组转换。 */hal_status HAL_ADC_Start(device-hadc);if (hal_status ! HAL_OK){return ADC_STATUS_HAL_ERROR;}/* 阻塞等待EOC转换完成标志最多等待timeout_ms。 */hal_status HAL_ADC_PollForConversion(device-hadc, timeout_ms);if (hal_status HAL_OK){/* 读取DR寄存器的转换结果当前F1配置为右对齐12位。 */*raw_value (uint16_t)HAL_ADC_GetValue(device-hadc);}/* 无论转换成功或超时都停止ADC避免影响下一次软件触发采样。 */if (HAL_ADC_Stop(device-hadc) ! HAL_OK){return ADC_STATUS_HAL_ERROR;}/* 若等待阶段超时或出错在停止ADC后再向调用者报告。 */return (hal_status HAL_OK) ? ADC_STATUS_OK : ADC_STATUS_HAL_ERROR;}ADC_Status_t ADC_Driver_ReadAverage(ADC_Driver_t *device,uint16_t sample_count,uint32_t timeout_ms,uint16_t *raw_average){uint64_t sum 0ULL;uint16_t raw_value;uint16_t index;ADC_Status_t status;if ((ADC_Driver_IsReady(device) 0U) ||(sample_count 0U) ||(raw_average NULL)){return ADC_STATUS_INVALID_ARGUMENT;}/* 本函数内部会重复调用ReadRaw()DMA期间同样不能执行。 */if (device-dma_running ! 0U){return ADC_STATUS_BUSY;}/* 循环执行单次转换ReadRaw内部已负责每次Start、Poll、Read、Stop。 */for (index 0U; index sample_count; index){status ADC_Driver_ReadRaw(device, timeout_ms, raw_value);if (status ! ADC_STATUS_OK){/* 任意一次采样失败即退出避免返回混有无效数据的平均值。 */return status;}/* 使用64位累加防止大量12位采样值相加时溢出。 */sum raw_value;}/* 加sample_count/2实现整数四舍五入然后除以采样数量。 */*raw_average (uint16_t)((sum ((uint64_t)sample_count / 2ULL)) /(uint64_t)sample_count);return ADC_STATUS_OK;}ADC_Status_t ADC_Driver_StartDMA(ADC_Driver_t *device,volatile uint16_t *buffer,uint16_t sample_count){uint32_t *hal_buffer;if ((ADC_Driver_IsReady(device) 0U) || (buffer NULL) || (sample_count 0U)){return ADC_STATUS_INVALID_ARGUMENT;}/* 同一ADC的DMA流只能启动一次重复启动会破坏HAL状态机。 */if (device-dma_running ! 0U){return ADC_STATUS_BUSY;}/* HAL原型使用uint32_t指针CubeMX的半字DMA配置保证每项实际写入16位。 */hal_buffer (uint32_t *)(void *)buffer;/* HAL启动ADC转换和关联的DMA请求DMA循环模式由CubeMX生成代码决定。 */if (HAL_ADC_Start_DMA(device-hadc, hal_buffer, (uint32_t)sample_count) ! HAL_OK){return ADC_STATUS_HAL_ERROR;}device-dma_running 1U;return ADC_STATUS_OK;}ADC_Status_t ADC_Driver_StopDMA(ADC_Driver_t *device){if (ADC_Driver_IsReady(device) 0U){return ADC_STATUS_NOT_INITIALIZED;}if (device-dma_running 0U){return ADC_STATUS_OK;}/* HAL关闭ADC的DMA请求并停止DMA通道缓冲区会保留最后一批样本。 */if (HAL_ADC_Stop_DMA(device-hadc) ! HAL_OK){return ADC_STATUS_HAL_ERROR;}device-dma_running 0U;return ADC_STATUS_OK;}uint8_t ADC_Driver_IsDMARunning(const ADC_Driver_t *device){return (uint8_t)((ADC_Driver_IsReady(device) ! 0U) (device-dma_running ! 0U));}ADC_Status_t ADC_Driver_SelectChannel(ADC_Driver_t *device,uint32_t channel,uint32_t sampling_time){ADC_ChannelConfTypeDef config {0};if (ADC_Driver_IsReady(device) 0U){return ADC_STATUS_NOT_INITIALIZED;}/* DMA循环采样时改通道会使同一缓冲区混入不同通道的数据。 */if (device-dma_running ! 0U){return ADC_STATUS_BUSY;}/* 在进入HAL前先验证宏参数避免HAL内部assert_param()因非法值停机。 */if ((!IS_ADC_CHANNEL(channel)) || (!IS_ADC_SAMPLE_TIME(sampling_time))){return ADC_STATUS_INVALID_ARGUMENT;}/* 选择规则组第1项这与当前CubeMX单通道、Scan关闭配置匹配。 */config.Channel channel;config.Rank ADC_REGULAR_RANK_1;/* 采样时间决定采样电容充电时间高阻分压需使用较长时间。 */config.SamplingTime sampling_time;/* HAL将上述配置写入SQR/SMPR等ADC规则组相关寄存器。 */if (HAL_ADC_ConfigChannel(device-hadc, config) ! HAL_OK){return ADC_STATUS_HAL_ERROR;}return ADC_STATUS_OK;}float ADC_Driver_RawToVoltage(const ADC_Driver_t *device,uint16_t raw_value){if (ADC_Driver_IsReady(device) 0U){return 0.0f;}/* 原始值异常高于满量程时先限制避免换算得到超过参考电压的假值。 */if ((uint32_t)raw_value device-full_scale){raw_value (uint16_t)device-full_scale;}/* 公式V_adc raw / full_scale * Vref。 */return ((float)raw_value * device-vref_voltage) /(float)device-full_scale;}ADC_Status_t ADC_Driver_DividerToVoltage(const ADC_Driver_t *device,uint16_t raw_value,float r_top_ohm,float r_bottom_ohm,float *input_voltage){float adc_voltage;if ((ADC_Driver_IsReady(device) 0U) ||!(r_top_ohm 0.0f) ||!(r_bottom_ohm 0.0f) ||(input_voltage NULL)){return ADC_STATUS_INVALID_ARGUMENT;}/* 先得到ADC引脚处低压侧的实际电压。 */adc_voltage ADC_Driver_RawToVoltage(device, raw_value);/* 根据二电阻分压公式还原R_top上端的被测输入电压。 */*input_voltage adc_voltage *((r_top_ohm r_bottom_ohm) / r_bottom_ohm);return ADC_STATUS_OK;}ADC_Status_t ADC_Driver_AverageBuffer(const volatile uint16_t *buffer,uint16_t sample_count,uint16_t *raw_average){uint64_t sum 0ULL;uint16_t index;if ((buffer NULL) || (sample_count 0U) || (raw_average NULL)){return ADC_STATUS_INVALID_ARGUMENT;}/* 遍历DMA或外部采样缓冲区累加每个16位码值。 */for (index 0U; index sample_count; index){sum buffer[index];}/* 四舍五入得到平均原始码值。 */*raw_average (uint16_t)((sum ((uint64_t)sample_count / 2ULL)) /(uint64_t)sample_count);return ADC_STATUS_OK;}ADC_Status_t ADC_Driver_GetBufferStatistics(const volatile uint16_t *buffer,uint16_t sample_count,uint16_t *raw_min,uint16_t *raw_max,uint16_t *raw_avg){uint64_t sum 0ULL;uint16_t minimum;uint16_t maximum;uint16_t index;if ((buffer NULL) || (sample_count 0U)){return ADC_STATUS_INVALID_ARGUMENT;}/* 使用第一个样本初始化上下界避免人为假设最小/最大初值。 */minimum buffer[0];maximum buffer[0];/* 一次遍历同时完成累加、最小值和最大值统计。 */for (index 0U; index sample_count; index){sum buffer[index];if (buffer[index] minimum){minimum buffer[index];}if (buffer[index] maximum){maximum buffer[index];}}/* 输出指针允许为NULL便于调用者只关心其中一项统计量。 */if (raw_min ! NULL){*raw_min minimum;}if (raw_max ! NULL){*raw_max maximum;}if (raw_avg ! NULL){*raw_avg (uint16_t)((sum ((uint64_t)sample_count / 2ULL)) /(uint64_t)sample_count);}return ADC_STATUS_OK;}