]> git.sur5r.net Git - freertos/blobdiff - Demo/CORTEX_LM3S811_IAR/LuminaryCode/osram96x16.c
New demo for M3 using LM3S811 and IAR tools.
[freertos] / Demo / CORTEX_LM3S811_IAR / LuminaryCode / osram96x16.c
diff --git a/Demo/CORTEX_LM3S811_IAR/LuminaryCode/osram96x16.c b/Demo/CORTEX_LM3S811_IAR/LuminaryCode/osram96x16.c
new file mode 100644 (file)
index 0000000..54f260d
--- /dev/null
@@ -0,0 +1,968 @@
+//*****************************************************************************\r
+//\r
+// osram96x16.c - Driver for the OSRAM 96x16 graphical OLED display.\r
+//\r
+// Copyright (c) 2006 Luminary Micro, Inc.  All rights reserved.\r
+//\r
+// Software License Agreement\r
+//\r
+// Luminary Micro, Inc. (LMI) is supplying this software for use solely and\r
+// exclusively on LMI's Stellaris Family of microcontroller products.\r
+//\r
+// The software is owned by LMI and/or its suppliers, and is protected under\r
+// applicable copyright laws.  All rights are reserved.  Any use in violation\r
+// of the foregoing restrictions may subject the user to criminal sanctions\r
+// under applicable laws, as well as to civil liability for the breach of the\r
+// terms and conditions of this license.\r
+//\r
+// THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED\r
+// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF\r
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.\r
+// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR\r
+// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.\r
+//\r
+// This is part of revision 991 of the Stellaris Driver Library.\r
+//\r
+//*****************************************************************************\r
+\r
+//*****************************************************************************\r
+//\r
+//! \addtogroup ev_lm3s811_api\r
+//! @{\r
+//\r
+//*****************************************************************************\r
+\r
+#include "DriverLib.h"\r
+#include "osram96x16.h"\r
+#define ewarm\r
+//*****************************************************************************\r
+//\r
+// The I2C slave address of the SSD0303 controller on the OLED display.\r
+//\r
+//*****************************************************************************\r
+#define SSD0303_ADDR            0x3d\r
+\r
+//*****************************************************************************\r
+//\r
+// A 5x7 font (in a 6x8 cell, where the sixth column is omitted from this\r
+// table) for displaying text on the OLED display.  The data is organized as\r
+// bytes from the left column to the right column, with each byte containing\r
+// the top row in the LSB and the bottom row in the MSB.\r
+//\r
+//*****************************************************************************\r
+static const unsigned char g_pucFont[95][5] =\r
+{\r
+    { 0x00, 0x00, 0x00, 0x00, 0x00 }, // " "\r
+    { 0x00, 0x00, 0x4f, 0x00, 0x00 }, // !\r
+    { 0x00, 0x07, 0x00, 0x07, 0x00 }, // "\r
+    { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // #\r
+    { 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // $\r
+    { 0x23, 0x13, 0x08, 0x64, 0x62 }, // %\r
+    { 0x36, 0x49, 0x55, 0x22, 0x50 }, // &\r
+    { 0x00, 0x05, 0x03, 0x00, 0x00 }, // '\r
+    { 0x00, 0x1c, 0x22, 0x41, 0x00 }, // (\r
+    { 0x00, 0x41, 0x22, 0x1c, 0x00 }, // )\r
+    { 0x14, 0x08, 0x3e, 0x08, 0x14 }, // *\r
+    { 0x08, 0x08, 0x3e, 0x08, 0x08 }, // +\r
+    { 0x00, 0x50, 0x30, 0x00, 0x00 }, // ,\r
+    { 0x08, 0x08, 0x08, 0x08, 0x08 }, // -\r
+    { 0x00, 0x60, 0x60, 0x00, 0x00 }, // .\r
+    { 0x20, 0x10, 0x08, 0x04, 0x02 }, // /\r
+    { 0x3e, 0x51, 0x49, 0x45, 0x3e }, // 0\r
+    { 0x00, 0x42, 0x7f, 0x40, 0x00 }, // 1\r
+    { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2\r
+    { 0x21, 0x41, 0x45, 0x4b, 0x31 }, // 3\r
+    { 0x18, 0x14, 0x12, 0x7f, 0x10 }, // 4\r
+    { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5\r
+    { 0x3c, 0x4a, 0x49, 0x49, 0x30 }, // 6\r
+    { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7\r
+    { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8\r
+    { 0x06, 0x49, 0x49, 0x29, 0x1e }, // 9\r
+    { 0x00, 0x36, 0x36, 0x00, 0x00 }, // :\r
+    { 0x00, 0x56, 0x36, 0x00, 0x00 }, // ;\r
+    { 0x08, 0x14, 0x22, 0x41, 0x00 }, // <\r
+    { 0x14, 0x14, 0x14, 0x14, 0x14 }, // =\r
+    { 0x00, 0x41, 0x22, 0x14, 0x08 }, // >\r
+    { 0x02, 0x01, 0x51, 0x09, 0x06 }, // ?\r
+    { 0x32, 0x49, 0x79, 0x41, 0x3e }, // @\r
+    { 0x7e, 0x11, 0x11, 0x11, 0x7e }, // A\r
+    { 0x7f, 0x49, 0x49, 0x49, 0x36 }, // B\r
+    { 0x3e, 0x41, 0x41, 0x41, 0x22 }, // C\r
+    { 0x7f, 0x41, 0x41, 0x22, 0x1c }, // D\r
+    { 0x7f, 0x49, 0x49, 0x49, 0x41 }, // E\r
+    { 0x7f, 0x09, 0x09, 0x09, 0x01 }, // F\r
+    { 0x3e, 0x41, 0x49, 0x49, 0x7a }, // G\r
+    { 0x7f, 0x08, 0x08, 0x08, 0x7f }, // H\r
+    { 0x00, 0x41, 0x7f, 0x41, 0x00 }, // I\r
+    { 0x20, 0x40, 0x41, 0x3f, 0x01 }, // J\r
+    { 0x7f, 0x08, 0x14, 0x22, 0x41 }, // K\r
+    { 0x7f, 0x40, 0x40, 0x40, 0x40 }, // L\r
+    { 0x7f, 0x02, 0x0c, 0x02, 0x7f }, // M\r
+    { 0x7f, 0x04, 0x08, 0x10, 0x7f }, // N\r
+    { 0x3e, 0x41, 0x41, 0x41, 0x3e }, // O\r
+    { 0x7f, 0x09, 0x09, 0x09, 0x06 }, // P\r
+    { 0x3e, 0x41, 0x51, 0x21, 0x5e }, // Q\r
+    { 0x7f, 0x09, 0x19, 0x29, 0x46 }, // R\r
+    { 0x46, 0x49, 0x49, 0x49, 0x31 }, // S\r
+    { 0x01, 0x01, 0x7f, 0x01, 0x01 }, // T\r
+    { 0x3f, 0x40, 0x40, 0x40, 0x3f }, // U\r
+    { 0x1f, 0x20, 0x40, 0x20, 0x1f }, // V\r
+    { 0x3f, 0x40, 0x38, 0x40, 0x3f }, // W\r
+    { 0x63, 0x14, 0x08, 0x14, 0x63 }, // X\r
+    { 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y\r
+    { 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z\r
+    { 0x00, 0x7f, 0x41, 0x41, 0x00 }, // [\r
+    { 0x02, 0x04, 0x08, 0x10, 0x20 }, // "\"\r
+    { 0x00, 0x41, 0x41, 0x7f, 0x00 }, // ]\r
+    { 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^\r
+    { 0x40, 0x40, 0x40, 0x40, 0x40 }, // _\r
+    { 0x00, 0x01, 0x02, 0x04, 0x00 }, // `\r
+    { 0x20, 0x54, 0x54, 0x54, 0x78 }, // a\r
+    { 0x7f, 0x48, 0x44, 0x44, 0x38 }, // b\r
+    { 0x38, 0x44, 0x44, 0x44, 0x20 }, // c\r
+    { 0x38, 0x44, 0x44, 0x48, 0x7f }, // d\r
+    { 0x38, 0x54, 0x54, 0x54, 0x18 }, // e\r
+    { 0x08, 0x7e, 0x09, 0x01, 0x02 }, // f\r
+    { 0x0c, 0x52, 0x52, 0x52, 0x3e }, // g\r
+    { 0x7f, 0x08, 0x04, 0x04, 0x78 }, // h\r
+    { 0x00, 0x44, 0x7d, 0x40, 0x00 }, // i\r
+    { 0x20, 0x40, 0x44, 0x3d, 0x00 }, // j\r
+    { 0x7f, 0x10, 0x28, 0x44, 0x00 }, // k\r
+    { 0x00, 0x41, 0x7f, 0x40, 0x00 }, // l\r
+    { 0x7c, 0x04, 0x18, 0x04, 0x78 }, // m\r
+    { 0x7c, 0x08, 0x04, 0x04, 0x78 }, // n\r
+    { 0x38, 0x44, 0x44, 0x44, 0x38 }, // o\r
+    { 0x7c, 0x14, 0x14, 0x14, 0x08 }, // p\r
+    { 0x08, 0x14, 0x14, 0x18, 0x7c }, // q\r
+    { 0x7c, 0x08, 0x04, 0x04, 0x08 }, // r\r
+    { 0x48, 0x54, 0x54, 0x54, 0x20 }, // s\r
+    { 0x04, 0x3f, 0x44, 0x40, 0x20 }, // t\r
+    { 0x3c, 0x40, 0x40, 0x20, 0x7c }, // u\r
+    { 0x1c, 0x20, 0x40, 0x20, 0x1c }, // v\r
+    { 0x3c, 0x40, 0x30, 0x40, 0x3c }, // w\r
+    { 0x44, 0x28, 0x10, 0x28, 0x44 }, // x\r
+    { 0x0c, 0x50, 0x50, 0x50, 0x3c }, // y\r
+    { 0x44, 0x64, 0x54, 0x4c, 0x44 }, // z\r
+    { 0x00, 0x08, 0x36, 0x41, 0x00 }, // {\r
+    { 0x00, 0x00, 0x7f, 0x00, 0x00 }, // |\r
+    { 0x00, 0x41, 0x36, 0x08, 0x00 }, // }\r
+    { 0x02, 0x01, 0x02, 0x04, 0x02 }, // ~\r
+};\r
+\r
+//*****************************************************************************\r
+//\r
+// The sequence of commands used to initialize the SSD0303 controller.  Each\r
+// command is described as follows:  there is a byte specifying the number of\r
+// bytes in the I2C transfer, followed by that many bytes of command data.\r
+//\r
+//*****************************************************************************\r
+static const unsigned char g_pucOSRAMInit[] =\r
+{\r
+    //\r
+    // Turn off the panel\r
+    //\r
+    0x04, 0x80, 0xae, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set lower column address\r
+    //\r
+    0x04, 0x80, 0x04, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set higher column address\r
+    //\r
+    0x04, 0x80, 0x12, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set contrast control register\r
+    //\r
+    0x06, 0x80, 0x81, 0x80, 0x2b, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set segment re-map\r
+    //\r
+    0x04, 0x80, 0xa1, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set display start line\r
+    //\r
+    0x04, 0x80, 0x40, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set display offset\r
+    //\r
+    0x06, 0x80, 0xd3, 0x80, 0x00, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set multiplex ratio\r
+    //\r
+    0x06, 0x80, 0xa8, 0x80, 0x0f, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set the display to normal mode\r
+    //\r
+    0x04, 0x80, 0xa4, 0x80, 0xe3,\r
+\r
+    //\r
+    // Non-inverted display\r
+    //\r
+    0x04, 0x80, 0xa6, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set the page address\r
+    //\r
+    0x04, 0x80, 0xb0, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set COM output scan direction\r
+    //\r
+    0x04, 0x80, 0xc8, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set display clock divide ratio/oscillator frequency\r
+    //\r
+    0x06, 0x80, 0xd5, 0x80, 0x72, 0x80, 0xe3,\r
+\r
+    //\r
+    // Enable mono mode\r
+    //\r
+    0x06, 0x80, 0xd8, 0x80, 0x00, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set pre-charge period\r
+    //\r
+    0x06, 0x80, 0xd9, 0x80, 0x22, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set COM pins hardware configuration\r
+    //\r
+    0x06, 0x80, 0xda, 0x80, 0x12, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set VCOM deslect level\r
+    //\r
+    0x06, 0x80, 0xdb, 0x80, 0x0f, 0x80, 0xe3,\r
+\r
+    //\r
+    // Set DC-DC on\r
+    //\r
+    0x06, 0x80, 0xad, 0x80, 0x8b, 0x80, 0xe3,\r
+\r
+    //\r
+    // Turn on the panel\r
+    //\r
+    0x04, 0x80, 0xaf, 0x80, 0xe3,\r
+};\r
+\r
+//*****************************************************************************\r
+//\r
+// The inter-byte delay required by the SSD0303 OLED controller.\r
+//\r
+//*****************************************************************************\r
+static unsigned long g_ulDelay;\r
+\r
+//*****************************************************************************\r
+//\r
+//! \internal\r
+//!\r
+//! Provide a small delay.\r
+//!\r
+//! \param ulCount is the number of delay loop iterations to perform.\r
+//!\r
+//! Since the SSD0303 controller needs a delay between bytes written to it over\r
+//! the I2C bus, this function provides a means of generating that delay.  It\r
+//! is written in assembly to keep the delay consistent across tool chains,\r
+//! avoiding the need to tune the delay based on the tool chain in use.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+#if defined(ewarm)\r
+static void\r
+OSRAMDelay(unsigned long ulCount)\r
+{\r
+    __asm("    subs    r0, #1\n"\r
+          "    bne     OSRAMDelay\n"\r
+          "    bx      lr");\r
+}\r
+#endif\r
+#if defined(gcc)\r
+static void __attribute__((naked))\r
+OSRAMDelay(unsigned long ulCount)\r
+{\r
+    __asm("    subs    r0, #1\n"\r
+          "    bne     OSRAMDelay\n"\r
+          "    bx      lr");\r
+}\r
+#endif\r
+#if defined(rvmdk) || defined(__ARMCC_VERSION)\r
+__asm void\r
+OSRAMDelay(unsigned long ulCount)\r
+{\r
+    subs    r0, #1;\r
+    bne     OSRAMDelay;\r
+    bx      lr;\r
+}\r
+#endif\r
+\r
+//*****************************************************************************\r
+//\r
+//! \internal\r
+//!\r
+//! Start a transfer to the SSD0303 controller.\r
+//!\r
+//! \param ucChar is the first byte to be written to the controller.\r
+//!\r
+//! This function will start a transfer to the SSD0303 controller via the I2C\r
+//! bus.\r
+//!\r
+//! The data is written in a polled fashion; this function will not return\r
+//! until the byte has been written to the controller.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+static void\r
+OSRAMWriteFirst(unsigned char ucChar)\r
+{\r
+    //\r
+    // Set the slave address.\r
+    //\r
+    I2CMasterSlaveAddrSet(I2C_MASTER_BASE, SSD0303_ADDR, false);\r
+\r
+    //\r
+    // Write the first byte to the controller.\r
+    //\r
+    I2CMasterDataPut(I2C_MASTER_BASE, ucChar);\r
+\r
+    //\r
+    // Start the transfer.\r
+    //\r
+    I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_START);\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! \internal\r
+//!\r
+//! Write a byte to the SSD0303 controller.\r
+//!\r
+//! \param ucChar is the byte to be transmitted to the controller.\r
+//!\r
+//! This function continues a transfer to the SSD0303 controller by writing\r
+//! another byte over the I2C bus.  This must only be called after calling\r
+//! OSRAMWriteFirst(), but before calling OSRAMWriteFinal().\r
+//!\r
+//! The data is written in a polled faashion; this function will not return\r
+//! until the byte has been written to the controller.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+static void\r
+OSRAMWriteByte(unsigned char ucChar)\r
+{\r
+    //\r
+    // Wait until the current byte has been transferred.\r
+    //\r
+    while(I2CMasterIntStatus(I2C_MASTER_BASE, false) == 0)\r
+    {\r
+    }\r
+\r
+    //\r
+    // Provide the required inter-byte delay.\r
+    //\r
+    OSRAMDelay(g_ulDelay);\r
+\r
+    //\r
+    // Write the next byte to the controller.\r
+    //\r
+    I2CMasterDataPut(I2C_MASTER_BASE, ucChar);\r
+\r
+    //\r
+    // Continue the transfer.\r
+    //\r
+    I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! \internal\r
+//!\r
+//! Write a sequence of bytes to the SSD0303 controller.\r
+//!\r
+//! This function continues a transfer to the SSD0303 controller by writing a\r
+//! sequence of bytes over the I2C bus.  This must only be called after calling\r
+//! OSRAMWriteFirst(), but before calling OSRAMWriteFinal().\r
+//!\r
+//! The data is written in a polled fashion; this function will not return\r
+//! until the entire byte sequence has been written to the controller.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+static void\r
+OSRAMWriteArray(const unsigned char *pucBuffer, unsigned long ulCount)\r
+{\r
+    //\r
+    // Loop while there are more bytes left to be transferred.\r
+    //\r
+    while(ulCount != 0)\r
+    {\r
+        //\r
+        // Wait until the current byte has been transferred.\r
+        //\r
+        while(I2CMasterIntStatus(I2C_MASTER_BASE, false) == 0)\r
+        {\r
+        }\r
+\r
+        //\r
+        // Provide the required inter-byte delay.\r
+        //\r
+        OSRAMDelay(g_ulDelay);\r
+\r
+        //\r
+        // Write the next byte to the controller.\r
+        //\r
+        I2CMasterDataPut(I2C_MASTER_BASE, *pucBuffer++);\r
+        ulCount--;\r
+\r
+        //\r
+        // Continue the transfer.\r
+        //\r
+        I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);\r
+    }\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! \internal\r
+//!\r
+//! Finish a transfer to the SSD0303 controller.\r
+//!\r
+//! \param ucChar is the final byte to be written to the controller.\r
+//!\r
+//! This function will finish a transfer to the SSD0303 controller via the I2C\r
+//! bus.  This must only be called after calling OSRAMWriteFirst().\r
+//!\r
+//! The data is written in a polled fashion; this function will not return\r
+//! until the byte has been written to the controller.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+static void\r
+OSRAMWriteFinal(unsigned char ucChar)\r
+{\r
+    //\r
+    // Wait until the current byte has been transferred.\r
+    //\r
+    while(I2CMasterIntStatus(I2C_MASTER_BASE, false) == 0)\r
+    {\r
+    }\r
+\r
+    //\r
+    // Provide the required inter-byte delay.\r
+    //\r
+    OSRAMDelay(g_ulDelay);\r
+\r
+    //\r
+    // Write the final byte to the controller.\r
+    //\r
+    I2CMasterDataPut(I2C_MASTER_BASE, ucChar);\r
+\r
+    //\r
+    // Finish the transfer.\r
+    //\r
+    I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);\r
+\r
+    //\r
+    // Wait until the final byte has been transferred.\r
+    //\r
+    while(I2CMasterIntStatus(I2C_MASTER_BASE, false) == 0)\r
+    {\r
+    }\r
+\r
+    //\r
+    // Provide the required inter-byte delay.\r
+    //\r
+    OSRAMDelay(g_ulDelay);\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! Clears the OLED display.\r
+//!\r
+//! This function will clear the display.  All pixels in the display will be\r
+//! turned off.\r
+//!\r
+//! This function is contained in <tt>osram96x16.c</tt>, with\r
+//! <tt>osram96x16.h</tt> containing the API definition for use by\r
+//! applications.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+void\r
+OSRAMClear(void)\r
+{\r
+    static const unsigned char pucRow1[] =\r
+    {\r
+        0xb0, 0x80, 0x04, 0x80, 0x12, 0x40\r
+    };\r
+    static const unsigned char pucRow2[] =\r
+    {\r
+        0xb1, 0x80, 0x04, 0x80, 0x12, 0x40\r
+    };\r
+    unsigned long ulIdx;\r
+\r
+    //\r
+    // Move the display cursor to the first column of the first row.\r
+    //\r
+    OSRAMWriteFirst(0x80);\r
+    OSRAMWriteArray(pucRow1, sizeof(pucRow1));\r
+\r
+    //\r
+    // Fill this row with zeros.\r
+    //\r
+    for(ulIdx = 0; ulIdx < 95; ulIdx++)\r
+    {\r
+        OSRAMWriteByte(0x00);\r
+    }\r
+    OSRAMWriteFinal(0x00);\r
+\r
+    //\r
+    // Move the display cursor to the first column of the second row.\r
+    //\r
+    OSRAMWriteFirst(0x80);\r
+    OSRAMWriteArray(pucRow2, sizeof(pucRow2));\r
+\r
+    //\r
+    // Fill this row with zeros.\r
+    //\r
+    for(ulIdx = 0; ulIdx < 95; ulIdx++)\r
+    {\r
+        OSRAMWriteByte(0x00);\r
+    }\r
+    OSRAMWriteFinal(0x00);\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! Displays a string on the OLED display.\r
+//!\r
+//! \param pcStr is a pointer to the string to display.\r
+//! \param ulX is the horizontal position to display the string, specified in\r
+//! columns from the left edge of the display.\r
+//! \param ulY is the vertical position to display the string, specified in\r
+//! eight scan line blocks from the top of the display (i.e. only 0 and 1 are\r
+//! valid).\r
+//!\r
+//! This function will draw a string on the display.  Only the ASCII characters\r
+//! between 32 (space) and 126 (tilde) are supported; other characters will\r
+//! result in random data being draw on the display (based on whatever appears\r
+//! before/after the font in memory).  The font is mono-spaced, so characters\r
+//! such as "i" and "l" have more white space around them than characters such\r
+//! as "m" or "w".\r
+//!\r
+//! If the drawing of the string reaches the right edge of the display, no more\r
+//! characters will be drawn.  Therefore, special care is not required to avoid\r
+//! supplying a string that is "too long" to display.\r
+//!\r
+//! This function is contained in <tt>osram96x16.c</tt>, with\r
+//! <tt>osram96x16.h</tt> containing the API definition for use by\r
+//! applications.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+void\r
+OSRAMStringDraw(const char *pcStr, unsigned long ulX, unsigned long ulY)\r
+{\r
+    //\r
+    // Check the arguments.\r
+    //\r
+    ASSERT(ulX < 96);\r
+    ASSERT(ulY < 2);\r
+\r
+    //\r
+    // Move the display cursor to the requested position on the display.\r
+    //\r
+    OSRAMWriteFirst(0x80);\r
+    OSRAMWriteByte((ulY == 0) ? 0xb0 : 0xb1);\r
+    OSRAMWriteByte(0x80);\r
+    OSRAMWriteByte((ulX + 36) & 0x0f);\r
+    OSRAMWriteByte(0x80);\r
+    OSRAMWriteByte(0x10 | (((ulX + 36) >> 4) & 0x0f));\r
+    OSRAMWriteByte(0x40);\r
+\r
+    //\r
+    // Loop while there are more characters in the string.\r
+    //\r
+    while(*pcStr != 0)\r
+    {\r
+        //\r
+        // See if there is enough space on the display for this entire\r
+        // character.\r
+        //\r
+        if(ulX <= 90)\r
+        {\r
+            //\r
+            // Write the contents of this character to the display.\r
+            //\r
+            OSRAMWriteArray(g_pucFont[*pcStr - ' '], 5);\r
+\r
+            //\r
+            // See if this is the last character to display (either because the\r
+            // right edge has been reached or because there are no more\r
+            // characters).\r
+            //\r
+            if((ulX == 90) || (pcStr[1] == 0))\r
+            {\r
+                //\r
+                // Write the final column of the display.\r
+                //\r
+                OSRAMWriteFinal(0x00);\r
+\r
+                //\r
+                // The string has been displayed.\r
+                //\r
+                return;\r
+            }\r
+\r
+            //\r
+            // Write the inter-character padding column.\r
+            //\r
+            OSRAMWriteByte(0x00);\r
+        }\r
+        else\r
+        {\r
+            //\r
+            // Write the portion of the character that will fit onto the\r
+            // display.\r
+            //\r
+            OSRAMWriteArray(g_pucFont[*pcStr - ' '], 95 - ulX);\r
+            OSRAMWriteFinal(g_pucFont[*pcStr - ' '][95 - ulX]);\r
+\r
+            //\r
+            // The string has been displayed.\r
+            //\r
+            return;\r
+        }\r
+\r
+        //\r
+        // Advance to the next character.\r
+        //\r
+        pcStr++;\r
+\r
+        //\r
+        // Increment the X coordinate by the six columns that were just\r
+        // written.\r
+        //\r
+        ulX += 6;\r
+    }\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! Displays an image on the OLED display.\r
+//!\r
+//! \param pucImage is a pointer to the image data.\r
+//! \param ulX is the horizontal position to display this image, specified in\r
+//! columns from the left edge of the display.\r
+//! \param ulY is the vertical position to display this image, specified in\r
+//! eight scan line blocks from the top of the display (i.e. only 0 and 1 are\r
+//! valid).\r
+//! \param ulWidth is the width of the image, specified in columns.\r
+//! \param ulHeight is the height of the image, specified in eight row blocks\r
+//! (i.e. only 1 and 2 are valid).\r
+//!\r
+//! This function will display a bitmap graphic on the display.  The image to\r
+//! be displayed must be a multiple of eight scan lines high (i.e. one row) and\r
+//! will be drawn at a vertical position that is a multiple of eight scan lines\r
+//! (i.e. scan line zero or scan line eight, corresponding to row zero or row\r
+//! one).\r
+//!\r
+//! The image data is organized with the first row of image data appearing left\r
+//! to right, followed immediately by the second row of image data.  Each byte\r
+//! contains the data for the eight scan lines of the column, with the top scan\r
+//! line being in the least significant bit of the byte and the bottom scan\r
+//! line being in the most significant bit of the byte.\r
+//!\r
+//! For example, an image four columns wide and sixteen scan lines tall would\r
+//! be arranged as follows (showing how the eight bytes of the image would\r
+//! appear on the display):\r
+//!\r
+//! \verbatim\r
+//!     +-------+  +-------+  +-------+  +-------+\r
+//!     |   | 0 |  |   | 0 |  |   | 0 |  |   | 0 |\r
+//!     | B | 1 |  | B | 1 |  | B | 1 |  | B | 1 |\r
+//!     | y | 2 |  | y | 2 |  | y | 2 |  | y | 2 |\r
+//!     | t | 3 |  | t | 3 |  | t | 3 |  | t | 3 |\r
+//!     | e | 4 |  | e | 4 |  | e | 4 |  | e | 4 |\r
+//!     |   | 5 |  |   | 5 |  |   | 5 |  |   | 5 |\r
+//!     | 0 | 6 |  | 1 | 6 |  | 2 | 6 |  | 3 | 6 |\r
+//!     |   | 7 |  |   | 7 |  |   | 7 |  |   | 7 |\r
+//!     +-------+  +-------+  +-------+  +-------+\r
+//!\r
+//!     +-------+  +-------+  +-------+  +-------+\r
+//!     |   | 0 |  |   | 0 |  |   | 0 |  |   | 0 |\r
+//!     | B | 1 |  | B | 1 |  | B | 1 |  | B | 1 |\r
+//!     | y | 2 |  | y | 2 |  | y | 2 |  | y | 2 |\r
+//!     | t | 3 |  | t | 3 |  | t | 3 |  | t | 3 |\r
+//!     | e | 4 |  | e | 4 |  | e | 4 |  | e | 4 |\r
+//!     |   | 5 |  |   | 5 |  |   | 5 |  |   | 5 |\r
+//!     | 4 | 6 |  | 5 | 6 |  | 6 | 6 |  | 7 | 6 |\r
+//!     |   | 7 |  |   | 7 |  |   | 7 |  |   | 7 |\r
+//!     +-------+  +-------+  +-------+  +-------+\r
+//! \endverbatim\r
+//!\r
+//! This function is contained in <tt>osram96x16.c</tt>, with\r
+//! <tt>osram96x16.h</tt> containing the API definition for use by\r
+//! applications.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+void\r
+OSRAMImageDraw(const unsigned char *pucImage, unsigned long ulX,\r
+               unsigned long ulY, unsigned long ulWidth,\r
+               unsigned long ulHeight)\r
+{\r
+    //\r
+    // Check the arguments.\r
+    //\r
+    ASSERT(ulX < 96);\r
+    ASSERT(ulY < 2);\r
+    ASSERT((ulX + ulWidth) <= 96);\r
+    ASSERT((ulY + ulHeight) <= 2);\r
+\r
+    //\r
+    // The first 36 columns of the LCD buffer are not displayed, so increment\r
+    // the X coorddinate by 36 to account for the non-displayed frame buffer\r
+    // memory.\r
+    //\r
+    ulX += 36;\r
+\r
+    //\r
+    // Loop while there are more rows to display.\r
+    //\r
+    while(ulHeight--)\r
+    {\r
+        //\r
+        // Write the starting address within this row.\r
+        //\r
+        OSRAMWriteFirst(0x80);\r
+        OSRAMWriteByte((ulY == 0) ? 0xb0 : 0xb1);\r
+        OSRAMWriteByte(0x80);\r
+        OSRAMWriteByte(ulX & 0x0f);\r
+        OSRAMWriteByte(0x80);\r
+        OSRAMWriteByte(0x10 | ((ulX >> 4) & 0x0f));\r
+        OSRAMWriteByte(0x40);\r
+\r
+        //\r
+        // Write this row of image data.\r
+        //\r
+        OSRAMWriteArray(pucImage, ulWidth - 1);\r
+        OSRAMWriteFinal(pucImage[ulWidth - 1]);\r
+\r
+        //\r
+        // Advance to the next row of the image.\r
+        //\r
+        pucImage += ulWidth;\r
+        ulY++;\r
+    }\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! Initialize the OLED display.\r
+//!\r
+//! \param bFast is a boolean that is \e true if the I2C interface should be\r
+//! run at 400 kbps and \e false if it should be run at 100 kbps.\r
+//!\r
+//! This function initializes the I2C interface to the OLED display and\r
+//! configures the SSD0303 controller on the panel.\r
+//!\r
+//! This function is contained in <tt>osram96x16.c</tt>, with\r
+//! <tt>osram96x16.h</tt> containing the API definition for use by\r
+//! applications.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+void\r
+OSRAMInit(tBoolean bFast)\r
+{\r
+    unsigned long ulIdx;\r
+\r
+    //\r
+    // Enable the I2C and GPIO port B blocks as they are needed by this driver.\r
+    //\r
+    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C);\r
+    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);\r
+\r
+    //\r
+    // Configure the I2C SCL and SDA pins for I2C operation.\r
+    //\r
+    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_2 | GPIO_PIN_3);\r
+\r
+    //\r
+    // Initialize the I2C master.\r
+    //\r
+    I2CMasterInit(I2C_MASTER_BASE, bFast);\r
+\r
+    //\r
+    // Compute the inter-byte delay for the SSD0303 controller.  This delay is\r
+    // dependent upon the I2C bus clock rate; the slower the clock the longer\r
+    // the delay required.\r
+    //\r
+    // The derivation of this formula is based on a measured delay of\r
+    // OSRAMDelay(1700) for a 100 kHz I2C bus with the CPU running at 50 MHz\r
+    // (referred to as C).  To scale this to the delay for a different CPU\r
+    // speed (since this is just a CPU-based delay loop) is:\r
+    //\r
+    //           f(CPU)\r
+    //     C * ----------\r
+    //         50,000,000\r
+    //\r
+    // To then scale this to the actual I2C rate (since it won't always be\r
+    // precisely 100 kHz):\r
+    //\r
+    //           f(CPU)     100,000\r
+    //     C * ---------- * -------\r
+    //         50,000,000    f(I2C)\r
+    //\r
+    // This equation will give the inter-byte delay required for any\r
+    // configuration of the I2C master.  But, as arranged it is impossible to\r
+    // directly compute in 32-bit arithmetic (without loosing a lot of\r
+    // accuracy).  So, the equation is simplified.\r
+    //\r
+    // Since f(I2C) is generated by dividing down from f(CPU), replace it with\r
+    // the equivalent (where TPR is the value programmed into the Master Timer\r
+    // Period Register of the I2C master, with the 1 added back):\r
+    //\r
+    //                        100,000\r
+    //           f(CPU)       -------\r
+    //     C * ---------- *    f(CPU)\r
+    //         50,000,000   ------------\r
+    //                      2 * 10 * TPR\r
+    //\r
+    // Inverting the dividend in the last term:\r
+    //\r
+    //           f(CPU)     100,000 * 2 * 10 * TPR\r
+    //     C * ---------- * ----------------------\r
+    //         50,000,000          f(CPU)\r
+    //\r
+    // The f(CPU) now cancels out.\r
+    //\r
+    //         100,000 * 2 * 10 * TPR\r
+    //     C * ----------------------\r
+    //               50,000,000\r
+    //\r
+    // Since there are no clock frequencies left in the equation, this equation\r
+    // also works for 400 kHz bus operation as well, since the 100,000 in the\r
+    // numerator becomes 400,000 but C is 1/4, which cancel out each other.\r
+    // Reducing the constants gives:\r
+    //\r
+    //         TPR              TPR             TPR\r
+    //     C * ---   =   1700 * ---   =   340 * ---   = 68 * TPR\r
+    //         25               25               5\r
+    //\r
+    // Note that the constant C is actually a bit larger than it needs to be in\r
+    // order to provide some safety margin.\r
+    //\r
+    g_ulDelay = 68 * (HWREG(I2C_MASTER_BASE + I2C_MASTER_O_TPR) + 1);\r
+\r
+    //\r
+    // Initialize the SSD0303 controller.  Loop through the initialization\r
+    // sequence doing a single I2C transfer for each command.\r
+    //\r
+    for(ulIdx = 0; ulIdx < sizeof(g_pucOSRAMInit);\r
+        ulIdx += g_pucOSRAMInit[ulIdx] + 1)\r
+    {\r
+        //\r
+        // Send this command.\r
+        //\r
+        OSRAMWriteFirst(g_pucOSRAMInit[ulIdx + 1]);\r
+        OSRAMWriteArray(g_pucOSRAMInit + ulIdx + 2, g_pucOSRAMInit[ulIdx] - 2);\r
+        OSRAMWriteFinal(g_pucOSRAMInit[ulIdx + g_pucOSRAMInit[ulIdx]]);\r
+    }\r
+\r
+    //\r
+    // Clear the frame buffer.\r
+    //\r
+    OSRAMClear();\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! Turns on the OLED display.\r
+//!\r
+//! This function will turn on the OLED display, causing it to display the\r
+//! contents of its internal frame buffer.\r
+//!\r
+//! This function is contained in <tt>osram96x16.c</tt>, with\r
+//! <tt>osram96x16.h</tt> containing the API definition for use by\r
+//! applications.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+void\r
+OSRAMDisplayOn(void)\r
+{\r
+    unsigned long ulIdx;\r
+\r
+    //\r
+    // Re-initialize the SSD0303 controller.  Loop through the initialization\r
+    // sequence doing a single I2C transfer for each command.\r
+    //\r
+    for(ulIdx = 0; ulIdx < sizeof(g_pucOSRAMInit);\r
+        ulIdx += g_pucOSRAMInit[ulIdx] + 1)\r
+    {\r
+        //\r
+        // Send this command.\r
+        //\r
+        OSRAMWriteFirst(g_pucOSRAMInit[ulIdx + 1]);\r
+        OSRAMWriteArray(g_pucOSRAMInit + ulIdx + 2, g_pucOSRAMInit[ulIdx] - 2);\r
+        OSRAMWriteFinal(g_pucOSRAMInit[ulIdx + g_pucOSRAMInit[ulIdx]]);\r
+    }\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+//! Turns off the OLED display.\r
+//!\r
+//! This function will turn off the OLED display.  This will stop the scanning\r
+//! of the panel and turn off the on-chip DC-DC converter, preventing damage to\r
+//! the panel due to burn-in (it has similar characters to a CRT in this\r
+//! respect).\r
+//!\r
+//! This function is contained in <tt>osram96x16.c</tt>, with\r
+//! <tt>osram96x16.h</tt> containing the API definition for use by\r
+//! applications.\r
+//!\r
+//! \return None.\r
+//\r
+//*****************************************************************************\r
+void\r
+OSRAMDisplayOff(void)\r
+{\r
+    //\r
+    // Turn off the DC-DC converter and the display.\r
+    //\r
+    OSRAMWriteFirst(0x80);\r
+    OSRAMWriteByte(0xae);\r
+    OSRAMWriteByte(0x80);\r
+    OSRAMWriteByte(0xad);\r
+    OSRAMWriteByte(0x80);\r
+    OSRAMWriteFinal(0x8a);\r
+}\r
+\r
+//*****************************************************************************\r
+//\r
+// Close the Doxygen group.\r
+//! @}\r
+//\r
+//*****************************************************************************\r