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 const struct ao_cmds ao_lcd_stm_cmds[] = {
273 { ao_lcd_stm_seg_set, "s <com> <seg> <value>\0Set LCD segment" },
274 { ao_lcd_stm_clear, "C\0Clear LCD" },
279 ao_lcd_stm_init(void)
285 stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
286 (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
287 (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
288 (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
289 (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
291 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
293 /* Turn on the LSI clock */
294 if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
295 stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
296 while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
300 /* Enable RTC clock config (required to change the RTC/LCD clock */
302 stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
304 /* Configure the LCDCLK - use the LSI clock */
307 csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
308 csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
311 for (s = 0; s < NSEG; s++) {
312 uint8_t reg = segs[s].reg;
313 uint8_t bit = segs[s].bit;
315 if (ao_lcd_stm_seg_enabled(s)) {
316 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
317 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
321 for (c = 0; c < NCOM; c++) {
322 uint8_t reg = coms[c].reg;
323 uint8_t bit = coms[c].bit;
325 if (ao_lcd_stm_com_enabled(c)) {
326 stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
327 stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
331 /* duty cycle 1/3, radio 352, frame rate about 33Hz */
332 stm_lcd.fcr = ((STM_LCD_FCR_PS_1 << STM_LCD_FCR_PS) |
333 (STM_LCD_FCR_DIV_31 << STM_LCD_FCR_DIV) |
334 (4 << STM_LCD_FCR_CC) |
335 (4 << STM_LCD_FCR_PON) |
336 (0 << STM_LCD_FCR_UDDIE) |
337 (0 << STM_LCD_FCR_SOFIE) |
338 (0 << STM_LCD_FCR_HD));
340 ao_lcd_stm_fcr_sync();
342 /* Program desired DUTY in LCD_CR */
343 /* Program desired BIAS in LCD_CR */
345 /* Internal voltage source */
346 stm_lcd.cr = ((STM_LCD_CR_DUTY_1_4 << STM_LCD_CR_DUTY) |
347 (STM_LCD_CR_BIAS_1_3 << STM_LCD_CR_BIAS) |
348 (0 << STM_LCD_CR_VSEL) |
349 (1 << STM_LCD_CR_MUX_SEG));
351 ao_lcd_stm_fcr_sync();
353 /* Enable the display (LCDEN bit in LCD_CR) */
354 stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
356 while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
359 /* Load initial data into LCD_RAM and set the
360 * UDR bit in the LCD_SR register */
361 for (r = 0; r < NCOM; r++) {
362 stm_lcd.ram[r*2] = 0;
363 stm_lcd.ram[r*2 + 1] = 0;
366 stm_lcd.sr = (1 << STM_LCD_SR_UDR);
368 /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
370 /* Program the contrast (CC bits in LCD_FCR) */
372 /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
374 /* Program the required interrupts */
377 ao_cmd_register(ao_lcd_stm_cmds);