DUDS
Distributed Update of Data from Something
HD44780.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2017 Jeff Jackowski
9  */
14 #include <thread>
15 
16 namespace duds { namespace hardware { namespace devices { namespace displays {
17 
18 HD44780::HD44780() : outcfg(5) { }
19 
23  unsigned int c,
24  unsigned int r,
25  std::chrono::nanoseconds delay
26 ) :
27  TextDisplay(c, r),
28  outcfg(5),
29  soonestSend(std::chrono::high_resolution_clock::now())
30 {
31  configure(std::move(outPins), std::move(enablePin), c, r, delay);
32 }
33 
35  if (outputs.havePins()) {
36  off();
37  }
38 }
39 
43  unsigned int c,
44  unsigned int r,
45  std::chrono::nanoseconds delay
46 ) {
47  // range check on display size
48  if ((c > 20) || (c < 1) || (r > 4) || (r < 1)) {
52  )
53  );
54  }
55  if (!outPins.havePins() || !enablePin) {
57  }
58  // requires 5 pins: 4 data, 1 control, all output
59  if (outPins.size() != 5) {
61  }
62  // get the capabilities for inspection
63  std::vector<duds::hardware::interface::DigitalPinCap> caps =
64  outPins.capabilities();
65  // iteratate over all the pins
66  std::vector<duds::hardware::interface::DigitalPinCap>::const_iterator iter =
67  caps.cbegin();
68  unsigned int pos = 0;
69  for (; iter != caps.cend(); ++pos, ++iter) {
70  // check for no output ability
71  if (!iter->canOutput()) {
74  duds::hardware::interface::PinErrorId(outPins.globalId(pos))
75  );
76  }
77  // work out the actual output config
79  iter->firstOutputDriveConfigFlags()
80  );
81  }
82  // store this access object; seems good if it got this far without
83  // an exception
84  outputs = std::move(outPins);
85  enable = std::move(enablePin);
86  columnsize = c;
87  rowsize = r;
88  nibblePeriod = delay;
89 }
90 
91 void HD44780::wait() const {
92  auto remain = soonestSend - std::chrono::high_resolution_clock::now();
93  if (remain.count() > 0) {
95  }
96 }
97 
99  if (!outputs.havePins()) {
101  }
102  // Wait until the display can take more data. Do this before getting
103  // hardware access so that the hadrware remains availble for other
104  // threads during this initial wait.
105  wait();
106  // request hardware access
107  outputs.access(acc.output);
108  enable.access(acc.enable);
109  // make all pins outputs
111 }
112 
113 void HD44780::sendByte(HD44780::Access &acc, int val) {
114  // write out the text flag as the MSb along with the high-order nibble
115  acc.output.write((val & 0x1F0) >> 4); // 5-bit output
116  // wait
118  // tell LCD to read
119  acc.enable.select();
120  // another wait
122  // LCD should be done reading
123  acc.enable.deselect();
124  // sending a whole byte?
125  if (~val & nibbleFlag) {
126  // write out the low-order nibble; leave command flag alone
127  acc.output.write(val & 0xF, 4); // 4-bit output
128  // wait
130  // tell LCD to read
131  acc.enable.select();
132  // wait again
134  // LCD should be done reading
135  acc.enable.deselect();
136  }
137  // record time when more data can be sent
138  soonestSend = std::chrono::high_resolution_clock::now();
139  if (val < 4) {
140  soonestSend += std::chrono::milliseconds(2);
141  } else {
142  soonestSend += std::chrono::microseconds(48);
143  }
144 }
145 
147  // LCD intialization commands in reverse order they are sent
148  static const std::uint8_t initdata[] {
149  0x6, // increment cursor, no display shift
150  0xC, // turn on display w/o cursor
151  0x1, // clear display
152  0x8, // turn off display
153  0x20, // 4 bit bus mode, 1 row
154  0x30, // 8-bit bus mode; sync nibble reception
155  0x30, // 8-bit bus mode; sync nibble reception
156  0x30 // 8-bit bus mode; sync nibble reception
157  };
158  Access acc;
159  preparePins(acc);
160  acc.output.output(false);
161  acc.enable.select();
162  std::this_thread::sleep_for(std::chrono::milliseconds(4));
163  acc.enable.deselect();
164  int loop = sizeof(initdata) - 1;
165  for (; loop >= sizeof(initdata) - 4; --loop) {
166  sendByte(acc, nibbleFlag | initdata[loop]);
167  std::this_thread::sleep_for(std::chrono::milliseconds(2));
168  }
169  // now using 4-bit bus mode for one row only; are more rows in use?
170  if (rowsize > 1) {
171  // configure for multiple rows; use 4-bit bus mode
172  sendByte(acc, 0x28);
173  std::this_thread::sleep_for(std::chrono::milliseconds(2));
174  }
175  for (; loop >= 0; --loop) {
176  sendByte(acc, initdata[loop]);
177  std::this_thread::sleep_for(std::chrono::milliseconds(2));
178  }
179  // cursor should now be at the upper left corner
180  cpos = rpos = 0;
181 }
182 
183 void HD44780::off() {
184  Access acc;
185  preparePins(acc);
186  sendByte(acc, 8); // display off command
187 }
188 
189 void HD44780::on() {
190  Access acc;
191  preparePins(acc);
192  sendByte(acc, 12); // display on command
193 }
194 
198 static std::uint8_t rowStartAddr[4] = {
199  0, 0x40, 0x14, 0x54
200 };
201 
202 void HD44780::moveImpl(unsigned int c, unsigned int r) {
203  Access acc;
204  preparePins(acc);
205  // send command to change display address or'd with the address
206  sendByte(acc, 0x80 | (rowStartAddr[r] + c));
207 }
208 
209 void HD44780::writeImpl(Access &acc, const std::string &text) {
210  // loop through characters
211  std::string::const_iterator iter = text.begin();
212  do {
213  wait();
214  // send the lower byte of the next character; discard the rest
215  sendByte(acc, textFlag | (*iter & 0xFF));
216  // need to reposition cursor?
217  if (advance()) {
218  wait();
219  // send command to change display address or'd with the address
220  // of the current position
221  sendByte(acc, 0x80 | (rowStartAddr[rpos] + cpos));
222  }
223  } while (++iter != text.end());
224 }
225 
226 void HD44780::writeImpl(int c) {
227  Access acc;
228  preparePins(acc);
229  // send the lower byte of the character; discard the rest
230  sendByte(acc, textFlag | (c & 0xFF));
231 }
232 
233 void HD44780::writeImpl(const std::string &text) {
234  Access acc;
235  preparePins(acc);
236  writeImpl(acc, text);
237 }
238 
240  const std::string &text,
241  unsigned int c,
242  unsigned int r
243 ) {
244  Access acc;
245  preparePins(acc);
246  // move cursor
247  sendByte(acc, 0x80 | (rowStartAddr[r] + c));
248  cpos = c;
249  rpos = r;
250  // do the write
251  writeImpl(acc, text);
252 }
253 
255  Access acc;
256  preparePins(acc);
257  // send clear command
258  sendByte(acc, 1);
259  // display moves the cursor; update the position to match
260  cpos = rpos = 0;
261 }
262 
264  // displays ignore the 4th bit
265  idx &= ~8;
266  // check for out of range index
267  if ((idx < 0) || (idx > 7)) {
270  );
271  }
272  // check for bad image size
273  if ((glyph->width() > 5) || (glyph->height() > 8)) {
275  duds::ui::graphics::ImageErrorDimensions(glyph->dimensions())
276  );
277  }
278  Access acc;
279  preparePins(acc);
280  // send command to set CGRAM address
281  sendByte(acc, 0x40 | (idx << 3));
282  // loop to send up to eight bytes
283  int y = 0;
284  for (; y < glyph->height(); ++y) {
285  // compute value to send to display
286  std::uint8_t row = *(glyph->bufferLine(y));
287  row = duds::general::ReverseBits(row) >> 3;
288  // send it
289  sendByte(acc, textFlag | row);
290  }
291  // allow less than 8 rows; clear unspecified rows
292  for (; y < 8; ++y) {
293  sendByte(acc, textFlag);
294  }
295  // change address back to current text position so any text writing request
296  // will work properly, not just those that set the position
297  sendByte(acc, 0x80 | (rowStartAddr[rpos] + cpos));
298 }
299 
300 } } } }
boost::error_info< struct Info_ImageDimensions, ImageDimensions > ImageErrorDimensions
Image dimensions relevant to the error.
Definition: BppImage.hpp:293
std::chrono::high_resolution_clock::time_point soonestSend
The soonest time a new command can be sent to the display.
Definition: HD44780.hpp:70
An attempt was made to use an uninitialized display object.
void YieldingWait(Duration duration)
Waits for a minumum period of time by calling std::this_thread::yield() in a loop.
void initialize()
Initializes the display for use.
Definition: HD44780.cpp:146
The image given for a definable glyph was an unsupported size.
void write(Int val, int bits) const
Writes out a number in binary to the pins.
Defines the configuration for a digital general purpose I/O pin.
static std::uint8_t rowStartAddr[4]
The address used by the display for the start of each row.
Definition: HD44780.cpp:198
void wait() const
Waits until the time in soonestSend has passed.
Definition: HD44780.cpp:91
The operation has too few or much data to work on the pins, which can alternately be stated as having...
Definition: PinErrors.hpp:50
HD44780()
Initializes the object with an invalid display size and no pins to use.
Definition: HD44780.cpp:18
STL namespace.
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
duds::hardware::interface::ChipSelect enable
Used to represent the enable line of the LCD.
Definition: HD44780.hpp:60
A pin required for the operation does not exist or is unavailable to the process. ...
Definition: PinErrors.hpp:70
DigitalPinConfig modifyConfig(unsigned int pos, const DigitalPinConfig &conf) const
Modifies the configuration of a pin.
boost::error_info< struct Info_PinId, unsigned int > PinErrorId
The pin global ID involved in the error.
Definition: PinErrors.hpp:97
An object to wrap together a ChipSelectManager and chip ID to simplify code that needs to repeatedly ...
Definition: ChipSelect.hpp:24
boost::error_info< struct Info_DisplayGlyph, int > DisplayGlyphIndex
Index used for a definable glyph.
The specified display size is unsupported, or there is a display size mismatch.
std::shared_ptr< BppImage > BppImageSptr
Definition: BppImage.hpp:1777
std::uint8_t cpos
Cursor column position.
Definition: TextDisplay.hpp:44
Stores column and row data for display errors.
duds::hardware::interface::DigitalPinSetAccess output
The set used for the 4 data pins and the text flag, more commonly referred to as "RS".
Definition: HD44780.hpp:112
Represents a set of pins on a single DigitalPort.
std::uint8_t columnsize
Number of columns on the display.
Definition: TextDisplay.hpp:36
std::unique_ptr< DigitalPinSetAccess > access() const
Obtains an access object for all the pins in this set.
Flag to send only a nibble rather than a whole byte; used in display initalization.
Definition: HD44780.hpp:93
bool havePins() const
Returns true if this object has been given any pins to represent.
std::uint8_t rowsize
Number of rows on the display.
Definition: TextDisplay.hpp:40
std::uint8_t rpos
Cursor row position.
Definition: TextDisplay.hpp:48
Stores access objects together.
Definition: HD44780.hpp:107
void off()
Commands the display to turn off.
Definition: HD44780.cpp:183
void deselect()
Deselects the chip.
Definition: ChipAccess.hpp:69
std::unique_ptr< ChipAccess > access()
Obtains a ChipAccess object.
Definition: ChipSelect.cpp:32
void output(unsigned int pos, bool state) const
Changes the output state of a pin.
virtual void clear()
Removes all text from the display and moves the cursor to the upper left corner.
Definition: HD44780.cpp:254
void on()
Commands the display to turn on.
Definition: HD44780.cpp:189
Indicates that a request to configure a pin to output was made of a pin that cannot output...
std::vector< duds::hardware::interface::DigitalPinConfig > outcfg
The best output configuration for the display bus given the port in use.
Definition: HD44780.hpp:64
void setGlyph(const std::shared_ptr< duds::ui::graphics::BppImage > &glyph, int idx)
Loads a glyph into the display&#39;s CGRAM (Character Generator Random Access Memory).
Definition: HD44780.cpp:263
TextDisplay()
Initializes the object with an invalid display size and cursor position.
Definition: TextDisplay.cpp:16
duds::hardware::interface::DigitalPinSet outputs
Represents the 5 output lines, other than enable, that are needed to communicate with the LCD...
Definition: HD44780.hpp:52
void preparePins(Access &acc)
Obtains access to the pins and configures them for output.
Definition: HD44780.cpp:98
duds::hardware::interface::ChipAccess enable
Used to assert the enable line of the LCD.
Definition: HD44780.hpp:116
void sendByte(Access &acc, int val)
Sends a byte to the display a nibble at a time.
Definition: HD44780.cpp:113
The index given for a definable glyph was outside the allowable range.
std::chrono::nanoseconds nibblePeriod
The amount of time to allow the display to read data.
Definition: HD44780.hpp:74
void configure(duds::hardware::interface::DigitalPinSet &&outPins, duds::hardware::interface::ChipSelect &&enablePin, unsigned int c, unsigned int r, std::chrono::nanoseconds delay=std::chrono::nanoseconds(8000))
Sets the pins to use for communicating with the display.
Definition: HD44780.cpp:40
virtual void writeImpl(int c)
Writes a single character to the display.
Definition: HD44780.cpp:226
Flag for sending text to the display rather than a command.
Definition: HD44780.hpp:84
Integer ReverseBits(Integer i)
Reverses the bits in a given value.
Definition: ReverseBits.hpp:26
void select()
Selects the chip.
Definition: ChipAccess.hpp:63
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
virtual void moveImpl(unsigned int c, unsigned int r)
Moves the display&#39;s cursor to the indicated position.
Definition: HD44780.cpp:202
virtual bool advance()
Advances the column position, and if it goes off the visible portion of the display, updates the row position.
Definition: TextDisplay.cpp:24
boost::error_info< struct Info_DisplaySize, Info_DisplayColRow > TextDisplaySizeInfo
Column and row size of a display as part of an error.