电流 :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、 样品存储为16位2的补数值微分和单端取样
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();
存在未解决问题:功耗问题,关闭单个通道问题????