2 * Copyright © 2012 Keith Packard <keithp@keithp.com>
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.
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.
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.
19 #include <ao_lcd_stm.h>
25 struct ao_lcd_segment {
36 static struct stm_gpio *gpios[] = {
44 static inline int ao_lcd_stm_seg_enabled(int seg) {
46 return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1;
48 return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1;
51 static inline int ao_lcd_stm_com_enabled(int com) {
52 return (AO_LCD_STM_COM_ENABLED >> com) & 1;
55 #define AO_LCD_STM_GPIOA_SEGS_0 ( \
63 #define AO_LCD_STM_GPIOA_SEGS_1 0
65 #define AO_LCD_STM_USES_GPIOA (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
66 (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
69 #define AO_LCD_STM_GPIOB_SEGS_0 ( \
83 #define AO_LCD_STM_GPIOB_SEGS_1 0
87 #define AO_LCD_STM_GPIOC_28_SEGS ( \
92 #define AO_LCD_STM_GPIOD_28_SEGS ( \
96 #define AO_LCD_STM_GPIOC_28_SEGS 0
98 #define AO_LCD_STM_GPIOD_28_SEGS ( \
105 #define AO_LCD_STM_GPIOC_SEGS_0 ( \
116 AO_LCD_STM_GPIOC_28_SEGS)
118 #define AO_LCD_STM_GPIOC_SEGS_1 ( \
123 #define AO_LCD_STM_GPIOD_SEGS_0 ( \
124 AO_LCD_STM_GPIOD_28_SEGS)
126 #define AO_LCD_STM_GPIOD_SEGS_1 ( \
133 #define AO_LCD_STM_GPIOE_SEGS_0 0
135 #define AO_LCD_STM_GPIOE_SEGS_1 ( \
141 #define AO_LCD_STM_USES_GPIOA (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
142 (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
144 #define AO_LCD_STM_USES_GPIOB (!!((AO_LCD_STM_GPIOB_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
145 (AO_LCD_STM_GPIOB_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
148 #define AO_LCD_STM_USES_GPIOC (!!((AO_LCD_STM_GPIOC_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
149 (AO_LCD_STM_GPIOC_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
152 #define AO_LCD_STM_USES_GPIOD (!!((AO_LCD_STM_GPIOD_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
153 (AO_LCD_STM_GPIOD_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
155 #define AO_LCD_STM_USES_GPIOE (!!((AO_LCD_STM_GPIOE_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
156 (AO_LCD_STM_GPIOE_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
159 static const struct ao_lcd_segment segs[] = {
222 static const struct ao_lcd_segment coms[] = {
232 #define NSEG (sizeof segs/sizeof segs[0])
233 #define NCOM (sizeof coms/sizeof coms[0])
235 static uint8_t ao_lcd_update_active;
240 if (stm_lcd.sr & (1 << STM_LCD_SR_UDD)) {
241 stm_lcd.clr = (1 << STM_LCD_CLR_UDDC);
242 if (ao_lcd_update_active) {
243 ao_lcd_update_active = 0;
244 ao_wakeup(&ao_lcd_update_active);
251 ao_lcd_stm_fcr_sync(void)
253 while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0)
260 ao_arch_block_interrupts();
261 ao_lcd_update_active = 1;
262 stm_lcd.sr = (1 << STM_LCD_SR_UDR);
263 while (ao_lcd_update_active)
264 ao_sleep(&ao_lcd_update_active);
265 ao_arch_release_interrupts();
273 for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++)
278 #ifdef AO_SEGMENT_MAP
280 static const struct ao_lcd_map {
282 } ao_lcd_map[AO_LCD_DIGITS * AO_LCD_SEGMENTS] = AO_SEGMENT_MAP;
284 static const uint8_t ao_lcd_map[] = AO_SEGMENT_MAP;
289 ao_lcd_set(uint8_t digit, uint8_t segment, uint8_t value)
294 #ifdef AO_SEGMENT_MAP
296 n = digit * AO_LCD_SEGMENTS + segment;
297 com = ao_lcd_map[n].com;
298 seg = ao_lcd_map[n].seg;
301 seg = ao_lcd_map[segment];
313 printf ("digit %d segment %d -> com %d seg %d\n", digit, segment, com, seg);
317 stm_lcd.ram[com * 2 + n] |= (1 << (seg & 0x1f));
319 stm_lcd.ram[com * 2 + n] &= ~(1 << (seg & 0x1f));
324 ao_lcd_stm_seg_set(void)
331 seg = ao_cmd_lex_u32;
334 printf ("com: %d seg: %d val: %d\n", com, seg, val);
335 ao_lcd_set(com, seg, val);
339 static const struct ao_cmds ao_lcd_stm_cmds[] = {
340 { ao_lcd_stm_seg_set, "s <com> <seg> <value>\0Set LCD segment" },
341 { ao_lcd_clear, "x\0Clear LCD" },
347 ao_lcd_stm_init(void)
352 stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
353 (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
354 (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
355 (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
356 (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
358 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
360 /* Turn on the LSI clock */
361 if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
362 stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
363 while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
367 /* Enable RTC clock config (required to change the RTC/LCD clock */
369 stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
371 /* Configure the LCDCLK - use the LSI clock */
374 csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
375 csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
378 for (s = 0; s < NSEG; s++) {
379 uint8_t reg = segs[s].reg;
380 uint8_t bit = segs[s].bit;
382 if (ao_lcd_stm_seg_enabled(s)) {
383 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
384 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
388 for (c = 0; c < NCOM; c++) {
389 uint8_t reg = coms[c].reg;
390 uint8_t bit = coms[c].bit;
392 if (ao_lcd_stm_com_enabled(c)) {
393 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
394 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
398 /* Disable the LCD */
401 /* duty cycle 1/3, radio 352, frame rate about 33Hz */
402 stm_lcd.fcr = ((STM_LCD_FCR_PS_8 << STM_LCD_FCR_PS) |
403 (STM_LCD_FCR_DIV_20 << STM_LCD_FCR_DIV) |
404 (7 << STM_LCD_FCR_CC) |
405 (0 << STM_LCD_FCR_DEAD) |
406 (1 << STM_LCD_FCR_PON) |
407 (1 << STM_LCD_FCR_UDDIE) |
408 (0 << STM_LCD_FCR_SOFIE) |
409 (1 << STM_LCD_FCR_HD));
411 ao_lcd_stm_fcr_sync();
413 /* Program desired DUTY in LCD_CR */
414 /* Program desired BIAS in LCD_CR */
416 /* Internal voltage source */
417 stm_lcd.cr = ((AO_LCD_DUTY << STM_LCD_CR_DUTY) |
418 (STM_LCD_CR_BIAS_1_2 << STM_LCD_CR_BIAS) |
419 (0 << STM_LCD_CR_VSEL) |
420 (0 << STM_LCD_CR_MUX_SEG));
422 ao_lcd_stm_fcr_sync();
424 /* Enable the display (LCDEN bit in LCD_CR) */
425 stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
427 while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
430 /* Load initial data into LCD_RAM and set the
431 * UDR bit in the LCD_SR register */
433 /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
435 /* Program the contrast (CC bits in LCD_FCR) */
437 /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
439 /* Program the required interrupts */
440 stm_nvic_set_enable(STM_ISR_LCD_POS);
441 stm_nvic_set_priority(STM_ISR_LCD_POS, AO_STM_NVIC_LOW_PRIORITY);
445 ao_cmd_register(ao_lcd_stm_cmds);