This driver is meant for the monochrome LCD display (model no: LS013B4DN04) from Sharp; but it should be easily adaptable to other Sharp displays.
Diff: SharpLCD.hpp
- Revision:
- 0:62d7cfac67ca
- Child:
- 1:ffc1d1d55581
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SharpLCD.hpp Wed Jul 23 10:40:35 2014 +0000 @@ -0,0 +1,345 @@ +/* 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 "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); + + /** + * \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 _displayEnablePin, PinName mosi, PinName miso_unused, PinName sclk, PinName _unused=NC) : + displayEnable(_displayEnablePin), chipSelect(p8), spi(mosi, miso_unused, sclk, _unused) { + displayEnable = 0; + chipSelect = 0; + 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) { + return (uint8_t)(__RBIT(byte) >> 24); + } + + /** + * \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__ */