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