ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RT-Thread 微芯 SAMC21 平台 ADC 同步驱动(hal_adc_sync)原理与实战指南

2026/9/24 21:35:58 拓冰建站 浏览量
RT-Thread 微芯 SAMC21 平台 ADC 同步驱动(hal_adc_sync)原理与实战指南 RT-Thread 微芯 SAMC21 平台 ADC 同步驱动hal_adc_sync原理与实战指南【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址: https://gitcode.com/gh_mirrors/rt/rt-thread本指南围绕 RT-Thread 仓库中 bsp/microchip/samc21/bsp/hal/documentation/adc_sync.rst 文档展开系统讲解微芯 SAM C21 系列 MCU 上 ADC 同步驱动hal_adc_sync的核心概念、API 用法与底层实现。读者读完后将掌握 ADC 量化原理、差分/单端模式、分辨率与参考电压取舍、窗口监视、单次/自由运行两种转换模式并能结合仓库中的 HAL/HPL 源码与配置文件在 SAMC21 板上独立完成一次完整的模拟量采集。一、ADC 与模拟量数字化基础ADCAnalog-to-Digital Converter模数转换器的作用是把连续的模拟信号转换为数字值。其工作方式是将一个电压已知的参考信号量化为若干个等宽的档位每个档位对应 0 到该 ADC 位分辨率所能表示的最大值之间的一个数字。ADC 测量到的输入电压与这些档位逐一比较与之电压水平最接近的那个档位所对应的数字值即代表该模拟输入电压的数字编码。SAMC21 的 ADC 外围ADC0/ADC1对应 HRI 头文件 hri_adc_c21.h即遵循上述原理模拟输入经输入多路选择器MUXPOS/MUXNEG选通后与参考电压比较最终结果写入RESULT寄存器同步驱动通过 hpl_adc.c 中的_adc_sync_read_channel_data()读取该寄存器获得采样值uint16_t _adc_sync_read_channel_data(const struct _adc_sync_device *const device, const uint8_t channel) { (void)channel; return hri_adc_read_RESULT_reg(device-hw); }二、差分模式与单端模式通常 ADC 支持差分或单端两种工作模式SAMC21 的差分能力由CTRLC.DIFFMODE位控制差分模式Differential将两个信号 V 与 V- 相互比较数字值表示二者之间的相对电压差。当 V 输入低于 V- 时结果为负因此差分模式会牺牲一个比特给符号位。在 hpl_adc_config.h 中注释明确In differential mode, the voltage difference between the MUXPOS and MUXNEG inputs will be converted by the ADC.单端模式Single-ended仅将 V 与参考电压比较数字值只能为正但可以完整使用 ADC 的全部位宽。驱动提供adc_sync_set_channel_differential_mode()切换两种模式底层实现hpl_adc.c实质是置位或清除CTRLC.DIFFMODE位void _adc_sync_set_channel_differential_mode(struct _adc_sync_device *const device, const uint8_t channel, const enum adc_differential_mode mode) { (void)channel; if (ADC_DIFFERENTIAL_MODE_DIFFERENTIAL mode) { hri_adc_set_CTRLC_DIFFMODE_bit(device-hw); } else { hri_adc_clear_CTRLC_DIFFMODE_bit(device-hw); } }枚举定义见 hpl_adc_sync.henum adc_differential_mode { ADC_DIFFERENTIAL_MODE_SINGLE_ENDED 0, ADC_DIFFERENTIAL_MODE_DIFFERENTIAL };在差分模式下输入通道的选择通过adc_sync_set_inputs()同时指定正、负输入MUXPOS/MUXNEG。SAMC21 的负输入除 AIN0~AIN5 引脚外还可选择内部地0x18或 I/O 地0x19具体选项见 hpl_adc_config.h 的CONF_ADC_0_MUXNEG注释。三、分辨率精度与转换速度的权衡ADC 通常支持多种分辨率降低分辨率可以缩短转换时间但会损失精度。SAMC21 的CTRLC.RESSEL字段支持 12 位、16 位需开启平均、10 位、8 位四种分辨率见 hpl_adc_config.h 中CONF_ADC_0_RESSEL的取值表。驱动通过adc_sync_set_resolution()设置分辨率底层写入CTRLC.RESSELvoid _adc_sync_set_resolution(struct _adc_sync_device *const device, const adc_resolution_t resolution) { hri_adc_write_CTRLC_RESSEL_bf(device-hw, resolution); }分辨率同时决定每次转换结果的字节数adc_sync_read_channel()在读取前会调用_adc_sync_get_data_size()判断8 位分辨率返回 1 字节其余返回 2 字节hpl_adc.cuint8_t _adc_sync_get_data_size(const struct _adc_sync_device *const device) { return hri_adc_read_CTRLC_RESSEL_bf(device-hw) ADC_CTRLC_RESSEL_8BIT_Val ? 1 : 2; }这意味着调用adc_sync_read_channel()时buffer长度必须与数据宽度对齐否则触发断言ASSERT(!(length % data_size))。四、增益级扩展动态范围部分 ADC 在输入线上带有增益级gain stage用于扩展动态范围。默认增益通常为 x1即转换范围为 0V 到参考电压应用可通过修改增益来增大或缩小转换范围。以 samc21 示例工程 的默认配置为例其转换结果分辨率、参考源、预分频等均可在 Atmel Start 生成的配置头文件中调整。需要说明的是从 SAMC21 的 HPL 层实现看_adc_sync_set_channel_gain()当前为空实现(void)device, (void)channel, (void)gain;见 hpl_adc.c即 SAMC21 具体器件上该 API 不产生实际寄存器操作若在支持增益级的芯片如部分 SAM 系列上使用HAL 层 API 仍保持统一接口方便移植。五、窗口监视模式与阈值窗口模式window mode允许将转换结果与一组预定义阈值比较。应用可通过回调函数监测转换结果是否超过预定义阈值。SAMC21 的窗口监视由CTRLC.WINMODE字段与WINLT/WINUT两个阈值寄存器实现共支持 5 种模式见 hpl_adc_config.h 的CONF_ADC_0_WINMODE注释0x0无窗口模式0x1Mode 1结果高于下限阈值0x2Mode 2结果低于上限阈值0x3Mode 3结果位于上下限阈值之内0x4Mode 4结果位于上下限阈值之外驱动层提供三个相关接口hal_adc_sync.hint32_t adc_sync_set_window_mode(struct adc_sync_descriptor *const descr, const adc_window_mode_t mode); int32_t adc_sync_set_thresholds(struct adc_sync_descriptor *const descr, const adc_threshold_t low_threshold, const adc_threshold_t up_threshold); int32_t adc_sync_get_threshold_state(const struct adc_sync_descriptor *const descr, adc_threshold_status_t *const state);底层实现分别操作CTRLC.WINMODE、WINLT/WINUT寄存器阈值状态则读取中断标志WINMONvoid _adc_sync_set_window_mode(struct _adc_sync_device *const device, const adc_window_mode_t mode) { hri_adc_write_CTRLC_WINMODE_bf(device-hw, mode); } void _adc_sync_set_thresholds(struct _adc_sync_device *const device, const adc_threshold_t low_threshold, const adc_threshold_t up_threshold) { hri_adc_write_WINLT_reg(device-hw, low_threshold); hri_adc_write_WINUT_reg(device-hw, up_threshold); } void _adc_sync_get_threshold_state(const struct _adc_sync_device *const device, adc_threshold_status_t *const state) { *state hri_adc_get_interrupt_WINMON_bit(device-hw); }阈值类型adc_threshold_t为uint16_t见 hpl_adc_sync.h取值范围 0~65535对应CONF_ADC_0_WINLT/CONF_ADC_0_WINUT的配置范围。六、参考电压的选择ADC 通常支持多种参考电压既有内部参考也有外部参考且电压水平各异。参考电压直接影响转换精度选择时应覆盖模拟输入信号的完整范围且绝不能低于预期的最大输入电压——否则超出参考范围的输入将无法被正确量化。SAMC21 支持 6 种参考源见 hpl_adc_config.h 的CONF_ADC_0_REFSEL注释取值参考源说明0x0内部带隙参考Internal bandgap默认值0x11/1.6 VDDANA0x21/2 VDDANA仅 VDDANA 2.0V 时可用0x3外部参考 AExternal reference A0x4DAC 内部输出0x5VDDANA驱动通过adc_sync_set_reference()动态切换参考源底层实现_adc_set_reference_source()hpl_adc.c会先检查 ADC 是否使能若已使能则先清除CTRLA.ENABLE、写入新的REFCTRL.REFSEL值再重新使能确保切换参考源时外围处于安全状态static void _adc_set_reference_source(void *const hw, const adc_reference_t reference) { bool enabled hri_adc_get_CTRLA_ENABLE_bit(hw); hri_adc_clear_CTRLA_ENABLE_bit(hw); hri_adc_write_REFCTRL_REFSEL_bf(hw, reference); if (enabled) { hri_adc_set_CTRLA_ENABLE_bit(hw); } }七、两种转换模式单次与自由运行ADC 支持两种转换模式单次模式Single shot仅在应用触发时执行一次转换。由adc_sync_read_channel()内部调用_adc_sync_convert()写入SWTRIG.START位启动单次转换。自由运行模式Free running从被触发那一刻起持续进行转换直到应用显式停止。使用窗口监视时ADC 应设置为自由运行模式原文档明确要求。模式由enum adc_conversion_mode定义hpl_adc_sync.henum adc_conversion_mode { ADC_CONVERSION_MODE_SINGLE_CONVERSION 0, ADC_CONVERSION_MODE_FREERUN };adc_sync_set_conversion_mode()的底层实现hpl_adc.c对应操作CTRLC.FREERUN位void _adc_sync_set_conversion_mode(struct _adc_sync_device *const device, const enum adc_conversion_mode mode) { if (ADC_CONVERSION_MODE_FREERUN mode) { hri_adc_set_CTRLC_FREERUN_bit(device-hw); } else { hri_adc_clear_CTRLC_FREERUN_bit(device-hw); } }八、驱动特性一览结合原文档与源码hal_adc_sync驱动具备以下能力初始化与去初始化adc_sync_init/adc_sync_deinit支持多种转换模式单次转换或自由运行adc_sync_set_conversion_mode启动 ADC 转换adc_sync_read_channel内部触发读取转换结果adc_sync_read_channel配置参考源adc_sync_set_reference、分辨率adc_sync_set_resolution、输入通道adc_sync_set_inputs、差分模式adc_sync_set_channel_differential_mode、增益adc_sync_set_channel_gain、窗口模式与阈值adc_sync_set_window_mode/adc_sync_set_thresholds/adc_sync_get_threshold_state查询转换完成状态adc_sync_is_channel_conversion_complete查询驱动版本adc_sync_get_version当前返回0x00000001u见 hal_adc_sync.c九、典型应用场景原文档列出了三类典型应用均可在 SAMC21 及同类 SAM 平台上落地内部传感器测量如 MCU 内部温度传感器的电压/温度值测量。外部传感器测量如温度、湿度传感器的模拟输出信号采集。信号采样测量如正弦波、方波等信号的采样与分析。十、同步读取的底层流程解析同步驱动读通道的完整流程体现在 hal_adc_sync.c 的adc_sync_read_channel()中先获取数据宽度循环执行发起转换 → 轮询转换完成 → 读取结果 → 写入缓冲直到填满用户缓冲区int32_t adc_sync_read_channel(struct adc_sync_descriptor *const descr, const uint8_t channel, uint8_t *const buffer, const uint16_t length) { uint8_t data_size; uint16_t offset 0; ASSERT(descr buffer length); data_size _adc_sync_get_data_size(descr-device); ASSERT(!(length % data_size)); do { uint16_t result; _adc_sync_convert(descr-device); while (!_adc_sync_is_channel_conversion_done(descr-device, channel)) ; result _adc_sync_read_channel_data(descr-device, channel); buffer[offset] result; if (1 data_size) { buffer[offset 1] result 8; } offset data_size; } while (offset length); return offset; }其关键调用链如下同步路径无中断参与_adc_sync_convert()→ 写SWTRIG.START位启动转换_adc_sync_is_channel_conversion_done()→ 轮询中断标志RESRDY_adc_sync_read_channel_data()→ 读取RESULT寄存器16 位结果按小端序拆为两个字节写入缓冲区。由于采用轮询等待RESRDY标志该驱动是阻塞式的调用期间 CPU 被占用适合对时延不敏感、转换频率不高的场景。十一、HAL 与 HPL 两层驱动架构同步 ADC 驱动采用 Microchip 标准的 HAL/HPL 分层结构在 bsp/microchip/samc21/bsp/hal 下可清晰看到HAL 层对应用可见的 APIinclude/hal_adc_sync.h 声明、src/hal_adc_sync.c 实现。每个 API 内部先ASSERT校验描述符指针再调用对应的_adc_sync_*HPL 函数并统一返回ERR_NONE。描述符结构struct adc_sync_descriptor仅含一个struct _adc_sync_device device成员其中保存硬件实例指针hw。HPL 层硬件抽象include/hpl_adc_sync.h 声明、hpl/adc/hpl_adc.c 实现直接操作 HRI 寄存器。同时该文件也提供_adc_async_*、_adc_dma_*实现三种驱动同步/异步/DMA共享同一份初始化与配置逻辑。配置层Atmel Start 生成的 config/hpl_adc_config.h 通过CONF_ADC_0_*宏定义默认配置hpl_adc.c 中的ADC_CONFIGURATION(n)宏将这些宏展开为寄存器写入值构成静态配置表_adcs[]实例使能数量由CONF_ADC_0_ENABLE CONF_ADC_1_ENABLE决定硬件索引通过((uint32_t)hw - (uint32_t)ADC0) 10计算。初始化时_adc_sync_init()除了写入上述配置表还会读取芯片出厂校准熔丝ADC0_FUSES_BIASREFBUF/ADC0_FUSES_BIASCOMP写入CALIB寄存器随后依次写入CTRLB预分频、REFCTRL参考源、EVCTRL事件、INPUTCTRL输入选择、CTRLC分辨率/差分/自由运行/窗口、AVGCTRL、SAMPCTRL、WINLT/WINUT、GAINCORR/OFFSETCORR、DBGCTRL、SEQCTRL最后写CTRLA使能控制。十二、在 SAMC21 工程中使用同步 ADC仓库中 bsp/microchip/samc21 是一个 Atmel Start 生成的 BSP 工程其初始化与示例可直接参考1. 实例化与初始化drierv_init.c 中定义了全局描述符并完成初始化struct adc_sync_descriptor ADC_0; void ADC_0_PORT_init(void) { // Disable digital pin circuitry gpio_set_pin_direction(PA10, GPIO_DIRECTION_OFF); gpio_set_pin_function(PA10, PINMUX_PA10B_ADC0_AIN10); } void ADC_0_CLOCK_init(void) { hri_mclk_set_APBCMASK_ADC0_bit(MCLK); hri_gclk_write_PCHCTRL_reg(GCLK, ADC0_GCLK_ID, CONF_GCLK_ADC0_SRC | (1 GCLK_PCHCTRL_CHEN_Pos)); } void ADC_0_init(void) { ADC_0_CLOCK_init(); ADC_0_PORT_init(); adc_sync_init(ADC_0, ADC0, _adc_get_adc_sync()); }可见完整初始化包含三部分使能 ADC 外设时钟MCLK.APBCMASK与GCLK.PCHCTRL、配置引脚复用将 PA10 复用到ADC0_AIN10、调用adc_sync_init()绑定硬件实例ADC0。system_init()中会依次调用ADC_0_init()等各外设初始化函数。2. 读取采样值examples/driver_examples.c 给出了最简示例——先使能通道 0然后循环读取 2 字节采样结果void ADC_0_example(void) { uint8_t buffer[2]; adc_sync_enable_channel(ADC_0, 0); while (1) { adc_sync_read_channel(ADC_0, 0, buffer, 2); } }adc_sync_enable_channel()底层对应写CTRLA.ENABLE位hpl_adc.c 中_adc_sync_enable_channel()channel 参数在当前实现中被忽略因为 SAMC21 ADC 通过单使能位控制整个外设。3. 典型配置模板基于 hpl_adc_config.h一个完整可用的配置组合如下Atmel Start 图形化配置与头文件等效#define CONF_ADC_0_ENABLE 1 /* 使能 ADC0 实例 */ #define CONF_ADC_0_RESSEL 0x0 /* 12-bit 分辨率 */ #define CONF_ADC_0_REFSEL 0x0 /* 内部带隙参考 */ #define CONF_ADC_0_PRESCALER 0x0 /* 外设时钟 / 2 */ #define CONF_ADC_0_FREERUN 0 /* 单次转换模式 */ #define CONF_ADC_0_DIFFMODE 0 /* 单端模式 */ #define CONF_ADC_0_MUXPOS 0x0A /* 正输入选 AIN10 */ #define CONF_ADC_0_MUXNEG 0x19 /* 负输入选 I/O 地 */ #define CONF_ADC_0_WINMODE 0x0 /* 不使用窗口监视 */十三、依赖、并发与限制说明原文档对驱动属性给出如下结论结合源码可进一步印证依赖Dependencies仅依赖 ADC 硬件本身ADC0/ADC1外设及其时钟、引脚配置。并发ConcurrencyN/A——同步驱动采用阻塞轮询不依赖中断或互斥机制同一时刻由一个执行流独占转换流程。限制LimitationsN/A——就 HAL 层抽象而言无固有限制但实际使用时需注意轮询读取会阻塞 CPU且buffer长度必须与分辨率对应的数据宽度对齐见上文ASSERT(!(length % data_size))。已知问题与规避Known issues and workaroundsN/A——仓库当前未记录已知问题。需要留意的是虽然 SAMC21 的 HPL 层同时提供同步hal_adc_sync、异步hal_adc_async见 include/hal_adc_async.h与 DMA 三种接口但原文档主题为同步驱动异步路径含窗口回调、中断注册不在本文范围内。若需非阻塞采样或高频连续采集可参考异步/DMA 接口进行二次开发。【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址: https://gitcode.com/gh_mirrors/rt/rt-thread创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考