]> fbox.kageds.com Git - richie-water-pump.git/blob - sevseg.cpp
1st commit
[richie-water-pump.git] / sevseg.cpp
1 /* SevSeg Library
2
3 Copyright 2017 Dean Reading
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15
16
17 This library allows an Arduino to easily display numbers in decimal format on
18 a 7-segment display without a separate 7-segment display controller.
19
20 Direct any questions or suggestions to deanreading@hotmail.com
21 See the included readme for instructions.
22 https://github.com/DeanIsMe/SevSeg
23
24 CHANGELOG
25 Version 3.3.0 - February 2017
26 Added the ability to keep leading zeros. This is now an extra
27 parameter in the begin() function.
28
29 Version 3.2.0 - December 2016
30 Updated to Arduino 1.5 Library Specification
31 New display function - no longer consumes processor time with delay()
32 Now supports hexadecimal number printing
33 The decimal point can now be omitted with a negative decPlaces
34 decPlaces is now optional in setNumber
35 Alphanumeric strings can be displayed (inaccurately) with setChars()
36 Removed #define RESISTORS_ON_SEGMENTS. Now a begin() input
37 Can now blank() the display
38
39 Version 3.1 - September 2016
40 Bug Fixes. No longer uses dynamic memory allocation.
41 Version 3.0 - November 2014
42 Library re-design. A display with any number of digits can be used.
43 Floats are supported. Support for using transistors for switching.
44 Much more user friendly. No backwards compatibility.
45 Uploaded to GitHub to simplify any further development.
46 Version 2.3; Allows for brightness control.
47 Version 2.2; Allows 1, 2 or 3 digit displays to be used.
48 Version 2.1; Includes a bug fix.
49 Version 2.0; Now works for any digital pin arrangement.
50 Supports both common anode and common cathode displays.
51 */
52
53 #include "sevseg.h"
54
55 #include "pico/stdlib.h"
56
57 #define BLANK_IDX 36 // Must match with 'digitCodeMap'
58 #define DASH_IDX 37
59
60 #define LOW false
61 #define HIGH true
62
63 static const long powersOf10[] = {
64 1, // 10^0
65 10,
66 100,
67 1000,
68 10000,
69 100000,
70 1000000,
71 10000000,
72 100000000,
73 1000000000
74 }; // 10^9
75
76 static const long powersOf16[] = {
77 0x1, // 16^0
78 0x10,
79 0x100,
80 0x1000,
81 0x10000,
82 0x100000,
83 0x1000000,
84 0x10000000
85 }; // 16^7
86
87 // The codes below indicate which segments must be illuminated to display
88 // each number.
89 static const byte digitCodeMap[] = {
90 // GFEDCBA Segments 7-segment map:
91 0x3f, // 0
92 0x06, // 1
93 0x5b, // 2
94 0x4f, // 3
95 0x66, // 4
96 0x6d, // 5
97 0x7d, // 6
98 0x07, // 7
99 0x7f, // 8
100 0x67 // 9
101 };
102
103 // Constant pointers to constant data
104 const byte * const numeralCodes = digitCodeMap;
105
106 // SevSeg Constructor
107 /******************************************************************************/
108 SevSeg::SevSeg()
109 {
110 // Initial value
111 ledOnTime = 2000; // Corresponds to a brightness of 100
112 numDigits = 0;
113 prevUpdateIdx = 0;
114 prevUpdateTime = 0;
115 resOnSegments = 0;
116 updateWithDelays = 0;
117 }
118
119
120 // begin
121 /******************************************************************************/
122 // Saves the input pin numbers to the class and sets up the pins to be used.
123 // If you use current-limiting resistors on your segment pins instead of the
124 // digit pins, then set resOnSegments as true.
125 // Set updateWithDelays to true if you want to use the 'pre-2017' update method
126 // That method occupies the processor with delay functions.
127 void SevSeg::begin(byte hardwareConfig, byte numDigitsIn, byte digitPinsIn[],
128 byte segmentPinsIn[], bool resOnSegmentsIn,
129 bool updateWithDelaysIn, bool leadingZerosIn) {
130
131 resOnSegments = resOnSegmentsIn;
132 updateWithDelays = updateWithDelaysIn;
133 leadingZeros = leadingZerosIn;
134
135 numDigits = numDigitsIn;
136 //Limit the max number of digits to prevent overflowing
137 if (numDigits > MAXNUMDIGITS) numDigits = MAXNUMDIGITS;
138
139 switch (hardwareConfig) {
140
141 case 0: // Common cathode
142 digitOn = LOW;
143 segmentOn = HIGH;
144 break;
145
146 case 1: // Common anode
147 digitOn = HIGH;
148 segmentOn = LOW;
149 break;
150
151 case 2: // With active-high, low-side switches (most commonly N-type FETs)
152 digitOn = HIGH;
153 segmentOn = HIGH;
154 break;
155
156 case 3: // With active low, high side switches (most commonly P-type FETs)
157 digitOn = LOW;
158 segmentOn = LOW;
159 break;
160 }
161
162 digitOff = !digitOn;
163 segmentOff = !segmentOn;
164
165 // Save the input pin numbers to library variables
166 for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
167 segmentPins[segmentNum] = segmentPinsIn[segmentNum];
168 }
169
170 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
171 digitPins[digitNum] = digitPinsIn[digitNum];
172 }
173
174 // Set the pins as outputs, and turn them off
175 for (byte digit = 0 ; digit < numDigits ; digit++) {
176 gpio_init(digitPins[digit]);
177 gpio_set_dir(digitPins[digit], GPIO_OUT);
178 gpio_put(digitPins[digit], segmentOff);
179 }
180
181 for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
182 gpio_init(segmentPins[segmentNum]);
183 gpio_set_dir(segmentPins[segmentNum], GPIO_OUT);
184 gpio_put(segmentPins[segmentNum], digitOff);
185 }
186
187 setNewNum(0, 0); // Initialise the number displayed to 0
188 }
189
190
191 // refreshDisplay
192 /******************************************************************************/
193 // Turns on the segments specified in 'digitCodes[]'
194 // There are 4 versions of this function, with the choice depending on the
195 // location of the current-limiting resistors, and whether or not you wish to
196 // use 'update delays' (the standard method until 2017).
197 // For resistors on *digits* we will cycle through all 8 segments (7 + period),
198 // turning on the *digits* as appropriate for a given segment, before moving on
199 // to the next segment.
200 // For resistors on *segments* we will cycle through all __ # of digits,
201 // turning on the *segments* as appropriate for a given digit, before moving on
202 // to the next digit.
203 // If using update delays, refreshDisplay has a delay between each digit/segment
204 // as it cycles through. It exits with all LEDs off.
205 // If not using updateDelays, refreshDisplay exits with a single digit/segment
206 // on. It will move to the next digit/segment after being called again (if
207 // enough time has passed).
208
209 void SevSeg::refreshDisplay() {
210
211 if (!updateWithDelays) {
212
213 // Exit if it's not time for the next display change
214 if (time_us_32() - prevUpdateTime < ledOnTime) return;
215 prevUpdateTime = time_us_32();
216
217 if (!resOnSegments) {
218 /**********************************************/
219 // RESISTORS ON DIGITS, UPDATE WITHOUT DELAYS
220
221
222 // Turn all lights off for the previous segment
223 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
224 gpio_put(digitPins[digitNum], digitOff);
225 }
226 gpio_put(segmentPins[prevUpdateIdx], segmentOff);
227
228 prevUpdateIdx++;
229 if (prevUpdateIdx >= 8) prevUpdateIdx = 0;
230
231 byte segmentNum = prevUpdateIdx;
232
233 // Illuminate the required digits for the new segment
234 gpio_put(segmentPins[segmentNum], segmentOn);
235 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
236 if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
237 gpio_put(digitPins[digitNum], digitOn);
238 }
239 }
240 }
241 else {
242 /**********************************************/
243 // RESISTORS ON SEGMENTS, UPDATE WITHOUT DELAYS
244
245
246 // Turn all lights off for the previous digit
247 for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
248 gpio_put(segmentPins[segmentNum], segmentOff);
249 }
250 gpio_put(digitPins[prevUpdateIdx], digitOff);
251
252 prevUpdateIdx++;
253 if (prevUpdateIdx >= numDigits) prevUpdateIdx = 0;
254
255 byte digitNum = prevUpdateIdx;
256
257 // Illuminate the required segments for the new digit
258 gpio_put(digitPins[digitNum], digitOn);
259 for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
260 if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
261 gpio_put(segmentPins[segmentNum], segmentOn);
262 }
263 }
264 }
265 }
266
267 else {
268 if (!resOnSegments) {
269 /**********************************************/
270 // RESISTORS ON DIGITS, UPDATE WITH DELAYS
271 for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
272
273 // Illuminate the required digits for this segment
274 gpio_put(segmentPins[segmentNum], segmentOn);
275 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
276 if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
277 gpio_put(digitPins[digitNum], digitOn);
278 }
279 }
280
281 //Wait with lights on (to increase brightness)
282 sleep_us(ledOnTime);
283
284 //Turn all lights off
285 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
286 gpio_put(digitPins[digitNum], digitOff);
287 }
288 gpio_put(segmentPins[segmentNum], segmentOff);
289 }
290 }
291 else {
292 /**********************************************/
293 // RESISTORS ON SEGMENTS, UPDATE WITH DELAYS
294 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
295
296 // Illuminate the required segments for this digit
297 gpio_put(digitPins[digitNum], digitOn);
298 for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
299 if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
300 gpio_put(segmentPins[segmentNum], segmentOn);
301 }
302 }
303
304 //Wait with lights on (to increase brightness)
305 sleep_us(ledOnTime);
306
307 //Turn all lights off
308 for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
309 gpio_put(segmentPins[segmentNum], segmentOff);
310 }
311 gpio_put(digitPins[digitNum], digitOff);
312 }
313 }
314 }
315 }
316
317
318 // setNumber
319 /******************************************************************************/
320 // This function only receives the input and passes it to 'setNewNum'.
321 // It is overloaded for all number data types, so that floats can be handled
322 // correctly.
323
324 void SevSeg::setNumber(long numToShow, char decPlaces, bool hex) //long
325 {
326 setNewNum(numToShow, decPlaces, hex);
327 }
328
329 void SevSeg::setNumber(unsigned long numToShow, char decPlaces, bool hex) //unsigned long
330 {
331 setNewNum(numToShow, decPlaces, hex);
332 }
333
334 void SevSeg::setNumber(int numToShow, char decPlaces, bool hex) //int
335 {
336 setNewNum(numToShow, decPlaces, hex);
337 }
338
339 void SevSeg::setNumber(unsigned int numToShow, char decPlaces, bool hex) //unsigned int
340 {
341 setNewNum(numToShow, decPlaces, hex);
342 }
343
344 void SevSeg::setNumber(char numToShow, char decPlaces, bool hex) //char
345 {
346 setNewNum(numToShow, decPlaces, hex);
347 }
348
349 void SevSeg::setNumber(byte numToShow, char decPlaces, bool hex) //byte
350 {
351 setNewNum(numToShow, decPlaces, hex);
352 }
353
354 void SevSeg::setNumber(float numToShow, char decPlaces, bool hex) //float
355 {
356 char decPlacesPos = decPlaces;
357 if (hex) {
358 numToShow = numToShow * powersOf16[decPlacesPos];
359 }
360 else {
361 numToShow = numToShow * powersOf10[decPlacesPos];
362 }
363 // Modify the number so that it is rounded to an integer correctly
364 numToShow += (numToShow >= 0) ? 0.5f : -0.5f;
365 setNewNum(numToShow, decPlaces, hex);
366 }
367
368
369 // setNewNum
370 /******************************************************************************/
371 // Changes the number that will be displayed.
372
373 void SevSeg::setNewNum(long numToShow, char decPlaces, bool hex) {
374 byte digits[numDigits];
375 findDigits(numToShow, decPlaces, hex, digits);
376 setDigitCodes(digits, decPlaces);
377 }
378
379
380 // setSegments
381 /******************************************************************************/
382 // Sets the 'digitCodes' that are required to display the desired segments.
383 // Using this function, one can display any arbitrary set of segments (like
384 // letters, symbols or animated cursors). See setDigitCodes() for common
385 // numeric examples.
386 //
387 // Bit-segment mapping: 0bHGFEDCBA
388 // Visual mapping:
389 // AAAA 0000
390 // F B 5 1
391 // F B 5 1
392 // GGGG 6666
393 // E C 4 2
394 // E C 4 2 (Segment H is often called
395 // DDDD H 3333 7 DP, for Decimal Point)
396
397 void SevSeg::setSegments(byte segs[])
398 {
399 for (byte digit = 0; digit < numDigits; digit++) {
400 digitCodes[digit] = segs[digit];
401 }
402 }
403
404 // setChars
405 /******************************************************************************/
406 // Displays the string on the display, as best as possible.
407 // Only numeric characters are supported
408 void SevSeg::setChars(char str[])
409 {
410 for (byte digit = 0; digit < numDigits; digit++) {
411 digitCodes[digit] = 0;
412 }
413
414 for (byte digitNum = 0; digitNum < numDigits; digitNum++) {
415 char ch = str[digitNum];
416 if (ch == '\0') break; // NULL string terminator
417 if (ch >= '0' && ch <= '9') { // Numerical
418 digitCodes[digitNum] = numeralCodes[ch - '0'];
419 }
420 }
421 }
422
423 // blank
424 /******************************************************************************/
425 void SevSeg::blank(void) {
426 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
427 digitCodes[digitNum] = digitCodeMap[BLANK_IDX];
428 }
429 refreshDisplay();
430 }
431
432 // findDigits
433 /******************************************************************************/
434 // Decides what each digit will display.
435 // Enforces the upper and lower limits on the number to be displayed.
436
437 void SevSeg::findDigits(long numToShow, char decPlaces, bool hex, byte digits[]) {
438 const long * powersOfBase = hex ? powersOf16 : powersOf10;
439 const long maxNum = powersOfBase[numDigits] - 1;
440 const long minNum = -(powersOfBase[numDigits - 1] - 1);
441
442 // If the number is out of range, just display dashes
443 if (numToShow > maxNum || numToShow < minNum) {
444 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
445 digits[digitNum] = DASH_IDX;
446 }
447 }
448 else {
449 byte digitNum = 0;
450
451 // Convert all number to positive values
452 if (numToShow < 0) {
453 digits[0] = DASH_IDX;
454 digitNum = 1; // Skip the first iteration
455 numToShow = -numToShow;
456 }
457
458 // Find all digits for base's representation, starting with the most
459 // significant digit
460 for ( ; digitNum < numDigits ; digitNum++) {
461 long factor = powersOfBase[numDigits - 1 - digitNum];
462 digits[digitNum] = numToShow / factor;
463 numToShow -= digits[digitNum] * factor;
464 }
465
466 // Find unnnecessary leading zeros and set them to BLANK
467 if (decPlaces < 0) decPlaces = 0;
468 if (!leadingZeros) {
469 for (digitNum = 0 ; digitNum < (numDigits - 1 - decPlaces) ; digitNum++) {
470 if (digits[digitNum] == 0) {
471 digits[digitNum] = BLANK_IDX;
472 }
473 // Exit once the first non-zero number is encountered
474 else if (digits[digitNum] <= 9) {
475 break;
476 }
477 }
478 }
479
480 }
481 }
482
483
484 // setDigitCodes
485 /******************************************************************************/
486 // Sets the 'digitCodes' that are required to display the input numbers
487
488 void SevSeg::setDigitCodes(byte digits[], char decPlaces) {
489
490 // Set the digitCode for each digit in the display
491 for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
492 digitCodes[digitNum] = digitCodeMap[digits[digitNum]];
493 // Set the decimal place segment
494 if (decPlaces >= 0) {
495 if (digitNum == numDigits - 1 - decPlaces) {
496 digitCodes[digitNum] |= 0x80;
497 }
498 }
499 }
500 }
501
502 /// END ///