Class Module for EA DOGS102 Graphic LCD display SPI Interface

Dependents:   mDotEVBM2X MTDOT-EVB-LinkCheck-AL MTDOT-EVBDemo-DRH MTDOT_BOX_EVB_LCD_Helloworld ... more

DOGS102.cpp

Committer:
Leon Lindenfelser
Date:
2016-10-17
Revision:
5:e66152f036d9
Parent:
4:8797ad72a9a8

File content as of revision 5:e66152f036d9:

/**
 * @file    DOGS102.cpp
 * @brief   Device driver - DOGS102 102x64 pixel Graphic LCD display W/RTOS Support
 * @author  Tim Barr
 * @version 1.01
 * @see     http://www.lcd-module.com/eng/pdf/grafik/dogs102-6e.pdf
 * @see     http://www.lcd-module.com/eng/pdf/zubehoer/uc1701.pdf
 *
 * Copyright (c) 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * 07/08/15 TAB  Fixed error boundary check error in endUpdate
 */
 
#include "DOGS102.h"
#include "mbed_debug.h"
#include "rtos.h"

// macro to make sure x falls into range from low to high (inclusive)
#define CLIP(x, low, high) { if ( (x) < (low) ) x = (low); if ( (x) > (high) ) x = (high); } while (0);
 
DOGS102::DOGS102(SPI &spi, DigitalOut &lcd_cs, DigitalOut &cmnd_data)
{
    _spi =  &spi;
    _lcd_cs = &lcd_cs;
    _cmnd_data = &cmnd_data;

    DOGS102::init();
    
    return;
}
uint8_t DOGS102::setCursor(uint8_t xcur, uint8_t ycur)
{
    uint8_t ypage;
    uint8_t y_shift;

	CLIP(xcur, 0, LCDWIDTH-1);
	ypage = ycur/8;
    CLIP(ypage, 0, LCDPAGES-1);
    y_shift = ycur % 8;
    DOGS102::writeCommand(SETPGADDR,ypage);
    DOGS102::writeCommand(SETCOLADDRMSB,xcur>>4);
    DOGS102::writeCommand(SETCOLADDRLSB,xcur);
    return y_shift;
 }

void DOGS102::clearBuffer(void)
{
    memset(_lcdbuffer, 0, sizeof(_lcdbuffer));

    if (!_update_flag)
    {
    	DOGS102::sendBuffer(_lcdbuffer);
    }

}

void DOGS102::writeText(uint8_t column, uint8_t page, const uint8_t *font_address, const char *text, const uint8_t size)
{
	// Position of character data in memory array
	uint16_t pos_array;
	// temporary column, page address, and column_cnt are used
	// to stay inside display area
	uint8_t i,y, column_cnt = 0;

	// font information, needed for calculation
	uint8_t start_code, last_code, width, page_height, bytes_p_char;

	uint8_t *txtbuffer;

	start_code 	 = font_address[2];  // get first defined character
	last_code	 = font_address[3];  // get last defined character
	width		 = font_address[4];  // width in pixel of one char
	page_height  = font_address[6];  // page count per char
	bytes_p_char = font_address[7];  // bytes per char

	if(page_height + page > LCDPAGES) //stay inside display area
		page_height = LCDPAGES - page;

	// The string is displayed character after character. If the font has more then one page,
	// the top page is printed first, then the next page and so on
	for(y = 0; y < page_height; y++)
	{
		txtbuffer = &_lcdbuffer[page*LCDWIDTH + column];
		column_cnt = 0;					// clear column_cnt start point
		i = 0;
		while(( i < size) && ((column_cnt + column) < LCDWIDTH))
		{
			if(text[i] < start_code || (uint8_t)text[i] > last_code) //make sure data is valid
				i++;
			else
			{
				// calculate position of ASCII character in font array
				// bytes for header + (ASCII - startcode) * bytes per char)
				pos_array = 8 + (uint8_t)(text[i++] - start_code) * bytes_p_char;

				// get the dot pattern for the part of the char to print
				pos_array += y*width;

				// stay inside display area
				if((column_cnt + width + column) > LCDWIDTH)
					column_cnt = LCDWIDTH-width;

				// copy character data to buffer
				memcpy (txtbuffer+column_cnt,font_address+pos_array,width);
			}

			column_cnt += width;
		}
		if (!_update_flag)
		{
			setCursor(column,(page+y)*8);	// set start position x and y
			DOGS102::writeData(txtbuffer,column_cnt);
		}
	}
}

void DOGS102::writeBitmap(uint8_t column, uint8_t page, const uint8_t *bm_address)
{
	uint8_t width, page_cnt, bm_pntr;

    width = bm_address[0];
	page_cnt = (bm_address[1] + 7) / 8; //height in pages, add 7 and divide by 8 for getting the used pages (byte boundaries)

	if(width + column > LCDWIDTH) //stay inside display area
		width = LCDWIDTH - column;
	if(page_cnt + page > LCDPAGES)
		page_cnt = LCDPAGES - page;

	for (uint8_t i=0;i < page_cnt;i++ )
	{
		bm_pntr = 2+i*width;
		memcpy(_lcdbuffer+column+((i+page)*LCDWIDTH),bm_address+bm_pntr, width);
	}

	if (_update_flag == 0)
	{
		DOGS102::sendBuffer(_lcdbuffer);
	}
}

void DOGS102::startUpdate(void)
{
	_update_flag++;
}

void DOGS102::endUpdate(void)
{
	if (_update_flag != 0)
		_update_flag--;
	
	if (_update_flag == 0)
	{
		DOGS102::sendBuffer(_lcdbuffer);
	}
}

uint8_t DOGS102::getUpdateState(void)
{
	return _update_flag;
}

uint8_t DOGS102::init(void)
{
    uint8_t result = 0;
    
    _spi->frequency(4000000);
    _spi->format(8,3);		// 8bit transfers, SPI mode 3
    _lcd_cs->write(1);		// initialize chip select pin
    _cmnd_data->write(1);	// initialize command/data pin
    _update_flag = 0;		// initialize update semaphore
    
    // Reset all registers to POR values
//    result = DOGS102::writeCommand(SOFTRESET);
    osDelay(50);

    // send initial setup commands to power up the display
    result |= DOGS102::writeCommand(SETSCROLLLINE,0x00);	// set scroll line to 0
    result |= DOGS102::writeCommand(SETSEGDIR,0x01);		// set reverse seg direction
    result |= DOGS102::writeCommand(SETCOMDIR,0x00);		// set normal com direction
    result |= DOGS102::writeCommand(SETALLPIXELON,0x00);	// disable all pixel on mode
    result |= DOGS102::writeCommand(SETINVDISP,0x00);		// Turn display inverse off
    result |= DOGS102::writeCommand(SETLCDBIAS,0x00);		// set bias ratio to 1/9
    result |= DOGS102::writeCommand(SETPWRCTRL,0x07);		// turn on booster,regulator and follower
    result |= DOGS102::writeCommand(SETVLCDRESRATIO,0x07);	// Set resistor ratio tomax
    result |= DOGS102::writeCommand(SETELECVOL,0x10);		// set contrast to 32 out of 63
    result |= DOGS102::writeCommand(SETAPROGCTRL,0x83);		// enable wrap around bits
    result |= DOGS102::writeCommand(SETDISPEN,0x01);		// set display enable bit

    DOGS102::clearBuffer();

    if(result != 0)
    {
        debug("%s %d: ILS29011:init failed\n\r", __FILE__, __LINE__);
    }
    
     return result;
}

uint8_t DOGS102::writeCommand(uint8_t const reg, uint8_t const data) const
{
    uint8_t buf;
    uint8_t result = 0;

    switch (reg)		// setup data byte for specific command register write
    {
    case SETCOLADDRLSB :
    	// use COLMASK for data
   		buf = SETCOLADDRLSB | (data & COLMASK);
   		break;
    case SETCOLADDRMSB :
    	// use COLMASK for data
   		buf = SETCOLADDRMSB | (data & COLMASK);
    	break;
    case SETPWRCTRL :
    	// use PCMASK for data
    	buf = SETPWRCTRL | (data & PCMASK);
    	break;
    case SETSCROLLLINE :
    	// use SLMASK for data
    	buf = SETSCROLLLINE | (data & SLMASK);
    	break;
    case SETPGADDR :
    	// use COLMASK for data
   		buf = SETPGADDR | (data & COLMASK);
    	break;
    case SETVLCDRESRATIO :
    	// use PCMASK for data
   		buf = SETVLCDRESRATIO | (data & PCMASK);
    	break;
    case SETELECVOL :
    	// double byte command use SLMASK for data
   		buf = data & SLMASK;
    	break;
    case SETALLPIXELON :
		// use LSBMASK for data
   		buf = SETALLPIXELON | (data & LSBMASK);
		break;
    case SETINVDISP :
		// use LSBMASK for data
   		buf = SETINVDISP | (data & LSBMASK);
		break;
    case SETDISPEN :
		// use LSBMASK for data
   		buf = SETDISPEN | (data & LSBMASK);
		break;
    case SETSEGDIR :
		// use LSBMASK for data
   		buf = SETSEGDIR | (data & LSBMASK);
		break;
    case SETLCDBIAS :
		// use LSBMASK for data
   		buf = SETLCDBIAS | (data & LSBMASK);
		break;
    case SETCOMDIR :
    	// use LC1MASK for data
   		buf = SETCOMDIR | ((data << 3) & COLMASK);
    	break;
    case SOFTRESET :
    	// no data mask needed
   		buf = SOFTRESET;
    	break;
    case SETAPROGCTRL :
    	// Double byte command use WAMASK and TCMASK for data
   		buf = data & (COLMASK | TCMASK);
    	break;
    default :
    	debug("Command Register not valid\n\r");
    	result = 1;
    }

    if (result == 0)
    {
        _spi->frequency(4000000);
        _spi->format(8,3);		// 8bit transfers, SPI mode 3

        _lcd_cs->write (0);			// enable LCD SPI interface
    	_cmnd_data->write(0);		// set to command mode

    	switch (reg)				// send first byte of double byte command for these register
    	{
    	case SETELECVOL :
    	case SETAPROGCTRL :
        	_spi->write(reg);
        	break;
        }

    	_spi->write(buf);			// send command register

    	_cmnd_data->write(1);		// set back to data mode
    	_lcd_cs->write(1);			// disable LCD SPI Interface

    }
    
    if(result != 0)
    {
        debug("DOGS102:writeCommand failed\n\r");
    }
    
    return result;
}

void DOGS102::writeData(const uint8_t* data, uint8_t count) const
{
    uint8_t result = 0;
    uint8_t i;
    
    _spi->frequency(4000000);
    _spi->format(8,3);		// 8bit transfers, SPI mode 3

    _lcd_cs->write(0);			// enable LCD SPI interface
	i = 0;						// initialize transfer counter

    do
    {
        _spi->write(data[i]);
        i++;
    } while ((result == 0) && (i <= count)) ;

	_lcd_cs->write(1);			// disable LCD SPI interface
    
    return;
}

void DOGS102::sendBuffer(const uint8_t* buffer)
{
    //debug("Sending LCD Buffer\n");
    for (int i=0; i<LCDPAGES; i++)
    {
    	DOGS102::setCursor(0,i*8);
    	DOGS102::writeData(buffer + i*LCDWIDTH, LCDWIDTH);
    }
}