/***************************************************************************//** * @file * @brief Dot matrix display Direct Driver for TFT SSD2119 "Generic" mode * @author Energy Micro AS * @version 2.0.1 ******************************************************************************* * @section License * (C) Copyright 2012 Energy Micro AS, http://www.energymicro.com ******************************************************************************* * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * 4. The source and compiled code may only be used on Energy Micro "EFM32" * microcontrollers and "EFR4" radios. * * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no * obligation to support this Software. Energy Micro AS is providing the * Software "AS IS", with no express or implied warranties of any kind, * including, but not limited to, any implied warranties of merchantability * or fitness for any particular purpose or warranties against infringement * of any proprietary rights of a third party. * * Energy Micro AS will not be liable for any consequential, incidental, or * special damages, or any other relief, or for any claim by any third party, * arising from your use of this Software. * *****************************************************************************/ #include #include "graphics/em_types.h" #include "dmdif_ssd2119_ebi.h" #include "dmd_ssd2119_registers.h" #include "dmd_ssd2119.h" #include "efm32.h" #include "em_usart.h" #include "em_cmu.h" #include "em_gpio.h" #include "tftspi.h" /** Dimensions of the display */ DMD_DisplayGeometry dimensions = { .xSize = 320, .ySize = 240, .xClipStart = 0, .yClipStart = 0, .clipWidth = 320, .clipHeight = 240, }; /** FramePointer base address */ static volatile uint16_t *frameBuffer; /* Local variables */ static uint32_t initialized = 0; static uint16_t rcDriverOutputControl = 0; /* Local function prototypes */ static uint16_t colorTransform24ToRGB565(uint8_t red, uint8_t green, uint8_t blue); #if 0 static void colorTransformRGB565To24bpp(uint16_t color, uint8_t *red, uint8_t *green, uint8_t *blue); #endif /**************************************************************************//** * @brief * Transforms a 24bpp pixel data into an RGB565 pixel * * @param red * 8-bit red component of the pixel * @param green * 8-bit green component of the pixel * @param blue * 8-bit blue component of the pixel * @return * 16-bit RGB565 value of pixel ******************************************************************************/ static uint16_t colorTransform24ToRGB565(uint8_t red, uint8_t green, uint8_t blue) { /* Transform each color into 6 bits by dropping the 2 LSB */ red = red >> 3; green = green >> 2; blue = blue >> 3; /* Put it together to one 18bpp color number */ return (red << 11) | (green << 5) | blue; } /**************************************************************************//** * @brief * Transforms an 16-bit RGB565 pixel into a 24bpp pixel * * @param[in] color * RGB565 16-bit color pixel * @param red * return value for red component of 24bpp pixel * @param green * return value for green component of 24bpp pixel * @param blue * return value for blue component of 24bpp pixel ******************************************************************************/ #if 0 static void colorTransformRGB565To24bpp(uint16_t color, uint8_t *red, uint8_t *green, uint8_t *blue) { /* Get the individual colors out of the 18bpp number */ uint8_t redValue = (color & 0x0003F000) >> 12; uint8_t greenValue = (color & 0x00000FC0) >> 6; uint8_t blueValue = (color & 0x0000003F); /* Convert each color to 8-bit */ redValue <<= 2; greenValue <<= 2; blueValue <<= 2; *red = redValue; *green = greenValue; *blue = blueValue; return; } #endif /**************************************************************************//** * @brief * Writes a value to a control register in the LCD controller * * @param reg * The register that will be written to * @param data * The value to write to the register * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMDIF_writeReg(uint8_t reg, uint16_t data) { SPI_TFT_WriteRegister(reg, data); return DMD_OK; } /**************************************************************************//** * @brief * Initializes the LCD display * * @param cmdRegAddr * If set to 0, initialize SSD2119 for direct drive, if set to 1 update * frame buffer base pointer (offset to EBI bank) only * @param dataRegAddr * Address in memory for frame buffer base pointer * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMD_init(uint32_t cmdRegAddr, uint32_t dataRegAddr) { uint16_t data; volatile uint32_t i; (void) cmdRegAddr; (void) dataRegAddr; /* Use data reg addr as pointer to frame buffer */ frameBuffer = (uint16_t *) dataRegAddr; if(cmdRegAddr==1) return DMD_OK; SPI_TFT_Init(); DMDIF_writeReg(DMD_SSD2119_VCOM_OTP_1, 0x0006); /* Oscillation driven DCLK */ #if 0 data = DMD_SSD2119_OSCILLATION_START_OSCEN; DMDIF_writeReg(DMD_SSD2119_OSCILLATION_START, data); #endif /* Display control */ data = 0; // data |= DMD_SSD2119_DISPLAY_CONTROL_DTE; data |= DMD_SSD2119_DISPLAY_CONTROL_GON; data |= DMD_SSD2119_DISPLAY_CONTROL_D1; data |= DMD_SSD2119_DISPLAY_CONTROL_D0; DMDIF_writeReg(DMD_SSD2119_DISPLAY_CONTROL, data); /* Exit sleep mode */ data = 0; DMDIF_writeReg(DMD_SSD2119_SLEEP_MODE_1, data); /* Wait */ for (i = 0; i < 100000; i++); /* Display control */ DMDIF_writeReg(DMD_SSD2119_DISPLAY_CONTROL, 0x33); /* Entry mode */ data = DMD_SSD2119_ENTRY_MODE_DFM_65K << DMD_SSD2119_ENTRY_MODE_DFM_SHIFT; data |= DMD_SSD2119_ENTRY_MODE_WMODE; data |= DMD_SSD2119_ENTRY_MODE_NOSYNC; data |= DMD_SSD2119_ENTRY_MODE_DMODE; data |= DMD_SSD2119_ENTRY_MODE_ID1; data |= DMD_SSD2119_ENTRY_MODE_ID0; DMDIF_writeReg(DMD_SSD2119_ENTRY_MODE, data); // DMDIF_writeReg(DMD_SSD2119_DRIVER_OUTPUT_CONTROL, 0x3aef); /* Generic Interface Control */ // data = DMD_SSD2119_GENERIC_INTERFACE_CONTROL_INVDEN; // DMDIF_writeReg(DMD_SSD2119_GENERIC_INTERFACE_CONTROL, data); /* Driver output control */ data = 0; data |= DMD_SSD2119_DRIVER_OUTPUT_CONTROL_REV; data |= DMD_SSD2119_DRIVER_OUTPUT_CONTROL_GD; // data |= DMD_SSD2119_DRIVER_OUTPUT_CONTROL_TB; // data |= DMD_SSD2119_DRIVER_OUTPUT_CONTROL_SM; // data |= DMD_SSD2119_DRIVER_OUTPUT_CONTROL_RL; data |= ((DMD_VERTICAL_SIZE - 1) << DMD_SSD2119_DRIVER_OUTPUT_CONTROL_MUX_SHIFT); DMDIF_writeReg(DMD_SSD2119_DRIVER_OUTPUT_CONTROL, data); /* LCD AC control */ data = DMD_SSD2119_LCD_AC_CONTROL_BC; data |= DMD_SSD2119_LCD_AC_CONTROL_EOR; /* data |= DMD_SSD2119_LCD_AC_CONTROL_FLC; */ DMDIF_writeReg(DMD_SSD2119_LCD_AC_CONTROL, data); /* RAM data write */ data = 0xff; DMDIF_writeReg(DMD_SSD2119_ACCESS_DATA, data); /* Mark graphics as initialized */ initialized = 1; return DMD_OK; } /**************************************************************************//** * @brief * Get the dimensions of the display and of the current clipping area * * @return * DMD_Dimensions structure containing the size of the display and the * clipping area ******************************************************************************/ EMSTATUS DMD_getDisplayGeometry(DMD_DisplayGeometry **geometry) { if (!initialized) { return DMD_ERROR_DRIVER_NOT_INITIALIZED; } *geometry = &dimensions; return DMD_OK; } /**************************************************************************//** * @brief * Sets the clipping area. All coordinates given to writeData/writeColor/readData * are relative to this clipping area. * * @param xStart * X coordinate of the upper left corner of the clipping area * @param yStart * Y coordinate of the upper left corner of the clipping area * @param width * Width of the clipping area * @param height * Height of the clipping area * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMD_setClippingArea(uint16_t xStart, uint16_t yStart, uint16_t width, uint16_t height) { if (!initialized) { return DMD_ERROR_DRIVER_NOT_INITIALIZED; } /* Check parameters */ if (xStart + width > dimensions.xSize || yStart + height > dimensions.ySize) { return DMD_ERROR_PIXEL_OUT_OF_BOUNDS; } if (width == 0 || height == 0) { return DMD_ERROR_EMPTY_CLIPPING_AREA; } /* Update the dimensions structure */ dimensions.xClipStart = xStart; dimensions.yClipStart = yStart; dimensions.clipWidth = width; dimensions.clipHeight = height; return DMD_OK; } /**************************************************************************//** * @brief * Draws pixels to the display * * @param x * X coordinate of the first pixel to be written, relative to the clipping area * @param y * Y coordinate of the first pixel to be written, relative to the clipping area * @param data * Array containing the pixel data. Each 8-bit element in the array are one color * component of the pixel, so that 3 bytes represent one pixel. The pixels are * ordered by increasing x coordinate, after the last pixel of a row, the next * pixel will be the first pixel on the next row. * @param numPixels * Number of pixels to be written * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMD_writeData(uint16_t x, uint16_t y, const uint8_t data[], uint32_t numPixels) { uint8_t *dest = (uint8_t *)((uint32_t) frameBuffer + (uint32_t) (y*dimensions.xSize*sizeof(uint16_t)) + (uint32_t) (x*sizeof(uint16_t))); while(numPixels--) { *dest++ = *data++; } return DMD_OK; } /**************************************************************************//** * @brief * Reads data from display memory * DOESN'T WORK yet - TIMING ISSUE? * * @param x * X coordinate of the first pixel to be read, relative to the clipping area * @param y * Y coordinate of the first pixel to be read, relative to the clipping area * @param data * Pointer to where the pixel data will be stored * @param numPixels * Number of pixels to be read * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMD_readData(uint16_t x, uint16_t y, uint8_t data[], uint32_t numPixels) { uint8_t *source = (uint8_t *)((uint32_t) frameBuffer + (uint32_t) (y*dimensions.xSize*sizeof(uint16_t)) + (uint32_t) (x*sizeof(uint16_t))); if (y > dimensions.ySize) return DMD_ERROR_PIXEL_OUT_OF_BOUNDS; while(numPixels--) { *data++ = *source++; } return DMD_OK; } /**************************************************************************//** * \brief * Draws a number of pixels of the same color to the display * * @param x * X coordinate of the first pixel to be written, relative to the clipping area * @param y * Y coordinate of the first pixel to be written, relative to the clipping area * @param red * Red component of the color * @param green * Green component of the color * @param blue * Blue component of the color * @param numPixels * Number of pixels to be written * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMD_writeColor(uint16_t x, uint16_t y, uint8_t red, uint8_t green, uint8_t blue, uint32_t numPixels) { uint16_t color; uint16_t xStart = x; uint16_t *pixelPointer = (uint16_t *) ((uint32_t) frameBuffer + (uint32_t) ((y+dimensions.yClipStart)*dimensions.xSize*sizeof(uint16_t)) + (uint32_t) ((x+dimensions.xClipStart)*sizeof(uint16_t))); color = colorTransform24ToRGB565(red,green,blue); /* Draw the requied number of pixels */ while(numPixels--) { x++; *pixelPointer++ = color; /* Increment line, start at the right x position inside clipping region */ if (x>=dimensions.clipWidth) { x = xStart; pixelPointer = (uint16_t *) ((uint32_t) frameBuffer + (uint32_t) (((++y)+dimensions.yClipStart)*dimensions.xSize*sizeof(uint16_t)) + (uint32_t) ((xStart+dimensions.xClipStart)*sizeof(uint16_t))); } } return DMD_OK; } /**************************************************************************//** * @brief * Turns off the display and puts it into sleep mode * Does not turn off backlight * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMD_sleep(void) { uint16_t data; if (!initialized) { return DMD_ERROR_DRIVER_NOT_INITIALIZED; } /* Put into sleep mode */ data = DMD_SSD2119_SLEEP_MODE_1_SLP; DMDIF_writeReg(DMD_SSD2119_SLEEP_MODE_1, data); /* Turn off display */ data = 0; DMDIF_writeReg(DMD_SSD2119_DISPLAY_CONTROL, 0x0000); /* Delay 1.5 frame */ /*DMDIF_delay((1000 / DMD_FRAME_FREQUENCY) * 3 / 2);*/ return DMD_OK; } /**************************************************************************//** * @brief * Wakes up the display from sleep mode * * @return * DMD_OK on success, otherwise error code ******************************************************************************/ EMSTATUS DMD_wakeUp(void) { uint16_t data; if (!initialized) { return DMD_ERROR_DRIVER_NOT_INITIALIZED; } /* Get out of sleep mode */ data = 0; DMDIF_writeReg(DMD_SSD2119_SLEEP_MODE_1, data); /* Turn on display */ data = DMD_SSD2119_DISPLAY_CONTROL_DTE; data |= DMD_SSD2119_DISPLAY_CONTROL_GON; data |= DMD_SSD2119_DISPLAY_CONTROL_D1; data |= DMD_SSD2119_DISPLAY_CONTROL_D0; DMDIF_writeReg(DMD_SSD2119_DISPLAY_CONTROL, data); /* Delay 10 frames */ /*DMDIF_delay((1000 / DMD_FRAME_FREQUENCY) * 10);*/ return DMD_OK; } /**************************************************************************//** * @brief * Set horizontal and vertical flip mode of display controller * * @param hor * Set to flip display horizontally * * @param ver * Set to flip display vertically * * @return * Returns DMD_OK is successful, error otherwise. ******************************************************************************/ EMSTATUS DMD_flipDisplay(int horizontal, int vertical) { uint16_t reg; reg = rcDriverOutputControl; if (horizontal) reg &= ~DMD_SSD2119_DRIVER_OUTPUT_CONTROL_RL; else reg |= DMD_SSD2119_DRIVER_OUTPUT_CONTROL_RL; if (vertical) reg &= ~DMD_SSD2119_DRIVER_OUTPUT_CONTROL_TB; else reg |= DMD_SSD2119_DRIVER_OUTPUT_CONTROL_TB; rcDriverOutputControl = reg; DMDIF_writeReg(DMD_SSD2119_DRIVER_OUTPUT_CONTROL, rcDriverOutputControl); return DMD_OK; }