1 //
2 // Copyright (c) 2010-2024 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Time;
11 
12 namespace Antmicro.Renode.Peripherals.Miscellaneous
13 {
14     public class Button : IPeripheral, IGPIOSender
15     {
16         // Registration address ('gpio 3' in the example below) has no influence on the button's logic.
17         // It's just a way to inform the peripherals tree ('peripherals' command) how the button is
18         // connected to the GPIO controller. The actual connection is done with '-> gpio@3'.
19         //
20         // button: Miscellaneous.Button @ gpio 3
21         //     -> gpio@3
Button(bool invert = false)22         public Button(bool invert = false)
23         {
24             ReleaseOnReset = true;
25             Inverted = invert;
26             IRQ = new GPIO();
27 
28             Reset();
29         }
30 
Reset()31         public void Reset()
32         {
33             if(ReleaseOnReset)
34             {
35                 Press();
36                 Release();
37             }
38             else
39             {
40                 // Toggle the button twice to refresh it's state to the same value as before the reset
41                 Toggle();
42                 Toggle();
43             }
44         }
45 
PressAndRelease()46         public void PressAndRelease()
47         {
48             Press();
49             Release();
50         }
51 
Press()52         public void Press()
53         {
54             SetGPIO(!Inverted);
55             Pressed = true;
56             OnStateChange(true);
57         }
58 
Release()59         public void Release()
60         {
61             SetGPIO(Inverted);
62             Pressed = false;
63             OnStateChange(false);
64         }
65 
Toggle()66         public void Toggle()
67         {
68             if(Pressed)
69             {
70                 Release();
71             }
72             else
73             {
74                 Press();
75             }
76         }
77 
78         public GPIO IRQ { get; }
79 
80         public event Action<bool> StateChanged;
81 
82         public bool Pressed { get; private set; }
83 
84         public bool Inverted { get; private set; }
85 
86         public bool ReleaseOnReset { get; set; }
87 
OnStateChange(bool pressed)88         private void OnStateChange(bool pressed)
89         {
90             var sc = StateChanged;
91             if (sc != null)
92             {
93                 sc(pressed);
94             }
95         }
96 
SetGPIO(bool value)97         private void SetGPIO(bool value)
98         {
99             if(!this.TryGetMachine(out var machine))
100             {
101                 // can happen during button creation
102                 IRQ.Set(value);
103                 return;
104             }
105             if(!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts))
106             {
107                 // this is almost always the case, but maybe someday we'll be able to press the
108                 // button by a machine-controlled actuator
109                 vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld);
110             }
111 
112             machine.HandleTimeDomainEvent(IRQ.Set, value, vts);
113         }
114     }
115 }
116