#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "SpiLCD.h"
//#include <stdio.h>
//#include <inttypes.h>
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡ BIT OPERATIONS ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
#define BOOL(q) (!(!(q)))
#define BIT_SET(a,b) ((a)|=(1<<(b)))
#define BIT_CLR(a,b) ((a)&=~(1<<(b)))
#define BIT_FLP(a,b) ((a)^=(1<<(b)))
#define BIT_CHK(a,b) BOOL((a)&(1<<(b)))
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡ LCD OPERATIONS ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
#define lcd_RS_Set() BIT_CLR(spi_data,spi_rs_bit)
#define lcd_RS_Clr() BIT_SET(spi_data,spi_rs_bit)
//≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
SpiLCD::SpiLCD(uint8_t sdo, uint8_t clk, uint8_t le){ //lcd backlight bit is 0 lcd rs bit is 2
_clk = clk;
_sdo = sdo;
_le = le;
pinMode(_sdo, OUTPUT);
pinMode(_clk, OUTPUT);
pinMode(_le, OUTPUT);
init(0, 2);
}
SpiLCD::SpiLCD(uint8_t sdo, uint8_t clk, uint8_t le, uint8_t bl, uint8_t rs){
_clk = clk;
_sdo = sdo;
_le = le;
pinMode(_sdo, OUTPUT);
pinMode(_clk, OUTPUT);
pinMode(_le, OUTPUT);
init(bl, rs);
}
void SpiLCD::setFunction(bool dl, bool n, bool font){
lcd_function = (1<<5)|(dl<<4)|(n<<3)|(font<<2);
}
void SpiLCD::setMode(bool id, bool shift){
lcd_mode = (1<<2)|(id<<1)|(shift<<0);
}
void SpiLCD::setDisplay(bool display, bool cursor, bool blinking){
lcd_display = (1<<3)|(display<<2)|(cursor<<1)|(blinking<<0);
}
void SpiLCD::init(uint8_t bit_backlight, uint8_t bit_rs){
spi_rs_bit = bit_rs;
spi_bl_bit = bit_backlight;
}
void SpiLCD::begin(uint8_t cols, uint8_t lines, uint8_t dotsize){
if (lines > 1) {
BIT_SET(lcd_function,FNC_LINE);
}
numlines = lines;
setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);
// for some 1 line displays you can select a 10 pixel high font
if ((dotsize != 0) && (lines == 1)) {
BIT_SET(lcd_function,FNC_FONTSIZE);
}
// Power on LCD Delay > 45ms
delay(100);
//***Reset Sequence***
lcd_RS_Clr();
spiSend(RESET_DISPLAY << 4);
pulseEnable();
delayMicroseconds(4500);
spiSend(RESET_DISPLAY << 4);
pulseEnable();
delayMicroseconds(160);
spiSend(RESET_DISPLAY << 4);
pulseEnable();
delayMicroseconds(80);
spiSend(RETURN_HOME << 4);
pulseEnable();
delayMicroseconds(80);
//***End Reset Sequence***
command(lcd_function);
command(lcd_mode);
command(lcd_display);
clear();
home();
}
void SpiLCD::backlight(){
setBacklight();
spiSend(0x00);
}
void SpiLCD::noBacklight(){
resetBacklight();
spiSend(0x00);
}
void SpiLCD::setBacklight(){
BIT_SET(spi_data,spi_bl_bit);
}
void SpiLCD::resetBacklight(){
BIT_CLR(spi_data,spi_bl_bit);
}
void SpiLCD::toggleBacklight(){
BIT_FLP(spi_data,spi_bl_bit) ;
}
bool SpiLCD::getBacklight(){
return BIT_CHK(spi_data,spi_bl_bit);
}
void SpiLCD::clear(){
command(CLEAR_DISPLAY);
delayMicroseconds(1640); // this command takes a long time!
}
void SpiLCD::home(){
command(RETURN_HOME);
delayMicroseconds(1640); // this command takes a long time!
}
// Turn the display on/off (quickly)
void SpiLCD::noDisplay() {
BIT_CLR(lcd_display,DISPLAY);
command(lcd_display);
}
void SpiLCD::display() {
BIT_SET(lcd_display,DISPLAY);
command(lcd_display);
}
// Turns the underline cursor on/off
void SpiLCD::noCursor() {
BIT_CLR(lcd_display,CURSOR);
command(lcd_display);
}
void SpiLCD::cursor() {
BIT_SET(lcd_display,CURSOR);
command(lcd_display);
}
// Turn on and off the blinking cursor
void SpiLCD::noBlink() {
BIT_CLR(lcd_display,BLINK);
command(lcd_display);
}
void SpiLCD::blink() {
BIT_SET(lcd_display,BLINK);
command(lcd_display);
}
// These commands scroll the display without changing the RAM
void SpiLCD::scrollDisplayLeft(void) {
BIT_SET(lcd_cursor,CUR_SHIFT);
BIT_CLR(lcd_cursor,CUR_RIGHT);
command(lcd_cursor);
}
void SpiLCD::scrollDisplayRight(void) {
BIT_SET(lcd_cursor,CUR_SHIFT);
BIT_SET(lcd_cursor,CUR_RIGHT);
command(lcd_cursor);
}
// This is for cursor follows the display shift
void SpiLCD::leftToRight(void) {
BIT_CLR(lcd_cursor,CUR_SHIFT);
BIT_CLR(lcd_cursor,CUR_RIGHT);
command(lcd_cursor);
}
void SpiLCD::rightToLeft(void) {
BIT_CLR(lcd_cursor,CUR_SHIFT);
BIT_SET(lcd_cursor,CUR_RIGHT);
command(lcd_cursor);
}
// This will autoscroll text from the cursor
void SpiLCD::autoscroll(void) {
BIT_SET(lcd_mode,MODE_SHIFT);
command(lcd_mode);
}
void SpiLCD::noAutoscroll(void) {
BIT_CLR(lcd_mode,MODE_SHIFT);
command(lcd_mode);
}
void SpiLCD::createChar(char location, char charmap[]) {
location &= 0x7; // we only have 8 locations 0-7
command(CGRAM | (location << 3));
for (uint8_t i=0; i<8; i++) {
write(charmap[i]);
}
}
void SpiLCD::setRowOffsets(uint8_t row0, uint8_t row1, uint8_t row2, uint8_t row3){
lcd_row_offset[0] = row0;
lcd_row_offset[1] = row1;
lcd_row_offset[2] = row2;
lcd_row_offset[3] = row3;
}
void SpiLCD::setCursor(uint8_t col, uint8_t row){
const size_t max_lines = sizeof(lcd_row_offset) / sizeof(*lcd_row_offset);
if ( row >= max_lines ) {
row = max_lines - 1; // we count rows starting w/0
}
if ( row >= numlines ) {
row = numlines - 1; // we count rows starting w/0
}
command(DDRAM | (col + lcd_row_offset[row]));
}
inline void SpiLCD::command(char value){
lcd_RS_Set();
dataSend(value);
}
inline size_t SpiLCD::write(char value) {
lcd_RS_Clr();
dataSend(value);
return 1;
}
void SpiLCD::dataSend(char data){
spiSend(data & 0xf0);
pulseEnable();
spiSend(data << 4 & 0xf0);
pulseEnable();
}
void SpiLCD::spiSend(char data){
data |= spi_data;
digitalWrite(_le, LOW);
delayMicroseconds(2);
for(int8_t i = 7; i >= 0 ; i--){
digitalWrite(_sdo, (data >> i) & 0x01);
digitalWrite(_clk, HIGH);
digitalWrite(_clk, LOW);
}
digitalWrite(_sdo, LOW);
delayMicroseconds(2);
digitalWrite(_le, HIGH);
}
void SpiLCD::pulseEnable(){
delayMicroseconds(2);
digitalWrite(_sdo, HIGH);
delayMicroseconds(60);
digitalWrite(_sdo, LOW);
}