|
1 /* |
|
2 * GraphLCD driver library |
|
3 * |
|
4 * hd61830.c - HD61830 driver class |
|
5 * |
|
6 * This file is released under the GNU General Public License. Refer |
|
7 * to the COPYING file distributed with this package. |
|
8 * |
|
9 * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de> |
|
10 */ |
|
11 |
|
12 #include <syslog.h> |
|
13 #include <sys/time.h> |
|
14 |
|
15 #include "common.h" |
|
16 #include "config.h" |
|
17 #include "hd61830.h" |
|
18 #include "port.h" |
|
19 |
|
20 |
|
21 namespace GLCD |
|
22 { |
|
23 |
|
24 // commands |
|
25 #define MCNT 0x00 |
|
26 #define CPIT 0x01 |
|
27 #define NOCH 0x02 |
|
28 #define NOTD 0x03 |
|
29 #define CPOS 0x04 |
|
30 |
|
31 #define DSAL 0x08 |
|
32 #define DSAH 0x09 |
|
33 #define CACL 0x0A |
|
34 #define CACH 0x0B |
|
35 |
|
36 #define WDDI 0x0C |
|
37 #define RDDI 0x0D |
|
38 |
|
39 #define CBIT 0x0E |
|
40 #define SBIT 0x0F |
|
41 |
|
42 // control bits for DirectIO |
|
43 #define EN 0x01 |
|
44 #define ENHI 0x00 |
|
45 #define ENLO 0x01 |
|
46 |
|
47 #define RW 0x02 |
|
48 #define RWHI 0x00 |
|
49 #define RWLO 0x02 |
|
50 |
|
51 #define RS 0x04 |
|
52 #define RSHI 0x04 |
|
53 #define RSLO 0x00 |
|
54 |
|
55 |
|
56 cDriverHD61830::cDriverHD61830(cDriverConfig * config) |
|
57 : config(config) |
|
58 { |
|
59 oldConfig = new cDriverConfig(*config); |
|
60 |
|
61 port = new cParallelPort(); |
|
62 |
|
63 useSleepInit = false; |
|
64 |
|
65 refreshCounter = 0; |
|
66 timeForPortCmdInNs = 0; |
|
67 } |
|
68 |
|
69 cDriverHD61830::~cDriverHD61830() |
|
70 { |
|
71 delete port; |
|
72 delete oldConfig; |
|
73 } |
|
74 |
|
75 int cDriverHD61830::Init() |
|
76 { |
|
77 int i; |
|
78 int x; |
|
79 struct timeval tv1, tv2; |
|
80 |
|
81 width = config->width; |
|
82 if (width <= 0) |
|
83 width = 240; |
|
84 height = config->height; |
|
85 if (height <= 0) |
|
86 height = 128; |
|
87 |
|
88 for (unsigned int i = 0; i < config->options.size(); i++) |
|
89 { |
|
90 if (config->options[i].name == "") |
|
91 { |
|
92 } |
|
93 } |
|
94 |
|
95 // setup lcd array (wanted state) |
|
96 newLCD = new unsigned char *[(width + 7) / 8]; |
|
97 if (newLCD) |
|
98 { |
|
99 for (x = 0; x < (width + 7) / 8; x++) |
|
100 { |
|
101 newLCD[x] = new unsigned char[height]; |
|
102 memset(newLCD[x], 0, height); |
|
103 } |
|
104 } |
|
105 // setup lcd array (current state) |
|
106 oldLCD = new unsigned char*[(width + 7) / 8]; |
|
107 if (oldLCD) |
|
108 { |
|
109 for (x = 0; x < (width + 7) / 8; x++) |
|
110 { |
|
111 oldLCD[x] = new unsigned char[height]; |
|
112 memset(oldLCD[x], 0, height); |
|
113 } |
|
114 } |
|
115 |
|
116 if (config->device == "") |
|
117 { |
|
118 // use DirectIO |
|
119 if (port->Open(config->port) != 0) |
|
120 return -1; |
|
121 uSleep(10); |
|
122 } |
|
123 else |
|
124 { |
|
125 // use ppdev |
|
126 if (port->Open(config->device.c_str()) != 0) |
|
127 return -1; |
|
128 } |
|
129 |
|
130 if (nSleepInit() != 0) |
|
131 { |
|
132 syslog(LOG_DEBUG, "%s: INFO: cannot change wait parameters (cDriver::Init)\n", config->name.c_str()); |
|
133 useSleepInit = false; |
|
134 } |
|
135 else |
|
136 { |
|
137 useSleepInit = true; |
|
138 } |
|
139 |
|
140 syslog(LOG_DEBUG, "%s: benchmark started.\n", config->name.c_str()); |
|
141 gettimeofday(&tv1, 0); |
|
142 for (i = 0; i < 1000; i++) |
|
143 { |
|
144 port->WriteData(1 % 0x100); |
|
145 } |
|
146 gettimeofday(&tv2, 0); |
|
147 if (useSleepInit) |
|
148 nSleepDeInit(); |
|
149 timeForPortCmdInNs = (tv2.tv_sec-tv1.tv_sec) * 1000000 + (tv2.tv_usec-tv1.tv_usec); |
|
150 syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", config->name.c_str(), timeForPortCmdInNs); |
|
151 |
|
152 // initialize graphic mode |
|
153 InitGraphic(); |
|
154 |
|
155 port->Release(); |
|
156 |
|
157 *oldConfig = *config; |
|
158 |
|
159 // clear display |
|
160 Clear(); |
|
161 |
|
162 syslog(LOG_INFO, "%s: HD61830 initialized.\n", config->name.c_str()); |
|
163 return 0; |
|
164 } |
|
165 |
|
166 int cDriverHD61830::DeInit() |
|
167 { |
|
168 int x; |
|
169 |
|
170 // free lcd array (wanted state) |
|
171 if (newLCD) |
|
172 { |
|
173 for (x = 0; x < (width + 7) / 8; x++) |
|
174 { |
|
175 delete[] newLCD[x]; |
|
176 } |
|
177 delete[] newLCD; |
|
178 } |
|
179 // free lcd array (current state) |
|
180 if (oldLCD) |
|
181 { |
|
182 for (x = 0; x < (width + 7) / 8; x++) |
|
183 { |
|
184 delete[] oldLCD[x]; |
|
185 } |
|
186 delete[] oldLCD; |
|
187 } |
|
188 if (port->Close() != 0) |
|
189 return -1; |
|
190 return 0; |
|
191 } |
|
192 |
|
193 int cDriverHD61830::CheckSetup() |
|
194 { |
|
195 if (config->device != oldConfig->device || |
|
196 config->port != oldConfig->port || |
|
197 config->width != oldConfig->width || |
|
198 config->height != oldConfig->height) |
|
199 { |
|
200 DeInit(); |
|
201 Init(); |
|
202 return 0; |
|
203 } |
|
204 |
|
205 if (config->upsideDown != oldConfig->upsideDown || |
|
206 config->invert != oldConfig->invert) |
|
207 { |
|
208 oldConfig->upsideDown = config->upsideDown; |
|
209 oldConfig->invert = config->invert; |
|
210 return 1; |
|
211 } |
|
212 return 0; |
|
213 } |
|
214 |
|
215 int cDriverHD61830::InitGraphic() |
|
216 { |
|
217 Write(MCNT, 0x32); // set Mode Control Register |
|
218 // DISP ON, MASTER ON, BLINK OFF, CURSOR OFF, GRAPHIC-Mode, int.Clock |
|
219 Write(CPIT, 0x07); // set Character Pitch Register |
|
220 // 8 pixels per byte |
|
221 Write(NOCH, std::max(1, (width + 7) / 8 - 1)); // set Number-Of-Characters Register |
|
222 // (width - 1) / 8 bytes per line horizontally |
|
223 Write(NOTD, std::max(1, height - 1)); // set Number-Of-Time-Divisions Register |
|
224 // height - 1 |
|
225 Write(CPOS, 0x00); // set Cursor Position Register |
|
226 // optional, because we havn't enabled a cursor |
|
227 Write(DSAL, 0x00); // set Display Start Address Register (Low Order Byte) |
|
228 Write(DSAH, 0x00); // set Display Start Address Register (High Order Byte) |
|
229 Write(CACL, 0x00); // set Cursor Address Counter Register (Low Order Byte) |
|
230 Write(CACH, 0x00); // set Cursor Address Counter Register (High Order Byte) |
|
231 |
|
232 return 0; |
|
233 } |
|
234 |
|
235 void cDriverHD61830::Write(unsigned char cmd, unsigned char data) |
|
236 { |
|
237 if (useSleepInit) |
|
238 nSleepInit(); |
|
239 |
|
240 // set RS high (instruction), RW low (write) and E low |
|
241 port->WriteControl(RSHI | RWLO | ENLO); |
|
242 nSleep(140 - timeForPortCmdInNs + 100 * config->adjustTiming); |
|
243 |
|
244 // Output the actual command |
|
245 port->WriteData(cmd); |
|
246 |
|
247 // set E high |
|
248 port->WriteControl(RSHI | RWLO | ENHI); |
|
249 nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming); |
|
250 |
|
251 // set E low |
|
252 port->WriteControl(RSHI | RWLO | ENLO); |
|
253 nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming); |
|
254 |
|
255 |
|
256 // set RS low (data), RW low (write) and E low |
|
257 port->WriteControl(RSLO | RWLO | ENLO); |
|
258 nSleep(140 - timeForPortCmdInNs + 100 * config->adjustTiming); |
|
259 |
|
260 // Output the actual data |
|
261 port->WriteData(data); |
|
262 |
|
263 // set E high |
|
264 port->WriteControl(RSLO | RWLO | ENHI); |
|
265 nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming); |
|
266 |
|
267 // set E low |
|
268 port->WriteControl(RSLO | RWLO | ENLO); |
|
269 nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming); |
|
270 |
|
271 switch (cmd) |
|
272 { |
|
273 case MCNT: |
|
274 case CPIT: |
|
275 case NOCH: |
|
276 case NOTD: |
|
277 case CPOS: |
|
278 case DSAL: |
|
279 case DSAH: |
|
280 case CACL: |
|
281 case CACH: |
|
282 nSleep(4000 - std::max(450l, timeForPortCmdInNs) + 100 * config->adjustTiming); |
|
283 break; |
|
284 case WDDI: |
|
285 case RDDI: |
|
286 nSleep(6000 - std::max(450l, timeForPortCmdInNs) + 100 * config->adjustTiming); |
|
287 break; |
|
288 case CBIT: |
|
289 case SBIT: |
|
290 nSleep(36000 - std::max(450l, timeForPortCmdInNs) + 100 * config->adjustTiming); |
|
291 break; |
|
292 } |
|
293 if (useSleepInit) |
|
294 nSleepDeInit(); |
|
295 } |
|
296 |
|
297 void cDriverHD61830::Clear() |
|
298 { |
|
299 for (int x = 0; x < (width + 7) / 8; x++) |
|
300 memset(newLCD[x], 0, height); |
|
301 } |
|
302 |
|
303 void cDriverHD61830::Set8Pixels(int x, int y, unsigned char data) |
|
304 { |
|
305 if (x >= width || y >= height) |
|
306 return; |
|
307 |
|
308 if (!config->upsideDown) |
|
309 { |
|
310 // normal orientation |
|
311 newLCD[x / 8][y] = newLCD[x / 8][y] | ReverseBits(data); |
|
312 } |
|
313 else |
|
314 { |
|
315 // upside down orientation |
|
316 x = width - 1 - x; |
|
317 y = height - 1 - y; |
|
318 newLCD[x / 8][y] = newLCD[x / 8][y] | data; |
|
319 } |
|
320 } |
|
321 |
|
322 void cDriverHD61830::Refresh(bool refreshAll) |
|
323 { |
|
324 int x; |
|
325 int y; |
|
326 int pos = 0; |
|
327 |
|
328 if (CheckSetup() > 0) |
|
329 refreshAll = true; |
|
330 |
|
331 if (config->refreshDisplay > 0) |
|
332 { |
|
333 refreshCounter = (refreshCounter + 1) % config->refreshDisplay; |
|
334 if (!refreshAll && !refreshCounter) |
|
335 refreshAll = true; |
|
336 } |
|
337 |
|
338 port->Claim(); |
|
339 |
|
340 if (refreshAll) |
|
341 { |
|
342 // draw all |
|
343 |
|
344 for (y = 0; y < height; y++) |
|
345 { |
|
346 for (x = 0; x < (width + 7) / 8; x++) |
|
347 { |
|
348 // (re-setting the cursor position |
|
349 // might be removed, when the graphic glitches are solved) |
|
350 Write(CACL, (pos % 0x100)); |
|
351 Write(CACH, (pos / 0x100)); |
|
352 Write(WDDI, (newLCD[x][y]) ^ (config->invert ? 0xff : 0x00)); |
|
353 oldLCD[x][y] = newLCD[x][y]; |
|
354 pos++; |
|
355 } |
|
356 } |
|
357 // and reset RefreshCounter |
|
358 refreshCounter = 0; |
|
359 } |
|
360 else |
|
361 { |
|
362 // draw only the changed bytes |
|
363 |
|
364 bool cs = false; |
|
365 for (y = 0; y < height; y++) |
|
366 { |
|
367 for (x = 0; x < (width + 7) / 8; x++) |
|
368 { |
|
369 if (newLCD[x][y] != oldLCD[x][y]) |
|
370 { |
|
371 if (!cs) |
|
372 { |
|
373 Write(CACL, (pos % 0x100)); |
|
374 Write(CACH, (pos / 0x100)); |
|
375 cs = true; |
|
376 } |
|
377 Write(WDDI, (newLCD[x][y]) ^ (config->invert ? 0xff : 0x00)); |
|
378 oldLCD[x][y] = newLCD[x][y]; |
|
379 } |
|
380 else |
|
381 { |
|
382 cs = false; |
|
383 } |
|
384 pos++; |
|
385 } |
|
386 } |
|
387 } |
|
388 port->Release(); |
|
389 } |
|
390 |
|
391 } // end of namespace |