-
Notifications
You must be signed in to change notification settings - Fork 0
/
LiquidCrystal_SR1W.cpp
254 lines (209 loc) · 7.54 KB
/
LiquidCrystal_SR1W.cpp
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
// ---------------------------------------------------------------------------
// 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
// ---------------------------------------------------------------------------
#if defined (__AVR__)
#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 == LCD_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 == LCD_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);
}
#endif // defined (__AVR__)