2d2fa9b3833177c51dd576d1fc45198edd643e3b
[fw/altos] / src / stm / ao_lcd_stm.c
1 /*
2  * Copyright © 2012 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 struct ao_lcd_segment {
21         uint8_t reg;
22         uint8_t bit;
23 };
24
25 #define A       0
26 #define B       1
27 #define C       2
28 #define D       3
29 #define E       4
30
31 static struct stm_gpio *gpios[] = {
32         &stm_gpioa,
33         &stm_gpiob,
34         &stm_gpioc,
35         &stm_gpiod,
36         &stm_gpioe
37 };
38
39 static inline int ao_lcd_stm_seg_enabled(int seg) {
40         if (seg < 32)
41                 return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1;
42         else
43                 return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1;
44 }
45
46 static inline int ao_lcd_stm_com_enabled(int com) {
47         return (AO_LCD_STM_COM_ENABLED >> com) & 1;
48 }
49
50 #define AO_LCD_STM_GPIOA_SEGS_0 (               \
51                 (1 << 0) |                      \
52                 (1 << 1) |                      \
53                 (1 << 2) |                      \
54                 (1 << 3) |                      \
55                 (1 << 4) |                      \
56                 (1 << 17))
57
58 #define AO_LCD_STM_GPIOA_SEGS_1 0
59
60 #define AO_LCD_STM_USES_GPIOA   (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
61                                     (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
62
63
64 #define AO_LCD_STM_GPIOB_SEGS_0 (               \
65                 (1 << 5) |                      \
66                 (1 << 6) |                      \
67                 (1 << 7) |                      \
68                 (1 << 8) |                      \
69                 (1 << 9) |                      \
70                 (1 << 10) |                     \
71                 (1 << 11) |                     \
72                 (1 << 12) |                     \
73                 (1 << 13) |                     \
74                 (1 << 14) |                     \
75                 (1 << 15) |                     \
76                 (1 << 16))
77
78 #define AO_LCD_STM_GPIOB_SEGS_1 0
79
80 #if AO_LCD_28_ON_C
81
82 #define AO_LCD_STM_GPIOC_28_SEGS        (       \
83                 (1 << 28) |                     \
84                 (1 << 29) |                     \
85                 (1 << 30))
86
87 #define AO_LCD_STM_GPIOD_28_SEGS        (       \
88                 (1 << 31))
89
90 #else
91 #define AO_LCD_STM_GPIOC_28_C_SEGS      0
92
93 #define AO_LCD_STM_GPIOD_28_SEGS        (       \
94                 (1 << 28) |                     \
95                 (1 << 29) |                     \
96                 (1 << 30) |                     \
97                 (1 << 31))
98 #endif
99
100 #define AO_LCD_STM_GPIOC_SEGS_0 (               \
101                 (1 << 18) |                     \
102                 (1 << 19) |                     \
103                 (1 << 20) |                     \
104                 (1 << 21) |                     \
105                 (1 << 22) |                     \
106                 (1 << 23) |                     \
107                 (1 << 24) |                     \
108                 (1 << 25) |                     \
109                 (1 << 26) |                     \
110                 (1 << 27) |                     \
111                 AO_LCD_STM_GPIOC_28_SEGS)
112
113 #define AO_LCD_STM_GPIOC_SEGS_1 (               \
114                 (1 << (40 - 32)) |              \
115                 (1 << (41 - 32)) |              \
116                 (1 << (42 - 32)))
117
118 #define AO_LCD_STM_GPIOD_SEGS_0 (               \
119                 AO_LCD_STM_GPIOD_28_SEGS)
120
121 #define AO_LCD_STM_GPIOD_SEGS_1 (               \
122                 (1 << (32 - 32)) |              \
123                 (1 << (33 - 32)) |              \
124                 (1 << (34 - 32)) |              \
125                 (1 << (35 - 32)) |              \
126                 (1 << (43 - 32)))
127
128 #define AO_LCD_STM_GPIOE_SEGS_0 0
129
130 #define AO_LCD_STM_GPIOE_SEGS_1 (               \
131                 (1 << (36 - 32)) |              \
132                 (1 << (37 - 32)) |              \
133                 (1 << (38 - 32)) |              \
134                 (1 << (39 - 32)))
135
136 #define AO_LCD_STM_USES_GPIOA   (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
137                                     (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
138
139 #define AO_LCD_STM_USES_GPIOB   (!!((AO_LCD_STM_GPIOB_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
140                                     (AO_LCD_STM_GPIOB_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
141
142
143 #define AO_LCD_STM_USES_GPIOC   (!!((AO_LCD_STM_GPIOC_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
144                                     (AO_LCD_STM_GPIOC_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
145
146
147 #define AO_LCD_STM_USES_GPIOD   (!!((AO_LCD_STM_GPIOD_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
148                                     (AO_LCD_STM_GPIOD_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
149
150 #define AO_LCD_STM_USES_GPIOE   (!!((AO_LCD_STM_GPIOE_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
151                                     (AO_LCD_STM_GPIOE_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
152
153
154 static const struct ao_lcd_segment segs[] = {
155         { A, 1 },       /* 0 */
156         { A, 2 },
157         { A, 3 },
158         { A, 6 },
159                 
160         { A, 7 },       /* 4 */
161         { B, 0 },
162         { B, 1 },
163         { B, 3 },
164
165         { B, 4 },       /* 8 */
166         { B, 5 },
167         { B, 10 },
168         { B, 11 },
169
170         { B, 12 },      /* 12 */
171         { B, 13 },
172         { B, 14 },
173         { B, 15 },
174
175         { B, 8 },       /* 16 */
176         { A, 15 },
177         { C, 0 },
178         { C, 1 },
179
180         { C, 2 },       /* 20 */
181         { C, 3 },
182         { C, 4 },
183         { C, 5 },
184
185         { C, 6 },       /* 24 */
186         { C, 7 },
187         { C, 8 },
188         { C, 9 },
189
190 #if AO_LCD_28_ON_C
191         { C, 10 },      /* 28 */
192         { C, 11 },
193         { C, 12 },
194         { D, 2 },
195 #else
196         { D, 8 },       /* 28 */
197         { D, 9 },
198         { D, 10 },
199         { D, 11 },
200 #endif
201         { D, 12 },      /* 32 */
202         { D, 13 },
203         { D, 14 },
204         { D, 15 },
205                 
206         { E, 0 },       /* 36 */
207         { E, 1 },
208         { E, 2 },
209         { E, 3 },
210
211         { C, 10 },      /* 40 */
212         { C, 11 },
213         { C, 12 },
214         { D, 2 },
215 };
216
217 static const struct ao_lcd_segment coms[] = {
218         { A, 8 },       /* 0 */
219         { A, 9 },       /* 1 */
220         { A, 10 },      /* 2 */
221         { B, 9 },       /* 3 */
222         { C, 10 },      /* 4 */
223         { C, 11 },      /* 5 */
224         { C, 12 },      /* 6 */
225 };
226
227 #define NSEG    (sizeof segs/sizeof segs[0])
228 #define NCOM    (sizeof coms/sizeof coms[0])
229
230 static void
231 ao_lcd_stm_fcr_sync(void)
232 {
233         while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0)
234                 asm("nop");
235 }
236
237 static void
238 ao_lcd_stm_seg_set(void)
239 {
240         int     com, seg, val;
241         int     n, bit;
242         ao_cmd_decimal();
243         com = ao_cmd_lex_i;
244         ao_cmd_decimal();
245         seg = ao_cmd_lex_u32;
246         ao_cmd_decimal();
247         val = ao_cmd_lex_i;
248         printf ("com: %d seg: %d val: %d\n", com, seg, val);
249         n = (seg >> 5) & 1;
250         if (com >= NCOM)
251                 com = NCOM-1;
252         if (seg >= NSEG)
253                 seg = NSEG-1;
254         if (val)
255                 stm_lcd.ram[com * 2 + n] |= (1 << (seg & 0x1f));
256         else
257                 stm_lcd.ram[com * 2 + n] &= ~(1 << (seg & 0x1f));
258         stm_lcd.sr = (1 << STM_LCD_SR_UDR);
259 }
260
261 static void
262 ao_lcd_stm_clear(void)
263 {
264         int     i;
265
266         for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++)
267                 stm_lcd.ram[i] = 0;
268         stm_lcd.sr = (1 << STM_LCD_SR_UDR);
269 }
270
271 static void
272 ao_lcd_stm_text(void)
273 {
274         char    string[7];
275         uint8_t c = 0;
276         ao_cmd_white();
277         while (ao_cmd_lex_c != '\n' && c < sizeof (string)) {
278                 string[c++] = ao_cmd_lex_c;
279                 ao_cmd_lex();
280         }
281         string[c++] = '\0';
282         ao_lcd_font_string(string);
283         stm_lcd.sr = (1 << STM_LCD_SR_UDR);
284 }
285
286 const struct ao_cmds ao_lcd_stm_cmds[] = {
287         { ao_lcd_stm_seg_set,   "s <com> <seg> <value>\0Set LCD segment" },
288         { ao_lcd_stm_clear,     "C\0Clear LCD" },
289         { ao_lcd_stm_text,      "t <string>\0Write <string> to LCD" },
290         { 0, NULL },
291 };
292
293 void
294 ao_lcd_stm_init(void)
295 {
296         int s, c;
297         int r;
298         uint32_t        csr;
299
300         stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
301                            (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
302                            (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
303                            (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
304                            (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
305
306         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
307
308         /* Turn on the LSI clock */
309         if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
310                 stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
311                 while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
312                         asm("nop");
313         }
314
315         /* Enable RTC clock config (required to change the RTC/LCD clock */
316
317         stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
318
319         /* Configure the LCDCLK - use the LSI clock */
320
321         csr = stm_rcc.csr;
322         csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
323         csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
324         stm_rcc.csr = csr;
325
326         for (s = 0; s < NSEG; s++) {
327                 uint8_t reg = segs[s].reg;
328                 uint8_t bit = segs[s].bit;
329                         
330                 if (ao_lcd_stm_seg_enabled(s)) {
331                         stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
332                         stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
333                 }
334         }
335
336         for (c = 0; c < NCOM; c++) {
337                 uint8_t reg = coms[c].reg;
338                 uint8_t bit = coms[c].bit;
339                         
340                 if (ao_lcd_stm_com_enabled(c)) {
341                         stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
342                         stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
343                 }
344         }
345
346         /* duty cycle 1/3, radio 352, frame rate about 33Hz */
347         stm_lcd.fcr = ((STM_LCD_FCR_PS_1 << STM_LCD_FCR_PS) |
348                        (STM_LCD_FCR_DIV_31 << STM_LCD_FCR_DIV) |
349                        (4 << STM_LCD_FCR_CC) |
350                        (4 << STM_LCD_FCR_PON) |
351                        (0 << STM_LCD_FCR_UDDIE) |
352                        (0 << STM_LCD_FCR_SOFIE) |
353                        (0 << STM_LCD_FCR_HD));
354
355         ao_lcd_stm_fcr_sync();
356
357         /* Program desired DUTY in LCD_CR */
358         /* Program desired BIAS in LCD_CR */
359         /* Enable mux seg */
360         /* Internal voltage source */
361         stm_lcd.cr = ((STM_LCD_CR_DUTY_1_4 << STM_LCD_CR_DUTY) |
362                       (STM_LCD_CR_BIAS_1_3 << STM_LCD_CR_BIAS) |
363                       (0 << STM_LCD_CR_VSEL) |
364                       (1 << STM_LCD_CR_MUX_SEG));
365
366         ao_lcd_stm_fcr_sync();
367
368         /* Enable the display (LCDEN bit in LCD_CR) */
369         stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
370
371         while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
372                 asm("nop");
373
374         /* Load initial data into LCD_RAM and set the
375          * UDR bit in the LCD_SR register */
376         for (r = 0; r < NCOM; r++) {
377                 stm_lcd.ram[r*2] = 0;
378                 stm_lcd.ram[r*2 + 1] = 0;
379         }
380
381         stm_lcd.sr = (1 << STM_LCD_SR_UDR);
382
383         /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
384
385         /* Program the contrast (CC bits in LCD_FCR) */
386
387         /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
388
389         /* Program the required interrupts */
390
391         /* All done */
392         ao_cmd_register(ao_lcd_stm_cmds);
393 }