Страницы

Страницы

пятница, 10 февраля 2012 г.

STM32F4 Assembler Subroutine

В функциях LedOn и LedOff в начале и в конце есть команды:
PUSH {LR} ; Сохранить регистр LR в стеке.
; Полезный код
POP {PC} ; Поместить адрес возврата в PC. Выход из подпрограммы.

Вообще, необходимо придерживаться следующего соглашения о вызовах для удобства и совместимости с функциями, написанными на Си.
Аргументы передаются в регистрах R0-R3, первый в R0 и так далее. Если аргументов больше, то они передаются уже через стек.
Содержимое остальных регистров общего назначения, которые в процедуре используются, надо сохранить на стеке же. В том числе и LR, если есть вложенные процедуры.
Вообще, необходимо придерживаться следующего соглашения о вызовах для удобства и совместимости с функциями, написанными на Си.
Аргументы передаются в регистрах R0-R3, первый в R0 и так далее. Если аргументов больше, то они передаются уже через стек.
Содержимое остальных регистров общего назначения, которые в процедуре используются, надо сохранить на стеке же. В том числе и LR, если есть вложенные процедуры.
Сравните:

; Подпрограмма без вложенных вызовов подпрограмм
Func0   PROC
; бла-бла-бла
BX LR
ENDP

; Подпрограмма с возможностью вложенного вызова подпрограмм
Func1   PROC
PUSH {LR}
; бла-бла бла
BL Func0
; бла-бла-бла
POP {PC}
ENDP

Адрес вершины стека в кортексах записан первым числом в таблице векторов! Регистр SP инициализируется при сбросе именно этим числом.

_Vectors       DCD     __initial_sp               ; Вершина стека!
                DCD     Reset_Handler              ; Таблица векторов, первым идёт вектор сброса
                DCD     NMI_Handler
...
Локальные метки можно использовать с помощью директивы ROUT, но наименование жуть какое неудобное, мне не нравится.

Пример программы

; Test STM32F4-Assembler project
; Ня
    AREA    |.text|, CODE, READONLY
RCC_AHB1ENR           EQU 0x40023830
RCC_AHB1ENR_GPIODEN   EQU 0x00000008 ; 3 бит
GPIOD_BASE           EQU 0x40020C00
GPIO_MODER_OFFSET     EQU 0x00000000
GPIO_MODER_MODER12_0 EQU 0x01000000
GPIO_MODER_MODER13_0 EQU 0x04000000
GPIO_MODER_MODER14_0 EQU 0x10000000
GPIO_MODER_MODER15_0 EQU 0x40000000
GPIO_PUPDR_OFFSET     EQU 0x0000000C
GPIO_PUPDR_PUPDR12_0 EQU 0x01000000
GPIO_PUPDR_PUPDR13_0 EQU 0x04000000
GPIO_PUPDR_PUPDR14_0 EQU 0x10000000
GPIO_PUPDR_PUPDR15_0 EQU 0x40000000
GPIO_BSRRL_OFFSET     EQU 0x00000018 ; Сброс
GPIO_BSRRH_OFFSET     EQU 0x0000001A ; Установка
DELAY_COUNTER         EQU 0x000F4240 ; 1000000UL
GPIO_BSSR_PIN12       EQU 0x00001000 ; 12 бит
GPIO_BSSR_PIN13       EQU 0x00002000 ; 13 бит
GPIO_BSSR_PIN14       EQU 0x00004000 ; 14 бит
GPIO_BSSR_PIN15       EQU 0x00008000 ; 15 бит
; Инициализация системы тактирования
;            EXPORT SystemInit
;SystemInit  PROC
;           
;            BX LR ; Возвращаемся обратно
;           
;            ENDP
ROUT
; Получить маску для зажигания светодиода по его номеру
; R0 - номер светодиода, от 0 до 3
; Результат: R0.
; Если номер не верный, в R0 будет 0.
GetPinMask  PROC
            ; Другой способ выбора:
            CMP  R0, #0
            BEQ  GetPinMask_led0
           
            CMP  R0, #1
            BEQ  GetPinMask_led1
           
            CMP  R0, #2
            BEQ  GetPinMask_led2
           
            CMP  R0, #3
            BEQ  GetPinMask_led3
           
            ; Ошибка
            B    GetPinMask_error
           
GetPinMask_led0 ; Светодиод 0
            MOV  R0, #GPIO_BSSR_PIN12 ; 12 бит
            B    GetPinMask_return
GetPinMask_led1 ; Светодиод 1
            MOV  R0, #GPIO_BSSR_PIN13 ; 13 бит
            B    GetPinMask_return
           
GetPinMask_led2 ; Светодиод 2
            MOV  R0, #GPIO_BSSR_PIN14 ; 14 бит
            B    GetPinMask_return
           
GetPinMask_led3 ; Светодиод 3
            MOV  R0, #GPIO_BSSR_PIN15 ; 15 бит
            B    GetPinMask_return
GetPinMask_error ; Ошибка
            MOV   R0, #0
           
GetPinMask_return
            BX    LR
            ENDP
; Погасить светодиод по его номеру
; R0 - номер светодиода, от 0 до 3
LedOff      PROC
            ; Сохраним в стеке адрес возврата
            PUSH {LR}
            ; Получить маску
            BL   GetPinMask
           
            ; Если 0, то выходим
            CMP  R0, #0
            BEQ  LedOff_return
           
            ; В R0 теперь маска
            ; Погасить выбронный светодиод
            LDR R1, =GPIOD_BASE
            STR R0, [R1, #GPIO_BSRRH_OFFSET]
           
LedOff_return
            ; Возврат из процедуры
            POP  {PC}
            ENDP
           
; Зажечь светодиод по его номеру
; R0 - номер светодиода, от 0 до 3
LedOn       PROC
            ; Сохраним в стеке адрес возврата
            PUSH {LR}
            ; Получить маску
            BL   GetPinMask
           
            ; Если 0, то выходим
            CMP  R0, #0
            BEQ  LedOn_return
           
            ; В R0 теперь маска
            ; Зажечь выбронный светодиод
            LDR R1, =GPIOD_BASE
            STR R0, [R1, #GPIO_BSRRL_OFFSET]
           
LedOn_return
            ; Возврат из процедуры
            POP  {PC}
            ENDP
; Программная задержка
; R0 - величина задержки
delay       PROC
            ; Вычитаем единицу
            SUBS R0, R0, #1
            ; Пока не обнулилась, крутим дальше
            BNE delay
           
            BX LR
            ENDP
            EXPORT __main
__main      PROC
           
            ; Разрешим тактирование порта D
           
            ; Загрузим в регистр R0 адрес регистра RCC_AHB1ENR
            LDR R0, =RCC_AHB1ENR
            ; Прочитаем в регистр R1 его содержимое
            LDR R1, [R0]
            ; Установим бит тактирования порта D
            ORR R1, R1, #RCC_AHB1ENR_GPIODEN
            ; Запишем обратно
            STR R1, [R0]
           
            ; Установим режим работы выводов (выход)
            LDR R0, =GPIOD_BASE
            ; Прочитаем в регистр R1 его содержимое
            LDR R1, [R0, #GPIO_MODER_OFFSET]
           
            ; Установим необходимые биты для выводов 12-15
            ORR R1, R1, #GPIO_MODER_MODER12_0
            ORR R1, R1, #GPIO_MODER_MODER13_0
            ORR R1, R1, #GPIO_MODER_MODER14_0
            ORR R1, R1, #GPIO_MODER_MODER15_0
           
            ; Запишем обратно
            STR R1, [R0, #GPIO_MODER_OFFSET]
           
            ; Подтяжка к питанию
            LDR R0, =GPIOD_BASE
            LDR R1, [R0, #GPIO_PUPDR_OFFSET]
           
            ; Установим необходимые биты для выводов 12-15
            ORR R1, R1, #GPIO_PUPDR_PUPDR12_0
            ORR R1, R1, #GPIO_PUPDR_PUPDR13_0
            ORR R1, R1, #GPIO_PUPDR_PUPDR14_0
            ORR R1, R1, #GPIO_PUPDR_PUPDR15_0
           
            ; Запишем обратно
            STR R1, [R0, #GPIO_PUPDR_OFFSET]
           
            ; В регистр R2 запишем величину задержки
            LDR R4, =DELAY_COUNTER
           
__mainloop
            ; Зажжём светодиод 0
            MOV R0, #0
            BL  LedOn
           
            ; Задержка
            MOV R0, R4
            BL  delay
           
            ; Погасим светодиод 0
            MOV R0, #0
            BL  LedOff
           
            ; Зажжём светодиод 1
            MOV R0, #1
            BL  LedOn
           
            ; Задержка
            MOV R0, R4
            BL  delay
           
            ; Погасим светодиод 1
            MOV R0, #1
            BL  LedOff
           
            ; Зажжём светодиод 2
            MOV R0, #2
            BL  LedOn
           
            ; Задержка
            MOV R0, R4
            BL  delay
           
            ; Погасим светодиод 2
            MOV R0, #2
            BL  LedOff
           
            ; Зажжём светодиод 3
            MOV R0, #3
            BL  LedOn
           
            ; Задержка
            MOV R0, R4
            BL  delay
           
            ; Погасим светодиод 3
            MOV R0, #3
            BL  LedOff
           
            ; На очередной круг
            B __mainloop
            ENDP
    END

Комментариев нет:

Отправить комментарий