#include "main.h" #include "device/adc.h" #include "sys/systick.h" #define TEMP_ADC ADC0 #define TEMP_ADC_CHANNEL ADC_CHANNEL_16 #define VREFINT_ADC_CHANNEL ADC_CHANNEL_17 #define ADC_MAX_VALUE 4095.0f #define VREFINT_TYPICAL_VOLTAGE 1.20f #define TEMP_V25 1.43f #define TEMP_AVG_SLOPE 0.0043f /** * @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(TEMP_ADC); adc_mode_config(ADC_MODE_FREE); adc_special_function_config(TEMP_ADC, ADC_SCAN_MODE, DISABLE); adc_special_function_config(TEMP_ADC, ADC_CONTINUOUS_MODE, DISABLE); adc_data_alignment_config(TEMP_ADC, ADC_DATAALIGN_RIGHT); adc_channel_length_config(TEMP_ADC, ADC_REGULAR_CHANNEL, 1U); adc_external_trigger_source_config(TEMP_ADC, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); adc_external_trigger_config(TEMP_ADC, ADC_REGULAR_CHANNEL, DISABLE); adc_tempsensor_vrefint_enable(); delay_ms(2U); adc_enable(TEMP_ADC); delay_ms(2U); adc_calibration_enable(TEMP_ADC); } /** * @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(TEMP_ADC, 0U, channel, ADC_SAMPLETIME_239POINT5); adc_flag_clear(TEMP_ADC, ADC_FLAG_EOC); adc_software_trigger_enable(TEMP_ADC, ADC_REGULAR_CHANNEL); while(RESET == adc_flag_get(TEMP_ADC, ADC_FLAG_EOC)) { } return adc_regular_data_read(TEMP_ADC); } /** * @brief 读取内部参考电压通道的原始 ADC 值。 * @param[in] 无。 * @param[out] 无。 * @retval uint16_t 内部参考电压通道的原始 ADC 值。 */ uint16_t adc_read_vref_raw(void) { return adc_single_read(VREFINT_ADC_CHANNEL); } /** * @brief 读取内部温度通道的原始 ADC 值。 * @param[in] 无。 * @param[out] 无。 * @retval uint16_t 内部温度通道的原始 ADC 值。 */ uint16_t adc_read_temp_raw(void) { return adc_single_read(TEMP_ADC_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 3.3f; } return (VREFINT_TYPICAL_VOLTAGE * 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) / ADC_MAX_VALUE; return ((TEMP_V25 - sensor_voltage) / 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); }