查看: 26056|回复: 10

[教程] DIY-给树莓派找的一张小脸儿---闲置手机TFT屏

  [复制链接]
  • TA的每日心情
    开心
    2015-4-29 08:59
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2015-4-29 11:31:26 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 lucky小黄毛 于 2015-4-29 13:53 编辑

          昨天逛贴吧,发现了一个用手机TFT屏给树莓派做屏幕的教程,抱着试试看的态度我打开了这个帖子,结果看了一会儿便觉得十分高大上,这帖子简直是DIY领域里的高富帅。。。OK,废话不多说,咱们开始:(这是一个漫长的过程,请自备面包和汽水
    先晒下这位大神的成果图:(大家流个口水先~~~)

    1.jpg
    2.jpg
    3.jpg
    4.jpg
    5.jpg
    6.jpg
        温馨提示:此教程只是提供一个思路,不能保证所有的TFT屏幕都能成功哦。移植过程涉及许多技术问题,比较繁琐复杂,如果怕麻烦建议还是购买现成的VGA/HDMI显示器。但是话又说回来,动手的过程远远比结果更有意义。
    各种参数:
        屏幕尺寸所用屏幕大小为2.2英寸,分辨率176x220TFT材质,引脚为18根,主控ICili9225b。其他TFT液晶屏移植的方法差不多,只不过接线、驱动和参数不同,找到显示屏相关资料,照葫芦画瓢;

         接口 GPIO口,5根信号线,两根电源线。采用SPI串行的方式传递数据,其他屏幕大点的一般采用并行传输,这里,树莓派上占用的端口有:GPIO3.3VGNDSPI中的SCLKMOSICE0以及GPIO25GPIO24。串行的接线较少,但是刷新率不高,一般SPICLK信号在16MHZ~32MHZ
    准备工作:
         一:找到液晶屏排线引脚的定义。
    液晶屏背面或者排线上都有一个型号,把那型号Google一下。很多小厂商生产的可能找不到足够的信息,但是实际上只是把一些常用的型号改一下布局,排线顺序还是一样,如果找不到这个型号的信息,可以找类似常用的比对,如果觉得可以冒这风险那就继续。下面是此教程所用数据:
                                            图1.png

    有主控IC型号ili9225b,和引脚的定义:
                                            图2.png
            二、找到主控的型号,以及数据手册(非必须)
    如果足够幸运,第一步就能找到TFT主控IC的型号,如果没有找到的话就麻烦了,据说拆开TFT可以看到主控,愿意冒风险的可以试一试。数据手册将是一个很大的帮助,可以说找到数据手册就一定能做成。下面会给出一个主控支持型号列表,如果你的TFT主控IC在下面的列表的话,没有数据手册也是可以的。如果能找到初始化方式和定位、写入代码,也可以自定义模块。
            三、型号(主控IC或者TFT型号)支持列表:
    st7735r, hx8340bn, ili9225, ili9320, ili9325,ili9341, ssd1289, ssd1351,Adafruit1.3,HY28A,HY28B,fb_hx8353d,Freetronics,ITDB02,Watterott MI0283QT-2,WatterottMI0283QT-9A,Nokia 5110/3310,ILSoft OLEDpioled,Sainsmart 1.8,Sainsmart3.2,ssd1331,tinylcd.com,tm022hdh26
    如果你的屏幕型号或者主控IC型号不在以上的列表里面,那就需要一些初始化参数和指令代码。
    动手:
    【硬件】:
    step1:拆机:
    S1.jpg
    step2:接线:
    S2.jpg


    S3.jpg

    这里采用直接接线到排针的办法,排线间距很小,这可是个技术活。建议还是用TFT转接板,转接板是方便接线的,就是把密集的排线转化成杜邦线的那种板子。淘宝上面有卖,方便得多。另外,接线的同时可以把一些相同的引脚连到一起,比如GND和LED阴极、VCC和LED阳极(注意电压)。
    【软件】:
        首先,解决驱动!
        大部分都是MTK平台的驱动和一些示例程序,本来以为需要自己写Raspberry的驱动,没想到后来在github上面发现了一个专门针对Raspberry的TFT开源驱动项目,以上的支持列表就是从那里来的,传送门:
    https://github.com/notro/fbtft
    点击右侧wiki,你就知道怎么做了。感谢notro提供如此好的项目!
    安装fbtft:
    这里说的都能在https://github.com/notro/fbtft/wiki#step-by-step-using-fbtft找到,英文好的建议直接看那教程。
    以下针对实际情况做了调整。
    一:安装REPO_URI 支持
    sudo wget https://raw.github.com/Hexxeh/rpi-update/master/rpi-update -O /usr/bin/rpi-update && sudo chmod +x/usr/bin/rpi-update
    二、启用SPI (采用并行传输的这一步可以省略)
    打开/etc/modprobe.d/raspi-blacklist.conf,(nano或者vi、vim,su权限),把blacklistspi-bcm2708注释掉或者去掉。就像这样:然后保存

    33.png

    三、下载安装fbtft
    原文提供了两种安装方式,一种作为可加载卸载的模块安装,一种编译到了内核,这里建议用可加载卸载的方式安装
    sudo REPO_URI=https://github.com/notro/rpi-firmware rpi-update
    重启 sudo reboot

    连线加载:
    一:连线
    先提供一张树莓派GPIO图:

    44.jpg

    (DNC是Donot connect的意思),根据电路图,那些引脚实际并不是空着的,所以还是悬空保险些。
    电源连接的时候要一定注意屏幕的电压范围,切勿超压、反向,我的连的都是3.3V。对于串行的SPI,把SPI对应的接口连接好。连接方法如下(标号不一定相同,对应功能连接):
    树莓派 TFT
    SCLK---SCK 时钟信号
    MOSI---SDA 数据线
    CE0 ---CS 片选线
    并行的根据对应型号的来,就不赘述了。
        除此之外,TFT还需要一些额外的线,比如RESET(复位)、RS(或DC或A0,寄存器选择线,用来选择数据寄存器还是指令寄存器)等,这些不同的型号接线是不同的,参考对应型号的接线方法。如果不知道怎么接线,这里有一个方法用来显示树莓派对应的GPIO所作的功能。
    二、加载
    (1)对于很常用的TFT主控IC(st7735r,hx8340bn, ili9225, ili9320, ili9325, ili9341, ssd1289, ssd1351),
    先用以下的方法注册:
    sudo modprobe fbtft_device name=flexfb 加参数
    参数参考 https://github.com/notro/fbtft/wiki/fbtft_device
    对于大多数而言参数可以采用默认值,但是建议把speed调低到4000000~8000000,或者更低,这样既能保证显示速度,又不会出现莫名其妙的不正常。
    再用以下方法加载:
    sudo modprobe flexfb chip=主控IC的型号 加其他参数参考 https://github.com/notro/fbtft/wiki/flexfb
    如果你看到TFT显示屏变成黑屏了,那么祝贺你,离成功已经很近了。
    (2)对于特定的屏幕(Adafruit 1.3,HY28A,HY28B,fb_hx8353d,Freetronics,ITDB02,Watterott MI0283QT-2,Watterott MI0283QT-9A,Nokia 5110/3310,ILSoft OLEDpioled,Sainsmart 1.8,Sainsmart 3.2,ssd1331,tinylcd.com,tm022hdh26)
    sudo modprobe fbtft_device name=屏幕型号
    (3)没在支持列表里面,但是知道初始化方法:
    sudo modprobe fbtft_device name=flexfb 加参数(参考(1))
    sudo modprobe flexfb chip=定位方法相同的芯片 width=宽 height=高 buswidth=数据线位数 regwidth=寄存器位数 init=初始化代码(-1代表命令开始,-2代表延时毫秒数,-3代表末尾),不能用setaddrwin表示定位方法。
    如果不知如何接线可以先用上一步的方法加载,然后查看日志:dmesg
    这时会显示从开机以来的模块日志,如果想要去掉多余的信息可以这样 dmesg|grep "fbtft|flexfb",如图:

    55.png

    这时,所用到的GPIO就显示出来了:
    GPIOS used by 'flexfb':
    'reset' = GPIO25
    'dc' = GPIO24
    这说明GPIO25是复位线,GPIO24是寄存器选择线(Data/Command,也叫RS、A0)
    除此之外,注意一下是不是用的SPI0:
    如[ 10.476789] fbtft_device: flexfb spi0.0 8000kHz 8 bits mode=0x00
    如果显示的是spi0.1,需要把TFT的CS线从CE0改到CE1。

    哇哦,做到这里你离成功就更进一步了。因为如果下一步测试通过的话,你就胜利啦!
    开始测试:
    一、字符测试
           用这个  con2fbmap 1 1
    这时没错的话,屏幕上就显示字符界面了。如果屏幕又变白了或者显示一段时间后变白,那么多半是加载模块的时候speed太高了,把调低点吧,就算是500000其实刷新速度也不低了。
    二、图形测试
           如果字符测试正常的话,再来测试图形吧
           参考https://github.com/notro/fbtft/wiki/Testing apt-get install libnetpbm10-dev git clone https://git.kernel.org/pub/scm/linux/kernel/git/geert/fbtest.git cd fbtest
    打开 fb.c 注释或去掉 #include <asm/page.h> make./fbtest --fbdev /dev/fb1
    正常的话,你首先会看到黑白的格子,然后是圆形正方形测试,接着是颜色和图形测试,正如一开始展示的图像。
    三、窗口测试
           首先打开/usr/share/X11/xorg.conf.d/99-fbturbo.conf 注释或去掉这一句:Option "fbdev" "/dev/fb0"
    然后 FRAMEBUFFER=/dev/fb1 startx,一会儿之后你就可以看到桌面了!

    开机自动加载:
    既然要作为显示器,当然得开机就自动加载了!
    参考https://github.com/notro/fbtft/wiki#step-by-step-using-fbtft
    一、把加载的命令加到/etc/modules中
    例如:

    66.png

    二、修改/boot/cmdline.txt把fbcon=map:10加到末尾,再加以下参数可以修改字符界面字体
    MINI4x6, VGA8x8, Acorn8x8, PEARL8x8, ProFont6x11, 7x14, VGA8x16, SUN8x16, 10x18, SUN12x22
    例如: 77.png

    OK,这样重启之后就能自动加载啦!
    当然,并不是所有事情进行得都是你想象中的那么顺利,以下就是可能遇到的问题以及解决方案。
    大神传授技巧:
    1.卸载模块(不是移除,只是关闭而已)
    sudo rmmod flexfb
    sudo mmod fbtft_device
    显示调试信息,加载的时候加debug=1~7的数字,等级越高显示越详细,信息写入到日志中,用dmesg查看!或者也可用
    echo "5" >/sys/class/graphics/fb1/debug

    2.接好线加载模块屏幕完全没反应
    可以肯定的是,初始化失败的。一般来说接线正确,加载模块正确屏幕就有反应。
    3.加载模块后花屏
    ##########
    程序有BUG#########
    首先第一反应是屏幕初始化了,但是没有更新屏幕数据(后来证明想法是正确的)
    至于为什么没能更新屏幕数据,有以下几个可能
    (1)framebuffer映射错误
    (2)SPI没有发送数据
    (3)接线问题
    (4)液晶屏坏了
    (5)程序本来就都是显示花屏的
    对于(1),从接受调用到映射到SPI都是正常的,硬是没看出端倪来。
    对于(2),怀疑是不是硬件坏了,所以在arduino上写了个小程序,连接树莓派的SPI,用来接收树莓派发送的SPI数据,并把数据通过串口发给电脑,这样就能测试SPI了。
    向树莓派spi写入数据的方法可以这样echo "Hello World">/dev/spidev0.0,结果电脑成功接收,说明SPI是没问题的。
    对于(3),觉得不太可能,因为液晶屏初始化的时候SPI和RST、RS都需要用到,而且不仅要写指令寄存器,而且要写数据寄存器,接线肯定是没问题的。
    对于(4),也不太可能,能够显示随机的颜色,说明显示是没问题的。
    对于(5),显然不太可能,不然这个BUG应该早就被修复了。
    到这里用逐个引脚测试的方法,判断是哪一个信号的问题。
    首先先用arduino写个屏幕测试程序,供给arduino3.3V,屏幕显示正常,说明arduino所有信号正常。
    接下来就是移花接木大法了!树莓派加载好模块,打开测试程序,把arduino和树莓派的刷新速率调到一致(大概一致就行)当液晶屏正常显示的时候,可以先移除掉arduino的MOSI数据线和SCLK时钟信号线,为什么能移除,直觉吧,把它们换到树莓派的对应端口上(注意共地),这时奇迹出现了,屏幕竟然有了反应了,只是显示的是斜的,这时可以断定树莓派的MOSI和SCLK信号没问题。
    至于RESET信号线可以判断是没问题的,CS信号也应该不会有问题(因为可以正确初始化),但还是测试一下,用树莓派的RESET、CS线替换arduino的线,这时,可以看到正常一些的画面了,只是颜色不对,时钟不同步可以理解。最后的疑点就落到RS上面了,替换掉,果然画面立即停止了。
    分析一下,一般液晶屏在初始化的时候要频繁用到RS进行寄存器的切换,而对GRAM写入的时候,并不需要访问指令寄存器,只要初始化最后在指令寄存器写入写GRAM的功能好就行,所以写屏的时候RS应该保持高电平不变,我写的arduino程序也正是这么做的。而用树莓派RS替换arduino的RS时候,屏幕停止工作说明树莓派RS在此期间发生了变化。用LED测试一下,看不出效果。这时又没示波器,真是欲哭无泪了。好吧,arduino也有16M的频率,用它看能不能检测到RS变化信号吧!写了个程序一检测,出乎意料!RS信号果然发生了变化,而且时间非常短!这时思路清晰了,难怪我初始化最后修改指令寄存器到指定功能号还是不行,原来是指令寄存器又被改了!
    这下好办了,慢慢读程序吧,framebuffer映射过程都在fbtft-core.c中,从fbtft_mkdirty函数开始,没修改RS,fbtft_deferred_io也没修改,fbtft_update_display中看出了端倪!其中调用了set_addr_win函数(用来定位的函数),这个肯定是要修改指令寄存器的,但是可惜第一次忽略了,因为程序是这样写的:
    if (par->fbtftops.set_addr_win)...set_addr_win(...);
    翻到flexfb.c文件
    set_addr_win的定义中又有这句
    switch (setaddrwin) {
    case 0:
    /* use default */
    break;
    case 1:
    par->fbtftops.set_addr_win = flexfb_set_addr_win_1;
    break;
    case 2:
    par->fbtftops.set_addr_win = flexfb_set_addr_win_2;
    break;
    case 3:
    par->fbtftops.set_addr_win = set_addr_win_3;
    break;
    default:
    dev_err(dev, "argument 'setaddrwin': unknown value %d.\n", setaddrwin);
    return -EINVAL;
    }
    在此次试验中,作者没有指定setaddrwin,索性就跳过了!
    然后又是各种毫无头绪的查错,最后查来查去硬是没找到原因,只好又查到了了set_addr_win,好吧,虽然我没指定setaddrwin,要是setaddrwin没初始化呢?所以又查setaddrwin的来龙去脉,最后定位到了这里(flexfb.c)
    } else if (!strcmp(chip, "ili9225")) {
    if (!width)
    width = 176;
    if (!height)
    height = 220;
    setaddrwin = 0;
    regwidth = 16;
    if (init_num == 0) {
    initp = ili9225_init;
    initp_num = ARRAY_SIZE(ili9225_init);
    }
    咋看也没错啊,setaddrwin = 0;不就跳过了?
    等等,看看par->fbtftops.set_addr_win是怎么初始化的(fbtft-core.c)
    /* default fbtft operations */
    par->fbtftops.write = fbtft_write_spi;
    par->fbtftops.read = fbtft_read_spi;
    par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
    par->fbtftops.write_register = fbtft_write_reg8_bus8;
    par->fbtftops.set_addr_win = fbtft_set_addr_win;
    看到了没,原以为fbtftops.set_addr_win初始是NULL,所以就直接跳过,没想到它竟然是初始化为fbtft_set_addr_win函数,好吧,找到fbtft_set_addr_win函数的定义一看,吓了一跳,果然是这东西作祟!
    void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
    {
    fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
    "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
    /* Column address set */
    write_reg(par, 0x2A,
    (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);
    /* Row adress set */
    write_reg(par, 0x2B,
    (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);
    /* Memory write */
    write_reg(par, 0x2C);
    }
    可恶的是,不仅修改了指令寄存器,而且把功能号写成常数0x2C!哎哟,这个屏幕写GRAM的功能号可是0x22(数据手册上有详细资料),修改程序再编译太麻烦了,先看看其他几个case对应的函数,其中case 1:的flexfb_set_addr_win_1是这样的:
    /* ili9320, ili9325 */
    static void flexfb_set_addr_win_1(struct fbtft_par *par, int xs, int ys, int xe, int ye)
    {
    fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
    switch (par->info->var.rotate) {
    /* R20h = Horizontal GRAM Start Address */
    /* R21h = Vertical GRAM Start Address */
    case 0:
    write_reg(par, 0x0020, xs);
    write_reg(par, 0x0021, ys);
    break;
    case 180:
    write_reg(par, 0x0020, width - 1 - xs);
    write_reg(par, 0x0021, height - 1 - ys);
    break;
    case 270:
    write_reg(par, 0x0020, width - 1 - ys);
    write_reg(par, 0x0021, xs);
    break;
    case 90:
    write_reg(par, 0x0020, ys);
    write_reg(par, 0x0021, height - 1 - xs);
    break;
    }
    write_reg(par, 0x0022); /* Write Data to GRAM */
    }
    天啊!这正是ili9225的定位方法,看,最后还贴心地把寄存器修改到了0x22,我瞬间明白了,只需要在加载flexfb的时候把setadderwin改成1就行,一试,不行,看程序:
    } else if (!strcmp(chip, "ili9225")) {
    if (!width)
    width = 176;
    if (!height)
    height = 220;
    setaddrwin = 0;
    regwidth = 16;
    if (init_num == 0) {
    initp = ili9225_init;
    initp_num = ARRAY_SIZE(ili9225_init);
    }
    setaddrwin 就是一个摆设好吧,真不知道为什么没像width一样加个if。
    真想大白。虽然setaddrwin 是个摆设,但是还是有其他方法的,最简单的就是用setaddrwin =1的chip来初始化,只不过加载的时候需要写初始化代码和大小信息,这个简单,果然,这样一弄成功显示了。真是汗颜了,setaddrwin = 1;写成setaddrwin = 0;,差之毫厘,谬以千里呀!
    注:笔记本屏幕转接难度很大,时钟频率应该比较高,可能要考虑信号干扰的问题,最好找现有的转接板。
    本文整理自 树莓派吧《移植手机TFT液晶屏到树莓派当显示器》。



    评分

    参与人数 2声望 +4 与非币 +4 收起 理由
    nemon + 3 + 1 赞一个!
    小菜儿 + 1 + 3 很给力!

    查看全部评分

    回复

    使用道具 举报

  • TA的每日心情
    开心
    2016-8-15 09:30
  • 签到天数: 162 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2015-4-29 13:55:01 | 显示全部楼层
    好贴,有机会试试!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2019-6-29 11:53
  • 签到天数: 368 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2015-4-29 14:07:32 | 显示全部楼层
    Great!!!!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-4-29 08:59
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2015-4-29 14:15:50 | 显示全部楼层
    这英语水平~~~
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2015-4-30 09:13
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2015-4-29 14:50:02 | 显示全部楼层
    弄下来就不止面包汽水这么简单了~~~待我去试试
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-4-30 09:16
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2015-4-29 14:58:01 | 显示全部楼层
    同为新人~~~刚入门树莓派就看到这样的帖子,楼主大赞,继续努力,多整点
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-6-24 16:41
  • 签到天数: 709 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2015-5-1 08:04:02 | 显示全部楼层
    这个真的不错哦
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2016-10-17 12:07
  • 签到天数: 306 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2015-5-2 11:01:29 | 显示全部楼层
    有点厉害。。。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2015-5-8 18:40:20 | 显示全部楼层
    非常非常好,支持原创
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2016-8-11 20:55
  • 签到天数: 11 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2015-9-2 23:15:14 | 显示全部楼层
    前提还要弄清楚屏的针脚定义啊。每个屏不一样吧??
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-11-19 11:34 , Processed in 0.205682 second(s), 35 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.