altos: Split out arch-specific bits of LCD driver
[fw/altos] / src / drivers / ao_lcd.c
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19
20 static uint16_t ao_lcd_time = 3;
21
22 static void
23 ao_lcd_delay(void)
24 {
25         volatile uint16_t       count;
26
27         for (count = 0; count < ao_lcd_time; count++)
28                 ;
29 }
30
31 static void
32 ao_lcd_send_ins(uint8_t ins)
33 {
34 //      printf("send ins %02x\n", ins);
35 //      ao_lcd_wait_idle();
36 //      ao_delay(1);
37         ao_lcd_delay();
38         ao_lcd_port_put_nibble(0, ins >> 4);
39         ao_lcd_port_put_nibble(0, ins & 0xf);
40 }
41
42 void
43 ao_lcd_put_byte(uint8_t c)
44 {
45 //      printf ("send data %02x\n", c);
46 //      ao_lcd_wait_idle();
47         ao_lcd_delay();
48         ao_lcd_port_put_nibble(1, c >> 4);
49         ao_lcd_port_put_nibble(1, c & 0x0f);
50 }
51
52 void
53 ao_lcd_putstring(char *string)
54 {
55         char    c;
56
57         while ((c = (uint8_t) *string++))
58                 ao_lcd_put_byte((uint8_t) c);
59 }
60
61 #define AO_LCD_POWER_CONTROL    0x54
62
63 void
64 ao_lcd_contrast_set(uint8_t contrast)
65 {
66         ao_lcd_send_ins(AO_LCD_POWER_CONTROL | ((contrast >> 4) & 0x3));
67         ao_lcd_send_ins(0x70 | (contrast & 0xf));
68 }
69
70 void
71 ao_lcd_clear(void)
72 {
73         ao_lcd_send_ins(0x01);
74         ao_delay(1);
75         /* Entry mode */
76         ao_lcd_send_ins(0x04 | 0x02);
77 }
78
79 void
80 ao_lcd_goto(uint8_t addr)
81 {
82         ao_lcd_send_ins(0x80 | addr);
83         ao_lcd_send_ins(0x04 | 0x02);
84 }
85
86 void
87 ao_lcd_start(void)
88 {
89         /* get to 4bit mode */
90         ao_lcd_port_put_nibble(0, 0x3);
91         ao_lcd_port_put_nibble(0, 0x3);
92         ao_lcd_port_put_nibble(0, 0x3);
93         ao_lcd_port_put_nibble(0, 0x2);
94
95         /* function set */
96         ao_lcd_send_ins(0x28);
97         /* function set, instruction table 1 */
98         ao_lcd_send_ins(0x29);
99
100         /* freq set */
101         ao_lcd_send_ins(0x14);
102
103         /* Power/icon/contrast control*/
104         ao_lcd_send_ins(AO_LCD_POWER_CONTROL);
105
106         /* Follower control */
107         ao_lcd_send_ins(0x6d);
108         ao_delay(AO_MS_TO_TICKS(200));
109
110         /* contrast set */
111         ao_lcd_contrast_set(0x18);
112
113         /* Display on */
114         ao_lcd_send_ins(0x08 | 0x04);
115
116         /* Clear */
117         ao_lcd_clear();
118 }
119
120 void
121 ao_lcd_contrast(void)
122 {
123         ao_cmd_hex();
124         if (ao_cmd_status == ao_cmd_success) {
125                 printf("setting contrast to %02x\n", ao_cmd_lex_i);
126                 ao_lcd_contrast_set(ao_cmd_lex_i & 0x3f);
127         }
128 }
129
130 static uint8_t
131 ao_cmd_hex_nibble(void)
132 {
133         if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9')
134                 return ao_cmd_lex_c - '0';
135         if ('a' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'f')
136                 return ao_cmd_lex_c - ('a' - 10);
137         if ('A' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'F')
138                 return ao_cmd_lex_c - ('A' - 10);
139         ao_cmd_status = ao_cmd_syntax_error;
140         return 0;
141 }
142
143 void
144 ao_lcd_string(void)
145 {
146         uint8_t col = 0;
147         char    c;
148
149         ao_cmd_decimal();
150         if (ao_cmd_status != ao_cmd_success)
151                 return;
152         ao_lcd_send_ins(0x80 | (ao_cmd_lex_i ? 0x40 : 0x00));
153         ao_cmd_white();
154         while (ao_cmd_lex_c != '\n') {
155                 c = ao_cmd_lex_c;
156                 if (c == '\\') {
157                         ao_cmd_lex();
158                         c = ao_cmd_hex_nibble() << 4;
159                         ao_cmd_lex();
160                         c |= ao_cmd_hex_nibble();
161                 }
162                 ao_lcd_put_byte(c);
163                 ao_cmd_lex();
164                 col++;
165         }
166         while (col < 16) {
167                 ao_lcd_put_byte(' ');
168                 col++;
169         }
170 }
171
172 void
173 ao_lcd_delay_set(void)
174 {
175         ao_cmd_decimal();
176         if (ao_cmd_status == ao_cmd_success) {
177                 printf("setting LCD delay to %d\n", ao_cmd_lex_i);
178                 ao_lcd_time = ao_cmd_lex_i;
179         }
180 }
181
182 __code struct ao_cmds ao_lcd_cmds[] = {
183         { ao_lcd_start, "S\0Start LCD" },
184         { ao_lcd_contrast, "C <contrast>\0Set LCD contrast" },
185         { ao_lcd_string, "s <line> <string>\0Send string to LCD" },
186         { ao_lcd_delay_set, "t <delay>\0Set LCD delay" },
187         { 0, NULL },
188 };
189
190 void
191 ao_lcd_init(void)
192 {
193         ao_lcd_port_init();
194         ao_cmd_register(&ao_lcd_cmds[0]);
195 }