nRF52xx 处理器中的ADC为一个逐次逼近的模拟数字转换器,所有nRF52xx 系列处理器的内部 ADC 称为 SAADC。
目录
nRF52xx SAADC基础介绍
nRF52xx 的结构图如下:
ADC对应管脚
采集信号输入通道 | 管脚名称 | 管脚标号 |
---|---|---|
AIN0 | P0.02 | 4 |
AIN1 | P0.03 | 5 |
AIN2 | P0.04 | 6 |
AIN3 | P0.05 | 7 |
AIN4 | P0.28 | 40 |
AIN5 | P0.29 | 41 |
AIN6 | P0.30 | 42 |
AIN7 | P0.31 | 43 |
VDD | VDD |
ADC的寄存器
和ADC有关的寄存器有点多,主要是通道多:
ADC的计算公式:
采样模式
通过CH[n].PSELP 和 CH[n].PSELN 设置输入通道:
增益 GAIN
在ADC参数配置 寄存器 CH[n].CONFIG (n = 0 ~7 )中的第 8 ~ 10 位 配置:
参考电压
和设置GAIN一个寄存器,在CH[n].CONFIG 的第 12位配置, 为 0 时,使用内部参考电压(0.6V),为1时,使用 VDD/4 作为参考电压。
采样精度
通过 RESOULUTION 寄存器 的第 0 ~ 4 位 设置, 0: 8bit 1:10bit 2:12bit 3: 14bit
通过以上寄存器的设置和算法,就可以成功使用ADC进行采集然后计算。此外,ADC还有不同的三种工作模式:单次转换模式,连续转换模式,扫描模式。
- 单次转换模式:通过CH[n].PSELP, CH[n].PSELN, CH[n].CONFIG 使能配置,通过CH[n].CONFIG.TACQ设置采样时间
- 连续转换模式:SAMPLERATE有关
- 扫描模式:如果超过一个通道被使能,就是扫描模式。
SAADC采样示例
ADC的例程都使用库函数开发,需要添加对应的库函数文件
SAADC单次单端采样模式示例:
/**
* @brief UART events handler.
*/
void saadc_callback(nrf_drv_saadc_evt_t const * p_event){
}
/*
saadc的初始化
*/
void saadc_init(void)
{
ret_code_t err_code;
/*
使用默认配置配置saadc,可以根据自己需求修改配置
#define NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE \
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE
* @brief Macro for setting @ref nrf_saadc_channel_config_t to default settings
* in single ended mode.
*
* @param PIN_P Analog input.
#define NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PIN_P) \
{ \
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, \
.resistor_n = 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, \
.burst = NRF_SAADC_BURST_DISABLED, \
.pin_p = (nrf_saadc_input_t)(PIN_P), \
.pin_n = NRF_SAADC_INPUT_DISABLED \
}
上面各项内容的解释:
NRF_SAADC_RESISTOR_DISABLED:这个是那个正端负端设置,怎么后面还有一个??
typedef enum
{
NRF_SAADC_RESISTOR_DISABLED = SAADC_CH_CONFIG_RESP_Bypass, ///< Bypass resistor ladder.
NRF_SAADC_RESISTOR_PULLDOWN = SAADC_CH_CONFIG_RESP_Pulldown, ///< Pull-down to GND.
NRF_SAADC_RESISTOR_PULLUP = SAADC_CH_CONFIG_RESP_Pullup, ///< Pull-up to VDD.
NRF_SAADC_RESISTOR_VDD1_2 = SAADC_CH_CONFIG_RESP_VDD1_2 ///< Set input at VDD/2.
} nrf_saadc_resistor_t;
NRF_SAADC_GAIN1_6:增益
typedef enum
{
NRF_SAADC_GAIN1_6 = SAADC_CH_CONFIG_GAIN_Gain1_6, ///< Gain factor 1/6.
NRF_SAADC_GAIN1_5 = SAADC_CH_CONFIG_GAIN_Gain1_5, ///< Gain factor 1/5.
NRF_SAADC_GAIN1_4 = SAADC_CH_CONFIG_GAIN_Gain1_4, ///< Gain factor 1/4.
NRF_SAADC_GAIN1_3 = SAADC_CH_CONFIG_GAIN_Gain1_3, ///< Gain factor 1/3.
NRF_SAADC_GAIN1_2 = SAADC_CH_CONFIG_GAIN_Gain1_2, ///< Gain factor 1/2.
NRF_SAADC_GAIN1 = SAADC_CH_CONFIG_GAIN_Gain1, ///< Gain factor 1.
NRF_SAADC_GAIN2 = SAADC_CH_CONFIG_GAIN_Gain2, ///< Gain factor 2.
NRF_SAADC_GAIN4 = SAADC_CH_CONFIG_GAIN_Gain4, ///< Gain factor 4.
} nrf_saadc_gain_t;
NRF_SAADC_REFERENCE_INTERNAL:参考电压
typedef enum
{
NRF_SAADC_REFERENCE_INTERNAL = SAADC_CH_CONFIG_REFSEL_Internal, ///< Internal reference (0.6 V).
NRF_SAADC_REFERENCE_VDD4 = SAADC_CH_CONFIG_REFSEL_VDD1_4 ///< VDD/4 as reference.
} nrf_saadc_reference_t;
NRF_SAADC_ACQTIME_10US: 转换时间
typedef enum
{
NRF_SAADC_ACQTIME_3US = SAADC_CH_CONFIG_TACQ_3us, ///< 3 us.
NRF_SAADC_ACQTIME_5US = SAADC_CH_CONFIG_TACQ_5us, ///< 5 us.
NRF_SAADC_ACQTIME_10US = SAADC_CH_CONFIG_TACQ_10us, ///< 10 us.
NRF_SAADC_ACQTIME_15US = SAADC_CH_CONFIG_TACQ_15us, ///< 15 us.
NRF_SAADC_ACQTIME_20US = SAADC_CH_CONFIG_TACQ_20us, ///< 20 us.
NRF_SAADC_ACQTIME_40US = SAADC_CH_CONFIG_TACQ_40us ///< 40 us.
} nrf_saadc_acqtime_t;
NRF_SAADC_MODE_SINGLE_ENDED:转换模式
typedef enum
{
NRF_SAADC_MODE_SINGLE_ENDED = SAADC_CH_CONFIG_MODE_SE, ///< Single ended, PSELN will be ignored, negative input to ADC shorted to GND.
NRF_SAADC_MODE_DIFFERENTIAL = SAADC_CH_CONFIG_MODE_Diff ///< Differential mode.
} nrf_saadc_mode_t;
NRF_SAADC_BURST_DISABLED:这个是啥?
typedef enum
{
NRF_SAADC_BURST_DISABLED = SAADC_CH_CONFIG_BURST_Disabled, ///< Burst mode is disabled (normal operation).
NRF_SAADC_BURST_ENABLED = SAADC_CH_CONFIG_BURST_Enabled ///< Burst mode is enabled. SAADC takes 2^OVERSAMPLE number of samples as fast as it can, and sends the average to Data RAM.
} nrf_saadc_burst_t;
(nrf_saadc_input_t)(PIN_P), :pin_p 输入通道,根据自己的引脚选择
typedef enum
{
NRF_SAADC_INPUT_DISABLED = SAADC_CH_PSELP_PSELP_NC, ///< Not connected.
NRF_SAADC_INPUT_AIN0 = SAADC_CH_PSELP_PSELP_AnalogInput0, ///< Analog input 0 (AIN0).
NRF_SAADC_INPUT_AIN1 = SAADC_CH_PSELP_PSELP_AnalogInput1, ///< Analog input 1 (AIN1).
NRF_SAADC_INPUT_AIN2 = SAADC_CH_PSELP_PSELP_AnalogInput2, ///< Analog input 2 (AIN2).
NRF_SAADC_INPUT_AIN3 = SAADC_CH_PSELP_PSELP_AnalogInput3, ///< Analog input 3 (AIN3).
NRF_SAADC_INPUT_AIN4 = SAADC_CH_PSELP_PSELP_AnalogInput4, ///< Analog input 4 (AIN4).
NRF_SAADC_INPUT_AIN5 = SAADC_CH_PSELP_PSELP_AnalogInput5, ///< Analog input 5 (AIN5).
NRF_SAADC_INPUT_AIN6 = SAADC_CH_PSELP_PSELP_AnalogInput6, ///< Analog input 6 (AIN6).
NRF_SAADC_INPUT_AIN7 = SAADC_CH_PSELP_PSELP_AnalogInput7, ///< Analog input 7 (AIN7).
NRF_SAADC_INPUT_VDD = SAADC_CH_PSELP_PSELP_VDD ///< VDD as input.
} nrf_saadc_input_t;
.pin_n = NRF_SAADC_INPUT_DISABLED 单端输入,这里DISABLED
*/
nrf_saadc_channel_config_t channel_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
/*
初始化saadc
__STATIC_INLINE ret_code_t nrf_drv_saadc_init(nrf_drv_saadc_config_t const * p_config,
nrf_drv_saadc_event_handler_t event_handler)
{
if (p_config == NULL)
{
static const nrfx_saadc_config_t default_config = NRFX_SAADC_DEFAULT_CONFIG;
p_config = &default_config;
}
return nrfx_saadc_init(p_config, event_handler);
}
NRFX_SAADC_DEFAULT_CONFIG: 默认配置
* @brief Macro for setting @ref nrfx_saadc_config_t to default settings.
#define NRFX_SAADC_DEFAULT_CONFIG \
{ \
.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION, \
.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE, \
.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY, \
.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE \
}
NRFX_SAADC_CONFIG_RESOLUTION:分辨率
#define NRFX_SAADC_CONFIG_RESOLUTION SAADC_CONFIG_RESOLUTION
// <0=> 8 bit
// <1=> 10 bit
// <2=> 12 bit
// <3=> 14 bit
#ifndef SAADC_CONFIG_RESOLUTION
#define SAADC_CONFIG_RESOLUTION 1
#endif
NRFX_SAADC_CONFIG_OVERSAMPLE: 这个是啥来着?反正就是
#define NRFX_SAADC_CONFIG_OVERSAMPLE SAADC_CONFIG_OVERSAMPLE
// <0=> Disabled
// <1=> 2x
// <2=> 4x
// <3=> 8x
// <4=> 16x
// <5=> 32x
// <6=> 64x
// <7=> 128x
// <8=> 256x
#ifndef SAADC_CONFIG_OVERSAMPLE
#define SAADC_CONFIG_OVERSAMPLE 0
#endif
NRFX_SAADC_CONFIG_IRQ_PRIORITY: 中断优先级这个简单
NRFX_SAADC_CONFIG_LP_MODE: 低功耗模式
NULL表示使用默认配置
*/
err_code = nrf_drv_saadc_init(NULL, saadc_callback);
APP_ERROR_CHECK(err_code);
/*
加入通道配置
#define nrf_drv_saadc_channel_init nrfx_saadc_channel_init
nrfx_err_t nrfx_saadc_channel_init(uint8_t channel,
nrf_saadc_channel_config_t const * const p_config)
所以 0 是通道名字,通道0 ,通道参数是上面的配置
*/
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code);
}
/**
* @brief Function for main application entry.
*/
int main(void)
{
nrf_saadc_value_t saadc_val;
float val;
uint32_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
ret_code_t ret_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(ret_code);
NRF_LOG_INFO("rSAADC HAL simple example.");
//saadc初始化
saadc_init();
while(1)
{
//启动一次ADC采样。
nrf_drv_saadc_sample_convert(0,&saadc_val);
/*
ADC采样值
根据公式分辨率是10,所以2^10 = 1024
saadc_val = val* 1/6 / 0.6 * 1024
*/
val = saadc_val * 3.6 /1024;
NRF_LOG_INFO(" %d mV",val*1000);
NRF_LOG_FLUSH();
//延时300msSAADC采样一次
nrf_delay_ms(300);
nrf_pwr_mgmt_run();
}
}
SAADC单次差分采样模式示例:
//...
void saadc_init(void)
{
ret_code_t err_code;
/*
使用的是2个通道的这个配置,其他都一样
#define NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(PIN_P, PIN_N) \
{ \
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, \
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, \
.gain = NRF_SAADC_GAIN1_6, \
.reference = NRF_SAADC_REFERENCE_INTERNAL, \
.acq_time = NRF_SAADC_ACQTIME_10US, \
.mode = NRF_SAADC_MODE_DIFFERENTIAL, \
.pin_p = (nrf_saadc_input_t)(PIN_P), \
.pin_n = (nrf_saadc_input_t)(PIN_N) \
}
*/
nrf_saadc_channel_config_t channel_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN2,NRF_SAADC_INPUT_AIN0);
//saadc性能参数不用变
err_code = nrf_drv_saadc_init(NULL, saadc_callback);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code);
}
/**
* @brief Function for main application entry.
*/
int main(void)
{
nrf_saadc_value_t saadc_val;
float val;
uint32_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
ret_code_t ret_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(ret_code);
NRF_LOG_INFO("rSAADC HAL simple example.");
saadc_init();
while(1)
{
//启动一次ADC采样。
nrf_drv_saadc_sample_convert(0,&saadc_val);
/*
串口输出ADC采样值
同样的参数,差分采样公司分辨率10 - 1 = 9
2^9 = 512;
测试的时候把AIN0 ,查表得知 P0.02 接GND
*/
val = saadc_val * 3.6 /512;
NRF_LOG_INFO(" %d mV",val*1000);
NRF_LOG_FLUSH();
//延时300ms
nrf_delay_ms(300);
}
}
SAADC EasyDMA 缓冲采样示例
对EasyDMA采样方式说明:
- EasyDMA作为adc采样的专用通道,负责把采样数据发送给 RAM 内的结果寄存器 RESULT
- 根据 RESULT 内的 RESULT.PTR 寄存器和 RESULT.MAXCNT 寄存器,分别决定了数据指针和这个数据指针的大小
- 只有填满这个数据指针内所有的空间,才能出发中断把转换数据读出。
因此整个转换次数 = 通道数 * buff 缓冲大小
SAADC EasyDMA单缓冲
EasyDMA单缓冲单通道中断采样示例:
/*
callback 函数的参数
#define nrf_drv_saadc_evt_t nrfx_saadc_evt_t
typedef struct
{
nrfx_saadc_evt_type_t type; ///< Event type.
union
{
nrfx_saadc_done_evt_t done; ///< Data for @ref NRFX_SAADC_EVT_DONE event.
nrfx_saadc_limit_evt_t limit; ///< Data for @ref NRFX_SAADC_EVT_LIMIT event.
} data;
} nrfx_saadc_evt_t;
typedef struct
{
nrf_saadc_value_t * p_buffer; ///< Pointer to buffer with converted samples.
uint16_t size; ///< Number of samples in the buffer.
} nrfx_saadc_done_evt_t;
typedef struct
{
uint8_t channel; ///< Channel on which the limit was detected.
nrf_saadc_limit_t limit_type; ///< Type of limit detected.
} nrfx_saadc_limit_evt_t;
*/
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
float val;
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);
//err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
int i;
NRF_LOG_INFO("ADC event number: %d\r\n",(int)m_adc_evt_counter);
for (i = 0; i < SAMPLES_IN_BUFFER; i++)
{
val = p_event->data.done.p_buffer[i] * 3.6 /1024;
NRF_LOG_INFO(" %d mV",val*1000);
}
m_adc_evt_counter++;
}
}
void saadc_init(void)
{
ret_code_t err_code;
nrf_saadc_channel_config_t channel_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
//saadc参数配置和上面样例一样
err_code = nrf_drv_saadc_init(NULL, saadc_callback);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code);
/*
配置申请一个缓冲区
#define SAMPLES_IN_BUFFER 1
static nrf_saadc_value_t m_buffer_pool[SAMPLES_IN_BUFFER];
#define nrf_drv_saadc_buffer_convert nrfx_saadc_buffer_convert
nrfx_err_t nrfx_saadc_buffer_convert(nrf_saadc_value_t * p_buffer, uint16_t size)
*/
err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool,SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
}
/**
* @brief Function for main application entry.
*/
int main(void)
{
uint32_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
ret_code_t ret_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(ret_code);
NRF_LOG_INFO("rSAADC HAL simple example.");
saadc_init();
while(1)
{
/*
启动一次ADC采样
启动后会进入回调函数
*/
nrf_drv_saadc_sample();
NRF_LOG_FLUSH();
nrf_delay_ms(300);
}
}
EasyDMA单缓冲双通道中断采样示例:
//...
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
float val;
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);
int i;
NRF_LOG_INFO("ADC event number: %d\r\n",(int)m_adc_evt_counter);
for (i = 0; i < SAMPLES_IN_BUFFER; i++){
val = p_event->data.done.p_buffer[i] * 3.6 /1024;
NRF_LOG_INFO(" %d mV",val*1000);
}
m_adc_evt_counter++;
}
}
void saadc_init(void)
{
//两个通道缓冲
ret_code_t err_code;
nrf_saadc_channel_config_t channel_0_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
nrf_saadc_channel_config_t channel_1_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
err_code = nrf_drv_saadc_init(NULL, saadc_callback);
APP_ERROR_CHECK(err_code);
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);
/*
#define SAMPLES_IN_BUFFER 6
buff为6,通道采样顺序是1,2,1,2,1,2 各3次采样完毕才会触发回调函数
*/
err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool,SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
}
//..main函数和上面示例一样
SAADC EasyDMA双缓冲
EasyDMA双缓冲说明
启动采样后数据首先进入缓冲区1,缓冲区1满了以后缓冲区1中的数据自动进入缓冲区2,然后新来的数据重新又进入缓冲区1
当两个缓冲区都有数据的时候引发中断 ,中断中输出缓冲数据2的内容
EasyDMA双缓冲双通道中断采样示例:
//...
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
float val;
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);
int i;
NRF_LOG_INFO("ADC event number: %d\r\n",(int)m_adc_evt_counter);
for (i = 0; i < SAMPLES_IN_BUFFER; i++){
val = p_event->data.done.p_buffer[i] * 3.6 /1024;
NRF_LOG_INFO(" %d mV",val*1000);
}
m_adc_evt_counter++;
}
}
void saadc_init(void)
{
ret_code_t err_code;
nrf_saadc_channel_config_t channel_0_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
nrf_saadc_channel_config_t channel_1_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
err_code = nrf_drv_saadc_init(NULL, saadc_callback);
APP_ERROR_CHECK(err_code);
/*通道大于1,会自动进入扫描模式*/
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_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);
}
int main(void)
{ int i;
uint32_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
ret_code_t ret_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(ret_code);
NRF_LOG_INFO("rSAADC HAL example.");
saadc_init();
/*
6次循环,2个通道12次数据,
2个缓冲区都有6个数据,但是只能读出缓冲2中的数据,也就是数据只会打印一次
读取次数 * 2 = 数据数量
数据数量 / 6 = 缓冲区数量
能够输出数据的次数 = 缓冲区数量 - 1
如果不能整除,比如7次,14次数据,2个+缓冲区,就能够输出2次数据,因为两个缓冲区只要都有数据了就会发生中断
*/
for(i= 0; i< 7; i++)
{
nrf_drv_saadc_sample();
NRF_LOG_FLUSH();
nrf_delay_ms(300);
}
}
SAADC PPI EasyDMA双缓冲
使用PPI,开启定时器,定时采集ADC数据,除了上面例程介绍的初始化SAADC,使用双缓冲的知识以外,还要根据以前学习的定时器和PPI通道的设置,初始化一个定时器,配置好一个PPI通道,使得定时器的比较事件 与 SAADC的采集任务分别挂在PPI通道的2端,最后再开启PPI通道:
(在这个例程中,我们就放了完整的.c程序,因为到后期的综合实验,所使用到的外设模块越来越多,我们也可以了解和查看哪些.h文件是需要一点一点添加的)
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#define SAMPLES_IN_BUFFER 6
volatile uint8_t state = 1;
static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_IN_BUFFER];
static nrf_ppi_channel_t m_ppi_channel;
static uint32_t m_adc_evt_counter;
void timer_handler(nrf_timer_event_t event_type, void * p_context){
}
/*
PPI和定时器初始化
*/
void saadc_sampling_event_init(void)
{
ret_code_t err_code;
err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(err_code);
/*
定时器定时配置
*/
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
APP_ERROR_CHECK(err_code);
/*
最后一个参数是开不开中断,false是不开中断
*/
uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, 400);
nrf_drv_timer_extended_compare(&m_timer,
NRF_TIMER_CC_CHANNEL0,
ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
false);
nrf_drv_timer_enable(&m_timer); //到这里使能了定时器
/*
获取事件和任务的地址
PPI通道的2端是地址
*/
uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
NRF_TIMER_CC_CHANNEL0);
uint32_t saadc_sample_task_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);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
timer_compare_event_addr,
saadc_sample_task_addr);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
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);
// }
/*
saadc的Callback和上面例子一样
*/
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
float val;
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);
int i;
NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
for (i = 0; i < SAMPLES_IN_BUFFER; i++)
{
val = p_event->data.done.p_buffer[i] * 3.6 /1024;
NRF_LOG_INFO(" %d mV",val*1000);
}
m_adc_evt_counter++;
}
}
/*
saadc 初始化双缓冲,和以前一样
*/
void saadc_init(void)
{
ret_code_t err_code;
nrf_saadc_channel_config_t channel_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
err_code = nrf_drv_saadc_init(NULL, saadc_callback);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_channel_init(0, &channel_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);
}
/**
* @brief Function for main application entry.
*/
int main(void)
{
uint32_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
ret_code_t ret_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(ret_code);
saadc_init();
saadc_sampling_event_init();
// saadc_sampling_event_enable();
NRF_LOG_INFO("SAADC HAL simple example started.");
while (1)
{
// nrf_pwr_mgmt_run();
NRF_LOG_FLUSH();
}
}