/** * @file ADC.cpp * @brief ADC 采样模块实现。 */ #include "main.h" #include "device/adc.h" #include "sys/systick.h" #include "sys/config.h" namespace { constexpr uint32_t kDmaPeriph = DMA0; constexpr dma_channel_enum kDmaChannel = DMA_CH0; constexpr uint32_t kChannelCount = 3U; constexpr uint32_t kVinGpio = GPIOA; constexpr uint32_t kVinPin = GPIO_PIN_6; volatile uint16_t s_dmaBuffer[kChannelCount] = {0U, 0U, 0U}; volatile uint8_t s_dmaReady = 0U; } // namespace void Adc::initDma() { dma_parameter_struct dma_initpara{}; rcu_periph_clock_enable(RCU_DMA0); dma_deinit(kDmaPeriph, kDmaChannel); dma_struct_para_init(&dma_initpara); dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE)); dma_initpara.memory_addr = (uint32_t)s_dmaBuffer; dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY; dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT; dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_initpara.number = kChannelCount; dma_initpara.priority = DMA_PRIORITY_HIGH; dma_init(kDmaPeriph, kDmaChannel, &dma_initpara); dma_circulation_enable(kDmaPeriph, kDmaChannel); dma_channel_enable(kDmaPeriph, kDmaChannel); } void Adc::init() { rcu_periph_clock_enable(RCU_ADC0); rcu_periph_clock_enable(RCU_GPIOA); rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8); /* 原理图:PA6 -> VIN_ADC,为外部电源分压检测输入 */ gpio_init(kVinGpio, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, kVinPin); initDma(); adc_deinit(CONFIG_ADC_INSTANCE); adc_mode_config(ADC_MODE_FREE); adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE); adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE); adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT); adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kChannelCount); adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_VIN_CHANNEL, CONFIG_ADC_SAMPLE_TIME); adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME); adc_regular_channel_config(CONFIG_ADC_INSTANCE, 2U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME); 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_dma_mode_enable(CONFIG_ADC_INSTANCE); adc_tempsensor_vrefint_enable(); SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS); adc_enable(CONFIG_ADC_INSTANCE); SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS); adc_calibration_enable(CONFIG_ADC_INSTANCE); adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL); SysTickTimer::delayMs(1U); s_dmaReady = 1U; } bool Adc::dmaReady() { return s_dmaReady != 0U; } const uint16_t *Adc::dmaBuffer() { return (const uint16_t *)s_dmaBuffer; } uint16_t Adc::readRaw(uint8_t channel) { if(channel == CONFIG_ADC_VIN_CHANNEL) return s_dmaBuffer[0]; if(channel == CONFIG_ADC_TEMP_CHANNEL) return s_dmaBuffer[1]; if(channel == CONFIG_ADC_VREFINT_CHANNEL) return s_dmaBuffer[2]; return 0U; } uint16_t Adc::readVinRaw() { return readRaw(CONFIG_ADC_VIN_CHANNEL); } uint16_t Adc::readVrefRaw() { return readRaw(CONFIG_ADC_VREFINT_CHANNEL); } uint16_t Adc::readTempRaw() { return readRaw(CONFIG_ADC_TEMP_CHANNEL); } float Adc::calculateVinAdcVoltage(uint16_t vinRaw, float vdda) { return (static_cast(vinRaw) * vdda) / CONFIG_ADC_MAX_VALUE; } float Adc::calculateVdda(uint16_t vrefRaw) { if(vrefRaw == 0U) return CONFIG_ADC_DEFAULT_VDDA; return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast(vrefRaw); } float Adc::calculateTemperature(uint16_t tempRaw, float vdda) { const float sensorVoltage = (static_cast(tempRaw) * vdda) / CONFIG_ADC_MAX_VALUE; return ((CONFIG_ADC_TEMP_V25 - sensorVoltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f; } void Adc::collectSample(Sample *sample) { if(sample == nullptr) return; sample->vin_raw = readVinRaw(); sample->temp_raw = readTempRaw(); sample->vref_raw = readVrefRaw(); sample->vdda = calculateVdda(sample->vref_raw); sample->vin_adc_v = calculateVinAdcVoltage(sample->vin_raw, sample->vdda); sample->temperature = calculateTemperature(sample->temp_raw, sample->vdda); }