diff --git a/ChangeLog b/ChangeLog index 1b9d44224e..598fc6ac6e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5025,3 +5025,6 @@ CONFIG_EXAMPLES_NSH_CXXINITIALIZE=y so that they behave as they did before, i.e., so that C++ initializers will be called when NSH starts up (2013-6-21). + * configs/sam4l-xplained/src/sam_slcd.c: Beginning of a driver for the + LED1 segment LCD module. This driver is incomplete on initial check- + in (2013-6-21). diff --git a/arch/arm/src/sam34/Kconfig b/arch/arm/src/sam34/Kconfig index bcc5ebab9c..79288bfcb0 100644 --- a/arch/arm/src/sam34/Kconfig +++ b/arch/arm/src/sam34/Kconfig @@ -423,15 +423,97 @@ config SAM34_HSMCI endmenu +if ARCH_CHIP_SAM4L +menu "AT91SAM3/4 Clock Configuration" + config SAM32_RESET_PERIPHCLKS bool "Enable all peripheral clocks on reset" default n - depends on ARCH_CHIP_SAM4L ---help--- By default, only a few necessary peripheral clocks are enabled at reset. If this setting is enabled, then all clocking will be enabled to all of the selected peripherals on reset. +config SAM34_OSC0 + bool "External oscillator 0" + default n + ---help--- + Oscillator 0 might be automatically selected for several reasons: + Oscillator 0 might be the system clock or the source clock for + either PLL0 or DFPLL. It might also be needed if OSC0 is the source + clock for GCLK9. By selecting SAM34_OSC0, you can also force the + clock to be enabled at boot time for other uses. + +config SAM34_OSC32K + bool "32.768KHz external oscillator" + default n + ---help--- + The 32K oscillator might be automatically selected for several + reasons: The 32K oscillator may be the source clock for DFPLL0 or + the source clock for GLK9 that might be used to driver PLL0. By + selecting SAM34_OSC32K, you can also force the clock to be enabled + at boot time. OSC32 may needed by other devices as well (AST, WDT, + PICUART, RTC). + +config SAM34_RC80M + bool "80MHz RC oscillator" + default n + ---help--- + The 80MHz RC oscillator might be automatically selected for several + reasons: This might be the system clock or the source clock for the + DFPLL or it could be the source for GCLK9 that drives PLL0. By + selecting SAM34_RC80M, you can also force the clock to be enabled at + boot time for other uses. + +config SAM34_RCFAST + bool "Fast RC oscillator" + default n + ---help--- + The fast RC oscillator might be automatically selected for several + reasons: The 12/8/4 fast RC oscillator may be used as the system + clock or as the source for GLCK9 that drives PLL0. If not then, it + may be enabled by setting the SAM34_RCFASTxM configuration variable. + +if SAM34_RCFAST +choice + prompt "Fast RC Oscillator Speed" + default SAM34_RCFAST8M + +config SAM34_RCFAST12M + bool "12MHz" + +config SAM34_RCFAST8M + bool "8MHz" + +config SAM34_RCFAST4M + bool "4MHz + +endchoice +endif + +config SAM34_RC1M + bool "1MHz RC oscillator" + default n + ---help--- + The 1MHz RC oscillator might be automatically selected for several + reasons: The 1MHz RC oscillator may be used as the system block or + may be the source clock for GLCK9 that drives PLL0. By selecting + SAM34_RC1M, you can also force the clock to be enabled at boot time + for other purposes. + +config SAM34_RC32K + bool "32KHz RC oscillator" + default n + ---help--- + The 32KHz RC oscillator might be automatically selected for several + reasons: The 32KHz RC oscillator may be used as the input to DFLL0 + or as the input to GCLK9 that drives PLL0. By selecting SAM34_RC32K, + you can also force the clock to be enabled at boot time for other + purposes. + +endmenu +endif + comment "AT91SAM3/4 USART Configuration" config USART0_ISUART @@ -459,6 +541,7 @@ config USART3_ISUART select ARCH_HAVE_USART2 comment "AT91SAM3/4 GPIO Interrupt Configuration" + config GPIO_IRQ bool "GPIO pin interrupts" ---help--- diff --git a/arch/arm/src/sam34/chip/sam4l_lcdca.h b/arch/arm/src/sam34/chip/sam4l_lcdca.h index 671f5807d3..7e17ba347e 100644 --- a/arch/arm/src/sam34/chip/sam4l_lcdca.h +++ b/arch/arm/src/sam34/chip/sam4l_lcdca.h @@ -141,14 +141,13 @@ # define LCDCA_CFG_DUTY_1TO3 (3 << LCDCA_CFG_DUTY_SHIFT) /* 1/3, 1/3, COM[0:2] */ #define LCDCA_CFG_FCST_SHIFT (16) /* Bits 16-21: Fine Contrast */ #define LCDCA_CFG_FCST_MASK (63 << LCDCA_CFG_FCST_SHIFT) -# define LCDCA_CFG_FCST(n) (((n) & 63) << LCDCA_CFG_FCST_SHIFT) /* n = -32..31 */ +# define LCDCA_CFG_FCST(n) (((uint32_t)(n) & 63) << LCDCA_CFG_FCST_SHIFT) /* n = -32..31 */ #define LCDCA_CFG_NSU_SHIFT (24) /* Bits 24-29: Number of Segment Terminals in Use */ #define LCDCA_CFG_NSU_MASK (63 << LCDCA_CFG_NSU_SHIFT) # define LCDCA_CFG_NSU(n) ((n) << LCDCA_CFG_NSU_SHIFT) /* n=0-40 */ /* Timing Register */ - #define LCDCA_TIM_PRESC (1 << 0) /* Bit 0: LCD Prescaler Select */ #define LCDCA_TIM_CLKDIV_SHIFT (1) /* Bits 1-3: LCD Clock Division */ #define LCDCA_TIM_CLKDIV_MASK (7 << LCDCA_TIM_CLKDIV_SHIFT) @@ -184,11 +183,13 @@ * memory for segments 0-31). */ +#define LCDCA_DRL_MASK 0xffffffff + /* Data Register High 0-3 (8 bits data, each bit defines a segment value in display * memory for segments 32-39) */ -#define LCDCA_DRH0_MASK 0xff +#define LCDCA_DRH_MASK 0xff /* Indirect Access Data Register */ diff --git a/configs/sam4l-xplained/Kconfig b/configs/sam4l-xplained/Kconfig index d80c38ff87..8de3ca320b 100644 --- a/configs/sam4l-xplained/Kconfig +++ b/configs/sam4l-xplained/Kconfig @@ -7,6 +7,14 @@ if ARCH_BOARD_SAM4L_XPLAINED menu "SAM4L Xplained Pro Modules" +config SAM4L_XPLAINED_SLCD1MODULE + bool "SLCD1 Module" + default n + ---help--- + The SLCD 1 module is attached. This module provides a segment LCD + that connects directly to the "EXT5 SEGMENT LCD" connector. When + the SLCD is connected, EXT2 is not available for other modules. + config SAM4L_XPLAINED_IOMODULE bool "I/O1 Module" default n @@ -15,6 +23,7 @@ config SAM4L_XPLAINED_IOMODULE slot. if SAM4L_XPLAINED_IOMODULE + choice prompt "I/O1 Module Location" default SAM4L_XPLAINED_IOMODULE_EXT1 @@ -24,6 +33,7 @@ config SAM4L_XPLAINED_IOMODULE_EXT1 config SAM4L_XPLAINED_IOMODULE_EXT2 bool "EXT2" + depends on !SAM4L_XPLAINED_SLCD1MODULE endchoice endif @@ -35,12 +45,21 @@ config SAM4L_XPLAINED_OLED1MODULE The OLED 1 module is attached. This module provides an OLED plus 3 additional switches and 3 additional LEDs. -config SAM4L_XPLAINED_SLCD1MODULE - bool "SLCD1 Module" - default n - ---help--- - The SLCD 1 module is attached. This module provides a segment LCD - that connects directly to the "EXT5 SEGMENT LCD" connector +if SAM4L_XPLAINED_OLED1MODULE + +choice + prompt "OLED1 Module Location" + default SAM4L_XPLAINED_OLED1MODULE_EXT1 + +config SAM4L_XPLAINED_OLED1MODULE_EXT1 + bool "EXT1" + +config SAM4L_XPLAINED_OLED1MODULE_EXT2 + bool "EXT2" + depends on !SAM4L_XPLAINED_SLCD1MODULE + +endchoice +endif endmenu endif diff --git a/configs/sam4l-xplained/src/Makefile b/configs/sam4l-xplained/src/Makefile index 3768f7ebb3..b1b3df445a 100644 --- a/configs/sam4l-xplained/src/Makefile +++ b/configs/sam4l-xplained/src/Makefile @@ -42,14 +42,14 @@ AOBJS = $(ASRCS:.S=$(OBJEXT)) CSRCS = sam_boot.c -ifeq ($(CONFIG_SAM34_SPI),y) -CSRCS += sam_spi.c -endif - ifeq ($(CONFIG_HAVE_CXX),y) CSRCS += sam_cxxinitialize.c endif +ifeq ($(CONFIG_SAM34_SPI),y) +CSRCS += sam_spi.c +endif + ifeq ($(CONFIG_ARCH_LEDS),y) CSRCS += sam_autoleds.c else @@ -64,6 +64,10 @@ ifeq ($(CONFIG_NSH_ARCHINIT),y) CSRCS += sam_nsh.c endif +ifeq ($(CONFIG_SAM34_LCDCA),y) +CSRCS += sam_slcd.c +endif + ifeq ($(CONFIG_SAM4L_XPLAINED_IOMODULE),y) CSRCS += sam_mmcsd.c endif diff --git a/configs/sam4l-xplained/src/sam4l-xplained.h b/configs/sam4l-xplained/src/sam4l-xplained.h index 9e7a0f4acb..a9b5498b3a 100644 --- a/configs/sam4l-xplained/src/sam4l-xplained.h +++ b/configs/sam4l-xplained/src/sam4l-xplained.h @@ -103,6 +103,75 @@ GPIO_PORTC | GPIO_PIN24) #define IRQ_SW0 SAM_IRQ_PC24 +/* LCD1 + * + * EXT5 SAM4L BOARD LCD1 SHARED + * PIN PIN FUNCTION FUNCTION WITH + * 1 PA09 COM3 COM3 EXT3 + * 2 PA10 COM2 COM2 EXT3 + * 3 PA11 COM1 COM1 EXT4 + * 4 PA12 COM0 COM0 EXT4 + * 5 PC15 SEG0 SEG0 EXT3 + * 6 PC16 SEG1 SEG1 EXT3 + * 7 PC17 SEG2 SEG2 EXT4 + * 8 PC18 SEG3 SEG3 EXT4 + * 9 PC19 SEG4 SEG4 + * 10 PA13 SEG5 SEG5 EXT4 + * 11 PA14 SEG6 SEG6 + * 12 PA15 SEG7 SEG7 EXT4 + * 13 PA16 SEG8 SEG8 EXT4 + * 14 PA17 SEG9 SEG9 EXT3 + * 15 PC20 SEG10 SEG10 + * 16 PC21 SEG11 SEG11 + * 17 PC22 SEG12 SEG12 + * 18 PC23 SEG13 SEG13 + * 19 PB08 SEG14 SEG14 + * 20 PB09 SEG15 SEG15 + * 21 PB10 SEG16 SEG16 EXT2 + * 22 PB11 SEG17 SEG17 EXT2 + * 23 PA18 SEG18 SEG18 EXT3-4 + * 24 PA19 SEG19 SEG19 EXT3-4 + * 25 PA20 SEG20 SEG20 EXT3-4 + * 26 PB07 SEG21 SEG21 + * 27 PB06 SEG22 SEG22 + * 28 PA08 SEG23 SEG32 EXT3 + * 29 PC24 SEG24 N/C + * 30 PC25 SEG25 N/C EXT1 + * 31 PC26 SEG26 N/C EXT2-3 + * 32 PC27 SEG27 N/C EXT2-3 + * 33 PC28 SEG28 N/C + * 34 PC29 SEG29 N/C + * 35 PC30 SEG30 N/C EXT1-2 + * 36 PC31 SEG31 N/C + * 37 PB12 SEG32 N/C EXT1 + * 38 PB13 SEG33 N/C EXT1 + * 39 PA21 SEG34 N/C EXT1-2 + * 40 PA22 SEG35 N/C EXT1-2 + * 41 PB14 SEG36 N/C EXT2-4 + * 42 PB15 SEG37 N/C EXT2-4 + * 43 PA23 SEG38 N/C EXT1 + * 44 PA24 SEG39 N/C EXT1 + * 45 --- N/C N/C + * 46 --- N/C N/C + * 47 --- VCC_P3V3 BL V+ + * 48 --- GND BL V- + * 49 PC05 BL BL CTRL EXT2 + * 50 --- ID ID + * 51 --- GND GND + * + * The backlight control is active high. + */ + +#ifdef CONFIG_SAM4L_XPLAINED_SLCD1MODULE + +# ifndef CONFIG_SAM34_LCDCA +# error CONFIG_SAM34_LCDCA is required to use the LCD1 module +# endif + +# define GPIO_LCD1_BL (GPIO_OUTPUT | GPIO_PULL_NONE | GPIO_OUTPUT_CLEAR | \ + GPIO_PORTC | GPIO_PIN5) +#endif + /* I/O1 * * Support for the microSD card slot on the I/O1 module. The I/O1 requires @@ -125,6 +194,10 @@ # if defined(CONFIG_SAM4L_XPLAINED_IOMODULE_EXT1) +# if defined(SAM4L_XPLAINED_OLED1MODULE) && defined(SAM4L_XPLAINED_OLED1MODULE_EXT1) +# error I/O1 and OLED1 cannot both reside in EXT1 +# endif + # define GPIO_SD_CD (GPIO_INTERRUPT | GPIO_INT_CHANGE | GPIO_PULL_UP | \ GPIO_GLITCH_FILTER | GPIO_PORTB | GPIO_PIN13) # define IRQ_SD_CD SAM_IRQ_PB13 @@ -134,6 +207,15 @@ # define SD_CSNO 0 # elif defined(CONFIG_SAM4L_XPLAINED_IOMODULE_EXT2) + +# ifndef CONFIG_SAM4L_XPLAINED_SLCD1MODULE +# error I/O1 cannot be in EXT2 if the LCD1 module is connected +# endif + +# if defined(SAM4L_XPLAINED_OLED1MODULE) && defined(SAM4L_XPLAINED_OLED1MODULE_EXT2) +# error I/O1 and OLED1 cannot both reside in EXT2 +# endif + # define GPIO_CD (GPIO_INTERRUPT | GPIO_INT_CHANGE | GPIO_PULL_UP | \ GPIO_GLITCH_FILTER | GPIO_PORTC | GPIO_PIN9) # define IRQ_CD SAM_IRQ_PC9 diff --git a/configs/sam4l-xplained/src/sam_slcd.c b/configs/sam4l-xplained/src/sam_slcd.c new file mode 100644 index 0000000000..abe06e03a8 --- /dev/null +++ b/configs/sam4l-xplained/src/sam_slcd.c @@ -0,0 +1,1139 @@ +/**************************************************************************** + * configs/sam4l-xlplained/src/sam_slcd.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "up_arch.h" +#include "sam_gpio.h" +#include "sam4l_periphclks.h" +#include "chip/sam_lcdca.h" + +#include "sam4l-xplained.h" + +#ifdef CONFIG_SAM34_LCDCA + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +/* Define CONFIG_DEBUG_LCD to enable detailed LCD debug output. Verbose debug + * must also be enabled. + */ + +#ifndef CONFIG_LIB_SLCDCODEC +# error This SLCD driver requires CONFIG_LIB_SLCDCODEC +#endif + +#if !defined(CONFIG_SAM34_OSC32K) && !defined(CONFIG_SAM34_RC32K) +# error This SLCD driver requires that either CONFIG_SAM34_OSC32K or +# error CONFIG_SAM34_RC32K be selected in the board configuration +#endif + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_VERBOSE +# undef CONFIG_DEBUG_GRAPHICS +# undef CONFIG_DEBUG_LCD +#endif + +#ifndef CONFIG_DEBUG_VERBOSE +# undef CONFIG_DEBUG_LCD +#endif + +/* The ever-present MIN/MAX macros ******************************************/ + +#ifndef MIN +# define MIN(a,b) (a < b ? a : b) +#endif + +#ifndef MAX +# define MAX(a,b) (a > b ? a : b) +#endif + +/* LCD **********************************************************************/ +/* LCD characteristics. The logic in this driver is not portable; it is + * tailored for the SAM4l Xplained Pro's LED1 module. However, in an effort + * to add some reusability to this module, some of the tunable settings are + * included here as BOARD_ definitions (although they do not appear in the + * board.h header file. + */ + +#define SLCD_NROWS 1 +#define SLCD_NCHARS 6 +#define SLCD_MAXCONTRAST 63 + +#define BOARD_SLCD_NCOM 4 +#define BOARD_SLCD_NSEG 40 +#define SLCD_NPINS (BOARD_SLCD_NCOM+BOARD_SLCD_NSEG+1) + +/* LCD controller bias configuration. */ + +#undef BOARD_XBIAS +#define BOARD_LPWAVE 1 + +/* LCD controller initial contrast setting. */ + +#define BOARD_INITIAL_CONTRAST (SLCD_MAXCONTRAST / 2) + +/* LCD controller timing configuration */ + +#define BOARD_TIM_PRES 0 /* Clock prescaler {0|LCDCA_TIM_PRESC} */ +#define BOARD_TIM_CLOCKDIV 8 /* Clock divider {1..8} */ +#define BOARD_TIM_FC0 2 /* Frame 0 configuration {0..31} */ +#define BOARD_TIM_FC1 2 /* Frame 1 configuration {0..31} */ +#define BOARD_TIM_FC2 1 /* Frame 2 configuration {0..31} */ + +/* LCD controller configuration */ + +#if BOARD_SLCD_NCOM < 2 +# define LCD_DUTY LCDCA_CFG_DUTY_STATIC /* Static COM0 */ +#elif BOARD_SLCD_NCOM < 3 +# define LCD_DUTY LCDCA_CFG_DUTY_1TO2 /* 1/2 COM[0:1] */ +#elif BOARD_SLCD_NCOM < 4 +# define LCD_DUTY LCDCA_CFG_DUTY_1TO3 /* 1/3 COM[0:2] */ +#elif BOARD_SLCD_NCOM < 5 +# define LCD_DUTY LCDCA_CFG_DUTY_1TO4 /* 1/4 COM[0:3] */ +#else +# error Value of BOARD_SLCD_NCOM not supported +#endif + +/* Debug ********************************************************************/ + +#ifdef CONFIG_DEBUG_LCD +# define lcddbg dbg +# define lcdvdbg vdbg +#else +# define lcddbg(x...) +# define lcdvdbg(x...) +#endif + +/**************************************************************************** + * Private Type Definition + ****************************************************************************/ + +/* SLCD incoming stream structure */ + +struct slcd_instream_s +{ + struct lib_instream_s stream; + FAR const char *buffer; + ssize_t nbytes; +}; + +/* Global SLCD state */ + +struct sam_slcdstate_s +{ + bool initialized; /* True: Completed initialization sequence */ + uint8_t curpos; /* The current cursor position */ + uint8_t buffer[SLCD_NCHARS]; /* SLCD ASCII content */ +}; + +/**************************************************************************** + * Private Function Protototypes + ****************************************************************************/ +/* Debug */ + +#if defined(CONFIG_DEBUG_LCD) && defined(CONFIG_DEBUG_VERBOSE) +static void slcd_dumpstate(FAR const char *msg); +static void slcd_dumpslcd(FAR const char *msg); +#else +# define slcd_dumpstate(msg) +# define slcd_dumpslcd(msg) +#endif + +/* Internal utilities */ + +static void slcd_clear(void); +static int slcd_getstream(FAR struct lib_instream_s *instream); +static uint8_t slcd_getcontrast(void); +static int slcd_setcontrast(int contrast); +static void slcd_writech(uint8_t ch, uint8_t curpos); +static void slcd_appendch(uint8_t ch); +static void slcd_action(enum slcdcode_e code, uint8_t count); + +/* Character driver methods */ + +static ssize_t slcd_read(FAR struct file *, FAR char *, size_t); +static ssize_t slcd_write(FAR struct file *, FAR const char *, size_t); +static int slcd_ioctl(FAR struct file *filp, int cmd, unsigned long arg); +#ifndef CONFIG_DISABLE_POLL +static int slcd_poll(FAR struct file *filp, FAR struct pollfd *fds, bool setup); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is the driver state structure (there is no retained state information) */ + +static const struct file_operations g_slcdops = +{ + 0, /* open */ + 0, /* close */ + slcd_read, /* read */ + slcd_write, /* write */ + 0, /* seek */ + slcd_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , slcd_poll /* poll */ +#endif +}; + +/* LCD state data */ + +static struct sam_slcdstate_s g_slcdstate; + +/* LCD pin configurations */ + +static gpio_pinset_t g_slcdgpio[SLCD_NPINS] = +{ + GPIO_LCDCA_COM0, GPIO_LCDCA_COM1, GPIO_LCDCA_COM2, GPIO_LCDCA_COM3, + + GPIO_LCDCA_SEG0, GPIO_LCDCA_SEG1, GPIO_LCDCA_SEG2, GPIO_LCDCA_SEG3, + GPIO_LCDCA_SEG4, GPIO_LCDCA_SEG5, GPIO_LCDCA_SEG6, GPIO_LCDCA_SEG7, + GPIO_LCDCA_SEG8, GPIO_LCDCA_SEG9, GPIO_LCDCA_SEG10, GPIO_LCDCA_SEG11, + GPIO_LCDCA_SEG12, GPIO_LCDCA_SEG13, GPIO_LCDCA_SEG14, GPIO_LCDCA_SEG15, + GPIO_LCDCA_SEG16, GPIO_LCDCA_SEG17, GPIO_LCDCA_SEG18, GPIO_LCDCA_SEG19, + GPIO_LCDCA_SEG20, GPIO_LCDCA_SEG21, GPIO_LCDCA_SEG22, GPIO_LCDCA_SEG23, + GPIO_LCDCA_SEG24, GPIO_LCDCA_SEG25, GPIO_LCDCA_SEG26, GPIO_LCDCA_SEG27, + GPIO_LCDCA_SEG28, GPIO_LCDCA_SEG29, GPIO_LCDCA_SEG30, + + GPIO_LCD1_BL +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: slcd_dumpstate + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_LCD) && defined(CONFIG_DEBUG_VERBOSE) +static void slcd_dumpstate(FAR const char *msg) +{ + lcdvdbg("%s:\n", msg); + lcdvdbg(" curpos: %d\n", + g_slcdstate.curpos); + lcdvdbg(" Display: [%c%c%c%c%c%c]\n", + g_slcdstate.buffer[0], g_slcdstate.buffer[1], g_slcdstate.buffer[2], + g_slcdstate.buffer[3], g_slcdstate.buffer[4], g_slcdstate.buffer[5]); +} +#endif + +/**************************************************************************** + * Name: slcd_dumpslcd + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_LCD) && defined(CONFIG_DEBUG_VERBOSE) +static void slcd_dumpslcd(FAR const char *msg) +{ + lcdvdbg("%s:\n", msg); + lcdvdbg(" CFG: %08x TIM: %08x SR: %08x\n", + getreg32(SAM_LCDCA_CFG), getreg32(SAM_LCDCA_TIM), + getreg32(SAM_LCDCA_SR)); + lcdvdbg(" DR0: %08x %08x DR1: %08x %08x\n", + getreg32(SAM_LCDCA_DRL0), getreg32(SAM_LCDCA_DRH0), + getreg32(SAM_LCDCA_DRL1), getreg32(SAM_LCDCA_DRH1)); + lcdvdbg(" DR2: %08x %08x DR3: %08x %08x\n", + getreg32(SAM_LCDCA_DRL2), getreg32(SAM_LCDCA_DRH2), + getreg32(SAM_LCDCA_DRL3), getreg32(SAM_LCDCA_DRH3)); + lcdvdbg(" BCFG: %08x CSRCFG: %08x CMCFG: %08x ACMCFG: %08x\n", + getreg32(SAM_LCDCA_BCFG), getreg32(SAM_LCDCA_CSRCFG), + getreg32(SAM_LCDCA_CMCFG), getreg32(SAM_LCDCA_ACMCFG)); + lcdvdbg(" ABMCFG: %08x IMR: %08x VER: %08x\n", + getreg32(SAM_LCDCA_ABMCFG), getreg32(SAM_LCDCA_IMR), + getreg32(SAM_LCDCA_VERSION)); +} +#endif + +/**************************************************************************** + * Name: slcd_clear + ****************************************************************************/ + +static void slcd_clear(void) +{ + uint32_t regaddr; + + lvdbg("Clearing\n"); +#warning Missing logic +} + +/**************************************************************************** + * Name: slcd_getstream + * + * Description: + * Get one character from the keyboard. + * + ****************************************************************************/ + +static int slcd_getstream(FAR struct lib_instream_s *instream) +{ + FAR struct slcd_instream_s *slcdstream = (FAR struct slcd_instream_s *)instream; + + DEBUGASSERT(slcdstream && slcdstream->buffer); + if (slcdstream->nbytes > 0) + { + slcdstream->nbytes--; + slcdstream->stream.nget++; + return (int)*slcdstream->buffer++; + } + + return EOF; +} + +/**************************************************************************** + * Name: slcd_getcontrast + ****************************************************************************/ + +static uint8_t slcd_getcontrast(void) +{ + uint32_t regval; + uint32_t ucontrast; + int32_t scontrast; + + /* Get the current contast value */ + + regval = getreg32(SAM_LCDCA_CFG); + ucontrast = (regval & LCDCA_CFG_FCST_MASK) >> LCDCA_CFG_FCST_MASK; + + /* Sign extend and translate the 6 bit signed value + * + * Unsigned Signed Extended Translated + * Value Hex Dec + * ---------- --------- ----- ----------- + * 0000 001f 0000 001f 31 63 + * 0000 0000 0000 0000 0 32 + * 0000 0020 ffff ffe0 -32 0 + */ + + scontrast = (int32_t)(ucontrast << (32-6)); + scontrast >>= (32 - 6); + return scontrast + 32; +} + +/**************************************************************************** + * Name: slcd_setcontrast + ****************************************************************************/ + +static int slcd_setcontrast(unsigned int contrast) +{ + uint32_t regval; + int scontrast; + int ret = OK; + + /* Make sure that the contrast setting is within range */ + + if (contrast > SLCD_MAXCONTRAST) + { + contrast = SLCD_MAXCONTRAST; + ret = -ERANGE; + } + + /* Translate to get a signed value: + * + * Input Translated Value Masked Value + * Dec Hex + * ------ --- ------------ ----------- + * 63 -> 31 0000 0001f 0000 0001f + * 32 -> 0 0000 00000 0000 00000 + * 0 -> -32 ffff fffe0 0000 00020 + */ + + scontrast = (int)contrast - 32; + + /* Set the new contast value */ + + regval = getreg32(SAM_LCDCA_CFG); + regval &= ~LCDCA_CFG_FCST_MASK; + regval |= LCDCA_CFG_FCST(scontrast); + putreg32(regval, SAM_LCDCA_CFG); + + lcdvdbg("contrast: %d CFG: %08x\n", contrast, getreg32(SAM_LCDCA_CFG)); + return ret; +} + +/**************************************************************************** + * Name: slcd_writech + ****************************************************************************/ + +static void slcd_writech(uint8_t ch, uint8_t curpos) +{ + uint16_t segset; + + /* Get a set describing the segment settings */ + +#warning Missing logic + + /* Decode the value and write it to the SLCD segment memory */ + + /* Save these values in the state structure */ + + g_slcdstate.buffer[curpos] = ch; + slcd_dumpstate("AFTER WRITE"); +} + +/**************************************************************************** + * Name: slcd_appendch + ****************************************************************************/ + +static void slcd_appendch(uint8_t ch, uint8) +{ + lcdvdbg("ch: %c[%02x]\n", isprint(ch) ? ch : '.', ch); + + /* Write the character at the current cursor position */ + + slcd_writech(ch, g_slcdstate.curpos); + if (g_slcdstate.curpos < (SLCD_NCHARS - 1)) + { + g_slcdstate.curpos++; + } + + slcd_dumpstate("AFTER APPEND"); +} + +/**************************************************************************** + * Name: slcd_action + ****************************************************************************/ + +static void slcd_action(enum slcdcode_e code, uint8_t count) +{ + lcdvdbg("Action: %d count: %d\n", code, count); + slcd_dumpstate("BEFORE ACTION"); + + switch (code) + { + /* Erasure */ + + case SLCDCODE_BACKDEL: /* Backspace (backward delete) N characters */ + { + int tmp; + + /* If we are at the home position or if the count is zero, then ignore the action */ + + if (g_slcdstate.curpos < 1 || count < 1) + { + break; + } + + /* Otherwise, BACKDEL is like moving the cursor back N characters then doing a + * forward deletion. Decrement the cursor position and fall through. + */ + + tmp = (int)g_slcdstate.curpos - count; + if (tmp < 0) + { + tmp = 0; + count = g_slcdstate.curpos; + } + + /* Save the updated cursor positions */ + + g_slcdstate.curpos = tmp; + } + + case SLCDCODE_FWDDEL: /* DELete (forward delete) N characters moving text */ + if (count > 0) + { + int nchars; + int nmove; + int i; + + /* How many characters are to the right of the cursor position + * (including the one at the cursor position)? Then get the + * number of characters to move. + */ + + nchars = SLCD_NCHARS - g_slcdstate.curpos; + nmove = MIN(nchars, count) - 1; + + /* Move all characters after the current cursor position left by 'nmove' characters */ + + for (i = g_slcdstate.curpos + nmove; i < SLCD_NCHARS - 1; i++) + { + slcd_writech(g_slcdstate.buffer[i-nmove], i); + } + + /* Erase the last 'nmove' characters on the display */ + + for (i = SLCD_NCHARS - nmove; i < SLCD_NCHARS; i++) + { + slcd_writech(' ', i, 0); + } + } + break; + + case SLCDCODE_ERASE: /* Erase N characters from the cursor position */ + if (count > 0) + { + int last; + int i; + + /* Get the last position to clear and make sure that the last + * position is on the SLCD. + */ + + last = g_slcdstate.curpos + count - 1; + if (last >= SLCD_NCHARS) + { + last = SLCD_NCHARS - 1; + } + + /* Erase N characters after the current cursor position left by one */ + + for (i = g_slcdstate.curpos; i < last; i++) + { + slcd_writech(' ', i, 0); + } + } + break; + + case SLCDCODE_CLEAR: /* Home the cursor and erase the entire display */ + { + /* This is like HOME followed by ERASEEOL. Home the cursor and + * fall through. + */ + + g_slcdstate.curpos = 0; + } + + case SLCDCODE_ERASEEOL: /* Erase from the cursor position to the end of line */ + { + int i; + + /* Erase characters after the current cursor position to the end of the line */ + + for (i = g_slcdstate.curpos; i < SLCD_NCHARS; i++) + { + slcd_writech(' ', i, 0); + } + } + break; + + /* Cursor movement */ + + case SLCDCODE_HOME: /* Cursor home */ + { + g_slcdstate.curpos = 0; + } + break; + + case SLCDCODE_END: /* Cursor end */ + { + g_slcdstate.curpos = SLCD_NCHARS - 1; + } + break; + + case SLCDCODE_LEFT: /* Cursor left by N characters */ + { + int tmp = (int)g_slcdstate.curpos - count; + + /* Don't permit movement past the beginning of the SLCD */ + + if (tmp < 0) + { + tmp = 0; + } + + /* Save the new cursor position */ + + g_slcdstate.curpos = (uint8_t)tmp; + } + break; + + case SLCDCODE_RIGHT: /* Cursor right by N characters */ + { + int tmp = (int)g_slcdstate.curpos + count; + + /* Don't permit movement past the end of the SLCD */ + + if (tmp >= SLCD_NCHARS) + { + tmp = SLCD_NCHARS - 1; + } + + /* Save the new cursor position */ + + g_slcdstate.curpos = (uint8_t)tmp; + } + break; + + case SLCDCODE_UP: /* Cursor up by N lines */ + case SLCDCODE_DOWN: /* Cursor down by N lines */ + case SLCDCODE_PAGEUP: /* Cursor up by N pages */ + case SLCDCODE_PAGEDOWN: /* Cursor down by N pages */ + break; /* Not supportable on this SLCD */ + + /* Blinking */ + + case SLCDCODE_BLINKSTART: /* Start blinking with current cursor position */ + case SLCDCODE_BLINKEND: /* End blinking after the current cursor position */ + case SLCDCODE_BLINKOFF: /* Turn blinking off */ + break; /* Not implemented */ + + /* These are actually unreportable errors */ + + default: + case SLCDCODE_NORMAL: /* Not a special keycode */ + break; + } + + slcd_dumpstate("AFTER ACTION"); +} + +/**************************************************************************** + * Name: slcd_read + ****************************************************************************/ + +static ssize_t slcd_read(FAR struct file *filp, FAR char *buffer, size_t len) +{ + int ret = 0; + int i; + + /* Try to read the entire display. Notice that the seek offset + * (filp->f_pos) is ignored. It probably should be taken into account + * and also updated after each read and write. + */ + + for (i = 0; i < SLCD_NCHARS && ret < len; i++) + { + /* Return the character */ + + *buffer++ = g_slcdstate.buffer[i]; + ret++; + + /* Check if the character is decorated with a folling period or colon */ + + if (ret < len && g_slcdstate.buffer[i] != 0) + { + if ((g_slcdstate.buffer[i] & SLCD_DP) != 0) + { + *buffer++ = '.'; + ret++; + } + else if ((g_slcdstate.buffer[i] & SLCD_COLON) != 0) + { + *buffer++ = ':'; + ret++; + } + } + } + + slcd_dumpstate("READ"); + return ret; +} + +/**************************************************************************** + * Name: slcd_write + ****************************************************************************/ + +static ssize_t slcd_write(FAR struct file *filp, + FAR const char *buffer, size_t len) +{ + struct slcd_instream_s instream; + struct slcdstate_s state; + enum slcdret_e result; + uint8_t ch; + uint8_t count; + uint8_t prev = ' '; + bool valid = false; + + /* Initialize the stream for use with the SLCD CODEC */ + + instream.stream.get = slcd_getstream; + instream.stream.nget = 0; + instream.buffer = buffer; + instream.nbytes = len; + + /* Prime the pump. This is messy, but necessary to handle decoration on a + * character based on any following period or colon. + */ + + memset(&state, 0, sizeof(struct slcdstate_s)); + result = slcd_decode(&instream.stream, &state, &prev, &count); + + lcdvdbg("slcd_decode returned result=%d char=%d count=%d\n", + result, prev, count); + + switch (result) + { + case SLCDRET_CHAR: + valid = true; + break; + + case SLCDRET_SPEC: + { + slcd_action((enum slcdcode_e)prev, count); + prev = ' '; + } + break; + + case SLCDRET_EOF: + return 0; + } + + /* Now decode and process every byte in the input buffer */ + + while ((result = slcd_decode(&instream.stream, &state, &ch, &count)) != SLCDRET_EOF) + { + lcdvdbg("slcd_decode returned result=%d char=%d count=%d\n", + result, ch, count); + + if (result == SLCDRET_CHAR) /* A normal character was returned */ + { + /* Check for ASCII control characters */ + + if (ch < ASCII_SPACE) + { + /* All are ignored except for backspace and carriage return */ + + if (ch == ASCII_BS) + { + /* If there is a pending character, then output it now before + * performing the action. + */ + + if (valid) + { + slcd_appendch(prev, 0); + prev = ' '; + valid = false; + } + + /* Then perform the backward deletion */ + + slcd_action(SLCDCODE_BACKDEL, 1); + } + else if (ch == ASCII_CR) + { + /* If there is a pending character, then output it now before + * performing the action. + */ + + if (valid) + { + slcd_appendch(prev, 0); + prev = ' '; + valid = false; + } + + /* Then perform the carriage return */ + + slcd_action(SLCDCODE_HOME, 0); + } + } + + /* Handle characters decoreated with a period or a colon */ + + else if (ch == '.') + { + /* Write the previous character with the decimal point appended */ + + slcd_appendch(prev, SLCD_DP); + prev = ' '; + valid = false; + } + else if (ch == ':') + { + /* Write the previous character with the colon appended */ + + slcd_appendch(prev, SLCD_COLON); + prev = ' '; + valid = false; + } + + /* Handle ASCII_DEL */ + + else if (ch == ASCII_DEL) + { + /* If there is a pending character, then output it now before + * performing the action. + */ + + if (valid) + { + slcd_appendch(prev, 0); + prev = ' '; + valid = false; + } + + /* Then perform the foward deletion */ + + slcd_action(SLCDCODE_FWDDEL, 1); + } + + /* The rest of the 7-bit ASCII characters are fair game */ + + else if (ch < 128) + { + /* Write the previous character if it valid */ + + if (valid) + { + slcd_appendch(prev, 0); + } + + /* There is now a valid output character */ + + prev = ch; + valid = true; + } + } + else /* (result == SLCDRET_SPEC) */ /* A special SLCD action was returned */ + { + /* If there is a pending character, then output it now before + * performing the action. + */ + + if (valid) + { + slcd_appendch(prev, 0); + prev = ' '; + valid = false; + } + + /* Then perform the action */ + + slcd_action((enum slcdcode_e)ch, count); + } + } + + /* Handle any unfinished output */ + + if (valid) + { + slcd_appendch(prev, 0); + } + + /* Assume that the entire input buffer was processed */ + + return (ssize_t)len; +} + +/**************************************************************************** + * Name: slcd_poll + ****************************************************************************/ + +static int slcd_ioctl(FAR struct file *filp, int cmd, unsigned long arg) +{ + switch (cmd) + { + + /* SLCDIOC_GETATTRIBUTES: Get the attributes of the SLCD + * + * argument: Pointer to struct slcd_attributes_s in which values will be + * returned + */ + + case SLCDIOC_GETATTRIBUTES: + { + FAR struct slcd_attributes_s *attr = (FAR struct slcd_attributes_s *)((uintptr_t)arg); + + lcdvdbg("SLCDIOC_GETATTRIBUTES:\n"); + + if (!attr) + { + return -EINVAL; + } + + attr->nrows = SLCD_NROWS; + attr->ncolumns = SLCD_NCHARS; + attr->nbars = 0; + attr->maxcontrast = SLCD_MAXCONTRAST; + attr->maxbrightness = 0; + } + break; + + /* SLCDIOC_CURPOS: Get the SLCD cursor positioni (rows x characters) + * + * argument: Pointer to struct slcd_curpos_s in which values will be + * returned + */ + + + case SLCDIOC_CURPOS: + { + FAR struct slcd_curpos_s *curpos = (FAR struct slcd_curpos_s *)((uintptr_t)arg); + + lcdvdbg("SLCDIOC_CURPOS: row=0 column=%d\n", g_slcdstate.curpos); + + if (!curpos) + { + return -EINVAL; + } + + curpos->row = 0; + curpos->column = g_slcdstate.curpos; + } + break; + + /* SLCDIOC_GETCONTRAST: Get the current contrast setting + * + * argument: Pointer type int that will receive the current contrast + * setting + */ + + case SLCDIOC_GETCONTRAST: + { + FAR int *contrast = (FAR int *)((uintptr_t)arg); + if (!contrast) + { + return -EINVAL; + } + + *contrast = (int)slcd_getcontrast(); + lcdvdbg("SLCDIOC_GETCONTRAST: contrast=%d\n", *contrast); + } + break; + + /* SLCDIOC_SETCONTRAST: Set the contrast to a new value + * + * argument: The new contrast value + */ + + case SLCDIOC_SETCONTRAST: + { + lcdvdbg("SLCDIOC_SETCONTRAST: arg=%ld\n", arg); + + if (arg > SLCD_MAXCONTRAST) + { + return -ERANGE; + } + + return slcd_setcontrast((int)arg); + } + break; + + case SLCDIOC_SETBAR: /* Get bar levels */ + case SLCDIOC_GETBRIGHTNESS: /* Get the current brightness setting */ + case SLCDIOC_SETBRIGHTNESS: /* Set the brightness to a new value */ + default: + return -ENOTTY; + } + + return OK; +} + +/**************************************************************************** + * Name: slcd_poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int slcd_poll(FAR struct file *filp, FAR struct pollfd *fds, + bool setup) +{ + if (setup) + { + /* Data is always avaialble to be read / Data can always be written */ + + fds->revents |= (fds->events & (POLLIN|POLLOUT)); + if (fds->revents != 0) + { + sem_post(fds->sem); + } + } + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_slcd_initialize + * + * Description: + * Initialize the SAM4L Xplained Pro LCD hardware and register the character + * driver as /dev/slcd. + * + ****************************************************************************/ + +int sam_slcd_initialize(void) +{ + uint32_t regval; + int ret = OK; + int i; + + /* Only initialize the driver once. */ + + if (!g_slcdstate.initialized) + { + lcdvdbg("Initializing\n"); + + /* Configure LCD GPIO pins */ + + for (i = 0; i < SLCD_NPINS; i++) + { + sam_configgpio(g_slcdgpio[i]); + } + + /* Enable APB clock for LCDCA */ + + sam_lcdca_enableclk(); + + /* Here we require that either CONFIG_SAM34_OSC32K or CONFIG_SAM34_RC32K + * is defined in the configuration. In that case, the source clock was + * initialized during boot up and we can be assured that it is read for + * use now. + */ + + /* Disable the LCD controller and frame counters */ + + putreg32(LCDCA_CR_DIS | LCDCA_CR_FC0DIS | LCDCA_CR_FC1DIS | + LCDCA_CR_FC2DIS, SAM_LCDCA_CR); + + /* Configure LCD controller timing */ + + regval = BOARD_TIM_PRES | LCDCA_TIM_CLKDIV(BOARD_TIM_CLOCKDIV) | + LCDCA_TIM_FC0(BOARD_TIM_FC0) | LCDCA_TIM_FC1(BOARD_TIM_FC1) | + LCDCA_TIM_FC2(BOARD_TIM_FC2); + putreg32(regval, SAM_LCDCA_TIM); + + /* Setup the LCD controller configuration. + * + * External bias generation (XBIAS): Controlled by board setting + * Waveform mode: Controlled by board setting + * Blank LCD: No + * Lock: No + * Duty: Controlled by board setting + * Fine Contrast (FCST): 0 + * Number of segments (NSU): Controlled by board setting + */ + + regval = +#ifdef BOARD_XBIAS + LCDCA_CFG_XBIAS | +#endif +#ifndef BOARD_LPWAVE + LCDCA_CFG_WMOD | +#endif + LCD_DUTY | + LCDCA_CFG_FCST(0) | + LCDCA_CFG_NSU(BOARD_SLCD_NSEG)); + + putreg32(regval, SAM_LCDCA_CFG); + + /* Provide an initial contrast setting */ + + slcd_setcontrast(BOARD_INITIAL_CONTRAST); + + /* Turn off blanking of display segments */ + + regval = getreg32(SAM_LCDCA_CFG); + regval &= ~LCDCA_CFG_BLANK; + putreg32(regval, SAM_LCDCA_CFG); + + /* Enable the display controller */ + + putreg32(LCDCA_CR_EN, SAM_LCDCA_CR); + + /* Clear all display memory */ + + putreg32(LCDCA_CR_CDM, SAM_LCDCA_CR); + + /* Wait for the LCD to be fully enabled */ + + while ((getreg32(SAM_LCDCA_SR) & LCDCA_SR_EN) == 0); + + /* Enable frame counters */ + + putreg32(LCDCA_CR_FC0EN, SAM_LCDCA_CR); + while ((getreg32(SAM_LCDCA_SR) & LCDCA_SR_FC0S) == 0); + + putreg32(LCDCA_CR_FC1EN, SAM_LCDCA_CR); + while ((getreg32(SAM_LCDCA_SR) & LCDCA_SR_FC1S) == 0); + + putreg32(LCDCA_CR_FC2EN, SAM_LCDCA_CR); + while ((getreg32(SAM_LCDCA_SR) & LCDCA_SR_FC2S) == 0); + + /* Make sure that blinking and circular shifting is off */ + + putreg32(LCDCA_CR_BSTOP | , SAM_LCDCA_CR); + putreg32(LCDCA_CR_CSTOP, SAM_LCDCA_CR); + + /* Disable any automated display */ + + regval = getreg32(SAM_LCDCA_ACMCFG); + regval &= ~LCDCA_ACMCFG_EN; + putreg32(regval, SAM_LCDCA_ACMCFG); + + /* Initialize display memory */ + + putreg32(LCDCA_DRL_MASK, SAM_LCDCA_DRL0); + putreg32(LCDCA_DRH_MASK, SAM_LCDCA_DRH0); + putreg32(LCDCA_DRL_MASK, SAM_LCDCA_DRL1); + putreg32(LCDCA_DRH_MASK, SAM_LCDCA_DRH1); + putreg32(LCDCA_DRL_MASK, SAM_LCDCA_DRL2); + putreg32(LCDCA_DRH_MASK, SAM_LCDCA_DRH2); + putreg32(LCDCA_DRL_MASK, SAM_LCDCA_DRL3); + putreg32(LCDCA_DRH_MASK, SAM_LCDCA_DRH3); + + /* Register the LCD device driver */ + + ret = register_driver("/dev/slcd", &g_slcdops, 0644, &g_slcdstate); + g_slcdstate.initialized = true; + + /* Turn on the backlight */ + + sam_gpiowrite(GPIO_LCD1_BL, true); + slcd_dumpstate("AFTER INITIALIZATION"); + } + + return ret; +} + +#endif /* CONFIG_SAM34_LCDCA */ diff --git a/configs/stm32ldiscovery/src/stm32_lcd.c b/configs/stm32ldiscovery/src/stm32_lcd.c index a859f5a474..17f6d2c618 100644 --- a/configs/stm32ldiscovery/src/stm32_lcd.c +++ b/configs/stm32ldiscovery/src/stm32_lcd.c @@ -465,7 +465,7 @@ static void slcd_dumpstate(FAR const char *msg) static void slcd_dumpslcd(FAR const char *msg) { lcdvdbg("%s:\n", msg); - lcdvdbg(" CR: %08x FCR: %08x SR: %08x CLR: %08x:\n", + lcdvdbg(" CR: %08x FCR: %08x SR: %08x CLR: %08x\n", getreg32(STM32_LCD_CR), getreg32(STM32_LCD_FCR), getreg32(STM32_LCD_SR), getreg32(STM32_LCD_CLR)); lcdvdbg(" RAM0L: %08x RAM1L: %08x RAM2L: %08x RAM3L: %08x\n",