This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Problems Implementing Workaround to get nrf52 to send an octet / one byte through SPI

I'm having trouble implementing the workaround that was mentioned in the following previous post: https://devzone.nordicsemi.com/question/55937/nrf52-sending-an-octet/

Thus, I've attached the relevant code that I have so far.

P.S. I'm a beginner so some things that may be obvious to you, might not be obvious to me. Thus I would greatly appreciate detailed answers! The more information, the better.

In my main.c file:

/**
 * @brief Work-around for transmitting 1 byte with SPIM.
 *
 * @param spim: The SPIM instance that is in use.
 * @param ppi_channel: An unused PPI channel that will be used by the
		workaround.
 * @param gpiote_channel: An unused GPIOTE channel that will be used by
		the workaround.
 *
 * @warning Must not be used when transmitting multiple bytes.
 * @warning After this workaround is used, the user must reset the PPI
		channel and the GPIOTE channel before attempting to transmit multiple
		bytes.
 */
void setup_workaround_for_ftpan_58(NRF_SPIM_Type *spim, 
							       uint32_t ppi_channel, 
							       uint32_t gpiote_channel){
	
    // Create an event when SCK toggles.
	NRF_GPIOTE->CONFIG[gpiote_channel] = 
		(GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | 
		(spim->PSEL.SCK << GPIOTE_CONFIG_PSEL_Pos) | 
		(GPIOTE_CONFIG_POLARITY_Toggle <<GPIOTE_CONFIG_POLARITY_Pos);

	// Stop the spim instance when SCK toggles.
	NRF_PPI->CH[ppi_channel].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[gpiote_channel];
	NRF_PPI->CH[ppi_channel].TEP = (uint32_t)&spim->TASKS_STOP;
	NRF_PPI->CHENSET = 1U << ppi_channel;
}

/**@brief Functions prepares buffers and starts data transfer to AS3911
 *
 * @param[in] p_tx_data     A pointer to a buffer TX.
 * @param[in] p_rx_data     A pointer to a buffer RX.
 * @param[in] len           A length of the data buffers.
 */
static void spi_send_recv(uint8_t * const p_tx_data,
                          uint8_t * const p_rx_data,
                          const uint16_t  len)
{
	SEGGER_RTT_WriteString(0, "Send and Receive info \n");
	
    if (len == 1){
		nrf_ppi_channel_t ppi_channel;
		uint32_t gpiote_task_addr;
		ret_code_t err_code;
			
		//initializes the GPIOTE channel so that SCK toggles
		nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
		err_code = nrf_drv_gpiote_out_init(SPIM0_SCK_PIN, &config);
		APP_ERROR_CHECK(err_code);	
	
		//allocates the first unused PPI Channel
		err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
		APP_ERROR_CHECK(err_code);
	
		//get the address of the GPIOTE task 
		//(is this is a GPIOTE channel??????)
		gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get	(SPIM0_SCK_PIN);
	
		//enable the PPI channel.
		err_code = nrf_drv_ppi_channel_enable(ppi_channel);
		APP_ERROR_CHECK(err_code);
	
		//enable the GPIOTE output pin task.
		nrf_drv_gpiote_out_task_enable(SPIM0_SCK_PIN);
			
		//Start transfer of data
		err_code = nrf_drv_spi_transfer(&m_spi_master, p_tx_data, len, p_rx_data, len);
		APP_ERROR_CHECK(err_code);
			
		//interrupt transfer of data so that only one byte is sent
		setup_workaround_for_ftpan_58(NRF_SPIM_Type *spim, 
									       ppi_channel, 
									       gpiote_task_addr);
																
		//disable the ppi channel
		err_code = nrf_drv_ppi_channel_disable(ppi_channel);
	
		//disable a GPIOTE output pin task.
		nrf_drv_gpiote_out_task_disable(SPIM0_SCK_PIN);
	}
	else{
		// Start transfer.
		uint32_t err_code = nrf_drv_spi_transfer(&m_spi_master, p_tx_data, len,         
                                                                            p_rx_data, len);
		APP_ERROR_CHECK(err_code);
	}
		
    nrf_delay_ms(delay);
}

/**@brief Function for sending 1 byte to AS3911
*
* @param[in] cmd is the register of the AS3911 that you want to write to
*/
void as3911ExecuteCommand(uint8_t cmd){
	uint8_t write_buffer[1];
	SEGGER_RTT_WriteString(0, "Entered Execute Command\n");
	write_buffer[0] = cmd | AS3911_CMD_MODE;
	spi_send_recv(write_buffer, NULL, 1);
}

int main(void){
	//0 is the terminal number where the text is output on the J-link RTT
	//These lines are meant for debugging purposes and are not necessary 
	SEGGER_RTT_WriteString(0, "Program Begin\n");			
		
	//INITIALIZING PERIPHERALS
	//Initialize UART, and board support
	uart_config();
	bsp_configuration();
		
	//Configure SPI interface
    nrf_drv_spi_config_t const config =
    {
        #if (SPI0_ENABLED == 1)
            .sck_pin  = SPIM0_SCK_PIN, 
            .mosi_pin = SPIM0_MOSI_PIN,
            .miso_pin = SPIM0_MISO_PIN,
            .ss_pin   = SPIM0_SS_PIN,
        
        #endif
        .irq_priority = APP_IRQ_PRIORITY_LOW, //tells computer what to prioritize over other things
				.orc          = 0x00,
        .frequency    = NRF_DRV_SPI_FREQ_1M,
        .mode         = NRF_DRV_SPI_MODE_1,
        .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,	
    };
		
	//Initialize SPI
	ret_code_t err_code = nrf_drv_spi_init(&m_spi_master, &config, spi_master_event_handler);
    APP_ERROR_CHECK(err_code);
		
	//initialize GPIOTE and PPI
    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);
		
	//VARIABLES
	uint8_t numBytesReg = 0x1D;
		
	//Infinite loop containing main functionality
	for(;;){
		if (m_transfer_completed)
		{
			m_transfer_completed = false;		
			as3911WriteRegister(numBytesReg, 0x01);
			as3911ExecuteCommand(0xC1);				
		}			
	}
}	

ATTACHMENTS: main.c nrf_drv_spi.c nrf_drv_config.h nrf_drv_gpiote.c nrf_drv_ppi.c

  • Hi,

    Thanks for uploading your code, I will have to look at this one tomorrow.

  • I looked into your code and there were few things you were doing wrong.

    1. You are using GPIOTE and PPI drivers and also accessing registers directly in setup_workaround_for_ftpan_58.

    2. You are configuring GPIOTE as task mode for sck changes, that is wrong, it should be in event mode that generates events with sck toggles.

    3. you are setting up the workaround after nrf_drv_spi_transfer

    4. you are disabling the workaround as soon as you return from nrf_drv_spi_transfer. You forgot that this function is asynchronous, which returns immediately even though the transfer is not started. You should disable the workaround once the transfer is complete in the callback.

    I will try to fix that function for you and upload the code here for reference.

  • Modified your code Update 28.01.16 : code fixed for this workaround to work correctly ...

    static void spi_master_event_handler(nrf_drv_spi_event_t event)
    {
        uint32_t err_code = NRF_SUCCESS;
        switch (event)
        {
            //if event is equal to NRF_DRV_SPI_EVENT_DONE, then this block is executed to check if data is valid
    			  case NRF_DRV_SPI_EVENT_DONE:   
                      
                  			//disable the ppi channel
    			err_code = nrf_drv_ppi_channel_disable(ppi_channel);
    	
    			//disable a GPIOTE output pin task.
    			nrf_drv_gpiote_out_task_disable(SPIM0_SCK_PIN);
                  
                // Check if data are valid.
                err_code = bsp_indication_set(BSP_INDICATE_RCV_OK);
                APP_ERROR_CHECK(err_code);
    
                // Inform application that transfer is completed.
                m_transfer_completed = true;
                break;
    				
    				//if event is not equal to NRF_DRV_SPI_EVENT_DONE, then nothing happens
            default:
                // No implementation needed.
                break;
        }
    }
    
    static void spi_send_recv(uint8_t * const p_tx_data,
                              uint8_t * const p_rx_data,
                              const uint16_t  len)
    {
    		//SEGGER_RTT_WriteString(0, "Send and Receive info \n");
    		uint32_t err_code;
        NRF_SPIM_Type * p_spim = m_spi_master.p_registers;
    	
        if (len == 1){
    			ret_code_t err_code;
    			
    			// initializes the GPIOTE channel so that SCK toggles generates events
    			nrf_drv_gpiote_in_config_t config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
    			err_code = nrf_drv_gpiote_in_init(SPIM0_SCK_PIN, &config, in_pin_handler);
    			APP_ERROR_CHECK(err_code);	
    	
    			//allocates the first unused PPI Channel
    			err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
    			APP_ERROR_CHECK(err_code);
    
                err_code = nrf_drv_ppi_channel_assign(ppi_channel,
                                              nrf_drv_gpiote_in_event_addr_get(SPIM0_SCK_PIN),
                                              (uint32_t)&p_spim->TASKS_STOP);
                APP_ERROR_CHECK(err_code);
    
    	
    			//enable the PPI channel.
    			err_code = nrf_drv_ppi_channel_enable(ppi_channel);
    			APP_ERROR_CHECK(err_code);
    	
    			//enable the GPIOTE output pin task.
    			nrf_drv_gpiote_in_event_enable(SPIM0_SCK_PIN, false);
    			
    			//Start transfer of data
    			err_code = nrf_drv_spi_transfer(&m_spi_master, p_tx_data, len, p_rx_data, len);
    			APP_ERROR_CHECK(err_code);
    			
    												
    			//uninitializes the gpiote channel
    			//the gpiote channel is represented by a 32-bit variable
    			//not sure if we need to unintitialize the pin, or if we can just disable it
    			//nrf_drv_gpiote_out_uninit(SPIM0_SCK_PIN);	
    		}
    		else{
    			// Start transfer.
    			err_code = nrf_drv_spi_transfer(&m_spi_master, p_tx_data, len, p_rx_data, len);
    			APP_ERROR_CHECK(err_code);
    		}
    		
        nrf_delay_ms(delay);
    }
    
  • you have to disable ppi and gpiote channels after the transfer is complete in the callback

    Also the function is now implementing the workaround using nrf_drivers. This should work

  • I'm having trouble with figuring out what should be included in the in_pin_handler(). Could you please clarify this. Furthermore, for some reason, the program will not run past the instruction err_code = nrf_drv_ppi_channel_assign(ppi_channel_workaround, nrf_drv_gpiote_in_event_addr_get(SPIM0_SCK_PIN), p_spim->TASKS_STOP); Currently, when I comment out the workaround, it does not affect anything so i have a feeling that the workaround is still not properly implemented.

Related