немного вопрос, основанный на мнении здесь ...
В настоящее время я использую 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;
}
}
или г)
Ни один из вышеперечисленных.
Я знаю, что обратные вызовы прерываний должны быть как можно быстрее и меньше, но модуль, который мы сейчас делаем, может использоваться другими приложениями, которые могут использовать или не использовать прерывания других выводов сами по себе, поэтому я думаю, что это была бы плохая идея. изменить общий код в соответствии с другими приложениями
Извините за длинный пост и спасибо за отзыв





Я бы выбрал 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 года или новее - он предупредит о неправильных присваиваниях в условиях. И с моим исправлением выше таблица указателей функций в любом случае доступна только для чтения.