|
1 /* |
|
2 * GraphLCD driver library |
|
3 * |
|
4 * noritake800.c - Noritake 800(A) series VFD graphlcd driver, |
|
5 * different "Medium 0.6 dot" sizes should work, |
|
6 * see http://www.noritake-itron.com: |
|
7 * - GU128X64-800A, |
|
8 * - GU256X32-800A, |
|
9 * - GU128X32-800A, |
|
10 * - GU160X16-800A, |
|
11 * - GU160X32-800A, |
|
12 * - GU192X16-800A. |
|
13 * |
|
14 * based on: |
|
15 * ideas and HW-command related stuff from the open source project |
|
16 * "lcdplugin for Winamp": |
|
17 * (c) 1999 - 2003 Markus Zehnder <lcdplugin AT markuszehnder.ch> |
|
18 * GU256x64-372 driver module for graphlcd |
|
19 * (c) 20040410 Andreas 'Randy' Weinberger <randy AT smue.org> |
|
20 * gu140x32f driver module for graphlcd |
|
21 * (c) 2003 Andreas Brachold <vdr04 AT deltab de> |
|
22 * HD61830 device |
|
23 * (c) 2001-2003 by Carsten Siebholz <c.siebholz AT t-online.de> |
|
24 * lcdproc 0.4 driver hd44780-ext8bit |
|
25 * (c) 1999, 1995 Benjamin Tse <blt AT Comports.com> |
|
26 * |
|
27 * This file is released under the GNU General Public License. Refer |
|
28 * to the COPYING file distributed with this package. |
|
29 * |
|
30 * (c) 2004 Lucian Muresan <lucianm AT users.sourceforge.net> |
|
31 */ |
|
32 |
|
33 #include <errno.h> |
|
34 #include <syslog.h> |
|
35 #include <unistd.h> |
|
36 #include <sys/time.h> |
|
37 |
|
38 #include "common.h" |
|
39 #include "config.h" |
|
40 #include "noritake800.h" |
|
41 #include "port.h" |
|
42 |
|
43 namespace GLCD |
|
44 { |
|
45 |
|
46 /* LPT Control Port lines */ |
|
47 #define LPT_CTL_HI_DIR 0x20 |
|
48 #define LPT_CTL_HI_IRQEN 0x10 |
|
49 #define LPT_CTL_LO_STROBE 0x01 |
|
50 #define LPT_CTL_LO_LFEED 0x02 |
|
51 #define LPT_CTL_LO_INIT 0x04 |
|
52 #define LPT_CTL_LO_SELECT 0x08 |
|
53 |
|
54 /* Noritake 800(A) VFD control signals bit masks*/ |
|
55 #define VFDSGN_CD 0x01 |
|
56 #define VFDSGN_WR 0x02 |
|
57 #define VFDSGN_RD 0x04 |
|
58 #define VFDSGN_CSS 0x08 |
|
59 |
|
60 //wirings |
|
61 #define WIRING_LIQUIDMP3 0 |
|
62 static const std::string kWiringLiquidmp3 = "LiquidMp3"; |
|
63 #define WIRING_MZ 1 |
|
64 static const std::string kWiringMZ = "MZ"; |
|
65 // ... other wirings may follow |
|
66 |
|
67 /* Command set for this display */ |
|
68 #define ANDCNTL 0x03 |
|
69 #define ORCNTL 0x01 |
|
70 #define XORCNTL 0x02 |
|
71 #define Init800A 0x5F /* initialization code sequence 5f */ |
|
72 #define Init800B 0x62 |
|
73 #define Init800C 0x00+n |
|
74 #define Init800D 0xFF |
|
75 #define CLEARSCREENS 0x5e /* clear all screens (layers) */ |
|
76 #define LAYER0ON 0x24 /* screen0 both on */ |
|
77 #define LAYER1ON 0x28 /* screen1 both on */ |
|
78 #define LAYERSON 0x2c /* both screens both on */ |
|
79 #define LAYERSOFF 0x20 /* screens both off */ |
|
80 #define ORON 0x40 /* OR screens */ |
|
81 #define ANDON 0x48 /* AND screens */ |
|
82 #define XORON 0x44 /* XOR screens */ |
|
83 #define SETX 0x64 /* set X position */ |
|
84 #define SETY 0x60 /* set Y position */ |
|
85 #define HSHIFT 0x70 /* set horizontal shift */ |
|
86 #define VSHIFT 0xB0 |
|
87 #define AUTOINCOFF 0x80 /* address auto increment off */ |
|
88 #define SETPOSITION 0xff |
|
89 |
|
90 |
|
91 cDriverNoritake800::cDriverNoritake800(cDriverConfig * config) |
|
92 { |
|
93 int x = 0; |
|
94 m_bGraphScreen0_On = true; |
|
95 m_bGraphScreen1_On = false; |
|
96 // default initilaization for the wiring |
|
97 m_nWiring = WIRING_LIQUIDMP3; |
|
98 |
|
99 m_Config = config; |
|
100 m_oldConfig = new cDriverConfig(* config); |
|
101 |
|
102 m_pport = new cParallelPort(); |
|
103 |
|
104 m_nTimingAdjustCmd = 0; |
|
105 m_nRefreshCounter = 0; |
|
106 |
|
107 width = m_Config->width; // 128 |
|
108 if (width <= 0) |
|
109 width = 128; |
|
110 height = m_Config->height; // 64 |
|
111 if (height <= 0) |
|
112 height = 64; |
|
113 m_iSizeYb = (height + 7)/8; // 8 |
|
114 |
|
115 // |
|
116 // initialize wiring |
|
117 // |
|
118 for (unsigned int i = 0; i < m_Config->options.size(); i++) |
|
119 { |
|
120 if (m_Config->options[i].name == "Wiring") |
|
121 { |
|
122 if (m_Config->options[i].value == kWiringLiquidmp3) |
|
123 { |
|
124 m_nWiring = WIRING_LIQUIDMP3; |
|
125 } |
|
126 else if (m_Config->options[i].value == kWiringMZ) |
|
127 { |
|
128 m_nWiring = WIRING_MZ; |
|
129 } |
|
130 else |
|
131 syslog(LOG_ERR, "%s error: wiring %s not supported, using default wiring(%s)!\n", |
|
132 config->name.c_str(), config->options[i].value.c_str(), kWiringLiquidmp3.c_str()); |
|
133 } |
|
134 } |
|
135 // fill the wiring mask cache for all the 16 possibilities |
|
136 m_pWiringMaskCache = new unsigned char[16]; |
|
137 for (unsigned int i = 0; i < 16; i++) |
|
138 { |
|
139 m_pWiringMaskCache[i] = N800LptWiringMask(i); |
|
140 } |
|
141 |
|
142 // setup linear lcd array |
|
143 m_pDrawMem = new unsigned char*[width]; |
|
144 if (m_pDrawMem) |
|
145 { |
|
146 for (x = 0; x < width; x++) |
|
147 { |
|
148 m_pDrawMem[x] = new unsigned char[m_iSizeYb]; |
|
149 memset(m_pDrawMem[x], 0, m_iSizeYb); |
|
150 } |
|
151 } |
|
152 Clear(); |
|
153 |
|
154 // setup the lcd array for the "vertikal" mem |
|
155 m_pVFDMem = new unsigned char*[width]; |
|
156 if (m_pVFDMem) |
|
157 { |
|
158 for (x = 0; x < width; x++) |
|
159 { |
|
160 m_pVFDMem[x] = new unsigned char[m_iSizeYb]; |
|
161 memset(m_pVFDMem[x], 0, m_iSizeYb); |
|
162 } |
|
163 } |
|
164 ClearVFDMem(); |
|
165 } |
|
166 |
|
167 cDriverNoritake800::~cDriverNoritake800() |
|
168 { |
|
169 int x; |
|
170 |
|
171 if (m_pVFDMem) |
|
172 for (x = 0; x < (width + 7) / 8; x++) |
|
173 { |
|
174 delete[] m_pVFDMem[x]; |
|
175 } |
|
176 delete[] m_pVFDMem; |
|
177 if (m_pDrawMem) |
|
178 for (x = 0; x < (width + 7) / 8; x++) |
|
179 { |
|
180 delete[] m_pDrawMem[x]; |
|
181 } |
|
182 delete[] m_pDrawMem; |
|
183 delete[] m_pWiringMaskCache; |
|
184 delete m_oldConfig; |
|
185 delete m_pport; |
|
186 } |
|
187 |
|
188 void cDriverNoritake800::Clear() |
|
189 { |
|
190 for (int x = 0; x < width; x++) |
|
191 { |
|
192 memset(m_pDrawMem[x], 0, m_iSizeYb); |
|
193 } |
|
194 } |
|
195 |
|
196 void cDriverNoritake800::ClearVFDMem() |
|
197 { |
|
198 for (int x = 0; x < width; x++) |
|
199 { |
|
200 memset(m_pVFDMem[x], 0, m_iSizeYb); |
|
201 } |
|
202 } |
|
203 |
|
204 int cDriverNoritake800::DeInit() |
|
205 { |
|
206 if (m_pport->Close() != 0) |
|
207 return -1; |
|
208 return 0; |
|
209 } |
|
210 |
|
211 int cDriverNoritake800::CheckSetup() |
|
212 { |
|
213 if (m_Config->device != m_oldConfig->device || |
|
214 m_Config->port != m_oldConfig->port || |
|
215 m_Config->width != m_oldConfig->width || |
|
216 m_Config->height != m_oldConfig->height) |
|
217 { |
|
218 DeInit(); |
|
219 Init(); |
|
220 return 0; |
|
221 } |
|
222 |
|
223 if (m_Config->brightness != m_oldConfig->brightness) |
|
224 { |
|
225 m_oldConfig->brightness = m_Config->brightness; |
|
226 SetBrightness(m_Config->brightness); |
|
227 } |
|
228 |
|
229 if (m_Config->upsideDown != m_oldConfig->upsideDown || |
|
230 m_Config->invert != m_oldConfig->invert) |
|
231 { |
|
232 m_oldConfig->upsideDown = m_Config->upsideDown; |
|
233 m_oldConfig->invert = m_Config->invert; |
|
234 return 1; |
|
235 } |
|
236 return 0; |
|
237 } |
|
238 |
|
239 int cDriverNoritake800::Init() |
|
240 { |
|
241 int x; |
|
242 struct timeval tv1, tv2; |
|
243 |
|
244 if (m_Config->device == "") |
|
245 { |
|
246 // use DirectIO |
|
247 if (m_pport->Open(m_Config->port) != 0) |
|
248 return -1; |
|
249 uSleep(10); |
|
250 } |
|
251 else |
|
252 { |
|
253 // use ppdev |
|
254 if (m_pport->Open(m_Config->device.c_str()) != 0) |
|
255 return -1; |
|
256 } |
|
257 |
|
258 if (nSleepInit() != 0) |
|
259 { |
|
260 syslog(LOG_ERR, "%s: INFO: cannot change wait parameters Err: %s (cDriver::Init)\n", m_Config->name.c_str(), strerror(errno)); |
|
261 m_bSleepIsInit = false; |
|
262 } |
|
263 else |
|
264 { |
|
265 m_bSleepIsInit = true; |
|
266 } |
|
267 |
|
268 // benchmark port access |
|
269 m_pport->Claim(); |
|
270 syslog(LOG_DEBUG, "%s: benchmark started.\n", m_Config->name.c_str()); |
|
271 gettimeofday(&tv1, 0); |
|
272 int nBenchFactor = 100000; |
|
273 for (x = 0; x < nBenchFactor; x++) |
|
274 { |
|
275 m_pport->WriteData(x % 0x100); |
|
276 } |
|
277 gettimeofday(&tv2, 0); |
|
278 nSleepDeInit(); |
|
279 //m_nTimingAdjustCmd = ((tv2.tv_sec - tv1.tv_sec) * 10000 + (tv2.tv_usec - tv1.tv_usec)) / 1000; |
|
280 m_nTimingAdjustCmd = long(double((tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec)) / double(nBenchFactor)); |
|
281 syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", m_Config->name.c_str(), m_nTimingAdjustCmd); |
|
282 m_pport->Release(); |
|
283 |
|
284 |
|
285 // initialize display |
|
286 N800Cmd(Init800A); |
|
287 |
|
288 int n; |
|
289 for (n=0; n < 15; n++) |
|
290 { |
|
291 N800Cmd(0x62); |
|
292 nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
293 N800Cmd(n); |
|
294 nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
295 N800Data(0xff); |
|
296 nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
297 } |
|
298 nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
299 |
|
300 |
|
301 N800Cmd(LAYERSOFF | LAYER0ON); // layer 0 of the graphic RAM on |
|
302 N800Cmd(ORON); // OR the layers |
|
303 N800Cmd(HSHIFT); // set horizontal shift |
|
304 N800Cmd(0x00); // no shift |
|
305 N800Cmd(VSHIFT); // Vertical shift =0 |
|
306 N800Cmd(AUTOINCOFF); // auto increment off |
|
307 N800Cmd(SETX); // set x coord |
|
308 N800Cmd(0x40); // to 0 |
|
309 N800Cmd(SETY); // set y coord |
|
310 N800Cmd(0); // to 0 |
|
311 |
|
312 m_pport->Release(); |
|
313 |
|
314 *m_oldConfig = *m_Config; |
|
315 |
|
316 // Set Display SetBrightness |
|
317 SetBrightness(m_Config->brightness); |
|
318 // clear display |
|
319 ClearVFDMem(); |
|
320 Refresh(true); |
|
321 |
|
322 syslog(LOG_INFO, "%s: initialization done.\n", m_Config->name.c_str()); |
|
323 return 0; |
|
324 } |
|
325 |
|
326 void cDriverNoritake800::Refresh(bool refreshAll) |
|
327 { |
|
328 // |
|
329 // for VFD displays, we can safely ignore refreshAll, as they are "sticky" |
|
330 // |
|
331 int xb, yb; |
|
332 |
|
333 if (CheckSetup() > 0) |
|
334 refreshAll = true; // we don't use it |
|
335 |
|
336 if (!m_pVFDMem || !m_pDrawMem) |
|
337 return; |
|
338 |
|
339 // // just refresh if the time needed between refreshes is up |
|
340 // m_nRefreshCounter = (m_nRefreshCounter + 1) % m_Config->refreshDisplay; |
|
341 // if(!m_nRefreshCounter) |
|
342 // { |
|
343 m_pport->Claim(); |
|
344 for (xb = 0; xb < width; ++xb) |
|
345 { |
|
346 for (yb = 0; yb < m_iSizeYb; ++yb) |
|
347 { |
|
348 if (m_pVFDMem[xb][yb] != m_pDrawMem[xb][yb]) |
|
349 { |
|
350 m_pVFDMem[xb][yb] = m_pDrawMem[xb][yb]; |
|
351 // reset RefreshCounter |
|
352 m_nRefreshCounter = 0; |
|
353 // actually write to display |
|
354 N800WriteByte( |
|
355 (m_pVFDMem[xb][yb]) ^ ((m_Config->invert != 0) ? 0xff : 0x00), |
|
356 xb, |
|
357 yb, |
|
358 0); |
|
359 } |
|
360 } |
|
361 } |
|
362 m_pport->Release(); |
|
363 // } |
|
364 } |
|
365 |
|
366 void cDriverNoritake800::N800Cmd(unsigned char data) |
|
367 { |
|
368 if (m_bSleepIsInit) |
|
369 nSleepInit(); |
|
370 |
|
371 // set direction to "port_output" & C/D to C |
|
372 m_pport->WriteControl(m_pWiringMaskCache[0x00]); |
|
373 // write to data port |
|
374 m_pport->WriteData(data); |
|
375 //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
376 // set /WR on the control port |
|
377 m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_WR]); |
|
378 //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
379 // reset /WR on the control port |
|
380 m_pport->WriteControl(m_pWiringMaskCache[0x00]); |
|
381 //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
382 // set direction to "port_input" |
|
383 m_pport->WriteControl(LPT_CTL_HI_DIR | m_pWiringMaskCache[0x00]); |
|
384 } |
|
385 |
|
386 void cDriverNoritake800::N800Data(unsigned char data) |
|
387 { |
|
388 if (m_bSleepIsInit) |
|
389 nSleepInit(); |
|
390 |
|
391 // set direction to "port_output" & C/D to C |
|
392 m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD]); |
|
393 // write to data port |
|
394 m_pport->WriteData(data); |
|
395 //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
396 // set /WR on the control port |
|
397 m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD | VFDSGN_WR]); |
|
398 //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
399 // reset /WR on the control port |
|
400 m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD]); |
|
401 //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd); |
|
402 // set direction to "port_input" |
|
403 m_pport->WriteControl(LPT_CTL_HI_DIR | m_pWiringMaskCache[0x00]); |
|
404 } |
|
405 |
|
406 void cDriverNoritake800::SetPixel(int x, int y) |
|
407 { |
|
408 unsigned char c; |
|
409 |
|
410 if (!m_pDrawMem) |
|
411 return; |
|
412 |
|
413 if (x >= width || x < 0) |
|
414 return; |
|
415 if (y >= height || y < 0) |
|
416 return; |
|
417 |
|
418 if (m_Config->upsideDown) |
|
419 { |
|
420 x = width - 1 - x; |
|
421 y = height - 1 - y; |
|
422 } |
|
423 |
|
424 c = 0x80 >> (y % 8); |
|
425 |
|
426 m_pDrawMem[x][y/8] |= c; |
|
427 } |
|
428 |
|
429 void cDriverNoritake800::Set8Pixels(int x, int y, unsigned char data) |
|
430 { |
|
431 int n; |
|
432 |
|
433 // x - pos is'nt mayby align to 8 |
|
434 x &= 0xFFF8; |
|
435 |
|
436 for (n = 0; n < 8; ++n) |
|
437 { |
|
438 if (data & (0x80 >> n)) // if bit is set |
|
439 SetPixel(x + n, y); |
|
440 } |
|
441 } |
|
442 |
|
443 void cDriverNoritake800::SetBrightness(unsigned int percent) |
|
444 { |
|
445 // display can do 16 brightness levels, |
|
446 // 0 = light |
|
447 // 15 = dark |
|
448 |
|
449 // convert from "light percentage" into darkness values from 0 to 15 |
|
450 if (percent > 100) |
|
451 { |
|
452 percent = 100; |
|
453 } |
|
454 unsigned int darkness = 16 - (unsigned int)((double)percent * 16.0 / 100.0); |
|
455 |
|
456 m_pport->Claim(); |
|
457 N800Cmd(0x40 + (darkness & 0xf)); |
|
458 m_pport->Release(); |
|
459 } |
|
460 |
|
461 unsigned char cDriverNoritake800::N800LptWiringMask(unsigned char ctrl_bits) |
|
462 { |
|
463 unsigned char newstatus = 0x0; |
|
464 |
|
465 if (m_nWiring == WIRING_LIQUIDMP3) |
|
466 { |
|
467 if (ctrl_bits & VFDSGN_CSS) |
|
468 newstatus |= LPT_CTL_LO_STROBE; |
|
469 else |
|
470 newstatus &= ~LPT_CTL_LO_STROBE; |
|
471 |
|
472 if (ctrl_bits & VFDSGN_RD) |
|
473 newstatus |= LPT_CTL_LO_LFEED; |
|
474 else |
|
475 newstatus &= ~LPT_CTL_LO_LFEED; |
|
476 |
|
477 if (ctrl_bits & VFDSGN_WR) |
|
478 newstatus |= LPT_CTL_LO_INIT; |
|
479 else |
|
480 newstatus &= ~LPT_CTL_LO_INIT; |
|
481 |
|
482 if (ctrl_bits & VFDSGN_CD) |
|
483 newstatus |= LPT_CTL_LO_SELECT; |
|
484 else |
|
485 newstatus &= ~LPT_CTL_LO_SELECT; |
|
486 |
|
487 // control commands are XOR-ed with 0x5 |
|
488 // to account for active lows and highs |
|
489 newstatus ^= 0x5; |
|
490 } |
|
491 else if (m_nWiring == WIRING_MZ) |
|
492 { |
|
493 if (ctrl_bits & VFDSGN_CSS) |
|
494 newstatus |= LPT_CTL_LO_INIT; |
|
495 else |
|
496 newstatus &= ~LPT_CTL_LO_INIT; |
|
497 |
|
498 if (ctrl_bits & VFDSGN_RD) |
|
499 newstatus |= LPT_CTL_LO_LFEED; |
|
500 else |
|
501 newstatus &= ~LPT_CTL_LO_LFEED; |
|
502 |
|
503 if (ctrl_bits & VFDSGN_WR) |
|
504 newstatus |= LPT_CTL_LO_STROBE; |
|
505 else |
|
506 newstatus &= ~LPT_CTL_LO_STROBE; |
|
507 |
|
508 if (ctrl_bits & VFDSGN_CD) |
|
509 newstatus |= LPT_CTL_LO_SELECT; |
|
510 else |
|
511 newstatus &= ~LPT_CTL_LO_SELECT; |
|
512 } |
|
513 return newstatus; |
|
514 } |
|
515 |
|
516 void cDriverNoritake800::N800WriteByte(unsigned char data, int nCol, int nRow, int layer) |
|
517 { |
|
518 /* set cursor to desired address */ |
|
519 N800Cmd(SETX); /* set upper cursor address */ |
|
520 N800Cmd(nCol); |
|
521 |
|
522 if (layer==0) |
|
523 { |
|
524 N800Cmd(SETY); /* set lower cursor address */ |
|
525 N800Cmd(nRow); /*layer0 */ |
|
526 } |
|
527 else if (layer==1) |
|
528 { |
|
529 N800Cmd(SETY); /* set lower cursor address */ |
|
530 N800Cmd(nRow+8); /* layer 1 */ |
|
531 } |
|
532 |
|
533 N800Data(ReverseBits(data)); |
|
534 } |
|
535 |
|
536 } // end of namespace |
|
537 |