// Макросы
//================================================== ================================================== ==
#define HIGH_P(port, num) ( port |= (1 ‹‹ num) ) // 1. Высокий уровень
#define LOW_P(port, num) ( port &= ~(1 ‹‹ num) ) // 0. Низкий уровень
#define OUT_P(port, num) ( port |= (1 ‹‹ num) ) // Выход
#define IN_P(port, num) ( port &= ~(1 ‹‹ num) ) // Вход
#define SCAN_P(port, num) ( port & (1 ‹‹ num) ) // Поступает ли сигнал на порт
#define F_CPU 16000000UL
// Сохраняем контекст
#define SAVE_CONTEXT() \
asm volatile ( \
"push r0 \n\t" \
"in r0, __SREG__ \n\t" \
"cli \n\t" \
"push r0 \n\t" \
"push r1 \n\t" \
"push r2 \n\t" \
"push r3 \n\t" \
"push r4 \n\t" \
"push r5 \n\t" \
"push r6 \n\t" \
"push r7 \n\t" \
"push r8 \n\t" \
"push r9 \n\t" \
"push r10 \n\t" \
"push r11 \n\t" \
"push r12 \n\t" \
"push r13 \n\t" \
"push r14 \n\t" \
"push r15 \n\t" \
"push r16 \n\t" \
"push r17 \n\t" \
"push r18 \n\t" \
"push r19 \n\t" \
"push r20 \n\t" \
"push r21 \n\t" \
"push r22 \n\t" \
"push r23 \n\t" \
"push r24 \n\t" \
"push r25 \n\t" \
"push r26 \n\t" \
"push r27 \n\t" \
"push r28 \n\t" \
"push r29 \n\t" \
"push r30 \n\t" \
"push r31 \n\t" \
"lds r26, this_sp \n\t" \
"lds r27, this_sp + 1 \n\t" \
"in r0, 0x3d \n\t" \
"st x+, r0 \n\t" \
"in r0, 0x3e \n\t" \
"st x+, r0 \n\t" \
)
// Перемещаем указатель стека
#define LOAD_PTR_TO_SP() \
asm volatile ( \
"lds r26, this_sp \n\t" \
"lds r27, this_sp + 1 \n\t" \
"ld r28, x+ \n\t" \
"out __SP_L__, r28 \n\t" \
"ld r29, x+ \n\t" \
"out __SP_H__, r29 \n\t" \
)
// Востанавливаем контекст
#define RESTORE_CONTEXT()\
asm volatile ( \
"lds r26, this_sp \n\t" \
"lds r27, this_sp + 1 \n\t" \
"ld r28, x+ \n\t" \
"out __SP_L__, r28 \n\t" \
"ld r29, x+ \n\t" \
"out __SP_H__, r29 \n\t" \
"pop r31 \n\t" \
"pop r30 \n\t" \
"pop r29 \n\t" \
"pop r28 \n\t" \
"pop r27 \n\t" \
"pop r26 \n\t" \
"pop r25 \n\t" \
"pop r24 \n\t" \
"pop r23 \n\t" \
"pop r22 \n\t" \
"pop r21 \n\t" \
"pop r20 \n\t" \
"pop r19 \n\t" \
"pop r18 \n\t" \
"pop r17 \n\t" \
"pop r16 \n\t" \
"pop r15 \n\t" \
"pop r14 \n\t" \
"pop r13 \n\t" \
"pop r12 \n\t" \
"pop r11 \n\t" \
"pop r10 \n\t" \
"pop r9 \n\t" \
"pop r8 \n\t" \
"pop r7 \n\t" \
"pop r6 \n\t" \
"pop r5 \n\t" \
"pop r4 \n\t" \
"pop r3 \n\t" \
"pop r2 \n\t" \
"pop r1 \n\t" \
"pop r0 \n\t" \
"out __SREG__, r0 \n\t" \
"pop r0 \n\t" \
)
// Кол-во процессов
#define TASKS_COUNT 2
// Стандартные библиотеки
#include ‹avr/io.h›
#include ‹avr/interrupt.h›
#include ‹util/delay.h›
// Струнтура контекста
struct __attribute__((__packed__)) tack_data {
unsigned int stack_pointer; // Указатель стека
unsigned int stack_space; // Выделяемое место в стеке
void(*tack_fn)(void); // Ссылка на процесс
};
// Массив всех процессов
struct tack_data tack_block[TASKS_COUNT];
// id текущего прорцесса
unsigned char this_tack_id = 0;
// Ссылка на текущий контекст
struct tack_data *this_tack_data;
// Текущий указатель стека
unsigned int *this_sp;
// ISR_NAKED — запрещает компилятору делать функцию инлайновой
ISR(TIMER0_OVF_vect, ISR_NAKED) {
// Сохраняем контекст
SAVE_CONTEXT();
// Сбросить таймер
TCNT0 = 0x38;
// Переключение задачи
this_tack_id++;
if(this_tack_id ›= TASKS_COUNT) {
this_tack_id = 0;
}
this_tack_data = &tack_block[this_tack_id];
// Меняем указатель стека
this_sp = &tack_block[this_tack_id].stack_pointer;
// Востанавливаем контекст
RESTORE_CONTEXT();
asm volatile("reti");
}
// Создать процесс
void new_tack(void(*t)(), unsigned int stack_space) {
tack_block[this_tack_id].stack_pointer = 0;
tack_block[this_tack_id].stack_space = stack_space;
tack_block[this_tack_id].tack_fn = t;
this_tack_id++;
}
void init_RTOS() {
unsigned char i;
// Текущий указатель стека
unsigned int sp = SPH ‹‹ 8 | SPL;
// Сбрасываем, ибо юзали для newTasck
this_tack_id = 0;
// Обходим все процессы
for(i = 0; i ‹ TASKS_COUNT; i++) {
// Записываем указатель стека
tack_block[i].stack_pointer = sp;
// Поднимаем точку указателя стека
sp -= tack_block[i].stack_space; // 100 байт на регистры и т.п.
}
// Инициализация таймера на 0.1мм
ASSR = 0 ‹‹ AS0;
TCCR0 = (0 ‹‹ WGM00) | (0 ‹‹ COM01) | (0 ‹‹ COM00) | (0 ‹‹ WGM01) | (0 ‹‹ CS02) | (1 ‹‹ CS01) | (0 ‹‹ CS00);
TCNT0 = 0x38;
OCR0 = 0x00;
TIMSK = (0 ‹‹ OCIE2) | (0 ‹‹ TOIE2) | (0 ‹‹ TICIE1) | (0 ‹‹ OCIE1A) | (0 ‹‹ OCIE1B) | (0 ‹‹ TOIE1) | (0 ‹‹ OCIE0) | (1 ‹‹ TOIE0);
sei(); // Разрешаем прерывания
// Записываем ссылку на нулевой процесс в "this_tack_data"
this_tack_data = &tack_block[0];
// Записываем ссылку на указатель стека
this_sp = &tack_block[0].stack_pointer;
// Перемещаем указатель куда надо
LOAD_PTR_TO_SP();
// Запускаем нулевую задачу
tack_block[0].tack_fn();
}
// Первый процесс
void tack_1(void) {
while(1) {
HIGH_P(PORTE, 5);
_delay_ms(500);
LOW_P(PORTE, 5);
_delay_ms(500);
}
}
// Второй процесс
void tack_2(void) {
while(1) {
HIGH_P(PORTE, 4);
_delay_ms(200);
LOW_P(PORTE, 4);
_delay_ms(200);
}
}
// Главная функция
int main(void) {
OUT_P(DDRE, 4);
OUT_P(DDRE, 5);
new_tack(tack_1, 200);
new_tack(tack_2, 200);
init_RTOS();
while(1);
} |