ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Воскресенье
30 июня
1436093 Топик полностью
RxTx (17.05.2024 17:55 - 18:10, просмотров: 628) ответил Yurasvs на Пытаюсь въехать в идеологию HALa (только не надо опять холиварить за то, что ХАЛ это для тупых, рождает кучу мусора, перегружает процессор лишними действиями...). Зачем-то же его сделали... Вот раньше все было просто, есть прерывание, у него есть обработчик, туда пишешь действие, и все работает. В ХАЛе надо обязательно писать через какие-то коллбэк функции, напрямую не хочет работать. Что такое коллбэк функция? С какой целью ее придумали? Почему нельзя как встарь, просто
Что такое callback ты понимаешь или нет? 

Callack иначе "обратный вызов", с английского "перезвонить". Когда ты пишешь какую-то полезную библиотеку для людей у тебя рано или поздно возникает необходимость в ответ на какое-то происшествие позвать код пользователя. Ну и как ты собрался это сделать, если переписывать код твоей библиотеки нельзя и если у пользователя не будет кода твоей библиотеки?


Делается это так.

У себя в библиотеке ты заводишь одну void* переменную-указатель с адресом пользовательской функции (пользовательского кода).

Если она 0 (NULL, nullptr) код твоей библиотеки это понимает н ничего не происходит. Но если проверка показывает что там что-то есть (не-0), то значит это адрес который пользователь установил.

И тогда когда нужно (допустим, по сети приходит пакет) ты вызываешь функцию по этому адресу.


Обрати внимание. На момент написания своей библиотеки ты ничего не знаешь о пользователе и его коде. Но он (пользователь, пользовательский код) используя твою библиотеку может в любой момент задать адрес обратного вызова, таким образом "подставив" себя, подставив свой код.

И таким образом, ты сможешь вызвать даже то, о чем ты сейчас не знаешь что. Вот для этого нужны колбэки (callbacks).


Обрати внимание, что устанавливать колбэки можно не при компиляции (* см ниже про weak), а динамически, в run-time, прямо во время выполнения.

callback можно установить или снять. А можно подменить уже кем-то установленный:


Сохраняешь адрес у себя. Вписываешь себя, свой адрес (своей функции). А дальше это дает тебе возможность решать. Когда библиотека "позовёт" callback, то вызовается твоя функция. (Ты же подменил адрес callback'а).

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


Дальнейшим расширением callback'ов является "придумка" (называемая также идиомой, паттерном) "publisher-subscriber".

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


Конкретный ответ. ЗАЧЕМ.

Затем, что подобный механизм позволяет какому-то коду (библиотеке, втч HAL) ничего не знать о твоем коде, но вызывать его.

Затем, что подобный механизм поключаемых callback'ов работает как "интерфейс'" (что-то типа разъёма между платами или блоками аппаратуры) и позволяет не сваливать всё в одну кучу, а разрабатывать систему отдельными модулями. Которые ты затем "подключишь" друг к другу. А может перключишь. Или выключишь.


И наконец самый главный ответ почему HAL сделано именно так.

Потому что HAL сидит на прерывании и обрабатывает МНОГО ЧЕГО ЕЩЕ, о чем ты даже и не догадываешься.

Недавно например возникал спор о обработке ошибок UART (втч при DMA). Вот HAL например обрабатывает ошибки "у тебя за спиной". Для этого он должен "сесть" на прерывания.
Ну и твою функцию, получается, если уж он сам сел на прерывания вызывать уже чисто как "callback", а как еще-то?


Еще один ответ про HAL.

Дело в том что в HAL даже для одного прерывания множество обработок на самом деле.

И соответственно, множество и Callback'ов в свою очередь. Если ты заинтересован - прописываешь callback.

Не заинтересован - ну и шут с ним, не прописываешь, тогда оно само там предпринимает усилия как и что делать. "Как-то делает"


weak callback'и

В HAL Через #define можно выбрать способ работы с колбэками.

Callback'и можно сделать динамические, ровно такие, как я описал выше (адреса вызываемых функция у них хранятся в структуре). Но благодаря компилятору GCC есть и другой способ, более простой, быстрый и компактный.
В GCC существуют так называемые "weak" (слабые) функции. Работает это так. В коде может быть определена функция помеченная как weak. Она будет использоваться как бы по-умолчанию.

Обрати внимание что в коде HAL заданы пустые функции-колбэки по умолчанию (заглушки), но помеченые как weak.

Но когда ты определишь самую обычную свою функцию, weak-функция по умолчанию уже не будет использоваться (отрубится).

Поэтому как только ты задал функцию с такой же сигнатурой как её предполагает callback, твоя функция будет использована.

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


От обработки прерываний механизмами HAL и вызова механизма callback'ов можно полностью (а можно и не полностью, а чуть-чуть) отказаться.

Иди в свой файл обработки прерываний, он всегда называется че-то там ***_it.c

Видишь там одиночные функции прерываний? (Адреса этих функций прописываются прямо в ARM вектор прерываний).

Вписывай между пользовательским /* USER CODE BEGIN */

и

/* USER CODE END */ любой свой код.

Ставишь например return до вызова HAL. И всё, HAL функция обработки прерываний за твоим return конечно уже не вызовется.

Есть выбор, вписать свой код до, HAL после HAL и вместо HAL, полностью свой. Т.е. пишешь всё сам, всю обработку в прерывании (я часто так делаю).

Надеюсь это было полезным.

Спасибо, князь. Вы настоящий дворянин. И программист.