52832 PPI+SPI移植

一、测试的平台

      本篇文章主要介绍将SPI功能移植到ble_app_hrs_pca10040_s132工程上。

整个测试平台如下

    环境:win10,64位,MDK集成开发环境.
    SDK:nRF5_SDK_15.2
    协议栈:s132_nrf52_6.1_softdevice.hex.
    硬件平台:pca10040开发板.

二、工作原理

        这里使用了SPI的PPI模式,能够极大的提高SPI的读写速度,减少MCU的干预,可以极大的节省功耗。当你对SPI的读写预先将NRF_SPIM0->TXD.PTR指向写buffer,将NRF_SPIM0->RXD.PTR指向读buffer。

       每次进行SPI读写的时候,指针会自动往后移,当数据的长度达到buffer的长度时,需要将NRF_SPIM0->TXD.PTR和NRF_SPIM0->TXD.PTR重新置位。

三、Application移植

1、添加相关C文件

      需要添加TIMER、PPI、SPI、GPIOTE四个外设的相关文件

      在工程中添加nrfx_ppi.c,nrf_drv_ppi.c,nrfx_spi.c,nrf_drv_spi.c,nrfx_spim.c,nrfx_gpiote.c这六个文件

2、添加头文件

       添加相关头文件

3、加入SPI相关代码

       SPI普通模式可以参考nRF5_SDK_15.2.0_9412b96\examples\peripheral\spi例程,在移植到协议栈中时,要注意在sdk_config.h中提高SPI的中断优先级,否则会无法进入中断

       SPI的PPI模式可以参照一下程序去编写:

     (1)SPI初始化

              在SPI的PPI模式中,只会调用到SCK,SIMO,SOMI这三个引脚。所以片选脚要自己去拉低拉高或者配置为GPIOTE模式,使用PPI去触发GPIOTE事件。

void ADC_SPIDMA_Init (void) //初始化SPI DMA相关参数
{ 
    NRF_SPIM0->TXD.MAXCNT = 2;//每次写两个字节 (16位寄存器)
    NRF_SPIM0->RXD.MAXCNT = 2;//每次读两个字节 (16位数据)
    NRF_SPIM0->TXD.LIST=1; //开启写DMA
    NRF_SPIM0->TXD.PTR=(uint32_t)&SPIWriteList; //指向写buffer
    NRF_SPIM0->RXD.LIST=1; //开启读DMA
    NRF_SPIM0->RXD.PTR=(uint32_t)&SPIReadList1; //指向读buffer 
}

void CS_Gpiote_Init(void) //初始化片选脚,一定要使用GPIOTE模式
{ 
    uint32_t err_code; 
	
    nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);  //设置为切换触发
    err_code = nrf_drv_gpiote_out_init(SPI_SS_PIN, &config); //设置GPIOTE引脚
    APP_ERROR_CHECK(err_code); // 
	
    nrf_drv_gpiote_out_task_enable(SPI_SS_PIN); //使能GPIOTE事件
    nrf_drv_gpiote_out_set(SPI_SS_PIN); //
}

void spi_init(void)
{
	nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
//	spi_config.ss_pin   = SPI_SS_PIN;
	spi_config.miso_pin = SPI_MISO_PIN;
	spi_config.mosi_pin = SPI_MOSI_PIN;
	spi_config.sck_pin  = SPI_SCK_PIN;
	APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event, NULL));

	CS_Gpiote_Init();
	ADC_SPIDMA_Init();
}

(2)SPI中断事件

         当数据达到指定长度时重新置位指针。

void spi_event(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
    spi_count++;
    if(spi_count==80)//buffer长度
    {
	spi_count=0; 
		
	NRF_SPIM0->TXD.PTR=(uint32_t)&SPIWriteList;//重新将指针指回起始地址
        NRF_SPIM0->RXD.PTR=(uint32_t)&SPIReadList1;//将指针指回起始地址
    }
}

       也可以加入开关控制,避免数据被覆盖。

      当检测到send_flag置1时,进行相关数据的读取,读完数据再调用SPI_start进行SPI的相关使能。

      中间可以调用SPI_stop失能SPI和定时器,这样可以节省大部分的功耗。

void spi_event(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
    spi_count++;
    if(spi_count==80)
    {
	nrf_drv_timer_pause(&m_timer1);//暂停PPI触发定时器
	spi_count=0; 
		
        send_flag = 1;
    }
}

void SPI_reset_buff(void)
{
    spi_count = 0;
    NRF_SPIM0->TXD.PTR=(uint32_t)&SPIWriteList;
    NRF_SPIM0->RXD.PTR=(uint32_t)&SPIReadList1;
}

void SPI_stop(void)
{
    nrf_drv_timer_disable(&m_timer1);
    nrf_spim_disable(NRF_SPIM0);
}

void SPI_start(void)
{
    SPI_reset_buff();//复位指针
    nrf_spim_enable(NRF_SPIM0);	//使能SPI
    nrf_drv_timer_enable(&m_timer1);//使能TIMER
}

(3)PPI事件

       这里一共有两个PPI事件,事件1是触发SPI开始以及GPIOTE,事件2是触发GPIOTE

void my_ppi_init(void)
{
    uint32_t err_code = NRF_SUCCESS;

    err_code = nrf_drv_ppi_init();//init PPI
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_timer_init(&m_timer1, NULL, timer_handler1);//init timer 1
    APP_ERROR_CHECK(err_code);

	/* setup m_timer for compare event every 15us */
    uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer1, 15);
    nrf_drv_timer_extended_compare(&m_timer1, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);//设置比较时间
    nrf_drv_timer_enable(&m_timer1);//使能定时器

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer1, NRF_TIMER_CC_CHANNEL0);
    uint32_t spi_start_event_addr = nrf_drv_spi_start_task_get(&spi);
    uint32_t spi_end_event_addr = nrf_drv_spi_end_event_get(&spi);
    uint32_t gpiote_out_event_addr = nrf_drv_gpiote_out_task_addr_get(SPI_SS_PIN);
	/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    err_code = nrf_drv_ppi_channel_alloc(&time_ppi_channel);
    APP_ERROR_CHECK(err_code);
	
    err_code = nrf_drv_ppi_channel_assign(time_ppi_channel, timer_compare_event_addr, gpiote_out_event_addr);//timer触发gpiote,片选脚拉低
    APP_ERROR_CHECK(err_code);
    err_code=nrf_drv_ppi_channel_fork_assign(time_ppi_channel,spi_start_event_addr);//timer触发spi
    APP_ERROR_CHECK(err_code);
	
    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, spi_end_event_addr, gpiote_out_event_addr);//spi结束触发gpiote,片选脚拉高
    APP_ERROR_CHECK(err_code);
	
    err_code = nrf_drv_ppi_channel_enable(time_ppi_channel);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
    APP_ERROR_CHECK(err_code);
}

 

你可能感兴趣的:(Nordic)