├── photos ├── connector.JPG ├── full_view.JPG └── X_running_screen.JPG ├── Makefile ├── README └── ili9341.c /photos/connector.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammyizimmy/ili9341/HEAD/photos/connector.JPG -------------------------------------------------------------------------------- /photos/full_view.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammyizimmy/ili9341/HEAD/photos/full_view.JPG -------------------------------------------------------------------------------- /photos/X_running_screen.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammyizimmy/ili9341/HEAD/photos/X_running_screen.JPG -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += ili9341.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ili9341 2 | ======== 3 | A Raspberry pi framebuffer driver for the ili9341 lcd driver. 4 | I am using the NHD-2.4-240320SF-CTXI#-T1 (http://www.newhavendisplay.com/nhd24240320sfctxit1-p-3975.html) 5 | 6 | 7 | NHD-2.4-240320SF-CTXI#-T1 Datasheet (http://www.newhavendisplay.com/specs/NHD-2.4-240320SF-CTXI-T1.pdf) 8 | ili9341 Datasheet (http://www.displayfuture.com/Display/datasheet/controller/ILI9341.pdf) 9 | 10 | Installing: 11 | download the source 12 | run make 13 | sudo insmod ili9341.ko 14 | this will create /dev/fb1. 15 | Use the new framebuffer (https://github.com/notro/fbtft/wiki/Framebuffer-use) 16 | 17 | 18 | The physical part of things. 19 | 20 | R-PI Model B v2 GPIO 21 | LCD GPIO p1 p2 GPIO LCD 22 | 3v--* o--5v 23 | 02--o o--5v 24 | 03--o o--GND 25 | DC----04--o o--14---RD 26 | GND--o o--15---WR 27 | RESET-17--o o--18---DB10 28 | CS----27--o o--GND 29 | 22--o o--23---DB11 30 | 3v--o o--24---DB12 31 | IM0---10--o o--GND 32 | DB8---09--o o--25---DB13 33 | DB9---11--o o--08---DB14 34 | GND--o o--07---DB15 35 | p25 p26 36 | 37 | Photos: See photos folder. 38 | -------------------------------------------------------------------------------- /ili9341.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ili9342 LCD for Raspberry Pi Model B rev2 3 | */ 4 | 5 | #define BCM2708_PERI_BASE 0x20000000 6 | #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | #define BLOCKSIZE (4*1024) 24 | 25 | // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) 26 | #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 27 | #define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) 28 | #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 29 | 30 | #define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0 31 | #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0 32 | 33 | #define DATA0 9 34 | #define DATA1 11 35 | #define DATA2 18 36 | #define DATA3 23 37 | #define DATA4 24 38 | #define DATA5 25 39 | #define DATA6 8 40 | #define DATA7 7 41 | 42 | #define DC 4 43 | #define CS 27 44 | #define RD 14 45 | #define RW 15 46 | #define IM0 10 47 | #define RESET 17 48 | 49 | #define ORIENTATION 0 //0=LANDSCAPE 1=PORTRAIT 50 | 51 | #define DISPLAY_WIDTH 320 52 | #define DISPLAY_HEIGHT 240 53 | 54 | #define DISPLAY_BPP 16 55 | 56 | 57 | 58 | volatile unsigned *gpio; 59 | 60 | 61 | // Set to output 62 | static void gpio_setoutput(char g) 63 | { 64 | INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO 65 | OUT_GPIO(g); 66 | } 67 | 68 | // Set state 1=high 0=low 69 | static void gpio_setstate(char g,char state) 70 | { 71 | (state) ? (GPIO_SET = 1<> 0) & 0x01)); 125 | gpio_setstate(DATA1,((data >> 1) & 0x01)); 126 | gpio_setstate(DATA2,((data >> 2) & 0x01)); 127 | gpio_setstate(DATA3,((data >> 3) & 0x01)); 128 | gpio_setstate(DATA4,((data >> 4) & 0x01)); 129 | gpio_setstate(DATA5,((data >> 5) & 0x01)); 130 | gpio_setstate(DATA6,((data >> 6) & 0x01)); 131 | gpio_setstate(DATA7,((data >> 7) & 0x01)); 132 | } 133 | 134 | // write command 135 | static void tft_command_write(char command) 136 | { 137 | gpio_setstate(DC,0); 138 | gpio_set_parallel_data(command); 139 | gpio_setstate(RW,0); 140 | gpio_setstate(RW,1); 141 | } 142 | 143 | // write data 144 | static void tft_data_write(char data) 145 | { 146 | gpio_setstate(DC,1); 147 | gpio_set_parallel_data(data); 148 | gpio_setstate(RW,0); 149 | gpio_setstate(RW,1); 150 | } 151 | 152 | // initialization of ili9341 153 | static void tft_init(struct fb_info *info) 154 | { 155 | 156 | tft_hard_reset(); 157 | 158 | tft_command_write(0x28); //display OFF 159 | tft_command_write(0x11); //exit SLEEP mode 160 | tft_data_write(0x00); 161 | tft_command_write(0xCB); //Power Control A 162 | tft_data_write(0x39); //always 0x39 163 | tft_data_write(0x2C); //always 0x2C 164 | tft_data_write(0x00); //always 0x 165 | tft_data_write(0x34); //Vcore = 1.6V 166 | tft_data_write(0x02); //DDVDH = 5.6V 167 | tft_command_write(0xCF); //Power Control B 168 | tft_data_write(0x00); //always 0x 169 | tft_data_write(0x81); //PCEQ off 170 | tft_data_write(0x30); //ESD protection 171 | tft_command_write(0xE8); //Driver timing control A 172 | tft_data_write(0x85); //non‐overlap 173 | tft_data_write(0x01); //EQ timing 174 | tft_data_write(0x79); //Pre‐charge timing 175 | tft_command_write(0xEA); //Driver timing control B 176 | tft_data_write(0x00); //Gate driver timing 177 | tft_data_write(0x00); //always 0x 178 | tft_command_write(0xED); //Power‐On sequence control 179 | tft_data_write(0x64); //soft start 180 | tft_data_write(0x03); //power on sequence 181 | tft_data_write(0x12); //power on sequence 182 | tft_data_write(0x81); //DDVDH enhance on 183 | tft_command_write(0xF7); //Pump ratio control 184 | tft_data_write(0x20); //DDVDH=2xVCI 185 | tft_command_write(0xC0); //power control 1 186 | tft_data_write(0x26); 187 | tft_data_write(0x04); //second parameter for ILI9340 (ignored by ILI9341) 188 | tft_command_write(0xC1); //power control 2 189 | tft_data_write(0x11); 190 | tft_command_write(0xC5); //VCOM control 1 191 | tft_data_write(0x35); 192 | tft_data_write(0x3E); 193 | tft_command_write(0xC7); //VCOM control 2 194 | tft_data_write(0xBE); 195 | tft_command_write(0x36); //memory access control = BGR 196 | tft_data_write(0x88); 197 | tft_command_write(0xB1); //frame rate control 198 | tft_data_write(0x00); 199 | tft_data_write(0x10); 200 | tft_command_write(0xB6); //display function control 201 | tft_data_write(0x0A); 202 | tft_data_write(0xA2); 203 | tft_command_write(0x3A); //pixel format = 16 bit per pixel 204 | tft_data_write(0x55); 205 | tft_command_write(0xF2); //3G Gamma control 206 | tft_data_write(0x02); //off 207 | tft_command_write(0x26); //Gamma curve 3 208 | tft_data_write(0x01); 209 | tft_command_write(0x2A); //column address set 210 | tft_data_write(0x00); 211 | tft_data_write(0x00); //start 0x00 212 | tft_data_write(0x00); 213 | tft_data_write(0xEF); //end 0xEF 214 | tft_command_write(0x2B); //page address set 215 | tft_data_write(0x00); 216 | tft_data_write(0x00); //start 0x00 217 | tft_data_write(0x01); 218 | tft_data_write(0x3F); //end 0x013F 219 | 220 | tft_command_write(0x29); //display ON 221 | 222 | } 223 | 224 | // write memory to TFT 225 | static void ili9341_update_display_area(const struct fb_image *image) 226 | { 227 | int x,y; 228 | 229 | // set column 230 | (ORIENTATION) ? tft_command_write(0x2B) : tft_command_write(0x2A); 231 | 232 | tft_data_write(image->dx >> 8); 233 | tft_data_write(image->dx); 234 | 235 | tft_data_write((image->dx + image->width) >> 8); 236 | tft_data_write(image->dx + image->width); 237 | // set row 238 | (ORIENTATION) ? tft_command_write(0x2A) : tft_command_write(0x2B); 239 | 240 | tft_data_write(image->dy >> 8); 241 | tft_data_write(image->dy); 242 | 243 | tft_data_write((image->dy + image->height) >> 8); 244 | tft_data_write(image->dy + image->height); 245 | 246 | tft_command_write(0x2C); //Memory Write 247 | 248 | if(ORIENTATION == 0){ 249 | for(y=0;y < image->width ;y++){ 250 | for(x=0;x < image->height ;x++){ 251 | tft_data_write(image->data[(image->dx * (2 * image->width)) + (image->dy * 2) + 1]); 252 | tft_data_write(image->data[(image->dx * (2 * image->width)) + (image->dy * 2) + 2]); 253 | } 254 | } 255 | }else{ 256 | for(y=0;y < image->width ;y++){ 257 | for(x=0;x < image->height ;x++){ 258 | tft_data_write(image->data[(image->dx * (2 * image->width)) + (image->dy * 2) + 1]); 259 | tft_data_write(image->data[(image->dx * (2 * image->width)) + (image->dy * 2) + 2]); 260 | } 261 | } 262 | } 263 | 264 | tft_command_write(0x29); //display ON 265 | } 266 | 267 | 268 | static void ili9341_update_display_color_area(const struct fb_fillrect *rect) 269 | { 270 | int x,y; 271 | // set column 272 | (ORIENTATION) ? tft_command_write(0x2B) : tft_command_write(0x2A); 273 | 274 | tft_data_write(rect->dx >> 8); 275 | tft_data_write(rect->dx); 276 | 277 | tft_data_write((rect->dx + rect->width) >> 8); 278 | tft_data_write(rect->dx + rect->width); 279 | // set row 280 | 281 | (ORIENTATION) ? tft_command_write(0x2A) : tft_command_write(0x2B); 282 | 283 | tft_data_write(rect->dy >> 8); 284 | tft_data_write(rect->dy); 285 | 286 | tft_data_write((rect->dy + rect->height) >> 8); 287 | tft_data_write(rect->dy + rect->height); 288 | 289 | tft_command_write(0x2C); //Memory Write 290 | 291 | if(ORIENTATION == 0){ 292 | for(y=0;y < rect->width ;y++){ 293 | for(x=0;x < rect->height ;x++){ 294 | tft_data_write(rect->color); 295 | tft_data_write(rect->color >> 8); 296 | } 297 | } 298 | }else{ 299 | for(y=0;y < rect->height ;y++){ 300 | for(x=0;x < rect->width ;x++){ 301 | tft_data_write(rect->color); 302 | tft_data_write(rect->color >> 8); 303 | } 304 | } 305 | } 306 | 307 | tft_command_write(0x29); //display ON 308 | } 309 | 310 | static void ili9341_update_display(const struct fb_info *info) 311 | { 312 | int x,y; 313 | 314 | tft_command_write(0x2C); //Memory Write 315 | 316 | if(ORIENTATION == 0){ 317 | for(y=0;y < DISPLAY_WIDTH ;y++){ 318 | for(x=0;x < DISPLAY_HEIGHT ;x++){ 319 | tft_data_write(info->screen_base[(x * (2 * DISPLAY_WIDTH)) + (y * 2) + 1]); 320 | tft_data_write(info->screen_base[(x * (2 * DISPLAY_WIDTH)) + (y * 2) + 2]); 321 | } 322 | } 323 | }else{ 324 | for(y=(DISPLAY_HEIGHT - 1);y >= 0 ;y--){ 325 | for(x=0;x < DISPLAY_WIDTH ;x++){ 326 | tft_data_write(info->screen_base[(y * (2 * DISPLAY_WIDTH)) + (x * 2) + 1]); 327 | tft_data_write(info->screen_base[(y * (2 * DISPLAY_WIDTH)) + (x * 2) + 2]); 328 | } 329 | } 330 | } 331 | tft_command_write(0x29); //display ON 332 | } 333 | 334 | static void ili9341_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 335 | { 336 | //printk(KERN_INFO "fb%d: ili9341_fillrect\n", info->node); 337 | //ili9341_update_display_color_area(rect); 338 | ili9341_update_display(info); 339 | } 340 | 341 | static void ili9341_copyarea(struct fb_info *info, const struct fb_copyarea *area) 342 | { 343 | //printk(KERN_INFO "fb%d: ili9341_copyarea\n", info->node); 344 | ili9341_update_display(info); 345 | } 346 | 347 | static void ili9341_imageblit(struct fb_info *info, const struct fb_image *image) 348 | { 349 | //printk(KERN_INFO "fb%d: ili9341_imageblit\n", info->node); 350 | //ili9341_update_display_area(image); 351 | ili9341_update_display(info); 352 | } 353 | 354 | static ssize_t ili9341_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos) 355 | { 356 | unsigned long p = *ppos; 357 | void *dst; 358 | int err = 0; 359 | unsigned long total_size; 360 | 361 | if (info->state != FBINFO_STATE_RUNNING) 362 | return -EPERM; 363 | 364 | total_size = info->screen_size; 365 | 366 | if (total_size == 0) 367 | total_size = info->fix.smem_len; 368 | 369 | if (p > total_size) 370 | return -EFBIG; 371 | 372 | if (count > total_size) { 373 | err = -EFBIG; 374 | count = total_size; 375 | } 376 | 377 | if (count + p > total_size) { 378 | if (!err) 379 | err = -ENOSPC; 380 | 381 | count = total_size - p; 382 | } 383 | 384 | dst = (void __force *) (info->screen_base + p); 385 | 386 | if (info->fbops->fb_sync) 387 | info->fbops->fb_sync(info); 388 | 389 | if (copy_from_user(dst, buf, count)) 390 | err = -EFAULT; 391 | 392 | if (!err) 393 | *ppos += count; 394 | 395 | ili9341_update_display(info); 396 | 397 | return (err) ? err : count; 398 | } 399 | 400 | static ssize_t ili9341_read(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos) 401 | { 402 | unsigned long p = *ppos; 403 | void *dst; 404 | int err = 0; 405 | unsigned long total_size; 406 | 407 | if (info->state != FBINFO_STATE_RUNNING) 408 | return -EPERM; 409 | 410 | total_size = info->screen_size; 411 | 412 | if (total_size == 0) 413 | total_size = info->fix.smem_len; 414 | 415 | if (p > total_size) 416 | return -EFBIG; 417 | 418 | if (count > total_size) { 419 | err = -EFBIG; 420 | count = total_size; 421 | } 422 | 423 | if (count + p > total_size) { 424 | if (!err) 425 | err = -ENOSPC; 426 | 427 | count = total_size - p; 428 | } 429 | 430 | dst = (void __force *) (info->screen_base + p); 431 | 432 | if (info->fbops->fb_sync) 433 | info->fbops->fb_sync(info); 434 | 435 | if (copy_from_user(dst, buf, count)) 436 | err = -EFAULT; 437 | 438 | if (!err) 439 | *ppos += count; 440 | 441 | return (err) ? err : count; 442 | } 443 | 444 | static void ili9341_deferred_io(struct fb_info *info, struct list_head *pagelist) 445 | { 446 | ili9341_update_display(info); 447 | } 448 | 449 | 450 | 451 | static struct fb_fix_screeninfo ili9341_fix = { 452 | .id = "ili9341", 453 | .type = FB_TYPE_PACKED_PIXELS, 454 | .visual = FB_VISUAL_TRUECOLOR, 455 | .accel = FB_ACCEL_NONE, 456 | .xpanstep = 0, 457 | .ypanstep = 0, 458 | .ywrapstep = 0, 459 | .line_length = DISPLAY_WIDTH * DISPLAY_BPP / 8, 460 | }; 461 | 462 | 463 | static struct fb_var_screeninfo ili9341_var = { 464 | .width = DISPLAY_WIDTH, 465 | .height = DISPLAY_HEIGHT, 466 | .bits_per_pixel = DISPLAY_BPP, 467 | .xres = DISPLAY_WIDTH, 468 | .yres = DISPLAY_HEIGHT, 469 | .xres_virtual = DISPLAY_WIDTH, 470 | .yres_virtual = DISPLAY_HEIGHT, 471 | .activate = FB_ACTIVATE_NOW, 472 | .vmode = FB_VMODE_NONINTERLACED, 473 | 474 | .nonstd = 0, 475 | .red.offset = 11, 476 | .red.length = 5, 477 | .green.offset = 5, 478 | .green.length = 6, 479 | .blue.offset = 0, 480 | .blue.length = 5, 481 | .transp.offset = 0, 482 | .transp.length = 0, 483 | }; 484 | 485 | 486 | static struct fb_ops ili9341_ops = { 487 | .owner = THIS_MODULE, 488 | .fb_read = ili9341_read, 489 | .fb_write = ili9341_write, 490 | .fb_fillrect = ili9341_fillrect, 491 | .fb_copyarea = ili9341_copyarea, 492 | .fb_imageblit = ili9341_imageblit, 493 | }; 494 | 495 | static struct fb_deferred_io ili9341_defio = { 496 | .delay = HZ/25, 497 | .deferred_io = ili9341_deferred_io, 498 | }; 499 | 500 | static unsigned int fps; 501 | 502 | 503 | static int ili9341_probe(struct platform_device *pdev) 504 | { 505 | struct fb_info *info; 506 | int retval = -ENOMEM; 507 | int vmem_size; 508 | unsigned char *vmem; 509 | 510 | 511 | vmem_size = ili9341_var.width * ili9341_var.height * ili9341_var.bits_per_pixel/8; 512 | vmem = vzalloc(vmem_size); 513 | if (!vmem) { 514 | return -ENOMEM; 515 | } 516 | memset(vmem, 0, vmem_size); 517 | 518 | 519 | info = framebuffer_alloc(0, &pdev->dev); 520 | if (!info) { 521 | vfree(vmem); 522 | return -ENOMEM; 523 | } 524 | 525 | 526 | info->screen_base = (char __force __iomem*)vmem; 527 | info->fbops = &ili9341_ops; 528 | info->fix = ili9341_fix; 529 | info->fix.smem_start = (unsigned long)vmem; 530 | info->fix.smem_len = vmem_size; 531 | info->var = ili9341_var; 532 | info->flags = FBINFO_DEFAULT | FBINFO_VIRTFB; 533 | 534 | info->fbdefio = &ili9341_defio; 535 | if (0 < fps) { 536 | info->fbdefio->delay = HZ/fps; 537 | } 538 | 539 | fb_deferred_io_init(info); 540 | 541 | retval = register_framebuffer(info); 542 | if (retval < 0) { 543 | framebuffer_release(info); 544 | vfree(vmem); 545 | return retval; 546 | } 547 | 548 | platform_set_drvdata(pdev, info); 549 | 550 | gpio = ioremap(GPIO_BASE, BLOCKSIZE); 551 | 552 | tft_init_board(info); 553 | tft_hard_reset(); 554 | tft_init(info); 555 | 556 | printk(KERN_INFO "fb%d: ili9341 LCD framebuffer device\n", info->node); 557 | return 0; 558 | } 559 | 560 | 561 | 562 | static int ili9341_remove(struct platform_device *dev) 563 | { 564 | struct fb_info *info = platform_get_drvdata(dev); 565 | 566 | if (info) { 567 | unregister_framebuffer(info); 568 | fb_deferred_io_cleanup(info); 569 | vfree((void __force *)info->screen_base); 570 | 571 | 572 | 573 | iounmap(gpio); 574 | 575 | 576 | framebuffer_release(info); 577 | } 578 | return 0; 579 | } 580 | 581 | 582 | static struct platform_driver ili9341_driver = { 583 | .probe = ili9341_probe, 584 | .remove = ili9341_remove, 585 | .driver = { 586 | .name = "ili9341", 587 | }, 588 | }; 589 | 590 | static struct platform_device *ili9341_device; 591 | 592 | static int __init ili9341_init(void) 593 | { 594 | int ret = platform_driver_register(&ili9341_driver); 595 | if (0 == ret) { 596 | ili9341_device = platform_device_alloc("ili9341", 0); 597 | if (ili9341_device) { 598 | ret = platform_device_add(ili9341_device); 599 | } else { 600 | ret = -ENOMEM; 601 | } 602 | if (0 != ret) { 603 | platform_device_put(ili9341_device); 604 | platform_driver_unregister(&ili9341_driver); 605 | } 606 | } 607 | return ret; 608 | } 609 | 610 | static void __exit ili9341_exit(void) 611 | { 612 | platform_device_unregister(ili9341_device); 613 | platform_driver_unregister(&ili9341_driver); 614 | } 615 | 616 | module_param(fps, uint, 0); 617 | MODULE_PARM_DESC(fps, "Frames per second (default 25)"); 618 | 619 | module_init(ili9341_init); 620 | module_exit(ili9341_exit); 621 | 622 | MODULE_DESCRIPTION("ili9341 LCD framebuffer driver"); 623 | MODULE_AUTHOR("sammyizimmy"); 624 | MODULE_LICENSE("GPL"); 625 | 626 | --------------------------------------------------------------------------------