altosdroid: initial attempt at a UI.
[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
272 const struct ao_cmds ao_lcd_stm_cmds[] = {
273         { ao_lcd_stm_seg_set,   "s <com> <seg> <value>\0Set LCD segment" },
274         { ao_lcd_stm_clear,     "C\0Clear LCD" },
275         { 0, NULL },
276 };
277
278 void
279 ao_lcd_stm_init(void)
280 {
281         int s, c;
282         int r;
283         uint32_t        csr;
284
285         stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
286                            (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
287                            (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
288                            (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
289                            (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
290
291         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
292
293         /* Turn on the LSI clock */
294         if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
295                 stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
296                 while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
297                         asm("nop");
298         }
299
300         /* Enable RTC clock config (required to change the RTC/LCD clock */
301
302         stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
303
304         /* Configure the LCDCLK - use the LSI clock */
305
306         csr = stm_rcc.csr;
307         csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
308         csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
309         stm_rcc.csr = csr;
310
311         for (s = 0; s < NSEG; s++) {
312                 uint8_t reg = segs[s].reg;
313                 uint8_t bit = segs[s].bit;
314                         
315                 if (ao_lcd_stm_seg_enabled(s)) {
316                         stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
317                         stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
318                 }
319         }
320
321         for (c = 0; c < NCOM; c++) {
322                 uint8_t reg = coms[c].reg;
323                 uint8_t bit = coms[c].bit;
324                         
325                 if (ao_lcd_stm_com_enabled(c)) {
326                         stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
327                         stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
328                 }
329         }
330
331         /* Disable the LCD */
332         stm_lcd.cr = 0;
333
334         /* duty cycle 1/3, radio 352, frame rate about 33Hz */
335         stm_lcd.fcr = ((STM_LCD_FCR_PS_16 << STM_LCD_FCR_PS) |
336                        (STM_LCD_FCR_DIV_20 << STM_LCD_FCR_DIV) |
337                        (4 << STM_LCD_FCR_CC) |
338                        (0 << STM_LCD_FCR_DEAD) |
339                        (4 << STM_LCD_FCR_PON) |
340                        (0 << STM_LCD_FCR_UDDIE) |
341                        (0 << STM_LCD_FCR_SOFIE) |
342                        (0 << STM_LCD_FCR_HD));
343
344         ao_lcd_stm_fcr_sync();
345
346         /* Program desired DUTY in LCD_CR */
347         /* Program desired BIAS in LCD_CR */
348         /* Enable mux seg */
349         /* Internal voltage source */
350         stm_lcd.cr = ((STM_LCD_CR_DUTY_STATIC << STM_LCD_CR_DUTY) |
351                       (STM_LCD_CR_BIAS_1_2 << STM_LCD_CR_BIAS) |
352                       (0 << STM_LCD_CR_VSEL) |
353                       (1 << STM_LCD_CR_MUX_SEG));
354
355         ao_lcd_stm_fcr_sync();
356
357         /* Enable the display (LCDEN bit in LCD_CR) */
358         stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
359
360         while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
361                 asm("nop");
362
363         /* Load initial data into LCD_RAM and set the
364          * UDR bit in the LCD_SR register */
365         for (r = 0; r < NCOM; r++) {
366                 stm_lcd.ram[r*2] = 0;
367                 stm_lcd.ram[r*2 + 1] = 0;
368         }
369
370         stm_lcd.sr = (1 << STM_LCD_SR_UDR);
371
372         /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
373
374         /* Program the contrast (CC bits in LCD_FCR) */
375
376         /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
377
378         /* Program the required interrupts */
379
380         /* All done */
381         ao_cmd_register(ao_lcd_stm_cmds);
382 }