A basic basic basic driver for 2x16 HD44780 based displays using the four wire interface.

Dependents:   lcdLibraryTest generado_final

lcd.cpp

Committer:
mattegan
Date:
2013-11-08
Revision:
0:0db835052212

File content as of revision 0:0db835052212:

#include "lcd.h"

//Public Methods
//////////////////////////////////////////////////////////////////////////////////////////

lcd::lcd(PinName _rs, PinName _rw, PinName _e, PinName _db4, PinName _db5, PinName _db6, PinName _db7):
    registerSelect(_rs), readWrite(_rw), enable(_e), dataBus(_db4, _db5, _db6, _db7) {
   
    currentMode = klcdReadMode;
    setMode(klcdWriteMode);
     
    //wait for more than 15ms after Vcc rises to 4.5v
    wait_ms(20);
    
    //function set (interface is 8 bits long)
    writeNibble(klcdInstructionRegister, 0x3);
    
    //wait for more than 4.1ms
    wait_ms(4.5);
    
    //function set(interface is 8 bits long)
    writeNibble(klcdInstructionRegister, 0x3);
    
    //wait for more than 100us
    wait_us(150);
    
    //function set (interface is 8 bits long)
    writeNibble(klcdInstructionRegister, 0x3);
    wait_us(90);
    
    //function set (interface is 4 bits long)
    writeNibble(klcdInstructionRegister, 0x2);
    wait_us(90);
        
    //function set (interface is 4 bits long) set display lines and character font
    //      2 lines, 5x8 character font (1 0 0 0)
    writeByte(klcdInstructionRegister, 0x28, false);
    wait_us(90);
      
    //display off
    writeByte(klcdInstructionRegister, 0x08, false);
    wait_us(90);
        
    //display clear
    writeByte(klcdInstructionRegister, 0x01, false);
    wait_ms(2.0);
       
    //entry mode set
    writeByte(klcdInstructionRegister, 0x0C, false);   
    wait_us(90);
   
    //clear display, and turn on
    writeByte(klcdInstructionRegister, 0x01);
    writeByte(klcdInstructionRegister, 0x0C);
    
    //turn shifting off
    writeByte(klcdInstructionRegister, 0x04);
    
    row = 0;
    column = 0;
    
}

void lcd::clear() {
    //clear screen -> 0 0 (0 0 0 0 0 0 0 1)
    writeByte(klcdInstructionRegister, 0x01);
    locate(0, 0);
}

void lcd::locate(int _row, int _column) {
    row = _row;
    column = _column;
}

void lcd::locateCharacter(int _row, int _column, int _char) {
    //set DDRAM address -> 0 0 (1 a a a a a a a)
    //write DDRAM -> 1 0 (d d d d d d d d);
    writeByte(klcdInstructionRegister, 0x80 + (_row * 0x40) + _column);
    writeByte(klcdDataRegister, _char);
}

void lcd::defineCharacter(int _char, int _charData[8]) {
    if(_char < 0 || _char > 7) return;
    //write CGRAM address 0 0 (0 1 a a a a a a)
    //  first three bits are character (0-7)
    //  second three bits are the row in the character
    writeByte(klcdInstructionRegister, 0x40 + (_char << 3));
    for(int row = 0; row < 8; row++) {
        //write CGRAM data 1 0 (d d d d d d d d)
        writeByte(klcdDataRegister, _charData[row]);
    }
}

//Protected Methods
//////////////////////////////////////////////////////////////////////////////////////////
void lcd::setMode(int _mode) {
    //since changing the databus mode takes time only do it
    //  if we're not in the mode already, this might be
    //  fuzzy science (read, not actually accurate), but it
    //  can't hurt
    if(_mode != currentMode) {
        readWrite = _mode;
        currentMode = _mode;
        switch(_mode) {
            case klcdWriteMode: {
                dataBus.output();
                break;
            }
            case klcdReadMode: {
                dataBus.input();
                break;
            }
        }
    }
}

void lcd::waitUntilNotBusy() {
    setMode(klcdReadMode);
    registerSelect = klcdInstructionRegister;
    wait_us(5);
    enable = klcdEnableHigh;
    wait_us(0.5);
    while(dataBus.read() >> 3) {};
    enable = klcdEnableLow;
    wait_us(0.6);
    readNibble(klcdInstructionRegister);
    return;
}

void lcd::writeByte(int _reg, int _byte, bool _wait) {
    //write the most significant four bytes first
    writeNibble(_reg, _byte >> 4);
    writeNibble(_reg, _byte);
    if(_wait) {
        waitUntilNotBusy();
    }
}

//this is useless at the moment, not sure how I want to structure
//  reading bytes with the four wire interface, as one read is
//  technically reading the busy flag and internal address counter,
//  another is actually reading bytes from CGRAM or DDRAM (which isn't
//  all that useful actually)
int lcd::readNibble(int _reg) {
    setMode(klcdReadMode);
    registerSelect = _reg;
    
    //wait more than 60ns for address set up, then bring enable high
    wait_us(0.1);
    enable = klcdEnableHigh;
    //wait for the enable pulse width to elapse (450ns), by which time the data
    //  should be present on the bus, then bring enable low, and wait
    //  till the end of the enable cycle
    wait_us(0.5);
    int nibble = dataBus.read();
    enable = klcdEnableLow;
    wait_us(0.6);
    return nibble;
}

//write a four bit nibble onto the bus with correct delays
void lcd::writeNibble(int _reg, int _nibble) {
    setMode(klcdWriteMode);
    registerSelect = _reg;
    
    dataBus = _nibble;
    //wait for more than 60ns for address set up, then bring enable high
    wait_us(0.1);
    enable = klcdEnableHigh;
    //wait for more than 450ns for enable pulse width, then bring enable low
    wait_us(0.5);
    enable = klcdEnableLow;
    //wait for more than 550ns for the completion of the enable cycle
    wait_us(0.6);
}


//Stream Implementation
//////////////////////////////////////////////////////////////////////////////////////
int lcd::_putc(int _char) {
    if(_char == '\n') {
        column = 0;
        row = (++row % 2);
    } else {
        locateCharacter(row, column, _char);
        column++;
    }    
    return _char;
}

//not respoinding to getc (yet, this would require reading out of the 
// DDRAM of the display, or caching
int lcd::_getc() {
    return -1;
}