altos: Make cmd number parsing functions return value
[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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao.h>
20 #include <ao_lcd_stm.h>
21
22 #ifndef LCD_DEBUG
23 #define LCD_DEBUG       0
24 #endif
25
26 struct ao_lcd_segment {
27         uint8_t reg;
28         uint8_t bit;
29 };
30
31 #define A       0
32 #define B       1
33 #define C       2
34 #define D       3
35 #define E       4
36
37 static struct stm_gpio *gpios[] = {
38         &stm_gpioa,
39         &stm_gpiob,
40         &stm_gpioc,
41         &stm_gpiod,
42         &stm_gpioe
43 };
44
45 static inline int ao_lcd_stm_seg_enabled(int seg) {
46         if (seg < 32)
47                 return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1;
48         else
49                 return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1;
50 }
51
52 static inline int ao_lcd_stm_com_enabled(int com) {
53         return (AO_LCD_STM_COM_ENABLED >> com) & 1;
54 }
55
56 #define AO_LCD_STM_GPIOA_SEGS_0 (               \
57                 (1 << 0) |                      \
58                 (1 << 1) |                      \
59                 (1 << 2) |                      \
60                 (1 << 3) |                      \
61                 (1 << 4) |                      \
62                 (1 << 17))
63
64 #define AO_LCD_STM_GPIOA_SEGS_1 0
65
66 #define AO_LCD_STM_USES_GPIOA   (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
67                                     (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
68
69
70 #define AO_LCD_STM_GPIOB_SEGS_0 (               \
71                 (1 << 5) |                      \
72                 (1 << 6) |                      \
73                 (1 << 7) |                      \
74                 (1 << 8) |                      \
75                 (1 << 9) |                      \
76                 (1 << 10) |                     \
77                 (1 << 11) |                     \
78                 (1 << 12) |                     \
79                 (1 << 13) |                     \
80                 (1 << 14) |                     \
81                 (1 << 15) |                     \
82                 (1 << 16))
83
84 #define AO_LCD_STM_GPIOB_SEGS_1 0
85
86 #if AO_LCD_28_ON_C
87
88 #define AO_LCD_STM_GPIOC_28_SEGS        (       \
89                 (1 << 28) |                     \
90                 (1 << 29) |                     \
91                 (1 << 30))
92
93 #define AO_LCD_STM_GPIOD_28_SEGS        (       \
94                 (1 << 31))
95
96 #else
97 #define AO_LCD_STM_GPIOC_28_SEGS        0
98
99 #define AO_LCD_STM_GPIOD_28_SEGS        (       \
100                 (1 << 28) |                     \
101                 (1 << 29) |                     \
102                 (1 << 30) |                     \
103                 (1 << 31))
104 #endif
105
106 #define AO_LCD_STM_GPIOC_SEGS_0 (               \
107                 (1 << 18) |                     \
108                 (1 << 19) |                     \
109                 (1 << 20) |                     \
110                 (1 << 21) |                     \
111                 (1 << 22) |                     \
112                 (1 << 23) |                     \
113                 (1 << 24) |                     \
114                 (1 << 25) |                     \
115                 (1 << 26) |                     \
116                 (1 << 27) |                     \
117                 AO_LCD_STM_GPIOC_28_SEGS)
118
119 #define AO_LCD_STM_GPIOC_SEGS_1 (               \
120                 (1 << (40 - 32)) |              \
121                 (1 << (41 - 32)) |              \
122                 (1 << (42 - 32)))
123
124 #define AO_LCD_STM_GPIOD_SEGS_0 (               \
125                 AO_LCD_STM_GPIOD_28_SEGS)
126
127 #define AO_LCD_STM_GPIOD_SEGS_1 (               \
128                 (1 << (32 - 32)) |              \
129                 (1 << (33 - 32)) |              \
130                 (1 << (34 - 32)) |              \
131                 (1 << (35 - 32)) |              \
132                 (1 << (43 - 32)))
133
134 #define AO_LCD_STM_GPIOE_SEGS_0 0
135
136 #define AO_LCD_STM_GPIOE_SEGS_1 (               \
137                 (1 << (36 - 32)) |              \
138                 (1 << (37 - 32)) |              \
139                 (1 << (38 - 32)) |              \
140                 (1 << (39 - 32)))
141
142 #define AO_LCD_STM_USES_GPIOA   (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
143                                     (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
144
145 #define AO_LCD_STM_USES_GPIOB   (!!((AO_LCD_STM_GPIOB_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
146                                     (AO_LCD_STM_GPIOB_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
147
148
149 #define AO_LCD_STM_USES_GPIOC   (!!((AO_LCD_STM_GPIOC_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
150                                     (AO_LCD_STM_GPIOC_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
151
152
153 #define AO_LCD_STM_USES_GPIOD   (!!((AO_LCD_STM_GPIOD_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
154                                     (AO_LCD_STM_GPIOD_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
155
156 #define AO_LCD_STM_USES_GPIOE   (!!((AO_LCD_STM_GPIOE_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
157                                     (AO_LCD_STM_GPIOE_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
158
159
160 static const struct ao_lcd_segment segs[] = {
161         { A, 1 },       /* 0 */
162         { A, 2 },
163         { A, 3 },
164         { A, 6 },
165                 
166         { A, 7 },       /* 4 */
167         { B, 0 },
168         { B, 1 },
169         { B, 3 },
170
171         { B, 4 },       /* 8 */
172         { B, 5 },
173         { B, 10 },
174         { B, 11 },
175
176         { B, 12 },      /* 12 */
177         { B, 13 },
178         { B, 14 },
179         { B, 15 },
180
181         { B, 8 },       /* 16 */
182         { A, 15 },
183         { C, 0 },
184         { C, 1 },
185
186         { C, 2 },       /* 20 */
187         { C, 3 },
188         { C, 4 },
189         { C, 5 },
190
191         { C, 6 },       /* 24 */
192         { C, 7 },
193         { C, 8 },
194         { C, 9 },
195
196 #if AO_LCD_28_ON_C
197         { C, 10 },      /* 28 */
198         { C, 11 },
199         { C, 12 },
200         { D, 2 },
201 #else
202         { D, 8 },       /* 28 */
203         { D, 9 },
204         { D, 10 },
205         { D, 11 },
206 #endif
207         { D, 12 },      /* 32 */
208         { D, 13 },
209         { D, 14 },
210         { D, 15 },
211                 
212         { E, 0 },       /* 36 */
213         { E, 1 },
214         { E, 2 },
215         { E, 3 },
216
217         { C, 10 },      /* 40 */
218         { C, 11 },
219         { C, 12 },
220         { D, 2 },
221 };
222
223 static const struct ao_lcd_segment coms[] = {
224         { A, 8 },       /* 0 */
225         { A, 9 },       /* 1 */
226         { A, 10 },      /* 2 */
227         { B, 9 },       /* 3 */
228         { C, 10 },      /* 4 */
229         { C, 11 },      /* 5 */
230         { C, 12 },      /* 6 */
231 };
232
233 #define NSEG    (sizeof segs/sizeof segs[0])
234 #define NCOM    (sizeof coms/sizeof coms[0])
235
236 static uint8_t  ao_lcd_update_active;
237
238 void
239 stm_lcd_isr(void)
240 {
241         if (stm_lcd.sr & (1 << STM_LCD_SR_UDD)) {
242                 stm_lcd.clr = (1 << STM_LCD_CLR_UDDC);
243                 if (ao_lcd_update_active) {
244                         ao_lcd_update_active = 0;
245                         ao_wakeup(&ao_lcd_update_active);
246                 }
247         }
248 }
249
250
251 static void
252 ao_lcd_stm_fcr_sync(void)
253 {
254         while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0)
255                 asm("nop");
256 }
257
258 void
259 ao_lcd_flush(void)
260 {
261         ao_arch_block_interrupts();
262         ao_lcd_update_active = 1;
263         stm_lcd.sr = (1 << STM_LCD_SR_UDR);
264         while (ao_lcd_update_active)
265                 ao_sleep(&ao_lcd_update_active);
266         ao_arch_release_interrupts();
267 }
268
269 void
270 ao_lcd_clear(void)
271 {
272         uint8_t i;
273
274         for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++)
275                 stm_lcd.ram[i] = 0;
276         ao_lcd_flush();
277 }
278
279 #ifdef AO_SEGMENT_MAP
280 #if AO_LCD_PER_DIGIT
281 static const struct ao_lcd_map {
282         uint8_t com, seg;
283 } ao_lcd_map[AO_LCD_DIGITS * AO_LCD_SEGMENTS] = AO_SEGMENT_MAP;
284 #else
285 static const uint8_t ao_lcd_map[] = AO_SEGMENT_MAP;
286 #endif
287 #endif
288
289 void
290 ao_lcd_set(uint8_t digit, uint8_t segment, uint8_t value)
291 {
292         uint8_t n;
293         uint8_t com, seg;
294
295 #ifdef AO_SEGMENT_MAP
296 #if AO_LCD_PER_DIGIT
297         n = digit * AO_LCD_SEGMENTS + segment;
298         com = ao_lcd_map[n].com;
299         seg = ao_lcd_map[n].seg;
300 #else
301         com = digit;
302         seg = ao_lcd_map[segment];
303 #endif
304 #else
305         com = digit;
306         seg = segment;
307 #endif
308         if (com >= NCOM)
309                 com = NCOM-1;
310         if (seg >= NSEG)
311                 seg = NSEG-1;
312
313 #if LCD_DEBUG
314         printf ("digit %d segment %d -> com %d seg %d\n", digit, segment, com, seg);
315 #endif
316         n = (seg >> 5) & 1;
317         if (value)
318                 stm_lcd.ram[com * 2 + n] |= (1 << (seg & 0x1f));
319         else
320                 stm_lcd.ram[com * 2 + n] &= ~(1 << (seg & 0x1f));
321 }
322
323 #if LCD_DEBUG
324 static void
325 ao_lcd_stm_seg_set(void)
326 {
327         int     com, seg, val;
328         int     n, bit;
329         com = ao_cmd_decimal();
330         seg = ao_cmd_decimal();
331         val = ao_cmd_decimal();
332         printf ("com: %d seg: %d val: %d\n", com, seg, val);
333         ao_lcd_set(com, seg, val);
334         ao_lcd_flush();
335 }
336
337 static const struct ao_cmds ao_lcd_stm_cmds[] = {
338         { ao_lcd_stm_seg_set,   "s <com> <seg> <value>\0Set LCD segment" },
339         { ao_lcd_clear,         "x\0Clear LCD" },
340         { 0, NULL },
341 };
342 #endif
343
344 void
345 ao_lcd_stm_init(void)
346 {
347         unsigned int s, c;
348         uint32_t        csr;
349
350         stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
351                            (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
352                            (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
353                            (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
354                            (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
355
356         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
357
358         /* Turn on the LSI clock */
359         if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
360                 stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
361                 while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
362                         asm("nop");
363         }
364
365         /* Enable RTC clock config (required to change the RTC/LCD clock */
366
367         stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
368
369         /* Configure the LCDCLK - use the LSI clock */
370
371         csr = stm_rcc.csr;
372         csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
373         csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
374         stm_rcc.csr = csr;
375
376         for (s = 0; s < NSEG; s++) {
377                 uint8_t reg = segs[s].reg;
378                 uint8_t bit = segs[s].bit;
379                         
380                 if (ao_lcd_stm_seg_enabled(s)) {
381                         stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
382                         stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
383                 }
384         }
385
386         for (c = 0; c < NCOM; c++) {
387                 uint8_t reg = coms[c].reg;
388                 uint8_t bit = coms[c].bit;
389                         
390                 if (ao_lcd_stm_com_enabled(c)) {
391                         stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
392                         stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
393                 }
394         }
395
396         /* Disable the LCD */
397         stm_lcd.cr = 0;
398
399         /* duty cycle 1/3, radio 352, frame rate about 33Hz */
400         stm_lcd.fcr = ((STM_LCD_FCR_PS_8 << STM_LCD_FCR_PS) |
401                        (STM_LCD_FCR_DIV_20 << STM_LCD_FCR_DIV) |
402                        (7 << STM_LCD_FCR_CC) |
403                        (0 << STM_LCD_FCR_DEAD) |
404                        (1 << STM_LCD_FCR_PON) |
405                        (1 << STM_LCD_FCR_UDDIE) |
406                        (0 << STM_LCD_FCR_SOFIE) |
407                        (1 << STM_LCD_FCR_HD));
408
409         ao_lcd_stm_fcr_sync();
410
411         /* Program desired DUTY in LCD_CR */
412         /* Program desired BIAS in LCD_CR */
413         /* Enable mux seg */
414         /* Internal voltage source */
415         stm_lcd.cr = ((AO_LCD_DUTY << STM_LCD_CR_DUTY) |
416                       (STM_LCD_CR_BIAS_1_2 << STM_LCD_CR_BIAS) |
417                       (0 << STM_LCD_CR_VSEL) |
418                       (0 << STM_LCD_CR_MUX_SEG));
419
420         ao_lcd_stm_fcr_sync();
421
422         /* Enable the display (LCDEN bit in LCD_CR) */
423         stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
424
425         while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
426                 asm("nop");
427
428         /* Load initial data into LCD_RAM and set the
429          * UDR bit in the LCD_SR register */
430
431         /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
432
433         /* Program the contrast (CC bits in LCD_FCR) */
434
435         /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
436
437         /* Program the required interrupts */
438         stm_nvic_set_enable(STM_ISR_LCD_POS);
439         stm_nvic_set_priority(STM_ISR_LCD_POS, AO_STM_NVIC_LOW_PRIORITY);
440
441         /* All done */
442 #if LCD_DEBUG
443         ao_cmd_register(ao_lcd_stm_cmds);
444 #endif
445 }