项目使用正点原子STM32F767阿波罗开发板+IPS TFT-LCD屏幕(非正点原子屏幕)进行开发,应用层移植轻量级GUI库LVGL实现列表按键点击,切换,显示当前内部工作电压功能。重点在于对于RGB屏幕驱动普适性的学习和LVGL开发的理解。希望我实习期间的项目学习过程能对大家有所帮助。

一.RGB LCD屏幕开发所需基础知识

1.颜色格式及帧缓冲区大小

与接线较少的MCU屏幕不同,RGB屏幕采用并口连接通常像素点颜色数据传输采用RGB888,RGB565,RGB666,ARGB8888等格式进行传输。这意味着开发过程中如果不使用专用LCD驱动IC,在MCU选择过程中应选择引脚数足够的MCU进行开发。

另外,对于屏幕开发,我们在MCU选型过程中还应注意所开发屏幕显示过程中需要占用的显存,并选择足够RAM的MCU或外扩SDRAM。其中RGB LCD的占用显存是这样计算出来的:

以一块分辨率为240*320的屏幕为例:屏幕在显示过程中经过设定可选择为从左到右,从上到下的扫描方式(或者通过改变驱动选择其他方式),LCD控制器将从一块专门的帧缓冲区(frame buffer)中将各像素点的颜色数据取出并在屏幕上呈现。每一个像素都由R(红色)G(绿色)B(蓝色)三种颜色的数据组合而成。我们之前提到的RGB565/RGB888等即为各颜色在一个像素点里所占的位数(bit)大小,项目中使用的屏幕为RGB565格式,即为红色数据占5bits,绿色占6bits,蓝色占5bits。

5bits+6bits+5bits=16bits=2bytes 即为RGB565接口格式屏幕每个像素显示需要占用2字节(bytes)大小空间。分辨率240*320意思就是一帧完整的图像显示需要显示240*320个像素点。一般项目中需要较为流畅的显示,项目中为帧缓冲区预留了一帧图像大小的显存,所占用的RAM=2bytes*240*320=153.6KB




STM32H7CubeMXDSP库移植 stm32移植grbl_单片机


2.屏幕刷新率

我们常说的屏幕刷新率,60Hz,144Hz等等意思显而易见,结尾屏幕在1s内显示了多少帧图像。在屏幕厂家的datasheet没有描述时,我们该如何计算呢?这就要去看datasheet中的时钟频率了。


STM32H7CubeMXDSP库移植 stm32移植grbl_stm32_02


项目中的屏幕的时钟频率为6.49Mhz,意为在1s中有6490000次时钟脉冲,每次对应一次像素数据的传输,datasheet中的如图两个数据前者为一次行扫描所需时钟数,后者为一次帧扫描所需行扫描数


STM32H7CubeMXDSP库移植 stm32移植grbl_arm_03


STM32H7CubeMXDSP库移植 stm32移植grbl_嵌入式硬件_04


它们的乘积即为显示一帧图像的时钟数,用6490000/(320*338)=60Hz即为屏幕的刷新率。

二.方案及硬件连接

在实际开发中如不外加LCD驱动IC,使用MCU直接驱动的方式需要尽量选择内部带有LCD控制器的MCU,模拟时序的方式既不方便又不能很快发现问题。项目使用的STM32F767开发板内部带有专用LCD控制器LTDC和图形加速器DMA2D。很方便进行开发。以下为本项目的硬件连接,其它屏幕开发时需注意接口对应的MCU LCD控制器引脚,并且注意为背光引脚供电。


STM32H7CubeMXDSP库移植 stm32移植grbl_单片机_05


STM32H7CubeMXDSP库移植 stm32移植grbl_嵌入式硬件_06


三.代码部分注意事项

我使用CUBEMX+HAL的方式直接进行开发,详细代码及配置可自行下载。下面我来讲述一下在开发过程中需要注意的点。

1.LTDC屏幕参数配置

在配置cubemx的ltdc时需要严格对应datasheet中的lcd显示参数的值,包括vd,vbl,hd,hbl等。

2.LCD驱动编写

利用DMA2D外设,编写LCD控制器绘制矩形函数,并且注意函数参数的设定之后要与LVGL的API接口相对应。实际上我们使用LVGL库时,LCD的驱动代码只需要写这一个即可,如果想自己写类似于画圆、画点做的函数、画线也可以加进去。

void LTDC_Color_Fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey,uint16_t *color)
{    uint32_t timeout=0;
    uint32_t psx,psy,pex,pey;    
    uint16_t offline;
    uint32_t addr; 
        psx=sx;psy=sy;
        pex=ex;pey=ey;
    offline=240-(pex-psx+1);
    RCC->AHB1ENR|=1<<23;            //使能DM2D时钟
    DMA2D->CR=0<<16;                //存储器到存储器模式
    DMA2D->FGPFCCR=LCD.ColorMode;    //设置颜色格式
    DMA2D->FGOR=0;                    //前景层行偏移为0
    DMA2D->OOR=offline;            //设置行偏移 
    DMA2D->CR&=~(1<<0);                //先停止DMA2D
    DMA2D->FGMAR=(uint32_t)color;        //源地址
    DMA2D->OMAR=LCD.LayerMemoryAdd + LCD.BytesPerPixel*(LCD_Width * sy + sx);                //输出存储器地址
    DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16);    //设定行数寄存器 
    DMA2D->CR|=1<<0;                //启动DMA2D
    while((DMA2D->ISR&(1<<1))==0)    //等待传输完成
    {
        timeout++;
        if(timeout>0X1FFFFF)break;    //超时退出
    } 
    DMA2D->IFCR|=1<<1;                //清除传输完成标志      
}

3.LVGL相关API

(1)lv_port_disp.c

在配置中需要注意刷新的方式,我选择了一次刷新一整屏,实际调试时才不会出现卡顿,在其它屏幕开发时需要根据实际而定。

在此驱动与我们的LCD驱动需要对应的API为如下函数,需要于我们自己驱动的绘制矩形函数相对应,建议使用DMA2D绘制。

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);

这里的函数里只需加入之前写好的绘制矩形函数并添加函数lv_disp_flush_ready(disp_drv);

LTDC_Color_Fill(area->x1,area->y1,(area->x2)-(area->x1),(area->y1)-(area->y2),(uint16_t*)color_p);

这里的area结构体中的(x1,y1),(x2,y2)分别对应绘制矩形区域的左上角和右下角,写入参数时需严格对应自己之前编写的参数值。

(2)lv_port_indev.c

在调试中因为屏幕非触屏且开发板仅有4个按键,我在相关api中选择了以按键对应坐标点的方式对应屏幕位置,在其它开发调试中可针对实际需要完善方案.

static const lv_point_t btn_points[4] = {
            {10, 10},      /*Button 0 -> x:10; y:10*/
            {80, 70},      /*Button 1 -> x:80; y:70*/
            {180,10},      /*button 2 -> x:180; y:10*/   
            {80,100}       /*button 3 -> x:80; y:100*/                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
    };
    lv_indev_set_button_points(indev_button, btn_points);

4.LVGL编程

在LVGL编程中要善用c++思想父类子类的关系,我认为这也是LVGL开发的关键之处。还需要将相关事件和功能底层函数对应起来,实现屏幕显示和具体功能的对应。

5.LVGL心跳时钟配置

了解过LVGL后大家应该知道,在LVGL的实际运行时,我们需要为其配置心跳始终才能正常运行,在这里我们使用定时器中断来为LVGL配置心跳。并将lv_tick_inc()函数在定时器的中断回调函数中调用。

在初始化过程中我们需要注意的是,如果使用的是CUBEMX进行的定时器配置,我们在初始化函数中需要启动定时器的计时。只进行初始化是没有用的。

HAL_TIM_Base_Start_IT(&htim3);

四.调试结果

使用官方demo提供和的背景图片,对LVGL的基本功能进行了简单的运用并和外设驱动实际联系起来。


STM32H7CubeMXDSP库移植 stm32移植grbl_stm32_07


STM32H7CubeMXDSP库移植 stm32移植grbl_stm32_08


STM32H7CubeMXDSP库移植 stm32移植grbl_STM32H7CubeMXDSP库移植_09