Files
rt-thread/components/drivers/adc/adc.c
T
wdfk-prog 937be2c97d feat[ADC]: 新增 ADC V2 驱动支持 (#11440)
* feat[ADC V2]: add ADC V2 driver support

add ADC V2 core APIs, session state management, and sequence read support
add STM32 ADC V2 HAL backend with internal-channel and VREF handling
add per-series STM32 ADC V2 default configuration headers
wire ADC V2 Kconfig and SConscript integration for components and STM32 BSP drivers
keep ADC V2 mutually exclusive with the legacy ADC driver path

* feat[ADC V2]: add ADC V2 MSH special channel read support

add generic ADC V2 MSH commands for probing, configuring, raw reads, voltage reads, and sequence reads
add STM32 ADC V2 backend special commands for VREFINT, temperature sensor, and VBAT reads
add STM32 helper APIs for special logical channels, sampling time, resolution, and temperature calculation
wire ADC V2 MSH sources through Kconfig and SConscript

* feat[ADC V2]: add ADC V2 stream DMA support

add ADC V2 stream APIs with latest-frame and FIFO buffering policies
wire STM32 ADC V2 to circular DMA stream mode with per-instance Kconfig options
extend STM32 ADC config headers and DMA helpers for stream DMA configuration
add FinSH stream commands for start, read, cancel, and stop operations
handle Cortex-M7 cache-safe DMA buffers for STM32 stream sampling

* feat[ADC V2]: add timer trigger support for ADC V2 streams

add ADC V2 trigger configuration, validation, and lifecycle coordination
add timer update trigger support through clock timer TRGO controls
map STM32 ADC V2 timer update triggers to HAL external trigger selector fields
add Kconfig, SConscript, and MSH entries for ADC trigger setup and inspection

* feat[ADCV2]: add timer and comparator trigger selectors

add ADC V2 timer compare and analog comparator trigger configuration support
extend clock timer trigger config with compare event and channel fields
wire STM32 ADC external trigger selector mappings for TIM update, TIM compare, and COMP output
add FinSH trigger_set commands and Kconfig switches for timer and comparator trigger backends

* fix[ADC V2]: resolve ADC V1 compatibility Kconfig loop

* ci[ADC V2][stm32]: add ADC v2 peripheral config for stm32f407-rt-spark BSP
2026-07-07 08:59:22 +08:00

301 lines
8.4 KiB
C

/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-07 aozima the first version
* 2018-11-16 Ernest Chen add finsh command and update adc function
* 2022-05-11 Stanley Lwin add finsh voltage conversion command
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
#include <stdlib.h>
#define DBG_TAG "adc"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static rt_ssize_t _adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
rt_err_t result;
rt_size_t i;
struct rt_adc_device *adc = (struct rt_adc_device *)dev;
rt_uint32_t *value = (rt_uint32_t *)buffer;
for (i = 0; i < size; i++)
{
result = adc->ops->convert(adc, pos, value);
if (result != RT_EOK)
{
return 0;
}
value++;
}
return i;
}
static rt_err_t _adc_control(rt_device_t dev, int cmd, void *args)
{
rt_adc_device_t adc = (struct rt_adc_device *)dev;
rt_err_t result = -RT_ERROR;
if (cmd == RT_ADC_CMD_ENABLE && adc->ops->enabled)
{
result = adc->ops->enabled(adc, (rt_int8_t)(rt_base_t)args, RT_TRUE);
}
else if (cmd == RT_ADC_CMD_DISABLE && adc->ops->enabled)
{
result = adc->ops->enabled(adc, (rt_int8_t)(rt_base_t)args, RT_FALSE);
}
else if (cmd == RT_ADC_CMD_GET_RESOLUTION && adc->ops->get_resolution && args)
{
rt_uint8_t resolution = adc->ops->get_resolution(adc);
if (resolution != 0)
{
*((rt_uint8_t *)args) = resolution;
LOG_D("resolution: %d bits", resolution);
result = RT_EOK;
}
}
else if (cmd == RT_ADC_CMD_GET_VREF && adc->ops->get_vref && args)
{
rt_int16_t value = adc->ops->get_vref(adc);
if (value != 0)
{
*((rt_int16_t *)args) = value;
result = RT_EOK;
}
}
return result;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops adc_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
_adc_read,
RT_NULL,
_adc_control,
};
#endif
rt_err_t rt_hw_adc_register(rt_adc_device_t device, const char *name, const struct rt_adc_ops *ops, const void *user_data)
{
RT_ASSERT(ops != RT_NULL && ops->convert != RT_NULL);
rt_err_t result;
device->parent.type = RT_Device_Class_ADC;
device->parent.rx_indicate = RT_NULL;
device->parent.tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
device->parent.ops = &adc_ops;
#else
device->parent.init = RT_NULL;
device->parent.open = RT_NULL;
device->parent.close = RT_NULL;
device->parent.read = _adc_read;
device->parent.write = RT_NULL;
device->parent.control = _adc_control;
#endif
device->ops = ops;
device->parent.user_data = (void *)user_data;
result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);
return result;
}
rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_int8_t channel)
{
RT_ASSERT(dev);
rt_uint32_t value;
rt_err_t result;
result = dev->ops->convert(dev, channel, &value);
if (result != RT_EOK)
return 0;
return value;
}
rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_int8_t channel)
{
RT_ASSERT(dev);
rt_err_t result;
if (dev->ops->enabled != RT_NULL)
{
result = dev->ops->enabled(dev, channel, RT_TRUE);
}
else
{
result = -RT_ENOSYS;
}
return result;
}
rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_int8_t channel)
{
RT_ASSERT(dev);
rt_err_t result;
if (dev->ops->enabled != RT_NULL)
{
result = dev->ops->enabled(dev, channel, RT_FALSE);
}
else
{
result = -RT_ENOSYS;
}
return result;
}
rt_int16_t rt_adc_voltage(rt_adc_device_t dev, rt_int8_t channel)
{
RT_ASSERT(dev);
rt_uint32_t value;
rt_int16_t vref, voltage = 0;
rt_uint8_t resolution;
rt_err_t result;
/*get the resolution in bits*/
resolution = dev->ops->get_resolution(dev);
/*get the reference voltage*/
vref = dev->ops->get_vref(dev);
if (vref == 0)
goto _voltage_exit;
/*read the value and convert to voltage*/
result = dev->ops->enabled(dev, channel, RT_TRUE);
if (result != RT_EOK)
goto _voltage_exit;
result = dev->ops->convert(dev, channel, &value);
if (result != RT_EOK)
goto _voltage_exit;
result = dev->ops->enabled(dev, channel, RT_FALSE);
if (result != RT_EOK)
goto _voltage_exit;
voltage = value * vref / ((1 << resolution) - 1);
_voltage_exit:
return voltage;
}
#ifdef RT_USING_FINSH
static int adc(int argc, char **argv)
{
int value = 0;
rt_int16_t voltage = 0;
rt_err_t result = -RT_ERROR;
static rt_adc_device_t adc_device = RT_NULL;
char *result_str;
if (argc > 1)
{
if (!strcmp(argv[1], "probe"))
{
if (argc == 3)
{
adc_device = (rt_adc_device_t)rt_device_find(argv[2]);
result_str = (adc_device == RT_NULL) ? "failure" : "success";
rt_kprintf("probe %s %s \n", argv[2], result_str);
}
else
{
rt_kprintf("adc probe <device name> - probe adc by name\n");
}
}
else
{
if (adc_device == RT_NULL)
{
rt_kprintf("Please using 'adc probe <device name>' first\n");
return -RT_ERROR;
}
if (!strcmp(argv[1], "enable"))
{
if (argc == 3)
{
result = rt_adc_enable(adc_device, atoi(argv[2]));
result_str = (result == RT_EOK) ? "success" : "failure";
rt_kprintf("%s channel %d enables %s \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), result_str);
}
else
{
rt_kprintf("adc enable <channel> - enable adc channel\n");
}
}
else if (!strcmp(argv[1], "read"))
{
if (argc == 3)
{
value = rt_adc_read(adc_device, atoi(argv[2]));
rt_kprintf("%s channel %d read value is 0x%08X \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), value);
}
else
{
rt_kprintf("adc read <channel> - read adc value on the channel\n");
}
}
else if (!strcmp(argv[1], "disable"))
{
if (argc == 3)
{
result = rt_adc_disable(adc_device, atoi(argv[2]));
result_str = (result == RT_EOK) ? "success" : "failure";
rt_kprintf("%s channel %d disable %s \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), result_str);
}
else
{
rt_kprintf("adc disable <channel> - disable adc channel\n");
}
}
else if (!strcmp(argv[1], "voltage"))
{
if (argc == 3)
{
voltage = rt_adc_voltage(adc_device, atoi(argv[2]));
rt_kprintf("%s channel %d voltage is %d.%03dV \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), voltage / 1000, voltage % 1000);
}
else
{
rt_kprintf("adc convert voltage <channel> \n");
}
}
else
{
rt_kprintf("Unknown command. Please enter 'adc' for help\n");
}
}
}
else
{
rt_kprintf("Usage: \n");
rt_kprintf("adc probe <device name> - probe adc by name\n");
rt_kprintf("adc read <channel> - read adc value on the channel\n");
rt_kprintf("adc disable <channel> - disable adc channel\n");
rt_kprintf("adc enable <channel> - enable adc channel\n");
rt_kprintf("adc voltage <channel> - read voltage on the channel\n");
result = -RT_ERROR;
}
return RT_EOK;
}
MSH_CMD_EXPORT(adc, adc [option]);
#endif /* RT_USING_FINSH */