diff options
Diffstat (limited to 'src/stm/ao_lcd_stm.c')
| -rw-r--r-- | src/stm/ao_lcd_stm.c | 393 | 
1 files changed, 393 insertions, 0 deletions
| diff --git a/src/stm/ao_lcd_stm.c b/src/stm/ao_lcd_stm.c new file mode 100644 index 00000000..2d2fa9b3 --- /dev/null +++ b/src/stm/ao_lcd_stm.c @@ -0,0 +1,393 @@ +/* + * Copyright © 2012 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> + +struct ao_lcd_segment { +	uint8_t	reg; +	uint8_t	bit; +}; + +#define A	0 +#define B	1 +#define C	2 +#define D	3 +#define E	4 + +static struct stm_gpio *gpios[] = { +	&stm_gpioa, +	&stm_gpiob, +	&stm_gpioc, +	&stm_gpiod, +	&stm_gpioe +}; + +static inline int ao_lcd_stm_seg_enabled(int seg) { +	if (seg < 32) +		return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1; +	else +		return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1; +} + +static inline int ao_lcd_stm_com_enabled(int com) { +	return (AO_LCD_STM_COM_ENABLED >> com) & 1; +} + +#define AO_LCD_STM_GPIOA_SEGS_0	(		\ +		(1 << 0) |			\ +		(1 << 1) |			\ +		(1 << 2) |			\ +		(1 << 3) |			\ +		(1 << 4) |			\ +		(1 << 17)) + +#define AO_LCD_STM_GPIOA_SEGS_1 0 + +#define AO_LCD_STM_USES_GPIOA	(!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \ +				    (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1))) + + +#define AO_LCD_STM_GPIOB_SEGS_0	(		\ +		(1 << 5) |			\ +		(1 << 6) |			\ +		(1 << 7) |			\ +		(1 << 8) |			\ +		(1 << 9) |			\ +		(1 << 10) |			\ +		(1 << 11) |			\ +		(1 << 12) |			\ +		(1 << 13) |			\ +		(1 << 14) |			\ +		(1 << 15) |			\ +		(1 << 16)) + +#define AO_LCD_STM_GPIOB_SEGS_1 0 + +#if AO_LCD_28_ON_C + +#define AO_LCD_STM_GPIOC_28_SEGS	(	\ +		(1 << 28) |			\ +		(1 << 29) |			\ +		(1 << 30)) + +#define AO_LCD_STM_GPIOD_28_SEGS	(	\ +		(1 << 31)) + +#else +#define AO_LCD_STM_GPIOC_28_C_SEGS	0 + +#define AO_LCD_STM_GPIOD_28_SEGS	(	\ +		(1 << 28) |			\ +		(1 << 29) |			\ +		(1 << 30) |			\ +		(1 << 31)) +#endif + +#define AO_LCD_STM_GPIOC_SEGS_0	(		\ +		(1 << 18) |			\ +		(1 << 19) |			\ +		(1 << 20) |			\ +		(1 << 21) |			\ +		(1 << 22) |			\ +		(1 << 23) |			\ +		(1 << 24) |			\ +		(1 << 25) |			\ +		(1 << 26) |			\ +		(1 << 27) |			\ +		AO_LCD_STM_GPIOC_28_SEGS) + +#define AO_LCD_STM_GPIOC_SEGS_1 (		\ +		(1 << (40 - 32)) |		\ +		(1 << (41 - 32)) |		\ +		(1 << (42 - 32))) + +#define AO_LCD_STM_GPIOD_SEGS_0	(		\ +		AO_LCD_STM_GPIOD_28_SEGS) + +#define AO_LCD_STM_GPIOD_SEGS_1 (		\ +		(1 << (32 - 32)) |		\ +		(1 << (33 - 32)) |		\ +		(1 << (34 - 32)) |		\ +		(1 << (35 - 32)) |		\ +		(1 << (43 - 32))) + +#define AO_LCD_STM_GPIOE_SEGS_0	0 + +#define AO_LCD_STM_GPIOE_SEGS_1 (		\ +		(1 << (36 - 32)) |		\ +		(1 << (37 - 32)) |		\ +		(1 << (38 - 32)) |		\ +		(1 << (39 - 32))) + +#define AO_LCD_STM_USES_GPIOA	(!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \ +				    (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1))) + +#define AO_LCD_STM_USES_GPIOB	(!!((AO_LCD_STM_GPIOB_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \ +				    (AO_LCD_STM_GPIOB_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1))) + + +#define AO_LCD_STM_USES_GPIOC	(!!((AO_LCD_STM_GPIOC_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \ +				    (AO_LCD_STM_GPIOC_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1))) + + +#define AO_LCD_STM_USES_GPIOD	(!!((AO_LCD_STM_GPIOD_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \ +				    (AO_LCD_STM_GPIOD_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1))) + +#define AO_LCD_STM_USES_GPIOE	(!!((AO_LCD_STM_GPIOE_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \ +				    (AO_LCD_STM_GPIOE_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1))) + + +static const struct ao_lcd_segment segs[] = { +	{ A, 1 },	/* 0 */ +	{ A, 2 }, +	{ A, 3 }, +	{ A, 6 }, +		 +	{ A, 7 },	/* 4 */ +	{ B, 0 }, +	{ B, 1 }, +	{ B, 3 }, + +	{ B, 4 },	/* 8 */ +	{ B, 5 }, +	{ B, 10 }, +	{ B, 11 }, + +	{ B, 12 },	/* 12 */ +	{ B, 13 }, +	{ B, 14 }, +	{ B, 15 }, + +	{ B, 8 },	/* 16 */ +	{ A, 15 }, +	{ C, 0 }, +	{ C, 1 }, + +	{ C, 2 },	/* 20 */ +	{ C, 3 }, +	{ C, 4 }, +	{ C, 5 }, + +	{ C, 6 },	/* 24 */ +	{ C, 7 }, +	{ C, 8 }, +	{ C, 9 }, + +#if AO_LCD_28_ON_C +	{ C, 10 },	/* 28 */ +	{ C, 11 }, +	{ C, 12 }, +	{ D, 2 }, +#else +	{ D, 8 },	/* 28 */ +	{ D, 9 }, +	{ D, 10 }, +	{ D, 11 }, +#endif +	{ D, 12 },	/* 32 */ +	{ D, 13 }, +	{ D, 14 }, +	{ D, 15 }, +		 +	{ E, 0 },	/* 36 */ +	{ E, 1 }, +	{ E, 2 }, +	{ E, 3 }, + +	{ C, 10 },	/* 40 */ +	{ C, 11 }, +	{ C, 12 }, +	{ D, 2 }, +}; + +static const struct ao_lcd_segment coms[] = { +	{ A, 8 },	/* 0 */ +	{ A, 9 },	/* 1 */ +	{ A, 10 }, 	/* 2 */ +	{ B, 9 },	/* 3 */ +	{ C, 10 },	/* 4 */ +	{ C, 11 },	/* 5 */ +	{ C, 12 },	/* 6 */ +}; + +#define NSEG	(sizeof segs/sizeof segs[0]) +#define NCOM	(sizeof coms/sizeof coms[0]) + +static void +ao_lcd_stm_fcr_sync(void) +{ +	while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0) +		asm("nop"); +} + +static void +ao_lcd_stm_seg_set(void) +{ +	int	com, seg, val; +	int	n, bit; +	ao_cmd_decimal(); +	com = ao_cmd_lex_i; +	ao_cmd_decimal(); +	seg = ao_cmd_lex_u32; +	ao_cmd_decimal(); +	val = ao_cmd_lex_i; +	printf ("com: %d seg: %d val: %d\n", com, seg, val); +	n = (seg >> 5) & 1; +	if (com >= NCOM) +		com = NCOM-1; +	if (seg >= NSEG) +		seg = NSEG-1; +	if (val) +		stm_lcd.ram[com * 2 + n] |= (1 << (seg & 0x1f)); +	else +		stm_lcd.ram[com * 2 + n] &= ~(1 << (seg & 0x1f)); +	stm_lcd.sr = (1 << STM_LCD_SR_UDR); +} + +static void +ao_lcd_stm_clear(void) +{ +	int	i; + +	for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++) +		stm_lcd.ram[i] = 0; +	stm_lcd.sr = (1 << STM_LCD_SR_UDR); +} + +static void +ao_lcd_stm_text(void) +{ +	char	string[7]; +	uint8_t	c = 0; +	ao_cmd_white(); +	while (ao_cmd_lex_c != '\n' && c < sizeof (string)) { +		string[c++] = ao_cmd_lex_c; +		ao_cmd_lex(); +	} +	string[c++] = '\0'; +	ao_lcd_font_string(string); +	stm_lcd.sr = (1 << STM_LCD_SR_UDR); +} + +const struct ao_cmds ao_lcd_stm_cmds[] = { +	{ ao_lcd_stm_seg_set,	"s <com> <seg> <value>\0Set LCD segment" }, +	{ ao_lcd_stm_clear,	"C\0Clear LCD" }, +	{ ao_lcd_stm_text,	"t <string>\0Write <string> to LCD" }, +	{ 0, NULL }, +}; + +void +ao_lcd_stm_init(void) +{ +	int s, c; +	int r; +	uint32_t	csr; + +	stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) | +			   (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) | +			   (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) | +			   (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) | +			   (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN)); + +	stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN); + +	/* Turn on the LSI clock */ +	if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) { +		stm_rcc.csr |= (1 << STM_RCC_CSR_LSION); +		while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) +			asm("nop"); +	} + +	/* Enable RTC clock config (required to change the RTC/LCD clock */ + +	stm_pwr.cr |= (1 << STM_PWR_CR_DBP); + +	/* Configure the LCDCLK - use the LSI clock */ + +	csr = stm_rcc.csr; +	csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL); +	csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL); +	stm_rcc.csr = csr; + +	for (s = 0; s < NSEG; s++) { +		uint8_t	reg = segs[s].reg; +		uint8_t bit = segs[s].bit; +			 +		if (ao_lcd_stm_seg_enabled(s)) { +			stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE); +			stm_afr_set(gpios[reg], bit, STM_AFR_AF11); +		} +	} + +	for (c = 0; c < NCOM; c++) { +		uint8_t	reg = coms[c].reg; +		uint8_t bit = coms[c].bit; +			 +		if (ao_lcd_stm_com_enabled(c)) { +			stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE); +			stm_afr_set(gpios[reg], bit, STM_AFR_AF11); +		} +	} + +	/* duty cycle 1/3, radio 352, frame rate about 33Hz */ +	stm_lcd.fcr = ((STM_LCD_FCR_PS_1 << STM_LCD_FCR_PS) | +		       (STM_LCD_FCR_DIV_31 << STM_LCD_FCR_DIV) | +		       (4 << STM_LCD_FCR_CC) | +		       (4 << STM_LCD_FCR_PON) | +		       (0 << STM_LCD_FCR_UDDIE) | +		       (0 << STM_LCD_FCR_SOFIE) | +		       (0 << STM_LCD_FCR_HD)); + +	ao_lcd_stm_fcr_sync(); + +	/* Program desired DUTY in LCD_CR */ +	/* Program desired BIAS in LCD_CR */ +	/* Enable mux seg */ +	/* Internal voltage source */ +	stm_lcd.cr = ((STM_LCD_CR_DUTY_1_4 << STM_LCD_CR_DUTY) | +		      (STM_LCD_CR_BIAS_1_3 << STM_LCD_CR_BIAS) | +		      (0 << STM_LCD_CR_VSEL) | +		      (1 << STM_LCD_CR_MUX_SEG)); + +	ao_lcd_stm_fcr_sync(); + +	/* Enable the display (LCDEN bit in LCD_CR) */ +	stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN); + +	while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0) +		asm("nop"); + +	/* Load initial data into LCD_RAM and set the +	 * UDR bit in the LCD_SR register */ +	for (r = 0; r < NCOM; r++) { +		stm_lcd.ram[r*2] = 0; +		stm_lcd.ram[r*2 + 1] = 0; +	} + +	stm_lcd.sr = (1 << STM_LCD_SR_UDR); + +	/* Program desired frame rate (PS and DIV bits in LCD_FCR) */ + +	/* Program the contrast (CC bits in LCD_FCR) */ + +	/* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */ + +	/* Program the required interrupts */ + +	/* All done */ +	ao_cmd_register(ao_lcd_stm_cmds); +} | 
