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; either version 2 of the License, or
7 * (at your option) any later version.
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.
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.
20 #include <ao_lcd_stm.h>
26 struct ao_lcd_segment {
37 static struct stm_gpio *gpios[] = {
45 static inline int ao_lcd_stm_seg_enabled(int seg) {
47 return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1;
49 return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1;
52 static inline int ao_lcd_stm_com_enabled(int com) {
53 return (AO_LCD_STM_COM_ENABLED >> com) & 1;
56 #define AO_LCD_STM_GPIOA_SEGS_0 ( \
64 #define AO_LCD_STM_GPIOA_SEGS_1 0
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)))
70 #define AO_LCD_STM_GPIOB_SEGS_0 ( \
84 #define AO_LCD_STM_GPIOB_SEGS_1 0
88 #define AO_LCD_STM_GPIOC_28_SEGS ( \
93 #define AO_LCD_STM_GPIOD_28_SEGS ( \
97 #define AO_LCD_STM_GPIOC_28_SEGS 0
99 #define AO_LCD_STM_GPIOD_28_SEGS ( \
106 #define AO_LCD_STM_GPIOC_SEGS_0 ( \
117 AO_LCD_STM_GPIOC_28_SEGS)
119 #define AO_LCD_STM_GPIOC_SEGS_1 ( \
124 #define AO_LCD_STM_GPIOD_SEGS_0 ( \
125 AO_LCD_STM_GPIOD_28_SEGS)
127 #define AO_LCD_STM_GPIOD_SEGS_1 ( \
134 #define AO_LCD_STM_GPIOE_SEGS_0 0
136 #define AO_LCD_STM_GPIOE_SEGS_1 ( \
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)))
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)))
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)))
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)))
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)))
160 static const struct ao_lcd_segment segs[] = {
223 static const struct ao_lcd_segment coms[] = {
233 #define NSEG (sizeof segs/sizeof segs[0])
234 #define NCOM (sizeof coms/sizeof coms[0])
236 static uint8_t ao_lcd_update_active;
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);
252 ao_lcd_stm_fcr_sync(void)
254 while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0)
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();
274 for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++)
279 #ifdef AO_SEGMENT_MAP
281 static const struct ao_lcd_map {
283 } ao_lcd_map[AO_LCD_DIGITS * AO_LCD_SEGMENTS] = AO_SEGMENT_MAP;
285 static const uint8_t ao_lcd_map[] = AO_SEGMENT_MAP;
290 ao_lcd_set(uint8_t digit, uint8_t segment, uint8_t value)
295 #ifdef AO_SEGMENT_MAP
297 n = digit * AO_LCD_SEGMENTS + segment;
298 com = ao_lcd_map[n].com;
299 seg = ao_lcd_map[n].seg;
302 seg = ao_lcd_map[segment];
314 printf ("digit %d segment %d -> com %d seg %d\n", digit, segment, com, seg);
318 stm_lcd.ram[com * 2 + n] |= (1 << (seg & 0x1f));
320 stm_lcd.ram[com * 2 + n] &= ~(1 << (seg & 0x1f));
325 ao_lcd_stm_seg_set(void)
332 seg = ao_cmd_lex_u32;
335 printf ("com: %d seg: %d val: %d\n", com, seg, val);
336 ao_lcd_set(com, seg, val);
340 static const struct ao_cmds ao_lcd_stm_cmds[] = {
341 { ao_lcd_stm_seg_set, "s <com> <seg> <value>\0Set LCD segment" },
342 { ao_lcd_clear, "x\0Clear LCD" },
348 ao_lcd_stm_init(void)
353 stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
354 (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
355 (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
356 (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
357 (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
359 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
361 /* Turn on the LSI clock */
362 if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
363 stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
364 while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
368 /* Enable RTC clock config (required to change the RTC/LCD clock */
370 stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
372 /* Configure the LCDCLK - use the LSI clock */
375 csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
376 csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
379 for (s = 0; s < NSEG; s++) {
380 uint8_t reg = segs[s].reg;
381 uint8_t bit = segs[s].bit;
383 if (ao_lcd_stm_seg_enabled(s)) {
384 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
385 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
389 for (c = 0; c < NCOM; c++) {
390 uint8_t reg = coms[c].reg;
391 uint8_t bit = coms[c].bit;
393 if (ao_lcd_stm_com_enabled(c)) {
394 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
395 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
399 /* Disable the LCD */
402 /* duty cycle 1/3, radio 352, frame rate about 33Hz */
403 stm_lcd.fcr = ((STM_LCD_FCR_PS_8 << STM_LCD_FCR_PS) |
404 (STM_LCD_FCR_DIV_20 << STM_LCD_FCR_DIV) |
405 (7 << STM_LCD_FCR_CC) |
406 (0 << STM_LCD_FCR_DEAD) |
407 (1 << STM_LCD_FCR_PON) |
408 (1 << STM_LCD_FCR_UDDIE) |
409 (0 << STM_LCD_FCR_SOFIE) |
410 (1 << STM_LCD_FCR_HD));
412 ao_lcd_stm_fcr_sync();
414 /* Program desired DUTY in LCD_CR */
415 /* Program desired BIAS in LCD_CR */
417 /* Internal voltage source */
418 stm_lcd.cr = ((AO_LCD_DUTY << STM_LCD_CR_DUTY) |
419 (STM_LCD_CR_BIAS_1_2 << STM_LCD_CR_BIAS) |
420 (0 << STM_LCD_CR_VSEL) |
421 (0 << STM_LCD_CR_MUX_SEG));
423 ao_lcd_stm_fcr_sync();
425 /* Enable the display (LCDEN bit in LCD_CR) */
426 stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
428 while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
431 /* Load initial data into LCD_RAM and set the
432 * UDR bit in the LCD_SR register */
434 /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
436 /* Program the contrast (CC bits in LCD_FCR) */
438 /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
440 /* Program the required interrupts */
441 stm_nvic_set_enable(STM_ISR_LCD_POS);
442 stm_nvic_set_priority(STM_ISR_LCD_POS, AO_STM_NVIC_LOW_PRIORITY);
446 ao_cmd_register(ao_lcd_stm_cmds);