понедельник, 06 августа 2012
Пустой код, конечно, хорошо, но он ничего полезного не делает.
Нужно его заставить хотя бы светодиодами поморгать. Алгоритм настройки можно посмотреть в более ранней статье про язык Си, а тут я рассмотрю как энто делать на ассемблере.
Все команды должны перед собой иметь хотя бы один пробел! Иначе команда будет трактоваться как метка, что не здорово.
читать дальше...
Для этого нужно знать несколько команд.
Во-первых, загрузка большого числа (32 бита) в регистр.
LDR Rn, =число.
Rn — название регистра (R0, R1, ...), число — то, что хотим загрузить.
Например:
LDR R0, =0x40023830 ; Адрес регистра RCC_AHB1ENR
Для красоты и читабельности число можно заменить обозначением:
; Загрузим в регистр R0 адрес регистра RCC_AHB1ENR
LDR R0, =RCC_AHB1ENR
Обозначение должно быть объявлено в начале файла примерно так:
RCC_AHB1ENR EQU 0x40023830
Так можно обозвать любые константы и использовать их в коде.
Во-вторых, загрузка из памяти по заданному в регистре адресу.
LDR Rn, [Rm]
Rn — куда читаем,
Rm — адрес, откуда читаем.
Например, хотим прочитать, что записано в регистре RCC_AHB1ENR, адрес которого у нас хранится в регистре R0.
; Прочитаем в регистр R1 его содержимое
LDR R1, [R0]
Запись происходит аналогично, только команда называется STR:
; Запишем обратно
STR R1, [R0]
Между чтением и записью необходимо установить бит, отвечающий за тактирование порта D.
На помощь приходят команда логики OR.
ORR Rd, Ra, Rb
ORR Rd, Ra, #число
Rd — куда записывается результат;
Ra — первый операнд;
Rb — второй операнд;
число — вариант записи второго операнда прямо числом, если число удовлетворяет ряду требований (см. документацию).
Есть и иные варианты, но фиг с ними пока.
Пример: установка бита.
; Установим бит тактирования порта D
ORR R1, R1, #RCC_AHB1ENR_GPIODEN ; RCC_AHB1ENR_GPIODEN объявлен как 0x00000008, бит 3.
Точно так же записываются и ряд других логических и математических операций, список есть в мануале по ядру.
Зная это, уже можно настраивать периферию и даже моргать светодиодами. Но грузить каждый раз адрес регистра так не круто. Он лежит во флеше отдельно и ядру приходится делать лишний запрос.
Очень помогает в борьбе с этим косвенная адресация.
Типа как оператор -> в языке Си.
Записывается:
LDR Rn, [Rm, #смещение]
STR Rn, [Rm, #смещение]
То есть к адресу, записанном в Rm добавляется некоторое смещение прям вот так. Что удобно, смещение от базового адреса периферии для каждого регистра указано прямо в Referense Manual. Конечно, базовый адрес тоже указан.
Пример:
LDR R1, [R0, #GPIO_MODER_OFFSET] ; GPIO_MODER_OFFSET — смещение от базового адреса периферии GPIO до регистра MODER: 0x00000000
К слову, базовый адрес GPIOD: 0x40020C00. Все адреса можно подглядеть в даташите или Reference Manual.
Безусловный переход.
Бесконечный цикл делается с помощью этой команды. Выполнение продолжится с метки, указанной в аргументе:
B метка
Метка может располагаться как до команды, так и после неё.
Последнее, что требуется: задержка.
Можно её оформить в виде отдельной процедуры.
Процедура оформляется так:
; Программная задержка
; R0 - величина задержки
delay PROC
; Какой-то полезный код
BX LR ; Возрат (типа return)
ENDP
BX LR — переход по адресу, записанному в регистре LR (R14). В этот регистр автоматически кладётся адрес команды, следующей за вызовом подпрограммы. Соответственно, эта команда осуществляет выход из процедуры.
Если есть вложенные процедуры, содержимое регистра надо сохранять, так как он перезаписывается автоматически при выполнении команд вызова подпрограммы вне зависимости от его содержания =) Но об этом потом.
Код задержки прост: принимаем число в регистре R0 и вычитаем его до тех пор, пока оно не станет 0.
; Вычитаем единицу
SUBS R0, R0, #1
; Пока не обнулилась, крутим дальше
BNE delay
Флаг S у процедуры SUB обозначает, что при выполнении команды будут выставлены флаги статуса, соответствующие результату выполнения. Ну, то есть, не ноль ли результат, было ли переполнение или заём и т.д.
Условный переход формируется из команды B и условного суффикса. Весь список можно посмотреть тут: Conditional execution.
В данном случае, выполняется проверка на «не ноль» (NE = Not Equal).
И последнее, вызов подпрограммы.
BL метка
метка — название подпрограммы. Пример:
BL delay
Именно команда перехода с суффиксом L сохраняет адрес следующей за ней команды в регистре LR.
Пример кода, моргающего светодиодами:
main.s.
<< Предыдущее Следующее >>
@темы:
arm,
программизмы,
электроника,
ассемблер,
stm32f4discovery,
stm32