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