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.
133 lines
3.1 KiB
133 lines
3.1 KiB
|
7 years ago
|
#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));
|