Лучший способ фильтрации прерываний

немного вопрос, основанный на мнении здесь ...

В настоящее время я использую uController (cc1310), который реализует прерывание при изменении всех 32 контактов в одном обратном вызове.

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

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );

    if (pinMask & 0x00000001)
    {
        /* pin_0 generated interrupt */
    }

    ...
}

На данный момент каждый бит в pinMask является соответствующим флагом прерывания вывода.

До сих пор каждому приложению требовался только один контакт/процесс для использования события ioc, поэтому каждый раз, когда нам это было нужно, мы просто регистрировали наш обратный вызов, и все было готово.

Это последнее приложение будет использовать больше, поэтому я думаю о создании модуля GPIO_interrupt, где я мог бы зарегистрировать обратный вызов для данного контакта, а модуль отфильтровал бы по pinMask и вызвал правильный обратный вызов.

Так...

какой вариант предлагают гуру?


а)

Я могу добавить только if's для прерываний, которые я использую

typedef void (*pin_interrupt_callback_t)( void );

pin_interrupt_callback_t pin0_callback;

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );


    if (pinMask & 0x00000001)
    {
        HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 ) |= 0x00000001;

        /* pin_0 generated interrupt */
        if (NULL != pin0_callback)
            pin0_callback();
    }
    else if (pinMask & 0x00000002)
    {
        ...
    }
    ...
}

б)

Таким образом, это немного более динамично?, где я могу добавлять или удалять обратные вызовы в/из таблицы. (Таблица также может быть использована в а))

typedef void (*pin_interrupt_callback_t)( void );

pin_interrupt_callback_t pin_callback[32] = {
   pin0_callback,
   pin1_callback,
   ...
}

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );

    for(uint8_t i = 0; i < 32; i++)
    {
        if (pinMask & (1 << i))
            if (NULL != pin_callback[i])
                pin_callback[i]();
    }
}

в)

Этот способ самый сложный, но я буду циклически перебирать только существующие прерывания, и я могу зарегистрировать один и тот же обратный вызов для нескольких выводов (можно также сделать это в а) и б), но здесь он будет вызываться только один раз, если больше чем одно прерывание происходит одновременно)

И мы даже можем зарегистрировать более одного обратного вызова для каждого контакта.

typedef void (*pin_interrupt_callback_t)( uint32_t mask );

typedef struct pin_callback_element
{
    uint32_t callbackMask;
    pin_callback_element *next;
    pin_interrupt_callback_t callback;
} pin_callback_element_t;

pin_callback_element_t *pin_callbacks = NULL;

/* elem must be declared as `static pin_callback_table_t elem;` */
uint8_t register_callback(
            pin_callback_element_t *elem,
            uint32_t pinMask,
            pin_interrupt_callback_t callback )
{
    /* Make sure no element is registered for a NULL callback */

    elem->next = NULL;
    elem->callbackMask = pinMask;
    elem->callback = callback;

    /* Add element to the pin_callbacks list */
}

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );

    pin_callback_element_t *p_cp = pin_callbacks;
    while(NULL != p_cp)
    {
        if (0 != (pinMask & p_cp->callbackMask))
            p_cp->callback(pinMask);

        p_cb = p_cb->next;
    }
}

или г)

Ни один из вышеперечисленных.


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

Извините за длинный пост и спасибо за отзыв

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
39
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я бы выбрал b), так как вы можете/должны регистрировать обратные вызовы в справочной таблице, доступной только для чтения. В идеале эта таблица должна быть установлена ​​во время компиляции и размещена там, где у вас есть все остальные константы, связанные с GPIO.

Причина этого в том, что очень раздражает, когда каждая децентрализованная часть проекта возится с GPIO внутри. Вам понадобится централизованный список, в котором вы можете получить представление о том, какие контакты являются входными, выходными, плавающими, вытянутыми и т. д. Таким образом, вы можете легко просмотреть / проверить программное обеспечение на схеме и наоборот. А также избегайте конфликтов, когда разные части программы случайно используют одни и те же выводы.

Теоретически вы мог создаете динамический список для чтения/записи, как в c), связанный список, в котором разные вызывающие абоненты могут регистрировать обратные вызовы. Я часто создаю такие для таймеров общего назначения, но в конкретном случае GPIO, который очень близок к оборудованию, я бы не советовал его по уже упомянутым причинам.

Так что, возможно, что-то вроде этого:

typedef void callback_t (void);

#define CALLBACK_N 32u

static callback_t* const callback [CALLBACK_N] =  // function ptr table in read-only flash
{
  [1] = callback_1,
  [22] = callback_22
};

Это означает, что все индексы, которые мы не инициализировали явно, будут неявно установлены в нуль.

К сожалению, странные библиотеки TI возвращают битовую маску вместо битового числа, или цикл for в ISR можно было удалить и заменить прямым доступом к справочной таблице.

Некоторые мелкие придирки:

  • В ISR, возможно, изменить порядок операторов if для немного более быстрого выполнения (микрооптимизация):

    if (pin_callback[i] != NULL) // check vs zero will be single instruction
      if (pinMask & (1u << i))
    
  • Всегда добавляйте суффикс u к целочисленным константам. Всегда делай 1u <<, никогда не делай 1 <<. Последнее может вызвать неопределенное поведение, если вы переместите данные в знаковый бит подписанного int, который 1 создал.

  • Не нужно никаких «условий йода» NULL != pin_callback[i] чепухи из эпохи 1980-х. Просто используйте полуприличный компилятор вроде Turbo C 1989 года или новее - он предупредит о неправильных присваиваниях в условиях. И с моим исправлением выше таблица указателей функций в любом случае доступна только для чтения.

Другие вопросы по теме