// --------------------------------------------------------------------------- // 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. // // @file LiquidCrystal_SR1W.h // Connects a hd44780 LCD using 1 pin from the Arduino, via an 8-bit Latching // ShiftRegister (SR1W from now on). // // @brief // This is the 1 wire shift register interface class for the LCD library // // The functionality provided by this class and its base class is a superset of // the original functionality of the Arduino LiquidCrystal library and can // be used as such. // See the LCD class for a full description of the API functions available. // // It works with a 8-bit latched, no-tristate, unidirectional SIPO (Serial-In-Parallel-Out) // shift register, and an hd44780 LCD in 4-bit mode. // The 74HC595 shift register has been tested. // // // 1 Pin required from the Arduino: // - Serial PIN: // The Serial PIN is wired directly to the shift register's Clock PIN and its // unaltered signal directly triggers the Clock on every LOW to HIGH transition. // // Additionally, the Serial PIN is wired through a resistor capacitor (RC) filter to // the shift register's Data PIN. During a quick transition of the Serial PIN the // RC filter will maintain the Data PIN's previous value because the capacitor isn't // given enough time to charge (or discharge) to the alternate state. If the transition // is held for enough time, however, the RC capacitor will charge (or discharge) and the // value seen by the Data PIN will have changed state. // // There are two circuit versions that behave differently for Latch, Enable, and Clear: // // HW_CLEAR version: // In this version the shift register's Latch and LCD's Enable PINs are wired directly to // the shift register's Q'H output. The shift register's /Clear PIN is then wired up // through two logic "gates": first QH and Q'H are AND'd together with a diode-resistor // "gate" the output of which is NAND'd with VCC using a resistor-NPN-resistor "gate". // So, /CLR = ((QH AND Q'H) NAND VCC). We also put a capacitor on the NPN base to GND // to delay the signal a bit and allow the Latch and EN signals some extra time to trigger. // // This all fits together as follows: // 1. We shift in a '1'. // 2. Ws shift in the other 7 bits. // 3. At this point the first '1' has been shifted into Q'H causing it to go HIGH. // 4. When Q'H is HIGH it causes Latch and EN to also go HIGH. // 5. When Latch transitions to HIGH it changes the shift register outputs to the bits // that were shifted in. // 6. This causes QH to go HIGH (if it wasn't already). // 7. Now that QH AND Q'H are both HIGH they causes the base capacitor to start charging. // 8. When the capacitor has charged enough the transistor brings /CLR LOW. // 8. This will cause /CLR to trigger and the shift register will be cleared // (NOTE: This doesn't change the latched outputs) // 9. The clearing of the shift register causes Q'H to go LOW. // 9. When Q'H is LOW it causes Latch and EN to also go LOW. // 10. When EN transitions to LOW the LCD reads in the bits on the shift register pins // and does it's thing. // 11. Now that Q'H is LOW the base capacitor starts discharging. // 12. When the capacitor has discharged enough the transistor will stop sinking /CLR. // 13. This will cause /CLR to be pulled back up to HIGH by the VCC pullup resistor // (it will stay that way until our next nibble/byte has been shifted in) // 14. We are now ready for our next nibble/byte. // // // SW_CLEAR version: // In this version the Serial PIN is wired to the shift register's Latch and LCD's Enable // PINs through another RC filter. These PINs are also wired through a diode (AND "gate") // tied to the shift register's Q'H output. This combination means that the Latch and // Enable PINs will be held LOW as long as EITHER the Q'H or RC output is LOW. // // This all fits together as follows: // 1. We shift in a '1'. // 2. We shift in the other 7 bits. (NOTE: We leave Serial PIN HIGH at the end of this) // 3. At this point the first '1' has been shifted into Q'H causing it to go HIGH. // (NOTE: Up until this time Q'H has been LOW so the attached diode has been keeping // the Latch/EN pins LOW.) // 4. Now that Q'H is HIGH it causes the attached diode to stop discharging the Latch/EN // capacitor. We delay here for a while to make sure it is fully charged. // 5. When the capacitor has charged enough Latch/EN will be HIGH // 5. When Latch transitions to HIGH it changes the shift register outputs to what was // shifted in. // 6. We now bring the Serial PIN LOW and wait for the Latch/EN capacitor to discharge. // 7. When the capacitor has discharged enough Latch/EN will be LOW // 8. When EN transitions to LOW the LCD reads in the bits on the shift register pins // and does it's thing. // 9. We now shift in '0' 8 times (as quickly as possible). // 10. If we keep the LOW to HIGH to LOW pulses short enough while shifting in the '0's // the Latch/EN capacitor won't have time to charge to a point where it will re-trigger // the Latch/EN pins. // 11. Now Q'H will be LOW and the shift register has been cleared (NOTE: This doesn't // change the latched outputs.) // 12. We now bring the Serial PIN HIGH again and wait for the Data capacitor to recharge. // 13. When the Data capacitor has fully charged we are ready for our next nibble/byte. // // // These designs incorporate (and merge) ideas originally found here (1-wire concept): // http://www.romanblack.com/shift1.htm // and here (diode-resistor AND "gate" EN control): // http://www.rentron.com/Myke1.htm // as well as introducing some new and original ideas (particularly how HW_CLEAR works). // // Because of its use of the diode AND "gate", the SW_CLEAR design allows for faster sending // of data to the LCD compared to Roman's original design. With the proposed 5uS delay (see // notes below), a byte can be sent to the LCD in as little as 30 uS (plus overhead) when // sending all 1's. This increases to as much as 190 uS (plus overhead) when sending all 0's. // This is in comparison to Roman's estimate of around 3-4 mS to send a byte. So this // implementation is 15-133 times faster for the cost of a single (1N4148 or similar) diode. // // The HW_CLEAR version is even faster as it can completely eliminate the clearSR() call as // well as the delays that are needed to latch the data in the SW_CLEAR version. // // // Default Shift Register Bits - Shifted MSB first: // Bit #0 (QA) - not used // Bit #1 (QB) - connects to LCD data input D7 // Bit #2 (QC) - connects to LCD data input D6 // Bit #3 (QD) - connects to LCD data input D5 // Bit #4 (QE) - connects to LCD data input D4 // Bit #5 (QF) - optional backlight control // Bit #6 (QG) - connects to RS (Register Select) on the LCD // Bit #7 (QH) - used for /CLR on the HW_CLEAR version (cannot be changed) // (Q'H) - used for Latch/EN (via the diode AND "gate") (cannot be changed) // // NOTE: Any of these can be changed around as needed EXCEPT Bit #7 (QH and Q'H). // // // Circuit Types (for the 74HC595) // ------------------------------- // The 74HC595 is a latching shift register. See the explanations above for how these circuits // work. // // // HW_CLEAR version: (Faster but higher part count) // ------------------------------------------------ // // 74HC595 (VCC) // +----u----+ | 2.2nF // (LCD D7)------------1-|QB VCC|-16--+ +----||----(GND) // (LCD D6)------------2-|QC QA|-15 | // (LCD D5)------------3-|QD SER|-14-------+--[ Resistor ]--+ // (LCD D4)------------4-|QE /OE|-13--(GND) 1.5k | // (BL Circuit)--------5-|QF RCK|-12-----+ | // | | \ | // (LCD RS)------------6-|QG SCK|-11-------)----------------+--(Serial PIN) // | | | // +-------7-|QH /CLR|-10-------)--+--[ Resistor ]--(VCC) // | | | / | 1k // | +--8-|GND Q'H|--9-----+ | // | | +---------+ | | (GND)--(LCD RW) // | | 0.1uF | \ // | (GND)-----||----(VCC) +------)--------------(LCD EN) // | | / // |----|<|----+--[ Resistor ]--| | // diode | 1k C // | | // +-------------+---B-|> (NPN) // | | // (2.2nF) = E // | | // (GND) (GND) // // // SW_CLEAR version: (Lower part count but slower) // ----------------------------------------------- // // 74HC595 (VCC) // +----u----+ | 2.2nF // (LCD D7)------------1-|QB VCC|-16--+ +----||----(GND) // (LCD D6)------------2-|QC QA|-15 | // (LCD D5)------------3-|QD SER|-14---------+--[ Resistor ]--+ // (LCD D4)------------4-|QE /OE|-13--(GND) 1.5k | // (BL Circuit)--------5-|QF RCK|-12---------+ | // | | \ | // (LCD RS)------------6-|QG SCK|-11-----------)--------------+--(Serial PIN) // 7-|QH /CLR|-10--(VCC) / | // +--8-|GND Q'H|--9---|<|---+--[ Resistor ]--+ // | +---------+ diode | 1.5k // | | // | 0.1uF | // (GND)-----||----(VCC) +----||----(GND) // | 2.2nF // (LCD EN)-------------------------------------+ // (LCD RW)--(GND) // // // In either case the LCD RW pin is hardwired to GND meaning we will only be able to write // to the LCD. // Therefore, the Busy Flag (BF, data bit D7) is not able to be read and we have to make use // of the minimum delay time constraints. This isn't really a problem because it usually // takes us longer to shift and latch the data than the minimum delay anyway. For now, we // simply keep track of our delays and add more delay at the end to get to at least 37 uS. // // // Backlight Control Circuit // ------------------------- // Since we are using the latching nature of the shift resiter we don't need the extra // backlight circuitry that SR2W uses. Keeping it around, however, would still work because // the circuit just slows down the transitions to the mosfet a bit. // // Here are two more optimized versions that can be used. // // // NPN Transistor version: (Cheaper but more power draw and higher part count) // --------------------------------------------------------------------------- // // (value depends on LCD, 100ohm is usually safe) // (LCD BL anode)---[ resistor ]---(VCC) // // (LCD BL cathode)---------------+ // | // C // | // (BL input)--[ Resistor ]---B-|> (NPN) // 1k | // E // | // (GND) // // NOTE: The Bate resistor is needed because the NPN is current fed. For lower // power draw, try a 10k resistor. // // // N-CH Mosfet version: (More costly but less power draw and lower part count) // --------------------------------------------------------------------------- // // (value depends on LCD, 100ohm is usually safe) // (LCD BL anode)---[ resistor ]---(VCC) // // (LCD BL cathode)---------------+ // | // D // | // (BL input)----------------G-|-< (2N7000 FET) // | // S // | // (GND) // // NOTE: Gate resistor not needed because the mosfet is voltage fed and only really // pulls current while switching. // // In either case, when the BL input is HIGH the LCD backlight will turn on. // // // 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 source to create SR1W // @author S. Erisman - arduino@serisman.com // -------------------------------------------------------------------------------- #ifndef _LIQUIDCRYSTAL_SR1W_ #define _LIQUIDCRYSTAL_SR1W_ #include #include "LCD.h" #include "FastIO.h" // 1-wire SR timing constants // --------------------------------------------------------------------------- // NOTE: // The 1.5k resistor (1.2k - 1.8k with a 20% tolerance) // takes between 2.376uS and 4.36uS to fully charge or discharge // the 2.2n capacitor (1.98n - 2.42n with a 10% tolerance). // We round this up to a 5uS delay to provide an additional safety margin. #define SR1W_DELAY_US 5 #define SR1W_DELAY() { delayMicroseconds(SR1W_DELAY_US); numDelays++; } // 1-wire SR output bit constants // --------------------------------------------------------------------------- #define SR1W_UNUSED_MASK 0x01 // Set unused bit(s) to '1' as they are slightly faster to clock in. #define SR1W_D7_MASK 0x02 #define SR1W_D6_MASK 0x04 #define SR1W_D5_MASK 0x08 #define SR1W_D4_MASK 0x10 #define SR1W_BL_MASK 0x20 #define SR1W_RS_MASK 0x40 #define SR1W_EN_MASK 0x80 // This cannot be changed. It has to be the first thing shifted in. #define SR1W_ATOMIC_WRITE_LOW(reg, mask) ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { *reg &= ~mask; } #define SR1W_ATOMIC_WRITE_HIGH(reg, mask) ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { *reg |= mask; } typedef enum { SW_CLEAR, HW_CLEAR } t_sr1w_circuitType; class LiquidCrystal_SR1W : public LCD { public: /*! @method @abstract LCD 1 wire SHIFT REGISTER constructor. @discussion Defines the pin assignments that connect to the shift register. The constructor does not initialize the LCD. Assuming 1 line 8 pixel high font. @param srdata[in] Arduino pin for shift register. @param circuitType[in] optionally select an alternate circuit type @param blpol[in] optional backlight polarity (default = POSITIVE) */ LiquidCrystal_SR1W (uint8_t srdata, t_sr1w_circuitType circuitType, t_backlighPol blpol = POSITIVE); /*! @function @abstract Send a particular value to the LCD. @discussion Sends a particular value to the LCD for writing to the LCD or as an LCD command using the shift register. Users should never call this method. @param value[in] Value to send to the LCD. @param mode[in] DATA=8bit data, COMMAND=8bit cmd, FOUR_BITS=4bit cmd the LCD. */ virtual void send(uint8_t value, uint8_t mode); /*! @function @abstract Switch-on/off the LCD backlight. @discussion Switch-on/off the LCD backlight. The setBacklightPin has to be called before setting the backlight for this method to work. @see setBacklightPin. @param mode[in] backlight mode (0 off, non-zero on) */ void setBacklight ( uint8_t mode ); private: /*! @method @abstract Initializes the LCD pin allocation @discussion Initializes the LCD pin allocation and configuration. */ void init ( uint8_t srdata, t_sr1w_circuitType circuitType, t_backlighPol blpol, uint8_t lines, uint8_t font ); /*! @method @abstract Clears the shift register to ensure the Latch/Enable pins aren't triggered accidentally. */ uint8_t clearSR (); /*! * @method * @abstract takes care of shifting and the enable pulse */ uint8_t loadSR (uint8_t val); fio_register _srRegister; // Serial PIN fio_bit _srMask; t_sr1w_circuitType _circuitType; uint8_t _blPolarity; uint8_t _blMask; }; #endif