EXT внешние прерывания

Сначала сконфигурируем в Кубе ноги R1, R2 , R3 , R4. Это ноги , на которых ожидаются внешние прерывания . Это реализация банальной клавиатуры , матрица R1..R4 на W1..W6 .

фотка 1

Надо не забыть включить прерывания :

фотка 2

Далее переходим к изучению HAL - ого кода , полученного из Куба. Придется раскрутить код , который скрывается за HAL оберткой. Первое , что происходит в результате вызова внешнего прерывания допустим по R1 - это вызов обработчика прерывания :

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  //PR (EXTI Pending register)
  // здесь проверяется EXTI->PR & (GPIO_Pin) != 0 , то есть бит PR для номера GPIO_Pin 
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    // соответственно сбрасываем флаг EXTI->PR & (GPIO_Pin) ! установкой в 1 !
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

Нас будут интересовать регистры относящиеся к прерыванию :

/**    * @brief External Interrupt/Event Controller   */
typedef struct
{
  __IO uint32_t IMR;    /*!< EXTI Interrupt mask register,            Address offset: 0x00 */
  __IO uint32_t EMR;    /*!< EXTI Event mask register,                Address offset: 0x04 */
  __IO uint32_t RTSR;   /*!< EXTI Rising trigger selection register,  Address offset: 0x08 */
  __IO uint32_t FTSR;   /*!< EXTI Falling trigger selection register, Address offset: 0x0C */
  __IO uint32_t SWIER;  /*!< EXTI Software interrupt event register,  Address offset: 0x10 */
  __IO uint32_t PR;     /*!< EXTI Pending register,                   Address offset: 0x14 */
} EXTI_TypeDef;

И нас будет интересовать контроллер вложенных прерываний , организующий очередь для всех приходящих прерываний :


/**
  \brief  Structure type to access the Nested Vectored Interrupt Controller (NVIC).
 */
typedef struct
{
  __IOM uint32_t ISER[8U];               /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register */
        uint32_t RESERVED0[24U];
  __IOM uint32_t ICER[8U];               /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register */
        uint32_t RSERVED1[24U];
  __IOM uint32_t ISPR[8U];               /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register */
        uint32_t RESERVED2[24U];
  __IOM uint32_t ICPR[8U];               /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register */
        uint32_t RESERVED3[24U];
  __IOM uint32_t IABR[8U];               /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register */
        uint32_t RESERVED4[56U];
  __IOM uint8_t  IP[240U];               /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */
        uint32_t RESERVED5[644U];
  __OM  uint32_t STIR;                   /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register */
}  NVIC_Type;

На данном этапе главное не запутаться с регистрами , отвечающими за работу внешнего прерывания, и NVIC . У них на входе функций подаются разные параметры и их легко перепутать.

Параметр IRQn_Type IRQn это не uint16_t GPIO_Pin , это совсем разные вещи. компилятор не будет ругаться , так как формально это uint16_t b uint32_t , то есть числа.

Обычно любая клавиатура обладает обалденным дребезгом. Это означает , что при нажатии кнопки происходит очень много срабатываний прерывания. То есть мы получаем не одно прерывание как хотелось бы , а несколько.

Что надо сделать , чтобы не срабатывало много прерываний : надо в конце обработчика прерывания например вызвать NVIC_ClearPendingIRQ . Таким образом все прерывания которые произошли во время отработки текущего прерывания очистятся.

R1..R2 можно указать PP (Push Pull ) или OD (open drain) . PP похоже надежнее работает.