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