nRF52832学习记录(九、SAADC)

nRF52xx 处理器中的ADC为一个逐次逼近的模拟数字转换器,所有nRF52xx 系列处理器的内部 ADC 称为 SAADC。

nRF52xx SAADC基础介绍

nRF52xx 的结构图如下:
在这里插入图片描述
ADC对应管脚

采集信号输入通道管脚名称管脚标号
AIN0P0.024
AIN1P0.035
AIN2P0.046
AIN3P0.057
AIN4P0.2840
AIN5P0.2941
AIN6P0.3042
AIN7P0.3143
VDDVDD

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();
    }
}
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

矜辰所致

您们的鼓励是我奋斗的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值