/******************************************************************************
RomanEXPCOG.c Open-source - Oct 2009 - www.RomanBlack.com
These are some small routines to use the EasyPIC6 port expander
and also drive the EasyPIC6 COG LCD.
They are nothing fancy, but they do compile MUCH smaller so
may be of use if you have limited ROM in an EasyPIC6 application.
Also, they will work fine on the old MikroC C compiler (where the
MikroE libraries require the new MikroC PRO compiler).
Include this one file at the top of your source code and then you
can use these simple port expander and COG LCD functions.
For use with EasyPIC6 you don't need to edit this file!!
NOTE! turn the EasyPIC6 port expander green LEDs OFF.
******************************************************************************/
// define port expander pins for EasyPIC6;
#define REXP_PIN_CS PORTA.F2
#define REXP_PIN_RST PORTA.F3
#define REXP_PIN_SCK PORTC.F3
#define REXP_PIN_SO PORTC.F4 (SO is not used)
#define REXP_PIN_SI PORTC.F5
// define EasyPIC6 COG LCD details
#define REXP_LCD_HEIGHT 2 // char LCD display lines; 1,2,4, allowed
#define REXP_LCD_WIDTH 16 // char LCD width; 8,16,20,40 allowed
#define RCMD 0 // used in RomanEXP_lcd_byte()
#define RCHAR 1
unsigned char REXP_trashdata; // used inside functions
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// this makes an 8 PIC instruction delay (including call)
void tiny_delay(void)
{
asm goto $+1; // 2 nops
asm goto $+1;
asm goto $+1;
}
// wrap compiler inline delay as a function to save code size.
void delay_6mS(void)
{
Delay_ms(6);
}
//=============================================================================
// RomanEXP_SEND_BYTE sends SPI byte to port expander IC
//=============================================================================
void RomanEXP_Send_Byte(unsigned char xbyte)
{
// this sends one SPI byte out the selected pins
unsigned char REXP_bit;
// loop and send 8 SPI bits
REXP_bit = 8;
while(REXP_bit)
{
if(xbyte.F7) REXP_PIN_SI = 1; // prep data bit on SPI bit SI
else REXP_PIN_SI = 0;
tiny_delay();
REXP_PIN_SCK = 1; // make a clock pulse
tiny_delay();
REXP_PIN_SCK = 0; // end clock pulse
tiny_delay();
xbyte = (xbyte << 1); // prep next bit
REXP_bit--;
}
}
//-----------------------------------------------------------------------------
//=============================================================================
// RomanEXP_LCD_BYTE sends a BYTE to COG LCD
//=============================================================================
void RomanEXP_Lcd_Byte(unsigned char REXP_char, unsigned char REXP_cmd)
{
//-----------------------------------------------------
// this sends 2 4bit nibbles to the LCD, and each
// nibble is clocked by a \ edge of the LCD E pin.
// the 6 LCD pins are controlled by the EasyPIC6 port
// expander IC.
//-----------------------------------------------------
// send top nibble first
REXP_trashdata = (REXP_char & 0xF0); // get top 4 bits
REXP_trashdata.F3 = 1; // set E pin hi
if(REXP_cmd) REXP_trashdata.F2 = 1; // set RS (command=0/char=1)
RomanEXP_Send_Byte(REXP_trashdata); // send data and E hi
REXP_trashdata.F3 = 0; // set E pin lo
RomanEXP_Send_Byte(REXP_trashdata); // send data and E \ clock pulse
// now send bottom nibble
REXP_trashdata = (REXP_char << 4);
REXP_trashdata.F3 = 1;
if(REXP_cmd) REXP_trashdata.F2 = 1; // set RS (command=0/char=1)
RomanEXP_Send_Byte(REXP_trashdata);
REXP_trashdata.F3 = 0;
RomanEXP_Send_Byte(REXP_trashdata);
}
//-----------------------------------------------------------------------------
//=============================================================================
// RomanEXP_LCD_MOVE moves LCD cursor to line 0-3 , col 0-39
//=============================================================================
void RomanEXP_Lcd_Move(unsigned char REXP_line, unsigned char REXP_col)
{
//-----------------------------------------------------
// formats line, col into the 1byte command needed
// by the LCD and send it to the LCD.
// LCD command; 1aaaaaaa
//
// This should work for all char LCD sizes;
// LCD display HEIGHT; 1, 2, 4
// LCD display WIDTH; 8, 16, 20, 40
//-----------------------------------------------------
// safe limit to the defined char LCD screen size
if(REXP_line >= REXP_LCD_HEIGHT) REXP_line = (REXP_LCD_HEIGHT - 1);
if(REXP_col >= REXP_LCD_WIDTH) REXP_col = (REXP_LCD_WIDTH - 1);
// (now re-use variable SH1_col to hold the command)
// for 4 line displays only; if line >1 fix the address
if(REXP_line.F1) REXP_col += REXP_LCD_WIDTH;
// for 2 and 4 line displays; set address bit6 if on ODD line
if(REXP_line.F0) REXP_col.F6 = 1;
// set bit7, makes this byte into an LCD move command
REXP_col.F7 = 1;
// finally send that command to the LCD
RomanEXP_Lcd_Byte(REXP_col,RCMD);
}
//-----------------------------------------------------------------------------
//=============================================================================
// RomanEXP_LCD_OUT send text string to COG LCD
//=============================================================================
void RomanEXP_Lcd_Out(unsigned char sline, unsigned char scol,
unsigned char *stext)
{
//-----------------------------------------------------
unsigned char scount;
// move display cursor
RomanEXP_Lcd_Move(sline,scol);
// now just print each char until NULL found
scount = 0;
while(stext[scount])
{
RomanEXP_Lcd_Byte(stext[scount],RCHAR);
scount++;
}
}
//-----------------------------------------------------------------------------
//=============================================================================
// RomanEXP_LCD_CHAR send one text character to COG LCD
//=============================================================================
void RomanEXP_Lcd_Char(unsigned char schar)
{
//-----------------------------------------------------
RomanEXP_Lcd_Byte(schar,RCHAR);
}
//-----------------------------------------------------------------------------
//=============================================================================
// RomanEXP_LCD_INIT initialise COG LCD
//=============================================================================
void RomanEXP_Lcd_Init(void)
{
//-----------------------------------------------------
// The COG LCD operates in 4bit data mode. (6 wires)
// It is controlled from the EasyPIC6 port expander IC.
//-----------------------------------------------------
// manually send first control nibble to LCD!
RomanEXP_Send_Byte(0b00101000); // 0010 code for init, E=1, RS=0
RomanEXP_Send_Byte(0b00100000); // make E low, is LCD \ clock pulse
delay_6mS(); // 6mS delay needed by LCD
//-----------------------------------------------------
// now we can send commands to LCD as bytes
//-----------------------------------------------------
// Cmd, set interface length
RomanEXP_Lcd_Byte(0x28,RCMD); // 2=4bit, 8=2line display
delay_6mS();
// Cmd, display on/off control
RomanEXP_Lcd_Byte(0x0C,RCMD); // disp on, cursor off, block off.
// Cmd, clear display (is the slow command)
RomanEXP_Lcd_Byte(0x01,RCMD); // clear display
delay_6mS(); // 6mS is long enough even for old LCDs
// Cmd, entry mode set
RomanEXP_Lcd_Byte(0x06,RCMD); // 6=move cursor right, but don't scroll display
}
//-----------------------------------------------------------------------------
//=============================================================================
// RomanEXP_INIT initialises the EasyPIC6 port EXPander
//=============================================================================
void RomanEXP_Init(void)
{
//-------------------------------------------------------
// this initialises the EasyPIC port EXPander very simply,
// to set port1 as output and use port1 only.
//-------------------------------------------------------
REXP_PIN_CS = 1; // make CS pin hi, disables IC
REXP_PIN_SCK = 0; // SCK low before we start
delay_6mS();
REXP_PIN_RST = 1; // reset hi, brings IC out of reset
delay_6mS();
// load IOCON, sets up port expander IC features
REXP_PIN_CS = 0;
RomanEXP_Send_Byte(0x40); // IC address
RomanEXP_Send_Byte(0x0A); // IOCON address
RomanEXP_Send_Byte(0b10100000); // setup IOCON; bank=1, SEQOP=off, HAEN=0
REXP_PIN_CS = 1;
// set PORT1(B) as output
REXP_PIN_CS = 0;
RomanEXP_Send_Byte(0x40); // IC address
RomanEXP_Send_Byte(0x10); // IODIRB address
RomanEXP_Send_Byte(0x00); // all 8 pins as outputs
REXP_PIN_CS = 1;
// set OLAT as the receiving register
REXP_PIN_CS = 0;
RomanEXP_Send_Byte(0x40); // IC address
RomanEXP_Send_Byte(0x1A); // OLATB address
// now every byte sent to expander IC will just write to port1 pins!
RomanEXP_Send_Byte(0x00); // set PORT1 pins LOW to start
}
//-----------------------------------------------------------------------------