lcd_driver_st7565.c


1
//--------------------------------------------------------------------
2
//
3
// OpenDCC
4
// Throttle MFT 1.0
5
//
6
// Copyright (c) 2009 Wolfgang Kufer
7
//
8
// This source file is subject of the GNU general public license 2,
9
// that is available at the world-wide-web at
10
// http://www.gnu.org/licenses/gpl.txt
11
//
12
// This code is initially based on:
13
// http://www.mikrocontroller.net/topic/75589
14
// it runs as driver for Sami Varjo's gLCD lib functions
15
// available from sourceforge.net
16
//
17
// Here we do the access to a graphic lcd display, no special functions
18
// for MFT contained (so this is a general purpose file)
19
//
20
//--------------------------------------------------------------------
21
//
22
// file:      lcd_driver_st7565.c
23
// author:    Wolfgang Kufer
24
// contact:   kufer@gmx.de
25
// webpage:   http://www.opendcc.de
26
// history:   2009-03-24 V0.01 kw  start
27
//            2009-03-27 V0.02 kw  font proportional added
28
//            2009-04-04 V0.03 kw  cursor control added
29
//            2009-04-06 V0.04 kw  cursor in housekeeping integriert, blinkend
30
//            2009-07-05 V0.05 kw  forced reinit of SPI
31
//            2010-07-12 V0.06 kw  added LCD_pixelPattern
32
//            2011-07-09 V0.07 kw  bugfix in update_display_page von Thomas Finn 
33
//
34
//--------------------------------------------------------------------
35
//
36
// purpose:   control of a spi driven graphic display
37
//            Controller: EA DOGM128, ST7565, 6B1713 
38
//
39
// Interface: downstream: we use hardware SPI, all data transfer is done
40
//                        with SPI_xmit. This must run with no other
41
//                        SPI access in between - so in a real time os
42
//                        you must use a mutex here.
43
//                        Note: cortos is preemptiv - no mutex needed
44
//
45
//            upstream: basic graphic print functions
46
//
47
//--------------------------------------------------------------------
48
// Howto:
49
//   All printing and drawing goes to internal ram.
50
//   These routines keep track of the changed area.
51
//   A separate task is used to update the real display
52
//   from this memory (see housekeeping)
53
//
54
// Reason:
55
//   a) display is write only (when running on SPI)
56
//   b) display content will not flicker during repaint
57
//-----------------------------------------------------------------
58
// Hardware:  we need SPI (Chip select: SPI_ENABLE_LCD)
59
//            we need to control line A0 (SPI_ADR0_LOW;SPI_ADR0_HIGH;)
60
//            
61
62
#include <avr/io.h>
63
#include <avr/interrupt.h>
64
#include <avr/eeprom.h>
65
#include <avr/pgmspace.h>        // put var to program memory
66
#include <util/delay.h>
67
#include <stdio.h>
68
69
#include "config.h"
70
#include "cortos.h"
71
#include "hardware.h"           // CS for SPI
72
#include "timer.h"
73
74
#include "lcd_font5x7.h"
75
#define FONT5x7_START      0x00  // was 0x20
76
#define FONT5x7_END        0x7F 
77
78
#include "lcd_driver_st7565.h"
79
80
81
#if (DISPLAY == LCD_EA128x64)
82
83
//------------------------------------------------------------------------
84
// hardware settings
85
//------------------------------------------------------------------------
86
#define LCD_CONTRAST_DEFAULT    22          // electronic contrast
87
                                            // default, if we don't have a eeprom
88
89
#define EA_DOGM_SINGLE_SUPPLY    1          // 0: init for dual supply
90
                                            // 1: init for single supply
91
92
#define EA_DOGM_TOPVIEW          0          // 0: bottom view (6:00)
93
                                            // 1: top view (12:00)
94
                                            
95
#define EA_DOGM_SOFTRESET        1          // 0: hardware pin
96
                                            // 1: we do software reset
97
98
99
100
//------------------------------------------------------------------------
101
//
102
// Global variables used by driver
103
//
104
//------------------------------------------------------------------------
105
//
106
//      0 1 2  .....................  127
107
//     |---------------------------------|
108
//    0|(0,0)      |                     | 127
109
//  128|           y                     |
110
//     |           |                     |
111
//     |           v                     |
112
//     |---- x --->*                     |
113
//     |                                 |
114
//     |                                 |
115
//  896|                         (127,63)|1023
116
//     |---------------------------------|
117
//
118
// The display is organized in 8 pages - 8 pixel high, 128 pixel wide.
119
// defines see header file
120
// 
121
// RAM data organisation: left to right; top to bottom
122
// (this is more efficient for font copying)
123
//
124
// Pixel(x,y):          index = (y/8)*128 + x
125
//                            = ((y & ~0x07) << 4 + x
126
//                      bit   = y%8
127
//                            = (y & 0x07)
128
// Set pixel (x,y):     video_ram[(y & ~0x07) * (LCD_LINE_LENGTH/LCD_PAGE_HEIGHT) + x] |=  (1 << (y & 0x07));
129
// Clear pixel (x,y):   video_ram[(y & ~0x07) * (LCD_LINE_LENGTH/LCD_PAGE_HEIGHT) + x] &= ~(1 << (y & 0x07));
130
131
#define LCD_PAGE_HEIGHT     8   //8 lines per page
132
#define LCD_LINES          64
133
#define LCD_LINE_LENGTH   128
134
135
static uint8_t video_ram[1024];
136
137
uint8_t LCD_currentX;           // cursor
138
uint8_t LCD_currentY;
139
140
//-----------------------------------------------------------------------------
141
// Mode
142
143
unsigned char display_invers = 0;   // this is either 0 or 0xFF
144
                                    // 00: print font bitmap as is
145
                                    // ff: print inverted 
146
unsigned char display_keep = 0;     // this is either 0 or 0xFF 
147
                                    // 00: replace video content
148
                                    // ff: keep video content             
149
150
//-----------------------------------------------------------------------------
151
// Cursor
152
153
uint8_t cursor_is_on_display;   // cursor currently on display
154
uint8_t CursorActive;           // cursor task could be enabled
155
uint8_t Cxmin, Cxmax;           // Cursor is a line from Cxmin to Cxmax
156
uint8_t Cy, CyPage, CyPattern;  // Cursor page and pattern
157
158
159
//------------------------------------------------------------------------
160
//
161
// raw hardware functions (only used by "disp_..."-functions)
162
//
163
//------------------------------------------------------------------------
164
// register defines
165
#define ST7565R_RESET                           0xE2
166
#define ST7565R_ADC_SELECT                      0xA0
167
#define ST7565R_COMMON_OUTPUT_MODE              0xC0
168
#define ST7565R_DISPLAY_NORMAL                  0xA6
169
#define ST7565R_DISPLAY_INVERS                  0xA7
170
#define ST7565R_LCD_BIAS_SET_1_9                0xA2
171
#define ST7565R_LCD_BIAS_SET_1_7                0xA3
172
#define ST7565R_POWER_CONTROL_SET               0x28
173
#define ST7565R_BOOSTER_MASK                    0x04
174
#define ST7565R_REGULATOR_MASK                  0x02
175
#define ST7565R_FOLLOWER_MASK                   0x01
176
#define ST7565R_BOOSTER_RATIO_SET               0xF8
177
#define ST7565R_BOOSTER_STEP_UP_2x3x4x          0x00
178
#define ST7565R_BOOSTER_STEP_UP_5x              0x01
179
#define ST7565R_BOOSTER_STEP_UP_6x              0x03
180
#define ST7565R_V0_VOLTAGE_REG_RATIO            0x20
181
#define ST7565R_DISPLAY_ON_OFF                  0xAE
182
#define ST7565R_SLEEP_MODE_SET                  0xAC
183
#define ST7565R_ELECTRONIC_VOLUME_MODE_SET      0x81
184
#define ST7565R_ELECTRONIC_VOLUME_REGISTER_SET  0x00
185
186
#define ST7565R_DISPLAY_START_LINE              0x40
187
#define ST7565R_SET_PAGE_ADDR                   0xB0
188
#define ST7565R_SET_COLUMN_NIBBLE_H             0x10
189
#define ST7565R_SET_COLUMN_NIBBLE_L             0x00
190
191
static void cursor_init();
192
193
//-----------------------------------------------------------------------------
194
// SPI access
195
//-----------------------------------------------------------------------------
196
197
static inline void SPI_init(void)
198
  {
199
    // Enable SPI, Master, set clock rate fck/16
200
    // ST7565 has fast SPI (up to 20MHz), no need to slow down SPI
201
    // our F_CPU is 10 MHz, so we use fcpu/2; a transfer lasts 1,6us 
202
203
    #define SPI_PRESCALER  2
204
205
    #if   (SPI_PRESCALER==2)
206
      #define SPI_PRESCALER_BITS   ((0<<SPR1)|(0<<SPR0))
207
      #define SPI2XMUL             (1 << SPI2X)
208
    #elif (SPI_PRESCALER==4)
209
      #define SPI_PRESCALER_BITS   ((0<<SPR1)|(0<<SPR0))
210
      #define SPI2XMUL             (0 << SPI2X)
211
    #elif (SPI_PRESCALER==8)
212
      #define SPI_PRESCALER_BITS   ((0<<SPR1)|(1<<SPR0))
213
      #define SPI2XMUL             (1 << SPI2X)
214
    #elif (SPI_PRESCALER==16)
215
      #define SPI_PRESCALER_BITS   ((0<<SPR1)|(1<<SPR0))
216
      #define SPI2XMUL             (0 << SPI2X)
217
    #elif (SPI_PRESCALER==32)
218
      #define SPI_PRESCALER_BITS   ((1<<SPR1)|(0<<SPR0))
219
      #define SPI2XMUL             (1 << SPI2X)
220
    #elif (SPI_PRESCALER==64)
221
      #define SPI_PRESCALER_BITS   ((1<<SPR1)|(0<<SPR0))
222
      #define SPI2XMUL             (0 << SPI2X)
223
    #elif (SPI_PRESCALER==128)
224
      #define SPI_PRESCALER_BITS   ((1<<SPR1)|(1<<SPR0))
225
      #define SPI2XMUL             (0 << SPI2X)
226
    #else
227
      #error SPI_PRESCALER is not defined or is not a supported value
228
    #endif
229
230
    SPCR = (0 << SPIE)    // 0 = no interrupt
231
         | (1 << SPE)     // 1 = enabled
232
         | (0 << DORD)    // 1 = data order LSB first; 0 = msb first
233
         | (1 << MSTR)    // 1 = master mode
234
         | (0 << CPOL)    // 0 = clock low when idle
235
         | (0 << CPHA)    // 0 = sample a leading edge
236
         | SPI_PRESCALER_BITS;
237
238
    SPSR = (SPI2XMUL);
239
  }
240
241
static unsigned char SPI_xmit (unsigned char aData) 
242
  {
243
    // sendchar(aData); // mirror to usb, debug only
244
245
    // here we do write only, so mormally no need to wait after xmit
246
    // we wait, since: we must ensure SPI_CS is kept low, otherwise
247
    // we would need a mutex.
248
249
    SPI_init();                    // only two accesses - but make sure we the correct setup!
250
    
251
    SPI_ENABLE_LCD;                // CS low
252
253
    SPDR = aData;                    
254
    while(!(SPSR & (1<<SPIF)));
255
256
    SPI_DISABLE_LCD;
257
    return (SPDR);          
258
  }
259
260
void dogm_send_command(unsigned char command)
261
  {
262
    SPI_ADR0_LOW;           //  clear A0-Bit -> command
263
    SPI_xmit(command);
264
  }  
265
266
void dogm_send_display(unsigned char data)
267
  {
268
    SPI_ADR0_HIGH;          //  set A0-Bit -> Display data
269
    SPI_xmit(data);
270
  }  
271
272
void dogm_reset()
273
  {
274
    #if (EA_DOGM_SOFTRESET == 1)
275
        dogm_send_command( ST7565R_RESET );
276
        _delay_ms(50);
277
    #else
278
        RES_PORT &= ~(1<<RES_BIT); // RES\ = LOW (Reset)
279
        _delay_ms(50);
280
        RES_PORT |= (1<<RES_BIT); // RES\ = HIGH (kein Reset)
281
    #endif
282
  }
283
284
285
void LCD_contrast(uint8_t contrast)
286
{
287
  /* "electronic volume" */
288
  dogm_send_command( ST7565R_ELECTRONIC_VOLUME_MODE_SET );
289
  dogm_send_command( ST7565R_ELECTRONIC_VOLUME_REGISTER_SET | ( contrast & 0x3F ) );
290
}
291
292
void dogm_sleep( uint8_t sleep )
293
{
294
  dogm_send_command( ST7565R_SLEEP_MODE_SET );
295
  if (sleep)  dogm_send_command(1);
296
  else        dogm_send_command(0);     // alive
297
}
298
299
void lcd_init(void)
300
  {
301
    uint8_t contrast;
302
303
    #ifdef eadr_lcd_contrast
304
       contrast = eeprom_read_byte((void *)eadr_lcd_contrast);
305
       if (contrast == 0 || contrast > 0x3F)
306
         // obviously void
307
         contrast = LCD_CONTRAST_DEFAULT;
308
    #else
309
       contrast = LCD_CONTRAST_DEFAULT;
310
    #endif
311
312
    SPI_init();
313
314
  //RESET Display
315
  dogm_reset();
316
317
  //Display start line
318
  dogm_send_command(ST7565R_DISPLAY_START_LINE | 0);          // Display start line 0
319
320
    #if (EA_DOGM_TOPVIEW == 1)
321
        dogm_send_command( ST7565R_ADC_SELECT | 0);             // ADC set normal
322
        dogm_send_command( ST7565R_COMMON_OUTPUT_MODE | 0x08 ); // Common output mode revers COM63-COM0
323
    #else // //Bottom view, default
324
        dogm_send_command( ST7565R_ADC_SELECT | 1 );            // ADC set reverse
325
        dogm_send_command( ST7565R_COMMON_OUTPUT_MODE | 0 );    // Common output mode normal COM0-COM63
326
    #endif // EA_DOGM_TOPVIEW 
327
328
  dogm_send_command(ST7565R_DISPLAY_NORMAL);                  // Display normal / invers
329
330
  //Hardware options
331
  dogm_send_command(ST7565R_LCD_BIAS_SET_1_9);                // Set bias 1/9 (Duty 1/65)
332
    #if (EA_DOGM_SINGLE_SUPPLY == 1)
333
        dogm_send_command( 
334
            ST7565R_POWER_CONTROL_SET | 
335
            ST7565R_BOOSTER_MASK |                              // booster on
336
            ST7565R_REGULATOR_MASK |                            // regulator on
337
            ST7565R_FOLLOWER_MASK );                            // follower on
338
        dogm_send_command( ST7565R_BOOSTER_RATIO_SET );         // internal booster to 4x
339
        dogm_send_command( ST7565R_BOOSTER_STEP_UP_2x3x4x );
340
    #else // dual supply 
341
        dogm_send_command( 
342
        ST7565R_POWER_CONTROL_SET |                             // booster off, regulator and follower on
343
        ST7565R_REGULATOR_MASK | ST7565R_FOLLOWER_MASK );
344
    #endif
345
346
  dogm_send_command(ST7565R_V0_VOLTAGE_REG_RATIO | 0x07 );    // V0 resistor-ratio (see table 11, here 6.5 -> 0b111 = 0x07)
347
348
  LCD_contrast(contrast);
349
350
  dogm_sleep(0);
351
352
  dogm_send_command(ST7565R_DISPLAY_ON_OFF | 1);              // Display on
353
354
    cursor_init();
355
  }
356
357
void lcd_reset(void)
358
  {
359
    dogm_reset();
360
  }
361
362
363
//------------------------------------------------------------------------------
364
//
365
// Housekeeping
366
//
367
//------------------------------------------------------------------------------
368
// These routines try to update the video_ram to the lcd
369
//
370
volatile uint8_t update_blocked;
371
372
373
374
t_update_lcd update_lcd[LCD_LINES / LCD_PAGE_HEIGHT ];
375
/*
376
struct
377
  {
378
    uint8_t page_modified;
379
  } update_lcd[LCD_LINES / LCD_PAGE_HEIGHT ];
380
*/
381
382
void update_display_page(unsigned char page)
383
  {
384
    dogm_send_command(ST7565R_SET_PAGE_ADDR + page);    // Set page address to <page>
385
    dogm_send_command(ST7565R_SET_COLUMN_NIBBLE_H + 0); // Set column address to 0 (4 MSBs)
386
    #if (EA_DOGM_TOPVIEW == 1)  
387
        dogm_send_command(ST7565R_SET_COLUMN_NIBBLE_L + 4); // Shift column address to 4 (4 LSBs) while ST7565 is 132x65 Controller
388
    #else
389
        dogm_send_command(ST7565R_SET_COLUMN_NIBBLE_L + 0); // Set column address to 0 (4 LSBs)
390
    #endif  
391
    uint8_t column;
392
393
    if (cursor_is_on_display && (page == CyPage))   // with cursor
394
      {
395
        for (column = 0; column < Cxmin; column++)
396
          {
397
            dogm_send_display(video_ram[page * LCD_LINE_LENGTH + (column)]);
398
          }
399
        for (column = Cxmin; column < Cxmax; column++)
400
          {
401
            dogm_send_display(video_ram[page * LCD_LINE_LENGTH + (column)] ^ CyPattern);
402
          }
403
        for (column = Cxmax; column < LCD_LINE_LENGTH; column++)
404
          {
405
            dogm_send_display(video_ram[page * LCD_LINE_LENGTH + (column)]);
406
          }
407
      }
408
    else                                            // normal
409
      {
410
        for (column = 0; column < LCD_LINE_LENGTH; column++)
411
          {
412
            dogm_send_display(video_ram[page * LCD_LINE_LENGTH + (column)]);
413
          }
414
      }
415
    update_lcd[page].page_modified = 0;
416
  }
417
418
// see cortos.h 
419
// This is the TASK_DISPLAY
420
// scan through the pages and if a page is modified: do the update
421
// if all pages are done: set inactive
422
t_cr_task update_display(void)
423
  {
424
    if (update_blocked) return(-1);     // fall asleep
425
426
    uint8_t page;
427
    for (page = 0; page < LCD_LINES / LCD_PAGE_HEIGHT; page++)
428
      {
429
        if (update_lcd[page].page_modified)
430
          {
431
            update_display_page(page);
432
            return(0);                  // ready again
433
          }
434
      }
435
    return(-1);                         // all up to date, fall asleep
436
  }
437
438
// call this function at end of your display routines
439
void enable_display_update(void)
440
  {
441
    update_blocked = 0;
442
    #ifdef _CORTOS_H_
443
        set_task_ready(TASK_DISPLAY);
444
    #else
445
        while (update_display() >= 0) {}
446
    #endif
447
448
  }
449
450
// complete update, no conditions; could be used in non cortos environment
451
// warning: this lasts 1,6ms
452
void update_display_complete(void)
453
  {
454
    uint8_t page;
455
    for (page = 0; page < LCD_LINES / LCD_PAGE_HEIGHT; page++)
456
      {
457
        update_display_page(page);
458
      }
459
  }
460
461
462
463
//------------------------------------------------------------------------------
464
//
465
// Upstream Interface
466
//
467
//------------------------------------------------------------------------------
468
// every access to video_ram must:
469
//   a) block the update process
470
//   b) set to corresponding page to modified
471
//
472
// when a display sequence is complete, it must enable the update
473
//
474
475
static void _blockcomplete(void)   // local helper functions
476
  {
477
    uint8_t page;
478
    for (page = 0; page < LCD_LINES / LCD_PAGE_HEIGHT; page++)
479
      {
480
        update_lcd[page].page_modified = 1;
481
      }
482
    update_blocked = 1;
483
  }
484
485
void LCD_clr(void)                   //clear LCD memory
486
  {
487
    _blockcomplete();
488
    uint16_t i;
489
    for (i=0; i<sizeof(video_ram); i++)
490
      {
491
        video_ram[i] = 0;
492
      }
493
    LCD_currentX = 0;
494
    LCD_currentY = 0;
495
    cursor_is_on_display = 0;
496
  }
497
498
void lcd_clear(void)
499
  {
500
    LCD_clr();
501
  }
502
503
void LCD_fill_with_counter(void)
504
  {
505
    _blockcomplete();
506
    uint16_t i;
507
    for (i=0; i<sizeof(video_ram); i++)
508
      {
509
        video_ram[i] = (unsigned char) i;
510
      }
511
  }
512
513
//------------------------------------------------------------------------------
514
// Copy a complete screen (no checking)
515
// bitmap_P points to bitmap in flash
516
// The bitmap must be organized like our page.
517
518
void LCD_fill_with_BMP(uint8_t* bitmap_P)
519
  {
520
    _blockcomplete();
521
    uint16_t i;
522
    for (i=0; i<sizeof(video_ram); i++)
523
      {
524
        video_ram[i] = pgm_read_byte(&bitmap_P[i]);
525
      }
526
  }
527
528
//------------------------------------------------------------------------------
529
//Move cursor to position x y (in pixels)
530
void LCD_setCursorXY(uint8_t x, uint8_t y)
531
  {
532
    LCD_currentX = x;
533
    LCD_currentY = y;
534
  }
535
536
void LCD_getCursorXY(uint8_t* x, uint8_t* y)
537
  {
538
    *x = LCD_currentX;
539
    *y = LCD_currentY;
540
  }
541
542
//------------------------------------------------------------------------------
543
//Write a byte to LCD.  Single 8 bit segment is send to current cursor position
544
//(page aligned!)
545
546
void LCD_writeByte(uint8_t data)
547
  {
548
    update_blocked = 1;
549
    
550
    uint8_t page;
551
    page = LCD_currentY / LCD_PAGE_HEIGHT;
552
    update_lcd[page].page_modified = 1;
553
    
554
    video_ram[page * LCD_LINE_LENGTH + LCD_currentX] = data;
555
    LCD_currentX++;
556
557
}
558
559
//------------------------------------------------------------------------------
560
// Pixel at x y
561
562
void LCD_pixelOn(uint8_t x, uint8_t y)
563
  {
564
    update_blocked = 1;
565
    uint8_t page;
566
    page = y / LCD_PAGE_HEIGHT;
567
    update_lcd[page].page_modified = 1;
568
569
    video_ram[page * LCD_LINE_LENGTH + x] |=  (1 << (y & 0x07));
570
  }
571
572
void LCD_pixelOff(uint8_t x, uint8_t y)
573
  {
574
    update_blocked = 1;
575
    uint8_t page;
576
    page = y / LCD_PAGE_HEIGHT;
577
    update_lcd[page].page_modified = 1;
578
579
    video_ram[page * LCD_LINE_LENGTH + x] &=  ~(1 << (y & 0x07));
580
  }
581
582
583
t_LCD_linePixel LCD_linePixel = 
584
  {
585
    1,  // Mode
586
    1,  // OffAct
587
    1,  // OffCount
588
    1,  // OnAct
589
    1,  // OnCount
590
  };
591
592
void LCD_pixelPattern(uint8_t x, uint8_t y)
593
  {
594
    if (LCD_linePixel.Mode)
595
      {
596
        LCD_linePixel.OnAct++;
597
        if (LCD_linePixel.OnAct >= LCD_linePixel.OnCount)
598
          {
599
            LCD_linePixel.OffAct = 0;
600
            LCD_linePixel.Mode = 0;
601
          }
602
        LCD_pixelOn(x, y);
603
      }
604
    else
605
      {
606
        LCD_linePixel.OffAct++;
607
        if (LCD_linePixel.OffAct >= LCD_linePixel.OffCount)
608
          {
609
            LCD_linePixel.OnAct = 0;
610
            LCD_linePixel.Mode = 1;
611
          }
612
        LCD_pixelOff(x, y);   
613
      }
614
  }
615
616
//------------------------------------------------------------------------------
617
//Invert pixel value at x y
618
void LCD_invertPixel(uint8_t x, uint8_t y)
619
  {
620
    update_blocked = 1;
621
    uint8_t mask;
622
    
623
    uint8_t page;
624
    page = y / LCD_PAGE_HEIGHT;
625
    update_lcd[page].page_modified = 1;
626
627
  
628
    mask = video_ram[page * LCD_LINE_LENGTH + x];
629
630
    if (mask & (1<<(y&0b111))) mask &= ~(1<<(y&0b111));
631
    else                       mask |= (1<<(y&0b111));
632
  
633
    video_ram[page * LCD_LINE_LENGTH + x] = mask;
634
}
635
636
//------------------------------------------------------------------------------
637
//Turn pixels on in a single page in LCD DDRAM from x1 to x2
638
void LCD_onPage(uint8_t page, uint8_t x1, uint8_t x2)
639
  {
640
    update_blocked = 1;
641
    update_lcd[page].page_modified = 1;
642
    uint8_t i;
643
    
644
    for (i=x1; i<=x2; i++)
645
      {
646
        video_ram[page * LCD_LINE_LENGTH + i] = 0xff;
647
      }
648
  }
649
650
//------------------------------------------------------------------------------
651
//invert pixels on a page from x1 to x2
652
// x1 must be smaller than x2
653
void LCD_invertPage(uint8_t page, uint8_t x1, uint8_t x2)
654
  {
655
    update_blocked = 1;
656
    uint8_t mask,i;
657
    update_lcd[page].page_modified = 1;
658
    for (i=x1; i<=x2; i++)
659
      {
660
        mask = video_ram[page * LCD_LINE_LENGTH + i];
661
        video_ram[page * LCD_LINE_LENGTH + i] = ~mask;
662
      }
663
}
664
665
//------------------------------------------------------------------------------
666
//clear a single page in LCD DDRAM from x1 to x2
667
void LCD_offPage(uint8_t page, uint8_t x1, uint8_t x2)
668
  {
669
    update_blocked = 1;
670
    uint8_t i;
671
    update_lcd[page].page_modified = 1;
672
    for (i=x1; i<=x2; i++)
673
      {
674
        video_ram[page * LCD_LINE_LENGTH + i] = 0x00;
675
      }
676
  }
677
678
//------------------------------------------------------------------------------
679
// Copy a 8 bit high bmp to page
680
// bitmap_P points to bitmap in flash, page correct
681
void LCD_fillPage_with_BMP(uint8_t width, PGM_VOID_P bitmap_P)
682
  {
683
    update_blocked = 1;
684
    
685
    uint8_t page;
686
    uint8_t upper_bound;
687
    page = LCD_currentY / LCD_PAGE_HEIGHT;
688
    update_lcd[page].page_modified = 1;
689
690
    if ((LCD_currentX + width) > LCD_LINE_LENGTH)
691
        upper_bound=LCD_LINE_LENGTH-1;
692
    else
693
        upper_bound = LCD_currentX + width;  
694
  
695
    for ( ; LCD_currentX < upper_bound; LCD_currentX++)
696
      {
697
        video_ram[page * LCD_LINE_LENGTH + LCD_currentX] = pgm_read_byte(bitmap_P++);
698
      }
699
  }
700
701
702
//-----------------------------------------------------------------------------
703
// put a graphical bitmap with width and height (given in pages)
704
// at LCD_currentX,LCD_currentY
705
// y must be page-aligned, input bitmap is page aligned
706
//
707
void LCD_drawBMP_aligned(PGM_VOID_P  bitmap_P, uint8_t width, uint8_t height)
708
{
709
  uint8_t saved_x,j;
710
  PGM_P my_bitmap = bitmap_P;
711
  //whole pages  
712
  saved_x = LCD_currentX;
713
  for (j=0; j < height; j++)
714
    {
715
      LCD_fillPage_with_BMP(width, &my_bitmap[j*width]);
716
      LCD_currentY += LCD_PAGE_HEIGHT;
717
      if(LCD_currentY>=LCD_LINES) LCD_currentY=0;
718
      LCD_currentX = saved_x;
719
    }
720
}
721
722
723
//-----------------------------------------------------------------------------
724
// put a graphical bitmap with width and height (given in first two locations of bitmap_P)
725
// at LCD_currentX,LCD_currentY
726
// input bitmap is page aligned
727
// parameters: display_keep, display_invers
728
//
729
void LCD_drawBMP(PGM_VOID_P  bitmap_P)
730
  {
731
    uint8_t page, pageoffset;
732
    uint8_t copy_width;
733
    uint8_t pixel_mask, and_mask, and_maskL, and_maskH;
734
    uint8_t j;
735
736
    PGM_P z_ptr = bitmap_P;
737
    
738
    update_blocked = 1;
739
740
    uint8_t width = pgm_read_byte(z_ptr++);
741
    int8_t height = pgm_read_byte(z_ptr++);
742
743
    // bound check
744
    if ((LCD_currentX + width) > LCD_LINE_LENGTH)    copy_width = LCD_LINE_LENGTH-LCD_currentX;
745
    else                                             copy_width = width; 
746
    
747
    if (copy_width == 0) return; 
748
749
    if ((LCD_currentY + height) > LCD_LINES)         height =  LCD_LINES-LCD_currentY;
750
751
    page = LCD_currentY / LCD_PAGE_HEIGHT;
752
    pageoffset = LCD_currentY & (LCD_PAGE_HEIGHT-1);
753
754
    and_maskL = 1 << pageoffset;
755
    and_maskL -= 1;                // aus pageoffset 3 wird dadurch 0b00000111
756
757
758
    while (height > 0)
759
      {
760
        update_lcd[page].page_modified = 1;
761
        // copy one page of char set to video_ram
762
        if ((pageoffset + height) < 8)
763
          {
764
            // mask on both sides required
765
766
            and_maskH = 1 << (pageoffset + height);        // z.B. 3 + 4
767
            and_maskH -= 1; 
768
            and_maskH = ~and_maskH;                         // z.B. 0b10000000
769
770
            pixel_mask = ~(and_maskL | and_maskH);          // z.B. 0b01111000
771
            and_mask = and_maskL | and_maskH | display_keep;
772
773
            for (j=0; j<copy_width; j++)
774
              {
775
                uint8_t temp;
776
                uint8_t pixels;
777
                pixels = pgm_read_byte(z_ptr+j) ^ display_invers;
778
                pixels = pixels << pageoffset;
779
                pixels &= pixel_mask;
780
781
                temp = video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j];
782
                temp &= and_mask;
783
                temp ^= pixels;
784
                video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j] = temp;
785
              }
786
787
788
          }
789
        else if ((pageoffset + height) == 8)
790
          {
791
            // mask only on lower bound required
792
793
            and_mask = and_maskL | display_keep;
794
795
            for (j=0; j<copy_width; j++)
796
              {
797
                uint8_t temp;
798
                uint8_t pixels;
799
                pixels = pgm_read_byte(z_ptr+j) ^ display_invers;
800
                pixels = pixels << pageoffset;
801
                temp = video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j];
802
                temp &= and_mask;
803
                temp ^= pixels;
804
                video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j] = temp;
805
              }
806
          }
807
        else
808
          {
809
            // two pages affected
810
811
            and_mask = and_maskL | display_keep;
812
813
            for (j=0; j<copy_width; j++)
814
              {
815
                uint8_t temp;
816
                uint8_t pixels;
817
                pixels = pgm_read_byte(z_ptr+j) ^ display_invers;
818
                pixels = pixels << pageoffset;                                  //es werden nullen nachgezogen
819
                temp = video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j];
820
                temp &= and_mask;
821
                temp ^= pixels;                                                 // einkopieren
822
                video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j] = temp;
823
              }
824
            
825
            // now next page (look at upper bound)
826
            page++;
827
            update_lcd[page].page_modified = 1;
828
829
            if (height < 8) and_maskH = 1 << (pageoffset + height - 8);
830
            else and_maskH = 1 << (pageoffset); 
831
            and_maskH -= 1;
832
            pixel_mask = and_maskH; 
833
            and_mask = ~and_maskH | display_keep;
834
835
            for (j=0; j<copy_width; j++)
836
              {
837
                uint8_t temp;
838
                uint8_t pixels;
839
                pixels = pgm_read_byte(z_ptr+j) ^ display_invers;
840
                pixels = pixels >> (8-pageoffset);
841
                pixels &= pixel_mask;
842
                temp = video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j];
843
                temp &= and_mask;
844
                temp ^= pixels;
845
                video_ram[page * LCD_LINE_LENGTH + LCD_currentX + j] = temp;
846
              }
847
            page--;
848
            
849
          }
850
        z_ptr += width;
851
        page++;
852
        height -= 8;
853
      }
854
    LCD_currentX = LCD_currentX + copy_width;
855
  }
856
857
858
//------------------------------------------------------------------------------
859
// Put a single char to LCD on line at current cursor position
860
// Note that page is used not exact coordinates 
861
// Default font is used, no checking of illegal chars
862
void LCD_putchar(uint8_t c)
863
  {
864
    update_blocked = 1;
865
    
866
    uint8_t i = 0;  
867
    for(i=0; i<Font5x7_WIDTH; i++)
868
      {         
869
        if (LCD_currentX >= LCD_LINE_LENGTH)
870
          {
871
            if (LCD_currentY < LCD_LINES+LCD_PAGE_HEIGHT)
872
              {
873
                LCD_setCursorXY(0,LCD_currentY+LCD_PAGE_HEIGHT);
874
              }
875
            else 
876
              {
877
              LCD_setCursorXY(0,0);    
878
              }
879
          }
880
        LCD_writeByte(pgm_read_byte(&Font5x7[(c - FONT5x7_START)*Font5x7_WIDTH + i])); 
881
      }    
882
    LCD_writeByte(0x00);  
883
  }
884
885
//------------------------------------------------------------------------------
886
// Put a single char to LCD on line at current cursor position
887
// Note that page is used not exact coordinates 
888
// Proportional font is used, no checking of illegal chars
889
// IgnP (=Ignore Pattern) is a pixel pattern (typ. 0xAA) which determines the end of pixel map
890
// 
891
892
#define Font5x7prop_WIDTH       5
893
#define Font5x7prop_HEIGHT      7
894
void LCD_PF_putchar(uint8_t c)
895
  {
896
    update_blocked = 1;
897
    
898
    uint8_t i = 0;  
899
    uint8_t pixels;  
900
    for(i=0; i<Font5x7prop_WIDTH; i++)
901
      {         
902
        if (LCD_currentX >= LCD_LINE_LENGTH)
903
          {
904
            if (LCD_currentY < LCD_LINES+LCD_PAGE_HEIGHT)
905
              {
906
                LCD_setCursorXY(0,LCD_currentY+LCD_PAGE_HEIGHT);
907
              }
908
            else 
909
              {
910
              LCD_setCursorXY(0,0);    
911
              }
912
          }
913
        pixels = pgm_read_byte(&Font5x7prop[(c - FONT5x7_START)*Font5x7prop_WIDTH + i]);
914
        if (pixels != IgnP) LCD_writeByte(pixels); 
915
      }    
916
    LCD_writeByte(0x00);  
917
  }
918
919
unsigned char LCD_PF_get_sizeX(uint8_t c)
920
  {
921
    uint8_t i = 0;  
922
    uint8_t pixels;  
923
    for(i=0; i<Font5x7prop_WIDTH; i++)
924
      {         
925
        pixels = pgm_read_byte(&Font5x7prop[(c - FONT5x7_START)*Font5x7prop_WIDTH + i]);
926
        if (pixels == IgnP) return(i); 
927
      }    
928
    return(i);
929
  }
930
931
932
//---------------------------------------------------------------------------------
933
// Put a string on display - auto for next line is default (from LCD_putchar)
934
// null terminated strings are expected
935
// /n  for new line 
936
void LCD_puts(uint8_t* pString)
937
  {      
938
    update_blocked = 1;
939
    
940
    uint8_t i=0;
941
    while (pString[i] != '\0')
942
      {
943
        if (pString[i]=='\n')
944
          {           
945
            LCD_currentX=0;
946
            LCD_currentY+=LCD_PAGE_HEIGHT;    
947
          }
948
        else
949
          {
950
            LCD_putchar(pString[i]);
951
          }
952
        i++;    
953
      }
954
  }
955
956
//-----------------------------------------------------------------------------
957
//Put a string on selected page (line) starting at x
958
//
959
void LCD_putsp(uint8_t* pString, uint8_t page, uint8_t x)
960
  {
961
    LCD_currentX = x;
962
    LCD_currentY = page * LCD_PAGE_HEIGHT;
963
    LCD_puts(pString);
964
  }
965
966
967
968
969
970
//------------------------------------------------------------------------------
971
// printf - fixed font 4x6, only for numbers
972
973
974
#include "bmp/number_6px_glcd.h"
975
976
void LCD_number_6px(char* data)
977
  {
978
    char val;
979
    uint8_t i = 0; 
980
    while( (val = *data++ ) )
981
      {
982
        if (val == ' ') 
983
          {
984
            for(i=0; i < NUMBER_6PX_GLCD_WIDTH; i++)  LCD_writeByte(0x00); 
985
          }
986
        else if ( val >= '0' && val <= '9' ) 
987
          {
988
            for(i=0; i < NUMBER_6PX_GLCD_WIDTH; i++)  
989
                LCD_writeByte(pgm_read_byte(&number_6px_glcd_bmp[(val-'0')*NUMBER_6PX_GLCD_WIDTH + i])); 
990
          }
991
        LCD_writeByte(0x00); 
992
      }
993
  }
994
995
996
//-----------------------------------------------------------------------------
997
// Cursor
998
999
1000
void cursor_init(void)
1001
  {
1002
    cursor_is_on_display = 0;
1003
    CursorActive = 0;
1004
  }
1005
1006
void cursor_toggle(void)
1007
  {
1008
    cursor_is_on_display ^= 1;
1009
  }
1010
1011
void cursor_off(void)
1012
  {
1013
    if (cursor_is_on_display)
1014
      {
1015
        cursor_is_on_display = 0;
1016
        update_lcd[CyPage].page_modified = 1;
1017
      } 
1018
    // CursorActive = 0;
1019
    set_task_blocked(TASK_CURSOR);
1020
  }
1021
1022
void cursor_on_at(uint8_t x, uint8_t y, uint8_t size)
1023
  {
1024
    if (cursor_is_on_display)
1025
      {
1026
        update_lcd[CyPage].page_modified = 1;  // force update at old location
1027
      }
1028
    Cxmin = 6 * x;
1029
    Cxmax = 6 * (x + size);
1030
    CyPage = y;
1031
    CyPattern = 0x80;
1032
    
1033
    cursor_is_on_display = 1;
1034
    update_lcd[CyPage].page_modified = 1;   // update at new location
1035
1036
    // CursorActive = 1;
1037
    set_task_ready(TASK_CURSOR);
1038
  }
1039
1040
1041
1042
#define CURSOR_PERIOD  600000L
1043
1044
t_cr_task cursor(void)
1045
  {
1046
    if (cursor_is_on_display)
1047
      {
1048
        cursor_is_on_display = 0;
1049
        update_lcd[CyPage].page_modified = 1;
1050
      }
1051
    else
1052
      {
1053
        cursor_is_on_display = 1;
1054
        update_lcd[CyPage].page_modified = 1;
1055
      }
1056
    enable_display_update();
1057
    return(CURSOR_PERIOD / SYSTICK_PERIOD);
1058
  }
1059
1060
1061
#endif // (DISPLAY == LCD_EA128x64)