Ярлыки

_GetPixelIndex (1) _SetPixelIndex (1) 3-phase (1) 800x480 (1) АЦП (1) генератор (1) синхронный усилитель (2) структура (1) учебный курс (1) шаговый двигатель (1) ШИМ (2) accert (1) AD7608 (1) AD8429 (1) ADC (5) amplifer (1) arccos (1) arcsin (1) arctang (2) arctg (3) ARM (2) arm_sqrt_q15 (2) assembler (6) ASSERT (1) atan (2) bit (1) Bitband (1) boot (3) bootlloader (1) BUTTON (1) C (5) C# (1) CAN (2) CC2530 (5) CMSIS (4) command (1) Cordic (1) Core746I (1) CubeMX (4) DBGMCU (2) debug (2) debug.ini (1) delegate (1) Digital Potentiometers (1) DigitalPOT (1) Discovery (1) DMA (9) DMA2D (1) DSP (1) DSP library (1) DWT (1) EFM32 (5) EmWin (9) EXTI (1) FATFS (1) FMC (2) FreeRTOS (2) gl868-dual cmux (1) GPIO (4) GUI (2) GUIBuilder (1) GUIDRV_CompactColor_16 (1) HAL (3) HappyGecko (1) Hard Fault (2) heap (1) I2C (1) ID (1) ILI9320 (1) ILI9325 (1) Initialisation (1) InitLTDC (1) Instrumentithion (1) Interrupt (4) ITR (1) JTAG (1) Keil (5) LCDConf (2) lock-in (1) LTCD (1) LTDC (3) main (1) memory (1) MINI_STM32 Revision 01 (1) nBoot0 (1) NVIC (1) OnePulse (2) OSAL (4) pack (1) phase (1) printf (3) Pulse (1) PWM (12) RCC (2) RCR (1) Register (1) RESET (2) RS232 (3) RSS (1) RTC (3) RTOS-RTX (1) RTT (1) RTX-RTOS (1) SDCard (1) SDRAM (6) Segger (2) SPI (3) sqrt (3) SSD1298 (1) SSD1963 (1) Standart Peripherial Library (3) STANDBAY (1) startup (1) STemWin (8) stepper motor (1) STlink (2) STM32 (17) STM32429ZI (1) STM32Cube (1) STM32DBG.IN (1) STM32F (28) STM32F0 (4) STM32F1 (13) STM32F4 (10) STM32F4 Discovery (1) STM32F407ZG (1) STM32F429 (2) STM32F746 (1) STOP (1) string (1) struct (1) SWD (1) SWD JTAG (1) Synhronization (1) system_stm32f4xx.c (1) SystemInit (1) SysTick (1) task (4) telit (1) TIM (27) typedef (1) UART (1) USART (9) viewer (2) WM_PAINT (1) Z-stack (5) ZigBee (5)

суббота, 25 февраля 2012 г.

Assembler STM32F4 GPIO

Некоторые основные  команды ассемблера

Во-первых, загрузка большого числа (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.

Пример кода, моргающего светодиодами:



; 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

; Программная задержка
; R0 - величина задержки
delay       PROC
            ; Вычитаем единицу
            SUBS R0, R0, #1
            ; Пока не обнулилась, крутим дальше
            BNE delay
         
            BX LR
            ENDP

            EXPORT __main
__main      PROC
         
            NOP
         
            ; Разрешим тактирование порта 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]
         
            ; В регистр R1 запишем адрес GPIOD
            LDR R1, =GPIOD_BASE
            ; В регистр R2 запишем величину задержки
            LDR R2, =DELAY_COUNTER
         
__mainloop
            ; Зажжём 12 бит
            MOV R4, #GPIO_BSSR_PIN12 ; 12 бит
            STR R4, [R1, #GPIO_BSRRL_OFFSET]
         
            ; Задержка
            MOV R0, R2
            BL  delay
         
            ; Погасим 12 бит
            STR R4, [R1, #GPIO_BSRRH_OFFSET]
         
            ; Зажжём 13 бит
            MOV R4, #GPIO_BSSR_PIN13 ; 13 бит
            STR R4, [R1, #GPIO_BSRRL_OFFSET]
         
            ; Задержка
            MOV R0, R2
            BL  delay
         
            ; Погасим 13 бит
            STR R4, [R1, #GPIO_BSRRH_OFFSET]
         
            ; Зажжём 14 бит
            MOV R4, #GPIO_BSSR_PIN14 ; 14 бит
            STR R4, [R1, #GPIO_BSRRL_OFFSET]
         
            ; Задержка
            MOV R0, R2
            BL  delay
         
            ; Погасим 14 бит
            STR R4, [R1, #GPIO_BSRRH_OFFSET]
         
            ; Зажжём 15 бит
            MOV R4, #GPIO_BSSR_PIN15 ; 15 бит
            STR R4, [R1, #GPIO_BSRRL_OFFSET]
         
            ; Задержка
            MOV R0, R2
            BL  delay
         
            ; Погасим 15 бит
            STR R4, [R1, #GPIO_BSRRH_OFFSET]
         
            ; На очередной круг
            B __mainloop

            ENDP

    END

1 комментарий:

  1. Если вы используете 32-битные константы, которые используются по нескольку раз, то лучше их сложить после тела функции и грузить в регистры командой LDR. Так объем программы получится меньше. Посмотрите код любой сишной функции в отладчике, там сделано именно так.

    ОтветитьУдалить