1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
|
/*
* This file is subject to the terms of the GFX License. If a copy of
* the license was not distributed with this file, you can obtain one at:
*
* http://ugfx.io/license.html
*/
#include "gfx.h"
#if GFX_USE_GDISP
#define GDISP_DRIVER_VMT GDISPVMT_UC8173
#include "gdisp_lld_config.h"
#include "../../../src/gdisp/gdisp_driver.h"
#if defined(GDISP_SCREEN_HEIGHT) || defined(GDISP_SCREEN_HEIGHT)
#if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
#warning "GDISP: This low level driver does not support setting a screen size. It is being ignored."
#elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
COMPILER_WARNING("GDISP: This low level driver does not support setting a screen size. It is being ignored.")
#endif
#undef GDISP_SCREEN_WIDTH
#undef GDISP_SCREEN_HEIGHT
#endif
/*------------------ Load the board file ------------------*/
typedef struct UC8173Lut {
gU8 const *lutVCOM; // 32 bytes
gU8 const *lutFW; // 512 bytes
gU8 const *lutFT; // 128 bytes
gBool regal;
} UC8173Lut;
#include "board_UC8173.h"
/*------------------ Default UC8173 parameters ------------------*/
#ifndef UC8173_REVERSEAXIS_Y
#define UC8173_REVERSEAXIS_Y GFXOFF
#endif
#ifndef UC8173_REVERSEAXIS_X
#define UC8173_REVERSEAXIS_X GFXOFF
#endif
#ifndef UC8173_DEFAULT_MODE
#define UC8173_DEFAULT_MODE 0
#endif
#ifndef UC8173_USE_OTP_LUT
#define UC8173_USE_OTP_LUT GFXOFF
#endif
#ifndef UC8173_CAN_READ
#define UC8173_CAN_READ GFXOFF
#endif
#ifdef UC8173_VCOM_VOLTAGE
#define UC8173_VCOM_VOLTAGEBYTE (((UC8173_VCOM_VOLTAGE) + 0.1)/-0.05)
#endif
#ifndef UC8171_BORDER
#define UC8171_BORDER 0 /* 0 = Hi-Z, 1 = Black, 2 = White */
#endif
#ifndef UC8173_INIT_REAL_LUT
#define UC8173_INIT_REAL_LUT GFXON
#endif
#define UC8173_HEIGHT 240
#define UC8173_WIDTH 240
/*------------------ Set FB parameters ------------------*/
#define FB_REVERSEAXIS_Y UC8173_REVERSEAXIS_Y
#define FB_REVERSEAXIS_X UC8173_REVERSEAXIS_X
#define FB_WIDTH UC8173_WIDTH
#define FB_HEIGHT UC8173_HEIGHT
#define FB_PAGES 1
#define FB_PIXELORDER_MSB GFXON
/*------------------ Include Generic FB Code ------------------*/
// This FB is for 1,2 or 4 bits per pixel packed along the x-axis
#define FB_TYPE_PIXELS (LLDCOLOR_TYPE_BITS/LLDCOLOR_BITS)
#define FB_AXIS_MASK (FB_TYPE_PIXELS-1)
#define FB_LINE_TYPES ((FB_WIDTH+FB_TYPE_PIXELS-1)/FB_TYPE_PIXELS)
#define FB_PAGE_TYPES (FB_LINE_TYPES*FB_HEIGHT)
#define FB_ADDR(fbp, pg, px, py) ((fbp)->fb + ((px)/FB_TYPE_PIXELS + (py)*FB_LINE_TYPES + (pg)*FB_PAGE_TYPES))
#if FB_PIXELORDER_MSB
#define FB_COLOR(px, py, c) ((c) << ((LLDCOLOR_TYPE_BITS-LLDCOLOR_BITS)-(((px) & FB_AXIS_MASK)<<(LLDCOLOR_BITS-1))))
#else
#define FB_COLOR(px, py, c) ((c) << (((px) & FB_AXIS_MASK)<<(LLDCOLOR_BITS-1)))
#endif
#define FB_FLUSH_REQUIRED(fbp) ((fbp)->fa0.x < (fbp)->fa1.x)
#define FB_FLUSH_WIDTH(fbp) ((fbp)->fa1.x - (fbp)->fa0.x + 1)
#define FB_FLUSH_HEIGHT(fbp) ((fbp)->fa1.y - (fbp)->fa0.y + 1)
#define FB_FLUSH_ALL(fbp) { (fbp)->fa0.x = (fbp)->fa0.y = 0; (fbp)->fa1.x = FB_WIDTH-1; (fbp)->fa1.y = FB_HEIGHT-1; }
#define FB_FLUSH_NONE(fbp) { (fbp)->fa0.x = FB_WIDTH; (fbp)->fa0.y = FB_HEIGHT; (fbp)->fa1.x = (fbp)->fa1.y = -1; }
#define FB_FLUSH_POINT(fbp, px, py) { \
if ((px) < (fbp)->fa0.x) (fbp)->fa0.x = (px) & ~FB_AXIS_MASK; \
if ((px) > (fbp)->fa1.x) (fbp)->fa1.x = (px) | FB_AXIS_MASK; \
if ((py) < (fbp)->fa0.y) (fbp)->fa0.y = (py); \
if ((py) > (fbp)->fa1.y) (fbp)->fa1.y = (py); \
}
#define FB_SETPIXEL(fbp, pg, px, py, c) { \
LLDCOLOR_TYPE *p, oc; \
p = FB_ADDR((fbp), (pg), (px), (py)); \
oc = (*p & ~FB_COLOR((px), (py), LLDCOLOR_MASK())) | FB_COLOR((px), (py), (c)); \
if (oc != *p) { *p = oc; FB_FLUSH_POINT((fbp), (px), (py)); } \
}
typedef struct FBpriv {
gPoint fa0, fa1;
LLDCOLOR_TYPE fb[FB_PAGE_TYPES * FB_PAGES];
} FBpriv;
/*------------------ UC8173 Driver Code ------------------*/
#include "UC8173.h"
// UC8173 Inernal Macros & types
typedef struct UC8173_Private {
UC8173Lut *lut;
FBpriv fb;
} UC8173_Private;
#if !UC8173_INIT_REAL_LUT
static gU8 const UC8173_LUT_None[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00
};
#endif
LLDSPEC gBool gdisp_lld_init(GDisplay* g)
{
UC8173_Private *priv;
// Allocate the private area
g->priv = gfxAlloc(sizeof(UC8173_Private));
if (!g->priv)
return gFalse;
priv = (UC8173_Private *)g->priv;
// Initialize the private area
// As the display is non-volatile we need to ensure a full flush on the first draw
priv->lut = &UC8173_ModeTable[UC8173_DEFAULT_MODE];
FB_FLUSH_ALL(&priv->fb);
// Initialise the board interface
if (!init_board(g))
return gFalse;
// Hardware reset
setpin_reset(g, gTrue);
gfxSleepMilliseconds(100);
setpin_reset(g, gFalse);
gfxSleepMilliseconds(300);
// Acquire the bus
acquire_bus(g);
// Booster soft-start - Values from example code
write_cmd(g, BTST);
write_data(g, 0x17); // data: PhaseA: Bits76=StartupTime [10ms+n*10ms def=0x00], Bits543=Strength, Bits210=MinGDROffTime
write_data(g, 0x97); // data: PhaseB: Bits76=StartupTime [10ms+n*10ms def=0x00], Bits543=Strength, Bits210=MinGDROffTime
write_data(g, 0x20); // data: PhaseC: Bits543=Strength, Bits210=MinGDROffTime [def=0x07]
// Power settings - Values from datasheet default values
write_cmd(g, PWR);
write_data(g, 0x03); // data: 0x02=Internal VDH/VDL, 0x01=Internal VGH/VGL
write_data(g, 0x00); // data: Always 0x00
write_data(g, 0x26); // data: 0x00->0x3F VSH Voltage 2.4V -> 11.0V step 0.2V - default=10V (0x26)
write_data(g, 0x26); // data: 0x00->0x3F VSL Voltage 2.4V -> 11.0V step 0.2V - default=10V (0x26)
write_data(g, 0x03); // data: 0x00->0x3F Red Voltage 2.4V -> 11.0V step 0.2V - default= 3V (0x03)
// Power-on
write_cmd(g, PON);
while (!getpin_busy(g));
// Panel setting register - Values from datasheet (OTP untested)
write_cmd(g, PSR); // data: 0x08 + ..., 0x04=Shift Right, 0x02=Booster On, 0x01=Reset Off
write_data(g, 0x0F);
#if UC8173_USE_OTP_LUT
write_cmd(g, 0x86); // data: Always 0x86, write_data=Register LUT, write_cmd=OTP LUT
#else
write_data(g, 0x86);
#endif
// Power-off sequence - Values from example code
write_cmd(g, PFS); // data: Shutdown frames - 0x00=1, 0x10=2, 0x20=3, 0x30=4
write_data(g, 0x00);
// PLL control - Values from example code
write_cmd(g, LPRD); // data: PLL Clock Freq = 1MHz/(n+1) Min=0x04 (=200kHz)
write_data(g, 0x25);
// Internal temperature sensor enable - Values from example code
write_cmd(g, TSE); // data: 0x00=Use internal temperature sensor, 0x80=external, Bits0-4=signed degrees to add eg 0x0F = -1
write_data(g, 0x00);
// VCOM settings - Values from datasheet
write_cmd(g, CDI); // data: 0x80=Border Hi-Z, 0x40=Border Output DC enabled, 0x30=BorderColor, 0x01=Normal/Inverted
#if UC8171_BORDER == 0
write_data(g, 0x81);
#elif UC8171_BORDER == 1
write_data(g, 0x41);
#elif UC8171_BORDER == 2
write_data(g, 0x71);
#else
#error "UC8173: UC8173_BORDER must be 0..2"
#endif
// More VCOM data - Values from example code
write_data(g, 0x20); // data: 0xX0=Src->VCOM interval (n+1 HSync, Def=0011), 0b0000-00XX=VCOM->Src interval (top 2 bits)
write_data(g, 0x10); // data: 0xXX=VCOM->Src interval (bottom 8 bits) Def=0x18
// Set display panel resolution - from datasheet: should always be 240x240 for this chip
write_cmd(g, TRES);
write_data(g, (UC8173_WIDTH-1));
write_data(g, ((UC8173_HEIGHT-1)>>8));
write_data(g, ((UC8173_HEIGHT-1) & 0xFF));
// Undocumented register - Values from example code
write_cmd(g, GDS);
write_data(g, 0xA9);
write_data(g, 0xA9);
write_data(g, 0xEB);
write_data(g, 0xEB);
write_data(g, 0x02);
// Auto measure VCOM - Values from example code
write_cmd(g, AMV);
write_data(g, 0x11);
while (!getpin_busy(g));
// Get current VCOM value and write it
#if defined(UC8173_VCOM_VOLTAGEBYTE)
// VCM_DC setting
write_cmd(g, VDCS);
write_data(g, UC8173_VCOM_VOLTAGEBYTE); // was 0x12 in example code = -1.0V
#elif UC8173_CAN_READ
{
gU8 vc;
write_cmd(g, VV);
vc = read_data(g);
write_cmd(g, VDCS);
write_data(g, vc);
}
#else
#error "UC8173: Either UC8173_VCOM_VOLTAGE or UC8173_VCOM_VOLTAGEBYTE must be defined or UC8173_CAN_READ must be GFXON"
#endif
// Undocumented register - Values from example code
write_cmd(g, VBDS);
write_data(g, 0x22);
// Undocumented register - Values from example code
write_cmd(g, LVSEL);
write_data(g, 0x02);
// Undocumented register - Values from example code
write_cmd(g, GBS);
write_data(g, 0x02);
write_data(g, 0x02);
// Undocumented register - Values from example code
write_cmd(g, GSS);
write_data(g, 0x02);
write_data(g, 0x02);
// Undocumented register - Values from example code
write_cmd(g, DF); // For REGAL (???)
write_data(g, 0x1F);
write_cmd(g, PON);
while (!getpin_busy(g));
#if UC8173_INIT_REAL_LUT
// Load the real LUT's
write_cmd(g, LUT_KWVCOM);
write_data_burst(g, priv->lut->lutVCOM, 32);
write_cmd(g, LUT_KW);
write_data_burst(g, priv->lut->lutFW, 512);
write_cmd(g, LUT_FT);
write_data_burst(g, priv->lut->lutFT, 128);
#else
// Clear the LUT's - Values from example code
write_cmd(g, LUT_KW);
write_data_burst(g, UC8173_LUT_None, sizeof(UC8173_LUT_None));
write_cmd(g, LUT_KWVCOM);
write_data_burst(g, UC8173_LUT_None, sizeof(UC8173_LUT_None));
#endif
write_cmd(g, POF);
while (getpin_busy(g));
// Finish Init
post_init_board(g);
// Release the bus
release_bus(g);
// Initialise the GDISP structure
g->g.Width = UC8173_WIDTH;
g->g.Height = UC8173_HEIGHT;
g->g.Orientation = gOrientation0;
g->g.Powermode = gPowerOn;
g->g.Backlight = 0;
g->g.Contrast = 0;
return gTrue;
}
#if GDISP_HARDWARE_FLUSH
LLDSPEC void gdisp_lld_flush(GDisplay* g)
{
gCoord cy, cx, dx, dy;
LLDCOLOR_TYPE *fb;
UC8173_Private *priv;
priv = (UC8173_Private *)g->priv;
if (!FB_FLUSH_REQUIRED(&priv->fb))
return;
#if 0
FB_FLUSH_ALL(&priv->fb);
#endif
// Acquire the bus to communicate with the display controller
acquire_bus(g);
// Upload the new temperature LUT
write_cmd(g, LUT_KWVCOM);
write_data_burst(g, priv->lut->lutVCOM, 32);
write_cmd(g, LUT_KW);
write_data_burst(g, priv->lut->lutFW, 512);
write_cmd(g, LUT_FT);
write_data_burst(g, priv->lut->lutFT, 128);
// Calculate the width, height
cx = FB_FLUSH_WIDTH(&priv->fb);
cy = FB_FLUSH_HEIGHT(&priv->fb);
// Setup the window
// Datasheet says x,y,w,h but in practice it needs to be x,y,w-1,h-1
write_cmd(g, DTMW);
write_data(g, (gU8)((priv->fb.fa0.x >> 0) & 0xFF));
write_data(g, (gU8)((priv->fb.fa0.y >> 8) & 0x03));
write_data(g, (gU8)((priv->fb.fa0.y >> 0) & 0xFF));
write_data(g, (gU8)(((cx-1) >> 0) & 0xFF));
write_data(g, (gU8)(((cy-1) >> 8) & 0x03));
write_data(g, (gU8)(((cy-1) >> 0) & 0xFF));
// Transfer the buffer
#if GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_MONO
write_cmd(g, DTM4);
#elif GDISP_LLD_PIXELFORMAT == GDISP_PIXELFORMAT_GRAY4
write_cmd(g, DTM2);
#else
#error "UC8173: Unsupported driver color format"
#endif
dx = (cx+FB_TYPE_PIXELS-1)/FB_TYPE_PIXELS * (LLDCOLOR_TYPE_BITS/8);
for (fb = FB_ADDR(&priv->fb, 0, priv->fb.fa0.x, priv->fb.fa0.y), dy = cy; dy; dy--, fb += FB_LINE_TYPES)
write_data_burst(g, (gU8 *)fb, dx);
// Power-up the DC/DC converter to update the display panel
write_cmd(g, PON);
while (!getpin_busy(g));
// Refresh the panel contents
// Datasheet says x,y,w,h but in practice it needs to be x,y,w-1,h-1
write_cmd(g, DRF); // data: Partial Scan = 0x10, REGAL = 0x08, VCOM_DoNothing = 0x04 (GC4/A2 = 0x00, GU4 = 0x08)
write_data(g, (priv->lut->regal ? 0x08 : 0x00));
write_data(g, (gU8)((priv->fb.fa0.x >> 0) & 0xFF));
write_data(g, (gU8)((priv->fb.fa0.y >> 8) & 0x03));
write_data(g, (gU8)((priv->fb.fa0.y >> 0) & 0xFF));
write_data(g, (gU8)(((cx-1) >> 0) & 0xFF));
write_data(g, (gU8)(((cy-1) >> 8) & 0x03));
write_data(g, (gU8)(((cy-1) >> 0) & 0xFF));
while (!getpin_busy(g));
// Power-down the DC/DC converter
write_cmd(g, POF);
while (getpin_busy(g));
// Release the bus
release_bus(g);
// Mark as flushed
FB_FLUSH_NONE(&priv->fb);
}
#endif
#if GDISP_HARDWARE_DRAWPIXEL
LLDSPEC void gdisp_lld_draw_pixel(GDisplay* g)
{
gCoord x, y;
UC8173_Private *priv;
priv = (UC8173_Private *)g->priv;
// Handle the different possible orientations
#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
switch(g->g.Orientation) {
default:
case gOrientation0:
#if FB_REVERSEAXIS_X
x = FB_WIDTH-1 - g->p.x;
#else
x = g->p.x;
#endif
#if FB_REVERSEAXIS_Y
y = FB_HEIGHT-1 - g->p.y;
#else
y = g->p.y;
#endif
break;
case gOrientation90:
#if FB_REVERSEAXIS_X
x = FB_WIDTH-1 - g->p.y;
#else
x = g->p.y;
#endif
#if FB_REVERSEAXIS_Y
y = g->p.x;
#else
y = FB_HEIGHT-1 - g->p.x;
#endif
break;
case gOrientation180:
#if FB_REVERSEAXIS_X
x = g->p.x;
#else
x = FB_WIDTH-1 - g->p.x;
#endif
#if FB_REVERSEAXIS_Y
y = g->p.y;
#else
y = FB_HEIGHT-1 - g->p.y;
#endif
break;
case gOrientation270:
#if FB_REVERSEAXIS_X
x = g->p.y;
#else
x = FB_WIDTH-1 - g->p.y;
#endif
#if FB_REVERSEAXIS_Y
y = FB_HEIGHT-1 - g->p.x;
#else
y = g->p.x;
#endif
break;
}
#else
#if FB_REVERSEAXIS_X
x = FB_WIDTH-1 - g->p.x;
#else
x = g->p.x;
#endif
#if FB_REVERSEAXIS_Y
y = FB_HEIGHT-1 - g->p.y;
#else
y = g->p.y;
#endif
#endif
// Modify the framebuffer content
FB_SETPIXEL(&priv->fb, 0, x, y, gdispColor2Native(g->p.color));
}
#endif
#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
LLDSPEC void gdisp_lld_control(GDisplay* g) {
UC8173_Private *priv;
priv = (UC8173_Private *)g->priv;
switch(g->p.x) {
// Custom gdispControl() to invert the framebuffer
case GDISP_CONTROL_INVERT:
{
unsigned i;
LLDCOLOR_TYPE *fb;
// Invert the framebuffer
for (fb = priv->fb.fb, i = FB_PAGE_TYPES; i ; i--, fb++)
fb[0] = ~fb[0];
// We should flush these changes to the display controller framebuffer at some point
FB_FLUSH_ALL(&priv->fb);
}
break;
// Custom gdispControl() to set which EINK Mode (waveform) to use
case GDISP_CONTROL_SETMODE:
if ((unsigned)g->p.ptr < sizeof(UC8173_ModeTable)/sizeof(UC8173_ModeTable[0]))
priv->lut = &UC8173_ModeTable[(unsigned)g->p.ptr];
break;
// Custom gdispControl() to set the EINK border
case GDISP_CONTROL_SETBORDER:
if ((unsigned)g->p.ptr > 2)
break;
acquire_bus(g);
write_cmd(g, PON);
while (!getpin_busy(g));
write_cmd(g, CDI);
switch((unsigned)g->p.ptr) {
case 0:
write_data(g, 0x81); // Border Hi-Z
break;
case 1:
write_data(g, 0x41); // Border Black
break;
case 2:
write_data(g, 0x71); // Border White
break;
}
write_cmd(g, POF);
while (getpin_busy(g));
release_bus(g);
break;
default:
break;
}
}
#endif
#endif // GFX_USE_GDISP
|