mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
@@ -11,6 +11,7 @@ digraph classes {
|
||||
MCP3xxx;
|
||||
MCP30xx;
|
||||
MCP32xx;
|
||||
MCP3xx2;
|
||||
MCP33xx;
|
||||
|
||||
/* Concrete classes */
|
||||
@@ -21,6 +22,8 @@ digraph classes {
|
||||
MCP30xx->MCP3xxx;
|
||||
MCP32xx->MCP3xxx;
|
||||
MCP33xx->MCP3xxx;
|
||||
MCP3xx2->MCP3xxx;
|
||||
|
||||
MCP3001->MCP30xx;
|
||||
MCP3002->MCP30xx;
|
||||
MCP3004->MCP30xx;
|
||||
@@ -29,6 +32,8 @@ digraph classes {
|
||||
MCP3202->MCP32xx;
|
||||
MCP3204->MCP32xx;
|
||||
MCP3208->MCP32xx;
|
||||
MCP3002->MCP3xx2;
|
||||
MCP3202->MCP3xx2;
|
||||
MCP3301->MCP33xx;
|
||||
MCP3302->MCP33xx;
|
||||
MCP3304->MCP33xx;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 46 KiB |
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.36.0 (20140111.2315)
|
||||
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
|
||||
-->
|
||||
<!-- Title: classes Pages: 1 -->
|
||||
<svg width="870pt" height="404pt"
|
||||
@@ -11,38 +11,38 @@
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-400 866,-400 866,4 -4,4"/>
|
||||
<!-- Device -->
|
||||
<g id="node1" class="node"><title>Device</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="498,-396 444,-396 444,-360 498,-360 498,-396"/>
|
||||
<text text-anchor="middle" x="471" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="418,-396 364,-396 364,-360 418,-360 418,-396"/>
|
||||
<text text-anchor="middle" x="391" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||
</g>
|
||||
<!-- SPIDevice -->
|
||||
<g id="node2" class="node"><title>SPIDevice</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="503,-324 439,-324 439,-288 503,-288 503,-324"/>
|
||||
<text text-anchor="middle" x="471" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">SPIDevice</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="423,-324 359,-324 359,-288 423,-288 423,-324"/>
|
||||
<text text-anchor="middle" x="391" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">SPIDevice</text>
|
||||
</g>
|
||||
<!-- SPIDevice->Device -->
|
||||
<g id="edge1" class="edge"><title>SPIDevice->Device</title>
|
||||
<path fill="none" stroke="black" d="M471,-324.303C471,-332.017 471,-341.288 471,-349.888"/>
|
||||
<polygon fill="black" stroke="black" points="467.5,-349.896 471,-359.896 474.5,-349.896 467.5,-349.896"/>
|
||||
<path fill="none" stroke="black" d="M391,-324.303C391,-332.017 391,-341.288 391,-349.888"/>
|
||||
<polygon fill="black" stroke="black" points="387.5,-349.896 391,-359.896 394.5,-349.896 387.5,-349.896"/>
|
||||
</g>
|
||||
<!-- AnalogInputDevice -->
|
||||
<g id="node3" class="node"><title>AnalogInputDevice</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="524,-252 418,-252 418,-216 524,-216 524,-252"/>
|
||||
<text text-anchor="middle" x="471" y="-231.5" font-family="Sans" font-size="10.00" fill="#000000">AnalogInputDevice</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="444,-252 338,-252 338,-216 444,-216 444,-252"/>
|
||||
<text text-anchor="middle" x="391" y="-231.5" font-family="Sans" font-size="10.00" fill="#000000">AnalogInputDevice</text>
|
||||
</g>
|
||||
<!-- AnalogInputDevice->SPIDevice -->
|
||||
<g id="edge2" class="edge"><title>AnalogInputDevice->SPIDevice</title>
|
||||
<path fill="none" stroke="black" d="M471,-252.303C471,-260.017 471,-269.288 471,-277.888"/>
|
||||
<polygon fill="black" stroke="black" points="467.5,-277.896 471,-287.896 474.5,-277.896 467.5,-277.896"/>
|
||||
<path fill="none" stroke="black" d="M391,-252.303C391,-260.017 391,-269.288 391,-277.888"/>
|
||||
<polygon fill="black" stroke="black" points="387.5,-277.896 391,-287.896 394.5,-277.896 387.5,-277.896"/>
|
||||
</g>
|
||||
<!-- MCP3xxx -->
|
||||
<g id="node4" class="node"><title>MCP3xxx</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="500.5,-180 441.5,-180 441.5,-144 500.5,-144 500.5,-180"/>
|
||||
<text text-anchor="middle" x="471" y="-159.5" font-family="Sans" font-size="10.00" fill="#000000">MCP3xxx</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="420.5,-180 361.5,-180 361.5,-144 420.5,-144 420.5,-180"/>
|
||||
<text text-anchor="middle" x="391" y="-159.5" font-family="Sans" font-size="10.00" fill="#000000">MCP3xxx</text>
|
||||
</g>
|
||||
<!-- MCP3xxx->AnalogInputDevice -->
|
||||
<g id="edge3" class="edge"><title>MCP3xxx->AnalogInputDevice</title>
|
||||
<path fill="none" stroke="black" d="M471,-180.303C471,-188.017 471,-197.288 471,-205.888"/>
|
||||
<polygon fill="black" stroke="black" points="467.5,-205.896 471,-215.896 474.5,-205.896 467.5,-205.896"/>
|
||||
<path fill="none" stroke="black" d="M391,-180.303C391,-188.017 391,-197.288 391,-205.888"/>
|
||||
<polygon fill="black" stroke="black" points="387.5,-205.896 391,-215.896 394.5,-205.896 387.5,-205.896"/>
|
||||
</g>
|
||||
<!-- MCP30xx -->
|
||||
<g id="node5" class="node"><title>MCP30xx</title>
|
||||
@@ -51,138 +51,158 @@
|
||||
</g>
|
||||
<!-- MCP30xx->MCP3xxx -->
|
||||
<g id="edge4" class="edge"><title>MCP30xx->MCP3xxx</title>
|
||||
<path fill="none" stroke="black" d="M220.7,-98.425C271.447,-111.112 374.849,-136.962 431.579,-151.145"/>
|
||||
<polygon fill="black" stroke="black" points="431.055,-154.621 441.605,-153.651 432.752,-147.83 431.055,-154.621"/>
|
||||
<path fill="none" stroke="black" d="M221.376,-101.631C256.184,-113.814 313.335,-133.817 351.556,-147.195"/>
|
||||
<polygon fill="black" stroke="black" points="350.725,-150.612 361.319,-150.612 353.037,-144.005 350.725,-150.612"/>
|
||||
</g>
|
||||
<!-- MCP32xx -->
|
||||
<g id="node6" class="node"><title>MCP32xx</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="501,-108 441,-108 441,-72 501,-72 501,-108"/>
|
||||
<text text-anchor="middle" x="471" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">MCP32xx</text>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="461,-108 401,-108 401,-72 461,-72 461,-108"/>
|
||||
<text text-anchor="middle" x="431" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">MCP32xx</text>
|
||||
</g>
|
||||
<!-- MCP32xx->MCP3xxx -->
|
||||
<g id="edge5" class="edge"><title>MCP32xx->MCP3xxx</title>
|
||||
<path fill="none" stroke="black" d="M471,-108.303C471,-116.017 471,-125.288 471,-133.888"/>
|
||||
<polygon fill="black" stroke="black" points="467.5,-133.896 471,-143.896 474.5,-133.896 467.5,-133.896"/>
|
||||
<path fill="none" stroke="black" d="M421.112,-108.303C416.511,-116.356 410.94,-126.106 405.847,-135.018"/>
|
||||
<polygon fill="black" stroke="black" points="402.696,-133.477 400.774,-143.896 408.774,-136.95 402.696,-133.477"/>
|
||||
</g>
|
||||
<!-- MCP3xx2 -->
|
||||
<g id="node7" class="node"><title>MCP3xx2</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="381,-108 321,-108 321,-72 381,-72 381,-108"/>
|
||||
<text text-anchor="middle" x="351" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">MCP3xx2</text>
|
||||
</g>
|
||||
<!-- MCP3xx2->MCP3xxx -->
|
||||
<g id="edge7" class="edge"><title>MCP3xx2->MCP3xxx</title>
|
||||
<path fill="none" stroke="black" d="M360.888,-108.303C365.489,-116.356 371.06,-126.106 376.153,-135.018"/>
|
||||
<polygon fill="black" stroke="black" points="373.226,-136.95 381.226,-143.896 379.304,-133.477 373.226,-136.95"/>
|
||||
</g>
|
||||
<!-- MCP33xx -->
|
||||
<g id="node7" class="node"><title>MCP33xx</title>
|
||||
<g id="node8" class="node"><title>MCP33xx</title>
|
||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="741,-108 681,-108 681,-72 741,-72 741,-108"/>
|
||||
<text text-anchor="middle" x="711" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">MCP33xx</text>
|
||||
</g>
|
||||
<!-- MCP33xx->MCP3xxx -->
|
||||
<g id="edge6" class="edge"><title>MCP33xx->MCP3xxx</title>
|
||||
<path fill="none" stroke="black" d="M681.089,-99.724C637.918,-112.316 558.088,-135.599 510.235,-149.556"/>
|
||||
<polygon fill="black" stroke="black" points="509.154,-146.226 500.534,-152.386 511.114,-152.946 509.154,-146.226"/>
|
||||
<path fill="none" stroke="black" d="M680.895,-97.5854C622.855,-110.282 495.498,-138.141 430.644,-152.328"/>
|
||||
<polygon fill="black" stroke="black" points="429.813,-148.927 420.792,-154.483 431.309,-155.765 429.813,-148.927"/>
|
||||
</g>
|
||||
<!-- MCP3001 -->
|
||||
<g id="node8" class="node"><title>MCP3001</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="62,-36 3.55271e-15,-36 3.55271e-15,-0 62,-0 62,-36"/>
|
||||
<text text-anchor="middle" x="31" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3001</text>
|
||||
<g id="node9" class="node"><title>MCP3001</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="142,-36 80,-36 80,-0 142,-0 142,-36"/>
|
||||
<text text-anchor="middle" x="111" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3001</text>
|
||||
</g>
|
||||
<!-- MCP3001->MCP30xx -->
|
||||
<g id="edge7" class="edge"><title>MCP3001->MCP30xx</title>
|
||||
<path fill="none" stroke="black" d="M61.8496,-32.4967C87.5187,-43.7269 124.21,-59.7795 151.929,-71.9066"/>
|
||||
<polygon fill="black" stroke="black" points="150.762,-75.2164 161.327,-76.018 153.568,-68.8032 150.762,-75.2164"/>
|
||||
</g>
|
||||
<!-- MCP3002 -->
|
||||
<g id="node9" class="node"><title>MCP3002</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="142,-36 80,-36 80,-0 142,-0 142,-36"/>
|
||||
<text text-anchor="middle" x="111" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3002</text>
|
||||
</g>
|
||||
<!-- MCP3002->MCP30xx -->
|
||||
<g id="edge8" class="edge"><title>MCP3002->MCP30xx</title>
|
||||
<g id="edge8" class="edge"><title>MCP3001->MCP30xx</title>
|
||||
<path fill="none" stroke="black" d="M130.775,-36.3034C140.754,-45.0345 153.011,-55.7595 163.857,-65.2497"/>
|
||||
<polygon fill="black" stroke="black" points="161.622,-67.9446 171.452,-71.8957 166.231,-62.6766 161.622,-67.9446"/>
|
||||
</g>
|
||||
<!-- MCP3002 -->
|
||||
<g id="node10" class="node"><title>MCP3002</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="302,-36 240,-36 240,-0 302,-0 302,-36"/>
|
||||
<text text-anchor="middle" x="271" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3002</text>
|
||||
</g>
|
||||
<!-- MCP3002->MCP30xx -->
|
||||
<g id="edge9" class="edge"><title>MCP3002->MCP30xx</title>
|
||||
<path fill="none" stroke="black" d="M251.225,-36.3034C241.246,-45.0345 228.989,-55.7595 218.143,-65.2497"/>
|
||||
<polygon fill="black" stroke="black" points="215.769,-62.6766 210.548,-71.8957 220.378,-67.9446 215.769,-62.6766"/>
|
||||
</g>
|
||||
<!-- MCP3002->MCP3xx2 -->
|
||||
<g id="edge16" class="edge"><title>MCP3002->MCP3xx2</title>
|
||||
<path fill="none" stroke="black" d="M290.775,-36.3034C300.754,-45.0345 313.011,-55.7595 323.857,-65.2497"/>
|
||||
<polygon fill="black" stroke="black" points="321.622,-67.9446 331.452,-71.8957 326.231,-62.6766 321.622,-67.9446"/>
|
||||
</g>
|
||||
<!-- MCP3004 -->
|
||||
<g id="node10" class="node"><title>MCP3004</title>
|
||||
<g id="node11" class="node"><title>MCP3004</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="222,-36 160,-36 160,-0 222,-0 222,-36"/>
|
||||
<text text-anchor="middle" x="191" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3004</text>
|
||||
</g>
|
||||
<!-- MCP3004->MCP30xx -->
|
||||
<g id="edge9" class="edge"><title>MCP3004->MCP30xx</title>
|
||||
<g id="edge10" class="edge"><title>MCP3004->MCP30xx</title>
|
||||
<path fill="none" stroke="black" d="M191,-36.3034C191,-44.0173 191,-53.2875 191,-61.8876"/>
|
||||
<polygon fill="black" stroke="black" points="187.5,-61.8956 191,-71.8957 194.5,-61.8957 187.5,-61.8956"/>
|
||||
</g>
|
||||
<!-- MCP3008 -->
|
||||
<g id="node11" class="node"><title>MCP3008</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="302,-36 240,-36 240,-0 302,-0 302,-36"/>
|
||||
<text text-anchor="middle" x="271" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3008</text>
|
||||
<g id="node12" class="node"><title>MCP3008</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="62,-36 3.55271e-15,-36 3.55271e-15,-0 62,-0 62,-36"/>
|
||||
<text text-anchor="middle" x="31" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3008</text>
|
||||
</g>
|
||||
<!-- MCP3008->MCP30xx -->
|
||||
<g id="edge10" class="edge"><title>MCP3008->MCP30xx</title>
|
||||
<path fill="none" stroke="black" d="M251.225,-36.3034C241.246,-45.0345 228.989,-55.7595 218.143,-65.2497"/>
|
||||
<polygon fill="black" stroke="black" points="215.769,-62.6766 210.548,-71.8957 220.378,-67.9446 215.769,-62.6766"/>
|
||||
<g id="edge11" class="edge"><title>MCP3008->MCP30xx</title>
|
||||
<path fill="none" stroke="black" d="M62.2294,-32.6629C87.7738,-43.8385 124.046,-59.7076 151.587,-71.757"/>
|
||||
<polygon fill="black" stroke="black" points="150.369,-75.044 160.933,-75.8457 153.174,-68.6309 150.369,-75.044"/>
|
||||
</g>
|
||||
<!-- MCP3201 -->
|
||||
<g id="node12" class="node"><title>MCP3201</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="382,-36 320,-36 320,-0 382,-0 382,-36"/>
|
||||
<text text-anchor="middle" x="351" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3201</text>
|
||||
<g id="node13" class="node"><title>MCP3201</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="542,-36 480,-36 480,-0 542,-0 542,-36"/>
|
||||
<text text-anchor="middle" x="511" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3201</text>
|
||||
</g>
|
||||
<!-- MCP3201->MCP32xx -->
|
||||
<g id="edge11" class="edge"><title>MCP3201->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M380.355,-36.1239C396.045,-45.2764 415.557,-56.6583 432.454,-66.515"/>
|
||||
<polygon fill="black" stroke="black" points="431.13,-69.7947 441.532,-71.8102 434.658,-63.7483 431.13,-69.7947"/>
|
||||
<g id="edge12" class="edge"><title>MCP3201->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M491.225,-36.3034C481.246,-45.0345 468.989,-55.7595 458.143,-65.2497"/>
|
||||
<polygon fill="black" stroke="black" points="455.769,-62.6766 450.548,-71.8957 460.378,-67.9446 455.769,-62.6766"/>
|
||||
</g>
|
||||
<!-- MCP3202 -->
|
||||
<g id="node13" class="node"><title>MCP3202</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="462,-36 400,-36 400,-0 462,-0 462,-36"/>
|
||||
<text text-anchor="middle" x="431" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3202</text>
|
||||
<g id="node14" class="node"><title>MCP3202</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="382,-36 320,-36 320,-0 382,-0 382,-36"/>
|
||||
<text text-anchor="middle" x="351" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3202</text>
|
||||
</g>
|
||||
<!-- MCP3202->MCP32xx -->
|
||||
<g id="edge12" class="edge"><title>MCP3202->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M440.888,-36.3034C445.489,-44.3564 451.06,-54.1055 456.153,-63.0176"/>
|
||||
<polygon fill="black" stroke="black" points="453.226,-64.9497 461.226,-71.8957 459.304,-61.4767 453.226,-64.9497"/>
|
||||
<g id="edge13" class="edge"><title>MCP3202->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M370.775,-36.3034C380.754,-45.0345 393.011,-55.7595 403.857,-65.2497"/>
|
||||
<polygon fill="black" stroke="black" points="401.622,-67.9446 411.452,-71.8957 406.231,-62.6766 401.622,-67.9446"/>
|
||||
</g>
|
||||
<!-- MCP3202->MCP3xx2 -->
|
||||
<g id="edge17" class="edge"><title>MCP3202->MCP3xx2</title>
|
||||
<path fill="none" stroke="black" d="M351,-36.3034C351,-44.0173 351,-53.2875 351,-61.8876"/>
|
||||
<polygon fill="black" stroke="black" points="347.5,-61.8956 351,-71.8957 354.5,-61.8957 347.5,-61.8956"/>
|
||||
</g>
|
||||
<!-- MCP3204 -->
|
||||
<g id="node14" class="node"><title>MCP3204</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="542,-36 480,-36 480,-0 542,-0 542,-36"/>
|
||||
<text text-anchor="middle" x="511" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3204</text>
|
||||
<g id="node15" class="node"><title>MCP3204</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="622,-36 560,-36 560,-0 622,-0 622,-36"/>
|
||||
<text text-anchor="middle" x="591" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3204</text>
|
||||
</g>
|
||||
<!-- MCP3204->MCP32xx -->
|
||||
<g id="edge13" class="edge"><title>MCP3204->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M501.112,-36.3034C496.511,-44.3564 490.94,-54.1055 485.847,-63.0176"/>
|
||||
<polygon fill="black" stroke="black" points="482.696,-61.4767 480.774,-71.8957 488.774,-64.9497 482.696,-61.4767"/>
|
||||
<g id="edge14" class="edge"><title>MCP3204->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M559.771,-32.6629C534.226,-43.8385 497.954,-59.7076 470.413,-71.757"/>
|
||||
<polygon fill="black" stroke="black" points="468.826,-68.6309 461.067,-75.8457 471.631,-75.044 468.826,-68.6309"/>
|
||||
</g>
|
||||
<!-- MCP3208 -->
|
||||
<g id="node15" class="node"><title>MCP3208</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="622,-36 560,-36 560,-0 622,-0 622,-36"/>
|
||||
<text text-anchor="middle" x="591" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3208</text>
|
||||
<g id="node16" class="node"><title>MCP3208</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="462,-36 400,-36 400,-0 462,-0 462,-36"/>
|
||||
<text text-anchor="middle" x="431" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3208</text>
|
||||
</g>
|
||||
<!-- MCP3208->MCP32xx -->
|
||||
<g id="edge14" class="edge"><title>MCP3208->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M561.645,-36.1239C545.955,-45.2764 526.443,-56.6583 509.546,-66.515"/>
|
||||
<polygon fill="black" stroke="black" points="507.342,-63.7483 500.468,-71.8102 510.87,-69.7947 507.342,-63.7483"/>
|
||||
<g id="edge15" class="edge"><title>MCP3208->MCP32xx</title>
|
||||
<path fill="none" stroke="black" d="M431,-36.3034C431,-44.0173 431,-53.2875 431,-61.8876"/>
|
||||
<polygon fill="black" stroke="black" points="427.5,-61.8956 431,-71.8957 434.5,-61.8957 427.5,-61.8956"/>
|
||||
</g>
|
||||
<!-- MCP3301 -->
|
||||
<g id="node16" class="node"><title>MCP3301</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="702,-36 640,-36 640,-0 702,-0 702,-36"/>
|
||||
<text text-anchor="middle" x="671" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3301</text>
|
||||
<g id="node17" class="node"><title>MCP3301</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="782,-36 720,-36 720,-0 782,-0 782,-36"/>
|
||||
<text text-anchor="middle" x="751" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3301</text>
|
||||
</g>
|
||||
<!-- MCP3301->MCP33xx -->
|
||||
<g id="edge15" class="edge"><title>MCP3301->MCP33xx</title>
|
||||
<path fill="none" stroke="black" d="M680.888,-36.3034C685.489,-44.3564 691.06,-54.1055 696.153,-63.0176"/>
|
||||
<polygon fill="black" stroke="black" points="693.226,-64.9497 701.226,-71.8957 699.304,-61.4767 693.226,-64.9497"/>
|
||||
</g>
|
||||
<!-- MCP3302 -->
|
||||
<g id="node17" class="node"><title>MCP3302</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="782,-36 720,-36 720,-0 782,-0 782,-36"/>
|
||||
<text text-anchor="middle" x="751" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3302</text>
|
||||
</g>
|
||||
<!-- MCP3302->MCP33xx -->
|
||||
<g id="edge16" class="edge"><title>MCP3302->MCP33xx</title>
|
||||
<g id="edge18" class="edge"><title>MCP3301->MCP33xx</title>
|
||||
<path fill="none" stroke="black" d="M741.112,-36.3034C736.511,-44.3564 730.94,-54.1055 725.847,-63.0176"/>
|
||||
<polygon fill="black" stroke="black" points="722.696,-61.4767 720.774,-71.8957 728.774,-64.9497 722.696,-61.4767"/>
|
||||
</g>
|
||||
<!-- MCP3304 -->
|
||||
<g id="node18" class="node"><title>MCP3304</title>
|
||||
<!-- MCP3302 -->
|
||||
<g id="node18" class="node"><title>MCP3302</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="862,-36 800,-36 800,-0 862,-0 862,-36"/>
|
||||
<text text-anchor="middle" x="831" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3304</text>
|
||||
<text text-anchor="middle" x="831" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3302</text>
|
||||
</g>
|
||||
<!-- MCP3304->MCP33xx -->
|
||||
<g id="edge17" class="edge"><title>MCP3304->MCP33xx</title>
|
||||
<!-- MCP3302->MCP33xx -->
|
||||
<g id="edge19" class="edge"><title>MCP3302->MCP33xx</title>
|
||||
<path fill="none" stroke="black" d="M801.645,-36.1239C785.955,-45.2764 766.443,-56.6583 749.546,-66.515"/>
|
||||
<polygon fill="black" stroke="black" points="747.342,-63.7483 740.468,-71.8102 750.87,-69.7947 747.342,-63.7483"/>
|
||||
</g>
|
||||
<!-- MCP3304 -->
|
||||
<g id="node19" class="node"><title>MCP3304</title>
|
||||
<polygon fill="#2980b9" stroke="#2980b9" points="702,-36 640,-36 640,-0 702,-0 702,-36"/>
|
||||
<text text-anchor="middle" x="671" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3304</text>
|
||||
</g>
|
||||
<!-- MCP3304->MCP33xx -->
|
||||
<g id="edge20" class="edge"><title>MCP3304->MCP33xx</title>
|
||||
<path fill="none" stroke="black" d="M680.888,-36.3034C685.489,-44.3564 691.06,-54.1055 696.153,-63.0176"/>
|
||||
<polygon fill="black" stroke="black" points="693.226,-64.9497 701.226,-71.8957 699.304,-61.4767 693.226,-64.9497"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
@@ -49,6 +49,9 @@ class SPIError(GPIOZeroError):
|
||||
class SPIBadArgs(SPIError, ValueError):
|
||||
"Error raised when invalid arguments are given while constructing :class:`SPIDevice`"
|
||||
|
||||
class SPIBadChannel(SPIError, ValueError):
|
||||
"Error raised when an invalid channel is given to an :class:`AnalogInputDevice`"
|
||||
|
||||
class GPIODeviceError(GPIOZeroError):
|
||||
"Base class for errors specific to the GPIODevice hierarchy"
|
||||
|
||||
|
||||
@@ -54,7 +54,8 @@ class MockPin(Pin):
|
||||
self._when_changed = None
|
||||
self.clear_states()
|
||||
return self
|
||||
if old_pin.__class__ != cls:
|
||||
# Ensure the pin class expected supports PWM (or not)
|
||||
if issubclass(cls, MockPWMPin) != isinstance(old_pin, MockPWMPin):
|
||||
raise ValueError('pin %d is already in use as a %s' % (number, old_pin.__class__.__name__))
|
||||
return old_pin
|
||||
|
||||
@@ -249,7 +250,6 @@ class MockPWMPin(MockPin):
|
||||
"""
|
||||
This derivative of :class:`MockPin` adds PWM support.
|
||||
"""
|
||||
|
||||
def __init__(self, number):
|
||||
super(MockPWMPin, self).__init__()
|
||||
self._frequency = None
|
||||
@@ -275,3 +275,141 @@ class MockPWMPin(MockPin):
|
||||
if value is None:
|
||||
self._change_state(0.0)
|
||||
|
||||
|
||||
class MockSPIClockPin(MockPin):
|
||||
"""
|
||||
This derivative of :class:`MockPin` is intended to be used as the clock pin
|
||||
of a mock SPI device. It is not intended for direct construction in tests;
|
||||
rather, construct a :class:`MockSPIDevice` with various pin numbers, and
|
||||
this class will be used for the clock pin.
|
||||
"""
|
||||
def __init__(self, number):
|
||||
super(MockSPIClockPin, self).__init__()
|
||||
if not hasattr(self, 'spi_devices'):
|
||||
self.spi_devices = []
|
||||
|
||||
def _set_state(self, value):
|
||||
super(MockSPIClockPin, self)._set_state(value)
|
||||
for dev in self.spi_devices:
|
||||
dev.on_clock()
|
||||
|
||||
|
||||
class MockSPISelectPin(MockPin):
|
||||
"""
|
||||
This derivative of :class:`MockPin` is intended to be used as the select
|
||||
pin of a mock SPI device. It is not intended for direct construction in
|
||||
tests; rather, construct a :class:`MockSPIDevice` with various pin numbers,
|
||||
and this class will be used for the select pin.
|
||||
"""
|
||||
def __init__(self, number):
|
||||
super(MockSPISelectPin, self).__init__()
|
||||
if not hasattr(self, 'spi_device'):
|
||||
self.spi_device = None
|
||||
|
||||
def _set_state(self, value):
|
||||
super(MockSPISelectPin, self)._set_state(value)
|
||||
if self.spi_device:
|
||||
self.spi_device.on_select()
|
||||
|
||||
|
||||
class MockSPIDevice(object):
|
||||
def __init__(
|
||||
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
|
||||
clock_polarity=False, clock_phase=False, lsb_first=False,
|
||||
bits_per_word=8, select_high=False):
|
||||
self.clock_pin = MockSPIClockPin(clock_pin)
|
||||
self.mosi_pin = None if mosi_pin is None else MockPin(mosi_pin)
|
||||
self.miso_pin = None if miso_pin is None else MockPin(miso_pin)
|
||||
self.select_pin = None if select_pin is None else MockSPISelectPin(select_pin)
|
||||
self.clock_polarity = clock_polarity
|
||||
self.clock_phase = clock_phase
|
||||
self.lsb_first = lsb_first
|
||||
self.bits_per_word = bits_per_word
|
||||
self.select_high = select_high
|
||||
self.rx_bit = 0
|
||||
self.rx_buf = []
|
||||
self.tx_buf = []
|
||||
self.clock_pin.spi_devices.append(self)
|
||||
self.select_pin.spi_device = self
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
if self in self.clock_pin.spi_devices:
|
||||
self.clock_pin.spi_devices.remove(self)
|
||||
if self.select_pin is not None:
|
||||
self.select_pin.spi_device = None
|
||||
|
||||
def on_select(self):
|
||||
if self.select_pin.state == self.select_high:
|
||||
self.on_start()
|
||||
|
||||
def on_clock(self):
|
||||
# Don't do anything if this SPI device isn't currently selected
|
||||
if self.select_pin is None or self.select_pin.state == self.select_high:
|
||||
# The XOR of the clock pin's values, polarity and phase indicates
|
||||
# whether we're meant to be acting on this edge
|
||||
if self.clock_pin.state ^ self.clock_polarity ^ self.clock_phase:
|
||||
self.rx_bit += 1
|
||||
if self.mosi_pin is not None:
|
||||
self.rx_buf.append(self.mosi_pin.state)
|
||||
if self.miso_pin is not None:
|
||||
try:
|
||||
tx_value = self.tx_buf.pop(0)
|
||||
except IndexError:
|
||||
tx_value = 0
|
||||
if tx_value:
|
||||
self.miso_pin.drive_high()
|
||||
else:
|
||||
self.miso_pin.drive_low()
|
||||
self.on_bit()
|
||||
|
||||
def on_start(self):
|
||||
"""
|
||||
Override this in descendents to detect when the mock SPI device's
|
||||
select line is activated.
|
||||
"""
|
||||
self.rx_bit = 0
|
||||
self.rx_buf = []
|
||||
self.tx_buf = []
|
||||
|
||||
def on_bit(self):
|
||||
"""
|
||||
Override this in descendents to react to receiving a bit.
|
||||
|
||||
The :attr:`rx_bit` attribute gives the index of the bit received (this
|
||||
is reset to 0 by default by :meth:`on_select`). The :attr:`rx_buf`
|
||||
sequence gives the sequence of 1s and 0s that have been recevied so
|
||||
far. The :attr:`tx_buf` sequence gives the sequence of 1s and 0s to
|
||||
transmit on the next clock pulses. All these attributes can be modified
|
||||
within this method.
|
||||
|
||||
The :meth:`rx_word` and :meth:`tx_word` methods can also be used to
|
||||
read and append to the buffers using integers instead of bool bits.
|
||||
"""
|
||||
pass
|
||||
|
||||
def rx_word(self):
|
||||
result = 0
|
||||
bits = reversed(self.rx_buf) if self.lsb_first else self.rx_buf
|
||||
for bit in bits:
|
||||
result <<= 1
|
||||
result |= bit
|
||||
return result
|
||||
|
||||
def tx_word(self, value, bits_per_word=None):
|
||||
if bits_per_word is None:
|
||||
bits_per_word = self.bits_per_word
|
||||
bits = [0] * bits_per_word
|
||||
for bit in range(bits_per_word):
|
||||
bits[bit] = value & 1
|
||||
value >>= 1
|
||||
assert not value
|
||||
if not self.lsb_first:
|
||||
bits = reversed(bits)
|
||||
self.tx_buf.extend(bits)
|
||||
|
||||
|
||||
@@ -85,16 +85,16 @@ class SPIHardwareInterface(Device):
|
||||
self._device.mode = value
|
||||
|
||||
def _get_clock_polarity(self):
|
||||
return bool(self.mode & 2)
|
||||
return bool(self.clock_mode & 2)
|
||||
|
||||
def _set_clock_polarity(self, value):
|
||||
self.mode = self.mode & (~2) | (bool(value) << 1)
|
||||
self.clock_mode = self.clock_mode & (~2) | (bool(value) << 1)
|
||||
|
||||
def _get_clock_phase(self):
|
||||
return bool(self.mode & 1)
|
||||
return bool(self.clock_mode & 1)
|
||||
|
||||
def _set_clock_phase(self, value):
|
||||
self.mode = self.mode & (~1) | bool(value)
|
||||
self.clock_mode = self.clock_mode & (~1) | bool(value)
|
||||
|
||||
def _get_lsb_first(self):
|
||||
return self._device.lsbfirst
|
||||
@@ -130,9 +130,6 @@ class SPISoftwareBus(SharedMixin, Device):
|
||||
self.miso = None
|
||||
super(SPISoftwareBus, self).__init__()
|
||||
self.lock = RLock()
|
||||
self.clock_phase = False
|
||||
self.lsb_first = False
|
||||
self.bits_per_word = 8
|
||||
try:
|
||||
self.clock = OutputDevice(clock_pin, active_high=True)
|
||||
if mosi_pin is not None:
|
||||
@@ -166,13 +163,7 @@ class SPISoftwareBus(SharedMixin, Device):
|
||||
def _shared_key(cls, clock_pin, mosi_pin, miso_pin):
|
||||
return (clock_pin, mosi_pin, miso_pin)
|
||||
|
||||
def read(self, n):
|
||||
return self.transfer((0,) * n)
|
||||
|
||||
def write(self, data):
|
||||
return len(self.transfer(data))
|
||||
|
||||
def transfer(self, data):
|
||||
def transfer(self, data, clock_phase=False, lsb_first=False, bits_per_word=8):
|
||||
"""
|
||||
Writes data (a list of integer words where each word is assumed to have
|
||||
:attr:`bits_per_word` bits or less) to the SPI interface, and reads an
|
||||
@@ -180,19 +171,19 @@ class SPISoftwareBus(SharedMixin, Device):
|
||||
"""
|
||||
result = []
|
||||
with self.lock:
|
||||
shift = operator.lshift if self.lsb_first else operator.rshift
|
||||
shift = operator.lshift if lsb_first else operator.rshift
|
||||
for write_word in data:
|
||||
mask = 1 if self.lsb_first else 1 << (self.bits_per_word - 1)
|
||||
mask = 1 if lsb_first else 1 << (bits_per_word - 1)
|
||||
read_word = 0
|
||||
for _ in range(self.bits_per_word):
|
||||
for _ in range(bits_per_word):
|
||||
if self.mosi is not None:
|
||||
self.mosi.value = bool(write_word & mask)
|
||||
self.clock.on()
|
||||
if self.miso is not None and not self.clock_phase:
|
||||
if self.miso is not None and not clock_phase:
|
||||
if self.miso.value:
|
||||
read_word |= mask
|
||||
self.clock.off()
|
||||
if self.miso is not None and self.clock_phase:
|
||||
if self.miso is not None and clock_phase:
|
||||
if self.miso.value:
|
||||
read_word |= mask
|
||||
mask = shift(mask, 1)
|
||||
@@ -205,6 +196,9 @@ class SPISoftwareInterface(OutputDevice):
|
||||
self._bus = None
|
||||
super(SPISoftwareInterface, self).__init__(select_pin, active_high=False)
|
||||
try:
|
||||
self._clock_phase = False
|
||||
self._lsb_first = False
|
||||
self._bits_per_word = 8
|
||||
self._bus = SPISoftwareBus(clock_pin, mosi_pin, miso_pin)
|
||||
except:
|
||||
self.close()
|
||||
@@ -230,16 +224,17 @@ class SPISoftwareInterface(OutputDevice):
|
||||
return "software SPI closed"
|
||||
|
||||
def read(self, n):
|
||||
return self._bus.read(n)
|
||||
return self.transfer((0,) * n)
|
||||
|
||||
def write(self, data):
|
||||
return self._bus.write(data)
|
||||
return len(self.transfer(data))
|
||||
|
||||
def transfer(self, data):
|
||||
with self._bus.lock:
|
||||
self.on()
|
||||
try:
|
||||
return self._bus.transfer(data)
|
||||
return self._bus.transfer(
|
||||
data, self._clock_phase, self._lsb_first, self._bits_per_word)
|
||||
finally:
|
||||
self.off()
|
||||
|
||||
@@ -250,40 +245,37 @@ class SPISoftwareInterface(OutputDevice):
|
||||
value = int(value)
|
||||
if not 0 <= value <= 3:
|
||||
raise ValueError('clock_mode must be a value between 0 and 3 inclusive')
|
||||
with self._bus.lock:
|
||||
self._bus.clock.active_high = not (value & 2)
|
||||
self._bus.clock.off()
|
||||
self._bus.clock_phase = bool(value & 1)
|
||||
self.clock_polarity = bool(value & 2)
|
||||
self.clock_phase = bool(value & 1)
|
||||
|
||||
def _get_clock_polarity(self):
|
||||
return not self._bus.clock.active_high
|
||||
with self._bus.lock:
|
||||
return not self._bus.clock.active_high
|
||||
|
||||
def _set_clock_polarity(self, value):
|
||||
with self._bus.lock:
|
||||
self._bus.clock.active_high = not value
|
||||
self._bus.clock.off()
|
||||
|
||||
def _get_clock_phase(self):
|
||||
return self._bus.clock_phase
|
||||
return self._clock_phase
|
||||
|
||||
def _set_clock_phase(self, value):
|
||||
with self._bus.lock:
|
||||
self._bus.clock_phase = bool(value)
|
||||
self._clock_phase = bool(value)
|
||||
|
||||
def _get_lsb_first(self):
|
||||
return self._bus.lsb_first
|
||||
return self._lsb_first
|
||||
|
||||
def _set_lsb_first(self, value):
|
||||
with self._bus.lock:
|
||||
self._bus.lsb_first = bool(value)
|
||||
self._lsb_first = bool(value)
|
||||
|
||||
def _get_bits_per_word(self):
|
||||
return self._bus.bits_per_word
|
||||
return self._bits_per_word
|
||||
|
||||
def _set_bits_per_word(self, value):
|
||||
if value < 1:
|
||||
raise ValueError('bits_per_word must be positive')
|
||||
with self._bus.lock:
|
||||
self._bus.bits_per_word = int(value)
|
||||
self._bits_per_word = int(value)
|
||||
|
||||
def _get_select_high(self):
|
||||
return self.active_high
|
||||
|
||||
@@ -7,7 +7,14 @@ from __future__ import (
|
||||
str = type('')
|
||||
|
||||
|
||||
from .exc import DeviceClosed, InputDeviceError
|
||||
from math import log, ceil
|
||||
from operator import or_
|
||||
try:
|
||||
from functools import reduce
|
||||
except ImportError:
|
||||
pass # py2's reduce is built-in
|
||||
|
||||
from .exc import DeviceClosed, SPIBadChannel
|
||||
from .devices import Device
|
||||
from .spi import SPI
|
||||
|
||||
@@ -34,6 +41,38 @@ class SPIDevice(Device):
|
||||
def closed(self):
|
||||
return self._spi is None
|
||||
|
||||
def _int_to_words(self, pattern):
|
||||
"""
|
||||
Given a bit-pattern expressed an integer number, return a sequence of
|
||||
the individual words that make up the pattern. The number of bits per
|
||||
word will be obtained from the internal SPI interface.
|
||||
"""
|
||||
try:
|
||||
bits_required = int(ceil(log(pattern, 2))) + 1
|
||||
except ValueError:
|
||||
# pattern == 0 (technically speaking, no bits are required to
|
||||
# transmit the value zero ;)
|
||||
bits_required = 1
|
||||
shifts = range(0, bits_required, self._spi.bits_per_word)[::-1]
|
||||
mask = 2 ** self._spi.bits_per_word - 1
|
||||
return [(pattern >> shift) & mask for shift in shifts]
|
||||
|
||||
def _words_to_int(self, words, expected_bits=None):
|
||||
"""
|
||||
Given a sequence of words which each fit in the internal SPI
|
||||
interface's number of bits per word, returns the value obtained by
|
||||
concatenating each word into a single bit-string.
|
||||
|
||||
If *expected_bits* is specified, it limits the size of the output to
|
||||
the specified number of bits (by masking off bits above the expected
|
||||
number). If unspecified, no limit will be applied.
|
||||
"""
|
||||
if expected_bits is None:
|
||||
expected_bits = len(words) * self._spi.bits_per_word
|
||||
shifts = range(0, expected_bits, self._spi.bits_per_word)[::-1]
|
||||
mask = 2 ** expected_bits - 1
|
||||
return reduce(or_, (word << shift for word, shift in zip(words, shifts))) & mask
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
self._check_open()
|
||||
@@ -72,10 +111,10 @@ class AnalogInputDevice(SPIDevice):
|
||||
.. _analog to digital converters: https://en.wikipedia.org/wiki/Analog-to-digital_converter
|
||||
"""
|
||||
|
||||
def __init__(self, bits=None, **spi_args):
|
||||
if bits is None:
|
||||
raise InputDeviceError('you must specify the bit resolution of the device')
|
||||
def __init__(self, bits, **spi_args):
|
||||
self._bits = bits
|
||||
self._min_value = -(2 ** bits)
|
||||
self._range = 2 ** (bits + 1) - 1
|
||||
super(AnalogInputDevice, self).__init__(shared=True, **spi_args)
|
||||
|
||||
@property
|
||||
@@ -92,9 +131,9 @@ class AnalogInputDevice(SPIDevice):
|
||||
def value(self):
|
||||
"""
|
||||
The current value read from the device, scaled to a value between 0 and
|
||||
1.
|
||||
1 (or -1 to +1 for certain devices operating in differential mode).
|
||||
"""
|
||||
return self._read() / (2**self.bits - 1)
|
||||
return (2 * (self._read() - self._min_value) / self._range) - 1
|
||||
|
||||
@property
|
||||
def raw_value(self):
|
||||
@@ -128,10 +167,9 @@ class MCP3xxx(AnalogInputDevice):
|
||||
@property
|
||||
def differential(self):
|
||||
"""
|
||||
If ``True``, the device is operated in pseudo-differential mode. In
|
||||
this mode one channel (specified by the channel attribute) is read
|
||||
relative to the value of a second channel (implied by the chip's
|
||||
design).
|
||||
If ``True``, the device is operated in differential mode. In this mode
|
||||
one channel (specified by the channel attribute) is read relative to
|
||||
the value of a second channel (implied by the chip's design).
|
||||
|
||||
Please refer to the device data-sheet to determine which channel is
|
||||
used as the relative base value (for example, when using an
|
||||
@@ -141,28 +179,73 @@ class MCP3xxx(AnalogInputDevice):
|
||||
return self._differential
|
||||
|
||||
def _read(self):
|
||||
# MCP3008/04 or MCP3208/04 protocol looks like the following:
|
||||
return self._words_to_int(
|
||||
self._spi.transfer(self._send())[-2:], self.bits
|
||||
)
|
||||
|
||||
def _send(self):
|
||||
# MCP3004/08 protocol looks like the following:
|
||||
#
|
||||
# Byte 0 1 2
|
||||
# ==== ======== ======== ========
|
||||
# Tx 0001MCCC xxxxxxxx xxxxxxxx
|
||||
# Rx xxxxxxxx x0RRRRRR RRRRxxxx for the 3004/08
|
||||
# Rx xxxxxxxx x0RRRRRR RRRRRRxx for the 3204/08
|
||||
# Tx 00000001 MCCCxxxx xxxxxxxx
|
||||
# Rx xxxxxxxx xxxxx0RR RRRRRRRR
|
||||
#
|
||||
# The transmit bits start with 3 preamble bits "000" (to warm up), a
|
||||
# start bit "1" followed by the single/differential bit (M) which is 1
|
||||
# for single-ended read, and 0 for differential read, followed by
|
||||
# 3-bits for the channel (C). The remainder of the transmission are
|
||||
# "don't care" bits (x).
|
||||
# MCP3204/08 protocol looks like the following:
|
||||
#
|
||||
# The first byte received and the top 1 bit of the second byte are
|
||||
# don't care bits (x). These are followed by a null bit (0), and then
|
||||
# the result bits (R). 10 bits for the MCP300x, 12 bits for the
|
||||
# MCP320x.
|
||||
# Byte 0 1 2
|
||||
# ==== ======== ======== ========
|
||||
# Tx 000001MC CCxxxxxx xxxxxxxx
|
||||
# Rx xxxxxxxx xxx0RRRR RRRRRRRR
|
||||
#
|
||||
# XXX Differential mode still requires testing
|
||||
data = self._spi.transfer([16 + [8, 0][self.differential] + self.channel, 0, 0])
|
||||
return ((data[1] & 63) << (self.bits - 6)) | (data[2] >> (14 - self.bits))
|
||||
# The transmit bits start with several preamble "0" bits, the number
|
||||
# of which is determined by the amount required to align the last byte
|
||||
# of the result with the final byte of output. A start "1" bit is then
|
||||
# transmitted, followed by the single/differential bit (M); 1 for
|
||||
# single-ended read, 0 for differential read. Next comes three bits for
|
||||
# channel (C).
|
||||
#
|
||||
# Read-out begins with a don't care bit (x), then a null bit (0)
|
||||
# followed by the result bits (R). All other bits are don't care (x).
|
||||
#
|
||||
# The 3x01 variant of the chips always operates in differential mode
|
||||
# and effectively only has one channel (composed of an IN+ and IN-). As
|
||||
# such it requires no input, just output.
|
||||
return self._int_to_words(
|
||||
(0b10000 | (not self.differential) << 3 | self.channel) << (self.bits + 2)
|
||||
)
|
||||
|
||||
|
||||
class MCP3xx2(MCP3xxx):
|
||||
def _send(self):
|
||||
# MCP3002 protocol looks like the following:
|
||||
#
|
||||
# Byte 0 1
|
||||
# ==== ======== ========
|
||||
# Tx 01MCLxxx xxxxxxxx
|
||||
# Rx xxxxx0RR RRRRRRRR for the 3002
|
||||
#
|
||||
# MCP3202 protocol looks like the following:
|
||||
#
|
||||
# Byte 0 1 2
|
||||
# ==== ======== ======== ========
|
||||
# Tx 00000001 MCLxxxxx xxxxxxxx
|
||||
# Rx xxxxxxxx xxx0RRRR RRRRRRRR
|
||||
#
|
||||
# The transmit bits start with several preamble "0" bits, the number of
|
||||
# which is determined by the amount required to align the last byte of
|
||||
# the result with the final byte of output. A start "1" bit is then
|
||||
# transmitted, followed by the single/differential bit (M); 1 for
|
||||
# single-ended read, 0 for differential read. Next comes a single bit
|
||||
# for channel (M) then the MSBF bit (L) which selects whether the data
|
||||
# will be read out in MSB form only (1) or whether LSB read-out will
|
||||
# occur after MSB read-out (0).
|
||||
#
|
||||
# Read-out begins with a null bit (0) followed by the result bits (R).
|
||||
# All other bits are don't care (x).
|
||||
return self._int_to_words(
|
||||
(0b1001 | (not self.differential) << 2 | self.channel << 1) << (self.bits + 1)
|
||||
)
|
||||
|
||||
|
||||
class MCP30xx(MCP3xxx):
|
||||
@@ -196,20 +279,32 @@ class MCP33xx(MCP3xxx):
|
||||
super(MCP33xx, self).__init__(channel, 12, differential, **spi_args)
|
||||
|
||||
def _read(self):
|
||||
# MCP3304/02 protocol looks like the following:
|
||||
if self.differential:
|
||||
result = self._words_to_int(
|
||||
self._spi.transfer(self._send())[-2:], self.bits + 1)
|
||||
# Account for the sign bit
|
||||
if result > 4095:
|
||||
return -(8192 - result)
|
||||
else:
|
||||
return result
|
||||
else:
|
||||
return super(MCP33xx, self)._read()
|
||||
|
||||
def _send(self):
|
||||
# MCP3302/04 protocol looks like the following:
|
||||
#
|
||||
# Byte 0 1 2
|
||||
# ==== ======== ======== ========
|
||||
# Tx 0001MCCC xxxxxxxx xxxxxxxx
|
||||
# Rx xxxxxxxx x0SRRRRR RRRRRRRx
|
||||
# Tx 00001MCC Cxxxxxxx xxxxxxxx
|
||||
# Rx xxxxxxxx xx0SRRRR RRRRRRRR
|
||||
#
|
||||
# The transmit bits start with 3 preamble bits "000" (to warm up), a
|
||||
# start bit "1" followed by the single/differential bit (M) which is 1
|
||||
# for single-ended read, and 0 for differential read, followed by
|
||||
# 3-bits for the channel (C). The remainder of the transmission are
|
||||
# "don't care" bits (x).
|
||||
# The transmit bits start with 4 preamble bits "0000", a start bit "1"
|
||||
# followed by the single/differential bit (M) which is 1 for
|
||||
# single-ended read, and 0 for differential read, followed by 3-bits
|
||||
# for the channel (C). The remainder of the transmission are "don't
|
||||
# care" bits (x).
|
||||
#
|
||||
# The first byte received and the top 1 bit of the second byte are
|
||||
# The first byte received and the top 2 bits of the second byte are
|
||||
# don't care bits (x). These are followed by a null bit (0), then the
|
||||
# sign bit (S), and then the 12 result bits (R).
|
||||
#
|
||||
@@ -217,22 +312,11 @@ class MCP33xx(MCP3xxx):
|
||||
# result is effectively 12-bits. In differential mode, the sign bit is
|
||||
# significant and the result is a two's-complement 13-bit value.
|
||||
#
|
||||
# The MCP3301 variant of the chip always operates in differential
|
||||
# mode and effectively only has one channel (composed of an IN+ and
|
||||
# IN-). As such it requires no input, just output. This is the reason
|
||||
# we split out _send() below; so that MCP3301 can override it.
|
||||
data = self._spi.transfer(self._send())
|
||||
# Extract the last two bytes (again, for MCP3301)
|
||||
data = data[-2:]
|
||||
result = ((data[0] & 63) << 7) | (data[1] >> 1)
|
||||
# Account for the sign bit
|
||||
if self.differential and result > 4095:
|
||||
result = -(8192 - result)
|
||||
assert -4096 <= result < 4096
|
||||
return result
|
||||
|
||||
def _send(self):
|
||||
return [16 + [8, 0][self.differential] + self.channel, 0, 0]
|
||||
# The MCP3301 variant operates similarly to the other MCP3x01 variants;
|
||||
# no input, just output and always differential.
|
||||
return self._int_to_words(
|
||||
(0b10000 | (not self.differential) << 3 | self.channel) << (self.bits + 3)
|
||||
)
|
||||
|
||||
@property
|
||||
def differential(self):
|
||||
@@ -259,15 +343,25 @@ class MCP33xx(MCP3xxx):
|
||||
|
||||
class MCP3001(MCP30xx):
|
||||
"""
|
||||
The `MCP3001`_ is a 10-bit analog to digital converter with 1 channel
|
||||
The `MCP3001`_ is a 10-bit analog to digital converter with 1 channel.
|
||||
Please note that the MCP3001 always operates in differential mode,
|
||||
measuring the value of IN+ relative to IN-.
|
||||
|
||||
.. _MCP3001: http://www.farnell.com/datasheets/630400.pdf
|
||||
"""
|
||||
def __init__(self, **spi_args):
|
||||
super(MCP3001, self).__init__(0, differential=True, **spi_args)
|
||||
|
||||
def _read(self):
|
||||
# MCP3001 protocol looks like the following:
|
||||
#
|
||||
# Byte 0 1
|
||||
# ==== ======== ========
|
||||
# Rx xx0RRRRR RRRRRxxx
|
||||
return self._words_to_int(self._spi.read(2), 13) >> 3
|
||||
|
||||
class MCP3002(MCP30xx):
|
||||
|
||||
class MCP3002(MCP30xx, MCP3xx2):
|
||||
"""
|
||||
The `MCP3002`_ is a 10-bit analog to digital converter with 2 channels
|
||||
(0-1).
|
||||
@@ -276,7 +370,7 @@ class MCP3002(MCP30xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 2:
|
||||
raise InputDeviceError('channel must be 0 or 1')
|
||||
raise SPIBadChannel('channel must be 0 or 1')
|
||||
super(MCP3002, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
@@ -289,7 +383,7 @@ class MCP3004(MCP30xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 4:
|
||||
raise InputDeviceError('channel must be between 0 and 3')
|
||||
raise SPIBadChannel('channel must be between 0 and 3')
|
||||
super(MCP3004, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
@@ -302,21 +396,31 @@ class MCP3008(MCP30xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 8:
|
||||
raise InputDeviceError('channel must be between 0 and 7')
|
||||
raise SPIBadChannel('channel must be between 0 and 7')
|
||||
super(MCP3008, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
class MCP3201(MCP32xx):
|
||||
"""
|
||||
The `MCP3201`_ is a 12-bit analog to digital converter with 1 channel
|
||||
The `MCP3201`_ is a 12-bit analog to digital converter with 1 channel.
|
||||
Please note that the MCP3201 always operates in differential mode,
|
||||
measuring the value of IN+ relative to IN-.
|
||||
|
||||
.. _MCP3201: http://www.farnell.com/datasheets/1669366.pdf
|
||||
"""
|
||||
def __init__(self, **spi_args):
|
||||
super(MCP3201, self).__init__(0, differential=True, **spi_args)
|
||||
|
||||
def _read(self):
|
||||
# MCP3201 protocol looks like the following:
|
||||
#
|
||||
# Byte 0 1
|
||||
# ==== ======== ========
|
||||
# Rx xx0RRRRR RRRRRRRx
|
||||
return self._words_to_int(self._spi.read(2), 13) >> 1
|
||||
|
||||
class MCP3202(MCP32xx):
|
||||
|
||||
class MCP3202(MCP32xx, MCP3xx2):
|
||||
"""
|
||||
The `MCP3202`_ is a 12-bit analog to digital converter with 2 channels
|
||||
(0-1).
|
||||
@@ -325,7 +429,7 @@ class MCP3202(MCP32xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 2:
|
||||
raise InputDeviceError('channel must be 0 or 1')
|
||||
raise SPIBadChannel('channel must be 0 or 1')
|
||||
super(MCP3202, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
@@ -338,7 +442,7 @@ class MCP3204(MCP32xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 4:
|
||||
raise InputDeviceError('channel must be between 0 and 3')
|
||||
raise SPIBadChannel('channel must be between 0 and 3')
|
||||
super(MCP3204, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
@@ -351,23 +455,33 @@ class MCP3208(MCP32xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 8:
|
||||
raise InputDeviceError('channel must be between 0 and 7')
|
||||
raise SPIBadChannel('channel must be between 0 and 7')
|
||||
super(MCP3208, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
class MCP3301(MCP33xx):
|
||||
"""
|
||||
The `MCP3301`_ is a signed 13-bit analog to digital converter. Please note
|
||||
that the MCP3301 always operates in differential mode between its two
|
||||
channels and the output value is scaled from -1 to +1.
|
||||
that the MCP3301 always operates in differential mode measuring the
|
||||
difference between IN+ and IN-. Its output value is scaled from -1 to +1.
|
||||
|
||||
.. _MCP3301: http://www.farnell.com/datasheets/1669397.pdf
|
||||
"""
|
||||
def __init__(self, **spi_args):
|
||||
super(MCP3301, self).__init__(0, differential=True, **spi_args)
|
||||
|
||||
def _send(self):
|
||||
return [0, 0]
|
||||
def _read(self):
|
||||
# MCP3301 protocol looks like the following:
|
||||
#
|
||||
# Byte 0 1
|
||||
# ==== ======== ========
|
||||
# Rx xx0SRRRR RRRRRRRR
|
||||
result = self._words_to_int(self._spi.read(2), 13)
|
||||
# Account for the sign bit
|
||||
if result > 4095:
|
||||
return -(8192 - result)
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
class MCP3302(MCP33xx):
|
||||
@@ -382,7 +496,7 @@ class MCP3302(MCP33xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 4:
|
||||
raise InputDeviceError('channel must be between 0 and 4')
|
||||
raise SPIBadChannel('channel must be between 0 and 4')
|
||||
super(MCP3302, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
@@ -398,6 +512,6 @@ class MCP3304(MCP33xx):
|
||||
"""
|
||||
def __init__(self, channel=0, differential=False, **spi_args):
|
||||
if not 0 <= channel < 8:
|
||||
raise InputDeviceError('channel must be between 0 and 7')
|
||||
raise SPIBadChannel('channel must be between 0 and 7')
|
||||
super(MCP3304, self).__init__(channel, differential, **spi_args)
|
||||
|
||||
|
||||
170
tests/test_spi.py
Normal file
170
tests/test_spi.py
Normal file
@@ -0,0 +1,170 @@
|
||||
from __future__ import (
|
||||
unicode_literals,
|
||||
absolute_import,
|
||||
print_function,
|
||||
division,
|
||||
)
|
||||
str = type('')
|
||||
|
||||
|
||||
import sys
|
||||
import mock
|
||||
import pytest
|
||||
from collections import namedtuple
|
||||
|
||||
from gpiozero import *
|
||||
from gpiozero.pins.mock import MockPin, MockSPIDevice
|
||||
from gpiozero.spi import *
|
||||
|
||||
|
||||
def setup_function(function):
|
||||
import gpiozero.devices
|
||||
gpiozero.devices.pin_factory = MockPin
|
||||
|
||||
def teardown_function(function):
|
||||
MockPin.clear_pins()
|
||||
|
||||
|
||||
def test_spi_hardware_params():
|
||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
||||
with SPI() as device:
|
||||
assert isinstance(device, SPIHardwareInterface)
|
||||
with SPI(port=0, device=0) as device:
|
||||
assert isinstance(device, SPIHardwareInterface)
|
||||
with SPI(port=0, device=1) as device:
|
||||
assert isinstance(device, SPIHardwareInterface)
|
||||
with SPI(clock_pin=11) as device:
|
||||
assert isinstance(device, SPIHardwareInterface)
|
||||
with SPI(clock_pin=11, mosi_pin=10, select_pin=8) as device:
|
||||
assert isinstance(device, SPIHardwareInterface)
|
||||
with SPI(clock_pin=11, mosi_pin=10, select_pin=7) as device:
|
||||
assert isinstance(device, SPIHardwareInterface)
|
||||
with SPI(shared=True) as device:
|
||||
assert isinstance(device, SharedSPIHardwareInterface)
|
||||
with pytest.raises(ValueError):
|
||||
SPI(port=1)
|
||||
with pytest.raises(ValueError):
|
||||
SPI(device=2)
|
||||
with pytest.raises(ValueError):
|
||||
SPI(port=0, clock_pin=12)
|
||||
with pytest.raises(ValueError):
|
||||
SPI(foo='bar')
|
||||
|
||||
def test_spi_software_params():
|
||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
||||
with SPI(select_pin=6) as device:
|
||||
assert isinstance(device, SPISoftwareInterface)
|
||||
with SPI(clock_pin=11, mosi_pin=9, miso_pin=10) as device:
|
||||
assert isinstance(device, SPISoftwareInterface)
|
||||
with SPI(select_pin=6, shared=True) as device:
|
||||
assert isinstance(device, SharedSPISoftwareInterface)
|
||||
# Ensure software fallback works when SpiDev isn't present
|
||||
with SPI() as device:
|
||||
assert isinstance(device, SPISoftwareInterface)
|
||||
|
||||
def test_spi_hardware_conflict():
|
||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
||||
with LED(11) as led:
|
||||
with pytest.raises(GPIOPinInUse):
|
||||
SPI(port=0, device=0)
|
||||
|
||||
def test_spi_hardware_read():
|
||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
||||
spidev.return_value.xfer2.side_effect = lambda data: list(range(10))[:len(data)]
|
||||
with SPI() as device:
|
||||
assert device.read(3) == [0, 1, 2]
|
||||
assert device.read(6) == list(range(6))
|
||||
|
||||
def test_spi_hardware_write():
|
||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
||||
spidev.return_value.xfer2.side_effect = lambda data: list(range(10))[:len(data)]
|
||||
with SPI() as device:
|
||||
assert device.write([0, 1, 2]) == 3
|
||||
assert spidev.return_value.xfer2.called_with([0, 1, 2])
|
||||
assert device.write(list(range(6))) == 6
|
||||
assert spidev.return_value.xfer2.called_with(list(range(6)))
|
||||
|
||||
def test_spi_hardware_modes():
|
||||
with mock.patch('gpiozero.spi.SpiDev') as spidev:
|
||||
spidev.return_value.mode = 0
|
||||
spidev.return_value.lsbfirst = False
|
||||
spidev.return_value.cshigh = True
|
||||
spidev.return_value.bits_per_word = 8
|
||||
with SPI() as device:
|
||||
assert device.clock_mode == 0
|
||||
assert not device.clock_polarity
|
||||
assert not device.clock_phase
|
||||
device.clock_polarity = False
|
||||
assert device.clock_mode == 0
|
||||
device.clock_polarity = True
|
||||
assert device.clock_mode == 2
|
||||
device.clock_phase = True
|
||||
assert device.clock_mode == 3
|
||||
assert not device.lsb_first
|
||||
assert device.select_high
|
||||
assert device.bits_per_word == 8
|
||||
device.select_high = False
|
||||
device.lsb_first = True
|
||||
device.bits_per_word = 12
|
||||
assert not spidev.return_value.cshigh
|
||||
assert spidev.return_value.lsbfirst
|
||||
assert spidev.return_value.bits_per_word == 12
|
||||
|
||||
def test_spi_software_read():
|
||||
class SPISlave(MockSPIDevice):
|
||||
def on_start(self):
|
||||
super(SPISlave, self).on_start()
|
||||
for i in range(10):
|
||||
self.tx_word(i)
|
||||
with SPISlave(11, 10, 9, 8) as slave, SPI() as master:
|
||||
assert master.read(3) == [0, 1, 2]
|
||||
assert master.read(6) == [0, 1, 2, 3, 4, 5]
|
||||
slave.clock_phase = True
|
||||
master.clock_phase = True
|
||||
assert master.read(3) == [0, 1, 2]
|
||||
assert master.read(6) == [0, 1, 2, 3, 4, 5]
|
||||
|
||||
def test_spi_software_write():
|
||||
with MockSPIDevice(11, 10, 9, 8) as test_device, SPI() as master:
|
||||
master.write([0])
|
||||
assert test_device.rx_word() == 0
|
||||
master.write([2, 0])
|
||||
assert test_device.rx_word() == 512
|
||||
master.write([0, 1, 1])
|
||||
assert test_device.rx_word() == 257
|
||||
|
||||
def test_spi_software_clock_mode():
|
||||
with SPI() as master:
|
||||
assert master.clock_mode == 0
|
||||
assert not master.clock_polarity
|
||||
assert not master.clock_phase
|
||||
master.clock_polarity = False
|
||||
assert master.clock_mode == 0
|
||||
master.clock_polarity = True
|
||||
assert master.clock_mode == 2
|
||||
master.clock_phase = True
|
||||
assert master.clock_mode == 3
|
||||
master.clock_mode = 0
|
||||
assert not master.clock_polarity
|
||||
assert not master.clock_phase
|
||||
with pytest.raises(ValueError):
|
||||
master.clock_mode = 5
|
||||
|
||||
def test_spi_software_attr():
|
||||
with SPI() as master:
|
||||
assert not master.lsb_first
|
||||
assert not master.select_high
|
||||
assert master.bits_per_word == 8
|
||||
master.bits_per_word = 12
|
||||
assert master.bits_per_word == 12
|
||||
master.lsb_first = True
|
||||
assert master.lsb_first
|
||||
master.select_high = True
|
||||
assert master.select_high
|
||||
with pytest.raises(ValueError):
|
||||
master.bits_per_word = 0
|
||||
|
||||
|
||||
# XXX Test two simultaneous SPI devices sharing clock, MOSI, and MISO, with
|
||||
# separate select pins (including threaded tests which attempt simultaneous
|
||||
# reading/writing)
|
||||
339
tests/test_spi_devices.py
Normal file
339
tests/test_spi_devices.py
Normal file
@@ -0,0 +1,339 @@
|
||||
from __future__ import (
|
||||
unicode_literals,
|
||||
absolute_import,
|
||||
print_function,
|
||||
division,
|
||||
)
|
||||
str = type('')
|
||||
|
||||
|
||||
import sys
|
||||
import pytest
|
||||
from collections import namedtuple
|
||||
try:
|
||||
from math import isclose
|
||||
except ImportError:
|
||||
from gpiozero.compat import isclose
|
||||
|
||||
from gpiozero import *
|
||||
from gpiozero.pins.mock import MockSPIDevice, MockPin
|
||||
|
||||
|
||||
def setup_function(function):
|
||||
import gpiozero.devices
|
||||
gpiozero.devices.pin_factory = MockPin
|
||||
|
||||
def teardown_function(function):
|
||||
MockPin.clear_pins()
|
||||
|
||||
def clamp(v, min_value, max_value):
|
||||
return min(max_value, max(min_value, v))
|
||||
|
||||
def scale(v, ref, bits):
|
||||
v /= ref
|
||||
vmin = -(2 ** bits)
|
||||
vmax = -vmin - 1
|
||||
vrange = vmax - vmin
|
||||
return int(((v + 1) / 2.0) * vrange + vmin)
|
||||
|
||||
|
||||
class MockMCP3xxx(MockSPIDevice):
|
||||
def __init__(
|
||||
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
|
||||
channels=8, bits=10):
|
||||
super(MockMCP3xxx, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin)
|
||||
self.vref = 3.3
|
||||
self.channels = [0.0] * channels
|
||||
self.channel_bits = 3
|
||||
self.bits = bits
|
||||
self.state = 'idle'
|
||||
|
||||
def on_start(self):
|
||||
super(MockMCP3xxx, self).on_start()
|
||||
self.state = 'idle'
|
||||
|
||||
def on_bit(self):
|
||||
if self.state == 'idle':
|
||||
if self.rx_buf[-1]:
|
||||
self.state = 'mode'
|
||||
self.rx_buf = []
|
||||
elif self.state == 'mode':
|
||||
if self.rx_buf[-1]:
|
||||
self.state = 'single'
|
||||
else:
|
||||
self.state = 'diff'
|
||||
self.rx_buf = []
|
||||
elif self.state in ('single', 'diff'):
|
||||
if len(self.rx_buf) == self.channel_bits:
|
||||
self.on_result(self.state == 'diff', self.rx_word())
|
||||
self.state = 'result'
|
||||
elif self.state == 'result':
|
||||
if not self.tx_buf:
|
||||
self.state = 'idle'
|
||||
self.rx_buf = []
|
||||
else:
|
||||
assert False
|
||||
|
||||
def on_result(self, differential, channel):
|
||||
if differential:
|
||||
pos_channel = channel
|
||||
neg_channel = pos_channel ^ 1
|
||||
result = self.channels[pos_channel] - self.channels[neg_channel]
|
||||
result = clamp(result, 0, self.vref)
|
||||
else:
|
||||
result = clamp(self.channels[channel], 0, self.vref)
|
||||
result = scale(result, self.vref, self.bits)
|
||||
self.tx_word(result, self.bits + 2)
|
||||
|
||||
|
||||
class MockMCP3xx1(MockMCP3xxx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None, bits=10):
|
||||
super(MockMCP3xx1, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=bits)
|
||||
|
||||
def on_start(self):
|
||||
super(MockMCP3xx1, self).on_start()
|
||||
result = self.channels[0] - self.channels[1]
|
||||
result = clamp(result, 0, self.vref)
|
||||
result = scale(result, self.vref, self.bits)
|
||||
self.tx_word(result, self.bits + 3)
|
||||
|
||||
def on_bit(self):
|
||||
pass
|
||||
|
||||
|
||||
class MockMCP3xx2(MockMCP3xxx):
|
||||
def __init__(
|
||||
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
|
||||
bits=10):
|
||||
super(MockMCP3xx2, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=bits)
|
||||
self.channel_bits = 1
|
||||
|
||||
|
||||
class MockMCP33xx(MockMCP3xxx):
|
||||
def __init__(
|
||||
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
|
||||
channels=8):
|
||||
super(MockMCP33xx, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels, 12)
|
||||
|
||||
def on_result(self, differential, channel):
|
||||
if differential:
|
||||
pos_channel = channel
|
||||
neg_channel = pos_channel ^ 1
|
||||
result = self.channels[pos_channel] - self.channels[neg_channel]
|
||||
result = clamp(result, -self.vref, self.vref)
|
||||
else:
|
||||
result = clamp(self.channels[channel], 0, self.vref)
|
||||
result = scale(result, self.vref, self.bits)
|
||||
if result < 0:
|
||||
result += 8192
|
||||
self.tx_word(result, self.bits + 3)
|
||||
|
||||
|
||||
class MockMCP3001(MockMCP3xx1):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3001, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, bits=10)
|
||||
|
||||
|
||||
class MockMCP3002(MockMCP3xx2):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3002, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, bits=10)
|
||||
|
||||
|
||||
class MockMCP3004(MockMCP3xxx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3004, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=4, bits=10)
|
||||
|
||||
|
||||
class MockMCP3008(MockMCP3xxx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3008, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=8, bits=10)
|
||||
|
||||
|
||||
class MockMCP3201(MockMCP3xx1):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3201, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, bits=12)
|
||||
|
||||
|
||||
class MockMCP3202(MockMCP3xx2):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3202, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, bits=12)
|
||||
|
||||
|
||||
class MockMCP3204(MockMCP3xxx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3204, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=4, bits=12)
|
||||
|
||||
|
||||
class MockMCP3208(MockMCP3xxx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3208, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=8, bits=12)
|
||||
|
||||
|
||||
class MockMCP3301(MockMCP3xxx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3301, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=12)
|
||||
|
||||
def on_start(self):
|
||||
super(MockMCP3301, self).on_start()
|
||||
result = self.channels[0] - self.channels[1]
|
||||
result = clamp(result, -self.vref, self.vref)
|
||||
result = scale(result, self.vref, self.bits)
|
||||
if result < 0:
|
||||
result += 8192
|
||||
self.tx_word(result, self.bits + 4)
|
||||
|
||||
|
||||
class MockMCP3302(MockMCP33xx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3302, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=4)
|
||||
|
||||
|
||||
class MockMCP3304(MockMCP33xx):
|
||||
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
|
||||
super(MockMCP3304, self).__init__(
|
||||
clock_pin, mosi_pin, miso_pin, select_pin, channels=8)
|
||||
|
||||
|
||||
def single_mcp_test(mock, pot, channel, bits):
|
||||
scale = 2**bits
|
||||
tolerance = 1 / scale
|
||||
mock.channels[channel] = 0.0
|
||||
assert pot.raw_value == 0
|
||||
assert isclose(pot.value, 0.0, abs_tol=tolerance)
|
||||
mock.channels[channel] = mock.vref / 2
|
||||
assert pot.raw_value == (scale / 2) - 1
|
||||
assert isclose(pot.value, 0.5, abs_tol=tolerance)
|
||||
mock.channels[channel] = mock.vref
|
||||
assert pot.raw_value == scale - 1
|
||||
assert isclose(pot.value, 1.0, abs_tol=tolerance)
|
||||
|
||||
def differential_mcp_test(mock, pot, pos_channel, neg_channel, bits, full=False):
|
||||
scale = 2**bits
|
||||
tolerance = 1 / scale
|
||||
mock.channels[pos_channel] = 0.0
|
||||
mock.channels[neg_channel] = 0.0
|
||||
assert pot.raw_value == 0
|
||||
assert isclose(pot.value, 0.0, abs_tol=tolerance)
|
||||
mock.channels[pos_channel] = mock.vref / 2
|
||||
assert pot.raw_value == (scale / 2) - 1
|
||||
assert isclose(pot.value, 0.5, abs_tol=tolerance)
|
||||
mock.channels[pos_channel] = mock.vref
|
||||
assert pot.raw_value == scale - 1
|
||||
assert isclose(pot.value, 1.0, abs_tol=tolerance)
|
||||
mock.channels[neg_channel] = mock.vref / 2
|
||||
assert pot.raw_value == (scale / 2) - 1
|
||||
assert isclose(pot.value, 0.5, abs_tol=tolerance)
|
||||
mock.channels[pos_channel] = mock.vref / 2
|
||||
assert pot.raw_value == 0
|
||||
assert isclose(pot.value, 0.0, abs_tol=tolerance)
|
||||
mock.channels[pos_channel] = 0.0
|
||||
mock.channels[neg_channel] = mock.vref
|
||||
if full:
|
||||
assert pot.raw_value == -scale
|
||||
assert isclose(pot.value, -1.0, abs_tol=tolerance)
|
||||
else:
|
||||
assert pot.raw_value == 0
|
||||
assert isclose(pot.value, 0.0, abs_tol=tolerance)
|
||||
|
||||
|
||||
def test_MCP3001():
|
||||
mock = MockMCP3001(11, 10, 9, 8)
|
||||
with MCP3001() as pot:
|
||||
differential_mcp_test(mock, pot, 0, 1, 10)
|
||||
|
||||
def test_MCP3002():
|
||||
mock = MockMCP3002(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3002(channel=5)
|
||||
with MCP3002(channel=1) as pot:
|
||||
single_mcp_test(mock, pot, 1, 10)
|
||||
with MCP3002(channel=1, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 1, 0, 10)
|
||||
|
||||
def test_MCP3004():
|
||||
mock = MockMCP3004(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3004(channel=5)
|
||||
with MCP3004(channel=3) as pot:
|
||||
single_mcp_test(mock, pot, 3, 10)
|
||||
with MCP3004(channel=3, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 3, 2, 10)
|
||||
|
||||
def test_MCP3008():
|
||||
mock = MockMCP3008(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3008(channel=9)
|
||||
with MCP3008(channel=0) as pot:
|
||||
single_mcp_test(mock, pot, 0, 10)
|
||||
with MCP3008(channel=0, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 0, 1, 10)
|
||||
|
||||
def test_MCP3201():
|
||||
mock = MockMCP3201(11, 10, 9, 8)
|
||||
with MCP3201() as pot:
|
||||
differential_mcp_test(mock, pot, 0, 1, 12)
|
||||
|
||||
def test_MCP3202():
|
||||
mock = MockMCP3202(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3202(channel=5)
|
||||
with MCP3202(channel=1) as pot:
|
||||
single_mcp_test(mock, pot, 1, 12)
|
||||
with MCP3202(channel=1, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 1, 0, 12)
|
||||
|
||||
def test_MCP3204():
|
||||
mock = MockMCP3204(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3204(channel=5)
|
||||
with MCP3204(channel=1) as pot:
|
||||
single_mcp_test(mock, pot, 1, 12)
|
||||
with MCP3204(channel=1, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 1, 0, 12)
|
||||
|
||||
def test_MCP3208():
|
||||
mock = MockMCP3208(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3208(channel=9)
|
||||
with MCP3208(channel=7) as pot:
|
||||
single_mcp_test(mock, pot, 7, 12)
|
||||
with MCP3208(channel=7, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 7, 6, 12)
|
||||
|
||||
def test_MCP3301():
|
||||
mock = MockMCP3301(11, 10, 9, 8)
|
||||
with MCP3301() as pot:
|
||||
differential_mcp_test(mock, pot, 0, 1, 12, full=True)
|
||||
|
||||
def test_MCP3302():
|
||||
mock = MockMCP3302(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3302(channel=4)
|
||||
with MCP3302(channel=0) as pot:
|
||||
single_mcp_test(mock, pot, 0, 12)
|
||||
with MCP3302(channel=0, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 0, 1, 12, full=True)
|
||||
|
||||
def test_MCP3304():
|
||||
mock = MockMCP3304(11, 10, 9, 8)
|
||||
with pytest.raises(ValueError):
|
||||
MCP3304(channel=9)
|
||||
with MCP3304(channel=5) as pot:
|
||||
single_mcp_test(mock, pot, 5, 12)
|
||||
with MCP3304(channel=5, differential=True) as pot:
|
||||
differential_mcp_test(mock, pot, 5, 4, 12, full=True)
|
||||
|
||||
Reference in New Issue
Block a user