![]() |
Forum Index : Microcontroller and PC projects : MM2: 2.4" SPI Colour Touchscreen
![]() ![]() ![]() ![]() |
|||||
Author | Message | ||||
tjwd Newbie ![]() Joined: 14/02/2015 Location: AustraliaPosts: 6 |
Hi, many thanks to matherp and WhiteWizzard for the follow-up to my questions. I will be sure to change the pin definitions in the demo code. It's good to know someone has used the TFT/touch with a 44 pin micromite - if it does not work at least I will know it's my fault ! I will try it over the weekend and report results. Regards, tjw. |
||||
OA47 Guru ![]() Joined: 11/04/2012 Location: AustraliaPosts: 982 |
Have a couple of pieces of code for Peters great Cfunctions for the 2.4" touchscreens: NumberPad ![]() Sub NumberPad
I9341.fillscreen(black) I9341.drect(0, 0, 240, 320, red) I9341.frndrect(10, 10, 70, 70, 5, green) I9341.pstring (30, 25, "1", 5, 0 , black, green) I9341.frndrect(85, 10, 70 ,70, 5,green) I9341.pstring (105, 25, "2", 5, 0 , black, green) I9341.frndrect(160, 10, 70, 70, 5, green) I9341.pstring (180, 25, "3", 5, 0 , black, green) I9341.frndrect(10, 85, 70, 70, 5, green) I9341.pstring (35, 100, "4", 5, 0 , black, green) I9341.frndrect(85, 85, 70, 70, 5,green) I9341.pstring (105, 100, "5", 5, 0 , black, green) I9341.frndrect(160,85, 70, 70, 5,green) I9341.pstring (180, 100, "6", 5, 0 , black, green) I9341.frndrect(10, 160, 70, 70, 5,green) I9341.pstring (35, 170, "7", 5, 0 , black, green) I9341.frndrect(85, 160, 70, 70, 5,green) I9341.pstring (105, 170, "8", 5, 0 , black, green) I9341.frndrect(160, 160, 70, 70, 5,green) I9341.pstring (180, 170, "9", 5, 0 , black, green) I9341.frndrect(10, 235, 70, 70, 5,green) I9341.pstring (30, 250, "<", 5, 0 , black, green) I9341.frndrect(85, 235, 70, 70, 5,green) I9341.pstring (105, 250, "0", 5, 0 , black, green) I9341.frndrect(160, 235, 70, 70, 5, green) I9341.pstring (180, 250, ">", 5, 0 , black, green) I9341.pstring (10,310, "Please enter the number",1,0,cyan,black) End Sub '******************************************************************************
'* * '* Sub to identify which Number Pad Button has been pressed * '* * '****************************************************************************** ' test for 1, 2 & 3 If 150 < y0 And y0 < 550 Then 'Row 1 If 1300 < x0 And x0 < 1800 Then 'Column 1 I9341.frndrect(10, 10, 70, 70, 5, black) I9341.frndrect(10, 10, 65, 65, 5, green) I9341.pstring (30, 25, "1", 5, 0 , yellow, green) Pause 200 'USE DIGIT 1 I9341.frndrect(10, 10, 70, 70, 5, green) I9341.pstring (30, 25, "1", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 800 < x0 And x0 < 1300 Then 'Column 2 I9341.frndrect(85, 10, 70, 70, 5, black) I9341.frndrect(85, 10, 65, 65, 5, green) I9341.pstring (105, 25, "2", 5, 0 , yellow, green) Pause 200 'USE DIGIT 2 I9341.frndrect(85, 10, 70 ,70, 5,green) I9341.pstring (105, 25, "2", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 100 < x0 And x0 < 750 Then 'Column 3 I9341.frndrect(160, 10, 70, 70, 5, black) I9341.frndrect(160, 10, 65, 65, 5, green) I9341.pstring (180, 25, "3", 5, 0 , yellow, green) Pause 200 'USE DIGIT 3 I9341.frndrect(160, 10, 70, 70, 5, green) I9341.pstring (180, 25, "3", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf EndIf ' test for 4, 5 & 6 If 550 < y0 And y0 < 950 Then 'Row 2 If 1300 < x0 And x0 < 1800 Then 'Column 1 I9341.frndrect(10, 85, 70, 70, 5, black) I9341.frndrect(10, 85, 65, 65, 5, green) I9341.pstring (35, 100, "4", 5, 0 , yellow, green) Pause 200 'USE DIGIT 4 I9341.frndrect(10, 85, 70, 70, 5, green) I9341.pstring (35, 100, "4", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 800 < x0 And x0 < 1300 Then 'Column 2 I9341.frndrect(85, 85, 70, 70, 5, black) I9341.frndrect(85, 85, 65, 65, 5, green) I9341.pstring (105, 100, "5", 5, 0 , yellow, green) Pause 200 'USE DIGIT 5 I9341.frndrect(85, 85, 70, 70, 5,green) I9341.pstring (105, 100, "5", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 100 < x0 And x0 < 750 Then 'Column 3 I9341.frndrect(160,85, 70, 70, 5, black) I9341.frndrect(160,85, 65, 65, 5, green) I9341.pstring (180, 100, "6", 5, 0 , yellow, green) Pause 200 ' Use DIGIT 6 I9341.frndrect(160,85, 70, 70, 5,green) I9341.pstring (180, 100, "6", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf EndIf ' test for 7, 8 & 9 If 950 < y0 And y0 < 1350 Then 'Row 3 If 1300 < x0 And x0 < 1800 Then 'Column 1 I9341.frndrect(10, 160, 70, 70, 5, black) I9341.frndrect(10, 160, 65, 65, 5, green) I9341.pstring (35, 170, "7", 5, 0 , yellow, green) Pause 200 'Use DIGIT 7 I9341.frndrect(10, 160, 70, 70, 5,green) I9341.pstring (35, 170, "7", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 800 < x0 And x0 < 1300 Then 'Column 2 I9341.frndrect(85, 160, 70, 70, 5, black) I9341.frndrect(85, 160, 65, 65, 5,green) I9341.pstring (105, 170, "8", 5, 0 , yellow, green) Pause 200 ' Use DIGIT 8 I9341.frndrect(85, 160, 70, 70, 5,green) I9341.pstring (105, 170, "8", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 100 < x0 And x0 < 750 Then 'Column 3 I9341.frndrect(160, 160, 70, 70, 5, black) I9341.frndrect(160, 160, 65, 65, 5, green) I9341.pstring (180, 170, "9", 5, 0 , yellow, green) Pause 200 'Use DIGIT 9 I9341.frndrect(160, 160, 70, 70, 5,green) I9341.pstring (180, 170, "9", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf EndIf 'Test for Last row If 1350 < y0 And y0 < 1750 Then 'Row 4 If 1300 < x0 And x0 < 1800 Then 'Column 1 I9341.frndrect(10, 235, 70, 70, 5, black) I9341.frndrect(10, 235, 65, 65, 5, green) I9341.pstring (30, 250, "<", 5, 0 , yellow, green) Pause 200 ' Use Back Symbol I9341.frndrect(10, 235, 70, 70, 5,green) I9341.pstring (30, 250, "<", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 800 < x0 And x0 < 1300 Then 'Column 2 I9341.frndrect(85, 235, 70, 70, 5, black) I9341.frndrect(85, 235, 65, 65, 5, green) I9341.pstring (105, 250, "0", 5, 0 , yellow, green) Pause 200 'Use 0 I9341.frndrect(85, 235, 70, 70, 5,green) I9341.pstring (105, 250, "0", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf If 100 < x0 And x0 < 750 Then 'Column 3 I9341.frndrect(160, 235, 70, 70, 5, black) I9341.frndrect(160, 235, 65, 65, 5, green) I9341.pstring (180, 250, ">", 5, 0 , yellow, green) Pause 200 'Use Enter I9341.frndrect(160, 235, 70, 70, 5, green) I9341.pstring (180, 250, ">", 5, 0 , black, green) Pause 200 x0=0:y0=0 EndIf EndIf 'Row 4 ![]() Sub MainMenu
I9341.fillscreen(black) I9341.drect(0, 0, 240, 320, red) I9341.frndrect(10, 10, 220, 30, 10, blue) I9341.pstring(40, 15, "MAIN MENU", 3, 0, white, blue) I9341.frndrect(10, 50, 220, 30, 10, green) I9341.pstring(45, 55, "OPTION 1", 3, 0, black, green) I9341.frndrect(10, 90, 220, 30, 10, cyan) I9341.pstring(45, 95, "OPTION 2", 3, 0, black, cyan) I9341.frndrect(10, 130, 220, 30, 10, magenta) I9341.pstring(45, 135, "OPTION 3", 3, 0, black, magenta) I9341.frndrect(10, 170, 220, 30, 10, white) I9341.pstring(45, 175, "OPTION 4", 3, 0, black, white) I9341.frndrect(10, 210, 220, 30, 10, yellow) I9341.pstring(45, 215, "OPTION 5", 3, 0, black, yellow) I9341.frndrect(10, 250, 220, 30, 10, red) I9341.pstring(45, 255, "OPTION 6", 3, 0, black, red) I9341.pstring(26, 290, "Please Select from the above", 1, 0, cyan, black) End Sub ' Test Touch for MainMenu
If mm=1 Then If 200 < x0 And x0 < 1820 Then If 320 < y0 And y0 < 580 Then I9341.pstring(26, 290,"OPTION 1",1,0,green,black) EndIf If 590 < y0 And y0 < 790 Then I9341.pstring(26, 290,"OPTION 2",1,0,cyan,black) EndIf If 800 < y0 And y0 < 1000 Then I9341.pstring(26, 290,"OPTION 3",1,0,magenta,black) EndIf If 1010 < y0 And y0 < 1210 Then I9341.pstring(26, 290,"OPTION 4",1,0,white,black) EndIf If 1220 < y0 And y0 < 1430 Then I9341.pstring(26, 290,"OPTION 5",1,0,yellow,black) EndIf If 1440 < y0 And y0 < 1640 Then I9341.pstring(26, 290,"OPTION 6",1,0,red,black) EndIf EndIf EndIf Peters Code for getting the x and y co-ordinates: If T.getxy(xvalues(0),yvalues(0),1000) And T.getxy(xvalues(1),yvalues(1),1000) And T.getxy(xvalues(2),yvalues(2),1000) Then
y0=filter(yvalues()) x0=filter(xvalues()) I9341.frect(1, 300, wi-2, 16, black) I9341.pstring(38, 300, "X="+Str$(x0)+" Y="+Str$(y0), 2, 0, green, black) Pause 200 EndIf Hope someone finds it useful |
||||
bigmik![]() Guru ![]() Joined: 20/06/2011 Location: AustraliaPosts: 2947 |
Great job Graeme, I hope my display with touch comes soon.. Regards, Mick Mick's uMite Stuff can be found >>> HERE (Kindly hosted by Dontronics) <<< |
||||
twofingers![]() Guru ![]() Joined: 02/06/2014 Location: GermanyPosts: 1566 |
@Greame, Yes! It is! Makes life easier. Thanks! ![]() Regards Michael causality ≠ correlation ≠ coincidence |
||||
Grogster![]() Admin Group ![]() Joined: 31/12/2012 Location: New ZealandPosts: 9585 |
Thanks for posting. More inspiration for when my 7" LCD gets here. ![]() Smoke makes things work. When the smoke gets out, it stops! |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
Yes, thanks from me too Graeme - I've just been trying it out and it looks good. BTW - should we be limiting the LCD current? I think I saw somewhere that it has an internal step-up supply for the screen but I'm not sure how that fits in. It seems reasonably bright with 33R in series on the LCD pin, i.e. pin7 and uses ~15mA with that (at 3.3v), but it certainly looks better with no other current limiting. In the latter case it's pulling about 45mA on pin7. Greg |
||||
OA47 Guru ![]() Joined: 11/04/2012 Location: AustraliaPosts: 982 |
On one of my projects where current minimization was needed, I used a trimpot on the LED supply and could get good backlight with a total current draw of 45mA for both Micromite and screen. If you can spare one of the PWM outputs you could control the backlight by software. GM |
||||
Zonker![]() Guru ![]() Joined: 18/08/2012 Location: United StatesPosts: 767 |
Excellent work Graeme..!! The first known GUI objects for the TFT's.! Hummm maybe start a library of these... ![]() |
||||
OA47 Guru ![]() Joined: 11/04/2012 Location: AustraliaPosts: 982 |
QWERTY KEYPAD (use of a pointer may be necessary) ![]() Unlike the previous pads instead of animating the key pressed, I wanted an audio beep when each key was pressed but found that all the PWM 1 ports were in use for the LCD and the PWM 2A is being used for the backlight control. Decided to use the PWM 2B output for the beep by putting a pietzo transducer on it with the added conformation of the backlight flashing for 50mS on each pad touched. ("That's a feature not a fault !!) Sub QwertyPad
Local integer J Local String Q$ Q$="QWERTYUIOPASDFGHJKL^ZXCVBNM< * ." ' Q$="qwertyuiopasdfghjkl zxcvbnm< * ." I9341.setrot(I9341_rt_left) I9341.fillscreen(black) I9341.drect(0, 0, 320, 240, red) For J = 1 To 10 I9341.frndrect((J*32)-30,100,29,29,3,cyan) I9341.pstring((J*32)-22,105,Mid$(Q$,J,1),3,0,black,cyan) Next J For J = 1 To 9 I9341.frndrect((J*32)-16,132,29,29,3,cyan) I9341.pstring((J*32)-7,137,Mid$(Q$,J+10,1),3,0,black,cyan) Next J For J = 1 To 10 I9341.frndrect((J*32)-30,164,29,29,3,cyan) I9341.pstring((J*32)-22,169,Mid$(Q$,J+19,1),3,0,black,cyan) Next J For J = 1 To 2 I9341.frndrect((J*32)-15,196,30,30,3,cyan) I9341.pstring((J*32)-7,201,Mid$(Q$,J+29,1),3,0,black,cyan) Next J I9341.pstring(17,210,"Shift",1,0,black,cyan) I9341.frndrect(81,196,160,30,3,cyan) I9341.pstring(125,210,"SPACE",2,0,black,cyan) For J = 8 To 9 I9341.frndrect((J*32)-13,196,30,30,3,cyan) Next J I9341.pstring(243,201,".",3,0,black,cyan) I9341.pstring(275,211,"Enter",1,0,black,cyan) I9341.pstring(245,230,"spilcdv2.bas",1,0,green,black) I9341.frndrect(10,10,300,80,3,white) I9341.pstring(15,15,"The First Line is Here",2,0,Black,White) I9341.pstring(15,35,"The Second Line is Here",2,0,Black,White) I9341.pstring(15,55,"The Third Line is Here",2,0,Black,White) I9341.pstring(15,75,"The Fourth Line is Here",2,0,Black,White) End Sub Sub QwertyTest ' Test Touch for QwertyPad SHIFT=1 If 1160 > x0 And 920 < x0 Then 'Value for first row If 1850 > y0 And 1680 < y0 Then 'Value for Key Q Beep If Shift=1 Then L1$=L1$+"Q" Else L1$=L1$+"q" EndIf If 1680 > y0 And 1510 < y0 Then 'Value for W Beep If Shift=1 Then L1$=L1$+"W" Else L1$=L1$+"w" EndIf If 1510 > y0 And 1340 < y0 Then 'Value for E Beep If Shift=1 Then L1$=L1$+"E" Else L1$=L1$+"e" EndIf If 1340 > y0 And 1170 < y0 Then 'Value for R Beep If Shift=1 Then L1$=L1$+"R" Else L1$=L1$+"r" EndIf If 1170 > y0 And 1000 < y0 Then 'Value for T Beep If Shift=1 Then L1$=L1$+"T" Else L1$=L1$+"t" EndIf If 1000 > y0 And 830 < y0 Then 'Value for Y Beep If Shift=1 Then L1$=L1$+"Y" Else L1$=L1$+"y" EndIf If 830 > y0 And 660 < y0 Then 'Value for U Beep If Shift=1 Then L1$=L1$+"U" Else L1$=L1$+"u" EndIf If 660 > y0 And 490 < y0 Then 'Value for I Beep If Shift=1 Then L1$=L1$+"I" Else L1$=L1$+"i" EndIf If 490 > y0 And 320 < y0 Then 'Value for O Beep If Shift=1 Then L1$=L1$+"O" Else L1$=L1$+"o" EndIf If 320 > y0 And 150 < y0 Then 'Value for P Beep If Shift=1 Then L1$=L1$+"P" Else L1$=L1$+"p" EndIf x0=0:y0=0 EndIf If 920 > x0 And 680 < x0 Then 'Value for second row If 1760 > y0 And 1590 < y0 Then 'Value for Key A Beep If Shift=1 Then L1$=L1$+"A" Else L1$=L1$+"a" EndIf If 1590 > y0 And 1420 < y0 Then 'Value for S Beep If Shift=1 Then L1$=L1$+"S" Else L1$=L1$+"s" EndIf If 1420 > y0 And 1250 < y0 Then 'Value for D Beep If Shift=1 Then L1$=L1$+"D" Else L1$=L1$+"d" EndIf If 1250 > y0 And 1080 < y0 Then 'Value for F Beep If Shift=1 Then L1$=L1$+"F" Else L1$=L1$+"f" EndIf If 1080 > y0 And 910 < y0 Then 'Value for G Beep If Shift=1 Then L1$=L1$+"G" Else L1$=L1$+"g" EndIf If 910 > y0 And 740 < y0 Then 'Value for H Beep If Shift=1 Then L1$=L1$+"H" Else L1$=L1$+"h" EndIf If 740 > y0 And 570 < y0 Then 'Value for J Beep If Shift=1 Then L1$=L1$+"J" Else L1$=L1$+"j" EndIf If 570 > y0 And 400 < y0 Then 'Value for K Beep If Shift=1 Then L1$=L1$+"K" Else L1$=L1$+"k" EndIf If 400 > y0 And 230 < y0 Then 'Value for L Beep If Shift=1 Then L1$=L1$+"L" Else L1$=L1$+"l" EndIf x0=0:y0=0 EndIf If 680 > x0 And 440 < x0 Then 'Value for third row If 1850 > y0 And 1680 < y0 Then 'Value for Key SHIFT Beep Shift= Not Shift ':QweryPad EndIf If 1680 > y0 And 1510 < y0 Then 'Value for Z Beep If Shift=1 Then L1$=L1$+"Z" Else L1$=L1$+"z" EndIf If 1510 > y0 And 1340 < y0 Then 'Value for X Beep If Shift=1 Then L1$=L1$+"X" Else L1$=L1$+"x" EndIf If 1340 > y0 And 1170 < y0 Then 'Value for C Beep If Shift=1 Then L1$=L1$+"C" Else L1$=L1$+"c" EndIf If 1170 > y0 And 1000 < y0 Then 'Value for V Beep If Shift=1 Then L1$=L1$+"V" Else L1$=L1$+"v" EndIf If 1000 > y0 And 830 < y0 Then 'Value for B Beep If Shift=1 Then L1$=L1$+"B" Else L1$=L1$+"b" EndIf If 830 > y0 And 660 < y0 Then 'Value for N Beep If Shift=1 Then L1$=L1$+"N" Else L1$=L1$+"n" EndIf If 660 > y0 And 490 < y0 Then 'Value for M Beep If Shift=1 Then L1$=L1$+"M" Else L1$=L1$+"m" EndIf If 490 > y0 And 320 < y0 Then 'Value for Backspace Beep L1$=Left$(L1$,(Len(L1$)-1)) EndIf x0=0:y0=0 EndIf End Sub Sub Beep PWM 2,1200,50:Pause 50:PWM 2,STOP End Sub Hope a TBSer may find some use. GM |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2931 |
Thanks for posting that GM - really useful ![]() |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
Nice job Graeme - another good one for the collection. ![]() Greg |
||||
OA47 Guru ![]() Joined: 11/04/2012 Location: AustraliaPosts: 982 |
@matherp Peter the code to interpret the touch pad seems to take 3 second @ 48Mhz. If T.getxy(xvalues(0),yvalues(0),1000) And T.getxy(xvalues(1),yvalues(1),1000) And T.getxy(xvalues(2),yvalues(2),1000) Then
y0=filter(yvalues()) x0=filter(xvalues()) I9341.pstring(38, 300, "X="+Str$(x0)+" Y="+Str$(y0), 2, 0, green, black) Pause 200 EndIf Is there a way of deceasing the time that this routine takes? |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10181 |
It takes three seconds because there are three 1000msec timeouts in the first line. You can change these to anything you want. This code for touch input is much more robust (in the zip file) . You will need to integrate it in with the Cfunction code rather than the in-built display code on the MX470. Also assuming the touch and screen are on the same SPI channel substitute this version of T.getdata which modifies the SPI speed to suit the slow XPT2046. function T.Getdata(command as integer) as integer 'get data from the touch controller local integer i,j,k,l pin(T_CS)=0 l=peek(word &HBF805830) 'read the SPI clock register poke word &HBF805830,&H17 'slow down the SPI i=spi(command) j=spi(0) k=spi(0) pin(T_CS)=1 poke word &HBF805830,l 'reset the SPI clock register T.Getdata= (j*256+k)>>4 end function |
||||
OA47 Guru ![]() Joined: 11/04/2012 Location: AustraliaPosts: 982 |
Thankyou again Peter, question answered. Keep up the great work. GM |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
@Graeme @Peter I've been 'playing' with Graeme's Qwerty pad code on the I9341 serial 2.4" screen (MX170 and MMBasic V4.6b) and added a number line. With or without the added number line though I'm getting an artifact character displaying in the first top left print position (of the "First line is here") when the T.getxy routine times out - in 'SUB QwertyTest' down near the bottom of this 2015-05-15_071924_QwertPadTest.zip file. The shot below shows the artifact (in red) - it's always the same. Has anyone else seen this or have I missed something? Greg ![]() |
||||
OA47 Guru ![]() Joined: 11/04/2012 Location: AustraliaPosts: 982 |
Greg, I haven't seen this sort of thing before. Interesting that it is in red as the only red I used was on the border. Could you post all of your code for perusal? GM |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10181 |
It is because after a timeout you are printing an empty string in this line: I9341.pstring(15,15,L1$,2,0,red,white) My code doesn't cater for null strings ![]() To fix it change I9341.pstring as below: [code] SUB I9341.pstring(xx0 AS INTEGER, yy0 AS INTEGER, text$, sz AS INTEGER, orientation AS INTEGER, fontcolour AS INTEGER, backgroundcolour AS INTEGER) 'x, y coordinates of top left of first character, text string, size, orientation if text$<>"" then LOCAL INTEGER i=I9341draw(I9341.fpstring,I9341_AddrOfFont,xx0,yy0,sz,orientation,fontcolour,backgroundcolour,I9341_params(),text$) endif END SUB [/code] |
||||
OA47 Guru ![]() Joined: 11/04/2012 Location: AustraliaPosts: 982 |
Sorry Greg, I will put that one down to grey hair rather than the grey cells. I missed the link to the code all together. Glad Peter is on the ball GM |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
No worries Graeme - I figured you'd be back pretty quick ![]() Greg |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
Thanks Peter, I'll give that a go. I knew it was printing an empty string but couldn't see the problem! Greg |
||||
![]() ![]() ![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |