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