Thursday, February 11, 2016

VGA Generation with Freescale i.MX23 + Linux

I have long wanted to become more well acquainted with the Linux kernel and finally decided to bite the bullet. I ordered an iMX233-OLinuXino-MAXI from Olimex to tinker with. The Freescale i.MX23 processor is noteworthy because it is available in an LQFP-128 package which means it can be installed on a PCB by hand with inexpensive tools and a steady hand. It is also a great platform to learn with because it is well supported by the upstream kernel and has documentation available without signing an NDA.

The first thing I did was modify the device tree (DTS files) to enable the LCD controller and tuned it to generate a VGA signal. I built a simple R/R2 DAC on a breadboard and was able to view the image on an LCD monitor.

The projected image next to my workstation
All of this was completed under Arch Linux ARM which provides a minimal base image and root filesystem upon which a large number of packages have been ported to run on ARM. This has been a great learning experience and I plan to continue working with this chip more.

Hackaday, it doesn't quite like the 640 width
Continue reading to see the very minor changes I made to the kernel and how I wired it  all up.

Arch Linux ARM


The first thing I did was install the "default" Arch Linux ARM system image onto a 2GB uSD card. You can find complete instructions on how how to install Arch Linux ARM on their website. I won't duplicate the steps required for a base setup here. I recommend using a larger card if you have one available.

Custom Kernel


The next thing you will need to do is obtain a copy of the kernel sources. I recommend performing a complete checkout of the Git repo available on GitHub. You can also download a snapshot from kernel.org if you are in a rush. You will be interested in version 4.4.1 to follow along with this guide.

I was inspired by an Olimex forum post from March of 2013. Things have changed quite a bit in Kernel land and I decided to try to adapt the changes necessary to a more modern kernel. I found that the changes were extremely minimal! Many of the changes made are either unnecessary or available upstream already. You will need to apply a patch (linked) to two files.

The patch to imx23.dtsi simply defines a 16-bit LCD interface. The patch to arch/arm/boot/dts/imx23-olinuxino.dts uses the 16-bit LCD interface and configures the timing to generate VGA waveforms.
lcdif@80030000 {
 pinctrl-names = "default";
 pinctrl-0 = <&lcdif_16bit_pins_a>;
 display = <&display0>;
 status = "okay";

 display0: display0 {
  bits-per-pixel = <16>;
  bus-width = <16>;

  display-timings {
   native-mode = <&timing0>;
   timing0: timing0 {
    clock-frequency = <25200000>;
    hactive = <640>;
    hfront-porch = <16>;
    hsync-len = <54>;
    hback-porch = <48>;
    hsync-active = <0>;

    vactive = <480>;
    vfront-porch = <10>;
    vsync-len = <2>;
    vback-porch = <33>;
    vsync-active = <0>;

    de-active = <1>;
    pixelclk-active = <0>;
   };
  };
 };
};
That's it! There are some other recommended patches, but I did not bother to apply them. You can see more details in the PKGBUILD for the OLinuXino kernel. This is essentially a script that describes how to build the kernel.

Building the Kernel


Building the kernel is quite painless. A complete description, in script format is available from the PKGBUILD for the OLinuXino kernel. I will summarize the steps here. I should note that what follows is not intended to be a script, but more of a guide. Your mileage may vary :]
# Configure the environment for an ARM kernel with the arm-eabi- toolchain prefix
export ARCH=arm
export CROSS_COMPILE=arm-eabi-

# Obtain the kernel configuration for Arch Linux ARM
curl -o .config https://raw.githubusercontent.com/archlinuxarm/PKGBUILDs/master/core/linux-armv5/config
# Build the kernel (this assume you have already applied the VGA patch)
make prepare
make -j 12 # tune this to the number of cores your system has

# Prepare updates to the root filesystem
mkdir ~/rootfs # put this anywhere you wish, it is a staging area
mkdir -p ~/rootfs/lib/modules
mkdir -p ~/rootfs/lib/firmware
mkdir -p ~/rootfs/boot/dtbs

# Install kernel modules / device tree binaries
make INSTALL_MOD_PATH=~/rootfs/ modules_install
make INSTALL_DTBS_PATH=~/rootfs/boot/dtbs dtbs_install

# Copy the kernel image
cp arch/arm/boot/zImage ~/rootfs/boot/

# Run depmod
export KERNVER=`make kernelrelease`
depmod -b ~/rootfs/ -F System.map $KERNVER

# Overlay the contents of ~/rootfs onto the SD card
mount /dev/sdd2 /mnt/rootfs # tune this to your device name and mount point
cd ~/rootfs/boot
sudo cp -R * /mnt/rootfs # do the same for lib/modules and lib/firmware

Hardware Hacking :]


At this point, you should be able to boot from the SD card with your newly-built kernel. Woohoo! If all is working as expected, you should see the following:
[alarm@alarm ~]$ dmesg | grep mxsfb
[    1.080000] mxsfb 80030000.lcdif: initialized
At this point there are 18 GPIO lines that are very excited to show you a console, but first we need to build a DAC in order to interface those lines with a typical VGA monitor. An R/R2 DAC will be used for this purpose. They are simple to build and have enough bandwidth to pass the video signal. For this design, we will take into account the 75-ohm impedance of the monitor.

The effective resistance of an R/R2 DAC is R. The full-swing (white) voltage on VGA is 0.7V and the GPIO voltage is 3.3V. In order to drive the monitor effectively, the DAC will form a portion of a resistive divider according to the following formula:
$$ 3.3 * \frac{75}{R + 75} = 0.7 $$ Solving for R yields a resistance of 278 ohms. I decided to use 270 ohms, this is close enough.

R/R2 DAC (courtesy of Wikipedia)
The resistors will be arranged according to the diagram above. The most significant bit is connected closest to the output and the least significant is connected furthest from the output.

The signal produced by this setup will be an RGB565 signal. As such, three DACs were built. Two are 5-bit DAC and the other is a 6-bit DAC.

Initial Construction :]
I arranged the resistors to use breadboard space efficiently. Below you can see one completed DAC that would eventually become the red channel. In the picture below, you can also see the first version of this circuit (on the right). I constructed it with components that I had on hand: 180 and 200 ohm resistors. This applied a severe skew to the colorspace, but it proved that I should buy the right values and build a second version.

Red Channel Completed
After some time, I finished assembling all three channels and connected them to the OLinuXino Maxi.

Completed DAC!
The inputs to the DAC come from the LCD data lines. The OLinuXino manual contains details. Data lines 0-4 form the blue channel, 5 - 10 form the green channel and 11-15 form the blue channel.

The RGB, HSync and VSync lines were routed into a VGA connector and connected to a small monitor.

Overview of the setup and my website loaded with Midori
There should be enough detail here for you to replicate the setup if you are inclined to do so. I am considering designing a small PCB that fits onto the OLinuXino MAXI and exposes a VGA connector. I'll will need to decide if this is worth it or not.

htop running under LXDE
The system has 64MB and runs at 454MHz. It is certainly not a speed daemon by any means. It took several minutes to render Hackaday and my blog and I had to add a swap file to avoid the OOM killer.

htop, again
I hope you enjoyed reading and feel free to share if you decide to copy the setup!

1 comment :

  1. Hi Andrew,

    I am working on a similar project.

    I wondered if I can ask you some questions that would really help me.

    - What resolution and FPS where you working with?
    - Was the image quality affected for using 2R DACs instead of a Vidoe DAC? was the image noisy o blurry?
    - Do you have any video of your set up working?

    Thank you in advance for your help.

    Aitor

    ReplyDelete