kub-kar-timer/lib/NewLiquidCrystal/LiquidCrystal_SR1W.cpp

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);
}