czwartek, 16 maja 2019

LPC1769 - Obsługa przerwania od pinu w FreeRtos

W tym krótkim poście opiszę sposób obsługi przerwania od portu na mikrokontolerze LPC1769 wraz z systemem FreeRtos.

Znalezione obrazy dla zapytania lpc1769
[Źródło: https://www.nxp.com]



Program:


Na samym początku należy uruchomić port wraz z przyporządkowanym do niego przerwaniem. Przykład zawiera obsługę 10 pinów wejściowych. Przerwania uruchomione na zbocze opadające oraz rosnące.

  1. void UstawWejscia()
  2. {
  3.     NVIC_DisableIRQ(EINT3_IRQn);
  4.     F_PORT_WE_01->FIODIR &= ~F_PIN_WE_01;
  5.     F_PORT_WE_02->FIODIR &= ~F_PIN_WE_02;
  6.     F_PORT_WE_03->FIODIR &= ~F_PIN_WE_03;
  7.     F_PORT_WE_04->FIODIR &= ~F_PIN_WE_04;
  8.     F_PORT_WE_05->FIODIR &= ~F_PIN_WE_05;
  9.     F_PORT_WE_06->FIODIR &= ~F_PIN_WE_06;
  10.     F_PORT_WE_07->FIODIR &= ~F_PIN_WE_07;
  11.     F_PORT_WE_05->FIODIR &= ~F_PIN_WE_05;
  12.     F_PORT_WE_06->FIODIR &= ~F_PIN_WE_06;
  13.     F_PORT_WE_07->FIODIR &= ~F_PIN_WE_07;
  14.     F_PORT_WE_08->FIODIR &= ~F_PIN_WE_08;
  15.     F_PORT_WE_09->FIODIR &= ~F_PIN_WE_09;
  16.     F_PORT_WE_10->FIODIR &= ~F_PIN_WE_10;

  17.     PINSEL_CFG_Type PinCfg;
  18.     // Inicjalizacja przerwania HID OBECNOŚĆ KARTY
  19.     PinCfg.Funcnum = PINSEL_FUNC_0;
  20.     PinCfg.OpenDrain = PINSEL_PINMODE_NORMAL;
  21.     PinCfg.Pinmode = 0;
  22.     PinCfg.Portnum = F_PORT_WEJSC_0_NR;
  23.     PinCfg.Pinnum = F_PIN_WE_01_NR;
  24.     PINSEL_ConfigPin(&PinCfg);
  25.     PinCfg.Pinnum = F_PIN_WE_02_NR;
  26.     PINSEL_ConfigPin(&PinCfg);
  27.     PinCfg.Pinnum = F_PIN_WE_07_NR;
  28.     PINSEL_ConfigPin(&PinCfg);
  29.     PinCfg.Pinnum = F_PIN_WE_08_NR;
  30.     PINSEL_ConfigPin(&PinCfg);
  31.     PinCfg.Pinnum = F_PIN_WE_09_NR;
  32.     PINSEL_ConfigPin(&PinCfg);
  33.     PinCfg.Pinnum = F_PIN_WE_10_NR;
  34.     PINSEL_ConfigPin(&PinCfg);

  35.     LPC_GPIOINT->IO0IntEnF &= ~0x3F0;
  36.     LPC_GPIOINT->IO0IntEnR &= ~0x3F0;

  37.     LPC_GPIOINT->IO0IntEnR |= F_PIN_WE_01;
  38.     LPC_GPIOINT->IO0IntEnF |= F_PIN_WE_01;
  39.     LPC_GPIOINT->IO0IntEnR |= F_PIN_WE_02;
  40.     LPC_GPIOINT->IO0IntEnF |= F_PIN_WE_02;
  41.     LPC_GPIOINT->IO0IntEnR |= F_PIN_WE_07;
  42.     LPC_GPIOINT->IO0IntEnF |= F_PIN_WE_07;
  43.     LPC_GPIOINT->IO0IntEnR |= F_PIN_WE_08;
  44.     LPC_GPIOINT->IO0IntEnF |= F_PIN_WE_08;
  45.     LPC_GPIOINT->IO0IntEnR |= F_PIN_WE_09;
  46.     LPC_GPIOINT->IO0IntEnF |= F_PIN_WE_09;
  47.     LPC_GPIOINT->IO0IntEnR |= F_PIN_WE_10;
  48.     LPC_GPIOINT->IO0IntEnF |= F_PIN_WE_10;

  49.     LPC_GPIOINT->IO0IntClr |= F_PIN_WE_01 | F_PIN_WE_02 | F_PIN_WE_07| F_PIN_WE_08 | F_PIN_WE_09 | F_PIN_WE_10;
  50.     //---------------------------------------------
  51.     PinCfg.Funcnum = PINSEL_FUNC_0;
  52.     PinCfg.OpenDrain = PINSEL_PINMODE_NORMAL;
  53.     PinCfg.Pinmode = 0;
  54.     PinCfg.Portnum = F_PORT_WEJSC_2_NR;
  55.     PinCfg.Pinnum = F_PIN_WE_03_NR;
  56.     PINSEL_ConfigPin(&PinCfg);
  57.     PinCfg.Portnum = F_PORT_WEJSC_2_NR;
  58.     PinCfg.Pinnum = F_PIN_WE_04_NR;
  59.     PINSEL_ConfigPin(&PinCfg);
  60.     PinCfg.Portnum = F_PORT_WEJSC_2_NR;
  61.     PinCfg.Pinnum = F_PIN_WE_05_NR;
  62.     PINSEL_ConfigPin(&PinCfg);
  63.     PinCfg.Portnum = F_PORT_WEJSC_2_NR;
  64.     PinCfg.Pinnum = F_PIN_WE_06_NR;
  65.     PINSEL_ConfigPin(&PinCfg);

  66.     LPC_GPIOINT->IO2IntEnF |= F_PIN_WE_03 | F_PIN_WE_04 | F_PIN_WE_05 | F_PIN_WE_06;
  67.     LPC_GPIOINT->IO2IntEnR |= F_PIN_WE_03 | F_PIN_WE_04 | F_PIN_WE_05 | F_PIN_WE_06;

  68.     LPC_GPIOINT->IO2IntClr |= F_PIN_WE_03 | F_PIN_WE_04 | F_PIN_WE_05 | F_PIN_WE_06;

  69.     NVIC_SetPriority(EINT3_IRQn, 0);
  70.     NVIC_EnableIRQ(EINT3_IRQn);
  71. }

Teraz należy stworzyć nowe zadanie, które będzie wyzwalane po wygenerowaniu przerwania:

  1. static xTaskHandle inputInterTaskHandle = NULL;
  2. //...
  3. xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) );
  4. if( xQueue != NULL )
  5. {
  6.         xTaskCreate( prvQueueInputInterruptTask,( signed char * )"INTERRUPT_TASK", configMINIMAL_STACK_SIZE, NULL ,configQUEUE_ISR_TASK_PRIORITY, &inputInterTaskHandle);
  7.         vTaskStartScheduler();
  8. }

Do zadania przypisujemy między-innymi funkcję wykonywaną po otrzymaniu przerwania oraz zmienną przechowującą parametry tego zadania.

Teraz funkcja IRQ Handler:

  1. void EINT3_IRQHandler(void)
  2. {
  3.     checkPinEnterInterrupt_Port_0(LPC_GPIOINT, F_PIN_WE_01);
  4.     checkPinEnterInterrupt_Port_0(LPC_GPIOINT, F_PIN_WE_02);
  5.     checkPinEnterInterrupt_Port_0(LPC_GPIOINT, F_PIN_WE_07);
  6.     checkPinEnterInterrupt_Port_0(LPC_GPIOINT, F_PIN_WE_08);
  7.     checkPinEnterInterrupt_Port_0(LPC_GPIOINT, F_PIN_WE_09);
  8.     checkPinEnterInterrupt_Port_0(LPC_GPIOINT, F_PIN_WE_10);
  9.     checkPinEnterInterrupt_Port_2(LPC_GPIOINT, F_PIN_WE_03);
  10.     checkPinEnterInterrupt_Port_2(LPC_GPIOINT, F_PIN_WE_04);
  11.     checkPinEnterInterrupt_Port_2(LPC_GPIOINT, F_PIN_WE_05);
  12.     checkPinEnterInterrupt_Port_2(LPC_GPIOINT, F_PIN_WE_06);

  13.     LPC_GPIOINT->IO0IntClr |= F_PIN_WE_01 | F_PIN_WE_02 | F_PIN_WE_07| F_PIN_WE_08 | F_PIN_WE_09 | F_PIN_WE_10;
  14.     LPC_GPIOINT->IO2IntClr |= F_PIN_WE_03 | F_PIN_WE_04 | F_PIN_WE_05 | F_PIN_WE_06;

  15.     portBASE_TYPE checkIfFieldRequired = xTaskResumeFromISR(inputInterTaskHandle);
  16.     portCLEAR_INTERRUPT_MASK_FROM_ISR(checkIfFieldRequired);
  17. }

Na początku sprawdzam, który pin wygenerował przerwanie po czym czyszczę flagi informujące o jego wygenerowaniu.

Główne zadanie sprawdzające przerwanie zostaje wyprowadzone ze stanu zawieszania za pomocą funkcji xTaskResumeFromISR. Po czym następuje wyczyszczenie wygenerowanego przerwania dla FreeRtos.

  1. static void prvQueueInputInterruptTask(void *pvParameters)
  2. {
  3.     (void)pvParameters;
  4.     while(1)
  5.     {
  6.         vTaskSuspend(NULL);
  7.         PrepareActionsForSpecificInput();
  8.     }
  9. }

W głównej funkcji vTaskSuspend(NULL); utrzymuje funkcje w stanie zawieszenia do momentu jej wybudzenia. Dojście do funkcji głównej PrepareActionsForSpecificInput następuje dopiero wyzwoleniu zadania w funkcji przerwania.