Давайте я попробую все-таки подробностей добавить.
Есть 4 задачи. В порядке возрастания приоритетов:
1. Основной поток (тот который main()). На данный момент там банальный for(;;). Чего там будет, я пока не знаю, и поэтому, этот поток трогать мне нельзя.
2. Задача обработки. Длительная, выполняется намного дольше, чем промежуток между вызовами задач 3,4. Во время выполнения должны уметь получать управление задачи 3,4, причем с минимальным временем отклика (особенно 4). Запуск задачи происходит от события. По окончанию обработки задача засыпает до следующего события.
3. Задача - не самое приоритетное прерывание. От таймера, но вызывается часто - 256/512 тактов. В задаче - по быстренькому опрос периферии и по результатам - возможна генерация события для задачи 2. Во время выполнения этой задачи необходимо обеспечить возможность быстрого реагирования на прерывание для задачи 4.
4. Самое приоритетное прерывание. Входит, выполняет дело, по результатам дела генерирует или не генерирует событие, выходит.
Да, забыл. Задача 1 тоже потом может будет генерить события для задачи 2, а может и не будет.
Для такого менеджмента задач идеально использовать RTOS, но есть одно но - пока переключаются контексты поезд задачи 4 уходит далеко - в смысле, время реакции не устраивает. Значит, надо делать подобие RTOS, но с минимальными временами нахождения в состояниях c запретом прерываний.
План у меня таков - задача 2 - это обработчик прерывания, запрос на выполнение которого я могу создать в любой момент (это генерация события), а вот обработать - только тогда, когда не выполняются задачи 3 или 4 (и не находимся внутри задачи 2, дабы не допустить вложенности). Для этого мне и необходимо раздельное управление флагом запроса и битом разрешения прерывания.
Вариант со счетчиком плохо проходит в связи с тем, что увеличение/уменьшение счетчика требует сохранения SREG (с последующим восстановлением) и работу с самим счетчиком до разрешения прерываний.
Вот примерно такой код
Код
#define TASK2_LOCK GPIOR0_Bit0
#define TASK3_LOCK GPIOR0_Bit1
#define DISABLE_TASK2() {asm("PUSH R16");ACSR=(1<<ACD)|(1<<ACIS1);asm("POP R16");}
#define ENABLE_TASK2() {asm("PUSH R16");ACSR=(1<<ACD)|(1<<ACIS1)|(1<<ACIE);asm("POP R16");}
#define WAKEUP_TASK2() {ACSR=(1<<ACD)|(1<<ACIS1)|(1<<ACIS0);}
#pragma diag_suppress=Ta006
__interrupt void TASK2(void)
{
__no_operation();
//....
//....много всякой долгой каки...
main(); //Например так
//....
__no_operation();
}
#pragma diag_default=Ta006
#pragma vector=ANA_COMP_vect
__interrupt __raw void TASK2dispatch(void)
{
DISABLE_TASK2();
TASK2_LOCK=1;
__enable_interrupt();
((void(*)(void))TASK2)();
__disable_interrupt();
TASK2_LOCK=0;
ENABLE_TASK2();
}
#pragma diag_suppress=Ta006
__interrupt void TASK3(void)
{
__no_operation();
//....
//....не очень много всякой долгой каки...
if (PINB_Bit0) WAKEUP_TASK2(); //К примеру
//....
__no_operation();
}
#pragma diag_default=Ta006
#pragma vector=TIMER0_OVF_vect
__interrupt __raw void TASK3dispatch(void)
{
if (TASK3_LOCK) return;
TASK3_LOCK=1;
DISABLE_TASK2();
__enable_interrupt();
((void(*)(void))TASK3)();
__disable_interrupt();
TASK3_LOCK=0;
if (!TASK2_LOCK) ENABLE_TASK2();
}
#pragma vector=INT0_vect
__interrupt void TASK4(void)
{
//Тут тоже колдовство, запрещаем все прерывания, например
TIMSK0=0;
//Запрещаем и TASK2
DISABLE_TASK2();
__enable_interrupt();
//Чего-то делаем, тут еще бывает INT1, но это уже не суть
__disable_interrupt();
if (PINB_Bit1) WAKEUP_TASK2(); //К примеру
TIMSK0=1<<TOV0;
if (!TASK2_LOCK) ENABLE_TASK2();
}
А вот чего выходит после компилятора
Код
RSEG CODE:CODE:NOROOT(1)
// 176 __interrupt void TASK2(void)
TASK2:
// 177 {
ST -Y, R24
ST -Y, R31
ST -Y, R30
ST -Y, R3
ST -Y, R2
ST -Y, R1
ST -Y, R0
ST -Y, R23
ST -Y, R22
ST -Y, R21
ST -Y, R20
ST -Y, R19
ST -Y, R18
ST -Y, R17
ST -Y, R16
IN R24, 0x3F
// 178 __no_operation();
NOP
// 179 //....
// 180 //....много всякой долгой каки...
// 181 main(); //Например так
RCALL main
// 182 //....
// 183 __no_operation();
NOP
// 184 }
OUT 0x3F, R24
LD R16, Y+
LD R17, Y+
LD R18, Y+
LD R19, Y+
LD R20, Y+
LD R21, Y+
LD R22, Y+
LD R23, Y+
LD R0, Y+
LD R1, Y+
LD R2, Y+
LD R3, Y+
LD R30, Y+
LD R31, Y+
LD R24, Y+
RETI
// 185 #pragma diag_default=Ta006
// 186
// 187
// 188 #pragma vector=ANA_COMP_vect
RSEG CODE:CODE:NOROOT(1)
// 189 __interrupt __raw void TASK2dispatch(void)
TASK2dispatch:
// 190 {
// 191 DISABLE_TASK2();
PUSH R16
LDI R16, 130
OUT 0x30, R16
POP R16
// 192 TASK2_LOCK=1;
SBI 0x1E, 0x00
// 193 __enable_interrupt();
SEI
// 194 ((void(*)(void))TASK2)();
RCALL TASK2
// 195 __disable_interrupt();
CLI
// 196 TASK2_LOCK=0;
CBI 0x1E, 0x00
// 197 ENABLE_TASK2();
PUSH R16
LDI R16, 138
OUT 0x30, R16
POP R16
// 198 }
RETI
REQUIRE _A_ACSR
REQUIRE _A_GPIOR0
// 199
// 200 #pragma diag_suppress=Ta006
RSEG CODE:CODE:NOROOT(1)
// 201 __interrupt void TASK3(void)
TASK3:
// 202 {
ST -Y, R16
// 203 __no_operation();
NOP
// 204 //....
// 205 //....не очень много всякой долгой каки...
// 206 if (PINB_Bit0) WAKEUP_TASK2(); //К примеру
SBIS 0x03, 0x00
RJMP ??TASK3_0
LDI R16, 131
OUT 0x30, R16
// 207 //....
// 208 __no_operation();
??TASK3_0:
NOP
// 209 }
LD R16, Y+
RETI
REQUIRE _A_ACSR
REQUIRE _A_PINB
// 210 #pragma diag_default=Ta006
// 211
// 212 #pragma vector=TIMER0_OVF_vect
RSEG CODE:CODE:NOROOT(1)
// 213 __interrupt __raw void TASK3dispatch(void)
TASK3dispatch:
// 214 {
// 215 if (TASK3_LOCK) return;
SBIC 0x1E, 0x01
RJMP ??TASK3dispatch_0
// 216 TASK3_LOCK=1;
SBI 0x1E, 0x01
// 217 DISABLE_TASK2();
PUSH R16
LDI R16, 130
OUT 0x30, R16
POP R16
// 218 __enable_interrupt();
SEI
// 219 ((void(*)(void))TASK3)();
RCALL TASK3
// 220 __disable_interrupt();
CLI
// 221 TASK3_LOCK=0;
CBI 0x1E, 0x01
// 221 if (!TASK2_LOCK) ENABLE_TASK2();
SBIC 0x1E, 0x00
RJMP ??TASK3dispatch_0
PUSH R16
LDI R16, 138
OUT 0x30, R16
POP R16
??TASK3dispatch_0:
RETI
REQUIRE _A_ACSR
REQUIRE _A_GPIOR0
// 222 }
// 223
// 224 #pragma vector=INT0_vect
RSEG CODE:CODE:NOROOT(1)
// 225 __interrupt void TASK4(void)
TASK4:
// 226 {
ST -Y, R17
ST -Y, R16
IN R17, 0x3F
// 227 //Тут тоже колдовство, запрещаем все прерывания, например
// 228 TIMSK0=0;
LDI R16, 0
STS 110, R16
// 229 //Запрещаем и TASK2
// 230 DISABLE_TASK2();
PUSH R16
LDI R16, 130
OUT 0x30, R16
POP R16
// 231 __enable_interrupt();
SEI
// 232 //Чего-то делаем, тут еще бывает INT1, но это уже не суть
// 233 __disable_interrupt();
CLI
// 234 if (PINB_Bit1) WAKEUP_TASK2(); //К примеру
SBIS 0x03, 0x01
RJMP ??TASK4_0
LDI R16, 131
OUT 0x30, R16
// 235 TIMSK0=1<<TOV0;
??TASK4_0:
LDI R16, 1
STS 110, R16
// 236 if (!TASK2_LOCK) ENABLE_TASK2();
SBIC 0x1E, 0x00
RJMP ??TASK4_1
PUSH R16
LDI R16, 138
OUT 0x30, R16
POP R16
// 237 }
??TASK4_1:
OUT 0x3F, R17
LD R16, Y+
LD R17, Y+
RETI
Вариант с прерываниями хорош еще и тем, что в задаче 1 я могу выполнить WAKEUP_TASK2();ENABLE_TASK2() - и сразу полетит выполять задачу 2. Все приоритеты красиво соблюдаются.