Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проблема со стэком при переключении задач на STM32
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Omnicake
Здравствуйте. Обнаружил в своем проекте ошибку, и никак не могу понять, в чем причина.
Сначала опишу то, где это возникает:
Я делаю простейший переключатель задач, используя 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


То сработает первая задача, переключится на третью, третья переключится на вторую и та опять выдаст ошибку. Понять такую ненависть программы к двойке я не могу, потому спрашиваю здесь.

Почему в случае с первой задачей стэк по метке оказался без изменений, а во втором случилась такая беда?
A. Fig Lee
Проще всего наверное, скачать CoOX, там простенькая ассемблерная программка переключения и сравнить.
Golikov A.
да уже обсуждали, ноги растут оттуда, откуда не надо качать какос и другой ОС....

Теперь вопрос, что есть за 4 бита до? Имелось ввиду за 4 байта? То есть у первой задачи стэк сохраняется куда надо, а у второй смещено на 1 слово 4 байтное в памяти?

А если стэки именно на это слово? Может там есть какое дурное правильно на выравнивание адресов? Указатель стэка на последние НЕ пустое слово в обоих задачах? Может в какой-то ошиблись?

я вот сейчас поглядел документацию....

Код
LDR        r0,    =TaskPointer
    LDR     r0, [r0]
    LDR     r0, [r0]
    MOV     r1,    r0
    LDR     SP, [r1,#24]
    BX      LR


это выглядит странно,
в r0 пихаете метку TaskPointer (адрес я так понимаю)
в r0 пихаете данные по адресу r0, то есть фактически адрес TaskPointer
зачем опять LDR r0, [r0] ?
и почему это все в итоге пихается в указатель стэка, и даже если каким то образом вы получили указатель стэка, почему оно пихается смещено на 24, когда после прерывания стэк увеличивается на 32?

ну и как то странно выглядят данные в стэке, здесь
http://infocenter.arm.com/help/index.jsp?t...a/BABIJDIC.html
другая последовательность... ну и константа у вас F9, то есть работа с основным стэком....

думаю из за неправильного смещения в стэке после первой задачи вы возвращаетесь сдвинуто, и запуск второй задачи выбирает криво... все таки я бы разделил стэки и Маин стэк использовал для нужд ОС, а задачам раздал бы программ стэки.

Omnicake
Второй LDR r0, [r0] грузит метку TaskTableStart ее адрес смещен на 4 байта относительно TaskPointer и используется как активный указатель плюс его адрес потом записывается по достижению конца списка. По поводу возвращения из 1 задачи я уже написал: я поставил последовательность - 1,3,4,2 задача. Они все отработали нормально до 2ой. Меня смущает, что все указатели и описания задач при этом абсолютно одинаковы и отличаются лишь номером. А на 24 смещено, из за того что в ddd1 со смещением на 24 байта лежит указатель на стэк задачи, которым я командой LDR sp, [r1,#24] подменяю текущий.
jcxz
Цитата(Golikov A. @ May 17 2014, 20:34) *
А если стэки именно на это слово? Может там есть какое дурное правильно на выравнивание адресов? Указатель стэка на последние НЕ пустое слово в обоих задачах? Может в какой-то ошиблись?

Я думаю ТС-у и невдомёк, что Cortex-M3 может опционально выравнивать стек на 8 при выполнении стекинга (при прерывании).
А всё потому, что "не читатель, а писатель..." smile3046.gif
Golikov A.
вот оно как....

Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.