Merge branch 'master' of github.com:jamesbowman/i2cdriver
This commit is contained in:
commit
23cc8a2a96
|
@ -312,6 +312,15 @@ void i2c_scan(I2CDriver *sd, uint8_t devices[128])
|
|||
(void)readFromSerialPort(sd->port, devices + 8, 112);
|
||||
}
|
||||
|
||||
uint8_t i2c_reset(I2CDriver *sd)
|
||||
{
|
||||
charCommand(sd, 'x');
|
||||
uint8_t a[1];
|
||||
if (readFromSerialPort(sd->port, a, 1) != 1)
|
||||
return 0;
|
||||
return a[0];
|
||||
}
|
||||
|
||||
int i2c_start(I2CDriver *sd, uint8_t dev, uint8_t op)
|
||||
{
|
||||
uint8_t start[2] = {'s', (dev << 1) | op};
|
||||
|
@ -382,6 +391,15 @@ int i2c_commands(I2CDriver *sd, int argc, char *argv[])
|
|||
);
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
{
|
||||
uint8_t sda_scl = i2c_reset(sd);
|
||||
printf("Bus reset. SDA = %d, SCL = %d\n",
|
||||
1 & (sda_scl >> 1),
|
||||
1 & sda_scl);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
{
|
||||
uint8_t devices[128];
|
||||
|
@ -468,6 +486,7 @@ int i2c_commands(I2CDriver *sd, int argc, char *argv[])
|
|||
fprintf(stderr, "Commands are:");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " i display status information (uptime, voltage, current, temperature)\n");
|
||||
fprintf(stderr, " x I2C bus reset\n");
|
||||
fprintf(stderr, " d device scan\n");
|
||||
fprintf(stderr, " w dev <bytes> write bytes to I2C device dev\n");
|
||||
fprintf(stderr, " p send a STOP\n");
|
||||
|
|
|
@ -14,7 +14,7 @@ class Dig2:
|
|||
|
||||
def raw(self, b0, b1):
|
||||
""" Set all 8 segments from the bytes b0 and b1 """
|
||||
self.i2.regwr(self.a, 0, b0, b1)
|
||||
self.i2.regwr(self.a, 0, struct.pack("BB", b0, b1))
|
||||
|
||||
def hex(self, b):
|
||||
""" Display a hex number 0-0xff """
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -0,0 +1,55 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'i2cdriver'
|
||||
copyright = '2020, Excamera Labs'
|
||||
author = 'Excamera Labs'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.intersphinx"
|
||||
]
|
||||
|
||||
intersphinx_mapping = {'https://docs.python.org/3/': None}
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# 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,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
|
@ -0,0 +1,4 @@
|
|||
set -e
|
||||
|
||||
# pip install --upgrade --force-reinstall ..
|
||||
make html
|
|
@ -0,0 +1,101 @@
|
|||
.. i2cdriver documentation master file, created by
|
||||
sphinx-quickstart on Thu Jan 16 10:21:28 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Excamera I2CDriver Python API
|
||||
=============================
|
||||
|
||||
|
||||
zxxx
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
xxcc
|
||||
|
||||
Official packages are available on PyPI.
|
||||
|
||||
https://pypi.org/project/i2cdriver/
|
||||
|
||||
|
||||
The main page for I2CDriver includes the complete User Guide:
|
||||
|
||||
https://i2cdriver.com
|
||||
|
||||
|
||||
System Requirements
|
||||
-------------------
|
||||
|
||||
Because it is a pure Python module, ``i2cdriver`` can run on any system supported by ``pyserial``.
|
||||
This includes:
|
||||
|
||||
- Windows 7 or 10
|
||||
- Mac OS
|
||||
- Linux, including all Ubuntu distributions
|
||||
|
||||
Both Python 2.7 and 3.x are supported.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The ``i2cdriver`` package can be installed from PyPI using ``pip``::
|
||||
|
||||
$ pip install i2cdriver
|
||||
|
||||
Quick start
|
||||
-----------
|
||||
|
||||
To connect to the I2CDriver and scan the bus for connected devices::
|
||||
|
||||
>>> import i2cdriver
|
||||
>>> i2c = i2cdriver.I2CDriver("/dev/ttyUSB0")
|
||||
>>> i2c.scan()
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- 1C -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
48 -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
68 -- -- -- -- -- -- --
|
||||
-- -- -- -- -- -- -- --
|
||||
[28, 72, 104]
|
||||
|
||||
To read the temperature in Celsius from a connected LM75 sensor:
|
||||
|
||||
>>> d=i2cdriver.EDS.Temp(i2c)
|
||||
>>> d.read()
|
||||
17.875
|
||||
>>> d.read()
|
||||
18.0
|
||||
|
||||
The User Guide at https://i2cdriver.com has more examples.
|
||||
|
||||
Module Contents
|
||||
---------------
|
||||
|
||||
.. autoclass:: i2cdriver.I2CDriver
|
||||
:member-order: bysource
|
||||
:members:
|
||||
setspeed,
|
||||
setpullups,
|
||||
scan,
|
||||
reset,
|
||||
start,
|
||||
read,
|
||||
write,
|
||||
stop,
|
||||
regwr,
|
||||
regrd,
|
||||
getstatus,
|
||||
monitor
|
||||
|
||||
.. autoclass:: i2cdriver.START
|
||||
.. autoclass:: i2cdriver.STOP
|
||||
.. autoclass:: i2cdriver.BYTE
|
|
@ -4,7 +4,7 @@ import time
|
|||
import struct
|
||||
from collections import OrderedDict
|
||||
|
||||
__version__ = '0.0.5'
|
||||
__version__ = '0.0.8'
|
||||
|
||||
PYTHON2 = (sys.version_info < (3, 0))
|
||||
|
||||
|
@ -35,6 +35,8 @@ class START(_I2CEvent):
|
|||
f.writerow(("START", self.rrw(), str(self.addr), self.rack()))
|
||||
else:
|
||||
assert False, "unsupported format"
|
||||
def __eq__(self, other):
|
||||
return (self.addr, self.rw, self.ack) == (other.addr, other.rw, other.ack)
|
||||
|
||||
class STOP(_I2CEvent):
|
||||
def __repr__(self):
|
||||
|
@ -44,6 +46,8 @@ class STOP(_I2CEvent):
|
|||
f.writerow(("STOP", None, None, None))
|
||||
else:
|
||||
assert False, "unsupported format"
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, STOP)
|
||||
|
||||
class BYTE(_I2CEvent):
|
||||
def __init__(self, b, rw, ack):
|
||||
|
@ -57,6 +61,8 @@ class BYTE(_I2CEvent):
|
|||
f.writerow(("BYTE", self.rrw(), str(self.b), self.rack()))
|
||||
else:
|
||||
assert False, "unsupported format"
|
||||
def __eq__(self, other):
|
||||
return (self.b, self.rw, self.ack) == (other.b, other.rw, other.ack)
|
||||
|
||||
class I2CDriver:
|
||||
"""
|
||||
|
@ -143,6 +149,7 @@ class I2CDriver:
|
|||
assert s in (100, 400)
|
||||
c = {100:b'1', 400:b'4'}[s]
|
||||
self.__ser_w(c)
|
||||
self.speed = s
|
||||
|
||||
def setpullups(self, s):
|
||||
"""
|
||||
|
@ -152,6 +159,7 @@ class I2CDriver:
|
|||
"""
|
||||
assert 0 <= s < 64
|
||||
self.__ser_w([ord('u'), s])
|
||||
self.pullups = s
|
||||
|
||||
def scan(self, silent = False):
|
||||
""" Performs an I2C bus scan.
|
||||
|
@ -259,7 +267,7 @@ class I2CDriver:
|
|||
|
||||
:param dev: 7-bit I2C device address
|
||||
:param reg: register address 0-255
|
||||
:param fmt: :py:func:`struct.unpack` format string for the register contents
|
||||
:param fmt: :py:func:`struct.unpack` format string for the register contents, or an integer byte count
|
||||
|
||||
If device 0x75 has a 16-bit register 102, it can be read with:
|
||||
|
||||
|
@ -268,28 +276,34 @@ class I2CDriver:
|
|||
"""
|
||||
|
||||
if isinstance(fmt, str):
|
||||
n = struct.calcsize(fmt)
|
||||
self.__ser_w(b'r' + struct.pack("BBB", dev, reg, n))
|
||||
r = struct.unpack(fmt, self.ser.read(n))
|
||||
r = struct.unpack(fmt, self.regrd(dev, reg, struct.calcsize(fmt)))
|
||||
if len(r) == 1:
|
||||
return r[0]
|
||||
else:
|
||||
return r
|
||||
else:
|
||||
n = fmt
|
||||
self.__ser_w(b'r' + struct.pack("BBB", dev, reg, n))
|
||||
if n <= 256:
|
||||
self.__ser_w(b'r' + struct.pack("BBB", dev, reg, n & 0xff))
|
||||
return self.ser.read(n)
|
||||
else:
|
||||
self.start(dev, 0)
|
||||
self.write([reg])
|
||||
self.start(dev, 1)
|
||||
r = self.read(n)
|
||||
self.stop()
|
||||
return r
|
||||
|
||||
def regwr(self, dev, reg, *vv):
|
||||
def regwr(self, dev, reg, vv):
|
||||
"""Write a device's register.
|
||||
|
||||
:param dev: 7-bit I2C device address
|
||||
:param reg: register address 0-255
|
||||
:param vv: sequence of values to write
|
||||
:param vv: value to write. Either a single byte, or a sequence
|
||||
|
||||
To set device 0x34 byte register 7 to 0xA1:
|
||||
|
||||
>>> i2c.regwr(0x34, 7, [0xa1])
|
||||
>>> i2c.regwr(0x34, 7, 0xa1)
|
||||
|
||||
If device 0x75 has a big-endian 16-bit register 102 you can set it to 4999 with:
|
||||
|
||||
|
@ -300,6 +314,8 @@ class I2CDriver:
|
|||
if r:
|
||||
r = self.write(struct.pack("B", reg))
|
||||
if r:
|
||||
if isinstance(vv, int):
|
||||
vv = struct.pack("B", vv)
|
||||
r = self.write(vv)
|
||||
self.stop()
|
||||
return r
|
||||
|
|
|
@ -5,6 +5,7 @@ from i2cdriver import I2CDriver, EDS
|
|||
|
||||
if __name__ == '__main__':
|
||||
i2 = I2CDriver(sys.argv[1], True)
|
||||
i2.setspeed(400)
|
||||
|
||||
d = EDS.Remote(i2)
|
||||
|
||||
|
@ -17,3 +18,4 @@ if __name__ == '__main__':
|
|||
if r is not None:
|
||||
(code, timestamp) = r
|
||||
print("Raw code: %02x %02x %02x %02x (time %.2f)" % (code[0], code[1], code[2], code[3], timestamp))
|
||||
time.sleep(.25)
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
from __future__ import print_function, division
|
||||
|
||||
import sys
|
||||
import time
|
||||
import struct
|
||||
import random
|
||||
|
||||
import i2cdriver
|
||||
import unittest
|
||||
|
||||
DUT = "dut" # grn0
|
||||
AGG = "agg" # blk1
|
||||
|
||||
def bit(b, x):
|
||||
return 1 & (x >> b)
|
||||
|
||||
def byte(x):
|
||||
return struct.pack("B", x)
|
||||
|
||||
class TestDUT(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.i2 = i2cdriver.I2CDriver(DUT)
|
||||
self.ag = i2cdriver.I2CDriver(AGG)
|
||||
|
||||
def init(self):
|
||||
self.i2.reboot()
|
||||
self.i2.setspeed(400)
|
||||
self.i2.getstatus()
|
||||
return self.i2
|
||||
|
||||
def lm75_read(self, i, reg):
|
||||
(tr,) = struct.unpack(">h", i.regrd(0x48, reg, 2))
|
||||
return tr
|
||||
|
||||
def lm75_slow_read(self, i, reg):
|
||||
i.start(0x48, 0)
|
||||
i.write(struct.pack("B", reg))
|
||||
i.start(0x48, 1)
|
||||
(tr,) = struct.unpack(">h", i.read(2))
|
||||
i.stop()
|
||||
return tr
|
||||
|
||||
def lm75_write(self, i, reg, v):
|
||||
i.start(0x48, 0)
|
||||
i.write(struct.pack(">Bh", reg, v))
|
||||
i.stop()
|
||||
|
||||
def stack0(self):
|
||||
self.s0 = self.i2.introspect()
|
||||
|
||||
def stacksame(self):
|
||||
s1 = self.i2.introspect()
|
||||
for i in ("ds", "sp"):
|
||||
self.assertEqual(self.s0[i], s1[i])
|
||||
|
||||
def confirm(self):
|
||||
# Basic i2c confirmation
|
||||
self.assertEqual(self.lm75_read(self.i2, 2), 0x4b00)
|
||||
|
||||
def confirm_sampling(self):
|
||||
# Check that analog sampling is happening
|
||||
econvs = {
|
||||
"i2cdriver1" : {0,1,2},
|
||||
"i2cdriverm" : {0}
|
||||
}[self.i2.product]
|
||||
s = set()
|
||||
while len(s) < len(econvs):
|
||||
s.add(self.i2.introspect()["convs"])
|
||||
self.assertEqual(s, econvs)
|
||||
|
||||
def test_temperature(self):
|
||||
# Confirm onboard temperature sensor is reasonable and changing
|
||||
i2 = self.i2
|
||||
i2.getstatus()
|
||||
onboard = i2.temp
|
||||
external = (self.lm75_read(i2, 0) >> 5) * 0.125
|
||||
self.assertTrue(abs(onboard - external) < 10)
|
||||
|
||||
# Wait up to 10 seconds for temperature to change
|
||||
t0 = time.time()
|
||||
while onboard == i2.temp:
|
||||
i2.getstatus()
|
||||
self.assertTrue(time.time() < (t0 + 10))
|
||||
|
||||
def test_coldstart(self):
|
||||
i2 = self.init()
|
||||
s = i2.introspect()
|
||||
self.assertEqual(i2.scl, 1)
|
||||
self.assertEqual(i2.sda, 1)
|
||||
|
||||
def test_scan(self):
|
||||
i2 = self.init()
|
||||
def det(a):
|
||||
r = i2.start(a, 0)
|
||||
i2.stop()
|
||||
return r
|
||||
scan = [det(a) for a in range(128)]
|
||||
e = [(i == 0x48) for i in range(128)]
|
||||
self.assertEqual(scan, e)
|
||||
|
||||
def test_lm75_reg(self):
|
||||
i2 = self.i2
|
||||
self.stack0()
|
||||
vals = (0, -128, 0x7f80)
|
||||
for a in vals:
|
||||
self.lm75_write(i2, 2, a)
|
||||
for b in vals:
|
||||
self.lm75_write(i2, 3, b)
|
||||
self.assertEqual(self.lm75_read(i2, 2), a)
|
||||
self.assertEqual(self.lm75_read(i2, 3), b)
|
||||
self.assertEqual(self.lm75_slow_read(i2, 2), a)
|
||||
self.assertEqual(self.lm75_slow_read(i2, 3), b)
|
||||
self.lm75_write(i2, 2, 0x4b00)
|
||||
self.lm75_write(i2, 3, 0x5000)
|
||||
self.assertEqual(self.lm75_read(i2, 2), 0x4b00)
|
||||
self.assertEqual(self.lm75_read(i2, 3), 0x5000)
|
||||
self.stacksame()
|
||||
|
||||
def test_regrd256(self):
|
||||
i2 = self.i2
|
||||
reg = 3
|
||||
self.lm75_write(i2, reg, 0x7480)
|
||||
for n in (127, 128, 129):
|
||||
self.assertEqual(i2.regrd(0x48, reg, ">" + str(n) + "h"), (0x7480,) * n)
|
||||
|
||||
def test_regwr(self):
|
||||
i2c = self.i2
|
||||
sa0 = 0x48
|
||||
wdata = []
|
||||
lcnt = 16
|
||||
for i in range(lcnt):
|
||||
x = random.randint(0, 255)
|
||||
wdata.append(x)
|
||||
# print(wdata)
|
||||
i2c.regwr(sa0, 0x00, wdata)
|
||||
time.sleep(0.5)
|
||||
x = i2c.regrd(sa0, 0x00, "<16B")
|
||||
# print(x)
|
||||
|
||||
def test_setspeed(self):
|
||||
i2 = self.init()
|
||||
self.stack0()
|
||||
for s in (100, 400, 400, 100, 400):
|
||||
i2.setspeed(s)
|
||||
i2.getstatus()
|
||||
self.assertEqual(i2.speed, s)
|
||||
self.confirm()
|
||||
self.stacksame()
|
||||
|
||||
def test_cap_idle(self):
|
||||
i2 = self.init()
|
||||
c = i2.capture_start()
|
||||
t0 = time.time()
|
||||
d = i2.ser.read(15)
|
||||
t1 = time.time()
|
||||
i2.capture_stop()
|
||||
|
||||
self.assertEqual(d, b'\x00' * 15)
|
||||
self.assertTrue(0.4 < (t1 - t0) < 0.6)
|
||||
|
||||
def test_cap_0(self):
|
||||
def test_0():
|
||||
self.lm75_write(ag, 2, 0x4b00)
|
||||
return [
|
||||
i2cdriver.START(0x48, 0, 1),
|
||||
i2cdriver.BYTE(0x02, 0, True),
|
||||
i2cdriver.BYTE(0x4b, 0, True),
|
||||
i2cdriver.BYTE(0x00, 0, True),
|
||||
i2cdriver.STOP()
|
||||
]
|
||||
def test_1():
|
||||
self.lm75_slow_read(ag, 2)
|
||||
return [
|
||||
i2cdriver.START,
|
||||
(0x90, True),
|
||||
(0x02, True),
|
||||
i2cdriver.START,
|
||||
(0x91, True),
|
||||
(0x4b, True),
|
||||
(0x00, False),
|
||||
i2cdriver.STOP
|
||||
]
|
||||
|
||||
i2 = self.init()
|
||||
ag = self.ag
|
||||
for t in (test_0, ): # test_1):
|
||||
c = i2.capture_start()
|
||||
time.sleep(.1)
|
||||
ee = t()
|
||||
for e,a in zip(ee, c()):
|
||||
self.assertEqual(a, e)
|
||||
i2.capture_stop()
|
||||
|
||||
def test_pullups(self):
|
||||
i2 = self.init()
|
||||
i2.getstatus()
|
||||
self.assertEqual(i2.pullups, 0b100100)
|
||||
|
||||
rr = random.sample(list(range(64)), 64)
|
||||
if i2.product == "i2cdriver1":
|
||||
respins = (0, 1, 3, 13, 14, 16)
|
||||
else:
|
||||
respins = (10, 11, 12, 6, 7, 8)
|
||||
for r in rr:
|
||||
i2.setpullups(r)
|
||||
i2.getstatus()
|
||||
self.assertEqual(i2.pullups, r)
|
||||
s = i2.introspect()
|
||||
p = s["P0"] + (s["P1"] << 8) + (s["P2"] << 16)
|
||||
d = s["P0MDOUT"] + (s["P1MDOUT"] << 8) + (s["P2MDOUT"] << 16)
|
||||
for b,pb in enumerate(respins):
|
||||
self.assertEqual(bit(pb, p), 1)
|
||||
self.assertEqual(bit(b, r), bit(pb, d))
|
||||
|
||||
def test_zz5s(self):
|
||||
i2 = self.init()
|
||||
time.sleep(5)
|
||||
i2.getstatus()
|
||||
self.assertTrue(i2.uptime in (4,5,6))
|
||||
|
||||
def checkmode(self, c):
|
||||
self.i2.getstatus()
|
||||
self.assertEqual(self.i2.mode, c)
|
||||
|
||||
def test_bitbang(self):
|
||||
i2 = self.init()
|
||||
self.checkmode('I')
|
||||
self.stack0()
|
||||
i2.ser.write(b'b')
|
||||
for i in range(1000): # Square wave for a while
|
||||
i2.ser.write(byte(0b1111) + byte(0b0101))
|
||||
i2.ser.write(byte(0b1010) + byte(0b11010)) # Float, request a byte
|
||||
self.assertEqual(struct.unpack("B", i2.ser.read(1)), (3, )) # both should be high
|
||||
i2.ser.write(byte(0b0101)) # Leave driven low
|
||||
i2.ser.write(b'@')
|
||||
self.checkmode('B')
|
||||
i2.restore()
|
||||
self.checkmode('I')
|
||||
self.stacksame()
|
||||
self.assertEqual(self.lm75_read(i2, 2), 0x4b00)
|
||||
|
||||
def test_bitbang_idem(self):
|
||||
# Confirm bitbang mode idempotence
|
||||
i2 = self.init()
|
||||
if i2.product != "spidriver1":
|
||||
return
|
||||
for n in [0b1101, 0b1011, 0b0000, 0b1111] + list(range(16)):
|
||||
i2.ser.write(b'b' + byte(n) + byte(0x40))
|
||||
s1 = i2.introspect()
|
||||
self.assertEqual(bit(0, n), bit(2, s1["P0MDOUT"]))
|
||||
self.assertEqual(bit(1, n), bit(2, s1["P0"]))
|
||||
self.assertEqual(bit(2, n), bit(4, s1["P1MDOUT"]))
|
||||
self.assertEqual(bit(3, n), bit(4, s1["P1"]))
|
||||
i2.ser.write(b'b' + byte(0x40))
|
||||
s2 = i2.introspect()
|
||||
for i in ("P0", "P1", "P0MDOUT", "P1MDOUT"):
|
||||
self.assertEqual(s1[i], s2[i])
|
||||
self.assertEqual(i2.introspect()["SMB0CF"], 0x00)
|
||||
i2.restore()
|
||||
self.assertEqual(i2.introspect()["SMB0CF"], 0xd8)
|
||||
|
||||
def test_bitbang_bidir(self):
|
||||
self.stack0()
|
||||
dd = (self.i2, self.ag)
|
||||
[i2.ser.write(b'b') for i2 in dd]
|
||||
LOW = 0b01
|
||||
HIGH = 0b11
|
||||
INPUT = 0b10
|
||||
def port(d, sda, scl, read = False):
|
||||
d.ser.write(byte(sda | (scl << 2) | (int(read) << 4)))
|
||||
if read:
|
||||
(r,) = struct.unpack("B", d.ser.read(1))
|
||||
return (r & 1, (r >> 1) & 1)
|
||||
|
||||
for sda in (LOW, HIGH, LOW):
|
||||
for scl in (HIGH, LOW, HIGH):
|
||||
expected = (int(sda == HIGH), int(scl == HIGH))
|
||||
|
||||
for (tx,rx) in [(0,1), (1,0)]:
|
||||
port(dd[tx], sda, scl)
|
||||
port(dd[rx], INPUT, INPUT)
|
||||
self.assertEqual(expected, port(dd[rx], INPUT, INPUT, True))
|
||||
|
||||
[i2.ser.write(b'@') for i2 in dd]
|
||||
[i2.restore() for i2 in dd]
|
||||
self.stacksame()
|
||||
|
||||
def test_reset(self):
|
||||
i2 = self.init()
|
||||
self.stack0()
|
||||
i2.reset()
|
||||
self.stacksame()
|
||||
for i in range(100):
|
||||
i2.reset()
|
||||
self.stacksame()
|
||||
self.confirm()
|
||||
|
||||
def test_sampling(self):
|
||||
self.confirm_sampling()
|
||||
|
||||
def test_weigh(self):
|
||||
# Confirm resistance measurement
|
||||
i2 = self.init()
|
||||
ag = self.ag
|
||||
self.stack0()
|
||||
|
||||
def sample(p, pv):
|
||||
i2.setpullups(p)
|
||||
i2.ser.write(b'v' + byte(pv))
|
||||
while True:
|
||||
i2.ser.write(b'w')
|
||||
r = struct.unpack("B", i2.ser.read(1))
|
||||
if r[0] == 0:
|
||||
break
|
||||
return struct.unpack("2B", i2.ser.read(2))
|
||||
|
||||
def estimate(a, hi, res):
|
||||
if a == 0:
|
||||
return 0
|
||||
v = a / hi
|
||||
return (res / v) - res
|
||||
def mean(s):
|
||||
return sum(s) / len(s)
|
||||
def resistance(rr):
|
||||
if rr == []:
|
||||
return 0
|
||||
return 1 / sum([1/r for r in rr])
|
||||
def pullups():
|
||||
sHH = sample(0b111111, 0b111111)
|
||||
sAA = sample(0b001001, 0b110110)
|
||||
sBB = sample(0b010010, 0b101101)
|
||||
sCC = sample(0b100100, 0b011011)
|
||||
sda_r = mean((estimate(sAA[0], sHH[0], 2200),
|
||||
estimate(sBB[0], sHH[0], 4300),
|
||||
estimate(sCC[0], sHH[0], 4700)))
|
||||
scl_r = mean((estimate(sAA[1], sHH[1], 2200),
|
||||
estimate(sBB[1], sHH[1], 4300),
|
||||
estimate(sCC[1], sHH[1], 4700)))
|
||||
return (sda_r, scl_r)
|
||||
|
||||
for x in range(64):
|
||||
# print(x)
|
||||
ag.setpullups(x)
|
||||
esda = resistance([r for i,r in enumerate([2200, 4300, 4700]) if bit(i, x)])
|
||||
escl = resistance([r for i,r in enumerate([2200, 4300, 4700]) if bit(3 + i, x)])
|
||||
# print("expected %d" % esda, escl)
|
||||
sda,scl = pullups()
|
||||
# print("SDA pullup %d, SCL pullup %d" % (sda,scl))
|
||||
|
||||
def close(e, a):
|
||||
margin = max(100, e / 10)
|
||||
return abs(a - e) < margin
|
||||
self.assertTrue(close(esda, sda))
|
||||
self.assertTrue(close(escl, scl))
|
||||
self.confirm_sampling()
|
||||
|
||||
self.init() # restore pullups
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue