This module provides a simple API to the Maxim MAX7456 on-screen display chip

Files at this revision

API Documentation at this revision

Comitter:
AjK
Date:
Tue Nov 16 10:47:25 2010 +0000
Commit message:
1.7

Changed in this revision

ChangeLog.c Show annotated file Show diff for this revision Revisions of this file
MAX7456.cpp Show annotated file Show diff for this revision Revisions of this file
MAX7456.h Show annotated file Show diff for this revision Revisions of this file
MAX7456CHARS.h Show annotated file Show diff for this revision Revisions of this file
OSD7456.cpp Show annotated file Show diff for this revision Revisions of this file
OSD7456.h Show annotated file Show diff for this revision Revisions of this file
example1.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ChangeLog.c	Tue Nov 16 10:47:25 2010 +0000
@@ -0,0 +1,34 @@
+/*
+
+1.0 - 13/10/2010 - Initial release.
+
+1.1 - 14/11/2010
+
+    * Remove a #include file that was left in by mistake.
+    
+1.2 - 14/11/2010
+
+    * Improve some of the documentation.
+    * Declare the register level API as __INLINE
+    * Added the OSD7456 "wrapper" class.
+    
+1.3 - 14/11/2010
+
+    * Fixed some documentation issues.
+
+1.4 - 15/11/2010
+
+    * Added doxygen manual comments.
+
+1.5 - 15/11/2010
+
+    * Added even more doxygen manual comments.
+    
+1.6 - 16/11/2010
+
+    * Added simple methods to blink rate and background brightness
+
+1.7 - 16/11/2010
+
+    * Added even more doxygen manual comments.    
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX7456.cpp	Tue Nov 16 10:47:25 2010 +0000
@@ -0,0 +1,303 @@
+/*
+    Copyright (c) 2010 Andy Kirkham
+ 
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+ 
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+ 
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+*/
+
+#include "mbed.h"
+#include "MAX7456.h"
+
+#ifdef MAX7456_DEBUG
+#include "IOMACROS.h"
+extern Serial pc;
+#endif
+
+#define CS_ASSERT   _cs->write(0)
+#define CS_DEASSERT _cs->write(1)
+
+// This header should only be included once here in this
+// file as it compiles inline directly. Include within
+// the library namespace.
+#include "MAX7456CHARS.h"
+
+/* Map ASCII table to the MAX7456 character map.
+   Note, the MAX7456 in-built character map is no where near the ascii
+   table mapping and very few characters are available to map. Where 
+   possible we create new characters for those we need that are missing
+   from the MAX7456 that we want to use and also we create some special
+   characters of our own that are not ASCII chars (crosshair for example).
+   These additional character definitions are listed below the table. 
+   Character maps we have create can be found in MAX7456CHARS.h */
+const unsigned char MAX7456_ascii[256] = {
+
+    /* Regular ASCII table. */
+    /*         00    01    02    03    04    05    06    07     08    09    0A    0B    0C    0D    0E    0F  */  
+    /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* 20 */ 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x46,  0x3F, 0x40, 0x00, 0x4d, 0x45, 0x49, 0x41, 0x47, 
+    /* 30 */ 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,  0x08, 0x09, 0x44, 0x43, 0x4A, 0x00, 0x4B, 0x42,
+    /* 40 */ 0x4C, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,  0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 
+    /* 50 */ 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21,  0x22, 0x23, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* 60 */ 0x46, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,  0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33,
+    /* 70 */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,  0x3C, 0x3D, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    
+    /* Extended ASCII table. */
+    /*         00    01    02    03    04    05    06    07     08    09    0A    0B    0C    0D    0E    0F  */  
+    /* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* B0 */ 0xB0, 0x00, 0x00, 0xB3, 0xB4, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF,   
+    /* C0 */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0xD9, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* E0 */ 0xe0, 0xe1, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+    /* F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00     
+};
+
+MAX7456::MAX7456(PinName mosi, PinName miso, PinName sclk, const char *name, PinName cs, PinName rst, PinName vsync)
+{
+    init(mosi, miso, sclk, name, cs, rst, vsync);
+}
+
+MAX7456::MAX7456(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName vsync)
+{
+    init(mosi, miso, sclk, NULL, cs, rst, vsync);
+}
+
+MAX7456::~MAX7456()
+{
+    delete(_ssp);
+    delete(_cs);
+    delete(_rst);
+    delete(_vsync);
+}
+
+void
+MAX7456::init(PinName mosi, PinName miso, PinName sclk, const char *name, PinName cs, PinName rst, PinName vsync)
+{
+    _ssp = new SPI(mosi, miso, sclk, name);
+    _cs  = new DigitalOut(cs);
+    _rst = new DigitalOut(rst);    
+    if (vsync != NC) {
+        _vsync = new InterruptIn(vsync);
+        _vsync->fall(this, &MAX7456::vsync_isr);   
+    }
+    
+    // Set default output signals.
+    CS_DEASSERT;
+    _rst->write(1);
+   
+    // Setup the SSP.
+    _ssp->format(8,0);
+    _ssp->frequency(25000000);
+    
+    // Reset the MAX7456
+    reset();
+    
+    /* Write the custom CM map. */
+    write(0, read(0) & 0xF7);
+    for (int index = 0; custom_chars[index].ascii != 0; index++) {
+        write_char_map(custom_chars[index].ascii, custom_chars[index].map);
+    }
+    
+    wait_us(100000);
+    
+    /* Change the vertical offset. */
+    vos(0x16);
+    
+    /* Set the blink rate. */
+    //vm1((3 << 2) | 3);
+    
+    /* Enable display of OSD image. */
+    vm0(0x48);
+}
+
+void 
+MAX7456::write(unsigned char address, unsigned char byte) 
+{
+    CS_ASSERT;
+ 
+    /* MAX7456 addresses are always less than 0x80 so if the
+       address is > 0x7F then the caller is requesting an direct
+       8bit data transfer. */
+    if (address < 0x80) { _ssp->write(address); }
+    
+    _ssp->write(byte);
+    CS_DEASSERT;
+}
+
+int 
+MAX7456::read(unsigned char address) 
+{
+    int data;
+    address |= 0x80;
+    CS_ASSERT;
+    _ssp->write(address);
+    data = _ssp->write(0xFF);
+    CS_DEASSERT;
+    return data;
+}
+
+void
+MAX7456::reset(void)
+{
+    _rst->write(0); 
+    wait_us(100000);
+    _rst->write(1);  
+    wait_us(100000);
+}
+
+void
+MAX7456::clear_display(void)
+{
+    dmm(1 << 2);
+    while(dmm() & (1 << 2));
+}
+
+void 
+MAX7456::cursor(int x, int y) 
+{
+    uint16_t pos = (y * 30) + x;
+    dmah((pos >> 8) & 0x1);
+    dmal(pos & 0xFF);    
+}
+
+void 
+MAX7456::convert_string(char *s) 
+{
+    while(*(s)) {
+        *(s) = MAX7456_ascii[*(s)];
+        s++;
+    }
+}
+
+int
+MAX7456::string(char *s) 
+{
+    int len = 0;
+    dmm(1 | (1 << 6)); /* Enable 8bit write */    
+    while(*(s)) {
+        write(0x80, MAX7456_ascii[*s++]);        
+        len++;
+    }
+    write(0x80, 0xFF);
+    return len;
+}
+
+void
+MAX7456::attributes_xyl(int x, int y, char *s, int len)
+{
+    uint16_t pos = (y * 30) + x;
+    dmah(((pos >> 8) & 0x1) | (1 << 1));
+    dmdi(pos & 0xFF);    
+    dmm(1 | (1 << 6)); /* Enable 8bit write */    
+    while (len--) {
+        write(0x80, *s++);
+    }
+    write(0x80, 0xFF);
+}
+
+int
+MAX7456::stringxy(int x, int y, char *s) 
+{
+    cursor(x, y);
+    return string(s);
+}
+
+int
+MAX7456::stringxy(int x, int y, char *s, uint8_t a) 
+{
+    char *q = NULL;
+    int len;
+    cursor(x, y);
+    len = string(s);
+    q = (char *)malloc(len);
+    if (!q) {
+        return -1;
+    }
+    memset(q, a, len);
+    attributes_xyl(x, y, q, len);
+    free(q);
+    return len;
+}
+
+int
+MAX7456::stringxy(int x, int y, char *s, char *a) 
+{
+    int len;
+    cursor(x, y);
+    len = string(s);
+    attributes_xyl(x, y, a, 30);
+    return len;
+}
+
+void 
+MAX7456::stringl(int x, int y, char *s, int len) 
+{
+    cursor(x, y);            
+    dmm(1); /* Enable 8bit write */    
+    while(len--) {
+        if (*s == '\0') break;
+        write(0x80, MAX7456_ascii[*s++]);
+    }
+    write(0x80, 0xFF);
+}
+
+void 
+MAX7456::read_char_map(unsigned char address, unsigned char *data54) 
+{
+    cmah((uint8_t)address);
+    cmm(0x50);
+    for (uint8_t index = 0; index < 54; index++) {
+        cmal(index);
+        wait_us(1000);        
+        *(data54 + index) = read(0xC0);
+    }
+}
+
+void 
+MAX7456::write_char_map(unsigned char address, const unsigned char *data54) 
+{
+    unsigned char index, c, match = 1;
+    
+    write(CMAH, address);
+    write(CMM, 0x50);
+    wait_us(20000);
+    for (index = 0; index < 54; index++) {
+        write(CMAL, index);
+        c = read(0xC0);
+        if (c != data54[index]) {
+            match = 0;            
+            break;
+        }
+    }
+    
+    if (!match) {   
+        write(CMAH, address);
+        for (index = 0; index < 0x36; index++) {
+            write(CMAL, index);
+            write(CMDI, data54[index]);
+        }
+        write(CMM, 0xA0);
+        wait_us(20000);
+        while ((read(0xA0) & 0x20) != 0x00);    
+    }
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX7456.h	Tue Nov 16 10:47:25 2010 +0000
@@ -0,0 +1,485 @@
+/*
+    Copyright (c) 2010 Andy Kirkham
+ 
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+ 
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+ 
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+*/
+
+#ifndef MODMAX7456_H
+#define MODMAX7456_H
+
+#include "mbed.h"
+
+/** @defgroup MAX7456_API */
+/** @defgroup MAX7456_REGISTER_API */
+
+/** MAX7456 module
+ *
+ * @see http://mbed.org/cookbook/
+ * @see example1.cpp
+ *
+ * Example:
+ * @code
+ * #include "mbed.h"
+ * #include "MAX7456.h"
+ * 
+ * DigitalOut led1(LED1);
+ * 
+ * MAX7456 *max7456;
+ * 
+ * int main() {
+ * 
+ *     max7456 = new MAX7456(p5, p6, p7, p8, p20, p15);
+ *     
+ *     max7456->stringxy(1, 1, "Hello World");
+ *     
+ *     while(1) {
+ *         led1 = 1;
+ *         wait(0.5);
+ *         led1 = 0;
+ *         wait(0.5);
+ *     }
+ * }
+ * @endcode
+ */
+ 
+class MAX7456
+{
+public:
+    
+    //! MAX7456 register definitions
+    enum Registers {
+        VM0 = 0,  VM1, HOS, VOS, DMM, DMAH, DMAL, DMDI, CMM, CMAH, CMAL, CMDI, OSDM,
+        OSDBL = 0x6C
+    };
+    
+    //! MAX7456 Row brightness register definitions.
+    enum RowBrightnessReg {
+        RB0 = 16, RB1, RB2, RB3, RB4, RB5, RB6, RB7, RB8, RB9, RB10, RB11, RB12, RB13, RB14, RB15,         
+    };
+    
+    //! Character attributes.
+    enum Attributes {
+        //! Inverse background
+        Inverse = (1 << 5),
+        //! Character blinks
+        Blink   = (1 << 6),
+        //! Use "local background"
+        LocalBG = (1 << 7)
+    };
+    
+    //! Background brightness levels (see register VM1)
+    enum BGbrightness {
+        //! 0%
+        Percent_0  = (0 << 4),
+        //! 7%
+        Percent_7  = (1 << 4),
+        //! 14%
+        Percent_14 = (2 << 4),
+        //! 21%
+        Percent_21 = (3 << 4),
+        //! 28%
+        Percent_28 = (4 << 4),
+        //! 35%
+        Percent_35 = (5 << 4),
+        //! 42%
+        Percent_42 = (6 << 4),
+        //! 49%
+        Percent_49 = (7 << 4)
+    };
+    
+    //! Character blink time.
+    enum BlinkTime {
+        //! 33milliseconds
+        ms_33  = (0 << 2),
+        //! 67milliseconds
+        ms_67  = (1 << 2),
+        //! 100milliseconds
+        ms_100 = (2 << 2),
+        //! 133milliseconds
+        ms_133 = (3 << 2) 
+    };
+    
+    // Blink Time duty cycle.
+    enum BlinkingDutyCycle {
+        //! 1:1 duty cycle
+        BT_BT   = (0 << 0),
+        //! 1:2 duty cycle
+        BT_BT2  = (1 << 0),
+        //! 1:3 duty cycle
+        BT_BT3  = (2 << 0),
+        //! 3:1 duty cycle
+        BT3_BT  = (3 << 0)
+    };
+    
+    //! MAX7456 constructor.
+    /**
+     * The MAX7456 constructor is used to initialise the MAX7456 object.
+     *
+     * Example 1
+     * @code
+     *     #include "mbed.h"
+     *     #include "MAX7456.h"
+     *
+     *     MAX7456 max(p5, p6, p7, NULL, p8, p20, p15);
+     *
+     *     int main() {
+     *
+     *         max.stringxy(1, 1, "Hello World");
+     *
+     *         while(1); 
+     *     }
+     * @endcode
+     * 
+     * Example 2
+     * @code
+     *     #include "mbed.h"
+     *     #include "MAX7456.h"
+     *
+     *     int main() {
+     *
+     *         MAX7456 max = new MAX7456(p5, p6, p7, NULL, p8, p20, p15);
+     *
+     *         max->stringxy(1, 1, "Hello World");
+     *
+     *         while(1); 
+     *     }
+     * @endcode
+     *
+     * @ingroup MAX7456_API
+     * @param miso PinName p5 or p11
+     * @param mosi PinName p6 or p12
+     * @param sclk PinName p7 pr p13
+     * @param name Optional const char * SSP object name
+     * @param cs   PinName CS signal
+     * @param rst  PinName RESET signal
+     * @param vsync PinName Vertical sync signal
+     */
+    MAX7456(PinName mosi, PinName miso, PinName sclk, const char *name, PinName cs, PinName rst, PinName vsync);         
+    
+    //! MAX7456 constructor.
+    /**
+     * The MAX7456 constructor is used to initialise the MAX7456 object.
+     *
+      * Example 1
+     * @code
+     *     #include "mbed.h"
+     *     #include "MAX7456.h"
+     *
+     *     MAX7456 max(p5, p6, p7, p8, p20, p15);
+     *
+     *     int main() {
+     *
+     *         max.stringxy(1, 1, "Hello World");
+     *
+     *         while(1); 
+     *     }
+     * @endcode
+     * 
+     * Example 2
+     * @code
+     *     #include "mbed.h"
+     *     #include "MAX7456.h"
+     *
+     *     int main() {
+     *
+     *         MAX7456 max = new MAX7456(p5, p6, p7, p8, p20, p15);
+     *
+     *         max->stringxy(1, 1, "Hello World");
+     *
+     *         while(1); 
+     *     }
+     * @endcode
+     *
+     * @ingroup MAX7456_API
+     * @param miso PinName p5 or p11
+     * @param mosi PinName p6 or p12
+     * @param sclk PinName p7 pr p13
+     * @param cs   PinName CS signal
+     * @param rst  PinName RESET signal
+     * @param vsync PinName Vertical sync signal
+     */
+    MAX7456(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName vsync);
+    
+    ~MAX7456();
+    
+    //! reset()
+    /**
+     * Resets the MAX7456 device.
+     *
+     * @ingroup MAX7456_API
+     */
+    void reset(void);
+    
+    //! clear_display()
+    /**
+     * Cleasr the entire display area.
+     *
+     * @ingroup MAX7456_API
+     */
+    void clear_display(void);
+    
+    //! cursor(x,y)
+    /**
+     * Moves the "cursor" to the screen X,Y position. Future writes to the
+     * screen ram will occur at this position.
+     *
+     * @ingroup MAX7456_API
+     * @param x The X position
+     * @param y The Y position
+     */
+    void cursor(int x, int y);
+    
+    //! convert_string(*s)
+    /**
+     * Takes a pointer to an ascii string and converts it to chars that will
+     * display via teh MAX7456 character set. Note, the string is converted.
+     *
+     * @param s A char * pointer to the NULL terminated string to convert.
+     */
+    void convert_string(char *s);
+    
+    //! string(*s)
+    /**
+     * Print the string pointed to by s at the current cursor position.
+     * The string should be an ASCII NULL terminated string.
+     *
+     * @ingroup MAX7456_API
+     * @param s A char * pointer to the NULL terminated string to print.
+     */
+    int string(char *s);
+    
+    //! stringxy(x,y,s)
+    /**
+     * Print the string pointed to by s at the position supplied by x,y.
+     * The string should be an ASCII NULL terminated string.
+     *
+     * @ingroup MAX7456_API
+     * @param x The X position
+     * @param y The Y position
+     * @param s A char * pointer to the NULL terminated string to print.
+     * @return int The length of the string written.
+     */
+    int stringxy(int x, int y, char *s);
+    
+    //! stringxy(x,y,s,a)
+    /**
+     * Print the string pointed to by s at the position supplied by x,y.
+     * The string should be an ASCII NULL terminated string.
+     *
+     * @ingroup MAX7456_API
+     * @param x The X position
+     * @param y The Y position
+     * @param s A char * pointer to the NULL terminated string to print.
+     * @param a An attribute byte to appply to the string.
+     * @return int The length of the string written.
+     */
+    int stringxy(int x, int y, char *s, uint8_t a);
+
+    //! stringxy(x,y,s,a)
+    /**
+     * Print the string pointed to by s at the position supplied by x,y.
+     * The string should be an ASCII NULL terminated string.
+     *
+     * @ingroup MAX7456_API
+     * @param x The X position
+     * @param y The Y position
+     * @param s A char * pointer to the NULL terminated string to print.
+     * @param a A char * pointer to the 30byte attribute string.
+     * @return int The length of the string written.
+     */
+    int stringxy(int x, int y, char *s, char *a);
+
+    //! stringxy(x,y,s,len)
+    /**
+     * Print the string pointed to by s at the position supplied by x,y.
+     * The string should be an ASCII terminated string. 
+     * len determines the length of the string to print (not the term NULL).
+     *
+     * @ingroup MAX7456_API
+     * @param x The X position
+     * @param y The Y position
+     * @param s A char * pointer to the NULL terminated string to print.
+     * @param len The length of teh string to print.
+     */
+    void stringl(int x, int y, char *s, int len);
+    
+    //! attribute_xyl(x,y,s,len)
+    /**
+     * Write the character attribute bytes at x,y
+     * Since attributes can be zero (NULL), the string length 
+     * must be provided.
+     *
+     * @ingroup MAX7456_API
+     * @param x The X position
+     * @param y The Y position
+     * @param s A char * pointer to the NULL terminated string of attributes.
+     * @param len The length of the string of attributes.
+     */
+    void attributes_xyl(int x, int y, char *s, int len);
+
+    /**
+     * Set the background brightness
+     *
+     * @see BGbrightness
+     * @ingroup MAX7456_API
+     * @param i The brightness level
+     */
+    void backGround(BGbrightness i) { vm1((vm1() & ~(7 << 4)) | i); } 
+    
+    /**
+     * Set the character blink rate
+     *
+     * @see BlinkTime
+     * @see BlinkingDutyCycle
+     * @ingroup MAX7456_API
+     * @param bt The blink time
+     * @param dc The duty cycle
+     */
+    void blinkRate(BlinkTime bt, BlinkingDutyCycle dc) { vm1((vm1() & ~(0xf)) | (bt | dc)); } 
+    
+    //! read_char_map(address,data54)
+    /**
+     * Read the 54byte character map from MAX7456 ROM.
+     *
+     * @ingroup MAX7456_API
+     * @param address The uchar address to read from (which character)
+     * @param data54 A char * pointer to a buffer where to write to.
+     */
+    void read_char_map(unsigned char address, unsigned char *data54);
+    
+    //! write_char_map(address,data54)
+    /**
+     * Write the 54byte character map to the MAX7456 ROM.
+     *
+     * @ingroup MAX7456_API
+     * @param address The uchar address to write to 
+     * @param data54 A char * pointer to a buffer where to read from.
+     */
+    void write_char_map(unsigned char address, const unsigned char *data54);
+
+    //! attach(tptr,mptr)
+    /**
+     * Attach a callback method to teh vertical sync interrupt.
+     *
+     * @ingroup MAX7456_API
+     * @param tptr The class object pointer conatining the method.
+     * @param mptr The method within the object to invoke.
+     */ 
+    template<typename T>
+    void vsync_set_callback(T* tptr, void (T::*mptr)(void)) {
+        _vsync_callback.attach(tptr, mptr);   
+    }
+    
+    //! attach(tptr,mptr)
+    /**
+     * Attach a callback function to the vertical sync interrupt.
+     *
+     * @ingroup MAX7456_API
+     * @param fptr A function pointer to call on vertical sync irq.
+     */ 
+    void vsync_set_callback(void (*fptr)(void)) { _vsync_callback.attach(fptr); }
+    
+    // Must be reimplemented by the child class.
+    virtual void vsync_isr(void) { _vsync_callback.call(); };
+    
+    // Register level API.
+    
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    vm0(uint8_t i)  { write(VM0, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t vm0(void)       { return (uint8_t)read(VM0); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    vm1(uint8_t i)  { write(VM1, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t vm1(void)       { return (uint8_t)read(VM1); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    hos(uint8_t i)  { write(HOS, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t hos(void)       { return (uint8_t)read(HOS); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    vos(uint8_t i)  { write(VOS, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t vos(void)       { return (uint8_t)read(VOS); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    dmm(uint8_t i)  { write(DMM, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t dmm(void)       { return (uint8_t)read(DMM); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    dmah(uint8_t i) { write(DMAH, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t dmah(void)      { return (uint8_t)read(DMAH); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    dmal(uint8_t i) { write(DMAL, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t dmal(void)      { return (uint8_t)read(DMAL); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    dmdi(uint8_t i) { write(DMDI, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t dmai(void)      { return (uint8_t)read(DMDI); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    cmm(uint8_t i)  { write(CMM, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t cmm(void)       { return (uint8_t)read(CMM); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    cmah(uint8_t i) { write(CMAH, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t cmah(void)      { return (uint8_t)read(CMAH); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    cmal(uint8_t i) { write(CMAL, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t cmal(void)      { return (uint8_t)read(CMAL); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    cmdi(uint8_t i) { write(CMDI, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t cmai(void)      { return (uint8_t)read(CMDI); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    osdm(uint8_t i) { write(OSDM, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t osdm(void)      { return (uint8_t)read(OSDM); }
+    /** @ingroup MAX7456_REGISTER_API */
+    
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t stat(void)      { return read(0xA0); }
+    
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  bool    los(void) { return (bool)!(stat() & (1 << 2)); }
+    
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t osdbl(void)   { return (uint8_t)read(OSDBL); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    osdbl(bool b) { uint8_t t = osdbl(); if (b) osdbl(t & ~(1 << 4)); else osdbl(t | (1 << 4)); }
+    
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  void    RowBrightness(RowBrightnessReg r, uint8_t i) { write(r, i); }
+    /** @ingroup MAX7456_REGISTER_API */
+    __INLINE  uint8_t RowBrightness(RowBrightnessReg r) { return read (r); }
+    
+protected:
+    void init(PinName mosi, PinName miso, PinName sclk, const char *name, PinName cs, PinName rst, PinName vsync = NC);
+    void write(unsigned char address, unsigned char byte);
+    int  read(unsigned char address);
+    
+    SPI         *_ssp;
+    DigitalOut  *_cs;
+    DigitalOut  *_rst;
+    InterruptIn *_vsync;
+    FunctionPointer _vsync_callback;   
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX7456CHARS.h	Tue Nov 16 10:47:25 2010 +0000
@@ -0,0 +1,409 @@
+/****************************************************************************
+ *    Copyright 2010 Andy Kirkham, Stellar Technologies Ltd
+ *    
+ *    This file is part of the Satellite Observers Workbench (SOWB).
+ *
+ *    SOWB is free software: you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation, either version 3 of the License, or
+ *    (at your option) any later version.
+ *
+ *    SOWB is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with SOWB.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *    $Id: main.cpp 5 2010-07-12 20:51:11Z ajk $
+ *    
+ ***************************************************************************/
+
+typedef struct _max7456_custom_char {
+    unsigned char         ascii;
+    const unsigned char   *map;
+} MAX7456_CUSTOM_CHAR;
+
+/* Why doesn't the MAX7456 have a + symbol? wtf! */
+const unsigned char ascii_4d[54] = {
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x00, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x00, 0x28, 0x00,
+    0x4A, 0xAA, 0xA1,
+    0x4A, 0xAA, 0xA1,
+    0x00, 0x28, 0x00,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x00, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55    };
+
+const unsigned char crosshair1_left[54] = {
+    0x55, 0x55, 0x56, // 0
+    0x55, 0x55, 0x56, // 1
+    0x55, 0x55, 0x56, // 2
+    0x55, 0x55, 0x56, // 3
+    0x55, 0x55, 0x56, // 4
+    0x55, 0x55, 0x56, // 5
+    0x55, 0x55, 0x55, // 6
+    0x55, 0x55, 0x55, // 7
+    0x55, 0xAA, 0x95, // 8
+    0x55, 0xAA, 0x95, // 9  - 8
+    0x55, 0x55, 0x55, // 10 - 7
+    0x55, 0x55, 0x55, // 11 - 6
+    0x55, 0x55, 0x56, // 12 - 5
+    0x55, 0x55, 0x56, // 13 - 4
+    0x55, 0x55, 0x56, // 14 - 3
+    0x55, 0x55, 0x56, // 15 - 2
+    0x55, 0x55, 0x56, // 16 - 1
+    0x55, 0x55, 0x56    };
+
+const unsigned char crosshair1_right[54] = {
+    0x95, 0x55, 0x55, // 0
+    0x95, 0x55, 0x55, // 1
+    0x95, 0x55, 0x55, // 2
+    0x95, 0x55, 0x55, // 3
+    0x95, 0x55, 0x55, // 4
+    0x95, 0x55, 0x55, // 5
+    0x55, 0x55, 0x55, // 6
+    0x55, 0x55, 0x55, // 7
+    0x56, 0xAA, 0x55, // 8
+    0x56, 0xAA, 0x55, // 9  - 8
+    0x55, 0x55, 0x55, // 10 - 7
+    0x55, 0x55, 0x55, // 11 - 6
+    0x95, 0x55, 0x55, // 12 - 5
+    0x95, 0x55, 0x55, // 13 - 4
+    0x95, 0x55, 0x55, // 14 - 3
+    0x95, 0x55, 0x55, // 15 - 2
+    0x95, 0x55, 0x55, // 16 - 1
+    0x95, 0x55, 0x55    };
+    
+const unsigned char crosshair_centre[54] = {
+    0x55, 0x28, 0x55,   // 0
+    0x55, 0x28, 0x55,   // 1
+    0x55, 0x28, 0x55,   // 2
+    0x55, 0x28, 0x55,   // 3
+    0x55, 0x28, 0x55,   // 4
+    0x55, 0x00, 0x55,   // 5
+    0x55, 0x55, 0x55,   // 6
+    0x01, 0x55, 0x40,   // 7
+    0xA1, 0x69, 0x4A,   // 8
+    0xA1, 0x69, 0x4A,   // 9
+    0x01, 0x55, 0x40,   // 10
+    0x55, 0x55, 0x55,   // 11
+    0x55, 0x00, 0x55,   // 12
+    0x55, 0x28, 0x55,   // 13
+    0x55, 0x28, 0x55,   // 14
+    0x55, 0x28, 0x55,   // 15
+    0x55, 0x28, 0x55,   // 16
+    0x55, 0x28, 0x55    };
+    
+
+const unsigned char ascii_b0[54] = {
+    0x55, 0x55, 0x55, // 0
+    0x55, 0x55, 0x55, // 1
+    0x50, 0x01, 0x55, // 2
+    0x42, 0xA0, 0x55, // 3
+    0x0A, 0xA8, 0x15, // 4
+    0x28, 0x0A, 0x15, // 5
+    0x28, 0x0A, 0x15, // 6
+    0x28, 0x0A, 0x15,
+    0x0A, 0xA8, 0x15,
+    0x42, 0xA0, 0x55,
+    0x50, 0x01, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55    };
+
+const unsigned char ascii_b3[54] = {
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55    };
+
+const unsigned char ascii_b4[54] = {
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x00, 0x28, 0x55,
+    0xAA, 0xA8, 0x55,
+    0xAA, 0xA8, 0x55,
+    0x00, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55    };
+
+const unsigned char ascii_bf[54] = {
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x00, 0x00, 0x55,
+    0xAA, 0xA8, 0x55,
+    0xAA, 0xA8, 0x55,
+    0x00, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55    };
+
+const unsigned char ascii_c0[54] = {
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x00,
+    0x55, 0x2A, 0xAA,
+    0x55, 0x2A, 0xAA,
+    0x55, 0x00, 0x00,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55    };
+
+const unsigned char ascii_c1[54] = {
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x00, 0x28, 0x00,
+    0xAA, 0xAA, 0xAA,
+    0xAA, 0xAA, 0xAA,
+    0x00, 0x00, 0x00,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55    };
+
+const unsigned char ascii_c2[54] = {
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x00, 0x00, 0x00,
+    0xAA, 0xAA, 0xAA,
+    0xAA, 0xAA, 0xAA,
+    0x00, 0x28, 0x00,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55    };
+
+const unsigned char ascii_c3[54] = {
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x00,
+    0x55, 0x2A, 0xAA,
+    0x55, 0x2A, 0xAA,
+    0x55, 0x28, 0x00,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55    };
+
+const unsigned char ascii_c4[54] = {
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x00, 0x00, 0x00,
+    0xAA, 0xAA, 0xAA,
+    0xAA, 0xAA, 0xAA,
+    0x00, 0x00, 0x00,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55    };
+
+const unsigned char ascii_c5[54] = {
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x00, 0x28, 0x00,
+    0xAA, 0xAA, 0xAA,
+    0xAA, 0xAA, 0xAA,
+    0x00, 0x28, 0x00,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55    };
+
+const unsigned char ascii_d9[54] = {
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x00, 0x28, 0x55,
+    0xAA, 0xA8, 0x55,
+    0xAA, 0xA8, 0x55,
+    0x00, 0x00, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55    };
+
+const unsigned char ascii_da[54] = {
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x55, 0x55,
+    0x55, 0x00, 0x00,
+    0x55, 0x2A, 0xAA,
+    0x55, 0x2A, 0xAA,
+    0x55, 0x28, 0x00,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55,
+    0x55, 0x28, 0x55    };
+
+/* For the curious, during testing I managed to screw up the
+   characher at CM index2, ('2'). So I needed to reprogram
+   index2 with the correct character, doh! Anyway, since I
+   went to the trouble I'll leave it in the code. I managed
+   to find a default CM map at:- 
+        http://www.maxim-ic.com/tools/evkit/index.cfm?EVKit=558
+   If you download this then be aware that the DEFAULTCM.MCM file
+   defines each charmap as 64bytes, the 10 additional bytes need
+   to be stripped off as "not used". */
+#ifdef FIX_CM2_SCREW_UP
+const unsigned char ascii_02[54] = {
+    0x55, 0x55, 0x55, // 01010101 01010101 01010101
+    0x55, 0x55, 0x55, // 01010101 01010101 01010101
+    0x54, 0x00, 0x15, // 01010100 00000000 00010101
+    0x52, 0xAA, 0x85, // 01010010 10101010 10000101
+    0x4A, 0xAA, 0xA1, // 01001010 10101010 10100001
+    0x2A, 0x80, 0xA8, // 00101010 10000000 10101000
+    0x2A, 0x15, 0x28, // 00101010 00010101 00101000
+    0x40, 0x55, 0x28, // 01000000 01010101 00101000
+    0x55, 0x54, 0xA8, // 01010101 01010100 10101000
+    0x55, 0x52, 0xA1, // 01010101 01010010 10100001
+    0x55, 0x4A, 0x85, // 01010101 01001010 10000101
+    0x55, 0x2A, 0x15, // 01010101 00101010 00010101
+    0x54, 0xA8, 0x55, // 01010100 10101000 01010101
+    0x52, 0xA1, 0x55, // 01010010 10100001 01010101
+    0x4A, 0x80, 0x01, // 01001010 10000000 00000001
+    0x2A, 0xAA, 0xA8, // 00101010 10101010 10101000
+    0x2A, 0xAA, 0xA8, // 00101010 10101010 10101000
+    0x40, 0x00, 0x01  // 01000000 00000000 00000001
+};
+#endif
+
+/* Create an array of structures that link the data
+   characters to ASCII characters. The last entry 
+   must always be null so we can detect the end of 
+   the array. */    
+MAX7456_CUSTOM_CHAR custom_chars[] = {
+    { 0xE0, crosshair1_left },
+    { 0xE1, crosshair1_right },
+    { 0xE2, crosshair_centre },
+    { 0x4D, ascii_4d },
+    { 0xB0, ascii_b0 },
+    { 0xB3, ascii_b3 },
+    { 0xB4, ascii_b4 },
+    { 0xBF, ascii_bf },
+    { 0xC0, ascii_c0 },
+    { 0xC1, ascii_c1 },
+    { 0xC2, ascii_c2 },
+    { 0xC3, ascii_c3 },
+    { 0xC4, ascii_c4 },
+    { 0xC5, ascii_c5 },
+    { 0xD9, ascii_d9 },
+    { 0xDA, ascii_da },
+#ifdef FIX_CM2_SCREW_UP    
+    { 0x02, ascii_02 },
+#endif    
+    { 0x00, 0 }
+};
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OSD7456.cpp	Tue Nov 16 10:47:25 2010 +0000
@@ -0,0 +1,108 @@
+/*
+    Copyright (c) 2010 Andy Kirkham
+ 
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+ 
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+ 
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+*/
+
+#include "mbed.h"
+#include "OSD7456.h"
+
+void
+OSD7456::init(void)
+{
+    for (int i = 0; i < OSD_NUM_LINES; i++) { clear(i); } 
+    oddEven = false;   
+    max7456->vsync_set_callback(this, &OSD7456::vsync);
+}
+
+void
+OSD7456::clear(int line)
+{
+    lines[line].updated = true;
+    memset(lines[line].line, ' ', OSD_LINE_LEN);
+    memset(lines[line].attrib, 0, OSD_LINE_LEN);
+}
+
+void 
+OSD7456::vsync(void)
+{
+    if (oddEven) {
+        oddEven = false;
+    }
+    else {
+        for (int i = 0; i < OSD_NUM_LINES; i++) {
+            if (lines[i].updated) {
+                max7456->stringxy(0, i, lines[i].line, lines[i].attrib);
+                lines[i].updated = false;
+            }
+        }
+        oddEven = true;
+        cb_vsync.call();
+    }
+}
+
+int 
+OSD7456::print(int line, char *s) 
+{ 
+    int i = 0; 
+    while (*s != '\0') { 
+        lines[line].line[i] = *s; 
+        s++; i++; 
+    } 
+    lines[line].updated = true; 
+    return i; 
+}
+ 
+int 
+OSD7456::print(int line, char *s, char attrib) 
+{ 
+    int i = 0; 
+    while (*s != '\0') { 
+        lines[line].line[i] = *s; 
+        lines[line].attrib[i] = attrib; 
+        s++; i++; 
+    } 
+    lines[line].updated = true; 
+    return i; 
+}
+
+int 
+OSD7456::print(int x, int y, char *s) 
+{ 
+    int i = 0; 
+    while (*s != '\0') { 
+        lines[y].line[i+x] = *s; 
+        s++; i++; 
+    } 
+    lines[y].updated = true; 
+    return i; 
+}
+
+int 
+OSD7456::print(int x, int y, char *s, char attrib) 
+{ 
+    int i = 0; 
+    while (*s != '\0') { 
+        lines[y].line[i+x] = *s; 
+        lines[y].attrib[i+x] = attrib; 
+        s++; i++; 
+    } 
+    lines[y].updated = true; 
+    return i; 
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OSD7456.h	Tue Nov 16 10:47:25 2010 +0000
@@ -0,0 +1,244 @@
+/*
+    Copyright (c) 2010 Andy Kirkham
+ 
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+ 
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+ 
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+*/
+
+#ifndef MODOSD7456_H
+#define MODOSD7456_H
+
+#include "mbed.h"
+#include "MAX7456.h"
+
+/** defgroup OSD7456_API */
+
+#define OSD_NUM_LINES   13
+#define OSD_LINE_LEN    30
+
+typedef struct _osd_line {
+    char line[OSD_LINE_LEN];
+    char attrib[OSD_LINE_LEN];
+    bool updated;
+} OSD_LINE;
+
+/** OSD7456 module
+ *
+ * The OSD7456 is a wrapper around the MAX7456 that abstracts the hardware
+ * into a simple to use screen writer system. It provides buffered output.
+ * To ensure a "flicker free" display, the buffers are written to the MAX7456
+ * chip at the start of the vertical sync period.
+ *
+ * @see http://mbed.org/cookbook/
+ * @see example2.cpp
+ *
+ * Example:
+ * @code
+ * #include "mbed.h"
+ * #include "OSD7456.h"
+ * 
+ * DigitalOut led1(LED1);
+ * 
+ * OSD7456 *osd;
+ * 
+ * int main() {
+ *     
+ *     osd = new OSD7456(p5, p6, p7, p8, p20, p15);
+ *     
+ *     osd->print(1, "Hello World");
+ *     osd->print2("This blinks", MAX7456::Blink);
+ *     osd->print3("Background", MAX7456::LocalBG);    
+ *     osd->print(5, 4, "Positioned");
+ *     osd->print(5, 5, "Positioned", MAX7456::LocalBG);
+ *     osd->print(3, 7, "Test");
+ *     osd->print(12, 7, "Test", MAX7456::LocalBG | MAX7456::Blink);
+ *     
+ *     while(1) {
+ *         led1 = 1;
+ *         wait(0.5);        
+ *         led1 = 0;
+ *         wait(0.5);    
+ *     }
+ * }
+ * @endcode
+ */
+ 
+class OSD7456
+{
+public:
+
+    //! OSD7456 constructor.
+    /**
+     * The OSD7456 constructor.
+     *
+     * @ingroup OSD7456_API
+     * @param miso PinName p5 or p11
+     * @param mosi PinName p6 or p12
+     * @param sclk PinName p7 pr p13
+     * @param name Optional const char * SSP object name
+     * @param cs   PinName CS signal
+     * @param rst  PinName RESET signal
+     * @param vsync PinName Vertical sync signal
+     */
+    OSD7456(PinName mosi, PinName miso, PinName sclk, const char *name, PinName cs, PinName rst, PinName vsync) {
+        max7456 = new MAX7456(mosi, miso, sclk, name, cs, rst, vsync); 
+        init();       
+    }
+    
+    //! OSD7456 constructor.
+    /**
+     * The OSD7456 constructor.
+     *
+     * @ingroup OSD7456_API
+     * @param miso PinName p5 or p11
+     * @param mosi PinName p6 or p12
+     * @param sclk PinName p7 pr p13
+     * @param cs   PinName CS signal
+     * @param rst  PinName RESET signal
+     * @param vsync PinName Vertical sync signal
+     */
+    OSD7456(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName vsync) {
+        max7456 = new MAX7456(mosi, miso, sclk, NULL, cs, rst, vsync);        
+        init();
+    }
+    
+    //! print()
+    /**
+     * Print ASCII text at line.
+     *
+     * @ingroup OSD7456_API
+     * @param line The line number to print at.
+     * @param s A pointer to the null terminated string to print.
+     */
+    int print(int line, char *s);
+    
+    //! print()
+    /**
+     * Print ASCII text on line y at position x
+     *
+     * @ingroup OSD7456_API
+     * @param x The line position to print at.
+     * @param y The line to print on.
+     * @param s A pointer to the null terminated string to print.
+     */
+    int print(int x, int y, char *s);
+    
+    //! print()
+    /**
+     * Print ASCII text on line y at position x with attribute.
+     *
+     * @ingroup OSD7456_API
+     * @param x The line position to print at.
+     * @param y The line to print on.
+     * @param s A pointer to the null terminated string to print.
+     * @param a An attribute byte to apply to the string.
+     */
+    int print(int line, char *s, char a);
+    
+    //! print()
+    /**
+     * Print ASCII text on line y at position x
+     *
+     * @ingroup OSD7456_API
+     * @param x The line position to print at.
+     * @param y The line to print on.
+     * @param s A pointer to the null terminated string to print.
+     * @param a An attribute byte to apply to the string.
+     */
+    int print(int x, int y, char *s, char attrib);    
+    
+    //! clear()
+    /**
+     * Clear the line number supplied.
+     *
+     * @ingroup OSD7456_API
+     * @param line The line number to clear.
+     */
+    void clear(int line);
+    
+    /** @ingroup OSD7456_API */
+    /** @{ */
+    __INLINE int print0(char *s) { return print(0, s); }
+    __INLINE int print1(char *s) { return print(1, s); }
+    __INLINE int print2(char *s) { return print(2, s); }
+    __INLINE int print3(char *s) { return print(3, s); }
+    __INLINE int print4(char *s) { return print(4, s); }
+    __INLINE int print5(char *s) { return print(5, s); }
+    __INLINE int print6(char *s) { return print(6, s); }
+    __INLINE int print7(char *s) { return print(7, s); }
+    __INLINE int print8(char *s) { return print(8, s); }
+    __INLINE int print9(char *s) { return print(9, s); }
+    __INLINE int print10(char *s) { return print(10, s); }
+    __INLINE int print11(char *s) { return print(11, s); }
+    __INLINE int print12(char *s) { return print(12, s); }
+    __INLINE int print13(char *s) { return print(13, s); }
+    
+    __INLINE int print0(char *s, char a) { return print(0, s, a); }
+    __INLINE int print1(char *s, char a) { return print(1, s, a); }
+    __INLINE int print2(char *s, char a) { return print(2, s, a); }
+    __INLINE int print3(char *s, char a) { return print(3, s, a); }
+    __INLINE int print4(char *s, char a) { return print(4, s, a); }
+    __INLINE int print5(char *s, char a) { return print(5, s, a); }
+    __INLINE int print6(char *s, char a) { return print(6, s, a); }
+    __INLINE int print7(char *s, char a) { return print(7, s, a); }
+    __INLINE int print8(char *s, char a) { return print(8, s, a); }
+    __INLINE int print9(char *s, char a) { return print(9, s, a); }
+    __INLINE int print10(char *s, char a) { return print(10, s, a); }
+    __INLINE int print11(char *s, char a) { return print(11, s, a); }
+    __INLINE int print12(char *s, char a) { return print(12, s, a); }
+    __INLINE int print13(char *s, char a) { return print(13, s, a); }
+    
+    /** @} */
+    
+    // The function registered with teh MAX7456 vertical sync interrupt handler.
+    void vsync(void);
+    
+    /**
+     * Attach a user callback object/method to call when the vsync signal activates. 
+     *
+     *
+     * @ingroup OSD7456_API
+     * @param tptr pointer to the object to call the member function on
+     * @param mptr pointer to the member function to be called
+     */
+    template<typename T>
+    void attach_vsync(T* tptr, void (T::*mptr)(void)) { cb_vsync.attach(tptr, mptr); }
+    
+    /**
+     * Attach a user callback function pointer to call when the vsync signal activates. 
+     *
+     * @ingroup OSD7456_API
+     * @param fptr Callback function pointer
+     */
+    void attach_vsync(void (*fptr)(void)) { cb_vsync.attach(fptr); } 
+    
+    //! A callback object for the 1PPS user API.
+    FunctionPointer cb_vsync;
+    
+    MAX7456 *max7456;
+    
+protected:
+    
+    OSD_LINE lines[OSD_NUM_LINES];
+    
+    void init(void); 
+    
+    bool oddEven;   
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/example1.cpp	Tue Nov 16 10:47:25 2010 +0000
@@ -0,0 +1,34 @@
+#ifdef COMPILE_EXAMPLE_CODE_MODOSD7456
+
+#include "mbed.h"
+#include "OSD7456.h"
+
+DigitalOut led1(LED1);
+
+OSD7456 *osd;
+
+int main() {
+    
+    osd = new OSD7456(MAX7456_MOSI, MAX7456_MISO, MAX7456_SCLK, MAX7456_CS, MAX7456_RST, MAX7456_VSYNC);
+    
+    // Set the character "local background" to 42%
+    osd->max7456->backGround(MAX7456::Percent_42);
+    
+    // Set the blink rate to 133ms with a duty cycle of 3:1
+    osd->max7456->blinkRate(MAX7456::ms_133, MAX7456::BT3_BT);
+    
+    osd->print(1, "        Hello World");
+    osd->print(7, 4,  "Positioned text");
+    osd->print(7, 6,  "Positioned text", MAX7456::LocalBG);
+    osd->print(3, 8,  "Positioned text blinks", MAX7456::Blink | MAX7456::LocalBG);
+    osd->print(4, 10, " Blinks and inverse ", MAX7456::Blink | MAX7456::LocalBG | MAX7456::Inverse);
+    
+    while(1) {
+        led1 = 1;
+        wait(0.5);        
+        led1 = 0;
+        wait(0.5);    
+    }
+}
+
+#endif