AceSegment  0.12.0
A library for rendering seven segment LED displays using the TM1637, TM1638, MAX7219, HT16K33, or 74HC595 controller chips
Tm1638AnodeModule.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_ANODE_MODULE_H
26 #define ACE_SEGMENT_TM1638_ANODE_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 
47 template <typename T_TMII, uint8_t T_DIGITS>
48 class Tm1638AnodeModule : public LedModule {
49  public:
50 
59  const T_TMII& tmiInterface
60  ) :
61  LedModule(mPatterns, T_DIGITS),
62  mTmiInterface(tmiInterface)
63  {}
64 
65  //-----------------------------------------------------------------------
66  // Initialization and termination.
67  //-----------------------------------------------------------------------
68 
73  void begin() {
75 
76  memset(mPatterns, 0, T_DIGITS);
77  setDisplayOn(true);
78  }
79 
81  void end() {
83  }
84 
85  //-----------------------------------------------------------------------
86  // Additional brightness control supported by the TM1638 chip.
87  //-----------------------------------------------------------------------
88 
93  void setDisplayOn(bool on = true) {
94  mDisplayOn = on;
95  setBrightness(getBrightness()); // mark the brightness dirty
96  }
97 
98  //-----------------------------------------------------------------------
99  // Methods related to rendering.
100  //-----------------------------------------------------------------------
101 
103  bool isFlushRequired() const {
104  return isAnyDigitDirty() || isBrightnessDirty();
105  }
106 
123  void flush() {
124  // Command1: Update the digits using auto incrementing mode.
125  mTmiInterface.beginTransaction();
126  mTmiInterface.write(kDataCmdAutoAddress);
127  mTmiInterface.endTransaction();
128 
129  // Command2: Send the LED patterns. A double loop is required because this
130  // board uses Common Anode LED modules, so the 8 segments are sunk by the
131  // GRn lines, and each digit is driven by a single SEGn line. Furthermore,
132  // the SEGn lines are arranged so that the left-most digit is SEG8 and the
133  // right-most digit is SEG1, so the `gridMask` starts at 0x80 and shifts
134  // to the right for each digit.
135  const uint8_t NUM_SEGMENTS = 8;
136  mTmiInterface.beginTransaction();
137  mTmiInterface.write(kAddressCmd);
138  uint8_t digitMask = 0x1;
139  for (uint8_t grid = 0; grid < NUM_SEGMENTS; ++grid) {
140  uint8_t gridPattern = 0x0;
141  uint8_t gridMask = 0x80;
142  for (uint8_t digit = 0; digit < T_DIGITS; ++digit) {
143  uint8_t digitPattern = mPatterns[digit];
144  if (digitPattern & digitMask) {
145  gridPattern |= gridMask;
146  }
147  gridMask >>= 1;
148  }
149  digitMask <<= 1;
150  mTmiInterface.write(gridPattern);
151  mTmiInterface.write(0x00); // SEG8 and SEG9 not supported in this class
152  }
153  mTmiInterface.endTransaction();
154 
155  // Command3: Update the brightness last. This matches the recommendation
156  // given in the Titan Micro TM1638 datasheet. But experimentation shows
157  // that things seems to work even if brightness is sent first, before the
158  // digit patterns.
159  mTmiInterface.beginTransaction();
160  mTmiInterface.write(kBrightnessCmd
161  | (mDisplayOn ? kBrightnessLevelOn : 0x0)
162  | (getBrightness() & 0xF));
163  mTmiInterface.endTransaction();
164 
167  }
168 
169  //-----------------------------------------------------------------------
170  // Methods related to buttons
171  //-----------------------------------------------------------------------
172 
179  uint32_t readButtons() const {
180  mTmiInterface.beginTransaction();
181  mTmiInterface.write(kDataCmdReadKeys);
182 
183  // The datasheet says that at least 2 micros are needed between the
184  // write() and the read(). On some microcontrollers (e.g. AVR), the
185  // accuracy of delayMicroseconds() is terrible for small values. So let's
186  // use 3 micros just in case.
187  delayMicroseconds(3);
188 
189  uint8_t byte1 = mTmiInterface.read();
190  uint8_t byte2 = mTmiInterface.read();
191  uint8_t byte3 = mTmiInterface.read();
192  uint8_t byte4 = mTmiInterface.read();
193  mTmiInterface.endTransaction();
194 
195  uint32_t data = ((uint32_t) byte4 << 24)
196  | ((uint32_t) byte3 << 16)
197  | ((uint32_t) byte2 << 8)
198  | (uint32_t) byte1;
199  return data;
200  }
201 
202  private:
203  // Give access to mIsDirty.
204  friend class ::Tm1638ModuleTest_flush;
205 
206  // These come from the TM1638 controller chip datasheet.
207  static uint8_t const kDataCmdWriteDisplay = 0b01000000;
208  static uint8_t const kDataCmdReadKeys = 0b01000010;
209  static uint8_t const kDataCmdAutoAddress = 0b01000000;
210  static uint8_t const kDataCmdFixedAddress = 0b01000100;
211  static uint8_t const kAddressCmd = 0b11000000;
212  static uint8_t const kBrightnessCmd = 0b10000000;
213  static uint8_t const kBrightnessLevelOn = 0b00001000;
214 
215  // The ordering of these fields is partially determined to save memory on
216  // 32-bit processors.
217 
218  // TM1638 interface object. Copied by value instead of reference to avoid an
219  // extra level of indirection.
220  const T_TMII mTmiInterface;
221 
222  uint8_t mPatterns[T_DIGITS];
223  bool mDisplayOn;
224 };
225 
226 } // ace_segment
227 
228 #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::Tm1638AnodeModule::flush
void flush()
Send segment patterns of all digits plus the brightness to the display.
Definition: Tm1638AnodeModule.h:123
ace_segment::Tm1638AnodeModule::Tm1638AnodeModule
Tm1638AnodeModule(const T_TMII &tmiInterface)
Constructor.
Definition: Tm1638AnodeModule.h:58
ace_segment::LedModule::isAnyDigitDirty
bool isAnyDigitDirty() const
Return true if any digits are dirty.
Definition: LedModule.h:131
ace_segment::Tm1638AnodeModule
An implementation of LedModule using the TM1638 chip.
Definition: Tm1638AnodeModule.h:48
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::Tm1638AnodeModule::readButtons
uint32_t readButtons() const
Read the 4 bytes with key information.
Definition: Tm1638AnodeModule.h:179
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::Tm1638AnodeModule::begin
void begin()
Initialize the module.
Definition: Tm1638AnodeModule.h:73
ace_segment::Tm1638AnodeModule::end
void end()
Signal end of usage.
Definition: Tm1638AnodeModule.h:81
ace_segment::Tm1638AnodeModule::isFlushRequired
bool isFlushRequired() const
Return true if flushing required.
Definition: Tm1638AnodeModule.h:103
ace_segment::Tm1638AnodeModule::setDisplayOn
void setDisplayOn(bool on=true)
Turn off the entire display.
Definition: Tm1638AnodeModule.h:93