626 lines
16 KiB
C
626 lines
16 KiB
C
/***************************************************************************//**
|
|
* @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 for EFM32
|
|
******************************************************************************/
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup efm32
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "board.h"
|
|
#include "drv_usart.h"
|
|
#include "dev_lcd.h"
|
|
|
|
#if defined(EFM32_USING_LCD)
|
|
#include <rtgui/rtgui.h>
|
|
#include <rtgui/driver.h>
|
|
|
|
#include <dmd_ssd2119.h>
|
|
|
|
/* 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 -----------------------------------------------*/
|
|
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);
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
static rt_device_t lcd;
|
|
static struct rt_device lcd_device;
|
|
static struct rt_device_graphic_info lcd_info;
|
|
static const struct rtgui_graphic_driver_ops lcd_ops =
|
|
{
|
|
efm32_spiLcd_setPixel,
|
|
efm32_spiLcd_getPixel,
|
|
efm32_spiLcd_drawHLine,
|
|
efm32_spiLcd_drawVLine,
|
|
efm32_spiLcd_drawRawHLine
|
|
};
|
|
static rt_bool_t lcdAutoCs = true;
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
/***************************************************************************//**
|
|
* @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
|
|
* 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);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @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
|
|
* 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_spiLed_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);
|
|
|
|
/* Reconfig speed */
|
|
usart = (struct efm32_usart_device_t *)(lcd->user_data);
|
|
USART_BaudrateSyncSet(usart->usart_device, 0, EFM32_LCD_SPICLK);
|
|
|
|
/* Config CS pin */
|
|
if (!(usart->state & USART_STATE_AUTOCS))
|
|
{
|
|
GPIO_PinModeSet(LCD_CS_PORT, LCD_CS_PIN, gpioModePushPull, 1);
|
|
lcdAutoCs = false;
|
|
}
|
|
|
|
// TODO: add another method
|
|
/* TFT initialize or reinitialize to Address Mapped Mode
|
|
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();
|
|
}
|
|
/* Configure display for Direct Drive + 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;
|
|
}
|
|
}
|
|
|
|
/* Get LCD geometry */
|
|
ret = DMD_getDisplayGeometry(&geometry);
|
|
if (ret != DMD_OK)
|
|
{
|
|
lcd_debug("LCD err: get geometry failed!\n");
|
|
break;
|
|
}
|
|
|
|
/* Init LCD info */
|
|
lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565P;
|
|
lcd_info.bits_per_pixel = 16;
|
|
lcd_info.width = geometry->xSize - 1;
|
|
lcd_info.height = geometry->ySize - 1;
|
|
lcd_info.framebuffer = RT_NULL;
|
|
|
|
flag = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_DMA_TX;
|
|
efm32_spiLcd_register(&lcd_device, LCD_DEVICE_NAME, flag, (void *)&lcd_ops);
|
|
|
|
/* 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;
|
|
code = DMDIF_readDeviceCode();
|
|
|
|
/* Set as rtgui graphic driver */
|
|
rtgui_graphic_set_device(&lcd_device);
|
|
|
|
lcd_debug("LCD: H/W (%x) init OK!\n", code);
|
|
return;
|
|
} while(0);
|
|
|
|
lcd_debug("LCD err: H/W init failed!\n");
|
|
}
|
|
|
|
#endif /* defined(EFM32_USING_LCD) */
|
|
/***************************************************************************//**
|
|
* @}
|
|
******************************************************************************/
|