Notice: Memcache::get(): Server localhost (tcp 11211) failed with: Connection refused (111) in /home/a146/www/we.easyelectronics.ru/engine/lib/external/DklabCache/Zend/Cache/Backend/Memcached.php on line 134
Подключение LCD индикатора к ПЛИС / ПЛИС / Сообщество EasyElectronics.ru

Подключение LCD индикатора к ПЛИС


В этой статье я расскажу о подключении цветного графического LCD индикатора к ПЛИС, установленной на отладочной плате DE0-nano.


Про многострадальный LCD индикатор от телевизора я писал ранее. Это цветной индикатор с разрешением 800x480 и параллельной 18 битной шиной. Так как в той статье использовался достаточно слабый микроконтроллер с малым объемом памяти, то на индикатор удавалось выводить только черно-белый текст. С появлением отладочной платы DE0-nano захотелось подключить индикатор к ПЛИС, а так как на плате установлена SDRAM, то вполне реально было получить цветное изображение.
Никаких приличных примеров подключения графических LCD к ПЛИС c использованием SDRAM на русском мне найти не удалось. А вот на английском нашелся очень полезный сайт sites.google.com/site/fpgaandco/, на котором приводятся примеры работы именно с DE0-nano. В том числе там имеется пример вывода данных на индикатор от PSP. Однако при повторении этого примера возникли определенные трудности, которые пришлось преодолевать.

В проекте на вышеуказанном сайте для работы с SDRAM и вывода изображения на экран используется софтовый процессор Nios II. Данные из SDRAM считываются при помощи DMA, после чего передаются на индикатор. Все компоненты системы — стандартные (встроены в Quartus).

Теперь подробнее о том, как это реализовать. Для начала нужно создать пустой проект в Quartus, сконфигурировать его под конкретную ПЛИС и создать систему SOPC. Более подробно это описано здесь, а также в руководстве к DE0-nano.

В списке «Clock Settings» переименовываем clk_0 to clk_100 и устанавливаем его частоту 100 МГц.
Далее в систему SOPC нужно добавить:
  • NiosII processor (Procesors>Nios II Processor). Это программный процессор.
    В окне настроек выбрать NiosII/E, остальные настройки оставить без изменений. В списке компонентов переименовать процессор в cpu
  • JTAG-UART (Interface Protocols>Serial>JTAG UART). Компонент, предназначенный для передачи данных из Nios в IDE (аналог semihosting в ARM). Полезен для отладки.
    Все настройки оставить без изменений. Переименовать в jtag_uart.
  • On-chip ram (Memories and Memory Controllers>On-Chip>On-Chip Memory RAM or ROM). Это ОЗУ процессора. Также здесь хранится сама программа процессора. В настройках памяти нужно установить размер ОЗУ — 30000 байт. Компонент переименовать в onchip_memory2.
  • PIO (Peripherals>Microcontroller Peripherals>PIO (Parallel IO)). Порт ввода-вывода.
    Оставить все настройки без изменений. Таким образом получим 8-битный порт, работающий только на вывод. Компонент переименовать в pio.
  • System ID Peripheral (Peripherals/Debug and Performance/System ID Peripheral). По сути это порт-константа, которой при каждой сборке SOPC присваивается значение, указанное пользователем и временная метка. Компонент предназначен для защиты от программирования неправильно сконфигурированной SOPC системы. В настройках желательно ввести свое значение (произвольное). Компонент переименовать в sysid.
  • SDRAM controller (Memories and Memory Controllers/SDRAM/SDRAM Controller). Контроллер SDRAM памяти. В данном случае в SDRAM будет размещаться видеобуфер.
    Мои настройки SDRAM:
    Presets: Custom
    Data width = 16 bits
    Chip select = 1; Banks = 4
    Address width:
    Row = 13; Column = 9
    На вкладке Timing:
    CAS latency cycles = 2
    Initialization refresh cycles = 8
    Issue one refresh command every = 7.8125 us
    Delay after powerup = 200us
    t_rfc = 70ns
    t_rp = 20ns
    t_rcd = 20ns
    t_ac = 5.5ns
    t_wr = 14 ns
    Возможно, это не самые оптимальные настройки, нужно экспериментировать.
    Компонент переименовать в sdram.
  • Scatter-Gather DMA Controller (Bridges and Adapters>DMA>Scatter-Gather DMA Controller). Это DMA, но не обычный. Он может последовательно выполнить несколько предварительно указанных пересылок. Так как максимальное число передаваемых данных для DMA составляет 65536 байт, а передавать нужно значительно больше, то такой DMA очень полезен. Данные DMA будет забирать напрямую из SDRAM, но при этом он должен быть связан с ОЗУ, так как дескрипторы хранятся именно там. В настройках нужно указать: Transfer mode = Memory To Stream. Компонент переименовать в sgdma.
  • Avalon-ST Dual Clock FIFO (Memories and Memory Controllers>On-Chip>Avalon-ST Dual Clock FIFO). Это FIFO с двумя входами тактовой частоты. Так как частота процессора и видеосистемы различается, то для передачи данных в видеосистему нужен FIFO. Кроме того, он задерживает данные на время передачи синхроимпульсов.
    Настройки:
    Symbols per beat = 4
    FIFO depth = 512
    Установить флаг «Use packets»
    Компонент переименовать в dc_fifo.
  • Pixel Converter (Peripherals>Display>Pixel Converter (BGR0->BGR)). Нужен для конвертации 32-битного потока данных в 24-битный. Установить Source symbols per beat = 1
    Компонент переименовать в pixel_converter.
  • Video Sync Generator (Peripherals>Display>Video Sync Generator). Компонент, формирующий видеосигнал для LCD. В частности, он формирует сигналы горизонтальной и вертикальной синхронизации, и управляет идущим на него потоком данных, приостанавливая его во время передачи синхроимпульсов.
    Мои настройки для компонента:
    Data Stream Bit Width = 24. Ширина шины данных.
    Beats per Pixel = 1. Это значит, что все данные о цвете передаются за 1 такт (полностью параллельная шина).
    Number of Columns = 800. Число пикселей по горизонтали.
    Number of Rows = 480. Число пикселей по вертикали.
    Horizontal Blank Pixels = 216. Время в тактах, в течении которого данные не передаются. Синхроимпульс входит в это время. Этот участок идет перед передачей данных.
    Horizontal Front Porch Pixels = 10. Время в тактах, в течении которого данные не передаются. Этот промежуток идет после передачи строки.
    Horizontal Sync Pulse Pixels = 16. Длительность горизонтального синхроимпульса в тактах.
    Horizontal Sync Pulse Polarity = 0. Активный уровень — низкий.
    Vertical Blank Lines = 39. Время в строках, в течении которого данные не передаются. Синхроимпульс входит в это время. Этот участок идет перед передачей кадра.
    Vertical Front Porch Lines = 6. Время в строках, в течении которого данные не передаются. Этот промежуток идет после передачи кадра.
    Vertical Sync Pulse Lines = 6. Длительность вертикального синхроимпульса в строках.
    Vertical Sync Pulse Polarity = 0. Активный уровень — низкий.
    Total Horizontal Scan Pixels = 1026 (Number of Columns + Horizontal Blank Pixel + Horizontal Front Porch Pixels)
    Total Vertical Scan Lines = 525 (Number of Rows + Vertical Blank Lines + Vertical Front Porch Lines)
    Все данные взяты из документации на контроллер индикатора.
  • Компонент переименовать в video_sync_generator.
  • Avalon ALTPLL ( PLL>Avalon ALTPLL ). Формирует нужные рабочие частоты для системы. В данном случае нужно создать частоту 100 МГц с фазовым сдвигом для тактирования SDRAM и частоту 20 МГц для тактирования видеосистемы.
    Настройки:
    general/modes:
    Установить частоту inclk0 = 100 Mhz (входная частота PLL).
    Inputs/Lock:
    Убрать «create an 'areset' input ...» и «Create 'locked' output»
    Output Clocks:
    clk c0:
    Установить 'use this clock'
    Установить clock phase shift = -3ns
    clk c1:
    Установить Clock division factor = 5 (100 / 5 = 20)
    Компонент переименовать в video_sync_generator.


После того, как все компоненты добавлены в систему, нужно убедится, что список «Clock Settings» выглядит так:

Затем нужно соединить все компоненты, как показано ниже:

Важно обратить внимание на поле Clock. Видно, что на компоненты dc_fifo, pixel_converter, video_sync_generator подается тактовая частота с выхода PLL c1 (20 МГц).

Далее нужно найти в настройки процессора, и установить reset vector = onchip_memory2, offset = 0x0 и exception vector = onchip_memory2, offset = 0x20
Затем нужно выполнить команды System > Assign Base Addresses и File>Refresh System, после чего должны исчезнуть все сообщения об ошибках внизу окна.
После этого кнопкой «Generate» нужно запустить сборку системы, перед этим сохранив ее.

После окончания сборки закрываем окно SOPC Builder. В Quartus создаем новый Block Diagram/Schematic File (BDF) и сохраняем его под каким-нибудь именем (у меня test2.bdf)
В окне «Project Navigator» во вкладке «Files» находим test2.bdf и через контекстное меню вызываем команду «Set as Top-Level Entity».
На самом поле схемы через контекстное меню вызываем команду Insert > Insert Symbol, и в разделе Project находим созданную систему SOPC и добавляем ее на схему.
Дальше похожим образом добавляем еще один модуль PLL. Опять вызываем команду Insert > Insert Symbol, нажимаем кнопку MegaWizard Plug-In Manager >> Next >> Выбираем пункт I/O > ALTPLL, указываем имя нового файла и используемый язык (у меня Verilog), указываем входную частоту 50 МГц, выходную частоту c0 — 100 МГц (clock multiplication factor = 2) и нажимаем Finish, после чего добавляем PLL в схему. Более подробно добавление PLL описано здесь. Это описание также может относиться к добавлению PLL в SOPC. Созданный PLL можно использовать для формирования других тактовых частот, не используемых в SOPC.
После этого можно соединить выход PLL со входом тактовой частоты SOPC, и соединить выводы SOPC с внешними выводами ПЛИС.
В результате должна получиться такая схема:

На ней clk_50 — соединен с кварцевым генератором платы, reset_n — с одной из кнопок на плате, altpll_0_c0_out — соединен с тактовым входом SDRAM (100 МГц с фазовым сдвигом), altpll_0_c1_out — тактовый сигнал для LCD (20 МГц),
out_port_from_the_pio[7..0] — соединен со светодиодами на плате, все выводы, начинающиеся на «zs» — соединены с SDRAM, HD_from_the_video_sync_generator_0 — выход кадровой синхронизации, VD_from_the_video_sync_generator_0 — выход строчной синхронизации, video_wire[23..0] — 24-битная шина видеоданных. Как можно было заметить, компонент video_sync_generator поддерживает только такую ширину шины (в памяти один пиксель при этом занимает 32 бита, а 8 старших бит не используются). Я не стал подключать к индикатору все 18 линий данных, и ограничился лишь 8 из них. Поэтому ниже SOPC шина video_wire разделяется на три порта (3-3-2 бит). Оставшиеся выводы индикаторы нужно соединить с землей.
Для того, чтобы автоматически создать порты для SOPC, нужно выделить его на схеме, и из контекстного меню вызвать команду Generate Pins For Symbol Ports.

После того, как схема нарисована, нужно запустить компиляцию, после чего каждому выводу каждого порта можно назначить физическую ножку ПЛИС (Assignments > Pin Planner). Таблицы, в которых указаны названия нужных физических выводов, также можно найти на сайте sites.google.com/site/fpgaandco/. После того, как все выводы назначены, проект нужно скомпилировать еще раз, после чего он готов к загрузке в ПЛИС. Проект занимает немногим больше 4000 LE.

После этого нужно написать и скомпилировать программу для Nios II. Я делал это в программе Nios II Software Build Tools for Eclipse. Так как Eclipse использует абсолютные пути, то просто скопировать программу из другого workspace не удастся. Каждый раз нужно создавать новый проект. Как это сделать, также хорошо расписано здесь. Отмечу, что при конфигурировании BSP в настройках Linker нужно для всех пунктов указать тип памяти — onchip_memory2.

Код самой программы здесь приводить не буду (он есть в папке software). Большая часть кода взята с «английского» сайта. Расскажу лишь о принципе работы — тут все достаточно просто.
В системе используется только один экранный буфер.
После включения Nios очищает экран (прямой записью в экранный буфер), инициализирует и запускает DMA. После передачи всех данных из экранного буфера DMA генерирует прерывание, в котором DMA запускается вновь. Таким образом, при непрерывной работе DMA имеем постоянный поток видеоданных.
В это же время программа в бесконечном цикле генерирует случайный цвет и координаты, и отрисовывает цветной кружок. В результате экран быстро заполняется кружками.
А вот инициализацию DMA нужно описать подробнее. Так как DMA последовательно выполняет несколько передач, по его нельзя сконфигурировать через регистры. Поэтому для конфигурации в программе создаются дескрипторы — область в ОЗУ, в которую записывается конфигурация DMA. При запуске DMA указатель на дескрипторы записывается в один из регистров DMA.
Код конфигурации дескрипторов выглядит так:


alt_u8* buff = (alt_u8*)frameBufferA; // frame buffer A
int i;
for(i = 0; i < 24; ++i) 
{
    alt_u16 size = (i<23)?0xfffc:0x705c;
    alt_avalon_sgdma_construct_mem_to_stream_desc(
    &dmaDescA[i],
    (i<23) ? (&dmaDescA[i+1]) : &dmaDescEND,
    (alt_u32*)buff,
    size, 0, i==0, i==23, 0);
    buff+= size;
}

Подробнее о «волшебных» числах. Суммарно DMA должен передать 800*480*4 = 1536000 байт. При этом величина одной передачи не должна превышать 65536 байт. Поэтому для вывода всего изображения нужно 1536000/65536 = 23.44 > 24 передачи и 24 дескриптора. Так как число не целое, то размер последней передачи будет отличаться от остальных. Размер первых 23 передач установлен 0xfffc = 65532 байт. Размер оставшейся передачи 1536000 — 65532*23 = 28764 (0x705c).
Также в качестве параметров указывается, начальный и конечный адрес в памяти, является ли дескриптор начальным или конечным.

Внешний вид конструкции сзади:


Видео работы конструкции:


Отрисовка кружков специально замедлена. Если задержку убрать, то экран полностью заполняется кружками меньше чем за секунду.

А теперь о проблемах, возникших в процессе создания системы.
  • Версия Quartus. Я остановился на Quartus 11.0 sp1. Десятый Quartus с диска, прилагавшегося к DE0-nano, работал нормально, но Eclipse, прилагавшийся к нему, часто намертво зависал при попытке записать программу для Nios.
    С Quartus 13 все оказалось еще хуже. Qsys и весь проект собирались, Eclipse нормально записывал программу в Nios, но видеосигналы были явно искажены. После длительных экспериментов я обнаружил, что некоторые компоненты в Qsys используются с дефолтными настройками. Побороть это не удалось, и пришлось перейти на Quartus 11.
  • SDRAM не виделась Nios из-за того, что в SOPC Builder на ее контроллер (SDRAM Controller) был подан тактовый сигал с выхода altpll_0_c0 PLL. Сигнал нужно подавать с clk_100. Вход тактирования самой микросхемы SDRAM должен быть соединен с altpll_0_c0.
  • В Eclipse при попытке посмотреть System ID текущей системы (через Nios II->Flash Programmer) он не отображался. Как оказалось, такое происходит, если не указать расположение используемого *.sopcinfo файла.
  • Размер ОЗУ. Тут стоит отметить, что память в используемой ПЛИС разбита на блоки M9K по 8192 бит. Два различных компонента не могут использовать один блок. Это приводит к тому, что даже если какой-нибудь компонент использует десяток байт памяти, то для него будет выделен целый блок памяти. Блоков M9K в используемой ПЛИС не так уж и много — 66, и при большом количестве компонентов, использующих память они очень быстро заканчиваются.
  • Так как памяти под ОЗУ выделено мало (30000 байт), а программа записывается именно туда, то программа может перестать влезать в ОЗУ. В таком случае нужно оптимизировать настройки компилятора. В интернете нашелся перевод, в котором показано, что можно оптимизировать: ссылка. Отмечу, что настройки оптимизации самой программы в Eclipse вызываются так: Project > Properties > Nios II Application Properties.
  • Так как видеоданные берутся из SDRAM, которая имеет достаточно сложный протокол работы, то при попытке записывать данные из Nios со слишком большой скоростью в SDRAM отображение изображения нарушается — изображение начинает дрожать, появляются артефакты. Такие проблемы возникают, если использовать оптимизацию программы — O3. Если использовать оптимизацию Os, то все нормально. Возможно, что если переписать компонент video_sync_generator для работы с 8-битной шиной, то эта проблема исчезнет, так как из SDRAM придется читывать в 4 раза меньше данных.

Ссылка на проект
  • +9
  • 23 октября 2013, 21:02
  • citizen

Комментарии (13)

RSS свернуть / развернуть
Может быть, стоит перекинуть это в блог «ПЛИС»?
размер ОЗУ — 30000 байт
А почему не 32768?
0
  • avatar
  • Vga
  • 23 октября 2013, 22:11
Размер ОЗУ устанавливается произвольно, а число 30000 ввести проще.
0
Но оно получается некруглым, да и 2768 байт памяти просто пропадают впустую (если я правильно понял, что она выделяется 8КБ блоками).
0
LCD display? ЖКИ индикатор? LED диод? В этих аббревиатурах уже есть и «дисплей», и «индикатор» и… линия ЛЭП, прейскурант цен, CD диск и ещё тысячи их.
+2
  • avatar
  • EW1UA
  • 23 октября 2013, 22:24
en.wikipedia.org/wiki/RAS_syndrome
Нет статьи на русском языке.
0
SOPS в Quartus 13? Вообще то оно теперь Qsys называется, ну это мелочь.:-)
В тему про обработку видео, у альтеры есть такая штука как University program в которых тоже есть мегафунции для видео. Там как раз уже собранные проекты под платы DEx, и хоть up_IP узкозаточены под кокретные примеры, их исходники доступны и значит их можно перепилить под себя.
+1
Да, действительно Qsys, забыл указать это в статье. Qsys несколько сложнее SOPC Builder — там нужно рисовать больше соединений.
Глянул примеры работы с видео в University program — похоже, для их использования нужно наличие SSRAM, хотя я могу и ошибаться.
0
Там примеры, конкретно под платы в которых SRAM есть, но по идее можно использовать и SDRAM, главное правильно подключить ( IP для DMA там же). Единственное с чем я толком не разобрался это их шина для потоковых данных, вот как ее SDRAM прикрутить не особо ясно.
0
Посмотрел еще раз ваш пост, у вас уже есть DMA, так что в их примеры надо не много изменить.
0
Полезная статейка. Только добыть такой индикатор проблема. Мой опыт ковыряний с DE0 Nano — Ethernet acvarif.info/prvhdl/prvhdl20.html Может кому пригодится.
0
Почему при настройке Video_Sync_Generator параметр Data Stream Bit Width = 24? У Вас же дисплей 18-битный. Вопрос по тексту: «Pixel Converter (Peripherals>Display>Pixel Converter (BGR0->BGR)). Нужен для конвертации 32-битного потока данных в 24-битный.» Почему поток 32-битный? Эту разрядность определяет DMA или FIFO? Разве при настройке DMA нельзя изменить разрядность в параметре Data width?
0
Честно говоря, уже не помню, почему сделано именно так. Насколько я помню, частично это сделано для того, чтобы не переделывать готовые примеры кода для Nios.
В этом проекте все сделано максимально просто, по примерам с ites.google.com/site/fpgaandco/
Как вы можете видеть, к индикатору подключены только 8 линий, но в SOPS на каждый цвет приходится 8 бит, что дает суммарно 24 бита на пиксель.
Насколько я знаю, сам процессор не умеет работать с 24-битными данными, так что приходится расширять разрядность шины данных до 32 бит на пиксель при помощи Pixel Converter.
0
Еще вопрос по тексту: «video_wire[23..0] — 24-битная шина видеоданных. Как можно было заметить, компонент video_sync_generator поддерживает только такую ширину шины». Почему ТОЛЬКО ТАКУЮ? Вы же сами изменили ее вот тут: «Data Stream Bit Width = 24. Ширина шины данных.» Этот параметр можно изменять!
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.