From 3f19319c7874fa38c90715c7147e25447c80ef64 Mon Sep 17 00:00:00 2001 From: Petr Malanik Date: Mon, 16 Aug 2021 15:04:04 +0200 Subject: [PATCH] EPS: add calculations for battery charger and output protection --- modules/EPS/calc.ipynb | 346 +++++++++++++++++++++++++++++++++++------ 1 file changed, 302 insertions(+), 44 deletions(-) diff --git a/modules/EPS/calc.ipynb b/modules/EPS/calc.ipynb index fef2a50..ae54afb 100644 --- a/modules/EPS/calc.ipynb +++ b/modules/EPS/calc.ipynb @@ -26,89 +26,347 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, - "metadata": {}, + "execution_count": 27, + "source": [ + "# Current sense aplifier preliminary design \n", + "\n", + "supply_voltage = 4.2\n", + "max_current = 2.0\n", + "acceptable_power_loss = 0.1 #watts\n", + "amp_ratios = [50,100,200]\n", + "sense_voltage = 3.3\n", + "\n", + "adc_resolution = 4096\n", + "divider_R1 = 100 # kilo-ohms\n", + "\n", + "max_power = supply_voltage * max_current\n", + "\n", + "acceptable_voltage_drop = acceptable_power_loss / supply_voltage\n", + "print(\"Acceptable voltage drop: {}V\".format(round(acceptable_voltage_drop,2)))\n", + "power_loss_percentage = acceptable_power_loss/max_power*100\n", + "print(\"Power loss percentage: {}%\".format(round(power_loss_percentage,2)))\n", + "\n", + "amp_ratios.sort()\n", + "\n", + "print(\"--------------- Full - range ---------------\")\n", + "for ratio in amp_ratios:\n", + " ideal_resistor = supply_voltage / ratio / max_current\n", + " power_loss = ideal_resistor * max_current**2\n", + "\n", + " print(\"Ideal resistor: {}R\".format(ideal_resistor))\n", + " print(\"Power loss: {}W\".format(power_loss))\n", + " print(\"--------------------------------------------\")\n", + "\n", + "if sense_voltage < supply_voltage:\n", + " divider_R2 = round((sense_voltage * divider_R1) / (supply_voltage - sense_voltage),3)\n", + " print(\"Output voltage divider: R1 = {} kΩ, R2 = {} kΩ\".format(divider_R1,divider_R2))\n", + "\n", + "max_output_voltage = min(supply_voltage, supply_voltage)\n", + "resolution = round(max_current/adc_resolution*1000,2)\n", + "print(\"Resolution: {}mA\".format(resolution))\n", + "\n", + "print(\"---------------- Acceptable ----------------\")\n", + "acceptable_resistor = acceptable_voltage_drop/max_current\n", + "power_loss = acceptable_resistor * max_current**2\n", + "\n", + "ideal_ratio = None\n", + "for ratio in amp_ratios:\n", + " if acceptable_voltage_drop * ratio < supply_voltage:\n", + " ideal_ratio = ratio\n", + " else:\n", + " break\n", + "\n", + "max_output_voltage = acceptable_voltage_drop * ideal_ratio\n", + "print(\"Maximal output voltage: {}V\".format(max_output_voltage))\n", + "\n", + "if sense_voltage < max_output_voltage:\n", + " divider_R2 = round((sense_voltage * divider_R1) / (max_output_voltage - sense_voltage),3)\n", + " print(\"Output voltage divider: R1 = {} kΩ, R2 = {} kΩ\".format(divider_R1,divider_R2))\n", + "\n", + "resolution = round(max_current/adc_resolution*1000,2)\n", + "\n", + "print(\"Ideal ratio: {}\".format(ideal_ratio))\n", + "print(\"Ideal resistor: {}R\".format(acceptable_resistor))\n", + "print(\"Power loss: {}W\".format(power_loss))\n", + "print(\"Resolution: {}mA\".format(resolution))" + ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "--------------------------------------------\nIdeal resistor: 0.0132R\nPower loss: 0.33W\n" - ] - }, - { - "output_type": "error", - "ename": "IndexError", - "evalue": "Replacement index 0 out of range for positional args tuple", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/tmp/ipykernel_112179/3645271640.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Ideal resistor: {}R\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mideal_resistor\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Power loss: {}W\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpower_loss\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Resolution: {}A\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mIndexError\u001b[0m: Replacement index 0 out of range for positional args tuple" + "Acceptable voltage drop: 0.02V\n", + "Power loss percentage: 1.19%\n", + "--------------- Full - range ---------------\n", + "Ideal resistor: 0.042R\n", + "Power loss: 0.168W\n", + "--------------------------------------------\n", + "Ideal resistor: 0.021R\n", + "Power loss: 0.084W\n", + "--------------------------------------------\n", + "Ideal resistor: 0.0105R\n", + "Power loss: 0.042W\n", + "--------------------------------------------\n", + "Output voltage divider: R1 = 100 kΩ, R2 = 366.667 kΩ\n", + "Resolution: 0.49mA\n", + "---------------- Acceptable ----------------\n", + "Maximal output voltage: 2.380952380952381V\n", + "Ideal ratio: 100\n", + "Ideal resistor: 0.011904761904761904R\n", + "Power loss: 0.047619047619047616W\n", + "Resolution: 0.49mA\n" ] } ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 4, "source": [ - "max_sense_voltage = 3.3\n", - "max_output_current = 5.0\n", + "# Current sense aplifier design evaluating\n", + "\n", + "supply_voltage = 2.8\n", + "sense_voltage = 3.3\n", + "sense_resistor = 0.01\n", + "amp_ratio = 50\n", + "divider_R1 = 100 # kilo-ohms\n", + "selected_R2 = 270 # kilo-ohms\n", + "\n", "adc_resolution = 4096\n", "\n", - "max_acceptable_power_loss = 0.1\n", + "max_current = supply_voltage / sense_resistor / amp_ratio\n", + "power_disipation = sense_resistor * max_current**2\n", + "voltage_drop = sense_resistor * max_current\n", + "resolution = round(max_current/adc_resolution*1000,3)\n", "\n", - "amp_ratios = [50,100,200]\n", "\n", - "for ratio in amp_ratios:\n", + "print(\"Maximal current: {}A\".format(round(max_current,2)))\n", + "print(\"Maximal power disipation: {}W\".format(round(power_disipation,2)))\n", + "print(\"Maximal voltage drop: {}V\".format(round(voltage_drop,2)))\n", "\n", - " ideal_resistor = max_sense_voltage/ratio / max_output_current\n", - " power_loss = ideal_resistor * max_output_current**2\n", - " resolution = \n", - " print(\"--------------------------------------------\")\n", - " print(\"Ideal resistor: {}R\".format(ideal_resistor))\n", - " print(\"Power loss: {}W\".format(power_loss))\n", - " print(\"Resolution: {}A\".format())\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, + "if sense_voltage > supply_voltage:\n", + " resolution *= 1 / (supply_voltage/sense_voltage)\n", + "\n", + "print(\"Resolution: {} mA\".format(round(resolution,2)))\n", + "\n", + "if sense_voltage < supply_voltage:\n", + " divider_R2 = round((sense_voltage * divider_R1) / (supply_voltage - sense_voltage),2)\n", + " print(\"Output voltage divider: R1 = {} kΩ, R2 = {} kΩ\".format(divider_R1,divider_R2))\n", + " divider_leakage = supply_voltage / ((divider_R1 + divider_R2)*1000)\n", + " print(\"Maximum divider leakage: {} uA\".format(round(divider_leakage*1000000),2))\n", + "\n", + "print(\"---------------- Selected divider ----------------\")\n", + "maximum_divider_voltage = supply_voltage * (selected_R2 / (selected_R2 + divider_R1))\n", + "print(\"Maximum divider voltage: {} V\".format(round(maximum_divider_voltage,2)))\n", + "resolution *= 1 / (maximum_divider_voltage/sense_voltage)\n", + "print(\"Resolution: {} mA\".format(round(resolution,2)))" + ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "Length: 1.1136363636363635\nWraps: 16.880069721867685\n" + "Maximal current: 2.8A\n", + "Maximal power disipation: 0.08W\n", + "Maximal voltage drop: 0.03V\n", + "Resolution: 0.81 mA\n", + "---------------- Selected divider ----------------\n", + "Maximum divider voltage: 2.04 V\n", + "Resolution: 1.3 mA\n" ] } ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 1, "source": [ + "# Battery heating wire calculator\n", "import math\n", "\n", - "bat_diameter = 21\n", - "wrap_length = bat_diameter * math.pi / 1000\n", - "heating_power = 2\n", - "voltage = 3.5\n", + "bat_diameter = 21 # in mm\n", + "heating_power = 2 # watts\n", + "voltage = 3.7\n", "wire_resistance = 5.5 # per meter\n", "\n", + "wrap_length = bat_diameter * math.pi / 1000\n", "current = heating_power / voltage\n", "resistance = voltage / current\n", "length = resistance / wire_resistance\n", "wraps = length / wrap_length\n", "\n", "print(\"Length: {}\".format(length))\n", - "print(\"Wraps: {}\".format(wraps))\n", + "print(\"Wraps: {}\".format(wraps))" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Length: 1.2445454545454548\n", + "Wraps: 18.86433914223418\n" + ] + } + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 24, + "source": [ + "# LTC4231\n", + "\n", + "# Voltage settings\n", + "undervoltage_rising = 3\n", + "undervoltage_falling = 2.9\n", + "overvoltage_falling = 4.4\n", + "input_voltage = 3.7\n", + "\n", + "# Design parameters\n", + "resistance_total = 2000000\n", + "overcurrent_threshold = 5\n", + "soa_time = 0.0005\n", + "\n", + "# Sence voltage constants from LTC4231 datasheet\n", + "sense_voltage_slow = 0.05\n", + "sense_voltage_min = 0.065\n", + "sense_voltage_fast = 0.08\n", + "sense_voltage_max = 0.09\n", + "\n", + "R4 = (0.795/overvoltage_falling) * resistance_total\n", + "R3 = ((overvoltage_falling/undervoltage_rising) - 1) * R4\n", + "R2 = ((undervoltage_rising/undervoltage_falling) - 1) * (overvoltage_falling/undervoltage_rising) * R4\n", + "R1 = ((overvoltage_falling/0.795) - 1) * R4 - R3 - R2\n", + "\n", + "print(\"R4 value = {}k\".format(round(R4/1000)))\n", + "print(\"R3 value = {}k\".format(round(R3/1000)))\n", + "print(\"R2 value = {}k\".format(round(R2/1000)))\n", + "print(\"R1 value = {}k\".format(round(R1/1000)))\n", + "\n", + "divider_leakage = input_voltage/(R1 + R2 + R3 + R4)\n", + "sense_resistor = sense_voltage_slow/overcurrent_threshold\n", + "current_limit_fast = sense_voltage_fast / sense_resistor\n", + "sense_resistor_power = sense_resistor * current_limit_fast**2\n", + "current_limit_max = sense_voltage_max/sense_resistor\n", + "\n", + "print(\"Divider leakage = {} uA\".format(divider_leakage*1000000))\n", + "print(\"Sensing resistor = {} mR\".format(sense_resistor*1000))\n", + "print(\"Sensing resistor power dissipation = {} W\".format(sense_resistor_power))\n", + "print(\"Worst case current = {} A\".format(current_limit_max))\n", + "\n", + "load_capacitor_charge = (0.0001 * input_voltage) / (sense_voltage_min / sense_resistor)\n", + "mosfet_dissipation = input_voltage * current_limit_max\n", + "overcurrent_triptime = (soa_time - load_capacitor_charge)/4 + load_capacitor_charge\n", + "timer_capacitor = overcurrent_triptime/input_voltage\n", + "\n", + "print(\"OC timer charge = {} ms\".format(round(load_capacitor_charge*1000,2)))\n", + "print(\"MOSFET power dissipation = {} W\".format(round(mosfet_dissipation,2)))\n", + "print(\"Time to OC protection trip = {} ms\".format(round(overcurrent_triptime*1000,2)))\n", + "print(\"Timer capacitor C_T value = {} nF\".format(round(timer_capacitor*1000000,2)))" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "R4 value = 361k\n", + "R3 value = 169k\n", + "R2 value = 18k\n", + "R1 value = 1452k\n", + "Divider leakage = 1.85 uA\n", + "Sensing resistor = 10.0 mR\n", + "Sensing resistor power dissipation = 0.64 W\n", + "Worst case current = 9.0 A\n", + "OC timer charge = 0.06 ms\n", + "MOSFET power dissipation = 33.3 W\n", + "Time to OC protection trip = 0.17 ms\n", + "Timer capacitor C_T value = 45.32 nF\n" + ] + } + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 22, + "source": [ + "#BQ25798\n", + "\n", + "## Termistor settings\n", + "# JEITA profile temperature points\n", + "temp_T1 = 0 # °C\n", + "temp_T5 = 60\n", "\n", + "# NTC resistance at temperature points\n", + "termistor_at_T1 = 29250.0\n", + "termistor_at_T5 = 2900.0\n", + "\n", + "# Voltage of temperature level in fraction of REGN voltage\n", + "temp_T1_threshold = 0.733\n", + "temp_T5_threshold = 0.342\n", + "\n", + "resistor_RT2 = (termistor_at_T1*termistor_at_T5 * ((1.0/temp_T5_threshold)-(1.0/temp_T1_threshold))) / ((termistor_at_T1 * ((1.0/temp_T1_threshold) - 1))-(termistor_at_T5 * ((1.0/temp_T5_threshold) - 1)))\n", + "resistor_RT1 = ((1.0/temp_T1_threshold) - 1) / ( (1.0 / resistor_RT2) + (1.0/termistor_at_T1) )\n", + "\n", + "print(\"Battery termistor resistor network\")\n", + "print(\"RT1 = {} k\".format(round(resistor_RT1/1000,2)))\n", + "print(\"RT2 = {} k\".format(round(resistor_RT2/1000,2)))\n", + "\n", + "## Input current limitation\n", + "maximal_input_current_draw = 0.9 # in Amp\n", + "divider_R1 = 100 # in kOhms\n", + "\n", + "voltage_REGN = 4.8\n", + "\n", + "voltage_ILIM = 1 + 0.8 * maximal_input_current_draw\n", + "divider_R2 = round((voltage_ILIM * divider_R1) / (voltage_REGN - voltage_ILIM),3)\n", + "print(\"ILIM voltage divider: R1 = {} kΩ, R2 = {} kΩ\".format(divider_R1,divider_R2))\n", "\n", "\n" - ] + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Battery termistor resistor network\n", + "RT1 = 5.02 k\n", + "RT2 = 26.07 k\n", + "ILIM voltage divider: R1 = 100 kΩ, R2 = 55.844 kΩ\n" + ] + } + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] + "execution_count": 42, + "source": [ + "# TPS6300\n", + "\n", + "output_voltages = [1.8,3.3,5.0]\n", + "resistor_R2 = 200 # in kOhms, recommended 100-500 kR\n", + "\n", + "feedback_voltage = 0.5 # V\n", + "\n", + "for output_voltage in output_voltages:\n", + " resistor_R1 = resistor_R2 * (output_voltage/feedback_voltage-1)\n", + " print(\"Resistor R2 for output voltage {} V is: {} kΩ\".format(output_voltage, resistor_R1))\n" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Resistor R2 for output voltage 1.8 V is: 520.0 kΩ\n", + "Resistor R2 for output voltage 3.3 V is: 1120.0 kΩ\n", + "Resistor R2 for output voltage 5.0 V is: 1800.0 kΩ\n" + ] + } + ], + "metadata": {} } ] } \ No newline at end of file