From 914fbd9ab6a7770f8be8230e21b49ebb171b084b Mon Sep 17 00:00:00 2001 From: Barbudor Date: Tue, 19 Mar 2019 23:39:14 +0100 Subject: [PATCH] Initial commit. On-going tests on CPX/CPy4.0.0beta5. --- .gitignore | 12 + .pylintrc | 432 ++++++++++++++++++++++++++++++++ LICENSE | 21 ++ README.rst | 3 +- barbudor_ina3221.py | 264 +++++++++++++++++++ examples/ina3221_memorycheck.py | 20 ++ examples/ina3221_simpletest.py | 33 +++ 7 files changed, 783 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 LICENSE create mode 100644 barbudor_ina3221.py create mode 100644 examples/ina3221_memorycheck.py create mode 100644 examples/ina3221_simpletest.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55f127b --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.mpy +.idea +__pycache__ +_build +*.pyc +.env +build* +bundles +*.DS_Store +.eggs +dist +**/*.egg-info \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..cb8d23d --- /dev/null +++ b/.pylintrc @@ -0,0 +1,432 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. +# jobs=1 +jobs=2 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio).You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=board + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +# expected-line-ending-format= +expected-line-ending-format=LF + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[BASIC] + +# Naming hint for argument names +argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct argument names +argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for attribute names +attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct attribute names +attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class names +# class-name-hint=[A-Z_][a-zA-Z0-9]+$ +class-name-hint=[A-Z_][a-zA-Z0-9_]+$ + +# Regular expression matching correct class names +# class-rgx=[A-Z_][a-zA-Z0-9]+$ +class-rgx=[A-Z_][a-zA-Z0-9_]+$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming hint for function names +function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct function names +function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Good variable names which should always be accepted, separated by a comma +# good-names=i,j,k,ex,Run,_ +good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for method names +method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct method names +method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming hint for variable names +variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of attributes for a class (see R0902). +# max-attributes=7 +max-attributes=11 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of statements in function / method body +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fef2ce6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Dean Miller for Adafruit Industries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.rst b/README.rst index 3c5e1f3..d68acba 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Introduction ============ -CircuitPython driver for the Texast Instruments' INA3221 3 channels current sensor. +CircuitPython driver for the Texas Instruments' INA3221 3 channels current sensor. Product page : http://www.ti.com/product/INA3221 Dependencies @@ -11,7 +11,6 @@ This driver depends on: * `Adafruit CircuitPython ` * `Bus Device ` -* `Register ` Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading diff --git a/barbudor_ina3221.py b/barbudor_ina3221.py new file mode 100644 index 0000000..adbe004 --- /dev/null +++ b/barbudor_ina3221.py @@ -0,0 +1,264 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Barbudor (IRL Jean-Michel Mercier) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`barbudor_INA3221` +==================================================== + +CircuitPython driver for the Texas Instruments' INA3221 3 channels current sensor. +Product page : http://www.ti.com/product/INA3221 + +* Author : Barbudor (IRL Jean-Michel Mercier) + +Implementation Notes +-------------------- + +**Important Note** +In order to be coherent with the datasheet, the channel index in the below interface is 1-based +Value of ``channel`` parameter must be ``1``, ``2`` or ``3``. __Do_not_use_``0``__ + +Memory usage (tested with CircuitPython 4.0.0beta5 on CircuitPlayground Express): + from barbudor_ina3221 import INA3221 --> 6480 bytes + ina3221 = INA3221(i2c_bus) --> 224 bytes + +**Hardware:** + +* `INA3221 Triple, Low-/High-Side, I2C Out Current/Voltage Monitor : + `_ + +* Available breakouts : +- `SwitchDoc Labs : + `_ +- Generic chinese INA3221 breakout available on eBay or AliExpress + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware (3.1+): https://github.com/adafruit/circuitpython/releases +* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +""" + +from micropython import const +from adafruit_bus_device.i2c_device import I2CDevice + + +# pylint: disable=bad-whitespace + +_DEFAULT_ADDRESS = const(0x40) + +# +# Registers and bits definitions +# + +# Config register +REG_CONFIG = const(0x00) + +RESET = const(0x8000) +ENABLE_CH1 = const(0x4000) # default set +ENABLE_CH2 = const(0x2000) # default set +ENABLE_CH3 = const(0x1000) # default set + +AVERAGING_MASK = const(0x0E00) +AVERAGING_NONE = const(0x0000) # 1 sample, default +AVERAGING_4_SAMPLES = const(0x0200) +AVERAGING_16_SAMPLES = const(0x0400) +AVERAGING_64_SAMPLES = const(0x0600) +AVERAGING_128_SAMPLES = const(0x0800) +AVERAGING_256_SAMPLES = const(0x0A00) +AVERAGING_512_SAMPLES = const(0x0C00) +AVERAGING_1024_SAMPLES = const(0x0E00) + +VBUS_CONV_TIME_MASK = const(0x01C0) +VBUS_CONV_TIME_140US = const(0x0000) +VBUS_CONV_TIME_204US = const(0x0040) +VBUS_CONV_TIME_332US = const(0x0080) +VBUS_CONV_TIME_588US = const(0x00C0) +VBUS_CONV_TIME_1MS = const(0x0100) # 1.1ms, default +VBUS_CONV_TIME_2MS = const(0x0140) # 2.116ms +VBUS_CONV_TIME_4MS = const(0x0180) # 4.156ms +VBUS_CONV_TIME_8MS = const(0x01C0) # 8.244ms + +SHUNT_CONV_TIME_MASK = const(0x0038) +SHUNT_CONV_TIME_140US = const(0x0000) +SHUNT_CONV_TIME_204US = const(0x0008) +SHUNT_CONV_TIME_332US = const(0x0010) +SHUNT_CONV_TIME_588US = const(0x0018) +SHUNT_CONV_TIME_1MS = const(0x0020) # 1.1ms, default +SHUNT_CONV_TIME_2MS = const(0x0028) # 2.116ms +SHUNT_CONV_TIME_4MS = const(0x0030) # 4.156ms +SHUNT_CONV_TIME_8MS = const(0x0038) # 8.244ms + +MODE_MASK = const(0x0007) +MODE_POWER_DOWN = const(0x0000) # Power-down +MODE_SHUNT_VOLTAGE_TRIGGERED = const(0x0001) # Shunt voltage, single-shot (triggered) +MODE_BUS_VOLTAGE_TRIGGERED = const(0x0002) # Bus voltage, single-shot (triggered) +MODE_SHUNT_AND_BUS_TRIGGERED = const(0x0003) # Shunt and bus, single-shot (triggered) +MODE_POWER_DOWN2 = const(0x0004) # Power-down +MODE_SHUNT_VOLTAGE_CONTINUOUS = const(0x0005) # Shunt voltage, continous +MODE_BUS_VOLTAGE_CONTINUOUS = const(0x0006) # Bus voltage, continuous +MODE_SHUNT_AND_BUS_CONTINOUS = const(0x0007) # Shunt and bus, continuous (default) + +REG_SHUNT_VOLTAGE_CH1 = const(0x01) +REG_BUS_VOLTAGE_CH1 = const(0x02) +REG_SHUNT_VOLTAGE_CH2 = const(0x03) +REG_BUS_VOLTAGE_CH2 = const(0x04) +REG_SHUNT_VOLTAGE_CH3 = const(0x05) +REG_BUS_VOLTAGE_CH3 = const(0x06) +REG_CRITICAL_ALERT_LIMIT_CH1 = const(0x07) +REG_WARNING_ALERT_LIMIT_CH1 = const(0x08) +REG_CRITICAL_ALERT_LIMIT_CH2 = const(0x09) +REG_WARNING_ALERT_LIMIT_CH2 = const(0x0A) +REG_CRITICAL_ALERT_LIMIT_CH3 = const(0x0B) +REG_WARNING_ALERT_LIMIT_CH3 = const(0x0C) +REG_SHUNT_VOLTAGE_SUM = const(0x0D) +REG_SHUNT_VOLTAGE_SUM_LIMIT = const(0x0E) + +REG_MASK_ENABLE = const(0x0F) +SUM_CONTROL_CH1 = const(0x4000) # default not set +SUM_CONTROL_CH2 = const(0x2000) # default not set +SUM_CONTROL_CH3 = const(0x1000) # default not set +WARNING_LATCH_ENABLE = const(0x0800) # default not set +CRITICAL_LATCH_ENABLE = const(0x0400) # default not set +CRITICAL_FLAG_CH1 = const(0x0200) +CRITICAL_FLAG_CH2 = const(0x0100) +CRITICAL_FLAG_CH3 = const(0x0080) +SUM_ALERT_FLAG = const(0x0040) +WARNING_FLAG_CH1 = const(0x0020) +WARNING_FLAG_CH2 = const(0x0010) +WARNING_FLAG_CH3 = const(0x0008) +POWER_ALERT_FLAG = const(0x0004) +TIMING_ALERT_FLAG = const(0x0002) +CONV_READY_FLAG = const(0x0001) + +REG_POWER_VALID_UPPER_LIMIT = const(0x10) +REG_POWER_VALID_LOWER_LIMIT = const(0x11) +REG_MANUFACTURER_ID = const(0xFE) +REG_DIE_ID = const(0xFF) + +MANUFACTURER_ID = const(0x5449) # "TI" +DIE_ID = const(0x3220) + + +class INA3221: + """Driver class for Texas Instruments INA3221 3 channel current sensor device""" + + @staticmethod + def _to_signed(val): + if val > 32767: + return val - 65536 + return val + + @staticmethod + def _to_unsigned(val): + if val < 0: + return val + 65536 + return val + + def write(self, reg, value): + """Write value in device register""" + seq = bytearray([reg, (value >> 8) & 0xFF, value & 0xFF]) + with self.i2c_device as i2c: + i2c.write(seq) + + def read(self, reg): + """Return value from device register""" + buf = bytearray(3) + buf[0] = reg + with self.i2c_device as i2c: + i2c.write(buf, end=1, stop=False) + i2c.readinto(buf, start=1) + value = (buf[1] << 8) | (buf[2]) + return value + + def update(self, reg, mask, value): + """Read-modify-write value in register""" + regvalue = self.read(reg) + regvalue &= ~mask + value &= mask + self.write(reg, regvalue | value) + + + def __init__(self, i2c_bus, i2c_addr = _DEFAULT_ADDRESS, shunt_resistor = (0.1, 0.1, 0.1)): + self.i2c_device = I2CDevice(i2c_bus, i2c_addr) + self.i2c_addr = i2c_addr + self.shunt_resistor = shunt_resistor + + self.write(REG_CONFIG, AVERAGING_16_SAMPLES | \ + VBUS_CONV_TIME_1MS | \ + SHUNT_CONV_TIME_1MS | \ + MODE_SHUNT_AND_BUS_CONTINOUS ) + + def isChannelEnabled(self, channel=1): + """Returns is agiven channel is enabled or not""" + bit = ENABLE_CH1 >> (channel-1) + return self.read(REG_CONFIG) & bit != 0 + + def enable_channel(self, channel=1, enable=True): + """Enables or disable a given channel""" + bit = ENABLE_CH1 >> (channel-1) + value = 0 + if enable: + value = bit + self.update(REG_CONFIG, bit, value) + + def shunt_voltage(self, channel=1): + """Returns the channel's shunt voltage in Volts""" + assert (channel >= 1 and channel <= 3), "channel argument must be 1, 2, or 3" + value = self._to_signed(self.read(REG_SHUNT_VOLTAGE_CH1 + channel-1)) / 8.0 + # convert to volts - LSB = 40uV + return value * 0.00004 + + def current(self, channel=1): + """Return's the channel current in A""" + assert (channel >= 1 and channel <= 3), "channel argument must be 1, 2, or 3" + return self.shunt_voltage(channel) / self.shunt_resistor[channel-1] + + def bus_voltage(self, channel=1): + """Returns the channel's bus voltage in Volts""" + assert (channel >= 1 and channel <= 3), "channel argument must be 1, 2, or 3" + value = self._to_signed(self.read(REG_BUS_VOLTAGE_CH1 + channel-1)) / 8 + # convert to volts - LSB = 8mV + return value * 0.008 + + def shunt_critical_alert_limit(self, channel=1): + """Returns the channel's shunt voltage critical alert limit in Volts""" + assert (channel >= 1 and channel <= 3), "channel argument must be 1, 2, or 3" + value = self._to_signed(self.read(REG_CRITICAL_ALERT_LIMIT_CH1 + channel-1)) / 8 + # convert to volts - LSB = 40uV + return value * 0.00004 + + def set_shunt_critical_alert_limit(self, channel, voltage): + """Sets the channel's shunt voltage critical alert limit in Volts""" + assert (channel >= 1 and channel <= 3), "channel argument must be 1, 2, or 3" + value = self.to_unsigned(round(voltage * 0.00004) * 8) + self.write(REG_CRITICAL_ALERT_LIMIT_CH1 + channel-1, value) + + def shunt_warning_alert_limit(self, channel=1): + """Returns the channel's shunt voltage warning alert limit in Volts""" + assert (channel >= 1 and channel <= 3), "channel argument must be 1, 2, or 3" + value = self._to_signed(self.read(REG_WARNING_ALERT_LIMIT_CH1 + channel-1)) / 8 + # convert to volts - LSB = 40uV + return value * 0.00004 + + def set_shunt_warning_alert_limit(self, channel, voltage): + """Sets the channel's shunt voltage warning alert limit in Volts""" + assert (channel >= 1 and channel <= 3), "channel argument must be 1, 2, or 3" + value = self.to_unsigned(round(voltage * 0.00004) * 8) + self.read(REG_WARNING_ALERT_LIMIT_CH1 + channel-1, value) diff --git a/examples/ina3221_memorycheck.py b/examples/ina3221_memorycheck.py new file mode 100644 index 0000000..cd7e13b --- /dev/null +++ b/examples/ina3221_memorycheck.py @@ -0,0 +1,20 @@ +### Memory test for barbudor_ina3221 ### +# Copy below sequence into + +import board +import gc + +i2c_bus = board.I2C() + +gc.collect(); gc.collect(); gc.collect(); before = gc.mem_free(); + +from barbudor_ina3221 import INA3221 + +gc.collect(); gc.collect(); gc.collect(); after = gc.mem_free(); + +ina3221 = INA3221(i2c_bus) + +gc.collect(); gc.collect(); gc.collect(); instance = gc.mem_free(); + +print( "Import used : %d bytes" % (before - after)) +print( "Instance used : %d bytes" % (after - instance)) diff --git a/examples/ina3221_simpletest.py b/examples/ina3221_simpletest.py new file mode 100644 index 0000000..015d985 --- /dev/null +++ b/examples/ina3221_simpletest.py @@ -0,0 +1,33 @@ +"""Sample code and test for barbudor_ina3221""" + +import time +import board +import gc +from barbudor_ina3221 import INA3221 + +# To enable relay on pin A1 to switch-on the test-load +#import digitalio +#pin = digitalio.DigitalInOut(board.A1) +#pin.switch_to_output() + +i2c_bus = board.I2C() +ina3221 = INA3221(i2c_bus) + +ina3221.enable_channel(1) + +while True: + + print("------------------------------") + for chan in (1, 2, 3): + if ina3221.isChannelEnabled(chan): + print("Channel %d" % chan) + bus_voltage = ina3221.bus_voltage(chan) + shunt_voltage = ina3221.shunt_voltage(chan) + current = ina3221.current(chan) + print(" PSU Voltage: {:6.3f} V".format(bus_voltage + shunt_voltage)) + print(" Shunt Voltage: {:9.6f} V".format(shunt_voltage)) + print(" Load Voltage: {:6.3f} V".format(bus_voltage)) + print(" Current: {:9.6f} A".format(current)) + print("") + + time.sleep(2.0)