打印
[STM32U5]

【NUCLEO-U5A5ZJ-Q测评】移植LVGL'

[复制链接]
2162|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lulugl|  楼主 | 2023-11-15 22:45 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 lulugl 于 2023-11-15 22:50 编辑

#有奖活动# #申请原创# @21小跑堂
【前言】
LVGL能实现非常简约美观的UI界面,前面移植好显示后,这章移植LVGL,实现一个对LED灯控制的小实例。
【开发环境】
  • win11。
  • STM32CubeIDE + FreeRTOS+LVGL

【硬件环境】
  • NUCLEO_STM32U5A5ZJ-Q开发板
  • ILI9488电阻触摸屏。

【实现步骤】
  • 移植lcd屏驱动,具体的移植,已经在帖子https://bbs.21ic.com/icview-3340004-1-1.html上移植好了。
  • freeRTOS,在帖子上移植好,需要的请移步查看:https://bbs.21ic.com/icview-3340310-1-1.html。
  • 移植触摸驱动。
  • 触摸的芯片是ADS7843,其原理图如下:


屏线接口原理图为:

使用到触的接口主要有TCLK(时钟)、TCS(片选)、TDI(MOSI)、TDO(MISO)、TPEN(中断)。电阻屏的原理就是当触摸屏按下后,取两个点的电压,来判断坐标在哪里,当屏被按下时,TPEN会给出信号。然后通过spi总线读取两个坐标点,计算出按下的坐标。
我们选取spi3为电阻触摸的通信,其接线如下
开发板    电阻屏
PEN       PC9 //INT
DOUT    PC11 //MISO
TDIN     PC12 //MOSI
TCLK     PC10 //SCLK
TCS      PC8 //CS
驱动程序的实现按下面的步骤来实现:
1、首先,宏定义如下:
#define TCLK_GPIO_Port GPIOC

#define TCLK_PIN GPIO_PIN_10

#define TCLK_L TCLK_GPIO_Port->BRR = TCLK_PIN; //拉低SCK

#define TCLK_H TCLK_GPIO_Port->BSRR = TCLK_PIN; //拉高SCK

#define TDIN_GPIO_Port GPIOC

#define TDIN_PIN GPIO_PIN_12

#define TDIN_L TDIN_GPIO_Port->BRR = TDIN_PIN; //MOSI 低电平

#define TDIN_H TDIN_GPIO_Port->BSRR = TDIN_PIN; //MOSI 高电平

#define TCS_GPIO_Port GPIOC

#define TCS_PIN GPIO_PIN_13

#define TCS_L TCS_GPIO_Port->BRR = TCS_PIN; //cs 低电平

#define TCS_H TCS_GPIO_Port->BSRR = TCS_PIN; //cs 高电平

#define DOUT_GPIO_Port GPIOC

#define DOUT_Pin GPIO_PIN_11

#define PEN HAL_GPIO_ReadPin(PEN_GPIO_Port, PEN_Pin)

#define DOUT HAL_GPIO_ReadPin(DOUT_GPIO_Port, DOUT_Pin)

初始化GPIO,把TCK、TDIN、TCS配置为输出模式,把DOUT、PEN配置为输入模式。

//注意,时钟使能之后,对GPIO的操作才有效

//所以上拉之前,必须使能时钟.才能实现真正的上拉输出

GPIO_InitTypeDefGPIO_InitStruct = {0};

//注意,时钟使能之后,对GPIO的操作才有效

//所以上拉之前,必须使能时钟.才能实现真正的上拉输出

/*Configure GPIO pin : PtPin */

GPIO_InitStruct.Pin = TCLK_PIN | TDIN_PIN;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(TCLK_GPIO_Port, &GPIO_InitStruct);

/*Configure GPIO pin : PtPin */

GPIO_InitStruct.Pin = PEN_Pin | DOUT_Pin;

GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

GPIO_InitStruct.Pull = GPIO_NOPULL;

HAL_GPIO_Init(PEN_GPIO_Port, &GPIO_InitStruct);

2、实现对电阻屏的写一个字节的函数TP_Write_Byte,代码如下:
void TP_Write_Byte(uint8_t num)
{
        uint8_t count=0;
        for(count=0;count<8;count++)
        {
                if(num&0x80)
                        {TDIN_H;}
                else
                        {TDIN_L;}
                num<<=1;
                TCLK_L;
                TCLK_H;                //上升沿有效
        }
}

3、实现对电阻屏的读一个字节的函数TP_Read_AD(uint8_t CMD),代码如下:
uint16_t TP_Read_AD(uint8_t CMD)
{
        uint8_t count=0;
        uint16_t Num=0;
        TCLK_L;                //先拉低时钟
        TDIN_L;         //拉低数据线
        TCS_L;                 //选中触摸屏IC
        TP_Write_Byte(CMD);//发送命令字
        delay_us(6);//ADS7846的转换时间最长为6us
        TCLK_L;
        delay_us(1);
        TCLK_H;                //给1个时钟,清除BUSY
        TCLK_L;
        for(count=0;count<16;count++)//读出16位数据,只有高12位有效
        {
                Num<<=1;
                TCLK_L;        //下降沿有效
                TCLK_H;
                if(DOUT)Num++;
        }
        Num>>=4;           //只有高12位有效.
        TCS_H;                //释放片选
        return(Num);

}

4、实现读取指定寄存器的函数 TP_Read_XOY(uint8_t xy),代码如下:
uint16_t TP_Read_XOY(uint8_t xy)
{
        uint16_t i, j;
        uint16_t buf[READ_TIMES];
        uint16_t sum=0;
        uint16_t temp;
        for(i=0;i<READ_TIMES;i++)buf[i]=TP_Read_AD(xy);
        for(i=0;i<READ_TIMES-1; i++)//排序
        {
                for(j=i+1;j<READ_TIMES;j++)
                {
                        if(buf[i]>buf[j])//升序排列
                        {
                                temp=buf[i];
                                buf[i]=buf[j];
                                buf[j]=temp;
                        }
                }
        }
        sum=0;
        for(i=LOST_VAL;i<READ_TIMES-LOST_VAL;i++)sum+=buf[i];
        temp=sum/(READ_TIMES-2*LOST_VAL);
        return temp;
}

5、实现读取指定xy坐标函数TP_Read_XY(uint16_t *x,uint16_t *y),代码如下:
uint8_t TP_Read_XY(uint16_t *x,uint16_t *y)
{
        uint16_t xtemp,ytemp;
        xtemp=TP_Read_XOY(CMD_RDX);
        ytemp=TP_Read_XOY(CMD_RDY);
        //if(xtemp<100||ytemp<100)return 0;//读数失败
        *x=xtemp;
        *y=ytemp;
        return 1;//读数成功
}

6、为了准备的读取坐标植,定义了误差值范围为50,进行滤波算法,连续采集5次,丢弃一个,然后做平均,来得出坐标。TP_Read_XY2(uint16_t *x,uint16_t *y),代码如下:
uint8_t TP_Read_XY2(uint16_t *x,uint16_t *y)
{
        uint16_t x1,y1;
         uint16_t x2,y2;
         uint8_t flag;
    flag=TP_Read_XY(&x1,&y1);
    if(flag==0)return(0);
    flag=TP_Read_XY(&x2,&y2);
    if(flag==0)return(0);
    if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后两次采样在+-50内
    &&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE)))
    {
        *x=(x1+x2)/2;
        *y=(y1+y2)/2;
        return 1;
    }else return 0;
}

7、电阻屏需要读取4个着的坐标进行屏幕较准,为此驱动设计了四点较准法来对屏幕进行较准,此次的较准点为:
1 (20,20)                                         (x0,y0)
2 (lcddev.width-20,20)                      (x1,y1)
3 (20,lcddev.height-20)                     (x2,y2)
4 (lcddev.width-20,lcddev.height-20)  (x3,y3)
8、算法步骤:

9、较准函数如下:
/*****************************************************************************
* [url=home.php?mod=space&uid=139335]@name[/url]       :uint8_t TP_Get_Adjdata(void)
* [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
* [url=home.php?mod=space&uid=42490]@function[/url]   :Calibration touch screen and Get 4 calibration parameters
* [url=home.php?mod=space&uid=2814924]@parameters[/url] :None
* @retvalue   :None
******************************************************************************/
void TP_Adjust(void)
{
        uint16_t pos_temp[4][2];//坐标缓存值
        uint8_t  cnt=0;
        uint16_t d1,d2;
        uint32_t tem1,tem2;
        float fac;
        uint16_t outtime=0;
         cnt=0;


        fillScreen(ILI9488_WHITE);//清屏
        ILI9488_printText("Please use the stylus click the", 10,40,ILI9488_BLACK, ILI9488_WHITE, 2);
        ILI9488_printText("cross on the screen.The cross will", 10,56,ILI9488_BLACK, ILI9488_WHITE, 2);
        ILI9488_printText("always move until the screen", 10,72,ILI9488_BLACK, ILI9488_WHITE, 2);
        ILI9488_printText("adjustment is completed.", 10,88,ILI9488_BLACK, ILI9488_WHITE, 2);
        TP_Drow_Touch_Point(20,20,ILI9488_RED);//画点1
        tp_dev.sta=0;//消除触发信号
        tp_dev.xfac=0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误
        while(1)//如果连续10秒钟没有按下,则自动退出
        {
                tp_dev.scan(1);//扫描物理坐标
                if((tp_dev.sta&0xc0)==TP_CATH_PRES)//按键按下了一次(此时按键松开了.)
                {
                        outtime=0;
                        tp_dev.sta&=~(1<<6);//标记按键已经被处理过了.

                        pos_temp[cnt][0]=tp_dev.x;
                        pos_temp[cnt][1]=tp_dev.y;
                        cnt++;
                        switch(cnt)
                        {
                                case 1:
                                        TP_Drow_Touch_Point(20,20,ILI9488_WHITE);                                //清除点1
                                        TP_Drow_Touch_Point(lcddev.width-20,20,ILI9488_RED);        //画点2
                                        break;
                                case 2:
                                         TP_Drow_Touch_Point(lcddev.width-20,20,ILI9488_WHITE);        //清除点2
                                        TP_Drow_Touch_Point(20,lcddev.height-20,ILI9488_RED);        //画点3
                                        break;
                                case 3:
                                         TP_Drow_Touch_Point(20,lcddev.height-20,ILI9488_WHITE);                        //清除点3
                                         TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_RED);        //画点4
                                        break;
                                case 4:         //全部四个点已经得到
                                //对边相等
                                        tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2
                                        tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2
                                        tem1*=tem1;
                                        tem2*=tem2;
                                        d1=sqrt(tem1+tem2);//得到1,2的距离

                                        tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4
                                        tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4
                                        tem1*=tem1;
                                        tem2*=tem2;
                                        d2=sqrt(tem1+tem2);//得到3,4的距离
                                        fac=(float)d1/d2;
                                        if(fac<0.95||fac>1.05||d1==0||d2==0)//不合格
                                        {
                                                cnt=0;
                                             TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
                                                    TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
                                                 TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据
                                                 continue;
                                        }
                                        tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3
                                        tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3
                                        tem1*=tem1;
                                        tem2*=tem2;
                                        d1=sqrt(tem1+tem2);//得到1,3的距离

                                        tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4
                                        tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4
                                        tem1*=tem1;
                                        tem2*=tem2;
                                        d2=sqrt(tem1+tem2);//得到2,4的距离
                                        fac=(float)d1/d2;
                                        if(fac<0.95||fac>1.05)//不合格
                                        {
                                                cnt=0;
                                             TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
                                                    TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
                                                 TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据
                                                continue;
                                        }//正确了

                                        //对角线相等
                                        tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3
                                        tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3
                                        tem1*=tem1;
                                        tem2*=tem2;
                                        d1=sqrt(tem1+tem2);//得到1,4的距离

                                        tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4
                                        tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4
                                        tem1*=tem1;
                                        tem2*=tem2;
                                        d2=sqrt(tem1+tem2);//得到2,3的距离
                                        fac=(float)d1/d2;
                                        if(fac<0.95||fac>1.05)//不合格
                                        {
                                                cnt=0;
                                             TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
                                                    TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
                                                 TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据
                                                continue;
                                        }//正确了
                                        //计算结果
                                        tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac
                                        tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xoff

                                        tp_dev.yfac=(float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfac
                                        tp_dev.yoff=(lcddev.height-tp_dev.yfac*(pos_temp[2][1]+pos_temp[0][1]))/2;//得到yoff
                                        if(abs(tp_dev.xfac)>2||abs(tp_dev.yfac)>2)//触屏和预设的相反了.
                                        {
                                                cnt=0;
                                             TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
                                                    TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
                                                ILI9488_printText("TP Need readjust!", 40,26,ILI9488_BLACK, ILI9488_WHITE, 2);
                                                tp_dev.touchtype=!tp_dev.touchtype;//修改触屏类型.
                                                if(tp_dev.touchtype)//X,Y方向与屏幕相反
                                                {
                                                        CMD_RDX=0X90;
                                                        CMD_RDY=0XD0;
                                                }else                                   //X,Y方向与屏幕相同
                                                {
                                                        CMD_RDX=0XD0;
                                                        CMD_RDY=0X90;
                                                }
                                                continue;
                                        }

                                        fillScreen(ILI9488_WHITE);//清屏
                                        ILI9488_printText("Touch Screen Adjust OK!", 35,110,ILI9488_BLACK, ILI9488_WHITE, 2);
                                        HAL_Delay(1000);
                                        TP_Save_Adjdata();
                                        fillScreen(ILI9488_WHITE);//清屏
                                        return;//校正完成
                        }
                }
                HAL_Delay(10);
                outtime++;
                if(outtime>1000)
                {
                        TP_Get_Adjdata();
                        break;
                 }
         }
}

10、较准参数的保存
较准参数,工程设计保存到备份寄存器中,stm32U5A5有32个TMP备份寄存器可以用,工程里RTC已初始化好了备份寄存器,并且有HAL的扩展读写函数。RTC已使用了RTC_BKP_DR1-RTC_BKP_DR5,因此此存我们使用RTC_BKP_DR6-11来保存较准因素、X偏移量、Y偏移量、触屏的模竖类型,以及是否较准的标记。具体读写代码如下:
void TP_Save_Adjdata(void)
{
        int32_t temp;
        //保存校正结果!
        temp=tp_dev.xfac*100000000;//保存x校正因素

    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR6, temp);
        temp=tp_dev.yfac*100000000;//保存y校正因素

    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR7, temp);
        //保存x偏移量
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR8, tp_dev.xoff);

        //保存y偏移量
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR9, tp_dev.yoff);

        //保存触屏类型
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR10, tp_dev.touchtype);

        temp=0X0A;//标记校准过了
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR11, temp);

}

/*****************************************************************************
* [url=home.php?mod=space&uid=139335]@name[/url]       :uint8_t TP_Get_Adjdata(void)
* [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
* [url=home.php?mod=space&uid=42490]@function[/url]   :Gets the calibration values stored in the EEPROM
* [url=home.php?mod=space&uid=2814924]@parameters[/url] :None
* @retvalue   :1-get the calibration values successfully
                                                                0-get the calibration values unsuccessfully and Need to recalibrate
******************************************************************************/
uint8_t TP_Get_Adjdata(void)
{
        int32_t tempfac;

        tempfac    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR11);
        if(tempfac==0X0A)//触摸屏已经校准过了
        {
                tempfac=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR6);
                tp_dev.xfac=(float)tempfac/100000000;//得到x校准参数
                tempfac=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR7);
                tp_dev.yfac=(float)tempfac/100000000;//得到y校准参数
            //得到x偏移量
                tp_dev.xoff=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR8);
             //得到y偏移量
                tp_dev.yoff=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR9);
                 tp_dev.touchtype=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR10);//读取触屏类型标记
                if(tp_dev.touchtype)//X,Y方向与屏幕相反
                {
                        CMD_RDX=0X90;
                        CMD_RDY=0XD0;
                }else                                   //X,Y方向与屏幕相同
                {
                        CMD_RDX=0XD0;
                        CMD_RDY=0X90;
                }
                return 1;
        }
        return 0;
}

至此,已经实了触摸的驱动,可以正式进入LVGL的移植了。
【LVGL移植】
  • 下载lvgl源码,官方下载,https://github.com/lvgl/lvgl。
  • 下载好后,我们把lvgl文件,真接粘帖到工程的Middlewares止录下:


3、把文件夹的目录添加到工程里面:

4、复制lv_port_disp_template.h/c、lv_port_indev_template.h/c到src目录下面,并且重命名为lv_port_disp.h/c、lv_port_indev.h/c。

5、打开lv_port_disp.h,把if 0修改为if 1,同时把文件的引用路径修改为#include “lvgl.h”

6、打开lv_port_disp.c,修改if 0,为if 1。添加lcd屏、的头文件引用,添加hspi1、touch、lcd等的变量的扩展声明。

7、在函数lv_port_disp_init中定义宽的参数,以及刷新缓存的方式

8、修改disp_flush函数为内容如下:
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/

                uint32_t i, n, cnt, buf_size,h,w;
                uint8_t r,g,b;
                w = area->x2- area->x1 +1;
                h = area->y2- area->y1 +1;
                setAddrWindow(area->x1, area->y1, area->x2, area->y2); //设置显示块大小
                n = w*h*3;

       //割分发送给屏
        if (n <= 65535){
                cnt = 1;
                buf_size = n;
        }
        else {
                        cnt = n/3;
                        buf_size = 3;
                        uint8_t min_cnt = n/(65535)+1;
                        for (i=min_cnt; i < n/3; i++)
                        {
                                        if(n%i == 0)
                                        {
                                                        cnt = i;
                                                        buf_size = n/i;
                                                        break;
                                        }
                        }
        }

        DC_DATA();
        CS_A();
                while(cnt>0)
                {
                                uint8_t frm_buf[buf_size];
                                for (i=0; i < buf_size/3; i++)
                                {
                                                r = (((color_p->full & 0xF800) >> 11) * 255) / 31;
                                                g = (((color_p->full & 0x07E0) >> 5) * 255) / 63;
                                                b = (color_p->full & 0x001F * 255) / 31;
                                                frm_buf[i*3] = r;
                                                frm_buf[i*3+1] = g;
                                                frm_buf[i*3+2] = b;
                                                 color_p++;
                                }
                                //HAL_SPI_Transmit(&hspi1, frm_buf, buf_size, HAL_MAX_DELAY);
                                HAL_SPI_Transmit(&hspi1, frm_buf, buf_size, 10);
                                cnt -= 1;
                }
                CS_D();


    /*IMPORTANT!!!
     *Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

10、在disp_init函数中,加入LCD屏初始化,设置屏有横竖类型,同时修改电阻屏的横竖:

11、重命名lvgl目录下的lvgl_conf_template.h为lvgl_conf.h。
12、打开lvgl_conf.h,修改if 0为if 1,打开代码。

13、为lvgl添加freertos的tick心跳包,在第88行左右,找到#define LV_TICK_CUSTOM,把他修改为1,同时修改内容如下,使得lvgl的心跳包为freertos来提供:
#define LV_TICK_CUSTOM 1

#if LV_TICK_CUSTOM

#define LV_TICK_CUSTOM_INCLUDE "FreeRTOS.h"/*Header for the system time function*/

#define LV_TICK_CUSTOM_SYS_TIME_EXPR (xTaskGetTickCount()) /*Expression evaluating to current system time in ms*/

/*If using lvgl as ESP32 component*/

// #define LV_TICK_CUSTOM_INCLUDE "esp_timer.h"

// #define LV_TICK_CUSTOM_SYS_TIME_EXPR ((esp_timer_get_time() / 1000LL))

#endif/*LV_TICK_CUSTOM*/

14、打开lv_port_indev.h 修改if 0为 if 1打开代码。

15、打开lv_port_indev.c,修改if 0 为if 1, 打开代码,同时添加touch.h的头文件引用:

16、修改touchpad_read函数内容如下:
/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    TP_Scan(0);
    if(tp_dev.sta&TP_PRES_DOWN)
    {
            last_x = tp_dev.x;
                last_y = tp_dev.y;

                data->point.x = last_x;
                data->point.y = last_y;
                data->state = LV_INDEV_STATE_PR;

    }
    else
    {
            data->point.x = last_x;
                data->point.y = last_y;
                data->state = LV_INDEV_STATE_REL;
    }
}

到此代码的移植全部完成,编译后有些错误提示,可以根据提示来添加头文件的引用等。
【测试代码】
1、在main.c中,我们需要先添加lcd的初始化与电阻屏的初化与较准。

2、在app_freertos.c文件中,我们在任务中添加一个LED的控制程序,添加一个LED及一个开关部件来实现对板载LED红灯的控制。添加一个label标签,用于展示当前的时间,开启一个定时器,在定时器回调函数中刷定时间显示。
lv_obj_t *led ;  //LED�?
lv_obj_t *sw_led; //按键
lv_obj_t *lab_time; //时间显示标签
lv_timer_t * lvgl_task1 = NULL;
lv_obj_t *obj1;
RTC_DateTypeDef GetData;  //获取日期结构

RTC_TimeTypeDef GetTime;   //获取时间结构

static void switc_led_envet_handler(lv_event_t* e)
{
        lv_event_code_t code = lv_event_get_code(e);
        if (code == LV_EVENT_VALUE_CHANGED)
        {
                if(lv_obj_has_state(sw_led, LV_STATE_CHECKED))
                {
                        lv_led_on(led);
                        HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, SET);
                }
                else
                {
                        lv_led_off(led);
                        HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, RESET);
                }
        }
}

static void lvgl_rtc_cb(lv_timer_t *tmr)
{
          HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
          /* Get the RTC current Date */
          HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
          lv_label_set_text_fmt(lab_time, "%04d/%02d/%02d %02d:%02d:%02d",
                          2000 + GetData.Year, GetData.Month, GetData.Date, GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
          lv_obj_align_to(lab_time,obj1,LV_ALIGN_OUT_BOTTOM_MID,0,20);
}

void StartTask02(void *argument)
{
  /* USER CODE BEGIN myTask02 */
        lv_init();                                          /* lvgl系统初始 */
        lv_port_disp_init();                                /* lvgl显示接口初始�????????,放在lv_init() */
        lv_port_indev_init();
        obj1 = lv_obj_create(lv_scr_act());
        lv_obj_set_size(obj1,200,300);
        lv_obj_set_align(obj1, LV_ALIGN_CENTER);    //居中

        lv_obj_t *label1 = lv_label_create(lv_scr_act());

        lv_obj_set_style_text_font(label1,&lv_font_montserrat_24,LV_STATE_DEFAULT);
        lv_label_set_text(label1, "STM32U5A5 LED DEMO");
        lv_obj_align_to(label1,obj1,LV_ALIGN_OUT_TOP_MID,0,-20);

        led = lv_led_create(obj1);
        lv_obj_set_size(led,80,80);

        lab_time = lv_label_create(lv_scr_act());
        lv_obj_set_style_text_font(lab_time, &lv_font_montserrat_24,LV_STATE_DEFAULT);

        lv_led_off(led);

        sw_led = lv_switch_create(obj1);
        lv_obj_set_size(sw_led,100,50);
        lv_obj_add_event_cb(sw_led, switc_led_envet_handler,LV_EVENT_VALUE_CHANGED,NULL);

        lv_obj_align_to(led,obj1,LV_ALIGN_CENTER,0,-60);
        lv_obj_align_to(sw_led,obj1,LV_ALIGN_CENTER,0,60);



        lvgl_task1 = lv_timer_create(lvgl_rtc_cb, 1000, 0);      // 运行周期为lvgl�?1000个滴答时�?

  /* Infinite loop */
  for(;;)
  {

        lv_timer_handler(); /* LVGL计时 */
    osDelay(10);
  }
  /* USER CODE END myTask02 */
}
【实现效果】

【总结】
  • 基于LVGL的移植历经一个星期才完成任务。下面谈谈这项目工程的一些经验与心得。
  • STM32U5A5拥有大的flash与大内存,编写lvgl时不需要考虑内存是否足够的情况。
  • Stm32CubeIDE提供了强大的编程工具,基础的组件基于图形化的工具配置,减少了开发者的复杂的寄存器的配置。生成基础工程非常之方便。国家厂家比如雅特力、TI等也有图形化的配置工程,但是相比stm32cubeIDE还是没有这么全面与方便。
  • Stm32cubeIDE的FreeRTOS的移植也是有软件包,虽然在stm32U5下面没有默认的包,但是可以经过手工安装来实现对freeRTOS的简单配置,使得移植工作也是非常的快捷方便。
  • 在LVGL的移植工作中,相比keil的移植,也是有相当大的优势,keil中,需要手工添加非常多的.c文件,与头文件的引用,在stm32cubeIDE中只需要把外部的文件夹拷贝进目录就行了,简单的添加编译的路径与工程里就行了。
【源代码】
代码比较大不能上传附件,我分享在网盘链接:https://pan.baidu.com/s/1parn-cC612MZLUbb1F6RCQ?pwd=b1v3
提取码:b1v3
--来自百度网盘超级会员V5的分享
【视频效果】



使用特权

评论回复
沙发
lulugl|  楼主 | 2023-11-15 22:55 | 只看该作者
我要给自己点个赞,这工程太耗时了!

使用特权

评论回复
板凳
yangjiaxu| | 2023-12-12 11:03 | 只看该作者
这个超级好啊,移植LVGL,确实很多人都想着要这么弄,而且LVGL现在真的是流行

使用特权

评论回复
地板
mintspring| | 2023-12-12 11:07 | 只看该作者
干的不错,IO模拟驱动啊,为何不用单片机的SPI接口呢。

使用特权

评论回复
5
gaoyang9992006| | 2023-12-12 11:08 | 只看该作者
片选引脚也可以省一省,如果只有一个SPI设备的话,可以直接拉低,固定下来,就少用一个IO了。可以尝试一下SPI硬件操作。

使用特权

评论回复
6
呐咯密密| | 2023-12-12 11:11 | 只看该作者
可以直接用TouchGFXDesigner来配置吗,我觉得这个工具很好用。

使用特权

评论回复
7
shenxiaolin| | 2023-12-12 11:12 | 只看该作者
超级赞!移植的步骤很详细,演示也非常到位,但是刷屏的速度是不是有点低?可以考虑升一下刷新频率,按道理来说U5开了CACHE的话应该是比较快的。

使用特权

评论回复
8
lulugl|  楼主 | 2023-12-12 14:26 | 只看该作者
shenxiaolin 发表于 2023-12-12 11:12
超级赞!移植的步骤很详细,演示也非常到位,但是刷屏的速度是不是有点低?可以考虑升一下刷新频率,按道理 ...

后面调整了内存,刷新速度超快,这块板子有很大的内存,可以同时开4个缓存。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

156

主题

744

帖子

10

粉丝