-
Notifications
You must be signed in to change notification settings - Fork 598
/
Copy pathM5ToughPowerControl.cs
229 lines (201 loc) · 8.16 KB
/
M5ToughPowerControl.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Device.Analog;
using System.Device.Gpio;
using System.Device.I2c;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Iot.Device.Arduino;
using Iot.Device.Common;
using Iot.Device.Axp192;
using Microsoft.Extensions.Logging;
using UnitsNet;
namespace Iot.Device.M5Stack
{
/// <summary>
/// High-level abstraction for the AXP192 in an M5Tough enclosure.
/// This binding knows the hardware connections from the ESP32 CPU to the AXP192 and the connected hardware and directly offers to toggle named devices (e.g. switch the
/// display backlight on and off.
/// </summary>
/// <remarks>
/// This binding is useful together with the Arduino/Firmata binding or the Arduino Compiler.
/// </remarks>
public class M5ToughPowerControl : IDisposable
{
private Axp192.Axp192 _axp;
private ILogger _logger;
private Board.Board _board;
/// <summary>
/// Create an instance of this class
/// </summary>
/// <param name="board">The board connection (typically an instance of ArduinoBoard)</param>
public M5ToughPowerControl(Board.Board board)
{
_board = board;
var bus = board.CreateOrGetI2cBus(0, new int[]
{
21, 22
});
_axp = new Axp192.Axp192(bus.CreateDevice(Axp192.Axp192.I2cDefaultAddress));
_logger = this.GetCurrentClassLogger();
Init();
}
/// <summary>
/// True to enable the speaker, false to mute it.
/// </summary>
public bool EnableSpeaker
{
get
{
return _axp.ReadGpioValue(2) == PinValue.High;
}
set
{
_axp.WriteGpioValue(2, value);
}
}
/// <summary>
/// Configures the AXP hardware to default operational settings.
/// Enables the LCD and sets the default charging mode for the optional battery.
/// </summary>
protected virtual void Init()
{
_axp.SetVbusSettings(true, false, VholdVoltage.V4_0, false, VbusCurrentLimit.MilliAmper500);
_logger.LogInformation("axp: VBus limit off");
// Configure GPIO outputs
// GPIO 0: Bus power enable
_axp.SetGPIO0(Gpio0Behavior.NmosLeakOpenOutput);
_axp.WriteGpioValue(0, PinValue.High);
// GPIO 1: Touch pad reset
_axp.SetGPIO1(GpioBehavior.NmosLeakOpenOutput);
// GPIO 2: Speaker enable
_axp.SetGPIO2(GpioBehavior.NmosLeakOpenOutput);
// GPIO 4: LCD Reset
_axp.SetGPIO4(GpioBehavior.NmosLeakOpenOutput);
_logger.LogInformation("GPIO Ports configured");
_axp.SetBackupBatteryChargingControl(true, BackupBatteryCharingVoltage.V3_0, BackupBatteryChargingCurrent.MicroAmperes200);
// Set MCU voltage (probably not a good idea to mess to much with this one)
_axp.SetDcVoltage(1, ElectricPotential.FromMillivolts(3350));
// LCD backlight voltage
SetLcdVoltage(ElectricPotential.FromMillivolts(3000));
// Peripheral bus voltage (for SD card and LCD logic)
_axp.SetLdoOutput(2, ElectricPotential.FromMillivolts(3300));
// LDO2: Peripheral voltage
_axp.SetLdoEnable(2, true);
// LDO3: LCD Backlight
_axp.SetLdoEnable(3, true);
// Unfortunately, the button cannot really be pressed on the M5Tough, unless the housing is not screwed together
_axp.SetButtonBehavior(LongPressTiming.S2_5, ShortPressTiming.Ms128, true, SignalDelayAfterPowerUp.Ms64, ShutdownTiming.S6);
// Enable all ADCs
_axp.SetAdcState(true);
// Pull GPIO4 low for a moment to reset the LCD driver IC
_axp.WriteGpioValue(4, PinValue.Low);
// Pull GPIO1 low for resetting the touch driver IC as well
_axp.WriteGpioValue(1, PinValue.Low);
Thread.Sleep(100);
_axp.WriteGpioValue(4, PinValue.High);
_axp.WriteGpioValue(1, PinValue.High);
// Ensure GPIO2 (Speaker enable) is high
_axp.WriteGpioValue(2, PinValue.High);
Thread.Sleep(100);
// Configure charging for external battery (not fitted to M5Tough by default)
_axp.SetChargingFunctions(true, ChargingVoltage.V4_2, ChargingCurrent.Current450mA, ChargingStopThreshold.Percent10);
}
/// <summary>
/// Beeps using the speaker for the given time, using a fixed frequency
/// </summary>
/// <param name="duration">The duration of the beep</param>
public void Beep(TimeSpan duration)
{
bool isEnabled = EnableSpeaker;
EnableSpeaker = true;
using var pwm = _board.CreatePwmChannel(0, 2);
pwm.DutyCycle = 0.5f;
pwm.Start();
Thread.Sleep(duration);
pwm.Stop();
EnableSpeaker = isEnabled;
}
/// <summary>
/// Set LCD backlight voltage. Valid values range from 1.8 - 3.3V. Low values switch off the backlight completely
/// </summary>
/// <param name="voltage">The voltage to set</param>
public virtual void SetLcdVoltage(ElectricPotential voltage)
{
if (voltage.Millivolts < 1800)
{
_axp.SetLdoEnable(3, false);
}
else
{
_axp.SetLdoEnable(3, true);
}
_axp.SetLdoOutput(3, voltage);
_logger.LogInformation($"LCD voltage set to {voltage}");
}
/// <summary>
/// Enter and leave low-power mode.
/// The physical power button that is normally used to recover the AXP from sleep mode is not accessible on the M5Tough when the case is closed.
/// After setting the AXP to sleep, we send the CPU to sleep as well. It will only wake up on tapping the screen.
/// </summary>
/// <param name="enterSleep">True to enter sleep, false to recover from sleep.</param>
/// <remarks>After recovering from sleep mode, some peripheral devices might need to be restarted (such as the display controller)</remarks>
public void Sleep(bool enterSleep)
{
_axp.SetSleep(enterSleep, false);
if (enterSleep)
{
SendCpuToSleep();
}
}
private void SendCpuToSleep()
{
// Sends the board to sleep mode
TimeSpan sleepDelay = TimeSpan.FromSeconds(10);
const int wakeupPin = 39; // The screen interrupt is connected to this pin
if (!(_board is ArduinoBoard ab))
{
return;
}
// Trigger value is low-active on the M5Tough
if (!ab.SetSystemVariable(SystemVariable.SleepModeInterruptEnable, wakeupPin, 0))
{
return;
}
if (!ab.SetSystemVariable(SystemVariable.EnterSleepMode, wakeupPin, (int)sleepDelay.TotalSeconds))
{
return;
}
}
/// <summary>
/// Gets a data set of current power parameters (power consumption, battery level, etc.)
/// </summary>
/// <returns></returns>
public PowerControlData GetPowerControlData()
{
return _axp.GetPowerControlData();
}
/// <summary>
/// Default dispose implementation
/// </summary>
/// <param name="disposing">True to dispose managed resources, false to dispose only unmanaged resources</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_axp.Dispose();
}
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}