#include "main.h" #include "device/adc.h" #include "sys/systick.h" #include "sys/config.h" /** * @brief 初始化 ADC 硬件及内部温度/VREF 通道。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 完成 ADC 时钟、模式、触发方式、内部通道和校准流程配置。 */ void adc_hw_init(void) { rcu_periph_clock_enable(RCU_ADC0); rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8); adc_deinit(CONFIG_ADC_INSTANCE); adc_mode_config(ADC_MODE_FREE); adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, DISABLE); adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, DISABLE); adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT); adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, 1U); adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE); adc_tempsensor_vrefint_enable(); delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); adc_enable(CONFIG_ADC_INSTANCE); delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS); adc_calibration_enable(CONFIG_ADC_INSTANCE); } /** * @brief 执行指定通道的一次 ADC 单次采样。 * @param[in] channel 待采样的 ADC 通道号。 * @param[out] 无。 * @retval uint16_t 指定通道的原始 ADC 采样值。 * @note 函数采用阻塞式等待,直到 EOC 置位后返回。 */ uint16_t adc_single_read(uint8_t channel) { adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, channel, CONFIG_ADC_SAMPLE_TIME); adc_flag_clear(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC); adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL); while(RESET == adc_flag_get(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC)) { } return adc_regular_data_read(CONFIG_ADC_INSTANCE); } /** * @brief 读取内部参考电压通道的原始 ADC 值。 * @param[in] 无。 * @param[out] 无。 * @retval uint16_t 内部参考电压通道的原始 ADC 值。 */ uint16_t adc_read_vref_raw(void) { return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL); } /** * @brief 读取内部温度通道的原始 ADC 值。 * @param[in] 无。 * @param[out] 无。 * @retval uint16_t 内部温度通道的原始 ADC 值。 */ uint16_t adc_read_temp_raw(void) { return adc_single_read(CONFIG_ADC_TEMP_CHANNEL); } /** * @brief 根据内部参考电压原始值计算实际 VDDA。 * @param[in] vref_raw 内部参考电压通道原始 ADC 数值。 * @param[out] 无。 * @retval float 换算得到的 VDDA 电压值,单位 V。 * @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。 */ float adc_calculate_vdda(uint16_t vref_raw) { if(vref_raw == 0U) { return CONFIG_ADC_DEFAULT_VDDA; } return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast(vref_raw); } /** * @brief 根据温度原始值与 VDDA 计算芯片温度。 * @param[in] temp_raw 内部温度通道原始 ADC 数值。 * @param[in] vdda 当前供电电压,单位 V。 * @param[out] 无。 * @retval float 换算得到的芯片温度,单位 ℃。 */ float adc_calculate_temperature(uint16_t temp_raw, float vdda) { const float sensor_voltage = (static_cast(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE; return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f; } /** * @brief 采集一组完整 ADC 样本并完成工程值换算。 * @param[in] sample 指向输出样本结构体的指针;允许为空。 * @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。 * @retval 无返回值。 * @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。 */ void adc_collect_sample(adc_sample_t *sample) { if(sample == nullptr) { return; } sample->vref_raw = adc_read_vref_raw(); sample->temp_raw = adc_read_temp_raw(); sample->vdda = adc_calculate_vdda(sample->vref_raw); sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda); }