SAADC

本文详细介绍了一个集成在Nordic芯片中的模拟数字转换器(SAADC)模块的特点与使用方法,包括其支持的不同分辨率、通道配置、输入范围及通过任务触发采样的灵活性。此外,还深入解析了官方提供的例程,并探讨了如何利用TIMER+PPI+SAADC实现高效的数据采集。
AI助手已提取文章相关产品:

电流 :EasyDMA draws about 1.2 mA, and added to the 700 uASAADC current it sums to ~2 mA。和实测差不多



官方例程解析:TIMER + PPI +SAADC。

特征:

1、       支持8/10/12位分辨率,和14位过采样分辨率

2、       8通道:单端输入最多可同时配置8个通道 和 差分输入最多可同时配置4个通道,也可以混合配置使用。多通道适配扫描模式。

3、       输入范围:(0 to VDD)

4、       通过任务从软件或采样触发PPI通道采样频率充分的灵活性源从低功率32.768千赫RTC或更准确的1/16MHz计时器

5、       采样一个单通道可以一次性转换

6、       扫描模式 是依次采样一系列通道。样本t + t conv消渠道之间的延迟可能不同渠道根据用户配置的ack

7、       EasyDMA,可以直接把数据打到RAM内

8、       中断在单个样本和全缓冲事件

9、       样品存储为162的补数值微分和单端取样

10、   连续采样,无需外部定时器

11、   内部电阻串

12、   限制检查


nrf_drv_saadc_buffer_convert():

这个函数实际是用来配置EasyDMA用的,也就是指定打到内存的位置和限制,但并不会开启转换。并且EasyDMA的指针是双buffer形式的,可以在前一个在使用的情况下直接更新。

如果处理4个通道的数据,SAMPLES_IN_BUFFER=4。各个通道采集一次就产生中断处理。

如果处理1个通道的数据,SAMPLES_IN_BUFFER也可以是4。表示对这个通道采集4次后才产生一次中断处理



nrf_drv_saadc_sample()这个就是正经开启一次转换的函数了。

即使使用TIMER+PPI触发,结合EasyDMA来传输数据,依旧存在一个缺陷,不能循环方式缓存,导致最终还是需要CPU的参与来维持正常的转换,一定程度上会减少CPU睡眠时间比例和持续长度。而且,使用库函数似乎并没有办法关掉ADC的中断,初始化的event_handler必须提供,且每次转换完成都会产生中断,势必唤醒CPU。这样的话,就导致使CPU在ADC连续采集一段时间之后,再批处理数据不太好实现了。


//输入范围

Input range = (+- 0.6 V or +-VDD/4)/Gain

Input range = (VDD/4)/(1/4) = VDD

Input range = (0.6 V)/(1/6) = 3.6 V

//增益GAIN,对输入电压处理。例如测量输入电压最大有1.2V,输入范围只有0.6V,则增益GAIN可为NRF_SAADC_GAIN1_2。


//结果

RESULT = [V(P) – V(N) ] * GAIN/REFERENCE *  2^(RESOLUTION - m)

m=0 if CONFIG.MODE=SE, or m=1 if CONFIG.MODE=Diff.

SE :NRF_SAADC_MODE_SINGLE_ENDED       ,Diff:NRF_SAADC_MODE_DIFFERENTIAL

 //参考电压

Reference

SAADC_CH_CONFIG_REFSEL_Internal (0.6V)

SAADC_CH_CONFIG_REFSEL_VDD1_4   (VDD/4 )


//简单例程

#define SAMPLES_IN_BUFFER 3//缓存大小
static const nrf_drv_timer_t   m_timer1 = NRF_DRV_TIMER_INSTANCE(1);//使用了协议栈,不能用定时器0
static nrf_saadc_value_t       m_buffer_pool[2][SAMPLES_IN_BUFFER];//开辟双缓存
static nrf_ppi_channel_t        m_ppi_channel;//

//已禁止定时器中断,所以不用处理
void timer1_handler(nrf_timer_event_t event_type, void* p_context)
{
}

void saadc_sampling_event_init(void)
{
    ret_code_t err_code;
    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_timer_init(&m_timer1, NULL, timer1_handler);
    APP_ERROR_CHECK(err_code);
    /* setup m_timer for compare event every 400ms */
    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer1, 400);
    nrf_drv_timer_extended_compare(&m_timer1, NRF_TIMER_CC_CHANNEL1, ticks,                                   NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, false);
    nrf_drv_timer_enable(&m_timer1);
    //get timer and saadc address
    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer1, NRF_TIMER_CC_CHANNEL1);
    uint32_t saadc_sample_event_addr = nrf_drv_saadc_sample_task_get();
    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);//·ÖÅäppiͨµÀ
    APP_ERROR_CHECK(err_code);  
    /* assigning task and event endpoints to the PPI channel.*/
   err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_sample_event_addr);
    APP_ERROR_CHECK(err_code);
}

void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
    APP_ERROR_CHECK(err_code);
}
void saadc_sampling_event_disable(void)
{
ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
APP_ERROR_CHECK(err_code);
}

void saadc_callback(nrf_drv_saadc_evt_t const * p_event)

{

    int16_t adc_value1 = 0;
    int16_t adc_value2 = 0;
    int16_t adc_value3 = 0;

    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);

adc_value1 =  p_event->data.done.p_buffer[0];
adc_value2 =  p_event->data.done.p_buffer[1];
adc_value3 =  p_event->data.done.p_buffer[2];
adc_value1 = adc_value1*0.6; //Vref
adc_value1 = adc_value1*2; // gain
adc_value1 = adc_value1/2.048f; // +-2^11 X 1000  == mv
adc_value2 = adc_value2*0.6; //Vref
adc_value2 = adc_value2*2; // gain
adc_value2 = adc_value2/2.048f; // +-2^11 X 1000  == mv
adc_value3 = adc_value3*0.6; //Vref
adc_value3 = adc_value3*2; // gain
adc_value3 = adc_value3/4.096f; // +-2^12 X 1000  == mv

printf("adc_value1=%d  ",adc_value1);
printf("adc_value2=%d  ",adc_value2);
printf("adc_value3=%d\r\n",adc_value3);
    }

}

void saadc_init(void)
{
    ret_code_t err_code;
    nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; 

    //settings in differential mode.
    nrf_saadc_channel_config_t channel_0_config =
    NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN0,NRF_SAADC_INPUT_AIN1);
    channel_0_config.gain = NRF_SAADC_GAIN1_2;
    channel_0_config.reference = NRF_SAADC_REFERENCE_INTERNAL;

    //settings in differential mode.
    nrf_saadc_channel_config_t channel_1_config =
    NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN2,NRF_SAADC_INPUT_AIN3);
    channel_1_config.gain = NRF_SAADC_GAIN1_2;
    channel_1_config.reference = NRF_SAADC_REFERENCE_INTERNAL;

    //settings in single ended mode.
    nrf_saadc_channel_config_t channel_2_config =
    NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN5);
    channel_2_config.gain = NRF_SAADC_GAIN1_2;
    channel_2_config.reference = NRF_SAADC_REFERENCE_INTERNAL;

    //err_code = nrf_drv_saadc_init(NULL, saadc_callback);//settings in DEFAULT mode.
    err_code = nrf_drv_saadc_init( &saadc_config, saadc_callback );
    APP_ERROR_CHECK(err_code);

    //configures and enables the channel.
    err_code = nrf_drv_saadc_channel_init(0, &channel_0_config);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(1, &channel_1_config);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(2, &channel_2_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1],SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);
}

//初始化

    saadc_sampling_event_init();
    saadc_init();
    saadc_sampling_event_enable();


存在未解决问题:功耗问题,关闭单个通道问题????








您可能感兴趣的与本文相关内容

### saADC 连续模式配置与使用 saADC(Single-ended Analog-to-Digital Converter)是一种单端模数转换器,在嵌入式系统中广泛用于将模拟信号数字化以便进一步处理。对于 saADC 的连续模式配置和使用,以下是详细的说明。 #### 配置 saADC 连续模式的关键要素 为了启用 saADC 的连续模式并确保其正常工作,需要完成以下几个方面的设置: 1. **使能 saADC 模块** 在硬件初始化阶段,需通过寄存器操作或驱动函数启动 saADC 模块。这通常涉及向控制寄存器写入特定值以激活模块[^1]。 2. **配置采样间隔** 设置合适的采样时间间隔是实现连续模式的核心之一。可以通过修改 `SAADC_SAMPLE_INTERVAL` 寄存器来指定两次采样之间的延迟。此参数决定了 ADC 转换频率,因此应根据应用需求调整[^3]。 3. **设定触发源** 对于连续采集而言,可以选择软件触发或者外部事件作为每次新测量周期的起点。当采用自动循环方式时,则无需额外干预即可持续获取数据流[^2]。 4. **定义缓冲区大小及位置** 创建一个足够大的内存区域用来存储由 SAADC 返回的结果值集合;同时告知外设这些地址信息所在之处——即加载相应的描述符结构体成员变量至对应字段之中去指向实际分配出来的 RAM 块首址及其长度等属性值。 5. **中断与 DMA 支持** 如果希望减少 CPU 占用率可以考虑利用直接内存访问(DMA)技术来进行大批量的数据搬运动作而不需要频繁唤醒处理器参与其中的工作流程当中去执行简单的复制粘贴类任务而已了事罢了嘛~ 另一方面来说呢?也可以依靠异常机制通知应用程序层有关已完成某些预定条件下的具体情形如何啦!比如说每当有一组新的样本准备好可供读取的时候就产生一次IRQ请求让上位机知道现在已经有新鲜出炉的好东西等着被拿走咯! 6. **校准过程** 开始任何精确度要求较高的检测之前最好先做一下零点漂移补偿之类的前期准备工作哦~这样可以获得更加可靠稳定的最终输出数值表现效果哟~ ```c // 示例代码展示如何配置 nRF5 SDK 中的 SAADC 为连续模式 void configure_saadc_continuous_mode(void){ ret_code_t err_code; // 初始化 SAADC 外设 err_code = saadc_init(); APP_ERROR_CHECK(err_code); // 定义输入通道以及增益因子等相关选项... saadc_channel_config_t channel_conf; memset(&channel_conf, 0, sizeof(channel_conf)); channel_conf.gain = SAADC_GAIN1_6; // Gain setting. channel_conf.reference = SAADC_REFERENCE_INTERNAL;// Reference voltage source selection. channel_conf.acquisition_time = SAADC_ACQUISITION_TIME_40US; // Acquisition time. // 启动第一个通道 (假设连接到AIN0) err_code = saadc_channel_init(0,&channel_conf); APP_ERROR_CHECK(err_code); // 设定采样速率 和 缓冲区管理策略 ... uint8_t buffer[CONFIG_BUFFER_SIZE]; err_code = saadc_buffer_set(buffer,sizeof(buffer)/sizeof(uint16_t),true); // true 表示开启环形缓冲功能 APP_ERROR_CHECK(err_code); // 最后一步:切换成 CONTINUOUS MODE 并开始监听 IRQs 或者等待 DMA 请求到来~ err_code = saadc_start(SAADC_MODE_CONTINUOUS,NULL); APP_ERROR_CHECK(err_code); } ``` 以上就是关于如何针对 saADC 实现连续扫描工作的基本指导方针概述内容总结完毕之后接下来让我们来看几个可能感兴趣的相关话题讨论吧?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值