Files
xiuos/board/aiit-riscv64-board/third_party_driver/lcd/connect_lcd.c
T
2021-04-28 17:49:18 +08:00

673 lines
20 KiB
C

/* Copyright 2018 Canaan Inc.
*
* 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.
*/
/**
* @file connect_lcd.c
* @brief support aiit-riscv64-board lcd function and register to bus framework
* @version 1.0
* @author AIIT XUOS Lab
* @date 2021-04-25
*/
/*************************************************
File name: connect_lcd.c
Description: support aiit-riscv64-board lcd configure and lcd bus register function
Others: https://canaan-creative.com/developer
History:
1. Date: 2021-04-25
Author: AIIT XUOS Lab
Modification:
1. support aiit-riscv64-board lcd configure, write and read
2. support aiit-riscv64-board lcd bus device and driver register
*************************************************/
#include <connect_lcd.h>
#include <board.h>
#include <drv_io_config.h>
#include <font.h>
#include <fpioa.h>
#include <gpiohs.h>
#include <graphic.h>
#define NO_OPERATION 0x00
#define SOFTWARE_RESET 0x01
#define READ_ID 0x04
#define READ_STATUS 0x09
#define READ_POWER_MODE 0x0A
#define READ_MADCTL 0x0B
#define READ_PIXEL_FORMAT 0x0C
#define READ_IMAGE_FORMAT 0x0D
#define READ_SIGNAL_MODE 0x0E
#define READ_SELT_DIAG_RESULT 0x0F
#define SLEEP_ON 0x10
#define SLEEP_OFF 0x11
#define PARTIAL_DISPALY_ON 0x12
#define NORMAL_DISPALY_ON 0x13
#define INVERSION_DISPALY_OFF 0x20
#define INVERSION_DISPALY_ON 0x21
#define GAMMA_SET 0x26
#define DISPALY_OFF 0x28
#define DISPALY_ON 0x29
#define HORIZONTAL_ADDRESS_SET 0x2A
#define VERTICAL_ADDRESS_SET 0x2B
#define MEMORY_WRITE 0x2C
#define COLOR_SET 0x2D
#define MEMORY_READ 0x2E
#define PARTIAL_AREA 0x30
#define VERTICAL_SCROL_DEFINE 0x33
#define TEAR_EFFECT_LINE_OFF 0x34
#define TEAR_EFFECT_LINE_ON 0x35
#define MEMORY_ACCESS_CTL 0x36
#define VERTICAL_SCROL_S_ADD 0x37
#define IDLE_MODE_OFF 0x38
#define IDLE_MODE_ON 0x39
#define PIXEL_FORMAT_SET 0x3A
#define WRITE_MEMORY_CONTINUE 0x3C
#define READ_MEMORY_CONTINUE 0x3E
#define SET_TEAR_SCANLINE 0x44
#define GET_SCANLINE 0x45
#define WRITE_BRIGHTNESS 0x51
#define READ_BRIGHTNESS 0x52
#define WRITE_CTRL_DISPALY 0x53
#define READ_CTRL_DISPALY 0x54
#define WRITE_BRIGHTNESS_CTL 0x55
#define READ_BRIGHTNESS_CTL 0x56
#define WRITE_MIN_BRIGHTNESS 0x5E
#define READ_MIN_BRIGHTNESS 0x5F
#define READ_ID1 0xDA
#define READ_ID2 0xDB
#define READ_ID3 0xDC
#define RGB_IF_SIGNAL_CTL 0xB0
#define NORMAL_FRAME_CTL 0xB1
#define IDLE_FRAME_CTL 0xB2
#define PARTIAL_FRAME_CTL 0xB3
#define INVERSION_CTL 0xB4
#define BLANK_PORCH_CTL 0xB5
#define DISPALY_FUNCTION_CTL 0xB6
#define ENTRY_MODE_SET 0xB7
#define BACKLIGHT_CTL1 0xB8
#define BACKLIGHT_CTL2 0xB9
#define BACKLIGHT_CTL3 0xBA
#define BACKLIGHT_CTL4 0xBB
#define BACKLIGHT_CTL5 0xBC
#define BACKLIGHT_CTL7 0xBE
#define BACKLIGHT_CTL8 0xBF
#define POWER_CTL1 0xC0
#define POWER_CTL2 0xC1
#define VCOM_CTL1 0xC5
#define VCOM_CTL2 0xC7
#define NV_MEMORY_WRITE 0xD0
#define NV_MEMORY_PROTECT_KEY 0xD1
#define NV_MEMORY_STATUS_READ 0xD2
#define READ_ID4 0xD3
#define POSITIVE_GAMMA_CORRECT 0xE0
#define NEGATIVE_GAMMA_CORRECT 0xE1
#define DIGITAL_GAMMA_CTL1 0xE2
#define DIGITAL_GAMMA_CTL2 0xE3
#define INTERFACE_CTL 0xF6
typedef enum _lcd_dir
{
DIR_XY_RLUD = 0x00,
DIR_YX_RLUD = 0x20,
DIR_XY_LRUD = 0x40,
DIR_YX_LRUD = 0x60,
DIR_XY_RLDU = 0x80,
DIR_YX_RLDU = 0xA0,
DIR_XY_LRDU = 0xC0,
DIR_YX_LRDU = 0xE0,
DIR_XY_MASK = 0x20,
DIR_MASK = 0xE0,
} lcd_dir_t;
#define LCD_SPI_CHANNEL SPI_DEVICE_0
#define LCD_SPI_CHIP_SELECT SPI_CHIP_SELECT_0
typedef struct lcd_8080_device
{
struct LcdBus lcd_bus;
struct DeviceLcdInfo lcd_info;
int spi_channel;
int cs;
int dc_pin;
int dma_channel;
} * lcd_8080_device_t;
lcd_8080_device_t lcd ;
static void drv_lcd_cmd(uint8 cmd)
{
gpiohs_set_pin(lcd->dc_pin, GPIO_PV_LOW);
spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
spi_init_non_standard(lcd->spi_channel/*spi num*/, 8 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, &cmd, 1, SPI_TRANS_CHAR);
}
static void drv_lcd_data_byte(uint8 *data_buf, uint32 length)
{
gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
spi_init_non_standard(lcd->spi_channel, 8 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, data_buf, length, SPI_TRANS_CHAR);
}
static void drv_lcd_data_half_word(uint16 *data_buf, uint32 length)
{
gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 16, 0);
spi_init_non_standard(lcd->spi_channel, 16 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, data_buf, length, SPI_TRANS_SHORT);
}
static void drv_lcd_data_word(uint32 *data_buf, uint32 length)
{
gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
/*spi num Polarity and phase mode Multi-line mode Data bit width little endian */
spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 32, 0);
/* spi num instrction length address length wait cycles spi address trans mode*/
spi_init_non_standard(lcd->spi_channel, 0 , 32, 0 ,SPI_AITM_AS_FRAME_FORMAT );
/*dma channel spi num chip_selete tx_buff tx_len spi_trans_data_width */
spi_send_data_normal_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, data_buf, length, SPI_TRANS_INT);
}
static void drv_lcd_hw_init(lcd_8080_device_t lcd)
{
gpiohs_set_drive_mode(lcd->dc_pin, GPIO_DM_OUTPUT);
gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
spi_set_clk_rate(lcd->spi_channel, 25000000);
}
static void drv_lcd_set_direction(lcd_dir_t dir)
{
#if !BOARD_LICHEEDAN
dir |= 0x08;
#endif
if (dir & DIR_XY_MASK)
{
lcd->lcd_info.width = 320;
lcd->lcd_info.height = 240;
}
else
{
lcd->lcd_info.width = 240;
lcd->lcd_info.height = 320;
}
drv_lcd_cmd(MEMORY_ACCESS_CTL);
drv_lcd_data_byte((uint8 *)&dir, 1);
}
static void drv_lcd_set_area(uint16 x1, uint16 y1, uint16 x2, uint16 y2)
{
uint8 data[4] = {0};
data[0] = (uint8)(x1 >> 8);
data[1] = (uint8)(x1);
data[2] = (uint8)(x2 >> 8);
data[3] = (uint8)(x2);
drv_lcd_cmd(HORIZONTAL_ADDRESS_SET);
drv_lcd_data_byte(data, 4);
data[0] = (uint8)(y1 >> 8);
data[1] = (uint8)(y1);
data[2] = (uint8)(y2 >> 8);
data[3] = (uint8)(y2);
drv_lcd_cmd(VERTICAL_ADDRESS_SET);
drv_lcd_data_byte(data, 4);
drv_lcd_cmd(MEMORY_WRITE);
}
static void drv_lcd_set_pixel(uint16_t x, uint16_t y, uint16_t color)
{
drv_lcd_set_area(x, y, x, y);
drv_lcd_data_half_word(&color, 1);
}
void LcdShowChar(uint16 x,uint16 y,uint8 num,uint8 size,uint16 color,uint16 back_color)
{
uint8 temp,t1,t;
uint16 y0=y;
uint8 csize=(size/8+((size%8)?1:0))*(size/2);
num=num-' ';
for(t=0;t<csize;t++)
{
if(size==12)temp=asc2_1206[num][t]; //1206
else if(size==16)temp=asc2_1608[num][t]; //1608
else if(size==24)temp=asc2_2412[num][t]; //2412
else if(size==32)temp=asc2_3216[num][t]; //3216
else return;
for(t1=0;t1<8;t1++)
{
if(temp&0x80)
drv_lcd_set_pixel(x,y,color);
else
{
drv_lcd_set_pixel(x,y,back_color);
}
temp<<=1;
y++;
if(y>=lcd->lcd_info.height)return;
if((y-y0)==size)
{
y=y0;
x++;
if(x>=lcd->lcd_info.width)return;
break;
}
}
}
}
void LcdShowString(uint16_t x,uint16_t y,uint16_t width,uint16_t height,uint8 size,uint8 *p,uint16_t color,uint16_t back_color)
{
uint16_t x0=x;
width+=x;
height+=y;
while((*p<='~')&&(*p>=' '))
{
if(x>=width){x=x0;y+=size;}
if(y>=height)break;
LcdShowChar(x,y,*p,size,color,back_color);
x+=size/2;
p++;
}
}
/*
* clear lcd by color
* para color
* return none
*/
void drv_lcd_clear(uint16 color)
{
uint32 data = ((uint32)color << 16) | (uint32)color;
drv_lcd_set_area(0, 0, lcd->lcd_info.width - 1, lcd->lcd_info.height - 1);
gpiohs_set_pin(lcd->dc_pin, GPIO_PV_HIGH);
spi_init(lcd->spi_channel, SPI_WORK_MODE_0, SPI_FF_OCTAL, 32, 0);
spi_init_non_standard(lcd->spi_channel,
0 /*instrction length*/,
32 /*address length */,
0 /*wait cycles */,
SPI_AITM_AS_FRAME_FORMAT );
spi_fill_data_dma(lcd->dma_channel, lcd->spi_channel, lcd->cs, (const uint32_t *)&data, lcd->lcd_info.width * lcd->lcd_info.height / 2);
}
static void drv_lcd_rect_update(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height)
{
static uint16 * rect_buffer = NONE;
if(!rect_buffer)
{
rect_buffer = x_malloc(lcd->lcd_info.height * lcd->lcd_info.width * (lcd->lcd_info.bits_per_pixel / 8));
if(!rect_buffer)
{
return;
}
}
if(x1 == 0 && y1 == 0 && width == lcd->lcd_info.width && height == lcd->lcd_info.height)
{
drv_lcd_set_area(x1, y1, x1 + width - 1, y1 + height - 1);
drv_lcd_data_word((uint32 *)lcd->lcd_info.framebuffer, width * height / (lcd->lcd_info.bits_per_pixel / 8));
}
else
{
drv_lcd_set_area(x1, y1, x1 + width - 1, y1 + height - 1);
drv_lcd_data_word((uint32 *)rect_buffer, width * height / 2);
}
}
x_err_t drv_lcd_init(lcd_8080_device_t dev)
{
x_err_t ret = EOK;
lcd = (lcd_8080_device_t)dev;
uint8 data = 0;
if(!lcd)
{
return ERROR;
}
drv_lcd_hw_init(lcd);
/* reset LCD */
drv_lcd_cmd(SOFTWARE_RESET);
MdelayKTask(100);
/* Enter normal status */
drv_lcd_cmd(SLEEP_OFF);
MdelayKTask(100);
/* pixel format rgb565 */
drv_lcd_cmd(PIXEL_FORMAT_SET);
data = 0x55;
drv_lcd_data_byte(&data, 1);
/* set direction */
drv_lcd_set_direction(DIR_YX_RLUD);
lcd->lcd_info.framebuffer = x_malloc(lcd->lcd_info.height * lcd->lcd_info.width * (lcd->lcd_info.bits_per_pixel / 8));
CHECK(lcd->lcd_info.framebuffer);
/*display on*/
drv_lcd_cmd(DISPALY_ON);
return ret;
}
static x_err_t drv_lcd_control(lcd_8080_device_t dev, int cmd, void *args)
{
x_err_t ret = EOK;
lcd_8080_device_t lcd = (lcd_8080_device_t)dev;
x_base level;
struct DeviceRectInfo* rect_info = (struct DeviceRectInfo*)args;
NULL_PARAM_CHECK(dev);
switch (cmd)
{
case GRAPHIC_CTRL_RECT_UPDATE:
if(!rect_info)
{
SYS_ERR("GRAPHIC_CTRL_RECT_UPDATE error args");
return -ERROR;
}
drv_lcd_rect_update(rect_info->x, rect_info->y, rect_info->width, rect_info->height);
break;
case GRAPHIC_CTRL_POWERON:
/* Todo: power on */
ret = -ENONESYS;
break;
case GRAPHIC_CTRL_POWEROFF:
/* Todo: power off */
ret = -ENONESYS;
break;
case GRAPHIC_CTRL_GET_INFO:
*(struct DeviceLcdInfo *)args = lcd->lcd_info;
break;
case GRAPHIC_CTRL_SET_MODE:
ret = -ENONESYS;
break;
case GRAPHIC_CTRL_GET_EXT:
ret = -ENONESYS;
break;
default:
SYS_ERR("drv_lcd_control cmd: %d", cmd);
break;
}
return ret;
}
void ClearHandwriting (void)
{
//clear the lcd
drv_lcd_clear(WHITE);
LcdShowString(10, 10, 100, 24, 24, "RST ", RED, WHITE);
}
#ifdef CONFIG_TOUCH
void HandTest(unsigned short *x_pos, unsigned short *y_pos)
{
float x1,y1;
TP_Read_XY(x_pos,y_pos); //address
float a = 12.1875,b = 16.25;
x1 = 320 - (*x_pos)/a +10;
y1 = (* y_pos)/b;
if ((*x_pos> 500)&&(*y_pos<500)) {
ClearHandwriting();
} else {
drv_lcd_set_pixel(x1, y1, RED);
drv_lcd_set_pixel(x1+1, y1, RED);
drv_lcd_set_pixel(x1-1, y1, RED);
drv_lcd_set_pixel(x1, y1+1, RED);
drv_lcd_set_pixel(x1, y1-1, RED);
drv_lcd_set_pixel(x1+1, y1+1, RED);
drv_lcd_set_pixel(x1-1, y1-1, RED);
drv_lcd_set_pixel(x1+1, y1-1, RED);
drv_lcd_set_pixel(x1-1, y1+1, RED);
}
}
#endif
static uint32 Lcd_Write(void *dev, struct BusBlockWriteParam *write_param)
{
if (write_param == NONE) {
return ERROR;
}
LcdStringParam * show = (LcdStringParam *)write_param->buffer;
if (0==write_param->pos) { //output string
LcdShowString(show->x_pos,show->y_pos,show->width,show->height,show->font_size,show->addr,show->font_color,show->back_color);
return EOK;
} else if (1==write_param->pos) { //output dot
drv_lcd_set_pixel(show->x_pos, show->y_pos, show->font_color);
return EOK;
} else {
return ERROR;
}
}
uint32 drv_lcd_clear_done(void * dev, struct BusConfigureInfo *configure_info)
{
uint16 color = 0;
color =(uint16)(configure_info->configure_cmd |0x0000ffff );
drv_lcd_clear( color);
return 0;
}
const struct LcdDevDone lcd_dev_done =
{
.open = NONE,
.close = NONE,
.write = Lcd_Write,
.read = NONE
};
static int BoardLcdBusInit(struct LcdBus * lcd_bus, struct LcdDriver * lcd_driver)
{
x_err_t ret = EOK;
/*Init the lcd bus */
ret = LcdBusInit( lcd_bus, LCD_BUS_NAME_1);
if (EOK != ret) {
KPrintf("Board_lcd_init LcdBusInit error %d\n", ret);
return ERROR;
}
lcd_driver->configure = drv_lcd_clear_done;
/*Init the lcd driver*/
ret = LcdDriverInit( lcd_driver, LCD_DRV_NAME_1);
if (EOK != ret) {
KPrintf("Board_LCD_init LcdDriverInit error %d\n", ret);
return ERROR;
}
/*Attach the lcd driver to the lcd bus*/
ret = LcdDriverAttachToBus(LCD_DRV_NAME_1, LCD_BUS_NAME_1);
if (EOK != ret) {
KPrintf("Board_LCD_init LcdDriverAttachToBus error %d\n", ret);
return ERROR;
}
return ret;
}
/*Attach the lcd device to the lcd bus*/
static int BoardLcdDevBend(void)
{
x_err_t ret = EOK;
static struct LcdHardwareDevice lcd_device;
memset(&lcd_device, 0, sizeof(struct LcdHardwareDevice));
lcd_device.dev_done = &(lcd_dev_done);
ret = LcdDeviceRegister(&lcd_device, NONE, LCD_1_DEVICE_NAME_0);
if (EOK != ret) {
KPrintf("Board_LCD_init LcdDeviceInit device %s error %d\n", LCD_1_DEVICE_NAME_0, ret);
return ERROR;
}
ret = LcdDeviceAttachToBus(LCD_1_DEVICE_NAME_0, LCD_BUS_NAME_1);
if (EOK != ret) {
KPrintf("Board_LCD_init LcdDeviceAttachToBus device %s error %d\n", LCD_1_DEVICE_NAME_0, ret);
return ERROR;
}
return ret;
}
int HwLcdInit(void)
{
x_err_t ret = EOK;
static struct LcdDriver lcd_driver;
memset(&lcd_driver, 0, sizeof(struct LcdDriver));
lcd_8080_device_t lcd_dev = (lcd_8080_device_t )malloc(sizeof( struct lcd_8080_device));
memset(lcd_dev, 0, sizeof(struct lcd_8080_device));
if (!lcd_dev){
return -1;
}
FpioaSetFunction(41,FUNC_GPIOHS9); //DC order / data
FpioaSetFunction(47,FUNC_GPIOHS10); //BL
FpioaSetFunction(40,FUNC_SPI0_SS0); //chip select
FpioaSetFunction(38,FUNC_SPI0_SCLK); //clk
lcd_dev->cs = SPI_CHIP_SELECT_0;
lcd_dev->dc_pin = 9;
lcd_dev->dma_channel = DMAC_CHANNEL0;
lcd_dev->spi_channel = SPI_DEVICE_0;
lcd_dev->lcd_info.bits_per_pixel = 16;
lcd_dev->lcd_info.pixel_format = PIXEL_FORMAT_BGR565;
sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18);
sysctl_set_power_mode(SYSCTL_POWER_BANK7, SYSCTL_POWER_V18);
sysctl_set_spi0_dvp_data(1); //open the lcd interface with spi0
ret = BoardLcdBusInit(&lcd_dev->lcd_bus, &lcd_driver); //init lcd bus
if (EOK != ret) {
KPrintf("Board_lcd_Init error ret %u\n", ret);
return ERROR;
}
ret = BoardLcdDevBend(); //init lcd device
if (EOK != ret) {
KPrintf("BoardLcdDevBend error ret %u\n", ret);
return ERROR;
}
gpiohs_set_drive_mode(10, GPIO_DM_OUTPUT);
gpiohs_set_pin(10, GPIO_PV_HIGH);
KPrintf("LCD driver inited ...\r\n");
drv_lcd_init(lcd_dev);
return ret;
}
//x1,y1:start
//x2,y2:end
void LCD_DrawLine(uint16 x1, uint16 y1, uint16 x2, uint16 y2,uint16 color)
{
uint16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1;
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1;
else if(delta_x==0)incx=0;
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x;
else distance=delta_y;
for(t=0;t<=distance+1;t++ )
{
drv_lcd_set_pixel(uRow,uCol,color);
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
void LCD_DrawRectangle(uint16 x1, uint16 y1, uint16 x2, uint16 y2,uint16 color)
{
LCD_DrawLine(x1,y1,x2,y1,color);
LCD_DrawLine(x1,y1,x1,y2,color);
LCD_DrawLine(x1,y2,x2,y2,color);
LCD_DrawLine(x2,y1,x2,y2,color);
}
void LCD_Draw_Circle(uint16 x0,uint16 y0,uint8 r,uint16 color)
{
int a,b;
int di;
a=0;b=r;
di=3-(r<<1);
while(a<=b)
{
drv_lcd_set_pixel(x0+a,y0-b,color); //5
drv_lcd_set_pixel(x0+b,y0-a,color); //0
drv_lcd_set_pixel(x0+b,y0+a,color); //4
drv_lcd_set_pixel(x0+a,y0+b,color); //6
drv_lcd_set_pixel(x0-a,y0+b,color); //1
drv_lcd_set_pixel(x0-b,y0+a,color);
drv_lcd_set_pixel(x0-a,y0-b,color); //2
drv_lcd_set_pixel(x0-b,y0-a,color); //7
a++;
//Bresenham
if(di<0)di +=4*a+6;
else
{
di+=10+4*(a-b);
b--;
}
}
}