209 lines
5.4 KiB
C++
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" */
|