Assignments for session 4


#1

Greetings everyone,

Here’s the reference video that was used in today’s class about interrupts. Go through it, it is quite informative.

There is one assignment for this session, to simulate a tachometer by pressing a button instead of the DOUT from the IR sensor.

  • Print the number of times the button on the Bluey board is pressed in say every 5 seconds.
    • This can be done by configuring a GPIOTE channel in Event mode, selecting BUTTON1 as the PSEL, LoToHi as the polarity (reference: config registers in page 162 of datasheet).
    • Enabling interrupt for EVENTS_IN[0] of GPIOTE.
    • Enabling and setting priority for the GPIOTE peripheral in the NVIC
    • Writing the counter task_count in the interrupt routine.
  • Next, instead of enabling interrupt of GPIOTE and writing its interrupt routine, one can use the PPI to send the event from GPIOTE on button press to the task_count of the counting timer.

If anyone is feeling adventurous, please go ahead with building an actual tachometer as demonstrated today.

Do get in touch with us if you want us to review your code anytime. Cheers!


#2

Tried my hands with codes. But output in vain!


#3

Don’t worry. Message me your code, I’ll take a look.


#4

Enabling interrupt for EVENTS_IN[0] of GPIOTE

static void gpio_init(void)
{
nrf_gpio_cfg_input(Record, NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_input(Play, NRF_GPIO_PIN_PULLUP);

NVIC_DisableIRQ(GPIOTE_IRQn);
NVIC_ClearPendingIRQ(GPIOTE_IRQn);

// Enable interrupt:
NVIC_SetPriority(GPIOTE_IRQn, 3); //optional: set priority of interrupt
NVIC_EnableIRQ(GPIOTE_IRQn);
NRF_GPIOTE->CONFIG[0] = (GPIOTE_CONFIG_POLARITY_HiToLo<< GPIOTE_CONFIG_POLARITY_Pos)
| (0x1C << GPIOTE_CONFIG_PSEL_Pos) //
| (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos;

NRF_GPIO->PIN_CNF[Record] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);

NRF_GPIO->PIN_CNF[Play] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
// Configure GPIOTE channel 0 to generate event on input pin low-to-high transition.
nrf_gpiote_event_config(0, Record, GPIOTE_CONFIG_POLARITY_Toggle);
}

/** @brief Function for handling the GPIOTE interrupt which is triggered on pin 0 change.
*/
void GPIOTE_IRQHandler(void)
{

/*
// Event causing the interrupt must be cleared.
if ((NRF_GPIOTE->EVENTS_IN[0] == 1) &&
(NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_IN0_Msk))
{

NRF_GPIOTE->EVENTS_IN[0] = 0;

}
*/
if(NRF_GPIOTE->EVENTS_IN[0] == 1)
{

        NRF_GPIOTE->EVENTS_IN[0] = 0;  //this is where you should set breakpoint to make sure CC[0] and cc[1] don't change while you are in breakpoint
}

if(NRF_GPIOTE->EVENTS_IN[1] == 1) //will run only if you intenset event 1
{

        NRF_GPIOTE->EVENTS_IN[1] = 0;  //this is where you should set breakpoint to make sure CC[0] and cc[1] don't change while you are in breakpoint
}

 if (nrf_gpio_pin_read(Record) == 0) {Record_Start = (!Record_Start);} //do something }

if (nrf_gpio_pin_read(Play) == 0) { Record_Play = (!Record_Play);}//do something }

}


#5

GPIOTE PPI Code

#include "boards.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "nrf_drv_gpiote.h"
#include "app_error.h"

#define GPIO_OUTPUT_PIN_NUMBER  BSP_LED_0     /**< Pin number for output. */
#define GPIO_INPUT_PIN_NUMBER   BSP_BUTTON_0  /**< Pin number for output. */

static void led_blinking_setup()
{
    uint32_t input_evt_addr;
    uint32_t gpiote_task_addr;
    nrf_ppi_channel_t ppi_channel;
    
    // Configure GPIOTE OUT task
    nrf_drv_gpiote_out_config_t output_config =
    {
        .init_state     = NRF_GPIOTE_INITIAL_VALUE_HIGH,
        .task_pin       = true,                                                                       \
        .action         = NRF_GPIOTE_POLARITY_TOGGLE
    };
    APP_ERROR_CHECK(nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER, &output_config));
    
    // Configure GPIOTE IN event
    nrf_drv_gpiote_in_config_t input_config = 
    {    
        .is_watcher     = false,
        .hi_accuracy    = true,
        .pull           = NRF_GPIO_PIN_PULLUP,
        .sense          = NRF_GPIOTE_POLARITY_TOGGLE       
    };
    APP_ERROR_CHECK(nrf_drv_gpiote_in_init(GPIO_INPUT_PIN_NUMBER, &input_config, NULL));
    
    // Get the instance allocated for both OUT task and IN event
    gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER);
    input_evt_addr = nrf_drv_gpiote_in_event_addr_get(GPIO_INPUT_PIN_NUMBER);

    // Get a PPI channel from the PPI pool
    APP_ERROR_CHECK(nrf_drv_ppi_channel_alloc(&ppi_channel));        
    // Tie the GPIOTE IN event and OUT task through allocated PPI channel
    APP_ERROR_CHECK(nrf_drv_ppi_channel_assign(ppi_channel, input_evt_addr, gpiote_task_addr));
    // Enable the allocated PPI channel
    APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(ppi_channel));    
    
    // Enable OUT task and IN event
    nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN_NUMBER);
    nrf_drv_gpiote_in_event_enable(GPIO_INPUT_PIN_NUMBER, false);    
}

/**
 * @brief Function for application main entry.
 */
int main(void)
{
    // Initialize PPI driver
    APP_ERROR_CHECK(nrf_drv_ppi_init());  
    
    // Initialize the GPIOTE driver
    APP_ERROR_CHECK(nrf_drv_gpiote_init());
    
    // Configure the PPI and GPIOTE to connect the button to the LED
    led_blinking_setup();  
    
    while (true)
    {
        // No need to do anything other than sleep, PPI/GPIOTE is self-driven
        __WFE();        
    }
}

#6

Set_Priority for GPIOTE via NVIC

void gpiote_init(app_sched_event_handler_t handler)
{
gpiote_interrupt_callback = handler;
// Initialize GPIOTE interrupt (will not be enabled until app_gpiote_user_enable() is called).
NRF_GPIOTE->INTENCLR = 0xFFFFFFFF;
NVIC_ClearPendingIRQ(GPIOTE_IRQn);
NVIC_SetPriority(GPIOTE_IRQn, APP_IRQ_PRIORITY_HIGH);
NVIC_EnableIRQ(GPIOTE_IRQn);
}

void gpiote_enable_pin(uint32_t pin,bool rising_edge)
{
NRF_GPIOTE->EVENTS_PORT = 0;
NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
NRF_GPIO->PIN_CNF[pin] &= ~GPIO_PIN_CNF_SENSE_Msk;
if (rising_edge) NRF_GPIO->PIN_CNF[pin] |= GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos;
else NRF_GPIO->PIN_CNF[pin] |= GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos;
}

void gpiote_enable_port_interrupt()
{
NRF_GPIOTE->EVENTS_PORT = 0;
NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
}

void GPIOTE_IRQHandler(void) {
gpiote_disable_port_interrupt();

static uint32_t pins_state;
pins_state = NRF_GPIO->IN;

// Clear event.
NRF_GPIOTE->EVENTS_PORT = 0;
if (gpiote_interrupt_callback != NULL)
{
    uint32_t retval =    app_sched_event_put(&pins_state, sizeof(pins_state), gpiote_interrupt_callback);
    if (retval != NRF_SUCCESS)
    DBG(0,"GPIOTEIRQ: scheduled handler with reval %d\n",retval);
} }

#7

Can you please provide the Tachometer circuitry used. Also a flow chart for the code. I will try coding with your flow chart inputs.


#8

I want to try the Tachometer but I need some data points. Like coding flow chart from your end and circuitry and tools you used.

My questions:

1/ Since the RPM is continously need to be monitored I need to calculate the time period for one pulse and divide 60 by this period.

If the period of a pulse is period and a revolution is completed in say 6 pulses, the period of a revolution is period*6

This period in minutes would be (period*6)/60

Now one the RPM would be RPM=1/((period6)/60) or 60/(period6)

Is my calculation correct???

2/Start the timer on the rising edge and stop it at the next rising edge.
3/How often should I generate the interrupt?
4/What is the frequency of the application I am using?
5/Where can I find data about the internal clock signal ?
6/What am I supposed to calculate at each rising edge ?


#9

Hi @bsayee

Here’s the flow of steps that you need to do in essentially three functions - main, GPIOTE interrupt handler and repeat_handler. This is for the counting the number of button presses in 5 seconds. Try this first since you don’t have to build any additional circuitry, unlike a tachometer application (so no schematic yet for the tachometer).

Here’s what the main function would do:

  • Initialize the RGB LEDs as output and set them to the non-active state.
  • Initialize the printf with UART (either using uart_printf or hal_uart)
  • Initialize the high-frequency crystal and low-frequency clock with it crystal (refer to the Bluey demo)
  • Initialize the ms_timer module.
  • Initialize BUTTON_1 as input without pull-ups.
  • Configure a GPIOTE channel (say 0) with the following parameters
    • Set it in event mode
    • Specify a pin to get the GPIOTE input (BUTTON_1 in case you are counting button presses)
    • Configure the polarity of detection as low to high
    • Enable the interrupt for GPIOTE’s EVENTS_IN[0] using INTENSET register
    • Enable and set priority for GPIOTE’s interrupt with NVIC.
  • Initialize a timer as a counter and start it.
  • Start a ms_timer with repeated call, ticks for 5000 ms and the handler as repeat_handler
  • Run a while(1) loop where the only call is to sleep with __WFI()

Here’s what the GPIOTE interrupt handler GPIOTE_IRQHandler would do:

  • Clear the EVENTS_IN[0]
  • Trigger the task to count for the counter
  • print that the button is pressed with printf

Here’s what the repeat_handler would do:

  • Trigger the capture task for one of the CC registers of the timer configured as a counter.
  • Store the captured CC register in a variable
  • Clear the counter by triggering the clear task
  • Print the variable holding the value of the number of times the button was pressed.

Here are answers to your questions:

  1. Yes, your calculation is correct. The only thing is that if it takes 6 pulses per revolution, the propellor would have 6 fins (unlike the two fin one that we have).
  2. One timer is used as a counter to count the number of times the button is pressed (or the number of times the propellor crossed the IR sensor). And ms timer (or another timer) can be used for timekeeping.
  3. See the above flow of code.
  4. The ARM processor runs at 64 MHz, if this is what you mean by frequency of application.
  5. Chapter 19 in the datasheet.
  6. Rising edge of the button press should trigger an interrupt.

I see that you’ve put in some code from the nRF5x SDK provided by Nordic Semi. Always please make sure that the code indentation is correct and is within [code] and [/code]. Then it’ll format the code well as shown below.

#include "stdint.h"
int32_t hello_world = 0;

#10

I copied whatever we did in the last class - 2 timers, one as timer, another as counter, the only change i made is initialize the GPIOTE and have the controller handle its interrupt to update the counter.

void setupButton() {
tfp_printf(“Setting up Button \n”);
// configure button 1 as input
hal_gpio_cfg_input(BUTTON_1, HAL_GPIO_PULL_DISABLED);

//    NRF_P0 -> PIN_CNF[16].DIR =  GPIO_DIR_PIN16_Input << GPIO_DIR_PIN17_Pos; 
NRF_GPIOTE-> CONFIG[0] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
				0x10 << GPIOTE_CONFIG_PSEL_Pos | 
				GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos |
				GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos);

    // Enable interrupt on gpiote
NRF_GPIOTE -> INTENSET = GPIOTE_INTENSET_IN0_Enabled << GPIOTE_INTENSET_IN0_Pos ;

}

/**
Called when the controller handles interrupts from GPIOTE - which happens whenever the button is pressed
*/
void GPIOTE_IRQHandler(void) {
// task to do counting for a “counter” timer
NRF_TIMER3 ->TASKS_COUNT = 1;

// task to clear the "press" event for the button
NRF_GPIOTE -> EVENTS_IN[0] = 0;
tfp_printf("Button Press Event \n");

}

This is the same function that was used during our class
/**
Called when the controller handles the interrupt from Timer4 (which is a clock)
*/
void TIMER4_IRQHandler(void) {

tfp_printf("It is %d micro secs \n" , time_interval);

    NRF_TIMER4 -> TASKS_CLEAR = 1 ; // this has been handled by using a short for timer 4 reset timer 4 so that it will start counting from 0 to 10 secs again

    NRF_TIMER4 -> EVENTS_COMPARE[2] = 0 ; // reset the events register for timer 4 else the controller will think that the event needs to be handled over and over again

    // task to capture "counter" value into a register [1]
NRF_TIMER3 -> TASKS_CAPTURE[1] = 1;
count_click = NRF_TIMER3->CC[1]; // CC[1] - register where we have captured the timer value

    // task to reset/ clear "counter" timer so it starts from 1	
    NRF_TIMER3 -> TASKS_CLEAR = 1 ;

tfp_printf("Button was clicked %d times \n" , count_click);

}

int main(void)
{
// not realy using this
ptr_handler_receive_input = &handler_receive_input;

hal_uart_init(HAL_UART_BAUD_9600, ptr_handler_receive_input);
tfp_printf("3 This is to count the number of button press in 10 secs \n");

hfclk_xtal_init_blocking();
lfclk_init(LFCLK_SRC_Xtal);

setupButton();    

// Timer 3 as a counter - All of these are in nrf52.h - it can count each time button is pressed
NRF_TIMER3->MODE= TIMER_MODE_MODE_LowPowerCounter;
NRF_TIMER3->BITMODE= TIMER_BITMODE_BITMODE_08Bit;
NRF_TIMER3->TASKS_START=1  ;// in nrf52.h

// Timer 4 - as a timer - that will count 10 secs 
NRF_TIMER4->MODE= TIMER_MODE_MODE_Timer;
NRF_TIMER4->BITMODE= TIMER_BITMODE_BITMODE_32Bit;
NRF_TIMER4->PRESCALER=4  ;// 1 Mega Hz/ 
NRF_TIMER4-> CC[2]= time_interval ;//10000000 - 10 secs timer - 1 Mega Hz - 1 Micro Sec  

//Short to clear compare 2 position for timer 4 
NRF_TIMER4 -> SHORTS = TIMER_SHORTS_COMPARE2_CLEAR_Enabled << TIMER_SHORTS_COMPARE2_CLEAR_Pos;

// Enable interrupt on the timer on CC2, write to 18 bit (data sheet)
NRF_TIMER4-> INTENSET = TIMER_INTENSET_COMPARE2_Enabled << TIMER_INTENSET_COMPARE2_Pos;//nrf52_bitfields


// Enable interrupt on the controller for timer 
NVIC_EnableIRQ(TIMER4_IRQn);
NVIC_SetPriority(TIMER4_IRQn, APP_IRQ_PRIORITY_MID);

// Enable interrupt on the controller for GPIOTE through button click 
NVIC_EnableIRQ(GPIOTE_IRQn);
NVIC_SetPriority(GPIOTE_IRQn, APP_IRQ_PRIORITY_HIGH);


// start timer4 
NRF_TIMER4->TASKS_START=1  ;// in nrf52.h

while (true)
{
     __WFI();
} 

}