Issue on nputs capture of two pins simultaneously

Hi all

Thanks in advance for any hints you could give on this.
I would like to measure two pwm signals (frequency and duty cycle) on two input pins of the nrf52832.

board : PCA10040

SDK : nRF5_SDK_17.0.2_d674dde

softdevice : s132_nrf52_7.2.0_softdevice.hex

The pwm frequency is in the range 2.5k - 5.5kHz and the duty is 20-80%
For this target, I thought of using two input captures initialized as follow:

#define SIZE_ROWS		30
#define SIZE_COLS		2
#define	TON_COL			0			
#define	TOFF_COL		1	

typedef struct{
	uint8_t	ID;
	uint8_t	Pin;
	uint8_t	PPIEvent;
	uint8_t	PPITask;
	uint8_t	PPIChn;
	uint8_t GPioSense;
	uint8_t Led;
	uint32_t CaptTmrNew;
	uint32_t CaptTmrOld;
	uint32_t CaptTmrDiff;
	uint16_t CaptBuff[SIZE_ROWS][SIZE_COLS];
	uint8_t	 CaptIdx;
	uint8_t	 CaptCnt;
} SGN_in_s;

SGN_in_s SGN_In_1 = {
	.ID 			= 1,
	.Pin			= 22,
	.PPIEvent	= 0,
	.PPITask	= 3,
	.PPIChn		=	4,
	.GPioSense		=	NRF_GPIO_PIN_SENSE_LOW,
	.Led					= 30,
	.CaptTmrNew		= 0,
	.CaptTmrOld		= 0,
	.CaptTmrDiff	= 0,
	.CaptBuff = {	{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0}},
	.CaptIdx		= 0,
	.CaptCnt		= 0,
};
	
SGN_in_s	SGN_In_2 = {
	.ID 			= 2,
	.Pin			= 25,
	.PPIEvent	= 1,
	.PPITask	= 3,
	.PPIChn		=	4,
	.GPioSense		=	NRF_GPIO_PIN_SENSE_LOW,
	.Led			= 31,
	.CaptTmrNew		= 0,
	.CaptTmrOld		= 0,
	.CaptTmrDiff	= 0,
	.CaptBuff = {	{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0},
								{0,0},{0,0},{0,0},{0,0},{0,0}},
	.CaptIdx	= 0,
	.CaptCnt	= 0,
};

void Config_PPI(SGN_in_s * p_SGNIn)
{
	/* Configure the channel as the caller expects */
	NRF_GPIOTE->CONFIG[p_SGNIn->PPIEvent] = (GPIOTE_CONFIG_MODE_Event	<< GPIOTE_CONFIG_MODE_Pos)	|
											((uint32_t)p_SGNIn->Pin 	<< GPIOTE_CONFIG_PSEL_Pos)	|
											((uint32_t)NRF_GPIOTE_POLARITY_TOGGLE << GPIOTE_CONFIG_POLARITY_Pos);

	/* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
	__NOP();
	__NOP();
	__NOP();

	NRF_PPI->CH[p_SGNIn->PPIChn].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[p_SGNIn->PPIEvent];				// set PPI event 
	NRF_PPI->CH[p_SGNIn->PPIChn].TEP = (uint32_t)&NRF_TIMER1->TASKS_CAPTURE[p_SGNIn->PPITask]; 	// set PPI task
	NRF_PPI->CHEN |= (PPI_CHEN_CH_Enabled << p_SGNIn->PPIChn);		// set PPI channel

	/* Clear the event that appears in some cases */
	NRF_GPIOTE->EVENTS_IN[p_SGNIn->PPIEvent] = 0; 
	
	/* Init input capture sense */
	p_SGNIn->GPioSense = NRF_GPIO_PIN_SENSE_LOW;
	nrf_gpio_cfg_sense_input(p_SGNIn->Pin, NRF_GPIO_PIN_NOPULL, p_SGNIn->GPioSense);
}

void Init_CAPT(void)
{
	NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
	NVIC_EnableIRQ(GPIOTE_IRQn);
}

The two PWM signals can be present individually or simultaneously.
When at least one PWM is present, I have to take the measurements.


My idea would be to read the frequency and duty cycle values during the interrupt and process them in the main.
I have implemented the following code to perform the task.

void GPIOTE_IRQHandler(void)
{
	if(NRF_GPIOTE->EVENTS_PORT == 1)
	{
		//--------------
		if(NRF_GPIOTE->EVENTS_IN[0] == 1)
		{
			CaptEvent(&SGN_In_1);
			NRF_GPIOTE->EVENTS_IN[0] = 0;
		}
		//--------------
		if(NRF_GPIOTE->EVENTS_IN[1] == 1)
		{
			CaptEvent(&SGN_In_2);
			NRF_GPIOTE->EVENTS_IN[1] = 0;
		}
		//--------------
		NRF_GPIOTE->EVENTS_PORT = 0;
	}
}

void CaptEvent(SGN_in_s * SGNIn_p)
{
	// Toggle Led
	nrf_gpio_pin_toggle(SGNIn_p->Led);
	
	// Launch tmr capture and save value
	NRF_TIMER1->TASKS_CAPTURE[SGNIn_p->PPITask] = 1;
	SGNIn_p->CaptTmrNew = NRF_TIMER1->CC[SGNIn_p->PPITask];

	// Inc the event counter 
	SGNIn_p->CaptCnt++; // must be cleared after freq and duty check
	
	// Calc and save tmr values
	SGNIn_p->CaptTmrDiff = SGNIn_p->CaptTmrNew - SGNIn_p->CaptTmrOld;
	SGNIn_p->CaptTmrOld  = SGNIn_p->CaptTmrNew;
	
	if((SGNIn_p->CaptCnt) < SIZE_ROWS)
	{
		SGNIn_p->CaptCnt++;
		// Save tmr in buff
		if(SGNIn_p->GPioSense == NRF_GPIO_PIN_SENSE_LOW)
		{
			SGNIn_p->CaptBuff[SGNIn_p->CaptIdx][TON_COL] = SGNIn_p->CaptTmrDiff;
		}
		else if(SGNIn_p->GPioSense == NRF_GPIO_PIN_SENSE_HIGH)
		{
			SGNIn_p->CaptBuff[SGNIn_p->CaptIdx][TOFF_COL] = SGNIn_p->CaptTmrDiff;
		}
	}
	
	// Toggle input capture sense
	SGNIn_p->GPioSense = (SGNIn_p->GPioSense == NRF_GPIO_PIN_SENSE_LOW)? NRF_GPIO_PIN_SENSE_HIGH : NRF_GPIO_PIN_SENSE_LOW;
	nrf_gpio_cfg_sense_input(SGNIn_p->Pin, NRF_GPIO_PIN_NOPULL, SGNIn_p->GPioSense);
}

My problem is that when one pwm is present, if the second one is added, sometimes the GPIOTE_IRQHandler() interrupt stops entering and I am no longer able to perform the measurements. In this condition, in debug mode a could see that NRF_GPIOTE->EVENTS_PORT is equal zero while NRF_GPIOTE->EVENTS_IN [0] or NRF_GPIOTE->EVENTS_IN[1] is equal to 1. Sometimes, by deactivating and reactivating the pwm on the input pins, the re-recognition restarts.


What could be the cause of this behavior?

Am I forgetting to clear any input capture flags?


This does not happen if only a single pwm is active while the second is never active.

Commenting incorrectly (at least in my opinion) the control on NRF_GPIOTE->EVENTS_PORT in the interrupt subroutine, the code works inside the interrupt itself and never exits. This is exactly what I expected it to do. The measurement on the two input pwm signals are performed correctly but the code, remaining inside the interrupt, never performs the other subroutines called by the main.

Thanks in advance for your support

Cristian

Parents
  • Hi,

    I would think you could reduce this issue by clearing EVENTS_PORT before, so that you reduce the risk of missing an interrupt. So something like this:

    void GPIOTE_IRQHandler(void)
    {
    	if(NRF_GPIOTE->EVENTS_PORT == 1)
    	{
                    NRF_GPIOTE->EVENTS_PORT = 0;
    		//--------------
    		if(NRF_GPIOTE->EVENTS_IN[0] == 1)
    		{
    			CaptEvent(&SGN_In_1);
    			NRF_GPIOTE->EVENTS_IN[0] = 0;
    		}
    		//--------------
    		if(NRF_GPIOTE->EVENTS_IN[1] == 1)
    		{
    			CaptEvent(&SGN_In_2);
    			NRF_GPIOTE->EVENTS_IN[1] = 0;
    		}
    		//--------------
    	}
    }

    That is also how it is done in the SDK driver (see modules\nrfx\drivers\src\nrfx_gpiote.c for reference).

    You may see issue still sometimes though, so it could be that it would make sense to alternate between measuring the two input signals, rather that trying to measure both at the same time.

  • Hi Einar

    Thanks for your hints.

    As you suggested, I moved the clearing EVENT_PORT just after the EVENT_PORT check.

    Moreover, I tried to leaver the code running inside the interrupt until both EVENT_IN flags are both zero. This appears to reduce missed readings and any interruptions in measurements.

    void GPIOTE_IRQHandler(void)
    {
    	bool EvntInRun = true;
    	
    	if(NRF_GPIOTE->EVENTS_PORT == 1)
    		NRF_GPIOTE->EVENTS_PORT = 0;
    	
    	while(EvntInRun)
    	{
    		//--------------
    		if(NRF_GPIOTE->EVENTS_IN[0] == 1)
    		{
    			CaptEvent(&SGN_In_1);
    			NRF_GPIOTE->EVENTS_IN[0] = 0;
    		}
    		//--------------
    		if(NRF_GPIOTE->EVENTS_IN[1] == 1)
    		{
    			CaptEvent(&SGN_In_2);
    			NRF_GPIOTE->EVENTS_IN[1] = 0;
    		}
    		//--------------
    		//NRF_GPIOTE->EVENTS_PORT = 0;
    		
    		if((NRF_GPIOTE->EVENTS_IN[0] == 0) && (NRF_GPIOTE->EVENTS_IN[1] == 0))
    			EvntInRun	= false;
    	}
    }


    Kind regards
    Cristian
     
Reply
  • Hi Einar

    Thanks for your hints.

    As you suggested, I moved the clearing EVENT_PORT just after the EVENT_PORT check.

    Moreover, I tried to leaver the code running inside the interrupt until both EVENT_IN flags are both zero. This appears to reduce missed readings and any interruptions in measurements.

    void GPIOTE_IRQHandler(void)
    {
    	bool EvntInRun = true;
    	
    	if(NRF_GPIOTE->EVENTS_PORT == 1)
    		NRF_GPIOTE->EVENTS_PORT = 0;
    	
    	while(EvntInRun)
    	{
    		//--------------
    		if(NRF_GPIOTE->EVENTS_IN[0] == 1)
    		{
    			CaptEvent(&SGN_In_1);
    			NRF_GPIOTE->EVENTS_IN[0] = 0;
    		}
    		//--------------
    		if(NRF_GPIOTE->EVENTS_IN[1] == 1)
    		{
    			CaptEvent(&SGN_In_2);
    			NRF_GPIOTE->EVENTS_IN[1] = 0;
    		}
    		//--------------
    		//NRF_GPIOTE->EVENTS_PORT = 0;
    		
    		if((NRF_GPIOTE->EVENTS_IN[0] == 0) && (NRF_GPIOTE->EVENTS_IN[1] == 0))
    			EvntInRun	= false;
    	}
    }


    Kind regards
    Cristian
     
Children
No Data
Related