1 //
2 // Copyright (c) 2020 LabMICRO FACET UNT
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Linq;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Migrant;
12 using System.Collections.Generic;
13 using System.Text;
14 
15 namespace Antmicro.Renode.Peripherals.Miscellaneous
16 {
17     // This class implements an multiplexed seven segment display with a variable number of digits
18     // First eighths input gpio lines are used to handle from "a" to "f" segments and dot point
19     // Remaining input gpio lines are used to enable each of digits starting from left
20     // To activate a segment, the digit and segment gpio inputs must be set to true
21     // Anode and catode common displays can be emulated with invertSegments and invertDigits parameters
22 
23     public class SevenSegmentsDisplay : IGPIOReceiver
24     {
SevenSegmentsDisplay(uint digitsCount = 1, bool invertSegments = false, bool invertDigits = false)25         public SevenSegmentsDisplay(uint digitsCount = 1, bool invertSegments = false, bool invertDigits = false)
26         {
27             this.invertSegments = invertSegments;
28             this.invertDigits = invertDigits;
29 
30             digit = new Digit();
31             enabledDigits = new bool[digitsCount];
32 
33             sync = new object();
34             Reset();
35         }
36 
Reset()37         public void Reset()
38         {
39             digit.Clear();
40             for(var index = 0; index < enabledDigits.Length; index++)
41             {
42                 enabledDigits[index] = invertDigits;
43             }
44 
45             Update();
46         }
47 
OnGPIO(int number, bool value)48         public void OnGPIO(int number, bool value)
49         {
50             if(number >= 0 && number < SegmentsCount)
51             {
52                 digit.SetSegment((Segments)(1 << number), invertSegments ? !value : value);
53             }
54             else if(number >= SegmentsCount && number - SegmentsCount < enabledDigits.Length)
55             {
56                 enabledDigits[number - SegmentsCount] = invertDigits ? !value : value;
57             }
58             else
59             {
60                 this.Log(LogLevel.Error, "This device can handle GPIOs in range 0 - {0}, but {1} was set", SegmentsCount + enabledDigits.Length, number);
61                 return;
62             }
63 
64             Update();
65         }
66 
67         [field: Transient]
68         public event Action<IPeripheral, string> StateChanged;
69 
70         public string Image { get; private set; }
71 
72         public string State { get; private set; }
73 
Update()74         private void Update()
75         {
76             lock(sync)
77             {
78                 var newState = AsSegmentsString();
79                 if(newState == State)
80                 {
81                     return;
82                 }
83 
84                 State = newState;
85                 Image = AsPrettyString();
86 
87                 StateChanged?.Invoke(this, State);
88 
89                 this.Log(LogLevel.Noisy, "Seven Segments state changed to {0} {1}", State, Image);
90             }
91         }
92 
AsPrettyString()93         private string AsPrettyString()
94         {
95             var result = new StringBuilder();
96 
97             result.Append("(");
98             foreach(var isEnabled in enabledDigits)
99             {
100                 result.Append(isEnabled
101                     ? digit.AsString()
102                     : "_");
103             }
104             result.Append(")");
105 
106             return result.ToString();
107         }
108 
AsSegmentsString()109         private string AsSegmentsString()
110         {
111             var result = new StringBuilder();
112 
113             foreach(var isEnabled in enabledDigits)
114             {
115                 result.Append("[");
116                 if(isEnabled)
117                 {
118                     result.Append(digit.Value.ToString());
119                 }
120                 result.Append("]");
121             }
122             return result.ToString();
123         }
124 
125         private readonly Digit digit;
126         private readonly bool[] enabledDigits;
127 
128         private readonly bool invertSegments;
129         private readonly bool invertDigits;
130         private readonly object sync;
131 
132         private const int SegmentsCount = 8;
133 
134         [Flags]
135         private enum Segments
136         {
137             A = 1 << 0,
138             B = 1 << 1,
139             C = 1 << 2,
140             D = 1 << 3,
141             E = 1 << 4,
142             F = 1 << 5,
143             G = 1 << 6,
144             DOT = 1 << 7
145         }
146 
147         private class Digit
148         {
149             public Segments Value { get; private set; }
150 
SetSegment(Segments segment, bool asOn)151             public void SetSegment(Segments segment, bool asOn)
152             {
153                 if(asOn)
154                 {
155                     Value |= segment;
156                 }
157                 else
158                 {
159                     Value &= ~segment;
160                 }
161             }
162 
Clear()163             public void Clear()
164             {
165                 Value = 0;
166             }
167 
AsString()168             public string AsString()
169             {
170                 var hasDot = (Value & Segments.DOT) == Segments.DOT;
171 
172                 if(!SegmentsToStringMapping.TryGetValue(Value & ~Segments.DOT, out var result))
173                 {
174                     result = "?";
175                 }
176 
177                 if(hasDot)
178                 {
179                     result += ".";
180                 }
181 
182                 return result;
183             }
184 
185             private static readonly Dictionary<Segments, string> SegmentsToStringMapping = new Dictionary<Segments, string>()
186             {
187                 { Segments.A | Segments.B | Segments.C | Segments.D | Segments.E | Segments.F             , "0" },
188                 {              Segments.B | Segments.C                                                    , "1" },
189                 { Segments.A | Segments.B |              Segments.D | Segments.E |              Segments.G, "2" },
190                 { Segments.A | Segments.B | Segments.C | Segments.D |                           Segments.G, "3" },
191                 {              Segments.B | Segments.C |                           Segments.F | Segments.G, "4" },
192                 { Segments.A |              Segments.C | Segments.D |              Segments.F | Segments.G, "5" },
193                 { Segments.A |              Segments.C | Segments.D | Segments.E | Segments.F | Segments.G, "6" },
194                 { Segments.A | Segments.B | Segments.C                                                    , "7" },
195                 { Segments.A | Segments.B | Segments.C | Segments.D | Segments.E | Segments.F | Segments.G, "8" },
196                 { Segments.A | Segments.B | Segments.C | Segments.D |              Segments.F | Segments.G, "9" },
197                 { Segments.A | Segments.B | Segments.C |              Segments.E | Segments.F | Segments.G, "A" },
198                 {                           Segments.C | Segments.D | Segments.E | Segments.F | Segments.G, "B" },
199                 { Segments.A |                           Segments.D | Segments.E | Segments.F             , "C" },
200                 {              Segments.B | Segments.C | Segments.D | Segments.E |              Segments.G, "D" },
201                 { Segments.A |                           Segments.D | Segments.E | Segments.F | Segments.G, "E" },
202                 { Segments.A |                                        Segments.E | Segments.F | Segments.G, "F" },
203             };
204         }
205     }
206 }
207 
208