From 1b63072ad18b938c1e756fafe89bf52c21ed0d87 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 30 Sep 2018 22:46:53 -0500 Subject: [PATCH 01/19] initial I2C library --- README.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index dfd3837..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# Adafruit_CircuitPython_FRAM \ No newline at end of file From a2536df8c08660a8b5856c0e173c9724a1fe6d26 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 30 Sep 2018 23:07:01 -0500 Subject: [PATCH 02/19] initial I2C library, part duex --- .gitignore | 6 + .pylintrc | 433 ++++++++++++++++++++++++++++++++ .readthedocs.yml | 3 + .travis.yml | 34 +++ CODE_OF_CONDUCT.md | 127 ++++++++++ LICENSE | 21 ++ README.rst | 89 +++++++ adafruit_fram.py | 280 +++++++++++++++++++++ docs/_static/favicon.ico | Bin 0 -> 4414 bytes docs/api.rst | 8 + docs/conf.py | 159 ++++++++++++ docs/examples.rst | 8 + docs/index.rst | 48 ++++ examples/fram_i2c_simpletest.py | 36 +++ requirements.txt | 2 + 15 files changed, 1254 insertions(+) create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 .readthedocs.yml create mode 100644 .travis.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 README.rst create mode 100644 adafruit_fram.py create mode 100644 docs/_static/favicon.ico create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 examples/fram_i2c_simpletest.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0dd8629 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +__pycache__ +_build +*.pyc +.env +build* +bundles diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..039eaec --- /dev/null +++ b/.pylintrc @@ -0,0 +1,433 @@ +[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 +notes=FIXME,XXX + + +[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/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..f4243ad --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,3 @@ +python: + version: 3 +requirements_file: requirements.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dce0e75 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +# This is a common .travis.yml for generating library release zip files for +# CircuitPython library releases using circuitpython-build-tools. +# See https://github.com/adafruit/circuitpython-build-tools for detailed setup +# instructions. + +dist: trusty +sudo: false +language: python +python: + - "3.6" + +cache: + pip: true + +deploy: + provider: releases + api_key: $GITHUB_TOKEN + file_glob: true + file: $TRAVIS_BUILD_DIR/bundles/* + skip_cleanup: true + overwrite: true + on: + tags: true + +install: + - pip install -r requirements.txt + - pip install circuitpython-build-tools Sphinx sphinx-rtd-theme + - pip install --force-reinstall pylint==1.9.2 + +script: + - pylint adafruit_fram.py + - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-fram --library_location . + - cd docs && sphinx-build -E -W -b html . _build/html diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8ee6e44 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,127 @@ +# Adafruit Community Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and leaders pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level or type of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +We are committed to providing a friendly, safe and welcoming environment for +all. + +Examples of behavior that contributes to creating a positive environment +include: + +* Be kind and courteous to others +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Collaborating with other community members +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and sexual attention or advances +* The use of inappropriate images, including in a community member's avatar +* The use of inappropriate language, including in a community member's nickname +* Any spamming, flaming, baiting or other attention-stealing behavior +* Excessive or unwelcome helping; answering outside the scope of the question + asked +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate + +The goal of the standards and moderation guidelines outlined here is to build +and maintain a respectful community. We ask that you don’t just aim to be +"technically unimpeachable", but rather try to be your best self. + +We value many things beyond technical expertise, including collaboration and +supporting others within our community. Providing a positive experience for +other community members can have a much more significant impact than simply +providing the correct answer. + +## Our Responsibilities + +Project leaders are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project leaders have the right and responsibility to remove, edit, or +reject messages, comments, commits, code, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. + +## Moderation + +Instances of behaviors that violate the Adafruit Community Code of Conduct +may be reported by any member of the community. Community members are +encouraged to report these situations, including situations they witness +involving other community members. + +You may report in the following ways: + +In any situation, you may send an email to . + +On the Adafruit Discord, you may send an open message from any channel +to all Community Helpers by tagging @community helpers. You may also send an +open message from any channel, or a direct message to @kattni#1507, +@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or +@Andon#8175. + +Email and direct message reports will be kept confidential. + +In situations on Discord where the issue is particularly egregious, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to Discord. + +These are the steps for upholding our community’s standards of conduct. + +1. Any member of the community may report any situation that violates the +Adafruit Community Code of Conduct. All reports will be reviewed and +investigated. +2. If the behavior is an egregious violation, the community member who +committed the violation may be banned immediately, without warning. +3. Otherwise, moderators will first respond to such behavior with a warning. +4. Moderators follow a soft "three strikes" policy - the community member may +be given another chance, if they are receptive to the warning and change their +behavior. +5. If the community member is unreceptive or unreasonable when warned by a +moderator, or the warning goes unheeded, they may be banned for a first or +second offense. Repeated offenses will result in the community member being +banned. + +## Scope + +This Code of Conduct and the enforcement policies listed above apply to all +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. As a community +member, you are representing our community, and are expected to behave +accordingly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +, +and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). + +For other projects adopting the Adafruit Community Code of +Conduct, please contact the maintainers of those projects for enforcement. +If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your +own moderation policy so as to avoid confusion. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a7ee128 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Michael Schroeder + +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 new file mode 100644 index 0000000..0ef5787 --- /dev/null +++ b/README.rst @@ -0,0 +1,89 @@ +Introduction +============ + +.. image:: https://readthedocs.org/projects/adafruit-circuitpython-fram/badge/?version=latest + :target: https://circuitpython.readthedocs.io/projects/fram/en/latest/ + :alt: Documentation Status + +.. image:: https://img.shields.io/discord/327254708534116352.svg + :target: https://discord.gg/nBQh6qu + :alt: Discord + +.. image:: https://travis-ci.org/adafruit/Adafruit_CircuitPython_FRAM.svg?branch=master + :target: https://travis-ci.org/adafruit/Adafruit_CircuitPython_FRAM + :alt: Build Status + +CircuitPython/Python library to support the I2C and SPI FRAM Breakouts. + +Dependencies +============= +This driver depends on: + +* `Adafruit CircuitPython `_ +* `Bus Device `_ + +Please ensure all dependencies are available on the CircuitPython filesystem. +This is easily achieved by downloading +`the Adafruit library and driver bundle `_. + +Usage Example +============= + +See simpletest examples in the ``/examples/`` directory. + +Contributing +============ + +Contributions are welcome! Please read our `Code of Conduct +`_ +before contributing to help this project stay welcoming. + +Building locally +================ + +Zip release files +----------------- + +To build this library locally you'll need to install the +`circuitpython-build-tools `_ package. + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install circuitpython-build-tools + +Once installed, make sure you are in the virtual environment: + +.. code-block:: shell + + source .env/bin/activate + +Then run the build: + +.. code-block:: shell + + circuitpython-build-bundles --filename_prefix adafruit-circuitpython-fram --library_location . + +Sphinx documentation +----------------------- + +Sphinx is used to build the documentation based on rST files and comments in the code. First, +install dependencies (feel free to reuse the virtual environment from above): + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install Sphinx sphinx-rtd-theme + +Now, once you have the virtual environment activated: + +.. code-block:: shell + + cd docs + sphinx-build -E -W -b html . _build/html + +This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to +view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to +locally verify it will pass. diff --git a/adafruit_fram.py b/adafruit_fram.py new file mode 100644 index 0000000..538a045 --- /dev/null +++ b/adafruit_fram.py @@ -0,0 +1,280 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Michael Schroeder +# +# 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. +""" +`adafruit_fram` +==================================================== + +CircuitPython/Python library to support the I2C and SPI FRAM Breakouts. + +* Author(s): Michael Schroeder + +Implementation Notes +-------------------- + +**Hardware:** + + * `Adafruit I2C Non-Volatile FRAM Breakout ` + * `Adafruit SPI Non-Volatile FRAM Breakout ` + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + + * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +""" + +# imports +from micropython import const + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FRAM.git" + +_MAX_SIZE_I2C = const(262143) # 32768 words x 8 bits +_MAX_SIZE_SPI = const(512000) + +class FRAM: + """ + Driver base for the FRAM Breakout. + """ + + def __init__(self, max_size, write_protect=False, wp_pin=None): + self._max_size = max_size + self._wp = write_protect + if not wp_pin == None: + import digitalio + self._wp_pin = digitalio.DigitalInOut(wp_pin) + # Make sure write_prot is set to output + self._wp_pin.switch_to_output() + self._wp_pin.value = self._wp + else: + self._wp_pin = wp_pin + + @property + def max_size(self): + """ The maximum size of the current FRAM chip. This is the highest + register location that can be read or written to. + """ + return self._max_size + + @property + def write_protected(self): + """ The status of write protection. Default value on initialization is + ``False``. + + When a ``WP`` pin is supplied during initialization, or using + ``write_protect_pin``, the status is tied to that pin and enables + hardware-level protection. + + When no ``WP`` pin is supplied, protection is only at the software + level in this library. + """ + if not self._wp_pin == None: + return self._wp_pin.value + else: + return self._wp + + @write_protected.setter + def write_protected(self, value): + self._wp = value + if not self._wp_pin == None: + self._wp_pin.value = value + + def write_protect_pin(self, wp_pin, write_protect=False): + """ Assigns the write protection (``WP``) pin. + + .. param: wp_pin: The ``board.PIN`` object connected to the ``WP`` pin + on the breakout board/chip. To remove a previously + set ``WP`` pin, set this value to ``None``. + .. param: bool write_protect: Turn on/off write protection immediately + when setting the pin. Default is ``False`` + + """ + if not wp_pin == None: + self._wp_pin = digitalio.DigitalInOut(wp_pin) + # Make sure wp_pin is set to switch_to_output + self._wp_pin.switch_to_output() + self._wp_pin.value = write_protect + else: + if not self._wp_pin == None: + # Deinit the pin to release it + self._wp_pin.deinit() + self._wp_pin = None + + def read(self, register, length=1): + """ Reads the data stored on the FRAM. + + .. param: int register: Register location to start reading. Range is: + ``1`` to ``max_size``. + .. param: int length: Length of registers to read from starting register. + This function will create a buffer the size of + ``length``; larger buffers can cause memory + allocation problems on some platforms. + Range is ``1`` (default) to ``max_size``. + However, ``register`` + ``length`` cannot be + greater than ``max_size``. + """ + if length < 1: + raise ValueError("Length must be '1' or greater.") + if length > self._max_size: + raise ValueError("Length '{0}' greater than maximum FRAM size." + " ({1})".format(length, self._max_size)) + if (register + length) > self._max_size: + raise ValueError("Register + Length greater than maximum FRAM size." + " ({0})".format(self._max_size)) + read_buffer = bytearray(length) + for i in range(length): + read_buffer[i] = self._read_byte(register + i)[0] + return read_buffer + + def write_single(self, register, data): + """ Writes a single byte to the FRAM. + + .. param: int register: Register location to write the byte data. + .. param: int data: The data to write. + """ + if not isinstance(data, int): + raise ValueError("Data must be an integer.") + if self.write_protected: + raise RuntimeError("FRAM currently write protected.") + if register > self._max_size: + raise ValueError("Requested register '{0}' greater than maximum" + " FRAM size. ({1})".format(register, + self._max_size)) + self._write_register(register, data) + + def write_sequence(self, start_register, data, wraparound=False): + """ Writes sequential data to the FRAM. + + .. param: int start_register: Register location to start writing the + data. + .. param: data: The data to write. Must be an iterable type of either + ``bytearray``, ``list``, or ``tuple``. + .. param: bool wraparound: Controls if sequential writes can wraparound + beyond the ``max_size`` to zero, when + ``start_register`` + ``data length`` is + greater than ``max_size``. + """ + if not isinstance(data, (bytearray, list, tuple)): + raise ValueError("Data must be either a bytearray, list, or tuple.") + if self.write_protected: + raise RuntimeError("FRAM currently write protected.") + if start_register > self._max_size: + raise ValueError("Requested register '{0}' greater than maximum" + " FRAM size. ({1})".format(start_register, + self._max_size)) + self._write_page(start_register, data, wraparound) + + def _read_byte(self, register): + return self._read_register(register) + + def _read_register(self, register): + # Implemented by subclass + raise NotImplementedError + + def _write_register(self, register, data): + # Implemented by subclass + raise NotImplementedError + + def _write_page(self, start_register, data, wraparound): + # Implemened by subclass + raise NotImplementedError + +class FRAM_I2C(FRAM): + """ + I2C class for FRAM_I2C. + + ..param: i2c_SCL: The I2C SCL pin. Must be a ``board.PIN`` object. + ..param: i2c_SDA: The I2C SDA print. Must be a ``board.PIN`` object. + ..param: int address: I2C address of FRAM. Default address is ``0x50``. + ..param: bool write_protect: Turns on/off initial write protection. + Default is `False`. + ..param: wp_pin: Physical ``WP`` breakout pin. Must be a ``board.PIN`` + object. + """ + _DEV_TYPE_I2C = const(0x0A) + _DEV_PROD_ID = const(0x510) + + def __init__(self, i2c_SCL, i2c_SDA, address=0x50, write_protect=False, + wp_pin=None): + from busio import I2C as i2c + i2c_bus = i2c(i2c_SCL, i2c_SDA) + i2c_bus.try_lock() + i2c_bus.writeto((0xF8 >> 1), bytearray([(address << 1)]), stop=False) + read_buf = bytearray(3) + i2c_bus.readfrom_into((0xF9 >> 1), read_buf) + manf_id = (((read_buf[0] << 4) +(read_buf[1] >> 4))) + prod_id = (((read_buf[1] & 0x0F) << 8) + read_buf[2]) + if (manf_id != _DEV_TYPE_I2C) and (prod_id != _DEV_PROD_ID): + raise OSError("FRAM I2C device not found.") + i2c_bus.unlock() + + from adafruit_bus_device.i2c_device import I2CDevice as i2cdev + self._i2c = i2cdev(i2c_bus, address) + super().__init__(_MAX_SIZE_I2C, write_protect, wp_pin) + + def _read_register(self, register): + write_buffer = bytearray(2) + write_buffer[0] = register >> 8 + write_buffer[1] = register & 0xFF + read_buffer = bytearray(1) + with self._i2c as i2c: + i2c.write_then_readinto(write_buffer, read_buffer) + return read_buffer + + def _write_register(self, register, data): + buffer = bytearray(3) + buffer[0] = register >> 8 + buffer[1] = register & 0xFF + buffer[2] = data + with self._i2c as i2c: + i2c.write(buffer) + + def _write_page(self, start_register, data, wraparound=False): + # Decided against using the chip's "Page Write", since that would require + # doubling the memory usage by creating a buffer that includes the passed + # in data so that it can be sent all in one `i2c.write`. The single-write + # method is slower, and forces us to handle wraparound, but I feel this + # is a better tradeoff for limiting the memory required for large writes. + buffer = bytearray(3) + data_length = len(data) + if (start_register + data_length) > self._max_size: + if wraparound: + pass + else: + raise ValueError("Starting register + data length extends beyond" + " FRAM maximum size. Use 'wraparound=True' to" + " override this warning.") + with self._i2c as i2c: + for i in range(0, data_length): + if not (start_register + i) > self._max_size: + buffer[0] = (start_register + i) >> 8 + buffer[1] = (start_register + i) & 0xFF + else: + buffer[0] = ((start_register + i) - self._max_size) >> 8 + buffer[1] = ((start_register + i) - self._max_size) & 0xFF + buffer[2] = data[i] + i2c.write(buffer) + +class FRAM_SPI(FRAM): + def __init__(self): + raise NotImplementedError diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5aca98376a1f7e593ebd9cf41a808512c2135635 GIT binary patch literal 4414 zcmd^BX;4#F6n=SG-XmlONeGrD5E6J{RVh+e928U#MG!$jWvO+UsvWh`x&VqGNx*en zx=qox7Dqv{kPwo%fZC$dDwVpRtz{HzTkSs8QhG0)%Y=-3@Kt!4ag|JcIo?$-F|?bXVS9UDUyev>MVZQ(H8K4#;BQW-t2CPorj8^KJrMX}QK zp+e<;4ldpXz~=)2GxNy811&)gt-}Q*yVQpsxr@VMoA##{)$1~=bZ1MmjeFw?uT(`8 z^g=09<=zW%r%buwN%iHtuKSg|+r7HkT0PYN*_u9k1;^Ss-Z!RBfJ?Un4w(awqp2b3 z%+myoFis_lTlCrGx2z$0BQdh+7?!JK#9K9@Z!VrG zNj6gK5r(b4?YDOLw|DPRoN7bdP{(>GEG41YcN~4r_SUHU2hgVtUwZG@s%edC;k7Sn zC)RvEnlq~raE2mY2ko64^m1KQL}3riixh?#J{o)IT+K-RdHae2eRX91-+g!y`8^># z-zI0ir>P%Xon)!@xp-BK2bDYUB9k613NRrY6%lVjbFcQc*pRqiK~8xtkNPLxt}e?&QsTB}^!39t_%Qb)~Ukn0O%iC;zt z<&A-y;3h++)>c1br`5VFM~5(83!HKx$L+my8sW_c#@x*|*vB1yU)_dt3vH;2hqPWx zAl^6@?ipx&U7pf`a*>Yq6C85nb+B=Fnn+(id$W#WB^uHAcZVG`qg;rWB}ubvi(Y>D z$ei>REw$#xp0SHAd^|1hq&9HJ=jKK8^zTH~nk)G?yUcmTh9vUM6Y0LMw4(gYVY$D$ zGl&WY&H<)BbJ&3sYbKjx1j^=3-0Q#f^}(aP1?8^`&FUWMp|rmtpK)bLQ1Zo?^s4jqK=Lfg*9&geMGVQ z#^-*!V`fG@;H&{M9S8%+;|h&Qrxym0Ar>WT4BCVLR8cGXF=JmEYN(sNT(9vl+S|%g z8r7nXQ(95i^`=+XHo|){$vf2$?=`F$^&wFlYXyXg$B{a>$-Fp+V}+D;9k=~Xl~?C4 zAB-;RKXdUzBJE{V&d&%R>aEfFe;vxqI$0@hwVM}gFeQR@j}a>DDxR+n+-*6|_)k%% z*mSpDV|=5I9!&VC&9tD%fcVygWZV!iIo2qFtm#!*(s|@ZT33*Ad;+<|3^+yrp*;oH zBSYLV(H1zTU?2WjrCQoQW)Z>J2a=dTriuvezBmu16`tM2fm7Q@d4^iqII-xFpwHGI zn9CL}QE*1vdj2PX{PIuqOe5dracsciH6OlAZATvE8rj6ykqdIjal2 z0S0S~PwHb-5?OQ-tU-^KTG@XNrEVSvo|HIP?H;7ZhYeZkhSqh-{reE!5di;1zk$#Y zCe7rOnlzFYJ6Z#Hm$GoidKB=2HBCwm`BbZVeZY4ukmG%1uz7p2URs6c9j-Gjj^oQV zsdDb3@k2e`C$1I5ML5U0Qs0C1GAp^?!*`=|Nm(vWz3j*j*8ucum2;r0^-6Aca=Gv) zc%}&;!+_*S2tlnnJnz0EKeRmw-Y!@9ob!XQBwiv}^u9MkaXHvM=!<3YX;+2#5Cj5pp?FEK750S3BgeSDtaE^ zXUM@xoV6yBFKfzvY20V&Lr0yC + Adafruit SPI Non-Volatile FRAM Breakout + +.. toctree:: + :caption: Other Links + + Download + CircuitPython Reference Documentation + CircuitPython Support Forum + Discord Chat + Adafruit Learning System + Adafruit Blog + Adafruit Store + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/examples/fram_i2c_simpletest.py b/examples/fram_i2c_simpletest.py new file mode 100644 index 0000000..714df5b --- /dev/null +++ b/examples/fram_i2c_simpletest.py @@ -0,0 +1,36 @@ +## Simple Example For CircuitPython/Python I2C FRAM Library + +import adafruit_fram +import board + +## Create a FRAM object (default address used). + +fram = adafruit_fram.FRAM_I2C(board.SCL, board.SDA) + +## Optional FRAM object with a different I2C address, as well +## as a pin to control the hardware write protection ('WP' +## pin on breakout). 'write_protected()' can be used +## independent of the hardware pin. + +#fram = adafruit_fram.FRAM_I2C(board.SCL, +# board.SDA, +# address=0x53, +# wp_pin=board.D4) + +## Write a single-byte value to register address '0' + +fram.write_single(0, 1) + +## Read that byte to ensure a proper write. +## Note: 'read()' returns a bytearray + +print(fram.read(0)[1]) + +## Or write a sequential value, then read the values back. +## Note: 'read()' returns a bytearray. It also allocates +## a buffer the size of 'length', which may cause +## problems on memory-constrained platforms. + +#values = list(range(100)) # or bytearray or tuple +#fram.write_sequence(0, values) +#fram.read(0, length=100) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3031961 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Adafruit-Blinka +adafruit-circuitpython-busdevice From d9673bd82b0c33906b3aae4d903f330c15c0fe6b Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 30 Sep 2018 23:39:16 -0500 Subject: [PATCH 03/19] update MAX SPI size...to trigger Travis build. --- adafruit_fram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 538a045..934fc97 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -50,7 +50,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FRAM.git" _MAX_SIZE_I2C = const(262143) # 32768 words x 8 bits -_MAX_SIZE_SPI = const(512000) +_MAX_SIZE_SPI = const(65535) # 8192 words x 8 bits class FRAM: """ From c80ec10437cf4767df37ae0c9f024f2ba1f1ecac Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 1 Oct 2018 00:21:32 -0500 Subject: [PATCH 04/19] pylint fixes --- adafruit_fram.py | 35 ++++++++++++++++----------------- examples/fram_i2c_simpletest.py | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 934fc97..008f488 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -52,6 +52,9 @@ _MAX_SIZE_I2C = const(262143) # 32768 words x 8 bits _MAX_SIZE_SPI = const(65535) # 8192 words x 8 bits +_I2C_MANF_ID = const(0x0A) +_I2C_PROD_ID = const(0x510) + class FRAM: """ Driver base for the FRAM Breakout. @@ -60,7 +63,7 @@ class FRAM: def __init__(self, max_size, write_protect=False, wp_pin=None): self._max_size = max_size self._wp = write_protect - if not wp_pin == None: + if not wp_pin is None: import digitalio self._wp_pin = digitalio.DigitalInOut(wp_pin) # Make sure write_prot is set to output @@ -88,15 +91,16 @@ def write_protected(self): When no ``WP`` pin is supplied, protection is only at the software level in this library. """ - if not self._wp_pin == None: - return self._wp_pin.value + if not self._wp_pin is None: + status = self._wp_pin.value else: - return self._wp + status = self._wp + return status @write_protected.setter def write_protected(self, value): self._wp = value - if not self._wp_pin == None: + if not self._wp_pin is None: self._wp_pin.value = value def write_protect_pin(self, wp_pin, write_protect=False): @@ -109,13 +113,14 @@ def write_protect_pin(self, wp_pin, write_protect=False): when setting the pin. Default is ``False`` """ - if not wp_pin == None: + if not wp_pin is None: + import digitalio self._wp_pin = digitalio.DigitalInOut(wp_pin) # Make sure wp_pin is set to switch_to_output self._wp_pin.switch_to_output() self._wp_pin.value = write_protect else: - if not self._wp_pin == None: + if not self._wp_pin is None: # Deinit the pin to release it self._wp_pin.deinit() self._wp_pin = None @@ -211,10 +216,8 @@ class FRAM_I2C(FRAM): ..param: wp_pin: Physical ``WP`` breakout pin. Must be a ``board.PIN`` object. """ - _DEV_TYPE_I2C = const(0x0A) - _DEV_PROD_ID = const(0x510) - - def __init__(self, i2c_SCL, i2c_SDA, address=0x50, write_protect=False, + #pylint: disable=too-many-arguments + def __init__(self, i2c_SCL, i2c_SDA, address=0x50, write_protect=False, wp_pin=None): from busio import I2C as i2c i2c_bus = i2c(i2c_SCL, i2c_SDA) @@ -224,7 +227,7 @@ def __init__(self, i2c_SCL, i2c_SDA, address=0x50, write_protect=False, i2c_bus.readfrom_into((0xF9 >> 1), read_buf) manf_id = (((read_buf[0] << 4) +(read_buf[1] >> 4))) prod_id = (((read_buf[1] & 0x0F) << 8) + read_buf[2]) - if (manf_id != _DEV_TYPE_I2C) and (prod_id != _DEV_PROD_ID): + if (manf_id != _I2C_MANF_ID) and (prod_id != _I2C_PROD_ID): raise OSError("FRAM I2C device not found.") i2c_bus.unlock() @@ -262,8 +265,8 @@ def _write_page(self, start_register, data, wraparound=False): pass else: raise ValueError("Starting register + data length extends beyond" - " FRAM maximum size. Use 'wraparound=True' to" - " override this warning.") + " FRAM maximum size. Use 'wraparound=True' to" + " override this warning.") with self._i2c as i2c: for i in range(0, data_length): if not (start_register + i) > self._max_size: @@ -274,7 +277,3 @@ def _write_page(self, start_register, data, wraparound=False): buffer[1] = ((start_register + i) - self._max_size) & 0xFF buffer[2] = data[i] i2c.write(buffer) - -class FRAM_SPI(FRAM): - def __init__(self): - raise NotImplementedError diff --git a/examples/fram_i2c_simpletest.py b/examples/fram_i2c_simpletest.py index 714df5b..4ead358 100644 --- a/examples/fram_i2c_simpletest.py +++ b/examples/fram_i2c_simpletest.py @@ -1,7 +1,7 @@ ## Simple Example For CircuitPython/Python I2C FRAM Library -import adafruit_fram import board +import adafruit_fram ## Create a FRAM object (default address used). From b97d35937427d0dc9e259bafe51dbc47f9ee1198 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 1 Oct 2018 17:09:41 -0500 Subject: [PATCH 05/19] Sphinx fix; attempt #1 --- adafruit_fram.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 008f488..135d8f6 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -205,14 +205,13 @@ def _write_page(self, start_register, data, wraparound): raise NotImplementedError class FRAM_I2C(FRAM): - """ - I2C class for FRAM_I2C. + """ I2C class for FRAM. ..param: i2c_SCL: The I2C SCL pin. Must be a ``board.PIN`` object. ..param: i2c_SDA: The I2C SDA print. Must be a ``board.PIN`` object. ..param: int address: I2C address of FRAM. Default address is ``0x50``. ..param: bool write_protect: Turns on/off initial write protection. - Default is `False`. + Default is ``False``. ..param: wp_pin: Physical ``WP`` breakout pin. Must be a ``board.PIN`` object. """ From 6e12b3bebf86e8744857bec688516dda9d8cfa4c Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 1 Oct 2018 17:24:57 -0500 Subject: [PATCH 06/19] start `param` with two dots...in the proper vertical orientation. :facepalm: --- adafruit_fram.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 135d8f6..7ab4010 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -106,10 +106,10 @@ def write_protected(self, value): def write_protect_pin(self, wp_pin, write_protect=False): """ Assigns the write protection (``WP``) pin. - .. param: wp_pin: The ``board.PIN`` object connected to the ``WP`` pin + :param: wp_pin: The ``board.PIN`` object connected to the ``WP`` pin on the breakout board/chip. To remove a previously set ``WP`` pin, set this value to ``None``. - .. param: bool write_protect: Turn on/off write protection immediately + :param: bool write_protect: Turn on/off write protection immediately when setting the pin. Default is ``False`` """ @@ -128,9 +128,9 @@ def write_protect_pin(self, wp_pin, write_protect=False): def read(self, register, length=1): """ Reads the data stored on the FRAM. - .. param: int register: Register location to start reading. Range is: - ``1`` to ``max_size``. - .. param: int length: Length of registers to read from starting register. + :param: int register: Register location to start reading. Range is: + ``0`` to ``max_size``. + :param: int length: Length of registers to read from starting register. This function will create a buffer the size of ``length``; larger buffers can cause memory allocation problems on some platforms. @@ -154,8 +154,8 @@ def read(self, register, length=1): def write_single(self, register, data): """ Writes a single byte to the FRAM. - .. param: int register: Register location to write the byte data. - .. param: int data: The data to write. + :param: int register: Register location to write the byte data. + :param: int data: The data to write. """ if not isinstance(data, int): raise ValueError("Data must be an integer.") @@ -170,14 +170,14 @@ def write_single(self, register, data): def write_sequence(self, start_register, data, wraparound=False): """ Writes sequential data to the FRAM. - .. param: int start_register: Register location to start writing the + :param: int start_register: Register location to start writing the data. - .. param: data: The data to write. Must be an iterable type of either - ``bytearray``, ``list``, or ``tuple``. - .. param: bool wraparound: Controls if sequential writes can wraparound - beyond the ``max_size`` to zero, when - ``start_register`` + ``data length`` is - greater than ``max_size``. + :param: data: The data to write. Must be an iterable type of either + ``bytearray``, ``list``, or ``tuple``. + :param: bool wraparound: Controls if sequential writes can wraparound + beyond the ``max_size`` to zero, when + ``start_register`` + ``data length`` is + greater than ``max_size``. """ if not isinstance(data, (bytearray, list, tuple)): raise ValueError("Data must be either a bytearray, list, or tuple.") @@ -207,13 +207,13 @@ def _write_page(self, start_register, data, wraparound): class FRAM_I2C(FRAM): """ I2C class for FRAM. - ..param: i2c_SCL: The I2C SCL pin. Must be a ``board.PIN`` object. - ..param: i2c_SDA: The I2C SDA print. Must be a ``board.PIN`` object. - ..param: int address: I2C address of FRAM. Default address is ``0x50``. - ..param: bool write_protect: Turns on/off initial write protection. + :param: i2c_SCL: The I2C SCL pin. Must be a ``board.PIN`` object. + :param: i2c_SDA: The I2C SDA print. Must be a ``board.PIN`` object. + :param: int address: I2C address of FRAM. Default address is ``0x50``. + :param: bool write_protect: Turns on/off initial write protection. Default is ``False``. - ..param: wp_pin: Physical ``WP`` breakout pin. Must be a ``board.PIN`` - object. + :param: wp_pin: Physical ``WP`` breakout pin. Must be a ``board.PIN`` + object. """ #pylint: disable=too-many-arguments def __init__(self, i2c_SCL, i2c_SDA, address=0x50, write_protect=False, From 1251ebba6912bb35cc708dd4102214808e1c591a Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 1 Oct 2018 17:34:55 -0500 Subject: [PATCH 07/19] Sphinx fix: product links --- adafruit_fram.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 7ab4010..ac8ded1 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -32,8 +32,10 @@ **Hardware:** - * `Adafruit I2C Non-Volatile FRAM Breakout ` - * `Adafruit SPI Non-Volatile FRAM Breakout ` + * Adafruit `I2C Non-Volatile FRAM Breakout + `_ (Product ID: 1895) + * Adafruit `SPI Non-Volatile FRAM Breakout + `_ (Product ID: 1897) **Software and Dependencies:** From 554660803a86f8a38be42d5b18e83ff97be15669 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Tue, 2 Oct 2018 20:08:48 -0500 Subject: [PATCH 08/19] Docstring formatting...to kick Travis. --- adafruit_fram.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index ac8ded1..f9ad0c9 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -109,10 +109,10 @@ def write_protect_pin(self, wp_pin, write_protect=False): """ Assigns the write protection (``WP``) pin. :param: wp_pin: The ``board.PIN`` object connected to the ``WP`` pin - on the breakout board/chip. To remove a previously - set ``WP`` pin, set this value to ``None``. + on the breakout board/chip. To remove a previously + set ``WP`` pin, set this value to ``None``. :param: bool write_protect: Turn on/off write protection immediately - when setting the pin. Default is ``False`` + when setting the pin. Default is ``False`` """ if not wp_pin is None: @@ -133,12 +133,12 @@ def read(self, register, length=1): :param: int register: Register location to start reading. Range is: ``0`` to ``max_size``. :param: int length: Length of registers to read from starting register. - This function will create a buffer the size of - ``length``; larger buffers can cause memory - allocation problems on some platforms. - Range is ``1`` (default) to ``max_size``. - However, ``register`` + ``length`` cannot be - greater than ``max_size``. + This function will create a buffer the size of + ``length``; larger buffers can cause memory + allocation problems on some platforms. + Range is ``1`` (default) to ``max_size``. + However, ``register`` + ``length`` cannot be + greater than ``max_size``. """ if length < 1: raise ValueError("Length must be '1' or greater.") @@ -173,7 +173,7 @@ def write_sequence(self, start_register, data, wraparound=False): """ Writes sequential data to the FRAM. :param: int start_register: Register location to start writing the - data. + data. :param: data: The data to write. Must be an iterable type of either ``bytearray``, ``list``, or ``tuple``. :param: bool wraparound: Controls if sequential writes can wraparound From 3deda9fe8f714162cd977d052f9921ee336ad7a2 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Wed, 3 Oct 2018 23:01:41 -0500 Subject: [PATCH 09/19] corrected max values --- adafruit_fram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index f9ad0c9..2dee811 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -51,8 +51,8 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FRAM.git" -_MAX_SIZE_I2C = const(262143) # 32768 words x 8 bits -_MAX_SIZE_SPI = const(65535) # 8192 words x 8 bits +_MAX_SIZE_I2C = const(32767) +_MAX_SIZE_SPI = const(8191) _I2C_MANF_ID = const(0x0A) _I2C_PROD_ID = const(0x510) From 0ebf851e1a631a5a037e12479580f9ae39fe7196 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 14 Oct 2018 23:48:16 -0500 Subject: [PATCH 10/19] change request fixes; getter/setter, external objects. --- adafruit_fram.py | 159 +++++++++++++------------------- examples/fram_i2c_simpletest.py | 26 +++--- 2 files changed, 77 insertions(+), 108 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 2dee811..a62ce0d 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -65,9 +65,10 @@ class FRAM: def __init__(self, max_size, write_protect=False, wp_pin=None): self._max_size = max_size self._wp = write_protect + self._wraparound = False if not wp_pin is None: - import digitalio - self._wp_pin = digitalio.DigitalInOut(wp_pin) + #import digitalio + self._wp_pin = wp_pin # Make sure write_prot is set to output self._wp_pin.switch_to_output() self._wp_pin.value = self._wp @@ -81,6 +82,20 @@ def max_size(self): """ return self._max_size + @property + def write_wraparound(self): + """ Determines if sequential writes will wrapaound the ``FRAM.max_size`` + address. If ``False``, and a requested write will extend beyond the + maximum size, an exception is raised. + """ + return self._wraparound + + @write_wraparound.setter + def write_wraparound(self, value): + if not value in (True, False): + raise ValueError("Write wraparound must be 'True' or 'False'.") + self._wraparound = value + @property def write_protected(self): """ The status of write protection. Default value on initialization is @@ -93,11 +108,7 @@ def write_protected(self): When no ``WP`` pin is supplied, protection is only at the software level in this library. """ - if not self._wp_pin is None: - status = self._wp_pin.value - else: - status = self._wp - return status + return self._wp if self._wp_pin is None else self._wp_pin.value @write_protected.setter def write_protected(self, value): @@ -105,91 +116,50 @@ def write_protected(self, value): if not self._wp_pin is None: self._wp_pin.value = value - def write_protect_pin(self, wp_pin, write_protect=False): - """ Assigns the write protection (``WP``) pin. + def __getitem__(self, key): + if isinstance(key, int): + if key > self._max_size: + raise ValueError("Register '{0}' greater than maximum FRAM size." + " ({1})".format(key, self._max_size)) + return self._read_byte(key)[0] + elif isinstance(key, slice): + registers = list(range(key.start if not key.start is None else 0, + key.stop if not key.stop is None else None, + key.step if not key.step is None else None)) + if (registers[0] + len(registers)) > self._max_size: + raise ValueError("Register + Length greater than maximum FRAM size." + " ({0})".format(self._max_size)) + + read_buffer = bytearray(len(registers)) + for i, register in enumerate(registers): + read_buffer[i] = self._read_byte(register)[0] + return read_buffer + + def __setitem__(self, key, value): + if self.write_protected: + raise RuntimeError("FRAM currently write protected.") - :param: wp_pin: The ``board.PIN`` object connected to the ``WP`` pin - on the breakout board/chip. To remove a previously - set ``WP`` pin, set this value to ``None``. - :param: bool write_protect: Turn on/off write protection immediately - when setting the pin. Default is ``False`` + if isinstance(key, int): + if not isinstance(value, int): + raise ValueError("Data must be an integer.") + if key.start > self._max_size: + raise ValueError("Requested register '{0}' greater than maximum" + " FRAM size. ({1})".format(key.start, + self._max_size)) - """ - if not wp_pin is None: - import digitalio - self._wp_pin = digitalio.DigitalInOut(wp_pin) - # Make sure wp_pin is set to switch_to_output - self._wp_pin.switch_to_output() - self._wp_pin.value = write_protect - else: - if not self._wp_pin is None: - # Deinit the pin to release it - self._wp_pin.deinit() - self._wp_pin = None - - def read(self, register, length=1): - """ Reads the data stored on the FRAM. - - :param: int register: Register location to start reading. Range is: - ``0`` to ``max_size``. - :param: int length: Length of registers to read from starting register. - This function will create a buffer the size of - ``length``; larger buffers can cause memory - allocation problems on some platforms. - Range is ``1`` (default) to ``max_size``. - However, ``register`` + ``length`` cannot be - greater than ``max_size``. - """ - if length < 1: - raise ValueError("Length must be '1' or greater.") - if length > self._max_size: - raise ValueError("Length '{0}' greater than maximum FRAM size." - " ({1})".format(length, self._max_size)) - if (register + length) > self._max_size: - raise ValueError("Register + Length greater than maximum FRAM size." - " ({0})".format(self._max_size)) - read_buffer = bytearray(length) - for i in range(length): - read_buffer[i] = self._read_byte(register + i)[0] - return read_buffer + self._write_register(key.start, value) - def write_single(self, register, data): - """ Writes a single byte to the FRAM. + elif isinstance(key, slice): + if not isinstance(value, (bytearray, list, tuple)): + raise ValueError("Data must be either a bytearray, list, or tuple.") + if (key.start > self._max_size): + raise ValueError("Requested register '{0}' greater than maximum" + " FRAM size. ({1})".format(key.start, + self._max_size)) + if not key.step is None: + raise ValueError("Slice steps are not allowed during write operations.") - :param: int register: Register location to write the byte data. - :param: int data: The data to write. - """ - if not isinstance(data, int): - raise ValueError("Data must be an integer.") - if self.write_protected: - raise RuntimeError("FRAM currently write protected.") - if register > self._max_size: - raise ValueError("Requested register '{0}' greater than maximum" - " FRAM size. ({1})".format(register, - self._max_size)) - self._write_register(register, data) - - def write_sequence(self, start_register, data, wraparound=False): - """ Writes sequential data to the FRAM. - - :param: int start_register: Register location to start writing the - data. - :param: data: The data to write. Must be an iterable type of either - ``bytearray``, ``list``, or ``tuple``. - :param: bool wraparound: Controls if sequential writes can wraparound - beyond the ``max_size`` to zero, when - ``start_register`` + ``data length`` is - greater than ``max_size``. - """ - if not isinstance(data, (bytearray, list, tuple)): - raise ValueError("Data must be either a bytearray, list, or tuple.") - if self.write_protected: - raise RuntimeError("FRAM currently write protected.") - if start_register > self._max_size: - raise ValueError("Requested register '{0}' greater than maximum" - " FRAM size. ({1})".format(start_register, - self._max_size)) - self._write_page(start_register, data, wraparound) + self._write_page(key.start, value, self._wraparound) def _read_byte(self, register): return self._read_register(register) @@ -209,19 +179,16 @@ def _write_page(self, start_register, data, wraparound): class FRAM_I2C(FRAM): """ I2C class for FRAM. - :param: i2c_SCL: The I2C SCL pin. Must be a ``board.PIN`` object. - :param: i2c_SDA: The I2C SDA print. Must be a ``board.PIN`` object. + :param: ~busio.I2C i2c_bus: The I2C bus the FRAM is connected to. :param: int address: I2C address of FRAM. Default address is ``0x50``. :param: bool write_protect: Turns on/off initial write protection. Default is ``False``. - :param: wp_pin: Physical ``WP`` breakout pin. Must be a ``board.PIN`` - object. + :param: wp_pin: Physical pin connected to the ``WP`` breakout pin. + Must be a ``digitalio.DigitalInOut`` object. """ #pylint: disable=too-many-arguments - def __init__(self, i2c_SCL, i2c_SDA, address=0x50, write_protect=False, + def __init__(self, i2c_bus, address=0x50, write_protect=False, wp_pin=None): - from busio import I2C as i2c - i2c_bus = i2c(i2c_SCL, i2c_SDA) i2c_bus.try_lock() i2c_bus.writeto((0xF8 >> 1), bytearray([(address << 1)]), stop=False) read_buf = bytearray(3) @@ -266,7 +233,7 @@ def _write_page(self, start_register, data, wraparound=False): pass else: raise ValueError("Starting register + data length extends beyond" - " FRAM maximum size. Use 'wraparound=True' to" + " FRAM maximum size. Use ``write_wraparound`` to" " override this warning.") with self._i2c as i2c: for i in range(0, data_length): diff --git a/examples/fram_i2c_simpletest.py b/examples/fram_i2c_simpletest.py index 4ead358..72c040f 100644 --- a/examples/fram_i2c_simpletest.py +++ b/examples/fram_i2c_simpletest.py @@ -1,36 +1,38 @@ ## Simple Example For CircuitPython/Python I2C FRAM Library import board +import busio import adafruit_fram ## Create a FRAM object (default address used). - -fram = adafruit_fram.FRAM_I2C(board.SCL, board.SDA) +i2c = busio.I2C(board.SCL, board.SDA) +fram = adafruit_fram.FRAM_I2C(i2c) ## Optional FRAM object with a different I2C address, as well ## as a pin to control the hardware write protection ('WP' ## pin on breakout). 'write_protected()' can be used ## independent of the hardware pin. -#fram = adafruit_fram.FRAM_I2C(board.SCL, -# board.SDA, +#import digitalio +#wp = digitalio.DigitalInOut(board.D10) +#fram = adafruit_fram.FRAM_I2C(i2c, # address=0x53, -# wp_pin=board.D4) +# wp_pin=wp) ## Write a single-byte value to register address '0' -fram.write_single(0, 1) +fram[0] = 1 ## Read that byte to ensure a proper write. -## Note: 'read()' returns a bytearray +## Note: reads return a bytearray -print(fram.read(0)[1]) +print(fram[0]) ## Or write a sequential value, then read the values back. -## Note: 'read()' returns a bytearray. It also allocates -## a buffer the size of 'length', which may cause +## Note: reads return a bytearray. Reads also allocate +## a buffer the size of slice, which may cause ## problems on memory-constrained platforms. #values = list(range(100)) # or bytearray or tuple -#fram.write_sequence(0, values) -#fram.read(0, length=100) +#fram[0:100] = values +#print(fram[0:100]) From fbd6a0e1c3caffbe48bd93776ef371574f56040d Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 15 Oct 2018 00:04:25 -0500 Subject: [PATCH 11/19] fix to the fixes; slice handling. --- adafruit_fram.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index a62ce0d..d09b159 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -121,11 +121,11 @@ def __getitem__(self, key): if key > self._max_size: raise ValueError("Register '{0}' greater than maximum FRAM size." " ({1})".format(key, self._max_size)) - return self._read_byte(key)[0] + return self._read_byte(key) elif isinstance(key, slice): registers = list(range(key.start if not key.start is None else 0, - key.stop if not key.stop is None else None, - key.step if not key.step is None else None)) + key.stop if not key.stop is None else self._max_size, + key.step if not key.step is None else 1)) if (registers[0] + len(registers)) > self._max_size: raise ValueError("Register + Length greater than maximum FRAM size." " ({0})".format(self._max_size)) From 4c5a58b5c6f2e28224c33afddc8440b737445316 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 15 Oct 2018 00:27:52 -0500 Subject: [PATCH 12/19] pylint fixes --- adafruit_fram.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index d09b159..e470a4c 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -121,11 +121,11 @@ def __getitem__(self, key): if key > self._max_size: raise ValueError("Register '{0}' greater than maximum FRAM size." " ({1})".format(key, self._max_size)) - return self._read_byte(key) + read_buffer = self._read_byte(key) elif isinstance(key, slice): registers = list(range(key.start if not key.start is None else 0, - key.stop if not key.stop is None else self._max_size, - key.step if not key.step is None else 1)) + key.stop if not key.stop is None else self._max_size, + key.step if not key.step is None else 1)) if (registers[0] + len(registers)) > self._max_size: raise ValueError("Register + Length greater than maximum FRAM size." " ({0})".format(self._max_size)) @@ -133,7 +133,8 @@ def __getitem__(self, key): read_buffer = bytearray(len(registers)) for i, register in enumerate(registers): read_buffer[i] = self._read_byte(register)[0] - return read_buffer + + return read_buffer def __setitem__(self, key, value): if self.write_protected: @@ -152,7 +153,7 @@ def __setitem__(self, key, value): elif isinstance(key, slice): if not isinstance(value, (bytearray, list, tuple)): raise ValueError("Data must be either a bytearray, list, or tuple.") - if (key.start > self._max_size): + if key.start > self._max_size: raise ValueError("Requested register '{0}' greater than maximum" " FRAM size. ({1})".format(key.start, self._max_size)) From 8166bbf80eeffaccf01a42e9625b0e1caaac8c6a Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 15 Oct 2018 21:24:56 -0500 Subject: [PATCH 13/19] Device ID now read with I2CDevice; updated docstring for getter/setter --- adafruit_fram.py | 41 +++++++++++++++++++++++++++++++++-------- docs/api.rst | 1 + 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index e470a4c..c6da639 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -117,6 +117,19 @@ def write_protected(self, value): self._wp_pin.value = value def __getitem__(self, key): + """ Read the value at the given index, or values in a slice. + + .. code-block:: python + + # read single index + fram[0] + + # read values 0 thru 9 with a slice + fram[0:9] + + # read every other value from 0 thru 10 using a step + fram[0:10:2] + """ if isinstance(key, int): if key > self._max_size: raise ValueError("Register '{0}' greater than maximum FRAM size." @@ -137,15 +150,27 @@ def __getitem__(self, key): return read_buffer def __setitem__(self, key, value): + """ Write the value at the given index, or values in a slice. + + .. code-block:: python + + # write single index + fram[0] = 1 + + # write values 0 thru 4 with a slice + fram[0:4] = [0,1,2,3] + + .. note:: Slice stepping is not available when writing + """ if self.write_protected: raise RuntimeError("FRAM currently write protected.") if isinstance(key, int): if not isinstance(value, int): raise ValueError("Data must be an integer.") - if key.start > self._max_size: + if key > self._max_size: raise ValueError("Requested register '{0}' greater than maximum" - " FRAM size. ({1})".format(key.start, + " FRAM size. ({1})".format(key, self._max_size)) self._write_register(key.start, value) @@ -184,23 +209,23 @@ class FRAM_I2C(FRAM): :param: int address: I2C address of FRAM. Default address is ``0x50``. :param: bool write_protect: Turns on/off initial write protection. Default is ``False``. - :param: wp_pin: Physical pin connected to the ``WP`` breakout pin. + :param: wp_pin: (Optional) Physical pin connected to the ``WP`` breakout pin. Must be a ``digitalio.DigitalInOut`` object. """ #pylint: disable=too-many-arguments def __init__(self, i2c_bus, address=0x50, write_protect=False, wp_pin=None): - i2c_bus.try_lock() - i2c_bus.writeto((0xF8 >> 1), bytearray([(address << 1)]), stop=False) + from adafruit_bus_device.i2c_device import I2CDevice as i2cdev + dev_id_addr = 0xF8 >> 1 read_buf = bytearray(3) - i2c_bus.readfrom_into((0xF9 >> 1), read_buf) + with i2cdev(i2c_bus, dev_id_addr) as dev_id: + dev_id.write(bytearray([(address << 1)]), stop=False) + dev_id.readinto(read_buf) manf_id = (((read_buf[0] << 4) +(read_buf[1] >> 4))) prod_id = (((read_buf[1] & 0x0F) << 8) + read_buf[2]) if (manf_id != _I2C_MANF_ID) and (prod_id != _I2C_PROD_ID): raise OSError("FRAM I2C device not found.") - i2c_bus.unlock() - from adafruit_bus_device.i2c_device import I2CDevice as i2cdev self._i2c = i2cdev(i2c_bus, address) super().__init__(_MAX_SIZE_I2C, write_protect, wp_pin) diff --git a/docs/api.rst b/docs/api.rst index 132a3ed..5c7525c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -6,3 +6,4 @@ .. automodule:: adafruit_fram :members: + :special-members: __getitem__, __setitem__ From 2544b4fce82c12078aed0c50734b0476618eacef Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 15 Oct 2018 21:29:16 -0500 Subject: [PATCH 14/19] fix fail; this fixes the fail. maybe. --- adafruit_fram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index c6da639..318d4c5 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -173,7 +173,7 @@ def __setitem__(self, key, value): " FRAM size. ({1})".format(key, self._max_size)) - self._write_register(key.start, value) + self._write_register(key, value) elif isinstance(key, slice): if not isinstance(value, (bytearray, list, tuple)): From 5666a1b99036337be20a0cd12569a5682f5bf10f Mon Sep 17 00:00:00 2001 From: sommersoft Date: Fri, 19 Oct 2018 16:21:52 -0500 Subject: [PATCH 15/19] change read() to read the whole request at once; not one byte at a time --- adafruit_fram.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 318d4c5..6121ff2 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -134,7 +134,8 @@ def __getitem__(self, key): if key > self._max_size: raise ValueError("Register '{0}' greater than maximum FRAM size." " ({1})".format(key, self._max_size)) - read_buffer = self._read_byte(key) + buffer = bytearray(1) + read_buffer = self._read_register(key, buffer) elif isinstance(key, slice): registers = list(range(key.start if not key.start is None else 0, key.stop if not key.stop is None else self._max_size, @@ -143,9 +144,8 @@ def __getitem__(self, key): raise ValueError("Register + Length greater than maximum FRAM size." " ({0})".format(self._max_size)) - read_buffer = bytearray(len(registers)) - for i, register in enumerate(registers): - read_buffer[i] = self._read_byte(register)[0] + buffer = bytearray(len(registers)) + read_buffer = self._read_register(registers[0], buffer) return read_buffer @@ -187,9 +187,6 @@ def __setitem__(self, key, value): self._write_page(key.start, value, self._wraparound) - def _read_byte(self, register): - return self._read_register(register) - def _read_register(self, register): # Implemented by subclass raise NotImplementedError @@ -229,11 +226,10 @@ def __init__(self, i2c_bus, address=0x50, write_protect=False, self._i2c = i2cdev(i2c_bus, address) super().__init__(_MAX_SIZE_I2C, write_protect, wp_pin) - def _read_register(self, register): + def _read_register(self, register, read_buffer): write_buffer = bytearray(2) write_buffer[0] = register >> 8 write_buffer[1] = register & 0xFF - read_buffer = bytearray(1) with self._i2c as i2c: i2c.write_then_readinto(write_buffer, read_buffer) return read_buffer From 8180928453188cc1febbf19add4475ae52554058 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Fri, 19 Oct 2018 17:13:57 -0500 Subject: [PATCH 16/19] change 'max_size()' to '__len__' --- adafruit_fram.py | 24 +++++++++++++++++------- docs/api.rst | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 6121ff2..efe8f35 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -75,13 +75,6 @@ def __init__(self, max_size, write_protect=False, wp_pin=None): else: self._wp_pin = wp_pin - @property - def max_size(self): - """ The maximum size of the current FRAM chip. This is the highest - register location that can be read or written to. - """ - return self._max_size - @property def write_wraparound(self): """ Determines if sequential writes will wrapaound the ``FRAM.max_size`` @@ -116,6 +109,23 @@ def write_protected(self, value): if not self._wp_pin is None: self._wp_pin.value = value + def __len__(self): + """ The maximum size of the current FRAM chip. This is the highest + register location that can be read or written to. + + .. code-block:: python + + fram = adafruit_fram.FRAM() + + # maximum size returned by len() + len(fram) + + # can be used with range + for i in range(0, len(fram)) + """ + return self._max_size + + def __getitem__(self, key): """ Read the value at the given index, or values in a slice. diff --git a/docs/api.rst b/docs/api.rst index 5c7525c..b6a266a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -6,4 +6,4 @@ .. automodule:: adafruit_fram :members: - :special-members: __getitem__, __setitem__ + :special-members: __len__, __getitem__, __setitem__ From f58f172f2417be3801fb8a35c496612cef7179d6 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 21 Oct 2018 19:34:44 -0500 Subject: [PATCH 17/19] clean up slicing; PEP8isms --- adafruit_fram.py | 64 +++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index efe8f35..50904ef 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -67,7 +67,6 @@ def __init__(self, max_size, write_protect=False, wp_pin=None): self._wp = write_protect self._wraparound = False if not wp_pin is None: - #import digitalio self._wp_pin = wp_pin # Make sure write_prot is set to output self._wp_pin.switch_to_output() @@ -136,9 +135,6 @@ def __getitem__(self, key): # read values 0 thru 9 with a slice fram[0:9] - - # read every other value from 0 thru 10 using a step - fram[0:10:2] """ if isinstance(key, int): if key > self._max_size: @@ -147,9 +143,11 @@ def __getitem__(self, key): buffer = bytearray(1) read_buffer = self._read_register(key, buffer) elif isinstance(key, slice): - registers = list(range(key.start if not key.start is None else 0, - key.stop if not key.stop is None else self._max_size, - key.step if not key.step is None else 1)) + if key.step is not None: + raise ValueError("Slice stepping is not currently available.") + + registers = list(range(key.start if key.start is not None else 0, + key.stop if key.stop is not None else self._max_size)) if (registers[0] + len(registers)) > self._max_size: raise ValueError("Register + Length greater than maximum FRAM size." " ({0})".format(self._max_size)) @@ -160,52 +158,38 @@ def __getitem__(self, key): return read_buffer def __setitem__(self, key, value): - """ Write the value at the given index, or values in a slice. + """ Write the value at the given starting index. .. code-block:: python # write single index fram[0] = 1 - # write values 0 thru 4 with a slice - fram[0:4] = [0,1,2,3] - - .. note:: Slice stepping is not available when writing + # write values 0 thru 4 with a list + fram[0] = [0,1,2,3] """ if self.write_protected: raise RuntimeError("FRAM currently write protected.") if isinstance(key, int): - if not isinstance(value, int): - raise ValueError("Data must be an integer.") + if not isinstance(value, (int, bytearray, list, tuple)): + raise ValueError("Data must be a single integer, or a bytearray," + " list, or tuple.") if key > self._max_size: raise ValueError("Requested register '{0}' greater than maximum" " FRAM size. ({1})".format(key, self._max_size)) - self._write_register(key, value) + self._write(key, value, self._wraparound) elif isinstance(key, slice): - if not isinstance(value, (bytearray, list, tuple)): - raise ValueError("Data must be either a bytearray, list, or tuple.") - if key.start > self._max_size: - raise ValueError("Requested register '{0}' greater than maximum" - " FRAM size. ({1})".format(key.start, - self._max_size)) - if not key.step is None: - raise ValueError("Slice steps are not allowed during write operations.") - - self._write_page(key.start, value, self._wraparound) + raise ValueError("Slicing not available during write operations.") - def _read_register(self, register): + def _read_register(self, register, buffer): # Implemented by subclass raise NotImplementedError - def _write_register(self, register, data): - # Implemented by subclass - raise NotImplementedError - - def _write_page(self, start_register, data, wraparound): + def _write(self, start_register, data, wraparound): # Implemened by subclass raise NotImplementedError @@ -244,23 +228,19 @@ def _read_register(self, register, read_buffer): i2c.write_then_readinto(write_buffer, read_buffer) return read_buffer - def _write_register(self, register, data): - buffer = bytearray(3) - buffer[0] = register >> 8 - buffer[1] = register & 0xFF - buffer[2] = data - with self._i2c as i2c: - i2c.write(buffer) - - def _write_page(self, start_register, data, wraparound=False): + def _write(self, start_register, data, wraparound=False): # Decided against using the chip's "Page Write", since that would require # doubling the memory usage by creating a buffer that includes the passed # in data so that it can be sent all in one `i2c.write`. The single-write # method is slower, and forces us to handle wraparound, but I feel this # is a better tradeoff for limiting the memory required for large writes. buffer = bytearray(3) - data_length = len(data) - if (start_register + data_length) > self._max_size: + if not isinstance(data, int): + data_length = len(data) + else: + data_length = 1 + data = [data] + if (start_register + data_length) - 1 > self._max_size: if wraparound: pass else: From 59a826fdf1ade5a510a598c13366ad51256c6d27 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 22 Oct 2018 20:06:30 -0500 Subject: [PATCH 18/19] docstring update; Travis re-sync kick. --- adafruit_fram.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index 50904ef..a2610b8 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -76,9 +76,9 @@ def __init__(self, max_size, write_protect=False, wp_pin=None): @property def write_wraparound(self): - """ Determines if sequential writes will wrapaound the ``FRAM.max_size`` - address. If ``False``, and a requested write will extend beyond the - maximum size, an exception is raised. + """ Determines if sequential writes will wrapaound highest memory address + (``len(FRAM)``) address. If ``False``, and a requested write will + extend beyond the maximum size, an exception is raised. """ return self._wraparound @@ -114,7 +114,7 @@ def __len__(self): .. code-block:: python - fram = adafruit_fram.FRAM() + fram = adafruit_fram.FRAM_xxx() # xxx = 'I2C' or 'SPI' # maximum size returned by len() len(fram) From 3005592637916b0e5e9dafa253229762685063c3 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 22 Oct 2018 20:48:48 -0500 Subject: [PATCH 19/19] pylint fix --- adafruit_fram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_fram.py b/adafruit_fram.py index a2610b8..45c8341 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -185,7 +185,7 @@ def __setitem__(self, key, value): elif isinstance(key, slice): raise ValueError("Slicing not available during write operations.") - def _read_register(self, register, buffer): + def _read_register(self, register, read_buffer): # Implemented by subclass raise NotImplementedError