/* 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 #include #include #include #include #include #include #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=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--; } } }