nRF52832学习记录(九、SAADC)

2023-11-10

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();
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

nRF52832学习记录(九、SAADC) 的相关文章

  • BES平台(恒玄) ANC调试笔记

    一 前言 最近比较忙 昨天更新了EQ 调试模块 今天就趁热打铁把ANC部分也写下 主要说一些基于恒玄平台2500的ANC 环境搭配 软件设置 和 常见问题分析 个人见解 有不足之处 敬请锤教 二 环境搭配 此处引用BES 原厂ANC调试指南
  • 蓝牙之九-AT命令

    AT命令用于HF协议 该命令使参考3GPP 27 007协议 以下是HFP规范 每个命令行只有一个命令 AG侧默认不回显命令 AG使用冗长的格式返回结果 以下字符将被用于AT命令和返回结果格式中
  • Dialog的IDE搭建systermView的方法步骤(DA1469X)

    1 背景 SystemView 是一个可以在线调试嵌入式系统的工具 它可以分析有哪些中断 任务执行了 以及这些中断 任务执行的先后关系 还可以查看一些内核对象持有和释放的时间点 比如信号量 互斥量 事件 消息队列等 这在开发和处理具有多个线
  • nRF52832 — 连接指定name、UUID、addr的蓝牙设备

    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX 作 者 文化人 XX 联系方式 XX 版权声明 原创文章 欢迎评论和转载 转载时能告诉我一声就最好了 XX 要
  • Matter (CHIP) 从入门到入土 (一)

    文章目录 前言 架构 规范 网络拓扑结构 Single network Star network topology matter 设备发现 using BLE using WIFI Using Existing IP bearing Net
  • 疑难杂症 解决Ubuntu16.04 蓝牙搜索不到附近设备 适用于18.04

    文章目录 1 问题描述 2 问题症结 3 解决办法 References 1 问题描述 打开蓝牙搜索设备无果 2 问题症结 终端下输入命令 dmesg grep i blue BCM Patch brcm BCM hcd not found
  • ESP-NOW无线通信

    本文由铁熊与默联合创作 在学习 Arduino 开发的过程中 无线通讯是我们学习道路上一道必过的坎 无线通讯摆脱了线材的束缚 使用更加灵活且通讯距离根据不同无线模块可达几十米甚至是数公里 常见的无线通讯方式有蓝牙 WiFi LoRa NB
  • Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现

    http blog csdn net wave 1102 article details 39271693 Android4 3 规范了BLE的API 但是直到目前的4 4 还有些功能不完善 在BLE协议中 有两个角色 周边 Periphe
  • 零基础开发蓝牙设备

    前言 现在几乎每个人的手机都具备蓝牙功能 所以如果你的硬件设备也具备蓝牙通信功能 那么便可以很容易和手机建立通信 从而具备IOT物联网属性 但我们也知道蓝牙Ble 目前已发展到5 2版本 协议极其复杂 并不是所有人都需要去详细了解它 我们更
  • Ble Mesh的Generic Model ID&Opcode

    MODEL ID 标准的为16为 自定义的Vendor Model ID为32位 Model Group Model Name SIG Model ID Generic Generic OnOff Server 0x1000 Generic
  • i12蓝牙耳机使用(小米手机)

    一 操作位置 操作点击按键在无用孔位和喇叭位之间 二 听音乐 项目 左耳 右耳 单击 暂停播放 暂停播放 双击 降低音量 提升音量 三击 小米手机打开小爱 小米手机打开小爱 长按 关耳机 关耳机 三 打电话 项目 打入 接听时 单击 接听
  • Uni-App开发BLE低功耗蓝牙流程

    Uni App开发BLE低功耗蓝牙步骤 示例文件已上传gitee https gitee com yan rookie uniapp bluetooth git 如果对你有记得点个赞哦 注 微信小程序同样适用 只需吧前缀uni 修改为wx
  • Android BLE学习笔记

    http blog csdn net xiaoyaoyou1212 article details 51854454 个人网站 http www xiaoyaoyou1212 com 欢迎吐槽围观 前言 本文主要描述Android BLE的
  • Bluetooth 蓝牙介绍(三):低功耗蓝牙BLE空口协议Ⅱ

    文章目录 前言 4 5 CONNECTION STATE 4 5 1 Connection events 4 5 2 Supervision timeout 4 5 3 Connection event transmit window 4
  • STM32+HC-05蓝牙模块学习与使用

    HC 05蓝牙串口通信 HC05模块是一款高性能主从一体蓝牙串口模块 是一种集成蓝牙功能的PCBA板 用于短距离无线通信 十分方便 从某宝商家那里可以看到 蓝牙可以使用多种方法使用 这里我使用的是蓝牙主机连接 所以我们这里需要准备的器件 两
  • macbook bluetooth is not available (蓝牙不可用)

    新的MacBookPro Retina 一直没用过蓝牙 今天碰巧带了蓝牙耳机 连接的时候发现蓝牙的图标上面多了一个波浪线 显示 is not available google了几种方法 1 删除 Library Preferences 并重
  • Uniapp低功耗蓝牙操作实例

    uniapp低功耗蓝牙在移动端使用较为平常 本文相较于官方文档介绍一下低功耗蓝牙的操作案例 即取即用 低功耗蓝牙虽工作原理与经典蓝牙类似 但是有着独特的架构体系 所以LE独立出来成为一种蓝牙形态 不过LE和经典蓝牙使用相同的2 4G无线电频
  • NRF52832学习笔记(1)—— 添加自有service(基于SDK15.3)

    前言 SDK版本15 3 评估板 pca10040 在uart的例程中添加battery service 添加之前 手机连上设备之后扫描到的service如下 一 分配ram空间 softdevice的flash code是确定 但ram是
  • Re: Programming in C with Bluetooth Sockets

    Re Programming in C with Bluetooth Sockets This is the function run by the bluetooth device that will recieve the data c
  • Filco圣手二代键盘蓝牙连接方法

    键盘前面的电源按钮按进去 即打开电源开关 同时按下Ctrl Alt Fn 看到蓝灯和红灯同时亮起 之后剩蓝灯闪烁 按下小键盘中数字键1 4中的一个 一共可以连4台设备 如果你选的数字之前连接过其他设备 可以在第2步做完之后先按两秒清除按钮

随机推荐

  • Python 删除列表中的'\n'和空格

    要爬取的span标签下的价格730 用的是xpath获取内容 但爬取的结果为 把 i 标签以及后面的div也爬取下来了 导致这部分为 n 和空格 很显然 这不是我要的结果 想过先用xpath 再用正则表达式匹配数字 但一直提示类型不一致 因
  • 苏小红版 c语言程序设计(第三版)系列实验题:学生成绩管理系统V6.0

    github https github com Jackie0Feng SAMS 系统需求描述 某班有最多不超过30人 具体人数由键盘输入 参加期末考试 考试科目最多不超过6门 具体门数由键盘输入 学生成绩管理系统是一个非常实用的程序 如果
  • 深入探索C++中模板参数的自动推导

    深入探索C 中模板参数的自动推导 利用模板参数推导时需要注意以下几点 编译器只根据函数调用时给出的实参列表来推导模板参数值 与函数参数类型无关的模板参数其值无法推导 与函数返回值相关的模板参数其值也无法推导 所有可推导模板参数必须是连续位于
  • 函数的调用过程及其运行时堆栈解析

    为了较为深入的了解一下函数的调用过程 所以我在学习的过程中用一段简单的代码对其进行了研究 代码和调用堆栈如下 下图为上述函数的反汇编及调用过程
  • pyecharts 安装报错 ModuleNotFoundError: No module named 'pyecharts_snapshot'

    安装方法 pip install pyecharts 正常导入 import pyecharts 报错信息 ERROR lml utils failed to import pyecharts snapshot Traceback most
  • PostgreSQL常用命令使用

    1 连接数据库 psql U postgres psql U username d databse name h host W U 指定用户 d 指定数据库 h 要链接的主机 W 提示输入密码 2 切换数据库 c dbname 3 列举数据
  • 《单片机原理及应用》——概述

    系列文章目录 文章目录 系列文章目录 前言 一 计算机的位数是什么 二 51单片机 前言 学习视频链接是 https www bilibili com video BV1sE411L7j5 spm id from 333 337 searc
  • C# 引用类型作为函数参数时

    C 引用类型作为函数参数时 在探讨本文的主题之前 先来介绍下C 中的值类型和引用类型 众所周知C 中有值类型和引用类型 值类型有基础数据类型 诸如int double bool等 结构体 枚举 引用类型有接口 类 委托 值类型全部在操作系统
  • sqli-labs--Less7

    目录 一 Less 7 1 按照之前文中的思路 输入 id 1 发现提示 2 提示我们使用outfile函数 接下来需要做的就是研究outfile 的用法 以及寻找注入点 3 执行写入的文件 发现执行成功 一 Less 7 1 按照之前文中
  • 3. Python3 运算符

    Hi 大家好 我是茶桁 前两节我们学习了基本的Python特性和语法 并且认识了一些基本的Python脚本 今天 我们来学习一下Python的运算符 而我们选择的版本为Python3 什么是运算符 为了能让我们的学习顺利进行下去 首先我们需
  • 【编程之路】常见的排序算法(一)

    常见的排序算法 一 本文将介绍五种基础的排序算法 分别是 冒泡 选择 插入 快速 归并 1 冒泡排序 冒泡排序应该是入门级的排序算法了 class solution def sort arr self arr n len arr for i
  • 聊聊最近几年的路径追踪技术的进展(一)

    路径追踪技术 Path tracing PT 已经是当下工业中离线渲染使用的主流技术 不管是商业渲染器如皮克斯的RenderMan Solid Angle的Arnold等 还是迪士尼的in house渲染器Hyperion以及Weta Di
  • 与finally单独使用的是try

    try一般和catch搭配使用 捕获时可以没有catch块 但是此时必须有finally块 换言之try可以和catch finally两个中的一个搭配使用 但是catch和finally不能单独搭配使用
  • 如何正视自己的劣势?面试!

    面试真的是最直接面对自己劣势的方式 平时身边人看不清或者不愿意指出你的劣势 自己如果又不肯正视 那就会逐渐的积累成大问题 印象最深刻的是某小公司的技术终面 做的跟我完全同领域 面试官与我对切模型然后我败下阵来 临走的时候他说 我们公司还是想
  • 域名备案后修改服务器,域名备案后修改服务器

    域名备案后修改服务器 内容精选 换一换 PHPWind 简称 PW 是一个基于PHP和MySQL的开源社区程序 是国内较受欢迎的论坛之一 轻架构 高效易开发 使用户可快速搭建并轻松管理 本文档指导用户使用华为云市场镜像 PHPWind 论坛
  • 推荐系统(一)

    协同过滤 Collaborative Filtering A基于邻域的算法 B隐语义模型 C基于图的随机游走算法 A 基于邻域的算法 一 基于用户的协同过滤算法 UserCF 给用户推荐与其兴趣相似的其他用户喜欢的物品 1 首先找到与目标用
  • 几种常见的神经网络了解

    神经网络技术起源 感知机 神经网络技术起源于上世纪五 六十年代 当时叫感知机 perceptron 拥有输入层 输出层和一个隐含层 输入的特征向量通过隐含层变换达到输出层 在输出层得到分类结果 早期感知机的推动者是Rosenblatt 当时
  • 【单片机毕业设计】【mcuclub-309】衣柜除湿消毒

    设计简介 项目名 基于单片机的智能衣柜除湿消毒控制系统设计 标准版 基于单片机的衣柜环境监测 控制系统设计 标准版 基于单片机的多功能衣柜控制系统设计 标准版 单片机 STC89C52 功能简介 1 通过DHT11检测衣柜内温湿度 当湿度大
  • 常用正则表达式以及校验

    1 邮箱验证 判断邮箱格式是否正确 String ruleEmail w w w A Za z0 9 A Za z0 9 A Za z0 9 正则表达式的模式 编译正则表达式 Pattern p Pattern compile ruleEm
  • nRF52832学习记录(九、SAADC)

    nRF52xx 处理器中的ADC为一个逐次逼近的模拟数字转换器 所有nRF52xx 系列处理器的内部 ADC 称为 SAADC 目录 nRF52xx SAADC基础介绍 SAADC采样示例 SAADC EasyDMA 缓冲采样示例 SAAD