-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathaxp209.py
More file actions
309 lines (249 loc) · 9.66 KB
/
Copy pathaxp209.py
File metadata and controls
309 lines (249 loc) · 9.66 KB
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function
from ctypes import c_uint8, BigEndianStructure, Union
from smbus2 import SMBus
AXP209_ADDRESS = 0x34 #7 bit address (will be left shifted to add the read write bit)
# from CHIP battery.sh script
POWER_INPUT_STATUS_REG = 0x00
POWER_OPERATING_MODE_REG = 0x01
CHARGE_CONTROL_REG = 0x33
CHARGE_CONTROL2_REG = 0x34
ADC_ENABLE1_REG = 0x82
INTERNAL_TEMPERATURE_MSB_REG = 0x5e
INTERNAL_TEMPERATURE_LSB_REG = 0x5f
BATTERY_VOLTAGE_MSB_REG = 0x78
BATTERY_VOLTAGE_LSB_REG = 0x79
BATTERY_CHARGE_CURRENT_MSB_REG = 0x7a
BATTERY_CHARGE_CURRENT_LSB_REG = 0x7b
BATTERY_DISCHARGE_CURRENT_MSB_REG = 0x7c
BATTERY_DISCHARGE_CURRENT_LSB_REG = 0x7d
GPIO0_FEATURE_SET_REG = 0x90
GPIO1_FEATURE_SET_REG = 0x92
GPIO2_FEATURE_SET_REG = 0x93
BATTERY_GAUGE_REG = 0xb9
VBUS_IPSOUT_CHANNEL_MANAGEMENT_REG = 0X30
class Union(Union):
def __repr__(self):
return self.__str__()
def __str__(self):
flags = " ".join("{}: {}".format(t[0], getattr(self._b, t[0])) for t in self._b._fields_)
return "<{}>".format(flags)
class ADC_ENABLE1_FLAGS(Union):
class _b(BigEndianStructure):
_fields_ = [
("battery_voltage_adc_enable", c_uint8, 1),
("battery_current_adc_enable", c_uint8, 1),
("acin_voltage_adc_enable", c_uint8, 1),
("acin_current_adc_enable", c_uint8, 1),
("vbus_voltage_adc_enable", c_uint8, 1),
("vbus_current_adc_enable", c_uint8, 1),
("aps_voltage_adc_enable", c_uint8, 1),
("ts_pin_adc_function_enable", c_uint8, 1),
]
_fields_ = [("_b", _b),
("asbyte", c_uint8)]
_anonymous_ = ("_b",)
class POWER_INPUT_STATUS_FLAGS(Union):
class _b(BigEndianStructure):
_fields_ = [
("acin_present", c_uint8, 1),
("acin_available", c_uint8, 1),
("vbus_present", c_uint8, 1),
("vbus_available", c_uint8, 1),
("vbus_direction", c_uint8, 1),
("battery_current_direction", c_uint8, 1), # 1: charging, 0: discharging
("acin_vbus_shorted", c_uint8, 1),
("start_source", c_uint8, 1)
]
_fields_ = [("_b", _b),
("asbyte", c_uint8)]
_anonymous_ = ("_b",)
class POWER_OPERATING_STATUS_FLAGS(Union):
class _b(BigEndianStructure):
_fields_ = [
("over-temperature", c_uint8, 1),
("battery_charging", c_uint8, 1), # 1: charging, 0: not charging or charging done
("battery_exists", c_uint8, 1), # 1: battery is connected, 0: not connected
("_reserved_", c_uint8, 1),
("battery_active", c_uint8, 1),
("reached_desired_charge_current", c_uint8, 1),
("_reserved_", c_uint8, 2),
]
_fields_ = [("_b", _b),
("asbyte", c_uint8)]
_anonymous_ = ("_b",)
class GPIO012_FEATURE_SET_FLAGS(Union):
class _b(BigEndianStructure):
_fields_ = [
("gpio_rising_edge_interupt", c_uint8, 1),
("gpio_falling_edge_interupt", c_uint8, 1),
("_reserved_", c_uint8, 3),
("gpio_function", c_uint8, 3),
]
_fields_ = [("_b", _b),
("asbyte", c_uint8)]
_anonymous_ = ("_b",)
class VBUS_CURRENT_LIMIT_CONTROL(Union):
class _b(BigEndianStructure):
_fields_ = [
("vbus_available", c_uint8, 1),
("hold_pressure_limiting", c_uint8, 1),
("hold_set_up", c_uint8, 3),
("_reserved_", c_uint8, 1),
("vbus_current_limit", c_uint8, 2),
]
_fields_ = [("_b", _b),
("asbyte", c_uint8)]
_anonymous_ = ("_b",)
class AXP209(object):
def __init__(self, bus=0):
"""
Initialize the AXP209 object
:param bus: i2c bus number or a SMBus object
:type bus: Integer or SMBus object
"""
if isinstance(bus, int):
self.bus = SMBus(bus, force=True)
self.autocleanup = True
else:
self.bus = bus
self.autocleanup = False
# force ADC enable for battery voltage and current
self.adc_enable1 = 0xc3
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.autocleanup:
self.close()
def close(self):
self.bus.close()
@property
def gpio2_output(self):
pass
@gpio2_output.setter
def gpio2_output(self, val):
flags = GPIO012_FEATURE_SET_FLAGS()
if bool(val):
flags.gpio_function = 0b111
else:
flags.gpio_function = 0b000
self.bus.write_byte_data(AXP209_ADDRESS, GPIO2_FEATURE_SET_REG, flags.asbyte)
@property
def adc_enable1(self):
flags = ADC_ENABLE1_FLAGS()
flags.asbyte = self.bus.read_byte_data(AXP209_ADDRESS, ADC_ENABLE1_REG)
return flags
@adc_enable1.setter
def adc_enable1(self, flags):
if hasattr(flags, "asbyte"):
flags = flags.asbyte
self.bus.write_byte_data(AXP209_ADDRESS, ADC_ENABLE1_REG, flags)
@property
def vbus_current_limit(self):
""" Returns the current vbus current limit setting """
limits = { #00:900 mA; 01:500 mA; 10:100 mA; 11: not limit
0: "900 mA",
1: "500 mA",
2: "100 mA",
3: "not limited",
}
current_data = self.bus.read_byte_data(AXP209_ADDRESS, VBUS_IPSOUT_CHANNEL_MANAGEMENT_REG)
current_limit = current_data & 0x03
return limits.get(current_limit, "invalid setting")
@vbus_current_limit.setter
def vbus_current_limit(self, val):
flags = VBUS_CURRENT_LIMIT_CONTROL()
limits = { #00:900 mA; 01:500 mA; 10:100 mA; 11: not limit
0: "900 mA",
1: "500 mA",
2: "100 mA",
3: "no limit",
}
for setting, limit in limits.items():
if limit == val:
flags.vbus_current_limit = setting
self.bus.write_byte_data(AXP209_ADDRESS, VBUS_IPSOUT_CHANNEL_MANAGEMENT_REG, flags.asbyte)
@property
def power_input_status(self):
flags = POWER_INPUT_STATUS_FLAGS()
flags.asbyte = self.bus.read_byte_data(AXP209_ADDRESS, POWER_INPUT_STATUS_REG)
return flags
@property
def battery_current_direction(self):
return bool(self.power_input_status.battery_current_direction)
@property
def power_operating_mode(self):
flags = POWER_OPERATING_STATUS_FLAGS()
flags.asbyte = self.bus.read_byte_data(AXP209_ADDRESS, POWER_OPERATING_MODE_REG)
return flags
@property
def battery_exists(self):
return bool(self.power_operating_mode.battery_exists)
@property
def battery_charging(self):
return bool(self.power_operating_mode.battery_charging)
@property
def battery_voltage(self):
""" Returns voltage in mV """
msb = self.bus.read_byte_data(AXP209_ADDRESS, BATTERY_VOLTAGE_MSB_REG)
lsb = self.bus.read_byte_data(AXP209_ADDRESS, BATTERY_VOLTAGE_LSB_REG)
voltage_bin = msb << 4 | lsb & 0x0f
return voltage_bin * 1.1
@property
def battery_charge_current(self):
""" Returns current in mA """
msb = self.bus.read_byte_data(AXP209_ADDRESS, BATTERY_CHARGE_CURRENT_MSB_REG)
lsb = self.bus.read_byte_data(AXP209_ADDRESS, BATTERY_CHARGE_CURRENT_LSB_REG)
# (12 bits)
charge_bin = msb << 4 | lsb & 0x0f
# 0 mV -> 000h, 0.5 mA/bit FFFh -> 1800 mA
return charge_bin * 0.5
@property
def battery_discharge_current(self):
""" Returns current in mA """
msb = self.bus.read_byte_data(AXP209_ADDRESS, BATTERY_DISCHARGE_CURRENT_MSB_REG)
lsb = self.bus.read_byte_data(AXP209_ADDRESS, BATTERY_DISCHARGE_CURRENT_LSB_REG)
# 13bits
discharge_bin = msb << 5 | lsb & 0x1f
# 0 mV -> 000h, 0.5 mA/bit 1FFFh -> 1800 mA
return discharge_bin * 0.5
@property
def internal_temperature(self):
""" Returns temperature in celsius C """
temp_msb = self.bus.read_byte_data(AXP209_ADDRESS, INTERNAL_TEMPERATURE_MSB_REG)
temp_lsb = self.bus.read_byte_data(AXP209_ADDRESS, INTERNAL_TEMPERATURE_LSB_REG)
# MSB is 8 bits, LSB is lower 4 bits
temp = temp_msb << 4 | temp_lsb & 0x0f
# -144.7c -> 000h, 0.1c/bit FFFh -> 264.8c
return temp*0.1-144.7
@property
def battery_gauge(self):
gauge_bin = self.bus.read_byte_data(AXP209_ADDRESS, BATTERY_GAUGE_REG)
gauge = gauge_bin & 0x7f
if gauge > 100:
return -1
return gauge
def print_axp(axp):
print("internal_temperature: %.2fC" % axp.internal_temperature)
print("battery_exists: %s" % axp.battery_exists)
print("battery_charging: %s" % ("charging" if axp.battery_charging else "done"))
print("battery_current_direction: %s" % ("charging" if axp.battery_current_direction else "discharging"))
print("battery_voltage: %.1fmV" % axp.battery_voltage)
print("battery_discharge_current: %.1fmA" % axp.battery_discharge_current)
print("battery_charge_current: %.1fmA" % axp.battery_charge_current)
print("battery_gauge: %d%%" % axp.battery_gauge)
print("vbus_current_limit: %s" % axp.vbus_current_limit)
def main():
"""CLI entrypoint used by setup.py console_scripts
"""
import argparse
parser = argparse.ArgumentParser(description='Show info about AXP209 PMS')
parser.add_argument('--bus', type=int, default=0,
help='the SMBus bus number [integer] (default: 0)')
args = parser.parse_args()
axp = AXP209(args.bus)
print_axp(axp)
axp.close()
if __name__ == "__main__":
main()