Да, у нас всего 2 КБ RAM и 16 МГц, но мы можем создать операционную систему реального времени, которая позволит выполнять несколько задач параллельно!
Структура RTOS:
#define MAX_TASKS 8
#define STACK_SIZE 64
// Структура задачи
typedef struct {
void (*task_func)();
uint16_t period;
uint16_t last_run;
uint8_t priority;
uint8_t stack[STACK_SIZE];
uint8_t *sp;
uint8_t state; // 0 = остановлена, 1 = готова, 2 = выполняется
} Task;
Task tasks[MAX_TASKS];
uint8_t task_count = 0;
// Добавление задачи
uint8_t add_task(void (*func)(), uint16_t period, uint8_t priority) {
if (task_count >= MAX_TASKS) return 0;
tasks[task_count].task_func = func;
tasks[task_count].period = period;
tasks[task_count].priority = priority;
tasks[task_count].last_run = millis();
tasks[task_count].state = 1;
tasks[task_count].sp = &tasks[task_count].stack[STACK_SIZE - 1];
return task_count++;
}
// Планировщик (вызывается в loop)
void scheduler() {
uint8_t highest_priority = 255;
uint8_t selected_task = 255;
uint32_t current_time = millis();
// Ищем задачу с наивысшим приоритетом, готовую к выполнению
for (uint8_t i = 0; i < task_count; i++) {
if (tasks[i].state == 1 && tasks[i].priority < highest_priority) {
if (current_time - tasks[i].last_run >= tasks[i].period) {
highest_priority = tasks[i].priority;
selected_task = i;
}
}
}
if (selected_task != 255) {
// Выполняем задачу
tasks[selected_task].state = 2;
tasks[selected_task].last_run = current_time;
tasks[selected_task].task_func();
tasks[selected_task].state = 1;
}
}
// Пример задачи 1: моргание LED
void task_led() {
static uint8_t state = 0;
state = !state;
digitalWrite(13, state);
}
// Пример задачи 2: чтение датчика
void task_sensor() {
int val = analogRead(A0);
Serial.println(val);
}
void setup() {
Serial.begin(115200);
pinMode(13, OUTPUT);
add_task(task_led, 500, 1); // Каждые 500 мс, высокий приоритет
add_task(task_sensor, 1000, 2); // Каждые 1000 мс, низкий приоритет
}
void loop() {
scheduler();
// Можно добавить idle-задачу для низкого приоритета
}
Расширенная RTOS с прерываниями (вытесняющая многозадачность):
; Переключение контекста через таймер
TIMER1_COMPA_vect:
; Сохраняем контекст текущей задачи
PUSH R0
PUSH R1
; ... все регистры
; Сохраняем SP
IN R16, SPL
IN R17, SPH
STS current_task_sp, R16
STS current_task_sp+1, R17
; Выбираем следующую задачу
CALL scheduler_asm
; Восстанавливаем SP следующей задачи
LDS R16, next_task_sp
LDS R17, next_task_sp+1
OUT SPL, R16
OUT SPH, R17
; Восстанавливаем регистры
POP R1
POP R0
RETI