Monday, April 5, 2010

Beagleboard & QNX: GPIO Interrupts

This is a short guide on how to use the GPIO interrupts on the Beagleboard running QNX.

My setup:
Beagleboard C3
512MB SD boot
4-port USB 2.0 hub
linksys USB 1.1 100TX adapter (for debug through momentics IDE)

references required:
  1. Beagleboard technical reference - http://beagleboard.org/resources
  2. OMAP3530 techinical reference manual (TRM) - http://focus.ti.com/docs/prod/folders/print/omap3515.html
steps overview:
  1. change mux to get GPIO signals (/src/hardware/startup/boards/omap3530/init_pinmux.c)
  2. add interrupt triggers (edge/level)
  3. write interrupt handler
For this guide, I will use use GPIO 130 and 131 as interrupt lines. Refer to BB TRM section 8.19.1 for what GPIO lines are available. Refer to section 7.4.4.3 in the TRM for which register the GPIO is muxed on. So in init_pinmux.c, I add:
/* ################GPIO5 SETUP################### */
/* MMC2 CLK */
pad = in32(CONTROL_PADCONF_MMC2_CLK) & 0x0000ffff;
out32(CONTROL_PADCONF_MMC2_CLK, pad | INPUTENABLE1 | PULLTYPE1_UP | PULLUDENABLE1 | MUXMODE1_MODE4);
/* MMC2 CMD */
pad = in32(CONTROL_PADCONF_MMC2_CLK) & 0xffff0000;
out32(CONTROL_PADCONF_MMC2_CLK, pad | INPUTENABLE0 | PULLTYPE0_UP | PULLUDENABLE0 | MUXMODE0_MODE4);

/* GPIO IRQ */
out32(OMAP3530_GPIO5_BASE + OMAP2420_GPIO_RISINGDETECT, (1 << 2)|(1 << 3));
out32(OMAP3530_GPIO5_BASE + OMAP2420_GPIO_FALLINGDETECT, (1 << 2)|(1 << 3));

Create a new QNX project to test the interrupt handler. GPIO5 lines share IRQ 33, so I would need to check the irqstatus of each line if I use more than one interrupt.
#define GPIO5_IRQ 33

int count;
int error;
struct sigevent event;

const struct sigevent *
isr_handler (void *arg, int id)
{
return (&event);
}

void *
int_thread (void *arg)
{
// enable I/O privilege
ThreadCtl (_NTO_TCTL_IO, 0);

// attach the ISR to IRQ
if (InterruptAttach (GPIO5_IRQ, isr_handler, NULL, 0, _NTO_INTR_FLAGS_TRK_MSK) == -1) {
error = 1;
}
while (1)
{
InterruptWait (NULL, NULL);
count++;
}
}

int main(int argc, char *argv[]) {
event.sigev_notify = SIGEV_INTR;

printf("Creating interrupt thread...\n");

pthread_create (NULL, NULL, int_thread, NULL);
delay(5);

while(!error) {
printf("count=%i\n", count);
fflush(stdout);
sleep(1);
}

return EXIT_SUCCESS;
}
In my example you have to reset 2 status registers to have the interrupts trigger again. After count++, add:

out32(omap3530_intc_base + 0xa8, 0x2);
out32(GPIO5 + OMAP35XX_GPIO_IRQSTATUS1, in32(GPIO5 + OMAP35XX_GPIO_IRQSTATUS1) | 0xFFFFFFFF);

I will make a more complete guide later on... feel free to comment or email questions.

2 comments:

Alessandro said...
This comment has been removed by the author.
Alessandro said...

Hi,
I would like to start using the GPIO Interrupts with QNX on BeagleBoard and I found your guide very interesting.
However, I didn't understand so good what I should set-up the GPIO5.

Practically,

/* ################GPIO5 SETUP################### */
/* MMC2 CLK */
pad = in32(CONTROL_PADCONF_MMC2_CLK) & 0x0000ffff;
-->> Why do you do the & between CONTROL_PADCONF_MMC2_CLK and 0x0000ffff?

I've read that mmc2_clk is Mode 0 on CONTROL_PADCONF_MMC2_CLK[15:0] (Address: 0x4800 2158).
So, with the mask are you initializing the register setting all the 16 bits to zero? (and leaving the other 16 bits untouched?)


out32(CONTROL_PADCONF_MMC2_CLK, pad | INPUTENABLE1 | PULLTYPE1_UP | PULLUDENABLE1 | MUXMODE1_MODE4);
-->> Why you use INPUTENABLE1 | PULLTYPE1_UP | PULLUDENABLE1 | MUXMODE1_MODE4 ? Can you explain that?



/* MMC2 CMD */
pad = in32(CONTROL_PADCONF_MMC2_CLK) & 0xffff0000;
-->> Why do you do the & between CONTROL_PADCONF_MMC2_CLK and 0xffff0000 and not 0x0000ffff?

out32(CONTROL_PADCONF_MMC2_CLK, pad | INPUTENABLE0 | PULLTYPE0_UP | PULLUDENABLE0 | MUXMODE0_MODE4);


/* GPIO IRQ */
out32(OMAP3530_GPIO5_BASE + OMAP2420_GPIO_RISINGDETECT, (1 << 2)|(1 << 3));
out32(OMAP3530_GPIO5_BASE + OMAP2420_GPIO_FALLINGDETECT, (1 << 2)|(1 << 3));
-->>Can you explain these lines?

Thank you so much!

Alessandro