/***************************************************************************//** * @file dev_lcd.c * @brief LCD driver of RT-Thread RTOS for EFM32 * COPYRIGHT (C) 2011, RT-Thread Development Team * @author onelife * @version 0.4 beta ******************************************************************************* * @section License * The license and distribution terms for this file may be found in the file * LICENSE in this distribution or at http://www.rt-thread.org/license/LICENSE ******************************************************************************* * @section Change Logs * Date Author Notes * 2011-12-16 onelife Initial creation of address mapped method (pixel * drive) for EFM32GG_DK3750 board * 2011-12-29 onelife Add direct drive method (frame buffer) support ******************************************************************************/ /***************************************************************************//** * @addtogroup EFM32GG_DK3750 * @{ ******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #include "board.h" #include "drv_usart.h" #include "dev_lcd.h" #if defined(EFM32_USING_LCD) #if (!defined(LCD_MAPPED) && !defined(LCD_DIRECT)) #error "Unknown LCD access mode" #endif #include #include #include /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ #ifdef EFM32_LCD_DEBUG #define lcd_debug(format,args...) rt_kprintf(format, ##args) #else #define lcd_debug(format,args...) #endif /* Private function prototypes -----------------------------------------------*/ #if defined(LCD_MAPPED) static void efm32_spiLcd_setPixel(rtgui_color_t *c, int x, int y); static void efm32_spiLcd_getPixel(rtgui_color_t *c, int x, int y); static void efm32_spiLcd_drawRawHLine(rt_uint8_t *pixels, int x1, int x2, int y); static void efm32_spiLcd_drawHLine(rtgui_color_t *c, int x1, int x2, int y); static void efm32_spiLcd_drawVLine(rtgui_color_t *c, int x1, int x2, int y); #endif /* Private variables ---------------------------------------------------------*/ static rt_device_t lcd; static struct rt_device lcd_device; static rt_bool_t lcdAutoCs = true; static struct rt_device_graphic_info lcd_info; #if defined(LCD_MAPPED) static const struct rtgui_graphic_driver_ops lcd_ops = { efm32_spiLcd_setPixel, efm32_spiLcd_getPixel, efm32_spiLcd_drawHLine, efm32_spiLcd_drawVLine, efm32_spiLcd_drawRawHLine }; /* Private functions ---------------------------------------------------------*/ /***************************************************************************//** * @brief * Draw a pixel with specified color * * @details * * @note * * @param[in] c * Pointer to color * * @param[in] x * Horizontal position * * @param[in] y * Vertical position ******************************************************************************/ static void efm32_spiLcd_setPixel(rtgui_color_t *c, int x, int y) { rt_uint32_t ret = RT_EOK; do { /* Check if pixel is outside clipping region */ if ((x < 0) || (x > lcd_info.width)) { break; } if ((y < 0) || (y > lcd_info.height)) { break; } /* Write color */ ret = DMD_writePixel((rt_uint16_t)x, (rt_uint16_t)y, (rt_uint16_t)*c, 1); if (ret != 0) { break; } return; } while(0); // lcd_debug("LCD err: Set pixel at (%d,%d: %x) failed (%x)!\n", x, y, *c, ret); } /***************************************************************************//** * @brief * Get the color of a pixel * * @details * * @note * * @param[out] c * Pointer to color * * @param[in] x * Horizontal position * * @param[in] y * Vertical position ******************************************************************************/ static void efm32_spiLcd_getPixel(rtgui_color_t *c, int x, int y) { rt_uint32_t ret = RT_EOK; do { /* Check if pixel is outside clipping region */ if ((x < 0) || (x > lcd_info.width)) { break; } if ((y < 0) || (y > lcd_info.height)) { break; } /* Read color */ ret = DMD_readPixel((rt_uint16_t)x, (rt_uint16_t)y, (rt_uint16_t *)c); if (ret != 0) { break; } return; } while(0); lcd_debug("LCD err: Get pixel at (%d,%d: %x) failed (%x)!\n", x, y, *c, ret); } /***************************************************************************//** * @brief * Draw a horizontal line with raw color * * @details * * @note * * @param[in] pixels * Pointer to raw color * * @param[in] x1 * Horizontal start position * * @param[in] x2 * Horizontal end position * * @param[in] y * Vertical position ******************************************************************************/ static void efm32_spiLcd_drawRawHLine(rt_uint8_t *pixels, int x1, int x2, int y) { lcd_debug("LCD: RAW H LINE!\n"); } /***************************************************************************//** * @brief * Draw a horizontal line with specified color * * @details * * @note * * @param[in] c * Pointer to color * * @param[in] x1 * Horizontal start position * * @param[in] x2 * Horizontal end position * * @param[in] y * Vertical position ******************************************************************************/ static void efm32_spiLcd_drawHLine(rtgui_color_t *c, int x1, int x2, int y) { rt_uint32_t ret = RT_EOK; do { /* Check if line is outside of clipping region */ if ((y < 0) || (y > lcd_info.height)) { break; } /* Swap the coordinates if x1 is larger than x2 */ if (x1 > x2) { int swap; swap = x1; x1 = x2; x2 = swap; } /* Check if entire line is outside clipping region */ if ((x1 > lcd_info.width) || (x2 < 0)) { /* Nothing to draw */ break; } /* Clip the line if necessary */ if (x1 < 0) { x1 = 0; } if (x2 > lcd_info.width) { x2 = lcd_info.width; } /* Write color */ rt_uint32_t length = x2 - x1 + 1; ret = DMD_writePixel((rt_uint16_t)x1, (rt_uint16_t)y, (rt_uint16_t)*c, length); if (ret != 0) { break; } return; } while(0); // lcd_debug("LCD err: Draw hline at (%d-%d,%d: %x) failed (%x)!\n", x1, x2, y, *c, ret); } /***************************************************************************//** * @brief * Draw a vertical line with specified color * * @details * * @note * * @param[in] c * Pointer to color * * @param[in] x * Horizontal position * * @param[in] y1 * Vertical start position * * @param[in] y2 * Vertical end position ******************************************************************************/ static void efm32_spiLcd_drawVLine(rtgui_color_t *c, int x , int y1, int y2) { rt_uint32_t ret = RT_EOK; do { /* Check if line is outside of clipping region */ if ((x < 0) || (x > lcd_info.width)) { break; } /* Swap the coordinates if y1 is larger than y2 */ if (y1 > y2) { rt_uint16_t swap; swap = y1; y1 = y2; y2 = swap; } /* Check if entire line is outside clipping region */ if ((y1 > lcd_info.height) || (y2 < 0)) { /* Nothing to draw */ break; } /* Clip the line if necessary */ if (y1 < 0) { y1 = 0; } if (y2 > lcd_info.height) { y2 = lcd_info.height; } /* Set clipping area */ rt_uint16_t length = y2 - y1 + 1; ret = DMD_setClippingArea((rt_uint16_t)x, (rt_uint16_t)y1, 1, length); if (ret != DMD_OK) { break; } /* Write color */ ret= DMD_writePixel(0, 0, (rt_uint16_t)*c, length); if (ret != DMD_OK) { break; } /* Reset clipping area */ ret = DMD_setClippingArea(0, 0, lcd_info.width, lcd_info.height); if (ret != DMD_OK) { break; } return; } while(0); // lcd_debug("LCD err: Draw vline at (%d,%d-%d: %x) failed (%x)!\n", x, y1, y2, *c, ret); } #endif /***************************************************************************//** * @brief * Configure LCD device * * @details * * @note * * @param[in] dev * Pointer to device descriptor * * @param[in] cmd * IIC control command * * @param[in] args * Arguments * * @return * Error code ******************************************************************************/ static rt_err_t efm32_spiLcd_control (rt_device_t dev, rt_uint8_t cmd, void *args) { switch (cmd) { case RTGRAPHIC_CTRL_RECT_UPDATE: break; case RTGRAPHIC_CTRL_POWERON: break; case RTGRAPHIC_CTRL_POWEROFF: break; case RTGRAPHIC_CTRL_GET_INFO: rt_memcpy(args, &lcd_info, sizeof(struct rt_device_graphic_info)); break; case RTGRAPHIC_CTRL_SET_MODE: break; } return RT_EOK; } /***************************************************************************//** * @brief * Set/Clear chip select * * @details * * @note * * @param[in] enable * Chip select pin setting ******************************************************************************/ static void efm32_spiLcd_cs(rt_uint8_t enable) { if (!lcdAutoCs) { if (enable) { GPIO_PinOutClear(LCD_CS_PORT, LCD_CS_PIN); } else { GPIO_PinOutSet(LCD_CS_PORT, LCD_CS_PIN); } } } /***************************************************************************//** * @brief * Write data to SSD2119 controller * * @param[in] reg * Register to write to * * @param[in] data * 16-bit data to write into register * * @note * It's not possible to read back register value through SSD2119 SPI interface ******************************************************************************/ rt_err_t efm32_spiLcd_writeRegister(rt_uint8_t reg, rt_uint16_t data) { struct efm32_usart_device_t *usart; rt_uint8_t buf_ins[3]; rt_uint8_t buf_res[3]; RT_ASSERT(lcd != RT_NULL); usart = (struct efm32_usart_device_t *)(lcd->user_data); /* Build instruction buffer */ buf_res[0] = (data & 0xff00) >> 8; buf_res[1] = data & 0x00ff; buf_ins[0] = 1; /* Instruction length */ buf_ins[1] = reg; /* Instruction */ *(rt_uint8_t **)(&buf_ins[2]) = buf_res; /* Data */ efm32_spiLcd_cs(1); if (lcd->write(lcd, EFM32_NO_DATA, buf_ins, 2) == 0) { lcd_debug("LCD: Write data failed!\n"); return -RT_ERROR; } efm32_spiLcd_cs(0); return RT_EOK; } /***************************************************************************//** * @brief * Register LCD device * * @details * * @note * * @param[in] device * Pointer to device descriptor * * @param[in] name * Device name * * @param[in] flag * Configuration flags * * @param[in] iic * Pointer to IIC device descriptor * * @return * Error code ******************************************************************************/ rt_err_t efm32_spiLcd_register( rt_device_t device, const char *name, rt_uint32_t flag, void *data) { RT_ASSERT(device != RT_NULL); device->type = RT_Device_Class_Graphic; device->rx_indicate = RT_NULL; device->tx_complete = RT_NULL; device->init = RT_NULL; device->open = RT_NULL; device->close = RT_NULL; device->read = RT_NULL; device->write = RT_NULL; device->control = efm32_spiLcd_control; device->user_data = data; /* register a character device */ return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | flag); } /***************************************************************************//** * @brief * Initialize LCD device * * @details * * @note * ******************************************************************************/ void efm32_spiLcd_init(void) { struct efm32_usart_device_t *usart; rt_uint32_t flag; DMD_DisplayGeometry *geometry; rt_uint32_t ret; do { USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT; /* Find SPI device */ lcd = rt_device_find(LCD_USING_DEVICE_NAME); if (lcd == RT_NULL) { lcd_debug("LCD err: Can't find %s!\n", LCD_USING_DEVICE_NAME); break; } lcd_debug("LCD: Find device %s\n", LCD_USING_DEVICE_NAME); /* Config CS pin */ usart = (struct efm32_usart_device_t *)(lcd->user_data); if (!(usart->state & USART_STATE_AUTOCS)) { GPIO_PinModeSet(LCD_CS_PORT, LCD_CS_PIN, gpioModePushPull, 1); lcdAutoCs = false; } /* TFT initialize or reinitialize. Assumes EBI has been configured correctly in DVK_init(DVK_Init_EBI) */ rt_uint32_t freq = SystemCoreClockGet(); rt_uint32_t i; rt_bool_t warning = RT_FALSE; /* If we are in BC_UIF_AEM_EFM state, we can redraw graphics */ while (DVK_readRegister(&BC_REGISTER->UIF_AEM) != BC_UIF_AEM_EFM) { if (!warning) { lcd_debug("LCD: Please press AEM button!!!\n"); warning = RT_TRUE; } } lcd_debug("LCD: Got LCD control\n"); /* If we're not BC_ARB_CTRL_EBI state, we need to reconfigure display controller */ if (DVK_readRegister(&BC_REGISTER->ARB_CTRL) != BC_ARB_CTRL_EBI) { lcd_debug("LCD: Set to EBI mode\n"); /* Configure for EBI mode and reset display */ DVK_displayControl(DVK_Display_EBI); DVK_displayControl(DVK_Display_ResetAssert); DVK_displayControl(DVK_Display_PowerDisable); /* Short delay */ freq = SystemCoreClockGet(); for(i = 0; i < (freq / 100); i++) { __NOP(); } #if defined(LCD_MAPPED) /* Configure display for address mapped method + 3-wire SPI mode */ DVK_displayControl(DVK_Display_Mode8080); DVK_displayControl(DVK_Display_PowerEnable); DVK_displayControl(DVK_Display_ResetRelease); /* Initialize graphics - abort on failure */ ret = DMD_init(BC_SSD2119_BASE, BC_SSD2119_BASE + 2); if (ret == DMD_OK) { /* Make sure display is configured with correct rotation */ DMD_flipDisplay(1, 1); } else if (ret != DMD_ERROR_DRIVER_ALREADY_INITIALIZED) { lcd_debug("LCD err: driver init failed %x\n", ret); break; } #elif defined(LCD_DIRECT) /* Configure TFT direct drive method from EBI BANK2 */ const EBI_TFTInit_TypeDef tftInit = { ebiTFTBank2, /* Select EBI Bank 2 */ ebiTFTWidthHalfWord, /* Select 2-byte (16-bit RGB565) increments */ ebiTFTColorSrcMem, /* Use memory as source for mask/blending */ ebiTFTInterleaveUnlimited, /* Unlimited interleaved accesses */ ebiTFTFrameBufTriggerVSync, /* VSYNC as frame buffer update trigger */ false, /* Drive DCLK from negative edge of internal clock */ ebiTFTMBDisabled, /* No masking and alpha blending enabled */ ebiTFTDDModeExternal, /* Drive from external memory */ ebiActiveLow, /* CS Active Low polarity */ ebiActiveHigh, /* DCLK Active High polarity */ ebiActiveLow, /* DATAEN Active Low polarity */ ebiActiveLow, /* HSYNC Active Low polarity */ ebiActiveLow, /* VSYNC Active Low polarity */ 320, /* Horizontal size in pixels */ 1, /* Horizontal Front Porch */ 30, /* Horizontal Back Porch */ 2, /* Horizontal Synchronization Pulse Width */ 240, /* Vertical size in pixels */ 1, /* Vertical Front Porch */ 4, /* Vertical Back Porch */ 2, /* Vertical Synchronization Pulse Width */ 0x0000, /* Frame Address pointer offset to EBI memory base */ 4, /* DCLK Period */ 0, /* DCLK Start cycles */ 0, /* DCLK Setup cycles */ 0, /* DCLK Hold cycles */ }; DVK_enablePeripheral(DVK_TFT); /* Configure display for Direct Drive + 3-wire SPI mode */ DVK_displayControl(DVK_Display_ModeGeneric); DVK_displayControl(DVK_Display_PowerEnable); DVK_displayControl(DVK_Display_ResetRelease); /* Configure GPIO for EBI and TFT */ /* EBI TFT DCLK/Dot Clock */ GPIO_PinModeSet(gpioPortA, 8, gpioModePushPull, 0); /* EBI TFT DATAEN */ GPIO_PinModeSet(gpioPortA, 9, gpioModePushPull, 0); /* EBI TFT VSYNC */ GPIO_PinModeSet(gpioPortA, 10, gpioModePushPull, 0); /* EBI TFT HSYNC */ GPIO_PinModeSet(gpioPortA, 11, gpioModePushPull, 0); /* Initialize display */ DMD_init(0, (rt_uint32_t)EBI_BankAddress(EBI_BANK2)); /* Configure EBI TFT direct drive */ EBI_TFTInit(&tftInit); #endif } /* Get LCD geometry */ ret = DMD_getDisplayGeometry(&geometry); if (ret != DMD_OK) { lcd_debug("LCD err: get geometry failed!\n"); break; } /* Init LCD info */ flag = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_DMA_TX; lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565P; lcd_info.bits_per_pixel = 16; lcd_info.width = geometry->xSize; lcd_info.height = geometry->ySize; #if defined(LCD_MAPPED) lcd_info.framebuffer = RT_NULL; efm32_spiLcd_register(&lcd_device, LCD_DEVICE_NAME, flag, (void *)&lcd_ops); #elif defined(LCD_DIRECT) lcd_info.framebuffer = (rt_uint8_t *)EBI_BankAddress(EBI_BANK2); efm32_spiLcd_register(&lcd_device, LCD_DEVICE_NAME, flag, RT_NULL); #endif /* Set clipping area */ ret = DMD_setClippingArea(0, 0, geometry->xSize, geometry->ySize); if (ret != DMD_OK) { lcd_debug("LCD err: set clipping area failed!\n"); break; } /* Read device code */ rt_uint16_t code = 0xFFFF; #if defined(LCD_MAPPED) code = DMDIF_readDeviceCode(); #endif /* Set as rtgui graphic driver */ rtgui_graphic_set_device(&lcd_device); lcd_debug("LCD: H/W init OK!\n"); return; } while(0); lcd_debug("LCD err: H/W init failed!\n"); } #endif /* defined(EFM32_USING_LCD) */ /***************************************************************************//** * @} ******************************************************************************/