← Homepage
IO-Warrior 40

Controlling an LCD from Mac OS X using IO-Warrior

After soldering everything in place according to the sheet that came with my IO-Warrior 40 Starter Kit and checking it using the demo AppleScript and the included LEDs, I connected the 20x4 LCD display (which I got from eBay) using the schematic from the IO-Warror spec sheet (Note: The IO-Warrior 24 Starter Kit already comes with the required circuit in place).

LCD plus IO-Warrior 40

The next step was to check the LCD using the Prober included in the SDK. It worked fine after I realized that you have to select interface 1 for the LCD features :).

After that, I tried to access the LCD from a C application. It's actually pretty easy, because Code Mercenaries includes C functions for Mac OS X. To initialize the display, all you have to do is to create a new Foundation tool project in Xcode, add their C-SDK, and write the following into main.m:

#import <Foundation/Foundation.h>
#include "IOWarriorLib.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    if(IOWarriorInit()) {
        NSLog(@"IOWarriorInit failed.");
        return 1;
    }
    
    if(!IOWarriorIsPresent()) {
        NSLog(@"No IO Warrior device found.");
        return 2;
    }
    
    data[0]=0x01;
    data[1]=0x00;
    data[2]=0x00;
    data[3]=0x00;
    data[4]=0x00;
    data[5]=0x00;
    data[6]=0x00;
    data[7]=0x00;
    
    IOWarriorWriteInterface1(0x4, data);
    [pool release];
    return 0;
}

When you compile and run this source, the LCD should light up.

Now we can clear the display and turn off the cursor (after writing the first command):

    data[0]=0x03;
    data[1]=0x38;
    data[2]=0x01;
    data[3]=0x0c;
    data[4]=0x00;
    data[5]=0x00;
    data[6]=0x00;
    data[7]=0x00;
    
    IOWarriorWriteInterface1(0x5, data);

Writing to the display is very easy, since the HD44780 controller uses a charmap that's very similar to ASCII. However, the IO-Warrior only allows us to send 6 characters at once, so I wrote a little utility function that takes care of splitting the data into chunks:

#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif

void writeLCD(NSString *str) {
    NSData *text=[str dataUsingEncoding:NSASCIIStringEncoding];
    short c;
    
    short chunks=([text length] / 6) + (([text length] % 6 > 0)?1:0);
    short chunk;
    
    for(chunk=0;chunk<chunks;chunk++) {
        char data[8];
        short offset;
        
        data[0]=(1<<7) | MIN(6, ((char)[text length])-chunk*6);
        for(offset=0;offset<MIN(6, ((char)[text length])-chunk*6);offset++)
            data[offset+1]=*(((char*)[text bytes])+chunk*6+offset);
        
        IOWarriorWriteInterface1(0x5, data);
    }
}

So all you have to do is to call writeLCD(@"Hello World!"); and it should be displayed!

Since I'm German speaking, I wanted support for non-ASCII characters like ä or ß. ASCII does not provide those characters, however, the HD44780 chip supports them in the character range above 127 (there are only 127 characters in ASCII) - here's a chart. For converting them, I had to get some kind of character translation table. Since I didn't want to write one myself, I copied the one from lcdproc.

Here's the modified version of writeLCD (note that you have to add #include "hd44780-charmap.h" at the top of main.m):

void writeLCD(NSString *str) {
    NSData *iso88591text=[str dataUsingEncoding:NSISOLatin1StringEncoding];
    short c;
    NSMutableData *text=[[NSMutableData alloc] init];
    
    // convert ISO-8859-1 to HD44780 encoding
    for(c=0;c<[iso88591text length];c++)
        [text appendBytes:
          &HD44780_charmap[*(((unsigned char*)[iso88591text bytes])+c)]
                   length:1];
    
    short chunks=([text length] / 6) + (([text length] % 6 > 0)?1:0);
    short chunk;
    
    for(chunk=0;chunk<chunks;chunk++) {
        char data[8];
        short offset;
        
        data[0]=(1<<7) | MIN(6, ((char)[text length])-chunk*6);
        for(offset=0;offset<MIN(6, ((char)[text length])-chunk*6);offset++)
            data[offset+1]=*(((char*)[text bytes])+chunk*6+offset);
        
        IOWarriorWriteInterface1(0x5, data);
    }
    [text release];
}

Since I wanted to display something useful, I enhanced this tool to read the current track information from iTunes and the current channel from EyeTV. If both applications aren't playing anything, I decided to turn off the display (which can be done by repeating the very first command with data[0] set to 0x00).

The only sane way to get the required information is to use AppleScript, so I used the NSAppleScript class from Foundation to compile & run my scripts I embedded into the source code in an NSString. Since Applescript doesn't support change notfications, I had to poll the application (I chose an interval of half a second). This also allowed me to implement scrolling the title of iTunes tracks on the display, like the iPod does (this is hard to capture in a photo).

Scrolling Title

Here's the full Xcode project of the tool: LCDWriter.tar.bz2
It is released under the GPL (since the character mapping file requires that), have fun with it!

If you have any questions regarding this project, feel free to , or ask somebody who really knows what he's talking about on Code Mercenaries' discussion board!

© 2004 Andreas Monitzer. All Rights Reserved.