@.CHARSET  CP1251

@ GNU AS
.syntax unified     @ синтаксис исходного кода
.thumb              @ тип используемых инструкций Thumb
.cpu cortex-m3      @ процессор


.INCLUDE       "/src/inc/rcc.inc"
.INCLUDE       "/src/inc/gpio.inc"
.INCLUDE       "/src/devices/st7789/lcd_st7789.inc"    @ определения дисплея

@.DESC     name=ST7789_Init type=proc
@ +---------------------------------------------------------------------------+
@ |          Процедура инициализации дисплея на контроллере ST7789            |
@ +---------------------------------------------------------------------------+
@ | Модуль может использовать процедуры программного или аппаратного SPI      |
@ |                                                                           |
@ | Выбор используемого SPI осуществляется следующими настройками:            |
.EQU  spi_init      , SPI_TX_INIT    @ имя подпрограммы инициализации SPI     |
.EQU  spi_send      , SPI_TX_SEND    @ имя подпрограммы отправки данных       |
@ |                                    @ (отправляемые данные в R4)           |
.EQU  spi_wait      , SPI_TXE_BSY_WAIT    @ процедура ожидание конца передачи |
@ |                                                                           |
@ | подключение управляющих линий дисплея (RST, DC, CS) должно быть выполнено |
@ | к одному GPIO:                                                            |
.EQU  lcd_gpio_base , GPIOA_BASE    @ базовый (xxx_BASE) адрес gpio           |
.EQU  gpio_enr_bit  , RCC_APB2ENR_IOPAEN_N    @ номер бита в RCC_APB2ENR для  |
@ |                                        включения выбранного spi_gpio_base |
@ |                                                                           |
@ | подключение управляющих линий дисплея (RST, DC, CS) должно быть в рамках  |
@ | одного диапазона пинов GPIO: 0..7 или 8..15, иначе будет получена ошибка  |
@ | компиляции или будет некорректная работа модуля                           |
.EQU  rst_pin       , 8          @ номер пина для RST сигнала дисплея         |
.EQU  dc_pin        , 9          @ номер пина для DC сигнала дисплея          |
.EQU  cs_pin        , 10         @ номер пина для CS сигнала дисплея          |
@ |                                                                           |
@ | Вызовы модуля:                                                            |
@ | - ST7789_INIT - Инициализация периферии микроконтроллера и дисплея        |
@ |            При вызове значения регистров микроконтроллера не сохраняются. |
@ |            Вызов не имеет параметров.                                     |
@ +---------------------------------------------------------------------------+
@.ENDDESC

@ блок расчета значений для настроек GPIO
.IF            rst_pin > 7
.EQU  lcd_rst_pin   , rst_pin - 8
.EQU  lcd_dc_pin    , dc_pin - 8
.EQU  lcd_cs_pin    , cs_pin - 8
.EQU  gpio_crx      , GPIO_CRH    @ имя регистра для настройки пинов: GPIO_CRH для пинов 8..15
.ELSE
.EQU  lcd_rst_pin   , rst_pin
.EQU  lcd_dc_pin    , dc_pin
.EQU  lcd_cs_pin    , cs_pin
.EQU  gpio_crx      , GPIO_CRL    @ имя регистра для настройки пинов: GPIO_CRL для пинов 0..7
.ENDIF


.SECTION    .asmcode

register_prepare:   @ установка значений регистров для упрвавления линиями дисплея
                    @ значения для bitband операций
                    MOV             R1, 1
                    MOV             R0, 0

                    @ включим gpio используемый для линий управления lcd
                    LDR             R2, = PERIPH_BASE + PERIPH_BB_BASE + ( lcd_gpio_base + GPIO_ODR ) * 32
                    BX              LR

.GLOBAL        ST7789_Init
ST7789_Init:        @ настройка интерфейса передачи данных (spi)
                    PUSH            { LR }

                    BL              spi_init    @ настройка SPI интерфейса

                    @ значения для bitband операций
                    MOV             R1, 1
                    MOV             R0, 0

                    @ включим gpio используемый для линий управления lcd
                    LDR             R2, = PERIPH_BASE + PERIPH_BB_BASE + ( RCC_BASE + RCC_APB2ENR ) * 32
                    STR             R1, [ R2, gpio_enr_bit * 4 ]

                    @ настройка режима работы пинов GPIO
                    LDR             R2, = PERIPH_BASE + lcd_gpio_base
                    LDR             R4, [ R2, gpio_crx ]
                    MOV             R3, GPIO_MODE_OUT50
                    BFI             R4, R3, lcd_rst_pin * 4, 4
                    BFI             R4, R3, lcd_dc_pin * 4, 4
                    BFI             R4, R3, lcd_cs_pin * 4, 4
                    STR             R4, [ R2, gpio_crx ]

                    @ перевод пинов в первоначальное положение
                    LDR             R2, = PERIPH_BASE + PERIPH_BB_BASE + ( lcd_gpio_base + GPIO_ODR ) * 32
                    STR             R1, [ R2, rst_pin * 4 ]   @ RST=1
                    STR             R1, [ R2, cs_pin * 4 ]    @ CS=1  дисплей не выбран
                    STR             R0, [ R2, dc_pin * 4 ]    @ DC=0  режим "команды"

                    @ аппаратный сброс дисплея прижатием RST к нулю
                    @ аппаратному сбросу не важно в каком состоянии линия CS
                    STR             R0, [ R2, rst_pin * 4 ]    @ RST=0
                    MOV             R0, 1
                    BL              SYSTICK_DELAY
                    STR             R1, [ R2, rst_pin * 4 ]    @ RST=1
                    MOV             R0, 10
                    BL              SYSTICK_DELAY

                    @ отправляем последовательность команд для настройки дисплея
                    @ R0 = 0
                    @ R1 = 1
                    @ R2 = PERIPH_BASE + PERIPH_BB_BASE + ( lcd_gpio_base + GPIO_ODR ) * 32

                    @ выбираем дисплей перед обменом, так как состояние линии CS дисплея обновляется
                    @ только при изменении ее состояния, и после сброса дисплей не помнит (!!) в каком
                    @ состоянии находится линия CS
                    STR             R0, [ R2, cs_pin * 4 ]    @ CS=0  "выбрать" дисплей

                    MOV             R4, ST7789_Cmd_SLPOUT    @ 0x11 разбудим дисплей после сброса
                    BL              LCD_SENDCOM
                    MOV             R0, 120
                    BL              SYSTICK_DELAY

                    MOV             R4, 0x36                 @ 0x36 поворот экрана на 90 градусов
                    BL              LCD_SENDCOM              @ в горизонтальную ориентацию
                    MOV             R4, 0b011<<5             @ 0 < X < 319 , 0 < Y < 239
                    BL              spi_send                 @ начало координат в левом верхнем углу

                    MOV             R4, ST7789_Cmd_COLMOD    @ 0x3A режим цвета 16 бит
                    BL              LCD_SENDCOM
                    MOV             R4, ST7789_ColorMode_65K | ST7789_ColorMode_16bit
                    BL              spi_send

                    MOV             R4, ST7789_Cmd_INVON     @ режим инверсии
                    BL              LCD_SENDCOM

                    MOV             R4, ST7789_Cmd_DISPON    @ 0x29 включить отображение
                    BL              LCD_SENDCOM

                    @ здесь наш дисплей проинициализирован
                    STR             R1, [ R2, cs_pin * 4 ]    @ CS=1 - сообщаем дисплею что обмен с ним завершен

                    POP             { PC }


@.DESC     name=LCD_SENDCOM type=proc
@ ***************************************************************************
@ *                  Отправка на дисплей байта команды                      *
@ ***************************************************************************
@ | Параметры:                                                              |
@ |  R4 - байт для отправки                                                 |
@ +-------------------------------------------------------------------------+
@.ENDDESC
LCD_SENDCOM:        @ отправка команды с предварительным  ожиданием BSY
                    PUSH            { LR }
                    @ R0 = 0
                    @ R1 = 1
                    @ R2 = PERIPH_BASE + PERIPH_BB_BASE + ( lcd_gpio_base + GPIO_ODR ) * 32

                    BL              spi_wait                  @ ожидание окончания прошлой отправки

                    STR             R0, [ R2, dc_pin * 4 ]    @ DC=0  режим "команды"

                    BL              spi_send                  @ отправляем команду дисплею

                    BL              spi_wait                  @ ожидание окончания прошлой отправки

                    STR             R1, [ R2, dc_pin * 4 ]    @ DC=1  режим "данные"

                    POP             { PC }


@.DESC     name=LCD_AT type=proc
@ ***************************************************************************
@ *                  Задание прямоугольной области экрана                   *
@ ***************************************************************************
@ | Задание области экрана для операций                                     |
@ | Параметры:                                                              |
@ |  R5 - координаты X: 31:16 - SX, 15:0 - EX                               |
@ |  R6 - координаты Y: 31:16 - SY, 15:0 - EY                               |
@ +-------------------------------------------------------------------------+
@.ENDDESC
LCD_AT:             @ определение прямоугольной области экрана
                    PUSH            { R4, LR }

                    MOV             R4, ST7789_Cmd_CASET    @ задать координаты StartX, EndX     столбец
                    BL              LCD_SENDCOM      @ отправка команды
                    UBFX            R4, R5, 24, 8    @ отправка StartX
                    BL              spi_send
                    UBFX            R4, R5, 16, 8    @ отправка StartX
                    BL              spi_send
                    UBFX            R4, R5, 8, 8     @ отправка EndX
                    BL              spi_send
                    UBFX            R4, R5, 0, 8     @ отправка EndX
                    BL              spi_send

                    MOV             R4, ST7789_Cmd_RASET    @ задать координаты StartY, EndY      строка
                    BL              LCD_SENDCOM      @ отправка команды
                    UBFX            R4, R6, 24, 8    @ отправка StartY
                    BL              spi_send
                    UBFX            R4, R6, 16, 8    @ отправка StartY
                    BL              spi_send
                    UBFX            R4, R6, 8, 8     @ отправка EndY
                    BL              spi_send
                    UBFX            R4, R6, 0, 8     @ отправка EndY
                    BL              spi_send

                    MOV             R4, ST7789_Cmd_RAMWR    @ команда заполнения области
                    BL              LCD_SENDCOM

                    POP             { R4, PC }


@.DESC     name=ST7789_FILLRECT type=proc
@ ***************************************************************************
@ *                  Заливка прямоугольной области экрана                   *
@ ***************************************************************************
@ | Очистка области экрана и заливка цветом                                 |
@ | Параметры:                                                              |
@ |  R3 - цвет заливки                                                      |
@ |  R5 - координаты X: 31:16 - START_X, 15:0 - END_X                       |
@ |  R6 - координаты Y: 31:16 - START_Y, 15:0 - END_Y                       |
@ +-------------------------------------------------------------------------+
@.ENDDESC
.GLOBAL ST7789_FILLRECT
ST7789_FILLRECT:
                         PUSH        { R0, R1, R2, R4, R5, R6, R7, R8, LR }

                         BL          register_prepare

                         STR         R0, [ R2, cs_pin * 4 ]    @ CS=0  "выбрать" дисплей

                         BL          LCD_AT        @ установим рабочую область на весь экран

                         @ Расчитаем размер по Y
                         UBFX        R7, R6, 0, 16
                         UBFX        R8, R6, 16, 16
                         SUB         R7, R7, R8

                         @ Расчитаем размер по X
                         UBFX        R8, R5, 0, 16
                         UBFX        R6, R5, 16, 16
                         SUB         R8, R8, R6

                         ADD         R5, R7, 1
                         ADD         R6, R8, 1
                         MUL         R5, R5, R6    @ Общее количество точек заполнения

LCD_CLEAR_loop:
                         UBFX        R4, R3, 8, 8
                         BL          spi_send
                         UBFX        R4, R3, 0, 8
                         BL          spi_send

                         SUBS            R5, R5, 1
                         BNE         LCD_CLEAR_loop

                         BL          spi_wait

                         STR         R1, [ R2, cs_pin * 4 ]    @ CS=1  отменить "выбор" дисплея

                         POP         { R0, R1, R2, R4, R5, R6, R7, R8, PC }


@.DESC     name=ST7789_PIXEL type=proc
@ +-------------------------------------------------------------------------+
@ |              Рисование пиксела с заданными координатами                 |
@ +-------------------------------------------------------------------------+
@ |                                                                         |
@ | Процедура рисует пиксел с координатами                                  |
@ | входные параметры:                                                      |
@ |    R0 - координата Y [0..239],                                          |
@ |    R1 - координата X [0..319],                                          |
@ |    R2 - цвет рисования                                                  |
@ | выходные параметры: нет                                                 |
@ } изменяемые регистры по выходу: нет                                      |
@ +-------------------------------------------------------------------------+
@.ENDDESC
.GLOBAL ST7789_PIXEL
ST7789_PIXEL:
                    PUSH            { R0, R1, R2, R3, R5, R6, LR }

                    MOV             R3, R2

                    ADD             R6, R0, R0, LSL 16
                    ADD             R5, R1, R1, LSL 16

                    BL              register_prepare
                    STR             R0, [ R2, cs_pin * 4 ]    @ CS=0  "выбрать" дисплей

                    BL              LCD_AT

                     @ передаем цвет
                    UBFX            R4, R3, 8, 8
                    BL              spi_send
                    UBFX            R4, R3, 0, 8
                    BL              spi_send

                    BL              spi_wait

                    STR             R1, [ R2, cs_pin * 4 ]    @ CS=1  отменить "выбор" дисплея

                    POP             { R0, R1, R2, R3, R5, R6, PC }


@.DESC     name=ST7789_CHAR_12x16 type=proc
@ ***************************************************************************
@ *                       Вывод символа по координатам                      *
@ ***************************************************************************
@ | Очистка области экрана и заливка цветом                                 |
@ | Параметры:                                                              |
@ |    LCD_CHAR: (R0:Y, R1:X, R2:Color, R3:Char) - native вывод символа     |
@ +-------------------------------------------------------------------------+
@.ENDDESC

.GLOBAL ST7789_CHAR_12x16
ST7789_CHAR_12x16:
                    PUSH            { R0 - R9, LR }
                    @ Создадим область вывода символа
                    @ Упаковываем координату Y
                    ADD             R6, R0, R0, LSL 16
                    ADD             R6, R6, FONT_CHAR_HEIGHT-1
                    @ Упаковываем координату X
                    ADD             R5, R1, R1, LSL 16
                    ADD             R5, R5, FONT_CHAR_WIDTH-1

                     @ рассчитаем адрес символа
                    LDR             R4, = LCD_CHARS_16x25
                    SUB             R3, R3, 32

                    MOV             R7, 50                 @ размер массива символа в байтах !!!
                    MUL             R3, R3, R7
                    ADD             R8, R4, R3             @ в R8 адрес символа

                    MOV             R3, R2                 @ цвет символа

                    @ задаем окно вывода для символа
                    BL              register_prepare
                    STR             R0, [ R2, cs_pin * 4 ]    @ CS=0  "выбрать" дисплей
                    BL              LCD_AT

                    @ R1 - 2 байта символа
                    @ R3 - адрес байта символа для вывода
                    @ R6 - счетчик строк символа для вывода
                    @ R7 - счетчик бит
                    MOV             R6, FONT_CHAR_HEIGHT
LCD_line_loop_12x16:
                      @ передача одной строки символа
                    MOV             R7, FONT_CHAR_WIDTH
                    LDRSH           R9, [ R8 ], 2
LCD_pix_loop_12x16:
                     @ выбираем цвет точки
                    TST             R9, 0x8000
                    ITE             EQ
                    UBFXEQ          R5, R3, 0, 16
                    UBFXNE          R5, R3, 16, 16
                     @ передаем цвет
                    UBFX            R4, R5, 8, 8
                    BL              spi_send
                    UBFX            R4, R5, 0, 8
                    BL              spi_send
                     @ переходим к следующему биту
                    LSL             R9, R9, 1             @ куда сдвигать битовое поле строки символа !
                     @ цикл передачи бит (снизу вверх)
                    SUBS            R7, R7, 1
                    BNE             LCD_pix_loop_12x16
                     @ цикл передачи линий (слева на право)
                    SUBS            R6, R6, 1
                    BNE             LCD_line_loop_12x16

                    BL              spi_wait

                    STR             R1, [ R2, cs_pin * 4 ]    @ CS=1  отменить "выбор" дисплея

                    POP             { R0 - R9, PC }

.INCLUDE "/src/devices/ST7789/font16x25.inc"
