252 lines
7.5 KiB
C++
252 lines
7.5 KiB
C++
// ---------------------------------------------------------------------------
|
|
// Created/Adapted by Stephen Erisman 2013-07-06
|
|
// Copyright 2013 - Under creative commons license 3.0:
|
|
// Attribution-ShareAlike CC BY-SA
|
|
//
|
|
// This software is furnished "as is", without technical support, and with no
|
|
// warranty, express or implied, as to its usefulness for any purpose.
|
|
//
|
|
// Thread Safe: No
|
|
// Extendable: Yes
|
|
//
|
|
// @file LiquidCrystal_SR1W.cpp
|
|
// Connects a hd44780 LCD using 1 pin from the Arduino, via an 8-bit Latching
|
|
// ShiftRegister (SR1W from now on).
|
|
//
|
|
// @brief
|
|
// This is an optimized implementation of the 1-wire shift concept developed by
|
|
// Roman Black (http://www.romanblack.com/shift1.htm) that also makes use of
|
|
// (and merges) the diode-resistor AND "gate" concept (http://www.rentron.com/Myke1.htm)
|
|
// as well as introducing some new and original ideas (particularly how HW_CLEAR works).
|
|
//
|
|
//
|
|
// See the corresponding SR1W header file for full details.
|
|
//
|
|
// History
|
|
// 2013.07.31 serisman - fixed potential interrupt bug and made more performance optimizations
|
|
// 2013.07.10 serisman - more performance optimizations and modified the HW_CLEAR circuit a bit
|
|
// 2013.07.09 serisman - added an even faster version that performs the clear in hardware
|
|
// 2013.07.08 serisman - changed code to shift data MSB first to match SR2W
|
|
// 2013.07.07 serisman - major speed optimization
|
|
// 2013.07.06 serisman - created/modified from SR2W and FastIO sources to create SR1W
|
|
// @author S. Erisman - arduino@serisman.com
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#include "LiquidCrystal_SR1W.h"
|
|
|
|
// CONSTRUCTORS
|
|
// ---------------------------------------------------------------------------
|
|
// Assuming 1 line 8 pixel high font
|
|
LiquidCrystal_SR1W::LiquidCrystal_SR1W (uint8_t srdata, t_sr1w_circuitType circuitType, t_backlighPol blpol)
|
|
{
|
|
init ( srdata, circuitType, blpol, 1, 0 );
|
|
}
|
|
|
|
// PRIVATE METHODS
|
|
// ---------------------------------------------------------------------------
|
|
|
|
//
|
|
// init
|
|
void LiquidCrystal_SR1W::init(uint8_t srdata, t_sr1w_circuitType circuitType, t_backlighPol blpol, uint8_t lines, uint8_t font)
|
|
{
|
|
_srRegister = fio_pinToOutputRegister(srdata);
|
|
_srMask = fio_pinToBit(srdata);
|
|
|
|
_circuitType = circuitType;
|
|
|
|
_blPolarity = blpol;
|
|
|
|
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
|
|
|
|
clearSR();
|
|
|
|
backlight(); // set default backlight state to on
|
|
}
|
|
|
|
//
|
|
// clearSR
|
|
uint8_t LiquidCrystal_SR1W::clearSR()
|
|
{
|
|
uint8_t numDelays = 0;
|
|
|
|
// Store these as local variables for extra performance (and smaller compiled sketch size)
|
|
fio_register srRegister = _srRegister;
|
|
fio_bit srMask = _srMask;
|
|
|
|
// Set the Serial PIN to a LOW state
|
|
SR1W_ATOMIC_WRITE_LOW(srRegister, srMask);
|
|
|
|
// We need to delay to make sure the Data and Latch/EN capacitors are fully discharged
|
|
// This also triggers the EN pin because of the falling edge.
|
|
SR1W_DELAY();
|
|
|
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
|
{
|
|
// Pre-calculate these values for extra performance and to make sure the clock pulse is as quick as possible
|
|
fio_bit reg_val = *srRegister;
|
|
fio_bit bit_low = reg_val & ~srMask;
|
|
fio_bit bit_high = reg_val | srMask;
|
|
|
|
// Clear the shift register (without triggering the Latch/EN pins)
|
|
// We only need to shift 7 bits here because the subsequent HIGH transistion will also shift a '0' in.
|
|
for (int8_t i = 6; i>=0; i--)
|
|
{
|
|
// Shift in a '0' (NOTE: This clock pulse needs to execute as quickly as possible)
|
|
*srRegister = bit_high;
|
|
*srRegister = bit_low;
|
|
}
|
|
|
|
// Set the Serial PIN to a HIGH state so the next nibble/byte can be loaded
|
|
// This also shifts the 8th '0' bit in.
|
|
*srRegister = bit_high;
|
|
}
|
|
|
|
// Give the Data capacitor a chance to fully charge
|
|
SR1W_DELAY();
|
|
|
|
return numDelays;
|
|
}
|
|
|
|
//
|
|
// loadSR
|
|
uint8_t LiquidCrystal_SR1W::loadSR(uint8_t val)
|
|
{
|
|
uint8_t numDelays = 0;
|
|
|
|
// Store these as local variables for extra performance (and smaller compiled sketch size)
|
|
fio_register srRegister = _srRegister;
|
|
fio_bit srMask = _srMask;
|
|
|
|
// NOTE: This assumes the Serial PIN is already HIGH and the Data capacitor is fully charged
|
|
uint8_t previousBit = 1;
|
|
|
|
// Send the data to the shift register (MSB first)
|
|
for (int8_t i = 7; i>=0; i--)
|
|
{
|
|
if (val & 0x80)
|
|
{
|
|
if (previousBit == 0)
|
|
{
|
|
// We need to make sure the Data capacitor has fully recharged
|
|
SR1W_DELAY();
|
|
}
|
|
|
|
previousBit = 1;
|
|
|
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
|
{
|
|
// Pre-calculate these values to make sure the clock pulse is as quick as possible
|
|
fio_bit reg_val = *srRegister;
|
|
fio_bit bit_low = reg_val & ~srMask;
|
|
fio_bit bit_high = reg_val | srMask;
|
|
|
|
// Shift in a '1' (NOTE: This clock pulse needs to execute as quickly as possible)
|
|
*srRegister = bit_low;
|
|
*srRegister = bit_high;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Shift in a '0'
|
|
SR1W_ATOMIC_WRITE_LOW(srRegister, srMask);
|
|
|
|
// We need to make sure the Data capacitor has fully discharged
|
|
SR1W_DELAY();
|
|
|
|
previousBit = 0;
|
|
|
|
SR1W_ATOMIC_WRITE_HIGH(srRegister, srMask);
|
|
}
|
|
val <<= 1;
|
|
}
|
|
|
|
// NOTE: Serial PIN is currently HIGH
|
|
|
|
// For SW_CLEAR, we need to delay to make sure the Latch/EN capacitor is fully charged.
|
|
// This triggers the Latch pin because of the rising edge.
|
|
// For HW_CLEAR, we need to delay to give the hardware time to perform the clear.
|
|
// This also gives the Data capacitor a chance to fully charge
|
|
SR1W_DELAY();
|
|
|
|
if (_circuitType == SW_CLEAR)
|
|
{
|
|
// Clear the shift register to get ready for the next nibble/byte
|
|
// This also discharges the Latch/EN capacitor which finally triggers the EN pin because of the falling edge.
|
|
numDelays += clearSR();
|
|
}
|
|
else
|
|
{
|
|
// For some reason HW_CLEAR isn't totally stable unless we delay a little bit more.
|
|
// TODO... figure this out...
|
|
SR1W_DELAY();
|
|
}
|
|
|
|
return numDelays;
|
|
}
|
|
|
|
// PUBLIC METHODS
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
/************ low level data pushing commands **********/
|
|
//
|
|
// send
|
|
void LiquidCrystal_SR1W::send(uint8_t value, uint8_t mode)
|
|
{
|
|
uint8_t numDelays = 0;
|
|
|
|
uint8_t data;
|
|
|
|
if ( mode != FOUR_BITS )
|
|
{
|
|
// upper nibble
|
|
data = ( mode == DATA ) ? SR1W_RS_MASK : 0;
|
|
data |= SR1W_EN_MASK | SR1W_UNUSED_MASK;
|
|
data |= _blMask;
|
|
|
|
if (value & _BV(4)) data |= SR1W_D4_MASK;
|
|
if (value & _BV(5)) data |= SR1W_D5_MASK;
|
|
if (value & _BV(6)) data |= SR1W_D6_MASK;
|
|
if (value & _BV(7)) data |= SR1W_D7_MASK;
|
|
|
|
numDelays += loadSR(data);
|
|
}
|
|
|
|
// lower nibble
|
|
data = ( mode == DATA ) ? SR1W_RS_MASK : 0;
|
|
data |= SR1W_EN_MASK | SR1W_UNUSED_MASK;
|
|
data |= _blMask;
|
|
|
|
if (value & _BV(0)) data |= SR1W_D4_MASK;
|
|
if (value & _BV(1)) data |= SR1W_D5_MASK;
|
|
if (value & _BV(2)) data |= SR1W_D6_MASK;
|
|
if (value & _BV(3)) data |= SR1W_D7_MASK;
|
|
|
|
numDelays += loadSR(data);
|
|
|
|
// Make sure we wait at least 40 uS between bytes.
|
|
unsigned int totalDelay = numDelays * SR1W_DELAY_US;
|
|
if (totalDelay < 40)
|
|
delayMicroseconds(40 - totalDelay);
|
|
}
|
|
|
|
//
|
|
// setBacklight
|
|
void LiquidCrystal_SR1W::setBacklight ( uint8_t value )
|
|
{
|
|
// Check for polarity to configure mask accordingly
|
|
// ----------------------------------------------------------
|
|
if ( ((_blPolarity == POSITIVE) && (value > 0)) ||
|
|
((_blPolarity == NEGATIVE ) && ( value == 0 )) )
|
|
{
|
|
_blMask = SR1W_BL_MASK;
|
|
}
|
|
else
|
|
{
|
|
_blMask = 0;
|
|
}
|
|
|
|
// Send a dummy (non-existant) command to allow the backlight PIN to be latched.
|
|
// The seems to be safe because the LCD appears to treat this as a NOP.
|
|
send(0, COMMAND);
|
|
} |