查看: 4363|回复: 0

【飞凌RT1052】基于ELCDIF_RGB接口与硬件LPSPI1接口的双液晶屏...

[复制链接]
  • TA的每日心情
    开心
    2018-4-20 15:04
  • 签到天数: 8 天

    [LV.3]偶尔看看II

    49

    主题

    188

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    2953
    最后登录
    2023-7-24
    发表于 2018-11-24 00:48:05 | 显示全部楼层 |阅读模式
        【飞凌RT1052】基于ELCDIF_RGB接口与硬件LPSPI1接口的双液晶屏异显实验
           双屏异显,即两个显示屏显示不同内容,在实际项目中应用非常多,比如一些工业控制系统会用到一个0.96寸的OLED+一个大尺寸带触摸的液晶彩屏,OLED用于显示简单的参数如温湿度等,液晶彩屏显示复杂的图形曲线并加上触摸控制的功能。
        本文涉及到飞凌板子两大硬件外设:ELCDIF接口与LPSPI接口,用于驱动一个4.3寸SPI总线液晶彩屏和一个4.3寸RGB接口的液晶屏。
        先讲讲SPI液晶彩屏。SPI液晶屏的接口接的是飞凌1052板子LPSPI1接口,即音频接口上方的10针接口,需要用1mm间距排针转2.54mm间距的排针转换:
    IMG_20181123_093634R.jpg IMG_20181123_093621R.jpg
        然后是RGB液晶彩屏,这个彩屏不能直接接到飞凌板子的RGB液晶接口,因为线序不同。好在有大佬提供了不带触摸总线的转接板,可以成功点亮RGB液晶屏:
    IMG_20181123_232003R.jpg IMG_20181123_232023R.jpg
        先讲讲SPI总线的驱动,在NXP官方提供的IMXRT1050的SDK以及飞凌提供的SDK中都没有LPSPI1总线阻塞收发的例程,只有一个基于LPSPI1+LPSPI3的,带FIFO和中断的主从机通信例程:
    27.jpg
    还有个FLEXSPI的例程,这个例程是直接读写板上QSPI NOR FLASH的,我们个人开发者不建议也绝对不能使用FLEXSPI总线直接读写板上的QSPI NOR FLASH,因为有些未经确认的读写操作都有可能会导致QSPI NOR FLASH中系统BOOT信息的缺失或出错,严重点甚至会导致板子无法继续烧写裸机程序,除了烧录回官方BOOT固件到QSPI NOR FLASH以外没有别的补救措施,此处也建议飞凌厂商,给BOOT用的QSPI NOR FLASH加上写保护措施
    28.jpg
    此处要调通LPSPI1的收发指令,RT1052的SPI分为阻塞和非阻塞两种收发方式,在日常使用中,单片机充当SPI主机,驱动SPI从机设备的方式,大多采用阻塞收发,只有在极少数追求高实时性的双MCU SPI通信或者操作系统中使用非阻塞收发,我个人的猜测是跟操作系统的时间片轮转调度有关,由于我只开发裸机程序,因此不需要了解非阻塞收发。需要参考FLEXSPI的例程中的SPI阻塞收发函数:
    status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr)
    {
        flexspi_transfer_t flashXfer;
        status_t status;


        /* Write neable */
        flashXfer.deviceAddress = baseAddr;
        flashXfer.port = kFLEXSPI_PortA1;
        flashXfer.cmdType = kFLEXSPI_Command;
        flashXfer.SeqNumber = 1;
        flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE;


        status = FLEXSPI_TransferBlocking(base, &flashXfer);


        return status;
    }


    将之改为LPSPI1的阻塞收发函数即可,只需要换个设备号:


    unsigned char LPSPI1_ReadWriteByte(unsigned char data)
    {
        unsigned char spirxdata=0;
        lpspi_transfer_t spi_tranxfer;
       
        spi_tranxfer.configFlags=kLPSPI_MasterPcs0|kLPSPI_MasterPcsContinuous;
        spi_tranxfer.txData=&data;
        spi_tranxfer.rxData=&spirxdata;
        spi_tranxfer.dataSize=1;
        LPSPI_MasterTransferBlocking(LPSPI1,&spi_tranxfer);          
        return spirxdata;
    }
    configFlags设置为硬件片选设置,如果不使用LPSPI1接口自带的片选引脚,而用普通IO来片选的话,设置此行参数无任何影响。


    在LPSPI1接口启用之前需初始化相关引脚和LPSPI1外设参数:
    void LPSPI1_Init(int baudrate)
    {
        int lpspiclk=0;
       
        CLOCK_SetMux(kCLOCK_LpspiMux,1);
        CLOCK_SetDiv(kCLOCK_LpspiDiv,5);
       
              //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0,0);   
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK,0);                                    
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO,0);                                    
        //IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI,0);                                    
       
                    //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0,0x10B0);  
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK,0x10B0);                              
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO,0x10B0);                                                               


                   
        //IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI,0x10B0);                                
       
        lpspiclk=(CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk)/(6));
                    //LPSPI1时钟
        lpspi1_config.baudRate=baudrate*1000000;
                    //SPI速度
        //lpspi1_config.whichPcs=kLPSPI_Pcs0;
                    //片选信号,PCS0
        //lpspi1_config.pcsActiveHighOrLow=kLPSPI_PcsActiveLow;
                    //片选信号低电平有效
        lpspi1_config.bitsPerFrame=8;
                    //设置SPI的数据大小:SPI发送接收8位帧结构
        lpspi1_config.cpol=kLPSPI_ClockPolarityActiveHigh;
                    //串行同步时钟低电平有效
        lpspi1_config.cpha=kLPSPI_ClockPhaseSecondEdge;
                    //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
        lpspi1_config.direction=kLPSPI_MsbFirst;
                    //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
        lpspi1_config.pinCfg=kLPSPI_SdiInSdoOut;
                    //SDI输入引脚,SDO输出引脚
        lpspi1_config.dataOutConfig=kLpspiDataOutRetained;
                    //输出数据保留
        lpspi1_config.pcsToSckDelayInNanoSec=10;
                    //片选拉低到时钟有效之间的延时时间,单位ns
        lpspi1_config.lastSckToPcsDelayInNanoSec=10;
                    //最后一个时钟到片选拉高之间的延时,单位ns
        lpspi1_config.betweenTransferDelayInNanoSec=10;
                    //两次传输之间的延时,单位ns
        LPSPI_MasterInit(LPSPI1,&lpspi1_config,lpspiclk);
        LPSPI_Enable(LPSPI1,true);
    }


    然后接上逻辑分析仪,就可以判断LPSPI时序是否符合从机要求,若不符合则按从机要求修改,这里我的SPI液晶彩屏的通信时序如下:
    29.jpg
    由图可知,SPI液晶彩屏CLK时钟脚闲置时要拉高,CLK上升沿进行数据传输,数据位从高到低。


    然后是RGB液晶屏的驱动,这里我参考的是SDK里面的ELCDIF-RGB例程:
    30.jpg
    看看这效果,真心不错:
    2.gif
    你们猜猜这是用什么方法实现的?没错,一整张屏幕的缓存一起刷的,完全看不出卡顿,1052就是牛逼。


    先说说硬件接口,飞凌板子的液晶接口是RGB565,也就是16位色,这是考点,大家先记着,不要管它,往下看...
    31.jpg
    在RT1052中,ELCDIF的RGB总线占用的显存空间,是从0x80200000地址开始,也就是外部SDRAM内存颗粒的其中一部分内存空间,也就是说,直接往这部分内存空间写数据,那就是直接操作RGB液晶屏的显存,于是乎,官方提供了一个初始化函数,用于搭建系统内存空间到用户内存空间的映射桥梁:


              const elcdif_rgb_mode_config_t config2 =
                            {
            .panelWidth = APP_IMG_WIDTH,
            .panelHeight = APP_IMG_HEIGHT,
            .hsw = APP_HSW,
            .hfp = APP_HFP,
            .hbp = APP_HBP,
            .vsw = APP_VSW,
            .vfp = APP_VFP,
            .vbp = APP_VBP,
            .polarityFlags = APP_POL_FLAGS,
            .bufferAddr = (uint32_t)buffer,
            .pixelFormat = kELCDIF_PixelFormatXRGB8888,
            .dataBus = kELCDIF_DataBus24Bit,
        };


        ELCDIF_RgbModeInit(APP_ELCDIF, &config2);


    .bufferAddr参数就是用户自己开辟的内存空间的地址,此处我们可以直接定义一个数组:


    uint32_t buffer[272][480];


    搭建映射关系:
    ...
            .bufferAddr = (uint32_t)buffer,
    ...
    完事了,直接
    buffer[100][100]=0xffff0000;
    那就是往屏幕的(100,100)坐标位置描一个红点,都不怎么需要加延时,多简单,是不是???NXP的东西就是这样,读起来难读,但是一旦读懂了SDK,用起来就跟手头的工具一样好用。


    问题来了,刚刚不是说到,飞凌板子的接口只有16位色嘛,但是看看这:
    .pixelFormat = kELCDIF_PixelFormatXRGB8888,


    在官方给出的色彩数据格式中,有五种:
    typedef enum _elcdif_pixel_format
    {
        kELCDIF_PixelFormatRAW8 = 0,     /*!< RAW 8 bit, four data use 32 bits. */
        kELCDIF_PixelFormatRGB565 = 1,   /*!< RGB565, two pixel use 32 bits. */
        kELCDIF_PixelFormatRGB666 = 2,   /*!< RGB666 unpacked, one pixel uses 32 bits, high byte unused,
                                              upper 2 bits of other bytes unused. */
        kELCDIF_PixelFormatXRGB8888 = 3, /*!< XRGB8888 unpacked, one pixel uses 32 bits, high byte unused. */
        kELCDIF_PixelFormatRGB888 = 4,   /*!< RGB888 packed, one pixel uses 24 bits. */

    } elcdif_pixel_format_t;


    在官方给出的数据位数格式中,有四种:
    typedef enum _elcdif_lcd_data_bus
    {
        kELCDIF_DataBus8Bit = LCDIF_CTRL_LCD_DATABUS_WIDTH(1),  /*!< 8-bit data bus. */
        kELCDIF_DataBus16Bit = LCDIF_CTRL_LCD_DATABUS_WIDTH(0), /*!< 16-bit data bus, support RGB565. */
        kELCDIF_DataBus18Bit = LCDIF_CTRL_LCD_DATABUS_WIDTH(2), /*!< 18-bit data bus, support RGB666. */
        kELCDIF_DataBus24Bit = LCDIF_CTRL_LCD_DATABUS_WIDTH(3), /*!< 24-bit data bus, support RGB888. */
    } elcdif_lcd_data_bus_t;


    既然接口只有16位长度,那么软件配置也应保持一致,将色彩格式改成16位即kELCDIF_PixelFormatRGB565,将buffer的数据类型改为short,画点函数也做了相应改变之后,可以直接使用16位数据格式传输色彩数据。而使用XRGB8888 32位格式,就会造成极大的资源浪费。
    ...
    short buffer[272][480];
    ...
    .bufferAddr = (uint32_t)buffer,
    .pixelFormat = kELCDIF_PixelFormatRGB565,
    .dataBus = kELCDIF_DataBus16Bit,


    ...


    最后是有关RAM空间的分配问题,因为使用ELCDIF外设本身需要占用外部RAM空间,并且内部RAM的资源不足以支撑程序运行,因此scf文件需要作出相应改变,不然内存不够用,导致编译器报错:
    32.jpg 33.jpg
    既然两个彩屏都调通了,那不妨做个非常简单的小DEMO:
    IMG_20181124_000815R.jpg
    工程.zip (898.38 KB, 下载次数: 100)
    今天心情不错
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-25 15:27 , Processed in 0.108191 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表