Merge pull request #430 from waveform80/spi-tests

Fix #421
This commit is contained in:
Dave Jones
2016-09-08 22:29:35 +01:00
committed by GitHub
10 changed files with 975 additions and 194 deletions

View File

@@ -11,6 +11,7 @@ digraph classes {
MCP3xxx; MCP3xxx;
MCP30xx; MCP30xx;
MCP32xx; MCP32xx;
MCP3xx2;
MCP33xx; MCP33xx;
/* Concrete classes */ /* Concrete classes */
@@ -21,6 +22,8 @@ digraph classes {
MCP30xx->MCP3xxx; MCP30xx->MCP3xxx;
MCP32xx->MCP3xxx; MCP32xx->MCP3xxx;
MCP33xx->MCP3xxx; MCP33xx->MCP3xxx;
MCP3xx2->MCP3xxx;
MCP3001->MCP30xx; MCP3001->MCP30xx;
MCP3002->MCP30xx; MCP3002->MCP30xx;
MCP3004->MCP30xx; MCP3004->MCP30xx;
@@ -29,6 +32,8 @@ digraph classes {
MCP3202->MCP32xx; MCP3202->MCP32xx;
MCP3204->MCP32xx; MCP3204->MCP32xx;
MCP3208->MCP32xx; MCP3208->MCP32xx;
MCP3002->MCP3xx2;
MCP3202->MCP3xx2;
MCP3301->MCP33xx; MCP3301->MCP33xx;
MCP3302->MCP33xx; MCP3302->MCP33xx;
MCP3304->MCP33xx; MCP3304->MCP33xx;

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> "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 --> <!-- Title: classes Pages: 1 -->
<svg width="870pt" height="404pt" <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"/> <polygon fill="white" stroke="none" points="-4,4 -4,-400 866,-400 866,4 -4,4"/>
<!-- Device --> <!-- Device -->
<g id="node1" class="node"><title>Device</title> <g id="node1" class="node"><title>Device</title>
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="498,-396 444,-396 444,-360 498,-360 498,-396"/> <polygon fill="#9ec6e0" stroke="#9ec6e0" points="418,-396 364,-396 364,-360 418,-360 418,-396"/>
<text text-anchor="middle" x="471" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text> <text text-anchor="middle" x="391" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
</g> </g>
<!-- SPIDevice --> <!-- SPIDevice -->
<g id="node2" class="node"><title>SPIDevice</title> <g id="node2" class="node"><title>SPIDevice</title>
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="503,-324 439,-324 439,-288 503,-288 503,-324"/> <polygon fill="#9ec6e0" stroke="#9ec6e0" points="423,-324 359,-324 359,-288 423,-288 423,-324"/>
<text text-anchor="middle" x="471" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">SPIDevice</text> <text text-anchor="middle" x="391" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">SPIDevice</text>
</g> </g>
<!-- SPIDevice&#45;&gt;Device --> <!-- SPIDevice&#45;&gt;Device -->
<g id="edge1" class="edge"><title>SPIDevice&#45;&gt;Device</title> <g id="edge1" class="edge"><title>SPIDevice&#45;&gt;Device</title>
<path fill="none" stroke="black" d="M471,-324.303C471,-332.017 471,-341.288 471,-349.888"/> <path fill="none" stroke="black" d="M391,-324.303C391,-332.017 391,-341.288 391,-349.888"/>
<polygon fill="black" stroke="black" points="467.5,-349.896 471,-359.896 474.5,-349.896 467.5,-349.896"/> <polygon fill="black" stroke="black" points="387.5,-349.896 391,-359.896 394.5,-349.896 387.5,-349.896"/>
</g> </g>
<!-- AnalogInputDevice --> <!-- AnalogInputDevice -->
<g id="node3" class="node"><title>AnalogInputDevice</title> <g id="node3" class="node"><title>AnalogInputDevice</title>
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="524,-252 418,-252 418,-216 524,-216 524,-252"/> <polygon fill="#9ec6e0" stroke="#9ec6e0" points="444,-252 338,-252 338,-216 444,-216 444,-252"/>
<text text-anchor="middle" x="471" y="-231.5" font-family="Sans" font-size="10.00" fill="#000000">AnalogInputDevice</text> <text text-anchor="middle" x="391" y="-231.5" font-family="Sans" font-size="10.00" fill="#000000">AnalogInputDevice</text>
</g> </g>
<!-- AnalogInputDevice&#45;&gt;SPIDevice --> <!-- AnalogInputDevice&#45;&gt;SPIDevice -->
<g id="edge2" class="edge"><title>AnalogInputDevice&#45;&gt;SPIDevice</title> <g id="edge2" class="edge"><title>AnalogInputDevice&#45;&gt;SPIDevice</title>
<path fill="none" stroke="black" d="M471,-252.303C471,-260.017 471,-269.288 471,-277.888"/> <path fill="none" stroke="black" d="M391,-252.303C391,-260.017 391,-269.288 391,-277.888"/>
<polygon fill="black" stroke="black" points="467.5,-277.896 471,-287.896 474.5,-277.896 467.5,-277.896"/> <polygon fill="black" stroke="black" points="387.5,-277.896 391,-287.896 394.5,-277.896 387.5,-277.896"/>
</g> </g>
<!-- MCP3xxx --> <!-- MCP3xxx -->
<g id="node4" class="node"><title>MCP3xxx</title> <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"/> <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="471" y="-159.5" font-family="Sans" font-size="10.00" fill="#000000">MCP3xxx</text> <text text-anchor="middle" x="391" y="-159.5" font-family="Sans" font-size="10.00" fill="#000000">MCP3xxx</text>
</g> </g>
<!-- MCP3xxx&#45;&gt;AnalogInputDevice --> <!-- MCP3xxx&#45;&gt;AnalogInputDevice -->
<g id="edge3" class="edge"><title>MCP3xxx&#45;&gt;AnalogInputDevice</title> <g id="edge3" class="edge"><title>MCP3xxx&#45;&gt;AnalogInputDevice</title>
<path fill="none" stroke="black" d="M471,-180.303C471,-188.017 471,-197.288 471,-205.888"/> <path fill="none" stroke="black" d="M391,-180.303C391,-188.017 391,-197.288 391,-205.888"/>
<polygon fill="black" stroke="black" points="467.5,-205.896 471,-215.896 474.5,-205.896 467.5,-205.896"/> <polygon fill="black" stroke="black" points="387.5,-205.896 391,-215.896 394.5,-205.896 387.5,-205.896"/>
</g> </g>
<!-- MCP30xx --> <!-- MCP30xx -->
<g id="node5" class="node"><title>MCP30xx</title> <g id="node5" class="node"><title>MCP30xx</title>
@@ -51,138 +51,158 @@
</g> </g>
<!-- MCP30xx&#45;&gt;MCP3xxx --> <!-- MCP30xx&#45;&gt;MCP3xxx -->
<g id="edge4" class="edge"><title>MCP30xx&#45;&gt;MCP3xxx</title> <g id="edge4" class="edge"><title>MCP30xx&#45;&gt;MCP3xxx</title>
<path fill="none" stroke="black" d="M220.7,-98.425C271.447,-111.112 374.849,-136.962 431.579,-151.145"/> <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="431.055,-154.621 441.605,-153.651 432.752,-147.83 431.055,-154.621"/> <polygon fill="black" stroke="black" points="350.725,-150.612 361.319,-150.612 353.037,-144.005 350.725,-150.612"/>
</g> </g>
<!-- MCP32xx --> <!-- MCP32xx -->
<g id="node6" class="node"><title>MCP32xx</title> <g id="node6" class="node"><title>MCP32xx</title>
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="501,-108 441,-108 441,-72 501,-72 501,-108"/> <polygon fill="#9ec6e0" stroke="#9ec6e0" points="461,-108 401,-108 401,-72 461,-72 461,-108"/>
<text text-anchor="middle" x="471" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">MCP32xx</text> <text text-anchor="middle" x="431" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">MCP32xx</text>
</g> </g>
<!-- MCP32xx&#45;&gt;MCP3xxx --> <!-- MCP32xx&#45;&gt;MCP3xxx -->
<g id="edge5" class="edge"><title>MCP32xx&#45;&gt;MCP3xxx</title> <g id="edge5" class="edge"><title>MCP32xx&#45;&gt;MCP3xxx</title>
<path fill="none" stroke="black" d="M471,-108.303C471,-116.017 471,-125.288 471,-133.888"/> <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="467.5,-133.896 471,-143.896 474.5,-133.896 467.5,-133.896"/> <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&#45;&gt;MCP3xxx -->
<g id="edge7" class="edge"><title>MCP3xx2&#45;&gt;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> </g>
<!-- MCP33xx --> <!-- 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"/> <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> <text text-anchor="middle" x="711" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">MCP33xx</text>
</g> </g>
<!-- MCP33xx&#45;&gt;MCP3xxx --> <!-- MCP33xx&#45;&gt;MCP3xxx -->
<g id="edge6" class="edge"><title>MCP33xx&#45;&gt;MCP3xxx</title> <g id="edge6" class="edge"><title>MCP33xx&#45;&gt;MCP3xxx</title>
<path fill="none" stroke="black" d="M681.089,-99.724C637.918,-112.316 558.088,-135.599 510.235,-149.556"/> <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="509.154,-146.226 500.534,-152.386 511.114,-152.946 509.154,-146.226"/> <polygon fill="black" stroke="black" points="429.813,-148.927 420.792,-154.483 431.309,-155.765 429.813,-148.927"/>
</g> </g>
<!-- MCP3001 --> <!-- MCP3001 -->
<g id="node8" class="node"><title>MCP3001</title> <g id="node9" 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"/> <polygon fill="#2980b9" stroke="#2980b9" points="142,-36 80,-36 80,-0 142,-0 142,-36"/>
<text text-anchor="middle" x="31" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3001</text> <text text-anchor="middle" x="111" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3001</text>
</g> </g>
<!-- MCP3001&#45;&gt;MCP30xx --> <!-- MCP3001&#45;&gt;MCP30xx -->
<g id="edge7" class="edge"><title>MCP3001&#45;&gt;MCP30xx</title> <g id="edge8" class="edge"><title>MCP3001&#45;&gt;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&#45;&gt;MCP30xx -->
<g id="edge8" class="edge"><title>MCP3002&#45;&gt;MCP30xx</title>
<path fill="none" stroke="black" d="M130.775,-36.3034C140.754,-45.0345 153.011,-55.7595 163.857,-65.2497"/> <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"/> <polygon fill="black" stroke="black" points="161.622,-67.9446 171.452,-71.8957 166.231,-62.6766 161.622,-67.9446"/>
</g> </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&#45;&gt;MCP30xx -->
<g id="edge9" class="edge"><title>MCP3002&#45;&gt;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&#45;&gt;MCP3xx2 -->
<g id="edge16" class="edge"><title>MCP3002&#45;&gt;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 --> <!-- 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"/> <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> <text text-anchor="middle" x="191" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3004</text>
</g> </g>
<!-- MCP3004&#45;&gt;MCP30xx --> <!-- MCP3004&#45;&gt;MCP30xx -->
<g id="edge9" class="edge"><title>MCP3004&#45;&gt;MCP30xx</title> <g id="edge10" class="edge"><title>MCP3004&#45;&gt;MCP30xx</title>
<path fill="none" stroke="black" d="M191,-36.3034C191,-44.0173 191,-53.2875 191,-61.8876"/> <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"/> <polygon fill="black" stroke="black" points="187.5,-61.8956 191,-71.8957 194.5,-61.8957 187.5,-61.8956"/>
</g> </g>
<!-- MCP3008 --> <!-- MCP3008 -->
<g id="node11" class="node"><title>MCP3008</title> <g id="node12" class="node"><title>MCP3008</title>
<polygon fill="#2980b9" stroke="#2980b9" points="302,-36 240,-36 240,-0 302,-0 302,-36"/> <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="271" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3008</text> <text text-anchor="middle" x="31" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3008</text>
</g> </g>
<!-- MCP3008&#45;&gt;MCP30xx --> <!-- MCP3008&#45;&gt;MCP30xx -->
<g id="edge10" class="edge"><title>MCP3008&#45;&gt;MCP30xx</title> <g id="edge11" class="edge"><title>MCP3008&#45;&gt;MCP30xx</title>
<path fill="none" stroke="black" d="M251.225,-36.3034C241.246,-45.0345 228.989,-55.7595 218.143,-65.2497"/> <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="215.769,-62.6766 210.548,-71.8957 220.378,-67.9446 215.769,-62.6766"/> <polygon fill="black" stroke="black" points="150.369,-75.044 160.933,-75.8457 153.174,-68.6309 150.369,-75.044"/>
</g> </g>
<!-- MCP3201 --> <!-- MCP3201 -->
<g id="node12" class="node"><title>MCP3201</title> <g id="node13" class="node"><title>MCP3201</title>
<polygon fill="#2980b9" stroke="#2980b9" points="382,-36 320,-36 320,-0 382,-0 382,-36"/> <polygon fill="#2980b9" stroke="#2980b9" points="542,-36 480,-36 480,-0 542,-0 542,-36"/>
<text text-anchor="middle" x="351" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3201</text> <text text-anchor="middle" x="511" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3201</text>
</g> </g>
<!-- MCP3201&#45;&gt;MCP32xx --> <!-- MCP3201&#45;&gt;MCP32xx -->
<g id="edge11" class="edge"><title>MCP3201&#45;&gt;MCP32xx</title> <g id="edge12" class="edge"><title>MCP3201&#45;&gt;MCP32xx</title>
<path fill="none" stroke="black" d="M380.355,-36.1239C396.045,-45.2764 415.557,-56.6583 432.454,-66.515"/> <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="431.13,-69.7947 441.532,-71.8102 434.658,-63.7483 431.13,-69.7947"/> <polygon fill="black" stroke="black" points="455.769,-62.6766 450.548,-71.8957 460.378,-67.9446 455.769,-62.6766"/>
</g> </g>
<!-- MCP3202 --> <!-- MCP3202 -->
<g id="node13" class="node"><title>MCP3202</title> <g id="node14" class="node"><title>MCP3202</title>
<polygon fill="#2980b9" stroke="#2980b9" points="462,-36 400,-36 400,-0 462,-0 462,-36"/> <polygon fill="#2980b9" stroke="#2980b9" points="382,-36 320,-36 320,-0 382,-0 382,-36"/>
<text text-anchor="middle" x="431" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3202</text> <text text-anchor="middle" x="351" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3202</text>
</g> </g>
<!-- MCP3202&#45;&gt;MCP32xx --> <!-- MCP3202&#45;&gt;MCP32xx -->
<g id="edge12" class="edge"><title>MCP3202&#45;&gt;MCP32xx</title> <g id="edge13" class="edge"><title>MCP3202&#45;&gt;MCP32xx</title>
<path fill="none" stroke="black" d="M440.888,-36.3034C445.489,-44.3564 451.06,-54.1055 456.153,-63.0176"/> <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="453.226,-64.9497 461.226,-71.8957 459.304,-61.4767 453.226,-64.9497"/> <polygon fill="black" stroke="black" points="401.622,-67.9446 411.452,-71.8957 406.231,-62.6766 401.622,-67.9446"/>
</g>
<!-- MCP3202&#45;&gt;MCP3xx2 -->
<g id="edge17" class="edge"><title>MCP3202&#45;&gt;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> </g>
<!-- MCP3204 --> <!-- MCP3204 -->
<g id="node14" class="node"><title>MCP3204</title> <g id="node15" class="node"><title>MCP3204</title>
<polygon fill="#2980b9" stroke="#2980b9" points="542,-36 480,-36 480,-0 542,-0 542,-36"/> <polygon fill="#2980b9" stroke="#2980b9" points="622,-36 560,-36 560,-0 622,-0 622,-36"/>
<text text-anchor="middle" x="511" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3204</text> <text text-anchor="middle" x="591" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3204</text>
</g> </g>
<!-- MCP3204&#45;&gt;MCP32xx --> <!-- MCP3204&#45;&gt;MCP32xx -->
<g id="edge13" class="edge"><title>MCP3204&#45;&gt;MCP32xx</title> <g id="edge14" class="edge"><title>MCP3204&#45;&gt;MCP32xx</title>
<path fill="none" stroke="black" d="M501.112,-36.3034C496.511,-44.3564 490.94,-54.1055 485.847,-63.0176"/> <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="482.696,-61.4767 480.774,-71.8957 488.774,-64.9497 482.696,-61.4767"/> <polygon fill="black" stroke="black" points="468.826,-68.6309 461.067,-75.8457 471.631,-75.044 468.826,-68.6309"/>
</g> </g>
<!-- MCP3208 --> <!-- MCP3208 -->
<g id="node15" class="node"><title>MCP3208</title> <g id="node16" class="node"><title>MCP3208</title>
<polygon fill="#2980b9" stroke="#2980b9" points="622,-36 560,-36 560,-0 622,-0 622,-36"/> <polygon fill="#2980b9" stroke="#2980b9" points="462,-36 400,-36 400,-0 462,-0 462,-36"/>
<text text-anchor="middle" x="591" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3208</text> <text text-anchor="middle" x="431" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3208</text>
</g> </g>
<!-- MCP3208&#45;&gt;MCP32xx --> <!-- MCP3208&#45;&gt;MCP32xx -->
<g id="edge14" class="edge"><title>MCP3208&#45;&gt;MCP32xx</title> <g id="edge15" class="edge"><title>MCP3208&#45;&gt;MCP32xx</title>
<path fill="none" stroke="black" d="M561.645,-36.1239C545.955,-45.2764 526.443,-56.6583 509.546,-66.515"/> <path fill="none" stroke="black" d="M431,-36.3034C431,-44.0173 431,-53.2875 431,-61.8876"/>
<polygon fill="black" stroke="black" points="507.342,-63.7483 500.468,-71.8102 510.87,-69.7947 507.342,-63.7483"/> <polygon fill="black" stroke="black" points="427.5,-61.8956 431,-71.8957 434.5,-61.8957 427.5,-61.8956"/>
</g> </g>
<!-- MCP3301 --> <!-- MCP3301 -->
<g id="node16" class="node"><title>MCP3301</title> <g id="node17" class="node"><title>MCP3301</title>
<polygon fill="#2980b9" stroke="#2980b9" points="702,-36 640,-36 640,-0 702,-0 702,-36"/> <polygon fill="#2980b9" stroke="#2980b9" points="782,-36 720,-36 720,-0 782,-0 782,-36"/>
<text text-anchor="middle" x="671" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3301</text> <text text-anchor="middle" x="751" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MCP3301</text>
</g> </g>
<!-- MCP3301&#45;&gt;MCP33xx --> <!-- MCP3301&#45;&gt;MCP33xx -->
<g id="edge15" class="edge"><title>MCP3301&#45;&gt;MCP33xx</title> <g id="edge18" class="edge"><title>MCP3301&#45;&gt;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&#45;&gt;MCP33xx -->
<g id="edge16" class="edge"><title>MCP3302&#45;&gt;MCP33xx</title>
<path fill="none" stroke="black" d="M741.112,-36.3034C736.511,-44.3564 730.94,-54.1055 725.847,-63.0176"/> <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"/> <polygon fill="black" stroke="black" points="722.696,-61.4767 720.774,-71.8957 728.774,-64.9497 722.696,-61.4767"/>
</g> </g>
<!-- MCP3304 --> <!-- MCP3302 -->
<g id="node18" class="node"><title>MCP3304</title> <g id="node18" class="node"><title>MCP3302</title>
<polygon fill="#2980b9" stroke="#2980b9" points="862,-36 800,-36 800,-0 862,-0 862,-36"/> <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> </g>
<!-- MCP3304&#45;&gt;MCP33xx --> <!-- MCP3302&#45;&gt;MCP33xx -->
<g id="edge17" class="edge"><title>MCP3304&#45;&gt;MCP33xx</title> <g id="edge19" class="edge"><title>MCP3302&#45;&gt;MCP33xx</title>
<path fill="none" stroke="black" d="M801.645,-36.1239C785.955,-45.2764 766.443,-56.6583 749.546,-66.515"/> <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"/> <polygon fill="black" stroke="black" points="747.342,-63.7483 740.468,-71.8102 750.87,-69.7947 747.342,-63.7483"/>
</g> </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&#45;&gt;MCP33xx -->
<g id="edge20" class="edge"><title>MCP3304&#45;&gt;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> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -49,6 +49,9 @@ class SPIError(GPIOZeroError):
class SPIBadArgs(SPIError, ValueError): class SPIBadArgs(SPIError, ValueError):
"Error raised when invalid arguments are given while constructing :class:`SPIDevice`" "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): class GPIODeviceError(GPIOZeroError):
"Base class for errors specific to the GPIODevice hierarchy" "Base class for errors specific to the GPIODevice hierarchy"

View File

@@ -54,7 +54,8 @@ class MockPin(Pin):
self._when_changed = None self._when_changed = None
self.clear_states() self.clear_states()
return self 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__)) raise ValueError('pin %d is already in use as a %s' % (number, old_pin.__class__.__name__))
return old_pin return old_pin
@@ -249,7 +250,6 @@ class MockPWMPin(MockPin):
""" """
This derivative of :class:`MockPin` adds PWM support. This derivative of :class:`MockPin` adds PWM support.
""" """
def __init__(self, number): def __init__(self, number):
super(MockPWMPin, self).__init__() super(MockPWMPin, self).__init__()
self._frequency = None self._frequency = None
@@ -275,3 +275,141 @@ class MockPWMPin(MockPin):
if value is None: if value is None:
self._change_state(0.0) 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)

View File

@@ -85,16 +85,16 @@ class SPIHardwareInterface(Device):
self._device.mode = value self._device.mode = value
def _get_clock_polarity(self): def _get_clock_polarity(self):
return bool(self.mode & 2) return bool(self.clock_mode & 2)
def _set_clock_polarity(self, value): 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): def _get_clock_phase(self):
return bool(self.mode & 1) return bool(self.clock_mode & 1)
def _set_clock_phase(self, value): 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): def _get_lsb_first(self):
return self._device.lsbfirst return self._device.lsbfirst
@@ -130,9 +130,6 @@ class SPISoftwareBus(SharedMixin, Device):
self.miso = None self.miso = None
super(SPISoftwareBus, self).__init__() super(SPISoftwareBus, self).__init__()
self.lock = RLock() self.lock = RLock()
self.clock_phase = False
self.lsb_first = False
self.bits_per_word = 8
try: try:
self.clock = OutputDevice(clock_pin, active_high=True) self.clock = OutputDevice(clock_pin, active_high=True)
if mosi_pin is not None: if mosi_pin is not None:
@@ -166,13 +163,7 @@ class SPISoftwareBus(SharedMixin, Device):
def _shared_key(cls, clock_pin, mosi_pin, miso_pin): def _shared_key(cls, clock_pin, mosi_pin, miso_pin):
return (clock_pin, mosi_pin, miso_pin) return (clock_pin, mosi_pin, miso_pin)
def read(self, n): def transfer(self, data, clock_phase=False, lsb_first=False, bits_per_word=8):
return self.transfer((0,) * n)
def write(self, data):
return len(self.transfer(data))
def transfer(self, data):
""" """
Writes data (a list of integer words where each word is assumed to have 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 :attr:`bits_per_word` bits or less) to the SPI interface, and reads an
@@ -180,19 +171,19 @@ class SPISoftwareBus(SharedMixin, Device):
""" """
result = [] result = []
with self.lock: 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: 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 read_word = 0
for _ in range(self.bits_per_word): for _ in range(bits_per_word):
if self.mosi is not None: if self.mosi is not None:
self.mosi.value = bool(write_word & mask) self.mosi.value = bool(write_word & mask)
self.clock.on() 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: if self.miso.value:
read_word |= mask read_word |= mask
self.clock.off() 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: if self.miso.value:
read_word |= mask read_word |= mask
mask = shift(mask, 1) mask = shift(mask, 1)
@@ -205,6 +196,9 @@ class SPISoftwareInterface(OutputDevice):
self._bus = None self._bus = None
super(SPISoftwareInterface, self).__init__(select_pin, active_high=False) super(SPISoftwareInterface, self).__init__(select_pin, active_high=False)
try: try:
self._clock_phase = False
self._lsb_first = False
self._bits_per_word = 8
self._bus = SPISoftwareBus(clock_pin, mosi_pin, miso_pin) self._bus = SPISoftwareBus(clock_pin, mosi_pin, miso_pin)
except: except:
self.close() self.close()
@@ -230,16 +224,17 @@ class SPISoftwareInterface(OutputDevice):
return "software SPI closed" return "software SPI closed"
def read(self, n): def read(self, n):
return self._bus.read(n) return self.transfer((0,) * n)
def write(self, data): def write(self, data):
return self._bus.write(data) return len(self.transfer(data))
def transfer(self, data): def transfer(self, data):
with self._bus.lock: with self._bus.lock:
self.on() self.on()
try: try:
return self._bus.transfer(data) return self._bus.transfer(
data, self._clock_phase, self._lsb_first, self._bits_per_word)
finally: finally:
self.off() self.off()
@@ -250,40 +245,37 @@ class SPISoftwareInterface(OutputDevice):
value = int(value) value = int(value)
if not 0 <= value <= 3: if not 0 <= value <= 3:
raise ValueError('clock_mode must be a value between 0 and 3 inclusive') raise ValueError('clock_mode must be a value between 0 and 3 inclusive')
with self._bus.lock: self.clock_polarity = bool(value & 2)
self._bus.clock.active_high = not (value & 2) self.clock_phase = bool(value & 1)
self._bus.clock.off()
self._bus.clock_phase = bool(value & 1)
def _get_clock_polarity(self): 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): def _set_clock_polarity(self, value):
with self._bus.lock: with self._bus.lock:
self._bus.clock.active_high = not value self._bus.clock.active_high = not value
self._bus.clock.off()
def _get_clock_phase(self): def _get_clock_phase(self):
return self._bus.clock_phase return self._clock_phase
def _set_clock_phase(self, value): def _set_clock_phase(self, value):
with self._bus.lock: self._clock_phase = bool(value)
self._bus.clock_phase = bool(value)
def _get_lsb_first(self): def _get_lsb_first(self):
return self._bus.lsb_first return self._lsb_first
def _set_lsb_first(self, value): def _set_lsb_first(self, value):
with self._bus.lock: self._lsb_first = bool(value)
self._bus.lsb_first = bool(value)
def _get_bits_per_word(self): def _get_bits_per_word(self):
return self._bus.bits_per_word return self._bits_per_word
def _set_bits_per_word(self, value): def _set_bits_per_word(self, value):
if value < 1: if value < 1:
raise ValueError('bits_per_word must be positive') raise ValueError('bits_per_word must be positive')
with self._bus.lock: self._bits_per_word = int(value)
self._bus.bits_per_word = int(value)
def _get_select_high(self): def _get_select_high(self):
return self.active_high return self.active_high

View File

@@ -7,7 +7,14 @@ from __future__ import (
str = type('') 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 .devices import Device
from .spi import SPI from .spi import SPI
@@ -34,6 +41,38 @@ class SPIDevice(Device):
def closed(self): def closed(self):
return self._spi is None 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): def __repr__(self):
try: try:
self._check_open() self._check_open()
@@ -72,10 +111,10 @@ class AnalogInputDevice(SPIDevice):
.. _analog to digital converters: https://en.wikipedia.org/wiki/Analog-to-digital_converter .. _analog to digital converters: https://en.wikipedia.org/wiki/Analog-to-digital_converter
""" """
def __init__(self, bits=None, **spi_args): def __init__(self, bits, **spi_args):
if bits is None:
raise InputDeviceError('you must specify the bit resolution of the device')
self._bits = bits self._bits = bits
self._min_value = -(2 ** bits)
self._range = 2 ** (bits + 1) - 1
super(AnalogInputDevice, self).__init__(shared=True, **spi_args) super(AnalogInputDevice, self).__init__(shared=True, **spi_args)
@property @property
@@ -92,9 +131,9 @@ class AnalogInputDevice(SPIDevice):
def value(self): def value(self):
""" """
The current value read from the device, scaled to a value between 0 and 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 @property
def raw_value(self): def raw_value(self):
@@ -128,10 +167,9 @@ class MCP3xxx(AnalogInputDevice):
@property @property
def differential(self): def differential(self):
""" """
If ``True``, the device is operated in pseudo-differential mode. In If ``True``, the device is operated in differential mode. In this mode
this mode one channel (specified by the channel attribute) is read one channel (specified by the channel attribute) is read relative to
relative to the value of a second channel (implied by the chip's the value of a second channel (implied by the chip's design).
design).
Please refer to the device data-sheet to determine which channel is Please refer to the device data-sheet to determine which channel is
used as the relative base value (for example, when using an used as the relative base value (for example, when using an
@@ -141,28 +179,73 @@ class MCP3xxx(AnalogInputDevice):
return self._differential return self._differential
def _read(self): 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 # Byte 0 1 2
# ==== ======== ======== ======== # ==== ======== ======== ========
# Tx 0001MCCC xxxxxxxx xxxxxxxx # Tx 00000001 MCCCxxxx xxxxxxxx
# Rx xxxxxxxx x0RRRRRR RRRRxxxx for the 3004/08 # Rx xxxxxxxx xxxxx0RR RRRRRRRR
# Rx xxxxxxxx x0RRRRRR RRRRRRxx for the 3204/08
# #
# The transmit bits start with 3 preamble bits "000" (to warm up), a # MCP3204/08 protocol looks like the following:
# 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 # Byte 0 1 2
# 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 # Tx 000001MC CCxxxxxx xxxxxxxx
# MCP320x. # Rx xxxxxxxx xxx0RRRR RRRRRRRR
# #
# XXX Differential mode still requires testing # The transmit bits start with several preamble "0" bits, the number
data = self._spi.transfer([16 + [8, 0][self.differential] + self.channel, 0, 0]) # of which is determined by the amount required to align the last byte
return ((data[1] & 63) << (self.bits - 6)) | (data[2] >> (14 - self.bits)) # 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): class MCP30xx(MCP3xxx):
@@ -196,20 +279,32 @@ class MCP33xx(MCP3xxx):
super(MCP33xx, self).__init__(channel, 12, differential, **spi_args) super(MCP33xx, self).__init__(channel, 12, differential, **spi_args)
def _read(self): 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 # Byte 0 1 2
# ==== ======== ======== ======== # ==== ======== ======== ========
# Tx 0001MCCC xxxxxxxx xxxxxxxx # Tx 00001MCC Cxxxxxxx xxxxxxxx
# Rx xxxxxxxx x0SRRRRR RRRRRRRx # Rx xxxxxxxx xx0SRRRR RRRRRRRR
# #
# The transmit bits start with 3 preamble bits "000" (to warm up), a # The transmit bits start with 4 preamble bits "0000", a start bit "1"
# start bit "1" followed by the single/differential bit (M) which is 1 # followed by the single/differential bit (M) which is 1 for
# for single-ended read, and 0 for differential read, followed by # single-ended read, and 0 for differential read, followed by 3-bits
# 3-bits for the channel (C). The remainder of the transmission are # for the channel (C). The remainder of the transmission are "don't
# "don't care" bits (x). # 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 # 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). # 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 # result is effectively 12-bits. In differential mode, the sign bit is
# significant and the result is a two's-complement 13-bit value. # significant and the result is a two's-complement 13-bit value.
# #
# The MCP3301 variant of the chip always operates in differential # The MCP3301 variant operates similarly to the other MCP3x01 variants;
# mode and effectively only has one channel (composed of an IN+ and # no input, just output and always differential.
# IN-). As such it requires no input, just output. This is the reason return self._int_to_words(
# we split out _send() below; so that MCP3301 can override it. (0b10000 | (not self.differential) << 3 | self.channel) << (self.bits + 3)
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]
@property @property
def differential(self): def differential(self):
@@ -259,15 +343,25 @@ class MCP33xx(MCP3xxx):
class MCP3001(MCP30xx): 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 .. _MCP3001: http://www.farnell.com/datasheets/630400.pdf
""" """
def __init__(self, **spi_args): def __init__(self, **spi_args):
super(MCP3001, self).__init__(0, differential=True, **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 The `MCP3002`_ is a 10-bit analog to digital converter with 2 channels
(0-1). (0-1).
@@ -276,7 +370,7 @@ class MCP3002(MCP30xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 2: 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) super(MCP3002, self).__init__(channel, differential, **spi_args)
@@ -289,7 +383,7 @@ class MCP3004(MCP30xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 4: 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) super(MCP3004, self).__init__(channel, differential, **spi_args)
@@ -302,21 +396,31 @@ class MCP3008(MCP30xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 8: 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) super(MCP3008, self).__init__(channel, differential, **spi_args)
class MCP3201(MCP32xx): 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 .. _MCP3201: http://www.farnell.com/datasheets/1669366.pdf
""" """
def __init__(self, **spi_args): def __init__(self, **spi_args):
super(MCP3201, self).__init__(0, differential=True, **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 The `MCP3202`_ is a 12-bit analog to digital converter with 2 channels
(0-1). (0-1).
@@ -325,7 +429,7 @@ class MCP3202(MCP32xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 2: 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) super(MCP3202, self).__init__(channel, differential, **spi_args)
@@ -338,7 +442,7 @@ class MCP3204(MCP32xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 4: 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) super(MCP3204, self).__init__(channel, differential, **spi_args)
@@ -351,23 +455,33 @@ class MCP3208(MCP32xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 8: 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) super(MCP3208, self).__init__(channel, differential, **spi_args)
class MCP3301(MCP33xx): class MCP3301(MCP33xx):
""" """
The `MCP3301`_ is a signed 13-bit analog to digital converter. Please note The `MCP3301`_ is a signed 13-bit analog to digital converter. Please note
that the MCP3301 always operates in differential mode between its two that the MCP3301 always operates in differential mode measuring the
channels and the output value is scaled from -1 to +1. difference between IN+ and IN-. Its output value is scaled from -1 to +1.
.. _MCP3301: http://www.farnell.com/datasheets/1669397.pdf .. _MCP3301: http://www.farnell.com/datasheets/1669397.pdf
""" """
def __init__(self, **spi_args): def __init__(self, **spi_args):
super(MCP3301, self).__init__(0, differential=True, **spi_args) super(MCP3301, self).__init__(0, differential=True, **spi_args)
def _send(self): def _read(self):
return [0, 0] # 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): class MCP3302(MCP33xx):
@@ -382,7 +496,7 @@ class MCP3302(MCP33xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 4: 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) super(MCP3302, self).__init__(channel, differential, **spi_args)
@@ -398,6 +512,6 @@ class MCP3304(MCP33xx):
""" """
def __init__(self, channel=0, differential=False, **spi_args): def __init__(self, channel=0, differential=False, **spi_args):
if not 0 <= channel < 8: 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) super(MCP3304, self).__init__(channel, differential, **spi_args)

170
tests/test_spi.py Normal file
View 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
View 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)