NRF52840学习历程(七)ADC模拟数字转换器_02PPI模组无CPU干扰多通道采样

时间在2021年2月2日,寒假放假在家好好学一学nRF52840

 

开发板:初雪的100出头那块 NRF52840 EVAL KIT

下载工具:JINLK V11(最好是JLINK V9以上 也有人用JLINK OB也行,其他的下载器诸如STLINK,DAP不建议用)

版本号: KEIL5编程环境,CMSIS为5.3.0, NRF52840的CMSIS为8.35.0

参考资料: NRF52840-Eval-Kit-Schematic.pdf(原理图)

nRF5_SDK_17.0.2_d674dde(官方例程)

nRF5_SDK_17.0.0_offline_doc(官方文档)

nRF52840_PS_v1.1.pdf(官方数据手册)

 

 

这章为ADC, 有两个实验,都为一个工程

1) 光敏电阻

2) 滑动变阻器,也叫电位器 (摇杆,X和Y轴)

 

 

SAADC介绍

nrf52840 的 ADC 为逐次逼近(successive-approximation)模拟数字转换器

 

SAADC 有三种工作模式,分别为:单次转换模式,连续转换模式,扫描模式

 

ADC引脚为固定引脚,无法remap重映射

ADCx(0~7)

对应引脚

ADC0

2

ADC1

3

ADC2

4

ADC3

5

ADC4

28

ADC5

29

ADC6

30

ADC7

31

 

 

添加驱动文件

 

 

..\..\..\..\..\..\modules\nrfx\drivers\src\nrfx_saadc.c

..\..\..\..\..\..\integration\nrfx\legacy\nrf_drv_ppi.c

添加SDK_CONFIG代码

其中不能使用低功耗模式,我试了会不运行

 

 

看原理图

光敏电阻为P0.05

摇杆 VRX接在P0.28,VRY接在P0.29, 摇杆里面的VCC接在了3,3V,GND接在GND,按键不接

 

IO5 -> ADC3  光敏

IO28 -> ADC4  摇杆X轴

IO29 -> ADC5  摇杆Y轴

 

 

添加头文件

#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"


#define ADC_Number 3 //有3个ADC
static nrf_saadc_value_t       m_buffer_pool[ADC_Number];

static const nrf_drv_timer_t ADC_TIMER = NRF_DRV_TIMER_INSTANCE(0); //绑定定时器0
static nrf_ppi_channel_t     adc_ppi_channel;
#define ADC_SCAN_TIME 500 //ADC按键扫描时间,单位ms

Adc配置代码:


	nrf_saadc_channel_config_t channel_config = //使用SDK_CONFIG那边的默认设置,通道为ADC3
				NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);				
				
	nrf_drv_saadc_init(NULL, My_ADCInterrupt); // 启动adc(相当于开启时钟)
	
	nrf_saadc_channel_init(0, &channel_config); //使用通道0(0~7任用), 把默认ADC写入寄存器中
	
	channel_config.pin_p  = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN4);
	nrf_saadc_channel_init(1, &channel_config);
	channel_config.pin_p  = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN5);
	nrf_saadc_channel_init(2, &channel_config);
	
	nrf_drv_saadc_buffer_convert(m_buffer_pool,ADC_Number); //给缓冲BUFF加入ADC_Number个ADC值

PPI模组配置代码:

	nrf_drv_ppi_init(); //启动PPI模组
//    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    nrf_drv_timer_init(&ADC_TIMER, &timer_cfg, MY_AdcTimer_handler);
	
	uint32_t ticks = nrf_drv_timer_ms_to_ticks(&ADC_TIMER, ADC_SCAN_TIME);
	nrf_drv_timer_extended_compare(&ADC_TIMER,
                                   NRF_TIMER_CC_CHANNEL0,
                                   ticks,
                                   NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                   false);
	nrf_drv_timer_enable(&ADC_TIMER);
	
	uint32_t timer_compare_event_addr = \
			nrf_drv_timer_compare_event_address_get(&ADC_TIMER,NRF_TIMER_CC_CHANNEL0);
	uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();
	
	nrf_drv_ppi_channel_alloc(&adc_ppi_channel);
	nrf_drv_ppi_channel_assign(adc_ppi_channel,
                                          timer_compare_event_addr,
                                          saadc_sample_task_addr);
										 
	nrf_drv_ppi_channel_enable(adc_ppi_channel);	

中断函数:

void My_ADCInterrupt(nrf_drv_saadc_evt_t const *p_event)
{
	float  val; 
	static uint32_t m_adc_evt_counter;
	
	if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
	{
		// 重新添加缓冲 个数为 ADC_Number个
		nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC_Number);


		printf("%2.3fV",p_event->data.done.p_buffer[0] * 3.6 /4096);
		printf("% 2.3fV",p_event->data.done.p_buffer[1] * 3.6 /4096);
		printf("% 2.3fV\r\n",p_event->data.done.p_buffer[2] * 3.6 /4096);
		
	}
}

完整代码:

#include "boards.h"

#include <stdbool.h>
#include <stdint.h>
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "nrf_drv_gpiote.h"

#include "nrf_uart.h"
#include "app_uart.h"
#include "nrf_drv_timer.h"

#include "nrf_drv_rtc.h"
#include "nrf_drv_clock.h"

#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"

uint32_t LED0,LED1,LED2,LED3;
uint32_t KEY0,KEY1,KEY2,KEY3;

void KEY_Interrupt(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action);


#define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */

void uart_interrupt(app_uart_evt_t * p_event);


const nrf_drv_timer_t My_Timer0 = NRF_DRV_TIMER_INSTANCE(4);
// 0~4共5个定时计数器
void My_Timer0_Interrupt(nrf_timer_event_t event_type,void  * p_context);



const nrf_drv_rtc_t rtc = NRF_DRV_RTC_INSTANCE(0); /**< Declaring an instance of nrf_drv_rtc for RTC0. */
void My_RTCInterrupt(nrfx_rtc_int_type_t int_type);

void My_ADCInterrupt(nrf_drv_saadc_evt_t const *p_event);

#define ADC_Number 3 //有3个ADC
static nrf_saadc_value_t       m_buffer_pool[ADC_Number];

static const nrf_drv_timer_t ADC_TIMER = NRF_DRV_TIMER_INSTANCE(0); //绑定定时器0
static nrf_ppi_channel_t     adc_ppi_channel;
#define ADC_SCAN_TIME 500 //ADC按键扫描时间,单位ms


void MY_AdcTimer_handler(nrf_timer_event_t event_type, void * p_context);

/**
 * @brief Function for application main entry.
 */
int main(void)
{
	nrf_drv_gpiote_in_config_t key_ex_config; //按键中断配置用
	
	LED0 =  NRF_GPIO_PIN_MAP(0,13);
	LED1 =  NRF_GPIO_PIN_MAP(0,14);
	LED2 =  NRF_GPIO_PIN_MAP(1,9);
	LED3 =  NRF_GPIO_PIN_MAP(0,16);

	KEY0 =  NRF_GPIO_PIN_MAP(0,11);
	KEY1 =  NRF_GPIO_PIN_MAP(0,24);
	KEY2 =  NRF_GPIO_PIN_MAP(0,20);
	KEY3 =  NRF_GPIO_PIN_MAP(0,17);

	nrf_gpio_cfg_output(LED0);
	nrf_gpio_cfg_output(LED1);
	nrf_gpio_cfg_output(LED2);
	nrf_gpio_cfg_output(LED3);

	nrf_gpio_pin_set(LED0);
	nrf_gpio_pin_set(LED1);
	nrf_gpio_pin_set(LED2);
	nrf_gpio_pin_set(LED3);
	
	nrf_gpio_cfg_input(KEY0,NRF_GPIO_PIN_PULLUP );
	nrf_gpio_cfg_input(KEY1,NRF_GPIO_PIN_PULLUP );
	nrf_gpio_cfg_input(KEY2,NRF_GPIO_PIN_PULLUP );
	nrf_gpio_cfg_input(KEY3,NRF_GPIO_PIN_PULLUP );
	
	nrf_drv_gpiote_init();//启动GPIOTE时钟,可以这么说
	
	key_ex_config.hi_accuracy=false; // 启用低精确度PORT事件
	key_ex_config.pull = NRF_GPIO_PIN_PULLUP ; //上啦
	key_ex_config.sense = NRF_GPIOTE_POLARITY_HITOLO ;//下降沿
	
	nrf_drv_gpiote_in_init(KEY0, &key_ex_config, KEY_Interrupt);
	nrf_drv_gpiote_in_init(KEY1, &key_ex_config, KEY_Interrupt);
	nrf_drv_gpiote_in_init(KEY2, &key_ex_config, KEY_Interrupt);
	nrf_drv_gpiote_in_init(KEY3, &key_ex_config, KEY_Interrupt);

	nrf_drv_gpiote_in_event_enable(KEY0, true);//启动KEY0中断
	nrf_drv_gpiote_in_event_enable(KEY1, true);//启动KEY1中断
	nrf_drv_gpiote_in_event_enable(KEY2, true);//启动KEY2中断
	nrf_drv_gpiote_in_event_enable(KEY3, true);//启动KEY3中断
	
	
	
    const app_uart_comm_params_t comm_params =
      {
          8,
          6,
          0,
          0,
          APP_UART_FLOW_CONTROL_DISABLED,
          false,
          NRF_UART_BAUDRATE_115200
      };
	uint32_t err_code;
    APP_UART_FIFO_INIT(&comm_params,
                         UART_RX_BUF_SIZE,
                         UART_TX_BUF_SIZE,
                         uart_interrupt,
                         APP_IRQ_PRIORITY_LOWEST,
                         err_code);
	
	nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;	//定义定时器结构体  
	timer_cfg.bit_width= NRF_TIMER_BIT_WIDTH_16 ;
	timer_cfg.frequency= NRF_TIMER_FREQ_1MHz;
	timer_cfg.interrupt_priority= 7;
	timer_cfg.mode=NRF_TIMER_MODE_TIMER ;
	  
	nrf_drv_timer_init(&My_Timer0, &timer_cfg,My_Timer0_Interrupt); 
	  
	uint32_t time_ticks ;
	time_ticks = nrfx_timer_ms_to_ticks(&My_Timer0, 1); //算出1ms需要计数多少次
	  
	nrf_drv_timer_extended_compare( //       time_ticks可以直接写1000
         &My_Timer0, NRF_TIMER_CC_CHANNEL5 , time_ticks, NRF_TIMER_SHORT_COMPARE5_CLEAR_MASK, true);  
/* 使用定时器实例0, CC通道0(定时器0可以用0~3), 比较时间为time_ticks次(1000次), 
	清除比较器CC0的任务, 开启定时器0CC通道 */

	nrf_drv_timer_enable(&My_Timer0);



	nrf_drv_clock_init(); // 开启时钟
	nrf_drv_clock_lfclk_request(NULL); // 请求低频时钟请求,没有配置事件中断

    nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG; //使用SDK_CONFIG那边的默认配置
	config.prescaler = 33; // 1/ (32768/33) = 1ms

	nrf_drv_rtc_init(&rtc, &config, My_RTCInterrupt); // 使用RTC0,写入结构体,中断函数
	nrf_drv_rtc_tick_enable(&rtc,true); //开启滴答定时器事件中断	
	nrf_drv_rtc_enable(&rtc); //启动RTC定时器

	

	nrf_saadc_channel_config_t channel_config = //使用SDK_CONFIG那边的默认设置,通道为ADC3
				NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);				
				
	nrf_drv_saadc_init(NULL, My_ADCInterrupt); // 启动adc(相当于开启时钟)
	
	nrf_saadc_channel_init(0, &channel_config); //使用通道0(0~7任用), 把默认ADC写入寄存器中
	
	channel_config.pin_p  = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN4);
	nrf_saadc_channel_init(1, &channel_config);
	channel_config.pin_p  = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN5);
	nrf_saadc_channel_init(2, &channel_config);
	
	nrf_drv_saadc_buffer_convert(m_buffer_pool,ADC_Number); //给缓冲BUFF加入ADC_Number个ADC值


	nrf_drv_ppi_init(); //启动PPI模组
//    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    nrf_drv_timer_init(&ADC_TIMER, &timer_cfg, MY_AdcTimer_handler);
	
	uint32_t ticks = nrf_drv_timer_ms_to_ticks(&ADC_TIMER, ADC_SCAN_TIME);
	nrf_drv_timer_extended_compare(&ADC_TIMER,
                                   NRF_TIMER_CC_CHANNEL0,
                                   ticks,
                                   NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                   false);
	nrf_drv_timer_enable(&ADC_TIMER);
	
	uint32_t timer_compare_event_addr = \
			nrf_drv_timer_compare_event_address_get(&ADC_TIMER,NRF_TIMER_CC_CHANNEL0);
	uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();
	
	nrf_drv_ppi_channel_alloc(&adc_ppi_channel);
	nrf_drv_ppi_channel_assign(adc_ppi_channel,
                                          timer_compare_event_addr,
                                          saadc_sample_task_addr);
										 
	nrf_drv_ppi_channel_enable(adc_ppi_channel);									 
	while(1)
	{		
//		printf("hello world! \r\n");		
	}
	
}

void KEY_Interrupt(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
	if(KEY0 == pin)
		nrf_gpio_pin_toggle(LED0);
	if(KEY1 == pin)
		nrf_gpio_pin_toggle(LED1);
	if(KEY2 == pin)
		nrf_gpio_pin_toggle(LED2);
	if(KEY3 == pin)
		nrf_gpio_pin_toggle(LED3);
}

void uart_interrupt(app_uart_evt_t * p_event)
{
	uint8_t dat;
    if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
    {
        APP_ERROR_HANDLER(p_event->data.error_communication);
    }
    else if (p_event->evt_type == APP_UART_FIFO_ERROR)
    {
        APP_ERROR_HANDLER(p_event->data.error_code);
    }
	
    else if (p_event->evt_type == APP_UART_DATA_READY)
	{//数据已到达串口 , 可以读数据了
		app_uart_get(&dat); //读取数据
		app_uart_put(dat); // 原路发回
	}
    else if (p_event->evt_type == APP_UART_TX_EMPTY) 
	{//发送完成
		//发送完成不知道要做什么的,可以点个灯提醒
		nrf_gpio_pin_toggle(LED0);
	}
}

void My_Timer0_Interrupt(nrf_timer_event_t event_type,
									void  * p_context) //1ms
{
	static uint16_t flag_500ms;
    switch (event_type)
    {
        case NRF_TIMER_EVENT_COMPARE5: // 匹配到了1ms的次数了
			if( ++flag_500ms == 500 ) //500ms
			{
				nrf_gpio_pin_toggle(LED1);
				flag_500ms = 0;
			}
            break;

        default:
            //Do nothing.
            break;
    }
}


void My_RTCInterrupt(nrfx_rtc_int_type_t int_type)
{
	static uint16_t flag500ms = 0;
	
    if (int_type == NRF_DRV_RTC_INT_COMPARE0) 
    { //无法恢复正常,只能使用1次,所以不要用比较了
//		nrf_gpio_pin_toggle(LED0);				
    }
	
    //滴答中断 这边是 1/ 32768 * 33 = 1ms
    else if (int_type == NRF_DRV_RTC_INT_TICK)
    {
		if( ++flag500ms == 500 )
		{
			nrf_gpio_pin_toggle(LED3);
			flag500ms = 0;
		}
    }
}


void My_ADCInterrupt(nrf_drv_saadc_evt_t const *p_event)
{
	float  val; 
	static uint32_t m_adc_evt_counter;
	
	if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
	{
		// 重新添加缓冲 个数为 ADC_Number个
		nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC_Number);


		printf("%2.3fV",p_event->data.done.p_buffer[0] * 3.6 /4096);
		printf("% 2.3fV",p_event->data.done.p_buffer[1] * 3.6 /4096);
		printf("% 2.3fV\r\n",p_event->data.done.p_buffer[2] * 3.6 /4096);
		
	}
}

void MY_AdcTimer_handler(nrf_timer_event_t event_type, void * p_context)
{

}

/**
 *@}
 **/

最后说明, ADC不能使用低功耗模式, 测试结果还算稳定

猜你喜欢

转载自blog.csdn.net/jwdeng1995/article/details/113576634