Omnicake
Mar 24 2014, 00:16
Здравствуйте, пытаюсь разобраться в логике включения таймера SysTick на Cortex-M3, которую использует стандартная библиотека CMSIS. Взять, например, команду загрузки в регистр SYST_RVR: за это дело в CMSIS отвечает команда вида "SysTick->LOAD=TimerTick", где TimerTick - выбранное время через которое таймер будет обнулять свое значение. В дизассемблере данная команда представляет из себя три команды на ассемблере:
LDR r0,[pc,#32] ; @0x08000210;
MOV r1,#0xE000E000;
STR r0,[r1,#0x14];
Команда MOV в данном случае, как я понял, пишет в R1 стартовый адрес всего регистра SysTick, затем командой STR смещает его на 14 бит, попадая на адрес SYST_RVR (однако при этом я почему-то не вижу, чтобы регистр R1 менялся). А вот, что делает первая строка, почему она взаймодействует с программным счетчиком, и где в этих трех командах происходит загрузка самого значения TimerTick, я не могу понять. Потому прошу помощи.
Проект запущен на Keil mVision, процессор STM32f10x.
Самому удалось разобраться, откуда берется TimerTick, но использование первой строчки все равно осталось загадкой.
kolobok0
Mar 24 2014, 01:53
Цитата(Omnicake @ Mar 24 2014, 04:16)

....однако при этом я почему-то не вижу, чтобы регистр R1 менялся...
что делает первая строка, почему она взаймодействует с программным счетчиком, и где в этих трех командах
происходит загрузка самого значения TimerTick, я не могу понять....
1) включаем компьютер
2) запускаем любой броузер
3) набираем сайт яндекса ya.ru
4) пишем магическую комбинацию поиска ASM ARM (не забываем нажать ввод!)
5) смотрим одну из первых ссылок по ассемблеру арма
6) читаем...
LDR, STRКак следует из описания.
LDR загружает ИЗ памяти по адресу (то что в квадратных скобках)
STR сохраняет В память по адресу (то что в квадратных скобках)
Отсюда понятно, что
1) Регистр r1 меняться НЕ должен(это адрес а не таргет регистр, читай построение ОЗУ у данного арма и доступ к портам, переферии и иже)
2) само значение счётчика r0 берётся из памяти по адресу [pc, #32] (смещение 32 относительно текущего указателя команды)
zhevak
Mar 24 2014, 02:08
Ваша проблема в том, что Вы не знаете команд Cortex. Вы их неправильно понимаете.
Команда
LDR r0,[pc,#32] ; @0x08000210;
загружает в регистр R0 4-байтное значение (число), которое находится рядом с кодом программы. Доступ к этому значению осуществляется относительно программного счетчика. В комментариях к листингу указан адрес -- 0х08000210.
Видимо, в исходном тексте программы Вы описывали переменную (точнее -- константу). А это ничто иное, как данные. Данные бывают разные -- константы, переменные. Переменные бывают инициализированными и неинициализированными. Кроме данных, в программах присутствует код. В общем, программа состоит из разного рода кирпичиков.
Более того, проекты, как правило, представляют представляют собой (или состоят из) нескольких исходных файлов. Каждый исходный файл проекта может иметь и программный код, и константы, и инициализированные данные и так далее. Так вот, задача ликовщика заключается в том, чтобы пройтись по всем объектным (откомпилированным) файлам и сгруппировать "корпичики" в кучки. Кучка кирпичиков типа "программный код", кучка кирпичиков типа "инициализированные данные", кучка кирпичиков типа "неинициализированные данные" и так далее.
Эти кучки называются секциями. Вы наверно уже встречались с их названиями -- .text, .bss, .data и так далее. Названия секций у разных компиляторов (линковщиков) могут отличаться. Я сижу в Линуксе и использую gcc, Вы сидите под Виндовсом и используете Кайл. У нас секции скорее всего будут называться по-разному. Но назначение секций, как правило, соблюдается.
Итак, возвращаемся к Вашей программе. У вас в коде, было какое-то определение данных. Эти данные после линковки попали на адрес, который находится рядом с секцией кода. Чтобы переместить эти данные (значение) из памяти в регистр, понятно, нужно как-то адресовать эту ячейку памяти. Для данного случая оказалось удобнее адресовать ее относительно программного счетчика, что и было сделано приведеной выше командой.
Следующая команда
MOV r1,#0xE000E000;
запиысывает в регистр R1 базовый адрес Системного Таймера.
Системный Таймер -- это группа регистров. Точно так же следует думать и про другие устройства. Например, порт ввода-вывода -- это тоже группа регистров с каким-то базовым адресом. USART, DMA, ADC и так далее -- все эти устройства имеют по нескольку (иногда даже десятков) регистров.
С одной стороны, в микроконтроллерах многие устройства зачастую присутствуют в нескольких экземплярах. А с другой, адресное пространство для регистров практически не ограничено (как антипример, микроконтроллеры ATMEL AVR -- там куда-только не пихают регистры и биты!)
Поэтому оказывается, что более удобно выделить для конкретного устройства базовый адрес в этом адресном пространстве и более не париться с размещением битов и байтов. Но так как, допустим, портов ввода-вывода в микроконтроллере может быть несколько экземпляров, а они все одинаковые, то было бы разумно для каждого порта назначить какой-то базоавый адрес для группы его регистров. А доступ к конкретному регистру осуществлять относительно этого базоаого адреса. Таким образом, всё становится унифицировано и удобно
Следующая команда
STR r0,[r1,#0x14];
сохраняет содержимое регистра R0 по базовому адресу (который находится в регистре R1) плюс смещение 0х14.
(Простите за то, что опять написал много. Был в ударе.)
Omnicake
Mar 24 2014, 06:38
Спасибо за развернутые ответы, помогло разобраться.