-
Notifications
You must be signed in to change notification settings - Fork 0
/
LiquidCrystal_SR1W.h
380 lines (351 loc) · 16.8 KB
/
LiquidCrystal_SR1W.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
// ---------------------------------------------------------------------------
// 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_
#if defined (__AVR__)
#include <inttypes.h>
#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;
};
#else
#error "ONLY SUPPORTED ON AVR PROCESSORS"
#endif // defined (__AVR__)
#endif