В общем, выделил часок на свои изыскания и, определенно, пришел к некоторому успеху.
Повторюсь, что я хотел сделать.
Имеется FreeRTOS v10.0.0, компилятор ARMCCv5 Keil.
В системе есть 2 задачи:
CODE
xTaskHandle Task1Handle;
xTaskHandle Task2Handle;
void Task1(void *Parameters)
{
while(1)
{
HW_LED_TOGGLE(GPIO_LED1);
vTaskDelay(1000);
}
}
void Task2(void *Parameters)
{
while(1)
{
HW_LED_TOGGLE(GPIO_LED2);
vTaskDelay(500);
}
}
int main(void)
{
HW_MCUInit();
xTaskCreate(&Task1, "Task1", 128, NULL, 1, &Task1Handle);
xTaskCreate(&Task2, "Task2", 128, NULL, 1, &Task2Handle);
vTaskStartScheduler();
return 0;
}
Моя цель была в том, чтобы избавиться от единожды используемого прерывания SVC, которую индусы задействовали для запуска первой задачи. Для этого:
- Удалил обработчик SVC.
- Скорректировал функцию запуска первой задачи следующим образом:
Код
__asm void prvStartFirstTask(void)
{
PRESERVE8
// установка MSP
ldr r0, =0xE000ED08
ldr r0, [r0]
ldr r0, [r0]
msr msp, r0
dsb
// получение указателя на стековый фрейм задачи
ldr r3, =pxCurrentTCB
ldr r1, [r3]
ldr r0, [r1]
// последовательная загрузка контекста
ldmia r0!, {r4-r11, r14} // загрузка R4-R11, LR (LR здесь уже, по сути, является аттавизмом)
msr psp, r0 // установка PSP
dsb
// перевод CPU на использование стека PSP
mov r0, #2
msr control, r0
dsb
// извлечение XPSR из стекового фрейма
mrs r1, psp
add r1, r1, #28
ldr r0, [r1]
msr xpsr, r0
// извлечение оставшихся регистров
pop {r0-r3, r12, r14}
// корректировка бита 0 регистра PC
push {r0, r1}
mrs r0, psp
add r0, r0, #8
ldr r1, [r0]
orr r1, r1, #1
str r1, [r0]
pop {r0, r1}
// переход на активную задачу
pop {r15}
}
Примечания:
- Как видно в конце ассемблерного кода, я подменяю в памяти адрес задачи на тот же, только с установленным 0-м битом. И он останется там, в то время, как адреса других задач будут иметь 0 в этом бите. Можно и тут заморочиться и исправить, но я пока не стал. Большой проблемы это не доставит, т.к. этот адрес является адресом начала задачи, а при переключении контекста там будет уже другой адрес (чуть ниже точки входа в задачу).
- Поставил DSB в местах, где меняется SP, а также после записи в CONTROL (как рекомендует ARM). Хотелось бы выслушать мнение насчет расстановки DSB, желательно аргументированно
ISB убрал полностью, ибо не вижу в них смысла здесь.
- Подразумевается, что в системе нет загрузчика, или загрузчик, после выполнения своего кода и перехода на пользовательское приложение, привел все регистры в состояние после сброса, а также режим работы CPU и активный SP.
В общем-то проверил - работает, и я рад, что оно получилось