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>
21 struct ao_lcd_segment {
32 static struct stm_gpio *gpios[] = {
40 static inline int ao_lcd_stm_seg_enabled(int seg) {
42 return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1;
44 return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1;
47 static inline int ao_lcd_stm_com_enabled(int com) {
48 return (AO_LCD_STM_COM_ENABLED >> com) & 1;
51 #define AO_LCD_STM_GPIOA_SEGS_0 ( \
59 #define AO_LCD_STM_GPIOA_SEGS_1 0
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)))
65 #define AO_LCD_STM_GPIOB_SEGS_0 ( \
79 #define AO_LCD_STM_GPIOB_SEGS_1 0
83 #define AO_LCD_STM_GPIOC_28_SEGS ( \
88 #define AO_LCD_STM_GPIOD_28_SEGS ( \
92 #define AO_LCD_STM_GPIOC_28_SEGS 0
94 #define AO_LCD_STM_GPIOD_28_SEGS ( \
101 #define AO_LCD_STM_GPIOC_SEGS_0 ( \
112 AO_LCD_STM_GPIOC_28_SEGS)
114 #define AO_LCD_STM_GPIOC_SEGS_1 ( \
119 #define AO_LCD_STM_GPIOD_SEGS_0 ( \
120 AO_LCD_STM_GPIOD_28_SEGS)
122 #define AO_LCD_STM_GPIOD_SEGS_1 ( \
129 #define AO_LCD_STM_GPIOE_SEGS_0 0
131 #define AO_LCD_STM_GPIOE_SEGS_1 ( \
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)))
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)))
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)))
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)))
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)))
155 static const struct ao_lcd_segment segs[] = {
218 static const struct ao_lcd_segment coms[] = {
228 #define NSEG (sizeof segs/sizeof segs[0])
229 #define NCOM (sizeof coms/sizeof coms[0])
231 static uint8_t ao_lcd_update_active;
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);
247 ao_lcd_stm_fcr_sync(void)
249 while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0)
256 ao_arch_block_interrupts();
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 ao_arch_release_interrupts();
269 for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++)
275 ao_lcd_set(uint8_t digit, uint8_t segment, uint8_t value)
284 n = (segment >> 5) & 1;
286 stm_lcd.ram[digit * 2 + n] |= (1 << (segment & 0x1f));
288 stm_lcd.ram[digit * 2 + n] &= ~(1 << (segment & 0x1f));
293 ao_lcd_stm_seg_set(void)
300 seg = ao_cmd_lex_u32;
303 printf ("com: %d seg: %d val: %d\n", com, seg, val);
304 ao_lcd_set(com, seg, val);
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" },
316 ao_lcd_stm_init(void)
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));
328 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
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)
337 /* Enable RTC clock config (required to change the RTC/LCD clock */
339 stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
341 /* Configure the LCDCLK - use the LSI clock */
344 csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
345 csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
348 for (s = 0; s < NSEG; s++) {
349 uint8_t reg = segs[s].reg;
350 uint8_t bit = segs[s].bit;
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);
358 for (c = 0; c < NCOM; c++) {
359 uint8_t reg = coms[c].reg;
360 uint8_t bit = coms[c].bit;
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);
368 /* Disable the LCD */
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));
381 ao_lcd_stm_fcr_sync();
383 /* Program desired DUTY in LCD_CR */
384 /* Program desired BIAS in LCD_CR */
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));
392 ao_lcd_stm_fcr_sync();
394 /* Enable the display (LCDEN bit in LCD_CR) */
395 stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
397 while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
400 /* Load initial data into LCD_RAM and set the
401 * UDR bit in the LCD_SR register */
403 /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
405 /* Program the contrast (CC bits in LCD_FCR) */
407 /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
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);
415 ao_cmd_register(ao_lcd_stm_cmds);