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)
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);
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" },
345 ao_lcd_stm_init(void)
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));
356 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
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)
365 /* Enable RTC clock config (required to change the RTC/LCD clock */
367 stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
369 /* Configure the LCDCLK - use the LSI clock */
372 csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
373 csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
376 for (s = 0; s < NSEG; s++) {
377 uint8_t reg = segs[s].reg;
378 uint8_t bit = segs[s].bit;
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);
386 for (c = 0; c < NCOM; c++) {
387 uint8_t reg = coms[c].reg;
388 uint8_t bit = coms[c].bit;
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);
396 /* Disable the LCD */
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));
409 ao_lcd_stm_fcr_sync();
411 /* Program desired DUTY in LCD_CR */
412 /* Program desired BIAS in LCD_CR */
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));
420 ao_lcd_stm_fcr_sync();
422 /* Enable the display (LCDEN bit in LCD_CR) */
423 stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
425 while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
428 /* Load initial data into LCD_RAM and set the
429 * UDR bit in the LCD_SR register */
431 /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
433 /* Program the contrast (CC bits in LCD_FCR) */
435 /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
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);
443 ao_cmd_register(ao_lcd_stm_cmds);