Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

Кодинг МК. Применение планировщика/очереди задач

Пн дек 20, 2021 13:26:47

Доброго.
Очередь задач - один из вариантов структуры программы на МК. Среди множества велосипедов остановимся на таком:
Спойлер
Код:
// Добавить задачу в очередь
void qtTask(qtTaskPtr ptr, qtDelay tick);

// Вызывается при каждом "tick", проводит отсчет задержки
void qtDecrementDelay(void);

// Пытается выполнить одну задачу из очереди
void qtDispatch(void);


Мы можем запланировать задачу либо "на сейчас" либо через N тиков. Для периодичного вызова задача планирует свой вызов сама.

Код:

volatile uint8_t f;

ISR(TIMER0_OVF_vect){
  TCNT0 = TCNT_TICK; // 125Hz
  f |= 1;
}

void tBlink(){
  PORTB ^= 0x01;
  qtTask(tBlink, 64);
}

int main(void)
{
    init();
    tBlink();
    while (1)
    {
      if(f & 0x01){
        f &= ~1;
        qtDecrementDelay();
      }
      qtDispatch();       
    }
}


Всё понятно, всё работает и вопросов нет... пока не начнешь навешивать задачи. Вместе с ними появляется путаница с "внешними данными" и "разделяемыми ресурсами".

Добавляю клавиатуру с тремя кнопками +/-/Set. Нажимая Set, заходим в настройки, +/- меняем значение параметра и повторным нажатием Set сохраняем значение.
Задача qtButton молотит с нужной периодичностью, всё отлавливает и на гора выдает в глобальной переменной битовую маску "кнопка нажата".
Код:
unit8_t btState; // Битовая маска. Единичка поднимается задачей qtButton при нажатии кнопки либо срабатывании автоповтора

И вот ту первый косяк: юзер нажимает +, затем только Set. Активируется задача изменения параметра, которая сразу увидит нажатый плюс и обработает его. Как исправить это правильно?

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

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

Вторая часть про разделяемый ресурс чуть позже. После осознания ошибок первой.

Re: Кодинг МК. Применение планировщика/очереди задач

Пн дек 20, 2021 15:20:40

AndryG писал(а):юзер нажимает +, затем только Set. Активируется задача изменения параметра,
Вы не описали - в какой момент он нажимает эти "+" и "Set" (и зачем еиу вообще их нажимать ?), и что из себя представляет задача "Изменение параметра" ?

Re: Кодинг МК. Применение планировщика/очереди задач

Пн дек 20, 2021 15:27:30

Задача может обработать изменение настроек пользователем во временную переменную, и лишь по нажатию Set записывать в действующую

Re: Кодинг МК. Применение планировщика/очереди задач

Пн дек 20, 2021 15:54:15

Аlex, в начальный момент времени задача qtDefault мониторит переход по кнопке Set в режим изменения параметра (где уже будут мониторится все три кнопки). Юзер до нажатия set нажимает плюс, а потом set. Задача изменения параметра сразу при начале работы увидит уже нажатый плюс. А это есть ошибка.

Вообще меня интересует не конкретный пример, а общая проблема событий в таком планировщике.

Если программа сделана на куче автоматов, которые по очереди вызываются в главном цикле, то там все ясно - пришедшие события обрабатываются в следующей итерации, по концу итерации все события очищаются. А в случае очереди задач эта идея не работает...
Последний раз редактировалось AndryG Пн дек 20, 2021 20:32:36, всего редактировалось 1 раз.

Re: Кодинг МК. Применение планировщика/очереди задач

Пн дек 20, 2021 19:58:55

Юзер до нажатия set нажимает плюс, а потом set. Задача изменения параметра сразу при начале работы увидит уже нажатый плюс. А это есть ошибка.
Непонятно - в чём именно проблема?
Задаём текущее состояние:
Код:
enum ConfigChange {ConfigChange_NONE, ConfigChange_EDIT};
ConfigChange state = ConfigChange_NONE;
Пока state==ConfigChange_NONE - ждём только "Set" (вход в режим редактирования), остальные нажатия или игнорим или пикаем (недопустимая кнопа). В режиме редактирования (ConfigChange_EDIT) же принимаем всё: "+","-","Set". Проще некуда....

Re: Кодинг МК. Применение планировщика/очереди задач

Пн дек 20, 2021 22:12:25

Юзер до нажатия set нажимает плюс, а потом set. Задача изменения параметра сразу при начале работы увидит уже нажатый плюс. А это есть ошибка.
Вы сами построили такую систему задач. Единственный выход в Вашем случае - очищать весь ненужный мусор перед созданием задачи.
А по-правильному - делать (или взять готовое) нормальный диспетчер, обслуживающий задачи с вечным циклом. Тогда всю подготовку можно будет делать вначале функции-задачи, до основного цикла.

PS: Ваша система ничем не отличается от КА. Те же периодичные вызовы, обычные функции с завершением, всё тоже самое ...

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 08:33:37

Можете привести пример "нормальный диспетчер" для средних камней? Что я встречал - вопрос событий не особо рассматривался.

http://we.easyelectronics.ru/AVR/mashin ... oshek.html

https://chipenable.ru/index.php/embedde ... llera.html

https://www.yumpu.com/en/document/view/ ... s-jennaron по мотивам этой статьи мой велосипед :)

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 11:57:44

Чем ваш то ненормальный ? IMHO вы хотите от него чего то не того. Обработка тех кнопок - это ваша задача/реализация, а не диспетчера.

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 12:29:33

Если копаешь глубже моргания светодиодов, то радужное восхищение архитектурой начинает испарятся. Вот я и спрашиваю совета, как таким пользуются? Может я просто не умею его готовить и для таким курьезов, типа описанного мною с "фантомного" нажатия кнопок, есть готовые рецепты.

Склоняюсь к мысли Аlex, что рассматривать эту очередь стоит как конечный вариант и никуда от кучи флагов и странных хитросплетений не уйти.

Второй мой вопрос был про организацию разделения ресурсов. Для примера берем AV-показометр в блоке питания. По умолчанию он показывает A и V, но если идет перегрев радиаторов, то он должен показать на индикаторе сообщение о перегреве. Если крутнуть ручку ограничения тока, то на индикаторе некоторое время показываются значения максимальной уставки.
Мы имеем один ресурс - "видеопамять индикатора" и три задачи, которые в разные моменты хотят им владеть.
Как в таких случаях организовать программу? Расталкивать по коду кучу взаимных условий и регулировать их совместную работу - это жуть.
Поделитесь, пжлст, опытом :)
Пока, надумал вариант рассматривать два отдельных индикатора. У каждого из них есть атрибут "номер владельца" он же приоритет. Новая задача может забрать управление индикатором на себя, если она более приоритетна.
Тогда получается так:
задача AV-метра крутится постоянно. Приоритет для индикатора равен единице.
задача уставки макс тока - 2
задача перегрева радиаторов - 3

В начале "владельца" у индикатора нет - в поле прописан ноль.
Задача AV-метр пишет туда единицу и вписывает свои значения для отображения.
Когда покрутили ручку тока, задача макс уставки перебивает владение индикатором тока, отображает значение тока. Через три секунды стабильных показаний (ручку больше не крутят) сбрасывает владение индикатором в ноль и его тут же подхватывает задача AV-метр.
Задача перегрева плюет на всех и на некоторое время выводит сообщение о перегреве - её никто не смеет затереть.
Какось так выглядит в планах мой велосипед. Хочется услышать мнение/совет опытных товарищей

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 12:57:58

Для примера берем AV-показометр в блоке питания. По умолчанию он показывает A и V, но если идет перегрев радиаторов, то он должен показать на индикаторе сообщение о перегреве. Если крутнуть ручку ограничения тока, то на индикаторе некоторое время показываются значения максимальной уставки.
Мы имеем один ресурс - "видеопамять индикатора" и три задачи, которые в разные моменты хотят им владеть.

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

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 13:31:05

Может я просто не умею его готовить и для таким курьезов, типа описанного мною с "фантомного" нажатия кнопок, есть готовые рецепты.
Чем не устраивает описанный мной алгоритм?

Для примера берем AV-показометр в блоке питания. По умолчанию он показывает A и V, но если идет перегрев радиаторов, то он должен показать на индикаторе сообщение о перегреве. Если крутнуть ручку ограничения тока, то на индикаторе некоторое время показываются значения максимальной уставки.
Мы имеем один ресурс - "видеопамять индикатора" и три задачи, которые в разные моменты хотят им владеть.
Как в таких случаях организовать программу?
Как уже сказал Alex: Задача индикации - единственная, а источник события "покрутили ручку" выставляет флаг этой задаче "активен режим отображения спец.значения" и устанавливает таймер таймаута, по истечении которого флаг будет сброшен и задача вернётся в обычный режим "отображения A и V".

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 14:34:23

jcxz, ваш алгоритм игнорирует саму соль - фантомный плюс, который прилетает в вновь запущенную задачу.
Пока state==ConfigChange_NONE - ждём только "Set" (вход в режим редактирования), остальные нажатия или игнорим или пикаем (недопустимая кнопа). В режиме редактирования (ConfigChange_EDIT) же принимаем всё: "+","-","Set". Проще некуда....

Если в первой задаче нажать плюс, то ничего внешне не изменится, но в программе флаг будет висеть. Через сутки юзер нажимает Set, запускается вторая задача, которая сразу видит нажатый плюс и обрабатывает его. Это косяк. Для его разрешения есть масса вариантов решения разного уровня костыльности. Цель этого топика - выявить наиболее подходящий для ситуаций такого рода. В архитектуре https://kit-e.ru/circuit/primenenie-swi ... v-chast-2/ , например, вопрос "зависших" событий решается централизовано "все сообщения должны быть обработаны на следующем шаге после генерации".

В моем случае, как я понял из ответов, нужно обработку делать на месте:
- предварительная очистка буфера при добавлении задачи qtEdit() в очередь
- дополнительная задача qtStartEdit, в которую выделить код предыдущего пункта
- флаг "первый запуск" в самой задаче qtEdit, по которому буфер клавиатуры будет игнорится.

Попробуем в коде, как оно будет.

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 15:02:41

jcxz, ваш алгоритм игнорирует саму соль - фантомный плюс, который прилетает в вновь запущенную задачу.
Не знаю что такое "фантомный плюс".

Если в первой задаче нажать плюс, то ничего внешне не изменится, но в программе флаг будет висеть. Через сутки юзер нажимает Set, запускается вторая задача, которая сразу видит нажатый плюс и обрабатывает его.
Что за "флаг"? Зачем он там? И зачем какая-то "2-я задача"? В моём описании ничего этого нет.
Почему у вас "нажатый плюс" может где-то "висеть" целые сутки??? :shock: Может надо сперва в этом разобраться - почему у вас так происходит?
Ничего не понятно......

PS: Выдумываете проблемы на ровном месте, в примитивнейшей задаче.

Re: Кодинг МК. Применение планировщика/очереди задач

Вт дек 21, 2021 16:28:30

странно...
есть ресурс - видеопамять и есть задача вывода на дисплей, которая, в зависимости от переменных (температура, открытый пункт меню и т.д.) выводит на экран те или иные показания.

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

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

есть служебные задачи, выполнение которых происходит независиао от изображения на экране и иоложениф в меню, например поддержание температуры в холодильнике... для таких процессов делается отдельная задача...


и не очень важно как это реализовано, через диспетчер или свитч—кейсом...
Ответить