|
1 /* |
|
2 * GraphLCD driver library |
|
3 * |
|
4 * gu140x32f.c - 8-bit driver module for Noritake GU140x32-F7806 VFD |
|
5 * displays. The VFD is operating in its 8 bit-mode |
|
6 * connected to a single PC parallel port. |
|
7 * |
|
8 * based on: |
|
9 * HD61830 device |
|
10 * (c) 2001-2003 by Carsten Siebholz <c.siebholz AT t-online.de> |
|
11 * lcdproc 0.4 driver hd44780-ext8bit |
|
12 * (c) 1999, 1995 Benjamin Tse <blt AT Comports com> |
|
13 * |
|
14 * This file is released under the GNU General Public License. Refer |
|
15 * to the COPYING file distributed with this package. |
|
16 * |
|
17 * (c) 2003 Andreas Brachold <vdr04 AT deltab.de> |
|
18 */ |
|
19 |
|
20 #include <errno.h> |
|
21 #include <syslog.h> |
|
22 #include <unistd.h> |
|
23 #include <sys/time.h> |
|
24 |
|
25 #include "common.h" |
|
26 #include "config.h" |
|
27 #include "gu140x32f.h" |
|
28 #include "port.h" |
|
29 |
|
30 |
|
31 namespace GLCD |
|
32 { |
|
33 |
|
34 // Defines for hd44780 Displays |
|
35 #define RS_DAT 0x00 |
|
36 #define RS_CMD 0x01 |
|
37 |
|
38 #define CLEAR 0x01 |
|
39 |
|
40 #define HOMECURSOR 0x02 |
|
41 |
|
42 #define ENTRYMODE 0x04 |
|
43 #define E_MOVERIGHT 0x02 |
|
44 #define E_MOVELEFT 0x00 |
|
45 #define EDGESCROLL 0x01 |
|
46 #define NOSCROLL 0x00 |
|
47 |
|
48 #define ONOFFCTRL 0x08 |
|
49 #define DISPON 0x04 |
|
50 #define DISPOFF 0x00 |
|
51 #define CURSORON 0x02 |
|
52 #define CURSOROFF 0x00 |
|
53 #define CURSORBLINK 0x01 |
|
54 #define CURSORNOBLINK 0x00 |
|
55 |
|
56 #define CURSORSHIFT 0x10 |
|
57 #define SCROLLDISP 0x08 |
|
58 #define MOVECURSOR 0x00 |
|
59 #define MOVERIGHT 0x04 |
|
60 #define MOVELEFT 0x00 |
|
61 |
|
62 #define FUNCSET 0x20 |
|
63 #define IF_8BIT 0x10 |
|
64 #define IF_4BIT 0x00 |
|
65 |
|
66 // Control output lines |
|
67 // Write to baseaddress+2 |
|
68 #define nSTRB 0x01 // pin 1; negative logic |
|
69 #define nLF 0x02 // pin 14 |
|
70 #define nINIT 0x04 // pin 16; the only positive logic output line |
|
71 #define nSEL 0x08 // pin 17 |
|
72 #define ENIRQ 0x10 // Enable IRQ via ACK line (don't enable this withouT |
|
73 // setting up interrupt stuff too) |
|
74 #define ENBI 0x20 // Enable bi-directional port (is nice to play with! |
|
75 // I first didn't know a SPP could do this) |
|
76 |
|
77 #define OUTMASK 0x0B // SEL, LF and STRB are hardware inverted |
|
78 // Use this mask only for the control output lines |
|
79 // XOR with this mask ( ^ OUTMASK ) |
|
80 |
|
81 static const std::string kWiringStandard = "Standard"; |
|
82 static const std::string kWiringWindows = "Windows"; |
|
83 |
|
84 // standard wiring |
|
85 // #define RS nSTRB |
|
86 // #define RW nLF |
|
87 // #define EN1 nINIT |
|
88 // #define BL nSEL |
|
89 |
|
90 // windows wiring |
|
91 // #define RS nINIT |
|
92 // #define RW nLF |
|
93 // #define EN1 nSTRB |
|
94 // #define BL nSEL |
|
95 |
|
96 |
|
97 cDriverGU140X32F::cDriverGU140X32F(cDriverConfig * config) |
|
98 : config(config), |
|
99 m_pDrawMem(0), |
|
100 m_pVFDMem(0) |
|
101 { |
|
102 oldConfig = new cDriverConfig(*config); |
|
103 |
|
104 port = new cParallelPort(); |
|
105 |
|
106 m_nRefreshCounter = 0; |
|
107 } |
|
108 |
|
109 cDriverGU140X32F::~cDriverGU140X32F() |
|
110 { |
|
111 delete port; |
|
112 delete oldConfig; |
|
113 } |
|
114 |
|
115 int cDriverGU140X32F::Init() |
|
116 { |
|
117 int x; |
|
118 struct timeval tv1, tv2; |
|
119 |
|
120 // default values |
|
121 width = config->width; |
|
122 if (width <= 0) |
|
123 width = 140; |
|
124 height = config->height; |
|
125 if (height <= 0) |
|
126 height = 32; |
|
127 m_iSizeYb = ((height + 7) / 8); // 4 |
|
128 m_WiringRS = nSTRB; |
|
129 m_WiringEN1 = nINIT; |
|
130 |
|
131 for (unsigned int i = 0; i < config->options.size(); i++) |
|
132 { |
|
133 if (config->options[i].name == "Wiring") |
|
134 { |
|
135 if (config->options[i].value == kWiringStandard) |
|
136 { |
|
137 m_WiringRS = nSTRB; |
|
138 m_WiringEN1 = nINIT; |
|
139 } |
|
140 else if (config->options[i].value == kWiringWindows) |
|
141 { |
|
142 m_WiringRS = nINIT; |
|
143 m_WiringEN1 = nSTRB; |
|
144 } |
|
145 else |
|
146 syslog(LOG_ERR, "%s error: wiring %s not supported, using default (Standard)!\n", |
|
147 config->name.c_str(), config->options[i].value.c_str()); |
|
148 } |
|
149 } |
|
150 |
|
151 // setup the memory array for the drawing array gu140x32f |
|
152 m_pDrawMem = new unsigned char[width * m_iSizeYb]; |
|
153 Clear(); |
|
154 |
|
155 // setup the memory array for the display array gu140x32f |
|
156 m_pVFDMem = new unsigned char[width * m_iSizeYb]; |
|
157 ClearVFDMem(); |
|
158 |
|
159 if (config->device == "") |
|
160 { |
|
161 // use DirectIO |
|
162 if (port->Open(config->port) != 0) |
|
163 return -1; |
|
164 uSleep(10); |
|
165 } |
|
166 else |
|
167 { |
|
168 // use ppdev |
|
169 if (port->Open(config->device.c_str()) != 0) |
|
170 return -1; |
|
171 } |
|
172 |
|
173 if (nSleepInit() != 0) |
|
174 { |
|
175 syslog(LOG_ERR, "%s: INFO: cannot change wait parameters Err: %s (cDriver::Init)\n", config->name.c_str(), strerror(errno)); |
|
176 m_bSleepIsInit = false; |
|
177 } |
|
178 else |
|
179 { |
|
180 m_bSleepIsInit = true; |
|
181 } |
|
182 |
|
183 syslog(LOG_DEBUG, "%s: benchmark started.\n", config->name.c_str()); |
|
184 gettimeofday(&tv1, 0); |
|
185 for (x = 0; x < 10000; x++) |
|
186 { |
|
187 port->WriteData(x % 0x100); |
|
188 } |
|
189 gettimeofday(&tv2, 0); |
|
190 nSleepDeInit(); |
|
191 m_nTimingAdjustCmd = ((tv2.tv_sec - tv1.tv_sec) * 10000 + (tv2.tv_usec - tv1.tv_usec)) / 1000; |
|
192 syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", config->name.c_str(), m_nTimingAdjustCmd); |
|
193 |
|
194 |
|
195 // setup the lcd in 8 bit mode |
|
196 Write(RS_CMD, FUNCSET | IF_8BIT, 4100); |
|
197 Write(RS_CMD, FUNCSET | IF_8BIT, 100); |
|
198 Write(RS_CMD, FUNCSET | IF_8BIT, 40); |
|
199 |
|
200 Write(RS_CMD, ONOFFCTRL | DISPON | CURSOROFF | CURSORNOBLINK, 40); |
|
201 Write(RS_CMD, CLEAR, 1600); |
|
202 Write(RS_CMD, HOMECURSOR, 1600); |
|
203 |
|
204 port->Release(); |
|
205 |
|
206 *oldConfig = *config; |
|
207 |
|
208 // Set Display SetBrightness |
|
209 SetBrightness(config->brightness); |
|
210 // clear display |
|
211 ClearVFDMem(); |
|
212 Clear(); |
|
213 |
|
214 syslog(LOG_INFO, "%s: gu140x32f initialized.\n", config->name.c_str()); |
|
215 return 0; |
|
216 } |
|
217 |
|
218 int cDriverGU140X32F::DeInit() |
|
219 { |
|
220 if (m_pVFDMem) |
|
221 delete[] m_pVFDMem; |
|
222 if (m_pDrawMem) |
|
223 delete[] m_pDrawMem; |
|
224 |
|
225 if (port->Close() != 0) |
|
226 return -1; |
|
227 return 0; |
|
228 } |
|
229 |
|
230 int cDriverGU140X32F::CheckSetup() |
|
231 { |
|
232 if (config->device != oldConfig->device || |
|
233 config->port != oldConfig->port || |
|
234 config->width != oldConfig->width || |
|
235 config->height != oldConfig->height) |
|
236 { |
|
237 DeInit(); |
|
238 Init(); |
|
239 return 0; |
|
240 } |
|
241 |
|
242 if (config->brightness != oldConfig->brightness) |
|
243 { |
|
244 oldConfig->brightness = config->brightness; |
|
245 SetBrightness(config->brightness); |
|
246 } |
|
247 |
|
248 if (config->upsideDown != oldConfig->upsideDown || |
|
249 config->invert != oldConfig->invert) |
|
250 { |
|
251 oldConfig->upsideDown = config->upsideDown; |
|
252 oldConfig->invert = config->invert; |
|
253 return 1; |
|
254 } |
|
255 return 0; |
|
256 } |
|
257 |
|
258 void cDriverGU140X32F::ClearVFDMem() |
|
259 { |
|
260 for (int n = 0; m_pVFDMem && n < (width * m_iSizeYb); n++) |
|
261 m_pVFDMem[n] = 0x00; |
|
262 } |
|
263 |
|
264 void cDriverGU140X32F::Clear() |
|
265 { |
|
266 for (int n = 0; m_pDrawMem && n < (width * m_iSizeYb); n++) |
|
267 m_pDrawMem[n] = 0x00; |
|
268 } |
|
269 |
|
270 void cDriverGU140X32F::SetBrightness(unsigned int percent) |
|
271 { |
|
272 port->Claim(); |
|
273 |
|
274 unsigned char level; |
|
275 if (percent > 100) |
|
276 percent = 100; |
|
277 level = percent / 25; |
|
278 if (level < 1) |
|
279 level = 1; |
|
280 level = (4 - level) & 0x03; |
|
281 // Set Brightness |
|
282 // 00 - 100% |
|
283 // 01 - 75% |
|
284 // 02 - 50% |
|
285 // 03 - 25% |
|
286 Write(RS_CMD, FUNCSET | IF_8BIT, 40); |
|
287 Write(RS_DAT, level, 40); |
|
288 |
|
289 port->Release(); |
|
290 } |
|
291 |
|
292 void cDriverGU140X32F::Write(unsigned char nFlags, unsigned char bData, unsigned int nMicroSecBusyTime) |
|
293 { |
|
294 if (m_bSleepIsInit) |
|
295 nSleepInit(); |
|
296 |
|
297 unsigned char enableLines = 0, portControl; |
|
298 |
|
299 // Only one controller is supported |
|
300 enableLines = m_WiringEN1; |
|
301 |
|
302 if (nFlags == RS_CMD) |
|
303 portControl = 0; |
|
304 else // if (nFlags == RS_DAT) |
|
305 portControl = m_WiringRS; |
|
306 |
|
307 // portControl |= m_WiringBL; |
|
308 |
|
309 port->WriteControl(portControl ^ OUTMASK); //Reset controlbits |
|
310 port->WriteData(bData); //Set data |
|
311 port->WriteControl((enableLines | portControl) ^ OUTMASK); //Set controlbits |
|
312 |
|
313 // How long hold the data active |
|
314 if (m_bSleepIsInit && (25 + (100 * config->adjustTiming) - m_nTimingAdjustCmd > 0)) |
|
315 { |
|
316 // Wait 50ns |
|
317 nSleep(std::max(25L, 50 + (100 * config->adjustTiming) - m_nTimingAdjustCmd)); |
|
318 } |
|
319 |
|
320 port->WriteControl(portControl ^ OUTMASK); //Reset controlbits |
|
321 |
|
322 nSleep((nMicroSecBusyTime * 1000) + (100 * config->adjustTiming) - m_nTimingAdjustCmd); |
|
323 |
|
324 if (m_bSleepIsInit) |
|
325 nSleepDeInit(); |
|
326 } |
|
327 |
|
328 void cDriverGU140X32F::SetPixel(int x, int y) |
|
329 { |
|
330 unsigned char c; |
|
331 int n; |
|
332 |
|
333 if (!m_pDrawMem) |
|
334 return; |
|
335 |
|
336 if (x >= width || x < 0) |
|
337 return; |
|
338 if (y >= height || y < 0) |
|
339 return; |
|
340 |
|
341 if (config->upsideDown) |
|
342 { |
|
343 x = width - 1 - x; |
|
344 y = height - 1 - y; |
|
345 } |
|
346 |
|
347 n = x + ((y / 8) * width); |
|
348 c = 0x80 >> (y % 8); |
|
349 |
|
350 m_pDrawMem[n] |= c; |
|
351 } |
|
352 |
|
353 void cDriverGU140X32F::Set8Pixels(int x, int y, unsigned char data) |
|
354 { |
|
355 int n; |
|
356 |
|
357 // x - pos is'nt mayby align to 8 |
|
358 x &= 0xFFF8; |
|
359 |
|
360 for (n = 0; n < 8; ++n) |
|
361 { |
|
362 if (data & (0x80 >> n)) // if bit is set |
|
363 SetPixel(x + n, y); |
|
364 } |
|
365 } |
|
366 |
|
367 void cDriverGU140X32F::Refresh(bool refreshAll) |
|
368 { |
|
369 int n, x, yb; |
|
370 |
|
371 if (!m_pVFDMem || !m_pDrawMem) |
|
372 return; |
|
373 |
|
374 bool doRefresh = false; |
|
375 int minX = width; |
|
376 int maxX = 0; |
|
377 int minYb = m_iSizeYb; |
|
378 int maxYb = 0; |
|
379 |
|
380 if (CheckSetup() > 0) |
|
381 refreshAll = true; |
|
382 |
|
383 for (yb = 0; yb < m_iSizeYb; ++yb) |
|
384 for (x = 0; x < width; ++x) |
|
385 { |
|
386 n = x + (yb * width); |
|
387 if (m_pVFDMem[n] != m_pDrawMem[n]) |
|
388 { |
|
389 m_pVFDMem[n] = m_pDrawMem[n]; |
|
390 minX = std::min(minX, x); |
|
391 maxX = std::max(maxX, x); |
|
392 minYb = std::min(minYb, yb); |
|
393 maxYb = std::max(maxYb, yb + 1); |
|
394 doRefresh = true; |
|
395 } |
|
396 } |
|
397 |
|
398 m_nRefreshCounter = (m_nRefreshCounter + 1) % config->refreshDisplay; |
|
399 |
|
400 if (!refreshAll && !m_nRefreshCounter) |
|
401 refreshAll = true; |
|
402 |
|
403 if (refreshAll || doRefresh) |
|
404 { |
|
405 if (refreshAll) |
|
406 { |
|
407 minX = 0; |
|
408 maxX = width; |
|
409 minYb = 0; |
|
410 maxYb = m_iSizeYb; |
|
411 // and reset RefreshCounter |
|
412 m_nRefreshCounter = 0; |
|
413 } |
|
414 |
|
415 minX = std::max(minX, 0); |
|
416 maxX = std::min(maxX, width - 1); |
|
417 minYb = std::max(minYb, 0); |
|
418 maxYb = std::min(maxYb, m_iSizeYb); |
|
419 |
|
420 port->Claim(); |
|
421 // send lcd data to display, controller |
|
422 Write(RS_CMD, 0xF1, 40); |
|
423 Write(RS_DAT, minX, 40); |
|
424 Write(RS_DAT, (minYb * 8) & 0xFFF8, 40); |
|
425 Write(RS_DAT, maxX, 40); |
|
426 Write(RS_DAT, (maxYb * 8), 40); |
|
427 |
|
428 Write(RS_DAT, 'v', 500); |
|
429 |
|
430 for (yb = minYb; yb <= maxYb; ++yb) |
|
431 for (x = minX; x <= maxX; ++x) |
|
432 { |
|
433 n = x + (yb * width); |
|
434 |
|
435 if (n >= (width * m_iSizeYb)) |
|
436 break; |
|
437 Write(RS_DAT, (m_pVFDMem[n]) ^ (config->invert ? 0xff : 0x00), 40); |
|
438 } |
|
439 port->Release(); |
|
440 } |
|
441 } |
|
442 |
|
443 } // end of namespace |