You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
3.1 KiB
132 lines
3.1 KiB
#include <stdint.h> |
|
#include <avr/io.h> |
|
#include <avr/interrupt.h> |
|
#include <util/atomic.h> |
|
#include <string.h> |
|
#include "os.h" |
|
#include "os_config.h" |
|
|
|
#ifndef ARRAYSIZE |
|
#define ARRAYSIZE(x) (sizeof(x)/sizeof(*(x))) |
|
#endif |
|
|
|
// Assemble tasks |
|
void os_asm_switch_to_task(void); |
|
|
|
// Temporary buffers for context switch |
|
volatile uint16_t ins_tmp; |
|
volatile uint8_t sreg_tmp; |
|
volatile uint16_t stack_tmp; |
|
volatile uint8_t reg_tmp[32]; |
|
|
|
struct os_task { |
|
uint8_t valid; |
|
os_task_func code_ptr; |
|
void* data_ptr; |
|
uint8_t* stack_ptr; |
|
uint8_t status; |
|
uint8_t registers[32]; |
|
uint8_t stack[OS_STACK_SIZE]; |
|
} tasks[OS_TASK_COUNT]; |
|
|
|
typedef size_t os_task_id_t; |
|
#define TASK_IDX_INVALID -1 |
|
static os_task_id_t task_current_idx = TASK_IDX_INVALID; |
|
|
|
void os_init(void) { |
|
for (size_t i = 0; i < ARRAYSIZE(tasks); i++) { |
|
tasks[i].valid = 0; |
|
} |
|
} |
|
|
|
void os_run(void) { |
|
TCCR0A = 0; |
|
OCR0A = 0xFF; |
|
TCCR0B = _BV(WGM12) | _BV(CS10); |
|
TIMSK0 |= _BV(OCIE1A); |
|
|
|
// Wait fo fist task switch |
|
sei(); |
|
TCNT0 = 0xFF; |
|
while (1); |
|
} |
|
|
|
static void os_do_switch_to_task(os_task_id_t task_idx) { |
|
task_current_idx = task_idx; |
|
struct os_task* task = tasks + task_idx; |
|
|
|
for (size_t i = 0; i < ARRAYSIZE(task->registers); i++) { |
|
reg_tmp[i] = task->registers[i]; |
|
} |
|
ins_tmp = (uint16_t) task->code_ptr; |
|
sreg_tmp = task->status; |
|
stack_tmp = (uint16_t) task->stack_ptr; |
|
|
|
// Reset Timer and do context switch |
|
TCNT0 = 0; |
|
TIFR0 &= ~_BV(OCF0A); |
|
os_asm_switch_to_task(); |
|
} |
|
|
|
void os_interrupt_saved(void) { |
|
os_task_id_t next_task_idx = TASK_IDX_INVALID; |
|
|
|
if (task_current_idx != TASK_IDX_INVALID) { |
|
struct os_task* task_current = tasks + task_current_idx; |
|
|
|
for (size_t i = 0; i < ARRAYSIZE(task_current->registers); i++) { |
|
task_current->registers[i] = reg_tmp[i]; |
|
} |
|
|
|
task_current->code_ptr = (void*) ins_tmp; |
|
task_current->status = sreg_tmp; |
|
task_current->stack_ptr = (uint8_t*) stack_tmp; |
|
|
|
next_task_idx = task_current_idx + 1; |
|
next_task_idx %= ARRAYSIZE(tasks); |
|
} else { |
|
if (tasks[0].valid) { |
|
next_task_idx = 0; |
|
} |
|
} |
|
|
|
if (next_task_idx == TASK_IDX_INVALID) { |
|
while (1); |
|
} |
|
|
|
os_do_switch_to_task(next_task_idx); |
|
} |
|
|
|
void os_task_add(os_task_func func, void* data) { |
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { |
|
for (size_t i = 0; i < ARRAYSIZE(tasks); i++) { |
|
struct os_task* t = tasks + i; |
|
if (t->valid) { |
|
continue; |
|
} |
|
|
|
t->valid = 1; |
|
t->code_ptr = func; |
|
t->stack_ptr = t->stack + sizeof (t->stack) - 1; |
|
t->status = 0; |
|
t->data_ptr = data; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void* os_current_task_get_data(void) { |
|
return tasks[task_current_idx].data_ptr; |
|
} |
|
|
|
void os_current_task_kill(void) { |
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { |
|
tasks[task_current_idx].valid = 0; |
|
} |
|
|
|
// Wait fo context switch. |
|
while (1); |
|
} |
|
|
|
// Linker magic to add interrupt vector |
|
void TIMER0_COMPA_vect(void) __attribute__((signal, naked, __INTR_ATTRS));
|
|
|