## Менеджер задач Task Manager

Библиотека Task Manager предоставляет функционал для управления периодическими и асинхронными задачами в микроконтроллерах. 

#### Она позволяет:
- создавать и управлять периодическими задачами с заданным интервалом;
- добавлять асинхронные задачи в очередь выполнения;
- контролировать выполнение задач с приоритетом асинхронных задач над периодическими;


#### Особенности
- атомарный доступ к функциям (с запретом прерываний);
- поддержка до 32 периодических и 32 асинхронных задач (настраивается);
- минимальные накладные расходы;
- интеграция с драйверами LMCAL;


#### Требования
1) компилятор: arm-none-eabi-gcc xPack v14.2.1;
2) make: v4.3;
3) библиотека LMCAL для работы с периферией;
4) библиотека ring_buffer для управления очередями;


#### Настройка
- перед использованием необходимо настроить параметры в файле task_man_cfg.h:
```
// максимальное количество периодических задач
#define TM_PERIODIC_QTY      (32U)
// максимальное количество асинхронных задач
#define TM_ASYNC_QTY         (32U)
// тип для хранения временных интервалов
typedef uint16_t TM_SZ;
```

#### Основные функции управления задачами
```
1) tm_init();
```
- инициализация менеджера задач;
- настройка внутренних структур и очередей;
- должна вызываться первой перед использованием других функций;

```
2) tm_add_periodic_task(u8* desc, void_fun_t fun);
```
- добавляет периодическую задачу;
- (desc - указатель на переменную для хранения дескриптора задачи);
- (fun - указатель на функцию-обработчик);
- озвращает ERR_OK при успехе или код ошибки;

```
3) tm_start_periodic_task(u8 desc, TM_SZ period);
```
- запускает периодическую задачу;
- (desc - дескриптор задачи);
- period - период выполнения в миллисекундах;

```
4) tm_stop_periodic_task(u8 desc);
```
- останавливает выполнение периодической задачи;
- сохраняет возможность последующего перезапуска;

```
5) tm_delay_periodic_task(u8 desc, TM_SZ delay);
```
- временно приостанавливает задачу на указанное время;
- после задержки задача автоматически возобновляется;
```
6) tm_deinit_periodic_task(u8 desc);
```
- полностью удаляет периодическую задачу;
- освобождает соответствующий слот;

#### Функции для асинхронных задач
```
7) tm_add_async_task(void_fun_t fun);
```
- добавляет асинхронную задачу в очередь выполнения;
- асинхронные задачи имеют приоритет над периодическими;

#### Функция выполнения задач
```
8) tm_loop();
```
- основная функция выполнения задач;
- должна вызываться в бесконечном цикле;

Выполняет:

- все асинхронные задачи из очереди;
- по одной периодической задаче за вызов;

#### Вспомогательные функции (внутренние)
```
9) f_do_periodic_task_cnt() (static);
```
- внутренняя функция для обработки счетчиков задач;
- вызывается периодически (обычно из прерывания таймера);
- инкрементит счетчики и добавляет готовые задачи в очередь;

#### Использование
- инициализация;

```
#include "task_man.h"

int main(void) {
    tm_init(); 
    
    // ... остальной код
}
```

- добавление и запуск периодической задачи;

```
u8 task1;

// функция-обработчик
void led_toggle(void) {
    lmcal_gpio_write_pin(LED1_CHANNEL, !lmcal_gpio_read_pin(LED1_CHANNEL));
}

// добавление задачи
tm_add_periodic_task(&task1, led_toggle);
// запуск с периодом 500 мс
tm_start_periodic_task(task1, 500);
```
- добавление асинхронной задачи;
```
// функция-обработчик
void process_event(void) {
    // обработка события
}

// добавление в очередь
tm_add_async_task(process_event);
```

- основной цикл;

```
while(1) {
    tm_loop(); // выполнение задач
    // ... другой код
}
```

#### Примеры

- управление светодиодами
```
u8 led1_task, led2_task;

void blink_led1(void) {
    lmcal_gpio_write_pin(LED1_CHANNEL, true);
    lmcal_ptim_delay(100);
    lmcal_gpio_write_pin(LED1_CHANNEL, false);
}

void toggle_led2(void) {
    static bool state = false;
    state = !state;
    lmcal_gpio_write_pin(LED2_CHANNEL, state);
}

void main(void) {
    tm_init();
    
    // Добавляем задачи
    tm_add_periodic_task(&led1_task, blink_led1);
    tm_add_periodic_task(&led2_task, toggle_led2);
    
    // Запускаем с разными периодами
    tm_start_periodic_task(led1_task, 200); // Каждые 200 мс
    tm_start_periodic_task(led2_task, 500); // Каждые 500 мс
    
    while(1) {
        tm_loop();
    }
}
```

#### Комбинирование периодических и асинхронных задач

```
u8 main_task;

void main_process(void) {
    // основная обработка
}

void emergency_handler(void) {
    // обработка аварийной ситуации
}

// прерывание по кнопке
void button_isr(void) {
    tm_add_async_task(emergency_handler);
}

void main(void) {
    tm_init();
    
    tm_add_periodic_task(&main_task, main_process);
    tm_start_periodic_task(main_task, 100);
    
    while(1) {
        tm_loop();
    }
}
```
#### Сборка
Для сборки проекта с Task Manager необходимо:

1) добавить в Makefile:
```
include $(PATH_TO_LMCAL)/lmcal.mk
include $(PATH_TO_TASK_MAN)/task_man.mk
```

2) указать пути к исходным файлам:
```
C_SOURCES += $(TASK_MAN_SRC)
C_INCLUDES += $(TASK_MAN_INC)
```

#### Тестирование
Библиотека включает проверки на:

- некорректные параметры функций;
- попытку добавления задачи когда нет свободных слотов;
- попытку управления неинициализированной задачей;

Пример теста:

```
// попытка добавить задачу с NULL-обработчиком

fun_res_t err = tm_add_periodic_task(&task, NULL);
if (err != ERR_OK) {
    // Обработка ошибки
}
```

#### Ограничения
- время выполнения задач должно быть значительно меньше периода их вызова;
- не рекомендуется выполнять длительные операции в обработчиках задач;
- для атомарности используются глобальные запреты прерываний;