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.
20 struct ao_lcd_segment {
31 static struct stm_gpio *gpios[] = {
39 static inline int ao_lcd_stm_seg_enabled(int seg) {
41 return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1;
43 return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1;
46 static inline int ao_lcd_stm_com_enabled(int com) {
47 return (AO_LCD_STM_COM_ENABLED >> com) & 1;
50 #define AO_LCD_STM_GPIOA_SEGS_0 ( \
58 #define AO_LCD_STM_GPIOA_SEGS_1 0
60 #define AO_LCD_STM_USES_GPIOA (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
61 (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
64 #define AO_LCD_STM_GPIOB_SEGS_0 ( \
78 #define AO_LCD_STM_GPIOB_SEGS_1 0
82 #define AO_LCD_STM_GPIOC_28_SEGS ( \
87 #define AO_LCD_STM_GPIOD_28_SEGS ( \
91 #define AO_LCD_STM_GPIOC_28_C_SEGS 0
93 #define AO_LCD_STM_GPIOD_28_SEGS ( \
100 #define AO_LCD_STM_GPIOC_SEGS_0 ( \
111 AO_LCD_STM_GPIOC_28_SEGS)
113 #define AO_LCD_STM_GPIOC_SEGS_1 ( \
118 #define AO_LCD_STM_GPIOD_SEGS_0 ( \
119 AO_LCD_STM_GPIOD_28_SEGS)
121 #define AO_LCD_STM_GPIOD_SEGS_1 ( \
128 #define AO_LCD_STM_GPIOE_SEGS_0 0
130 #define AO_LCD_STM_GPIOE_SEGS_1 ( \
136 #define AO_LCD_STM_USES_GPIOA (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
137 (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
139 #define AO_LCD_STM_USES_GPIOB (!!((AO_LCD_STM_GPIOB_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
140 (AO_LCD_STM_GPIOB_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
143 #define AO_LCD_STM_USES_GPIOC (!!((AO_LCD_STM_GPIOC_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
144 (AO_LCD_STM_GPIOC_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
147 #define AO_LCD_STM_USES_GPIOD (!!((AO_LCD_STM_GPIOD_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
148 (AO_LCD_STM_GPIOD_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
150 #define AO_LCD_STM_USES_GPIOE (!!((AO_LCD_STM_GPIOE_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
151 (AO_LCD_STM_GPIOE_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
154 static const struct ao_lcd_segment segs[] = {
217 static const struct ao_lcd_segment coms[] = {
227 #define NSEG (sizeof segs/sizeof segs[0])
228 #define NCOM (sizeof coms/sizeof coms[0])
231 ao_lcd_stm_fcr_sync(void)
233 while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0)
238 ao_lcd_stm_seg_set(void)
245 seg = ao_cmd_lex_u32;
248 printf ("com: %d seg: %d val: %d\n", com, seg, val);
255 stm_lcd.ram[com * 2 + n] |= (1 << (seg & 0x1f));
257 stm_lcd.ram[com * 2 + n] &= ~(1 << (seg & 0x1f));
258 stm_lcd.sr = (1 << STM_LCD_SR_UDR);
262 ao_lcd_stm_clear(void)
266 for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++)
268 stm_lcd.sr = (1 << STM_LCD_SR_UDR);
272 ao_lcd_stm_text(void)
277 while (ao_cmd_lex_c != '\n' && c < sizeof (string)) {
278 string[c++] = ao_cmd_lex_c;
282 ao_lcd_font_string(string);
283 stm_lcd.sr = (1 << STM_LCD_SR_UDR);
286 const struct ao_cmds ao_lcd_stm_cmds[] = {
287 { ao_lcd_stm_seg_set, "s <com> <seg> <value>\0Set LCD segment" },
288 { ao_lcd_stm_clear, "C\0Clear LCD" },
289 { ao_lcd_stm_text, "t <string>\0Write <string> to LCD" },
294 ao_lcd_stm_init(void)
300 stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
301 (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
302 (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
303 (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
304 (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
306 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
308 /* Turn on the LSI clock */
309 if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
310 stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
311 while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
315 /* Enable RTC clock config (required to change the RTC/LCD clock */
317 stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
319 /* Configure the LCDCLK - use the LSI clock */
322 csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
323 csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
326 for (s = 0; s < NSEG; s++) {
327 uint8_t reg = segs[s].reg;
328 uint8_t bit = segs[s].bit;
330 if (ao_lcd_stm_seg_enabled(s)) {
331 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
332 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
336 for (c = 0; c < NCOM; c++) {
337 uint8_t reg = coms[c].reg;
338 uint8_t bit = coms[c].bit;
340 if (ao_lcd_stm_com_enabled(c)) {
341 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
342 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
346 /* duty cycle 1/3, radio 352, frame rate about 33Hz */
347 stm_lcd.fcr = ((STM_LCD_FCR_PS_1 << STM_LCD_FCR_PS) |
348 (STM_LCD_FCR_DIV_31 << STM_LCD_FCR_DIV) |
349 (4 << STM_LCD_FCR_CC) |
350 (4 << STM_LCD_FCR_PON) |
351 (0 << STM_LCD_FCR_UDDIE) |
352 (0 << STM_LCD_FCR_SOFIE) |
353 (0 << STM_LCD_FCR_HD));
355 ao_lcd_stm_fcr_sync();
357 /* Program desired DUTY in LCD_CR */
358 /* Program desired BIAS in LCD_CR */
360 /* Internal voltage source */
361 stm_lcd.cr = ((STM_LCD_CR_DUTY_1_4 << STM_LCD_CR_DUTY) |
362 (STM_LCD_CR_BIAS_1_3 << STM_LCD_CR_BIAS) |
363 (0 << STM_LCD_CR_VSEL) |
364 (1 << STM_LCD_CR_MUX_SEG));
366 ao_lcd_stm_fcr_sync();
368 /* Enable the display (LCDEN bit in LCD_CR) */
369 stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
371 while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
374 /* Load initial data into LCD_RAM and set the
375 * UDR bit in the LCD_SR register */
376 for (r = 0; r < NCOM; r++) {
377 stm_lcd.ram[r*2] = 0;
378 stm_lcd.ram[r*2 + 1] = 0;
381 stm_lcd.sr = (1 << STM_LCD_SR_UDR);
383 /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
385 /* Program the contrast (CC bits in LCD_FCR) */
387 /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
389 /* Program the required interrupts */
392 ao_cmd_register(ao_lcd_stm_cmds);