Omnicake
Участник-
Постов
56 -
Зарегистрирован
-
Посещение
Репутация
0 ОбычныйИнформация о Omnicake
-
Звание
Участник
Информация
-
Город
Array
-
Второй LDR r0, [r0] грузит метку TaskTableStart ее адрес смещен на 4 байта относительно TaskPointer и используется как активный указатель плюс его адрес потом записывается по достижению конца списка. По поводу возвращения из 1 задачи я уже написал: я поставил последовательность - 1,3,4,2 задача. Они все отработали нормально до 2ой. Меня смущает, что все указатели и описания задач при этом абсолютно одинаковы и отличаются лишь номером. А на 24 смещено, из за того что в ddd1 со смещением на 24 байта лежит указатель на стэк задачи, которым я командой LDR sp, [r1,#24] подменяю текущий.
-
Здравствуйте. Обнаружил в своем проекте ошибку, и никак не могу понять, в чем причина. Сначала опишу то, где это возникает: Я делаю простейший переключатель задач, используя Keil Uvision и процессор STM32. Диспетчер срабатывает от прерывания таймера и в зависимости от статуса и значения «количества шагов» либо выходит из прерывания на нужную задачу, либо переключается на следующую. Реализовано это таким образом: 1. Массив указателей на задачи. TaskPointer DCD TaskTableStart TaskTableStart DCD ddd1 DCD ddd2 DCD ddd3 DCD ddd4 TaskTableEnd END Где ddd – указатель на задачу, вида: ddd1={1,1,4,0,(int*)task1,(int*)task1,(int*)Stack_task1,(int*)Stack_task1}; Первое число это статус, второе количество шагов, также есть указатели на метки задачи и метку стэка задачи. В стэк, для того чтобы из прерывания корректно выходило в задачу, изначально дописываю такие числа: int Stack_task1[512]={0xfffffff9,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,(int*)task1,(int*)task1,0x21000000}; Структура полностью повторяет то, что делает Cortex-M3 при срабатывании прерывания (0 поставил на месте регистров R1-R4,R12 так как их состояние при первом запуске неважно) При входе в прерывание диспетчер вызывается так Systick_Handler { PUSH {LR} BL scheduler POP {pc} } Вызов задачи делаю так: LDR r0, =TaskPointer LDR r0, [r0] LDR r0, [r0] MOV r1, r0 LDR SP, [r1,#24] BX LR Тем самым после BX LR идет выход из диспетчера на команду POP {pc} и срабатывает выход из прерывания на задачу по метке записанной в стэке. Смену указателей на задачу делаю прибавлением 4 битов к текущему адресу и записью в TaskTableStart. Тем самым перед следующим срабатыванием диспетчера указатель заменяется. Задачи task1, task2, task3, task4 абсолютно идентичны (отличаются только метками) . И вот тут возникает проблема со стэками этих задач. При первом срабатывании диспетчер подгружает указатель и стэк для первой задачи и запускает ее, выходя из прерывания и потом вновь приходит на прерывание. Вид стэка задачи при первом заходе на прерывание и втором (после выполнения команды PUSH {LR} приведены на рисунках. Как видно все отработало нормально и число 0xfffffff9 осталось на месте. Однако при переключении на вторую задачу и выполнение тех же самых действий получается вот так: Причем если выбрать адрес за 4 бита до метки до число 0xfffffff9, используемое для выхода из прерывания окажется там. Однако так как я вызываю стэк по метке при выполнении команды POP {pc} туда грузится там самая 0x00000001 и уводит в ошибку. Причем самое интересное, если поменять порядок указателей например на TaskTableStart DCD ddd1 DCD ddd3 DCD ddd2 DCD ddd4 То сработает первая задача, переключится на третью, третья переключится на вторую и та опять выдаст ошибку. Понять такую ненависть программы к двойке я не могу, потому спрашиваю здесь. Почему в случае с первой задачей стэк по метке оказался без изменений, а во втором случилась такая беда?
-
Починил, правда, для меня, это шаманством осталось. Я говорил выше, что компилятор создает файл main.s вместо main.c, а так как внутри него я не мог ничего редактировать (он создан компилятором) я решил просто скопировать его и заменить им файл main.c, после этого когда я запустил в шедулере этот код: LDR r0, =TaskPointer LDR r0, [r0] LDR r0, [r0] MOV r1, r0 LDR sp, [r1,#24] BX lr При этом стэк для задачи я задал вручную вот так: int Stack_task1[512]={0x00000000,0xfffffff9,0x00000007,0xe000e000,0x200021e8,0x200021e8,0x200021 c4,0x08000211,0x08000212,0x21000000}; поместив в него нужные метки и то самое число. С этим стэком я уже запускал до этого (на main.c) и ничего не работало, однако тут удалось: после BX LR он вышел из шедулера на хэндлер, выполнил POP {pc} и прыгнул на задачу. Метод скорее всего "напролом" но от него уже можно плясать. Спасибо всем за советы и помощь, буду работать дальше.
-
Видимо это Keil uVision и делает: после того как я добавил ассемблерные вставки в main, написанный на C, он создал автоматически файл main.s и там функция SysTick_Handler { scheduler(); } Выглядит вот так SysTick_Handler PUSH {r4,lr} BL systick POP {r4,pc} Однако из-за того, что из systick я прыгаю на другую программу, POP {r4,pc} пропускается. Значит, судя по всему, нужно делать это внутри шедулера.
-
Тут возможно я недостаточно подробно описал, при входе в прерывание там действительно лежит число EXC_RETURN но затем, из за того что в обработчике у меня стоит вызов другой программы (а именно переключателя задач), он уходит на ту программу, помещая в LR адрес возврата.
-
1. Ну у меня и отображается активным MSP все это время, и до прерывания и внутри него. Командой LDR я меняю указатель на метку Stack_task1 - массив из 512 чисел, который я взял за стэк задачи. 2. Как корректно класть туда эту структуру? Заранее прописывать ее в массив в самой задаче или загонять через PUSH необходимые регистры?
-
Локальные стэки я задал, в каждой задаче у меня прописано Stack_task1[512]. LDR sp, [r1,#24] как раз и грузить адрес метки Stack_task1. А по поводу Может я чего-то не увидел, но напрямую через команду MOV в PC на Cortex M3 вроде бы писать нельзя, компилятор мне выдает ошибку. Да и в инструкции написано, что грузятся значения в PC только через LDR или POP, а для этого нужно магическое число куда-то положить (я и положил его в стэк задачи).
-
Видимо действительно у меня нет понимания. Я решаю, извините за каламбур, задачу переключения задач в ассемблере. Для этого хотелось использовать прерывание от системного таймера, так как там вполне ясный и понятный режим срабатывания (время прошло - сработало прерывание - задача сменилась). Про PendSV мне удалось найти только информацию о том, что он пригодится при переключении задач. Но то как его вызывать, как он срабатывает и что делает - нет. Каким образом тогда построить стэк, чтобы при записи числа EXC он правильно подцеплялся?
-
Используется MSP, число там лежит точно, так как в симуляторе я могу свободно просматривать память и пошагово следить за программой. После входа в прерывание у меня лежит LR возврата на main, а мне нужно из прерывания уйти в другую задачу, я и меняю LR внутри обработчика но по выходу на задачу процессор остается в обработке прерывания.
-
Попробовал сделать такое после входа в обработчик LDR r0, =TaskPointer LDR r0, [r0] LDR r0, [r0] MOV r1, r0 LDR sp, [r1,#24];Загрузил стэк программы MOVW R2,#0x00d0; в котором по адресу 0x200000d0 MOVT R2,#0x2000; положил число 0xFFFFFFF9 LDR pc, [r2]; Загрузил его в PC И улетел на HardFault. Что я не так сделал?
-
Да, находился в Handler mode. Под "простым выходом из прерывания" я имел ввиду процесс входа в прерывание и выход на ту же функцию, откуда оно было вызвано, потому что при таком режиме все работало нормально. Сейчас нет возможности проверить ваш способ, вечером попробую. Спасибо за совет.
-
Перепробовал вчера такие варианты: 1) Запись в LR или PC значения EXC_RETURN уводит в HardFault исключение, то есть он воспринимает это как просто переход в неадреуемую область. 2) Проверил, что пишется в стэк при простом выходе из прерывания (то есть в шедулере оставил просто команду BX LR), и скопировал записанные значения в стэк задачи. Результат тот же, значения он не подцепил. Хотя в стэк были записаны и PC регистры и LR и значение FFFFFFF9(EXC_RETURN).
-
Оно и происходит внутри прерывания, ведь scheduler() выполняется внутри SysTick_Handler. Сейчас обратил внимание на другую особенность при обработке задачи. В Cortex-M# есть регистр xPSR, которые содержит в себе под-регистр ISR в момент до прерывания значение этого регистра равно 0, при входе в прерывание оно изменяется на 15 (что как я понимаю соответствует прерыванию от системного таймера), однако при выходе на task1 ISR не обнуляется, а так и остается равным 15. Есть ли возможность ручной корректировке этого бита, и можно ли с помощью этого решить проблему?
-
Таймер точно работает и генерирует прерывания, для примера убрал вызов шедулера и поставил простейшую команду i++, он корректно увеличивает ее через равные промежутки времени. В инструкции меня смущает строка "The processor saves an EXC_RETURN value to the LR on exception entry." Получается когда я заменяю LR внутри прерывания на LR задачи, я затираю тот самый EXC_RETURN value?