nRF52832芯片内置的SAADC(Successive Approximation Analog-to-Digital Converter)模块是开发者在物联网设备中实现模拟信号采集的核心外设。这个逐次逼近型ADC支持8个独立输入通道,最高可达14位分辨率,特别适合需要精确测量传感器信号的场景。在实际项目中,我经常用它来采集温度、压力、光照等模拟传感器的数据。
SAADC的硬件设计有几个关键特性值得注意:
寄存器配置是SAADC开发的基础,主要涉及以下几个关键寄存器组:
c复制// 典型通道配置寄存器示例
typedef struct {
uint32_t PSELP; // 正输入引脚选择
uint32_t PSELN; // 负输入引脚选择(差分模式使用)
uint32_t CONFIG; // 增益/参考电压/采集时间配置
uint32_t LIMIT; // 阈值限制设置
} nrf_saadc_channel_reg_t;
单次转换是最简单的采样方式,适合不频繁的采样需求。在最近的一个环境监测项目中,我就是用这种模式每5分钟采集一次温湿度数据。关键配置步骤如下:
c复制nrf_saadc_channel_config_t config = {
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.gain = NRF_SAADC_GAIN1_6,
.reference = NRF_SAADC_REFERENCE_INTERNAL,
.acq_time = NRF_SAADC_ACQTIME_10US,
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
.pin_p = NRF_SAADC_INPUT_AIN2
};
c复制nrf_saadc_value_t adc_value;
nrfx_saadc_sample_convert(0, &adc_value);
float voltage = adc_value * 0.6f / 4096 * (6.0f); // 计算实际电压值
当需要实时监控信号变化时,连续转换模式更为合适。但要注意这种模式会持续消耗较多功耗,我在电池供电设备中使用时通常会配合定时器控制采样时段。一个实用的配置技巧是:
c复制// 启用连续采样并设置采样率
nrf_saadc_continuous_mode_enable(500); // 500Hz采样率
// 在中断中处理数据
void saadc_handler(nrfx_saadc_evt_t const * event) {
if(event->type == NRFX_SAADC_EVT_DONE) {
// 处理采样数据
}
}
EasyDMA是nRF52832的特色功能,可以自动将ADC数据搬运到内存,极大减轻CPU负担。在开发心率监测设备时,单缓冲DMA模式帮我实现了稳定的数据流采集。具体实现需要注意:
c复制#define BUFFER_SIZE 256
static nrf_saadc_value_t m_buffer[BUFFER_SIZE];
// 初始化时配置DMA缓冲区
nrfx_saadc_buffer_convert(m_buffer, BUFFER_SIZE);
c复制void saadc_event_handler(nrfx_saadc_evt_t const * event) {
if(event->type == NRFX_SAADC_EVT_DONE) {
// 立即重新配置缓冲区
nrfx_saadc_buffer_convert(event->data.done.p_buffer, BUFFER_SIZE);
// 处理采集到的数据
process_adc_data(event->data.done.p_buffer, event->data.done.size);
}
}
对于高采样率应用,双缓冲模式能避免数据丢失。通过实测对比,在1kHz采样率下:
| 模式 | CPU占用率 | 最大稳定采样率 |
|---|---|---|
| 单缓冲 | 15% | 800Hz |
| 双缓冲 | 8% | 2kHz |
双缓冲配置的关键代码:
c复制// 双缓冲初始化
nrfx_saadc_buffer_convert(m_buffer[0], BUFFER_SIZE);
nrfx_saadc_buffer_convert(m_buffer[1], BUFFER_SIZE);
// 在中断中交替使用缓冲区
if(current_buffer == 0) {
nrfx_saadc_buffer_convert(m_buffer[0], BUFFER_SIZE);
process_data(m_buffer[1]);
} else {
nrfx_saadc_buffer_convert(m_buffer[1], BUFFER_SIZE);
process_data(m_buffer[0]);
}
PPI(Programmable Peripheral Interconnect)可以让外设间直接交互,完全不需要CPU参与。在开发低功耗无线传感器节点时,这个特性特别有用。典型配置流程:
c复制// 配置100ms间隔的定时器
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.frequency = NRF_TIMER_FREQ_1MHz;
nrf_drv_timer_init(&m_timer, &timer_cfg, NULL);
// 设置PPI通道
nrf_drv_ppi_channel_alloc(&m_ppi_channel);
nrf_drv_ppi_channel_assign(m_ppi_channel,
nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0),
nrf_drv_saadc_sample_task_get());
结合PPI和EasyDMA可以实现极低功耗的数据采集系统。在我的一个野外监测项目中,设备平均电流仅8μA:
系统工作流程:
关键功耗参数:
c复制// 进入低功耗模式前配置
nrf_saadc_low_power_enable();
nrf_drv_timer_enable(&m_timer);
nrf_drv_ppi_channel_enable(m_ppi_channel);
// 主循环中进入系统OFF模式
while(1) {
nrf_pwr_mgmt_run();
}
在多个实际项目中,我遇到过各种ADC采样异常情况。以下是几个典型问题及解决方案:
信号抖动严重:
基准电压不稳定:
c复制// 改用内部基准并增加去耦电容
nrf_saadc_channel_config_t config = {
.reference = NRF_SAADC_REFERENCE_INTERNAL,
// 其他配置...
};
c复制// 错误的中断处理方式(耗时操作)
void saadc_handler(...) {
complex_data_processing(); // 避免!
}
// 推荐方式
void saadc_handler(...) {
flag_data_ready = true; // 仅设置标志
}
c复制// 使用SDK提供的对齐宏
NRFX_SAADC_BUFFER_DEF(m_buffer, BUFFER_SIZE);
在实际调试中,我发现使用J-Link配合nRF Connect SDK的实时调试功能特别有用,可以实时观察ADC寄存器的变化和采样数据波形。