6 #include "core/ssp/ssp.h"
8 #include "basic/basic.h"
9 #include "basic/config.h"
10 #include "usb/usbmsc.h"
13 #define DISPLAY_N1200 0
14 #define DISPLAY_N1600 1
16 #define MODE 8 /* 8 or 16 */
19 #define putpix(x) _helper_pixel8(x)
20 #define px_INIT_MODE 2
21 #define px_PACK(r,g,b) COLORPACK_RGB332(r,g,b)
22 #define px_type uint8_t
25 #define putpix(x) _helper_pixel12(x)
26 #define px_INIT_MODE 3
27 #define px_PACK(r,g,b) COLORPACK_RGB444(r,g,b)
28 #define px_type uint16_t
30 #define putpix(x) _helper_pixel16(x)
31 #define px_INIT_MODE 5
32 #define px_PACK(r,g,b) COLORPACK_RGB565(r,g,b)
33 #define px_type uint16_t
37 /**************************************************************************/
38 /* Utility routines to manage nokia display */
39 /**************************************************************************/
41 uint8_t lcdBuffer
[RESX
*RESY_B
];
42 uint32_t intstatus
; // Caches USB interrupt state
43 // (need to disable MSC while displaying)
49 static void lcd_select() {
52 intstatus
=USB_DEVINTEN
;
56 /* the LCD requires 9-Bit frames */
57 uint32_t configReg
= ( SSP_SSP0CR0_DSS_9BIT
// Data size = 9-bit
58 | SSP_SSP0CR0_FRF_SPI
// Frame format = SPI
59 | SSP_SSP0CR0_SCR_8
); // Serial clock rate = 8
60 SSP_SSP0CR0
= configReg
;
61 gpioSetValue(RB_LCD_CS
, 0);
64 static void lcd_deselect() {
65 gpioSetValue(RB_LCD_CS
, 1);
66 /* reset the bus to 8-Bit frames that everyone else uses */
67 uint32_t configReg
= ( SSP_SSP0CR0_DSS_8BIT
// Data size = 8-bit
68 | SSP_SSP0CR0_FRF_SPI
// Frame format = SPI
69 | SSP_SSP0CR0_SCR_8
); // Serial clock rate = 8
70 SSP_SSP0CR0
= configReg
;
73 USB_DEVINTEN
=intstatus
;
78 static void lcdWrite(uint8_t cd
, uint8_t data
) {
84 while ((SSP_SSP0SR
& (SSP_SSP0SR_TNF_NOTFULL
| SSP_SSP0SR_BSY_BUSY
)) != SSP_SSP0SR_TNF_NOTFULL
);
86 while ((SSP_SSP0SR
& (SSP_SSP0SR_BSY_BUSY
|SSP_SSP0SR_RNE_NOTEMPTY
)) != SSP_SSP0SR_RNE_NOTEMPTY
);
96 uint8_t lcdRead(uint8_t data
)
98 uint32_t op211cache
=IOCON_PIO2_11
;
99 uint32_t op09cache
=IOCON_PIO0_9
;
100 uint32_t dircache
=GPIO_GPIO2DIR
;
101 IOCON_PIO2_11
=IOCON_PIO2_11_FUNC_GPIO
|IOCON_PIO2_11_MODE_PULLUP
;
102 IOCON_PIO0_9
=IOCON_PIO0_9_FUNC_GPIO
|IOCON_PIO0_9_MODE_PULLUP
;
108 gpioSetValue(SCK
, 0);
112 gpioSetValue(SDA
, 0);
113 gpioSetValue(SCK
, 1);
117 gpioSetValue(SCK
, 0);
120 gpioSetValue(SDA
, 1);
122 gpioSetValue(SDA
, 0);
124 gpioSetValue(SCK
, 1);
131 gpioSetValue(SCK
, 0);
134 ret
|= gpioGetValue(SDA
);
135 gpioSetValue(SCK
, 1);
138 gpioSetValue(SCK
, 0);
142 IOCON_PIO2_11
=op211cache
;
143 IOCON_PIO0_9
=op09cache
;
144 GPIO_GPIO2DIR
=dircache
;
153 sspInit(0, sspClockPolarity_Low
, sspClockPhase_RisingEdge
);
155 gpioSetValue(RB_LCD_CS
, 1);
156 gpioSetValue(RB_LCD_RST
, 1);
158 gpioSetDir(RB_LCD_CS
, gpioDirection_Output
);
159 gpioSetDir(RB_LCD_RST
, gpioDirection_Output
);
162 gpioSetValue(RB_LCD_RST
, 0);
164 gpioSetValue(RB_LCD_RST
, 1);
167 id
=lcdRead(220); // ID3
170 displayType
=DISPLAY_N1600
;
172 displayType
=DISPLAY_N1200
;
174 /* Small Nokia 1200 LCD docs:
178 * mirror-x 0xA0 / 0xA1
179 * mirror-y 0xc7 / 0xc8
181 * 0x20+x contrast (0=black - 0x2e)
182 * 0x40+x offset in rows from top (-0x7f)
183 * 0x80+x contrast? (0=black -0x9f?)
184 * 0xd0+x black lines from top? (-0xdf?)
189 if(displayType
==DISPLAY_N1200
){
192 * AF: Display on/off: DON = 1
194 * A4: all on/normal: DAL = 0
195 * 2F: charge pump on/off: PC = 1
196 * B0: set y address: Y[0-3] = 0
197 * 10: set x address (upper bits): X[6-4] = 0
199 static uint8_t initseq
[]= { 0xE2,0xAF, // Display ON
201 0xA4, 0x2F, 0xB0, 0x10};
203 while(i
<sizeof(initseq
)){
204 lcdWrite(TYPE_CMD
,initseq
[i
++]);
205 delayms(5); // actually only needed after the first
207 }else{ /* displayType==DISPLAY_N1600 */
208 static uint8_t initseq_d
[] = {
209 /* The controller is a PCF8833 -
210 documentation can be found online.
214 * CMD 03: Booster voltage ON
215 * CMD 13: Normal display mode
216 * CMD 3A: interface pixel format
217 * DAT 02 | 02: 8 bit/pixel (3:3:2)
218 * | 03: 12 bit/pixel (4:4:4)
219 * | 05: 16 bit/pixel (5:6:5)
220 * CMD 2A: column address set
223 * CMD 2B: page address set
235 uint16_t initseq_c
= ~ ( /* comands: 1, data: 0 */
241 (1<<6) | (0<< 7) | (0<< 8) |
242 (1<<9) | (0<<10) | (0<<11) |
246 lcdWrite(0, 0x01); /* most color displays need the pause */
249 while(i
<sizeof(initseq_d
)){
250 lcdWrite(initseq_c
&1, initseq_d
[i
++]);
251 initseq_c
= initseq_c
>> 1;
257 void lcdFill(char f
){
258 memset(lcdBuffer
,f
,RESX
*RESY_B
);
261 for(x
=0;x
<RESX
*RESY_B
;x
++) {
267 void lcdSetPixel(char x
, char y
, bool f
){
268 if (x
<0 || x
> RESX
|| y
<0 || y
> RESY
)
270 char y_byte
= (RESY
-(y
+1)) / 8;
271 char y_off
= (RESY
-(y
+1)) % 8;
272 char byte
= lcdBuffer
[y_byte
*RESX
+(RESX
-(x
+1))];
274 byte
|= (1 << y_off
);
276 byte
&= ~(1 << y_off
);
278 lcdBuffer
[y_byte
*RESX
+(RESX
-(x
+1))] = byte
;
281 bool lcdGetPixel(char x
, char y
){
282 char y_byte
= (RESY
-(y
+1)) / 8;
283 char y_off
= (RESY
-(y
+1)) % 8;
284 char byte
= lcdBuffer
[y_byte
*RESX
+(RESX
-(x
+1))];
285 return byte
& (1 << y_off
);
288 // Color display helper functions
289 static inline void _helper_pixel8(uint8_t color1
){
290 lcdWrite(TYPE_DATA
, color1
);
293 static void _helper_pixel12(uint16_t color
){
297 lcdWrite(TYPE_DATA
,(rest
<<4) | (color
>>8));
298 lcdWrite(TYPE_DATA
,color
&0xff);
300 lcdWrite(TYPE_DATA
,(color
>>4)&0xff);
306 static void _helper_pixel16(uint16_t color
){
307 lcdWrite(TYPE_DATA
,color
>>8);
308 lcdWrite(TYPE_DATA
,color
&0xFF);
311 #define COLORPACK_RGB565(r,g,b) (((r&0xF8) << 8) | ((g&0xFC)<<3) | ((b&0xF8) >> 3))
312 #define COLORPACK_RGB444(r,g,b) ( ((r&0xF0)<<4) | (g&0xF0) | ((b&0xF0)>>4) )
313 #define COLORPACK_RGB332(r,g,b) ( (((r>>5)&0x7)<<5) | (((g>>5)&0x7)<<2) | ((b>>6)&0x3) )
315 static const px_type COLOR_FG
= px_PACK(0x00, 0x00, 0x00);
316 static const px_type COLOR_BG
= px_PACK(0xff, 0xff, 0xff);
318 void lcdDisplay(void) {
322 if(displayType
==DISPLAY_N1200
){
323 lcdWrite(TYPE_CMD
,0xB0);
324 lcdWrite(TYPE_CMD
,0x10);
325 lcdWrite(TYPE_CMD
,0x00);
327 for(page
=0; page
<RESY_B
;page
++) {
328 for(i
=0; i
<RESX
; i
++) {
329 if (GLOBAL(lcdmirror
))
330 byte
=lcdBuffer
[page
*RESX
+RESX
-1-(i
)];
332 byte
=lcdBuffer
[page
*RESX
+(i
)];
334 if (GLOBAL(lcdinvert
))
337 lcdWrite(TYPE_DATA
,byte
);
340 } else { /* displayType==DISPLAY_N1600 */
344 lcdWrite(TYPE_CMD
,0x2C);
348 if(GLOBAL(lcdmirror
))
349 px
=lcdGetPixel(RESX
-x
,y
-1);
351 px
=lcdGetPixel(x
-1,y
-1);
353 if((!px
)^(!GLOBAL(lcdinvert
))) {
354 putpix(COLOR_FG
); /* foreground */
356 putpix(COLOR_BG
); /* background */
364 void lcdRefresh() __attribute__ ((weak
, alias ("lcdDisplay")));
366 inline void lcdInvert(void) {
367 GLOBAL(lcdinvert
)=!GLOBAL(lcdinvert
);
370 void lcdSetContrast(int c
) {
372 if(displayType
==DISPLAY_N1200
){
374 lcdWrite(TYPE_CMD
,0x80+c
);
375 }else{ /* displayType==DISPLAY_N1600 */
377 lcdWrite(TYPE_CMD
,0x25);
378 lcdWrite(TYPE_DATA
,4*c
);
384 void lcdSetInvert(int c
) {
386 /* it doesn't harm N1600, save space */
387 // if(displayType==DISPLAY_N1200)
388 lcdWrite(TYPE_CMD
,(c
&1)+0xa6);
393 void __attribute__((__deprecated__
)) lcdToggleFlag(int flag
) {
394 if(flag
==LCD_MIRRORX
)
395 GLOBAL(lcdmirror
)=!GLOBAL(lcdmirror
);
396 if(flag
==LCD_INVERTED
)
397 GLOBAL(lcdinvert
)=!GLOBAL(lcdinvert
);
400 void lcdShiftH(bool right
, bool wrap
) {
402 for (int yb
= 0; yb
<RESY_B
; yb
++) {
404 tmp
= lcdBuffer
[yb
*RESX
];
405 memmove(lcdBuffer
+ yb
*RESX
,lcdBuffer
+ yb
*RESX
+1 ,RESX
-1);
406 lcdBuffer
[yb
*RESX
+(RESX
-1)] = wrap
?tmp
:0;
408 tmp
= lcdBuffer
[yb
*RESX
+(RESX
-1)];
409 memmove(lcdBuffer
+ yb
*RESX
+1,lcdBuffer
+ yb
*RESX
,RESX
-1);
410 lcdBuffer
[yb
*RESX
] = wrap
?tmp
:0;
415 void lcdShiftV8(bool up
, bool wrap
) {
419 memmove(tmp
, lcdBuffer
, RESX
);
422 memmove(lcdBuffer
,lcdBuffer
+RESX
,RESX
*(RESY_B
-1));
423 memmove(lcdBuffer
+RESX
*(RESY_B
-1),tmp
,RESX
);
426 memmove(tmp
, lcdBuffer
+RESX
*(RESY_B
-1), RESX
);
429 memmove(lcdBuffer
+RESX
,lcdBuffer
,RESX
*(RESY_B
-1));
430 memmove(lcdBuffer
,tmp
,RESX
);
434 void lcdShiftV(bool up
, bool wrap
) {
438 memmove(tmp
,lcdBuffer
+((RESY_B
-1)*RESX
),RESX
);
441 for (int x
= 0; x
<RESX
; x
++){
442 for (int y
= RESY_B
-1; y
> 0; y
--){
443 lcdBuffer
[x
+(y
*RESX
)] = (lcdBuffer
[x
+(y
*RESX
)] << 1) |( lcdBuffer
[x
+((y
-1)*RESX
)] >> 7);
445 lcdBuffer
[x
] = ( lcdBuffer
[x
] << 1) | ((tmp
[x
]>>3)&1);
450 memmove(tmp
,lcdBuffer
,RESX
);
453 for (int x
= 0; x
<RESX
; x
++){
454 for (int y
= 0; y
< (RESY_B
-1); y
++){
455 lcdBuffer
[x
+(y
*RESX
)] = (lcdBuffer
[x
+(y
*RESX
)] >> 1) |( lcdBuffer
[x
+((y
+1)*RESX
)] << 7);
457 lcdBuffer
[x
+((RESY_B
-1)*RESX
)] = ( lcdBuffer
[x
+((RESY_B
-1)*RESX
)] >> 1) | ((tmp
[x
]<<3)&8);
462 void lcdShift(int x
, int y
, bool wrap
) {
471 lcdShiftH(dir
, wrap
);
482 lcdShiftV8(dir
, wrap
);
486 lcdShiftV(dir
, wrap
);