diff options
| author | Scott Shawcroft <scott@chickadee.tech> | 2016-08-23 16:47:53 -0700 |
|---|---|---|
| committer | Scott Shawcroft <scott@chickadee.tech> | 2016-08-23 16:47:53 -0700 |
| commit | 9485634d41369e896a0ef0f7504f2195b68679e7 (patch) | |
| tree | e97b3a81e8e4160ed7d03afdbd462880995422e1 /atmel-samd/asf | |
| parent | 8f26d181c30f8d371a900355a6f9edfab29c8fbe (diff) | |
Add support for REPL on Arduino Zero through EDBG via UART.
Diffstat (limited to 'atmel-samd/asf')
38 files changed, 21189 insertions, 5 deletions
diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_state_diagram.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_state_diagram.svg new file mode 100644 index 000000000..4bd4175a0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_state_diagram.svg @@ -0,0 +1,533 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + id="svg2985" + version="1.1" + inkscape:version="0.48.2 r9819" + width="380.12842" + height="320.95972" + xml:space="preserve" + sodipodi:docname="Sercom_I2C_Bus_State_State_Diagram.svg"><metadata + id="metadata2991"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs + id="defs2989"><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3001"><path + d="m 0,762.9 612,0 0,0.53998 -612,0 L 0,762.9 z" + id="path3003" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3011"><path + d="m 611.76,757.86 0.23999,0 0,5.34 -0.23999,0 0,-5.34 z" + id="path3013" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3021"><path + d="m 0,757.86 612,0 0,0.54004 -612,0 0,-0.54004 z" + id="path3023" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3031"><path + d="m 0,758.1 0.29999,0 0,5.34 -0.29999,0 0,-5.34 z" + id="path3033" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3043"><path + d="m 0,791.7 612,0 0,0.29999 -612,0 L 0,791.7 z" + id="path3045" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3053"><path + d="m 611.76,762.9 0.23999,0 0,29.1 -0.23999,0 0,-29.1 z" + id="path3055" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3063"><path + d="m 0,762.9 612,0 0,0.53998 -612,0 L 0,762.9 z" + id="path3065" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3073"><path + d="m 0,763.14 0.29999,0 0,28.86 L 0,792 0,763.14 z" + id="path3075" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3105"><path + d="m 35.28,48.42 50.22,0 0,-12.48 -50.22,0 0,12.48 z" + clip-rule="evenodd" + id="path3107" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3111"><path + d="m 0,792.03 612,0 0,-792 -612,0 0,792 z" + id="path3113" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3321"><path + d="m 161.64,724.98 324.72,0 0,-271.02 -324.72,0 0,271.02 z" + clip-rule="evenodd" + id="path3323" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3327"><path + d="m 109.8,726.84 428.46,0 0,-274.74 -428.46,0 0,274.74 z" + clip-rule="evenodd" + id="path3329" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3333"><path + d="m 72,735.84 504,0 0,-503.76 -504,0 0,503.76 z" + clip-rule="evenodd" + id="path3335" + inkscape:connector-curvature="0" /></clipPath><clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath3339"><path + d="m 0,792.03 612,0 0,-792 -612,0 0,792 z" + id="path3341" + inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1230" + inkscape:window-height="873" + id="namedview2987" + showgrid="true" + inkscape:zoom="1.6066596" + inkscape:cx="241.9873" + inkscape:cy="159.0032" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="g2993" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0"><inkscape:grid + type="xygrid" + id="grid3962" + empspacing="5" + visible="true" + enabled="true" + snapvisiblegridlinesonly="true" /></sodipodi:namedview><g + id="g2993" + inkscape:groupmode="layer" + inkscape:label="SAMD20" + transform="matrix(1.25,0,0,-1.25,-183.91829,749.20451)"><g + transform="translate(179.56813,517.13011)" + id="g3343" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3345" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 11.82,35.04 44.58,58.56 81.48,58.5" /></g><g + transform="translate(183.04813,517.07011)" + id="g3347" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3349" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 -5.94,-8.88 -6.48,1.8 0,0" /></g><text + id="text3351" + transform="scale(0.99830575,-1.0016971)" + x="197.48271" + y="-537.51788" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3353" + sodipodi:role="line" + y="-537.51788" + x="197.48271 202.8923 204.69655 209.50446 211.90959 216.71907 219.12421 226.3349 230.54115 235.35063 240.16011" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">P + Timeout</tspan></text> + +<g + transform="translate(202.96813,594.41011)" + id="g3355" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3357" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 19.08,4.56 39.12,2.64 56.94,-5.46" /></g><g + transform="translate(260.62813,592.31011)" + id="g3359" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3361" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 4.56,-6 -3,-6 0,0" /></g><text + id="text3363" + transform="scale(0.99830575,-1.0016971)" + x="223.32651" + y="-583.81927" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3365" + sodipodi:role="line" + y="-583.81927" + x="223.32651 229.33542 234.74501 240.1546 245.56419" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">RESET</tspan></text> + +<g + transform="translate(251.80813,384.53011)" + id="g3367" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3369" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C -36.66,3.84 -66.72,30.78 -74.7,66.9" /></g><g + transform="translate(251.14813,387.89011)" + id="g3371" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3373" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 9.9,-3.84 -0.36,-6.72 0,0" /></g><text + id="text3375" + transform="scale(0.99830575,-1.0016971)" + x="197.48271" + y="-420.29681" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3377" + sodipodi:role="line" + y="-420.29681" + x="197.48271 205.29744 208.30112 210.70624 213.1106 217.31683 219.72197 225.73088 231.73981 237.74872" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">Write ADDR</tspan><tspan + id="tspan3379" + sodipodi:role="line" + y="-410.71301" + x="214.91249 217.31683 222.72643" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">(S)</tspan></text> + +<g + transform="translate(147.22813,479.81011)" + id="g3381" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3383" + style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.72,28.44 28.32,28.44 43.98,28.44 56.7,15.72 56.7,0 56.7,-15.72 43.98,-28.44 28.32,-28.44 12.72,-28.44 0,-15.72 0,0" /></g><g + transform="translate(147.22813,479.81011)" + id="g3385" + style="fill:#8cc63f;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3387" + style="fill:#8cc63f;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.72,28.44 28.32,28.44 43.98,28.44 56.7,15.72 56.7,0 56.7,-15.72 43.98,-28.44 28.32,-28.44 12.72,-28.44 0,-15.72 0,0" /></g><text + id="text3389" + transform="scale(0.99831175,-1.0016911)" + x="165.02673" + y="-481.03674" + style="font-size:9.60000001999999950px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;fill:#000000;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3391" + sodipodi:role="line" + y="-481.03674" + x="165.02673 167.43201 174.64301 180.65474" + style="font-size:9.60000001999999950px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none">IDLE</tspan></text> + +<text + id="text3393" + transform="scale(0.99830575,-1.0016971)" + x="163.8257" + y="-470.79111" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;fill:#000000;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3395" + sodipodi:role="line" + y="-470.79111" + x="163.8257 166.83093 171.03717 175.84665 180.0529 184.86003" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">(0b01)</tspan></text> + +<g + transform="translate(202.96813,487.13011)" + id="g3397" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3399" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 54.66,12.54 111.42,12.96 166.26,1.26" /></g><g + transform="translate(369.16813,491.93011)" + id="g3401" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3403" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 5.82,-4.8 -1.5,-6.6 0,0" /></g><text + id="text3405" + transform="scale(0.99830575,-1.0016971)" + x="287.03442" + y="-485.22662" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3407" + y="-485.22662" + x="287.03442" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">S</tspan></text> + +<g + transform="translate(374.02813,479.81011)" + id="g3409" + style="fill:#f99d31;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3411" + style="fill:#f99d31;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.66,28.44 28.38,28.44 43.98,28.44 56.7,15.72 56.7,0 56.7,-15.72 43.98,-28.44 28.38,-28.44 12.66,-28.44 0,-15.72 0,0" /></g><g + transform="translate(374.02813,479.81011)" + id="g3413" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3415" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.66,28.44 28.38,28.44 43.98,28.44 56.7,15.72 56.7,0 56.7,-15.72 43.98,-28.44 28.38,-28.44 12.66,-28.44 0,-15.72 0,0" /></g><text + id="text3417" + transform="scale(0.99831175,-1.0016911)" + x="389.80621" + y="-481.03674" + style="font-size:9.60000001999999950px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000000000000002;stroke-miterlimit:4;stroke-dasharray:none;fill:#000000" + sodipodi:linespacing="125%"><tspan + id="tspan3419" + sodipodi:role="line" + y="-481.03674" + x="389.80621 397.01337 404.22052 410.89874" + style="font-size:9.60000001999999950px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000000000000002;stroke-miterlimit:4;stroke-dasharray:none">BUSY</tspan></text> + +<text + id="text3421" + transform="scale(0.99830575,-1.0016971)" + x="391.01059" + y="-470.79111" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001000000002;stroke-miterlimit:4;stroke-dasharray:none;fill:#000000" + sodipodi:linespacing="125%"><tspan + id="tspan3423" + sodipodi:role="line" + y="-470.79111" + x="391.01059 394.01584 398.22208 403.09174 407.29797 412.1051" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001000000002;stroke-miterlimit:4;stroke-dasharray:none">(0b11)</tspan></text> + +<g + transform="translate(212.02813,470.51011)" + id="g3425" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3427" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 53.82,-10.98 109.38,-10.32 162.96,1.92" /></g><g + transform="translate(213.52813,473.63011)" + id="g3429" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3431" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 -10.56,-1.2 -1.38,-6.6 0,0" /></g><text + id="text3433" + transform="scale(0.99830575,-1.0016971)" + x="266.59982" + y="-467.7962" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3435" + sodipodi:role="line" + y="-467.7962" + x="266.59982 272.0094 274.41455 279.22244 281.0267 285.83618 288.2413 295.452 299.65823 304.46774 309.27719" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">P + Timeout</tspan></text> + +<g + transform="translate(260.62813,570.83011)" + id="g3437" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3439" + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.72,28.44 28.32,28.44 44.04,28.44 56.7,15.72 56.7,0 56.7,-15.72 44.04,-28.44 28.32,-28.44 12.72,-28.44 0,-15.72 0,0" /></g><g + transform="translate(260.62813,570.83011)" + id="g3441" + style="stroke-width:0.40000000000000002;stroke-miterlimit:4;stroke-dasharray:none;fill:#dcdcdc"><path + inkscape:connector-curvature="0" + id="path3443" + style="fill:#dcdcdc;stroke:#000000;stroke-width:0.40000000000000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.72,28.44 28.32,28.44 44.04,28.44 56.7,15.72 56.7,0 56.7,-15.72 44.04,-28.44 28.32,-28.44 12.72,-28.44 0,-15.72 0,0" /></g><text + id="text3445" + transform="scale(0.99831175,-1.0016911)" + x="263.59314" + y="-571.78314" + style="font-size:9.60000001999999950px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3447" + sodipodi:role="line" + y="-571.78314" + x="263.59314 270.80414 278.01514 285.22617 292.43716 299.65103 308.66815" + style="font-size:9.60000001999999950px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none">UNKNOWN</tspan></text> + +<text + id="text3449" + transform="scale(0.99830575,-1.0016971)" + x="277.41815" + y="-561.53711" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3451" + sodipodi:role="line" + y="-561.53711" + x="277.41815 280.4234 284.62964 289.43912 293.64536 298.45248" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">(0b00)</tspan></text> + +<g + transform="translate(260.62813,388.79011)" + id="g3453" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3455" + style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.72,28.44 28.32,28.44 44.04,28.44 56.7,15.72 56.7,0 56.7,-15.72 44.04,-28.44 28.32,-28.44 12.72,-28.44 0,-15.72 0,0" /></g><g + transform="translate(260.62813,388.79011)" + id="g3457" + style="fill:#cdeae8;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3459" + style="fill:#cdeae8;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 0,15.72 12.72,28.44 28.32,28.44 44.04,28.44 56.7,15.72 56.7,0 56.7,-15.72 44.04,-28.44 28.32,-28.44 12.72,-28.44 0,-15.72 0,0" /></g><text + id="text3461" + transform="scale(0.99831175,-1.0016911)" + x="270.80533" + y="-390.23019" + style="font-size:9.60000001999999950px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3463" + sodipodi:role="line" + y="-390.23019" + x="270.80533 278.0192 287.03632 294.24734 300.85822" + style="font-size:9.60000001999999950px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none">OWNER</tspan></text> + +<text + id="text3465" + transform="scale(0.99830575,-1.0016971)" + x="277.41815" + y="-380.04514" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3467" + sodipodi:role="line" + y="-380.04514" + x="277.41815 280.4234 284.62964 289.43912 293.64536 298.45248" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">(0b10)</tspan></text> + +<g + transform="translate(399.46813,442.37011)" + id="g3469" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3471" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C -11.22,-36.12 -45,-60.3 -82.68,-59.22" /></g><g + transform="translate(395.98813,442.37011)" + id="g3473" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3475" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 5.7,9 6.54,-1.62 0,0" /></g><text + id="text3477" + transform="scale(0.99830575,-1.0016971)" + x="338.72202" + y="-419.69794" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3479" + sodipodi:role="line" + y="-419.69794" + x="338.72202 344.73093 347.73462 352.5441 354.94922 357.35358 360.35724 364.56348 366.96783 369.37296 374.18243" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">Arbitration</tspan><tspan + id="tspan3481" + sodipodi:role="line" + y="-410.11414" + x="350.74219 355.55167 360.36115 364.56738" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">Lost</tspan></text> + +<g + transform="translate(209.44813,463.55011)" + id="g3483" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3485" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 23.58,-13.68 44.7,-31.32 62.4,-52.08" /></g><g + transform="translate(208.54813,460.19011)" + id="g3487" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3489" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 -7.14,7.8 3.3,5.88 0,0" /></g><g + transform="translate(294.64813,360.89011)" + id="g3491" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3493" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 0,0 c 4.5,-3.12 5.64,-9.36 2.52,-13.92 -3.12,-4.5 -9.3,-5.64 -13.86,-2.52 -3.78,2.64 -5.28,7.5 -3.66,11.82" /></g><g + transform="translate(276.52813,357.71011)" + id="g3495" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3497" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 6.78,3.18 5.28,-4.14 0,0" /></g><text + id="text3499" + transform="scale(0.99830575,-1.0016971)" + x="247.36723" + y="-435.93027" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3501" + sodipodi:role="line" + y="-435.93027" + x="247.36723 253.37614 258.18564 265.39633 272.60703 276.81329 281.62277 286.43222 288.83737" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">Command P</tspan><tspan + id="tspan3503" + sodipodi:role="line" + y="-351.17404" + x="304.46368 312.27841 315.28207 317.68723 320.09155 324.29779 326.70294 332.71185 338.72076 344.72968 350.73862 351.94196 354.34631 359.75589 362.75958" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">Write ADDR (Sr)</tspan></text> + +<g + transform="translate(430.12813,485.51011)" + id="g3505" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3507" + style="fill:none;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="M 0,0 C 3.18,5.4 10.08,7.2 15.42,4.08 20.76,0.9 22.62,-6 19.5,-11.4 17.04,-15.6 12.3,-17.7 7.56,-16.8" /></g><g + transform="translate(436.42813,465.47011)" + id="g3509" + style="stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none"><path + inkscape:connector-curvature="0" + id="path3511" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.4;stroke-miterlimit:4;stroke-dasharray:none" + d="M 0,0 -6.3,8.64 3.9,5.52 0,0" /></g><text + id="text3513" + transform="scale(0.99830575,-1.0016971)" + x="438.55115" + y="-494.27127" + style="font-size:9.60000012000000070px;-inkscape-font-specification:Arial;font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none" + sodipodi:linespacing="125%"><tspan + id="tspan3515" + sodipodi:role="line" + y="-494.27127" + x="438.55115 443.95996" + style="font-size:9.60000012000000070px;font-variant:normal;font-weight:normal;font-stretch:normal;writing-mode:lr;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;font-style:normal;text-anchor:start;text-align:start;line-height:125%;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none">Sr</tspan></text> + +</g></svg> diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_topology.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_topology.svg new file mode 100644 index 000000000..80df58f30 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/bus_topology.svg @@ -0,0 +1,351 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<!-- Generated by Microsoft Visio 11.0, SVG Export, v1.0 bus_topology.svg Page-1 --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="7.30346in" + height="2.76924in" viewBox="0 0 525.849 199.385" xml:space="preserve" color-interpolation-filters="sRGB" class="st13"> + <v:documentProperties v:langID="1033" v:metric="true" v:viewMarkup="false"/> + + <style type="text/css"> + <![CDATA[ + .st1 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24} + .st2 {fill:#000000;font-family:Arial;font-size:1.00001em;font-weight:bold} + .st3 {font-size:1em} + .st4 {fill:#ffffff;stroke:#000000;stroke-dasharray:1.68,1.2;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24} + .st5 {stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24} + .st6 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72} + .st7 {fill:#000000;font-family:Arial;font-size:0.833336em} + .st8 {baseline-shift:-32.4947%;font-size:0.649894em} + .st9 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24} + .st10 {stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.2} + .st11 {baseline-shift:-32.4941%;font-size:0.649882em} + .st12 {fill:#000000;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24} + .st13 {fill:none;fill-rule:evenodd;font-size:12;overflow:visible;stroke-linecap:square;stroke-miterlimit:3} + ]]> + </style> + + <g v:mID="0" v:index="1" v:groupContext="foregroundPage"> + <title>Page-1</title> + <v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394" + v:shadowOffsetY="-8.50394"/> + <g id="group1-1" transform="translate(0.24,-0.72)" v:mID="1" v:groupContext="group"> + <title>Sheet.1</title> + <g id="shape2-2" v:mID="2" v:groupContext="shape" transform="translate(155.906,-113.386)"> + <title>Box.448</title> + <desc>I2C DEVICE #1</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="42.5197" cy="171.039" width="85.04" height="56.6929"/> + <rect x="0" y="142.692" width="85.0394" height="56.6929" class="st1"/> + <text x="9.18" y="167.44" class="st2" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>I2C DEVICE <tspan + x="35.85" dy="1.2em" class="st3">#</tspan>1</text> </g> + <g id="shape3-6" v:mID="3" v:groupContext="shape" transform="translate(180.709,-70.8661)"> + <title>Box.450</title> + <path d="M0 199.39 L7.09 199.39 L7.09 171.04 L0 171.04 L0 199.39 Z" class="st4"/> + </g> + <g id="shape4-8" v:mID="4" v:groupContext="shape" transform="translate(209.055,-70.8661)"> + <title>Box.451</title> + <path d="M0 199.39 L7.09 199.39 L7.09 171.04 L0 171.04 L0 199.39 Z" class="st4"/> + </g> + <g id="shape5-10" v:mID="5" v:groupContext="shape" transform="translate(184.252,-99.2126)"> + <title>Bottom to top 1</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape6-13" v:mID="6" v:groupContext="shape" transform="translate(212.598,-99.2126)"> + <title>Bottom to top 1.453</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape7-16" v:mID="7" v:groupContext="shape" transform="translate(212.598,-56.6929)"> + <title>Bottom to top 1.454</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape8-19" v:mID="8" v:groupContext="shape" transform="translate(184.252,-28.3465)"> + <title>Bottom to top 1.455</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 156.87 L0 178.13 L0 199.39" class="st5"/> + </g> + <g id="shape9-22" v:mID="9" v:groupContext="shape" transform="translate(109.843,-127.559)"> + <title>Box.449</title> + <rect x="0" y="171.039" width="7.08661" height="28.3465" class="st1"/> + </g> + <g id="shape10-24" v:mID="10" v:groupContext="shape" transform="translate(113.386,-155.906)"> + <title>Bottom to top 1.456</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 171.04 L0 185.21 L0 199.39" class="st5"/> + </g> + <g id="shape11-27" v:mID="11" v:groupContext="shape" transform="translate(113.386,-56.6929)"> + <title>Bottom to top 1.457</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 128.52 L0 163.95 L0 199.39" class="st5"/> + </g> + <g id="shape12-30" v:mID="12" v:groupContext="shape" transform="translate(67.3228,-127.559)"> + <title>Box.447</title> + <rect x="0" y="171.039" width="7.08661" height="28.3465" class="st1"/> + </g> + <g id="shape13-32" v:mID="13" v:groupContext="shape" transform="translate(70.8661,-155.906)"> + <title>Bottom to top 1.458</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 171.04 L0 185.21 L0 199.39" class="st5"/> + </g> + <g id="shape14-35" v:mID="14" v:groupContext="shape" transform="translate(70.8661,-28.3465)"> + <title>Bottom to top 1.459</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 100.17 L0 149.78 L0 199.39" class="st5"/> + </g> + <g id="shape15-38" v:mID="15" v:groupContext="shape" transform="translate(77.9528,-141.732)"> + <title>Sheet.15</title> + <desc>RP</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">P</tspan></text> </g> + <g id="shape16-42" v:mID="16" v:groupContext="shape" transform="translate(120.472,-141.732)"> + <title>Sheet.16</title> + <desc>RP</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">P</tspan></text> </g> + <g id="shape17-46" v:mID="17" v:groupContext="shape" transform="translate(187.795,-77.9528)"> + <title>Sheet.17</title> + <desc>RS</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">S</tspan></text> </g> + <g id="shape18-50" v:mID="18" v:groupContext="shape" transform="translate(219.685,-77.9528)"> + <title>Sheet.18</title> + <desc>RS</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">S</tspan></text> </g> + <g id="shape19-54" v:mID="19" v:groupContext="shape" transform="translate(-3.59712E-014,-42.5197)"> + <title>Box.211</title> + <desc>SDA</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="21.2598" cy="185.212" width="42.52" height="28.3465"/> + <rect x="0" y="171.039" width="42.5197" height="28.3465" class="st9"/> + <text x="13.18" y="188.81" class="st2" v:langID="1044"><v:paragraph v:horizAlign="2"/><v:tabList/>SDA</text> </g> + <g id="shape20-57" v:mID="20" v:groupContext="shape" transform="translate(-3.59712E-014,-14.1732)"> + <title>Box.465</title> + <desc>SCL</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="21.2598" cy="185.212" width="42.52" height="28.3465"/> + <rect x="0" y="171.039" width="42.5197" height="28.3465" class="st9"/> + <text x="14.52" y="188.81" class="st2" v:langID="1044"><v:paragraph v:horizAlign="2"/><v:tabList/>SCL</text> </g> + <g id="shape21-60" v:mID="21" v:groupContext="shape" transform="translate(42.5197,-56.6929)"> + <title>Side to side 1</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 199.39 L240.94 199.39 L481.89 199.39" class="st10"/> + </g> + <g id="shape22-63" v:mID="22" v:groupContext="shape" transform="translate(42.5197,-28.3465)"> + <title>Side to side 1.467</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 199.39 L240.94 199.39 L481.89 199.39" class="st10"/> + </g> + <g id="shape23-66" v:mID="23" v:groupContext="shape" transform="translate(198.425,-170.079)"> + <title>Bottom to top 1.468</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape24-69" v:mID="24" v:groupContext="shape" transform="translate(42.5197,-184.252)"> + <title>Side to side 1.469</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 199.39 L240.94 199.39 L481.89 199.39" class="st10"/> + </g> + <g id="shape25-72" v:mID="25" v:groupContext="shape" transform="translate(-3.59712E-014,-170.079)"> + <title>Box.470</title> + <desc>VCC</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="21.2598" cy="185.212" width="42.52" height="28.3465"/> + <rect x="0" y="171.039" width="42.5197" height="28.3465" class="st9"/> + <text x="19.25" y="188.81" class="st2" v:langID="1044"><v:paragraph v:horizAlign="2"/><v:tabList/>V<tspan + dy="-0.287em" class="st11" v:baseFontSize="12">CC</tspan></text> </g> + <g id="shape26-76" v:mID="26" v:groupContext="shape" transform="translate(269.291,-113.386)"> + <title>Box.471</title> + <desc>I2C DEVICE #2</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="42.5197" cy="171.039" width="85.04" height="56.6929"/> + <rect x="0" y="142.692" width="85.0394" height="56.6929" class="st1"/> + <text x="9.18" y="167.44" class="st2" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>I2C DEVICE <tspan + x="35.85" dy="1.2em" class="st3">#</tspan>2</text> </g> + <g id="shape27-80" v:mID="27" v:groupContext="shape" transform="translate(294.094,-70.8661)"> + <title>Box.472</title> + <path d="M0 199.39 L7.09 199.39 L7.09 171.04 L0 171.04 L0 199.39 Z" class="st4"/> + </g> + <g id="shape28-82" v:mID="28" v:groupContext="shape" transform="translate(322.441,-70.8661)"> + <title>Box.473</title> + <path d="M0 199.39 L7.09 199.39 L7.09 171.04 L0 171.04 L0 199.39 Z" class="st4"/> + </g> + <g id="shape29-84" v:mID="29" v:groupContext="shape" transform="translate(297.638,-99.2126)"> + <title>Bottom to top 1.474</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape30-87" v:mID="30" v:groupContext="shape" transform="translate(325.984,-99.2126)"> + <title>Bottom to top 1.475</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape31-90" v:mID="31" v:groupContext="shape" transform="translate(325.984,-56.6929)"> + <title>Bottom to top 1.476</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape32-93" v:mID="32" v:groupContext="shape" transform="translate(297.638,-28.3465)"> + <title>Bottom to top 1.477</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 156.87 L0 178.13 L0 199.39" class="st5"/> + </g> + <g id="shape33-96" v:mID="33" v:groupContext="shape" transform="translate(301.181,-77.9528)"> + <title>Sheet.33</title> + <desc>RS</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">S</tspan></text> </g> + <g id="shape34-100" v:mID="34" v:groupContext="shape" transform="translate(333.071,-77.9528)"> + <title>Sheet.34</title> + <desc>RS</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">S</tspan></text> </g> + <g id="shape35-104" v:mID="35" v:groupContext="shape" transform="translate(311.811,-170.079)"> + <title>Bottom to top 1.480</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape36-107" v:mID="36" v:groupContext="shape" transform="translate(425.197,-113.386)"> + <title>Box.481</title> + <desc>I2C DEVICE #N</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="42.5197" cy="171.039" width="85.04" height="56.6929"/> + <rect x="0" y="142.692" width="85.0394" height="56.6929" class="st1"/> + <text x="9.18" y="167.44" class="st2" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>I2C DEVICE <tspan + x="34.85" dy="1.2em" class="st3">#</tspan>N</text> </g> + <g id="shape37-111" v:mID="37" v:groupContext="shape" transform="translate(450,-70.8661)"> + <title>Box.482</title> + <path d="M0 199.39 L7.09 199.39 L7.09 171.04 L0 171.04 L0 199.39 Z" class="st4"/> + </g> + <g id="shape38-113" v:mID="38" v:groupContext="shape" transform="translate(478.346,-70.8661)"> + <title>Box.483</title> + <path d="M0 199.39 L7.09 199.39 L7.09 171.04 L0 171.04 L0 199.39 Z" class="st4"/> + </g> + <g id="shape39-115" v:mID="39" v:groupContext="shape" transform="translate(453.543,-99.2126)"> + <title>Bottom to top 1.484</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape40-118" v:mID="40" v:groupContext="shape" transform="translate(481.89,-99.2126)"> + <title>Bottom to top 1.485</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape41-121" v:mID="41" v:groupContext="shape" transform="translate(481.89,-56.6929)"> + <title>Bottom to top 1.486</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape42-124" v:mID="42" v:groupContext="shape" transform="translate(453.543,-28.3465)"> + <title>Bottom to top 1.487</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 156.87 L0 178.13 L0 199.39" class="st5"/> + </g> + <g id="shape43-127" v:mID="43" v:groupContext="shape" transform="translate(457.087,-77.9528)"> + <title>Sheet.43</title> + <desc>RS</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">S</tspan></text> </g> + <g id="shape44-131" v:mID="44" v:groupContext="shape" transform="translate(488.976,-77.9528)"> + <title>Sheet.44</title> + <desc>RS</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="10.6299" cy="192.299" width="21.26" height="14.1732"/> + <rect x="0" y="185.212" width="21.2598" height="14.1732" class="st6"/> + <text x="4" y="195.3" class="st7" v:langID="1033"><v:paragraph/><v:tabList/>R<tspan dy="-0.287em" class="st8" + v:langID="1044" v:baseFontSize="10">S</tspan></text> </g> + <g id="shape45-135" v:mID="45" v:groupContext="shape" transform="translate(467.717,-170.079)"> + <title>Bottom to top 1.490</title> + <v:userDefs> + <v:ud v:nameU="TextPos" v:val="VT0(1):26"/> + </v:userDefs> + <path d="M0 185.21 L0 192.3 L0 199.39" class="st5"/> + </g> + <g id="shape46-138" v:mID="46" v:groupContext="shape" transform="translate(372.047,-138.189)"> + <title>Sheet.46</title> + <ellipse cx="3.54331" cy="195.842" rx="3.54331" ry="3.54331" class="st12"/> + </g> + <g id="shape47-140" v:mID="47" v:groupContext="shape" transform="translate(386.22,-138.189)"> + <title>Sheet.47</title> + <ellipse cx="3.54331" cy="195.842" rx="3.54331" ry="3.54331" class="st12"/> + </g> + <g id="shape48-142" v:mID="48" v:groupContext="shape" transform="translate(400.394,-138.189)"> + <title>Sheet.48</title> + <ellipse cx="3.54331" cy="195.842" rx="3.54331" ry="3.54331" class="st12"/> + </g> + <g id="shape49-144" v:mID="49" v:groupContext="shape" transform="translate(411.024,0)"> + <title>Sheet.49</title> + <desc>Note: RS is optional</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="56.6929" cy="185.212" width="113.39" height="28.3465"/> + <rect x="0" y="171.039" width="113.386" height="28.3465" class="st6"/> + <text x="23.34" y="188.21" class="st7" v:langID="1033"><v:paragraph v:horizAlign="2"/><v:tabList/>Note: R<tspan + dy="-0.287em" class="st8" v:langID="1044" v:baseFontSize="10">S</tspan><tspan dy="0.186em" class="st3" + v:langID="1044"> </tspan><tspan class="st3" v:langID="1044">is optional</tspan></text> </g> + </g> + </g> +</svg> diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_read.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_read.svg new file mode 100644 index 000000000..5be8085d9 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_read.svg @@ -0,0 +1,1037 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1062.9928" + height="39.954796" + id="svg2" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="i2c_read.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.4" + inkscape:cx="431.58384" + inkscape:cy="191.3885" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1920" + inkscape:window-height="1138" + inkscape:window-x="1042" + inkscape:window-y="123" + inkscape:window-maximized="1" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + units="cm" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-13.760627,-247.09327)"> + <g + id="g3025-8-1" + transform="matrix(0.2273934,0,0,0.2273934,337.19144,210.19696)"> + <g + id="g3084-7"> + <g + id="g3137"> + <rect + y="250.80614" + x="29.7995" + height="84.852814" + width="160.10918" + id="rect2985-2-1" + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-45-1" + y="305.35434" + x="68.236969" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="305.35434" + x="68.236969" + id="tspan2989-5-5" + sodipodi:role="line">ACK</tspan></text> + </g> + </g> + </g> + <g + id="g3367" + transform="matrix(0.2273934,0,0,0.2273934,10.929948,221.47683)" /> + <g + id="g3651" + transform="matrix(0.2273934,0,0,0.2273934,10.929948,221.47683)"> + <g + transform="translate(-15.152288,-49.497478)" + id="g3025"> + <rect + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985" + width="160.10918" + height="84.852814" + x="29.7995" + y="250.80614" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="45.961941" + y="307.37466" + id="text2987" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989" + x="45.961941" + y="307.37466">START</tspan></text> + </g> + <g + transform="translate(0.08928571,0.54971913)" + id="g3371"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363"> + <text + sodipodi:linespacing="125%" + id="text2987-41" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1" + sodipodi:role="line">Bit 0</tspan></text> + </g> + </g> + </g> + <g + id="g3025-0" + transform="matrix(0.2273934,0,0,0.2273934,44.171155,210.21446)"> + <rect + y="250.80614" + x="29.799496" + height="84.852814" + width="1126" + id="rect2985-9" + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-4" + y="307.37466" + x="45.961941" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="307.37466" + x="45.961941" + id="tspan2989-8" + sodipodi:role="line">ADDRESS</tspan></text> + </g> + <g + id="g3572" + transform="matrix(0.2273934,0,0,0.2273934,10.929948,221.47683)"> + <g + transform="translate(161.20365,0.3799057)" + id="g3371-3"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2" + sodipodi:role="line">Bit 1</tspan></text> + </g> + </g> + <g + transform="translate(322.34682,0.36425801)" + id="g3371-3-9"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0" + sodipodi:role="line">Bit 2</tspan></text> + </g> + </g> + <g + transform="translate(483.53819,0.33959304)" + id="g3371-3-2"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-86" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-0"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-2" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-4" + sodipodi:role="line">Bit 3</tspan></text> + </g> + </g> + <g + transform="translate(644.68136,0.32394304)" + id="g3371-3-9-8"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-6" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6-5"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-0" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-9" + sodipodi:role="line">Bit 4</tspan></text> + </g> + </g> + <g + transform="translate(805.87366,0.28165828)" + id="g3371-3-0"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-0" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-61"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-3" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-8" + sodipodi:role="line">Bit 5</tspan></text> + </g> + </g> + <g + transform="translate(967.01682,0.26600828)" + id="g3371-3-9-9"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-3" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6-4"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-4" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-6" + sodipodi:role="line">Bit 6</tspan></text> + </g> + </g> + <g + transform="translate(1128.2082,0.24133828)" + id="g3371-3-2-0"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-86-6" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-0-6"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-2-1" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-4-8" + sodipodi:role="line">Bit 7</tspan></text> + </g> + </g> + </g> + <g + id="g3025-8" + transform="matrix(0.2273934,0,0,0.2273934,300.48366,210.19696)"> + <g + id="g3084"> + <rect + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-2" + width="160.10918" + height="84.852814" + x="29.7995" + y="250.80614" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="57.578697" + y="306.3645" + id="text2987-45" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-5" + x="57.578697" + y="306.3645">READ</tspan></text> + </g> + </g> + <g + id="g3371-3-9-8-4" + transform="matrix(0.2273934,0,0,0.2273934,303.94836,221.52814)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-9" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-5-6"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-7" + x="51.433456" + y="170.37161">Bit 8</tspan></text> + </g> + </g> + <g + id="g3371-3-9-8-4-8" + transform="matrix(0.2273934,0,0,0.2273934,340.65614,221.52499)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-9-8" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-5-6-2"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-3-9" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-7-1" + x="51.433456" + y="170.37161">Bit 9</tspan></text> + </g> + </g> + <g + id="g4012" + transform="matrix(0.2273934,0,0,0.2273934,10.929948,221.47683)"> + <g + transform="translate(1601.3444,-51.122901)" + id="g3137-6"> + <g + transform="translate(-5.2678572,1.5178572)" + id="g3184"> + <rect + y="250.80614" + x="29.799498" + height="84.852814" + width="1287" + id="rect2985-2-1-1" + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-45-1-4" + y="305.35434" + x="68.236969" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="305.35434" + x="68.236969" + id="tspan2989-5-5-2" + sodipodi:role="line">DATA</tspan></text> + </g> + </g> + <g + id="g3970"> + <g + id="g3371-3-5" + transform="translate(1611.2553,0.1918423)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-9" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-8"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-07" + x="51.433456" + y="170.37161">Bit 10</tspan></text> + </g> + </g> + <g + id="g3371-3-9-6" + transform="translate(1772.3984,0.17619461)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-36" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-1"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-5" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-4" + x="51.433456" + y="170.37161">Bit 11</tspan></text> + </g> + </g> + <g + id="g3371-3-2-2" + transform="translate(1933.5898,0.15152964)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-9"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-7" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-3" + x="51.433456" + y="170.37161">Bit 12</tspan></text> + </g> + </g> + <g + id="g3371-3-9-8-7" + transform="translate(2094.733,0.13587964)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-2" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-5-60"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-6" + x="51.433456" + y="170.37161">Bit 13</tspan></text> + </g> + </g> + <g + id="g3371-3-0-5" + transform="translate(2255.9253,0.09359488)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-0-7" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-61-5"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-3-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-8-1" + x="51.433456" + y="170.37161">Bit 14</tspan></text> + </g> + </g> + <g + id="g3371-3-9-9-2" + transform="translate(2417.0684,0.07794488)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-3-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-4-0"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-4-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-6-4" + x="51.433456" + y="170.37161">Bit 15</tspan></text> + </g> + </g> + <g + id="g3371-3-2-0-6" + transform="translate(2578.1601,0.05327488)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-6-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-6-7"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-1-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-8-7" + x="51.433456" + y="170.37161">Bit 16</tspan></text> + </g> + </g> + <g + id="g3371-3-2-0-6-7" + transform="translate(2739.1003,0.03811784)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-6-0-7" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-6-7-7"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-1-1-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-8-7-3" + x="51.433456" + y="170.37161">Bit 17</tspan></text> + </g> + </g> + </g> + </g> + <g + transform="matrix(0.2273934,0,0,0.2273934,10.929948,221.4397)" + id="g3234"> + <rect + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-2-1-16" + width="160.10918" + height="84.852814" + x="2914.0579" + y="201.36435" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="2952.4954" + y="255.91255" + id="text2987-45-1-8" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-5-5-5" + x="2952.4954" + y="255.91255">ACK</tspan></text> + </g> + <g + id="g3363-7-6-5-6-9" + transform="matrix(0.2273934,0,0,0.2273934,670.25138,221.5058)"> + <g + id="g4454"> + <rect + transform="translate(-2899.4747,-0.12740393)" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-9-9" + width="160.10918" + height="84.852814" + x="2914.0376" + y="114.94062" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-3-8" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-7-18" + x="51.433456" + y="170.37161">Bit 18</tspan></text> + </g> + </g> + <g + transform="matrix(0.2273934,0,0,0.2273934,340.5387,221.48936)" + id="g4012-2"> + <g + id="g4349"> + <g + id="g3137-6-6" + transform="translate(1601.3444,-51.122901)"> + <g + id="g3184-6" + transform="translate(-5.2678572,1.5178572)"> + <rect + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-2-1-1-0" + width="1287" + height="84.852814" + x="29.799498" + y="250.80614" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="68.236969" + y="305.35434" + id="text2987-45-1-4-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-5-5-2-8" + x="68.236969" + y="305.35434">DATA</tspan></text> + </g> + </g> + <g + id="g4307"> + <g + transform="translate(1611.2553,0.1918423)" + id="g3371-3-5-1"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-9-2" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-8-5"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-4-0" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-07-9" + sodipodi:role="line">Bit 19</tspan></text> + </g> + </g> + <g + transform="translate(1772.3984,0.17619461)" + id="g3371-3-9-6-4"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-36-7" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6-1-8"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-5-3" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-4-5" + sodipodi:role="line">Bit 20</tspan></text> + </g> + </g> + <g + transform="translate(1933.5898,0.15152964)" + id="g3371-3-2-2-1"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-86-0-2" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-0-9-0"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-2-7-1" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-4-3-6" + sodipodi:role="line">Bit 21</tspan></text> + </g> + </g> + <g + transform="translate(2094.733,0.13587964)" + id="g3371-3-9-8-7-4"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-6-2-0" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6-5-60-6"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-0-1-1" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-9-6-8" + sodipodi:role="line">Bit 22</tspan></text> + </g> + </g> + <g + transform="translate(2255.9253,0.09359488)" + id="g3371-3-0-5-9"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-0-7-8" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-61-5-4"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-3-4-1" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-8-1-4" + sodipodi:role="line">Bit 23</tspan></text> + </g> + </g> + <g + transform="translate(2417.0684,0.07794488)" + id="g3371-3-9-9-2-3"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-3-0-9" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6-4-0-8"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-4-1-8" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-6-4-0" + sodipodi:role="line">Bit 24</tspan></text> + </g> + </g> + <g + transform="translate(2578.1601,0.05327488)" + id="g3371-3-2-0-6-8"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-86-6-0-77" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-0-6-7-8"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-2-1-1-38" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-4-8-7-37" + sodipodi:role="line">Bit 25</tspan></text> + </g> + </g> + <g + transform="translate(2739.1003,0.03811784)" + id="g3371-3-2-0-6-7-1"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-86-6-0-7-0" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-0-6-7-7-7"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-2-1-1-3-3" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-4-8-7-3-4" + sodipodi:role="line">Bit 26</tspan></text> + </g> + </g> + </g> + </g> + </g> + <g + id="g4561" + transform="matrix(0.2273934,0,0,0.2273934,10.929948,221.47683)"> + <g + transform="translate(1449.4207,-0.08294312)" + id="g3296"> + <rect + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-2-1-16-7" + width="160.10918" + height="84.852814" + x="2914.0579" + y="201.36435" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="2941.0669" + y="258.05542" + id="text2987-45-1-8-9" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-5-5-5-5" + x="2941.0669" + y="258.05542">NACK</tspan></text> + </g> + <g + transform="translate(4348.9157,0.18342068)" + id="g4499"> + <rect + y="114.94062" + x="2914.0376" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-6-9-9-9" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="translate(-2899.4747,-0.12740393)" /> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-0-3-8-9" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-9-7-18-6" + sodipodi:role="line">Bit 27</tspan></text> + </g> + </g> + <g + id="g4571" + transform="matrix(0.2273934,0,0,0.2273934,10.929948,221.47683)"> + <g + transform="translate(1610.7689,-0.16325573)" + id="g3296-4"> + <g + id="g3328"> + <rect + y="201.36435" + x="2914.0579" + height="84.852814" + width="160.10918" + id="rect2985-2-1-16-7-3" + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-45-1-8-9-1" + y="257.04526" + x="2945.1074" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="257.04526" + x="2945.1074" + id="tspan2989-5-5-5-5-2" + sodipodi:role="line">STOP</tspan></text> + </g> + </g> + <g + transform="translate(4510.1629,0.12740431)" + id="g4454-8"> + <rect + transform="translate(-2899.4747,-0.12740393)" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.39766502px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-9-9-3" + width="160.10918" + height="84.852814" + x="2914.0376" + y="114.94062" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-3-8-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-7-18-8" + x="51.433456" + y="170.37161">Bit 28</tspan></text> + </g> + </g> + </g> +</svg> diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_write.svg b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_write.svg new file mode 100644 index 000000000..6f2628100 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/docimg/i2c_write.svg @@ -0,0 +1,1031 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1062.9977" + height="39.199993" + id="svg2" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="i2c_write.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.9899495" + inkscape:cx="-488.42509" + inkscape:cy="-132.2365" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1920" + inkscape:window-height="1138" + inkscape:window-x="1042" + inkscape:window-y="123" + inkscape:window-maximized="1" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + units="cm" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-994.63325,-178.88436)"> + <g + id="g3367" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)" /> + <g + id="g3651" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + transform="translate(-15.152288,-49.497478)" + id="g3025"> + <rect + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985" + width="160.10918" + height="84.852814" + x="29.7995" + y="250.80614" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="45.961941" + y="307.37466" + id="text2987" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989" + x="45.961941" + y="307.37466">START</tspan></text> + </g> + <g + transform="translate(0.08928571,0.54971913)" + id="g3371"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363"> + <text + sodipodi:linespacing="125%" + id="text2987-41" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1" + sodipodi:role="line">Bit 0</tspan></text> + </g> + </g> + </g> + <g + id="g3600" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + transform="translate(146.18369,-49.528111)" + id="g3025-0"> + <rect + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-9" + width="1126" + height="84.852814" + x="29.799496" + y="250.80614" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="45.961941" + y="307.37466" + id="text2987-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-8" + x="45.961941" + y="307.37466">ADDRESS</tspan></text> + </g> + <g + id="g3572"> + <g + id="g3371-3" + transform="translate(161.20365,0.3799057)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2" + x="51.433456" + y="170.37161">Bit 1</tspan></text> + </g> + </g> + <g + id="g3371-3-9" + transform="translate(322.34682,0.36425801)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0" + x="51.433456" + y="170.37161">Bit 2</tspan></text> + </g> + </g> + <g + id="g3371-3-2" + transform="translate(483.53819,0.33959304)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4" + x="51.433456" + y="170.37161">Bit 3</tspan></text> + </g> + </g> + <g + id="g3371-3-9-8" + transform="translate(644.68136,0.32394304)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-5"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9" + x="51.433456" + y="170.37161">Bit 4</tspan></text> + </g> + </g> + <g + id="g3371-3-0" + transform="translate(805.87366,0.28165828)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-61"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-8" + x="51.433456" + y="170.37161">Bit 5</tspan></text> + </g> + </g> + <g + id="g3371-3-9-9" + transform="translate(967.01682,0.26600828)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-3" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-4"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-6" + x="51.433456" + y="170.37161">Bit 6</tspan></text> + </g> + </g> + <g + id="g3371-3-2-0" + transform="translate(1128.2082,0.24133828)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-6" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-6"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-8" + x="51.433456" + y="170.37161">Bit 7</tspan></text> + </g> + </g> + </g> + </g> + <g + id="g3588" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + transform="translate(1273.3602,-49.605043)" + id="g3025-8"> + <g + id="g3084"> + <rect + y="250.80614" + x="29.7995" + height="84.852814" + width="160.10918" + id="rect2985-2" + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-45" + y="306.3645" + x="45.456867" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="306.3645" + x="45.456867" + id="tspan2989-5" + sodipodi:role="line">WRITE</tspan></text> + </g> + </g> + <g + transform="translate(1288.5968,0.22568828)" + id="g3371-3-9-8-4"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-6-9" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6-5-6"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-0-3" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-9-7" + sodipodi:role="line">Bit 8</tspan></text> + </g> + </g> + </g> + <g + id="g3575" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + transform="translate(1434.7887,-49.605043)" + id="g3025-8-1"> + <g + id="g3084-7"> + <g + id="g3137"> + <rect + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-2-1" + width="160.10918" + height="84.852814" + x="29.7995" + y="250.80614" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="68.236969" + y="305.35434" + id="text2987-45-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-5-5" + x="68.236969" + y="305.35434">ACK</tspan></text> + </g> + </g> + </g> + <g + transform="translate(1450.0253,0.21184383)" + id="g3371-3-9-8-4-8"> + <rect + y="114.81322" + x="14.562887" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-6-9-8" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3363-7-6-5-6-2"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-0-3-9" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-9-7-1" + sodipodi:role="line">Bit 9</tspan></text> + </g> + </g> + </g> + <g + id="g4012" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <rect + y="201.2011" + x="1625.876" + height="84.852814" + width="1287" + id="rect2985-2-1-1" + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-45-1-4" + y="255.7493" + x="1664.3135" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="255.7493" + x="1664.3135" + id="tspan2989-5-5-2" + sodipodi:role="line">DATA</tspan></text> + <g + id="g3970"> + <g + id="g3371-3-5" + transform="translate(1611.2553,0.1918423)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-9" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-8"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-07" + x="51.433456" + y="170.37161">Bit 10</tspan></text> + </g> + </g> + <g + id="g3371-3-9-6" + transform="translate(1772.3984,0.17619461)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-36" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-1"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-5" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-4" + x="51.433456" + y="170.37161">Bit 11</tspan></text> + </g> + </g> + <g + id="g3371-3-2-2" + transform="translate(1933.5898,0.15152964)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-9"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-7" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-3" + x="51.433456" + y="170.37161">Bit 12</tspan></text> + </g> + </g> + <g + id="g3371-3-9-8-7" + transform="translate(2094.733,0.13587964)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-2" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-5-60"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-6" + x="51.433456" + y="170.37161">Bit 13</tspan></text> + </g> + </g> + <g + id="g3371-3-0-5" + transform="translate(2255.9253,0.09359488)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-0-7" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-61-5"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-3-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-8-1" + x="51.433456" + y="170.37161">Bit 14</tspan></text> + </g> + </g> + <g + id="g3371-3-9-9-2" + transform="translate(2417.0684,0.07794488)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-3-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-4-0"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-4-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-6-4" + x="51.433456" + y="170.37161">Bit 15</tspan></text> + </g> + </g> + <g + id="g3371-3-2-0-6" + transform="translate(2578.1601,0.05327488)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-6-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-6-7"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-1-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-8-7" + x="51.433456" + y="170.37161">Bit 16</tspan></text> + </g> + </g> + <g + id="g3371-3-2-0-6-7" + transform="translate(2739.1003,0.03811784)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-6-0-7" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-6-7-7"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-1-1-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-8-7-3" + x="51.433456" + y="170.37161">Bit 17</tspan></text> + </g> + </g> + </g> + </g> + <g + id="g3564" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + id="g3559"> + <rect + y="201.2011" + x="2914.0579" + height="84.852814" + width="160.10918" + id="rect2985-2-1-16" + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-45-1-8" + y="255.7493" + x="2952.4954" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="255.7493" + x="2952.4954" + id="tspan2989-5-5-5" + sodipodi:role="line">ACK</tspan></text> + </g> + <g + transform="translate(2899.4747,0.12740393)" + id="g3363-7-6-5-6-9"> + <g + id="g4454"> + <rect + y="114.94062" + x="2914.0376" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-6-9-9" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="translate(-2899.4747,-0.12740393)" /> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-0-3-8" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-9-7-18" + sodipodi:role="line">Bit 18</tspan></text> + </g> + </g> + </g> + <rect + y="179.42236" + x="1727.759" + height="18.926369" + width="36.407738" + id="rect2985-3-8-8-36-7" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <g + id="g3498" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + id="g3361"> + <rect + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-2-1-1-0" + width="1287" + height="84.852814" + x="3075.3848" + y="201.2562" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="3113.8223" + y="255.8044" + id="text2987-45-1-4-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-5-5-2-8" + x="3113.8223" + y="255.8044">DATA</tspan></text> + </g> + <g + id="g3458"> + <g + id="g3371-3-5-1" + transform="translate(3060.7641,0.24694231)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-9-2" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-8-5"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-4-0" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-07-9" + x="51.433456" + y="170.37161">Bit 19</tspan></text> + </g> + </g> + <g + id="g3363-7-6-1-8" + transform="translate(3221.9072,0.23129462)"> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-5-3" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-4-5" + sodipodi:role="line">Bit 20</tspan></text> + </g> + <g + id="g3371-3-2-2-1" + transform="translate(3383.0986,0.20662965)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-0-2" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-9-0"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-7-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-3-6" + x="51.433456" + y="170.37161">Bit 21</tspan></text> + </g> + </g> + <g + id="g3371-3-9-8-7-4" + transform="translate(3544.2418,0.19097965)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-2-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-5-60-6"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-1-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-6-8" + x="51.433456" + y="170.37161">Bit 22</tspan></text> + </g> + </g> + <g + id="g3371-3-0-5-9" + transform="translate(3705.4341,0.14869489)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-0-7-8" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-61-5-4"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-3-4-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-8-1-4" + x="51.433456" + y="170.37161">Bit 23</tspan></text> + </g> + </g> + <g + id="g3371-3-9-9-2-3" + transform="translate(3866.5772,0.13304489)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-3-0-9" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-6-4-0-8"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-4-1-8" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-6-4-0" + x="51.433456" + y="170.37161">Bit 24</tspan></text> + </g> + </g> + <g + id="g3371-3-2-0-6-8" + transform="translate(4027.6689,0.10837489)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-6-0-77" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-6-7-8"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-1-1-38" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-8-7-37" + x="51.433456" + y="170.37161">Bit 25</tspan></text> + </g> + </g> + <g + id="g3371-3-2-0-6-7-1" + transform="translate(4188.6091,0.09321785)"> + <rect + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-86-6-0-7-0" + width="160.10918" + height="84.852814" + x="14.562887" + y="114.81322" /> + <g + id="g3363-7-0-6-7-7-7"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-2-1-1-3-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-4-8-7-3-4" + x="51.433456" + y="170.37161">Bit 26</tspan></text> + </g> + </g> + </g> + </g> + <g + id="g3351" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + id="g3346"> + <rect + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-2-1-16-7" + width="160.10918" + height="84.852814" + x="4363.4785" + y="201.2814" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="4402.6099" + y="257.97247" + id="text2987-45-1-8-9" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-5-5-5-5" + x="4402.6099" + y="257.97247">ACK</tspan></text> + </g> + <g + transform="translate(4348.9157,0.18342068)" + id="g4499"> + <rect + y="114.94062" + x="2914.0376" + height="84.852814" + width="160.10918" + id="rect2985-3-8-8-6-9-9-9" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + transform="translate(-2899.4747,-0.12740393)" /> + <text + sodipodi:linespacing="125%" + id="text2987-41-4-5-0-3-8-9" + y="170.37161" + x="51.433456" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="170.37161" + x="51.433456" + id="tspan2989-1-2-0-9-7-18-6" + sodipodi:role="line">Bit 27</tspan></text> + </g> + </g> + <g + id="g4571" + transform="matrix(0.22739325,0,0,0.22304957,991.80606,153.76187)"> + <g + transform="translate(1610.7689,-0.16325573)" + id="g3296-4"> + <g + id="g3328"> + <rect + y="201.36435" + x="2914.0579" + height="84.852814" + width="160.10918" + id="rect2985-2-1-16-7-3" + style="fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <text + sodipodi:linespacing="125%" + id="text2987-45-1-8-9-1" + y="257.04526" + x="2945.1074" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="257.04526" + x="2945.1074" + id="tspan2989-5-5-5-5-2" + sodipodi:role="line">STOP</tspan></text> + </g> + </g> + <g + transform="translate(4510.1629,0.12740431)" + id="g4454-8"> + <rect + transform="translate(-2899.4747,-0.12740393)" + style="fill:#333333;fill-rule:evenodd;stroke:#000000;stroke-width:4.4402833px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2985-3-8-8-6-9-9-3" + width="160.10918" + height="84.852814" + x="2914.0376" + y="114.94062" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="51.433456" + y="170.37161" + id="text2987-41-4-5-0-3-8-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan2989-1-2-0-9-7-18-8" + x="51.433456" + y="170.37161">Bit 28</tspan></text> + </g> + </g> + </g> +</svg> diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_common.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_common.h new file mode 100644 index 000000000..de4f0d23f --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_common.h @@ -0,0 +1,613 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Common Driver + * + * Copyright (c) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#ifndef I2C_COMMON_H_INCLUDED +#define I2C_COMMON_H_INCLUDED + +#include <compiler.h> +#include <sercom.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \if (I2C_MASTER_MODE && I2C_SLAVE_MODE) + * \defgroup asfdoc_sam0_sercom_i2c_group SAM I2C (SERCOM I2C) Driver + * \elseif I2C_MASTER_MODE + * \defgroup asfdoc_sam0_sercom_i2c_group SAM I2C Master Mode (SERCOM I2C) Driver + * \elseif I2C_SLAVE_MODE + * \defgroup asfdoc_sam0_sercom_i2c_group SAM I2C Slave Mode (SERCOM I2C) Driver + * \endif + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides + * an interface for the configuration and management of the device's SERCOM + * I<SUP>2</SUP>C module, for the transfer of data via an I<SUP>2</SUP>C bus. + * The following driver API modes are covered by this manual: + * + * \if I2C_MASTER_MODE + * - Master Mode Polled APIs + * \endif + * \if I2C_MASTER_CALLBACK_MODE + * - Master Mode Callback APIs + * \endif + * \if I2C_SLAVE_MODE + * - Slave Mode Polled APIs + * \endif + * \if I2C_SLAVE_CALLBACK_MODE + * - Slave Mode Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - SERCOM (Serial Communication Interface) + * + * The following devices can use this module: + * - Atmel | SMART SAM D20/D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D09/D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_sercom_i2c_prerequisites + * - \ref asfdoc_sam0_sercom_i2c_overview + * - \ref asfdoc_sam0_sercom_i2c_special_considerations + * - \ref asfdoc_sam0_sercom_i2c_extra + * - \ref asfdoc_sam0_sercom_i2c_examples + * - \ref asfdoc_sam0_sercom_i2c_api_overview + * + * \section asfdoc_sam0_sercom_i2c_prerequisites Prerequisites + * There are no prerequisites. + * + * \section asfdoc_sam0_sercom_i2c_overview Module Overview + * The outline of this section is as follows: + * - \ref asfdoc_sam0_sercom_i2c_module_features + * - \ref asfdoc_sam0_sercom_i2c_functional_desc + * - \ref asfdoc_sam0_sercom_i2c_bus_topology + * - \ref asfdoc_sam0_sercom_i2c_transactions + * - \ref asfdoc_sam0_sercom_i2c_multi_master + * - \ref asfdoc_sam0_sercom_i2c_bus_states + * - \ref asfdoc_sam0_sercom_i2c_timeout + * - \ref asfdoc_sam0_sercom_i2c_sleep_modes + * + * \subsection asfdoc_sam0_sercom_i2c_module_features Driver Feature Macro Definition + * <table> + * <tr> + * <th>Driver Feature Macro</th> + * <th>Supported devices</th> + * </tr> + * <tr> + * <td>FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_I2C_10_BIT_ADDRESS</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_I2C_SCL_STRETCH_MODE</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_I2C_SCL_EXTEND_TIMEOUT</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * </table> + * \note The specific features are only available in the driver when the selected + * device supports those features. + * \note When using the I2C high-speed mode for off-board communication, + * there are various high frequency interference, which can lead to distortion of the signals + * and communication failure. When using Xplained Pro boards in order to test I2C high-speed + * communication, the following recommendation should be followed: + * - Use the SDA-line on PA08 and SCL-line on PA09 for both boards. This will provide stronger + * pull-ups on both SDA and SCL. + * - The SCL should not be higher than 1.5MHz. + * + * \subsection asfdoc_sam0_sercom_i2c_functional_desc Functional Description + * The I<SUP>2</SUP>C provides a simple two-wire bidirectional bus consisting of a + * wired-AND type serial clock line (SCL) and a wired-AND type serial data line + * (SDA). + * + * The I<SUP>2</SUP>C bus provides a simple, but efficient method of interconnecting + * multiple master and slave devices. An arbitration mechanism is provided for + * resolving bus ownership between masters, as only one master device may own + * the bus at any given time. The arbitration mechanism relies on the wired-AND + * connections to avoid bus drivers short-circuiting. + * + * A unique address is assigned to all slave devices connected to the bus. A + * device can contain both master and slave logic, and can emulate multiple + * slave devices by responding to more than one address. + * + * \subsection asfdoc_sam0_sercom_i2c_bus_topology Bus Topology + * The I<SUP>2</SUP>C bus topology is illustrated in + * \ref asfdoc_sam0_sercom_i2c_bus_topology_figure "the figure below". The pull-up + * resistors (Rs) will provide a high level on the bus lines when none of the + * I<SUP>2</SUP>C devices are driving the bus. These are optional, and can be + * replaced with a constant current source. + * + * \anchor asfdoc_sam0_sercom_i2c_bus_topology_figure + * \image html bus_topology.svg "I2C Bus Topology" Width=100% + * + * \subsection asfdoc_sam0_sercom_i2c_transactions Transactions + * The I<SUP>2</SUP>C standard defines three fundamental transaction formats: + * - Master Write + * - The master transmits data packets to the slave after addressing it + * - Master Read + * - The slave transmits data packets to the master after being addressed + * - Combined Read/Write + * - A combined transaction consists of several write and read transactions + * + * A data transfer starts with the master issuing a \b Start condition on the + * bus, followed by the address of the slave together with a bit to indicate + * whether the master wants to read from or write to the slave. + * The addressed slave must respond to this by sending an \b ACK back to the + * master. + * + * After this, data packets are sent from the master or slave, according to the + * read/write bit. Each packet must be acknowledged (ACK) or not + * acknowledged (NACK) by the receiver. + * + * If a slave responds with a NACK, the master must assume that the slave + * cannot receive any more data and cancel the write operation. + * + * The master completes a transaction by issuing a \b Stop condition. + * + * A master can issue multiple \b Start conditions during a transaction; this + * is then called a \b Repeated \b Start condition. + * + * \subsubsection asfdoc_sam0_sercom_i2c_address_packets Address Packets + * The slave address consists of seven bits. The 8<SUP>th</SUP> bit in the transfer + * determines the data direction (read or write). An address packet always + * succeeds a \b Start or \b Repeated \b Start condition. The 8<SUP>th</SUP> bit is handled + * in the driver, and the user will only have to provide the 7-bit address. + * + * \subsubsection asfdoc_sam0_sercom_i2c_data_packets Data Packets + * Data packets are nine bits long, consisting of one 8-bit data byte, and an + * acknowledgement bit. Data packets follow either an address packet or another + * data packet on the bus. + * + * \subsubsection asfdoc_sam0_sercom_i2c_trans_examples Transaction Examples + * The gray bits in the following examples are sent from master to slave, and + * the white bits are sent from slave to master. + * Example of a read transaction is shown in + * \ref asfdoc_sam0_sercom_i2c_trans_examples_i2c_read "the figure below". Here, the + * master first issues a \b Start condition and gets ownership of the bus. An + * address packet with the direction flag set to read is then sent and + * acknowledged by the slave. Then the slave sends one data packet which is + * acknowledged by the master. The slave sends another packet, which is not + * acknowledged by the master and indicates that the master will terminate the + * transaction. In the end, the transaction is terminated by the master issuing + * a \b Stop condition. + * + * \anchor asfdoc_sam0_sercom_i2c_trans_examples_i2c_read + * \image html i2c_read.svg "I2C Packet Read" Width=100% + * + * Example of a write transaction is shown in + * \ref asfdoc_sam0_sercom_i2c_trans_examples_i2c_write "the figure below". Here, the + * master first issues a \b Start condition and gets ownership of the bus. An + * address packet with the dir flag set to write is then sent and acknowledged + * by the slave. Then the master sends two data packets, each acknowledged by + * the slave. In the end, the transaction is terminated by the master issuing + * a \b Stop condition. + * + * \anchor asfdoc_sam0_sercom_i2c_trans_examples_i2c_write + * \image html i2c_write.svg "I2C Packet Write" Width=100% + * + * \subsubsection asfdoc_sam0_sercom_i2c_packet_timeout Packet Timeout + * When a master sends an I<SUP>2</SUP>C packet, there is no way of + * being sure that a slave will acknowledge the packet. To avoid stalling the + * device forever while waiting for an acknowledge, a user selectable timeout + * is provided in the \ref i2c_master_config struct which + * lets the driver exit a read or write operation after the specified time. + * The function will then return the STATUS_ERR_TIMEOUT flag. + * + * This is also the case for the slave when using the functions postfixed + * \c _wait. + * + * The time before the timeout occurs, will be the same as + * for \ref asfdoc_sam0_sercom_i2c_unknown_bus_timeout "unknown bus state" timeout. + * + * \subsubsection asfdoc_sam0_sercom_i2c_repeated_start Repeated Start + * To issue a \b Repeated \b Start, the functions postfixed \c _no_stop must be + * used. + * These functions will not send a \b Stop condition when the transfer is done, + * thus the next transfer will start with a \b Repeated \b Start. To end the + * transaction, the functions without the \c _no_stop postfix must be used + * for the last read/write. + * + * \subsection asfdoc_sam0_sercom_i2c_multi_master Multi Master + * In a multi master environment, arbitration of the bus is important, as only + * one master can own the bus at any point. + * + * \subsubsection asfdoc_sam0_sercom_i2c_arbitration Arbitration + * + * \par Clock stretching + * The serial clock line is always driven by a master device. However, all + * devices connected to the bus are allowed stretch the low period of the clock + * to slow down the overall clock frequency or to insert wait states while + * processing data. + * Both master and slave can randomly stretch the clock, which will force the + * other device into a wait-state until the clock line goes high again. + * + * \par Arbitration on the data line + * If two masters start transmitting at the same time, they will both transmit + * until one master detects that the other master is pulling the data line low. + * When this is detected, the master not pulling the line low, will stop the + * transmission and wait until the bus is idle. + * As it is the master trying to contact the slave with the lowest address that + * will get the bus ownership, this will create an arbitration scheme always + * prioritizing the slaves with the lowest address in case of a bus collision. + * + * \subsubsection asfdoc_sam0_sercom_i2c_clock_sync Clock Synchronization + * In situations where more than one master is trying to control the bus clock + * line at the same time, a clock synchronization algorithm based on the same + * principles used for clock stretching is necessary. + * + * + * \subsection asfdoc_sam0_sercom_i2c_bus_states Bus States + * As the I<SUP>2</SUP>C bus is limited to one transaction at the time, + * a master that wants to perform a bus transaction must wait until the bus is + * free. + * Because of this, it is necessary for all masters in a multi-master system to + * know the current status of the bus to be able to avoid conflicts and to + * ensure data integrity. + * \li \b IDLE No activity on the bus (between a \b Stop and a new \b Start + * condition) + * \li \b OWNER If the master initiates a transaction successfully + * \li \b BUSY If another master is driving the bus + * \li \b UNKNOWN If the master has recently been enabled or connected to + * the bus. Is forced to \b IDLE after given + * \ref asfdoc_sam0_sercom_i2c_unknown_bus_timeout "timeout" when + * the master module is enabled + * + * The bus state diagram can be seen in + * \ref asfdoc_sam0_sercom_i2c_bus_states_figure "the figure below". + * \li S: Start condition + * \li P: Stop condition + * \li Sr: Repeated start condition + * \anchor asfdoc_sam0_sercom_i2c_bus_states_figure + * \image html bus_state_diagram.svg "I2C Bus State Diagram" Width=100% + * + * \subsection asfdoc_sam0_sercom_i2c_timeout Bus Timing + * Inactive bus timeout for the master and SDA hold time is configurable in the + * drivers. + * + * \subsubsection asfdoc_sam0_sercom_i2c_unknown_bus_timeout Unknown Bus State Timeout + * When a master is enabled or connected to the bus, the bus state will be + * unknown until either a given timeout or a stop command has occurred. The + * timeout is configurable in the \ref i2c_master_config struct. + * The timeout time will depend on toolchain and optimization level used, as + * the timeout is a loop incrementing a value until it reaches the specified + * timeout value. + * + * \subsubsection sda_hold SDA Hold Timeout + * When using the I<SUP>2</SUP>C in slave mode, it will be important to + * set a SDA hold time which assures that the master will be able to pick up + * the bit sent from the slave. The SDA hold time makes sure that this is the + * case by holding the data line low for a given period after the negative edge + * on the clock. + * + * The SDA hold time is also available for the master driver, but is not a + * necessity. + * + * \subsection asfdoc_sam0_sercom_i2c_sleep_modes Operation in Sleep Modes + * The I<SUP>2</SUP>C module can operate in all sleep modes by setting + * the run_in_standby Boolean in the \ref i2c_master_config or + * \ref i2c_slave_config struct. + * The operation in slave and master mode is shown in + * \ref asfdoc_sam0_sercom_i2c_sleep_modes_table "the table below". + * + * \anchor asfdoc_sam0_sercom_i2c_sleep_modes_table + * <table> + * <caption>I2C Standby Operations</caption> + * <tr> + * <th>Run in standby</th> + * <th>Slave</th> + * <th>Master</th> + * </tr> + * <tr> + * <td>false</td> + * <td>Disabled, all reception is dropped</td> + * <td>Generic Clock (GCLK) disabled when master is idle</td> + * </tr> + * <tr> + * <td>true</td> + * <td>Wake on address match when enabled</td> + * <td>GCLK enabled while in sleep modes</td> + * </tr> + * </table> + * + * + * \section asfdoc_sam0_sercom_i2c_special_considerations Special Considerations + * + * \if (I2C_MASTER_CALLBACK_MODE || I2C_SLAVE_CALLBACK_MODE) + * \subsection asfdoc_sam0_sercom_i2c_common_interrupt Interrupt-driven Operation + * While an interrupt-driven operation is in progress, subsequent calls to a + * write or read operation will return the STATUS_BUSY flag, indicating that + * only one operation is allowed at any given time. + * + * To check if another transmission can be initiated, the user can either call + * another transfer operation, or use the + * \ref i2c_master_get_job_status/\ref i2c_slave_get_job_status functions + * depending on mode. + * + * If the user would like to get callback from operations while using the + * interrupt-driven driver, the callback must be registered and then enabled + * using the "register_callback" and "enable_callback" functions. + * \else + * There are no special considerations for this driver for the APIs listed in + * this document. + * \endif + * + * \section asfdoc_sam0_sercom_i2c_extra Extra Information + * For extra information, see \ref asfdoc_sam0_sercom_i2c_extra_info_page. + * This includes: + * - \ref asfdoc_sam0_sercom_i2c_acronyms + * - \ref asfdoc_sam0_sercom_i2c_extra_dependencies + * - \ref asfdoc_sam0_sercom_i2c_extra_errata + * - \ref asfdoc_sam0_sercom_i2c_extra_history + * + * \section asfdoc_sam0_sercom_i2c_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_i2c_exqsg. + * + * \section asfdoc_sam0_sercom_i2c_api_overview API Overview + * @{ + */ + +/** + * \name Driver Feature Definition + * Define SERCOM I<SUP>2</SUP>C driver features set according to different device family. + * + * @{ + */ +#if (SAMD21) || (SAMR21) || (SAMD10) || (SAMD11) || (SAML21) || (SAMDA1) || \ + (SAML22) || (SAMC20) || (SAMC21) || (SAMD09) || (SAMR30) || defined(__DOXYGEN__) +/** Fast mode plus and high speed support. */ +# define FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED +/** 10-bit address support */ +# define FEATURE_I2C_10_BIT_ADDRESS +/** SCL stretch mode support */ +# define FEATURE_I2C_SCL_STRETCH_MODE +/** SCL extend timeout support */ +# define FEATURE_I2C_SCL_EXTEND_TIMEOUT +# define FEATURE_I2C_DMA_SUPPORT +#endif +/*@}*/ + +/** \brief Transfer direction + * + * For master: transfer direction or setting direction bit in address. + * For slave: direction of request from master. + */ +enum i2c_transfer_direction { + /** Master write operation is in progress */ + I2C_TRANSFER_WRITE = 0, + /** Master read operation is in progress */ + I2C_TRANSFER_READ = 1, +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \page asfdoc_sam0_sercom_i2c_extra_info_page Extra Information for SERCOM I2C Driver + * + * \section asfdoc_sam0_sercom_i2c_acronyms Acronyms + * \ref asfdoc_sam0_sercom_i2c_acronyms_table "Below" is a table listing the acronyms + * used in this module, along with their intended meanings. + * + * \anchor asfdoc_sam0_sercom_i2c_acronyms_table + * <table> + * <caption>Acronyms</caption> + * <tr> + * <th>Acronym</th> + * <th>Description</th> + * </tr> + * <tr> + * <td>SDA</td> + * <td>Serial Data Line</td> + * </tr> + * <tr> + * <td>SCL</td> + * <td>Serial Clock Line</td> + * </tr> + * <tr> + * <td>SERCOM</td> + * <td>Serial Communication Interface</td> + * </tr> + * <tr> + * <td>DMA</td> + * <td>Direct Memory Access</td> + * </tr> + * </table> + * + * \section asfdoc_sam0_sercom_i2c_extra_dependencies Dependencies + * The I<SUP>2</SUP>C driver has the following dependencies: + * \li \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" + * + * + * \section asfdoc_sam0_sercom_i2c_extra_errata Errata + * There are no errata related to this driver. + * + * \section asfdoc_sam0_sercom_i2c_extra_history Module History + * \ref asfdoc_sam0_sercom_i2c_extra_history_table "Below" is an overview of the + * module history, detailing enhancements and fixes made to the module since + * its first release. The current version of this corresponds to the newest + * version listed in + * \ref asfdoc_sam0_sercom_i2c_extra_history_table "the table below". + * + * \anchor asfdoc_sam0_sercom_i2c_extra_history_table + * <table> + * <caption>Module History</caption> + * <tr> + * <th>Changelog</th> + * </tr> + * <tr> + * <td> + * \li Added 10-bit addressing and high speed support in SAM D21 + * \li Separate structure i2c_packet into i2c_master_packet and i2c_slave packet + * </td> + * </tr> + * <tr> + * <td> + * \li Added support for SCL stretch and extended timeout hardware features in SAM D21 + * \li Added fast mode plus support in SAM D21 + * </td> + * </tr> + * <tr> + * <td>Fixed incorrect logical mask for determining if a bus error has + * occurred in I<SUP>2</SUP>C Slave mode + * </td> + * </tr> + * <tr> + * <td>Initial Release</td> + * </tr> + * </table> + */ + +/** + * \page asfdoc_sam0_sercom_i2c_exqsg Examples for SERCOM I2C Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_i2c_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * \if I2C_MASTER_MODE + * - \subpage asfdoc_sam0_sercom_i2c_master_basic_use_case "Quick Start Guide for the I2C Master module - Basic Use Case" + * \endif + * \if I2C_MASTER_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_i2c_master_callback_use_case "Quick Start Guide for the I2C Master module - Callback Use Case" + * - \subpage asfdoc_sam0_sercom_i2c_master_dma_use_case "Quick Start Guide for the I2C Master module - DMA Use Case" + * \endif + * \if I2C_SLAVE_MODE + * - \subpage asfdoc_sam0_sercom_i2c_slave_basic_use_case "Quick Start Guide for the I2C Slave module - Basic Use Case" + * \endif + * \if I2C_SLAVE_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_i2c_slave_callback_use_case "Quick Start Guide for the I2C Slave module - Callback Use Case" + * - \subpage asfdoc_sam0_sercom_i2c_slave_dma_use_case "Quick Start Guide for the I2C Slave module - DMA Use Case" + * \endif + * + * \page asfdoc_sam0_sercom_i2c_document_revision_history Document Revision History + * + * \if (I2C_MASTER_MODE || I2C_MASTER_CALLBACK_MODE) + * <table> + * <tr> + * <th>Doc. Rev.</th> + * <th>Date</th> + * <th>Comments</th> + * </tr> + * <tr> + * <td>42117E</td> + * <td>12/2015</td> + * <td>Added support for SAM L21/L22, SAM DA1, SAM D09, and SAM C21</td> + * </tr> + * <tr> + * <td>42117D</td> + * <td>12/2014</td> + * <td>Added support for 10-bit addressing and high speed in SAM D21. + * Added support for SAM R21 and SAM D10/D11.</td> + * </tr> + * <tr> + * <td>42117C</td> + * <td>01/2014</td> + * <td>Added support for SAM D21</td> + * </tr> + * <tr> + * <td>42117B</td> + * <td>06/2013</td> + * <td>Corrected documentation typos. Updated I<SUP>2</SUP>C Bus State Diagram.</td> + * </tr> + * <tr> + * <td>42117A</td> + * <td>06/2013</td> + * <td>Initial release</td> + * </tr> + * </table> + * \else + * <table> + * <tr> + * <th>Doc. Rev.</th> + * <th>Date</th> + * <th>Comments</th> + * </tr> + * <tr> + * <td>42116E</td> + * <td>12/2015</td> + * <td>Added support for SAM L21/L22, SAM DA1, SAM D09, and SAM C21</td> + * </tr> + * <tr> + * <td>42116D</td> + * <td>12/2014</td> + * <td>Added support for 10-bit addressing and high speed in SAM D21. + * Added support for SAM R21 and SAM D10/D11.</td> + * </tr> + * <tr> + * <td>42116C</td> + * <td>01/2014</td> + * <td>Added support for SAM D21</td> + * </tr> + * <tr> + * <td>42116B</td> + * <td>06/2013</td> + * <td>Corrected documentation typos. Updated I<SUP>2</SUP>C Bus State Diagram.</td> + * </tr> + * <tr> + * <td>42116A</td> + * <td>06/2013</td> + * <td>Initial release</td> + * </tr> + * </table> + *\endif + */ + +#endif /* I2C_COMMON_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master.h new file mode 100644 index 000000000..79c2effe8 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master.h @@ -0,0 +1,629 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Master Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef I2C_MASTER_H_INCLUDED +#define I2C_MASTER_H_INCLUDED + +#include "i2c_common.h" +#include <sercom.h> +#include <pinmux.h> + +#if I2C_MASTER_CALLBACK_MODE == true +# include <sercom_interrupt.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PINMUX_DEFAULT +# define PINMUX_DEFAULT 0 +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * + * @{ + */ + +/** + * \brief I<SUP>2</SUP>C master packet for read/write + * + * Structure to be used when transferring I<SUP>2</SUP>C master packets. + */ +struct i2c_master_packet { + /** Address to slave device */ + uint16_t address; + /** Length of data array */ + uint16_t data_length; + /** Data array containing all data to be transferred */ + uint8_t *data; + /** Use 10-bit addressing. Set to false if the feature is not supported by the device */ + bool ten_bit_address; + /** Use high speed transfer. Set to false if the feature is not supported by the device */ + bool high_speed; + /** High speed mode master code (0000 1XXX), valid when high_speed is true */ + uint8_t hs_master_code; +}; + +/** \brief Interrupt flags + * + * Flags used when reading or setting interrupt flags. + */ +enum i2c_master_interrupt_flag { + /** Interrupt flag used for write */ + I2C_MASTER_INTERRUPT_WRITE = 0, + /** Interrupt flag used for read */ + I2C_MASTER_INTERRUPT_READ = 1, +}; + +/** + * \brief Values for hold time after start bit. + * + * Values for the possible I<SUP>2</SUP>C master mode SDA internal hold times after start + * bit has been sent. + */ +enum i2c_master_start_hold_time { + /** Internal SDA hold time disabled */ + I2C_MASTER_START_HOLD_TIME_DISABLED = SERCOM_I2CM_CTRLA_SDAHOLD(0), + /** Internal SDA hold time 50ns - 100ns */ + I2C_MASTER_START_HOLD_TIME_50NS_100NS = SERCOM_I2CM_CTRLA_SDAHOLD(1), + /** Internal SDA hold time 300ns - 600ns */ + I2C_MASTER_START_HOLD_TIME_300NS_600NS = SERCOM_I2CM_CTRLA_SDAHOLD(2), + /** Internal SDA hold time 400ns - 800ns */ + I2C_MASTER_START_HOLD_TIME_400NS_800NS = SERCOM_I2CM_CTRLA_SDAHOLD(3), +}; + +/** + * \brief Values for inactive bus time-out. + * + * If the inactive bus time-out is enabled and the bus is inactive for + * longer than the time-out setting, the bus state logic will be set to idle. + */ +enum i2c_master_inactive_timeout { + /** Inactive bus time-out disabled */ + I2C_MASTER_INACTIVE_TIMEOUT_DISABLED = SERCOM_I2CM_CTRLA_INACTOUT(0), + /** Inactive bus time-out 5-6 SCL cycle time-out */ + I2C_MASTER_INACTIVE_TIMEOUT_55US = SERCOM_I2CM_CTRLA_INACTOUT(1), + /** Inactive bus time-out 10-11 SCL cycle time-out */ + I2C_MASTER_INACTIVE_TIMEOUT_105US = SERCOM_I2CM_CTRLA_INACTOUT(2), + /** Inactive bus time-out 20-21 SCL cycle time-out */ + I2C_MASTER_INACTIVE_TIMEOUT_205US = SERCOM_I2CM_CTRLA_INACTOUT(3), +}; + +/** + * \brief I<SUP>2</SUP>C frequencies + * + * Values for I<SUP>2</SUP>C speeds supported by the module. The driver + * will also support setting any other value, in which case set + * the value in the \ref i2c_master_config at desired value divided by 1000. + * + * Example: If 10KHz operation is required, give baud_rate in the configuration + * structure the value 10. + */ +enum i2c_master_baud_rate { + /** Baud rate at 100KHz (Standard-mode) */ + I2C_MASTER_BAUD_RATE_100KHZ = 100, + /** Baud rate at 400KHz (Fast-mode) */ + I2C_MASTER_BAUD_RATE_400KHZ = 400, +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + /** Baud rate at 1MHz (Fast-mode Plus) */ + I2C_MASTER_BAUD_RATE_1000KHZ = 1000, + /** Baud rate at 3.4MHz (High-speed mode) */ + I2C_MASTER_BAUD_RATE_3400KHZ = 3400, +#endif +}; + +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED +/** + * \brief Enum for the transfer speed + * + * Enum for the transfer speed. + */ +enum i2c_master_transfer_speed { + /** Standard-mode (Sm) up to 100KHz and Fast-mode (Fm) up to 400KHz */ + I2C_MASTER_SPEED_STANDARD_AND_FAST = SERCOM_I2CM_CTRLA_SPEED(0), + /** Fast-mode Plus (Fm+) up to 1MHz */ + I2C_MASTER_SPEED_FAST_MODE_PLUS = SERCOM_I2CM_CTRLA_SPEED(1), + /** High-speed mode (Hs-mode) up to 3.4MHz */ + I2C_MASTER_SPEED_HIGH_SPEED = SERCOM_I2CM_CTRLA_SPEED(2), +}; +#endif + +#if I2C_MASTER_CALLBACK_MODE == true +/** + * \brief Callback types + * + * The available callback types for the I<SUP>2</SUP>C master module. + */ +enum i2c_master_callback { + /** Callback for packet write complete */ + I2C_MASTER_CALLBACK_WRITE_COMPLETE = 0, + /** Callback for packet read complete */ + I2C_MASTER_CALLBACK_READ_COMPLETE = 1, + /** Callback for error */ + I2C_MASTER_CALLBACK_ERROR = 2, +# if !defined(__DOXYGEN__) + /** Total number of callbacks */ + _I2C_MASTER_CALLBACK_N = 3, +# endif +}; + +# if !defined(__DOXYGEN__) +/* Prototype for software module */ +struct i2c_master_module; + +typedef void (*i2c_master_callback_t)( + struct i2c_master_module *const module); +# endif +#endif + +/** + * \brief SERCOM I<SUP>2</SUP>C Master driver software device instance structure. + * + * SERCOM I<SUP>2</SUP>C Master driver software instance structure, used to + * retain software state information of an associated hardware module instance. + * + * \note The fields of this structure should not be altered by the user + * application; they are reserved for module-internal use only. + */ +struct i2c_master_module { +#if !defined(__DOXYGEN__) + /** Hardware instance initialized for the struct */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** Unknown bus state timeout */ + uint16_t unknown_bus_state_timeout; + /** Buffer write timeout value */ + uint16_t buffer_timeout; + /** If true, stop condition will be sent after a read/write */ + bool send_stop; + /** If true, nack signal will be sent after a read/write */ + bool send_nack; +# if I2C_MASTER_CALLBACK_MODE == true + /** Pointers to callback functions */ + volatile i2c_master_callback_t callbacks[_I2C_MASTER_CALLBACK_N]; + /** Mask for registered callbacks */ + volatile uint8_t registered_callback; + /** Mask for enabled callbacks */ + volatile uint8_t enabled_callback; + /** The total number of bytes to transfer */ + volatile uint16_t buffer_length; + /** + * Counter used for bytes left to send in write and to count number of + * obtained bytes in read + */ + volatile uint16_t buffer_remaining; + /** Data buffer for packet write and read */ + volatile uint8_t *buffer; + /** Save direction of async request. 1 = read, 0 = write */ + volatile enum i2c_transfer_direction transfer_direction; + /** Status for status read back in error callback */ + volatile enum status_code status; +# endif +#endif +}; + +/** + * \brief Configuration structure for the I<SUP>2</SUP>C Master device + * + * This is the configuration structure for the I<SUP>2</SUP>C Master device. It + * is used as an argument for \ref i2c_master_init to provide the desired + * configurations for the module. The structure should be initialized using the + * \ref i2c_master_get_config_defaults. + */ +struct i2c_master_config { + /** Baud rate (in KHz) for I<SUP>2</SUP>C operations in + * standard-mode, Fast-mode, and Fast-mode Plus Transfers, + * \ref i2c_master_baud_rate */ + uint32_t baud_rate; +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + /** Baud rate (in KHz) for I<SUP>2</SUP>C operations in + * High-speed mode, \ref i2c_master_baud_rate */ + uint32_t baud_rate_high_speed; + /** Transfer speed mode */ + enum i2c_master_transfer_speed transfer_speed; +#endif + /** GCLK generator to use as clock source */ + enum gclk_generator generator_source; + /** Bus hold time after start signal on data line */ + enum i2c_master_start_hold_time start_hold_time; + /** Unknown bus state \ref asfdoc_sam0_sercom_i2c_unknown_bus_timeout "timeout" */ + uint16_t unknown_bus_state_timeout; + /** Timeout for packet write to wait for slave */ + uint16_t buffer_timeout; + /** Set to keep module active in sleep modes */ + bool run_in_standby; + /** PAD0 (SDA) pinmux */ + uint32_t pinmux_pad0; + /** PAD1 (SCL) pinmux */ + uint32_t pinmux_pad1; + /** Set to enable SCL low time-out */ + bool scl_low_timeout; + /** Inactive bus time out */ + enum i2c_master_inactive_timeout inactive_timeout; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + /** Set to enable SCL stretch only after ACK bit (required for high speed) */ + bool scl_stretch_only_after_ack_bit; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + /** Set to enable slave SCL low extend time-out */ + bool slave_scl_low_extend_timeout; + /** Set to enable maser SCL low extend time-out */ + bool master_scl_low_extend_timeout; +#endif + /** Get more accurate BAUD, considering rise time(required for standard-mode and Fast-mode) */ + uint16_t sda_scl_rise_time_ns; +}; + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code i2c_master_lock( + struct i2c_master_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline void i2c_master_unlock(struct i2c_master_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Configuration and Initialization + * @{ + */ + +/** + * \brief Returns the synchronization status of the module + * + * Returns the synchronization status of the module. + * + * \param[in] module Pointer to software module structure + * + * \return Status of the synchronization. + * \retval true Module is busy synchronizing + * \retval false Module is not synchronizing + */ +static inline bool i2c_master_is_syncing ( + const struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_hw = &(module->hw->I2CM); + +#if defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1) + return (i2c_hw->STATUS.reg & SERCOM_I2CM_STATUS_SYNCBUSY); +#elif defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2) + return (i2c_hw->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK); +#else +# error Unknown SERCOM SYNCBUSY scheme! +#endif +} + +#if !defined(__DOXYGEN__) +/** + * \internal + * Wait for hardware module to sync + * + * \param[in] module Pointer to software module structure + */ +static void _i2c_master_wait_for_sync( + const struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + + while (i2c_master_is_syncing(module)) { + /* Wait for I2C module to sync. */ + } +} +#endif + +/** + * \brief Gets the I<SUP>2</SUP>C master default configurations + * + * Use to initialize the configuration structure to known default values. + * + * The default configuration is as follows: + * - Baudrate 100KHz + * - GCLK generator 0 + * - Do not run in standby + * - Start bit hold time 300ns - 600ns + * - Buffer timeout = 65535 + * - Unknown bus status timeout = 65535 + * - Do not run in standby + * - PINMUX_DEFAULT for SERCOM pads + * + * Those default configuration only available if the device supports it: + * - High speed baudrate 3.4MHz + * - Standard-mode and Fast-mode transfer speed + * - SCL stretch disabled + * - Slave SCL low extend time-out disabled + * - Master SCL low extend time-out disabled + * + * \param[out] config Pointer to configuration structure to be initiated + */ +static inline void i2c_master_get_config_defaults( + struct i2c_master_config *const config) +{ + /*Sanity check argument */ + Assert(config); + config->baud_rate = I2C_MASTER_BAUD_RATE_100KHZ; +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + config->baud_rate_high_speed = I2C_MASTER_BAUD_RATE_3400KHZ; + config->transfer_speed = I2C_MASTER_SPEED_STANDARD_AND_FAST; +#endif + config->generator_source = GCLK_GENERATOR_0; + config->run_in_standby = false; + config->start_hold_time = I2C_MASTER_START_HOLD_TIME_300NS_600NS; + config->buffer_timeout = 65535; + config->unknown_bus_state_timeout = 65535; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->scl_low_timeout = false; + config->inactive_timeout = I2C_MASTER_INACTIVE_TIMEOUT_DISABLED; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + config->scl_stretch_only_after_ack_bit = false; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + config->slave_scl_low_extend_timeout = false; + config->master_scl_low_extend_timeout = false; +#endif + /* The typical value is 215ns */ + config->sda_scl_rise_time_ns = 215; +} + +enum status_code i2c_master_init( + struct i2c_master_module *const module, + Sercom *const hw, + const struct i2c_master_config *const config); + +/** + * \brief Enables the I<SUP>2</SUP>C module + * + * Enables the requested I<SUP>2</SUP>C module and set the bus state to IDLE + * after the specified \ref asfdoc_sam0_sercom_i2c_timeout "timeout" period if no + * stop bit is detected. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_master_enable( + const struct i2c_master_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Timeout counter used to force bus state */ + uint32_t timeout_counter = 0; + + /* Wait for module to sync */ + _i2c_master_wait_for_sync(module); + + /* Enable module */ + i2c_module->CTRLA.reg |= SERCOM_I2CM_CTRLA_ENABLE; + +#if I2C_MASTER_CALLBACK_MODE == true + /* Enable module interrupts */ + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +#endif + /* Start timeout if bus state is unknown */ + while (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(1))) { + timeout_counter++; + if(timeout_counter >= (module->unknown_bus_state_timeout)) { + /* Timeout, force bus state to idle */ + i2c_module->STATUS.reg = SERCOM_I2CM_STATUS_BUSSTATE(1); + /* Workaround #1 */ + return; + } + } +} + +/** + * \brief Disable the I<SUP>2</SUP>C module + * + * Disables the requested I<SUP>2</SUP>C module. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_master_disable( + const struct i2c_master_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Disable module interrupts */ + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait for module to sync */ + _i2c_master_wait_for_sync(module); + + /* Disbale interrupt */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK; + /* Clear interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MASK; + + /* Disable module */ + i2c_module->CTRLA.reg &= ~SERCOM_I2CM_CTRLA_ENABLE; + +} + +void i2c_master_reset(struct i2c_master_module *const module); + +/** @} */ + +/** +* \name Read and Write +* @{ +*/ + +enum status_code i2c_master_read_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +void i2c_master_send_stop(struct i2c_master_module *const module); + +void i2c_master_send_nack(struct i2c_master_module *const module); + +enum status_code i2c_master_read_byte( + struct i2c_master_module *const module, + uint8_t *byte); + +enum status_code i2c_master_write_byte( + struct i2c_master_module *const module, + uint8_t byte); + +enum status_code i2c_master_read_packet_wait_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +/** @} */ + +#ifdef FEATURE_I2C_DMA_SUPPORT +/** +* \name SERCOM I2C Master with DMA Interfaces +* @{ +*/ + +/** + * \brief Set I<SUP>2</SUP>C for DMA transfer with slave address and transfer size. + * + * This function will set the slave address, transfer size and enable the auto transfer + * mode for DMA. + * + * \param[in,out] module Pointer to the driver instance to lock + * \param[in] addr I<SUP>2</SUP>C slave address + * \param[in] length I<SUP>2</SUP>C transfer length with DMA + * \param[in] direction I<SUP>2</SUP>C transfer direction + * + */ +static inline void i2c_master_dma_set_transfer(struct i2c_master_module *const module, + uint16_t addr, uint8_t length, enum i2c_transfer_direction direction) +{ + module->hw->I2CM.ADDR.reg = + SERCOM_I2CM_ADDR_ADDR(addr<<1) | + SERCOM_I2CM_ADDR_LENEN | + SERCOM_I2CM_ADDR_LEN(length) | + direction; +} + +/** @} */ +#endif + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_MASTER_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master_interrupt.h new file mode 100644 index 000000000..249525d3c --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_master_interrupt.h @@ -0,0 +1,214 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Master Interrupt Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef I2C_MASTER_INTERRUPT_H_INCLUDED +#define I2C_MASTER_INTERRUPT_H_INCLUDED + +#include "i2c_master.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * @{ + * + */ + +/** + * \name Callbacks + * @{ + */ +#if !defined(__DOXYGEN__) +void _i2c_master_interrupt_handler( + uint8_t instance); +#endif + +void i2c_master_register_callback( + struct i2c_master_module *const module, + i2c_master_callback_t callback, + enum i2c_master_callback callback_type); + +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type); + +/** + * \brief Enables callback + * + * Enables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to enable + */ +static inline void i2c_master_enable_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as enabled */ + module->enabled_callback |= (1 << callback_type); +} + +/** + * \brief Disables callback + * + * Disables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to disable + */ +static inline void i2c_master_disable_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as disabled */ + module->enabled_callback &= ~(1 << callback_type); +} + +/** @} */ + +/** + * \name Read and Write, Interrupt-driven + * @{ + */ + +enum status_code i2c_master_read_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_read_packet_job_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet); + +/** + * \brief Cancel any currently ongoing operation + * + * Terminates the running transfer operation. + * + * \param[in,out] module Pointer to software module structure + */ +static inline void i2c_master_cancel_job( + struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Set buffer to 0 */ + module->buffer_remaining = 0; + /* Update status */ + module->status = STATUS_ABORTED; +} + +/** + * \brief Get status from ongoing job + * + * Will return the status of a transfer operation. + * + * \param[in] module Pointer to software module structure + * + * \return Last status code from transfer operation. + * \retval STATUS_OK No error has occurred + * \retval STATUS_BUSY If transfer is in progress + * \retval STATUS_BUSY If master module is busy + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave does not + * want more data and was not able to read + */ +static inline enum status_code i2c_master_get_job_status( + struct i2c_master_module *const module) +{ + /* Check sanity */ + Assert(module); + Assert(module->hw); + + /* Return current status code */ + return module->status; +} + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_MASTER_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c new file mode 100644 index 000000000..4e37b4f09 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master.c @@ -0,0 +1,1050 @@ +/** + * \file + * + * \brief SAM I2C Master Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_master.h" + +#if I2C_MASTER_CALLBACK_MODE == true +# include "i2c_master_interrupt.h" +#endif + +/* Forward declaration */ +enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module); + +enum status_code _i2c_master_address_response( + struct i2c_master_module *const module); + +enum status_code _i2c_master_send_hs_master_code( + struct i2c_master_module *const module, + uint8_t hs_master_code); + +#if !defined(__DOXYGEN__) + +/** + * \internal Sets configurations to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK If module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baudrate is not compatible + * with set GCLK frequency + */ +static enum status_code _i2c_master_set_config( + struct i2c_master_module *const module, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(config); + + /* Temporary variables. */ + uint32_t tmp_ctrla; + int32_t tmp_baud = 0; + int32_t tmp_baud_hs = 0; + int32_t tmp_baudlow_hs = 0; + enum status_code tmp_status_code = STATUS_OK; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + Sercom *const sercom_hw = module->hw; + + uint8_t sercom_index = _sercom_get_sercom_inst_index(sercom_hw); + + /* Pin configuration */ + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Save timeout on unknown bus state in software module. */ + module->unknown_bus_state_timeout = config->unknown_bus_state_timeout; + + /* Save timeout on buffer write. */ + module->buffer_timeout = config->buffer_timeout; + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + /* Check and set start data hold timeout. */ + if (config->start_hold_time != I2C_MASTER_START_HOLD_TIME_DISABLED) { + tmp_ctrla |= config->start_hold_time; + } + + /* Check and set transfer speed */ + tmp_ctrla |= config->transfer_speed; + + /* Check and set SCL low timeout. */ + if (config->scl_low_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUTEN; + } + + /* Check and set inactive bus timeout. */ + if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) { + tmp_ctrla |= config->inactive_timeout; + } + + /* Check and set SCL clock stretch mode. */ + if (config->scl_stretch_only_after_ack_bit || (config->transfer_speed == I2C_MASTER_SPEED_HIGH_SPEED)) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM; + } + + /* Check and set slave SCL low extend timeout. */ + if (config->slave_scl_low_extend_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_SEXTTOEN; + } + + /* Check and set master SCL low extend timeout. */ + if (config->master_scl_low_extend_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_MEXTTOEN; + } + + /* Write config to register CTRLA. */ + i2c_module->CTRLA.reg |= tmp_ctrla; + + /* Set configurations in CTRLB. */ + i2c_module->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; + + /* Find and set baudrate, considering sda/scl rise time */ + uint32_t fgclk = system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index); + uint32_t fscl = 1000 * config->baud_rate; + uint32_t fscl_hs = 1000 * config->baud_rate_high_speed; + uint32_t trise = config->sda_scl_rise_time_ns; + + tmp_baud = (int32_t)(div_ceil( + fgclk - fscl * (10 + fgclk * trise * 0.000000001), 2 * fscl)); + + /* For High speed mode, set the SCL ratio of high:low to 1:2. */ + if (config->transfer_speed == I2C_MASTER_SPEED_HIGH_SPEED) { + tmp_baudlow_hs = (int32_t)((fgclk * 2.0) / (3.0 * fscl_hs) - 1); + if (tmp_baudlow_hs) { + tmp_baud_hs = (int32_t)(fgclk / fscl_hs) - 2 - tmp_baudlow_hs; + } else { + tmp_baud_hs = (int32_t)(div_ceil(fgclk, 2 * fscl_hs)) - 1; + } + } + + /* Check that baudrate is supported at current speed. */ + if (tmp_baud > 255 || tmp_baud < 0 || tmp_baud_hs > 255 || tmp_baud_hs < 0) { + /* Baud rate not supported. */ + tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + if (tmp_status_code != STATUS_ERR_BAUDRATE_UNAVAILABLE) { + /* Baud rate acceptable. */ + i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) | + SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs) | SERCOM_I2CM_BAUD_HSBAUDLOW(tmp_baudlow_hs); + } + + return tmp_status_code; +} +#endif /* __DOXYGEN__ */ + +/** + * \brief Initializes the requested I<SUP>2</SUP>C hardware module + * + * Initializes the SERCOM I<SUP>2</SUP>C master device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baudrate is not compatible + * with set GCLK frequency + * + */ +enum status_code i2c_master_init( + struct i2c_master_module *const module, + Sercom *const hw, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; + +#if (SAML22) || (SAMC20) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#elif (SAML21) || (SAMR30) + if (sercom_index == 5) { + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC21) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + if (sercom_index == 5) { + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + + /* Check if module is enabled. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) { + return STATUS_BUSY; + } + +#if I2C_MASTER_CALLBACK_MODE == true + /* Get sercom instance index and register callback. */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _i2c_master_interrupt_handler); + _sercom_instances[instance_index] = module; + + /* Initialize values in module. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + + module->status = STATUS_OK; + module->buffer = NULL; +#endif + + /* Set sercom module to operate in I2C master mode. */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE(0x5); + + /* Set config and return status. */ + return _i2c_master_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * Reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_master_reset(struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Disable module */ + i2c_master_disable(module); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Clear all pending interrupts */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Reset module */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; +} + +#if !defined(__DOXYGEN__) +/** + * \internal + * Address response. Called when address is answered or timed out. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of address response. + * \retval STATUS_OK No error has occurred + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code _i2c_master_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error and ignore bus-error; workaround for BUSSTATE stuck in + * BUSY */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) { + + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB; + + /* Check arbitration. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return packet collision. */ + return STATUS_ERR_PACKET_COLLISION; + } + /* Check that slave responded with ack. */ + } else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Slave busy. Issue ack and stop command. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + + /* Return bad address value. */ + return STATUS_ERR_BAD_ADDRESS; + } + + return STATUS_OK; +} + +/** + * \internal + * Waits for answer on bus. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply. */ + uint16_t timeout_counter = 0; + while (!(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) && + !(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) { + + /* Check timeout condition. */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} +#endif /* __DOXYGEN__ */ + +/** + * \internal + * Send master code for high speed transfer. + * + * \param[in,out] module Pointer to software module structure + * \param[in] hs_master_code 8-bit master code (0000 1XXX) + * + * \return Status of bus. + * \retval STATUS_OK No error happen + */ +enum status_code _i2c_master_send_hs_master_code( + struct i2c_master_module *const module, + uint8_t hs_master_code) +{ + SercomI2cm *const i2c_module = &(module->hw->I2CM); + /* Return value. */ + enum status_code tmp_status; + + /* Set NACK for high speed code */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + /* Send high speed code */ + i2c_module->ADDR.reg = hs_master_code; + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; + + return tmp_status; +} + + +/** + * \internal + * Starts blocking read operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + /* Written buffer counter. */ + uint16_t counter = 0; + + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Set address and direction bit. Will send start command on bus. */ + if (packet->ten_bit_address) { + /* + * Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must + * be set and read/write bit (ADDR.ADDR[0]) equal to 0. + */ + i2c_module->ADDR.reg = (packet->address << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + if (tmp_status == STATUS_OK) { + /* + * Write ADDR[7:0] register to "11110 address[9:8] 1" + * ADDR.TENBITEN must be cleared + */ + i2c_module->ADDR.reg = (((packet->address >> 8) | 0x78) << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + I2C_TRANSFER_READ; + } else { + return tmp_status; + } + } else { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack or nack. */ + if ((sclsm_flag) && (packet->data_length == 1)) { + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Read data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + if (module->send_nack && (((!sclsm_flag) && (tmp_data_length == 0)) || + ((sclsm_flag) && (tmp_data_length == 1)))) { + /* Set action to NACK */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + /* Save data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter++] = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + } + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + } + + if (module->send_stop) { + /* Send stop command unless arbitration is lost. */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Save last data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter] = i2c_module->DATA.reg; + } + + return tmp_status; +} + +/** + * \brief Reads data packet from slave + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Reads data packet from slave without sending a stop condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus without sending a stop condition when done, thus retaining ownership of + * the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking read operation. + * \brief Reads data packet from slave without sending a nack signal and a stop + * condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus without sending a nack signal and a stop condition when done, + * thus retaining ownership of the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = false; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking write operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK The packet was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + _i2c_master_wait_for_sync(module); + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Set address and direction bit. Will send start command on bus. */ + if (packet->ten_bit_address) { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + } else { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Buffer counter. */ + uint16_t buffer_counter = 0; + + /* Write data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = packet->data[buffer_counter++]; + + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + + /* Check for NACK from slave. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad data value. */ + tmp_status = STATUS_ERR_OVERFLOW; + break; + } + } + + if (module->send_stop) { + /* Stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + return tmp_status; +} + +/** + * \brief Writes data packet to slave + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus + * and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy with a job + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave does not + * want more data and was not able to read + * last data sent + */ +enum status_code i2c_master_write_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Writes data packet to slave without sending a stop condition when done + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending a + * stop with the \ref i2c_master_send_stop function must be performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave do not want + * more data + */ +enum status_code i2c_master_write_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Sends stop condition on bus + * + * Sends a stop condition on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_stop function. If a stop condition + * is to be sent after a read, the \ref i2c_master_read_packet_wait + * function must be used. + * + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_stop(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); +} + +/** + * \brief Sends nack signal on bus + * + * Sends a nack signal on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_nack function, + * or \ref i2c_master_read_byte function. + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_nack(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send nack signal */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; +} + +/** + * \brief Reads one byte data from slave + * + * \param[in,out] module Pointer to software module struct + * \param[out] byte Read one byte data to slave + * + * \return Status of reading byte. + * \retval STATUS_OK One byte was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_byte( + struct i2c_master_module *const module, + uint8_t *byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + *byte = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + return tmp_status; +} + +/** + * \brief Write one byte data to slave + * + * \param[in,out] module Pointer to software module struct + * \param[in] byte Send one byte data to slave + * + * \return Status of writing byte. + * \retval STATUS_OK One byte was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_write_byte( + struct i2c_master_module *const module, + uint8_t byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = byte; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + return tmp_status; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c new file mode 100644 index 000000000..7551b2912 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c @@ -0,0 +1,762 @@ +/** + * \file + * + * \brief SAM I2C Master Interrupt Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_master_interrupt.h" + +extern enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module); + +extern enum status_code _i2c_master_address_response( + struct i2c_master_module *const module); + +extern enum status_code _i2c_master_send_hs_master_code( + struct i2c_master_module *const module, + uint8_t hs_master_code);; + +/** + * \internal + * Read next data. Used by interrupt handler to get next data byte from slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_read( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Find index to save next value in buffer */ + uint16_t buffer_index = module->buffer_length; + buffer_index -= module->buffer_remaining; + + module->buffer_remaining--; + + if (sclsm_flag) { + if (module->send_nack && module->buffer_remaining == 1) { + /* Set action to NACK. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } + } else { + if (module->send_nack && module->buffer_remaining == 0) { + /* Set action to NACK. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } + } + + if (module->buffer_remaining == 0) { + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + /* Read byte from slave and put in buffer */ + _i2c_master_wait_for_sync(module); + module->buffer[buffer_index] = i2c_module->DATA.reg; +} + +/** + * \internal + * + * Write next data. Used by interrupt handler to send next data byte to slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_write(struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for ack from slave */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) + { + /* Set status */ + module->status = STATUS_ERR_OVERFLOW; + /* Do not write more data */ + return; + } + + /* Find index to get next byte in buffer */ + uint16_t buffer_index = module->buffer_length; + buffer_index -= module->buffer_remaining; + + module->buffer_remaining--; + + /* Write byte from buffer to slave */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = module->buffer[buffer_index]; +} + +/** + * \internal + * Acts on slave address response. Checks for errors concerning master->slave + * handshake. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_async_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error. Ignore bus-error; workaround for bus state stuck in + * BUSY. + */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) + { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; + + /* Check arbitration */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return busy */ + module->status = STATUS_ERR_PACKET_COLLISION; + } + /* No slave responds */ + else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + module->status = STATUS_ERR_BAD_ADDRESS; + module->buffer_remaining = 0; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + } + + module->buffer_length = module->buffer_remaining; + + /* Check for status OK. */ + if (module->status == STATUS_BUSY) { + /* Call function based on transfer direction. */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. + * + * To enable the callback, the \ref i2c_master_enable_callback function + * must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_master_register_callback( + struct i2c_master_module *const module, + const i2c_master_callback_t callback, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as registered */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * When called, the currently registered callback for the given callback type + * will be removed. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Specifies the callback type to unregister + */ +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Register callback */ + module->callbacks[callback_type] = NULL; + + /* Clear corresponding bit to set callback as unregistered */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \internal + * Starts a read bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal + * Starts a read packet operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + enum status_code tmp_status; + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK or NACK. */ + if ((sclsm_flag) && (packet->data_length == 1)) { + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + if (packet->ten_bit_address) { + /* + * Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must + * be set and read/write bit (ADDR.ADDR[0]) equal to 0. + */ + i2c_module->ADDR.reg = (packet->address << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + if (tmp_status == STATUS_OK) { + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* + * Write ADDR[7:0] register to "11110 address[9:8] 1" + * ADDR.TENBITEN must be cleared + */ + i2c_module->ADDR.reg = (((packet->address >> 8) | 0x78) << 1) | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + I2C_TRANSFER_READ; + } else { + return tmp_status; + } + } else { + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit. Will send start command on bus */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + + return STATUS_OK; +} + +/** + * \brief Initiates a read packet operation + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus. This is the non-blocking equivalent of \ref i2c_master_read_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP */ + module->send_stop = true; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a STOP condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C bus without + * sending a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a NACK signal and a + * STOP condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C bus without + * sending a nack and a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = false; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts a write bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting write I<SUP>2</SUP>C bytes. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal Initiates a write packet operation + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Switch to high speed mode */ + if (packet->high_speed) { + _i2c_master_send_hs_master_code(module, packet->hs_master_code); + } + + /* Set action to ACK. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit, will send start command on bus */ + if (packet->ten_bit_address) { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | + SERCOM_I2CM_ADDR_TENBITEN; + } else { + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | + (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); + } + + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C + * bus. This is the non-blocking equivalent of \ref i2c_master_write_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP at end*/ + module->send_stop = true; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Initiates a write packet operation without sending a STOP condition when done + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending + * a stop with the \ref i2c_master_send_stop function must be performed. + * + * This is the non-blocking equivalent of \ref i2c_master_write_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another + */ +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Do not send stop condition when done */ + module->send_stop = false; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \internal + * Interrupt handler for I<SUP>2</SUP>C master. + * + * \param[in] instance SERCOM instance that triggered the interrupt + */ +void _i2c_master_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling */ + struct i2c_master_module *module = + (struct i2c_master_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; + + /* Combine callback registered and enabled masks */ + uint8_t callback_mask = module->enabled_callback; + callback_mask &= module->registered_callback; + + /* Check if the module should respond to address ack */ + if ((module->buffer_length <= 0) && (module->buffer_remaining > 0)) { + /* Call function for address response */ + _i2c_master_async_address_response(module); + + /* Check if buffer write is done */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->status = STATUS_OK; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MB; + } + + if (callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + + /* Continue buffer write/read */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining > 0)){ + /* Check that bus ownership is not lost */ + if ((!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) && + (!(sclsm_flag && (module->buffer_remaining == 1)))) { + module->status = STATUS_ERR_PACKET_COLLISION; + } else if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } + + /* Check if read buffer transfer is complete */ + if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_READ)) { + + /* Clear read interrupt flag */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) { + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB; + } + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + module->buffer_length = 0; + module->status = STATUS_OK; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_MASTER_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + module->callbacks[I2C_MASTER_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + } + + /* Check for error */ + if ((module->status != STATUS_BUSY) && (module->status != STATUS_OK)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | + SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Send nack and stop command unless arbitration is lost */ + if ((module->status != STATUS_ERR_PACKET_COLLISION) && + module->send_stop) { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | + SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Call error callback if enabled and registered */ + if (callback_mask & (1 << I2C_MASTER_CALLBACK_ERROR)) { + module->callbacks[I2C_MASTER_CALLBACK_ERROR](module); + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c new file mode 100644 index 000000000..113087794 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c @@ -0,0 +1,745 @@ +/** + * \file + * + * \brief SAM I2C Slave Driver + * + * Copyright (C) 2013-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_slave.h" +#if I2C_SLAVE_CALLBACK_MODE == true +# include "i2c_slave_interrupt.h" +#endif + +/** + * \internal Sets configuration to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK Module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +static enum status_code _i2c_slave_set_config( + struct i2c_slave_module *const module, + const struct i2c_slave_config *const config) +{ + uint32_t tmp_ctrla; + + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(config); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + Sercom *const sercom_hw = module->hw; + + module->buffer_timeout = config->buffer_timeout; + module->ten_bit_address = config->ten_bit_address; + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Prepare config to write to register CTRLA */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CS_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + /* Check and set SCL clock stretch mode. */ + if (config->scl_stretch_only_after_ack_bit || (config->transfer_speed == I2C_SLAVE_SPEED_HIGH_SPEED)) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM; + } + + tmp_ctrla |= ((uint32_t)config->sda_hold_time | + config->transfer_speed | + (config->scl_low_timeout << SERCOM_I2CS_CTRLA_LOWTOUTEN_Pos) | + (config->slave_scl_low_extend_timeout << SERCOM_I2CS_CTRLA_SEXTTOEN_Pos)); + + i2c_hw->CTRLA.reg |= tmp_ctrla; + + /* Set CTRLB configuration */ + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_SMEN | config->address_mode; + + i2c_hw->ADDR.reg = config->address << SERCOM_I2CS_ADDR_ADDR_Pos | + config->address_mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos | + config->ten_bit_address << SERCOM_I2CS_ADDR_TENBITEN_Pos | + config->enable_general_call_address << SERCOM_I2CS_ADDR_GENCEN_Pos; + + return STATUS_OK; +} + +/** + * \brief Initializes the requested I<SUP>2</SUP>C hardware module + * + * Initializes the SERCOM I<SUP>2</SUP>C slave device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +enum status_code i2c_slave_init( + struct i2c_slave_module *const module, + Sercom *const hw, + const struct i2c_slave_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Check if module is enabled */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_SWRST) { + return STATUS_BUSY; + } + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#else + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Get sercom instance index */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + + /* Save software module in interrupt handler */ + _sercom_set_handler(instance_index, _i2c_slave_interrupt_handler); + + /* Save software module */ + _sercom_instances[instance_index] = module; + + /* Initialize values in module */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->nack_on_address = config->enable_nack_on_address; +#endif + + /* Set SERCOM module to operate in I2C slave mode */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_MODE(0x4); + + /* Set config and return status */ + return _i2c_slave_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * This will reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_reset( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Reset module instance */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + module->buffer = NULL; +#endif + + /* Disable module */ + i2c_slave_disable(module); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Clear all pending interrupts */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync */ + _i2c_slave_wait_for_sync(module); + + /* Reset module */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_SWRST; +} + +/** + * \internal Waits for answer on bus + * + * \param[in] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +static enum status_code _i2c_slave_wait_for_bus( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply */ + uint16_t timeout_counter = 0; + while ((!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH))) { + + /* Check timeout condition */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} + +/** + * \brief Writes a packet to the master + * + * Writes a packet to the master. This will wait for the master to issue + * a request. + * + * \param[in] module Pointer to software module structure + * \param[in] packet Packet to write to master + * + * \return Status of packet write. + * \retval STATUS_OK Packet was written successfully + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_BAD_FORMAT Master wants to write data + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I<SUP>2</SUP>C module is busy with a job + * \retval STATUS_ERR_ERR_OVERFLOW Master NACKed before entire packet was + * transferred + * \retval STATUS_ERR_TIMEOUT No response was given within the timeout + * period + */ +enum status_code i2c_slave_write_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + if (module->ten_bit_address) { + /* ACK the first address */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + + /* Wait for address interrupt */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return STATUS_ERR_TIMEOUT; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + } + + /* Check if there was an error in last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + + /* Check direction */ + if (!(i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Write request from master, send NACK and return */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + return STATUS_ERR_BAD_FORMAT; + } + + /* Read request from master, ACK address */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + + uint16_t i = 0; + + /* Wait for data interrupt */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + while (length--) { + /* Write data */ + _i2c_slave_wait_for_sync(module); + i2c_hw->DATA.reg = packet->data[i++]; + + /* Wait for response from master */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK && + length !=0) { + /* NACK from master, abort */ + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_ERR_OVERFLOW; + } + /* ACK from master, continue writing */ + } + + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_OK; +} + +/** + * \brief Reads a packet from the master + * + * Reads a packet from the master. This will wait for the master to issue a + * request. + * + * \param[in] module Pointer to software module structure + * \param[out] packet Packet to read from master + * + * \return Status of packet read. + * \retval STATUS_OK Packet was read successfully + * \retval STATUS_ABORTED Master sent stop condition or repeated + * start before specified length of bytes + * was received + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I<SUP>2</SUP>C module is busy with a job + * \retval STATUS_ERR_BAD_FORMAT Master wants to read data + * \retval STATUS_ERR_ERR_OVERFLOW Last byte received overflows buffer + */ +enum status_code i2c_slave_read_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + /* Check if there was an error in the last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master, send NACK and return */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + return STATUS_ERR_BAD_FORMAT; + } + + /* Write request from master, ACK address */ + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); + + uint16_t i = 0; + while (length--) { + + /* Wait for next byte or stop condition */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if ((i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) || + i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Master sent stop condition, or repeated start, read done */ + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + return STATUS_ABORTED; + } + + /* Read data */ + _i2c_slave_wait_for_sync(module); + packet->data[i++] = i2c_hw->DATA.reg; + + } + + /* Packet read done, wait for packet to NACK, Stop or repeated start */ + status = _i2c_slave_wait_for_bus(module); + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Buffer is full, send NACK */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + } + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + return STATUS_OK; +} + +/** + * \brief Waits for a start condition on the bus + * + * \note This function is only available for 7-bit slave addressing. + * + * Waits for the master to issue a start condition on the bus. + * + * \note This function does not check for errors in the last transfer, + * this will be discovered when reading or writing. + * + * \param[in] module Pointer to software module structure + * + * \return Direction of the current transfer, when in slave mode. + * \retval I2C_SLAVE_DIRECTION_NONE No request from master within timeout + * period + * \retval I2C_SLAVE_DIRECTION_READ Write request from master + * \retval I2C_SLAVE_DIRECTION_WRITE Read request from master + */ +enum i2c_slave_direction i2c_slave_get_direction_wait( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + enum status_code status; + + /* Wait for address interrupt */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return I2C_SLAVE_DIRECTION_NONE; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return I2C_SLAVE_DIRECTION_NONE; + } + + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master */ + return I2C_SLAVE_DIRECTION_WRITE; + } else { + /* Write request from master */ + return I2C_SLAVE_DIRECTION_READ; + } +} + +/** + * \brief Retrieves the current module status + * + * Checks the status of the module and returns it as a bitmask of status + * flags. + * + * \param[in] module Pointer to the I<SUP>2</SUP>C slave software device struct + * + * \return Bitmask of status flags. + * + * \retval I2C_SLAVE_STATUS_ADDRESS_MATCH A valid address has been received + * \retval I2C_SLAVE_STATUS_DATA_READY A I<SUP>2</SUP>C slave byte transmission is + * successfully completed + * \retval I2C_SLAVE_STATUS_STOP_RECEIVED A stop condition is detected for a + * transaction being processed + * \retval I2C_SLAVE_STATUS_CLOCK_HOLD The slave is holding the SCL line + * low + * \retval I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT An SCL low time-out has occurred + * \retval I2C_SLAVE_STATUS_REPEATED_START Indicates a repeated start, only + * valid if \ref + * I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set + * \retval I2C_SLAVE_STATUS_RECEIVED_NACK The last data packet sent was not + * acknowledged + * \retval I2C_SLAVE_STATUS_COLLISION The I<SUP>2</SUP>C slave was not able to + * transmit a high data or NACK bit + * \retval I2C_SLAVE_STATUS_BUS_ERROR An illegal bus condition has + * occurred on the bus + */ +uint32_t i2c_slave_get_status( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint8_t intflags = i2c_hw->INTFLAG.reg; + uint8_t status = i2c_hw->STATUS.reg; + uint32_t status_flags = 0; + + /* Check Address Match flag */ + if (intflags & SERCOM_I2CS_INTFLAG_AMATCH) { + status_flags |= I2C_SLAVE_STATUS_ADDRESS_MATCH; + } + /* Check Data Ready flag */ + if (intflags & SERCOM_I2CS_INTFLAG_DRDY) { + status_flags |= I2C_SLAVE_STATUS_DATA_READY; + } + /* Check Stop flag */ + if (intflags & SERCOM_I2CS_INTFLAG_PREC) { + status_flags |= I2C_SLAVE_STATUS_STOP_RECEIVED; + } + /* Check Clock Hold */ + if (status & SERCOM_I2CS_STATUS_CLKHOLD) { + status_flags |= I2C_SLAVE_STATUS_CLOCK_HOLD; + } + /* Check SCL Low Timeout */ + if (status & SERCOM_I2CS_STATUS_LOWTOUT) { + status_flags |= I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT; + } + /* Check Repeated Start */ + if (status & SERCOM_I2CS_STATUS_SR) { + status_flags |= I2C_SLAVE_STATUS_REPEATED_START; + } + /* Check Received Not Acknowledge */ + if (status & SERCOM_I2CS_STATUS_RXNACK) { + status_flags |= I2C_SLAVE_STATUS_RECEIVED_NACK; + } + /* Check Transmit Collision */ + if (status & SERCOM_I2CS_STATUS_COLL) { + status_flags |= I2C_SLAVE_STATUS_COLLISION; + } + /* Check Bus Error */ + if (status & SERCOM_I2CS_STATUS_BUSERR) { + status_flags |= I2C_SLAVE_STATUS_BUS_ERROR; + } + + return status_flags; +} + +/** + * \brief Clears a module status flag + * + * Clears the given status flag of the module. + * + * \note Not all status flags can be cleared. + * + * \param[in] module Pointer to the I<SUP>2</SUP>C software device struct + * \param[in] status_flags Bit mask of status flags to clear + * + */ +void i2c_slave_clear_status( + struct i2c_slave_module *const module, + uint32_t status_flags) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Clear Address Match flag */ + if (status_flags & I2C_SLAVE_STATUS_ADDRESS_MATCH) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + /* Clear Data Ready flag */ + if (status_flags & I2C_SLAVE_STATUS_DATA_READY) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + /* Clear Stop flag */ + if (status_flags & I2C_SLAVE_STATUS_STOP_RECEIVED) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + /* Clear SCL Low Timeout */ + if (status_flags & I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_LOWTOUT; + } + /* Clear Transmit Collision */ + if (status_flags & I2C_SLAVE_STATUS_COLLISION) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_COLL; + } + /* Clear Bus Error */ + if (status_flags & I2C_SLAVE_STATUS_BUS_ERROR) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_BUSERR; + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c new file mode 100644 index 000000000..0a6f234f8 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c @@ -0,0 +1,434 @@ +/** + * \file + * + * \brief SAM I2C Slave Interrupt Driver + * + * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_slave_interrupt.h" + +/** + * \brief Enables sending of NACK on address match + * + * Enables sending of NACK on address match, thus discarding + * any incoming transaction. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_enable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = true; +} + +/** + * \brief Disables sending NACK on address match + * + * Disables sending of NACK on address match, thus + * acknowledging incoming transactions. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_disable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = false; +} + +/** + * \internal + * Reads next data. Used by interrupt handler to get next data byte from master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_read( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Read byte from master and put in buffer. */ + *(module->buffer++) = i2c_hw->DATA.reg; + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \internal + * Writes next data. Used by interrupt handler to send next data byte to master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_write( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Write byte from buffer to master */ + i2c_hw->DATA.reg = *(module->buffer++); + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. To enable the callback, the + * \ref i2c_slave_enable_callback function must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_slave_register_callback( + struct i2c_slave_module *const module, + i2c_slave_callback_t callback, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback. */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * Removes the currently registered callback for the given callback + * type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to unregister + */ +void i2c_slave_unregister_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Register callback. */ + module->callbacks[callback_type] = NULL; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \brief Initiates a reads packet operation + * + * Reads a data packet from the master. A write request must be initiated by + * the master before the packet can be read. + * + * The \ref I2C_SLAVE_CALLBACK_WRITE_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting asynchronously reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_read_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + /* Read will begin when master initiates the transfer */ + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the master. A read request must be initiated by + * the master before the packet can be written. + * + * The \ref I2C_SLAVE_CALLBACK_READ_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_write_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + return STATUS_OK; +} + +/** + * \internal Interrupt handler for I<SUP>2</SUP>C slave + * + * \param[in] instance Sercom instance that triggered the interrupt + */ +void _i2c_slave_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling. */ + struct i2c_slave_module *module = + (struct i2c_slave_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Combine callback registered and enabled masks. */ + uint8_t callback_mask = module->enabled_callback; + callback_mask &= module->registered_callback; + + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Address match */ + /* Check if last transfer is done - repeated start */ + if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_WRITE) { + + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } + } else if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_READ) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + /* An error occurred in last packet transfer */ + module->status = STATUS_ERR_IO; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER))) { + module->callbacks[I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER](module); + } + } + if (module->nack_on_address) { + /* NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { + /* Set transfer direction in module instance */ + module->transfer_direction = I2C_TRANSFER_READ; + + /* Read request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574*/ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } else { + /* Set transfer direction in dev inst */ + module->transfer_direction = I2C_TRANSFER_WRITE; + + /* Write request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } + + /* ACK or NACK address, Workaround 13574 */ + _i2c_slave_set_ctrlb_cmd3(module); + + /* ACK next incoming packet, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Stop condition on bus - current transfer done */ + + /* Clear Stop interrupt */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_DRDY; + + if (!((module->enabled_callback & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) + || (module->enabled_callback == (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)))) { + /* Disable address match if read/write request is not enabled */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + + if (!(module->status == STATUS_ERR_OVERFLOW || module->status == STATUS_ERR_IO)) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Read from master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + /* Write to master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Check if buffer is full, or NACK from master */ + if (module->buffer_remaining <= 0 || + (module->transfer_direction == I2C_TRANSFER_READ && + (module->buffer_length > module->buffer_remaining) && + (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK))) { + + module->buffer_remaining = 0; + module->buffer_length = 0; + + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + /* Buffer is full, send NACK, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Set status, new character in DATA register will overflow + * buffer */ + module->status = STATUS_ERR_OVERFLOW; + + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR)) { + /* Read complete */ + module->callbacks[I2C_SLAVE_CALLBACK_ERROR](module); + } + } else { + /* Release SCL and wait for new start condition */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Transfer successful */ + module->status = STATUS_OK; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + + /* Continue buffer write/read */ + } else if (module->buffer_length > 0 && module->buffer_remaining > 0) { + /* Call function based on transfer direction */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_slave_read(module); + } else { + _i2c_slave_write(module); + } + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master.c new file mode 100644 index 000000000..ce4f58c23 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master.c @@ -0,0 +1,881 @@ +/** + * \file + * + * \brief SAM D20 I2C Master Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_master.h" + +#if I2C_MASTER_CALLBACK_MODE == true +# include "i2c_master_interrupt.h" +#endif + +#if !defined(__DOXYGEN__) + +/** + * \internal Sets configurations to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK If module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baud rate is not compatible + * with set GCLK frequency + */ +static enum status_code _i2c_master_set_config( + struct i2c_master_module *const module, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(config); + + /* Temporary variables. */ + uint32_t tmp_ctrla; + int32_t tmp_baud; + enum status_code tmp_status_code = STATUS_OK; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + Sercom *const sercom_hw = module->hw; + + uint8_t sercom_index = _sercom_get_sercom_inst_index(sercom_hw); + + /* Pin configuration */ + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Save timeout on unknown bus state in software module. */ + module->unknown_bus_state_timeout = config->unknown_bus_state_timeout; + + /* Save timeout on buffer write. */ + module->buffer_timeout = config->buffer_timeout; + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + /* Check and set start data hold timeout. */ + if (config->start_hold_time != I2C_MASTER_START_HOLD_TIME_DISABLED) { + tmp_ctrla |= config->start_hold_time; + } + + /* Check and set SCL low timeout. */ + if (config->scl_low_timeout) { + tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUT; + } + + /* Check and set inactive bus timeout. */ + if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) { + tmp_ctrla |= config->inactive_timeout; + } + + /* Write config to register CTRLA. */ + i2c_module->CTRLA.reg |= tmp_ctrla; + + /* Set configurations in CTRLB. */ + i2c_module->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; + + /* Find and set baudrate. */ + tmp_baud = (int32_t)(div_ceil( + system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index), + (2000*(config->baud_rate))) - 5); + + /* Check that baud rate is supported at current speed. */ + if (tmp_baud > 255 || tmp_baud < 0) { + /* Baud rate not supported. */ + tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE; + } else { + /* Baud rate acceptable. */ + i2c_module->BAUD.reg = (uint8_t)tmp_baud; + } + + return tmp_status_code; +} +#endif /* __DOXYGEN__ */ + +/** + * \brief Initializes the requested I<SUP>2</SUP>C hardware module + * + * Initializes the SERCOM I<SUP>2</SUP>C master device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + * \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baudrate is not compatible + * with set GCLK frequency + * + */ +enum status_code i2c_master_init( + struct i2c_master_module *const module, + Sercom *const hw, + const struct i2c_master_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Turn on module in PM */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + + /* Check if module is enabled. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress. */ + if (i2c_module->CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) { + return STATUS_BUSY; + } + +#if I2C_MASTER_CALLBACK_MODE == true + /* Get sercom instance index and register callback. */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _i2c_master_interrupt_handler); + _sercom_instances[instance_index] = module; + + /* Initialize values in module. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + + module->status = STATUS_OK; + module->buffer = NULL; +#endif + + /* Set sercom module to operate in I2C master mode. */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER; + + /* Set config and return status. */ + return _i2c_master_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * Reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_master_reset(struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Disable module */ + i2c_master_disable(module); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Clear all pending interrupts */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync */ + _i2c_master_wait_for_sync(module); + + /* Reset module */ + i2c_module->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; +} + +#if !defined(__DOXYGEN__) +/** + * \internal + * Address response. Called when address is answered or timed out. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of address response. + * \retval STATUS_OK No error has occurred + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +static enum status_code _i2c_master_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error and ignore bus-error; workaround for BUSSTATE stuck in + * BUSY */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) { + + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB; + + /* Check arbitration. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return packet collision. */ + return STATUS_ERR_PACKET_COLLISION; + } + /* Check that slave responded with ack. */ + } else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Slave busy. Issue ack and stop command. */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + + /* Return bad address value. */ + return STATUS_ERR_BAD_ADDRESS; + } + + return STATUS_OK; +} + +/** + * \internal + * Waits for answer on bus. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +static enum status_code _i2c_master_wait_for_bus( + struct i2c_master_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply. */ + uint16_t timeout_counter = 0; + while (!(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) && + !(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) { + + /* Check timeout condition. */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} +#endif /* __DOXYGEN__ */ + +/** + * \internal + * Starts blocking read operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + /* Written buffer counter. */ + uint16_t counter = 0; + + /* Set address and direction bit. Will send start command on bus. */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Set action to ack. */ + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Read data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + if (module->send_nack && tmp_data_length == 0) { + /* Set action to NACK */ + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + } else { + /* Save data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter++] = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + } + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + } + + if (module->send_stop) { + /* Send stop command unless arbitration is lost. */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(1); + } + + /* Save last data to buffer. */ + _i2c_master_wait_for_sync(module); + packet->data[counter] = i2c_module->DATA.reg; + } + + return tmp_status; +} + +/** + * \brief Reads data packet from slave + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Reads data packet from slave without sending a stop condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus without sending a stop condition when done, thus retaining ownership of + * the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking read operation. + * \brief Reads data packet from slave without sending a nack signal and a stop + * condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus without sending a nack signal and a stop condition when done, + * thus retaining ownership of the bus when done. To end the transaction, a + * \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of reading packet. + * \retval STATUS_OK The packet was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_packet_wait_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = false; + + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts blocking write operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK The packet was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Return value. */ + enum status_code tmp_status; + uint16_t tmp_data_length = packet->data_length; + + _i2c_master_wait_for_sync(module); + + /* Set address and direction bit. Will send start command on bus. */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE; + + /* Wait for response on bus. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for address response error unless previous error is + * detected. */ + if (tmp_status == STATUS_OK) { + tmp_status = _i2c_master_address_response(module); + } + + /* Check that no error has occurred. */ + if (tmp_status == STATUS_OK) { + /* Buffer counter. */ + uint16_t buffer_counter = 0; + + /* Write data buffer. */ + while (tmp_data_length--) { + /* Check that bus ownership is not lost. */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + return STATUS_ERR_PACKET_COLLISION; + } + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = packet->data[buffer_counter++]; + + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + /* Check for error. */ + if (tmp_status != STATUS_OK) { + break; + } + + /* Check for NACK from slave. */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad data value. */ + tmp_status = STATUS_ERR_OVERFLOW; + break; + } + } + + if (module->send_stop) { + /* Stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + return tmp_status; +} + +/** + * \brief Writes data packet to slave + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus + * and sends a stop condition when finished. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy with a job + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave does not + * want more data and was not able to read + * last data sent + */ +enum status_code i2c_master_write_packet_wait( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = true; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Writes data packet to slave without sending a stop condition when done + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending a + * stop with the \ref i2c_master_send_stop function must be performed. + * + * \note This will stall the device from any other operation. For + * interrupt-driven operation, see \ref i2c_master_read_packet_job. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of write packet. + * \retval STATUS_OK If packet was write successfully + * \retval STATUS_BUSY If master module is busy + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + * \retval STATUS_ERR_TIMEOUT If timeout occurred + * \retval STATUS_ERR_OVERFLOW If slave did not acknowledge last sent + * data, indicating that slave do not want + * more data + */ +enum status_code i2c_master_write_packet_wait_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + +#if I2C_MASTER_CALLBACK_MODE == true + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } +#endif + + module->send_stop = false; + module->send_nack = true; + + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Sends stop condition on bus + * + * Sends a stop condition on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_stop function. If a stop condition + * is to be sent after a read, the \ref i2c_master_read_packet_wait + * function must be used. + * + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_stop(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send stop command */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); +} + +/** + * \brief Sends nack signal on bus + * + * Sends a nack signal on bus. + * + * \note This function can only be used after the + * \ref i2c_master_write_packet_wait_no_nack function, + * or \ref i2c_master_read_byte function. + * \param[in,out] module Pointer to the software instance struct + */ +void i2c_master_send_nack(struct i2c_master_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Send nack signal */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; +} + +/** + * \brief Reads one byte data from slave + * + * \param[in,out] module Pointer to software module struct + * \param[out] byte Read one byte data to slave + * + * \return Status of reading byte. + * \retval STATUS_OK One byte was read successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_read_byte( + struct i2c_master_module *const module, + uint8_t *byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + *byte = i2c_module->DATA.reg; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + + return tmp_status; +} + +/** + * \brief Write one byte data to slave + * + * \param[in,out] module Pointer to software module struct + * \param[in] byte Send one byte data to slave + * + * \return Status of writing byte. + * \retval STATUS_OK One byte was write successfully + * \retval STATUS_ERR_TIMEOUT If no response was given within + * specified timeout period + * \retval STATUS_ERR_DENIED If error on bus + * \retval STATUS_ERR_PACKET_COLLISION If arbitration is lost + * \retval STATUS_ERR_BAD_ADDRESS If slave is busy, or no slave + * acknowledged the address + */ +enum status_code i2c_master_write_byte( + struct i2c_master_module *const module, + uint8_t byte) +{ + enum status_code tmp_status; + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Write byte to slave. */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = byte; + /* Wait for response. */ + tmp_status = _i2c_master_wait_for_bus(module); + return tmp_status; +}
\ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master_interrupt.c new file mode 100644 index 000000000..07c892a03 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_master_interrupt.c @@ -0,0 +1,670 @@ +/** + * \file + * + * \brief SAM D20 I2C Master Interrupt Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_master_interrupt.h" + +/** + * \internal + * Read next data. Used by interrupt handler to get next data byte from slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_read( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Find index to save next value in buffer */ + uint16_t buffer_index = module->buffer_length - module->buffer_remaining; + + module->buffer_remaining--; + + if (!module->buffer_remaining) { + /* Send nack */ + if (module->send_nack) + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(1); + } + } else { + i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + } + + /* Read byte from slave and put in buffer */ + _i2c_master_wait_for_sync(module); + module->buffer[buffer_index] = i2c_module->DATA.reg; +} + +/** + * \internal + * + * Write next data. Used by interrupt handler to send next data byte to slave. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_write(struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for ack from slave */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) + { + /* Set status */ + module->status = STATUS_ERR_OVERFLOW; + /* Do not write more data */ + return; + } + + /* Find index to get next byte in buffer */ + uint16_t buffer_index = module->buffer_length - module->buffer_remaining; + + module->buffer_remaining--; + + /* Write byte from buffer to slave */ + _i2c_master_wait_for_sync(module); + i2c_module->DATA.reg = module->buffer[buffer_index]; +} + +/** + * \internal + * Acts on slave address response. Checks for errors concerning master->slave + * handshake. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_master_async_address_response( + struct i2c_master_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Check for error. Ignore bus-error; workaround for bus state stuck in + * BUSY. + */ + if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) + { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; + + /* Check arbitration */ + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { + /* Return busy */ + module->status = STATUS_ERR_PACKET_COLLISION; + } + } + + if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { + /* Return bad address value */ + module->status = STATUS_ERR_BAD_ADDRESS; + module->buffer_remaining = 0; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } + } + + module->buffer_length = module->buffer_remaining; + + /* Check for status OK. */ + if (module->status == STATUS_BUSY) { + /* Call function based on transfer direction. */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. + * + * To enable the callback, the \ref i2c_master_enable_callback function + * must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_master_register_callback( + struct i2c_master_module *const module, + const i2c_master_callback_t callback, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as registered */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * When called, the currently registered callback for the given callback type + * will be removed. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Specifies the callback type to unregister + */ +void i2c_master_unregister_callback( + struct i2c_master_module *const module, + enum i2c_master_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Register callback */ + module->callbacks[callback_type] = NULL; + + /* Clear corresponding bit to set callback as unregistered */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \internal + * Starts a read bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal + * Starts a read packet operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_read_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_READ; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit. Will send start command on bus */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ; + + return STATUS_OK; +} + +/** + * \brief Initiates a read packet operation + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C + * bus. This is the non-blocking equivalent of \ref i2c_master_read_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_read_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP */ + module->send_stop = true; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a STOP condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C bus without + * sending a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = true; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \brief Initiates a read packet operation without sending a NACK signal and a + * STOP condition when done + * + * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C bus without + * sending a nack and a stop condition, thus retaining ownership of the bus when done. + * To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition must be + * performed. + * + * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another operation + */ +enum status_code i2c_master_read_packet_job_no_nack( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with a job */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we don't send STOP */ + module->send_stop = false; + module->send_nack = false; + /* Start reading */ + return _i2c_master_read_packet(module, packet); +} + +/** + * \internal + * Starts a write bytes operation. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting write I<SUP>2</SUP>C bytes. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_bytes( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + module->send_stop = false; + module->send_nack = false; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + return STATUS_OK; +} + +/** + * \internal Initiates a write packet operation + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +static enum status_code _i2c_master_write_packet( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Save packet to software module */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->transfer_direction = I2C_TRANSFER_WRITE; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + i2c_module->INTENSET.reg = + SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; + + /* Set address and direction bit, will send start command on bus */ + i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE; + + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C + * bus. This is the non-blocking equivalent of \ref i2c_master_write_packet_wait. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_master_write_packet_job( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Make sure we send STOP at end*/ + module->send_stop = true; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \brief Initiates a write packet operation without sending a STOP condition when done + * + * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus + * without sending a stop condition, thus retaining ownership of the bus when + * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or + * \ref i2c_master_write_packet_wait "write" with stop condition or sending + * a stop with the \ref i2c_master_send_stop function must be performed. + * + * This is the non-blocking equivalent of \ref i2c_master_write_packet_wait_no_stop. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet job. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another + */ +enum status_code i2c_master_write_packet_job_no_stop( + struct i2c_master_module *const module, + struct i2c_master_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy with another job. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Do not send stop condition when done */ + module->send_stop = false; + module->send_nack = true; + /* Start write operation */ + return _i2c_master_write_packet(module, packet); +} + +/** + * \internal + * Interrupt handler for I<SUP>2</SUP>C master. + * + * \param[in] instance SERCOM instance that triggered the interrupt + */ +void _i2c_master_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling */ + struct i2c_master_module *module = + (struct i2c_master_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Combine callback registered and enabled masks */ + uint8_t callback_mask = module->enabled_callback & + module->registered_callback; + + /* Check if the module should respond to address ack */ + if ((module->buffer_length <= 0) && (module->buffer_remaining > 0)) { + /* Call function for address response */ + _i2c_master_async_address_response(module); + + /* Check if buffer write is done */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->status = STATUS_OK; + + if (module->send_stop) { + /* Send stop condition */ + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + } else { + /* Clear write interrupt flag */ + i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MB; + } + if (callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + + /* Continue buffer write/read */ + } else if ((module->buffer_length > 0) && (module->buffer_remaining > 0)){ + /* Check that bus ownership is not lost */ + if (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) { + module->status = STATUS_ERR_PACKET_COLLISION; + } else if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_master_write(module); + } else { + _i2c_master_read(module); + } + } + + /* Check if read buffer transfer is complete */ + if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && + (module->status == STATUS_BUSY) && + (module->transfer_direction == I2C_TRANSFER_READ)) { + + /* Stop packet operation */ + i2c_module->INTENCLR.reg = + SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; + module->buffer_length = 0; + module->status = STATUS_OK; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_MASTER_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + module->callbacks[I2C_MASTER_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); + } + } + + /* Check for error */ + if ((module->status != STATUS_BUSY) && (module->status != STATUS_OK)) { + /* Stop packet operation */ + i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | + SERCOM_I2CM_INTENCLR_SB; + + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Send nack and stop command unless arbitration is lost */ + if ((module->status != STATUS_ERR_PACKET_COLLISION) && + module->send_stop) { + _i2c_master_wait_for_sync(module); + i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | + SERCOM_I2CM_CTRLB_CMD(3); + } + + /* Call error callback if enabled and registered */ + if (callback_mask & (1 << I2C_MASTER_CALLBACK_ERROR)) { + module->callbacks[I2C_MASTER_CALLBACK_ERROR](module); + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c new file mode 100644 index 000000000..c403479a3 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave.c @@ -0,0 +1,693 @@ +/** + * \file + * + * \brief SAM D20 I2C Slave Driver + * + * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_slave.h" +#if I2C_SLAVE_CALLBACK_MODE == true +# include "i2c_slave_interrupt.h" +#endif + +/** + * \internal Sets configuration to module + * + * \param[out] module Pointer to software module structure + * \param[in] config Configuration structure with configurations to set + * + * \return Status of setting configuration. + * \retval STATUS_OK Module was configured correctly + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +static enum status_code _i2c_slave_set_config( + struct i2c_slave_module *const module, + const struct i2c_slave_config *const config) +{ + uint32_t tmp_ctrla; + + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(config); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + Sercom *const sercom_hw = module->hw; + + module->buffer_timeout = config->buffer_timeout; + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + uint32_t pad0 = config->pinmux_pad0; + uint32_t pad1 = config->pinmux_pad1; + + /* SERCOM PAD0 - SDA */ + if (pad0 == PINMUX_DEFAULT) { + pad0 = _sercom_get_default_pad(sercom_hw, 0); + } + pin_conf.mux_position = pad0 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad0 >> 16, &pin_conf); + + /* SERCOM PAD1 - SCL */ + if (pad1 == PINMUX_DEFAULT) { + pad1 = _sercom_get_default_pad(sercom_hw, 1); + } + pin_conf.mux_position = pad1 & 0xFFFF; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK; + system_pinmux_pin_set_config(pad1 >> 16, &pin_conf); + + /* Prepare config to write to register CTRLA */ + if (config->run_in_standby || system_is_debugger_present()) { + tmp_ctrla = SERCOM_I2CS_CTRLA_RUNSTDBY; + } else { + tmp_ctrla = 0; + } + + tmp_ctrla |= config->sda_hold_time | + (config->scl_low_timeout << SERCOM_I2CS_CTRLA_LOWTOUT_Pos); + + i2c_hw->CTRLA.reg |= tmp_ctrla; + + /* Set CTRLB configuration */ + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_SMEN | config->address_mode; + + i2c_hw->ADDR.reg = config->address << SERCOM_I2CS_ADDR_ADDR_Pos | + config->address_mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos | + config->enable_general_call_address << SERCOM_I2CS_ADDR_GENCEN_Pos; + + return STATUS_OK; +} + +/** + * \brief Initializes the requested I<SUP>2</SUP>C hardware module + * + * Initializes the SERCOM I<SUP>2</SUP>C slave device requested and sets the provided + * software module struct. Run this function before any further use of + * the driver. + * + * \param[out] module Pointer to software module struct + * \param[in] hw Pointer to the hardware instance + * \param[in] config Pointer to the configuration struct + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than + * previously set + */ +enum status_code i2c_slave_init( + struct i2c_slave_module *const module, + Sercom *const hw, + const struct i2c_slave_config *const config) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize software module */ + module->hw = hw; + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Check if module is enabled. */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress. */ + if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_SWRST) { + return STATUS_BUSY; + } + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Turn on module in PM */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Get sercom instance index. */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + + /* Save software module in interrupt handler. */ + _sercom_set_handler(instance_index, _i2c_slave_interrupt_handler); + + /* Save software module. */ + _sercom_instances[instance_index] = module; + + /* Initialize values in module. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->nack_on_address = config->enable_nack_on_address; +#endif + + /* Set SERCOM module to operate in I2C slave mode. */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_MODE_I2C_SLAVE; + + /* Set config and return status. */ + return _i2c_slave_set_config(module, config); +} + +/** + * \brief Resets the hardware module + * + * This will reset the module to hardware defaults. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_reset( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Reset module instance. */ + module->registered_callback = 0; + module->enabled_callback = 0; + module->buffer_length = 0; + module->buffer_remaining = 0; + module->buffer = NULL; +#endif + + /* Disable module */ + i2c_slave_disable(module); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Clear all pending interrupts. */ + system_interrupt_enter_critical_section(); + system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); + system_interrupt_leave_critical_section(); +#endif + + /* Wait for sync. */ + _i2c_slave_wait_for_sync(module); + + /* Reset module. */ + i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_SWRST; +} + +/** + * \internal Waits for answer on bus + * + * \param[in] module Pointer to software module structure + * + * \return Status of bus. + * \retval STATUS_OK If given response from slave device + * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout + * period + */ +static enum status_code _i2c_slave_wait_for_bus( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cm *const i2c_module = &(module->hw->I2CM); + + /* Wait for reply. */ + uint16_t timeout_counter = 0; + while ((!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC)) && + (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH))) { + + /* Check timeout condition. */ + if (++timeout_counter >= module->buffer_timeout) { + return STATUS_ERR_TIMEOUT; + } + } + return STATUS_OK; +} + +/** + * \brief Writes a packet to the master + * + * Writes a packet to the master. This will wait for the master to issue + * a request. + * + * \param[in] module Pointer to software module structure + * \param[in] packet Packet to write to master + * + * \return Status of packet write. + * \retval STATUS_OK Packet was written successfully + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_BAD_FORMAT Master wants to write data + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I<SUP>2</SUP>C module is busy with a job + * \retval STATUS_ERR_ERR_OVERFLOW Master NACKed before entire packet was + * transferred + * \retval STATUS_ERR_TIMEOUT No response was given within the timeout + * period + */ +enum status_code i2c_slave_write_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + /* Check if there was an error in last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + + /* Check direction */ + if (!(i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Write request from master, send NACK and return, workaround 13574*/ + _i2c_slave_set_ctrlb_ackact(module, false); + _i2c_slave_set_ctrlb_cmd3(module); + return STATUS_ERR_BAD_FORMAT; + } + + /* Read request from master, ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + _i2c_slave_set_ctrlb_cmd3(module); + + uint16_t i = 0; + + /* Wait for data interrupt */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + while (length--) { + /* Write data */ + _i2c_slave_wait_for_sync(module); + i2c_hw->DATA.reg = packet->data[i++]; + + /* Wait for response from master */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK && + length !=0) { + /* NACK from master, abort */ + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_ERR_OVERFLOW; + /* Workaround: PIF will probably not be set, ignore */ + } + /* ACK from master, continue writing */ + } + + /* Release line */ + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); + + return STATUS_OK; +} + +/** + * \brief Reads a packet from the master + * + * Reads a packet from the master. This will wait for the master to issue a + * request. + * + * \param[in] module Pointer to software module structure + * \param[out] packet Packet to read from master + * + * \return Status of packet read. + * \retval STATUS_OK Packet was read successfully + * \retval STATUS_ABORTED Master sent stop condition or repeated + * start before specified length of bytes + * was received + * \retval STATUS_ERR_IO There was an error in the previous transfer + * \retval STATUS_ERR_DENIED Start condition not received, another + * interrupt flag is set + * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided + * \retval STATUS_ERR_BUSY The I<SUP>2</SUP>C module is busy with a job + * \retval STATUS_ERR_BAD_FORMAT Master wants to read data + * \retval STATUS_ERR_ERR_OVERFLOW Last byte received overflows buffer + */ +enum status_code i2c_slave_read_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + Assert(packet); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint16_t length = packet->data_length; + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Check if the module is busy with a job or AMATCH is enabled */ + if (module->buffer_remaining > 0 || + (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + return STATUS_BUSY; + } +#endif + + enum status_code status; + + /* Wait for master to send address packet */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return STATUS_ERR_DENIED; + } + + /* Check if there was an error in the last transfer */ + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + return STATUS_ERR_IO; + } + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master, send NACK and return, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + _i2c_slave_set_ctrlb_cmd3(module); + return STATUS_ERR_BAD_FORMAT; + } + + /* Write request from master, ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + _i2c_slave_set_ctrlb_cmd3(module); + + uint16_t i = 0; + while (length--) { + + /* Wait for next byte or stop condition */ + status = _i2c_slave_wait_for_bus(module); + if (status != STATUS_OK) { + /* Timeout, return */ + return status; + } + + if ((i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) || + i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Master sent stop condition, or repeated start, read done */ + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + return STATUS_ABORTED; + } + + /* Read data */ + _i2c_slave_wait_for_sync(module); + packet->data[i++] = i2c_hw->DATA.reg; + + } + + /* Packet read done, wait for packet to NACK, Stop or repeated start */ + status = _i2c_slave_wait_for_bus(module); + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Buffer is full, send NACK */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + } + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Clear stop flag */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + return STATUS_OK; +} + +/** + * \brief Waits for a start condition on the bus + * + * \note This function is only available for 7-bit slave addressing. + * + * Waits for the master to issue a start condition on the bus. + * \note This function does not check for errors in the last transfer, + * this will be discovered when reading or writing. + * + * \param[in] module Pointer to software module structure + * + * \return Direction of the current transfer, when in slave mode. + * \retval I2C_SLAVE_DIRECTION_NONE No request from master within timeout + * period + * \retval I2C_SLAVE_DIRECTION_READ Write request from master + * \retval I2C_SLAVE_DIRECTION_WRITE Read request from master + */ +enum i2c_slave_direction i2c_slave_get_direction_wait( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + enum status_code status; + + /* Wait for address interrupt */ + status = _i2c_slave_wait_for_bus(module); + + if (status != STATUS_OK) { + /* Timeout, return */ + return I2C_SLAVE_DIRECTION_NONE; + } + + if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { + /* Not address interrupt, something is wrong */ + return I2C_SLAVE_DIRECTION_NONE; + } + + /* Check direction */ + if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { + /* Read request from master */ + return I2C_SLAVE_DIRECTION_WRITE; + } else { + /* Write request from master */ + return I2C_SLAVE_DIRECTION_READ; + } +} + +/** + * \brief Retrieves the current module status + * + * Checks the status of the module and returns it as a bitmask of status + * flags. + * + * \param[in] module Pointer to the I<SUP>2</SUP>C slave software device struct + * + * \return Bitmask of status flags. + * + * \retval I2C_SLAVE_STATUS_ADDRESS_MATCH A valid address has been received + * \retval I2C_SLAVE_STATUS_DATA_READY A I<SUP>2</SUP>C slave byte transmission is + * successfully completed + * \retval I2C_SLAVE_STATUS_STOP_RECEIVED A stop condition is detected for a + * transaction being processed + * \retval I2C_SLAVE_STATUS_CLOCK_HOLD The slave is holding the SCL line + * low + * \retval I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT An SCL low time-out has occurred + * \retval I2C_SLAVE_STATUS_REPEATED_START Indicates a repeated start, only + * valid if \ref + * I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set + * \retval I2C_SLAVE_STATUS_RECEIVED_NACK The last data packet sent was not + * acknowledged + * \retval I2C_SLAVE_STATUS_COLLISION The I<SUP>2</SUP>C slave was not able to + * transmit a high data or NACK bit + * \retval I2C_SLAVE_STATUS_BUS_ERROR An illegal bus condition has + * occurred on the bus + */ +uint32_t i2c_slave_get_status( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + uint8_t intflags = i2c_hw->INTFLAG.reg; + uint8_t status = i2c_hw->STATUS.reg; + uint32_t status_flags = 0; + + /* Check Address Match flag */ + if (intflags & SERCOM_I2CS_INTFLAG_AMATCH) { + status_flags |= I2C_SLAVE_STATUS_ADDRESS_MATCH; + } + /* Check Data Ready flag */ + if (intflags & SERCOM_I2CS_INTFLAG_DRDY) { + status_flags |= I2C_SLAVE_STATUS_DATA_READY; + } + /* Check Stop flag */ + if (intflags & SERCOM_I2CS_INTFLAG_PREC) { + status_flags |= I2C_SLAVE_STATUS_STOP_RECEIVED; + } + /* Check Clock Hold */ + if (status & SERCOM_I2CS_STATUS_CLKHOLD) { + status_flags |= I2C_SLAVE_STATUS_CLOCK_HOLD; + } + /* Check SCL Low Timeout */ + if (status & SERCOM_I2CS_STATUS_LOWTOUT) { + status_flags |= I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT; + } + /* Check Repeated Start */ + if (status & SERCOM_I2CS_STATUS_SR) { + status_flags |= I2C_SLAVE_STATUS_REPEATED_START; + } + /* Check Received Not Acknowledge */ + if (status & SERCOM_I2CS_STATUS_RXNACK) { + status_flags |= I2C_SLAVE_STATUS_RECEIVED_NACK; + } + /* Check Transmit Collision */ + if (status & SERCOM_I2CS_STATUS_COLL) { + status_flags |= I2C_SLAVE_STATUS_COLLISION; + } + /* Check Bus Error */ + if (status & SERCOM_I2CS_STATUS_BUSERR) { + status_flags |= I2C_SLAVE_STATUS_BUS_ERROR; + } + + return status_flags; +} + +/** + * \brief Clears a module status flag + * + * Clears the given status flag of the module. + * + * \note Not all status flags can be cleared. + * + * \param[in] module Pointer to the I<SUP>2</SUP>C software device struct + * \param[in] status_flags Bit mask of status flags to clear + * + */ +void i2c_slave_clear_status( + struct i2c_slave_module *const module, + uint32_t status_flags) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Clear Address Match flag */ + if (status_flags & I2C_SLAVE_STATUS_ADDRESS_MATCH) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + /* Clear Data Ready flag */ + if (status_flags & I2C_SLAVE_STATUS_DATA_READY) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + /* Clear Stop flag */ + if (status_flags & I2C_SLAVE_STATUS_STOP_RECEIVED) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + /* Clear SCL Low Timeout */ + if (status_flags & I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_LOWTOUT; + } + /* Clear Transmit Collision */ + if (status_flags & I2C_SLAVE_STATUS_COLLISION) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_COLL; + } + /* Clear Bus Error */ + if (status_flags & I2C_SLAVE_STATUS_BUS_ERROR) { + i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_BUSERR; + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave_interrupt.c new file mode 100644 index 000000000..29d222bd2 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_samd20/i2c_slave_interrupt.c @@ -0,0 +1,434 @@ +/** + * \file + * + * \brief SAM D20 I2C Slave Interrupt Driver + * + * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "i2c_slave_interrupt.h" + +/** + * \brief Enables sending of NACK on address match + * + * Enables sending of NACK on address match, thus discarding + * any incoming transaction. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_enable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = true; +} + +/** + * \brief Disables sending NACK on address match + * + * Disables sending of NACK on address match, thus + * acknowledging incoming transactions. + * + * \param[in,out] module Pointer to software module structure + */ +void i2c_slave_disable_nack_on_address( + struct i2c_slave_module *const module) +{ + /* Sanity check arguments. */ + Assert(module); + + module->nack_on_address = false; +} + +/** + * \internal + * Reads next data. Used by interrupt handler to get next data byte from master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_read( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Read byte from master and put in buffer. */ + *(module->buffer++) = i2c_hw->DATA.reg; + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \internal + * Writes next data. Used by interrupt handler to send next data byte to master. + * + * \param[in,out] module Pointer to software module structure + */ +static void _i2c_slave_write( + struct i2c_slave_module *const module) +{ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Write byte from buffer to master */ + i2c_hw->DATA.reg = *(module->buffer++); + + /*Decrement remaining buffer length */ + module->buffer_remaining--; +} + +/** + * \brief Registers callback for the specified callback type + * + * Associates the given callback function with the + * specified callback type. To enable the callback, the + * \ref i2c_slave_enable_callback function must be used. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback Pointer to the function desired for the + * specified callback + * \param[in] callback_type Callback type to register + */ +void i2c_slave_register_callback( + struct i2c_slave_module *const module, + i2c_slave_callback_t callback, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + Assert(callback); + + /* Register callback. */ + module->callbacks[callback_type] = callback; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters callback for the specified callback type + * + * Removes the currently registered callback for the given callback + * type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to unregister + */ +void i2c_slave_unregister_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Register callback. */ + module->callbacks[callback_type] = NULL; + + /* Set corresponding bit to set callback as initiated. */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \brief Initiates a reads packet operation + * + * Reads a data packet from the master. A write request must be initiated by + * the master before the packet can be read. + * + * The \ref I2C_SLAVE_CALLBACK_WRITE_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting asynchronously reading I<SUP>2</SUP>C packet. + * \retval STATUS_OK If reading was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_read_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + /* Read will begin when master initiates the transfer */ + return STATUS_OK; +} + +/** + * \brief Initiates a write packet operation + * + * Writes a data packet to the master. A read request must be initiated by + * the master before the packet can be written. + * + * The \ref I2C_SLAVE_CALLBACK_READ_REQUEST callback can be used to call this + * function. + * + * \param[in,out] module Pointer to software module struct + * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer + * + * \return Status of starting writing I<SUP>2</SUP>C packet. + * \retval STATUS_OK If writing was started successfully + * \retval STATUS_BUSY If module is currently busy with another transfer + */ +enum status_code i2c_slave_write_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + Assert(packet); + + /* Check if the I2C module is busy doing async operation. */ + if (module->buffer_remaining > 0) { + return STATUS_BUSY; + } + + /* Save packet to software module. */ + module->buffer = packet->data; + module->buffer_remaining = packet->data_length; + module->buffer_length = packet->data_length; + module->status = STATUS_BUSY; + + /* Enable interrupts */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; + + return STATUS_OK; +} + +/** + * \internal Interrupt handler for I<SUP>2</SUP>C slave + * + * \param[in] instance Sercom instance that triggered the interrupt + */ +void _i2c_slave_interrupt_handler( + uint8_t instance) +{ + /* Get software module for callback handling. */ + struct i2c_slave_module *module = + (struct i2c_slave_module*)_sercom_instances[instance]; + + Assert(module); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Combine callback registered and enabled masks. */ + uint8_t callback_mask = + module->enabled_callback & module->registered_callback; + + + if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { + /* Address match */ + /* Check if last transfer is done - repeated start */ + if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_WRITE) { + + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } + } else if (module->buffer_length != module->buffer_remaining && + module->transfer_direction == I2C_TRANSFER_READ) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE))) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + + if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | + SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { + /* An error occurred in last packet transfer */ + module->status = STATUS_ERR_IO; + + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER))) { + module->callbacks[I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER](module); + } + } + if (module->nack_on_address) { + /* NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { + /* Set transfer direction in module instance */ + module->transfer_direction = I2C_TRANSFER_READ; + + /* Read request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_READ_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574*/ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } else { + /* Set transfer direction in dev inst */ + module->transfer_direction = I2C_TRANSFER_WRITE; + + /* Write request from master */ + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)) { + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_REQUEST](module); + } + + if (module->buffer_length == 0) { + /* Data buffer not set up, NACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + } else { + /* ACK address, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + } + } + + /* ACK or NACK address, Workaround 13574 */ + _i2c_slave_set_ctrlb_cmd3(module); + + /* ACK next incoming packet, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, true); + + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { + /* Stop condition on bus - current transfer done */ + + /* Clear Stop interrupt */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_DRDY; + + if (!((module->enabled_callback & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) + || (module->enabled_callback == (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)))) { + /* Disable address match if read/write request is not enabled */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } + + if (!(module->status == STATUS_ERR_OVERFLOW || module->status == STATUS_ERR_IO)) { + module->status = STATUS_OK; + module->buffer_length = 0; + module->buffer_remaining = 0; + + /* Call appropriate callback if enabled and registered */ + if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_WRITE)) { + /* Read from master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); + } else if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE)) + && (module->transfer_direction == I2C_TRANSFER_READ)) { + /* Write to master complete */ + module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); + } + } + } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { + /* Check if buffer is full, or NACK from master */ + if (module->buffer_remaining <= 0 || + (module->transfer_direction == I2C_TRANSFER_READ && + (module->buffer_length > module->buffer_remaining) && + (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK))) { + + module->buffer_remaining = 0; + module->buffer_length = 0; + + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + /* Buffer is full, send NACK, workaround 13574 */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Set status, new character in DATA register will overflow + * buffer */ + module->status = STATUS_ERR_OVERFLOW; + + if (callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR)) { + /* Read complete */ + module->callbacks[I2C_SLAVE_CALLBACK_ERROR](module); + } + } else { + /* Release SCL and wait for new start condition */ + _i2c_slave_set_ctrlb_ackact(module, false); + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); + + /* Transfer successful */ + module->status = STATUS_OK; + + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_DRDY; + } + + /* Continue buffer write/read */ + } else if (module->buffer_length > 0 && module->buffer_remaining > 0) { + /* Call function based on transfer direction */ + if (module->transfer_direction == I2C_TRANSFER_WRITE) { + _i2c_slave_read(module); + } else { + _i2c_slave_write(module); + } + } + } +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h new file mode 100644 index 000000000..20eb78ec0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave.h @@ -0,0 +1,753 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Slave Driver + * + * Copyright (c) 2013-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef I2C_SLAVE_H_INCLUDED +#define I2C_SLAVE_H_INCLUDED + +#include "i2c_common.h" +#include <sercom.h> +#include <pinmux.h> + +#if I2C_SLAVE_CALLBACK_MODE == true +# include <sercom_interrupt.h> +#endif + +#ifndef PINMUX_DEFAULT +# define PINMUX_DEFAULT 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * + * @{ + * + */ + +/** + * \name I2C Slave Status Flags + * + * I<SUP>2</SUP>C slave status flags, returned by \ref i2c_slave_get_status() and cleared + * by \ref i2c_slave_clear_status(). + * @{ + */ + +/** Address Match. + * \note Should only be cleared internally by driver. + */ +#define I2C_SLAVE_STATUS_ADDRESS_MATCH (1UL << 0) +/** Data Ready. */ +#define I2C_SLAVE_STATUS_DATA_READY (1UL << 1) +/** Stop Received. */ +#define I2C_SLAVE_STATUS_STOP_RECEIVED (1UL << 2) +/** Clock Hold. + * \note Cannot be cleared, only valid when I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set. + */ +#define I2C_SLAVE_STATUS_CLOCK_HOLD (1UL << 3) +/** SCL Low Timeout. */ +#define I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT (1UL << 4) +/** Repeated Start. + * \note Cannot be cleared, only valid when I2C_SLAVE_STATUS_ADDRESS_MATCH is + * set. + */ +#define I2C_SLAVE_STATUS_REPEATED_START (1UL << 5) +/** Received not acknowledge. + * \note Cannot be cleared. + */ +#define I2C_SLAVE_STATUS_RECEIVED_NACK (1UL << 6) +/** Transmit Collision. */ +#define I2C_SLAVE_STATUS_COLLISION (1UL << 7) +/** Bus error. */ +#define I2C_SLAVE_STATUS_BUS_ERROR (1UL << 8) + +/** @} */ + +/** + * \brief I<SUP>2</SUP>C slave packet for read/write + * + * Structure to be used when transferring I<SUP>2</SUP>C slave packets. + */ +struct i2c_slave_packet { + /** Length of data array */ + uint16_t data_length; + /** Data array containing all data to be transferred */ + uint8_t *data; +}; + +#if I2C_SLAVE_CALLBACK_MODE == true + /** + * \brief Callback types + * + * The available callback types for the I<SUP>2</SUP>C slave. + */ +enum i2c_slave_callback { + /** Callback for packet write complete */ + I2C_SLAVE_CALLBACK_WRITE_COMPLETE, + /** Callback for packet read complete */ + I2C_SLAVE_CALLBACK_READ_COMPLETE, + /** + * Callback for read request from master - can be used to + * issue a write + */ + I2C_SLAVE_CALLBACK_READ_REQUEST, + /** + * Callback for write request from master - can be used to issue a read + */ + I2C_SLAVE_CALLBACK_WRITE_REQUEST, + /** Callback for error */ + I2C_SLAVE_CALLBACK_ERROR, + /** + * Callback for error in last transfer. Discovered on a new address + * interrupt. + */ + I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER, +# if !defined(__DOXYGEN__) + /** Total number of callbacks */ + _I2C_SLAVE_CALLBACK_N, +# endif +}; + +# if !defined(__DOXYGEN__) +/** Software module prototype */ +struct i2c_slave_module; + +/** Callback type */ +typedef void (*i2c_slave_callback_t)( + struct i2c_slave_module *const module); +# endif +#endif + +/** + * \brief Enum for the possible SDA hold times with respect to the negative + * edge of SCL + * + * Enum for the possible SDA hold times with respect to the negative edge + * of SCL. + */ +enum i2c_slave_sda_hold_time { + /** SDA hold time disabled */ + I2C_SLAVE_SDA_HOLD_TIME_DISABLED = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((0) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), + /** SDA hold time 50ns - 100ns */ + I2C_SLAVE_SDA_HOLD_TIME_50NS_100NS = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((1) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), + /** SDA hold time 300ns - 600ns */ + I2C_SLAVE_SDA_HOLD_TIME_300NS_600NS = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((2) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), + /** SDA hold time 400ns - 800ns */ + I2C_SLAVE_SDA_HOLD_TIME_400NS_800NS = + ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((3) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), +}; + +/** + * \brief Enum for the possible address modes + * + * Enum for the possible address modes. + */ +enum i2c_slave_address_mode { + /** Address match on address_mask used as a mask to address */ + I2C_SLAVE_ADDRESS_MODE_MASK = SERCOM_I2CS_CTRLB_AMODE(0), + /** Address math on both address and address_mask */ + I2C_SLAVE_ADDRESS_MODE_TWO_ADDRESSES = SERCOM_I2CS_CTRLB_AMODE(1), + /** + * Address match on range of addresses between and including address and + * address_mask + */ + I2C_SLAVE_ADDRESS_MODE_RANGE = SERCOM_I2CS_CTRLB_AMODE(2), +}; + +/** + * \brief Enum for the direction of a request + * + * Enum for the direction of a request. + */ +enum i2c_slave_direction { + /** Read */ + I2C_SLAVE_DIRECTION_READ, + /** Write */ + I2C_SLAVE_DIRECTION_WRITE, + /** No direction */ + I2C_SLAVE_DIRECTION_NONE, +}; + +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED +/** + * \brief Enum for the transfer speed + * + * Enum for the transfer speed. + */ +enum i2c_slave_transfer_speed { + /** Standard-mode (Sm) up to 100KHz and Fast-mode (Fm) up to 400KHz */ + I2C_SLAVE_SPEED_STANDARD_AND_FAST = SERCOM_I2CS_CTRLA_SPEED(0), + /** Fast-mode Plus (Fm+) up to 1MHz */ + I2C_SLAVE_SPEED_FAST_MODE_PLUS = SERCOM_I2CS_CTRLA_SPEED(1), + /** High-speed mode (Hs-mode) up to 3.4MHz */ + I2C_SLAVE_SPEED_HIGH_SPEED = SERCOM_I2CS_CTRLA_SPEED(2), +}; +#endif + +/** + * \brief SERCOM I<SUP>2</SUP>C slave driver software device instance structure. + * + * SERCOM I<SUP>2</SUP>C slave driver software instance structure, used to + * retain software state information of an associated hardware module instance. + * + * \note The fields of this structure should not be altered by the user + * application; they are reserved for module-internal use only. + */ +struct i2c_slave_module { +#if !defined(__DOXYGEN__) + /** Hardware instance initialized for the struct */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** Timeout value for polled functions */ + uint16_t buffer_timeout; +# ifdef FEATURE_I2C_10_BIT_ADDRESS + /** Using 10-bit addressing for the slave */ + bool ten_bit_address; +# endif +# if I2C_SLAVE_CALLBACK_MODE == true + /** Nack on address match */ + bool nack_on_address; + /** Pointers to callback functions */ + volatile i2c_slave_callback_t callbacks[_I2C_SLAVE_CALLBACK_N]; + /** Mask for registered callbacks */ + volatile uint8_t registered_callback; + /** Mask for enabled callbacks */ + volatile uint8_t enabled_callback; + /** The total number of bytes to transfer */ + volatile uint16_t buffer_length; + /** + * Counter used for bytes left to send in write and to count number of + * obtained bytes in read + */ + uint16_t buffer_remaining; + /** Data buffer for packet write and read */ + volatile uint8_t *buffer; + /** Save direction of request from master. 1 = read, 0 = write. */ + volatile enum i2c_transfer_direction transfer_direction; + /** Status for status read back in error callback */ + volatile enum status_code status; +# endif +#endif +}; + +/** + * \brief Configuration structure for the I<SUP>2</SUP>C slave device + * + * This is the configuration structure for the I<SUP>2</SUP>C slave device. It is used + * as an argument for \ref i2c_slave_init to provide the desired + * configurations for the module. The structure should be initialized using the + * \ref i2c_slave_get_config_defaults. + */ +struct i2c_slave_config { + /** Set to enable the SCL low timeout */ + bool enable_scl_low_timeout; + /** SDA hold time with respect to the negative edge of SCL */ + enum i2c_slave_sda_hold_time sda_hold_time; + /** Timeout to wait for master in polled functions */ + uint16_t buffer_timeout; + /** Addressing mode */ + enum i2c_slave_address_mode address_mode; + /** Address or upper limit of address range */ + uint16_t address; + /** Address mask, second address, or lower limit of address range */ + uint16_t address_mask; +#ifdef FEATURE_I2C_10_BIT_ADDRESS + /** Enable 10-bit addressing */ + bool ten_bit_address; +#endif + /** + * Enable general call address recognition (general call address + * is defined as 0000000 with direction bit 0). + */ + bool enable_general_call_address; + +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + /** Transfer speed mode */ + enum i2c_slave_transfer_speed transfer_speed; +#endif + +#if I2C_SLAVE_CALLBACK_MODE == true + /** + * Enable NACK on address match (this can be changed after initialization + * via the \ref i2c_slave_enable_nack_on_address and + * \ref i2c_slave_disable_nack_on_address functions). + */ + bool enable_nack_on_address; +#endif + /** GCLK generator to use as clock source */ + enum gclk_generator generator_source; + /** Set to keep module active in sleep modes */ + bool run_in_standby; + /** PAD0 (SDA) pinmux */ + uint32_t pinmux_pad0; + /** PAD1 (SCL) pinmux */ + uint32_t pinmux_pad1; + /** Set to enable SCL low time-out */ + bool scl_low_timeout; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + /** Set to enable SCL stretch only after ACK bit (required for high speed) */ + bool scl_stretch_only_after_ack_bit; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + /** Set to enable slave SCL low extend time-out */ + bool slave_scl_low_extend_timeout; +#endif +}; + + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code i2c_slave_lock( + struct i2c_slave_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline void i2c_slave_unlock(struct i2c_slave_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Configuration and Initialization + * @{ + */ + +/** + * \brief Returns the synchronization status of the module + * + * Returns the synchronization status of the module. + * + * \param[out] module Pointer to software module structure + * + * \return Status of the synchronization. + * \retval true Module is busy synchronizing + * \retval false Module is not synchronizing + */ +static inline bool i2c_slave_is_syncing( + const struct i2c_slave_module *const module) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + + /* Return sync status */ +#if defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1) + return (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_SYNCBUSY); +#elif defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2) + return (i2c_hw->SYNCBUSY.reg & SERCOM_I2CS_SYNCBUSY_MASK); +#else +# error Unknown SERCOM SYNCBUSY scheme! +#endif +} + +#if !defined(__DOXYGEN__) +/** + * \internal Wait for hardware module to sync + * + * \param[in] module Pointer to software module structure + */ +static void _i2c_slave_wait_for_sync( + const struct i2c_slave_module *const module) +{ + /* Sanity check */ + Assert(module); + + while (i2c_slave_is_syncing(module)) { + /* Wait for I<SUP>2</SUP>C module to sync */ + } +} +#endif + +///@cond INTERNAL +/** + * \internal Workaround for errata 13574 + * Instead set ACK/NACK of CTRLB + * + * This errata exist in part revisions of SAM D20/D21/D09 + * D10/D11/L21/L22/DA1/C20/C21/R30, but workaround can be works in all + * revision of those device. As this function operation + * should be use less cpu time as possible, so caller + * function can ignore to check revision number, and use + * this workaround in all revision of those device. + * + * \param[in,out] module Pointer to software module structure + * \param[in] send_ack true send ACK, false send NACK + */ +static inline void _i2c_slave_set_ctrlb_ackact( + struct i2c_slave_module *const module, + bool send_ack) +{ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if (SAMD20 || SAMD21 || SAMD09 || SAMD10 || SAMD11 || SAML21 || SAMDA1 || \ + SAML22 || SAMC20 || SAMC21 || SAMR30) + /* Workaround, Following two write are atomic */ + system_interrupt_enter_critical_section(); + i2c_hw->STATUS.reg = 0; + + if (send_ack == true) { + i2c_hw->CTRLB.reg = 0; + } + else { + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_ACKACT; + } + system_interrupt_leave_critical_section(); +#else + /* Normal operation */ + if (send_ack == true) { + i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; + } + else { + i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; + } +#endif + return; +} + +/** + * \internal Workaround for SAM0 errata 13574, + * instead Set CMD3 of CTRLB + * + * This errata exist in part revisions of SAM D20/D21/D09 + * D10/D11/L21/DA1/C20/C21/R30, but workaround can be works in all + * revision of those device. As this function operation + * should be use less cpu time as possible, so caller + * function can ignore to check revision number, and use + * this workaround in all revision of those device. + * + * \param[in,out] module Pointer to software module structure + */ +static inline void _i2c_slave_set_ctrlb_cmd3( + struct i2c_slave_module *const module) +{ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if (SAMD20 || SAMD21 || SAMD09 || SAMD10 || SAMD11 || SAML21 || SAML22 || \ + SAMDA1 || SAMC20 || SAMC21 || SAMR30) + /* Workaround */ + /* + * Below code instead i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_CMD(0x3); + * CMD=0x3 clears all interrupts, so to keep the result similar + * PREC is cleared if it was set + */ + if (i2c_hw->INTFLAG.bit.PREC) { + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; + } + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; +#else + /* Normal operation */ + i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_CMD(0x3); +#endif + return; +} +///@endcond + +/** + * \brief Gets the I<SUP>2</SUP>C slave default configurations + * + * This will initialize the configuration structure to known default values. + * + * The default configuration is as follows: + * - Disable SCL low timeout + * - 300ns - 600ns SDA hold time + * - Buffer timeout = 65535 + * - Address with mask + * - Address = 0 + * - Address mask = 0 (one single address) + * - General call address disabled + * - Address nack disabled if the interrupt driver is used + * - GCLK generator 0 + * - Do not run in standby + * - PINMUX_DEFAULT for SERCOM pads + * + * Those default configuration only available if the device supports it: + * - Not using 10-bit addressing + * - Standard-mode and Fast-mode transfer speed + * - SCL stretch disabled + * - Slave SCL low extend time-out disabled + * + * \param[out] config Pointer to configuration structure to be initialized + */ +static inline void i2c_slave_get_config_defaults( + struct i2c_slave_config *const config) +{ + /*Sanity check argument */ + Assert(config); + config->enable_scl_low_timeout = false; + config->sda_hold_time = I2C_SLAVE_SDA_HOLD_TIME_300NS_600NS; + config->buffer_timeout = 65535; + config->address_mode = I2C_SLAVE_ADDRESS_MODE_MASK; + config->address = 0; + config->address_mask = 0; +#ifdef FEATURE_I2C_10_BIT_ADDRESS + config->ten_bit_address = false; +#endif + config->enable_general_call_address = false; +#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED + config->transfer_speed = I2C_SLAVE_SPEED_STANDARD_AND_FAST; +#endif +#if I2C_SLAVE_CALLBACK_MODE == true + config->enable_nack_on_address = false; +#endif + config->generator_source = GCLK_GENERATOR_0; + config->run_in_standby = false; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->scl_low_timeout = false; +#ifdef FEATURE_I2C_SCL_STRETCH_MODE + config->scl_stretch_only_after_ack_bit = false; +#endif +#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT + config->slave_scl_low_extend_timeout = false; +#endif +} + +enum status_code i2c_slave_init(struct i2c_slave_module *const module, + Sercom *const hw, + const struct i2c_slave_config *const config); + +/** + * \brief Enables the I<SUP>2</SUP>C module + * + * This will enable the requested I<SUP>2</SUP>C module. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_slave_enable( + const struct i2c_slave_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Enable global interrupt for module */ + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait for module to sync */ + _i2c_slave_wait_for_sync(module); + + /* Enable module */ + i2c_hw->CTRLA.reg |= SERCOM_I2CS_CTRLA_ENABLE; +} + + +/** + * \brief Disables the I<SUP>2</SUP>C module + * + * This will disable the I<SUP>2</SUP>C module specified in the provided software module + * structure. + * + * \param[in] module Pointer to the software module struct + */ +static inline void i2c_slave_disable( + const struct i2c_slave_module *const module) +{ + /* Sanity check of arguments */ + Assert(module); + Assert(module->hw); + + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + +#if I2C_SLAVE_CALLBACK_MODE == true + /* Disable interrupts */ + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTENSET_PREC | + SERCOM_I2CS_INTENSET_AMATCH | SERCOM_I2CS_INTENSET_DRDY; + + /* Clear interrupt flags */ + i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_AMATCH | + SERCOM_I2CS_INTFLAG_DRDY; + + /* Disable global interrupt for module */ + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait for module to sync */ + _i2c_slave_wait_for_sync(module); + + /* Disable module */ + i2c_hw->CTRLA.reg &= ~SERCOM_I2CS_CTRLA_ENABLE; +} + +void i2c_slave_reset( + struct i2c_slave_module *const module); + +/** @} */ + +/** + * \name Read and Write + * @{ + */ + +enum status_code i2c_slave_write_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); +enum status_code i2c_slave_read_packet_wait( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); +enum i2c_slave_direction i2c_slave_get_direction_wait( + struct i2c_slave_module *const module); + +/** @} */ + +/** + * \name Status Management + * @{ + */ +uint32_t i2c_slave_get_status( + struct i2c_slave_module *const module); +void i2c_slave_clear_status( + struct i2c_slave_module *const module, + uint32_t status_flags); +/** @} */ + +#ifdef FEATURE_I2C_DMA_SUPPORT +/** + * \name SERCOM I2C slave with DMA Interfaces + * @{ + */ + +/** + * \brief Read SERCOM I<SUP>2</SUP>C interrupt status. + * + * Read I<SUP>2</SUP>C interrupt status for DMA transfer. + * + * \param[in,out] module Pointer to the driver instance to lock + * + */ +static inline uint8_t i2c_slave_dma_read_interrupt_status(struct i2c_slave_module *const module) +{ + return (uint8_t)module->hw->I2CS.INTFLAG.reg; +} + +/** + * \brief Write SERCOM I<SUP>2</SUP>C interrupt status. + * + * Write I<SUP>2</SUP>C interrupt status for DMA transfer. + * + * \param[in,out] module Pointer to the driver instance to lock + * \param[in] flag Interrupt flag status + * + */ +static inline void i2c_slave_dma_write_interrupt_status(struct i2c_slave_module *const module, + uint8_t flag) +{ + module->hw->I2CS.INTFLAG.reg = flag; +} + +/** @} */ +#endif + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_SLAVE_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave_interrupt.h new file mode 100644 index 000000000..cf1c4362c --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/i2c/i2c_slave_interrupt.h @@ -0,0 +1,215 @@ +/** + * \file + * + * \brief SAM SERCOM I2C Slave Interrupt Driver + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef I2C_SLAVE_INTERRUPT_H_INCLUDED +#define I2C_SLAVE_INTERRUPT_H_INCLUDED + +#include "i2c_slave.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_i2c_group + * @{ + * + */ + +/** + * \name Address Match Functionality + * @{ + */ + +void i2c_slave_enable_nack_on_address( + struct i2c_slave_module *const module); +void i2c_slave_disable_nack_on_address( + struct i2c_slave_module *const module); + +/** @} */ + +/** + * \name Callbacks + * @{ + */ +#if !defined(__DOXYGEN__) +void _i2c_slave_interrupt_handler(uint8_t instance); +#endif + +void i2c_slave_register_callback( + struct i2c_slave_module *const module, + i2c_slave_callback_t callback, + enum i2c_slave_callback callback_type); + +void i2c_slave_unregister_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type); + +/** + * \brief Enables callback + * + * Enables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to enable + */ +static inline void i2c_slave_enable_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as enabled */ + module->enabled_callback |= (1 << callback_type); + + /* Enable address callback */ + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + if (callback_type == I2C_SLAVE_CALLBACK_READ_REQUEST || + callback_type == I2C_SLAVE_CALLBACK_WRITE_REQUEST) { + i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } +} + +/** + * \brief Disables callback + * + * Disables the callback specified by the callback_type. + * + * \param[in,out] module Pointer to the software module struct + * \param[in] callback_type Callback type to disable + */ +static inline void i2c_slave_disable_callback( + struct i2c_slave_module *const module, + enum i2c_slave_callback callback_type) +{ + /* Sanity check */ + Assert(module); + Assert(module->hw); + + /* Mark callback as disabled */ + module->enabled_callback &= ~(1 << callback_type); + SercomI2cs *const i2c_hw = &(module->hw->I2CS); + if (callback_type == I2C_SLAVE_CALLBACK_READ_REQUEST || + callback_type == I2C_SLAVE_CALLBACK_WRITE_REQUEST || + module->status != STATUS_BUSY) { + i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH; + } +} + +/** @} */ + +/** + * \name Read and Write, Interrupt-Driven + * @{ + */ + + +enum status_code i2c_slave_read_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); + +enum status_code i2c_slave_write_packet_job( + struct i2c_slave_module *const module, + struct i2c_slave_packet *const packet); + +/** + * \brief Cancels any currently ongoing operation + * + * Terminates the running transfer operation. + * + * \param[in,out] module Pointer to software module structure + */ +static inline void i2c_slave_cancel_job( + struct i2c_slave_module *const module) +{ + /* Sanity check. */ + Assert(module); + Assert(module->hw); + + /* Set buffer to 0. */ + module->buffer_remaining = 0; + module->buffer_length = 0; +} + +/** + * \brief Gets status of ongoing job + * + * Will return the status of the ongoing job, or the error that occurred + * in the last transfer operation. + * The status will be cleared when starting a new job. + * + * \param[in,out] module Pointer to software module structure + * + * \return Status of job. + * \retval STATUS_OK No error has occurred + * \retval STATUS_BUSY Transfer is in progress + * \retval STATUS_ERR_IO A collision, timeout or bus error happened in + * the last transfer + * \retval STATUS_ERR_TIMEOUT A timeout occurred + * \retval STATUS_ERR_OVERFLOW Data from master overflows receive buffer + */ +static inline enum status_code i2c_slave_get_job_status( + struct i2c_slave_module *const module) +{ + /* Check sanity. */ + Assert(module); + Assert(module->hw); + + /* Return current status code. */ + return module->status; +} + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_SLAVE_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom.c b/atmel-samd/asf/sam0/drivers/sercom/sercom.c new file mode 100644 index 000000000..637f3947f --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom.c @@ -0,0 +1,296 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#include "sercom.h" + +#define SHIFT 32 +#define BAUD_INT_MAX 8192 +#define BAUD_FP_MAX 8 + +#if !defined(__DOXYGEN__) +/** + * \internal Configuration structure to save current gclk status. + */ +struct _sercom_conf { + /* Status of gclk generator initialization */ + bool generator_is_set; + /* Sercom gclk generator used */ + enum gclk_generator generator_source; +}; + +static struct _sercom_conf _sercom_config; + + +/** + * \internal Calculate 64 bit division, ref can be found in + * http://en.wikipedia.org/wiki/Division_algorithm#Long_division + */ +static uint64_t long_division(uint64_t n, uint64_t d) +{ + int32_t i; + uint64_t q = 0, r = 0, bit_shift; + for (i = 63; i >= 0; i--) { + bit_shift = (uint64_t)1 << i; + + r = r << 1; + + if (n & bit_shift) { + r |= 0x01; + } + + if (r >= d) { + r = r - d; + q |= bit_shift; + } + } + + return q; +} + +/** + * \internal Calculate synchronous baudrate value (SPI/UART) + */ +enum status_code _sercom_get_sync_baud_val( + const uint32_t baudrate, + const uint32_t external_clock, + uint16_t *const baudvalue) +{ + /* Baud value variable */ + uint16_t baud_calculated = 0; + uint32_t clock_value = external_clock; + + + /* Check if baudrate is outside of valid range */ + if (baudrate > (external_clock / 2)) { + /* Return with error code */ + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + + /* Calculate BAUD value from clock frequency and baudrate */ + clock_value = external_clock / 2; + while (clock_value >= baudrate) { + clock_value = clock_value - baudrate; + baud_calculated++; + } + baud_calculated = baud_calculated - 1; + + /* Check if BAUD value is more than 255, which is maximum + * for synchronous mode */ + if (baud_calculated > 0xFF) { + /* Return with an error code */ + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } else { + *baudvalue = baud_calculated; + return STATUS_OK; + } +} + +/** + * \internal Calculate asynchronous baudrate value (UART) +*/ +enum status_code _sercom_get_async_baud_val( + const uint32_t baudrate, + const uint32_t peripheral_clock, + uint16_t *const baudval, + enum sercom_asynchronous_operation_mode mode, + enum sercom_asynchronous_sample_num sample_num) +{ + /* Temporary variables */ + uint64_t ratio = 0; + uint64_t scale = 0; + uint64_t baud_calculated = 0; + uint8_t baud_fp; + uint32_t baud_int = 0; + uint64_t temp1, temp2; + + /* Check if the baudrate is outside of valid range */ + if ((baudrate * sample_num) > peripheral_clock) { + /* Return with error code */ + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + + if(mode == SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC) { + /* Calculate the BAUD value */ + temp1 = ((sample_num * (uint64_t)baudrate) << SHIFT); + ratio = long_division(temp1, peripheral_clock); + scale = ((uint64_t)1 << SHIFT) - ratio; + baud_calculated = (65536 * scale) >> SHIFT; + } else if(mode == SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL) { + for(baud_fp = 0; baud_fp < BAUD_FP_MAX; baud_fp++) { + temp1 = BAUD_FP_MAX * (uint64_t)peripheral_clock; + temp2 = ((uint64_t)baudrate * sample_num); + baud_int = long_division(temp1, temp2); + baud_int -= baud_fp; + baud_int = baud_int / BAUD_FP_MAX; + if(baud_int < BAUD_INT_MAX) { + break; + } + } + if(baud_fp == BAUD_FP_MAX) { + return STATUS_ERR_BAUDRATE_UNAVAILABLE; + } + baud_calculated = baud_int | (baud_fp << 13); + } + + *baudval = baud_calculated; + return STATUS_OK; +} +#endif + +/** + * \brief Set GCLK channel to generator. + * + * This will set the appropriate GCLK channel to the requested GCLK generator. + * This will set the generator for all SERCOM instances, and the user will thus + * only be able to set the same generator that has previously been set, if any. + * + * After the generator has been set the first time, the generator can be changed + * using the \c force_change flag. + * + * \param[in] generator_source The generator to use for SERCOM. + * \param[in] force_change Force change the generator. + * + * \return Status code indicating the GCLK generator change operation. + * \retval STATUS_OK If the generator update request was + * successful. + * \retval STATUS_ERR_ALREADY_INITIALIZED If a generator was already configured + * and the new configuration was not + * forced. + */ +enum status_code sercom_set_gclk_generator( + const enum gclk_generator generator_source, + const bool force_change) +{ + /* Check if valid option */ + if (!_sercom_config.generator_is_set || force_change) { + /* Create and fill a GCLK configuration structure for the new config */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = generator_source; + system_gclk_chan_set_config(SERCOM_GCLK_ID, &gclk_chan_conf); + system_gclk_chan_enable(SERCOM_GCLK_ID); + + /* Save config */ + _sercom_config.generator_source = generator_source; + _sercom_config.generator_is_set = true; + + return STATUS_OK; + } else if (generator_source == _sercom_config.generator_source) { + /* Return status OK if same config */ + return STATUS_OK; + } + + /* Return invalid config to already initialized GCLK */ + return STATUS_ERR_ALREADY_INITIALIZED; +} + +/** \internal + * Creates a switch statement case entry to convert a SERCOM instance and pad + * index to the default SERCOM pad MUX setting. + */ +#define _SERCOM_PAD_DEFAULTS_CASE(n, pad) \ + case (uintptr_t)SERCOM##n: \ + switch (pad) { \ + case 0: \ + return SERCOM##n##_PAD0_DEFAULT; \ + case 1: \ + return SERCOM##n##_PAD1_DEFAULT; \ + case 2: \ + return SERCOM##n##_PAD2_DEFAULT; \ + case 3: \ + return SERCOM##n##_PAD3_DEFAULT; \ + } \ + break; + +/** + * \internal Gets the default PAD pinout for a given SERCOM. + * + * Returns the pinmux settings for the given SERCOM and pad. This is used + * for default configuration of pins. + * + * \param[in] sercom_module Pointer to the SERCOM module + * \param[in] pad PAD to get default pinout for + * + * \returns The default pinmux for the given SERCOM instance and PAD + * + */ +uint32_t _sercom_get_default_pad( + Sercom *const sercom_module, + const uint8_t pad) +{ + switch ((uintptr_t)sercom_module) { + /* Auto-generate a lookup table for the default SERCOM pad defaults */ + MREPEAT(SERCOM_INST_NUM, _SERCOM_PAD_DEFAULTS_CASE, pad) + } + + Assert(false); + return 0; +} + +/** + * \internal + * Find index of given instance. + * + * \param[in] sercom_instance Instance pointer. + * + * \return Index of given instance. + */ +uint8_t _sercom_get_sercom_inst_index( + Sercom *const sercom_instance) +{ + /* Save all available SERCOM instances for compare */ + Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS; + + /* Find index for sercom instance */ + for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) { + if ((uintptr_t)sercom_instance == (uintptr_t)sercom_instances[i]) { + return i; + } + } + + /* Invalid data given */ + Assert(false); + return 0; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom.h b/atmel-samd/asf/sam0/drivers/sercom/sercom.h new file mode 100644 index 000000000..60ce6f8ad --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom.h @@ -0,0 +1,118 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef SERCOM_H_INCLUDED +#define SERCOM_H_INCLUDED + +#include <compiler.h> +#include <system.h> +#include <clock.h> +#include <system_interrupt.h> +#include "sercom_pinout.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* SERCOM modules should share same slow GCLK channel ID */ +#define SERCOM_GCLK_ID SERCOM0_GCLK_ID_SLOW + +#if (0x1ff >= REV_SERCOM) +# define FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1 +#elif (0x400 >= REV_SERCOM) +# define FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2 +#else +# error "Unknown SYNCBUSY scheme for this SERCOM revision" +#endif + +/** + * \brief sercom asynchronous operation mode + * + * Select sercom asynchronous operation mode + */ +enum sercom_asynchronous_operation_mode { + SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC = 0, + SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL, +}; + +/** + * \brief sercom asynchronous samples per bit + * + * Select number of samples per bit + */ +enum sercom_asynchronous_sample_num { + SERCOM_ASYNC_SAMPLE_NUM_3 = 3, + SERCOM_ASYNC_SAMPLE_NUM_8 = 8, + SERCOM_ASYNC_SAMPLE_NUM_16 = 16, +}; + +enum status_code sercom_set_gclk_generator( + const enum gclk_generator generator_source, + const bool force_change); + +enum status_code _sercom_get_sync_baud_val( + const uint32_t baudrate, + const uint32_t external_clock, + uint16_t *const baudval); + +enum status_code _sercom_get_async_baud_val( + const uint32_t baudrate, + const uint32_t peripheral_clock, + uint16_t *const baudval, + enum sercom_asynchronous_operation_mode mode, + enum sercom_asynchronous_sample_num sample_num); + +uint32_t _sercom_get_default_pad( + Sercom *const sercom_module, + const uint8_t pad); + +uint8_t _sercom_get_sercom_inst_index( + Sercom *const sercom_instance); +#ifdef __cplusplus +} +#endif + +#endif //__SERCOM_H_INCLUDED diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.c new file mode 100644 index 000000000..b26d05b6e --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.c @@ -0,0 +1,141 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#include "sercom_interrupt.h" + +void *_sercom_instances[SERCOM_INST_NUM]; + +/** Save status of initialized handlers */ +static bool _handler_table_initialized = false; + +/** Void pointers for saving device instance structures */ +static void (*_sercom_interrupt_handlers[SERCOM_INST_NUM])(const uint8_t instance); + +/** + * \internal + * Default interrupt handler. + * + * \param[in] instance SERCOM instance used. + */ +static void _sercom_default_handler( + const uint8_t instance) +{ + Assert(false); +} + +/** + * \internal + * Saves the given callback handler. + * + * \param[in] instance Instance index. + * \param[in] interrupt_handler Pointer to instance callback handler. + */ +void _sercom_set_handler( + const uint8_t instance, + const sercom_handler_t interrupt_handler) +{ + /* Initialize handlers with default handler and device instances with 0 */ + if (_handler_table_initialized == false) { + for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) { + _sercom_interrupt_handlers[i] = &_sercom_default_handler; + _sercom_instances[i] = NULL; + } + + _handler_table_initialized = true; + } + + /* Save interrupt handler */ + _sercom_interrupt_handlers[instance] = interrupt_handler; +} + + +/** \internal + * Converts a given SERCOM index to its interrupt vector index. + */ +#define _SERCOM_INTERRUPT_VECT_NUM(n, unused) \ + SYSTEM_INTERRUPT_MODULE_SERCOM##n, + +/** \internal + * Generates a SERCOM interrupt handler function for a given SERCOM index. + */ +#define _SERCOM_INTERRUPT_HANDLER(n, unused) \ + void SERCOM##n##_Handler(void) \ + { \ + _sercom_interrupt_handlers[n](n); \ + } + +/** + * \internal + * Returns the system interrupt vector. + * + * \param[in] sercom_instance Instance pointer + * + * \return Enum of system interrupt vector + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM0 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM1 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM2 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM3 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM4 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM5 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM6 + * \retval SYSTEM_INTERRUPT_MODULE_SERCOM7 + */ +enum system_interrupt_vector _sercom_get_interrupt_vector( + Sercom *const sercom_instance) +{ + const uint8_t sercom_int_vectors[SERCOM_INST_NUM] = + { + MREPEAT(SERCOM_INST_NUM, _SERCOM_INTERRUPT_VECT_NUM, ~) + }; + + /* Retrieve the index of the SERCOM being requested */ + uint8_t instance_index = _sercom_get_sercom_inst_index(sercom_instance); + + /* Get the vector number from the lookup table for the requested SERCOM */ + return (enum system_interrupt_vector)sercom_int_vectors[instance_index]; +} + +/** Auto-generate a set of interrupt handlers for each SERCOM in the device */ +MREPEAT(SERCOM_INST_NUM, _SERCOM_INTERRUPT_HANDLER, ~) diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.h new file mode 100644 index 000000000..18945f518 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom_interrupt.h @@ -0,0 +1,72 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#ifndef SERCOM_INTERRUPT_H_INCLUDED +#define SERCOM_INTERRUPT_H_INCLUDED + +#include "sercom.h" +#include <system_interrupt.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Look-up table for device instances */ +extern void *_sercom_instances[SERCOM_INST_NUM]; + +typedef void (*sercom_handler_t)(uint8_t instance); + +enum system_interrupt_vector _sercom_get_interrupt_vector( + Sercom *const sercom_instance); + +void _sercom_set_handler( + const uint8_t instance, + const sercom_handler_t interrupt_handler); + +#ifdef __cplusplus +} +#endif + +#endif /* SERCOM_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h b/atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h new file mode 100644 index 000000000..94ebc9cc0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/sercom_pinout.h @@ -0,0 +1,526 @@ +/** + * \file + * + * \brief SAM SERCOM Module Pinout Definitions + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#ifndef SERCOM_PINOUT_H_INCLUDED +#define SERCOM_PINOUT_H_INCLUDED + +#include <compiler.h> + +#if SAMR21E + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ +# if SAM_PART_IS_DEFINED(SAMR21E19A) + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 +# else + #define SERCOM3_PAD0_DEFAULT PINMUX_PA27F_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA28F_SERCOM3_PAD1 +#endif + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + + /* SERCOM4 */ +# if SAM_PART_IS_DEFINED(SAMR21E19A) + #define SERCOM4_PAD0_DEFAULT PINMUX_PB08D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB09D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 +# else + #define SERCOM4_PAD0_DEFAULT PINMUX_PC19F_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB31F_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB30F_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PC18F_SERCOM4_PAD3 +# endif + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB30D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB31D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + +#elif SAMR21G + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA12C_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA13C_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PC19F_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB31F_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB30F_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PC18F_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + +#elif (SAMD09) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA08D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA09D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA30C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA31C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA24C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA25C_SERCOM1_PAD3 + +#elif (SAMD10DS) || (SAMD10DM) || (SAMD10DU) || (SAMD11DS) || (SAMD11DM) || (SAMD11DU) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA22C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA23C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA16D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA25D_SERCOM2_PAD3 + +#elif (SAMD10C) || (SAMD11C) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA08D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA09D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA30C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA31C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA24C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA25C_SERCOM1_PAD3 + +#elif SAM_PART_IS_DEFINED(SAMD21E15L) || SAM_PART_IS_DEFINED(SAMD21E16L) + + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + +#elif (SAML22N) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA10C_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA11C_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA20D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA21D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PB02C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PB21C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PB00C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PB01C_SERCOM3_PAD3 + + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PA12C_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PA13C_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14C_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15C_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB30D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB31D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PB22D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PB23D_SERCOM5_PAD3 +#elif (SAML22J) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA10C_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA11C_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA20D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA21D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PB02C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PB13C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PB00C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PB01C_SERCOM3_PAD3 +#elif (SAML22G) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA08C_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA09C_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA10C_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA11C_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA22D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA23D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA20D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA21D_SERCOM2_PAD3 +#elif (SAMC20E) || (SAMC21E) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + +#elif (SAMC20G) || (SAMC21G) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA12C_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA13C_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + + #ifdef ID_SERCOM4 + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PB08D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB09D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB10D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PB11D_SERCOM4_PAD3 + #endif + + #ifdef ID_SERCOM5 + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB02D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB03D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PB22D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PB23D_SERCOM5_PAD3 + #endif + +#elif (SAMC20J) || (SAMC21J) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA12C_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA13C_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA14C_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA15C_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA22C_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA23C_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA24C_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA25C_SERCOM3_PAD3 + + #ifdef ID_SERCOM4 + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PB08D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PB09D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PB10D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PB11D_SERCOM4_PAD3 + #endif + + #ifdef ID_SERCOM5 + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PB02D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PB03D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PB00D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PB01D_SERCOM5_PAD3 + #endif + +#elif (SAMDA1) + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + #if (SAMDA1E) + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD1_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + #else + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PA12D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PA13D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + #endif + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + +#elif (SAML21E) || (SAMR30E) + + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + #if !SAM_PART_IS_DEFINED(SAML21E18A) && !SAM_PART_IS_DEFINED(SAMR30E18A) + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD1_DEFAULT 0 /* No available pin */ + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + #endif + +#else + /* SERCOM0 */ + #define SERCOM0_PAD0_DEFAULT PINMUX_PA04D_SERCOM0_PAD0 + #define SERCOM0_PAD1_DEFAULT PINMUX_PA05D_SERCOM0_PAD1 + #define SERCOM0_PAD2_DEFAULT PINMUX_PA06D_SERCOM0_PAD2 + #define SERCOM0_PAD3_DEFAULT PINMUX_PA07D_SERCOM0_PAD3 + + /* SERCOM1 */ +#if SAM_PART_IS_DEFINED(SAMD21G15L) || SAM_PART_IS_DEFINED(SAMD21G16L) + #define SERCOM1_PAD0_DEFAULT PINMUX_PA16C_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA17C_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA18C_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA19C_SERCOM1_PAD3 +#else + #define SERCOM1_PAD0_DEFAULT PINMUX_PA00D_SERCOM1_PAD0 + #define SERCOM1_PAD1_DEFAULT PINMUX_PA01D_SERCOM1_PAD1 + #define SERCOM1_PAD2_DEFAULT PINMUX_PA30D_SERCOM1_PAD2 + #define SERCOM1_PAD3_DEFAULT PINMUX_PA31D_SERCOM1_PAD3 +#endif + + /* SERCOM2 */ + #define SERCOM2_PAD0_DEFAULT PINMUX_PA08D_SERCOM2_PAD0 + #define SERCOM2_PAD1_DEFAULT PINMUX_PA09D_SERCOM2_PAD1 + #define SERCOM2_PAD2_DEFAULT PINMUX_PA10D_SERCOM2_PAD2 + #define SERCOM2_PAD3_DEFAULT PINMUX_PA11D_SERCOM2_PAD3 + + /* SERCOM3 */ + #define SERCOM3_PAD0_DEFAULT PINMUX_PA16D_SERCOM3_PAD0 + #define SERCOM3_PAD1_DEFAULT PINMUX_PA17D_SERCOM3_PAD1 + #define SERCOM3_PAD2_DEFAULT PINMUX_PA18D_SERCOM3_PAD2 + #define SERCOM3_PAD3_DEFAULT PINMUX_PA19D_SERCOM3_PAD3 + + #if !(SAMD20E || SAMD21E) + /* SERCOM4 */ + #define SERCOM4_PAD0_DEFAULT PINMUX_PA12D_SERCOM4_PAD0 + #define SERCOM4_PAD1_DEFAULT PINMUX_PA13D_SERCOM4_PAD1 + #define SERCOM4_PAD2_DEFAULT PINMUX_PA14D_SERCOM4_PAD2 + #define SERCOM4_PAD3_DEFAULT PINMUX_PA15D_SERCOM4_PAD3 + + /* SERCOM5 */ + #define SERCOM5_PAD0_DEFAULT PINMUX_PA22D_SERCOM5_PAD0 + #define SERCOM5_PAD1_DEFAULT PINMUX_PA23D_SERCOM5_PAD1 + #define SERCOM5_PAD2_DEFAULT PINMUX_PA24D_SERCOM5_PAD2 + #define SERCOM5_PAD3_DEFAULT PINMUX_PA25D_SERCOM5_PAD3 + #endif + +#endif + +#endif /* SERCOM_PINOUT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/module_config/conf_spi.h b/atmel-samd/asf/sam0/drivers/sercom/spi/module_config/conf_spi.h new file mode 100644 index 000000000..18b4f76ef --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/module_config/conf_spi.h @@ -0,0 +1,55 @@ +/** + * \file + * + * \brief SAM SPI configuration + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + + +#ifndef CONF_SPI_H_INCLUDED +# define CONF_SPI_H_INCLUDED + +# define CONF_SPI_MASTER_ENABLE true +# define CONF_SPI_SLAVE_ENABLE true + +#endif /* CONF_SPI_H_INCLUDED */ + diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi.c b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.c new file mode 100644 index 000000000..c0b4acb4a --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.c @@ -0,0 +1,1265 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#include "spi.h" + +/** + * \brief Resets the SPI module + * + * This function will reset the SPI module to its power on default values and + * disable it. + * + * \param[in,out] module Pointer to the software instance struct + */ +void spi_reset( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Disable the module */ + spi_disable(module); + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Software reset the module */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST; +} + +/** + * \brief Set the baudrate of the SPI module + * + * This function will set the baudrate of the SPI module. + * + * \param[in] module Pointer to the software instance struct + * \param[in] baudrate The baudrate wanted + * + * \return The status of the configuration. + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_OK If the configuration was written + */ +enum status_code spi_set_baudrate( + struct spi_module *const module, + uint32_t baudrate) +{ + /* Sanity check arguments */ + Assert(module); + Assert(baudrate); + Assert(module->hw); + + /* Value to write to BAUD register */ + uint16_t baud = 0; + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Disable the module */ + spi_disable(module); + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Find frequency of the internal SERCOMi_GCLK_ID_CORE */ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index); + + /* Get baud value, based on baudrate and the internal clock frequency */ + enum status_code error_code = _sercom_get_sync_baud_val( + baudrate, internal_clock, &baud); + + if (error_code != STATUS_OK) { + /* Baud rate calculation error, return status code */ + return STATUS_ERR_INVALID_ARG; + } + + spi_module->BAUD.reg = (uint8_t)baud; + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Enable the module */ + spi_enable(module); + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + return STATUS_OK; +} + +# if CONF_SPI_SLAVE_ENABLE == true +/** + * \internal Clears the Transmit Complete interrupt flag. + * + * \param[in] module Pointer to the software instance struct + */ +static void _spi_clear_tx_complete_flag( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Clear interrupt flag */ + spi_module->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; +} +# endif + +/** + * \internal Writes an SPI SERCOM configuration to the hardware module. + * + * This function will write out a given configuration to the hardware module. + * Can only be done when the module is disabled. + * + * \param[in] module Pointer to the software instance struct + * \param[in] config Pointer to the configuration struct + * + * \return The status of the configuration. + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_OK If the configuration was written + */ +static enum status_code _spi_set_config( + struct spi_module *const module, + const struct spi_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(config); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + Sercom *const hw = module->hw; + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; + if(config->mode == SPI_MODE_SLAVE) { + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; + } + + uint32_t pad_pinmuxes[] = { + config->pinmux_pad0, config->pinmux_pad1, + config->pinmux_pad2, config->pinmux_pad3 + }; + + /* Configure the SERCOM pins according to the user configuration */ + for (uint8_t pad = 0; pad < 4; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(hw, pad); + } + + if (current_pinmux != PINMUX_UNUSED) { + pin_conf.mux_position = current_pinmux & 0xFFFF; + system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); + } + } + + module->mode = config->mode; + module->character_size = config->character_size; + module->receiver_enabled = config->receiver_enable; +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + module->master_slave_select_enable = config->master_slave_select_enable; +# endif + +# if CONF_SPI_MASTER_ENABLE == true + /* Value to write to BAUD register */ + uint16_t baud = 0; +# endif + /* Value to write to CTRLA register */ + uint32_t ctrla = 0; + /* Value to write to CTRLB register */ + uint32_t ctrlb = 0; + +# if CONF_SPI_MASTER_ENABLE == true + /* Find baud value and write it */ + if (config->mode == SPI_MODE_MASTER) { + /* Find frequency of the internal SERCOMi_GCLK_ID_CORE */ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index); + + /* Get baud value, based on baudrate and the internal clock frequency */ + enum status_code error_code = _sercom_get_sync_baud_val( + config->mode_specific.master.baudrate, + internal_clock, &baud); + + if (error_code != STATUS_OK) { + /* Baud rate calculation error, return status code */ + return STATUS_ERR_INVALID_ARG; + } + + spi_module->BAUD.reg = (uint8_t)baud; + } +# endif +# if CONF_SPI_SLAVE_ENABLE == true + if (config->mode == SPI_MODE_SLAVE) { + /* Set frame format */ + ctrla = config->mode_specific.slave.frame_format; + + /* Set address mode */ + ctrlb = config->mode_specific.slave.address_mode; + + /* Set address and address mask*/ + spi_module->ADDR.reg |= + (config->mode_specific.slave.address << SERCOM_SPI_ADDR_ADDR_Pos) | + (config->mode_specific.slave.address_mask << SERCOM_SPI_ADDR_ADDRMASK_Pos); + + if (config->mode_specific.slave.preload_enable) { + /* Enable pre-loading of shift register */ + ctrlb |= SERCOM_SPI_CTRLB_PLOADEN; + } + } +# endif + /* Set data order */ + ctrla |= config->data_order; + + /* Set clock polarity and clock phase */ + ctrla |= config->transfer_mode; + + /* Set MUX setting */ + ctrla |= config->mux_setting; + + /* Set SPI character size */ + ctrlb |= config->character_size; + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + ctrla |= SERCOM_SPI_CTRLA_RUNSTDBY; + } + + if (config->receiver_enable) { + /* Enable receiver */ + ctrlb |= SERCOM_SPI_CTRLB_RXEN; + } +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + if (config->select_slave_low_detect_enable) { + /* Enable Slave Select Low Detect */ + ctrlb |= SERCOM_SPI_CTRLB_SSDE; + } +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + if (config->master_slave_select_enable) { + /* Enable Master Slave Select */ + ctrlb |= SERCOM_SPI_CTRLB_MSSEN; + } +# endif + /* Write CTRLA register */ + spi_module->CTRLA.reg |= ctrla; + + /* Write CTRLB register */ + spi_module->CTRLB.reg |= ctrlb; + + return STATUS_OK; +} + +#if SPI_CALLBACK_MODE == false +/** + * \internal Checks an SPI config against current set config + * + * This function will check that the config does not alter the + * configuration of the module. If the new config changes any + * setting, the initialization will be discarded. + * + * \param[in] module Pointer to the software instance struct + * \param[in] config Pointer to the configuration struct + * + * \return The status of the configuration. + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_DENIED If configuration was different from previous + * \retval STATUS_OK If the configuration was written + */ +static enum status_code _spi_check_config( + struct spi_module *const module, + const struct spi_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(config); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + Sercom *const hw = module->hw; + + uint32_t pad_pinmuxes[] = { + config->pinmux_pad0, config->pinmux_pad1, + config->pinmux_pad2, config->pinmux_pad3 + }; + + /* Compare the current SERCOM pins against the user configuration */ + for (uint8_t pad = 0; pad < 4; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(hw, pad); + } + + if (current_pinmux == PINMUX_UNUSED) { + continue; + } + + if ((current_pinmux & 0xFFFF) != + system_pinmux_pin_get_mux_position(current_pinmux >> 16)) { + module->hw = NULL; + return STATUS_ERR_DENIED; + } + } + +# if CONF_SPI_MASTER_ENABLE == true + /* Value to read BAUD register */ + uint16_t baud; + uint32_t external_clock = system_gclk_chan_get_hz(SERCOM_GCLK_ID); +# endif + /* Value to read CTRLA, CTRLB and ADDR register */ + uint32_t ctrla = 0; + uint32_t ctrlb = 0; +# if CONF_SPI_SLAVE_ENABLE == true + uint32_t addr = 0; +# endif + +# if CONF_SPI_MASTER_ENABLE == true + /* Find baud value and compare it */ + if (config->mode == SPI_MODE_MASTER) { + enum status_code error_code = _sercom_get_sync_baud_val( + config->mode_specific.master.baudrate, + external_clock, &baud); + + if (error_code != STATUS_OK) { + /* Baud rate calculation error, return status code */ + return STATUS_ERR_INVALID_ARG; + } + + if (spi_module->BAUD.reg != (uint8_t)baud) { + return STATUS_ERR_DENIED; + } + + ctrla |= SERCOM_SPI_CTRLA_MODE(0x3); + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (config->mode == SPI_MODE_SLAVE) { + + /* Set frame format */ + ctrla |= config->mode_specific.slave.frame_format; + + /* Set address mode */ + ctrlb |= config->mode_specific.slave.address_mode; + + /* Set address and address mask*/ + addr |= (config->mode_specific.slave.address << SERCOM_SPI_ADDR_ADDR_Pos) | + (config->mode_specific.slave.address_mask << SERCOM_SPI_ADDR_ADDRMASK_Pos); + if (spi_module->CTRLA.reg != addr) { + return STATUS_ERR_DENIED; + } + + if (config->mode_specific.slave.preload_enable) { + /* Enable pre-loading of shift register */ + ctrlb |= SERCOM_SPI_CTRLB_PLOADEN; + } + ctrla |= SERCOM_SPI_CTRLA_MODE(0x2); + } +# endif + /* Set data order */ + ctrla |= config->data_order; + + /* Set clock polarity and clock phase */ + ctrla |= config->transfer_mode; + + /* Set MUX setting */ + ctrla |= config->mux_setting; + + /* Set SPI character size */ + ctrlb |= config->character_size; + + if (config->run_in_standby) { + /* Enable in sleep mode */ + ctrla |= SERCOM_SPI_CTRLA_RUNSTDBY; + } + + if (config->receiver_enable) { + /* Enable receiver */ + ctrlb |= SERCOM_SPI_CTRLB_RXEN; + } + +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + if (config->select_slave_low_detect_enable) { + /* Enable Slave Select Low Detect */ + ctrlb |= SERCOM_SPI_CTRLB_SSDE; + } +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + if (config->master_slave_select_enable) { + /* Enable Master Slave Select */ + ctrlb |= SERCOM_SPI_CTRLB_MSSEN; + } +# endif + + ctrla |= SERCOM_SPI_CTRLA_ENABLE; + + /* Check that same config is set */ + if (spi_module->CTRLA.reg == ctrla && + spi_module->CTRLB.reg == ctrlb) { + module->mode = config->mode; + module->character_size = config->character_size; + return STATUS_OK; + } + + /* Not same config, wipe module pointer and return */ + module->hw = NULL; + + return STATUS_ERR_DENIED; +} +#endif + +/** + * \brief Initializes the SERCOM SPI module + * + * This function will initialize the SERCOM SPI module, based on the values + * of the config struct. + * + * \param[out] module Pointer to the software instance struct + * \param[in] hw Pointer to hardware instance + * \param[in] config Pointer to the config struct + * + * \return Status of the initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + */ +enum status_code spi_init( + struct spi_module *const module, + Sercom *const hw, + const struct spi_config *const config) +{ + + /* Sanity check arguments */ + Assert(module); + Assert(hw); + Assert(config); + + /* Initialize device instance */ + module->hw = hw; + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check if module is enabled. */ + if (spi_module->CTRLA.reg & SERCOM_SPI_CTRLA_ENABLE) { +# if SPI_CALLBACK_MODE == false + /* Check if config is valid */ + return _spi_check_config(module, config); +# else + return STATUS_ERR_DENIED; +# endif + } + + /* Check if reset is in progress. */ + if (spi_module->CTRLA.reg & SERCOM_SPI_CTRLA_SWRST){ + return STATUS_BUSY; + } + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { +# ifdef ID_SERCOM5 + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; +# else + return STATUS_ERR_INVALID_ARG; +# endif + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC21) + if (sercom_index == 5) { +# ifdef ID_SERCOM5 + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; +# else + return STATUS_ERR_INVALID_ARG; +# endif + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC20) || (SAML22) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { +# ifdef ID_SERCOM5 + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); +# else + return STATUS_ERR_INVALID_ARG; +# endif + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + +# if CONF_SPI_MASTER_ENABLE == true + if (config->mode == SPI_MODE_MASTER) { + /* Set the SERCOM in SPI master mode */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3); + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (config->mode == SPI_MODE_SLAVE) { + /* Set the SERCOM in SPI slave mode */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x2); + } +# endif + +#if SPI_CALLBACK_MODE == true + /* Temporary variables */ + uint8_t i; + uint8_t instance_index; + + /* Initialize parameters */ + for (i = 0; i < SPI_CALLBACK_N; i++) { + module->callback[i] = NULL; + } + module->tx_buffer_ptr = NULL; + module->rx_buffer_ptr = NULL; + module->remaining_tx_buffer_length = 0x0000; + module->remaining_rx_buffer_length = 0x0000; + module->registered_callback = 0x00; + module->enabled_callback = 0x00; + module->status = STATUS_OK; + module->dir = SPI_DIRECTION_IDLE; + module->locked = false; + /* + * Set interrupt handler and register SPI software module struct in + * look-up table + */ + instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _spi_interrupt_handler); + _sercom_instances[instance_index] = module; +#endif + + /* Write configuration to module and return status code */ + return _spi_set_config(module, config); +} + +/** + * \brief Reads buffer of \c length SPI characters + * + * This function will read a buffer of data from an SPI peripheral by sending + * dummy SPI character if in master mode, or by waiting for data in slave mode. + * + * \note If address matching is enabled for the slave, the first character + * received and placed in the buffer will be the address. + * + * \param[in] module Pointer to the software instance struct + * \param[out] rx_data Data buffer for received data + * \param[in] length Length of data to receive + * \param[in] dummy 8- or 9-bit dummy byte to shift out in master mode + * + * \return Status of the read operation. + * \retval STATUS_OK If the read was completed + * \retval STATUS_ABORTED If transaction was ended by master before + * the entire buffer was transferred + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_OVERFLOW If the data is overflown + */ +enum status_code spi_read_buffer_wait( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + + /* Sanity check arguments */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } +# if CONF_SPI_SLAVE_ENABLE == true + if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { + /* Clear TX complete flag */ + _spi_clear_tx_complete_flag(module); + } +# endif + uint16_t rx_pos = 0; + + while (length--) { +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER) { + /* Wait until the module is ready to write a character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Send dummy SPI character to read in master mode */ + spi_write(module, dummy); + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_read(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to read a character */ + while (!spi_is_ready_to_read(module)) { + } + + uint16_t received_data = 0; + enum status_code retval = spi_read(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + + return STATUS_OK; +} + +/** + * \brief Sends and reads a single SPI character + * + * This function will transfer a single SPI character via SPI and return the + * SPI character that is shifted into the shift register. + * + * In master mode the SPI character will be sent immediately and the received + * SPI character will be read as soon as the shifting of the data is + * complete. + * + * In slave mode this function will place the data to be sent into the transmit + * buffer. It will then block until an SPI master has shifted a complete + * SPI character, and the received data is available. + * + * \note The data to be sent might not be sent before the next transfer, as + * loading of the shift register is dependent on SCK. + * \note If address matching is enabled for the slave, the first character + * received and placed in the buffer will be the address. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data SPI character to transmit + * \param[out] rx_data Pointer to store the received SPI character + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_OVERFLOW If the incoming data is overflown + */ +enum status_code spi_transceive_wait( + struct spi_module *const module, + uint16_t tx_data, + uint16_t *rx_data) +{ + /* Sanity check arguments */ + Assert(module); + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + uint16_t j; +# endif + enum status_code retval = STATUS_OK; + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (j = 0; j <= SPI_TIMEOUT; j++) { + if (spi_is_ready_to_write(module)) { + break; + } else if (j == SPI_TIMEOUT) { + /* Not ready to write data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } + } +# endif + /* Wait until the module is ready to write the character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Write data */ + spi_write(module, tx_data); + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (j = 0; j <= SPI_TIMEOUT; j++) { + if (spi_is_ready_to_read(module)) { + break; + } else if (j == SPI_TIMEOUT) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } + } +# endif + + /* Wait until the module is ready to read the character */ + while (!spi_is_ready_to_read(module)) { + } + + /* Read data */ + retval = spi_read(module, rx_data); + + return retval; +} + + /** + * \brief Selects slave device + * + * This function will drive the slave select pin of the selected device low or + * high depending on the select Boolean. + * If slave address recognition is enabled, the address will be sent to the + * slave when selecting it. + * + * \param[in] module Pointer to the software module struct + * \param[in] slave Pointer to the attached slave + * \param[in] select Boolean stating if the slave should be selected or + * deselected + * + * \return Status of the operation. + * \retval STATUS_OK If the slave device was selected + * \retval STATUS_ERR_UNSUPPORTED_DEV If the SPI module is operating in slave + * mode + * \retval STATUS_BUSY If the SPI module is not ready to write + * the slave address + */ +enum status_code spi_select_slave( + struct spi_module *const module, + struct spi_slave_inst *const slave, + const bool select) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(slave); + + /* Check that the SPI module is operating in master mode */ + if (module->mode != SPI_MODE_MASTER) { + return STATUS_ERR_UNSUPPORTED_DEV; + } +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + if(!(module->master_slave_select_enable)) +# endif + { + if (select) { + /* Check if address recognition is enabled */ + if (slave->address_enabled) { + /* Check if the module is ready to write the address */ + if (!spi_is_ready_to_write(module)) { + /* Not ready, do not select slave and return */ + port_pin_set_output_level(slave->ss_pin, true); + return STATUS_BUSY; + } + + /* Drive Slave Select low */ + port_pin_set_output_level(slave->ss_pin, false); + + /* Write address to slave */ + spi_write(module, slave->address); + + if (!(module->receiver_enabled)) { + /* Flush contents of shift register shifted back from slave */ + while (!spi_is_ready_to_read(module)) { + } + uint16_t flush = 0; + spi_read(module, &flush); + } + } else { + /* Drive Slave Select low */ + port_pin_set_output_level(slave->ss_pin, false); + } + } else { + /* Drive Slave Select high */ + port_pin_set_output_level(slave->ss_pin, true); + } + } + return STATUS_OK; +} + +/** + * \brief Sends a buffer of \c length SPI characters + * + * This function will send a buffer of SPI characters via the SPI + * and discard any data that is received. To both send and receive a buffer of + * data, use the \ref spi_transceive_buffer_wait function. + * + * Note that this function does not handle the _SS (slave select) pin(s) in + * master mode; this must be handled by the user application. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Pointer to the buffer to transmit + * \param[in] length Number of SPI characters to transfer + * + * \return Status of the write operation. + * \retval STATUS_OK If the write was completed + * \retval STATUS_ABORTED If transaction was ended by master before + * entire buffer was transferred + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + */ +enum status_code spi_write_buffer_wait( + struct spi_module *const module, + const uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + +# if CONF_SPI_SLAVE_ENABLE == true + if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { + /* Clear TX complete flag */ + _spi_clear_tx_complete_flag(module); + } +# endif + + uint16_t tx_pos = 0; + uint16_t flush_length = length; + + /* Write block */ + while (length--) { +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_write(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_write(module)) { + /* Not ready to write data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to write a character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = tx_data[tx_pos++]; + + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + + if (module->receiver_enabled) { +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (length && spi_is_ready_to_write(module)) { + data_to_send = tx_data[tx_pos++]; + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + length--; + } + if (spi_is_ready_to_read(module)) { + break; + } + } + + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + while (!spi_is_ready_to_read(module)) { + } + + /* Flush read buffer */ + uint16_t flush; + spi_read(module, &flush); + flush_length--; + } + } + +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER) { + /* Wait for last byte to be transferred */ + while (!spi_is_write_complete(module)) { + } + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + if (module->receiver_enabled) { + while (flush_length) { + /* Start timeout period for slave */ + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_read(module)) { + break; + } + } + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + /* Flush read buffer */ + uint16_t flush; + spi_read(module, &flush); + flush_length--; + } + } + } +# endif + return STATUS_OK; +} + +/** + * \brief Sends and receives a buffer of \c length SPI characters + * + * This function will send and receive a buffer of data via the SPI. + * + * In master mode the SPI characters will be sent immediately and the + * received SPI character will be read as soon as the shifting of the SPI + * character is complete. + * + * In slave mode this function will place the data to be sent into the transmit + * buffer. It will then block until an SPI master has shifted the complete + * buffer and the received data is available. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Pointer to the buffer to transmit + * \param[out] rx_data Pointer to the buffer where received data will be stored + * \param[in] length Number of SPI characters to transfer + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided + * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the + * timeout in slave mode + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_OVERFLOW If the data is overflown + */ +enum status_code spi_transceive_buffer_wait( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + +# if SPI_CALLBACK_MODE == true + if (module->status == STATUS_BUSY) { + /* Check if the SPI module is busy with a job */ + return STATUS_BUSY; + } +# endif + + /* Sanity check arguments */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + +# if CONF_SPI_SLAVE_ENABLE == true + if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { + /* Clear TX complete flag */ + _spi_clear_tx_complete_flag(module); + } +# endif + + uint16_t tx_pos = 0; + uint16_t rx_pos = 0; + uint16_t rx_length = length; + + /* Send and receive buffer */ + while (length--) { +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_write(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_write(module)) { + /* Not ready to write data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to write a character */ + while (!spi_is_ready_to_write(module)) { + } + + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = tx_data[tx_pos++]; + + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + +# if CONF_SPI_SLAVE_ENABLE == true + /* Start timeout period for slave */ + if (module->mode == SPI_MODE_SLAVE) { + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_write(module)) { + data_to_send = tx_data[tx_pos++]; + /* If 9-bit data, get next byte to send from the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Write the data to send */ + spi_write(module, data_to_send); + length--; + } + if (spi_is_ready_to_read(module)) { + break; + } + } + /* Check if master has ended the transaction */ + if (spi_is_write_complete(module)) { + _spi_clear_tx_complete_flag(module); + return STATUS_ABORTED; + } + + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + } +# endif + + /* Wait until the module is ready to read a character */ + while (!spi_is_ready_to_read(module)) { + } + + enum status_code retval; + uint16_t received_data = 0; + rx_length--; + + retval = spi_read(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER) { + /* Wait for last byte to be transferred */ + while (!spi_is_write_complete(module)) { + } + } +# endif + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + while (rx_length) { + /* Start timeout period for slave */ + for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { + if (spi_is_ready_to_read(module)) { + break; + } + } + if (!spi_is_ready_to_read(module)) { + /* Not ready to read data within timeout period */ + return STATUS_ERR_TIMEOUT; + } + enum status_code retval; + uint16_t received_data = 0; + rx_length--; + + retval = spi_read(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + } +# endif + return STATUS_OK; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi.h b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.h new file mode 100644 index 000000000..01d21a21e --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi.h @@ -0,0 +1,1807 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef SPI_H_INCLUDED +#define SPI_H_INCLUDED + +/** + * \defgroup asfdoc_sam0_sercom_spi_group SAM Serial Peripheral Interface (SERCOM SPI) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides + * an interface for the configuration and management of the SERCOM module in + * its SPI mode to transfer SPI data frames. The following driver API modes + * are covered by this manual: + * + * - Polled APIs + * \if SPI_CALLBACK_MODE + * - Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - SERCOM (Serial Communication Interface) + * + * The following devices can use this module: + * - Atmel | SMART SAM D20/D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D09/D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * - Atmel | SMART SAM R30 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_sercom_spi_prerequisites + * - \ref asfdoc_sam0_sercom_spi_module_overview + * - \ref asfdoc_sam0_sercom_spi_special_considerations + * - \ref asfdoc_sam0_sercom_spi_extra_info + * - \ref asfdoc_sam0_sercom_spi_examples + * - \ref asfdoc_sam0_sercom_spi_api_overview + * + * \section asfdoc_sam0_sercom_spi_prerequisites Prerequisites + * There are no prerequisites. + * + * + * \section asfdoc_sam0_sercom_spi_module_overview Module Overview + * The Serial Peripheral Interface (SPI) is a high-speed synchronous data + * transfer interface using three or four pins. It allows fast communication + * between a master device and one or more peripheral devices. + * + * A device connected to the bus must act as a master or a slave. The master + * initiates and controls all data transactions. + * The SPI master initiates a communication cycle by pulling low the Slave + * Select (SS) pin of the desired slave. The Slave Select pin is active low. + * Master and slave prepare data to be sent in their respective shift + * registers, and the master generates the required clock pulses on the SCK + * line to interchange data. Data is always shifted from master to slave on + * the Master Out - Slave In (MOSI) line, and from slave to master on the + * Master In - Slave Out (MISO) line. After each data transfer, the master can + * synchronize to the slave by pulling the SS line high. + * + * \subsection asfdoc_sam0_sercom_spi_module_features Driver Feature Macro Definition + * <table> + * <tr> + * <th>Driver feature macro</th> + * <th>Supported devices</th> + * </tr> + * <tr> + * <td>FEATURE_SPI_SLAVE_SELECT_LOW_DETECT</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30</td> + * </tr> + * <tr> + * <td>FEATURE_SPI_HARDWARE_SLAVE_SELECT</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30</td> + * </tr> + * <tr> + * <td>FEATURE_SPI_ERROR_INTERRUPT</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30</td> + * </tr> + * <tr> + * <td>FEATURE_SPI_SYNC_SCHEME_VERSION_2</td> + * <td>SAM D21/R21/D10/D11/L21/L22/DA1/C20/C21/R30</td> + * </tr> + * </table> + * \note The specific features are only available in the driver when the + * selected device supports those features. + * + * \subsection asfdoc_sam0_sercom_spi_bus SPI Bus Connection + * In \ref asfdoc_sam0_spi_connection_example "the figure below", the + * connection between one master and one slave is shown. + * + * \anchor asfdoc_sam0_spi_connection_example + * \dot + * digraph spi_slaves_par { + * subgraph cluster_spi_master { + * shift_reg [label="Shift register", shape=box]; + * mosi_m [label="MOSI", shape=none]; + * miso_m [label="MISO", shape=none]; + * sck_m [label="SCK", shape=none]; + * ss_m [label="GPIO pin", shape=none]; + * {rank=same; mosi_m miso_m sck_m ss_m} + * label="SPI Master"; + * } + * subgraph cluster_spi_slave { + * mosi_s [label="MOSI", shape=none]; + * miso_s [label="MISO", shape=none]; + * sck_s [label="SCK", shape=none]; + * ss_s [label="SS", shape=none]; + * shift_reg_s [label="Shift register", shape=box]; + * {rank=same; mosi_s miso_s sck_s ss_s} + * label="SPI Slave"; + * rankdir=LR; + * } + * shift_reg:e -> mosi_m:w [label=""]; + * mosi_m:e -> mosi_s:w [label=""]; + * mosi_s:e -> shift_reg_s:w [label=""]; + * miso_s:w -> miso_m:e [label=""]; + * sck_m -> sck_s; + * ss_m -> ss_s; + * shift_reg_s:se -> miso_s:e [label=""]; + * miso_m:w -> shift_reg:sw [label=""]; + * rankdir=LR; + * } + * \enddot + * + * The different lines are as follows: + * - \b MISO Master Input Slave Output. The line where the data is shifted + * out from the slave and into the master. + * - \b MOSI Master Output Slave Input. The line where the data is shifted + * out from the master and into the slave. + * - \b SCK Serial Clock. Generated by the master device. + * - \b SS Slave Select. To initiate a transaction, the master must pull this + * line low. + * + * If the bus consists of several SPI slaves, they can be connected in parallel + * and the SPI master can use general I/O pins to control separate SS lines to + * each slave on the bus. + * + * It is also possible to connect all slaves in series. In this configuration, + * a common SS is provided to \c N slaves, enabling them simultaneously. The + * MISO from the \c N-1 slaves is connected to the MOSI on the next slave. The + * \c N<SUP>th</SUP> slave connects its MISO back to the master. For a + * complete transaction, the master must shift \c N+1 characters. + * + * \subsection asfdoc_sam0_sercom_spi_chsize SPI Character Size + * The SPI character size is configurable to eight or nine bits. + * + * \subsection asfdoc_sam0_sercom_spi_master_mode Master Mode + * When configured as a master, the SS pin will be configured as an output. + * + * \subsubsection asfdoc_sam0_sercom_spi_master_mode_data_transfer Data Transfer + * Writing a character will start the SPI clock generator, and + * the character is transferred to the shift register when the shift + * register is empty. + * Once this is done, a new character can be written. + * As each character is shifted out from the master, a character is shifted in + * from the slave. If the receiver is enabled, the data is moved to the receive + * buffer at the completion of the frame and can be read. + * + * \subsection asfdoc_sam0_sercom_spi_slave_mode Slave Mode + * When configured as a slave, the SPI interface will remain inactive with MISO + * tri-stated as long as the SS pin is driven high. + * + * \subsubsection asfdoc_sam0_sercom_spi_slave_mode_data_transfer_slave Data Transfer + * The data register can be updated at any time. + * As the SPI slave shift register is clocked by SCK, a minimum of three SCK + * cycles are needed from the time new data is written, until the character is + * ready to be shifted out. If the shift register has not been loaded with + * data, the current contents will be transmitted. + * + * If constant transmission of data is needed in SPI slave mode, the system + * clock should be faster than SCK. + * If the receiver is enabled, the received character can be read from the + * receive buffer. When SS line is driven high, the slave will not receive any + * additional data. + * + * \subsubsection asfdoc_sam0_sercom_spi_slave_mode_addr_recognition Address Recognition + * When the SPI slave is configured with address recognition, the first + * character in a transaction is checked for an address match. If there is a + * match, the MISO output is enabled and the transaction is processed. + * If the address does not match, the complete transaction is ignored. + * + * If the device is asleep, it can be woken up by an address match in order + * to process the transaction. + * + * \note In master mode, an address packet is written by the + * \ref spi_select_slave function if the address_enabled configuration is + * set in the \ref spi_slave_inst_config struct. + * + * \subsection asfdoc_sam0_sercom_spi_data_modes Data Modes + * There are four combinations of SCK phase and polarity with respect to + * serial data. \ref asfdoc_sam0_spi_mode_table "The table below" shows the + * clock polarity (CPOL) and clock phase (CPHA) in the different modes. + * <i>Leading edge</i> is the first clock edge in a clock cycle and + * <i>trailing edge</i> is the last clock edge in a clock cycle. + * + * \anchor asfdoc_sam0_spi_mode_table + * <table> + * <caption>SPI Data Modes</caption> + * <tr> + * <th>Mode</th> + * <th>CPOL</th> + * <th>CPHA</th> + * <th>Leading Edge</th> + * <th>Trailing Edge</th> + * </tr> + * <tr> + * <td> 0 </td> + * <td> 0 </td> + * <td> 0 </td> + * <td> Rising, Sample </td> + * <td> Falling, Setup </td> + * </tr> + * <tr> + * <td> 1 </td> + * <td> 0 </td> + * <td> 1 </td> + * <td> Rising, Setup </td> + * <td> Falling, Sample </td> + * </tr> + * <tr> + * <td> 2 </td> + * <td> 1 </td> + * <td> 0 </td> + * <td> Falling, Sample </td> + * <td> Rising, Setup </td> + * </tr> + * <tr> + * <td> 3 </td> + * <td> 1 </td> + * <td> 1 </td> + * <td> Falling, Setup </td> + * <td> Rising, Sample </td> + * </tr> + * </table> + * + * + * \subsection asfdoc_sam0_sercom_spi_pads SERCOM Pads + * The SERCOM pads are automatically configured as seen in + * \ref asfdoc_sam0_spi_sercom_pad_table "the table below". If the receiver + * is disabled, the data input (MISO for master, MOSI for slave) can be used + * for other purposes. + * + * In master mode, the SS pin(s) must be configured using the \ref spi_slave_inst + * struct. + * + * \anchor asfdoc_sam0_spi_sercom_pad_table + * <table> + * <caption>SERCOM SPI Pad Usages</caption> + * <tr> + * <th> Pin </th> + * <th> Master SPI </th> + * <th> Slave SPI </th> + * </tr> + * <tr> + * <td> MOSI </td> + * <td> Output </td> + * <td> Input </td> + * </tr> + * <tr> + * <td> MISO </td> + * <td> Input </td> + * <td> Output </td> + * </tr> + * <tr> + * <td> SCK </td> + * <td> Output </td> + * <td> Input </td> + * </tr> + * <tr> + * <td> SS </td> + * <td> User defined output enable </td> + * <td> Input </td> + * </tr> + * </table> + * + * \subsection asfdoc_sam0_sercom_spi_sleep_modes Operation in Sleep Modes + * The SPI module can operate in all sleep modes by setting the run_in_standby + * option in the \ref spi_config struct. The operation in slave and master mode + * is shown in the table below. + * <table> + * <tr> + * <th> run_in_standby </th> + * <th> Slave </th> + * <th> Master </th> + * </tr> + * <tr> + * <td> false </td> + * <td> Disabled, all reception is dropped </td> + * <td> GCLK is disabled when master is idle, wake on transmit complete </td> + * </tr> + * <tr> + * <td> true </td> + * <td> Wake on reception </td> + * <td> GCLK is enabled while in sleep modes, wake on all interrupts </td> + * </tr> + * </table> + * + * \subsection asfdoc_sam0_sercom_spi_clock_generation Clock Generation + * In SPI master mode, the clock (SCK) is generated internally using the + * SERCOM baudrate generator. In SPI slave mode, the clock is provided by + * an external master on the SCK pin. This clock is used to directly clock + * the SPI shift register. + * + * \section asfdoc_sam0_sercom_spi_special_considerations Special Considerations + * \subsection pin_mux pinmux Settings + * The pin MUX settings must be configured properly, as not all settings + * can be used in different modes of operation. + * + * \section asfdoc_sam0_sercom_spi_extra_info Extra Information + * For extra information, see \ref asfdoc_sam0_sercom_spi_extra. This includes: + * - \ref asfdoc_sam0_sercom_spi_extra_acronyms + * - \ref asfdoc_sam0_sercom_spi_extra_dependencies + * - \ref asfdoc_sam0_sercom_spi_extra_workarounds + * - \ref asfdoc_sam0_sercom_spi_extra_history + * + * \section asfdoc_sam0_sercom_spi_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_spi_exqsg. + * + * \section asfdoc_sam0_sercom_spi_api_overview API Overview + * @{ + */ + +#include <compiler.h> +#include <port.h> +#include <sercom.h> +#include <pinmux.h> +#include <string.h> +#include <conf_spi.h> + +# if SPI_CALLBACK_MODE == true +# include <sercom_interrupt.h> +# endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if (CONF_SPI_MASTER_ENABLE == false) && (CONF_SPI_SLAVE_ENABLE == false) +#error "Not possible compile SPI driver, invalid driver configuration. Make sure that either/both CONF_SPI_MASTER_ENABLE/CONF_SPI_SLAVE_ENABLE is set to true." +#endif + +/** + * \name Driver Feature Definition + * Define SERCOM SPI features set according to different device family. + * @{ + */ +# if (SAMD21) || (SAMR21) || (SAMD11) || (SAMD10) || (SAML21) || (SAMDA1) || \ + (SAML22) || (SAMC20) || (SAMC21) || (SAMD09) || (SAMR30) || defined(__DOXYGEN__) +/** SPI slave select low detection. */ +# define FEATURE_SPI_SLAVE_SELECT_LOW_DETECT +/** Slave select can be controlled by hardware. */ +# define FEATURE_SPI_HARDWARE_SLAVE_SELECT +/** SPI with error detect feature. */ +# define FEATURE_SPI_ERROR_INTERRUPT +/** SPI sync scheme version 2. */ +# define FEATURE_SPI_SYNC_SCHEME_VERSION_2 +# endif +/*@}*/ + +# ifndef PINMUX_DEFAULT +/** Default pinmux. */ +# define PINMUX_DEFAULT 0 +# endif + +# ifndef PINMUX_UNUSED +/** Unused pinmux. */ +# define PINMUX_UNUSED 0xFFFFFFFF +# endif + +# ifndef SPI_TIMEOUT +/** SPI timeout value. */ +# define SPI_TIMEOUT 10000 +# endif + +# if SPI_CALLBACK_MODE == true +/** + * \brief SPI Callback enum + * + * Callbacks for SPI callback driver. + * + * \note For slave mode, these callbacks will be called when a transaction + * is ended by the master pulling Slave Select high. + * + */ +enum spi_callback { + /** Callback for buffer transmitted */ + SPI_CALLBACK_BUFFER_TRANSMITTED, + /** Callback for buffer received */ + SPI_CALLBACK_BUFFER_RECEIVED, + /** Callback for buffers transceived */ + SPI_CALLBACK_BUFFER_TRANSCEIVED, + /** Callback for error */ + SPI_CALLBACK_ERROR, + /** + * Callback for transmission ended by master before the entire buffer was + * read or written from slave + */ + SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE, +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /** Callback for slave select low */ + SPI_CALLBACK_SLAVE_SELECT_LOW, +# endif +# ifdef FEATURE_SPI_ERROR_INTERRUPT + /** Callback for combined error happen */ + SPI_CALLBACK_COMBINED_ERROR, +# endif +# if !defined(__DOXYGEN__) + /** Number of available callbacks */ + SPI_CALLBACK_N, +# endif +}; +# endif + +#if SPI_CALLBACK_MODE == true +# if !defined(__DOXYGEN__) +/** + * \internal SPI transfer directions + */ +enum _spi_direction { + /** Transfer direction is read */ + SPI_DIRECTION_READ, + /** Transfer direction is write */ + SPI_DIRECTION_WRITE, + /** Transfer direction is read and write */ + SPI_DIRECTION_BOTH, + /** No transfer */ + SPI_DIRECTION_IDLE, +}; +# endif +#endif + +/** + * \brief SPI Interrupt Flags + * + * Interrupt flags for the SPI module. + * + */ +enum spi_interrupt_flag { + /** + * This flag is set when the contents of the data register has been moved + * to the shift register and the data register is ready for new data + */ + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY = SERCOM_SPI_INTFLAG_DRE, + /** + * This flag is set when the contents of the shift register has been + * shifted out + */ + SPI_INTERRUPT_FLAG_TX_COMPLETE = SERCOM_SPI_INTFLAG_TXC, + /** This flag is set when data has been shifted into the data register */ + SPI_INTERRUPT_FLAG_RX_COMPLETE = SERCOM_SPI_INTFLAG_RXC, +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /** This flag is set when slave select low */ + SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW = SERCOM_SPI_INTFLAG_SSL, +# endif +# ifdef FEATURE_SPI_ERROR_INTERRUPT + /** This flag is set when combined error happen */ + SPI_INTERRUPT_FLAG_COMBINED_ERROR = SERCOM_SPI_INTFLAG_ERROR, +# endif +}; + +/** + * \brief SPI transfer modes enum + * + * SPI transfer mode. + */ +enum spi_transfer_mode { + /** Mode 0. Leading edge: rising, sample. Trailing edge: falling, setup */ + SPI_TRANSFER_MODE_0 = 0, + /** Mode 1. Leading edge: rising, setup. Trailing edge: falling, sample */ + SPI_TRANSFER_MODE_1 = SERCOM_SPI_CTRLA_CPHA, + /** Mode 2. Leading edge: falling, sample. Trailing edge: rising, setup */ + SPI_TRANSFER_MODE_2 = SERCOM_SPI_CTRLA_CPOL, + /** Mode 3. Leading edge: falling, setup. Trailing edge: rising, sample */ + SPI_TRANSFER_MODE_3 = SERCOM_SPI_CTRLA_CPHA | SERCOM_SPI_CTRLA_CPOL, +}; + +/** + * \brief SPI frame format enum + * + * Frame format for slave mode. + */ +enum spi_frame_format { + /** SPI frame */ + SPI_FRAME_FORMAT_SPI_FRAME = SERCOM_SPI_CTRLA_FORM(0), + /** SPI frame with address */ + SPI_FRAME_FORMAT_SPI_FRAME_ADDR = SERCOM_SPI_CTRLA_FORM(2), +}; + +/** + * \brief SPI signal MUX settings + * + * Set the functionality of the SERCOM pins. As not all combinations can be used + * in different modes of operation, proper combinations must be chosen according + * to the rest of the configuration. + * + * \note In master operation: DI is MISO, DO is MOSI. + * In slave operation: DI is MOSI, DO is MISO. + * + * See \ref asfdoc_sam0_sercom_spi_mux_settings for a description of the + * various MUX setting options. + */ +enum spi_signal_mux_setting { + /** SPI MUX combination A. DOPO: 0x0, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_A = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination B. DOPO: 0x0, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_B = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination C. DOPO: 0x0, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_C = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination D. DOPO: 0x0, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_D = + (0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination E. DOPO: 0x1, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_E = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination F. DOPO: 0x1, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_F = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination G. DOPO: 0x1, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_G = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination H. DOPO: 0x1, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_H = + (0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination I. DOPO: 0x2, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_I = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination J. DOPO: 0x2, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_J = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination K. DOPO: 0x2, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_K = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination L. DOPO: 0x2, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_L = + (0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination M. DOPO: 0x3, DIPO: 0x0 */ + SPI_SIGNAL_MUX_SETTING_M = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination N. DOPO: 0x3, DIPO: 0x1 */ + SPI_SIGNAL_MUX_SETTING_N = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x1 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination O. DOPO: 0x3, DIPO: 0x2 */ + SPI_SIGNAL_MUX_SETTING_O = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x2 << SERCOM_SPI_CTRLA_DIPO_Pos), + /** SPI MUX combination P. DOPO: 0x3, DIPO: 0x3 */ + SPI_SIGNAL_MUX_SETTING_P = + (0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) | + (0x3 << SERCOM_SPI_CTRLA_DIPO_Pos), +}; + +/** + * \brief SPI address modes enum + * + * For slave mode when using the SPI frame with address format. + * + */ +enum spi_addr_mode { + /** + * \c address_mask in the \ref spi_config struct is used as a mask to the register + */ + SPI_ADDR_MODE_MASK = SERCOM_SPI_CTRLB_AMODE(0), + /** + * The slave responds to the two unique addresses in \c address and + * \c address_mask in the \ref spi_config struct + */ + SPI_ADDR_MODE_UNIQUE = SERCOM_SPI_CTRLB_AMODE(1), + /** + * The slave responds to the range of addresses between and including \c address + * and \c address_mask in the \ref spi_config struct + */ + SPI_ADDR_MODE_RANGE = SERCOM_SPI_CTRLB_AMODE(2), +}; + +/** + * \brief SPI modes enum + * + * SPI mode selection. + */ +enum spi_mode { + /** Master mode */ + SPI_MODE_MASTER = 1, + /** Slave mode */ + SPI_MODE_SLAVE = 0, +}; + +/** + * \brief SPI data order enum + * + * SPI data order. + * + */ +enum spi_data_order { + /** The LSB of the data is transmitted first */ + SPI_DATA_ORDER_LSB = SERCOM_SPI_CTRLA_DORD, + /** The MSB of the data is transmitted first */ + SPI_DATA_ORDER_MSB = 0, +}; + +/** + * \brief SPI character size enum + * + * SPI character size. + * + */ +enum spi_character_size { + /** 8-bit character */ + SPI_CHARACTER_SIZE_8BIT = SERCOM_SPI_CTRLB_CHSIZE(0), + /** 9-bit character */ + SPI_CHARACTER_SIZE_9BIT = SERCOM_SPI_CTRLB_CHSIZE(1), +}; + +# if SPI_CALLBACK_MODE == true +/** Prototype for the device instance */ +struct spi_module; + +/** Type of the callback functions */ +typedef void (*spi_callback_t)(struct spi_module *const module); + +# if !defined(__DOXYGEN__) +/** Prototype for the interrupt handler */ +extern void _spi_interrupt_handler(uint8_t instance); +# endif +# endif + +/** + * \brief SERCOM SPI driver software device instance structure. + * + * SERCOM SPI driver software instance structure, used to retain software state + * information of an associated hardware module instance. + * + * \note The fields of this structure should not be altered by the user + * application; they are reserved for module-internal use only. + */ +struct spi_module { +# if !defined(__DOXYGEN__) + /** SERCOM hardware module */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** SPI mode */ + enum spi_mode mode; + /** SPI character size */ + enum spi_character_size character_size; + /** Receiver enabled */ + bool receiver_enabled; +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + /** Enable Hardware Slave Select */ + bool master_slave_select_enable; +# endif +# if SPI_CALLBACK_MODE == true + /** Direction of transaction */ + volatile enum _spi_direction dir; + /** Array to store callback function pointers in */ + spi_callback_t callback[SPI_CALLBACK_N]; + /** Buffer pointer to where the next received character will be put */ + volatile uint8_t *rx_buffer_ptr; + /** Buffer pointer to where the next character will be transmitted from + **/ + volatile uint8_t *tx_buffer_ptr; + /** Remaining characters to receive */ + volatile uint16_t remaining_rx_buffer_length; + /** Remaining dummy characters to send when reading */ + volatile uint16_t remaining_dummy_buffer_length; + /** Remaining characters to transmit */ + volatile uint16_t remaining_tx_buffer_length; + /** Bit mask for callbacks registered */ + uint8_t registered_callback; + /** Bit mask for callbacks enabled */ + uint8_t enabled_callback; + /** Holds the status of the ongoing or last operation */ + volatile enum status_code status; +# endif +# endif +}; + +/** + * \brief SPI peripheral slave instance structure + * + * SPI peripheral slave software instance structure, used to configure the + * correct SPI transfer mode settings for an attached slave. See + * \ref spi_select_slave. + */ +struct spi_slave_inst { + /** Pin to use as slave select */ + uint8_t ss_pin; + /** Address recognition enabled in slave device */ + bool address_enabled; + /** Address of slave device */ + uint8_t address; +}; + +/** + * \brief SPI peripheral slave configuration structure + * + * SPI Peripheral slave configuration structure. + */ +struct spi_slave_inst_config { + /** Pin to use as slave select */ + uint8_t ss_pin; + /** Enable address */ + bool address_enabled; + /** Address of slave */ + uint8_t address; +}; + +/** + * \brief SPI Master configuration structure + * + * SPI Master configuration structure. + */ +struct spi_master_config { + /** Baud rate */ + uint32_t baudrate; +}; + +/** + * \brief SPI slave configuration structure + * + * SPI slave configuration structure. + */ +struct spi_slave_config { + /** Frame format */ + enum spi_frame_format frame_format; + /** Address mode */ + enum spi_addr_mode address_mode; + /** Address */ + uint8_t address; + /** Address mask */ + uint8_t address_mask; + /** Preload data to the shift register while SS is high */ + bool preload_enable; +}; + +/** + * \brief SPI configuration structure + * + * Configuration structure for an SPI instance. This structure should be + * initialized by the \ref spi_get_config_defaults function before being + * modified by the user application. + */ +struct spi_config { + /** SPI mode */ + enum spi_mode mode; + /** Data order */ + enum spi_data_order data_order; + /** Transfer mode */ + enum spi_transfer_mode transfer_mode; + /** MUX setting */ + enum spi_signal_mux_setting mux_setting; + /** SPI character size */ + enum spi_character_size character_size; + /** Enabled in sleep modes */ + bool run_in_standby; + /** Enable receiver */ + bool receiver_enable; +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /** Enable Slave Select Low Detect */ + bool select_slave_low_detect_enable; +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + /** Enable Master Slave Select */ + bool master_slave_select_enable; +# endif + /** Union for slave or master specific configuration */ + union { + /** Slave specific configuration */ + struct spi_slave_config slave; + /** Master specific configuration */ + struct spi_master_config master; + } mode_specific; + /** GCLK generator to use as clock source */ + enum gclk_generator generator_source; + /** PAD0 pinmux */ + uint32_t pinmux_pad0; + /** PAD1 pinmux */ + uint32_t pinmux_pad1; + /** PAD2 pinmux */ + uint32_t pinmux_pad2; + /** PAD3 pinmux */ + uint32_t pinmux_pad3; +}; + +/** + * \brief Determines if the SPI module is currently synchronizing to the bus. + * + * This function will check if the underlying hardware peripheral module is + * currently synchronizing across multiple clock domains to the hardware bus. + * This function can be used to delay further operations on the module until it + * is ready. + * + * \param[in] module SPI hardware module + * + * \return Synchronization status of the underlying hardware module. + * \retval true Module synchronization is ongoing + * \retval false Module synchronization is not ongoing + * + */ +static inline bool spi_is_syncing( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + +# ifdef FEATURE_SPI_SYNC_SCHEME_VERSION_2 + /* Return synchronization status */ + return (spi_module->SYNCBUSY.reg); +# else + /* Return synchronization status */ + return (spi_module->STATUS.reg & SERCOM_SPI_STATUS_SYNCBUSY); +# endif +} + +/** + * \name Driver Initialization and Configuration + * @{ + */ + +/** + * \brief Initializes an SPI configuration structure to default values + * + * This function will initialize a given SPI configuration structure to a set + * of known default values. This function should be called on any new + * instance of the configuration structures before being modified by the + * user application. + * + * The default configuration is as follows: + * \li Master mode enabled + * \li MSB of the data is transmitted first + * \li Transfer mode 0 + * \li MUX Setting D + * \li Character size eight bits + * \li Not enabled in sleep mode + * \li Receiver enabled + * \li Baudrate 100000 + * \li Default pinmux settings for all pads + * \li GCLK generator 0 + * + * \param[out] config Configuration structure to initialize to default values + */ +static inline void spi_get_config_defaults( + struct spi_config *const config) +{ + /* Sanity check arguments */ + Assert(config); + + /* Default configuration values */ + config->mode = SPI_MODE_MASTER; + config->data_order = SPI_DATA_ORDER_MSB; + config->transfer_mode = SPI_TRANSFER_MODE_0; + config->mux_setting = SPI_SIGNAL_MUX_SETTING_D; + config->character_size = SPI_CHARACTER_SIZE_8BIT; + config->run_in_standby = false; + config->receiver_enable = true; +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + config->select_slave_low_detect_enable= true; +# endif +# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT + config->master_slave_select_enable= false; +# endif + config->generator_source = GCLK_GENERATOR_0; + + /* Clear mode specific config */ + memset(&(config->mode_specific), 0, sizeof(config->mode_specific)); + + /* Master config defaults */ + config->mode_specific.master.baudrate = 100000; + + /* pinmux config defaults */ + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->pinmux_pad2 = PINMUX_DEFAULT; + config->pinmux_pad3 = PINMUX_DEFAULT; + +}; + +/** + * \brief Initializes an SPI peripheral slave device configuration structure to default values + * + * This function will initialize a given SPI slave device configuration + * structure to a set of known default values. This function should be called + * on any new instance of the configuration structures before being modified by + * the user application. + * + * The default configuration is as follows: + * \li Slave Select on GPIO pin 10 + * \li Addressing not enabled + * + * \param[out] config Configuration structure to initialize to default values + */ +static inline void spi_slave_inst_get_config_defaults( + struct spi_slave_inst_config *const config) +{ + Assert(config); + + config->ss_pin = 10; + config->address_enabled = false; + config->address = 0; +} + +/** + * \brief Attaches an SPI peripheral slave + * + * This function will initialize the software SPI peripheral slave, based on + * the values of the config struct. The slave can then be selected and + * optionally addressed by the \ref spi_select_slave function. + * + * \param[out] slave Pointer to the software slave instance struct + * \param[in] config Pointer to the config struct + * + */ +static inline void spi_attach_slave( + struct spi_slave_inst *const slave, + const struct spi_slave_inst_config *const config) +{ + Assert(slave); + Assert(config); + + slave->ss_pin = config->ss_pin; + slave->address_enabled = config->address_enabled; + slave->address = config->address; + + /* Get default config for pin */ + struct port_config pin_conf; + port_get_config_defaults(&pin_conf); + + /* Edit config to set the pin as output */ + pin_conf.direction = PORT_PIN_DIR_OUTPUT; + + /* Set config on Slave Select pin */ + port_pin_set_config(slave->ss_pin, &pin_conf); + port_pin_set_output_level(slave->ss_pin, true); +} + +enum status_code spi_init( + struct spi_module *const module, + Sercom *const hw, + const struct spi_config *const config); + +/** @} */ + +/** + * \name Enable/Disable + * @{ + */ + +/** + * \brief Enables the SERCOM SPI module + * + * This function will enable the SERCOM SPI module. + * + * \param[in,out] module Pointer to the software instance struct + */ +static inline void spi_enable( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + +# if SPI_CALLBACK_MODE == true + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +# endif + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Enable SPI */ + spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE; +} + +/** + * \brief Disables the SERCOM SPI module + * + * This function will disable the SERCOM SPI module. + * + * \param[in,out] module Pointer to the software instance struct + */ +static inline void spi_disable( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + +# if SPI_CALLBACK_MODE == true + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +# endif + + while (spi_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } + + /* Disbale interrupt */ + spi_module->INTENCLR.reg = SERCOM_SPI_INTENCLR_MASK; + /* Clear interrupt flag */ + spi_module->INTFLAG.reg = SERCOM_SPI_INTFLAG_MASK; + + /* Disable SPI */ + spi_module->CTRLA.reg &= ~SERCOM_SPI_CTRLA_ENABLE; +} + +void spi_reset( + struct spi_module *const module); + +/** @} */ + +enum status_code spi_set_baudrate( + struct spi_module *const module, + uint32_t baudrate); + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code spi_lock(struct spi_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline void spi_unlock(struct spi_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Ready to Write/Read + * @{ + */ + + /** + * \brief Checks if the SPI in master mode has shifted out last data, or if the master has ended the transfer in slave mode. + * + * This function will check if the SPI master module has shifted out last data, + * or if the slave select pin has been drawn high by the master for the SPI + * slave module. + * + * \param[in] module Pointer to the software instance struct + * + * \return Indication of whether any writes are ongoing. + * \retval true If the SPI master module has shifted out data, or slave select + * has been drawn high for SPI slave + * \retval false If the SPI master module has not shifted out data + */ +static inline bool spi_is_write_complete( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check interrupt flag */ + return (spi_module->INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC); +} + + /** + * \brief Checks if the SPI module is ready to write data + * + * This function will check if the SPI module is ready to write data. + * + * \param[in] module Pointer to the software instance struct + * + * \return Indication of whether the module is ready to read data or not. + * \retval true If the SPI module is ready to write data + * \retval false If the SPI module is not ready to write data + */ +static inline bool spi_is_ready_to_write( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check interrupt flag */ + return (spi_module->INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE); +} + +/** + * \brief Checks if the SPI module is ready to read data + * + * This function will check if the SPI module is ready to read data. + * + * \param[in] module Pointer to the software instance struct + * + * \return Indication of whether the module is ready to read data or not. + * \retval true If the SPI module is ready to read data + * \retval false If the SPI module is not ready to read data + */ +static inline bool spi_is_ready_to_read( + struct spi_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check interrupt flag */ + return (spi_module->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC); +} +/** @} */ + +/** + * \name Read/Write + * @{ + */ + + /** + * \brief Transfers a single SPI character + * + * This function will send a single SPI character via SPI and ignore any data + * shifted in by the connected device. To both send and receive data, use the + * \ref spi_transceive_wait function or use the \ref spi_read function after + * writing a character. The \ref spi_is_ready_to_write function + * should be called before calling this function. + * + * Note that this function does not handle the SS (Slave Select) + * pin(s) in master mode; this must be handled from the user application. + * + * \note In slave mode, the data will not be transferred before a master + * initiates a transaction. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Data to transmit + * + * \return Status of the procedure. + * \retval STATUS_OK If the data was written + * \retval STATUS_BUSY If the last write was not completed + */ +static inline enum status_code spi_write( + struct spi_module *module, + uint16_t tx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check if the data register has been copied to the shift register */ + if (!spi_is_ready_to_write(module)) { + /* Data register has not been copied to the shift register, return */ + return STATUS_BUSY; + } + + /* Write the character to the DATA register */ + spi_module->DATA.reg = tx_data & SERCOM_SPI_DATA_MASK; + + return STATUS_OK; +} + +enum status_code spi_write_buffer_wait( + struct spi_module *const module, + const uint8_t *tx_data, + uint16_t length); + +/** + * \brief Reads last received SPI character + * + * This function will return the last SPI character shifted into the receive + * register by the \ref spi_write function. + * + * \note The \ref spi_is_ready_to_read function should be called before calling + * this function. + * + * \note Receiver must be enabled in the configuration. + * + * \param[in] module Pointer to the software instance struct + * \param[out] rx_data Pointer to store the received data + * + * \returns Status of the read operation. + * \retval STATUS_OK If data was read + * \retval STATUS_ERR_IO If no data is available + * \retval STATUS_ERR_OVERFLOW If the data is overflown + */ +static inline enum status_code spi_read( + struct spi_module *const module, + uint16_t *rx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomSpi *const spi_module = &(module->hw->SPI); + + /* Check if data is ready to be read */ + if (!spi_is_ready_to_read(module)) { + /* No data has been received, return */ + return STATUS_ERR_IO; + } + + /* Return value */ + enum status_code retval = STATUS_OK; + + /* Check if data is overflown */ + if (spi_module->STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) { + retval = STATUS_ERR_OVERFLOW; + /* Clear overflow flag */ + spi_module->STATUS.reg = SERCOM_SPI_STATUS_BUFOVF; + } + + /* Read the character from the DATA register */ + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + *rx_data = (spi_module->DATA.reg & SERCOM_SPI_DATA_MASK); + } else { + *rx_data = (uint8_t)spi_module->DATA.reg; + } + + return retval; +} + +enum status_code spi_read_buffer_wait( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy); + +enum status_code spi_transceive_wait( + struct spi_module *const module, + uint16_t tx_data, + uint16_t *rx_data); + +enum status_code spi_transceive_buffer_wait( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length); + +enum status_code spi_select_slave( + struct spi_module *const module, + struct spi_slave_inst *const slave, + bool select); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** @} */ + + +/** + * \page asfdoc_sam0_sercom_spi_extra Extra Information for SERCOM SPI Driver + * + * \section asfdoc_sam0_sercom_spi_extra_acronyms Acronyms + * Below is a table listing the acronyms used in this module, along with their + * intended meanings. + * + * <table> + * <tr> + * <th>Acronym</th> + * <th>Description</th> + * </tr> + * <tr> + * <td>SERCOM</td> + * <td>Serial Communication Interface</td> + * </tr> + * <tr> + * <td>SPI</td> + * <td>Serial Peripheral Interface</td> + * </tr> + * <tr> + * <td>SCK</td> + * <td>Serial Clock</td> + * </tr> + * <tr> + * <td>MOSI</td> + * <td>Master Output Slave Input</td> + * </tr> + * <tr> + * <td>MISO</td> + * <td>Master Input Slave Output</td> + * </tr> + * <tr> + * <td>SS</td> + * <td>Slave Select</td> + * </tr> + * <tr> + * <td>DIO</td> + * <td>Data Input Output</td> + * </tr> + * <tr> + * <td>DO</td> + * <td>Data Output</td> + * </tr> + * <tr> + * <td>DI</td> + * <td>Data Input</td> + * </tr> + * <tr> + * <td>DMA</td> + * <td>Direct Memory Access</td> + * </tr> + * </table> + * + * \section asfdoc_sam0_sercom_spi_extra_dependencies Dependencies + * The SPI driver has the following dependencies: + * \li \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" + * + * + * \section asfdoc_sam0_sercom_spi_extra_workarounds Workarounds Implemented by Driver + * No workarounds in driver. + * + * \section asfdoc_sam0_sercom_spi_extra_history Module History + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in the table. + * + * <table> + * <tr> + * <th>Changelog</th> + * </tr> + * <tr> + * <td>Added new features as below: + * \li Slave select low detect + * \li Hardware slave select + * \li DMA support </td> + * </tr> + * <tr> + * <td>Edited slave part of write and transceive buffer functions to ensure + * that second character is sent at the right time</td> + * </tr> + * <tr> + * <td>Renamed the anonymous union in \c struct spi_config to + * \c mode_specific</td> + * </tr> + * <tr> + * <td>Initial Release</td> + * </tr> + * </table> + */ + +/** + * \page asfdoc_sam0_sercom_spi_exqsg Examples for SERCOM SPI Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_spi_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_sercom_spi_master_basic_use + * - \subpage asfdoc_sam0_sercom_spi_slave_basic_use + * \if SPI_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_spi_master_callback_use + * - \subpage asfdoc_sam0_sercom_spi_slave_callback_use + * \endif + * - \subpage asfdoc_sam0_sercom_spi_dma_use_case + */ + + /** + * \page asfdoc_sam0_sercom_spi_mux_settings MUX Settings + * + * The following lists the possible internal SERCOM module pad function + * assignments for the four SERCOM pads in both SPI Master and SPI Slave + * modes. They are combinations of DOPO and DIPO in CTRLA. + * Note that this is in addition to the physical GPIO pin MUX of the device, + * and can be used in conjunction to optimize the serial data pin-out. + * + * \section asfdoc_sam0_sercom_spi_mux_settings_master Master Mode Settings + * The following table describes the SERCOM pin functionalities for the various + * MUX settings, whilst in SPI Master mode. + * + * \note If MISO is unlisted, the SPI receiver must not be enabled for the + * given MUX setting. + * + * <table> + * <tr> + * <th>Combination</th> + * <th>DOPO / DIPO</th> + * <th>SERCOM PAD[0]</th> + * <th>SERCOM PAD[1]</th> + * <th>SERCOM PAD[2]</th> + * <th>SERCOM PAD[3]</th> + * </tr> + * <tr> + * <td>A</td> + * <td>0x0 / 0x0</td> + * <td>MOSI</td> + * <td>SCK</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td>B</td> + * <td>0x0 / 0x1</td> + * <td>MOSI</td> + * <td>SCK</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td>C</td> + * <td>0x0 / 0x2</td> + * <td>MOSI</td> + * <td>SCK</td> + * <td>MISO</td> + * <td>-</td> + * </tr> + * <tr> + * <td>D</td> + * <td>0x0 / 0x3</td> + * <td>MOSI</td> + * <td>SCK</td> + * <td>-</td> + * <td>MISO</td> + * </tr> + * <tr> + * <td>E</td> + * <td>0x1 / 0x0</td> + * <td>MISO</td> + * <td>-</td> + * <td>MOSI</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>F</td> + * <td>0x1 / 0x1</td> + * <td>-</td> + * <td>MISO</td> + * <td>MOSI</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>G</td> + * <td>0x1 / 0x2</td> + * <td>-</td> + * <td>-</td> + * <td>MOSI</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>H</td> + * <td>0x1 / 0x3</td> + * <td>-</td> + * <td>-</td> + * <td>MOSI</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>I</td> + * <td>0x2 / 0x0</td> + * <td>MISO</td> + * <td>SCK</td> + * <td>-</td> + * <td>MOSI</td> + * </tr> + * <tr> + * <td>J</td> + * <td>0x2 / 0x1</td> + * <td>-</td> + * <td>SCK</td> + * <td>-</td> + * <td>MOSI</td> + * </tr> + * <tr> + * <td>K</td> + * <td>0x2 / 0x2</td> + * <td>-</td> + * <td>SCK</td> + * <td>MISO</td> + * <td>MOSI</td> + * </tr> + * <tr> + * <td>L</td> + * <td>0x2 / 0x3</td> + * <td>-</td> + * <td>SCK</td> + * <td>-</td> + * <td>MOSI</td> + * </tr> + * <tr> + * <td>M</td> + * <td>0x3 / 0x0</td> + * <td>MOSI</td> + * <td>-</td> + * <td>-</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>N</td> + * <td>0x3 / 0x1</td> + * <td>MOSI</td> + * <td>MISO</td> + * <td>-</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>O</td> + * <td>0x3 / 0x2</td> + * <td>MOSI</td> + * <td>-</td> + * <td>MISO</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>P</td> + * <td>0x3 / 0x3</td> + * <td>MOSI</td> + * <td>-</td> + * <td>-</td> + * <td>SCK</td> + * </tr> + * </table> + * + * + * \section asfdoc_sam0_sercom_spi_mux_settings_slave Slave Mode Settings + * The following table describes the SERCOM pin functionalities for the various + * MUX settings, whilst in SPI Slave mode. + * + * \note If MISO is unlisted, the SPI receiver must not be enabled for the + * given MUX setting. + * + * <table> + * <tr> + * <th>Combination</th> + * <th>DOPO / DIPO</th> + * <th>SERCOM PAD[0]</th> + * <th>SERCOM PAD[1]</th> + * <th>SERCOM PAD[2]</th> + * <th>SERCOM PAD[3]</th> + * </tr> + * <tr> + * <td>A</td> + * <td>0x0 / 0x0</td> + * <td>MISO</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>-</td> + * </tr> + * <tr> + * <td>B</td> + * <td>0x0 / 0x1</td> + * <td>MISO</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>-</td> + * </tr> + * <tr> + * <td>C</td> + * <td>0x0 / 0x2</td> + * <td>MISO</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>-</td> + * </tr> + * <tr> + * <td>D</td> + * <td>0x0 / 0x3</td> + * <td>MISO</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>MOSI</td> + * </tr> + * <tr> + * <td>E</td> + * <td>0x1 / 0x0</td> + * <td>MOSI</td> + * <td>/SS</td> + * <td>MISO</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>F</td> + * <td>0x1 / 0x1</td> + * <td>-</td> + * <td>/SS</td> + * <td>MISO</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>G</td> + * <td>0x1 / 0x2</td> + * <td>-</td> + * <td>/SS</td> + * <td>MISO</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>H</td> + * <td>0x1 / 0x3</td> + * <td>-</td> + * <td>/SS</td> + * <td>MISO</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>I</td> + * <td>0x2 / 0x0</td> + * <td>MOSI</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>MISO</td> + * </tr> + * <tr> + * <td>J</td> + * <td>0x2 / 0x1</td> + * <td>-</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>MISO</td> + * </tr> + * <tr> + * <td>K</td> + * <td>0x2 / 0x2</td> + * <td>-</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>MISO</td> + * </tr> + * <tr> + * <td>L</td> + * <td>0x2 / 0x3</td> + * <td>-</td> + * <td>SCK</td> + * <td>/SS</td> + * <td>MISO</td> + * </tr> + * <tr> + * <td>M</td> + * <td>0x3 / 0x0</td> + * <td>MISO</td> + * <td>/SS</td> + * <td>-</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>N</td> + * <td>0x3 / 0x1</td> + * <td>MISO</td> + * <td>/SS</td> + * <td>-</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>O</td> + * <td>0x3 / 0x2</td> + * <td>MISO</td> + * <td>/SS</td> + * <td>MOSI</td> + * <td>SCK</td> + * </tr> + * <tr> + * <td>P</td> + * <td>0x3 / 0x3</td> + * <td>MISO</td> + * <td>/SS</td> + * <td>-</td> + * <td>SCK</td> + * </tr> + * </table> + * + * + * + * \page asfdoc_sam0_sercom_spi_document_revision_history Document Revision History + * + * <table> + * <tr> + * <th>Doc. Rev.</th> + * <th>Date</th> + * <th>Comments</th> + * </tr> + * <tr> + * <td>42115E</td> + * <td>12/2015</td> + * <td>Add SAM L21/L22, SAM DA1, SAM D09, SAMR30 and SAM C21 support</td> + * </tr> + * <tr> + * <td>42115D</td> + * <td>12/2014</td> + * <td>Add SAM R21/D10/D11 support</td> + * </tr> + * <tr> + * <td>42115C</td> + * <td>01/2014</td> + * <td>Add SAM D21 support</td> + * </tr> + * <tr> + * <td>42115B</td> + * <td>11/2013</td> + * <td>Replaced the pad multiplexing documentation with a condensed table</td> + * </tr> + * <tr> + * <td>42115A</td> + * <td>06/2013</td> + * <td>Initial release</td> + * </tr> + * </table> + */ + +#endif /* SPI_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.c new file mode 100644 index 000000000..5909843b2 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.c @@ -0,0 +1,741 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver + * + * Copyright (c) 2013-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#include "spi_interrupt.h" + +/** + * \internal + * + * Dummy byte to send when reading in master mode. + */ +uint16_t dummy_write; + +/** + * \internal + * Starts transceive of buffers with a given length + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] rx_data Pointer to data to be received + * \param[in] tx_data Pointer to data to be transmitted + * \param[in] length Length of data buffer + * + */ +static void _spi_transceive_buffer( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length) +{ + Assert(module); + Assert(tx_data); + + /* Write parameters to the device instance */ + module->remaining_tx_buffer_length = length; + module->remaining_rx_buffer_length = length; + module->rx_buffer_ptr = rx_data; + module->tx_buffer_ptr = tx_data; + module->status = STATUS_BUSY; + + module->dir = SPI_DIRECTION_BOTH; + + /* Get a pointer to the hardware module instance */ + SercomSpi *const hw = &(module->hw->SPI); + + /* Enable the Data Register Empty and RX Complete Interrupt */ + hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY | + SPI_INTERRUPT_FLAG_RX_COMPLETE); + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Clear TXC flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Enable transmit complete interrupt for slave */ + hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + } +# endif +} + +/** + * \internal + * Starts write of a buffer with a given length + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] tx_data Pointer to data to be transmitted + * \param[in] length Length of data buffer + * + */ +static void _spi_write_buffer( + struct spi_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + Assert(module); + Assert(tx_data); + + /* Write parameters to the device instance */ + module->remaining_tx_buffer_length = length; + module->remaining_dummy_buffer_length = length; + module->tx_buffer_ptr = tx_data; + module->status = STATUS_BUSY; + + module->dir = SPI_DIRECTION_WRITE; + + /* Get a pointer to the hardware module instance */ + SercomSpi *const hw = &(module->hw->SPI); + +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Clear TXC flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Enable transmit complete interrupt for slave */ + hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + } +# endif + + if (module->receiver_enabled) { + /* Enable the Data Register Empty and RX Complete interrupt */ + hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY | + SPI_INTERRUPT_FLAG_RX_COMPLETE); + } else { + /* Enable the Data Register Empty interrupt */ + hw->INTENSET.reg = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + } +} + +/** + * \internal + * Setup SPI to read a buffer with a given length + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] rx_data Pointer to data to be received + * \param[in] length Length of data buffer + * + */ +static void _spi_read_buffer( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + Assert(module); + Assert(rx_data); + + uint8_t tmp_intenset = 0; + + /* Set length for the buffer and the pointer, and let + * the interrupt handler do the rest */ + module->remaining_rx_buffer_length = length; + module->remaining_dummy_buffer_length = length; + module->rx_buffer_ptr = rx_data; + module->status = STATUS_BUSY; + + module->dir = SPI_DIRECTION_READ; + + /* Get a pointer to the hardware module instance */ + SercomSpi *const hw = &(module->hw->SPI); + + /* Enable the RX Complete Interrupt */ + tmp_intenset = SPI_INTERRUPT_FLAG_RX_COMPLETE; + +# if CONF_SPI_MASTER_ENABLE == true + if (module->mode == SPI_MODE_MASTER && module->dir == SPI_DIRECTION_READ) { + /* Enable Data Register Empty interrupt for master */ + tmp_intenset |= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + } +# endif +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Clear TXC flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Enable transmit complete interrupt for slave */ + tmp_intenset |= SPI_INTERRUPT_FLAG_TX_COMPLETE; + + /* Workaround for SSL flag enable */ +#ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT + /* Clear SSL flag if set */ + hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; + /* Enable Slave Select Low Interrupt for slave */ + tmp_intenset |= SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; +#endif + } +# endif + + /* Enable all interrupts simultaneously */ + hw->INTENSET.reg = tmp_intenset; +} + +/** + * \brief Registers a SPI callback function + * + * Registers a callback function which is implemented by the user. + * + * \note The callback must be enabled by \ref spi_enable_callback, in order + * for the interrupt handler to call it when the conditions for the + * callback type are met. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_func Pointer to callback function + * \param[in] callback_type Callback type given by an enum + * + */ +void spi_register_callback( + struct spi_module *const module, + spi_callback_t callback_func, + enum spi_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(callback_func); + + /* Register callback function */ + module->callback[callback_type] = callback_func; + + /* Set the bit corresponding to the callback_type */ + module->registered_callback |= (1 << callback_type); +} + +/** + * \brief Unregisters a SPI callback function + * + * Unregisters a callback function which is implemented by the user. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] callback_type Callback type given by an enum + * + */ +void spi_unregister_callback( + struct spi_module *const module, + enum spi_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Unregister callback function */ + module->callback[callback_type] = NULL; + + /* Clear the bit corresponding to the callback_type */ + module->registered_callback &= ~(1 << callback_type); +} + +/** + * \brief Asynchronous buffer write + * + * Sets up the driver to write to the SPI from a given buffer. If registered + * and enabled, a callback function will be called when the write is finished. + * + * \param[in] module Pointer to SPI software instance struct + * \param[out] tx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * + * \returns Status of the write request operation. + * \retval STATUS_OK If the operation completed successfully + * \retval STATUS_ERR_BUSY If the SPI was already busy with a write + * operation + * \retval STATUS_ERR_INVALID_ARG If requested write length was zero + */ +enum status_code spi_write_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + Assert(module); + Assert(tx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check if the SPI is busy transmitting or slave waiting for TXC*/ + if (module->status == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Issue internal write */ + _spi_write_buffer(module, tx_data, length); + + return STATUS_OK; +} + +/** + * \brief Asynchronous buffer read + * + * Sets up the driver to read from the SPI to a given buffer. If registered + * and enabled, a callback function will be called when the read is finished. + * + * \note If address matching is enabled for the slave, the first character + * received and placed in the RX buffer will be the address. + * + * \param[in] module Pointer to SPI software instance struct + * \param[out] rx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * \param[in] dummy Dummy character to send when reading in master mode + * + * \returns Status of the operation. + * \retval STATUS_OK If the operation completed successfully + * \retval STATUS_ERR_BUSY If the SPI was already busy with a read + * operation + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_INVALID_ARG If requested read length was zero + */ +enum status_code spi_read_buffer_job( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Check if the SPI is busy transmitting or slave waiting for TXC*/ + if (module->status == STATUS_BUSY) { + return STATUS_BUSY; + } + + dummy_write = dummy; + /* Issue internal read */ + _spi_read_buffer(module, rx_data, length); + return STATUS_OK; +} + +/** + * \brief Asynchronous buffer write and read + * + * Sets up the driver to write and read to and from given buffers. If registered + * and enabled, a callback function will be called when the transfer is finished. + * + * \note If address matching is enabled for the slave, the first character + * received and placed in the RX buffer will be the address. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] tx_data Pointer to data buffer to send + * \param[out] rx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * + * \returns Status of the operation. + * \retval STATUS_OK If the operation completed successfully + * \retval STATUS_ERR_BUSY If the SPI was already busy with a read + * operation + * \retval STATUS_ERR_DENIED If the receiver is not enabled + * \retval STATUS_ERR_INVALID_ARG If requested read length was zero + */ +enum status_code spi_transceive_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Check if the SPI is busy transmitting or slave waiting for TXC*/ + if (module->status == STATUS_BUSY) { + return STATUS_BUSY; + } + + /* Issue internal transceive */ + _spi_transceive_buffer(module, tx_data, rx_data, length); + + return STATUS_OK; +} +/** + * \brief Aborts an ongoing job + * + * This function will abort the specified job type. + * + * \param[in] module Pointer to SPI software instance struct + */ +void spi_abort_job( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw + = &(module->hw->SPI); + + /* Abort ongoing job */ + + /* Disable interrupts */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE | + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY | + SPI_INTERRUPT_FLAG_TX_COMPLETE; + + module->status = STATUS_ABORTED; + module->remaining_rx_buffer_length = 0; + module->remaining_dummy_buffer_length = 0; + module->remaining_tx_buffer_length = 0; + + module->dir = SPI_DIRECTION_IDLE; +} + +# if CONF_SPI_SLAVE_ENABLE == true || CONF_SPI_MASTER_ENABLE == true +/** + * \internal + * Writes a character from the TX buffer to the Data register. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_write( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = *(module->tx_buffer_ptr); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + + if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { + data_to_send |= ((*(module->tx_buffer_ptr)) << 8); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + } + + /* Write the data to send*/ + spi_hw->DATA.reg = data_to_send & SERCOM_SPI_DATA_MASK; + + /* Decrement remaining buffer length */ + (module->remaining_tx_buffer_length)--; +} +# endif + +# if CONF_SPI_MASTER_ENABLE == true +/** + * \internal + * Writes a dummy character to the Data register. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_write_dummy( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + /* Write dummy byte */ + spi_hw->DATA.reg = dummy_write; + + /* Decrement remaining dummy buffer length */ + module->remaining_dummy_buffer_length--; +} +# endif + +/** + * \internal + * Writes a dummy character from the to the Data register. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_read_dummy( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + uint16_t flush = 0; + + /* Read dummy byte */ + flush = spi_hw->DATA.reg; + UNUSED(flush); + + /* Decrement remaining dummy buffer length */ + module->remaining_dummy_buffer_length--; +} + +/** + * \internal + * Reads a character from the Data register to the RX buffer. + * + * \param[in,out] module Pointer to SPI software instance struct + */ +static void _spi_read( + struct spi_module *const module) +{ + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + uint16_t received_data = (spi_hw->DATA.reg & SERCOM_SPI_DATA_MASK); + + /* Read value will be at least 8-bits long */ + *(module->rx_buffer_ptr) = received_data; + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + + if(module->character_size == SPI_CHARACTER_SIZE_9BIT) { + /* 9-bit data, write next received byte to the buffer */ + *(module->rx_buffer_ptr) = (received_data >> 8); + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + } + + /* Decrement length of the remaining buffer */ + module->remaining_rx_buffer_length--; +} + +/** + * \internal + * + * Handles interrupts as they occur, and it will run callback functions + * which are registered and enabled. + * + * \note This function will be called by the Sercom_Handler, and should + * not be called directly from any application code. + * + * \param[in] instance ID of the SERCOM instance calling the interrupt + * handler. + */ +void _spi_interrupt_handler( + uint8_t instance) +{ + /* Get device instance from the look-up table */ + struct spi_module *module + = (struct spi_module *)_sercom_instances[instance]; + + /* Pointer to the hardware module instance */ + SercomSpi *const spi_hw = &(module->hw->SPI); + + /* Combine callback registered and enabled masks. */ + uint8_t callback_mask = + module->enabled_callback & module->registered_callback; + + /* Read and mask interrupt flag register */ + uint16_t interrupt_status = spi_hw->INTFLAG.reg; + interrupt_status &= spi_hw->INTENSET.reg; + + /* Data register empty interrupt */ + if (interrupt_status & SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY) { +# if CONF_SPI_MASTER_ENABLE == true + if ((module->mode == SPI_MODE_MASTER) && + (module->dir == SPI_DIRECTION_READ)) { + /* Send dummy byte when reading in master mode */ + _spi_write_dummy(module); + if (module->remaining_dummy_buffer_length == 0) { + /* Disable the Data Register Empty Interrupt */ + spi_hw->INTENCLR.reg + = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + } + } +# endif + + if (0 +# if CONF_SPI_MASTER_ENABLE == true + || ((module->mode == SPI_MODE_MASTER) && + (module->dir != SPI_DIRECTION_READ)) +# endif +# if CONF_SPI_SLAVE_ENABLE == true + || ((module->mode == SPI_MODE_SLAVE) && + (module->dir != SPI_DIRECTION_READ)) +# endif + ) { + /* Write next byte from buffer */ + _spi_write(module); + if (module->remaining_tx_buffer_length == 0) { + /* Disable the Data Register Empty Interrupt */ + spi_hw->INTENCLR.reg + = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + + if (module->dir == SPI_DIRECTION_WRITE && + !(module->receiver_enabled)) { + /* Enable the Data Register transmit complete Interrupt */ + spi_hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + } + } + } + } + + /* Receive complete interrupt*/ + if (interrupt_status & SPI_INTERRUPT_FLAG_RX_COMPLETE) { + /* Check for overflow */ + if (spi_hw->STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) { + if (module->dir != SPI_DIRECTION_WRITE) { + /* Store the error code */ + module->status = STATUS_ERR_OVERFLOW; + + /* End transaction */ + module->dir = SPI_DIRECTION_IDLE; + + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE | + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + /* Run callback if registered and enabled */ + if (callback_mask & (1 << SPI_CALLBACK_ERROR)) { + (module->callback[SPI_CALLBACK_ERROR])(module); + } + } + /* Flush */ + uint16_t flush = spi_hw->DATA.reg; + UNUSED(flush); + /* Clear overflow flag */ + spi_hw->STATUS.reg = SERCOM_SPI_STATUS_BUFOVF; + } else { + if (module->dir == SPI_DIRECTION_WRITE) { + /* Flush receive buffer when writing */ + _spi_read_dummy(module); + if (module->remaining_dummy_buffer_length == 0) { + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE; + module->status = STATUS_OK; + module->dir = SPI_DIRECTION_IDLE; + /* Run callback if registered and enabled */ + if (callback_mask & + (1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){ + (module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED])(module); + } + } + } else { + /* Read data register */ + _spi_read(module); + + /* Check if the last character have been received */ + if (module->remaining_rx_buffer_length == 0) { + module->status = STATUS_OK; + /* Disable RX Complete Interrupt and set status */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE; + if(module->dir == SPI_DIRECTION_BOTH) { + if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSCEIVED)) { + (module->callback[SPI_CALLBACK_BUFFER_TRANSCEIVED])(module); + } + } else if (module->dir == SPI_DIRECTION_READ) { + if (callback_mask & (1 << SPI_CALLBACK_BUFFER_RECEIVED)) { + (module->callback[SPI_CALLBACK_BUFFER_RECEIVED])(module); + } + } + } + } + } + } + + /* Transmit complete */ + if (interrupt_status & SPI_INTERRUPT_FLAG_TX_COMPLETE) { +# if CONF_SPI_SLAVE_ENABLE == true + if (module->mode == SPI_MODE_SLAVE) { + /* Transaction ended by master */ + + /* Disable interrupts */ + spi_hw->INTENCLR.reg = + SPI_INTERRUPT_FLAG_TX_COMPLETE | + SPI_INTERRUPT_FLAG_RX_COMPLETE | + SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY; + /* Clear interrupt flag */ + spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; + + + /* Reset all status information */ + module->dir = SPI_DIRECTION_IDLE; + module->remaining_tx_buffer_length = 0; + module->remaining_rx_buffer_length = 0; + module->status = STATUS_OK; + + if (callback_mask & + (1 << SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE)) { + (module->callback[SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE]) + (module); + } + + } +# endif +# if CONF_SPI_MASTER_ENABLE == true + if ((module->mode == SPI_MODE_MASTER) && + (module->dir == SPI_DIRECTION_WRITE) && !(module->receiver_enabled)) { + /* Clear interrupt flag */ + spi_hw->INTENCLR.reg + = SPI_INTERRUPT_FLAG_TX_COMPLETE; + /* Buffer sent with receiver disabled */ + module->dir = SPI_DIRECTION_IDLE; + module->status = STATUS_OK; + /* Run callback if registered and enabled */ + if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){ + (module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED]) + (module); + } + } +#endif + } + +# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT +# if CONF_SPI_SLAVE_ENABLE == true + /* When a high to low transition is detected on the _SS pin in slave mode */ + if (interrupt_status & SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW) { + if (module->mode == SPI_MODE_SLAVE) { + /* Disable interrupts */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; + /* Clear interrupt flag */ + spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW; + + if (callback_mask & (1 << SPI_CALLBACK_SLAVE_SELECT_LOW)) { + (module->callback[SPI_CALLBACK_SLAVE_SELECT_LOW])(module); + } + } + } +# endif +# endif + +# ifdef FEATURE_SPI_ERROR_INTERRUPT + /* When combined error happen */ + if (interrupt_status & SPI_INTERRUPT_FLAG_COMBINED_ERROR) { + /* Disable interrupts */ + spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR; + /* Clear interrupt flag */ + spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR; + + if (callback_mask & (1 << SPI_CALLBACK_COMBINED_ERROR)) { + (module->callback[SPI_CALLBACK_COMBINED_ERROR])(module); + } + } +# endif +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.h new file mode 100644 index 000000000..bb7168250 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi/spi_interrupt.h @@ -0,0 +1,190 @@ +/** + * \file + * + * \brief SAM Serial Peripheral Interface Driver (Callback Mode) + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef SPI_INTERRUPT_H_INCLUDED +#define SPI_INTERRUPT_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_spi_group + * + * @{ + */ + +#include "spi.h" + +/** + * \name Callback Management + * @{ + */ + +void spi_register_callback( + struct spi_module *const module, + spi_callback_t callback_func, + enum spi_callback callback_type); + +void spi_unregister_callback( + struct spi_module *module, + enum spi_callback callback_type); + +/** + * \brief Enables an SPI callback of a given type + * + * Enables the callback function registered by the \ref spi_register_callback. + * The callback function will be called from the interrupt handler when the + * conditions for the callback type are met. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void spi_enable_callback( + struct spi_module *const module, + enum spi_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Enable callback */ + module->enabled_callback |= (1 << callback_type); +} + +/** + * \brief Disables callback + * + * Disables the callback function registered by the \ref spi_register_callback, + * and the callback will not be called from the interrupt routine. + * + * \param[in] module Pointer to SPI software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void spi_disable_callback( + struct spi_module *const module, + enum spi_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Disable callback */ + module->enabled_callback &= ~(1 << callback_type); +} + +/** @} */ + + +/** + * \name Writing and Reading + * @{ + */ +enum status_code spi_write_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint16_t length); + +enum status_code spi_read_buffer_job( + struct spi_module *const module, + uint8_t *rx_data, + uint16_t length, + uint16_t dummy); + +enum status_code spi_transceive_buffer_job( + struct spi_module *const module, + uint8_t *tx_data, + uint8_t *rx_data, + uint16_t length); + +void spi_abort_job( + struct spi_module *const module); + +/** + * \brief Retrieves the current status of a job. + * + * Retrieves the current status of a job that was previously issued. + * + * \param[in] module Pointer to SPI software instance struct + * + * \return Current job status. + */ +static inline enum status_code spi_get_job_status( + const struct spi_module *const module) +{ + return module->status; +} + +/** + * \brief Retrieves the status of job once it ends. + * + * Waits for current job status to become non-busy, then returns its value. + * + * \param[in] module Pointer to SPI software instance struct + * + * \return Current non-busy job status. + */ +static inline enum status_code spi_get_job_status_wait( + const struct spi_module *const module) +{ + enum status_code status; + + do { + status = spi_get_job_status(module); + } while (status == STATUS_BUSY); + + return status; +} + +/** @} */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* SPI_INTERRUPT_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi.h b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi.h new file mode 100644 index 000000000..3bb66ff5b --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi.h @@ -0,0 +1,55 @@ +/** + * \file + * + * \brief common SPI configuration + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + + +#ifndef CONF_SPI_H_INCLUDED +# define CONF_SPI_H_INCLUDED + +# define CONF_SPI_MASTER_ENABLE true +# define CONF_SPI_SLAVE_ENABLE false + +#endif /* CONF_SPI_H_INCLUDED */ + diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi_master_vec.h b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi_master_vec.h new file mode 100644 index 000000000..394b1fa61 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/module-config/conf_spi_master_vec.h @@ -0,0 +1,68 @@ +/** + * \file + * + * \brief SERCOM SPI master with vectored I/O driver configuration + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef CONF_SPI_MASTER_VEC_H +#define CONF_SPI_MASTER_VEC_H + +#if defined(__FREERTOS__) || defined(__DOXYGEN__) +# include <FreeRTOS.h> +# include <semphr.h> + +# define CONF_SPI_MASTER_VEC_OS_SUPPORT +# define CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE xSemaphoreHandle +# define CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE(semaphore) \ + vSemaphoreCreateBinary(semaphore) +# define CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE(semaphore) \ + vSemaphoreDelete(semaphore) +# define CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE(semaphore) \ + xSemaphoreTake((semaphore), portMAX_DELAY) +# define CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE(semaphore) \ + xSemaphoreGive((semaphore)) +# define CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR(semaphore) \ + xSemaphoreGiveFromISR((semaphore), NULL) +#endif + +#endif // CONF_SPI_MASTER_VEC_H
\ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.c b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.c new file mode 100644 index 000000000..89de7dd0d --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.c @@ -0,0 +1,588 @@ +/** + * \file + * + * \brief SERCOM SPI master with vectored I/O driver implementation + * + * Copyright (C) 2013-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "spi_master_vec.h" +#include <sercom_interrupt.h> +#include <system.h> +#include <system_interrupt.h> + +/** + * \ingroup asfdoc_sam0_sercom_spi_master_vec_group + * + * @{ + */ + +/** + * \name Internal functions + * @{ + */ + +/** \cond INTERNAL */ + +static void _spi_master_vec_int_handler(uint8_t sercom_index); + +/** + * \brief Wait for SERCOM SPI to synchronize + * + * \param[in] sercom_spi SERCOM SPI to check for synchronization. + * + * \note The implementation of this function depends on the SERCOM revision. + */ +static inline void _spi_master_vec_wait_for_sync(SercomSpi *const sercom_spi) +{ +#if defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1) + while (sercom_spi->STATUS.reg & SERCOM_SPI_STATUS_SYNCBUSY) { + /* Intentionally left empty */ + } +#elif defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2) + while (sercom_spi->SYNCBUSY.reg) { + /* Intentionally left empty */ + } +#else +# error Unknown SERCOM SYNCBUSY scheme! +#endif +} + +/** + * \brief Pin MUX configuration helper + * + * \param[in] pinmux Pin MUX setting to apply. Special values: + * \arg \c PINMUX_UNUSED to do nothing. + * \arg \c PINMUX_DEFAULT to use default pin MUX for the SERCOM pad. + * \param[in] sercom,padnum SERCOM pad specification, for \ref PINMUX_DEFAULT. + */ +static inline void _spi_master_vec_pinmux_helper(uint32_t pinmux, + Sercom *const sercom, uint8_t padnum) +{ + struct system_pinmux_config pin_conf; + + if (pinmux == PINMUX_DEFAULT) { + pinmux = _sercom_get_default_pad(sercom, padnum); + } + + if (pinmux == PINMUX_UNUSED) { + return; + } + + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.mux_position = pinmux & 0xFFFF; + system_pinmux_pin_set_config(pinmux >> 16, &pin_conf); +}; + +/** \endcond */ + +/** @} */ + +/** + * \brief Initialize hardware and driver instance + * + * This function configures the clock system for the specified SERCOM module, + * sets up the related pins and their MUX, initializes the SERCOM in SPI master + * mode, and prepares the driver instance for operation. + * + * \pre \ref system_init() must have been called prior to this function. + * + * The SERCOM SPI module is left disabled after initialization, and must be + * enabled with \ref spi_master_vec_enable() before a transfer can be done. + * + * \param[out] module Driver instance to initialize. + * \param[in,out] sercom SERCOM module to initialize and associate driver + * instance with. + * \param[in] config Driver configuration to use. + * + * \return Status of initialization. + * \retval STATUS_OK if initialization succeeded. + * \retval STATUS_ERR_INVALID_ARG if driver has been misconfigured. + */ +enum status_code spi_master_vec_init(struct spi_master_vec_module *const module, + Sercom *const sercom, const struct spi_master_vec_config *const config) +{ + Assert(module); + Assert(sercom); + Assert(config); + + enum status_code status; + SercomSpi *const spi_hw = &(sercom->SPI); + struct system_gclk_chan_config gclk_chan_conf; + uint16_t tmp_baud; + uint32_t sercom_index = _sercom_get_sercom_inst_index((Sercom *)spi_hw); +#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30) + uint32_t pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; +#else + uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; +#endif + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + uint32_t gclk_hz; + + module->sercom = sercom; + + /* Enable clock for the module interface */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + + /* Set up the GCLK for the module */ + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->gclk_generator; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->gclk_generator, false); + + _spi_master_vec_wait_for_sync(spi_hw); + + /* Set up the SERCOM SPI module as master */ + spi_hw->CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3); + spi_hw->CTRLA.reg |= (uint32_t)config->mux_setting + | config->transfer_mode + | config->data_order + | ((config->run_in_standby || system_is_debugger_present()) ? + SERCOM_SPI_CTRLA_RUNSTDBY : 0); + + /* Get baud value from configured baudrate and internal clock rate */ + gclk_hz = system_gclk_chan_get_hz(gclk_index); + status = _sercom_get_sync_baud_val(config->baudrate, gclk_hz, &tmp_baud); + + if (status != STATUS_OK) { + /* Baud rate calculation error! */ + return STATUS_ERR_INVALID_ARG; + } + + spi_hw->BAUD.reg = (uint8_t)tmp_baud; + + /* Configure the pin multiplexers */ + _spi_master_vec_pinmux_helper(config->pinmux_pad0, sercom, 0); + _spi_master_vec_pinmux_helper(config->pinmux_pad3, sercom, 3); + + /* SERCOM PAD1 and PAD2 are used for slave SS. + * This is a SPI master driver, so control of slave SS must be left to + * the PORT module, i.e., peripheral MUX should not be set for that pin. + * DOPO controls which PAD is used for slave SS: + * If DOPO is odd, SERCOM_PAD1 is SS: SERCOM_PAD2 can be MUXed. + * If DOPO is even, SERCOM_PAD2 is SS: SERCOM_PAD1 can be MUXed. + */ + if (config->mux_setting & (1 << SERCOM_SPI_CTRLA_DOPO_Pos)) { + _spi_master_vec_pinmux_helper(config->pinmux_pad2, sercom, 2); + } else { + _spi_master_vec_pinmux_helper(config->pinmux_pad1, sercom, 1); + } + + /* Initialize our instance and register interrupt handler + data */ + module->rx_bufdesc_ptr = NULL; + module->tx_bufdesc_ptr = NULL; + module->direction = SPI_MASTER_VEC_DIRECTION_IDLE; + module->status = STATUS_OK; +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE(module->busy_semaphore); +#endif + + _sercom_set_handler(sercom_index, _spi_master_vec_int_handler); + _sercom_instances[sercom_index] = module; + + return STATUS_OK; +} + +/** + * \brief Enable the SERCOM SPI module + * + * This function must be called after \ref spi_master_vec_init() before a + * transfer can be started. + * + * \param[in,out] module Driver instance to operate on. + */ +void spi_master_vec_enable(const struct spi_master_vec_module *const module) +{ + Assert(module); + Assert(module->sercom); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC + | SERCOM_SPI_INTFLAG_TXC; + + _spi_master_vec_wait_for_sync(spi_hw); + + spi_hw->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE; + + system_interrupt_enable(_sercom_get_interrupt_vector(module->sercom)); +} + +/** + * \brief Disable the SERCOM SPI module + * + * \param[in,out] module Driver instance to operate on. + */ +void spi_master_vec_disable(struct spi_master_vec_module *const module) +{ + Assert(module); + Assert(module->sercom); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + + system_interrupt_disable(_sercom_get_interrupt_vector(module->sercom)); + + _spi_master_vec_wait_for_sync(spi_hw); + + spi_hw->INTENCLR.reg = SERCOM_SPI_INTENCLR_MASK; + spi_hw->INTFLAG.reg = SERCOM_SPI_INTFLAG_MASK; + spi_hw->CTRLB.reg = 0; + spi_hw->CTRLA.reg &= ~SERCOM_SPI_CTRLA_ENABLE; + module->rx_bufdesc_ptr = NULL; + module->tx_bufdesc_ptr = NULL; + module->direction = SPI_MASTER_VEC_DIRECTION_IDLE; + module->status = STATUS_OK; +} + +/** + * \brief Reset the SERCOM SPI module + * + * This function will disable and reset the SPI module to its power on default + * values. + * + * \param[in,out] module Pointer to a driver instance. + */ +void spi_master_vec_reset(struct spi_master_vec_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->sercom); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + + /* Disable the module */ + spi_master_vec_disable(module); + + _spi_master_vec_wait_for_sync(spi_hw); + + /* Software reset the module */ + spi_hw->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST; + +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE(module->busy_semaphore); +#endif +} + +/** + * \brief Start vectored I/O transfer + * + * This function initiates a uni- or bidirectional SPI transfer from/to any + * number of data buffers. The transfer is interrupt-driven and will run in the + * background, after this function has returned. + * + * The buffers to transmit from or receive into must be described in arrays of + * buffer descriptors. These arrays \e must end with descriptors that specify + * zero buffer length. The first descriptor in an array can \e not specify zero + * length. The number of bytes to transmit and to receive do not have to be + * equal. + * + * If the address for a receive buffer is set to \c NULL, the received bytes + * corresponding to that buffer descriptor will be discarded. This is useful if + * slave is already set up to transfer a number of bytes, but the master has no + * available buffer to receive them into. As an example, to receive the two + * first bytes and discard the 128 following, the buffer descriptors could be: +\code + struct spi_master_vec_bufdesc rx_buffers[3] = { + // Read two status bytes + {.data = status_buffer, .length = 2}, + // Discard 128 data bytes + {.data = NULL, .length = 128}, + // End of reception + {.length = 0}, + }; +\endcode + * + * To initiate a unidirectional transfer, pass \c NULL as the address of either + * buffer descriptor array, like this: +\code + // Transmit some buffers + spi_master_vec_transceive_buffer_job(&module, tx_buffers, NULL); + + // Receive some buffers + spi_master_vec_transceive_buffer_job(&module, NULL, rx_buffers); +\endcode + * + * \pre \ref spi_master_vec_init() and \ref spi_master_vec_enable() must have + * been called before this function. + * + * \param[in,out] module Driver instance to operate on. + * \param[in] tx_bufdescs address of buffer descriptor array for bytes to + * transmit. + * \arg \c NULL if the transfer is a simplex read. + * \param[in,out] rx_bufdescs address of buffer descriptor array for storing + * received bytes. + * \arg \c NULL if the transfer is a simplex write. + * + * \return Status of transfer start. + * \retval STATUS_OK if transfer was started. + * \retval STATUS_BUSY if a transfer is already on-going. + */ +enum status_code spi_master_vec_transceive_buffer_job( + struct spi_master_vec_module *const module, + struct spi_master_vec_bufdesc tx_bufdescs[], + struct spi_master_vec_bufdesc rx_bufdescs[]) +{ + Assert(module); + Assert(module->sercom); + Assert(tx_bufdescs || rx_bufdescs); + + SercomSpi *const spi_hw = &(module->sercom->SPI); + uint32_t tmp_ctrlb; + uint8_t tmp_intenset; + + system_interrupt_enter_critical_section(); + if (module->status == STATUS_BUSY) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } else { + module->status = STATUS_BUSY; + system_interrupt_leave_critical_section(); + } + +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE(module->busy_semaphore); +#endif + + module->tx_bufdesc_ptr = tx_bufdescs; + module->rx_bufdesc_ptr = rx_bufdescs; + + if (tx_bufdescs && rx_bufdescs) { + Assert(tx_bufdescs[0].length); + Assert(rx_bufdescs[0].length); + + module->direction = SPI_MASTER_VEC_DIRECTION_BOTH; + module->tx_length = tx_bufdescs[0].length; + module->tx_head_ptr = tx_bufdescs[0].data; + module->rx_length = rx_bufdescs[0].length; + module->rx_head_ptr = rx_bufdescs[0].data; + module->tx_lead_on_rx = 0; + tmp_ctrlb = SERCOM_SPI_CTRLB_RXEN; + tmp_intenset = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC; + } else { + if (tx_bufdescs) { + Assert(tx_bufdescs[0].length); + + module->direction = SPI_MASTER_VEC_DIRECTION_WRITE; + module->tx_length = tx_bufdescs[0].length; + module->tx_head_ptr = tx_bufdescs[0].data; + tmp_ctrlb = 0; + tmp_intenset = SERCOM_SPI_INTFLAG_DRE; + } else { + Assert(rx_bufdescs[0].length); + + module->direction = SPI_MASTER_VEC_DIRECTION_READ; + module->rx_length = rx_bufdescs[0].length; + module->rx_head_ptr = rx_bufdescs[0].data; + module->tx_lead_on_rx = 0; + tmp_ctrlb = SERCOM_SPI_CTRLB_RXEN; + tmp_intenset = SERCOM_SPI_INTFLAG_DRE | SERCOM_SPI_INTFLAG_RXC; + } + } + + /* Ensure the SERCOM is sync'ed before writing these registers */ + _spi_master_vec_wait_for_sync(spi_hw); + + spi_hw->CTRLB.reg = tmp_ctrlb; + spi_hw->INTENSET.reg = tmp_intenset; + + return STATUS_OK; +} + +/** + * \brief Interrupt handler + * + * \param[in] sercom_index SERCOM instance number passed from the master SERCOM + * driver. + */ +static void _spi_master_vec_int_handler(uint8_t sercom_index) +{ + struct spi_master_vec_module *const module = + _sercom_instances[sercom_index]; + enum _spi_master_vec_direction dir = module->direction; + SercomSpi *const spi_hw = &(module->sercom->SPI); + uint8_t int_status; + + int_status = spi_hw->INTFLAG.reg ; + int_status &= spi_hw->INTENSET.reg; + + if (int_status & SERCOM_SPI_INTFLAG_DRE) { + uint_fast8_t tx_lead_on_rx = module->tx_lead_on_rx; + + /* If TX is ahead of RX by 2+ bytes, allow RX to catch up. + * Note: will only happen _once_ per READ or BOTH. + */ + if ((tx_lead_on_rx >= 2) && (dir != SPI_MASTER_VEC_DIRECTION_WRITE)) { + Assert((dir == SPI_MASTER_VEC_DIRECTION_READ) + || (dir == SPI_MASTER_VEC_DIRECTION_BOTH)); + Assert(int_status & SERCOM_SPI_INTFLAG_RXC); + /* Otherwise, we can send more bytes */ + } else { + module->tx_lead_on_rx = ++tx_lead_on_rx; + + /* If doing a READ, just send 0 to trigger the transfer */ + if (dir == SPI_MASTER_VEC_DIRECTION_READ) { + uint32_t tx_lead_limit; + + spi_hw->DATA.reg = 0; + +check_for_read_end: + /* With current TX'ed bytes, will we get the last RX byte? + * If so, we can disable the DRE interrupt to stop transmitting. + * + * Since a buffer can have minimum 1 byte length, this check is + * simplified by first checking if the RX end is so close that + * the max. 2 byte lead of TX may actually fill the buffers. + */ + tx_lead_limit = (module->rx_bufdesc_ptr + 1)->length; + + if (!tx_lead_limit || !(module->rx_bufdesc_ptr + 2)->length) { + tx_lead_limit += module->rx_length; + + if (tx_lead_on_rx >= tx_lead_limit) { + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE; + } + } + /* For WRITE and BOTH, output current byte */ + } else { + spi_master_vec_buflen_t tx_length; + uint8_t *tx_head_ptr; + + tx_head_ptr = module->tx_head_ptr; + spi_hw->DATA.reg = *(tx_head_ptr++); + + /* Check if this was the last byte to send */ + tx_length = module->tx_length - 1; + + if (tx_length) { + module->tx_head_ptr = tx_head_ptr; + module->tx_length = tx_length; + } else { + /* Any more buffers left to send, perhaps? */ + tx_length = (++module->tx_bufdesc_ptr)->length; + + if (tx_length) { + module->tx_head_ptr = module->tx_bufdesc_ptr->data; + module->tx_length = tx_length; + } else { + if (dir == SPI_MASTER_VEC_DIRECTION_WRITE) { + /* Disable DRE and enable TXC to end WRITE */ + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE; + spi_hw->INTENSET.reg = SERCOM_SPI_INTFLAG_TXC; + } else { + /* For BOTH, check if we still have bytes to read */ + dir = SPI_MASTER_VEC_DIRECTION_READ; + module->direction = dir; + goto check_for_read_end; + } + } + } + } + } + } + + /* For READ and BOTH, store the received byte */ + if (int_status & SERCOM_SPI_INTFLAG_RXC) { + spi_master_vec_buflen_t rx_length; + uint8_t *rx_head_ptr; + + rx_head_ptr = module->rx_head_ptr; + if (rx_head_ptr != NULL) { + *(rx_head_ptr++) = spi_hw->DATA.reg; + } else { + uint8_t dummy; + dummy = spi_hw->DATA.reg; + UNUSED(dummy); + } + module->tx_lead_on_rx--; + + /* Check if this was the last byte to receive */ + rx_length = module->rx_length - 1; + + if (rx_length) { + module->rx_head_ptr = rx_head_ptr; + module->rx_length = rx_length; + } else { + /* Any more buffers left to receive into? */ + rx_length = (++module->rx_bufdesc_ptr)->length; + + if (rx_length) { + module->rx_head_ptr = module->rx_bufdesc_ptr->data; + module->rx_length = rx_length; + } else { + /* Disable the SPI receiver (instant effect) and RX interrupt */ + spi_hw->CTRLB.reg = 0; + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_RXC; + + if (dir == SPI_MASTER_VEC_DIRECTION_READ) { + /* If doing READ, end the transaction here */ + dir = SPI_MASTER_VEC_DIRECTION_IDLE; + module->direction = dir; + module->status = STATUS_OK; +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR(module->busy_semaphore); +#endif + } else { + /* If doing BOTH, change direction to WRITE */ + dir = SPI_MASTER_VEC_DIRECTION_WRITE; + module->direction = dir; + } + } + } + } + + /* For WRITE */ + if (int_status & SERCOM_SPI_INTFLAG_TXC) { + /* End transaction here, since last byte has been sent */ + spi_hw->INTENCLR.reg = SERCOM_SPI_INTFLAG_TXC; + + dir = SPI_MASTER_VEC_DIRECTION_IDLE; + module->direction = dir; + module->status = STATUS_OK; +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR(module->busy_semaphore); +#endif + } +} + +/** + * @} + */
\ No newline at end of file diff --git a/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.h b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.h new file mode 100644 index 000000000..a10ae22f1 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/spi_master_vec/spi_master_vec.h @@ -0,0 +1,692 @@ +/** + * \file + * + * \brief SERCOM SPI master with vectored I/O driver include + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef SPI_MASTER_VEC_H +#define SPI_MASTER_VEC_H + +#include <compiler.h> +#include <gclk.h> +#include <port.h> +#include <spi.h> +#include <status_codes.h> +#include <conf_spi_master_vec.h> + +/** + * \defgroup asfdoc_sam0_sercom_spi_master_vec_group SAM Serial Peripheral Interface Master Driver w/ Vectored I/O (SERCOM SPI) + * + * This driver for Atmel® | SMART SAM devices provides an interface for the configuration + * and operation of the SERCOM module in SPI master mode and uses vectored I/O + * for data transfers. + * + * The following peripherals are used by this driver: + * - SERCOM (Serial Communication Interface) + * + * The following devices can use this driver: + * - Atmel | SMART SAM D20/D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D09/D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * + * The reader is assumed to be familiar with the regular SERCOM SPI driver, and + * how it is configured and operated. Configuration of this driver is done a + * similar way and actually re-uses several enumerations (configuration values) + * from the regular SERCOM SPI driver. + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_sercom_spi_master_vec_prerequisites + * - \ref asfdoc_sam0_sercom_spi_master_vec_module_overview + * - \ref asfdoc_sam0_sercom_spi_master_vec_special_considerations + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_info + * - \ref asfdoc_sam0_sercom_spi_master_vec_examples + * - \ref asfdoc_sam0_sercom_spi_master_vec_api_overview + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_prerequisites Prerequisites + * + * This driver uses the \ref asfdoc_sam0_system_clock_group "SYSTEM clock driver" + * to select the SERCOM's clock source and to configure the SERCOM for the + * desired baud rate. Ensure that the selected clock source is configured and + * that the clock system is initialized. This is typically done indirectly with + * \ref system_init(), or directly with \ref system_clock_init(). + * + * According to the datasheet, the minimum and maximum limits for the baud rate + * is given by: \f$ \frac{1}{2^{17}} \times f_{clk} \le f_{baud} + * \le \frac{1}{2} \times f_{clk} \f$. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_module_overview Module Overview + * + * This SERCOM SPI master driver supports uni- and bidirectional transfers of + * 8-bit data with vectored I/O, also know as scatter/gather. + * It does not implement control of SS or slave addressing since the intended + * usage is in stacks which usually have their own protocols and handshaking + * schemes. + * + * + * \subsection asfdoc_sam0_sercom_spi_master_vectored_io Vectored I/O + * + * Vectored I/O enables the transfer of data from/to any number of buffers with + * arbitrary memory locations without having to do several transfers, i.e., one + * buffer at a time. This feature is useful in stacks because it allows each + * layer of the stack to have a dedicated data buffer, thus avoiding the need + * for a centralized data buffer that the different layers must use in + * cooperation. + * + * The vectored I/O relies on arrays of buffer descriptors which must be passed + * to the driver to start a transfer. These buffer descriptors specify where in + * memory each buffer is, and how large they are. + * \ref asfdoc_sam0_vectored_io_example "The figure below" illustrates this + * for an example with three buffers of varying sizes that are transmitted. + * + * \anchor asfdoc_sam0_vectored_io_example + * \dot +digraph bufptr_to_spiord { + rankdir=LR; + subgraph cluster_bufptr { + style=invis; + bufptr_label [shape=none, label="Buffer descriptors"]; + bufptrs [shape=record, label="<bf1> [0]|<bf2> [1]|<bf3> [2]|<bf4> [3]"]; + } + subgraph cluster_buf { + style=invis; + buf_label [shape=none, label="Memory layout"]; + bufs [shape=record, label="...|<b1>"yy"|...|<b3>"z"|<b2>"xxx"|..."]; + } + subgraph cluster_spiord { + style=invis; + spiord_label [shape=none, label="SPI transmission"]; + spiord [shape=record, label="<s1>"yy"|<s2>"xxx"|<s3>"z""]; + } + bufptrs:bf1 -> bufs:b1 -> spiord:s1; + bufptrs:bf2 -> bufs:b2 -> spiord:s2; + bufptrs:bf3 -> bufs:b3 -> spiord:s3; + bufptrs:bf4 -> "none"; +} + * \enddot + * + * Note that the last descriptor \e must indicate no next buffer in order for + * the driver to detect that the end of the buffer list has been reached. This + * means that for \c N buffers, \c N+1 buffer descriptors are needed. + * + * Bidirectional transfers are supported without any restrictions on the buffer + * descriptors, so the number of bytes and buffers to receive and transmit do + * \e not have to be the same. + * + * \sa spi_master_vec_transceive_buffer_job() for details on starting transfers. + * + * + * \subsection asfdoc_sam0_sercom_spi_master_os_support OS support + * + * Since this driver is interrupt-driven, it is possible for the MCU to run + * other code while a transfer is on-going. + * + * In a single-threaded application, this can be achieved by starting a transfer + * and then avoid any waiting for completion until absolutely required, e.g., + * when a new transfer is needed. + * + * But in a multi-threaded application, for example based on FreeRTOS, one can + * utilize \e semaphores to let the OS know when a function is waiting and thus + * blocking the thread, and that other threads can be run instead. Put another + * way, the waiting can be made efficient. + * + * This driver has an internal semaphore which is used to signal to the OS + * whenever a function is waiting for a transfer to complete. And since the + * semaphore datatypes and functions are OS-specific, the support has been made + * configurable by the use of macros. Note that support for FreeRTOS is already + * implemented, but must be enabled. + * + * \sa CONF_SPI_MASTER_VEC_OS_SUPPORT for more on the configurable OS support. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_special_considerations Special Considerations + * + * \subsection asfdoc_sam0_sercom_spi_master_vec_special_isr Interrupt safety + * + * This driver should not be used within interrupt contexts. The reason for this + * is that the driver itself is interrupt driven. Further, the configurable OS + * support is implemented with the assumption that transfers are only started + * in threads, not in interrupt service routines, because it gives the simplest + * API. + * + * + * \subsection asfdoc_sam0_sercom_spi_master_vec_special_mux Signal MUX + * + * The SERCOM module has two layers of signal multiplexing in SPI mode: + * -# SERCOM pad MUX: This routes the SPI signals to internal lines. + * -# PORT pin MUX: This routes the internal line to a device pin. + * + * Both of these layers are configured in the \ref spi_master_vec_config + * "configuration structure", using the members named \c mux_setting and + * \c pinmux_padN. These must be set in combination. + * + * The driver supplies values for the + * \ref spi_master_vec_config::mux_setting "SERCOM pad MUX" from the standard + * ASF SERCOM SPI driver. For the PORT pin MUX configuration, refer to the + * peripheral include file for the device (\c pio_samd20XNN.h ) and use the + * macros that are prefixed with \c PINMUX_, such as + * \c PINMUX_PA04D_SERCOM0_PAD0. It is also possible to use the default pin MUX + * setting for a SERCOM pad by using the \ref PINMUX_DEFAULT macro. The defaults + * are defined in the file \ref sercom_pinout.h. + * + * Note that for \ref spi_master_vec_init() to function properly with the macro + * \ref PINMUX_DEFAULT, the order of the values in \c pinmux_padN \e must be + * correct, i.e., \c pinmux_pad0 must contain the pin MUX setting for + * multiplexing SERCOM pad 0, and so on. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_info Extra Information + * + * For extra information, see + * \subpage asfdoc_sam0_sercom_spi_master_vec_extra. This includes: + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_acronyms + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_dependencies + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_errata + * - \ref asfdoc_sam0_sercom_spi_master_vec_extra_history + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_spi_master_vec_exqsg. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_api_overview API Overview + * + * @{ + */ + +/** + * Driver configuration structure + * + * \sa asfdoc_sam0_sercom_spi_master_vec_special_considerations for more + * information regarding SERCOM pad and pin MUX. + */ +struct spi_master_vec_config { + /** Baud rate in Hertz. */ + uint32_t baudrate; + /** GCLK generator to use for the SERCOM. */ + enum gclk_generator gclk_generator; + /** Enabled in sleep modes. */ + bool run_in_standby; + /** SERCOM pad MUX setting. */ + enum spi_signal_mux_setting mux_setting; + /** Transfer mode. */ + enum spi_transfer_mode transfer_mode; + /** Data order. */ + enum spi_data_order data_order; + /** Pin MUX setting for SERCOM pad 0. */ + uint32_t pinmux_pad0; + /** Pin MUX setting for SERCOM pad 1. */ + uint32_t pinmux_pad1; + /** Pin MUX setting for SERCOM pad 2. */ + uint32_t pinmux_pad2; + /** Pin MUX setting for SERCOM pad 4. */ + uint32_t pinmux_pad3; +}; + +/** Buffer length container. */ +typedef uint16_t spi_master_vec_buflen_t; + +/** Buffer descriptor structure. */ +struct spi_master_vec_bufdesc { + /** Pointer to buffer start. */ + void *data; + /** Length of buffer. */ + spi_master_vec_buflen_t length; +}; + +/** Transfer direction */ +enum _spi_master_vec_direction { + SPI_MASTER_VEC_DIRECTION_READ, + SPI_MASTER_VEC_DIRECTION_WRITE, + SPI_MASTER_VEC_DIRECTION_BOTH, + SPI_MASTER_VEC_DIRECTION_IDLE, +}; + +/** Driver instance. */ +struct spi_master_vec_module { +#if !defined(__DOXYGEN__) + Sercom *volatile sercom; + volatile bool locked; + volatile enum _spi_master_vec_direction direction; + volatile enum status_code status; + volatile spi_master_vec_buflen_t rx_length; + volatile spi_master_vec_buflen_t tx_length; + uint8_t *volatile rx_head_ptr; + uint8_t *volatile tx_head_ptr; + volatile uint_fast8_t tx_lead_on_rx; + struct spi_master_vec_bufdesc *volatile rx_bufdesc_ptr; + struct spi_master_vec_bufdesc *volatile tx_bufdesc_ptr; +# ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE busy_semaphore; +# endif +#endif +}; + +/** + * \name Configuration and Initialization + * @{ + */ + +/** + * \brief Initialize configuration with default values. + * + * \param[out] config Configuration struct to initialize. + */ +static inline void spi_master_vec_get_config_defaults( + struct spi_master_vec_config *const config) +{ + config->baudrate = 100000; + config->gclk_generator = GCLK_GENERATOR_0; + config->run_in_standby = false; + config->mux_setting = SPI_SIGNAL_MUX_SETTING_D; + config->transfer_mode = SPI_TRANSFER_MODE_0; + config->data_order = SPI_DATA_ORDER_MSB; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->pinmux_pad2 = PINMUX_DEFAULT; + config->pinmux_pad3 = PINMUX_DEFAULT; +} + +#ifdef __cplusplus +extern "C" { +#endif + +enum status_code spi_master_vec_init(struct spi_master_vec_module *const module, + Sercom *const sercom, const struct spi_master_vec_config *const config); + +/** @} */ + +/** + * \name Enable/Disable and Reset + * @{ + */ + +void spi_master_vec_enable(const struct spi_master_vec_module *const module); +void spi_master_vec_disable(struct spi_master_vec_module *const module); +void spi_master_vec_reset(struct spi_master_vec_module *const module); + +/** @} */ + +/** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock. + * + * \retval STATUS_OK if the module was locked. + * \retval STATUS_BUSY if the module was already locked. + */ +static inline enum status_code spi_master_vec_lock( + struct spi_master_vec_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock. + */ +static inline void spi_master_vec_unlock( + struct spi_master_vec_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \name Read/Write and Status + * @{ + */ + +enum status_code spi_master_vec_transceive_buffer_job( + struct spi_master_vec_module *const module, + struct spi_master_vec_bufdesc tx_bufdescs[], + struct spi_master_vec_bufdesc rx_bufdescs[]); + +/** + * \brief Get current status of transfer. + * + * \param[in] module Driver instance to operate on. + * + * \return Current status of driver instance. + * \retval STATUS_OK if idle and previous transfer succeeded. + * \retval STATUS_BUSY if a transfer is ongoing. + * \retval <other> if previous transfer failed. + */ +static inline enum status_code spi_master_vec_get_job_status( + const struct spi_master_vec_module *const module) +{ + return module->status; +} + +/** + * \brief Get status of transfer upon job end. + * + * \param[in] module Driver instance to operate on. + * + * \return Current status of driver instance. + * \retval STATUS_OK if idle and previous transfer succeeded. + * \retval <other> if previous transfer failed. + */ +static inline enum status_code spi_master_vec_get_job_status_wait( + const struct spi_master_vec_module *const module) +{ + enum status_code status; + +#ifdef CONF_SPI_MASTER_VEC_OS_SUPPORT + CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE(module->busy_semaphore); + status = spi_master_vec_get_job_status(module); + CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE(module->busy_semaphore); +#else + do { + status = spi_master_vec_get_job_status(module); + } while (status == STATUS_BUSY); +#endif + + return status; +} + + +/** + * \brief Start vectored I/O transfer, wait for it to end. + * + * \param[in,out] module Driver instance to operate on. + * \param[in] tx_bufdescs address of buffer descriptor array for bytes to + * transmit. + * \arg \c NULL if the transfer is a simplex read. + * \param[in,out] rx_bufdescs address of buffer descriptor array for storing + * received bytes. + * \arg \c NULL if the transfer is a simplex write. + * + * \return Status of transfer start. + * \retval STATUS_OK if transfer succeeded. + * \retval STATUS_BUSY if a transfer was already on-going. + * \retval <other> if transfer failed. + */ +static inline enum status_code spi_master_vec_transceive_buffer_wait( + struct spi_master_vec_module *const module, + struct spi_master_vec_bufdesc tx_bufdescs[], + struct spi_master_vec_bufdesc rx_bufdescs[]) +{ + enum status_code status; + + status = spi_master_vec_transceive_buffer_job(module, tx_bufdescs, + rx_bufdescs); + + if (status == STATUS_BUSY) { + return status; + } + + return spi_master_vec_get_job_status_wait(module); +} + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** + * \name OS Support Configuration + * @{ + */ + +/** + * \def CONF_SPI_MASTER_VEC_OS_SUPPORT + * \brief Enable support for OS + * + * Defining this symbol will enable support for an OS, e.g., FreeRTOS, by using + * the macros in this group: + * - \ref CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE + * - \ref CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE + * - \ref CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR + * + * The user must ensure that these macros map to the implementation for the OS + * that is used. Further, required includes must be added alongside these macros + * in \ref conf_spi_master_vec.h. + * + * The purpose of this configuration is to enable usage of the OS' semaphore + * system to wait for transfers to end rather than continuously polling the + * transfer status, which is an inefficient approach. + */ + +/** + * \def CONF_SPI_MASTER_VEC_SEMAPHORE_TYPE + * \brief Semaphore datatype + */ + +/** + * \def CONF_SPI_MASTER_VEC_CREATE_SEMAPHORE + * \brief Create/initialize semaphore + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_DELETE_SEMAPHORE + * \brief Delete semaphore + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_TAKE_SEMAPHORE + * \brief Wait for and take semaphore + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE + * \brief Give semaphore back + * + * \param semaphore Semaphore member in driver instance. + */ + +/** + * \def CONF_SPI_MASTER_VEC_GIVE_SEMAPHORE_FROM_ISR + * \brief Give semaphore back from an ISR + * + * \param semaphore Semaphore member in driver instance. + */ + +/** @} */ + +/** + * @} + */ + +/** + * \page asfdoc_sam0_sercom_spi_master_vec_extra Extra Information for SERCOM SPI Master Driver w/ Vectored I/O + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_acronyms Acronyms + * + * The table below presents the acronyms used in this module. + * + * <table> + * <tr> + * <th>Acronym</td> + * <th>Description</td> + * </tr> + * <tr> + * <td>GCLK</td> + * <td>Generic Clock</td> + * </tr> + * <tr> + * <td>MISO</td> + * <td>Master Input, Slave Output</td> + * </tr> + * <tr> + * <td>MOSI</td> + * <td>Master Output, Slave Input</td> + * </tr> + * <tr> + * <td>SCK</td> + * <td>Serial Clock</td> + * </tr> + * <tr> + * <td>SPI</td> + * <td>Serial Peripheral Interface</td> + * </tr> + * <tr> + * <td>SS</td> + * <td>Slave Select</td> + * </tr> + * </table> + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_dependencies Dependencies + * + * This driver has the following dependencies: + * - \ref asfdoc_sam0_system_group "System driver" + * - \ref group_sam0_utils "Compiler driver" + * - \ref asfdoc_sam0_port_group "Port driver" + * - \ref Common SERCOM driver base + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_errata Errata + * + * There are no errata related to this driver. + * + * + * \section asfdoc_sam0_sercom_spi_master_vec_extra_history Module History + * + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in the + * table. + * + * <table> + * <tr> + * <th>Changelog</th> + * </tr> + * <tr> + * <td>Initial Release</td> + * </tr> + * </table> + */ + +/** + * \page asfdoc_sam0_sercom_spi_master_vec_exqsg Examples for SERCOM SPI Master Driver w/ Vectored I/O + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_spi_master_vec_group. QSGs are + * simple examples with step-by-step instructions to configure and use this + * driver in a selection of use cases. Note that QSGs can be compiled as a + * standalone application or be added to the user application. + * + * - \subpage asfdoc_sam0_sercom_spi_master_vec_basic + */ + + /** + * \page asfdoc_sam0_sercom_spi_master_vec_document_revision_history Document Revision History + * + * <table> + * <tr> + * <th>Doc. Rev.</td> + * <th>Date</td> + * <th>Comments</td> + * </tr> + * <tr> + * <td>D</td> + * <td>12/2014</td> + * <td>Add SAM L21/C21 support.</td> + * </tr> + * <tr> + * <td>C</td> + * <td>04/2014</td> + * <td>Add SAM D10/D11 support.</td> + * </tr> + * <tr> + * <td>B</td> + * <td>03/2014</td> + * <td>Add SAMR21 support.</td> + * </tr> + * <tr> + * <td>A</td> + * <td>01/2014</td> + * <td>Initial release</td> + * </tr> + * </table> + */ + +#endif /* SPI_MASTER_VEC_H */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/docimg/usart_frame.svg b/atmel-samd/asf/sam0/drivers/sercom/usart/docimg/usart_frame.svg new file mode 100644 index 000000000..2a7550184 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/docimg/usart_frame.svg @@ -0,0 +1,272 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<!-- Generated by Microsoft Visio 11.0, SVG Export, v1.0 usart_frame.svg Page-1 --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="6.50273in" + height="0.879475in" viewBox="0 0 468.197 63.3222" xml:space="preserve" color-interpolation-filters="sRGB" class="st7"> + <v:documentProperties v:langID="1044" v:metric="true" v:viewMarkup="false"/> + + <style type="text/css"> + <![CDATA[ + .st1 {stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24} + .st2 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72} + .st3 {fill:#000000;font-family:Arial;font-size:0.666664em} + .st4 {marker-end:url(#mrkr13-134);marker-start:url(#mrkr13-132);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24} + .st5 {fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.08695652173913} + .st6 {fill:#ffffff;stroke:none;stroke-linecap:butt;stroke-width:7.2} + .st7 {fill:none;fill-rule:evenodd;font-size:12;overflow:visible;stroke-linecap:square;stroke-miterlimit:3} + ]]> + </style> + + <defs id="Markers"> + <g id="lend13"> + <path d="M 3 1 L -0 0 L 3 -1 L 3 1 " style="stroke:none"/> + </g> + <marker id="mrkr13-132" class="st5" v:arrowType="13" v:arrowSize="2" v:setback="33" refX="33" orient="auto" + markerUnits="strokeWidth"> + </marker> + <marker id="mrkr13-134" class="st5" v:arrowType="13" v:arrowSize="2" v:setback="34.5" refX="-34.5" orient="auto" + markerUnits="strokeWidth"> + </marker> + </defs> + <g v:mID="0" v:index="1" v:groupContext="foregroundPage"> + <v:userDefs> + <v:ud v:nameU="ShapeNumCriteria" v:prompt="" v:val="VT4(0,0,0,1,1,'',0,0,0,1,0,1,)"/> + </v:userDefs> + <title>Page-1</title> + <v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394" + v:shadowOffsetY="-8.50394"/> + <g id="group3-1" transform="translate(99.4526,-0.72)" v:mID="3" v:groupContext="group"> + <title>Sheet.3</title> + <g id="shape1-2" v:mID="1" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.1</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape2-5" v:mID="2" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.2</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="group4-8" transform="translate(127.799,-0.72)" v:mID="4" v:groupContext="group"> + <title>Sheet.4</title> + <g id="shape5-9" v:mID="5" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.5</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape6-12" v:mID="6" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.6</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="group7-15" transform="translate(156.146,-0.72)" v:mID="7" v:groupContext="group"> + <title>Sheet.7</title> + <g id="shape8-16" v:mID="8" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.8</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape9-19" v:mID="9" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.9</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="group10-22" transform="translate(184.492,-0.72)" v:mID="10" v:groupContext="group"> + <title>Sheet.10</title> + <g id="shape11-23" v:mID="11" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.11</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape12-26" v:mID="12" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.12</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="group13-29" transform="translate(212.838,-0.72)" v:mID="13" v:groupContext="group"> + <title>Sheet.13</title> + <g id="shape14-30" v:mID="14" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.14</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape15-33" v:mID="15" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.15</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="group16-36" transform="translate(241.185,-0.72)" v:mID="16" v:groupContext="group"> + <title>Sheet.16</title> + <g id="shape17-37" v:mID="17" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.17</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape18-40" v:mID="18" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.18</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="group19-43" transform="translate(269.531,-0.72)" v:mID="19" v:groupContext="group"> + <title>Sheet.19</title> + <g id="shape20-44" v:mID="20" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.20</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape21-47" v:mID="21" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.21</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="shape22-50" v:mID="22" v:groupContext="shape" transform="translate(107.957,-0.72)"> + <title>Sheet.22</title> + <desc>1</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="11.95" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text> </g> + <g id="shape23-53" v:mID="23" v:groupContext="shape" transform="translate(134.886,-0.72)"> + <title>Sheet.23</title> + <desc>2</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="11.95" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>2</text> </g> + <g id="shape24-56" v:mID="24" v:groupContext="shape" transform="translate(163.232,-0.72)"> + <title>Sheet.24</title> + <desc>3</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="11.95" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>3</text> </g> + <g id="shape25-59" v:mID="25" v:groupContext="shape" transform="translate(191.579,-0.72)"> + <title>Sheet.25</title> + <desc>4</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="11.95" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>4</text> </g> + <g id="shape26-62" v:mID="26" v:groupContext="shape" transform="translate(219.925,-0.72)"> + <title>Sheet.26</title> + <desc>[5]</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="9.73" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>[5]</text> </g> + <g id="shape27-65" v:mID="27" v:groupContext="shape" transform="translate(248.271,-0.72)"> + <title>Sheet.27</title> + <desc>[6]</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="9.73" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>[6]</text> </g> + <g id="shape28-68" v:mID="28" v:groupContext="shape" transform="translate(275.909,-0.72)"> + <title>Sheet.28</title> + <desc>[7]</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="9.73" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>[7]</text> </g> + <g id="shape29-71" v:mID="29" v:groupContext="shape" transform="translate(302.838,-0.72)"> + <title>Sheet.29</title> + <desc>[8]</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="9.73" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>[8]</text> </g> + <g id="shape30-74" v:mID="30" v:groupContext="shape" transform="translate(141.916,62.5743) rotate(116.565)"> + <title>Sheet.30</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="group31-77" transform="translate(297.878,-0.72)" v:mID="31" v:groupContext="group"> + <title>Sheet.31</title> + <g id="shape32-78" v:mID="32" v:groupContext="shape" transform="translate(56.6371,6.6572) rotate(63.4349)"> + <title>Sheet.32</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape33-81" v:mID="33" v:groupContext="shape" transform="translate(-56.6371,35.0037) rotate(-63.4349)"> + <title>Sheet.33</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + </g> + <g id="shape34-84" v:mID="34" v:groupContext="shape" transform="translate(269.587,34.2837) rotate(-63.4349)"> + <title>Sheet.34</title> + <path d="M0 63.32 L31.69 63.32" class="st1"/> + </g> + <g id="shape35-87" v:mID="35" v:groupContext="shape" transform="translate(0.24,-0.72)"> + <title>Sheet.35</title> + <path d="M325.98 63.32 L70.87 63.32 L56.69 63.32 L42.52 34.98 L0 34.98" class="st1"/> + </g> + <g id="shape37-90" v:mID="37" v:groupContext="shape" transform="translate(467.957,97.578) rotate(180)"> + <title>Sheet.37</title> + <path d="M0 63.32 L56.69 63.32" class="st1"/> + </g> + <g id="shape38-93" v:mID="38" v:groupContext="shape" transform="translate(79.6101,-0.72)"> + <title>Sheet.38</title> + <desc>0</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="11.95" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text> </g> + <g id="shape39-96" v:mID="39" v:groupContext="shape" transform="translate(49.8463,-0.72)"> + <title>Sheet.39</title> + <desc>St</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="10.39" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>St</text> </g> + <g id="shape40-99" v:mID="40" v:groupContext="shape" transform="translate(8.74394,-0.72)"> + <title>Sheet.40</title> + <desc>(IDLE)</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="18.4252" cy="49.149" width="36.86" height="28.3465"/> + <rect x="0" y="34.9757" width="36.8504" height="28.3465" class="st2"/> + <text x="6.87" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>(IDLE)</text> </g> + <g id="shape41-102" v:mID="41" v:groupContext="shape" transform="translate(361.657,-0.72)"> + <title>Sheet.41</title> + <desc>Sp1</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="7.06" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>Sp1</text> </g> + <g id="shape42-105" v:mID="42" v:groupContext="shape" transform="translate(388.586,-0.72)"> + <title>Sheet.42</title> + <desc>[Sp2]</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="4.83" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>[Sp2]</text> </g> + <g id="shape43-108" v:mID="43" v:groupContext="shape" transform="translate(422.602,-0.72)"> + <title>Sheet.43</title> + <desc>(St/IDLE)</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="21.2598" cy="49.149" width="42.52" height="28.3465"/> + <rect x="0" y="34.9757" width="42.5197" height="28.3465" class="st2"/> + <text x="4.81" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>(St/IDLE)</text> </g> + <g id="shape44-111" v:mID="44" v:groupContext="shape" transform="translate(-20.5625,22.9172) rotate(-90)"> + <title>Sheet.44</title> + <path d="M0 63.32 L22.68 63.32" class="st1"/> + </g> + <g id="shape45-114" v:mID="45" v:groupContext="shape" transform="translate(347.941,22.9172) rotate(-90)"> + <title>Sheet.45</title> + <path d="M0 63.32 L22.68 63.32" class="st1"/> + </g> + <g id="shape240-117" v:mID="240" v:groupContext="shape" transform="translate(326.224,-0.72)"> + <title>Sheet.240</title> + <path d="M42.52 34.98 L28.35 63.32 L14.17 63.32 L0 34.98" class="st1"/> + </g> + <g id="shape36-120" v:mID="36" v:groupContext="shape" transform="translate(85.2794,-0.72)"> + <title>Sheet.36</title> + <path d="M0 34.98 L325.98 34.98 L340.16 63.32 L379.84 63.32" class="st1"/> + </g> + <g id="shape241-123" v:mID="241" v:groupContext="shape" transform="translate(333.311,-0.72)"> + <title>Sheet.241</title> + <desc>[P]</desc> + <v:textBlock v:margins="rect(4,4,4,4)"/> + <v:textRect cx="14.1732" cy="49.149" width="28.35" height="28.3465"/> + <rect x="0" y="34.9757" width="28.3465" height="28.3465" class="st2"/> + <text x="9.28" y="51.55" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>[P]</text> </g> + <g id="shape242-126" v:mID="242" v:groupContext="shape" transform="translate(42.7597,-51.7436)"> + <title>Sheet.242</title> + <desc>Frame</desc> + <v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/> + <v:textRect cx="184.252" cy="63.3222" width="368.51" height="0"/> + <path d="M7.92 63.32 L8.28 63.32 L360.22 63.32" class="st4"/> + <rect v:rectContext="textBkgnd" x="172.695" y="58.5223" width="23.1133" height="9.59985" class="st6"/> + <text x="172.7" y="65.72" class="st3" v:langID="1044"><v:paragraph v:horizAlign="1"/><v:tabList/>Frame</text> </g> + </g> +</svg> diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart.c b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.c new file mode 100644 index 000000000..e0abee9b8 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.c @@ -0,0 +1,817 @@ +/** + * \file + * + * \brief SAM SERCOM USART Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#include "usart.h" +#include <pinmux.h> +#if USART_CALLBACK_MODE == true +# include "usart_interrupt.h" +#endif + +/** + * \internal + * Set Configuration of the USART module + */ +static enum status_code _usart_set_config( + struct usart_module *const module, + const struct usart_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Index for generic clock */ + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Cache new register values to minimize the number of register writes */ + uint32_t ctrla = 0; + uint32_t ctrlb = 0; +#ifdef FEATURE_USART_ISO7816 + uint32_t ctrlc = 0; +#endif + uint16_t baud = 0; + uint32_t transfer_mode; + + enum sercom_asynchronous_operation_mode mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + enum sercom_asynchronous_sample_num sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + +#ifdef FEATURE_USART_OVER_SAMPLE + switch (config->sample_rate) { + case USART_SAMPLE_RATE_16X_ARITHMETIC: + mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + break; + case USART_SAMPLE_RATE_8X_ARITHMETIC: + mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_8; + break; + case USART_SAMPLE_RATE_3X_ARITHMETIC: + mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_3; + break; + case USART_SAMPLE_RATE_16X_FRACTIONAL: + mode = SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + break; + case USART_SAMPLE_RATE_8X_FRACTIONAL: + mode = SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL; + sample_num = SERCOM_ASYNC_SAMPLE_NUM_8; + break; + } +#endif + + /* Set data order, internal muxing, and clock polarity */ + ctrla = (uint32_t)config->data_order | + (uint32_t)config->mux_setting | + #ifdef FEATURE_USART_OVER_SAMPLE + config->sample_adjustment | + config->sample_rate | + #endif + #ifdef FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION + (config->immediate_buffer_overflow_notification << SERCOM_USART_CTRLA_IBON_Pos) | + #endif + (config->clock_polarity_inverted << SERCOM_USART_CTRLA_CPOL_Pos); + + enum status_code status_code = STATUS_OK; + + transfer_mode = (uint32_t)config->transfer_mode; +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + transfer_mode = config->iso7816_config.protocol_t; + } +#endif + /* Get baud value from mode and clock */ +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + baud = config->baudrate; + } else { +#endif + switch (transfer_mode) + { + case USART_TRANSFER_SYNCHRONOUSLY: + if (!config->use_external_clock) { + status_code = _sercom_get_sync_baud_val(config->baudrate, + system_gclk_chan_get_hz(gclk_index), &baud); + } + + break; + + case USART_TRANSFER_ASYNCHRONOUSLY: + if (config->use_external_clock) { + status_code = + _sercom_get_async_baud_val(config->baudrate, + config->ext_clock_freq, &baud, mode, sample_num); + } else { + status_code = + _sercom_get_async_baud_val(config->baudrate, + system_gclk_chan_get_hz(gclk_index), &baud, mode, sample_num); + } + + break; + } + + /* Check if calculating the baudrate failed */ + if (status_code != STATUS_OK) { + /* Abort */ + return status_code; + } +#ifdef FEATURE_USART_ISO7816 + } +#endif + +#ifdef FEATURE_USART_IRDA + if(config->encoding_format_enable) { + usart_hw->RXPL.reg = config->receive_pulse_length; + } +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /*Set baud val */ + usart_hw->BAUD.reg = baud; + + /* Set sample mode */ + ctrla |= transfer_mode; + + if (config->use_external_clock == false) { + ctrla |= SERCOM_USART_CTRLA_MODE(0x1); + } + else { + ctrla |= SERCOM_USART_CTRLA_MODE(0x0); + } + + /* Set stopbits and enable transceivers */ + ctrlb = + #ifdef FEATURE_USART_IRDA + (config->encoding_format_enable << SERCOM_USART_CTRLB_ENC_Pos) | + #endif + #ifdef FEATURE_USART_START_FRAME_DECTION + (config->start_frame_detection_enable << SERCOM_USART_CTRLB_SFDE_Pos) | + #endif + #ifdef FEATURE_USART_COLLISION_DECTION + (config->collision_detection_enable << SERCOM_USART_CTRLB_COLDEN_Pos) | + #endif + (config->receiver_enable << SERCOM_USART_CTRLB_RXEN_Pos) | + (config->transmitter_enable << SERCOM_USART_CTRLB_TXEN_Pos); + +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + ctrla |= SERCOM_USART_CTRLA_FORM(0x07); + if (config->iso7816_config.enable_inverse) { + ctrla |= SERCOM_USART_CTRLA_TXINV | SERCOM_USART_CTRLA_RXINV; + } + ctrlb |= USART_CHARACTER_SIZE_8BIT; + + switch(config->iso7816_config.protocol_t) { + case ISO7816_PROTOCOL_T_0: + ctrlb |= (uint32_t)config->stopbits; + ctrlc |= SERCOM_USART_CTRLC_GTIME(config->iso7816_config.guard_time) | \ + (config->iso7816_config.inhibit_nack) | \ + (config->iso7816_config.successive_recv_nack) | \ + SERCOM_USART_CTRLC_MAXITER(config->iso7816_config.max_iterations); + break; + case ISO7816_PROTOCOL_T_1: + ctrlb |= USART_STOPBITS_1; + break; + } + } else { +#endif + ctrlb |= (uint32_t)config->character_size; + /* Check parity mode bits */ + if (config->parity != USART_PARITY_NONE) { + ctrla |= SERCOM_USART_CTRLA_FORM(1); + ctrlb |= config->parity; + } else { +#ifdef FEATURE_USART_LIN_SLAVE + if(config->lin_slave_enable) { + ctrla |= SERCOM_USART_CTRLA_FORM(0x4); + } else { + ctrla |= SERCOM_USART_CTRLA_FORM(0); + } +#else + ctrla |= SERCOM_USART_CTRLA_FORM(0); +#endif + } +#ifdef FEATURE_USART_ISO7816 + } +#endif + +#ifdef FEATURE_USART_LIN_MASTER + usart_hw->CTRLC.reg = ((usart_hw->CTRLC.reg) & SERCOM_USART_CTRLC_GTIME_Msk) + | config->lin_header_delay + | config->lin_break_length; + + if (config->lin_node != LIN_INVALID_MODE) { + ctrla &= ~(SERCOM_USART_CTRLA_FORM(0xf)); + ctrla |= config->lin_node; + } +#endif + + /* Set whether module should run in standby. */ + if (config->run_in_standby || system_is_debugger_present()) { + ctrla |= SERCOM_USART_CTRLA_RUNSTDBY; + } + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Write configuration to CTRLB */ + usart_hw->CTRLB.reg = ctrlb; + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Write configuration to CTRLA */ + usart_hw->CTRLA.reg = ctrla; + +#ifdef FEATURE_USART_RS485 + if ((usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_FORM_Msk) != \ + SERCOM_USART_CTRLA_FORM(0x07)) { + usart_hw->CTRLC.reg &= ~(SERCOM_USART_CTRLC_GTIME(0x7)); + usart_hw->CTRLC.reg |= SERCOM_USART_CTRLC_GTIME(config->rs485_guard_time); + } +#endif + +#ifdef FEATURE_USART_ISO7816 + if(config->iso7816_config.enabled) { + _usart_wait_for_sync(module); + usart_hw->CTRLC.reg = ctrlc; + } +#endif + + return STATUS_OK; +} + +/** + * \brief Initializes the device + * + * Initializes the USART device based on the setting specified in the + * configuration struct. + * + * \param[out] module Pointer to USART device + * \param[in] hw Pointer to USART hardware instance + * \param[in] config Pointer to configuration struct + * + * \return Status of the initialization. + * + * \retval STATUS_OK The initialization was successful + * \retval STATUS_BUSY The USART module is busy + * resetting + * \retval STATUS_ERR_DENIED The USART has not been disabled in + * advance of initialization + * \retval STATUS_ERR_INVALID_ARG The configuration struct contains + * invalid configuration + * \retval STATUS_ERR_ALREADY_INITIALIZED The SERCOM instance has already been + * initialized with different clock + * configuration + * \retval STATUS_ERR_BAUD_UNAVAILABLE The BAUD rate given by the + * configuration + * struct cannot be reached with + * the current clock configuration + */ +enum status_code usart_init( + struct usart_module *const module, + Sercom *const hw, + const struct usart_config *const config) +{ + /* Sanity check arguments */ + Assert(module); + Assert(hw); + Assert(config); + + enum status_code status_code = STATUS_OK; + + /* Assign module pointer to software instance struct */ + module->hw = hw; + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); + uint32_t pm_index, gclk_index; +#if (SAML22) || (SAMC20) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#elif (SAML21) || (SAMR30) + if (sercom_index == 5) { + pm_index = MCLK_APBDMASK_SERCOM5_Pos; + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#elif (SAMC21) + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + + if (sercom_index == 5){ + gclk_index = SERCOM5_GCLK_ID_CORE; + } else { + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + } +#else + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif + + if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_SWRST) { + /* The module is busy resetting itself */ + return STATUS_BUSY; + } + + if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_ENABLE) { + /* Check the module is enabled */ + return STATUS_ERR_DENIED; + } + + /* Turn on module in PM */ +#if (SAML21) || (SAMR30) + if (sercom_index == 5) { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); + } else { + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + } +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); +#endif + + /* Set up the GCLK for the module */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->generator_source; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(config->generator_source, false); + + /* Set character size */ + module->character_size = config->character_size; + + /* Set transmitter and receiver status */ + module->receiver_enabled = config->receiver_enable; + module->transmitter_enabled = config->transmitter_enable; + +#ifdef FEATURE_USART_LIN_SLAVE + module->lin_slave_enabled = config->lin_slave_enable; +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + module->start_frame_detection_enabled = config->start_frame_detection_enable; +#endif +#ifdef FEATURE_USART_ISO7816 + module->iso7816_mode_enabled = config->iso7816_config.enabled; +#endif + /* Set configuration according to the config struct */ + status_code = _usart_set_config(module, config); + if(status_code != STATUS_OK) { + return status_code; + } + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; + + uint32_t pad_pinmuxes[] = { + config->pinmux_pad0, config->pinmux_pad1, + config->pinmux_pad2, config->pinmux_pad3 + }; + + /* Configure the SERCOM pins according to the user configuration */ + for (uint8_t pad = 0; pad < 4; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(hw, pad); + } + + if (current_pinmux != PINMUX_UNUSED) { + pin_conf.mux_position = current_pinmux & 0xFFFF; + system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); + } + } + +#if USART_CALLBACK_MODE == true + /* Initialize parameters */ + for (uint32_t i = 0; i < USART_CALLBACK_N; i++) { + module->callback[i] = NULL; + } + + module->tx_buffer_ptr = NULL; + module->rx_buffer_ptr = NULL; + module->remaining_tx_buffer_length = 0x0000; + module->remaining_rx_buffer_length = 0x0000; + module->callback_reg_mask = 0x00; + module->callback_enable_mask = 0x00; + module->rx_status = STATUS_OK; + module->tx_status = STATUS_OK; + + /* Set interrupt handler and register USART software module struct in + * look-up table */ + uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); + _sercom_set_handler(instance_index, _usart_interrupt_handler); + _sercom_instances[instance_index] = module; +#endif + + return status_code; +} + +/** + * \brief Transmit a character via the USART + * + * This blocking function will transmit a single character via the + * USART. + * + * \param[in] module Pointer to the software instance struct + * \param[in] tx_data Data to transfer + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_BUSY If the operation was not completed, due to the USART + * module being busy + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_wait( + struct usart_module *const module, + const uint16_t tx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + +#if USART_CALLBACK_MODE == true + /* Check if the USART is busy doing asynchronous operation. */ + if (module->remaining_tx_buffer_length > 0) { + return STATUS_BUSY; + } + +#else + /* Check if USART is ready for new data */ + if (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)) { + /* Return error code */ + return STATUS_BUSY; + } +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Write data to USART module */ + usart_hw->DATA.reg = tx_data; + + while (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC)) { + /* Wait until data is sent */ + } + + return STATUS_OK; +} + +/** + * \brief Receive a character via the USART + * + * This blocking function will receive a character via the USART. + * + * \param[in] module Pointer to the software instance struct + * \param[out] rx_data Pointer to received data + * + * \return Status of the operation. + * \retval STATUS_OK If the operation was completed + * \retval STATUS_BUSY If the operation was not completed, + * due to the USART module being busy + * \retval STATUS_ERR_BAD_FORMAT If the operation was not completed, + * due to configuration mismatch between USART + * and the sender + * \retval STATUS_ERR_BAD_OVERFLOW If the operation was not completed, + * due to the baudrate being too low or the + * system frequency being too high + * \retval STATUS_ERR_BAD_DATA If the operation was not completed, due to + * data being corrupted + * \retval STATUS_ERR_DENIED If the receiver is not enabled + */ +enum status_code usart_read_wait( + struct usart_module *const module, + uint16_t *const rx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Error variable */ + uint8_t error_code; + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Check that the receiver is enabled */ + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + +#if USART_CALLBACK_MODE == true + /* Check if the USART is busy doing asynchronous operation. */ + if (module->remaining_rx_buffer_length > 0) { + return STATUS_BUSY; + } +#endif + + /* Check if USART has new data */ + if (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC)) { + /* Return error code */ + return STATUS_BUSY; + } + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Read out the status code and mask away all but the 3 LSBs*/ + error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK); + + /* Check if an error has occurred during the receiving */ + if (error_code) { + /* Check which error occurred */ + if (error_code & SERCOM_USART_STATUS_FERR) { + /* Clear flag by writing a 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_FERR; + + return STATUS_ERR_BAD_FORMAT; + } else if (error_code & SERCOM_USART_STATUS_BUFOVF) { + /* Clear flag by writing a 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF; + + return STATUS_ERR_OVERFLOW; + } else if (error_code & SERCOM_USART_STATUS_PERR) { + /* Clear flag by writing a 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR; + + return STATUS_ERR_BAD_DATA; + } +#ifdef FEATURE_USART_LIN_SLAVE + else if (error_code & SERCOM_USART_STATUS_ISF) { + /* Clear flag by writing 1 to it and + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_ISF; + + return STATUS_ERR_PROTOCOL; + } +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + else if (error_code & SERCOM_USART_STATUS_COLL) { + /* Clear flag by writing 1 to it + * return with an error code */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_COLL; + + return STATUS_ERR_PACKET_COLLISION; + } +#endif + } + + /* Read data from USART module */ + *rx_data = usart_hw->DATA.reg; + + return STATUS_OK; +} + +/** + * \brief Transmit a buffer of characters via the USART + * + * This blocking function will transmit a block of \c length characters + * via the USART. + * + * \note Using this function in combination with the interrupt (\c _job) functions is + * not recommended as it has no functionality to check if there is an + * ongoing interrupt driven operation running or not. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Pointer to data to transmit + * \param[in] length Number of characters to transmit + * + * \note If using 9-bit data, the array that *tx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define TX_LEN 3 + uint16_t tx_buf[TX_LEN] = {0x0111, 0x0022, 0x0133}; + usart_write_buffer_wait(&module, (uint8_t*)tx_buf, TX_LEN); + \endcode + * + * \return Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid + * arguments + * \retval STATUS_ERR_TIMEOUT If operation was not completed, due to USART + * module timing out + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_buffer_wait( + struct usart_module *const module, + const uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Check if the buffer length is valid */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + uint16_t tx_pos = 0; + + /* Blocks while buffer is being transferred */ + while (length--) { + /* Wait for the USART to be ready for new data and abort + * operation if it doesn't get ready within the timeout*/ + for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE) { + break; + } else if (i == USART_TIMEOUT) { + return STATUS_ERR_TIMEOUT; + } + } + + /* Data to send is at least 8 bits long */ + uint16_t data_to_send = tx_data[tx_pos++]; + + /* Check if the character size exceeds 8 bit */ + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + data_to_send |= (tx_data[tx_pos++] << 8); + } + + /* Send the data through the USART module */ + usart_write_wait(module, data_to_send); + } + + /* Wait until Transmit is complete or timeout */ + for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) { + break; + } else if (i == USART_TIMEOUT) { + return STATUS_ERR_TIMEOUT; + } + } + + return STATUS_OK; +} + +/** + * \brief Receive a buffer of \c length characters via the USART + * + * This blocking function will receive a block of \c length characters + * via the USART. + * + * \note Using this function in combination with the interrupt (\c *_job) + * functions is not recommended as it has no functionality to check if + * there is an ongoing interrupt driven operation running or not. + * + * \param[in] module Pointer to USART software instance struct + * \param[out] rx_data Pointer to receive buffer + * \param[in] length Number of characters to receive + * + * \note If using 9-bit data, the array that *rx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define RX_LEN 3 + uint16_t rx_buf[RX_LEN] = {0x0,}; + usart_read_buffer_wait(&module, (uint8_t*)rx_buf, RX_LEN); + \endcode + * + * \return Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to an + * invalid argument being supplied + * \retval STATUS_ERR_TIMEOUT If operation was not completed, due + * to USART module timing out + * \retval STATUS_ERR_BAD_FORMAT If the operation was not completed, + * due to a configuration mismatch + * between USART and the sender + * \retval STATUS_ERR_BAD_OVERFLOW If the operation was not completed, + * due to the baudrate being too low or the + * system frequency being too high + * \retval STATUS_ERR_BAD_DATA If the operation was not completed, due + * to data being corrupted + * \retval STATUS_ERR_DENIED If the receiver is not enabled + */ +enum status_code usart_read_buffer_wait( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Check if the buffer length is valid */ + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the receiver is enabled */ + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + uint16_t rx_pos = 0; + + /* Blocks while buffer is being received */ + while (length--) { + /* Wait for the USART to have new data and abort operation if it + * doesn't get ready within the timeout*/ + for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC) { + break; + } else if (i == USART_TIMEOUT) { + return STATUS_ERR_TIMEOUT; + } + } + + enum status_code retval; + uint16_t received_data = 0; + + retval = usart_read_wait(module, &received_data); + + if (retval != STATUS_OK) { + /* Overflow, abort */ + return retval; + } + + /* Read value will be at least 8-bits long */ + rx_data[rx_pos++] = received_data; + + /* If 9-bit data, write next received byte to the buffer */ + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + rx_data[rx_pos++] = (received_data >> 8); + } + } + + return STATUS_OK; +} diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart.h b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.h new file mode 100644 index 000000000..ac79c1116 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart.h @@ -0,0 +1,1593 @@ +/** + * + * \file + * + * \brief SAM SERCOM USART Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#ifndef USART_H_INCLUDED +#define USART_H_INCLUDED + +/** + * \defgroup asfdoc_sam0_sercom_usart_group SAM Serial USART (SERCOM USART) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides + * an interface for the configuration and management of the SERCOM module in + * its USART mode to transfer or receive USART data frames. The following driver + * API modes are covered by this manual: + * + * - Polled APIs + * \if USART_CALLBACK_MODE + * - Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - SERCOM (Serial Communication Interface) + * + * The following devices can use this module: + * - Atmel | SMART SAM D20/D21 + * - Atmel | SMART SAM R21 + * - Atmel | SMART SAM D09/D10/D11 + * - Atmel | SMART SAM D10/D11 + * - Atmel | SMART SAM L21/L22 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C20/C21 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_sercom_usart_prerequisites + * - \ref asfdoc_sam0_sercom_usart_overview + * - \ref asfdoc_sam0_sercom_usart_special_considerations + * - \ref asfdoc_sam0_sercom_usart_extra_info + * - \ref asfdoc_sam0_sercom_usart_examples + * - \ref asfdoc_sam0_sercom_usart_api_overview + * + * \section asfdoc_sam0_sercom_usart_prerequisites Prerequisites + * + * To use the USART you need to have a GCLK generator enabled and running + * that can be used as the SERCOM clock source. This can either be configured + * in conf_clocks.h or by using the system clock driver. + * + * \section asfdoc_sam0_sercom_usart_overview Module Overview + * + * This driver will use one (or more) SERCOM interface(s) in the system + * and configure it to run as a USART interface in either synchronous + * or asynchronous mode. + * + * \subsection asfdoc_sam0_sercom_usart_features Driver Feature Macro Definition + * <table> + * <tr> + * <th>Driver Feature Macro</th> + * <th>Supported devices</th> + * </tr> + * <tr> + * <td>FEATURE_USART_SYNC_SCHEME_V2</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_OVER_SAMPLE</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_HARDWARE_FLOW_CONTROL</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_IRDA</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_LIN_SLAVE</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_COLLISION_DECTION</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_START_FRAME_DECTION</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION</td> + * <td>SAM D21/R21/D09/D10/D11/L21/L22/DA1/C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_RS485</td> + * <td>SAM C20/C21</td> + * </tr> + * <tr> + * <td>FEATURE_USART_LIN_MASTER</td> + * <td>SAM L22/C20/C21</td> + * </tr> + * </table> + * \note The specific features are only available in the driver when the + * selected device supports those features. + * + * \subsection asfdoc_sam0_sercom_usart_overview_frame_format Frame Format + * + * Communication is based on frames, where the frame format can be customized + * to accommodate a wide range of standards. A frame consists of a start bit, + * a number of data bits, an optional parity bit for error detection as well + * as a configurable length stop bit(s) - see + * \ref asfdoc_sam0_sercom_usart_frame_diagram "the figure below". + * \ref asfdoc_sam0_sercom_usart_frame_params "The table below" shows the + * available parameters you can change in a frame. + * + * \anchor asfdoc_sam0_sercom_usart_frame_params + * <table> + * <caption>USART Frame Parameters</caption> + * <tr> + * <th>Parameter</th> + * <th>Options</th> + * </tr> + * <tr> + * <td>Start bit</td> + * <td>1</td> + * </tr> + * <tr> + * <td>Data bits</td> + * <td>5, 6, 7, 8, 9</td> + * </tr> + * <tr> + * <td>Parity bit</td> + * <td>None, Even, Odd</td> + * </tr> + * <tr> + * <td>Stop bits</td> + * <td>1, 2</td> + * </tr> + * </table> + * + * \anchor asfdoc_sam0_sercom_usart_frame_diagram + * \image html usart_frame.svg "USART Frame Overview" width=100% + * + * \subsection asfdoc_sam0_sercom_usart_overview_sync Synchronous Mode + * + * In synchronous mode a dedicated clock line is provided; either by the USART + * itself if in master mode, or by an external master if in slave mode. + * Maximum transmission speed is the same as the GCLK clocking the USART + * peripheral when in slave mode, and the GCLK divided by two if in + * master mode. In synchronous mode the interface needs three lines to + * communicate: + * - TX (Transmit pin) + * - RX (Receive pin) + * - XCK (Clock pin) + * + * \subsubsection asfdoc_sam0_sercom_usart_overview_sync_sampling Data Sampling + * In synchronous mode the data is sampled on either the rising or falling edge + * of the clock signal. This is configured by setting the clock polarity in the + * configuration struct. + * + * \subsection asfdoc_sam0_sercom_usart_overview_async Asynchronous Mode + * + * In asynchronous mode no dedicated clock line is used, and the communication + * is based on matching the clock speed on the transmitter and receiver. The + * clock is generated from the internal SERCOM baudrate generator, and the + * frames are synchronized by using the frame start bits. Maximum transmission + * speed is limited to the SERCOM GCLK divided by 16. + * In asynchronous mode the interface only needs two lines to communicate: + * - TX (Transmit pin) + * - RX (Receive pin) + * + * \subsubsection asfdoc_sam0_sercom_usart_overview_async_clock_matching Transmitter/receiver Clock Matching + * + * For successful transmit and receive using the asynchronous mode the receiver + * and transmitter clocks needs to be closely matched. When receiving a frame + * that does not match the selected baudrate closely enough the receiver will + * be unable to synchronize the frame(s), and garbage transmissions will + * result. + * + * \subsection asfdoc_sam0_sercom_usart_parity Parity + * Parity can be enabled to detect if a transmission was in error. This is done + * by counting the number of "1" bits in the frame. When using even parity the + * parity bit will be set if the total number of "1"s in the frame are an even + * number. If using odd parity the parity bit will be set if the total number + * of "1"s are odd. + * + * When receiving a character the receiver will count the number of "1"s in the + * frame and give an error if the received frame and parity bit disagree. + * + * \subsection asfdoc_sam0_sercom_usart_overview_pin_configuration GPIO Configuration + * + * The SERCOM module has four internal pads; the RX pin can be placed freely on + * any one of the four pads, and the TX and XCK pins have two predefined + * positions that can be selected as a pair. The pads can then be routed to an + * external GPIO pin using the normal pin multiplexing scheme on the SAM. + * + * \section asfdoc_sam0_sercom_usart_special_considerations Special Considerations + * + * \if USART_CALLBACK_MODE + * Never execute large portions of code in the callbacks. These + * are run from the interrupt routine, and thus having long callbacks will + * keep the processor in the interrupt handler for an equally long time. + * A common way to handle this is to use global flags signaling the + * main application that an interrupt event has happened, and only do the + * minimal needed processing in the callback. + * \else + * No special considerations. + * \endif + * + * \section asfdoc_sam0_sercom_usart_extra_info Extra Information + * + * For extra information, see \ref asfdoc_sam0_sercom_usart_extra. This includes: + * - \ref asfdoc_sam0_sercom_usart_extra_acronyms + * - \ref asfdoc_sam0_sercom_usart_extra_dependencies + * - \ref asfdoc_sam0_sercom_usart_extra_errata + * - \ref asfdoc_sam0_sercom_usart_extra_history + * + * \section asfdoc_sam0_sercom_usart_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_sercom_usart_exqsg. + * + * \section asfdoc_sam0_sercom_usart_api_overview API Overview + * @{ + */ + +#include <compiler.h> +#include <sercom.h> +#include <pinmux.h> + +#if USART_CALLBACK_MODE == true +# include <sercom_interrupt.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name Driver Feature Definition + * Define SERCOM USART features set according to different device family. + * @{ + */ +#if (SAMD21) || (SAMR21) || (SAMD09) || (SAMD10) || (SAMD11) || (SAML21) || \ + (SAML22) ||(SAMDA1) || (SAMC20) || (SAMC21) || (SAMR30) || defined(__DOXYGEN__) +/** USART sync scheme version 2. */ +# define FEATURE_USART_SYNC_SCHEME_V2 +/** USART oversampling. */ +# define FEATURE_USART_OVER_SAMPLE +/** USART hardware control flow. */ +# define FEATURE_USART_HARDWARE_FLOW_CONTROL +/** IrDA mode. */ +# define FEATURE_USART_IRDA +/** LIN slave mode. */ +# define FEATURE_USART_LIN_SLAVE +/** USART collision detection. */ +# define FEATURE_USART_COLLISION_DECTION +/** USART start frame detection. */ +# define FEATURE_USART_START_FRAME_DECTION +/** USART start buffer overflow notification. */ +# define FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION +#endif + +#if (SAML22) || defined(__DOXYGEN__) +/** ISO7816 for smart card interfacing. */ +#define FEATURE_USART_ISO7816 +#endif +#if (SAMC20) || (SAMC21) || defined(__DOXYGEN__) +/** LIN master mode. */ +#define FEATURE_USART_LIN_MASTER +#endif +#if (SAML22) || (SAMC20) || (SAMC21) || defined(__DOXYGEN__) +/** RS485 mode. */ +# define FEATURE_USART_RS485 +#endif +/*@}*/ + +#ifdef FEATURE_USART_LIN_MASTER +/** + * \brief LIN node type + * + * LIN node type. + */ +enum lin_node_type { + /** LIN master mode */ + LIN_MASTER_NODE = SERCOM_USART_CTRLA_FORM(0x02), + /** LIN slave mode */ + LIN_SLAVE_NODE = SERCOM_USART_CTRLA_FORM(0x04), + /** Neither LIN master nor LIN slave mode */ + LIN_INVALID_MODE = SERCOM_USART_CTRLA_FORM(0x00), +}; + +/** + * \brief LIN master command enum + * + * LIN master command enum. + */ +enum lin_master_cmd { + /** LIN master software control transmission command */ + LIN_MASTER_SOFTWARE_CONTROL_TRANSMIT_CMD = SERCOM_USART_CTRLB_LINCMD(0x01), + /** LIN master automatically transmission command */ + LIN_MASTER_AUTO_TRANSMIT_CMD = SERCOM_USART_CTRLB_LINCMD(0x02), +}; + +/** + * \brief LIN master header delay + * + * LIN master header delay between break and sync transmission, + * and between the sync and identifier (ID) fields. + * This field is only valid when using automatically transmission command + */ +enum lin_master_header_delay { + /** Delay between break and sync transmission is 1 bit time. + Delay between sync and ID transmission is 1 bit time. */ + LIN_MASTER_HEADER_DELAY_0 = SERCOM_USART_CTRLC_HDRDLY(0x0), + /** Delay between break and sync transmission is 4 bit time. + Delay between sync and ID transmission is 4 bit time. */ + LIN_MASTER_HEADER_DELAY_1 = SERCOM_USART_CTRLC_HDRDLY(0x01), + /** Delay between break and sync transmission is 8 bit time. + Delay between sync and ID transmission is 4 bit time. */ + LIN_MASTER_HEADER_DELAY_2 = SERCOM_USART_CTRLC_HDRDLY(0x02), + /** Delay between break and sync transmission is 14 bit time. + Delay between sync and ID transmission is 4 bit time. */ + LIN_MASTER_HEADER_DELAY_3 = SERCOM_USART_CTRLC_HDRDLY(0x03), +}; + +/** + * \brief LIN master break length + * + * Length of the break field transmitted when in LIN master mode + */ +enum lin_master_break_length { + /** Break field transmission is 13 bit times */ + LIN_MASTER_BREAK_LENGTH_13_BIT = SERCOM_USART_CTRLC_BRKLEN(0x0), + /** Break field transmission is 17 bit times */ + LIN_MASTER_BREAK_LENGTH_17_BIT = SERCOM_USART_CTRLC_BRKLEN(0x1), + /** Break field transmission is 21 bit times */ + LIN_MASTER_BREAK_LENGTH_21_BIT = SERCOM_USART_CTRLC_BRKLEN(0x2), + /** Break field transmission is 26 bit times */ + LIN_MASTER_BREAK_LENGTH_26_BIT = SERCOM_USART_CTRLC_BRKLEN(0x3), +}; +#endif +#ifdef FEATURE_USART_ISO7816 +/** + * \brief ISO7816 protocol type + * + * ISO7816 protocol type. + */ +enum iso7816_protocol_type { + /** ISO7816 protocol type 0 */ + ISO7816_PROTOCOL_T_0 = SERCOM_USART_CTRLA_CMODE, + /** ISO7816 protocol type 1 */ + ISO7816_PROTOCOL_T_1 = (0x0ul << SERCOM_USART_CTRLA_CMODE_Pos), +}; + +/** + * \brief ISO7816 guard time + * + * The value of ISO7816 guard time. + */ +enum iso7816_guard_time { + /** The guard time is 2-bit times */ + ISO7816_GUARD_TIME_2_BIT = 2, + /** The guard time is 3-bit times */ + ISO7816_GUARD_TIME_3_BIT, + /** The guard time is 4-bit times */ + ISO7816_GUARD_TIME_4_BIT, + /** The guard time is 5-bit times */ + ISO7816_GUARD_TIME_5_BIT, + /** The guard time is 6-bit times */ + ISO7816_GUARD_TIME_6_BIT, + /** The guard time is 7-bit times */ + ISO7816_GUARD_TIME_7_BIT, +}; + +/** + * \brief ISO7816 receive NACK inhibit + * + * The value of ISO7816 receive NACK inhibit. + */ +enum iso7816_inhibit_nack { + /** The NACK is generated */ + ISO7816_INHIBIT_NACK_DISABLE = (0x0ul << SERCOM_USART_CTRLC_INACK_Pos), + /** The NACK is not generated */ + ISO7816_INHIBIT_NACK_ENABLE = SERCOM_USART_CTRLC_INACK, +}; + +/** + * \brief ISO7816 disable successive receive NACK + * + * The value of ISO7816 disable successive receive NACK. + */ +enum iso7816_successive_recv_nack { + /** The successive receive NACK is enable. */ + ISO7816_SUCCESSIVE_RECV_NACK_DISABLE = (0x0ul << SERCOM_USART_CTRLC_INACK_Pos), + /** The successive receive NACK is disable. */ + ISO7816_SUCCESSIVE_RECV_NACK_ENABLE = SERCOM_USART_CTRLC_DSNACK, +}; + +/** + * \brief ISO7816 configuration struct + * + * ISO7816 configuration structure. + */ +struct iso7816_config_t { + /* ISO7816 mode enable */ + bool enabled; + /** ISO7816 protocol type */ + enum iso7816_protocol_type protocol_t; + /** Enable inverse transmission and reception */ + bool enable_inverse; + /** Guard time, which lasts two bit times */ + enum iso7816_guard_time guard_time; + /** + * Inhibit Non Acknowledge: + * - 0: the NACK is generated; + * - 1: the NACK is not generated. + */ + enum iso7816_inhibit_nack inhibit_nack; + /** + * Disable successive NACKs. + * - 0: NACK is sent on the ISO line as soon as a parity error occurs + * in the received character. Successive parity errors are counted up to + * the value in the max_iterations field. These parity errors generate + * a NACK on the ISO line. As soon as this value is reached, no additional + * NACK is sent on the ISO line. The ITERATION flag is asserted. + */ + enum iso7816_successive_recv_nack successive_recv_nack; + /* Max number of repetitions */ + uint32_t max_iterations; +}; +#endif + +#ifndef PINMUX_DEFAULT +/** Default pinmux */ +# define PINMUX_DEFAULT 0 +#endif + +#ifndef PINMUX_UNUSED +/** Unused pinmux */ +# define PINMUX_UNUSED 0xFFFFFFFF +#endif + +#ifndef USART_TIMEOUT +/** USART timeout value */ +# define USART_TIMEOUT 0xFFFF +#endif + +#if USART_CALLBACK_MODE == true +/** + * \brief USART callback enum + * + * Callbacks for the Asynchronous USART driver. + */ +enum usart_callback { + /** Callback for buffer transmitted */ + USART_CALLBACK_BUFFER_TRANSMITTED, + /** Callback for buffer received */ + USART_CALLBACK_BUFFER_RECEIVED, + /** Callback for error */ + USART_CALLBACK_ERROR, +#ifdef FEATURE_USART_LIN_SLAVE + /** Callback for break character is received */ + USART_CALLBACK_BREAK_RECEIVED, +#endif +#ifdef FEATURE_USART_HARDWARE_FLOW_CONTROL + /** Callback for a change is detected on the CTS pin */ + USART_CALLBACK_CTS_INPUT_CHANGE, +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + /** Callback for a start condition is detected on the RxD line */ + USART_CALLBACK_START_RECEIVED, +#endif +# if !defined(__DOXYGEN__) + /** Number of available callbacks */ + USART_CALLBACK_N, +# endif +}; +#endif + +/** + * \brief USART Data Order enum + * + * The data order decides which MSB or LSB is shifted out first when data is + * transferred. + */ +enum usart_dataorder { + /** The MSB will be shifted out first during transmission, + * and shifted in first during reception */ + USART_DATAORDER_MSB = 0, + /** The LSB will be shifted out first during transmission, + * and shifted in first during reception */ + USART_DATAORDER_LSB = SERCOM_USART_CTRLA_DORD, +}; + +/** + * \brief USART Transfer mode enum + * + * Select USART transfer mode. + */ +enum usart_transfer_mode { + /** Transfer of data is done synchronously */ + USART_TRANSFER_SYNCHRONOUSLY = (SERCOM_USART_CTRLA_CMODE), + /** Transfer of data is done asynchronously */ + USART_TRANSFER_ASYNCHRONOUSLY = (0x0ul << SERCOM_USART_CTRLA_CMODE_Pos), +}; + +/** + * \brief USART Parity enum + * + * Select parity USART parity mode. + */ +enum usart_parity { + /** For odd parity checking, the parity bit will be set if number of + * ones being transferred is even */ + USART_PARITY_ODD = SERCOM_USART_CTRLB_PMODE, + + /** For even parity checking, the parity bit will be set if number of + * ones being received is odd */ + USART_PARITY_EVEN = 0, + + /** No parity checking will be executed, and there will be no parity bit + * in the received frame */ + USART_PARITY_NONE = 0xFF, +}; + +/** + * \brief USART signal MUX settings + * + * Set the functionality of the SERCOM pins. + * + * See \ref asfdoc_sam0_sercom_usart_mux_settings for a description of the + * various MUX setting options. + */ +enum usart_signal_mux_settings { +#ifdef FEATURE_USART_HARDWARE_FLOW_CONTROL + /** MUX setting RX_0_TX_0_XCK_1 */ + USART_RX_0_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_0_TX_2_XCK_3 */ + USART_RX_0_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_0_TX_0_RTS_2_CTS_3 */ + USART_RX_0_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(2)), + /** MUX setting RX_1_TX_0_XCK_1 */ + USART_RX_1_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_1_TX_2_XCK_3 */ + USART_RX_1_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_1_TX_0_RTS_2_CTS_3 */ + USART_RX_1_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(2)), + /** MUX setting RX_2_TX_0_XCK_1 */ + USART_RX_2_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_2_TX_2_XCK_3 */ + USART_RX_2_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_2_TX_0_RTS_2_CTS_3 */ + USART_RX_2_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(2)), + /** MUX setting RX_3_TX_0_XCK_1 */ + USART_RX_3_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(0)), + /** MUX setting RX_3_TX_2_XCK_3 */ + USART_RX_3_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(1)), + /** MUX setting USART_RX_3_TX_0_RTS_2_CTS_3 */ + USART_RX_3_TX_0_RTS_2_CTS_3 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(2)), +#ifdef FEATURE_USART_RS485 + /** MUX setting USART_RX_0_TX_0_XCK_1_TE_2 */ + USART_RX_0_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO(3)), + /** MUX setting USART_RX_1_TX_0_XCK_1_TE_2 */ + USART_RX_1_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO(3)), + /** MUX setting USART_RX_2_TX_0_XCK_1_TE_2 */ + USART_RX_2_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO(3)), + /** MUX setting USART_RX_3_TX_0_XCK_1_TE_2 */ + USART_RX_3_TX_0_XCK_1_TE_2 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(3)), +#endif +#else + /** MUX setting RX_0_TX_0_XCK_1 */ + USART_RX_0_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(0)), + /** MUX setting RX_0_TX_2_XCK_3 */ + USART_RX_0_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(0) | SERCOM_USART_CTRLA_TXPO), + /** MUX setting RX_1_TX_0_XCK_1 */ + USART_RX_1_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(1)), + /** MUX setting RX_1_TX_2_XCK_3 */ + USART_RX_1_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(1) | SERCOM_USART_CTRLA_TXPO), + /** MUX setting RX_2_TX_0_XCK_1 */ + USART_RX_2_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(2)), + /** MUX setting RX_2_TX_2_XCK_3 */ + USART_RX_2_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(2) | SERCOM_USART_CTRLA_TXPO), + /** MUX setting RX_3_TX_0_XCK_1 */ + USART_RX_3_TX_0_XCK_1 = (SERCOM_USART_CTRLA_RXPO(3)), + /** MUX setting RX_3_TX_2_XCK_3 */ + USART_RX_3_TX_2_XCK_3 = (SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO), +#endif +}; + +/** + * \brief USART Stop Bits enum + * + * Number of stop bits for a frame. + */ +enum usart_stopbits { + /** Each transferred frame contains one stop bit */ + USART_STOPBITS_1 = 0, + /** Each transferred frame contains two stop bits */ + USART_STOPBITS_2 = SERCOM_USART_CTRLB_SBMODE, +}; + +/** + * \brief USART Character Size + * + * Number of bits for the character sent in a frame. + */ +enum usart_character_size { + /** The char being sent in a frame is five bits long */ + USART_CHARACTER_SIZE_5BIT = SERCOM_USART_CTRLB_CHSIZE(5), + /** The char being sent in a frame is six bits long */ + USART_CHARACTER_SIZE_6BIT = SERCOM_USART_CTRLB_CHSIZE(6), + /** The char being sent in a frame is seven bits long */ + USART_CHARACTER_SIZE_7BIT = SERCOM_USART_CTRLB_CHSIZE(7), + /** The char being sent in a frame is eight bits long */ + USART_CHARACTER_SIZE_8BIT = SERCOM_USART_CTRLB_CHSIZE(0), + /** The char being sent in a frame is nine bits long */ + USART_CHARACTER_SIZE_9BIT = SERCOM_USART_CTRLB_CHSIZE(1), +}; + +#ifdef FEATURE_USART_OVER_SAMPLE +/** + * \brief USART Sample Rate + * + * The value of sample rate and baudrate generation mode. + */ +enum usart_sample_rate { + /** 16x over-sampling using arithmetic baudrate generation */ + USART_SAMPLE_RATE_16X_ARITHMETIC = SERCOM_USART_CTRLA_SAMPR(0), + /** 16x over-sampling using fractional baudrate generation */ + USART_SAMPLE_RATE_16X_FRACTIONAL = SERCOM_USART_CTRLA_SAMPR(1), + /** 8x over-sampling using arithmetic baudrate generation */ + USART_SAMPLE_RATE_8X_ARITHMETIC = SERCOM_USART_CTRLA_SAMPR(2), + /** 8x over-sampling using fractional baudrate generation */ + USART_SAMPLE_RATE_8X_FRACTIONAL = SERCOM_USART_CTRLA_SAMPR(3), + /** 3x over-sampling using arithmetic baudrate generation */ + USART_SAMPLE_RATE_3X_ARITHMETIC = SERCOM_USART_CTRLA_SAMPR(4), +}; + +/** + * \brief USART Sample Adjustment + * + * The value of sample number used for majority voting. + */ +enum usart_sample_adjustment { + /** The first, middle and last sample number used for majority voting is 7-8-9 */ + USART_SAMPLE_ADJUSTMENT_7_8_9 = SERCOM_USART_CTRLA_SAMPA(0), + /** The first, middle and last sample number used for majority voting is 9-10-11 */ + USART_SAMPLE_ADJUSTMENT_9_10_11 = SERCOM_USART_CTRLA_SAMPA(1), + /** The first, middle and last sample number used for majority voting is 11-12-13 */ + USART_SAMPLE_ADJUSTMENT_11_12_13 = SERCOM_USART_CTRLA_SAMPA(2), + /** The first, middle and last sample number used for majority voting is 13-14-15 */ + USART_SAMPLE_ADJUSTMENT_13_14_15 = SERCOM_USART_CTRLA_SAMPA(3), +}; +#endif + +#ifdef FEATURE_USART_RS485 +/** + * \brief RS485 Guard Time + * + * The value of RS485 guard time. + */ +enum rs485_guard_time { + /** The guard time is 0-bit time */ + RS485_GUARD_TIME_0_BIT = 0, + /** The guard time is 1-bit time */ + RS485_GUARD_TIME_1_BIT, + /** The guard time is 2-bit times */ + RS485_GUARD_TIME_2_BIT, + /** The guard time is 3-bit times */ + RS485_GUARD_TIME_3_BIT, + /** The guard time is 4-bit times */ + RS485_GUARD_TIME_4_BIT, + /** The guard time is 5-bit times */ + RS485_GUARD_TIME_5_BIT, + /** The guard time is 6-bit times */ + RS485_GUARD_TIME_6_BIT, + /** The guard time is 7-bit times */ + RS485_GUARD_TIME_7_BIT, +}; +#endif + +/** + * \brief USART Transceiver + * + * Select Receiver or Transmitter. + */ +enum usart_transceiver_type { + /** The parameter is for the Receiver */ + USART_TRANSCEIVER_RX, + /** The parameter is for the Transmitter */ + USART_TRANSCEIVER_TX, +}; + +/** + * \brief USART configuration struct + * + * Configuration options for USART. + */ +struct usart_config { + /** USART bit order (MSB or LSB first) */ + enum usart_dataorder data_order; + /** USART in asynchronous or synchronous mode */ + enum usart_transfer_mode transfer_mode; + /** USART parity */ + enum usart_parity parity; + /** Number of stop bits */ + enum usart_stopbits stopbits; + /** USART character size */ + enum usart_character_size character_size; + /** USART pin out */ + enum usart_signal_mux_settings mux_setting; +#ifdef FEATURE_USART_OVER_SAMPLE + /** USART sample rate */ + enum usart_sample_rate sample_rate; + /** USART sample adjustment */ + enum usart_sample_adjustment sample_adjustment; +#endif +#ifdef FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION + /** Controls when the buffer overflow status bit is asserted when a buffer overflow occurs */ + bool immediate_buffer_overflow_notification; +#endif +#ifdef FEATURE_USART_IRDA + /** Enable IrDA encoding format */ + bool encoding_format_enable; + /** The minimum pulse length required for a pulse to be accepted by the IrDA receiver */ + uint8_t receive_pulse_length; +#endif +#ifdef FEATURE_USART_LIN_SLAVE + /** Enable LIN Slave Support */ + bool lin_slave_enable; +#endif + +#ifdef FEATURE_USART_LIN_MASTER + /** LIN node type */ + enum lin_node_type lin_node; + /** LIN master header delay */ + enum lin_master_header_delay lin_header_delay; + /** LIN Master Break Length */ + enum lin_master_break_length lin_break_length; +#endif + +#ifdef FEATURE_USART_START_FRAME_DECTION + /** Enable start of frame dection */ + bool start_frame_detection_enable; +#endif +#ifdef FEATURE_USART_ISO7816 + /** Enable ISO7816 for smart card interfacing */ + struct iso7816_config_t iso7816_config; +#endif +#ifdef FEATURE_USART_RS485 + /** RS485 guard time */ + enum rs485_guard_time rs485_guard_time; +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + /** Enable collision dection */ + bool collision_detection_enable; +#endif + /** USART baudrate */ + uint32_t baudrate; + /** Enable receiver */ + bool receiver_enable; + /** Enable transmitter */ + bool transmitter_enable; + + /** USART Clock Polarity. + * If true, data changes on falling XCK edge and + * is sampled at rising edge. + * If false, data changes on rising XCK edge and + * is sampled at falling edge. + * */ + bool clock_polarity_inverted; + + /** States whether to use the external clock applied to the XCK pin. + * In synchronous mode the shift register will act directly on the XCK clock. + * In asynchronous mode the XCK will be the input to the USART hardware module. + */ + bool use_external_clock; + /** External clock frequency in synchronous mode. + * This must be set if \c use_external_clock is true. */ + uint32_t ext_clock_freq; + /** If true the USART will be kept running in Standby sleep mode */ + bool run_in_standby; + /** GCLK generator source */ + enum gclk_generator generator_source; + /** PAD0 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD0, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD0 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD0. + */ + uint32_t pinmux_pad0; + /** PAD1 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD1, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD1 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD1. + */ + uint32_t pinmux_pad1; + /** PAD2 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD2, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD2 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD2. + */ + uint32_t pinmux_pad2; + /** PAD3 pinmux. + * + * If current USARTx has several alternative multiplexing I/O pins for PAD3, then + * only one peripheral multiplexing I/O can be enabled for current USARTx PAD3 + * function. Make sure that no other alternative multiplexing I/O is associated + * with the same USARTx PAD3. + */ + uint32_t pinmux_pad3; +}; + +#if USART_CALLBACK_MODE == true +/** + * \brief USART module instance + * + * Forward Declaration for the device instance. + */ +struct usart_module; + +/** + * \brief USART callback type + * + * Type of the callback functions. + */ +typedef void (*usart_callback_t)(struct usart_module *const module); +#endif + +/** + * \brief SERCOM USART driver software device instance structure. + * + * SERCOM USART driver software instance structure, used to retain software + * state information of an associated hardware module instance. + * + * \note The fields of this structure should not be altered by the user + * application; they are reserved for module-internal use only. + */ +struct usart_module { +#if !defined(__DOXYGEN__) + /** Pointer to the hardware instance */ + Sercom *hw; + /** Module lock */ + volatile bool locked; + /** Character size of the data being transferred */ + enum usart_character_size character_size; + /** Receiver enabled */ + bool receiver_enabled; + /** Transmitter enabled */ + bool transmitter_enabled; +#ifdef FEATURE_USART_LIN_SLAVE + /** LIN Slave Support enabled */ + bool lin_slave_enabled; +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + /** Start of frame dection enabled */ + bool start_frame_detection_enabled; +#endif +#ifdef FEATURE_USART_ISO7816 + /** ISO7816 mode enable */ + bool iso7816_mode_enabled; +#endif +# if USART_CALLBACK_MODE == true + /** Array to store callback function pointers in */ + usart_callback_t callback[USART_CALLBACK_N]; + /** Buffer pointer to where the next received character will be put */ + volatile uint8_t *rx_buffer_ptr; + + /** Buffer pointer to where the next character will be transmitted from + **/ + volatile uint8_t *tx_buffer_ptr; + /** Remaining characters to receive */ + volatile uint16_t remaining_rx_buffer_length; + /** Remaining characters to transmit */ + volatile uint16_t remaining_tx_buffer_length; + /** Bit mask for callbacks registered */ + uint8_t callback_reg_mask; + /** Bit mask for callbacks enabled */ + uint8_t callback_enable_mask; + /** Holds the status of the ongoing or last read operation */ + volatile enum status_code rx_status; + /** Holds the status of the ongoing or last write operation */ + volatile enum status_code tx_status; +# endif +#endif +}; + + /** + * \name Lock/Unlock + * @{ + */ + +/** + * \brief Attempt to get lock on driver instance + * + * This function checks the instance's lock, which indicates whether or not it + * is currently in use, and sets the lock if it was not already set. + * + * The purpose of this is to enable exclusive access to driver instances, so + * that, e.g., transactions by different services will not interfere with each + * other. + * + * \param[in,out] module Pointer to the driver instance to lock + * + * \retval STATUS_OK If the module was locked + * \retval STATUS_BUSY If the module was already locked + */ +static inline enum status_code usart_lock( + struct usart_module *const module) +{ + enum status_code status; + + system_interrupt_enter_critical_section(); + + if (module->locked) { + status = STATUS_BUSY; + } else { + module->locked = true; + status = STATUS_OK; + } + + system_interrupt_leave_critical_section(); + + return status; +} + +/** + * \brief Unlock driver instance + * + * This function clears the instance lock, indicating that it is available for + * use. + * + * \param[in,out] module Pointer to the driver instance to lock + * + */ +static inline void usart_unlock(struct usart_module *const module) +{ + module->locked = false; +} + +/** @} */ + +/** + * \brief Check if peripheral is busy syncing registers across clock domains + * + * Return peripheral synchronization status. If doing a non-blocking + * implementation this function can be used to check the sync state and hold of + * any new actions until sync is complete. If this function is not run; the + * functions will block until the sync has completed. + * + * \param[in] module Pointer to peripheral module + * + * \return Peripheral sync status. + * + * \retval true Peripheral is busy syncing + * \retval false Peripheral is not busy syncing and can be read/written without + * stalling the bus + */ +static inline bool usart_is_syncing( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + SercomUsart *const usart_hw = &(module->hw->USART); + +#ifdef FEATURE_USART_SYNC_SCHEME_V2 + return (usart_hw->SYNCBUSY.reg); +#else + return (usart_hw->STATUS.reg & SERCOM_USART_STATUS_SYNCBUSY); +#endif +} + +#if !defined (__DOXYGEN__) +/** + * \internal + * Waits until synchronization is complete + */ +static inline void _usart_wait_for_sync( + const struct usart_module *const module) +{ + /* Sanity check */ + Assert(module); + + while (usart_is_syncing(module)) { + /* Wait until the synchronization is complete */ + } +} +#endif + +/** + * \brief Initializes the device to predefined defaults + * + * Initialize the USART device to predefined defaults: + * - 8-bit asynchronous USART + * - No parity + * - One stop bit + * - 9600 baud + * - Transmitter enabled + * - Receiver enabled + * - GCLK generator 0 as clock source + * - Default pin configuration + * + * The configuration struct will be updated with the default + * configuration. + * + * \param[in,out] config Pointer to configuration struct + */ +static inline void usart_get_config_defaults( + struct usart_config *const config) +{ + /* Sanity check arguments */ + Assert(config); + + /* Set default config in the config struct */ + config->data_order = USART_DATAORDER_LSB; + config->transfer_mode = USART_TRANSFER_ASYNCHRONOUSLY; + config->parity = USART_PARITY_NONE; + config->stopbits = USART_STOPBITS_1; + config->character_size = USART_CHARACTER_SIZE_8BIT; + config->baudrate = 9600; + config->receiver_enable = true; + config->transmitter_enable = true; + config->clock_polarity_inverted = false; + config->use_external_clock = false; + config->ext_clock_freq = 0; + config->mux_setting = USART_RX_1_TX_2_XCK_3; + config->run_in_standby = false; + config->generator_source = GCLK_GENERATOR_0; + config->pinmux_pad0 = PINMUX_DEFAULT; + config->pinmux_pad1 = PINMUX_DEFAULT; + config->pinmux_pad2 = PINMUX_DEFAULT; + config->pinmux_pad3 = PINMUX_DEFAULT; +#ifdef FEATURE_USART_OVER_SAMPLE + config->sample_adjustment = USART_SAMPLE_ADJUSTMENT_7_8_9; + config->sample_rate = USART_SAMPLE_RATE_16X_ARITHMETIC; +#endif +#ifdef FEATURE_USART_LIN_SLAVE + config->lin_slave_enable = false; +#endif + +#ifdef FEATURE_USART_LIN_MASTER + config->lin_node = LIN_INVALID_MODE; + config->lin_header_delay = LIN_MASTER_HEADER_DELAY_0; + config->lin_break_length = LIN_MASTER_BREAK_LENGTH_13_BIT; +#endif + +#ifdef FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION + config->immediate_buffer_overflow_notification = false; +#endif +#ifdef FEATURE_USART_START_FRAME_DECTION + config->start_frame_detection_enable = false; +#endif +#ifdef FEATURE_USART_IRDA + config->encoding_format_enable = false; + config->receive_pulse_length = 19; +#endif +#ifdef FEATURE_USART_ISO7816 + config->iso7816_config.enabled = false; + config->iso7816_config.guard_time = ISO7816_GUARD_TIME_2_BIT; + config->iso7816_config.protocol_t = ISO7816_PROTOCOL_T_0; + config->iso7816_config.enable_inverse = false; + config->iso7816_config.inhibit_nack = ISO7816_INHIBIT_NACK_DISABLE; + config->iso7816_config.successive_recv_nack = ISO7816_SUCCESSIVE_RECV_NACK_DISABLE; + config->iso7816_config.max_iterations = 7; +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + config->collision_detection_enable = false; +#endif +#ifdef FEATURE_USART_RS485 + config->rs485_guard_time = RS485_GUARD_TIME_0_BIT; +#endif +} + +enum status_code usart_init( + struct usart_module *const module, + Sercom *const hw, + const struct usart_config *const config); + +/** + * \brief Enable the module + * + * Enables the USART module. + * + * \param[in] module Pointer to USART software instance struct + */ +static inline void usart_enable( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + +#if USART_CALLBACK_MODE == true + /* Enable Global interrupt for module */ + system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Enable USART module */ + usart_hw->CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE; +} + +/** + * \brief Disable module + * + * Disables the USART module. + * + * \param[in] module Pointer to USART software instance struct + */ +static inline void usart_disable( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + +#if USART_CALLBACK_MODE == true + /* Disable Global interrupt for module */ + system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Disable USART module */ + usart_hw->CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE; +} + +/** + * \brief Resets the USART module + * + * Disables and resets the USART module. + * + * \param[in] module Pointer to the USART software instance struct + */ +static inline void usart_reset( + const struct usart_module *const module) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + usart_disable(module); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* Reset module */ + usart_hw->CTRLA.reg = SERCOM_USART_CTRLA_SWRST; +} + +/** + * \name Writing and Reading + * @{ + */ +enum status_code usart_write_wait( + struct usart_module *const module, + const uint16_t tx_data); + +enum status_code usart_read_wait( + struct usart_module *const module, + uint16_t *const rx_data); + +enum status_code usart_write_buffer_wait( + struct usart_module *const module, + const uint8_t *tx_data, + uint16_t length); + +enum status_code usart_read_buffer_wait( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length); +/** @} */ + +/** + * \name Enabling/Disabling Receiver and Transmitter + * @{ + */ + +/** + * \brief Enable Transceiver + * + * Enable the given transceiver. Either RX or TX. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transceiver type + */ +static inline void usart_enable_transceiver( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + switch (transceiver_type) { + case USART_TRANSCEIVER_RX: + /* Enable RX */ + usart_hw->CTRLB.reg |= SERCOM_USART_CTRLB_RXEN; + module->receiver_enabled = true; + break; + + case USART_TRANSCEIVER_TX: + /* Enable TX */ + usart_hw->CTRLB.reg |= SERCOM_USART_CTRLB_TXEN; + module->transmitter_enabled = true; + break; + } + _usart_wait_for_sync(module); +} + +/** + * \brief Disable Transceiver + * + * Disable the given transceiver (RX or TX). + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transceiver type + */ +static inline void usart_disable_transceiver( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + switch (transceiver_type) { + case USART_TRANSCEIVER_RX: + /* Disable RX */ + usart_hw->CTRLB.reg &= ~SERCOM_USART_CTRLB_RXEN; + module->receiver_enabled = false; + break; + + case USART_TRANSCEIVER_TX: + /* Disable TX */ + usart_hw->CTRLB.reg &= ~SERCOM_USART_CTRLB_TXEN; + module->transmitter_enabled = false; + break; + } +} + +/** @} */ + +#ifdef FEATURE_USART_LIN_MASTER +/** + * \name LIN Master Command and Status + * @{ + */ + +/** + * \brief Sending LIN command. + * + * Sending LIN command. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] cmd Cammand type + */ +static inline void lin_master_send_cmd( + struct usart_module *const module, + enum lin_master_cmd cmd) +{ + SercomUsart *const usart_hw = &(module->hw->USART); + _usart_wait_for_sync(module); + usart_hw->CTRLB.reg |= cmd; +} + +/** + * \brief Get LIN transmission status + * + * Get LIN transmission status. + * + * \param[in] module Pointer to USART software instance struct + * + * \return Status of LIN master transmission. + * \retval true Data transmission completed + * \retval false Transmission is ongoing + */ +static inline bool lin_master_transmission_status(struct usart_module *const module) +{ + SercomUsart *const usart_hw = &(module->hw->USART); + return ((usart_hw->STATUS.reg & SERCOM_USART_STATUS_TXE)? true:false); +} + +/** @} */ +#endif + +#ifdef __cplusplus +} +#endif + +/** @} */ + +/** +* \page asfdoc_sam0_sercom_usart_extra Extra Information for SERCOM USART Driver +* +* \section asfdoc_sam0_sercom_usart_extra_acronyms Acronyms +* +* Below is a table listing the acronyms used in this module, along with their +* intended meanings. +* +* <table> +* <tr> +* <th>Acronym</th> +* <th>Description</th> +* </tr> +* <tr> +* <td>SERCOM</td> +* <td>Serial Communication Interface</td> +* </tr> +* <tr> +* <td>USART</td> +* <td>Universal Synchronous and Asynchronous Serial Receiver and Transmitter</td> +* </tr> +* <tr> +* <td>LSB</td> +* <td>Least Significant Bit</td> +* </tr> +* <tr> +* <td>MSB</td> +* <td>Most Significant Bit</td> +* </tr> +* <tr> +* <td>DMA</td> +* <td>Direct Memory Access</td> +* </tr> +* </table> +* +* +* \section asfdoc_sam0_sercom_usart_extra_dependencies Dependencies +* This driver has the following dependencies: +* +* - \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" +* - \ref asfdoc_sam0_system_clock_group "System clock configuration" +* +* +* \section asfdoc_sam0_sercom_usart_extra_errata Errata +* There are no errata related to this driver. +* +* +* \section asfdoc_sam0_sercom_usart_extra_history Module History +* An overview of the module history is presented in the table below, with +* details on the enhancements and fixes made to the module since its first +* release. The current version of this corresponds to the newest version in +* the table. +* + * <table> + * <tr> + * <th>Changelog</th> + * </tr> + * <tr> + * <td>Added new feature as below: + * \li ISO7816 + * </tr> + * <tr> + * <td>Added new features as below: + * \li LIN master + * \li RS485 + * </tr> + * <tr> + * <td>Added new features as below: + * \li Oversample + * \li Buffer overflow notification + * \li Irda + * \li Lin slave + * \li Start frame detection + * \li Hardware flow control + * \li Collision detection + * \li DMA support </td> + * </tr> + * <tr> + * <td>\li Added new \c transmitter_enable and \c receiver_enable Boolean + * values to \c struct usart_config + * \li Altered \c usart_write_* and usart_read_* functions to abort with + * an error code if the relevant transceiver is not enabled + * \li Fixed \c usart_write_buffer_wait() and \c usart_read_buffer_wait() + * not aborting correctly when a timeout condition occurs</td> + * </tr> + * <tr> + * <td>Initial Release</td> + * </tr> + * </table> +*/ + +/** + * \page asfdoc_sam0_sercom_usart_exqsg Examples for SERCOM USART Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_sercom_usart_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_sercom_usart_basic_use_case + * \if USART_CALLBACK_MODE + * - \subpage asfdoc_sam0_sercom_usart_callback_use_case + * \endif + * - \subpage asfdoc_sam0_sercom_usart_dma_use_case + * - \subpage asfdoc_sam0_sercom_usart_lin_use_case + */ + +/** + * \page asfdoc_sam0_sercom_usart_mux_settings SERCOM USART MUX Settings + * + * The following lists the possible internal SERCOM module pad function + * assignments, for the four SERCOM pads when in USART mode. Note that this is + * in addition to the physical GPIO pin MUX of the device, and can be used in + * conjunction to optimize the serial data pin-out. + * + * When TX and RX are connected to the same pin, the USART will operate in + * half-duplex mode if both one transmitter and several receivers are enabled. + * + * \note When RX and XCK are connected to the same pin, the receiver must not + * be enabled if the USART is configured to use an external clock. + * + * + * <table> + * <tr> + * <th>MUX/Pad</th> + * <th>PAD 0</th> + * <th>PAD 1</th> + * <th>PAD 2</th> + * <th>PAD 3</th> + * </tr> + * <tr> + * <td>RX_0_TX_0_XCK_1</td> + * <td>TX / RX</td> + * <td>XCK</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td>RX_0_TX_2_XCK_3</td> + * <td>RX</td> + * <td>-</td> + * <td>TX</td> + * <td>XCK</td> + * </tr> + * <tr> + * <td>RX_1_TX_0_XCK_1</td> + * <td>TX</td> + * <td>RX / XCK</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td>RX_1_TX_2_XCK_3</td> + * <td>-</td> + * <td>RX</td> + * <td>TX</td> + * <td>XCK</td> + * </tr> + * <tr> + * <td>RX_2_TX_0_XCK_1</td> + * <td>TX</td> + * <td>XCK</td> + * <td>RX</td> + * <td>-</td> + * </tr> + * <tr> + * <td>RX_2_TX_2_XCK_3</td> + * <td>-</td> + * <td>-</td> + * <td>TX / RX</td> + * <td>XCK</td> + * </tr> + * <tr> + * <td>RX_3_TX_0_XCK_1</td> + * <td>TX</td> + * <td>XCK</td> + * <td>-</td> + * <td>RX</td> + * </tr> + * <tr> + * <td>RX_3_TX_2_XCK_3</td> + * <td>-</td> + * <td>-</td> + * <td>TX</td> + * <td>RX / XCK</td> + * </tr> + * </table> + * + * \page asfdoc_sam0_sercom_usart_document_revision_history Document Revision History + * + * <table> + * <tr> + * <th>Doc. Rev.</td> + * <th>Date</td> + * <th>Comments</td> + * </tr> + * <tr> + * <td>42118F</td> + * <td>12/2015</td> + * <td>Added support for SAM L21/L22, SAM DA1, SAM D09, SAMR30 and SAM C20/C21</td> + * </tr> + * <tr> + * <td>42118E</td> + * <td>12/2014</td> + * <td>Added support for SAM R21 and SAM D10/D11</td> + * </tr> + * <tr> + * <td>42118D</td> + * <td>01/2014</td> + * <td>Added support for SAM D21</td> + * </tr> + * <tr> + * <td>42118C</td> + * <td>10/2013</td> + * <td>Replaced the pad multiplexing documentation with a condensed table</td> + * </tr> + * <tr> + * <td>42118B</td> + * <td>06/2013</td> + * <td>Corrected documentation typos</td> + * </tr> + * <tr> + * <td>42118A</td> + * <td>06/2013</td> + * <td>Initial release</td> + * </tr> + * </table> + */ +#endif /* USART_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.c b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.c new file mode 100644 index 000000000..cc042894c --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.c @@ -0,0 +1,666 @@ +/** + * \file + * + * \brief SAM SERCOM USART Asynchronous Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "usart_interrupt.h" + +/** + * \internal + * Asynchronous write of a buffer with a given length + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Pointer to data to be transmitted + * \param[in] length Length of data buffer + * + */ +enum status_code _usart_write_buffer( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(tx_data); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + system_interrupt_enter_critical_section(); + + /* Check if the USART transmitter is busy */ + if (module->remaining_tx_buffer_length > 0) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } + + /* Write parameters to the device instance */ + module->remaining_tx_buffer_length = length; + + system_interrupt_leave_critical_section(); + + module->tx_buffer_ptr = tx_data; + module->tx_status = STATUS_BUSY; + + /* Enable the Data Register Empty Interrupt */ + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_DRE; + + return STATUS_OK; +} + +/** + * \internal + * Asynchronous read of a buffer with a given length + * + * \param[in] module Pointer to USART software instance struct + * \param[in] rx_data Pointer to data to be received + * \param[in] length Length of data buffer + * + */ +enum status_code _usart_read_buffer( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + Assert(rx_data); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + system_interrupt_enter_critical_section(); + + /* Check if the USART receiver is busy */ + if (module->remaining_rx_buffer_length > 0) { + system_interrupt_leave_critical_section(); + return STATUS_BUSY; + } + + /* Set length for the buffer and the pointer, and let + * the interrupt handler do the rest */ + module->remaining_rx_buffer_length = length; + + system_interrupt_leave_critical_section(); + + module->rx_buffer_ptr = rx_data; + module->rx_status = STATUS_BUSY; + + /* Enable the RX Complete Interrupt */ + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXC; + +#ifdef FEATURE_USART_LIN_SLAVE + /* Enable the break character is received Interrupt */ + if(module->lin_slave_enabled) { + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXBRK; + } +#endif + +#ifdef FEATURE_USART_START_FRAME_DECTION + /* Enable a start condition is detected Interrupt */ + if(module->start_frame_detection_enabled) { + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXS; + } +#endif + + return STATUS_OK; +} + +/** + * \brief Registers a callback + * + * Registers a callback function, which is implemented by the user. + * + * \note The callback must be enabled by \ref usart_enable_callback + * in order for the interrupt handler to call it when the conditions for + * the callback type are met. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_func Pointer to callback function + * \param[in] callback_type Callback type given by an enum + * + */ +void usart_register_callback( + struct usart_module *const module, + usart_callback_t callback_func, + enum usart_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(callback_func); + + /* Register callback function */ + module->callback[callback_type] = callback_func; + + /* Set the bit corresponding to the callback_type */ + module->callback_reg_mask |= (1 << callback_type); +} + +/** + * \brief Unregisters a callback + * + * Unregisters a callback function, which is implemented by the user. + * + * \param[in,out] module Pointer to USART software instance struct + * \param[in] callback_type Callback type given by an enum + * + */ +void usart_unregister_callback( + struct usart_module *const module, + enum usart_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Unregister callback function */ + module->callback[callback_type] = NULL; + + /* Clear the bit corresponding to the callback_type */ + module->callback_reg_mask &= ~(1 << callback_type); +} + +/** + * \brief Asynchronous write a single char + * + * Sets up the driver to write the data given. If registered and enabled, + * a callback function will be called when the transmit is completed. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Data to transfer + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_BUSY If operation was not completed, due to the + * USART module being busy + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_job( + struct usart_module *const module, + const uint16_t *tx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(tx_data); + + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Call internal write buffer function with length 1 */ + return _usart_write_buffer(module, (uint8_t *)tx_data, 1); +} + +/** + * \brief Asynchronous read a single char + * + * Sets up the driver to read data from the USART module to the data + * pointer given. If registered and enabled, a callback will be called + * when the receiving is completed. + * + * \param[in] module Pointer to USART software instance struct + * \param[out] rx_data Pointer to where received data should be put + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_BUSY If operation was not completed + */ +enum status_code usart_read_job( + struct usart_module *const module, + uint16_t *const rx_data) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + /* Call internal read buffer function with length 1 */ + return _usart_read_buffer(module, (uint8_t *)rx_data, 1); +} + +/** + * \brief Asynchronous buffer write + * + * Sets up the driver to write a given buffer over the USART. If registered and + * enabled, a callback function will be called. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] tx_data Pointer do data buffer to transmit + * \param[in] length Length of the data to transmit + * + * \note If using 9-bit data, the array that *tx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define TX_LEN 3 + uint16_t tx_buf[TX_LEN] = {0x0111, 0x0022, 0x0133}; + usart_write_buffer_job(&module, (uint8_t*)tx_buf, TX_LEN); + \endcode + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed successfully. + * \retval STATUS_BUSY If operation was not completed, due to the + * USART module being busy + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid + * arguments + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_write_buffer_job( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(tx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the transmitter is enabled */ + if (!(module->transmitter_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Issue internal asynchronous write */ + return _usart_write_buffer(module, tx_data, length); +} + +/** + * \brief Asynchronous buffer read + * + * Sets up the driver to read from the USART to a given buffer. If registered + * and enabled, a callback function will be called. + * + * \param[in] module Pointer to USART software instance struct + * \param[out] rx_data Pointer to data buffer to receive + * \param[in] length Data buffer length + * + * \note If using 9-bit data, the array that *rx_data point to should be defined + * as uint16_t array and should be casted to uint8_t* pointer. Because it + * is an address pointer, the highest byte is not discarded. For example: + * \code + #define RX_LEN 3 + uint16_t rx_buf[RX_LEN] = {0x0,}; + usart_read_buffer_job(&module, (uint8_t*)rx_buf, RX_LEN); + \endcode + * + * \returns Status of the operation. + * \retval STATUS_OK If operation was completed + * \retval STATUS_BUSY If operation was not completed, due to the + * USART module being busy + * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid + * arguments + * \retval STATUS_ERR_DENIED If the transmitter is not enabled + */ +enum status_code usart_read_buffer_job( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length) +{ + /* Sanity check arguments */ + Assert(module); + Assert(rx_data); + + if (length == 0) { + return STATUS_ERR_INVALID_ARG; + } + + /* Check that the receiver is enabled */ + if (!(module->receiver_enabled)) { + return STATUS_ERR_DENIED; + } + + /* Issue internal asynchronous read */ + return _usart_read_buffer(module, rx_data, length); +} + +/** + * \brief Cancels ongoing read/write operation + * + * Cancels the ongoing read/write operation modifying parameters in the + * USART software struct. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transfer type to cancel + */ +void usart_abort_job( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + Assert(module->hw); + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + switch(transceiver_type) { + case USART_TRANSCEIVER_RX: + /* Clear the interrupt flag in order to prevent the receive + * complete callback to fire */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXC; + + /* Clear the software reception buffer */ + module->remaining_rx_buffer_length = 0; + + break; + + case USART_TRANSCEIVER_TX: + /* Clear the interrupt flag in order to prevent the receive + * complete callback to fire */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_TXC; + + /* Clear the software reception buffer */ + module->remaining_tx_buffer_length = 0; + + break; + } +} + +/** + * \brief Get status from the ongoing or last asynchronous transfer operation + * + * Returns the error from a given ongoing or last asynchronous transfer operation. + * Either from a read or write transfer. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] transceiver_type Transfer type to check + * + * \return Status of the given job. + * \retval STATUS_OK No error occurred during the last transfer + * \retval STATUS_BUSY A transfer is ongoing + * \retval STATUS_ERR_BAD_DATA The last operation was aborted due to a + * parity error. The transfer could be affected + * by external noise + * \retval STATUS_ERR_BAD_FORMAT The last operation was aborted due to a + * frame error + * \retval STATUS_ERR_OVERFLOW The last operation was aborted due to a + * buffer overflow + * \retval STATUS_ERR_INVALID_ARG An invalid transceiver enum given + */ +enum status_code usart_get_job_status( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Variable for status code */ + enum status_code status_code; + + switch(transceiver_type) { + case USART_TRANSCEIVER_RX: + status_code = module->rx_status; + break; + + case USART_TRANSCEIVER_TX: + status_code = module->tx_status; + break; + + default: + status_code = STATUS_ERR_INVALID_ARG; + break; + } + + return status_code; +} + +/** + * \internal + * Handles interrupts as they occur, and it will run callback functions + * which are registered and enabled. + * + * \param[in] instance ID of the SERCOM instance calling the interrupt + * handler. + */ +void _usart_interrupt_handler( + uint8_t instance) +{ + /* Temporary variables */ + uint16_t interrupt_status; + uint16_t callback_status; + uint8_t error_code; + + + /* Get device instance from the look-up table */ + struct usart_module *module + = (struct usart_module *)_sercom_instances[instance]; + + /* Pointer to the hardware module instance */ + SercomUsart *const usart_hw + = &(module->hw->USART); + + /* Wait for the synchronization to complete */ + _usart_wait_for_sync(module); + + /* Read and mask interrupt flag register */ + interrupt_status = usart_hw->INTFLAG.reg; + interrupt_status &= usart_hw->INTENSET.reg; + callback_status = module->callback_reg_mask & + module->callback_enable_mask; + + /* Check if a DATA READY interrupt has occurred, + * and if there is more to transfer */ + if (interrupt_status & SERCOM_USART_INTFLAG_DRE) { + if (module->remaining_tx_buffer_length) { + /* Write value will be at least 8-bits long */ + uint16_t data_to_send = *(module->tx_buffer_ptr); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + data_to_send |= (*(module->tx_buffer_ptr) << 8); + /* Increment 8-bit pointer */ + (module->tx_buffer_ptr)++; + } + /* Write the data to send */ + usart_hw->DATA.reg = (data_to_send & SERCOM_USART_DATA_MASK); + + if (--(module->remaining_tx_buffer_length) == 0) { + /* Disable the Data Register Empty Interrupt */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE; + /* Enable Transmission Complete interrupt */ + usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_TXC; + + } + } else { + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE; + } + } + + /* Check if the Transmission Complete interrupt has occurred and + * that the transmit buffer is empty */ + if (interrupt_status & SERCOM_USART_INTFLAG_TXC) { + + /* Disable TX Complete Interrupt, and set STATUS_OK */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_TXC; + module->tx_status = STATUS_OK; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_BUFFER_TRANSMITTED)) { + (*(module->callback[USART_CALLBACK_BUFFER_TRANSMITTED]))(module); + } + } + + /* Check if the Receive Complete interrupt has occurred, and that + * there's more data to receive */ + if (interrupt_status & SERCOM_USART_INTFLAG_RXC) { + + if (module->remaining_rx_buffer_length) { + /* Read out the status code and mask away all but the 4 LSBs*/ + error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK); +#if !SAMD20 + /* CTS status should not be considered as an error */ + if(error_code & SERCOM_USART_STATUS_CTS) { + error_code &= ~SERCOM_USART_STATUS_CTS; + } +#endif +#ifdef FEATURE_USART_LIN_MASTER + /* TXE status should not be considered as an error */ + if(error_code & SERCOM_USART_STATUS_TXE) { + error_code &= ~SERCOM_USART_STATUS_TXE; + } +#endif + /* Check if an error has occurred during the receiving */ + if (error_code) { + /* Check which error occurred */ + if (error_code & SERCOM_USART_STATUS_FERR) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_BAD_FORMAT; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_FERR; + } else if (error_code & SERCOM_USART_STATUS_BUFOVF) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_OVERFLOW; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF; + } else if (error_code & SERCOM_USART_STATUS_PERR) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_BAD_DATA; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR; + } +#ifdef FEATURE_USART_LIN_SLAVE + else if (error_code & SERCOM_USART_STATUS_ISF) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_PROTOCOL; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_ISF; + } +#endif +#ifdef FEATURE_USART_COLLISION_DECTION + else if (error_code & SERCOM_USART_STATUS_COLL) { + /* Store the error code and clear flag by writing 1 to it */ + module->rx_status = STATUS_ERR_PACKET_COLLISION; + usart_hw->STATUS.reg = SERCOM_USART_STATUS_COLL; + } +#endif + + /* Run callback if registered and enabled */ + if (callback_status + & (1 << USART_CALLBACK_ERROR)) { + (*(module->callback[USART_CALLBACK_ERROR]))(module); + } + + } else { + + /* Read current packet from DATA register, + * increment buffer pointer and decrement buffer length */ + uint16_t received_data = (usart_hw->DATA.reg & SERCOM_USART_DATA_MASK); + + /* Read value will be at least 8-bits long */ + *(module->rx_buffer_ptr) = received_data; + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + /* 9-bit data, write next received byte to the buffer */ + *(module->rx_buffer_ptr) = (received_data >> 8); + /* Increment 8-bit pointer */ + module->rx_buffer_ptr += 1; + } + + /* Check if the last character have been received */ + if(--(module->remaining_rx_buffer_length) == 0) { + /* Disable RX Complete Interrupt, + * and set STATUS_OK */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC; + module->rx_status = STATUS_OK; + + /* Run callback if registered and enabled */ + if (callback_status + & (1 << USART_CALLBACK_BUFFER_RECEIVED)) { + (*(module->callback[USART_CALLBACK_BUFFER_RECEIVED]))(module); + } + } + } + } else { + /* This should not happen. Disable Receive Complete interrupt. */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC; + } + } + +#ifdef FEATURE_USART_HARDWARE_FLOW_CONTROL + if (interrupt_status & SERCOM_USART_INTFLAG_CTSIC) { + /* Disable interrupts */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_CTSIC; + /* Clear interrupt flag */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_CTSIC; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_CTS_INPUT_CHANGE)) { + (*(module->callback[USART_CALLBACK_CTS_INPUT_CHANGE]))(module); + } + } +#endif + +#ifdef FEATURE_USART_LIN_SLAVE + if (interrupt_status & SERCOM_USART_INTFLAG_RXBRK) { + /* Disable interrupts */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_RXBRK; + /* Clear interrupt flag */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXBRK; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_BREAK_RECEIVED)) { + (*(module->callback[USART_CALLBACK_BREAK_RECEIVED]))(module); + } + } +#endif + +#ifdef FEATURE_USART_START_FRAME_DECTION + if (interrupt_status & SERCOM_USART_INTFLAG_RXS) { + /* Disable interrupts */ + usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_RXS; + /* Clear interrupt flag */ + usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXS; + + /* Run callback if registered and enabled */ + if (callback_status & (1 << USART_CALLBACK_START_RECEIVED)) { + (*(module->callback[USART_CALLBACK_START_RECEIVED]))(module); + } + } +#endif +} + diff --git a/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.h b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.h new file mode 100644 index 000000000..26595d0a0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/sercom/usart/usart_interrupt.h @@ -0,0 +1,177 @@ +/** + * \file + * + * \brief SAM SERCOM USART Asynchronous Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#ifndef USART_INTERRUPT_H_INCLUDED +#define USART_INTERRUPT_H_INCLUDED + +#include "usart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__DOXYGEN__) +enum status_code _usart_write_buffer( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length); + +enum status_code _usart_read_buffer( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length); + +void _usart_interrupt_handler( + uint8_t instance); +#endif + +/** + * \addtogroup asfdoc_sam0_sercom_usart_group + * + * @{ + */ + +/** + * \name Callback Management + * @{ + */ +void usart_register_callback( + struct usart_module *const module, + usart_callback_t callback_func, + enum usart_callback callback_type); + +void usart_unregister_callback( + struct usart_module *module, + enum usart_callback callback_type); + +/** + * \brief Enables callback + * + * Enables the callback function registered by the \ref usart_register_callback. + * The callback function will be called from the interrupt handler when the + * conditions for the callback type are met. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void usart_enable_callback( + struct usart_module *const module, + enum usart_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Enable callback */ + module->callback_enable_mask |= (1 << callback_type); + +} + +/** + * \brief Disable callback + * + * Disables the callback function registered by the \ref usart_register_callback, + * and the callback will not be called from the interrupt routine. + * + * \param[in] module Pointer to USART software instance struct + * \param[in] callback_type Callback type given by an enum + */ +static inline void usart_disable_callback( + struct usart_module *const module, + enum usart_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module); + + /* Disable callback */ + module->callback_enable_mask &= ~(1 << callback_type); +} + +/** + * @} + */ + +/** + * \name Writing and Reading + * @{ + */ +enum status_code usart_write_job( + struct usart_module *const module, + const uint16_t *tx_data); + +enum status_code usart_read_job( + struct usart_module *const module, + uint16_t *const rx_data); + +enum status_code usart_write_buffer_job( + struct usart_module *const module, + uint8_t *tx_data, + uint16_t length); + +enum status_code usart_read_buffer_job( + struct usart_module *const module, + uint8_t *rx_data, + uint16_t length); + +void usart_abort_job( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type); + +enum status_code usart_get_job_status( + struct usart_module *const module, + enum usart_transceiver_type transceiver_type); +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* USART_INTERRUPT_H_INCLUDED */ + diff --git a/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c b/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c index d2611b3d5..c5561b316 100644 --- a/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c +++ b/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c @@ -263,7 +263,7 @@ void Reset_Handler(void) NVMCTRL->CTRLB.bit.MANW = 1; /* Initialize the C library */ - __libc_init_array(); + //__libc_init_array(); /* Branch to main function */ main(); diff --git a/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c b/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c index c8c7ae605..45859e862 100644 --- a/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c +++ b/atmel-samd/asf/sam0/utils/syscalls/gcc/syscalls.c @@ -109,10 +109,10 @@ extern int _lseek(int file, int ptr, int dir) return 0; } -extern void _exit(int status) -{ - asm("BKPT #0"); -} +// extern void _exit(int status) +// { +// asm("BKPT #0"); +// } extern void _kill(int pid, int sig) { |
