当前位置:   article > 正文

[STM32U5]【NUCLEO-U5A5ZJ-Q测评】移植LVGL_nucleo 可以用lvgl

nucleo 可以用lvgl

【前言】
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、首先,宏定义如下:

复制
  1. #define TCLK_GPIO_Port GPIOC
  2. #define TCLK_PIN GPIO_PIN_10
  3. #define TCLK_L TCLK_GPIO_Port->BRR = TCLK_PIN; //拉低SCK
  4. #define TCLK_H TCLK_GPIO_Port->BSRR = TCLK_PIN; //拉高SCK
  5. #define TDIN_GPIO_Port GPIOC
  6. #define TDIN_PIN GPIO_PIN_12
  7. #define TDIN_L TDIN_GPIO_Port->BRR = TDIN_PIN; //MOSI 低电平
  8. #define TDIN_H TDIN_GPIO_Port->BSRR = TDIN_PIN; //MOSI 高电平
  9. #define TCS_GPIO_Port GPIOC
  10. #define TCS_PIN GPIO_PIN_13
  11. #define TCS_L TCS_GPIO_Port->BRR = TCS_PIN; //cs 低电平
  12. #define TCS_H TCS_GPIO_Port->BSRR = TCS_PIN; //cs 高电平
  13. #define DOUT_GPIO_Port GPIOC
  14. #define DOUT_Pin GPIO_PIN_11
  15. #define PEN HAL_GPIO_ReadPin(PEN_GPIO_Port, PEN_Pin)
  16. #define DOUT HAL_GPIO_ReadPin(DOUT_GPIO_Port, DOUT_Pin)
  17. 初始化GPIO,把TCK、TDIN、TCS配置为输出模式,把DOUT、PEN配置为输入模式。
  18. //注意,时钟使能之后,对GPIO的操作才有效
  19. //所以上拉之前,必须使能时钟.才能实现真正的上拉输出
  20. GPIO_InitTypeDefGPIO_InitStruct = {0};
  21. //注意,时钟使能之后,对GPIO的操作才有效
  22. //所以上拉之前,必须使能时钟.才能实现真正的上拉输出
  23. /*Configure GPIO pin : PtPin */
  24. GPIO_InitStruct.Pin = TCLK_PIN | TDIN_PIN;
  25. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  26. GPIO_InitStruct.Pull = GPIO_PULLUP;
  27. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  28. HAL_GPIO_Init(TCLK_GPIO_Port, &GPIO_InitStruct);
  29. /*Configure GPIO pin : PtPin */
  30. GPIO_InitStruct.Pin = PEN_Pin | DOUT_Pin;
  31. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  32. GPIO_InitStruct.Pull = GPIO_NOPULL;
  33. HAL_GPIO_Init(PEN_GPIO_Port, &GPIO_InitStruct);


2、实现对电阻屏的写一个字节的函数TP_Write_Byte,代码如下:

复制
  1. void TP_Write_Byte(uint8_t num)
  2. {
  3.         uint8_t count=0;
  4.         for(count=0;count<8;count++)
  5.         {
  6.                 if(num&0x80)
  7.                         {TDIN_H;}
  8.                 else
  9.                         {TDIN_L;}
  10.                 num<<=1;
  11.                 TCLK_L;
  12.                 TCLK_H;                //上升沿有效
  13.         }
  14. }


3、实现对电阻屏的读一个字节的函数TP_Read_AD(uint8_t CMD),代码如下:

复制
  1. uint16_t TP_Read_AD(uint8_t CMD)
  2. {
  3.         uint8_t count=0;
  4.         uint16_t Num=0;
  5.         TCLK_L;                //先拉低时钟
  6.         TDIN_L;         //拉低数据线
  7.         TCS_L;                 //选中触摸屏IC
  8.         TP_Write_Byte(CMD);//发送命令字
  9.         delay_us(6);//ADS7846的转换时间最长为6us
  10.         TCLK_L;
  11.         delay_us(1);
  12.         TCLK_H;                //1个时钟,清除BUSY
  13.         TCLK_L;
  14.         for(count=0;count<16;count++)//读出16位数据,只有高12位有效
  15.         {
  16.                 Num<<=1;
  17.                 TCLK_L;        //下降沿有效
  18.                 TCLK_H;
  19.                 if(DOUT)Num++;
  20.         }
  21.         Num>>=4;           //只有高12位有效.
  22.         TCS_H;                //释放片选
  23.         return(Num);
  24. }


4、实现读取指定寄存器的函数 TP_Read_XOY(uint8_t xy),代码如下:

复制
  1. uint16_t TP_Read_XOY(uint8_t xy)
  2. {
  3.         uint16_t i, j;
  4.         uint16_t buf[READ_TIMES];
  5.         uint16_t sum=0;
  6.         uint16_t temp;
  7.         for(i=0;i<READ_TIMES;i++)buf[i]=TP_Read_AD(xy);
  8.         for(i=0;i<READ_TIMES-1; i++)//排序
  9.         {
  10.                 for(j=i+1;j<READ_TIMES;j++)
  11.                 {
  12.                         if(buf[i]>buf[j])//升序排列
  13.                         {
  14.                                 temp=buf[i];
  15.                                 buf[i]=buf[j];
  16.                                 buf[j]=temp;
  17.                         }
  18.                 }
  19.         }
  20.         sum=0;
  21.         for(i=LOST_VAL;i<READ_TIMES-LOST_VAL;i++)sum+=buf[i];
  22.         temp=sum/(READ_TIMES-2*LOST_VAL);
  23.         return temp;
  24. }


5、实现读取指定xy坐标函数TP_Read_XY(uint16_t *x,uint16_t *y),代码如下:

复制
  1. uint8_t TP_Read_XY(uint16_t *x,uint16_t *y)
  2. {
  3.         uint16_t xtemp,ytemp;
  4.         xtemp=TP_Read_XOY(CMD_RDX);
  5.         ytemp=TP_Read_XOY(CMD_RDY);
  6.         //if(xtemp<100||ytemp<100)return 0;//读数失败
  7.         *x=xtemp;
  8.         *y=ytemp;
  9.         return 1;//读数成功
  10. }


6、为了准备的读取坐标植,定义了误差值范围为50,进行滤波算法,连续采集5次,丢弃一个,然后做平均,来得出坐标。TP_Read_XY2(uint16_t *x,uint16_t *y),代码如下:

复制
  1. uint8_t TP_Read_XY2(uint16_t *x,uint16_t *y)
  2. {
  3.         uint16_t x1,y1;
  4.          uint16_t x2,y2;
  5.          uint8_t flag;
  6.     flag=TP_Read_XY(&x1,&y1);
  7.     if(flag==0)return(0);
  8.     flag=TP_Read_XY(&x2,&y2);
  9.     if(flag==0)return(0);
  10.     if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后两次采样在+-50
  11.     &&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE)))
  12.     {
  13.         *x=(x1+x2)/2;
  14.         *y=(y1+y2)/2;
  15.         return 1;
  16.     }else return 0;
  17. }


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、较准函数如下:

复制
  1. /*****************************************************************************
  2. * [url=home.php?mod=space&uid=139335]@name[/url]       :uint8_t TP_Get_Adjdata(void)
  3. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  4. * [url=home.php?mod=space&uid=42490]@function[/url]   :Calibration touch screen and Get 4 calibration parameters
  5. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :None
  6. * @retvalue   :None
  7. ******************************************************************************/
  8. void TP_Adjust(void)
  9. {
  10.         uint16_t pos_temp[4][2];//坐标缓存值
  11.         uint8_t  cnt=0;
  12.         uint16_t d1,d2;
  13.         uint32_t tem1,tem2;
  14.         float fac;
  15.         uint16_t outtime=0;
  16.          cnt=0;
  17.         fillScreen(ILI9488_WHITE);//清屏
  18.         ILI9488_printText("Please use the stylus click the", 10,40,ILI9488_BLACK, ILI9488_WHITE, 2);
  19.         ILI9488_printText("cross on the screen.The cross will", 10,56,ILI9488_BLACK, ILI9488_WHITE, 2);
  20.         ILI9488_printText("always move until the screen", 10,72,ILI9488_BLACK, ILI9488_WHITE, 2);
  21.         ILI9488_printText("adjustment is completed.", 10,88,ILI9488_BLACK, ILI9488_WHITE, 2);
  22.         TP_Drow_Touch_Point(20,20,ILI9488_RED);//画点1
  23.         tp_dev.sta=0;//消除触发信号
  24.         tp_dev.xfac=0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误
  25.         while(1)//如果连续10秒钟没有按下,则自动退出
  26.         {
  27.                 tp_dev.scan(1);//扫描物理坐标
  28.                 if((tp_dev.sta&0xc0)==TP_CATH_PRES)//按键按下了一次(此时按键松开了.)
  29.                 {
  30.                         outtime=0;
  31.                         tp_dev.sta&=~(1<<6);//标记按键已经被处理过了.
  32.                         pos_temp[cnt][0]=tp_dev.x;
  33.                         pos_temp[cnt][1]=tp_dev.y;
  34.                         cnt++;
  35.                         switch(cnt)
  36.                         {
  37.                                 case 1:
  38.                                         TP_Drow_Touch_Point(20,20,ILI9488_WHITE);                                //清除点1
  39.                                         TP_Drow_Touch_Point(lcddev.width-20,20,ILI9488_RED);        //画点2
  40.                                         break;
  41.                                 case 2:
  42.                                          TP_Drow_Touch_Point(lcddev.width-20,20,ILI9488_WHITE);        //清除点2
  43.                                         TP_Drow_Touch_Point(20,lcddev.height-20,ILI9488_RED);        //画点3
  44.                                         break;
  45.                                 case 3:
  46.                                          TP_Drow_Touch_Point(20,lcddev.height-20,ILI9488_WHITE);                        //清除点3
  47.                                          TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_RED);        //画点4
  48.                                         break;
  49.                                 case 4:         //全部四个点已经得到
  50.                                 //对边相等
  51.                                         tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2
  52.                                         tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2
  53.                                         tem1*=tem1;
  54.                                         tem2*=tem2;
  55.                                         d1=sqrt(tem1+tem2);//得到1,2的距离
  56.                                         tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4
  57.                                         tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4
  58.                                         tem1*=tem1;
  59.                                         tem2*=tem2;
  60.                                         d2=sqrt(tem1+tem2);//得到3,4的距离
  61.                                         fac=(float)d1/d2;
  62.                                         if(fac<0.95||fac>1.05||d1==0||d2==0)//不合格
  63.                                         {
  64.                                                 cnt=0;
  65.                                              TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
  66.                                                     TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
  67.                                                  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);//显示数据
  68.                                                  continue;
  69.                                         }
  70.                                         tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3
  71.                                         tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3
  72.                                         tem1*=tem1;
  73.                                         tem2*=tem2;
  74.                                         d1=sqrt(tem1+tem2);//得到1,3的距离
  75.                                         tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4
  76.                                         tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4
  77.                                         tem1*=tem1;
  78.                                         tem2*=tem2;
  79.                                         d2=sqrt(tem1+tem2);//得到2,4的距离
  80.                                         fac=(float)d1/d2;
  81.                                         if(fac<0.95||fac>1.05)//不合格
  82.                                         {
  83.                                                 cnt=0;
  84.                                              TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
  85.                                                     TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
  86.                                                  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);//显示数据
  87.                                                 continue;
  88.                                         }//正确了
  89.                                         //对角线相等
  90.                                         tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3
  91.                                         tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3
  92.                                         tem1*=tem1;
  93.                                         tem2*=tem2;
  94.                                         d1=sqrt(tem1+tem2);//得到1,4的距离
  95.                                         tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4
  96.                                         tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4
  97.                                         tem1*=tem1;
  98.                                         tem2*=tem2;
  99.                                         d2=sqrt(tem1+tem2);//得到2,3的距离
  100.                                         fac=(float)d1/d2;
  101.                                         if(fac<0.95||fac>1.05)//不合格
  102.                                         {
  103.                                                 cnt=0;
  104.                                              TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
  105.                                                     TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
  106.                                                  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);//显示数据
  107.                                                 continue;
  108.                                         }//正确了
  109.                                         //计算结果
  110.                                         tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac
  111.                                         tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xoff
  112.                                         tp_dev.yfac=(float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfac
  113.                                         tp_dev.yoff=(lcddev.height-tp_dev.yfac*(pos_temp[2][1]+pos_temp[0][1]))/2;//得到yoff
  114.                                         if(abs(tp_dev.xfac)>2||abs(tp_dev.yfac)>2)//触屏和预设的相反了.
  115.                                         {
  116.                                                 cnt=0;
  117.                                              TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,ILI9488_WHITE);        //清除点4
  118.                                                     TP_Drow_Touch_Point(20,20,ILI9488_RED);                                                                //画点1
  119.                                                 ILI9488_printText("TP Need readjust!", 40,26,ILI9488_BLACK, ILI9488_WHITE, 2);
  120.                                                 tp_dev.touchtype=!tp_dev.touchtype;//修改触屏类型.
  121.                                                 if(tp_dev.touchtype)//X,Y方向与屏幕相反
  122.                                                 {
  123.                                                         CMD_RDX=0X90;
  124.                                                         CMD_RDY=0XD0;
  125.                                                 }else                                   //X,Y方向与屏幕相同
  126.                                                 {
  127.                                                         CMD_RDX=0XD0;
  128.                                                         CMD_RDY=0X90;
  129.                                                 }
  130.                                                 continue;
  131.                                         }
  132.                                         fillScreen(ILI9488_WHITE);//清屏
  133.                                         ILI9488_printText("Touch Screen Adjust OK!", 35,110,ILI9488_BLACK, ILI9488_WHITE, 2);
  134.                                         HAL_Delay(1000);
  135.                                         TP_Save_Adjdata();
  136.                                         fillScreen(ILI9488_WHITE);//清屏
  137.                                         return;//校正完成
  138.                         }
  139.                 }
  140.                 HAL_Delay(10);
  141.                 outtime++;
  142.                 if(outtime>1000)
  143.                 {
  144.                         TP_Get_Adjdata();
  145.                         break;
  146.                  }
  147.          }
  148. }


10、较准参数的保存
较准参数,工程设计保存到备份寄存器中,stm32U5A5有32个TMP备份寄存器可以用,工程里RTC已初始化好了备份寄存器,并且有HAL的扩展读写函数。RTC已使用了RTC_BKP_DR1-RTC_BKP_DR5,因此此存我们使用RTC_BKP_DR6-11来保存较准因素、X偏移量、Y偏移量、触屏的模竖类型,以及是否较准的标记。具体读写代码如下:

复制
  1. void TP_Save_Adjdata(void)
  2. {
  3.         int32_t temp;
  4.         //保存校正结果!
  5.         temp=tp_dev.xfac*100000000;//保存x校正因素
  6.     HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR6, temp);
  7.         temp=tp_dev.yfac*100000000;//保存y校正因素
  8.     HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR7, temp);
  9.         //保存x偏移量
  10.     HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR8, tp_dev.xoff);
  11.         //保存y偏移量
  12.     HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR9, tp_dev.yoff);
  13.         //保存触屏类型
  14.     HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR10, tp_dev.touchtype);
  15.         temp=0X0A;//标记校准过了
  16.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR11, temp);
  17. }
  18. /*****************************************************************************
  19. * [url=home.php?mod=space&uid=139335]@name[/url]       :uint8_t TP_Get_Adjdata(void)
  20. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  21. * [url=home.php?mod=space&uid=42490]@function[/url]   :Gets the calibration values stored in the EEPROM
  22. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :None
  23. * @retvalue   :1-get the calibration values successfully
  24.                                                                 0-get the calibration values unsuccessfully and Need to recalibrate
  25. ******************************************************************************/
  26. uint8_t TP_Get_Adjdata(void)
  27. {
  28.         int32_t tempfac;
  29.         tempfac    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR11);
  30.         if(tempfac==0X0A)//触摸屏已经校准过了
  31.         {
  32.                 tempfac=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR6);
  33.                 tp_dev.xfac=(float)tempfac/100000000;//得到x校准参数
  34.                 tempfac=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR7);
  35.                 tp_dev.yfac=(float)tempfac/100000000;//得到y校准参数
  36.             //得到x偏移量
  37.                 tp_dev.xoff=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR8);
  38.              //得到y偏移量
  39.                 tp_dev.yoff=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR9);
  40.                  tp_dev.touchtype=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR10);//读取触屏类型标记
  41.                 if(tp_dev.touchtype)//X,Y方向与屏幕相反
  42.                 {
  43.                         CMD_RDX=0X90;
  44.                         CMD_RDY=0XD0;
  45.                 }else                                   //X,Y方向与屏幕相同
  46.                 {
  47.                         CMD_RDX=0XD0;
  48.                         CMD_RDY=0X90;
  49.                 }
  50.                 return 1;
  51.         }
  52.         return 0;
  53. }


至此,已经实了触摸的驱动,可以正式进入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函数为内容如下:

复制
  1. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
  2. {
  3.     /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
  4.                 uint32_t i, n, cnt, buf_size,h,w;
  5.                 uint8_t r,g,b;
  6.                 w = area->x2- area->x1 +1;
  7.                 h = area->y2- area->y1 +1;
  8.                 setAddrWindow(area->x1, area->y1, area->x2, area->y2); //设置显示块大小
  9.                 n = w*h*3;
  10.        //割分发送给屏
  11.         if (n <= 65535){
  12.                 cnt = 1;
  13.                 buf_size = n;
  14.         }
  15.         else {
  16.                         cnt = n/3;
  17.                         buf_size = 3;
  18.                         uint8_t min_cnt = n/(65535)+1;
  19.                         for (i=min_cnt; i < n/3; i++)
  20.                         {
  21.                                         if(n%i == 0)
  22.                                         {
  23.                                                         cnt = i;
  24.                                                         buf_size = n/i;
  25.                                                         break;
  26.                                         }
  27.                         }
  28.         }
  29.         DC_DATA();
  30.         CS_A();
  31.                 while(cnt>0)
  32.                 {
  33.                                 uint8_t frm_buf[buf_size];
  34.                                 for (i=0; i < buf_size/3; i++)
  35.                                 {
  36.                                                 r = (((color_p->full & 0xF800) >> 11) * 255) / 31;
  37.                                                 g = (((color_p->full & 0x07E0) >> 5) * 255) / 63;
  38.                                                 b = (color_p->full & 0x001F * 255) / 31;
  39.                                                 frm_buf[i*3] = r;
  40.                                                 frm_buf[i*3+1] = g;
  41.                                                 frm_buf[i*3+2] = b;
  42.                                                  color_p++;
  43.                                 }
  44.                                 //HAL_SPI_Transmit(&hspi1, frm_buf, buf_size, HAL_MAX_DELAY);
  45.                                 HAL_SPI_Transmit(&hspi1, frm_buf, buf_size, 10);
  46.                                 cnt -= 1;
  47.                 }
  48.                 CS_D();
  49.     /*IMPORTANT!!!
  50.      *Inform the graphics library that you are ready with the flushing*/
  51.     lv_disp_flush_ready(disp_drv);
  52. }


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来提供:

复制
  1. #define LV_TICK_CUSTOM 1
  2. #if LV_TICK_CUSTOM
  3. #define LV_TICK_CUSTOM_INCLUDE "FreeRTOS.h"/*Header for the system time function*/
  4. #define LV_TICK_CUSTOM_SYS_TIME_EXPR (xTaskGetTickCount()) /*Expression evaluating to current system time in ms*/
  5. /*If using lvgl as ESP32 component*/
  6. // #define LV_TICK_CUSTOM_INCLUDE "esp_timer.h"
  7. // #define LV_TICK_CUSTOM_SYS_TIME_EXPR ((esp_timer_get_time() / 1000LL))
  8. #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函数内容如下:

复制
  1. /*Will be called by the library to read the touchpad*/
  2. static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
  3. {
  4.     static lv_coord_t last_x = 0;
  5.     static lv_coord_t last_y = 0;
  6.     TP_Scan(0);
  7.     if(tp_dev.sta&TP_PRES_DOWN)
  8.     {
  9.             last_x = tp_dev.x;
  10.                 last_y = tp_dev.y;
  11.                 data->point.x = last_x;
  12.                 data->point.y = last_y;
  13.                 data->state = LV_INDEV_STATE_PR;
  14.     }
  15.     else
  16.     {
  17.             data->point.x = last_x;
  18.                 data->point.y = last_y;
  19.                 data->state = LV_INDEV_STATE_REL;
  20.     }
  21. }


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


2、在app_freertos.c文件中,我们在任务中添加一个LED的控制程序,添加一个LED及一个开关部件来实现对板载LED红灯的控制。添加一个label标签,用于展示当前的时间,开启一个定时器,在定时器回调函数中刷定时间显示。

复制
  1. lv_obj_t *led ;  //LED�?
  2. lv_obj_t *sw_led; //按键
  3. lv_obj_t *lab_time; //时间显示标签
  4. lv_timer_t * lvgl_task1 = NULL;
  5. lv_obj_t *obj1;
  6. RTC_DateTypeDef GetData;  //获取日期结构
  7. RTC_TimeTypeDef GetTime;   //获取时间结构
  8. static void switc_led_envet_handler(lv_event_t* e)
  9. {
  10.         lv_event_code_t code = lv_event_get_code(e);
  11.         if (code == LV_EVENT_VALUE_CHANGED)
  12.         {
  13.                 if(lv_obj_has_state(sw_led, LV_STATE_CHECKED))
  14.                 {
  15.                         lv_led_on(led);
  16.                         HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, SET);
  17.                 }
  18.                 else
  19.                 {
  20.                         lv_led_off(led);
  21.                         HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, RESET);
  22.                 }
  23.         }
  24. }
  25. static void lvgl_rtc_cb(lv_timer_t *tmr)
  26. {
  27.           HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
  28.           /* Get the RTC current Date */
  29.           HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
  30.           lv_label_set_text_fmt(lab_time, "%04d/%02d/%02d %02d:%02d:%02d",
  31.                           2000 + GetData.Year, GetData.Month, GetData.Date, GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
  32.           lv_obj_align_to(lab_time,obj1,LV_ALIGN_OUT_BOTTOM_MID,0,20);
  33. }
复制
  1. void StartTask02(void *argument)
  2. {
  3.   /* USER CODE BEGIN myTask02 */
  4.         lv_init();                                          /* lvgl系统初始 */
  5.         lv_port_disp_init();                                /* lvgl显示接口初始�????????,放在lv_init() */
  6.         lv_port_indev_init();
  7.         obj1 = lv_obj_create(lv_scr_act());
  8.         lv_obj_set_size(obj1,200,300);
  9.         lv_obj_set_align(obj1, LV_ALIGN_CENTER);    //居中
  10.         lv_obj_t *label1 = lv_label_create(lv_scr_act());
  11.         lv_obj_set_style_text_font(label1,&lv_font_montserrat_24,LV_STATE_DEFAULT);
  12.         lv_label_set_text(label1, "STM32U5A5 LED DEMO");
  13.         lv_obj_align_to(label1,obj1,LV_ALIGN_OUT_TOP_MID,0,-20);
  14.         led = lv_led_create(obj1);
  15.         lv_obj_set_size(led,80,80);
  16.         lab_time = lv_label_create(lv_scr_act());
  17.         lv_obj_set_style_text_font(lab_time, &lv_font_montserrat_24,LV_STATE_DEFAULT);
  18.         lv_led_off(led);
  19.         sw_led = lv_switch_create(obj1);
  20.         lv_obj_set_size(sw_led,100,50);
  21.         lv_obj_add_event_cb(sw_led, switc_led_envet_handler,LV_EVENT_VALUE_CHANGED,NULL);
  22.         lv_obj_align_to(led,obj1,LV_ALIGN_CENTER,0,-60);
  23.         lv_obj_align_to(sw_led,obj1,LV_ALIGN_CENTER,0,60);
  24.         lvgl_task1 = lv_timer_create(lvgl_rtc_cb, 1000, 0);      // 运行周期为lvgl�?1000个滴答时�?
  25.   /* Infinite loop */
  26.   for(;;)
  27.   {
  28.         lv_timer_handler(); /* LVGL计时 */
  29.     osDelay(10);
  30.   }
  31.   /* USER CODE END myTask02 */
  32. }

【实现效果】
 


【总结】

  • 基于LVGL的移植历经一个星期才完成任务。下面谈谈这项目工程的一些经验与心得。
  • STM32U5A5拥有大的flash与大内存,编写lvgl时不需要考虑内存是否足够的情况。
  • Stm32CubeIDE提供了强大的编程工具,基础的组件基于图形化的工具配置,减少了开发者的复杂的寄存器的配置。生成基础工程非常之方便。国家厂家比如雅特力、TI等也有图形化的配置工程,但是相比stm32cubeIDE还是没有这么全面与方便。
  • Stm32cubeIDE的FreeRTOS的移植也是有软件包,虽然在stm32U5下面没有默认的包,但是可以经过手工安装来实现对freeRTOS的简单配置,使得移植工作也是非常的快捷方便。
  • 在LVGL的移植工作中,相比keil的移植,也是有相当大的优势,keil中,需要手工添加非常多的.c文件,与头文件的引用,在stm32cubeIDE中只需要把外部的文件夹拷贝进目录就行了,简单的添加编译的路径与工程里就行了。
    ---------------------
    作者:lulugl
    链接:https://bbs.21ic.com/icview-3341056-1-1.html
    来源:21ic.com
    此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/579277
推荐阅读
相关标签
  

闽ICP备14008679号