nRF52832 Timer+PPI+SPI 全硬件触发方式控制DAC,减少CPU干预,效率极大提升

版权声明:本文为博主原创文章,保留最终解释权,转载请注明出处。

【问题】:测试Timer每次通过PPI触发SPI写,SPI写完调用回调函数,回调函数中测试 NRF_SPIM2->TXD.PTR 的每次都是正常递增变化的,但是DAC就是没有实际的输出;
PS:1、要写的源数据测试是正确的;
2、为了不频繁占用CPU资料,用的PPI,用上后 timer+PPI+SPI(DMA) 写DAC 一直没调通,不加PPI的timer+SPI 写DAC 是OK的;
【原因】:进一步测试发现CS始终为高电平,手动飞线拉低CS再放开,DAC才有输出,判断为CS控制异常原因导致,而nRF52832的硬件SPI没有CS功能,SPI发送前需要软件设置CS为低,而设计中采用的是PPI触发,全硬件运行,没有软件可以控制CS电平变化,所以,CS一直为高;
在这里插入图片描述
【解决方案】:
1、TIMER+PPI+SPI(DMA)& CS中,PPI的二级任务端点 设置成CS pin(使用GPIOTE功能实现)后,使Timer 通过PPI触发 CS拉低+ SPI写;
2、要使SPI传输完成后CS拉高,所以再加一个PPI,EEP为SPI 传输完成,TEP为CS IO口的电平toggle;

【源码】:

【执行函数顺序】
	1、Timer_Initial_DA_Freq();  
	2、DAC_SPI_Init();  
	3、Dac_PPI_Config();   

【其他功能函数】
 //控制 CS,因硬件PPI触发SPI,没有软件控制CS情况下,CS一直为高电平
void CS_Gpiote_Init(void)
{
	ret_code_t err_code;	
	//初始化GPIOTE程序模块
		if(GPIOTE_init==0)
	{
	err_code = nrf_drv_gpiote_init();
	APP_ERROR_CHECK(err_code);
	GPIOTE_init=1;
		printf("GPIOTE Init OK \r\n");
	}
	
	//定义GPIOTE输出初始化结构体,并对其成员变量赋值
	nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);  
	//初始化GPIOTE输出引脚,初始化时会分配一个GPIOTE通道
  err_code = nrf_drv_gpiote_out_init(DAC_SPI_SS, &config);
  APP_ERROR_CHECK(err_code);
	
	//使能引脚 所在GPIOTE通道的任务触发
	nrf_drv_gpiote_out_task_enable(DAC_SPI_SS);
	nrf_drv_gpiote_out_set(DAC_SPI_SS);
}

/****************************************************************************************
 * 描  述 : dac_ppi_config函数,这里我们通过库函数来编写PPI代码,需要注意的是PPI通道是由驱动函数分配的。
            调用nrf_drv_ppi_channel_alloc函数后,该函数会把第一个找到的空闲PPI通道分配给应用程序。
 * 入  参 : 无
 * 返回值 : 无
 ***************************************************************************************/
void Dac_PPI_Config(void)
{
	  uint32_t err_code = NRF_SUCCESS;

    //初始化PPI程序模块
	  err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    //分配PPI通道,注意PPI通道的分配是由驱动函数完成的,分配的通道号保存到my_ppi_channel
    err_code = nrf_drv_ppi_channel_alloc(&timer2DAC_ppi_channel);
    APP_ERROR_CHECK(err_code);
	  //分配PPI通道的EEP和TEP

	    err_code = nrf_drv_ppi_channel_assign(timer2DAC_ppi_channel,
                                          nrf_drv_timer_compare_event_address_get(&TIMER_DAC,NRF_TIMER_CC_CHANNEL0),
                                         nrf_drv_gpiote_out_task_addr_get(DAC_SPI_SS));

    APP_ERROR_CHECK(err_code);
	
		err_code = nrf_drv_ppi_channel_fork_assign(timer2DAC_ppi_channel,
                                         nrf_drv_spi_start_task_get(&DAC_spi));																				 																		 
    APP_ERROR_CHECK(err_code);
	
	  //使能PPI通道
	  err_code = nrf_drv_ppi_channel_enable(timer2DAC_ppi_channel);
    APP_ERROR_CHECK(err_code);	

	//SPI 结束触发 CS 拉高
		err_code = nrf_drv_ppi_channel_alloc(&CS_ppi_channel);
    APP_ERROR_CHECK(err_code);
		
	    err_code = nrf_drv_ppi_channel_assign(CS_ppi_channel,
                                          nrf_drv_spi_end_event_get(&DAC_spi),
										  nrf_drv_gpiote_out_task_addr_get(DAC_SPI_SS)
                                         );
    APP_ERROR_CHECK(err_code);

	  //使能PPI通道
	  err_code = nrf_drv_ppi_channel_enable(CS_ppi_channel);
    APP_ERROR_CHECK(err_code);	
	printf("PPI enable OK \r\n");
	
 nrf_drv_timer_enable(&TIMER_DAC);
printf("nrf_drv_timer_enable OK \r\n");
}

//初始化Timer定时器,设置正弦波频率函数
//Freq :输出的正弦波频率 Hz
//Num  :一周期正弦波点数
void Timer_Initial_DA_Freq(uint32_t Freq,uint16_t Num)
{
	  //定义定时器配置结构体,并使用默认配置参数初始化结构体
   nrf_drv_timer_config_t timer_cfg =NRF_DRV_TIMER_DEFAULT_CONFIG;
	  //初始化定时器,初始化时会注册timer_led_event_handler事件回调函数
    err_code = nrf_drv_timer_init(&TIMER_DAC, &timer_cfg, timer_DAC_event_handler);
    APP_ERROR_CHECK(err_code);
	  //定时时间(单位us)转换为ticks
    time_ticks = nrf_drv_timer_us_to_ticks(&TIMER_DAC, time_us);
			printf("time_us= %d,time_ticks= %d \r\n",time_us,time_ticks);
    //设置定时器捕获/比较通道及该通道的比较值,使能通道的比较中断
    nrf_drv_timer_extended_compare(
         &TIMER_DAC, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
}

//Timer事件回调函数
void timer_DAC_event_handler(nrf_timer_event_t event_type, void* p_context)
{
}

//基于nRF52832硬件SPI 函数 
void spi_event_handler(nrf_drv_spi_evt_t const * p_event)
{
			uint8_t i=0;
			DAC_COUNT++;
			if(DAC_COUNT==DAC_NUMBER)
			{
			DAC_COUNT=0;
			NRF_SPIM2->TXD.PTR=(uint32_t)&SPI2ArrayList;
			}			
}

//SPI initialization
void DAC_SPI_Init (void)
{
	  /* 初始化SPI2 */
	nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
    spi_config.mosi_pin = DAC_SPI_MOSI;
    spi_config.sck_pin  = DAC_SPI_SCK;
	CS_Gpiote_Init();
	spi_config.frequency= NRF_SPI_FREQ_8M;

    APP_ERROR_CHECK(nrf_drv_spi_init(&DAC_spi, &spi_config, spi_event_handler));
	DAC_SPIDMA_Init();
    printf("DAC_SPI_Init OK \r\n");
    nrf_delay_ms(500);     
}

//正弦函数表sine_wave16bit的数据转换,并关联SPI DMA缓存
void DAC_SPIDMA_Init (void)
{
	uint8_t i=0;
	uint8_t temp_H,temp_L;
	for(i=0;i<DAC_NUMBER;i++)    //格式转换
	{
	temp_H=(uint8_t)(sine_wave16bit[i]>>8);       //取高4位
	temp_L=(uint8_t)sine_wave16bit[i];     //取低8位
  SPI2ArrayList[i].buffer[0]=DAC_WriteDAC_CMD | temp_H;
	SPI2ArrayList[i].buffer[1]=temp_L;	
	}
	//关联SPI  DMA缓存	
	NRF_SPIM2->TXD.MAXCNT = 2;        
	NRF_SPIM2->RXD.MAXCNT = 2;
	NRF_SPIM2->TXD.LIST=1;
	NRF_SPIM2->TXD.PTR=(uint32_t)&SPI2ArrayList;   //SPI2ArrayList;  
}