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.
/* 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.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.
/* 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.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:
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. , .
(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
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.