TPS55288 DC/DC Buck-Boost I2C Driver
- class src.pico_power_management.TPS55288.TPS55288(name: str, address: int, i2c_bus, description=None, *args, **kwargs)
- ALT_ADDR = 117
- DEFAULT_ADDR = 116
- I2C_MAX_FREQ = 1000000
- I2C_MIN_FREQ = 100000
- IOUT_LIMIT: src.pico_power_management.i2c_device.Register
- current_limit(current_limit=None, enable=None)
Query or Command the output current limit and enable status. if no value is given, return the status of the current limit system.
- Parameters
value (int, optional) – Enable value, active high, defaults to None
- feedback_ratio(feedback_ratio=None)
Command and Query the internal feedback ratio. Changes the step size of voltage increments.
0 = 5mV, 1 = 10mV, 2 = 15mV, 3 = 20mV
- Parameters
value (_type_, optional) – _description_, defaults to None
- Returns
Returns the feedback ratio integer
- Return type
int
- feedback_select(select=None)
Command and Query the output feedback voltage location. Defaults to internal.
- Parameters
select (int, optional) – Feedback Select Either external (1) or internal (0), defaults to None
- Returns
Returns the current feedback select setting.
- Return type
int
- initialize_rail(voltage, current_limit)
Startup Sequence for a TPS voltage rail.
- Parameters
voltage (int | float) – Output voltage desired for the rail
current_limit (int | float) – Current limit boundary for the voltage rail
- mode(output_en=None, freq_doubling=None, hiccup=None, discharge=None, vcc=None, i2c_addr=None, pfm=None, mode=None)
Command or Query the operation mode of the device.
- Parameters
output_en (int, optional) – Output enable 0 = off, 1 = on, defaults to None
freq_doubling (int, optional) – Switching frequency doubling in buck-boost mode, 0 = off, 1 = on, defaults to None
hiccup (int, optional) – Hiccup mode, 0 = off, 1 = on, defaults to None
discharge (int, optional) – Output discharged through 100mA current sink when active, 0 = off, 1 = on, defaults to None
vcc (int, optional) – Choose between internal LDO (0, default) or external 5V power supply (1), defaults to None
i2c_addr (int, optional) – Swap between 0x74 (0, default) and 0x75 (1), defaults to None
pfm (int, optional) – Select operating mode at light load condition PFM (0, default) or FPWM (1), defaults to None
mode (int, optional) – Mode control approach set VCC, I2CADD, and PFM either through external resistor(0) or internal register (1), defaults to None
- output_enable(value=None)
Query or Command the output enable status. if no value is given, return the current status.
- Parameters
value (int, optional) – Enable value, active high, defaults to None
- reference_voltage(value=None)
method for setting reference voltage on the device, if no value is given, returns the device set reference voltage.
- Parameters
value (int, optional) – Voltage value, defaults to None
- status()
method to query the status register on the device. Reset’s the STATUS register after reading.
- voltage(value=None)
method for setting the voltage on the device, if no value is given, returns the device set voltage.
- Parameters
value (int, optional) – Voltage value, defaults to None
Code
1# -----------------------------------------
2# NOTES
3# -----------------------------------------
4#
5# Dieter Steinhauser
6# 10/2023
7# TPS55288 I2C Driver
8
9
10# -----------------------------------------
11# IMPORTS
12# -----------------------------------------
13
14desc = r"""
15The TPS55288 uses I2C interface for flexible converter parameter programming. I2C is a bi-directional 2-wire
16serial interface. Only two bus lines are required: a serial data line (SDA) and a serial clock line (SCL). I2C
17devices can be considered as masters or slaves when performing data transfers. A master is the device that
18initiates a data transfer on the bus and generates the clock signals to permit that transfer. At that time, any
19device addressed is considered a slave.
20The TPS55288 operates as a slave device with address 74h and 75h set by a different resistor at the MODE pin.
21Receiving control inputs from the master device like a microcontroller or a digital signal processor reads and
22writes the internal registers 00h through 07h. The I2C interface of the TPS55288 supports both standard mode
23(up to 100 kbit/s) and fast mode plus (up to 1000 kbit/s). Both SDA and SCL must be connected to the positive
24supply voltage through current sources or pullup resistors. When the bus is free, both lines are in high voltage.
25"""
26
27
28from machine import Pin, I2C
29from time import sleep
30from src.pico_power_management.i2c_device import Device, Register, Field
31from src.pico_power_management.helpers import *
32# from i2c_device import Device, Register, Field
33# from helpers import *
34# -----------------------------------------
35# Class:
36# -----------------------------------------
37
38class TPS55288(Device):
39
40 I2C_MIN_FREQ = 100_000
41 I2C_MAX_FREQ = 1_000_000
42 DEFAULT_ADDR = 0x74
43 ALT_ADDR = 0x75
44
45 VREF_LOW: Register
46 VREF_HIGH: Register
47 IOUT_LIMIT: Register
48 VOUT_SR: Register
49 VOUT_FS: Register
50 CDC: Register
51 VOUT_FS: Register
52 MODE: Register
53 STATUS: Register
54
55
56 def __init__(self, name:str, address:int, i2c_bus, description = None, *args, **kwargs) -> None:
57 """Object initialization for TPS55288. Follow device initialization and adds register information to object"""
58 description = desc if desc is None else description
59 super().__init__(name, address, i2c_bus, description, *args, **kwargs)
60
61 # Add device registers
62 self.add_register("VREF_LOW", 0x0, r_w = "R/W", description='Low byte of internal reference voltage')
63 self.add_register("VREF_HIGH", 0x1, r_w = "R/W", description='high byte of internal reference voltage')
64 self.add_register("IOUT_LIMIT", 0x2, r_w = "R/W", description='Current Limit Setting')
65 self.add_register("VOUT_SR", 0x3, r_w = "R/W", description='Slew Rate ')
66 self.add_register("VOUT_FS", 0x4, r_w = "R/W", description='Feedback Selection')
67 self.add_register("CDC", 0x5, r_w = "R/W", description='Cable Compensation')
68 self.add_register("MODE", 0x6, r_w = "R/W", description='Mode Control')
69 self.add_register("STATUS", 0x7, r_w = "R", description='Operating Status')
70
71 # Add fields to each register
72 self.VREF_LOW.add_field(name='REF_LSB', bit_offset=0, width=8, r_w="R/W", description="VREF Lower byte. 10 bits total.")
73
74 self.VREF_HIGH.add_field(name='REF_MSB', bit_offset=0, width=2, r_w="R/W", description="VREF higher bits. 10 bits total.")
75
76 self.IOUT_LIMIT.add_field(name='CURR_LIMIT', bit_offset=0, width=7, r_w="R/W", description="Current Limit, setting the target voltage between ISP and ISN.")
77 self.IOUT_LIMIT.add_field(name='CURR_LIMIT_EN', bit_offset=7, width=1, r_w="R/W", description="Current Limit Enable, Active High and on by default")
78
79 self.VOUT_SR.add_field(name='SR', bit_offset=0, width=2, r_w="R/W", description="Sets slew rate for output voltage change.")
80 self.VOUT_SR.add_field(name='OCP_DELAY', bit_offset=4, width=2, r_w="R/W", description="Sets the response time of the device when the output overcurrent is reached.")
81
82 self.VOUT_FS.add_field(name='INTFB', bit_offset=0, width=2, r_w="R/W", description="Internal Feedback ratio")
83 self.VOUT_FS.add_field(name='FB', bit_offset=7, width=1, r_w="R/W", description="Output feedback voltage.")
84
85 self.CDC.add_field(name='CDC', bit_offset=0, width=3, r_w="R/W", description="Compensation for voltage droop over cable")
86 self.CDC.add_field(name='CDC_OPTION', bit_offset=3, width=1, r_w="R/W", description="Select the cable voltage droop compensation approach")
87 self.CDC.add_field(name='OVP_MASK', bit_offset=5, width=1, r_w="R/W", description="Overvoltage Mask")
88 self.CDC.add_field(name='OCP_MASK', bit_offset=6, width=1, r_w="R/W", description="Overcurrent Mask")
89 self.CDC.add_field(name='SC_MASK', bit_offset=7, width=1, r_w="R/W", description="Short Circuit Mask")
90
91 self.MODE.add_field(name='MODE', bit_offset=0, width=1, r_w="R/W", description="Mode Control Approach")
92 self.MODE.add_field(name='PFM', bit_offset=1, width=1, r_w="R/W", description="Select operating mode at light load condition")
93 self.MODE.add_field(name='I2CADD', bit_offset=2, width=1, r_w="R/W", description="I2C address")
94 self.MODE.add_field(name='VCC', bit_offset=3, width=1, r_w="R/W", description="VCC option")
95 self.MODE.add_field(name='DISCHG', bit_offset=4, width=1, r_w="R/W", description="Output discharge")
96 self.MODE.add_field(name='HICCUP', bit_offset=5, width=1, r_w="R/W", description="Hiccup mode")
97 self.MODE.add_field(name='FSWDBL', bit_offset=6, width=1, r_w="R/W", description="Switching frequency doubling in buck-boost mode")
98 self.MODE.add_field(name='OE', bit_offset=7, width=1, r_w="R/W", description="Output enable")
99
100 self.STATUS.add_field(name='STATUS', bit_offset=0, width=2, r_w="R", description="Operating status")
101 self.STATUS.add_field(name='OVP', bit_offset=5, width=1, r_w="R", description="Overvoltage protection")
102 self.STATUS.add_field(name='OCP', bit_offset=6, width=1, r_w="R", description="Overcurrent protection")
103 self.STATUS.add_field(name='SCP', bit_offset=7, width=1, r_w="R", description="Short circuit protection")
104
105
106 def voltage(self, value=None):
107 """
108 method for setting the voltage on the device, if no value is given, returns the device set voltage.
109
110 :param value: Voltage value, defaults to None
111 :type value: int, optional
112 """
113
114 # read back the voltage on the device
115 if value is None:
116
117 # Throw Error if external feedback is being used.
118 if self.feedback_select() == 1:
119 raise ValueError("Cannot determine state of output voltage digitally because the system is utilizing an external feedback for voltage selection")
120
121 # Check the internal feedback ratio
122 ratio_int = self.feedback_ratio() # 0 = 5mV, 1 = 10mV, 2 = 15mV, 3 = 20mV
123 ratio = (ratio_int+1) * 0.00520833
124
125 # check the reference voltage
126 low = self.VREF_LOW.REF_LSB.read()
127 high = self.VREF_HIGH.REF_MSB.read()
128 ref_value = (high << 8) | low
129
130 # multiply the reference voltage by the feedback ratio to give the output value.
131 voltage = ratio * ref_value
132 return voltage
133
134 # Throw errors for incorrect values given
135 check_type(value, 'value', (int, float))
136 check_range(value, 'value', 0.8, 21)
137
138 # Determine the smallest step size required to produce the desired value
139 if (0 <= value < 5.31):
140 ratio_int = 0
141 elif (5.31 <= value < 10.64):
142 ratio_int = 1
143 elif (10.64 <= value < 15.97):
144 ratio_int = 2
145 else:
146 ratio_int = 3
147
148 # calculate the ratio and reference value required.
149 ratio = (ratio_int+1) * 0.00520833
150 ref_value = 0.045 + int(value/ratio) * 0.001129
151
152 # set the feedback ratio and reference value
153 self.feedback_ratio(ratio_int) # 0 = 5mV, 1 = 10mV, 2 = 15mV, 3 = 20mV
154 self.reference_voltage(ref_value)
155
156
157
158 def reference_voltage(self, value=None):
159 """
160 method for setting reference voltage on the device, if no value is given, returns the device set reference voltage.
161
162 :param value: Voltage value, defaults to None
163 :type value: int, optional
164 """
165
166 # read back the reference voltage on the device
167 if value is None:
168 low = self.VREF_LOW.REF_LSB.read()
169 high = self.VREF_HIGH.REF_MSB.read()
170 ref_value = (high << 8) | low
171
172 # Reference voltage starts at 45mV and goes in 1.129mV increments
173 ref_voltage = 0.045 + ref_value * 0.001129
174 return ref_voltage
175
176 # Throw errors for incorrect values given
177 check_type(value, 'value', (int, float))
178 check_range(value, 'value', 0.045, 1.2)
179
180 # make voltage value into clean byte for data transfer
181 ref_value = int(round((value - 0.045) / 0.001129))
182 ref_low = ref_value & 0x0FF
183 ref_high = (ref_value & 0x300) >> 8
184
185 # write to the registers
186 self.VREF_LOW.write(ref_low)
187 self.VREF_HIGH.write(ref_high)
188
189
190 def status(self):
191 """
192 method to query the status register on the device. Reset's the STATUS register after reading.
193 """
194 return_dict = {}
195 status_vals = ['BOOST', 'BUCK', 'BUCK_BOOST']
196 status_ovp = ['NONE', 'OVP']
197 status_ocp = ['NONE', 'OCP']
198 status_scp = ['NONE', 'SCP']
199
200 # read back the reference voltage on the device
201 read_data = self.STATUS.read()
202 status = (read_data & 0x03)
203 ovp = (read_data & 0x20) >> 5
204 ocp = (read_data & 0x40) >> 6
205 scp = (read_data & 0x80) >> 7
206
207 # format the returned data
208 return_dict['STATUS'] = status_vals[status]
209 return_dict['OVP'] = status_ovp[ovp]
210 return_dict['OCP'] = status_ocp[ocp]
211 return_dict['SCP'] = status_scp[scp]
212 return return_dict
213
214
215 def output_enable(self, value=None):
216 """
217 Query or Command the output enable status. if no value is given, return the current status.
218
219 :param value: Enable value, active high, defaults to None
220 :type value: int, optional
221 """
222
223 if value is None:
224 return self.MODE.OE.read()
225
226 check_type(value, 'output_en', int)
227 check_range(value, 'output_en', 0, 1)
228
229 self.MODE.OE.write(value)
230
231
232 def current_limit(self, current_limit=None, enable=None):
233 """
234 Query or Command the output current limit and enable status. if no value is given, return the status of the current limit system.
235
236 :param value: Enable value, active high, defaults to None
237 :type value: int, optional
238 """
239
240 if all (val is None for val in [current_limit, enable]):
241 return_dict = {}
242 register = self.IOUT_LIMIT.read()
243 return_dict['current_limit'] = (register & 0x7F) * 0.0005
244 return_dict['enable'] = (register >> 7) & 1
245 return register
246
247 check_type(current_limit, 'current_limit', (int, float))
248 check_range(current_limit, 'current_limit', 0, 0.0635)
249
250 check_type(enable, 'enable', int)
251 check_range(enable, 'enable', 0, 1)
252
253 if current_limit is not None:
254 self.IOUT_LIMIT.CURR_LIMIT.write(int(current_limit/0.0005))
255
256 if enable is not None:
257 self.IOUT_LIMIT.CURR_LIMIT_EN.write(enable)
258
259
260 def feedback_ratio(self, feedback_ratio=None):
261 """
262 Command and Query the internal feedback ratio. Changes the step size of voltage increments.
263
264 0 = 5mV, 1 = 10mV, 2 = 15mV, 3 = 20mV
265
266 :param value: _description_, defaults to None
267 :type value: _type_, optional
268 :return: Returns the feedback ratio integer
269 :rtype: int
270 """
271 if feedback_ratio is None:
272 return self.VOUT_FS.INTFB.read()
273
274 check_type(feedback_ratio, 'feedback_ratio', int)
275 check_range(feedback_ratio, 'feedback_ratio', 0, 3)
276
277 self.VOUT_FS.INTFB.write(feedback_ratio)
278
279
280 def feedback_select(self, select=None):
281 """
282 Command and Query the output feedback voltage location. Defaults to internal.
283
284 :param select: Feedback Select Either external (1) or internal (0), defaults to None
285 :type select: int, optional
286 :return: Returns the current feedback select setting.
287 :rtype: int
288 """
289
290 if select is None:
291 return self.VOUT_FS.FB.read()
292
293 check_type(select, 'feedback_ratio', int)
294 check_range(select, 'feedback_ratio', 0, 1)
295
296 self.VOUT_FS.FB.write(select)
297
298
299 def mode(self, output_en=None, freq_doubling=None, hiccup=None, discharge=None, vcc=None, i2c_addr=None, pfm=None, mode=None):
300 """
301 Command or Query the operation mode of the device.
302
303 :param output_en: Output enable 0 = off, 1 = on, defaults to None
304 :type output_en: int, optional
305 :param freq_doubling: Switching frequency doubling in buck-boost mode, 0 = off, 1 = on, defaults to None
306 :type freq_doubling: int, optional
307 :param hiccup: Hiccup mode, 0 = off, 1 = on, defaults to None
308 :type hiccup: int, optional
309 :param discharge: Output discharged through 100mA current sink when active, 0 = off, 1 = on, defaults to None
310 :type discharge: int, optional
311 :param vcc: Choose between internal LDO (0, default) or external 5V power supply (1), defaults to None
312 :type vcc: int, optional
313 :param i2c_addr: Swap between 0x74 (0, default) and 0x75 (1), defaults to None
314 :type i2c_addr: int, optional
315 :param pfm: Select operating mode at light load condition PFM (0, default) or FPWM (1), defaults to None
316 :type pfm: int, optional
317 :param mode: Mode control approach set VCC, I2CADD, and PFM either through external resistor(0) or internal register (1), defaults to None
318 :type mode: int, optional
319 """
320 inputs = [output_en, freq_doubling, hiccup, discharge, vcc, i2c_addr, pfm, mode]
321 # if all are none, read back the state of the device
322 if all (val is None for val in inputs):
323
324 return_dict = {}
325 register = self.MODE.read()
326 return_dict['output_en'] = (register >> 7) & 1
327 return_dict['freq_doubling'] = (register >> 6) & 1
328 return_dict['hiccup'] = (register >> 5) & 1
329 return_dict['discharge'] = (register >> 4) & 1
330 return_dict['vcc'] = (register >> 3) & 1
331 return_dict['i2c_addr'] = (register >> 2) & 1
332 return_dict['pfm'] = (register >> 1) & 1
333 return_dict['mode'] = (register >> 0) & 1
334 return return_dict
335
336 # check the types of the input
337 check_type(output_en, 'output_en', int)
338 check_type(freq_doubling, 'freq_doubling', int)
339 check_type(hiccup, 'hiccup', int)
340 check_type(discharge, 'discharge', int)
341 check_type(vcc, 'vcc', int)
342 check_type(i2c_addr, 'i2c_addr', int)
343 check_type(pfm, 'pfm', int)
344 check_type(mode, 'mode', int)
345
346 # check the range of the inputs
347 check_range(output_en, 'output_en', 0, 1)
348 check_range(freq_doubling, 'freq_doubling', 0, 1)
349 check_range(hiccup, 'hiccup', 0, 1)
350 check_range(discharge, 'discharge', 0, 1)
351 check_range(vcc, 'vcc', 0, 1)
352 check_range(i2c_addr, 'i2c_addr', 0, 1)
353 check_range(pfm, 'pfm', 0, 1)
354 check_range(mode, 'mode', 0, 1)
355
356 # read the current controls of the device
357 register = self.MODE.read()
358
359 # Have the inputs as binary values
360 bin_list = [0 if value is None else value for value in inputs]
361
362 # create the field mask from the changed inputs and data to be written
363 field_mask = 0x00
364 write_data = 0x00
365
366 for index in range (7, -1, -1):
367 if inputs[index] is not None:
368
369 # Place a 1 in the position desired for editing.
370 field_mask |= (1 << index)
371
372 # make the write data equivalent to the inputs given
373 # write_command = (output_en << 7) | (freq_doubling << 6) | (hiccup << 5) | (discharge << 4) | (vcc << 3) | (i2c_addr << 2) | (pfm << 1) | (mode << 0)
374 write_data |= inputs[index] << (7-index)
375
376 else:
377 write_data |= 0 << index
378
379 # clear the field bits we are writing to while leaving the others untouched
380 register &= ~field_mask
381
382 # Make changes to the register
383 register |= write_data
384
385 # write the register
386 self.MODE.write(register)
387
388 def initialize_rail(self, voltage, current_limit):
389 """
390 Startup Sequence for a TPS voltage rail.
391
392 :param voltage: Output voltage desired for the rail
393 :type voltage: int | float
394 :param current_limit: Current limit boundary for the voltage rail
395 :type current_limit: int | float
396 """
397 # set the output enable low.
398 self.output_enable(0)
399
400 # check the current status of the rail, if protection has been tripped then throw errors.
401 status_dict = self.status()
402 status_dict.pop("STATUS")
403 msg = ''
404 for key, value in status_dict.items():
405 if value != "NONE":
406 msg += f'Protection field {key} is in effect. Device status is reset and output is off. \n'
407
408 if len(msg) > 0:
409 raise ValueError(msg)
410
411
412 # Throw an error for incorrect voltage or current values
413 check_type(current_limit, 'current_limit', (int, float))
414 check_range(current_limit, 'current_limit', 0, 6.35)
415
416 # calculate the acceptable voltage drop between the ISP and ISN rails, a 10m ohm resistor on the EVM
417 v_drop = current_limit / 100
418
419 # write the voltage and current limit
420 self.voltage(voltage)
421 self.current_limit(v_drop)
422
423 # set the output enable high
424 self.output_enable(1)
425
426 # TODO Implement CDC Method for CDC controls
427
428 # TODO Implement Slew Rate method for slew controls
429
430
431if __name__ == '__main__':
432 i2c_bus = I2C(1, sda=Pin(14), scl=Pin(15), freq=100_000)
433 tps = TPS55288(name="TPS_rail", address=0x74, i2c_bus=i2c_bus)
434 devices = tps.i2c_bus.scan()
435 hex_addr = [hex(x) for x in devices]
436 print(f"Seen device addresses: {hex_addr}")
437
438# -----------------------------------------
439# END OF FILE
440# -----------------------------------------