NXP i.MX7D
запуск контроллера экрана LCD
@ Переназначить функцию ножек
MOV32 R0, IOMUXC_BASE
MOV R1, 0 @ Режим ALT0 для LCD
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA00]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA01]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA02]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA03]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA04]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA05]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA06]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA07]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA08]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA09]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA10]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA11]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA12]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA13]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA14]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA15]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA16]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA17]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA18]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA19]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA20]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA21]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA22]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_DATA23]
STR R1, [R0, SW_MUX_CTL_PAD_LCD_CLK] @ CLK
STR R1, [R0, SW_MUX_CTL_PAD_LCD_HSYNC] @ HSYNC
STR R1, [R0, SW_MUX_CTL_PAD_LCD_VSYNC] @ VSYNC
STR R1, [R0, SW_MUX_CTL_PAD_LCD_RESET] @ RESET
STR R1, [R0, SW_MUX_CTL_PAD_LCD_ENABLE] @ ENABLE
MOV R1, 7 @ Режим ALT7 для PWM4_OUT
STR R1, [R0, SW_MUX_CTL_PAD_GPIO1_IO11] @ ШИМ подсветка
MOV32 R2, IOMUXC_LPSR_BASE
MOV R1, 0 @ Режим ALT0 для GPIO1_IO06
STR R1, [R2, SW_MUX_CTL_PAD_GPIO1_IO06] @ LCD_VDD_EN
@ Режим ножек
@ 100k PullUp, Pull enable, Hysteresis enable, Drive Strength ~49 ом
MOV R1, 3<<5 + 1<<4 + 1<<3 + 1<<0
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA00]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA01]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA02]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA03]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA04]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA05]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA06]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA07]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA08]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA09]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA10]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA11]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA12]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA13]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA14]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA15]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA16]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA17]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA18]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA19]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA20]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA21]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA22]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_DATA23]
STR R1, [R0, SW_PAD_CTL_PAD_LCD_CLK] @ CLK
STR R1, [R0, SW_PAD_CTL_PAD_LCD_HSYNC] @ HSYNC
STR R1, [R0, SW_PAD_CTL_PAD_LCD_VSYNC] @ VSYNC
STR R1, [R0, SW_PAD_CTL_PAD_LCD_RESET] @ RESET
STR R1, [R0, SW_PAD_CTL_PAD_LCD_ENABLE] @ ENABLE
STR R1, [R0, SW_PAD_CTL_PAD_GPIO1_IO11] @ ШИМ подсветка
MOV32 R0, IOMUXC_LPSR_BASE
STR R1, [R0, SW_PAD_CTL_PAD_GPIO1_IO06] @ LCD_VDD_EN
@ Раскачаем VIDEO PLL
MOV32 R0, CCM_ANALOG_BASE
@ Выходная частота PLL_VIDEO рассчитывается по формуле
@ 24 * (DIV_SELECT + PLL_VIDEO_NUM / PLL_VIDEO_DENOM)
@ 24 * (27 + 255816000 / 279072000) = 670 МГц
@ Частота PLL должна быть в пределах 650МГц ... 1300МГц
@ И должна быть кратна pixel clock
@ Далее сигнал PLL проходит через неотключаемый делитель на 2
@ И только после этого заходит в модуль LCD
MOV32 R9, 255816000 @ Делимое
STR R9, [R0, PLL_VIDEO_NUM]
MOV32 R9, 279072000 @ Делитель
STR R9, [R0, PLL_VIDEO_DENOM]
MOV32 R9, 1 << 16 + 1 << 13 + 27 @ DIV_SELECT = 27
STR R9, [R0, PLL_VIDEO] @ Стартуем PLL
@ Дождаться стабилизации частоты
1:
LDR R8, [R0, PLL_VIDEO]
TST R8, 1 << PLL_VIDEO_LOCK
BEQ 1b
@ Разрешаем выход тактовых импульсов из PLL
MOV R9, 1 << PLL_VIDEO_BYPASS
STR R9, [R0, PLL_VIDEO_CLR]
@ В модуль поступает 670 / 2 = 335 МГц
@ Определить источник LCDIF_PIXEL_CLK
MOV32 R0, CCM_BASE + TARGET_ROOT70 @ LCDIF_PIXEL_CLK_ROOT
MOV32 R9, 1 << ROOT_ENABLE + 6 << MUX + (5-1) << PRE_PODF + (2-1) << POST_PODF
STR R9, [R0] @ Источник: VIDEO_PLL, 335 МГц / 5 / 2 = 33500 кГц идут в модуль
@ Затактировать модуль eLCDIF
MOV32 R0, CCM_BASE + CCGR75 @ LCDIF clock gate
MOV R9, 0x00003333
STR R9, [R0]
На самом деле, PLL_VIDEO можно и не запускать.
Зачем вам источник лишнего тепла, молотящий на гигагерцовых частотах?
Для ЖК панелек, экстремально точные частоты не требуются.
Возьмите близкую частоту из уже работающих PLL.
@ Настраиваем ШИМ подсветку
@ PWM4 CLOCK ROOT
MOV32 R0, CCM_BASE + TARGET_ROOT109 @ PWM4_CLK_ROOT
MOV32 R1, 1 << ROOT_ENABLE + 2 << MUX + (2-1) << PRE_PODF + (1-1) << POST_PODF
STR R1, [R0] @ Источник: SYS_PLL_DIV4 / 2 = 480 / 4 / 2 = 60МГц тактируют PWM4
@ Затактировать
MOV32 R0, CCM_BASE + CCGR135 @ PWM4 clock gate
MOV R1, 0x00003333
STR R1, [R0]
@ Настроить модуль PWM4
MOV32 R0, PWM4_BASE
@ Длительность импульса
@ Определяет яркость подсветки экрана
MOV R1, 100
STR R1, [R0, PWMSAR]
@ Период ШИМ
@ Определяет частоту мерцания подсветки
MOV32 R1, 1000-2 @ 1МГц / 1000 = 1000гц
STR R1, [R0, PWMPR]
@ Внутренний делитель входящих 60МГц на 60 = 1МГц
MOV32 R1, 1 << PWMCR_CLKSRC + (60-1) << PWMCR_PRESCALER + 1 << PWMCR_EN
STR R1, [R0, PWMCR] @ Ловите ШИМ на ножке!
@ Поднять питание LCD
MOV32 R0, GPIO1_BASE
LDR R1, [R0, GDIR]
BIS R1, 1 << 6 @ LCD_VDD_EN на выход
STR R1, [R0, GDIR]
LDR R1, [R0, DR]
BIS R1, 1 << 6 @ LCD_VDD_EN = 1
STR R1, [R0, DR]
@ Константы настроек экрана
@ Берутся из pdf на матрицу
lcd_xres = 800 @ Точки
lcd_yres = 480 @ Строчки
lcd_left_margin = 46
lcd_right_margin = 210
lcd_upper_margin = 23
lcd_lower_margin = 22
lcd_hsync_len = 20 @ Измеряется в клоках
lcd_vsync_len = 10 @ Измеряется в строках
MOV32 R0, LCDIF1_BASE
@ Активировать LCD1
MOV R5, 1 << CLKGATE + 1 << SFTRST
STR R5, [R0, CTRL_CLR]
@ Базовые настройки
MOV32 R5, 1 << BYPASS_COUNT @ Передавать буфер безконечно
BIS R5, 1 << DOTCLK_MODE @ Режим DOT CLOCK
BIS R5, 3 << LCD_DATABUS_WIDTH @ Шина 24 бита
BIS R5, 3 << LCD_WORD_LENGTH @ Данные 24 бита
BIS R5, 1 << MASTER @ На шине
STR R5, [R0, CTRL]
@ Формат слова в экранном буфере
MOV32 R5, 7 << BYTE_PACKING_FORMAT @ Упаковка пикселей: xRGB
BIS R5, 1 @ Снять RESET с панельки
STR R5, [R0, CTRL1]
@ Размер экранного буфера
MOV32 R5, lcd_yres << V_COUNT @ Счётчик слов
ADD32 R5, lcd_xres @ В экранном буфере
STR R5, [R0, TRANSFER_COUNT]
@ Настройки синхронизации
MOV32 R5, 1 << ENABLE_PRESENT @ Выставлять ENABLE
BIS R5, 1 << ENABLE_POL @ ENABLE активный 1
BIS R5, 1 << DOTCLK_POL @ CLK активный 0
BIS R5, 1 << VSYNC_PERIOD_UNIT @ Считать линиями
BIS R5, 1 << VSYNC_PULSE_WIDTH_UNIT @ Считать линиями
ADD R5, lcd_vsync_len @ Длительность VSYNC
STR R5, [R0, VDCTRL0]
@ Размер кадра по вертикали
MOV32 R5, lcd_upper_margin + lcd_lower_margin + lcd_vsync_len + lcd_yres
STR R5, [R0, VDCTRL1]
@ Параметры строки
MOV32 R5, lcd_left_margin + lcd_right_margin + lcd_hsync_len + lcd_xres
ADD R5, lcd_hsync_len << HSYNC_PULSE_WIDTH @ Длительность HSYNC
STR R5, [R0, VDCTRL2]
@ Дополнительные параметры интерфейса
MOV32 R5, (lcd_left_margin + lcd_hsync_len) << HORIZONTAL_WAIT_CNT
ADD R5, lcd_upper_margin + lcd_vsync_len @ VERTICAL WAIT COUNT
STR R5, [R0, VDCTRL3]
@ Размер активной области по горизонтали
MOV32 R5, lcd_xres + 1 << SYNC_SIGNALS_ON
STR R5, [R0, VDCTRL4]
@ Адреса экранных буферов
MOV32 R5, Адрес_экранного_буфера
STR R5, [R0, CUR_BUF] @ Текущий
MOV32 R5, Адрес_экранного_буфера
STR R5, [R0, NEXT_BUF] @ Следующий
@ Запускаем контроллер
MOV R5, 1
STR R5, [R0, CTRL_SET] @ Картинка пошла
@ ===========================
@ Экран уже светится и выдаёт картинку!
@ ===========================
@ Очистим экранный буфер
MOV32 R5, Адрес_экранного_буфера
MOV32 R6, 0x00000000 @ Заполнить нулями
MOV32 R7, 800*480 @ Весь экран
1:
STR R6, [R5], 4
DEC R7
BNE 1b
@ Нарисуем белые точки
@ По углам экрана
Адрес_точки_1 = Адрес_экранного_буфера @ Верхний левый
Адрес_точки_2 = Адрес_экранного_буфера+800*4-4 @ Верхний правый
Адрес_точки_3 = Адрес_экранного_буфера+800*4*479 @ Нижний левый
Адрес_точки_4 = Адрес_экранного_буфера+800*4*480-4 @ Нижний правый
@ Должны отображаться однопиксельные белые точки
@ Значит контроллер настроен правильно
MOV32 R6, 0x00FFFFFF
MOV32 R0, Адрес_точки_1
STR R6, [R0]
MOV32 R0, Адрес_точки_2
STR R6, [R0]
MOV32 R0, Адрес_точки_3
STR R6, [R0]
MOV32 R0, Адрес_точки_4
STR R6, [R0]
@ Создадим экранный буфер
@ В некешируемой области
.section .noncache
.align 3, 0 @ Выровнять по 8 байт
Адрес_экранного_буфера:
.skip 800*480*4
.text 0