diff --git a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md
index 71ef8f8..8de294e 100644
--- a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md
+++ b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md
@@ -4,7 +4,7 @@
Thank you for contributing! Before you submit a pull request, please read the following.
-Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://circuitpython.readthedocs.io/en/latest/docs/design_guide.html
+Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html
If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ca35544..041a337 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -10,66 +10,5 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - name: Dump GitHub context
- env:
- GITHUB_CONTEXT: ${{ toJson(github) }}
- run: echo "$GITHUB_CONTEXT"
- - name: Translate Repo Name For Build Tools filename_prefix
- id: repo-name
- run: |
- echo ::set-output name=repo-name::$(
- echo ${{ github.repository }} |
- awk -F '\/' '{ print tolower($2) }' |
- tr '_' '-'
- )
- - name: Set up Python 3.7
- uses: actions/setup-python@v1
- with:
- python-version: 3.7
- - name: Versions
- run: |
- python3 --version
- - name: Checkout Current Repo
- uses: actions/checkout@v1
- with:
- submodules: true
- - name: Checkout tools repo
- uses: actions/checkout@v2
- with:
- repository: adafruit/actions-ci-circuitpython-libs
- path: actions-ci
- - name: Install dependencies
- # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.)
- run: |
- source actions-ci/install.sh
- - name: Pip install Sphinx, pre-commit
- run: |
- pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit
- - name: Library version
- run: git describe --dirty --always --tags
- - name: Pre-commit hooks
- run: |
- pre-commit run --all-files
- - name: Build assets
- run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
- - name: Archive bundles
- uses: actions/upload-artifact@v2
- with:
- name: bundles
- path: ${{ github.workspace }}/bundles/
- - name: Build docs
- working-directory: docs
- run: sphinx-build -E -W -b html . _build/html
- - name: Check For setup.py
- id: need-pypi
- run: |
- echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
- - name: Build Python package
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- run: |
- pip install --upgrade setuptools wheel twine readme_renderer testresources
- python setup.py sdist
- python setup.py bdist_wheel --universal
- twine check dist/*
- - name: Setup problem matchers
- uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1
+ - name: Run Build CI workflow
+ uses: adafruit/workflows-circuitpython-libs/build@main
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 6d0015a..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
-#
-# SPDX-License-Identifier: MIT
-
-name: Release Actions
-
-on:
- release:
- types: [published]
-
-jobs:
- upload-release-assets:
- runs-on: ubuntu-latest
- steps:
- - name: Dump GitHub context
- env:
- GITHUB_CONTEXT: ${{ toJson(github) }}
- run: echo "$GITHUB_CONTEXT"
- - name: Translate Repo Name For Build Tools filename_prefix
- id: repo-name
- run: |
- echo ::set-output name=repo-name::$(
- echo ${{ github.repository }} |
- awk -F '\/' '{ print tolower($2) }' |
- tr '_' '-'
- )
- - name: Set up Python 3.6
- uses: actions/setup-python@v1
- with:
- python-version: 3.6
- - name: Versions
- run: |
- python3 --version
- - name: Checkout Current Repo
- uses: actions/checkout@v1
- with:
- submodules: true
- - name: Checkout tools repo
- uses: actions/checkout@v2
- with:
- repository: adafruit/actions-ci-circuitpython-libs
- path: actions-ci
- - name: Install deps
- run: |
- source actions-ci/install.sh
- - name: Build assets
- run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
- - name: Upload Release Assets
- # the 'official' actions version does not yet support dynamically
- # supplying asset names to upload. @csexton's version chosen based on
- # discussion in the issue below, as its the simplest to implement and
- # allows for selecting files with a pattern.
- # https://github.com/actions/upload-release-asset/issues/4
- #uses: actions/upload-release-asset@v1.0.1
- uses: csexton/release-asset-action@master
- with:
- pattern: "bundles/*"
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- upload-pypi:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v1
- - name: Check For setup.py
- id: need-pypi
- run: |
- echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
- - name: Set up Python
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- uses: actions/setup-python@v1
- with:
- python-version: '3.x'
- - name: Install dependencies
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- run: |
- python -m pip install --upgrade pip
- pip install setuptools wheel twine
- - name: Build and publish
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- env:
- TWINE_USERNAME: ${{ secrets.pypi_username }}
- TWINE_PASSWORD: ${{ secrets.pypi_password }}
- run: |
- python setup.py sdist
- twine upload dist/*
diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml
new file mode 100644
index 0000000..9acec60
--- /dev/null
+++ b/.github/workflows/release_gh.yml
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+name: GitHub Release Actions
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ upload-release-assets:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Run GitHub Release CI workflow
+ uses: adafruit/workflows-circuitpython-libs/release-gh@main
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ upload-url: ${{ github.event.release.upload_url }}
diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml
new file mode 100644
index 0000000..65775b7
--- /dev/null
+++ b/.github/workflows/release_pypi.yml
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+name: PyPI Release Actions
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ upload-release-assets:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Run PyPI Release CI workflow
+ uses: adafruit/workflows-circuitpython-libs/release-pypi@main
+ with:
+ pypi-username: ${{ secrets.pypi_username }}
+ pypi-password: ${{ secrets.pypi_password }}
diff --git a/.gitignore b/.gitignore
index 9647e71..db3d538 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,48 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
+# SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries
#
-# SPDX-License-Identifier: Unlicense
+# SPDX-License-Identifier: MIT
+# Do not include files and directories created by your personal work environment, such as the IDE
+# you use, except for those already listed here. Pull requests including changes to this file will
+# not be accepted.
+
+# This .gitignore file contains rules for files generated by working with CircuitPython libraries,
+# including building Sphinx, testing with pip, and creating a virual environment, as well as the
+# MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs.
+
+# If you find that there are files being generated on your machine that should not be included in
+# your git commit, you should create a .gitignore_global file on your computer to include the
+# files created by your personal setup. To do so, follow the two steps below.
+
+# First, create a file called .gitignore_global somewhere convenient for you, and add rules for
+# the files you want to exclude from git commits.
+
+# Second, configure Git to use the exclude file for all Git repositories by running the
+# following via commandline, replacing "path/to/your/" with the actual path to your newly created
+# .gitignore_global file:
+# git config --global core.excludesfile path/to/your/.gitignore_global
+
+# CircuitPython-specific files
*.mpy
-.idea
+
+# Python-specific files
__pycache__
-_build
*.pyc
+
+# Sphinx build-specific files
+_build
+
+# This file results from running `pip -e install .` in a local repository
+*.egg-info
+
+# Virtual environment-specific files
.env
-bundles
+.venv
+
+# MacOS-specific files
*.DS_Store
-.eggs
-dist
-**/*.egg-info
+
+# IDE-specific files
+.idea
+.vscode
+*~
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1b9fadc..70ade69 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,40 +3,40 @@
# SPDX-License-Identifier: Unlicense
repos:
-- repo: https://github.com/python/black
- rev: 20.8b1
+ - repo: https://github.com/python/black
+ rev: 23.3.0
hooks:
- - id: black
-- repo: https://github.com/fsfe/reuse-tool
- rev: v0.12.1
+ - id: black
+ - repo: https://github.com/fsfe/reuse-tool
+ rev: v1.1.2
hooks:
- - id: reuse
-- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v2.3.0
+ - id: reuse
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.4.0
hooks:
- - id: check-yaml
- - id: end-of-file-fixer
- - id: trailing-whitespace
-- repo: https://github.com/pycqa/pylint
- rev: v2.11.1
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ - repo: https://github.com/pycqa/pylint
+ rev: v2.17.4
hooks:
- - id: pylint
+ - id: pylint
name: pylint (library code)
types: [python]
args:
- --disable=consider-using-f-string
exclude: "^(docs/|examples/|tests/|setup.py$)"
- - id: pylint
+ - id: pylint
name: pylint (example code)
description: Run pylint rules on "examples/*.py" files
types: [python]
files: "^examples/"
args:
- - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
- - id: pylint
+ - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
+ - id: pylint
name: pylint (test code)
description: Run pylint rules on "tests/*.py" files
types: [python]
files: "^tests/"
args:
- - --disable=missing-docstring,consider-using-f-string,duplicate-code
+ - --disable=missing-docstring,consider-using-f-string,duplicate-code
diff --git a/.pylintrc b/.pylintrc
index b934d95..f945e92 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
+# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
@@ -9,11 +9,11 @@
# run arbitrary code
extension-pkg-whitelist=
-# Add files or directories to the blacklist. They should be base names, not
+# Add files or directories to the ignore-list. They should be base names, not
# paths.
ignore=CVS
-# Add files or directories matching the regex patterns to the blacklist. The
+# Add files or directories matching the regex patterns to the ignore-list. The
# regex matches against base names, not paths.
ignore-patterns=
@@ -26,7 +26,7 @@ jobs=1
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
-load-plugins=
+load-plugins=pylint.extensions.no_self_use
# Pickle collected data for later comparisons.
persistent=yes
@@ -54,8 +54,8 @@ confidence=
# --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,bad-continuation
+# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call
+disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding
# 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
@@ -225,12 +225,6 @@ 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
@@ -252,43 +246,27 @@ ignore-docstrings=yes
ignore-imports=yes
# Minimum lines number of a similarity.
-min-similarity-lines=4
+min-similarity-lines=12
[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_]*)|(__.*__))$
@@ -296,9 +274,6 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# 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_]*))$
@@ -309,21 +284,12 @@ 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]+))$
@@ -339,9 +305,6 @@ no-docstring-rgx=^_
# 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_]*))$
@@ -398,7 +361,7 @@ valid-metaclass-classmethod-first-arg=mcs
[DESIGN]
# Maximum number of arguments for function / method
-max-args=6
+max-args=5
# Maximum number of attributes for a class (see R0902).
# max-attributes=7
@@ -408,7 +371,7 @@ max-attributes=11
max-bool-expr=5
# Maximum number of branch for function / method body
-max-branches=15
+max-branches=12
# Maximum number of locals for function / method body
max-locals=15
@@ -433,4 +396,4 @@ min-public-methods=1
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
-overgeneral-exceptions=Exception
+overgeneral-exceptions=builtins.Exception
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..88bca9f
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,22 @@
+# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
+#
+# SPDX-License-Identifier: Unlicense
+
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+sphinx:
+ configuration: docs/conf.py
+
+build:
+ os: ubuntu-20.04
+ tools:
+ python: "3"
+
+python:
+ install:
+ - requirements: docs/requirements.txt
+ - requirements: requirements.txt
diff --git a/.readthedocs.yml b/.readthedocs.yml
deleted file mode 100644
index 49dcab3..0000000
--- a/.readthedocs.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
-#
-# SPDX-License-Identifier: Unlicense
-
-python:
- version: 3
-requirements_file: docs/requirements.txt
diff --git a/README.rst b/README.rst
index 4ab5828..5da80bf 100644
--- a/README.rst
+++ b/README.rst
@@ -3,10 +3,10 @@ Introduction
============
.. image:: https://readthedocs.org/projects/adafruit-circuitpython-avrprog/badge/?version=latest
- :target: https://circuitpython.readthedocs.io/projects/avrprog/en/latest/
+ :target: https://docs.circuitpython.org/projects/avrprog/en/latest/
:alt: Documentation Status
-.. image :: https://img.shields.io/discord/327254708534116352.svg
+.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg
:target: https://adafru.it/discord
:alt: Discord
@@ -14,6 +14,10 @@ Introduction
:target: https://github.com/adafruit/Adafruit_CircuitPython_AVRprog/actions/
:alt: Build Status
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code Style: Black
+
Program your favorite AVR chips directly from CircuitPython with this handy helper class that will let you make stand-alone programmers right from your REPL. Should work with any/all AVR chips, via SPI programming. Tested with ATmega328, ATtiny85 and ATmega2560
Dependencies
@@ -47,8 +51,8 @@ To install in a virtual environment in your current project:
.. code-block:: shell
mkdir project-name && cd project-name
- python3 -m venv .env
- source .env/bin/activate
+ python3 -m venv .venv
+ source .venv/bin/activate
pip3 install adafruit-circuitpython-avrprog
Usage Example
@@ -59,7 +63,9 @@ See examples folder for full examples that progam various bootloaders onto chips
Documentation
=============
-API documentation for this library can be found on `Read the Docs `_.
+API documentation for this library can be found on `Read the Docs `_.
+
+For information on building library documentation, please check out `this guide `_.
Contributing
============
@@ -67,8 +73,3 @@ Contributing
Contributions are welcome! Please read our `Code of Conduct
`_
before contributing to help this project stay welcoming.
-
-Documentation
-=============
-
-For information on building library documentation, please check out `this guide `_.
diff --git a/adafruit_avrprog.py b/adafruit_avrprog.py
index ab8dae2..88353a0 100644
--- a/adafruit_avrprog.py
+++ b/adafruit_avrprog.py
@@ -29,13 +29,55 @@
# imports
-__version__ = "0.0.0-auto.0"
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AVRprog.git"
-from digitalio import Direction, DigitalInOut
+try:
+ from typing import List, Optional, Tuple, Union, TypedDict
+ from typing_extensions import TypeAlias
+ from os import PathLike
+ from busio import SPI
+ from microcontroller import Pin
-_SLOW_CLOCK = 100000
-_FAST_CLOCK = 1000000
+ # Technically this type should come from: from _typeshed import FileDescriptorOrPath
+ # Unfortunately _typeshed is only in the standard library in newer releases of Python, e.g. 3.11
+ # Thus have to define a placeholder
+ FileDescriptorOrPath: TypeAlias = Union[
+ int, str, bytes, PathLike[str], PathLike[bytes]
+ ]
+
+ from io import TextIOWrapper
+
+ class ChipDictionary(TypedDict):
+ """
+ Dictionary representing a specific target chip type
+ """
+
+ name: str
+ sig: List[int]
+ flash_size: int
+ page_size: int
+ fuse_mask: Tuple[int, int, int, int]
+
+ class FileState(TypedDict):
+ """
+ Dictionary representing a File State
+ """
+
+ # pylint: disable=invalid-name
+ line: int
+ ext_addr: int
+ eof: bool
+ f: Optional[TextIOWrapper]
+
+except ImportError:
+ pass
+
+
+from digitalio import DigitalInOut, Direction
+
+_SLOW_CLOCK: int = 100000
+_FAST_CLOCK: int = 1000000
class AVRprog:
@@ -71,6 +113,20 @@ class Boards:
"page_size": 128,
"fuse_mask": (0xFF, 0xFF, 0x07, 0x3F),
}
+ ATmega328pb = {
+ "name": "ATmega328pb",
+ "sig": [0x1E, 0x95, 0x16],
+ "flash_size": 32768,
+ "page_size": 128,
+ "fuse_mask": (0xFF, 0xFF, 0x07, 0x3F),
+ }
+ ATmega644pa = {
+ "name": "ATmega644pa",
+ "sig": [0x1E, 0x96, 0x0A],
+ "flash_size": 65536,
+ "page_size": 256,
+ "fuse_mask": (0xF7, 0x8F, 0xFD, 0xFF),
+ }
ATmega2560 = {
"name": "ATmega2560",
"sig": [0x1E, 0x98, 0x01],
@@ -79,10 +135,10 @@ class Boards:
"fuse_mask": (0xFF, 0xFF, 0x07, 0x3F),
}
- _spi = None
- _rst = None
+ _spi: Optional[SPI] = None
+ _rst: Optional[DigitalInOut] = None
- def init(self, spi_bus, rst_pin):
+ def init(self, spi_bus: SPI, rst_pin: Pin) -> None:
"""
Initialize the programmer with an SPI port that will be used to
communicate with the chip. Make sure your SPI supports 'write_readinto'
@@ -93,7 +149,7 @@ def init(self, spi_bus, rst_pin):
self._rst.direction = Direction.OUTPUT
self._rst.value = True
- def verify_sig(self, chip, verbose=False):
+ def verify_sig(self, chip: ChipDictionary, verbose: bool = False) -> bool:
"""
Verify that the chip is connected properly, responds to commands,
and has the correct signature. Returns True/False based on success
@@ -107,7 +163,14 @@ def verify_sig(self, chip, verbose=False):
return False
return True
- def program_file(self, chip, file_name, verbose=False, verify=True):
+ # pylint: disable=too-many-branches
+ def program_file(
+ self,
+ chip: ChipDictionary,
+ file_name: FileDescriptorOrPath,
+ verbose: bool = False,
+ verify: bool = True,
+ ) -> bool:
"""
Perform a chip erase and program from a file that
contains Intel HEX data. Returns true on verify-success, False on
@@ -120,14 +183,12 @@ def program_file(self, chip, file_name, verbose=False, verify=True):
print("Erasing chip....")
self.erase_chip()
- clock_speed = getattr(chip, "clock_speed", _FAST_CLOCK)
+ clock_speed = chip.get("clock_speed", _FAST_CLOCK)
self.begin(clock=clock_speed)
# create a file state dictionary
- file_state = {"line": 0, "ext_addr": 0, "eof": False}
- with open(file_name, "r") as file_state[ # pylint: disable=unspecified-encoding
- "f"
- ]:
+ file_state = {"line": 0, "ext_addr": 0, "eof": False, "f": None}
+ with open(file_name, "r") as file_state["f"]:
page_size = chip["page_size"]
for page_addr in range(0, chip["flash_size"], page_size):
@@ -175,7 +236,12 @@ def program_file(self, chip, file_name, verbose=False, verify=True):
self.end()
return True
- def verify_file(self, chip, file_name, verbose=False):
+ def verify_file(
+ self,
+ chip: ChipDictionary,
+ file_name: FileDescriptorOrPath,
+ verbose: bool = False,
+ ) -> bool:
"""
Perform a chip full-flash verification from a file that
contains Intel HEX data. Returns True/False on success/fail.
@@ -184,12 +250,10 @@ def verify_file(self, chip, file_name, verbose=False):
raise RuntimeError("Signature read failure")
# create a file state dictionary
- file_state = {"line": 0, "ext_addr": 0, "eof": False}
- with open(file_name, "r") as file_name[ # pylint: disable=unspecified-encoding
- "f"
- ]:
+ file_state = {"line": 0, "ext_addr": 0, "eof": False, "f": None}
+ with open(file_name, "r") as file_state["f"]:
page_size = chip["page_size"]
- clock_speed = getattr(chip, "clock_speed", _FAST_CLOCK)
+ clock_speed = chip.get("clock_speed", _FAST_CLOCK)
self.begin(clock=clock_speed)
for page_addr in range(0x0, chip["flash_size"], page_size):
page_buffer = bytearray(page_size)
@@ -222,12 +286,12 @@ def verify_file(self, chip, file_name, verbose=False):
self.end()
return True
- def read_fuses(self, chip):
+ def read_fuses(self, chip: ChipDictionary) -> Tuple[int, int, int, int]:
"""
- Read the 4 fuses and return them in a list (low, high, ext, lock)
+ Read the 4 fuses and return them in a tuple (low, high, ext, lock)
Each fuse is bitwise-&'s with the chip's fuse mask for simplicity
"""
- mask = chip["fuse_mask"]
+ mask: Tuple[int, int, int, int] = chip["fuse_mask"]
self.begin(clock=_SLOW_CLOCK)
low = self._transaction((0x50, 0, 0, 0))[2] & mask[0]
high = self._transaction((0x58, 0x08, 0, 0))[2] & mask[1]
@@ -236,22 +300,37 @@ def read_fuses(self, chip):
self.end()
return (low, high, ext, lock)
- # pylint: disable=unused-argument,expression-not-assigned
- def write_fuses(self, chip, low=None, high=None, ext=None, lock=None):
+ # pylint: disable=unused-argument,too-many-arguments
+ def write_fuses(
+ self,
+ chip: ChipDictionary,
+ low: Optional[int] = None,
+ high: Optional[int] = None,
+ ext: Optional[int] = None,
+ lock: Optional[int] = None,
+ ) -> None:
"""
Write any of the 4 fuses. If the kwarg low/high/ext/lock is not
passed in or is None, that fuse is skipped
"""
+ transaction_comp = (0xE0, 0xA0, 0xA8, 0xA4)
+ fuses = (lock, low, high, ext)
self.begin(clock=_SLOW_CLOCK)
- lock and self._transaction((0xAC, 0xE0, 0, lock))
- low and self._transaction((0xAC, 0xA0, 0, low))
- high and self._transaction((0xAC, 0xA8, 0, high))
- ext and self._transaction((0xAC, 0xA4, 0, ext))
+ for fuse, comp in zip(fuses, transaction_comp):
+ if fuse:
+ self._transaction((0xAC, comp, 0, fuse))
+ self._busy_wait()
self.end()
- # pylint: enable=unused-argument,expression-not-assigned
-
- def verify_fuses(self, chip, low=None, high=None, ext=None, lock=None):
+ # pylint: disable=too-many-arguments
+ def verify_fuses(
+ self,
+ chip: ChipDictionary,
+ low: Optional[int] = None,
+ high: Optional[int] = None,
+ ext: Optional[int] = None,
+ lock: Optional[int] = None,
+ ) -> bool:
"""
Verify the 4 fuses. If the kwarg low/high/ext/lock is not
passed in or is None, that fuse is not checked.
@@ -266,7 +345,7 @@ def verify_fuses(self, chip, low=None, high=None, ext=None, lock=None):
return False
return True
- def erase_chip(self):
+ def erase_chip(self) -> None:
"""
Fully erases the chip.
"""
@@ -277,7 +356,7 @@ def erase_chip(self):
#################### Mid level
- def begin(self, clock=_FAST_CLOCK):
+ def begin(self, clock: int = _FAST_CLOCK) -> None:
"""
Begin programming mode: pull reset pin low, initialize SPI, and
send the initialization command to get the AVR's attention.
@@ -288,14 +367,14 @@ def begin(self, clock=_FAST_CLOCK):
self._spi.configure(baudrate=clock)
self._transaction((0xAC, 0x53, 0, 0))
- def end(self):
+ def end(self) -> None:
"""
End programming mode: SPI is released, and reset pin set high.
"""
self._spi.unlock()
self._rst.value = True
- def read_signature(self):
+ def read_signature(self) -> List[int]:
"""
Read and return the signature of the chip as two bytes in an array.
Requires calling begin() beforehand to put in programming mode.
@@ -306,7 +385,7 @@ def read_signature(self):
sig.append(self._transaction((0x30, 0, i, 0))[2])
return sig
- def read(self, addr, read_buffer):
+ def read(self, addr: int, read_buffer: bytearray) -> None:
"""
Read a chunk of memory from address 'addr'. The amount read is the
same as the size of the bytearray 'read_buffer'. Data read is placed
@@ -330,13 +409,15 @@ def read(self, addr, read_buffer):
last_addr = read_addr
#################### Low level
- def _flash_word(self, addr, low, high):
+ def _flash_word(self, addr: int, low: int, high: int) -> None:
self._transaction((0x40, addr >> 8, addr, low))
self._transaction((0x48, addr >> 8, addr, high))
- def _flash_page(self, page_buffer, page_addr, page_size):
+ def _flash_page(
+ self, page_buffer: bytearray, page_addr: int, page_size: int
+ ) -> None:
page_addr //= 2 # address is by 'words' not bytes!
- for i in range(page_size / 2): # page indexed by words, not bytes
+ for i in range(page_size // 2): # page indexed by words, not bytes
lo_byte, hi_byte = page_buffer[2 * i : 2 * i + 2]
self._flash_word(i, lo_byte, hi_byte)
@@ -348,23 +429,26 @@ def _flash_page(self, page_buffer, page_addr, page_size):
raise RuntimeError("Failed to commit page to flash")
self._busy_wait()
- def _transaction(self, command):
+ def _transaction(self, command: Tuple[int, int, int, int]) -> bytearray:
reply = bytearray(4)
- command = bytearray([i & 0xFF for i in command])
+ command_bytes = bytearray([i & 0xFF for i in command])
- self._spi.write_readinto(command, reply)
- # s = [hex(i) for i in command]
- # print("Sending %s reply %s" % ([hex(i) for i in command], [hex(i) for i in reply]))
- if reply[2] != command[1]:
+ self._spi.write_readinto(command_bytes, reply)
+ # s = [hex(i) for i in command_bytes]
+ # print("Sending %s reply %s" % ([hex(i) for i in command_bytes], [hex(i) for i in reply]))
+ if reply[2] != command_bytes[1]:
raise RuntimeError("SPI transaction failed")
return reply[1:] # first byte is ignored
- def _busy_wait(self):
+ def _busy_wait(self) -> None:
while self._transaction((0xF0, 0, 0, 0))[2] & 0x01:
pass
-def read_hex_page(file_state, page_addr, page_size, page_buffer):
+def read_hex_page(
+ file_state: FileState, page_addr: int, page_size: int, page_buffer: bytearray
+) -> bool:
+ # pylint: disable=too-many-branches
"""
Helper function that does the Intel Hex parsing. Takes in a dictionary
that contains the file 'state'. The dictionary should have file_state['f']
@@ -420,6 +504,10 @@ def read_hex_page(file_state, page_addr, page_size, page_buffer):
continue
if rec_type == 3: # sometimes appears, we ignore this
continue
+ if rec_type == 4:
+ file_state["ext_addr"] = int(line[9:13], 16) << 16
+ # print("ExtLin addr: %05X" % file_state['ext_addr'])
+ continue
if rec_type != 0: # if not the above or a data record...
raise RuntimeError(
"Unsupported record type %d on line %d" % (rec_type, file_state["line"])
diff --git a/docs/conf.py b/docs/conf.py
index b608fce..43d74bd 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -6,6 +6,7 @@
import os
import sys
+import datetime
sys.path.insert(0, os.path.abspath(".."))
@@ -16,6 +17,7 @@
# ones.
extensions = [
"sphinx.ext.autodoc",
+ "sphinxcontrib.jquery",
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
]
@@ -26,8 +28,8 @@
# autodoc_mock_imports = ["digitalio"]
intersphinx_mapping = {
- "python": ("https://docs.python.org/3.4", None),
- "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
+ "python": ("https://docs.python.org/3", None),
+ "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None),
}
# Add any paths that contain templates here, relative to this directory.
@@ -40,7 +42,14 @@
# General information about the project.
project = "Adafruit AVRprog Library"
-copyright = "2017 ladyada"
+creation_year = "2017"
+current_year = str(datetime.datetime.now().year)
+year_duration = (
+ current_year
+ if current_year == creation_year
+ else creation_year + " - " + current_year
+)
+copyright = year_duration + " ladyada"
author = "ladyada"
# The version info for the project you're documenting, acts as replacement for
@@ -57,7 +66,7 @@
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
-language = None
+language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@@ -88,19 +97,9 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-on_rtd = os.environ.get("READTHEDOCS", None) == "True"
-
-if not on_rtd: # only import and set the theme if we're building docs locally
- try:
- import sphinx_rtd_theme
-
- html_theme = "sphinx_rtd_theme"
- html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."]
- except:
- html_theme = "default"
- html_theme_path = ["."]
-else:
- html_theme_path = ["."]
+import sphinx_rtd_theme
+
+html_theme = "sphinx_rtd_theme"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
diff --git a/docs/index.rst b/docs/index.rst
index 979de03..e820993 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -29,8 +29,9 @@ Table of Contents
.. toctree::
:caption: Other Links
- Download
- CircuitPython Reference Documentation
+ Download from GitHub
+ Download Library Bundle
+ CircuitPython Reference Documentation
CircuitPython Support Forum
Discord Chat
Adafruit Learning System
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 88e6733..979f568 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -2,4 +2,6 @@
#
# SPDX-License-Identifier: Unlicense
-sphinx>=4.0.0
+sphinx
+sphinxcontrib-jquery
+sphinx-rtd-theme
diff --git a/examples/avrprog_program_mega2560.py b/examples/avrprog_program_mega2560.py
index f9948e5..d3ace05 100644
--- a/examples/avrprog_program_mega2560.py
+++ b/examples/avrprog_program_mega2560.py
@@ -35,7 +35,7 @@
def error(err):
- """ Helper to print out errors for us and then halt """
+ """Helper to print out errors for us and then halt"""
print("ERROR: " + err)
avrprog.end()
while True:
diff --git a/examples/avrprog_program_tiny13a.py b/examples/avrprog_program_tiny13a.py
index e5b77e3..4e8b5c5 100644
--- a/examples/avrprog_program_tiny13a.py
+++ b/examples/avrprog_program_tiny13a.py
@@ -25,7 +25,7 @@
def error(err):
- """ Helper to print out errors for us and then halt """
+ """Helper to print out errors for us and then halt"""
print("ERROR: " + err)
avrprog.end()
while True:
diff --git a/examples/avrprog_program_trinket85.py b/examples/avrprog_program_trinket85.py
index 366a4fc..c8b8007 100644
--- a/examples/avrprog_program_trinket85.py
+++ b/examples/avrprog_program_trinket85.py
@@ -25,7 +25,7 @@
def error(err):
- """ Helper to print out errors for us and then halt """
+ """Helper to print out errors for us and then halt"""
print("ERROR: " + err)
avrprog.end()
while True:
diff --git a/examples/avrprog_program_uno328.py b/examples/avrprog_program_uno328.py
index b832300..71e0051 100644
--- a/examples/avrprog_program_uno328.py
+++ b/examples/avrprog_program_uno328.py
@@ -31,7 +31,7 @@
def error(err):
- """ Helper to print out errors for us and then halt """
+ """Helper to print out errors for us and then halt"""
print("ERROR: " + err)
avrprog.end()
while True:
diff --git a/optional_requirements.txt b/optional_requirements.txt
new file mode 100644
index 0000000..d4e27c4
--- /dev/null
+++ b/optional_requirements.txt
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
+#
+# SPDX-License-Identifier: Unlicense
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..61df63a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+[build-system]
+requires = [
+ "setuptools",
+ "wheel",
+ "setuptools-scm",
+]
+
+[project]
+name = "adafruit-circuitpython-avrprog"
+description = "CircuitPython helper library for programming AVR chips."
+version = "0.0.0+auto.0"
+readme = "README.rst"
+authors = [
+ {name = "Adafruit Industries", email = "circuitpython@adafruit.com"}
+]
+urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_AVRprog"}
+keywords = [
+ "adafruit",
+ "avr",
+ "spi",
+ "atmega",
+ "attiny",
+ "hardware",
+ "micropython",
+ "circuitpython",
+]
+license = {text = "MIT"}
+classifiers = [
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Libraries",
+ "Topic :: Software Development :: Embedded Systems",
+ "Topic :: System :: Hardware",
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3",
+]
+dynamic = ["dependencies", "optional-dependencies"]
+
+[tool.setuptools]
+py-modules = ["adafruit_avrprog"]
+
+[tool.setuptools.dynamic]
+dependencies = {file = ["requirements.txt"]}
+optional-dependencies = {optional = {file = ["optional_requirements.txt"]}}
diff --git a/requirements.txt b/requirements.txt
index 17a850d..7a984a4 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
+# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 81f63eb..0000000
--- a/setup.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
-#
-# SPDX-License-Identifier: MIT
-
-"""A setuptools based setup module.
-
-See:
-https://packaging.python.org/en/latest/distributing.html
-https://github.com/pypa/sampleproject
-"""
-
-# Always prefer setuptools over distutils
-from setuptools import setup, find_packages
-
-# To use a consistent encoding
-from codecs import open
-from os import path
-
-here = path.abspath(path.dirname(__file__))
-
-# Get the long description from the README file
-with open(path.join(here, "README.rst"), encoding="utf-8") as f:
- long_description = f.read()
-
-setup(
- name="adafruit-circuitpython-avrprog",
- use_scm_version=True,
- setup_requires=["setuptools_scm"],
- description="CircuitPython helper library for programming AVR chips.",
- long_description=long_description,
- long_description_content_type="text/x-rst",
- # The project's main homepage.
- url="https://github.com/adafruit/Adafruit_CircuitPython_AVRprog",
- # Author details
- author="Adafruit Industries",
- author_email="circuitpython@adafruit.com",
- install_requires=["Adafruit-Blinka"],
- # Choose your license
- license="MIT",
- # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
- classifiers=[
- "Development Status :: 3 - Alpha",
- "Intended Audience :: Developers",
- "Topic :: Software Development :: Libraries",
- "Topic :: System :: Hardware",
- "License :: OSI Approved :: MIT License",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.4",
- "Programming Language :: Python :: 3.5",
- ],
- # What does your project relate to?
- keywords="adafruit avr spi atmega attiny hardware micropython circuitpython",
- # You can just specify the packages manually here if your project is
- # simple. Or you can use find_packages().
- py_modules=["adafruit_avrprog"],
-)