This driver is meant for the monochrome LCD display (model no: LS013B4DN04) from Sharp; but it should be easily adaptable to other Sharp displays.

Dependents:   sharpLCD-demo

SharpLCD.hpp

Committer:
rgrover1
Date:
2014-08-07
Revision:
3:761d0f489b61
Parent:
2:270ee57c0367

File content as of revision 3:761d0f489b61:

/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed.h"
#include "core_cmInstr.h"
#include "font.h"

#ifndef __SHARP_LCD_HPP__
#define __SHARP_LCD_HPP__

/**
 * This driver is meant for the monochrome LCD display (model
 * no: LS013B4DN04) from Sharp.
 *
 * The LCD has the following pixel dimensions: width=96pixels,
 * height=96pixels. This is a monochrome display with an inbuilt
 * memory of 1 bit per pixel. If a pixel-bit is set to one, the
 * corresponding pixel will show as black.
 *
 * The LCD memory is accessible to the micro-controller only through a
 * serial interface; and <i>only for write operations</i>. It is
 * necessary for the application to maintain its own frame-buffer
 * memory in the micro-controller's SRAM (see fb_alloc())---the
 * application is not restricted to a single framebuffer; if SRAM size
 * permits, multiple buffers may be employed. In order to update the
 * LCD, the application first draws (bitmaps or text) into some
 * framebuffer memory, and then flushes the framebuffer to the LCD
 * over the serial interface.
 *
 * Here's some sample code to drive the LCD display:
 *
 *     DigitalOut led1(LED1);
 *     SharpLCD lcd(p9, MBED_SPI0);
 *
 *     uint8_t framebuffer[SharpLCD::SIZEOF_FRAMEBUFFER_FOR_ALLOC];
 *
 *     int main(void)
 *     {
 *         SharpLCD::FrameBuffer fb(framebuffer);
 *
 *         lcd.enableDisplay();
 *         lcd.clear();
 *         fb.printString(lookupFontFace("DejaVu Serif", 8),
 *                        20,
 *                        40,
 *                        BLACK,
 *                        "Rohit");
 *         lcd.drawFrameBuffer(fb);
 *
 *         led1 = 1;
 *         while (true) {
 *             wait(0.5);
 *             led1 = !led1;
 *         }
 *     }
 */

class SharpLCD {
public:
    class FrameBuffer {
    public:
        /**
         * \brief initialize the hardware dependent component of a given
         * framebuffer; and set it up to show all-white.
         *
         * \note This does not update the LCD automatically; it only
         * initializes a framebuffer.
         *
         * \param[in] fb
         *     A memory buffer to initialize.
         */
        FrameBuffer(uint8_t *fb);

        /**
         * Clear the framebuffer to prepare for new drawing. The cleared buffer
         * still needs to be drawn on the LCD if clearning the LCD is desired.
         */
        void clear(void);

        /**
         * \brief Copy over a bitmap to a specified location into the framebuffer.
         *
         * This is the main work-horse for displaying bitmaps on the LCD. We
         * only support mono-chrome bitmaps with an encoding of 1 for white
         * and 0 for black. We have rendering tools to convert a bitmap into
         * the required encoding.

         * \note The placement of the target bitmap is limited to the LCD's
         * boundary--otherwise this routine fails.
         *
         * In case you are wondering, 'blit' stands for Block Image Transfer.
         *
         * Sample code:
         * <pre>
                fb_bitBlit(fb,
                           (const uint8_t *)pixel_data,
                           width,
                           height,
                           0, // posx
                           0  // posy
                           );
                lcd_drawFrameBuffer(fb);
        </pre>
         */
        void bitBlit(const uint8_t *bitmap,
                     unsigned int   width,  /*!< width of the bitmap */
                     unsigned int   height, /*!< height of the bitmap */
                     unsigned int   posx,   /*!< x-offset for the
                                             * placement of the top-left
                                             * corner of the bitmap
                                             * w.r.t. the top-left
                                             * corner of the screen */
                     unsigned int   posy    /*!< y-offset for the
                                             * placement of the top-left
                                             * corner of the bitmap
                                             * w.r.t. the top-left
                                             * corner of the screen */
            );

        /*
         * \brief Fetch a byte (8-bit pixel sequence) from a given scan-line
         * in the framebuffer.
         *
         * The scan-line is identified by the row; and pixels are grouped into
         * 8-bit bytes within a row.
         *
         * \note This function is declared inline for a faster implementation.
         *
         * \param[in] framebuffer
         *     The framebuffer to fetch the byte from.
         *
         * \param[in] row
         *     The row index of the scan line.
         *
         * \param[in] byteIndex
         *     The pixel-index expressed as a byte-index.
         */
        uint8_t
        getRowByte(unsigned int row, unsigned int byteIndex) {
            return buffer[rowColToIndex(row, byteIndex)];
        }

        /*
         * \brief Set a byte (8-bit pixel sequence) for a given scan-line in
         * the framebuffer.
         *
         * The scan-line is identified by the row; and pixels are grouped into
         * 8-bit bytes within a row.
         *
         * \note This function is declared inline for a faster implementation.
         *
         * \param[in] framebuffer
         *     The framebuffer to set the byte into.
         *
         * \param[in] row
         *     The row index of the scan line.
         *
         * \param[in] byteIndex
         *     The pixel-index expressed as a byte-index.
         *
         * \param[in] pixels
         *     The actual 8 pixels to set.
         */
        void
        setRowByte(unsigned int row, unsigned int byteIndex, uint8_t pixels) {
            buffer[rowColToIndex(row, byteIndex)] = pixels;
        }

        /**
         * \brief The printf function for the frameBuffer.
         *
         * This can be used to render strings in a given
         * font-face. Internally, it uses fb_bitBlit to bilt the glyphs onto a
         * framebuffer. Currently, since bitBlit doesn't handle the case where
         * a bitmap exceeds the framebuffer's boundary, you must be very
         * careful about the placement of the text string. Later, when
         * fb_bitBlit is able to handle bitmaps which fall outside the LCD's
         * boundary, the rendered text may be clipped if it doesn't fit the
         * frame.
         *
         * \param[in] face
         *     The font-face to be used for rendering the text.
         *
         * \param[in] baselineX
         *     The X-offset from the left corner of the screen of the starting
         *     pen position; this defines the X-coordinate of the baseline.
         *
         * \param[in] baselineY
         *     The Y-offset from the top corner of the screen of the starting
         *     pen position; this defines the Y-coordinate of the baseline.
         *
         * \param[in] fgColor
         *     The foreground colour.
         *
         * \param[in] string
         *     The text to be rendered.
         *
         * Sample code:
         * <pre>
         *      face = lookupFontFace("DejaVu Serif", 9);
         *      if (face == NULL) {
         *          TRACE_FATAL("failed to find face for DejaVu Serif; 10\n");
         *      }
         *      fb_printString(fb,
         *                     face,
         *                     90,      // baselineX
         *                     140,     // baselineY
         *                     BLACK,   // foregroundColor
         *                     "Hello Mr. Obama!");
         *      lcd_drawFrameBuffer(fb);
         * </pre>
         */
        void printString(const font_face_t *face,
                         unsigned short     baselineX,
                         unsigned short     baselineY,
                         font_color_t       fgColor,
                         const char        *string);

        const uint8_t *getBuffer(void) const {
            return (buffer);
        }

        uint8_t *getBuffer(void) {
            return (buffer);
        }

    private:
        unsigned rowColToIndex(unsigned row, unsigned col) {
            return (row * LCD_FRAMEBUFFER_SIZEOF_SCAN_LINE) + LCD_FRAMEBUFFER_SIZEOF_SCAN_LINE_METADATA + col;
        }

    private:
        uint8_t *const buffer;
    };

public:
    SharpLCD(PinName enable, PinName cs, PinName mosi, PinName miso_unused, PinName sclk, PinName _unused = NC) :
        displayEnable(enable, 0), chipSelect(cs, 0), spi(mosi, miso_unused, sclk, _unused) {
        spi.frequency(1000000);
        spi.format(8, 0);
    };

    /**
     * \brief Turn on the LCD's display.
     *
     * \note Updates to the LCD's memory won't show up on the display
     * until the display is enabled through this function.
     */
    void enableDisplay(void);

    /**
     * \brief Turn off the LCD's display---i.e. make it go blank.
     *
     * \note The LCD will retain its memory even when the display is disabled.
     *
     * This is different from re-initializing the LCD's display, since it does
     * not affect the LCD memory. When the display is re-enabled, the LCD
     * will show the contents of its memory.
     */
    void disableDisplay(void);

    /**
     * \brief Clear the LCD's display
     *
     * Write all-white to the LCD's memory.  If a frameBuffer is passed in
     * then it is re-initialized as well; otherwise this function does not
     * operate on any global frame-buffer and updating any
     * application-specific frameBuffer is still the application's
     * responsibility.
     */
    void clear(void);

    static uint8_t bitReverse8(uint8_t byte) {
        #if (__CORTEX_M >= 0x03)
            return (uint8_t)(__RBIT(byte) >> 24);
        #else /* #if (__CORTEX_M < 0x03) */
            uint8_t rev8 = 0;

            for (unsigned i = 0; i < 8; i++) {
                if (byte & (1 << i)) {
                    rev8 |= 1 << (7 - i);
                }
            }

            return rev8;
        #endif /* #if (__CORTEX_M >= 0x03) */
    }

    /**
     * \brief Update LCD using a given framebuffer.
     *
     * The entire contents of the framebuffer will be DMA'd to the LCD;
     * the calling thread will loose the CPU during the transfer, but
     * other threads may remain active in that duration.
     *
     * \param[in] fb
     *     The frame buffer to send to the LCD hardware.
     */
    void drawFrameBuffer(const FrameBuffer &fb);

    /**
     * Toggle the VCOM mode of the LCD; it is recommended to trigger this
     * periodically. Check the datasheet.
     */
    void toggleVCOM(void);

private:
    /**
     * Helper function to write out a buffer onto the LCD's SPI channel.
     */
    void writeBuffer(const uint8_t *buffer, unsigned len);

public:
    static const unsigned LCD_WIDTH  = 96;  ///< Constant defining the LCD's geometry.
    static const unsigned LCD_HEIGHT = 96;  ///< Constant defining the LCD's geometry.
    static const unsigned LCD_END_OF_DUMMY_SIZE = 2;
    static const unsigned LCD_FRAMEBUFFER_SIZEOF_SCAN_LINE_METADATA =
        (1 + /* mode byte in SPI update command */
         1   /* addr byte in SPI update command */);
    static const unsigned LCD_FRAMEBUFFER_SIZEOF_SCAN_LINE =
        (LCD_FRAMEBUFFER_SIZEOF_SCAN_LINE_METADATA + (LCD_WIDTH / 8));

    static const unsigned SIZEOF_FRAMEBUFFER = (LCD_HEIGHT * LCD_FRAMEBUFFER_SIZEOF_SCAN_LINE);
    static const unsigned SIZEOF_FRAMEBUFFER_FOR_ALLOC = SIZEOF_FRAMEBUFFER + LCD_END_OF_DUMMY_SIZE;

private:
    /* Constants for the LCD's command protocol */
    static const uint8_t M0_FLAG = 0x80;
    static const uint8_t M1_FLAG = 0x40;
    static const uint8_t M2_FLAG = 0x20;
    static const uint8_t DUMMY8  = 0x00;

private:
    DigitalOut displayEnable;
    DigitalOut chipSelect;
    SPI        spi;
};

inline void
SharpLCD::enableDisplay(void) {
    displayEnable = 1;
}

inline void
SharpLCD::disableDisplay(void) {
    displayEnable = 0;
}

#endif /* #ifndef __SHARP_LCD_HPP__ */