A library with drivers for different peripherals on the LPC4088 QuickStart Board or related add-on boards.
Fork of EALib by
Diff: EaLcdBoard.cpp
- Revision:
- 0:0fdadbc3d852
- Child:
- 4:b32cf4ef45c5
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EaLcdBoard.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,602 @@ + +#include "mbed.h" +#include "EaLcdBoard.h" + +#define LCDB_MAGIC 0xEA01CDAE + + +#define LCDB_PCA9532_I2C_ADDR (0x64 << 1) + +/* PCA9532 registers*/ +#define LCDB_PCA9532_INPUT0 0x00 +#define LCDB_PCA9532_INPUT1 0x01 +#define LCDB_PCA9532_PSC0 0x02 +#define LCDB_PCA9532_PWM0 0x03 +#define LCDB_PCA9532_PSC1 0x04 +#define LCDB_PCA9532_PWM1 0x05 +#define LCDB_PCA9532_LS0 0x06 +#define LCDB_PCA9532_LS1 0x07 +#define LCDB_PCA9532_LS2 0x08 +#define LCDB_PCA9532_LS3 0x09 +#define LCDB_PCA9532_AUTO_INC 0x10 + +#define LCDB_LS_MODE_ON 0x01 +#define LCDB_LS_MODE_BLINK0 0x02 +#define LCDB_LS_MODE_BLINK1 0x03 + +#define LCDB_CTRL_3V3 0x0001 +#define LCDB_CTRL_5V 0x0002 +#define LCDB_CTRL_DISP_EN 0x0010 +#define LCDB_CTRL_BL_EN 0x0080 +#define LCDB_CTRL_BL_C 0x0100 +#define LCDB_EEPROM_WP 0x8000 + +#define LCDB_EEPROM_I2C_ADDR (0x56 << 1) +#define LCDB_EEPROM_PAGE_SIZE 32 +#define LCDB_EEPROM_TOTAL_SIZE 8192 + +/* + * Set which sequence string version that is supported + */ +#define LCDB_SEQ_VER 1 + +#ifndef MIN +#define MIN(x, y) (((x)<(y))?(x):(y)) +#endif + +#define EA_LCD_TMP_BUFFER_SZ 256 +static char* eaLcdTmpBuffer[EA_LCD_TMP_BUFFER_SZ]; + + +/* Structure containing the parameters for the LCD panel as stored on LCD Board */ + +/* LCD display types */ +typedef enum { + TFT = 0, /* standard TFT */ + ADTFT, /* advanced TFT */ + HRTFT, /* highly reflective TFT */ + MONO_4BIT, /* 4-bit mono */ + MONO_8BIT, /* 8-bit mono */ + CSTN /* color STN */ +} nxp_lcd_panel_t; + +typedef struct { + uint8_t h_back_porch; /* Horizontal back porch in clocks */ + uint8_t h_front_porch; /* Horizontal front porch in clocks */ + uint8_t h_sync_pulse_width; /* HSYNC pulse width in clocks */ + uint16_t pixels_per_line; /* Pixels per line (horizontal resolution) */ + uint8_t v_back_porch; /* Vertical back porch in clocks */ + uint8_t v_front_porch; /* Vertical front porch in clocks */ + uint8_t v_sync_pulse_width; /* VSYNC pulse width in clocks */ + uint16_t lines_per_panel; /* Lines per panel (vertical resolution) */ + uint8_t invert_output_enable; /* Invert output enable, 1 = invert*/ + uint8_t invert_panel_clock; /* Invert panel clock, 1 = invert*/ + uint8_t invert_hsync; /* Invert HSYNC, 1 = invert */ + uint8_t invert_vsync; /* Invert VSYNC, 1 = invert */ + uint8_t ac_bias_frequency; /* AC bias frequency in clocks */ + uint8_t bits_per_pixel; /* Maximum bits per pixel the display supports */ + uint32_t optimal_clock; /* Optimal clock rate (Hz) */ + nxp_lcd_panel_t lcd_panel_type; /* LCD panel type */ + uint8_t dual_panel; /* Dual panel, 1 = dual panel display */ +} nxp_lcd_param_t; + +static uint32_t str_to_uint(char* str, uint32_t len); + +EaLcdBoard::EaLcdBoard(PinName sda, PinName scl) : _i2c(sda, scl) { + _blink0Shadow = 0; + _blink1Shadow = 0; + _ledStateShadow = 0; + _lcdPwrOn = false; +} + +EaLcdBoard::Result EaLcdBoard::open(LcdController::Config* cfg, char* initSeq) { + + EaLcdBoard::Result result = Ok; + + // load LCD configuration from storage + if (cfg == NULL) { + result = getLcdConfig(&_cfg); + cfg = &_cfg; + } + + // load init sequence from storage + if (result == Ok && initSeq == NULL) { + result = getInitSeq((char*)eaLcdTmpBuffer, EA_LCD_TMP_BUFFER_SZ); + initSeq = (char*)eaLcdTmpBuffer; + } + + if (result != Ok) { + return result; + } + + return parseInitString(initSeq, cfg); +} + +EaLcdBoard::Result EaLcdBoard::close() { + int r = 0; + + do { + r = lcdCtrl.setPower(false); + if (r != 0) break; + + _lcdPwrOn = false; + + r = lcdCtrl.close(); + } while(0); + + if (r != 0) { + return LcdAccessError; + } + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::setFrameBuffer(uint32_t address) { + int r = 0; + + do { + + // begin by powering on the display + if (!_lcdPwrOn) { + r = lcdCtrl.setPower(true); + if (r != 0) break; + + _lcdPwrOn = true; + } + + // activate specified frame buffer + r = lcdCtrl.setFrameBuffer(address); + if (r != 0) break; + + } while(0); + + if (r != 0) { + return LcdAccessError; + } + + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getLcdConfig(LcdController::Config* cfg) { + store_t h; + + nxp_lcd_param_t lcdParam; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + eepromRead((uint8_t*)&lcdParam, h.lcdParamOff, + (h.initOff-h.lcdParamOff)); + + cfg->horizontalBackPorch = lcdParam.h_back_porch; + cfg->horizontalFrontPorch = lcdParam.h_front_porch; + cfg->hsync = lcdParam.h_sync_pulse_width; + cfg->width = lcdParam.pixels_per_line; + cfg->verticalBackPorch = lcdParam.v_back_porch; + cfg->verticalFrontPorch = lcdParam.v_front_porch; + cfg->vsync = lcdParam.v_sync_pulse_width; + cfg->height = lcdParam.lines_per_panel; + cfg->invertOutputEnable = (lcdParam.invert_output_enable == 1); + cfg->invertPanelClock = (lcdParam.invert_panel_clock == 1); + cfg->invertHsync = (lcdParam.invert_hsync == 1); + cfg->invertVsync = (lcdParam.invert_vsync == 1); + cfg->acBias = lcdParam.ac_bias_frequency; + cfg->bpp = LcdController::Bpp_16_565; + cfg->optimalClock = lcdParam.optimal_clock; + cfg->panelType = (LcdController::LcdPanel)lcdParam.lcd_panel_type; + cfg->dualPanel = (lcdParam.dual_panel == 1); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getDisplayName(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if (len < NameBufferSize) { + return BufferTooSmall; + } + + strncpy(buf, (char*)h.lcd_name, NameBufferSize); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getDisplayMfg(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if (len < NameBufferSize) { + return BufferTooSmall; + } + + strncpy(buf, (char*)h.lcd_mfg, NameBufferSize); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getInitSeq(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if ((h.pdOff-h.initOff) > len) { + return BufferTooSmall; + } + + eepromRead((uint8_t*)buf, h.initOff, + (h.pdOff-h.initOff)); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getPowerDownSeq(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if ((h.tsOff-h.pdOff) > len) { + return BufferTooSmall; + } + + eepromRead((uint8_t*)buf, h.pdOff, + (h.tsOff-h.pdOff)); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getStore(store_t* store) { + int num = 0; + + if (store == NULL) return InvalidArgument; + + num = eepromRead((uint8_t*)store, 0, sizeof(store_t)); + if (num < (int)sizeof(store_t)) { + return InvalidStorage; + } + + return Ok; +} + +// ########################### +// An EEPROM is used for persistent storage +// ########################### + +int EaLcdBoard::eepromRead(uint8_t* buf, uint16_t offset, uint16_t len) { + int i = 0; + char off[2]; + + if (len > LCDB_EEPROM_TOTAL_SIZE || offset+len > LCDB_EEPROM_TOTAL_SIZE) { + return -1; + } + + off[0] = ((offset >> 8) & 0xff); + off[1] = (offset & 0xff); + + _i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)off, 2); + for ( i = 0; i < 0x2000; i++); + _i2c.read(LCDB_EEPROM_I2C_ADDR, (char*)buf, len); + + return len; +} + +int EaLcdBoard::eepromWrite(uint8_t* buf, uint16_t offset, uint16_t len) { + int16_t written = 0; + uint16_t wLen = 0; + uint16_t off = offset; + uint8_t tmp[LCDB_EEPROM_PAGE_SIZE+2]; + + if (len > LCDB_EEPROM_TOTAL_SIZE || offset+len > LCDB_EEPROM_TOTAL_SIZE) { + return -1; + } + + wLen = LCDB_EEPROM_PAGE_SIZE - (off % LCDB_EEPROM_PAGE_SIZE); + wLen = MIN(wLen, len); + + while (len) { + tmp[0] = ((off >> 8) & 0xff); + tmp[1] = (off & 0xff); + memcpy(&tmp[2], (void*)&buf[written], wLen); + _i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)tmp, wLen+2); + + // delay to wait for a write cycle + //eepromDelay(); + wait_ms(1); + + len -= wLen; + written += wLen; + off += wLen; + + wLen = MIN(LCDB_EEPROM_PAGE_SIZE, len); + } + + return written; +} + +// ########################### +// string parsing (initialization and power down strings) +// ########################### + +EaLcdBoard::Result EaLcdBoard::parseInitString(char* str, LcdController::Config* cfg) { + char* c = NULL; + uint32_t len = 0; + Result result = Ok; + + if (str == NULL) { + return InvalidCommandString; + } + + while(*str != '\0') { + + // skip whitespaces + while(*str == ' ') { + str++; + } + + c = str; + + // find end of command + while(*str != ',' && *str != '\0') { + str++; + } + + len = (str-c); + + if (*str == ',') { + str++; + } + + switch (*c++) { + + case 'v': + result = checkVersion(c, len-1); + break; + + // sequence control command (pca9532) + case 'c': + execSeqCtrl(c, len-1); + break; + + // delay + case 'd': + execDelay(c, len-1); + break; + + // open lcd (init LCD controller) + case 'o': + + if (cfg != NULL) { + + if (lcdCtrl.open(cfg) != 0) { + result = LcdAccessError; + } + } + + else { + result = InvalidArgument; + } + + break; + + } + + if (result != Ok) { + break; + } + + + } + + + return result; +} + +EaLcdBoard::Result EaLcdBoard::checkVersion(char* v, uint32_t len) { + uint32_t ver = str_to_uint(v, len); + + if (ver > LCDB_SEQ_VER) { + return VersionNotSupported; + } + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::execDelay(char* del, uint32_t len) { + wait_ms(str_to_uint(del, len)); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::execSeqCtrl(char* cmd, uint32_t len) { + + switch (*cmd++) { + + // display enable + case 'd': + setDisplayEnableSignal(*cmd == '1'); + break; + + // backlight contrast + case 'c': + setBacklightContrast(str_to_uint(cmd, len)); + break; + + // 3v3 enable + case '3': + set3V3Signal(*cmd == '1'); + break; + + // 5v enable + case '5': + set5VSignal(*cmd == '1'); + break; + } + + return Ok; +} + +// ########################### +// PCA9532 is used as a control inteface to the display. +// voltages can be turned on/off and backlight can be controlled. +// ########################### + +// Helper function to set LED states +void EaLcdBoard::setLsStates(uint16_t states, uint8_t* ls, uint8_t mode) +{ +#define IS_LED_SET(bit, x) ( ( ((x) & (bit)) != 0 ) ? 1 : 0 ) + + int i = 0; + + for (i = 0; i < 4; i++) { + + ls[i] |= ( (IS_LED_SET(0x0001, states)*mode << 0) + | (IS_LED_SET(0x0002, states)*mode << 2) + | (IS_LED_SET(0x0004, states)*mode << 4) + | (IS_LED_SET(0x0008, states)*mode << 6) ); + + states >>= 4; + } +} + +void EaLcdBoard::setLeds(void) +{ + uint8_t buf[5]; + uint8_t ls[4] = {0,0,0,0}; + uint16_t states = _ledStateShadow; + + // LEDs in On/Off state + setLsStates(states, ls, LCDB_LS_MODE_ON); + + // set the LEDs that should blink + setLsStates(_blink0Shadow, ls, LCDB_LS_MODE_BLINK0); + setLsStates(_blink1Shadow, ls, LCDB_LS_MODE_BLINK1); + + buf[0] = LCDB_PCA9532_LS0 | LCDB_PCA9532_AUTO_INC; + buf[1] = ls[0]; + buf[2] = ls[1]; + buf[3] = ls[2]; + buf[4] = ls[3]; + + _i2c.write(LCDB_PCA9532_I2C_ADDR, (char*)buf, 5); +} + +void EaLcdBoard::pca9532_setLeds (uint16_t ledOnMask, uint16_t ledOffMask) +{ + // turn off leds + _ledStateShadow &= (~(ledOffMask) & 0xffff); + + // ledOnMask has priority over ledOffMask + _ledStateShadow |= ledOnMask; + + // turn off blinking + _blink0Shadow &= (~(ledOffMask) & 0xffff); + _blink1Shadow &= (~(ledOffMask) & 0xffff); + + setLeds(); +} + +void EaLcdBoard::pca9532_setBlink0Period(uint8_t period) +{ + uint8_t buf[2]; + + buf[0] = LCDB_PCA9532_PSC0; + buf[1] = period; + + _i2c.write(LCDB_PCA9532_I2C_ADDR, (char*)buf, 2); +} + +void EaLcdBoard::pca9532_setBlink0Duty(uint8_t duty) +{ + uint8_t buf[2]; + uint32_t tmp = duty; + if (tmp > 100) { + tmp = 100; + } + + tmp = (255 * tmp)/100; + + buf[0] = LCDB_PCA9532_PWM0; + buf[1] = tmp; + + _i2c.write(LCDB_PCA9532_I2C_ADDR, (char*)buf, 2); +} + +void EaLcdBoard::pca9532_setBlink0Leds(uint16_t ledMask) +{ + _blink0Shadow |= ledMask; + setLeds(); +} + +void EaLcdBoard::set3V3Signal(bool enabled) { + if (enabled) { + pca9532_setLeds(LCDB_CTRL_3V3, 0); + } else { + pca9532_setLeds(0, LCDB_CTRL_3V3); + } +} + +void EaLcdBoard::set5VSignal(bool enabled) { + if (enabled) { + pca9532_setLeds(LCDB_CTRL_5V, 0); + } else { + pca9532_setLeds(0, LCDB_CTRL_5V); + } +} + +void EaLcdBoard::setDisplayEnableSignal(bool enabled) { + if (!enabled) { + pca9532_setLeds(LCDB_CTRL_DISP_EN, 0); + } else { + pca9532_setLeds(0, LCDB_CTRL_DISP_EN); + } +} + +void EaLcdBoard::setBacklightContrast(uint32_t value) { + + if (value > 100) return; + + pca9532_setBlink0Duty(100-value); + pca9532_setBlink0Period(0); + pca9532_setBlink0Leds(LCDB_CTRL_BL_C); +} + + +// convert string to integer +static uint32_t str_to_uint(char* str, uint32_t len) +{ + uint32_t val = 0; + + while(len > 0 && *str <= '9' && *str >= '0') { + val = (val * 10) + (*str - '0'); + str++; + len--; + } + + return val; +} + + + +