@.CHARSET CP1251
@GNU AS
@.DESC     type=module
@ ***************************************************************************
@ *                  МОДУЛЬ УПРАВЛЕНИЯ LCD НА ILI9341                       *
@ ***************************************************************************
@ * Процедуры:                                                              *
@ *     LCD_INIT:     Инициализация SPI, LCD (инициализации GPIO нет !)     *
@ *     LCD_FILLREC   Заполнение области цветом                             *
@ *     LCD_CHAR_12x16  Вывод символа (размер 12 на 16 точек)               *
@ *     LCD_CHAR_24x32  Вывод символа (размер 24 на 32 точки)               *
@ *                                                                         *
@ *  ПРИМЕЧАНИЕ !                                                           *
@ *  - допустимые координаты: 0<X<319, 0<Y<239                              *
@ *  - вывод символов LCD_CHAR с точностью до пискела                       *
@ *  - в знакогенераторе нет прописных (маленьких) букв                     *
@ *                                                                         *
@ ***************************************************************************
@.ENDDESC

.SYNTAX unified                     @ синтаксис исходного кода
.THUMB                              @ тип используемых инструкций Thumb
.CPU    cortex-m4                   @ процессор
.FPU    fpv4-sp-d16                 @ сопроцессор

.INCLUDE  "/src/inc/rcc.inc"
.INCLUDE  "/src/inc/gpio.inc"
.INCLUDE  "/src/inc/spi.inc"

@ подключение дисплея

.EQU  pCS                           , 10
.EQU  pCS_GPIO_BASE                 , GPIOB_BASE

.EQU  pDC                           , 12
.EQU  pDC_GPIO_BASE                 , GPIOB_BASE

.EQU  pRST                          , 14
.EQU  pRst_GPIO_BASE                , GPIOB_BASE

@ аппаратный SPI (смотрите файл настройки GPIO для выбранного SPI)
.EQU  LCD_SPI                       , SPI2_BASE

@ --------------------------------------------------------------
@ Команды дисплея ILI9341
@ --------------------------------------------------------------
.EQU LCD_SOFTRESET         ,  0x01   @ Soft reset
.EQU LCD_SLEEP_OUT         ,  0x11   @ Sleep out register
.EQU LCD_GAMMA             ,  0x26   @ Gamma register
.EQU LCD_DISPLAY_OFF       ,  0x28   @ Display off register
.EQU LCD_DISPLAY_ON        ,  0x29   @ Display on register
.EQU LCD_COLUMN_ADDR       ,  0x2A   @ Colomn address register
.EQU LCD_PAGE_ADDR         ,  0x2B   @ Page address register
.EQU LCD_GRAM              ,  0x2C   @ GRAM register
.EQU LCD_MAC               ,  0x36   @ Memory Access Control register
.EQU LCD_PIXEL_FORMAT      ,  0x3A   @ Pixel Format register
.EQU LCD_WDB               ,  0x51   @ Write Brightness Display register
.EQU LCD_WCD               ,  0x53   @ Write Control Display register
.EQU LCD_RGB_INTERFACE     ,  0xB0   @ RGB Interface Signal Control
.EQU LCD_FRC               ,  0xB1   @ Frame Rate Control register
.EQU LCD_BPC               ,  0xB5   @ Blanking Porch Control register
.EQU LCD_DFC               ,  0xB6   @ Display Function Control register
.EQU LCD_POWER1            ,  0xC0   @ Power Control 1 register
.EQU LCD_POWER2            ,  0xC1   @ Power Control 2 register
.EQU LCD_VCOM1             ,  0xC5   @ VCOM Control 1 register
.EQU LCD_VCOM2             ,  0xC7   @ VCOM Control 2 register
.EQU LCD_POWERA            ,  0xCB   @ Power control A register
.EQU LCD_POWERB            ,  0xCF   @ Power control B register
.EQU LCD_PGAMMA            ,  0xE0   @ Positive Gamma Correction register
.EQU LCD_NGAMMA            ,  0xE1   @ Negative Gamma Correction register
.EQU LCD_DTCA              ,  0xE8   @ Driver timing control A
.EQU LCD_DTCB              ,  0xEA   @ Driver timing control B
.EQU LCD_POWER_SEQ         ,  0xED   @ Power on sequence register
.EQU LCD_3GAMMA_EN         ,  0xF2   @ 3 Gamma enable register
.EQU LCD_INTERFACE         ,  0xF6   @ Interface control register
.EQU LCD_PRC               ,  0xF7   @ Pump ratio control register

.SECTION .asmcode
@.DESC     name=LCD_INIT type=proc
@ +--------------------------------------------------------------------+
@ |               Процедура инициализации дисплея ILI9341              |
@ +--------------------------------------------------------------------+
@ | Процедура проводит инициализацию: SPI, и самого дисплея            |
@ | Инициализацию GPIO нужно выполнить до вызова этой процедуры        |
@ +--------------------------------------------------------------------+
@.ENDDESC
.GLOBAL LCD_INIT
LCD_INIT:                LKU:
                         PUSH        { R0, R1, LR }
                    @ включим SPI 2
                         MOV         R0, 1
                         LDR         R1, = ( PERIPH_BB_BASE + ( RCC_BASE + RCC_APB1ENR ) * 32 + RCC_APB1ENR_SPI2EN_N * 4 )
                         STR         R0, [ R1 ]

          @ настройка и включение интерфейса SPI
.EQU  spi_dir       , SPI_CR1_BIDIOE | SPI_CR1_BIDIMODE    @ SPI_Direction_1Line_Tx
.EQU  spi_mode_master, SPI_CR1_MSTR | SPI_CR1_SPE          @ SPI_Mode_Master & SPI_Enable
.EQU  spi_nss       , SPI_CR1_SSM | SPI_CR1_SSI            @ SPI_NSS_Soft
.EQU  spi_brpresc   , SPI_CR1_BR_DIV_2                     @ делитель частоты для SPI

                    @ включаем SPI2 с выбранными выше настройками
                         LDR         R1, = ( PERIPH_BASE + LCD_SPI )
                         LDR         R0, = ( spi_dir | spi_mode_master | spi_nss | spi_brpresc )
                         STR         R0, [ R1, SPI_CR1 ]

          @ начало работы с дисплеем

                    @ укажем дисплею что работаем с ним
                         BL          LCD_CS0

                    @ аппаратный сброс LCD
                         @ установим линию RST=0
                         MOV         R0, 0
                         LDR         R1, = ( PERIPH_BB_BASE + ( pRst_GPIO_BASE + GPIO_ODR ) * 32 + pRST * 4 )
                         STR         R0, [ R1 ]
                     @ пауза для фиксации сигнала сброса устройством
                         MOV         R0, 10
                         BL          SYSTICK_DELAY
                         @ установим линию RST=1
                         MOV         R0, 1
                         STR         R0, [ R1 ]
                     @ пауза после сброса
                         MOV         R0, 50              @ ниже 37 мс будут глюки !!
                         BL          SYSTICK_DELAY

          @ отправка команд инициализации LCD
                         MOV         R0, LCD_POWER1
                         BL          LCD_SENDCOM
                         MOV         R0, 0x23
                         BL          LCD_SENDDATA

                         MOV         R0, LCD_POWER2
                         BL          LCD_SENDCOM
                         MOV         R0, 0x10
                         BL          LCD_SENDDATA

                         MOV         R0, LCD_MAC  @ направление отрисовки
                         BL          LCD_SENDCOM
                         MOV         R0, 0b01000000  @------- MY MX MV ML
                         BL          LCD_SENDDATA

                         MOV         R0, LCD_PIXEL_FORMAT
                         BL          LCD_SENDCOM
                         MOV         R0, 0X55
                         BL          LCD_SENDDATA

                         MOV         R0, LCD_SLEEP_OUT
                         BL          LCD_SENDCOM

                         MOV         R0, LCD_DISPLAY_ON
                         BL          LCD_SENDCOM

                         BL          LCD_CS1

          @ отключимся от дисплея, он уже проинициализирован
                         POP         { R0, R1, PC }

LCD_SENDCOM:  @ отправка команды с предварительным  ожиданием BSY
                         PUSH        { R1, R2, R3, LR }

                    @ ожидание ухода предыдущих данных или команды
                         BL          SPI_WAIT_TXE
                         BL          SPI_WAIT_BSY            @ ожидание окончания прошлой отправки

                    @ переходим в режим команд  DC=0
                         MOV         R2, 0
                         LDR         R3, = ( PERIPH_BB_BASE + ( pDC_GPIO_BASE + GPIO_ODR ) * 32 + pDC * 4 )
                         STR         R2, [ R3 ]

                         STR         R0, [ R1, SPI_DR ]     @ отправка команды
                    @ ожидание ухода команды
                         BL          SPI_WAIT_TXE           @ ждем когда данные уйдут на передачу
                         BL          SPI_WAIT_BSY           @ ждем физического окончания передачи

                    @ переходим в режим данных DC=1
                         MOV         R2, 1
                         STR         R2, [ R3 ]

                         POP         { R1, R2, R3, PC }


LCD_SENDDATA:            @ отправка данных с предварительным ожиданием TXE
                         PUSH        { R1, R2, LR }
                         BL          SPI_WAIT_TXE           @ проверим что прошлый байт ушел на отправку
                         STR         R0, [ R1, SPI_DR ]
                         POP         { R1, R2, PC }


SPI_WAIT_BSY:            @ ожидание конца физической передачи
                         LDR         R2, [ R1, SPI_SR ]
                         TST         R2, SPI_SR_BSY
                         BNE         SPI_WAIT_BSY
                         BX          LR

SPI_WAIT_TXE:            @ ожидание записи данных из spi_dr в shift
                         LDR         R1, = ( PERIPH_BASE + LCD_SPI )
spi_TXE_wait:
                         LDR         R2, [ R1, SPI_SR ]
                         TST         R2, SPI_SR_TXE
                         BEQ         spi_TXE_wait

                         BX          LR

     @ управление линией CS - - - - - - - - - - - - - - - - - - - - - - -
LCD_CS0:
                         PUSH        { R0, R1 }
                         MOV         R0, 0
                         LDR         R1, = ( PERIPH_BB_BASE + ( pCS_GPIO_BASE + GPIO_ODR ) * 32 + pCS * 4 )
                         STR         R0, [ R1 ]
                         POP         { R0, R1 }
                         BX          LR

LCD_CS1:
                         MOV         R0, 1
                         LDR         R1, = ( PERIPH_BB_BASE + ( pCS_GPIO_BASE + GPIO_ODR ) * 32 + pCS * 4 )
                         STR         R0, [ R1 ]
                         BX          LR


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

                    MOV             R0, LCD_PAGE_ADDR    @ задать координаты StartY, EndY
                    BL              LCD_SENDCOM    @ отправка команды

                    UBFX            R0, R2, 24, 8    @ отправка StartY
                    BL              LCD_SENDDATA
                    UBFX            R0, R2, 16, 8    @ отправка StartY
                    BL              LCD_SENDDATA

                    UBFX            R0, R2, 8, 8    @ отправка EndY
                    BL              LCD_SENDDATA
                    UBFX            R0, R2, 0, 8    @ отправка EndY
                    BL              LCD_SENDDATA

                    MOV             R0, LCD_COLUMN_ADDR    @ задать координаты StartX, EndX
                    BL              LCD_SENDCOM    @ отправка команды

                    UBFX            R0, R3, 24, 8    @ отправка StartX
                    BL              LCD_SENDDATA
                    UBFX            R0, R3, 16, 8    @ отправка StartX
                    BL              LCD_SENDDATA

                    UBFX            R0, R3, 8, 8    @ отправка EndX
                    BL              LCD_SENDDATA
                    UBFX            R0, R3, 0, 8    @ отправка EndX
                    BL              LCD_SENDDATA

                    MOV             R0, LCD_GRAM
                    BL              LCD_SENDCOM    @ отправка команды

                    POP             { R0, PC }

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

                         BL          LCD_CS0

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

                         @ Расчитаем размер по Y
                         UBFX        R5, R2, 0, 16
                         UBFX        R6, R2, 16, 16
                         SUB         R5, R5, R6
                         @ Расчитаем размер по X
                         UBFX        R0, R3, 0, 16
                         UBFX        R6, R3, 16, 16
                         SUB         R6, R0, R6
                         ADD         R5, R5, 1
                         ADD         R6, R6, 1
                         MUL         R4, R5, R6    @ Общее количество точек заполнения

LCD_CLEAR_loop:
                         UBFX        R0, R1, 8, 8
                         BL          LCD_SENDDATA
                         UBFX        R0, R1, 0, 8
                         BL          LCD_SENDDATA

                         SUBS        R4, R4, 1
                         BNE         LCD_CLEAR_loop

                         BL          SPI_WAIT_TXE
                         BL          SPI_WAIT_BSY

                         BL          LCD_CS1                 @ завершим сеанс работы с дисплеем

                         POP         { R0, R4, R5, R6, PC }

@.DESC     name=LCD_ILI9341_PIXEL type=proc
@ ***************************************************************************
@ *                       Вывод точки по координатам                        *
@ ***************************************************************************
@ | Вывод точки по координатам
@ | Параметры:                                                              |
@ |    LCD_ILI9341_PIXEL: (R0:Y, R1:X, R2:Color) - native вывод пиксела     |
@ +-------------------------------------------------------------------------+
@.ENDDESC
.GLOBAL LCD_ILI9341_PIXEL
LCD_ILI9341_PIXEL:
                    push {r1, r2, r3, lr}

                    mov r2, r0              @ формируем координаты Y_Start, Y_End
                    add r2, 1
                    orr r2, r2, r0, lsl 16

                    mov r3, r1              @ формируем координаты X_Start, X_End
                    add r3, 1
                    orr r3, r3, r1, lsl 16

                    mov r1, r2

                    BL              LCD_FILLRECT

                    pop {r1, r2, r3, pc}

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

                    BL              LCD_CS0

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

                    MOV             R6, 24
                    MUL             R3, R3, R6
                    ADD             R3, R5, R3    @ в R3 адрес символа


                     @ Меняем X  Y  местами
                   MOV         R6, R0
                    MOV         R0, R1
                    MOV         R1, R6

                     @ Создадим область вывода
                    PUSH            { R2, R3 }

                    mov r6, 223
                    sub r1, r6, r1

@                    mov r6, 313
@                    sub r0, r6, r0

                         @ Упаковываем координату Y
                    PKHBT           R0, R0, R0, LSL 16
                    ADD             R0, R0, 11
                         @ Упаковываем координату X
                    PKHBT           R1, R1, R1, LSL 16
                    ADD             R1, R1, 15
                    MOV             R2, R0
                    MOV             R3, R1
                    BL              LCD_AT
                    POP             { R2, R3 }


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

                    BL              SPI_WAIT_TXE
                    BL              SPI_WAIT_BSY

                    BL              LCD_CS1

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

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

                    BL              LCD_CS0

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

                    MOV             R6, 96
                    MUL             R3, R3, R6
                    ADD             R3, R5, R3    @ в R3 адрес символа


                     @ Меняем X  Y  местами
                     MOV         R6, R0
                     MOV         R0, R1
                     MOV         R1, R6

                    mov r6, 239-32
                    sub r1, r6, r1

                     @ Создадим область вывода
                    PUSH            { R2, R3 }
                         @ Упаковываем координату Y
                    PKHBT           R0, R0, R0, LSL 16
                    ADD             R0, R0, 23
                         @ Упаковываем координату X
                    PKHBT           R1, R1, R1, LSL 16
                    ADD             R1, R1, 31
                    MOV             R2, R0
                    MOV             R3, R1
                    BL              LCD_AT
                    POP             { R2, R3 }

                    @ R1 - 4 байта символа
                    @ R2 - color
                    @ R3 - адрес байта символа для вывода
                    @ R6 - счетчик строк символа для вывода
                    @ R7 - счетчик бит

                    MOV             R6, 24
LCD_line_loop_24x32:
                      @ передача одной строки символа
                    MOV             R7, 32

                    LDR             R1, [ R3 ], 4

LCD_pix_loop_24x32:
                     @ выбираем цвет точки
                    TST             R1, 0x80000000
                    ITE             EQ
                    UBFXEQ          R8, R2, 0, 16
                    UBFXNE          R8, R2, 16, 16
                     @ передаем цвет
                    UBFX            R0, R8, 8, 8
                    BL              LCD_SENDDATA
                    UBFX            R0, R8, 0, 8
                    BL              LCD_SENDDATA
                     @ переходим к следующему биту
                    LSL             R1, R1, 1
                     @ цикл передачи бит (снизу вверх)
                    SUBS            R7, R7, 1
                    BNE             LCD_pix_loop_24x32
                     @ цикл передачи линий (слева на право)
                    SUBS            R6, R6, 1
                    BNE             LCD_line_loop_24x32

                    BL              SPI_WAIT_TXE
                    BL              SPI_WAIT_BSY

                    BL              LCD_CS1

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

.section .rodata

.INCLUDE "/src/devices/ili9341/font12x16.inc"
.INCLUDE "/src/devices/ili9341/font24x32.inc"




