AceSegment  0.12.0
A library for rendering seven segment LED displays using the TM1637, TM1638, MAX7219, HT16K33, or 74HC595 controller chips
Tm1638Module.h
1 /*
2 MIT License
3 
4 Copyright (c) 2022 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_TM1638_MODULE_H
26 #define ACE_SEGMENT_TM1638_MODULE_H
27 
28 #include <stdint.h>
29 #include <string.h> // memset()
30 #include <Arduino.h> // delayMicroseconds()
31 #include "../LedModule.h"
32 
33 class Tm1638ModuleTest_flushIncremental;
34 class Tm1638ModuleTest_flush;
35 
36 namespace ace_segment {
37 
42 static const uint16_t kDefaultTm1638DelayMicros = 1;
43 
53 extern const uint8_t kDigitRemapArray8Tm1638[8];
54 
64 template <typename T_TMII, uint8_t T_DIGITS>
65 class Tm1638Module : public LedModule {
66  public:
67 
75  explicit Tm1638Module(
76  const T_TMII& tmiInterface,
77  const uint8_t* remapArray = nullptr
78  ) :
79  LedModule(mPatterns, T_DIGITS),
80  mTmiInterface(tmiInterface),
81  mRemapArray(remapArray)
82  {}
83 
84  //-----------------------------------------------------------------------
85  // Initialization and termination.
86  //-----------------------------------------------------------------------
87 
92  void begin() {
94 
95  memset(mPatterns, 0, T_DIGITS);
96  setDisplayOn(true);
97  }
98 
100  void end() {
101  LedModule::end();
102  }
103 
104  //-----------------------------------------------------------------------
105  // Additional brightness control supported by the TM1638 chip.
106  //-----------------------------------------------------------------------
107 
112  void setDisplayOn(bool on = true) {
113  mDisplayOn = on;
114  setBrightness(getBrightness()); // mark the brightness dirty
115  }
116 
117  //-----------------------------------------------------------------------
118  // Methods related to rendering.
119  //-----------------------------------------------------------------------
120 
122  bool isFlushRequired() const {
123  return isAnyDigitDirty() || isBrightnessDirty();
124  }
125 
142  void flush() {
143  // Command1: Update the digits using auto incrementing mode.
144  mTmiInterface.beginTransaction();
145  mTmiInterface.write(kDataCmdAutoAddress);
146  mTmiInterface.endTransaction();
147 
148  // Command2: Send the LED patterns.
149  mTmiInterface.beginTransaction();
150  mTmiInterface.write(kAddressCmd);
151  for (uint8_t chipPos = 0; chipPos < T_DIGITS; ++chipPos) {
152  // Remap the logical position used by the controller to the actual
153  // position. For example, if the controller digit 0 appears at physical
154  // digit 2, we need to display the segment pattern given by logical
155  // position 2 when sending the byte to controller digit 0.
156  uint8_t physicalPos = remapLogicalToPhysical(chipPos);
157  uint8_t effectivePattern = mPatterns[physicalPos];
158  mTmiInterface.write(effectivePattern);
159  mTmiInterface.write(0x00); // SEG8 and SEG9 not supported in this class
160  }
161  mTmiInterface.endTransaction();
162 
163  // Command3: Update the brightness last. This matches the recommendation
164  // given in the Titan Micro TM1638 datasheet. But experimentation shows
165  // that things seems to work even if brightness is sent first, before the
166  // digit patterns.
167  mTmiInterface.beginTransaction();
168  mTmiInterface.write(kBrightnessCmd
169  | (mDisplayOn ? kBrightnessLevelOn : 0x0)
170  | (getBrightness() & 0xF));
171  mTmiInterface.endTransaction();
172 
175  }
176 
177  //-----------------------------------------------------------------------
178  // Methods related to buttons
179  //-----------------------------------------------------------------------
180 
187  uint32_t readButtons() const {
188  mTmiInterface.beginTransaction();
189  mTmiInterface.write(kDataCmdReadKeys);
190 
191  // The datasheet says that at least 2 micros are needed between the
192  // write() and the read(). On some microcontrollers (e.g. AVR), the
193  // accuracy of delayMicroseconds() is terrible for small values. So let's
194  // use 3 micros just in case.
195  delayMicroseconds(3);
196 
197  uint8_t byte1 = mTmiInterface.read();
198  uint8_t byte2 = mTmiInterface.read();
199  uint8_t byte3 = mTmiInterface.read();
200  uint8_t byte4 = mTmiInterface.read();
201  mTmiInterface.endTransaction();
202 
203  uint32_t data = ((uint32_t) byte4 << 24)
204  | ((uint32_t) byte3 << 16)
205  | ((uint32_t) byte2 << 8)
206  | (uint32_t) byte1;
207  return data;
208  }
209 
210  private:
212  uint8_t remapLogicalToPhysical(uint8_t pos) const {
213  return mRemapArray ? mRemapArray[pos] : pos;
214  }
215 
216  private:
217  // Give access to mIsDirty.
218  friend class ::Tm1638ModuleTest_flush;
219 
220  // These come from the TM1638 controller chip datasheet.
221  static uint8_t const kDataCmdWriteDisplay = 0b01000000;
222  static uint8_t const kDataCmdReadKeys = 0b01000010;
223  static uint8_t const kDataCmdAutoAddress = 0b01000000;
224  static uint8_t const kDataCmdFixedAddress = 0b01000100;
225  static uint8_t const kAddressCmd = 0b11000000;
226  static uint8_t const kBrightnessCmd = 0b10000000;
227  static uint8_t const kBrightnessLevelOn = 0b00001000;
228 
229  // The ordering of these fields is partially determined to save memory on
230  // 32-bit processors.
231 
232  // TM1638 interface object. Copied by value instead of reference to avoid an
233  // extra level of indirection.
234  const T_TMII mTmiInterface;
235 
236  const uint8_t* const mRemapArray;
237  uint8_t mPatterns[T_DIGITS];
238  bool mDisplayOn;
239 };
240 
241 } // ace_segment
242 
243 #endif
ace_segment::LedModule::clearDigitsDirty
void clearDigitsDirty()
Clear dirty bits of all digits.
Definition: LedModule.h:126
ace_segment::Tm1638Module::begin
void begin()
Initialize the module.
Definition: Tm1638Module.h:92
ace_segment::LedModule
General interface that represents a generic seven-segment LED module with multiple digits.
Definition: LedModule.h:44
ace_segment::Tm1638Module::readButtons
uint32_t readButtons() const
Read the 4 bytes with key information.
Definition: Tm1638Module.h:187
ace_segment::LedModule::isAnyDigitDirty
bool isAnyDigitDirty() const
Return true if any digits are dirty.
Definition: LedModule.h:131
ace_segment::Tm1638Module
An implementation of LedModule using the TM1638 chip.
Definition: Tm1638Module.h:65
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::Tm1638Module::Tm1638Module
Tm1638Module(const T_TMII &tmiInterface, const uint8_t *remapArray=nullptr)
Constructor.
Definition: Tm1638Module.h:75
ace_segment::Tm1638Module::isFlushRequired
bool isFlushRequired() const
Return true if flushing required.
Definition: Tm1638Module.h:122
ace_segment::Tm1638Module::flush
void flush()
Send segment patterns of all digits plus the brightness to the display.
Definition: Tm1638Module.h:142
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::LedModule::isBrightnessDirty
bool isBrightnessDirty() const
Check if the brightness level is dirty.
Definition: LedModule.h:136
ace_segment::Tm1638Module::setDisplayOn
void setDisplayOn(bool on=true)
Turn off the entire display.
Definition: Tm1638Module.h:112
ace_segment::Tm1638Module::end
void end()
Signal end of usage.
Definition: Tm1638Module.h:100