Escribir un controlador de framebuffer para una Raspberry Pi con LCD

Después de leer una serie monumental de artículos sobre cómo conectar una pantalla LCD a un enrutador, quise hacer lo mismo. Sin embargo, la variedad de la pila utilizada (openwrt, stm32, usb) combinada con la falta de código fuente completopero tal vez me veía malalgo complicado la tarea. Decidí empezar poco a poco: escribir mi propia implementación de framebuffer para raspberry y llevar el entorno gráfico de raspberry a LCD. Lo que resultó de esto, lo describo a continuación.







En realidad, hay controladores listos para usar para la pantalla LCD (proyecto tbtft), pero escribiremos los nuestros para comprender mejor cómo funciona todo.







LCD



LCD 320x240 con controlador ILI9341. Transferencia de datos en un bus de 8 bits.









La escritura de datos en la pantalla LCD se realiza de la siguiente manera (página 28) :







Mantenemos 1 en RD y 1 en RESET después del inicio de la pantalla LCD todo el tiempo. Antes de transferir datos, enviamos 0 a CS, configuramos 8 bits de datos en el bus, configuramos 1 o 0 en RS (D / CX en el gráfico), según el tipo de transferencia: datos / comando, restablecemos WR a 0 y luego lo configuramos en 1. Después de terminar la transferencia de datos establece CS en 1.







Código de transmisión de datos / comandos
/*  lcd.c */
void LCD_write(u8 VAL)
{
    LCD_CS_CLR;
    DATAOUT(VAL);
    LCD_WR_CLR;
    LCD_WR_SET;
    LCD_CS_SET;
}

/*   */
void LCD_WR_REG(u8 data)
{
    LCD_RS_CLR;
    LCD_write(data);
}

/*   */
void LCD_WR_DATA(u8 data)
{
    LCD_RS_SET;
    LCD_write(data);
}

/*     */
void LCD_WriteReg(u8 LCD_Reg, u8 LCD_RegValue)
{
    LCD_WR_REG(LCD_Reg);
    LCD_WR_DATA(LCD_RegValue);
}

/*  16   */
void Lcd_WriteData_16Bit(u16 Data)
{
    LCD_RS_SET;
    LCD_CS_CLR;
    DATAOUT((u8)(Data>>8));
    LCD_WR_CLR;
    LCD_WR_SET;
    DATAOUT((u8)Data);
    LCD_WR_CLR;
    LCD_WR_SET;
    LCD_CS_SET;
}
      
      





LCD ( STM32), raspberry. LCD 16 RGB565 (5 , 6 , 5 ).







LCD
/*  lcd.h */
#define LCD_W 320
#define LCD_H 240

/*  lcd.c */
/*  ,       */
void LCD_WriteRAM_Prepare(void)
{
    LCD_WR_REG(0x2C);
}

/*    ,    */
void LCD_SetWindows(u16 xStart, u16 yStart,u16 xEnd,u16 yEnd)
{
    LCD_WR_REG(0x2A);
    LCD_WR_DATA(xStart>>8);
    LCD_WR_DATA(0x00FF&xStart);
    LCD_WR_DATA(xEnd>>8);
    LCD_WR_DATA(0x00FF&xEnd);

    LCD_WR_REG(0x2B);
    LCD_WR_DATA(yStart>>8);
    LCD_WR_DATA(0x00FF&yStart);
    LCD_WR_DATA(yEnd>>8);
    LCD_WR_DATA(0x00FF&yEnd);

    LCD_WriteRAM_Prepare();
}

/*   */
void LCD_RESET(void)
{
    LCD_RST_CLR;
    delay(100);
    LCD_RST_SET;
    delay(50);
}

/*   */
void LCD_Init(void)
{
    LCD_RESET();
    LCD_WR_REG(0xCF);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0xC9);
    LCD_WR_DATA(0X30);
    LCD_WR_REG(0xED);
    LCD_WR_DATA(0x64);
    LCD_WR_DATA(0x03);
    LCD_WR_DATA(0X12);
    LCD_WR_DATA(0X81);
    LCD_WR_REG(0xE8);
    LCD_WR_DATA(0x85);
    LCD_WR_DATA(0x10);
    LCD_WR_DATA(0x7A);
    LCD_WR_REG(0xCB);
    LCD_WR_DATA(0x39);
    LCD_WR_DATA(0x2C);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x34);
    LCD_WR_DATA(0x02);
    LCD_WR_REG(0xF7);
    LCD_WR_DATA(0x20);
    LCD_WR_REG(0xEA);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_REG(0xC0);    
    LCD_WR_DATA(0x1B);   
    LCD_WR_REG(0xC1);    
    LCD_WR_DATA(0x00);   
    LCD_WR_REG(0xC5);    
    LCD_WR_DATA(0x30);   
    LCD_WR_DATA(0x30);   
    LCD_WR_REG(0xC7);   
    LCD_WR_DATA(0XB7);
    LCD_WR_REG(0x36);    
    LCD_WR_DATA(0x08);
    LCD_WR_REG(0x3A);
    LCD_WR_DATA(0x55);
    LCD_WR_REG(0xB1);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x1A);
    LCD_WR_REG(0xB6);    
    LCD_WR_DATA(0x0A);
    LCD_WR_DATA(0xA2);
    LCD_WR_REG(0xF2);    
    LCD_WR_DATA(0x00);
    LCD_WR_REG(0x26);    
    LCD_WR_DATA(0x01);
    LCD_WR_REG(0xE0);    
    LCD_WR_DATA(0x0F);
    LCD_WR_DATA(0x2A);
    LCD_WR_DATA(0x28);
    LCD_WR_DATA(0x08);
    LCD_WR_DATA(0x0E);
    LCD_WR_DATA(0x08);
    LCD_WR_DATA(0x54);
    LCD_WR_DATA(0XA9);
    LCD_WR_DATA(0x43);
    LCD_WR_DATA(0x0A);
    LCD_WR_DATA(0x0F);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_REG(0XE1);    
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x15);
    LCD_WR_DATA(0x17);
    LCD_WR_DATA(0x07);
    LCD_WR_DATA(0x11);
    LCD_WR_DATA(0x06);
    LCD_WR_DATA(0x2B);
    LCD_WR_DATA(0x56);
    LCD_WR_DATA(0x3C);
    LCD_WR_DATA(0x05);
    LCD_WR_DATA(0x10);
    LCD_WR_DATA(0x0F);
    LCD_WR_DATA(0x3F);
    LCD_WR_DATA(0x3F);
    LCD_WR_DATA(0x0F);
    LCD_WR_REG(0x2B);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x01);
    LCD_WR_DATA(0x3f);
    LCD_WR_REG(0x2A);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0xef);
    LCD_WR_REG(0x11); 
    delay(120);
    LCD_WR_REG(0x29); 
    LCD_WriteReg(0x36,(1<<3)|(1<<5)|(1<<6)); 
}

/*     */
void LCD_Clear(u16 Color)
{
    unsigned int i;
    LCD_SetWindows(0,0,LCD_W-1,LCD_H-1);
    for(i=0;i<LCD_H*LCD_W;i++)
    {
        Lcd_WriteData_16Bit(Color);
    }
}

/*    raw  (        RGB565) */
void LCD_draw_image(char *file){
    int fd = open(file, O_RDWR);
    if(fd < 0){
        perror("Open file");
        exit(1);
    }
    u16 buffer[128];
    LCD_SetWindows(0,0,LCD_W-1,LCD_H-1);
    while(1){
        int nread = read(fd, buffer, 256);
        if(nread == 0 || nread < 0)
            break;
        /* buffer[i] - 2 ,   nread/2  */
        for(int i=0; i < nread/2; i++){
            Lcd_WriteData_16Bit(buffer[i]);
        }
    }
    close(fd);
}
      
      





Raspberry



raspberry pi 3 raspbian lite ( 4.14). GUI lxde xinit.







sudo apt-get install lxde xinit
      
      





GPIO









LCD raspberry



  • LCD Data 0 -> GPIO 12
  • LCD Data 1 -> GPIO 13
  • ...
  • LCD Data 7 -> GPIO 19
  • LCD CS -> GPIO 20
  • LCD RS -> GPIO 21
  • LCD RST -> GPIO 22
  • LCD WR -> GPIO 23
  • LCD RD -> GRPIO 24
  • LCD 5V -> 5V
  • LCD GND -> Ground


GPIO



raspberry GPIO . BCM 2837 32 GPFSEL0-5 GPIO. GPIO 3 . 0 2-0 GPFSEL0, 1 5-3 .. 10 GPIO. 000 input, 001 output. :







/*  rpi_gpio.h */
/*  input  */
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
/*  output  */
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
      
      





0 — 31 output 1 GPSET0. GPIO n 1, , n- 1. , 1 GPIO 10 11 GPSET0 0b11 << 10.







, 0 GPCLR0.







/*  1  GPIO, , 1  GPIO 10 - GPIO_SET = 1<<10 */
#define GPIO_SET *(gpio+7)
/*   0  GPIO, , 0  GPIO 10 - GPIO_CLR = 1<<10 */
#define GPIO_CLR *(gpio+10)
      
      





gpio — 0x3F200000 ( mmap ). *gpio GPFSEL0. *(gpio+7) GPSET0. *(gpio+10) GPCLR0.







gpio
/*  rpi_gpio.c */
int setup_rpi_gpio()
{
    unsigned int gpio_base_addr = 0x3F200000;

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      return -1;
   }

   /* mmap GPIO */
   gpio_map = mmap(
      NULL,             //Any adddress in our space will do
      BLOCK_SIZE,       //Map length
      PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
      MAP_SHARED,       //Shared with other processes
      mem_fd,           //File to map
      gpio_base_addr    //Offset to GPIO peripheral
   );

   close(mem_fd); //No need to keep mem_fd open after mmap

   if (gpio_map == MAP_FAILED) {
      printf("mmap error %d\n", (int)gpio_map);//errno also set!
      return -1;
   }

   // Always use volatile pointer!
   gpio = (volatile uint32_t *)gpio_map;
   return 0;
}
      
      





LCD c raspberry



LCD :
/*  lcd.h */
#define BIT_BASE 12
#define CS   20
#define RS   21
#define RST  22
#define WR   23
#define RD   24

#define LCD_CS_SET  GPIO_SET=(1<<CS)
#define LCD_RS_SET  GPIO_SET=(1<<RS)
#define LCD_RST_SET GPIO_SET=(1<<RST)
#define LCD_WR_SET  GPIO_SET=(1<<WR)
#define LCD_RD_SET  GPIO_SET=(1<<RD)

#define LCD_CS_CLR  GPIO_CLR=(1<<CS)
#define LCD_RS_CLR  GPIO_CLR=(1<<RS)
#define LCD_RST_CLR GPIO_CLR=(1<<RST)
#define LCD_WR_CLR  GPIO_CLR=(1<<WR)
#define LCD_RD_CLR  GPIO_CLR=(1<<RD)

#define DATAOUT(x) GPIO_SET=(x<<BIT_BASE);GPIO_CLR=(x<<BIT_BASE)^(0xFF<<BIT_BASE)
      
      





LCD user space



kernel, LCD user space. image.jpg raw 320x240. output.raw 16 (RGB565):







mogrify -format bmp -resize 320 -crop 320x240 image.jpg
ffmpeg -vcodec bmp -i image.bmp -vcodec rawvideo -f rawvideo -pix_fmt rgb565 output.raw
      
      





output.raw LCD:







/*  main.c */
int main(int argc , char *argv[]){
    if( setup_rpi_gpio() ) {
        printf("Cannot map GPIO memory, probably use <sudo>\n");
        return -1;
    }
    for(int i = BIT_BASE; i <= RD; i++){
        INP_GPIO(i);
        OUT_GPIO(i);
    }
    //set BITS_BASE - RD to 1
    GPIO_SET = 0xFFF<<12;
    GPIO_SET = 1 << RD;
    LCD_Init();

    if(argc >= 2){
        LCD_draw_image(argv[1]);
    }
}
      
      





gcc main.c rpi_gpio.c lcd.c -o main
sudo ./main output.raw
      
      













, .







raspbian , linux, , . reference . raspbian.







git clone --depth=1 -b rpi-4.14.y https://github.com/raspberrypi/linux.git
cd linux
KERNEL=kernel7
make bcm2709_defconfig
make -j4 zImage modules dtbs
sudo make modules_install
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
sudo cp arch/arm/boot/zImage /boot/$KERNEL.img

      
      





make, Makefile:







Makefile
ifeq ($(KERNELRELEASE),)

    KERNELDIR ?= /lib/modules/$(shell uname -r)/build    
    PWD := $(shell pwd)

modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    #  ,   vfb.c,   vfb.o    
    obj-m := lcd_drv_simple.o
endif
      
      







, .







(vfb.c). , , /dev/fbX (X — ). cat /dev/fbX. ( , ).







.







make
sudo cp vfb.ko /lib/modules/$(uname -r)/extra/
#    
sudo depmod
#  
sudo modprobe vfb_enable=1
#       (16 ,  RGB565)
fbset -fb /dev/fb1 -g 320 240 320 240 16
      
      





framebuffer (/dev/fb1). - ,







sudo apt-get install fbi
# fbi     ,    ssh  sudo  -T 1     
sudo fbi -a -d /dev/fb1 -T 1 image.jpg
      
      











cat /dev/fb1 > scrn.raw
      
      





gimp raw rgb565. , .









LCD. , . , LCD , .







(1/0) ( I/O ):







/*  lcd_drv_simple.c */
static void inp_gpio(u32 g){
    u32 *addr = gpio+g/10;
    u32 val = readl(addr);
    u32 tmp =  ~(7<<((g%10)*3));
    val &= tmp;
    writel(val,addr);
}
static void out_gpio(u32 g){
    u32 *addr = gpio+g/10;
    u32 val = readl(addr);
    u32 tmp =  (1<<(((g)%10)*3));
    val |= tmp;
    writel(val,addr);
}
static void GPIO_SET(u32 val){
    writel(val,gpio+7);
}
static void GPIO_CLR(u32 val){
    writel(val,gpio+10);
}
      
      





gpio ioremap:







gpio = ioremap(PORT, RANGE);
      
      





:
u32 *gpio;
static unsigned PORT = 0x3F200000;
static unsigned RANGE =  0x40;

#define W 320
#define H 240

static struct fb_fix_screeninfo ili9341_fix  = {
        .type        = FB_TYPE_PACKED_PIXELS,
        .visual      = FB_VISUAL_TRUECOLOR,
        .accel       = FB_ACCEL_NONE,
        .line_length = W * 2,
};

static struct fb_var_screeninfo ili9341_var  = {
        .xres        = W,
        .yres        = H,
        .xres_virtual    = W,
        .yres_virtual    = H,
        .width        = W,
        .height        = H,
        .bits_per_pixel = 16,
        .red         = {11, 5, 0}, /*  11 , 5     */
        .green         = {5, 6, 0}, /*  5 , 6     */
        .blue         = {0, 5, 0}, /*  0 , 5     */
        .activate     = FB_ACTIVATE_NOW,
        .vmode     = FB_VMODE_NONINTERLACED,
};

/*       */
static struct fb_ops ili9341_fbops = {
        .owner        = THIS_MODULE,
        .fb_write     = fb_sys_write,
        .fb_fillrect  = sys_fillrect,
        .fb_copyarea  = sys_copyarea,
        .fb_imageblit = sys_imageblit,
        .fb_setcolreg   = ili9341_setcolreg,
};

/*    probe  remove */
struct platform_driver ili9341_driver = {
        .probe = ili9341_probe,
        .remove = ili9341_remove,
        .driver = { .name = "my_fb_driver" }
};

/*   ili9341_update,   (     delay) */
static struct fb_deferred_io ili9341_defio = {
        .delay          = HZ / 25,
        .deferred_io    = &ili9341_update,
};
      
      





:
static int  ili9341_probe(struct platform_device *dev)
{
    int ret = 0;
    struct ili9341 *item;
    struct fb_info *info;
    unsigned char  *videomemory;
    printk("ili9341_probe\n");

    /*        */
    item = kzalloc(sizeof(struct ili9341), GFP_KERNEL);
    if (!item) {
        printk(KERN_ALERT "unable to kzalloc for ili9341\n");
        ret = -ENOMEM;
        goto out;
    }

    /*   */
    item->dev = &dev->dev;
    dev_set_drvdata(&dev->dev, item);

    /*      fb_info */
    info = framebuffer_alloc(0, &dev->dev);
    if (!info) {
        ret = -ENOMEM;
        printk(KERN_ALERT "unable to framebuffer_alloc\n");
        goto out_item;
    }
    item->info = info;

    /*   fb_info   */
    info->par = item;
    info->dev = &dev->dev;
    info->fbops = &ili9341_fbops;
    info->flags = FBINFO_FLAG_DEFAULT;
    info->fix = ili9341_fix;
    info->var = ili9341_var;
    info->fix.smem_len = VIDEOMEM_SIZE; //   
    info->pseudo_palette = &pseudo_palette;

    /*    ,    ,  /dev/fbX */
    videomemory=vmalloc(info->fix.smem_len);
    if (!videomemory)
    {
        printk(KERN_ALERT "Can not allocate memory for framebuffer\n");
        ret = -ENOMEM;
        goto out_info;
    }

    /*     fb_info      ili9341    */
    info->fix.smem_start =(unsigned long)(videomemory);
    info->screen_base = (char __iomem *)info->fix.smem_start;
    item->videomem = videomemory;

    /*       */
    info->fbdefio = &ili9341_defio;
    fb_deferred_io_init(info);

    /*    fb_info  */
    ret = register_framebuffer(info);
    if (ret < 0) {
        printk(KERN_ALERT "unable to register_frambuffer\n");
        goto out_pages;
    }

    if (ili9341_setup(item)) goto out_pages;
    return ret;

    out_pages:
    kfree(videomemory);
    out_info:
    framebuffer_release(info);
    out_item:
    kfree(item);
    out:
    return ret;
}

int ili9341_setup(struct ili9341 *item)
{
    int i;

    /*       GPIO  gpio */
    gpio = ioremap(PORT, RANGE);
    if(gpio == NULL){
        printk(KERN_ALERT "ioremap error\n");
        return 1;
    }

    /*  LCD */
    for(i = BIT_BASE; i <= RD; i++){
        inp_gpio(i);
        out_gpio(i);
    }
    GPIO_SET(0xFFF<<12);
    GPIO_SET(1 << RD);
    LCD_Init();
    printk("ili9341_setup\n");
    return 0;
}
static void ili9341_update(struct fb_info *info, struct list_head *pagelist)
{
    /*        */
    struct ili9341 *item = (struct ili9341 *)info->par;
    /*   */
    u16 *videomemory = (u16 *)item->videomem;
    int i, j, k;

    /*    */
    LCD_SetWindows(0,0,LCD_W-1,LCD_H-1);    
    for(i = 0; i < LCD_W * LCD_H; i++){
        /*          LCD */
        Lcd_WriteData_16Bit(readw(videomemory));
        videomemory++;
    }
}
      
      





LCD



. ,







make
sudo cp lcd_drv_simple.ko /lib/modules/$(uname -r)/extra/
sudo depmod
sudo modprobe lcd_drv_simple
      
      





:







cat /dev/urandom > /dev/fb1
      
      





/dev/fbX :







sudo fbi -a -d /dev/fb1 -T 1 image.jpg
mplayer -vo fbdev:/dev/fb1 video.mp4
      
      





LCD. Desktop environment (DE) (, raspbian), :







sudo apt-get install lxde
      
      





/etc/X11/xorg.conf:







Section "Device"
    Identifier "FBDEV"
    Driver "fbdev"
    Option "fbdev" "/dev/fb1"
EndSection

      
      





/etc/rc.local:







/sbin/modprobe lcd_drv_simple
      
      





LCD .









, . . Deferred_io , ili9341_update , . .. , 4096 ( ).







  • 4096 6 128 7 , .. 4096 = 320*2*6 + 128*2 (2 )
  • 4096 129 7 , 384 (128*2 + 384 = 640), 5 256 6 (4096 = 384 + 640*5 + 512).


, , 5 . 5 . 37, .. 2048 :







/*  lcd_drv_fast.c */

/*    ,      , ..  ili9341_touch  raspberry     (..     ,  toUpdate  */ 
static void ili9341_update(struct fb_info *info, struct list_head *pagelist)
{
    struct ili9341 *item = (struct ili9341 *)info->par;
    struct page *page;
    int i;    
    /*     1  toUpdate , toUpdate      -2 */ 
    list_for_each_entry(page, pagelist, lru)
    {
        atomic_dec(&item->videopages[page->index].toUpdate);
    }
    for (i=0; i<FP_PAGE_COUNT; i++)
    {
        /*     toUpdate  1.    ,   1    -1.  ,    -1  ,           */
        if(atomic_inc_and_test(&item->videopages[i].toUpdate)){
            atomic_dec(&item->videopages[i].toUpdate);
        }
        else
        {
            draw(item, i);          
        }
    }

}
static void draw(struct ili9341 *item, int page){
    int xs,ys,i;
    /*      */
    u16 *videomemory = (u16*)(item->videomem + PAGE_SIZE*page);

    /*  LCD,     */
    ys = (((unsigned long)(PAGE_SIZE*page)>>1)/W);

    /*   ,   */
    if (page == 37){
        // write PAGE_SIZE / 2;
        //write 128 bytes
        LCD_SetWindows(256, ys, LCD_W-1, ys);
        for(i = 0; i < 128 / 2; i++){
            Lcd_WriteData_16Bit(readw(videomemory));
            videomemory++;
        }
        //write 3 lines
        LCD_SetWindows(0, ys+1, LCD_W-1, ys+6);
        for(i = 0; i < 640 * 3 / 2; i++){
            Lcd_WriteData_16Bit(readw(videomemory));
            videomemory++;
        }

    }
    else{
        switch (page % 5){
        //xs = 0. write full six lines and 256 bytes
        //640 * 6 + 256
        case 0:
            //write 6 lines
            LCD_SetWindows(0,ys,LCD_W-1,ys + 5);
            for(i = 0; i < 640 * 6 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            //write 256 bytes
            LCD_SetWindows(0, ys+6, 256/2-1, ys + 6); //7th line from x = 0 to x = 256/2
            for(i = 0; i < 256 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            break;
        //xs = 128 (256 bytes). write 384 bytes, 5 full lines and 512 bytes
        //384 + 640 * 5 + 512
        case 1:
            //write 384 bytes
            LCD_SetWindows(256/2, ys, LCD_W-1, ys);
            for(i = 0; i < 384 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            //write 5 lines
            LCD_SetWindows(0, ys+1, LCD_W-1, ys+5);
            for(i = 0; i < 640 * 5 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            //write 512 bytes
            LCD_SetWindows(0, ys+6, 512/2-1, ys+6);
            for(i = 0; i < 512 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            break;
        //xs = 256 (512 bytes). write 128 bytes, then 6 full lines and 128 bytes
        //128 + 640*6 + 128
        case 2:
            //write 128 bytes
            LCD_SetWindows(256, ys, LCD_W-1, ys);
            for(i = 0; i < 128 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            //write 6 lines
            LCD_SetWindows(0, ys+1, LCD_W-1, ys+6);
            for(i = 0; i < 640 * 6 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            //write 128 bytes
            LCD_SetWindows(0, ys+7, 128/2-1, ys+7);
            for(i = 0; i < 128 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            break;
        //xs = 64 (128 /2). write 512 bytes, then 5 lines and 384 bytes
        //512 + 640*5 + 384
        case 3:
            //write 512 bytes
            LCD_SetWindows(64, ys, LCD_W-1, ys);
            for(i = 0; i < 512 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            //write 5 lines
            LCD_SetWindows(0, ys+1, LCD_W-1, ys+5);
            for(i = 0; i < 640 * 5 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            //write 384 bytes
            LCD_SetWindows(0, ys+6, 384/2-1, ys+6);
            for(i = 0; i < 384 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            break;
        //xs = 384/2. write 256 bytes, then 6 full lines
        //256 + 640*6
        case 4:
            //write 256 bytes
            LCD_SetWindows(384/2, ys, LCD_W-1, ys);
            for(i = 0; i < 256 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            LCD_SetWindows(0, ys+1, LCD_W-1, ys+6);
            for(i = 0; i < 640 * 6 / 2; i++){
                Lcd_WriteData_16Bit(readw(videomemory));
                videomemory++;
            }
            break;
        default: break;

        }
    }
}

      
      





ili9341 ili9341_probe:







struct videopage
{
    atomic_t                toUpdate;
};
struct ili9341 {
    struct device *dev;
    struct fb_info *info;
    unsigned char *videomem;
    /*       */
    struct videopage videopages[FP_PAGE_COUNT];
};
static int  ili9341_probe(struct platform_device *dev){
    ...
    /*        */
    for(i=0;i<FP_PAGE_COUNT;i++)
    {
        atomic_set(&item->videopages[i].toUpdate, -1);      
    }
}

      
      





ili9341_fbops , , ili9341_touch. , , ili9341_fbops, ili9341_update . , raspbian .







static struct fb_ops ili9341_fbops = {
        .owner        = THIS_MODULE,
        .fb_write     = ili9341_write,
        .fb_fillrect  = ili9341_fillrect,
        .fb_copyarea  = ili9341_copyarea,
        .fb_imageblit = ili9341_imageblit,
        .fb_setcolreg   = ili9341_setcolreg,
};

static ssize_t ili9341_write(struct fb_info *p, const char __user *buf, size_t count, loff_t *ppos){
    ssize_t retval;
    printk("ili9341_write\n");
    retval=fb_sys_write(p, buf, count, ppos);
    ili9341_touch(p, 0, 0, p->var.xres, p->var.yres);
    return retval;
}
static void ili9341_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
{
    printk("ili9341_fillrect\n");
    sys_fillrect(p, rect);
    ili9341_touch(p, rect->dx, rect->dy, rect->width, rect->height);
}
static void ili9341_imageblit(struct fb_info *p, const struct fb_image *image)
{
    printk("ili9341_imageblit\n");
    sys_imageblit(p, image);
    ili9341_touch(p, image->dx, image->dy, image->width, image->height);
}

static void ili9341_copyarea(struct fb_info *p, const struct fb_copyarea *area)
{
    printk("ili9341_copyarea\n");
    sys_copyarea(p, area);
    ili9341_touch(p, area->dx, area->dy, area->width, area->height);
}
static void ili9341_touch(struct fb_info *info, int x, int y, int w, int h)
{

    struct ili9341 *item = (struct ili9341 *)info->par;
    int firstPage;
    int lastPage;
    int i;
    printk("touch x %d, y %d, w %d, h %d",x,y,w,h);
    firstPage=((y*W)+x)*BYTE_DEPTH/PAGE_SIZE-1;
    lastPage=(((y+h)*W)+x+w)*BYTE_DEPTH/PAGE_SIZE+1;

    if(firstPage<0)
        firstPage=0;
    if(lastPage>FP_PAGE_COUNT)
        lastPage=FP_PAGE_COUNT;

    for(i=firstPage;i<lastPage;i++)
        atomic_dec(&item->videopages[i].toUpdate);

    schedule_delayed_work(&info->deferred_work, info->fbdefio->delay);
}
      
      







. raspberry . / , HDMI. LCD.







, "" LCD, linux 640x480. 640x480, LCD . ili9341_update:







/*  lcd_drv_simple_640_480.c */

#define W 320*2
#define H 240*2

/*   ili9341_update     */
for(j = 0; j < H; j++){
    if (j % 2 == 1){ //skip
        videomemory += W;
    }
    else{
        for(i = 0; i < W; i += 2){
            Lcd_WriteData_16Bit(readw(videomemory));
            videomemory += 2;               
        }
    }
}
      
      





. /boot/config.txt:







[all]
framebuffer_depth=16
      
      





xinerama :







sudo apt-get install libxinerama-dev
      
      





/etc/X11/xorg.conf







xorg.conf
Section "Device"
        Identifier      "LCD"
        Driver          "fbdev"
        Option          "fbdev" "/dev/fb1"
        Option          "ShadowFB" "off"
        Option          "SwapbuffersWait" "true"
EndSection

Section "Device"
        Identifier      "HDMI"
        Driver          "fbdev"
        Option          "fbdev" "/dev/fb0"
        Option          "ShadowFB" "off"
        Option          "SwapbuffersWait" "true"
EndSection

Section "Monitor"
        Identifier      "LCD-monitor"
        Option          "RightOf" "HDMI-monitor"
EndSection

Section "Monitor"
        Identifier      "HDMI-monitor"
        Option          "Primary" "true"        
EndSection

Section "Screen"
        Identifier      "screen0"
        Device          "LCD"
        Monitor         "LCD-monitor"
EndSection

Section "Screen"
        Identifier      "screen1"
        Device          "HDMI" 
        Monitor         "HDMI-monitor"
EndSection

Section "ServerLayout"
        Identifier      "default"
        Option          "Xinerama" "on"
        Option          "Clone" "off"
        Screen 0        "screen0" RightOf "screen1"
        Screen 1        "screen1" 
EndSection
      
      





:









. github.








All Articles