Eiffel and a VFD

by Paul Bates (modified: 2008 May 06)

A project I started a few years ago (I could say completed but I got bored) I've recently revisited and just updated to use newer Eiffel syntax. It concerned itself with something not too often published uses for Eiffel, an API stack whose bottom layer interacts with low level system calls to manipulate hardware.

The project started because of a simple aggravation with building a media center that would handled power events correctly. As with everything that starts small the project kept on growing and growing (The API stack is not finished but a couple of levels will soon be available for anyone to use.) The API is a simple and effective controller for manipulating a Vacuum Florescent Display (VFD) in Eiffel. You may have seen a number of projects related the HD44780 controller all written in languages such as C or C# but that's about it, and the implementation not far off the actual low level calls. In fact to call them APIs would be overstating their use.

So a few years ago I started working on an API that begun as just getting information displayed on a VFD. Once I learned about the signal processing of the parallel port and the HD44780 processing bit instructions, I set out to create a parallel port interface and a controller for the display in Eiffel. It all went together quite well and without complication thanks to code strewn with contracts. Then emerged a higher level API for interacting with the controller, then abstracting communication channel to use either 8 (parallel) or 4 (serial) bit communication (although I have not implemented the USB part yet because I do not have a USB VFD or LCD :). Then abstracting further to support all platforms. The software stack came close to being completed with a "panel" API for rendering different displays by "activating" and swapping panels, which supported transitions and text scrolling functionality. One day I carried my media center into the office for a little show and tell, showing off the EiffelVision 2 application used to manipulate the display and drawing of custom characters, and the scrolling effects. All written in Eiffel except for a two calls to a Windows port driver (A driver has to be used because Windows does not allow applications to access Windows Ports directly.)

Then, I got bored. It got left to collect binary dust scattered around on several drives to a point where I couldn't remember which version was the most recent. I didn't just get bored, I also replace my notebook with a MacBook Pro, sans parallel port. I kept with it for a while and even created a client and server service so I could remotely manipulate the VFD on my media center from my Mac.

Recently I've felt like I should clean it up and put it out there. Not that anyone is screaming out for a parallel port library these days, but having access to a simple API for writing text to a VFD and even authoring custom characters might be worth something. Anyone looking to create a POS system could use it?!

So I took the code and cleaned it up.

You know when you spend hours working on something and then finally get it to compile, you run it only to find out it doesn't run as expected? I was dreading that moment. Double so because I'd swapped the DLPORTIO driver I was using for InpOut32 because of its Windows x64 support. I'd also switched to using a new Dynamic Library API I recently added to our repository. I debug-executed it and recieved a post-condition violation within a minute. The post-condition was wrong, fixed it, it ran though and this was what I got:

Yea! Almost a first run, which never happens after modifying that much code off and on over a year.

It wouldn't be complete without the code used to create the above screenshot, so here it is:

test_vdf -- Perform a simple VDF test. local l_connection: !PRT_PARALLEL_CONNECTION l_channel: !HD44780_PARALLEL_COMM_CHANNEL l_controller: !HD44780_CONTROLLER do -- Create the communication channel, in this case using a parallel port -- channel using port LPT1, using the InpOut32 API. create l_connection.make ( {PRT_PARALLEL_ADDRESS}.lpt1, create {PRT_INPOUT32_STREAM_FACTORY}) if l_connection.is_writable then create l_channel.make (l_connection) -- Now we have a writable channel, create the controller. create l_controller.make ({HD44780_DISPLAY_CONFIG}.display_16x2, l_channel) -- Initialize defaults and turn on. l_controller.initialize -- Write some text l_controller.put_string ("=Eiffel Rulez=") -- Display the cursor and reset it's position to home (1, 1) l_controller.is_cursor_shown := True l_controller.is_cursor_blinking_enabled := False l_controller.set_cursor_position (1, 1) end end

The code seems a bit verbose for initialization but it's required to abstract the channel of communication for the controller and for the port library used.

The project combines a number of personal libraries that I need to figure out how to organize for ease of compilation. I'll keep you posted.