AceSegment  0.8.2
A framework for rendering seven segment LED displays using the TM1637, MAX7219, HT16K33, or 74HC595 controller chips
Tm1637Module.h
1 /*
2 MIT License
3 
4 Copyright (c) 2021 Brian T. Park
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 
25 #ifndef ACE_SEGMENT_TM1637_MODULE_H
26 #define ACE_SEGMENT_TM1637_MODULE_H
27 
28 #include <Arduino.h>
29 #include <AceCommon.h> // incrementMod()
30 #include "../LedModule.h"
31 
32 class Tm1637ModuleTest_flushIncremental;
33 class Tm1637ModuleTest_flush;
34 
35 namespace ace_segment {
36 
47 static const uint16_t kDefaultTm1637DelayMicros = 100;
48 
62 extern const uint8_t kDigitRemapArray6Tm1637[6];
63 
74 template <typename T_TMII, uint8_t T_DIGITS>
75 class Tm1637Module : public LedModule {
76  public:
77 
85  explicit Tm1637Module(
86  const T_TMII& tmiInterface,
87  const uint8_t* remapArray = nullptr
88  ) :
89  LedModule(mPatterns, T_DIGITS),
90  mTmiInterface(tmiInterface),
91  mRemapArray(remapArray)
92  {}
93 
94  //-----------------------------------------------------------------------
95  // Initialization and termination.
96  //-----------------------------------------------------------------------
97 
102  void begin() {
104 
105  memset(mPatterns, 0, T_DIGITS);
106  setDisplayOn(true);
107  setBrightness(0x7);
108  mFlushStage = 0;
109  }
110 
112  void end() {
113  LedModule::end();
114  }
115 
116  //-----------------------------------------------------------------------
117  // Additional brightness control supported by the TM1637 chip.
118  //-----------------------------------------------------------------------
119 
124  void setDisplayOn(bool on = true) {
125  mDisplayOn = on;
126  setBrightness(getBrightness()); // mark the brightness dirty
127  }
128 
129  //-----------------------------------------------------------------------
130  // Methods related to rendering.
131  //-----------------------------------------------------------------------
132 
134  bool isFlushRequired() const {
135  return isAnyDigitDirty() || isBrightnessDirty();
136  }
137 
145  void flush() {
146  // Update the brightness first
147  mTmiInterface.startCondition();
148  mTmiInterface.sendByte(kBrightnessCmd
149  | (mDisplayOn ? kBrightnessLevelOn : 0x0)
150  | (getBrightness() & 0xF));
151  mTmiInterface.stopCondition();
152 
153  // Update the digits using auto incrementing mode.
154  mTmiInterface.startCondition();
155  mTmiInterface.sendByte(kDataCmdAutoAddress);
156  mTmiInterface.stopCondition();
157 
158  mTmiInterface.startCondition();
159  mTmiInterface.sendByte(kAddressCmd);
160  for (uint8_t chipPos = 0; chipPos < T_DIGITS; ++chipPos) {
161  // Remap the logical position used by the controller to the actual
162  // position. For example, if the controller digit 0 appears at physical
163  // digit 2, we need to display the segment pattern given by logical
164  // position 2 when sending the byte to controller digit 0.
165  uint8_t physicalPos = remapLogicalToPhysical(chipPos);
166  uint8_t effectivePattern = mPatterns[physicalPos];
167  mTmiInterface.sendByte(effectivePattern);
168  }
169  mTmiInterface.stopCondition();
170 
173  }
174 
209  if (mFlushStage == T_DIGITS) {
210  // Update brightness.
211  if (isBrightnessDirty()) {
212  mTmiInterface.startCondition();
213  mTmiInterface.sendByte(kBrightnessCmd
214  | (mDisplayOn ? kBrightnessLevelOn : 0x0)
215  | (getBrightness() & 0xF));
216  mTmiInterface.stopCondition();
218  }
219  } else {
220  // Remap the logical position used by the controller to the actual
221  // position. For example, if the controller digit 0 appears at physical
222  // digit 2, we need to display the segment pattern given by logical
223  // position 2 when sending the byte to controller digit 0.
224  const uint8_t chipPos = mFlushStage;
225  const uint8_t physicalPos = remapLogicalToPhysical(chipPos);
226  if (isDigitDirty(physicalPos)) {
227  // Update changed digit.
228  mTmiInterface.startCondition();
229  mTmiInterface.sendByte(kDataCmdFixedAddress);
230  mTmiInterface.stopCondition();
231 
232  mTmiInterface.startCondition();
233  mTmiInterface.sendByte(kAddressCmd | chipPos);
234  mTmiInterface.sendByte(mPatterns[physicalPos]);
235  mTmiInterface.stopCondition();
236  clearDigitDirty(physicalPos);
237  }
238  }
239 
240  // An extra dirty bit is used for the brightness so use `T_DIGITS + 1`.
241  ace_common::incrementMod(mFlushStage, (uint8_t) (T_DIGITS + 1));
242  }
243 
244  private:
246  uint8_t remapLogicalToPhysical(uint8_t pos) const {
247  return mRemapArray ? mRemapArray[pos] : pos;
248  }
249 
250  private:
251  // Give access to mIsDirty and mFlushStage
252  friend class ::Tm1637ModuleTest_flushIncremental;
253  friend class ::Tm1637ModuleTest_flush;
254 
255  // These come from the TM1637 controller chip datasheet.
256  static uint8_t const kDataCmdWriteDisplay = 0b01000000;
257  static uint8_t const kDataCmdReadKeys = 0b01000010;
258  static uint8_t const kDataCmdAutoAddress = 0b01000000;
259  static uint8_t const kDataCmdFixedAddress = 0b01000100;
260  static uint8_t const kAddressCmd = 0b11000000;
261  static uint8_t const kBrightnessCmd = 0b10000000;
262  static uint8_t const kBrightnessLevelOn = 0b00001000;
263 
264  // The ordering of these fields is partially determined to save memory on
265  // 32-bit processors.
266 
267  // TM1637 interface object. Copied by value instead of reference to avoid an
268  // extra level of indirection.
269  const T_TMII mTmiInterface;
270 
271  const uint8_t* const mRemapArray;
272  uint8_t mPatterns[T_DIGITS];
273  bool mDisplayOn;
274  uint8_t mFlushStage; // [0, T_DIGITS], with T_DIGITS for brightness update
275 };
276 
277 } // ace_segment
278 
279 #endif
ace_segment::LedModule::clearDigitsDirty
void clearDigitsDirty()
Clear dirty bits of all digits.
Definition: LedModule.h:126
ace_segment::LedModule
General interface that represents a generic seven-segment LED module with multiple digits.
Definition: LedModule.h:44
ace_segment::Tm1637Module::flushIncremental
void flushIncremental()
Update only a single digit or the brightness.
Definition: Tm1637Module.h:208
ace_segment::LedModule::isAnyDigitDirty
bool isAnyDigitDirty() const
Return true if any digits are dirty.
Definition: LedModule.h:131
ace_segment::Tm1637Module::isFlushRequired
bool isFlushRequired() const
Return true if flushing required.
Definition: Tm1637Module.h:134
ace_segment::Tm1637Module::setDisplayOn
void setDisplayOn(bool on=true)
Turn off the entire display.
Definition: Tm1637Module.h:124
ace_segment::LedModule::clearDigitDirty
void clearDigitDirty(uint8_t pos)
Clear the dirty bit of digit pos.
Definition: LedModule.h:116
ace_segment::Tm1637Module::begin
void begin()
Initialize the module.
Definition: Tm1637Module.h:102
ace_segment::LedModule::begin
void begin()
Subclasses should call this from its own begin().
Definition: LedModule.h:93
ace_segment::LedModule::clearBrightnessDirty
void clearBrightnessDirty()
Clear the dirty bit for brightness.
Definition: LedModule.h:146
ace_segment::LedModule::setBrightness
void setBrightness(uint8_t brightness)
Set global brightness of all digits.
Definition: LedModule.h:81
ace_segment::LedModule::end
void end()
Subclasses should call this from its own end().
Definition: LedModule.h:108
ace_segment::LedModule::getBrightness
uint8_t getBrightness() const
Get the current brightness.
Definition: LedModule.h:87
ace_segment::Tm1637Module::end
void end()
Signal end of usage.
Definition: Tm1637Module.h:112
ace_segment::LedModule::isBrightnessDirty
bool isBrightnessDirty() const
Check if the brightness level is dirty.
Definition: LedModule.h:136
ace_segment::LedModule::isDigitDirty
bool isDigitDirty(uint8_t pos) const
Check the dirty bit of digit pos.
Definition: LedModule.h:121
ace_segment::Tm1637Module::flush
void flush()
Send segment patterns of all digits plus the brightness to the display.
Definition: Tm1637Module.h:145
ace_segment::Tm1637Module
An implementation of LedModule using the TM1637 chip.
Definition: Tm1637Module.h:75
ace_segment::Tm1637Module::Tm1637Module
Tm1637Module(const T_TMII &tmiInterface, const uint8_t *remapArray=nullptr)
Constructor.
Definition: Tm1637Module.h:85