Subversion Repositories idreammicro-avr

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
34 jlesech 1
/**************************************************************************//**
2
 * \brief Snootlab Deuligne library
3
 * \author Copyright (C) 2012  Julien Le Sech - www.idreammicro.com
4
 * \version 1.0
5
 * \date 20121026
6
 *
7
 * This file is part of the iDreamMicro library.
8
 *
9
 * This library is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU Lesser General Public License as published by the Free
11
 * Software Foundation, either version 3 of the License, or (at your option) any
12
 * later version.
13
 *
14
 * This library is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program. If not, see http://www.gnu.org/licenses/
21
 ******************************************************************************/
22
 
23
/**************************************************************************//**
24
 * \headerfile deuligne.c
25
 ******************************************************************************/
26
 
27
/******************************************************************************
28
 * Header file inclusions.
29
 ******************************************************************************/
30
 
31
#include "../deuligne.h"
32
 
33
#include <adc/adc.h>
34
#include <mcp23008/mcp23008.h>
35
 
36
#include <util/delay.h>
37
 
38
#include <assert.h>
39
#include <stdbool.h>
40
#include <stdint.h>
41
#include <stdlib.h>
42
 
43
/******************************************************************************
44
 * Private macro definitions.
45
 ******************************************************************************/
46
 
47
/**************************************************************************//**
48
 * \def DEULIGNE__HARDWARE_ADDRESS
49
 * \brief MCP23008 hardware address. Pins A0 to A3.
50
 ******************************************************************************/
51
#define DEULIGNE__HARDWARE_ADDRESS          0x07
52
 
53
#define DEULIGNE__GPIO_E                    4
54
#define DEULIGNE__GPIO_RW                   5
55
#define DEULIGNE__GPIO_RS                   6
56
#define DEULIGNE__GPIO_BACKLIGHT            7
57
 
58
/**************************************************************************//**
59
 * \def LCD__CMD_CLEAR_DISPLAY
60
 * \brief Clear display.
61
 ******************************************************************************/
62
#define LCD__CMD_CLEAR_DISPLAY              0x01
63
 
64
/**************************************************************************//**
65
 * \def LCD__CMD_RETURN_HOME
66
 * \brief Return Home (execution time = 1.52 ms).
67
 ******************************************************************************/
68
#define LCD__CMD_RETURN_HOME                0x02
69
 
70
// Function set (execution time = 37 s).
71
#define LCD__CMD_FUNCTION_SET               0x20
72
#define LCD__CMD_DATA_LENGTH_4_BIT          0x00
73
#define LCD__CMD_DATA_LENGTH_8_BIT          0x10
74
#define LCD__CMD_DISPLAY_LINE_1             0x00
75
#define LCD__CMD_DISPLAY_LINE_2             0x08
76
#define LCD__CMD_CHARACTER_FONT_5x8_DOTS    0x00
77
#define LCD__CMD_CHARACTER_FONT_5x10_DOTS   0x04
78
 
79
// Set entry mode (execution time = 37 s).
80
#define LCD__CMD_ENTRY_MODE_SET             0x04
81
#define LCD__CMD_INCREMENT_ADDRESS          0x02
82
#define LCD__CMD_DECREMENT_ADDRESS          0x00
83
#define LCD__CMD_DISPLAY_SHIFT_DISABLED     0x00
84
#define LCD__CMD_DISPLAY_SHIFT_RIGHT        0x01
85
#define LCD__CMD_DISPLAY_SHIFT_LEFT         0x03
86
 
87
// Display on/off control (execution time = 37 s).
88
#define LCD__CMD_DISPLAY_CONTROL            0x08
89
#define LCD__CMD_ENABLE_DISPLAY             0x04
90
#define LCD__CMD_DISABLE_DISPLAY            0x00
91
#define LCD__CMD_ENABLE_CURSOR_DISPLAY      0x02
92
#define LCD__CMD_DISABLE_CURSOR_DISPLAY     0x00
93
#define LCD__CMD_ENABLE_CURSOR_BLINK        0x01
94
#define LCD__CMD_DISABLE_CURSOR_BLINK       0x00
95
 
96
// Cursor or display shift (execution time = 37 s).
97
#define LCD__CMD_CURSOR_OR_DISPLAY_SHIFT    0x10
98
#define LCD__CMD_SHIFT_CURSOR               0x00
99
#define LCD__CMD_SHIFT_DISPLAY              0x08
100
#define LCD__CMD_SHIFT_RIGHT                0x04
101
#define LCD__CMD_SHIFT_LEFT                 0x00
102
 
103
// CGRAM address.
104
#define LCD__CMD_SET_CGRAM_ADDRESS          0x40
105
 
106
// DDRAM address.
107
#define LCD__CMD_SET_DDRAM_ADDRESS          0x80
108
 
109
// Line addresses.
110
#define LCD__LINE_1_ADDRESS                 0x40
111
#define LCD__LINE_2_ADDRESS                 0x80
112
#define LCD__LINE_3_ADDRESS                 0x54
113
#define LCD__LINE_4_ADDRESS                 0x94
114
 
115
/******************************************************************************
116
 * Private function prototypes.
117
 ******************************************************************************/
118
 
119
/**************************************************************************//**
120
 * \fn static void deuligne__write_command(uint8_t command)
121
 *
122
 * \brief Write a command on LCD port.
123
 *
124
 * \param command   Command to write.
125
 ******************************************************************************/
126
static
127
void
128
deuligne__write_command
129
(
130
    uint8_t command
131
);
132
 
133
 /**************************************************************************//**
134
  * \fn static void deuligne__write_byte(uint8_t data)
135
  *
136
  * \brief Write a byte on LCD port.
137
  *
138
  * \param data  Byte to write.
139
  ******************************************************************************/
140
static
141
void
142
deuligne__write_byte
143
(
144
    uint8_t data
145
);
146
 
147
/**************************************************************************//**
148
 * \fn static void deuligne__write_high_nibble(uint8_t data)
149
 *
150
 * \brief Write the high nibble of a byte on LCD port.
151
 *
152
 * \param data  Byte which write high nibble.
153
 ******************************************************************************/
154
static
155
void
156
deuligne__write_high_nibble
157
(
158
    uint8_t data
159
);
160
 
161
/**************************************************************************//**
162
 * \fn static void deuligne__enable_data(void)
163
 *
164
 * \brief Enable data on LCD port.
165
 ******************************************************************************/
166
static
167
void
168
deuligne__enable_data
169
(
170
    void
171
);
172
 
173
/******************************************************************************
174
 * Private constant definitions.
175
 ******************************************************************************/
176
 
177
/**************************************************************************//**
178
 *
179
 ******************************************************************************/
180
static const uint16_t deuligne__adc_values[] =
181
{
182
    [DEULIGNE__KEY__RIGHT]  = 50,
183
    [DEULIGNE__KEY__UP]     = 190,
184
    [DEULIGNE__KEY__DOWN]   = 400,
185
    [DEULIGNE__KEY__LEFT]   = 540,
186
    [DEULIGNE__KEY__SELECT] = 770,
187
    [DEULIGNE__KEY__NONE]   = 1024
188
};
189
 
190
/******************************************************************************
191
 * Public function definitions.
192
 ******************************************************************************/
193
 
194
/**************************************************************************//**
195
 * \fn void deuligne__initialize(void)
196
 *
197
 * \brief Initialize Deuligne.
198
 ******************************************************************************/
199
void
200
deuligne__initialize
201
(
202
    void
203
){
204
    // Initialize MCP23008.
205
    mcp23008__initialize(DEULIGNE__HARDWARE_ADDRESS);
206
 
207
    // Configure all GPIOs in output.
208
    mcp23008__configure_port(0x00);
209
 
210
    mcp23008__set_pin_level(DEULIGNE__GPIO_RW, MCP23008__LEVEL__LOW);
211
 
212
    // Wait for LCD display power-up initialization.
213
    _delay_ms(45);
214
 
215
    // Initialize LCD display.
216
 
217
    // Set interface.
218
    deuligne__write_high_nibble(LCD__CMD_FUNCTION_SET | LCD__CMD_DATA_LENGTH_8_BIT);
219
    deuligne__write_high_nibble(LCD__CMD_FUNCTION_SET | LCD__CMD_DATA_LENGTH_8_BIT);
220
    deuligne__write_high_nibble(LCD__CMD_FUNCTION_SET | LCD__CMD_DATA_LENGTH_8_BIT);
221
    deuligne__write_high_nibble(LCD__CMD_FUNCTION_SET | LCD__CMD_DATA_LENGTH_4_BIT);
222
 
223
    // Set function.
224
    deuligne__set_function(DEULIGNE__DISPLAY_LINE_2, DEULIGNE__CHARACTER_FONT_5x8_DOTS);
225
 
226
    // Set entry mode.
227
    deuligne__set_entry_mode(DEULIGNE__INCREMENT_ADDRESS, DEULIGNE__DISPLAY_SHIFT_DISABLED);
228
 
229
    // Clear display.
230
    deuligne__clear_display();
231
 
232
    // Initialize ADC.
233
    adc__single_channel_initialize(ADC__CHANNEL_0);
234
}
235
 
236
/**************************************************************************//**
237
 * \fn void deuligne__switch_on_backlight(void)
238
 *
239
 * \brief Switch on backlight.
240
 ******************************************************************************/
241
void
242
deuligne__switch_on_backlight
243
(
244
    void
245
){
246
    mcp23008__set_pin_level(DEULIGNE__GPIO_BACKLIGHT, MCP23008__LEVEL__HIGH);
247
}
248
 
249
/**************************************************************************//**
250
 * \fn void deuligne__switch_off_backlight(void)
251
 *
252
 * \brief Switch off backlight.
253
 ******************************************************************************/
254
void
255
deuligne__switch_off_backlight
256
(
257
    void
258
){
259
    mcp23008__set_pin_level(DEULIGNE__GPIO_BACKLIGHT, MCP23008__LEVEL__LOW);
260
}
261
 
262
/**************************************************************************//**
263
 * \fn void deuligne__set_function(
264
 * deuligne__display_line_t     display_line,
265
 * deuligne__character_font_t   character_font)
266
 *
267
 * \brief Set the number of lines available on LCD display and the character font.
268
 *
269
 * \param display_line number of lines
270
 * \param character_font character font
271
 ******************************************************************************/
272
void
273
deuligne__set_function
274
(
275
    deuligne__display_line_t     display_line,
276
    deuligne__character_font_t   character_font
277
){
278
    uint8_t command = LCD__CMD_FUNCTION_SET;
279
 
280
    switch (display_line)
281
    {
282
        case DEULIGNE__DISPLAY_LINE_1:
283
        {
284
            command |= LCD__CMD_DISPLAY_LINE_1;
285
        }
286
        break;
287
 
288
        case DEULIGNE__DISPLAY_LINE_2:
289
        {
290
            command |= LCD__CMD_DISPLAY_LINE_2;
291
        }
292
        break;
293
 
294
        default:
295
        break;
296
    }
297
 
298
    switch (character_font)
299
    {
300
        case DEULIGNE__CHARACTER_FONT_5x8_DOTS:
301
        {
302
            command |= LCD__CMD_CHARACTER_FONT_5x8_DOTS;
303
        }
304
        break;
305
 
306
        case DEULIGNE__CHARACTER_FONT_5x10_DOTS:
307
        {
308
            command |= LCD__CMD_CHARACTER_FONT_5x10_DOTS;
309
        }
310
        break;
311
 
312
        default:
313
        break;
314
    }
315
 
316
    deuligne__write_command(command);
317
}
318
 
319
/**************************************************************************//**
320
 * \fn void deuligne__set_entry_mode(
321
 * deuligne__address_mode_t     address_mode,
322
 * deuligne__display_shift_t    display_shift)
323
 *
324
 * \brief Set LCD display entry mode.
325
 *
326
 * \param address_mode specify if DDRAM address is incremented or decremented
327
 * when a character code is written
328
 * \param display_shift set the display shift when a character code is written
329
 ******************************************************************************/
330
void
331
deuligne__set_entry_mode
332
(
333
    deuligne__address_mode_t     address_mode,
334
    deuligne__display_shift_t    display_shift
335
){
336
    uint8_t command = LCD__CMD_ENTRY_MODE_SET;
337
 
338
    switch (address_mode)
339
    {
340
        case DEULIGNE__INCREMENT_ADDRESS:
341
        {
342
            command |= LCD__CMD_INCREMENT_ADDRESS;
343
        }
344
        break;
345
 
346
        case DEULIGNE__DECREMENT_ADDRESS:
347
        {
348
            command |= LCD__CMD_DECREMENT_ADDRESS;
349
        }
350
        break;
351
 
352
        default:
353
        break;
354
    }
355
 
356
    switch (display_shift)
357
    {
358
        case DEULIGNE__DISPLAY_SHIFT_DISABLED:
359
        {
360
            command |= LCD__CMD_DISPLAY_SHIFT_DISABLED;
361
        }
362
        break;
363
 
364
        case DEULIGNE__DISPLAY_SHIFT_RIGHT:
365
        {
366
            command |= LCD__CMD_DISPLAY_SHIFT_RIGHT;
367
        }
368
        break;
369
 
370
        case DEULIGNE__DISPLAY_SHIFT_LEFT:
371
        {
372
            command |= LCD__CMD_DISPLAY_SHIFT_LEFT;
373
        }
374
        break;
375
 
376
        default:
377
        break;
378
    }
379
 
380
    deuligne__write_command(command);
381
}
382
 
383
/**************************************************************************//**
384
 * \fn void deuligne__set_display(
385
 * bool enable_display,
386
 * bool enable_cursor_display,
387
 * bool enable_cursor_blink)
388
 *
389
 * \brief Set LCD display.
390
 * Enable or disable display, cursor display and cursor blink.
391
 *
392
 * \param enable_display true to enable display or false in contrary case
393
 * \param enable_cursor_display true to enable cursor display or false in
394
 * contrary case
395
 * \param enable_cursor_blink true to enable cursor blink or false in contrary
396
 * case
397
 ******************************************************************************/
398
void
399
deuligne__set_display
400
(
401
    bool enable_display,
402
    bool enable_cursor_display,
403
    bool enable_cursor_blink
404
){
405
    uint8_t command = LCD__CMD_DISPLAY_CONTROL;
406
    if (enable_display)
407
    {
408
        command |= LCD__CMD_ENABLE_DISPLAY;
409
    }
410
    if (enable_cursor_display)
411
    {
412
        command |= LCD__CMD_ENABLE_CURSOR_DISPLAY;
413
    }
414
    if (enable_cursor_blink)
415
    {
416
        command |= LCD__CMD_ENABLE_CURSOR_BLINK;
417
    }
418
    deuligne__write_command(command);
419
}
420
 
421
/**************************************************************************//**
422
 * \fn void deuligne__clear_display(void)
423
 *
424
 * \brief Clear LCD display.
425
 * Wait time of 2 ms.
426
 ******************************************************************************/
427
void
428
deuligne__clear_display
429
(
430
    void
431
){
432
    deuligne__write_command(LCD__CMD_CLEAR_DISPLAY);
433
    _delay_ms(2);
434
}
435
 
436
/**************************************************************************//**
437
 * \fn void deuligne__return_home(void)
438
 *
439
 * \brief Set cursor on the first column of the first line.
440
 * Wait time of 2 ms.
441
 ******************************************************************************/
442
void
443
deuligne__return_home
444
(
445
    void
446
){
447
    deuligne__write_command(LCD__CMD_RETURN_HOME);
448
    _delay_ms(2);
449
}
450
 
451
/**************************************************************************//**
452
 *\fn void deuligne__set_cursor_position(
453
 * deuligne__line_t line,
454
 * uint8_t          column)
455
 *
456
 * \brief Set cursor position.
457
 *
458
 * \param line destination line
459
 * \param column destination column
460
 ******************************************************************************/
461
void
462
deuligne__set_cursor_position
463
(
464
    deuligne__line_t    line,
465
    uint8_t             column
466
){
467
    uint8_t address = 0;
468
 
469
    switch (line)
470
    {
471
        case DEULIGNE__LINE_1:
472
        {
473
            address = LCD__LINE_1_ADDRESS;
474
        }
475
        break;
476
 
477
        case DEULIGNE__LINE_2:
478
        {
479
            address = LCD__LINE_2_ADDRESS;
480
        }
481
        break;
482
 
483
        case DEULIGNE__LINE_3:
484
        {
485
            address = LCD__LINE_3_ADDRESS;
486
        }
487
        break;
488
 
489
        case DEULIGNE__LINE_4:
490
        {
491
            address = LCD__LINE_4_ADDRESS;
492
        }
493
        break;
494
 
495
        default:
496
        {
497
            address = LCD__LINE_1_ADDRESS;
498
        }
499
        break;
500
    }
501
 
502
    address += column;
503
 
504
    deuligne__write_command(LCD__CMD_SET_CGRAM_ADDRESS + address);
505
}
506
 
507
/**************************************************************************//**
508
 * \fn void deuligne__shift_display(deuligne__shift_direction_t shift_direction)
509
 *
510
 * \brief Shift the display on the right or on the left.
511
 *
512
 * \param shift_direction display shift direction
513
 ******************************************************************************/
514
void
515
deuligne__shift_display
516
(
517
    deuligne__shift_direction_t shift_direction
518
){
519
    uint8_t command = LCD__CMD_CURSOR_OR_DISPLAY_SHIFT | LCD__CMD_SHIFT_DISPLAY;
520
 
521
    switch (shift_direction)
522
    {
523
        case DEULIGNE__SHIFT_DIRECTION_RIGHT:
524
        {
525
            command |= LCD__CMD_SHIFT_RIGHT;
526
        }
527
        break;
528
 
529
        case DEULIGNE__SHIFT_DIRECTION_LEFT:
530
        {
531
            command |= LCD__CMD_SHIFT_LEFT;
532
        }
533
        break;
534
 
535
        default:
536
        break;
537
    }
538
 
539
    deuligne__write_command(command);
540
}
541
 
542
/**************************************************************************//**
543
 * \fn void deuligne__shift_cursor(deuligne__shift_direction_t shift_direction)
544
 *
545
 * \brief Shift the cursor on the right or on the left.
546
 *
547
 * \param shift_direction cursor shift direction
548
 ******************************************************************************/
549
void
550
deuligne__shift_cursor
551
(
552
    deuligne__shift_direction_t shift_direction
553
){
554
    uint8_t command = LCD__CMD_CURSOR_OR_DISPLAY_SHIFT | LCD__CMD_SHIFT_CURSOR;
555
 
556
    switch (shift_direction)
557
    {
558
        case DEULIGNE__SHIFT_DIRECTION_RIGHT:
559
        {
560
            command |= LCD__CMD_SHIFT_RIGHT;
561
        }
562
        break;
563
 
564
        case DEULIGNE__SHIFT_DIRECTION_LEFT:
565
        {
566
            command |= LCD__CMD_SHIFT_LEFT;
567
        }
568
        break;
569
 
570
        default:
571
        break;
572
    }
573
 
574
    deuligne__write_command(command);
575
}
576
 
577
/**************************************************************************//**
578
 * \fn void deuligne__write_char(char data)
579
 *
580
 * \brief Write a character on LCD.
581
 *
582
 * \param data  Character to write.
583
 ******************************************************************************/
584
void
585
deuligne__write_char
586
(
587
    char data
588
){
589
    mcp23008__set_pin_level(DEULIGNE__GPIO_RS, MCP23008__LEVEL__HIGH);
590
    deuligne__write_byte(data);
591
}
592
 
593
/**************************************************************************//**
594
 * \fn void deuligne__write_string(const char* string_to_write)
595
 *
596
 * \brief Write a string on LCD display from current cursor position.
597
 *
598
 * \param string_to_write string to write
599
 ******************************************************************************/
600
void
601
deuligne__write_string
602
(
603
    const char* string_to_write
604
){
605
    // Check the preconditions.
606
    assert(NULL != string_to_write);
607
 
608
    while (*string_to_write != '\0')
609
    {
610
        deuligne__write_char(*string_to_write++);
611
    }
612
}
613
 
614
/**************************************************************************//**
615
 * \fn void deuligne__get_key(void)
616
 *
617
 * \brief Get key.
618
 *
619
 * \return Key.
620
 ******************************************************************************/
621
deuligne__key_t
622
deuligne__get_key
623
(
624
    void
625
){
626
    deuligne__key_t key = DEULIGNE__KEY__NONE;
627
 
628
    // Read ADC0 value.
629
    uint16_t value = adc__single_channel_read();
630
 
631
    // Find key.
632
    for (deuligne__key_t i = DEULIGNE__KEY__RIGHT; i <= DEULIGNE__KEY__NONE; i++)
633
    {
634
        if (value < deuligne__adc_values[i])
635
        {
636
            key = i;
637
            break;
638
        }
639
    }
640
 
641
    return key;
642
}
643
 
644
/******************************************************************************
645
 * Private function definitions.
646
 ******************************************************************************/
647
 
648
/**************************************************************************//**
649
 * \fn static void deuligne__write_command(uint8_t command)
650
 *
651
 * \brief Write a command on LCD port.
652
 *
653
 * \param command   Command to write.
654
 ******************************************************************************/
655
static
656
void
657
deuligne__write_command
658
(
659
    uint8_t command
660
){
661
    mcp23008__set_pin_level(DEULIGNE__GPIO_RS, MCP23008__LEVEL__LOW);
662
    deuligne__write_byte(command);
663
}
664
 
665
/**************************************************************************//**
666
 * \fn static void deuligne__write_byte(uint8_t data)
667
 *
668
 * \brief Write a byte on LCD port.
669
 *
670
 * \param data  Byte to write.
671
 ******************************************************************************/
672
static
673
void
674
deuligne__write_byte
675
(
676
    uint8_t data
677
){
678
    // Get current port value.
679
    uint8_t port_value = mcp23008__get_port_value() & 0xF0;
680
 
681
    // Write high nibble.
682
    port_value |= data >> 4;
683
    mcp23008__set_port_value(port_value);
684
    deuligne__enable_data();
685
    _delay_ms(1);
686
 
687
    // Write low nibble.
688
    port_value &= 0xF0;
689
    port_value |= data & 0x0F;
690
    mcp23008__set_port_value(port_value);
691
    deuligne__enable_data();
692
    _delay_ms(1);
693
}
694
 
695
/**************************************************************************//**
696
 * \fn static void deuligne__write_high_nibble(uint8_t data)
697
 *
698
 * \brief Write the high nibble of a byte on LCD port.
699
 *
700
 * \param data  Byte which write high nibble.
701
 ******************************************************************************/
702
static
703
void
704
deuligne__write_high_nibble
705
(
706
    uint8_t data
707
){
708
    // Get current port value.
709
    uint8_t port_value = mcp23008__get_port_value() & 0xF0;
710
 
711
    // Write high nibble.
712
    port_value |= data >> 4;
713
    mcp23008__set_port_value(port_value);
714
    deuligne__enable_data();
715
    _delay_ms(1);
716
}
717
 
718
/**************************************************************************//**
719
 * \fn static void deuligne__enable_data(void)
720
 *
721
 * \brief Enable data on LCD port.
722
 ******************************************************************************/
723
static
724
void
725
deuligne__enable_data
726
(
727
    void
728
){
729
    mcp23008__set_pin_level(DEULIGNE__GPIO_E, MCP23008__LEVEL__HIGH);
730
    _delay_ms(1);
731
    mcp23008__set_pin_level(DEULIGNE__GPIO_E, MCP23008__LEVEL__LOW);
732
    _delay_ms(1);
733
}