Skip to content

Commit 0ecb179

Browse files
authored
Added support for LilygoTWatch2021 (ESP32-PICO-V3-02 + GC9A01) (#2425)
***NO_CI***
1 parent 9337371 commit 0ecb179

File tree

6 files changed

+1522
-1
lines changed

6 files changed

+1522
-1
lines changed

CMakePresets.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,31 @@
593593
"TOUCHPANEL_INTERFACE": "Spi_To_TouchPanel.cpp"
594594
}
595595
},
596+
{
597+
"name": "LilygoTWatch2021_preset",
598+
"inherits": [
599+
"general-preset",
600+
"xtensa-esp32-preset"
601+
],
602+
"hidden": true,
603+
"cacheVariables": {
604+
"SDK_CONFIG_FILE": "sdkconfig.default_pico_ble",
605+
"TARGET_SERIAL_BAUDRATE": "115200",
606+
"NF_BUILD_RTM": "OFF",
607+
"NF_FEATURE_DEBUGGER": "ON",
608+
"NF_FEATURE_RTC": "OFF",
609+
"NF_FEATURE_HAS_SDCARD": "OFF",
610+
"API_System.IO.FileSystem": "OFF",
611+
"API_nanoFramework.Graphics": "ON",
612+
"API_nanoFramework.Device.OneWire": "OFF",
613+
"API_nanoFramework.Device.Bluetooth": "ON",
614+
"API_nanoFramework.Hardware.Esp32.Rmt": "OFF",
615+
"GRAPHICS_DISPLAY": "GC9A01_240x240_SPI.cpp",
616+
"GRAPHICS_DISPLAY_INTERFACE": "Spi_To_Display.cpp",
617+
"TOUCHPANEL_DEVICE": "CST816S.cpp",
618+
"TOUCHPANEL_INTERFACE": "Spi_To_TouchPanel.cpp"
619+
}
620+
},
596621
{
597622
"name": "M5StickC_preset",
598623
"inherits": [

CMakeUserPresets.TEMPLATE.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,18 @@
318318
"NF_INTEROP_ASSEMBLIES": null
319319
}
320320
},
321+
{
322+
"name": "LilygoTWatch2021",
323+
"inherits": [
324+
"user-local-tools",
325+
"user-prefs",
326+
"LilygoTWatch2021_preset"
327+
],
328+
"cacheVariables": {
329+
"TARGET_NAME": "${presetName}",
330+
"NF_INTEROP_ASSEMBLIES": null
331+
}
332+
},
321333
{
322334
"name": "M5StickC",
323335
"inherits": [
@@ -698,6 +710,11 @@
698710
"displayName": "LilygoTWatch2020",
699711
"configurePreset": "LilygoTWatch2020"
700712
},
713+
{
714+
"name": "LilygoTWatch2021",
715+
"displayName": "LilygoTWatch2021",
716+
"configurePreset": "LilygoTWatch2021"
717+
},
701718
{
702719
"name": "M5StickC",
703720
"displayName": "M5StickC",
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
4+
// See LICENSE file in the project root for full license information.
5+
//
6+
7+
#include "Graphics.h"
8+
#include "DisplayInterface.h"
9+
#include "Display.h"
10+
11+
/*
12+
13+
GC9A01 is a 262,144-color single-chip SOC driver for a-TFT liquid crystal display with resolution of
14+
240RGBx240 dots, comprising a 360-channel source driver, a 32-channel gate driver, 129,600 bytes
15+
GRAM for graphic display data of 240RGBx240 dots, and power supply circuit
16+
17+
Full datasheet at https://www.waveshare.com/w/upload/5/5e/GC9A01A.pdf
18+
19+
*/
20+
21+
struct DisplayDriver g_DisplayDriver;
22+
extern DisplayInterface g_DisplayInterface;
23+
extern DisplayInterfaceConfig g_DisplayInterfaceConfig;
24+
25+
enum GC9A01_CMD : CLR_UINT8
26+
{
27+
ReadDisplayInfo = 0x04,
28+
ReadDisplayStatus = 0x09,
29+
EnterSleepMode = 0x10,
30+
SleepOut = 0x11,
31+
PartialModeOn = 0x12,
32+
NormalDisplayMode = 0x13,
33+
DisplayInversionOff = 0x20,
34+
DisplayInversionOn = 0x21,
35+
DisplayOff = 0x28,
36+
DisplayOn = 0x29,
37+
ColumnAddressSet = 0x2A,
38+
PageAddressSet = 0x2B,
39+
MemoryWrite = 0x2C,
40+
PartialArea = 0x30,
41+
VerticalScrollingDefinition = 0x33,
42+
TearingEffectLineOff = 0x34,
43+
TearingEffectLineOn = 0x34,
44+
MemoryAccessControl = 0x36,
45+
VerticalScrollingStartAddress = 0x37,
46+
IdleModeOff = 0x38,
47+
IdleModeOn = 0x39,
48+
PixelFormatSet = 0x3A,
49+
WriteMemoryContinue = 0x3C,
50+
SetTearScanline = 0x44,
51+
GetScanline = 0x45,
52+
WriteDisplayBrightness = 0x51,
53+
WriteCtrlDisplay = 0x53,
54+
ReadId1 = 0xDA,
55+
ReadId2 = 0xDB,
56+
ReadId3 = 0xDC,
57+
RgbInterfaceSignalControl = 0xB0,
58+
BlankingPorchControl = 0xB5,
59+
DisplayFunctionControl = 0xB6,
60+
TeControl = 0xBA,
61+
InterfaceControl = 0xF6,
62+
PowerCriterionControl = 0xC1,
63+
VcoreVoltageControl = 0xA7,
64+
Vreg1aVoltageControl = 0xC3,
65+
Vreg2aVoltageControl = 0xC9,
66+
FrameRate = 0xE8,
67+
Spi2DataControl = 0xE9,
68+
ChargePumpFrequentControl = 0xEC,
69+
InnerRegisterEnable1 = 0xFE,
70+
InnerRegisterEnable2 = 0xEF,
71+
SetGamma1 = 0xF0,
72+
SetGamma2 = 0xF1,
73+
SetGamma3 = 0xF2,
74+
SetGamma4 = 0xF3
75+
};
76+
77+
enum GC9A01_PIXEL_FORMAT : CLR_UINT8
78+
{
79+
Pixel12Bit = 0x03,
80+
Pixel16Bit = 0x05,
81+
Pixel18Bit = 0x06
82+
};
83+
84+
enum GC9A01_MEMORY_ACCESS_CTRL : CLR_UINT8
85+
{
86+
Portrait = 0x18,
87+
Portrait180 = 0x28,
88+
Landscape = 0x48,
89+
Landscape180 = 0x88
90+
};
91+
92+
void DisplayDriver::SetupDisplayAttributes()
93+
{
94+
Attributes.LongerSide = g_DisplayInterfaceConfig.Screen.width;
95+
Attributes.ShorterSide = g_DisplayInterfaceConfig.Screen.height;
96+
Attributes.Width = Attributes.LongerSide;
97+
Attributes.Height = Attributes.ShorterSide;
98+
Attributes.PowerSave = PowerSaveState::NORMAL;
99+
Attributes.BitsPerPixel = 16;
100+
g_DisplayInterface.GetTransferBuffer(Attributes.TransferBuffer, Attributes.TransferBufferSize);
101+
return;
102+
}
103+
104+
bool DisplayDriver::Initialize()
105+
{
106+
SetupDisplayAttributes();
107+
108+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::InnerRegisterEnable2);
109+
g_DisplayInterface.SendCommand(2, 0xEB, 0x14);
110+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::InnerRegisterEnable1);
111+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::InnerRegisterEnable2);
112+
g_DisplayInterface.SendCommand(2, 0xEB, 0x14);
113+
g_DisplayInterface.SendCommand(2, 0x84, 0x40);
114+
g_DisplayInterface.SendCommand(2, 0x85, 0xFF);
115+
g_DisplayInterface.SendCommand(2, 0x86, 0xFF);
116+
g_DisplayInterface.SendCommand(2, 0x87, 0xFF);
117+
g_DisplayInterface.SendCommand(2, 0x88, 0x0A);
118+
g_DisplayInterface.SendCommand(2, 0x89, 0x21);
119+
g_DisplayInterface.SendCommand(2, 0x8A, 0x00);
120+
g_DisplayInterface.SendCommand(2, 0x8B, 0x80);
121+
g_DisplayInterface.SendCommand(2, 0x8C, 0x01);
122+
g_DisplayInterface.SendCommand(2, 0x8D, 0x01);
123+
g_DisplayInterface.SendCommand(2, 0x8E, 0xFF);
124+
g_DisplayInterface.SendCommand(2, 0x8F, 0xFF);
125+
126+
g_DisplayInterface.SendCommand(3, GC9A01_CMD::DisplayFunctionControl, 0x00, 0x20);
127+
128+
g_DisplayInterface.SendCommand(2, GC9A01_CMD::PixelFormatSet, GC9A01_PIXEL_FORMAT::Pixel16Bit);
129+
g_DisplayInterface.SendCommand(5, 0x90, 0x08, 0x08, 0x08, 0x08);
130+
g_DisplayInterface.SendCommand(2, 0xBD, 0x06);
131+
g_DisplayInterface.SendCommand(2, 0xBC, 0x00);
132+
g_DisplayInterface.SendCommand(4, 0xFF, 0x60, 0x01, 0x04);
133+
g_DisplayInterface.SendCommand(2, GC9A01_CMD::Vreg1aVoltageControl, 0x13);
134+
g_DisplayInterface.SendCommand(2, 0xC4, 0x13);
135+
g_DisplayInterface.SendCommand(2, GC9A01_CMD::Vreg2aVoltageControl, 0x22);
136+
g_DisplayInterface.SendCommand(2, 0xBE, 0x11);
137+
g_DisplayInterface.SendCommand(3, 0xE1, 0x10, 0x0E);
138+
g_DisplayInterface.SendCommand(4, 0xDF, 0x21, 0x0C, 0x02);
139+
g_DisplayInterface.SendCommand(7, 0xF0, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A);
140+
g_DisplayInterface.SendCommand(7, 0xF1, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F);
141+
g_DisplayInterface.SendCommand(7, 0xF2, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A);
142+
g_DisplayInterface.SendCommand(7, 0xF3, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F);
143+
g_DisplayInterface.SendCommand(3, 0xED, 0x1B, 0x0B);
144+
g_DisplayInterface.SendCommand(2, 0xAE, 0x77);
145+
g_DisplayInterface.SendCommand(2, 0xCD, 0x63);
146+
g_DisplayInterface.SendCommand(10, 0x70, 0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03);
147+
g_DisplayInterface.SendCommand(2, GC9A01_CMD::FrameRate, 0x34);
148+
g_DisplayInterface.SendCommand(13, 0x62, 0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70);
149+
g_DisplayInterface.SendCommand(13, 0x63, 0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70);
150+
g_DisplayInterface.SendCommand(8, 0x64, 0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07);
151+
g_DisplayInterface.SendCommand(11, 0x66, 0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00);
152+
g_DisplayInterface.SendCommand(11, 0x67, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98);
153+
g_DisplayInterface.SendCommand(8, 0x74, 0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00);
154+
g_DisplayInterface.SendCommand(3, 0x98, 0x3E, 0x07);
155+
g_DisplayInterface.SendCommand(1, 0x35);
156+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::DisplayInversionOn);
157+
158+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::SleepOut);
159+
OS_DELAY(120);
160+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::DisplayOn);
161+
OS_DELAY(20);
162+
163+
return true;
164+
}
165+
166+
bool DisplayDriver::Uninitialize()
167+
{
168+
Clear();
169+
return true;
170+
}
171+
172+
void DisplayDriver::Clear()
173+
{
174+
SetWindow(0, 0, Attributes.Width - 1, Attributes.Height - 1);
175+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::MemoryWrite);
176+
g_DisplayInterface.FillData16(0, Attributes.Width * Attributes.Height);
177+
}
178+
179+
void DisplayDriver::DisplayBrightness(CLR_INT16 brightness)
180+
{
181+
_ASSERTE(brightness >= 0 && brightness <= 100);
182+
g_DisplayInterface.SendCommand(2, GC9A01_CMD::WriteDisplayBrightness, (CLR_UINT8)brightness);
183+
}
184+
185+
bool DisplayDriver::SetWindow(CLR_INT16 x1, CLR_INT16 y1, CLR_INT16 x2, CLR_INT16 y2)
186+
{
187+
CLR_UINT8 buff[4];
188+
buff[0] = ((x1 + g_DisplayInterfaceConfig.Screen.x) >> 8) & 0xFF;
189+
buff[1] = (x1 + g_DisplayInterfaceConfig.Screen.x) & 0xFF;
190+
buff[2] = ((x2 + g_DisplayInterfaceConfig.Screen.x) >> 8) & 0xFF;
191+
buff[3] = (x2 + g_DisplayInterfaceConfig.Screen.x) & 0xFF;
192+
g_DisplayInterface.SendCommand(5, GC9A01_CMD::ColumnAddressSet, buff[0], buff[1], buff[2], buff[3]);
193+
194+
buff[0] = ((y1 + g_DisplayInterfaceConfig.Screen.y) >> 8) & 0xFF;
195+
buff[1] = (y1 + g_DisplayInterfaceConfig.Screen.y) & 0xFF;
196+
buff[2] = ((y2 + g_DisplayInterfaceConfig.Screen.y) >> 8) & 0xFF;
197+
buff[3] = (y2 + g_DisplayInterfaceConfig.Screen.y) & 0xFF;
198+
g_DisplayInterface.SendCommand(5, GC9A01_CMD::PageAddressSet, buff[0], buff[1], buff[2], buff[3]);
199+
200+
return true;
201+
}
202+
203+
void DisplayDriver::BitBlt(
204+
int srcX,
205+
int srcY,
206+
int width,
207+
int height,
208+
int stride,
209+
int screenX,
210+
int screenY,
211+
CLR_UINT32 data[])
212+
{
213+
// 16 bit colour RRRRRGGGGGGBBBBB mode 565
214+
215+
ASSERT((screenX >= 0) && ((screenX + width) <= Attributes.Width));
216+
ASSERT((screenY >= 0) && ((screenY + height) <= Attributes.Height));
217+
218+
SetWindow(screenX, screenY, (screenX + width - 1), (screenY + height - 1));
219+
220+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::MemoryWrite);
221+
g_DisplayInterface.SendData16Windowed((CLR_UINT16 *)&data[0], srcX, srcY, width, height, stride, true);
222+
}
223+
224+
void DisplayDriver::PowerSave(PowerSaveState powerState)
225+
{
226+
switch (powerState)
227+
{
228+
default: // Illegal fall through to Power on
229+
case PowerSaveState::NORMAL:
230+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::SleepOut);
231+
OS_DELAY(120);
232+
break;
233+
case PowerSaveState::SLEEP:
234+
g_DisplayInterface.SendCommand(1, GC9A01_CMD::EnterSleepMode);
235+
OS_DELAY(120);
236+
break;
237+
}
238+
return;
239+
}
240+
241+
void DisplayDriver::SetDefaultOrientation()
242+
{
243+
ChangeOrientation(LANDSCAPE);
244+
}
245+
246+
bool DisplayDriver::ChangeOrientation(DisplayOrientation orientation)
247+
{
248+
CLR_UINT8 orientationByte = GC9A01_MEMORY_ACCESS_CTRL::Portrait;
249+
switch (orientation)
250+
{
251+
case DisplayOrientation::PORTRAIT:
252+
orientationByte = GC9A01_MEMORY_ACCESS_CTRL::Portrait;
253+
break;
254+
case DisplayOrientation::PORTRAIT180:
255+
orientationByte = GC9A01_MEMORY_ACCESS_CTRL::Portrait180;
256+
break;
257+
case DisplayOrientation::LANDSCAPE:
258+
orientationByte = GC9A01_MEMORY_ACCESS_CTRL::Landscape;
259+
break;
260+
case DisplayOrientation::LANDSCAPE180:
261+
orientationByte = GC9A01_MEMORY_ACCESS_CTRL::Portrait180;
262+
break;
263+
}
264+
265+
g_DisplayInterface.SendCommand(2, GC9A01_CMD::MemoryAccessControl, orientationByte);
266+
return true;
267+
}
268+
269+
CLR_UINT32 DisplayDriver::PixelsPerWord()
270+
{
271+
return (32 / Attributes.BitsPerPixel);
272+
}
273+
274+
CLR_UINT32 DisplayDriver::WidthInWords()
275+
{
276+
return ((Attributes.Width + (PixelsPerWord() - 1)) / PixelsPerWord());
277+
}
278+
279+
CLR_UINT32 DisplayDriver::SizeInWords()
280+
{
281+
return (WidthInWords() * Attributes.Height);
282+
}
283+
284+
CLR_UINT32 DisplayDriver::SizeInBytes()
285+
{
286+
return (SizeInWords() * sizeof(CLR_UINT32));
287+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
4+
// See LICENSE file in the project root for full license information.
5+
//
6+
7+
#include "TouchDevice.h"
8+
#include "TouchInterface.h"
9+
10+
struct TouchDevice g_TouchDevice;
11+
extern TouchInterface g_TouchInterface;
12+
13+
bool TouchDevice::Initialize()
14+
{
15+
ReadsToIgnore = 1;
16+
ReadsPerSample = 1;
17+
MaxFilterDistance = 1; // This is actually squared value of the max distance allowed between two points.
18+
return true;
19+
}
20+
21+
bool TouchDevice::Enable(GPIO_INTERRUPT_SERVICE_ROUTINE touchIsrProc)
22+
{
23+
if (touchIsrProc == NULL)
24+
{
25+
};
26+
27+
return true;
28+
}
29+
30+
bool TouchDevice::Disable()
31+
{
32+
return true;
33+
}
34+
35+
TouchPointDevice TouchDevice::GetPoint()
36+
{
37+
TouchPointDevice TouchValue;
38+
39+
TouchValue.x = 0;
40+
TouchValue.y = 0;
41+
42+
return TouchValue;
43+
}

0 commit comments

Comments
 (0)