1. 程式人生 > >nRF52832 Timer+PPI+SPI 全硬體觸發方式控制DAC,減少CPU干預,效率極大提升

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、MCP4821_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;  
}