Files
lishanpi/drv/adc/adc_driver.cpp
T
hm fc10ef2e10 Initial commit: LSPi (LiangShanPi) project
GD32F470ZGT6 based project with full peripheral support:
- SDRAM (W9825G6KH) via EXMC
- LCD NT35510 (480x800) via EXMC NOR/PSRAM
- Flash (W25Q64) via SPI
- UART (USART0) debug console
- LED indicators
- CMSIS-DAP debug interface
- CMake + ARM GCC toolchain build system
2026-04-26 16:24:42 +08:00

209 lines
5.4 KiB
C++

#include "adc_driver.h"
#include "gd32f4xx.h"
#include <cstddef>
struct adc_hw_config_t {
uint32_t adc_periph;
rcu_periph_enum rcu_clock;
};
static constexpr adc_hw_config_t adc_hw_map[] = {
{ADC0, RCU_ADC0},
{ADC1, RCU_ADC1},
{ADC2, RCU_ADC2},
};
static bool adc_clock_configured = false;
static float adc_resolution_to_max_value(uint32_t resolution)
{
if (resolution == ADC_RESOLUTION_12B) return 4095.0f;
if (resolution == ADC_RESOLUTION_10B) return 1023.0f;
if (resolution == ADC_RESOLUTION_8B) return 255.0f;
if (resolution == ADC_RESOLUTION_6B) return 63.0f;
return 4095.0f;
}
AdcBus::AdcBus(AdcPort port)
: port_(port)
{
}
AdcBus::~AdcBus()
{
if (initialized_) {
deinit();
}
}
RetCode AdcBus::init(const AdcConfig &config)
{
uint8_t port_idx = static_cast<uint8_t>(port_);
if (port_idx >= sizeof(adc_hw_map) / sizeof(adc_hw_map[0])) {
return RET_INVALID_PARAM;
}
const adc_hw_config_t &hw = adc_hw_map[port_idx];
if (hw.adc_periph == 0) {
return RET_NOT_SUPPORTED;
}
config_ = config;
rcu_periph_clock_enable(hw.rcu_clock);
if (!adc_clock_configured) {
adc_clock_config(config.clock_prescaler);
adc_clock_configured = true;
}
adc_resolution_config(hw.adc_periph, config.resolution);
adc_data_alignment_config(hw.adc_periph, ADC_DATAALIGN_RIGHT);
if (config.use_dma) {
adc_dma_mode_enable(hw.adc_periph);
}
adc_special_function_config(hw.adc_periph, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(hw.adc_periph, ADC_CONTINUOUS_MODE, DISABLE);
adc_enable(hw.adc_periph);
for (volatile uint32_t i = 0; i < 10000; i++);
adc_calibration_enable(hw.adc_periph);
if (config.use_interrupt) {
adc_interrupt_enable(hw.adc_periph, ADC_INT_EOC);
nvic_irq_enable(ADC_IRQn, 0, 0);
}
initialized_ = true;
return RET_OK;
}
RetCode AdcBus::deinit()
{
uint8_t port_idx = static_cast<uint8_t>(port_);
if (port_idx >= sizeof(adc_hw_map) / sizeof(adc_hw_map[0])) {
return RET_INVALID_PARAM;
}
const adc_hw_config_t &hw = adc_hw_map[port_idx];
adc_disable(hw.adc_periph);
rcu_periph_clock_disable(hw.rcu_clock);
initialized_ = false;
return RET_OK;
}
RetCode AdcBus::channel_config(uint8_t channel, uint8_t rank)
{
uint8_t port_idx = static_cast<uint8_t>(port_);
if (port_idx >= sizeof(adc_hw_map) / sizeof(adc_hw_map[0])) {
return RET_INVALID_PARAM;
}
if (!initialized_) return RET_NOT_INITIALIZED;
const adc_hw_config_t &hw = adc_hw_map[port_idx];
adc_channel_length_config(hw.adc_periph, ADC_ROUTINE_CHANNEL, rank + 1);
adc_routine_channel_config(hw.adc_periph, rank, channel, config_.sample_time);
return RET_OK;
}
RetCode AdcBus::start_conversion(uint8_t channel)
{
uint8_t port_idx = static_cast<uint8_t>(port_);
if (port_idx >= sizeof(adc_hw_map) / sizeof(adc_hw_map[0])) {
return RET_INVALID_PARAM;
}
if (!initialized_) return RET_NOT_INITIALIZED;
const adc_hw_config_t &hw = adc_hw_map[port_idx];
adc_routine_channel_config(hw.adc_periph, 0, channel, config_.sample_time);
adc_software_trigger_enable(hw.adc_periph, ADC_ROUTINE_CHANNEL);
return RET_OK;
}
uint16_t AdcBus::read()
{
uint8_t port_idx = static_cast<uint8_t>(port_);
if (port_idx >= sizeof(adc_hw_map) / sizeof(adc_hw_map[0])) {
return 0;
}
if (!initialized_) return 0;
const adc_hw_config_t &hw = adc_hw_map[port_idx];
while (RESET == adc_flag_get(hw.adc_periph, ADC_FLAG_EOC));
uint16_t value = static_cast<uint16_t>(adc_routine_data_read(hw.adc_periph));
adc_flag_clear(hw.adc_periph, ADC_FLAG_EOC);
return value;
}
float AdcBus::read_voltage()
{
uint16_t raw = read();
float max_val = adc_resolution_to_max_value(config_.resolution);
return static_cast<float>(raw) * 3.3f / max_val;
}
void adc_irq_handler()
{
if (adc_interrupt_flag_get(ADC0, ADC_INT_FLAG_EOC) != RESET) {
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOC);
}
if (adc_interrupt_flag_get(ADC1, ADC_INT_FLAG_EOC) != RESET) {
adc_interrupt_flag_clear(ADC1, ADC_INT_FLAG_EOC);
}
if (adc_interrupt_flag_get(ADC2, ADC_INT_FLAG_EOC) != RESET) {
adc_interrupt_flag_clear(ADC2, ADC_INT_FLAG_EOC);
}
}
extern "C" {
static AdcBus adc_0(AdcPort::_0);
static AdcBus adc_1(AdcPort::_1);
static AdcBus adc_2(AdcPort::_2);
static AdcBus *adc_instances[] = { &adc_0, &adc_1, &adc_2 };
static uint8_t adc_inited[3] = {0};
int adc_init(uint8_t port, uint32_t resolution)
{
if (port >= 3) return -1;
AdcConfig cfg;
cfg.resolution = resolution;
cfg.clock_prescaler = ADC_ADCCK_PCLK2_DIV8;
cfg.sample_time = ADC_SAMPLETIME_56;
RetCode ret = adc_instances[port]->init(cfg);
adc_inited[port] = (ret == RET_OK) ? 1 : 0;
return adc_inited[port] ? 0 : -1;
}
uint16_t adc_read(uint8_t port, uint8_t channel)
{
if (port >= 3 || !adc_inited[port]) return 0;
adc_instances[port]->channel_config(channel, 0);
adc_instances[port]->start_conversion(channel);
return adc_instances[port]->read();
}
float adc_read_voltage(uint8_t port, uint8_t channel)
{
if (port >= 3 || !adc_inited[port]) return 0.0f;
adc_instances[port]->channel_config(channel, 0);
adc_instances[port]->start_conversion(channel);
return adc_instances[port]->read_voltage();
}
} /* extern "C" */