]*>(?:|[\S\n]*)?\[!(\w*)((?:\|[\w*:[\s\w\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]*)*?)\]([\s\S]*?)(?:<\/p>)?<\s*\/\s*blockquote>/g,function(t,e,a,l){!w[e.toLowerCase()]&&w.typeMappings[e.toLowerCase()]&&(e=w.typeMappings[e.toLowerCase()]);var o=w[e.toLowerCase()];if(!o)return t;var r,n=p(a,"style",w.style),i=p(a,"iconVisibility","visible",function(t){return"hidden"!==t}),c=p(a,"labelVisibility","visible",function(t){return"hidden"!==t}),d=p(a,"label",o.label),s=p(a,"icon",o.icon),g=p(a,"className",o.className);"object"===h(d)&&((r=Object.keys(d).filter(function(t){return-1'),u=''.concat(i?m:"").concat(c?d:"","
");return'\n ').concat(i||c?u:"","\n
").concat(l,"
\n
")}))})},window.$docsify.plugins)}();
+//# sourceMappingURL=docsify-plugin-flexible-alerts.min.js.map
diff --git a/_scripts/docsify-scroll-to-top.min.js b/_scripts/docsify-scroll-to-top.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..b54d5b69ce43b7be8e7df3d84af6f91df33c7652
--- /dev/null
+++ b/_scripts/docsify-scroll-to-top.min.js
@@ -0,0 +1 @@
+var CONFIG={auto:true,text:"Top",right:15,bottom:15,offset:500};var install=function(hook,vm){var opts=vm.config.scrollToTop||CONFIG;CONFIG.auto=opts.auto&&typeof opts.auto==="boolean"?opts.auto:CONFIG.auto;CONFIG.text=opts.text&&typeof opts.text==="string"?opts.text:CONFIG.text;CONFIG.right=opts.right&&typeof opts.right==="number"?opts.right:CONFIG.right;CONFIG.bottom=opts.bottom&&typeof opts.bottom==="number"?opts.bottom:CONFIG.bottom;CONFIG.offset=opts.offset&&typeof opts.offset==="number"?opts.offset:CONFIG.offset;var onScroll=function(e){if(!CONFIG.auto){return}var offset=window.document.documentElement.scrollTop;var $scrollBtn=Docsify.dom.find("span.scroll-to-top");$scrollBtn.style.display=offset>=CONFIG.offset?"block":"none"};hook.mounted(function(){var scrollBtn=document.createElement("span");scrollBtn.className="scroll-to-top";scrollBtn.style.display=CONFIG.auto?"none":"block";scrollBtn.style.overflow="hidden";scrollBtn.style.position="fixed";scrollBtn.style.right=CONFIG.right+"px";scrollBtn.style.bottom=CONFIG.bottom+"px";scrollBtn.style.width="50px";scrollBtn.style.height="50px";scrollBtn.style.background="#";scrollBtn.style.color="#34495E";scrollBtn.style.border="1px solid #ddd";scrollBtn.style.borderRadius="4px";scrollBtn.style.lineHeight="42px";scrollBtn.style.fontWeight="600";scrollBtn.style.fontSize="16px";scrollBtn.style.textAlign="center";scrollBtn.style.boxShadow="0px 0px 6px #000";scrollBtn.style.cursor="pointer";var textNode=document.createTextNode(CONFIG.text);scrollBtn.appendChild(textNode);document.body.appendChild(scrollBtn);window.addEventListener("scroll",onScroll);scrollBtn.onclick=function(e){e.stopPropagation();var step=window.scrollY/15;var scroll=function(){window.scrollTo(0,window.scrollY-step);if(window.scrollY>0){setTimeout(scroll,15)}};scroll()}})};$docsify.plugins=[].concat(install,$docsify.plugins);
\ No newline at end of file
diff --git a/_scripts/docsify-sidebar-collapse.min.js b/_scripts/docsify-sidebar-collapse.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..fb2d8506d21af413a2fddefa4effe07edc689f92
--- /dev/null
+++ b/_scripts/docsify-sidebar-collapse.min.js
@@ -0,0 +1 @@
+!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(e,n){void 0===n&&(n={});var t=n.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===t&&a.firstChild?a.insertBefore(o,a.firstChild):a.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}var t;function a(e){if(e&&null!=t){var n=e.getBoundingClientRect().top;document.querySelector(".sidebar").scrollBy(0,n-t)}}function n(){requestAnimationFrame(function(){var e=document.querySelector(".app-sub-sidebar > .active");if(e)for(e.parentNode.parentNode.querySelectorAll(".app-sub-sidebar").forEach(function(e){return e.classList.remove("open")});e.parentNode.classList.contains("app-sub-sidebar")&&!e.parentNode.classList.contains("open");)e.parentNode.classList.add("open"),e=e.parentNode})}function o(e){t=e.target.getBoundingClientRect().top;var n=s(e.target,"LI",2);n&&(n.classList.contains("open")?(n.classList.remove("open"),setTimeout(function(){n.classList.add("collapse")},0)):(!function(e){if(e)for(e.classList.remove("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.remove("open"),e=e.parentNode}(r()),i(n),setTimeout(function(){n.classList.remove("collapse")},0)),a(n))}function r(){var e=document.querySelector(".sidebar-nav .active");e||(e=s(document.querySelector('.sidebar-nav a[href="'.concat(decodeURIComponent(location.hash).replace(/ /gi,"%20"),'"]')),"LI",2))&&e.classList.add("active");return e}function i(e){if(e)for(e.classList.add("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.add("open"),e=e.parentNode}function s(e,n,t){if(e&&e.tagName===n)return e;for(var a=0;e;){if(t<++a)return;if(e.parentNode.tagName===n)return e.parentNode;e=e.parentNode}}e(".sidebar-nav > ul > li ul {\n display: none;\n}\n\n.app-sub-sidebar {\n display: none;\n}\n\n.app-sub-sidebar.open {\n display: block;\n}\n\n.sidebar-nav .open > ul:not(.app-sub-sidebar),\n.sidebar-nav .active:not(.collapse) > ul {\n display: block;\n}\n\n/* 抖动 */\n.sidebar-nav li.open:not(.collapse) > ul {\n display: block;\n}\n\n.active + ul.app-sub-sidebar {\n display: block;\n}\n"),document.addEventListener("DOMContentLoaded",function(){document.querySelector(".sidebar-nav").addEventListener("click",o)}),document.addEventListener("scroll",n);e("@media screen and (max-width: 768px) {\n /* 移动端适配 */\n .markdown-section {\n max-width: none;\n padding: 16px;\n }\n /* 改变原来按钮热区大小 */\n .sidebar-toggle {\n padding: 0 0 10px 10px;\n }\n /* my pin */\n .sidebar-pin {\n appearance: none;\n outline: none;\n position: fixed;\n bottom: 0;\n border: none;\n width: 40px;\n height: 40px;\n background: transparent;\n }\n}\n");var d="DOCSIFY_SIDEBAR_PIN_FLAG";function c(){var e=localStorage.getItem(d);e="true"===e,localStorage.setItem(d,!e),document.querySelector(".content").style.transform=e?document.querySelector(".sidebar").style.transform="translateX(0px)":document.querySelector(".sidebar").style.transform="translateX(300px)"}!function(){if(!(768)[\r\n]+([\s|\S]*?)[\r\n\s]+()/m,tabCommentMarkup:/[\r\n]*(\s*)[\r\n]+([\s\S]*?)[\r\n]*\s*(?=)/m},m={persist:!0,sync:!0,theme:"classic",tabComments:!0,tabHeadings:!0};function g(t,a){var o=1 --\x3e'),a="\n".concat(s,"\x3c!-- ").concat(u," --\x3e");for(var i=function(){var t=(f[2]||"[Tab]").trim(),a=(f[3]||"").trim();o=o.replace(f[0],function(){return["\n".concat(s,"\x3c!-- ").concat(u,' --\x3e"),"\n".concat(s,"\x3c!-- ").concat(u,' --\x3e'),"\n\n".concat(s).concat(a),"\n\n".concat(s,"\x3c!-- ").concat(u,"
--\x3e")].join("")})};null!==(f=(m.tabComments?p.tabCommentMarkup.exec(o):null)||(m.tabHeadings?p.tabHeadingMarkup.exec(o):null));)i()}o=(o=o.replace(r,function(){return t})).replace(n,function(){return a}),d=d.replace(b[0],function(){return o})};null!==(b=p.tabBlockMarkup.exec(d));)a();return t.forEach(function(t,a){d=d.replace(t,function(){return o[a]})}),d}(t)),t}),t.afterEach(function(t,a){o&&(t=function(o){for(var c,t=function(){var t=c[0],a=c[1]||"";o=o.replace(t,function(){return a})};null!==(c=p.commentReplaceMarkup.exec(o));)t();return o}(t)),a(t)}),t.doneEach(function(){var t,a,c,e;o&&(t=document.querySelector(".".concat(y.tabsContainer)),a=t?Array.apply(null,t.querySelectorAll(".".concat(y.tabBlock))):[],c=JSON.parse(sessionStorage.getItem(window.location.href))||{},e=JSON.parse(sessionStorage.getItem("*"))||[],s(),a.forEach(function(a,t){var o=a.querySelector(".".concat(y.tabButtonActive));o||(m.sync&&e.length&&(o=e.map(function(t){return a.querySelector(".".concat(y.tabButton,'[data-tab="').concat(t,'"]'))}).filter(function(t){return t})[0]),!o&&m.persist&&(o=a.querySelector(".".concat(y.tabButton,'[data-tab="').concat(c[t],'"]'))),(o=o||a.querySelector(".".concat(y.tabButton)))&&o.classList.add(y.tabButtonActive))}))}),t.mounted(function(){var t=document.querySelector(".".concat(y.tabsContainer));t&&t.addEventListener("click",function(t){g(t.target)}),window.addEventListener("hashchange",s,!1)})},window.$docsify.plugins||[])))}();
+//# sourceMappingURL=docsify-tabs.min.js.map
\ No newline at end of file
diff --git a/_scripts/docsify.min.js b/_scripts/docsify.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..13ee7221c1ea4aebc66dd5de0a2367dc45e0da19
--- /dev/null
+++ b/_scripts/docsify.min.js
@@ -0,0 +1 @@
+!function(){function s(n){var r=Object.create(null);return function(e){var t=c(e)?e:JSON.stringify(e);return r[t]||(r[t]=n(e))}}var a=s(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),l=Object.prototype.hasOwnProperty,f=Object.assign||function(e){for(var t=arguments,n=1;n/gm),Ve=$(/^data-[\-\w.\u00B7-\uFFFF]/),Xe=$(/^aria-[\-\w]+$/),Ke=$(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Qe=$(/^(?:\w+script|data):/i),Je=$(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g),et="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function tt(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t"+e;else{var r=Re(e,/^[\s]+/);n=r&&r[0]}var i=y?y.createHTML(e):e;if(o)try{t=(new m).parseFromString(i,"text/html")}catch(e){}if(s&&Ne(U,["title"]),!t||!t.documentElement){var a=(t=w.createHTMLDocument("")).body;a.parentNode.removeChild(a.parentNode.firstElementChild),a.outerHTML=i}return e&&n&&t.body.insertBefore(l.createTextNode(n),t.body.childNodes[0]||null),S.call(t,X?"html":"body")[0]}var F=Ye,z=Ge,O=Ve,M=Xe,N=Qe,P=Je,D=Ke,j=null,H=Ne({},[].concat(tt(De),tt(je),tt(He),tt(Ie),tt(qe))),I=null,q=Ne({},[].concat(tt(Ue),tt(Be),tt(Ze),tt(We))),U=null,B=null,Z=!0,W=!0,Y=!1,G=!1,V=!1,X=!1,K=!1,Q=!1,J=!1,ee=!1,te=!1,ne=!1,re=!0,ie=!0,ae=!1,oe={},se=Ne({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","plaintext","script","style","svg","template","thead","title","video","xmp"]),le=Ne({},["audio","video","img","source","image"]),ce=null,ue=Ne({},["alt","class","for","id","label","name","pattern","placeholder","summary","title","value","style","xmlns"]),pe=null,de=l.createElement("form");d.isSupported&&(function(){try{$('
$');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];_(n,"add","has-mask"),Q(i[1])||(a=re(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),W()}else _(n,"remove","show")},Xn._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1'}}();
diff --git a/_scripts/external-script.min.js b/_scripts/external-script.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..c4f80268250fb1d53efdcdf92aa6274195040f50
--- /dev/null
+++ b/_scripts/external-script.min.js
@@ -0,0 +1 @@
+!function(){function e(){for(var o=Docsify.dom.getNode("#main"),e=Docsify.dom.findAll(o,"script"),n=e.length;n--;){var i=e[n];if(i&&i.src){var t=document.createElement("script");Array.prototype.slice.call(i.attributes).forEach(function(o){t[o.name]=o.value}),i.parentNode.insertBefore(t,i),i.parentNode.removeChild(i)}}}window.$docsify.plugins=[].concat(function(o){o.doneEach(e)},window.$docsify.plugins)}();
diff --git a/_scripts/prism-c.min.js b/_scripts/prism-c.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..ec001aed714ba2622204bc466272c62845b97004
--- /dev/null
+++ b/_scripts/prism-c.min.js
@@ -0,0 +1 @@
+Prism.languages.c=Prism.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+/,lookbehind:!0},keyword:/\b(?:__attribute__|define|_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,function:/[a-z_]\w*(?=\s*\()/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/,number:/(?:\b0x(?:[\da-f]+\.?[\da-f]*|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+(?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],comment:Prism.languages.c.comment,directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}},constant:/\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/}),delete Prism.languages.c.boolean;
\ No newline at end of file
diff --git a/_scripts/search.min.js b/_scripts/search.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..f74474415d827d53d0f0ba3249774255b2a342bf
--- /dev/null
+++ b/_scripts/search.min.js
@@ -0,0 +1 @@
+!function(){var h={},f={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function l(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function p(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function u(r,e,i,o){void 0===e&&(e="");var s,n=window.marked.lexer(e),c=window.Docsify.slugify,d={};return n.forEach(function(e){if("heading"===e.type&&e.depth<=o){var n=function(e){void 0===e&&(e="");var a={};return{str:e=e&&e.replace(/^'/,"").replace(/'$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(a[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:a}}(e.text),t=n.str,a=n.config;s=a.id?i.toURL(r,{id:c(a.id)}):i.toURL(r,{id:c(l(e.text))}),d[s]={slug:s,title:t,body:""}}else{if(!s)return;d[s]?d[s].body?(e.text=p(e),d[s].body+="\n"+(e.text||"")):(e.text=p(e),d[s].body=d[s].body?d[s].body+e.text:e.text):d[s]={slug:s,title:"",body:""}}}),c.clear(),d}function c(e){var r=[],i=[];Object.keys(h).forEach(function(n){i=i.concat(Object.keys(h[n]).map(function(e){return h[n][e]}))});var o=(e=e.trim()).split(/[\s\-,\\/]+/);1!==o.length&&(o=[].concat(e,o));function n(e){var n=i[e],s=0,c="",d=n.title&&n.title.trim(),p=n.body&&n.body.trim(),t=n.slug||"";if(d&&(o.forEach(function(e){var n,t=new RegExp(e.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&"),"gi"),a=-1;if(n=d?d.search(t):-1,a=p?p.search(t):-1,0<=n||0<=a){s+=0<=n?3:0<=a?2:0,a<0&&(a=0);var r,i=0;i=0==(r=a<11?0:a-10)?70:a+e.length+60,p&&i>p.length&&(i=p.length);var o="..."+l(p).substring(r,i).replace(t,function(e){return''+e+""})+"...";c+=o}}),0\n\n'+e.title+"
\n"+e.content+"
\n\n"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=s||''+m+"
",d.hideOtherSidebarContent&&(r.classList.add("hide"),i.classList.add("hide"))}function a(e){d=e}function o(e,n){var t=n.router.parse().query.s;a(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n='\n \n ',t=Docsify.dom.create("div",n),a=Docsify.dom.find("aside");Docsify.dom.toggleClass(t,"search"),Docsify.dom.before(a,t)}(t),function(){var e,n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,"input"),a=Docsify.dom.find(n,".input-wrap");Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(t,"input",function(n){clearTimeout(e),e=setTimeout(function(e){return r(n.target.value.trim())},100)}),Docsify.dom.on(a,"click",function(e){"INPUT"!==e.target.tagName&&(t.value="",r())})}(),t&&setTimeout(function(e){return r(t)},500)}function s(e,n){a(e),function(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var a=Object.keys(e).filter(function(e){return-1=0;)n=n.replace(" "+t+" "," ");e.className=n.replace(/^\s+|\s+$/g,"")}},i=function(e){var n=t.createElement("div");return n.appendChild(t.createTextNode(e)),n.innerHTML},u=function(e){e.style.opacity="",e.style.display="block"},c=function(e){if(e&&!e.length)return u(e);for(var t=0;t0?setTimeout(o,t):e.style.display="none"});o()},h=function(n){if("function"==typeof MouseEvent){var o=new MouseEvent("click",{view:e,bubbles:!1,cancelable:!0});n.dispatchEvent(o)}else if(t.createEvent){var a=t.createEvent("MouseEvents");a.initEvent("click",!1,!1),n.dispatchEvent(a)}else t.createEventObject?n.fireEvent("onclick"):"function"==typeof n.onclick&&n.onclick()},b=function(t){"function"==typeof t.stopPropagation?(t.stopPropagation(),t.preventDefault()):e.event&&e.event.hasOwnProperty("cancelBubble")&&(e.event.cancelBubble=!0)};a.hasClass=r,a.addClass=s,a.removeClass=l,a.escapeHtml=i,a._show=u,a.show=c,a._hide=d,a.hide=f,a.isDescendant=p,a.getTopMargin=m,a.fadeIn=v,a.fadeOut=y,a.fireClick=h,a.stopEventPropagation=b},{}],5:[function(t,o,a){Object.defineProperty(a,"__esModule",{value:!0});var r=t("./handle-dom"),s=t("./handle-swal-dom"),l=function(t,o,a){var l=t||e.event,i=l.keyCode||l.which,u=a.querySelector("button.confirm"),c=a.querySelector("button.cancel"),d=a.querySelectorAll("button[tabindex]");if(-1!==[9,13,32,27].indexOf(i)){for(var f=l.target||l.srcElement,p=-1,m=0;m"),i.innerHTML=e.html?e.text:s.escapeHtml(e.text||"").split("\n").join("
"),e.text&&s.show(i),e.customClass)s.addClass(t,e.customClass),t.setAttribute("data-custom-class",e.customClass);else{var d=t.getAttribute("data-custom-class");s.removeClass(t,d),t.setAttribute("data-custom-class","")}if(s.hide(t.querySelectorAll(".sa-icon")),e.type&&!a.isIE8()){var f=function(){for(var o=!1,a=0;ao;o++)n=parseInt(e.substr(2*o,2),16),n=Math.round(Math.min(Math.max(0,n+n*t),255)).toString(16),a+=("00"+n).substr(n.length);return a};o.extend=a,o.hexToRgb=r,o.isIE8=s,o.logStr=l,o.colorLuminance=i},{}]},{},[1]),"function"==typeof define&&define.amd?define(function(){return sweetAlert}):"undefined"!=typeof module&&module.exports&&(module.exports=sweetAlert)}(window,document);
\ No newline at end of file
diff --git a/_scripts/zoom-image.min.js b/_scripts/zoom-image.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..8e713ce7361a02c3f9cdbcc0805a6a10f0639f12
--- /dev/null
+++ b/_scripts/zoom-image.min.js
@@ -0,0 +1 @@
+!function(){function t(e){return"IMG"===e.tagName}function w(e){return e&&1===e.nodeType}function L(e){return".svg"===(e.currentSrc||e.src).substr(-4).toLowerCase()}function p(e){try{return Array.isArray(e)?e.filter(t):function(e){return NodeList.prototype.isPrototypeOf(e)}(e)?[].slice.call(e).filter(t):w(e)?[e].filter(t):"string"==typeof e?[].slice.call(document.querySelectorAll(e)).filter(t):[]}catch(e){throw new TypeError("The provided selector is invalid.\nExpects a CSS selector, a Node element, a NodeList or an array.\nSee: https://github.com/francoischalifour/medium-zoom")}}function g(e,t){var o=H({bubbles:!1,cancelable:!1,detail:void 0},t);if("function"==typeof window.CustomEvent)return new CustomEvent(e,o);var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,o.bubbles,o.cancelable,o.detail),n}function v(e,t){function o(){for(var e=arguments,t=arguments.length,o=Array(t),n=0;nb.scrollOffset&&setTimeout(m,150)}}),window.addEventListener("resize",m);var f={open:n,close:m,toggle:r,update:function(e){var t=0
+
+- [文档在线提交说明](/README.md)
diff --git a/_styles/sidebar.min.css b/_styles/sidebar.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..2238a684cf99667049956d140fb904c5c96ac517
--- /dev/null
+++ b/_styles/sidebar.min.css
@@ -0,0 +1 @@
+.sidebar-nav li{position:relative;margin:0;cursor:pointer}.sidebar-nav ul:not(.app-sub-sidebar)>li:not(.file)::before{content:'';display:block;position:absolute;top:13px;left:-12px;height:6px;width:6px;border-right:2px solid #3f6b85;border-bottom:2px solid #3f6b85;transform:rotate(-45deg);transition:transform .1s}.sidebar-nav ul:not(.app-sub-sidebar)>li.open::before{transform:rotate(45deg)}.sidebar-nav ul:not(.app-sub-sidebar)>li.collapse::before{transform:rotate(-45deg)}
\ No newline at end of file
diff --git a/_styles/sweetalert.min.css b/_styles/sweetalert.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..f698e78dba302859dbcb251c1d916f3dbf452046
--- /dev/null
+++ b/_styles/sweetalert.min.css
@@ -0,0 +1,5 @@
+body.stop-scrolling{height:100%;overflow:hidden}.sweet-overlay{background-color:black;-ms-filter:"alpha(opacity=40)";background-color:rgba(0,0,0,0.4);position:fixed;left:0;right:0;top:0;bottom:0;display:none;z-index:10000}.sweet-alert{background-color:white;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;width:478px;padding:17px;border-radius:5px;text-align:center;position:fixed;left:50%;top:50%;margin-left:-256px;margin-top:-200px;overflow:hidden;display:none;z-index:99999}@media all and (max-width:540px){.sweet-alert{width:auto;margin-left:0;margin-right:0;left:15px;right:15px}}.sweet-alert h2{color:#575757;font-size:30px;text-align:center;font-weight:600;text-transform:none;position:relative;margin:25px 0;padding:0;line-height:40px;display:block}.sweet-alert p{color:#797979;font-size:16px;text-align:center;font-weight:300;position:relative;text-align:inherit;float:none;margin:0;padding:0;line-height:normal}.sweet-alert fieldset{border:0;position:relative}.sweet-alert .sa-error-container{background-color:#f1f1f1;margin-left:-17px;margin-right:-17px;overflow:hidden;padding:0 10px;max-height:0;webkit-transition:padding .15s,max-height .15s;transition:padding .15s,max-height .15s}.sweet-alert .sa-error-container.show{padding:10px 0;max-height:100px;webkit-transition:padding .2s,max-height .2s;transition:padding .25s,max-height .25s}.sweet-alert .sa-error-container .icon{display:inline-block;width:24px;height:24px;border-radius:50%;background-color:#ea7d7d;color:white;line-height:24px;text-align:center;margin-right:3px}.sweet-alert .sa-error-container p{display:inline-block}.sweet-alert .sa-input-error{position:absolute;top:29px;right:26px;width:20px;height:20px;opacity:0;-webkit-transform:scale(0.5);transform:scale(0.5);-webkit-transform-origin:50% 50%;transform-origin:50% 50%;-webkit-transition:all .1s;transition:all .1s}.sweet-alert .sa-input-error::before,.sweet-alert .sa-input-error::after{content:"";width:20px;height:6px;background-color:#f06e57;border-radius:3px;position:absolute;top:50%;margin-top:-4px;left:50%;margin-left:-9px}.sweet-alert .sa-input-error::before{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.sweet-alert .sa-input-error::after{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.sweet-alert .sa-input-error.show{opacity:1;-webkit-transform:scale(1);transform:scale(1)}.sweet-alert input{width:100%;box-sizing:border-box;border-radius:3px;border:1px solid #d7d7d7;height:43px;margin-top:10px;margin-bottom:17px;font-size:18px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.06);padding:0 12px;display:none;-webkit-transition:all .3s;transition:all .3s}.sweet-alert input:focus{outline:0;box-shadow:0 0 3px #c4e6f5;border:1px solid #b4dbed}.sweet-alert input:focus::-moz-placeholder{transition:opacity .3s .03s ease;opacity:.5}.sweet-alert input:focus:-ms-input-placeholder{transition:opacity .3s .03s ease;opacity:.5}.sweet-alert input:focus::-webkit-input-placeholder{transition:opacity .3s .03s ease;opacity:.5}.sweet-alert input::-moz-placeholder{color:#bdbdbd}.sweet-alert input:-ms-input-placeholder{color:#bdbdbd}.sweet-alert input::-webkit-input-placeholder{color:#bdbdbd}.sweet-alert.show-input input{display:block}.sweet-alert .sa-confirm-button-container{display:inline-block;position:relative}.sweet-alert .la-ball-fall{position:absolute;left:50%;top:50%;margin-left:-27px;margin-top:4px;opacity:0;visibility:hidden}.sweet-alert button{background-color:#8cd4f5;color:white;border:0;box-shadow:none;font-size:17px;font-weight:500;-webkit-border-radius:4px;border-radius:5px;padding:10px 32px;margin:26px 5px 0 5px;cursor:pointer}.sweet-alert button:focus{outline:0;box-shadow:0 0 2px rgba(128,179,235,0.5),inset 0 0 0 1px rgba(0,0,0,0.05)}.sweet-alert button:hover{background-color:#7ecff4}.sweet-alert button:active{background-color:#5dc2f1}.sweet-alert button.cancel{background-color:#c1c1c1}.sweet-alert button.cancel:hover{background-color:#b9b9b9}.sweet-alert button.cancel:active{background-color:#a8a8a8}.sweet-alert button.cancel:focus{box-shadow:rgba(197,205,211,0.8) 0 0 2px,rgba(0,0,0,0.0470588) 0 0 0 1px inset !important}.sweet-alert button[disabled]{opacity:.6;cursor:default}.sweet-alert button.confirm[disabled]{color:transparent}.sweet-alert button.confirm[disabled] ~ .la-ball-fall{opacity:1;visibility:visible;transition-delay:0}.sweet-alert button::-moz-focus-inner{border:0}.sweet-alert[data-has-cancel-button=false] button{box-shadow:none !important}.sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false]{padding-bottom:40px}.sweet-alert .sa-icon{width:80px;height:80px;border:4px solid gray;-webkit-border-radius:40px;border-radius:40px;border-radius:50%;margin:20px auto;padding:0;position:relative;box-sizing:content-box}.sweet-alert .sa-icon.sa-error{border-color:#f27474}.sweet-alert .sa-icon.sa-error .sa-x-mark{position:relative;display:block}.sweet-alert .sa-icon.sa-error .sa-line{position:absolute;height:5px;width:47px;background-color:#f27474;display:block;top:37px;border-radius:2px}.sweet-alert .sa-icon.sa-error .sa-line.sa-left{-webkit-transform:rotate(45deg);transform:rotate(45deg);left:17px}.sweet-alert .sa-icon.sa-error .sa-line.sa-right{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);right:16px}.sweet-alert .sa-icon.sa-warning{border-color:#f8bb86}.sweet-alert .sa-icon.sa-warning .sa-body{position:absolute;width:5px;height:47px;left:50%;top:10px;-webkit-border-radius:2px;border-radius:2px;margin-left:-2px;background-color:#f8bb86}.sweet-alert .sa-icon.sa-warning .sa-dot{position:absolute;width:7px;height:7px;-webkit-border-radius:50%;border-radius:50%;margin-left:-3px;left:50%;bottom:10px;background-color:#f8bb86}.sweet-alert .sa-icon.sa-info{border-color:#c9dae1}.sweet-alert .sa-icon.sa-info::before{content:"";position:absolute;width:5px;height:29px;left:50%;bottom:17px;border-radius:2px;margin-left:-2px;background-color:#c9dae1}.sweet-alert .sa-icon.sa-info::after{content:"";position:absolute;width:7px;height:7px;border-radius:50%;margin-left:-3px;top:19px;background-color:#c9dae1}.sweet-alert .sa-icon.sa-success{border-color:#a5dc86}.sweet-alert .sa-icon.sa-success::before,.sweet-alert .sa-icon.sa-success::after{content:'';-webkit-border-radius:40px;border-radius:40px;border-radius:50%;position:absolute;width:60px;height:120px;background:white;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.sweet-alert .sa-icon.sa-success::before{-webkit-border-radius:120px 0 0 120px;border-radius:120px 0 0 120px;top:-7px;left:-33px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:60px 60px;transform-origin:60px 60px}.sweet-alert .sa-icon.sa-success::after{-webkit-border-radius:0 120px 120px 0;border-radius:0 120px 120px 0;top:-11px;left:30px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:0 60px;transform-origin:0 60px}.sweet-alert .sa-icon.sa-success .sa-placeholder{width:80px;height:80px;border:4px solid rgba(165,220,134,0.2);-webkit-border-radius:40px;border-radius:40px;border-radius:50%;box-sizing:content-box;position:absolute;left:-4px;top:-4px;z-index:2}.sweet-alert .sa-icon.sa-success .sa-fix{width:5px;height:90px;background-color:white;position:absolute;left:28px;top:8px;z-index:1;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.sweet-alert .sa-icon.sa-success .sa-line{height:5px;background-color:#a5dc86;display:block;border-radius:2px;position:absolute;z-index:2}.sweet-alert .sa-icon.sa-success .sa-line.sa-tip{width:25px;left:14px;top:46px;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.sweet-alert .sa-icon.sa-success .sa-line.sa-long{width:47px;right:8px;top:38px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.sweet-alert .sa-icon.sa-custom{background-size:contain;border-radius:0;border:0;background-position:center center;background-repeat:no-repeat}@-webkit-keyframes showSweetAlert{0{transform:scale(0.7);-webkit-transform:scale(0.7)}45%{transform:scale(1.05);-webkit-transform:scale(1.05)}80%{transform:scale(0.95);-webkit-transform:scale(0.95)}100%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes showSweetAlert{0{transform:scale(0.7);-webkit-transform:scale(0.7)}45%{transform:scale(1.05);-webkit-transform:scale(1.05)}80%{transform:scale(0.95);-webkit-transform:scale(0.95)}100%{transform:scale(1);-webkit-transform:scale(1)}}@-webkit-keyframes hideSweetAlert{0{transform:scale(1);-webkit-transform:scale(1)}100%{transform:scale(0.5);-webkit-transform:scale(0.5)}}@keyframes hideSweetAlert{0{transform:scale(1);-webkit-transform:scale(1)}100%{transform:scale(0.5);-webkit-transform:scale(0.5)}}@-webkit-keyframes slideFromTop{0{top:0}100%{top:50%}}@keyframes slideFromTop{0{top:0}100%{top:50%}}@-webkit-keyframes slideToTop{0{top:50%}100%{top:0}}@keyframes slideToTop{0{top:50%}100%{top:0}}@-webkit-keyframes slideFromBottom{0{top:70%}100%{top:50%}}@keyframes slideFromBottom{0{top:70%}100%{top:50%}}@-webkit-keyframes slideToBottom{0{top:50%}100%{top:70%}}@keyframes slideToBottom{0{top:50%}100%{top:70%}}.showSweetAlert[data-animation=pop]{-webkit-animation:showSweetAlert .3s;animation:showSweetAlert .3s}.showSweetAlert[data-animation=none]{-webkit-animation:none;animation:none}.showSweetAlert[data-animation=slide-from-top]{-webkit-animation:slideFromTop .3s;animation:slideFromTop .3s}.showSweetAlert[data-animation=slide-from-bottom]{-webkit-animation:slideFromBottom .3s;animation:slideFromBottom .3s}.hideSweetAlert[data-animation=pop]{-webkit-animation:hideSweetAlert .2s;animation:hideSweetAlert .2s}.hideSweetAlert[data-animation=none]{-webkit-animation:none;animation:none}.hideSweetAlert[data-animation=slide-from-top]{-webkit-animation:slideToTop .4s;animation:slideToTop .4s}.hideSweetAlert[data-animation=slide-from-bottom]{-webkit-animation:slideToBottom .3s;animation:slideToBottom .3s}@-webkit-keyframes animateSuccessTip{0{width:0;left:1px;top:19px}54%{width:0;left:1px;top:19px}70%{width:50px;left:-8px;top:37px}84%{width:17px;left:21px;top:48px}100%{width:25px;left:14px;top:45px}}@keyframes animateSuccessTip{0{width:0;left:1px;top:19px}54%{width:0;left:1px;top:19px}70%{width:50px;left:-8px;top:37px}84%{width:17px;left:21px;top:48px}100%{width:25px;left:14px;top:45px}}@-webkit-keyframes animateSuccessLong{0{width:0;right:46px;top:54px}65%{width:0;right:46px;top:54px}84%{width:55px;right:0;top:35px}100%{width:47px;right:8px;top:38px}}@keyframes animateSuccessLong{0{width:0;right:46px;top:54px}65%{width:0;right:46px;top:54px}84%{width:55px;right:0;top:35px}100%{width:47px;right:8px;top:38px}}@-webkit-keyframes rotatePlaceholder{0{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}5%{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}12%{transform:rotate(-405deg);-webkit-transform:rotate(-405deg)}100%{transform:rotate(-405deg);-webkit-transform:rotate(-405deg)}}@keyframes rotatePlaceholder{0{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}5%{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}12%{transform:rotate(-405deg);-webkit-transform:rotate(-405deg)}100%{transform:rotate(-405deg);-webkit-transform:rotate(-405deg)}}.animateSuccessTip{-webkit-animation:animateSuccessTip .75s;animation:animateSuccessTip .75s}.animateSuccessLong{-webkit-animation:animateSuccessLong .75s;animation:animateSuccessLong .75s}.sa-icon.sa-success.animate::after{-webkit-animation:rotatePlaceholder 4.25s ease-in;animation:rotatePlaceholder 4.25s ease-in}@-webkit-keyframes animateErrorIcon{0{transform:rotateX(100deg);-webkit-transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0);-webkit-transform:rotateX(0);opacity:1}}@keyframes animateErrorIcon{0{transform:rotateX(100deg);-webkit-transform:rotateX(100deg);opacity:0}100%{transform:rotateX(0);-webkit-transform:rotateX(0);opacity:1}}.animateErrorIcon{-webkit-animation:animateErrorIcon .5s;animation:animateErrorIcon .5s}@-webkit-keyframes animateXMark{0{transform:scale(0.4);-webkit-transform:scale(0.4);margin-top:26px;opacity:0}50%{transform:scale(0.4);-webkit-transform:scale(0.4);margin-top:26px;opacity:0}80%{transform:scale(1.15);-webkit-transform:scale(1.15);margin-top:-6px}100%{transform:scale(1);-webkit-transform:scale(1);margin-top:0;opacity:1}}@keyframes animateXMark{0{transform:scale(0.4);-webkit-transform:scale(0.4);margin-top:26px;opacity:0}50%{transform:scale(0.4);-webkit-transform:scale(0.4);margin-top:26px;opacity:0}80%{transform:scale(1.15);-webkit-transform:scale(1.15);margin-top:-6px}100%{transform:scale(1);-webkit-transform:scale(1);margin-top:0;opacity:1}}.animateXMark{-webkit-animation:animateXMark .5s;animation:animateXMark .5s}@-webkit-keyframes pulseWarning{0{border-color:#f8d486}100%{border-color:#f8bb86}}@keyframes pulseWarning{0{border-color:#f8d486}100%{border-color:#f8bb86}}.pulseWarning{-webkit-animation:pulseWarning .75s infinite alternate;animation:pulseWarning .75s infinite alternate}@-webkit-keyframes pulseWarningIns{0{background-color:#f8d486}100%{background-color:#f8bb86}}@keyframes pulseWarningIns{0{background-color:#f8d486}100%{background-color:#f8bb86}}.pulseWarningIns{-webkit-animation:pulseWarningIns .75s infinite alternate;animation:pulseWarningIns .75s infinite alternate}@-webkit-keyframes rotate-loading{0{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes rotate-loading{0{transform:rotate(0)}100%{transform:rotate(360deg)}}.sweet-alert .sa-icon.sa-error .sa-line.sa-left{-ms-transform:rotate(45deg) \9}.sweet-alert .sa-icon.sa-error .sa-line.sa-right{-ms-transform:rotate(-45deg) \9}.sweet-alert .sa-icon.sa-success{border-color:transparent\9}.sweet-alert .sa-icon.sa-success .sa-line.sa-tip{-ms-transform:rotate(45deg) \9}.sweet-alert .sa-icon.sa-success .sa-line.sa-long{-ms-transform:rotate(-45deg) \9}/*!
+ * Load Awesome v1.1.0 (http://github.danielcardoso.net/load-awesome/)
+ * Copyright 2015 Daniel Cardoso <@DanielCardoso>
+ * Licensed under MIT
+ */.la-ball-fall,.la-ball-fall>div{position:relative;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.la-ball-fall{display:block;font-size:0;color:#fff}.la-ball-fall.la-dark{color:#333}.la-ball-fall>div{display:inline-block;float:none;background-color:currentColor;border:0 solid currentColor}.la-ball-fall{width:54px;height:18px}.la-ball-fall>div{width:10px;height:10px;margin:4px;border-radius:100%;opacity:0;-webkit-animation:ball-fall 1s ease-in-out infinite;-moz-animation:ball-fall 1s ease-in-out infinite;-o-animation:ball-fall 1s ease-in-out infinite;animation:ball-fall 1s ease-in-out infinite}.la-ball-fall>div:nth-child(1){-webkit-animation-delay:-200ms;-moz-animation-delay:-200ms;-o-animation-delay:-200ms;animation-delay:-200ms}.la-ball-fall>div:nth-child(2){-webkit-animation-delay:-100ms;-moz-animation-delay:-100ms;-o-animation-delay:-100ms;animation-delay:-100ms}.la-ball-fall>div:nth-child(3){-webkit-animation-delay:0;-moz-animation-delay:0;-o-animation-delay:0;animation-delay:0}.la-ball-fall.la-sm{width:26px;height:8px}.la-ball-fall.la-sm>div{width:4px;height:4px;margin:2px}.la-ball-fall.la-2x{width:108px;height:36px}.la-ball-fall.la-2x>div{width:20px;height:20px;margin:8px}.la-ball-fall.la-3x{width:162px;height:54px}.la-ball-fall.la-3x>div{width:30px;height:30px;margin:12px}@-webkit-keyframes ball-fall{0{opacity:0;-webkit-transform:translateY(-145%);transform:translateY(-145%)}10%{opacity:.5}20%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}80%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}90%{opacity:.5}100%{opacity:0;-webkit-transform:translateY(145%);transform:translateY(145%)}}@-moz-keyframes ball-fall{0{opacity:0;-moz-transform:translateY(-145%);transform:translateY(-145%)}10%{opacity:.5}20%{opacity:1;-moz-transform:translateY(0);transform:translateY(0)}80%{opacity:1;-moz-transform:translateY(0);transform:translateY(0)}90%{opacity:.5}100%{opacity:0;-moz-transform:translateY(145%);transform:translateY(145%)}}@-o-keyframes ball-fall{0{opacity:0;-o-transform:translateY(-145%);transform:translateY(-145%)}10%{opacity:.5}20%{opacity:1;-o-transform:translateY(0);transform:translateY(0)}80%{opacity:1;-o-transform:translateY(0);transform:translateY(0)}90%{opacity:.5}100%{opacity:0;-o-transform:translateY(145%);transform:translateY(145%)}}@keyframes ball-fall{0{opacity:0;-webkit-transform:translateY(-145%);-moz-transform:translateY(-145%);-o-transform:translateY(-145%);transform:translateY(-145%)}10%{opacity:.5}20%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}80%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}90%{opacity:.5}100%{opacity:0;-webkit-transform:translateY(145%);-moz-transform:translateY(145%);-o-transform:translateY(145%);transform:translateY(145%)}}
\ No newline at end of file
diff --git a/_styles/vue.css b/_styles/vue.css
new file mode 100644
index 0000000000000000000000000000000000000000..80e62ef132592ac438e3279796da70f6e9957093
--- /dev/null
+++ b/_styles/vue.css
@@ -0,0 +1,872 @@
+@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");
+* {
+ -webkit-font-smoothing: antialiased;
+ -webkit-overflow-scrolling: touch;
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ -webkit-text-size-adjust: none;
+ -webkit-touch-callout: none;
+ box-sizing: border-box;
+}
+body:not(.ready) {
+ overflow: hidden;
+}
+body:not(.ready) [data-cloak],
+body:not(.ready) .app-nav,
+body:not(.ready) > nav {
+ display: none;
+}
+div#app {
+ font-size: 30px;
+ font-weight: lighter;
+ margin: 40vh auto;
+ text-align: center;
+}
+div#app:empty::before {
+ content: 'Loading...';
+}
+.emoji {
+ height: 1.2rem;
+ vertical-align: middle;
+}
+.progress {
+ background-color: var(--theme-color, #42b983);
+ height: 2px;
+ left: 0px;
+ position: fixed;
+ right: 0px;
+ top: 0px;
+ transition: width 0.2s, opacity 0.4s;
+ width: 0%;
+ z-index: 999999;
+}
+.search a:hover {
+ color: var(--theme-color, #42b983);
+}
+.search .search-keyword {
+ color: var(--theme-color, #42b983);
+ font-style: normal;
+ font-weight: bold;
+}
+html,
+body {
+ height: 100%;
+}
+body {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ color: #34495e;
+ font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
+ font-size: 18px;
+ letter-spacing: 0.2px;
+ margin: 0;
+ overflow-x: hidden;
+}
+img {
+ max-width: 100%;
+}
+a[disabled] {
+ cursor: not-allowed;
+ opacity: 0.6;
+}
+kbd {
+ border: solid 1px #ccc;
+ border-radius: 3px;
+ display: inline-block;
+ font-size: 12px !important;
+ line-height: 12px;
+ margin-bottom: 3px;
+ padding: 3px 5px;
+ vertical-align: middle;
+}
+li input[type='checkbox'] {
+ margin: 0 0.2em 0.25em 0;
+ vertical-align: middle;
+}
+.app-nav {
+ margin: 25px 60px 0 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ z-index: 10;
+/* navbar dropdown */
+}
+.app-nav.no-badge {
+ margin-right: 25px;
+}
+.app-nav p {
+ margin: 0;
+}
+.app-nav > a {
+ margin: 0 1rem;
+ padding: 5px 0;
+}
+.app-nav ul,
+.app-nav li {
+ display: inline-block;
+ list-style: none;
+ margin: 0;
+}
+.app-nav a {
+ color: inherit;
+ font-size: 16px;
+ text-decoration: none;
+ transition: color 0.3s;
+}
+.app-nav a:hover {
+ color: var(--theme-color, #42b983);
+}
+.app-nav a.active {
+ border-bottom: 2px solid var(--theme-color, #42b983);
+ color: var(--theme-color, #42b983);
+}
+.app-nav li {
+ display: inline-block;
+ margin: 0 1rem;
+ padding: 5px 0;
+ position: relative;
+ cursor: pointer;
+}
+.app-nav li ul {
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-bottom-color: #ccc;
+ border-radius: 4px;
+ box-sizing: border-box;
+ display: none;
+ max-height: calc(100vh - 61px);
+ overflow-y: auto;
+ padding: 10px 0;
+ position: absolute;
+ right: -15px;
+ text-align: left;
+ top: 100%;
+ white-space: nowrap;
+}
+.app-nav li ul li {
+ display: block;
+ font-size: 14px;
+ line-height: 1rem;
+ margin: 0;
+ margin: 8px 14px;
+ white-space: nowrap;
+}
+.app-nav li ul a {
+ display: block;
+ font-size: inherit;
+ margin: 0;
+ padding: 0;
+}
+.app-nav li ul a.active {
+ border-bottom: 0;
+}
+.app-nav li:hover ul {
+ display: block;
+}
+.github-corner {
+ border-bottom: 0;
+ position: fixed;
+ right: 0;
+ text-decoration: none;
+ top: 0;
+ z-index: 1;
+}
+.github-corner:hover .octo-arm {
+ -webkit-animation: octocat-wave 560ms ease-in-out;
+ animation: octocat-wave 560ms ease-in-out;
+}
+.github-corner svg {
+ color: #fff;
+ fill: var(--theme-color, #42b983);
+ height: 80px;
+ width: 80px;
+}
+main {
+ display: block;
+ position: relative;
+ width: 100vw;
+ height: 100%;
+ z-index: 0;
+}
+main.hidden {
+ display: none;
+}
+.anchor {
+ display: inline-block;
+ text-decoration: none;
+ transition: all 0.3s;
+}
+.anchor span {
+ color: #34495e;
+}
+.anchor:hover {
+ text-decoration: underline;
+}
+.sidebar {
+ border-right: 15px solid rgba(0,0,0,0.07);
+ overflow-y: auto;
+ padding: 40px 5px 0 0;
+ position: absolute;
+ font-size: 1.0rem;
+ font-weight: 700;
+ top: 0;
+ bottom: 0;
+ left: 30px;
+ transition: transform 250ms ease-out;
+ width: 300;
+ z-index: 20;
+}
+.sidebar > h1 {
+ margin: 0 auto 1rem;
+ font-size: 1.5rem;
+ font-weight: 700;
+ text-align: center;
+}
+.sidebar > h1 a {
+ color: inherit;
+ color: #2973b7;
+ text-decoration: none;
+}
+.sidebar > h1 .app-nav {
+ display: block;
+ position: static;
+}
+.sidebar .sidebar-nav {
+ line-height: 2em;
+ padding-bottom: 40px;
+}
+.sidebar li.collapse .app-sub-sidebar {
+ display: none;
+}
+.sidebar ul {
+ margin: 0 0 0 15px;
+ padding: 0;
+}
+.sidebar li > p {
+ font-weight: 700;
+ margin: 0;
+}
+.sidebar ul,
+.sidebar ul li {
+ list-style: none;
+}
+.sidebar ul li a {
+ border-bottom: none;
+ display: block;
+}
+.sidebar ul li ul {
+ padding-left: 20px;
+}
+.sidebar::-webkit-scrollbar {
+ width: 4px;
+}
+.sidebar::-webkit-scrollbar-thumb {
+ background: transparent;
+ border-radius: 4px;
+}
+.sidebar:hover::-webkit-scrollbar-thumb {
+ background: rgba(136,136,136,0.4);
+}
+.sidebar:hover::-webkit-scrollbar-track {
+ background: rgba(136,136,136,0.1);
+}
+.sidebar-toggle {
+ background-color: transparent;
+ background-color: rgba(255,255,255,0.8);
+ border: 0;
+ outline: none;
+ padding: 10px;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ text-align: center;
+ transition: opacity 0.3s;
+ width: 284px;
+ z-index: 30;
+ cursor: pointer;
+}
+.sidebar-toggle:hover .sidebar-toggle-button {
+ opacity: 0.4;
+}
+.sidebar-toggle span {
+ background-color: var(--theme-color, #42b983);
+ display: block;
+ margin-bottom: 4px;
+ width: 20px;
+ height: 4px;
+}
+body.sticky .sidebar,
+body.sticky .sidebar-toggle {
+ position: fixed;
+}
+.content {
+ padding-top: 60px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 300px;
+ transition: left 250ms ease;
+}
+.markdown-section {
+ margin: 0 auto;
+ max-width: 80%;
+ padding: 30px 15px 40px 15px;
+ position: relative;
+}
+.markdown-section > * {
+ box-sizing: border-box;
+ font-size: inherit;
+}
+.markdown-section > :first-child {
+ margin-top: 0 !important;
+}
+.markdown-section hr {
+ border: none;
+ border-bottom: 1px solid #eee;
+ margin: 2em 0;
+}
+.markdown-section iframe {
+ border: 1px solid #eee;
+/* fix horizontal overflow on iOS Safari */
+ width: 1px;
+ min-width: 100%;
+}
+.markdown-section table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ display: block;
+ margin-bottom: 1rem;
+ overflow: auto;
+ width: 100%;
+}
+.markdown-section th {
+ border: 1px solid #ddd;
+ font-weight: bold;
+ padding: 6px 13px;
+}
+.markdown-section td {
+ border: 1px solid #ddd;
+ padding: 6px 13px;
+}
+.markdown-section tr {
+ border-top: 1px solid #ccc;
+}
+.markdown-section tr:nth-child(2n) {
+ background-color: #f8f8f8;
+}
+.markdown-section p.tip {
+ background-color: #f8f8f8;
+ border-bottom-right-radius: 2px;
+ border-left: 4px solid #f66;
+ border-top-right-radius: 2px;
+ margin: 2em 0;
+ padding: 12px 24px 12px 30px;
+ position: relative;
+}
+.markdown-section p.tip:before {
+ background-color: #f66;
+ border-radius: 100%;
+ color: #fff;
+ content: '!';
+ font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
+ font-size: 14px;
+ font-weight: bold;
+ left: -12px;
+ line-height: 20px;
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ text-align: center;
+ top: 14px;
+}
+.markdown-section p.tip code {
+ background-color: #efefef;
+}
+.markdown-section p.tip em {
+ color: #34495e;
+}
+.markdown-section p.warn {
+ background: rgba(66,185,131,0.1);
+ border-radius: 2px;
+ padding: 1rem;
+}
+.markdown-section ul.task-list > li {
+ list-style-type: none;
+}
+body.close .sidebar {
+ transform: translateX(-1000px);
+}
+body.close .sidebar-toggle {
+ width: auto;
+}
+body.close .content {
+ left: 0;
+}
+@media print {
+ .github-corner,
+ .sidebar-toggle,
+ .sidebar,
+ .app-nav {
+ display: none;
+ }
+}
+@media screen and (max-width: 768px) {
+ .github-corner,
+ .sidebar-toggle,
+ .sidebar {
+ position: fixed;
+ }
+ .app-nav {
+ margin-top: 16px;
+ }
+ .app-nav li ul {
+ top: 30px;
+ }
+ main {
+ height: auto;
+ overflow-x: hidden;
+ }
+ .sidebar {
+ left: -300px;
+ transition: transform 250ms ease-out;
+ }
+ .content {
+ left: 0;
+ max-width: 100vw;
+ position: static;
+ padding-top: 20px;
+ transition: transform 250ms ease;
+ }
+ .app-nav,
+ .github-corner {
+ transition: transform 250ms ease-out;
+ }
+ .sidebar-toggle {
+ background-color: transparent;
+ width: auto;
+ padding: 30px 30px 10px 10px;
+ }
+ body.close .sidebar {
+ transform: translateX(300px);
+ }
+ body.close .sidebar-toggle {
+ background-color: rgba(255,255,255,0.8);
+ transition: 1s background-color;
+ width: 284px;
+ padding: 10px;
+ }
+ body.close .content {
+ transform: translateX(300px);
+ }
+ body.close .app-nav,
+ body.close .github-corner {
+ display: none;
+ }
+ .github-corner:hover .octo-arm {
+ -webkit-animation: none;
+ animation: none;
+ }
+ .github-corner .octo-arm {
+ -webkit-animation: octocat-wave 560ms ease-in-out;
+ animation: octocat-wave 560ms ease-in-out;
+ }
+}
+@-webkit-keyframes octocat-wave {
+ 0%, 100% {
+ transform: rotate(0);
+ }
+ 20%, 60% {
+ transform: rotate(-25deg);
+ }
+ 40%, 80% {
+ transform: rotate(10deg);
+ }
+}
+@keyframes octocat-wave {
+ 0%, 100% {
+ transform: rotate(0);
+ }
+ 20%, 60% {
+ transform: rotate(-25deg);
+ }
+ 40%, 80% {
+ transform: rotate(10deg);
+ }
+}
+section.cover {
+ align-items: center;
+ background-position: center center;
+ background-repeat: no-repeat;
+ background-size: cover;
+ height: 100vh;
+ width: 100vw;
+ display: none;
+}
+section.cover.show {
+ display: flex;
+}
+section.cover.has-mask .mask {
+ background-color: #fff;
+ opacity: 0.8;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 100%;
+}
+section.cover .cover-main {
+ flex: 1;
+ margin: -20px 16px 0;
+ text-align: center;
+ position: relative;
+}
+section.cover a {
+ color: inherit;
+ text-decoration: none;
+}
+section.cover a:hover {
+ text-decoration: none;
+}
+section.cover p {
+ line-height: 1.5rem;
+ margin: 1em 0;
+}
+section.cover h1 {
+ color: inherit;
+ font-size: 2.5rem;
+ font-weight: 500;
+ margin: 0.625rem 0 2.5rem;
+ position: relative;
+ text-align: center;
+}
+section.cover h1 a {
+ display: block;
+}
+section.cover h1 small {
+ bottom: -0.4375rem;
+ font-size: 1rem;
+ position: absolute;
+}
+section.cover blockquote {
+ font-size: 1.5rem;
+ text-align: center;
+}
+section.cover ul {
+ line-height: 1.8;
+ list-style-type: none;
+ margin: 1em auto;
+ max-width: 500px;
+ padding: 0;
+}
+/* section.cover .cover-main > p:last-child a {
+ border-color: var(--theme-color, #42b983);
+ border-radius: 2rem;
+ border-style: solid;
+ border-width: 1px;
+ box-sizing: border-box;
+ color: var(--theme-color, #42b983);
+ display: inline-block;
+ font-size: 1.05rem;
+ letter-spacing: 0.1rem;
+ margin: 0.5rem 1rem;
+ padding: 0.75em 2rem;
+ text-decoration: none;
+ transition: all 0.15s ease;
+}
+section.cover .cover-main > p:last-child a:last-child {
+ background-color: var(--theme-color, #42b983);
+ color: #fff;
+}
+section.cover .cover-main > p:last-child a:last-child:hover {
+ color: inherit;
+ opacity: 0.8;
+}
+section.cover .cover-main > p:last-child a:hover {
+ color: inherit;
+} */
+section.cover .cover-main > p:last-child a {
+ border-color: var(--theme-color, #42b983);
+ border-radius: 2rem;
+ border-style: solid;
+ border-width: 2px;
+ box-sizing: border-box;
+ color: var(--theme-color, #42b983);
+ display: inline-block;
+ font-size: 1.25rem;
+ letter-spacing: 0.1rem;
+ margin: 0.5rem 1rem;
+ padding: 0.75em 2rem;
+ text-decoration: none;
+ transition: all 0.15s ease;
+}
+section.cover .cover-main > p:last-child a:first-child {
+ background-color: var(--theme-color, #42b983);
+ color: #fff;
+}
+section.cover .cover-main > p:last-child a:last-child:hover {
+ color: inherit;
+ opacity: 0.8;
+}
+section.cover .cover-main > p:last-child a:hover {
+ color: inherit;
+}
+
+section.cover blockquote > p > a {
+ border-bottom: 2px solid var(--theme-color, #42b983);
+ transition: color 0.3s;
+}
+section.cover blockquote > p > a:hover {
+ color: var(--theme-color, #42b983);
+}
+body {
+ background-color: #fff;
+}
+/* sidebar */
+.sidebar {
+ background-color: #fff;
+ color: #3f6b85;
+ width:300px;
+ padding-left:5px;
+}
+.sidebar li {
+ margin: 6px 0 6px 0;
+}
+.sidebar ul li a {
+ display:inline-block;
+ width: 100%;
+ color: #3f6b85;
+ font-size: 15px;
+ font-weight: 500;
+ overflow: visible;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ word-break: break-all;
+ line-height:22px;
+ padding-bottom:5px;
+ padding-top:5px;
+}
+.sidebar ul li a:hover {
+ text-decoration: underline;
+}
+.sidebar ul li ul {
+ padding: 0;
+}
+.sidebar ul li.active > a {
+ border-right: 3px solid;
+ color: var(--theme-color, #3f6b85);
+ font-size: 18px;
+ font-weight: 700;
+ background-color: #e3e6e9;
+}
+/* .app-sub-sidebar li::before {
+ content: '•';
+ padding-right: 4px;
+ float: left;
+} */
+/* markdown content found on pages */
+.markdown-section h1,
+.markdown-section h2,
+.markdown-section h3,
+.markdown-section h4,
+.markdown-section strong {
+ color: #2c3e50;
+ font-weight: 600;
+}
+.markdown-section a {
+ color: var(--theme-color, #42b983);
+ font-weight: 600;
+}
+.markdown-section h1 {
+ font-size: 2.2rem;
+ margin: 0 0 1rem;
+}
+.markdown-section h2 {
+ font-size: 2.0rem;
+ margin: 45px 0 0.8rem;
+}
+.markdown-section h3 {
+ font-size: 1.75rem;
+ margin: 40px 0 0.6rem;
+}
+.markdown-section h4 {
+ font-size: 1.5rem;
+}
+.markdown-section h5 {
+ font-size: 1rem;
+}
+.markdown-section h6 {
+ color: #777;
+ font-size: 1rem;
+}
+.markdown-section figure,
+.markdown-section p {
+ margin: 1.2em 0;
+}
+.markdown-section p,
+.markdown-section ul,
+.markdown-section ol {
+ line-height: 1.6rem;
+ word-spacing: 0.05rem;
+}
+.markdown-section ul,
+.markdown-section ol {
+ padding-left: 1.5rem;
+}
+.markdown-section blockquote {
+ border-left: 4px solid var(--theme-color, #42b983);
+ color: #858585;
+ margin: 2em 0;
+ padding-left: 20px;
+}
+.markdown-section blockquote p {
+ font-weight: 600;
+ margin-left: 0;
+}
+.markdown-section iframe {
+ margin: 1em 0;
+}
+.markdown-section em {
+ color: #7f8c8d;
+}
+.markdown-section code {
+ background-color: #f8f8f8;
+ border-radius: 2px;
+ color: #e96900;
+ font-family: 'Roboto Mono', Monaco, courier, monospace;
+ font-size: 0.8rem;
+ margin: 0 2px;
+ padding: 3px 5px;
+ white-space: pre-wrap;
+}
+.markdown-section pre {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+ background-color: #f8f8f8;
+ font-family: 'Roboto Mono', Monaco, courier, monospace;
+ line-height: 1.5rem;
+ margin: 1.2em 0;
+ overflow: auto;
+ padding: 0 1.4rem;
+ position: relative;
+ word-wrap: normal;
+}
+/* code highlight */
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: #997b19;
+}
+.token.namespace {
+ opacity: 0.7;
+}
+.token.boolean,
+.token.number {
+ color: #c76b29;
+}
+.token.punctuation {
+ color: #525252;
+}
+.token.tag {
+ color: #2973b7;
+}
+.token.string {
+ color: var(--theme-color, #04834a);
+}
+.token.selector {
+ color: #6679cc;
+}
+.token.attr-name {
+ color: #2973b7;
+}
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #22a2c9;
+}
+.token.attr-value,
+.token.control,
+.token.directive,
+.token.unit {
+ color: var(--theme-color, #42b983);
+}
+.token.property,
+.token.keyword,
+.token.function {
+ color: #960063;
+}
+.token.statement,
+.token.regex,
+.token.atrule {
+ color: #22a2c9;
+}
+.token.placeholder,
+.token.variable {
+ color: #3d8fd1;
+}
+.token.deleted {
+ text-decoration: line-through;
+}
+.token.inserted {
+ border-bottom: 1px dotted #202746;
+ text-decoration: none;
+}
+.token.italic {
+ font-style: italic;
+}
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+.token.important {
+ color: #c94922;
+}
+.token.entity {
+ cursor: help;
+}
+.markdown-section pre > code {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+ background-color: #f8f8f8;
+ border-radius: 2px;
+ color: #8c972b;
+ display: block;
+ font-family: 'Roboto Mono', Monaco, courier, monospace;
+ font-size: 0.8rem;
+ line-height: inherit;
+ margin: 0 2px;
+ max-width: inherit;
+ overflow: inherit;
+ padding: 2.2em 5px;
+ white-space: inherit;
+}
+.markdown-section code::after,
+.markdown-section code::before {
+ letter-spacing: 0.05rem;
+}
+code .token {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+ min-height: 1.5rem;
+ position: relative;
+ left: auto;
+}
+pre::after {
+ color: #ccc;
+ content: attr(data-lang);
+ font-size: 0.6rem;
+ font-weight: 600;
+ height: 15px;
+ line-height: 15px;
+ padding: 5px 10px 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 0;
+}
+.docsify-tabs--classic .docsify-tabs__content{
+ margin-top: -3px;
+}
+.docsify-tabs--classic .docsify-tabs__tab~.docsify-tabs__tab{
+ background:#fff;
+}
diff --git a/development-tools/_navbar.md b/development-tools/_navbar.md
new file mode 100644
index 0000000000000000000000000000000000000000..5982080c0c2a4644e9039165eab3195305afad94
--- /dev/null
+++ b/development-tools/_navbar.md
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/development-tools/_sidebar.md b/development-tools/_sidebar.md
new file mode 100644
index 0000000000000000000000000000000000000000..c70680b377e890927bf6bbe91ec97f7b712940f2
--- /dev/null
+++ b/development-tools/_sidebar.md
@@ -0,0 +1,32 @@
+
+
+
+- RT-Thread Studio
+ - [快速开始](/development-tools/rtthread-studio/um/studio-user-begin.md)
+ - [用户手册](/development-tools/rtthread-studio/um/studio-user-manual.md)
+ - 应用开发
+ - [快速上手](/development-tools/rtthread-studio/applications/quick-start/rtthread-studio-quick-start.md)
+ - [应用开发示例 - 线程](/development-tools/rtthread-studio/applications/thread/rtthread-studio-thread.md)
+ - [应用开发示例 - 串口设备](/development-tools/rtthread-studio/applications/uart/rtthread-studio-uart.md)
+ - 驱动开发
+ - [驱动概况](/development-tools/rtthread-studio/drivers/drv-list/support-driver-list.md)
+ - [PIN 设备](/development-tools/rtthread-studio/drivers/pin/rtthread-studio-pin.md)
+ - 串口设备
+ - [nano 版](/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/rtthread-studio-uart-nano-v3.1.3.md)
+ - [完整版](/development-tools/rtthread-studio/drivers/uart/v4.0.2/rtthread-studio-uart-v4.0.2.md)
+ - I2C 设备
+ - [软件 I2C](/development-tools/rtthread-studio/drivers/soft-i2c/rtthread-studio-soft-i2c.md)
+ - [SPI 设备](/development-tools/rtthread-studio/drivers/spi/rtthread-studio-spi.md)
+ - [ETH 设备](/development-tools/rtthread-studio/drivers/eth/rtthread-studio-eth.md)
+ - [USB Device 设备](/development-tools/rtthread-studio/drivers/usb-device/rtthread-studio-usb-device.md)
+ - [其它驱动](/development-tools/rtthread-studio/drivers/cubemx/rtthread-studio-cubemx.md)
+ - [精彩项目集合](/development-tools/rtthread-studio/applications/project-collection/project-collection.md)
+ - [常见问题](/development-tools/rtthread-studio/faq/studio-faq.md)
+ - [更新日志](/development-tools/rtthread-studio/changelog/changelog.md)
+
+- Env 开发工具
+ - [Env 用户手册](/development-tools/env/env.md)
+- Scons 介绍
+ - [Scons 构建工具](/development-tools/scons/scons.md)
+- Kconfig 介绍
+ - [Kconfig 语法](/development-tools/kconfig/kconfig.md)
diff --git a/development-tools/env/env.md b/development-tools/env/env.md
new file mode 100644
index 0000000000000000000000000000000000000000..c133baf5b0d8a4a36988cc96d6b2e89925492241
--- /dev/null
+++ b/development-tools/env/env.md
@@ -0,0 +1,284 @@
+# Env 用户手册
+
+Env 是 RT-Thread 推出的开发辅助工具,针对基于 RT-Thread 操作系统的项目工程,提供编译构建环境、图形化系统配置及软件包管理功能。
+
+其内置的 menuconfig 提供了简单易用的配置剪裁工具,可对内核、组件和软件包进行自由裁剪,使系统以搭积木的方式进行构建。
+
+## 主要特性
+
+- menuconfig 图形化配置界面,交互性好,操作逻辑强;
+- 丰富的文字帮助说明,配置无需查阅文档;
+- 使用灵活,自动处理依赖,功能开关彻底;
+- 自动生成 rtconfig.h,无需手动修改;
+- 使用 scons 工具生成工程,提供编译环境,操作简单;
+- 提供多种软件包,模块化软件包耦合关联少,可维护性好;
+- 软件包可在线下载,软件包持续集成,包可靠性高;
+
+## 准备工作
+
+Env 工具包含了 RT-Thread 源代码开发编译环境和软件包管理系统。
+
+- 从 RT-Thread 官网下载 [Env 工具](https://www.rt-thread.org/page/download.html)。
+- 在电脑上装好 git,软件包管理功能需要 git 的支持。git 的下载地址为`https://git-scm.com/downloads`,根据向导正确安装 git,并将 git 添加到系统环境变量。
+- 注意在工作环境中,所有的路径都不可以有中文字符或者空格。
+
+## Env 的使用方法
+
+### 打开 Env 控制台
+
+RT-Thread 软件包环境主要以命令行控制台为主,同时以字符型界面来进行辅助,使得尽量减少修改配置文件的方式即可搭建好 RT-Thread 开发环境的方式。
+打开 Env 控制台有两种方式:
+
+#### 方法一:点击 Env 目录下可执行文件
+
+进入 Env 目录,可以运行本目录下的 `env.exe`,如果打开失败可以尝试使用 `env.bat`。
+
+#### 方法二:在文件夹中通过右键菜单打开 Env 控制台
+
+Env 目录下有一张 `Add_Env_To_Right-click_Menu.png`(添加 Env 至右键菜单.png) 的图片,如下:
+
+
+
+根据图片上的步骤操作,就可以在任意文件夹下通过右键菜单来启动 Env 控制台。效果如下:
+
+
+
+> [!NOTE]
+> 注:因为需要设置 Env 进程的环境变量,第一次启动可能会出现杀毒软件误报的情况,如果遇到了 **杀毒软件误报** ,允许 Env 相关程序运行,然后将相关程序添加至白名单即可。
+
+### 编译 BSP
+
+scons 是 RT-Thread 使用的编译构建工具,可以使用 scons 相关命令来编译 RT-Thread 。
+
+#### 第一步:切换到 BSP 根目录
+
+- 打开控制台后,可以在命令行模式下使用 cd 命令切换到你想要配置的 BSP 根目录中。
+
+例如工程目录为: `rt-thread\bsp\stm32f429-apollo` :
+
+
+
+#### 第二步:bsp 的编译
+
+- Env 中携带了 `Python & scons` 环境,只需在 `rt-thread\bsp\stm32f429-apollo` 目录中运行 `scons` 命令即可使用默认的 ARM_GCC 工具链编译 bsp。
+
+
+
+编译成功:
+
+
+
+如果使用 mdk/iar 来进行项目开发,可以直接使用 BSP 中的工程文件或者使用以下命令中的其中一种,重新生成工程,再进行编译下载。
+
+```
+scons --target=iar
+scons --target=mdk4
+scons --target=mdk5
+```
+
+更多 scons 教程,请参考 [《Scons 构建工具》](../scons/scons.md)
+
+### BSP 配置:menuconfig
+
+menuconfig 是一种图形化配置工具,RT-Thread 使用其对整个系统进行配置、裁剪。
+
+#### 快捷键介绍
+
+进入 BSP 根目录,输入 `menuconfig` 命令后即可打开其界面。 menuconfig 常用快捷键如图所示:
+
+
+
+#### 修改配置
+
+menuconfig 有多种类型的配置项,修改方法也有所不同,常见类型如下:
+
+- 开/关 型:使用空格键来选中或者关闭
+- 数值、字符串型:按下回车键后会出现对话框,在对话框中对配置项进行修改
+
+#### 保存配置
+
+选择好配置项之后按 ESC 键退出,选择保存修改即可自动生成 rtconfig.h 文件。此时再次使用 scons 命令就会根据新的 rtconfig.h 文件重新编译工程了。
+
+### 软件包管理:package
+
+RT-Thread 提供一个软件包管理平台,这里存放了官方提供或开发者提供的软件包。该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread 生态的重要组成部分。
+
+[点击这里](https://github.com/RT-Thread-packages) 可以查看到 RT-Thread 官方的提供的软件包,绝大多数软件包都有详细的说明文档及使用示例。
+
+> 提示:截止到 2018-03-13 ,当前软件包数量达到 **40+**
+
+**package** 工具作为 Env 的组成部分,为开发者提供了软件包的下载、更新、删除等管理功能。
+
+Env 命令行输入 `pkgs` 可以看到命令简介:
+
+```
+> pkgs
+usage: env.py package [-h] [--update] [--list] [--wizard] [--upgrade]
+ [--printenv]
+
+optional arguments:
+ -h, --help show this help message and exit
+ --update update packages, install or remove the packages as you set in
+ menuconfig
+ --list list target packages
+ --wizard create a package with wizard
+ --upgrade update local packages list from git repo
+ --printenv print environmental variables to check
+```
+
+#### 下载、更新、删除软件包
+
+在下载、更新软件包前,需要先在 `menuconfig` 中 **开启** 你想要操作的软件包
+
+这些软件包位于 `RT-Thread online packages` 菜单下,进入该菜单后,则可以看如下软件包分类:
+
+
+
+找到你需要的软件包然后选中开启,保存并退出 menuconfig 。此时软件包已被标记选中,但是还没有下载到本地,所以还无法使用。
+
+- **下载** :如果软件包在本地已被选中,但是未下载,此时输入:`pkgs --update` ,该软件包自动下载;
+- **更新** :如果选中的软件包在服务器端有更新,并且版本号选择的是 **latest** 。此时输入: `pkgs --update` ,该软件包将会在本地进行更新;
+- **删除** :某个软件包如果无需使用,需要先在 menuconfig 中取消其的选中状态,然后再执行: `pkgs --update` 。此时本地已下载但未被选中的软件包将会被删除。
+
+#### 升级本地软件包信息
+
+随着 package 系统的不断壮大,会有越来越多的软件包加入进来,所以本地看到 menuconfig 中的软件包列表可能会与服务器 **不同步** 。使用 `pkgs --upgrade` 命令即可解决该问题,这个命令不仅会对本地的包信息进行更新同步,还会对 Env 的功能脚本进行升级,建议定期使用。
+
+### Env 工具配置
+
+- 新版本的 Env 工具中加入了自动更新软件包和自动生成 mdk/iar 工程的选项,默认是不开启的。可以使用 `menuconfig -s/--setting` 命令来进行配置。
+
+* 使用 `menuconfig -s` 命令进入 Env 配置界面
+
+ 
+
+ 按下回车进入配置菜单,里面共有 3 个配置选项
+
+ 
+
+3 个选项分别为:
+
+* **软件包自动更新功能**:在退出 menuconfig 功能后,会自动使用`pkgs --update`命令来下载并安装软件包,同时删除旧的软件包。本功能在下载在线软件包时使用。
+
+* **自动创建 MDK 或 IAR 工程功能**:当修改 menuconfig 配置后 ,必须输入 `scons --target=xxx` 来重新生成工程。开启此功能,就会在退出 menuconfig 时,自动重新生成工程,无需再手动输入 scons 命令来重新生成工程。
+
+* **使用镜像服务器下载软件包**:由于大部分软件包目前均存放在 GitHub 上,所以在国内的特殊环境下,下载体验非常差。开启此功能,可以通过 **国内镜像服务器** 下载软件包,大幅提高软件包的下载速度和稳定性,减少更新软件包和 submodule 时的等待时间,提升下载体验。
+
+## 在项目中使用 Env
+
+### 使用 Env 的要求
+
+- menuconfig 是 RT-Thread 3.0 以上版本的特性,推荐将 RT-Thread 更新到 3.0 以上版本。
+- 目前 RT-Thread 还没有对所有的 BSP 做 menuconfig 的支持,也就是说有些 BSP 暂时还不能使用 menuconfig 来进行配置,但常用的 BSP 都已经支持。
+
+### menuconfig 中选项的修改方法
+
+如果想在 menuconfig 的配置项中添加宏定义,则可以修改 BSP 下的 Kconfig 文件,修改方法可以在网络中搜索`Kconfig语法`关键字获得详细的说明文档,也可以参考 RT-Thread 中的 Kconfig 文件或者已经支持过 menuconfig 的 BSP 中的 Kconfig 文件。
+
+### 新的项目添加 menuconfig 功能
+
+这里的新项目指的是,**还未生成 .config 和 rtconfig.h** 的全新开发的项目。因为这两个文件,只有在 menuconfig 第一次保存时才会创建。具体流程如下:
+
+ 1. 将已经支持 menuconfig 功能的 BSP 里面的 kconfig 文件拷贝到新的项目根目录中。
+ 2. 注意修改 Kconfig 中的 RTT_ROOT 值为 RT-Thread 所在目录,否则可能提示找不到 RTT_ROOT 。
+ 3. 使用 menuconfig 命令开始配置即可。
+
+### 旧项目添加 menuconfig 功能
+
+这里的旧项目指的是已经经过一段时间的开发,而且项目中存在已经修改过的 rtconfig.h文件 ,但是没有使用过 menuconfig 来配置的项目。具体流程如下:
+
+ 1. 首先备份旧项目内的 rtconfig.h 文件。
+ 2. 使用 `scons --genconfig` 命令根据已有的 rtconfig.h 生成 .config 文件,这里生成的 .config 文件保存了旧项目中 rtconfig.h 文件对项目的配置参数。
+ 3. 将已经支持 menuconfig 功能的 BSP 里面的 kconfig 文件拷贝到要修改项目的根目录中。
+ 4. 注意修改 Kconfig 中的 RTT_ROOT 值为 RT-Thread 所在目录,否则可能提示找不到 RTT_ROOT 。
+ 5. 使用 menuconfig 命令来配置我们要修改的旧项目。menuconfig 会读取第二步生成的 .config 文件,并根据旧项目的配置参数生成新的 .config 文件和 rtconfig.h 文件 。
+ 6. 对比检查新旧两份 rtconfig.h 文件,如果有不一致的地方,可以使用 menuconfig 命令对配置项进行调整。
+
+### 用户软件包管理功能
+
+实际开发项目时,开发者可能想要将已下载的软件包加入 git 管理,或者想自己管理该软件包。不希望 Env 工具再拉取该软件包的最新版本,此时可以使用用户软件包管理功能。
+
+如果用户手动将 `EasyFlash-v4.1.0` 文件夹的后缀,也就是软件包的版本号删除,修改为 `EasyFlash`,此时再次使用 `pkgs --update` 命令将不会再拉取 `EasyFlash-v4.0.0` 软件包。Env 工具此时认为 EasyFlash 软件包由用户管理,此时使用 `pkgs --force-update` 命令才可以重新拉取附带 version 的新版本软件包。
+
+
+
+## 使用 pip 扩展更多功能
+
+在 Env 环境下暂时不能直接使用 Python 提供的 pip 工具来安装更多模块。如果需要在 Env 环境下使用 pip 功能,可以按照如下方法重新安装 pip 工具:
+
+1. 从地址 https://bootstrap.pypa.io/get-pip.py 下载 get-pip.py 文件,存放在磁盘中。
+
+2. 在 **Env 环境下**执行 `python get-pip.py` 命令来重新安装 pip 工具。
+
+3. pip 工具重新安装成功后,可以使用 `pip install module-name` 命令来安装所需模块。
+
+## Env 工具使用注意事项
+
+- 第一次使用 Env 推荐去官网下载最新版本的 Env 工具,新版本的 Env 会有更好的兼容性,也支持自动更新的命令。
+- 可以使用 Env 内置命令 pkgs --upgrade 来更新软件包列表和 Env 的功能代码,这样可以最大程度避免遇到已经修复的问题。
+- Env 所在路径不要有中文或者空格存在。
+- BSP 工程所在的路径不要有中文或者空格存在。
+
+## 常见问题
+
+### Q: Env 工具出现乱码怎么办?
+
+**A:** 首先检查是否有中文路径。
+检查 chcp 命令是否加入了系统环境变量,尝试使用 chcp 437 命令将字符格式改为英文。如果提示没有 chcp 命令,则考虑是没有加入到环境变量中。
+chcp 命令所在的目录可能在 system32 目录,添加到环境变量即可。
+[Env 工具乱码问题传送门](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=5763&page=1#pid32213) 。
+
+### Q: 提示找不到 git 命令?
+
+ 'git' is not recognized as an internal or external command, operable program or batch file.
+
+**A:** 没安装 git,需要安装 git 并加入环境变量。
+
+### Q: 提示找不到 CMD 命令?
+
+**A:** 计算机右键–>> 属性—>> 高级系统设置—->> 环境变量,`C:\Windows\System32`; 加入系统环境变量即可
+
+### Q: 运行 python 的时候提示 no module named site 怎么办?
+
+**A:** 计算机右键–>> 属性—>> 高级系统设置—->> 环境变量,在管理员的用户变量中,新建变量名为 PYTHONHOME,变量值为:`F:\git_repositories\env\tools\Python27` (是 Env 里面 Python 的安装路径),注意后面不要加 “;”,否则会无效。 如果添加 PYTHONHOME 没好,再用同样的方法添加 PYTHONPATH。就可以解决这个问题了。
+
+有一篇博文详细的描述了这个问题:[传送门在这里](http://blog.csdn.net/nullzeng/article/details/45293333),如果想了解原理可以看一看。
+
+### Q: 在 Env 下能生成哪些类型的工程?
+
+ **A:**
+
+ 1. 目前在 Env 下可以使用 scons 工具生成 mdk/iar 的工程,还没有支持 eclipse 工程的自动生成。
+ 2. 一般在使用 Env 的开发,使用 gcc 的工具链,那么只需要一个 source insight 或者 vs code 之类的编辑器来看代码,使用 scons 编译即可。
+
+### Q: 自己制作的 BSP 如何能支持 menuconfig?
+
+**A:** 可以查阅本章 **在项目中使用 Env** 章节。
+
+### Q: pkgs --upgrade 命令和 pkgs --update 命令有什么区别?
+
+ **A:**
+
+ 1. pkgs --upgrade 命令是用来升级 Env 功能脚本本身和软件包列表的。没有最新的包列表就不能选择最近更新的软件包。
+ 2. pkgs --update 命令是用来更新软件包本身的,比如说你在 menuconfig 中选中了 json 和 mqtt 的软件包,但是退出 menuconfig 时并没有下载这些软件包。你需要使用 pkgs --update 命令,这时候 Env 就会下载你选中的软件包并且加入到你的工程中去。
+ 3. 新版本的 Env 支持 menuconfig -s/--setting 命令,如果你不想每次更换软件包后使用 pkgs --update 命令,在使用 menuconfig -s/--setting 命令后配置 Env 选择每次使用 menuconfig 后自动更新软件包即可。
+
+### Q: VC98 文件夹问题
+
+详细描述:出现错误 MissingConfiguration: registry dir `D:\Program Files (x86)\Microsoft Visual Studio\VC98` not found on the filesystem
+
+
+
+**A:** 在划线的目录新建一个 VC98 的空文件夹,就可以使用 scons 了。
+
+### Q: 使用 menuconfig 命令提示“can't find file Kconfig”。
+
+**A:** 当前工作的 BSP 目录下缺少 Kconfig 文件,参考本文《新的项目添加 menuconfig 功能》 和 《旧项目添加 menuconfig 功能》。
+
+### Q: IOError: [Errno 2] No such file or directory: 'nul'
+
+**A:** 这是由于 windows 系统没有开启 `Null Service` 服务的缘故,常见于在 win10 的早期版本中(如版本号 1703),该问题有两种解决方法,第一种是开启 windows 更新将 windows 更新到最新版本,因为在后续的补丁中 windows 默认开启了该服务,第二种是参考该 [link](http://revertservice.com/10/null/) 手动开启 `Null Service` 服务。
+
+## 常用资料链接
+
+* [论坛持续更新的 Env 常见问题问答帖](https://www.rt-thread.org/qa/thread-5699-1-1.html)
diff --git a/development-tools/env/figures/Add_Env_To_Right-click_Menu.png b/development-tools/env/figures/Add_Env_To_Right-click_Menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..607a819349f5967bf6f3d0186de44fe69ad04ef7
Binary files /dev/null and b/development-tools/env/figures/Add_Env_To_Right-click_Menu.png differ
diff --git a/development-tools/env/figures/cd_cmd.png b/development-tools/env/figures/cd_cmd.png
new file mode 100644
index 0000000000000000000000000000000000000000..fbf358443ba8ceda5c33bba2183e10866a3f6dfa
Binary files /dev/null and b/development-tools/env/figures/cd_cmd.png differ
diff --git a/development-tools/env/figures/console.png b/development-tools/env/figures/console.png
new file mode 100644
index 0000000000000000000000000000000000000000..23f1163a2fd3458b5884683198343c879eadc40a
Binary files /dev/null and b/development-tools/env/figures/console.png differ
diff --git a/development-tools/env/figures/hotkey.png b/development-tools/env/figures/hotkey.png
new file mode 100644
index 0000000000000000000000000000000000000000..f46239c73ba54e39ea4b4f83a082d6ca7232ee4c
Binary files /dev/null and b/development-tools/env/figures/hotkey.png differ
diff --git a/development-tools/env/figures/menuconfig_packages_list.png b/development-tools/env/figures/menuconfig_packages_list.png
new file mode 100644
index 0000000000000000000000000000000000000000..073456bc130d36d8c3598b8da07234218e794e2c
Binary files /dev/null and b/development-tools/env/figures/menuconfig_packages_list.png differ
diff --git a/development-tools/env/figures/menuconfig_s.png b/development-tools/env/figures/menuconfig_s.png
new file mode 100644
index 0000000000000000000000000000000000000000..0c087b1fde5352f3f46b4d2c094aa81492d6282d
Binary files /dev/null and b/development-tools/env/figures/menuconfig_s.png differ
diff --git a/development-tools/env/figures/menuconfig_s_auto_prj.png b/development-tools/env/figures/menuconfig_s_auto_prj.png
new file mode 100644
index 0000000000000000000000000000000000000000..57ac4ae52d124f43a55c4107b8971fc02762e74f
Binary files /dev/null and b/development-tools/env/figures/menuconfig_s_auto_prj.png differ
diff --git a/development-tools/env/figures/menuconfig_s_auto_update.png b/development-tools/env/figures/menuconfig_s_auto_update.png
new file mode 100644
index 0000000000000000000000000000000000000000..462fa74b7ed9805cb6e9a238befdba632b1e992f
Binary files /dev/null and b/development-tools/env/figures/menuconfig_s_auto_update.png differ
diff --git a/development-tools/env/figures/q1.png b/development-tools/env/figures/q1.png
new file mode 100644
index 0000000000000000000000000000000000000000..186362f0cf771c09f91cd4ae4e443a0b62cd92e7
Binary files /dev/null and b/development-tools/env/figures/q1.png differ
diff --git a/development-tools/env/figures/scons_done.png b/development-tools/env/figures/scons_done.png
new file mode 100644
index 0000000000000000000000000000000000000000..d819bd894aa3ea16a84c4f835ae0cb17c7c539bf
Binary files /dev/null and b/development-tools/env/figures/scons_done.png differ
diff --git a/development-tools/env/figures/use_scons.png b/development-tools/env/figures/use_scons.png
new file mode 100644
index 0000000000000000000000000000000000000000..a57995b210e90d04eb5d7a8f449cde26e0699e99
Binary files /dev/null and b/development-tools/env/figures/use_scons.png differ
diff --git a/development-tools/env/figures/user_manage_package.png b/development-tools/env/figures/user_manage_package.png
new file mode 100644
index 0000000000000000000000000000000000000000..89c60bd3c4213a40eb62db6b22620a448e798302
Binary files /dev/null and b/development-tools/env/figures/user_manage_package.png differ
diff --git a/development-tools/kconfig/figures/bool.png b/development-tools/kconfig/figures/bool.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ed985e78d7f395686cfb8883bcc9f1e19114bea
Binary files /dev/null and b/development-tools/kconfig/figures/bool.png differ
diff --git a/development-tools/kconfig/figures/choice.png b/development-tools/kconfig/figures/choice.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d6ef18d9ad0723f6f4f2ff5c6b5c948bfa727e5
Binary files /dev/null and b/development-tools/kconfig/figures/choice.png differ
diff --git a/development-tools/kconfig/figures/comment.png b/development-tools/kconfig/figures/comment.png
new file mode 100644
index 0000000000000000000000000000000000000000..b292a5e687e5e25baebeed5290a03af097070e2a
Binary files /dev/null and b/development-tools/kconfig/figures/comment.png differ
diff --git a/development-tools/kconfig/figures/config.png b/development-tools/kconfig/figures/config.png
new file mode 100644
index 0000000000000000000000000000000000000000..9b9c79b2bed3444a1b4452a06a13b8fdc0b1c330
Binary files /dev/null and b/development-tools/kconfig/figures/config.png differ
diff --git a/development-tools/kconfig/figures/help.png b/development-tools/kconfig/figures/help.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c53605dd8e29c1c55a34a03a745c7e1cbeabce6
Binary files /dev/null and b/development-tools/kconfig/figures/help.png differ
diff --git a/development-tools/kconfig/figures/hex.png b/development-tools/kconfig/figures/hex.png
new file mode 100644
index 0000000000000000000000000000000000000000..83b1ec88efebaa4da6daedf0695348676a7aad39
Binary files /dev/null and b/development-tools/kconfig/figures/hex.png differ
diff --git a/development-tools/kconfig/figures/if0.png b/development-tools/kconfig/figures/if0.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0c11665936c8c0f98feb11763de51a75f52e0fe
Binary files /dev/null and b/development-tools/kconfig/figures/if0.png differ
diff --git a/development-tools/kconfig/figures/if1.png b/development-tools/kconfig/figures/if1.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc3a53c7ffadf66b244c21bd79c91d658ca49a70
Binary files /dev/null and b/development-tools/kconfig/figures/if1.png differ
diff --git a/development-tools/kconfig/figures/if11.png b/development-tools/kconfig/figures/if11.png
new file mode 100644
index 0000000000000000000000000000000000000000..671942c5600d7009d3e11493908403bd45fce650
Binary files /dev/null and b/development-tools/kconfig/figures/if11.png differ
diff --git a/development-tools/kconfig/figures/if2.png b/development-tools/kconfig/figures/if2.png
new file mode 100644
index 0000000000000000000000000000000000000000..f24ebc83c4e0c8a059df593999eea6e6cc738cfd
Binary files /dev/null and b/development-tools/kconfig/figures/if2.png differ
diff --git a/development-tools/kconfig/figures/int.png b/development-tools/kconfig/figures/int.png
new file mode 100644
index 0000000000000000000000000000000000000000..b415032659fb001e043d4ae80a3dd12610e11d07
Binary files /dev/null and b/development-tools/kconfig/figures/int.png differ
diff --git a/development-tools/kconfig/figures/menu.png b/development-tools/kconfig/figures/menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1be5a56f1e248bfc425117858762b28142d64bd
Binary files /dev/null and b/development-tools/kconfig/figures/menu.png differ
diff --git a/development-tools/kconfig/figures/menu1.png b/development-tools/kconfig/figures/menu1.png
new file mode 100644
index 0000000000000000000000000000000000000000..408c9dc2be787ba1d3bcfe256d79ba03c2128dd1
Binary files /dev/null and b/development-tools/kconfig/figures/menu1.png differ
diff --git a/development-tools/kconfig/figures/menuconfig1.png b/development-tools/kconfig/figures/menuconfig1.png
new file mode 100644
index 0000000000000000000000000000000000000000..08a540e06c3c59241b9e4803b125ccb933425fbf
Binary files /dev/null and b/development-tools/kconfig/figures/menuconfig1.png differ
diff --git a/development-tools/kconfig/figures/menuconfig2.png b/development-tools/kconfig/figures/menuconfig2.png
new file mode 100644
index 0000000000000000000000000000000000000000..63a54e76ca192fd7de050e7cacae62a3df2a6d57
Binary files /dev/null and b/development-tools/kconfig/figures/menuconfig2.png differ
diff --git a/development-tools/kconfig/figures/string.png b/development-tools/kconfig/figures/string.png
new file mode 100644
index 0000000000000000000000000000000000000000..b2c88824f1272efa425ff2a9f02d6b290542fc98
Binary files /dev/null and b/development-tools/kconfig/figures/string.png differ
diff --git a/development-tools/kconfig/figures/tristate.png b/development-tools/kconfig/figures/tristate.png
new file mode 100644
index 0000000000000000000000000000000000000000..a42eed73c318f2936a85327453c91ac7f90417ed
Binary files /dev/null and b/development-tools/kconfig/figures/tristate.png differ
diff --git a/development-tools/kconfig/kconfig.md b/development-tools/kconfig/kconfig.md
new file mode 100644
index 0000000000000000000000000000000000000000..5a96fa13c60ca55bc00d6cd00e81faeed91d99f5
--- /dev/null
+++ b/development-tools/kconfig/kconfig.md
@@ -0,0 +1,311 @@
+# Kconfig 语法
+
+## Kconfig 简介
+
+RT-Thread 借助 Kconfig 文件生成的配置文件 rtconfig.h 来配置系统,Kconfig 文件是各种配置界面的源文件。当在 bsp 目录下使用 env 工具执行 menuconfig 命令时会出现 RT-Thread 系统的配置界面,所有配置工具都是通过读取当前 bsp 目录下的 Kconfig 文件来生成配置界面的,这个文件就是所有配置的总入口,它会包含其他目录的 Kconfig 文件。配置工具读取各个 Kconfig 文件,生成配置界面供开发人员配置系统,最终生成 RT-Thread 系统的配置文件 rtconfig.h。
+
+## Kconfig 基本语法
+
+### config 语句
+
+config 定义了一组新的配置选项
+
+以下为 RT-Thread 系统中 config 语句的示例
+
+```c
+config BSP_USING_GPIO
+ bool "Enable GPIO"
+ select RT_USING_PIN
+ default y
+ help
+ config gpio
+```
+
+以上代码对应的配置界面如下所示
+
+
+
+对应的帮助信息界面如下所示
+
+
+
+语句分析:
+- config 表示一个配置选项的开始,紧跟着的 BSP_USING_GPIO 是配置选项的名称,config 下面几行定义了该配置选项的属性。属性可以是该配置选项的
+ - 类型
+ - 输入提示
+ - 依赖关系
+ - 默认值
+ - 帮助信息
+
+- bool 表示配置选项的类型,每个 config 菜单项都要有类型定义,变量有5种类型
+ - bool 布尔类型
+ - tristate 三态类型
+ - string 字符串
+ - hex 十六进制
+ - int 整型
+- select 是反向依赖关系的意思,即当前配置选项被选中,则 RT_USING_PIN 就会被选中。
+- default 表示配置选项的默认值,bool 类型的默认值可以是 y/n。
+- help 帮助信息。
+
+通过 env 选中以上配置界面的选项后,最终可在 rtconfig.h 文件中生成如下两个宏
+
+```c
+#define RT_USING_PIN
+#define BSP_USING_GPIO
+```
+
+### 变量类型
+
+#### bool 类型
+
+布尔类型变量的取值范围是 y/n ,其使用示例如下
+```c
+config BSP_USING_WDT
+ bool "Enable Watchdog Timer"
+ select RT_USING_WDT
+ default n
+```
+
+
+上述语句对应的配置界面如下所示
+
+
+
+以上配置项在 rtconfig.h 文件中生成的宏如下所示
+
+```c
+#define BSP_USING_WDT
+#define RT_USING_WDT
+```
+
+#### string 类型
+
+字符串变量的默认值是一个字符串,其使用示例如下
+
+```c
+config RT_CONSOLE_DEVICE_NAME
+ string "the device name for console"
+ default "uart1"
+```
+
+上述语句对应的配置界面如下所示
+
+
+
+以上配置项在 rtconfig.h 文件中生成的宏如下所示
+
+```c
+#define RT_CONSOLE_DEVICE_NAME "uart1"
+```
+
+#### int 类型
+
+整型变量的取值范围是一个整型的数,其使用示例如下
+
+```c
+config BSP_I2C1_SCL_PIN
+ int "I2C1 scl pin number"
+ range 1 176
+ default 116
+```
+
+上述语句对应的配置界面如下所示
+
+
+
+以上配置项在 rtconfig.h 文件中生成的宏如下所示
+
+```c
+#define BSP_I2C1_SCL_PIN 116
+```
+
+
+#### hex 类型
+
+十六进制类型变量的取值范围是一个十六进制的数,其使用方法和整型变量用法一致,整型变量生成的是十进制的数,而十六进制生成的是十六进制的数。
+
+
+#### tristate 类型
+
+三态类型变量的取值范围是 y、n 和 m。tristate 代表在内核中有三种状态,一种是不选中,一种是选中直接编译进内核,还有一种是 m 手动添加驱动,而布尔类型变量只有两种状态,即选中和不选中。其使用方法和布尔类型变量类似。
+
+
+### menu/endmenu 语句
+
+menu 语句用于生成菜单。
+
+以下为 RT-Thread 系统中 menu/endmenu 语句的示例
+
+```c
+menu "Hardware Drivers Config"
+ config BSP_USING_COM2
+ bool "Enable COM2 (uart2 pin conflict with Ethernet and PWM)"
+ select BSP_USING_UART
+ select BSP_USING_UART2
+ default n
+ config BSP_USING_COM3
+ bool "Enable COM3 (uart3 pin conflict with Ethernet)"
+ select BSP_USING_UART3
+ default n
+endmenu
+
+```
+menu 之后的字符串是菜单名。menu 和 endmenu 间有很多 config 语句,以上代码对应的配置界面如下所示
+
+
+
+其中 Hardware Drivers Config 就是菜单名,然后进入这个菜单有 Enable COM2、Enable COM3 等选项,如下图所示
+
+
+
+
+通过 env 选中以上配置界面的所有选项后,最终可在 rtconfig.h 文件中生成如下五个宏
+
+```c
+#define BSP_USING_UART
+#define BSP_USING_UART2
+#define BSP_USING_UART3
+#define BSP_USING_COM2
+#define BSP_USING_COM3
+```
+
+### if/endif 语句
+
+if/endif 语句是一个条件判断,定义了一个 if 结构,示例代码如下
+
+```c
+menu "Hardware Drivers Config"
+ menuconfig BSP_USING_CAN
+ bool "Enable CAN"
+ default n
+ select RT_USING_CAN
+ if BSP_USING_CAN
+ config BSP_USING_CAN1
+ bool "Enable CAN1"
+ default n
+ endif
+endmenu
+```
+
+当没有选中 "Enable CAN" 选项时,下面通过 if 判断的 Enable CAN1 选项并不会显示出来,如下图所示
+
+
+
+
+当上一级菜单选中 "Enable CAN" 时
+
+
+
+
+
+
+### menuconfig 语句
+
+menuconfig 语句表示带菜单的配置项
+
+以下为 RT-Thread 系统 menuconfig 语句的示例
+```c
+menu "Hardware Drivers Config"
+ menuconfig BSP_USING_UART
+ bool "Enable UART"
+ default y
+ select RT_USING_SERIAL
+ if BSP_USING_UART
+ config BSP_USING_UART1
+ bool "Enable UART1"
+ default y
+
+ config BSP_UART1_RX_USING_DMA
+ bool "Enable UART1 RX DMA"
+ depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
+ default n
+ endif
+endmenu
+```
+
+当没有打开串口 DMA 配置时,以上代码对应的界面为
+
+
+
+当打开串口 DMA 配置时,以上代码对应的界面为
+
+
+
+语句分析:
+- menuconfig 这个语句和 config 语句很相似,但它在 config 的基础上要求所有的子选项作为独立的行显示。
+- depends on 表示依赖某个配置选项,`depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA` 表示只有当 BSP_USING_UART1 和 RT_SERIAL_USING_DMA 配置选项同时被选中时,当前配置选项的提示信息才会出现,才能设置当前配置选项
+
+通过 env 选中以上配置界面的所有选项后,最终可在 rtconfig.h 文件中生成如下五个宏
+
+```c
+#define RT_USING_SERIAL
+#define BSP_USING_UART
+#define BSP_USING_UART1
+#define RT_SERIAL_USING_DMA
+#define BSP_UART1_RX_USING_DMA
+```
+
+### choice/endchoice 语句
+
+choice 语句将多个类似的配置选项组合在一起,供用户选择一组配置项
+
+RT-Thread Kconfig 文件中 choice 代码示例如下
+```c
+menu "Hardware Drivers Config"
+ menuconfig BSP_USING_ONCHIP_RTC
+ bool "Enable RTC"
+ select RT_USING_RTC
+ select RT_USING_LIBC
+ default n
+ if BSP_USING_ONCHIP_RTC
+ choice
+ prompt "Select clock source"
+ default BSP_RTC_USING_LSE
+
+ config BSP_RTC_USING_LSE
+ bool "RTC USING LSE"
+
+ config BSP_RTC_USING_LSI
+ bool "RTC USING LSI"
+ endchoice
+ endif
+endmenu
+```
+以上代码对应的配置界面为
+
+
+
+语句解析:
+- prompt 给出提示信息,光标选中后回车进入就可以看到多个 config 条目定义的配置选项;
+- choice/endchoice 给出选择项,中间可以定义多个配置项供选择,但是在配置界面只能选择一个配置项。
+
+### comment 语句
+
+comment 语句出现在界面的第一行,用于定义一些提示信息。
+
+comment 代码示例如下
+```c
+menu "Hardware Drivers Config"
+ comment "uart2 pin conflict with Ethernet and PWM"
+ config BSP_USING_COM2
+ bool "Enable COM2"
+ select BSP_USING_UART
+ select BSP_USING_UART2
+ default n
+endmenu
+```
+
+以上代码对应的配置界面为
+
+
+
+### source 语句
+
+source 语句用于读取另一个文件中的 Kconfig 文件,如:
+
+```c
+source "../libraries/HAL_Drivers/Kconfig"
+```
+
+上述语句用于读取当前 Kconfig 文件所在路径的上一级文件夹 libraries/HAL_Drivers 下的 Kconfig 文件。
+
diff --git a/development-tools/rtthread-studio/README.md b/development-tools/rtthread-studio/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..99b404b6d67d00c34bf6de546e2fc28252f083d3
--- /dev/null
+++ b/development-tools/rtthread-studio/README.md
@@ -0,0 +1,8 @@
+# 概览
+
+
+
+
+RT-Thread Studio 作为一个开发工具软件,需要有一个从了解到熟悉,从熟悉到能熟练应用的过程,特别是对于以前没有用过基于 eclipse 的开发工具软件的用户,建议先熟悉软件基本使用方法和主要功能入口,然后再尝试进行项目开发,遇到问题可以先参考查阅相关文档和视频教程以及 FAQs,若找不到解决办法,可以在 Studio QQ 群(941959043)或 Studio 论坛发帖,Studio 支持人员会协助解决问题。
+
+
\ No newline at end of file
diff --git a/development-tools/rtthread-studio/applications/project-collection/project-collection.md b/development-tools/rtthread-studio/applications/project-collection/project-collection.md
new file mode 100644
index 0000000000000000000000000000000000000000..b482a89061cb98292a54852c4a175f31daf4a25e
--- /dev/null
+++ b/development-tools/rtthread-studio/applications/project-collection/project-collection.md
@@ -0,0 +1,31 @@
+
+# 精彩项目集合
+
+[DIY 迷你桌面时钟(一)| 基于STM32芯片创建HelloWorld工程](https://blog.csdn.net/Mculover666/article/details/104146623)
+
+[DIY 迷你桌面时钟(二)| 获取温湿度传感器数据(I2C设备驱动+SHT3x软件包)](https://blog.csdn.net/Mculover666/article/details/104153715)
+
+[DIY 迷你桌面时钟(三)| 获取NTP时间(at_device软件包 + netutils软件包)](https://blog.csdn.net/Mculover666/article/details/104418075)
+
+[DIY 迷你桌面时钟(四)| OLED显示时钟和温湿度(cpp组件 + u8g2软件包)](https://blog.csdn.net/Mculover666/article/details/104422501)
+
+[DIY 迷你桌面时钟(五)| 使用内置 Git 插件管理项目](https://blog.csdn.net/Mculover666/article/details/104427445)
+
+
+
+[使用笔记(六)| 获取光传感器数据(I2C设备驱动+BH1750手写驱动代码分享)](https://blog.csdn.net/Mculover666/article/details/104675712)
+
+[使用笔记(七)| 配合STM32CubeMX添加裸机驱动(以ADC为例)](https://blog.csdn.net/Mculover666/article/details/104677481)
+
+[使用笔记(八)| 使用MQTT对接EMQ-X服务器(使用 pahomqtt 包)](https://blog.csdn.net/Mculover666/article/details/104680797)
+
+[使用笔记(九)| 开启OLED显示(使用 u8g2 软件包 c-latest 版本)](https://blog.csdn.net/Mculover666/article/details/104685289)
+
+[项目实战教程 | 快速打造一个桌面mini网络时钟](https://blog.csdn.net/Mculover666/article/details/104428132)
+
+[项目实战教程 | STM32F4在RT-Thread Studio中使用CCM SRAM](https://www.rt-thread.org/qa/thread-423945-1-1.html)
+
+[项目实战教程 | 基于STM32+RT-Thread的新冠肺炎疫情监控平台](https://mp.weixin.qq.com/s/ax7aL460rbFRwztHtDfnqQ)
+
+
+
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/build.png b/development-tools/rtthread-studio/applications/quick-start/figures/build.png
new file mode 100644
index 0000000000000000000000000000000000000000..2cc598d4cbf52be4a31625676c0e5a20c9d19f65
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/build.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/compile.png b/development-tools/rtthread-studio/applications/quick-start/figures/compile.png
new file mode 100644
index 0000000000000000000000000000000000000000..6931f30e5933e0c1e27c9ac500dc7533816dd311
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/compile.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/debug.png b/development-tools/rtthread-studio/applications/quick-start/figures/debug.png
new file mode 100644
index 0000000000000000000000000000000000000000..fdb048b5bf1f7bf619f15ed67aad785b1ae23fdc
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/debug.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/download.png b/development-tools/rtthread-studio/applications/quick-start/figures/download.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea8ccf633e40d6d15b0054bb87793990128eebaf
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/download.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/new-project.png b/development-tools/rtthread-studio/applications/quick-start/figures/new-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..92cea65138f43c6c1dab5ad2e81743641505ba63
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/new-project.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/printf.png b/development-tools/rtthread-studio/applications/quick-start/figures/printf.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ace06e599b9a33991d61f76ca26a9510c8f2645
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/printf.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/red-download.png b/development-tools/rtthread-studio/applications/quick-start/figures/red-download.png
new file mode 100644
index 0000000000000000000000000000000000000000..303e658438542befe5362112efc1527568fd9071
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/red-download.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/figures/terminal.png b/development-tools/rtthread-studio/applications/quick-start/figures/terminal.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d36cb0f182e82adc121f69e006cc49abc708628
Binary files /dev/null and b/development-tools/rtthread-studio/applications/quick-start/figures/terminal.png differ
diff --git a/development-tools/rtthread-studio/applications/quick-start/rtthread-studio-quick-start.md b/development-tools/rtthread-studio/applications/quick-start/rtthread-studio-quick-start.md
new file mode 100644
index 0000000000000000000000000000000000000000..7aeabea3ef20b240e17c4be5826d2ec63bf3e43b
--- /dev/null
+++ b/development-tools/rtthread-studio/applications/quick-start/rtthread-studio-quick-start.md
@@ -0,0 +1,107 @@
+# RT-Thread Studio 快速上手
+
+## 简介
+
+此文档主要介绍如何基于 RT-Thread Studio 新建 RT-Thread 完整版工程,帮助开发者快速上手 RT-Thread Studio。
+
+本文主要内容如下
+
+- 新建 RT-Thread 工程
+
+- 修改 `main.c` 中的引脚信息
+
+- 编译、下载与验证
+
+## 创建 RT-Thread 完整版工程
+
+使用 RT-Thread Studio 新建基于 v4.0.2 的工程,界面如下图所示
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径。
+
+- 选择`基于芯片`创建工程,选择的 RT-Thread 版本为 v4.0.2
+
+- 选择厂商及芯片型号。
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的`完成`按钮即可创建 RT-Thread 完整版工程。创建 nano 版工程是步骤和上述步骤一致,只需选择 RT-Thread 版本为 nano 即可,这里需要注意的是 nano 版无设备的概念。
+
+## 修改 `main.c`
+
+基于 RT-Thread 完整版创建工程时,会自动启用 PIN 设备,在 `main.c` 函数里面会自动生成如下代码
+
+```c
+/* PLEASE DEFINE the LED0 pin for your board, such as: PA5 */
+#define LED0_PIN GET_PIN(A, 5)
+
+int main(void)
+{
+ int count = 1;
+ /* set LED0 pin mode to output */
+ rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
+
+ while (count++)
+ {
+ /* set LED0 pin level to high or low */
+ rt_pin_write(LED0_PIN, count % 2);
+ LOG_D("Hello RT-Thread!");
+ rt_thread_mdelay(1000);
+ }
+
+ return RT_EOK;
+}
+```
+
+使用 PIN 设备时,只需要使用 `GET_PIN` 获取相应的引脚编号,并使用 `rt_pin_write` 等函数来操作引脚编号即可。
+
+例如,`stm32l475-atk-pandora` 开发板的 LED 所接的引脚为 PE7,所以修改为
+
+```c
+#define LED0_PIN GET_PIN(E, 7)
+```
+
+## 编译
+
+鼠标左键单击选中当前工程,在菜单栏单击 `为项目"quick-start"构建"Debug"`按钮即可编译工程,`quick-start` 为自己的工程名。或者使用`右键->构建项目`也可以编译工程。
+
+工程编译结果如下图
+
+
+
+## 下载
+
+工程编译无错误后即可下载程序到自己的板卡。
+
+下载程序可以通过菜单栏的`下载程序`按钮来下载。也可以通过`右键->下载程序`来进行下载,下载程序默认使用的是新建工程时选择的下载方式,如果需要更改,点击菜单栏`下载程序`按钮旁边的倒三角即可根据自己的调试器来选择下载方式。目前 Studio 支持 ST-Link 和 J-Link 这两种下载方式。选择仿真器的示例如下
+
+
+
+选择好仿真器后直接点击下载按钮即可下载
+
+程序下载结果如下图所示
+
+
+
+## 验证
+
+程序下载成功后按下开发板的复位键,并使用 Studio 菜单栏的 `Open a Terminal` 选项来打开一个串口终端,如下图所示
+
+
+
+终端中打印的信息如下
+
+
+
+从终端打印的信息中可以看到,RT-Thread 已经成功运行起来了,同时观察板载的 LED 也可以看到 LED 每隔 1 s 状态就翻转一次。
+
+## 注意事项
+
+- LED 的引脚需要根据自己的板卡进行修改
+
+- nano 版本无设备概念,所以无法使用 PIN 设备
diff --git a/development-tools/rtthread-studio/applications/thread/figures/configuration.png b/development-tools/rtthread-studio/applications/thread/figures/configuration.png
new file mode 100644
index 0000000000000000000000000000000000000000..2bf645b7eaa7d0233192e6f1cbb991ba7061328a
Binary files /dev/null and b/development-tools/rtthread-studio/applications/thread/figures/configuration.png differ
diff --git a/development-tools/rtthread-studio/applications/thread/figures/include.png b/development-tools/rtthread-studio/applications/thread/figures/include.png
new file mode 100644
index 0000000000000000000000000000000000000000..3894aea4937a1883d144e0879b53a5665dd64c6c
Binary files /dev/null and b/development-tools/rtthread-studio/applications/thread/figures/include.png differ
diff --git a/development-tools/rtthread-studio/applications/thread/figures/new-file.png b/development-tools/rtthread-studio/applications/thread/figures/new-file.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a1808b771c4fda535db4b649ac37faefc1676b9
Binary files /dev/null and b/development-tools/rtthread-studio/applications/thread/figures/new-file.png differ
diff --git a/development-tools/rtthread-studio/applications/thread/figures/studio-file.png b/development-tools/rtthread-studio/applications/thread/figures/studio-file.png
new file mode 100644
index 0000000000000000000000000000000000000000..22fe7befb978a73fd19e95c1755f1145ed37968e
Binary files /dev/null and b/development-tools/rtthread-studio/applications/thread/figures/studio-file.png differ
diff --git a/development-tools/rtthread-studio/applications/thread/figures/terminal.png b/development-tools/rtthread-studio/applications/thread/figures/terminal.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d4f5f57c2645290c6c9274c3b37d886b96d6b69
Binary files /dev/null and b/development-tools/rtthread-studio/applications/thread/figures/terminal.png differ
diff --git a/development-tools/rtthread-studio/applications/thread/figures/uart-level.png b/development-tools/rtthread-studio/applications/thread/figures/uart-level.png
new file mode 100644
index 0000000000000000000000000000000000000000..39f4df6b33a9d1e6e085bc831c3d0340e34649a8
Binary files /dev/null and b/development-tools/rtthread-studio/applications/thread/figures/uart-level.png differ
diff --git a/development-tools/rtthread-studio/applications/thread/figures/uart-uml.png b/development-tools/rtthread-studio/applications/thread/figures/uart-uml.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d1969f8276b514a04bdcebc95b0197aadd7ff3b
Binary files /dev/null and b/development-tools/rtthread-studio/applications/thread/figures/uart-uml.png differ
diff --git a/development-tools/rtthread-studio/applications/thread/rtthread-studio-thread.md b/development-tools/rtthread-studio/applications/thread/rtthread-studio-thread.md
new file mode 100644
index 0000000000000000000000000000000000000000..7f9ea3378fec70c8e60e0a3af082ee2783621ff2
--- /dev/null
+++ b/development-tools/rtthread-studio/applications/thread/rtthread-studio-thread.md
@@ -0,0 +1,142 @@
+# RT-Thread Studio 应用开发示例 - 线程
+
+## 简介
+
+本文档将主要介绍如何基于 RT-Thread Studio 创建并启动第一个线程。
+
+## 启动第一个线程
+
+创建线程时,可以在 `main.c` 文件中加入自己创建线程的代码,也可以另外新建源文件和头文件来存放创建线程的代码,本文将以新建文件的方式创建线程。
+
+启动第一个线程的步骤主要如下
+
+- 新建源文件及头文件
+
+- 将头文件路径添加到工程
+
+- 创建线程
+
+- `main` 函数中调用
+
+
+### 新建文件
+
+创建文件夹、源文件及头文件时,可以基于 Studio 进行创建,也可以在本地创建好之后拷贝到工程路径中。这里以 Studio 新建文件为例。
+
+使用 Studio 新建文件时,参考以下步骤
+
+- 在新文件需要存放的目录下右键
+
+- 选择 `新建` 选项
+
+- 选择自己需要新建的文件类型,如:文件夹、源文件和头文件等。
+
+如下图所示
+
+
+
+
+本例中,在 application 目录下新建 `my-application` 文件夹,在该文件夹下,分别存放 `my_application.c` 源文件和 `my_applications.h` 头文件。
+
+创建文件结果如如下图所示
+
+
+
+这里需要注意的是,基于 Studio 构建工程时,Studio 会默认将工程路径下的所有源文件都参与编译,所以新建的源文件无需做任何配置即可参与工程的编译,而头文件则需要手动添加到工程中。
+
+### 添加头文件路径
+
+应用程序中如果需要包含新添加的头文件,那么就需要将新添加的头文件路径添加到工程中。添加头文件路径的过程如下
+
+- 选择菜单栏的  选项(打开构建配置)
+
+- C/C++ 构建
+
+- 设置
+
+- 如果是 C 的头文件选择 `GNU ARM Cross Compiler`,如果是汇编的头文件则选择 `GNU ARM Cross Assembler`
+
+- 选择头文件包含 `Includes`
+
+- 添加自己的头文件路径
+
+示例工程添加头文件的结果如下图所示
+
+
+
+
+### 创建线程
+
+在 `my_application.c` 文件中创建示例线程,如下代码所示
+```c
+#include
+
+#define THREAD_PRIORITY 25
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+
+/* 使用静态线程时,线程的栈需要设置字节对齐 */
+ALIGN(RT_ALIGN_SIZE)
+static rt_uint8_t thread_stack[THREAD_STACK_SIZE];
+static struct rt_thread tid1;
+
+/* 线程 1 的入口函数 */
+static void thread1_entry(void *parameter)
+{
+ rt_uint32_t count = 0;
+
+ while (1)
+ {
+ rt_kprintf("thread1 count: %d\n", count ++);
+ rt_thread_mdelay(500);
+ }
+}
+
+/* 线程示例 */
+int thread_sample(void)
+{
+ /* 静态创建线程 */
+
+ /* 初始化线程 1,名称是 thread1,入口是 thread1_entry*/
+ rt_thread_init(&tid1,
+ "thread1",
+ thread1_entry,
+ RT_NULL,
+ thread_stack,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY,
+ THREAD_TIMESLICE);
+ /* 启动线程 */
+ rt_thread_startup(&tid1);
+
+ return 0;
+}
+```
+
+在 `my_application.h` 头文件中加入函数声明
+```c
+int thread_sample(void);
+```
+
+### 编译下载&验证
+
+在 `main.c` 文件中包含 `my_application.h` 头文件,同时在 main 函数中调用 `thread_sample` 函数,如下所示
+```c
+#include
+#include "my_application.h"
+
+int main(void)
+{
+ thread_sample();
+
+ return RT_EOK;
+}
+```
+
+编译并下载程序,打开串口终端,输出信息如下
+
+
+
+从终端输出的信息可以看出,创建的第一个线程已经成功运行了。
+
+更多关于线程操作和示例请参考 [线程管理](https://www.rt-thread.org/document/site/programming-manual/thread/thread/)
diff --git a/development-tools/rtthread-studio/applications/uart/figures/dev-precsss.vsdx b/development-tools/rtthread-studio/applications/uart/figures/dev-precsss.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..83e20869a4452f557c74390e784fa1991b521c69
Binary files /dev/null and b/development-tools/rtthread-studio/applications/uart/figures/dev-precsss.vsdx differ
diff --git a/development-tools/rtthread-studio/applications/uart/figures/list_device.png b/development-tools/rtthread-studio/applications/uart/figures/list_device.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5c81842a032493effa644c770932cca17d07a0d
Binary files /dev/null and b/development-tools/rtthread-studio/applications/uart/figures/list_device.png differ
diff --git a/development-tools/rtthread-studio/applications/uart/figures/new-file.png b/development-tools/rtthread-studio/applications/uart/figures/new-file.png
new file mode 100644
index 0000000000000000000000000000000000000000..354f1cd4919dc4da9c16044af03e6480968ee209
Binary files /dev/null and b/development-tools/rtthread-studio/applications/uart/figures/new-file.png differ
diff --git a/development-tools/rtthread-studio/applications/uart/figures/process.png b/development-tools/rtthread-studio/applications/uart/figures/process.png
new file mode 100644
index 0000000000000000000000000000000000000000..24c66de67de49778f24e81832433a8d3fb80e259
Binary files /dev/null and b/development-tools/rtthread-studio/applications/uart/figures/process.png differ
diff --git a/development-tools/rtthread-studio/applications/uart/figures/uart-recv.png b/development-tools/rtthread-studio/applications/uart/figures/uart-recv.png
new file mode 100644
index 0000000000000000000000000000000000000000..c481d6c10299ee2890e22cb1a05548f1e0eb5452
Binary files /dev/null and b/development-tools/rtthread-studio/applications/uart/figures/uart-recv.png differ
diff --git a/development-tools/rtthread-studio/applications/uart/figures/uart_sample.png b/development-tools/rtthread-studio/applications/uart/figures/uart_sample.png
new file mode 100644
index 0000000000000000000000000000000000000000..c21ca30a5ad10f5a5af8c9addc6144ccf4feb47c
Binary files /dev/null and b/development-tools/rtthread-studio/applications/uart/figures/uart_sample.png differ
diff --git a/development-tools/rtthread-studio/applications/uart/rtthread-studio-uart.md b/development-tools/rtthread-studio/applications/uart/rtthread-studio-uart.md
new file mode 100644
index 0000000000000000000000000000000000000000..3597bf15e9da812801c3d84ec5e9d51ff7249d1f
--- /dev/null
+++ b/development-tools/rtthread-studio/applications/uart/rtthread-studio-uart.md
@@ -0,0 +1,125 @@
+# RT-Thread Studio 应用开发示例 - 串口设备
+
+## 简介
+
+在 RT-Thread 中设备的开发总体上可以分为两类:设备的驱动开发和设备的应用开发。
+
+- 设备驱动开发的主要工作是操作硬件,对接设备驱动框架,并注册设备到系统中。
+
+- 设备应用开发的主要工作是直接调用操作系统提供的接口,例如:`rt_device_find`、`rt_device_open` 等函数,完成上层的业务逻辑。
+
+所以在 RT-Thread 中,对于一个具体的设备,其自底向上的开发流程可总结为如下步骤
+
+- 打开设备驱动框架支持
+
+- 对接设备驱动框架提供的接口函数
+
+- 注册设备到系统中
+
+- 使用操作系统提供 API 操作设备
+
+流程图如下所示
+
+
+
+## 新增设备
+
+本文将以串口为例,主要讲解串口设备的应用开发。串口设备驱动的开发和新增串口设备请参考 [串口设备驱动开发](https://www.rt-thread.org/document/site/rtthread-studio/drivers/uart/v4.0.2/rtthread-studio-uart-v4.0.2/)
+
+## 查找设备
+
+使用设备前需要确保系统中已经成功注册了该设备,查找设备是否已经注册,可以在命令行中使用 `list_device` 命令来查找设备,例如本例中需要使用串口 2 ,命令行查询结果如下
+
+
+
+从结果可以看出设备已经成功注册到系统中了。
+
+应用程序也可以根据设备名称来查找设备。查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+|**参数**|**描述** |
+|----------|------------------------------------|
+| name | 设备名称 |
+|**返回**| —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+## 使用设备
+
+当需要使用的设备成功注册到系统中时,该设备就可以直接通过系统提供的接口来进行访问了。
+
+例如通用设备可以使用 `rt_device_read`、`rt_device_write` 等函数来进行数据的传输, I2C 设备可以使用 `rt_i2c_transfer` 等函数进行数据传输。更多设备相关的 API 请查看 [设备与驱动程序](https://www.rt-thread.org/document/site/programming-manual/device/device/) 中对应的设备。
+
+本例中需要使用的是串口 2,在 `application` 目录下新建 `my_uart.c` 源文件,如下图所示
+
+
+
+并在 `my_uart.c` 文件中添加如下串口设备的示例程序
+```c
+#include
+
+#define SAMPLE_UART_NAME "uart2" /* 需要操作的设备 */
+static rt_device_t serial; /* 设备句柄 */
+static char str[] = "hello RT-Thread!\r\n"; /* 需要发送的数据 */
+
+static int uart_sample(void)
+{
+ rt_err_t ret = RT_EOK;
+ rt_size_t send_len = 0;
+
+ /* 查找系统中的串口设备 */
+ serial = rt_device_find(SAMPLE_UART_NAME);
+ if (!serial)
+ {
+ rt_kprintf("find %s failed!\n", SAMPLE_UART_NAME);
+ return -RT_ERROR;
+ }
+
+ /* 以中断接收及轮询发送模式打开串口设备 */
+ ret = rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("open device failed\r\n");
+ return -RT_ERROR;
+ }
+ /* 发送字符串 */
+ send_len = rt_device_write(serial, 0, str, (sizeof(str) - 1));
+ if (send_len != sizeof(str) - 1)
+ {
+ rt_kprintf("send data failed\r\n");
+ return -RT_ERROR;
+ }
+ /* 关闭设备 */
+ ret = rt_device_close(serial);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("close device failed\r\n");
+ return -RT_ERROR;
+ }
+
+ rt_kprintf("serial device test successful\r\n");
+
+ return RT_EOK;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(uart_sample, uart device sample);
+```
+
+编译并下载程序。在控制台中输入 `uart_sample` 命令,结果如下图
+
+
+
+使用串口工具将开发板上的串口 2 连接到电脑,可以看到开发板串口 2 收到的数据如下
+
+
+
+从上面的实验中可以看到我们成功使用系统提供的接口向串口 2 输出了数据,更多关于串口设备的使用请参看 [串口设备](https://www.rt-thread.org/document/site/programming-manual/device/uart/uart/)
+
+## 注意事项
+
+- 设备能够使用的前提是已经注册成功
+
+- nano 版没有设备的概念
diff --git a/development-tools/rtthread-studio/changelog/changelog.md b/development-tools/rtthread-studio/changelog/changelog.md
new file mode 100644
index 0000000000000000000000000000000000000000..7064ce76d32c89166996d99426d846780b16a0d8
--- /dev/null
+++ b/development-tools/rtthread-studio/changelog/changelog.md
@@ -0,0 +1,199 @@
+## 更新日志
+
+##### [V2.0.0更新发布](https://club.rt-thread.org/ask/article/2393.html)
+
+- 新增集成platformio裸机框架功能
+- 新增Rebuild功能(自动先清理工程再重新构建工程)
+- 新增开发板详细信息查看入口
+- 新增Studio工程模板支持包方便制作开发板支持包的模板工程
+- 新增支持jlink通过tcp/ip连接下载和调试
+- 新增QEMU支持网络功能
+- 新增支持arm-linux-musleabi编译RT-Thread Smart内核
+- 新增FAQ-添加软件包后在packages目录找不到的解决方法
+- 新增FAQ-如何解决清理工程时命令行超出windows长度限制
+- 完善链接脚本编辑器兼容更多格式链接脚本
+- 解决QEMU网络配置没有正常生效的问题
+- 解决RT-Thread配置项出现重复配置节点问题
+- 解决RT-Thread配置节点展开时不停重复创建配置项节点问题
+
+##### [V1.1.5更新发布](https://club.rt-thread.org/ask/article/2290.html)
+
+- 新增ART-PI开发板支持(请先升级至V1.1.5,再去sdk下载ART-PI开发板资源包V1.0.0版本)
+- 新增STLINK Debugger调试软件v1.4.0版本资源包
+- 新增STLINK调试软件的版本切换
+- 新增SDK资源包显示更新时间
+- 新增GD32VF103-NUCLEI-RVSTAR开发板v0.1.1版本资源包
+- 完善RISCV工程构建信息展示和调试外设寄存器显示问题
+- 完善开发板大图查看
+- 解决开发板信息展示页面购买链接不显示的问题
+- 解决工程构建时链接命令长度超出windows长度限制问题(见[FAQ](https://www.rt-thread.org/document/site/rtthread-studio/faq/studio-faq/#studio-windows))
+- 解决只修改了链接脚本构建无效的问题
+- 解决完整安装包体积过大问题
+- 解决pyocd烧写问题(切换成烧写bin文件)
+
+##### [V1.1.4更新发布](https://club.rt-thread.org/ask/article/306.html)
+
+- 工程向导开发和完善
+ 1.新增基于开发板创建工程模式替代老的基于bsp模式
+
+- 工具链支持
+ 1.新增RISC-V工具链支持
+
+- 构建功能完善
+ 1.解决停止并启动调试没自动构建的问题
+ 2.解决只修改了链接脚本后构建无效的问题
+
+- SDK Manager完善
+ 1.新增开发板类型资源包
+ 2.完善美化SDK Manger图标
+ 3.解决SDK资源包安装失败的问题
+
+- 新开发板支持
+ 1.新增IMXRT1064/1052开发板的支持
+
+- RT-Thread配置完善
+ 1.解决LWIP配置选项ICMP名称问题
+ 2.解决RT-Thread配置页面在构建时没有自动保存的问题
+
+- QEMU功能完善
+ 1.新增芯来rvstar开发板qemu仿真支持
+ 2.新增vexpress-a9的qemu仿真支持
+ 3.新增qemu点击下载直接启动运行模式
+ 4.新增STM32 sdio模拟支持
+
+- 调试器功能完善
+ 1.新增ST-LINK调试支持复位
+ 2.新增ST-LINK调试支持查看外设寄存器
+ 3.新增ST-LINK外部FLASH下载算法支持
+ 4.解决ST-LINK下载Verify时日志出现乱码的问题
+ 5.新增DAP-LINK对多下载算法的支持
+ 6.新增DAP-LINK调试器对雅特力芯片下载和调试的支持
+ 7.新增DAP-LINK下载调试参数配置便于加快下载速度
+ 8.解决J-Link下载时工程路径有空格弹框需要手动输入起始地址的问题
+
+- 语言切换完善
+ 1.解决切换到英文后部分窗口仍有中文的问题
+
+##### [V1.1.3更新发布](https://club.rt-thread.org/ask/article/45.html)
+
+- 新增支持新建工程时选择DAP-Link调试器功能(暂时不支持stm32H7 系列)
+- 新增支持新建工程时选择QEMU模拟处理器功能
+- 新增新建RT-Thread Nano项目功能
+- 新增在 “关于RT-Thread Studio” 菜单查看更新日志功能
+- 完善切换语言的功能,切换后自动重启studio
+- 解决导入工程未识别到芯片时无法退出的问题
+
+##### [V1.1.2 更新发布](https://club.rt-thread.org/ask/article/17.html)
+
+- 新增新建工程向导"添加更多"芯片系列选项
+- 新增启动时自动检测并安装必需的内置资源包
+- 新增J-Link软件支持包6.80d版本(支持雅特力芯片)
+- 完善切换芯片支持包版本功能
+- 完善J-Link调试配置提示信息
+- 完善SDK Manager离线资源包导入功能
+- 解决生成工程时board.h少了大括号的问题
+
+##### [V1.1.1更新发布](https://www.rt-thread.org/qa/thread-424893-1-1.html)
+
+- 新增切换工程芯片支持包功能
+- 新增切换工程RT-Thread版本功能
+- 新增启动调试时先自动构建功能
+- 新增创建工程默认设置为UTF8编码
+- 完善切换工程芯片功能
+- 完善下载和调试快捷键
+- 完善新建工程向导
+- 完善新建工程失败信息提示框
+- 完善调试配置内路径保存为相对路径
+- 完善SDK Manager离线资源包导入功能
+- 解决树形配置列宽拖动会抖动问题
+- 解决配置树点击过快弹框问题
+- 解决部分软件包添加失效问题
+- 解决SDK Manager安装状态图标显示异常问题
+
+##### [V1.1.0更新发布](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=424576&page=1#pid478987)
+
+- 新增ST-Link指定扇区擦除功能
+- 新增Code Analysis功能
+- SDK Manager全新升级
+- 新建工程向导全新升级
+
+##### [V1.0.6更新发布](https://www.rt-thread.org/qa/thread-424221-1-1.html)
+
+- 新增导入MDK/IAR工程入口和功能
+- 新增编译结果显示ROM/RAM使用情况
+- 完善基于芯片的工程创建向导
+- 完善搜索和替换功能
+- 优化配置器打开速度
+
+
+##### [V1.0.5更新发布](https://www.rt-thread.org/qa/thread-424084-1-1.html)
+
+- 新增STM32L1系列芯片支持(仅latest版本支持)
+- 完善工程生成添加更多可用外设驱动
+- 完善链接脚本编辑器
+- 完善软件包下载日志
+- 解决导入带空格路径下工程下载失败问题
+- 解决导入工程后下载提示无调试配置的问题
+- 优化配置器打开速度 修复部分显示问题
+
+
+##### [V1.0.4更新发布](https://www.rt-thread.org/qa/thread-423911-1-1.html)
+
+- 新增编译日志简化输出和详细输出功能
+- 新增显示当前工程RT-Thread版本功能
+- 新增link.lds文件图形编辑器功能
+- 完善导入工程时中文路径检查
+- 优化调整RT-Thread配置界面
+- 解决软件包版本显示不一致问题
+- 解决导入工程后下载提示无调试配置的问题
+- 解决5.0以下版本JLink下载结束后进程驻留问题
+
+
+##### [V1.0.3更新发布](https://www.rt-thread.org/qa/thread-423751-1-1.html)
+
+- 新建工程向导芯片列表更新
+- 调整工程向导基于芯片创建优先
+- 调整F1系列芯片默认选择103VE
+- 完善树形配置界面提示框中信息展示
+- 解决软件包版本显示不正确的问题
+- 解决工程重命名后芯片切换和调试配置打不开问题
+- 解决删除工程时占用异常的问题
+- 升级工程生成器
+
+
+##### [V1.0.2更新发布](https://www.rt-thread.org/qa/thread-423613-1-1.html)
+
+- 新增美观完善的DevStyle黑色主题
+- 新增ST-Link程序下载后自动运行和配置功能
+- 新增问题反馈菜单导向Studio论坛
+- 新增配置项依赖信息查看
+- 优化完善串口终端功能
+- 解决C++支持问题
+
+
+##### V1.0.1更新发布
+
+- 新增项目创建向导控制台串口LPUART1选项
+- 新增树形配置搜索功能右键菜单(Ctrl+F),便于查找配置项
+- 新增树形配置界面一键最大化功能按钮,便于树形配置操作
+- 新增Drivers组件开发文档右键功能菜单,便于查看开发文档
+- 新增moonrise主题功能插件
+- 新增RT-Thread类别的工程导入入口
+- 新增启用C++组件时工程自动添加C++支持
+- 解决树形配置出现重复节点的问题
+- 解决欢迎页在部分电脑不显示的问题
+- 解决软件包版本无法修改的问题
+- 简化JLink配置,去掉JLink下载配置页
+- 采用新的终端软件putty
+- 采用带封装命名的芯片名形式
+- 优化完善工程生成器
+
+
+
+
+
+
+
+
+
+
diff --git a/development-tools/rtthread-studio/drivers/cubemx/figures/adc-gnd.png b/development-tools/rtthread-studio/drivers/cubemx/figures/adc-gnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..53974f56f7d24d94c5ecebb868f962be5bcc8d42
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/cubemx/figures/adc-gnd.png differ
diff --git a/development-tools/rtthread-studio/drivers/cubemx/figures/adc-hal.png b/development-tools/rtthread-studio/drivers/cubemx/figures/adc-hal.png
new file mode 100644
index 0000000000000000000000000000000000000000..72cdc6ffe53fcf2d1bc957ea1471ebf53f287ae8
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/cubemx/figures/adc-hal.png differ
diff --git a/development-tools/rtthread-studio/drivers/cubemx/figures/adc-vcc.png b/development-tools/rtthread-studio/drivers/cubemx/figures/adc-vcc.png
new file mode 100644
index 0000000000000000000000000000000000000000..66bd878d87f7acf0383cc8ff173d5220b3e1c146
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/cubemx/figures/adc-vcc.png differ
diff --git a/development-tools/rtthread-studio/drivers/cubemx/figures/board-paste.png b/development-tools/rtthread-studio/drivers/cubemx/figures/board-paste.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e0ea7d1578a0b355a19e6824a0d470716e75f6e
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/cubemx/figures/board-paste.png differ
diff --git a/development-tools/rtthread-studio/drivers/cubemx/figures/cubemx.png b/development-tools/rtthread-studio/drivers/cubemx/figures/cubemx.png
new file mode 100644
index 0000000000000000000000000000000000000000..c27eb87a3f8c149c10e104accd3afeee152f4db8
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/cubemx/figures/cubemx.png differ
diff --git a/development-tools/rtthread-studio/drivers/cubemx/figures/new-adc.png b/development-tools/rtthread-studio/drivers/cubemx/figures/new-adc.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7960d23e3c226f0f013f636f78cf4c10546d11a
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/cubemx/figures/new-adc.png differ
diff --git a/development-tools/rtthread-studio/drivers/cubemx/rtthread-studio-cubemx.md b/development-tools/rtthread-studio/drivers/cubemx/rtthread-studio-cubemx.md
new file mode 100644
index 0000000000000000000000000000000000000000..f7bbeef31523d5a1fefd331d488231d108137e87
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/cubemx/rtthread-studio-cubemx.md
@@ -0,0 +1,198 @@
+# RT-Thread Studio 结合 STM32CubeMx 开发其他驱动文档
+
+使用 `RT-Thread Studio` 新建 RT-Thread 的项目时直接就将 RT-Thread 实时操作系统移植到对应的芯片上了,省去了系统移植的步骤。
+
+使用 `STM32CubeMx` 配置工具可以方便快速的配置芯片外设的时钟和引脚,使驱动的开发变得简单。
+
+所以本文将结合这两个 IDE 的优点,介绍基于 `RT-Thread Studio` 和 `STM32CubeMx` 的驱动开发。
+
+需要注意的是这里开发的驱动是不基于 RT-Thread 设备驱动框架的,即直接使用 `STM32CubeMx` 生成的 HAL 库文件来开发外设驱动。
+
+## 简介
+
+使用 `RT-Thread Studio` 和 `STM32CubeMx` 开发驱动可分为以下几个步骤
+
+- 使用 `RT-Thread Studio` 新建 RT-Thread 工程
+
+- 使用 `STM32CubeMx` 配置外设和系统时钟
+
+- 复制 `stm32xxxx_hal_msp.c` 函数
+
+- 修改 `stm32xxxx_hal_config.h` 文件,打开相应外设支持。
+
+- 替换 `board.c ` 文件中时钟配置函数
+
+- 使用外设
+
+## 新建 RT-Thread 项目
+
+使用 `RT-Thread Studio` 新建基于 `nano-v3.1.3` 的工程,界面如下图所示
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择 `基于芯片` 创建工程,选择的 RT-Thread 版本为 `nano-v3.1.3`。
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的 `完成` 按钮即可创建 RT-Thread 的工程。
+
+基于 Studio 创建 RT-Thread 工程后,就可以基于创建的工程开发自己的驱动。下面将以 `stm32l475-atk-pandora` 开发板为例,讲解如何开发 ADC 驱动。
+
+## 配置外设
+
+新建基于目标板卡的 `CubeMx` 工程,并配置自己需要使用的外设。
+
+例如,示例开发板的 PC2 连接的是 ADC1 的通道 3,使用 `CubeMx` 生成 ADC 的驱动代码的配置结果如下所示:
+
+
+
+
+## 复制 `stm32xxxx_hal_msp.c` 函数
+
+将 `CubeMx` 生成的代码 `stm32l4xx_hal_msp.c` 函数复制到 RT-Thread Studio 生成的工程中,并参与工程编译。复制完成后的结果如下图所示
+
+
+
+由于我们并没有使用 CubeMx 生成的工程,所以这里需要将 `stm32l4xx_hal_msp.c` 文件中 `#include "main.h"` 替换为 `#include "board.h"`。
+
+## 打开 HAL 库配置文件对应外设的支持宏
+
+这里我们使用了 ADC 外设,所以需要在 `stm32l4xx_hal_config.h` 文件中将 ADC 模块使能,取消 ADC 模块的注释即可,示例代码如下
+
+```c
+#define HAL_ADC_MODULE_ENABLED
+```
+
+## 修改系统时钟(可选)
+
+使用 RT-Thread Studio 创建 RT-Thread 工程时默认使用的是系统内部时钟 HSI,这里需要根据自己的板卡配置将 `STM32CubeMx` 生成的时钟配置函数拷贝到 RT-hread 的工程中。步骤如下
+
+- 使用 CubeMx 配置自己板卡的系统时钟,并生成代码
+
+- 复制 CubeMx 工程中 `main.c` 文件的 `void SystemClock_Config(void)` 系统时钟初始化函数
+
+- 替换 `RT-Thread Studio` 生成的工程中的 `drv_clk.c` 文件中的系统时钟配置函数`void system_clock_config(int target_freq_mhz)` ,如下图所示。
+
+ 
+
+- 如果使用外部时钟,则需要更新工程中的`stm32xxxx_hal_conf.h` 中的对应的外部时钟频率的值,以 HSE 为例,需要修改下面的时钟频率为实际使用的值:
+
+ ```c
+
+ #define HSE_VALUE ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
+
+ ```
+
+
+
+## 使用外设
+
+上述文件配置完成之后,ADC 外设就可以使用的,在 `main.c` 中添加外设的使用代码
+
+ADC 外设的使用示例代码如下
+
+```c
+#include
+#include "board.h"
+
+ADC_HandleTypeDef hadc1;
+
+/* ADC1 init function */
+void MX_ADC1_Init(void)
+{
+ ADC_MultiModeTypeDef multimode = {0};
+ ADC_ChannelConfTypeDef sConfig = {0};
+
+ /** Common config
+ */
+ hadc1.Instance = ADC1;
+ hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
+ hadc1.Init.Resolution = ADC_RESOLUTION_12B;
+ hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
+ hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
+ hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
+ hadc1.Init.LowPowerAutoWait = DISABLE;
+ hadc1.Init.ContinuousConvMode = DISABLE;
+ hadc1.Init.NbrOfConversion = 1;
+ hadc1.Init.DiscontinuousConvMode = DISABLE;
+ hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
+ hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
+ hadc1.Init.DMAContinuousRequests = DISABLE;
+ hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
+ hadc1.Init.OversamplingMode = DISABLE;
+ if (HAL_ADC_Init(&hadc1) != HAL_OK)
+ {
+ Error_Handler();
+ }
+ /** Configure the ADC multi-mode
+ */
+ multimode.Mode = ADC_MODE_INDEPENDENT;
+ if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
+ {
+ Error_Handler();
+ }
+ /** Configure Regular Channel
+ */
+ sConfig.Channel = ADC_CHANNEL_3;
+ sConfig.Rank = ADC_REGULAR_RANK_1;
+ sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
+ sConfig.SingleDiff = ADC_SINGLE_ENDED;
+ sConfig.OffsetNumber = ADC_OFFSET_NONE;
+ sConfig.Offset = 0;
+ if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+}
+
+rt_uint32_t get_adc_value(void)
+{
+ HAL_ADC_Start(&hadc1);
+ HAL_ADC_PollForConversion(&hadc1, 10);
+
+ return (rt_uint32_t)HAL_ADC_GetValue(&hadc1);
+}
+
+int main(void)
+{
+ int count = 1;
+ rt_uint32_t read_value = 0;
+
+ MX_ADC1_Init();
+ while (count++)
+ {
+ read_value = get_adc_value();
+ rt_thread_mdelay(1000);
+ rt_kprintf("adc value = %d\r\n", read_value);
+ }
+
+ return RT_EOK;
+}
+```
+
+编译下载工程,将开发板的 PC2 引脚连接到开发板上的地,终端打印信息如下
+
+
+
+将开发板的 PC2 引脚连接到开发板的 3.3V 引脚,终端打印信息如下
+
+
+
+从上面两个实验打印结果可以看出我们成功使用了 ADC 外设。
+
+## 注意事项
+
+- `board.c` 文件中的系统时钟配置函数需要根据自己的板卡进行修改
+
+- `stm32xxxx_hal_msp.c` 函数中主要完成的是外设引脚和时钟的初始化,所以在使用 `CubeMx` 生成外设的配置代码时不能选择为每个外设都生成 `.c/.h` 文件
+
+- 使用 `CubeMx` 外设时只需要配置实际使用的外设,如果 `stm32xxxx_hal_msp.c` 文件和 `drv_uart.c` 文件或者 `drv_spi.c` 文件外设的初始化函数重定义,需要删除 `stm32xxxx_hal_msp.c` 文件中外设的初始化函数。
\ No newline at end of file
diff --git a/development-tools/rtthread-studio/drivers/drv-list/support-driver-list.md b/development-tools/rtthread-studio/drivers/drv-list/support-driver-list.md
new file mode 100644
index 0000000000000000000000000000000000000000..2ead247995cbfda5a6bfea561422b14e68aeacf1
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/drv-list/support-driver-list.md
@@ -0,0 +1,58 @@
+# RT-Thread Studio 驱动支持概况
+
+本文将介绍 RT-Thread Studio 对于硬件外设驱动的支持情况。由于 RT-Thread nano 版本不具有设备概念,所以本文提到的 RT-Thread 为完整版本。nano 版建议直接使用 HAL 库进行驱动开发。
+
+## 驱动概况
+
+当前 RT-Thread Studio 驱动开发方式主要分为二种
+
+- 1、由 RT Thread Studio 自动生成,无需修改任何文件或者简单定义几个宏即可直接使用的驱动,如 GPIO,UART,I2C,SPI,SDIO 和 ETH 等。
+
+- 2、没有对接到设备驱动框架,可直接使用 HAL 库函数进行开发的驱动,如 DAC,FSMC 等。
+
+RT-Thread Studio (截止V1.0.4)的驱动支持情况见下表
+
+| 外设 | Studio 自动生成 | HAL 库函数开发 |
+|------------|-----------------------|----------------|
+| PIN | 支持 | 支持 |
+| UART | 支持 | 支持 |
+| I2C | 支持 | 支持 |
+| SPI | 支持 | 支持 |
+| QSPI | 支持 | 支持 |
+| WDT | 支持 | 支持 |
+| PWM | 支持 | 支持 |
+| RTC | 支持 | 支持 |
+| FLASH | 支持 | 支持 |
+| SDIO | 支持 | 支持 |
+| USB HOST | 支持 | 支持 |
+| USB DEVICE | 支持 | 支持 |
+| ETH | 支持 | 支持 |
+| TIMER | 支持(推荐使用 HAL 库) | 支持 |
+| ADC | 支持(推荐使用 HAL 库) | 支持 |
+| DAC | 不支持 | 支持 |
+| FSMC | 不支持 | 支持 |
+| CAN | 不支持 | 支持 |
+
+## 使用简介
+
+### Studio 自动生成的驱动
+
+使用 RT-Thread Studio 新建完整版工程时,用户不需要修改任何代码,例如:
+
+- PIN :该驱动对接到了设备驱动框架并且可以直接使用,具体 PIN 的使用参考 [PIN设备](https://www.rt-thread.org/document/site/rtthread-studio/drivers/pin/rtthread-studio-pin/)
+
+- UART :对于串口驱动来说,生成工程的时候串口驱动也自动对接到了设备驱动框架,使用时需要自己定义串口号以及串口引脚对应的宏,具体参考 [串口设备](https://www.rt-thread.org/document/site/rtthread-studio/drivers/uart/v4.0.2/rtthread-studio-uart-v4.0.2/)
+
+- I2C :软件 I2C 的驱动在使用 RT-Thread Studio 自动生成工程时也对接到了设备驱动框架,软件 I2C 驱动的开发参考 [软件模拟I2C设备](https://www.rt-thread.org/document/site/rtthread-studio/drivers/soft-i2c/rtthread-studio-soft-i2c/)
+
+- SPI :SPI 设备驱动的开发参考 [SPI设备](https://www.rt-thread.org/document/site/rtthread-studio/drivers/spi/rtthread-studio-spi/)
+
+- 更多设备:更多设备驱动的开发请参考 RT-Thread 官网文档中心的相关章节。
+
+> 提示:虽然 ADC 及 Timer 外设已经对接到了设备驱动框架,但是其功能不够完善,例如:不支持 DMA,定时器的一些高级功能无法使用等。对于这类驱动,目前推荐使用 HAL 库函数方式进行开发。
+
+### 直接使用 HAL 库函数开发的驱动
+
+虽然可以通过 Studio 自动或者手动方式添加设备驱动代码,但是还是有些外设暂时无法对接到设备框架
+
+此时用户直接按照 HAL 库开发的方式,开发对应的驱动即可。具体开发方式可以参考[RT-Thread Studio 结合 STM32CubeMx 开发其他驱动文档](https://www.rt-thread.org/document/site/rtthread-studio/drivers/cubemx/rtthread-studio-cubemx/)
diff --git a/development-tools/rtthread-studio/drivers/eth/figures/detail-config.png b/development-tools/rtthread-studio/drivers/eth/figures/detail-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..88083cc984b2fbcc6661baa1b864d4bfe745dc01
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/eth/figures/detail-config.png differ
diff --git a/development-tools/rtthread-studio/drivers/eth/figures/new-pro.png b/development-tools/rtthread-studio/drivers/eth/figures/new-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d1884d6659815b1d18f730bf24c05daa8e4012e
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/eth/figures/new-pro.png differ
diff --git a/development-tools/rtthread-studio/drivers/eth/figures/open-lwIP.png b/development-tools/rtthread-studio/drivers/eth/figures/open-lwIP.png
new file mode 100644
index 0000000000000000000000000000000000000000..eb108749792f94a59428843cef39e1ef175f1a2a
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/eth/figures/open-lwIP.png differ
diff --git a/development-tools/rtthread-studio/drivers/eth/figures/ping-test.png b/development-tools/rtthread-studio/drivers/eth/figures/ping-test.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc11e27f757d3e282968ff199b318e653bb9ac1d
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/eth/figures/ping-test.png differ
diff --git a/development-tools/rtthread-studio/drivers/eth/rtthread-studio-eth.md b/development-tools/rtthread-studio/drivers/eth/rtthread-studio-eth.md
new file mode 100644
index 0000000000000000000000000000000000000000..e71fe0c30cd069259371b1978a3e4719b5fcc790
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/eth/rtthread-studio-eth.md
@@ -0,0 +1,180 @@
+# 基于 RT-Thread Studio 的 ETH 驱动开发文档
+
+## 简介
+
+`RT-Thread` 为了方便用户开发网络应用,引入了网络设备框架,同时 `RT-Thread` 还提供了数量丰富的网络组件包,方便用户快速开发自己的网络应用。
+
+本文将基于正点原子 `stm32f407-atk-explorer` 开发板主要介绍如何使用 `RT-Thread Studio` 来添加以太网驱动和 `lwIP` 协议栈。
+
+`ETH` 设备驱动的开发可总结为如下:
+
+- 新建 `RT-Thread` 完整版项目
+- `board.h`中定义 `BSP_USING_ETH` 和 `PHY` 相关的宏
+- `board.c`中初始化 `ETH` 相关的引脚和时钟
+- `stm32xxxx_hal_config.h`中打开 `HAL` 库函数对 `ETH` 的支持
+- `board.c` 中实现自己的 `PHY` 复位函数
+- 配置 `lwIP` 协议栈
+
+更多 `ETH` 的配置及添加步骤也可以参考相应工程文件 `board.h` 中对 `ETH` 部分的描述。
+
+## 新建 RT-Thread 项目
+
+使用 `RT-Thread Studio` 新建基于 `v4.0.2` 的工程,界面如下图所示:
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择 `基于芯片` 创建工程,选择的 `RT-Thread` 版本为 `v4.0.2`
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的 `Finish` 按钮即可创建 `RT-Thread` 完整版工程。
+
+## 定义 ETH 相关的宏
+
+定位到工程文件 `board.h` 中 `ETH` 配置说明部分,按照注释部分的说明分别定义 `BSP_USING_ETH` 和 `PHY` 相关的宏,本例中使用板载以太网 `PHY` 芯片为 `LAN8720A`, 所以 `ETH` 相关的宏定义如下 :
+
+```c
+#define BSP_USING_ETH
+#ifdef BSP_USING_ETH
+#define PHY_USING_LAN8720A
+#endif
+```
+
+## 初始化引脚和时钟
+
+定义了 `BSP_USING_ETH` 宏之后,`drv_eth.c` 文件就会参与编译,该文件只是配置了 `ETH` 的工作方式和传输函数等,具体 `ETH` 外设的时钟和引脚的初始化需要借助 `STM32CubeMx` 生成的代码。
+
+将 `STM32CubeMx` 工具生成的 `ETH` 引脚和时钟初始化代码(一般在 `stm32_xxxx_hal_msp.c` 文件中)复制到自己工程的 `board.c` 文件的末尾,使之参与编译,如下所示:
+
+```c
+void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
+{
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+ if(heth->Instance==ETH)
+ {
+ /* USER CODE BEGIN ETH_MspInit 0 */
+
+ /* USER CODE END ETH_MspInit 0 */
+ /* Peripheral clock enable */
+ __HAL_RCC_ETH_CLK_ENABLE();
+
+ __HAL_RCC_GPIOC_CLK_ENABLE();
+ __HAL_RCC_GPIOA_CLK_ENABLE();
+ __HAL_RCC_GPIOG_CLK_ENABLE();
+ /**ETH GPIO Configuration
+ PC1 ------> ETH_MDC
+ PA1 ------> ETH_REF_CLK
+ PA2 ------> ETH_MDIO
+ PA7 ------> ETH_CRS_DV
+ PC4 ------> ETH_RXD0
+ PC5 ------> ETH_RXD1
+ PG11 ------> ETH_TX_EN
+ PG13 ------> ETH_TXD0
+ PG14 ------> ETH_TXD1
+ */
+ GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
+ HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+ GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+ GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13|GPIO_PIN_14;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
+ HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
+
+ /* USER CODE BEGIN ETH_MspInit 1 */
+
+ /* USER CODE END ETH_MspInit 1 */
+ }
+
+}
+```
+
+## 打开 HAL 库对 ETH 的支持
+
+在 `stm32_xxxx_hal_config.h` 文件中打开对 `ETH` 的支持,也就是取消掉 `HAL_ETH_MODULE_ENABLED` 这个宏定义的注释,如下所示:
+
+```c
+#define HAL_ETH_MODULE_ENABLED
+```
+
+## 实现 PHY 复位函数
+
+在 `drv_eth.c` 文件中会调用 `phy_reset` 函数,该函数需要根据自己的实际情况进行实现,本例中 `PHY` 的复位引脚接在了 `PD3` 引脚,所以复位函数的实现如下所示 :
+
+```c
+#include
+#define RESET_IO GET_PIN(D, 3)
+
+void phy_reset(void)
+{
+ rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);
+ rt_pin_write(RESET_IO, PIN_HIGH);
+ rt_thread_mdelay(50);
+ rt_pin_write(RESET_IO, PIN_LOW);
+ rt_thread_mdelay(50);
+ rt_pin_write(RESET_IO, PIN_HIGH);
+}
+```
+
+> [!NOTE]
+> 注:不同的 `PHY` 芯片复位方式不同,需要根据具体自己板卡的实际情况进行实现。
+
+## 配置 lwIP 协议栈
+
+在前面的配置中我们已经配置好了 `ETH` 相关的硬件部分驱动,接下来就需要配置 `lwIP` 协议栈相关的内容。
+
+打开 `RT-Thread Settings` 文件,在图形化配置界面中左键单击 `lwIP` 图标即可打开 `lwIP` 协议栈的支持(组件开启,相应的图标会高亮),如下图所示:
+
+
+
+在该选项上右键,可查看 `lwIP` 的详细配置,具体配置路径如下所示:
+```c
+RT-Thread Settings
+---- 组件
+--------网络
+------------轻量级 TCP/IP 堆栈
+```
+
+配置结果如下图所示:
+
+
+
+本例中 `lwIP` 协议栈的配置直接选择默认配置,没有做任何修改。实际使用中可以根据自己的板卡情况进行修改。
+
+## 使用网络
+
+将 `ETH` 和 `lwIP` 配置好之后编译下载程序,打开串口终端工具,在命令行中输入如下网络测试命令
+
+```c
+ping www.baidu.com
+```
+
+终端输出结果如下所示:
+
+
+
+从上图可以看出开发板已经成功连接到了网络。
+
+## 注意事项
+
+- 本例中只配置了 `lwIP` 协议栈以及 `ETH` 相关的驱动,更多关于 `RT-Thread` 网络部分的使用及介绍可以参考 [netdev 网卡](https://www.rt-thread.org/document/site/programming-manual/netdev/netdev/),[SAL套接字抽象层](https://www.rt-thread.org/document/site/programming-manual/sal/sal/)。
\ No newline at end of file
diff --git a/development-tools/rtthread-studio/drivers/pin/figures/pin-device.png b/development-tools/rtthread-studio/drivers/pin/figures/pin-device.png
new file mode 100644
index 0000000000000000000000000000000000000000..1533d7a76928ddc450a90eaaace90cdfb14dd519
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/pin/figures/pin-device.png differ
diff --git a/development-tools/rtthread-studio/drivers/pin/figures/pin-project.png b/development-tools/rtthread-studio/drivers/pin/figures/pin-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7694c1cc896cdfe4cce7441b7193a286fcf3c85
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/pin/figures/pin-project.png differ
diff --git a/development-tools/rtthread-studio/drivers/pin/rtthread-studio-pin.md b/development-tools/rtthread-studio/drivers/pin/rtthread-studio-pin.md
new file mode 100644
index 0000000000000000000000000000000000000000..30752ebd042353dcddb1d7d990322151a3916f00
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/pin/rtthread-studio-pin.md
@@ -0,0 +1,69 @@
+# 基于 RT-Thread Studio 的 PIN 设备应用文档
+
+## 简介
+
+一般情况下 MCU 引出供用户使用的引脚有很多个,RT-Thread 提供的 PIN 设备驱动将这些 GPIO 引脚抽象为了一个 PIN 设备,应用程序通过 PIN 设备管理接口就可以访问控制引脚。PIN 设备驱动有以下特点:
+
+- 在 PIN 驱动文件中为每个引脚重新编号,这不同于芯片手册中的编号。使用时可以通过 PIN 驱动中的引脚号操作 PIN 设备。
+- 可设置引脚输入/输出模式、可读取/设置引脚电平状态、可设置引脚中断回调函数等。
+
+使用 RT-Thread Studio 创建基于 RT-Thread 完整版的工程时,默认开启了 RT-Thread 的 PIN 设备,所以用户无须重新配置或修改源码,即可直接使用 PIN 设备。
+
+
+## 创建 RT-Thread 完整版工程
+
+使用 RT-Thread Studio 新建基于 v4.0.2 的工程,界面如下图所示
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择`基于芯片` 创建工程,选择的 RT-Thread 版本为 v4.0.2
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的`完成`按钮即可创建 RT-Thread 的工程。
+
+## 使用 PIN 设备
+
+基于 RT-Thread 完整版创建的工程中,main.c 函数里面会自动生成如下定义
+
+```c
+#define LED0_PIN GET_PIN(A, 5)
+
+int main(void)
+{
+ int count = 1;
+ /* set LED0 pin mode to output */
+ rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
+
+ while (count++)
+ {
+ /* set LED0 pin level to high or low */
+ rt_pin_write(LED0_PIN, count % 2);
+ LOG_D("Hello RT-Thread!");
+ rt_thread_mdelay(1000);
+ }
+
+ return RT_EOK;
+}
+```
+
+使用 PIN 驱动需要使用 `GET_PIN` 获取相应的引脚编号,获取到引脚编号后,可使用 `rt_pin_write` 等函数来操作引脚。
+例如,`stm32l475-atk-pandora` 开发板的 LED 所接的引脚为 PE7,所以修改为
+
+```c
+#define LED0_PIN GET_PIN(E, 7)
+```
+
+编译并下载代码,可以看到开发板上面的 LED 每间隔 1000 ms 闪烁一次。在终端中输入 `list_device` 命令可以看到 pin 设备已经成功注册到系统中了,如下图所示
+
+
+
+PIN 设备的更多使用说明请参考 [PIN 设备](https://www.rt-thread.org/document/site/programming-manual/device/pin/pin/)
\ No newline at end of file
diff --git a/development-tools/rtthread-studio/drivers/soft-i2c/figures/aht10-data.png b/development-tools/rtthread-studio/drivers/soft-i2c/figures/aht10-data.png
new file mode 100644
index 0000000000000000000000000000000000000000..1adebe82845ccd68fd89c4b64115835c068a1f18
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/soft-i2c/figures/aht10-data.png differ
diff --git a/development-tools/rtthread-studio/drivers/soft-i2c/figures/f5-project.png b/development-tools/rtthread-studio/drivers/soft-i2c/figures/f5-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..448cf93543aebc0c6f206d506feeb3709ee3f655
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/soft-i2c/figures/f5-project.png differ
diff --git a/development-tools/rtthread-studio/drivers/soft-i2c/figures/i2c-project.png b/development-tools/rtthread-studio/drivers/soft-i2c/figures/i2c-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f4fb7e5220bf9955cad9bee2fb42d21a01baa8c
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/soft-i2c/figures/i2c-project.png differ
diff --git a/development-tools/rtthread-studio/drivers/soft-i2c/figures/list_device.png b/development-tools/rtthread-studio/drivers/soft-i2c/figures/list_device.png
new file mode 100644
index 0000000000000000000000000000000000000000..7dd00d0c63bb69177b6e19332365eab07f95cd0b
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/soft-i2c/figures/list_device.png differ
diff --git a/development-tools/rtthread-studio/drivers/soft-i2c/figures/open-frame.png b/development-tools/rtthread-studio/drivers/soft-i2c/figures/open-frame.png
new file mode 100644
index 0000000000000000000000000000000000000000..61bc2976cf8ae7fcff653fae241632926ed6f927
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/soft-i2c/figures/open-frame.png differ
diff --git a/development-tools/rtthread-studio/drivers/soft-i2c/figures/open-picture.png b/development-tools/rtthread-studio/drivers/soft-i2c/figures/open-picture.png
new file mode 100644
index 0000000000000000000000000000000000000000..fda891db25a1954c4283e7701e1a2a4f95420e6b
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/soft-i2c/figures/open-picture.png differ
diff --git a/development-tools/rtthread-studio/drivers/soft-i2c/rtthread-studio-soft-i2c.md b/development-tools/rtthread-studio/drivers/soft-i2c/rtthread-studio-soft-i2c.md
new file mode 100644
index 0000000000000000000000000000000000000000..59422ad7b6251c6f56a079b5571b453e24034ffd
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/soft-i2c/rtthread-studio-soft-i2c.md
@@ -0,0 +1,89 @@
+# 基于 RT-Thread Studio 的软件 I2C 驱动开发文档
+
+## 简介
+
+I2C 总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA,另一根是双向时钟线 SCL。
+
+I2C 总线可以通过芯片上板载的 I2C 外设实现,也可以通过 GPIO 引脚模拟 I2C 总线协议来实现。
+
+硬件 I2C 对应芯片上的 I2C 外设,由相应的 I2C 控制器和驱动电路组成,其所使用的引脚也是专用的。软件 I2C 使用的是普通的 GPIO 引脚,用软件控制 GPIO 管脚状态来模拟 I2C 通信波形。所以软件 I2C 不受管脚限制,接口也比较灵活。
+
+RT-Thread 的 I2C 设备驱动框架即支持硬件 I2C 也支持软件模拟 I2C。本文将基于 `stm32l475-atk-pandora` 开发板就软件 I2C 的驱动开发展开讲解。
+
+软件 I2C 的配置步骤总结如下:
+
+- 新建 RT-Thread 完整版项目
+
+- 打开软件 I2C 设备驱动框架
+
+- 定义软件 I2C 总线相关端口和引脚的宏
+
+- 使用 I2C 总线
+
+更多软件 I2C 的配置及添加步骤也可以参考相应工程文件 `board.h` 中对软件模拟 I2C 部分的描述。
+
+
+## 新建 RT-Thread 项目
+
+使用 RT-Thread Studio 新建基于 v4.0.2 的工程,界面如下图所示
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择 `基于芯片` 创建工程,选择的 RT-Thread 版本为 v4.0.2
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的 `完成` 按钮即可创建 RT-Thread 的工程。
+
+## 打开 I2C 设备驱动框架
+
+在 `RT-Thread Setting` 文件中借助图形化配置工具打开软件 I2C 的驱动框架,如下图所示
+
+
+
+左键单击即可开启 `软件模拟 I2C`(组件开启,相应的图标会高亮),在该选项上右键,可以查看软件模拟 I2C 的 `详细配置`,具体配置如下所示
+
+```c
+RT-Thread Setting
+----组件
+--------设备驱动程序
+------------使用 I2C 设备驱动程序
+----------------使用 GPIO 模拟 I2C
+```
+
+配置结果如下图所示
+
+
+
+## 定义软件 I2C 相关的宏
+
+在 board.h 文件中定义软件 I2C 相关的宏
+
+```c
+#define BSP_USING_I2C1 /* 使用 I2C1 总线 */
+#define BSP_I2C1_SCL_PIN GET_PIN(C, 1) /* SCL -> PC1 */
+#define BSP_I2C1_SDA_PIN GET_PIN(D, 6) /* SDA -> PD6 */
+```
+
+上述 I2C 总线相关的宏表示 I2C1 总线的 SCL 时钟线连接在 PC1 引脚,SDA 数据线连接在 PD6 引脚。如果需要使用多个 I2C 总线,相关的宏定义参考 board.h 文件中 I2C1 的宏进行定义即可
+
+## I2C 总线的使用
+
+编译并下载程序在终端中输入 `list_device` 测试命令可以看到 i2c1 已经成功注册到系统中了,如下图所示
+
+
+
+更多 I2C 总线的应用请参考 [I2C 总线设备应用示例](https://www.rt-thread.org/document/site/programming-manual/device/i2c/i2c/#i2c_4)
+
+
+## 注意事项
+
+- STM32 软件 I2C 的驱动依赖于 PIN 设备驱动。
diff --git a/development-tools/rtthread-studio/drivers/spi/figures/list_device.png b/development-tools/rtthread-studio/drivers/spi/figures/list_device.png
new file mode 100644
index 0000000000000000000000000000000000000000..5fb2db265dda3a959fe4675a67dc5952db0c95d8
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/spi/figures/list_device.png differ
diff --git a/development-tools/rtthread-studio/drivers/spi/figures/open-frame.png b/development-tools/rtthread-studio/drivers/spi/figures/open-frame.png
new file mode 100644
index 0000000000000000000000000000000000000000..8c11885fff253000c181e5ad735e0c543ce12a9b
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/spi/figures/open-frame.png differ
diff --git a/development-tools/rtthread-studio/drivers/spi/figures/open-spi.png b/development-tools/rtthread-studio/drivers/spi/figures/open-spi.png
new file mode 100644
index 0000000000000000000000000000000000000000..16cf812cfb6db22b2eebcb72253cf1f2f3f3c57b
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/spi/figures/open-spi.png differ
diff --git a/development-tools/rtthread-studio/drivers/spi/figures/spi-project.png b/development-tools/rtthread-studio/drivers/spi/figures/spi-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..9df5b0c58782026399d2b14a34f1ced85b6c5c24
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/spi/figures/spi-project.png differ
diff --git a/development-tools/rtthread-studio/drivers/spi/figures/spi-source.png b/development-tools/rtthread-studio/drivers/spi/figures/spi-source.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0d71d197b971c10d8c6ceba4463dcebacf2370b
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/spi/figures/spi-source.png differ
diff --git a/development-tools/rtthread-studio/drivers/spi/rtthread-studio-spi.md b/development-tools/rtthread-studio/drivers/spi/rtthread-studio-spi.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e68361bc47114af0479245e714387cc4948a530
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/spi/rtthread-studio-spi.md
@@ -0,0 +1,131 @@
+# 基于 RT-Thread Studio 的 SPI 驱动开发文档
+
+## 简介
+
+SPI 是一种高速、全双工、同步串行通信总线,常用于 MCU 与数字芯片之间的短距离通讯。RT-Thread 的 SFUD 组件,RW007 WIFI 模块均使用到了 SPI 驱动。下面将基于 `stm32l475-atk-pandora` 开发板,讲解基于 RT-Thread Studio 开发 SPI 驱动。
+
+SPI 设备驱动的开发可总结为如下:
+
+- 新建 RT-Thread 完整版项目
+
+- 打开 SPI 设备驱动框架
+
+- 定义 SPI 总线相关的宏
+
+- 打开 HAL 库函数对 SPI 总线的支持
+
+- 复制 SPI 引脚初始化函数到工程
+
+更多 SPI 总线的配置及添加步骤也可以参考相应工程文件 `board.h` 中对 SPI 总线部分的描述。
+
+## 新建 RT-Thread 项目
+
+使用 RT-Thread Studio 新建基于 v4.0.2 的工程,界面如下图所示
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择 `基于芯片` 创建工程,选择的 RT-Thread 版本为 v4.0.2
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的 `完成` 按钮即可创建 RT-Thread 的工程。
+
+## 打开 SPI 设备驱动框架
+
+在 `RT-Thread Setting` 文件中借助图形化配置工具打开软件 SPI 的驱动框架,如下图所示
+
+
+
+左键单击即可开启 `SPI` 驱动框架(组件开启,相应的图标会高亮),在该选项上右键,可查看 SPI 的 `详细配置`,具体配置路径如下所示
+
+```c
+RT-Thread Setting
+----组件
+--------设备驱动程序
+------------使用 SPI 总线/设备驱动程序
+```
+
+配置结果如下图所示
+
+
+
+## 定义 SPI 总线相关的宏
+
+在 board.h 文件中定义 SPI 总线相关的宏,本例中使用 SPI3 总线,只需定义如下宏即可
+
+```c
+#define BSP_USING_SPI3
+```
+
+## 打开 HAL 库对 SPI 的支持
+
+在 stm32xxxx_hal_config.h 文件中打开对 SPI 的支持,也就是取消掉 HAL_SPI_MODULE_ENABLED 这个宏定义的注释,如下所示:
+```c
+#define HAL_SPI_MODULE_ENABLED
+```
+
+## 初始化引脚和时钟
+
+定义了 `BSP_USING_SPI3` 宏之后,`drv_spi.c` 文件就会参与编译,该文件只是配置了 SPI 的工作方式和传输函数,具体 SPI 外设的时钟和引脚的初始化需要借助 `STM32CubeMx` 生成的代码。
+
+例如 `stm32l475-atk-pandora` 开发板的 `SPI3` 外设连接了一个 `LCD` 屏幕,所以需要将 CubeMx 生成的 `SPI3` 的初始化代码(一般在 `stm32_xxxx_hal_msp.c` 文件中)复制到自己工程的 `board.c` 文件的末尾,使之参与编译,如下所示
+
+```c
+void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
+{
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+ if(hspi->Instance == SPI3)
+ {
+ /* USER CODE BEGIN SPI3_MspInit 0 */
+
+ /* USER CODE END SPI3_MspInit 0 */
+ /* Peripheral clock enable */
+ __HAL_RCC_SPI3_CLK_ENABLE();
+
+ __HAL_RCC_GPIOC_CLK_ENABLE();
+ __HAL_RCC_GPIOB_CLK_ENABLE();
+ /**SPI3 GPIO Configuration
+ PC11 ------> SPI3_MISO
+ PB3 (JTDO-TRACESWO) ------> SPI3_SCK
+ PB5 ------> SPI3_MOSI
+ */
+ GPIO_InitStruct.Pin = GPIO_PIN_11;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
+ HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+ GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_5;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+ /* USER CODE BEGIN SPI3_MspInit 1 */
+
+ /* USER CODE END SPI3_MspInit 1 */
+ }
+}
+```
+
+如果需要注册更多的 SPI 总线设备,只需参考 `board.h` 文件中 SPI 相关的宏定义并拷贝引脚初始化函数即可。
+
+## SPI 总线的使用
+
+编译并下载程序输入 `list_device` 测试命令可以看到 SPI 总线设备已经成功注册到系统中了,如下图所示
+
+
+
+> 提示:这里只是注册了一个 SPI 总线设备,SPI 从设备的挂载请参考 [挂载 SPI 从设备](https://www.rt-thread.org/document/site/programming-manual/device/spi/spi/#spi_2)
+
+更多关于 SPI 总线的使用请参考 [SPI 总线设备](https://www.rt-thread.org/document/site/programming-manual/device/spi/spi/)。
diff --git a/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/nano-putc.png b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/nano-putc.png
new file mode 100644
index 0000000000000000000000000000000000000000..24eadca0970499a13ae3a40fc792cb1a35d9c853
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/nano-putc.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/nano-putc2.png b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/nano-putc2.png
new file mode 100644
index 0000000000000000000000000000000000000000..1826054d34a53d9963adc53ec915ec419d799371
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/nano-putc2.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/new-project.png b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/new-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b9b6e34c88ffeab6be50c05de7f1eee57853895
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/figures/new-project.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/rtthread-studio-uart-nano-v3.1.3.md b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/rtthread-studio-uart-nano-v3.1.3.md
new file mode 100644
index 0000000000000000000000000000000000000000..ca190176bba582d589cd352a8efa5e43a6b565d0
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/uart/nano-v3.1.3/rtthread-studio-uart-nano-v3.1.3.md
@@ -0,0 +1,93 @@
+# 基于 RT-Thread Studio 的串口驱动开发文档
+
+## 简介
+
+使用 RT-Thread Studio 新建基于 RT-Thread 的项目时,串口驱动已经自动添加到工程中,不需要用户手动添加和串口驱动相关的源码,用户可以很方便的使用串口进行输入输出。同时,基于 Studio 的图形化配置界面,更改串口的配置也更加简洁,用户不需要更改和驱动相关的源代码,只需要更改或新增相关的宏即可,下面将基于 `stm32l475-atk-pandora` 开发板就 RT-Thread 的 nano 版和完整版分别展开讲解。完整版串口驱动开发文档参见 [完整版串口驱动开发](../v4.0.2/rtthread-studio-uart-v4.0.2.md)。
+
+## RT-Thread nano 版串口的驱动开发
+
+nano 版串口驱动的使用主要用于控制台的输入与输出。
+
+### 配置默认串口
+
+使用 RT-Thread Studio 新建基于 nano 的工程,界面如下图所示
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择 `基于芯片` 创建工程,并选择 RT-Thread 的版本为 nano 版
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的`完成`按钮即可创建 RT-Thread 的工程。在 RT-Thread 工程的 main.c 文件中会自动生成如下代码:
+
+```c
+int main(void)
+{
+ int count = 1;
+
+ while (count++)
+ {
+ LOG_D("Hello RT-Thread!");
+ rt_thread_mdelay(1000);
+ }
+
+ return RT_EOK;
+}
+```
+
+编译并下载工程后,打开串口 Studio 自带的串口工具即可看到串口输出的打印信息,如下所示
+
+
+
+从串口输出的信息中可以看到我们已经成功使用串口输出了打印信息。
+
+
+### 修改默认串口
+
+上节中基于 Studio 的图形化界面成功配置了串口的输出,但是一些实际的应用的可能需要使用其他串口输出信息,这种情况则可以通过修改一些宏定义实现。
+
+#### 修改 `board.h` 宏
+
+演示所用的开发板 `stm32l475-atk-pandora` 默认使用 UART1 进行输出,若要改为 UART2(`TX->PA2`、`RX->PA3`)进行输出,,则将 `board.h` 中串口 1 的 5 个宏
+
+```c
+#define BSP_USING_UART1
+#define BSP_UART1_TX_PIN "PA9"
+#define BSP_UART1_RX_PIN "PA10"
+```
+
+修改为串口 2 的以下宏
+```c
+#define BSP_USING_UART2
+#define BSP_UART2_TX_PIN "PA2"
+#define BSP_UART2_RX_PIN "PA3"
+```
+
+宏定义的修改主要分为三个方面
+
+- 修改串口对应的宏定义,如 `BSP_USING_UART1`、`BSP_USING_UART2`等。
+
+- 修改串口 `TX/RX` 所使用的端口,如 `"PA2"`、`"PA3"`等。
+
+
+#### 使用更改后的串口输出
+
+board.h 中的宏修改完成之后,编译、下载。使用 USB 转串口线连接好 PA2、和 PA3 的引脚,并打开 Studio 中自带的串口工具,串口输出的 log 如下所示
+
+
+
+从输出的信息可以看出控制台切换到串口 2 后成功进行了输出。
+
+## 常见问题
+
+### Q:如何给 Nano 添加 Finsh?
+
+A: 参考 [基于 Nano 添加 Finsh 章节](https://www.rt-thread.org/document/site/tutorial/nano/nano-port-studio/an0047-nano-port-studio/#nano-finsh)。
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/add-uart.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/add-uart.png
new file mode 100644
index 0000000000000000000000000000000000000000..f160e958a1c48aaceef4b0df54c9a841f0a04f88
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/add-uart.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/change-pin.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/change-pin.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7dc88f9b0a93c28628004b6e417d14556c66a83
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/change-pin.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/change-terminal.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/change-terminal.png
new file mode 100644
index 0000000000000000000000000000000000000000..c56917bcfd09c49b0e5dd511dadbc3b379c71c19
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/change-terminal.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/dma-config.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/dma-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..62642727f270c48baa4c72ba6b3fbfc10ca1c37f
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/dma-config.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/dma-put.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/dma-put.png
new file mode 100644
index 0000000000000000000000000000000000000000..6cc3a68153fd7d21ddc48ef1c9b76ecbc55cb2b7
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/dma-put.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list-device3.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list-device3.png
new file mode 100644
index 0000000000000000000000000000000000000000..906b3977d4ebb3a15fecdea300f9cbf2e65eaf2e
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list-device3.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list_device1.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list_device1.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae1afd6de1b7efd9de8ece1b987fe0d894c762e5
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list_device1.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list_device2.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list_device2.png
new file mode 100644
index 0000000000000000000000000000000000000000..b6c401a7ca765287b0777aaec76298d31a4e7f2b
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/list_device2.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/open-dma.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/open-dma.png
new file mode 100644
index 0000000000000000000000000000000000000000..096bf44f531daf80309a9977b44787a9565f8fbe
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/open-dma.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-project.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..20ab3e8b6eae477dd87e010332c7e52b2c19498e
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-project.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-putc1.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-putc1.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac457cc4ef3bdb9b7e1cd91174a97ab98a62da68
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-putc1.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-putc2.png b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-putc2.png
new file mode 100644
index 0000000000000000000000000000000000000000..7725c98a5daaff11340741cce84c96a2948358d0
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/uart/v4.0.2/figures/v4.0.2-putc2.png differ
diff --git a/development-tools/rtthread-studio/drivers/uart/v4.0.2/rtthread-studio-uart-v4.0.2.md b/development-tools/rtthread-studio/drivers/uart/v4.0.2/rtthread-studio-uart-v4.0.2.md
new file mode 100644
index 0000000000000000000000000000000000000000..ab36d17e19d31c686be2573e4f430b03d70f3e6d
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/uart/v4.0.2/rtthread-studio-uart-v4.0.2.md
@@ -0,0 +1,164 @@
+# 基于 RT-Thread Studio 的串口驱动开发文档
+
+## RT-Thread 完整版串口的驱动开发
+
+与 nano 版串口不同的是,完整版的串口基于设备驱动框架,使用完整版的串口可以使用 RT-Thread 丰富的组件及软件包。如 AT 组件,modbus 软件包等
+
+### 配置默认串口
+
+使用 RT-Thread Studio 新建基于 v4.0.2 的工程,界面如下图所示
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择`基于芯片` 创建工程,选择的 RT-Thread 版本为 v4.0.2
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的`完成`按钮即可创建 RT-Thread 的工程。在 RT-Thread 工程的 main.c 文件中会自动生成如下代码:
+
+```c
+/* PLEASE DEFINE the LED0 pin for your board, such as: PA5 */
+#define LED0_PIN GET_PIN(A, 5)
+
+int main(void)
+{
+ int count = 1;
+ /* set LED0 pin mode to output */
+ rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
+
+ while (count++)
+ {
+ /* set LED0 pin level to high or low */
+ rt_pin_write(LED0_PIN, count % 2);
+ LOG_D("Hello RT-Thread!");
+ rt_thread_mdelay(1000);
+ }
+
+ return RT_EOK;
+}
+```
+
+代码中会每间隔 1000 ms 在控制台中打印 `Hello RT-Thread!` 。编译并下载工程后,打开串口 Studio 自带的串口工具即可看到串口输出的打印信息,如下所示
+
+
+
+从串口输出的信息中可以看到我们已经成功使用串口输出了打印信息。
+
+在控制台中输入 `list_device` 可以看到 `uart1` 设备已经成功注册到系统中,如下图所示
+
+
+
+### 修改默认串口
+
+上节中基于 Studio 的图形化界面成功配置了串口的输出,但是一些实际的应用的可能需要使用其他串口输出信息,这种情况则可以通过修改一些宏定义及 RT-Thread Setting 文件来实现。
+
+#### 修改 `board.h` 宏
+
+演示所用的开发板 `stm32l475-atk-pandora` 默认使用 UART1 进行输出,若要修改为串口 2 (`TX->PA2`、`RX->PA3`)进行输出,则在 board.h 中定义宏 `BSP_USING_UART2`,并将串口 2 对应的引脚信息修改为实际所使用的引脚,如下所示
+
+
+
+宏定义的修改主要分为三个方面
+
+- 修改串口对应的宏定义,如 `BSP_USING_UART1`、`BSP_USING_UART2`等。
+
+- 修改串口 `TX/RX` 所使用的端口,如 `"PA2"`、`"PA3"`等。
+
+
+
+#### 修改 `RT-Thread Setting` 文件
+
+在 RT-Thread Setting 文件中修改输出控制台的串口设备名称,修改路径如下:
+```c
+RT-Thread Setting
+----内核
+--------RT-Thread 内核
+------------内核设备对象
+----------------为 rt_kprintf 使用控制台
+--------------------控制设备台的名称
+```
+
+配置如下图所示
+
+
+
+保存工程配置并编译、下载,串口输出如下图所示
+
+
+
+输入 `list_device` 命令可查看已注册的设备,如下图所示
+
+
+
+从终端打印的信息可以看到控制台的串口设备已经成功的从串口 1 更换到了串口 2。
+
+### 新增串口
+
+新增串口只需要在 `board.h` 文件中定义相关串口的宏定义 `BSP_USING_UARTx` 及修改引脚信息即可
+
+新增串口的步骤总结如下
+
+- 新增对应串口的宏定义,如 BSP_USING_UART1、BSP_USING_UART2等。
+
+- 修改串口 `TX/RX` 所使用的端口,如 `"PA9"`、`"PA10"`等。
+
+
+基于修改控制台章节新增串口 1 的示例如下
+
+
+
+编译并下载程序,在控制台输入 `list_device` 命令可以看到已经注册了两个串口设备,串口 1 和串口 2。如下图所示
+
+
+
+从控制台输出的信息可以看到两个串口设备均已注册到系统中了。
+
+
+### 串口 DMA 的使用
+
+#### RT-Thread Setting 配置 DMA
+
+如果要使用串口 DMA 的功能,需要使用 RT-Thread Setting 打开 DMA 的支持。配置路径为
+
+```c
+RT-Thread Setting
+----组件
+--------设备驱动程序
+------------使用 UART 设备驱动程序
+----------------使能串口 DMA 模式。
+```
+
+配置过程如下图所示
+
+
+
+#### 配置 `board.h` 中的宏
+
+如果需要使用串口 DMA 只需要在 `board.h` 文件中定义如下宏即可
+
+```c
+#define BSP_UARTx_RX_USING_DMA
+#define BSP_UARTx_TX_USING_DMA
+```
+
+- `UARTx` 表示的是哪个串口需要使用 DMA,使用的是 DMA 的发送还是接收功能。此例中使用的是串口 2 的 DMA 接收功能,所以定义了宏 `BSP_UART2_RX_USING_DMA`,串口 2 使用 DMA 的配置如下所示
+
+
+
+将 [DMA 接收及轮询发送](https://www.rt-thread.org/document/site/programming-manual/device/uart/uart/#dma) 章节中的DMA 的测试代码添加到工程中
+
+编译并下载程序,在控制台中输入 `uart_dma_sample` 命令,并使用 USB 转串口线连接串口 2,在串口 2 中可以看到如下打印信息
+
+
+
+测试程序已经成功使用 DMA 进行了接收。
+
+更多关于串口的使用请查看 [UART设备](https://www.rt-thread.org/document/site/programming-manual/device/uart/uart/)
\ No newline at end of file
diff --git a/development-tools/rtthread-studio/drivers/usb-device/figures/cdc-usb.png b/development-tools/rtthread-studio/drivers/usb-device/figures/cdc-usb.png
new file mode 100644
index 0000000000000000000000000000000000000000..f796acfa325fb9dcefa7959a339dcc05a3c7d292
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/usb-device/figures/cdc-usb.png differ
diff --git a/development-tools/rtthread-studio/drivers/usb-device/figures/list-device.png b/development-tools/rtthread-studio/drivers/usb-device/figures/list-device.png
new file mode 100644
index 0000000000000000000000000000000000000000..c372ac643a37fd062886fdc8b6938c362fe9cd65
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/usb-device/figures/list-device.png differ
diff --git a/development-tools/rtthread-studio/drivers/usb-device/figures/new-project.png b/development-tools/rtthread-studio/drivers/usb-device/figures/new-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..cf820b17e42fc8e44f22d3306e3a5e454676993f
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/usb-device/figures/new-project.png differ
diff --git a/development-tools/rtthread-studio/drivers/usb-device/figures/usb-device.png b/development-tools/rtthread-studio/drivers/usb-device/figures/usb-device.png
new file mode 100644
index 0000000000000000000000000000000000000000..e3bd27a839147c5d0c9a4816d922d2f15e715e7d
Binary files /dev/null and b/development-tools/rtthread-studio/drivers/usb-device/figures/usb-device.png differ
diff --git a/development-tools/rtthread-studio/drivers/usb-device/rtthread-studio-usb-device.md b/development-tools/rtthread-studio/drivers/usb-device/rtthread-studio-usb-device.md
new file mode 100644
index 0000000000000000000000000000000000000000..de8334a327565a53ee177ded5072dbaba58ba9e3
--- /dev/null
+++ b/development-tools/rtthread-studio/drivers/usb-device/rtthread-studio-usb-device.md
@@ -0,0 +1,219 @@
+# 基于 RT-Thread Studio 的 USB Device 驱动开发文档
+
+
+## 简介
+
+`USB` 即 `Universal Serial Bus` 是一种支持热插拔的通用串行总线,在 `USB` 体系中又分为 `USB Host` 和 `USB Device`。本文将基于 `stm32l475-atk-pandora` 开发板,讲解基于 `RT-Thread Studio` 开发 `USB Device` 驱动。
+
+`USB Device` 设备驱动的开发可总结为如下几个步骤:
+
+- 新建 `RT-Thread` 完整版项目
+
+- 打开 `USB Device` 设备驱动框架,并配置相关的子类
+
+- `board.h`中定义 `USB Device` 相关的宏
+
+- `board.c` 中初始化 `USB Device` 的引脚及外设时钟
+
+- `stm32xxxx_hal_config.h` 中打开 `HAL` 库函数对 `USB Device` 的支持
+
+
+
+更多关于`USB Device` 的配置及添加步骤也可以参考相应工程文件 `board.h` 中对 `USB Device` 部分的描述。
+
+## 新建 RT-Thread 项目
+
+使用 `RT-Thread Studio` 新建基于 `v4.0.2` 的工程,界面如下图所示:
+
+
+
+配置过程可总结为以下步骤:
+
+- 定义自己的工程名及工程生成文件的存放路径
+
+- 选择 `基于芯片` 创建工程,选择的 `RT-Thread` 版本为 `v4.0.2`
+
+- 选择厂商及芯片型号
+
+- 配置串口信息
+
+- 配置调试器信息
+
+工程配置完成后点击下方的 `Finish` 按钮即可创建 `RT-Thread` 的工程。
+
+## 打开 USB Device 设备驱动框架
+
+在 `RT-Thread Settings` 文件中配置 `USB Device` ,配置路径如下:
+
+```c
+组件
+---- 设备驱动程序
+--------使用 USB
+------------使用 USB 设备
+----------------选择设备类型
+```
+
+本例中将 `USB Device` 配置为一个 `CDC` 的子类—虚拟串口,配置图如下所示:
+
+
+
+虚拟串口的属性等信息这里选择默认并未做任何修改,实际使用过程中按照需要修改即可。
+
+## 定义 USB Device 宏
+
+定位到工程文件 `board.h` 中关于 `USB Device` 的配置说明部分,按照注释部分的说明定义 `BSP_USING_USBDEVICE` 的宏,如下所示:
+
+```c
+#define BSP_USING_USBDEVICE
+```
+
+## 初始化引脚和时钟
+
+定义了 `BSP_USING_USBDEVICE` 宏之后,`drv_usbd.c` 文件就会参与编译,该文件只是配置了 `USB Device` 的工作方式和传输函数等,具体 `USB Device` 引脚和时钟的初始化需要借助 `STM32CubeMx` 生成的代码。
+
+将 `STM32CubeMx` 工具生成的 `USB Device` 引脚和时钟初始化代码(一般在 `stm32_xxxx_hal_msp.c` 文件中)复制到自己工程的 `board.c` 文件的末尾,使之参与编译,如下所示:
+
+```c
+void HAL_PCD_MspInit(PCD_HandleTypeDef* hpcd)
+{
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+ if(hpcd->Instance==USB_OTG_FS)
+ {
+ /* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
+
+ /* USER CODE END USB_OTG_FS_MspInit 0 */
+
+ __HAL_RCC_GPIOA_CLK_ENABLE();
+ /**USB_OTG_FS GPIO Configuration
+ PA11 ------> USB_OTG_FS_DM
+ PA12 ------> USB_OTG_FS_DP
+ */
+ GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+ /* Peripheral clock enable */
+ __HAL_RCC_USB_OTG_FS_CLK_ENABLE();
+
+ /* Enable VDDUSB */
+ if(__HAL_RCC_PWR_IS_CLK_DISABLED())
+ {
+ __HAL_RCC_PWR_CLK_ENABLE();
+ HAL_PWREx_EnableVddUSB();
+ __HAL_RCC_PWR_CLK_DISABLE();
+ }
+ else
+ {
+ HAL_PWREx_EnableVddUSB();
+ }
+ /* USB_OTG_FS interrupt Init */
+ HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
+ HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
+ /* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
+
+ /* USER CODE END USB_OTG_FS_MspInit 1 */
+ }
+}
+```
+
+## 配置 USB 外设时钟
+
+使用 `RT-Thread Studio` 自动生成工程的时候,在 `board.c` 文件中的系统时钟初始化函数 `SystemClock_Config` 中,有些外设的时钟是默认没有开启的。当需要使用这些外设的时候,同样需要借助 `STM32CubeMX` 工具生成的代码来初始化外设时钟。
+
+本例中需要使用到 `USB` 这个外设,使用 `STM32CubeMX` 工具配置好系统时钟以后将工程 `board.c` 文件中的 `SystemClock_Config` 函数替换为自己使用 `STM32CubeMX` 工具生成的函数,如下所示:
+
+```c
+void SystemClock_Config(void)
+{
+ RCC_OscInitTypeDef RCC_OscInitStruct = {0};
+ RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
+ RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
+
+ /** Configure LSE Drive Capability
+ */
+ HAL_PWR_EnableBkUpAccess();
+ __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
+ /** Initializes the CPU, AHB and APB busses clocks
+ */
+ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE
+ |RCC_OSCILLATORTYPE_LSE;
+ RCC_OscInitStruct.HSEState = RCC_HSE_ON;
+ RCC_OscInitStruct.LSEState = RCC_LSE_ON;
+ RCC_OscInitStruct.LSIState = RCC_LSI_ON;
+ RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+ RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+ RCC_OscInitStruct.PLL.PLLM = 1;
+ RCC_OscInitStruct.PLL.PLLN = 20;
+ RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
+ RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
+ RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
+ if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
+ {
+ Error_Handler();
+ }
+ /** Initializes the CPU, AHB and APB busses clocks
+ */
+ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
+ |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
+ RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+ RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+ RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
+ RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
+
+ if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
+ {
+ Error_Handler();
+ }
+ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
+ |RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_USB
+ |RCC_PERIPHCLK_ADC;
+ PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
+ PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
+ PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
+ PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
+ PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
+ PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE;
+ PeriphClkInit.PLLSAI1.PLLSAI1M = 1;
+ PeriphClkInit.PLLSAI1.PLLSAI1N = 12;
+ PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
+ PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
+ PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
+ PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_48M2CLK|RCC_PLLSAI1_ADC1CLK;
+ if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
+ {
+ Error_Handler();
+ }
+ /** Configure the main internal regulator output voltage
+ */
+ if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
+ {
+ Error_Handler();
+ }
+}
+```
+
+## 打开 HAL 库对 USB Device 的支持
+
+在 `stm32_xxxx_hal_config.h` 文件中打开对 `USB Device` 的支持,也就是取消掉 `HAL_PCD_MODULE_ENABLED` 这个宏定义的注释,如下所示:
+```c
+#define HAL_PCD_MODULE_ENABLED
+```
+
+## 使用
+
+上述步骤配置完成以后,编译下载程序,在终端中输入 `list_device` 命令,结果如下:
+
+
+
+从终端中输出的结果可以看到 `USB Device` 设备和虚拟串口设备已经成功注册到系统中了。
+
+在 `Windows` 上打开设备管理器,并将开发板上的 `USB Device` 接口通过数据线连接到电脑,在电脑的设备管理器中可以看到下图:
+
+
+
+从上图可以看到电脑已经成功的枚举了上面配置的虚拟串口设备。
+
+
diff --git a/development-tools/rtthread-studio/faq/figures/binfile.png b/development-tools/rtthread-studio/faq/figures/binfile.png
new file mode 100644
index 0000000000000000000000000000000000000000..f91160c399e64157d6b81cbde074f3094f4ca8b3
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/binfile.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/build-data.png b/development-tools/rtthread-studio/faq/figures/build-data.png
new file mode 100644
index 0000000000000000000000000000000000000000..edff6e8b48d644cb88297702872c69ff129f7e40
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/build-data.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/build-dfs.png b/development-tools/rtthread-studio/faq/figures/build-dfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff63f142887067b14db4fffb3f7c7d7cc7a0cc24
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/build-dfs.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/buildhe.png b/development-tools/rtthread-studio/faq/figures/buildhe.png
new file mode 100644
index 0000000000000000000000000000000000000000..320cd750cd40b864345ba1fb3fcfb36f1244d69b
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/buildhe.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/bulid_cancel.png b/development-tools/rtthread-studio/faq/figures/bulid_cancel.png
new file mode 100644
index 0000000000000000000000000000000000000000..4ee29d75d0786e04a2784bc1abcca7c4be3168fa
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/bulid_cancel.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/clean_error.png b/development-tools/rtthread-studio/faq/figures/clean_error.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4bc31a1bbffd85667901c69ac6732bcf1f939fa
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/clean_error.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/clean_script.exe b/development-tools/rtthread-studio/faq/figures/clean_script.exe
new file mode 100644
index 0000000000000000000000000000000000000000..6262e9b264529eefc36e75f2b1222f23d9b216cc
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/clean_script.exe differ
diff --git a/development-tools/rtthread-studio/faq/figures/devstyle1.png b/development-tools/rtthread-studio/faq/figures/devstyle1.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb6940f0520a87fc523a5d44266d0c3e55a75bea
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/devstyle1.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/devstyle2.png b/development-tools/rtthread-studio/faq/figures/devstyle2.png
new file mode 100644
index 0000000000000000000000000000000000000000..00b77c33332448d3d9968e4e3639f2d2fdb8951f
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/devstyle2.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/exclude_build_0.png b/development-tools/rtthread-studio/faq/figures/exclude_build_0.png
new file mode 100644
index 0000000000000000000000000000000000000000..5aa9a9443e83d22b4b2a748a18390669a9c440d6
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/exclude_build_0.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/exclude_build_1.png b/development-tools/rtthread-studio/faq/figures/exclude_build_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..9017df20fa2b753ad8ca462f7e3ac248bf6dfd6c
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/exclude_build_1.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/exclude_build_2.png b/development-tools/rtthread-studio/faq/figures/exclude_build_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..7687960a70fd5cba1d7ba9603cef3965cbdea3b4
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/exclude_build_2.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/hexfile.png b/development-tools/rtthread-studio/faq/figures/hexfile.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d8a9355d0f09b38f6cf1fb58fca78140c98a0ff
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/hexfile.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/importdone.png b/development-tools/rtthread-studio/faq/figures/importdone.png
new file mode 100644
index 0000000000000000000000000000000000000000..ed58379600aeaec1ab4aa02d1327cd186f31b702
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/importdone.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/importpro.png b/development-tools/rtthread-studio/faq/figures/importpro.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0ac8c372b662a6802a2dcf63973b53a41107f1a
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/importpro.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/link-cmd.png b/development-tools/rtthread-studio/faq/figures/link-cmd.png
new file mode 100644
index 0000000000000000000000000000000000000000..668ac70ed27965df197c59addbcb3698499198e6
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/link-cmd.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/look_key1.png b/development-tools/rtthread-studio/faq/figures/look_key1.png
new file mode 100644
index 0000000000000000000000000000000000000000..a4977811fc5ff0d58a10b32d021ef9ea7662591f
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/look_key1.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/look_key2.png b/development-tools/rtthread-studio/faq/figures/look_key2.png
new file mode 100644
index 0000000000000000000000000000000000000000..8cb1d568c6fbaa6efd68dd4abccb05c311718676
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/look_key2.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/open-dfs.png b/development-tools/rtthread-studio/faq/figures/open-dfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..0534220c6dfa7a7d1e0f3936969006eda91b3c30
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/open-dfs.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/open_settings.png b/development-tools/rtthread-studio/faq/figures/open_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..e86300f950ae62dc160310308ed81783f35d86cf
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/open_settings.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/openproject.png b/development-tools/rtthread-studio/faq/figures/openproject.png
new file mode 100644
index 0000000000000000000000000000000000000000..b668ce7db7daaaa49b4849f5167493e9a48d31a9
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/openproject.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/pkg_cfg.png b/development-tools/rtthread-studio/faq/figures/pkg_cfg.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c6615392f6ebff89760e90662483a75ac433073
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/pkg_cfg.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/pkg_open.png b/development-tools/rtthread-studio/faq/figures/pkg_open.png
new file mode 100644
index 0000000000000000000000000000000000000000..6fcdf163a4bd08b0201ad403dff2215647646e3b
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/pkg_open.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/project_linker.exe b/development-tools/rtthread-studio/faq/figures/project_linker.exe
new file mode 100644
index 0000000000000000000000000000000000000000..045f8fe9c17e9bb0df17773bed47685b28b3671a
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/project_linker.exe differ
diff --git a/development-tools/rtthread-studio/faq/figures/project_linker.jpg b/development-tools/rtthread-studio/faq/figures/project_linker.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e81f22c99ac82a988e6acc11d17592bfb53663d0
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/project_linker.jpg differ
diff --git a/development-tools/rtthread-studio/faq/figures/project_root.png b/development-tools/rtthread-studio/faq/figures/project_root.png
new file mode 100644
index 0000000000000000000000000000000000000000..b2d5be37f577df991bc98a6469843a50e592cbb9
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/project_root.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/python36.png b/development-tools/rtthread-studio/faq/figures/python36.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d8366d6aa5f0705ede75fbf340577de5f6539fd
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/python36.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/resetpage.png b/development-tools/rtthread-studio/faq/figures/resetpage.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f6224be37ece2a9714513a5bea561a76a5d5862
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/resetpage.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/restartpro.png b/development-tools/rtthread-studio/faq/figures/restartpro.png
new file mode 100644
index 0000000000000000000000000000000000000000..f38e288546bd653857712e008a18a1cf0ec235f5
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/restartpro.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/settings-link-cmd.png b/development-tools/rtthread-studio/faq/figures/settings-link-cmd.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc464b9ff9bd935bafcffcd981bb22a4159d2ed5
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/settings-link-cmd.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/showline.png b/development-tools/rtthread-studio/faq/figures/showline.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ada6aea37afc2436700da1eaef0de83d6bc9bc4
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/showline.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/stlinkport.png b/development-tools/rtthread-studio/faq/figures/stlinkport.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ee1b79f2bfe78992ce284ef2df608dc9f282e51
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/stlinkport.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/switchdebug.png b/development-tools/rtthread-studio/faq/figures/switchdebug.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f4de8757afeffcf79cfca4660180c4fde7cb3df
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/switchdebug.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/upadte_error1.png b/development-tools/rtthread-studio/faq/figures/upadte_error1.png
new file mode 100644
index 0000000000000000000000000000000000000000..46cdeb0ffb8fcd6462f2f44cb7c29e61fd00c569
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/upadte_error1.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/upadte_error2.png b/development-tools/rtthread-studio/faq/figures/upadte_error2.png
new file mode 100644
index 0000000000000000000000000000000000000000..275868d4eb7a60668b822c16395a9f8a859daa14
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/upadte_error2.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/upadte_error3.png b/development-tools/rtthread-studio/faq/figures/upadte_error3.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0aa99e8a5f4eb534ba7d7dbea1a5d04d65f4eb4
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/upadte_error3.png differ
diff --git a/development-tools/rtthread-studio/faq/figures/upgrade.png b/development-tools/rtthread-studio/faq/figures/upgrade.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8f5b9a8b9c5f2b9999041cc5c62083ac63c4117
Binary files /dev/null and b/development-tools/rtthread-studio/faq/figures/upgrade.png differ
diff --git a/development-tools/rtthread-studio/faq/studio-faq.md b/development-tools/rtthread-studio/faq/studio-faq.md
new file mode 100644
index 0000000000000000000000000000000000000000..93b8438d2915dd05f5139f6beb82503733f52a4f
--- /dev/null
+++ b/development-tools/rtthread-studio/faq/studio-faq.md
@@ -0,0 +1,240 @@
+# 常见问题
+
+
+
+## 如何添加文件到工程
+
+可以直接通过复制粘贴往RT-Thread Studio工程里添加文件,甚至可以直接可以往工程里复制粘贴整个文件夹,也可以通过右键新建向导选择新建文件夹,新建C源码文件或H头文件,如果添加文件后构建提示文件找不到,请到工程构建配置里将添加的文件添加Include包含头文件路径
+
+## 如何让源码显示行号
+
+在源码编辑窗口的左边栏上右键,选择`显示行号`,即可显示行号,如下图所示:
+
+
+
+## 导入项目的入口在哪里
+
+通过`项目资源管理器`窗口右键`导入`菜单打开导入向导窗口,选择 `现有项目到工作空间中`,如下图所示:
+
+
+
+点击`下一步`后,通过`浏览`按钮,选择要导入的项目所在目录,向导会自动扫描目录下所有可导入的工程并列在项目列表中,勾选要导入的工程,然后点击`完成`即可完成导入工程,如下图所示:
+
+
+
+## 如何生成 HEX 文件
+
+选中工程后,点击工具栏上的`打开构建配置`按钮,将相应的输出文件格式设置成 hex 文件格式,即可实现输出 hex 文件,如下图所示:
+
+
+
+如果需要同时生成 bin 文件和 hex 文件,需要在`构建后步骤`里添加构建后生成 HEX 文件的命令,如下图所示:
+
+
+
+构建后生成的 hex 文件,在工程的`Debug`目录下,如下图所示:
+
+
+
+## 串口出现丢失字符怎么办
+
+排除程序的原因外,串口线的质量,波特率是否设置过高都是需要考虑的因素,可以尝试换个串口线,或者将波特率调低点试试。
+
+## 删除工程的时候删不掉怎么办
+
+由于工程里可能有 git 文件被 git 程序占用造成工程有时删除部分后失败,提示占用问题,可以试试删除工程先前先关闭工程,等待一会后再删除,或者删除失败后,通过重启菜单重启一下 RT-Thread Studio 就可以正常删除了,重新启动菜单入口(菜单栏文件菜单内)和关闭项目(项目上右键菜单内)入口如下图所示:
+
+
+
+## 用户是否可以修改 rt-thread 及 packages 目录下的文件及配置
+
+不可以。rt-thread 及 packages 目录下的文件统一使用 RT-Thread Setting 文件来管理和配置,当需要这两个目录下的相关文件参与编译时,应该在 RT-Thread Setting 文件中来打开相关功能。
+
+例如用户想让当前 rt-thread 目录下的 dfs 参与编译,如下图所示
+
+
+
+用户不能直接修改文件属性让 dfs 文件参与编译,而是通过 RT-Thread Setting 文件打开 DFS 的配置,保存配置后 dfs 的配置后,Studio 会自动将 dfs 文件添加到构建文件中,如下图所示
+
+
+
+## Studio 编译结果中 text,data,bss,dec 和 hex 的各个含义是什么
+
+Studio 的编译结果如下图所示
+
+
+
+- text:代码段,用来存放代码及一些只读常量,一般是只读的区域
+
+- data:数据段,用来存放全局初始化变量,以及全局或局部静态变量
+
+- bss:BSS 段,用来存放所有未初始化的数据,用 0 来初始化
+
+- dec:是 decimal 即十进制的缩写,是 text,data 和 bss 的算术和
+本例中:51344 + 372 + 2808 = 54524
+
+- hex:是 hexadecimal 即十六进制的缩写,本例中:十进制数 54524 对应的十六进制就是 d4fc
+
+- filename:编译生成的目标文件名,本例中即为:rtthread.elf
+
+编译生成的目标文件占用内存的计算方法
+
+程序占用的 FLASH 大小 = text 段的大小 + data 段的大小
+
+程序占用的 RAM 大小 = data 段的大小 + bss 段的大小
+
+## ST-LINK烧写端口被占用怎么办
+
+ 当弹出错误对话框提示“TCP Port 61234 not available”时, 打开调试配置修改 ST-LINK端口号即可, 如下图所示:
+
+
+
+## 打开RT-thread settings窗口,为什么看不到图标界面
+
+新建工程选择RT-Thread非Nano版本源码即可, Nano是纯净版,没有组件概念
+
+## RT-Thread Studio 升级失败
+
+总共分两种情况:
+
+- 如果更新失败提示信息里有 “org.eclipse.equinox.internal.p2.engine.phases.CheckTrust”
+RT-Thread Studio 升级前请先关闭翻墙工具,或尝试删除安装目录下的“artifacts.xml”文件重启再试
+
+- 如果更新失败提示信息里有 “No Repository found”或发生下图所示错误,请手动探测更新。在更新页面内,取消勾选 “Group items by category”和“Contact all update sites during install to find required software” 并勾选"Eclipse Platform Launcher Executables",然后尝试安装studio更新,若安装过程中出错,关闭后重试一次即可 。
+
+ 
+
+ 
+
+ 
+
+## 如何启用黑色主题和设置编辑器配色
+
+通过`首选项`的`外观`配置项选择“DevStyle Theme”即可启用新的黑色主题,切换主题后需要重启Studio后才会生效
+
+
+
+
+
+## 如何查看和修改快捷键
+
+通过`帮助`菜单打开`键辅助`可查看快捷键
+
+
+
+
+
+## 如何恢复界面
+
+
+
+## 如何打开关闭的项目
+右键菜单,`打开项目`即可,如下图所示:
+
+
+
+## 创建工程失败 装载 python DLL 出错 怎么办
+
+- 问题描述: Error loading Python DLL `xxx/MEI85262/python36.dll`. LoadLibrary:找不到指定程序
+
+
+
+- 解决方法:请手动安装 Visual C++ Redistributable, [点我下载](https://realthread.cowtransfer.com/s/96d5e5928fc54e)
+
+## 如何取消启动调试前的自动构建
+
+首先通过`窗口`菜单打开`首选项`窗口,然后展开`运行/调试`选项并点击进入`启动`选项,最后将`在启动之前构建(如必需)`选项取消勾选即可取消启动调试前的自动构建,如下图所示:
+
+
+
+## 项目资源管理器如何 显示/隐藏 被排除构建的资源
+
+RT-Thread Studio 从 V 1.1.4 版本开始支持`显示/隐藏`被排除构建的资源,如下图所示,这些被排除构建的资源显示为灰色,并且资源图标上有斜杠标志,被排除构建的资源将不会在启动编译时参与编译,Studio 默认隐藏了这些资源。
+
+
+
+- 如果想`显示/隐藏`这些资源,请先点击项目资源管理器右上角的倒三角,会展示如下菜单,点击过滤器和定制:
+
+
+
+- 在过滤器和定制中,有很多可以选择过滤的资源,`勾选/取消勾选` RTT Excluded Resource ,即可`隐藏/显示`被排除构建的资源。
+
+
+
+## 如何解决 studio 编译链接时命令行超出 windows 长度限制的问题
+
+编译工程链接的时候,若出现如下图所示【 CreateProcess : No such file or directory 】的错误,可能是因为工程文件过多或者目录名过长等原因导致链接时命令行过长,部分 .o 被截断产生的问题。
+
+
+
+Windows 下命令行的字符串长度限制为 8191 个字符,当然我们也有办法去解决此限制,请使用以下一种或者多种方法来解决这个问题(根据您的具体情况):
+
+ - 缩短链接文件的路径
+ - 为文件夹和文件使用较短的名称
+ - 减少文件夹树的深度
+ - 将文件存储在更少的文件夹中
+
+ - 修改工程链接配置命令**(推荐)**
+ - 在 C/C++ Build/Settings/Gnu ARM Cross C++ Linker 中配置 Command line pattern 为
+
+ ```
+ @$(file >link.temp,${cross_toolchain_flags} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}) ../project_linker.exe link.temp ${COMMAND}
+ ```
+
+ 
+ - 在工程目录下添加 project_link.exe 可执行文件,可在此下载 [project_linker.exe](../figures/project_linker.exe)
+
+ 
+
+ - 重新编译工程,即可正常进行链接
+
+## 如何解决 studio 清理工程时命令行超出 windows 限制的问题
+
+在清理 ( Clean ) 工程的时候,当工程过大时,可能会和链接时一样出现 process_begin : CreateProcess 的问题,由于命令 rm 后的参数过长,导致 windows 不能处理此命令。
+
+
+
+推荐您使用以下方法解决这个问题:
+
+- 点击下载 [clean_script.exe](../figures/clean_script.exe)
+
+- 将 clean_script.exe 置于工程根目录下
+
+- 修改工程根目录下的 makefile.targets 中的 clean2 命令为 :
+
+ ```
+ clean2:
+ @$(file > clean, $(OBJS) $(C_DEPS) $(CPP_DEPS))../clean_script.exe clean
+ -$(RM) $(CC_DEPS) $(C++_DEPS) $(C_UPPER_DEPS) $(CXX_DEPS) $(SECONDARY_FLASH) $(SECONDARY_SIZE) $(ASM_DEPS) $(S_UPPER_DEPS) *.elf
+ -@echo ' '
+ ```
+
+ 注:clean2 下的命令前的空格是 Tab ,不能使用空格键
+
+- 重新清理工程
+
+## 添加软件包后在 packages 目录找不到的解决办法
+
+如果在 RT-Thread Settings 添加软件包后,在 packages 目录下找不到软件包或者软件包被排除编译,请尝试以下方法:
+
+- 以 network_samples-v0.3.0 软件包为例:
+
+ - 双击打开 RT-Thread Settings
+
+ 
+
+ - 右键不能显示的软件包,打开详细配置
+
+ 
+
+ - 为该软件包配置至少配置一个子选项,不然和没勾选的参与编译情况是一样的
+
+ 
+
+ - 保存该配置即可
+
+ 
+
+
+
+
\ No newline at end of file
diff --git a/development-tools/rtthread-studio/figures/image-20210127181325681.png b/development-tools/rtthread-studio/figures/image-20210127181325681.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e01972022642674a0727f221c4f6fdd7c29cbff
Binary files /dev/null and b/development-tools/rtthread-studio/figures/image-20210127181325681.png differ
diff --git a/development-tools/rtthread-studio/um/figures/1587034536740.png b/development-tools/rtthread-studio/um/figures/1587034536740.png
new file mode 100644
index 0000000000000000000000000000000000000000..4b54f4d1de04ed950400fffe762d84017f2e76cd
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/1587034536740.png differ
diff --git a/development-tools/rtthread-studio/um/figures/1587034628405.png b/development-tools/rtthread-studio/um/figures/1587034628405.png
new file mode 100644
index 0000000000000000000000000000000000000000..3b1de7851af1e595757351a5ed6e7c766d145171
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/1587034628405.png differ
diff --git a/development-tools/rtthread-studio/um/figures/1587034767730.png b/development-tools/rtthread-studio/um/figures/1587034767730.png
new file mode 100644
index 0000000000000000000000000000000000000000..a17b45ad24a333d08bb24b7a37d16112dd5ce7f2
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/1587034767730.png differ
diff --git a/development-tools/rtthread-studio/um/figures/1587034943959.png b/development-tools/rtthread-studio/um/figures/1587034943959.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc1727d604a852edb527db74eb277c4dac8795ea
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/1587034943959.png differ
diff --git a/development-tools/rtthread-studio/um/figures/1587034996637.png b/development-tools/rtthread-studio/um/figures/1587034996637.png
new file mode 100644
index 0000000000000000000000000000000000000000..86a08c23b903f6cefe76290b1e7975c41b5835d4
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/1587034996637.png differ
diff --git a/development-tools/rtthread-studio/um/figures/1587036090606.png b/development-tools/rtthread-studio/um/figures/1587036090606.png
new file mode 100644
index 0000000000000000000000000000000000000000..95f94dfa9af627c3125f544cbc298bee1bed852c
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/1587036090606.png differ
diff --git a/development-tools/rtthread-studio/um/figures/Encoding_01.png b/development-tools/rtthread-studio/um/figures/Encoding_01.png
new file mode 100644
index 0000000000000000000000000000000000000000..36acf9da292e829822759219eee38731095d2c51
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/Encoding_01.png differ
diff --git a/development-tools/rtthread-studio/um/figures/Encoding_02.png b/development-tools/rtthread-studio/um/figures/Encoding_02.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c858cbb0e52e323c7e37088cece4d27112a31da
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/Encoding_02.png differ
diff --git a/development-tools/rtthread-studio/um/figures/Encoding_03.png b/development-tools/rtthread-studio/um/figures/Encoding_03.png
new file mode 100644
index 0000000000000000000000000000000000000000..37b01db52253b16164315d8da9cbdde9f0d57693
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/Encoding_03.png differ
diff --git a/development-tools/rtthread-studio/um/figures/Encoding_04.png b/development-tools/rtthread-studio/um/figures/Encoding_04.png
new file mode 100644
index 0000000000000000000000000000000000000000..b5380a0f404c110f4de876bb0574b51550bf3564
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/Encoding_04.png differ
diff --git a/development-tools/rtthread-studio/um/figures/Encoding_05.png b/development-tools/rtthread-studio/um/figures/Encoding_05.png
new file mode 100644
index 0000000000000000000000000000000000000000..adfef5dd2d7e302ae3922c0e1326c0e9972ec103
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/Encoding_05.png differ
diff --git a/development-tools/rtthread-studio/um/figures/RTOS-API.png b/development-tools/rtthread-studio/um/figures/RTOS-API.png
new file mode 100644
index 0000000000000000000000000000000000000000..c3960abb7db6022f8ed1d6150dcfd15cc578d6b5
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/RTOS-API.png differ
diff --git a/development-tools/rtthread-studio/um/figures/SDK-Manager.png b/development-tools/rtthread-studio/um/figures/SDK-Manager.png
new file mode 100644
index 0000000000000000000000000000000000000000..8a069216a12cad133bb99f4599a5705ff230129e
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/SDK-Manager.png differ
diff --git a/development-tools/rtthread-studio/um/figures/add-binary-library-re.png b/development-tools/rtthread-studio/um/figures/add-binary-library-re.png
new file mode 100644
index 0000000000000000000000000000000000000000..e658541ad9652f3ef3f9d6b04a05f29ddfe27598
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/add-binary-library-re.png differ
diff --git a/development-tools/rtthread-studio/um/figures/add-binary-library.png b/development-tools/rtthread-studio/um/figures/add-binary-library.png
new file mode 100644
index 0000000000000000000000000000000000000000..60803deb03ec3a8409481c22928e73347dd70604
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/add-binary-library.png differ
diff --git a/development-tools/rtthread-studio/um/figures/add-packages.png b/development-tools/rtthread-studio/um/figures/add-packages.png
new file mode 100644
index 0000000000000000000000000000000000000000..db20938e3142cdb450a5b0b22ae8c3dd45e3d154
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/add-packages.png differ
diff --git a/development-tools/rtthread-studio/um/figures/add_file_to_studio.gif b/development-tools/rtthread-studio/um/figures/add_file_to_studio.gif
new file mode 100644
index 0000000000000000000000000000000000000000..7314d89de022db4e567167b1583c7969e4191892
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/add_file_to_studio.gif differ
diff --git a/development-tools/rtthread-studio/um/figures/addformat.png b/development-tools/rtthread-studio/um/figures/addformat.png
new file mode 100644
index 0000000000000000000000000000000000000000..baa6cfe6edd649eeabe6b40af42e36fb38076134
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/addformat.png differ
diff --git a/development-tools/rtthread-studio/um/figures/apollo_project.png b/development-tools/rtthread-studio/um/figures/apollo_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a5b47d7238facd8db3d9e642711413d80bd8515
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/apollo_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/asmstep.png b/development-tools/rtthread-studio/um/figures/asmstep.png
new file mode 100644
index 0000000000000000000000000000000000000000..90d1108502225193a6e80ac2665b5c4c402b6a6b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/asmstep.png differ
diff --git a/development-tools/rtthread-studio/um/figures/breakpoint.png b/development-tools/rtthread-studio/um/figures/breakpoint.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e73782568e21a02ee62925e7033400290dec347
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/breakpoint.png differ
diff --git a/development-tools/rtthread-studio/um/figures/build-entry.png b/development-tools/rtthread-studio/um/figures/build-entry.png
new file mode 100644
index 0000000000000000000000000000000000000000..f4a7faa9b28c355c3db62e0338e1f4bcc1eb1b83
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/build-entry.png differ
diff --git a/development-tools/rtthread-studio/um/figures/build-info.png b/development-tools/rtthread-studio/um/figures/build-info.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae527ee22d8dcadd604fab38ae488ab78bbbedca
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/build-info.png differ
diff --git a/development-tools/rtthread-studio/um/figures/build-pro.png b/development-tools/rtthread-studio/um/figures/build-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..1eb7d1f9311fd6b4866bd44db3dbd2be5ee622ca
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/build-pro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/build_project.png b/development-tools/rtthread-studio/um/figures/build_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..349bcd1306f4e7f70f0ecc39adf5f1566f09ca21
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/build_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/can-close.png b/development-tools/rtthread-studio/um/figures/can-close.png
new file mode 100644
index 0000000000000000000000000000000000000000..25601d6b16bb99f7ccb8f3a2252e6347148768fd
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/can-close.png differ
diff --git a/development-tools/rtthread-studio/um/figures/code-edit.png b/development-tools/rtthread-studio/um/figures/code-edit.png
new file mode 100644
index 0000000000000000000000000000000000000000..57d92be462354bcb571442e028ec1366019fb55c
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/code-edit.png differ
diff --git a/development-tools/rtthread-studio/um/figures/compile.png b/development-tools/rtthread-studio/um/figures/compile.png
new file mode 100644
index 0000000000000000000000000000000000000000..8df332c3292170c7d0e725ac32c688bebe125714
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/compile.png differ
diff --git a/development-tools/rtthread-studio/um/figures/component.png b/development-tools/rtthread-studio/um/figures/component.png
new file mode 100644
index 0000000000000000000000000000000000000000..3ec044c6c4291a147740fff4a98dd04737b627c3
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/component.png differ
diff --git a/development-tools/rtthread-studio/um/figures/create_new_folder.gif b/development-tools/rtthread-studio/um/figures/create_new_folder.gif
new file mode 100644
index 0000000000000000000000000000000000000000..391cbccbdbd1802640aa0d478d537a3429187fda
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/create_new_folder.gif differ
diff --git a/development-tools/rtthread-studio/um/figures/debug-pro.png b/development-tools/rtthread-studio/um/figures/debug-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..252b024b9c06c32ec9d78dadb34b264e18af5f89
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/debug-pro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/debug-see.png b/development-tools/rtthread-studio/um/figures/debug-see.png
new file mode 100644
index 0000000000000000000000000000000000000000..e8a1aef5ee36fb7176fbad83a469168d9ec55d99
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/debug-see.png differ
diff --git a/development-tools/rtthread-studio/um/figures/debugentry.png b/development-tools/rtthread-studio/um/figures/debugentry.png
new file mode 100644
index 0000000000000000000000000000000000000000..f549245b9468777001ccc0968939f45d4621a0b4
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/debugentry.png differ
diff --git a/development-tools/rtthread-studio/um/figures/debuger.png b/development-tools/rtthread-studio/um/figures/debuger.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0e0c36cc2152d8a90c56289194966df6e6bbf07
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/debuger.png differ
diff --git a/development-tools/rtthread-studio/um/figures/debugopt.png b/development-tools/rtthread-studio/um/figures/debugopt.png
new file mode 100644
index 0000000000000000000000000000000000000000..f898d4276af17876c2a3456840ca14e9a1a2ad09
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/debugopt.png differ
diff --git a/development-tools/rtthread-studio/um/figures/debugset.png b/development-tools/rtthread-studio/um/figures/debugset.png
new file mode 100644
index 0000000000000000000000000000000000000000..bbad0f572b9f8158dcd2f56d50947378135ae38d
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/debugset.png differ
diff --git a/development-tools/rtthread-studio/um/figures/deletesdk.png b/development-tools/rtthread-studio/um/figures/deletesdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ad4abc05c0acc6e06bdae3ca68cfa8294a4b805
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/deletesdk.png differ
diff --git a/development-tools/rtthread-studio/um/figures/detail-set.png b/development-tools/rtthread-studio/um/figures/detail-set.png
new file mode 100644
index 0000000000000000000000000000000000000000..c94609ecd89d8aba17ed488f5276c94c7b3ad144
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/detail-set.png differ
diff --git a/development-tools/rtthread-studio/um/figures/devstyle1.png b/development-tools/rtthread-studio/um/figures/devstyle1.png
new file mode 100644
index 0000000000000000000000000000000000000000..67b13339598f6ad7e8860e827a5183125fc3e625
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/devstyle1.png differ
diff --git a/development-tools/rtthread-studio/um/figures/devstyle2.png b/development-tools/rtthread-studio/um/figures/devstyle2.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff43ae95f9400d342bebf015eec3065509b9b53d
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/devstyle2.png differ
diff --git a/development-tools/rtthread-studio/um/figures/done-pro.png b/development-tools/rtthread-studio/um/figures/done-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..ceab326bb9a0bf71c5e6a86c8274dc52b4bc4da9
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/done-pro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/download-info.png b/development-tools/rtthread-studio/um/figures/download-info.png
new file mode 100644
index 0000000000000000000000000000000000000000..78eee462888bd4f2e46947276f83d4eb9b650a37
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/download-info.png differ
diff --git a/development-tools/rtthread-studio/um/figures/download-pro.png b/development-tools/rtthread-studio/um/figures/download-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..56a14b028842cb112bf50faf6f5d0b38e6c8ed97
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/download-pro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/editmd.png b/development-tools/rtthread-studio/um/figures/editmd.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f52fc570ca5e342b016805ed154d54818254517
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/editmd.png differ
diff --git a/development-tools/rtthread-studio/um/figures/editreg.png b/development-tools/rtthread-studio/um/figures/editreg.png
new file mode 100644
index 0000000000000000000000000000000000000000..e5a1871b8be21c9d32acc652bb964e69d1dcfaa7
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/editreg.png differ
diff --git a/development-tools/rtthread-studio/um/figures/firstopt.png b/development-tools/rtthread-studio/um/figures/firstopt.png
new file mode 100644
index 0000000000000000000000000000000000000000..64263e2a2c9021d1c7126520abc8a0d227dda320
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/firstopt.png differ
diff --git a/development-tools/rtthread-studio/um/figures/generate_dirs.png b/development-tools/rtthread-studio/um/figures/generate_dirs.png
new file mode 100644
index 0000000000000000000000000000000000000000..b98754f6f8d8f4cc443456b30ae526101770bbd6
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/generate_dirs.png differ
diff --git a/development-tools/rtthread-studio/um/figures/help-bottom.png b/development-tools/rtthread-studio/um/figures/help-bottom.png
new file mode 100644
index 0000000000000000000000000000000000000000..635bfda0e8d68a5b9ebb60fec9f200030bade908
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/help-bottom.png differ
diff --git a/development-tools/rtthread-studio/um/figures/import_project_name.png b/development-tools/rtthread-studio/um/figures/import_project_name.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a2e329d110eff055a7aef22b3d1379b72ace94b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/import_project_name.png differ
diff --git a/development-tools/rtthread-studio/um/figures/import_success.png b/development-tools/rtthread-studio/um/figures/import_success.png
new file mode 100644
index 0000000000000000000000000000000000000000..a0f9b7055e06082343e484e13a911967cecf3127
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/import_success.png differ
diff --git a/development-tools/rtthread-studio/um/figures/importdone.png b/development-tools/rtthread-studio/um/figures/importdone.png
new file mode 100644
index 0000000000000000000000000000000000000000..ed58379600aeaec1ab4aa02d1327cd186f31b702
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/importdone.png differ
diff --git a/development-tools/rtthread-studio/um/figures/importpro.png b/development-tools/rtthread-studio/um/figures/importpro.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0ac8c372b662a6802a2dcf63973b53a41107f1a
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/importpro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/install-studio.png b/development-tools/rtthread-studio/um/figures/install-studio.png
new file mode 100644
index 0000000000000000000000000000000000000000..809bffe0b88b4f3b7d8d1a8d78f67510ced4b480
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/install-studio.png differ
diff --git a/development-tools/rtthread-studio/um/figures/installsdk.png b/development-tools/rtthread-studio/um/figures/installsdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0fa56d6d69d968a386fd9e4af05f13c6655185d
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/installsdk.png differ
diff --git a/development-tools/rtthread-studio/um/figures/license.png b/development-tools/rtthread-studio/um/figures/license.png
new file mode 100644
index 0000000000000000000000000000000000000000..376f545ca1e4cc109f7a55475b6dd6b16fb6fe93
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/license.png differ
diff --git a/development-tools/rtthread-studio/um/figures/look-detail.png b/development-tools/rtthread-studio/um/figures/look-detail.png
new file mode 100644
index 0000000000000000000000000000000000000000..378313be9779806013aa48d15fb5ac51e355afae
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/look-detail.png differ
diff --git a/development-tools/rtthread-studio/um/figures/max-pic.png b/development-tools/rtthread-studio/um/figures/max-pic.png
new file mode 100644
index 0000000000000000000000000000000000000000..2fc36f3f9fc4d77ccf4b135f40318f939818697b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/max-pic.png differ
diff --git a/development-tools/rtthread-studio/um/figures/max-size.png b/development-tools/rtthread-studio/um/figures/max-size.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a9b68bd1fab5ef3b1ed700388b8330810d6ae1b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/max-size.png differ
diff --git a/development-tools/rtthread-studio/um/figures/mdk_iar_import.png b/development-tools/rtthread-studio/um/figures/mdk_iar_import.png
new file mode 100644
index 0000000000000000000000000000000000000000..d01194ccb4119c81797f8fccb14f6ace4a30c057
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/mdk_iar_import.png differ
diff --git a/development-tools/rtthread-studio/um/figures/new-pro.png b/development-tools/rtthread-studio/um/figures/new-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e200a205d4194241cdda298b658aa7b0d7837f6
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/new-pro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/new-resource.png b/development-tools/rtthread-studio/um/figures/new-resource.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce2e008e172b29c450b71bd31870a33b2f313559
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/new-resource.png differ
diff --git a/development-tools/rtthread-studio/um/figures/open-detail.png b/development-tools/rtthread-studio/um/figures/open-detail.png
new file mode 100644
index 0000000000000000000000000000000000000000..06808da196b4b4f8906041ace279a72dc397542f
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/open-detail.png differ
diff --git a/development-tools/rtthread-studio/um/figures/open-element.png b/development-tools/rtthread-studio/um/figures/open-element.png
new file mode 100644
index 0000000000000000000000000000000000000000..31778cf547988c9c6f9ccc6c3304b6e2f22a95a4
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/open-element.png differ
diff --git a/development-tools/rtthread-studio/um/figures/open-set.png b/development-tools/rtthread-studio/um/figures/open-set.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b225f953a198b6e1f8a74a477f5fafcc6b37e50
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/open-set.png differ
diff --git a/development-tools/rtthread-studio/um/figures/optmenu.png b/development-tools/rtthread-studio/um/figures/optmenu.png
new file mode 100644
index 0000000000000000000000000000000000000000..03aeb36c635c0d491b7df8d5e20ae51c2bb5c675
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/optmenu.png differ
diff --git a/development-tools/rtthread-studio/um/figures/pahomqtt.png b/development-tools/rtthread-studio/um/figures/pahomqtt.png
new file mode 100644
index 0000000000000000000000000000000000000000..b7586c4557507ddd61eb0515d80519876deffbe9
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/pahomqtt.png differ
diff --git a/development-tools/rtthread-studio/um/figures/path-include.png b/development-tools/rtthread-studio/um/figures/path-include.png
new file mode 100644
index 0000000000000000000000000000000000000000..341a0921c24fed7924580fb1ac6d019dbcd8c138
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/path-include.png differ
diff --git a/development-tools/rtthread-studio/um/figures/path-install.png b/development-tools/rtthread-studio/um/figures/path-install.png
new file mode 100644
index 0000000000000000000000000000000000000000..637c79ad183329cea09a099b46b3a030a8186c1e
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/path-install.png differ
diff --git a/development-tools/rtthread-studio/um/figures/platformio_build_project.png b/development-tools/rtthread-studio/um/figures/platformio_build_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..8c8569168ea24385e88503ad7511160928e0d08b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/platformio_build_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/platformio_debug_project.png b/development-tools/rtthread-studio/um/figures/platformio_debug_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..50cb00b892fbaff010ccc6b4a9fcbd142d69bb2a
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/platformio_debug_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/platformio_debug_view.png b/development-tools/rtthread-studio/um/figures/platformio_debug_view.png
new file mode 100644
index 0000000000000000000000000000000000000000..de40130dcca9204693ccbebb23f1e7fa6ba3fca2
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/platformio_debug_view.png differ
diff --git a/development-tools/rtthread-studio/um/figures/platformio_download.png b/development-tools/rtthread-studio/um/figures/platformio_download.png
new file mode 100644
index 0000000000000000000000000000000000000000..62a29cd2ebf44da9d09dc853414d8e5b59cbeaa2
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/platformio_download.png differ
diff --git a/development-tools/rtthread-studio/um/figures/platformio_download_project.png b/development-tools/rtthread-studio/um/figures/platformio_download_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c294b651c39423e1a4f2a2fc1764da7aae331b9
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/platformio_download_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/platformio_new_project.png b/development-tools/rtthread-studio/um/figures/platformio_new_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a4075b9c8589a9a06ba9f52cb5ff3b0ef5d2ed5
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/platformio_new_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/platformio_open_serial.png b/development-tools/rtthread-studio/um/figures/platformio_open_serial.png
new file mode 100644
index 0000000000000000000000000000000000000000..9666c7ce263dae7899f40ec336ac33ec6c6fe0c1
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/platformio_open_serial.png differ
diff --git a/development-tools/rtthread-studio/um/figures/pro-set.png b/development-tools/rtthread-studio/um/figures/pro-set.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b3a2eb300cd84ee47a038c76183f1c062528ede
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/pro-set.png differ
diff --git a/development-tools/rtthread-studio/um/figures/project_structure.png b/development-tools/rtthread-studio/um/figures/project_structure.png
new file mode 100644
index 0000000000000000000000000000000000000000..78d031f1a2b75754af69e841c499fc788faf2579
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/project_structure.png differ
diff --git a/development-tools/rtthread-studio/um/figures/putty.png b/development-tools/rtthread-studio/um/figures/putty.png
new file mode 100644
index 0000000000000000000000000000000000000000..d03346dc8dfc8e6541bd738c822ac2b377a37a18
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/putty.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_configure.png b/development-tools/rtthread-studio/um/figures/qemu_configure.png
new file mode 100644
index 0000000000000000000000000000000000000000..894a8c30656a6dfbf1d9737e9cdabaf05ac1bbaa
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_configure.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_debug.png b/development-tools/rtthread-studio/um/figures/qemu_debug.png
new file mode 100644
index 0000000000000000000000000000000000000000..fe50075b4f7d3213ff1229833219620b2d58f2cf
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_debug.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_network_configure_stm32f4.png b/development-tools/rtthread-studio/um/figures/qemu_network_configure_stm32f4.png
new file mode 100644
index 0000000000000000000000000000000000000000..880987124ef156996857fad7377abf873b519d30
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_network_configure_stm32f4.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_network_debug_stm32f4.png b/development-tools/rtthread-studio/um/figures/qemu_network_debug_stm32f4.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc140924de8e666049a7c95944f5bbd47c741e4f
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_network_debug_stm32f4.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_network_new_stm32f4.png b/development-tools/rtthread-studio/um/figures/qemu_network_new_stm32f4.png
new file mode 100644
index 0000000000000000000000000000000000000000..25ca5f9b2cf44637f43338671f5d6d91b50eb24e
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_network_new_stm32f4.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_network_rt-thread_settings.png b/development-tools/rtthread-studio/um/figures/qemu_network_rt-thread_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..e68a04ccf2906debb65baf18a0aad8b27be8aa48
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_network_rt-thread_settings.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_network_rtthread_setting2.png b/development-tools/rtthread-studio/um/figures/qemu_network_rtthread_setting2.png
new file mode 100644
index 0000000000000000000000000000000000000000..9b837be952a68d306a53ae5872f6cce8afff00fe
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_network_rtthread_setting2.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_network_test_stm32f4.png b/development-tools/rtthread-studio/um/figures/qemu_network_test_stm32f4.png
new file mode 100644
index 0000000000000000000000000000000000000000..b99723fa22c5c5a69f2e17a052015fa819990b53
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_network_test_stm32f4.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_new_project.png b/development-tools/rtthread-studio/um/figures/qemu_new_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..82cbfe7d5d9d3e5435959c5ea79b6bd0b166b11d
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_new_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/qemu_run.png b/development-tools/rtthread-studio/um/figures/qemu_run.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6c5ae459db2e20f5ce3f5854c60b1b46a609ff4
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/qemu_run.png differ
diff --git a/development-tools/rtthread-studio/um/figures/rebuild.png b/development-tools/rtthread-studio/um/figures/rebuild.png
new file mode 100644
index 0000000000000000000000000000000000000000..757ad04acab7e00a1eabaa3648f677583d0207a3
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/rebuild.png differ
diff --git a/development-tools/rtthread-studio/um/figures/rebuild_project.png b/development-tools/rtthread-studio/um/figures/rebuild_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f76950093da3863d99a9c6771fab58e33246393
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/rebuild_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/reset-see.png b/development-tools/rtthread-studio/um/figures/reset-see.png
new file mode 100644
index 0000000000000000000000000000000000000000..e9382362960022dbf9b935e763d44c84a956e325
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/reset-see.png differ
diff --git a/development-tools/rtthread-studio/um/figures/restore.png b/development-tools/rtthread-studio/um/figures/restore.png
new file mode 100644
index 0000000000000000000000000000000000000000..1779c2f53b948102b98d6738506353cd2bf55ba6
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/restore.png differ
diff --git a/development-tools/rtthread-studio/um/figures/rtthread-pro.png b/development-tools/rtthread-studio/um/figures/rtthread-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..e61d13a8c0b387e2154e24f3258636e6c917e6b9
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/rtthread-pro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/save-set.png b/development-tools/rtthread-studio/um/figures/save-set.png
new file mode 100644
index 0000000000000000000000000000000000000000..cff03ffadf3f8592650c9f8f9986017d397907b5
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/save-set.png differ
diff --git a/development-tools/rtthread-studio/um/figures/sdkmanager.png b/development-tools/rtthread-studio/um/figures/sdkmanager.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7eb56f0fea82633b334fc7a7ead0b515892b47b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/sdkmanager.png differ
diff --git a/development-tools/rtthread-studio/um/figures/search-menu.png b/development-tools/rtthread-studio/um/figures/search-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7ab5ad4ea8afe9633832bc50f988adfe66a8459
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/search-menu.png differ
diff --git a/development-tools/rtthread-studio/um/figures/search-set.png b/development-tools/rtthread-studio/um/figures/search-set.png
new file mode 100644
index 0000000000000000000000000000000000000000..4b5915311cb9497acc67fc64fc0ac46a571fc544
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/search-set.png differ
diff --git a/development-tools/rtthread-studio/um/figures/see-depend.png b/development-tools/rtthread-studio/um/figures/see-depend.png
new file mode 100644
index 0000000000000000000000000000000000000000..7e968e561c4172531ee07e444de1d6706e3668c4
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/see-depend.png differ
diff --git a/development-tools/rtthread-studio/um/figures/seemem.png b/development-tools/rtthread-studio/um/figures/seemem.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5e3a2a4cec5eda784f4106dc5514a1520eef98a
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/seemem.png differ
diff --git a/development-tools/rtthread-studio/um/figures/seememreg.png b/development-tools/rtthread-studio/um/figures/seememreg.png
new file mode 100644
index 0000000000000000000000000000000000000000..7e35d6e88da9f7c063f3219ce89baa26790b62cc
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/seememreg.png differ
diff --git a/development-tools/rtthread-studio/um/figures/seeperial.png b/development-tools/rtthread-studio/um/figures/seeperial.png
new file mode 100644
index 0000000000000000000000000000000000000000..239446daf3df519b3b2af91770b0db67a77b13db
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/seeperial.png differ
diff --git a/development-tools/rtthread-studio/um/figures/seereg.png b/development-tools/rtthread-studio/um/figures/seereg.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f929003e5cbad6e8371381bbd5c69febeb6ef14
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/seereg.png differ
diff --git a/development-tools/rtthread-studio/um/figures/seeterminal.png b/development-tools/rtthread-studio/um/figures/seeterminal.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d1edece58b77a202367173a3eb67644d74cd202
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/seeterminal.png differ
diff --git a/development-tools/rtthread-studio/um/figures/seevalue.png b/development-tools/rtthread-studio/um/figures/seevalue.png
new file mode 100644
index 0000000000000000000000000000000000000000..f2945428026a9204922be5fcba0061e76cb210c4
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/seevalue.png differ
diff --git a/development-tools/rtthread-studio/um/figures/select_import.png b/development-tools/rtthread-studio/um/figures/select_import.png
new file mode 100644
index 0000000000000000000000000000000000000000..0b0fef9f22261c7e171189803bc16b166334965e
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/select_import.png differ
diff --git a/development-tools/rtthread-studio/um/figures/select_rtt_project.png b/development-tools/rtthread-studio/um/figures/select_rtt_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..a9cc0b0effc480fdeff5c9e6f9fa9d66365fe08c
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/select_rtt_project.png differ
diff --git a/development-tools/rtthread-studio/um/figures/set-ok.png b/development-tools/rtthread-studio/um/figures/set-ok.png
new file mode 100644
index 0000000000000000000000000000000000000000..10766a2998c149d8d62186e5ccaf52c66114756e
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/set-ok.png differ
diff --git a/development-tools/rtthread-studio/um/figures/setlinkscripts.png b/development-tools/rtthread-studio/um/figures/setlinkscripts.png
new file mode 100644
index 0000000000000000000000000000000000000000..79e4d884c8c84a9a6fea8207735581268ca904f7
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/setlinkscripts.png differ
diff --git a/development-tools/rtthread-studio/um/figures/setmicro.png b/development-tools/rtthread-studio/um/figures/setmicro.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f50ccd1f6db217f644370acc59bd767f193b2b7
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/setmicro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/setother.png b/development-tools/rtthread-studio/um/figures/setother.png
new file mode 100644
index 0000000000000000000000000000000000000000..82114d94285c46fc3bb1f56a3ec4e59eeca208d4
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/setother.png differ
diff --git a/development-tools/rtthread-studio/um/figures/setting.png b/development-tools/rtthread-studio/um/figures/setting.png
new file mode 100644
index 0000000000000000000000000000000000000000..642447282eb1397545e7be613e8ac86d75999305
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/setting.png differ
diff --git a/development-tools/rtthread-studio/um/figures/shuxing.png b/development-tools/rtthread-studio/um/figures/shuxing.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f9e9bc319b3d00854f54bec8be4db8f8d53bafb
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/shuxing.png differ
diff --git a/development-tools/rtthread-studio/um/figures/sign-in.png b/development-tools/rtthread-studio/um/figures/sign-in.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f25de8dc85b404e5c152453bfa7c2c8651f0243
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/sign-in.png differ
diff --git a/development-tools/rtthread-studio/um/figures/source.png b/development-tools/rtthread-studio/um/figures/source.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c5d811cbb5ada5b6998a315db8d7c1f08d17708
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/source.png differ
diff --git a/development-tools/rtthread-studio/um/figures/specific_linker_file.png b/development-tools/rtthread-studio/um/figures/specific_linker_file.png
new file mode 100644
index 0000000000000000000000000000000000000000..896d2f01dcb3a81f49ad4389572933ac4a5f1e62
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/specific_linker_file.png differ
diff --git a/development-tools/rtthread-studio/um/figures/start-install.png b/development-tools/rtthread-studio/um/figures/start-install.png
new file mode 100644
index 0000000000000000000000000000000000000000..a3f1bf61c6e5559dffaa9a5c5277ce523dbb7999
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/start-install.png differ
diff --git a/development-tools/rtthread-studio/um/figures/start-menu.png b/development-tools/rtthread-studio/um/figures/start-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..e14238c2b3131c0c5b509b97187acd132f4f893c
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/start-menu.png differ
diff --git a/development-tools/rtthread-studio/um/figures/start-name.png b/development-tools/rtthread-studio/um/figures/start-name.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c327871f9b9686ef19538e3614f99cee43cc9a2
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/start-name.png differ
diff --git a/development-tools/rtthread-studio/um/figures/start-studio.png b/development-tools/rtthread-studio/um/figures/start-studio.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b6d2aa7fbb9ae88d80cca4b83cb9a8c6dac059c
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/start-studio.png differ
diff --git a/development-tools/rtthread-studio/um/figures/studio-frame.png b/development-tools/rtthread-studio/um/figures/studio-frame.png
new file mode 100644
index 0000000000000000000000000000000000000000..37fda07631c4631a9c44244d144d866e33ba5bcb
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/studio-frame.png differ
diff --git a/development-tools/rtthread-studio/um/figures/studio-pic.png b/development-tools/rtthread-studio/um/figures/studio-pic.png
new file mode 100644
index 0000000000000000000000000000000000000000..c2a83f2364758a18d9a03453e64110502bbdac5b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/studio-pic.png differ
diff --git a/development-tools/rtthread-studio/um/figures/switch_qemu_prompt.png b/development-tools/rtthread-studio/um/figures/switch_qemu_prompt.png
new file mode 100644
index 0000000000000000000000000000000000000000..bda8cb60286cb63c79438b8b534f91c3cd845432
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/switch_qemu_prompt.png differ
diff --git a/development-tools/rtthread-studio/um/figures/switch_to_qemu.png b/development-tools/rtthread-studio/um/figures/switch_to_qemu.png
new file mode 100644
index 0000000000000000000000000000000000000000..198765d7bedde25cc4b652c42f3d8ca21d6a115b
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/switch_to_qemu.png differ
diff --git a/development-tools/rtthread-studio/um/figures/switchdebug.png b/development-tools/rtthread-studio/um/figures/switchdebug.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ccc5e958553cf2376bf8edf99c4dd58dbb3ea99
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/switchdebug.png differ
diff --git a/development-tools/rtthread-studio/um/figures/terminal.png b/development-tools/rtthread-studio/um/figures/terminal.png
new file mode 100644
index 0000000000000000000000000000000000000000..d3c50e99dcfe98d0b7a310975acfcbc5f1a40dca
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/terminal.png differ
diff --git a/development-tools/rtthread-studio/um/figures/test-pro.png b/development-tools/rtthread-studio/um/figures/test-pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..d03570019ba5dbb6cf5e62141c1c23f6c68a9360
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/test-pro.png differ
diff --git a/development-tools/rtthread-studio/um/figures/tree-set.png b/development-tools/rtthread-studio/um/figures/tree-set.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c684d3d34d2ca326377da604dfb67d491536d1e
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/tree-set.png differ
diff --git a/development-tools/rtthread-studio/um/figures/updatesdk.png b/development-tools/rtthread-studio/um/figures/updatesdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..9157be56e46019ab4d5bf9573fc954cc201c8a35
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/updatesdk.png differ
diff --git a/development-tools/rtthread-studio/um/figures/waitting.png b/development-tools/rtthread-studio/um/figures/waitting.png
new file mode 100644
index 0000000000000000000000000000000000000000..a87ff6501eed95b3664f2a19fcefcfcc57c6db5e
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/waitting.png differ
diff --git a/development-tools/rtthread-studio/um/figures/welcome-page.png b/development-tools/rtthread-studio/um/figures/welcome-page.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd4d685a16e6166fba140139104fa0b6a3d013fe
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/welcome-page.png differ
diff --git a/development-tools/rtthread-studio/um/figures/windows_express.png b/development-tools/rtthread-studio/um/figures/windows_express.png
new file mode 100644
index 0000000000000000000000000000000000000000..4599f4be5921ea6ee599946d8236db2194acfba2
Binary files /dev/null and b/development-tools/rtthread-studio/um/figures/windows_express.png differ
diff --git a/development-tools/rtthread-studio/um/studio-user-begin.md b/development-tools/rtthread-studio/um/studio-user-begin.md
new file mode 100644
index 0000000000000000000000000000000000000000..74f01c54a45edb06f1e8f2cc0eb76720c5017b04
--- /dev/null
+++ b/development-tools/rtthread-studio/um/studio-user-begin.md
@@ -0,0 +1,111 @@
+## 快速开始
+
+### 安装 RT-Thread Studio
+
+#### 下载 RT-Thread Studio 安装包
+
+访问官网 [RT-Thread Studio 下载地址](https://www.rt-thread.org/page/studio.html),在官网下载最新的 RT-Thread Studio 软件安装包。
+
+#### 安装 RT-Thread Studio
+
+双击安装包的 `.exe` 文件进行安装,安装界面如下图所示:
+
+
+
+安装前需要接受许可协议,如下图所示:
+
+
+
+指定安装路径时不要带有空格和中文字符,如下图所示:
+
+
+
+指定开始菜单文件夹名,如下图所示:
+
+
+
+开始安装
+
+
+
+一直点击`下一步`直到最后点击`安装`按钮可开始进行安装,待安装完成后可直接点击`确定`即可启动 RT-Thread Studio,如下图所示:
+
+
+
+或者取消`运行RT-Thread Studio`勾选,点击完成后,从桌面快捷方式启动 RT-Thread Studio。桌面快捷方式如下图所示:
+
+
+
+第一次启动 RT-Thread Studio 需要进行账户登录,登录一次后会自动记住账号,后续不需要再登录,登录支持第三方账号登陆,登录界面如下:
+
+
+
+### 新建项目
+
+在`项目资源管理器`窗口内点击右键,选择`新建`子菜单`项目`,如下图所示:
+
+
+
+在弹出的新建项目向导对话框中选择`RT-Thread项目`类型,然后点击`下一步`如下图所示:
+
+
+
+填写工程名,选择 RT-Thread 源码版本,选择对应的 BSP,然后点击`完成`按钮,如下图所示:
+
+
+
+点击`完成`后,等待工程创建过程如下图所示:
+
+
+
+工程创建成功后`项目资源管理器`窗口会出现刚创建的工程`test`,如下图所示:
+
+
+
+### 配置项目
+
+双击`RT-Thread Settings`文件,打开 RT-Thread 项目配置界面,配置界面默认显示软件包以及组件和服务层的架构配置图界面,如下图所示:
+
+
+
+点击架构图配置界面右侧侧栏按钮,即可打开配置树配置界面,如下图所示:
+
+
+
+配置完成后,保存配置退出配置界面后,RT-Thread Studio 会自动将配置应用到项目中,比如会自动下载相关资源文件到项目中并设置好项目配置,确保项目配置后能够构建成功,如下图所示:
+
+
+
+### 构建项目
+
+点击工具栏上的`构建`按钮对项目进行构建。如下图所示:
+
+
+
+构建的过程日志在控制台进行打印,如下图所示:
+
+
+
+### 下载程序
+
+当项目构建成功后,点击工具栏`下载程序`按钮旁的三角下拉框选择相应的烧写器,以`J-Link`烧写器为例,如下图所示:
+
+
+
+选择完烧写器后可直接点击`下载程序`按钮进行程序下载,下载日志会在控制台窗口打印,如下图所示:
+
+
+
+### 启动调试
+
+选中`test`工程,然后点击工具栏`调试`按钮,如下图所示:
+
+
+
+当成功启动调试后,RT-Thread Studio 会自动跳转到调试透视图,在调试透视图可以进行各种调试功能操作。当停止调试后会自动跳转会 C 透视图,如下图所示:
+
+
+
+### 视频教程
+
+访问官网 [RT-Thread Studio 视频教程](https://www.rt-thread.org/page/video.html),在官网观看视频教程。
\ No newline at end of file
diff --git a/development-tools/rtthread-studio/um/studio-user-manual.md b/development-tools/rtthread-studio/um/studio-user-manual.md
new file mode 100644
index 0000000000000000000000000000000000000000..09dce1f75cad118b75ba57bc44ab7d124d6e0376
--- /dev/null
+++ b/development-tools/rtthread-studio/um/studio-user-manual.md
@@ -0,0 +1,727 @@
+# RT-Thread Studio 用户手册
+
+## 界面介绍
+
+### 界面简介
+
+RT-Thread Studio 基于 eclipse 平台开发,界面设计和风格继承自 eclipse,RT-Thread Studio 启动后主界面结构如下图所示:
+
+
+
+### 透视图简介
+
+透视图定义了当前界面呈现的菜单栏,工具栏,以及功能窗口集合及其布局。不同透视图提供了完成特定类型任务的功能集合。例如 C 透视图组合了项目开发,源文件编辑,项目构建等常用的开发功能窗口,菜单和功能按钮,调试透视图包含了调试项目程序常用的调试功能窗口,菜单和功能按钮。
+
+RT-Thread Studio 已实现启动调试时自动切换到调试透视图,停止调试时自动恢复到 C 透视图,用户平时也可以根据需要从 ` 透视图切换栏 ` 手动进行透视图切换,切换到其它透视图进行相关工作。
+
+### 功能窗口特性
+
+#### 可移动
+
+RT-Thread Studio 在初次打开的时候功能窗口位置呈现的是默认布局,但所有功能窗口位置都不是固定的,可以在窗口标题处按住鼠标左键,随意拖动窗口的位置,如下图所示左键按住 ` 属性 ` 窗口,拖动到 ` 项目资源管理器 ` 窗口下方,会出现一个灰色方框指示 ` 属性 ` 窗口将要被放置的位置,此时松开鼠标按键即可将 ` 属性 ` 窗口放置在该位置:
+
+
+
+当窗口拖乱了,或者整体布局不满意想恢复回默认布局的样子时,可以通过 ` 复位透视图 ` 菜单功能恢复默认窗口布局,如下图所示:
+
+
+
+#### 可关闭
+
+每个功能窗口标题旁边都有一个 `X` 可以通过点击该处,关闭功能窗口,如下图所示:
+
+
+
+若想再次打开已关闭的功能窗口,可通过菜单栏的 ` 窗口 ` 菜单的子菜单 ` 显示视图 ` 菜单中再次打开对应功能窗口,如果当前菜单中没显示想要打开的功能窗口,可以点击从 ` 其他 ` 菜单中查找。
+
+
+
+#### 最大化
+
+每个功能窗口都有自己单独的工具栏,工具栏最右边是最小化和最大化功能按钮,如下图所示:
+
+
+
+在功能窗口的标题上双击或者点击功能窗口栏上最大化按钮,即可将窗口最大化,占满整个功能窗口区域,其它窗口将会暂时最小化到侧栏内,如下图所示:
+
+
+
+再次双击 ` 项目资源管理器 ` 功能窗口即可恢复之前的功能窗口位置和状态。
+
+#### 最小化
+
+点击功能窗口最小化按钮,功能窗口将会暂时缩小到侧栏位置放置,点击 ` 恢复 ` 按钮即可恢复原来状态,如下图所示:
+
+
+
+### 工具栏按钮介绍
+
+#### 编译
+
+选中一个项目,然后点击 ` 编译 ` 按钮即可完成编译,如下图所示:
+
+
+
+#### 重构建
+
+选中一个项目,然后点击 ` 重构建 ` 按钮即可完成重构建,如下图所示:
+
+
+
+#### 构建配置
+
+构建项目之前如果需要对项目进行构建参数配置,点击工具栏上 ` 打开构建配置 ` 按钮对项目进行构建参数配置,如下图所示:
+
+
+
+#### 调试配置
+
+进行下载或启动调试之前如果需要对项目进行相关调试参数配置,通过点击工具栏上的 ` 调试配置 ` 按钮,可打开调试配置对话框界面,如下图所示:
+
+
+
+#### 启动调试
+
+选中一个项目,然后点击 ` 启动调试 ` 按钮即可进入调试模式,如下图所示:
+
+
+
+#### 打开元素
+
+` 打开元素 ` 按钮其实就是一个搜索功能,可以指定搜索的类型,如下图所示:
+
+
+
+
+
+#### 搜索
+
+通过搜索菜单或者搜索按钮,选择对应的搜索功能,如下图所示:
+
+
+
+#### 打开终端
+
+通过点击工具栏 ` 打开终端 ` 按钮即可打开终端选择界面,如下图所示:
+
+
+
+#### 打开 RT-Thread RTOS API 文档
+
+通过点击工具栏 ` 打开 RT-Thread RTOS API 文档 ` 按钮即可打开 RT-Thread API 参考手册,如下图所示:
+
+
+
+#### 下载程序
+
+` 下载程序 ` 按钮除了可以下载程序以外,还可以通过旁边的三角下拉按钮来切换调试器,如下图所示:
+
+
+
+#### SDK Manger
+
+`SDK Manger` 可以管理源码包、芯片支持包、开发板资源包、工具链资源包、调试工具包、第三方资源包,可以根据需要进行下载相应的资源。
+
+
+
+## 欢迎页
+
+RT-Thread Studio 每次启动打开软件主界面后会展示一个最大化的欢迎页窗口,如下图所示:
+
+
+
+欢迎页左侧有四个便利的功能入口:` 创建 RT-Thread 项目 `,`RT-Thread 论坛 `,` 视频教程 `,` 帮助文档 `,直接点击相应的功能名称即可使用对应功能。欢迎页右侧展示了三类内容:` 最新动态 `,` 视频教程 `,` 最新 PR`,点击对应的标签即可查看或者浏览对应标签页的内容。
+
+
+## 新建
+
+新建资源功能包括新建各类资源,例如工程,文件,文件夹等,新建入口有菜单,工具栏按钮,和工程右键菜单如下图所示:
+
+
+
+
+## 导入
+
+RT-Thread Studio 的导入功能不仅支持导入现有的 RT-Thread Studio 工程,还支持用户将 MDK/IAR 格式的工程导入到 RT-Thread Studio 中,便于用户迁移开发环境。
+
+### RT-Thread Studio 项目导入
+
+在 Studio 资源管理器窗口中点击右键,在下拉菜单中选择导入功能:
+
+
+
+打开导入功能向导,选择导入 RT-Thread Studio 工程:
+
+
+
+点击 **下一步** 后,点击 **浏览** 按钮选择要导入项目所在的工程目录,导入程序会自动扫描该目录下所有可导入的工程,将结果列出在项目列表中。在工程列表中勾选要导入的工程,然后点击 ` 完成 ` 即可。
+
+
+
+### MDK/IAR 项目导入
+
+开发者可以将现有的 RT-Thread MDK/IAR 工程直接导入到 RT-Thread Studio 中,然后就可以使用 RT-Thread Studio 提供的更多工程配置功能。
+
+`MDK/IAR` 工程在导入到 RT-Thread Studio 后,将有如下特性:
+
+- 保持原有项目的目录结构
+- 保持保持原有项目的源文件
+- 保持原有项目的头文件路径
+- 保持原有项目的宏定义
+- 将原工程中使用的 libc 库相关配置转换为 newlibc
+- 可以使用 RT-Thread Studio 提供的 RT-Thread 系统配置及软件包配置功能
+
+注意 :目前仅支持 STM32 系列芯片的工程导入,后续会支持更多芯片。
+
+#### 导入示例
+
+本小节将以 `bsp/stm32/stm32f429-atk-apollo` 工程为例,演示如何导入一个 MDK 工程到 RT-Thread Studio 中,导入前工程目录如下图所示:
+
+
+
+在 Studio 资源管理器窗口中点击右键,在下拉菜单中选择导入功能,然后选择导入 MDK/IAR 项目到工作空间,然后点击下一步, 如下图所示:
+
+
+
+点击浏览选择工程目录下要导入的 MDK/IAR 工程,然后输入导入后的工程名,点击完成即可, 如下图所示:
+
+
+
+导入成功后,会在原工程目录下创建 RT-Thread Studio 的工程目录文件夹,如下图所示:
+
+
+
+项目资源管理器此时显示界面, 如下图所示 :
+
+
+
+此时直接点击编译按钮编译成功后,如下图所示:
+
+
+
+#### 导入工程管理
+
+当一个 MDK/IAR 工程被导入到 RT-Thread Studio 之后,原工程的组织结构会保持不变,如下图所示:
+
+
+
+与原 MDK 工程相同,RT-Thread Studio Group 中的源文件也可以存放在工程的各个位置,而不必实际上按照这种组织结构而存放文件。
+
+可以注意到导入的 Group 和源文件右下角有一个小方块和箭头指示,表示区别于原生 eclipse 那种所见即所得的文件组织形式。这种右下角带方框或者箭头标识的文件夹或者文件,在 RT-Thread Studio 中分别称为虚拟文件夹和链接文件。
+
+##### 添加与删除源文件
+
+如果想在导入的工程中添加源文件,此时只需要保证该文件存在于工程目录中,然后手动拖入到相应的 group 中。如果想要从工程中删除某个源文件,则可以右键点击该文件,在下拉菜单中选择删除即可,如下图所示:
+
+
+
+即可在工程中看到 README.md 文件:
+
+
+
+如果想要创建一个虚拟文件夹 (虚拟文件夹并不是真实存在的文件夹,并没有实际的逻辑结构,其显示的内容可能实际上是分散于若干个真实的文件夹中,虚拟文件夹只是起到了一个归纳和汇总的作用),可以采用如下方式:
+
+
+
+
+
+即可在工程中看到新创建的文件夹:
+
+
+
+#### 导入错误说明
+
+本小节将介绍在导入过程中可能出现的错误,以及遇到这类问题该如何解决。由于在导入的过程中要根据用户所导入的工程进行芯片检查以及一些文件的替换,有时会遇到芯片不支持或者文件找不到的情况,此时用户可以自行手动替换某些缺少的文件,使得工程可以构建成功。
+
+常见的错误提示信息如下:
+
+- **ERROR STM32MP157AAAx doesn't support import to RT-Thread Studio now**
+
+ 说明 RT-Thread Studio 暂不支持导入当前系列的芯片。
+
+- **ERROR get IAR version failed. Please update the IAR installation path in rtconfig.py!**
+
+ 说明找不到 IAR 的安装路径 打开工程目录下 rtconfig.py 文件 , 修改 IAR 的 **EXEC_PATH** 。
+
+ ```c
+ elif CROSS_TOOL == 'iar':
+ PLATFORM = 'iar'
+ EXEC_PATH = r'C:/Program Files (x86)/IAR Systems/Embedded Workbench 8.0'
+ ```
+
+- **WARNING Can't auto specific link.lds file, Please specific a linker file mannually.**
+
+ 出现这种错误意味着在导入过程中不能自动替换链接脚本文件,需要手动指定链接脚本的位置。
+
+ 
+
+- **WARNING Can't find xxx_startup_stm32fxxx.s, replace startup files failed.**
+
+ 出现这种错误意味着在导入过程中不能找到可自动替换的芯片启动文件,需要手动添加启动文件到工程中。此时直接将可用的 gcc 启动文件拷贝到工程中,然后拖到相应的 group 中即可。
+
+
+## RT-Thread 配置
+
+### 打开 RT-Thread 配置界面
+
+通过双击工程根目录下的 `RT-Thread Settings` 文件,可以打开 RT-Thread 配置界面,如下图所示:
+
+
+
+### 软件包中心
+
+通过点击 ` 立即查看 ` 进入软件包中心,软件包中心首先展示了软件包的大分类,在软件包中心,可以先选择一个分类,也可以直接搜索软件包,点击搜索到的软件包进入软件包详情页面后,可以通过点击 ` 添加软件包到工程 ` 按钮将软件包添加到工程,如下图所示:
+
+
+
+当软件包成功添加到工程后,软件包中心会提示 ` 软件包添加成功 `,同时添加的软件包会显示在软件包层,该软件包依赖的组件也会被自动启用,例如添加 `pahomqtt` 软件包,`DFS`,`SAL`,`POSIX`,`libc` 组件会自动被启用,如下图所示:
+
+
+
+### 组件和服务层
+
+在图标上双击可直接启用该组件。启用的组件是亮色图标,未启用组件为灰色图标。通过在组件和服务层的图标上右键弹出可操作的右键菜单,如果该组件已经启用,则该组件的右键菜单有 ` 停用 `、` 查看依赖 `、` 详情配置 ` 三个选项,如果该组件未启用,则该组件右键菜单只有 ` 启用 ` 选项,如下图所示:
+
+
+
+### 查看依赖
+
+在启用的组件上右键选择`查看依赖`,可以查看该组件被哪些组件依赖,例如查看`pin`组件的依赖,`依赖关系图`窗口显示`pin`组件依赖了`gpio`,如下图所示:
+
+
+
+### 查看详细配置
+
+在启用的组件上右键选择 ` 详细配置 `,可以打开该组件的详细配置树形界面,例如在 `DFS` 上右键选择 ` 详细配置 `,打开的属性配置界面如下图所示:
+
+
+
+#### 详细配置
+
+当打开 RT-Thread 配置界面的时候,详细配置默认是隐藏的,通过启用的组件的右键菜单 ` 详细配置 ` 或者 RT-Thread 配置界面侧栏按钮,可以将详细配置界面调出来,侧栏按钮位置如下图所示:
+
+
+
+详细配置界面即右侧的树形配置界面,树形配置界面分成了四大类配置: `内核`, `组件`, `软件包`, `硬件` 。通过标签可以切换不同的配置类别,点击侧栏按钮可以隐藏该属性配置界面,如下图所示:
+
+
+
+#### 搜索配置
+
+当需要搜索某个配置的时候,需要在详细配置里选中任意配置树节点右键,会弹出 ` 搜索 ` 菜单,或者在详细配置里选中任意配置树节点后,按下快捷键 `Ctrl + F` 即可弹出配置搜索对话框,输入搜索关键词点击搜索即可搜索出所有匹配关键词的配置,在结果列表里选择不同结果查看时,配置树会自动跳转到对应配置位置,如下图所示:
+
+
+
+#### 保存配置
+
+当配置修改后,`RT-Thread Configuration` 标签会有脏标记,配置完后要记得点击 ` 保存 ` 按钮,将配置保存并应用到工程中。保存的时候会弹出进度提示框,提示保存进度,如下图所示:
+
+
+
+## 代码编辑
+
+### 编码修改
+
+- **设置当前文件的编码格式**
+
+ 在当前文件中,按`Alt+Enter`,会出现下图所示界面。可以看到设置编码格式的选项(如图中红色矩形所示)。下拉列表中可以选择想要的编码格式。
+
+
+
+- **设置当前项目工程(Project)的编码格式**
+
+ 选中你所创建的项目,右键点击会弹出以下界面,选择最下面的一个选项`属性`(图中红色矩形样式),点击进入。
+
+
+
+点击`属性`之后,弹出以下页面,默认的是`从容器继承`,我们选择`其他`, 下拉框选择编码,然后点击`Apply and Close`即可
+
+
+
+- **设置工作区间的编码格式**
+
+ 通过`窗口`菜单打开`首选项`窗口弹出如下图界面:
+
+
+
+ 点击`常规`选项后再点击`工作空间`会出现设置编码格式的选项。默认的是`缺省值(GBK)`,这里我们选择`其他`,下拉框选择编码,然后点击`Apply and Close`即可
+
+
+
+### 编辑
+
+通过 ` 编辑 ` 菜单或者直接在源码编辑器内右键菜单,可以选择对应的编辑系列功能,如下图所示:
+
+
+
+### 源码
+
+通过 ` 源码 ` 菜单或者直接在源码编辑器内右键菜单,可以选择对应的源码系列功能,如下图所示:
+
+
+
+### 重构
+
+通过 ` 重构 ` 菜单或者直接在源 码编辑器内右键菜单,可以选择对应的重构系列功能,如下图所示:
+
+
+
+### 导航
+
+通过 ` 导航 ` 菜单或者直接在源码编辑器内右键菜单,可以选择对应的导航系列功能,如下图所示:
+
+
+
+### 搜索
+
+通过搜索菜单或者搜索按钮,选择对应的搜索功能,如下图所示:
+
+
+
+### 辅助键
+
+通过 ` 帮助 ` 菜单的子菜单 ` 辅助键 ` 查看所有的快捷键,如下图所示:
+
+
+
+## 构建配置
+
+### 构建配置入口
+
+构建项目之前如果需要对项目进行构建参数配置,点击工具栏上 ` 打开构建配置 ` 按钮对项目进行构建参数配置,如下图所示:
+
+
+
+### 配置头文件包含
+
+若要增删改头文件路径,在 ` 工具设置 ` 配置页,点击 `GNU ARM Cross C Compiler` 下的 `Includes` 配置项即可打开头文件路径配置参数,点击 `Inlucde paths(-I)` 配置栏相应的按钮即可进行头文件的增删改操作,如下图所示:
+
+
+
+### 配置宏定义
+
+若要增删改宏定义,在 ` 工具设置 ` 配置页,点击 `GNU ARM Cross C Compiler` 下的 `Preprocessor` 配置项即可打开宏定义配置参数,点击 `Define symbols(-D)` 配置栏相应的按钮即可进行宏定义的增删改操作,如下图所示:
+
+
+
+### 配置链接脚本
+
+若要增删改链接脚本配置,在 ` 工具设置 ` 配置页,点击 `Cross ARM C Linker` 下的 `General` 配置项即可设置链接脚本文件,点击 `Script files(-T)` 配置栏相应的按钮即可进行链接脚本的增删改操作,在 `Script files(-T)` 下方有一些基本的链接参数可配置,如下图所示:
+
+
+
+### 配置外部二进制库文件
+
+若要增删改外部二进制库文件,在 ` 工具设置 ` 配置页,点击 `Cross ARM C Linker` 下的 `Libraries` 配置项即可设置外部二进制库文件,点击 `Libraries(-l)` 配置栏相应的按钮即可进行库文件的增删改操作,在 `Library search path(-L)` 配置栏配置库文件相应的路径。
+
+如下图所示:
+
+- 项目本地新增 GCC 二进制库文件,命名为:libxxx.a(如图中示例 libwifi_1.0.0_gcc.a)。
+- 在 `Libraries(-l)` 配置栏增加二进制库文件名称:xxx(如图中示例 wifi_1.0.0_gcc),注意需要去掉前缀 `lib` 与后缀 `.a` 。
+- 在 `Library search path(-L)` 配置栏,添加该库文件所在的路径。
+
+
+
+
+
+### 配置其它
+
+配置其它构建参数可直接在 ` 工具设置 ` 配置页面中选择相应类型配置树节点,并设置其提供的详细配置项,配置完成后,点击 ` 应用并关闭 ` 按钮配置即可生效。如下图所示:
+
+
+
+## 调试配置
+
+### 调试配置入口
+
+进行下载或启动调试之前如果需要对项目进行相关调试参数配置,通过点击工具栏上的 ` 调试配置 ` 按钮,可打开调试配置对话框界面,如下图所示:
+
+
+
+### 调试配置项
+
+选中一个调试配置后,调试配置对话框将展示所有配置项,配置项通过 ` 配置项分类标签页 ` 进行了分类,通过点击不同 ` 标签页 ` 展示不同类别配置项,修改配置项后,点击 ` 确定 ` 按钮即可保存配置修改,如下图所示:
+
+
+
+## 下载功能
+
+### 切换调试器
+
+目前 RT-Thread Studio 支持 JLink 、ST-Link、DAP-Link 以及软件仿真器 QEMU,新建工程的时候可以在新建工程向导里选择硬件调试器也可以选择软件仿真器 QEMU。工程创建好之后,如果想切换硬件调试器或直接进行软件仿真,可以通过工具栏下载程序按钮旁边的三角下拉按钮来切换硬件调试器或软件仿真器 QEMU,如下图所示:
+
+
+
+
+## 调试
+
+### 调试常用操作
+
+当调试启动成功后,程序会在 main 方法处挂起,这时可以通过工具栏上的调试相关操作按钮或者快捷键进行常用的调试操作,如下图所示:
+
+
+
+### 启用汇编单步调试模式
+
+点击工具栏上的 ` 汇编单步模式 ` 按钮,会自动打开 ` 反汇编 ` 功能窗口,此时 ` 汇编单步模式 ` 按钮呈凹下去的状态,代表此时处于汇编单步模式,如下图所示:
+
+
+
+当进入 ` 汇编单步模式 ` 后,所有单步调试操作将变为以一条汇编指令为单位进行单步执行,此时指令跳转情况可以在 ` 反汇编 ` 窗口进行查看。
+
+若要退出 ` 汇编单步模式 `,直接再次点击 ` 汇编单步模式 ` 按钮即可。
+
+### 查看寄存器
+
+通过 ` 窗口 ` 菜单的 ` 显示视图 ` 子菜单,选择打开 ` 寄存器 ` 窗口即可查看核心寄存器,如下图所示:
+
+
+
+### 查看外设寄存器
+
+点击 `Peripherals` 窗口,让 `Peripherals` 窗口显示在最前面,若 RT-Thread Studio 存在相应的 svd 文件,该窗口将会显示所有外设名称及其地址和描述。可在 `Peripherals` 窗口勾选要查看的外设,内存窗口将会显示该外设的所有寄存器的名称及其地址和当前值,如下图所示:
+
+
+
+若需要修改某个寄存器的当前值,可以直接点击进入寄存器 `Value` 那一列,输入想要修改的值后,敲击回车键即可执行修改。(注:只有可读可写的寄存器可以修改值,只读寄存器无法修改值。)
+
+
+
+### 查看变量
+
+点击 ` 变量 ` 窗口,让 ` 变量 ` 窗口显示在最前面,即可查看当前程序挂起时所有可见的变量,点击 ` 变量 ` 窗口最右边的三角下拉菜单,可以设置变量显示的数值格式,如下图所示:
+
+
+
+### 查看内存
+
+点击 ` 内存 ` 窗口,让 ` 内存 ` 窗口显示在最前面。点击 ` 添加内存监视器 ` 按钮,在弹出的输入框内,输入要查看内存的起始地址,点击 ` 确定 ` 即可添加要查看的内存,如下图所示:
+
+
+
+添加内存监视器后,内存窗口会立即展示刚输入的内存起始地址的一段内存,如下图所示:
+
+
+
+### 断点
+
+在源码编辑窗口边栏,双击即可设置断点,再次双击即可删除断点,打开 ` 断点 ` 窗口即可查看和管理所有断点,通过 ` 断点 ` 窗口工具栏可以进行删除,取消等断点管理操作,如下图所示:
+
+
+
+### 表达式
+
+在源码内选中表达式后点击右键,选择 ` 添加监看表达式 ` 即可将表达式添加到 ` 表达式 ` 窗口,或者直接点击 ` 表达式 ` 窗口内的 ` 添加新的表达式 ` 通过直接输入的方式,添加想要查看的表达式的值。
+
+
+
+## 模拟器仿真
+
+### QEMU 模拟器仿真
+
+QEMU 是一个支持跨平台虚拟化的虚拟机,它可以虚拟很多开发板。为了方便在没有开发板的情况下体验 RT-Thread,RT-Thread Studio 提供了 QEMU 模拟仿真调试器。 本文主要介绍在 Windows 平台上使用 RT-Thread Studio QEMU 模拟器进行仿真。
+
+#### 创建工程
+
+点击新建一个工程,选择或者设置各个配置选项,Adapter 配置项可以先选择 QEMU,并且配置适合的模拟器。目前的最新版本中,系列、子系列需要手动选择,还未与模拟器支持的系列关联起来。点击完成,即会在工作空间中创建一个 QEMU 调式的工程。
+
+
+
+#### 切换调试器到 QEMU
+
+如果当前工程为老工程或者当前选择的调试器是 QEMU 以外的调试器,想使用 QEMU 进行调试,可点击下载按钮右边的下拉框,选择 QEMU :
+
+
+
+若当前工程还未配置 QEMU ,那么在选择 QEMU 后,会弹出【跳转到配置界面】的提示,点击【是】,会显示 QEMU 配置界面,具体详情将在下一节介绍。
+
+
+
+#### QEMU 配置
+
+点击【打开调试配置】或者其他情况下跳转到 QEMU 配置界面,需要配置的参数如下:
+
+| 参数 | 英文 | 命令 |
+| :-----------------------: | :------------------------: | :-------: |
+| 开发板型号 | Board Name | -M |
+| 支持 Cpu 的数量 | Cpu Quantity | -smp |
+| 是否支持网络 | Enable Network | -net |
+| 是否支持图形 | Don't open grapgic windows | -nographic |
+| SD 卡内存大小 | SD Card Memory | -sd |
+| 额外的命令 | Extra Commands | 无 |
+
+填写完整后,点击确定,即可正常进行调试和运行。
+
+
+
+
+
+#### 仿真调试
+
+在编译正常的情况下,点击调式按钮,IDE 会自动启动 QEMU 并打开串口,并进入断点调试模式,在此模式中,可一步一步观察每次断点的输出情况,亦可在串口中执行自己的命令。
+
+- ** 注:在 windows 上 QEMU 不支持上下键查看历史命令。**
+
+
+
+#### 网络仿真
+
+- 基于开发板创建一个 stm32f4 的工程
+
+
+
+- 打开 RT-Thread Settings ,使能 SAL, 切换到 【硬件】栏,使能以太网,Ctrl + S 保存配置,编译
+
+ 
+
+
+
+- 配置 QEMU,选择模拟器,配置网卡(配置TAP 过程可参考 https://www.rt-thread.org/document/site/tutorial/qemu-network/qemu_setup/qemu_setup/)
+
+ 
+
+- 点击下载按钮,程序会自动启动并下载,进入到终端页面,显示 lwip initialized.
+
+ 
+
+- 输入网络命令测试一下
+
+
+
+## PlatformIO
+
+RT-Thread Studio 自 V2.0.0 版本开始支持 PlatformIO 工程的创建、编译和调试。下面,我们一步步的来实践这个过程
+
+- 在 SDK Manager 下载 PlatformIO 资源包,此下载安装过程可能需要花费一定时间,请耐心等待
+
+ 
+
+- 新建 RT-Thread 工程,选择基于 PlatformIO,选择您需要的开发板、框架,点击 **Finish**,自动创建了一个 PlatformIO 的工程
+
+ 
+
+- 打开 src/main.cpp, 添加您自己的代码,然后点击编译按钮编译工程
+
+ 
+
+- 编译成功后,点击下载按钮,下载程序到开发板
+
+ 
+
+- 下载成功后,打开串口查看输出内容
+
+ 
+
+- 调试。点击调试按钮,自动进入调试界面,您可以查看寄存器信息,进行单步调试操作
+
+ 
+
+ 
+
+## 终端
+
+通过点击工具栏 ` 终端 ` 按钮即可打开终端选择界面如下图所示:
+
+
+
+点击 ` 确定 ` 按钮后,即会自动打开对应的终端功能窗口,如下图所示:
+
+
+
+
+## SDK Manager
+
+### SDK Manager 简介
+
+通过 SDK Manager 维护 RT-Thread Studio 内部的 RT-Thread SDK 资源包,包括安装,卸载,升级各类资源包。通过工具栏的 `SDK Manager` 按钮即可打开 SDK Manager 功能界面,如下图所示:
+
+
+
+### SDK Manager 功能
+
+#### 安装资源包
+
+勾选状态为 `Not installed` 的资源包,点击 `Install packages` 按钮即可启动资源包安装过程,如下图所示:
+
+
+
+#### 卸载资源包
+
+勾选状态为 `Installed` 的资源包,点击 `Delete packages` 按钮即可启动卸载过程,如下图所示:
+
+
+
+#### 升级资源包
+
+点击资源包对应的 ` 升级 ` 按钮,可以将资源包同步更新到最新状态。如下图所示:
+
+
+
+## MarkDown 编辑
+
+RT-Thread Studio 自带 MarkDown 编辑器,若工程里有 md 文件,只需要双击即可打开 md 文件进行编辑,如下图所示:
+
+
+
+
+## 常用快捷键
+
+**代码阅读**
+
+- Ctrl+H 全局 打开搜索对话框
+- Ctrl+Shift+T 全局 打开类型
+- Ctrl+Shift+R 全局 打开资源
+- Ctrl+Shift+"+" 放大字体
+- Ctrl+"-" 缩小字体
+
+**代码编辑**
+
+- Ctrl+D 删除当前行
+
+- Ctrl+/ 注释当前行, 再按则取消注释
+
+- Ctrl+Shift+F 格式化
+
+- Alt+→ /← 全局 前进 / 后退历史记录
+
+- Ctrl+Q 定位到最后编辑的地方
+
+- Ctrl+K 参照选中的 Word 快速定位到下一个
+
+- Ctrl+L 定位在某行
+
+- Alt+→ /← 前一个 / 下一个编辑的页面
+
+- Shift+Enter 在当前行的下一行插入空行
+
+
+## 首选项
+
+### 首选项介绍
+
+RT-Thread Studio 基于 eclipse 平台开发,eclipse 是一个高度可定制的平台,基于 eclipse 实现的功能都会提供大量的配置项,来定制功能的行为方式,来满足用户自身的使用习惯,这些配置项 eclipse 称为首选项。
+
+通过点击 ` 窗口 ` 菜单的字菜单 ` 首选项 ` 即可进入首选项配置界面,如下图所示:
+
+
+
+由于首选项数量大,种类多,首选项对话框左侧以树形的形式展示所有的首选项,点击对应的首选项类别即可展开对应的首选项配置树,若要查找某个首选项,可直接在 ` 输入过滤文本 ` 框中,输入关键字,进行查找。
+
+首选项右侧即为具体的可配置项页面,修改 后直接点击 ` 应用 ` 即可保存配置,若想恢复当前配置页默认值,点击 ` 恢复默认值 ` 按钮即可。
+
+首选项配置还可以通过左下角 ` 导入 `,` 导出 ` 功能,将配置进行保存或者在不同用户间传递或者共享配置,导出功能将把所有配置导出到 `.epf` 文件中,其它用户直接导入这个 `.epf` 文件,即可使用该文件记录的所有配置。
+
+
+
+### 设置主题
+
+通过 ` 首选项 ` 的 ` 外观 ` 配置项选择 “DevStyle Theme” 即可启用新的黑色主题,切换主题后需要重启 Studio 后才会生效
+
+
+
+通过 `DevStyle Theme` 的 `Color Themes` 配置项选择 “Editor theme” 可以切换其他黑色主题,切换主题后需要重启 Studio 后才会生效
+
+
+
+
+
diff --git a/development-tools/scons/figures/1149b400a917cdd7906affecf08503f6.png b/development-tools/scons/figures/1149b400a917cdd7906affecf08503f6.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1238809314a1fa21edd5c75c0837ccf04625b1b
Binary files /dev/null and b/development-tools/scons/figures/1149b400a917cdd7906affecf08503f6.png differ
diff --git a/development-tools/scons/figures/b1e0b39d090bfc4640958ffdb11d9741.png b/development-tools/scons/figures/b1e0b39d090bfc4640958ffdb11d9741.png
new file mode 100644
index 0000000000000000000000000000000000000000..1d533c22ff41fffadaf4f8cf54998aa0cd47100d
Binary files /dev/null and b/development-tools/scons/figures/b1e0b39d090bfc4640958ffdb11d9741.png differ
diff --git a/development-tools/scons/figures/caef0674c4a148411789c42f8f8b70b7.png b/development-tools/scons/figures/caef0674c4a148411789c42f8f8b70b7.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a5e50fb07b24bd374a0aa5ac1d2a9c390bb8df5
Binary files /dev/null and b/development-tools/scons/figures/caef0674c4a148411789c42f8f8b70b7.png differ
diff --git a/development-tools/scons/figures/cb02d48b93098e73681818fd62f22f8b.png b/development-tools/scons/figures/cb02d48b93098e73681818fd62f22f8b.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3a0ba2726b7c9adb8df3434b96d49009ac6efc2
Binary files /dev/null and b/development-tools/scons/figures/cb02d48b93098e73681818fd62f22f8b.png differ
diff --git a/development-tools/scons/figures/f42ee8cdfd03c1782679c759f70f072f.png b/development-tools/scons/figures/f42ee8cdfd03c1782679c759f70f072f.png
new file mode 100644
index 0000000000000000000000000000000000000000..0842ada7755f50ec11bb0b9aee5455a71405124d
Binary files /dev/null and b/development-tools/scons/figures/f42ee8cdfd03c1782679c759f70f072f.png differ
diff --git a/development-tools/scons/figures/f96388acd1264b4ffcd391c3fd399a50.png b/development-tools/scons/figures/f96388acd1264b4ffcd391c3fd399a50.png
new file mode 100644
index 0000000000000000000000000000000000000000..40f592af35bc80eca619aec7c2f714319b813dff
Binary files /dev/null and b/development-tools/scons/figures/f96388acd1264b4ffcd391c3fd399a50.png differ
diff --git a/development-tools/scons/scons.md b/development-tools/scons/scons.md
new file mode 100644
index 0000000000000000000000000000000000000000..78ac27d8a2b7a548a0377e1ba2a31843e30208f0
--- /dev/null
+++ b/development-tools/scons/scons.md
@@ -0,0 +1,610 @@
+# SCons 构建工具
+
+## SCons 简介
+
+SCons 是一套由 Python 语言编写的开源构建系统,类似于 GNU Make。它采用不同于通常 Makefile 文件的方式,而是使用 SConstruct 和 SConscript 文件来替代。这些文件也是 Python 脚本,能够使用标准的 Python 语法来编写。所以在 SConstruct、SConscript 文件中可以调用 Python 标准库进行各类复杂的处理,而不局限于 Makefile 设定的规则。
+
+在 [SCons](http://www.scons.org/doc/production/HTML/scons-user/index.html) 的网站上可以找到详细的 SCons 用户手册,本章节讲述 SCons 的基本用法,以及如何在 RT-Thread 中用好 SCons 工具。
+
+### 什么是构建工具
+
+构建工具 (software construction tool) 是一种软件,它可以根据一定的规则或指令,将源代码编译成可执行的二进制程序。这是构建工具最基本也是最重要的功能。实际上构建工具的功能不止于此,通常这些规则有一定的语法,并组织成文件。这些文件用来控制构建工具的行为,在完成软件构建之外,也可以做其他事情。
+
+目前最流行的构建工具是 GNU Make。很多知名开源软件,如 Linux 内核就采用 Make 构建。Make 通过读取 Makefile 文件来检测文件的组织结构和依赖关系,并完成 Makefile 中所指定的命令。
+
+由于历史原因,Makefile 的语法比较混乱,不利于初学者学习。此外在 Windows 平台上使用 Make 也不方便,需要安装 Cygwin 环境。为了克服 Make 的种种缺点,人们开发了其他构建工具,如 CMake 和 SCons 等。
+
+### RT-Thread 构建工具
+
+RT-Thread 早期使用 Make/Makefile 构建。从 0.3.x 开始,RT-Thread 开发团队逐渐引入了 SCons 构建系统,引入 SCons 唯一的目是:使大家从复杂的 Makefile 配置、IDE 配置中脱离出来,把精力集中在 RT-Thread 功能开发上。
+
+有些读者可能会有些疑惑,这里介绍的构建工具与 IDE 有什么不同呢?IDE 通过图形化界面的操作来完成构建。大部分 IDE 会根据用户所添加的源码生成类似 Makefile 或 SConscript 的脚本文件,在底层调用类似 Make 或 SCons 的工具来构建源码。
+
+### 安装 SCons
+
+在使用 SCons 系统前需要在 PC 主机中安装它,因为它是 Python 语言编写的,所以在使用 SCons 之前需要安装 Python 运行环境。
+
+RT-Thread 提供的 Env 配置工具带有 SCons 和 Python,因此在 windows 平台使用 SCons 则不需要安装这两个软件。
+
+在 Linux、BSD 环境中 Python 应该已经默认安装了,一般也是 2.x 版本系列的 Python 环境。这时只需要安装 SCons 即可,例如在 Ubuntu 中可以使用如下命令安装 SCons:
+
+`sudo apt-get install scons`
+
+## SCons 基本功能
+
+RT-Thread 构建系统支持多种编译器。目前支持的编译器包括 ARM GCC、MDK、IAR、VisualStudio、Visual DSP。主流的 ARM Cortex M0、M3、M4 平台,基本上 ARM GCC、MDK、IAR 都是支持的。有一些 BSP 可能仅支持一种,读者可以阅读该 BSP 目录下的 rtconfig.py 里的 CROSS_TOOL 选项查看当前支持的编译器。
+
+如果是 ARM 平台的芯片,则可以使用 Env 工具,输入 scons 命令直接编译 BSP,这时候默认使用的是 ARM GCC 编译器,因为 Env 工具带有 ARM GCC 编译器。 如下图所示使用 `scons` 命令编译 stm32f10x-HAL BSP,后文讲解 SCons 也将基于这个 BSP。
+
+
+
+如果用户要使用其他的 BSP 已经支持的编译器编译工程,或者 BSP 为非 ARM 平台的芯片,那么不能直接使用 scons 命令编译工程,需要自己安装对应的编译器,并且指定使用的编译器路径。在编译工程前,可以在 Env 命令行界面使用下面的 2 个命令指定编译器为 MDK 和编译器路径为 MDK 的安装路径。
+
+```c
+set RTT_CC=keil
+set RTT_EXEC_PATH=C:/Keilv5
+```
+
+### SCons 基本命令
+
+本节介绍 RT-Thread 中常用的 SCons 命令。SCons 不仅完成基本的编译,还可以生成 MDK/IAR/VS 工程。
+
+#### scons
+
+在 Env 命令行窗口进入要编译的 BSP 工程目录,然后使用此命令可以直接编译工程。如果执行过 `scons` 命令后修改了一些源文件,再次执行 scons 命令时,则 SCons 会进行增量编译,仅编译修改过的源文件并链接。
+
+如果在 Windows 上执行 `scons` 输出以下的警告信息:
+
+```c
+scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly.
+```
+
+说明 scons 并没在你的机器上找到 Visual Studio 编译器,但实际上我们主要是针对设备开发,和 Windows 本地没什么关系,请直接忽略掉它。
+
+`scons` 命令后面还可以增加一个 - s 参数,即命令 `scons -s`,和 scons 命令不同的是此命令不会打印具体的内部命令。
+
+#### scons -c
+
+清除编译目标。这个命令会清除执行 scons 时生成的临时文件和目标文件。
+
+#### scons --target=XXX
+
+如果使用 mdk/iar 来进行项目开发,当修改了 rtconfig.h 打开或者关闭某些组件时,需要使用以下命令中的其中一种重新生成对应的定制化的工程,然后在 mdk/iar 进行编译下载。
+
+```c
+scons --target=iar
+scons --target=mdk4
+scons --target=mdk5
+```
+
+在命令行窗口进入要编译的 BSP 工程目录,使用 `scons --target=mdk5` 命令后会在 BSP 目录生成一个新的 MDK 工程文件名为 project.uvprojx。双击它打开,就可以使用 MDK 来编译、调试。使用 `scons --target=iar` 命令后则会生成一个新的 IAR 工程文件名为 project.eww。不习惯 SCons 的用户可以使用这种方式。如果打开 project.uvproj 失败,请删除 project.uvopt 后,重新生成工程。
+
+在 bsp/simulator 下,可以使用下面的命令生成 vs2012 的工程或 vs2005 的工程。
+
+```c
+scons --target=vs2012
+Scons --target=vs2005
+```
+
+如果 BSP 目录下提供其他 IDE 工程的模板文件也可以使用此命令生成对应的新工程,比如 ua、vs、cb、cdk。
+
+这个命令后面同样可以增加一个 -s 参数,如命令 `scons –target=mdk5 -s`,执行此命令时不会打印具体的内部命令。
+
+> [!NOTE]
+> 注:要生成 MDK 或者 IAR 的工程文件,前提条件是 BSP 目录存在一个工程模版文件,然后 scons 才会根据这份模版文件加入相关的源码,头文件搜索路径,编译参数,链接参数等。而至于这个工程是针对哪颗芯片的,则直接由这份工程模版文件指定。所以大多数情况下,这个模版文件是一份空的工程文件,用于辅助 SCons 生成 project.uvprojx 或者 project.eww。
+
+#### scons -jN
+
+多线程编译目标,在多核计算机上可以使用此命令加快编译速度。一般来说一颗 cpu 核心可以支持 2 个线程。双核机器上使用 `scons -j4` 命令即可。
+
+> [!NOTE]
+> 注:如果你只是想看看编译错误或警告,最好是不使用 - j 参数,这样错误信息不会因为多个文件并行编译而导致出错信息夹杂在一起。
+
+#### scons --dist
+
+搭建项目框架,使用此命令会在 BSP 目录下生成 dist 目录,这便是开发项目的目录结构,包含了RT-Thread源码及BSP相关工程,不相关的BSP文件夹及libcpu都会被移除,并且可以随意拷贝此工程到任何目录下使用。
+
+#### scons --verbose
+
+默认情况下,使用 scons 命令编译的输出不会显示编译参数,如下所示:
+
+```c
+D:\repository\rt-thread\bsp\stm32f10x>scons
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+scons: building associated VariantDir targets: build
+CC build\applications\application.o
+CC build\applications\startup.o
+CC build\components\drivers\serial\serial.o
+...
+```
+
+使用 scons –verbose 命令的效果如下:
+
+```c
+armcc -o build\src\mempool.o -c --device DARMSTM --apcs=interwork -ID:/Keil/ARM/
+RV31/INC -g -O0 -DUSE_STDPERIPH_DRIVER -DSTM32F10X_HD -Iapplications -IF:\Projec
+t\git\rt-thread\applications -I. -IF:\Project\git\rt-thread -Idrivers -IF:\Proje
+ct\git\rt-thread\drivers -ILibraries\STM32F10x_StdPeriph_Driver\inc -IF:\Project
+\git\rt-thread\Libraries\STM32F10x_StdPeriph_Driver\inc -ILibraries\STM32_USB-FS
+-Device_Driver\inc -IF:\Project\git\rt-thread\Libraries\STM32_USB-FS-Device_Driv
+er\inc -ILibraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x -IF:\Project\git\rt-thre
+...
+```
+
+## SCons 进阶
+
+SCons 使用 SConscript 和 SConstruct 文件来组织源码结构,通常来说一个项目只有一 SConstruct,但是会有多个 SConscript。一般情况下,每个存放有源代码的子目录下都会放置一个 SConscript。
+
+为了使 RT-Thread 更好的支持多种编译器,以及方便的调整编译参数,RT-Thread 为每个 BSP 单独创建了一个名为 rtconfig.py 的文件。因此每一个 RT-Thread BSP 目录下都会存在下面三个文件:rtconfig.py、SConstruct 和 SConscript,它们控制 BSP 的编译。一个 BSP 中只有一个 SConstruct 文件,但是却会有多个 SConscript 文件,可以说 SConscript 文件是组织源码的主力军。
+
+RT-Thread 大部分源码文件夹下也存在 SConscript 文件,这些文件会被 BSP 目录下的 SConscript 文件 “找到” 从而将 rtconfig.h 中定义的宏对应的源代码加入到编译器中来。后文将以 stm32f10x-HAL BSP 为例,讲解 SCons 是如何构建工程。
+
+### SCons 内置函数
+
+如果想要将自己的一些源代码加入到 SCons 编译环境中,一般可以创建或修改已有 SConscript 文件。SConscript 文件可以控制源码文件的加入,并且可以指定文件的 Group(与 MDK/IAR 等 IDE 中的 Group 的概念类似)。
+
+SCons 提供了很多内置函数可以帮助我们快速添加源码程序,利用这些函数,再配合一些简单的 Python 语句我们就能随心所欲向项目中添加或者删除源码。下面将简单介绍一些常用函数。
+
+#### GetCurrentDir()
+
+获取当前路径。
+
+#### Glob('\*.c')
+
+获取当前目录下的所有 C 文件。修改参数的值为其他后缀就可以匹配当前目录下的所有某类型的文件。
+
+#### GetDepend(macro)
+
+该函数定义在 tools 目录下的脚本文件中,它会从 rtconfig.h 文件读取配置信息,其参数为 rtconfig.h 中的宏名。如果 rtconfig.h 打开了某个宏,则这个方法(函数)返回真,否则返回假。
+
+#### Split(str)
+
+将字符串 str 分割成一个列表 list。
+
+#### DefineGroup(name, src, depend,**parameters)
+
+这是 RT-Thread 基于 SCons 扩展的一个方法(函数)。DefineGroup 用于定义一个组件。组件可以是一个目录(下的文件或子目录),也是后续一些 IDE 工程文件中的一个 Group 或文件夹。
+
+`DefineGroup() ` 函数的参数描述:
+
+|**参数**|**描述** |
+|-------|------------------------------------|
+| name | Group 的名字 |
+| src | Group 中包含的文件,一般指的是 C/C++ 源文件。方便起见,也能够通过 Glob 函数采用通配符的方式列出 SConscript 文件所在目录中匹配的文件 |
+| depend | Group 编译时所依赖的选项(例如 FinSH 组件依赖于 RT_USING_FINSH 宏定义)。编译选项一般指 rtconfig.h 中定义的 RT_USING_xxx 宏。当在 rtconfig.h 配置文件中定义了相应宏时,那么这个 Group 才会被加入到编译环境中进行编译。如果依赖的宏并没在 rtconfig.h 中被定义,那么这个 Group 将不会被加入编译。相类似的,在使用 scons 生成为 IDE 工程文件时,如果依赖的宏未被定义,相应的 Group 也不会在工程文件中出现 |
+| parameters | 配置其他参数,可取值见下表,实际使用时不需要配置所有参数 |
+
+parameters 可加入的参数:
+
+|**参数**|**描述** |
+|------------|--------------------------------------------------|
+| CCFLAGS | C 源文件编译参数 |
+| CPPPATH | 头文件路径 |
+| CPPDEFINES | 链接时参数 |
+| LIBRARY | 包含此参数,则会将组件生成的目标文件打包成库文件 |
+
+#### SConscript(dirs,variant_dir,duplicate)
+
+读取新的 SConscript 文件,SConscript() 函数的参数描述如下所示:
+
+|**参数** |**描述** |
+|-------------|---------------------------------------|
+| dirs | SConscript 文件路径 |
+| variant_dir | 指定生成的目标文件的存放路径 |
+| duiplicate | 设定是否拷贝或链接源文件到 variant_dir |
+
+## SConscript 示例
+
+下面我们将以几个 SConscript 为例讲解 scons 构建工具的使用方法。
+
+### SConscript 示例 1
+
+我们先从 stm32f10x-HAL BSP 目录下的 SConcript 文件开始讲解,这个文件管理 BSP 下面的所有其他 SConscript 文件,内容如下所示。
+
+```c
+import os
+cwd = str(Dir('#'))
+objs = []
+list = os.listdir(cwd)
+for d in list:
+ path = os.path.join(cwd, d)
+ if os.path.isfile(os.path.join(path, 'SConscript')):
+ objs = objs + SConscript(os.path.join(d, 'SConscript'))
+Return('objs')
+```
+
+* `import os:` 导入 Python 系统编程 os 模块,可以调用 os 模块提供的函数用于处理文件和目录。
+
+* `cwd = str(Dir('#')):` 获取工程的顶级目录并赋值给字符串变量 cwd,也就是工程的 SConstruct 所在的目录,在这里它的效果与 `cwd = GetCurrentDir()` 相同。
+
+* `objs = []:` 定义了一个空的 list 型变量 objs。
+
+* `list = os.listdir(cwd):` 得到当前目录下的所有子目录,并保存到变量 list 中。
+
+* 随后是一个 python 的 for 循环,这个 for 循环会遍历一遍 BSP 的所有子目录并运行这些子目录的 SConscript 文件。具体操作是取出一个当前目录的子目录,利用 `os.path.join(cwd,d)` 拼接成一个完整路径,然后判断这个子目录是否存在一个名为 SConscript 的文件,若存在则执行 `objs = objs + SConscript(os.path.join(d,'SConscript'))。` 这一句中使用了 SCons 提供的一个内置函数 `SConscript()`,它可以读入一个新的 SConscript 文件,并将 SConscript 文件中所指明的源码加入到了源码编译列表 objs 中来。
+
+通过这个 SConscript 文件,BSP 工程所需要的源代码就被加入了编译列表中。
+
+### SConscript 示例 2
+
+那么 stm32f10x-HAL BSP 其他的 SConcript 文件又是怎样的呢?我们再看一下 drivers 目录下 SConcript 文件,这个文件将管理 drivers 目录下面的源代码。drivers 目录用于存放根据 RT-Thread 提供的驱动框架实现的底层驱动代码。
+
+```c
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+
+# add the general drivers.
+src = Split("""
+board.c
+stm32f1xx_it.c
+""")
+
+if GetDepend(['RT_USING_PIN']):
+ src += ['drv_gpio.c']
+if GetDepend(['RT_USING_SERIAL']):
+ src += ['drv_usart.c']
+if GetDepend(['RT_USING_SPI']):
+ src += ['drv_spi.c']
+if GetDepend(['RT_USING_USB_DEVICE']):
+ src += ['drv_usb.c']
+if GetDepend(['RT_USING_SDCARD']):
+ src += ['drv_sdcard.c']
+
+if rtconfig.CROSS_TOOL == 'gcc':
+ src += ['gcc_startup.s']
+
+CPPPATH = [cwd]
+
+group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
+
+```
+
+* `Import('rtconfig'):` 导入 rtconfig 对象,后面用到的 rtconfig.CROSS_TOOL 定义在这个 rtconfig 模块。
+
+* `from building import *:` 把 building 模块的所有内容全都导入到当前模块,后面用到的 DefineGroup 定义在这个模块。
+
+* `cwd = GetCurrentDir():` 获得当前路径并保存到字符串变量 cwd 中。
+
+后面一行使用 `Split()` 函数来将一个文件字符串分割成一个列表,其效果等价于
+
+`src = ['board.c','stm32f1xx_it.c']`
+
+后面使用了 if 判断和 `GetDepend()` 检查 rtconfig.h 中的某个宏是否打开,如果打开,则使用 `src += [src_name]` 来往列表变量 src 中追加源代码文件。
+
+* `CPPPATH = [cwd]:` 将当前路径保存到一个列表变量 CPPPATH 中。
+
+最后一行使用 DefineGroup 创建一个名为 Drivers 的组,这个组也就对应 MDK 或者 IAR 中的分组。这个组的源代码文件为 src 指定的文件,depend 为空表示该组不依赖任何 rtconfig.h 的宏。
+
+`CPPPATH =CPPPATH` 表示将当前路径添加到系统的头文件路径中。左边的 CPPPATH 是 DefineGroup 中内置参数,表示头文件路径。右边的 CPPPATH 是本文件上面一行定义的。这样我们就可以在其他源码中引用 drivers 目录下的头文件了。
+
+### SConscript 示例 3
+
+我们再看一下 applications 目录下的 SConcript 文件,这个文件将管理 applications 目录下面的源代码,用于存放用户自己的应用代码。
+
+```c
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd, str(Dir('#'))]
+
+group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
+```
+
+`src = Glob('*.c'):` 得到当前目录下所有的 C 文件。
+
+`CPPPATH = [cwd, str(Dir('#'))]:` 将当前路径和工程的 SConstruct 所在的路径保存到列表变量 CPPPATH 中。
+
+最后一行使用 DefineGroup 创建一个名为 Applications 的组。这个组的源代码文件为 src 指定的文件,depend 为空表示该组不依赖任何 rtconfig.h 的宏,并将 CPPPATH 保存的路径添加到了系统头文件搜索路径中。这样 applications 目录和 stm32f10x-HAL BSP 目录里面的头文件在源代码的其他地方就可以引用了。
+
+总结:这个源程序会将当前目录下的所有 c 程序加入到组 Applications 中,因此如果在这个目录下增加或者删除文件,就可以将文件加入工程或者从工程中删除。它适用于批量添加源码文件。
+
+### SConscript 示例 4
+
+下面是 RT-Thread 源代码 component/finsh/SConscript 文件的内容,这个文件将管理 finsh 目录下面的源代码。
+
+```c
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+src = Split('''
+shell.c
+symbol.c
+cmd.c
+''')
+
+fsh_src = Split('''
+finsh_compiler.c
+finsh_error.c
+finsh_heap.c
+finsh_init.c
+finsh_node.c
+finsh_ops.c
+finsh_parser.c
+finsh_var.c
+finsh_vm.c
+finsh_token.c
+''')
+
+msh_src = Split('''
+msh.c
+msh_cmd.c
+msh_file.c
+''')
+
+CPPPATH = [cwd]
+if rtconfig.CROSS_TOOL == 'keil':
+ LINKFLAGS = '--keep *.o(FSymTab)'
+
+ if not GetDepend('FINSH_USING_MSH_ONLY'):
+ LINKFLAGS = LINKFLAGS + '--keep *.o(VSymTab)'
+else:
+ LINKFLAGS = ''
+
+if GetDepend('FINSH_USING_MSH'):
+ src = src + msh_src
+if not GetDepend('FINSH_USING_MSH_ONLY'):
+ src = src + fsh_src
+
+group = DefineGroup('finsh', src, depend = ['RT_USING_FINSH'], CPPPATH = CPPPATH, LINKFLAGS = LINKFLAGS)
+
+Return('group')
+```
+
+我们来看一下文件中第一个 Python 条件判断语句的内容,如果编译工具是 keil,则变量 `LINKFLAGS = '--keep *.o(FSymTab)'` 否则置空。
+
+DefinGroup 同样将 finsh 目录下的 src 指定的文件创建为 finsh 组。`depend = ['RT_USING_FINSH']` 表示这个组依赖 rtconfig.h 中的宏 RT_USING_FINSH。当 rtconfig.h 中打开宏 RT_USING_FINSH 时,finsh 组内的源码才会被实际编译,否则 SCons 不会编译。
+
+然后将 finsh 目录加入到系统头文件目录中,这样我们就可以在其他源码中引用 finsh 目录下的头文件。
+
+`LINKFLAGS = LINKFLAGS` 的含义与 `CPPPATH = CPPPATH` 类似。左边的 LINKFLAGS 表示链接参数,右边的 LINKFLAGS 则是前面 if else 语句所定义的值。也就是给工程指定链接参数。
+
+## 使用 SCons 管理工程
+
+前面小节对 RT-Thread 源代码的相关 SConscript 做了详细讲解,大家也应该知道了 SConscript 文件的一些常见写法,本小节将指导大家如何使用 SCons 管理自己的工程。
+
+### 添加应用代码
+
+前文提到过 BSP 下的 Applications 文件夹用于存放用户自己的应用代码,目前只有一个 main.c 文件。如果用户的应用代码不是很多,建议相关源文件都放在这个文件夹下面。在 Applications 文件夹下新增了 2 个简单的文件 hello.c 和 hello.h,内容如下所示。
+
+```c
+/* file: hello.h */
+
+#ifndef _HELLO_H_
+#define _HELLO_H_
+
+int hello_world(void);
+
+#endif /* _HELLO_H_ */
+
+/* file: hello.c */
+#include
+#include
+#include
+
+int hello_world(void)
+{
+ rt_kprintf("Hello, world!\n");
+
+ return 0;
+}
+
+MSH_CMD_EXPORT(hello_world, Hello world!)
+```
+
+applications 目录下的 SConcript 文件会把当前目录下的所有源文件都添加到工程中。需要使用 `scons --target=xxx` 命令才会把新增的 2 个文件添加到工程项目中。注意每次新增文件都要重新生成工程。
+
+### 添加模块
+
+前文提到在自己源代码文件不多的情况下,建议所有源代码文件都放在 applications 文件夹里面。如果用户源代码很多了,并且想创建自己的工程模块,或者需要使用自己获取的其他模块,怎么做会比较合适呢?
+
+同样以上文提到的 hello.c 和 hello.h 为例,这两个文件将会放到一个单独的文件夹里管理,并且在 MDK 工程文件里有自己的分组,且可以通过 menuconfig 选择是否使用这个模块。在 BSP 下新增 hello 文件夹。
+
+
+
+大家注意到文件夹里多了一个 SConscript 文件,如果想要将自己的一些源代码加入到 SCons 编译环境中,一般可以创建或修改已有的 SConscript 文件。参考上文对 RT-Thread 源代码的一些对 SConscript 文件的分析,这个新增的 hello 模块 SConscript 文件内容如下所示:
+
+```c
+from building import *
+
+cwd = GetCurrentDir()
+include_path = [cwd]
+src = []
+
+if GetDepend(['RT_USING_HELLO']):
+ src += ['hello.c']
+
+group = DefineGroup('hello', src, depend = [''], CPPPATH = include_path)
+
+Return('group')
+```
+
+通过上面几行简单的代码,就创建了一个新组 hello,并且可以通过宏定义控制要加入到组里面的源文件,还将这个组所在的目录添加到了系统头文件路径中。那么自定义宏 RT_USING_HELLO 又是通过怎样的方式定义呢?这里要介绍一个新的文件 Kconfig。Kconfig 用来配置内核,使用 Env 配置系统时使用的 menuconfig 命令生成的配置界面就依赖 Kconfig 文件。menuconfig 命令通过读取工程的各个 Kconfig 文件,生成配置界面供用户配置内核,最后所有配置相关的宏定义都会自动保存到 BSP 目录里的 rtconfig.h 文件中,每一个 BSP 都有一个 rtconfig.h 文件,也就是这个 BSP 的配置信息。
+
+在 stm32f10x-HAL BSP 目录下已经有了关于这个 BSP 的 Kconfig 文件,我们可以基于这个文件添加自己需要的配置选项。关于 hello 模块添加了如下配置选项,# 号后面为注释。
+
+
+
+使用 Env 工具进入 stm32f10x-HAL BSP 目录后,使用 menuconfig 命令在主页面最下面就可以看到新增的 hello 模块的配置菜单,进入菜单后如下图所示。
+
+
+
+还可以修改 hello value 的值。
+
+
+
+保存配置后退出配置界面,打开 stm32f10x-HAL BSP 目录下的 rtconfig.h 文件可以看到 hello 模块的配置信息已经有了。
+
+
+
+**注意:每次 menuconfig 配置完成后都要使用 scons --target=XXX 命令生成新工程。**
+
+因为 rtconfig.h 中已经定义了 RT_USING_HELLO 宏,所以新生成工程时就会把 hello.c 的源文件添加到新工程中。
+
+上面只是简单列举了在 Kconfig 文件中添加自己模块的配置选项,用户还可以参考[《Env 用户手册》](../env/env.md),里面也有对配置选项修改和添加的讲解,也可以自己百度查看 Kconfig 的相关文档,实现其他更复杂的配置选项。
+
+### 添加库
+
+如果要往工程中添加一个额外的库,需要注意不同的工具链对二进制库的命名。
+
+- ARMCC 工具链下的库名称应该是 xxx.lib,一个以 .lib 为后缀的文件。
+- IAR 工具链下的库名称应该是 xxx.a,一个以 .a 为后缀的文件。
+- GCC 工具链下的库名称应该是 libxxx.a,一个以 .a 为后缀的文件,并且有 lib 前缀。
+
+ARMCC / IAR 工具链下,若添加库名为 libabc.lib / libabc_iar.a 时,在指定库时指定全名 libabc。
+
+GCC 工具链比较特殊,它识别的是 libxxx.a 这样的库名称,若添加库名为 libabc.a 时,在指定库时是指定 abc,而不是 libabc。
+
+例如,`/libs` 下有以下库文件需要添加:
+
+```
+libabc_keil.lib
+libabc_iar.a
+libabc_gcc.a
+```
+
+则对应的 SConscript 如下:
+
+```
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+src = Split('''
+''')
+
+LIBPATH = [cwd + '/libs'] # LIBPATH 指定库的路径,表示库的搜索路径是当前目录下的'libs'目录
+
+if rtconfig.CROSS_TOOL == 'gcc':
+ LIBS = ['abc_gcc'] # GCC 下 LIBS 指定库的名称
+elif rtconfig.CROSS_TOOL == 'keil':
+ LIBS = ['libabc_keil'] # ARMCC 下 LIBS 指定库的名称
+else:
+ LIBS = ['libabc_iar'] # IAR 下 LIBS 指定库的名称
+
+group = DefineGroup('ABC', src, depend = [''], LIBS = LIBS, LIBPATH=LIBPATH)
+
+Return('group')
+```
+
+### 编译器选项
+
+rtconfig.py 是一个 RT-Thread 标准的编译器配置文件,控制了大部分编译选项,是一个使用 python 语言编写的脚本文件,主要用于完成以下工作:
+
+* 指定编译器(从支持的多个编译器中选择一个你现在使用的编译器)。
+
+* 指定编译器参数,如编译选项、链接选项等。
+
+当我们使用 scons 命令编译工程时,就会按照 rtconfig.py 的编译器配置选项编译工程。下面的代码为 stm32f10x-HAL BSP 目录下 rtconfig.py 的部分代码。
+
+```c
+import os
+
+# toolchains options
+ARCH='arm'
+CPU='cortex-m3'
+CROSS_TOOL='gcc'
+
+if os.getenv('RTT_CC'):
+ CROSS_TOOL = os.getenv('RTT_CC')
+
+# cross_tool provides the cross compiler
+# EXEC_PATH is the compiler execute path, for example, CodeSourcery, Keil MDK, IAR
+
+if CROSS_TOOL == 'gcc':
+ PLATFORM = 'gcc'
+ EXEC_PATH = '/usr/local/gcc-arm-none-eabi-5_4-2016q3/bin/'
+elif CROSS_TOOL == 'keil':
+ PLATFORM = 'armcc'
+ EXEC_PATH = 'C:/Keilv5'
+elif CROSS_TOOL == 'iar':
+ PLATFORM = 'iar'
+ EXEC_PATH = 'C:/Program Files/IAR Systems/Embedded Workbench 6.0 Evaluation'
+
+if os.getenv('RTT_EXEC_PATH'):
+ EXEC_PATH = os.getenv('RTT_EXEC_PATH')
+
+BUILD = 'debug'
+
+if PLATFORM == 'gcc':
+ # toolchains
+ PREFIX = 'arm-none-eabi-'
+ CC = PREFIX + 'gcc'
+ AS = PREFIX + 'gcc'
+ AR = PREFIX + 'ar'
+ LINK = PREFIX + 'gcc'
+ TARGET_EXT = 'elf'
+ SIZE = PREFIX + 'size'
+ OBJDUMP = PREFIX + 'objdump'
+ OBJCPY = PREFIX + 'objcopy'
+
+ DEVICE = '-mcpu=cortex-m3 -mthumb -ffunction-sections -fdata-sections'
+ CFLAGS = DEVICE
+ AFLAGS = '-c' + DEVICE + '-x assembler-with-cpp'
+ LFLAGS = DEVICE + '-Wl,--gc-sections,-Map=rtthread-stm32.map,-cref,-u,Reset_Handler -T stm32_rom.ld'
+```
+
+其中 CFLAGS 是 C 文件的编译选项,AFLAGS 则是汇编文件的编译选项,LFLAGS 是链接选项。BUILD 变量控制代码优化的级别。默认 BUILD 变量取值为'debug',即使用 debug 方式编译,优化级别 0。如果将这个变量修改为其他值,就会使用优化级别 2 编译。下面几种都是可行的写法(总之只要不是'debug'就可以了)。
+
+```c
+BUILD = ''
+BUILD = 'release'
+BUILD = 'hello, world'
+```
+
+建议在开发阶段都使用 debug 方式编译,不开优化,等产品稳定之后再考虑优化。
+
+关于这些选项的具体含义需要参考编译器手册,如上面使用的 armcc 是 MDK 的底层编译器。其编译选项的含义在 MDK help 中有详细说明。
+
+前文提到过如果用户执行 scons 命令时希望使用其他编译器编译工程,可以在 Env 的命令行端使用相关命令指定编译器和编译器路径。但是这样修改只对当前的 Env 进程有效,再次打开时又需要重新使用命令设置,我们可以直接修改 rtconfig.py 文件达到永久配置编译器的目的。一般来说,我们只需要修改 CROSS_TOOL 和下面的 EXEC_PATH 两个选项。
+
+* CROSS_TOOL:指定编译器。可选的值有 keil、gcc、iar,浏览 rtconfig.py 可以查看当前 BSP 所支持的编译器。如果您的机器上安装了 MDK,那么可以将 CROSS_TOOL 修改为 keil,则使用 MDK 编译工程。
+
+* EXEC_PATH:编译器的安装路径。这里有两点需要注意:
+
+安装编译器时(如 MDK、GNU GCC、IAR 等),不要安装到带有中文或者空格的路径中。否则,某些解析路径时会出现错误。有些程序默认会安装到 `C:\Program Files` 目录下,中间带有空格。建议安装时选择其他路径,养成良好的开发习惯。
+
+修改 EXEC_PATH 时,需要注意路径的格式。在 windows 平台上,默认的路径分割符号是反斜杠 `“\”`,而这个符号在 C 语言以及 Python 中都是用于转义字符的。所以修改路径时,可以将`“\”` 改为 `“/”`,或者在前面加 r(python 特有的语法,表示原始数据)。
+
+假如某编译器安装位置为 `D:\Dir1\Dir2` 下。下面几种是正确的写法:
+
+* EXEC_PATH = `r'D:\Dir1\Dir2'` 注意,字符串前带有 r,则可正常使用 `“\”`。
+
+* EXEC_PATH = `'D:/Dir1/Dir2'` 注意,改用 `“/”`,前面没有 r。
+
+* EXEC_PATH = `'D:\\Dir1\\Dir2'` 注意,这里使用 `“\”` 的转义性来转义 `“\”` 自己。
+
+* 这是错误的写法:EXEC_PATH = `'D:\Dir1\Dir2'`。
+
+如果 rtconfig.py 文件有以下代码,在配置自己的编译器时请将下列代码注释掉。
+
+```c
+if os.getenv('RTT_CC'):
+ CROSS_TOOL = os.getenv('RTT_CC')
+... ...
+if os.getenv('RTT_EXEC_PATH'):
+ EXEC_PATH = os.getenv('RTT_EXEC_PATH')
+```
+
+上面 2 个 if 判断会设置 CROSS_TOOL 和 EXEC_PATH 为 Env 的默认值。
+
+编译器配置完成之后,我们就可以使用 SCons 来编译 RT-Thread 的 BSP 了。在 BSP 目录打开命令行窗口,执行 `scons` 命令就会启动编译过程。
+
+### RT-Thread 辅助编译脚本
+
+在 RT-Thread 源代码的 tools 目录下存放有 RT-Thread 自己定义的一些辅助编译的脚本,例如用于自动生成 RT-Thread 针对一些 IDE 集成开发环境的工程文件。其中最主要的是 building.py 脚本。
+
+### SCons 更多使用
+
+对于复杂、大型的系统,显然不仅仅是一个目录下的几个文件就可以搞定的,很可能是由数个文件夹一级一级组合而成。
+
+在 SCons 中,可以编写 SConscript 脚本文件来编译这些相对独立目录中的文件,同时也可以使用 SCons 中的 Export 和 Import 函数在 SConstruct 与 SConscript 文件之间共享数据(也就是 Python 中的一个对象数据)。更多 SCons 的使用方法请参考 [SCons 官方文档](https://scons.org/documentation.html)。
diff --git a/index.html b/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..6c7b8c8f91b1d8aa8b440ba76b8fa9919d327e22
--- /dev/null
+++ b/index.html
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+ RT-Thread 文档中心
+
+
+
+
+
+
+
+
+ Loading ...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/other/figures/image-20210129101544289.png b/other/figures/image-20210129101544289.png
new file mode 100644
index 0000000000000000000000000000000000000000..c1074471d98fa7d187b429c7f7f6fcefef36ce52
Binary files /dev/null and b/other/figures/image-20210129101544289.png differ
diff --git a/other/figures/image-20210129101736085.png b/other/figures/image-20210129101736085.png
new file mode 100644
index 0000000000000000000000000000000000000000..72f6552beeacd3e1b7e5224df1f93d5f16377218
Binary files /dev/null and b/other/figures/image-20210129101736085.png differ
diff --git a/other/figures/image-20210129102019156.png b/other/figures/image-20210129102019156.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ed7a5de4ea2ca036bad5c71a14263e388a61a1d
Binary files /dev/null and b/other/figures/image-20210129102019156.png differ
diff --git a/other/figures/image-20210129102214629.png b/other/figures/image-20210129102214629.png
new file mode 100644
index 0000000000000000000000000000000000000000..555db45173762b2fdf4700616306d20aff127b76
Binary files /dev/null and b/other/figures/image-20210129102214629.png differ
diff --git a/other/novice-guide/README.md b/other/novice-guide/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c49ff8152a3f08b95ffaa4c8abe2102db138be5e
--- /dev/null
+++ b/other/novice-guide/README.md
@@ -0,0 +1,216 @@
+
+[](#入门学习)
+[](#进阶学习)
+[](#应用开发)
+
+
+初次使用 RT-Thread 操作系统进行开发的小伙伴,可以参考本篇文档的学习路线进行上手入门。
+
+
+#####
+# 版本简介
+
+
+
+## ** 标准版本 **
+
+RT-Thread,全称是 Real Time-Thread,顾名思义,它是一个嵌入式实时多线程操作系统,基本属性之一是支持多任务,允许多个任务同时运行并不意味着处理器在同一时刻真地执行了多个任务。事实上,一个处理器核心在某一时刻只能运行一个任务,由于每次对一个任务的执行时间很短、任务与任务之间通过任务调度器进行非常快速地切换(调度器根据优先级决定此刻该执行的任务),给人造成多个任务在一个时刻同时运行的错觉。在 RT-Thread 系统中,任务通过线程实现的,RT-Thread 中的线程调度器也就是以上提到的任务调度器。
+
+RT-Thread 主要采用 C 语言编写,浅显易懂,方便移植。它把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。针对资源受限的微控制器(MCU)系统,可通过方便易用的工具,裁剪出仅需要 3KB Flash、1.2KB RAM 内存资源的 NANO 版本(NANO 是 RT-Thread 官方于 2017 年 7 月份发布的一个极简版内核);而对于资源丰富的物联网设备,RT-Thread 又能使用在线的软件包管理工具,配合系统配置工具实现直观快速的模块化裁剪,无缝地导入丰富的软件功能包,实现类似 Android 的图形界面及触摸滑动效果、智能语音交互效果等复杂功能。
+
+相较于 Linux 操作系统,RT-Thread 体积小,成本低,功耗低、启动快速,除此以外 RT-Thread 还具有实时性高、占用资源小等特点,非常适用于各种资源受限(如成本、功耗限制等)的场合。虽然 32 位 MCU 是它的主要运行平台,实际上很多带有 MMU、基于 ARM9、ARM11 甚至 Cortex-A 系列级别 CPU 的应用处理器在特定应用场合也适合使用 RT-Thread。
+
+适用于需要使用 RT-Thread 的丰富功能,如各类外设、物联网组件、软件包等的场景。[更多...](/rt-thread-version/rt-thread-standard/README.md)
+
+
+
+# ** Nano 版本 **
+
+RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于家电、消费电子、医疗设备、工控等领域大量使用的 32 位 ARM 入门级 MCU 的场合。
+
+下图是 RT-Thread Nano 的软件框图,包含支持的 CPU 架构与内核源码,还有可拆卸的 FinSH 组件:
+
+
+
+**支持架构**:ARM:Cortex M0/ M3/ M4/ M7 等、RISC-V 及其他。
+
+**功能**:线程管理、线程间同步与通信、时钟管理、中断管理、内存管理。[更多...](/rt-thread-version/rt-thread-nano/an0038-nano-introduction.md)
+
+## ** Smart 版本 **
+
+
+RT-Thread Smart 是基于 RT-Thread 操作系统上的混合操作系统,简称为 rt-smart,它把应用从内核中独立出来,形成独立的用户态应用程序,并具备独立的地址空间(32 位系统上是 4G 的独立地址空间)。
+
+以下是 rt-smart 的整体结构框图,在硬件平台的基础上通过 MMU、系统调用的方式把整个系统分成了内核态及用户态。[更多...](/rt-thread-version/rt-thread-smart/rt-smart-quickstart/rt-smart-quickstart.md)
+
+
+
+
+
+
+# 学习路线
+
+从版本简介中可以看出,**Nano 版本** 是 **标准版本** 的极简内核版本,而 **Smart 版本** 是由 **标准版本** 创造而来,所以学习 **标准版本** 是学习 RT-Thread 的基础。本篇文章以学习 **RT-Thread 标准版本** 为例,为初学者制定学习路线如下(供参考),分为入门学习、进阶学习、应用开发三部分。
+## 入门学习
+
+
+
+### ** 无 RTOS 经验 **
+
+
+
+**针对人群:有 C 语言、嵌入式基础,想系统学习 RT-Thread 操作系统**
+
+#### 模拟运行
+
+[Keil MDK 模拟器 STM32F103 体验](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f103-simulator/stm32f103-simulator.md)
+
+#### 快速上手
+
+推荐使用 [潘多拉开发板](https://item.taobao.com/item.htm?id=583527145598) 配套使用 [潘多拉开发板教程. pdf](https://www.rt-thread.org/document/site/tutorial/iot_board_tutorial.pdf),或者以下主流的学习板进行学习,不建议没有任何基础就将 RT-Thread 移植到一块开发板上。
+
+- [RT-Thread 潘多拉 STM32L475 上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/iot_board/quick-start.md)
+- [野火霸道 STM32F103 上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f103-fire-arbitrary/quick-start.md)
+- [正点原子 nanoSTM32F103 上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f103-atk-nano/quick-start)
+- [野火挑战者 STM32F429 上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f429-fire-challenger/quick-start.md)
+- [正点原子探索者 STM32F407 上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f407-atk-explorer/quick-start.md)
+- [正点原子阿波罗 STM32F429 上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f429-atk-apolo/quick-start.md)
+- [野火 I.MX RT1052 上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/imxrt1052-fire-mini/quick-start.md)
+- [正点原子 I.MX RT1052 号令者上手指南](/rt-thread-version/rt-thread-standard/tutorial/quick-start/imxrt1052-atk-commander/quick-start.md)
+- [其他...](/rt-thread-version/rt-thread-standard/tutorial/quick-start/iot_board/quick-start.md)
+
+#### 内核学习
+
+[内核视频教程](https://www.rt-thread.org/page/video.html)
+
+[《内核实验手册》](https://www.rt-thread.org/document/site/tutorial/experimental-manual/experimental-manual.pdf)
+
+### ** 有 RTOS 经验 **
+
+
+
+**针对人群:学过 FreeRTOS 或 uC/OS, 想把 RT-Thread 使用起来**
+
+#### 快速上手
+
+准备一块板子,根据 RT-Thread 支持的板子 BSP 进行 [快速上手](/rt-thread-version/rt-thread-standard/tutorial/quick-start/more.md),或者根据 [STM32 系列 BSP 制作教程进行移植](https://github.com/RT-Thread/rt-thread/blob/master/bsp/stm32/README.md)。
+
+如果使用 Ubuntu 进行开发,可以参考:[在 Ubuntu 下开发 RT-Thread](/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/an0006-qemu-windows.md)。
+
+#### 编程指南
+
+快速学习内核,参考:[《RT-Thread 编程指南》](https://www.rt-thread.org/document/site/um4003-rtthread-programming-manual.pdf)。
+
+#### API 手册
+查看 [在线 API 手册](https://www.rt-thread.org/document/api/) 或 [下载 API 手册](https://www.rt-thread.org/document/api/api.zip)。
+
+
+
+## 进阶学习
+
+
+
+### ** 开发工具 **
+#### Env 工具
+
+Env 工具:Env 工具用于对源码功能进行配置或裁减,可以生成 MDK/IAR/GCC 工程,需要配合 DK/IAR/GCC 使用,详见 [Env 用户手册](/development-tools/env/env.md)。
+
+#### RT-Thread IDE
+
+RT-Thread Studio :可以在 Studio 中下载源码包并创建 rt-thread 工程,独立完成开发、编译、下载、调试等功能,并能进行功能裁剪,详见 [RT-Thread Studio 用户手册](/development-tools/rtthread-studio/um/studio-user-manual.md)。
+
+### ** 设备与驱动 **
+
+
+
+[IO 设备模型](/rt-thread-version/rt-thread-standard/programming-manual/device/device.md)
+
+[PIN 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin.md)
+
+[UART 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart.md)
+
+[CAN 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/can/can.md)
+
+[HWTIMER 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md)
+
+[I2C 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c.md)
+
+[PWM 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/pwm.md)
+
+[RTC 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/rtc/rtc)
+
+[SPI 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi)
+
+[WATCHDOG 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/watchdog/watchdog)
+
+[SENSOR 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor)
+
+[更多...](/rt-thread-version/rt-thread-standard/programming-manual/device/device.md)
+### ** 组件 **
+
+
+
+[FinSH 控制台](/rt-thread-version/rt-thread-standard/programming-manual/finsh/finsh)
+
+[文件系统](/rt-thread-version/rt-thread-standard/programming-manual/filesystem/filesystem)
+
+[netdev 网卡](/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev)
+
+[SAL 套接字抽象层](/rt-thread-version/rt-thread-standard/programming-manual/sal/sal)
+
+[AT 命令](/rt-thread-version/rt-thread-standard/programming-manual/at/at)
+
+[ulog 日志](/rt-thread-version/rt-thread-standard/programming-manual/ulog/ulog)
+
+[utest 测试框架](/rt-thread-version/rt-thread-standard/programming-manual/utest/utest)
+
+[动态模块](/rt-thread-version/rt-thread-standard/programming-manual/dlmodule/dlmodule)
+
+[POSIX 接口](/rt-thread-version/rt-thread-standard/programming-manual/posix/posix)
+
+[电源管理](/rt-thread-version/rt-thread-standard/programming-manual/pm/pm)
+
+[更多...](/rt-thread-version/rt-thread-standard/programming-manual/finsh/finsh)
+
+
+
+## 应用开发
+
+| 应用开发列表 | 应用开发列表 |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [使用 Eclipse 开发 RT-Thread](/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/an0020-qemu-eclipse) | [CmBacktrace应用](https://www.rt-thread.org/document/site/application-note/debug/cmbacktrace/an0013-CmBacktrace/) |
+| [使用 VS Code 开发 RT-Thread](/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/an0021-qemu-vscode) | [在STM32 Nucleo 开发板上使用 RW007 WiFi 模块](https://www.rt-thread.org/document/site/application-note/packages/rw007_module_using/an0034-rw007-module-using) |
+| [使用 Env 创建 RT-Thread 项目工程](/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/an0017-standard-project) | [在 STM32L4 上应用 littlefs 文件系统](https://www.rt-thread.org/document/site/application-note/components/dfs/an0027-littlefs/) |
+| [搭建RT-Thread项目框架](https://www.rt-thread.org/document/site/application-note/setup/standard-project/an0017-standard-project/) | [在潘多拉上使用 SFUD 操作 Flash](https://www.rt-thread.org/document/site/application-note/components/sfud/an0048-sfud/) |
+| [在IoT Board上实现电源管理](https://www.rt-thread.org/document/site/application-note/system/pm/an0025-pm/) | [STM32 通用 Bootloader](https://www.rt-thread.org/document/site/application-note/system/rtboot/an0028-rtboot/) |
+| [网络协议栈驱动移植](https://www.rt-thread.org/document/site/application-note/components/network/an0010-lwip-driver-porting/) | [wireshark 抓取 tls 数据包](https://www.rt-thread.org/document/site/application-note/packages/mbedtls_wireshark_sniffer/an0029-mbedtls_wireshark_sniffer) |
+| [在STM32F429上应用网络功能](https://www.rt-thread.org/document/site/application-note/components/network/an0011-network-started/) | [在 STM32 上应用 C++](https://www.rt-thread.org/document/site/application-note/components/cplusplus/an0035-cpp/) |
+| [在STM32F429上应用文件系统](https://www.rt-thread.org/document/site/application-note/components/dfs/an0012-dfs/) | [STM32 上使用 PWM](https://www.rt-thread.org/document/site/application-note/driver/pwm/an0037-rtthread-driver-pwm/) |
+| [FreeModbus 应用笔记](https://www.rt-thread.org/document/site/application-note/packages/freemodbus/an0036-freemodbus/) | [STM32 上使用 USB Host 读写 U 盘](https://www.rt-thread.org/document/site/application-note/driver/usb/an0046-rtthread-driver-usbh/) |
+| [应用AT组件连接ESP8266模块](https://www.rt-thread.org/document/site/application-note/components/at/an0014-at-client/) | [QEMU网络视频教程](https://www.rt-thread.org/document/site/tutorial/qemu-network/) |
+| [多线程非阻塞网络编程](https://www.rt-thread.org/document/site/application-note/components/network/an0019-tcpclient-socket/) | [使用QEMU运行动态模块组件](https://www.rt-thread.org/document/site/application-note/components/dlmodule/an0023-dlmodule/) |
+
+[应用设计参考...](https://www.rt-thread.org/page/projects.html)
+
+## Demo 示例
+
+
+
+|
Demo演示和教程
|
Sample示例
|
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [基于RT-Thread和i.MX RT1052的开源AutoQuad飞控](https://mp.weixin.qq.com/s/OYyE1QmtFLp17IKHPEDUfg) | [内核示例代码](https://github.com/RT-Thread-packages/kernel-sample) |
+| [基于RT-Thread的开源飞控StarryPilot](https://mp.weixin.qq.com/s/j3ihGjkZ5Jt0hwUkgY9AdQ) | [设备示例代码](https://github.com/RT-Thread-packages/peripheral-sample) |
+| [基于RT-Thread的人体健康监测系统](https://mp.weixin.qq.com/s/ptiz9UFzbVH-jt2gNVvlHg) | [文件系统示例代码](https://github.com/RT-Thread-packages/filesystem-sample) |
+| [基于RT-Thread的激光雷达避障小车](https://mp.weixin.qq.com/s/rjKExoGqhI1cPErGogEHDQ) | [网络示例代码](https://github.com/RT-Thread-packages/network-sample) |
+| [基于RT-Thread的蓝牙遥控平衡小车](https://mp.weixin.qq.com/s/bslr8Z2vyoT5uOVNXsafjA) | |
+| [蜂鸣器播放器](https://www.rt-thread.org/document/site/tutorial/beep-player/) | |
+| [分布式温度监控系统](https://www.rt-thread.org/document/site/tutorial/temperature-system/) | |
+| [智能车教程](https://www.rt-thread.org/document/site/tutorial/smart-car/) | |
+
+## 代码贡献
+
+|
开发指南
|
代码规范
|
提交代码
|
+| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [软件包开发指南](https://www.rt-thread.org/document/site/development-guide/package/package/) | [RT-Thread编程风格](https://github.com/RT-Thread/rt-thread/blob/master/documentation/coding_style_cn.md) | [向RT-Thread贡献代码](https://www.rt-thread.org/document/site/development-guide/github/github/) |
+| [STM32系列BSP制作教程](https://github.com/RT-Thread/rt-thread/blob/master/bsp/stm32/docs/STM32系列BSP制作教程.md) | [BSP开发规范](https://github.com/RT-Thread/rtthread-specification) | |
+| [传感器驱动开发指南](https://www.rt-thread.org/document/site/development-guide/sensor/sensor_driver_development/) | | |
diff --git a/other/novice-guide/_sidebar.md b/other/novice-guide/_sidebar.md
new file mode 100644
index 0000000000000000000000000000000000000000..e1c48b326fb3c31a058c6eb117f3b613178fd49d
--- /dev/null
+++ b/other/novice-guide/_sidebar.md
@@ -0,0 +1,3 @@
+
+
+- [入门指南](/other/novice-guide/README.md)
diff --git a/other/novice-guide/figures/1.png b/other/novice-guide/figures/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..958f11816b5b413b943c34bfd38edec6ae2c790b
Binary files /dev/null and b/other/novice-guide/figures/1.png differ
diff --git a/other/novice-guide/figures/2.png b/other/novice-guide/figures/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..b87c4bad401f28218f5705a4bc0c220c854a938d
Binary files /dev/null and b/other/novice-guide/figures/2.png differ
diff --git a/other/novice-guide/figures/3.png b/other/novice-guide/figures/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f8841d07f5ab39ca970f8ea303790e01b5016a1
Binary files /dev/null and b/other/novice-guide/figures/3.png differ
diff --git a/other/novice-guide/figures/4.png b/other/novice-guide/figures/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..1db37a71c6200c81dff25079c5424effc7e7a2a4
Binary files /dev/null and b/other/novice-guide/figures/4.png differ
diff --git a/other/novice-guide/figures/5.png b/other/novice-guide/figures/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..922cf5588427f43234c0ea005898e703ebba02d1
Binary files /dev/null and b/other/novice-guide/figures/5.png differ
diff --git a/other/novice-guide/figures/contents.png b/other/novice-guide/figures/contents.png
new file mode 100644
index 0000000000000000000000000000000000000000..8234ce0e1a1dfbc8966e09d9379b4aa4bb18fc15
Binary files /dev/null and b/other/novice-guide/figures/contents.png differ
diff --git a/other/novice-guide/figures/framework.png b/other/novice-guide/figures/framework.png
new file mode 100644
index 0000000000000000000000000000000000000000..577507b224edce7cb96c52e60077ab991e277f06
Binary files /dev/null and b/other/novice-guide/figures/framework.png differ
diff --git a/other/novice-guide/figures/i1.png b/other/novice-guide/figures/i1.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8d199613a35174e5de89caefe55f52552f7fd03
Binary files /dev/null and b/other/novice-guide/figures/i1.png differ
diff --git a/other/novice-guide/figures/i12.gif b/other/novice-guide/figures/i12.gif
new file mode 100644
index 0000000000000000000000000000000000000000..bdc0e702a2c162558a64e6f9729c3a11dc6ed8f5
Binary files /dev/null and b/other/novice-guide/figures/i12.gif differ
diff --git a/other/novice-guide/figures/i2.png b/other/novice-guide/figures/i2.png
new file mode 100644
index 0000000000000000000000000000000000000000..3793f91dfa2e33f24959bbea720e46d28139526d
Binary files /dev/null and b/other/novice-guide/figures/i2.png differ
diff --git a/other/novice-guide/figures/i3.png b/other/novice-guide/figures/i3.png
new file mode 100644
index 0000000000000000000000000000000000000000..ffeb415ee69b1b182f2034ba241b38ab0166d30e
Binary files /dev/null and b/other/novice-guide/figures/i3.png differ
diff --git a/other/novice-guide/figures/i4.png b/other/novice-guide/figures/i4.png
new file mode 100644
index 0000000000000000000000000000000000000000..bcb7a1a72c0356c2c78ab1cf15a82a6eda7a0772
Binary files /dev/null and b/other/novice-guide/figures/i4.png differ
diff --git a/other/novice-guide/figures/i5.png b/other/novice-guide/figures/i5.png
new file mode 100644
index 0000000000000000000000000000000000000000..3320fdd3cf1159118d15a8f030a8ddbe6c14b99e
Binary files /dev/null and b/other/novice-guide/figures/i5.png differ
diff --git a/other/novice-guide/figures/i6.png b/other/novice-guide/figures/i6.png
new file mode 100644
index 0000000000000000000000000000000000000000..b571b375ae746903376829fe1448fbed44ca184c
Binary files /dev/null and b/other/novice-guide/figures/i6.png differ
diff --git a/other/novice-guide/figures/size.png b/other/novice-guide/figures/size.png
new file mode 100644
index 0000000000000000000000000000000000000000..d34b6d5a9a08dea934b9b67cfc3401d83cf0d3fa
Binary files /dev/null and b/other/novice-guide/figures/size.png differ
diff --git a/other/novice-guide/figures/startup.png b/other/novice-guide/figures/startup.png
new file mode 100644
index 0000000000000000000000000000000000000000..4128df8d949bc2e7dd062a176beb280cff75d3cf
Binary files /dev/null and b/other/novice-guide/figures/startup.png differ
diff --git a/rt-thread-version/rt-thread-nano/README.md b/rt-thread-version/rt-thread-nano/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4b606b78cbc1cb5a9eee88972e80726598053a99
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/README.md
@@ -0,0 +1,65 @@
+# RT-Thread Nano 简介
+
+RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于家电、消费电子、医疗设备、工控等领域大量使用的 32 位 ARM 入门级 MCU 的场合。
+
+下图是 RT-Thread Nano 的软件框图,包含支持的 CPU 架构与内核源码,还有可拆卸的 FinSH 组件:
+
+
+
+**支持架构**:ARM:Cortex M0/ M3/ M4/ M7 等、RISC-V 及其他。
+
+**功能**:线程管理、线程间同步与通信、时钟管理、中断管理、内存管理。
+
+## Nano 的特点
+
+### 简单
+
+**1、下载简单**
+
+RT-Thread Nano 以软件包的方式集成在 Keil MDK 与 CubeMX 中,可以直接在软件中下载 Nano 软件包获取源码,获取方式详见 [基于 Keil MDK 移植 RT-Thread Nano](nano-port-keil/an0039-nano-port-keil.md) 与 [基于 CubeMX 移植 RT-Thread Nano](nano-port-cube/an0041-nano-port-cube.md) 。
+
+同时也提供 [下载 Nano 源码压缩包](https://www.rt-thread.org/download/nano/rt-thread-3.1.3.zip) 的途径,方便在其他开发环境移植 RT-Thread Nano,如 [基于 IAR 移植 RT-Thread Nano](nano-port-iar/an0040-nano-port-iar.md)。
+
+**2、代码简单**
+
+与 RT-Thread 完整版不同的是,Nano 不含 Scons 构建系统,不需要 Kconfig 以及 Env 配置工具,也去除了完整版特有的 device 框架和组件,仅是一个纯净的内核。
+
+**3、移植简单**
+
+由于 Nano 的极简特性,使 Nano 的移植过程变得极为简单。添加 Nano 源码到工程,就已完成 90% 的移植工作。
+
+在 Keil MDK 与 Cube MX 中还提供了 Nano 的软件包,可以一键下载加入到工程。另外,在 RT-Thread Studio 中可以基于 Nano 创建工程直接使用。以下是使用不同开发环境时,可以选择移植或使用 Nano 的方法:
+
+- [在 RT-Thread Studio 上使用 RT-Thread Nano](nano-port-studio/an0047-nano-port-studio.md)
+- [基于 KEIL MDK 移植 RT-Thread Nano](nano-port-keil/an0039-nano-port-keil.md)
+- [基于 CubeMX 移植 RT-Thread Nano](nano-port-cube/an0041-nano-port-cube.md)
+- [基于 IAR 移植 RT-Thread Nano](nano-port-iar/an0040-nano-port-iar.md)
+- [移植 RT-Thread Nano 到 RISC-V](nano-port-gcc-riscv/an0042-nano-port-gcc-riscv.md)
+
+**4、使用简单**
+
+RT-Thread Nano 在使用上也非常简单,带给开发者友好的开发体验。
+
+- 易裁剪:Nano 的配置文件为 rtconfig.h,该文件中列出了内核中的所有宏定义,有些默认没有打开,如需使用,打开即可。具体的配置可见 Nano 版块的 [RT-Thread Nano 配置](nano-config/an0043-nano-config.md) 教程。
+- 易添加 FinSH 组件:[FinSH 组件](../../programming-manual/finsh/finsh.md) 可以很方便的在 Nano 上进行移植,而不再依赖 device 框架,只需要对接两个必要的函数即可完成 [FinSH 移植](finsh-port/an0045-finsh-port.md)。
+- 自选驱动库:可以使用厂商提供的固件驱动库,如 ST 的 STD 库、HAL 库、LL 库等,可以自行选择。
+- 完善的文档:包含 [内核基础](../../programming-manual/basic/basic.md)、[线程管理 (例程)](../../programming-manual/thread/thread.md)、[时钟管理 (例程)](../../programming-manual/timer/timer.md)、[线程间同步 (例程)](../../programming-manual/ipc1/ipc1.md)、[线程间通信 (例程)](../../programming-manual/ipc2/ipc2.md)、[内存管理 (例程)](../../programming-manual/memory/memory.md)、[中断管理](../../programming-manual/interrupt/interrupt.md) ,以及 Nano 版块的移植教程。
+
+### 小巧
+
+**资源占用小**:对 RAM 与 ROM 的开销非常小,在支持 semaphore 和 mailbox 特性,并运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸,RAM 占用约 1K 左右,ROM 占用 4K 左右。
+
+Nano 资源占用情况举例:在运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸。以下是基于 Cortex M3 的 MDK 工程编译结果(优化等级 3)。
+
+```
+ Total RO Size (Code + RO Data) 4000 ( 3.91kB)
+ Total RW Size (RW Data + ZI Data) 1168 ( 1.14kB)
+ Total ROM Size (Code + RO Data + RW Data) 4092 ( 4.00kB)
+```
+
+
+> 注:如果需要丰富的组件、驱动以及软件包等功能,则建议使用 [RT-Thread 完整版](../../index.md)。
+
+### 开源免费(Apache 2.0)
+
+RT-Thread Nano 实时操作系统遵循 Apache 许可证 2.0 版本,实时操作系统内核及所有开源组件可以免费在商业产品中使用,不需要公布应用程序源码,没有潜在商业风险。
diff --git a/rt-thread-version/rt-thread-nano/_navbar.md b/rt-thread-version/rt-thread-nano/_navbar.md
new file mode 100644
index 0000000000000000000000000000000000000000..0ad4782e64805bd8ddd0b68593c9895e297b105c
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/_navbar.md
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-nano/_sidebar.md b/rt-thread-version/rt-thread-nano/_sidebar.md
new file mode 100644
index 0000000000000000000000000000000000000000..a8a1ec56aa6e1321e32a0085f00e863fabcf7062
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/_sidebar.md
@@ -0,0 +1,12 @@
+
+
+- RT-Thread Nano版本
+ - [Nano 简介](/rt-thread-version/rt-thread-nano/an0038-nano-introduction.md)
+ - [使用 RT-Thread Studio 移植](/rt-thread-version/rt-thread-nano/nano-port-studio/an0047-nano-port-studio.md)
+ - [使用 MDK 移植](/rt-thread-version/rt-thread-nano/nano-port-keil/an0039-nano-port-keil.md)
+ - [使用 IAR 移植](/rt-thread-version/rt-thread-nano/nano-port-iar/an0040-nano-port-iar.md)
+ - [使用 CubeMX 移植](/rt-thread-version/rt-thread-nano/nano-port-cube/an0041-nano-port-cube.md)
+ - [移植 Nano 到 RISC-V](/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/an0042-nano-port-gcc-riscv.md)
+ - [Nano 配置](/rt-thread-version/rt-thread-nano/nano-config/an0043-nano-config.md)
+ - [Nano 移植原理](/rt-thread-version/rt-thread-nano/nano-port-principle/an0044-nano-port-principle.md)
+ - [移植控制台/FinSH](/rt-thread-version/rt-thread-nano/finsh-port/an0045-finsh-port.md)
diff --git a/rt-thread-version/rt-thread-nano/an0038-nano-introduction.md b/rt-thread-version/rt-thread-nano/an0038-nano-introduction.md
new file mode 100644
index 0000000000000000000000000000000000000000..30ecfbd10da1cd3f187df13e10336b7a3bd78a7e
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/an0038-nano-introduction.md
@@ -0,0 +1,64 @@
+# RT-Thread Nano 简介
+
+RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于家电、消费电子、医疗设备、工控等领域大量使用的 32 位 ARM 入门级 MCU 的场合。
+
+下图是 RT-Thread Nano 的软件框图,包含支持的 CPU 架构与内核源码,还有可拆卸的 FinSH 组件:
+
+
+
+**支持架构**:ARM:Cortex M0/ M3/ M4/ M7 等、RISC-V 及其他。
+
+**功能**:线程管理、线程间同步与通信、时钟管理、中断管理、内存管理。
+
+## Nano 的特点
+
+### 简单
+
+**1、下载简单**
+
+RT-Thread Nano 以软件包的方式集成在 Keil MDK 与 CubeMX 中,可以直接在软件中下载 Nano 软件包获取源码,获取方式详见 [基于 Keil MDK 移植 RT-Thread Nano](nano-port-keil/an0039-nano-port-keil.md) 与 [基于 CubeMX 移植 RT-Thread Nano](nano-port-cube/an0041-nano-port-cube.md) 。
+
+同时也提供 [下载 Nano 源码压缩包](https://www.rt-thread.org/download/nano/rt-thread-3.1.3.zip) 的途径,方便在其他开发环境移植 RT-Thread Nano,如 [基于 IAR 移植 RT-Thread Nano](nano-port-iar/an0040-nano-port-iar.md)。
+
+**2、代码简单**
+
+与 RT-Thread 完整版不同的是,Nano 不含 Scons 构建系统,不需要 Kconfig 以及 Env 配置工具,也去除了完整版特有的 device 框架和组件,仅是一个纯净的内核。
+
+**3、移植简单**
+
+由于 Nano 的极简特性,使 Nano 的移植过程变得极为简单。添加 Nano 源码到工程,就已完成 90% 的移植工作。
+
+在 Keil MDK 与 Cube MX 中还提供了 Nano 的软件包,可以一键下载加入到工程。另外,在 RT-Thread Studio 中可以基于 Nano 创建工程直接使用。以下是使用不同开发环境时,可以选择移植或使用 Nano 的方法:
+
+- [在 RT-Thread Studio 上使用 RT-Thread Nano](nano-port-studio/an0047-nano-port-studio.md)
+- [基于 KEIL MDK 移植 RT-Thread Nano](nano-port-keil/an0039-nano-port-keil.md)
+- [基于 CubeMX 移植 RT-Thread Nano](nano-port-cube/an0041-nano-port-cube.md)
+- [基于 IAR 移植 RT-Thread Nano](nano-port-iar/an0040-nano-port-iar.md)
+- [移植 RT-Thread Nano 到 RISC-V](nano-port-gcc-riscv/an0042-nano-port-gcc-riscv.md)
+
+**4、使用简单**
+
+RT-Thread Nano 在使用上也非常简单,带给开发者友好的开发体验。
+
+- 易裁剪:Nano 的配置文件为 rtconfig.h,该文件中列出了内核中的所有宏定义,有些默认没有打开,如需使用,打开即可。具体的配置可见 Nano 版块的 [RT-Thread Nano 配置](nano-config/an0043-nano-config.md) 教程。
+- 易添加 FinSH 组件:[FinSH 组件](../../programming-manual/finsh/finsh.md) 可以很方便的在 Nano 上进行移植,而不再依赖 device 框架,只需要对接两个必要的函数即可完成 [FinSH 移植](finsh-port/an0045-finsh-port.md)。
+- 自选驱动库:可以使用厂商提供的固件驱动库,如 ST 的 STD 库、HAL 库、LL 库等,可以自行选择。
+- 完善的文档:包含 [内核基础](../../programming-manual/basic/basic.md)、[线程管理 (例程)](../../programming-manual/thread/thread.md)、[时钟管理 (例程)](../../programming-manual/timer/timer.md)、[线程间同步 (例程)](../../programming-manual/ipc1/ipc1.md)、[线程间通信 (例程)](../../programming-manual/ipc2/ipc2.md)、[内存管理 (例程)](../../programming-manual/memory/memory.md)、[中断管理](../../programming-manual/interrupt/interrupt.md) ,以及 Nano 版块的移植教程。
+
+### 小巧
+
+**资源占用小**:对 RAM 与 ROM 的开销非常小,在支持 semaphore 和 mailbox 特性,并运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸,RAM 占用约 1K 左右,ROM 占用 4K 左右。
+
+Nano 资源占用情况举例:在运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸。以下是基于 Cortex M3 的 MDK 工程编译结果(优化等级 3)。
+
+```
+ Total RO Size (Code + RO Data) 4000 ( 3.91kB)
+ Total RW Size (RW Data + ZI Data) 1168 ( 1.14kB)
+ Total ROM Size (Code + RO Data + RW Data) 4092 ( 4.00kB)
+```
+
+> 注:如果需要丰富的组件、驱动以及软件包等功能,则建议使用 [RT-Thread 完整版](../../index.md)。
+
+### 开源免费(Apache 2.0)
+
+RT-Thread Nano 实时操作系统遵循 Apache 许可证 2.0 版本,实时操作系统内核及所有开源组件可以免费在商业产品中使用,不需要公布应用程序源码,没有潜在商业风险。
diff --git a/rt-thread-version/rt-thread-nano/figures/framework.png b/rt-thread-version/rt-thread-nano/figures/framework.png
new file mode 100644
index 0000000000000000000000000000000000000000..577507b224edce7cb96c52e60077ab991e277f06
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/figures/framework.png differ
diff --git a/rt-thread-version/rt-thread-nano/figures/size.png b/rt-thread-version/rt-thread-nano/figures/size.png
new file mode 100644
index 0000000000000000000000000000000000000000..d34b6d5a9a08dea934b9b67cfc3401d83cf0d3fa
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/figures/size.png differ
diff --git a/rt-thread-version/rt-thread-nano/figures/startup.png b/rt-thread-version/rt-thread-nano/figures/startup.png
new file mode 100644
index 0000000000000000000000000000000000000000..4128df8d949bc2e7dd062a176beb280cff75d3cf
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/figures/startup.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/an0045-finsh-port.md b/rt-thread-version/rt-thread-nano/finsh-port/an0045-finsh-port.md
new file mode 100644
index 0000000000000000000000000000000000000000..77e24f8ee0c0d5b1da4046e7a92837b08f41837d
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/finsh-port/an0045-finsh-port.md
@@ -0,0 +1,467 @@
+# 在 RT-Thread Nano 上添加控制台与 FinSH
+
+本片文档分为两部分:第一部分是实现 UART 控制台,该部分只需要实现两个函数即可完成 UART 控制台打印功能。第二部分是实现移植 FinSH 组件,实现在控制台输入命令调试系统,该部分实现基于第一部分,只需要添加 FinSH 组件源码并再对接一个系统函数即可实现。下面将对这两部分进行说明。
+
+## 在 Nano 上添加 UART 控制台
+
+在 RT-Thread Nano 上添加 UART 控制台打印功能后,就可以在代码中使用 RT-Thread 提供的打印函数 rt_kprintf() 进行信息打印,从而获取自定义的打印信息,方便定位代码 bug 或者获取系统当前运行状态等。实现控制台打印(需要确认 rtconfig.h 中已使能 `RT_USING_CONSOLE` 宏定义),需要完成基本的硬件初始化,以及对接一个系统输出字符的函数,本小节将详细说明。
+
+### 实现串口初始化
+
+使用串口对接控制台的打印,首先需要初始化串口,如引脚、波特率等。 uart_init() 需要在 board.c 中的 `rt_hw_board_init()` 函数中调用。
+
+```c
+/* 实现 1:初始化串口 */
+static int uart_init(void);
+```
+
+**示例代码**:如下是基于 HAL 库的 STM32F103 串口驱动,完成添加控制台的示例代码,仅做参考。
+
+```c
+static UART_HandleTypeDef UartHandle;
+static int uart_init(void)
+{
+ /* 初始化串口参数,如波特率、停止位等等 */
+ UartHandle.Instance = USART1;
+ UartHandle.Init.BaudRate = 115200;
+ UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+ UartHandle.Init.Mode = UART_MODE_TX_RX;
+ UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
+ UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
+ UartHandle.Init.StopBits = UART_STOPBITS_1;
+ UartHandle.Init.Parity = UART_PARITY_NONE;
+
+ /* 初始化串口引脚等 */
+ if (HAL_UART_Init(&UartHandle) != HAL_OK)
+ {
+ while(1);
+ }
+
+ return 0;
+}
+INIT_BOARD_EXPORT(uart_init);
+```
+
+```c
+/* board.c */
+void rt_hw_board_init(void)
+{
+ ....
+ uart_init(); // 在 rt_hw_board_init 函数中调用 串口初始化 函数
+ ....
+}
+```
+
+### 实现 rt_hw_console_output
+
+实现 finsh 组件输出一个字符,即在该函数中实现 uart 输出字符:
+
+```c
+/* 实现 2:输出一个字符,系统函数,函数名不可更改 */
+void rt_hw_console_output(const char *str);
+```
+
+> [!NOTE]
+> 注:注意:RT-Thread 系统中已有的打印均以 `\n` 结尾,而并非 `\r\n`,所以在字符输出时,需要在输出 `\n` 之前输出 `\r`,完成回车与换行,否则系统打印出来的信息将只有换行。
+
+**示例代码**:如下是基于STM32F103 HAL 串口驱动对接的 rt_hw_console_output() 函数,实现控制台字符输出,示例仅做参考。
+
+```c
+void rt_hw_console_output(const char *str)
+{
+ rt_size_t i = 0, size = 0;
+ char a = '\r';
+
+ __HAL_UNLOCK(&UartHandle);
+
+ size = rt_strlen(str);
+ for (i = 0; i < size; i++)
+ {
+ if (*(str + i) == '\n')
+ {
+ HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1);
+ }
+ HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1);
+ }
+}
+```
+
+### 结果验证
+
+在应用代码中编写含有 rt_kprintf() 打印的代码,编译下载,打开串口助手进行验证。如下图是一个在 main() 函数中每隔 1 秒进行循环打印 `Hello RT-Thread` 的示例效果:
+
+
+
+## 在 Nano 上添加 FinSH 组件
+
+[RT-Thread FinSH](https://www.rt-thread.org/document/site/programming-manual/finsh/finsh/) 是 RT-Thread 的命令行组件(shell),提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。它可以使用串口 / 以太网 / USB 等与 PC 机进行通信,使用 FinSH 组件基本命令的效果图如下所示:
+
+
+
+本文以串口 UART 作为 FinSH 的输入输出端口与 PC 进行通信,描述如何在 Nano 上实现 FinSH shell 功能。
+
+在 RT-Thread Nano 上添加 FinSH 组件,实现 FinSH 功能的步骤主要如下:
+
+1. 添加 FinSH 源码到工程。
+2. 实现函数对接。
+
+### 添加 FinSH 源码到工程
+
+#### KEIL 添加 FinSH 源码
+
+点击 Manage Run-Environment:
+
+
+
+勾选 shell,这将自动把 FinSH 组件的源码到工程:
+
+
+
+#### Cube MX 添加 FinSH 源码
+
+打开一个 cube 工程,点击 Additional Software,在 Pack Vendor 中可勾选 RealThread 快速定位 RT-Thread 软件包,然后在 RT-Thread 软件包中勾选 shell,即可添加 FinSH 组件的源码到工程中。
+
+
+
+#### 其他 IDE 添加 FinSH 源码
+
+其他 IDE 添加 FinSH 源码,需要手动添加 FinSH 源码以及头文件路径到工程中,以 IAR IDE 为例进行结介绍。
+
+1、复制 FinSH 源码到目标裸机工程:直接复制 Nano 源码中 rtthread-nano/components 文件夹下的 `finsh` 文件夹到工程中,如图:
+
+
+
+2、目标工程添加 FinSH 源码:
+
+- 打开工程,新建 `finsh` 分组,添加工程中 `finsh` 文件夹下的所有. c 文件,如下图;
+- 添加 `finsh` 文件夹的头文件路径(点击 `Project -> Options... ` 进入弹窗进行添加,如下图);
+- 在 rtconfig.h 中添加 `#define RT_USING_FINSH` 宏定义,这样 FinSH 将生效,如下图。
+
+
+
+
+
+
+
+### 实现 rt_hw_console_getchar
+
+要实现 FinSH 组件功能:既可以打印也能输入命令进行调试,控制台已经实现了打印功能,现在还需要在 board.c 中对接控制台输入函数,实现字符输入:
+
+```c
+/* 实现 3:finsh 获取一个字符,系统函数,函数名不可更改 */
+char rt_hw_console_getchar(void);
+```
+
+- rt_hw_console_getchar():控制台获取一个字符,即在该函数中实现 uart 获取字符,可以使用查询方式获取(注意不要死等,在未获取到字符时,需要让出 CPU),也可以使用中断方式获取。
+
+**示例代码**:如下是基于 STM32F103 HAL 串口驱动对接的 rt_hw_console_getchar(),完成对接 FinSH 组件,其中获取字符采用查询方式,示例仅做参考。
+
+```c
+char rt_hw_console_getchar(void)
+{
+ int ch = -1;
+
+ if (__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_RXNE) != RESET)
+ {
+ ch = UartHandle.Instance->DR & 0xff;
+ }
+ else
+ {
+ if(__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_ORE) != RESET)
+ {
+ __HAL_UART_CLEAR_OREFLAG(&UartHandle);
+ }
+ rt_thread_mdelay(10);
+ }
+ return ch;
+}
+```
+
+### 结果验证
+
+编译下载代码,打开串口助手,可以在串口助手中打印输入 help 命令,回车查看系统支持的命令:
+
+
+
+如果没有成功运行,请检查对接的函数实现是否正确。
+
+## 移植示例代码
+
+### 中断示例
+
+如下是基于 STM32F103 HAL 串口驱动,实现控制台输出与 FinSH Shell,其中获取字符采用中断方式。原理是,在 uart 接收到数据时产生中断,在中断中把数据存入 ringbuffer 缓冲区,然后释放信号量,tshell 线程接收信号量,然后读取存在 ringbuffer 中的数据。示例仅做参考。
+
+```c
+/* 第一部分:ringbuffer 实现部分 */
+#include
+#include
+
+#define rt_ringbuffer_space_len(rb) ((rb)->buffer_size - rt_ringbuffer_data_len(rb))
+
+struct rt_ringbuffer
+{
+ rt_uint8_t *buffer_ptr;
+
+ rt_uint16_t read_mirror : 1;
+ rt_uint16_t read_index : 15;
+ rt_uint16_t write_mirror : 1;
+ rt_uint16_t write_index : 15;
+
+ rt_int16_t buffer_size;
+};
+
+enum rt_ringbuffer_state
+{
+ RT_RINGBUFFER_EMPTY,
+ RT_RINGBUFFER_FULL,
+ /* half full is neither full nor empty */
+ RT_RINGBUFFER_HALFFULL,
+};
+
+rt_inline enum rt_ringbuffer_state rt_ringbuffer_status(struct rt_ringbuffer *rb)
+{
+ if (rb->read_index == rb->write_index)
+ {
+ if (rb->read_mirror == rb->write_mirror)
+ return RT_RINGBUFFER_EMPTY;
+ else
+ return RT_RINGBUFFER_FULL;
+ }
+ return RT_RINGBUFFER_HALFFULL;
+}
+
+/**
+ * get the size of data in rb
+ */
+rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb)
+{
+ switch (rt_ringbuffer_status(rb))
+ {
+ case RT_RINGBUFFER_EMPTY:
+ return 0;
+ case RT_RINGBUFFER_FULL:
+ return rb->buffer_size;
+ case RT_RINGBUFFER_HALFFULL:
+ default:
+ if (rb->write_index > rb->read_index)
+ return rb->write_index - rb->read_index;
+ else
+ return rb->buffer_size - (rb->read_index - rb->write_index);
+ };
+}
+
+void rt_ringbuffer_init(struct rt_ringbuffer *rb,
+ rt_uint8_t *pool,
+ rt_int16_t size)
+{
+ RT_ASSERT(rb != RT_NULL);
+ RT_ASSERT(size > 0);
+
+ /* initialize read and write index */
+ rb->read_mirror = rb->read_index = 0;
+ rb->write_mirror = rb->write_index = 0;
+
+ /* set buffer pool and size */
+ rb->buffer_ptr = pool;
+ rb->buffer_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
+}
+
+/**
+ * put a character into ring buffer
+ */
+rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch)
+{
+ RT_ASSERT(rb != RT_NULL);
+
+ /* whether has enough space */
+ if (!rt_ringbuffer_space_len(rb))
+ return 0;
+
+ rb->buffer_ptr[rb->write_index] = ch;
+
+ /* flip mirror */
+ if (rb->write_index == rb->buffer_size-1)
+ {
+ rb->write_mirror = ~rb->write_mirror;
+ rb->write_index = 0;
+ }
+ else
+ {
+ rb->write_index++;
+ }
+
+ return 1;
+}
+/**
+ * get a character from a ringbuffer
+ */
+rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch)
+{
+ RT_ASSERT(rb != RT_NULL);
+
+ /* ringbuffer is empty */
+ if (!rt_ringbuffer_data_len(rb))
+ return 0;
+
+ /* put character */
+ *ch = rb->buffer_ptr[rb->read_index];
+
+ if (rb->read_index == rb->buffer_size-1)
+ {
+ rb->read_mirror = ~rb->read_mirror;
+ rb->read_index = 0;
+ }
+ else
+ {
+ rb->read_index++;
+ }
+
+ return 1;
+}
+
+
+/* 第二部分:finsh 移植对接部分 */
+#define UART_RX_BUF_LEN 16
+rt_uint8_t uart_rx_buf[UART_RX_BUF_LEN] = {0};
+struct rt_ringbuffer uart_rxcb; /* 定义一个 ringbuffer cb */
+static UART_HandleTypeDef UartHandle;
+static struct rt_semaphore shell_rx_sem; /* 定义一个静态信号量 */
+
+/* 初始化串口,中断方式 */
+static int uart_init(void)
+{
+ /* 初始化串口接收 ringbuffer */
+ rt_ringbuffer_init(&uart_rxcb, uart_rx_buf, UART_RX_BUF_LEN);
+
+ /* 初始化串口接收数据的信号量 */
+ rt_sem_init(&(shell_rx_sem), "shell_rx", 0, 0);
+
+ /* 初始化串口参数,如波特率、停止位等等 */
+ UartHandle.Instance = USART2;
+ UartHandle.Init.BaudRate = 115200;
+ UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+ UartHandle.Init.Mode = UART_MODE_TX_RX;
+ UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
+ UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
+ UartHandle.Init.StopBits = UART_STOPBITS_1;
+ UartHandle.Init.Parity = UART_PARITY_NONE;
+
+ /* 初始化串口引脚等 */
+ if (HAL_UART_Init(&UartHandle) != HAL_OK)
+ {
+ while (1);
+ }
+
+ /* 中断配置 */
+ __HAL_UART_ENABLE_IT(&UartHandle, UART_IT_RXNE);
+ HAL_NVIC_EnableIRQ(USART2_IRQn);
+ HAL_NVIC_SetPriority(USART2_IRQn, 3, 3);
+
+ return 0;
+}
+INIT_BOARD_EXPORT(uart_init);
+
+/* 移植控制台,实现控制台输出, 对接 rt_hw_console_output */
+void rt_hw_console_output(const char *str)
+{
+ rt_size_t i = 0, size = 0;
+ char a = '\r';
+
+ __HAL_UNLOCK(&UartHandle);
+
+ size = rt_strlen(str);
+ for (i = 0; i < size; i++)
+ {
+ if (*(str + i) == '\n')
+ {
+ HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1);
+ }
+ HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1);
+ }
+}
+
+/* 移植 FinSH,实现命令行交互, 需要添加 FinSH 源码,然后再对接 rt_hw_console_getchar */
+/* 中断方式 */
+char rt_hw_console_getchar(void)
+{
+ char ch = 0;
+
+ /* 从 ringbuffer 中拿出数据 */
+ while (rt_ringbuffer_getchar(&uart_rxcb, (rt_uint8_t *)&ch) != 1)
+ {
+ rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER);
+ }
+ return ch;
+}
+
+/* uart 中断 */
+void USART2_IRQHandler(void)
+{
+ int ch = -1;
+ rt_base_t level;
+ /* enter interrupt */
+ rt_interrupt_enter(); //在中断中一定要调用这对函数,进入中断
+
+ if ((__HAL_UART_GET_FLAG(&(UartHandle), UART_FLAG_RXNE) != RESET) &&
+ (__HAL_UART_GET_IT_SOURCE(&(UartHandle), UART_IT_RXNE) != RESET))
+ {
+ while (1)
+ {
+ ch = -1;
+ if (__HAL_UART_GET_FLAG(&(UartHandle), UART_FLAG_RXNE) != RESET)
+ {
+ ch = UartHandle.Instance->DR & 0xff;
+ }
+ if (ch == -1)
+ {
+ break;
+ }
+ /* 读取到数据,将数据存入 ringbuffer */
+ rt_ringbuffer_putchar(&uart_rxcb, ch);
+ }
+ rt_sem_release(&shell_rx_sem);
+ }
+
+ /* leave interrupt */
+ rt_interrupt_leave(); //在中断中一定要调用这对函数,离开中断
+}
+
+#define USART_TX_Pin GPIO_PIN_2
+#define USART_RX_Pin GPIO_PIN_3
+
+void HAL_UART_MspInit(UART_HandleTypeDef *huart)
+{
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+ if (huart->Instance == USART2)
+ {
+ __HAL_RCC_USART2_CLK_ENABLE();
+
+ __HAL_RCC_GPIOA_CLK_ENABLE();
+ /**USART2 GPIO Configuration
+ PA2 ------> USART2_TX
+ PA3 ------> USART2_RX
+ */
+ GPIO_InitStruct.Pin = USART_TX_Pin | USART_RX_Pin;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ }
+}
+```
+
+## 常见问题
+
+### Q: rt_kprintf() 不能打印浮点数吗?
+
+A: 不可以。但是可以通过其他方法实现打印浮点数的目的,比如成倍扩大数值后,分别打印整数与小数部分。
+
+### Q: 在实现 FinSH 完整功能时,却不能输入。
+
+
+
+A:可能的原因有:UART 驱动未实现字符输入函数、未打开 FinSH 组件等;如果手动开启了 HEAP,需要确定 HEAP 是否过小,导致 tshell 线程创建失败 。
+
+### Q: 出现 hard fault。
+
+A: ps 后关注各个线程栈的最大利用率,若某线程出现 100% 的情况,则表示该线程栈过小,需要将值调大。
+
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/add-finsh-src.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/add-finsh-src.png
new file mode 100644
index 0000000000000000000000000000000000000000..c36b4ae6a198b3e3d5a0931c9ec8a7aa1658ac4c
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/add-finsh-src.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/add.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/add.png
new file mode 100644
index 0000000000000000000000000000000000000000..d48da40295c2747b30a0fe533886e34a5125fbe5
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/add.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/cube-add-finsh-src.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/cube-add-finsh-src.png
new file mode 100644
index 0000000000000000000000000000000000000000..82ebb128ddae57bda9cce69c01fcaa616d1f5a16
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/cube-add-finsh-src.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/enable.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/enable.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb25c476bc7b58d50a8178c0c237c5afa9a07bc9
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/enable.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/finsh-ok.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/finsh-ok.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b3b66dcfeb9af0d6053bbddd30abdb594116c36
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/finsh-ok.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/finsh-src.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/finsh-src.png
new file mode 100644
index 0000000000000000000000000000000000000000..5cbff5cae7dd437ad07a4e0a038abf80c5edf8f4
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/finsh-src.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/i12.gif b/rt-thread-version/rt-thread-nano/finsh-port/figures/i12.gif
new file mode 100644
index 0000000000000000000000000000000000000000..bdc0e702a2c162558a64e6f9729c3a11dc6ed8f5
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/i12.gif differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-add-finsh-src.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-add-finsh-src.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2abd27101db8ca8af45befdf3e88e96c6022419
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-add-finsh-src.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-finsh-h.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-finsh-h.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab47dacc99469059bbe30856937e89e310246db4
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-finsh-h.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-finsh.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-finsh.png
new file mode 100644
index 0000000000000000000000000000000000000000..94c8986a20c9a2e6648f49048efc2a376faed9aa
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/iar-finsh.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/keil-add-src.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/keil-add-src.png
new file mode 100644
index 0000000000000000000000000000000000000000..e493726600d31b0cb3fc8a0d70b595108a581455
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/keil-add-src.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/ok1.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/ok1.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b9cda954e94501d6fb5f6b652eeeed27e93fd7c
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/ok1.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig-add.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig-add.png
new file mode 100644
index 0000000000000000000000000000000000000000..50893dabb53d89e7dadf6b91461f318d34a5647a
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig-add.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig-fini.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig-fini.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1f17adfc96be50c162e7f055f0f940981a9e58f
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig-fini.png differ
diff --git a/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig.png b/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..8406f9a2405715e8b7f27340a7a2ad23a43515c2
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/finsh-port/figures/rtconfig.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-config/an0043-nano-config.md b/rt-thread-version/rt-thread-nano/nano-config/an0043-nano-config.md
new file mode 100644
index 0000000000000000000000000000000000000000..cf9620a860d2a8fbf6dbcc9682b5d7fd97b81cc8
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/nano-config/an0043-nano-config.md
@@ -0,0 +1,151 @@
+# RT-Thread Nano 配置
+
+RT-Thread Nano 的配置在 rtconfig.h 中进行,通过开关宏定义来使能或关闭某些功能,接下来对该配置文件中的宏定义进行说明。
+
+## 头文件
+
+头文件 `RTE_Components.h` 仅由 Keil MDK 工程生成,其中仅定义了一个打开 FinSH 组件的宏 `RTE_USING_FINSH`。
+
+```c
+
+#if defined (__CC_ARM) || (__CLANG_ARM)
+#include "RTE_Components.h" /* 用来开关 FinSH 组件,仅 MDK 会产生该文件 */
+
+#if defined(RTE_USING_FINSH)
+#define RT_USING_FINSH
+#endif //RTE_USING_FINSH
+
+#endif //(__CC_ARM) || (__CLANG_ARM)
+
+```
+
+非 Keil MDK 则不需要该头文件,若需打开 FinSH 组件,可直接在 rtconfig.h 中手动定义 `RT_USING_FINSH` 打开 FinSH 组件。
+
+## 基础配置
+
+1、设置系统最大优先级,可设置范围 8 到 256,默认值 8,可修改。
+
+```c
+#define RT_THREAD_PRIORITY_MAX 8
+```
+2、设置 RT-Thread 操作系统节拍,表示多少 tick 每秒,如默认值为 100 ,表示一个时钟节拍(os tick)长度为 10ms。常用值为 100 或 1000。时钟节拍率越快,系统的额外开销就越大。
+
+```c
+#define RT_TICK_PER_SECOND 100
+```
+3、字节对齐时设定对齐的字节个数,默认 4,常使用 ALIGN(RT_ALIGN_SIZE) 进行字节对齐。
+
+```c
+#define RT_ALIGN_SIZE 4
+```
+4、设置对象名称的最大长度,默认 8 个字符,一般无需修改。
+
+```c
+#define RT_NAME_MAX 8
+```
+5、设置使用组件自动初始化功能,默认需要使用,开启该宏则可以使用自动初始化功能。
+
+```c
+#define RT_USING_COMPONENTS_INIT
+```
+6、开启 `RT_USING_USER_MAIN` 宏,则打开 user_main 功能,默认需要开启,这样才能调用 RT-Thread 的启动代码;main 线程的栈大小默认为 256,可修改。
+
+```c
+#define RT_USING_USER_MAIN
+#define RT_MAIN_THREAD_STACK_SIZE 256
+```
+## 内核调试功能配置
+
+
+定义 `RT_DEBUG` 宏则开启 debug 模式,默认关闭。若开启系统调试,则可以打印系统 LOG 日志。
+
+```c
+//#define RT_DEBUG // 关闭 debug
+
+#define RT_DEBUG_INIT 0 // 启用组件初始化调试配置,设置为 1 则会打印自动初始化的函数名称
+
+//#define RT_USING_OVERFLOW_CHECK // 关闭栈溢出检查
+```
+## 钩子函数配置
+
+设置是否使用钩子函数,默认关闭。
+
+```c
+//#define RT_USING_HOOK // 是否 开启系统钩子功能
+
+//#define RT_USING_IDLE_HOOK // 是否 开启空闲线程钩子功能
+```
+## 软件定时器配置
+
+设置是否启用软件定时器,以及相关参数的配置,默认关闭。
+
+```c
+#define RT_USING_TIMER_SOFT 0 // 关闭软件定时器功能,为 1 则打开
+#if RT_USING_TIMER_SOFT == 0
+#undef RT_USING_TIMER_SOFT
+#endif
+
+#define RT_TIMER_THREAD_PRIO 4 // 设置软件定时器线程的优先级,默认为 4
+
+#define RT_TIMER_THREAD_STACK_SIZE 512 // 设置软件定时器线程的栈大小,默认为 512 字节
+```
+## IPC 配置
+
+系统支持的 IPC 有:信号量、互斥量、事件集、邮箱、消息队列。通过定义相应的宏打开或关闭该 IPC 的使用。
+
+```c
+#define RT_USING_SEMAPHORE // 设置是否使用 信号量
+
+//#define RT_USING_MUTEX // 设置是否使用 互斥量
+
+//#define RT_USING_EVENT // 设置是否使用 事件集
+
+#define RT_USING_MAILBOX // 设置是否使用 邮箱
+
+//#define RT_USING_MESSAGEQUEUE // 设置是否使用 消息队列
+```
+## 内存配置
+
+RT-Thread 内存管理包含:内存池、内存堆、小内存算法。通过开启相应的宏定义使用相应的功能。
+
+```c
+//#define RT_USING_MEMPOOL // 是否使用 内存池
+
+//#define RT_USING_HEAP // 是否使用 内存堆
+
+#define RT_USING_SMALL_MEM // 是否使用 小内存管理
+
+//#define RT_USING_TINY_SIZE // 是否使用 小体积的算法,牵扯到 rt_memset、rt_memcpy 所产生的体积
+```
+## FinSH 控制台配置
+
+定义 RT_USING_CONSOLE 则开启控制台功能,失能该宏则关闭控制台,不能实现打印;修改 RT_CONSOLEBUF_SIZE 可配置控制台缓冲大小。
+
+```c
+#define RT_USING_CONSOLE // 控制台宏开关
+#define RT_CONSOLEBUF_SIZE 128 // 设置控制台数据 buf 大小,默认 128 byte
+```
+FinSH 组件的使用通过定义 RT_USING_FINSH 开启,开启后可对 FinSH 组件相关的参数进行配置修改,FINSH_THREAD_STACK_SIZE 的值默认较小,请根据实际情况改大。
+```c
+#if defined (RT_USING_FINSH) // 开关 FinSH 组件
+
+ #define FINSH_USING_MSH // 使用 FinSH 组件 MSH 模式
+ #define FINSH_USING_MSH_ONLY // 仅使用 MSH 模式
+
+ #define __FINSH_THREAD_PRIORITY 5 // 设置 FinSH 组件优先级,配置该值后通过下面的公式进行计算
+ #define FINSH_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 8 * __FINSH_THREAD_PRIORITY + 1)
+
+ #define FINSH_THREAD_STACK_SIZE 512 // 设置 FinSH 线程栈大小,范围 1-4096
+
+ #define FINSH_HISTORY_LINES 1 // 设置 FinSH 组件记录历史命令个数,值范围 1-32
+
+ #define FINSH_USING_SYMTAB // 使用符号表,需要打开,默认打开
+
+#endif
+```
+
+## 常见问题
+
+Q:移植完成之后出现 hard fault。
+
+A:在默认情况下,系统配置的各种线程栈大小均较小,若不能正常运行,很有可能是栈不够用,可将栈值调大。例如 main 线程栈大小默认为 256,在实际使用时,main 中可能加入其它代码导致栈不够用的情况;FinSH 组件的线程 tshell,默认栈 512 也比较小,在使用时可以调大。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/an0041-nano-port-cube.md b/rt-thread-version/rt-thread-nano/nano-port-cube/an0041-nano-port-cube.md
new file mode 100644
index 0000000000000000000000000000000000000000..9f316ef1b8fb70d446be87fb0ee101495d3d434a
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/nano-port-cube/an0041-nano-port-cube.md
@@ -0,0 +1,189 @@
+# 基于 CubeMX 移植 RT-Thread Nano
+
+本文介绍了如何基于 CubeMX 移植 RT-Thread Nano,并说明生成代码工程的步骤。
+
+RT-Thread Nano 已集成在 CubeMX 中,可以直接在 IDE 中进行下载添加。本文档介绍了如何使用 CubeMX 移植 RT-Thread Nano,并以一个 stm32f103 的基础工程作为示例进行讲解。
+
+移植 Nano 的主要步骤:
+
+1. 准备一个 CubeMX 基础工程,并获取 RT-Thread Nano pack 安装包进行安装。
+2. 在基础工程中添加 RT-Thread Nano 源码。
+3. 适配 Nano,主要从 中断、时钟、内存、应用 这几个方面进行适配,实现移植。
+4. 最后可对 Nano 进行配置:Nano 是可裁剪的,可以通过配置文件 rtconfig.h 实现对系统的裁剪。
+
+## 准备工作
+
+- 下载 Cube MX 5.0 ,下载地址 。
+- 在 CubeMX 上下载 RT-Thread Nano pack 安装包。
+
+### Nano pack 安装
+
+要获取 RT-Thread Nano 软件包,需要在 CubeMX 中添加 。
+
+具体步骤:进入打开 CubeMX,从菜单栏 `help` 进入 `Manage embedded software packages` 界面,点击 `From Url` 按钮,进入 `User Defined Packs Manager` 界面,其次点击 `new`,填入上述网址,然后点击 `check`,如下图所示:
+
+
+
+`check` 通过后,点击 OK 回到 `User Defined Packs Manager` 界面,再次点击 OK,CubeMX 自动连接服务器,获取包描述文件。回到 `Manage embedded software packages` 界面,就会发现 `RT-Thread Nano 3.1.3` 软件包,选择该软件包,点击 `Install Now`,如下图所示:
+
+
+
+点击安装之后,弹出 `Licensing Agreement` ,同意协议,点击 `Finish`,如下图所示:
+
+
+
+等待安装完成,成功安装后,版本前面的小蓝色框变成填充的黄绿色,现象如下图所示:
+
+
+
+至此,RT-Thread Nano 软件包安装完毕,退出 `Manage embedded software packages` 界面,进入 CubeMX 主界面。
+
+### 创建基础工程
+
+在 CubeMX 主界面的菜单栏中 `File` 选择 `New Project`,如下图所示
+
+
+
+新建工程之后,在弹出界面芯片型号中输入某一芯片型号,方便锁定查找需要的芯片,双击被选中的芯片,如下图所示
+
+
+
+时钟树的配置直接使用默认即可,然后还需要配置下载方式。
+
+## 添加 RT-Thread Nano 到工程
+
+### 选择 Nano 组件
+
+选中芯片型号之后,点击 `Additional Softwares`,进入 `Additional Software Components selection` 界面,在 `Pack Vendor` 中选择 `RealThread`, 然后根据需求选择 RT-Thread 组件(此处只移植 Nano,只选择 kernel 即可),然后点击 OK 按钮,如下图所示:
+
+
+
+> 注意:RT-Thread Nano 软件包中包含 kernel 与 shell 两个部分,仅选择 kernel 表示只使用 RT-Thread 内核,工程中会添加内核代码;选择 kernel 与 shell 表示在使用 RT-Thread Nano 的基础上使用 FinSH Shell 组件,工程中会添加内核代码与 FinSH 组件的代码,FinSH 的移植详见 [《在 RT-Thread Nano 上添加控制台与 FinSH》](../finsh-port/an0045-finsh-port.md)。
+
+### 配置 Nano
+
+选择组件之后,对组件参数进行配置。在工程界面 `Pinout & Configuration` 中,进入所选组件参数配置区,按照下图进行配置
+
+
+
+### 工程管理
+
+给工程取名、选择代码存放位置、选择生成代码的 `Toolchain/IDE`。`Cube MX` 不仅能够生成 `Keil4/Keil5` 的工程,而且还能够生成 `IAR7/IAR8` 等 IDE 的工程,功能强大,本文从下拉框中选择 MDK5,操作如图所示
+
+
+
+### 配置 MCU
+
+根据需求配置 MCU 的功能。
+
+## 适配 RT-Thread Nano
+
+### 中断与异常处理
+
+RT-Thread 操作系统重定义 `HardFault_Handler`、`PendSV_Handler`、`SysTick_Handler` 中断函数,为了避免重复定义的问题,在生成工程之前,需要在中断配置中,代码生成的选项中,取消选择三个中断函数(对应注释选项是 `Hard fault interrupt`, `Pendable request`, `Time base :System tick timer`),最后点击生成代码,具体操作如下图中步骤 11-15 所示:
+
+
+
+等待工程生成完毕,点击打开工程,如下图所示,即可进入 MDK5 工程中。
+
+
+
+### 系统时钟配置
+
+需要在 board.c 中实现 ` 系统时钟配置 `(为 MCU、外设提供工作时钟)与 `OS Tick 的配置 `(为操作系统提供心跳 / 节拍)。
+
+如下代码所示, `HAL_Init()` 初始化 HAL 库, `SystemClock_Config() `配置了系统时钟, `SystemCoreClockUpdate()` 对系统时钟进行更新,`_SysTick_Config()` 配置了 OS Tick。此处 OS Tick 使用滴答定时器 systick 实现,需要用户在 board.c 中实现 `SysTick_Handler()` 中断服务例程,调用 RT-Thread 提供的 `rt_tick_increase()` ,如下图所示。
+
+```c
+/* board.c */
+void rt_hw_board_init()
+{
+ HAL_Init();
+ SystemClock_Config();
+
+ /* System Clock Update */
+ SystemCoreClockUpdate();
+
+ /* System Tick Configuration */
+ _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
+
+ /* Call components board initial (use INIT_BOARD_EXPORT()) */
+#ifdef RT_USING_COMPONENTS_INIT
+ rt_components_board_init();
+#endif
+
+#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
+ rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
+#endif
+}
+```
+
+
+
+### 内存堆初始化
+
+系统内存堆的初始化在 board.c 中的 rt_hw_board_init() 函数中完成,内存堆功能是否使用取决于宏 RT_USING_HEAP 是否开启,RT-Thread Nano 默认不开启内存堆功能,这样可以保持一个较小的体积,不用为内存堆开辟空间。
+
+开启系统 heap 将可以使用动态内存功能,如使用 rt_malloc、rt_free 以及各种系统动态创建对象的 API。若需要使用系统内存堆功能,则打开 RT_USING_HEAP 宏定义即可,此时内存堆初始化函数 rt_system_heap_init() 将被调用,如下所示:
+
+
+
+初始化内存堆需要堆的起始地址与结束地址这两个参数,系统中默认使用数组作为 heap,并获取了 heap 的起始地址与结束地址,该数组大小可手动更改,如下所示:
+
+
+
+注意:开启 heap 动态内存功能后,heap 默认值较小,在使用的时候需要改大,否则可能会有申请内存失败或者创建线程失败的情况,修改方法有以下两种:
+
+- 可以直接修改数组中定义的 RT_HEAP_SIZE 的大小,至少大于各个动态申请内存大小之和,但要小于芯片 RAM 总大小。
+- 也可以参考[《RT-Thread Nano 移植原理》——实现动态内存堆](../nano-port-principle/an0044-nano-port-principle.md) 章节进行修改,使用 RAM ZI 段结尾处作为 HEAP 的起始地址,使用 RAM 的结尾地址作为 HEAP 的结尾地址,这是 heap 能设置的最大值的方法。
+
+## 编写第一个应用
+
+移植好 RT-Thread Nano 之后,则可以开始编写第一个应用代码。此时 main() 函数就转变成 RT-Thread 操作系统的一个线程,现在可以在 main() 函数中实现第一个应用:板载 LED 指示灯闪烁。
+
+1. 首先在文件首部包含 RT-Thread 的相关头文件 `` 。
+2. 在 main() 函数中(也就是在 main 线程中)写 LED 闪烁代码:初始化 LED 引脚、在循环中点亮 / 熄灭 LED。
+3. 延时函数使用 RT-Thread 提供的延时函数 rt_thread_mdelay(),该函数会引起系统调度,切换到其他线程运行,体现了线程实时性的特点。
+
+
+
+编译程序之后下载到芯片就可以看到基于 RT-Thread 的程序运行起来了,LED 正常闪烁。
+
+> [!NOTE]
+> 注:当添加 RT-Thread 之后,裸机中的 main() 函数会自动变成 RT-Thread 系统中 main 线程 的入口函数。由于线程不能一直独占 CPU,所以此时在 main() 中使用 while(1) 时,需要有让出 CPU 的动作,比如使用 `rt_thread_mdelay()` 系列的函数让出 CPU。
+
+**与裸机 LED 闪烁应用代码的不同**:
+
+1). 延时函数不同: RT-Thread 提供的 `rt_thread_mdelay()` 函数可以引起操作系统进行调度,当调用该函数进行延时时,本线程将不占用 CPU,调度器切换到系统的其他线程开始运行。而裸机的 delay 函数是一直占用 CPU 运行的。
+
+2). 初始化系统时钟的位置不同:移植好 RT-Thread Nano 之后,不需要再在 main() 中做相应的系统配置(如 hal 初始化、时钟初始化等),这是因为 RT-Thread 在系统启动时,已经做好了系统时钟初始化等的配置,这在上一小节 “系统时钟配置” 中有讲解。
+
+## 配置 RT-Thread Nano
+
+配置 RT-Thread Nano 可以在上面小节 ` 添加 RT-Thread Nano -> 配置 Nano` 中,这是在生成工程之前做的配置。如果生成工程之后,想直接在目标工程的 IDE 中配置,那么直接修改工程中 rtconfig.h 文件即可,完整配置详见 [《 RT-Thread Nano 配置》](../nano-config/an0043-nano-config.md)。
+
+## 常见问题
+
+### Q: 出现三个中断重复定义
+
+**A**: 参考生成工程章节中 **中断配置** 小节。
+
+### Q: 生成的工程不包含 RT-Thread
+
+**A**: 可能是没有添加 RT-Thread Nano 组件到工程,参考生成工程章节中 **选择 Nano 组件** 小节。
+
+### Q: 在添加 Nano 时,选择 shell 后生成工程,编译工程报错。
+
+**A**: 报错 "Undefined symbol rt_hw_console_getchar (referrred from shell.o)"。这是由于添加 FinSH 组件源码之后,还需要自行定义与实现该函数才能完成 FinSH 的移植,详见 [《在 RT-Thread Nano 上添加控制台与 FinSH》](../finsh-port/an0045-finsh-port.md)。
+
+### Q: 生成的工程不包含 .S 文件
+
+**A**: 生成的工程中发现 `context_rvds.S`, `context_iar.S` 或者其他文件存在丢失的情况,建议重新使用 CubeMX 生成工程。
+
+### Q: check 网址失败
+
+**A**: 建议升级 Cube MX 版本至 5.0。
+
+### Q: CubeMX 如何升级
+
+**A**:本文创建工程基于 CubeMX 5.0.0,如果比较低的版本,建议升级,升级方式: `help -> Check for updates`,进入后,点击 Refresh,CubeMX 自动去获取最新的程序,成功获取后选择版本,点击 `Install now`,等待完成安装。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/3-5.jpg b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/3-5.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..62a6ec89c58212b4e8ff80ef5b099387f3830043
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/3-5.jpg differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/4-1.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/4-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..eba9746543da0527a1b0d270705f498bf24e0480
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/4-1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/6-2.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/6-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..9be4c302e3ae1a748a5dfad858db0016c26f89eb
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/6-2.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/exisit_pack.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/exisit_pack.png
new file mode 100644
index 0000000000000000000000000000000000000000..a6e53960a94f759d11beb57d5e3dc5a9c359bc73
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/exisit_pack.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/finish.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/finish.png
new file mode 100644
index 0000000000000000000000000000000000000000..7eae30ed16775866dbc5e0a256433215169139a5
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/finish.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/from_url.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/from_url.png
new file mode 100644
index 0000000000000000000000000000000000000000..a7cd9595c2310d24e59c665eb4a7e3f97164109b
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/from_url.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/heap1.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/heap1.png
new file mode 100644
index 0000000000000000000000000000000000000000..88280781fcacf92aff963064215cca73895eced7
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/heap1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/installed_url.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/installed_url.png
new file mode 100644
index 0000000000000000000000000000000000000000..d2a9de0c1989a63ee79191d994420791f8841bb5
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/installed_url.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/mcu_choice.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/mcu_choice.png
new file mode 100644
index 0000000000000000000000000000000000000000..8d34dc6fc2bda439c5f77a80ae13ecf71b77339f
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/mcu_choice.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/new_project.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/new_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..f43ed4c535d886ccb43159e97be2dffe00208307
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/new_project.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/nvic_config.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/nvic_config.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1b208968d9488fcb2aedf435b5f55a402b10adb
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/nvic_config.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/open_project.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/open_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..1e89a7b8c1a6607828c62a5d2f80a4f78400e8bf
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/open_project.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/pack_choice.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/pack_choice.png
new file mode 100644
index 0000000000000000000000000000000000000000..42b62596b4c044d1bfb489aaf5b9f90b78bf8439
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/pack_choice.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/pack_config.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/pack_config.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d1aa5c4d4093e8952eb401013ea59353fdcf4c3
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/pack_config.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/project_manager.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/project_manager.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c9957e4c641e0f93485d9019d816cc06264aecb
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/project_manager.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/rtos-main.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/rtos-main.png
new file mode 100644
index 0000000000000000000000000000000000000000..150edbf261e76c9327200e52d0aa6d530c99fbdd
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/rtos-main.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-cube/figures/systick.png b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/systick.png
new file mode 100644
index 0000000000000000000000000000000000000000..b456a1a9d9d204e7861653517286306335e29e4a
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-cube/figures/systick.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/an0042-nano-port-gcc-riscv.md b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/an0042-nano-port-gcc-riscv.md
new file mode 100644
index 0000000000000000000000000000000000000000..6b9064032522ca52545a2fa37d4b105f914a0654
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/an0042-nano-port-gcc-riscv.md
@@ -0,0 +1,201 @@
+# 移植 RT-Thread Nano 到 RISC-V
+
+本文介绍了如何移植 RT-Thread Nano 到 RISC-V 架构,以 Eclipse GCC 环境为例,基于一个 GD32V103 MCU 的基础工程作为示例进行讲解。
+
+移植 Nano 的主要步骤:
+
+1. 准备一个基础的 Eclipse 工程,并获取 RT-Thread Nano 源码压缩包。
+2. 在基础工程中添加 RT-Thread Nano 源码,添加相应头文件路径。
+3. 适配 Nano,主要从 中断、时钟、内存、应用 这几个方面进行适配,实现移植。
+4. 最后可对 Nano 进行配置:Nano 是可裁剪的,通过配置文件 rtconfig.h 实现对系统的裁剪。
+
+## 准备工作
+
+- 下载 RT-Thread Nano 发布版本代码。
+- 准备一份基础的裸机源码工程,如 LED 指示灯闪烁示例代码。
+
+### 下载 Nano 源码
+
+[点击此处](https://www.rt-thread.org/download/nano/rt-thread-3.1.3.zip) 下载 RT-Thread Nano 源码。
+
+### 基础工程准备
+
+在移植 RT-Thread Nano 之前,我们需要准备一个能正常运行的裸机工程。作为示例,本文使用的是基于 GD32V103 的一个 LED 闪烁程序。程序的主要截图如下:
+
+
+
+在我们的例程中主要做了系统初始化与 LED 闪烁功能,编译下载程序后,就可以看到开发板上的 LED 在闪烁了。读者可以根据自己的需要使用的芯片,完成一个类似的裸机工程。
+
+## 添加 RT-Thread Nano 到工程
+
+### 添加 Nano 源文件
+
+在准备好的 Eclipse 工程下面新建 rtthread 文件夹,并在该文件中添加以下文件:
+
+- Nano 源码中的 include、libcpu、src 文件夹。注意 libcpu 仅保留与该芯片架构相关的文件,如示例中使用的是 `bunblebee` 与 `common`。
+- 配置文件:示例代码 rtthread-nano/bsp 中的两个文件:`board.c` 与 `rtconfig.h`。
+
+
+
+重新打开 eclipse 工作空间,导入工程,rtthread 已经加载到工程中:
+
+
+
+Cortex-M 芯片内核移植代码:
+
+```
+context_gcc.s
+cpuport.c
+```
+
+Kernel 文件包括:
+
+```
+clock.c
+components.c
+device.c
+idle.c
+ipc.c
+irq.c
+kservice.c
+mem.c
+object.c
+scheduler.c
+thread.c
+timer.c
+```
+
+板级配置代码及配置文件:
+
+```
+board.c
+rtconfig.h
+```
+
+### 添加头文件路径
+
+右击工程,点击 `properties` 进入下图所示界面,点击 ` C/C++ Build` -> `settings` ,分别添加汇编与 C 的头文件路径:添加 rtconfig.h 头文件所在位置的路径,添加 include 文件夹下的头文件路径。然后点击 ` C/C++ General` -> `Path and Symbols` ,添加相应的头文件,最后点击应用即可。
+
+
+
+## 适配 RT-Thread Nano
+
+### 修改 start.S
+
+修改启动文件,实现 RT-Thread 的启动:由于 RT-Thread Nano 在 GCC 环境下的启动是由 entrry() 函数调用了启动函数 rt_thread_startup(),所以需要修改启动文件 start.S,使其在启动时先跳转至 entry() 函数执行,而不是跳转至 main(),这样就实现了 RT-Thread 的启动。
+
+```c
+/* RT-Thread 在 GCC 下的启动方式 */
+int entry(void)
+{
+ rtthread_startup();
+ return 0;
+}
+```
+
+
+
+### 中断与异常处理
+
+RT-Thread 提供中断管理方法,当系统没有实现类似中断向量表的功能,物理中断要和用户的中断服务例程相关联,就需要使用中断管理接口对中断进行管理,这样当发生中断时就可以触发相应的中断,执行中断服务例程。
+
+本例程中的 gd32f103 芯片在启动文件中提供了中断向量表,用户可直接使用中断向量提供的函数实现对应 IRQ。当一个中断触发时,处理器将直接判定是哪个中断源,然后直接跳转到相应的固定位置进行处理,不需要再自行实现中断管理。
+
+### 系统时钟配置
+
+需要在 board.c 中实现 ` 系统时钟配置 `(为 MCU、外设提供工作时钟)与 `OS Tick 的配置 `(为操作系统提供心跳 / 节拍)。
+
+配置示例如下图所示,`riscv_clock_init()` 配置了系统时钟,`ostick_config()` 配置了 OS Tick。
+
+
+
+`riscv_clock_init()` 配置了系统时钟,示例如下:
+
+
+
+`ostick_config()` 配置了 OS Tick,示例如下,此处 OS Tick 使用硬件定时器实现,需要用户在 board.c 中实现该硬件定时器的中断服务例程 `eclic_mtip_handler()` ,调用 RT-Thread 提供的 `rt_tick_increase()` 。
+
+
+
+
+
+由于 `eclic_mtip_handler()` 中断服务例程由用户在 board.c 中重新实现,做了系统 OS Tick,所以需要将自定义的 `eclic_mtip_handler` 删除,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。
+
+### 内存堆初始化
+
+系统内存堆的初始化在 board.c 中的 rt_hw_board_init() 函数中完成,内存堆功能是否使用取决于宏 RT_USING_HEAP 是否开启,RT-Thread Nano 默认不开启内存堆功能,这样可以保持一个较小的体积,不用为内存堆开辟空间。
+
+开启系统 heap 将可以使用动态内存功能,如使用 rt_malloc、rt_free 以及各种系统动态创建对象的 API。若需要使用系统内存堆功能,则打开 RT_USING_HEAP 宏定义即可,此时内存堆初始化函数 rt_system_heap_init() 将被调用,如下所示:
+
+
+
+初始化内存堆需要堆的起始地址与结束地址这两个参数,系统中默认使用数组作为 heap,并获取了 heap 的起始地址与结束地址,该数组大小可手动更改,如下所示:
+
+
+
+注意:开启 heap 动态内存功能后,heap 默认值较小,在使用的时候需要改大,否则可能会有申请内存失败或者创建线程失败的情况,修改方法有以下两种:
+
+- 可以直接修改数组中定义的 RT_HEAP_SIZE 的大小,至少大于各个动态申请内存大小之和,但要小于芯片 RAM 总大小。
+- 也可以参考[《RT-Thread Nano 移植原理》——实现动态内存堆](../nano-port-principle/an0044-nano-port-principle.md) 章节进行修改,使用 RAM ZI 段结尾处作为 HEAP 的起始地址,使用 RAM 的结尾地址作为 HEAP 的结尾地址,这是 heap 能设置的最大值的方法。
+
+## 编写第一个应用
+
+移植好 RT-Thread Nano 之后,则可以开始编写第一个应用代码。此时 main() 函数就转变成 RT-Thread 操作系统的一个线程,现在可以在 main() 函数中实现第一个应用:板载 LED 指示灯闪烁,这里直接基于裸机 LED 指示灯进行修改。
+
+1. 首先在文件首部增加 RT-Thread 的相关头文件 `` 。
+2. 在 main() 函数中(也就是在 main 线程中)实现 LED 闪烁代码:初始化 LED 引脚、在循环中点亮 / 熄灭 LED。
+3. 将延时函数替换为 RT-Thread 提供的延时函数 rt_thread_mdelay()。该函数会引起系统调度,切换到其他线程运行,体现了线程实时性的特点。
+
+
+
+编译程序之后下载到芯片就可以看到基于 RT-Thread 的程序运行起来了,LED 正常闪烁。
+
+> [!NOTE]
+> 注:当添加 RT-Thread 之后,裸机中的 main() 函数会自动变成 RT-Thread 系统中 main 线程 的入口函数。由于线程不能一直独占 CPU,所以此时在 main() 中使用 while(1) 时,需要有让出 CPU 的动作,比如使用 `rt_thread_mdelay()` 系列的函数让出 CPU。
+
+**与裸机 LED 闪烁应用代码的不同**:
+
+1). 延时函数不同: RT-Thread 提供的 `rt_thread_mdelay()` 函数可以引起操作系统进行调度,当调用该函数进行延时时,本线程将不占用 CPU,调度器切换到系统的其他线程开始运行。而裸机的 delay 函数是一直占用 CPU 运行的。
+
+2). 初始化系统时钟的位置不同:移植好 RT-Thread Nano 之后,不需要再在 main() 中做相应的系统配置(如 hal 初始化、时钟初始化等),这是因为 RT-Thread 在系统启动时,已经做好了系统时钟初始化等的配置,这在上一小节 “系统时钟配置” 中有讲解。
+
+## 配置 RT-Thread Nano ##
+
+用户可以根据自己的需要通过打开或关闭 rtconfig.h 文件里面的宏定义,配置相应功能,如下是 rtconfig.h 的代码片段:
+
+```c
+...
+
+// IPC(Inter-process communication) Configuration
+// Using Semaphore
+// Using Semaphore
+#define RT_USING_SEMAPHORE
+//
+// Using Mutex
+// Using Mutex
+//#define RT_USING_MUTEX // 打开此宏则使能互斥量的使用
+//
+// Using Event
+// Using Event
+//#define RT_USING_EVENT // 打开此宏则使能事件集的使用
+//
+// Using MailBox
+// Using MailBox
+//#define RT_USING_MAILBOX // 打开此宏则使能邮箱的使用
+//
+// Using Message Queue
+// Using Message Queue
+//#define RT_USING_MESSAGEQUEUE // 打开此宏则使能消息队列的使用
+//
+//
+
+// Memory Management Configuration
+// Using Memory Pool Management
+// Using Memory Pool Management
+//#define RT_USING_MEMPOOL // 打开此宏则使能内存池的使用
+
+...
+```
+
+RT-Thread Nano 默认未开启宏 RT_USING_HEAP,故只支持静态方式创建任务及信号量。若要通过动态方式创建对象则需要在 rtconfig.h 文件里开启 RT_USING_HEAP 宏定义。完整配置详见 [《 RT-Thread Nano 配置》](../nano-config/an0043-nano-config.md)。
+
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano-proj.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano-proj.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e5b7f550a31839acaddba4c930157ce9a7ab9fb
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano-proj.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano-project.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..da880ed91eff04f9833f7459654ef64272835717
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano-project.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano.png
new file mode 100644
index 0000000000000000000000000000000000000000..306df599c525977ea14e436d01a1068296a1d6ba
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/add-nano.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/clock-config.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/clock-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c78ed9bac412cff8ae206421384631d82a29739
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/clock-config.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/clock.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/clock.png
new file mode 100644
index 0000000000000000000000000000000000000000..17fbf73a86b979097a08f9bcd3b1742a895821ce
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/clock.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/gcc-rtos-main.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/gcc-rtos-main.png
new file mode 100644
index 0000000000000000000000000000000000000000..56d52a6721f2a3b5bb957ffeec2381253e700a05
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/gcc-rtos-main.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/heap-def.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/heap-def.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa2892a3057600a3be0c66be5a4fdd50a0c45fd5
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/heap-def.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/heap1.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/heap1.png
new file mode 100644
index 0000000000000000000000000000000000000000..c540188a7dec0b5b756f76a6a4f899d2fb6bf3bd
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/heap1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/mcu-main.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/mcu-main.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5f42eb9479945f2da43bbac2366e2f1b779c635
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/mcu-main.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/ostick-config.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/ostick-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..39c0513a56407fa860e20500e0972bdde35a5d1d
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/ostick-config.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/path.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/path.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6f9214bd4f7764f19f56e8aafd324c01c4d7bcd
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/path.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/startS.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/startS.png
new file mode 100644
index 0000000000000000000000000000000000000000..476f74dd18ac2d8c9ad9a06bee7f191c9a7dd2e5
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/startS.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/systick.png b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/systick.png
new file mode 100644
index 0000000000000000000000000000000000000000..d814b51f896fea16f1bb1a0687d59ad13f810141
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-gcc-riscv/figures/systick.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/an0040-nano-port-iar.md b/rt-thread-version/rt-thread-nano/nano-port-iar/an0040-nano-port-iar.md
new file mode 100644
index 0000000000000000000000000000000000000000..7921007252365adf12742755da6cf1b09e90b345
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/nano-port-iar/an0040-nano-port-iar.md
@@ -0,0 +1,201 @@
+# 基于 IAR 移植 RT-Thread Nano
+
+本文介绍了如何基于 IAR 移植 RT-Thread Nano,并以一个 stm32f103 的基础工程作为示例进行讲解。
+
+移植 Nano 的主要步骤:
+
+1. 准备一个基础的 IAR 工程,并获取 RT-Thread Nano 压缩包源码。
+2. 在基础工程中添加 RT-Thread Nano 源码,添加相应头文件路径。
+3. 适配 Nano,主要从 中断、时钟、内存、应用 这几个方面进行适配,实现移植。
+4. 最后可对 Nano 进行配置:Nano 是可裁剪的,通过配置文件 rtconfig.h 实现对系统的裁剪。
+
+## 准备工作
+
+- 下载 RT-Thread Nano 发布版本代码。
+- 准备一份基础的裸机源码工程,如 LED 指示灯闪烁示例代码。
+
+### 下载 Nano 源码
+
+[点击此处](https://www.rt-thread.org/download/nano/rt-thread-3.1.3.zip) 下载 RT-Thread Nano 源码。
+
+### 基础工程准备 ###
+
+在移植 RT-Thread Nano 之前,我们需要准备一个能正常运行的裸机工程。作为示例,本文使用的是基于 STM32F103 的一个 LED 闪烁程序。程序的主要截图如下:
+
+
+
+在我们的例程中主要做了系统初始化与 LED 闪烁功能,编译下载程序后,就可以看到开发板上的 LED 在闪烁了。读者可以根据自己的需要使用的芯片,准备一个类似的裸机工程。
+
+## 添加 RT-Thread Nano 到工程 ##
+
+### 添加 Nano 源文件
+
+在准备好的 IAR 裸机工程下面新建 rtthread 文件夹,并在该文件中添加以下文件:
+
+- Nano 源码中的 include、libcpu、src 文件夹。
+- 配置文件:源码代码 rtthread/bsp 文件夹中的两个文件:`board.c` 与 `rtconfig.h`。
+
+
+
+双击打开 IAR 裸机工程,新建 rtthread 分组,并在该分组下添加以下源码:
+
+- 添加工程下 rtthread/src/ 文件夹中所有文件到工程;
+- 添加工程下 rtthread/libcpu/ 文件夹中相应内核的 CPU 移植文件及上下文切换文件: `cpuport.c` 以及 `context_iar.S`;
+- 添加 rtthread/ 文件夹下的 `board.c` 。
+
+
+
+Cortex-M 芯片内核移植代码:
+
+```
+context_iar.s
+cpuport.c
+```
+
+Kernel 文件包括:
+
+```
+clock.c
+components.c
+device.c
+idle.c
+ipc.c
+irq.c
+kservice.c
+mem.c
+object.c
+scheduler.c
+thread.c
+timer.c
+```
+
+板级配置代码:
+
+```
+board.c
+```
+
+### 添加头文件路径
+
+点击 `Project -> Options... ` 进入下图所示界面,添加 rtconfig.h 头文件所在位置的路径,添加 include 文件夹下的头文件路径。
+
+
+
+## 适配 RT-Thread Nano
+
+### 中断与异常处理
+
+RT-Thread 会接管异常处理函数 `HardFault_Handler()` 和悬挂处理函数 `PendSV_Handler()`,这两个函数已由 RT-Thread 实现,所以需要删除工程里中断服务例程文件中的这两个函数,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。
+
+### 系统时钟配置
+
+需要在 board.c 中实现 ` 系统时钟配置 `(为 MCU、外设提供工作时钟)与 `OS Tick 的配置 `(为操作系统提供心跳 / 节拍)。
+
+如下代码所示, `HAL_Init()` 初始化 HAL 库, `SystemClock_Config() `配置了系统时钟, `SystemCoreClockUpdate()` 对系统时钟进行更新,`_SysTick_Config()` 配置了 OS Tick。此处 OS Tick 使用滴答定时器 systick 实现,需要用户在 board.c 中实现 `SysTick_Handler()` 中断服务例程,调用 RT-Thread 提供的 `rt_tick_increase()` ,如下图所示。
+
+```c
+/* board.c */
+void rt_hw_board_init()
+{
+ HAL_Init();
+ SystemClock_Config();
+
+ /* System Clock Update */
+ SystemCoreClockUpdate();
+
+ /* System Tick Configuration */
+ _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
+
+ /* Call components board initial (use INIT_BOARD_EXPORT()) */
+#ifdef RT_USING_COMPONENTS_INIT
+ rt_components_board_init();
+#endif
+
+#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
+ rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
+#endif
+}
+```
+
+
+
+由于 `SysTick_Handler()` 中断服务例程由用户在 board.c 中重新实现,做了系统 OS Tick,所以还需要删除工程里中断服务例程文件中的 `SysTick_Handler()` ,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。
+
+### 内存堆初始化
+
+系统内存堆的初始化在 board.c 中的 rt_hw_board_init() 函数中完成,内存堆功能是否使用取决于宏 RT_USING_HEAP 是否开启,RT-Thread Nano 默认不开启内存堆功能,这样可以保持一个较小的体积,不用为内存堆开辟空间。
+
+开启系统 heap 将可以使用动态内存功能,如使用 rt_malloc、rt_free 以及各种系统动态创建对象的 API。若需要使用系统内存堆功能,则打开 RT_USING_HEAP 宏定义即可,此时内存堆初始化函数 rt_system_heap_init() 将被调用,如下所示:
+
+
+
+初始化内存堆需要堆的起始地址与结束地址这两个参数,系统中默认使用数组作为 heap,并获取了 heap 的起始地址与结束地址,该数组大小可手动更改,如下所示:
+
+
+
+注意:开启 heap 动态内存功能后,heap 默认值较小,在使用的时候需要改大,否则可能会有申请内存失败或者创建线程失败的情况,修改方法有以下两种:
+
+- 可以直接修改数组中定义的 RT_HEAP_SIZE 的大小,至少大于各个动态申请内存大小之和,但要小于芯片 RAM 总大小。
+- 也可以参考[《RT-Thread Nano 移植原理》——实现动态内存堆](../nano-port-principle/an0044-nano-port-principle.md) 章节进行修改,使用 RAM ZI 段结尾处作为 HEAP 的起始地址,使用 RAM 的结尾地址作为 HEAP 的结尾地址,这是 heap 能设置的最大值的方法。
+
+## 编写第一个应用
+
+移植好 RT-Thread Nano 之后,则可以开始编写第一个应用代码。此时 main() 函数就转变成 RT-Thread 操作系统的一个线程,现在可以在 main() 函数中实现第一个应用:板载 LED 指示灯闪烁,这里直接基于裸机 LED 指示灯进行修改。
+
+1. 首先在文件首部增加 RT-Thread 的相关头文件 `` 。
+2. 在 main() 函数中(也就是在 main 线程中)实现 LED 闪烁代码:初始化 LED 引脚、在循环中点亮 / 熄灭 LED。
+3. 将延时函数替换为 RT-Thread 提供的延时函数 rt_thread_mdelay()。该函数会引起系统调度,切换到其他线程运行,体现了线程实时性的特点。
+
+
+
+编译程序之后下载到芯片就可以看到基于 RT-Thread 的程序运行起来了,LED 正常闪烁。
+
+> [!NOTE]
+> 注:当添加 RT-Thread 之后,裸机中的 main() 函数会自动变成 RT-Thread 系统中 main 线程 的入口函数。由于线程不能一直独占 CPU,所以此时在 main() 中使用 while(1) 时,需要有让出 CPU 的动作,比如使用 `rt_thread_mdelay()` 系列的函数让出 CPU。
+
+**与裸机 LED 闪烁应用代码的不同**:
+
+1). 延时函数不同: RT-Thread 提供的 `rt_thread_mdelay()` 函数可以引起操作系统进行调度,当调用该函数进行延时时,本线程将不占用 CPU,调度器切换到系统的其他线程开始运行。而裸机的 delay 函数是一直占用 CPU 运行的。
+
+2). 初始化系统时钟的位置不同:移植好 RT-Thread Nano 之后,不需要再在 main() 中做相应的系统配置(如 hal 初始化、时钟初始化等),这是因为 RT-Thread 在系统启动时,已经做好了系统时钟初始化等的配置,这在上一小节 “系统时钟配置” 中有讲解。
+
+## 配置 RT-Thread Nano ##
+
+用户可以根据自己的需要通过打开或关闭 rtconfig.h 文件里面的宏定义,配置相应功能,如下是 rtconfig.h 的代码片段:
+
+```c
+...
+
+// IPC(Inter-process communication) Configuration
+// Using Semaphore
+// Using Semaphore
+#define RT_USING_SEMAPHORE
+//
+// Using Mutex
+// Using Mutex
+//#define RT_USING_MUTEX // 打开此宏则使能互斥量的使用
+//
+// Using Event
+// Using Event
+//#define RT_USING_EVENT // 打开此宏则使能事件集的使用
+//
+// Using MailBox
+// Using MailBox
+//#define RT_USING_MAILBOX // 打开此宏则使能邮箱的使用
+//
+// Using Message Queue
+// Using Message Queue
+//#define RT_USING_MESSAGEQUEUE // 打开此宏则使能消息队列的使用
+//
+//
+
+// Memory Management Configuration
+// Using Memory Pool Management
+// Using Memory Pool Management
+//#define RT_USING_MEMPOOL // 打开此宏则使能内存池的使用
+
+...
+```
+
+RT-Thread Nano 默认未开启宏 RT_USING_HEAP,故只支持静态方式创建任务及信号量。若要通过动态方式创建对象则需要在 rtconfig.h 文件里开启 RT_USING_HEAP 宏定义。完整配置详见 [《 RT-Thread Nano 配置》](../nano-config/an0043-nano-config.md)。
+
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/4-1.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/4-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..98e03edf65d7c0bd241bde128059c139213119ef
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/4-1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/add-nano.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/add-nano.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a2ff969e3647ca56d284b292e65f8909d0bfa9f
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/add-nano.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/h-file.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/h-file.png
new file mode 100644
index 0000000000000000000000000000000000000000..afbf00bbbfdb728fcd3dbbe8d1682c65a5e9587c
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/h-file.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/heap.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/heap.png
new file mode 100644
index 0000000000000000000000000000000000000000..683ba6f32aaaf3d07133cfa1e4ac8c4a7c187056
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/heap.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/heap1.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/heap1.png
new file mode 100644
index 0000000000000000000000000000000000000000..73d604a84b98adbeea9fbfea434d2a453b311049
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/heap1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/main.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/main.png
new file mode 100644
index 0000000000000000000000000000000000000000..e97feb89a96dc60e43d882b3e91e4b7ac4e7a5a9
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/main.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/nano-port-src.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/nano-port-src.png
new file mode 100644
index 0000000000000000000000000000000000000000..8173ee4c8e3e8094d22c1566895f705fbdbe62d1
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/nano-port-src.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/nano-src.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/nano-src.png
new file mode 100644
index 0000000000000000000000000000000000000000..b3399d753b715468570602a39aac0519d535a711
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/nano-src.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/rtos-main.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/rtos-main.png
new file mode 100644
index 0000000000000000000000000000000000000000..2caf9e82cb9f9803f9269cf7b8693c3143b017cd
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/rtos-main.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/sample.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/sample.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb4c627a7a7c219cfa5a8bfdc521c150c13ff156
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/sample.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-iar/figures/systick.png b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/systick.png
new file mode 100644
index 0000000000000000000000000000000000000000..9e53050e38139c03966e78065ca14d480415dd32
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-iar/figures/systick.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/an0039-nano-port-keil.md b/rt-thread-version/rt-thread-nano/nano-port-keil/an0039-nano-port-keil.md
new file mode 100644
index 0000000000000000000000000000000000000000..61ecdf0968dfbe5c47547e87420177c189e1e90a
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/nano-port-keil/an0039-nano-port-keil.md
@@ -0,0 +1,220 @@
+# 基于 Keil MDK 移植 RT-Thread Nano
+
+本文介绍如何基于 Keil MDK 移植 RT-Thread Nano ,并以一个 stm32f103 的基础工程作为示例进行讲解。
+
+RT-Thread Nano 已集成在 Keil MDK 中,可以直接在 IDE 中进行下载添加。本文档介绍了如何使用 MDK 移植 RT-Thread Nano,并以一个 stm32f103 的基础工程作为示例进行讲解。
+
+移植 Nano 的主要步骤:
+
+1. 准备一个基础的 keil MDK 工程,并获取 RT-Thread Nano pack 安装包并进行安装。
+2. 在基础工程中添加 RT-Thread Nano 源码。
+3. 适配 Nano,主要从 中断、时钟、内存这几个方面进行适配,实现移植。
+4. 验证移植结果:编写第一个应用代码,基于 RT-Thread Nano 闪烁 LED。
+5. 最后可对 Nano 进行配置:Nano 是可裁剪的,通过配置文件 rtconfig.h 实现对系统的裁剪。
+
+## 准备工作
+
+- 准备一份基础的裸机源码工程,如一份 stm32 的 LED 指示灯闪烁示例代码。
+- 在 KEIL 上安装 RT-Thread Nano Pack。
+
+### 基础工程准备
+
+在移植 RT-Thread Nano 之前,我们需要准备一个能正常运行的裸机工程。作为示例,本文使用的是基于 STM32F103 的一个 LED 闪烁程序。程序的主要截图如下:
+
+
+
+在我们的例程中主要做了系统初始化与 LED 闪烁功能,编译下载程序后,就可以看到 LED 闪烁了。读者可以根据自己的需要使用的芯片,准备一个类似的裸机工程。
+
+### Nano Pack 安装
+
+Nano Pack 可以通过在 Keil MDK IDE 内进行安装,也可以手动安装。下面开始介绍两种安装方式。
+
+#### 方法一:在 IDE 内安装
+
+打开 MDK 软件,点击工具栏的 Pack Installer 图标:
+
+
+
+点击右侧的 Pack,展开 Generic,可以找到 RealThread::RT-Thread,点击 Action 栏对应的 Install ,就可以在线安装 Nano Pack 了。另外,如果需要安装其他版本,则需要展开 RealThread::RT-Thread,进行选择。
+
+
+
+#### 方法二:手动安装
+
+我们也可以从官网下载安装文件,[RT-Thread Nano 离线安装包下载](https://download.rt-thread.org/download/mdk/RealThread.RT-Thread.3.1.3.pack),下载结束后双击文件进行安装:
+
+
+
+
+
+## 添加 RT-Thread Nano 到工程 ##
+
+打开已经准备好的可以运行的裸机程序,将 RT-Thread 添加到工程。如下图,点击 Manage Run-Time Environment。
+
+
+
+在 Manage Rum-Time Environment 里 "Software Component" 栏找到 RTOS,Variant 栏选择 RT-Thread,然后勾选 kernel,点击 "OK" 就添加 RT-Thread 内核到工程了。
+
+
+
+现在可以在 Project 看到 RT-Thread RTOS 已经添加进来了,展开 RTOS,可以看到添加到工程的文件:
+
+
+
+Cortex-M 芯片内核移植代码:
+
+```
+context_rvds.s
+cpuport.c
+```
+
+Kernel 文件包括:
+
+```
+clock.c
+components.c
+device.c
+idle.c
+ipc.c
+irq.c
+kservice.c
+mem.c
+object.c
+scheduler.c
+thread.c
+timer.c
+```
+
+配置文件:
+
+```
+board.c
+rtconfig.h
+```
+
+## 适配 RT-Thread Nano
+
+### 中断与异常处理
+
+RT-Thread 会接管异常处理函数 `HardFault_Handler()` 和悬挂处理函数 `PendSV_Handler()`,这两个函数已由 RT-Thread 实现,所以需要删除工程里中断服务例程文件中的这两个函数,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。
+
+### 系统时钟配置
+
+需要在 board.c 中实现 ` 系统时钟配置 `(为 MCU、外设提供工作时钟)与 `os tick 的配置 `(为操作系统提供心跳 / 节拍)。
+
+如下代码所示, `HAL_Init()` 初始化 HAL 库, `SystemClock_Config() `配置了系统时钟, `SystemCoreClockUpdate()` 对系统时钟进行更新,`_SysTick_Config()` 配置了 OS Tick。此处 OS Tick 使用滴答定时器 systick 实现,需要用户在 board.c 中实现 `SysTick_Handler()` 中断服务例程,调用 RT-Thread 提供的 `rt_tick_increase()` ,如下图所示。
+
+```c
+/* board.c */
+void rt_hw_board_init()
+{
+ HAL_Init();
+ SystemClock_Config();
+
+ /* System Clock Update */
+ SystemCoreClockUpdate();
+
+ /* System Tick Configuration */
+ _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
+
+ /* Call components board initial (use INIT_BOARD_EXPORT()) */
+#ifdef RT_USING_COMPONENTS_INIT
+ rt_components_board_init();
+#endif
+
+#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
+ rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
+#endif
+}
+```
+
+
+
+由于 `SysTick_Handler()` 中断服务例程由用户在 board.c 中重新实现,做了系统 OS Tick,所以还需要删除工程里中原本已经实现的 `SysTick_Handler()` ,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。
+
+### 内存堆初始化
+
+系统内存堆的初始化在 board.c 中的 rt_hw_board_init() 函数中完成,内存堆功能是否使用取决于宏 RT_USING_HEAP 是否开启,RT-Thread Nano 默认不开启内存堆功能,这样可以保持一个较小的体积,不用为内存堆开辟空间。
+
+开启系统 heap 将可以使用动态内存功能,如使用 rt_malloc、rt_free 以及各种系统动态创建对象的 API。若需要使用系统内存堆功能,则打开 RT_USING_HEAP 宏定义即可,此时内存堆初始化函数 rt_system_heap_init() 将被调用,如下所示:
+
+
+
+初始化内存堆需要堆的起始地址与结束地址这两个参数,系统中默认使用数组作为 heap,并获取了 heap 的起始地址与结束地址,该数组大小可手动更改,如下所示:
+
+
+
+注意:开启 heap 动态内存功能后,heap 默认值较小,在使用的时候需要改大,否则可能会有申请内存失败或者创建线程失败的情况,修改方法有以下两种:
+
+- 可以直接修改数组中定义的 RT_HEAP_SIZE 的大小,至少大于各个动态申请内存大小之和,但要小于芯片 RAM 总大小。
+- 也可以参考[《RT-Thread Nano 移植原理》——实现动态内存堆](../nano-port-principle/an0044-nano-port-principle.md) 章节进行修改,使用 RAM ZI 段结尾处作为 HEAP 的起始地址,使用 RAM 的结尾地址作为 HEAP 的结尾地址,这是 heap 能设置的最大值的方法。
+
+## 编写第一个应用
+
+移植好 RT-Thread Nano 之后,则可以开始编写第一个应用代码验证移植结果。此时 main() 函数就转变成 RT-Thread 操作系统的一个线程,现在可以在 main() 函数中实现第一个应用:板载 LED 指示灯闪烁,这里直接基于裸机 LED 指示灯进行修改。
+
+1. 首先在文件首部增加 RT-Thread 的相关头文件 `` 。
+2. 在 main() 函数中(也就是在 main 线程中)实现 LED 闪烁代码:初始化 LED 引脚、在循环中点亮 / 熄灭 LED。
+3. 将延时函数替换为 RT-Thread 提供的延时函数 rt_thread_mdelay()。该函数会引起系统调度,切换到其他线程运行,体现了线程实时性的特点。
+
+
+
+编译程序之后下载到芯片就可以看到基于 RT-Thread 的程序运行起来了,LED 正常闪烁。
+
+> [!NOTE]
+> 注:当添加 RT-Thread 之后,裸机中的 main() 函数会自动变成 RT-Thread 系统中 main 线程 的入口函数。由于线程不能一直独占 CPU,所以此时在 main() 中使用 while(1) 时,需要有让出 CPU 的动作,比如使用 `rt_thread_mdelay()` 系列的函数让出 CPU。
+
+**与裸机 LED 闪烁应用代码的不同**:
+
+1). 延时函数不同: RT-Thread 提供的 `rt_thread_mdelay()` 函数可以引起操作系统进行调度,当调用该函数进行延时时,本线程将不占用 CPU,调度器切换到系统的其他线程开始运行。而裸机的 delay 函数是一直占用 CPU 运行的。
+
+2). 初始化系统时钟的位置不同:移植好 RT-Thread Nano 之后,不需要再在 main() 中做相应的系统配置(如 hal 初始化、时钟初始化等),这是因为 RT-Thread 在系统启动时,已经做好了系统时钟初始化等的配置,这在上一小节 “系统时钟配置” 中有讲解。
+
+## 配置 RT-Thread Nano ##
+
+用户可以根据自己的需要通过修改 rtconfig.h 文件里面的宏定义配置相应功能。
+
+RT-Thread Nano 默认未开启宏 RT_USING_HEAP,故只支持静态方式创建任务及信号量。若要通过动态方式创建对象则需要在 rtconfig.h 文件里开启 RT_USING_HEAP 宏定义。
+
+MDK 的配置向导 configuration Wizard 可以很方便的对工程进行配置,Value 一栏可以选中对应功能及修改相关值,等同于直接修改配置文件 rtconfig.h。更多细节配置详见 [《 RT-Thread Nano 配置》](../nano-config/an0043-nano-config.md)。
+
+
+
+## 获取示例代码
+
+Keil MDK 中集成的 RT-Thread Nano 软件包附带示例代码,如果需要参照示例代码,则可以在 Keil 中打开相应的示例代码工程。
+
+首先点击 Pack Installer,进入下图所示界面:
+
+
+
+右侧界面切换到 Examples,然后在左侧界面搜索 Device 或者 Boards,点击搜索出的芯片或者开发板,会显示与其相关的所有示例代码,同时可以看到 RT-Thread 的示例代码也在其中,点击 Copy,选择一个路径,然后点击 OK 即可打开示例代码工程。
+
+
+
+## 常见问题
+
+### Q: 如何升级 pack?
+
+**A**: Pack 升级步骤基本如同软件包,展开 RealThread::RT-Thread 后,选择比较新的 Nano 版本,点击 Install 进行安装。如下图所示,点击红色框中的 Install 进行升级,即可将 3.1.2 版本升级到 3.1.3。
+
+> 需要注意的是,若多个版本同时安装,则最终向工程添加 Nano 时,只能选择高版本进行添加。
+
+
+
+### Q: 在安装 pack 后,未找到可选的 RT-Thread pack。
+
+**A**: 点击下图中的下拉小三角即可找到 RT-Thread,选择 RT-Thread 即可。
+
+
+
+### Q: 在添加 Nano 时,选择 shell 后,编译报错。
+
+**A**: 报错 "Undefined symbol rt_hw_console_getchar (referrred from shell.o)"。这是由于添加 FinSH 组件源码之后,还需要自行定义与实现该函数才能完成 FinSH 的移植,详见 [《在 RT-Thread Nano 上添加控制台与 FinSH》](../finsh-port/an0045-finsh-port.md)。
+
+
+
+
+
+
+
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-1.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..bb65fa0a614c71c33d2c8cc2c640fd39c92bb861
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-2.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c420dbd0a9049f61800b969535d86a2f7de272a
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-2.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-3.jpg b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-3.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..bc05debb4971d1be2920d65f3b2dcb813f640f70
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-3.jpg differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-5.jpg b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-5.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..62a6ec89c58212b4e8ff80ef5b099387f3830043
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/3-5.jpg differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/4-1.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/4-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..eba9746543da0527a1b0d270705f498bf24e0480
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/4-1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/6-2.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/6-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..9be4c302e3ae1a748a5dfad858db0016c26f89eb
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/6-2.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/heap1.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/heap1.png
new file mode 100644
index 0000000000000000000000000000000000000000..88280781fcacf92aff963064215cca73895eced7
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/heap1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/keil-samples.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/keil-samples.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f93aef21b8c0bc73a793ba941f9228afacf7f27
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/keil-samples.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/keil_pack.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/keil_pack.png
new file mode 100644
index 0000000000000000000000000000000000000000..03b903fc426fdf1ca9e16547056c12bc059a5dd0
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/keil_pack.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/pack-upgrade.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/pack-upgrade.png
new file mode 100644
index 0000000000000000000000000000000000000000..7886d8116de67f7686af3aeffb825d4acb655bd1
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/pack-upgrade.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs-install-man.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs-install-man.png
new file mode 100644
index 0000000000000000000000000000000000000000..25118bae6baaca440e887d81ee2852bb5765c6f3
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs-install-man.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs-install.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs-install.png
new file mode 100644
index 0000000000000000000000000000000000000000..279bd93ae0d9edfa7487915ed8b18f71ead7d412
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs-install.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs.png
new file mode 100644
index 0000000000000000000000000000000000000000..d287c52c1446e1eae44659b4bf8049e6a8f6d07b
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/packs.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/project-eg.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/project-eg.png
new file mode 100644
index 0000000000000000000000000000000000000000..79753375a7627ba4e5c7be9425081e51891b99b4
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/project-eg.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtconfig.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..25f7523d0e5b6c7b2b62d045012ab86b3cce1cf8
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtconfig.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtos-main.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtos-main.png
new file mode 100644
index 0000000000000000000000000000000000000000..150edbf261e76c9327200e52d0aa6d530c99fbdd
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtos-main.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtos2.jpg b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtos2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..aa603a6530b714f002f3f53af29b435fd46eed0a
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/rtos2.jpg differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-keil/figures/systick.png b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/systick.png
new file mode 100644
index 0000000000000000000000000000000000000000..b456a1a9d9d204e7861653517286306335e29e4a
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-keil/figures/systick.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-principle/an0044-nano-port-principle.md b/rt-thread-version/rt-thread-nano/nano-port-principle/an0044-nano-port-principle.md
new file mode 100644
index 0000000000000000000000000000000000000000..8a55b25526fa3e74dcd206db46c4ad3b61b6adc2
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/nano-port-principle/an0044-nano-port-principle.md
@@ -0,0 +1,315 @@
+# RT-Thread Nano 移植原理
+
+本片文档介绍 Nano 移植原理,针对的是不同 MCU 的移植,如 Cortex M,RISC-V,或者是其他 MCU 的移植。移植过程主要分为两个部分:libcpu 移植与板级移植,在讲解移植之前,本文档对 RT-Thread Nano 的启动流程与移植目录结构先进行说明。
+
+## 启动流程
+
+RT-Thread 启动流程如下所示,在图中标出颜色的部分需要用户特别注意(黄色表示 libcpu 移植相关的内容,绿色部分表示板级移植相关的内容)。
+
+
+
+RT-Thread 启动代码统一入口为 `rtthread_startup() ` ,芯片启动文件在完成必要工作(如初始化时钟、配置中断向量表、初始化堆栈等)后,最终会在程序跳转时,跳转至 RT-Thread 的启动入口中。RT-Thread 的启动流程如下:
+
+1. 全局关中断,初始化与系统相关的硬件。
+2. 打印系统版本信息,初始化系统内核对象(如定时器、调度器)。
+3. 初始化用户 main 线程(同时会初始化线程栈),在 main 线程中对各类模块依次进行初始化。
+4. 初始化软件定时器线程、初始化空闲线程。
+5. 启动调度器,系统切换到第一个线程开始运行(如 main 线程),并打开全局中断。
+
+## 移植目录结构
+
+在 `rtthread-nano` 源码中,与移植相关的文件位于下图中有颜色标记的路径下(黄色表示 libcpu 移植相关的文件,绿色部分表示板级移植相关的文件):
+
+
+
+## libcpu 移植
+
+RT-Thread 的 libcpu 抽象层向下提供了一套统一的 CPU 架构移植接口,这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、Cache 等等内容,RT-Thread 支持的 cpu 架构在源码的 `libcpu` 文件夹下。
+
+### 启动文件 startup.s
+
+启动文件由芯片厂商提供,位于芯片固件库中。每款芯片都有相对应的启动文件,在不同开发环境下启动文件也不相同。当系统加入 RT-Thread 之后,会将 RT-Thread 的启动放在调用 main() 函数之前,如下图所示:
+
+
+
+startup.s:主要完成初始化时钟、配置中断向量表;完成全局 / 静态变量的初始化工作;初始化堆栈;库函数的初始化;程序的跳转等内容。
+
+程序跳转:芯片在 KEIL MDK 与 IAR 下的启动文件不用做修改,会自动转到 RT-Thread 系统启动函数 `rtthread_startup()` 。GCC 下的启动文件需要修改,让其跳转到 RT-Thread 提供的 entry() 函数,其中 entry() 函数调用了 RT-Thread 系统启动函数 `rtthread_startup()`。
+
+**举例**: stm32 在 GCC 开发环境下的启动文件,修改 GCC 启动文件,使其跳转到 entry 函数。以下是启动文件的代码片段:
+
+```c
+//修改前:
+ bl SystemInit
+ bl main
+
+//修改后:
+ bl SystemInit
+ bl entry /* 修改此处,由 main 改为 entry */
+```
+
+RT-Thread 在 entry 函数中实现了 GCC 环境下的 RT-Thread 启动:
+
+```c
+int entry(void)
+{
+ rtthread_startup();
+ return 0;
+}
+```
+
+最终调用 main() 函数进入用户 main()。
+
+### 上下文切换 context_xx.s
+
+上下文切换表示 CPU 从一个线程切换到另一个线程、或者线程与中断之间的切换等。在上下文切换过程中,CPU 一般会停止处理当前运行的代码,并保存当前程序运行的具体位置以便之后继续运行。
+
+在该文件中除了实现上下文切换的函数外,还需完成全局开关中断函数,详见编程指南 [《内核移植》 - CPU 架构移植](../../../programming-manual/porting/porting/) 章节中的 “实现全局开关中断 ” 小节与 “实现上下文切换” 小节。
+
+| 需实现的函数 | 描述 |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| rt_base_t rt_hw_interrupt_disable(void); | 关闭全局中断 |
+| void rt_hw_interrupt_enable(rt_base_t level); | 打开全局中断 |
+| void rt_hw_context_switch_to(rt_uint32 to); | 没有来源线程的上下文切换,在调度器启动第一个线程的时候调用,以及在 signal 里面会调用 |
+| void rt_hw_context_switch(rt_uint32 from, rt_uint32 to); | 从 from 线程切换到 to 线程,用于线程和线程之间的切换 |
+| void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to); | 从 from 线程切换到 to 线程,用于中断里面进行切换的时候使用 |
+
+注意:在 Cortex-M 中,PendSV 中断处理函数是 PendSV_Handler(),线程切换的实际工作在 PendSV_Handler() 里完成。
+
+### 线程栈初始化 cpuport.c
+
+在 RT-Thread 中,线程具有独立的栈,当进行线程切换时,会将当前线程的上下文存在栈中,当线程要恢复运行时,再从栈中读取上下文信息,进行恢复。
+
+故障异常处理函数 rt_hw_hard_fault_exception(),在发生硬件错误时,执行 HardFault_Handler 中断,会执行该函数。
+
+该文件中主要实现线程栈的初始化 rt_hw_stack_init() 与 hard fault 异常处理函数,线程栈初始化函数的参数以及实现的步骤详见编程指南 [《内核移植》 - CPU 架构移植](../../../programming-manual/porting/porting/) 章节中的 ”实现线程栈初始化“ 小节。
+
+| 需实现的函数 | 描述 |
+| ---------------------------- | ---------------------- |
+| rt_hw_stack_init() | 实现线程栈的初始化 |
+| rt_hw_hard_fault_exception() | 异常函数:系统硬件错误 |
+
+### 中断与异常挂接 interrupt.c
+
+> [!NOTE]
+> 注:注意:在 Cortex-M 内核上,所有中断都采用中断向量表的方式进行处理,即当一个中断触发时,处理器将直接判定是哪个中断源,然后直接跳转到相应的固定位置进行处理,不需要再自行实现中断管理。
+
+在一些非 Cortex-M 架构中,系统没有实现类似中断向量表的功能,物理中断要和用户的中断服务例程相关联,就需要使用中断管理接口对中断进行管理,这样当发生中断时就可以触发相应的中断,执行中断服务例程。
+
+详见编程指南 [《中断管理》](../../../programming-manual/interrupt/interrupt.md) 章节。
+
+| 需实现的中断管理接口 | 描述 |
+| ------------------------- | ------------------ |
+| rt_hw_interrupt_init() | 硬件中断初始化 |
+| rt_hw_interrupt_install() | 中断服务程序挂接 |
+| rt_hw_interrupt_mask() | 屏蔽指定的中断源 |
+| rt_hw_interrupt_umask() | 打开被屏蔽的中断源 |
+
+## 板级移植 board.c
+
+> [!NOTE]
+> 注:board.c、rtconfig.h 是与硬件 / 板级相关的文件,在移植时需自行实现。Cortex M 架构可参考 Nano 源码 bsp 文件夹中已有的的 board.c、rtconfig.h 。
+
+板级移植主要是针对 `rt_hw_board_init()` 函数内容的实现,该函数在板级配置文件 board.c 中,函数中做了许多系统启动必要的工作,其中包含:
+
+1. 配置系统时钟。
+2. 实现 OS 节拍。
+3. 初始化外设:如 GPIO/UART 等等。
+4. 初始化系统内存堆,实现动态堆内存管理。
+5. 板级自动初始化,使用 INIT_BOARD_EXPORT() 自动初始化的函数会在此处被初始化。
+6. 其他必要的初始化,如 MMU 配置(需要时请自行在 rt_hw_board_init 函数中调用应用函数实现)。
+
+```c
+/* board.c */
+void rt_hw_board_init(void)
+{
+ /* System Clock Update */
+ SystemCoreClockUpdate();
+
+ /* System Tick Configuration */
+ _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
+
+#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
+ rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
+#endif
+
+ /* Call components board initial (use INIT_BOARD_EXPORT()) */
+#ifdef RT_USING_COMPONENTS_INIT
+ rt_components_board_init();
+#endif
+}
+```
+
+### 配置系统时钟
+
+系统时钟是给各个硬件模块提供工作时钟的基础,一般在 `rt_hw_board_init()` 函数中完成,可以调用库函数实现配置,也可以自行实现。
+
+如下是 stm32 配置系统时钟调用示例(调用库函数 SystemCoreClockUpdate()):
+
+```c
+/* board.c */
+void rt_hw_board_init()
+{
+ SystemCoreClockUpdate(); // 在无库函数使用时,一般使用 rt_hw_clock_init() 配置,函数名不做要求,函数自行实现
+ ...
+}
+```
+
+### 实现 OS 节拍
+
+OS 节拍也叫时钟节拍或 OS tick。任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件。
+
+**时钟节拍的实现**:通过硬件 timer 实现周期性中断,在定时器中断中调用 `rt_tick_increase()` 函数实现全局变量 rt_tick 自加,从而实现时钟节拍。一般地,在 Cortex M 上直接使用内部的滴答定时器 Systick 实现。
+
+**示例**:如下是 stm32 配置 OS 节拍示例,在初始化时钟节拍后,直接在 SysTick_Handler() 中断服务例程中调用 `rt_tick_increase()`。
+
+```c
+/* board.c */
+void rt_hw_board_init()
+{
+ ...
+ _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND); // 使用 SysTick 实现时钟节拍
+ ...
+}
+
+/* 中断服务例程 */
+void SysTick_Handler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ rt_tick_increase();
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+```
+
+对于使用了 RT-Thread 中断管理的 CPU 架构,中断服务例程需要通过 `rt_hw_interrupt_install()` 进行装载(关于中断及其装载,详见本文档的” 中断管理 “ 小节),如下示例:
+
+```c
+/* board.c */
+void rt_hw_board_init()
+{
+ ...
+ rt_hw_timer_init(); // 使用 硬件定时器 实现时钟节拍,一般命名为 rt_hw_timer_init()
+ ...
+}
+
+int rt_hw_timer_init(void) // 函数自行实现,并需要装载中断服务例程
+{
+ ...
+ rt_hw_interrupt_install(IRQ_PBA8_TIMER2_3, rt_hw_timer_isr, RT_NULL, "tick");
+ rt_hw_interrupt_umask(IRQ_PBA8_TIMER2_3);
+}
+
+/* 中断服务例程 */
+static void rt_hw_timer_isr(int vector, void *param)
+{
+ rt_interrupt_enter();
+ rt_tick_increase();
+ rt_interrupt_leave();
+}
+```
+
+> [!NOTE]
+> 注:在初始化时钟节拍的时候,会用到宏 `RT_TICK_PER_SECOND`。通过修改该宏的值,可以修改系统中一个时钟节拍的时间长度。
+
+### 硬件外设初始化
+
+硬件初始化,如 UART 初始化等(对接控制台),需要在 rt_hw_board_init() 函数中手动调用 UART 初始化函数。
+
+```c
+/* board.c */
+void rt_hw_board_init(void)
+{
+ ....
+ uart_init();
+ ....
+}
+```
+
+### 实现动态内存堆
+
+RT-Thread Nano 默认不开启动态内存堆功能,开启 RT_USING_HEAP 将可以使用动态内存功能,即可以使用 rt_malloc、rt_free 以及各种系统动态创建对象的 API。动态内存堆管理功能的初始化是通过 rt_system_heap_init() 函数完成的,动态内存堆的初始化需要指定堆内存的起始地址和结束地址,函数原型如下:
+
+```c
+void rt_system_heap_init(void *begin_addr, void *end_addr)
+```
+
+开启 RT_USING_HEAP 后,系统默认使用数组作为 heap,heap 的起始地址与结束地址作为参数传入 heap 初始化函数,heap 初始化函数 rt_system_heap_init() 将在 rt_hw_board_init() 中被调用。
+
+开启 heap 后,系统中默认使用数组作为 heap(heap 默认较小,实际使用时请根据芯片 RAM 情况改大),获得的 heap 的起始地址与结束地址,作为参数传入 heap 初始化函数:
+
+```c
+#define RT_HEAP_SIZE 1024
+static uint32_t rt_heap[RT_HEAP_SIZE];
+RT_WEAK void *rt_heap_begin_get(void)
+{
+ return rt_heap;
+}
+
+RT_WEAK void *rt_heap_end_get(void)
+{
+ return rt_heap + RT_HEAP_SIZE;
+}
+
+void rt_hw_board_init(void)
+{
+ ....
+#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
+ rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get()); //传入 heap 的起始地址与结束地址
+#endif
+ ....
+}
+```
+
+如果不想使用数组作为动态内存堆,则可以重新指定系统 HEAP 的大小,例如使用 RAM ZI 段结尾处作为 HEAP 的起始地址(这里需检查与链接脚本是否对应),使用 RAM 的结尾地址作为 HEAP 的结尾地址,这样可以将空余RAM 全部作为动态内存 heap 使用。如下示例重新定义了 HEAP 的起始地址与结尾地址,并作为初始化参数进行系统 HEAP 初始化。
+
+```c
+#define STM32_SRAM1_START (0x20000000)
+#define STM32_SRAM1_END (STM32_SRAM1_START + 20 * 1024) // 结束地址 = 0x20000000(基址) + 20K(RAM大小)
+
+#if defined(__CC_ARM) || defined(__CLANG_ARM)
+extern int Image$$RW_IRAM1$$ZI$$Limit; // RW_IRAM1,需与链接脚本中运行时域名相对应
+#define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
+#endif
+
+#define HEAP_END STM32_SRAM1_END
+```
+
+```c
+void rt_hw_board_init(void)
+{
+ ....
+#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
+ rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
+#endif
+ ....
+}
+```
+
+#### 链接脚本
+
+链接脚本,也称分散加载文件,决定在生成 image 文件时如何来分配相关数据的存放基址,如果不指定特定的链接脚本,连接器就会自动采用默认的链接脚本来生成镜像。
+
+举例 stm32 在 KEIL MDK 开发环境下的链接脚本文件 xxx.sct:
+
+```c
+LR_IROM1 0x08000000 0x00020000 { ; load region size_region
+ ER_IROM1 0x08000000 0x00020000 { ; load address = execution address
+ *.o (RESET, +First)
+ *(InRoot$$Sections)
+ .ANY (+RO)
+ }
+ RW_IRAM1 0x20000000 0x00005000 { ; RW data
+ .ANY (+RW +ZI)
+ }
+}
+```
+
+其中 `RW_IRAM1 0x20000000 0x00005000` 表示定义一个运行时域 RW_IRAM1(默认域名),域基址为 0x20000000,域大小为 0x00005000(即 20K ),对应实际 RAM 大小。`.ANY (+RW +ZI)` 表示加载所有匹配目标文件的可读写数据 RW-Data、清零数据 ZI-Data。所以运行时所占内存的结尾处就是 ZI 段结尾处,可以将 ZI 结尾处之后的内存空间作为系统动态内存堆使用。
+
+
+
+
diff --git a/rt-thread-version/rt-thread-nano/nano-port-principle/figures/low_level.png b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/low_level.png
new file mode 100644
index 0000000000000000000000000000000000000000..deee294493e2f2fff89da9df01fc2b1e2bc74694
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/low_level.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-principle/figures/mcu-rtt-start.png b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/mcu-rtt-start.png
new file mode 100644
index 0000000000000000000000000000000000000000..5150a51259995059940d2d6c0915d93f5f7319e4
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/mcu-rtt-start.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-principle/figures/mcu-start.png b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/mcu-start.png
new file mode 100644
index 0000000000000000000000000000000000000000..5a267934989a59c38c8dfacc3ebd990a2a7367b5
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/mcu-start.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-principle/figures/nano-file.png b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/nano-file.png
new file mode 100644
index 0000000000000000000000000000000000000000..0cefad7b0a5b1e467013580468be766b4160f981
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/nano-file.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-principle/figures/startup.png b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/startup.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e1296038c402789116ca57f7ce9d8c0e7d35a76
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/startup.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-principle/figures/startup1.png b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/startup1.png
new file mode 100644
index 0000000000000000000000000000000000000000..6879750f929ebe01a3f858d7cb97e5557857c888
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-principle/figures/startup1.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/an0047-nano-port-studio.md b/rt-thread-version/rt-thread-nano/nano-port-studio/an0047-nano-port-studio.md
new file mode 100644
index 0000000000000000000000000000000000000000..14736d99e3d8f5a32470d71db39fc4cb85b1c7e9
--- /dev/null
+++ b/rt-thread-version/rt-thread-nano/nano-port-studio/an0047-nano-port-studio.md
@@ -0,0 +1,45 @@
+# 在 RT-Thread Studio 上使用 RT-Thread Nano
+
+本文介绍了如何在 RT-Thread Studio 上使用 RT-Thread Nano,并以创建 stm32f103RB 的 Nano 工程为例。
+
+## 准备工作
+
+[安装 RT-Thread Studio](https://www.rt-thread.org/page/studio.html)。
+
+## 新建 Nano 工程
+
+打开 IDE,点击【文件】-【新建】-【RT-Thread 项目】:
+
+
+
+进入新建工程的配置向导:
+
+
+
+注:可以通过修改 board.c 的 `SystemClock_Config()` 更改系统时钟。
+
+工程创建完毕,连接硬件,可直接进行编译下载,如下所示:
+
+
+
+由于在创建工程向导中配置了控制台串口号及其引脚号,所以工程中已经实现了 uart 的驱动以及 `rt_hw_console_output()` ,默认可以进行打印。打开串口终端,可以发现在终端中执行了打印。
+
+
+
+## 基于 Nano 添加 FinSH
+
+双击 `RT-Thread Settings` 进入配置,打开组件,勾选 FinSH Shell,保存配置。此操作将把 FinSH 组件的源码加入工程中。
+
+其中,`rt_hw_console_getchar()` 已经在 drv_uart.c 中实现,无需再实现对接 FinSH 的代码。
+
+
+
+链接硬件,编译下载后,在串口终端中按下 Tab 键,可查看系统中的命令:
+
+
+
+## 常见问题
+
+### Q:如何修改系统时钟?
+
+A: 可以通过修改 board.c 的 `SystemClock_Config()` 更改系统时钟。
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/figures/config-guide.png b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/config-guide.png
new file mode 100644
index 0000000000000000000000000000000000000000..f2e6ef7a83f7551d9e05cc755a4c786c74a0fe6c
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/config-guide.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/figures/creat-proj.png b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/creat-proj.png
new file mode 100644
index 0000000000000000000000000000000000000000..8eb1da5696132e803d55601fc3c5301c3d03bc4e
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/creat-proj.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/figures/finsh-config.png b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/finsh-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d2d76b3854fcd51fcc10612af9dadfbc6a9342c
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/finsh-config.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/figures/finsh.png b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/finsh.png
new file mode 100644
index 0000000000000000000000000000000000000000..93e83197e5dd9678b41e01c8d091286611bcd526
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/finsh.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/figures/nano-proj.png b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/nano-proj.png
new file mode 100644
index 0000000000000000000000000000000000000000..d1e3fec6ed05350b4d722094fd2b5b0149a19109
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/nano-proj.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/figures/proj-guide.png b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/proj-guide.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea3f31939e50956a194c93de1f8ada45db62960c
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/proj-guide.png differ
diff --git a/rt-thread-version/rt-thread-nano/nano-port-studio/figures/rt_kprintf.png b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/rt_kprintf.png
new file mode 100644
index 0000000000000000000000000000000000000000..26267374c3c6272018bd6a1eb112bd8d9d40adff
Binary files /dev/null and b/rt-thread-version/rt-thread-nano/nano-port-studio/figures/rt_kprintf.png differ
diff --git a/rt-thread-version/rt-thread-smart/_navbar.md b/rt-thread-version/rt-thread-smart/_navbar.md
new file mode 100644
index 0000000000000000000000000000000000000000..559c01e680e85979b8da20409a893fc5b20f8660
--- /dev/null
+++ b/rt-thread-version/rt-thread-smart/_navbar.md
@@ -0,0 +1,5 @@
+
+
+
diff --git a/rt-thread-version/rt-thread-smart/_sidebar.md b/rt-thread-version/rt-thread-smart/_sidebar.md
new file mode 100644
index 0000000000000000000000000000000000000000..f9c2ca61896b536c04f174a4c7728bb4f0f23ff3
--- /dev/null
+++ b/rt-thread-version/rt-thread-smart/_sidebar.md
@@ -0,0 +1,6 @@
+
+
+- RT-Thread Smart版本
+
+ - [入门指南](/rt-thread-version/rt-thread-smart/rt-smart-quickstart/rt-smart-quickstart.md)
+ - [架构说明](/rt-thread-version/rt-thread-smart/architecture/architecture.md)
diff --git a/rt-thread-version/rt-thread-smart/architecture/architecture.md b/rt-thread-version/rt-thread-smart/architecture/architecture.md
new file mode 100644
index 0000000000000000000000000000000000000000..5c10aceec7158a9a9806480c9a6e092d76d0d598
--- /dev/null
+++ b/rt-thread-version/rt-thread-smart/architecture/architecture.md
@@ -0,0 +1,225 @@
+# RT-Thread Smart 架构说明
+
+## RT-Thread Smart 的架构
+
+RT-Thread Smart 是基于 RT-Thread 操作系统上的混合操作系统,简称为 rt-smart,它把应用从内核中独立出来,形成独立的用户态应用程序,并具备独立的地址空间(32 位系统上是 4G 的独立地址空间)。
+
+以下是 rt-smart 的整体结构框图,在硬件平台的基础上通过 MMU、系统调用的方式把整个系统分成了内核态及用户态。
+
+
+
+RT-Thread Smart 的核心实现是 lwP,进程管理模块( `rtthread-smart/kernel/components/lwp` )。它包括了如下的几个部分:
+
+* 面向用户态的系统调用(system call,`lwp_syscall.c/h`);
+* 用户态进程管理(`lwp_pid.c/h, lwp.c/h`);
+* elf 可执行应用程序加载器;
+* 基于 MMU 的虚拟内存管理,地址空间管理;
+* 进程间的 channel 通信机制及共享内存机制;
+
+而在整体操作系统中,内核中还额外的包括了(原 RT-Thread 操作系统上的):
+
+* 文件系统接口(DFS) - 虚拟文件系统接口;
+* BSD socket 接口(SAL/socket) - 抽象套接字;
+* 设备驱动框架接口;
+* 可选的设备驱动(如 UART,GPIO,IIC 等);
+
+## 用户态环境
+
+用户态应用是一份 elf(Executable Linkable Format)文件,由 GNU GCC 编译链接而产生。在 RT-Thread Smart 中,它被固定加载到虚拟地址 0x100000 处执行。
+
+一般来说,应用程序具备自己独立的 3G 地址空间,而高 1G 地址空间则留给了内核。对于一个 32 位芯片来说,典型的 RT-Thread Smart 应用程序及内核空间的内存分布如下图所示:
+
+
+
+RT-Thread Smart 的用户态是固定地址方式运行,当需要系统服务时通过系统调用的方式陷入到内核中。用户态应用环境也存在对应的 API 环境:
+
+* libc,rt-smart 选择的是 [musl libc](https://musl.libc.org)(在内核中目前也是 musl libc)。它提供了常规意义上的 POSIX 接口调用及 C 运行环境;
+* 原 RT-Thread API 环境,也称为 RT-Thread CRT 环境。在这套 API 中,具备了原 RT-Thread 的 API 接口,例如 rt_thread_create,rt_malloc 等。一些原 RT-Thread 软件包,应用程序也非常方便的移植到 rt-smart 用户态环境中执行。同样的,因为 RT-Thread 内核中也存在一套 POSIX 环境,所以一些用户态应用也可以经过重新编译的方式,和内核编译在一起,从而在内核中执行。
+
+## 基本的 IPC 客户端与服务端
+
+IPC 服务(`rtthread-smart/kernel/components/lwp/lwp_ipc.c/h`)是实现用户应用程序和其他服务的桥梁,同时也可以是用户进程与用户进程之间的通信机制、内核与用户进程之间的通信机制。
+
+在使用 IPC 服务时,需要先创建出对应的通道(channel),然后在通道上进行数据收发。一个 IPC 通道是一个双向数据传递的软件抽象,数据收发过程包括如下几种操作:
+
+| 函数名 | 说明 |
+| --- | --- |
+| rt_channel_send | 向指定的 IPC channel 发送消息,当接收任务从 channel 中取走消息,并处理完毕后返回 |
+| rt_channel_send_recv | 向指定的 IPC channel 发送消息,同时等待对端回复相应的消息 |
+| rt_channel_notify | 向指定的 IPC channel 发送消息,并且不管后续情况直接返回 |
+| rt_channel_recv | 从指定的 IPC channel 接收消息,直到接收到消息然后返回 |
+
+以下是一份最基本的客户端与服务端,他们之间通过 IPC channel 的方式进行交互。先看 pong 服务端的代码情况,它实现的是一个消息接收,当收到消息数据时,再返回给客户端。
+
+```c
+#include
+/* 使用 IPC 服务,需要包含 lwp.h 头文件 */
+#include
+/* 包含 rtthread.h,可以使用原来的 RT-Thread API */
+#include
+
+int main(int argc, char **argv)
+{
+ int i;
+ int channel;
+ char *str;
+ int shmid; /* 数据传输的共享内存块 ID */
+ struct rt_channel_msg msg_text;
+
+ /* 创建一个名字是 pong 的通道 */
+ channel = rt_channel_open("pong", O_CREAT);
+ if (channel == -1)
+ {
+ printf("Error: channel_open: fail to create the IPC channel for pong!\n");
+ return -1;
+ }
+ /* 输出等待的通道号 */
+ printf("\nPong: wait on the IPC channel: %d\n", channel);
+
+ /* 接收 100 次消息,然后退出 */
+ for (i = 0; i < 100; i++)
+ {
+ /* 从 pong 通道中接收消息,消息会放置在 msg_text 中 */
+ rt_channel_recv(channel, &msg_text);
+
+ /* 对应的共享内存 id */
+ shmid = (int)msg_text.u.d;
+ /* 通过共享内存 id,拿到对应的数据块 */
+ if (shmid < 0 || !(str = (char *)lwp_shmat(shmid, NULL)))
+ {
+ /* 接收错误,恢复错误信息 */
+ msg_text.u.d = (void *)-1;
+ printf("Pong: receive an invalid data.\n");
+ rt_channel_reply(channel, &msg_text);
+ continue;
+ }
+
+ /* 在终端上输出接收到的字符串 */
+ printf("Pong: receive %s\n", str);
+ /* 脱离数据块,代表本端不再使用这块数据 */
+ lwp_shmdt(str);
+
+ /* 准备回复信息 */
+ printf("Pong: reply count = %d\n", i);
+ msg_text.type = RT_CHANNEL_RAW;
+ msg_text.u.d = (void *)i;
+ /* 恢复给发送端 */
+ rt_channel_reply(channel, &msg_text);
+ }
+
+ /* 关闭这个通道 */
+ rt_channel_close(channel);
+
+ return 0;
+}
+```
+
+以下是 ping 客户端的代码,它会把一段字符串数据发送给 pong 进程。它会先准备这段字符串数据,然后从共享内存中创建出一块共享内存数据块,并把数据复制到共享内存数据块上,然后把共享内存数据 id 通过通道发送给 pong 进程:
+
+```c
+#include
+#include
+
+/* 使用 IPC 服务,需要包含 lwp.h 头文件 */
+#include
+#include
+
+/*
+ * 因为需要把一段数据发送到接收端,而这段数据是在自己的进程空间中,所以
+ * 需要以共享内存的方式,把数据放到共享内存上,然后把对应的 id 发送到接收
+ * 端。这个过程中包括了开辟对应的共享内存页,复制数据,并返回对应的共享
+ * 内存 id。
+ */
+rt_inline int prepare_data(void *data, size_t len)
+{
+ int shmid;
+ void *shm_vaddr;
+
+ /* 以当前的任务 ID 来做为共享内存标识的 key */
+ size_t key = (size_t) rt_thread_self();
+
+ /* 创建新的共享内存,并返回 id */
+ shmid = lwp_shmget(key, len, 1);
+ if (shmid == -1)
+ {
+ printf("Fail to allocate a shared memory!\n");
+ return -1;
+ }
+
+ /* 通过 id 获得共享内存数据块映射在本进程空间的地址 */
+ shm_vaddr = lwp_shmat(shmid, NULL);
+ if (shm_vaddr == RT_NULL)
+ {
+ printf("invalid address!\n");
+ lwp_shmrm(shmid);
+ return -1;
+ }
+
+ /* 把数据复制到共享内存上 */
+ memcpy(shm_vaddr, data, len);
+ /* 脱离共享内存,表示不再使用它了 */
+ lwp_shmdt(shm_vaddr);
+
+ return shmid;
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ int channel;
+
+ /* 用于放置消息的字符串数组 */
+ char ping[256] = { 0 };
+ size_t len = 0;
+
+ /* 定义发送的消息结构体和收到回复的消息结构体 */
+ struct rt_channel_msg ch_msg, ch_msg_ret;
+
+ /* 打开名字是 pong 的数据通道 */
+ channel = rt_channel_open("pong", 0);
+ if (channel == -1)
+ {
+ printf("Error: could not find the pong channel!\n");
+ return -1;
+ }
+
+ /* 100 次的经过 IPC 通道发送 ping 消息 */
+ for (i = 0; i < 100; i++)
+ {
+ printf("\n");
+
+ /* 初始化通道上要发送的消息结构体 */
+ ch_msg.type = RT_CHANNEL_RAW;
+ snprintf(ping, 255, "count = %d", i);
+ len = strlen(ping) + 1;
+ ping[len] = '\0';
+
+ /* 复制数据到共享内存中,并返回共享内存数据块的 id */
+ int shmid = prepare_data(ping, len);
+ if (shmid < 0)
+ {
+ printf("Ping: fail to prepare the ping message.\n");
+ continue;
+ }
+ /* 把共享内存块 id 赋值到消息结构体上 */
+ ch_msg.u.d = (void *)shmid;
+
+ printf("Ping: send %s\n", ping);
+ /* 发送消息并等待回复 */
+ rt_channel_send_recv(channel, &ch_msg, &ch_msg_ret);
+ printf("Ping: receive the reply %d\n", (int) ch_msg_ret.u.d);
+
+ /* 删除对应的共享内存块 */
+ lwp_shmrm(shmid);
+ }
+
+ /* 关闭通道 */
+ rt_channel_close(channel);
+
+ return 0;
+}
+```
+
+整体的消息流图如下所示
+
+
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-smart/architecture/figures/ipc-seq.png b/rt-thread-version/rt-thread-smart/architecture/figures/ipc-seq.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f12f7596b425ad90fe866e759194db514108d08
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/architecture/figures/ipc-seq.png differ
diff --git a/rt-thread-version/rt-thread-smart/architecture/figures/memlayout.drawio.png b/rt-thread-version/rt-thread-smart/architecture/figures/memlayout.drawio.png
new file mode 100644
index 0000000000000000000000000000000000000000..3ef30cd2159be213935418e2de7c030541911e34
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/architecture/figures/memlayout.drawio.png differ
diff --git a/rt-thread-version/rt-thread-smart/architecture/figures/rt-smart.drawio.png b/rt-thread-version/rt-thread-smart/architecture/figures/rt-smart.drawio.png
new file mode 100644
index 0000000000000000000000000000000000000000..922cf5588427f43234c0ea005898e703ebba02d1
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/architecture/figures/rt-smart.drawio.png differ
diff --git a/rt-thread-version/rt-thread-smart/architecture/figures/user_signal.drawio.png b/rt-thread-version/rt-thread-smart/architecture/figures/user_signal.drawio.png
new file mode 100644
index 0000000000000000000000000000000000000000..18c9e312f4dcffc3d17356ed739f19f2680747a9
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/architecture/figures/user_signal.drawio.png differ
diff --git a/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/Rapi4.png b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/Rapi4.png
new file mode 100644
index 0000000000000000000000000000000000000000..d355c00146bcdf092b5e02fca0c6412649208381
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/Rapi4.png differ
diff --git a/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/Rapi4B.jpg b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/Rapi4B.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..067a4b97a914e50100f18cdb6215d05700ebc85d
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/Rapi4B.jpg differ
diff --git a/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/boot.png b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/boot.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ef2839ab0d711e42ce511f8e58f1eeb53b40b4a
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/boot.png differ
diff --git a/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/contents.png b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/contents.png
new file mode 100644
index 0000000000000000000000000000000000000000..8234ce0e1a1dfbc8966e09d9379b4aa4bb18fc15
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/contents.png differ
diff --git a/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/uart.png b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/uart.png
new file mode 100644
index 0000000000000000000000000000000000000000..64d5f77aad33de446a7a3ea08007164d4c1f5d9c
Binary files /dev/null and b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/figures/uart.png differ
diff --git a/rt-thread-version/rt-thread-smart/rt-smart-quickstart/rt-smart-quickstart.md b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/rt-smart-quickstart.md
new file mode 100644
index 0000000000000000000000000000000000000000..6164da43efe413e10db94981e52a8e7ca2dae268
--- /dev/null
+++ b/rt-thread-version/rt-thread-smart/rt-smart-quickstart/rt-smart-quickstart.md
@@ -0,0 +1,256 @@
+# RT-Thread Smart 入门指南
+
+RT-Thread Smart(简称 rt-smart)是基于 RT-Thread 操作系统衍生的新分支,面向带 MMU,中高端应用的芯片,例如 ARM Cortex-A 系列芯片,MIPS 芯片,带 MMU 的 RISC-V 芯片等。rt-smart 在 RT-Thread 操作系统的基础上启用独立、完整的进程方式,同时以混合微内核模式执行。
+
+rt-smart 软件包是 RT-Thread Smart 的用户体验软件包,可在 QEMU 模拟的 VExpress-A9 机器中或树莓派 4B 开发板上执行。本文档是针对 QEMU、树莓派 4B 开发板上快速上手 rt-smart 入门指南文档。
+
+## 软件包说明
+### 下载 rt-smart 软件包
+
+下载 [rt-smart.zip](http://117.143.63.254:9012/www/rt-smart/rt-smart-20201125.zip) 软件包,解压后的目录说明如下图所示:
+
+
+
+### 准备工具链
+
+针对 Linux 和 Windows 环境需要下载对应的 arm-linux-musleabi 工具链,如果是 rt-smart 的软件包,则自带 Linux 环境下的工具链。
+
+- Linux 版本工具链下载:[Linux 版本工具链](http://117.143.63.254:9012/www/rt-smart/install_arm-linux-musleabi_for_x86_64-pc-linux-gnu.tar.bz2)
+
+- Windows 版本工具链下载:[Windows 版本工具链](http://117.143.63.254:9012/www/rt-smart/install_arm-linux-musleabi_for_i686-w64-mingw32.zip)
+
+请根据自己的开发环境选择对用的工具链下载使用。
+
+下载下来后分别解压展开到 `rt-smart/tools/gnu_gcc` 目录下,rt-smart 目录下的 smart-env.bat/sh 设置的环境变量,其中工具链路径都指向到这个目录下。
+
+## Linux 下编译并在 QEMU 模拟环境上执行
+
+在 Linux 系统下,需要安装一些基本的环境,然后才能编译 rt-smart。本文档是基于 Ubuntu16.04 系统环境操作,其它 Linux 版本类似。
+
+### 安装编译环境
+
+首先安装编译时需要用到的其他工具,软件包,可以通过如下的一些命令来安装:
+
+```bash
+sudo apt-get update
+sudo apt-get install vim scons git bzip2 net-tools
+sudo apt-get install python-all python-all-dev
+sudo apt-get install qemu-system-arm qemu-system-common qemu-utils
+```
+
+以上命令会安装一些系统基础工具、python 环境、scons、qemu 工具等。
+
+### 编译应用程序
+
+```bash
+# 进入到 rt-smart 目录
+cd rt-smart
+
+# 设置对应的环境变量,和原 RT-Thread 相比,多了 RTT_CC_PREFIX 环境变量
+source smart-env.sh
+
+# 编译用户态程序
+cd userapps
+scons
+
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+CC build/hello/main.o
+CC build/ping/main.o
+CC build/pong/main.o
+CC build/vi/optparse-v1.0.0/optparse.o
+CC build/vi/vi.o
+CC build/vi/vi_utils.o
+CC build/webclient/main.o
+LINK root/bin/hello.elf
+LINK root/bin/ping.elf
+LINK root/bin/pong.elf
+LINK root/bin/vi.elf
+LINK root/bin/webclient.elf
+scons: done building targets.
+```
+
+编译成功后,`userapps/apps` 下的应用程序会编译成一个个的 elf 可执行文件,并放置于 `userapps/root/bin` 目录下,可以把它转换成 romfs、C 语言数组的方式给 rt-smart 内核使用,这样可以不依赖于其他文件系统就可以直接执行,生成 romfs、C 语言数组可以用如下的命令行:
+
+```bash
+cd userapps
+python ../tools/mkromfs.py root ../kernel/bsp/qemu-vexpress-a9/applications/romfs.c
+```
+
+### 编译 rt-smart 内核
+
+```bash
+cd rt-smart/kernel/bsp/qemu-vexpress-a9
+scons
+... ...
+CC build/kernel/src/thread.o
+CC build/kernel/src/timer.o
+LINK rtthread.elf
+arm-linux-musleabi-objcopy -O binary rtthread.elf rtthread.bin
+arm-linux-musleabi-size rtthread.elf
+ text data bss dec hex filename
+1219480 40652 122444 1382576 1518b0 rtthread.elf
+scons: done building targets.
+```
+
+如果编译无误,会生成 rtthread.elf 内核文件。
+
+### 模拟执行
+
+通过 qemu 模拟的 vexpress-a9 开发板来直接运行:
+
+```bash
+cd rt-smart/kernel/bsp/qemu-vexpress-a9
+./qemu-nographic.sh
+
+ \ | /
+- RT - Thread Smart Operating System
+ / | \ 5.0.0 build Nov 15 2020
+ 2006 - 2020 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+try to allocate fb... | w - 640, h - 480 | done!
+fb => 0x61100000
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/SDIO] SD card capacity 65536 KB.
+Dir /mnt mount failed!
+hello rt-thread
+msh />
+msh />/bin/hello.elf
+msh />hello world!
+```
+
+退出 qemu 的方法: "Ctrl+A, X"。上面我们也运行了这次编译的应用程序 `/bin/hello.elf`,并看到它输出 `hello world!`。
+
+在第一次执行时,会输出 `Dir /mnt mount failed!`,这个是因为最开始执行时,qemu 的脚本会生成一个空的 sd.bin 文件做为一个 sd 卡给到 vexpress-a9 使用。所以在第一次执行时,需要对 sd0 进行格式化:
+
+```bash
+ \ | /
+- RT - Thread Smart Operating System
+ / | \ 5.0.0 build Nov 24 2020
+ 2006 - 2020 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+try to allocate fb... | w - 640, h - 480 | done!
+fb => 0x61100000
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/SDIO] SD card capacity 65536 KB.
+[I/SDIO] switching card to high speed failed!
+Dir /mnt mount failed!
+hello rt-thread
+msh />mkfs sd0
+msh />
+
+# Ctrl + A, X 退出
+QEMU: Terminated
+
+bernard@fuchsia-NUC8i7BEH:~/workspace/rt-smart/smart/kernel/bsp/qemu-vexpress-a9$ ./qemu-nographic.sh
+ \ | /
+- RT - Thread Smart Operating System
+ / | \ 5.0.0 build Nov 24 2020
+ 2006 - 2020 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+try to allocate fb... | w - 640, h - 480 | done!
+fb => 0x61100000
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/SDIO] SD card capacity 65536 KB.
+[I/SDIO] switching card to high speed failed!
+file system initialization done!
+hello rt-thread
+msh />
+```
+## Windows 下编译并在树莓派 4B 上执行
+
+在 Windows 上同样也可以进行编译,在 qemu 或树莓派 4B 开发板上执行起来,这里主要提及如何在树莓派 4B 上执行的方式。
+
+
+
+### 准备编译环境
+
+在 Windows 上编译 rt-smart,可以借助 RT-Thread 的 env 工具,env 工具下载及安装请参考 [这里](https://www.rt-thread.org/page/download.html), 请确保 env 可以正常使用。因为 rt-smart 软件包并不携带 Windows 环境下的工具链,所以务必记得安装前面描述的 准备工具链 章节下载 Windows 工具链并在 `rt-smart\tools\gnu_gcc` 下解压展开。打开 env 的终端窗口,切换到这个 rt-smart 代码包根目录,运行 smart-env.bat,它会设置一定的环境变量,然后整体的 smart 开发环境就可以使用了。
+
+```bash
+# 进入 rt-smart 目录,设置环境变量
+cd rt-smart
+> smart-env.bat
+```
+
+注:此处运行 smart-env.bat 设置环境,它包括编译器设置,同时它也会设置工具链的前缀,可以在 env 终端下输入以下命令查看返回结果是否生效:
+
+```bash
+# 查看环境变量是否生效
+> set RTT_CC_PREFIX
+RTT_CC_PREFIX=arm-linux-musleabi-
+```
+
+### 编译应用程序
+当要编译应用程序时,使用方式和 Linux 的类似:
+
+```bash
+# 进入 userapps 目录进行编译
+cd rt-smart\userapps
+scons
+```
+
+### 编译 rt-smart 内核
+
+```bash
+# 进入 raspberry-pi\raspi4-32 目录进行编译
+cd rt-smart\kernel\bsp\raspberry-pi\raspi4-32
+scons
+... ...
+CC build/kernel/src/signal.o
+CC build/kernel/src/thread.o
+CC build/kernel/src/timer.o
+LINK rtthread.elf
+arm-linux-musleabi-objcopy -O binary rtthread.elf kernel7.img
+arm-linux-musleabi-size rtthread.elf
+ text data bss dec hex filename
+ 710780 40448 64730 815958 c7356 rtthread.elf
+scons: done building targets.
+```
+
+编译无误后,会在当前目录下生成 kernel7.img 文件,这个是树莓派上 32 位的版本。目前 rt-smart 还只支持 32 位系统,所以在树莓派 4B 上是以 32 位模式来执行。
+
+### 在树莓派上执行
+#### 准备硬件连接
+
+为了在树莓派 4B 上执行,需要准备如下硬件清单,并连接串口线到开发板,连接图示如下:
+
+- 树莓派 4B
+- SD 卡(32GB 或 32GB 以下)
+- USB 转 TTL 串口线
+- 网线
+- TYPE-C(用于供电)
+- 读卡器(用于把编译好的文件写入到 SD 卡中)
+
+
+
+#### 准备 SD 卡上的软件
+
+树莓派的加载需要将一些 boot 文件放到 sd 卡中。[rpi4_rt-smart_boot.zip](http://117.143.63.254:9012/www/rt-smart/rpi4_rt-smart_boot.zip) 为树莓派的加载需要的一些 boot 文件,将下载后的文件解压后和 kernel7.img 一起放入空的 SD 卡根目录,如下图所示,其中 bin 文件夹中存放 `userapps\root\bin` 目录下已编译好的可执行 elf 文件。
+
+
+
+打开串口调试助手,插上电源,可以看到程序已经正常的运行起来,进入 bin 目录下即可执行示例程序:
+
+```bash
+heap: 0xc00c9a0a - 0xc40c9a0a
+\ | /
+- RT - Thread Smart Operating System
+/ | \ 5.0.0 build Nov 15 2020
+ 2006 - 2020 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+version is B1
+bcmgenet: PHY startup ok!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/SDIO] SD card capacity 31465472 KB.
+found part[0], begin: 4194304, size: 256.0MB
+found part[1], begin: 272629760, size: 29.772GB
+file system initialization done!
+hello rt-thread!
+msh />/bin/hello.elf
+msh />hello world!
+```
+
+
+
diff --git a/rt-thread-version/rt-thread-standard/README.md b/rt-thread-version/rt-thread-standard/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..18bfa87e2b2c0e51a6ecb3bd862efa1f58215113
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/README.md
@@ -0,0 +1,36 @@
+# RT-Thread 简介
+
+作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread 的魅力和它相较于其他同类型 RTOS 的种种优越之处。RT-Thread 是一款完全由国内团队开发维护的嵌入式实时操作系统(RTOS),具有完全的自主知识产权。经过近 15 个年头的沉淀,伴随着物联网的兴起,它正演变成一个功能强大、组件丰富的物联网操作系统。
+
+## RT-Thread 概述
+
+RT-Thread,全称是 Real Time-Thread,顾名思义,它是一个嵌入式实时多线程操作系统,基本属性之一是支持多任务,允许多个任务同时运行并不意味着处理器在同一时刻真地执行了多个任务。事实上,一个处理器核心在某一时刻只能运行一个任务,由于每次对一个任务的执行时间很短、任务与任务之间通过任务调度器进行非常快速地切换(调度器根据优先级决定此刻该执行的任务),给人造成多个任务在一个时刻同时运行的错觉。在 RT-Thread 系统中,任务通过线程实现的,RT-Thread 中的线程调度器也就是以上提到的任务调度器。
+
+RT-Thread 主要采用 C 语言编写,浅显易懂,方便移植。它把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。针对资源受限的微控制器(MCU)系统,可通过方便易用的工具,裁剪出仅需要 3KB Flash、1.2KB RAM 内存资源的 NANO 版本(NANO 是 RT-Thread 官方于 2017 年 7 月份发布的一个极简版内核);而对于资源丰富的物联网设备,RT-Thread 又能使用在线的软件包管理工具,配合系统配置工具实现直观快速的模块化裁剪,无缝地导入丰富的软件功能包,实现类似 Android 的图形界面及触摸滑动效果、智能语音交互效果等复杂功能。
+
+相较于 Linux 操作系统,RT-Thread 体积小,成本低,功耗低、启动快速,除此以外 RT-Thread 还具有实时性高、占用资源小等特点,非常适用于各种资源受限(如成本、功耗限制等)的场合。虽然 32 位 MCU 是它的主要运行平台,实际上很多带有 MMU、基于 ARM9、ARM11 甚至 Cortex-A 系列级别 CPU 的应用处理器在特定应用场合也适合使用 RT-Thread。
+
+## 许可协议
+
+RT-Thread 系统完全开源,3.1.0 及以前的版本遵循 GPL V2 + 开源许可协议。从 3.1.0 以后的版本遵循 Apache License 2.0 开源许可协议,可以免费在商业产品中使用,并且不需要公开私有代码。
+
+## RT-Thread 的架构
+
+近年来,物联网(Internet Of Things,IoT)概念广为普及,物联网市场发展迅猛,嵌入式设备的联网已是大势所趋。终端联网使得软件复杂性大幅增加,传统的 RTOS 内核已经越来越难满足市场的需求,在这种情况下,物联网操作系统(IoT OS)的概念应运而生。**物联网操作系统是指以操作系统内核(可以是 RTOS、Linux 等)为基础,包括如文件系统、图形库等较为完整的中间件组件,具备低功耗、安全、通信协议支持和云端连接能力的软件平台,**RT-Thread 就是一个 IoT OS。
+
+RT-Thread 与其他很多 RTOS 如 FreeRTOS、uC/OS 的主要区别之一是,它**不仅仅是一个实时内核,还具备丰富的中间层组件**,如下图所示。
+
+
+
+它具体包括以下部分:
+
+- 内核层:RT-Thread 内核,是 RT-Thread 的核心部分,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等;libcpu/BSP(芯片移植相关文件 / 板级支持包)与硬件密切相关,由外设驱动和 CPU 移植构成。
+- 组件与服务层:组件是基于 RT-Thread 内核之上的上层软件,例如虚拟文件系统、FinSH 命令行界面、网络框架、设备框架等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。
+- RT-Thread 软件包:运行于 RT-Thread 物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。RT-Thread 提供了开放的软件包平台,这里存放了官方提供或开发者提供的软件包,该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread 生态的重要组成部分。软件包生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。RT-Thread 已经支持的软件包数量已经达到 60+,如下举例:
+- 物联网相关的软件包:Paho MQTT、WebClient、mongoose、WebTerminal 等等。
+- 脚本语言相关的软件包:目前支持 JerryScript、MicroPython。
+- 多媒体相关的软件包:Openmv、mupdf。
+- 工具类软件包:CmBacktrace、EasyFlash、EasyLogger、SystemView。
+- 系统相关的软件包:RTGUI、Persimmon UI、lwext4、partition、SQLite 等等。
+- 外设库与驱动类软件包:RealTek RTL8710BN SDK。
+- 其他。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/_navbar.md b/rt-thread-version/rt-thread-standard/_navbar.md
new file mode 100644
index 0000000000000000000000000000000000000000..9d2007a29799597c638037335e61ca6a8777b549
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/_navbar.md
@@ -0,0 +1,3 @@
+
diff --git a/rt-thread-version/rt-thread-standard/_sidebar.md b/rt-thread-version/rt-thread-standard/_sidebar.md
new file mode 100644
index 0000000000000000000000000000000000000000..6294927cc0ba3538242af973dee08f32fe0f7281
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/_sidebar.md
@@ -0,0 +1,147 @@
+
+
+
+
+- **RT-Thread 标准版本**
+- 快速上手
+ - [Keil模拟器STM32F103](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f103-simulator/stm32f103-simulator.md)
+ - [RT-Thread潘多拉STM32L475](/rt-thread-version/rt-thread-standard/tutorial/quick-start/iot_board/quick-start.md)
+ - [野火霸道STM32F103](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f103-fire-arbitrary/quick-start.md)
+ - [正点原子nanoSTM32F103](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f103-atk-nano/quick-start.md)
+ - [野火挑战者STM32F429](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f429-fire-challenger/quick-start.md)
+ - [正点原子探索者STM32F407](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f407-atk-explorer/quick-start.md)
+ - [正点原子阿波罗STM32F429](/rt-thread-version/rt-thread-standard/tutorial/quick-start/stm32f429-atk-apolo/quick-start.md)
+ - [野火I.MX RT1052](/rt-thread-version/rt-thread-standard/tutorial/quick-start/imxrt1052-fire-mini/quick-start.md)
+ - [正点原子号令者I.MX RT1052](/rt-thread-version/rt-thread-standard/tutorial/quick-start/imxrt1052-atk-commander/quick-start.md)
+ - [其他开发板...](/rt-thread-version/rt-thread-standard/tutorial/quick-start/more.md)
+- 内核
+ - [内核基础](/rt-thread-version/rt-thread-standard/programming-manual/basic/basic.md)
+ - [线程管理](/rt-thread-version/rt-thread-standard/programming-manual/thread/thread.md)
+ - [时钟管理](/rt-thread-version/rt-thread-standard/programming-manual/timer/timer.md)
+ - [线程间同步](/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md)
+ - [线程间通信](/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md)
+ - [内存管理](/rt-thread-version/rt-thread-standard/programming-manual/memory/memory.md)
+ - [中断管理](/rt-thread-version/rt-thread-standard/programming-manual/interrupt/interrupt.md)
+ - [内核移植](/rt-thread-version/rt-thread-standard/programming-manual/porting/porting.md)
+- 设备和驱动
+ - [I/O设备模型](/rt-thread-version/rt-thread-standard/programming-manual/device/device.md)
+ - [UART设备](/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart.md)
+ - [PIN设备](/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin.md)
+ - [ADC设备](/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md)
+ - [DAC设备](/rt-thread-version/rt-thread-standard/programming-manual/device/dac/dac.md)
+ - [CAN设备](/rt-thread-version/rt-thread-standard/programming-manual/device/can/can.md)
+ - [HWTIMER设备](/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md)
+ - [I2C总线设备](/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c.md)
+ - [PWM设备](/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/pwm.md)
+ - [RTC设备](/rt-thread-version/rt-thread-standard/programming-manual/device/rtc/rtc.md)
+ - [SPI设备](/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi.md)
+ - [WATCHDOG设备](/rt-thread-version/rt-thread-standard/programming-manual/device/watchdog/watchdog.md)
+ - [WLAN设备](/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/wlan.md)
+ - [SENSOR设备](/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor.md)
+ - [TOUCH设备](/rt-thread-version/rt-thread-standard/programming-manual/device/touch/touch.md)
+ - [CRYPTO 设备](/rt-thread-version/rt-thread-standard/programming-manual/device/crypto/crypto.md)
+ - [AUDIO设备](/rt-thread-version/rt-thread-standard/programming-manual/device/audio/audio.md)
+ - [Pulse Encoder设备](/rt-thread-version/rt-thread-standard/programming-manual/device/pulse_encoder/pulse_encoder.md)
+- 组件
+ - [FinSH控制台](/rt-thread-version/rt-thread-standard/programming-manual/finsh/finsh.md)
+ - [虚拟文件系统](/rt-thread-version/rt-thread-standard/programming-manual/filesystem/filesystem.md)
+ - [netdev网卡](/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev.md)
+ - [SAL套接字抽象层](/rt-thread-version/rt-thread-standard/programming-manual/sal/sal.md)
+ - [AT命令](/rt-thread-version/rt-thread-standard/programming-manual/at/at.md)
+ - [ulog日志](/rt-thread-version/rt-thread-standard/programming-manual/ulog/ulog.md)
+ - [utest测试框架](/rt-thread-version/rt-thread-standard/programming-manual/utest/utest.md)
+ - [动态模块](/rt-thread-version/rt-thread-standard/programming-manual/dlmodule/dlmodule.md)
+ - [POSIX接口](/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md)
+ - [电源管理](/rt-thread-version/rt-thread-standard/programming-manual/pm/pm.md)
+- 软件包
+ - 物联网
+ - [网络工具集 (NetUtils) 应用](/rt-thread-version/rt-thread-standard/application-note/packages/netutils/an0018-system-netutils.md)
+ - [rw007 SPI WiFi 模块使用](/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/an0034-rw007-module-using.md)
+ - 工具
+ - [SystemView分析工具](/rt-thread-version/rt-thread-standard/application-note/debug/systemview/an0009-systemview.md)
+ - MicroPython 用户手册
+ - [介绍](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/introduction.md)
+ - [潘多拉 MicroPython 开发板](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_pandora_iot_board.md)
+ - [W601 MicroPython 开发板](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_w601_iot_board.md)
+ - [麻雀一号音视频开发板](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_sparrow_one_board.md)
+ - [MicroPython IDE](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-ide.md)
+ - [MicroPython 库介绍](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-librarys.md)
+ - [MicroPython 固件开发指南](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/firmware-develop.md)
+ - [MicroPython C 模块扩展](/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/external_c_modules.md)
+ - [更多软件包...](/rt-thread-version/rt-thread-standard/packages-manual/more.md)
+- 开发环境搭建
+ - [在windows平台使用QEMU运行RT-Thread](/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/an0006-qemu-windows.md)
+ - [在Ubuntu平台开发RT-Thread](/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/an0005-qemu-ubuntu.md)
+ - [使用Eclipse开发RT-Thread](/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/an0020-qemu-eclipse.md)
+ - [使用VS Code开发RT-Thread](/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/an0021-qemu-vscode.md)
+ - [使用Env创建RT-Thread项目工程](/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/an0017-standard-project.md)
+ - [固件尺寸优化](/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/an0049-optimize-code-size.md)
+ - [在RT-Thread潘多拉开发板上实现电源管理](/rt-thread-version/rt-thread-standard/application-note/system/pm/an0025-pm.md)
+ - [网络协议栈驱动移植](/rt-thread-version/rt-thread-standard/application-note/components/network/an0010-lwip-driver-porting.md)
+ - [在STM32F429上应用网络功能](/rt-thread-version/rt-thread-standard/application-note/components/network/an0011-network-started.md)
+ - [在STM32F429上应用文件系统](/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0012-dfs.md)
+ - [在潘多拉上使用SFUD操作Flash](/rt-thread-version/rt-thread-standard/application-note/components/sfud/an0048-sfud.md)
+ - [FreeModbus应用笔记](/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/an0036-freemodbus.md)
+ - [应用AT组件连接ESP8266模块](/rt-thread-version/rt-thread-standard/application-note/components/at/an0014-at-client.md)
+ - [多线程非阻塞网络编程](/rt-thread-version/rt-thread-standard/application-note/components/network/an0019-tcpclient-socket.md)
+ - [使用QEMU运行动态模块组件](/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/an0023-dlmodule.md)
+ - [CmBacktrace应用](/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/an0013-CmBacktrace.md)
+ - [在STM32L4上应用littlefs文件系统](/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0027-littlefs.md)
+ - [STM32通用Bootloader](/rt-thread-version/rt-thread-standard/application-note/system/rtboot/an0028-rtboot.md)
+ - [wireshark抓取tls数据](/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/an0029-mbedtls_wireshark_sniffer.md)
+ - [在STM32上应用C++](/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/an0035-cpp.md)
+ - [STM32上使用PWM](/rt-thread-version/rt-thread-standard/application-note/driver/pwm/an0037-rtthread-driver-pwm.md)
+ - [STM32上使用USB Host读写U盘](/rt-thread-version/rt-thread-standard/application-note/driver/usb/an0046-rtthread-driver-usbh.md)
+ - QEMU网络视频教程
+ - [教程简介](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/README.md)
+ - [基础知识之TCP/IP协议](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/tcp_ip/tcp_ip.md)
+ - [基础知识之BSD socket](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/socket/socket.md)
+ - [1.QEMU环境搭建](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/qemu_setup/qemu_setup.md)
+ - [2.QEMU调试](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/qemu_vscode/qemu_vscode.md)
+ - [3.文件系统](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/filesystems/filesystems.md)
+ - [4.网络抓包](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/wireshark/wireshark.md)
+ - [5.Ping分析](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/ping_principle/ping_principle.md)
+ - [6.TCP客户端](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/tcpclient/tcpclient.md)
+ - [7.TCP服务器](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/tcpserver/tcpserver.md)
+ - [8.UDP客户端](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/udpclient/udpclient.md)
+ - [9.UDP服务器](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/udpserver/udpserver.md)
+ - [10.TCP/UDP通信原理](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/tcp_udp_principle/tcp_udp_principle.md)
+ - [11.Select](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/tcpclient_select/tcpclient_select.md)
+ - [12.MQTT协议](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/mqtt/mqtt.md)
+ - [13.HTTP协议](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/httpclient/httpclient.md)
+ - [14.连接OneNet云](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/onenet/onenet.md)
+ - [15.NTP网络时间](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/ntp/ntp.md)
+ - [16.TFTP文件传输](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/tftp/tftp.md)
+ - [17.Telnet远程控制](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/telnet/telnet.md)
+ - [常见问题及解决方法](/rt-thread-version/rt-thread-standard/tutorial/qemu-network/faq/faq.md)
+
+- Demo示例
+ - 蜂鸣器播放器
+ - [简介](/rt-thread-version/rt-thread-standard/tutorial/beep-player/README.md)
+ - [使用PIN设备控制LED](/rt-thread-version/rt-thread-standard/tutorial/beep-player/pin.md)
+ - [使用PIN设备进行按键控制](/rt-thread-version/rt-thread-standard/tutorial/beep-player/button.md)
+ - [使用PWM设备驱动蜂鸣器](/rt-thread-version/rt-thread-standard/tutorial/beep-player/pwm.md)
+ - [音乐数据的编码与解码](/rt-thread-version/rt-thread-standard/tutorial/beep-player/decode.md)
+ - [播放器实现](/rt-thread-version/rt-thread-standard/tutorial/beep-player/player.md)
+ - [按键控制](/rt-thread-version/rt-thread-standard/tutorial/beep-player/key.md)
+ - 分布式温度监控系统:
+ - [简介](/rt-thread-version/rt-thread-standard/tutorial/temperature-system/README.md)
+ - [Sensor框架对接实战](/rt-thread-version/rt-thread-standard/tutorial/temperature-system/sensor.md)
+ - [邮箱与消息队列实战](/rt-thread-version/rt-thread-standard/tutorial/temperature-system/ipc.md)
+ - [文件系统实战](/rt-thread-version/rt-thread-standard/tutorial/temperature-system/dfs.md)
+ - [OneNet连云实战](/rt-thread-version/rt-thread-standard/tutorial/temperature-system/onenet.md)
+ - 智能车:
+ - [简介](/rt-thread-version/rt-thread-standard/tutorial/smart-car/README.md)
+ - [RT-Thread卷积神经网络手写体识别](/rt-thread-version/rt-thread-standard/tutorial/smart-car/cnn-mnist/cnn-mnist.md)
+ - [Darknet训练目标检测模型](/rt-thread-version/rt-thread-standard/tutorial/smart-car/darknet-yolov2/darknet-yolov2.md)
+ - [RT-Thread连接ROS](/rt-thread-version/rt-thread-standard/tutorial/smart-car/ros-connect/ros-connect.md)
+ - [RT-Thread连接ROS控制小车](/rt-thread-version/rt-thread-standard/tutorial/smart-car/ros-camera-car/ros-camera-car.md)
+ - [RT-Thread连接RPLidar激光雷达](/rt-thread-version/rt-thread-standard/tutorial/smart-car/rplidar-connect/rplidar-connect.md)
+ - [RT-Thread搭配ROS实现目标检测小车](/rt-thread-version/rt-thread-standard/tutorial/smart-car/object-detection/object-detection.md)
+- 代码贡献
+ - [驱动开发](/rt-thread-version/rt-thread-standard/development-guide/package/package.md)
+ - [传感器驱动开发指南](/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development.md)
+ - [软件包开发](/rt-thread-version/rt-thread-standard/development-guide/package/package.md)
+ - [向RT-Thread贡献代码](/rt-thread-version/rt-thread-standard/development-guide/github/github.md)
+- [RT-Thread 标准版的版本选择](/rt-thread-version/rt-thread-standard/application-note/setup/rt-thread-version/an0030-rtthread-version.md)
+- [API参考手册](https://www.rt-thread.org/document/api/)
diff --git a/rt-thread-version/rt-thread-standard/application-note/README.md b/rt-thread-version/rt-thread-standard/application-note/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0bcf310463e7d9e504fca86350812b91c1731dec
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/README.md
@@ -0,0 +1,5 @@
+# RT-Thread应用笔记 #
+
+----------
+
+RT-Thread应用笔记是针对某一技术点的专题介绍,方便开发者更快更好的学习和掌握该技术点。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/an0014-at-client.md b/rt-thread-version/rt-thread-standard/application-note/components/at/an0014-at-client.md
new file mode 100644
index 0000000000000000000000000000000000000000..028dcc23bdb5901a11d3618a0b62288821b1d72f
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/at/an0014-at-client.md
@@ -0,0 +1,312 @@
+# 应用 AT 组件连接 ESP8266 模块
+
+本文介绍了 RT-Thread AT 组件的基本知识和 AT 客户端的使用方法,帮助开发者更好地使用 RT-Thread AT 组件。
+
+## 简介
+
+为了方便用户使用 AT 命令,简单的适配不同的 AT 模块, RT-Thread 提供了 AT 组件用于 AT 设备的连接和数据通讯。**AT 组件的实现包括客户端的和服务器两部分**。对于嵌入式设备而言,更多的情况下设备使用 AT 组件作为客户端连接服务器设备,所以本文将为大家重点介绍 AT 组件中客户端的主要功能、移植方式和实现原理,并介绍在客户端的基础上实现标准 BSD Socket API,使用 AT 命令完成复杂网络通讯。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+## AT Client 功能
+
+本文将基于正点原子 STM32F4 探索者开发板和乐鑫 ESP8266 开发板,给出了 AT 组件中 AT Client 功能的配置、移植和使用方式。
+
+**AT Client 功能主要用于完成 AT 命令的数据收发和解析过程。**
+
+下图为本文使用的两个开发板的底板图,开发者可以使用 ESP8266 开发板或模组,若缺少正点原子 STM32F4 探索者开发板可使用其他带额外串口的开发板代替,需确保开发板正常运行 RT-Thread 系统且串口使用正常:
+
+
+
+
+
+AT 组件中 AT Client 主要完成 AT 命令的发送和响应数据的接收与解析。这里我们使用正点原子 STM32F4 探索者开发板串口 3 作为 AT Client 连接 ESP8266 开发板的串口 2,ESP8266 开发板的串口 2 作为 AT Server,完成 AT Client 数据收发和解析的功能,下面就具体给出配置和使用方式的介绍。
+
+### AT Client 配置
+
+开启 Env 工具,进入 `rt-thread\bsp\stm32\stm32f407-atk-explorer` 目录,在 Env 命令行输入 menuconfig 进入配置界面配置工程。
+
+- 配置串口支持:配置开启 UART3 选项;
+
+
+
+- 开启 AT Client 功能:RT-Thread Components ---> Network ---> AT commands --> 开启 AT DEBUG,开启 AT Client 支持,目前 AT Client 支持多连接功能,后面需要手动初始化 AT Client。
+
+
+
+AT Client 配置选项介绍如下:
+
+- Enable debug log output:配置开启调试日志;
+- Enable AT commands client:配置开启 AT 客户端;
+- The muxinum number of supported clients: 配置最大同时支持的客户端数量,该例程使用单客户端连接,配置为 1 即可。
+- Enable BSD Socket API support by AT commands: 配置开启 BSD Socket API 支持,下面 AT Client 例程没有使用可暂时不开启。
+- Enable CLI(Command-Line Interface) for AT commands: 配置开启 AT 命令行交互模式。
+- Enable print RAW format AT command communication data: 配置开启收发数据实时打印功能。
+- The maxinum length of AT Commonds:配置发送 AT 命令的最大数据长度
+
+配置完成,保存并退出配置选项,输入命令 scons --target=mdk5 生成 MDK 工程;
+
+### AT Client 添加示例
+
+下载 [AT Client 示例代码](https://github.com/RT-Thread-packages/at_device/blob/master/samples/at_sample_client.c),添加到打开的 MDK 工程中,如下图所示:
+
+
+
+示例添加完成,就可以编译、下载程序到开发板,之后打开 PC 上串口工具,这里使用 xshell 工具,选择正确的串口(配置串口参数为 115200-8-1-N、无流控),然后按下复位后就可以在串口 1 连接的终端上看到 RT-Thread 系统启动日志。
+
+系统初始化成功之后,在 shell 中执行 `at_client_init uart3 ` 命令,这里的 `uart3` 为开发板中作为 AT client 的设备名。然后可以看到 AT Client 的初始化日志,说明 AT Client 功能配置启动成功,如下图所示:
+
+
+
+### AT Client 运行示例
+
+### AT Client 模式
+
+该模式下正点原子 STM32F4 探索者开发板串口 3 作为 AT Client, ESP8266 开发板作为 AT Server,进行数据交互模式,在本地 shell 中输入 `at_client_test` 命令,该 shell 命令用于发送 AT 命令到服务器,并且接收和解析服务器响应数据,如下图所示过程:
+
+
+
+### AT Client CLI 模式
+
+AT Client CLI 功能可以转发本地 shell 输入的数据到设备连接的 AT Server 串口设备上,并在本地 shell 上实时显示 AT Client 串口接收到的数据。在本地 shell 中执行 `at client` 命令进入 AT Client CLI 模式即可进行数据的收发。通过 AT Client CLI 模式,用户可以很方便的完成与 AT Server 的连接与调试,极大的提高开发效率。
+
+下图演示了 AT Client CLI 功能的使用和退出:
+
+
+
+### AT Client 示例详解
+
+本文使用的 AT Client 示例代码演示了 AT Client 的整个使用流程,示例代码完成 STM32F4 设备 AT 命令的发送并接收和解析 ESP8266 设备的响应数据。代码的使用和平台有关,开发者可以根据自己使用的平台修改示例代码并运行,主要修改命令的名称和解析的方式。下面通过示例代码介绍一下 AT Client 的具体使用流程:
+
+```c
+#include
+#include
+#include
+#include
+
+/* AT+CIFSR Query local IP address and MAC */
+int at_client_test(int argc, char**argv)
+{
+ at_response_t resp = RT_NULL;
+ int result = 0;
+
+ if (argc != 1)
+ {
+ LOG_E("at_client_test - AT client send commands to AT server.");
+ return -1;
+ }
+
+ /* 创建响应结构体,设置最大支持响应数据长度为 256 字节
+ (最大响应长度用户根据实际需求自定义),响应数据行数无限制,超时时间为 5 秒 */
+ resp = at_create_resp(256, 0, rt_tick_from_millisecond(5000));
+ if (resp == RT_NULL)
+ {
+ LOG_E("No memory for response structure!");
+ return -2;
+ }
+
+ /* 关闭回显功能 */
+ at_exec_cmd(resp, "ATE0");
+
+ /* AT Client 发送查询 IP 地址命令并接收 AT Server 响应 */
+ /* 响应数据及信息存放在 resp 结构体中 */
+ result = at_exec_cmd(resp, "AT+CIFSR");
+ if (result != RT_EOK)
+ {
+ LOG_E("AT client send commands failed or return response error!");
+ goto __exit;
+ }
+
+ /* 按行数循环打印接收到的响应数据 */
+ {
+ const char *line_buffer = RT_NULL;
+
+ LOG_D("Response buffer");
+ for(rt_size_t line_num = 1; line_num <= resp->line_counts; line_num++)
+ {
+ if((line_buffer = at_resp_get_line(resp, line_num)) != RT_NULL)
+ {
+ LOG_D("line %d buffer : %s", line_num, line_buffer);
+ }
+ else
+ {
+ LOG_E("Parse line buffer error!");
+ }
+ }
+ }
+ /* 按自定义表达式(sscanf 解析方式)解析数据,得到对应数据 */
+ {
+ char resp_arg[AT_CMD_MAX_LEN] = { 0 };
+ /* 自定义数据解析表达式 ,用于解析两双引号之间字符串信息 */
+ const char * resp_expr = "%*[^\"]\"%[^\"]\"";
+
+ LOG_D("Parse arguments");
+ /* 解析响应数据中第一行数据,得到对应 IP 地址 */
+ if (at_resp_parse_line_args(resp, 1, resp_expr, resp_arg) == 1)
+ {
+ LOG_D("Station IP : %s", resp_arg);
+ memset(resp_arg, 0x00, AT_CMD_MAX_LEN);
+ }
+ else
+ {
+ LOG_E("Parse error, current line buff : %s", at_resp_get_line(resp, 4));
+ }
+
+ /* 解析响应数据中第二行数据,得到对应 MAC 地址 */
+ if (at_resp_parse_line_args(resp, 2, resp_expr, resp_arg) == 1)
+ {
+ LOG_D("Station MAC : %s", resp_arg);
+ }
+ else
+ {
+ LOG_E("Parse error, current line buff : %s", at_resp_get_line(resp, 5));
+ goto __exit;
+ }
+ }
+__exit:
+ if(resp)
+ {
+ /* 删除 resp 结构体 */
+ at_delete_resp(resp);
+ }
+
+ return result;
+}
+/* 设置当前 AT 客户端最大支持的一次接收数据的长度 */
+#define AT_CLIENT_RECV_BUFF_LEN 512
+int at_client_test_init(int argc, char**argv)
+{
+ if (argc != 2)
+ {
+ rt_kprintf("at_client_init -- AT client initialize.\n");
+ return -RT_ERROR;
+ }
+
+ at_client_init(argv[1], AT_CLIENT_RECV_BUFF_LEN);
+
+ return RT_EOK;
+}
+#ifdef FINSH_USING_MSH
+#include
+/* 添加 AT Client 测试命令到 shell */
+MSH_CMD_EXPORT(at_client_test, AT client send cmd and get response);
+/* 添加 AT Client 初始化命令到 shell */
+MSH_CMD_EXPORT_ALIAS(at_client_test_init, at_client_init, initialize AT client);
+#endif
+```
+- 整个示例为单客户端示例,可以直接使用单客户端模式 API。
+
+- AT Client 使用流程大致如下:at_create_resp() 创建响应结构体 ---> at_exec_cmd() 发送命令并接收响应 ---> at_resp_get_line()/at_resp_parse_line_args() 打印或解析响应数据 ---> at_delete_resp() 删除响应结构体。
+
+ - at_exec_cmd() 函数完成对传入 AT 命令的发送和响应数据的接收,响应数据以按行的形式存放与结构体中,便于数据按行打印或及解析。
+
+ - 打印或解析数据时对于不同的命令的响应数据有不同的数据解析方式,需要自定义数据解析的表达式,这要求开发者提前知道发送命令的具体响应结构,可以通过查看设备 AT 命令手册了解。
+
+## AT Socket 功能
+
+为了方便开发者使用 AT 组件进行网络相关操作,降低 RT-Thread 系统对单独协议栈网络连接的依赖,RT-Thread 系统在 AT 组件和 SAL 组件的基础上推出了 AT Socket 功能。
+
+**AT Socket 功能是建立在 AT Client 功能基础上,主要作用是完成 AT 设备连接网络并进行数据通讯,对应用层提供标准 BSD Socket API 接口,方便应用层代码移植和使用。**
+
+ AT Socket 功能使设备无需实现其他网络连接方式,直接使用串口完成设备联网功能,简化了设备开发的软硬件设计,方便开发者开发。此外,不同于传统的软件网络协议栈,AT Socket 网络功能的运行主要是在串口连接的 AT Server 设备上完成,根据不同的 AT Server 设备,可同时支持 5-6 个 socket,这样极大了降低了 AT Client 设备上 MCU 资源占用,提高 MCU 工作效率,确保数据通讯的质量和硬件的资源的合理分配。
+
+AT Socket 功能目前占用最少资源体积约为**20K ROM 、 3K RAM**(支持 5 个 Socket)。
+
+AT Socket 功能对于不同的 AT 设备需要完成移植适配过程,目前已经完成多种设备的适配,包括:ESP8266、 M26 、MC20、EC20、SIM800、SIM76XX、RW007、MW31 等,各种适配的方式通过 [AT Device 软件包](https://github.com/RT-Thread-packages/at_device) 给出,所以 AT Socket 功能的实现基于 AT Device 软件包。下面主要通过 ESP8266 设备,AT Socket 功能使用和 AT Device 软件包配置进行介绍。
+
+### AT Socket 配置
+
+AT Socket 功能的使用依赖于如下几个组件:
+
+- **AT 组件**:AT Socket 功能基于 AT Client 功能的实现;
+- **SAL 组件**:SAL 组件主要是 AT Socket 接口的抽象,实现标准 BSD Socket API;
+- **netdev 组件**:用于抽象和管理 AT 设备生成的网卡设备相关信息,提供 ping、ifconfig、netstat 等网络命令;
+- **AT Device 软件包**:针对不同设备的 AT Socket 移植和示例文件,以软件包的形式给出;
+
+下面主要介绍 Env 中配置 AT Socket 功能的整个流程:
+
+1. 开启 Env 工具,进入`rt-thread\bsp\stm32\stm32f407-atk-explorer` 目录,在 Env 命令行输入 menuconfig 进入配置界面配置工程。
+
+2. 开启 AT Device 软件包,示例中使用 `laster` 最新版本,需要配置使用的 AT 模块型号(ESP8266)和 AT Client 设备名称(UART3):
+
+- `RT-Thread online packages ---> IoT - internet of things ---> AT Device`配置开启 AT DEVICE 软件包支持;
+- 配置使用的设备为 ESP8266 设备;
+- 配置 AT Client 设备名称和最大支持的接收数据长度;
+- 配置 wifi ssid 和 wifi password 用于设备联网;
+- 配置使用 `laster` 版本软件包;
+
+
+
+3. AT Device 软件包开启,并且选择指定 AT 设备之后,会默认选上 AT 组件中 AT Client 功能已经 AT Socket 功能支持,如下图所示:
+
+
+
+**AT Device 软件包中每种 AT 设备配置选项,都是 AT Socket 功能针对该设备的实现方式。**
+
+4. 之后需要开启 SAL 组件支持,用于抽象统一标准网络接口,`RT-Thread Components ---> Network ---> Socket abstraction laye ---> Support AT Commands stack`,开启 SAL 组件功能支持,然后开启 `SAL_USING_POSIX` 支持,支持使用 read/write、poll/select 等文件系统接口函数。
+
+
+
+5. 配置完成,保存并退出配置选项,输入命令 scons --target=mdk5 生成 keil 工程。
+
+6. 打开 MDK 工程,编译、下载代码到开发板中。
+
+7. 打开 PC 上串口工具 xshell,配置打开串口(配置串口参数为 115200-8-1-N、无流控),然后按下复位后就可以在串口 1 连接的终端上看到 RT-Thread 系统启动日志,并可以看到 AT Client 的启动日志、SAL 的启动日志且设备自动连接网络成功,说明 AT Socket 功能初始化成功,如下图所示。
+
+
+
+### AT Socket 使用
+
+#### 网络连接测试
+
+AT Socket 功能提供 `ping` 或者 `ifconfig`命令用于测试设备网络连接环境,,`ping` 命令原理是通过 AT 命令发送请求到服务器,服务器响应数据,客户端解析 ping 数据并显示。`ifocnfig` 命令可以查看当前设备网络状态和 AT 设备生成的网卡基本信息。如下图所示,设备网络连接成功之后,执行网络测试命令:
+
+
+
+#### MQTT 组件示例测试
+
+AT Socket 功能完成设备通过串口 AT 命令进行网络数据通讯,设备可以通过 AT Socket 功能启动 MQTT 协议并运行 MQTT 示例代码,具体配置步骤和示例使用方式如下:
+
+- AT Device 软件包开启的基础上,配置下载 MQTT 组件包及示例代码,具体配置方式:RT-Thread online packages ---> IOT - internet of things ---> 开启 paho MQTT 组件包,配置开启 MQTT 示例代码。
+
+
+
+- 配置完成,保存并退出配置选项,scons 重新生成工程,编译下载代码到开发板中。
+
+- 打开串口工具,系统启动成功,输入 `mqtt_start` 命令启动 MQTT 协议,启动完成之后输入 `mqtt_publish mqtt_test_data` 命令,用于向固定的 MQTT Topic 发送数据,同时 MQTT 服务器会立刻向该 Topic 发送同样数据,MQTT 示例测试完成,如下图所示:
+
+
+
+上述展示了正点原子 STM32F4 设备在未连接网络的情况下使用 AT Socket 功能运行 MQTT 网络示例,实现了 AT Socket 网络数据收发的功能,目前 AT Socket 功能只支持设备作为网络客户端连接服务器,这也符合嵌入式设备多用于客户端设备的特性。AT Socket 目前已经支持多种网络相关组软件包和功能,如下所示:
+
+- tcpclient/udpclient 功能
+- MQTT 软件包
+- webclient 软件包
+- mbedtls 软件包
+- onenet 软件包
+- ali-linkkit 软件包
+- NTP 时间查询功能
+- iperf 网络测试功能
+- ping/ifconfig/netstat 网络测试功能
+
+## 参考资料
+
+* [《AT 组件编程指南》](../../../programming-manual/at/at.md)
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
+
+## 常见问题
+
+### Q: esp8266 设备连上网络后过一段时间 wifi 自动断开重连怎么办?
+
+**A:** 该错误一般为 esp8266 设备供电问题,可以使用万用表查看设备当前电压情况,如果出现供电不足问题,可以为 esp8266 vin 接口添加额外供电。
+
+### Q: esp8266 设备一直显示连接超时,命令发送失败怎么办?
+
+**A:** 检查 esp8266 设备串口接线,反接 RX/TX 接线,检查设备供电,进 AT Client CLI 模式确定命令发送是否正常。
+
+更多 AT 组件或者 AT Device 软件包相关问题请查看 [ AT 相关问题汇总贴](https://www.rt-thread.org/qa/thread-11919-1-1.html)。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/add_sample.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/add_sample.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..bc1a1b2440989056521a8a565dfbf169339ed82a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/add_sample.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_frame.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_frame.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f5c868dc6c26a7715b9946817c68ca24d9be68cd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_frame.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_ping.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_ping.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..101a6987060c796006a9e7dfc8a28e0e1b04bdbe
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_ping.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_scoket_start.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_scoket_start.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..8749dfede6b71c41d3023b76f9435147f1cb7047
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/at_scoket_start.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_cli.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_cli.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..c1994c9d6a3c53b51e317e85c46deec5d65289a2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_cli.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_sample.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_sample.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..087f8aac2b9038fcdcfeb95bc034e8eeff240e33
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_sample.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_start.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_start.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..df8e5d0a70b39dd29aa0adab0422f4fc88b5cbf0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/client_start.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_at_device.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_at_device.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..fd5ea7281e492bd1b5cb9fc88e5dd585ec7ed5a5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_at_device.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_at_socket.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_at_socket.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..975bccc4e0b3520f51b680789cbf8d45f75700f2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_at_socket.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_client.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_client.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..bcc16b083c9c0d7e26c1fd3a550ee0d16d726151
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_client.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_mqtt.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_mqtt.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..28ce9e7c8f19feb1f6edd95f541eb2007e0f07ff
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_mqtt.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_sal.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_sal.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..65009d237433e709378d84a98d7e8cd8ac665987
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_sal.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_uart.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_uart.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..0eb14a45af444ae46565130bd50195fc8b6853cd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/config_uart.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/esp8266.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/esp8266.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..3506413a4240c683e2446a9af6a4a04eb26c7463
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/esp8266.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/mqtt_sample.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/mqtt_sample.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6bfb9e98f2997f4dcb5f8bb581b83a9b507a4d4a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/mqtt_sample.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/at/figures/stm32f4.jpg b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/stm32f4.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..00a33ef881871a000a0cd65b5447cf14a5e1ef0f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/at/figures/stm32f4.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/an0035-cpp.md b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/an0035-cpp.md
new file mode 100644
index 0000000000000000000000000000000000000000..7536b56f9bb6b14e8d7a5849d47b2cfd9aec3f9d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/an0035-cpp.md
@@ -0,0 +1,254 @@
+# 在 STM32 上使用 C++ 指南
+
+本文描述了如何使用在搭载了 `RT-Thread` 系统的 `STM32` 平台上使用 `C++`,包括 `C++` 的配置和应用等。并给出了在意法半导体 `STM32F411 nucleo` 开发板上验证的代码示例。
+
+## 硬件平台简介
+
+本文基于意法半导体 `STM32F411 nucleo` 开发板,给出了 `C++` 的具体应用示例代码,由于 `RT-Thread` 上层应用 `API` 的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。
+
+`STM32F411 nucleo` 是意法半导体推出的一款基于 `ARM Cortex-M4` 内核的开发板,最高主频为 `100Mhz`,该开发板具有丰富的板载资源,可以充分发挥 `STM32F411RE` 的芯片性能。
+
+
+
+## 如何在 `STM32` 上使用 `C++`
+
+准备工作:
+
+1. 下载 [RT-Thread 源码](https://github.com/RT-Thread/rt-thread)
+
+2. 下载 [ENV 工具](https://www.rt-thread.org/page/download.html)
+
+
+3. 进入 `rt-thread\bsp\stm32f411-st-nucleo` 目录,检查 BSP `rtconfig.py` 文件和 `SConstruct` 文件是否支持 `C++` 配置,如下图所示
+
+检查 `rtconfig.py` 文件中对 `C++` 的支持
+
+
+
+检查 `SConstruct` 文件中对 `C++` 的支持
+
+
+
+打开 `C++` 支持:
+
+1. 打开 `Env` 工具,在 `Env` 命令行中输入 `menuconfig`,进入配置界面,使用 `menuconfig` 工具(学习如何使用)配置工程。在 `menuconfig` 配置界面依次选择 `RT-Thread Components ---> C++ features ---> Support C++ features`,如图所示:
+
+
+
+编译工程:
+`scons --target=mdk5`
+1. 生成 `mdk5` 工程,将附录章节的 `main.cpp` 文件替换掉 `BSP` 中的 `main.c` 文件并重新加入到工程中,如图所示:
+
+
+
+2. 编译,下载程序,在终端输入 `help` 命令可以看到 `test_cpp` 已经添加成功了。
+
+
+
+
+3. 运行 `C++` 程序:
+
+ 在终端输入 `test_cpp` 运行结果如下图所示。
+
+
+
+## C++ 全局对象构造函数的调用
+
+`RT-Thread` 中对全局对象构造函数的实现中,以 `GNUC` 为例,在 `rt-thread\components\cplusplus` 目录下的 `crt_init.c` 文件中对 `C++` 进行了系统初始化,
+在特定的 `BSP` 目录下,连接脚本文件 `link.lds` 为 `C++` 全局构造函数的代码分配了段,使 `C++` 全局对象构造函数链接后能够存放在指定的段中。如下图所示:
+
+ 
+
+1. `crt_init.c` 文件完成了 `C++` 系统的初始化工作
+
+2. `C++` 系统初始化部分:
+
+ RT_WEAK int cplusplus_system_init(void)
+ {
+ typedef void(*pfunc)();
+ extern pfunc __ctors_start__[];
+ extern pfunc __ctors_end__[];
+ pfunc *p;
+
+ for (p = __ctors_start__; p < __ctors_end__; p++)
+ (*p)();
+
+ return 0;
+ }
+ INIT_COMPONENT_EXPORT(cplusplus_system_init);
+
+在 `cplusplus_system_init` 函数中,将全局对象的构造函数依次链接到了链接脚本文件中为其分配的段中,并且调用了 `RT-Thread` 组件自动初始化的宏 `INIT_COMPONENT_EXPORT`,所以在链接的时候,`C++` 全局对象构造函数所产生的目标文件就被链接到了 `__ctors_start__` 和 `__ctors_end__`组成的段中。
+
+3. 链接脚本中为 `C++` 全局构造函数分配的段部分:
+
+
+ PROVIDE(__ctors_start__ = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE(__ctors_end__ = .);
+
+
+`__ctors_start__` 分配了 `C++` 全局构造函数段的起始地址, `__ctors_end__` 分配了 `C++` 全局构造函数段的结束地址,所以全局构造函数在系统初始化的时候,就会被链接到这里分配的段地址中。
+
+
+
+
+
+## RT-Thread C++ 异常说明
+
+同样,在链接脚本文件 `link.lds` 中,也为 `C++` 异常分配了段地址:
+
+ __exidx_start = .;
+ ARM.exidx :
+ {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ _sidata = .;
+ } > CODE
+ __exidx_end = .;
+
+
+`__exidx_start` 分配了 `C++` 异常的起始地址, `__exidx_end` 分配了 `C++` 异常的结束地址,当异常产生的时候,就会被分配到指定的段地址中.
+
+这里以一个 `C++` 除零异常的抛出和捕获为例:
+
+
+ #include
+
+ #define MIN_VALUE (1e-4)
+ #define IS_DOUBLE_ZERO(d) (abs(d) < MIN_VALUE)
+
+ double div_func(double x, double y)
+ {
+ if (IS_DOUBLE_ZERO(y))
+ {
+ throw y; /* throw exception */
+ }
+
+ return x / y;
+ }
+
+ void throw_exceptions(void *args)
+ {
+ try
+ {
+ div_func(6, 3);
+ rt_kprintf("there is no err\n");
+ div_func(4, 0); /* create exception*/
+ rt_kprintf("you can run here?\n");
+ }
+ catch(double) /* catch exception */
+ {
+ rt_kprintf("error of dividing zero\n");
+ }
+ }
+
+ MSH_CMD_EXPORT(throw_exceptions, throw cpp exceptions);
+
+
+当除零异常发生的时候 `div_func` 函数会抛出一个异常,在 `throw_exceptions` 函数中会去捕获这个异常。
+
+ 下载代码,并在终端输入 `throw_exceptions` 运行结果如下图所示。
+
+ 
+
+
+到这一步为止,如何在搭载了 `RT-Thread` 系统的 `STM32` 平台上如何使用 `C++` 的介绍就结束了。
+
+## 附录
+
+`main.cpp` 源码
+
+```c
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2019-05-23 tyustli first version
+ */
+
+#include
+
+int main(void)
+{
+ int count = 1;
+ while (count++)
+ {
+ rt_thread_mdelay(500);
+ rt_kprintf("hello rtthread\r\n");
+ }
+
+ return RT_EOK;
+}
+using namespace rtthread;
+
+class tran
+{
+public:
+ void getnumber(int a, int b)
+ {
+ x = a;
+ y = b;
+ }
+ void out(tran & s)
+ {
+ rt_kprintf("x = %d, y = %d\n", x, y);
+ }
+private:
+ int x, y;
+};
+
+int test_cpp(void)
+{
+ tran s;
+
+ s.getnumber(13, 54);
+ s.out(s);
+
+ return 0;
+}
+
+MSH_CMD_EXPORT(test_cpp, test cpp);
+
+#include
+#include
+
+#define MIN_VALUE (1e-4)
+#define IS_DOUBLE_ZERO(d) (abs(d) < MIN_VALUE)
+
+double div_func(double x, double y)
+{
+ if (IS_DOUBLE_ZERO(y))
+ {
+ throw y; /* throw exception */
+ }
+
+ return x / y;
+}
+
+void throw_exceptions(void *args)
+{
+ try
+ {
+ div_func(6, 3);
+ rt_kprintf("there is no err\n");
+ div_func(4, 0); /* create exception*/
+ rt_kprintf("you can run here\n");
+ }
+ catch(double) /* catch exception */
+ {
+ rt_kprintf("error of dividing zero\n");
+ }
+}
+
+MSH_CMD_EXPORT(throw_exceptions, throw cpp exceptions);
+
+```
+
+## 参考资料
+* [ENV 用户手册](https://www.rt-thread.org/document/site/programming-manual/env/env/)
+
+* [STM32F411-ST-NUCLEO BSP 源码](https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32f411-st-nucleo)
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/SConstruct.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/SConstruct.png
new file mode 100644
index 0000000000000000000000000000000000000000..fdf869876d2bb04393316acd49dac3e112c4b487
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/SConstruct.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/board.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/board.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2a4e57a7f03caab5b9ddd7b4a3b70275d3db75e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/board.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/build_func.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/build_func.png
new file mode 100644
index 0000000000000000000000000000000000000000..f19aaee8272d27ce0922e73c2c16bc8d0d6b1a56
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/build_func.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/help_cpp.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/help_cpp.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ad3f5f5becff94479567c4043337692ea2dd4ce
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/help_cpp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/main_cpp.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/main_cpp.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a89c8df8ea1f86bb0fe7d6ced4e664988680feb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/main_cpp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/rtconfig_py.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/rtconfig_py.png
new file mode 100644
index 0000000000000000000000000000000000000000..4b5da68e85abd713f39eca55b6ae57543fc902db
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/rtconfig_py.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/run_cpp.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/run_cpp.png
new file mode 100644
index 0000000000000000000000000000000000000000..6892f30c0d8a26a9058c2d34327dc61901d14c7c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/run_cpp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/support_cpp.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/support_cpp.png
new file mode 100644
index 0000000000000000000000000000000000000000..99b0687b5615af0201c8db1e4bcb666484ab7f05
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/support_cpp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/throw_exceptions.png b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/throw_exceptions.png
new file mode 100644
index 0000000000000000000000000000000000000000..18bba441594cedf14bb0cd9cd019f700ab19fab5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/figures/throw_exceptions.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/main.zip b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/main.zip
new file mode 100644
index 0000000000000000000000000000000000000000..bb87301a971408fdca0e211351816d47c4a76f5d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/main.zip differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/main/main.cpp b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/main/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4340da9596532d48ff6dc013867aebdd3d385438
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/cplusplus/main/main.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2019-05-23 tyustli first version
+ */
+
+#include
+
+int main(void)
+{
+ int count = 1;
+ while (count++)
+ {
+ rt_thread_mdelay(500);
+ rt_kprintf("hello rtthread\r\n");
+ }
+
+ return RT_EOK;
+}
+using namespace rtthread;
+
+class tran
+{
+public:
+ void getnumber(int a, int b)
+ {
+ x = a;
+ y = b;
+ }
+ void out(tran & s)
+ {
+ rt_kprintf("x = %d, y = %d\n", x, y);
+ }
+private:
+ int x, y;
+};
+
+int test_cpp(void)
+{
+ tran s;
+
+ s.getnumber(13, 54);
+ s.out(s);
+
+ return 0;
+}
+
+MSH_CMD_EXPORT(test_cpp, test cpp);
+
+#include
+
+#define MIN_VALUE (1e-4)
+#define IS_DOUBLE_ZERO(d) (abs(d) < MIN_VALUE)
+
+double div_func(double x, double y)
+{
+ if (IS_DOUBLE_ZERO(y))
+ {
+ throw y; /* throw exception */
+ }
+
+ return x / y;
+}
+
+void throw_exceptions(void *args)
+{
+ try
+ {
+ div_func(6, 3);
+ rt_kprintf("there is no err\n");
+ div_func(4, 0); /* create exception*/
+ rt_kprintf("you can run here\n");
+ }
+ catch(double) /* catch exception */
+ {
+ rt_kprintf("error of dividing zero\n");
+ }
+}
+
+MSH_CMD_EXPORT(throw_exceptions, throw cpp exceptions);
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0012-dfs.md b/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0012-dfs.md
new file mode 100644
index 0000000000000000000000000000000000000000..42eb81a338961c8ee010d38b720863041f90039d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0012-dfs.md
@@ -0,0 +1,240 @@
+# 在 STM32F429 上应用文件系统
+
+本文介绍了 RT-Thread 文件系统的基本知识和使用方法,帮助开发者更好地使用 RT-Thread 文件系统。并给出了在正点原子 `STM32F429-apollo` 开发板上验证的代码示例。
+
+## 简介
+
+第一次接触 RT-Thread 文件系统的开发者可能觉得 RT-Thread 文件系统过于复杂,不知道该从何入手。想要在项目中使用文件系统,却不知道该怎么做。本文将介绍 RT-Thread 文件系统的移植及文件系统的使用方法。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+## 文件系统的移植
+
+本次演示使用正点原子开发板 `STM32F429-Apollo` ,选择的文件系统类型是 `elm FatFS` 。由于 RT-Thread 自带了这个文件系统,所以移植工作较为简单,只需要通过 Env 工具对系统进行合适的配置既可。其他 RT-Thread 支持的文件系统,移植过程也是类似的,只需要对系统进行合适的配置即可使用。
+
+
+
+文件系统的移植主要包括下面几个方面:
+
+- 开启 / 配置 DFS 框架
+- 开启 / 配置 指定的文件系统
+- 确保开发板上的存储设备驱动正常工作
+
+通过 Env 工具可以方便地开启文件系统,将所需的文件系统类型添加到工程中。
+
+对存储设备进行功能测试,可以确保存储设备驱动是正常工作的。驱动程序的稳定工作是文件系统正常使用的基础。
+
+### 配置文件系统
+
+使用 Env 工具进入 `rt-thread\bsp\stm32f429-apollo` 目录,在命令行中输入 `menuconfig` 命令进入配置界面。
+
+在 `menuconfig` 配置界面依次选择 `RT-Thread Components → Device virtual file system`,如下图所示:
+
+
+
+进入到 DFS 的配置界面,开启下图所示的选项,就可以将 `FatFS` 添加到系统中。如图所示:
+
+
+
+这里需要注意的是还需要进入到 `elm-chan's FatFs, Generic FAT Filesystem Module` 选项中修改关于长文件名支持的选项,否则在后面使用文件系统的过程中,创建的文件或者文件夹的名称不能超过 8 个字符。修改方式如下图所示:
+
+
+
+
+
+设置文件系统扇区大小,表示可处理的最大字节数,这里设置为 4096 字节。设置的值需要根据存储器件手册确定,不能小于存储器件的最小可擦除扇区。
+
+
+
+因为要使用一些 C 库函数,所以需要打开 `libc` 功能:
+
+
+
+保存选项后即可退出,此时 `elm FatFS` 已经添加到项目中 。
+
+### 存储设备初始化
+
+#### 开启 SPI 设备驱动
+
+DFS 框架的文件系统实现层需要存储设备驱动层提供驱动接口用于对接,本次使用的存储设备为 `SPI Flash`。
+
+重新打开 menuconfig 配置界面,在 `RT-Thread Components → Device Drivers` 界面中选中 `Using SPI Bus/Device device drivers` 以及 `Using Serial Flash Universal Driver` 选项,如下图所示:
+
+
+
+为了方便地使用 shell 命令,我们在 `RT-Thread Components → Command shell` 选项中开启 `Using module shell` 选项,如下图所示:
+
+
+
+保存选项并退出,在 Env 中输入命令 `scons --target=mdk5 -s` 生成 mdk5 工程,编译并下载程序。
+
+#### 检查存储设备驱动
+
+在 stm32f429-apollo 开发板上 ` SPI Flash` 挂在了 SPI5 总线上,对应的 `SPI Device` 的设备名为 `spi50`。在终端输入 `list_device` 命令可以看到名为 `spi50` 的设备类型为 `SPI Device`,就说明 SPI 设备添加成功。如果没有出现相应的设备,则需要检查驱动程序,查找错误。
+
+
+
+为了确保该驱动工作正常,可以使用 `sf` 命令对该设备做 `benchmark` 测试。该功能由 `sfud` 组件提供,可以通过检查存储设备的读、写和擦除功能来判断存储设备的驱动程序是否正常。 如果像下图一样提示成功,所示则认为该驱动工作正常。如果无法通过测试,则需要检查驱动程序,使用逻辑分析仪对存储设备的接口波形进行分析。测试过程如下图:
+
+
+
+#### 创建存储设备
+
+由于只有块设备类型的设备才能和文件系统对接,所以需要根据 `SPI Device` 找到 `SPI Flash` 设备,并创建与其对应的 `Block Device`。
+
+这里需要使用到万能 SPI Flash 驱动库:[SFUD](https://github.com/armink/SFUD) ,RT-Thread 已经集成了该组件,在上面的配置过程中我们已经开启这个功能。此时只需要使用 SFUD 提供的 `rt_sfud_flash_probe` 函数即可。该函数将执行如下操作:
+
+ - 根据名为 `spi50` 的 `SPI Device` 设备找到对应的 `Flash` 存储设备。
+
+ - 初始化 `Flash` 设备。
+
+ - 在 Flash 存储设备上创建名为 `W25Q256` 的 `Block Device`。
+
+- 如果开启了组件自动初始化功能,该函数会被自动执行,否则需要手动调用运行。
+
+```c
+static int rt_hw_spi_flash_with_sfud_init(void)
+{
+ if (RT_NULL == rt_sfud_flash_probe("W25Q256", "spi50"))
+ {
+ return RT_ERROR;
+ };
+
+ return RT_EOK;
+}
+INIT_COMPONENT_EXPORT(rt_hw_spi_flash_with_sfud_init)
+```
+
+在终端输入 `list_device` 命令如果看到名为 `W25Q256` 的设备类型为 `Block Device`,这说明块设备已经创建成功,如果失败则需要对 `spi50` 设备进行检查。如下图所示:
+
+
+
+获得可以用于挂载的块类型设备,那么移植的工作就算完成了。
+
+## 文件系统的使用
+
+### 文件系统初始化
+
+RT-Thread 文件系统初始化过程一般按以下流程来进行:
+
+1. 初始化 DFS 框架
+2. 初始化具体文件系统
+3. 初始化存储设备
+
+下面我们按照这样的顺序来逐步讲解文件系统的初始化过程:
+
+#### DFS 框架的初始化
+
+DFS 框架的初始化主要是对内部数据结构以及资源的初始化。这一过程包括初始化文件系统必须的数据表,以及互斥锁。该功能由如下函数完成。如果开启了组件自动初始化功能,该函数会被自动执行,否则需要手动调用运行。
+
+
+
+#### 中间层文件系统的初始化
+
+这一步的初始化主要是将 `elm FatFS` 的操作函数注册到 DFS 框架中。该功能由如下函数完成。如果开启了组件自动初始化功能,该函数会被自动执行,否则需要手动调用运行。
+
+
+
+#### 存储设备的初始化
+
+存储设备的初始化可以参考创建存储设备小节。
+
+### 创建文件系统
+
+第一次使用 `SPI Flash` 作为文件系统地存储设备时,如果我们直接重启开发板来挂载文件系统,就会看到 `spi flash mount to /spi failed!` 的提示。这是因为此时在 SPI Flash 中还没有创建相应类型的文件系统,这就用到了创建文件系统 shell 命令:`mkfs`。
+
+`mkfs` 命令的功能是在指定的存储设备上创建指定类型的文件系统。使用格式为:`mkfs [-t type] device` 。第一次挂载文件系统前需要使用 `mkfs` 命令在存储设备上创建相应的文件系统,否则就会挂载失败。如果要在 `W25Q256` 设备上创建 `elm` 类型的文件系统,就可以使用 `mkfs -t elm W25Q256` 命令,使用方法如下图:
+
+
+
+文件系统创建完成后需要重启设备。
+
+### 文件系统的挂载
+
+文件系统的挂载指的是将文件系统和具体的存储设备关联起来,并挂载到某个挂载点,这个挂载点即为这个文件系统的根目录。在下面的示例中,我们将 `elm FatFS` 文件系统和名为 `W25Q256` 的存储设备关联起来,并且挂载到 `/spi` 文件夹中。(这里可以挂载到 `/spi` 文件夹的原因是 `stm32f429-apollo BSP` 的文件系统根目录已经挂载了 `RomFS`,并且已经创建了 `/spi` 文件夹。如果没有特殊情况,文件系统可以直接挂载到根目录 `/` 上。)
+
+挂载文件系统的操作由 `dfs_mount()` 函数完成,`dfs_mount()` 函数的参数分别为:块设备名、文件系统挂载点路径、挂载文件系统类型、读写标志位以及文件系统的私有数据,使用方法如下图所示:
+
+
+
+经过了上面的创建文件系统操作,我们重启开发板(会自动重新执行挂载函数),就可以成功地挂载文件系统了。可以看到提示 `spi flash mount to /spi !` 。这时再次使用 `list_device` 命令可以看到 `W25Q256` 设备已经被挂载成功。如下图所示:
+
+
+
+到这一步为止,文件系统已经初始化完成,接下来可以对文件和目录进行操作了。
+
+### FinSH 命令
+
+在这一小节介绍关于文件和目录操作常用的 shell 命令:
+
+使用 `ls` 命令显示文件和目录的信息:
+
+
+
+使用 `cd` 命令切换到指定工作目录:
+
+
+
+使用 `cp` 命令 copy 文件:
+
+
+
+使用 `rm` 命令删除文件或目录:
+
+
+
+使用 `mv` 命令将文件移动位置或者改名:
+
+
+
+使用 `echo` 命令将指定内容写入文件:
+
+
+
+使用 `cat` 命令展示文件的内容:
+
+
+
+使用 `pwd` 命令打印出当前目录地址:
+
+
+
+使用 `mkdir` 命令创建文件夹:
+
+
+
+### 文件操作示例
+
+本节以创建文件夹操作为例,介绍如何使用 RT-Thread 文件系统 Sample 来对文件系统进行操作。
+
+- 在 `menuconfig` 配置界面依次选择 `RT-Thread online packages → miscellaneous packages → filesystem sample options`,选中 `[filesystem] mkdir` 选项,如下图所示:
+
+
+
+- 保存并退出后,使用 `pkgs --update` 命令更新软件包,然后使用 `scons --target=mdk5 -s` 命令重新生成工程。可以看到该 Sample 已经添加到工程中:
+
+
+
+- 这里需要注意的是由于我们文件系统的根目录挂载了 `RomFS`,不可修改,所以我们不能直接在根目录创建文件夹。因此,我们需要对程序进行简单的修改,如下图所示:
+
+
+
+- 重新编译后下载运行,在 msh 中可以使用 `mkdir_sample_init` 命令来创建 web 文件夹,效果如下图所示:
+
+
+
+- 此时切换到 `/spi` 文件夹中可以看到 web 文件夹已经被创建。
+
+
+
+- 文件系统提供的 Sample 还有 `openfile`、`readwrite`、`stat`、`rename`、`opendir`、`readdir` 、`tell_seek_dir`,大家可以用上面的方法来使用这些功能。
+
+## 参考资料
+
+* [《虚拟文件系统》](../../../programming-manual/filesystem/filesystem.md)
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0027-littlefs.md b/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0027-littlefs.md
new file mode 100644
index 0000000000000000000000000000000000000000..d4b3dae96208d4aaf6b6f1209b7e95a54a3d990b
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/dfs/an0027-littlefs.md
@@ -0,0 +1,242 @@
+# 在 STM32L4 上应用 littlefs 文件系统
+
+本文介绍了 littlefs 文件系统的基本知识和使用方法,帮助开发者更好地使用 littlefs 文件系统。并给出了基于 FAL 移植的代码示例。
+
+## littlefs 简介
+
+littlefs 是 ARM 官方推出的,专为嵌入式系统设计的文件系统,相比传统的文件系统,littlefs 具有以下优点:
+
+- 自带擦写均衡
+- 支持掉电保护
+- 占用的 RAM/ROM 少
+
+littlefs 自带的擦写均衡和掉电保护使开发者可以放心的将文件系统挂载到 nor flash 上。
+
+## 名词解释
+
+在 RT-Thread 上使用 littlefs,会依赖一些软件包或者系统组件,下面会对这些需要用到的名词做出解释:
+
+1. DFS 框架
+
+ [DFS 框架](https://www.rt-thread.org/document/site/programming-manual/filesystem/filesystem/) 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统。DFS 框架为应用程序提供统一的 POSIX 文件和目录操作接口,如 read、write、poll/select 等。DFS 框架支持多种类型的文件系统,如 FatFS、RomFS、DevFS 等,并提供普通文件、设备文件、网络文件描述符的管理。
+
+1. MTD 设备
+
+ MTD 设备,全称为 Memory Technology Device,使用 MTD 设备为 NOR FLASH 和 NAND FLASH 提供统一接口,将文件系统与底层 FLASH 存储器进行了隔离。
+
+1. fal 组件
+
+ [fal](https://github.com/RT-Thread-packages/fal) 全称为 Flash Abstraction Layer,即 Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API。并提供了将分区创建成 MTD 设备的 API。
+
+1. SFUD 组件
+
+ [SFUD](https://github.com/armink/SFUD) 是一款开源的串行 SPI Flash 通用驱动库。现有市面的大部分串行 Flash,用户只需要提供 SPI 或 QSPI 的读写接口,SFUD 就可以识别并驱动。同时 RT-Thread 提供了 FAL 针对 SFUD 的驱动移植,可以使两个组件无缝连接。
+
+## 层级关系
+
+littlefs 在 RT-Thread 上运行的层级关系图如下所示:
+
+
+
+开发者使用的是 DFS 框架提供的统一的 POSIX API,DFS 框架会调用 littlefs 的 API,littlefs 会使用 MTD 设备的读写接口,开发者可以使用 RT-Thread 提供的 fal 组件和 SFUD 组件来完成对 FLASH 的读写任务,也可以自己实现 MTD 设备的驱动程序,使 littlefs 可以挂载到更多的存储介质上。
+
+## littlefs 的移植
+
+社区热心开发者 [geniusgogo](https://github.com/geniusgogo) 为 RT-Thread 移植了 [littlefs](http://packages.rt-thread.org/itemDetail.html?package=littlefs) 的软件包,开发者只需要提供一个 MTD 设备即可使用 littlefs。开发者可以自己实现一个 MTD 设备,也可以利用 RT-Thread 提供的 fal 组件,非常方便的创建出一个 MTD 设备。相比于直接使用 flash 底层函数来自己构造一个 MTD 设备,使用 fal 创建 MTD 设备有以下三个优点:
+
+- 创建 MTD 设备方便
+- 驱动的可移植性和可重用性强
+- fal 的分区功能可以让 littlefs 只使用指定区域的 flash
+
+fal 的详细介绍可以点击 [此处](https://github.com/RT-Thread-packages/fal) 查看。
+
+本次演示使用 stm32l475-atk-pandora,由于 RT-Thread 有 littlefs 软件包,所以移植工作较为简单。littlefs 的移植主要包括下面几个方面:
+
+- 开启 / 配置 DFS 框架
+ - 为应用程序提供统一的 POSIX 文件和目录操作接口
+- 使能 littlefs 软件包
+- 使能 MTD 设备
+ - 默认不开启
+- 使能 fal
+ - 用来创建 MTD 设备
+- 使能外置 flash
+ - 会自动启用 SFUD
+ - 文件系统的物理载体
+- 创建 MTD 设备
+- 确保开发板上的存储设备驱动正常工作
+
+### 使能DFS框架
+
+在 BSP 目录 `rt-thread\bsp\stm32\stm32l475-atk-pandora` 下打开 env,输入 menuconfig,在 ` RT-Thread Components → Device virtual file system` 中打开 DFS 框架。
+
+
+
+### 配置 littlefs
+在 `RT-Thread online packages → system packages → Littlefs: A high-integrity embedded file system` 中打开 littlefs。
+
+
+
+### 使能 MTD 设备
+
+在 `RT-Thread Components → Device Drivers` 中使能 MTD 设备。
+
+
+
+### 配置 fal
+
+在 `RT-Thread online packages → system packages → fal: Flash Abstraction Layer implement` 中打开 fal。使能 `FAL uses SFUD drivers`,并修改 `FLASH device name` 为 `W25Q128`(SFUD 初始化 FLASH 后创建的设备名)。
+
+
+
+### 使能外置 FLASH
+
+在 `Hardware Drivers Config → Onboard Peripheral Drivers` 中使能外置 flash。
+
+
+
+### 更新软件包并生成工程
+
+设置完成后,退出配置界面,在 env 中输入 pkgs --update,env 会自动把需要用到的软件包下载下来。然后在 env 中输入 scons --target=mdk5,等待自动生成 mdk5 的工程。
+
+### 检查分区表
+
+打开工程中的 fal_cfg.h,检查宏 `SPI_FLASH_PARTITION` 是否如下所示:
+
+```c
+#define SPI_FLASH_PARTITION {FAL_PART_MAGIC_WROD, "filesystem", "W25Q128", 9 * 1024 * 1024, 16 * 1024 * 1024, 0},
+```
+
+默认的 filesystem 分区起始地址是 FLASH 零地址往后偏移 9MB,这是为了兼容 RT-Thread 提供的针对潘多拉开发板的一系列例程。如果开发者需要更大的分区,可以参考 fal 软件包的介绍自行修改,这里不再赘述。
+
+### 创建 MTD 设备并挂载文件系统
+
+fal 组件并没有加入自动初始化的代码,所以我们需要在 main 函数中初始化 fal,并使用 fal 提供的 API 来创建一个 MTD 设备。创建 MTD 设备后,就可以将 littlefs 挂载到刚刚生成的 MTD 设备上了。
+
+在 main.c 文件中添加的代码如下所示:
+
+```c
+...
+/* 添加 fal 头文件 */
+#include
+/* 添加文件系统头文件 */
+#include
+
+/* 添加 DEBUG 头文件 */
+#define DBG_SECTION_NAME "main"
+#define DBG_LEVEL DBG_INFO
+#include
+/* 定义要使用的分区名字 */
+#define FS_PARTITION_NAME "filesystem"
+
+int main(void)
+{
+ ...
+ struct rt_device *mtd_dev = RT_NULL;
+
+ ...
+ /* 初始化 fal */
+ fal_init();
+ /* 生成 mtd 设备 */
+ mtd_dev = fal_mtd_nor_device_create(FS_PARTITION_NAME);
+ if (!mtd_dev)
+ {
+ LOG_E("Can't create a mtd device on '%s' partition.", FS_PARTITION_NAME);
+ }
+ else
+ {
+ /* 挂载 littlefs */
+ if (dfs_mount(FS_PARTITION_NAME, "/", "lfs", 0, 0) == 0)
+ {
+ LOG_I("Filesystem initialized!");
+ }
+ else
+ {
+ /* 格式化文件系统 */
+ dfs_mkfs("lfs", FS_PARTITION_NAME);
+ /* 挂载 littlefs */
+ if (dfs_mount("filesystem", "/", "lfs", 0, 0) == 0)
+ {
+ LOG_I("Filesystem initialized!");
+ }
+ else
+ {
+ LOG_E("Failed to initialize filesystem!");
+ }
+ }
+ }
+
+ while (1)
+ {
+ ...
+ }
+
+ return RT_EOK;
+}
+
+```
+
+### 验证 fal 分区和 MTD 设备
+
+移植工作已经完成,我们需要将代码下载到开发板,检查 fal 分区是否正常,MTD 设备是否创建成功,文件系统有没有挂载成功。将工程编译下载,查看系统启动时是否打印了 fal 分区、 MTD 设备创建成功和文件系统初始化成功的提示信息。
+
+分区表的打印和 MTD 设备创建成功的提示信息如下所示:
+
+```c
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.1 build Mar 26 2019
+ 2006 - 2019 Copyright by rt-thread team
+[D/drv.qspi] qspi init succsee!
+[SFUD] Find a Winbond flash chip. Size is 16777216 bytes.
+[SFUD] W25Q128 flash device is initialize success.
+[D/drv.qspi] qspi init succsee!
+[D/FAL] (fal_flash_init:61) Flash device | W25Q128 | addr: 0x00000000 | len: 0x01000000 | blk_size: 0x00001000 |initialized finish.
+[I/FAL] ==================== FAL partition table ====================
+[I/FAL] | name | flash_dev | offset | length |
+[I/FAL] -------------------------------------------------------------
+[I/FAL] | filesystem | W25Q128 | 0x00900000 | 0x01000000 |
+[I/FAL] =============================================================
+[I/FAL] RT-Thread Flash Abstraction Layer (V0.4.0) initialize success.
+[I/FAL] The FAL MTD NOR device (filesystem) created successfully
+[I/main] Filesystem initialized!
+```
+
+## 文件系统的使用
+
+RT-thread 支持的文件系统很多,但是所有的文件系统都对接到 DFS 框架。对于上层应用,DFS 框架提供了统一的 POSIX 文件和目录操作接口。开发者更换文件系统后,可以将原来的代码无缝的移植到新的文件系统上而不需要修改代码。有关文件系统的示例代码可以点击 [此处](https://www.rt-thread.org/document/site/programming-manual/filesystem/filesystem/#dfs_4) 查看。
+
+## FinSH 命令
+
+启用 DFS 框架后,RT-Thread 的 FinSH 控制台就会新添加一些命令,我们可以利用这些命令快速的来验证文件系统是否正常工作。
+
+使用 `ls` 命令查看当前目录信息,运行结果如下所示:
+
+```msh
+msh />ls # 使用 ls 命令查看文件系统目录信息
+Directory /: # 可以看到已经存在根目录 /
+```
+
+使用 `echo` 命令将输入的字符串输出到指定输出位置,运行结果如下所示:
+
+```msh
+msh />echo "hello RT-Thread!!!" hello.txt # 将字符串出输出到 hello.txt 文件
+msh />ls
+Directory /:
+hello.txt 18
+msh />
+```
+
+使用 `cat` 命令查看文件内容,运行结果如下所示:
+
+```msh
+msh />cat hello.txt # 查看 hello.txt 文件的内容并输出
+hello RT-Thread!!!
+```
+
+## 参考资料
+
+* [《虚拟文件系统》](../../../programming-manual/filesystem/filesystem.md)
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
+
+* [《在 STM32F429 上应用文件系统》](./an0012-dfs.md)
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528354745063.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528354745063.png
new file mode 100644
index 0000000000000000000000000000000000000000..878f3371f7fcb2592dbda99955949f326b724acf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528354745063.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528355204158.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528355204158.png
new file mode 100644
index 0000000000000000000000000000000000000000..d40470ca5a3b0316e417e5e4969dbe88065d4869
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528355204158.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528355291434.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528355291434.png
new file mode 100644
index 0000000000000000000000000000000000000000..984ce56767759f1ac53fe1c670959fb96b413f3a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528355291434.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528356393167.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528356393167.png
new file mode 100644
index 0000000000000000000000000000000000000000..a0913beec775946a93128a21aad2e228a67136e7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528356393167.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528356861018.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528356861018.png
new file mode 100644
index 0000000000000000000000000000000000000000..dfd188e83d474d9291d11d7150a8398e82f14c66
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528356861018.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528362120705.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528362120705.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1713c134b80f3383322e6912b87fc91d032e1cf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528362120705.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528416425397.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528416425397.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd17fe40fd0abf597da69cdad27537e0195a3784
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528416425397.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528417402476.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528417402476.png
new file mode 100644
index 0000000000000000000000000000000000000000..925f7461f33d5bd9e58170400b3d530e1dc05853
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528417402476.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419313880.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419313880.png
new file mode 100644
index 0000000000000000000000000000000000000000..66ef837d02245674cba3e1fc63b8a68d91092d88
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419313880.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419330733.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419330733.png
new file mode 100644
index 0000000000000000000000000000000000000000..c71ce97f3cff0d549586508fe1780c2c617b95c0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419330733.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419369526.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419369526.png
new file mode 100644
index 0000000000000000000000000000000000000000..a00b6a20d087c05f5ad304091fb45f60342b0df1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419369526.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419422313.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419422313.png
new file mode 100644
index 0000000000000000000000000000000000000000..04c6079dca87f042102b650cdd021811d57384f7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419422313.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419519371.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419519371.png
new file mode 100644
index 0000000000000000000000000000000000000000..c2e5dc12a9a75b8055b93ecc2fbb42cd846eb5ab
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419519371.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419537678.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419537678.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a8383eac58e3339d04f82cab648b7c9470052a7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419537678.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419554110.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419554110.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6b34a6ae504ff6bc5b1df748908e86f19142c1c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419554110.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419584942.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419584942.png
new file mode 100644
index 0000000000000000000000000000000000000000..921e0411927dc4826865ee1af97329257c34afaa
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528419584942.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528420167135.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528420167135.png
new file mode 100644
index 0000000000000000000000000000000000000000..c88365ca327c5eaa78418dad4309ba856369b537
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528420167135.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528421204197.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528421204197.png
new file mode 100644
index 0000000000000000000000000000000000000000..0031fd31d3523b7063223a867581d076646dc3ce
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528421204197.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423169454.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423169454.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6a4d73cda5aa69d029e6a271952c5c978e467f5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423169454.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423305271.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423305271.png
new file mode 100644
index 0000000000000000000000000000000000000000..dea73bd76b52d6c96c1517a003a37421a76de004
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423305271.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423429971.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423429971.png
new file mode 100644
index 0000000000000000000000000000000000000000..f910a8d6a85b2a69be1dfb4be4a1e111a2fe1994
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528423429971.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528449652153.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528449652153.png
new file mode 100644
index 0000000000000000000000000000000000000000..e91b1dbee5b681ad4e9f40d42f712550ecba13d6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528449652153.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528449677236.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528449677236.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e415d403b195859f6fd65d192d061ae0415eec4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528449677236.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528450972107.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528450972107.png
new file mode 100644
index 0000000000000000000000000000000000000000..7528808f82a738495258e521a6aac2eaeb535230
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528450972107.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528451222822.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528451222822.png
new file mode 100644
index 0000000000000000000000000000000000000000..fcb0343808c5b4e1469aec50823cc336793aaef8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528451222822.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528451278720.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528451278720.png
new file mode 100644
index 0000000000000000000000000000000000000000..c618127c53b123668c86c01eaa9b4faeeab69650
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528451278720.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528457058001.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528457058001.png
new file mode 100644
index 0000000000000000000000000000000000000000..da383bdc97e2f77e804dd79d3ede3cdfc18b6fce
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528457058001.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528459997690.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528459997690.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c8d59beab83592d708e58b9f37fc182bba7a298
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528459997690.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528460010565.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528460010565.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c8d59beab83592d708e58b9f37fc182bba7a298
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528460010565.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528680175178.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528680175178.png
new file mode 100644
index 0000000000000000000000000000000000000000..82ca239d8f39ab586d2f8b8a52c245f5f2f2ba28
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/1528680175178.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/dfs.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/dfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..85db1cbad52399071edd7fde137ce67d3b7ae28f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/dfs.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/fal.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/fal.png
new file mode 100644
index 0000000000000000000000000000000000000000..bef1309e777b97ab31cb47aaf37197e3bd54ed78
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/fal.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/flash.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/flash.png
new file mode 100644
index 0000000000000000000000000000000000000000..157f8aa08ea465d1cbe6e7903a491d71e60e2ec2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/flash.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/hierarchical.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/hierarchical.png
new file mode 100644
index 0000000000000000000000000000000000000000..15c9cf2f6af632df96c6848b06db2238c52fefc1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/hierarchical.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/littlefs.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/littlefs.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a742524a7b67e407d681d8d8df00b3357ac867a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/littlefs.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/mtd_device.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/mtd_device.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8f3606cd95737f92cbc186a8bb9e52dc5e3b580
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/mtd_device.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/rt_fs_structure.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/rt_fs_structure.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc652a5ea08a3d353e6a078df6c55c3ca6da1dc2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/rt_fs_structure.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/set_sector_size.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/set_sector_size.png
new file mode 100644
index 0000000000000000000000000000000000000000..6b319de52590604e9b43e5491325f0e571458479
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/set_sector_size.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/stm32f429-apollo.png b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/stm32f429-apollo.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab9ffea70c8ecea4fdf9f218b1f56570dd02f1ba
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dfs/figures/stm32f429-apollo.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/an0023-dlmodule.md b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/an0023-dlmodule.md
new file mode 100644
index 0000000000000000000000000000000000000000..2681684e05a4c96eb3023140fadbb9c1867dad3d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/an0023-dlmodule.md
@@ -0,0 +1,348 @@
+# 使用 QEMU 运行动态模块组件 #
+
+本文描述了在 Windows 平台使用 QEMU 运行 RT-Thread 动态模块及动态库。
+
+## 简介
+
+RT-Thread 动态模块组件 `dlmodule` 提供了动态加载程序模块的机制。dlmodule 组件更多的是一个 ELF 格式加载器,把单独编译的一个 elf 文件的代码段,数据段加载到内存中,并对其中的符号进行解析,绑定到内核导出的 API 地址上。动态模块 elf 文件主要放置于 RT-Thread 下的文件系统上。
+
+RT-Thread 的动态模块组件目前支持两种格式:
+
+* `.mo` 则是编译出来时以 `.mo` 做为后缀名的可执行动态模块。它可以被加载,并且系统中会自动创建一个主线程执行这个动态模块中的 `main` 函数;同时这个 `main(int argc, char** argv)` 函数也可以接受命令行上的参数。
+* `.so` 则是编译出来时以 `.so` 做为后缀名的动态库。它可以被加载,并驻留在内存中,并提供一些函数集由其他程序(内核里的代码或动态模块)来使用。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+* [rtthread-apps](https://github.com/RT-Thread/rtthread-apps)
+
+> [!NOTE]
+> 注:动态模块组件部分源代码编译当前只支持 GNU GCC 工具链编译,暂不支持 MDK 或者 IAR。
+
+## 使能动态模块组件
+
+### 配置工程
+
+在 Env 控制台切换到 qemu-vexpress-a9 BSP 根目录,然后输入 `menuconfig` 命令打开配置菜单。
+
+
+
+进入“ RT-Thread Components → POSIX layer and C standard library”菜单,按下图箭头所示打开 libc 和动态模块的配置选项。
+
+
+
+进入“RT-Thread Components → Device virtual file system”菜单打开文件系统的配置选项。退出 menuconfig 并保存配置。
+
+
+
+### 编译工程
+
+使用 `scons` 命令编译工程。
+
+
+
+### 运行动态模块命令
+
+编译完成后使用 `qemu.bat` 命令运行工程。按 Tab 键查看所有命令可以看到动态模块的两个命令 `list_module` 和 `list_symbols`,表明动态模块组件配置成功。
+
+* `list_module` 命令可以查看当前正在运行的动态模块。
+* `list_symbols` 命令可以查看动态模块可以使用的函数及其对应的内存地址。加载动态模块的时候会对其中的符号进行解析,并绑定到对应的函数地址上。
+
+
+
+### 生成动态模块编译依赖环境
+
+关闭运行的程序,在 Env 控制台使用 `scons --target=ua -s` 命令生成编译动态模块时需要包括的内核头文件搜索路径及全局宏定义。
+
+
+
+## 运行动态模块
+
+### 运行最简单的动态模块
+
+#### 创建动态模块
+
+##### 获取示例
+
+ 下载 RT-Thread 动态模块工具库 [rtthread-apps](https://github.com/RT-Thread/rtthread-apps),rtthread-apps 的 tools 目录放置了编译动态模块需要使用到的 Python 和 SConscript 脚本。hello 目录下的 main.c 是一个简单的动态模块使用示例,源代码如下所示。
+
+```
+#include
+
+int main(int argc, char *argv[])
+{
+ printf("Hello, world\n");
+
+ return 0;
+}
+```
+
+这段代码实现了一个最简单的 main 函数,打印字符串“Hello world”。
+
+##### 设置环境变量
+
+在 Env 控制台切换到 rtthread-apps 根目录(目录所在全路径不包含空格和中文字符),然后通过下面 2 条命令设置环境变量。
+
+* `set RTT_ROOT=d:\repository\rt-thread`,设置 RTT_ROOT 为 RT-Thread 源代码根目录。
+* `set BSP_ROOT=d:\repository\rt-thread\bsp\qemu-vexpress-a9`,设置 BSP_ROOT 为 qemu-vexpress-a9 BSP 根目录。
+
+
+
+##### 编译动态模块
+
+使用 `scons --app=hello` 命令编译动态模块。
+
+
+
+在 rtthread-apps/hello 目录下会生成动态模块文件 hello.mo。
+
+#### 将动态模块放入文件系统
+
+编译好的动态模块 hello.mo 需要放到文件系统下。qemu-vexpress-a9 BSP 会使用一个虚拟的 sd 卡设备 sd.bin,我们需要把动态模块放到这个虚拟的 sd 卡里面。对于物理设备来说,直接将动态模块添加到文件系统管理的存储设备中就可以。这里需要使用到 Env 工具里面的一个小工具 fatdisk,它位于 Env 的 tools 目录下,里面也提供了一份 fatdisk 的使用说明。这里使用 fatdisk 用于把 PC 上本地的一个目录转换成 sd.bin 映像文件,这个映像文件是做为一个 fat 文件系统而存在。
+
+##### 新建目录
+
+在 fatdisk 目录下新建一个 sd 目录,并复制刚刚编译的动态模块 hello.mo 文件到 sd 目录。
+
+
+
+##### 修改配置文件
+
+按照下面的配置修改 fatdisk 目录下的配置文件 fatdisk.xml。
+
+* 映像文件空间大小 disk_size 配置为了 5120Kbytes(大小可根据需要配置)。
+* 映像文件的扇区大小 sector_size 需要配置为 512 KBytes。
+* 要转换目录名 root_dir 配置为 sd,表示当前目录下的 sd 目录。
+* 指定生成的映像文件名称 output 配置为 sd.bin。
+* strip 需要配置为 0。
+
+```
+
+
+ 5120
+ 512
+ sd
+
+ 0
+
+```
+
+##### 生成映像文件
+
+在 Env 控制台切换到 fatdisk 根目录,运行 `fatdisk` 命令则会按照配置文件 fatdisk.xml 中的配置,把里面指定的目录转换成 flash 映像文件。
+
+
+
+运行成功则会在 fatdisk 目录生成一个 sd.bin 文件,大小为 5MB。
+
+
+
+生成的映像文件 sd.bin 需要复制到 qemu-vexpress-a9 BSP 目录。
+
+#### 运行动态模块
+
+在 Env 控制台切换到 qemu-vexpress-a9 BSP 根目录输入 `qemu.bat` 命令运行工程。
+
+
+
+* 系统运行起来后会看到文件系统初始化成功信息 “file system initialization done!”。
+* 使用 `ls` 命令可以看到根目录下的动态模块文件 `hello.mo`。
+* 输入 `hello` 命令运行动态模块 hello.mo。可以看到动态模块main函数打印的字符串“Hello,world”
+
+使用动态模块组件运行动态模块的主要原理如下图所示:
+
+
+
+### 动态模块的初始化和清理函数
+
+动态模块组件提供了2个扩展的函数供用户使用,分别是`module_init()`和`module_cleanup()`。
+
+* `module_init()`函数会在动态模块运行前被执行,用户可以根据需要做一些初始化工作。
+* `module_cleanup()`函数会在动态模块运行结束后在 idle 线程里回调一次,执行用户设置的清理工作。
+
+RT-Thread 系统会自动创建一个线程执行动态模块中的 main 函数,同时这个 `main(int argc, char* argv[])` 函数也可以接受命令行上的参数。这个线程默认的优先级等同空闲线程的优先级,线程堆栈默认为2048字节。用户可以在`module_init()`函数里修改这个线程的优先级和堆栈。
+
+#### 示例代码
+
+基于前面简单的动态模块示例代码 main.c 增加`module_init()`和`module_cleanup()`函数的使用,示例代码如下所示。
+
+```
+#include
+#include
+
+/* 动态模块的初始化函数 */
+void module_init(struct rt_dlmodule *module)
+{
+ module->priority = 8;
+ module->stack_size = 4096;
+
+ printf("this is module %s initial function!\n",module->parent.name);
+}
+
+/* 动态模块的清理函数 */
+void module_cleanup(struct rt_dlmodule *module)
+{
+ printf("this is module %s cleanup function!\n",module->parent.name);
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ printf("hello world from RTT::dynamic module!\n");
+
+ /* 打印命令行参数 */
+ for(i = 0;i < argc;i ++)
+ {
+ printf("argv[%d]:%s\n",i,argv[i]);
+ }
+
+ return 0;
+}
+```
+
+示例代码主要实现了如下功能:
+
+* 在动态模块的初始化函数里可以设置这个线程的优先级和堆栈。
+* 清理函数简单的打印信息。
+* main 函数解析命令行参数并打印出来。
+
+请参考前面小节将此示例代码生成的动态模块文件放到文件系统里,并将生成的映像文件 sd.bin 复制到 qemu-vexpress-a9 BSP 目录。
+
+#### 运行结果
+
+在 Env 控制台切换到 qemu-vexpress-a9 BSP 根目录输入 `qemu.bat` 命令运行工程。
+
+
+
+* 系统运行起来后会看到文件系统初始化成功信息 “file system initialization done!”。
+* 使用 `ls` 命令可以看到根目录下的动态模块文件 `hello.mo`。
+* 输入 `hello this is rt-thread!` 命令运行动态模块 hello.mo。hello 后面的字符串为参数。
+* 执行到动态模块初始化函数 `module_init` 时会打印字符串 "this is module hello initial function!"。
+* 执行动态模块的 main 函数时会打印字符串 “hello world from RTT::dynamic module!”,命令行参数也依次打印了出来。
+* 动态模块运行结束后又执行清理函数 `module_cleanup`,打印字符串 "this is module hello cleanup function!"。
+
+## 运行动态库
+
+### 创建动态库
+
+#### 获取示例
+
+ 下载 RT-Thread 动态模块工具库 [rtthread-apps](https://github.com/RT-Thread/rtthread-apps),rtthread-apps 的 lib 目录下有一个简单的动态库示例的 lib.c,源代码如下所示,它实现了 2 个简单的函数供使用。
+
+```
+#include
+
+int lib_func(void)
+{
+ printf("hello world from RTT::dynamic library!\n");
+
+ return 0;
+}
+
+int add_func(int a, int b)
+{
+ return (a + b);
+}
+```
+
+#### 编译动态库
+
+编译动态库之前需要先设置环境变量。然后使用 `scons --lib=lib` 命令编译动态库。
+
+
+
+在 rtthread-apps/lib 目录下会生成动态库文件 lib.so。
+
+请参考前面小节将动态库文件 lib.so 放到文件系统里,并将生成的映像文件 sd.bin 复制到 qemu-vexpress-a9 BSP 目录。
+
+### 运行动态库
+
+#### 添加示例代码
+
+将以下示例代码添加到 qemu-vexpress-a9 BSP applications 目录下的 main.c 里。
+
+```
+#include
+#include
+#include
+
+/* 动态库文件路径 */
+#define APP_PATH "/lib.so"
+
+/* 函数指针类型 */
+typedef int (*add_func_t)(int, int);
+typedef void (*lib_func_t)(void);
+
+int dlmodule_sample(void)
+{
+ void* handle;
+ lib_func_t lib_function;
+ add_func_t add_function;
+ /* 以RTLD_LAZY模式打开动态库文件,并获取动态库操作句柄 */
+ handle = dlopen(APP_PATH,RTLD_LAZY);
+
+ if(!handle)
+ {
+ printf("dlopen %s failed!\n",APP_PATH);
+ return -1;
+ }
+
+ /* 根据动态库操作句柄handle,返回动态库函数lib_func()对应的地址 */
+ lib_function = (lib_func_t)dlsym(handle,"lib_func");
+ if(!lib_function)
+ {
+ printf("dlsym %p failed!\n",handle);
+ return -1;
+ }
+ /* 运行动态库函数 */
+ lib_function();
+ /* 根据动态库操作句柄handle,返回动态库函数add_func()对应的地址 */
+ add_function = (add_func_t)dlsym(handle,"add_func");
+ if(!add_function)
+ {
+ printf("dlsym %p failed!\n",handle);
+ return -1;
+ }
+ /* 运行动态库函数计算 3+4 并打印结果 */
+ printf("add_function result is:%d\n",add_function(3,4));
+ /* 运行完毕根据操作句柄handle关闭动态库 */
+ dlclose(handle);
+
+ return 0;
+}
+
+MSH_CMD_EXPORT(dlmodule_sample, dlmodule sample);
+
+int main(void)
+{
+ printf("hello rt-thread!\n");
+
+ return 0;
+}
+```
+
+RT-Thread 动态模块组件也支持 POSIX 标准的 libdl API,此示例代码调用 libdl API 运行动态库。示例代码首先根据动态库的路径打开动态库文件 lib.so,然后获取动态库的 lib_func() 函数的地址并运行此函数。之后获取动态库的 add_func() 函数的地址,并传入参数 3 和 4 运行函数计算结果。最后关闭动态库。
+
+#### 运行动态库
+
+在 Env 控制台切换到 qemu-vexpress-a9 BSP 根目录,输入 `scons` 命令重新编译工程。编译完成后输入 `qemu.bat` 命令运行工程。按 Tab 键可以看到新增的示例代码命令 `dlmodule_Sample`。
+
+
+
+使用 `ls` 命令可以看到根目录下的动态库文件 lib.so,输入 `dlmodule_sample` 命令就可以运行动态库示例代码。
+
+
+
+* 第一行运行了 lib_func() 函数打印了字符串 “hello world from RTT::dynamic library!”
+* 第二行运行了 add_func() 函数计算了 3+4 并打印了相加结果 7。
+
+## 参考资料
+
+* [《动态模块》](../../../programming-manual/dlmodule/dlmodule.md)
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/add-dlmodule.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/add-dlmodule.png
new file mode 100644
index 0000000000000000000000000000000000000000..3374f8924caf713f48deac0afed12cbf29679e67
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/add-dlmodule.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/add-hello.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/add-hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd56ae7b18df08c951f8b87b17b4bd8c706d4055
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/add-hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/dfs.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/dfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c77d3481b6f9e7ebb9e7741edd0a0ae3f20f40c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/dfs.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/enable-dlmodule.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/enable-dlmodule.png
new file mode 100644
index 0000000000000000000000000000000000000000..8a37ac0bee19aa8172c346bf8cef4e5041f046fa
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/enable-dlmodule.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/fatdisk-hello.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/fatdisk-hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab5688fb00a174e1e6a3062c782951a8f930d0b7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/fatdisk-hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/hello.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..32ccfa10aa81d96a16bde4d15afcf6f53142f483
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..a726b97b6f3555e5d14157a8c4182264fa725a62
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/relation.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/relation.png
new file mode 100644
index 0000000000000000000000000000000000000000..ca7fb00fbb0942562d41747efc343e5789c48dbd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/relation.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/run-lib.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/run-lib.png
new file mode 100644
index 0000000000000000000000000000000000000000..be8197eed3f29311c308f7e5075fe2df545315f9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/run-lib.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/run-lib2.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/run-lib2.png
new file mode 100644
index 0000000000000000000000000000000000000000..151f3a2037fc8a20124b9ee76694406a10248fe8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/run-lib2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/rundlmodule.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/rundlmodule.png
new file mode 100644
index 0000000000000000000000000000000000000000..70197de165dbfe76e6aece83954f23d80979dc09
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/rundlmodule.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-app.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-app.png
new file mode 100644
index 0000000000000000000000000000000000000000..9dfefb66709b78c7fcc0de40b6a8333e97777719
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-app.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-lib.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-lib.png
new file mode 100644
index 0000000000000000000000000000000000000000..c8db74071e92e7705d96f823eaccca8ac5aa1242
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-lib.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-ua.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-ua.png
new file mode 100644
index 0000000000000000000000000000000000000000..1735f56553dd8461a76821e83ab945e1ac919318
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons-ua.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa1acfc3a5d5e36759bf94c021b82eb10dac2971
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/scons.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/sd-bin-new.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/sd-bin-new.png
new file mode 100644
index 0000000000000000000000000000000000000000..81e96bcba821498a9a0879083e32ee72acf107f7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/sd-bin-new.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/set-env.png b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/set-env.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a4a6d35806de586d91b2aa960add3f4a2f87fdb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/dlmodule/figures/set-env.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/an0010-lwip-driver-porting.md b/rt-thread-version/rt-thread-standard/application-note/components/network/an0010-lwip-driver-porting.md
new file mode 100644
index 0000000000000000000000000000000000000000..c5ab7c1310f18063679419507896b497969f78d8
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/network/an0010-lwip-driver-porting.md
@@ -0,0 +1,722 @@
+# 网络协议栈驱动移植笔记 #
+
+本文描述了如何在 RT-Thread 中,如何根据具体的硬件配置网络驱动,并灵活运用调试手段解决问题。
+
+## 简介
+
+在 RT-Thread 所支持的 BSP 中,大部分都有支持以太网驱动。但具体到用户的硬件中,可能会和默认的代码有所差异。本文选择相对以太网驱动比较完善的 stm32 BSP,介绍了驱动的主要实现方式,以及针对不同硬件的修改方法。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+## 以太网相关概念简介
+
+### 常见的以太网芯片种类
+
+以太网芯片有很多种,大致可以分成 3 种:
+
+- 以太网芯片只有 PHY(物理接口收发器 ),需要单片机带 MAC(以太网媒体接入控制器 ),通过 MII 或者 RMII 接口和单片机通讯。例如 LAN8720。
+- 以太网芯片带 MAC 和 PHY,通过 SPI 接口和单片机通讯。例如 ENC28J60。
+- 以太网芯片带 MAC 和 PHY,通过 SPI 接口和单片机通讯,同时内置硬件协议栈,适合低速单片机。例如 W5500。
+
+### 常见名词解释
+
+[MAC](https://baike.baidu.com/item/MAC/329671):媒体介入控制层,属于 OSI 模型中数据链路层下层子层 。
+
+[PHY](https://baike.baidu.com/item/PHY):PHY 指物理层,OSI 的最底层。 一般指与外部信号接口的芯片。
+
+[MII](https://baike.baidu.com/item/MII/16017003):MII (Media Independent Interface),介质无关接口,也被称为媒体独立接口,它是 IEEE-802.3 定义的以太网行业标准,支持 10Mbit/s 和 100Mbit/s 数据传输模式 。
+
+[RMII](https://baike.baidu.com/item/RMII/8989175):RMII (Reduced Media Independent Interface) ,简化媒体独立接口,是 IEEE 802.3u 标准中除 MII 接口之外的另一种实现,支持 10Mbit/s 和 100Mbit/s 数据传输模式 。相比 MII,精简了引脚数量。
+
+[lwIP](https://baike.baidu.com/item/lwip):lwIP 是瑞典计算机科学院 (SICS) 的 Adam Dunkels 开发的一个小型开源的 [TCP/IP](https://baike.baidu.com/item/TCP%2FIP) 协议栈。实现的重点是在保持 TCP 协议主要功能的基础上减少对 RAM 的占用。
+
+pbuf:lwIP 中用来管理数据包的结构体。
+
+## 驱动架构图
+
+
+
+RT-Thread 的 lwIP 移植在原版的基础上,添加了网络设备层以替换原来的驱动层。和原来的驱动层不同的是,对于以太网数据的收发采用了独立的双线程结构,erx 线程和 etx 线程在正常情况下,两者的优先级设置成相同,用户可以根据自身实际要求进行微调以侧重接收或发送。
+
+### 数据接收流程
+
+
+
+当以太网硬件设备收到网络报文产生中断时,接收到的数据会被存放到接收缓冲区,然后以太网中断程序会发送邮件来唤醒 erx 线程,erx 线程会按照接收到的数据长度来申请 pbuf,并将数据放入 pbuf 的 payload 中,然后将 pbuf 通过邮件发送给 去处理。
+
+### 数据发送流程
+
+
+
+当有数据需要发送时,LwIP 会将数据通过邮件发送给 etx 线程,然后永久等待在 tx_ack 信号量上。etx 线程接收到邮件后,通过调用驱动中的 rt_stm32_eth_tx() 函数发送数据,发送完成之后再发送一次 tx_ack 信号量唤醒 LwIP 。
+
+> [!NOTE]
+> 注:在一定条件下也可以把 RT-Thread 中加入的 etx/erx 任务移除掉,当移除掉 RX_THREAD 时,需要由其他线程或中断把接收到的 pbuf 数据包提交给 lwIP 主任务,这里不做详细介绍。
+
+## 网络设备介绍
+
+RT-Thread 网络设备继承了标准设备,是由 eth_device 结构体定义的,这里贴出 eth_device 结构体代码提供参考
+
+```
+struct eth_device
+{
+ /* 标准设备 */
+ struct rt_device parent;
+
+ /* lwIP 网络接口 */
+ struct netif *netif;
+ /* 发送应答信号量 */
+ struct rt_semaphore tx_ack;
+
+ /* 网络状态标志 */
+ rt_uint16_t flags;
+ rt_uint8_t link_changed;
+ rt_uint8_t link_status;
+
+ /* 数据包收发接口 */
+ struct pbuf* (*eth_rx)(rt_device_t dev);
+ rt_err_t (*eth_tx)(rt_device_t dev, struct pbuf* p);
+};
+```
+
+本文以太网驱动比较完善 stm32f407 为例进行讲解,除 ST 固件库以外,需要实现以下驱动:
+
+### 标准设备接口
+
+标准设备接口需要提供给 eth_device 结构体中的 parent 元素:
+
+```c
+static rt_err_t rt_stm32_eth_init(rt_device_t dev);
+static rt_err_t rt_stm32_eth_open(rt_device_t dev, rt_uint16_t oflag);
+static rt_err_t rt_stm32_eth_close(rt_device_t dev);
+static rt_size_t rt_stm32_eth_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+static rt_size_t rt_stm32_eth_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
+static rt_err_t rt_stm32_eth_control(rt_device_t dev, int cmd, void *args);
+```
+
+` rt_stm32_eth_init` 用于初始化 DMA 和 MAC 控制器。
+
+`rt_stm32_eth_open` 用于上层应用打开网络设备,目前未使用到,直接返回 RT_EOK。
+
+`rt_stm32_eth_close` 用于上层应用关闭网络设备,目前未使用到,直接返回 RT_EOK。
+
+`rt_stm32_eth_read` 用于上层应用向底层设备进行直接读写的情况,对于网络设备,每个报文都有固定的格式,所以这个接口目前并未使用,直接返回 0 值。
+
+`rt_stm32_eth_write` 用于上层应用向底层设备进行直接读写的情况,对于网络设备,每个报文都有固定的格式,所以这个接口目前并未使用,直接返回 0 值。
+
+`rt_stm32_eth_control` 用于控制以太网接口设备,目前用于获取以太网接口的 mac 地址。如果需要,也可以通过增加控制字的方式来扩展其他控制功能。
+
+### 数据包收发接口
+
+对应了 eth_device 结构体中的 `eth_rx` 及 `eth_tx` 元素,实现数据包收发功能,如下所示:
+
+```c
+rt_err_t rt_stm32_eth_tx(rt_device_t dev, struct pbuf* p);
+struct pbuf *rt_stm32_eth_rx(rt_device_t dev);
+```
+
+`rt_stm32_eth_tx` 函数被 etx 线程调用,实现了数据发送的功能。这里贴出部分代码片段提供参考:
+
+```c
+rt_err_t rt_stm32_eth_tx(rt_device_t dev, struct pbuf *p)
+{
+ ...
+
+ /* 从 pbuf 复制数据到驱动中的缓冲区 */
+ for (q = p; q != NULL; q = q->next)
+ {
+ /* 获取当前 LWIP 缓冲区中的字节 */
+ byteslefttocopy = q->len;
+ payloadoffset = 0;
+
+ /* 检查要复制的数据长度是否大于 Tx 缓冲区大小 */
+ while ((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE)
+ {
+ /* 将数据复制到 Tx 缓冲区 */
+ memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset));
+
+ /* 指向下一个描述符 */
+ DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
+
+ buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
+
+ byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
+ payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
+ framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
+ bufferoffset = 0;
+ }
+
+ /* 复制剩余的字节 */
+ memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), byteslefttocopy);
+ bufferoffset = bufferoffset + byteslefttocopy;
+ framelength = framelength + byteslefttocopy;
+ }
+
+ LOG_D("transmit frame lenth :%d", framelength);
+
+ /* 等待锁 */
+ while (EthHandle.Lock == HAL_LOCKED);
+
+ /* 发送数据帧 */
+ state = HAL_ETH_TransmitFrame(&EthHandle, framelength);
+ if (state != HAL_OK)
+ {
+ LOG_E("eth transmit frame faild: %d", state);
+ }
+
+ ret = ERR_OK;
+
+ ...
+
+ return ret;
+}
+```
+
+`rt_stm32_eth_rx` 函数被 erx 线程调用,实现了接收数据的功能,这里贴出部分代码片段提供参考:
+
+```c
+struct pbuf *rt_stm32_eth_rx(rt_device_t dev)
+{
+ ....
+
+ /* 接收数据帧 */
+ state = HAL_ETH_GetReceivedFrame_IT(&EthHandle);
+ if (state != HAL_OK)
+ {
+ LOG_D("receive frame faild");
+ return NULL;
+ }
+
+ /* 获取数据长度 */
+ len = EthHandle.RxFrameInfos.length;
+ buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer;
+
+ LOG_D("receive frame len : %d", len);
+
+ if (len> 0)
+ {
+ /* 分配 pbuf */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+ }
+
+ if (p != NULL)
+ {
+ dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc;
+ bufferoffset = 0;
+ for (q = p; q != NULL; q = q->next)
+ {
+ byteslefttocopy = q->len;
+ payloadoffset = 0;
+
+ /* 检查当前 pbuf 中要复制的字节长度是否大于 rx 缓冲区大小 */
+ while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE)
+ {
+ /* 将数据复制到 pbuf */
+ memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
+
+ /* 指向下一个描述符 */
+ dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
+ buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);
+
+ byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
+ payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
+ bufferoffset = 0;
+ }
+ /* 复制剩余数据 */
+ memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), byteslefttocopy);
+ bufferoffset = bufferoffset + byteslefttocopy;
+ }
+ }
+
+ ...
+
+ return p;
+}
+```
+
+### 驱动初始化
+
+```c
+void rt_hw_stm32_eth_init(void);
+```
+
+`rt_hw_stm32_eth_init` 用于注册以太网设备,以太网硬件,配置 MAC 地址等。
+
+## 使能 lwIP 与 net dev
+
+首先使能以太网外设:
+
+
+
+启用 lwIP 与 net device:
+
+
+
+
+
+- 输入命令 scons --target=mdk5 -s 生成 mdk5 工程。
+- 打开工程,打开 drv_eth.c 文件。
+
+## 驱动移植
+
+STM32F407 芯片自带以太网模块,该模块包括带专用 DMA 控制器的 MAC 802.3(介质访问控制)控制器,支持介质独立接口 (MII) 和简化介质独立接口 (RMII),并自带了一个用于外部 PHY 通信的 SMI 接口,通过一组配置寄存器,用户可以为 MAC 控制器和 DMA 控制器选择所需模式和功能。
+
+因为同系列 stm32 的 MAC 控制器初始化基本一样,所以同系列的驱动 MAC 部分的代码不需要做修改,要修改的只是 PHY 的部分,这里以 LAN8720 为例讲解。
+
+### 开启日志
+
+在调试驱动时,建议先打开 drv_eth.c 中的日志功能。
+
+```c
+/* debug 设置 */
+#define ETH_DEBUG
+#define ETH_RX_DUMP
+#define ETH_TX_DUMP
+```
+
+### 更新 PHY 复位引脚
+
+查看原理图,RESET 引脚为 PD3,修改复位管脚为 PD3。
+
+
+
+```c
+static rt_err_t rt_stm32_eth_init(rt_device_t dev)
+{
+ ...
+ phy_reset();
+ ....
+}
+```
+
+
+
+在 `rt_hw_stm32_eth_init` 函数中,使用 phy_reset() 对 PHY 芯片进行了复位。如果不修改为正确的引脚,除了 PHY 可能没有被正确复位外,还可能造成引脚冲突而损坏板子。
+
+> 注:phy_reset.c 是移植文件,一般会位于 board/ports 文件夹下。如果 BSP 中没有,则需要自己实现。
+
+### 确认 MII/RMII 模式
+
+根据原理图,确认外接的 PHY 是使用 MII 还是 RMII 模式。
+
+
+
+修改 drv_eth.c 中的模式设置:
+
+- RMII 模式: ETH_MEDIA_INTERFACE_RMII
+- MII 模式: ETH_MEDIA_INTERFACE_MII
+
+```c
+static rt_err_t rt_stm32_eth_init(rt_device_t dev)
+{
+ ...
+ EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
+ ...
+}
+```
+
+### 引脚初始化
+
+下面列出正点原子探索者用于连接外部 PHY 的引脚, 用户需要按照自己的原理图对相应的管脚进行初始化。STM32 可以直接在 CubeMX 中进行引脚配置,然后生成代码。以下是生成的初始化代码,该函数最终被 rt_stm32_eth_init() 所调用。
+
+```c
+/* stm32f4xx_hal_msp.c */
+
+void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
+{
+
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+ if(heth->Instance==ETH)
+ {
+ /* USER CODE BEGIN ETH_MspInit 0 */
+
+ /* USER CODE END ETH_MspInit 0 */
+ /* Peripheral clock enable */
+ __HAL_RCC_ETH_CLK_ENABLE();
+
+ __HAL_RCC_GPIOC_CLK_ENABLE();
+ __HAL_RCC_GPIOA_CLK_ENABLE();
+ __HAL_RCC_GPIOG_CLK_ENABLE();
+ /**ETH GPIO Configuration
+ PC1 ------> ETH_MDC
+ PA1 ------> ETH_REF_CLK
+ PA2 ------> ETH_MDIO
+ PA7 ------> ETH_CRS_DV
+ PC4 ------> ETH_RXD0
+ PC5 ------> ETH_RXD1
+ PG11 ------> ETH_TX_EN
+ PG13 ------> ETH_TXD0
+ PG14 ------> ETH_TXD1
+ */
+ GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
+ HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+ GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+ GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13|GPIO_PIN_14;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
+ HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
+
+ /* USER CODE BEGIN ETH_MspInit 1 */
+
+ /* USER CODE END ETH_MspInit 1 */
+ }
+
+}
+```
+### 更新 PHY 管理程序
+
+PHY 是 IEEE802.3 中定义的一个标准模块。PHY 寄存器的地址空间为 5 位,因此寄存器范围是 0 到 31 ,最多有 32 个寄存器。IEEE802.3 定义了地址为 0-15 这 16 个基础寄存器的功能,因此只需要修改少量寄存器的定义即可完成移植。
+
+驱动默认使用 LAN8720,如果使用的是其它型号的 PHY,应该修改 rtconfig.h 中的宏定义(在 drv_eth.h 中有多个 PHY 的宏定义),使程序能正确设置自动协商并读取 PHY 的连接状态和速率。
+
+```c
+/* drv_eth.h */
+
+#ifdef PHY_USING_LAN8720A
+#define PHY_INTERRUPT_FLAG_REG 0x1DU
+#define PHY_INTERRUPT_MSAK_REG 0x1EU
+...
+#endif /* PHY_USING_LAN8720A */
+
+#ifdef PHY_USING_DM9161CEP
+#define PHY_Status_REG 0x11U
+#define PHY_10M_MASK ((1<<12) || (1<<13))
+#define PHY_100M_MASK ((1<<14) || (1<<15))
+...
+#endif /* PHY_USING_DM9161CEP */
+```
+
+RT-Thread 的驱动实现中,做了 PHY 地址搜索的功能,可以正确搜索出 PHY 的地址,所以不必定义 PHY 地址。
+
+这里贴出 drv_eth.c 文件中 `phy_monitor_thread_entry` 函数中的地址搜索函数供用户参考:
+
+```c
+static void phy_monitor_thread_entry(void *parameter)
+{
+ uint8_t phy_addr = 0xFF;
+ uint8_t detected_count = 0;
+
+ while(phy_addr == 0xFF)
+ {
+ /* 搜索 PHY */
+ rt_uint32_t i, temp;
+ for (i = 0; i <= 0x1F; i++)
+ {
+ EthHandle.Init.PhyAddress = i;
+ HAL_ETH_ReadPHYRegister(&EthHandle, PHY_ID1_REG, (uint32_t *)&temp);
+
+ if (temp != 0xFFFF && temp != 0x00)
+ {
+ phy_addr = i;
+ break;
+ }
+ }
+
+ detected_count++;
+ rt_thread_mdelay(1000);
+
+ if (detected_count> 10)
+ {
+ LOG_E("No PHY device was detected, please check hardware!");
+ }
+ }
+
+ LOG_D("Found a phy, address:0x%02X", phy_addr);
+ ...
+}
+```
+
+如果提示 `No PHY device was detected, please check hardware!` 或者 `link_down` 则应该检查 IO 配置和硬件。
+
+> 提示:现在主流的 PHY 一般在复位后都默认工作在自动协商模式下,且现在的交换机和网线,一般都可以支持支持 100M 全双工。所以在末正确适配 PHY 以前,也可以临时修改 PHY 的工作模式为 100M 全双工供测试用(修改基础控制寄存器中相应的控制位)。
+
+### 中断回调函数
+
+接收回调函数:中断函数在接收到数据时,会调用回调函数发邮件来通知 “erx” 线程来读取数据。
+
+```c
+void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
+{
+ rt_err_t result;
+ /* 发送邮件通知 erx 线程 */
+ result = eth_device_ready(&(stm32_eth_device.parent));
+ if (result != RT_EOK)
+ LOG_I("RxCpltCallback err = %d", result);
+}
+```
+
+### ETH 设备初始化
+
+RT-Thread 实时操作系统提供了一套设备管理框架 ,应用程序通过 RT-Thread 的设备操作接口实现通用的设备驱动。 我们这里对 ETH 设备,实现 `Network Interface` 类型的设备驱动,然后注册到 RT-Thread。
+
+drv_eth.c 中的 `rt_hw_stm32_eth_init` 是 ETH 设备初始化入口,负责 stm32_eth_device 结构体的初始化,并将其注册到 RT-Thread。
+
+```c
+ /* 设置工作速度和模式 */
+ stm32_eth_device.ETH_Speed = ETH_Speed_100M;
+ stm32_eth_device.ETH_Mode = ETH_Mode_FullDuplex;
+
+ /* 利用 STM32 全球唯一 ID 设置 MAC 地址 */
+ stm32_eth_device.dev_addr[0] = 0x00;
+ stm32_eth_device.dev_addr[1] = 0x80;
+ stm32_eth_device.dev_addr[2] = 0xE1;
+ stm32_eth_device.dev_addr[3] = *(rt_uint8_t*)(0x1FFF7A10+4);
+ stm32_eth_device.dev_addr[4] = *(rt_uint8_t*)(0x1FFF7A10+2);
+ stm32_eth_device.dev_addr[5] = *(rt_uint8_t*)(0x1FFF7A10+0);
+
+ /* 设置标准驱动接口 */
+ stm32_eth_device.parent.parent.init = rt_stm32_eth_init;
+ stm32_eth_device.parent.parent.open = rt_stm32_eth_open;
+ stm32_eth_device.parent.parent.close = rt_stm32_eth_close;
+ stm32_eth_device.parent.parent.read = rt_stm32_eth_read;
+ stm32_eth_device.parent.parent.write = rt_stm32_eth_write;
+ stm32_eth_device.parent.parent.control = rt_stm32_eth_control;
+ stm32_eth_device.parent.parent.user_data = RT_NULL;
+
+ /* 设置网络驱动接收和发送接口 */
+ stm32_eth_device.parent.eth_rx = rt_stm32_eth_rx;
+ stm32_eth_device.parent.eth_tx = rt_stm32_eth_tx;
+
+ /* 注册网络设备 */
+ state = eth_device_init(&(stm32_eth_device.parent), "e0");
+```
+
+#### 设置标准驱动接口
+
+##### rt_stm32_eth_init()
+
+`rt_stm32_eth_init` 是初始化以太网外设的,应按照实际需求初始化。
+
+这里只贴出 MAC 配置和 DMA 配置的代码:
+
+```c
+/* EMAC initialization function */
+static rt_err_t rt_stm32_eth_init(rt_device_t dev)
+{
+ __HAL_RCC_ETH_CLK_ENABLE();
+
+ /* 复位 PHY 芯片 */
+ phy_reset();
+
+ /* ETHERNET 配置 */
+ EthHandle.Instance = ETH;
+ EthHandle.Init.MACAddr = (rt_uint8_t *)&stm32_eth_device.dev_addr[0];
+ EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_DISABLE;
+ EthHandle.Init.Speed = ETH_SPEED_100M;
+ EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
+ EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
+ EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE;
+#ifdef RT_LWIP_USING_HW_CHECKSUM
+ EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
+#else
+ EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE;
+#endif
+
+ HAL_ETH_DeInit(&EthHandle);
+
+ /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */
+ if (HAL_ETH_Init(&EthHandle) != HAL_OK)
+ {
+ LOG_E("eth hardware init failed");
+ return -RT_ERROR;
+ }
+ else
+ {
+ LOG_D("eth hardware init success");
+ }
+
+ /* Initialize Tx Descriptors list: Chain Mode */
+ HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, Tx_Buff, ETH_TXBUFNB);
+
+ /* Initialize Rx Descriptors list: Chain Mode */
+ HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, Rx_Buff, ETH_RXBUFNB);
+
+ /* ETH interrupt Init */
+ HAL_NVIC_SetPriority(ETH_IRQn, 0x07, 0);
+ HAL_NVIC_EnableIRQ(ETH_IRQn);
+
+ /* Enable MAC and DMA transmission and reception */
+ if (HAL_ETH_Start(&EthHandle) == HAL_OK)
+ {
+ LOG_D("emac hardware start");
+ }
+ else
+ {
+ LOG_E("emac hardware start faild");
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+```
+
+##### rt_stm32_eth_control()
+
+`rt_stm32_eth_control` 函数需要实现获取 MAC 地址的功能
+
+```c
+static rt_err_t rt_stm32_eth_control(rt_device_t dev, int cmd, void *args)
+{
+ switch (cmd)
+ {
+ case NIOCTL_GADDR:
+ /* 获取 MAC 地址 */
+ if (args) rt_memcpy(args, stm32_eth_device.dev_addr, 6);
+ else return -RT_ERROR;
+ break;
+
+ default :
+ break;
+ }
+
+ return RT_EOK;
+}
+```
+
+别的设置标准驱动接口可以不实现,先写个空函数即可。
+
+#### 数据包收发接口
+
+##### rt_stm32_eth_rx()
+
+`rt_stm32_eth_rx` 会去读取接收缓冲区中的数据,并放入 pbuf(lwIP 中利用结构体 pbuf 来管理数据包 )中,并返回 pbuf 指针。
+
+“erx” 接收线程会阻塞在获取 `eth_rx_thread_mb` 邮箱上,当它接收到邮件时,会调用 `rt_stm32_eth_rx` 去接收数据。
+
+##### rt_stm32_eth_tx()
+
+`rt_stm32_eth_tx` 会将要发送的数据放入发送缓冲区,等待 DMA 来发送数据。
+
+“etx” 发送线程会阻塞在获取 `eth_tx_thread_mb` 邮箱上, 当它接收到邮件时,会调用 `rt_stm32_eth_tx` 来发送数据。
+
+## EMAC 驱动调试
+
+### 实验环境搭建
+
+工程默认启用了 DHCP 功能,需要有 DHCP 服务器来分配 IP 地址,常见的连接拓展如图:
+
+
+
+如果没有方便的实际环境,也可以先通过 ENV 配置固定 IP,然后用网线直接连接到调试用的电脑。
+
+
+
+电脑和开发板需要设置同网段的 IP 地址。
+
+### 确认 PHY 连接状态
+
+当支持网络的固件在开发板上面运行起来后,应该首先检查 RJ45 指示灯的状态。
+
+正常应该是有灯常亮,且有数据收发时会出现闪烁。
+
+如果发现灯没有亮,请先确认开发板硬件是否完好;然后检查供电是否充足,网线是否接好。
+
+然后程序上配合硬件确认是否有正确复位 PHY,直到正常闪烁为止。
+
+### 确认 IP 地址
+
+在 shell 命令行执行 `ifconfig` 命令即可查看网卡的 IP 地址。
+
+如果启用了 DHCP 功能,且此时显示已经获取到了 IP 地址,说明驱动的收发功能都已经正常。
+
+如果是静态 IP 或是没有获取到 IP,请留意网卡 FLAG 中的 UP 和 LINK_UP 标志。
+如果显示有 LINK_DOWN,请确认 PHY 的管理程序有正确识别到 PHY 的速率和双工状态。
+并正确通过 `eth_device_linkchange` 通知到 lwIP。
+
+如果启用了 DHCP 功能,且已经显示 LINK_UP。但没能正确获取到 IP。
+说明开发板与 DHCP 服务器通信不畅,需要进一步调试。
+
+
+
+可以通过拔插网线观察 LINK 状态来判断是否正确读取了 PHY 寄存器的值。
+
+### 打印数据包
+
+通过打开驱动中的 ETH_RX_DUMP 和 ETH_TX_DUMP 功能。
+可以把收发的数据包打印出来。
+
+
+
+当 RJ45 指示灯正常闪烁时,说明有数据包在收发。
+因为网络中经常会有广播数据包,如果此时有数据包进来,会有 RX_DUMP 的打印。
+如果一直没有打印,则重点检查两点:
+
+* MII/RMII 的 RX 线路有问题,包括硬件问题或 IO 映射错误。
+
+* EMAC 和 PHY 的速率和双工模式不配置,如 EMAC 工作在 10M,而 PHY 连接为 100M。
+
+需要确认有正确获取 PHY 的速率和双工模式,同时可以与网线另外一端对比。
+如电脑上在显示 10M,而板子上面没有更新 PHY 管理程序,默认为 100M。
+
+如果有必要,也可以打印 PHY 的 LOOPBACK 功能。以确认 MII/RMII 总线是完好的。
+
+### ping 测试
+
+可以通过电脑 ping 板子或者板子 ping 电脑(需要开启 [netutils](https://github.com/RT-Thread-packages/netutils) 组件包中的 ping 功能)来测试驱动是否移植成功。
+
+
+
+
+
+> [!NOTE]
+> 注:- 部分 PHY 芯片需要按照手册进行额外的初始化。
+ - 板子和电脑要在一个网段内。
+ - 如果电脑同时连着网线和 wifi,会出现 ping 不通板子的现象。
+ - 查看防火墙是否禁用了 ping 功能。
+ - 有些企业网络会禁止 ping 功能,建议更换网络环境。
+ - MAC 地址不规范,板子无法 ping 通外部网络。
+ - 如果 erx 栈大小设置的太小,会造成 erx 线程栈溢出 。
+
+### wireshark 抓包
+
+如果板子有 RX DUMP,但依然无法通信时,可以在电脑上面使用 [wireshark](https://www.wireshark.org/) 抓包。
+
+电脑 ping 开发板,开发板收到目标地址为自己的 IP 地址的请求包 (request),然后,板子会给电脑做出回应,发送一个目标地址为电脑 IP 地址的回应包 (reply)。
+
+根据是否有回应 (reply),可以分两种情况检查
+
+- 板子是否收到了请求包 (request)。
+- 板子是否有发送回应 (reply)。
+
+如果有回应,但是 ping 不成功,可以检查数据包内容是否符合规范。
+
+#### 按 MAC 地址过滤
+
+当板子有发出广播包,在电脑上面可以收到。(如 DHCP Discoverer)
+
+在使用 wireshark 抓包过程中,主要是灵活使用各种过滤器,过滤出我们所关心的数据包。
+
+当开发板还没有拿到 IP 地址时,可以使用开发板的 MAC 地址作为过滤器条件。
+
+
+
+当开发板成功 link_up 时,会主动发出 DHCP 请求包,源地址就是开发板自己的 MAC 地址。
+
+#### 按 IP 地址过滤
+
+
+
+或配置开发板为静态 IP,然后 PC 上面执行 ping 命令,ping 开发板的 IP 地址。
+
+这里把开发板的 IP 地址作为过滤条件,正常情况下,PC 会先发出请求包 (request)。
+
+## 参考资料
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/an0011-network-started.md b/rt-thread-version/rt-thread-standard/application-note/components/network/an0011-network-started.md
new file mode 100644
index 0000000000000000000000000000000000000000..64bb1bcd9c6686268bb359b755bce823892d7963
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/network/an0011-network-started.md
@@ -0,0 +1,430 @@
+# 在 STM32F407 上应用网络功能
+
+本文描述了如何在 RT-Thread 中利用标准 BSD Socket API 来开发网络应用。并给出了在正点原子 STM32F4 探索者开发板上运行 NTP(通过网络获取时间)和 MQTT(通过 MQTT 收发数据) 的代码示例。
+
+## 简介
+
+越来越多的单片机需要接入以太网来收发数据,市面上也有非常多的接入方案,可以用单片机加自带硬件协议栈的 PHY 芯片来接入网络,也可以单片机跑软件协议栈加 PHY 芯片来接入网络,不同的接入方案需要调用不同的 API,降低了上层应用的可移植性。
+
+为了方便用户开发网络应用,RT-Thread 中引入了网络框架。并提供标准 BSD Socket API 用于开发网络应用,同时,RT-Thread 还提供了数量丰富的网络组件包,方便用户快速开发自己的应用。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+* 一块能上网的开发板, 这里以正点原子 STM32F4 探索者开发板为例
+
+* 移植好网络底层驱动,驱动移植可以参考 [网络协议栈驱动移植笔记](an0010-lwip-driver-porting.md)
+
+* 网络调试工具
+
+## 主要调试命令
+
+这里介绍下 RT-Thread 提供的三个网络信息查看命令,在 shell 中输入命令即可很方便的查看网络连接状况,方便用户进行调试。
+
+**ifconfig**
+
+`ifconfig` 可以打印出板子现在的网络连接状态,IP 地址,网关地址,dns 等信息。
+
+
+
+**netstate**
+
+`netstate` 可以打印出板子所有的 TCP / IP 连接信息
+
+
+
+**dns**
+
+`dns` 命令可以打印出现在使用的 dns 服务器地址。
+
+`dns [dns_num] ` 命令可以手动设置 dns 服务器地址。
+
+
+
+## 硬件连接准备
+
+工程默认启用了 DHCP 功能,需要有 DHCP 服务器来分配 IP 地址,常见的连接拓展如图:
+
+
+
+注:如果没有方便的实际环境,也可以先通过 ENV 配置固定 IP,然后用网线直接连接到调试用的电脑。电脑和开发板需要设置同网段的 IP 地址。配置静态 IP 如下:
+
+```
+ -> RT-Thread Components
+ -> Network
+ -> light weight TCP/IP stack
+ -> Enable lwIP stack
+ -> Static IPv4 Address
+```
+
+
+
+## ENV 配置
+
+RT-Thread 可以很方便的通过 ENV 来配置和生成工程
+
+1. 打开板载外设 ethernet,选中之后, LWIP 也将自动被开启:
+
+
+
+
+
+2. 打开 SAL 层,并打开 BSD socket:
+
+
+
+此时 net device 也将自动被打开:
+
+
+
+文件系统也将自动被打开(fd 的管理在文件系统中,所以需要文件系统):
+
+
+
+4. 基础应用:tcp client,udp client。在软件包中开启基础示例代码 tcp client 与 udp client。
+
+```
+ -> RT-Thread online packages
+ -> miscellaneous packages
+ -> samples: kernel and components samples
+ -> a network_samples package for rt-thread
+```
+
+
+
+5. 高级应用 1:MQTT。在软件包中开启高级应用软件包:
+
+```
+ -> RT-Thread online packages
+ -> IoT - internet of things
+ -> Paho MQTT: Eclipse Paho MQTT C/C++ client for Embedded platforms
+```
+
+选中 MQTT 示例代码:
+
+
+
+6. 高级应用 2:NTP。在软件包中开启高级应用软件包:
+
+```
+-> RT-Thread online packages
+ -> IoT - internet of things
+ -> netutils: Networking utilities for RT-Thread
+```
+
+
+
+7. 按 ESC 退出配置界面。
+
+8. 在 Env 命令行中输入 pkgs --update 下载软件包。
+
+9. 在 Env 命令行中输入 scons --target=mdk5 -s 生成 mdk5 工程。
+
+10. 打开工程,编译,下载代码。
+
+## 网络测试
+
+将 Env 生成的工程编译后下载到板子上,可以看到网口的两盏灯会亮起,一盏会闪烁,说明 PHY 已经正常初始化了。
+
+在 shell 中输入 ifconfig 可以打印板子的网络状态,正常获取到 ip 即表示网络驱动正常,准备工作完成。
+
+
+
+## 基础应用示例
+
+在实际应用中,单片机一般作为客户端去和服务器进行数据交换,在这里,以 tcp client 和 udp client 为例进行讲解。
+
+### tcpclient 示例
+
+这个例程展示了如何创建一个 TCP 客户端,跟远端服务器进行通信。 在 shell 中输入 tcpclient URL PORT 来连接服务器,程序接收并显示从服务端发送过来的信息,接收到开头是'q' 或'Q' 的信息则退出程序。
+
+源码解析如下所示:
+
+```c
+void tcpclient(int argc, char **argv)
+{
+ int ret;
+ char *recv_data;
+ struct hostent *host;
+ int sock, bytes_received;
+ struct sockaddr_in server_addr;
+ const char *url;
+ int port;
+
+ /* 接收到的参数小于 3 个 */
+ if (argc < 3)
+ {
+ rt_kprintf("Usage: tcpclient URL PORT\n");
+ rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
+ return ;
+ }
+
+ url = argv[1];
+ port = strtoul(argv[2], 0, 10);
+
+ /* 通过函数入口参数 url 获得 host 地址(如果是域名,会做域名解析) */
+ host = gethostbyname(url);
+
+ /* 分配用于存放接收数据的缓冲 */
+ recv_data = rt_malloc(BUFSZ);
+ if (recv_data == RT_NULL)
+ {
+ rt_kprintf("No memory\n");
+ return;
+ }
+
+ /* 创建一个 socket,类型是 SOCKET_STREAM,TCP 类型 */
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ /* 创建 socket 失败 */
+ rt_kprintf("Socket error\n");
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ return;
+ }
+
+ /* 初始化预连接的服务端地址 */
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+ server_addr.sin_addr = *((struct in_addr *)host->h_addr);
+ rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
+
+ /* 连接到服务端 */
+ if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
+ {
+ /* 连接失败 */
+ rt_kprintf("Connect fail!\n");
+ closesocket(sock);
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ return;
+ }
+
+ while (1)
+ {
+ /* 从 sock 连接中接收最大 BUFSZ - 1 字节数据 */
+ bytes_received = recv(sock, recv_data, BUFSZ - 1, 0);
+ if (bytes_received < 0)
+ {
+ /* 接收失败,关闭这个连接 */
+ closesocket(sock);
+ rt_kprintf("\nreceived error,close the socket.\r\n");
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ break;
+ }
+ else if (bytes_received == 0)
+ {
+ /* 打印 recv 函数返回值为 0 的警告信息 */
+ rt_kprintf("\nReceived warning,recv function return 0.\r\n");
+
+ continue;
+ }
+
+ /* 有接收到数据,把末端清零 */
+ recv_data[bytes_received] = '\0';
+
+ if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0)
+ {
+ /* 如果是首字母是 q 或 Q,关闭这个连接 */
+ closesocket(sock);
+ rt_kprintf("\n got a'q'or'Q',close the socket.\r\n");
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ break;
+ }
+ else
+ {
+ /* 在控制终端显示收到的数据 */
+ rt_kprintf("\nReceived data = %s", recv_data);
+ }
+
+ /* 发送数据到 sock 连接 */
+ ret = send(sock, send_data, strlen(send_data), 0);
+ if (ret < 0)
+ {
+ /* 接收失败,关闭这个连接 */
+ closesocket(sock);
+ rt_kprintf("\nsend error,close the socket.\r\n");
+
+ rt_free(recv_data);
+ break;
+ }
+ else if (ret == 0)
+ {
+ /* 打印 send 函数返回值为 0 的警告信息 */
+ rt_kprintf("\n Send warning,send function return 0.\r\n");
+ }
+ }
+ return;
+}
+```
+
+用网络调试工具在电脑上搭建一个 TCP 服务器,记录下打开的端口
+
+
+
+在 shell 中输入 tcpclient PC 的 IP 地址 刚才记录下的端口号
+
+```
+msh />tcpclient 192.168.12.45 5000
+```
+
+利用服务器发送 Hello RT-Thread! ,shell 中会显示收到的信息
+
+
+
+服务器会收到 This is TCP Client from RT-Thread. 的消息
+
+
+
+### udpclient 示例
+
+这个例程展示了如何创建一个 UDP 客户端,给远端服务器发送数据。在 shell 中输入 udpclient URL PORT 来连接服务器。程序会给服务端发送信息(默认 10 条)。
+
+源码解析如下所示:
+
+```c
+void udpclient(int argc, char **argv)
+{
+ int sock, port, count;
+ struct hostent *host;
+ struct sockaddr_in server_addr;
+ const char *url;
+
+ /* 接收到的参数小于 3 个 */
+ if (argc < 3)
+ {
+ rt_kprintf("Usage: udpclient URL PORT [COUNT = 10]\n");
+ rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
+ return ;
+ }
+
+ url = argv[1];
+ port = strtoul(argv[2], 0, 10);
+
+ if (argc> 3)
+ count = strtoul(argv[3], 0, 10);
+ else
+ count = 10;
+
+ /* 通过函数入口参数 url 获得 host 地址(如果是域名,会做域名解析) */
+ host = (struct hostent *) gethostbyname(url);
+
+ /* 创建一个 socket,类型是 SOCK_DGRAM,UDP 类型 */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ {
+ rt_kprintf("Socket error\n");
+ return;
+ }
+
+ /* 初始化预连接的服务端地址 */
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+ server_addr.sin_addr = *((struct in_addr *)host->h_addr);
+ rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
+
+ /* 总计发送 count 次数据 */
+ while (count)
+ {
+ /* 发送数据到服务远端 */
+ sendto(sock, send_data, strlen(send_data), 0,
+ (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
+
+ /* 线程休眠一段时间 */
+ rt_thread_delay(50);
+
+ /* 计数值减一 */
+ count --;
+ }
+
+ /* 关闭这个 socket */
+ closesocket(sock);
+}
+```
+
+用网络调试工具在电脑上搭建一个 UDP 服务器,记录下打开的端口
+
+
+
+在 shell 中输入 udpclient PC 的 IP 地址 刚才记录下的端口号
+
+```
+udpclient 192.168.12.45 1001
+```
+
+服务器会收到 10 条 This is UDP Client from RT-Thread. 的消息
+
+
+
+## 高级应用示例
+
+为了方便网络应用开发,RT-Thread 提供了丰富的网络组件包,例如:[netutils 网络小工具集](https://github.com/RT-Thread-packages/netutils),[webclient](https://github.com/RT-Thread-packages/webclient),[cJSON](https://github.com/RT-Thread-packages/cJSON),[paho-mqtt](https://github.com/RT-Thread-packages/paho-mqtt) 等等,用户可以根据需求直接在 Env 中使能即可使用各个组件包,省去了自己移植的过程,加速网络应用开发。
+
+
+
+我们这里以 netutils 网络小工具集中的 [NTP](https://github.com/RT-Thread-packages/netutils/blob/master/ntp/README.md)(时间同步)小工具和 [paho-mqtt](https://github.com/RT-Thread-packages/paho-mqtt) 为例进行讲解
+
+### NTP
+
+NTP(Network Time Protocol) 是网络时间协议,它是用来同步网络中各个计算机时间的协议。
+
+RT-Thread 实现了 NTP 客户端,可以通过网络获取本地时间,并同步板子的 RTC 时间。
+
+ENV 的配置参考前面准备工作章节的 ENV 配置。
+
+在 msh 中输入 `ntp_sync` 即可从默认的 NTP 服务器 (cn.ntp.org.cn) 获取本地时间,默认时区为东八时区
+
+```
+msh />ntp_sync
+```
+
+如果输入 `ntp_sync` 后提示超时或者连接失败,可以在 `ntp_sync` 后面输入 NTP 服务器地址,程序将从新的服务器获取时间。
+
+```
+msh />ntp_sync edu.ntp.org.cn
+```
+
+
+
+### MQTT
+
+[Paho MQTT](http://www.eclipse.org/paho/downloads.php) 是 Eclipse 实现的 MQTT 协议的客户端,本软件包是在 Eclipse [paho-mqtt](https://github.com/eclipse/paho.mqtt.embedded-c) 源码包的基础上设计的一套 MQTT 客户端程序。
+
+MQTT 使用发布 / 订阅消息模式,发送消息时要指定发给哪个主题名(Topic Name),接收消息前要订阅一个主题名,然后才能接收到发送给这个主题名的消息内容。
+
+RT-Thread MQTT 客户端功能特点:
+
+- 断线自动重连
+- pipe 模型,非阻塞 API
+- 事件回调机制
+- TLS 加密传输
+
+ENV 的配置参考前面准备工作章节的 ENV 配置
+
+在 msh 中输入 `mqtt_start` 命令, 客户端会自动连接服务器,并订阅 `/mqtt/test` 主题
+
+```
+msh />mqtt_start
+```
+
+通过 `mqtt_publish` 命令 可以发送消息给所有订阅了 `/mqtt/test` 的客户端,我们利用 `mqtt_publish` 发送 RT-Thread!
+
+```
+msh />mqtt_publish RT-Thread!
+```
+
+由于我们之前订阅了 `/mqtt/test` 主题,shell 很快会显示服务器发来的 RT-Thread! 消息。
+
+
+
+## 参考资料
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/an0019-tcpclient-socket.md b/rt-thread-version/rt-thread-standard/application-note/components/network/an0019-tcpclient-socket.md
new file mode 100644
index 0000000000000000000000000000000000000000..a0f261c65334e95a51e140f980e47e3f03598899
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/network/an0019-tcpclient-socket.md
@@ -0,0 +1,280 @@
+# 多线程非阻塞网络编程
+
+本文描述了使用 QEMU 运行 RT-Thread 提供的基于多线程的非阻塞 socket 编程示例。
+
+## 简介
+
+随着物联网的发展,越来越多产品需要基于网络进行数据传输。在实际开发中,往往要求网络传输时不能阻塞当前线程,以致无法及时处理其他消息。在用户无法直接套用简单的 socket demo 时,RT-Thread 提供基于多线程的非阻塞 socket 编程示例,方便用户进行应用程序开发。
+
+在 RT-Thread 使用 socket 网络编程时,当一个任务调用 socket的 recv()函数接收数据时,如果 socket 上并没有接收到数据,这个任务将阻塞在这个 recv() 函数里。这个时候,这个任务想要处理一些其他事情,例如进行一些数据采集,发送一些额外数据到网络上等,将变得不可能了。与此同时,其他线程也需要将数据上传同一个服务器,如果直接多个线程共同使用一个 socket 操作,这将会破坏底层 lwip 的消息事件模型。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+* [README.md](https://github.com/neverxie/tcpclient)
+
+* [tcpclient 源码](https://github.com/neverxie/tcpclient/blob/master/src/tcpclient.c)
+
+* [example 源码](https://github.com/neverxie/tcpclient/blob/master/examples/tcpclient_example.c)
+
+## socket 编程模型简介
+
+socket 编程模型如下图所示:
+
+
+
+客户端使用流程:
+
+1. `socket()` 创建一个 socket,返回套接字的描述符,并为其分配系统资源。
+2. `connect()` 向服务器发出连接请求。
+3. `send()/recv()` 与服务器进行通信。
+4. `closesocket()` 关闭 socket,回收资源。
+
+服务器使用流程:
+
+1. `socket()` 创建一个 socket,返回套接字的描述符,并为其分配系统资源。
+2. `bind()` 将套接字绑定到一个本地地址和端口上。
+3. `listen()` 将套接字设为监听模式并设置监听数量,准备接收客户端请求。
+4. `accept()` 等待监听的客户端发起连接,并返回已接受连接的新套接字描述符。
+5. `recv()/send()` 用新套接字与客户端进行通信。
+6. `closesocket()` 关闭 socket,回收资源。
+
+例如在上面网络客户端操作过程中,当进行 recv 操作时,如果对应的通道数据没有准备好,那系统就会让当前任务进入阻塞状态,当前任务不能再进行其他的操作。
+
+## 非阻塞 socket 编程简介
+
+在 RT-Thread 中,自 v3.0.0 以来更标准化,支持更多的 POSIX API。这其中就包括 poll / select 接口实现,并且可以进行 socket 和设备文件的联合 poll / select。select、poll的内部实现机制相似,由于本文选用 select 方式,故在此不对 poll 展开介绍。
+
+下面结合框图进一步说明如何使用 select 和 pipe 来解决这类问题。
+
+
+
+图中存在有三个线程:应用线程 `thread1`、`thread2` 和客户端线程 `thread client`,其中 `thread client` 完成 select 功能。
+
+- 数据发送过程:
+ - 应用线程通过 pipe 往 `thread client` 发送数据 data1,select 探测到 pipe 有数据可读,`thread client` 被唤醒,然后读取 pipe 中的数据并通过 TCP socket 发送到 server
+- 数据接收过程:
+ - server 通过 TCP socket 发送数据 data2 到 `thread client`,select 探测到 socket 有数据可读,`thread client` 被唤醒,`thread client` 可以获得接收到的数据
+
+下面将详细介绍 select 和 pipe 的使用方法。
+
+### select
+
+`select()` 可以阻塞地同时探测一组支持非阻塞的 I / O 设备是否有事件发生(如可读,可写,出现异常等等),直至某一个设备触发了事件或者超过了指定的等待时间。此时我们可以把需要的数据源通道放到 select 的探测范围内,只要相应的数据源准备好 select 就会返回,这时就能无阻塞地读取到数据。
+
+`select()` 主要用来处理 I / O 多路复用的情况,适用如下场合:
+
+- 客户端处理多个描述符时(一般是交互式输入和网络套接口)
+- 服务器既要处理监听套接口,又要处理已连接套接口
+- 服务器既要处理 TCP,又要处理 UDP
+- 服务器要处理多个服务或多个协议
+
+select()函数原型及介绍如下所示:
+
+```c
+int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
+```
+
+| 参数 | 描述 |
+| -------- | ------------------------------------------------------- |
+| nfds | 集合中所有文件描述符的范围,即所有文件描述符的最大值加1 |
+| readfds | 需要监视读变化的文件描述符集合 |
+| writefds | 需要监视写变化的文件描述符集合 |
+| errorfds | 需要监视出现异常的文件描述符集合 |
+| timeout | select 的超时时间 |
+| **返回** | -- |
+| 正值 | 监视的文件集合出现可读写事件或异常事件 |
+| 0 | 等待超时,没有可读写或异常的事件 |
+| 负值 | select 出现错误 |
+
+### pipe
+
+pipe 是一个基于文件描述符的单向数据通道,可用于线程间的通信。
+
+在 RT-Thread 里面,pipe 支持文件描述符的形式操作,而且 pipe 不需要控制协议,操作简单。
+
+> 提示:在 msh />中,输入 `list_fd` 可查看当前打开的文件描述符,详情如下:
+
+```c
+msh />list_fd
+fd type ref magic path
+
+-- ------ --- ----- ------
+
+ 0 file 1 fdfd /uart0
+ 1 socket 1 fdfd
+ 2 file 1 fdfd /pipe0
+ 3 file 1 fdfd /pipe0
+msh />
+```
+
+下面将详细介绍代码的实现情况。
+
+## tcpclient 示例
+
+`tcpclient.c` 是上文提出的 select、pipe 方案的具体实现代码,该源码采用面向对象的思想实现,提供 TCP 连接、发送、关闭以及注册接收回调四个 API 提供用户使用。
+
+下面的序列图为 `tcpclient.c`的运行流程:
+
+
+
+各流程详细解释如下所示:
+
+1. 调用 `rt_tcpclient_start()` 设置服务器 ip 地址 & 端口号,以及完成 pipe、socket 初始化和 TCP 连接、select 配置等工作。
+2. 注册接收回调函数 `rt_tc_rx_cb()`。
+3. 调用 `rt_tcpclient_send()` 通过 pipe 发送数据(*图中绿线表示 select 探测到 pipe 可读事件*)。
+4. 图中绿线表示 select 探测到 pipe 可读事件, tcpclient 被唤醒并读取 pipe 的数据。
+5. tcpclient 通过 socket 发送数据给 server。
+6. server 通过 socket 发送数据给 tcpclient。
+7. 图中蓝线表示 select 探测到 socket 可读事件,tcpclient 被唤醒并读取 socket 的数据。
+8. app 通过 `rt_tc_rx_cb()` 获得 tcpclient 读取到的数据。
+9. 通信完毕,app 调用 `rt_tcpclient_close()` 关闭 pipe、socket,并清理相关资源。
+
+### 源码详解
+
+下面代码的核心代码:
+
+```c
+static void select_handle(rt_tcpclient_t *thiz, char *pipe_buff, char *sock_buff)
+{
+ fd_set fds;
+
+ rt_int32_t max_fd = 0, res = 0;
+ max_fd = MAX_VAL(thiz->sock_fd, thiz->pipe_read_fd) + 1;
+
+ /* 清空可读事件描述符列表 */
+ FD_ZERO(&fds);
+
+ while (1)
+ {
+ /* 将需要监听可读事件的描述符加入列表 */
+ FD_SET(thiz->sock_fd, &fds);
+ FD_SET(thiz->pipe_read_fd, &fds);
+
+ /* 等待设定的网络描述符有事件发生 */
+ res = select(max_fd, &fds, RT_NULL, RT_NULL, RT_NULL);
+
+ /* select 返回错误及超时处理 */
+ EXCEPTION_HANDLE(res, "select handle", "error", "timeout");
+
+ /* 查看 sock 描述符上有没有发生可读事件 */
+ if (FD_ISSET(thiz->sock_fd, &fds))
+ {
+ /* 从 sock 连接中接收最大BUFSZ - 1字节数据 */
+ res = recv(thiz->sock_fd, sock_buff, BUFF_SIZE, 0);
+
+ /* recv 返回异常 */
+ EXCEPTION_HANDLE(res, "socket recv handle", "error", "TCP disconnected");
+
+ /* 有接收到数据,把末端清零 */
+ sock_buff[res] = '\0';
+
+ /* 通过回调函数的方式,数据发给 thread1 */
+ RX_CB_HANDLE(sock_buff, res);
+
+ /* 如果接收的是exit,关闭这个连接 */
+ EXIT_HANDLE(sock_buff);
+ }
+
+ /* 查看 pipe 描述符上有没有发生可读事件 */
+ if (FD_ISSET(thiz->pipe_read_fd, &fds))
+ {
+ /* 从 pipe 连接中接收最大BUFSZ - 1字节数据 */
+ res = read(thiz->pipe_read_fd, pipe_buff, BUFF_SIZE);
+
+ /* recv 返回异常 */
+ EXCEPTION_HANDLE(res, "pipe recv handle", "error", RT_NULL);
+
+ /* 有接收到数据,把末端清零 */
+ pipe_buff[res] = '\0';
+
+ /* 读取 pipe 的数据,转发给 server */
+ send(thiz->sock_fd, pipe_buff, res, 0);
+
+ /* recv 返回异常 */
+ EXCEPTION_HANDLE(res, "socket write handle", "error", "warning");
+
+ /* 如果接收的是 exit,关闭这个连接 */
+ EXIT_HANDLE(pipe_buff);
+ }
+ }
+
+exit:
+ /* 释放接收缓冲 */
+ free(pipe_buff);
+ free(sock_buff);
+}
+```
+
+这段代码是 tcpclient 线程的核心部分,按照例程配置 select,根据 `FD_ISSET()` 宏检查描述符。
+
+- 假如 socket 有数据可读,采用回调函数的方式把数据发送给应用线程。
+- 假如 pipe 有数据可读,处理数据,通过 socket 发送到服务器。
+
+### 准备工作
+
+首先在 github 上拉取 `tcpclient.c` 的源码,然后将tcpclient 文件夹放在 `rt-thread\bsp\qemu-vexpress-a9`目录下,详情如下:
+
+
+
+在 Env 里使用 `scons` 命令编译 QEMU 工程,详情如下:
+
+
+
+在 Env 里使用 `.\qemu.bat` 命令启动,详情如下:
+
+
+
+QEMU 成功启动,下面来介绍代码运行情况。
+
+设置网络调试助手端口号,详情如下:
+
+
+
+在 cmd 命令行输入 `ipconfig` 查看本机 ip 地址,详情如下:
+
+```c
+> ipconfig
+...
+IPv4 Address. . . . . . . . . . . : 192.168.12.53
+...
+```
+
+example 代码中通过 `rt_tcpclient_start()` API 设置服务器 IP 地址和端口号,详情如下:
+
+```c
+rt_tcpclient_start("192.168.12.53", 9008);
+```
+
+> [!NOTE]
+> 注:这里需要根据自己的环境设置 ip 地址和端口号!!!
+
+在 `msh />` 里,输入 `rt_tc_test_init` 详情如下:
+
+```c
+msh />rt_tc_test_init
+```
+
+### 运行效果
+
+在 example.c 里建立两个线程,一个是 thread1,另一个是 thread2,两个线程交替给服务端发送数据。服务端每秒钟往客户端发送数据。
+
+ 
+
+网络助手发送 `i am server` ,thread1 接收并且打印出来,详情如下:
+```
+msh />D/tc_rx_cb [-30-01-01 00:00:00 tcpc] (packages\tcpclient\examples\tcpclient_example.c:52)recv data: i am server
+```
+
+## 总结
+
+- select() 也是阻塞模式,它的好处在于可以同时选择多个数据源通道:只要通道里数据有效时,就可以进行操作;在没有数据需要处理时,则操作线程会被挂起。
+- 通过使用 pipe / select 的方式,让 tcpclient 网络任务实现了在等待网络数据的同时额外处理其他消息的目的。
+
+## 参考资料
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/BSD soc.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/BSD soc.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6fe03df537e91b15393bcbd4b65e2446b9946bd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/BSD soc.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_qemu.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_qemu.png
new file mode 100644
index 0000000000000000000000000000000000000000..e4f64a3d130972ce298c533416a81ed2ac0708c2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_qemu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_scons.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_scons.png
new file mode 100644
index 0000000000000000000000000000000000000000..e04086cd4c6f3b1e3f36094ca426728ba5b2576f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_scons.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_server_1.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_server_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..109708f985fe891f0bae83a4d91ece089589165b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_server_1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_server_2.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_server_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..174e11dd50347a71102e11e814a2ce29e4a209f2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_server_2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_socket.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_socket.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb1e6d4317e0b131929e471f76159319d5c186cd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_socket.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_tcpclient.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_tcpclient.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f6e92ee0076159d7ce373f429a9c0e8a6594ba1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_tcpclient.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_uml.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_uml.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ef9a0d911f0e59ae87a0e5e9c2e7d92d4bb7777
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_uml.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_url.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_url.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd90163dfe2941296353a24e039910dee59ccd9d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an0019_url.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an009_menuconfig_lwip.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an009_menuconfig_lwip.png
new file mode 100644
index 0000000000000000000000000000000000000000..91ac7c0d14ea74e0b0666780aec7ccb07e589e5f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an009_menuconfig_lwip.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_LAN8720.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_LAN8720.png
new file mode 100644
index 0000000000000000000000000000000000000000..39480ff8119d52f90698863eca56932785dd05f1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_LAN8720.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_dump.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_dump.png
new file mode 100644
index 0000000000000000000000000000000000000000..7983f601a677ec5be6647251c3d31cb4b98e7a0a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_dump.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_f4_eth_RJ45.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_f4_eth_RJ45.png
new file mode 100644
index 0000000000000000000000000000000000000000..4abf9c436e6f16cdcb3f590d0d933fa8774aefcf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_f4_eth_RJ45.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_f4_eth_driver.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_f4_eth_driver.png
new file mode 100644
index 0000000000000000000000000000000000000000..052ac6233589fa552d09d160a3f8797395e67021
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_f4_eth_driver.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ifconfig.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ifconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..3303edad348c1a52e095ede614bb606c7b676e83
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ifconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ipadress.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ipadress.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0130236523ce987d036c2b7b4f82241c27f5c13
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ipadress.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_lwip.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_lwip.png
new file mode 100644
index 0000000000000000000000000000000000000000..8052651235ab00397b16489a9a210ac22b523a0d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_lwip.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_lwip_block.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_lwip_block.png
new file mode 100644
index 0000000000000000000000000000000000000000..795b29297c1bc2084ca1f9d9176bd6c675e17467
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_lwip_block.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ping1.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ping1.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac433872c91ff4e405c6f602ad5472cb7dc6f9b9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ping1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ping2.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ping2.png
new file mode 100644
index 0000000000000000000000000000000000000000..4ffd240c4ca2c7389ed64b26381afe7db9a93a9e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_ping2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_reset_pin.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_reset_pin.png
new file mode 100644
index 0000000000000000000000000000000000000000..3390c6e3841e92590ddcbdd015ca05636c01891b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_reset_pin.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_rt_xxx_erh_tx.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_rt_xxx_erh_tx.png
new file mode 100644
index 0000000000000000000000000000000000000000..02ac505fd0a7b2ac0843356d9fbde977b08b4ffd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_rt_xxx_erh_tx.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_rt_xxx_eth_rx.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_rt_xxx_eth_rx.png
new file mode 100644
index 0000000000000000000000000000000000000000..18ae21ea4b6bd812807e606fb06756ba5d005ee7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_rt_xxx_eth_rx.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_tcpclient.jpg b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_tcpclient.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e5175fd13bd42ec2589980211f74d200aafd99cb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_tcpclient.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_uart.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_uart.png
new file mode 100644
index 0000000000000000000000000000000000000000..8df3314958f40b029463de61c553483e64b2178e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_uart.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_wireshark_ip.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_wireshark_ip.png
new file mode 100644
index 0000000000000000000000000000000000000000..61dcb8573d34f4a651e29716476f880cb66f8e77
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_wireshark_ip.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_wireshark_mac.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_wireshark_mac.png
new file mode 100644
index 0000000000000000000000000000000000000000..3731ac62f43d77c1e27d0410f359dd9bee760a09
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an010_wireshark_mac.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_IoT.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_IoT.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b7678a247298a9c59b69d7f51c4614bbf642deb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_IoT.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_client.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_client.png
new file mode 100644
index 0000000000000000000000000000000000000000..493f0ef7e99f6c112b875ac8c5459aaad7a4d96e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_client.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_dns.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_dns.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3d11d5b300ca324f6c7ca1bcd085a09ecfab0b5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_dns.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_frame.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_frame.png
new file mode 100644
index 0000000000000000000000000000000000000000..62d8d3694ffebed5fea36003d85cb8d4db8abc0c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_frame.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_gettime.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_gettime.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0e3a214dfacd253b549a5341c6e6b5b66e4d784
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_gettime.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_lwip.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_lwip.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1b1bffc799875526f7031cfdd8570ea4de89c1f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_lwip.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_menuconfig_filesystem.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_menuconfig_filesystem.png
new file mode 100644
index 0000000000000000000000000000000000000000..438f4cb9901ff98eafd5d6b6ea68a99061c90793
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_menuconfig_filesystem.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_mqtt.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_mqtt.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d8140a5627172dc425e6f2286f086a805ae4a9f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_mqtt.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_mqtttest.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_mqtttest.png
new file mode 100644
index 0000000000000000000000000000000000000000..627336dfce204a43129a953f16879ee91c95ba0e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_mqtttest.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_netstate.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_netstate.png
new file mode 100644
index 0000000000000000000000000000000000000000..a326d74f61ff795977718e49aee55f7619fc2a5f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_netstate.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_ntp.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_ntp.png
new file mode 100644
index 0000000000000000000000000000000000000000..dda7ebce8dc1f458d8a378a173b37bf08b83293f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_ntp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_sai.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_sai.png
new file mode 100644
index 0000000000000000000000000000000000000000..06d0e28fd8c67fe6b548f5e3996b09bb30fb6881
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_sai.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcp_set.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcp_set.png
new file mode 100644
index 0000000000000000000000000000000000000000..aca0900d4acc9862d00022c8dc25c61d1b04d94d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcp_set.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcpclient.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcpclient.png
new file mode 100644
index 0000000000000000000000000000000000000000..aec33b859c789b99083d059002550651894d4d7e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcpclient.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcpclient_shell.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcpclient_shell.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0f8f7f4700fecd0d31294092a112e27f4db8e55
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_tcpclient_shell.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_udp_set.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_udp_set.png
new file mode 100644
index 0000000000000000000000000000000000000000..093e160d05cd5269704fed8a72dda61c334413f4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_udp_set.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_udpclient.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_udpclient.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0e916b4217de1204589257d85349cf8e2e0bcbc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/an011_udpclient.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/dns.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/dns.png
new file mode 100644
index 0000000000000000000000000000000000000000..4adb4826c1f79aa63f119ce546e95c7ceadc57d6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/dns.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/eth1.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/eth1.png
new file mode 100644
index 0000000000000000000000000000000000000000..f348bb847dbf55bf0f7fc3c8163709da41f08cd7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/eth1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/ethernet.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/ethernet.png
new file mode 100644
index 0000000000000000000000000000000000000000..84e20f70d186d424feadae1b231acaae8af63572
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/ethernet.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/etx.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/etx.png
new file mode 100644
index 0000000000000000000000000000000000000000..40dd277ba115bf07d8ab24bb57da7cf42be21f80
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/etx.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/filesystem.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/filesystem.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c271a45861d8d7e1f3477951c2b3fc54f80bf02
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/filesystem.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/lwip_0.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/lwip_0.png
new file mode 100644
index 0000000000000000000000000000000000000000..15465aec34aca25a72222fd9f72486821a73fc33
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/lwip_0.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/lwip_auto_open.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/lwip_auto_open.png
new file mode 100644
index 0000000000000000000000000000000000000000..5a69317ec5d86be8e829568b1ca07b0adbacd5c1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/lwip_auto_open.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/mqtt.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/mqtt.png
new file mode 100644
index 0000000000000000000000000000000000000000..c5274455143181baf317e0ccf55d7b3c0a3f22bb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/mqtt.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/net_device.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/net_device.png
new file mode 100644
index 0000000000000000000000000000000000000000..15cdb6a35c9b8561056cdcdf4d5b8c03cc554902
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/net_device.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/net_sample.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/net_sample.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea73629dbc241547e5a583533647f0b914e9ccb4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/net_sample.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/ntp.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/ntp.png
new file mode 100644
index 0000000000000000000000000000000000000000..d716bee6b86ebd1b27b6fd377dc04f04ac05ac6c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/ntp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/phy_reset.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/phy_reset.png
new file mode 100644
index 0000000000000000000000000000000000000000..f03daa15d021a3bc0d55d9a28c25c8a4d16213b5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/phy_reset.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/sal_frame.jpg b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/sal_frame.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..18513726f68ca2df6b722b17b2e07b9b48557c67
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/sal_frame.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/network/figures/static_ip.png b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/static_ip.png
new file mode 100644
index 0000000000000000000000000000000000000000..0fad733801d2d02c836cf8e6670f0f64fcf639ba
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/network/figures/static_ip.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/sfud/an0048-sfud.md b/rt-thread-version/rt-thread-standard/application-note/components/sfud/an0048-sfud.md
new file mode 100644
index 0000000000000000000000000000000000000000..72fb543888cc529c897fe129cd5b3b5186b175a1
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/components/sfud/an0048-sfud.md
@@ -0,0 +1,287 @@
+# 在潘多拉上使用 SFUD 操作 Flash
+
+[SFUD](https://github.com/armink/SFUD) 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。
+
+在使用 SFUD 时候需要进行移植,可以参考文件 [sfud/port/sfud_port.c](https://github.com/armink/SFUD/blob/master/sfud/port/sfud_port.c)。本篇文档将说明如何在潘多拉上使用 SFUD 操作 Flash(SFUD 部分已经移植完成),本文准备资料如下:
+
+- [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+- [Env 工具](https://www.rt-thread.org/page/download.html)
+- 板载 spi flash 的开发板,以潘多拉为例
+
+## Env 配置
+
+使用 SFUD 操作 Flash,需要在 Env 中打开: QSPI 或 SPI 总线、SFUD 组件。
+
+(1)在片上外设中,打开 QSPI/SPI 总线:根据实际情况选择 SPI/QSPI 总线,在本示例中使用的是 QSPI 总线。
+
+
+
+(2)在组件中,选中 SFUD 组件,保存并生成工程。
+
+
+
+## 使用流程
+
+如下图是 SFUD 的使用流程图,首先需要移植 SFUD 组件、对 flash 进行初始化,然后再进行应用:根据名称获取 sfud_dev,对 sfud_dev 进行擦写读的操作。
+
+
+
+(1)SFUD 移植:
+
+- SFUD 组件移植:可以参考文件 [sfud/port/sfud_port.c](https://github.com/armink/SFUD/blob/master/sfud/port/sfud_port.c) 或 RT-Thread 已实现的 [spi_flash_sfud.c](https://github.com/RT-Thread/rt-thread/blob/master/components/drivers/spi/spi_flash_sfud.c)。
+- Flash 设备支持:当目标 Flash 设备不支持 SFDP 功能时,需要在 sfud_flash_def.h 中定义 Flash 设备参数:
+
+```c
+// | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
+#define SFUD_FLASH_CHIP_TABLE \
+{ \
+ {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81}, \
+ {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
+ ....
+ {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
+ {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
+}
+#endif /* SFUD_USING_FLASH_INFO_TABLE */
+```
+
+(2)SPI flash 驱动实现([参考潘多拉 flash 驱动](https://github.com/RT-Thread/rt-thread/blob/master/bsp/stm32/stm32l475-atk-pandora/board/ports/drv_qspi_flash.c)):在 SPI flash 驱动中完成了 flash 的初始化(可以导出到自动初始化),执行了 spi 从设备的挂载与探测,如下是本次示例中的 flash 初始化代码:
+
+```c
+/* QSPI Flash 驱动 */
+static int rt_hw_qspi_flash_with_sfud_init(void)
+{
+ /* 往总线 qspi1 上挂载一个 qspi10 从设备 */
+ stm32_qspi_bus_attach_device("qspi1", "qspi10", RT_NULL, 4, w25qxx_enter_qspi_mode, RT_NULL);
+
+ /* 使用 SFUD 探测 qspi10 从设备,并将 qspi10 连接的 flash 初始化为块设备,名称 W25Q128 */
+ if (RT_NULL == rt_sfud_flash_probe("W25Q128", "qspi10"))
+ {
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+/* 导出到自动初始化 */
+INIT_COMPONENT_EXPORT(rt_hw_qspi_flash_with_sfud_init);
+```
+
+其中 qspi 模式需要实现进入 qspi 模式(如上面的 w25qxx_enter_qspi_mode),退出 qspi 模式为 RT_NULL 表示不再退出。stm32_qspi_bus_attach_device 原型如下:
+
+```c
+rt_err_t stm32_qspi_bus_attach_device(const char *bus_name,
+ const char *device_name,
+ rt_uint32_t pin, // CS 引脚编号
+ rt_uint8_t data_line_width, // QSPI 数据宽度:如 1/2/4
+ void (*enter_qspi_mode)(), // 进入 qspi 模式
+ void (*exit_qspi_mode)()); // 退出 qspi 模式
+```
+
+注:如果是 SPI 模式,则 SPI flash 驱动如下所示:
+
+```c
+/* SPI Flash 驱动 */
+static int rt_hw_spi_flash_init(void)
+{
+ /* 往总线 spi1 上挂载一个 spi10 从设备 */
+ rt_hw_spi_device_attach("spi1", "spi10", GPIOB, GPIO_PIN_14); // CS 脚:PB14
+
+ /* 使用 SFUD 探测 spi10 从设备,并将 spi10 连接的 flash 初始化为块设备,名称 W25Q128 */
+ if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
+ {
+ return -RT_ERROR;
+ };
+
+ return RT_EOK;
+}
+/* 导出到自动初始化 */
+INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
+```
+
+(3)sfud_dev 的获取有两种方法:通过 flash 名获取、通过从设备名获取,如下所示:
+
+```c
+// sfud_dev = rt_sfud_flash_find_by_dev_name("W25Q128");
+sfud_flash_t rt_sfud_flash_find_by_dev_name(const char *flash_dev_name);
+
+// sfud_dev = rt_sfud_flash_find("qspi10");
+sfud_flash_t rt_sfud_flash_find(const char *spi_dev_name);
+```
+
+## 应用示例
+
+使用 SFUD 组件,可通过命令行与编写应用两种方式对 flash 进行操作。
+
+### sf 命令操作 Flash
+
+使用 sf 查看 sf 命令的使用方法,并且对 qspi10 进行探测(sf probe 后再经过转换可以得到 sfud_dev):
+
+```c
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.3 build Apr 26 2020
+ 2006 - 2020 Copyright by rt-thread team
+[D/drv.qspi] qspi init success!
+[SFUD] Find a Winbond flash chip. Size is 16777216 bytes.
+[SFUD] W25Q128 flash device is initialize success.
+[D/drv.qspi] qspi init success!
+msh >
+msh >sf
+Usage:
+sf probe [spi_device] - probe and init SPI flash by given 'spi_device'
+sf read addr size - read 'size' bytes starting at 'addr'
+sf write addr data1 ... dataN - write some bytes 'data' to flash starting at 'addr'
+sf erase addr size - erase 'size' bytes starting at 'addr'
+sf status [ ] - read or write '1:volatile|0:non-volatile' 'status'
+sf bench - full chip benchmark. DANGER: It will erase full chip!
+msh >
+msh >sf probe qspi10
+[D/drv.qspi] qspi init success!
+[SFUD] Find a Winbond flash chip. Size is 16777216 bytes.
+[SFUD] sf_cmd flash device is initialize success.
+[D/drv.qspi] qspi init success!
+16 MB sf_cmd is current selected device.
+msh >
+```
+
+sf 擦、写、读操作测试(通过 sf 命令对获取到的 sfud_dev 进行擦写读操作):
+
+```c
+msh >sf erase 0 10
+Erase the sf_cmd flash data success. Start from 0x00000000, size is 10.
+
+msh >sf write 0 1 2 3
+Write the sf_cmd flash data success. Start from 0x00000000, size is 3.
+Write data: 1 2 3 .
+
+msh >sf read 0 10
+Read the sf_cmd flash data success. Start from 0x00000000, size is 10. The data is:
+Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+[00000000] 01 02 03 FF FF FF FF FF FF FF ..........
+```
+
+### 编写应用操作 Flash
+
+可以在应用中对 flash 进行读写擦操作,读写擦的操作均是基于 `sfud_dev` 设备,然后在对设备进行擦读写。如下是一个简单的示例:
+
+```c
+sfud_err result;
+uint8_t *read_data; // 读取到的数据
+uint8_t *write_data; // 将要写入的数据
+sfud_flash *sfud_dev = NULL;
+
+sfud_dev = rt_sfud_flash_find("qspi10"); // 获取 sfud_dev
+// 或者 sfud_dev = rt_sfud_flash_find_by_dev_name("W25Q128");
+
+sfud_erase(sfud_dev, 0, 4096); // 擦除从 0 开始的 4096 字节
+
+write_data = rt_malloc(32);
+rt_memset(write_data, 1, 32);
+sfud_write(sfud_dev, 0, 32, write_data); // 将数据 32 字节的 write_data 从 0 开始写入 flash
+
+read_data = rt_malloc(32);
+sfud_read(sfud_dev, 0, 32, read_data); // 读取从 0 开始的 32 字节,存入 read_data
+```
+
+这样 SFUD 操作 Flash 的应用就完成了。
+
+## 注意事项
+
+### 1. Flash 先擦后写
+
+写入之前请先擦除,这是 flash 特性决定的,因为 flash 的编程原理就是只能将 1 写为 0,而不能将 0 写为 1。擦除动作就是相应的页 / 块的所有位变为 1(所有字节均为 0xFF),所以不擦除直接写入会有问题。
+
+例如以下示例,对 qspi10 进行探测:
+
+```c
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.3 build Apr 26 2020
+ 2006 - 2020 Copyright by rt-thread team
+[D/drv.qspi] qspi init success!
+[SFUD] Find a Winbond flash chip. Size is 16777216 bytes.
+[SFUD] W25Q128 flash device is initialize success.
+[D/drv.qspi] qspi init success!
+msh >
+msh >sf probe qspi10
+[D/drv.qspi] qspi init success!
+[SFUD] Find a Winbond flash chip. Size is 16777216 bytes.
+[SFUD] sf_cmd flash device is initialize success.
+[D/drv.qspi] qspi init success!
+16 MB sf_cmd is current selected device.
+msh >
+```
+
+(1)首先进行不擦先写的测试,可以发现读出来的数据并非写入的数据:
+
+```c
+msh >sf write 0 1 2 3
+Write the sf_cmd flash data success. Start from 0x00000000, size is 3.
+Write data: 1 2 3 .
+msh >
+msh >sf read 0 10
+Read the sf_cmd flash data success. Start from 0x00000000, size is 10. The data is:
+Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+[00000000] 00 00 00 00 23 00 00 00 00 00 ....#.....
+```
+
+(2)然后进行先擦后写的测试,可以发现读出来的数据与写入数据一致:
+
+```c
+msh >sf erase 0 10
+Erase the sf_cmd flash data success. Start from 0x00000000, size is 10.
+msh >
+msh >sf read 0 20
+Read the sf_cmd flash data success. Start from 0x00000000, size is 20. The data is:
+Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+[00000000] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
+[00000010] FF FF FF FF ....
+msh >
+msh >sf read 4090 20
+Read the sf_cmd flash data success. Start from 0x00000FFA, size is 20. The data is:
+Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+[00000FFA] FF FF FF FF FF FF 2C 10 00 00 22 00 00 00 00 00 ......,...".....
+[0000100A] 00 00 DD 3A ...:
+
+msh >sf write 0 1 2 3
+Write the sf_cmd flash data success. Start from 0x00000000, size is 3.
+Write data: 1 2 3 .
+msh >
+msh >sf read 0 10
+Read the sf_cmd flash data success. Start from 0x00000000, size is 10. The data is:
+Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+[00000000] 01 02 03 FF FF FF FF FF FF FF ..........
+```
+
+### 2. Flash 按页 / 块擦除
+
+Flash 页大小一般是 4K、8K、16K 等,一个块也可能有 192、256、384、512 个页。Flash 按页擦除、按块擦除也是 flash 的特性之一。
+
+在上面步骤(2)中使用了 sf erase 命令进行擦除。虽然 `sf erase 0 10 ` 命令的意思是从 0 地址开始擦除 10 个字节,但是该 flash 是按照 4K 字节进行擦除的,所以会擦出从 0 开始的 4K 字节。使用读取 `sf read 4090 20` 命令进行验证,读取从 4090 开始的 20 个字节时,发现从 4096 及之后均未进行擦除,也就是擦除了从 0 开始的 4K 字节。
+
+`sf erase 2 10 ` 也是擦除从 0 开始的 4K 字节,而不是从 2 开始的 4K 字节。
+
+```c
+msh >sf erase 2 10
+Erase the sf_cmd flash data success. Start from 0x00000002, size is 10.
+msh >
+msh >sf read 0 10
+Read the sf_cmd flash data success. Start from 0x00000000, size is 10. The data is:
+Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+[00000000] FF FF FF FF FF FF FF FF FF FF ..........
+
+msh >sf read 4090 20
+Read the sf_cmd flash data success. Start from 0x00000FFA, size is 20. The data is:
+Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+[00000FFA] FF FF FF FF FF FF 2C 10 00 00 22 00 00 00 00 00 ......,...".....
+[0000100A] 00 00 DD 3A
+```
+
+### 3. Flash 写粒度
+
+在上面的示例中我们使用的是 spi nor flash,每次支持写入一个 bit 的数据。而不同的 flash 支持的写入粒度不尽相同,必须一次性写入写粒度的整数倍才可以写入成功。以下列举几种常见 Flash 写粒度:
+
+- nor flash: 1 bit
+- stm32f4: 8 bit
+- stm32f1: 32 bit
+- stm32l4: 64 bit
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/flash-select.png b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/flash-select.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b27c898d01d1c7b128f1e905ead824004b1c350
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/flash-select.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/process.png b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/process.png
new file mode 100644
index 0000000000000000000000000000000000000000..7f714c4467502e8c02cb3e4cc658f2c25ed68bf5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/process.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/qspi-flash.png b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/qspi-flash.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa0058cb728793cca5eb9e150bca286ff054c6ea
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/qspi-flash.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/sfud-comp.png b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/sfud-comp.png
new file mode 100644
index 0000000000000000000000000000000000000000..25bc9605efa942e81cb7e71c3b84e707a47b153d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/sfud-comp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/spi-bus.png b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/spi-bus.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a339d73784afab77fde0e1a9997bdd8864d406c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/components/sfud/figures/spi-bus.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/README.md b/rt-thread-version/rt-thread-standard/application-note/debug/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/an0013-CmBacktrace.md b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/an0013-CmBacktrace.md
new file mode 100644
index 0000000000000000000000000000000000000000..94f25baa1deba8492c089b4639c900b7423ded82
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/an0013-CmBacktrace.md
@@ -0,0 +1,233 @@
+# CmBacktrace 应用
+
+本文描述了在 RT-Thread 中使用 CmBacktrace。
+
+## 简介
+
+对于从 C51 、MSP430 等简单单片机转而使用更加复杂的 ARM 新人来说,时不时出现的 "hard falut" 死机会让新人瞬间懵掉。定位错误的方法也往往是连接上仿真器,一步步 F10/F11 单步,定位到具体的错误代码,再去猜测、排除、推敲错误原因,这种过程十分痛苦,且花费的时间很长。 当然,也有部分开发者通过故障寄存器信息来定位故障原因及故障代码地址,虽然这样能解决一小部分问题,但是重复的、繁琐的分析过程也会耽误很多时间。而且对于一些复杂问题,只依靠代码地址是无法解决的,必须得还原错误现场的函数调用逻辑关系。虽然连接仿真器可以查看到的函数调用栈,但故障状态下是无法显示的,所以还是得一步步 F10/F11 单步去定位错误代码的位置。另外,很多产品真机调试时必须断开仿真器,这又使定位错误代码雪上加霜。
+
+为了能让开发者更快的知道造成 hard falut 的原因,更快的定位到错误代码的位置,本文将一步步介绍 CmBacktrace 的相关知识和使用方法,让开发者能不费吹灰之力就找出代码中的问题所在。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+* 在 https://github.com/armink/CmBacktrace/tree/master/tools/addr2line 页面中下载 addr2line(需要按照自己的系统版本下载),然后将下载下来的 addr2line 拷贝至 `C:\Windows` 下 ,这样就可以使用 addr2line 了。
+
+## CmBacktrace 简介
+
+[CmBacktrace](https://github.com/armink/CmBacktrace) (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。
+
+主要特性如下:
+
+- 支持的错误包括:
+
+ - 断言(assert)
+
+ - 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
+
+- 故障原因 **自动诊断** :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器;
+
+- 输出错误现场的 **函数调用栈**(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈;
+
+- 支持 裸机 及以下操作系统平台:
+
+ - [RT-Thread](http://www.rt-thread.org)
+
+ - UCOS
+
+ - FreeRTOS(需修改源码)
+
+- 根据错误现场状态,输出对应的 线程栈 或 C 主栈;
+
+- 故障诊断信息支持多国语言(目前:简体中文、英文);
+
+- 适配 Cortex-M0/M3/M4/M7 MCU;
+
+- 支持 IAR、MDK、GCC 编译器;
+
+## 配置选项
+
+### ENV 配置
+
+RT-Thread 已经对 CmBacktrace 做了适配,直接在 ENV 使能 CmBacktrace 就可以使用了。
+
+下面介绍如何在 ENV 中配置 CmBacktrace:
+
+- 打开 ENV,进入相应的 BSP 目录
+- 输入 menuconfig
+- 进入 RT-Thread online packages -> tools packages
+- 使能 CmBacktrace
+- 进入 CmBacktrace 配置界面
+- 选择自己的 CPU 平台
+- 选择打印的语言
+- 选择版本,推荐使用最新版
+
+
+
+### 确认宏定义
+
+CmBacktrace 的运行需要知道存放代码的 SECTION 的开始地址和结束地址以及栈的 SECTION 的开始地址和结束地址。用户只需要查看 cmb_def.h 文件里默认定义的 CMB_CSTACK_BLOCK_NAME 和 CMB_CODE_SECTION_NAME 这两个宏是否正确即可。如不正确,用户需要根据分散加载文件和启动文件来确定这两个宏的值并在 cmb_cfg.h 里重新定义这两个宏。
+
+这里以 rt1052 的 mdk 工程为例进行讲解如何在工程里找到这两个宏的值。首先找 CMB_CSTACK_BLOCK_NAME 的值,我们打开工程里的启动文件,可以在文件的开头看到这样一段代码
+
+```
+ AREA RESET, DATA, READONLY
+ EXPORT __Vectors
+ EXPORT __Vectors_End
+ EXPORT __Vectors_Size
+ IMPORT |Image$$ARM_LIB_STACK$$ZI$$Limit|
+
+__Vectors DCD |Image$$ARM_LIB_STACK$$ZI$$Limit| ; Top of Stack
+```
+
+Image$$ARM_LIB_STACK$$ZI$$Limit 就是我们要找的内容了,因为代码里会自动拼接上最后的 $$Limit,所以 CMB_CSTACK_BLOCK_NAME 的值应该是 Image$$ARM_LIB_STACK$$ZI。
+
+CMB_CODE_SECTION_NAME 的值在分散加载文件里寻找,分散加载文件可以点击 MDK 的 Options -> Linker 选项面板里的 Edit... 按纽打开
+
+
+
+我们可以找到这样一段代码
+
+```
+ ER_IROM1 m_text_start m_text_size ; load address = execution address
+ {
+ * (RESET,+FIRST)
+ * (InRoot$$Sections)
+ .ANY (+RO)
+ }
+```
+
+保存有 .ANY (+RO) 的 SECTION 名字就是我们要找的值,所以,CMB_CODE_SECTION_NAME 的值为 ER_IROM1。
+
+### 开启 C99
+
+CmBacktrace 的使用需要 C99 的支持,使用 MDK 的开发者可以在 Options -> C/C++ 面板中勾选 C99 Mode 选项。
+
+
+
+使用 IAR 的开发者,可以在 Options -> C/C++ Compiler 中选择 C99。
+
+
+
+使用 GCC 进行编译的用户,在编译配置中增加 `-std=c99` 即可 。
+
+### 确定初始化参数
+
+在使用 CmBacktrace 之前需要先调用下初始化函数,函数原型如下:
+
+```
+void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver)
+```
+CmBacktrace 的初始化函数需要 3 个参数,第一个参数是固件名字,第二个参数是硬件版本,第三个参数是软件版本。这三个参数会在发生 hard fault 时打印出来,firmware_name 需要填写生成的固件名称,错误填写会导致在使用 addr2line 时无法找到文件。hardware_ver 和 software_ver 建议填写真实的软硬件版本号,方便后期调试和维护。在 cmb_port.c 文件中,我们可以看到 RT-Thread 已经将 rt_cm_backtrace_init 函数进行了自动初始化,默认的三个参数分别是 `rtthread`,`1.0`,`1.0`,开发者需要按照实际情况进行更改。
+
+## 运行示例代码
+
+CmBacktrace 提供了一个测试函数,提供除零测试和执行非对齐访问的测试。当做完上面的准备工作后,开发者可以直接将工程编译,下载进板子里,进行测试,判断 CmBacktrace 是否正常工作。
+
+CmBacktrace 导出到 finsh shell 中的测试函数命令为 `cmb_test`,输入 `cmb_test DIVBYZERO` 就是进行除零测试,输入 `cmb_test UNALIGNED` 就是执行非对齐访问的测试。
+
+我们看下运行完除零测试的结果:
+
+```
+msh />cmb_test DIVBYZERO
+thread pri status sp stack size max used left tick error
+-------- --- ------- ---------- ---------- ------ ---------- ---
+tshell 20 ready 0x00000100 0x00001000 23% 0x00000009 000
+phy 30 suspend 0x0000006c 0x00000200 30% 0x00000001 000
+tcpip 10 suspend 0x000000b4 0x00000800 17% 0x00000014 000
+etx 12 suspend 0x00000088 0x00000400 13% 0x00000010 000
+erx 12 suspend 0x00000088 0x00000400 13% 0x00000010 000
+mmcsd_de 22 suspend 0x00000090 0x00000400 49% 0x00000013 000
+tidle 31 ready 0x00000054 0x00000100 32% 0x00000018 000
+main 10 suspend 0x00000064 0x00000800 35% 0x00000012 000
+
+Firmware name: rtthread-imxrt, hardware version: 1.0, software version: 1.0
+Fault on thread tshell
+===== Thread stack information =====
+ addr: 80002ad0 data: 00000012
+ addr: 80002ad4 data: 6002ae58
+ addr: 80002ad8 data: 80001a40
+ addr: 80002adc data: 6000b575
+ addr: 80002ae0 data: 80001a40
+ addr: 80002ae4 data: 80001a49
+ addr: 80002ae8 data: 00000000
+ addr: 80002aec data: 00000000
+ addr: 80002af0 data: 00000000
+ addr: 80002af4 data: 00000000
+ addr: 80002af8 data: 00000000
+ addr: 80002afc data: 00000000
+ addr: 80002b00 data: 00000000
+ addr: 80002b04 data: 00000000
+ addr: 80002b08 data: 20000c7c
+ addr: 80002b0c data: 00000012
+ addr: 80002b10 data: 80001a40
+ addr: 80002b14 data: 20000c7c
+ addr: 80002b18 data: 00000001
+ addr: 80002b1c data: deadbeef
+ addr: 80002b20 data: deadbeef
+ addr: 80002b24 data: deadbeef
+ addr: 80002b28 data: deadbeef
+ addr: 80002b2c data: 60019ffb
+ addr: 80002b30 data: 00000001
+ addr: 80002b34 data: 0000000d
+ addr: 80002b38 data: 00000000
+ addr: 80002b3c data: 60015a7b
+ addr: 80002b40 data: 23232323
+====================================
+=================== Registers information ====================
+ R0 : 0000000a R1 : 00000000 R2 : 0000004f R3 : 80808000
+ R12: 01010101 LR : 6000c5ad PC : 6000c5c8 PSR: 41000000
+==============================================================
+Usage fault is caused by Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
+Show more call stack info by run: addr2line -e rtthread-imxrt.axf -a -f 6000c5c8 6000c5a9 6002ae54 6000b571 60019ff7 60015a77
+
+```
+
+CmBacktrace 首先打印出了发生 hard falut 时的所有线程信息,接着打印了固件名字和软硬件版本号,再打印了错误是发生在 tshell 这个线程里面的(因为我们是在 tshell 这个线程里调用的除零测试函数),紧接着打印的是线程的栈信息和寄存器信息。最后两行信息是最重要的,倒数第二行介绍了发生故障的原因,是因为除零造成的。最后一行提示如果需要获取函数调用栈,需要在 addr2line 中运行 CmBacktrace 给出的参数。
+
+在使用 addr2line 之前我们要先确认保存了工程对象文件的文件夹位置。使用 mdk 的开发者,可以在 Options -> Output 面板中查看,设置对象文件的保存路径。
+
+
+
+使用 IAR 的开发者,可以在 Options -> General Options 面板的 Output 选项中查看和设置。
+
+
+
+我们将 run:后面的所有内容都复制下来,然后进入保存了工程生成的对象文件的文件夹,打开 env,将刚刚复制的内容粘贴上去,按下回车,错误现场的函数调用栈就会输出出来,我们看下刚刚进行除零测试时的函数调用栈的信息
+
+```
+> addr2line -e rtthread-imxrt.axf -a -f 6000c5c8 6000c5a9 6002ae54 6000b571 60019ff7 60015a77
+0x6000c5c8
+cmb_test
+D:\rt-thread\bsp\imxrt1052-evk/packages\CmBacktrace-v1.2.0\/cmb_port.c:87
+0x6000c5a9
+cmb_test
+D:\rt-thread\bsp\imxrt1052-evk/packages\CmBacktrace-v1.2.0\/cmb_port.c:82
+0x6002ae54
+FSymTab$$Base
+??:?
+0x6000b571
+msh_get_cmd
+D:\rt-thread\bsp\imxrt1052-evk/..\..\components\finsh\/msh.c:312
+0x60019ff7
+msh_exec
+D:\rt-thread\bsp\imxrt1052-evk/..\..\components\finsh\/msh.c:335
+0x60015a77
+finsh_thread_entry
+D:\rt-thread\bsp\imxrt1052-evk/..\..\components\finsh\/shell.c:613
+```
+
+我们可以看到,CmBacktrace 不仅仅定位出了是 cmb_port.c 里第 87 行产生的问题,还打印出了函数调用逻辑关系,方便开发者进行 BUG 修复。
+
+> [!NOTE]
+> 注:* 使用前必须确认自己的 MCU 是 ARM Cortex-M0(+)/M3/M4/M7 架构,其他架构暂不支持。
+ * 使用前要确定 CMB_CSTACK_BLOCK_NAME 和 CMB_CODE_SECTION_NAME 两个宏的宏定义,如果定义错误,CmBacktrace 会无法正确使用。
+ * 初始化时要输入正确的固件名称,不然使用 addr2line 时会提示找不到文件
+ * 当线程的栈被写穿时,CmBacktrace 无法正常使用。
+
+## 参考资料
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/CmBacktrace_env.png b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/CmBacktrace_env.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd10ea9541f424f33ad90c8db210712ed742bc14
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/CmBacktrace_env.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/IAR_C99.png b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/IAR_C99.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae9b527e3951c99d711bd915f26d01d4958c1485
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/IAR_C99.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/IAR_object.png b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/IAR_object.png
new file mode 100644
index 0000000000000000000000000000000000000000..f006551f353a09612ca01b4dd1e1cc481140eb2d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/IAR_object.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/MDK_C99.png b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/MDK_C99.png
new file mode 100644
index 0000000000000000000000000000000000000000..6784b4336904b515ecc00c2148b250147dda918f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/MDK_C99.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/MDK_scf.png b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/MDK_scf.png
new file mode 100644
index 0000000000000000000000000000000000000000..9620a3fb21768a6e2401fff52c23af1bcacc1236
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/MDK_scf.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/mdk_object.png b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/mdk_object.png
new file mode 100644
index 0000000000000000000000000000000000000000..51df8e75c9b07b69636c2c39bc0a3ee1d3dde667
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/cmbacktrace/figures/mdk_object.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/an0009-systemview.md b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/an0009-systemview.md
new file mode 100644
index 0000000000000000000000000000000000000000..b762cfd3ccb99322fef09110722b84d2ce2651c0
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/an0009-systemview.md
@@ -0,0 +1,230 @@
+# SystemView 使用指南
+
+> [!NOTE]
+> 本文主要介绍 SystemView 可视化分析工具,以及如何在 RT-Thread 上使用它对系统进行调试分析。
+
+## 简介
+
+随着 MCU 的性能越来越强,嵌入式产品的功能越来越复杂,对于系统的调试和分析提出了新挑战,调试某个功能或问题通常需要花费大量精力,SystemView 是一款帮助用户进行系统调试和分析的强大工具,能够显著缩短开发和调试时间,提高开发效率。本文的目的在于帮助大家在 RT-Thread 上使用 SystemView 工具对系统进行调试和分析。
+
+本文准备资料如下:
+
+* [SystemView 软件包](https://github.com/RT-Thread-packages/SEGGER_SystemView)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+## SystemView 简介
+
+SystemView 是一个可以在线调试嵌入式系统的工具,它可以分析有哪些中断、任务执行了,以及这些中断、任务执行的先后关系。还可以查看一些内核对象持有和释放的时间点,比如信号量、互斥量、事件、消息队列等。这在开发和处理具有多个线程和事件的复杂系统时尤其有效。
+
+SystemView 由两个部分组成:
+
+* PC端程序,收集并展示来自嵌入端传来的数据,也可以将这些数据保存到本地供以后分析。
+* 嵌入式端程序,收集嵌入式系统的运行数据,并将它们通过 J-Link 的 RTT 模块传输给 PC 端
+
+> [!NOTE]
+> 因 SystemView 的数据传输利用了 J-Link 的 RTT 技术,所以只有用 J-Link 连接开发板的时候才能使用 SystemView
+
+
+
+RT-Thread 提供的 [SystemView 软件包](https://github.com/RT-Thread/segger_debug) 是 SystemView 工具的嵌入式端程序实现,主要功能有:配置 SYSTEMVIEW 和 RTT 的具体参数,收集和格式化监视数据,将数据通过 J-Link 发送给 PC 端等。只需要利用 RT-Thread 推出的 [Env 工具](https://www.rt-thread.org/page/download.html) 使能 SystemView 软件包,并对其进行简单的配置,就能完成 SystemView 的嵌入式端程序的配置。
+
+## 配置 SystemView 软件包
+
+以正点原子 RT1052 开发板为例
+
+步骤一:在 Env 工具中进入 menuconfig 图形化配置工具
+
+打开 Env 工具,使用命令 `cd D:\rt-thread\bsp\imxrt1052-evk` 切换到 RT-Thread 源码 BSP 根目录下的 imxrt1052-evk 目录,然后输入命令 `menuconfig` 配置工程。
+
+> [!NOTE]
+> menuconfig 是一种图形化配置工具,RT-Thread 使用其对整个系统进行配置、裁剪。
+
+
+
+步骤二:使能 SystemView 软件包。
+
+利用上下键选中 RT-Thread online packages,按回车键进入下级菜单,然后在 tools packages 中打开 SystemView 。
+
+
+
+步骤三:具体的配置。
+
+按回车键进入下级菜单,进行具体的配置(输入 ?可以显示选项的具体信息)。
+
+下面介绍两个常用的选项,其他的选项可以根据需要自己进行配置(参考最后一部分参数目录):
+
+(1) 将下图红框圈住的选项,更改为自己开发板对应的内核,如 I.MX-RT1052 对应 M7 内核
+
+
+
+(2) 进入 SystemView buffer configuration 下级菜单,关闭事后分析模式
+
+
+
+开启事后分析模式之后,**需要**在工程里调用函数 SEGGER_SYSVIEW_Start() 启动录制。系统事件会被不断的记录下来,并在缓冲区满时覆盖旧的事件。所以当读取缓冲区时,只能读取到最新的事件。
+
+> [!NOTE]
+> 当系统运行了很长时间并突然崩溃时,事后分析可能会很有用。在这种情况下,可以从目标中的缓冲区中读取最新的事件,SystemView 可以显示系统崩溃之前发生的情况。
+
+关闭事后分析模式之后,就处于连续记录模式,可以在 PC 端控制录制的开始和结束,连续记录系统的运行。要分析下面的 demo 的运行,需要开启连续记录模式。
+
+配置好选项之后,按 ESC 返回,退出并保存配置,这样 SystemView 软件包的使能和相关配置就完成了。
+
+后面我们以一个具体的 demo 来讲解 SystemView 工具的使用
+
+## 添加示例代码
+
+在文件 main.c 中添加以下代码,然后在 main 函数中调用 demo 初始化函数 `demo_init();` 运行 demo。
+
+```c
+/*
+* 程序清单:systemview 演示代码
+*
+* 这个例子中将创建一个动态信号量(初始值为0)及两个动态线程,在这个两个动态线程中
+* 线程2将试图采用永远等待方式去持有信号量,持有成功之后发送运行标志。
+* 线程1将先发送正在运行标志,然后释放一次信号量,因线程2的优先级较高,线程2持有到信号量将线程1抢断。
+* 然后线程2发送运行标志之后,获取不到信号量,被挂起,线程1继续运行
+*/
+
+#define THREAD_PRIORITY 25
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+/* 指向信号量的指针 */
+rt_sem_t sem_food;
+/* 线程1入口 */
+void thread1_entry(void* parameter)
+{
+ while (1)
+ {
+ /* 线程1第一次运行 */
+ rt_kprintf("thread1 is run!\n");
+ /* 释放一次信号量 */
+ rt_sem_release(sem_food);
+ /* 线程1第二次运行 */
+ rt_kprintf("thread1 run again!\n");
+ /* 线程1延时1秒 */
+ rt_thread_delay(RT_TICK_PER_SECOND);
+ }
+}
+/* 线程2入口 */
+void thread2_entry(void* parameter)
+{
+ while (1)
+ {
+ /* 试图持有信号量,并永远等待直到持有到信号量 */
+ rt_sem_take(sem_food, RT_WAITING_FOREVER);
+ /* 线程2正在运行 */
+ rt_kprintf("thread2 is run!\n");
+ }
+}
+/* DEMO初始化函数 */
+void demo_init(void)
+{
+ /* 指向线程控制块的指针 */
+ rt_thread_t thread1_id, thread2_id;
+ /* 创建一个信号量,初始值是0 */
+ sem_food = rt_sem_create("sem_food", 0, RT_IPC_FLAG_FIFO);
+ if (sem_food == RT_NULL)
+ {
+ rt_kprintf("sem created fail!\n");
+ return ;
+ }
+ /* 创建线程1 */
+ thread1_id = rt_thread_create("thread1",
+ thread1_entry, RT_NULL,/* 线程入口是thread1_entry, 参数RT_NULL */
+ THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
+ if (thread1_id != RT_NULL)
+ rt_thread_startup(thread1_id);
+ /* 创建线程2 */
+ thread2_id = rt_thread_create("thread2",
+ thread2_entry, RT_NULL,/* 线程入口是thread2_entry, 参数RT_NULL */
+ THREAD_STACK_SIZE, THREAD_PRIORITY - 1, THREAD_TIMESLICE);
+ if (thread2_id != RT_NULL)
+ rt_thread_startup(thread2_id);
+}
+```
+
+## SystemView 配置及使用
+
+步骤一:下载 SystemView 分析工具 [下载链接](https://www.segger.com/products/development-tools/systemview/)
+
+步骤二:为 RT-Thread 添加系统描述文件
+
+首先找到开发板目录下的 packages 目录,然后在 packages 目录下找到 segger_debug-xxx 目录,在这个目录里面有一个 SystemView_Description 文件夹,RT-Thread 系统的描述文件就在里面,具体的目录结构如下所示:
+
+`bsp\\你自己的开发板\\packages\\segger_debug-xxx\\SystemView_Description\\SYSVIEW_RT-Thread.txt`
+
+将这个文件复制到 SystemView 工具安装目录下的 Description 目录下,这样 SystemView 就可以识别出 RT-Thread 系统了。
+
+步骤三:配置设备信息,开始录制
+
+双击打开 SystemView PC端程序,下面是主界面上一些常用按钮的功能介绍。
+
+
+
+当处于连续记录模式时,点击开始录制按钮,会弹出一个配置设备信息的窗口。
+
+
+
+图中 RTT 控制块的地址已经通过串口打印出来了,只要打开终端软件将打印出来的地址复制到上方即可。
+
+
+
+点击 OK ,现在 SystemView 就开始实时录制系统信息了,下面是系统实时的运行情况。
+
+
+
+步骤四:结束录制,分析系统
+
+点击结束录制按钮,结束录制。将鼠标放置到时间轴窗口里,利用滚轮将事件放大到适合分析的大小
+
+
+
+利用 SystemView 工具我们可以看出来,系统的运行确实如我们设想的那样,线程1先开始运行,然后在释放信号量之后被线程2抢断。然后线程2运行,之后因获取不到信号量被挂起,线程1继续运行。从上面的事件列表还可以看到每个线程获取或释放信号量的具体时间。
+
+> [!NOTE]
+> 如果开启了事后分析模式,就不能点击开始录制按钮了,而是要点击读记录数据的按钮,其他的的操作和实时分析模式一样。
+
+看完这篇文档,相信你已经学会如何使用 RT-Thread 上 的 SystemView 分析工具了。
+
+## menuconfig 配置选项
+
+| **参数** | **描述** |
+| ---------------------------- | ------------------------------------------------------------ |
+| App name | 应用程序的名字 |
+| Device name | 设备所用内核 |
+| Timestap freq | 时间戳频率 (0 表示使用系统默认频率) |
+| cpu freq | cpu频率(0 表示使用系统默认频率) |
+| RAM base | RAM基地址 默认值:0x2000 0000 |
+| Event ID offset | 事件ID的偏移 默认值:32 |
+| Using the Cortex-M cycle ... | 使用系统频率作为时间戳 |
+| System description 0-2 | 系统描述符 "I#num=name, ..." num 是中断标号, name 是中断名称 |
+
+| **参数** | **描述** |
+| ----------------------------------------- | -------------------------------------------------- |
+| Max num of up buffer | RTT输出缓冲区的最大数目 默认值:2 |
+| Max num of dowm buffer | RTT输入缓冲区的最大数目 默认值:2 |
+| buffer size up | 用于RTT终端输出通道的字节数 默认值:1024 字节 |
+| buffer size down | 用于RTT终端输入通道的字节数 默认值:16 字节 |
+| Segger RTT printf buffer size | 通过RTT printf发送字符块时的缓冲区大小 默认值:64 |
+| Mode for pre-initialized terminal channel | 预初始RTT终端通道模式 默认值: NO_BLOCK_SKIP |
+| Max Interrupt priority | 中断优先级的最大值 |
+| Use RTT ASM | 使用汇编版本的RTT |
+| memcpy use byte-loop | 使用一个简单的byte-loop代替memcpy |
+
+| **参数** | **描述** |
+| -------------------------------- | ---------------------------------------------------- |
+| RTT buffer size | SystemView用于记录缓冲区的字节数 |
+| RTT channel | RTT通道是用于SystemView事件记录和通信,0表示自动选择 |
+| Use static buffer | 使用静态缓冲区 ,能够节省空间 |
+| Enable post mortem analysis mode | 使能事后分析模式 |
+
+| **参数** | **描述** |
+| -------- | ------------------------------------------------------- |
+| ID Base | 从SystemView包中记录的ID中减去的值 默认值:0x1000 0000 |
+| ID Shift | 在SystemView包中记录的ID偏移的位数 默认值:2 |
+
+## 参考资料
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/PC1.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/PC1.png
new file mode 100644
index 0000000000000000000000000000000000000000..75e2bbd2db8c965946549eb49fad967b749438ef
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/PC1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/PC2.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/PC2.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7f47a62b7ed8b640f8fee34a9f2f495341fca65
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/PC2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/RTT.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/RTT.png
new file mode 100644
index 0000000000000000000000000000000000000000..4afd632152cb51b21f1ca2c1cdf6b1137f2acfcb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/RTT.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/env1.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/env1.png
new file mode 100644
index 0000000000000000000000000000000000000000..46c795681031c35b9c4e71cb4cb1b1d5adb41b1b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/env1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/env3.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/env3.png
new file mode 100644
index 0000000000000000000000000000000000000000..fcc49173e2de2028b612467ecbbdcd5871a55fc6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/env3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/run.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/run.png
new file mode 100644
index 0000000000000000000000000000000000000000..4904b2fba251b3e361c427a0b1968e19af6a5d7e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/run.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/run1.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/run1.png
new file mode 100644
index 0000000000000000000000000000000000000000..fdc44d42cae9e4f0a1c1546a98af09d735a3ac6a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/run1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/systemview.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/systemview.png
new file mode 100644
index 0000000000000000000000000000000000000000..b31db2773e15d6934cd17eaea3b8ede289ef2c4a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/systemview.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/systemview1.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/systemview1.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a1464773f473e95d7b9d3e6c687c389b218579e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/systemview1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/view.png b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/view.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd65692041c4679053795564100b87b899a79c61
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/debug/systemview/figures/view.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/an0002-rtthread-driver-gpio.md b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/an0002-rtthread-driver-gpio.md
new file mode 100644
index 0000000000000000000000000000000000000000..7835a61835a5df1f0d299f48f2c3f5e8629553bd
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/an0002-rtthread-driver-gpio.md
@@ -0,0 +1,210 @@
+# PIN 设备应用笔记 #
+
+本文描述了如何使用 RT-Thread 的PIN 设备驱动,包括驱动的配置、相关 API 的应用。并给出了在正点原子 STM32F4 探索者开发板上验证的代码示例。
+
+## 本文的目的和结构 ##
+
+### 本文的目的和背景 ###
+
+为了给用户提供操作 GPIO 的通用 API,方便应用程序开发,RT-Thread 提供了类似 Arduino 风格的 API 用于操作 GPIO,如设置 GPIO 模式和输出电平、读取 GPIO 输入电平、配置 GPIO 外部中断。本文说明了如何使用 RT-Thread 提供的 PIN 设备接口。
+
+### 本文的结构 ###
+
+本文给出了在正点原子 STM32F4 探索者开发板上验证的代码示例。
+
+## 硬件平台简介
+
+本文基于正点原子 STM32F4 探索者开发板,给出了PIN 设备的具体应用示例代码,包含管脚输入、输出和外部中断的使用方法。由于 RT-Thread 上层应用 API 的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。
+
+正点原子 STM32F4 探索者开发板使用的 MCU 是 STM32F407ZGT6,板载 2 颗 LED 和 4 个独立按键。LED 分别连接到 MCU 的 GPIOF9、GPIOF10,KEY0 按键连接到 GPIOE4,KEY1 按键连接到 GPIOE3,KEY2 按键连接到 GPIOE2,WK_UP 按键连接到 GPIOA0,2 颗 LED 均为低电平点亮,独立按键 KEY0、KEY1、KEY2 按下为低电平;WK_UP 按下为高电平。
+
+
+
+## 准备和配置工程
+
+1. 下载 [RT-Thread 源码](https://github.com/RT-Thread/rt-thread)
+
+2. 下载 [示例代码](main.rar)
+
+3.
+
+4. 进入 `rt-thread\bsp\stm32f4xx-HAL` 目录,在 Env 命令行中输入 menuconfig,进入配置界面,使用 menuconfig 工具(学习如何使用)配置工程。
+
+(1) 在 menuconfig 配置界面依次选择 RT-Thread Components ---> Device Drivers ---> Using generic GPIO device drivers,如图所示:
+
+
+
+(2) 输入命令
+`scons --target=mdk5 -s`
+生成 mdk5 工程。将示例代码附带的 main.c 替换掉 BSP 中的 main.c,如图所示:
+
+
+
+(3) 编译,下载程序,在终端输入 list_device 命令可以看到 device 是 pin、类型是 Miscellaneous Device 就说明PIN 设备驱动添加成功了。
+
+
+
+下面是 3 个PIN 设备驱动 API 应用示例,分别是:GPIO 输出、GPIO 输入、GPIO 外部中断,这些代码在正点原子 STM32F4 探索者开发板上验证通过。
+
+## GPIO 输出配置
+
+示例 1:配置 GPIO 为输出,点亮 LED。根据原理图,GPIOF9 连接到了板载红色 LED,丝印为 DS0;GPIOF10 连接到了板载绿色 LED,丝印为 DS1。GPIOF9 输出低电平则点亮 DS0,GPIOF9 输出高电平则 DS0 不亮;GPIOF10 输出低电平则点亮 DS1,GPIOF10 输出高电平则 DS1 不亮。
+
+
+
+```c
+#define LED0 21 //PF9--21,在 drv_gpio.c 文件 pin_index pins[] 中查到 PF9 编号为 21
+#define LED1 22 //PF10--22,在 drv_gpio.c 文件 pin_index pins[] 中查到 PF10 编号为 22
+ void led_thread_entry(void* parameter)
+{
+ // 设置管脚为输出模式
+ rt_pin_mode(LED0, PIN_MODE_OUTPUT);
+ // 设置管脚为输出模式
+ rt_pin_mode(LED1, PIN_MODE_OUTPUT);
+ while (1)
+ {
+ // 输出低电平,LED0 亮
+ rt_pin_write(LED0, PIN_LOW);
+ // 输出低电平,LED1 亮
+ rt_pin_write(LED1, PIN_LOW);
+ // 挂起 500ms
+ rt_thread_delay(rt_tick_from_millisecond(500));
+
+ // 输出高电平,LED0 灭
+ rt_pin_write(LED0, PIN_HIGH);
+ // 输出高电平,LED1 灭
+ rt_pin_write(LED1, PIN_HIGH);
+ // 挂起 500ms
+ rt_thread_delay(rt_tick_from_millisecond(500));
+ }
+}
+```
+
+在线程入口函数 led_thread_entry 里首先调用 rt_pin_mode 设置管脚模式为输出模式,然后就进入 while(1) 循环,间隔 500ms 调用 rt_pin_write 来改变 GPIO 输出电平。
+下面是创建线程的代码:
+
+```c
+ rt_thread_t tid;// 线程句柄
+ /* 创建 led 线程 */
+ tid = rt_thread_create("led",
+ led_thread_entry,
+ RT_NULL,
+ 1024,
+ 3,
+ 10);
+ /* 创建成功则启动线程 */
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+```
+
+编译、下载程序,我们将看到 LED 间隔 500ms 闪烁的现象。
+
+## GPIO 输入配置
+
+示例 2:配置 GPIOE3、GPIOE2 为上拉输入,GPIOA0 为下拉输入,检测按键信号。根据原理图,GPIOE3 连接到按键 KEY1,按键被按下时 GPIOE3 应读取到低电平,按键没有被按下时 GPIOE3 应读取到高电平;GPIOE2 连接到按键 KEY2,按键被按下时 GPIOE2 应读取到低电平,按键没有被按下时 GPIOE2 应读取到高电平;GPIOA0 连接到按键 WK_UP,按键被按下时 GPIOA0 应读取到高电平,按键没有被按下时 GPIOA0 应读取到低电平。
+
+
+
+```c
+#define KEY1 2 //PE3--2,在 drv_gpio.c 文件 pin_index pins[] 中查到 PE3 编号为 2
+#define KEY2 1 //PE2--1,在 drv_gpio.c 文件 pin_index pins[] 中查到 PE2 编号为 1
+#define WK_UP 34 //PA0--34,在 drv_gpio.c 文件 pin_index pins[] 中查到 PA0 编号为 34
+void key_thread_entry(void* parameter)
+{
+ //PE2、PE3 设置上拉输入
+ rt_pin_mode(KEY1, PIN_MODE_INPUT_PULLUP);
+ rt_pin_mode(KEY2, PIN_MODE_INPUT_PULLUP);
+ //PA0 设置为下拉输入
+ rt_pin_mode(WK_UP, PIN_MODE_INPUT_PULLDOWN);
+
+ while (1)
+ {
+ // 检测到低电平,即按键 1 按下了
+ if (rt_pin_read(KEY1) == PIN_LOW)
+ {
+ rt_kprintf("key1 pressed!\n");
+ }
+ // 检测到低电平,即按键 2 按下了
+ if (rt_pin_read(KEY2) == PIN_LOW)
+ {
+ rt_kprintf("key2 pressed!\n");
+ }
+ // 检测到高电平,即按键 wp 按下了
+ if (rt_pin_read(WK_UP) == PIN_HIGH)
+ {
+ rt_kprintf("WK_UP pressed!\n");
+ }
+ // 挂起 10ms
+ rt_thread_delay(rt_tick_from_millisecond(10));
+ }
+}
+```
+
+在线程入口函数 key_thread_entry 里首先调用 rt_pin_mode 设置管脚 GPIOE3 为上拉输入模式。这样当用户按下按键 KEY1 时,GPIOE3 读取到的电平是低电平;按键未被按下时,GPIOE3 读取到的电平是高电平。然后进入 while(1) 循环,调用 rt_pin_read 读取管脚 GPIOE3 电平,如果读取到低电平则表示按键 KEY1 被按下,就在终端打印字符串 "key1 pressed!"。每隔 10ms 检测一次按键输入情况。
+下面是创建线程的代码:
+
+```c
+ rt_thread_t tid;
+ /* 创建 key 线程 */
+ tid = rt_thread_create("key",
+ key_thread_entry,
+ RT_NULL,
+ 1024,
+ 2,
+ 10);
+ /* 创建成功则启动线程 */
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+```
+
+编译、下载程序,我们按下开发板上的用户按键,终端将打印提示字符。
+
+## GPIO 中断配置
+
+示例 3:配置 GPIO 为外部中断模式、下降沿触发,检测按键信号。根据原理图,GPIOE4 连接到按键 KEY0,按键被按下时 MCU 应探测到电平下降沿。
+
+```c
+#define KEY0 3 //PE4--3,在 gpio.c 文件 pin_index pins[] 中查到 PE4 编号为 3
+void hdr_callback(void *args)// 回调函数
+{
+ char *a = args;// 获取参数
+ rt_kprintf("key0 down! %s\n",a);
+}
+
+void irq_thread_entry(void* parameter)
+{
+ // 上拉输入
+ rt_pin_mode(KEY0, PIN_MODE_INPUT_PULLUP);
+ // 绑定中断,下降沿模式,回调函数名为 hdr_callback
+ rt_pin_attach_irq(KEY0, PIN_IRQ_MODE_FALLING, hdr_callback, (void*)"callback
+args");
+ // 使能中断
+ rt_pin_irq_enable(KEY0, PIN_IRQ_ENABLE);
+
+}
+```
+
+在线程入口函数 irq_thread_entry 里首先调用 rt_pin_attach_irq 设置管脚 GPIOE4 为下降沿中断模式,并绑定了中断回调函数,还传入了字符串 "callback args"。然后调用 rt_pin_irq_enable 使能中断,这样按键 KEY0 被按下时 MCU 会检测到电平下降沿,触发外部中断,在中断服务程序中会调用回调函数 hdr_callback,在回调函数中打印传入的参数和提示信息。
+下面是创建线程的代码:
+
+```c
+ rt_thread_t tid;// 线程句柄
+ /* 创建 irq 线程 */
+ tid = rt_thread_create("exirq",
+ irq_thread_entry,
+ RT_NULL,
+ 1024,
+ 4,
+ 10);
+ /* 创建成功则启动线程 */
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+```
+
+编译、下载程序,我们按下按键 KEY0,终端将打印提示字符。
+
+## 参考资料
+
+* 《PIN设备》
+
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_2.png b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..8341cac5a0a414e8bbf635b32e22b28f30b9065d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_3.png b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_3.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa67376d164811c3f9f3f7c63a2b0261281620ed
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_4.png b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_4.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a6c9ab5bad96348c5e11b77e5265f6205197b6f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_4.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_5.png b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_5.png
new file mode 100644
index 0000000000000000000000000000000000000000..9776247c524688a7906573465ce22f3428f07faa
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_5.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_6.png b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_6.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c4c9be400eeb73271421e6ab4b70098743fb871
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_6.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_7.png b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_7.png
new file mode 100644
index 0000000000000000000000000000000000000000..71d624f409123eaec67a03335de9a87a906d82e9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_7.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_9.png b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_9.png
new file mode 100644
index 0000000000000000000000000000000000000000..44aa67d5a59d5c59cb076fc98ab73299cbea8b9f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/figures/an0002_9.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/gpio/main.rar b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/main.rar
new file mode 100644
index 0000000000000000000000000000000000000000..a5ed59540bfedf3e812f7ec53c6d894c96c08a99
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/gpio/main.rar differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/an0003-rtthread-driver-i2c.md b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/an0003-rtthread-driver-i2c.md
new file mode 100644
index 0000000000000000000000000000000000000000..85a3fac86f3e2a1eefdb533b12794cc417258421
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/an0003-rtthread-driver-i2c.md
@@ -0,0 +1,306 @@
+# I2C 设备应用笔记 #
+
+本文以驱动 I2C 接口的 6 轴传感器 MPU6050 为例,说明了如何使用 I2C 设备驱动接口开发应用程序,并详细讲解了 RT-Thread I2C 设备驱动框架及相关函数。
+
+## 本文的目的和结构 ##
+
+### 本文的目的和背景 ###
+
+I2C(或写作 i2c、IIC、iic)总线是由 Philips 公司开发的一种简单、双向二线制(时钟 SCL、数据 SDA)同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息,是半导体芯片使用最为广泛的通信接口之一。RT-Thread 中引入了 I2C 设备驱动框架,I2C 设备驱动框架提供了基于 GPIO 模拟和硬件控制器的 2 种底层硬件接口。
+
+### 本文的结构 ###
+
+本文描述了使用 I2C 设备驱动接口编写 MPU6050 的驱动程序,并给出了在正点原子 STM32F4 探索者开发板上验证的代码示例。
+
+## 运行 I2C 设备驱动示例代码 ##
+
+### 示例代码软硬件平台 ###
+
+1. [正点原子 STM32F4 探索者开发板](http://www.openedv.com/thread-13912-1-1.html)
+2. GY-521 MPU-6050 模块
+3. MDK5
+4. [RT-Thread 源码](https://github.com/RT-Thread/rt-thread)
+5. [示例代码](i2c-mpu6050.rar)
+6.
+
+正点原子探索者 STM32F4 开发板的 MCU 是 STM32F407ZGT6,本示例使用 USB 串口(USART1)发送数据及供电,使用 SEGGER JLINK 连接 JTAG 调试。
+
+本次实验用的 GY521 模块是一款 6 轴惯性传感器模块,板载 MPU6050。我们使用开发板的 PD6(SCL)、PD7(SDA)作为模拟 I2C 管脚,用杜邦线将 GY521 模块的 SCL 硬件连接到 PD6、SDA 连接到 PD7、GND 连接到开发板的 GND、VCC 连接到 3.3V。
+
+
+
+
+
+本文基于正点原子 STM32F4 探索者开发板,给出了底层 I2C 驱动(GPIO 模拟方式)的添加方法和 I2C 设备的具体应用示例代码(以驱动 MPU6050 为例),包含寄存器读、写操作方法。由于 RT-Thread 上层应用 API 的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。
+
+### 启用 I2C 设备驱动 ###
+
+1. 使用 [Env 工具](https://www.rt-thread.org/document/site/zh/5chapters/01-chapter_env_manual/) 命令行进入 `rt-thread\bsp\stm32f4xx-HAL` 目录,然后输入 `menuconfig` 命令进入配置界面。
+2. 配置 shell 使用串口 1:选中 Using UART1,进入 RT-Thread Kernel ---> Kernel Device Object 菜单,修改 the device name for console 为 uart1。
+3. 进入 RT-Thread Components ---> Device Drivers 菜单,选中 Using I2C device drivers,本示例使用 GPIO 模拟 I2C,因此还要开启 Use GPIO to simulate I2C。
+
+
+
+4. 退出 menuconfig 配置界面并保存配置,在 Env 命令行输入 `scons --target=mdk5 -s` 命令生成 mdk5 工程,新工程名为 project。使用 MDK5 打开工程,修改 MCU 型号为 STM32F407ZGTx,修改调试选项为 J-LINK。
+
+
+
+
+
+5. 编译工程后下载程序至开发板运行。在终端 PuTTY(打开对应端口,波特率配置为 115200) 输入 `list_device` 命令可以看到名为 i2c2 的设备,设备类型是 I2C Bus,说明 I2C 设备驱动添加成功了。如图所示:
+
+
+
+### 运行示例代码 ###
+
+将 I2C 示例代码里的 `main.c` 拷贝到 `\rt-thread\bsp\stm32f4xx-HAL\applications` 目录,替换原有的 `main.c`。
+`drv_mpu6050.c、drv_mpu6050.h` 拷贝到 `\rt-thread\bsp\stm32f4xx-HAL\drivers` 目录,并将它们添加到工程中对应分组。如图所示:
+
+
+
+本例使用 GPIO PD6 作为 SCL、GPIO PD7 作为 SDA,I2C 总线设置名字是 i2c2,读者可根据需要修改 `drv_i2c.c` 件中如下参数以适配自己的板卡,确保 `drv_mpu6050.c` 中定义的宏 MPU6050_I2C_BUS_NAME 与 `drv_i2c.c` 中的宏 I2C_BUS_NAME 相同。本示例需要将 `drv_i2c.c` 默认驱动端口 GPIOB 改为 GPIOD,如下图所示:
+
+
+
+连接好 MPU6050 模块和开发板,编译工程并下载程序至开发板,复位 MCU,终端 PuTTY 会打印出读取到的 MPU6050 传感器数据,依次是温度,三轴加速度,三轴角速度:
+
+
+
+## 示例代码详解 ##
+
+按照前文的步骤,相信读者能很快的将 RT-ThreadI2C 设备驱动运行起来,那么如何使用 I2C 设备驱动接口开发应用程序呢?
+
+RT-Thread I2C 设备驱动目前只支持主机模式,使用 RT-Thread I2C 设备驱动需要使用 menuconfig 工具开启宏 RT_USING_DEVICE 和 RT_USING_I2C,如果要使用 GPIO 模拟 I2C 还需开启宏 RT_USING_I2C_BITOPS。
+
+使用 I2C 设备驱动的大致流程如下:
+
+1. 用户可以在 msh shell 输入 `list_device` 命令查看已有的 I2C 总线设备,确定 I2C 总线设备名称。
+
+2. 查找设备使用 `rt_i2c_bus_device_find()` 或者 `rt_device_find()`,传入 I2C 设备名称获取 i2c 总线设备句柄。
+
+3. 使用 `rt_i2c_transfer()` 即可以发送数据也可以接收数据。
+
+接下来本章将详细讲解 I2C 设备驱动接口的使用。
+
+### 查找设备 ###
+
+应用程序要使用已经由操作系统管理的 I2C 设备需要调用查找设备函数,找到 I2C 设备后才可以对该设备进行信息传送。可使用`rt_device_find()`查找 I2C 设备。
+
+本文示例代码底层驱动 `drv_mpu6050.c` 中 `mpu6050_hw_init()` 查找设备源码如下:
+
+```c
+#define MPU6050_I2CBUS_NAME "i2c2" /* I2C 设备名称, 必须和 drv_i2c.c 注册的 I2C 设备名称一致 */
+static struct rt_i2c_bus_device *mpu6050_i2c_bus; /* I2C 设备句柄 */
+... ...
+... ...
+
+int mpu6050_hw_init(void)
+{
+ rt_uint8_t res;
+
+ mpu6050_i2c_bus = rt_device_find(MPU6050_I2CBUS_NAME); /* 查找 I2C 设备 */
+
+ if (mpu6050_i2c_bus == RT_NULL)
+ {
+ MPUDEBUG("can't find mpu6050 %s device\r\n",MPU6050_I2CBUS_NAME);
+ return -RT_ERROR;
+ }
+
+... ...
+... ...
+}
+
+```
+
+### 数据传输 ###
+
+RT-Thread I2C 设备驱动的核心 API 是 `rt_i2c_transfer()`,它传递的消息是链式结构的。可以通过消息链,实现调用一次完成多次数据的收发,此函数既可以用于发送数据,也可以用于接收数据。
+
+#### 发送数据 ####
+
+`drv_mpu6050.c` 中的 `mpu6050_write_reg()` 函数是 MCU 向 mpu6050 寄存器写数据。此函数的实现共有 2 种,分别调用了 I2C 设备驱动接口 `rt_i2c_transfer()` 和 `rt_i2c_master_send()` 实现。
+
+本文示例使用的 MPU6050 数据手册中提到 7 位从机地址是 110100X,X 由芯片的 AD0 管脚决定,GY521 模块的 AD0 连接到了 GND,因此 MPU6050 作为从机时地址是 1101000,16 进制形式是 0x68。写 MPU6050 某个寄存器,主机首先发送从机地址 MPU6050_ADDR、读写标志 R/W 为 RT_I2C_WR(0 为写,1 为读),然 后主机发送从机寄存器地址 reg 及数据 data。
+
+1) 使用 rt_i2c_transfer() 发送数据
+
+本文示例代码底层驱动 `drv_mpu6050.c` 发送数据源码如下:
+
+```c
+#define MPU6050_ADDR 0X68
+
+// 写 mpu6050 单个寄存器
+//reg: 寄存器地址
+//data: 数据
+// 返回值: 0, 正常 / -1, 错误代码
+rt_err_t mpu6050_write_reg(rt_uint8_t reg, rt_uint8_t data)
+{
+ struct rt_i2c_msg msgs;
+ rt_uint8_t buf[2] = {reg, data};
+
+ msgs.addr = MPU6050_ADDR; /* 从机地址 */
+ msgs.flags = RT_I2C_WR; /* 写标志 */
+ msgs.buf = buf; /* 发送数据指针 */
+ msgs.len = 2;
+
+ if (rt_i2c_transfer(mpu6050_i2c_bus, &msgs, 1) == 1)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+```
+
+以本文示例代码其中一次调用 `rt_i2c_transfer()` 发送数据为例,从机 MPU6050 地址 16 进制值为 0X68,寄存器地址 reg 16 进制值为 0X6B,发送的数据 data 16 进制值为 0X80。示例波形如下图所示,第一个发送的数据是 0XD0,第一个数据的高 7 位是从机地址,最低位是读写位为写(值为 0),所以第一个数据为:0X68 << 1|0 = 0XD0,然后依次发送寄存器地址 0X6B 和数据 0X80。
+
+
+
+2) 使用 rt_i2c_master_send() 发送数据
+
+本文示例代码底层驱动 `drv_mpu6050.c` 发送数据源码如下:
+
+```c
+#define MPU6050_ADDR 0X68
+
+// 写 mpu6050 单个寄存器
+//reg: 寄存器地址
+//data: 数据
+// 返回值: 0, 正常 / -1, 错误代码
+rt_err_t mpu6050_write_reg(rt_uint8_t reg, rt_uint8_t data)
+{
+ rt_uint8_t buf[2];
+
+ buf[0] = reg;
+ buf[1] = data;
+
+ if (rt_i2c_master_send(mpu6050_i2c_bus, MPU6050_ADDR, 0, buf ,2) == 2)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+```
+
+#### 接收数据 ####
+
+用户可以调用 I2C 设备驱动接口 `rt_i2c_master_recv()` 或者 `rt_i2c_transfer()` 接受数据。
+
+本文示例代码 `drv_mpu6050.c` 中的 `mpu6050_read_reg()` 函数是 MCU 从 MPU6050 寄存器读取数据,此函数的实现同样有 2 种方式,分别调用了 I2C 设备驱动接口 `rt_i2c_transfer()` 和 `rt_i2c_master_recv()` 实现。
+
+读 MPU6050 某个寄存器,主机首先发送从机地址 MPU6050_ADDR、读写标志 R/W 为 RT_I2C_WR(0 为写,1 为读)、从机寄存器地址 reg 之后才能开始读设备。然后发送从机地址 MPU6050_ADDR、读写标志 R/W 为 RT_I2C_RD(0 为写,1 为读)、保存读取数据指针。
+
+1) 使用 rt_i2c_transfer() 接收数据
+
+本文示例代码底层驱动 `drv_mpu6050.c` 接收数据源码如下:
+
+```c
+#define MPU6050_ADDR 0X68
+
+// 读取寄存器数据
+//reg: 要读取的寄存器地址
+//len: 要读取的数据字节数
+//buf: 读取到的数据存储区
+// 返回值: 0, 正常 / -1, 错误代码
+rt_err_t mpu6050_read_reg(rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf)
+{
+ struct rt_i2c_msg msgs[2];
+
+ msgs[0].addr = MPU6050_ADDR; /* 从机地址 */
+ msgs[0].flags = RT_I2C_WR; /* 写标志 */
+ msgs[0].buf = ® /* 从机寄存器地址 */
+ msgs[0].len = 1; /* 发送数据字节数 */
+
+ msgs[1].addr = MPU6050_ADDR; /* 从机地址 */
+ msgs[1].flags = RT_I2C_RD; /* 读标志 */
+ msgs[1].buf = buf; /* 读取数据指针 */
+ msgs[1].len = len; /* 读取数据字节数 */
+
+ if (rt_i2c_transfer(mpu6050_i2c_bus, msgs, 2) == 2)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+```
+
+以本文示例代码其中一次调用 rt_i2c_transfer() 接收数据为例,从机 MPU6050 地址 16 进制值为 0X68,寄存器地址 reg 16 进制值为 0X75。示例波形如下图所示,第一个发送的数据是 0XD0,第一个数据的高 7 位是从机地址,最低位是读写位是写(值为 0),所以第一个数据值为:0X68 << 1|0 = 0XD0,然后发送寄存器地址 0X75。第二次发送的第一个数据为 0XD1,读写位是读(值为 1),值为:0X68 << 1 | 1 = 0XD1,然后收到读取到的数据 0X68。
+
+
+
+2) 使用 rt_i2c_master_recv() 接收数据
+
+本文示例代码底层驱动 `drv_mpu6050.c` 接收数据源码如下:
+
+```c
+#define MPU6050_ADDR 0X68
+
+// 读取寄存器数据
+//reg: 要读取的寄存器地址
+//len: 要读取的数据字节数
+//buf: 读取到的数据存储区
+// 返回值: 0, 正常 / -1, 错误代码
+rt_err_t mpu6050_read_reg(rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf)
+{
+ if (rt_i2c_master_send(mpu6050_i2c_bus, MPU6050_ADDR, 0, ®, 1) == 1)
+ {
+ if (rt_i2c_master_recv(mpu6050_i2c_bus, MPU6050_ADDR, 0, buf, len) == len)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+
+}
+```
+
+### I2C 设备驱动应用 ###
+
+通常 I2C 接口芯片的只读寄存器分为 2 种情况,一种是单一功能寄存器,另一种是地址连续,功能相近的寄存器。例如 MPU6050 的寄存器 0X3B、0X3C、0X3D、0X3E、0X3F、0X40 依次存放的是三轴加速度 X、Y、Z 轴的高 8 位、低 8 位数据。
+
+本文示例代码底层驱动 `drv_mpu6050.c` 使用 `mpu6050_read_reg()` 函数读取 MPU6050 的 3 轴加速度数据:
+
+```c
+#define MPU_ACCEL_XOUTH_REG 0X3B // 加速度值, X 轴高 8 位寄存器
+
+// 得到加速度值 (原始值)
+//gx,gy,gz: 陀螺仪 x,y,z 轴的原始读数 (带符号)
+// 返回值: 0, 成功 / -1, 错误代码
+rt_err_t mpu6050_accelerometer_get(rt_int16_t *ax, rt_int16_t *ay, rt_int16_t *az)
+{
+ rt_uint8_t buf[6], ret;
+
+ ret = mpu6050_read_reg(MPU_ACCEL_XOUTH_REG, 6, buf);
+ if (ret == 0)
+ {
+ *ax = ((rt_uint16_t)buf[0] << 8) | buf[1];
+ *ay = ((rt_uint16_t)buf[2] << 8) | buf[3];
+ *az = ((rt_uint16_t)buf[4] << 8) | buf[5];
+
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+```
+
+## 参考资料
+
+* 《I2C 设备》
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/flow.docx b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/flow.docx
new file mode 100644
index 0000000000000000000000000000000000000000..3c4f56156a7dd2aec48590d3f26415b2ee4b63db
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/flow.docx differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image10.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image10.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e4d402e2819ef1f7c25703c8b3cfb7d25f406b8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image10.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image11.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image11.png
new file mode 100644
index 0000000000000000000000000000000000000000..1bf473a5a160ac3dfda41934d951a87fbdd0128f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image11.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image2.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image2.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c69a680a578c8a4f0244ea7fb33b703e854066a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image3.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image3.png
new file mode 100644
index 0000000000000000000000000000000000000000..705b44ca92ebbeb86da30c93ef5e4b4111ecfe5c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image4.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image4.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a89a79108a81f0292553e5ce6edd0927325983f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image4.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image5.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image5.png
new file mode 100644
index 0000000000000000000000000000000000000000..1d2a32a91f0942b6f43c01ad8a39fc192fa09e97
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image5.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image6.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image6.png
new file mode 100644
index 0000000000000000000000000000000000000000..63c7b1097820164d4e6a5cf2d51b53fea78200ac
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image6.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image7.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image7.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7f444ff7369c7ef0d19a162b3cc7a49511e712e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image7.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image9.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image9.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a0f0156f3c16ff137dc133355d89c5f99d1b851
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/image9.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/jlink.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/jlink.png
new file mode 100644
index 0000000000000000000000000000000000000000..a54c6a7d79881bc2d2c766d9daaaf14bc1ddd762
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/jlink.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/mcu.png b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/mcu.png
new file mode 100644
index 0000000000000000000000000000000000000000..c1acf1e57631bc58e4d0ed89b6d2e59f8223d71e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/figures/mcu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/i2c/i2c-mpu6050.rar b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/i2c-mpu6050.rar
new file mode 100644
index 0000000000000000000000000000000000000000..a49789d9903c830c7e7122cded4af8b2a7ac9b0c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/i2c/i2c-mpu6050.rar differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/an0037-rtthread-driver-pwm.md b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/an0037-rtthread-driver-pwm.md
new file mode 100644
index 0000000000000000000000000000000000000000..1f4b3502f7fe4f3e252e10b48a1bb82546c0b7dc
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/an0037-rtthread-driver-pwm.md
@@ -0,0 +1,195 @@
+# STM32 上使用 PWM
+
+本文描述了如何在搭载了 RT-Thread 操作系统的平台上使用 PWM 输出波形,包括 PWM 的应用、配置和驱动的添加等。并给出了在正点原子 `STM32L475 pandora` 开发板上验证的代码示例。
+
+## 硬件平台简介
+
+本文基于正点原子 `STM32L475 pandora` 开发板,给出了 PWM 的具体应用示例代码,由于 RT-Thread 上层应用 API 的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。
+
+`STM32L475 pandora` 是正点原子推出的一款基于 ARM Cortex-M4 内核的开发板,最高主频为 80Mhz,该开发板具有丰富的板载资源,可以充分发挥 STM32L475 的芯片性能。
+
+
+
+## 使用 PWM
+
+### 在 menuconfig 中打开 PWM 通道
+
+打开 Env 工具,使用 menuconfig 工具配置工程,在 Env 命令行中输入 menuconfig 进入配置界面。在 menuconfig 配置界面依次选择 `Hardware Driver Config ---> On-chip Peripheral Drivers ---> Enable pwm ---> Enable timer2 output pwm` 如下图所示:
+
+
+
+ 选中需要使用的 PWM 通道后保存退出,使用 `scons --target=mdk5` 生成 mdk5 工程,打开工程进行编译并下载程序,在终端输入 list_device 命令可以看到 PWM2 设备已经成功添加了,如下图所示:
+
+ 
+
+### 使用 PWM 输出波形
+应用程序可以通过 RT-Thread 提供的设备管理接口来访问 PWM 设备硬件,相关接口如下所示:
+
+| **函数** | **描述** |
+| ----------------- | ---------------------------------- |
+| rt_device_find | 根据 PWM 设备名称查找设备获取设备句柄 |
+| rt_pwm_set | 设置 PWM 周期和脉冲宽度 |
+| rt_pwm_enable | 使能 PWM 设备 |
+| rt_pwm_disable | 关闭 PWM 设备 |
+
+接口参数的具体描述请参考官网 [PWM 设备](https://www.rt-thread.org/document/site/programming-manual/device/pwm/pwm/)
+
+#### PWM 设备使用步骤
+
+PWM 设备的具体使用方式可以参考如下步骤:
+
+1. 初始化 PWM 设备。
+
+ * 使用 `rt_device_find` 查找指定的 PWM 设备。
+ * 使用 `rt_pwm_set` 设置通道的默认 PWM 周期和脉冲宽度。
+ * 使用 `rt_pwm_enable` 使能需要输出波形的 PWM 通道。
+
+2. 使用 PWM 设备输出波形。
+
+ * 使用 `rt_pwm_set` 输出特定的波形。
+
+3. 关闭 PWM 输出通道。
+
+ * 当不再需要使用 PWM 通道输出波形时,可以调用 `rt_pwm_disable` 关闭对应的输出通道。
+
+代码如下所示:
+
+```c
+#define PWM_DEV_NAME "pwm2" /* PWM设备名称 */
+#define PWM_DEV_CHANNEL 3 /* PWM通道 */
+#define THREAD_PRIORITY 25 /* 线程优先级 */
+#define THREAD_STACK_SIZE 512 /* 线程栈大小 */
+#define THREAD_TIMESLICE 5 /* 线程时间片大小 */
+
+static rt_thread_t tid1 = RT_NULL; /* 线程句柄 */
+struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
+static rt_uint32_t period = 500000; /* 周期为0.5ms,单位为纳秒ns */
+static rt_uint32_t pulse = 0; /* PWM脉冲宽度值的增减方向 */
+
+/* 线程 pwm_entry 的入口函数 */
+static void pwm_entry(void *parameter)
+{
+ rt_uint32_t count = 0;
+
+ while (count++ < 1000)
+ {
+ rt_thread_mdelay(50);
+ /* step 2、设置 PWM 周期和脉冲宽度,输出特定的波形 */
+ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse++);
+ }
+ /* step 3、如果不再使用该通道,可以关闭 PWM 通道的输出 */
+ rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL);
+}
+
+static int pwm_test(int argc, char *argv[])
+{
+ /* step 1.1、查找 PWM 设备 */
+ pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
+ if (pwm_dev == RT_NULL)
+ {
+ rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
+ return RT_ERROR;
+ }
+
+ /* step 1.2、设置 PWM 周期和脉冲宽度默认值 */
+ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
+ /* step 1.3、使能 PWM 设备的输出通道 */
+ rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
+
+ /* 创建线程,名称是 pwm_thread ,入口是 pwm_entry*/
+ tid1 = rt_thread_create("pwm_thread",
+ pwm_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY,
+ THREAD_TIMESLICE);
+
+ /* 如果获得线程控制块,启动这个线程 */
+ if (tid1 != RT_NULL)
+ rt_thread_startup(tid1);
+
+ return RT_EOK;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(pwm_test, pwm sample);
+```
+
+编译、下载程序,在终端输入 help 命令可以看到 pwm_test 命令已经成功导出,如下图所示:
+
+
+
+#### 运行 PWM 测试程序
+
+要运行 PWM 测试程序,需要在终端输入 pwm_test 由于此 BSP 的 PWM2 通道 3 的输出并没有连接到外设上,无法直观的看到现象,所以这里使用逻辑分析仪来抓取 PWM 输出的波形,波形输出如下图所示:
+
+
+
+从逻辑分析仪抓取的波形可以看到,PWM 波形已经成功输出。
+## 添加 PWM 驱动
+
+如果使用的 BSP 在 menuconfig 中没有给出 PWM 通道的配置项,那么就需要自己添加 PWM 的驱动,下面就如何自己添加 PWM 驱动展开讲解。
+
+### 检查驱动文件是否支持 PWM
+
+进入 `rt-thread\bsp\stm32\libraries\HAL_Drivers` 目录检查 drv_pwm.c 文件是否支持相应的 PWM 外设输出。
+
+检查驱动文件是否支持相应的 PWM 外设(PWM1、2、n)
+
+
+
+检查驱动文件是否支持相应的 PWM 输出通道(1、2、3、4)
+
+
+
+### 初始化 PWM 通道引脚
+
+进入 `rt-thread\bsp\stm32l475-atk-pandora\board\CubeMX_Config` 目录,双击打开 `STM32L475VE.ioc` 文件初始化 PWM 通道对应的引脚,这里以 PWM2 通道 3 为例,如下图所示:
+
+
+
+点击 `GENERATE CODE` 按钮生成代码,虽然 `STM32CubeMX` 生成了多个文件用来初始化外设,但 RT-Thread 只使用了 `STM32CubeMX` 生成的 `stm32fxx_hal_msp.c` 文件和 `stm32fxx_hal_conf.h` 文件,生成的 PWM 代码如下所示:
+
+
+
+### 配置 Kconfig 文件
+
+进入 `rt-thread\bsp\stm32l475-atk-pandora\board` 目录,添加 Kconfig 选项,如下图所示:
+
+
+
+使用 `scons --target=mdk5` 命令生成 mdk5 工程,打开工程并编译,如果工程提示 PWMn_CONFIG 未定义。 可以在 `stm32/libraries/HAL_Drivers/config/f4/pwm_config.h` 中进行定义,如下图所示:
+
+ 
+
+完成以上步骤就可以在 menuconfig 菜单中添加支持的 PWM 输出通道,至于如何使用 PWM 通道输出波形请参考上一章节。
+
+到这一步为止,如何在搭载了 RT-Thread 操作系统的平台上如何使用 PWM 的介绍就结束了。
+
+
+## 参考资料
+* [ENV 用户手册](https://www.rt-thread.org/document/site/programming-manual/env/env/)
+* [PWM 设备](https://www.rt-thread.org/document/site/programming-manual/device/pwm/pwm/)
+* [STM32L475-atk-pandora BSP 源码](https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32l475-atk-pandora)
+* [STM32 系列驱动添加指南](https://github.com/RT-Thread/rt-thread/blob/master/bsp/stm32/docs/STM32%E7%B3%BB%E5%88%97%E5%A4%96%E8%AE%BE%E9%A9%B1%E5%8A%A8%E6%B7%BB%E5%8A%A0%E6%8C%87%E5%8D%97.md)
+
+## 常见问题
+
+### Q: 按照上述使用 PWM 章节进行操作,无法输出 PWM 波形怎么办?
+
+**A:** 该问题一般是 menuconfig 菜单中给出了 PWM 的配置选项,但是相应的 PWM 输出通道和时钟没有初始化引起的。使用 `STM32CubeMX` 工具使能相应的输出通道和时钟即可。
+
+### Q: 编译提示 PWMn_CONFIG 未定义怎么办?
+
+**A:** 在 `stm32/libraries/HAL_Drivers/config/f1/pwm_config.h` 文件中参考已定义的配置文件来定义自己需要的 PWM 输出通道。
+
+### Q: 当使用 I/O 作为 PWM 输出通道时是否还需要使用 `rt_pin_mode` 设置 I/O 引脚的工作模式?
+
+**A:** 不需要。在使用 `STM32CubeMX` 配置 PWM 输出通道时已经生成了对应通道引脚的初始化文件,所以不再需要对引脚做初始化。
+
+### Q: 当前 PWM 驱动是否支持同一通道的反向输出?
+
+**A:** PWM 驱动暂未支持同一通道的反向输出。
+
+### Q:使用 `STM32CubeMX` 工具将 PWM 通道重映射后,需要修改驱动文件吗?
+
+**A:** 不需要。驱动文件初始化的是 PWM 外设和引脚无关。不管通道引脚如何映射,对应的 PWM 通道是不会改变的。
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/board.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/board.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d8f0a6e269c6a3c4ff9dfb7f5bf2cbaa1e416ba
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/board.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/config_para.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/config_para.png
new file mode 100644
index 0000000000000000000000000000000000000000..32f89db50225b5b247da5b89e824cd40c48df2e4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/config_para.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/hal_code.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/hal_code.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0cd8f5c98105d3ed5d2a0dc63bdce3914c8ac34
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/hal_code.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/help.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/help.png
new file mode 100644
index 0000000000000000000000000000000000000000..70dec3b24b8c66b4c298ebd540cbddc1d65d43f0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/help.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/init_pin.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/init_pin.png
new file mode 100644
index 0000000000000000000000000000000000000000..108b7409fe75f84623648f073f4cfb1adb66e417
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/init_pin.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/list_device.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/list_device.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e8230791be5602ae0d521fa32e6003679896049
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/list_device.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/open_channel.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/open_channel.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6694dd7740b830ced457b7843425f6c68eecda8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/open_channel.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_channel.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_channel.png
new file mode 100644
index 0000000000000000000000000000000000000000..e073a4adf80fd47fbf303c74208f45978adb463e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_channel.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_kconfig.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_kconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ce62238dade3de40642799070f69759f12b2828
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_kconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_num.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_num.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc93adee5705f8001177bcbcf899edaff1f98be0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_num.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_test.png b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_test.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1006c2697d0b81a283654da50ec28d67918f412
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/pwm/figures/pwm_test.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/an0004-rtthread-driver-spi.md b/rt-thread-version/rt-thread-standard/application-note/driver/spi/an0004-rtthread-driver-spi.md
new file mode 100644
index 0000000000000000000000000000000000000000..c8bfcb6d2c5ca19552385ef6395bb6636271fec2
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/driver/spi/an0004-rtthread-driver-spi.md
@@ -0,0 +1,239 @@
+# SPI设备应用笔记 #
+
+本文以驱动SPI接口的OLED显示屏为例,说明了如何添加SPI设备驱动框架及底层硬件驱动,使用SPI设备驱动接口开发应用程序。并给出了在正点原子STM32F4探索者开发板上验证的代码示例。
+
+## 本文的目的和结构 ##
+
+### 本文的目的和背景 ###
+
+为了方便应用层程序开发,RT-Thread中引入了SPI设备驱动框架。本文说明了如何使用RT-Thread SPI设备驱动。
+
+### 本文的结构 ###
+
+本文首先介绍了在正点原子STM32F4探索者开发板上运行了SPI设备驱动示例代码。
+
+## 运行示例代码 ##
+
+本章节基于正点原子探索者STM32F4 开发板及SPI示例代码,给出了RT-Thread SPI设备驱动框架的使用方法。
+
+### 示例代码软硬件资源 ###
+
+1. [RT-Thread 源码](https://github.com/RT-Thread/rt-thread)
+2. [ENV工具](https://www.rt-thread.org/document/site/docs/tools/env/env-user-manual/)
+3. [SPI设备驱动示例代码](spi-oled.rar)
+4.
+5. [正点原子STM32F4探索者开发板](http://www.openedv.com/thread-13912-1-1.html)
+6. 1.5寸彩色OLED显示屏(SSD1351控制器)
+7. MDK5
+
+正点原子探索者STM32F4 开发板的MCU是STM32F407ZGT6,本示例使用USB转串口(USART1)发送数据及供电,使用SEGGER J-LINK连接JTAG调试,STM32F4 有多个硬件SPI控制器,本例使用 SPI1。彩色OLED显示屏板载SSD1351控制器,分辨率128*128。
+
+STM32F4 与 OLED 显示屏管脚连接如下表所示:
+
+STM32管脚 | OLED显示屏管脚 | 说明
+- | - | -
+PA5 | D0 | SPI1 SCK,时钟
+PA6 | | SPI1 MISO,未使用
+PA7 | D1 | SPI1 MOSI,主机输出,从机输入
+PC6 | D/C | GPIO,输出,命令0/数据1选择
+PC7 | RES | GPIO,输出,复位,低电平有效
+PC8 | CS | GPIO,输出,片选,低电平有效
+3.3V | VCC | 供电
+GND | GND | 接地
+
+
+
+
+
+SPI设备驱动示例代码包括`app.c、drv_ssd1351.c、drv_ssd1351.h` 3个文件,`drv_ssd1351.c`是OLED显示屏驱动文件,此驱动文件包含了SPI设备ssd1351的初始化、挂载到系统及通过命令控制OLED显示的操作方法。由于RT-Thread上层应用API的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。
+
+### 配置工程 ###
+
+使用menuconfig配置工程:在env工具命令行使用cd 命令进入 rt-thread/bsp/stm32f4xx-HAL 目录,然后输入`menuconfig` 命令进入配置界面。
+
+
+
+* 修改工程芯片型号:修改 Device type为STM32F407ZG。
+* 配置shell使用串口1:选中Using UART1,进入RT-Thread Kernel ---> Kernel Device Object菜单,修改the device name for console为uart1。
+* 开启SPI总线及设备驱动并注册SPI总线到系统:进入RT-Thread Components ---> Device Drivers菜单,选中Using SPI Bus/Device device drivers,RT-Thread Configuration界面会默认选中Using SPI1,spi1总线设备会注册到操作系统。
+* 开启GPIO驱动:进入RT-Thread Components ---> Device Drivers菜单,选中Using generic GPIO device drivers。OLED屏需要2个额外的GPIO用于DC、RES信号,SPI总线驱动也需要对片选管脚进行操作,都需要调用系统的GPIO驱动接口。
+
+生成新工程及修改调试选项:退出menuconfig配置界面并保存配置,在ENV命令行输入`scons --target=mdk5 -s` 命令生成mdk5工程,新工程名为project。使用MDK5打开工程,修改调试选项为J-LINK。
+
+
+
+使用list_device命令查看SPI总线:添加SPI底层硬件驱动无误后,在终端PuTTY(打开对应端口,波特率配置为115200)使用`list_device`命令就能看到SPI总线。同样可以看到我们使用的UART设备和PIN设备。
+
+
+
+### 添加示例代码 ###
+
+将SPI设备驱动示例代码里的`app.c`拷贝到`/rt-thread/bsp/stm32f4xx-HAL/applications`目录。`drv_ssd1351.c、drv_ssd1351.h`拷贝到`/rt-thread/bsp/stm32f4xx-HAL/drivers`目录,并将它们添加到工程中对应分组。如图所示:
+
+
+
+在`main.c`中调用`app_init()`,`app_init()`会创建一个oled线程,线程会循环展示彩虹颜色图案和正方形颜图案。
+
+
+
+`main.c`调用测试代码源码如下:
+
+```c
+#include
+#include
+
+extern int app_init(void);
+
+int main(void)
+{
+ /* user app entry */
+
+ app_init();
+
+ return 0;
+}
+```
+
+
+
+## SPI设备驱动接口使用详解 ##
+
+按照前文的步骤,相信读者能很快的将RT-Thread SPI设备驱动运行起来,那么如何使用SPI设备驱动接口开发应用程序呢?
+
+RT-Thread SPI设备驱动使用流程大致如下:
+
+1. 定义SPI设备对象,调用`rt_spi_bus_attach_device()`挂载SPI设备到SPI总线。
+1. 调用`rt_spi_configure()`配置SPI总线模式。
+1. 使用`rt_spi_send()`等相关数据传输接口传输数据。
+
+接下来本章节将详细讲解示例代码使用到的主要的SPI设备驱动接口。
+
+### 挂载SPI设备到总线 ###
+
+用户定义了SPI设备对象后就可以调用此函数挂载SPI设备到SPI总线。可使用函数`rt_spi_bus_attach_device()`。
+
+本文示例代码底层驱动`drv_ssd1351.c` 中 `rt_hw_ssd1351_config()`挂载ssd1351设备到SPI总线源码如下:
+
+```c
+#define SPI_BUS_NAME "spi1" /* SPI总线名称 */
+#define SPI_SSD1351_DEVICE_NAME "spi10" /* SPI设备名称 */
+
+... ...
+
+static struct rt_spi_device spi_dev_ssd1351; /* SPI设备ssd1351对象 */
+static struct stm32_hw_spi_cs spi_cs; /* SPI设备CS片选引脚 */
+
+... ...
+
+static int rt_hw_ssd1351_config(void)
+{
+ rt_err_t res;
+
+ /* oled use PC8 as CS */
+ spi_cs.pin = CS_PIN;
+ rt_pin_mode(spi_cs.pin, PIN_MODE_OUTPUT); /* 设置片选管脚模式为输出 */
+
+ res = rt_spi_bus_attach_device(&spi_dev_ssd1351, SPI_SSD1351_DEVICE_NAME, SPI_BUS_NAME, (void*)&spi_cs);
+ if (res != RT_EOK)
+ {
+ OLED_TRACE("rt_spi_bus_attach_device!\r\n");
+ return res;
+ }
+
+ ... ...
+}
+```
+
+### 配置SPI模式 ###
+
+挂载SPI设备到SPI总线后,为满足不同设备的时钟、数据宽度等要求,通常需要配置SPI模式、频率参数。SPI从设备的模式决定主设备的模式,所以SPI主设备的模式必须和从设备一样两者才能正常通讯。可使用函数`rt_spi_configure()`配置模式。
+
+本文示例代码底层驱动`drv_ssd1351.c` 中`rt_hw_ssd1351_config()`配置SPI传输参数源码如下:
+
+```c
+static int rt_hw_ssd1351_config(void)
+{
+ ... ...
+
+ /* config spi */
+ {
+ struct rt_spi_configuration cfg;
+ cfg.data_width = 8;
+ cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
+ cfg.max_hz = 20 * 1000 *1000; /* 20M,SPI max 42MHz,ssd1351 4-wire spi */
+
+ rt_spi_configure(&spi_dev_ssd1351, &cfg);
+ }
+
+ ... ...
+
+```
+
+### 数据传输 ###
+
+本文示例代码底层驱动`drv_ssd1351.c`调用`rt_spi_send()`向SSD1351发送指令和数据的函数源码如下:
+```
+rt_err_t ssd1351_write_cmd(const rt_uint8_t cmd)
+{
+ rt_size_t len;
+
+ rt_pin_write(DC_PIN, PIN_LOW); /* 命令低电平 */
+
+ len = rt_spi_send(&spi_dev_ssd1351, &cmd, 1);
+
+ if (len != 1)
+ {
+ OLED_TRACE("ssd1351_write_cmd error. %d\r\n",len);
+ return -RT_ERROR;
+ }
+ else
+ {
+ return RT_EOK;
+ }
+
+}
+
+rt_err_t ssd1351_write_data(const rt_uint8_t data)
+{
+ rt_size_t len;
+
+ rt_pin_write(DC_PIN, PIN_HIGH); /* 数据高电平 */
+
+ len = rt_spi_send(&spi_dev_ssd1351, &data, 1);
+
+ if (len != 1)
+ {
+ OLED_TRACE("ssd1351_write_data error. %d\r\n",len);
+ return -RT_ERROR;
+ }
+ else
+ {
+ return RT_EOK;
+ }
+}
+
+```
+
+### SPI设备驱动应用 ###
+
+本文示例使用SSD1351显示图像信息,首先需要确定信息在显示器上的行列起始地址,调用`ssd1351_write_cmd()`向SSD1351发送指令,调用`ssd1351_write_data()`向SSD1351发送数据,源代码如下:
+
+```c
+void set_column_address(rt_uint8_t start_address, rt_uint8_t end_address)
+{
+ ssd1351_write_cmd(0x15); // Set Column Address
+ ssd1351_write_data(start_address); // Default => 0x00 (Start Address)
+ ssd1351_write_data(end_address); // Default => 0x7F (End Address)
+}
+void set_row_address(rt_uint8_t start_address, rt_uint8_t end_address)
+{
+ ssd1351_write_cmd(0x75); // Set Row Address
+ ssd1351_write_data(start_address); // Default => 0x00 (Start Address)
+ ssd1351_write_data(end_address); // Default => 0x7F (End Address)
+}
+```
+
+## 参考资料
+
+* 《SPI 设备》
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/add-sample.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/add-sample.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e438f0a18125879d17a3f8e6bd9c5cbcb96ecf3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/add-sample.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/board.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/board.png
new file mode 100644
index 0000000000000000000000000000000000000000..57099a65f8d0b25b924eecfcb1be1bf7473b8881
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/board.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/enable-spi.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/enable-spi.png
new file mode 100644
index 0000000000000000000000000000000000000000..17a3def43c4a92d11c5e4ed306f08b2235fc73ca
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/enable-spi.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/flow.docx b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/flow.docx
new file mode 100644
index 0000000000000000000000000000000000000000..d32a242e306888fdd5a6a7b9d257f24c83147818
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/flow.docx differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/jlink.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/jlink.png
new file mode 100644
index 0000000000000000000000000000000000000000..a54c6a7d79881bc2d2c766d9daaaf14bc1ddd762
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/jlink.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/pheno.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/pheno.png
new file mode 100644
index 0000000000000000000000000000000000000000..20ad08bc3c41669f320d7ca16b3f704cf2a65c68
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/pheno.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/spi-bus.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/spi-bus.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b9764cd1b7913abd674edc2e702316a96b05cfd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/spi-bus.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/spi-drv.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/spi-drv.png
new file mode 100644
index 0000000000000000000000000000000000000000..96a5379d6f20205fb235872106e5a1b59293ff06
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/spi-drv.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/ssd1351.png b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/ssd1351.png
new file mode 100644
index 0000000000000000000000000000000000000000..7adc5f45e6e680de4b96e3618e8e064b7b7934f5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/figures/ssd1351.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/spi/spi-oled.rar b/rt-thread-version/rt-thread-standard/application-note/driver/spi/spi-oled.rar
new file mode 100644
index 0000000000000000000000000000000000000000..e79a400d4078a090faa4be4d26e91365b1a71ea9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/spi/spi-oled.rar differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/an0001-rtthread-driver-uart.md b/rt-thread-version/rt-thread-standard/application-note/driver/uart/an0001-rtthread-driver-uart.md
new file mode 100644
index 0000000000000000000000000000000000000000..7829e3df4cce80d8d829a73213dfb7d140d5f084
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/driver/uart/an0001-rtthread-driver-uart.md
@@ -0,0 +1,255 @@
+# 串口设备应用笔记 #
+
+本文描述了如何使用 RT-Thread 的串口设备,包括串口配置、设备操作接口的应用。并给出了在正点原子 STM32F4 探索者开发板上验证的代码示例。
+
+## 本文的目的和结构 ##
+
+### 本文的目的和背景 ###
+
+串口(通用异步收发器,常写作 UART、uart)是最为广泛使用的通信接口之一。在裸机平台或者是没有设备管理框架的 RTOS 平台上,我们通常只需要根据官方手册编写串口硬件初始化代码即可。引入了带设备管理框架的实时操作系统 RT-Thread 后,串口的使用则与裸机或者其它 RTOS 有很大的不同之处。RT-Thread 中自带 I/O 设备管理层,将各种各样的硬件设备封装成具有统一接口的逻辑设备,方便管理及使用。本文说明了如何在 RT-Thread 中使用串口。
+
+### 本文的结构 ###
+
+本文首先给出使用 RT-Thread 的设备操作接口开发串口收、发数据程序的示例代码,并在正点原子 STM32F4 探索者开发板上验证。接着分析了示例代码的实现,最后深入地描述了 RT-Thread 设备管理框架与串口的联系。
+
+## 运行示例代码 ##
+
+本文基于正点原子 STM32F4 探索者开发板,给出了串口的配置流程和应用代码示例。由于 RT-Thread 设备操作接口的通用性,因此这些代码与硬件平台无关,读者可以直接将它用在自己使用的硬件平台上。
+正点原子 STM32F4 探索者开发板使用的是 STM32F407ZGT6,具有多路串口。我们使用串口 1 作为 shell 终端,串口 2 作为实验用串口,测试数据收发。终端软件使用 putty。板载串口 1 带有 USB 转串口芯片,因此使用 USB 线连接串口 1 和 PC 即可;串口 2 则需要使用 USB 转串口模块连接到 PC。
+
+
+
+### 准备和配置工程 ###
+
+1. 下载 [RT-Thread 源码](https://github.com/RT-Thread/rt-thread)
+
+2. 进入 `rt-thread\bsp\stm32f4xx-HAL` 目录,在 Env 命令行中输入 menuconfig,进入配置界面,使用 menuconfig 工具(学习如何使用)配置工程。
+
+(1) 配置 shell 使用串口 1:RT-Thread Kernel ---> Kernel Device Object ---> 修改 the device name for console 为 uart1。
+
+(2) 勾选 Using UART1、Using UART2,选择芯片型号为 STM32F407ZG,时钟源为外部 8MHz,如图所示:
+
+
+
+3. 输入命令 scons --target=mdk5 -s 生成 keil 工程,打开工程后先修改 MCU 型号为 STM32F407ZETx,如图所示:
+
+
+
+4. 打开 putty,选择正确的串口,软件参数配置为 115200-8-1-N、无流控。如图所示:
+
+
+
+5. 编译、下载程序,按下复位后就可以在串口 1 连接的终端上看到 RT-Thread 标志 log 了,输入 list_device 命令能查看到 uart1、uart2 Character Device 就表示串口配置好了。
+
+
+
+### 加入串口相关代码 ###
+
+[下载串口示例代码](uart.rar)
+
+
+
+本文示例代码 app_uart.c、app_uart.h,app_uart.c 中是串口相关操作的代码,方便阅读。app_uart.c 中提供了 4 个函数 uart_open、uart_putchar、uart_putstring、uart_getchar 以方便使用串口。app_uart.c 中的代码与硬件平台无关,读者可以把它直接添加到自己的工程。利用这几个函数在 main.c 中编写测试代码。
+main.c 源码如下:
+
+```c
+#include "app_uart.h"
+#include "board.h"
+void test_thread_entry(void* parameter)
+{
+ rt_uint8_t uart_rx_data;
+ /* 打开串口 */
+ if (uart_open("uart2") != RT_EOK)
+ {
+ rt_kprintf("uart open error.\n");
+ while (1)
+ {
+ rt_thread_delay(10);
+ }
+ }
+ /* 单个字符写 */
+ uart_putchar('2');
+ uart_putchar('0');
+ uart_putchar('1');
+ uart_putchar('8');
+ uart_putchar('\n');
+ /* 写字符串 */
+ uart_putstring("Hello RT-Thread!\r\n");
+ while (1)
+ {
+ /* 读数据 */
+ uart_rx_data = uart_getchar();
+ /* 错位 */
+ uart_rx_data = uart_rx_data + 1;
+ /* 输出 */
+ uart_putchar(uart_rx_data);
+ }
+}
+int main(void)
+{
+ rt_thread_t tid;
+ /* 创建 test 线程 */
+ tid = rt_thread_create("test",
+ test_thread_entry,
+ RT_NULL,
+ 1024,
+ 2,
+ 10);
+ /* 创建成功则启动线程 */
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+ return 0;
+}
+```
+
+这段程序实现了如下功能:
+
+1. main 函数里面创建并启动了测试线程 test_thread_entry。
+
+2. 测试线程调用 uart_open 函数打开指定的串口后,首先使用 uart_putchar 函数发送字符和 uart_putstring 函数发送字符串。
+
+3. 接着在 while 循环里面调用 uart_getchar 函数读取接收到的数据并保存到局部变量 uart_rx_data 中,最后将数据错位后输出。
+
+### 运行结果 ###
+
+编译、将代码下载到板卡,复位,串口 2 连接的终端软件 putty(软件参数配置为 115200-8-1-N、无流控)输出了字符 2、0、1、8 和字符串 Hello RT-Thread!。输入字符 ‘A’,串口 2 接收到将其错位后输出。实验现象如图所示:
+
+
+
+> 提示:图中 putty 连接开发板的串口 2 作为测试串口。
+
+## 进阶阅读 ##
+
+串口通常被配置为接收中断和轮询发送模式。在中断模式下,CPU 不需要一直查询等待串口相关标志寄存器,串口接收到数据后触发中断,我们在中断服务程序进行数据处理,效率较高。RT-Thread 官方 BSP 默认便是这种模式。
+
+### 使用哪个串口 ###
+
+uart_open 函数用于打开指定的串口,它完成了串口设备回调函数设置、串口设备的开启和事件的初始化。源码如下:
+
+```c
+rt_err_t uart_open(const char *name)
+{
+ rt_err_t res;
+ /* 查找系统中的串口设备 */
+ uart_device = rt_device_find(name);
+ /* 查找到设备后将其打开 */
+ if (uart_device != RT_NULL)
+ {
+ res = rt_device_set_rx_indicate(uart_device, uart_intput);
+ /* 检查返回值 */
+ if (res != RT_EOK)
+ {
+ rt_kprintf("set %s rx indicate error.%d\n",name,res);
+ return -RT_ERROR;
+ }
+ /* 打开设备,以可读写、中断方式 */
+ res = rt_device_open(uart_device, RT_DEVICE_OFLAG_RDWR |
+ RT_DEVICE_FLAG_INT_RX );
+ /* 检查返回值 */
+ if (res != RT_EOK)
+ {
+ rt_kprintf("open %s device error.%d\n",name,res);
+ return -RT_ERROR;
+ }
+ }
+ else
+ {
+ rt_kprintf("can't find %s device.\n",name);
+ return -RT_ERROR;
+ }
+ /* 初始化事件对象 */
+ rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);
+ return RT_EOK;
+}
+```
+
+简要流程如下:
+
+
+
+uart_open 函数使用到的设备操作接口有:rt_device_find、rt_device_set_rx_indicate、rt_device_open。
+uart_open 函数首先调用 rt_device_find 根据串口名字获得串口句柄,保存在静态全局变量 uart_device 中,后面关于串口的操作都是基于这个串口句柄。这里的名字是在 drv_usart.c 中调用注册函数 rt_hw_serial_register 决定的,该函数将串口硬件驱动和 RT-Thread 设备管理框架联系起来了。
+
+```c
+ /* register UART2 device */
+ rt_hw_serial_register(&serial2,
+ "uart2",
+ RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
+ uart);
+```
+
+接着调用 rt_device_set_rx_indicate 设置串口接收中断的回调函数。
+最后调用 rt_device_open 以可读写、中断接收方式打开串口。它的第二个参数为标志,与上面提到的注册函数 rt_hw_serial_register 保持一致即可。
+
+```c
+rt_device_open(uart_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
+```
+
+最后调用 rt_event_init 初始化事件。
+RT-Thread 中默认开启了自动初始化机制,因此用户不需要在应用程序中手动调用串口的初始化函数(drv_usart.c 中的 INIT_BOARD_EXPORT 实现了自动初始化)。用户实现的由宏 RT_USING_UARTx 选定的串口硬件驱动将自动关联到 RT-Thread 中来(drv_usart.c 中的 rt_hw_serial_register 实现了串口硬件注册)。
+
+### 串口发送 ###
+
+uart_putchar 函数用于发送 1 字节数据。uart_putchar 函数实际上调用的是 rt_device_write 来发送一个字节,并采取了防出错处理,即检查返回值,失败则重新发送,并限定了超时。源码如下:
+
+```c
+void uart_putchar(const rt_uint8_t c)
+{
+ rt_size_t len = 0;
+ rt_uint32_t timeout = 0;
+ do
+ {
+ len = rt_device_write(uart_device, 0, &c, 1);
+ timeout++;
+ }
+ while (len != 1 && timeout < 500);
+}
+```
+
+### 串口接收 ###
+
+uart_getchar 函数用于接收数据,uart_getchar 函数的实现采用了串口接收中断回调机制和事件用于异步通信,它具有阻塞特性。
+相关源码如下:
+
+```c
+/* 串口接收事件标志 */
+#define UART_RX_EVENT (1 << 0)
+/* 事件控制块 */
+static struct rt_event event;
+/* 设备句柄 */
+static rt_device_t uart_device = RT_NULL;
+
+/* 回调函数 */
+static rt_err_t uart_intput(rt_device_t dev, rt_size_t size)
+{
+ /* 发送事件 */
+ rt_event_send(&event, UART_RX_EVENT);
+ return RT_EOK;
+}
+rt_uint8_t uart_getchar(void)
+{
+ rt_uint32_t e;
+ rt_uint8_t ch;
+ /* 读取 1 字节数据 */
+ while (rt_device_read(uart_device, 0, &ch, 1) != 1)
+{
+ /* 接收事件 */
+ rt_event_recv(&event, UART_RX_EVENT,RT_EVENT_FLAG_AND |
+ RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER, &e);
+}
+ return ch;
+}
+```
+
+uart_getchar 函数内部有一个 while() 循环,先调用 rt_device_read 去读取一字节数据,没有读到则调用 rt_event_recv 等待事件标志,挂起调用线程;串口接收到一字节数据后产生中断,调用回调函数 uart_intput,回调函数里面调用了 rt_event_send 发送事件标志以唤醒等待该 event 事件的线程。
+调用 uart_getchar 函数发生的数据流向示意图如下:
+
+
+
+应用程序调用 uart_getchar 时,实际调用关系为:rt_device_read ==> rt_serial_read ==> drv_getc,最终从串口数据寄存器读取到数据。
+
+## 参考资料
+
+* 《串口设备》
+
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_10.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_10.png
new file mode 100644
index 0000000000000000000000000000000000000000..5912b22206a904ed38a44108d0af88b56350f940
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_10.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_11.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_11.png
new file mode 100644
index 0000000000000000000000000000000000000000..3895ffe9fccd1b33e62448a1009e9a9788b475f9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_11.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_12.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_12.png
new file mode 100644
index 0000000000000000000000000000000000000000..d19ac7c50264786e8255d7a071efb361f5868dd2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_12.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_2.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd3fda20741494f6b6f1b5dd2b78fe8f53666778
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_3.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_3.png
new file mode 100644
index 0000000000000000000000000000000000000000..2038f1b0a84b5847234cb6dc1e762cd1a329c4ec
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_4.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_4.png
new file mode 100644
index 0000000000000000000000000000000000000000..6d09d8aaf90e925ba6fab0d5bced3d32ffb2daf2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_4.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_5.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_5.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3df46c745a697ef9a3718c018ff996123923b63
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_5.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_6.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_6.png
new file mode 100644
index 0000000000000000000000000000000000000000..fe6c7e6a93d13b8dd02fa034dec81f83f9d6493d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_6.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_7.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_7.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ca46b50dd15f2308faed24735f04bfb1f372490
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_7.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_9.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_9.png
new file mode 100644
index 0000000000000000000000000000000000000000..7f5c5d17ea740663dacabc56e55c8c9da07a15b9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/an0001_9.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/mcu.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/mcu.png
new file mode 100644
index 0000000000000000000000000000000000000000..c1acf1e57631bc58e4d0ed89b6d2e59f8223d71e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/mcu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..41bb5460d434cec415d2fad14b0307fad15bc228
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/result.png b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/result.png
new file mode 100644
index 0000000000000000000000000000000000000000..ffeecbba39b93b8e5d29e77982eb97319f56455d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/figures/result.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/uart/uart.rar b/rt-thread-version/rt-thread-standard/application-note/driver/uart/uart.rar
new file mode 100644
index 0000000000000000000000000000000000000000..00ed6452557fd62aabd63e0e665a544e01bcc67f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/uart/uart.rar differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/an0046-rtthread-driver-usbh.md b/rt-thread-version/rt-thread-standard/application-note/driver/usb/an0046-rtthread-driver-usbh.md
new file mode 100644
index 0000000000000000000000000000000000000000..1b006c38a2f29ac345ae1f31d366bb457b8f409d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/driver/usb/an0046-rtthread-driver-usbh.md
@@ -0,0 +1,177 @@
+# STM32 上使用 USB Host 读写 U 盘
+
+本文描述了如何在搭载了 RT-Thread 操作系统的平台上使用 USB Host 读写 U 盘,包括 USB Host 的应用、配置等。并给出了在正点原子 `STM32F767 apollo` 开发板上验证的演示。
+
+## 简介
+
+USB(Universal Serial Bus)是一种支持热插拔的通用串行总线。它使用差分信号来传输数据,在 USB 1.0和 USB 1.1 版本中,只支持 1.5Mb/s 的低速(low-speed)模式和 12Mb/s 的全速(full-speed)模式,在 USB 2.0 中,又加入了480Mb/s 的高速模式,USB 3.0(super speed),传输速率最大5Gbps。
+
+在 USB 体系中又包括 USB Host(主机)和USB Device(设备)
+
+- USB Host
+
+ - 任何USB系统中只有一个主机。 主机系统的USB接口被称为主机控制器。 主机控制器可以以硬件,固件或软件的组合来实现。 根集线器集成在主机系统内以提供一个或多个连接点。
+
+- USB Device
+ USB Device 可以分为 USB Hub 和 USB Function。
+
+ USB Hub 提供了一种低成本、低复杂度的 USB接口扩展方法。Hub 的上行端口面向 HOST,下行端口面向设备(Hub 或功能设备)。在下行端口上,Hub 提供了设备连接检测和设备移除检测的能力,并给各下行端口供电。Hub 可以单独使能各下行端口。不同端口可以工作在不同的速度等级(高速/全速/低速)。
+
+ USB Function 能够通过总线传输或接收数据或控制信息的设备,在 USB2.0 标准中,别称为 Class
+
+本文主要是基于正点原子 `stm32f767-atk-apollo` 开发板,给出了 USB Host 读写 U 盘的配置和使用示例。
+
+本文准备资料如下:
+
+- [RT-Thread 源码](https://github.com/RT-Thread/rt-thread)
+- [Env 工具](https://www.rt-thread.org/page/download.html)
+- U 盘
+
+依赖
+
+- RT-Thread 4.0.2+
+- RT-Thread 设备驱动框架
+
+## 硬件连接准备
+
+本文是基于 U 盘的读写,所以需要准备好一个 U 盘,并插入开发板上的 U 盘接口。
+
+## ENV 配置
+
+### 打开 USB Host
+RT-Thread 可以很方便的通过 ENV 来配置和生成工程。在 `rt-thread\bsp\stm32\stm32f767-atk-apollo` 目录下打开 ENV 工具,使用 menuconfig 进入如下配置界面并选中。
+
+ ---- Hardware Drivers Config
+ ----On-Chip Peripheral Drivers
+ ----Enable USH Host
+ ----Enable Udisk Drivers
+
+配置界面如下图所示
+
+
+
+### 打开文件系统
+
+本文使用的是 USB Host 读写 U 盘的功能,所以需要打开 RT-Thread 的虚拟文件系统功能,打开文件系统的操作如下
+
+ ---- RT-Thread Components
+ ----Device virtual file system
+ ----Using device virtual file system
+
+配置界面如下图所示
+
+
+
+### 生成工程并下载
+
+在 ENV 中打开 USB Host 和虚拟文件系统的功能之后,工程配置就结束了,退出配置界面并保存。在 ENV 工具中使用 `scons --target=mdk5` 命令重新生成工程并打开。工程打开之后可以看到 USB Host 的框架代码和驱动代码都已经自动加入到工程里面了,如下图所示
+
+
+
+在 `main.c` 文件中加入以下测试代码并下载
+```c
+#include
+#define TEST_FN "/test_usbh.c"
+static char test_data[120], buffer[120];
+
+void readwrite(const char* filename)
+{
+ int fd;
+ int index, length;
+
+ fd = open(TEST_FN, O_WRONLY | O_CREAT | O_TRUNC, 0);
+ if (fd < 0)
+ {
+ rt_kprintf("open file for write failed\n");
+ return;
+ }
+
+ for (index = 0; index < sizeof(test_data); index ++)
+ {
+ test_data[index] = index + 27;
+ }
+
+ length = write(fd, test_data, sizeof(test_data));
+ if (length != sizeof(test_data))
+ {
+ rt_kprintf("write data failed\n");
+ close(fd);
+ return;
+ }
+
+ close(fd);
+
+ fd = open(TEST_FN, O_RDONLY, 0);
+ if (fd < 0)
+ {
+ rt_kprintf("check: open file for read failed\n");
+ return;
+ }
+
+ length = read(fd, buffer, sizeof(buffer));
+ if (length != sizeof(buffer))
+ {
+ rt_kprintf("check: read file failed\n");
+ close(fd);
+ return;
+ }
+
+ for (index = 0; index < sizeof(test_data); index ++)
+ {
+ if (test_data[index] != buffer[index])
+ {
+ rt_kprintf("check: check data failed at %d\n", index);
+ close(fd);
+ return;
+ }
+ }
+
+ rt_kprintf("usb host read/write udisk successful\r\n");
+
+ close(fd);
+}
+
+MSH_CMD_EXPORT(readwrite, usb host read write test);
+```
+
+### 运行测试程序
+
+将以上程序下载入开发板之后连接串口调试工具可以看到如下调试信息
+
+
+
+这里可以看到 U 盘的大小约为 7.4G,说明 U 盘已经成功挂载到开发板上面的文件系统了。使用文件系统的 ls 命令查看 U 盘目录的结果如下图:
+
+
+
+在串口运行导出的 readwrite 测试函数的结果如下图所示
+
+
+
+再次使用 ls 命令可以看到在 U 盘的根目录下已经新建了一个 test_usbh.c 文件,并且大小为 120 个字节,如下图所示
+
+
+
+我们可以将 U 盘拔出并插在电脑上验证一下我们刚才写入的文件。
+
+
+
+从电脑上我们可以看到已经成功创建了一个 `test_usbh.c` 的文件。到这一步为止,在 STM32 上使用 USB Host 读写 U 盘的介绍就结束了
+
+## 参考资料
+
+[ENV 用户手册](https://www.rt-thread.org/document/site/programming-manual/env/env/)
+
+[虚拟文件系统](https://www.rt-thread.org/document/site/programming-manual/filesystem/filesystem/)
+
+[RT-Thread源码](https://github.com/RT-Thread/rt-thread)
+
+## 常见问题
+
+### Q: 在 `rt-thread\bsp\stm32\libraries` 目录下没有 drv_usbh.c 文件?
+
+**A:** 该问题一般是当前版本还没有支持 USB Host 的驱动,请使用 RT-Thread 4.0.2+ 的版本进行测试。
+
+### Q:工程目录下没有 drv_usbh.c 文件?
+
+**A:** 该问题一般是当前 BSP 没有添加 drv_usbh.c 的驱动,请参考[STM32 外设添加指南](https://github.com/RT-Thread/rt-thread/blob/master/bsp/stm32/docs/STM32%E7%B3%BB%E5%88%97%E5%A4%96%E8%AE%BE%E9%A9%B1%E5%8A%A8%E6%B7%BB%E5%8A%A0%E6%8C%87%E5%8D%97.md)。
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/filesystem.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/filesystem.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ddea7d71e13c358f264672ffcc75eaf5d5b62d6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/filesystem.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/ls.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/ls.png
new file mode 100644
index 0000000000000000000000000000000000000000..72f941c58bd51bb1a9b71e357a3015f3a86bb8e6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/ls.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/ls2.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/ls2.png
new file mode 100644
index 0000000000000000000000000000000000000000..d85f3136c3c61eafdb3466568b57a6d604d8f017
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/ls2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..de12a65f2a4af41192d67bc667bd1e8eaf88692a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/pc.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/pc.png
new file mode 100644
index 0000000000000000000000000000000000000000..3033c87de7ea579aff0f87212d3841884855f76a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/pc.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/project.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/project.png
new file mode 100644
index 0000000000000000000000000000000000000000..01504d18040dcb1e25fb9f17cf235250fd5582ae
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/project.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/readwrite.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/readwrite.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa2402eb50b45c93225fe475ab3972afccd3691d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/readwrite.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/usize.png b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/usize.png
new file mode 100644
index 0000000000000000000000000000000000000000..a4f80b7a5856a73928506df50936182a9f063f4a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/driver/usb/figures/usize.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/an0036-freemodbus.md b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/an0036-freemodbus.md
new file mode 100644
index 0000000000000000000000000000000000000000..3d0309f82008ce695663576fc6d8568ebf85b883
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/an0036-freemodbus.md
@@ -0,0 +1,372 @@
+# FreeModbus 应用笔记
+
+## 简介
+
+FreeModbus 是一款开源的 Modbus 协议栈,但是只有从机开源,主机源码是需要**收费**的。同时网上也没有比较好的开源的 Modbus 主机协议栈,在这样的背景下,armink 大神开发了这款支持主机模式的 FreeModbus 协议栈。
+
+本文的目的是介绍如何在潘多拉开发板上运行 Modbus 主机与从机。首先介绍串口方式通信的 Modbus 主机和从机。然后介绍如何使用网络进行 Modbus 通信。
+
+## 准备工作
+
+1. 首先演示使用串口作为 Modbus 通信通道的方式,将潘多拉开发板的 `uart2` 通过 usb 转串口线连接到电脑上
+
+2. 使用 usb 线连接开发板的 st-link 接口到电脑上,打开电源开发,开发板上电。
+
+3. 查看设备管理器,可以看到两个串口设备,一个用来 shell 通信,一个用来 Modbus 通信。
+
+
+
+## 运行 Modbus 主机
+
+### 配置工程
+
+在 RT-Thread 源码目录下找到潘多拉的bsp `rt-thread\bsp\stm32\stm32l475-atk-pandora` ,在此目录下打开 ENV 工具。
+
+#### 配置 FreeModbus 软件包
+
+1. 输入 menuconfig 命令打开配置工具
+
+
+
+2. 按照下面的路径进入 FreeModbus 软件包的配置菜单,并开启`主机`模式
+
+ ```
+ RT-Thread online packages --->
+ IoT - internet of things --->
+ [*] FreeModbus: Modbus master and slave stack --->
+ [*] Master mode --->
+ [ ] Slave mode ----
+ Version (latest) --->
+ ```
+
+3. 配置主机模式选项
+
+进入 `Mastar mode` 配置菜单,然后开启主机示例程序,如下图所示:
+
+
+
+- **advanced configuration**:高级配置选项
+- **Enable RTU master mode**:开启 RTU 模式支持(主机暂只支持 RTU 模式)
+- **Enable master sample**:开启主机示例程序
+- **Test slave device address**:测试用的从机设备地址
+- **uart number used by master sample, e.g. 2 means uart2**:表示使用串口几进行通信,默认使用 uart2
+- **uart baudrate used by master sample**:通信用的波特率
+
+#### 配置硬件 uart2
+
+然后返回到主菜单,进入硬件配置的菜单里开启 uart2 。
+
+```
+Hardware Drivers Config --->
+ On-chip Peripheral Drivers --->
+ -*- Enable UART --->
+ [*] Enable UART2
+```
+
+
+
+退出 `menuconfig` 配置工具并保存。然后 `pkgs --update` 下载软件包,然后 `scons --target=mdk5` 生成工程。
+
+
+
+### 运行示例程序
+
+打开工程,在 FreeModbus 分组里可以看到主机的示例代码 `sample_mb_master.c` 关键代码如下所示:
+
+```
+#define MB_POLL_CYCLE_MS 500
+
+static void send_thread_entry(void *parameter)
+{
+ eMBMasterReqErrCode error_code = MB_MRE_NO_ERR;
+ rt_uint16_t error_count = 0;
+ USHORT data[2] = {0};
+
+ while (1)
+ {
+ /* 准备要写入的数据 */
+ data[0] = (USHORT)(rt_tick_get() / 10);
+ data[1] = (USHORT)(rt_tick_get() % 10);
+ /* 向从机写多个保持寄存器 */
+ error_code = eMBMasterReqWriteMultipleHoldingRegister(SLAVE_ADDR, /* salve address */
+ MB_SEND_REG_START, /* register start address */
+ MB_SEND_REG_NUM, /* register total number */
+ data, /* data to be written */
+ RT_WAITING_FOREVER); /* timeout */
+
+ /* Record the number of errors */
+ if (error_code != MB_MRE_NO_ERR)
+ {
+ error_count++;
+ }
+ }
+}
+
+static void mb_master_poll(void *parameter)
+{
+ /* Modbus 主机协议栈初始化,初始化为 RTU 模式 */
+ eMBMasterInit(MB_RTU, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
+ eMBMasterEnable();
+
+ while (1)
+ {
+ /* 定时轮询 */
+ eMBMasterPoll();
+ rt_thread_mdelay(MB_POLL_CYCLE_MS);
+ }
+}
+```
+
+编译下载,程序开始运行。
+
+
+
+输入 `mb_master_samlpe` 运行主机示例程序。
+
+### 运行 Modbus Slave
+
+Modbus Slave 是一个 Modbus 从设备仿真器,可以仿真 32 个从设备/地址域。每个接口都提供了对 EXCEL 报表的 OLE 自动化支持。主要用来模拟 Modbus 从站设备,接收主站的命令包,回送数据包。帮助 Modbus 通讯设备开发人员进行 Modbus 通讯协议的模拟和测试。安装运行 Modbus Slave。下载安装软件 [Modbus Slave]() 。
+
+Modbus Slave 需要先配置从机参数,然后连接主机。
+
+**modbus slave 程序主窗口介绍**
+
+其中:ID = 1表示模拟的Modbus子设备的设备地址;F = 03表示所使用的Modbus功能码,图中为03功能码。红字部分,表示当前的错误状态,“No Connection”表示未连接状态。
+
+单击菜单【Setup】中【Slave Definition.. F8】进行参数设置,会弹出参数设置对话框。
+
+
+
+
+
+然后点击连接(Connection),连接对应开发板上 Modbus 主机控制的 uart2 的端口即可。
+
+
+
+### 运行结果
+
+可以看到 Modbus Slave 与开发板上运行的主机通信成功,并且可以看到其对应的保持寄存器的寄存器 2,3 的数据被不断改变。
+
+
+
+## 运行 Modbus 从机
+
+### 配置工程
+
+在 RT-Thread 源码目录下找到潘多拉的bsp `rt-thread\bsp\stm32\stm32l475-atk-pandora` ,在此目录下打开 ENV 工具。
+
+#### 配置 FreeModebus 软件包
+
+1. 输入 menuconfig 命令打开配置工具
+
+
+
+2. 按照下面的路径进入 FreeModbus 软件包的配置菜单,并开启`从机`模式
+
+ ```
+ RT-Thread online packages --->
+ IoT - internet of things --->
+ [*] FreeModbus: Modbus master and slave stack --->
+ [ ] Master mode ----
+ [*] Slave mode --->
+ Version (latest) --->
+ ```
+
+3. 配置从机模式选项
+
+进入 `Slave mode` 配置菜单,然后开启从机示例程序,如下图所示:
+
+
+
+- **advanced configuration**:高级配置选项
+- **Enable RTU slave mode**:开启 RTU 模式支持
+- **Enable ASCII slave mode**:开启 ASCII 模式支持
+- **Enable TCP slave mode**:开启 TCP 模式支持(需要设备可以连接网络,且可做服务器使用)
+- **Enable slave sample**:开启主机示例程序
+- **Test slave device address**:测试用的从机设备地址
+- **uart number used by master sample, e.g. 2 means uart2**:表示使用串口几进行通信,默认使用 uart2
+- **uart baudrate used by master sample**:通信用的波特率
+
+#### 配置硬件 uart2
+
+然后返回到主菜单,进入硬件配置的菜单里开启 uart2 。
+
+```
+Hardware Drivers Config --->
+ On-chip Peripheral Drivers --->
+ -*- Enable UART --->
+ [*] Enable UART2
+```
+
+
+
+退出 `menuconfig` 配置工具并保存。然后 `pkgs --update` 下载软件包,然后 `scons --target=mdk5` 生成工程。
+
+
+
+### 运行示例程序
+
+打开工程,在 FreeModbus 分组里可以看到从机的示例代码 `sample_mb_slave.c` 关键代码如下所示:
+
+```c
+#define MB_POLL_CYCLE_MS 200
+extern USHORT usSRegHoldBuf[S_REG_HOLDING_NREGS]; /* 存储保持寄存器的数组 */
+
+static void send_thread_entry(void *parameter)
+{
+ USHORT *usRegHoldingBuf;
+ usRegHoldingBuf = usSRegHoldBuf;
+ rt_base_t level;
+
+ while (1)
+ {
+ level = rt_hw_interrupt_disable();
+ /* 改变保持寄存器 3 的数据 */
+ usRegHoldingBuf[3] = (USHORT)(rt_tick_get() / 100);
+
+ rt_hw_interrupt_enable(level);
+ /* 数据产生的速率为 1个/秒 */
+ rt_thread_mdelay(1000);
+ }
+}
+
+static void mb_slave_poll(void *parameter)
+{
+ if (rt_strstr(parameter, "RTU"))
+ {
+#ifdef PKG_MODBUS_SLAVE_RTU # 如果开启了 RTU 模式就检测 RTU 参数
+ eMBInit(MB_RTU, SLAVE_ADDR, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
+#else
+ rt_kprintf("Error: Please open RTU mode first");
+#endif
+ }
+ else if (rt_strstr(parameter, "ASCII"))
+ {
+#ifdef PKG_MODBUS_SLAVE_ASCII # 如果开启了 ASCII 模式就检测 ASCII 参数
+ eMBInit(MB_ASCII, SLAVE_ADDR, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
+#else
+ rt_kprintf("Error: Please open ASCII mode first");
+#endif
+ }
+ else if (rt_strstr(parameter, "TCP"))
+ {
+#ifdef PKG_MODBUS_SLAVE_TCP # 如果开启了 TCP 模式就检测 TCP 参数
+ eMBTCPInit(0); # TCP 模式下使用默认端口 502
+#else
+ rt_kprintf("Error: Please open TCP mode first");
+#endif
+ }
+ else
+ {
+ rt_kprintf("Error: unknown parameter");
+ }
+ eMBEnable();
+ while (1)
+ {
+ eMBPoll();
+ rt_thread_mdelay(MB_POLL_CYCLE_MS);
+ }
+}
+```
+
+编译下载,程序开始运行。
+
+
+
+输入 `mb_slave_samlpe ` 运行示例程序。如运行 `RTU` 模式的从机示例程序。
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.2 build Jul 11 2019
+ 2006 - 2019 Copyright by rt-thread team
+msh />mb_slave_samlpe RTU # 运行 RTU 模式的从机示例程序
+msh />
+```
+
+### 运行 Modbus Poll
+
+Modbus Poll 是一个 Modbus 主机仿真器,用于测试和调试Modbus从设备。该软件支持ModbusRTU、ASCII、TCP/IP。用来帮助开发人员测试Modbus从设备,或者其它Modbus协议的测试和仿真。下载安装软件 [Modbus Poll]() 。
+
+
+
+**modbus poll 程序主窗口介绍**
+
+其中:Tx = 0表示向主站发送数据帧次数; Error = 0表示通讯错误次数; ID = 1表示模拟的Modbus子设备的设备地址;F = 03表示所使用的Modbus功能码,图中为03功能码; SR = 1000ms表示扫描周期。红字部分,表示当前的错误状态,“No Connection”表示未连接状态。
+
+单击菜单【Setup】中【Read/Write Definition.. F8】进行参数设置,会弹出参数设置对话框。
+
+
+
+
+
+然后点击 connection,连接从机。
+
+
+
+
+
+### 运行结果
+
+可以看到 Modbus Poll 与开发板上运行的从机通信成功,并且可以查看到从机 1 保持寄存器寄存器 3 的数据每秒改变一次。
+
+
+
+## 使用网络进行 Modbus 通信
+
+FreeModbus 的**从机**支持 TCP 模式,可以在**已经连接网络,且可做服务端**的设备上运行,并利用 TCP 协议和 远端主机进行通讯。
+
+### 配置 FreeModebus 软件包
+
+按上一节的操作,打开并配置 FreeModbus 软件包,配置支持 TCP 模式。
+
+
+
+退出 `menuconfig` 配置工具并保存。然后 `pkgs --update` 下载软件包,然后 `scons --target=mdk5` 生成工程。
+
+
+
+### 运行示例程序
+
+编译下载,程序开始运行。输入 `mb_slave_samlpe TCP` 运行 `TCP` 模式下的示例程序。
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.2 build Jul 11 2019
+ 2006 - 2019 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+msh />ifconfig # 查看设备 IP 地址
+network interface device: e0 (Default)
+MTU: 1500
+MAC: 00 80 e1 14 2e 34
+FLAGS: UP LINK_UP INTERNET_UP DHCP_ENABLE ETHARP BROADCAST IGMP
+ip address: 192.168.12.162 # 设备的 IP 地址
+gw address: 192.168.10.1
+net mask : 255.255.0.0
+dns server #0: 192.168.10.1
+dns server #1: 223.5.5.5
+msh />mb_slave_samlpe TCP # 运行 TCP 模式的从机示例程序
+msh />
+```
+
+### 运行 Modbus Poll
+
+单击菜单【Connection】中【connect.. F3】进行连接参数设置,会弹出参数设置对话框。选择 TCP/IP 连接方式,配置 从机设备的 IP 地址和端口号,点击 OK 即可。
+
+
+
+### 运行结果
+
+可以看到 Modbus Poll 与开发板上运行的从机通信成功,并且可以查看到从机 1 保持寄存器寄存器 3 的数据每秒改变一次。
+
+
+
+## 注意事项
+
+- 运行 TCP 通信示例之前,请确认当前 BSP **支持网络通信**且**可作为服务端**运行。
+
+## 引用参考
+
+- [FreeModbus 软件包主页]()
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/hw_com.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/hw_com.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1d5e78544d74dde16b8d3e4ec4a979963499d83
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/hw_com.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_menu.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..33af7eb60d189d7d465ea069d35b711069dfe1b7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_menu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_run1.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_run1.png
new file mode 100644
index 0000000000000000000000000000000000000000..cba1c668c76c0cd848751af9f2b7a4878d2d536c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_run1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_run2.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_run2.png
new file mode 100644
index 0000000000000000000000000000000000000000..95e1ffc05aab75eb16a6c7f7351361961126a3dd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/master_run2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/menu.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..53b823fd9075d8758be8881f292f9b9b8f998fb3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/menu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_connection1.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_connection1.png
new file mode 100644
index 0000000000000000000000000000000000000000..18fe7d17018c9f3b8b2117aa416a427851ad1666
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_connection1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_connection2.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_connection2.png
new file mode 100644
index 0000000000000000000000000000000000000000..86c237447a81619047333be2ac0d6546ed26b6c6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_connection2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_main.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_main.png
new file mode 100644
index 0000000000000000000000000000000000000000..b5dffdfba8c5e7db12ebe1deb2addba66559abb6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_main.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_run.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_run.png
new file mode 100644
index 0000000000000000000000000000000000000000..f634625d2750e6f23e7a7b3e7a2df1e578e167d6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_run.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_setup.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_setup.png
new file mode 100644
index 0000000000000000000000000000000000000000..7156fc75021570561c147219e82206f712615c96
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_setup.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_setup2.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_setup2.png
new file mode 100644
index 0000000000000000000000000000000000000000..38cbeeb323ed000d2c543e77da641e92c5cd5185
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/poll_setup2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/run1.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/run1.png
new file mode 100644
index 0000000000000000000000000000000000000000..c32015154ef36904e5421cac6ecbe0e5f54b0f44
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/run1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/scons_project.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/scons_project.png
new file mode 100644
index 0000000000000000000000000000000000000000..1e02084178c704e4f18f9df318e2148bf25488af
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/scons_project.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_connection1.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_connection1.png
new file mode 100644
index 0000000000000000000000000000000000000000..ba0fd7bce01ee4bd91768353c19467ad6fb4814f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_connection1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_menu.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..eae4f7dfb1fc6ef78bcf9ee65cdc7fde9aaa74c2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_menu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_setup.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_setup.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a6a9335e1b4e09ac5c74911dff52d50915be19f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_setup.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_setup2.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_setup2.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f53c660fb0ee60b0348addfeb57879f6aa0f2cd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/slave_setup2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_menu.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..35a926b4fcb7296e0c57a7259137f5b5287a05a2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_menu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_poll.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_poll.png
new file mode 100644
index 0000000000000000000000000000000000000000..f7b7e42bea226c910f97dacd75f8470486ac23ad
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_poll.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_run.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_run.png
new file mode 100644
index 0000000000000000000000000000000000000000..3b6d831a33b1c406f2f234dfe14203d13e02ace3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_run.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_update.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_update.png
new file mode 100644
index 0000000000000000000000000000000000000000..8491461a5f09460e8d0a0ebf9fe206acabdd45aa
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/tcp_update.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/uart2_menu.png b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/uart2_menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..28258d0131912baf920428e8e933d7af7f7a984a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/freemodbus/figures/uart2_menu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/an0029-mbedtls_wireshark_sniffer.md b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/an0029-mbedtls_wireshark_sniffer.md
new file mode 100644
index 0000000000000000000000000000000000000000..fb0c92d7a3b8325f97e8e6e956f0360b88b90f54
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/an0029-mbedtls_wireshark_sniffer.md
@@ -0,0 +1,270 @@
+# 前言
+随着物联网的发展,连接到互联网的设备数量呈指数增长,物联网信息安全越来越重要。
+
+因此,TLS逐渐成为物联网通讯的标配。但是TLS是加密传输,这给调试增加了一定的难度。
+
+笔者最近工作中一直用到HTTPS,但是苦于wireshark只能抓取HTTP的明文数据包,无法抓取HTTPS的数据包,于是就有了这篇文章,使用wireshark抓取HTTPS的数据包.
+
+# 简单介绍TLS1.2握手和协商过程
+
+
+
+## client hello
+客户端向服务器发送客户端信息:
+
+- 支持的最高tls协议版本
+- 客户端支持的加密方式(cipher suites)列表
+- 客户端随机数(ramdom_c)
+- 其他扩展字段
+
+## server hello
+- 服务器端返回 tls 版本, 加密方式(cipher suite), 服务器的随机数(random_s)
+- 服务器发送证书,用于身份验证
+- 通知客户端 server hello 信息发送完成
+
+## 证书校验
+客户端验证证书的合法性,如果验证通过才会进行后续通讯
+
+## client key exchange
+- 客户端计算产生随机数 pre-master,并使用服务器公钥(非对称加密的公钥)加密,发送给服务器
+- 客户端通过random_c,random_s,pre_master计算出密钥
+- 客户端发送change cipher spec通知服务器后续使用此密钥和加密算法进行通信
+- 发送握手数据.
+
+## server change cipher spec
+- 服务器接收 pre_master,并使用服务器私钥(非对称加密的私钥)解密
+- 服务器通过random_c, random_s, pre_master计算出密钥
+- 服务器发送change cipher spec告诉客户端后续的通讯都采用协商的密钥和协商的加密算法通讯
+
+## hangshake message finish
+客户端接收服务器发送的握手消息,验证通过后,握手完成。
+
+此后的通讯都采用协商密钥和加密算法通讯。
+
+# 设备端解密https数据包
+查阅文档得知,wireshark 支持将 tls 会话中使用的密钥保存到外部文件中,供 wireshark 使用。
+
+## 流程图
+
+
+在没有抓包路由器的情况下,使用方案A, 电脑创建 wifi 热点,设备端连接电脑热点,并发起 https 请求,服务器接收到请求,向设备端发出响应,设备端根据响应的内容,计算出密钥, 并将设备端随机数和密钥通过 udp 发送到 pc,保存到 sslkey.log 文件,wireshark 根据设备端随机数和密钥即可将 tls 数据包解密。
+
+## 配置wireshark
+- 新建 sslkey.log 文件,并配置为 windows 系统变量。
+
+
+
+- 配置 wireshark
+
+编辑->首选项->protocols->SSL(version 2.4.9),更高版本的 wireshark 操作步骤为:编辑->首选项->protocols->TLS
+
+
+
+>配置好之后重启 wireshark
+
+按照下面的格式,向 sslkey.log 写入客户端随机数和密钥, 即可使 wireshark 解密 tls 数据包.
+
+```
+CLIENT_RANDOM 5a497axx 3756f69b4axxx
+CLIENT_RANDOM 5dfb96xx b07a9da164xxx
+CLIENT_RANDOM 5a497axx 12e14567b9xxx
+CLIENT_RANDOM 55c00xxx b07a9da164xxx
+CLIENT_RANDOM 5a497xxx b03ca0d5fcxxx
+```
+
+数据的含义如下:
+
+- CLIENT_RANDOM: 固定标签(支持 SSL 3.0, TLS 1.0, 1.1, 1.2)
+- 第二个参数:客户端随机数(random_c)32个字节,编码为64个十六进制字符
+- 第三个参数: 48字节的协商密钥,编码为96个十六进制字符
+
+接下来只要找到设备上的客户端随机数和密钥,保存到 syskey.log,即可通过 wireshark 解密 tls 数据包。
+
+下面函数,保存了客户端随机数和密钥信息。
+
+ssl_tls.c
+
+```c
+int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
+{
+ ...
+
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ciphersuite = %s",
+ mbedtls_ssl_get_ciphersuite_name( session->ciphersuite ) ) );
+ MBEDTLS_SSL_DEBUG_BUF( 3, "master secret", , 48 );
+ MBEDTLS_SSL_DEBUG_BUF( 4, "random bytes", handshake->randbytes, 64 );
+ MBEDTLS_SSL_DEBUG_BUF( 4, "key block", keyblk, 256 );
+ ...
+}
+
+```
+其中`session->master`保存的是密钥,`handshake->randbytes`保存的是客户端和服务器的随机数。
+也就是说,将这两个参数保存到 sslkey.log 文件中,那么 wireshark 就能解密设备上的https数据包。
+
+编写 udp 客户端,将客户端随机数和密钥发送到 windows,windows 编写 udp server python 脚本,用于接收数据,并将数据写入 sslkey.log 文件
+
+```c
+#include
+#include
+
+#include
+#include "netdb.h"
+
+static int port = 5000;
+
+void udpcli_send(char* ip, char* random_c, int random_len, char* master, int master_len)
+{
+ int sock;
+ struct hostent *host;
+ struct sockaddr_in server_addr;
+ char random_ptr[100] = {0};
+ char master_ptr[100] = {0};
+ int i = 0;
+
+ if(random_c == RT_NULL || master == RT_NULL)
+ {
+ rt_kprintf("random_c or master is null\n");
+ return;
+ }
+
+ host = (struct hostent *) gethostbyname(ip);
+ if(host == RT_NULL)
+ {
+ rt_kprintf("Get host by name failed!\n");
+ return;
+ }
+
+ //random server_random : 32bit + client_random : 32bit
+ for(i = 0; i < 32; i++)
+ {
+ sprintf(&random_ptr[i*2], "%02x", random_c[32+i]);
+ }
+
+ for(i = 0; i < 48; i++)
+ {
+ sprintf(&master_ptr[i*2], "%02x", master[i]);
+ }
+ rt_kprintf("random : %s\n", random_ptr);
+ rt_kprintf("master : %s\n", master_ptr);
+
+ if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ {
+ rt_kprintf("Create socket error");
+ return;
+ }
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+ server_addr.sin_addr = *((struct in_addr *)host->h_addr);
+
+ rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
+
+ // sendto(sock, send_data, rt_strlen(send_data), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
+ sendto(sock, random_ptr, 64, 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
+ sendto(sock, master_ptr, 96, 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
+
+ if(sock >= 0)
+ {
+ closesocket(sock);
+ sock = -1;
+ }
+}
+
+```
+
+udpserver.py
+```python
+import socket
+
+BUFSIZ = 1024
+ip_port = ('0.0.0.0', 5000)
+file = r'd:\work\tmp\sslkey.log'
+
+server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+server.bind(ip_port)
+
+while True:
+ random_c, client_addr = server.recvfrom(BUFSIZ)
+ master, client_addr = server.recvfrom(BUFSIZ)
+ print("open file" + " " + file)
+ write_data = 'CLIENT_RANDOM ' + str(random_c, encoding='utf-8') + ' ' + str(master, encoding='utf-8')
+ print(write_data)
+
+ with open(file, 'a') as f:
+ f.write(write_data)
+ print("close file" + " " +file)
+```
+
+需要注意的是,设备使用上述方法解密 https 的数据包,加密算法目前只能是 RSA,所以还需要强制客户端发送的加密方式(cipher suites)只能是 RSA。
+
+修改`packages\mbedtls-latest\ports\inc\tls_config.h`,注释掉如下宏定义:
+
+```c
+// #define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED
+// #define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED
+```
+这样就可以确保客户端和服务器只使用 RSA 的加密方式进行通信, 但是部分服务器不支持 RSA 的方式,握手过程会失败。
+
+将`udpcli_send`函数添加到`mbedtls_ssl_derive_keys`函数中,如下所示
+```c
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ciphersuite = %s",
+ mbedtls_ssl_get_ciphersuite_name( session->ciphersuite ) ) );
+ MBEDTLS_SSL_DEBUG_BUF( 3, "master secret", session->master, 48 );
+ MBEDTLS_SSL_DEBUG_BUF( 4, "random bytes", handshake->randbytes, 64 );
+ MBEDTLS_SSL_DEBUG_BUF( 4, "key block", keyblk, 256 );
+
+ //replace your ip address
+ udpcli_send("192.168.123.206", handshake->randbytes, 32, session->master, 48);
+
+ mbedtls_zeroize( handshake->randbytes, sizeof( handshake->randbytes ) );
+```
+
+windows 运行 python 脚本(注意修改sslkey.log的文件路径)
+
+```
+python udpserver.py
+```
+
+设备联网成功后,在 MSH 终端输入
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.1 build Apr 2 2019
+ 2006 - 2019 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+[I/SAL_SOC] Socket Abstraction Layer initialize success.
+
+...........
+msh /mnt/sdcard>
+msh /mnt/sdcard>
+msh /mnt/sdcard>
+msh /mnt/sdcard>wget https://www.rt-thread.com/service/rt-thread.txt 1.txt
+```
+## wireshark抓包
+
+加密的数据包
+
+
+
+解密的数据包
+
+
+
+## 分析tls数据包
+
+### 查看客户端随机数
+
+
+### 查看服务端随机数
+
+
+### 查看http请求
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/1.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..0b39566a6849fa3ea2a74ee13c2105294897b7b2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/2.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..94fe8c138649030d47f2ce5a3c91f18f1907144c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/3.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..757ce80596b52972d943dcd144ee9c337a3a6a57
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/client_hello.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/client_hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..41018bebb279ea8610047b34601f813c71d9bb37
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/client_hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/flow.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/flow.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ad9b1c8a1be7268a408dd8185f57c48125361ff
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/flow.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/handshake.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/handshake.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab7487362e335956458bf60659dcd41a533f1bc1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/handshake.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/http.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/http.png
new file mode 100644
index 0000000000000000000000000000000000000000..23cbbbeec9e8991cf76174a7198a37a445da7f92
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/http.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/server_hello.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/server_hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..d81faec3c0993851bf6dab6d02ab7a2594f8b63c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/server_hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/system.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/system.png
new file mode 100644
index 0000000000000000000000000000000000000000..5185e36329c6f93a2d88fde72a51a054572312a7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/system.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_config.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_config.png
new file mode 100644
index 0000000000000000000000000000000000000000..94fe8c138649030d47f2ce5a3c91f18f1907144c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_decode.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_decode.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c34c02928d70449879c99d723978e4a9d7a7656
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_decode.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_encrypt.png b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_encrypt.png
new file mode 100644
index 0000000000000000000000000000000000000000..92bbe58a8e603831dca47b08a08ec80bb6800d8c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/mbedtls_wireshark_sniffer/figures/wireshark_encrypt.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/netutils/an0018-system-netutils.md b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/an0018-system-netutils.md
new file mode 100644
index 0000000000000000000000000000000000000000..9abaac3c23b2af48708f222e27aea3c8b5aa2f8d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/an0018-system-netutils.md
@@ -0,0 +1,359 @@
+# 网络工具集 (NetUtils) 使用指南
+
+本文介绍 RT-Thread NetUtils 的使用方法,帮助开发者更好地使用 RT-Thread NetUtils 组件来解决网络开发过程中遇到的问题。
+
+## 简介
+
+在进行网络相关的产品开发和调试时,一些好用的小工具往往能取到事半功倍的效果。 RT-Thread NetUtils 组件基于此应用场景,开发和封装了一系列简洁好用的网络工具集合,为开发者提供便利。
+
+为了方便用户开发网络应用,RT-Thread 将常用的网络工具制作 NetUtils 组件包,通过 Env 动态配置,即开即用, 有效降低资源的占用。
+
+## NetUtils 组件简介
+
+RT-Thread NetUtils 作为网络工具合集,既有用于测试调试的 Ping 命令, 同步时间的 NTP 工具, 性能和带宽测试的 Iperf 、 NetIO,还有在嵌入式系统中广泛使用的轻量级文件传输工具 TFTP,方便地通过网络完成两个设备间的文件互传。另外, RT-Thread 还针对开发中的实际问题,提供了一些高级的辅助工具,如可以远程登录到 RT-Thread Finsh/MSH Shell 的 Telnet 工具,以及基于 lwIP 的网络抓包工具 tcpdump。
+
+下面是 RT-Thread NetUtils 的分类和简介:
+
+| **名称** | **分类** | **描述** |
+|:--|:--:|:--|
+| Ping | 调试测试 | 利用 “ping” 命令可以检查网络是否连通,可以很好地帮助我们分析和判定网络故障 |
+| NTP | 时间同步 | 网络时间协议 |
+| TFTP | 文件传输 | TFTP 是一个传输文件的简单协议,比 FTP 还要轻量级 |
+| Iperf | 性能测试 | 测试最大 TCP 和 UDP 带宽性能,可以报告带宽、延迟抖动和数据包丢失 |
+| NetIO | 性能测试 | 测试网络的吞吐量的工具 |
+| Telnet | 远程访问 | 可以远程登录到 RT-Thread 的 Finsh/MSH Shell|
+| tcpdump | 网络调试 | tcpdump 是 RT-Thread 基于 lwIP 的网络抓包工具 |
+
+每个小工具可使用 menuconfig 独立控制启用 / 停用,并提供了 Finsh/MSH 的使用命令。
+首先打开 Env 工具,进入 BSP 目录,在 Env 命令行输入 menuconfig 进入配置界面配置工程,根据需求选择合适的 NetUtils 功能,如图所示
+
+> [!NOTE]
+> 注:Ping 和 TFTP 依赖于 lwIP,需要先开启 lwIP 的依赖后才能显示
+
+```c
+RT-Thread online packages
+ -> IoT - internet of things
+ -> netutils: Networking utilities for RT-Thread
+```
+
+
+
+## Ping 工具
+
+[Ping](https://baike.baidu.com/item/ping/6235) 是一种网络诊断工具,用来测试数据包能否通过 IP 协议到达特定主机。估算与主机间的丢失数据包率(丢包率)和数据包往返时间(网络时延,Round-trip delay time)
+
+Ping 工具依赖 lwIP,需要先在 Env 工具 开启 lwIP 的依赖才可见,步骤如下:
+
+```c
+-> RT-Thread Components
+ -> Network stack
+ -> light weight TCP/IP stack
+ -> Enable lwIP stack
+```
+
+在 NetUtils 菜单栏使能 Ping 选项:
+
+```c
+RT-Thread online packages
+ -> IoT - internet of things
+ -> netutils: Networking utilities for RT-Thread
+ [*] Enable Ping utility
+```
+
+Ping 支持访问 `IP 地址 ` 或 ` 域名 ` ,使用 Finsh/MSH 命令进行测试,大致使用效果如下:
+
+- Ping 域名
+
+```
+msh />ping rt-thread.org
+60 bytes from 116.62.244.242 icmp_seq=0 ttl=49 time=11 ticks
+60 bytes from 116.62.244.242 icmp_seq=1 ttl=49 time=10 ticks
+60 bytes from 116.62.244.242 icmp_seq=2 ttl=49 time=12 ticks
+60 bytes from 116.62.244.242 icmp_seq=3 ttl=49 time=10 ticks
+msh />
+
+```
+- Ping IP
+
+```
+msh />ping 192.168.10.12
+60 bytes from 192.168.10.12 icmp_seq=0 ttl=64 time=5 ticks
+60 bytes from 192.168.10.12 icmp_seq=1 ttl=64 time=1 ticks
+60 bytes from 192.168.10.12 icmp_seq=2 ttl=64 time=2 ticks
+60 bytes from 192.168.10.12 icmp_seq=3 ttl=64 time=3 ticks
+msh />
+
+```
+
+## NTP 工具
+
+[NTP](https://baike.baidu.com/item/NTP) 是网络时间协议 (Network Time Protocol),它是用来同步网络中各个计算机时间的协议。
+在 RT-Thread 上实现了 NTP 客户端,连接上网络后,可以获取当前 UTC 时间,并更新至 RTC 中。
+
+在 NetUtils 菜单栏使能 NTP 选项:
+
+```c
+RT-Thread online packages
+ -> IoT - internet of things
+ -> netutils: Networking utilities for RT-Thread
+ [*] Enable NTP(Network Time Protocol) client
+```
+
+### 获取 UTC 时间
+
+[UTC 时间](https://baike.baidu.com/item/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6/787659?fromtitle=UTC&fromid=5899996) 又称世界统一时间、世界标准时间、国际协调时间。北京时间为 UTC+8 时间,比 UTC 时间多 8 小时,或者理解为早 8 小时。
+
+获取 UTC 时间函数原型为:`time_t time_t ntp_get_time(void)`,返回值大于 0 则获取时间成功,等于 0 失败。
+
+示例代码:
+
+```c
+#include
+
+void main(void)
+{
+ time_t cur_time;
+
+ cur_time = ntp_get_time();
+
+ if (cur_time)
+ {
+ rt_kprintf("NTP Server Time: %s", ctime((const time_t*) &cur_time));
+ }
+}
+```
+
+### 获取本地时间
+
+本地时间比 UTC 时间多了时区的概念,例如:北京时间为东八区,比 UTC 时间多 8 个小时。在 `menuconfig` 中可以设置当前时区,默认为 `8`。
+
+获取本地时间函数原型为: `time_t ntp_get_local_time(void)`,返回值大于 0 则获取时间成功,等于 0 失败。该 API 使用方法与 `ntp_get_time()` 类似。
+
+### 同步本地时间至 RTC
+
+如果开启 RTC 设备,还可以使用下面的命令及 API 同步 NTP 的本地时间至 RTC 设备。
+
+Finsh/MSH 命令效果如下:
+
+```
+msh />ntp_sync
+Get local time from NTP server: Sat Feb 10 15:22:33 2018
+The system time is updated. Timezone is 8.
+msh />
+```
+
+同步本地时间至 RTC 函数原型为:`time_t ntp_sync_to_rtc(void)`,返回值大于 0 成功,等于 0 失败。
+
+> [!NOTE]
+> NTP API 方法执行时会占用较多的线程堆栈,使用时保证堆栈空间充足(≥1.5K)。
+>
+> NTP API 方法 **不支持可重入**,并发使用时,请注意加锁。
+
+## TFTP 工具
+
+[TFTP](https://baike.baidu.com/item/TFTP) (Trivial File Transfer Protocol, 简单文件传输协议)是 TCP/IP 协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务,端口号为**69**,比传统的 FTP 协议要轻量级很多,适用于小型的嵌入式产品上。
+
+RT-Thread 目前支持的是 TFTP 服务器。
+
+TFTP 工具依赖 lwIP,需要先在 Env 工具 开启 lwIP 的依赖才可见,步骤如下:
+```c
+-> RT-Thread Components
+ -> Network stack
+ -> light weight TCP/IP stack
+ -> Enable lwIP stack
+```
+在 NetUtils 菜单栏使能 TFTP 选项:
+```c
+RT-Thread online packages
+ -> IoT - internet of things
+ -> netutils: Networking utilities for RT-Thread
+ [*] Enable TFTP(Trivial File Transfer Protocol) server
+```
+
+- 安装 TFTP 客户端
+
+安装文件位于 `netutils/tools/Tftpd64-4.60-setup.exe` ,使用 TFTP 前,请先安装该软件。
+
+- 启动 TFTP 服务器
+
+在传输文件前,需要在 RT-Thread 上使用 Finsh/MSH 命令来启动 TFTP 服务器,大致效果如下:
+
+```
+msh />tftp_server
+TFTP server start successfully.
+msh />
+```
+
+### 传输文件
+
+打开刚安装的 `Tftpd64` 软件,按如下操作进行配置:
+
+1、选择 `Tftp Client` ;
+
+2、在 `Server interfaces` 下拉框中,务必选择好与 RT-Thread 处于同一网段的网卡;
+
+3、填写 TFTP 服务器的 IP 地址。可以在 RT-Thread 的 MSH 下使用 `ifconfig` 命令查看;
+
+4、填写 TFTP 服务器端口号,默认: `69`
+
+
+
+### 发送文件到 RT-Thread
+
+1、在 `Tftpd64` 软件中,选择好要发送文件;
+
+2、`Remote File` 是服务器端保存文件的路径(包括文件名),选项支持相对路径和绝对路径。由于 RT-Thread 默认开启 `DFS_USING_WORKDIR` 选项,此时相对路径是基于 Finsh/MSH 当前进入的目录。所以,使用相对路径时,务必提前切换好目录;
+
+3、点击 `Put` 按钮即可。
+
+如下图所示,将文件发送至 Finsh/MSH 当前进入的目录下,这里使用的是**相对路径**:
+
+
+
+> [!NOTE]
+> 注:如果 `DFS_USING_WORKDIR` 未开启,同时 `Remote File` 为空,文件会将保存至根路径下。
+
+### 从 RT-Thread 接收文件
+
+1、在 `Tftpd64` 软件中,填写好要接收保存的文件路径(包含文件名);
+
+2、`Remote File` 是服务器端待接收回来的文件路径(包括文件名),选项支持相对路径和绝对路径。由于 RT-Thread 默认开启 `DFS_USING_WORKDIR` 选项,此时相对路径是基于 Finsh/MSH 当前进入的目录。所以,使用相对路径时,务必提前切换好目录;
+
+3、点击 `Get` 按钮即可。
+
+
+如下所示,将 `/web_root/image.jpg` 保存到本地,这里使用的是**绝对路径**:
+
+```
+msh /web_root>ls ## 查看文件是否存在
+Directory /web_root:
+image.jpg 10559
+msh /web_root>
+```
+
+
+## Iperf 工具
+
+[Iperf](https://baike.baidu.com/item/iperf) 是一个网络性能测试工具。Iperf 可以测试最大 TCP 和 UDP 带宽性能,具有多种参数和 UDP 特性,可以根据需要调整,可以报告带宽、延迟抖动和数据包丢失。
+
+在 NetUtils 菜单栏使能 Iperf 选项:
+```c
+RT-Thread online packages
+ -> IoT - internet of things
+ -> netutils: Networking utilities for RT-Thread
+ [*] Enable iperf-liked network performance tool
+```
+
+Iperf 使用的是主从式架构,即一端是服务器,另一端是客户端,我们提供的 Iperf 软件包实现了 TCP 服务器模式和客户端模式,暂不支持 UDP 测试。下面将具体讲解 2 种模式的使用方法。
+
+### Iperf 服务器模式
+
+#### 获取 IP 地址
+
+需要在 RT-Thread 上使用 Finsh/MSH 命令来获取 IP 地址,大致效果如下:
+
+```
+msh />ifconfig
+network interface: e0 (Default)
+MTU: 1500
+MAC: 00 04 9f 05 44 e5
+FLAGS: UP LINK_UP ETHARP
+ip address: 192.168.12.71
+gw address: 192.168.10.1
+net mask : 255.255.0.0
+dns server #0: 192.168.10.1
+dns server #1: 223.5.5.5
+```
+记下获得的 IP 地址 192.168.12.71(按实际情况记录)
+
+#### 启动 Iperf 服务器
+
+需要在 RT-Thread 上使用 Finsh/MSH 命令来启动 Iperf 服务器,大致效果如下:
+
+```
+msh />iperf -s -p 5001
+```
+
+-s 表示作为服务器启动
+-p 表示监听 5001 端口
+
+- 安装 JPerf 测试软件
+
+安装文件位于 `netutils/tools/jperf.rar` ,这个是绿色软件,安装实际上是解压的过程,解压到新文件夹即可。
+
+- 进行 jperf 测试
+
+打开 `jperf.bat` 软件,按如下操作进行配置:
+
+1、 选择 `Client` 模式;
+
+2、 输入刚刚获得的 IP 地址 192.168.12.71(按实际地址填写);
+
+3、 修改端口号为 5001;
+
+4、 点击 `run Lperf!` 开始测试;
+
+5、 等待测试结束。测试时,测试数据会在 shell 界面和 JPerf 软件上显示。
+
+
+
+### Iperf 客户端模式
+
+- 获取 PC 的 IP 地址
+
+在 PC 的命令提示符窗口上使用 ipconfig 命令获取 PC 的 IP 地址,记下获得的 PC IP 地址为 192.168.12.45(按实际情况记录)。
+
+- 安装 JPerf 测试软件
+
+安装文件位于 `netutils/tools/jperf.rar` ,这个是绿色软件,安装实际上是解压的过程,解压到新文件夹即可。
+
+- 开启 jperf 服务器
+
+打开 `jperf.bat` 软件,按如下操作进行配置:
+
+1、 选择 `Server` 模式
+
+2、 修改端口号为 5001
+
+3、 点击 `run Lperf!` 开启服务器
+
+- 启动 Iperf 客户端
+
+需要在 RT-Thread 上使用 Finsh/MSH 命令来启动 Iperf 客户端,大致效果如下:
+
+```
+msh />iperf -c 192.168.12.45 -p 5001
+```
+-c 表示作为客户端启动,后面需要加运行服务器端的 pc 的 IP 地址
+-p 表示连接 5001 端口
+等待测试结束。测试时,测试数据会在 shell 界面和 JPerf 软件上显示。
+
+
+
+## 其他网络调试工具的介绍和使用
+除了上述常用的网络工具,RT-Thread 也提供一些开发调试中比较实用的网络工具,如 NetIO 工具、 Telnet 工具和 tcpdump 工具。
+
+### NetIO 工具
+[NetIO](http://www.nwlab.net/art/netio/netio.html) 用于在 OS/2 2.x 、 Windows 、 Linux 和 Unix 上进行网络性能测试的工具。它会通过 TCP/UDP 方式,使用不同大小的数据包进行网络净吞吐量测试。
+
+RT-Thread 目前支持的是 NetIO TCP 服务器。
+
+NetIO 的使用请参考组件目录下的 README ,此处不再赘述。
+
+### Telnet 工具
+
+[Telnet](https://baike.baidu.com/item/Telnet) 协议是一种应用层协议,使用于互联网及局域网中,使用虚拟终端机的形式,提供双向、以文字字符串为主的交互功能。属于 TCP/IP 协议族的其中之一,是 Internet 远程登录服务的标准协议和主要方式,常用于网页服务器的远程控制,可供用户在本地主机运行远程主机上的工作。
+
+RT-Thread 目前支持的是 Telnet 服务器, Telnet 客户端连接成功后,将会远程连接到设备的 Finsh/MSH ,实现设备的远程控制。
+
+Telnet 的使用请参考组件目录下的 README ,此处不再赘述。
+
+### tcpdump 工具
+
+tcpdump 是一款基于 RT-Thread 的捕获 IP 报文的小工具, 抓包的数据可以通过文件系统保存,或者通过 rdb 工具导入 PC,利用 wireshark 软件解析。
+
+tcpdump 的使用请参考组件目录下的 README ,此处不再赘述。
+
+## 参考资料
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/env_config.png b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/env_config.png
new file mode 100644
index 0000000000000000000000000000000000000000..40ca4458256b1d0232e1664417351958564b2b92
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/env_config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/iperfc.png b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/iperfc.png
new file mode 100644
index 0000000000000000000000000000000000000000..99422edcb8966f95eed4bb6d35cc182f68b343b6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/iperfc.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/iperfs.png b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/iperfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..b082ac464b552e0e1c41051ff1dccb64aeba1633
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/iperfs.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_cfg.png b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_cfg.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a77b8dfd268294aaa24d7af8d76d9c9075b5832
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_cfg.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_get.png b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_get.png
new file mode 100644
index 0000000000000000000000000000000000000000..79609822aeef762125beccad63e823af9c62147b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_get.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_put.png b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_put.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a14e0ef60afaf978ca690c05e1ac70aa66f5173
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/netutils/figures/tftpd_put.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/an0034-rw007-module-using.md b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/an0034-rw007-module-using.md
new file mode 100644
index 0000000000000000000000000000000000000000..96f20f8b58cec2ab036d918965eeb12ec1e3f529
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/an0034-rw007-module-using.md
@@ -0,0 +1,275 @@
+# 简介
+
+本文使用 STM32 Nucleo 系列开发板连接 RW007 WiFi 模块,通过运行 RT-Thread 操作系统,让开发板轻松愉快联网。
+
+## STM32F401 Nucleo-64
+
+STM32 Nucleo-64 是 ST 官方推出的开发板,依据搭载的 STM32 芯片型号不同(皆为 LQFP64 封装),分为众多版本,本文所使用的是带 STM32F401RE 芯片的板子 —— `STM32F401 Nucleo-64`作为本文的示例,使用其他型号的板子也可以参考本文的方法进行操作,通过 RW007 WIFI 模块方便快速联网。
+
+
+
+### 主要特性
+
+* STM32F401RET6 64 脚 MCU
+* ARM Cortex-M4 内核,84MHz 主频
+* 512KB Flash, 96KB SRAM 存储容量
+* Arduino Uno 和 ST morpho 两类扩展接口
+* 板载 ST-LINK/V2-1 调试编程器、USB 调试串口
+
+Nucleo 上的 Arduino 接口能让开发板与 WiFi 模块「无缝衔接」,值得一提的是,这款开发板还自带了 ST-LINK 和 USB 串口,这就意味着:只需要一根 Mini-USB 线,就能完成开发和调试工作。
+
+快速入门:[Getting started with STM32 Nucleo board software development tools](https://www.st.com/resource/en/user_manual/dm00105928.pdf)
+
+原理图下载:[STM32 Nucleo (64 pins) schematics](https://www.st.com/resource/en/schematic_pack/nucleo_64pins_sch.zip)
+
+*更多相关信息资料见 ST 官网详情页:[STM32 Nucleo-64 development board with STM32F401RE MCU](https://www.st.com/zh/evaluation-tools/nucleo-f401re.html)*
+
+## RW007
+
+RW007 是由上海睿赛德电子科技有限公司开发的高速 WiFi 模块,模块基于 Realtek RTL8710BN(Ameba Z 系列) WIFI SOC,使用 SPI/UART 与主机通信 ,支持 IEEE 802.11b/g/n 网络、 WEP/WPA/WPA2 加密方式和 STA 和 AP 模式。
+
+
+
+### 主要特性
+
+* Cortex-M4 高性能 MCU
+* 可自由选择的 AT SPI 双模式,工作模式可由主机配置
+* SPI 时钟高达 30Mbps,UART 波特率高达 6Mbps。
+* SPI 模式下有效以太网带宽高达上传 1MBytes/s,下载 1MBytes/s
+* 内置 Bootloader,支持固件升级、安全固件功能。
+* 支持快速连接、airkiss 配网
+* 支持存储多达 5 条连接信息
+
+*更多相关信息资料见 RW007 介绍页面:[睿赛德科技推出高速Wi-Fi模块RW007:内置RT-Thread物联网操作系统](https://mp.weixin.qq.com/s/HYHoMnOhzad2m6IBS2Z-Qg)*
+
+由睿赛德推出的 WiFi 模块,可以说是 RT-Thread 的「亲儿子」了,操作系统原生支持,相应的网络组件、WLAN 框架都能完美兼容,在跑 RTT 的板子上使用 RW007,几乎不需要过多配置,即插即用式的使用体验,大大减轻了嵌入式开发者的工作量。
+
+# 准备工作
+
+在把 RW007 畅快跑起来之前,以下准备工作必不可少,你将需要:
+
+1. STM32 Nucleo-64 开发板(或其他支持 RTT 的板子)
+2. RW007 WiFi 模块
+3. Mini-USB 连接线(连接开发板与电脑)
+4. ENV 编译构建环境([安装使用说明](https://www.rt-thread.org/document/site/programming-manual/env/env/))
+5. 开发常用软件(git、Keil5、串口调试等)
+6. 一颗爱折腾的心
+
+
+
+
+# 开始上路
+
+RT-Thread 包含了 RW007 的软件包,用户无需自己编写驱动程序,下面以 SPI 模式(断开模块上 UART 的电阻 R5 和 R7)为例,介绍如何在 STM32F401 Nucleo-64 上驱动 RW007 模块,并完成 AP 扫描、连接等基本 WiFi 功能。
+
+## 硬件连接
+
+得益于 Nucleo 上的 Arduino 接口,只需把 RW007 往开发板上一插,即可完成了两者的硬件连接。显然,其他带 Arduino 接口的开发板也能直接插,就是这么简单粗暴……
+
+
+
+电路连接示意图如下:
+
+
+
+各 IO 接口与功能之间的对应关系表:
+
+| STM32 引脚名 | 封装管脚序号 | Arduino 接口序号 | 功能 |
+|--------------|--------------|------------------|-----------|
+| PA5 | 5 | D13 | BOOT0/CLK |
+| PA6 | 6 | D12 | MISO |
+| PA7 | 7 | D11 | MOSI |
+| PB6 | 22 | D10 | BOOT1/CS |
+| PC7 | 39 | D9 | INT/BUSY |
+| PA9 | 9 | D8 | RESET |
+
+**特别注意!!!**
+
+**关于pin 序号规则,与旧 bsp 使用封装管脚序号不同,在新的 stm32 bsp 框架中,统一采用顺序编号的方式,对 GPIO 驱动进行管理,移植旧程序时要留意。**
+
+pin 序号与引脚名对应关系如下表:
+
+| STM32 引脚名 | 管脚序号 pin |
+|--------------|--------------|
+| PA0 - PA15 | 0 - 15 |
+| PB0 - PB15 | 16 - 31 |
+| PC0 - PC15 | 32 - 47 |
+| PD0 - ... | 48 - ... |
+
+*在 [bsp/stm32/libraries/HAL_Drivers/drv_gpio.c](https://github.com/RT-Thread/rt-thread/blob/master/bsp/stm32/libraries/HAL_Drivers/drv_gpio.c) 的 `pins[]` 数组中,能清除看到 pinmap 关系。*
+
+## STM32 bsp 配置(Menuconfig)
+
+### 步骤一:下载 RT-Thread SDK
+
+* Github 链接:[RT-Thread/rt-thread: RT-Thread is an open source IoT operating system from China.](https://github.com/RT-Thread/rt-thread)
+* Gitee 链接:[RT-Thread/rt-thread: RT-Thread is an open source IoT operating system from China.](https://gitee.com/rtthread/rt-thread)
+
+打开 `rt-thread\bsp\stm32` 目录,能看到 RT-Thread 所支持的开发板型号,把 RT-Thread 在 STM32 上跑起来并不是一件难事,但在编译内核组件之前,要先对 bsp 进行简单配置(别慌,通过 Menuconfig 图形化界面即可完成)。
+
+本次实验所使用的 bsp 为 `stm32f401-st-nucleo`,Github 仓库链接:[rt-thread/bsp/stm32/stm32f401-st-nucleo at master · RT-Thread/rt-thread](https://github.com/RT-Thread/rt-thread/bsp/stm32/stm32f401-st-nucleo), Gitee 仓库链接:[rt-thread/bsp/stm32/stm32f401-st-nucleo at master · RT-Thread/rt-thread](https://gitee.com/rtthread/rt-thread/tree/gitee_master/bsp/stm32/stm32f401-st-nucleo).
+从 RT-Thread SDK 中分离 stm32f401-st-nucleo 分离 BSP 出来。
+进入 `rt-thread\bsp\stm32\stm32f401-st-nucleo` 文件夹,右键打开 ENV 窗口(前提是已在 windows 下搭好 ENV 环境),输入 `scons --dist`命令。
+
+
+
+到此可以把分离出来的 BSP 拷贝到任意的目录中,进行项目的开发。
+
+### 步骤二 通过 CubeMX 配置 SPI 初始化程序
+
+1. 查看对应的引脚:
+
+
+
+2.引脚列表
+
+| STM32 引脚名 | 封装管脚序号 | Arduino 接口序号 | 功能 |
+|--------------|--------------|------------------|-----------|
+| PA5 | 5 | D13 | BOOT0/CLK |
+| PA6 | 6 | D12 | MISO |
+| PA7 | 7 | D11 | MOSI |
+| PB6 | 22 | D10 | BOOT1/CS |
+| PC7 | 39 | D9 | INT/BUSY |
+| PA9 | 9 | D8 | RESET |
+
+3. CubeMX 配置 SPI
+
+ 一般 STM32 系列的引脚分配可以通过对应 BSP 中`board\CubeMX_Config`目录下的`CubeMX_Config.ioc`打开 CubeMX 工程,进行配置 SPI1,并生成代码,保存退出即可 。
+
+ 
+
+
+
+
+
+ 
+
+
+
+### 步骤三 :通过`menuconfig`配置 RW007 软件包
+
+进入 `rt-thread\bsp\stm32\stm32f401-st-nucleo` 文件夹,右键打开 ENV 窗口(前提是已在 Windows 下搭好 ENV 环境),输入 `pkgs --upgrade` 更新 ENV 和软件包,再输入 `menuconfig` 进行系统配置:
+
+
+
+附 Menuconfig 常用操作按键:
+
+| 按键 | ↑↓ | ←→ | Enter | 空格 | Esc |
+|------ |---------|---------|-------|----------|------|
+| 功能 | 列表选择 | 菜单选择 | 确认 | 选中/取消 | 后退 |
+
+**1. 配置开启 SPI 外设**
+
+开发板与模块的通讯依赖 SPI 设备,在 bsp 中已经实现了 SPI 驱动,只需在设置中打开即可使用。 进入 `Hardware Drivers Config --->` 下的 `On-chip Peripheral Drivers`,勾选 `Enable SPI BUS --->` 选项,并按回车键进入,进一步选中 `Enable SPI1 BUS`,完成配置:
+
+
+
+
+
+如果在 bsp 中的 menuconfig 中没有对应 `spi`的配置,可以通过修改 `Kconfig`文件增加对应`spi`的配置。 `Kconfig` 的路径在`board/Kconfig` ,如下面是添加 `SPI1`的配置。
+
+
+
+
+
+**2. 配置 RW007 软件包**
+
+RT-Thread 通过软件包的形式,对 RW007 模块提供配套驱动支持,系统默认选项不包含软件包,用户需手动开启:通过 `Esc` 键回到 Menuconfig 主界面,依次进入 `RT-Thread online packages ---> `、`IoT - internet of things --->`、`Wi-Fi --->`,勾选 `rw007: SPI WIFI rw007 driver --->` 选项:
+
+
+
+*RW007 软件包 Github 仓库链接:[RT-Thread-packages/rw007: RW007 (SPI Wi-Fi module) driver for RT-Thread](https://github.com/RT-Thread-packages/rw007)*
+
+紧接着按下 `Enter` 键进一步设置软件包参数,完成 SPI 总线和 IO 的配置,更改总线设备名称 `RW007 BUS NAME` 为 `spi1`:
+
+
+
+然后配置 SPI 控制 IO,各管脚号依次按下表填入:
+
+| 引脚号 | 功能 |
+|-------|---------------------------------------|
+| 22 | CS pin index |
+| 5 | BOOT0 pin index (same as spi clk pin) |
+| 22 | BOOT1 pin index (same as spi cs pin) |
+| 39 | INT/BUSY pin index |
+| 9 | RESET pin index |
+
+
+
+最高 SPI 速率配置:**从 v1.1.0 版本起**,用户可以根据实际使用情况提高或降低总线速率(默认为 30MHz),为满足对通讯稳定性和传输速度的需求,建议更新至最新版本,对应的 Menuconfig 配置项如下:
+
+
+
+**3. 开启 WiFi 框架**
+
+RW007 驱动使用了 WLAN 相关的接口,按以下选项路径打开 WiFi 框架:`RT-Thread Components --->`、`Device Drivers --->`、`Using WiFi --->`,勾选 `Using Wi-Fi framework`:
+
+
+
+**4. 保存 Menuconfig 配置**
+
+完成了上面的 3 步,bsp 配置算大功告成了,但最最重要的一步不能漏 —— 保存 Menuconfig 配置:直接一路狂按 `Esc` 键退出,在保存提示窗口中选择 `Yes` 确认即可:
+
+
+
+## 编译烧写固件
+
+**1. 更新本地软件包**
+
+根据 RT-Thread 的软件包机制,在 Menucofnig 选中了软件包后,相关代码文件并未添加到工程中。在 ENV 终端输入 `pkgs --update` 命令,便能从服务器下载所选软件包,更新到本地目录:
+
+
+
+**2. 生成 MDK5 项目文件**
+
+使用 Keil IDE 可以十分方便对 STM32 程序编译和烧录,在 ENV 终端输入 `scons --target=mdk5 -s`,生成 Keil5 工程文件:
+
+
+
+**3. 编译、下载工程**
+
+使用工具栏的 `Build` 按钮编译工程,出现 `0 Error(s)` 表示编译成功,将开发板连接电脑,再点击 `Download` 按钮下载固件到开发板,完成上面所有步骤后,接下来就是见证奇迹的时刻了。
+
+## 运行、测试模块功能
+
+下载完程序便能自动复位运行,打开串口工具(推荐使用 XShell 等交互型终端),设置参数为 `115200 8-1-N`。若系统启动正常,且开发板与模块间的通讯也没有问题,会看到如下初始化打印信息:
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.1 build Mar 7 2019
+ 2006 - 2019 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+[I/WLAN.dev] wlan init success
+[I/WLAN.lwip] eth device init ok name:w0
+[I/WLAN.dev] wlan init success
+[I/WLAN.lwip] eth device init ok name:w1
+
+rw007 sn: [rw0072795b24400ac48]
+rw007 ver: [1.2.3]
+
+msh >
+```
+
+使用 `wifi scan` 命令扫描周边热点、`wifi join ssid password` 命令连接路由、`ifconfig` 命令查看网络配置,验证模块功能:
+
+
+
+# 常见问题与解决方法
+
+* **Menuconfig 中没看到 rw007 pkg?**
+
+ ENV 和 包索引不是最新,执行 `pkgs --upgrade` 命令更新软件包源。
+
+* **Keil 编译错误?**
+
+ 是否已经把软件包替换为 RW007 适配版本,新老版本 stm32 bsp 中 spi 接口稍有差异。
+
+* **运行后串口无输出?**
+
+ 1. 检查开发板与电脑的连接是否正常
+ 2. 检查串口工具参数配置,应为 `115200, 8, 1, None`
+
+* **运行出现 `wspi` device not found**
+
+ 确认 RW007 软件包中总线的设备名为 `spi1`,否则会导致设备挂载失败。
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/007-pkg-config.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/007-pkg-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..c31f6d4ab2ec0f9af0650e6108bf759ebf66fbf3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/007-pkg-config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/change-spi-bus-name.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/change-spi-bus-name.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab6fb6d18baa39dfc779e37f681fbe25ac6638ef
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/change-spi-bus-name.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/cubemx_setting.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/cubemx_setting.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c04f689d9fe8ca0623565ff680aae7803a4c986
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/cubemx_setting.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/en.nucleo-F4.jpg b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/en.nucleo-F4.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..ff1e19eb5e5219cee3baa18bed23549292a1ce03
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/en.nucleo-F4.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/enable-spi.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/enable-spi.png
new file mode 100644
index 0000000000000000000000000000000000000000..307285a32a53409485a88a10b23ec928218d6345
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/enable-spi.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/generate-mdk5-project.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/generate-mdk5-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..ec5f311dc6ea13902e6c03509a7d8d54259ca03a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/generate-mdk5-project.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/hardware-connect.jpg b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/hardware-connect.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..ea9cc9dfd6d0b64f8f912e63a3d5842a5382a01c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/hardware-connect.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/kconfig_setting.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/kconfig_setting.png
new file mode 100644
index 0000000000000000000000000000000000000000..124a8b1624c3c33f9349208e11259e0db718e6d1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/kconfig_setting.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/line-connect.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/line-connect.png
new file mode 100644
index 0000000000000000000000000000000000000000..03556eb90ad9cab1c6ed48a896dcf7503c30d24e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/line-connect.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..de1b502080f8d0a3769509a5c99aaebd12e48c95
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/packages-update.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/packages-update.png
new file mode 100644
index 0000000000000000000000000000000000000000..b26f8ee0afbfd04ffe3f6d3cf6fb06e9090b38d9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/packages-update.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/prepare-work.jpg b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/prepare-work.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b73350026c12c8059390925c66cff9d11f768154
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/prepare-work.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/rtt-rw007.jpg b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/rtt-rw007.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..7bd7c87092153bb929ed2c2a16c8d619dea75123
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/rtt-rw007.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/save-menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/save-menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..9732759de09b638fac64bee5d0ee1286cf337ea2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/save-menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/spi_max_hz_config.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/spi_max_hz_config.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac7f0ee2c917f07d831d3f08e4f6cf7c91b45255
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/spi_max_hz_config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/stm32_f401_bsp_split.gif b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/stm32_f401_bsp_split.gif
new file mode 100644
index 0000000000000000000000000000000000000000..59d11cdf7236b781ae555b49cdf535fc5acb166b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/stm32_f401_bsp_split.gif differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/stm32_f401_cubeMX_Setting.gif b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/stm32_f401_cubeMX_Setting.gif
new file mode 100644
index 0000000000000000000000000000000000000000..ccdcb05ba30caedf55f20c1faaaa7d30b4f4f90d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/stm32_f401_cubeMX_Setting.gif differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/use-rw007-pkg.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/use-rw007-pkg.png
new file mode 100644
index 0000000000000000000000000000000000000000..b5e86e8b09e47e9171634b8f02e4f1ffb4865dbd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/use-rw007-pkg.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/using-wifi-framework.png b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/using-wifi-framework.png
new file mode 100644
index 0000000000000000000000000000000000000000..13f2936f0b22bcc0e231aacef94a40d6d1be8942
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/using-wifi-framework.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/wifi_test.gif b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/wifi_test.gif
new file mode 100644
index 0000000000000000000000000000000000000000..216afd1c13cfd0075f8122af9d64521ba71d2161
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/packages/rw007_module_using/figures/wifi_test.gif differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/README.md b/rt-thread-version/rt-thread-standard/application-note/setup/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/an0015-rtthread-setup-git.md b/rt-thread-version/rt-thread-standard/application-note/setup/git/an0015-rtthread-setup-git.md
new file mode 100644
index 0000000000000000000000000000000000000000..0356a89c57eb8e955f70e574529153f34ab0a8a6
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/git/an0015-rtthread-setup-git.md
@@ -0,0 +1,39 @@
+# Git 安装应用笔记 #
+
+本文将详细介绍如何安装 Git,并将 Git 添加到 Windows 系统环境变量。
+
+## 本文的目的和结构 ##
+
+### 本文的目的和背景 ###
+
+Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目,任何程序员想要很好的管理自己的项目肯定离不开 Git 。RT-Thread提供的开发辅助工具 Env 的软件包管理功能也需要 Git 提供支持。
+
+### 本文的结构 ###
+
+本文先解释 Git 安装步骤,然后介绍了怎么添加 Git 到系统环境变量中。
+
+## Git 安装 ##
+
+从 Git 官方网站(https://git-scm.com/download/win)下载 Git 安装包,根据自己的系统配置选择对应的安装包。
+
+
+
+下载完成后运行安装程序,会出现如下图所示的软件安装画面,这是git的安装说明。
+
+
+
+点击“Next >”进入组件选择界面,在这里可以选择自己需要安装的组件,默认选择就可以,默认选择直到安装成功。
+
+
+
+在 Windows 环境下 Git 有两个命令行输入窗口 Git Bash 和 Git CMD,以及一个图形界面窗口 Git Gui。Git Bash是基于Git CMD,在 Git CMD 的基础上增添一些新的命令与功能。所以建议在使用的时候,用 Git Bash 更加方便。
+
+
+
+安装好 Git 后启用 Windows 命令行工具或者 Git Bash ,输入Git 命令将会显示下图所示的提示信息,说明 Git 安装成功。
+
+
+
+
+
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-3.png b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-3.png
new file mode 100644
index 0000000000000000000000000000000000000000..e4cfa3d0bd1c2d5747c35c571f32ba045e930b1e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup.png b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup.png
new file mode 100644
index 0000000000000000000000000000000000000000..cede165c3e47de796e6f022dc1e098a7eb68740e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup2.png b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup2.png
new file mode 100644
index 0000000000000000000000000000000000000000..f9bdfbb649a229d5d7d0e5f6d60b0e5d5a74b1c2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup3.png b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup3.png
new file mode 100644
index 0000000000000000000000000000000000000000..68a349b8fa7b4fef93a566bacdc11e01436ac40a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/git-setup3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path1.png b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path1.png
new file mode 100644
index 0000000000000000000000000000000000000000..fff61bf0a99dd79689a47d559938f11058766548
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path2.png b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path2.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0280bdaf1f8ddd0fad3f7eda3427827af5e972b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path3.png b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path3.png
new file mode 100644
index 0000000000000000000000000000000000000000..b68bb80ebaeecad31088683f83fd5a550afdec45
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/git/figures/path3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/an0020-qemu-eclipse.md b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/an0020-qemu-eclipse.md
new file mode 100644
index 0000000000000000000000000000000000000000..2080ee881bdb1db5ccf4ea4a984b4cc3e10fe30c
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/an0020-qemu-eclipse.md
@@ -0,0 +1,81 @@
+# 使用 Eclipse 开发 RT-Thread #
+
+本文描述了在 Windows 平台使用 Eclipse 开发 RT-Thread qemu-vexpress-a9 BSP 工程。
+
+## 简介
+
+Eclipse 是跨平台的自由集成开发环境(IDE)。最初主要用来 Java 语言开发,通过安装不同的插件 Eclipse 可以支持不同的计算机语言,比如 C++ 和 Python 等开发工具。Eclipse 的本身只是一个框架平台,但是众多插件的支持使得 Eclipse 拥有其他功能相对固定的 IDE 软件很难具有的灵活性。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+* [Eclipse](https://www.eclipse.org/downloads/)
+
+## 步骤一 使用 scons 命令编译工程
+
+打开 Env 文件夹,双击 env.exe 文件打开 Env 控制台:
+
+
+
+在 Env 控制台下切换目录,输入命令 `cd D:\repository\rt-thread\bsp\qemu-vexpress-a9` 切换到 RT-Thread 源码文件夹下的 qemu-vexpress-a9 BSP 根目录,然后输入 `scons` 命令编译工程,如果编译正确无误,会在 BSP 目录下生成 QEMU 下运行的 rtthread.elf 目标文件,调试工程需要这个文件。
+
+
+
+## 步骤二 安装调试插件
+
+在 Eclipse Marketplace 里下载并安装支持 QEMU 的调试插件:
+
+
+
+
+
+## 步骤三 新建 Eclipse 工程
+
+按照如下图所示步骤添加 RT-Thread 源代码到 Eclipse:
+
+
+
+
+
+## 步骤四 新建调试项目
+
+新建调试项目并配置调试参数,如下图所示步骤配置:
+
+
+
+
+
+
+
+
+
+
+
+## 步骤五 调试工程
+
+1、调试相关参数配置好后就可以开始调试了,回到 Env 命令行界面输入 `qemu-dbg.bat` 开启调试模式:
+
+
+
+这时候启动的 QEMU 虚拟机处于暂停状态,等待连接调试:
+
+
+
+2、点击 eclipse 调试配置界面的 “Debug” 按钮,或者点击调试项目名称,开启 eclipse 调试界面,这时就可以对工程进行调试了:
+
+
+
+3、Eclipse 主要调试选项简介如下所示:
+
+
+
+可以使用快捷键 `Ctrl+Shift+r` 查看其他源文件内容。
+
+
+
+## 参考资料
+
+* [《Env 用户手册》](../../../../programming-manual/env/env.md)
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-1-Eclipse-Marketplace.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-1-Eclipse-Marketplace.png
new file mode 100644
index 0000000000000000000000000000000000000000..dd01fb3d41f277ad67e9a9bd824df24c59e329fe
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-1-Eclipse-Marketplace.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-10-debug.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-10-debug.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2a8b366e5d585b1470ea1484c3b49787afc33e6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-10-debug.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-11-debug.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-11-debug.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6aded792bd31a4adf0ac858795f04fe17395076
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-11-debug.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-12-eclipse-debug.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-12-eclipse-debug.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a3bb523b33e0b55c62fac8787ce078cda83e55c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-12-eclipse-debug.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-2-gnu-mcu-eclipse.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-2-gnu-mcu-eclipse.png
new file mode 100644
index 0000000000000000000000000000000000000000..964a71c782318ebd1f9857ca3ef437633d77fbff
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-2-gnu-mcu-eclipse.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-3-eclipse-debug-config.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-3-eclipse-debug-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..ebe2cf0e7750ca427b5a985730255c6eb46adb4d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-3-eclipse-debug-config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-4-eclipse-debug-config.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-4-eclipse-debug-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc9143e9e6541a0d59ddd52bee0c1fc446fc6263
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-4-eclipse-debug-config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-5-eclipse-debug-config.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-5-eclipse-debug-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c2a6f76f8cbc818c7c49bb13fc70ea0c289d1f9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-5-eclipse-debug-config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-6-eclipse-debug-config.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-6-eclipse-debug-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..9401e286e848cb3e8ed7f54013ca12e6abf63d31
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-6-eclipse-debug-config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-7-eclipse-debug-config.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-7-eclipse-debug-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..773ad344d7daf59f437b923672fdd8f6e9096f70
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-7-eclipse-debug-config.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-9-lcd-init.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-9-lcd-init.png
new file mode 100644
index 0000000000000000000000000000000000000000..59f9a8274634f7332550c9897a9c784ba15c0425
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/2.4-9-lcd-init.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/eclipse-debug.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/eclipse-debug.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4e61f24eb86f7647630d9bd3419ef20ed124cdb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/eclipse-debug.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/eclipse-new-project.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/eclipse-new-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..65866cff756a1d51eb95f82847136a6c498e13fb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/eclipse-new-project.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/env.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/env.png
new file mode 100644
index 0000000000000000000000000000000000000000..4794a0b3ac9c35a5996b4e2ca54098e960d41694
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/env.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/open-file.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/open-file.png
new file mode 100644
index 0000000000000000000000000000000000000000..d727d39196aba85d32aaf2bf1b2e5f7a26f3b9b8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/open-file.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/project-dir.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/project-dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..36e17204c32cb2a72748b1ada1cd1ab8f2d117e9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/project-dir.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/qemu-dbg.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/qemu-dbg.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e6aa328594c78df7df0c110e6595de5d6b17e9f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/qemu-dbg.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/scons.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/scons.png
new file mode 100644
index 0000000000000000000000000000000000000000..3365797484214e3aa54ffc5562e005e90649d112
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/eclipse/figures/scons.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/an0005-qemu-ubuntu.md b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/an0005-qemu-ubuntu.md
new file mode 100644
index 0000000000000000000000000000000000000000..827d17c3d87d7e0747f5355de5d14e347fb77266
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/an0005-qemu-ubuntu.md
@@ -0,0 +1,118 @@
+# 在 Ubuntu 平台开发 RT-Thread
+
+本文描述了如何在 Ubuntu 平台使用 QEMU 运行 RT-Thread qemu-vexpress-a9 BSP 工程。
+
+## 简介
+
+嵌入式软件开发离不开开发板,在没有物理开发板的情况下,可以使用 QEMU 等类似的虚拟机来模拟开发板。QEMU 是一个支持跨平台虚拟化的虚拟机,它可以虚拟很多开发板。为了方便大家在没有开发板的情况下体验 RT-Thread,RT-Thread 提供了 QEMU 模拟的 ARM vexpress A9 开发板的板级支持包 (BSP)。
+
+本文主要介绍在 Ubuntu 平台使用 QEMU 运行 RT-Thread qemu-vexpress-a9 BSP 工程。
+
+## 准备工作
+
+### 安装环境
+
+* 下载 RT-Thread 源码,使用命令:
+
+```
+git clone https://github.com/RT-Thread/rt-thread.git
+```
+
+* 安装 QEMU,使用命令:
+
+```
+sudo apt-get install qemu
+```
+
+* 安装 Scons,使用命令:
+
+```
+sudo apt-get install scons
+```
+
+* 安装编译器。使用 apt-get 命令安装的编译器版本太旧会导致编译报错,可依次使用如下命令下载安装新版本,下载链接和解压文件夹名因下载版本而异:
+
+```
+tom@laptop:~$ cd /tmp/
+tom@laptop:/tmp$ wget https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/6-2016q4/gcc-arm-none-eabi-6_2-2016q4-20161216-linux.tar.bz2
+tom@laptop:/tmp$ tar xf ./gcc-arm-none-eabi-6_2-2016q4-20161216-linux.tar.bz2
+tom@laptop:/tmp$ mv gcc-arm-none-eabi-6_2-2016q4/ /opt/
+tom@laptop:/tmp$ /opt/gcc-arm-none-eabi-6_2-2016q4/bin/arm-none-eabi-gcc --version
+```
+
+以上命令下载编译器,并安装到 `/opt/gcc-arm-none-eabi-6_2-2016q4/` 。如果最后输出结果如下,则安装完成:
+
+```
+arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 6.2.1 20161205 (release) [ARM/embedded-6-branch revision 243739]
+Copyright (C) 2016 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+```
+
+* 安装 ncurses 库,使用命令:
+
+```
+sudo apt-get install libncurses5-dev
+```
+
+### 配置 rtconfig.py
+
+编译器安装好以后需要修改源码中的 `bsp/qemu-vexpress-a9/rtconfig.py` 文件,修改 gcc 链接的路径为刚才安装路径,如将 `EXEC_PATH = '/usr/bin'` 改为:
+
+```
+EXEC_PATH = '/opt/gcc-arm-none-eabi-6_2-2016q4/bin'
+```
+
+如下:
+
+
+
+## 使用 menuconfig 配置工程
+
+1、在 qemu-vexpress-a9 BSP 根目录输入
+
+```
+scons --menuconfig
+```
+
+此命令开启配置界面,配置操作和 Window 平台一样:
+
+
+
+2、使用 `scons --menuconfig` 命令后会安装及初始化 Env 工具,并在 home 目录下面生成 “.env” 文件夹,此文件夹为隐藏文件夹,切换到 home 目录,使用 `la` 命令可查看所有目录和文件。
+
+```
+$ la ~/.env
+env.sh local_pkgs packages tools
+```
+
+运行 env.sh 会配置好环境变量,让我们可以使用 `pkgs` 命令来更新软件包,执行
+
+```
+$ source ~/.env/env.sh
+```
+
+若已经选择了在线软件包,就可以使用 `pkgs --update` 命令下载软件包到 BSP 目录下的 packages 文件夹里:
+
+```
+$ pkgs --update
+```
+
+## 编译和运行 RT-Thread
+
+1、在 qemu-vexpress-a9 BSP 目录下输入 `scons` 命令编译工程:
+
+
+
+2、输入 ls 命令查看 BSP 下面的文件明细,绿色显示的文件是有执行权限的文件,我们需要给 qemu.sh 文件新增执行权限,输入 `chmod +x qemu.sh` 命令:
+
+
+
+3、输入 `./qemu.sh` 命令执行脚本文件,这时候虚拟机便运行起来,如下图所示,命令行显示了 RT-Thread 操作系统启动过程所打印的相关信息,弹出的窗口为虚拟的 LCD 屏。
+
+
+
+## 参考资料
+
+* [《Env 用户手册》](../../../../programming-manual/env/env.md)
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.1-1-rtconfig.py.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.1-1-rtconfig.py.png
new file mode 100644
index 0000000000000000000000000000000000000000..74b56845c796821f60f59b88e8f7bebbef5c0a91
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.1-1-rtconfig.py.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-1-linux-menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-1-linux-menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..32a0ec17250da4a6e17c2b5b0940e7055994ec3d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-1-linux-menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-2-linux-env.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-2-linux-env.png
new file mode 100644
index 0000000000000000000000000000000000000000..c790fcbc38fdd4e48771c7ebb4b6f37024c7ec9c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-2-linux-env.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-3-pkgs--update.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-3-pkgs--update.png
new file mode 100644
index 0000000000000000000000000000000000000000..714dcc6b473d13752200592847dd62e1867058d3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.2-3-pkgs--update.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-1-linux-scons.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-1-linux-scons.png
new file mode 100644
index 0000000000000000000000000000000000000000..5beecb66da315509d69e924dc77b7f39cc1b0574
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-1-linux-scons.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-2-qemu.sh.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-2-qemu.sh.png
new file mode 100644
index 0000000000000000000000000000000000000000..3648d7fec5796ecc87112f3ad9903d56a3c2b717
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-2-qemu.sh.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-3-qemu.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-3-qemu.png
new file mode 100644
index 0000000000000000000000000000000000000000..b02740a4dbb9dcaa3c366695024b6ccb7a616005
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/ubuntu/figures/3.3-3-qemu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/an0021-qemu-vscode.md b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/an0021-qemu-vscode.md
new file mode 100644
index 0000000000000000000000000000000000000000..34d06e21043d0373b87987783723fe0792deecd5
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/an0021-qemu-vscode.md
@@ -0,0 +1,136 @@
+# 使用 VS Code 开发 RT-Thread #
+
+本文描述了在 Windows 平台使用 VS Code 开发 RT-Thread qemu-vexpress-a9 BSP 工程。
+
+## 简介
+
+VS Code(全称 Visual Studio Code)是一个轻量且强大的代码编辑器,支持 Windows,OS X 和 Linux。内置 JavaScript、TypeScript 和 Node.js 支持,而且拥有丰富的插件生态系统,可通过安装插件来支持 C++、C#、Python、PHP 等其他语言。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+* [VS Code](https://code.visualstudio.com/Download)
+
+## 步骤一 安装调试插件
+
+在 VS Code Extensions 里下载并安装支持 C/C++ 的调试插件:
+
+
+
+安装好后确认插件为以下状态,如果不是则点击重新加载:
+
+
+
+## 步骤二 打开 VS Code 项目工程
+
+在 Env 控制台进入 qemu-vexpress-a9 BSP 根目录,然后输入命令 `code .` (**注意:code 后面有一个点**)打开 VS Code,表示使用 VS Code 打开当前目录。
+
+
+
+VS Code 打开后会自动打开 qemu-vexpress-a9 BSP 文件夹,如下图所示。
+
+
+
+## 步骤三 编译 RT-Thread
+
+点击 VS Code “查看 -> 终端” 打开 VS Code 内部终端,在终端里输入命令 `scons` 即可编译工程,终端会打印出编译信息。
+
+
+
+编译完成后输入 `.\qemu.bat` 命令就可以运行工程。终端会输出 RT-Thread 启动 logo 信息,QEMU 也运行了起来。
+
+
+
+> [!NOTE]
+> 注:1、调试 BSP 工程前需要先编译工程生成 rtthread.elf 文件。
+ 2、可以使用 `scons --target=vsc -s` 命令更新 VS Code 需要用到的 C/C++ 头文件搜索路径信息。不是每次都需要更新,只有在使用了 menuconfig 重新配置了 RT-Thread 或更改了 rtconfig.h 头文件时才需要。
+
+## 步骤四 修改 qemu-dbg.bat 文件
+
+开始调试前需要编辑 `qemu-vexpress-a9` 目录下的 `qemu-dbg.bat` 文件,在 qemu-system-arm 前加入 start :
+
+```bash
+@echo off
+if exist sd.bin goto run
+qemu-img create -f raw sd.bin 64M
+
+:run
+start qemu-system-arm -M vexpress-a9 -kernel rtthread.elf -serial stdio -sd sd.bin -S -s
+
+```
+
+## 步骤五 调试工程
+
+如下图所示,在 VS Code 里点击调试菜单(小虫子图标),调试平台选择 Windows,然后按 F5 就可以开启 QEMU 调试模式,断点停留在 main 函数。VS Code 调试选项如下图所示:
+
+
+
+QEMU 也运行了起来,如下图所示。
+
+
+
+在 VS Code 里可以使用 GDB 命令,需要在最前面加上 `-exec`。 例如 `-exec info registers` 命令可以查看寄存器的内容:
+
+
+
+其他一些主要命令介绍如下所示:
+
+查看内存地址内容:`x/ `,各个参数说明如下所示:
+
+* n 是一个正整数,表示需要显示的内存单元的个数,也就是说从当前地址向后显示几个内存单元的内容,一个内存单元的大小由后面的 u 定义
+* f 表示显示的格式,参见下面。如果地址所指的是字符串,那么格式可以是 s。其他格式如下表所示:
+
+| 参数 | 描述 |
+| ------------- | --------------- |
+| x | 按十六进制格式显示变量 |
+| d | 按十进制格式显示变量 |
+| u | 按十六进制格式显示无符号整型 |
+| o | 按八进制格式显示变量 |
+| t | 按二进制格式显示变量 |
+| a | 按十六进制格式显示变量 |
+| c | 按字符格式显示变量 |
+| f | 按浮点数格式显示变量 |
+
+* u 表示从当前地址往后请求的字节数,如果不指定的话,GDB 默认是 4 个 bytes。u 参数可以用下面的字符来代替,b 表示单字节,h 表示双字节,w 表示四字 节,g 表示八字节。当我们指定了字节长度后,GDB 会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
+* addr 表示一个内存地址。
+
+> [!NOTE]
+> 注:严格区分 n 和 u 的关系,n 表示单元个数,u 表示每个单元的大小。
+
+示例: `x/3uh 0x54320` 表示从内存地址 0x54320 读取内容,h 表示以双字节为一个单位,3 表示输出三个单位,u 表示按十六进制显示。
+
+查看当前程序栈的内容: x/10x $sp--> 打印 stack 的前 10 个元素
+查看当前程序栈的信息: info frame----list general info about the frame
+查看当前程序栈的参数: info args---lists arguments to the function
+查看当前程序栈的局部变量: info locals---list variables stored in the frame
+查看当前寄存器的值:info registers(不包括浮点寄存器) info all-registers(包括浮点寄存器)
+查看当前栈帧中的异常处理器:info catch(exception handlers)
+
+> 提示:输入命令时可以只输入每个命令的第一个字母。例如:`info registers` 可以只输入 `i r`。
+
+> [!NOTE]
+> 注:如果在 VS Code 目录中额外添加了文件夹,会导致调试不能够启动。
+ 每次开始调试都需要使用 Env 工具在 BSP 根目录使用`code .`命令打开 VS Code 才能正常调试工程。
+
+## 参考资料
+
+* [《Env 用户手册》](../../../../programming-manual/env/env.md)
+
+## 常见问题
+
+### Env 工具的相关问题请参考《Env 用户手册》常见问题小节。
+
+### Q: 提示找不到 ‘qemu-system-arm’。
+
+**A:** 直接打开 VS Code 调试工程会有这个错误,**每次调试请使用 Env 工具在 BSP 根目录使用`code .`命令打开 VS Code** 。
+
+### Q: VS Code 调试选项没有出现 Debug@windows选项或者其他不能调试问题。
+
+**A:** 请更新 RT-Thread 源代码到 v3.1.0 及以上版本。
+
+### Q: VS Code 出现错误提示:Unable to start debugging.Unexpected GDB output from command "-interpreter-exec console" ........
+
+**A:** 请修改 qemu-dbg.bat 文件,特别是有更新源代码的情况下。有问题请按照文档步骤检查一遍是否每一步都做了。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/dbg.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/dbg.png
new file mode 100644
index 0000000000000000000000000000000000000000..0cbd4f19066bf72c7703bf0582ebb620446ea325
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/dbg.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/debug-set.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/debug-set.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e1fa42d83b9c89924655e703f9d0a917b5eddca
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/debug-set.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/installplugin.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/installplugin.png
new file mode 100644
index 0000000000000000000000000000000000000000..61c38e048a89c47b500451c9dd7a2d2082663d8e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/installplugin.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/open.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/open.png
new file mode 100644
index 0000000000000000000000000000000000000000..e9495739f649c590576ade0cb3f78502e5c74c90
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/open.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/plugin-status.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/plugin-status.png
new file mode 100644
index 0000000000000000000000000000000000000000..d2050a4d86eac14ba58e58d14806e3b7d305cacd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/plugin-status.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/register.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/register.png
new file mode 100644
index 0000000000000000000000000000000000000000..29dc8e82c5bb984df31281707a6b696ad66c211f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/register.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/sconsvsc.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/sconsvsc.png
new file mode 100644
index 0000000000000000000000000000000000000000..3df2c87a92b0a0ba16928d9f99196100335d6b55
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/sconsvsc.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/vscode-run.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/vscode-run.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b5d4a8c54c6990742821cb23780f776e963e10a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/vscode-run.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/vsscons.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/vsscons.png
new file mode 100644
index 0000000000000000000000000000000000000000..a3c272bc0bf1378b38e60178f497a790898c01b7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/vscode/figures/vsscons.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/an0006-qemu-windows.md b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/an0006-qemu-windows.md
new file mode 100644
index 0000000000000000000000000000000000000000..0e236beb151c10fd2384820ad5958280dd7909b2
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/an0006-qemu-windows.md
@@ -0,0 +1,207 @@
+# 在 Window 平台使用 QEMU 运行 RT-Thread
+
+本文描述了如何在 Window 平台使用 QEMU 运行 RT-Thread qemu-vexpress-a9 BSP 工程。
+
+## 本文的目的和结构
+
+### 本文的目的和背景
+
+嵌入式软件开发离不开开发板,在没有物理开发板的情况下,可以使用 QEMU 等类似的虚拟机来模拟开发板。QEMU 是一个支持跨平台虚拟化的虚拟机,它可以虚拟很多开发板。为了方便大家在没有开发板的情况下体验 RT-Thread,RT-Thread 提供了 QEMU 模拟的 ARM vexpress A9 开发板的板级支持包 (BSP)。
+
+本文主要介绍在 Window 平台使用 QEMU 运行 RT-Thread qemu-vexpress-a9 BSP 工程,并介绍了如何使用虚拟网卡连接 QEMU 到网络。
+
+### 本文的结构
+
+本文介绍了 qemu-vexpress-a9 BSP 在 Window 平台的使用明细。
+
+## 准备工作
+
+* [下载 RT-Thread 源码](https://www.rt-thread.org/page/download.html),推荐下载3.1.0及以上版本。
+
+* 下载 RT-Thread Env 工具,推荐下载1.0.0及以上版本。参考编程指南-《Env 用户手册了解如何下载及使用。
+
+RT-Thread 提供的 QEMU 模拟的 ARM vexpress A9 开发板的板级支持包 (BSP) 位于 RT-Thread 源码 BSP 目录下的 qemu-vexpress-a9 文件夹,此 BSP 实现了 LCD、键盘、鼠标、SD 卡、以太网卡、串口等相关驱动,文件夹内容如下图所示。
+
+
+
+qemu-vexpress-a9 BSP 主要文件及目录描述如下所示:
+
+| 文件 / 目录 | 描述 |
+| ------------- | --------------- |
+| .vscode | vscode 配置文件 |
+| applications | 用户应用代码目录 |
+| cpu | 芯片相关 |
+| drivers | RT-Thread 提供的底层驱动 |
+| qemu.bat | Windows 平台运行脚本文件 |
+| qemu.sh | Linux 平台运行脚本文件 |
+| qemu-dbg.bat | Windows 平台调试脚本文件 |
+| qemu-dbg.sh | Linux 平台调试脚本文件 |
+| README.md | BSP 说明文件 |
+| rtconfig.h | BSP 配置头文件 |
+
+## 编译和运行工程
+
+### 步骤一 使用 scons 命令编译工程
+
+打开 Env 文件夹,双击 env.exe 文件打开 Env 控制台:
+
+
+
+在 Env 控制台下切换目录,输入命令 `cd D:\repository\rt-thread\bsp\qemu-vexpress-a9` 切换到 RT-Thread 源码文件夹下的 qemu-vexpress-a9 BSP 根目录,然后输入 `scons` 命令编译工程,如果编译正确无误,会在 BSP 目录下生成 QEMU 下运行的 rtthread.elf 目标文件。
+
+
+
+### 步骤二 使用 qemu.bat 命令运行工程
+
+编译完成后输入 `qemu.bat` 启动虚拟机及 BSP 工程,qemu.bat 是 Window 批处理文件,此文件位于 BSP 文件夹下,主要包括 QEMU 的执行指令,第一次运行工程会在 BSP 文件夹下创建一份空白的 sd.bin 文件,这是虚拟的 sd 卡,大小为 64M。Env 命令行界面显示 RT-Thread 系统启动过程中打印的初始化信息及版本号信息等,qemu 虚拟机也运行起来了。如下面图片所示:
+
+
+
+
+
+**注意事项:**若电脑安装有 360 安全卫士会有警告,请点击允许程序运行。
+
+## 运行 Finsh 控制台
+
+RT-Thread 支持 Finsh,用户可以在命令行模式使用命令操作。输入 `help` 或按 tab 键可以查看所有支持的命令。如下图所示,左边为命令,右边为命令描述。
+
+
+
+如下图所示,比如输入`list_thread`命令可以查看当前运行的线程,以及线程状态和堆栈大小等信息。输入`list_timer`可以查看定时器的状态。
+
+
+
+## 运行文件系统
+
+输入 `list_device` 可以查看注册到系统的所有设备。如下面图片所示可以看到虚拟的 sd 卡 “sd0” 设备,接下来我们可以使用 `mkfs sd0` 命令格式化 sd 卡,执行该命令会将 sd 卡格式化成 FatFS 文件系统。FatFs 是专为小型嵌入式设备开发的一个兼容微软 fat 的文件系统,采用 ANSI C 编写,采用抽象的硬件 I/O 层以及提供持续的维护,因此具有良好的硬件无关性以及可移植性。
+
+了解 FatFS 详细信息请点击链接:
+
+
+
+第一次格式化 sd 卡后文件系统不会马上装载上,第二次启动才会被正确装载。我们退出虚拟机,然后在 Env 命令行界面输入 `qemu.bat` 重新启动虚拟机及工程,输入 `ls` 命令可以看到新增了 Directory 目录,文件系统已经装载上,然后可以使用 RT-Thread 提供的其他命令体验文件系统
+
+
+
+## 运行网络功能
+
+### 步骤一 安装和配置 TAP 网卡
+
+1、下载 TAP 网卡 [tap-windows-9.21.2.exe](https://pan.baidu.com/s/1h2BmdL9myK6S0g8TlfSW0g)。下载好后双击安装程序,默认安装即可。
+
+2、打开网络和共享中心更改适配器设置,将安装的虚拟网卡重命名为 tap,如下图所示:
+
+ 
+
+**方法A:**右键当前能上网的网络连接(本文使用以太网),打开属性 -> 共享,选择家庭网络连接为 tap,点击确定完成设置,如下图所示:
+
+ 
+
+ **方法B:**也可以将一个能正常连接网络的物理网卡与 tap 进行桥接,桥接成功后会出现一个网桥。如下面图片所示:
+
+
+
+
+
+**注意事项:** tap 网卡和 VMware 的虚拟网卡可能会冲突,如果出现无法开启网络共享,或者 ping 不通网络的情况,请在删除 VMware 虚拟网卡之后再尝试一次。
+
+### 步骤二 修改 qemu.bat 脚本文件
+
+打开 qemu-vexpress-a9 BSP 目录下的 qemu.bat 文件,在下图所示位置添加以下配置内容:
+
+`-net nic -net tap,ifname=tap`
+
+其中 ifname=tap 的意思是网卡的名称是 tap。
+
+
+
+### 步骤三 查看 IP 地址
+
+输入 `qemu.bat` 命令运行工程,在 shell 中输入 `ifconfig`命令查看网络状态,正常获取到 IP 即表示网络驱动正常,配置工作完成,效果如下图所示:
+
+
+
+### 注意事项
+
+* 当出现获取不到 IP 地址的情况时,先将以太网的共享关闭,然后再次打开即可。
+
+* 如果获取到的 IP 是 10.0.x,x,是因为没有为 QEMU 添加启动参数 `-net nic -net tap,ifname=tap` 。
+
+* 虚拟机刚开始运行的时候并不会立刻获取到 IP 地址,有时需要等待几秒钟才会获取到 IP。
+
+* 关闭虚拟机可以按 Ctrl + 'C' 来结束程序运行。
+
+## 运行 Ping 工具
+
+### 步骤一 下载网络工具软件包
+
+1、在路径`bsp\qemu-vexpress-a9`下打开 Env 工具,执行 menuconfig,如下图所示:
+
+
+
+2、在 RT-Thread online packages->IoT - internet of things 页面打开 netutils: Networking utilities for RT-Thread 功能,如下图所示:
+
+ 
+
+3、进入 netutils: Networking utilities for RT-Thread 页面,打开 Enable Ping utility 功能,如下图所示:
+
+ 
+
+4、保存并退出配置界面。如果没有开启 Env 自动更新软件包功能的话,需要输入 `pkgs --update` 更新软件包配置。更新完成后使用 scons 命令重新编译工程,如下图所示:
+
+
+
+5、编译完成之后运行 qemu.bat 文件,如下图所示:
+
+
+
+### 步骤二 运行 ping 工具
+
+在 shell 中输入 `ifconfig`命令查看网络状态,正常获取到 IP 即表示网络驱动正常:
+
+
+
+在 shell 中输入 ping www.rt-thread.com 可以看到 ping 通的返回结果, 表示网络配置成功,能够 ping 通,如下图所示:
+
+
+
+## 运行 GUI 引擎
+
+### 步骤一 下载 GUI 引擎软件包
+
+关掉 QEMU 虚拟机,回到 ENV 控制台,输入 `menuconfig` 命令进入配置界面:
+
+
+
+进入 “RT-Thread oneline packages” → “system packages” → “RT-Thread GUI Engine” 子菜单,选中 “Enable GUI Engine” 和“Enable the example of GUI Engine”:
+
+
+
+按‘→’键选中 “save” 保存配置,并按 “Exit” 键退出配置界面,回到命令行界面,输入 `pkgs --update` 下载 GUI 软件包及示例代码:
+
+
+
+### 步骤二 运行 GUI 引擎
+
+软件包下载完成后输入 `scons` 重新编译工程:
+
+
+
+编译完成后输入 `qemu.bat` 命令启动 QEMU 虚拟机及工程,可以看到 QEMU 虚拟的显示屏上显示了示例代码展示的图片、文字和图形信息:
+
+
+
+## 参考资料
+
+* 《Env 用户手册》
+
+* 《文件系统》
+
+## 常见问题
+
+* **Env 工具的相关问题请参考编程指南-《Env 用户手册》章节。**
+
+* **编译工程提示 fatal error: rtgui/driver.h: No such file or directory**
+
+> 提示:解决方法:使用 menuconfig 使能 “Enable GUI Engine”后需要使用命令`pkgs --update`下载 GUI 软件包。
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/2.3-5-gui.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/2.3-5-gui.png
new file mode 100644
index 0000000000000000000000000000000000000000..4649339c3c14aed33d270e84cb2cd568920537e7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/2.3-5-gui.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/echo-cat.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/echo-cat.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2d3e56bb3d067f69bdfad392bf4a4493aea7f12
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/echo-cat.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/enable_ping.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/enable_ping.png
new file mode 100644
index 0000000000000000000000000000000000000000..68ad5cfd01d4a7f4429aeff096de4a1339765639
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/enable_ping.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/env.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/env.png
new file mode 100644
index 0000000000000000000000000000000000000000..4794a0b3ac9c35a5996b4e2ca54098e960d41694
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/env.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/env_menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/env_menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..1cedd2650432e0153f5ff1a4e3f5fa8a1bd01424
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/env_menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/finsh-cmd.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/finsh-cmd.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5145c075c51d5da18c60f7b410a4d3d886ee293
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/finsh-cmd.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/finsh-thread.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/finsh-thread.png
new file mode 100644
index 0000000000000000000000000000000000000000..5cd53699862825d53836c8ab5c9cd64325d88557
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/finsh-thread.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/gui.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/gui.png
new file mode 100644
index 0000000000000000000000000000000000000000..e667396c8c6e9cfb6ef6c4325e9914f2ce1bed69
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/gui.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/ifconfig.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/ifconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..feadc74b86e43f74255f87f976f61077ef52b43e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/ifconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..213c964f831f712dfee338834cc2db4c4e765074
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/mkfs-sd0.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/mkfs-sd0.png
new file mode 100644
index 0000000000000000000000000000000000000000..4bcea502b01bca5a8ebf0e1b9344097bc505fd27
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/mkfs-sd0.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/online_packages.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/online_packages.png
new file mode 100644
index 0000000000000000000000000000000000000000..63391b83dab188595371b4123eac48e5e1f5a4e0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/online_packages.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/ping.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/ping.png
new file mode 100644
index 0000000000000000000000000000000000000000..12ac99b24678f66e60dc57bfe827a819e2ce52c4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/ping.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/pkgs--update.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/pkgs--update.png
new file mode 100644
index 0000000000000000000000000000000000000000..dfc1116180244859166d974164a73c3c7d4bbe3c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/pkgs--update.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu-dbg.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu-dbg.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e6aa328594c78df7df0c110e6595de5d6b17e9f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu-dbg.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu.bat.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu.bat.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e6c4fc46921d8d08a31ee9f3bbe28bd0743b174
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu.bat.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu.png
new file mode 100644
index 0000000000000000000000000000000000000000..bed1e4d3c75b28665c634b1e3993ede5107dbc72
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu_bat.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu_bat.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8306ddb134757cc7530fa67cd487b2dd8f3f1af
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu_bat.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu_modify.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu_modify.png
new file mode 100644
index 0000000000000000000000000000000000000000..e014667302ccb6baf4718d7bd58f8a7d6ca9138f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemu_modify.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemubsp.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemubsp.png
new file mode 100644
index 0000000000000000000000000000000000000000..2584eedc7b0eec38ab8cf522bd20a6fcdce6c4e9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/qemubsp.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons-again.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons-again.png
new file mode 100644
index 0000000000000000000000000000000000000000..9d1ec2f49fdb792904c04f0d96cc08672962aa16
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons-again.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons-net.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons-net.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a23c03e815391be93e759ff342c3d2421e86dc4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons-net.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons.png
new file mode 100644
index 0000000000000000000000000000000000000000..3365797484214e3aa54ffc5562e005e90649d112
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/scons.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_bridge1.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_bridge1.png
new file mode 100644
index 0000000000000000000000000000000000000000..f025d2ba9701178b73f4acb862aa85559ad5ae48
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_bridge1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_bridge2.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_bridge2.png
new file mode 100644
index 0000000000000000000000000000000000000000..a221e605aef38aa8c5121ee925fc0e1a9fefe373
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_bridge2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_rename.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_rename.png
new file mode 100644
index 0000000000000000000000000000000000000000..927149b6611f0a15c185dd722545df207b7b8a86
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_rename.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_share_internet.png b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_share_internet.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f1c8f42605e5704ecba519e4d21a263da56700b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/qemu/windows/figures/tap_share_internet.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/rt-thread-version/an0030-rtthread-version.md b/rt-thread-version/rt-thread-standard/application-note/setup/rt-thread-version/an0030-rtthread-version.md
new file mode 100644
index 0000000000000000000000000000000000000000..6be03578cea713dcaef6e4fabbfec88c673a89a6
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/rt-thread-version/an0030-rtthread-version.md
@@ -0,0 +1,70 @@
+# 如何选择合适的 RT-Thread 版本进行开发?
+
+RT-Thread 是以 Apache License v2 开源许可发布的物联网操作系统。RT-Thread 有十多年的历史,在开发过程中也放在 Github 上由大家协同开发,并发布一个个版本,导致了不同人群面对多样的版本无从下手,此文档将说明如何选择一个合适自己的版本进行开发。
+
+RT-Thread 的版本 / 分支有以下几种可供选择:
+
+开发分支(master 主分支)、长期支持分支(lts-v3.1.x 分支)、发布版本(release),**推荐用户使用已发布的版本**。
+
+## RT-Thread 分支与版本介绍
+
+**分支情况**:迄今为止,RT-Thread 已经存在的分支有:
+
+- stable-v1.2.x(已不维护)
+- stable-v2.0.x(已不维护)
+- stable-v2.1.x(已不维护)
+- stable-v3.0.x(已不维护)
+- lts-v3.1.x(长期支持、维护)
+- master(master 主分支是 RT-Thread 开发分支,一直活跃)
+
+当有较大的版本变化时(如 3.0.x 更新为 3.1.x,或者是主版本变化时),此时会在 master 分支上建立一个新分支对旧版本进行维护。
+
+**版本发布**:RT-Thread 已发布版本众多,如 3.1.1、3.1.2、4.0.0 发布版等等。新版本是基于 master 主分支或者基于正在维护的分支进行发布的。
+
+例如长期支持分支 lts-v3.1.x,最新发布版是 3.1.2,之后还可能会发布 3.1.3、3.1.4、... 等版本,但是该分支上不会发布 3.2.x 版本。
+
+例如当前 master 分支的版本是 4.0.1,最新发布版本是 4.0.0,之后也可能会发布 4.0.1、4.0.2、... 等版本。若待到有较大版本变化时,比如发布 4.1.0 时,此时就会建立 4.0.x 分支,对 4.0.x 进行维护。
+
+**分支图**:
+
+
+
+
+
+## 如何选择
+
+### 发布版本(GitHub releases)
+
+发布版本位于 [GitHub releases](https://github.com/RT-Thread/rt-thread/releases)([百度网盘快速下载链接](https://pan.baidu.com/s/1mgIAyWo)),其中包含 RT-Thread 所有的发布版本。发布版本稳定性高,推荐使用最新发布版本。最新的发布版本有两个:3.1.2 版本与 4.0.0 版本,这两个发布版本可以根据自己需求进行选择。
+
+**最新发布版本 3.1.2:**
+
+- 做产品 / 项目:适合公司做产品或者项目
+ - 若产品已经使用的是较早的发布版本,那么在维护产品时,建议仍然在旧的版本上进行维护
+ - 如果是新的产品,那么建议使用 3.1.x 最新发布版本,如 3.1.x 中最新的 3.1.2 发布版本
+- 学习 / 研究:适合新手入门学习
+
+**最新发布版本 4.0.0:**
+
+- 做产品 / 项目:适合公司做产品或者项目
+ - 4.0.0 支持 SMP,适合有多核需求的产品或项目
+- 学习 / 研究:适合新手入门学习、适合有入门经验的 RT-Thread 开发者
+
+### 开发分支(GitHub master 主分支)
+
+开发分支是 RT-Thread 团队在开发中过程中提交的代码的分支,位于 [GitHub master 分支](https://github.com/RT-Thread/rt-thread/tree/master)。该分支会一直更新迭代、优化功能,并且更新频率非常高。
+
+- 做产品 / 项目:开发中的分支不稳定,不适合做产品或者用于项目中
+- 学习 / 研究:由于更新速度快,适合有较多经验的 RT-Thread 开发者研究尝鲜
+- 代码贡献:可以提交代码或者修补 Bug,欢迎广大开发者 [为 RT-Thread 贡献代码](https://www.rt-thread.org/document/site/development-guide/github/github/),成为 RT-Thread 贡献者
+
+### 长期支持分支(GitHub lts-v3.1.x 分支)
+
+长期支持分支位于 [GitHub lts-v3.1.x 分支](https://github.com/RT-Thread/rt-thread/tree/lts-v3.1.x),是 3.1.x 版本的维护分支,主要在于修复 Bug 以及 BSP 的更新。由于从 4.0.0 开始,增加了较多特性,如 SMP、lwp等,对于 3.1.x 来说有非常大的变化,所以对 3.1.x 会做长期的支持。
+
+- 做产品 / 项目:开发中的分支不稳定,不适合做产品或者用于项目中
+- 学习 / 研究:适合有较多经验的 RT-Thread 开发者
+
+
+
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/rt-thread-version/figures/RT-Thread_tree.jpg b/rt-thread-version/rt-thread-standard/application-note/setup/rt-thread-version/figures/RT-Thread_tree.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..331dc8fb4d47c0c0af384093a67b4ed4103675a8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/rt-thread-version/figures/RT-Thread_tree.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/an0016-source-code.md b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/an0016-source-code.md
new file mode 100644
index 0000000000000000000000000000000000000000..2eb0383434c308f5d175a3b2596aa162b4d23a0b
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/an0016-source-code.md
@@ -0,0 +1,87 @@
+# RT-Thread 源码下载应用笔记 #
+
+本文将详细介绍如何获取 RT-Thread 源代码。
+
+## 简介
+
+RT-Thread 源代码托管在 GitHub,对于不熟悉 GitHub 的新手,初次接触 RT-Thread 不知道如何从 GitHub 获取 RT-Thread 源代码。本文档将一步步介绍如何从 GitHub 获取 RT-Thread 源代码。
+
+## RT-Thread 源代码下载链接 ##
+
+**前提条件:安装好 Git ,git clone 命令需要用到 Git 工具。请参考文档[Git 安装应用笔记][Git 安装应用笔记]。**
+
+在 [RT-Thread 官网](https://www.rt-thread.org/)提供了3种 RT-Thread 源代码的下载链接。
+
+
+
+1、GitHub 的下载链接:https://github.com/RT-Thread/rt-thread
+
+2、Gitee 的下载链接:https://gitee.com/rtthread/rt-thread
+
+3、百度网盘的下载链接:https://pan.baidu.com/s/1mgIAyWo#list/path=%2F
+
+百度网盘包含 RT-Thread 发布的各个版本源代码压缩包,压缩包不包括 RT-Thread 项目版本的历史记录。
+
+Gitee 的源码是 GitHub 的镜像,会及时自动和 GitHub 保持同步,从这2个地方下载源代码都有2种方式,一种是复制 RT-Thread 项目仓库地址后使用 git clone 命令下载,另外一种是直接下载 RT-Thread 项目压缩包。两者的差别是后者不包括 RT-Thread 项目版本的历史记录,而前者会在本地创建一个本地仓库,并且包含 RT-Thread 项目所有版本的历史记录,并且可以直接使用 Git 工具对这个本地仓库进行管理,**强烈建议使用 git clone 命令下载 RT-Thread 源代码**。
+
+Gitee 和 GitHub 2种下载方式如下面2张图所示,可以通过点击对应链接进入下载页面。
+
+
+
+
+
+## RT-Thread源代码下载 ##
+
+如上图所示复制 GitHub 上 RT-Thread 项目仓库地址后,打开 windows 命令行工具 或者Git Bash,切换到你想要放置源代码的目录,注意目录路径中不可以有中文字符或者空格,否则影响后续 Env 工具的使用。然后使用`git clone https://github.com/RT-Thread/rt-thread.git`命令克隆 RT-Thread 远程仓库到本地,如下图所示使用 git clone命令后将在 D盘的repository 目录下创建 rt-thread目录。
+
+
+
+由于国外服务器 GitHub 下载代码速度较慢,建议从 Gitee 下载,可以很快就下载好 RT-Thread 源代码,如上图所示复制 Gitee 上 RT-Thread 项目仓库地址后,同样使用git clone命令下载代码,只是仓库地址为 RT-Thread 项目在 Gitee 的仓库地址。使用`git clone https://gitee.com/rtthread/rt-thread.git`命令克隆 RT-Thread 远程仓库到本地。
+
+
+
+如果想要修改从 Gitee 克隆的本地 RT-Thread 仓库对应的远程仓库为 GitHub 上的仓库,可以按照下图所示修改,打开本地 RT-Thread 仓库 .git 目录里的config文件,修改 url 地址为 GitHub 上 RT-Thread 项目仓库地址。
+
+
+
+说明:RT-Thread 源代码下载包较大,因其包含有将近 90 个 BSP(实际你只需要提取自己的板卡对应的 BSP 即可),而 Git 仓库不提供单独下载某个文件,请下载源码时多一些耐心。
+
+## RT-Thread 源代码目录简介 ##
+
+RT-Thread 源代码目录结构如下图所示:
+
+
+
+| **目录名** | **描述** |
+| -------- | -------- |
+| BSP | Board Support Package(板级支持包)基于各种开发板的移植 |
+| components | RT-Thread 的各个组件代码,例如 finsh,gui 等。 |
+| documentation | 相关文档,如编码规范等 |
+| examples | 相关示例代码 |
+| include | RT-Thread 内核的头文件。 |
+| libcpu | 各类芯片的移植代码。 |
+| src | RT-Thread 内核的源文件。 |
+| tools | RT-Thread 命令构建工具的脚本文件。 |
+
+## RT-Thread BSP 介绍 ##
+
+目前 RT-Thread 已经针对将近90种开发板做好了移植,大部分 BSP 都支持 MDK﹑IAR开发环境和GCC编译器,并且已经提供了默认的 MDK 和 IAR 工程,用户可以直接基于这个工程添加自己的应用代码。 每个 BSP 的目录结构都类似,大部分 BSP 都提供一个 README.md 文件,这是一个 markdown 格式的文件,包含了对这个 BSP 的基本介绍,以及怎么样简单上手使用这个BSP,相当于是这份BSP的使用说明。大家找到对应的 BSP 以后首先应该看一下这份使用说明,才能少走弯路,快速的把RT-Thread 操作系统跑起来。下图是 stm32f4xx-HAL BSP 的目录描述。
+
+
+
+双击打开 stm32f4xx-HAL BSP 下的MDK 工程文件 project.uvprojx 。
+
+
+
+在工程主窗口的左侧 “Project” 栏里可以看到该工程的文件列表,这些文件被分别存放到如下几个组内,其他 BSP 提供的工程文件也是类似的分组方式。
+
+| 目录组 | 描述 |
+| ------------- | --------------- |
+| Drivers | 对应的目录为 stm32f4xx-HAL/drivers,它用于存放根据RT-Thread 提供的驱动框架实现的底层驱动代码。 |
+| CMSIS | 对应目录为 stm32f4xx-HAL/Libraries/CMSIS, stm32 CMSIS系统文件。 |
+| STM32F4XX_HAL_Driver | 对应的目录为 stm32f4xx-HAL/Libraries/STM32F4xx_HAL_Driver,它用于存放 STM32 的固件库文件。 |
+| Applications | 对应的目录为 stm32f4xx-HAL/applications,它用于存放用户自己的应用代码。 |
+| Kernel | 对应的目录为 rt-thread/src,它用于存放 RT-Thread 内核核心代码。 |
+| CORTEX-M4 | 对应的目录为 rt-thread/libcpu/arm,它用于存放 ARM Cortex-M4移植代码。 |
+| DeviceDrivers | 对应的目录为 rt-thread/components/drivers,它用于存放 RT-Thread 驱动框架源代码。 |
+| finsh | 对应的目录为 rt-thread/components/finsh,它用于存放 RT-Thread finsh 命令行组件。 |
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/change-url.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/change-url.png
new file mode 100644
index 0000000000000000000000000000000000000000..80250c2f4920babc17c3dd7172fc6616840e9596
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/change-url.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-clone-gitee.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-clone-gitee.png
new file mode 100644
index 0000000000000000000000000000000000000000..8a373bd1fed02010ebd0c455399de0d3282688a3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-clone-gitee.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-clone-github.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-clone-github.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3b949211e4ad316e3595c76b4b1d46d7c057635
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-clone-github.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-hub.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-hub.png
new file mode 100644
index 0000000000000000000000000000000000000000..6f45b8a4936db7144a021bc2fd06f992189dd6b1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-hub.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-order.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-order.png
new file mode 100644
index 0000000000000000000000000000000000000000..744c907ec8d2a4e382f2ca075dabef7fbbedcfbf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/git-order.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/gitee.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/gitee.png
new file mode 100644
index 0000000000000000000000000000000000000000..4f25184ebe9b667f30ffe8e933a0716c2d445d9f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/gitee.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/rt-thread-dir.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/rt-thread-dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea31e6e2ac0217a550db18e587d2c2688cbd8ddf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/rt-thread-dir.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/rt-thread-link.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/rt-thread-link.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1fa3b8e0aed1761ee30c391a319efc9e5dcfa04
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/rt-thread-link.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/stm32-1.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/stm32-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..70e30e966ffd17de0c33f95679ed4334be1a7123
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/stm32-1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/stm32f4-mdk.png b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/stm32f4-mdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..089cac1b6c6d8de704d7eefdbf33c14880631cf2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/source-code/figures/stm32f4-mdk.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/an0017-standard-project.md b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/an0017-standard-project.md
new file mode 100644
index 0000000000000000000000000000000000000000..78126c4efdbca76dc0d6a46f837e5a05602185e5
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/an0017-standard-project.md
@@ -0,0 +1,265 @@
+# 使用 Env 创建 RT-Thread 项目工程
+
+本文用来指导用户按照标准方式创建和管理 RT-Thread 工程。
+
+## 简介
+
+RT-Thread 完全开源开放,支持几十款 BSP,支持多种编译器,支持众多基础组件以及数量持续增长的软件包,然而对于工程项目开发来说,只需要支持一款或者有限几款 MCU,使用一种熟悉的 IDE 开发环境,使用有限的外设和组件,本文档旨在指导用户在全功能 RT-Thread 版本基础上,根据项目需求搭建 RT-Thread 工程框架。
+
+本文准备资料如下:
+
+* [RT-Thread 源码](https://www.rt-thread.org/page/download.html)
+
+* [Env 工具](https://www.rt-thread.org/page/download.html)
+
+* 安装好 MDK 软件或者 IAR 软件。
+
+## 标准工程管理
+
+### 选择 BSP
+
+用户获取 RT-Thread 源代码后需要根据自己手上的开发板型号找到对应的 BSP,就可以运行 BSP 提供的默认工程。大部分 BSP 都支持 MDK﹑IAR 开发环境和 GCC 编译器,并且已经提供了默认的 MDK 和 IAR 工程。
+
+本文后续章节将使用正点原子潘多拉开发板演示相关操作,该开发板对应的 BSP 为 stm32l475-atk-pandora,默认使用串口 1 作为 shell 控制台输出使用串口。
+
+### 搭建项目框架
+
+在 BSP 目录下打开 Env 工具,运行 `scons --dist` 命令。使用此命令会在该 BSP 目录下生成 dist 目录,这便是开发项目的目录结构,RT-Thread 源码位于项目文件夹内,可以随意拷贝 dist 文件夹的工程到任何目录下使用。
+
+
+
+进入dist目录下面的 stm32f4xx-HAL 工程目录,项目框架目录结构如下图所示:
+
+
+
+项目框架主要目录及文件的说明如下表所示:
+
+| 文件 / 目录 | 描述 |
+| ------------- | --------------- |
+| applications | 用户应用代码目录 |
+| drivers 或 board | RT-Thread 提供的底层驱动/板级相关的移植 |
+| Libraries | 芯片官网下载的固件库 |
+| rt-thread | RT-Thread 源代码 |
+| Kconfig | menuconfig 使用的文件 |
+| project.ewww | 用户使用的 IAR 工程文件 |
+| project.uvprojx | 用户使用的 MDK 工程文件 |
+| template. uvprojx | MDK 工程模板文件 |
+| SConscript | SCons 配置工具使用的文件 |
+| SConstruct | SCons 配置工具使用的文件 |
+| README.md | BSP 说明文件 |
+| rtconfig.h | BSP 配置头文件 |
+
+> [!NOTE]
+> 注:此命令从 RT-Thread 3.1.0 正式版才开始支持。
+
+### 修改工程模板
+
+用户一般都需要根据自己的需求对工程做一些工程配置,比如配置 MCU 型号,设置调试选项等。建议大家直接修改工程模板,这样使用 Scons 相关命令生成的新工程也都会包含对模板的修改。MDK 的模板工程为 template.uvprojx。IAR 的模板工程为 template.eww。注意:直接双击打开 IAR 工程模板修改可能会导致生成的新工程低版本 IAR 软件用不了。
+
+下图为修改 MDK 工程模板文件的芯片型号示例,选择相应 MCU 型号。
+
+
+
+下图为根据自己使用的调试工具设置对应的调试选项,相关配置修改完成后就可以关闭模板工程。
+
+
+
+> [!NOTE]
+> 注:源代码和头文件路径的添加不建议直接修改工程模板,后面章节会介绍使用 Scons 工具往工程添加源代码及头文件路径。
+
+### 配置和裁剪 RT-Thread
+
+每个 BSP 下的工程都有默认的配置,比如系统内核支持的最大线程优先级、系统时钟频率、使用的设备驱动、控制台使用的串口等。RT-Thread 操作系统具有高度的可裁剪性,用户可以根据自己的需求使用 Env 工具进行配置和裁剪。
+
+在 BSP 目录下打开 Env,然后在使用 `menuconfig` 命令打开配置界面。
+
+
+
+menuconfig 常用快捷键如图所示:
+
+
+
+### 使能在线软件包
+
+下图使能了 mqtt 相关的软件包。
+
+
+
+### 生成工程
+
+选择软件包后需要使用 `pkgs --update` 命令下载软件包,然后使用`scons --target=mdk5` 命令或者 `scons --target=iar` 命令生成 MDK 或者 IAR 工程。如果大家直接修改 MDK 工程文件 project.uvprojx 或者 IAR 的工程文件 project.ewww 添加了自己的代码,或者修改了工程的一些基本配置,生成的新工程会覆盖之前对工程文件 project 的修改。
+
+
+
+打开新生成的 MDK 工程 project.uvprojx ,可以看到我们选择的 paho mqtt 相关的软件包源文件已经被添加到了工程中。工程对应的芯片型号也是前文基于工程模板选择的芯片型号。
+
+
+
+### 验证工程
+
+编译工程,生成目标代码,然后就可以下载至开发板运行。本文使用终端软件 PuTTY 接收工程控制台对应串口 2 发送的数据,电脑右键→属性→设备管理器→端口(COM 和 LPT),即可查看串口对应的 COM 号,本文为 COM14。打开 PuTTY 按照下图配置,波特率一般配置为 115200。
+
+
+
+点击 open 打开,重启开发板后会看到 RT-Thread 的启动 logo 信息。
+
+
+
+### 添加文件到工程
+
+BSP 下的 applications 文件夹用于存放用户自己的应用代码,目前只有一个 main.c 文件。如果用户的应用代码不是很多,建议相关源文件都放在这个文件夹下面,本文在 applications 文件夹下新增了 2 个简单的文件 hello.c 和 hello.h。
+
+```c
+/* file: hello.h */
+
+#ifndef _HELLO_H_
+#define _HELLO_H_
+
+int hello_world(void);
+
+#endif /* _HELLO_H_ */
+```
+
+```c
+/* file: hello.c */
+#include
+#include
+#include
+
+int hello_world(void)
+{
+ rt_kprintf("Hello, world!\n");
+
+ return 0;
+}
+
+MSH_CMD_EXPORT(hello_world, Hello world!)
+```
+
+那么用户如何加入自己的应用程序的初始化代码呢?RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。
+
+```c
+#include
+#include
+#include "hello.h"
+
+int main(void)
+{
+ /* user app entry */
+
+ hello_world();
+
+ return 0;
+}
+```
+
+RT-Thread 还支持 finsh,提供一套供用户在命令行的操作接口,通过使用宏 `MSH_CMD_EXPORT()` 就可以新增一个自己命令,并可以在命令行端启动自己的应用代码。
+
+
+> [!NOTE]
+> 注:applications 文件夹下新增的 2 个文件需要使用 `scons --target=xxx` 命令生成新的工程,才会添加到工程项目中。每次新增文件都要重新生成工程。
+
+下图为应用程序 hello_world() 的两种调用结果。
+
+
+
+applications 文件夹下新增的文件是怎么样添加到工程中的呢?每个 BSP 的 applications 文件夹里都有一个 SConscript 文件,这些文件的功能都相似。RT-Thread 使用 SCons 构建系统,SCons 使用 SConscript 和 SConstruct 文件来组织源码结构。通常来说一个项目只有一个 SConstruct,但是会有多个 SConscript 。一般情况下,每个存放有源代码的子目录下都会放置一个 SConscript。
+
+SConscript 文件是使用 Python 语言编写,stm32f4xx-HAL BSP 里面的 applications 文件夹里的 SConscript 文件的内容如下所示,# 号后面内容为注释。
+
+```c
+Import('RTT_ROOT')
+Import('rtconfig')
+from building import *
+
+# str(Dir('#') 表示获取工程的 SConstruct 所在的目录, 也就是 D:\repository\rt-thread\bsp\stm32f4xx-HAL
+# os.path.join() 表示将 `str(Dir('#')` 表示的目录和 applications 一起拼接成一个完整目录。
+# 也就是获得当前目录 `D:\repository\rt-thread\bsp\stm32f4xx-HAL\applications`
+cwd = os.path.join(str(Dir('#')), 'applications')
+
+# 获取当前目录下所有的 C 文件
+src = Glob('*.c')
+
+# 将当前目录和 SConstruct 所在的目录保存到 list 变量 CPPPATH 中
+CPPPATH = [cwd, str(Dir('#'))]
+
+# 将 src 包含的源文件创建为 Applications 组。depend 为空表示该组不依赖 rtconfig.h 中的任何宏。
+# CPPPATH = CPPPATH 表示将右边 CPPPATH 变量包含的目录添加到系统的头文件路径中,左边的 CPPPATH 表示头文件路径。
+# 这个 Applications 组也就对应 MDK 或者 IAR 工程里面的 Applications 分组。
+group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
+```
+
+通过这个 SConscript 文件,applications 目录下的所有源文件和相关头文件目录就添加到了工程中,而不需要用户手动添加。
+
+### 添加模块到工程
+
+前文提到在自己源代码文件不多的情况下,建议所有源代码文件都放在 applications 文件夹里面。如果用户源代码很多了,并且想创建自己的工程模块,或者需要使用自己获取的其他模块,怎么做会比较合适呢?前文对 applications 文件夹里的 SConscript 文件做了简单介绍,这一小节将简单演示怎么实现自己的 SConscript 文件来管理工程文件。
+
+#### 添加源代码
+
+同样以上文提到的 hello.c 和 hello.h 为例,需要把他们作为一个单独的模块管理,并且在 MDK 或者 IAR 的工程文件里有自己的分组,且可以通过 menuconfig 选择是否使用这个模块。在 BSP 下新增 hello 文件夹。
+
+
+
+#### 编写 SConscript 文件
+
+大家注意到文件夹里多了一个 SConscript 文件,如果想要将自己的一些源代码加入到 SCons 编译环境中,一般可以创建或修改已有 SConscript 文件。SConscript 文件可以控制源码文件的加入,并且可以指定文件的 Group(与 MDK/IAR 等 IDE 中的 Group 的概念类似)。对于这个新增的 hello 模块 SConscript 文件内容如下,# 号后面的内容为注释。
+
+```c
+# -*- coding: UTF-8 -*- # 指定文件的编码格式是 UTF-8,文件可以用中文
+Import('RTT_ROOT')
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir() # 将当前路径赋值给字符串 cwd
+include_path = [cwd] # 将当前头文件路径保存到 list 变量 include_path 中
+src = [] # 定义一个 list 变量 src
+
+if GetDepend(['RT_USING_HELLO']): # hello.c 依赖宏 RT_USING_HELLO
+ src += ['hello.c'] # 保存 hello.c 字符串到 list 变量 src 中
+
+# 将 src 包含的源文件创建为 hello 组。depend 为空表示该组不依赖 rtconfig.h 的任何宏。
+# CPPPATH = include_path 表示将当前目录添加到系统的头文件路径中
+group = DefineGroup('hello', src, depend = [''], CPPPATH = include_path)
+
+Return('group')
+```
+
+通过上面几行简单的 python 代码就创建了一个新的 hello 分组,并且可以通过宏定义控制要加入到组里面的源文件。关于 SCons 的更多使用可以参考 [Scons 官方文档](https://scons.org/documentation.html) 或者参考 RT-Thread 提供的 [Scons 构建工具手册][Scons 构建工具手册]。
+
+#### 增加 menuconfig 菜单
+
+那么自定义宏 RT_USING_HELLO 又是通过怎样的方式定义呢?这里要介绍一个新的文件 Kconfig 。Kconfig 用来配置内核,menuconfig 命令通过读取工程的各个 Kconfig 文件,生成配置界面供用户配置内核 ,最后所有配置相关的宏定义都会自动保存到 BSP 目录里的 rtconfig.h 文件中,每一个 BSP 都有一个 rtconfig.h 文件,也就是这个 BSP 的配置信息。
+
+在潘多拉 stm32l475-atk-pandora BSP 目录下已经有了关于这个 BSP 的 Kconfig 文件,如果用户的 BSP 没有则可以自己新建一个。我们可以基于这个文件添加自己需要的配置选项。关于 hello 模块添加了如下配置选项,# 号后面为注释。
+
+
+
+使用 Env 工具进入 stm32l475-atk-pandora BSP 目录后,使用 menuconfig 命令在主页面最下面就可以看到新增的 hello 模块的配置菜单,进入菜单后如下图显示。
+
+
+
+我们还可以修改 hello value 的值。
+
+
+
+保存配置后退出配置界面,打开 stm32l475-atk-pandora BSP 目录下的 rtconfig.h 文件可以看到 hello 模块的配置信息已经有了。
+
+
+
+> [!NOTE]
+> 注:每次 menuconfig 配置完成后要使用 `scons --target=XXX` 生成新工程。
+
+
+
+打开新生成的 MDK5 工程文件 project.uvprojx 后可以看到新增一个 hello 分组,以及此分组下包含的 hello.c 文件。
+
+
+
+## 参考资料
+
+* [《Env 用户手册》](../../../programming-manual/env/env.md)
+
+* [《Scons 构建工具》](../../../programming-manual/scons/scons.md)
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/add-hello.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/add-hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..e0606276a255fc822d0a94b4f86c082c7447ba42
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/add-hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/debug.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/debug.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e3b16503f00dd6aac6d9af125cec313fe422b63
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/debug.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/dist-dir.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/dist-dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..439b71086e90d15bd32d2deb60ec0b497ab6766f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/dist-dir.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/env-setup.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/env-setup.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f51eb83efd61bc2b04d7e61f0fab2ce2d4bd6b9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/env-setup.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/env.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/env.png
new file mode 100644
index 0000000000000000000000000000000000000000..e165d44c64728c0d4e1be38f846643cdbddb1d35
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/env.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-mdk.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-mdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..4531d5011283035ca4832f7f0d04fbb46321c930
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-mdk.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-menu.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1238809314a1fa21edd5c75c0837ccf04625b1b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-menu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-module.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-module.png
new file mode 100644
index 0000000000000000000000000000000000000000..79952ada81718f4599979a667884acc4c4dc0d3e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-module.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-rtconfig.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-rtconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..e67bf04fae39c391e083ca0ed16e992eecbbeee4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-rtconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-scons.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-scons.png
new file mode 100644
index 0000000000000000000000000000000000000000..b768e346fc266a88166a875cd286da5320baff8b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-scons.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-value.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-value.png
new file mode 100644
index 0000000000000000000000000000000000000000..40f592af35bc80eca619aec7c2f714319b813dff
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello-value.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..f94bd4a8aad5a9b65d08aa010f9f3c2abf237175
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/kconfig-hello.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/kconfig-hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..01862e13da3180e96dc14b415cd67a69c5738b43
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/kconfig-hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..f46239c73ba54e39ea4b4f83a082d6ca7232ee4c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/mqtt.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/mqtt.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c536291ee1b58a383f334fee3451dc094bd4596
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/mqtt.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/new-mdk5.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/new-mdk5.png
new file mode 100644
index 0000000000000000000000000000000000000000..488f14c0f1d2eacefe4b4e6d7ad314fb57232b9d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/new-mdk5.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/putty-setup.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/putty-setup.png
new file mode 100644
index 0000000000000000000000000000000000000000..4206760e7fe6091efcccbc2da3e0abaf4e021f61
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/putty-setup.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/rtt-logo.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/rtt-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..98d48658337b528d319f7bdf2b7ac5b4f0b09183
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/rtt-logo.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-dist.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-dist.png
new file mode 100644
index 0000000000000000000000000000000000000000..f56da4dbbe521660b4e01c251a019733ae2d0cff
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-dist.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-mdk5.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-mdk5.png
new file mode 100644
index 0000000000000000000000000000000000000000..abfb5bb0179f84a1359cbf6014ba51ab33967e79
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-mdk5.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-target.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-target.png
new file mode 100644
index 0000000000000000000000000000000000000000..0fb9edf1c3e6538b7e257f801dba086a527ed9d7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/scons-target.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/stm32-mcu.png b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/stm32-mcu.png
new file mode 100644
index 0000000000000000000000000000000000000000..56a0ae59abdf230e0bf3e32ac22580e0cb5fb6fc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/setup/standard-project/figures/stm32-mcu.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/init/an0007-rtthread-system-component-init.md b/rt-thread-version/rt-thread-standard/application-note/system/init/an0007-rtthread-system-component-init.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/libc/an0008-rtthread-system-libc.md b/rt-thread-version/rt-thread-standard/application-note/system/libc/an0008-rtthread-system-libc.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/an0049-optimize-code-size.md b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/an0049-optimize-code-size.md
new file mode 100644
index 0000000000000000000000000000000000000000..2c3f1e04c1ce5126ad8a8fdcbecf42718b4f9de6
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/an0049-optimize-code-size.md
@@ -0,0 +1,306 @@
+# 固件尺寸优化
+
+## 前言
+
+使用 RT-Thread-Studio 进行工程构建时,为了实现业务需求,我们常常会增加驱动文件、组件或者软件包等等,并且在调试代码时也可能需要使能调试相关的功能(例如打开 ulog 功能 )或者自行打印一些调试的信息。因此,我们会编译得到一个稍微冗余的固件。对于 MCU 的 Flash 比较紧张时,我们需要考虑代码体积的优化,使其尽量精简,这样的代码在之后的迭代开发中才可以实现小而美的目标。
+
+下面是几个可以去考虑的优化的方向:
+
+* 裁剪
+* 选择合适的优化等级
+* 开启 newlib-nano 选项
+* 使用 Map File 分析工具
+* readelf 命令分析 ELF 文件
+* 更换 libc 库
+* 更换同类型 Flash 较大的硬件平台
+
+需要注意的是,并不是所有的优化都是行之有效的,如果收效甚微的优化却造成了系统性能的大幅衰减,这是非常不可取的,所以优化的时候要认真分析,综合考虑,不可能一蹴而就。
+
+## 1. 裁剪
+
+裁剪是优先需要考虑的方向,这种方式操作简单,也最为见效。
+
+以下是基于 stm32l475-atk-pandora BSP 进行裁剪的例子,该示例使用 RT-Thread 4.0.3 版本,优化等级 -O0。
+
+MCU:STM32L475VET6,512KB FLASH ,128KB RAM
+
+在做了一系列配置之后(模拟项目工程),该 BSP 现已有资源为:
+
+- 内核:信号量、互斥量、事件集、邮箱、消息队列;main 线程、tshell 线程、idle 线程
+- 组件:文件系统(fatfs)、Finsh 组件、UART 框架、GPIO 框架、SFUD 组件、QSPI 框架
+- 外设驱动:片上 UART、GPIO、QSPI、板上 QSPI FLASH、NRF24L01、LCD、PWM、ADC等
+
+其中关系为:
+
+1. main 中的 led 闪烁:PIN 驱动、PIN 框架
+2. FinSH 控制台:使用 UART 驱动、UART 框架、FinSH 组件
+3. 文件系统(板上 QSPI FLASH):使用 QSPI 驱动、QSPI 框架、文件系统组件 Fatfs、FAL 软件包
+4. Ulog:ulog 组件
+5. 其他硬件板载设备驱动:LCD、TIMER、PWM、ADC、RTC、Audio
+
+利用 RT-Thread Setting 的图形界面,我们可以比较直观的看到使能了哪些软件包、驱动和组件。
+
+
+当前系统体积大小如下所示:
+
+```information
+ text data bss dec hex filename
+ 260932 1648 5388 267968 416c0 rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 262580 B 256.43 KB
+RAM: 7036 B 6.87 KB
+```
+
+可以看到**当前系统体积大小 256KB**,下面对该BSP进行裁剪,打开工程的 RT-Thread Settings 配置界面:
+
+### 裁剪 Ulog 组件(-3.8KB)
+
+
+
+去除异步日志功能后
+
+
+
+```information
+ text data bss dec hex filename
+ 257000 1648 5120 263768 40658 rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 258648 B 252.59 KB
+RAM: 6768 B 6.61 KB
+```
+
+### 裁剪文件系统及 Flash 设备(-83.2KB)
+
+由于系统使能了 FAL 软件包,如下图
+
+
+
+关闭 QSPI Flash 设备,在Hardware选项中,将已经适配好的 QSPI FLASH相关设备除能。
+
+
+
+**由于系统不再使用 QSPI 设备,那么相对应的 QSPI设备驱动框架,也是可以取消掉的。这点在裁剪系统时候很重要,因为我们开发中经常 使能/除能 一些总线上的设备,却常常忘记关 总线/设备驱动框架 造成系统体积上的损耗。**
+
+
+
+最后将虚拟文件系统 DFS 关闭。
+
+
+
+```
+ text data bss dec hex filename
+ 172148 1308 3556 177012 2b374 rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 173456 B 169.39 KB
+RAM: 4864 B 4.75 KB
+```
+
+### 裁剪外设驱动(-101.8KB)
+
+关闭 LCD、Audio 设备(**由于SPI无其他设备挂载,因为可以裁剪掉SPI BUS**)( -75KB)
+
+
+
+```information
+ text data bss dec hex filename
+ 95204 1260 2864 99328 18400 rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 96464 B 94.20 KB
+RAM: 4124 B 4.03 KB
+```
+
+裁剪掉 TIMER、PWM、ADC、RTC 外设驱动 (-26.6KB)
+
+
+
+```information
+ text data bss dec hex filename
+ 68856 384 2524 71764 11854 rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 69240 B 67.62 KB
+RAM: 2908 B 2.84 KB
+```
+
+### 裁剪 FinSH(-13K)
+
+
+
+```information
+ text data bss dec hex filename
+ 55500 384 2240 58124 e30c rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 55884 B 54.57 KB
+RAM: 2624 B 2.56 KB
+```
+
+### 裁剪内核 IPC(体积几乎不变)
+
+关闭事件集、邮箱、消息队列
+
+
+
+```information
+ text data bss dec hex filename
+ 54888 336 2232 57456 e070 rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 55224 B 53.93 KB
+RAM: 2568 B 2.51 KB
+```
+
+### 检查 RT-Thread Setting 和 rtconfig.h 配置文件
+
+经过以上的裁剪步骤,差不多裁剪了十之八九了,接下来就要检查还有什么地方在裁剪的过程中被忽略了,然后再按照上面的步骤做深入的裁剪,在此不再一一演示,仅作展示说明为主。
+
+打开 RT-Thread Setting 图形化界面如下图所示:
+
+
+
+可以看到目前系统有使用到 libc 组件、Serial 驱动、Pin 设备驱动、Soft I2C 驱动。例如现在除能 libc 组件,直接单击 libc 按钮去除使能即可。
+
+另外,我们也可以根据 rtconfig.h 文件查看各个宏定义信息,避免遗漏。下面是 rtcopnfig.h 的部分配置信息。
+
+```c
+#ifndef RT_CONFIG_H__
+#define RT_CONFIG_H__
+
+/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */
+
+/* RT-Thread Kernel */
+
+#define RT_NAME_MAX 8
+#define RT_ALIGN_SIZE 4
+#define RT_THREAD_PRIORITY_32
+#define RT_THREAD_PRIORITY_MAX 32
+#define RT_TICK_PER_SECOND 1000
+#define RT_USING_OVERFLOW_CHECK
+#define RT_USING_HOOK
+#define RT_USING_IDLE_HOOK
+#define RT_IDLE_HOOK_LIST_SIZE 4
+#define IDLE_THREAD_STACK_SIZE 256
+#define RT_DEBUG //DEBUG相关还可以再优化掉
+#define RT_DEBUG_COLOR //DEBUG相关还可以再优化掉
+
+/* Inter-Thread communication */
+
+#define RT_USING_SEMAPHORE
+#define RT_USING_MUTEX
+/* end of Inter-Thread communication */
+
+/* Memory Management */
+
+#define RT_USING_MEMPOOL //内存池还可以再优化掉
+#define RT_USING_SMALL_MEM
+#define RT_USING_HEAP
+/* end of Memory Management */
+
+/* Kernel Device Object */
+
+#define RT_USING_DEVICE
+#define RT_USING_CONSOLE
+#define RT_CONSOLEBUF_SIZE 256
+#define RT_CONSOLE_DEVICE_NAME "uart1"
+/* end of Kernel Device Object */
+#define RT_VER_NUM 0x40003
+/* end of RT-Thread Kernel */
+#define ARCH_ARM
+#define RT_USING_CPU_FFS
+#define ARCH_ARM_CORTEX_M
+#define ARCH_ARM_CORTEX_M4
+
+#endif
+```
+
+## 2.选择合适的优化等级
+
+RT-Thread-Studio 使用的是 GCC 编译器,GCC 编译器对代码的编译优化有一系列的配置项,大体分为五个优化等级:-O0、-O1、-O2、-O3 和 -Os。
+
+-O0:关闭所有优化选项,是 GCC 默认的等级,目的是让编译器减少编译时间并使调试产生预期的结果。在 RT-Thread-Studio 中,默认也是配置的该选项,如果编译的代码尺寸较大,我们建议更换优化等级(一般我们会选择 O2 等级)。
+
+-O1:这是最基本的优化等级。编译器会在不花费太多编译时间的同时试图生成更快更小的代码。这些优化是非常基础的,但一般这些任务肯定能顺利完成。
+
+-O2:O1 的进阶。这是推荐的优化等级,除非你有特殊的需求。O2 会比 O1 启用更多的优化选项。当设置了 O2 等级后,编译器会试图增加编译的时间和提升生成代码的性能(我们一般选用此优化等级完成编译任务)。
+
+-O3:这是最高的优化等级,O3 开启了 O2 指定的所有优化,并启用了更多的优化选项。例如构建用于保存变量的伪寄存器网络(使得调试更加困难)、优化循环执行过程等。开启 O3 优化不一定会减少代码尺寸,有可能会为了减少代码执行时间反而增加代码体积。一般我们不使用此优化等级。
+
+-Os:该这个等级用来优化代码尺寸。其中启用了 O2 中不增加目标文件大小的优化选项。这对于磁盘空间极其紧张或者 CPU 缓存较小的机器非常有用。一般使用 O2 等级之后发现生成的目标文件尺寸偏大,可以尝试使用 Os 等级进一步的优化。下表是[GCC 优化等级列表](https://www.rapidtables.com/code/linux/gcc/gcc-o.html#optimization)。
+
+### gcc -O option flag
+
+Set the compiler's optimization level.
+
+| option | optimization level | execution time | code size | memory usage | compile time |
+| :-------: | :------------------------------------------------: | :------------: | :-------: | :----------: | :----------: |
+| -O0 | optimization for compilation time (default) | + | + | - | - |
+| -O1 or -O | optimization for code size and execution time | - | - | + | + |
+| -O2 | optimization more for code size and execution time | -- | | + | ++ |
+| -O3 | optimization more for code size and execution time | --- | | + | +++ |
+| -Os | optimization for code size | | -- | | ++ |
+| -Ofast | O3 with fast none accurate math calculations | --- | | + | +++ |
+
++increase ++increase more +++increase even more -reduce --reduce more ---reduce even more
+
+RT-Thread-Studio 默认选择的是 -O0(关闭所有优化)等级,按照上一章节,系统最后的裁剪的尺寸为 53.93KB,下面开启 O2 优化等级,代码尺寸缩小为 38.14 KB:
+
+
+
+```information
+ text data bss dec hex filename
+ 38724 336 2232 41292 a14c rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 39060 B 38.14 KB
+RAM: 2568 B 2.51 KB
+```
+
+下面开启 -Os 优化等级,代码尺寸缩小为 34.64 KB:
+
+
+
+```information
+ text data bss dec hex filename
+ 35140 336 2232 37708 934c rtthread.elf
+ Used Size(B) Used Size(KB)
+Flash: 35476 B 34.64 KB
+RAM: 2568 B 2.51 KB
+```
+
+## 3.开启 newlib-nano 选项
+
+RT-Thread-Studio 默认使用的 libc,提供了 printf、scanf 等很多标准库函数,但是这些库函数相对都比较大(在嵌入式平台上),而且很可能一些复杂的功能,我们在项目中并没有使用到,这样会造成代码体积的增大。
+
+因此 newlib 提供了一个精简功能的版本,将一些标准库函数进行简化,仅仅实现一些简单常用的功能,这样便可以使得编译的代码轻量化,更适合嵌入式平台使用。(但是如果我们使用了标准库的一些复杂的功能,而 newlib-nano 并没有完备的实现这些功能,那么可能会造成一些意外的运行结果,我们在使用时要注意这些。)
+
+如下图所示,我们在RT-Thread-Studio 中便可以开启该选项。
+
+
+
+另外,开启 newlib-nano 时,对于 printf 和 scanf 等的使用是默认不带浮点运算的,如果使用浮点的话,则需要开启对应选项,如上图中 `Use float with nano printf` 和 `Use float with nano scanf` 选项框。
+
+## 4.对 Map File 进行分析优化
+
+在进行裁剪之后,我们还可以使用 Amap.exe 工具(( map 文件分析工具)[http://www.sikorskiy.net/prj/amap/])
+
+使用该工具只是辅助性的分析函数调用所占字段大小,从而针对各个组件和函数进行优化裁剪等。
+
+
+
+## 5.使用 readelf 命令分析 ELF文件
+
+与 Amap 工具类似,我们也可以使用 readelf 命令分析系统生成的 elf 文件。详细命令介绍见 [readelf - Linux man page](https://linux.die.net/man/1/readelf),或者直接 `readelf --help`查看用法。
+
+使用 `readelf -all rtthread.elf` 可以查看 elf 的所有信息。
+
+
+
+依据生成的符号表 ( Symbol table ) ,可以看到生成的字段信息,例如类型为 `GLOBAL` 代表全局符号,`OBJECT` 代表数据对象,比如变量数组,`FUNC` 代表函数等等。我们可以利用这些这些信息,分析具体的段对应的大小。
+
+
+
+## 6.更换 libc 库
+
+目前 RT-Thread-Studio 在使用 libc 时,默认使用的是 newlib,也有 minilibc 库支持,这个主要是提供给 gcc 编译器的,minilibc 可以不需要再链接 GCC 自带的 libc 库。newlib 则是用于链接到 GCC 自带的 libc 库。newlib 提供的底层c库接口相对 minilibc 库更全面,而 minilibc 库在实现上可以使得代码体积更小。
+
+如果我们项目上需要用到 C 库时,可以按照具体需求选择更换 C 库,甚至有能力的开发者可以自行设计优化 C 库代码使得编译尺寸减小的同时,又不会造成性能上的损失。
+
+## 7.更换同类型 Flash
+
+最后还要提一点的是,当系统经过优化后仍然无法满足需求,如果有必要的话,建议更换成同类型 Flash 较大的硬件平台,这样可以在软件和硬件完全不需要修改的情况下完成项目功能,达到预期目标。
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Amap.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Amap.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1206747964176eac45f49106cf1d4546594804d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Amap.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Optimazation1.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Optimazation1.png
new file mode 100644
index 0000000000000000000000000000000000000000..d413b2856c5e926904f27338e29956a317b57b4e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Optimazation1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Optimazation2.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Optimazation2.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0ac6c2bd7b4a3ed6e217f130380877a5c9a39ce
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/Optimazation2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/QspiFlash.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/QspiFlash.png
new file mode 100644
index 0000000000000000000000000000000000000000..800e4ce8021f3a0bf1ea0cf601e02fbede70108e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/QspiFlash.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/RT-Thread Setting_01.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/RT-Thread Setting_01.png
new file mode 100644
index 0000000000000000000000000000000000000000..d4c4530d7fe55d13295c2104f1deed9639fa1c6a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/RT-Thread Setting_01.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/RT-Thread Setting_02.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/RT-Thread Setting_02.png
new file mode 100644
index 0000000000000000000000000000000000000000..12e01924697fc2125eb3228158f69f7f9455f3e0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/RT-Thread Setting_02.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-dfs.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-dfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..39da51232ae11b2507385bd3c011e94ea481b4c3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-dfs.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-finsh.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-finsh.png
new file mode 100644
index 0000000000000000000000000000000000000000..a0ae3822f0d813886207a41cd3d4efcb98022edb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-finsh.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-kernel-ipc.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-kernel-ipc.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e98c4c10a4e53227bda109b28e0f09446f69cb5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-kernel-ipc.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-ulog-async.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-ulog-async.png
new file mode 100644
index 0000000000000000000000000000000000000000..c339352398fd42d06d5defbf8a9b273225b7d389
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-ulog-async.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-ulog.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-ulog.png
new file mode 100644
index 0000000000000000000000000000000000000000..986589d55e0a86517569dd5163a25aeef007a13e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable-ulog.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_hd_drivers.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_hd_drivers.png
new file mode 100644
index 0000000000000000000000000000000000000000..d8f5c7acf831cad3fd73b9698e2a8e573599681a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_hd_drivers.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_hd_drivers2.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_hd_drivers2.png
new file mode 100644
index 0000000000000000000000000000000000000000..49f1b91ca5d985ca68ce9aacd3a6230ca77687ad
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_hd_drivers2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_qspi_drivers.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_qspi_drivers.png
new file mode 100644
index 0000000000000000000000000000000000000000..92398c72746437e489f36f3883f9237aed3481cb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/disable_qspi_drivers.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/fal.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/fal.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a38ab6f2edd998bb5e0af7d3981f579e9b10e39
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/fal.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/newlib_nano.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/newlib_nano.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d33573ae3b7d246e1d7f14b2b3a3fc210ea7ce6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/newlib_nano.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/readelf.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/readelf.png
new file mode 100644
index 0000000000000000000000000000000000000000..79edb37fdcbd5724b233a2d06b214a531ebec0a1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/readelf.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/readelf1.png b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/readelf1.png
new file mode 100644
index 0000000000000000000000000000000000000000..07a34e3b1590d6d7146c7b3814897be636abe00c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/optimization/Optimize-code-size/figures/readelf1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/an0025-pm.md b/rt-thread-version/rt-thread-standard/application-note/system/pm/an0025-pm.md
new file mode 100644
index 0000000000000000000000000000000000000000..43955e57c7e05552a23e51a43dfcc7fa3abc719c
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/system/pm/an0025-pm.md
@@ -0,0 +1,469 @@
+# 在 RT-Thread 潘多拉开发板上实现电源管理
+
+本文介绍了基于 RT-Thread潘多拉开发板电源管理组件的使用和移植过程。
+
+## 简介
+
+随着物联网(IoT)的兴起,产品对功耗的需求越来越强烈。作为数据采集的传感器节点通常需要在电池供电时长期工作,而作为联网的SOC也需要有快速的响应功能和较低的功耗。
+
+在产品开发的起始阶段,首先考虑是尽快完成产品的功能开发。在产品功能逐步完善之后,就需要加入电源管理功能。为了适应IoT的这种需求,RT-Thread提供了电源管理框架。电源管理框架的理念是尽量透明,使得产品加入低功耗功能更加轻松。
+
+本文的示例都是在潘多拉开发板下运行。潘多拉开发板是 RT-Thread 和正点原子联合推出的硬件平台,该平台上专门为 IoT 领域设计,并提供了丰富的例程和文档。
+
+MCU通常提供了多种时钟源供用户选择。例如潘多拉开发板上板载的 STM32L475 就可以选择 LSI/MSI/HSI 等内部时钟,还可以选择 HSE/LSE 等外部时钟。MCU 内通常也集成了 PLL(Phase-locked loops),基于不同的时钟源,向 MCU 的其他模块提供更高频率的时钟。
+
+为了支持低功耗功能,MCU 里也会提供不同的休眠模式。例如 STM32L475 里,可以分成 SLEEP模式、STOP模式、STANDBY模式。这些模式还可以有进一步的细分,以适应不同的场合。
+
+本节主要展示了如何开启 PM 组件和相应的驱动,并通过例程来演示常见场景下,应用应该如何管理模式。
+
+最后,本节将介绍 RT-Thread PM 组件在 STM32L476 上的移植和注意事项。
+
+## 配置工程
+
+在潘多拉开发板上运行电源管理组件,需要下载潘多拉开发板的相关资料、RT-Thread 源码和 ENV 工具。
+
+* [RT-Thread 源码](https://github.com/RT-Thread/rt-thread)
+* [ENV 工具](https://www.rt-thread.org/page/download.html)
+
+开启 Env 工具,进入潘多拉开发板的 BSP 目录(`rt-thread\bsp\stm32\stm32l475-atk-pandora`),在 Env 命令行里输入 `menuconfig` 进入配置界面配置工程。
+
+- 配置 PM 组件:勾选 BSP 里面的`RT-Thread Components ---> Device Drivers ---> [*] Using Power Management device drivers`:
+
+ 
+
+- 配置内核选项:使用 PM 组件需要更大的 IDLE 线程的栈,这里使用了1024 字节。例程里还使用 Software timer,所以我们还需要开启相应的配置
+
+ 
+
+- 配置完成,保存并退出配置选项,输入命令`scons --target=mdk5`生成 mdk5 工程;
+
+打开mdk5 工程可以看到相应的源码以及被添加进来:
+
+
+
+## 使用
+### 定时应用
+
+在定时应用里,我们创建了一个周期性的软件定时器,定时器任务里周期性输出当前的 OS Tick。如果创建软件定时器成功之后,使用`rt_pm_request(PM_SLEEP_MODE_DEEP)`请求深度睡眠模式。以下是示例核心代码(该代码可以直接复制 main.c 里运行):
+
+```c
+#include
+#include
+#include
+
+#ifndef RT_USING_TIMER_SOFT
+ #error "Please enable soft timer feature!"
+#endif
+
+#define TIMER_APP_DEFAULT_TICK (RT_TICK_PER_SECOND * 2)
+
+#ifdef RT_USING_PM
+
+static rt_timer_t timer1;
+
+static void _timeout_entry(void *parameter)
+{
+ rt_kprintf("current tick: %ld\n", rt_tick_get());
+}
+
+static int timer_app_init(void)
+{
+ rt_pm_request(PM_SLEEP_MODE_IDLE);
+ rt_pm_request(PM_SLEEP_MODE_LIGHT);
+
+ timer1 = rt_timer_create("timer_app",
+ _timeout_entry,
+ RT_NULL,
+ TIMER_APP_DEFAULT_TICK,
+ RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
+ if (timer1 != RT_NULL)
+ {
+ rt_timer_start(timer1);
+
+ /* keep in timer mode */
+ rt_pm_request(PM_SLEEP_MODE_DEEP);
+
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+INIT_APP_EXPORT(timer_app_init);
+
+#endif /* RT_USING_PM */
+```
+
+按下复位按键重启开发板,打开终端软件,我们可以看到有定时输出日志:
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.1 build May 9 2019
+ 2006 - 2019 Copyright by rt-thread team
+msh >current tick: 2001
+current tick: 4002
+current tick: 6003
+current tick: 8004
+```
+
+我们可以在msh里输入`pm_dump`命令观察PM组件的模式状态:
+
+```shell
+pm_dump
+| Power Management Mode | Counter | Timer |
++-----------------------+---------+-------+
+| None Mode | 0 | 0 |
+| Idle Mode | 1 | 0 |
+| LightSleep Mode | 1 | 0 |
+| DeepSleep Mode | 1 | 1 |
+| Standby Mode | 0 | 0 |
+| Shutdown Mode | 0 | 0 |
++-----------------------+---------+-------+
+pm current sleep mode: Idle Mode
+pm current run mode: Normal Speed
+msh >
+```
+
+以上的输出说明,PM 组件里 Idle、Light Sleep、Deep Sleep 都被请求了一次,现在正处于空闲模式(Idle Mode)。
+
+我们依次输入命令`pm_release 1`和`pm_release 2` 手动释放 Idle 和 Light Sleep 模式后,将进入`Deep Sleep Mode`。进入`Deep Sleep Mode`之后会定时唤醒,shell 还是一直在输出:
+
+```shell
+msh />pm_release 1
+msh />
+msh />current tick: 8023
+current tick: 10024
+current tick: 12025
+
+msh />pm_release 2
+msh />
+msh />current tick: 14026
+current tick: 16027
+current tick: 18028
+current tick: 20029
+current tick: 22030
+current tick: 24031
+
+```
+
+我们可以通过功耗仪器观察功耗的变化。下图是基于 Monsoon Solutions Inc 的 Power Monitor 的运行截图,可以看到随着模式变化,功耗明显变化:
+
+
+
+休眠时显示2mA是仪器的误差。
+
+### 按键唤醒应用
+
+在按键唤醒应用里,我们使用 wakeup 按键来唤醒处于休眠模式的 MCU。一般情况下,在 MCU 处于比较深度的休眠模式,只能通过特定的方式唤醒。MCU 被唤醒之后,会触发相应的中断。以下例程是从 Deep Sleep 模式唤醒 MCU 并闪烁 LED 之后,再次进入休眠的例程。以下是核心代码(该代码可以直接复制 main.c 里运行):
+
+```c
+#include
+#include
+#include
+
+#ifdef RT_USING_PM
+
+#define WAKEUP_EVENT_BUTTON (1 << 0)
+#define PIN_LED_R GET_PIN(E, 7)
+#define WAKEUP_PIN GET_PIN(C, 13)
+#define WAKEUP_APP_THREAD_STACK_SIZE 1024
+
+static rt_event_t wakeup_event;
+
+static void wakeup_callback(void *args)
+{
+ rt_event_send(wakeup_event, WAKEUP_EVENT_BUTTON);
+}
+
+static void wakeup_init(void)
+{
+ rt_pin_mode(WAKEUP_PIN, PIN_MODE_INPUT_PULLUP);
+ rt_pin_attach_irq(WAKEUP_PIN, PIN_IRQ_MODE_FALLING, wakeup_callback, RT_NULL);
+ rt_pin_irq_enable(WAKEUP_PIN, 1);
+}
+
+static void wakeup_app_entry(void *parameter)
+{
+ wakeup_init();
+ rt_pm_request(PM_SLEEP_MODE_DEEP);
+
+ while (1)
+ {
+ if (rt_event_recv(wakeup_event,
+ WAKEUP_EVENT_BUTTON,
+ RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER, RT_NULL) == RT_EOK)
+ {
+ rt_pm_request(PM_SLEEP_MODE_NONE);
+
+ rt_pin_mode(PIN_LED_R, PIN_MODE_OUTPUT);
+ rt_pin_write(PIN_LED_R, 0);
+ rt_thread_delay(rt_tick_from_millisecond(500));
+ rt_pin_write(PIN_LED_R, 1);
+
+ rt_pm_release(PM_SLEEP_MODE_NONE);
+ }
+ }
+}
+
+static int wakeup_app(void)
+{
+ rt_thread_t tid;
+
+ wakeup_event = rt_event_create("wakup", RT_IPC_FLAG_FIFO);
+ RT_ASSERT(wakeup_event != RT_NULL);
+
+ tid = rt_thread_create("wakeup_app", wakeup_app_entry, RT_NULL,
+ WAKEUP_APP_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
+ RT_ASSERT(tid != RT_NULL);
+
+ rt_thread_startup(tid);
+
+ return 0;
+}
+INIT_APP_EXPORT(wakeup_app);
+
+#endif
+```
+
+上面的代码里,我们创建一个线程,这个线程里注册了按键中断唤醒回调函数,接着请求深度睡眠模式,每当唤醒中断之后就会触发回调。回调函数里会发送事件`WAKEUP_EVENT_BUTTON`。这样我们的线程里接收到这个事件之后,首先请求在 None 模式,然后完成 LED 闪烁功能之后,再去释放 None 。
+
+
+
+上图是我们三次按下 wakeup 按键的运行截图。每次按下按键,MCU 都会被唤醒点亮 LED 2秒之后,再次进入休眠。
+
+## STM32L4 移植 PM
+
+### STM32L4 的低功耗模式简介
+
+STM32L476 是 ST 公司推出的一款超低功耗的 Crotex-M4 内核的 MCU,支持多个电源管理模式,其中最低功耗 Shutdown 模式下,待机电流仅 30 nA。ST 公司 把 L476 的电管管理分为很多种,但各个模式的并非功耗逐级递减的特点,下面是各个模式之间的状态转换图:
+
+
+
+尽管 STM32L476 的低功耗模式很多,但本质上并不复杂,理解它的原理有助于我们移植驱动,同时更好的在产品中选择合适的模式。
+
+最终决定 STM32L476 系统功耗的主要是三个因素:稳压器(voltage regulator)、CPU 工作频率、芯片自身低功耗的处理,下面分别对三个因素进行阐述。
+
+- 稳压器
+
+L4 使用两个嵌入式线性稳压器为所有数字电路、待机电路以及备份时钟域供电,分别是主稳压器(main regulator,下文简称 MR)和低功耗稳压器(low-power
+regulator,下文简称 LPR)。稳压器在复位后处于使能状态,根据应用模式,选择不同的稳压器对 Vcore 域供电。其中,MR 的输出电压可以由软件配置为不同的范围(Range 1 和 Rnage 2)。
+
+| 稳压器 | 应用场合 |
+| -- | -- |
+| MR(Range 1) | Vcore = 1.2V,用于运行模式、睡眠模式和停止模式0,MR 未 Vcore 域提供全功率 |
+| MR(Range 2) | Vcore = 1.0V,使用的场景同上|
+| LPR | 用于低功耗运行模式、低功耗休眠模式、停止模式 1、停止模式2 |
+| OFF | Standby 和 Shutdown 模式下,MR 和 LPR 都被关闭 |
+
+- CPU 工作频率
+
+通过降低 CPU 的主频达到降低功耗的目的:
+MR 工作在 Range 1 正常模式时,SYSCLK 最高可以工作在 80M;
+MR 工作在 Range 2 时,SYSCLK 最高不能超过 26 M;
+低功耗运行模式和低功耗休眠模式,即 Vcore 域由 LPR 供电,SYSCLK 必须小于 2M。
+
+- 芯片本身的低功耗处理
+
+芯片本身定义了一系列的休眠模式,如 Sleeep、Stop、Standby 和 Shutdown,前面的四种模式功耗逐渐降低,实质是芯片内部通过关闭外设和时钟来实现。
+
+### 移植的具体实现
+
+上文简要说明 STM32 的低功耗模式和工作原理,下面介绍 RT-Thread PM 的功能和移植接口。
+
+RT-Thread 低功耗管理系统从设计上分离运行模式和休眠模式,独立管理,运行模式用于变频和变电压,休眠调用芯片的休眠特性。对于多数芯片和开发来说,可能并不需要考虑变频和变电压,仅需关注休眠模式。
+
+STM32 L4 系列的芯片有运行模式和低功耗运行模式的概念,同时 MR 还有 Range 2 模式,可用于变频场景。
+
+PM 组件的底层功能都是通过struct rt_pm_ops结构体里的函数完成:
+
+```c
+/**
+ * low power mode operations
+ */
+struct rt_pm_ops
+{
+ void (*sleep)(struct rt_pm *pm, uint8_t mode);
+ void (*run)(struct rt_pm *pm, uint8_t mode);
+ void (*timer_start)(struct rt_pm *pm, rt_uint32_t timeout);
+ void (*timer_stop)(struct rt_pm *pm);
+ rt_tick_t (*timer_get_tick)(struct rt_pm *pm);
+};
+```
+
+- 移植休眠模式
+
+移植休眠模式仅需关注 sleep 接口,根据 PM 用户手册相关介绍,首先将 RT-Thread 的休眠模式和 STM32 的模式作一个转换:
+
+| RT-Thread | STM32 | 描述 |
+| -- | -- | -- |
+|PM_SLEEP_MODE_NONE | Run | 正常运行模式,不进行任何降功耗的措施 |
+|PM_SLEEP_MODE_IDLE | Run | 正常运行模式,可选择 WFI(等待中断唤醒)和WFE(等待事件唤醒),此处暂不处理 |
+|PM_SLEEP_MODE_LIGHT | Sleep | 轻度睡眠模式,执行 ST 的 Sleep 模式|
+|PM_SLEEP_MODE_DEEP | Stop2 | 深度睡眠模式,执行 ST 的 Stop2 模式 |
+|PM_SLEEP_MODE_STANDBY | Standby| 待机模式,执行 ST 的 Standby 模式 |
+|PM_SLEEP_MODE_SHUTDOWN | Shutdown |停止模式,执行 ST 的 Shtudown 模式 |
+
+下面是具体的实现:
+
+```c
+
+#include
+#include
+#include
+
+static void sleep(struct rt_pm *pm, uint8_t mode)
+{
+ switch (mode)
+ {
+ case PM_SLEEP_MODE_NONE:
+ break;
+
+ case PM_SLEEP_MODE_IDLE:
+ // __WFI();
+ break;
+
+ case PM_SLEEP_MODE_LIGHT:
+ /* Enter SLEEP Mode, Main regulator is ON */
+ HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
+ break;
+
+ case PM_SLEEP_MODE_DEEP:
+ /* Enter STOP 2 mode */
+ HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
+ break;
+
+ case PM_SLEEP_MODE_STANDBY:
+ /* Enter STANDBY mode */
+ HAL_PWR_EnterSTANDBYMode();
+ break;
+
+ case PM_SLEEP_MODE_SHUTDOWN:
+ /* Enter SHUTDOWNN mode */
+ HAL_PWREx_EnterSHUTDOWNMode();
+ break;
+
+ default:
+ RT_ASSERT(0);
+ break;
+ }
+}
+
+int rt_hw_pm_init(void)
+{
+ static const struct rt_pm_ops _ops =
+ {
+ sleep,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL
+ };
+
+ rt_uint8_t timer_mask = 0;
+
+ /* Enable Power Clock */
+ __HAL_RCC_PWR_CLK_ENABLE();
+
+ /* initialize system pm module */
+ rt_system_pm_init(&_ops, timer_mask, RT_NULL);
+
+ return 0;
+}
+
+INIT_BOARD_EXPORT(rt_hw_pm_init);
+
+```
+
+目前为止,ST 的休眠模式已经初步加入了,能够满足部分的应用场景。打开命令行,输入请求/释放休眠模式命令,可以观察到功耗显著降低。
+
+- 移植时间补偿接口
+
+某些情况下,我们可能需要系统在空闲时进入 Stop 模式,以达到更低的将功耗效果。L476 Stop 2 模式下的电流可以达到 1.6 uA 左右,ST 手册上对 Stop2 模式的描述如下:
+
+Stop 2 模式基于 Cortex-M4 深度睡眠模式与外设时钟门控。在 Stop 2 模式下, Vcore 域中的所有时钟都会停止, PLL、 MSI、 HSI16 和 HSE 振荡器也被禁止。一些带有唤醒功能(I2C3 和 LPUART)的外设可以开启 HSI16 以获取帧,如果该帧不是唤醒帧,也可以在接收到帧后关闭 HSI16。SRAM1、 SRAM2、 SRAM3 和寄存器内容将保留,所有 I/O 引脚的状态与运行模式下相同。
+
+根据手册可知,Stop 2 模式会关闭系统时钟,当前的 OS Tick 基于内核的 Systick 定时器。那么在系统时钟停止后,OS Tick 也会停止,对于某些依赖 OS Tick 的应用,在进入 Stop 2 模式,又被中断唤醒后,就会出现问题,因此需要在系统唤醒后,对 OS Tick 进行补偿。Stop 2 模式下,绝大多数外设都停止工作,仅低功耗定时器 1(LP_TIM1)选择 LSI 作为时钟源后,仍然能正常运行,所以选择 LP_TIM1 作为 Stop 2 模式的时间补偿定时器。
+
+休眠的时间补偿需要实现三个接口,分别用于启动低功耗定时器、停止定时器、唤醒后获取休眠的 Tick,下面是具体的实现:
+
+```c
+static void pm_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
+{
+ RT_ASSERT(pm != RT_NULL);
+ RT_ASSERT(timeout > 0);
+
+ /**
+ * 当超时为 RT_TICK_MAX 时,表明系统此时没有依赖 OS Tick 的应用,
+ * 因此不启动低功耗定时器,避免超时唤醒而增加系统功耗
+ */
+ if (timeout != RT_TICK_MAX)
+ {
+ /* Convert OS Tick to pmtimer timeout value */
+ timeout = stm32l4_pm_tick_from_os_tick(timeout);
+ if (timeout > stm32l4_lptim_get_tick_max())
+ {
+ timeout = stm32l4_lptim_get_tick_max();
+ }
+
+ /* Enter PM_TIMER_MODE */
+ stm32l4_lptim_start(timeout);
+ }
+}
+
+static void pm_timer_stop(struct rt_pm *pm)
+{
+ RT_ASSERT(pm != RT_NULL);
+
+ /* Reset pmtimer status */
+ stm32l4_lptim_stop();
+}
+
+static rt_tick_t pm_timer_get_tick(struct rt_pm *pm)
+{
+ rt_uint32_t timer_tick;
+
+ RT_ASSERT(pm != RT_NULL);
+
+ timer_tick = stm32l4_lptim_get_current_tick();
+
+ return stm32l4_os_tick_from_pm_tick(timer_tick);
+}
+
+int rt_hw_pm_init(void)
+{
+ static const struct rt_pm_ops _ops =
+ {
+ sleep,
+ RT_NULL,
+ pm_timer_start,
+ pm_timer_stop,
+ pm_timer_get_tick
+ };
+
+ rt_uint8_t timer_mask = 0;
+
+ /* Enable Power Clock */
+ __HAL_RCC_PWR_CLK_ENABLE();
+
+ /* initialize timer mask */
+ timer_mask = 1UL << PM_SLEEP_MODE_DEEP;
+
+ /* initialize system pm module */
+ rt_system_pm_init(&_ops, timer_mask, RT_NULL);
+
+ return 0;
+}
+```
+
+休眠时间补偿的移植相对并不复杂,根据 Tick 配置低功耗定时器超时,唤醒后获取实际休眠时间并转换为OS Tick,告知 PM 组件即可。另外,从 Stop 2 模式唤醒后,默认会切换到内部的 MSI 时钟,通常需要重新配置时钟树。
+
+- 移植运行模式
+
+STM32L476 的运行模式移植主要是通过改变CPU 频率 和 稳压器,让其工作在 MR Range 2 或者 LP_RUN 模式,两个模式切换都会触发 CPU 频率改变的操作,这是一个比较危险的操作,我们在此处不作介绍。但是RT-Thread 仓库的 stm32l476-nucleo 有完整的实现,且已通过测试,如果项目中有此需求,可以参考该 bsp。
+
+## 参考资料
+
+* [《用户指南-电源管理》](../../../programming-manual/pm/pm.md)
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/.gitkeep b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..b09e6a4fdf05b586844b14353a9b145b9cbc4c08
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/.gitkeep
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/deep_sleep.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/deep_sleep.png
new file mode 100644
index 0000000000000000000000000000000000000000..26d2f323b539490e797dde263e6bab9415b45d9e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/deep_sleep.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/menuconfig1.jpg b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/menuconfig1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f5748c1423c1936834e92e7fd2e9a59e92c6d9e3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/menuconfig1.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/menuocnfig0.jpg b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/menuocnfig0.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..72c3766370714839a03e24156648def437f5a599
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/menuocnfig0.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a37b2b69ee10e49063c824eca5da63d3e72484f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_figure1.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_figure1.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c1b2c171fdafc816cb02e2630bfd7b874d82139
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_figure1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_figure2.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_figure2.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c9c64894c843c79918a678334b52a720b1e52ef
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_figure2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_process.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_process.png
new file mode 100644
index 0000000000000000000000000000000000000000..97f016ab81e9eca7bb0251482bbebf2b5f897d2f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/pm_process.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/project.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/project.png
new file mode 100644
index 0000000000000000000000000000000000000000..6aecd078848b287f270f62d08d2d5d4d910a8ca4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/project.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/stl476_mode.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/stl476_mode.png
new file mode 100644
index 0000000000000000000000000000000000000000..cfaa04f553a1d79b4944448fa6482863d827e18a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/stl476_mode.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/wakeup.png b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/wakeup.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff05a7a1621565ef5025ea9048b97248aae5cf73
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/pm/figures/wakeup.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/an0028-rtboot.md b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/an0028-rtboot.md
new file mode 100644
index 0000000000000000000000000000000000000000..01eb28c85cb92db92f08708b4805efd9fa4257db
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/an0028-rtboot.md
@@ -0,0 +1,342 @@
+# STM32 通用 Bootloader
+
+## 简介
+
+为了能让开发者快速掌握 OTA 升级这把利器,RT-Thread 开发团队提供了通用的 Bootloader。开发者通过该 Bootloader 即可直接使用 RT-Thread OTA 功能,轻松实现对设备端固件的管理、升级与维护。
+
+下图展示了 RT-Thread 通用 Bootloader 的软件框架:
+
+
+
+RT-Thread 通用 Bootloader 有如下特点:
+
+- 以 bin 文件的形式提供,无需修改即可使用
+
+- 资源占用小,ROM 最小只需要 16KB,最大 32KB
+
+- 适用于多系列 STM32 芯片(目前支持 F1 和 F4 系列 )
+
+- 支持各种 SPI Flash 存储固件
+
+- 支持固件加解密功能
+
+- 支持多种固件压缩方式
+
+- 支持恢复出厂固件功能
+
+- 以上功能均可自由配置
+
+## 功能说明
+
+Bootloader 的主要功能是更新 app 分区中的固件。
+
+### 分区表介绍
+
+通用 Bootloader 中的分区表包含如下三个分区:
+
+| 分区名 | 起始地址 | 分区大小 | 分区位置 | 介绍 |
+| -------- | -------- | -------- | ----------------------------- | -------------- |
+| app | 自定义 | 自定义 | 片内 Flash | 存储 app 固件 |
+| download | 自定义 | 自定义 | 片内 Flash 或者片外 SPI Flash | 存储待升级固件 |
+| factory | 自定义 | 自定义 | 片内 Flash 或者片外 SPI Flash | 存储出厂固件 |
+
+### 升级固件功能
+
+当系统需要升级固件时,Bootloader 将从 `download` 分区将固件搬运到 `app` 分区,主要功能流程如下所示:
+
+1. Bootloader 启动时检查 `download` 分区和 `app` 分区中的固件版本。
+2. 如果两个固件版本相同,则跳转到 app 分区,Bootloader 运行结束。
+3. 固件版本不同则将 `download` 分区中的固件搬运到 `app` 分区。
+4. 在搬运的过程中 Bootloader 可以对固件进行校验、解密、解压缩等操作。
+5. 搬运完毕后,删除 `download` 分区中存储的固件。
+6. 重启系统跳转到 `app` 分区中的固件运行,Bootloader 运行结束。
+
+Bootloader 工作过程如下图所示:
+
+
+
+### 恢复固件功能
+
+当系统中的固件损坏,Bootloader 将从 `factory` 分区将固件搬运到 `app` 分区,主要功能流程如下所示:
+
+1. Bootloader 启动时检查触发固件恢复的引脚是否为有效电平。
+2. 如果有效电平持续超过 10S 则将 `factory` 分区中的固件搬运到 `app` 分区中。
+3. 如果有效电平没有持续超过 10S 则继续进行 2.2 小节中介绍的启动步骤。
+4. 在搬运的过程中 Bootloader 可以对固件进行校验、解密、解压缩等操作。
+5. 搬运完毕后,保持 `factory` 分区中的固件不变。
+6. 重启系统跳转到 `app` 分区中的固件运行,Bootloader 运行结束。
+
+## 获取 Bootloader
+
+Bootloader 可以通过网页端在线生成的方式来获取。开发者根据自己使用的芯片,填写相关参数,然后点击生成按钮,即可在线生成 Bootloader。
+
+Bootloader 在线获取地址: http://iot.rt-thread.com
+
+### 登陆账号
+
+
+
+### 新建产品
+
+
+
+
+
+### 进入设备管理
+
+
+
+### 进入生成页面
+
+进入 Bootloader 生成页面后,根据页面提示填写板卡参数,点击生成固件按钮即可在线自动生成 BootLoader,同时也会将生成的固件发送到用户邮箱中。
+
+下面提供一个示例配置以供参考,开发者则需要根据自己手中板卡的实际情况,勾选和填写所需功能。
+
+
+
+
+
+点击生成按钮后,等待大约一分钟即可通过自动下载或者邮件的方式获取定制的 Bootloader。
+
+## 使用 Bootloader
+
+### 准备工作
+
+1. 在线生成 Bootloader 时可以自定义使用某个引脚作为串口输出引脚,例如 PA9。波特率设置为 115200 ,通过 usb 转串口正确地连接到 PC 端。
+
+2. 保证 jlink 正常连接到开发板。
+
+### 烧录 Bootloader
+
+#### 方法 1: J-Flash 工具烧写
+
+以 `STM32F407ZGT6` 芯片为例讲述如何使用 J-Flash 工具烧录 bootloader.bin 到开发板中,操作步骤如下:
+
+1. 打开 J-Flash 工具,配置烧录参数。
+
+
+
+2. 选择打开固件功能。
+
+
+
+3. 选择需要烧录的固件,这里选择在线生成后下载到本地的 Bootloader 固件。
+
+
+
+4. 烧录 bootloader.bin 到指定地址 0x8000000。
+
+
+
+5. 连接开发板。
+
+
+
+6. 开始烧录固件。
+
+
+
+固件烧录成功后会自动运行 Bootloader,打印出 RT-Thread 的 logo。
+
+#### 方法 2: ST-LINK Utility 工具烧写
+
+以下介绍如何使用 ST-LINK Utility 工具烧录 bootloader.bin 到开发板中,这需要配合 ST-LINK 进行烧写,操作步骤如下所示:
+
+1. 连接开发板。
+
+
+
+2. 设置连接选项。
+
+
+
+3. 烧录:选择需要烧录的固件,这里选择在线生成后下载到本地的 Bootloader 固件。
+
+
+
+
+
+## 制作 app 固件
+
+本小节介绍如何使用 stm32 系列的 BSP 制作一个可以用于 OTA 升级的,包含 OTA 下载器功能 app 固件。
+
+接下来的示例中所用的 BSP 路径为 `stm32/stm32f407-atk-explorer`。
+
+固件中使用的分区表如下所示:
+
+| 分区名 | 起始地址 | 分区大小 | 分区位置 |
+| -------- | --------- | -------- | ---------- |
+| app | 0x8040000 | 128k | 片内 Flash |
+| download | 0x8020000 | 128k | 片内 Flash |
+| factory | 0x8060000 | 128k | 片内 Flash |
+
+制作该 app 固件有如下三个步骤:
+
+- 为 BSP 添加下载器功能,下载需要的软件包并修改 FAL 分区表
+- 修改 stm32 BSP 中断向量表跳转地址
+- 修改 BSP 链接脚本
+
+在后面的章节中,将按照上述步骤来制作 app 固件。
+
+### 添加下载器功能
+
+本小节介绍如何将下载器功能添加到 app 固件中。
+
+添加该功能需要使用 env 工具,本次下载的软件包在 iot 类别中,需要按照如下步骤操作:
+
+ 1. 下载 ota_downloader 软件包,选中 Ymodem 功能。
+
+
+
+ 2. 添加 BSP Flash 驱动。
+
+
+
+注:如果 BSP 没有该选项,则需要手动在 board 文件夹的 Kconfig 添加下面定义,保存,然后重新进入 menuconfig 即可。
+
+```
+ config BSP_USING_ON_CHIP_FLASH
+ bool "Enable on-chip FLASH"
+ default n
+```
+
+3. 配置完毕后,先使用 `pkgs --update` 命令将所需要的软件包下载下来,然后使用 `scons --target=mdk5` 命令重新生成 mdk 工程。
+
+### 配置 FAL 分区
+
+本小节将讲述如何初始化 FAL 组件,并修改 FAL 分区表。开发者需要对 FAL 进行简单入门,无需移植,只需要了解如何配置即可,详细内容可参考 [官方文档](https://github.com/RT-Thread-packages/fal)。
+
+本次制作的 `app` 固件将附带下载器功能,下载器会将固件下载到 `download` 分区。根据第 4 章开始时的分区表可知,`download` 分区的地址为 0x8020000,而 `app` 分区的地址为 0x8040000。
+
+#### 初始化 FAL
+
+由于 FAL 组件会被 ota_downloader 软件包自动选中,因此直接添加 FAL 组件的初始化代码即可。
+
+
+
+2. 修改 `stm32f407-atk-explorer/board/ports/fal_cfg.h` 文件中的分区表,使分区表中 download 分区的起始地址和大小与 Bootloader 中的 download 分区一致。
+
+
+
+注意:如果 BSP 中没有该头文件,可以在该 BSP 目录下 `/packages/fal-latest/samples/porting` 中复制一份进行修改,其中分区地址和大小是根据实际 bootloader 中定义的大小进行设置。下图中标记处了可能需要修改的地方,请根据个人实际情况进行修改。
+
+
+
+#### 修改 app 固件配置
+
+由于 `app` 分区的起始地址为 `0x08040000`,`app` 固件如果想运行在该地址,就需要修改链接脚本和中断向量的跳转地址。
+
+1. 修改固件的链接地址为 0x8040000。
+
+
+
+2. 修改中断向量表的跳转基地址为 0x8040000。
+
+首先在 main.c 文件中添加如下代码,这段代码的功能是重新设定中断向量跳转地址为 app 分区的地址。
+
+```c
+/**
+ * Function ota_app_vtor_reconfig
+ * Description Set Vector Table base location to the start addr of app(RT_APP_PART_ADDR).
+*/
+static int ota_app_vtor_reconfig(void)
+{
+ #define NVIC_VTOR_MASK 0x3FFFFF80
+ /* Set the Vector Table base location by user application firmware definition */
+ SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;
+
+ return 0;
+}
+INIT_BOARD_EXPORT(ota_app_vtor_reconfig);
+```
+
+然后在 main 函数中添加版本信息打印,如下图所示:
+
+
+
+3. 下载 app 程序
+
+直接点击下载程序,固件就会被烧录到 app 分区。Bootloader 启动后将跳转到 app 分区运行,实验效果如下图所示:
+
+
+
+可以看到串口输出的信息 `The current version of APP firmware is 1.0.0`,即当前固件的版本为 1.0.0 。
+
+## 打包 app 固件
+
+本小节讲述如何使用 RT-Thread OTA 固件打包器对 app 固件进行打包,制作可以被下载到 download 分区的升级固件。固件打包工具可以在 ota_downloader 软件包下的 tools 文件夹内找到。
+
+1. 在对固件进行打包操作前,先修改 `stm32f407-atk-explorer/applications/main.c` 中 APP_VERSION 宏的值为 2.0.0 作为参照,**然后重新编译一遍生成新的 rtthread.bin 文件**,修改内容如下图所示:
+
+
+
+2. 双击打开 `tools\ota_packager\rt_ota_packaging_tool.exe` 程序,使用 OTA 固件打包工具将上一步编译出的 `rtthread.bin` 文件打包成可被升级的 `rtthread.rbl` 文件,如下图所示:
+
+
+
+固件打包器提供三种固件压缩方式:fastlz、quicklz 和 gzip,一种固件加密方式 AES256。开发者可以根据实际需求选择合适的加密压缩方式。
+
+## 执行 OTA
+
+### Ymodem 升级固件
+
+使用 Ymodem 协议升级固件时,推荐使用 Xshell 终端。
+
+在 msh 命令行中输入 `ymodem_ota` 命令后,点击鼠标右键,然后在菜单栏找到用 YMODEM 发送选项发送文件,如下图所示:
+
+1. 选择 Ymodem 方式发送升级固件。
+
+
+
+2. 选中之前 OTA 固件打包工具生成的 rtthread.rbl 文件。
+
+
+
+接下来升级固件就会通过 Ymodem 的方式被下载到 download 分区。
+
+### 执行 OTA 升级
+
+固件被下载到 download 分区后,系统会自动重启,执行 OTA 升级。
+
+
+
+升级完毕后可以看到如下效果:
+
+
+
+串口输出信息为 `The current version of APP firmware is 2.0.0` ,说明固件已经被升级到 2.0.0 版本了。
+
+### 更多固件下载方式
+
+HTTP/HTTPS 固件升级是另外一种固件下载方式,制作下载器时如果开启了系统中的网络驱动,即可使用此种方式下载固件。具体步骤与 **Ymodem 升级固件** 小节大体一致。以下是配置截图:
+
+
+
+
+
+
+
+在终端输入 `http_ota http://xxx/xxx/rtthreadf.rbl` 命令,系统将会从链接 http://xxx/xxx/rtthreadf.rbl` 处下载固件到 download 分区,之后系统会自动重启,执行 OTA 升级程序。
+
+
+
+### 恢复出厂固件
+
+在生成页面中选中了 **恢复出厂固件引脚** 功能后,即开启了出厂固件恢复功能。开发者可以将制作好的 app 固件烧录至 `factory` 分区中,在系统启动前按下恢复出厂固件引脚(可选择一个或者两个引脚作为固件恢复触发引脚)并保持 10S,即可从 `factory` 分区中恢复出厂固件到 `app` 分区中。
+
+恢复出厂按键引脚配置如下:
+
+
+
+`factory` 分区的配置如下所示:
+
+
+
+按照上图的配置,`factory` 分区被设置为从片内 Flash 首地址偏移 0x60000 的位置开始,大小为 128KB,想要使用固件恢复功能,则需要将可用的 app 固件烧录到该分区中,固件恢复过程如下图所示:
+
+
+
+
+
+
+
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1550821330959.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1550821330959.png
new file mode 100644
index 0000000000000000000000000000000000000000..300bb842dd0485be775859550a4934ac581c649a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1550821330959.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247249515.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247249515.png
new file mode 100644
index 0000000000000000000000000000000000000000..90250e7f972b4a10dd65cd0cd0a6692b9094dfc0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247249515.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247299946.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247299946.png
new file mode 100644
index 0000000000000000000000000000000000000000..033970a0a3c209432198b3328de78f28d532d297
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247299946.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247372693.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247372693.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e9fc19480430fd2e507aa8bcd19a3f193f31210
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247372693.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247497496.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247497496.png
new file mode 100644
index 0000000000000000000000000000000000000000..21295522795da6f94c3cda53502c5d4059108038
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247497496.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247783487.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247783487.png
new file mode 100644
index 0000000000000000000000000000000000000000..f54606983cff6b78eba096f284d574cff7d05b9a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553247783487.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553673658407.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553673658407.png
new file mode 100644
index 0000000000000000000000000000000000000000..ad24cef431a1cd180ca074dd11b76b461ffcdc75
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553673658407.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553675707296.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553675707296.png
new file mode 100644
index 0000000000000000000000000000000000000000..2936a1672a21925cb64bc80ad8486c24b069c918
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553675707296.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553761443975.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553761443975.png
new file mode 100644
index 0000000000000000000000000000000000000000..e078f7ca34571f94222f2303efabadd1b45bd021
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553761443975.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553826295876.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553826295876.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7027a436adfa97b452956949160c912017f7ac5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553826295876.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553830878300.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553830878300.png
new file mode 100644
index 0000000000000000000000000000000000000000..de9f125174fc477be3059e3d927998485c7b1948
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553830878300.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553832436431.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553832436431.png
new file mode 100644
index 0000000000000000000000000000000000000000..9fdc94c4c30d8ea08c4cd4cab92168420bda427f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553832436431.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553840590537.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553840590537.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7231a034a995607f473164f751460a6617db849
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/1553840590537.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/Bootloader_workprocess.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/Bootloader_workprocess.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c4cfc2fb95bda29ad90a9e17626d9055ab8882f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/Bootloader_workprocess.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash1.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash1.png
new file mode 100644
index 0000000000000000000000000000000000000000..a7481cddfe4621849d5e47779c2c31962db0f798
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash2.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash2.png
new file mode 100644
index 0000000000000000000000000000000000000000..2816226a7b769feac02bf816a961ebd4d65ec858
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash3.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash3.png
new file mode 100644
index 0000000000000000000000000000000000000000..35742ad739e0dc44cca8cef3dbc65b1075042c6b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash4.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash4.png
new file mode 100644
index 0000000000000000000000000000000000000000..f29648000ed7a999843667068e2580b019a4d801
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash4.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash5.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash5.png
new file mode 100644
index 0000000000000000000000000000000000000000..754bd78df37cda0a60dd1b65441aafa96597f997
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash5.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash6.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash6.png
new file mode 100644
index 0000000000000000000000000000000000000000..35fef9ba36e99fdcf7b4eb6f1409dde871b4c23d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/J-Flash6.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app1.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app1.png
new file mode 100644
index 0000000000000000000000000000000000000000..403e13920cc7684e68b683e49b207f1b4963ee7c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app2.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app2.png
new file mode 100644
index 0000000000000000000000000000000000000000..e50bb56d35bd2981481f8517e417eeb14edd9a5b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app3.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app3.png
new file mode 100644
index 0000000000000000000000000000000000000000..340587da44b7d036145e6cfac7bc2f2f62ac32b6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app4.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app4.png
new file mode 100644
index 0000000000000000000000000000000000000000..160c5aa10d11ec9eb6f20c3849b975dca417e87f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app4.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app5.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app5.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5ae87f48f1d98674aee12f71f20128ff72fb408
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app5.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app5xxx.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app5xxx.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d43bcb752331e6fd129759e2c2f7994f38a2694
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app5xxx.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app6.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app6.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae6b91bc8776f749a74ee4ca6346b9e6e24c4079
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app6.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app7.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app7.png
new file mode 100644
index 0000000000000000000000000000000000000000..bf61766ee37349c0fe79adeafabd9e07657ff2d2
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app7.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app8.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app8.png
new file mode 100644
index 0000000000000000000000000000000000000000..155e737ceeec5415bba2467a4096169f33992603
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app8.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app9.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app9.png
new file mode 100644
index 0000000000000000000000000000000000000000..cabcaee1b947c13b0ddf75a1fd27737dfef5833d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app9.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app_pack_1.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app_pack_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..33717acc891506d583eaf176d4b4271275e04b49
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app_pack_1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app_pack_2.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app_pack_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..95cd7138e950d9d34c07719d0cf329b0342baf03
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/app_pack_2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/en_eth_drv.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/en_eth_drv.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4b795535cfe9c61c2c0ebc3742cb2953fd6074b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/en_eth_drv.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/exp_show.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/exp_show.png
new file mode 100644
index 0000000000000000000000000000000000000000..358130c807a80fb07ce5e636ffac38e240b37ba8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/exp_show.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/fal_cfg_modify.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/fal_cfg_modify.png
new file mode 100644
index 0000000000000000000000000000000000000000..d49b64cbfc9c8fc5d3bef7baf44fcb60dfe2f9c5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/fal_cfg_modify.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/fal_partition.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/fal_partition.png
new file mode 100644
index 0000000000000000000000000000000000000000..030f07ce1b4e77c8e0d0f9d09470c1c1d7615b11
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/fal_partition.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/http_menuconfig.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/http_menuconfig.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d5f181f86a0cc239ef1f70acc05d1bd517d8bb6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/http_menuconfig.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/http_ota.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/http_ota.png
new file mode 100644
index 0000000000000000000000000000000000000000..477fe59641d093d601c60826b99e9207cfe90796
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/http_ota.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/nvic_fun.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/nvic_fun.png
new file mode 100644
index 0000000000000000000000000000000000000000..67b759e988bb4ceb4458193da840da2d495a73f7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/nvic_fun.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility1.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility1.png
new file mode 100644
index 0000000000000000000000000000000000000000..8eb358d50812df3d030e3f3aa4139b54cc10a55d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility1.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility2.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility2.png
new file mode 100644
index 0000000000000000000000000000000000000000..4b64e40c0990beda38ad7812451a572d2ad84035
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility2.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility3.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility3.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ae20c5662b21c8461d17c008aef01a849ddfd51
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility3.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility4.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility4.png
new file mode 100644
index 0000000000000000000000000000000000000000..4735f75952b1fc3a866c563f7265f3dfa4d6ba7b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/utility4.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/ymodem_ota.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/ymodem_ota.png
new file mode 100644
index 0000000000000000000000000000000000000000..86f8080237157cc338c6ba2f0477ec98efb608af
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/ymodem_ota.png differ
diff --git a/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/ymodem_ota_select.png b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/ymodem_ota_select.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d89e927c2f28469276d76d7e4ea1b3325d31c28
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/application-note/system/rtboot/figures/ymodem_ota_select.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/bug1.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/bug1.png
new file mode 100644
index 0000000000000000000000000000000000000000..26419b508cc88b1b22883f05883556eb887aae07
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/bug1.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/bug2.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/bug2.png
new file mode 100644
index 0000000000000000000000000000000000000000..491766d8ab6d764737d4ee794378276baf21eb74
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/bug2.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/checklist.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/checklist.png
new file mode 100644
index 0000000000000000000000000000000000000000..db627bc5aad404b030c3204953530c5a91f0cab8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/checklist.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/checklistyes.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/checklistyes.png
new file mode 100644
index 0000000000000000000000000000000000000000..30bb3bbfdd319672df36ba6c15ef41cc3b04e86e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/checklistyes.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/checkok.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/checkok.png
new file mode 100644
index 0000000000000000000000000000000000000000..9be4ef0a5e6f0dcad48aa201e51768fcf292d1c4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/checkok.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/cla.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cla.png
new file mode 100644
index 0000000000000000000000000000000000000000..7e3f7021e21aa728a9ca95a92bf7187ac3e5582a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cla.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit.png
new file mode 100644
index 0000000000000000000000000000000000000000..c69427353228de389680229d922040ef42f8e836
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit2.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit2.png
new file mode 100644
index 0000000000000000000000000000000000000000..193f44d4586e2de315c9bec5b41d953e12dbf8eb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit2.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit3.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit3.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef373c416a94774b6ab91a86fd18ca91985e10b8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/cloneformgit3.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/create_pull_request.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/create_pull_request.png
new file mode 100644
index 0000000000000000000000000000000000000000..69fb7a0e253d2ff8207237b2f0ef1b6c2fa49fc6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/create_pull_request.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/fork.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/fork.png
new file mode 100644
index 0000000000000000000000000000000000000000..4010586778c96f3981c88e446624637eddf250a9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/fork.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/pullrequest.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/pullrequest.png
new file mode 100644
index 0000000000000000000000000000000000000000..912a0369b580cb5f055f279d8005603063a487ab
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/pullrequest.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/figures/push.png b/rt-thread-version/rt-thread-standard/development-guide/github/figures/push.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6f13ca8d07006cf53bb504c7d108521ca38e8ce
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/github/figures/push.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/github/github.md b/rt-thread-version/rt-thread-standard/development-guide/github/github.md
new file mode 100644
index 0000000000000000000000000000000000000000..17e0eba3873b7ad4f01caa85815b36b70b6d3157
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/development-guide/github/github.md
@@ -0,0 +1,151 @@
+# 向 RT-Thread 贡献代码
+
+我们真诚地感谢您的贡献,欢迎通过 GitHub 的 fork 和 Pull Request 流程来提交代码。
+
+首先解释一下 Pull Request 这个词,pull request的意思是推送请求,开发者发起 Pull Request 的目的是请求仓库维护人采用开发者提交的代码。
+
+下面是摘自[知乎](https://www.zhihu.com/question/21682976) 网友的一段解释:
+
+我尝试用类比的方法来解释一下pull reqeust。想想我们中学考试,老师改卷的场景吧。你做的试卷就像仓库,你的试卷肯定会有很多错误,就相当于程序里的bug。老师把你的试卷拿过来,相当于先fork。在你的卷子上做一些修改批注,相当于git commit。最后把改好的试卷给你,相当于发pull request,你拿到试卷重新改正错误,相当于merge。
+
+当你想更正别人仓库里的错误时,要按照下面的流程进行:
+
+* 先 fork 别人的仓库,相当于拷贝一份别人的资料。因为不能保证你的修改一定是正确的,对项目有利的,所以你不能直接在别人的仓库里修改,而是要先fork到自己的git仓库中。
+
+* clone 到自己的本地分支,做一些 bug fix,然后发起 pull request给原仓库,让原仓库的管理者看到你提交的修改。
+
+* 原仓库的管理者 review 这个 bug,如果是正确的话,就会 merge 到他自己的项目中。merge 的意思就是合并,将你修改的这部分代码合并到原来的仓库中添加代码或者替换掉原来的代码。至此,整个 Pull Request 的过程就结束了。
+
+## 编程风格
+
+RT-Thread 代码编程风格请参考 rt_thread 项目 documentation 目录下的 coding_style_cn.txt文件。
+
+## 准备工作
+
+* 安装 git
+* 安装 TortoiseGit 工具,这个工具是 git 的一种图形化界面
+* 注意安装 git 的时候记得勾选将 git 所在目录添加到系统环境变量
+
+现在以rt-thread仓库为例说明贡献代码的流程:
+
+## fork
+
+将 rt-thread 仓库 fork 到自己的 git 仓库中。
+
+
+
+## 克隆(clone)
+
+将 rt-thread 仓库 clone 到自己的本地 PC。
+
+
+
+
+
+
+
+## 创建本地分支
+
+建议从 master 分支创建自己的开发分支,可使用命令 `git checkout -b branchName`。
+
+## 开发
+
+发现了一个小 bug 并进行修改。
+
+
+
+注意:开发时,代码需要符合 [RT-Thread 代码规范](https://github.com/RT-Thread/rt-thread/blob/master/documentation/coding_style_cn.md),请仔细检查。
+
+## 提交(commit)
+
+向本地仓库提交 bug.
+
+
+
+> [!NOTE]
+> 注:若本地分支多个 commit,为了保证 RT-Thread 仓库 commit 干净,请整理一下本地的 commit,不接受 Pull Request 有超过 5 个及以上个commit。
+
+## Push 到远程仓库
+
+push 到开发者自己的远程仓库中。
+
+
+
+## 发起 Pull Request
+
+在 git 仓库中选择自己修改了的分支,点击 create Pull Request 按钮发起 pull request。
+
+
+
+
+
+## checklist 核对
+
+在正式发起 Pull Request 之前,需要根据 Preview 里面默认的描述信息即 checklist 仔细核对代码,在没问题的 checklist 对应选项复选框填写[x]确认,**注意[x]两边没有空格**。比如若代码是成熟版本,请选择成熟版本,且可以添加相应的描述信息。checklist 核对完成才可发起 Pull Request。
+
+
+
+
+
+## 签署 CLA
+
+第一次为 RT-Thread 贡献代码需要需要签署 Contributor License Agreement。
+
+
+
+请确认 CLA 显示签署成功及 CI 编译通过,如下图所示:
+
+
+
+> [!NOTE]
+> 注:注意不要使用非 GitHub 账号提交 commmit,或者使用不同的账号提交 commit 后提 Pull Request,这会导致 CLA 签署失败。
+
+## Pull Request 审核
+
+发起请求成功后,RT-Thread 维护人就可以看到你提交的代码。并且会对代码进行审核,相关的评审意见会填写在 GitHub 上。请及时查看 PR 状态并根据评审意见更新代码。
+
+## 合并成功
+
+Pull Request 审核没有问题代码就会被合并到 RT-Thread 仓库中。这样一次 Pull Request 就成功了。
+
+至此,我们就完成了一次代码贡献的过程。
+
+## 和 RT-Thread 仓库保持更新
+
+RT-Thread GitHub 仓库的内容是经常处于更新的状态,若要基于最新的 RT-Thread 代码开发,那么就需要更新本地仓库。
+
+clone 后本地 master 分支内容默认是和 clone 的远程仓库的 master 分支内容保持一致。建议本地新建其他分支开发,master 则和 fork 的 RT-Thread 原仓库保持同步,master 分支不要有内容修改,可以根据以下步骤保持同步:
+
+* 查看现有的远程仓库,一般只有一个默认的 origin,也就是自己的远程仓库:
+
+```c
+$ git remote -v
+origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
+origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
+```
+
+* 添加 RT-Thread 远程仓库并命名为 rtt,rtt 可以是自己自定义的名称:
+
+```c
+$ git remote add rtt https://github.com/RT-Thread/rt-thread.git
+```
+
+* 查看本地跟踪的所有远程仓库:
+
+```c
+$ git remote -v
+origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
+origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
+rtt https://github.com/RT-Thread/rt-thread.git (fetch)
+rtt https://github.com/RT-Thread/rt-thread.git (push)
+```
+
+* 从 RT-Thread 远程仓库的 master 分支拉取代码并合并到本地的 master 分支:
+
+```c
+git pull rtt master
+```
+
+## 参考资料
+
+* 若对贡献代码还有不明白的地方,可以参考 Git 官方中文文档[《GitHub - 对项目做出贡献》](https://git-scm.com/book/zh/v2/GitHub-%E5%AF%B9%E9%A1%B9%E7%9B%AE%E5%81%9A%E5%87%BA%E8%B4%A1%E7%8C%AE)章节。
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/add_new_index.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/add_new_index.png
new file mode 100644
index 0000000000000000000000000000000000000000..c40b188f20da3ccc53b8fda6967828b8b36c486b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/add_new_index.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/add_new_source.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/add_new_source.png
new file mode 100644
index 0000000000000000000000000000000000000000..b8d34044de48bb8505abcdeb23d71c55778caf2c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/add_new_source.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/create_index_1.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/create_index_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a6c70731e6e91a537c78989fc534a6178c303c6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/create_index_1.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/create_index_2.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/create_index_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5c69d78302efc688faaed8bb74e60df9c8b5833
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/create_index_2.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/kconfig_ex.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/kconfig_ex.png
new file mode 100644
index 0000000000000000000000000000000000000000..9dee7916f380727f7ad0bfd439835913febb1bd0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/kconfig_ex.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/make_software_index.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/make_software_index.png
new file mode 100644
index 0000000000000000000000000000000000000000..0385bfc75bcf00fd2c27c0c4fe5b5adfc47d8d55
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/make_software_index.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/release_process.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/release_process.png
new file mode 100644
index 0000000000000000000000000000000000000000..85a6922795c01cdda9bd72e3fbcd2492806a2fb6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/release_process.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/figures/software_index.png b/rt-thread-version/rt-thread-standard/development-guide/package/figures/software_index.png
new file mode 100644
index 0000000000000000000000000000000000000000..32bbd54bb7a72c304eff724065ba2e2dd5da4dad
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/package/figures/software_index.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/package/package.md b/rt-thread-version/rt-thread-standard/development-guide/package/package.md
new file mode 100644
index 0000000000000000000000000000000000000000..5419a6ba2a014f4d31af76083765cb3e208b8328
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/development-guide/package/package.md
@@ -0,0 +1,184 @@
+# 软件包开发指南
+
+**软件包定义**:运行于 RT-Thread 物联网操作系统平台上,面向不同应用领域的软件组件形成一个个软件包,由软件包描述信息,软件包源代码或库文件组成。
+
+在制作软件包前,需要先对软件包的功能进行准确的定义,确保 **不要耦合** 与产品业务逻辑有关的代码,提高软件包的 **通用性** 。
+
+软件包应该包含以下内容:
+
+* 软件包代码和说明文档。
+* 软件包索引。
+
+例如作为示例的 hello 软件包,这两部分的地址为分别是:
+
+* [软件包代码和说明文档](https://github.com/RT-Thread-packages/hello)
+* [软件包索引](https://github.com/RT-Thread/packages/tree/master/misc/hello)
+
+### 整理软件包代码和说明文档
+
+制作软件包前需要仔细阅读[示例软件包说明文档](https://github.com/RT-Thread-packages/hello),参考示例软件包的文件夹结构。
+
+软件包代码和说明文档应该由以下部分组成:
+
+* 软件包的功能代码;
+* 根目录下的 SConscript 文件,用于和 RT-Thread 环境一起进行编译;
+* 根目录下的 examples 文件夹,用于提供一份使用的例子;
+* 如果需要加入 git submodule ,则可以直接在根目录下添加;
+* 根目录下的 README.md 文件,说明这个软件包的功能;
+* 根目录下的 docs 文件夹,放置除了 README 之外的其他文档;
+* 如果需要额外的移植代码,可将其放在 port 文件夹下;
+
+### 创建软件包索引
+
+**软件包索引** :指存放在 `env\packages` 文件夹下的软件包 **描述文件** 。以 `env\packages\packages\iot\pahomqtt` 文件夹为例,包含内容如图所示:
+
+
+
+- **Kconfig 文件**:软件包的配置项,如软件包版本、功能选项等信息。
+- **package.json 文件**:存放软件包的名称、简介、各个版本对应的下载链接等信息。
+
+#### 使用索引生成向导
+
+我们可以使用 Env 的软件包索引生成向导功能来制作软件包索引,命令为 `pkgs --wizard`,大致流程如下如图所示:
+
+
+
+生成内容如下图所示:
+
+
+
+
+
+> 提示:SConscript 文件在制作软件包代码时使用,将其移动到软件包源码文件夹中即可,无需保留在索引文件夹中。
+
+#### 修改 package.json 文件
+
+- **package.json 文件**介绍:
+
+```json
+{
+ "name" : "pahomqtt",
+ "description" : "a pahomqtt package for rt-thread", # 软件包描述信息
+ "keywords" : [
+ "pahomqtt"
+ ],
+ "site" : [
+ {
+ "version" : "v1.0.0",
+ "URL" : "https://pahomqtt-1.0.0.zip", # 根据版本号修改软件包压缩包的下载地址
+ "filename" : "pahomqtt-1.0.0.zip",
+ "VER_SHA" : "fill in the git version SHA value" # 压缩包形式无需填写
+ },
+ {
+ "version" : "latest", # latest 版本
+ "URL" : "https://xxxxx.git", # 可以填入 Git 仓库地址
+ "filename" : "Null for git package",
+ "VER_SHA" : "fill in latest version branch name,such as mater" # 填入 SHA 值或者分支名
+ }
+ ]
+}
+```
+
+关于文件中 `URL` 值,每个版本可以填入两种类型 :
+
+- **Git** :可以填入 Git 仓库地址和对应版本的 SHA 值。一般 `latest` 版本会在 **SHA** 填入`master`。
+- **压缩包** : 指定软件包压缩包的下载地址,此时无需填入 **SHA** 。
+
+**package.json** 是软件包的描述信息文件,包括软件包名称,软件包描述,作者等信息,以及必须的 package 代码下载链接。另外,请务必包含许可证的说明,使用了哪种许可证( GPLv2,LGPLv2.1,MIT,Apache license v2.0,BSD 等)。
+
+修改后的 package.json 大致如下:
+```json
+{
+ "name" : "pahomqtt",
+ "description" : "Eclipse Paho MQTT C/C++ client for Embedded platforms", # 更新了描述信息
+ "keywords" : [
+ "pahomqtt"
+ ],
+ "site" : [
+ {
+ "version" : "v1.0.0", # v1.0.0 版本
+ "URL" : "https://github.com/RT-Thread-packages/paho-mqtt.git", # 更新了 git 仓库地址
+ "filename" : "paho-mqtt-1.0.0.zip",
+ "VER_SHA" : "cff7e82e3a7b33e100106d34d1d6c82e7862e6ab" # 填入了指定版本的 SHA 值
+ },
+ {
+ "version" : "latest", # 最新版本
+ "URL" : "https://github.com/RT-Thread-packages/paho-mqtt.git",
+ "filename" : "paho-mqtt.zip",
+ "VER_SHA" : "master" # 填入 master
+ }
+ ]
+}
+```
+
+#### 修改 Kconfig 文件
+
+ **Kconfig** 文件内容大致如下:
+
+
+
+软件包索引中的 Kconfig 文件主要由 menuconfig 命令使用,软件包的一些选项必须在 Kconfig 文件中定义出来,注意事项如下:
+
+* 1、索引向导自动生成的 **Kconfig** 文件中的内容大多是必须的,可以参考其他软件包修改选项的值,但是**不要删除选项**。
+
+* 2、软件包必须包含一个以`PKG_USING_`开头的配置项,这样RT-Thread的包管理器才能将其正确识别。假设这个包的名称叫做SOFTA,那么软件包总选项应该是`PKG_USING_SOFTA`;
+
+* 3、和这个SOFTA软件包相关的其他选项,需要以`SOFTA_`开头的配置项进行定义,可以是`SOFTA_USING_A`或者`SOFTA_ENABLE_A`等方式。
+
+* 4、支持 **latest** 版本的软件包也至少需要一个固定版本,以防止在某个时候找不到合适的版本。
+
+* 5、软件包如果还需要更多的配置项,可以搜索 **Kconfig 语法** ,并参考已有的软件包来对 Kconfig 文件进行修改。
+
+### 上传软件包
+
+软件包可以上传到 git 上或者其他可供下载的地方,推荐使用 git 仓库的方式进行保存,这样方便更新软件包版本。
+
+参考:[RT-Thread 软件包仓库]( https://github.com/RT-Thread-packages)
+
+### 测试软件包
+
+- **软件包的下载**:将软件包索引拷贝到 `env\packages\packages` 下对应的位置,然后在 Env 中尝试在线下载软件包,测试下载是否成功。
+- **软件包的功能**:下载完成后,使用 scons 命令重新编译项目,在相应的环境下运行,测试软件包功能是否正确。
+- **软件包版本的切换**:尝试在 menuconfig 下切换软件包的版本,查看版本切换是否正常。
+
+### 提交软件包索引
+
+最后需要将软件包索引通过 PR 流程推送到:[https://github.com/RT-Thread/packages](https://github.com/RT-Thread/packages)
+
+[点击这里](../github/github.md)了解如何提交 PR 。
+
+## 软件包索引源的管理
+
+Env 可以从多个软件包源来下载软件包,每个源的软件包列表就存放在 `env\packages` 文件夹中,如 `env\packages\packages` 文件夹下就是 RT-Thread 官方的软件包列表。
+
+### 添加软件包源
+
+- 复制一份 RT-Thread 官方的 packages 文件夹,修改文件夹名称后,删除该文件夹内不需要的软件包索引,将需要的索引添加进去。
+
+
+
+- 更新 `env\packages` 文件夹下的 Kconfig 文件,在 Kconfig 文件中添加软件包源信息。
+
+
+
+### 删除软件包源
+
+- 删除软件源文件夹;
+- 将 Kconfig 文件中相应的源文件夹信息删除。
+
+## 新版本发布流程
+
+软件包发布新版本需要遵循以下流程:
+
+1、检查软件包,确保软件包功能使用正常。
+
+2、在 github 上使用 release 功能发布新版本,如果没有权限可以通知管理员发布新版本。如果不知道该如何 release,可以参考 [paho-mqtt 软件包仓库](https://github.com/RT-Thread-packages/paho-mqtt/releases),发布新版本界面如下所示:
+
+ 
+
+3、修改本地软件包索引,在 kconfig 文件和 package.json 文件中添加新版本的信息。
+
+4、在本地测试新版本软件包的下载和删除,以及安装是否正常,确保软件包可以被正常添加到工程中使用。
+
+5、向[软件包索引](https://github.com/RT-Thread/packages)提交 PR,并通知管理员合并。
+
diff --git a/rt-thread-version/rt-thread-standard/development-guide/sensor/figures/sensor.png b/rt-thread-version/rt-thread-standard/development-guide/sensor/figures/sensor.png
new file mode 100644
index 0000000000000000000000000000000000000000..b52961814a2c6c89f0a54dc42d0b740ce1d00afc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/sensor/figures/sensor.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/sensor/figures/sensor1.png b/rt-thread-version/rt-thread-standard/development-guide/sensor/figures/sensor1.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0cd5a84ffadf8a5c8628b0529cb288fcf935bcc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/sensor/figures/sensor1.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver.md b/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver.md
new file mode 100644
index 0000000000000000000000000000000000000000..fbc56a1a6a3fddcd0df8fdcf64593d17b50007fa
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver.md
@@ -0,0 +1,205 @@
+# 传感器驱动框架介绍
+
+## 背景与概述
+
+Sensor 是物联网重要的一部分,“Sensor 之于物联网”相当于“眼睛之于人类”。人没有眼睛就看不到这大千的花花世界,物联网没有了 Sensor 更是不能感知这变化万千的世界。
+
+现在,为物联网开发的 Sensor 已经很多了,有加速度计(Accelerometer),磁力计(Magnetometer),陀螺仪(Gyroscope),光感计(Ambient light sensor),接近光(Proximity),气压计(Barometer/pressure),湿度计(Humidometer)等等。这些传感器,世界上的各大半导体厂商都有出产,虽然增加了市场的可选择性,同时也加大了应用程序开发的难度。因为不同的传感器厂商、不同的传感器都需要配套自己独有的驱动才能运转起来,这样在开发应用程序的时候就需要针对不同的传感器做适配,自然加大了开发难度。为了降低应用开发的难度,增加传感器驱动的可复用性,我们设计了 Sensor 驱动框架。
+
+Sensor 驱动框架的作用是:为上层提供统一的操作接口,提高上层代码的可重用性;简化底层驱动开发的难度,只要实现简单的 ops(operations: 操作命令) 就可以将传感器注册到系统上。
+
+## 整体框架
+
+Sensor 驱动框架的整体架构图如下:
+
+
+
+它为上层提供的是标准 device 接口`open/close/read/write/control` ,为底层驱动提供的是简单的 ops 接口:`fetch_data/control`。并且框架支持 module(模块),为底层存在耦合的传感器设备提供服务。
+
+## 工作原理
+
+Sensor 设备其实是对标准设备 `rt_device` 的一个丰富,是在原有标准设备的基础上增加了 Sensor 自己独有的一部分 `属性` 和 `控制命令` ,如下图所示:
+
+
+
+整个 Sensor 设备包括两个部分:
+
+- 继承自标准设备的一些特性,包括:标准的控制接口 、`回调函数`、`device_id` 等。
+- Sensor 设备独有的部分,包括:`Sensor 的类型`、`相关的信息`、`特有的控制命令`、`ops`、以及一些 `数据的结构`。
+
+### sensor 的结构体
+
+Sensor 设备的结构体如下所示:
+
+```c
+struct rt_sensor_device
+{
+ struct rt_device parent; /* The standard device */
+
+ struct rt_sensor_info info; /* The sensor info data */
+ struct rt_sensor_config config; /* The sensor config data */
+
+ void *data_buf; /* The buf of the data received */
+ rt_size_t data_len; /* The size of the data received */
+
+ const struct rt_sensor_ops *ops; /* The sensor ops */
+
+ struct rt_sensor_module *module; /* The sensor module */
+
+ rt_err_t (*irq_handle)(rt_sensor_t sensor); /* Called when an interrupt is generated, registered by the driver */
+};
+typedef struct rt_sensor_device *rt_sensor_t;
+```
+
+### Sensor 的信息
+
+struct rt_sensor_info info 里存储的是一些与 Sensor 自身相关的信息,在 Sensor 设备注册的时候提供,在使用的过程中不应修改其内容。具体成员如下所示。
+
+```c
+struct rt_sensor_info
+{
+ rt_uint8_t type; /* The sensor type */
+ rt_uint8_t vendor; /* Vendor of sensors */
+ const char *model; /* model name of sensor */
+ rt_uint8_t unit; /* unit of measurement */
+ rt_uint8_t intf_type; /* Communication interface type */
+ rt_int32_t range_max; /* maximum range of this sensor's value. unit is 'unit' */
+ rt_int32_t range_min; /* minimum range of this sensor's value. unit is 'unit' */
+ rt_uint32_t period_min; /* Minimum measurement period,unit:ms. zero = not a constant rate */
+ rt_uint8_t fifo_max; /* Maximum depth of fifo */
+};
+```
+
+Sensor 的类型暂时只有以下几种,如果有新的传感器类型,可以提 PR 添加上。
+
+```c
+#define RT_SENSOR_CLASS_ACCE (1) /* Accelerometer */
+#define RT_SENSOR_CLASS_GYRO (2) /* Gyroscope */
+#define RT_SENSOR_CLASS_MAG (3) /* Magnetometer */
+#define RT_SENSOR_CLASS_TEMP (4) /* Temperature */
+#define RT_SENSOR_CLASS_HUMI (5) /* Relative Humidity */
+#define RT_SENSOR_CLASS_BARO (6) /* Barometer */
+#define RT_SENSOR_CLASS_LIGHT (7) /* Ambient light */
+#define RT_SENSOR_CLASS_PROXIMITY (8) /* Proximity */
+#define RT_SENSOR_CLASS_HR (9) /* Heart Rate */
+#define RT_SENSOR_CLASS_TVOC (10) /* TVOC Level */
+#define RT_SENSOR_CLASS_NOISE (11) /* Noise Loudness */
+#define RT_SENSOR_CLASS_STEP (12) /* Step sensor */
+#define RT_SENSOR_CLASS_FORCE (13) /* Force sensor */
+```
+其他的几个成员,分别是厂商、model(如:"mpu6050")、传感器数据的单位、通信接口类型、测量的最大范围、测量的最小范围、最小测量周期、硬件 FIFO 的最大深度。
+
+### Sensor 的配置
+
+Sensor 驱动框架抽象出了一些公共的配置选项,这些可配置的选项置于 `struct rt_sensor_config` 里, 成员如下:
+
+```c
+struct rt_sensor_config
+{
+ struct rt_sensor_intf intf; /* sensor interface config */
+ struct rt_device_pin_mode irq_pin; /* Interrupt pin, The purpose of this pin is to notification read data */
+ rt_uint8_t mode; /* sensor work mode */
+ rt_uint8_t power; /* sensor power mode */
+ rt_uint16_t odr; /* sensor out data rate */
+ rt_int32_t range; /* sensor range of measurement */
+};
+```
+这些配置项中的 intf 和 irq_pin 是为了将传感器和硬件解耦而抽象出来的,通过在底层初始化的时候传入 `struct rt_sensor_config` 这个参数,完成了通信接口的解耦。
+
+```c
+struct rt_sensor_intf
+{
+ char *dev_name; /* The name of the communication device */
+ rt_uint8_t type; /* Communication interface type */
+ void *user_data; /* Private data for the sensor. ex. i2c addr,spi cs,control I/O */
+};
+```
+其余的一些配置项是用 Sensor 特有控制命令控制的,如下所示:
+
+```c
+#define RT_SENSOR_CTRL_GET_ID (0) /* 读设备ID */
+#define RT_SENSOR_CTRL_GET_INFO (1) /* 获取设备信息 */
+#define RT_SENSOR_CTRL_SET_RANGE (2) /* 设置传感器测量范围 */
+#define RT_SENSOR_CTRL_SET_ODR (3) /* 设置传感器数据输出速率,unit is HZ */
+#define RT_SENSOR_CTRL_SET_MODE (4) /* 设置工作模式 */
+#define RT_SENSOR_CTRL_SET_POWER (5) /* 设置电源模式 */
+#define RT_SENSOR_CTRL_SELF_TEST (6) /* 自检 */
+```
+结合 ops 中的 control 接口使用,就可以完成传感器的配置了。
+
+### Sensor 数据的存储
+
+为了方便数据的解析,规定每一个类型的 Sensor 都有自己独有的数据结构,这些成员之间使用`共用体`以减少代码量。
+
+```c
+/* 3-axis Data Type */
+struct sensor_3_axis
+{
+ rt_int32_t x;
+ rt_int32_t y;
+ rt_int32_t z;
+};
+struct rt_sensor_data
+{
+ rt_uint32_t timestamp; /* The timestamp when the data was received */
+ rt_uint8_t type; /* The sensor type of the data */
+ union
+ {
+ struct sensor_3_axis acce; /* Accelerometer. unit: mG */
+ struct sensor_3_axis gyro; /* Gyroscope. unit: mdps */
+ struct sensor_3_axis mag; /* Magnetometer. unit: mGauss */
+ rt_int32_t temp; /* Temperature. unit: dCelsius */
+ rt_int32_t humi; /* Relative humidity. unit: permillage */
+ rt_int32_t baro; /* Pressure. unit: pascal (Pa) */
+ rt_int32_t light; /* Light. unit: lux */
+ rt_int32_t proximity; /* Distance. unit: centimeters */
+ rt_int32_t hr; /* Heat rate. unit: HZ */
+ rt_int32_t tvoc; /* TVOC. unit: permillage */
+ rt_int32_t noise; /* Noise Loudness. unit: HZ */
+ rt_uint32_t step; /* Step sensor. unit: 1 */
+ rt_int32_t force; /* Force sensor. unit: mN */
+ } data;
+};
+```
+
+### 特有的 ops
+
+ops(操作函数)包含两个函数指针, 一个的作用是获取传感器数据(fetch_data),另一个的作用是通过控制命令控制传感器(control)。
+
+```c
+struct rt_sensor_ops
+{
+ rt_size_t (*fetch_data)(struct rt_sensor_device *sensor, void *buf, rt_size_t len);
+ rt_err_t (*control)(struct rt_sensor_device *sensor, int cmd, void *arg);
+};
+```
+
+### 注册方式
+
+传感器驱动框架提供了一个 Sensor 注册函数,通过传入 Sensor 的控制块,名称,标志位和私有数据,就可以完成传感器设备的注册。
+
+```c
+int rt_hw_sensor_register(rt_sensor_t sensor,
+ const char *name,
+ rt_uint32_t flag,
+ void *data);
+```
+
+这样看来 Sensor 驱动框架依托于标准的设备框架,只要将传感器驱动对接到 Sensor 的 ops 上,并通过调用 `rt_hw_sensor_register` 函数注册为 Sensor 设备就可以通过标准的设备接口控制传感器了。
+
+### module支持
+
+module 的定义是解决底层有耦合的两个传感器而出现的,有些传感器既有加速度计的功能又有陀螺仪的功能,并且他们的FIFO是共用的,在 FIFO 模式下,只能将两个类型的传感器的数据同时读出,这就说明他们的数据是耦合的。
+
+为了解决这个问题,我们定义了 module 的类型
+
+```c
+struct rt_sensor_module
+{
+ rt_mutex_t lock; /* The module lock */
+
+ rt_sensor_t sen[RT_SENSOR_MODULE_MAX]; /* The module contains a list of sensors */
+ rt_uint8_t sen_num; /* Number of sensors contained in the module */
+};
+```
+里面包含有耦合的传感器的设备控制块指针,通过这个功能就可以在读取陀螺仪的数据的时候,同时更新加速度计的值,解决了底层耦合的问题。
diff --git a/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development.md b/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development.md
new file mode 100644
index 0000000000000000000000000000000000000000..5708673115337d1fca2eaa229d3724dcee46915b
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/development-guide/sensor/sensor_driver_development.md
@@ -0,0 +1,264 @@
+# 传感器驱动开发指南
+
+## 概述
+
+### 目的与概述
+
+本文档为 RT-Thread Sensor 驱动框架下传感器驱动的开发指南文档,给开发团队提供开发标准和规范。
+
+### 阅读对象
+
+- 进行传感器驱动开发的工程人员
+
+> [!NOTE]
+> 注:在阅读本篇文档之前,请先查看 [传感器驱动框架介绍](sensor_driver.md)。
+
+## 开发指南
+
+开发一个传感器驱动一般需要下面的几个步骤:调研与准备、开发、测试、提交。
+
+开发过程可以参考已经支持的传感器,点击查看[支持的传感器列表](../../programming-manual/device/sensor/sensor_list.md)。
+
+### 调研与准备
+
+根据 datasheet 或其他途径,了解传感器的特性,并记录下来,如下面几种:
+
+- 传感器类型
+- 通讯接口(i2c/spi/...)
+- 测量范围
+- 最短测量周期
+- 支持几种工作模式、电源模式
+
+### 开发
+
+开发的主要任务就是对接 Sensor 驱动框架的 ops 接口,然后注册为 Sensor 设备,进而能够通过驱动框架控制传感器的相关行为。
+
+#### ops 接口对接
+
+sensor 框架共给出了两个接口(`fetch_data` / `control`),需要在驱动中实现这两个接口。
+
+**fetchdata**
+
+作用: 获取传感器的数据。接口原型:
+
+```c
+rt_size_t (*fetch_data)(struct rt_sensor_device *sensor, void *buf, rt_size_t len);
+```
+
+Sensor 驱动框架当前默认支持 轮询(RT_DEVICE_FLAG_RDONLY)、中断(RT_DEVICE_FLAG_INT_RX)、FIFO(RT_DEVICE_FLAG_FIFO_RX) 这三种打开方式。需要在这里判断传感器的工作模式,然后再根据不同的模式返回传感器数据。
+
+如下所示:
+
+```c
+static rt_size_t xxx_acc_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
+{
+ if (sensor->parent.open_flag & RT_DEVICE_FLAG_RDONLY)
+ {
+ return _xxx_acc_polling_get_data(sensor, buf, len);
+ }
+ else if (sensor->parent.open_flag & RT_DEVICE_FLAG_INT_RX)
+ {
+ return _xxx_acc_int_get_data(sensor, buf, len);
+ }
+ else if (sensor->parent.open_flag & RT_DEVICE_FLAG_FIFO_RX)
+ {
+ return _xxx_acc_fifo_get_data(sensor, buf, len);
+ }
+ else
+ return 0;
+}
+```
+
+开发人员在返回数据时应先标识存储数据的数据类型,然后再填充数据域与时间戳,如下所示:
+
+```c
+sensor_data->type = RT_SENSOR_CLASS_ACCE
+sensor_data->data.acce.x = acceleration.x;
+sensor_data->data.acce.y = acceleration.y;
+sensor_data->data.acce.z = acceleration.z;
+sensor_data->timestamp = rt_sensor_get_ts();
+```
+
+> [!NOTE]
+> 注:- 时间戳的获取函数请使用 Sensor 驱动框架提供的时间戳获取函数 `rt_sensor_get_ts`。
+ - 在 FIFO 模式下底层数据可能会有耦合,需要使用 module,同时更新两个传感器的数据。
+ - 要将数据的单位转换为 Sensor 驱动框架里规定的数据单位。
+
+数据单位参考如下:
+
+| 传感器名称 | 类型 | 单位 | 说明 |
+| ---------- | ------------------------- | ----------- | -------------------------------------- |
+| 加速度计 | RT_SENSOR_CLASS_ACCE | mg | 1 g = 1000 mg, 1 g = 9.8 m/s2 |
+| 陀螺仪 | RT_SENSOR_CLASS_GYRO | mdps | 1 dps = 1000 mdps, dps(度每秒) |
+| 磁力计 | RT_SENSOR_CLASS_MAG | mGauss | 1 G = 1000 mGauss(毫高斯) |
+| 环境光 | RT_SENSOR_CLASS_LIGHT | lux | 亮度流明值 |
+| 接近光 | RT_SENSOR_CLASS_PROXIMITY | centimeters | 代表物体到传感器的距离大小,单位:厘米 |
+| 气压计 | RT_SENSOR_CLASS_BARO | Pa | 100 Pa = 1 hPa(百帕) |
+| 温度计 | RT_SENSOR_CLASS_TEMP | °c/10 | 0.1摄氏度 |
+| 湿度计 | RT_SENSOR_CLASS_HUMI | ‰ | 相对湿度RH,一般用‰表示 |
+| 心率计 | RT_SENSOR_CLASS_HR | bpm | 次数每分钟 |
+| 噪声 | RT_SENSOR_CLASS_NOISE | HZ | 频率单位 |
+| 计步计 | RT_SENSOR_CLASS_STEP | 1 | 步数:无量纲单位 1 |
+| 力传感器 | RT_SENSOR_UNIT_MN | mN | 压力的大小:10^-3 N |
+
+*注:后期会迭代增加新的传感器数据单位。*
+
+**control**
+
+```c
+rt_err_t (*control)(struct rt_sensor_device *sensor, int cmd, void *arg);
+```
+
+传感器的控制就是依靠这个接口函数实现的,通过判断传入的命令字的不同执行不同的操作,目前支持以下命令字:
+
+```c
+#define RT_SENSOR_CTRL_GET_ID (0) /* 读设备ID */
+#define RT_SENSOR_CTRL_GET_INFO (1) /* 获取设备信息(由框架实现,在驱动中不需要实现)*/
+#define RT_SENSOR_CTRL_SET_RANGE (2) /* 设置传感器测量范围 */
+#define RT_SENSOR_CTRL_SET_ODR (3) /* 设置传感器数据输出速率,unit is HZ */
+#define RT_SENSOR_CTRL_SET_MODE (4) /* 设置工作模式 */
+#define RT_SENSOR_CTRL_SET_POWER (5) /* 设置电源模式 */
+#define RT_SENSOR_CTRL_SELF_TEST (6) /* 自检 */
+```
+
+需要在驱动里实现这个函数,如下所示:
+
+```c
+static rt_err_t xxx_acc_control(struct rt_sensor_device *sensor, int cmd, void *args)
+{
+ rt_err_t result = RT_EOK;
+
+ switch (cmd)
+ {
+ case RT_SENSOR_CTRL_GET_ID:
+ result = _xxx_acc_get_id(sensor, args);
+ break;
+ case RT_SENSOR_CTRL_SET_RANGE:
+ result = _xxx_acc_set_range(sensor, (rt_int32_t)args);
+ break;
+ case RT_SENSOR_CTRL_SET_ODR:
+ result = _xxx_acc_set_odr(sensor, (rt_uint32_t)args & 0xffff);
+ break;
+ case RT_SENSOR_CTRL_SET_MODE:
+ result = _xxx_acc_set_mode(sensor, (rt_uint32_t)args & 0xff);
+ break;
+ case RT_SENSOR_CTRL_SET_POWER:
+ result = _xxx_acc_set_power(sensor, (rt_uint32_t)args & 0xff);
+ break;
+ case RT_SENSOR_CTRL_SELF_TEST:
+ break;
+ default:
+ return -RT_ERROR;
+ }
+ return result;
+}
+```
+> [!NOTE]
+> 注:这里需要注意传来参数的数据类型是由 struct rt_sensor_config 这个结构体规定的,因此 `RT_SENSOR_CTRL_SET_RANGE` 这个命令传来的参数是 `rt_int32_t` 类型的,需要经过强转一次,才可以得到正确的参数。
+
+然后 实现一个设备接口的结构体 ops 存储上面的接口函数,
+
+```c
+static struct rt_sensor_ops xxx_ops =
+{
+ xxx_acc_fetch_data,
+ xxx_acc_control
+};
+```
+
+#### 设备注册
+
+完成 Sensor 的 ops 的对接之后还要注册一个 sensor 设备,这样上层才能找到这个传感器设备,进而进行控制。
+
+设备的注册一共需要下面几步:创建一个 `rt_sensor_t` 的结构体指针,然后为结构体分配内存,并完成相关初始化。
+
+```c
+int rt_hw_xxx_init(const char *name, struct rt_sensor_config *cfg)
+{
+ rt_int8_t result;
+ rt_sensor_t sensor = RT_NULL;
+
+ sensor = rt_calloc(1, sizeof(struct rt_sensor_device));
+ if (sensor == RT_NULL)
+ return -1;
+
+ sensor->info.type = RT_SENSOR_CLASS_ACCE;
+ sensor->info.vendor = RT_SENSOR_VENDOR_STM;
+ sensor->info.model = "xxx_acce";
+ sensor->info.unit = RT_SENSOR_UNIT_MG;
+ sensor->info.intf_type = RT_SENSOR_INTF_I2C;
+ sensor->info.range_max = SENSOR_ACC_RANGE_16G;
+ sensor->info.range_min = SENSOR_ACC_RANGE_2G;
+ sensor->info.period_min = 100;
+
+ rt_memcpy(&sensor->config, cfg, sizeof(struct rt_sensor_config));
+ sensor->ops = &sensor_ops;
+
+ result = rt_hw_sensor_register(sensor, name, RT_DEVICE_FLAG_RDONLY | RT_DEVICE_FLAG_FIFO_RX, RT_NULL);
+ if (result != RT_EOK)
+ {
+ LOG_E("device register err code: %d", result);
+ rt_free(sensor);
+ return -RT_ERROR;
+ }
+
+ LOG_I("acc sensor init success");
+ return 0;
+}
+```
+
+> [!NOTE]
+> 注:- `rt_hw_sensor_register` 会为传入的 name 自动添加前缀,如`加速度计`类型的传感器会自动添加 `acce_` 的前缀。由于系统默认的设备名最长为 7 个字符,因此如果传入的名称超过3个字符的话会被裁掉。
+ - 注册时如传感器支持 FIFO 的话,需要添加 RT_DEVICE_FLAG_FIFO_RX 的标志位。
+ - 如果两个设备有耦合的话,需要利用 module 解耦。初始化一个 module,将两个传感器的设备控制块赋值到里面,并将 module 的地址赋值给两个传感器设备。框架会自动完成 module 锁的创建。
+
+其中传入参数 `struct rt_sensor_config *cfg` 是用来解耦硬件的通讯接口的,通过在底层驱动初始化的时候传入这个参数,实现硬件接口的配置。里面包含 `struct rt_sensor_intf` 这个结构体,
+
+```c
+struct rt_sensor_intf
+{
+ char *dev_name; /* The name of the communication device */
+ rt_uint8_t type; /* Communication interface type */
+ void *user_data; /* Private data for the sensor. ex. i2c addr,spi cs,control I/O */
+};
+```
+
+其中,type 表示接口的类型,dev_name 表示设备的名字,例如"i2c1"。user_data 是此接口类型的一些私有数据,如果是 I2C 的话,这里就是传感器对应的 i2c 地址,传入方式为 `(void*)0x55`。
+
+在底层驱动初始化时,需要先初始化此结构体,然后作为参数传入,以便完成通讯接口的解耦。类似这样:
+
+```c
+#define irq_pin GET_PIN(B, 0)
+
+int lps22hb_port(void)
+{
+ struct rt_sensor_config cfg;
+
+ cfg.intf.dev_name = "i2c1";
+ cfg.intf.user_data = (void *)0x55;
+ cfg.irq_pin.pin = irq_pin;
+ cfg.irq_pin.mode = PIN_MODE_INPUT_PULLDOWN;
+ rt_hw_xxx_init("xxx", &cfg);
+
+ return RT_EOK;
+}
+INIT_APP_EXPORT(lps22hb_port);
+```
+### 测试
+
+1. 通过 list_device 命令查看对应设备是否注册成功。
+2. 通过导出的测试函数 `sensor_polling/int/fifo `,判断能否成功读取数据。
+3. 测试其他的模式和控制接口
+
+### 提交
+
+Sensor 驱动需要按 `软件包` 的方式提交,具体的结构可以参考已有的 Sensor 软件包。具体的提交流程也可以参考文档中心:[软件包开发指南](https://www.rt-thread.org/document/site/development-guide/package/package/)
+
+### 注意事项
+
+- 动态分配内存时使用 `rt_calloc`,该 API 会将申请的内存初始化为 0,无需再手动清零。
+- 静态定义的变量请赋初值,未使用的初始化为 0。
+- 如果可能的话,请尽量支持多实例。需要注意下面的问题:
+ - 不能出现全局变量
+ - 所有的配置信息存储到 sensor 结构体里
+ - sensor 结构体里没有的配置,利用 rt_device 的 user_data 来存储
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/gt9147_reg.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/gt9147_reg.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7da71b4ca5fb95944850dfbf3d838ef2025fce1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/gt9147_reg.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/power_on.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/power_on.png
new file mode 100644
index 0000000000000000000000000000000000000000..09448cebde57606b7043ddd2577098a590db6da9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/power_on.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/read_data.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/read_data.png
new file mode 100644
index 0000000000000000000000000000000000000000..30dc4b0005b5332f71c78d903ced7bf0a7e6fb29
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/read_data.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch
new file mode 100644
index 0000000000000000000000000000000000000000..cd9f6d48ab866d06a75619a2482f82169b7dca32
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch
@@ -0,0 +1,31 @@
+@startuml
+
+participant Ӧó
+participant IO豸
+participant Touch豸
+participant Touch豸
+
+Touch豸->Touch豸: Touch豸
+
+Touch豸->Touch豸: עTouch豸 rt_hw_touch_register()
+
+Touch豸->IO豸: עIO豸 rt_device_register()
+
+Ӧó->IO豸: Touch豸 rt_device_find()
+
+Ӧó->IO豸: Touch豸 rt_device_open()
+IO豸->Touch豸:rt_touch_open()
+Touch豸->Touch豸: rt_touch_irq_init()
+
+Ӧó->IO豸: ȡTouch豸 rt_device_read()
+IO豸->Touch豸:rt_touch_read()
+Touch豸->Touch豸: touch_readpoint()
+
+Ӧó->IO豸: Touch豸 rt_device_control()
+IO豸->Touch豸:rt_touch_control()
+Touch豸->Touch豸: touch_control()
+
+Ӧó->IO豸: رTouch豸 rt_device_close()
+IO豸->Touch豸:rt_touch_close()
+Touch豸->Touch豸: rt_touch_irq_disable()
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch.vsd b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch.vsd
new file mode 100644
index 0000000000000000000000000000000000000000..cc6a75d593e0a95a4a41c0299b61443cbd232160
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch.vsd differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_hardware.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_hardware.png
new file mode 100644
index 0000000000000000000000000000000000000000..b51b6c7c7615de3f430a0cf437957f3287664382
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_hardware.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_irq.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_irq.png
new file mode 100644
index 0000000000000000000000000000000000000000..b24d76fb5880e50ce46c94e7f4dce56b7cb02886
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_irq.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_pull.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_pull.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c646ef258e1620ae19aad35033de3ec77c380cb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_pull.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_reg.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_reg.png
new file mode 100644
index 0000000000000000000000000000000000000000..b8670447aeefea9505a461555a79274ee4a58277
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_reg.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_uml.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_uml.png
new file mode 100644
index 0000000000000000000000000000000000000000..9564428e4c446dfbf513f1feb238311e0ab34449
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/touch_uml.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/figures/write_data.png b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/write_data.png
new file mode 100644
index 0000000000000000000000000000000000000000..84a3f8b034a0cbbf6494f01915c1e55005639982
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/development-guide/touch/figures/write_data.png differ
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/touch_device.md b/rt-thread-version/rt-thread-standard/development-guide/touch/touch_device.md
new file mode 100644
index 0000000000000000000000000000000000000000..2c9341092c1793af79596024b899900e8b50c358
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/development-guide/touch/touch_device.md
@@ -0,0 +1,227 @@
+# Touch 设备驱动框架详解
+
+## 概述
+
+### 特性
+RT-Thread 为方便管理和使用触摸设备和与 UI 的对接,抽象出了 Touch 设备驱动框架。应用程序可通过 I/O 设备管理接口来访问触摸设备硬件。Touch 设备驱动框架有以下特点:
+
+ * 为上层提供统一的操作接口,提高上层代码的可重用性;
+ * 简化底层驱动开发的难度,只要实现简单的 ops(operations: 操作命令) 就可以将触摸设备注册到系统上。
+ * 支持中断模式读取触摸点数据;
+ * Touch 框架支持读取多触摸点数据。
+
+### 软件架构
+
+Touch 设备驱动框架层级如下图所示:
+
+
+
+* I/O 设备管理器 `src\device.c`:
+ * 为应用程序提供通用的 I/O 设备管理接口,比如 rt_device_open(read/control) 等,用户使用这些接口访问 Touch 设备。
+ * 为设备驱动提供了通用的 IO 设备的操作方法接口 struct rt_device_ops(init/open/close/read/write/control),设备驱动需要实现这些接口。
+
+* Touch 设备驱动框架 `components\drivers\touch\touch.c`:
+ * Touch 设备驱动框架实现了 I/O 设备的操作方法接口: rt_touch_open(close/read/control) 。
+ * 为 Touch 设备驱动提供了 Touch 设备的操作方法 `struct rt_touch_ops`。
+ * 为 Touch 设备提供了注册接口 `rt_hw_touch_register`。
+
+* Touch 设备驱动
+ * 实现由 Touch 设备驱动框架提供的具体 Touch 设备的操作方法 `struct rt_touch_ops`。
+ * 注册 Touch 设备到操作系统;
+
+### Touch 设备使用序列
+
+ * Touch 设备驱动创建 Touch 设备实例,并将该 Touch 设备通过 `rt_hw_touch_register()` 注册到 Touch 设备驱动框架中。Touch 设备驱动框架通过 `rt_device_register()` 将 Touch 设备注册到 I/O 设备管理器中。
+
+ * Touch 设备驱动通过 Touch 设备驱动框架提供的接口访问硬件 Touch 控制器。
+
+Touch 设备注册和使用序列图如下图所示:
+
+
+
+## Touch 设备注册机制
+
+### Touch 设备控制块
+Touch 设备驱动框架定义了 Touch 设备模型,它从设备对象派生而来,Touch 设备模型定义如下面代码所示:
+
+```c
+struct rt_touch_device
+{
+ struct rt_device parent; /* 设备基类 */
+ struct rt_touch_info info; /* Touch 设备基本信息*/
+ struct rt_touch_config config; /* Touch 设备配置参数 */
+
+ const struct rt_touch_ops *ops; /* Touch 的操作方法 */
+ rt_err_t (*irq_handle)(rt_touch_t touch); /* 中断回调函数 */
+};
+```
+* 设备基类(parent):Touch 设备继承至设备基类 `struct rt_device` ,这个类是所有设备的父类。
+* Touch 设备基本信息(info):Touch 设备的基本信息例如分辨率、支持的触点个数等;
+* Touch 设备的配置参数(config):Touch 设备初始化的时候会根据配置参数初始化 Touch 芯片
+* Touch 设备的操作方法(ops):提供了 Touch 设备的操作方法,由 Touch 设备驱动实现。
+
+ ### Touch 的信息
+struct rt_touch_info info 里存储的是一些与 Touch 自身相关的信息,在使用的过程中不应修改其内容。具体成员如下所示:
+
+```c
+struct rt_touch_info
+{
+ rt_uint8_t type; /* 触摸芯片类型:电阻屏/电容屏 */
+ rt_uint8_t vendor; /* 厂商信息 */
+ rt_uint8_t point_num; /* 支持的触控点数 */
+ rt_int32_t range_x; /* X 轴分辨率 */
+ rt_int32_t range_y; /* Y 轴分辨率 */
+};
+```
+
+### Touch 的配置
+Touch 驱动框架抽象出了一些公共的配置选项,这些可配置的选项置于 `struct rt_touch_config` 里, 成员如下:
+```c
+struct rt_touch_config
+{
+ struct rt_device_pin_mode irq_pin; /* 中断引脚和模式 */
+ char *dev_name; /* 接口总线名称 */
+ void *user_data; /* 扩展数据 */
+};
+```
+
+### Touch 设备操作方法
+
+Touch 设备的操作方法是 Touch 设备驱动需要实现的主要功能, 结构体原型如下:
+
+```c
+/* Touch 设备的操作方法 */
+struct rt_touch_ops
+{
+ rt_size_t (*touch_readpoint)(struct rt_touch_device *touch, void *buf, rt_size_t touch_num);
+ rt_err_t (*touch_control)(struct rt_touch_device *touch, int cmd, void *arg);
+};
+```
+
+操作方法的描述如下表所示:
+
+| 接口 | 描述 |
+|:------------------|:------------------------------------|
+|touch_readpoint | 读取 Touch 设备触摸点信息 |
+|touch_control | 根据命令控制字 cmd 控制 Touch 设备 |
+
+#### 接口函数 `touch_readpoint`
+
+读取触点信息的接口函数 `touch_readpoint` 的描述如下表所示:
+
+| 参数 | 描述 |
+|----------|------------------------------------|
+| touch | Touch 设备句柄 |
+| buf | 读到的数据指针(数据的类型如下触点信息组成所示) |
+| touch_num | 需要读取的触点信息的个数 |
+| 返回 | —— |
+| rt_size_t | 实际读到的触点信息的个数 |
+
+
+#### 接口函数 `touch_control`
+
+控制 Touch 设备的接口函数 `touch_control` 的描述如下表所示:
+
+
+| 参数 | 描述 |
+|----------|------------------------------------|
+| touch | Touch 设备句柄 |
+| cmd | 控制命令 |
+| arg | 控制参数 |
+| 返回 | —— |
+| RT_EOK | 成功 |
+| 其他错误码 | 失败 |
+
+该接口根据控制命令 cmd 和控制参数 arg 控制 Touch 设备。
+参数 cmd 可取以下宏定义值:
+```c
+#define RT_TOUCH_CTRL_GET_ID (0)
+#define RT_TOUCH_CTRL_GET_INFO (1)
+#define RT_TOUCH_CTRL_SET_MODE (2)
+#define RT_TOUCH_CTRL_SET_X_RANGE (3)
+#define RT_TOUCH_CTRL_SET_Y_RANGE (4)
+#define RT_TOUCH_CTRL_SET_X_TO_Y (5)
+#define RT_TOUCH_CTRL_DISABLE_INT (6)
+#define RT_TOUCH_CTRL_ENABLE_INT (7)
+```
+
+### Touch 设备注册
+
+Touch 设备的实例化、操作方法的实现和注册都是在 Touch 驱动中完成。Touch 设备注册函数的源代码如下所示:
+```c
+/* 注册 Touch 设备 */
+int rt_hw_touch_register(rt_touch_t touch,
+ const char *name,
+ rt_uint32_t flag,
+ void *data)
+{
+ rt_int8_t result;
+ rt_device_t device;
+ RT_ASSERT(touch != RT_NULL);
+ /* 获取设备基本对象 */
+ device = &touch->parent;
+ /* 保存 Touch 设备的操作方法 */
+#ifdef RT_USING_DEVICE_OPS
+ device->ops = &rt_touch_ops;
+#else
+ device->init = RT_NULL;
+ device->open = rt_touch_open;
+ device->close = rt_touch_close;
+ device->read = rt_touch_read;
+ device->write = RT_NULL;
+ device->control = rt_touch_control;
+#endif
+ /* 设备类型为 Touch 设备 */
+ device->type = RT_Device_Class_Touch;
+ device->rx_indicate = RT_NULL;
+ device->tx_complete = RT_NULL;
+ device->user_data = data;
+ /* 注册 Touch 设备 */
+ result = rt_device_register(device, name, flag | RT_DEVICE_FLAG_STANDALONE);
+ ......
+}
+```
+
+注册 Touch 设备的时候 Touch 设备控制块会被初始化,操作方法也会保存,最终会调用 `rt_device_register()` 注册 Touch 到内核对象管理器中。RT-Thread 采用内核对象管理器来管理所有的内核对象,系统中所有的设备都属于设备类型对象。所有注册到系统的设备都被链接到了设备对象管理链表上。链接到对象管理器中的 Touch 设备如下图所示:
+
+
+
+## Touch 设备数据传输
+
+### 触点信息组成
+
+```c
+struct rt_touch_data
+{
+ rt_uint8_t event;
+ rt_uint8_t track_id;
+ rt_uint8_t width;
+ rt_uint16_t x_coordinate;
+ rt_uint16_t y_coordinate;
+ rt_tick_t timestamp;
+};
+```
+
+* event:触摸事件,包括抬起事件、按下事件和移动事件。
+* track_id:每个触摸点都有自己的触摸轨迹,这个数据用来保存触摸轨迹 ID。
+* width:触摸点宽度。
+* x_coordinate:触摸点 X 轴坐标。
+* y_coordinate:触摸点 Y 轴坐标。
+* timestamp:触摸事件时间戳。
+
+### 中断模式接收触点信息
+
+中断模式下接收数据有很好的实时性,这种模式下对 CPU 的占用率大大降低,应用程序只用在底层中断发生时去读取数据,不需要一直占用 CPU。使用中断接收模式时,首先应用程序打开 Touch 设备会指定打开标志为 `RT_DEVICE_FLAG_INT_RX`,此时 Touch 设备驱动框架会按照初始化时指定的中断引脚和模式去使能中断。
+
+中断接收数据的应用:
+
+一般情况下应用程序使用 Touch 接收数据会配合操作系统提供的通信机制一起使用。Touch 数据读取线程会等待一个信号量,获取信号量后才会去读取数据。Touch 接收数据回调函数则会释放信号量,通知应用程序有触摸事件发生。流程如下图所示:
+
+
+
+
+## 参考资料
+
+* 《Touch 设备使用指南》
+
+* 《Touch 设备驱动开发指南》
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/development-guide/touch/touch_gt9147_driver.md b/rt-thread-version/rt-thread-standard/development-guide/touch/touch_gt9147_driver.md
new file mode 100644
index 0000000000000000000000000000000000000000..cc0203ab211154a76012b69974bc9f8530d12270
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/development-guide/touch/touch_gt9147_driver.md
@@ -0,0 +1,437 @@
+# TOUCH 从设备 GT9147 驱动开发指南
+
+## 开发步骤
+
+本文档为 RT-Thread Touch 从设备驱动开发指南,给开发团队提供开发指导和规范。
+
+> [!NOTE]
+> 注:在阅读本篇文档之前,请先查看文档 [《Touch 设备驱动框架详解》](./touch_device.md)。
+
+Touch 从设备驱动开发可以按照如下的步骤进行:
+
+ 1. 创建 Touch 从设备。
+
+ 2. 实现 Touch 设备的操作方法。
+
+ 3. 注册 Touch 设备到操作系统。
+
+本文档将会以 GT9147 为例讲解 Touch 从设备驱动的具体实现。
+
+## GT9147 简介
+
+### 概述
+
+GT9147 是采用最新的电容检测技术,内置高性能微信号检测电路,可以很好地解决 LCD 干扰和共模干扰问题。软件方面,专门基于单层互容的电气环境设计,可支持 5 点触控。同时支持 HotKnot 功能。
+
+GT9147 可同时识别 5 个触摸点位的实时准确位置,移动轨迹及触摸面积。并可根据主控需要,读取相应点数的触摸信息。功能框图如下图所示:
+
+ 
+
+GT9147 采用 I2C 连接的方式读取转换数据。GT9147 相关使用引脚和 MCU 的连接方式如下表所示:
+
+| GT9147 引脚 | MCU 引脚 |
+|----------------|----------------------------------------------------------------------------------------------------------------------------|
+| RSTB(复位) | GPIO 输出,输出高、低来控制 GT9147 的 RESET 口为高或低。为保证可靠复位,建议 RESET 脚输出低 100μs 以上。 |
+| INT(中断) | GPIO 输入, 主控的 INT 口线需具有上升沿或下降沿中断触发功能,并且当其在输入态时,主控端必须设为悬浮态,取消内部上下拉功能; |
+| I2C | I2C 总线 |
+
+### 硬件配置
+
+这里以配置 GT9147 从设备地址为 0xBA/0xBB 为例,配置的时序图如下图所示:
+
+ 
+
+* 首先配置 INT 引脚和 RST 引脚均为输出。
+* INT 引脚输出低电平。
+* 拉低 RST 引脚,为保证可靠复位,建议 RST 引脚输出低 100 us 以上。
+* 拉高 RST 引脚,为保证可靠复位,建议 RST 引脚输出高 100 us 以上。
+* 配置 INT 引脚为输入,用于接收中断。
+
+GT9147 使用到了 RT-Thread 提供的 PIN 设备管理框架、I2C 设备管理框架和 Touch 设备管理框架。
+
+## 创建 Touch 从设备
+
+GT9147 的接口总线是基于 I2C 从设备来实现数据的传输,所以创建一个 Touch 从设备也就是创建一个 I2C 从设备。部分源代码如下所示:
+
+```c
+......
+static struct rt_i2c_client *gt9147_client;
+/* 创建 I2C 从设备 */
+gt9147_client = (struct rt_i2c_client *)rt_calloc(1, sizeof(struct rt_i2c_client));
+/* 查找I2C 从设备 */
+gt9147_client->bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name);
+if (gt9147_client->bus == RT_NULL)
+{
+ LOG_E("Can't find device\n");
+ return RT_ERROR;
+}
+/* 打开 I2C 接口总线 */
+if (rt_device_open((rt_device_t)gt9147_client->bus, RT_DEVICE_FLAG_RDWR) != RT_EOK)
+{
+ LOG_E("open device failed\n");
+ return RT_ERROR;
+}
+
+gt9147_client->client_addr = GT9147_ADDRESS_HIGH;
+/* 复位 I2C 从设备 */
+gt9147_soft_reset(gt9147_client);
+```
+
+用户创建 Touch 从设备时只需要传入 Touch 从设备使用的接口总线的名称,就可以创建对应 Touch 从设备。
+
+I2C 从设备框架中定义了 I2C 的从设备控制块,如下面代码所示:
+
+```c
+struct rt_i2c_client
+{
+ struct rt_device parent; /* 设备基类 */
+ struct rt_i2c_bus_device *bus; /* I2C 总线控制块指针 */
+ rt_uint16_t client_addr; /* 从设备地址 */
+};
+```
+## 实现 Touch 设备的操作方法
+
+### 读 GT9147 寄存器
+
+GT9147 是标准的 I2C 从设备,所以使用 I2C 总线读取数据,下图为使用 I2C 从 GT9147 读取数据的时序图:
+
+ 
+
+ GT9147 读时序的描述如下:
+ 1. 发送 I2C 总线起始信号;
+ 2. 发送 I2C 器件地址(写标志);
+ 3. 发送寄存器地址的高八位并等待应答;
+ 4. 发送寄存器地址的低八位并等待应答;
+ 5. 重新发送 I2C 起始信号;
+ 6. 发送 I2C 地址(读标志);
+ 7. 读数据(data1、2...n);
+ 8. 数据读取完成发送停止 I2C 总线信号
+
+读 GT9147 寄存器的源代码如下:
+
+```c
+static rt_err_t gt9147_read_regs(struct rt_i2c_client *dev,
+ rt_uint8_t *cmd_buf,
+ rt_uint8_t cmd_len,
+ rt_uint8_t read_len,
+ rt_uint8_t *read_buf)
+{
+ rt_int8_t res = 0;
+
+ struct rt_i2c_msg msgs[2];
+ /* I2C 总线写数据 */
+ msgs[0].addr = dev->client_addr; /* 从设备地址 */
+ msgs[0].flags = RT_I2C_WR; /* 写标志 */
+ msgs[0].buf = cmd_buf; /* 要写入的寄存器地址 */
+ msgs[0].len = cmd_len; /* 写入数据长度 */
+
+ msgs[1].addr = dev->client_addr; /* 从设备地址 */
+ msgs[1].flags = RT_I2C_RD; /* 读标志 */
+ msgs[1].buf = read_buf; /* 读到的数据指针 */
+ msgs[1].len = read_len; /* 要读的数据长度 */
+ /* 传输数据 */
+ if (rt_i2c_transfer(dev->bus, msgs, 2) == 2)
+ {
+ res = RT_EOK;
+ }
+ else
+ {
+ res = RT_ERROR;
+ }
+
+ return res;
+}
+```
+
+### 写 GT9147 寄存器
+
+下图为使用 I2C 向 GT9147 写入数据的时序图:
+
+
+
+ GT9147 写时序的描述如下:
+ 1. 发送 I2C 总线起始信号;
+ 2. 发送 I2C 器件地址(写标志);
+ 3. 发送寄存器地址的高八位并等待应答;
+ 4. 发送寄存器地址的低八位并等待应答;
+ 7. 发送要写入的数据(data1、2...n);
+ 8. 数据发送完成发送 I2C 总线停止信号
+
+写 GT9147 寄存器的源代码如下:
+
+```c
+static rt_err_t gt9147_write_reg(struct rt_i2c_client *dev, rt_uint8_t write_len, rt_uint8_t *write_data)
+{
+ rt_int8_t res = 0;
+ struct rt_i2c_msg msgs;
+
+ msgs.addr = dev->client_addr; /* 从设备地址 */
+ msgs.flags = RT_I2C_WR; /* 写标志 */
+ msgs.buf = write_data; /* 需要写入的数据 */
+ msgs.len = write_len; /* 写入数据的长度 */
+ /* I2C 总线传输数据 */
+ if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1)
+ {
+ res = RT_EOK;
+ }
+ else
+ {
+ res = RT_ERROR;
+ }
+
+ return res;
+}
+```
+
+### 读 GT9147 触摸点信息
+GT9147 触摸设备检测到有点按下时会更新状态寄存器和存放触点信息的寄存器,读取触点信息的代码如下所示:
+```c
+static rt_size_t gt9147_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num)
+{
+ /* 读触摸点状态寄存器 */
+ if (gt9147_read_regs(gt9147_client, cmd, 2, 1, &point_status) != RT_EOK)
+ {
+ LOG_D("read point failed\n");
+ read_num = 0;
+ goto exit_;
+ }
+
+ /* 根据参数读取 read_num 个触摸点数据 */
+ if (gt9147_read_regs(gt9147_client, cmd, 2, read_num * GT9147_POINT_INFO_NUM, read_buf) != RT_EOK)
+ {
+ LOG_D("read point failed\n");
+ read_num = 0;
+ goto exit_;
+ }
+
+ /*有触点抬起 */
+ if (pre_touch > touch_num)
+ {
+ for (read_index = 0; read_index < pre_touch; read_index++)
+ {
+ rt_uint8_t j;
+
+ for (j = 0; j < touch_num; j++)
+ {
+ read_id = read_buf[j * 8] & 0x0F;
+
+ if (pre_id[read_index] == read_id)
+ break;
+
+ if (j >= touch_num - 1)
+ {
+ rt_uint8_t up_id;
+ up_id = pre_id[read_index];
+ /* 抬起事件 */
+ gt9147_touch_up(buf, up_id);
+ }
+ }
+ }
+ }
+
+ /* 有触点按下 */
+ if (touch_num)
+ {
+ rt_uint8_t off_set;
+
+ for (read_index = 0; read_index < touch_num; read_index++)
+ {
+ off_set = read_index * 8;
+ read_id = read_buf[off_set] & 0x0f;
+ pre_id[read_index] = read_id;
+ input_x = read_buf[off_set + 1] | (read_buf[off_set + 2] << 8); /* x */
+ input_y = read_buf[off_set + 3] | (read_buf[off_set + 4] << 8); /* y */
+ input_w = read_buf[off_set + 5] | (read_buf[off_set + 6] << 8); /* size */
+ /* 按下事件 */
+ gt9147_touch_down(buf, read_id, input_x, input_y, input_w);
+ }
+ }
+ else if (pre_touch)
+ {
+ for(read_index = 0; read_index < pre_touch; read_index++)
+ {
+ /* 抬起事件 */
+ gt9147_touch_up(buf, pre_id[read_index]);
+ }
+ }
+
+ pre_touch = touch_num;
+
+exit_:
+ /* 清除触摸点状态寄存器 */
+ gt9147_write_reg(gt9147_client, 3, write_buf);
+ return read_num;
+}
+
+/* 抬起事件处理函数 */
+static void gt9147_touch_up(void *buf, int8_t id)
+{
+ read_data = (struct rt_touch_data *)buf;
+
+ if(s_tp_dowm[id] == 1)
+ {
+ s_tp_dowm[id] = 0;
+ read_data[id].event = RT_TOUCH_EVENT_UP;
+ }
+ else
+ {
+ read_data[id].event = RT_TOUCH_EVENT_NONE;
+ }
+
+ read_data[id].timestamp = rt_touch_get_ts();
+ read_data[id].width = pre_w[id];
+ read_data[id].x_coordinate = pre_x[id];
+ read_data[id].y_coordinate = pre_y[id];
+ read_data[id].track_id = id;
+
+ pre_x[id] = -1; /* last point is none */
+ pre_y[id] = -1;
+ pre_w[id] = -1;
+}
+
+/* 按下事件处理函数 */
+static void gt9147_touch_down(void *buf, int8_t id, int16_t x, int16_t y, int16_t w)
+{
+ read_data = (struct rt_touch_data *)buf;
+
+ if (s_tp_dowm[id] == 1)
+ {
+ read_data[id].event = RT_TOUCH_EVENT_MOVE;
+
+ }
+ else
+ {
+ read_data[id].event = RT_TOUCH_EVENT_DOWN;
+ s_tp_dowm[id] = 1;
+ }
+
+ read_data[id].timestamp = rt_touch_get_ts();
+ read_data[id].width = w;
+ read_data[id].x_coordinate = x;
+ read_data[id].y_coordinate = y;
+ read_data[id].track_id = id;
+
+ pre_x[id] = x; /* save last point */
+ pre_y[id] = y;
+ pre_w[id] = w;
+}
+```
+
+### 控制 GT9147
+Touch 框架中定义了 Touch 控制设备的宏,宏定义如下所示:
+
+```c
+#define RT_TOUCH_CTRL_GET_ID (0)
+#define RT_TOUCH_CTRL_GET_INFO (1)
+#define RT_TOUCH_CTRL_SET_MODE (2)
+#define RT_TOUCH_CTRL_SET_X_RANGE (3)
+#define RT_TOUCH_CTRL_SET_Y_RANGE (4)
+#define RT_TOUCH_CTRL_SET_X_TO_Y (5)
+#define RT_TOUCH_CTRL_DISABLE_INT (6)
+#define RT_TOUCH_CTRL_ENABLE_INT (7)
+```
+
+其中 `RT_TOUCH_CTRL_DISABLE_INT` 和 `RT_TOUCH_CTRL_ENABLE_INT` 已经在框架层中实现,所以从设备驱动需要实现剩下的宏定义,对接到 Touch 从设备的代码如下:
+
+```c
+static rt_err_t gt9147_control(struct rt_touch_device *device, int cmd, void *data)
+{
+ if (cmd == RT_TOUCH_CTRL_GET_ID)
+ {
+ return gt9147_get_product_id(gt9147_client, 6, data);
+ }
+
+ if (cmd == RT_TOUCH_CTRL_GET_INFO)
+ {
+ return gt9147_get_info(gt9147_client, data);
+ }
+
+ switch(cmd)
+ {
+ case RT_TOUCH_CTRL_SET_X_RANGE:
+ {
+ /* 设置 X 轴分辨率 */
+ func_set_x_range();
+ break;
+ }
+ case RT_TOUCH_CTRL_SET_Y_RANGE:
+ {
+ /* 设置 Y 轴分辨率 */
+ func_set_y_range();
+ break;
+ }
+ case RT_TOUCH_CTRL_SET_X_TO_Y:
+ {
+ /* 交换 X、Y 轴坐标 */
+ func_set_x_to_y();
+ break;
+ }
+ case RT_TOUCH_CTRL_SET_MODE:
+ {
+ /* 设置触摸芯片工作模式 */
+ func_set_mode();
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return RT_EOK;
+}
+```
+
+## 注册 Touch 设备
+Touch 设备的操作方法实现后,需要注册 Touch 设备到操作系统中,注册部分代码如下:
+
+```c
+......
+/* 保存 Touch 设备的操作方法 */
+static struct rt_touch_ops touch_ops =
+{
+ .touch_readpoint = gt9147_read_point,
+ .touch_control = gt9147_control,
+};
+
+int rt_hw_gt9147_init(const char *name, struct rt_touch_config *cfg)
+{
+ rt_touch_t touch_device = RT_NULL;
+
+ /* 创建 Touch 设备 */
+ touch_device = (rt_touch_t)rt_calloc(1, sizeof(struct rt_touch_device));
+
+ /* 硬件初始化 */
+ gt9147_hw_init();
+
+ /* 创建接口总线从设备 */
+ gt9147_client = (struct rt_i2c_client *)rt_calloc(1, sizeof(struct rt_i2c_client));
+ /* 查找接口总线设备 */
+ gt9147_client->bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name);
+ /* 打开接口总线 */
+ if (rt_device_open((rt_device_t)gt9147_client->bus, RT_DEVICE_FLAG_RDWR) != RT_EOK)
+ {
+ LOG_E("open device failed\n");
+ return RT_ERROR;
+ }
+
+ gt9147_client->client_addr = GT9147_ADDRESS_HIGH;
+ gt9147_soft_reset(gt9147_client);
+ /* 触摸设备类型是电容屏 */
+ touch_device->info.type = RT_TOUCH_TYPE_CAPACITANCE;
+ /* 厂商信息为 GT 系列 */
+ touch_device->info.vendor = RT_TOUCH_VENDOR_GT;
+ rt_memcpy(&touch_device->config, cfg, sizeof(struct rt_touch_config));
+ /* 保存操作方法 */
+ touch_device->ops = &touch_ops;
+
+ /* 注册 Touch 设备到操作系统 */
+ rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL);
+
+ ......
+}
+```
+
+## 参考资料
+《Touch 设备驱动框架详解》
+
diff --git a/rt-thread-version/rt-thread-standard/figures/02Software_framework_diagram.png b/rt-thread-version/rt-thread-standard/figures/02Software_framework_diagram.png
new file mode 100644
index 0000000000000000000000000000000000000000..0ece529620db6b5cf75d243088c5ca72b0ea48cf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/figures/02Software_framework_diagram.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/08_direct_run_files.gif b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/08_direct_run_files.gif
new file mode 100644
index 0000000000000000000000000000000000000000..823b59e71767cdef95494589984414f4f666d504
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/08_direct_run_files.gif differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/IoT_Board.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/IoT_Board.png
new file mode 100644
index 0000000000000000000000000000000000000000..be664acdff4b0a522799e70021d3621ca7be4328
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/IoT_Board.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/W60x_HW_origin.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/W60x_HW_origin.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c92885ececf6d579d4de34530b6234f0132bff0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/W60x_HW_origin.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/add_main_stack.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/add_main_stack.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7967af829d16201b4f22c821f9ab401455b8e45
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/add_main_stack.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/c-gen.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/c-gen.png
new file mode 100644
index 0000000000000000000000000000000000000000..918ccd4a3acf93966cb6da359b509474ff45e44f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/c-gen.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/check_memory.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/check_memory.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c2bc1483d4b1dda1b2e46dfcdc9b00cf82cfedf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/check_memory.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/check_pandora_examples.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/check_pandora_examples.png
new file mode 100644
index 0000000000000000000000000000000000000000..6bdb9cf5abb442a412a57af6292d0edf59c32515
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/check_pandora_examples.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/config_runtime.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/config_runtime.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1f2611c160037d1e314c6c1a80893d85786a45c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/config_runtime.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/en_connect_board.gif b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/en_connect_board.gif
new file mode 100644
index 0000000000000000000000000000000000000000..84c395b057e5001b9780b08ef0bafc238790aa71
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/en_connect_board.gif differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/mount_fs.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/mount_fs.png
new file mode 100644
index 0000000000000000000000000000000000000000..52d3f05360bfc7bd54572d3f50411ab0ed14a77d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/mount_fs.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/select_micropython.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/select_micropython.png
new file mode 100644
index 0000000000000000000000000000000000000000..54969586a1cab4a2f90914db8464331f2841f13a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/select_micropython.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/select_mpy_package.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/select_mpy_package.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a8792971d352d32d875a54056025cf587e91292
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/select_mpy_package.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/sparrow_example.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/sparrow_example.png
new file mode 100644
index 0000000000000000000000000000000000000000..af065469f36222206e016caf249c311b49568ebf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/sparrow_example.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/sparrow_one_board.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/sparrow_one_board.png
new file mode 100644
index 0000000000000000000000000000000000000000..70c08d950e980929edca1156dbd69cddd443e8b6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/sparrow_one_board.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/w601_examples.png b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/w601_examples.png
new file mode 100644
index 0000000000000000000000000000000000000000..9909488a8da001a79c3ef578845a6e4a519be71e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/assets/w601_examples.png differ
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/external_c_modules.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/external_c_modules.md
new file mode 100644
index 0000000000000000000000000000000000000000..3825ee20b783e7b603b3df6a080e5705094622de
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/external_c_modules.md
@@ -0,0 +1,53 @@
+# 为 MicroPython 扩展 C 模块
+
+当使用原生 MicroPython 进行开发时,你可能会遇到这样一些限制,比如官方没有实现自己想要的功能,或者你觉得这些实现不符合自己的工作需求。此时,添加自己的 C 模块到 MicroPython 中是一个不错的选择,你可以按照自己的想法,设计适合自己的 Python 函数调用。
+
+为了帮助各位开发者快速添加 C 模块,RT-Thread 提供了相应的辅助工具 [C 绑定代码自动生成器](https://summerlife.github.io/RT-MicroPython-Generator/)。该工具可以帮助开发者自动生成 C 代码和 MicroPython 之间的接口层,开发者只需将 C 语言编写的功能代码添加到指定位置,MicroPython 即可直接调用该功能。
+
+## Python 调用 C 函数的实现原理
+
+C 语言和 Python 是两种完全不同的语言,如何使用 MicroPython 来调用 C 语言所实现的函数是许多小伙伴非常疑惑的地方。简单来说,这个问题的关键点在于,如何用 C 语言的形式在 MicroPython 源代码中**表述函数的入参和出参**。我举一个例子来讲解这个问题, 请观察如下 Python 函数:
+
+```python
+def add(a, b):
+ return a + b
+```
+
+该函数有两个入参,返回一个参数。此时如果能用 C 语言表示该 **Python 函数的输入输出参数**,就可以将一个实现该功能的 C 函数对接到 MicroPython 中。
+
+### 添加用户函数到 MicroPython
+
+假设上述函数的参数类型都为整形,通过自动生成器可以得到如下样板函数:
+
+```c
+STATIC mp_obj_t add(
+ mp_obj_t arg_1_obj,
+ mp_obj_t arg_2_obj) {
+ mp_int_t arg_1 = mp_obj_get_int(arg_1_obj); /* 通过 Python 获取的第一个整形参数 arg_1 */
+ mp_int_t arg_2 = mp_obj_get_int(arg_2_obj); /* 通过 Python 获取的第二个整形参数 arg_2 */
+ mp_int_t ret_val;
+
+ /* Your code start! */
+
+ ret_val = arg_1 + arg_2; /* 处理入参 arg_1 和 arg_2,并将结果赋给返回值 ret_val */
+
+ /* Your code end! */
+
+ return mp_obj_new_int(ret_val); /* 向 python 返回整形参数 ret_val */
+}
+MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add);
+```
+
+生成器会处理好需要导出到 MicroPython 的函数的入参和出参,而开发者只需要编写相应的代码来处理这些输入参数,并且把返回值赋给输出参数即可。 通过包含头文件的方式,可以调用先前编写的 C 函数来对输入参数进行处理,或者根据输入参数来执行相应的动作,添加控制硬件的驱动的原理也是一样的。
+
+最终使用 Python 调用 C 函数的效果如下:
+
+```python
+>>> import userfunc
+>>> userfunc.add(666,777)
+1443
+```
+
+### 添加用户模块到 MicroPython
+
+添加用户模块到 MicroPython 中也不难,首先应当熟练掌握上述添加 C 函数的过程,然后参考 PR [add module userfunc to MicroPython](https://github.com/RT-Thread-packages/micropython/pull/144) 来添加属于自己的模块,该 PR 实现了添加 `userfunc` 模块到 MicroPython 的功能,你可以按照同样的方式将自己编写的模块注册到 MicroPython 中,要注意仔细查看这个 PR 中修改的 4 个文件,不要漏掉修改的细节。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/firmware-develop.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/firmware-develop.md
new file mode 100644
index 0000000000000000000000000000000000000000..7fe121f47f7dbb0be2cf3d5116800d25c321cada
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/firmware-develop.md
@@ -0,0 +1,103 @@
+# MicroPython 固件开发指南
+
+如果手上没有官方支持固件的开发板,就需要自己来动手制作 MicroPython 固件了。由于 RT-Thread 官方提供了 [MicroPython 软件包](https://github.com/RT-Thread-packages/micropython),并且 MicroPython 底层和硬件绑定时对接了 RT-Thread 驱动框架,因此可以很方便地在运行了 RT-Thread 的板卡上将 MicroPython 跑起来。
+
+**注意**:RT-Thread MicroPython 需要运行在 **RT-Thread 3.0** 版本以上。
+
+## 选择合适的 BSP 平台
+
+RT-Thread MicroPython mini 版本占用资源最大不超过:
+
+- ROM : 190KB
+- RAM : 20KB
+
+只要系统资源满足上述要求,常见的许多开发板都可以运行 MicroPython,例如 STM32 系列 BSP。
+
+接下来我们以 `rt-thread\bsp\stm32\stm32f407-atk-explorer` 上的 MDK 工程为例,讲解如何在 BSP 的基础上制作 MicroPython 固件。
+
+## 获取 MicroPython 软件包
+
+先使用 `pkgs --upgrade` 命令更新软件包列表,然后通过 env 工具选中 MicroPython 软件包,最后使用 `pkgs -update` 命令将软件包拉取到本地。
+
+
+
+## 增大 main 线程栈
+
+为了能后续在 main 线程中启动 MicroPython 运行时环境,需要增大 main 线程的栈大小,这里我们将栈大小增加到 8k。
+
+
+
+## 配置 MicroPython 运行环境堆大小
+
+接下来根据板卡内存实际剩余情况来给 MicroPython 运行环境分配内存,这里填写的数值越大,就能运行更大代码量的 Python 程序。但是如果这里填写的数值超过了实际可分配内存,就可能会出现无法分配内存而报错。因此在配置此项目之前,需要对系统 RAM 资源的分配情况有一定了解。
+
+### 查看系统剩余内存
+
+重新生成工程,编译下载后通过 `msh` 的 `free` 命令来查看内存使用情况。
+
+
+
+### 配置系统
+
+通过上一步查询的内存分配情况,对系统 RAM 资源有了一定的了解。在本次示例中,我们分配 20k 内存用于 MicroPython 运行时环境。后续如果想要运行更多 MicroPython 代码,可以将更多空余内存分配给 MicroPython 运行时环境,配置如下图所示:
+
+
+
+## 在根目录挂载文件系统
+
+最后要确保系统中 `/` 目录挂载了文件系统。有了文件系统,后续才能使用 [**MicroPython 开发环境**](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) 将 Python 代码文件同步到板卡中来运行,本次示例中将使用 elm-fat 文件系统,需要对系统进行如下配置:
+
+
+
+配置完成后,记得要使用 `scons --target=mkd5` 重新生成工程,使配置在工程中生效。
+
+## 在 main 线程中启动 MicroPython
+
+最后要在 main 线程中启动 MicroPython,代码修改如下所示:
+
+```c
+#include
+#include
+#include
+#include
+#include
+
+/* 文件系统所在分区名称,根据实际情况填写 */
+#define FS_PARTITION_NAME "W25Q128"
+
+int main(void)
+{
+ /* 挂载 elm 文件系统到 / 目录,如果你所使用的开发板没有文件系统也可以跳过这一步 */
+ if (dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
+ {
+ rt_kprintf("Filesystem initialized!");
+ }
+ else
+ {
+ /* 如果挂载失败,则尝试在文件系统分区重新创建文件系统 */
+ dfs_mkfs("elm", FS_PARTITION_NAME);
+ /* 尝试重新挂载文件系统 */
+ if (dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
+ {
+ /* 仍然挂载文件系统失败,请自行检查失败原因 */
+ rt_kprintf("Failed to initialize filesystem!");
+ }
+ }
+
+ rt_thread_mdelay(100);
+
+ while(1)
+ {
+ /* 在这里让程序进入循环,通过这种方式实现 MicroPython 的软复位*/
+ extern void mpy_main(const char *filename);
+ /* 启动 MicroPython */
+ mpy_main(NULL);
+ }
+
+ return RT_EOK;
+}
+```
+
+重新编译工程并下载程序到板卡中,就会在 main 线程中自动启动 MicroPython,接下来就可以使用 [**RT-Thread MicroPython 开发环境**](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython) 来进行应用开发了。 通过开发环境连接到开发板,即可看到 MicroPython 的交互环境 REPL,如下图所示:
+
+
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/introduction.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/introduction.md
new file mode 100644
index 0000000000000000000000000000000000000000..1fedb66e156cec416c6219d1d9c9a10731035920
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/introduction.md
@@ -0,0 +1,49 @@
+# MicroPython 入门必读
+
+本文档将初步介绍 MicroPython 的基本概念,RT-Thread MicroPython 的特性与优势,以及可以被用在哪些领域。
+
+## 主要特性
+
+- MicroPython 是 Python 3 编程语言的一种精简而高效的实现,它包含 Python 标准库的一个子集,并被优化为在微控制器和受限环境中运行。
+
+- RT-Thread MicroPython 可以运行在任何搭载了 RT-Thread 操作系统并且有一定资源的嵌入式平台上。
+
+- MicroPython 可以运行在有一定资源的开发板上,给你一个低层次的 Python 操作系统,可以用来控制各种电子系统。
+
+- MicroPython 富有各种高级特性,比如交互式提示、任意精度整数、闭包函数、列表解析、生成器、异常处理等等。
+
+- MicroPython 的目标是尽可能与普通 Python 兼容,使开发者能够轻松地将代码从桌面端转移到微控制器或嵌入式系统。程序可移植性很强,因为不需要考虑底层驱动,所以程序移植变得轻松和容易。
+
+## MicroPython 的优势
+
+- Python 是一款容易上手的脚本语言,同时具有强大的功能,语法优雅简单。使用 MicroPython 编程可以降低嵌入式的开发门槛,让更多的人体验嵌入式的乐趣。
+- 通过 MicroPython 实现硬件底层的访问和控制,不需要了解底层寄存器、数据手册、厂家的库函数等,即可轻松控制硬件。
+- 外设与常用功能都有相应的模块,降低开发难度,使开发和移植变得容易和快速。
+
+## MicroPython 的应用领域
+
+- MicroPython 在嵌入式系统上完整实现了 Python3 的核心功能,可以在产品开发的各个阶段给开发者带来便利。
+- 通过 MicroPython 提供的库和函数,开发者可以快速控制 LED、液晶、舵机、多种传感器、SD、UART、I2C 等,实现各种功能,而不用再去研究底层硬件模块的使用方法,翻看寄存器手册。这样不但降低了开发难度,而且减少了重复开发工作,可以加快开发速度,提高开发效率。以前需要较高水平的嵌入式工程师花费数天甚至数周才能完成的功能,现在普通的嵌入式开发者用几个小时就能实现类似的功能。
+- 随着半导体技术的不断发展,芯片的功能、内部的存储器容量和资源不断增加,成本不断降低,可以使用 MicroPython 来进行开发设计的应用领域也会越来越多。
+
+### 产品原型验证
+
+- 众所周知,在开发新产品时,原型设计是一个非常重要的环节,这个环节需要以最快速的方式设计出产品的大致模型,并验证业务流程或者技术点。与传统开发方法相比,使用 MicroPython 对于原型验证非常有用,让原型验证过程变得轻松,加速原型验证过程。
+- 在进行一些物联网功能开发时,网络功能也是 MicroPython 的长处,可以利用现成的众多 MicroPython 网络模块,节省开发时间。而这些功能如果使用 C/C++ 来完成,会耗费几倍的时间。
+
+### 硬件测试
+
+- 嵌入式产品在开发时,一般会分为硬件开发及软件开发。硬件工程师并不一定都擅长软件开发,所以在测试新硬件时,经常需要软件工程师参与。这就导致软件工程师可能会耗费很多时间帮助硬件工程师查找设计或者焊接问题。有了 MicroPython 后,将 MicroPython 固件烧入待测试的新硬件,在检查焊接、连线等问题时,只需使用简单的 Python 命令即可测试。这样,硬件工程师一人即可搞定,再也不用麻烦别人了。
+
+### 创客 DIY
+
+- MicroPython 无需复杂的设置,不需要安装特别的软件环境和额外的硬件,使用任何文本编辑器就可以进行编程。大部分硬件功能,使用一个命令就能驱动,不用了解硬件底层就能快速开发。这些特性使得 MicroPython 非常适合创客使用来开发一些有创意的项目。
+- 下面是使用 MicroPython 开发的一些 DIY 项目:
+ - [显示温湿度的 WIFI 时钟](https://www.bilibili.com/video/av15929152?from=search&seid=16285206333541196172)
+ - [OpenMV 智能摄像头](https://www.bilibili.com/video/av16418889?from=search&seid=16285206333541196172)
+ - [快速实现人脸识别](https://www.bilibili.com/video/av73853903?from=search&seid=9793819178982436353)
+ - [搭建 MQTT 服务器](http://www.360doc.com/content/17/1218/22/8473307_714341237.shtml)
+
+### 教育
+
+- MicroPython 使用简单、方便,非常适合于编程入门。在校学生或者业余爱好者都可以通过 MicroPython 快速的开发一些好玩的项目,在开发的过程中学习编程思想,提高自己的动手能力。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-ide.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-ide.md
new file mode 100644
index 0000000000000000000000000000000000000000..c4589a9334bd672e9674acd055111693071aeae3
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-ide.md
@@ -0,0 +1,24 @@
+# MicroPython IDE
+
+RT-Thread 为广大开发者提供了 VSCode 最好用的 MicroPython 插件 来帮助大家使用 MicroPython 来开发应用程序。该插件为 MicroPython 开发提供了功能强大的开发环境,主要特性如下:
+
+- 便捷的开发板连接方式(串口、网络、USB)
+- 支持基于 MicroPython 的代码智能补全与语法检查
+- 支持 MicroPython REPL 交互环境
+- 提供丰富的代码示例与 demo 程序
+- 提供工程同步功能
+- 支持下载单个文件或文件夹至开发板
+- 支持在内存中快速运行代码文件功能
+- 支持运行代码片段功能
+- 支持多款主流 MicroPython 开发板
+- 支持 Windows、Ubuntu、Mac 操作系统
+
+### 安装 IDE 开发环境
+
+开发者可以通过 RT-Thread MicroPython IDE 来快速开发 MicroPython 应用,下图展示了 IDE 的快速调试功能:
+
+
+
+可通过查看如下文档进一步了解并使用 RT-Thread MicroPython IDE:
+
+- [RT-Thread MicroPython Develop Environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython)
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-librarys.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-librarys.md
new file mode 100644
index 0000000000000000000000000000000000000000..604a86755ab61d6b47cb8e0551354203df1f692a
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython-librarys.md
@@ -0,0 +1,47 @@
+# MicroPython 库
+
+## MicroPython 标准库
+
+- [Builtin functions and exceptions](std-librarys/builtins.md) – 内置函数与异常
+- [cmath](std-librarys/cmath.md) – 复数运算函数功能
+- [gc](std-librarys/gc.md) – 控制垃圾收集器
+- [math](std-librarys/math.md) – 数学函数功能
+- [sys](std-librarys/sys.md) – 系统特定功能
+- [uarray](std-librarys/uarray.md) – 数组存储功能
+- [ubinascii](std-librarys/ubinascii.md) – 二进制与 ASCII 码转换功能
+- [ucollections](std-librarys/ucollections.md) – 集合与容器类型
+- [uerrno](std-librarys/uerrno.md) – 系统错误码
+- [uhashlib](std-librarys/uhashlib.md) – 哈希算法
+- [uheapq](std-librarys/uheapq.md) – 堆队列算法
+- [uio](std-librarys/uio.md) – 输入输出流
+- [ujson](std-librarys/ujson.md) – JSON 编解码
+- [uos](std-librarys/uos.md) – 基本的操作系统服务
+- [ure](std-librarys/ure.md) – 正则表达式
+- [uselect](std-librarys/uselect.md) – 在一组 streams 上等待事件
+- [usocket](std-librarys/usocket.md) – socket 模块
+- [ussl](std-librarys/ussl.md) – SSL/TLS 模块
+- [ustruct](std-librarys/ustruct.md) – 原生数据类型的打包和解包
+- [utime](std-librarys/utime.md) – 时间相关功能
+- [uzlib](std-librarys/uzlib.md) – zlib 解压
+- [_thread](std-librarys/_thread.md) – 多线程支持
+
+## MicroPython 特定库
+
+在 RT-Thread 移植的 MicroPython 版本中,实现了如下特定功能库:
+
+- [micropython](spec-librarys/micropython.md) – 实现 MicroPython 内部功能访问与控制
+- [rtthread](spec-librarys/rtthread.md) – RT-Thread 系统功能模块
+- [machine](spec-librarys/machine.md) – 硬件控制模块
+ - [Pin](spec-librarys/machine/Pin.md)
+ - [I2C](spec-librarys/machine/I2C.md)
+ - [SPI](spec-librarys/machine/SPI.md)
+ - [UART](spec-librarys/machine/UART.md)
+ - [LCD](spec-librarys/machine/LCD.md)
+ - [RTC](spec-librarys/machine/RTC.md)
+ - [PWM](spec-librarys/machine/PWM.md)
+ - [ADC](spec-librarys/machine/ADC.md)
+ - [WDT](spec-librarys/machine/WDT.md)
+ - [TIMER](spec-librarys/machine/Timer.md)
+
+- [network](spec-librarys/network.md) – 网络功能配置模块
+ - [wlan](spec-librarys/network/wlan.md)
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_pandora_iot_board.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_pandora_iot_board.md
new file mode 100644
index 0000000000000000000000000000000000000000..83e4df985bff0118c10ad2b10dde88d23447a3cc
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_pandora_iot_board.md
@@ -0,0 +1,76 @@
+# MicroPython for Pandora IoT Board
+
+
+
+[**IoT Board 潘多拉**](https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-18400369818.12.2ba47ea5PzJxZx&id=583843059625) 是 RT-Thread 推出的一款物联网开发板,它给开发者带来了物联网时代的无限可能。而现在,它已经不仅仅是一块简单的物联网开发板,因为它已经全面支持 **MicroPython** 。在 IoT Board 上,你将会体验到有别于传统的,前所未有的开发方式。
+
+借助于 MicroPython,你将有能力使用 Python 语言控制所有硬件外设,体验高级语言带来的便利特性,与此同时你还可以利用高级软件库快速实现你的 IoT 构想。
+
+## 硬件支持
+
+Pandora MicroPython 固件硬件功能如下所示:
+
+| 外设名称 | 引脚号 | 简介 |
+| -------- | ---------------------------------------------- | ----------------------------------------- |
+| pin | PA4 PA8, PB8-9 PB10-15, PC2 PC4 PC6-7, PD12-15 | 开发板引出的可自由分配的 IO,支持引脚中断 |
+| led | PE7 | 红色 led 灯 |
+| rgb | R: PE7, G: PE8, B: PE9 | rgb 灯 |
+| key | KEY0: PD10, KEY1: PD9, KEY2: PD8 | 输入按键 |
+| uart1 | PA9, PA10 | 串口1 |
+| i2c | | 软件 i2c 可选择任意 pin |
+| spi | | 软件 spi 可选择任意引出 pin |
+| adc | PC4 | adc1,通道 13 |
+| pwm | PB0 | pwm3, 通道 3, 用于红外发射 |
+| timer | | 硬件定时器 15 |
+| wdt | | 看门狗 |
+| rtc | | 实时时钟 |
+| beeper | PB2 | 蜂鸣器 |
+| lcd | | lcd 显示屏 |
+| wifi | | wifi 网络连接 |
+| aht10 | CLK: PD6, SDA: PC1 | 温湿度传感器 |
+| ap3216c | CLK: PC0, SDA: PC1 | 接近与光强传感器 |
+| icm20608 | CLK: PC0, SDA: PC1 | 六轴传感器 |
+
+
+## 入门必读
+
+如果您从来没有了解过 MicroPython, 可以阅读这篇简短的文章来 [带你入门 MicroPython](introduction.md)。
+
+## 开启 MicroPython 之旅
+
+推荐遵循如下步骤开始进行 MicroPython 开发:
+
+- 在您的开发板上烧录合适的固件
+- 在 PC 机上安装 RT-Thread MicroPython 开发环境并连接上开发板
+
+接下来就可以尽情挥洒您的创意了,更详细的内容可以点击下文中的链接来进一步了解。
+
+### 下载合适的固件
+
+- [Pandora IoT Board firmware](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20)
+
+### 安装 IDE 开发环境
+
+- [RT-Thread MicroPython develop environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython)
+
+## 开发资料
+
+### 示例程序
+
+以下示例程序可以在 RT-Thread MicroPython IDE 开发环境中直接添加到工程:
+
+
+
+### MicroPython 模块详解
+
+- [MicroPython Librarys](micropython-librarys.md)
+
+
+## 联系我们
+
+如果在使用的过程中遇到问题,您可以用如下方式联系我们:
+
+- 在 github 上提交 issue
+- [RT-Thread MicroPython 官方论坛](https://www.rt-thread.org/qa/forum.php?mod=forumdisplay&fid=2&filter=typeid&typeid=20)
+
+- RT-Thread MicroPython 交流 QQ 群:703840633
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_sparrow_one_board.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_sparrow_one_board.md
new file mode 100644
index 0000000000000000000000000000000000000000..c3b815417f2371902fe2b3d2335dbdb5965d5299
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_sparrow_one_board.md
@@ -0,0 +1,65 @@
+# MicroPython for sparrow one board
+
+
+
+[**麻雀一号开发板**](https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-5210898174.2.29401ae39JyGKY&id=606684373403) 是 RT-Thread 推出的一款物联网音视频开发板,现在它已经全面支持 **MicroPython** 。在麻雀一号开发板上,你将会体验到有别于传统的,前所未有的开发方式。
+
+借助于 MicroPython,你将有能力使用 Python 语言控制所有硬件外设,体验高级语言带来的便利特性,与此同时你还可以利用高级软件库快速实现你的 IoT 构想。
+
+## 硬件支持
+
+麻雀一号开发板固件硬件功能如下所示:
+
+| 外设名称 | 简介 |
+| --------- | ---------------------------------------------------------- |
+| key | 输入按键 |
+| uart1 | 串口1 |
+| lcd | lcd 显示屏 |
+| wifi | wifi 网络连接 |
+| bluetooth | 蓝牙 |
+| player | 扬声器,音频播放 |
+| recorder | 麦克风,录音功能 |
+| camera | 摄像头,可拍照并存入文件系统,开启 TCP Server 查看实时图像 |
+
+## 入门必读
+
+如果您从来没有了解过 MicroPython, 可以阅读这篇简短的文章来 [带你入门 MicroPython](https://github.com/RT-Thread-packages/micropython/blob/master/docs/introduction.md)。
+
+## 开启 MicroPython 之旅
+
+推荐遵循如下步骤开始进行 MicroPython 开发:
+
+- 在您的开发板上烧录合适的固件
+- 在 PC 机上安装 RT-Thread MicroPython 开发环境并连接上开发板
+
+接下来就可以尽情挥洒您的创意了,更详细的内容可以点击下文中的链接来进一步了解。
+
+### 下载合适的固件
+
+- [Sparrow One Board firmware](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20)
+
+### 安装 IDE 开发环境
+
+- [RT-Thread MicroPython develop environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython)
+
+## 开发资料
+
+### 示例程序
+
+以下示例程序可以在 RT-Thread MicroPython IDE 开发环境中直接添加到工程:
+
+
+
+### MicroPython 模块详解
+
+- [MicroPython Librarys](https://github.com/RT-Thread-packages/micropython/blob/master/docs/micropython-librarys.md)
+
+
+## 联系我们
+
+如果在使用的过程中遇到问题,您可以用如下方式联系我们:
+
+- 在 github 上提交 issue
+- [RT-Thread MicroPython 官方论坛](https://www.rt-thread.org/qa/forum.php?mod=forumdisplay&fid=2&filter=typeid&typeid=20)
+
+- RT-Thread MicroPython 交流 QQ 群:703840633
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_w601_iot_board.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_w601_iot_board.md
new file mode 100644
index 0000000000000000000000000000000000000000..ac7e19742b89a19f699a12ea9939b27a16f47c59
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/micropython_for_w601_iot_board.md
@@ -0,0 +1,74 @@
+# MicroPython for W601 IoT Board
+
+
+
+[**W601 IoT Board**](https://item.taobao.com/item.htm?spm=a230r.1.14.13.7c5b4a9bS2LYUD&id=602233847745&ns=1&abbucket=17#detail) 是 RT-Thread 推出的一款物联网开发板,它给开发者带来了物联网时代的无限可能。而现在,它已经不仅仅是一块简单的物联网开发板,因为它已经全面支持 **MicroPython** 。在 IoT Board 上,你将会体验到有别于传统的,前所未有的开发方式。
+
+借助于 MicroPython,你将有能力使用 Python 语言控制所有硬件外设,体验高级语言带来的便利特性,与此同时你还可以利用高级软件库快速实现你的 IoT 构想。
+
+## 硬件支持
+
+W601 IoT Board MicroPython 固件硬件功能如下所示:
+
+| 外设名称 | 引脚号 | 简介 |
+| -------- | -------------------------------------- | ----------------------------------------- |
+| pin | PA11, PB4、10-14 、17-18、23-26、30-31 | 开发板引出的可自由分配的 IO,支持引脚中断 |
+| led | PA13 | 红色 led 灯 |
+| rgb | R: PA13, G: PA14, B: PA15 | rgb 灯 |
+| key | KEY0: PA7, KEY1: PA6, | 输入按键 |
+| uart1 | PA4, PA5 | 串口1 |
+| i2c | | 软件 i2c 可选择任意 pin |
+| spi | | 软件 spi 可选择任意引出 pin |
+| adc | PB23 - 26 | adc,通道 5 - 8 |
+| pwm | PB17、PB18 | pwm1, 通道 1、2 |
+| timer | | 硬件定时器 1 |
+| wdt | | 看门狗 |
+| rtc | | 实时时钟 |
+| beeper | PB15 | 蜂鸣器 |
+| lcd | | lcd 显示屏 |
+| wifi | | wifi 网络连接 |
+| aht10 | CLK: PA0, SDA: PA1 | 温湿度传感器 |
+| ap3216c | CLK: PA2, SDA: PA1 | 接近与光强传感器 |
+
+## 入门必读
+
+如果您从来没有了解过 MicroPython, 可以阅读这篇简短的文章来 [带你入门 MicroPython](https://github.com/RT-Thread-packages/micropython/blob/master/docs/introduction.md)。
+
+## 开启 MicroPython 之旅
+
+推荐遵循如下步骤开始进行 MicroPython 开发:
+
+- 在您的开发板上烧录合适的固件
+- 在 PC 机上安装 RT-Thread MicroPython 开发环境并连接上开发板
+
+接下来就可以尽情挥洒您的创意了,更详细的内容可以点击下文中的链接来进一步了解。
+
+### 下载合适的固件
+
+- [W601 IoT Board firmware](https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=12305&extra=page%3D1%26filter%3Dtypeid%26typeid%3D20)
+
+### 安装 IDE 开发环境
+
+- [RT-Thread MicroPython develop environment](https://marketplace.visualstudio.com/items?itemName=RT-Thread.rt-thread-micropython)
+
+## 开发资料
+
+### 示例程序
+
+以下示例程序可以在 RT-Thread MicroPython IDE 开发环境中直接添加到工程:
+
+
+
+### MicroPython 模块详解
+
+- [MicroPython Librarys](https://github.com/RT-Thread-packages/micropython/blob/master/docs/micropython-librarys.md)
+
+
+## 联系我们
+
+如果在使用的过程中遇到问题,您可以用如下方式联系我们:
+
+- 在 github 上提交 issue
+- [RT-Thread MicroPython 官方论坛](https://www.rt-thread.org/qa/forum.php?mod=forumdisplay&fid=2&filter=typeid&typeid=20)
+
+- RT-Thread MicroPython 交流 QQ 群:703840633
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine.md
new file mode 100644
index 0000000000000000000000000000000000000000..cea6db977b0a167b32b1cb4f59ea5db63ea55579
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine.md
@@ -0,0 +1,60 @@
+## **machine** – 与硬件相关的功能
+
+**machine** 模块包含与特定开发板上的硬件相关的特定函数。 在这个模块中的大多数功能允许实现直接和不受限制地访问和控制系统上的硬件块(如CPU,定时器,总线等)。如果使用不当,会导致故障,死机,崩溃,在极端的情况下,硬件会损坏。
+
+需要注意的是,由于不同开发板的硬件资源不同,MicroPython 移植所能控制的硬件也是不一样的。因此对于控制硬件的例程来说,在使用前需要修改相关的配置参数来适配不同的开发板,或者直接运行已经对某一开发板适配好的 MicroPython 示例程序。本文档中的例程都是基于 RT-Thread IoT Board 潘多拉开发板而讲解的。
+
+### 函数
+
+#### 复位相关函数
+
+##### **machine.info**()
+ 显示关于系统介绍和内存占用等信息。
+
+##### **machine.rest**()
+ 重启设备,类似于按下复位按钮。
+
+##### **machine.reset_cause**()
+ 获得复位的原因,查看可能的返回值的常量。
+
+#### 中断相关函数
+
+##### **machine.disable_irq**()
+ 禁用中断请求。返回先前的 `IRQ` 状态,该状态应该被认为是一个未知的值。这个返回值应该在 `disable_irq` 函数被调用之前被传给 `enable_irq` 函数来重置中断到初始状态。
+
+##### **machine.enable_irq**(state)
+ 重新使能中断请求。状态参数应该是从最近一次禁用功能的调用中返回的值。
+
+#### 功耗相关函数
+
+##### **machine.freq**()
+ 返回 `CPU` 的运行频率。
+
+##### **machine.idle**()
+ 阻断给 `CPU` 的时钟信号,在较短或者较长的周期里减少功耗。当中断发生时,外设将继续工作。
+
+##### **machine.sleep**()
+ 停止 `CPU` 并禁止除了 `WLAN` 之外的所有外设。系统会从睡眠请求的地方重新恢复工作。为了确保唤醒一定会发生,应当首先配置中断源。
+
+##### **machine.deepsleep**()
+ 停止 `CPU` 和所有外设(包括网络接口)。执行从主函数中恢复,就像被复位一样。复位的原因可以检查 `machine.DEEPSLEEP` 参数获得。为了确保唤醒一定会发生,应该首先配置中断源,比如一个引脚的变换或者 `RTC` 的超时。
+
+### 常数
+
+- `IRQ` 唤醒值
+#### **machine.IDLE**
+#### **machine.SLEEP**
+#### **machine.DEEPSLEEP**
+
+- 复位
+#### **machine.PWRON_RESET **
+#### **machine.HARD_RESET **
+#### **machine.WDT_RESET **
+#### **machine.DEEPSLEEP_RESET **
+#### **machine.SOFT_RESET**
+
+- 唤醒
+#### **machine.WLAN_WAKE**
+#### **machine.PIN_WAKE**
+#### **machine.RTC_WAKE**
+
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/ADC.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/ADC.md
new file mode 100644
index 0000000000000000000000000000000000000000..43639d2a656f8f03bdeae6fdf743298bda1bc499
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/ADC.md
@@ -0,0 +1,44 @@
+## machine.ADC
+
+**machine.ADC** 类是 machine 模块下的一个硬件类,用于指定 ADC 设备的配置和控制,提供对 ADC 设备的操作方法。
+
+- ADC(Analog-to-Digital Converter,模数转换器),用于将连续变化的模拟信号转化为离散的数字信号。
+- ADC 设备两个重要参数:采样值、分辨率;
+ - 采样值:当前时间由模拟信号转化的数值信号的数值;
+ - 分辨率:以二进制(或十进制)数的位数来表示,一般有 8 位、10 位、12 位、16 位等,它说明模数转换器对输入信号的分辨能力,位数越多,表示分辨率越高,采样值会更精确。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `ADC` 对象的构造函数如下:
+
+#### **class machine.ADC**(id, channel)
+
+- **id**:使用的 ADC 设备编号,`id = 1` 表示编号为 1 的 ADC 设备,或者表示使用的 ADC 设备名,如 `id = "adc"` 表示设备名为 `adc` 的 ADC 设备;
+- **channel**:使用的 ADC 设备通道号,每个 ADC 设备对应多个通道;
+
+例如:`ADC(1,4)` 表示当前使用编号为 1 的 ADC 设备的 4 通道。
+
+### 方法
+
+#### **ADC.init**(channel)
+
+根据输入的层参数初始化 ADC 对象,入参为使用的 ADC 对象通道号;
+
+#### **ADC.deinit**()
+
+用于关闭 ADC 对象,ADC 对象 deinit 之后需要重新 init 才能使用。
+
+#### **ADC.read**()
+
+用于获取并返回当前 ADC 对象的采样值。例如当前采样值为 2048,对应设备的分辨率为 12位,当前设备参考电压为 3.3V ,则该 ADC 对象通道上实际电压值的计算公式为:**采样值 * 参考电压 / (1 << 分辨率位数)**,即 `vol = 2048 / 4096 * 3.3 V = 1.15V`。
+
+### 示例
+
+``` python
+>>> from machine import ADC # 从 machine 导入 ADC 类
+>>> adc = ADC(1, 13) # 创建 ADC 对象,当前使用编号为 1 的 ADC 设备的 13 通道
+>>> adc.read() # 获取 ADC 对象采样值
+4095
+>>> adc.deinit() # 关闭 ADC 对象
+>>> adc.init(13) # 开启并重新配置 ADC 对象
+```
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/I2C.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/I2C.md
new file mode 100644
index 0000000000000000000000000000000000000000..a1fd7f89e35ee5e23321fbeb13b7582f7b8df4da
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/I2C.md
@@ -0,0 +1,111 @@
+## machine.I2C
+
+**machine.I2C** 类是 `machine` 模块下面的一个硬件类,用于对 `I2C` 的配置和控制,提供对 `I2C` 设备的操作方法。
+
+- `I2C` 是一种用于设备间通信的两线协议。在物理层上,它由两根线组成:`SCL` 和 `SDA` ,即时钟和数据线。
+- `I2C` 对象被创建到一个特定的总线上,它们可以在创建时被初始化,也可以之后再来初始化。
+- 打印 `I2C` 对象会打印出配置时的信息。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `I2C` 对象的构造函数如下:
+
+#### **class machine.I2C**(id= -1, scl, sda, freq=400000)
+使用下面的参数构造并返回一个新的 `I2C` 对象:
+
+- **id** :标识特定的 `I2C` 外设。如果填入 id = -1,即选择软件模拟的方式实现 `I2C`,这时可以使用任意引脚来模拟 `I2C` 总线 ,这样在初始化时就必须指定 `scl` 和 `sda` 。
+软件 I2C 的初始化方式可参考 [软件 I2C 示例](#i2c_2)。
+硬件 I2C 的初始化方式可参考 [硬件 I2C 示例](#i2c_3)。
+
+- **scl** : 应该是一个 `Pin` 对象,指定为一个用于 `scl` 的 `Pin` 对象。
+- **sda** : 应该是一个 `Pin` 对象,指定为一个用于 `sda` 的 `Pin` 对象。
+- **freq** :应该是为 `scl` 设置的最大频率。
+
+### 方法
+
+#### **I2C.init**(scl, sda, freq=400000)
+初始化 `I2C` 总线,参数介绍可以参考构造函数中的参数。
+
+#### **I2C.deinit**()
+关闭 `I2C` 总线。
+
+#### **I2C.scan**()
+扫描所有 0x08 和 0x77 之间的 `I2C` 地址,然后返回一个有响应地址的列表。如果一个设备在总线上收到了他的地址,就会通过拉低 `SDA` 的方式来响应。
+
+### I2C 基础方法
+下面的方法实现了基本的 `I2C` 总线操作,可以组合成任何的 `I2C` 通信操作,如果需要对总线进行更多的控制,可以可以使用他们,否则可以使用后面介绍的标准使用方法。
+
+#### **I2C.start**()
+在总线上产生一个启动信号。(`SCL` 为高时,`SDA` 转换为低)
+
+#### **I2C.stop**()
+在总线上产生一个停止信号。(`SCL` 为高时,`SDA` 转换为高)
+
+#### **I2C.readinto**(buf, nack=True)
+从总线上读取字节并将他们存储到 `buf` 中,读取的字节数时 `buf` 的长度。在收到最后一个字节以外的所有内容后,将在总线上发送 `ACK`。在收到最后一个字节之后,如果 `NACK` 是正确的,那么就会发送一个 `NACK`,否则将会发送 `ACK`。
+
+#### **I2C.write**(buf)
+将 `buf` 中的数据接入到总线,检查每个字节之后是否收到 `ACK`,并在收到 `NACK` 时停止传输剩余的字节。这个函数返回接收到的 `ACK` 的数量。
+
+### I2C 标准总线操作
+下面的方法实现了标准 `I2C` 主设备对一个给定从设备的读写操作。
+
+#### **I2C.readfrom**(addr, nbytes, stop=True)
+从 `addr` 指定的从设备中读取 n 个字节,如果 `stop = True`,那么在传输结束时会产生一个停止信号。函数会返回一个存储着读到数据的字节对象。
+
+#### **I2C.readfrom_into**(addr, buf, stop=True)
+从 `addr` 指定的从设备中读取数据存储到 `buf` 中,读取的字节数将是 `buf` 的长度,如果 `stop = True`,那么在传输结束时会产生一个停止信号。
+这个方法没有返回值。
+
+#### **I2C.writeto**(addr, buf, stop=True)
+将 `buf` 中的数据写入到 `addr` 指定的的从设备中,如果在写的过程中收到了 `NACK` 信号,那么就不会发送剩余的字节。如果 `stop = True`,那么在传输结束时会产生一个停止信号,即使收到一个 `NACK`。这个函数返回接收到的 `ACK` 的数量。
+
+### 内存操作
+
+一些 `I2C` 设备充当一个内存设备,可以读取和写入。在这种情况下,有两个与 `I2C` 相关的地址,从机地址和内存地址。下面的方法是与这些设备进行通信的便利函数。
+
+#### **I2C.readfrom_mem**(addr, memaddr, nbytes, \*, addrsize=8)
+从 `addr` 指定的从设备中 `memaddr` 地址开始读取 n 个字节。`addrsize` 参数指定地址的长度。返回一个存储读取数据的字节对象。
+
+#### **I2C.readfrom_mem_into**(addr, memaddr, buf, \*, addrsize=8)
+从 `addr` 指定的从设备中 `memaddr` 地址读取数据到 `buf` 中,,读取的字节数是 `buf` 的长度。
+这个方法没有返回值。
+
+#### **I2C.writeto_mem**(addr, memaddr, buf, \*, addrsize=8)
+将 `buf` 里的数据写入 `addr` 指定的从机的 `memaddr` 地址中。
+这个方法没有返回值。
+
+### 示例
+
+#### 软件模拟 I2C
+```python
+>>> from machine import Pin, I2C
+>>> clk = Pin(("clk", 29), Pin.OUT_OD) # Select the 29 pin device as the clock
+>>> sda = Pin(("sda", 30), Pin.OUT_OD) # Select the 30 pin device as the data line
+>>> i2c = I2C(-1, clk, sda, freq=100000) # create I2C peripheral at frequency of 100kHz
+>>> i2c.scan() # scan for slaves, returning a list of 7-bit addresses
+[81] # Decimal representation
+>>> i2c.writeto(0x51, b'123') # write 3 bytes to slave with 7-bit address 42
+3
+>>> i2c.readfrom(0x51, 4) # read 4 bytes from slave with 7-bit address 42
+b'\xf8\xc0\xc0\xc0'
+>>> i2c.readfrom_mem(0x51, 0x02, 1) # read 1 bytes from memory of slave 0x51(7-bit),
+b'\x12' # starting at memory-address 8 in the slave
+>>> i2c.writeto_mem(0x51, 2, b'\x10') # write 1 byte to memory of slave 42,
+ # starting at address 2 in the slave
+```
+
+#### 硬件 I2C
+
+需要先开启 `I2C` 设备驱动,查找设备可以在 `msh` 中输入`list_device` 命令。
+在构造函数的第一个参数传入 `0`,系统就会搜索名为 `i2c0` 的设备,找到之后使用这个设备来构建 `I2C` 对象:
+
+```python
+>>> from machine import Pin, I2C
+>>> i2c = I2C(0) # create I2C peripheral at frequency of 100kHz
+>>> i2c.scan() # scan for slaves, returning a list of 7-bit addresses
+[81] # Decimal representation
+```
+
+ 更多内容可参考 [machine.I2C](http://docs.micropython.org/en/latest/pyboard/library/machine.I2C.html) 。
+
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/LCD.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/LCD.md
new file mode 100644
index 0000000000000000000000000000000000000000..a9ba9e0debb59d0fb95314a4e2d14374db6ee866
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/LCD.md
@@ -0,0 +1,76 @@
+## machine.LCD
+
+**machine.LCD** 类是 machine 模块下面的一个硬件类,用于对 LCD 的配置和控制,提供对 LCD 设备的操作方法。
+
+IoT board 板载一块 1.3 寸,分辨率为 `240*240` 的 LCD 显示屏,因此对该屏幕操作时,(x, y) 坐标的范围是 `0 - 239`。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `LCD` 对象的构造函数如下:
+
+#### **class machine.LCD**()
+在给定总线上构造一个 `LCD` 对象,无入参,初始化的对象取决于特定硬件,初始化方式可参考 [示例](#_3)。
+
+### 方法
+
+#### **LCD.light**(value)
+
+控制是否开启 LCD 背光,入参为 True 则打开 LCD 背光,入参为 False 则关闭 LCD 背光。
+
+#### **LCD.fill**(color)
+
+根据给定的颜色填充整个屏幕,支持多种颜色,可以传入的参数有:
+
+```
+WHITE BLACK BLUE BRED GRED GBLUE RED MAGENTA GREEN CYAN YELLOW BROWN BRRED GRAY GRAY175 GRAY151 GRAY240
+```
+
+详细的使用方法可参考[示例](#_3)。
+
+#### **LCD.pixel**(x, y, color)
+
+向指定的位置(x, y)画点,点的颜色为 color 指定的颜色,可指定的颜色和上一个功能相同。
+
+注意:(x, y) 坐标的范围是 0 - 239,使用下面的方法对坐标进行操作时同样需要遵循此限制。
+
+#### **LCD.text**(str, x, y, size)
+
+在指定的位置(x,y)写入字符串,字符串由 str 指定,字体的大小由 size 指定,size 的大小可为 16,24,32。
+
+#### **LCD.line**(x1, y1, x2, y2)
+
+在 LCD 上画一条直线,起始地址为 (x1, y1),终点为(x2, y2)。
+
+#### **LCD.rectangle**(x1, y1, x2, y2)
+
+在 LCD 上画一个矩形,左上角的位置为(x1, y1),右下角的位置为(x2, y2)。
+
+#### **LCD.circle**(x1, y1, r)
+
+在 LCD 上画一个圆形,圆心的位置为(x1, y1),半径长度为 r。
+
+#### **LCD.show_bmp**( x, y, pathname)
+
+在 LCD 指定位置上显示 32-bit bmp 格式的图片信息,注意显示 bmp 图片时,(x, y) 坐标是图片的左下角。
+
+### 示例
+
+```python
+from machine import LCD # 从 machine 导入 LCD 类
+lcd = LCD() # 创建一个 lcd 对象
+lcd.light(False) # 关闭背光
+lcd.light(True) # 打开背光
+lcd.fill(lcd.BLACK) # 将整个 LCD 填充为黑色
+lcd.fill(lcd.RED) # 将整个 LCD 填充为红色
+lcd.fill(lcd.GRAY) # 将整个 LCD 填充为灰色
+lcd.fill(lcd.WHITE) # 将整个 LCD 填充为白色
+lcd.pixel(50, 50, lcd.BLUE) # 将(50,50)位置的像素填充为蓝色
+lcd.text("hello RT-Thread", 0, 0, 16) # 在(0, 0) 位置以 16 字号打印字符串
+lcd.text("hello RT-Thread", 0, 16, 24) # 在(0, 16)位置以 24 字号打印字符串
+lcd.text("hello RT-Thread", 0, 48, 32) # 在(0, 48)位置以 32 字号打印字符串
+lcd.line(0, 50, 239, 50) # 以起点(0,50),终点(239,50)画一条线
+lcd.line(0, 50, 239, 50) # 以起点(0,50),终点(239,50)画一条线
+lcd.rectangle(100, 100, 200, 200) # 以左上角为(100,100),右下角(200,200)画矩形
+lcd.circle(150, 150, 80) # 以圆心位置(150,150),半径为 80 画圆
+lcd.show_bmp(180, 50, "sun.bmp") # 以位置(180,50)为图片左下角坐标显示文件系统中的 bmp 图片 "sun.bmp"
+```
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/PWM.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/PWM.md
new file mode 100644
index 0000000000000000000000000000000000000000..7269984ec2ea3f625ad5faddebe91312205864ef
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/PWM.md
@@ -0,0 +1,57 @@
+## machine.PWM
+
+**machine.PWM** 类是 machine 模块下的一个硬件类,用于指定 PWM 设备的配置和控制,提供对 PWM 设备的操作方法。
+
+- PWM (Pulse Width Modulation,脉冲宽度调制) 是一种对模拟信号电平进行数字编码的方式;
+- PWM 设备可以通过调节有效电平在一个周期信号中的比例时间来操作设备;
+- PWM 设备两个重要的参数:频率(freq)和占空比(duty);
+ - 频率:从一个上升沿(下降沿)到下一个上升沿(下降沿)的时间周期,单位为 Hz;
+ - 占空比:有效电平(通常为电平)在一个周期内的时间比例;
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `PWM` 对象的构造函数如下:
+
+#### **class machine.PWM**(id, channel, freq, duty)
+
+在给定的总线上构建一个 `PWM` 对象,参数介绍如下:
+
+- **id**:使用的 PWM 设备编号,如 `id = 1` 表示编号为 1 的 PWM 设备,或者表示使用的 PWM 设备名,如 `id = "pwm"` 表示设备名为 `pwm` 的 PWM 设备;
+- **channel**:使用的 PWM 设备通道号,每个 PWM 设备包含多个通道,范围为 [0, 4];
+- **freq**:初始化频率,范围 [1, 156250];
+- **duty**:初始化占空比数值,范围 [0 255];
+
+例如:`PWM(1,4,100,100)` 表示当前使用 编号为 1 的 PWM 设备的 4 通道,初始化频率为 1000 Hz,初始化占空比的数值为 100。
+
+### 方法
+
+#### **PWM.init**(channel, freq, duty)
+
+根据输入的参数初始化 PWM 对象,参数说明同上。
+
+#### **PWM.deinit**()
+
+用于关闭 PWM 对象,对象 deinit 之后需要重新 init 才能使用。
+
+#### **PWM.freq**(freq)
+
+用于获取或者设置 PWM 对象的频率,频率的范围为 [1, 156250]。如果参数为空,返回当前 PWM 对象的频率;如果参数非空,则使用该参数设置当前 PWM 对象的频率。
+
+#### **PWM.duty**(duty)
+
+用于获取或者设置 PWM 对象的占空比数值,占空比数值的范围为 [0, 255],例如 `duty = 100`,表示当前设备占空比为 `100/255 = 39.22%` 。如果参数为空,返回当前 PWM 对象的占空比数值;如果参数非空,则使用该参数设置当前 PWM 对象的占空比数值。
+
+### 示例
+
+``` python
+>>> from machine import PWM # 从 machine 导入 PWM 类
+>>> pwm = PWM(3, 3, 1000, 100) # 创建 PWM 对象,当前使用编号为 3 的 PWM 设备的 3 通道,初始化的频率为 1000Hz,占空比数值为 100(占空比为 100/255 = 39.22%)
+>>> pwm.freq(2000) # 设置 PWM 对象频率
+>>> pwm.freq() # 获取 PWM 对象频率
+2000
+>>> pwm.duty(200) # 设置 PWM 对象占空比数值
+>>> pwm.duty() # 获取 PWM 对象占空比数值
+200
+>>> pwm.deinit() # 关闭 PWM 对象
+>>> pwm.init(3, 1000, 100) # 开启并重新配置 PWM 对象
+```
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/Pin.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/Pin.md
new file mode 100644
index 0000000000000000000000000000000000000000..8b1039d519bb2bc948555da9ff8dc4a2f0678e54
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/Pin.md
@@ -0,0 +1,110 @@
+## machine.Pin
+
+**machine.Pin** 类是 machine 模块下面的一个硬件类,用于对引脚的配置和控制,提供对 `Pin` 设备的操作方法。
+
+`Pin` 对象用于控制输入/输出引脚(也称为 `GPIO`)。`Pin` 对象通常与一个物理引脚相关联,他可以驱动输出电压和读取输入电压。Pin 类中有设置引脚模式(输入/输出)的方法,也有获取和设置数字逻辑(`0` 或 `1`)的方法。
+
+一个 `Pin` 对象是通过一个标识符来构造的,它明确地指定了一个特定的输入输出。标识符的形式和物理引脚的映射是特定于一次移植的。标识符可以是整数,字符串或者是一个带有端口和引脚号码的元组。在 RT-Thread MicroPython 中,引脚标识符是一个由代号和引脚号组成的元组,如 `Pin(("PB15", 31), Pin.OUT_PP)` 中的` ("PB15", 31)`。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `Pin` 对象的构造函数如下:
+
+#### **class machine.Pin**( id, mode = -1, pull = -1,value)
+- **id** :由用户自定义的引脚名和 `Pin` 设备引脚号组成,如 ("PB15", 31),"PB15" 为用户自定义的引脚名,`31` 为 `RT-Thread Pin` 设备驱动在本次移植中的引脚号。
+
+- **mode** : 指定引脚模式,可以是以下几种:
+ - **Pin.IN** :输入模式
+ - **Pin.OUT** :输出模式
+ - **Pin.OPEN_DRAIN** :开漏模式
+
+- **pull** : 如果指定的引脚连接了上拉下拉电阻,那么可以配置成下面的状态:
+ - **None** :没有上拉或者下拉电阻。
+ - **Pin.PULL_UP** :使能上拉电阻。
+ - **Pin.PULL_DOWN** :使能下拉电阻。
+
+- **value** : `value` 的值只对输出模式和开漏输出模式有效,用来设置初始输出值。
+
+### 方法
+
+#### **Pin.init**(mode= -1, pull= -1, \*, value, drive, alt)
+
+根据输入的参数重新初始化引脚。只有那些被指定的参数才会被设置,其余引脚的状态将保持不变,详细的参数可以参考上面的构造函数。
+
+#### **Pin.value**([x])
+如果没有给定参数 `x` ,这个方法可以获得引脚的值。
+如果给定参数 `x` ,如 `0` 或 `1`,那么设置引脚的值为 逻辑 `0` 或 逻辑 `1`。
+
+#### **Pin.name**()
+返回引脚对象在构造时用户自定义的引脚名。
+
+#### **Pin.irq**(handler=None, trigger=(Pin.IRQ_RISING))
+
+配置在引脚的触发源处于活动状态时调用的中断处理程序。如果引脚模式是, `Pin.IN` 则触发源是引脚上的外部值。 如果引脚模式是, `Pin.OUT` 则触发源是引脚的输出缓冲器。 否则,如果引脚模式是, `Pin.OPEN_DRAIN` 那么触发源是状态'0'的输出缓冲器和状态'1'的外部引脚值。
+
+参数:
+
+- `handler` 是一个可选的函数,在中断触发时调用
+- `trigger` 配置可以触发中断的事件。可能的值是:
+ - `Pin.IRQ_FALLING` 下降沿中断
+ - `Pin.IRQ_RISING` 上升沿中断
+ - `Pin.IRQ_RISING_FALLING` 上升沿或下降沿中断
+ - `Pin.IRQ_LOW_LEVEL` 低电平中断
+ - `Pin.IRQ_HIGH_LEVEL` 高电平中断
+
+### 常量
+
+下面的常量用来配置 `Pin` 对象。
+
+#### 选择引脚模式:
+##### **Pin.IN**
+##### **Pin.OUT**
+##### **Pin.OPEN_DRAIN**
+
+#### 选择上/下拉模式:
+##### **Pin.PULL_UP**
+##### **Pin.PULL_DOWN**
+##### **None**
+使用值 `None` 代表不进行上下拉。
+
+#### 选择中断触发模式:
+##### **Pin.IRQ_FALLING**
+##### **Pin.IRQ_RISING**
+##### **Pin.IRQ_RISING_FALLING**
+##### **Pin.IRQ_LOW_LEVEL**
+##### **Pin.IRQ_HIGH_LEVEL**
+
+### 示例一
+
+控制引脚输出高低电平信号,并读取按键引脚电平信号。
+
+```
+from machine import Pin
+
+PIN_OUT = 31
+PIN_IN = 58
+
+p_out = Pin(("PB15", PIN_OUT), Pin.OUT_PP)
+p_out.value(1) # set io high
+p_out.value(0) # set io low
+
+p_in = Pin(("key_0", PIN_IN), Pin.IN, Pin.PULL_UP)
+print(p_in.value() ) # get value, 0 or 1
+```
+
+### 示例二
+
+上升沿信号触发引脚中断后执行中断处理函数。
+
+```
+from machine import Pin
+
+PIN_KEY0 = 58 # PD10
+key_0 = Pin(("key_0", PIN_KEY0), Pin.IN, Pin.PULL_UP)
+
+def func(v):
+ print("Hello rt-thread!")
+
+key_0.irq(trigger=Pin.IRQ_RISING, handler=func)
+```
+更多内容可参考 [machine.Pin](http://docs.micropython.org/en/latest/pyboard/library/machine.Pin.html) 。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/RTC.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/RTC.md
new file mode 100644
index 0000000000000000000000000000000000000000..be8e4b13a5e409513f52bc699961f9680fa54054
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/RTC.md
@@ -0,0 +1,56 @@
+## machine.RTC
+
+**machine.RTC** 类是 machine 模块下面的一个硬件类,用于对指定 RTC 设备的配置和控制,提供对 RTC 设备的操作方法。
+
+- RTC(Real-Time Clock )实时时钟可以提供精确的实时时间,它可以用于产生年、月、日、时、分、秒等信息。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `RTC` 对象的构造函数如下:
+
+#### **class machine.RTC**()
+
+所以在给定的总线上构造一个 `RTC` 对象,无入参对象,使用方式可参考 [示例](#_3)。
+
+### 方法
+
+#### **RTC.init**(datetime)
+
+根据传入的参数初始化 RTC 设备起始时间。入参 `datetime` 为一个时间元组,格式如下:
+
+```
+(year, month, day, wday, hour, minute, second, yday)
+```
+参数介绍如下所示:
+
+- **year**:年份;
+- **month**:月份,范围 [1, 12];
+- **day**:日期,范围 [1, 31];
+- **wday**:星期,范围 [0, 6],0 表示星期一,以此类推;
+- **hour**:小时,范围 [0, 23];
+- **minute**:分钟,范围[0, 59];
+- **second**:秒,范围[0, 59];
+- **yday**:从当前年份 1 月 1 日开始的天数,范围 [0, 365],一般置位 0 未实现。
+
+使用的方式可参考 [示例](#_3)。
+
+#### **RTC.deinit**()
+
+重置 RTC 设备时间到 2015 年 1 月 1日,重新运行 RTC 设备。
+
+#### **RTC.now**()
+
+获取当前时间,返回值为上述 `datetime` 时间元组格式。
+
+### 示例
+
+```python
+>>> from machine import RTC
+>>> rtc = RTC() # 创建 RTC 设备对象
+>>> rtc.init((2019,6,5,2,10,22,30,0)) # 设置初始化时间
+>>> rtc.now() # 获取当前时间
+(2019, 6, 5, 2, 10, 22, 40, 0)
+>>> rtc.deinit() # 重置时间到2015年1月1日
+>>> rtc.now() # 获取当前时间
+(2015, 1, 1, 3, 0, 0, 1, 0)
+```
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/SPI.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/SPI.md
new file mode 100644
index 0000000000000000000000000000000000000000..93994d2a8e5d136e4fe07b84fbe5c5d6b803fa3b
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/SPI.md
@@ -0,0 +1,99 @@
+## machine.SPI
+
+**machine.SPI** 类是 machine 模块下面的一个硬件类,用于对 SPI 的配置和控制,提供对 SPI 设备的操作方法。
+
+- `SPI` 是一个由主机驱动的同步串行协议。在物理层,总线有三根:`SCK`、`MOSI`、`MISO`。多个设备可以共享同一总线,每个设备都由一个单独的信号 `SS` 来选中,也称片选信号。
+- 主机通过片选信号选定一个设备进行通信。`SS` 信号的管理应该由用户代码负责。(通过 [machine.Pin](Pin.md))
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `SPI` 对象的构造函数如下:
+
+#### **class machine.SPI**(id, ...)
+在给定总线上构造一个 `SPI` 对象,`id` 取决于特定的移植。
+
+如果想要使用软件 `SPI` , 即使用引脚模拟 `SPI` 总线,那么初始化的第一个参数需要设置为 `-1` ,可参考 [软件 SPI 示例](#spi) 。
+
+使用硬件 `SPI` 在初始化时只需传入 `SPI` 设备的编号即可,如 '50' 表示 `SPI5` 总线上的第 0 个设备。初始化方式可参考 [硬件 SPI 示例](#spi_1)。
+
+如果没有额外的参数,`SPI` 对象会被创建,但是不会被初始化,如果给出额外的参数,那么总线将被初始化,初始化参数可以参考下面的 `SPI.init` 方法。
+
+### 方法
+
+#### **SPI.init**(baudrate=1000000, \*, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=None, mosi=None, miso=None)
+
+用给定的参数初始化`SPI`总线:
+
+- **baudrate** :`SCK` 时钟频率。
+- **polarity** :极性可以是 `0` 或 `1`,是时钟空闲时所处的电平。
+- **phase** :相位可以是 `0` 或 `1`,分别在第一个或者第二个时钟边缘采集数据。
+- **bits** :每次传输的数据长度,一般是 8 位。
+- **firstbit** :传输数据从高位开始还是从低位开始,可以是 `SPI.MSB` 或者 `SPI.LSB`。
+- **sck** :用于 `sck` 的 `machine.Pin` 对象。
+- **mosi** :用于 `mosi` 的 `machine.Pin` 对象。
+- **miso** :用于`miso` 的 `machine.Pin` 对象。
+
+#### **SPI.deinit**()
+关闭 `SPI` 总线。
+
+#### **SPI.read**(nbytes, write=0x00)
+读出 n 字节的同时不断的写入 `write` 给定的单字节。返回一个存放着读出数据的字节对象。
+
+#### **SPI.readinto**(buf, write=0x00)
+读出 n 字节到 `buf` 的同时不断地写入 `write` 给定的单字节。
+这个方法返回读入的字节数。
+
+#### **SPI.write**(buf)
+写入 `buf` 中包含的字节。返回`None`。
+
+#### **SPI.write_readinto**(write_buf, read_buf)
+在读出数据到 `readbuf` 时,从 `writebuf` 中写入数据。缓冲区可以是相同的或不同,但是两个缓冲区必须具有相同的长度。返回 `None`。
+
+### 常量
+
+#### **SPI.MASTER**
+用于初始化 `SPI` 总线为主机。
+
+#### **SPI.MSB**
+设置从高位开始传输数据。
+
+#### **SPI.LSB**
+设置从低位开始传输数据。
+
+### 示例
+
+#### 软件模拟 SPI
+```
+>>> from machine import Pin, SPI
+>>> clk = Pin(("clk", 26), Pin.OUT_PP)
+>>> mosi = Pin(("mosi", 27), Pin.OUT_PP)
+>>> miso = Pin(("miso", 28), Pin.IN)
+>>> spi = SPI(-1, 500000, polarity = 0, phase = 0, bits = 8, firstbit = 0, sck = clk, mosi = mosi, miso = miso)
+>>> print(spi)
+SoftSPI(baudrate=500000, polarity=0, phase=0, sck=clk, mosi=mosi, miso=miso)
+>>> spi.write("hello rt-thread!")
+>>> spi.read(10)
+b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+```
+
+#### 硬件 SPI
+
+需要先开启 `SPI` 设备驱动,查找设备可以在 `msh` 中输入`list_device` 命令。
+在构造函数的第一个参数传入 `50`,系统就会搜索名为 `spi50` 的设备,找到之后使用这个设备来构建 `SPI` 对象:
+
+```
+>>> from machine import SPI
+>>> spi = SPI(50)
+>>> print(spi)
+SPI(device port : spi50)
+>>> spi.write(b'\x9f')
+>>> spi.read(5)
+b'\xff\xff\xff\xff\xff'
+>>> buf = bytearray(1)
+>>> spi.write_readinto(b"\x9f",buf)
+>>> buf
+bytearray(b'\xef')
+>>> spi.init(100000,0,0,8,1) # Resetting SPI parameter
+```
+
+ 更多内容可参考 [machine.SPI](http://docs.micropython.org/en/latest/pyboard/library/machine.SPI.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/Timer.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/Timer.md
new file mode 100644
index 0000000000000000000000000000000000000000..54cfe59108e4471b060ab81b3fe57fd52b76b034
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/Timer.md
@@ -0,0 +1,73 @@
+## machine.Timer
+
+**machine.Timer** 类是 machine 模块下的一个硬件类,用于 Timer 设备的配置和控制,提供对 Timer 设备的操作方法。
+
+- Timer(硬件定时器),是一种用于处理周期性和定时性事件的设备。
+- Timer 硬件定时器主要通过内部计数器模块对脉冲信号进行计数,实现周期性设备控制的功能。
+- Timer 硬件定时器可以自定义**超时时间**和**超时回调函数**,并且提供两种**定时器模式**:
+ - `ONE_SHOT`:定时器只执行一次设置的回调函数;
+ - `PERIOD`:定时器会周期性执行设置的回调函数;
+- 打印 Timer 对象会打印出配置的信息。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `Timer` 对象的构造函数如下:
+
+#### **class machine.Timer**(id)
+
+- **id**:使用的 Timer 设备编号,`id = 1` 表示编号为 1 的 Timer 设备,或者表示使用的 timer 设备名,如 `id = "timer"` 表示设备名为 `timer` 的 Timer 设备;
+
+该函数主要用于通过设备编号创建 Timer 设备对象。
+
+### 方法
+
+#### **Timer.init**(mode = Timer.PERIODIC, period = 0, callback = None)
+
+- **mode**:设置 Timer 定时器模式,可以设置两种模式:`ONE_SHOT`(执行一次)、`PERIOD`(周期性执行),默认设置的模式为 `PERIOD` 模式;
+
+- **period**:设置 Timer 定时器定时周期,单位:毫秒(ms)
+
+- **callback**:设置 Timer 定义器超时回调函数,默认设置的函数为 None 空函数,设置的函数格式如下所示:
+
+```python
+def callback_test(device): # 回调函数有且只有一个入参,为创建的 Timer 对象
+ print("Timer callback test")
+ print(device) # 打印 Timer 对象配置信息
+```
+
+该函数使用方式如下示例所示:
+
+```python
+timer.init(wdt.PERIOD, 5000, callback_test) # 设置定时器模式为周期性执行,超时时间为 5 秒, 超时函数为 callback_test
+```
+#### **Timer.deinit**()
+
+该函数用于停止并关闭 Timer 设备。
+
+### 常量
+
+下面的常量用来配置 `Timer` 对象。
+
+#### 选择定时器模式:
+##### **Timer.PERIODIC**
+##### **Timer.ONE_SHOT**
+
+### 示例
+
+```python
+>>> from machine import Timer # 从 machine 导入 Timer 类
+>>> timer = Timer(15) # 创建 Timer 对象,当前设备编号为 11
+>>> # 进入粘贴模式
+paste mode; Ctrl-C to cancel, Ctrl-D to finish
+=== def callback_test(device): # 定义超时回调函数
+=== print("Timer callback test")
+>>> timer.init(timer.PERIODIC, 5000, callback_test) # 初始化 Timer 对象,设置定时器模式为循环执行,超时时间为 5 秒,超时回调函数 callback_test
+>>> Timer callback test # 5 秒超时循环执行回调函数,打印日志
+>>> Timer callback test
+>>> Timer callback test
+>>> timer.init(timer.ONE_SHOT, 5000, callback_test) # 设置定时器模式为只执行一次,超时时间为 5 秒,超时回调函数为 callback_test
+>>> Timer callback test # 5 秒超时后执行一次回调函数,打印日志
+>>> timer.deinit() # 停止并关闭 Timer 定时器
+```
+
+更多内容可参考 [machine.Timer](http://docs.micropython.org/en/latest/library/machine.Timer.html)。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/UART.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/UART.md
new file mode 100644
index 0000000000000000000000000000000000000000..e10ea9c9d6120000b1dcc2c93374d63fea58401a
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/UART.md
@@ -0,0 +1,60 @@
+## machine.UART
+
+**machine.UART** 类是 machine 模块下面的一个硬件类,用于对 UART 的配置和控制,提供对 UART 设备的操作方法。
+
+`UART` 实现了标准的 `uart/usart` 双工串行通信协议,在物理层上,他由两根数据线组成:`RX` 和 `TX`。通信单元是一个字符,它可以是 8 或 9 位宽。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `UART` 对象的构造函数如下:
+
+#### **class machine.UART**(id, ...)
+在给定总线上构造一个 `UART` 对象,`id` 取决于特定的移植。
+初始化参数可以参考下面的 `UART.init` 方法。
+
+使用硬件 UART 在初始化时只需传入 `UART` 设备的编号即可,如传入 `1` 表示 `uart1` 设备。
+初始化方式可参考 [示例](#_3)。
+
+### 方法
+
+#### **UART.init**(baudrate = 9600, bits=8, parity=None, stop=1)
+- **baudrate** :`SCK` 时钟频率。
+- **bits** :每次发送数据的长度。
+- **parity** :校验方式。
+- **stop** :停止位的长度。
+
+#### **UART.deinit**()
+关闭串口总线。
+
+#### **UART.read**([nbytes])
+读取字符,如果指定读 n 个字节,那么最多读取 n 个字节,否则就会读取尽可能多的数据。
+返回值:一个包含读入数据的字节对象。如果如果超时则返回 `None`。
+
+#### **UART.readinto**(buf[, nbytes])
+读取字符到 `buf` 中,如果指定读 n 个字节,那么最多读取 n 个字节,否则就读取尽可能多的数据。另外读取数据的长度不超过 `buf` 的长度。
+返回值:读取和存储到 `buf` 中的字节数。如果超时则返回 `None`。
+
+#### **UART.readline**()
+读一行数据,以换行符结尾。
+返回值:读入的行数,如果超时则返回 `None`。
+
+#### **UART.write**(buf)
+将 `buf` 中的数据写入总线。
+返回值:写入的字节数,如果超时则返回 `None`。
+
+### 示例
+
+在构造函数的第一个参数传入`1`,系统就会搜索名为 `uart1` 的设备,找到之后使用这个设备来构建 `UART` 对象:
+
+```python
+from machine import UART
+uart = UART(1, 115200) # init with given baudrate
+uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
+uart.read(10) # read 10 characters, returns a bytes object
+uart.read() # read all available characters
+uart.readline() # read a line
+uart.readinto(buf) # read and store into the given buffer
+uart.write('abc') # write the 3 characters
+```
+
+ 更多内容可参考 [machine.UART](http://docs.micropython.org/en/latest/pyboard/library/machine.UART.html) 。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/WDT.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/WDT.md
new file mode 100644
index 0000000000000000000000000000000000000000..64af73f6b14b0e3398b4974faa5c37179037462e
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/machine/WDT.md
@@ -0,0 +1,42 @@
+## machine.WDT
+
+**machine.WDT** 类是 machine 模块下的一个硬件类,用于 WDT 设备的配置和控制,提供对 WDT 设备的操作方法。
+
+如下为 WDT 设备基本介绍:
+
+- WDT(WatchDog Timer,硬件看门狗),是一个定时器设备,用于系统程序结束或出错导致系统进入不可恢复状态时重启系统。
+
+- WDT 启动之后,计数器开始计数,在计数器溢出前没有被复位,会对 CPU 产生一个复位信号使设备重启(简称 “被狗咬”);
+
+- 系统正常运行时,需要在 WDT 设备允许的时间间隔内对看门狗计数清零(简称“喂狗”),WDT 设备一旦启动,需要定时“喂狗”以确保设备正常运行。
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `WDT` 对象的构造函数如下:
+
+#### **class machine.WDT**(id = "wdt", timeout=5)
+
+- **id**: 使用的 WDT 设备编号,`id = 1` 表示编号为 1 的 WDT 设备,或者表示使用的 WDT 设备名,如 `id = "wdt"` 表示设备名为 `wdt` 的 WDT 设备;
+
+- **timeout**:设置看门狗超时时间,单位:秒(s);
+
+用于创建一个 WDT 对象并且启动 WDT 功能。一旦启动,设置的超时时间无法改动,WDT 功能无法停止。
+
+如果该函数入参为空,则设置超时时间为 5 秒;如果入参非空,则使用该入参设置 WDT 超时时间,超时时间最小设置为 1 秒。
+
+### 方法
+
+#### **WDT.feed**()
+
+用于执行“喂狗”操作,清空看门狗设备计数。应用程序应该合理的周期性调用该函数,以防止系统重启。
+
+### 示例
+
+``` python
+>>> from machine import WDT # 从 machine 导入 WDT 类
+>>> wdt = WDT() # 创建 WDT 对象,默认超时时间为 5 秒
+>>> wdt = WDT(10) # 创建 WDT 对象,设置超时时间为 10 秒
+>>> wdt.feed() # 在 10 秒超时时间内需要执行“喂狗”操作,清空看门狗设备计数,否则系统将重启
+```
+
+更多内容可参考 [machine.WDT](http://docs.micropython.org/en/latest/library/machine.WDT.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/micropython.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/micropython.md
new file mode 100644
index 0000000000000000000000000000000000000000..040e8e3ef93caae8710bd3a679cedbbc7ed1c127
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/micropython.md
@@ -0,0 +1,72 @@
+
+# micropython – 内部功能访问与控制模块
+
+## Functions
+
+### micropython.const(expr)
+Used to declare that the expression is a constant so that the compile can optimise it. The use of this function should be as follows:
+
+
+```python
+from micropython import const
+CONST_X = const(123)
+CONST_Y = const(2 * CONST_X + 1)
+```
+
+Constants declared this way are still accessible as global variables from outside the module they are declared in. On the other hand, if a constant begins with an underscore then it is hidden, it is not available as a global variable, and does not take up any memory during execution.
+
+This const function is recognised directly by the MicroPython parser and is provided as part of the micropython module mainly so that scripts can be written which run under both CPython and MicroPython, by following the above pattern.
+
+### micropython.opt_level([level])
+
+If level is given then this function sets the optimisation level for subsequent compilation of scripts, and returns None. Otherwise it returns the current optimisation level.
+
+- The optimisation level controls the following compilation features:
+- Assertions: at level 0 assertion statements are enabled and compiled into the bytecode; at levels 1 and higher assertions are not compiled.
+- Built-in __debug__ variable: at level 0 this variable expands to True; at levels 1 and higher it expands to False.
+ Source-code line numbers: at levels 0, 1 and 2 source-code line number are stored along with the bytecode so that exceptions can report the line number they occurred at; at levels 3 and higher line numbers are not stored.
+- The default optimisation level is usually level 0.
+
+### micropython.alloc_emergency_exception_buf(size)
+Allocate size bytes of RAM for the emergency exception buffer (a good size is around 100 bytes). The buffer is used to create exceptions in cases when normal RAM allocation would fail (eg within an interrupt handler) and therefore give useful traceback information in these situations.
+
+A good way to use this function is to put it at the start of your main script (eg boot.py or main.py) and then the emergency exception buffer will be active for all the code following it.
+
+### micropython.mem_info([verbose])
+
+Print information about currently used memory. If the verbose argument is given then extra information is printed.
+
+The information that is printed is implementation dependent, but currently includes the amount of stack and heap used. In verbose mode it prints out the entire heap indicating which blocks are used and which are free.
+
+### micropython.qstr_info([verbose])
+Print information about currently interned strings. If the verbose argument is given then extra information is printed.
+
+The information that is printed is implementation dependent, but currently includes the number of interned strings and the amount of RAM they use. In verbose mode it prints out the names of all RAM-interned strings.
+
+### micropython.stack_use()
+ Return an integer representing the current amount of stack that is being used. The absolute value of this is not particularly useful, rather it should be used to compute differences in stack usage at different points.
+
+### micropython.heap_lock()
+### micropython.heap_unlock()
+ Lock or unlock the heap. When locked no memory allocation can occur and a MemoryError will be raised if any heap allocation is attempted.
+
+These functions can be nested, ie heap_lock() can be called multiple times in a row and the lock-depth will increase, and then heap_unlock() must be called the same number of times to make the heap available again.
+
+If the REPL becomes active with the heap locked then it will be forcefully unlocked.
+
+### micropython.kbd_intr(chr)
+Set the character that will raise a KeyboardInterrupt exception. By default this is set to 3 during script execution, corresponding to Ctrl-C. Passing -1 to this function will disable capture of Ctrl-C, and passing 3 will restore it.
+
+This function can be used to prevent the capturing of Ctrl-C on the incoming stream of characters that is usually used for the REPL, in case that stream is used for other purposes.
+
+### micropython.schedule(func, arg)
+ Schedule the function func to be executed “very soon”. The function is passed the value arg as its single argument. “Very soon” means that the MicroPython runtime will do its best to execute the function at the earliest possible time, given that it is also trying to be efficient, and that the following conditions hold:
+
+- A scheduled function will never preempt another scheduled function.
+- Scheduled functions are always executed “between opcodes” which means that all fundamental Python operations (such as appending to a list) are guaranteed to be atomic.
+- A given port may define “critical regions” within which scheduled functions will never be executed. Functions may be scheduled within a critical region but they will not be executed until that region is exited. An example of a critical region is a preempting interrupt handler (an IRQ).
+ A use for this function is to schedule a callback from a preempting IRQ. Such an IRQ puts restrictions on the code that runs in the IRQ (for example the heap may be locked) and scheduling a function to call later will lift those restrictions.
+
+Note: If schedule() is called from a preempting IRQ, when memory allocation is not allowed and the callback to be passed to schedule() is a bound method, passing this directly will fail. This is because creating a reference to a bound method causes memory allocation. A solution is to create a reference to the method in the class constructor and to pass that reference to schedule(). This is discussed in detail here reference documentation under “Creation of Python objects”.
+
+There is a finite queue to hold the scheduled functions and schedule() will raise a RuntimeError if the queue is full.
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/network.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/network.md
new file mode 100644
index 0000000000000000000000000000000000000000..3f145cbb8ae1acd2bd0d8b6b9158eb5a80ae01e2
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/network.md
@@ -0,0 +1,10 @@
+## network – 网络配置
+
+此模块提供网络驱动程序和路由配置。特定硬件的网络驱动程序在此模块中可用,用于配置硬件网络接口。然后,配置接口提供的网络服务可以通过 `usocket` 模块使用。
+
+### 专用的网络类配置
+
+下面具体的类实现了抽象网卡的接口,并提供了一种控制各种网络接口的方法。
+
+- [class WLAN – control built-in WiFi interfaces](network/wlan.md)
+
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/network/wlan.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/network/wlan.md
new file mode 100644
index 0000000000000000000000000000000000000000..f71c0861fda95f67f557e4644eaedcb62a3f5fc1
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/network/wlan.md
@@ -0,0 +1,109 @@
+## class WLAN – 控制内置的 WiFi 网络接口
+
+该类为 WiFi 网络处理器提供一个驱动程序。使用示例:
+
+```python
+import network
+# enable station interface and connect to WiFi access point
+nic = network.WLAN(network.STA_IF)
+nic.active(True)
+nic.connect('your-ssid', 'your-password')
+# now use sockets as usual
+```
+
+### 构造函数
+
+在 RT-Thread MicroPython 中 `WLAN` 对象的构造函数如下:
+
+#### class network.WLAN(interface_id)
+
+创建一个 WLAN 网络接口对象。支持的接口是 ` network.STA_IF`(STA 模式,可以连接到上游的 WiFi 热点上) 和 `network.AP_IF`(AP 模式,允许其他 WiFi 客户端连接到自身的热点)。下面方法的可用性取决于接口的类型。例如,只有STA 接口可以使用 `WLAN.connect()` 方法连接到 AP 热点上。
+
+### 方法
+
+#### **WLAN.active**([is_active])
+
+如果向该方法传入布尔数值,传入 True 则使能卡,传入 False 则禁止网卡。否则,如果不传入参数,则查询当前网卡的状态。
+
+#### **WLAN.connect**(ssid, password)
+使用指定的账号和密码链接指定的无线热点。
+
+#### **WLAN.disconnect**()
+从当前链接的无线网络中断开。
+
+#### **WLAN.scan**()
+
+扫描当前可以连接的无线网络。
+
+只能在 STA 模式下进行扫描,使用元组列表的形式返回 WiFi 接入点的相关信息。
+
+(ssid, bssid, channel, rssi, authmode, hidden)
+
+#### **WLAN.status**([param])
+
+返回当前无线连接的状态。
+
+当调用该方法时没有附带参数,就会返回值描述当前网络连接的状态。如果还没有从热点连接中获得 IP 地址,此时的状态为 `STATION_IDLE`。如果已经从连接的无线网络中获得 IP 地址,此时的状态为 `STAT_GOT_IP`。
+
+当调用该函数使用的参数为 `rssi` 时,则返回 `rssi` 的值,该函数目前只支持这一个参数。
+
+#### **WLAN.isconnected**()
+
+在 STA 模式时,如果已经连接到 WiFi 网络,并且获得了 IP 地址,则返回 True。如果处在 AP 模式,此时已经与客户端建立连接,则返回 True。其他情况下都返回 False。
+
+#### WLAN.ifconfig([(ip, subnet, gateway, dns)])
+
+获取或者设置网络接口的参数,IP 地址,子网掩码,网关,DNS 服务器。当调用该方法不附带参数时,该方法会返回一个包含四个元素的元组来描述上面的信息。想要设置上面的值,传入一个包含上述四个元素的元组,例如:
+
+```python
+nic.ifconfig(('192.168.0.4', '255.255.255.0', '192.168.0.1', '8.8.8.8'))
+```
+
+#### **WLAN.config**('param')
+
+#### WLAN.config(param=value, ...)
+
+获取或者设置一般网络接口参数,这些方法允许处理标准的 ip 配置之外的其他参数,如 `WLAN. ifconfig()` 函数处理的参数。这些参数包括特定网络和特定硬件的参数。对于参数的设置,应该使用关键字的语法,可以一次性设置多个参数。
+
+当查询参数时,参数名称的引用应该为字符串,且每次只能查询一个参数。
+
+```python
+# Set WiFi access point name (formally known as ESSID) and WiFi password
+ap.config(essid='My_AP', password="88888888")
+# Query params one by one
+print(ap.config('essid'))
+print(ap.config('channel'))
+```
+下面是目前支持的参数:
+
+| Parameter | Description |
+| --------- | --------------------------------- |
+| mac | MAC address (bytes) |
+| essid | WiFi access point name (string) |
+| channel | WiFi channel (integer) |
+| hidden | Whether ESSID is hidden (boolean) |
+| password | Access password (string) |
+
+
+### 示例
+
+STA 模式下:
+
+```python
+import network
+wlan = network.WLAN(network.STA_IF)
+wlan.scan()
+wlan.connect("rtthread","02188888888")
+wlan.isconnected()
+```
+
+AP 模式下:
+
+```python
+import network
+ap = network.WLAN(network.AP_IF)
+ap.config(essid="hello_rt-thread", password="88888888")
+ap.active(True)
+ap.config("essid")
+```
+
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/rtthread.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/rtthread.md
new file mode 100644
index 0000000000000000000000000000000000000000..5fe265767f6ce8e77029a71999bba595f3e9a72f
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/spec-librarys/rtthread.md
@@ -0,0 +1,23 @@
+## **rtthread** – 系统相关函数
+
+**rtthread** 模块提供了与 RT-Thread 操作系统相关的功能,如查看栈使用情况等。
+
+### 函数
+
+#### rtthread.current_tid()
+返回当前线程的 id 。
+
+#### rtthread.is_preempt_thread()
+返回是否是可抢占线程。
+
+### 示例
+
+```
+>>> import rtthread
+>>>
+>>> rtthread.is_preempt_thread() # determine if it's a preemptible thread
+True
+>>> rtthread.current_tid() # current thread id
+268464956
+>>>
+```
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/_thread.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/_thread.md
new file mode 100644
index 0000000000000000000000000000000000000000..29faf523684409748ee82dd0707d5e9ecdc718d9
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/_thread.md
@@ -0,0 +1,28 @@
+## _thread – 多线程支持
+
+`_thread` 模块提供了用于处理多线程的基本方法——多个控制线程共享它们的全局数据空间。为了实现同步,提供了简单的锁(也称为互斥锁或二进制信号量)。
+
+### 示例
+
+```python
+import _thread
+import time
+def testThread():
+ while True:
+ print("Hello from thread")
+ time.sleep(2)
+
+_thread.start_new_thread(testThread, ())
+while True:
+ pass
+```
+
+输出结果(启动新的线程,每隔两秒打印字符):
+
+Hello from thread
+Hello from thread
+Hello from thread
+Hello from thread
+Hello from thread
+
+更多内容可参考 [_thread](http://docs.micropython.org/en/latest/pyboard/library/_thread.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/builtins.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/builtins.md
new file mode 100644
index 0000000000000000000000000000000000000000..3f92ff7c0e7252255de74b168fcae39e990edf82
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/builtins.md
@@ -0,0 +1,104 @@
+# Builtin functions and exceptions
+
+所有的内饰函数和异常类型都在下面描述。
+
+## 函数和类型
+
+- abs()
+- all()
+- any()
+- bin()
+- **class** bool
+- **class** bytearray
+- **class** bytes
+- callable()
+- chr()
+- classmethod()
+- compile()
+- **class** complex
+- delattr(obj, name)
+
+- **class** dict
+- dir()
+
+- divmod()
+- enumerate()
+- eval()
+- exec()
+- filter()
+- **class** float
+- **class** frozenset
+- getattr()
+- globals()
+- hasattr()
+- hash()
+- hex()
+- id()
+- input()
+- **class** int
+ - classmethod from_bytes(bytes, byteorder)
+ In MicroPython, byteorder parameter must be positional (this is compatible with CPython).
+ - to_bytes(size, byteorder)
+ In MicroPython, byteorder parameter must be positional (this is compatible with CPython).
+
+- isinstance()
+- issubclass()
+- iter()
+- len()
+- class list
+- locals()
+- map()
+- max()
+- **class** memoryview
+- min()
+- next()
+- **class** object
+- oct()
+- open()
+- ord()
+- pow()
+- print()
+- property()
+- range()
+- repr()
+- reversed()
+- round()
+- class set
+- setattr()
+- **class** slice
+ - The slice builtin is the type that slice objects have.
+- sorted()
+- staticmethod()
+- **class** str
+- sum()
+- super()
+- **class** tuple
+- type()
+- zip()
+
+## 异常类型
+- **exception** AssertionError
+- **exception** AttributeError
+- **exception** Exception
+- **exception** ImportError
+- **exception** IndexError
+- **exception** KeyboardInterrupt
+- **exception** KeyError
+- **exception** MemoryError
+- **exception** NameError
+- **exception** NotImplementedError
+- **exception** OSError
+ - See CPython documentation: OSError. MicroPython doesn’t implement errno attribute, instead use the standard way to access exception arguments: exc.args[0].
+
+- **exception** RuntimeError
+- **exception** StopIteration
+- **exception** SyntaxError
+- **exception** SystemExit
+ - See CPython documentation: SystemExit.
+
+- **exception** TypeError
+ - See CPython documentation: TypeError.
+
+- **exception** ValueError
+- **exception** ZeroDivisionError
+
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/cmath.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/cmath.md
new file mode 100644
index 0000000000000000000000000000000000000000..1add57e4af11ef3ea9e476c4814b52050ec4fe25
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/cmath.md
@@ -0,0 +1,42 @@
+## **cmath** – 复数运算函数功能
+
+`cmath` 模块提供了一些用于复数运算的函数。这个模块中的函数接受整数、浮点数或复数作为参数。他们还将接受任何有复数或浮点方法的 Python 对象:这些方法分别用于将对象转换成复数或浮点数,然后将该函数应用到转换的结果中。
+
+### 函数
+
+#### **cmath.cos**(z)
+返回``z``的余弦。
+
+#### **cmath.exp**(z)
+返回``z``的指数。
+
+#### **cmath.log**(z)
+返回``z``的对数。
+
+#### **cmath.log10**(z)
+返回``z``的常用对数。
+
+#### **cmath.phase**(z)
+返回``z``的相位, 范围是(-pi, +pi],以弧度表示。
+
+#### **cmath.polar**(z)
+返回``z``的极坐标。
+
+#### **cmath.rect**(r, phi)
+返回`模量r`和相位``phi``的复数。
+
+#### **cmath.sin**(z)
+返回``z``的正弦。
+
+#### **cmath.sqrt**(z)
+返回``z``的平方根。
+
+### 常数
+
+#### **cmath.e**
+自然对数的指数。
+
+#### **cmath.pi**
+圆周率。
+
+更多内容可参考 [cmath](http://docs.micropython.org/en/latest/pyboard/library/cmath.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/gc.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/gc.md
new file mode 100644
index 0000000000000000000000000000000000000000..b5147cf7be25097cdb7078b86b9419a56a9c16c7
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/gc.md
@@ -0,0 +1,22 @@
+## **gc** – 控制垃圾回收
+
+**gc** 模块提供了垃圾收集器的控制接口。
+
+### 函数
+
+#### **gc.enable**()
+允许自动回收内存碎片。
+
+#### **gc.disable**()
+禁止自动回收,但可以通过collect()函数进行手动回收内存碎片。
+
+#### **gc.collect**()
+运行一次垃圾回收。
+
+#### **gc.mem_alloc**()
+返回已分配的内存数量。
+
+#### **gc.mem_free**()
+返回剩余的内存数量。
+
+更多内容可参考 [gc](http://docs.micropython.org/en/latest/pyboard/library/gc.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/math.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/math.md
new file mode 100644
index 0000000000000000000000000000000000000000..7de3bd809966f08f99f20bf86829dd5db47551c1
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/math.md
@@ -0,0 +1,283 @@
+## **math** – 数学函数
+
+**math** 模块提供了对 C 标准定义的数学函数的访问。
+
+> 注意:本模块需要带有硬件 FPU,精度是32位,这个模块需要浮点功能支持。
+
+### 常数
+
+#### **math.e**
+自然对数的底数。
+
+示例:
+```
+>>>import math
+>>>print(math.e)
+2.718282
+```
+#### **math.pi**
+圆周长与直径的比值。
+
+示例:
+
+```
+>>> print(math.pi)
+3.141593
+```
+
+### 函数
+
+#### **math.acos(x)**
+传入弧度值,计算cos(x)的反三角函数。
+
+#### **math.acosh(x)**
+ 返回 ``x`` 的逆双曲余弦。
+
+#### **math.asin(x)**
+传入弧度值,计算sin(x)的反三角函数。
+示例:
+
+```
+>>> x = math.asin(0.5)
+>>> print(x)
+0.5235988
+```
+
+#### **math.asinh(x)**
+ 返回``x`` 的逆双曲正弦。
+
+#### **math.atan(x)**
+ 返回 ``x`` 的逆切线。
+
+#### **math.atan2(y, x)**
+ Return the principal value of the inverse tangent of y/x.
+
+#### **math.atanh(x)**
+ Return the inverse hyperbolic tangent of x.
+
+#### **math.ceil(x)**
+向上取整。
+示例:
+
+```
+>>> x = math.ceil(5.6454)
+>>> print(x)
+6
+```
+
+#### **math.copysign(x, y)**
+ Return x with the sign of y.
+
+#### **math.cos(x)**
+传入弧度值,计算余弦。
+示例:计算cos60°
+
+```
+>>> math.cos(math.radians(60))
+0.5
+```
+
+#### **math.cosh(x)**
+ Return the hyperbolic cosine of x.
+
+#### **math.degrees(x)**
+弧度转化为角度。
+示例:
+
+```
+>>> x = math.degrees(1.047198)
+>>> print(x)
+60.00002
+```
+
+#### **math.erf(x)**
+ Return the error function of x.
+
+#### **math.erfc(x)**
+ Return the complementary error function of x.
+
+#### **math.exp(x)**
+计算e的x次方(幂)。
+示例:
+
+```
+>>> x = math.exp(2)
+>>> print(x)
+7.389056
+```
+
+#### **math.expm1(x)**
+计算 math.exp(x) - 1。
+
+#### **math.fabs(x)**
+计算绝对值。
+示例:
+
+```
+>>> x = math.fabs(-5)
+>>> print(x)
+5.0
+>>> y = math.fabs(5.0)
+>>> print(y)
+5.0
+```
+
+#### **math.floor(x)**
+向下取整。
+示例:
+
+```
+>>> x = math.floor(2.99)
+>>> print(x)
+2
+>>> y = math.floor(-2.34)
+>>> print(y)
+-3
+```
+
+#### **math.fmod(x, y)**
+取x除以y的模。
+示例:
+
+```
+>>> x = math.fmod(4, 5)
+>>> print(x)
+4.0
+```
+
+#### **math.frexp(x)**
+ Decomposes a floating-point number into its mantissa and exponent. The returned value is the tuple (m, e) such that x == m * 2**e exactly. If x == 0 then the function returns (0.0, 0), otherwise the relation 0.5 <= abs(m) < 1 holds.
+
+#### **math.gamma(x)**
+返回伽马函数。
+示例:
+
+```
+>>> x = math.gamma(5.21)
+>>> print(x)
+33.08715
+```
+
+#### **math.isfinite(x)**
+ Return True if x is finite.
+
+#### **math.isinf(x)**
+ Return True if x is infinite.
+
+#### **math.isnan(x)**
+ Return True if x is not-a-number
+
+#### **math.ldexp(x, exp)**
+ Return x * (2**exp).
+
+#### **math.lgamma(x)**
+返回伽马函数的自然对数。
+示例:
+
+```
+>>> x = math.lgamma(5.21)
+>>> print(x)
+3.499145
+```
+
+#### **math.log(x)**
+计算以e为底的x的对数。
+示例:
+
+```
+>>> x = math.log(10)
+>>> print(x)
+2.302585
+```
+
+#### **math.log10(x)**
+计算以10为底的x的对数。
+示例:
+
+```
+>>> x = math.log10(10)
+>>> print(x)
+1.0
+```
+
+#### **math.log2(x)**
+ 计算以2为底的x的对数。
+示例:
+
+```
+>>> x = math.log2(8)
+>>> print(x)
+3.0
+```
+
+#### **math.modf(x)**
+ Return a tuple of two floats, being the fractional and integral parts of x. Both return values have the same sign as x.
+
+#### **math.pow(x, y)**
+计算 x 的 y 次方(幂)。
+示例:
+
+```
+>>> x = math.pow(2, 3)
+>>> print(x)
+8.0
+```
+
+#### **math.radians(x)**
+角度转化为弧度。
+示例:
+
+```
+>>> x = math.radians(60)
+>>> print(x)
+1.047198
+```
+
+#### **math.sin(x)**
+传入弧度值,计算正弦。
+示例:计算sin90°
+
+```
+>>> math.sin(math.radians(90))
+1.0
+```
+
+#### **math.sinh(x)**
+ Return the hyperbolic sine of x.
+
+#### **math.sqrt(x)**
+计算平方根。
+示例:
+
+```
+>>> x = math.sqrt(9)
+>>> print(x)
+3.0
+```
+
+#### **math.tan(x)**
+传入弧度值,计算正切。
+示例:计算tan60°
+
+```
+>>> math.tan(math.radians(60))
+1.732051
+```
+
+#### **math.tanh(x)**
+ Return the hyperbolic tangent of x.
+
+#### **math.trunc(x)**
+取整。
+示例:
+
+```
+>>> x = math.trunc(5.12)
+>>> print(x)
+5
+>>> y = math.trunc(-6.8)
+>>> print(y)
+-6
+```
+
+更多内容可参考 [math](http://docs.micropython.org/en/latest/pyboard/library/math.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/rtthread.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/rtthread.md
new file mode 100644
index 0000000000000000000000000000000000000000..4591032fc8ebfd8bd24efc414d566886210a1035
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/rtthread.md
@@ -0,0 +1,34 @@
+## **rtthread** – 系统相关函数
+
+**rtthread** 模块提供了与 RT-Thread 操作系统相关的功能,如查看栈使用情况等。
+
+### 函数
+
+#### rtthread.current_tid()
+返回当前线程的 id 。
+
+#### rtthread.is_preempt_thread()
+返回是否是可抢占线程。
+
+#### rtthread.stacks_analyze()
+返回当前系统线程和栈使用信息。
+
+### 示例
+
+```
+>>> import rtthread
+>>>
+>>> rtthread.is_preempt_thread() # determine if code is running in a preemptible thread
+True
+>>> rtthread.current_tid() # current thread id
+268464956
+>>> rtthread.stacks_analyze() # show thread information
+thread pri status sp stack size max used left tick error
+---------- --- ------- ---------- ---------- ------ ---------- ---
+elog_async 31 suspend 0x000000a8 0x00000400 26% 0x00000003 000
+tshell 20 ready 0x00000260 0x00001000 39% 0x00000003 000
+tidle 31 ready 0x00000070 0x00000100 51% 0x0000000f 000
+SysMonitor 30 suspend 0x000000a4 0x00000200 32% 0x00000005 000
+timer 4 suspend 0x00000080 0x00000200 25% 0x00000009 000
+>>>
+```
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/sys.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/sys.md
new file mode 100644
index 0000000000000000000000000000000000000000..e4eb9077715b6ba87469de75c7c633c919ebecf8
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/sys.md
@@ -0,0 +1,69 @@
+## **sys** – 系统特有功能函数
+
+**sys** 模块提供系统特有的功能。
+
+### 函数
+
+#### **sys.exit**(retval=0)
+ 终止当前程序给定的退出代码。 函数会抛出 `SystemExit` 异常。
+#### **sys.print_exception**(exc, file=sys.stdout)
+ 打印异常与追踪到一个类似文件的对象 file (或者缺省 `sys.stdout` ).
+
+> 提示:这是 CPython 中回溯模块的简化版本。不同于 `traceback.print_exception()`,这个函数用异常值代替了异常类型、异常参数和回溯对象。文件参数在对应位置,不支持更多参数。CPython 兼容回溯模块在 `micropython-lib`。
+
+### 常数
+
+#### **sys.argv**
+ 当前程序启动时参数的可变列表。
+
+#### **sys.byteorder**
+ 系统字节顺序 (“little” or “big”).
+
+#### **sys.implementation**
+ 关于当前 Python 实现的信息,对于 MicroPython 来说,有以下属性:
+ - 名称 - ‘’micropython“
+ - 版本 - 元组(主要,次要,小),比如(1,9,3)
+
+#### **sys.modules**
+ 已加载模块的字典。在一部分移植中,它可能不包含内置模块。
+
+#### **sys.path**
+ 用来搜索导入模块地址的列表。
+
+#### **sys.platform**
+ 返回当前平台的信息。
+
+#### **sys.stderr**
+ 标准错误流。
+
+#### **sys.stdin**
+ 标准输入流。
+
+#### **sys.stdout**
+ 标准输出流。
+
+#### **sys.version**
+ 符合的 Python 语言版本,如字符串。
+
+#### **sys.version_info**
+ 本次实现使用的 Python 语言版本,用一个元组的方式表示。
+
+### 示例
+
+```
+>>> import sys
+>>> sys.version
+'3.4.0'
+>>> sys.version_info
+(3, 4, 0)
+>>> sys.path
+['', '/libs/mpy/']
+>>> sys.__name__
+'sys'
+>>> sys.platform
+'rt-thread'
+>>> sys.byteorder
+'little'
+```
+
+更多内容可参考 [sys](http://docs.micropython.org/en/latest/pyboard/library/sys.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uarray.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uarray.md
new file mode 100644
index 0000000000000000000000000000000000000000..e352a082a79c194e676714665f0a96885be7d03d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uarray.md
@@ -0,0 +1,56 @@
+## **array** – 数字数据数组
+
+**array** 模块定义了一个对象类型,它可以简洁地表示基本值的数组:字符、整数、浮点数。支持代码格式: b, B, h, H, i, I, l, L, q, Q, f, d (最后2个需要支持浮点数)。
+
+### 构造函数
+
+#### **class array.array**(typecode[, iterable])
+用给定类型的元素创建数组。数组的初始内容由 iterable 提供,如果没有提供,则创建一个空数组。
+
+```
+typecode:数组的类型
+iterable:数组初始内容
+```
+
+示例:
+
+```python
+>>> import array
+>>> a = array.array('i', [2, 4, 1, 5])
+>>> b = array.array('f')
+>>> print(a)
+array('i', [2, 4, 1, 5])
+>>> print(b)
+array('f')
+```
+
+### 方法
+
+#### **array.append**(val)
+将一个新元素追加到数组的末尾。
+
+示例:
+
+```python
+>>> a = array.array('f', [3, 6])
+>>> print(a)
+array('f', [3.0, 6.0])
+>>> a.append(7.0)
+>>> print(a)
+array('f', [3.0, 6.0, 7.0])
+```
+
+#### **array.extend**(iterable)
+将一个新的数组追加到数组的末尾,注意追加的数组和原来数组的数据类型要保持一致。
+
+示例:
+
+```python
+>>> a = array.array('i', [1, 2, 3])
+>>> b = array.array('i', [4, 5])
+>>> a.extend(b)
+>>> print(a)
+array('i', [1, 2, 3, 4, 5])
+```
+
+更多内容可参考 [array](http://docs.micropython.org/en/latest/pyboard/library/array.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ubinascii.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ubinascii.md
new file mode 100644
index 0000000000000000000000000000000000000000..17ebf299a1fdf48dbd5b5f930f6bb53499eff1f4
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ubinascii.md
@@ -0,0 +1,47 @@
+## **ubinascii** – 二进制/ ASCII转换
+
+`ubinascii` 模块包含许多在二进制和各种 ascii 编码的二进制表示之间转换的方法。
+
+### 函数
+
+#### **ubinascii.hexlify**(data[, sep])
+将字符串转换为十六进制表示的字符串。
+
+示例:
+
+```
+>>> ubinascii.hexlify('hello RT-Thread')
+b'68656c6c6f2052542d546872656164'
+>>> ubinascii.hexlify('summer')
+b'73756d6d6572'
+```
+如果指定了第二个参数sep,它将用于分隔两个十六进制数。
+
+示例:
+
+```
+如果指定了第二个参数sep,它将用于分隔两个十六进制数。
+示例:
+>>> ubinascii.hexlify('hello RT-Thread'," ")
+b'68 65 6c 6c 6f 20 52 54 2d 54 68 72 65 61 64'
+>>> ubinascii.hexlify('hello RT-Thread',",")
+b'68,65,6c,6c,6f,20,52,54,2d,54,68,72,65,61,64'
+```
+
+#### **ubinascii.unhexlify**(data)
+转换十六进制字符串为二进制字符串,功能和 hexlify 相反。
+
+示例:
+
+```
+>>> ubinascii.unhexlify('73756d6d6572')
+b'summer'
+```
+
+#### **ubinascii.a2b_base64**(data)
+Base64编码的数据转换为二进制表示。返回字节串。
+
+#### **ubinascii.b2a_base64**(data)
+编码base64格式的二进制数据。返回的字符串。
+
+更多内容可参考 [ubinascii](http://docs.micropython.org/en/latest/pyboard/library/ubinascii.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ucollections.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ucollections.md
new file mode 100644
index 0000000000000000000000000000000000000000..7e68dbf6ff7b8a3f6103ef20839c788f652cb02b
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ucollections.md
@@ -0,0 +1,44 @@
+## **ucollections** – 集合与容器类型
+
+**ucollections** 模块实现了专门的容器数据类型,它提供了 Python 的通用内置容器的替代方案,包括了字典、列表、集合和元组。
+
+### 类
+
+#### **ucollections.namedtuple**(name, fields)
+这是工厂函数创建一个新的 `namedtuple` 型与一个特定的字段名称和集合。`namedtuple` 是元组允许子类要访问它的字段不仅是数字索引,而且还具有属性使用符号字段名访问语法。 字段是字符串序列指定字段名称。为了兼容的实现也可以用空间分隔的字符串命名的字段(但效率较低) 。
+
+代码示例:
+```python
+from ucollections import namedtuple
+
+MyTuple = namedtuple("MyTuple", ("id", "name"))
+t1 = MyTuple(1, "foo")
+t2 = MyTuple(2, "bar")
+print(t1.name)
+assert t2.name == t2[1]
+ucollections.OrderedDict(...)
+```
+
+#### **ucollections.OrderedDict**(...)
+字典类型的子类,会记住并保留键/值的追加顺序。当有序的字典被迭代输出时,键/值 会按照他们被添加的顺序返回 :
+
+```python
+from ucollections import OrderedDict
+
+# To make benefit of ordered keys, OrderedDict should be initialized
+# from sequence of (key, value) pairs.
+d = OrderedDict([("z", 1), ("a", 2)])
+# More items can be added as usual
+d["w"] = 5
+d["b"] = 3
+for k, v in d.items():
+ print(k, v)
+```
+输出:
+
+z 1
+a 2
+w 5
+b 3
+
+更多的内容可参考 [ucollections](http://docs.micropython.org/en/latest/pyboard/library/ucollections.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uctypes.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uctypes.md
new file mode 100644
index 0000000000000000000000000000000000000000..810c0843e47fdbaf32b95ab06111134047626159
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uctypes.md
@@ -0,0 +1,82 @@
+## **uctypes** – 以结构化的方式访问二进制数据
+
+uctypes 模块用来访问二进制数据结构,它提供 C 兼容的数据类型。
+
+### 常量
+- uctypes.LITTLE_ENDIAN — 小端压缩结构。
+- uctypes.BIG_ENDIAN — 大端压缩结构类型。
+- NATIVE — mricopython 本地的存储类型
+
+
+### 构造函数
+
+#### class uctypes.struct(addr, descriptor, type)
+将内存中以 c 形式打包的结构体或联合体转换为字典,并返回该字典。
+```
+addr:开始转换的地址
+descriptor:转换描述符
+格式:"field_name":offset|uctypes.UINT32
+offset:偏移量,
+单位:字节、VOID、UINT8、INT8、UINT16、INT16、UINT32、INT32、UINT64、INT64、BFUINT8、BFINT8、BFUINT16、BFINT16、BFUINT32、BFINT32、BF_POS、BF_LEN、FLOAT32、FLOAT64、PTR、ARRAY
+type:c 结构体或联合体存储类型,默认为本地存储类型
+```
+
+示例:
+
+```python
+>>> a = b"0123"
+>>> s = uctypes.struct(uctypes.addressof(a), {"a": uctypes.UINT8 | 0, "b": uctypes.UINT16 | 1}, uctypes.LITTLE_ENDIAN)
+>>> print(s)
+
+>>> print(s.a)
+48
+>>> s.a = 49
+>>> print(a)
+b'1123'
+```
+
+### 方法
+
+#### **uctypes.sizeof**(struct)
+按字节返回数据的大小。参数可以是类或者数据对象 (或集合)。
+示例:
+```python
+>>> a = b"0123"
+>>>b = uctypes.struct(uctypes.addressof(a), {"a": uctypes.UINT8 | 0, "b": uctypes.UINT16 | 1}, uctypes.LITTLE_ENDIAN)
+>>> b.a
+48
+>>> print(uctypes.sizeof(b))
+3
+```
+
+#### **uctypes.addressof**(obj)
+返回对象地址。参数需要是 bytes, bytearray 。
+示例:
+
+```python
+>>> a = b"0123"
+>>> print(uctypes.addressof(a))
+1073504048
+```
+
+#### **uctypes.bytes_at**(addr, size)
+捕捉从 addr 开始到 size 个地址偏移量结束的内存数据为 bytearray 对象并返回。
+示例:
+
+```python
+>>> a = b"0123"
+>>>print( uctypes.bytes_at(uctypes.addressof(a), 4))
+b'0123'
+```
+
+#### **uctypes.bytearray_at**(addr, size)
+捕捉给定大小和地址内存为 bytearray 对象。与 bytes_at() 函数不同的是,它可以被再次写入,可以访问给定地址的参数。
+示例:
+
+```python
+>>> a = b"0123"
+>>> print(uctypes.bytearray_at(uctypes.addressof(a), 2))
+bytearray(b'01')
+```
+
+更多内容可参考 [uctypes](http://docs.micropython.org/en/latest/pyboard/library/uctypes.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uerrno.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uerrno.md
new file mode 100644
index 0000000000000000000000000000000000000000..07872df3fb13fcd75837b1dc3f22a6b739b84ad5
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uerrno.md
@@ -0,0 +1,20 @@
+## **uerrno** – 系统错误码模块
+
+`uerrno` 模块提供了标准的 errno 系统符号,每个符号都有对应的整数值。
+
+### 示例
+
+```python
+try:
+ uos.mkdir("my_dir")
+except OSError as exc:
+ if exc.args[0] == uerrno.EEXIST:
+ print("Directory already exists")
+uerrno.errorcode
+Dictionary mapping numeric error codes to strings with symbolic error code (see above):
+
+>>> print(uerrno.errorcode[uerrno.EEXIST])
+EEXIST
+```
+
+更多内容可参考 [uerrno](http://docs.micropython.org/en/latest/pyboard/library/uerrno.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uhashlib.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uhashlib.md
new file mode 100644
index 0000000000000000000000000000000000000000..93f359f3210b8a28bf8ef0dbc3da7ab117f0729d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uhashlib.md
@@ -0,0 +1,36 @@
+## **uhashlib** – 哈希算法
+
+`uhashlib` 模块实现了二进制数据哈希算法。
+
+### 算法功能
+
+#### **SHA256**
+当代的散列算法(SHA2系列),它适用于密码安全的目的。被包含在 MicroPython 内核中,除非有特定的代码大小限制,否则推荐任何开发板都支持这个功能。
+
+#### **SHA1**
+上一代的算法,不推荐新的应用使用这种算法,但是 SHA1 算法是互联网标准和现有应用程序的一部分,所以针对网络连接便利的开发板会提供这种功能。
+
+#### **MD5**
+一种遗留下来的算法,作为密码使用被认为是不安全的。只有特定的开发板,为了兼容老的应用才会提供这种算法。
+
+### 函数
+
+#### **class uhashlib.sha256**([data])
+创建一个SHA256哈希对象并提供 data 赋值。
+
+#### **class uhashlib.sha1**([data])
+创建一个SHA1哈希对象并提供 data 赋值。
+
+#### **class uhashlib.md5**([data])
+创建一个MD5哈希对象并提供 data 赋值。
+
+#### **hash.update**(data)
+将更多二进制数据放入哈希表中。
+
+#### **hash.digest**()
+返回字节对象哈希的所有数据。调用此方法后,将无法将更多数据送入哈希。
+
+#### **hash.hexdigest**()
+此方法没有实现, 使用 ubinascii.hexlify(hash.digest()) 达到类似效果。
+
+更多内容可参考 [uhashlib](http://docs.micropython.org/en/latest/pyboard/library/uhashlib.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uheapq.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uheapq.md
new file mode 100644
index 0000000000000000000000000000000000000000..be7dfdad9a1b64ff2a0d0fcf97110052ec955041
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uheapq.md
@@ -0,0 +1,16 @@
+## **uheapq** – 堆排序算法
+
+`uheapq` 模块提供了堆排序相关算法,堆队列是一个列表,它的元素以特定的方式存储。
+
+### 函数
+
+#### **uheapq.heappush**(heap, item)
+将对象压入堆中。
+
+#### **uheapq.heappop**(heap)
+从 heap 弹出第一个元素并返回。 如果是堆时空的会抛出 IndexError。
+
+#### **uheapq.heapify**(x)
+将列表 x 转换成堆。
+
+更多内容可参考 [uheapq](http://docs.micropython.org/en/latest/pyboard/library/uheapq.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uio.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uio.md
new file mode 100644
index 0000000000000000000000000000000000000000..6f5476e208d44f8e147ada33bff8a6427cbb2e79
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uio.md
@@ -0,0 +1,27 @@
+## **uio** – 输入/输出流
+
+**uio** 模块包含流类型 (类似文件) 对象和帮助函数。
+
+### 函数
+
+#### **uio.open**(name, mode='r', \*\*kwargs)
+
+打开一个文件,关联到内建函数``open()``。所有端口 (用于访问文件系统) 需要支持模式参数,但支持其他参数不同的端口。
+
+### 类
+
+#### **class uio.FileIO**(...)
+ 这个文件类型用二进制方式打开文件,等于使用``open(name, “rb”)``。 不应直接使用这个实例。
+
+#### **class uio.TextIOWrapper**(...)
+ 这个类型以文本方式打开文件,等同于使用``open(name, “rt”)``不应直接使用这个实例。
+
+#### **class uio.StringIO**([string])
+
+#### **class uio.BytesIO**([string])
+ 内存文件对象。`StringIO` 用于文本模式 I/O (用 “t” 打开文件),`BytesIO` 用于二进制方式 (用 “b” 方式)。文件对象的初始内容可以用字符串参数指定(`stringio`用普通字符串,`bytesio`用`bytes`对象)。所有的文件方法,如 `read(), write(), seek(), flush(), close()` 都可以用在这些对象上,包括下面方法:
+
+#### **getvalue**()
+ 获取缓存区内容。
+
+更多内容可参考 [uio](http://docs.micropython.org/en/latest/pyboard/library/uio.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ujson.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ujson.md
new file mode 100644
index 0000000000000000000000000000000000000000..fd8ecb8f726e2dba7087a98e45b8a921e2b0889d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ujson.md
@@ -0,0 +1,42 @@
+## **ujson** – JSON编码与解码
+
+`ujson` 模块提供 Python 对象到 JSON(JavaScript Object Notation) 数据格式的转换。
+
+### 函数
+
+#### **ujson.dumps**(obj)
+
+将 dict 类型转换成 str。
+
+```
+obj:要转换的对象
+```
+
+示例:
+
+```
+>>> obj = {1:2, 3:4, "a":6}
+>>> print(type(obj), obj) #原来为dict类型
+ {3: 4, 1: 2, 'a': 6}
+>>> jsObj = json.dumps(obj) #将dict类型转换成str
+>>> print(type(jsObj), jsObj)
+ {3: 4, 1: 2, "a": 6}
+```
+
+#### **ujson.loads**(str)
+解析 JSON 字符串并返回对象。如果字符串格式错误将引发 ValueError 异常。
+示例:
+
+```
+>>> obj = {1:2, 3:4, "a":6}
+>>> jsDumps = json.dumps(obj)
+>>> jsLoads = json.loads(jsDumps)
+>>> print(type(obj), obj)
+ {3: 4, 1: 2, 'a': 6}
+>>> print(type(jsDumps), jsDumps)
+ {3: 4, 1: 2, "a": 6}
+>>> print(type(jsLoads), jsLoads)
+ {'a': 6, 1: 2, 3: 4}
+```
+
+更多内容可参考 [ujson](http://docs.micropython.org/en/latest/pyboard/library/ujson.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uos.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uos.md
new file mode 100644
index 0000000000000000000000000000000000000000..73f270d5bc8195b0336d9e6ca6f70051b913286f
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uos.md
@@ -0,0 +1,56 @@
+## **uos** – 基本的操作系统服务
+
+`uos` 模块包含了对文件系统的访问操作,是对应 CPython 模块的一个子集。
+
+### 函数
+
+#### **uos.chdir**(path)
+更改当前目录。
+
+#### **uos.getcwd**()
+获取当前目录。
+
+#### **uos.listdir**([dir])
+没有参数就列出当前目录,否则列出给定目录。
+
+#### **uos.mkdir**(path)
+创建一个目录。
+
+#### **uos.remove**(path)
+删除文件。
+
+#### **uos.rmdir**(path)
+删除目录。
+
+#### **uos.rename**(old_path, new_path)
+重命名文件或者文件夹。
+
+#### **uos.stat**(path)
+获取文件或目录的状态。
+
+#### **uos.sync**()
+同步所有的文件系统。
+
+### 示例
+
+```
+>>> import uos
+>>> uos. # Tab
+__name__ uname chdir getcwd
+listdir mkdir remove rmdir
+stat unlink mount umount
+>>> uos.mkdir("rtthread")
+>>> uos.getcwd()
+'/'
+>>> uos.chdir("rtthread")
+>>> uos.getcwd()
+'/rtthread'
+>>> uos.listdir()
+['web_root', 'rtthread', '11']
+>>> uos.rmdir("11")
+>>> uos.listdir()
+['web_root', 'rtthread']
+>>>
+```
+
+更多内容可参考 [uos](http://docs.micropython.org/en/latest/pyboard/library/uos.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/urandom.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/urandom.md
new file mode 100644
index 0000000000000000000000000000000000000000..794b56a36ab7701739402384e5068dd2e3faf13b
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/urandom.md
@@ -0,0 +1,174 @@
+## **urandom** - 随机数生成模块
+
+`urandom` 模块实现了伪随机数生成器。
+
+### 函数
+
+#### **urandom.choice**(obj)
+
+随机生成对象 obj 中的元数。
+
+```
+obj:元数列表
+```
+
+示例:
+
+```python
+>>> print(random.choice("DFRobot"))
+R
+>>> print(random.choice("DFRobot"))
+D
+>>> print(random.choice([0, 2, 4, 3]))
+3
+>>> print(random.choice([0, 2, 4, 3]))
+3
+>>> print(random.choice([0, 2, 4, 3]))
+2
+```
+
+#### **urandom.getrandbits**(size)
+
+随机生成 0 到 size 个位二进制数范围内的正整数。 比如 :
+
+- size = 4,那么便是从 0 到0b1111中随机一个正整数。
+- size = 8,那么便是从 0 到 0b11111111中随机一个正整数。
+
+```python
+size:位大小
+```
+
+示例:
+
+```python
+>>> print( random.getrandbits(1)) #1位二进制位,范围为0~1(十进制:0~1)
+1
+>>> print(random.getrandbits(1))
+0
+>>> print(random.getrandbits(8)) #8位二进制位,范围为0000 0000~1111 11111(十进制:0~255)
+224
+>>> print(random.getrandbits(8))
+155
+```
+
+#### **urandom.randint**(start, end)
+
+随机生成一个 start 到 end 之间的整数。
+
+```
+start:指定范围内的开始值,包含在范围内
+end:指定范围内的结束值,包含在范围内
+```
+
+示例:
+
+```python
+>>> import random
+>>> print(random.randint(1, 4))
+4
+>>> print(random.randint(1, 4))
+2
+```
+
+#### **urandom.random**()
+随机生成一个 0 到 1 之间的浮点数。
+示例:
+
+```python
+>>> print(random.random())
+0.7111824
+>>> print(random.random())
+0.3168149
+```
+
+#### **urandom.randrange**(start, end, step)
+
+随机生成 start 到 end 并且递增为 step 的范围内的正整数。例如,randrange(0, 8, 2)中,随机生成的数为 0、2、4、6 中任一个。
+
+```
+start:指定范围内的开始值,包含在范围内
+end:指定范围内的结束值,包含在范围内
+step:递增基数
+```
+
+示例:
+
+```python
+>>> print(random.randrange(2, 8, 2))
+4
+>>> print(random.randrange(2, 8, 2))
+6
+>>> print(random.randrange(2, 8, 2))
+2
+```
+
+#### **urandom.seed**(sed)
+
+指定随机数种子,通常和其他随机数生成函数搭配使用。
+**注意:**
+ MicroPython 中的随机数其实是一个稳定算法得出的稳定结果序列,而不是一个随机序列。sed 就是这个算法开始计算的第一个值。所以就会出现只要 sed 是一样的,那么后续所有“随机”结果和顺序也都完全一致。
+
+```
+sed:随机数种子
+```
+
+示例:
+
+```python
+import random
+
+for j in range(0, 2):
+ random.seed(13) #指定随机数种子
+ for i in range(0, 10): #生成0到10范围内的随机序列
+ print(random.randint(1, 10))
+ print("end")
+```
+
+运行结果:
+
+```
+5
+2
+3
+2
+3
+4
+2
+5
+8
+2
+end
+5
+2
+3
+2
+3
+4
+2
+5
+8
+2
+end
+```
+
+ 从上面可以看到生成两个随机数列表是一样的,你也可以多生成几个随机数列表查看结果。
+
+#### **urandom.uniform**(start, end)
+
+随机生成start到end之间的浮点数。
+
+```
+start:指定范围内的开始值,包含在范围内
+stop:指定范围内的结束值,包含在范围内
+```
+
+示例:
+
+```python
+>>> print(random.uniform(2, 4))
+2.021441
+>>> print(random.uniform(2, 4))
+3.998012
+```
+
+更多内容可参考 [urandom](https://docs.python.org/3/library/random.html?highlight=random#module-random) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ure.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ure.md
new file mode 100644
index 0000000000000000000000000000000000000000..7c8805f6fbcf9ebf581903bb1ebe7ce1fe2d08b7
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ure.md
@@ -0,0 +1,52 @@
+## **ure** – 正则表达式
+
+`ure` 模块用于测试字符串的某个模式,执行正则表达式操作。
+
+### 匹配字符集
+
+
+#### 匹配任意字符
+ ``'.'``
+
+#### 匹配字符集合,支持单个字符和一个范围
+ ``'[]'``
+
+#### 支持多种匹配元字符
+ ``'^'``
+ ``'$'``
+ ``'?'``
+ ``'*'``
+ ``'+'``
+ ``'??'``
+ ``'*?'``
+ ``'+?'``
+ ``'{m,n}'``
+
+### 函数
+
+#### **ure.compile**(regex)
+编译正则表达式,返回 regex 对象。
+
+#### **ure.match**(regex, string)
+用 string 匹配 regex,匹配总是从字符串的开始匹配。
+
+#### **ure.search**(regex, string)
+在 string 中搜索 regex。不同于匹配,它搜索第一个匹配位置的正则表达式字符串 (结果可能会是0)。
+
+#### **ure.DEBUG**
+标志值,显示表达式的调试信息。
+
+### **正则表达式对象**:
+编译正则表达式,使用 `ure.compile()` 创建实例。
+
+#### **regex.match**(string)
+#### **regex.search**(string)
+#### **regex.split**(string, max_split=-1)
+
+### **匹配对象** :
+匹配对象是 match() 和 search() 方法的返回值。
+
+#### **match.group**([index])
+只支持数字组。
+
+更多内容可参考 [ure](http://docs.micropython.org/en/latest/pyboard/library/ure.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uselect.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uselect.md
new file mode 100644
index 0000000000000000000000000000000000000000..0743c5c5d5fba088cfcb471dd532dbafa20cd18e
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uselect.md
@@ -0,0 +1,102 @@
+## **uselect** – 等待流事件
+
+`uselect` 模块提供了等待数据流的事件功能。
+
+### 常数
+
+#### **select.POLLIN** - 读取可用数据
+
+#### **select.POLLOUT** - 写入更多数据
+
+#### **select.POLLERR** - 发生错误
+
+#### **select.POLLHUP** - 流结束/连接终止检测
+
+### 函数
+
+#### **select.select**(rlist, wlist, xlist[, timeout])
+监控对象何时可读或可写,一旦监控的对象状态改变,返回结果(阻塞线程)。这个函数是为了兼容,效率不高,推荐用 `poll` 函数 。
+
+```
+rlist:等待读就绪的文件描述符数组
+wlist:等待写就绪的文件描述符数组
+xlist:等待异常的数组
+timeout:等待时间(单位:秒)
+```
+示例:
+
+```python
+def selectTest():
+ global s
+ rs, ws, es = select.select([s,], [], [])
+ #程序会在此等待直到对象s可读
+ print(rs)
+ for i in rs:
+ if i == s:
+ print("s can read now")
+ data,addr=s.recvfrom(1024)
+ print('received:',data,'from',addr)
+```
+
+### Poll 类
+
+#### **select.poll**()
+创建 poll 实例。
+
+示例:
+
+```
+>>>poller = select.poll()
+>>>print(poller)
+
+```
+
+#### **poll.register**(obj[, eventmask])
+注册一个用以监控的对象,并设置被监控对象的监控标志位 flag。
+
+```
+obj:被监控的对象
+flag:被监控的标志
+ select.POLLIN — 可读
+ select.POLLHUP — 已挂断
+ select.POLLERR — 出错
+ select.POLLOUT — 可写
+```
+
+#### **poll.unregister**(obj)
+解除监控的对象的注册。
+
+```
+obj:注册过的对象
+```
+
+示例:
+
+```
+>>>READ_ONLY = select.POLLIN | select.POLLHUP | select.POLLERR
+>>>READ_WRITE = select.POLLOUT | READ_ONLY
+>>>poller.register(s, READ_WRITE)
+>>>poller.unregister(s)
+```
+
+#### **poll.modify**(obj, eventmask)
+修改已注册的对象监控标志。
+
+```
+obj:已注册的被监控对象
+flag:修改为的监控标志
+```
+
+示例:
+
+```
+>>>READ_ONLY = select.POLLIN | select.POLLHUP | select.POLLERR
+>>>READ_WRITE = select.POLLOUT | READ_ONLY
+>>>poller.register(s, READ_WRITE)
+>>>poller.modify(s, READ_ONLY)
+```
+
+#### **poll.poll**([timeout])
+等待至少一个已注册的对象准备就绪。返回 (obj, event, ...) 元组, event 元素指定了一个流发生的事件,是上面所描述的 `select.POLL*`常量组合。 根据平台和版本的不同,在元组中可能有其他元素,所以不要假定元组的大小是 2 。如果超时,则返回空列表。
+
+更多内容可参考 [uselect](http://docs.micropython.org/en/latest/pyboard/library/uselect.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/usocket.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/usocket.md
new file mode 100644
index 0000000000000000000000000000000000000000..0413e449c4a8ccc937a44a6d4a7d9a1ff0f44ddc
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/usocket.md
@@ -0,0 +1,195 @@
+## **usocket** – 套接字模块
+
+`usocket` 模块提供对BSD套接字接口的访问。
+
+### 常数
+
+#### 地址簇
+- socket.AF_INET =2 — TCP/IP – IPv4
+- socket.AF_INET6 =10 — TCP/IP – IPv6
+
+#### 套接字类型
+- socket.SOCK_STREAM =1 — TCP流
+- socket.SOCK_DGRAM =2 — UDP数据报
+- socket.SOCK_RAW =3 — 原始套接字
+- socket.SO_REUSEADDR =4 — socket可重用
+
+#### IP协议号
+- socket.IPPROTO_TCP =16
+- socket.IPPROTO_UDP =17
+
+#### 套接字选项级别
+- socket.SOL_SOCKET =4095
+
+### 函数
+
+#### **socket.socket**
+
+`socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)`
+
+创建新的套接字,使用指定的地址、类型和协议号。
+
+#### **socket.getaddrinfo**(host, port)
+将主机域名(host)和端口(port)转换为用于创建套接字的5元组序列。元组列表的结构如下:
+
+```
+(family, type, proto, canonname, sockaddr)
+```
+
+示例:
+
+```
+>>> info = socket.getaddrinfo("rt-thread.org", 10000)
+>>> print(info)
+[(2, 1, 0, '', ('118.31.15.152', 10000))]
+```
+
+#### **socket.close**()
+关闭套接字。一旦关闭后,套接字所有的功能都将失效。远端将接收不到任何数据 (清理队列数据后)。 虽然在垃圾回收时套接字会自动关闭,但还是推荐在必要时用 close() 去关闭。
+
+#### **socket.bind**(address)
+将套接字绑定到地址,套接字不能是已经绑定的。
+
+#### **socket.listen**([backlog])
+监听套接字,使服务器能够接收连接。
+```
+backlog:接受套接字的最大个数,至少为0,如果没有指定,则默认一个合理值。
+```
+
+#### **socket.accept**()
+接收连接请求。
+**注意:**
+ 只能在绑定地址端口号和监听后调用,返回 conn 和 address。
+
+```
+conn:新的套接字对象,可以用来收发消息
+address:连接到服务器的客户端地址
+```
+
+#### **socket.connect**(address)
+连接服务器。
+
+```
+address:服务器地址和端口号的元组或列表
+```
+
+#### **socket.send**(bytes)
+发送数据,并返回成功发送的字节数,返回字节数可能比发送的数据长度少。
+
+```
+bytes:bytes类型数据
+```
+
+#### **socket.recv**(bufsize)
+接收数据,返回接收到的数据对象。
+
+```
+bufsize:指定一次接收的最大数据量
+```
+
+示例:
+
+```
+data = conn.recv(1024)
+```
+
+#### **socket.sendto**(bytes, address)
+发送数据,目标由address决定,常用于UDP通信,返回发送的数据大小。
+
+```
+bytes:bytes类型数据
+address:目标地址和端口号的元组
+```
+
+示例:
+
+```
+data = sendto("hello RT-Thread", ("192.168.10.110", 100))
+```
+
+#### **socket.recvfrom**(bufsize)
+接收数据,常用于UDP通信,并返回接收到的数据对象和对象的地址。
+
+```
+bufsize:指定一次接收的最大数据量
+```
+
+示例:
+
+```
+data,addr=fd.recvfrom(1024)
+```
+
+#### **socket.setsockopt**(level, optname, value)
+根据选项值设置套接字。
+
+```
+level:套接字选项级别
+optname:套接字的选项
+value:可以是一个整数,也可以是一个表示缓冲区的bytes类对象。
+```
+
+示例:
+
+```
+s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+```
+
+#### **socket.settimeout**(value)
+设置超时时间,单位:秒。
+示例:
+
+```
+s.settimeout(2)
+```
+
+#### **socket.setblocking**(flag)
+设置阻塞或非阻塞模式: 如果 flag 是 false,设置非阻塞模式。
+
+#### **socket.read**([size])
+Read up to size bytes from the socket. Return a bytes object. If size is not given, it reads all data available from the socket until EOF; as such the method will not return until the socket is closed. This function tries to read as much data as requested (no “short reads”). This may be not possible with non-blocking socket though, and then less data will be returned.
+
+#### **socket.readinto**(buf[, nbytes])
+Read bytes into the buf. If nbytes is specified then read at most that many bytes. Otherwise, read at most len(buf) bytes. Just as read(), this method follows “no short reads” policy.
+Return value: number of bytes read and stored into buf.
+
+#### **socket.readline**()
+接收一行数据,遇换行符结束,并返回接收数据的对象 。
+
+#### **socket.write**(buf)
+将字节类型数据写入套接字,并返回写入成功的数据大小。
+
+### 示例
+
+#### TCP Server example
+
+```
+>>> import usocket
+>>> s = usocket.socket(usocket.AF_INET,usocket.SOCK_STREAM) # Create STREAM TCP socket
+>>> s.bind(('192.168.12.32', 6001))
+>>> s.listen(5)
+>>> s.setblocking(True)
+>>> sock,addr=s.accept()
+>>> sock.recv(10)
+b'rt-thread\r'
+>>> s.close()
+```
+
+#### TCP Client example
+
+```
+>>> import usocket
+>>> s = usocket.socket(usocket.AF_INET,usocket.SOCK_STREAM)
+>>> s.connect(("192.168.10.110",6000))
+>>> s.send("micropython")
+11
+>>> s.close()
+```
+
+`connect to a web site example`:
+```
+s = socket.socket()
+s.connect(socket.getaddrinfo('www.micropython.org', 80)[0][-1])
+```
+
+更多的内容可参考 [usocket](http://docs.micropython.org/en/latest/pyboard/library/usocket.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ussl.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ussl.md
new file mode 100644
index 0000000000000000000000000000000000000000..6351659e7e2c155bd5544ae79956a0d55310e16e
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ussl.md
@@ -0,0 +1,28 @@
+# ussl – SSL/TLS 模块
+
+This module implements a subset of the corresponding* [`CPython`](http://docs.micropython.org/en/latest/reference/glossary.html#term-cpython) *module, as described below. For more information, refer to the original CPython documentation: [`ssl`](https://docs.python.org/3.5/library/ssl.html#module-ssl).
+
+This module provides access to Transport Layer Security (previously and widely known as “Secure Sockets Layer”) encryption and peer authentication facilities for network sockets, both client-side and server-side.
+
+## 功能函数
+
+- `ussl.wrap_socket`(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None)
+
+Takes a [`stream`](http://docs.micropython.org/en/latest/reference/glossary.html#term-stream) *sock* (usually usocket.socket instance of `SOCK_STREAM` type), and returns an instance of ssl.SSLSocket, which wraps the underlying stream in an SSL context. Returned object has the usual [`stream`](http://docs.micropython.org/en/latest/reference/glossary.html#term-stream) interface methods like `read()`, `write()`, etc. In MicroPython, the returned object does not expose socket interface and methods like `recv()`, `send()`. In particular, a server-side SSL socket should be created from a normal socket returned from[`accept()`](http://docs.micropython.org/en/latest/library/usocket.html#usocket.socket.accept) on a non-SSL listening server socket. Depending on the underlying module implementation in a particular [`MicroPython port`](http://docs.micropython.org/en/latest/reference/glossary.html#term-micropython-port), some or all keyword arguments above may be not supported.
+
+Warning: Some implementations of `ussl` module do NOT validate server certificates, which makes an SSL connection established prone to man-in-the-middle attacks.
+
+## 异常类型
+
+- `ssl.SSLError`
+
+ This exception does NOT exist. Instead its base class, OSError, is used.
+
+## 常量
+
+- `ussl.CERT_NONE`
+
+- `ussl.CERT_OPTIONAL`
+
+- `ussl.CERT_REQUIRED`
+- Supported values for **cert_reqs** parameter.
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ustruct.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ustruct.md
new file mode 100644
index 0000000000000000000000000000000000000000..18cff7209c9592f7670dbe02697767fcb539cebb
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/ustruct.md
@@ -0,0 +1,82 @@
+## **ustruct** – 打包和解包原始数据类型
+
+**ustruct** 模块在 Python 值和以 Python 字节对象表示的 C 结构之间执行转换。
+
+- 支持 size/byte 的前缀: @, <, >, !.
+- 支持的格式代码: b, B, h, H, i, I, l, L, q, Q, s, P, f, d (最后2个需要支持浮点数).
+
+### 函数
+
+#### **ustruct.calcsize**(fmt)
+返回存放某一类型数据 fmt 需要的字节数。
+
+```
+fmt:数据类型
+ b — 字节型
+ B — 无符号字节型
+ h — 短整型
+ H — 无符号短整型
+ i — 整型
+ I — 无符号整型
+ l — 整型
+ L — 无符号整型
+ q — 长整型
+ Q — 无符号长整型
+ f — 浮点型
+ d — 双精度浮点型
+ P — 无符号型
+```
+
+示例:
+
+```python
+>>> print(struct.calcsize("i"))
+4
+>>> print(struct.calcsize("B"))
+1
+```
+
+#### **ustruct.pack**(fmt, v1, v2, ...)
+按照格式字符串 fmt 打包参数 v1, v2, ... 。返回值是参数打包后的字节对象。
+
+```
+fmt:同上
+```
+
+示例:
+
+```python
+>>> struct.pack("ii", 3, 2)
+b'\x03\x00\x00\x00\x02\x00\x00\x00'
+```
+
+#### **ustruct.unpack**(fmt, data)
+从 fmt 中解包数据。返回值是解包后参数的元组。
+
+```
+data:要解压的字节对象
+```
+
+示例:
+
+```python
+>>> buf = struct.pack("bb", 1, 2)
+>>> print(buf)
+b'\x01\x02'
+>>> print(struct.unpack("bb", buf))
+(1, 2)
+```
+
+#### **ustruct.pack_into**(fmt, buffer, offset, v1, v2, ...)
+按照格式字符串 fmt 压缩参数 v1, v2, ... 到缓冲区 buffer,开始位置是 offset。当offset 为负数时,从缓冲区末尾开始计数。
+
+#### **ustruct.unpack_from**(fmt, data, offset=0)
+以 fmt 作为规则从 data 的 offset 位置开始解包数据,如果 offset 是负数就是从缓冲区末尾开始计算。返回值是解包后的参数元组。
+```python
+>>> buf = struct.pack("bb", 1, 2)
+>>> print(struct.unpack("bb", buf))
+(1, 2)
+>>> print(struct.unpack_from("b", buf, 1))
+(2,)
+```
+更多的内容可参考 [ustruct](http://docs.micropython.org/en/latest/pyboard/library/ustruct.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/utime.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/utime.md
new file mode 100644
index 0000000000000000000000000000000000000000..38ec6c5182b5b642d17467aa585e54f0ef23d5d7
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/utime.md
@@ -0,0 +1,121 @@
+## utime – 时间相关函数
+
+utime 模块提供获取当前时间和日期、测量时间间隔和延迟的功能。
+
+初始时刻: `Unix` 使用 `POSIX` 系统标准,从 1970-01-01 00:00:00 `UTC` 开始。
+嵌入式程序从 2000-01-01 00:00:00 `UTC` 开始。
+
+保持实际日历日期/时间:需要一个实时时钟 `(RTC)`。在底层系统 (包括一些 `RTOS` 中),`RTC` 已经包含在其中。设置时间是通过 `OS/RTOS` 而不是 MicroPython 完成,查询日期/时间也需要通过系统 `API`。对于裸板系统时钟依赖于 ``machine.RTC()`` 对象。设置时间通过 ``machine.RTC().datetime(tuple)`` 函数,并通过下面方式维持:
+
+* 后备电池 (可能是选件、扩展板等)。
+* 使用网络时间协议 (需要用户设置)。
+* 每次上电时手工设置 (大部分只是在硬复位时需要设置,少部分每次复位都需要设置)。
+
+如果实际时间不是通过系统 / MicroPython RTC 维持,那么下面函数结果可能不是和预期的相同。
+
+### 函数
+
+#### utime.localtime([secs])
+
+ 从初始时间的秒转换为元组: (年, 月, 日, 时, 分, 秒, 星期, `yearday`) 。如果 `secs` 是空或者 `None`,那么使用当前时间。
+
+* `year` 年份包括世纪(例如2014)
+* `month` 范围 1-12
+* `day` 范围 1-31
+* `hour` 范围 0-23
+* `minute` 范围 0-59
+* `second` 范围 0-59
+* `weekday` 范围 0-6 对应周一到周日
+* `yearday` 范围 1-366
+
+#### utime.mktime()
+
+ 时间的反函数,它的参数是完整8参数的元组,返回值一个整数自2000年1月1日以来的秒数。
+
+#### utime.sleep(seconds)
+
+ 休眠指定的时间(秒),``Seconds`` 可以是浮点数。注意有些版本的 MicroPython不支持浮点数,为了兼容可以使用 ``sleep_ms()`` 和 ``sleep_us()``函数。
+
+#### utime.sleep_ms(ms)
+
+ 延时指定毫秒,参数不能小于0。
+
+#### utime.sleep_us(us)
+
+ 延时指定微秒,参数不能小于0。
+
+#### utime.ticks_ms()
+
+ 返回不断递增的毫秒计数器,在某些值后会重新计数(未指定)。计数值本身无特定意义,只适合用在``ticks_diff()``。
+ 注: 直接在这些值上执行标准数学运算(+,-)或关系运算符(<,>,>,> =)会导致无效结果。执行数学运算然后传递结果作为参数给`ticks_diff()` 或 ` ticks_add() ` 也将导致函数产生无效结果。
+
+#### utime.ticks_us()
+
+ 和上面 `ticks_ms()` 类似,只是返回微秒。
+
+#### utime.ticks_cpu()
+
+ 与 ``ticks_ms()`` 和 ``ticks_us()`` 类似,具有更高精度 (使用 CPU 时钟),并非每个端口都实现此功能。
+
+#### utime.ticks_add(ticks, delta)
+
+ 给定一个数字作为节拍的偏移值 `delta`,这个数字的值是正数或者负数都可以。
+ 给定一个 `ticks` 节拍值,本函数允许根据节拍值的模算数定义来计算给定节拍值之前或者之后 `delta` 个节拍的节拍值 。
+ `ticks` 参数必须是 `ticks_ms()`, `ticks_us()`, or `ticks_cpu()` 函数的直接返回值。然而,`delta` 可以是一个任意整数或者是数字表达式。`ticks_add` 函数对计算事件/任务的截至时间很有用。(注意:必须使用 `ticksdiff()` 函数来处理
+最后期限)。
+
+代码示例:
+
+```python
+## 查找 100ms 之前的节拍值
+print(utime.ticks_add(utime.ticks_ms(), -100))
+
+## 计算操作的截止时间然后进行测试
+deadline = utime.ticks_add(utime.ticks_ms(), 200)
+while utime.ticks_diff(deadline, utime.ticks_ms()) > 0:
+ do_a_little_of_something()
+
+## 找出本次移植节拍值的最大值
+print(utime.ticks_add(0, -1))
+```
+
+#### utime.ticks_diff(ticks1, ticks2)
+
+ 计算两次调用 `ticksms()`, `ticks_us()`, 或 `ticks_cpu()`之间的时间。因为这些函数的计数值可能会回绕,所以不能直接相减,需要使用 `ticks_diff()` 函数。“旧” 时间需要在 “新” 时间之前,否则结果无法确定。这个函数不要用在计算很长的时间 (因为 `ticks*()` 函数会回绕,通常周期不是很长)。通常用法是在带超时的轮询事件中调用:
+
+代码示例:
+
+```python
+## 等待 GPIO 引脚有效,但是最多等待500微秒
+start = time.ticks_us()
+while pin.value() == 0:
+ if time.ticks_diff(time.ticks_us(), start) > 500:
+ raise TimeoutError
+```
+
+#### utime.time()
+
+ 返回从开始时间的秒数(整数),假设 `RTC` 已经按照前面方法设置好。如果 `RTC` 没有设置,函数将返回参考点开始计算的秒数 (对于 `RTC` 没有后备电池的板子,上电或复位后的情况)。如果你开发便携版的 MicroPython 应用程序,你不要依赖函数来提供超过秒级的精度。如果需要高精度,使用 `ticks_ms()` 和 `ticks_us()` 函数。如果需要日历时间,使用不带参数的 `localtime()` 是更好选择。
+
+> [!NOTE]
+> 与 `CPython` 的区别:
+> 在 `CPython` 中,这个函数用浮点数返回从 `Unix` 开始时间(1970-01-01 00:00 `UTC`)的秒数,通常是毫秒级的精度。在 MicroPython 中,只有 `Unix` 版才使用相同开始时间,如果允许浮点精度,将返回亚秒精度。嵌入式硬件通常没有用浮点数表示长时间访问和亚秒精度,所以返回值是整数。一些嵌入式系统硬件不支持 `RTC` 电池供电方式,所以返回的秒数是从最后上电、或相对某个时间、以及特定硬件时间 (如复位)。
+
+### 示例
+
+``` msh
+>>> import utime
+>>> utime.sleep(1) # sleep for 1 second
+>>> utime.sleep_ms(500) # sleep for 500 milliseconds
+>>> utime.sleep_us(10) # sleep for 10 microseconds
+>>> start = utime.ticks_ms() # get value of millisecond counter
+>>> delta = utime.ticks_diff(utime.ticks_ms(), start) # compute time difference
+>>> delta
+6928
+>>> print(utime.ticks_add(utime.ticks_ms(), -100))
+1140718
+>>> print(utime.ticks_add(0, -1))
+1073741823
+```
+
+更多内容可参考 [utime](http://docs.micropython.org/en/latest/pyboard/library/utime.html#module-utime) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uzlib.md b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uzlib.md
new file mode 100644
index 0000000000000000000000000000000000000000..4be9fe0af853294e14ae77e333795cc0ff4b5688
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/micropython-docs/std-librarys/uzlib.md
@@ -0,0 +1,10 @@
+## **uzlib** – zlib 解压缩
+
+`uzlib` 模块实现了使用 DEFLATE 算法解压缩二进制数据 (常用的 zlib 库和 gzip 文档)。目前不支持压缩。
+
+### 函数
+
+#### **uzlib.decompress**(data)
+返回解压后的 bytes 数据。
+
+更多内容可参考 [uzlib](http://docs.micropython.org/en/latest/pyboard/library/uzlib.html) 。
diff --git a/rt-thread-version/rt-thread-standard/packages-manual/more.md b/rt-thread-version/rt-thread-standard/packages-manual/more.md
new file mode 100644
index 0000000000000000000000000000000000000000..4c39b90d3e794c9ca8c797c2f0fa7732dbc0c1b1
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/packages-manual/more.md
@@ -0,0 +1,121 @@
+# 软件包分类
+
+以下列出一些常用的软件包,更多软件包还请访问 [RT-Thread 软件包中心](http://packages.rt-thread.org/) 进行查看和使用。
+
+## IoT
+
+与物联网相关的软件包,包括网络想干软件包,云接入软件包等。
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | ----------------------------------------------------- |
+| [ota_downloader](http://packages.rt-thread.org/detail.html?package=ota_downloader) | 基于RT-Thread OTA 组件的 固件下载器 |
+| [onenet](http://packages.rt-thread.org/detail.html?package=onenet) | 连接中国移动 OneNet 云的软件包 |
+| [webclient](http://packages.rt-thread.org/detail.html?package=webclient) | RT-Thread 官方开源的 http/https 协议客户端 |
+| [webnet](http://packages.rt-thread.org/detail.html?package=webnet) | RT-Thread 官方开源的、轻量级、可定制嵌入式 Web 服务器 |
+| [webTerminal](http://packages.rt-thread.org/detail.html?package=WebTerminal) | 可以在浏览器上运行的终端 |
+| [rw007](http://packages.rt-thread.org/detail.html?package=rw007) | RT-Thread 的 RW007 驱动 (SPI Wi-Fi 模式) |
+| [pahomqtt](http://packages.rt-thread.org/detail.html?package=pahomqtt) | Eclipse 开源的 MQTT C/C++ 客户端 |
+| [netutils](http://packages.rt-thread.org/detail.html?package=netutils) | RT-Thread 网络小工具集 |
+| [at_device](http://packages.rt-thread.org/detail.html?package=at_device) | AT 组件在不同设备上的移植或示例 |
+
+[更多IOT相关软件包...](http://packages.rt-thread.org/search.html?classify=iot)
+
+## 外设
+
+与底层外设硬件相关的软件包,sensor软件包等
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [aht10](http://packages.rt-thread.org/detail.html?package=aht10) | 数字温湿度传感器 aht10 的驱动库 |
+| [dht11](http://packages.rt-thread.org/detail.html?package=dht11) | DHT11 单总线数字温湿度传感器 |
+| [gt9147](http://packages.rt-thread.org/detail.html?package=gt9147) | GT9147 触摸芯片的驱动包 |
+| [ft6236](http://packages.rt-thread.org/detail.html?package=ft6236) | FT6236 触摸芯片的驱动包 |
+| [mpu6xxx](http://packages.rt-thread.org/detail.html?package=mpu6xxx) | 兼容 mpu6000, mpu6050, mpu6500, mpu9250 等等型号的驱动库 |
+| [pcf8574](http://packages.rt-thread.org/detail.html?package=pcf8574) | 针对 I2C 并行口扩展 8 位 I/O 软件包 |
+| [easyblink](http://packages.rt-thread.org/detail.html?package=easyblink) | 小巧轻便的 LED 控制软件包, 可以容易地控制 LED 开 、关、反转和各种间隔闪烁 |
+| [max17048](http://packages.rt-thread.org/detail.html?package=max17048) | 电池监测芯片 |
+| [ds18b20](http://packages.rt-thread.org/detail.html?package=ds18b20) | 单总线数字温湿度传感器 ds18b20 的软件包 |
+
+[更多外设相关软件包...](http://packages.rt-thread.org/search.html?classify=peripherals)
+
+## 系统
+
+系统级软件包,监控系统行为、其他文件系统等
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | ---------------------------------------------------- |
+| [CMSIS](http://packages.rt-thread.org/detail.html?package=CMSIS) | CMSIS 软件包在 RT-Thread 上的移植 |
+| [FlashDB](http://packages.rt-thread.org/detail.html?package=FlashDB) | 一款支持 KV 数据和时序数据的轻量级数据库 |
+| [fal](http://packages.rt-thread.org/detail.html?package=fal) | Flash 抽象层的实现,负责管理 Flash 设备和 Flash 分区 |
+| [littlefs](http://packages.rt-thread.org/detail.html?package=littlefs) | 为微控制器设计的一个小型的且掉电安全的文件系统 |
+| [syswatch](http://packages.rt-thread.org/detail.html?package=syswatch) | 系统看守:一个用于保障系统长期正常运行的组件 |
+| [rt_printf](http://packages.rt-thread.org/detail.html?package=rt_printf) | 线程安全版本的rt_kprintf |
+| [LittlevGL2RTT](http://packages.rt-thread.org/detail.html?package=LittlevGL2RTT) | LittlevGL2RTT 是基于 RT-Thread 的图形库软件包 |
+
+[更多系统相关软件包...](http://packages.rt-thread.org/search.html?classify=system)
+
+## 编程语言
+
+可运行在终端板卡上的各种编程语言,脚本或解释器
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [jerryscript](http://packages.rt-thread.org/detail.html?package=jerryscript) | 针对 RT-Thread 的JerryScript 移植 |
+| [Lua](http://packages.rt-thread.org/detail.html?package=Lua) | Lua 库适配 RT-Thread 3.0 (基于 lua 5.1.4版本 和 lua 5.3.4版本) |
+| [micropython](http://packages.rt-thread.org/detail.html?package=micropython) | MicroPython 在 RT-Thread 上的移植 |
+
+[更多编程语言相关软件包...](http://packages.rt-thread.org/search.html?classify=language)
+
+## 工具
+
+辅助使用的一些工具软件包
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | -------------------------------------------------------- |
+| [adbd](http://packages.rt-thread.org/detail.html?package=adbd) | 在 RT-Thread 上实现的 Android ADB daemon |
+| [CmBacktrace](http://packages.rt-thread.org/detail.html?package=CmBacktrace) | ARM Cortex-M 系列 MCU 错误追踪库 |
+| [EasyFlash](http://packages.rt-thread.org/detail.html?package=EasyFlash) | 轻量级嵌入式 Flash 存储器库,让 Flash 成为小型 KV 数据库 |
+| [EasyLogger](http://packages.rt-thread.org/detail.html?package=EasyLogger) | 一款超轻量级(ROM<1.6K, RAM<0.3k)、高性能的 C/C++ 日志库 |
+| [SystemView](http://packages.rt-thread.org/detail.html?package=SystemView) | SEGGER 的 SystemView 移植 |
+| [ulog_easyflash](http://packages.rt-thread.org/detail.html?package=ulog_easyflash) | 基于 EasyFlash 的 ulog 插件 |
+
+[更多工具相关软件包...](http://packages.rt-thread.org/search.html?classify=tools)
+
+## 杂类
+
+一些未归类的软件包,demo,示例等
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | ------------------------------------------ |
+| [fastlz](http://packages.rt-thread.org/detail.html?package=fastlz) | 一款极速的压缩库 |
+| [filesystem_samples](http://packages.rt-thread.org/detail.html?package=filesystem_samples) | RT-Thread 文件系统示例 |
+| [network_samples](http://packages.rt-thread.org/detail.html?package=network_samples) | RT-Thread 网络示例 |
+| [peripheral_samples](http://packages.rt-thread.org/detail.html?package=peripheral_samples) | RT-Thread 外设示例 |
+| [crclib](http://packages.rt-thread.org/detail.html?package=crclib) | 一个包含8位、16位、32位CRC校验计算的函数库 |
+
+[更多杂类相关软件包...](http://packages.rt-thread.org/search.html?classify=misc)
+
+## 多媒体
+
+RT-Thread上的音视频软件包
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | --------------------------------------------- |
+| [TJpgDec](http://packages.rt-thread.org/detail.html?package=TJpgDec) | JPEG 解码库 |
+| [touchgfx2rtt](http://packages.rt-thread.org/detail.html?package=touchgfx2rtt) | touchgfx在RT-Thread上的移植。 |
+| [wavplayer](http://packages.rt-thread.org/detail.html?package=wavplayer) | 简洁的wav格式的音乐播放器,提供播放和录音功能 |
+| [STemWin](http://packages.rt-thread.org/detail.html?package=STemWin) | STemWin在RT-Thread上的移植 |
+
+[更多多媒体相关软件包...](http://packages.rt-thread.org/search.html?classify=multimedia)
+
+## 安全
+
+加解密算法及安全传输层
+
+| 软件包名称 | 备注 |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [mbedtls](http://packages.rt-thread.org/detail.html?package=mbedtls) | 一个开源的、可移植的、易于使用的、可读的且灵活的 SSL 库 |
+| [tinycrypt](http://packages.rt-thread.org/detail.html?package=tinycrypt) | 一个简小并且可配置的加解密软件包 |
+| [yd_crypto](http://packages.rt-thread.org/detail.html?package=yd_crypto) | 用于微控制器的加解密算法库,平台无关、算法独立、易移植、易使用。 |
+
+[更多安全相关软件包...](http://packages.rt-thread.org/search.html?classify=security)
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/at/at.md b/rt-thread-version/rt-thread-standard/programming-manual/at/at.md
new file mode 100644
index 0000000000000000000000000000000000000000..64ea5b19fc3164a7b20084f219e54d768f4f1e89
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/at/at.md
@@ -0,0 +1,885 @@
+# AT 组件 #
+
+## AT 命令简介
+
+AT 命令(AT Commands)最早是由发明拨号调制解调器(MODEM)的贺氏公司(Hayes)为了控制 MODEM 而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号 MODEM 基本退出一般使用市场,但是 AT 命令保留下来。当时主要的移动电话生产厂家共同为 GSM 研制了一整套 AT 命令,用于控制手机的 GSM 模块。AT 命令在此基础上演化并加入 GSM 07.05 标准以及后来的 GSM 07.07 标准,实现比较健全的标准化。
+
+在随后的 GPRS 控制、3G 模块等方面,均采用的 AT 命令来控制,AT 命令逐渐在产品开发中成为实际的标准。如今,AT 命令也广泛的应用于嵌入式开发领域,AT 命令作为主芯片和通讯模块的协议接口,硬件接口一般为串口,这样主控设备可以通过简单的命令和硬件设计完成多种操作。
+
+**AT 命令集是一种应用于 AT 服务器(AT Server)与 AT 客户端(AT Client)间的设备连接与数据通信的方式**。 其基本结构如下图所示:
+
+
+
+1. 一般 AT 命令由三个部分组成,分别是:前缀、主体和结束符。其中前缀由字符 AT 构成;主体由命令、参数和可能用到的数据组成;结束符一般为 `` (`"\r\n"`)。
+
+2. AT 功能的实现需要 AT Server 和 AT Client 两个部分共同完成。
+
+3. AT Server 主要用于接收 AT Client 发送的命令,判断接收的命令及参数格式,并下发对应的响应数据,或者主动下发数据。
+
+4. AT Client 主要用于发送命令、等待 AT Server 响应,并对 AT Server 响应数据或主动发送的数据进行解析处理,获取相关信息。
+
+5. AT Server 和 AT Client 之间支持多种数据通讯的方式(UART、SPI 等),目前最常用的是串口 UART 通讯方式。
+
+6. AT Server 向 AT Client 发送的数据分成两种:响应数据和 URC 数据。
+
+- 响应数据: AT Client 发送命令之后收到的 AT Server 响应状态和信息。
+
+- URC 数据: AT Server 主动发送给 AT Client 的数据,一般出现在一些特殊的情况,比如 WIFI 连接断开、TCP 接收数据等,这些情况往往需要用户做出相应操作。
+
+随着 AT 命令的逐渐普及,越来越多的嵌入式产品上使用了 AT 命令,AT 命令作为主芯片和通讯模块的协议接口,硬件接口一般为串口,这样主控设备可以通过简单的命令和硬件设计完成多种操作。
+
+虽然 AT 命令已经形成了一定的标准化,但是不同的芯片支持的 AT 命令并没有完全统一,这直接提高了用户使用的复杂性。对于 AT 命令的发送和接收以及数据的解析没有统一的处理方式。并且在使用 AT 设备连接网络时,只能通过命令完成简单的设备连接和数据收发功能,很难做到对上层网络应用接口的适配,不利于产品设备的开发。
+
+为了方便用户使用 AT 命令,简单的适配不同的 AT 模块, RT-Thread 提供了 AT 组件用于 AT 设备的连接和数据通讯。AT 组件的实现包括客户端的和服务器两部分。
+
+## AT 组件简介
+
+AT 组件是基于 RT-Thread 系统的 `AT Server` 和 `AT Client` 的实现,组件完成 AT 命令的发送、命令格式及参数判断、命令的响应、响应数据的接收、响应数据的解析、URC 数据处理等整个 AT 命令数据交互流程。
+
+通过 AT 组件,设备可以作为 AT Client 使用串口连接其他设备发送并接收解析数据,可以作为 AT Server 让其他设备甚至电脑端连接完成发送数据的响应,也可以在本地 shell 启动 CLI 模式使设备同时支持 AT Server 和 AT Client 功能,该模式多用于设备开发调试。
+
+**AT 组件资源占用:**
+
+- AT Client 功能:4.6K ROM 和 2.0K RAM;
+
+- AT Server 功能:4.0K ROM 和 2.5K RAM;
+
+- AT CLI 功能: 1.5K ROM ,几乎没有使用 RAM。
+
+整体看来,AT 组件资源占用极小,因此非常适用应用于资源有限的嵌入式设备中。AT 组件代码主要位于 `rt-thread/components/net/at/` 目录中。主要的功能包括如下,
+
+**AT Server 主要功能特点:**
+
+- 基础命令: 实现多种通用基础命令(ATE、ATZ 等);
+- 命令兼容: 命令支持忽略大小写,提高命令兼容性;
+- 命令检测: 命令支持自定义参数表达式,并实现对接收的命令参数自检测功能;
+- 命令注册: 提供简单的用户自定义命令添加方式,类似于 `finsh/msh` 命令添加方式;
+- 调试模式: 提供 AT Server CLI 命令行交互模式,主要用于设备调试。
+
+**AT Client 主要功能特点:**
+
+- URC 数据处理: 完备的 URC 数据的处理方式;
+- 数据解析: 支持自定义响应数据的解析方式,方便获取响应数据中相关信息;
+- 调试模式: 提供 AT Client CLI 命令行交互模式,主要用于设备调试。
+- AT Socket:作为 AT Client 功能的延伸,使用 AT 命令收发作为基础,实现标准的 BSD Socket API,完成数据的收发功能,使用户通过 AT 命令完成设备连网和数据通讯。
+- 多客户端支持: AT 组件目前支持多客户端同时运行。
+
+## AT Server ##
+
+### AT Server 配置 ###
+
+当我们使用 AT 组件中的 AT Server 功能时需要在 rtconfig.h 中定义如下配置:
+
+| **宏定义** | **描述** |
+| ---- | ---- |
+|RT_USING_AT| 开启 AT 组件 |
+|AT_USING_SERVER |开启 AT Server 功能|
+|AT_SERVER_DEVICE |定义设备上 AT Server 功能使用的串口通讯设备名称,确保未被使用且设备名称唯一,例如 `uart3` 设备|
+|AT_SERVER_RECV_BUFF_LEN|AT Server 设备最大接收数据的长度|
+|AT_CMD_END_MARK_CRLF|判断接收命令的行结束符 |
+|AT_USING_CLI | 开启服务器命令行交互模式|
+|AT_DEBUG|开启 AT 组件 DEBUG 模式,可以显示更多调试日志信息 |
+|AT_PRINT_RAW_CMD | 开启实时显示 AT 命令通信数据模式,方便调试|
+
+对于不同的 AT 设备,发送命令的行结束符的格式有几种: `"\r\n"`、`"\r"`、`"\n"`,用户需要根据 AT Server 连接的设备类型选用对应的行结束符,进而判断发送命令行的结束, 定义的方式如下:
+
+| **宏定义** | **结束符** |
+| ---- | ---- |
+| AT_CMD_END_MARK_CRLF | `"\r\n"` |
+| AT_CMD_END_MARK_CR | `"\r"` |
+| AT_CMD_END_MARK_LF | `"\n"` |
+
+上面配置选项可以直接在 `rtconfig.h` 文件中添加使用,也可以通过组件包管理工具 Env 配置选项加入,ENV 中具体路径如下:
+
+```c
+RT-Thread Components --->
+ Network --->
+ AT commands --->
+ [*] Enable AT commands
+ [*] Enable debug log output
+ [*] Enable AT commands server
+ (uart3) Server device name
+ (256) The maximum length of server data accepted
+ The commands new line sign (\r\n) --->
+ [ ] Enable AT commands client
+ [*] Enable command-line interface for AT commands
+ [ ] Enable print RAW format AT command communication data
+```
+
+添加配置完成之后可以使用命令行重新生成工程,或使用 scons 来进行编译生成。
+
+### AT Server 初始化 ###
+
+配置开启 AT Server 配置之后,需要在启动时对它进行初始化,开启 AT Server 功能,如果程序中已经使用了组件自动初始化,则不再需要额外进行单独的初始化,否则需要在初始化任务中调用如下函数:
+
+```c
+int at_server_init(void);
+```
+AT Server 初始化函数,属于应用层函数,需要在使用 AT Server 功能或者使用 AT Server CLI 功能前调用。`at_server_init()` 函数完成对 AT 命令存放数据段初始化、AT Server 设备初始化以及 AT Server 使用的信号量等资源的初始化,并创建 `at_server` 线程用于 AT Server 中数据的接收的解析。
+
+AT Server 初始化成功之后,设备就可以作为 AT 服务器与 AT 客户端的串口设备连接并进行数据通讯,或者使用串口转化工具连接 PC,使 PC 端串口调试助手作为 AT 客户端与其进行数据通讯。
+
+### 自定义 AT 命令添加方式 ###
+
+目前,不同厂家的 AT 设备使用的 AT 命令集的格式没有完全的统一的标准,所以 AT 组件中的 AT Server 只支持了部分基础通用 AT 命令,例如:ATE、AT+RST 等,这些命令只能满足设备基本操作,用户想使用更多功能需要针对不同 AT 设备完成自定义 AT Server 命令,AT 组件提供类似于 `finsh/msh` 命令添加方式的 AT 命令添加方式,方便用户实现需要的命令。
+
+AT Server 目前默认支持的基础命令如下:
+
+- AT:AT 测试命令;
+- ATZ:设备恢复出厂设置;
+- AT+RST:设备重启;
+- ATE:ATE1 开启回显,ATE0 关闭回显;
+- AT&L:列出全部命令列表;
+- AT+UART:设置串口设置信息。
+
+AT 命令根据传入的参数格式不同可以实现不同的功能,对于每个 AT 命令最多包含四种功能,如下所述:
+
+- 测试功能:`AT+=?` 用于查询命令参数格式及取值范围;
+- 查询功能:`AT+?` 用于返回命令参数当前值;
+- 设置功能:`AT+=...` 用于用户自定义参数值;
+- 执行功能:`AT+` 用于执行相关操作。
+
+每个命令的四种功能并不需要全部实现,用户自定义添加 AT Server 命令时,可根据自己需求实现一种或几种上述功能函数,未实现的功能可以使用 `NULL` 表示,再通过自定义命令添加函数添加到基础命令列表,添加方式类似于 `finsh/msh` 命令添加方式,添加函数如下:
+
+```c
+AT_CMD_EXPORT(_name_, _args_expr_, _test_, _query_, _setup_, _exec_);
+```
+
+|**参数** |**描述** |
+| ---------- | ------------------------------- |
+| `_name_ ` | AT 命令名称 |
+| `_args_expr_` | AT 命令参数表达式;(无参数为 NULL,`<>` 中为必选参数,`[]` 中为可选参数) |
+| `_test_` | AT 测试功能函数名;(无实现为 NULL) |
+| `_query_` | AT 查询功能函数名;(同上) |
+| `_setup_` | AT 设置功能函数名;(同上) |
+| `_exec_` | AT 执行功能函数名;(同上) |
+
+如下为 AT 命令注册示例,`AT+TEST` 命令存在两个参数,第一个参数为必选参数,第二个参数为可选参数,命令实现查询功能和执行功能:
+
+```c
+static at_result_t at_test_exec(void)
+{
+ at_server_printfln("AT test commands execute!");
+
+ return 0;
+}
+static at_result_t at_test_query(void)
+{
+ at_server_printfln("AT+TEST=1,2");
+
+ return 0;
+}
+
+AT_CMD_EXPORT("AT+TEST", =[,], NULL, at_test_query, NULL, at_test_exec);
+```
+
+### AT Server API 接口
+
+#### 发送数据至客户端(不换行)
+
+```c
+void at_server_printf(const char *format, ...);
+```
+
+该函数用于 AT Server 通过串口设备发送固定格式的数据到对应的 AT Client 串口设备上,数据结尾不带换行符。用于自定义 AT Server 中 AT 命令的功能函数中。
+
+| **参数** | **描述** |
+|------|-------------------------|
+| format | 自定义输入数据的表达式 |
+| ... | 输入数据列表,为可变参数 |
+
+#### 发送数据至客户端(换行)
+
+```c
+void at_server_printfln(const char *format, ...);
+```
+
+ 该函数用于 AT Server 通过串口设备发送固定格式的数据到对应的 AT Client 串口设备上,数据结尾带换行符。用于自定义 AT Server 中 AT 命令的功能函数中。
+
+| **参数** | **描述** |
+|------|-------------------------|
+| format | 自定义输入数据的表达式 |
+| ... | 输入数据列表,为可变参数 |
+
+#### 发送命令执行结果至客户端
+
+```c
+void at_server_print_result(at_result_t result);
+```
+
+该函数用于 AT Server 通过串口设备发送命令执行结果到对应的 AT Client 串口设备上。AT 组件提供多种固定的命令执行结果类型,自定义命令时可以直接使用函数返回结果;
+
+| **参数** | **描述** |
+|------|-----------------|
+| result | 命令执行结果类型 |
+
+AT 组件中命令执行结果类型以枚举类型给出,如下表所示:
+
+| 命令执行结果类型 | 解释 |
+|------------------------|------------------|
+| AT_RESULT_OK | 命令执行成功 |
+| AT_RESULT_FAILE | 命令执行失败 |
+| AT_RESULT_NULL | 命令无返回结果 |
+| AT_RESULT_CMD_ERR | 输入命令错误 |
+| AT_RESULT_CHECK_FAILE | 参数表达式匹配错误 |
+| AT_RESULT_PARSE_FAILE | 参数解析错误 |
+
+可参考以下代码了解如何使用 at_server_print_result 函数:
+
+```c
+static at_result_t at_test_setup(const char *args)
+{
+ if(!args)
+ {
+ /* 如果传入的命令之后的参数错误,返回表达式匹配错误结果 */
+ at_server_print_result(AT_RESULT_CHECK_FAILE);
+ }
+
+ /* 正常情况下返回执行成功结果 */
+ at_server_print_result(AT_RESULT_OK);
+ return 0;
+}
+static at_result_t at_test_exec(void)
+{
+ // execute some functions of the AT command.
+
+ /* 该命令不需要返回结果 */
+ at_server_print_result(AT_RESULT_NULL);
+ return 0;
+}
+AT_CMD_EXPORT("AT+TEST", =,, NULL, NULL, at_test_setup, at_test_exec);
+```
+
+#### 解析输入命令参数
+
+```c
+int at_req_parse_args(const char *req_args, const char *req_expr, ...);
+```
+
+一个 AT 命令的四种功能函数中,只有设置函数有入参,该入参为去除 AT 命令剩余部分,例如一个命令输入为 `"AT+TEST=1,2,3,4"`,则设置函数的入参为参数字符串 `"=1,2,3,4"` 部分。
+
+该命令解析函数主要用于 AT 命令的设置函数中,用于解析传入字符串参数,得到对应的多个输入参数,用于执行后面操作,这里的解析语法使用的标准 `sscanf` 解析语法,后面 AT Client 参数解析函数中会详细介绍。
+
+| **参数** | **描述** |
+|---------|-----------------------------------------------|
+| req_args | 请求命令的传入参数字符串 |
+| req_expr | 自定义参数解析表达式,用于解析上述传入参数数据 |
+| ... | 输出的解析参数列表,为可变参数 |
+| **返回** | -- |
+| >0 | 成功,返回匹配参数表达式的可变参数个数 |
+| =0 | 失败,无匹配参数表达式的参数 |
+| -1 | 失败,参数解析错误 |
+
+可参考以下代码了解如何使用 at_server_print_result 函数:
+
+```c
+static at_result_t at_test_setup(const char *args)
+{
+ int value1,value2;
+
+ /* args 的输入标准格式应为 "=1,2","=%d,%d" 为自定义参数解析表达式,解析得到结果存入 value1 和 value2 变量 */
+ if (at_req_parse_args(args, "=%d,%d", &value1, &value2) > 0)
+ {
+ /* 数据解析成功,回显数据到 AT Server 串口设备 */
+ at_server_printfln("value1 : %d, value2 : %d", value1, value2);
+
+ /* 数据解析成功,解析参数的个数大于零,返回执行成功 */
+ at_server_print_result(AT_RESULT_OK);
+ }
+ else
+ {
+ /* 数据解析失败,解析参数的个数不大于零,返回解析失败结果类型 */
+ at_server_print_result(AT_RESULT_PARSE_FAILE);
+ }
+ return 0;
+}
+/* 添加 "AT+TEST" 命令到 AT 命令列表,命令参数格式为两个必选参数 和 */
+AT_CMD_EXPORT("AT+TEST", =,, NULL, NULL, at_test_setup, NULL);
+```
+
+**移植相关接口**
+
+AT Server 默认已支持多种基础命令(ATE、ATZ 等),其中部分命令的函数实现与硬件或平台相关,需要用户自定义实现。AT 组件源码 `src/at_server.c` 文件中给出了移植文件的弱函数定义,用户可在项目中新建移植文件实现如下函数完成移植接口,也可以直接在文件中修改弱函数完成移植接口。
+
+1. 设备重启函数:`void at_port_reset(void);`。该函数完成设备软重启功能,用于 AT Server 中基础命令 AT+RST 的实现。
+
+2. 设备恢复出厂设置函数:`void at_port_factory_reset(void);`。该函数完成设备恢复出厂设置功能,用于 AT Server 中基础命令 ATZ 的实现。
+
+3. 链接脚本中添加命令表(gcc 添加,keil、iar 跳过)
+
+工程中若使用 gcc 工具链,需在链接脚本中添加 AT 服务器命令表对应的 section ,参考如下链接脚本:
+
+```c
+/* Constant data goes into FLASH */
+.rodata :
+{
+ ...
+
+ /* section information for RT-thread AT package */
+ . = ALIGN(4);
+ __rtatcmdtab_start = .;
+ KEEP(*(RtAtCmdTab))
+ __rtatcmdtab_end = .;
+ . = ALIGN(4);
+} > CODE
+```
+
+## AT Client
+
+### AT Client 配置
+
+当我们使用 AT 组件中的 AT Client 功能是需要在 rtconfig.h 中定义如下配置:
+
+```c
+#define RT_USING_AT
+#define AT_USING_CLIENT
+#define AT_CLIENT_NUM_MAX 1
+#define AT_USING_SOCKET
+#define AT_USING_CLI
+#define AT_PRINT_RAW_CMD
+```
+
+- `RT_USING_AT`: 用于开启或关闭 AT 组件;
+
+- `AT_USING_CLIENT`: 用于开启 AT Client 功能;
+
+- `AT_CLIENT_NUM_MAX`: 最大同时支持的 AT 客户端数量。
+
+- `AT_USING_SOCKET`:用于 AT 客户端支持标准 BSD Socket API,开启 AT Socket 功能。
+
+- `AT_USING_CLI`: 用于开启或关闭客户端命令行交互模式。
+
+- `AT_PRINT_RAW_CMD`:用于开启 AT 命令通信数据的实时显示模式,方便调试
+
+上面配置选项可以直接在 `rtconfig.h` 文件中添加使用,也可以通过组件包管理工具 Env 配置选项加入,ENV 中具体路径如下:
+
+```c
+RT-Thread Components --->
+ Network --->
+ AT commands --->
+ [*] Enable AT commands
+ [ ] Enable debug log output
+ [ ] Enable AT commands server
+ [*] Enable AT commands client
+ (1) The maximum number of supported clients
+ [*] Enable BSD Socket API support by AT commnads
+ [*] Enable command-line interface for AT commands
+ [ ] Enable print RAW format AT command communication data
+```
+
+添加配置完成之后可以使用命令行重新生成工程,或使用 scons 来进行编译生成。
+
+### AT Client 初始化 ###
+
+配置开启 AT Client 配置之后,需要在启动时对它进行初始化,开启 AT client 功能,如果程序中已经使用了组件自动初始化,则不再需要额外进行单独的初始化,否则需要在初始化任务中调用如下函数:
+
+```c
+int at_client_init(const char *dev_name, rt_size_t recv_bufsz);
+```
+
+AT Client 初始化函数,属于应用层函数,需要在使用 AT Client 功能或者使用 AT Client CLI 功能前调用。`at_client_init()` 函数完成对 AT Client 设备初始化、AT Client 移植函数的初始化、AT Client 使用的信号量、互斥锁等资源初始化,并创建 `at_client` 线程用于 AT Client 中数据的接收的解析以及对 URC 数据的处理。
+
+### AT Client 数据收发方式 ###
+
+AT Client 主要功能是发送 AT 命令、接收数据并解析数据。下面是对 AT Client 数据接收和发送相关流程与函数介绍。相关结构体定义:
+
+```c
+struct at_response
+{
+ /* response buffer */
+ char *buf;
+ /* the maximum response buffer size */
+ rt_size_t buf_size;
+ /* the number of setting response lines
+ * == 0: the response data will auto return when received 'OK' or 'ERROR'
+ * != 0: the response data will return when received setting lines number data */
+ rt_size_t line_num;
+ /* the count of received response lines */
+ rt_size_t line_counts;
+ /* the maximum response time */
+ rt_int32_t timeout;
+};
+typedef struct at_response *at_response_t;
+```
+
+AT 组件中,该结构体用于定义一个 AT 命令响应数据的控制块,用于存放或者限制 AT 命令响应数据的数据格式。其中 `buf` 用于存放接收到的响应数据,注意的是 buf 中存放的数据并不是原始响应数据,而是原始响应数据去除结束符(`"\r\n"`)的数据,**buf 中每行数据以 '\0' 分割,方便按行获取数据**。`buf_size` 为用户自定义本次响应最大支持的接收数据的长度,由用户根据自己命令返回值长度定义。`line_num` 为用户自定义的本次响应数据需要接收的行数,**如果没有响应行数限定需求,可以置为 0**。 `line_counts` 用于记录本次响应数据总行数。`timeout` 为用户自定义的本次响应数据最大响应时间。该结构体中 `buf_size`、`line_num`、`timeout` 三个参数为限制条件,在结构体创建时设置,其他参数为存放数据参数,用于后面数据解析。
+
+相关 API 接口介绍:
+
+#### 创建响应结构体
+
+```c
+at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
+```
+
+| **参数** | **描述** |
+|---------|-----------------------------------------|
+| buf_size | 本次响应最大支持的接收数据的长度 |
+| line_num | 本次响应需要返回数据的行数,行数是以标准结束符(如 `"\r\n"`)划分。若为 0 ,则接收到 "OK" 或 "ERROR" 数据后结束本次响应接收;若大于 0,接收完当前设置行号的数据后返回成功 |
+| timeout | 本次响应数据最大响应时间,数据接收超时返回错误 |
+| **返回** | -- |
+| != NULL | 成功,返回指向响应结构体的指针 |
+| = NULL | 失败,内存不足 |
+
+该函数用于创建自定义的响应数据接收结构,用于后面接收并解析发送命令响应数据。
+
+#### 删除响应结构体
+
+```c
+void at_delete_resp(at_response_t resp);
+```
+
+| **参数** | **描述** |
+|----|-------------------------|
+| resp | 准备删除的响应结构体指针 |
+
+该函数用于删除创建的响应结构体对象,一般与**at_create_resp**创建函数成对出现。
+
+#### 设置响应结构体参数
+
+```c
+at_response_t at_resp_set_info(at_response_t resp, rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
+```
+
+| **参数** | **描述** |
+|---------|----------------------------------|
+| resp | 已经创建的响应结构体指针 |
+| buf_size | 本次响应最大支持的接收数据的长度 |
+| line_num | 本次响应需要返回数据的行数,行数是以标准结束符划分 若为 0 ,则接收到 "OK" 或 "ERROR" 数据后结束本次响应接收 若大于 0,接收完当前设置行号的数据后返回成功 |
+| timeout | 本次响应数据最大响应时间,数据接收超时返回错误 |
+| **返回** | -- |
+| != NULL | 成功,返回指向响应结构体的指针 |
+| = NULL | 失败,内存不足 |
+
+该函数用于设置已经创建的响应结构体信息,主要设置对响应数据的限制信息,一般用于创建结构体之后,发送 AT 命令之前。该函数主要用于设备初始化时命令的发送,可以减少响应结构体创建次数,降低代码资源占用。
+
+#### 发送命令并接收响应
+
+```c
+rt_err_t at_exec_cmd(at_response_t resp, const char *cmd_expr, ...);
+```
+
+| **参数** | **描述** |
+|---------|-----------------------------|
+| resp | 创建的响应结构体指针 |
+| cmd_expr | 自定义输入命令的表达式 |
+| ... | 输入命令数据列表,为可变参数 |
+| **返回** | -- |
+| >=0 | 成功 |
+| -1 | 失败 |
+| -2 | 失败,接收响应超时 |
+
+该函数用于 AT Client 发送命令到 AT Server,并等待接收响应,其中 `resp` 是已经创建好的响应结构体的指针,AT 命令的使用匹配表达式的可变参输入,**输入命令的结尾不需要添加命令结束符**。
+
+可参考以下代码了解如何使用以上几个 AT 命令收发相关函数使用方式:
+
+```c
+/*
+ * 程序清单:AT Client 发送命令并接收响应例程
+ */
+
+#include
+#include /* AT 组件头文件 */
+
+int at_client_send(int argc, char**argv)
+{
+ at_response_t resp = RT_NULL;
+
+ if (argc != 2)
+ {
+ LOG_E("at_cli_send [command] - AT client send commands to AT server.");
+ return -RT_ERROR;
+ }
+
+ /* 创建响应结构体,设置最大支持响应数据长度为 512 字节,响应数据行数无限制,超时时间为 5 秒 */
+ resp = at_create_resp(512, 0, rt_tick_from_millisecond(5000));
+ if (!resp)
+ {
+ LOG_E("No memory for response structure!");
+ return -RT_ENOMEM;
+ }
+
+ /* 发送 AT 命令并接收 AT Server 响应数据,数据及信息存放在 resp 结构体中 */
+ if (at_exec_cmd(resp, argv[1]) != RT_EOK)
+ {
+ LOG_E("AT client send commands failed, response error or timeout !");
+ return -ET_ERROR;
+ }
+
+ /* 命令发送成功 */
+ LOG_D("AT Client send commands to AT Server success!");
+
+ /* 删除响应结构体 */
+ at_delete_resp(resp);
+
+ return RT_EOK;
+}
+#ifdef FINSH_USING_MSH
+#include
+/* 输出 at_Client_send 函数到 msh 中 */
+MSH_CMD_EXPORT(at_Client_send, AT Client send commands to AT Server and get response data);
+#endif
+```
+
+发送和接收数据的实现原理比较简单,主要是对 AT Client 绑定的串口设备的读写操作,并设置相关行数和超时来限制响应数据,值得注意的是,正常情况下需要先创建 resp 响应结构体传入 at_exec_cmd 函数用于数据的接收,当 at_exec_cmd 函数传入 resp 为 NULL 时说明本次发送数据**不考虑处理响应数据直接返回结果**。
+
+### AT Client 数据解析方式 ###
+
+数据正常获取之后,需要对响应的数据进行解析处理,这也是 AT Client 重要的功能之一。 AT Client 中数据的解析提供自定义解析表达式的解析形式,其解析语法使用标准的 `sscanf` 解析语法。开发者可以通过自定义数据解析表达式回去响应数据中有用信息,前提是开发者需要提前查看相关手册了解 AT Client 连接的 AT Server 设备响应数据的基本格式。下面通过几个函数和例程简单 AT Client 数据解析方式。
+
+#### 获取指定行号的响应数据
+
+```c
+const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line);
+```
+
+| **参数** | **描述** |
+|----------|-----------------------------|
+| resp |响应结构体指针 |
+| resp_line | 需要获取数据的行号 |
+| **返回** | -- |
+| != NULL | 成功,返回对应行号数据的指针 |
+| = NULL | 失败,输入行号错误 |
+
+该函数用于在 AT Server 响应数据中获取指定行号的一行数据。行号是以标准数据结束符来判断的,上述发送和接收函数 at_exec_cmd 已经对响应数据的数据和行号进行记录处理存放于 resp 响应结构体中,这里可以直接获取对应行号的数据信息。
+
+#### 获取指定关键字的响应数据
+
+```c
+const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword);
+```
+
+| **参数** | **描述** |
+|-------|-----------------------------|
+| resp |响应结构体指针 |
+| keyword | 关键字信息 |
+| **返回** | -- |
+| != NULL | 成功,返回对应行号数据的指针 |
+| = NULL | 失败,未找到关键字信息 |
+
+该函数用于在 AT Server 响应数据中通过关键字获取对应的一行数据。
+
+#### 解析指定行号的响应数据
+
+```c
+int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, ...);
+```
+
+| **参数** | **描述** |
+|----------|---------------------------------|
+| resp |响应结构体指针 |
+| resp_line | 需要解析数据的行号,**行号从 1 开始计数** |
+| resp_expr | 自定义的参数解析表达式 |
+| ... | 解析参数列表,为可变参数 |
+| **返回** | -- |
+| >0 | 成功,返回解析成功的参数个数 |
+| =0 | 失败,无匹参配数解析表达式的参数 |
+| -1 | 失败,参数解析错误 |
+
+该函数用于在 AT Server 响应数据中获取指定行号的一行数据, 并解析该行数据中的参数。
+
+#### 解析指定关键字行的响应数据
+
+```c
+int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...);
+```
+
+| **参数** | **描述** |
+|----------|---------------------------------|
+| resp |响应结构体指针 |
+| keyword | 关键字信息 |
+| resp_expr | 自定义的参数解析表达式 |
+| ... | 解析参数列表,为可变参数 |
+| **返回** | -- |
+| >0 | 成功,返回解析成功的参数个数 |
+| =0 | 失败,无匹参配数解析表达式的参数 |
+| -1 | 失败,参数解析错误 |
+
+该函数用于在 AT Server 响应数据中获取包含关键字的一行数据, 并解析该行数据中的参数。
+
+数据解析语法使用标准 `sscanf` 解析语法,语法的内容比较多,开发者可以自行搜索其解析语法,这里使用两个例程介绍简单使用方法。
+
+#### 串口配置信息解析示例
+
+客户端发送的数据:
+
+```c
+AT+UART?
+```
+
+客户端获取的响应数据:
+
+```c
+UART=115200,8,1,0,0\r\n
+OK\r\n
+```
+
+解析伪代码如下:
+
+```c
+/* 创建服务器响应结构体,64 为用户自定义接收数据最大长度 */
+resp = at_create_resp(64, 0, rt_tick_from_millisecond(5000));
+
+/* 发送数据到服务器,并接收响应数据存放在 resp 结构体中 */
+at_exec_cmd(resp, "AT+UART?");
+
+/* 解析获取串口配置信息,1 表示解析响应数据第一行,'%*[^=]'表示忽略等号之前的数据 */
+at_resp_parse_line_args(resp, 1,"%*[^=]=%d,%d,%d,%d,%d", &baudrate, &databits, &stopbits, &parity, &control);
+printf("baudrate=%d, databits=%d, stopbits=%d, parity=%d, control=%d\n",
+ baudrate, databits, stopbits, parity, control);
+
+/* 删除服务器响应结构体 */
+at_delete_resp(resp);
+```
+
+#### IP 和 MAC 地址解析示例 ####
+
+客户端发送的数据:
+
+```c
+AT+IPMAC?
+```
+
+服务器获取的响应数据:
+
+```c
+IP=192.168.1.10\r\n
+MAC=12:34:56:78:9a:bc\r\n
+OK\r\n
+```
+
+解析伪代码如下:
+
+```c
+/* 创建服务器响应结构体,128 为用户自定义接收数据最大长度 */
+resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000));
+
+at_exec_cmd(resp, "AT+IPMAC?");
+
+/* 自定义解析表达式,解析当前行号数据中的信息 */
+at_resp_parse_line_args(resp, 1,"IP=%s", ip);
+at_resp_parse_line_args(resp, 2,"MAC=%s", mac);
+printf("IP=%s, MAC=%s\n", ip, mac);
+
+at_delete_resp(resp);
+```
+
+解析数据的关键在于解析表达式的正确定义,因为对于 AT 设备的响应数据,不同设备厂家不同命令的响应数据格式不唯一,所以只能提供自定义解析表达式的形式获取需要信息,at_resp_parse_line_args 解析参数函数的设计基于 `sscanf` 数据解析方式,开发者使用之前需要先了解基本的解析语法,再结合响应数据设计合适的解析语法。如果开发者不需要解析具体参数,可以直接使用 at_resp_get_line 函数获取一行的具体数据。
+
+### AT Client URC 数据处理 ###
+
+URC 数据的处理是 AT Client 另一个重要功能,URC 数据为服务器主动下发的数据,不能通过上述数据发送接收函数接收,并且对于不同设备 URC 数据格式和功能不一样,所以 URC 数据处理的方式也是需要用户自定义实现的。AT 组件中对 URC 数据的处理提供列表管理方式,用户可自定义添加 URC 数据和其执行函数到管理列表中,所以 URC 数据的处理也是 AT Client 的主要移植工作。
+
+相关结构体:
+
+```c
+struct at_urc
+{
+ const char *cmd_prefix; // URC 数据前缀
+ const char *cmd_suffix; // URC 数据后缀
+ void (*func)(const char *data, rt_size_t size); // URC 数据执行函数
+};
+typedef struct at_urc *at_urc_t;
+```
+
+每种 URC 数据都有一个结构体控制块,用于定义判断 URC 数据的前缀和后缀,以及 URC 数据的执行函数。一段数据只有完全匹配 URC 的前缀和后缀才能定义为 URC 数据,获取到匹配的 URC 数据后会立刻执行 URC 数据执行函数。所以开发者添加一个 URC 数据需要自定义匹配的前缀、后缀和执行函数。
+
+
+#### URC 数据列表初始化
+
+```c
+void at_set_urc_table(const struct at_urc *table, rt_size_t size);
+```
+
+| **参数** | **描述** |
+|-----|-----------------------|
+| table | URC 数据结构体数组指针 |
+| size | URC 数据的个数 |
+
+该函数用于初始化开发者自定义的 URC 数据列表,主要在 AT Client 移植函数中使用。
+
+下面给出 AT Client 移植具体示例,该示例主要展示 `at_client_port_init()` 移植函数中 URC 数据的具体处理方式,开发者可直接应用到自己的移植文件中,或者自定义修改实现功能,完成 AT Client 的移植。
+
+```c
+static void urc_conn_func(const char *data, rt_size_t size)
+{
+ /* WIFI 连接成功信息 */
+ LOG_D("AT Server device WIFI connect success!");
+}
+
+static void urc_recv_func(const char *data, rt_size_t size)
+{
+ /* 接收到服务器发送数据 */
+ LOG_D("AT Client receive AT Server data!");
+}
+
+static void urc_func(const char *data, rt_size_t size)
+{
+ /* 设备启动信息 */
+ LOG_D("AT Server device startup!");
+}
+
+static struct at_urc urc_table[] = {
+ {"WIFI CONNECTED", "\r\n", urc_conn_func},
+ {"+RECV", ":", urc_recv_func},
+ {"RDY", "\r\n", urc_func},
+};
+
+int at_client_port_init(void)
+{
+ /* 添加多种 URC 数据至 URC 列表中,当接收到同时匹配 URC 前缀和后缀的数据,执行 URC 函数 */
+ at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
+ return RT_EOK;
+}
+```
+
+### AT Client 其他 API 接口介绍
+
+#### 发送指定长度数据
+
+```c
+rt_size_t at_client_send(const char *buf, rt_size_t size);
+```
+
+| **参数** | **描述** |
+|----|-----------------------------|
+| buf | 发送数据的指针 |
+| size | 发送数据的长度 |
+| **返回** | -- |
+| >0 | 成功,返回发送成功的数据长度 |
+| <=0 | 失败 |
+
+该函数用于通过 AT Client 设备发送指定长度数据到 AT Server 设备,多用于 AT Socket 功能。
+
+#### 接收指定长度数据
+
+```c
+rt_size_t at_client_recv(char *buf, rt_size_t size,rt_int32_t timeout);
+```
+
+| **参数** | **描述** |
+|----|-----------------------------|
+| buf | 接收数据的指针 |
+| size | 最大支持接收数据的长度 |
+| timeout | 接收数据超时时间,单位为 tick |
+| **返回** | -- |
+| >0 | 成功,返回接收成功的数据长度 |
+| <=0 | 失败,接收数据错误或超时 |
+
+该函数用于通过 AT Client 设备接收指定长度的数据,多用于 AT Socket 功能。**该函数只能在 URC 回调处理函数中使用**。
+
+#### 设置接收数据的行结束符 ####
+
+```c
+void at_set_end_sign(char ch);
+```
+
+| 参数 | 描述 |
+| ----- | ----- |
+|ch | 行结束符 |
+| **返回** | **描述** |
+|无 | 无 |
+
+该函数用于设置行结束符,用于判断客户端接收一行数据的结束, 多用于 AT Socket 功能。
+
+#### 等待模块初始化完成 ####
+
+```c
+int at_client_wait_connect(rt_uint32_t timeout);
+```
+
+| 参数 | 描述 |
+| ----- | ----- |
+|timeout | 等待超时时间 |
+| **返回** | **描述** |
+|0 | 成功 |
+|<0 | 失败,超时时间内无数据返回 |
+
+该函数用于 AT 模块启动时循环发送 AT 命令,直到模块响应数据,说明模块启动成功。
+
+### AT Client 多客户端支持 ###
+
+一般情况下,设备作为 AT Client 只连接一个 AT 模块(AT 模块作为 AT Server)可直接使用上述数据收发和命令解析的函数。少数情况,设备作为 AT Client 需要连接多个 AT 模块,这种情况下需要设备的多客户端支持功能。
+
+AT 组件提供对多客户端连接的支持,并且提供两套不同的函数接口:**单客户端模式函数** 和 **多客户端模式函数**。
+
+- 单客户端模式函数:该类函数接口主要用于设备只连接一个 AT 模块情况,或者在设备连接多个 AT 模块时,用于**第一个初始化**的 AT 客户端中。
+
+- 多客户端模式函数:该类函数接口主要用设备连接多个 AT 模块情况。
+
+两种不同模式函数和在不同应用场景下的优缺点如下图:
+
+
+
+单客户端模式函数定义与单连接模式函数相比,主要是对传入的客户端对象的定义不同,单客户端模式函数默认使用第一个初始化的 AT 客户端对象,多客户端模式函数可以传入用户自定义获取的客户端对象, 获取客户端对象的函数如下:
+
+```c
+at_client_t at_client_get(const char *dev_name);
+```
+
+该函数通过传入的设备名称获取该设备创建的 AT 客户端对象,用于多客户端连接时区分不同的客户端。
+
+单客户端模式和多客户端模式函数接口定义区别如下几个函数:
+
+| 单客户端模式函数 | 多客户端模式函数 |
+| ----------------------------| ---------------------------------------|
+| at_exec_cmd(...) | at_obj_exec_cmd(client, ...) |
+| at_set_end_sign(...) | at_obj_set_end_sign(client, ...) |
+| at_set_urc_table(...) | at_obj_set_urc_table(client, ...) |
+| at_client_wait_connect(...) | at_client_obj_wait_connect(client, ...) |
+| at_client_send(...) | at_client_obj_send(client, ...) |
+| at_client_recv(...) | at_client_obj_recv(client, ...) |
+
+两种模式客户端数据收发和解析的方式基本相同,在函数使用流程上有所不同,如下所示:
+
+```c
+/* 单客户端模式函数使用方式 */
+
+at_response_t resp = RT_NULL;
+
+at_client_init("uart2", 512);
+
+resp = at_create_resp(256, 0, 5000);
+
+/* 使用单客户端模式函数发送命令 */
+at_exec_cmd(resp, "AT+CIFSR");
+
+at_delete_resp(resp);
+```
+
+```c
+/* 多客户端模式函数使用方式 */
+
+at_response_t resp = RT_NULL;
+at_client_t client = RT_NULL;
+
+/* 初始化两个 AT 客户端 */
+at_client_init("uart2", 512);
+at_client_init("uart3", 512);
+
+/* 通过名称获取对应的 AT 客户端对象 */
+client = at_client_get("uart3");
+
+resp = at_create_resp(256, 0, 5000);
+
+/* 使用多客户端模式函数发送命令 */
+at_obj_exec_cmd(client, resp, "AT+CIFSR");
+
+at_delete_resp(resp);
+```
+
+其他函数使用的流程区别类似于上述 `at_obj_exec_cmd()` 函数,主要是先通过 `at_client_get()` 函数获取客户端对象,再通过传入的对象判断是哪个客户端,实现多客户端的支持。
+
+## 常见问题
+
+### Q: 开启 AT 命令收发数据实时打印功能,shell 上日志显示错误怎么办?
+
+**A:** 提高 shell 对应串口设备波特率为 921600,提高串口打印速度,防止数据量过大时打印显示错误。
+
+### Q: AT Socket 功能启动时,编译提示 “The AT socket device is not selected, please select it through the env menuconfig”?
+
+**A:** 该错误因为开启 AT Socket 功能之后,默认开启 at device 软件包中为配置对应的设备型号,进入 at device 软件包,配置设备为 ESP8266 设备,配置 WIFI 信息,重新 scons 生成工程,编译下载。
+
+### Q: AT Socket 功能数据接收超时或者数据接收不全?
+
+**A:** 该错误可能是 AT 使用的串口设备中接收数据缓冲区过小(RT_SERIAL_RB_BUFSZ 默认为 64 bytes),数据未及时接收完就被覆盖导致的,适当增加串口接收数据的缓冲区大小(如 256 bytes)。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/at/figures/at_framework.jpg b/rt-thread-version/rt-thread-standard/programming-manual/at/figures/at_framework.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f5c868dc6c26a7715b9946817c68ca24d9be68cd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/at/figures/at_framework.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/at/figures/at_multiple_client.jpg b/rt-thread-version/rt-thread-standard/programming-manual/at/figures/at_multiple_client.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..58d01f6bf6adf0d8f506d81d99a531705b79e7fd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/at/figures/at_multiple_client.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/basic.md b/rt-thread-version/rt-thread-standard/programming-manual/basic/basic.md
new file mode 100644
index 0000000000000000000000000000000000000000..dc8f618a6236ec0deb6d34e12d493069206cc0b2
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/basic/basic.md
@@ -0,0 +1,743 @@
+# 内核基础
+
+本章介绍 RT-Thread 内核基础,包括:内核简介、系统的启动流程及内核配置的部分内容,为后面的章节奠定基础。
+
+RT-Thread 内核的简单介绍,从软件架构入手讲解实时内核的组成与实现,这部分给初学者引入一些 RT-Thread 内核相关的概念与基础知识,让初学者对内核有初步的了解。学完本章,读者将会对 RT-Thread 内核有基本的了解,知道内核的组成部分、系统如何启动、内存分布情况以及内核配置方法。
+
+RT-Thread 内核介绍
+-----------------
+
+内核是操作系统最基础也是最重要的部分。下图为 RT-Thread 内核架构图,内核处于硬件层之上,内核部分包括内核库、实时内核实现。
+
+
+
+内核库是为了保证内核能够独立运行的一套小型的类似 C 库的函数实现子集。这部分根据编译器的不同自带 C 库的情况也会有些不同,当使用 GNU GCC 编译器时,会携带更多的标准 C 库实现。
+
+> 提示:C 库:也叫 C 运行库(C Runtime Library),它提供了类似 “strcpy”、“memcpy” 等函数,有些也会包括 “printf”、“scanf” 函数的实现。RT-Thread Kernel Service Library 仅提供内核用到的一小部分 C 库函数实现,为了避免与标准 C 库重名,在这些函数前都会添加上 rt_前缀。
+
+实时内核的实现包括:对象管理、线程管理及调度器、线程间通信管理、时钟管理及内存管理等等,内核最小的资源占用情况是 3KB ROM,1.2KB RAM。
+
+### 线程调度
+
+线程是 RT-Thread 操作系统中最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。支持 256 个线程优先级(也可通过配置文件更改为最大支持 32 个或 8 个线程优先级,针对 STM32 默认配置是 32 个线程优先级),0 优先级代表最高优先级,最低优先级留给空闲线程使用;同时它也支持创建多个具有相同优先级的线程,相同优先级的线程间采用时间片的轮转调度算法进行调度,使每个线程运行相应时间;另外调度器在寻找那些处于就绪状态的具有最高优先级的线程时,所经历的时间是恒定的,系统也不限制线程数量的多少,线程数目只和硬件平台的具体内存相关。
+
+线程管理将在[《线程管理》](../thread/thread.md)章节详细介绍。
+
+### 时钟管理
+
+RT-Thread 的时钟管理以时钟节拍为基础,时钟节拍是 RT-Thread 操作系统中最小的时钟单位。RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止定时器否则将永远持续执行下去。
+
+另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以设置为 HARD_TIMER 模式或者 SOFT_TIMER 模式。
+
+通常使用定时器定时回调函数(即超时函数),完成定时服务。用户根据自己对定时处理的实时性要求选择合适类型的定时器。
+
+定时器将在[《时钟管理》](../timer/timer.md)章节展开讲解。
+
+### 线程间同步
+
+RT-Thread 采用信号量、互斥量与事件集实现线程间同步。线程通过对信号量、互斥量的获取与释放进行同步;互斥量采用优先级继承的方式解决了实时系统常见的优先级翻转问题。线程同步机制支持线程按优先级等待或按先进先出方式获取信号量或互斥量。线程通过对事件的发送与接收进行同步;事件集支持多事件的 “或触发” 和“与触发”,适合于线程等待多个事件的情况。
+
+信号量、互斥量与事件集的概念将在[《线程间同步》](../ipc1/ipc1.md)章节详细介绍。
+
+### 线程间通信
+
+RT-Thread 支持邮箱和消息队列等通信机制。邮箱中一封邮件的长度固定为 4 字节大小;消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。邮箱效率较消息队列更为高效。邮箱和消息队列的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待或按先进先出方式获取。
+
+邮箱和消息队列的概念将在[《线程间通信》](../ipc2/ipc2.md)章节详细介绍。
+
+### 内存管理
+
+RT-Thread 支持静态内存池管理及动态内存堆管理。当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的;当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉 (即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。等待的时间取决于申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒。
+
+动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存系统的内存管理算法及面向大内存系统的
+SLAB 内存管理算法。
+
+还有一种动态内存堆管理叫做 memheap,适用于系统含有多个地址可不连续的内存堆。使用 memheap 可以将多个内存堆 “粘贴” 在一起,让用户操作起来像是在操作一个内存堆。
+
+内存管理的概念将在[《内存管理》](../memory/memory.md)章节展开讲解。
+
+### I/O 设备管理
+
+RT-Thread 将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。实现了按名称访问的设备管理子系统,可按照统一的 API 界面访问硬件设备。在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件。当设备事件触发时,由驱动程序通知给上层的应用程序。
+
+I/O 设备管理的概念将在[《设备模型》](../device/device.md)及[《通用设备》](../device/adc/adc.md)章节展开讲解。
+
+RT-Thread 启动流程
+------------------
+
+一般了解一份代码大多从启动部分开始,同样这里也采用这种方式,先寻找启动的源头。RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。一般执行顺序是:系统先从启动文件开始运行,然后进入 RT-Thread 的启动 rtthread_startup() ,最后进入用户入口 main(),如下图所示:
+
+
+
+以 MDK-ARM 为例,用户程序入口为 main() 函数,位于 main.c 文件中。系统启动后先从汇编代码 startup_stm32f103xe.s 开始运行,然后跳转到 C 代码,进行 RT-Thread 系统启动,最后进入用户程序入口 main()。
+
+为了在进入 main() 之前完成 RT-Thread 系统功能初始化,我们使用了 MDK 的扩展功能 `$Sub$$` 和 `$Super$$`。可以给 main 添加 `$Sub$$` 的前缀符号作为一个新功能函数 `$Sub$$main`,这个 `$Sub$$main` 可以先调用一些要补充在 main 之前的功能函数(这里添加 RT-Thread 系统启动,进行系统一系列初始化),再调用 `$Super$$main` 转到 main() 函数执行,这样可以让用户不用去管 main() 之前的系统初始化操作。
+
+关于 `$Sub$$` 和 `$Super$$` 扩展功能的使用,详见 [ARM® Compiler v5.06 for µVision®armlink User Guide](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0377g/pge1362065967698.html)。
+
+下面我们来看看在 components.c 中定义的这段代码:
+
+```c
+/* $Sub$$main 函数 */
+int $Sub$$main(void)
+{
+ rtthread_startup();
+ return 0;
+}
+```
+
+
+在这里 `$Sub$$main` 函数调用了 rtthread_startup() 函数,其中 rtthread_startup() 函数的代码如下所示:
+
+```c
+int rtthread_startup(void)
+{
+ rt_hw_interrupt_disable();
+
+ /* 板级初始化:需在该函数内部进行系统堆的初始化 */
+ rt_hw_board_init();
+
+ /* 打印 RT-Thread 版本信息 */
+ rt_show_version();
+
+ /* 定时器初始化 */
+ rt_system_timer_init();
+
+ /* 调度器初始化 */
+ rt_system_scheduler_init();
+
+#ifdef RT_USING_SIGNALS
+ /* 信号初始化 */
+ rt_system_signal_init();
+#endif
+
+ /* 由此创建一个用户 main 线程 */
+ rt_application_init();
+
+ /* 定时器线程初始化 */
+ rt_system_timer_thread_init();
+
+ /* 空闲线程初始化 */
+ rt_thread_idle_init();
+
+ /* 启动调度器 */
+ rt_system_scheduler_start();
+
+ /* 不会执行至此 */
+ return 0;
+}
+```
+
+
+这部分启动代码,大致可以分为四个部分:
+
+(1)初始化与系统相关的硬件;
+
+(2)初始化系统内核对象,例如定时器、调度器、信号;
+
+(3)创建 main 线程,在 main 线程中对各类模块依次进行初始化;
+
+(4)初始化定时器线程、空闲线程,并启动调度器。
+
+启动调度器之前,系统所创建的线程在执行 rt_thread_startup() 后并不会立马运行,它们会处于就绪状态等待系统调度;待启动调度器之后,系统才转入第一个线程开始运行,根据调度规则,选择的是就绪队列中优先级最高的线程。
+
+rt_hw_board_init() 中完成系统时钟设置,为系统提供心跳、串口初始化,将系统输入输出终端绑定到这个串口,后续系统运行信息就会从串口打印出来。
+
+main() 函数是 RT-Thread 的用户代码入口,用户可以在 main() 函数里添加自己的应用。
+
+```c
+int main(void)
+{
+ /* user app entry */
+ return 0;
+}
+```
+
+
+RT-Thread 程序内存分布
+---------------------
+
+一般 MCU 包含的存储空间有:片内 Flash 与片内 RAM,RAM 相当于内存,Flash 相当于硬盘。编译器会将一个程序分类为好几个部分,分别存储在 MCU 不同的存储区。
+
+Keil 工程在编译完之后,会有相应的程序所占用的空间提示信息,如下所示:
+
+```
+linking...
+Program Size: Code=48008 RO-data=5660 RW-data=604 ZI-data=2124
+After Build - User command \#1: fromelf --bin.\\build\\rtthread-stm32.axf--output rtthread.bin
+".\\build\\rtthread-stm32.axf" - 0 Error(s), 0 Warning(s).
+Build Time Elapsed: 00:00:07
+```
+
+上面提到的 Program Size 包含以下几个部分:
+
+1)Code:代码段,存放程序的代码部分;
+
+2)RO-data:只读数据段,存放程序中定义的常量;
+
+3)RW-data:读写数据段,存放初始化为非 0 值的全局变量;
+
+4)ZI-data:0 数据段,存放未初始化的全局变量及初始化为 0 的变量;
+
+编译完工程会生成一个. map 的文件,该文件说明了各个函数占用的尺寸和地址,在文件的最后几行也说明了上面几个字段的关系:
+
+```
+Total RO Size (Code + RO Data) 53668 ( 52.41kB)
+Total RW Size (RW Data + ZI Data) 2728 ( 2.66kB)
+Total ROM Size (Code + RO Data + RW Data) 53780 ( 52.52kB)
+```
+
+1)RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小;
+
+2)RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
+
+3)ROM Size 包含了 Code、RO Data 以及 RW Data,表示烧写程序所占用的 Flash 空间的大小;
+
+程序运行之前,需要有文件实体被烧录到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,该被烧录文件称为可执行映像文件。如图 3-3 中左图所示,是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
+
+STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。
+
+
+
+其中动态内存堆为未使用的 RAM 空间,应用程序申请和释放的内存块都来自该空间。
+
+如下面的例子:
+
+```c
+rt_uint8_t* msg_ptr;
+msg_ptr = (rt_uint8_t*) rt_malloc (128);
+rt_memset(msg_ptr, 0, 128);
+```
+
+代码中的 msg_ptr 指针指向的 128 字节内存空间位于动态内存堆空间中。
+
+而一些全局变量则是存放于 RW 段和 ZI 段中,RW 段存放的是具有初始值的全局变量(而常量形式的全局变量则放置在 RO 段中,是只读属性的),ZI 段存放的系统未初始化的全局变量,如下面的例子:
+
+```c
+#include
+
+const static rt_uint32_t sensor_enable = 0x000000FE;
+rt_uint32_t sensor_value;
+rt_bool_t sensor_inited = RT_FALSE;
+
+void sensor_init()
+{
+ /* ... */
+}
+```
+
+sensor_value 存放在 ZI 段中,系统启动后会自动初始化成零(由用户程序或编译器提供的一些库函数初始化成零)。sensor_inited 变量则存放在 RW 段中,而 sensor_enable 存放在 RO 段中。
+
+RT-Thread 自动初始化机制
+-----------------------
+
+自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。
+
+例如在串口驱动中调用一个宏定义告知系统初始化需要调用的函数,代码如下:
+
+```c
+int rt_hw_usart_init(void) /* 串口初始化函数 */
+{
+ ... ...
+ /* 注册串口 1 设备 */
+ rt_hw_serial_register(&serial1, "uart1",
+ RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
+ uart);
+ return 0;
+}
+INIT_BOARD_EXPORT(rt_hw_usart_init); /* 使用组件自动初始化机制 */
+```
+
+示例代码最后的 INIT_BOARD_EXPORT(rt_hw_usart_init) 表示使用自动初始化功能,按照这种方式,rt_hw_usart_init() 函数就会被系统自动调用,那么它是在哪里被调用的呢?
+
+在系统启动流程图中,有两个函数:rt_components_board_init() 与 rt_components_init(),其后的带底色方框内部的函数表示被自动初始化的函数,其中:
+
+1. “board init functions” 为所有通过 INIT_BOARD_EXPORT(fn) 申明的初始化函数。
+
+2. “pre-initialization functions” 为所有通过 INIT_PREV_EXPORT(fn)申明的初始化函数。
+
+3. “device init functions” 为所有通过 INIT_DEVICE_EXPORT(fn) 申明的初始化函数。
+
+4. “components init functions” 为所有通过 INIT_COMPONENT_EXPORT(fn)申明的初始化函数。
+
+5. “enviroment init functions” 为所有通过 INIT_ENV_EXPORT(fn) 申明的初始化函数。
+
+6. “application init functions” 为所有通过 INIT_APP_EXPORT(fn)申明的初始化函数。
+
+rt_components_board_init() 函数执行的比较早,主要初始化相关硬件环境,执行这个函数时将会遍历通过 INIT_BOARD_EXPORT(fn) 申明的初始化函数表,并调用各个函数。
+
+rt_components_init() 函数会在操作系统运行起来之后创建的 main 线程里被调用执行,这个时候硬件环境和操作系统已经初始化完成,可以执行应用相关代码。rt_components_init() 函数会遍历通过剩下的其他几个宏申明的初始化函数表。
+
+RT-Thread 的自动初始化机制使用了自定义 RTI 符号段,将需要在启动时进行初始化的函数指针放到了该段中,形成一张初始化函数表,在系统启动过程中会遍历该表,并调用表中的函数,达到自动初始化的目的。
+
+用来实现自动初始化功能的宏接口定义详细描述如下表所示:
+
+|**初始化顺序**|**宏接口** |**描述** |
+|----------------|------------------------------------|----------------------------------------------|
+| 1 | INIT_BOARD_EXPORT(fn) | 非常早期的初始化,此时调度器还未启动 |
+| 2 | INIT_PREV_EXPORT(fn) | 主要是用于纯软件的初始化、没有太多依赖的函数 |
+| 3 | INIT_DEVICE_EXPORT(fn) | 外设驱动初始化相关,比如网卡设备 |
+| 4 | INIT_COMPONENT_EXPORT(fn) | 组件初始化,比如文件系统或者 LWIP |
+| 5 | INIT_ENV_EXPORT(fn) | 系统环境初始化,比如挂载文件系统 |
+| 6 | INIT_APP_EXPORT(fn) | 应用初始化,比如 GUI 应用 |
+
+初始化函数主动通过这些宏接口进行申明,如 INIT_BOARD_EXPORT(rt_hw_usart_init),链接器会自动收集所有被申明的初始化函数,放到 RTI 符号段中,该符号段位于内存分布的 RO 段中,该 RTI 符号段中的所有函数在系统初始化时会被自动调用。
+
+RT-Thread 内核对象模型
+---------------------
+
+### 静态对象和动态对象
+
+RT-Thread 内核采用面向对象的设计思想进行设计,系统级的基础设施都是一种内核对象,例如线程,信号量,互斥量,定时器等。内核对象分为两类:静态内核对象和动态内核对象,静态内核对象通常放在 RW 段和 ZI 段中,在系统启动后在程序中初始化;动态内核对象则是从内存堆中创建的,而后手工做初始化。
+
+以下代码是一个关于静态线程和动态线程的例子:
+
+
+```c
+/* 线程 1 的对象和运行时用到的栈 */
+static struct rt_thread thread1;
+static rt_uint8_t thread1_stack[512];
+
+/* 线程 1 入口 */
+void thread1_entry(void* parameter)
+{
+ int i;
+
+ while (1)
+ {
+ for (i = 0; i < 10; i ++)
+ {
+ rt_kprintf("%d\n", i);
+
+ /* 延时 100ms */
+ rt_thread_mdelay(100);
+ }
+ }
+}
+
+/* 线程 2 入口 */
+void thread2_entry(void* parameter)
+{
+ int count = 0;
+ while (1)
+ {
+ rt_kprintf("Thread2 count:%d\n", ++count);
+
+ /* 延时 50ms */
+ rt_thread_mdelay(50);
+ }
+}
+
+/* 线程例程初始化 */
+int thread_sample_init()
+{
+ rt_thread_t thread2_ptr;
+ rt_err_t result;
+
+ /* 初始化线程 1 */
+ /* 线程的入口是 thread1_entry,参数是 RT_NULL
+ * 线程栈是 thread1_stack
+ * 优先级是 200,时间片是 10 个 OS Tick
+ */
+ result = rt_thread_init(&thread1,
+ "thread1",
+ thread1_entry, RT_NULL,
+ &thread1_stack[0], sizeof(thread1_stack),
+ 200, 10);
+
+ /* 启动线程 */
+ if (result == RT_EOK) rt_thread_startup(&thread1);
+
+ /* 创建线程 2 */
+ /* 线程的入口是 thread2_entry, 参数是 RT_NULL
+ * 栈空间是 512,优先级是 250,时间片是 25 个 OS Tick
+ */
+ thread2_ptr = rt_thread_create("thread2",
+ thread2_entry, RT_NULL,
+ 512, 250, 25);
+
+ /* 启动线程 */
+ if (thread2_ptr != RT_NULL) rt_thread_startup(thread2_ptr);
+
+ return 0;
+}
+```
+
+在这个例子中,thread1 是一个静态线程对象,而 thread2 是一个动态线程对象。thread1 对象的内存空间,包括线程控制块 thread1 与栈空间 thread1_stack 都是编译时决定的,因为代码中都不存在初始值,都统一放在未初始化数据段中。thread2 运行中用到的空间都是动态分配的,包括线程控制块(thread2_ptr 指向的内容)和栈空间。
+
+静态对象会占用 RAM 空间,不依赖于内存堆管理器,内存分配时间确定。动态对象则依赖于内存堆管理器,运行时申请 RAM 空间,当对象被删除后,占用的 RAM 空间被释放。这两种方式各有利弊,可以根据实际环境需求选择具体使用方式。
+
+### 内核对象管理架构
+
+RT-Thread 采用内核对象管理系统来访问 / 管理所有内核对象,内核对象包含了内核中绝大部分设施,这些内核对象可以是静态分配的静态对象,也可以是从系统内存堆中分配的动态对象。
+
+通过这种内核对象的设计方式,RT-Thread 做到了不依赖于具体的内存分配方式,系统的灵活性得到极大的提高。
+
+RT-Thread 内核对象包括:线程,信号量,互斥量,事件,邮箱,消息队列和定时器,内存池,设备驱动等。对象容器中包含了每类内核对象的信息,包括对象类型,大小等。对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上,如图 RT-Thread 的内核对象容器及链表如下图所示:
+
+
+
+下图则显示了 RT-Thread 中各类内核对象的派生和继承关系。对于每一种具体内核对象和对象控制块,除了基本结构外,还有自己的扩展属性(私有属性),例如,对于线程控制块,在基类对象基础上进行扩展,增加了线程状态、优先级等属性。这些属性在基类对象的操作中不会用到,只有在与具体线程相关的操作中才会使用。因此从面向对象的观点,可以认为每一种具体对象是抽象对象的派生,继承了基本对象的属性并在此基础上扩展了与自己相关的属性。
+
+
+
+在对象管理模块中,定义了通用的数据结构,用来保存各种对象的共同属性,各种具体对象只需要在此基础上加上自己的某些特别的属性,就可以清楚的表示自己的特征。
+
+这种设计方法的优点有:
+
+(1)提高了系统的可重用性和扩展性,增加新的对象类别很容易,只需要继承通用对象的属性再加少量扩展即可。
+
+(2)提供统一的对象操作方式,简化了各种具体对象的操作,提高了系统的可靠性。
+
+上图中由对象控制块 rt_object 派生出来的有:线程对象、内存池对象、定时器对象、设备对象和 IPC 对象(IPC:Inter-Process Communication,进程间通信。在 RT-Thread 实时操作系统中,IPC 对象的作用是进行线程间同步与通信);由 IPC 对象派生出信号量、互斥量、事件、邮箱与消息队列、信号等对象。
+
+### 对象控制块
+
+内核对象控制块的数据结构:
+
+```c
+struct rt_object
+{
+ /* 内核对象名称 */
+ char name[RT_NAME_MAX];
+ /* 内核对象类型 */
+ rt_uint8_t type;
+ /* 内核对象的参数 */
+ rt_uint8_t flag;
+ /* 内核对象管理链表 */
+ rt_list_t list;
+};
+```
+
+目前内核对象支持的类型如下:
+
+```c
+enum rt_object_class_type
+{
+ RT_Object_Class_Thread = 0, /* 对象为线程类型 */
+#ifdef RT_USING_SEMAPHORE
+ RT_Object_Class_Semaphore, /* 对象为信号量类型 */
+#endif
+#ifdef RT_USING_MUTEX
+ RT_Object_Class_Mutex, /* 对象为互斥量类型 */
+#endif
+#ifdef RT_USING_EVENT
+ RT_Object_Class_Event, /* 对象为事件类型 */
+#endif
+#ifdef RT_USING_MAILBOX
+ RT_Object_Class_MailBox, /* 对象为邮箱类型 */
+#endif
+#ifdef RT_USING_MESSAGEQUEUE
+ RT_Object_Class_MessageQueue, /* 对象为消息队列类型 */
+#endif
+#ifdef RT_USING_MEMPOOL
+ RT_Object_Class_MemPool, /* 对象为内存池类型 */
+#endif
+#ifdef RT_USING_DEVICE
+ RT_Object_Class_Device, /* 对象为设备类型 */
+#endif
+ RT_Object_Class_Timer, /* 对象为定时器类型 */
+#ifdef RT_USING_MODULE
+ RT_Object_Class_Module, /* 对象为模块 */
+#endif
+ RT_Object_Class_Unknown, /* 对象类型未知 */
+ RT_Object_Class_Static = 0x80 /* 对象为静态对象 */
+};
+```
+
+从上面的类型说明,我们可以看出,如果是静态对象,那么对象类型的最高位将是 1(是 RT_Object_Class_Static 与其他对象类型的与操作),否则就是动态对象,系统最多能够容纳的对象类别数目是 127 个。
+
+### 内核对象管理方式
+
+内核对象容器的数据结构:
+
+```c
+struct rt_object_information
+{
+ /* 对象类型 */
+ enum rt_object_class_type type;
+ /* 对象链表 */
+ rt_list_t object_list;
+ /* 对象大小 */
+ rt_size_t object_size;
+};
+```
+
+一类对象由一个 rt_object_information 结构体来管理,每一个这类对象的具体实例都通过链表的形式挂接在 object_list 上。而这一类对象的内存块尺寸由 object_size 标识出来(每一类对象的具体实例,他们占有的内存块大小都是相同的)。
+
+#### 初始化对象
+
+在使用一个未初始化的静态对象前必须先对其进行初始化。初始化对象使用以下接口:
+
+```c
+void rt_object_init(struct rt_object* object ,
+ enum rt_object_class_type type ,
+ const char* name)
+```
+
+当调用这个函数进行对象初始化时,系统会把这个对象放置到对象容器中进行管理,即初始化对象的一些参数,然后把这个对象节点插入到对象容器的对象链表中,对该函数的输入参数的描述如下表:
+
+
+|**参数**|**描述** |
+| -------- | ------------------------------------------------------------ |
+| object | 需要初始化的对象指针,它必须指向具体的对象内存块,而不能是空指针或野指针 |
+| type | 对象的类型,必须是 rt_object_class_type 枚举类型中列出的除 RT_Object_Class_Static 以外的类型(对于静态对象,或使用 rt_object_init 接口进行初始化的对象,系统会把它标识成 RT_Object_Class_Static 类型) |
+| name | 对象的名字。每个对象可以设置一个名字,这个名字的最大长度由 RT_NAME_MAX 指定,并且系统不关心它是否是由’`\0`’做为终结符 |
+
+#### 脱离对象
+
+从内核对象管理器中脱离一个对象。脱离对象使用以下接口:
+
+```c
+void rt_object_detach(rt_object_t object);
+```
+
+调用该接口,可使得一个静态内核对象从内核对象容器中脱离出来,即从内核对象容器链表上删除相应的对象节点。对象脱离后,对象占用的内存并不会被释放。
+
+#### 分配对象
+
+上述描述的都是对象初始化、脱离的接口,都是面向对象内存块已经有的情况下,而动态的对象则可以在需要时申请,不需要时释放出内存空间给其他应用使用。申请分配新的对象可以使用以下接口:
+
+```c
+rt_object_t rt_object_allocate(enum rt_object_class_type type ,
+ const char* name)
+```
+
+在调用以上接口时,系统首先需要根据对象类型来获取对象信息(特别是对象类型的大小信息以用于系统能够分配正确大小的内存数据块),而后从内存堆中分配对象所对应大小的内存空间,然后再对该对象进行必要的初始化,最后将其插入到它所在的对象容器链表中。对该函数的输入参数的描述如下表:
+
+
+|**参数** |**描述** |
+| ------------------ | ------------------------------------------------------------ |
+| type | 分配对象的类型,只能是 rt_object_class_type 中除 RT_Object_Class_Static 以外的类型。并且经过这个接口分配出来的对象类型是动态的,而不是静态的 |
+| name | 对象的名字。每个对象可以设置一个名字,这个名字的最大长度由 RT_NAME_MAX 指定,并且系统不关心它是否是由’`\0`’做为终结符 |
+|**返回** | —— |
+| 分配成功的对象句柄 | 分配成功 |
+| RT_NULL | 分配失败 |
+
+#### 删除对象
+
+对于一个动态对象,当不再使用时,可以调用如下接口删除对象,并释放相应的系统资源:
+
+```c
+void rt_object_delete(rt_object_t object);
+```
+
+当调用以上接口时,首先从对象容器链表中脱离对象,然后释放对象所占用的内存。对该函数的输入参数的描述下表:
+
+
+|**参数**|**描述** |
+|----------|------------|
+| object | 对象的句柄 |
+
+#### 辨别对象
+
+判断指定对象是否是系统对象(静态内核对象)。辨别对象使用以下接口:
+
+```c
+rt_err_t rt_object_is_systemobject(rt_object_t object);
+```
+
+调用 rt_object_is_systemobject 接口可判断一个对象是否是系统对象,在 RT-Thread 操作系统中,一个系统对象也就是一个静态对象,对象类型标识上 RT_Object_Class_Static 位置位。通常使用 rt_object_init() 方式初始化的对象都是系统对象。对该函数的输入参数的描述如下表:
+
+rt_object_is_systemobject() 的输入参数
+
+|**参数**|**描述** |
+|----------|------------|
+| object | 对象的句柄 |
+
+RT-Thread 内核配置示例
+----------------------
+
+RT-Thread 的一个重要特性是高度可裁剪性,支持对内核进行精细调整,对组件进行灵活拆卸。
+
+配置主要是通过修改工程目录下的 rtconfig.h 文件来进行,用户可以通过打开 / 关闭该文件中的宏定义来对代码进行条件编译,最终达到系统配置和裁剪的目的,如下:
+
+(1)RT-Thread 内核部分
+
+```c
+/* 表示内核对象的名称的最大长度,若代码中对象名称的最大长度大于宏定义的长度,
+ * 多余的部分将被截掉。*/
+#define RT_NAME_MAX 8
+
+/* 字节对齐时设定对齐的字节个数。常使用 ALIGN(RT_ALIGN_SIZE) 进行字节对齐。*/
+#define RT_ALIGN_SIZE 4
+
+/* 定义系统线程优先级数;通常用 RT_THREAD_PRIORITY_MAX-1 定义空闲线程的优先级 */
+#define RT_THREAD_PRIORITY_MAX 32
+
+/* 定义时钟节拍,为 100 时表示 100 个 tick 每秒,一个 tick 为 10ms */
+#define RT_TICK_PER_SECOND 100
+
+/* 检查栈是否溢出,未定义则关闭 */
+#define RT_USING_OVERFLOW_CHECK
+
+/* 定义该宏开启 debug 模式,未定义则关闭 */
+#define RT_DEBUG
+/* 开启 debug 模式时:该宏定义为 0 时表示关闭打印组件初始化信息,定义为 1 时表示启用 */
+#define RT_DEBUG_INIT 0
+/* 开启 debug 模式时:该宏定义为 0 时表示关闭打印线程切换信息,定义为 1 时表示启用 */
+#define RT_DEBUG_THREAD 0
+
+/* 定义该宏表示开启钩子函数的使用,未定义则关闭 */
+#define RT_USING_HOOK
+
+/* 定义了空闲线程的栈大小 */
+#define IDLE_THREAD_STACK_SIZE 256
+```
+
+(2)线程间同步与通信部分,该部分会使用到的对象有信号量、互斥量、事件、邮箱、消息队列、信号等。
+
+```c
+/* 定义该宏可开启信号量的使用,未定义则关闭 */
+#define RT_USING_SEMAPHORE
+
+/* 定义该宏可开启互斥量的使用,未定义则关闭 */
+#define RT_USING_MUTEX
+
+/* 定义该宏可开启事件集的使用,未定义则关闭 */
+#define RT_USING_EVENT
+
+/* 定义该宏可开启邮箱的使用,未定义则关闭 */
+#define RT_USING_MAILBOX
+
+/* 定义该宏可开启消息队列的使用,未定义则关闭 */
+#define RT_USING_MESSAGEQUEUE
+
+/* 定义该宏可开启信号的使用,未定义则关闭 */
+#define RT_USING_SIGNALS
+```
+
+(3)内存管理部分
+
+```c
+/* 开启静态内存池的使用 */
+#define RT_USING_MEMPOOL
+
+/* 定义该宏可开启两个或以上内存堆拼接的使用,未定义则关闭 */
+#define RT_USING_MEMHEAP
+
+/* 开启小内存管理算法 */
+#define RT_USING_SMALL_MEM
+
+/* 关闭 SLAB 内存管理算法 */
+/* #define RT_USING_SLAB */
+
+/* 开启堆的使用 */
+#define RT_USING_HEAP
+```
+
+(4)内核设备对象
+
+```c
+/* 表示开启了系统设备的使用 */
+#define RT_USING_DEVICE
+
+/* 定义该宏可开启系统控制台设备的使用,未定义则关闭 */
+#define RT_USING_CONSOLE
+/* 定义控制台设备的缓冲区大小 */
+#define RT_CONSOLEBUF_SIZE 128
+/* 控制台设备的名称 */
+#define RT_CONSOLE_DEVICE_NAME "uart1"
+```
+
+(5)自动初始化方式
+
+```c
+/* 定义该宏开启自动初始化机制,未定义则关闭 */
+#define RT_USING_COMPONENTS_INIT
+
+/* 定义该宏开启设置应用入口为 main 函数 */
+#define RT_USING_USER_MAIN
+/* 定义 main 线程的栈大小 */
+#define RT_MAIN_THREAD_STACK_SIZE 2048
+```
+
+(6)FinSH
+
+```c
+/* 定义该宏可开启系统 FinSH 调试工具的使用,未定义则关闭 */
+#define RT_USING_FINSH
+
+/* 开启系统 FinSH 时:将该线程名称定义为 tshell */
+#define FINSH_THREAD_NAME "tshell"
+
+/* 开启系统 FinSH 时:使用历史命令 */
+#define FINSH_USING_HISTORY
+/* 开启系统 FinSH 时:对历史命令行数的定义 */
+#define FINSH_HISTORY_LINES 5
+
+/* 开启系统 FinSH 时:定义该宏开启使用 Tab 键,未定义则关闭 */
+#define FINSH_USING_SYMTAB
+
+/* 开启系统 FinSH 时:定义该线程的优先级 */
+#define FINSH_THREAD_PRIORITY 20
+/* 开启系统 FinSH 时:定义该线程的栈大小 */
+#define FINSH_THREAD_STACK_SIZE 4096
+/* 开启系统 FinSH 时:定义命令字符长度 */
+#define FINSH_CMD_SIZE 80
+
+/* 开启系统 FinSH 时:定义该宏开启 MSH 功能 */
+#define FINSH_USING_MSH
+/* 开启系统 FinSH 时:开启 MSH 功能时,定义该宏默认使用 MSH 功能 */
+#define FINSH_USING_MSH_DEFAULT
+/* 开启系统 FinSH 时:定义该宏,仅使用 MSH 功能 */
+#define FINSH_USING_MSH_ONLY
+```
+
+(7)关于 MCU
+
+```c
+/* 定义该工程使用的 MCU 为 STM32F103ZE;系统通过对芯片类型的定义,来定义芯片的管脚 */
+#define STM32F103ZE
+
+/* 定义时钟源频率 */
+#define RT_HSE_VALUE 8000000
+
+/* 定义该宏开启 UART1 的使用 */
+#define RT_USING_UART1
+```
+
+> [!NOTE]
+> 注:在实际应用中,系统配置文件 rtconfig.h 是由配置工具自动生成的,无需手动更改。
+
+常见宏定义说明
+--------------
+
+RT-Thread 中经常使用一些宏定义,举例 Keil 编译环境下一些常见的宏定义:
+
+1)rt_inline,定义如下,static 关键字的作用是令函数只能在当前的文件中使用;inline 表示内联,用 static 修饰后在调用函数时会建议编译器进行内联展开。
+
+```c
+#define rt_inline static __inline
+```
+
+2)RT_USED,定义如下,该宏的作用是向编译器说明这段代码有用,即使函数中没有调用也要保留编译。例如 RT-Thread 自动初始化功能使用了自定义的段,使用 RT_USED 会将自定义的代码段保留。
+
+```c
+#define RT_USED __attribute__((used))
+```
+
+3)RT_UNUSED,定义如下,表示函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
+
+```c
+#define RT_UNUSED __attribute__((unused))
+```
+
+4)RT_WEAK,定义如下,常用于定义函数,编译器在链接函数时会优先链接没有该关键字前缀的函数,如果找不到则再链接由 weak 修饰的函数。
+
+```c
+#define RT_WEAK __weak
+```
+
+5)ALIGN(n),定义如下,作用是在给某对象分配地址空间时,将其存放的地址按照 n 字节对齐,这里 n 可取 2 的幂次方。字节对齐的作用不仅是便于 CPU 快速访问,同时合理的利用字节对齐可以有效地节省存储空间。
+
+```c
+#define ALIGN(n) __attribute__((aligned(n)))
+```
+
+6)RT_ALIGN(size,align),定义如下,作用是将 size 提升为 align 定义的整数的倍数,例如,RT_ALIGN(13,4) 将返回 16。
+
+```c
+#define RT_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
+```
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03Memory_distribution.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03Memory_distribution.png
new file mode 100644
index 0000000000000000000000000000000000000000..8664ec89f9aaf53a2fc7afbadeb1dcf47e5a3a0d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03Memory_distribution.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03Startup_process.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03Startup_process.png
new file mode 100644
index 0000000000000000000000000000000000000000..4128df8d949bc2e7dd062a176beb280cff75d3cf
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03Startup_process.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_Framework.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_Framework.png
new file mode 100644
index 0000000000000000000000000000000000000000..a68f0e0afb3c323aa5754b0f8359b299fe791eaa
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_Framework.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_object.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_object.png
new file mode 100644
index 0000000000000000000000000000000000000000..8a5cbd9236b2729b8955d708af49c1cebbcd2c62
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_object.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_object2.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_object2.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1601b48686cb94abb817b7213c52e85f80cf6c4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/03kernel_object2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/rtt_startup.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/rtt_startup.png
new file mode 100644
index 0000000000000000000000000000000000000000..04dbb79fed05b7463c5ceea179f5dd75c6553724
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/rtt_startup.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/startup-rtt.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/startup-rtt.png
new file mode 100644
index 0000000000000000000000000000000000000000..db72d70a1c03a7946c586cafbe8e527befb150f0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/startup-rtt.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/startup.png b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/startup.png
new file mode 100644
index 0000000000000000000000000000000000000000..7fe7c643dbd896ea9e1708b700a150612e87cf11
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/startup.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md
new file mode 100644
index 0000000000000000000000000000000000000000..40c907ddc5da4088affbec20a82f6e0681d8aa8b
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md
@@ -0,0 +1,271 @@
+# ADC 设备
+
+## ADC 简介
+
+ADC(Analog-to-Digital Converter) 指模数转换器。是指将连续变化的模拟信号转换为离散的数字信号的器件。真实世界的模拟信号,例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的数字形式。模数转换器可以实现这个功能,在各种不同的产品中都可以找到它的身影。与之相对应的 DAC(Digital-to-Analog Converter),它是 ADC 模数转换的逆向过程。ADC 最早用于对无线信号向数字信号转换。如电视信号,长短播电台发接收等。
+
+### 转换过程
+
+如下图所示模数转换一般要经过采样、保持和量化、编码这几个步骤。在实际电路中,有些过程是合并进行的,如采样和保持,量化和编码在转换过程中是同时实现的。
+
+
+
+采样是将时间上连续变化的模拟信号转换为时间上离散的模拟信号。采样取得的模拟信号转换为数字信号都需要一定时间,为了给后续的量化编码过程提供一个稳定的值,在采样电路后要求将所采样的模拟信号保持一段时间。
+
+将数值连续的模拟量转换为数字量的过程称为量化。数字信号在数值上是离散的。采样保持电路的输出电压还需要按照某种近似方式归化到与之相应的离散电平上,任何数字量只能是某个最小数量单位的整数倍。量化后的数值最后还需要编码过程,也就是 A/D 转换器输出的数字量。
+
+### 分辨率
+
+分辨率以二进制(或十进制)数的位数来表示,一般有8位、10位、12位、16位等,它说明模数转换器对输入信号的分辨能力,位数越多,表示分辨率越高,恢复模拟信号时会更精确。
+
+### 精度
+
+精度表示 ADC 器件在所有的数值点上对应的模拟值和真实值之间的最大误差值,也就是输出数值偏离线性最大的距离。
+
+> [!NOTE]
+> 注:精度与分辨率是两个不一样的概念,请注意区分。
+
+### 转换速率
+
+转换速率是指 A/D 转换器完成一次从模拟到数字的 AD 转换所需时间的倒数。例如,某 A/D 转换器的转换速率为 1MHz,则表示完成一次 AD 转换时间为 1 微秒。
+
+## 访问 ADC 设备
+
+应用程序通过 RT-Thread 提供的 ADC 设备管理接口来访问 ADC 硬件,相关接口如下所示:
+
+| **函数** | **描述** |
+| --------------- | ------------------ |
+| rt_device_find() | 根据 ADC 设备名称查找设备获取设备句柄 |
+| rt_adc_enable() | 使能 ADC 设备 |
+| rt_adc_read() | 读取 ADC 设备数据 |
+| rt_adc_disable() | 关闭 ADC 设备 |
+
+### 查找 ADC 设备
+
+应用程序根据 ADC 设备名称获取设备句柄,进而可以操作 ADC 设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | ADC 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到设备 |
+
+一般情况下,注册到系统的 ADC 设备名称为 adc0,adc1等,使用示例如下所示:
+
+```c
+#define ADC_DEV_NAME "adc1" /* ADC 设备名称 */
+rt_adc_device_t adc_dev; /* ADC 设备句柄 */
+/* 查找设备 */
+adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
+```
+
+### 使能 ADC 通道
+
+在读取 ADC 设备数据前需要先使能设备,通过如下函数使能设备:
+
+```c
+rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_uint32_t channel);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | ADC 设备句柄 |
+| channel | ADC 通道 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| -RT_ENOSYS | 失败,设备操作方法为空 |
+| 其他错误码 | 失败 |
+
+使用示例如下所示:
+
+```c
+#define ADC_DEV_NAME "adc1" /* ADC 设备名称 */
+#define ADC_DEV_CHANNEL 5 /* ADC 通道 */
+rt_adc_device_t adc_dev; /* ADC 设备句柄 */
+/* 查找设备 */
+adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
+/* 使能设备 */
+rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
+```
+
+### 读取 ADC 通道采样值
+
+读取 ADC 通道采样值可通过如下函数完成:
+
+```c
+rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_uint32_t channel);
+```
+
+| **参数** | **描述** |
+| ---------- | ----------------- |
+| dev | ADC 设备句柄 |
+| channel | ADC 通道 |
+| **返回** | —— |
+| 读取的数值 | |
+
+使用 ADC 采样电压值的使用示例如下所示:
+
+```c
+#define ADC_DEV_NAME "adc1" /* ADC 设备名称 */
+#define ADC_DEV_CHANNEL 5 /* ADC 通道 */
+#define REFER_VOLTAGE 330 /* 参考电压 3.3V,数据精度乘以100保留2位小数*/
+#define CONVERT_BITS (1 << 12) /* 转换位数为12位 */
+
+rt_adc_device_t adc_dev; /* ADC 设备句柄 */
+rt_uint32_t value;
+/* 查找设备 */
+adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
+/* 使能设备 */
+rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
+/* 读取采样值 */
+value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
+/* 转换为对应电压值 */
+vol = value * REFER_VOLTAGE / CONVERT_BITS;
+rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
+```
+
+实际电压值的计算公式为:采样值 * 参考电压 / (1 << 分辨率位数),上面示例代码乘以 100 将数据放大,最后通过 vol / 100 获得电压的整数位值,通过 vol % 100 获得电压的小数位值。
+
+### 关闭 ADC 通道
+
+关闭 ADC 通道可通过如下函数完成:
+
+```c
+rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_uint32_t channel);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | ADC 设备句柄 |
+| channel | ADC 通道 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| -RT_ENOSYS | 失败,设备操作方法为空 |
+| 其他错误码 | 失败 |
+
+使用示例如下所示:
+
+```c
+#define ADC_DEV_NAME "adc1" /* ADC 设备名称 */
+#define ADC_DEV_CHANNEL 5 /* ADC 通道 */
+rt_adc_device_t adc_dev; /* ADC 设备句柄 */
+rt_uint32_t value;
+/* 查找设备 */
+adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
+/* 使能设备 */
+rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
+/* 读取采样值 */
+value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
+/* 转换为对应电压值 */
+vol = value * REFER_VOLTAGE / CONVERT_BITS;
+rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
+/* 关闭通道 */
+rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
+```
+
+### FinSH 命令
+
+在使用设备前,需要先查找设备是否存在,可以使用命令 `adc probe` 后面跟注册的 ADC 设备的名称。如下所示:
+
+```c
+msh >adc probe adc1
+probe adc1 success
+```
+
+使能设备的某个通道可以使用命令 `adc enable` 后面跟通道号。
+
+```c
+msh >adc enable 5
+adc1 channel 5 enables success
+```
+
+读取 ADC 设备某个通道的数据可以使用命令 `adc read` 后面跟通道号。
+
+```c
+msh >adc read 5
+adc1 channel 5 read value is 0x00000FFF
+msh >
+```
+
+关闭设备的某个通道可以使用命令 `adc disable` 后面跟通道号。
+
+```c
+msh >adc disable 5
+adc1 channel 5 disable success
+msh >
+```
+
+## ADC 设备使用示例
+
+ADC 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 首先根据 ADC 设备名称 “adc1” 查找设备获取设备句柄。
+
+2. 使能设备后读取 adc1 设备对应的通道 5 的采样值,然后根据分辨率为 12 位,参考电压为 3.3V 计算实际的电压值。
+
+3. 最后关闭 ADC 设备对应通道。
+
+运行结果:打印实际读取到的转换的原始数据和经过计算后的实际电压值。
+
+```c
+/*
+ * 程序清单: ADC 设备使用例程
+ * 例程导出了 adc_sample 命令到控制终端
+ * 命令调用格式:adc_sample
+ * 程序功能:通过 ADC 设备采样电压值并转换为数值。
+ * 示例代码参考电压为3.3V,转换位数为12位。
+*/
+
+#include
+#include
+
+#define ADC_DEV_NAME "adc1" /* ADC 设备名称 */
+#define ADC_DEV_CHANNEL 5 /* ADC 通道 */
+#define REFER_VOLTAGE 330 /* 参考电压 3.3V,数据精度乘以100保留2位小数*/
+#define CONVERT_BITS (1 << 12) /* 转换位数为12位 */
+
+static int adc_vol_sample(int argc, char *argv[])
+{
+ rt_adc_device_t adc_dev;
+ rt_uint32_t value, vol;
+ rt_err_t ret = RT_EOK;
+
+ /* 查找设备 */
+ adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
+ if (adc_dev == RT_NULL)
+ {
+ rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_DEV_NAME);
+ return RT_ERROR;
+ }
+
+ /* 使能设备 */
+ ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
+
+ /* 读取采样值 */
+ value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
+ rt_kprintf("the value is :%d \n", value);
+
+ /* 转换为对应电压值 */
+ vol = value * REFER_VOLTAGE / CONVERT_BITS;
+ rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
+
+ /* 关闭通道 */
+ ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
+
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample);
+```
+
+## 常见问题
+
+### Q: menuconfig 找不到 ADC 设备的配置选项?
+
+ **A:** 使用的源代码还不支持 ADC 设备驱动框架。建议更新源代码。
+
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc-p.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc-p.png
new file mode 100644
index 0000000000000000000000000000000000000000..8a36ca066a8c590cd05214f8bfe3a8a6acc080c5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc-p.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..e7b912f110cd5fb4238d0af3aa805cc0a4550aeb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc.vsdx differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/open_other.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/open_other.png
new file mode 100644
index 0000000000000000000000000000000000000000..018fc722205ed8ef01785abb29c6535ffaaa39b5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/open_other.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/audio/audio.md b/rt-thread-version/rt-thread-standard/programming-manual/device/audio/audio.md
new file mode 100644
index 0000000000000000000000000000000000000000..b574891fc1f80bf4a1362e7ac89cef44dd9347d3
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/audio/audio.md
@@ -0,0 +1,544 @@
+# AUDIO 设备
+
+## Audio 简介
+
+Audio (音频)设备是嵌入式系统中非常重要的一个组成部分,负责音频数据的采样和输出。Audio 设备通常由数据总线接口、控制总线接口、音频编解码器(Codec)、扬声器和麦克风等组成,如下图所示:
+
+
+
+### Audio 设备特性
+
+RT-Thread Audio 设备驱动框架是 Audio 框架的底层部分,主要负责原生音频数据的采集和输出、音频流的控制、音频设备的管理、音量调节以及不同硬件和 Codec 的抽象等。
+
+- 接口:标准 device 接口(open/close/read/control)。
+- 同步模式访问。
+- 支持播放和录音。
+- 支持音频参数管理。
+- 支持音量调节。
+
+## 访问 Audio 设备
+
+### 查找 Audio 设备
+
+应用程序根据 Audio 设备名称获取设备句柄,进而可以操作 Audio 设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | Audio 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+使用示例如下所示:
+```c
+#define SOUND_DEVICE_NAME "sound0" /* Audio 设备名称 */
+
+static rt_device_t snd_dev; /* Audio 设备句柄 */
+
+/* 根据设备名称查找 Audio 设备,获取设备句柄 */
+snd_dev = rt_device_find(SOUND_DEVICE_NAME);
+```
+
+### 打开 Audio 设备
+
+通过设备句柄,应用程序可以打开和关闭设备,通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 设备句柄 |
+| oflags | 设备模式标志 |
+| **返回** | —— |
+| RT_EOK | 设备打开成功 |
+| -RT_EBUSY | 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开 |
+| -RT_EINVAL | 不支持的打开参数 |
+| 其他错误码 | 设备打开失败 |
+
+oflags 参数支持下列参数:
+
+```c
+#define RT_DEVICE_OFLAG_WRONLY 0x002 /* 标准设备的只写模式,对应 Audio 播放设备 */
+#define RT_DEVICE_FLAG_RDONLY 0x001 /* 标准设备的只读模式,对应 Audio 录音设备 */
+```
+
+Audio 设备分为播放和录音 2 种类型,播放设备输出音频数据到 Codec 编解码器,录音设备则读取数据。在使用的时候,播放设备通过只写标志进行标识,录音设备通过只读标志进行标识。
+
+打开 Audio 播放设备使用示例如下所示:
+```c
+rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY)
+```
+
+打开 Audio 录音设备使用示例如下所示:
+```c
+rt_device_open(mic_dev, RT_DEVICE_FLAG_RDONLY)
+```
+
+### 控制 Audio 设备
+
+通过命令控制字,应用程序可以对 Audio 设备进行配置,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| cmd | 命令控制字,详细介绍见下面 |
+| arg | 控制的参数, 详细介绍见下面 |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+其中的 cmd 目前支持以下几种命令控制字
+
+```c
+/* AUDIO command */
+#define _AUDIO_CTL(a) (0x10 + a)
+
+#define AUDIO_CTL_GETCAPS _AUDIO_CTL(1) /* 获取设备功能属性 */
+#define AUDIO_CTL_CONFIGURE _AUDIO_CTL(2) /* 配置设备功能属性 */
+
+```
+
+- 设备功能属性结构体的定义如下
+
+```c
+struct rt_audio_caps
+{
+ int main_type; /* 命令主类型 */
+ int sub_type; /* 命令子类型 */
+
+ union
+ {
+ rt_uint32_t mask;
+ int value; /* 参数值 */
+ struct rt_audio_configure config; /* 音频参数信息 */
+ } udata;
+};
+```
+
+#### 设置播放的音频参数信息
+
+设置播放的采样率、采样通道、以及采样位数。
+
+```c
+struct rt_audio_caps caps;
+
+caps.main_type = AUDIO_TYPE_OUTPUT; /* 输出类型(播放设备 )*/
+caps.sub_type = AUDIO_DSP_PARAM; /* 设置所有音频参数信息 */
+caps.udata.config.samplerate = 44100; /* 采样率 */
+caps.udata.config.channels = 2; /* 采样通道 */
+caps.udata.config.samplebits = 16; /* 采样位数 */
+rt_device_control(device, AUDIO_CTL_CONFIGURE, &caps);
+
+```
+
+#### 设置播放的主音量
+
+设置播放的主音量。
+
+```c
+struct rt_audio_caps caps;
+
+caps.main_type = AUDIO_TYPE_MIXER; /* 音量管理类型 */
+caps.sub_type = AUDIO_MIXER_VOLUME; /* 设置播放的主音量 */
+caps.udata.value = volume; /* 范围 0 ~ 100 */
+rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
+
+```
+
+#### 设置录音的音频参数信息
+
+设置录音的采样率、采样通道、以及采样位数。
+
+```c
+struct rt_audio_caps caps;
+
+caps.main_type = AUDIO_TYPE_INPUT; /* 输入类型(录音设备 )*/
+caps.sub_type = AUDIO_DSP_PARAM; /* 设置所有音频参数信息 */
+caps.udata.config.samplerate = 44100; /* 采样率 */
+caps.udata.config.channels = 2; /* 采样通道 */
+caps.udata.config.samplebits = 16; /* 采样位数 */
+rt_device_control(device, AUDIO_CTL_CONFIGURE, &caps);
+
+```
+
+#### 设置录音的主音量
+
+设置录音的主音量。
+
+```c
+struct rt_audio_caps caps;
+
+caps.main_type = AUDIO_TYPE_MIXER; /* 音量管理类型 */
+caps.sub_type = AUDIO_MIXER_MIC; /* 设置录音的主音量 */
+caps.udata.value = volume; /* 范围 0 ~ 100 */
+rt_device_control(player->device, AUDIO_CTL_CONFIGURE, &caps);
+
+```
+
+### 写入音频数据
+
+向音频播放设备中写入数据,可以通过如下函数完成:
+
+```c
+rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| pos | 写入数据偏移量,此参数音频设备未使用 |
+| buffer | 内存缓冲区指针,放置要写入的数据 |
+| size | 写入数据的大小 |
+| **返回** | —— |
+| 写入数据的实际大小 | 以字节为单位; |
+
+调用这个函数,会把缓冲区 buffer 中的数据写入到设备 dev 中,写入数据的大小是 size。该函数为同步接口,驱动框架内部会将数据先保存到音频设备的缓冲区,当缓冲区满时,函数被阻塞。
+
+### 读取音频数据
+
+可调用如下函数读取音频录音设备接收到的数据:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ------------------ | ---------------------------------------------- |
+| dev | 设备句柄 |
+| pos | 读取数据偏移量,此参数串口设备未使用 |
+| buffer | 缓冲区指针,读取的数据将会被保存在缓冲区中 |
+| size | 读取数据的大小 |
+| **返回** | —— |
+| 读到数据的实际大小 | 如果是字符设备,返回大小以字节为单位 |
+| 0 | 需要读取当前线程的 errno 来判断错误状态 |
+
+调用这个函数,从音频录音设备读取 size 大小的数据到 buffer 中。该函数为同步接口,当驱动框架内部 Pipe缓存的数据小于 size 时,函数被阻塞。
+
+### 关闭音频设备
+
+当应用程序完成串口操作后,可以关闭音频设备,通过如下函数完成:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| dev | 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+## 音频设备使用示例
+
+音频设备用于播放和录音,通常伴随着音频文件的编解码使用。下面介绍播放和录制 wav 文件的示例,完整的代码可以通过 [RT-Thread wavplayer 软件包](https://github.com/RT-Thread-packages/wavplayer) 获取。
+
+### 播放
+
+播放一段音频数据的主要步骤如下:
+
+1. 首先查找 Audio 设备获取设备句柄。
+
+2. 以只写方式打开 Audio 设备。
+
+3. 设置音频参数信息(采样率、通道等)。
+
+4. 解码音频文件的数据。
+
+5. 写入音频文件数据。
+
+6. 播放完成,关闭设备。
+
+```c
+#include
+#include
+#include
+
+#define BUFSZ 1024
+#define SOUND_DEVICE_NAME "sound0" /* Audio 设备名称 */
+static rt_device_t snd_dev; /* Audio 设备句柄 */
+
+struct RIFF_HEADER_DEF
+{
+ char riff_id[4]; // 'R','I','F','F'
+ uint32_t riff_size;
+ char riff_format[4]; // 'W','A','V','E'
+};
+
+struct WAVE_FORMAT_DEF
+{
+ uint16_t FormatTag;
+ uint16_t Channels;
+ uint32_t SamplesPerSec;
+ uint32_t AvgBytesPerSec;
+ uint16_t BlockAlign;
+ uint16_t BitsPerSample;
+};
+
+struct FMT_BLOCK_DEF
+{
+ char fmt_id[4]; // 'f','m','t',' '
+ uint32_t fmt_size;
+ struct WAVE_FORMAT_DEF wav_format;
+};
+
+struct DATA_BLOCK_DEF
+{
+ char data_id[4]; // 'R','I','F','F'
+ uint32_t data_size;
+};
+
+struct wav_info
+{
+ struct RIFF_HEADER_DEF header;
+ struct FMT_BLOCK_DEF fmt_block;
+ struct DATA_BLOCK_DEF data_block;
+};
+
+int wavplay_sample(int argc, char **argv)
+{
+ int fd = -1;
+ uint8_t *buffer = NULL;
+ struct wav_info *info = NULL;
+ struct rt_audio_caps caps = {0};
+
+ if (argc != 2)
+ {
+ rt_kprintf("Usage:\n");
+ rt_kprintf("wavplay_sample song.wav\n");
+ return 0;
+ }
+
+ fd = open(argv[1], O_WRONLY);
+ if (fd < 0)
+ {
+ rt_kprintf("open file failed!\n");
+ goto __exit;
+ }
+
+ buffer = rt_malloc(BUFSZ);
+ if (buffer == RT_NULL)
+ goto __exit;
+
+ info = (struct wav_info *) rt_malloc(sizeof * info);
+ if (info == RT_NULL)
+ goto __exit;
+
+ if (read(fd, &(info->header), sizeof(struct RIFF_HEADER_DEF)) <= 0)
+ goto __exit;
+ if (read(fd, &(info->fmt_block), sizeof(struct FMT_BLOCK_DEF)) <= 0)
+ goto __exit;
+ if (read(fd, &(info->data_block), sizeof(struct DATA_BLOCK_DEF)) <= 0)
+ goto __exit;
+
+ rt_kprintf("wav information:\n");
+ rt_kprintf("samplerate %d\n", info->fmt_block.wav_format.SamplesPerSec);
+ rt_kprintf("channel %d\n", info->fmt_block.wav_format.Channels);
+
+ /* 根据设备名称查找 Audio 设备,获取设备句柄 */
+ snd_dev = rt_device_find(SOUND_DEVICE_NAME);
+
+ /* 以只写方式打开 Audio 播放设备 */
+ rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY);
+
+ /* 设置采样率、通道、采样位数等音频参数信息 */
+ caps.main_type = AUDIO_TYPE_OUTPUT; /* 输出类型(播放设备 )*/
+ caps.sub_type = AUDIO_DSP_PARAM; /* 设置所有音频参数信息 */
+ caps.udata.config.samplerate = info->fmt_block.wav_format.SamplesPerSec; /* 采样率 */
+ caps.udata.config.channels = info->fmt_block.wav_format.Channels; /* 采样通道 */
+ caps.udata.config.samplebits = 16; /* 采样位数 */
+ rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
+
+ while (1)
+ {
+ int length;
+
+ /* 从文件系统读取 wav 文件的音频数据 */
+ length = read(fd, buffer, BUFSZ);
+
+ if (length <= 0)
+ break;
+
+ /* 向 Audio 设备写入音频数据 */
+ rt_device_write(snd_dev, 0, buffer, length);
+ }
+
+ /* 关闭 Audio 设备 */
+ rt_device_close(snd_dev);
+
+__exit:
+
+ if (fd >= 0)
+ close(fd);
+
+ if (buffer)
+ rt_free(buffer);
+
+ if (info)
+ rt_free(info);
+
+ return 0;
+}
+
+MSH_CMD_EXPORT(wavplay_sample, play wav file);
+
+```
+
+
+
+### 录音
+
+录制一段音频数据的主要步骤如下:
+
+1. 首先查找 Audio 设备获取设备句柄。
+
+2. 以只读方式打开 Audio 设备。
+
+3. 设置音频参数信息(采样率、通道等)。
+
+4. 从音频设备读取数据。
+
+5. 处理读取的数据等。
+
+6. 录音完成,关闭设备。
+
+```c
+#include
+#include
+#include
+
+#define RECORD_TIME_MS 5000
+#define RECORD_SAMPLERATE 16000
+#define RECORD_CHANNEL 2
+#define RECORD_CHUNK_SZ ((RECORD_SAMPLERATE * RECORD_CHANNEL * 2) * 20 / 1000)
+
+#define SOUND_DEVICE_NAME "mic0" /* Audio 设备名称 */
+static rt_device_t mic_dev; /* Audio 设备句柄 */
+
+struct wav_header
+{
+ char riff_id[4]; /* "RIFF" */
+ int riff_datasize; /* RIFF chunk data size,exclude riff_id[4] and riff_datasize,total - 8 */
+ char riff_type[4]; /* "WAVE" */
+ char fmt_id[4]; /* "fmt " */
+ int fmt_datasize; /* fmt chunk data size,16 for pcm */
+ short fmt_compression_code; /* 1 for PCM */
+ short fmt_channels; /* 1(mono) or 2(stereo) */
+ int fmt_sample_rate; /* samples per second */
+ int fmt_avg_bytes_per_sec; /* sample_rate * channels * bit_per_sample / 8 */
+ short fmt_block_align; /* number bytes per sample, bit_per_sample * channels / 8 */
+ short fmt_bit_per_sample; /* bits of each sample(8,16,32). */
+ char data_id[4]; /* "data" */
+ int data_datasize; /* data chunk size,pcm_size - 44 */
+};
+
+static void wavheader_init(struct wav_header *header, int sample_rate, int channels, int datasize)
+{
+ memcpy(header->riff_id, "RIFF", 4);
+ header->riff_datasize = datasize + 44 - 8;
+ memcpy(header->riff_type, "WAVE", 4);
+ memcpy(header->fmt_id, "fmt ", 4);
+ header->fmt_datasize = 16;
+ header->fmt_compression_code = 1;
+ header->fmt_channels = channels;
+ header->fmt_sample_rate = sample_rate;
+ header->fmt_bit_per_sample = 16;
+ header->fmt_avg_bytes_per_sec = header->fmt_sample_rate * header->fmt_channels * header->fmt_bit_per_sample / 8;
+ header->fmt_block_align = header->fmt_bit_per_sample * header->fmt_channels / 8;
+ memcpy(header->data_id, "data", 4);
+ header->data_datasize = datasize;
+}
+
+int wavrecord_sample(int argc, char **argv)
+{
+ int fd = -1;
+ uint8_t *buffer = NULL;
+ struct wav_header header;
+ struct rt_audio_caps caps = {0};
+ int length, total_length = 0;
+
+ if (argc != 2)
+ {
+ rt_kprintf("Usage:\n");
+ rt_kprintf("wavrecord_sample file.wav\n");
+ return -1;
+ }
+
+ fd = open(argv[1], O_WRONLY | O_CREAT);
+ if (fd < 0)
+ {
+ rt_kprintf("open file for recording failed!\n");
+ return -1;
+ }
+ write(fd, &header, sizeof(struct wav_header));
+
+ buffer = rt_malloc(RECORD_CHUNK_SZ);
+ if (buffer == RT_NULL)
+ goto __exit;
+
+ /* 根据设备名称查找 Audio 设备,获取设备句柄 */
+ mic_dev = rt_device_find(SOUND_DEVICE_NAME);
+ if (mic_dev == RT_NULL)
+ goto __exit;
+
+ /* 以只读方式打开 Audio 录音设备 */
+ rt_device_open(mic_dev, RT_DEVICE_OFLAG_RDONLY);
+
+ /* 设置采样率、通道、采样位数等音频参数信息 */
+ caps.main_type = AUDIO_TYPE_INPUT; /* 输入类型(录音设备 )*/
+ caps.sub_type = AUDIO_DSP_PARAM; /* 设置所有音频参数信息 */
+ caps.udata.config.samplerate = RECORD_SAMPLERATE; /* 采样率 */
+ caps.udata.config.channels = RECORD_CHANNEL; /* 采样通道 */
+ caps.udata.config.samplebits = 16; /* 采样位数 */
+ rt_device_control(mic_dev, AUDIO_CTL_CONFIGURE, &caps);
+
+ while (1)
+ {
+ /* 从 Audio 设备中,读取 20ms 的音频数据 */
+ length = rt_device_read(mic_dev, 0, buffer, RECORD_CHUNK_SZ);
+
+ if (length)
+ {
+ /* 写入音频数据到到文件系统 */
+ write(fd, buffer, length);
+ total_length += length;
+ }
+
+ if ((total_length / RECORD_CHUNK_SZ) > (RECORD_TIME_MS / 20))
+ break;
+ }
+
+ /* 重新写入 wav 文件的头 */
+ wavheader_init(&header, RECORD_SAMPLERATE, RECORD_CHANNEL, total_length);
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &header, sizeof(struct wav_header));
+ close(fd);
+
+ /* 关闭 Audio 设备 */
+ rt_device_close(mic_dev);
+
+__exit:
+ if (fd >= 0)
+ close(fd);
+
+ if (buffer)
+ rt_free(buffer);
+
+ return 0;
+}
+MSH_CMD_EXPORT(wavrecord_sample, record voice to a wav file);
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/audio/figures/audio_system.png b/rt-thread-version/rt-thread-standard/programming-manual/device/audio/figures/audio_system.png
new file mode 100644
index 0000000000000000000000000000000000000000..d58bd3a1980b6104e4442c9cd9dc911db590c165
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/audio/figures/audio_system.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/can/can.md b/rt-thread-version/rt-thread-standard/programming-manual/device/can/can.md
new file mode 100644
index 0000000000000000000000000000000000000000..0db30bee5cbe82af085df0bcfdf9b9cdbe2506da
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/can/can.md
@@ -0,0 +1,615 @@
+# CAN 设备
+
+## CAN 简介
+
+CAN 是控制器局域网络 (Controller Area Network, CAN) 的简称,是由以研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。
+
+CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。 CAN 的连接示意图如下图所示:
+
+
+
+CAN 总线有如下特点:
+
+ * CAN 总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。
+ * 多主控制。在总线空闲时,所有的单元都可开始发送消息(多主控制)。多个单元同时开始发送时,发送高优先级 ID 消息的单元可获得发送权。
+ * 消息的发送。在 CAN 协议中,所有的消息都以固定的格式发送。总线空闲时,所有与总线相连的单元都可以开始发送新消息。两个以上的单元同时开始发送消息时,根据标识符 ID 决定优先级。ID 表示访问总线的消息的优先级。两个以上的单元同时开始发送消息时,对各消息 ID 的每个位进行逐个仲裁比较。仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。
+ * 根据整个网络的规模,可设定适合的通信速度。在同一网络中,所有单元必须设定成统一的通信速度。即使有一个单元的通信速度与其它的不一样,此单元也会输出错误信号,妨碍整个网络的通信。不同网络间则可以有不同的通信速度。
+
+ CAN 协议包括 5 种类型的帧:
+
+ * 数据帧
+ * 遥控帧
+ * 错误帧
+ * 过载帧
+ * 帧间隔
+
+数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有 11 个位的 ID,扩展格式有 29 个位的 ID。
+
+各种帧的用途如下表所示:
+
+| 帧 | 帧用途 |
+| --------------------------- | -------------------------- |
+| 数据帧 |用于发送单元向接收单元传送数据的帧 |
+| 遥控帧 | 用于接收单元向具有相同 ID 的发送单元请求数据的帧 |
+| 错误帧 | 用于当检测出错误时向其它单元通知错误的帧 |
+| 过载帧 |用于接收单元通知其尚未做好接收准备的帧 |
+| 帧间隔 |用于将数据帧及遥控帧与前面的帧分离开来的帧 |
+
+## 访问 CAN 设备
+
+应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问 CAN 硬件控制器,相关接口如下所示:
+
+| 函数 | 描述 |
+| --------------------------- | -------------------------- |
+| rt_device_find | 查找设备 |
+| rt_device_open | 打开设备 |
+| rt_device_read | 读取数据 |
+| rt_device_write | 写入数据 |
+| rt_device_control | 控制设备 |
+| rt_device_set_rx_indicate | 设置接收回调函数 |
+| rt_device_close | 关闭设备 |
+
+### 查找 CAN 设备
+
+应用程序根据 CAN 设备名称查找设备获取设备句柄,进而可以操作 CAN 设备,查找设备函数如下所示,
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| 参数 | 描述 |
+| -------- | ---------------------------------- |
+| name | 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+一般情况下,注册到系统的 CAN 设备名称为 can1,can2 等,使用示例如下所示:
+
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+/* 查找 CAN 设备 */
+can_dev = rt_device_find(CAN_DEV_NAME);
+```
+
+### 打开 CAN 设备
+
+通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+| 参数 | 描述 |
+| ---------- | ------------------------------- |
+| dev | 设备句柄 |
+| oflags | 打开设备模式标志 |
+| **返回** | —— |
+| RT_EOK | 设备打开成功 |
+| -RT_EBUSY | 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开 |
+| 其他错误码 | 设备打开失败 |
+
+目前 RT-Thread CAN 设备驱动框架支持中断接收和中断发送模式。oflags 参数支持下列取值 (可以采用或的方式支持多种取值):
+
+```c
+#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收模式 */
+#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中断发送模式 */
+```
+
+以中断接收及发送模式打开 CAN 设备的示例如下所示:
+
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+/* 查找 CAN 设备 */
+can_dev = rt_device_find(CAN_DEV_NAME);
+/* 以中断接收及发送模式打开 CAN 设备 */
+rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
+```
+
+### 控制 CAN 设备
+
+通过命令控制字,应用程序可以对 CAN 设备进行配置,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| 参数 | 描述 |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| cmd | 控制命令 |
+| arg | 控制参数 |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| 其他错误码 | 执行失败 |
+
+arg(控制参数)根据命令不同而不同,cmd(控制命令)可取以下值:
+
+```c
+#define RT_DEVICE_CTRL_RESUME 0x01 /* 恢复设备 */
+#define RT_DEVICE_CTRL_SUSPEND 0x02 /* 挂起设备 */
+#define RT_DEVICE_CTRL_CONFIG 0x03 /* 配置设备 */
+
+#define RT_CAN_CMD_SET_FILTER 0x13 /* 设置硬件过滤表 */
+#define RT_CAN_CMD_SET_BAUD 0x14 /* 设置波特率 */
+#define RT_CAN_CMD_SET_MODE 0x15 /* 设置 CAN 工作模式 */
+#define RT_CAN_CMD_SET_PRIV 0x16 /* 设置发送优先级 */
+#define RT_CAN_CMD_GET_STATUS 0x17 /* 获取 CAN 设备状态 */
+#define RT_CAN_CMD_SET_STATUS_IND 0x18 /* 设置状态回调函数 */
+#define RT_CAN_CMD_SET_BUS_HOOK 0x19 /* 设置 CAN 总线钩子函数 */
+```
+#### 设置波特率
+设置波特率的示例代码如下所示:
+
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+
+/* 查找 CAN 设备 */
+can_dev = rt_device_find(CAN_DEV_NAME);
+/* 以中断接收及发送方式打开 CAN 设备 */
+res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
+/* 设置 CAN 通信的波特率为 500kbit/s*/
+res = rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud);
+
+```
+
+#### 设置工作模式
+
+设置工作模式的示例代码如下所示:
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+
+/* 查找 CAN 设备 */
+can_dev = rt_device_find(CAN_DEV_NAME);
+/* 以中断接收及发送方式打开 CAN 设备 */
+res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
+/* 设置 CAN 的工作模式为正常工作模式 */
+res = rt_device_control(can_dev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_NORMAL);
+```
+#### 获取 CAN 设备状态
+
+获取 CAN 设备状态的示例代码如下所示:
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+static struct rt_can_status status; /* 获取到的 CAN 总线状态 */
+
+/* 查找 CAN 设备 */
+can_dev = rt_device_find(CAN_DEV_NAME);
+/* 以中断接收及发送方式打开 CAN 设备 */
+res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
+/* 获取 CAN 总线设备的状态 */
+res = rt_device_control(can_dev, RT_CAN_CMD_GET_STATUS, &status);
+```
+
+#### 设置硬件过滤表
+
+过滤表控制块各成员描述如下所示:
+```c
+struct rt_can_filter_item
+{
+ rt_uint32_t id : 29; /* 报文 ID */
+ rt_uint32_t ide : 1; /* 扩展帧标识位 */
+ rt_uint32_t rtr : 1; /* 远程帧标识位 */
+ rt_uint32_t mode : 1; /* 过滤表模式 */
+ rt_uint32_t mask; /* ID 掩码,0 表示对应的位不关心,1 表示对应的位必须匹配 */
+ rt_int32_t hdr; /* -1 表示不指定过滤表号,对应的过滤表控制块也不会被初始化,正数为过滤表号,对应的过滤表控制块会被初始化 */
+#ifdef RT_CAN_USING_HDR
+ /* 过滤表回调函数 */
+ rt_err_t (*ind)(rt_device_t dev, void *args , rt_int32_t hdr, rt_size_t size);
+ /* 回调函数参数 */
+ void *args;
+#endif /*RT_CAN_USING_HDR*/
+};
+```
+
+如果需要过滤的报文 ID 为 0x01 的标准数据帧,使用默认过滤表,则过滤表各个成员设置如下:
+```c
+struct rt_can_filter_item filter;
+/* 报文 ID */
+filter.id = 0x01;
+/* 标准格式 */
+filter.ide = 0x00;
+/* 数据帧 */
+filter.rtr = 0x00;
+/* 过滤表模式 */
+filter.mode = 0x01;
+/* 匹配 ID */
+filter.mask = 0x01;
+/* 使用默认过滤表 */
+filter.hdr = -1;
+```
+
+为了方便表示过滤表的各个成员变量的值, RT-Thread 系统提供了匹配过滤表的宏
+
+```c
+#define RT_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask,ind,args) \
+ {(id), (ide), (rtr), (mode), (mask), -1, (ind), (args)}
+```
+过滤表宏中各个位分别和过滤表结构体成员变量一一对应,只是使用的过滤表是默认的过滤表。
+
+则上述过滤信息使用过滤表的宏可以表示为
+
+```c
+RT_CAN_FILTER_ITEM_INIT(0x01, 0, 0, 1, 0x01, RT_NULL, RT_NULL);
+```
+
+当需要使用过滤表时还需要指定过滤表配置控制块的成员变量,过滤表的配置控制块成员变量的组成如下所示:
+
+```c
+struct rt_can_filter_config
+{
+ rt_uint32_t count; /* 过滤表数量 */
+ rt_uint32_t actived; /* 过滤表激活选项,1 表示初始化过滤表控制块,0 表示去初始化过滤表控制块 */
+ struct rt_can_filter_item *items; /* 过滤表指针,可指向一个过滤表数组 */
+};
+```
+
+设置硬件过滤表示例代码如下所示:
+
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+
+can_dev = rt_device_find(CAN_DEV_NAME);
+
+/* 以中断接收及发送模式打开 CAN 设备 */
+rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
+
+struct rt_can_filter_item items[1] =
+{
+ RT_CAN_FILTER_ITEM_INIT(0x01, 0, 0, 1, 0x01, RT_NULL, RT_NULL),
+ /* 过滤 ID 为 0x01,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
+};
+struct rt_can_filter_config cfg = {1, 1, items}; /* 一共有 1 个过滤表 */
+/* 设置硬件过滤表 */
+res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);
+```
+
+
+
+### 发送数据
+
+使用 CAN 设备发送数据,可以通过如下函数完成:
+
+```c
+rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
+```
+
+| 参数 | 描述 |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| pos | 写入数据偏移量,此参数 CAN 设备未使用 |
+| buffer | CAN 消息指针 |
+| size | CAN 消息大小 |
+| **返回** | —— |
+| 不为 0 | 实际发送的 CAN 消息大小 |
+| 0 | 发送失败 |
+
+CAN 消息原型如下所示:
+
+```c
+struct rt_can_msg
+{
+ rt_uint32_t id : 29; /* CAN ID, 标志格式 11 位,扩展格式 29 位 */
+ rt_uint32_t ide : 1; /* 扩展帧标识位 */
+ rt_uint32_t rtr : 1; /* 远程帧标识位 */
+ rt_uint32_t rsv : 1; /* 保留位 */
+ rt_uint32_t len : 8; /* 数据段长度 */
+ rt_uint32_t priv : 8; /* 报文发送优先级 */
+ rt_uint32_t hdr : 8; /* 硬件过滤表号 */
+ rt_uint32_t reserved : 8;
+ rt_uint8_t data[8]; /* 数据段 */
+};
+```
+
+使用 CAN 设备发送数据示例程序如下所示:
+
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+struct rt_can_msg msg = {0}; /* CAN 消息 */
+
+can_dev = rt_device_find(CAN_DEV_NAME);
+
+/* 以中断接收及发送模式打开 CAN 设备 */
+rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
+
+msg.id = 0x78; /* ID 为 0x78 */
+msg.ide = RT_CAN_STDID; /* 标准格式 */
+msg.rtr = RT_CAN_DTR; /* 数据帧 */
+msg.len = 8; /* 数据长度为 8 */
+/* 待发送的 8 字节数据 */
+msg.data[0] = 0x00;
+msg.data[1] = 0x11;
+msg.data[2] = 0x22;
+msg.data[3] = 0x33;
+msg.data[4] = 0x44;
+msg.data[5] = 0x55;
+msg.data[6] = 0x66;
+msg.data[7] = 0x77;
+/* 发送一帧 CAN 数据 */
+size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
+```
+
+### 设置接收回调函数
+
+可以通过如下函数来设置数据接收指示,当 CAN 收到数据时,通知上层应用线程有数据到达 :
+
+```c
+rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
+```
+
+| 参数 | 描述 |
+| -------- | ------------ |
+| dev | 设备句柄 |
+| rx_ind | 回调函数指针 |
+| dev | 设备句柄(回调函数参数)|
+| size | 缓冲区数据大小(回调函数参数)|
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+
+该函数的回调函数由调用者提供。CAN 设备在中断接收模式下,当 CAN 接收到一帧数据产生中断时,就会调用回调函数,并且会把此时缓冲区的数据大小放在 size 参数里,把 CAN 设备句柄放在 dev 参数里供调用者获取。
+
+一般情况下接收回调函数可以发送一个信号量或者事件通知 CAN 数据处理线程有数据到达。使用示例如下所示:
+
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+static rt_device_t can_dev; /* CAN 设备句柄 */
+struct rt_can_msg msg = {0}; /* CAN 消息 */
+
+/* 接收数据回调函数 */
+static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
+{
+ /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
+ rt_sem_release(&rx_sem);
+
+ return RT_EOK;
+}
+
+/* 设置接收回调函数 */
+rt_device_set_rx_indicate(can_dev, can_rx_call);
+```
+
+### 接收数据
+
+可调用如下函数读取 CAN 设备接收到的数据:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+```
+
+| 参数 | 描述 |
+| ------------------ | ---------------------------------------------- |
+| dev | 设备句柄 |
+| pos | 读取数据偏移量,此参数 CAN 设备未使用 |
+| buffer | CAN 消息指针,读取的数据将会被保存在缓冲区中 |
+| size | CAN 消息大小 |
+| **返回** | —— |
+| 不为 0 | CAN 消息大小 |
+| 0 | 失败 |
+
+> [!NOTE]
+> 注:接收数据时 CAN 消息的 hdr 参数必须要指定值,默认指定为 -1 就可以,表示从接收数据的 uselist 链表读取数据。也可以指定为硬件过滤表号的值,表示此次读取数据从哪一个硬件过滤表对应的消息链接读取数据,此时需要设置硬件过滤表的时候 hdr 有指定正确的过滤表号。如果设置硬件过滤表的时候 hdr 都为 -1,则读取数据的时候也要赋值为-1。
+
+CAN 使用中断接收模式并配合接收回调函数的使用示例如下所示:
+
+```c
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static rt_device_t can_dev; /* CAN 设备句柄 */
+struct rt_can_msg rxmsg = {0}; /* CAN 接收消息缓冲区 */
+
+/* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
+rxmsg.hdr = -1;
+
+/* 阻塞等待接收信号量 */
+rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
+/* 从 CAN 读取一帧数据 */
+rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
+```
+
+### 关闭 CAN 设备
+
+当应用程序完成 CAN 操作后,可以关闭 CAN 设备,通过如下函数完成:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| 参数 | 描述 |
+| ---------- | ---------------------------------- |
+| dev | 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+## CAN 设备使用示例
+
+示例代码的主要步骤如下所示:
+
+1. 首先查找 CAN 设备获取设备句柄。
+
+2. 初始化信号量,然后以中断接收及中断发送方式打开 CAN 设备。
+
+3. 创建读取数据线程。
+
+4. 发送一帧 CAN 数据。
+
+* 读取数据线程首先会设置接收回调函数,然后设置硬件过滤表,之后会等待信号量。当 CAN 设备接收到一帧数据时会触发中断并调用接收回调函数,此函数会发送信号量唤醒线程,此时线程会马上读取接收到的数据。
+
+* 此示例代码不局限于特定的 BSP,根据 BSP 注册的 CAN 设备,修改示例代码宏定义 CAN_DEV_NAME 对应的 CAN 设备名称即可运行。
+
+运行序列图如下图所示:
+
+
+
+程序运行起来后在命令行输入 `can_sample` 即可运行示例代码,后面数据为 CAN 设备接收到的数据:
+
+```c
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.1 build Jun 24 2019
+ 2006 - 2019 Copyright by rt-thread team
+msh >can_sample
+ID:486 0 11 22 33 0 23 4 86
+ID:111 0 11 22 33 0 23 1 11
+ID:555 0 11 22 33 0 23 5 55
+ID:211 0 11 22 33 0 23 2 11
+ID:344 0 11 22 33 0 23 3 44
+```
+
+可以使用 CAN 分析工具连接对应 CAN 设备收发数据,第一帧数据为 CAN 示例代码发送的 ID 为 0X78的数据, 效果如下图所示:
+
+
+
+```c
+/*
+ * 程序清单:这是一个 CAN 设备使用例程
+ * 例程导出了 can_sample 命令到控制终端
+ * 命令调用格式:can_sample can1
+ * 命令解释:命令第二个参数是要使用的 CAN 设备名称,为空则使用默认的 CAN 设备
+ * 程序功能:通过 CAN 设备发送一帧,并创建一个线程接收数据然后打印输出。
+*/
+
+#include
+#include "rtdevice.h"
+
+#define CAN_DEV_NAME "can1" /* CAN 设备名称 */
+
+static struct rt_semaphore rx_sem; /* 用于接收消息的信号量 */
+static rt_device_t can_dev; /* CAN 设备句柄 */
+
+/* 接收数据回调函数 */
+static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
+{
+ /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
+ rt_sem_release(&rx_sem);
+
+ return RT_EOK;
+}
+
+static void can_rx_thread(void *parameter)
+{
+ int i;
+ rt_err_t res;
+ struct rt_can_msg rxmsg = {0};
+
+ /* 设置接收回调函数 */
+ rt_device_set_rx_indicate(can_dev, can_rx_call);
+
+#ifdef RT_CAN_USING_HDR
+ struct rt_can_filter_item items[5] =
+ {
+ RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
+ RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */
+ RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 1, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */
+ RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std,match ID:0x486,hdr 为 - 1 */
+ {0x555, 0, 0, 1, 0x7ff, 7,} /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */
+ };
+ struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */
+ /* 设置硬件过滤表 */
+ res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);
+ RT_ASSERT(res == RT_EOK);
+#endif
+
+ while (1)
+ {
+ /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
+ rxmsg.hdr = -1;
+ /* 阻塞等待接收信号量 */
+ rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
+ /* 从 CAN 读取一帧数据 */
+ rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
+ /* 打印数据 ID 及内容 */
+ rt_kprintf("ID:%x", rxmsg.id);
+ for (i = 0; i < 8; i++)
+ {
+ rt_kprintf("%2x", rxmsg.data[i]);
+ }
+
+ rt_kprintf("\n");
+ }
+}
+
+int can_sample(int argc, char *argv[])
+{
+ struct rt_can_msg msg = {0};
+ rt_err_t res;
+ rt_size_t size;
+ rt_thread_t thread;
+ char can_name[RT_NAME_MAX];
+
+ if (argc == 2)
+ {
+ rt_strncpy(can_name, argv[1], RT_NAME_MAX);
+ }
+ else
+ {
+ rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);
+ }
+ /* 查找 CAN 设备 */
+ can_dev = rt_device_find(can_name);
+ if (!can_dev)
+ {
+ rt_kprintf("find %s failed!\n", can_name);
+ return RT_ERROR;
+ }
+
+ /* 初始化 CAN 接收信号量 */
+ rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
+
+ /* 以中断接收及发送方式打开 CAN 设备 */
+ res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
+ RT_ASSERT(res == RT_EOK);
+ /* 创建数据接收线程 */
+ thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10);
+ if (thread != RT_NULL)
+ {
+ rt_thread_startup(thread);
+ }
+ else
+ {
+ rt_kprintf("create can_rx thread failed!\n");
+ }
+
+ msg.id = 0x78; /* ID 为 0x78 */
+ msg.ide = RT_CAN_STDID; /* 标准格式 */
+ msg.rtr = RT_CAN_DTR; /* 数据帧 */
+ msg.len = 8; /* 数据长度为 8 */
+ /* 待发送的 8 字节数据 */
+ msg.data[0] = 0x00;
+ msg.data[1] = 0x11;
+ msg.data[2] = 0x22;
+ msg.data[3] = 0x33;
+ msg.data[4] = 0x44;
+ msg.data[5] = 0x55;
+ msg.data[6] = 0x66;
+ msg.data[7] = 0x77;
+ /* 发送一帧 CAN 数据 */
+ size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
+ if (size == 0)
+ {
+ rt_kprintf("can dev write data failed!\n");
+ }
+
+ return res;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(can_sample, can device sample);
+```
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-data-frame.png b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-data-frame.png
new file mode 100644
index 0000000000000000000000000000000000000000..09e2efd55eaef1b5ec199ef9d331980b9544c75d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-data-frame.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-init.png b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-init.png
new file mode 100644
index 0000000000000000000000000000000000000000..927801fc55dccd894d207157363b22b5934eea44
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-init.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-int b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-int
new file mode 100644
index 0000000000000000000000000000000000000000..a7ff6cfc634004df451b71753fad0e8f5babc11d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-int
@@ -0,0 +1,19 @@
+@startuml
+
+participant Ӧó
+participant ISR
+participant CANӲ
+
+Ӧó->Ӧó: ʼź
+Ӧó->Ӧó: ݴ߳
+Ӧó->Ӧó: ýջص
+Ӧó->Ӧó: һ֡ CAN
+Ӧó->Ӧó: ݴ߳ȴź
+
+CANӲ->ISR: յһ֡ݲCAN ж
+
+ISR->Ӧó: ISRݷ뻺,ڽջصзź߳
+
+Ӧó->Ӧó: ݴ̻߳ȡźӻȡһ֡
+
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-link.png b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-link.png
new file mode 100644
index 0000000000000000000000000000000000000000..d027c50a8cb90380dc6f9304142448d41c0acb2e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-link.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-test.png b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-test.png
new file mode 100644
index 0000000000000000000000000000000000000000..4f1f8e3a06aa8757609314436fa119f94392322f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can-test.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..622f618bac9e129d1e0afec15409a720e77dd9a8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/can/figures/can.vsdx differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/crypto/crypto.md b/rt-thread-version/rt-thread-standard/programming-manual/device/crypto/crypto.md
new file mode 100644
index 0000000000000000000000000000000000000000..2252444e94c4b5d3a1046673b062a33726ba321f
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/crypto/crypto.md
@@ -0,0 +1,1336 @@
+# CRYPTO 设备
+
+加解密(Encryption/Decryption)是一种文件、消息加密、解密技术。这种技术随着物联网快速发展,用户的正常工作中对数据的加解密需求有着强烈的需求。
+
+为保证物联网设备的信息安全,软件层面引入了 TLS 安全传输层协议,同时硬件芯片上也逐渐添加安全相关的加解密模块,甚至出现了专为安全设计的安全芯片。芯片上的硬件安全模块相比纯软件实现的安全算法,拥有更快的运算速度,更小的资源占用。但大多数物联网设备上仍在使用纯软件的安全算法。其中最重要的一个原因,就是硬件接口不一,种类繁杂,软件对接起来比较困难。
+
+因此 RT-Thread 推出了 hwcrypto 硬件加解密驱动框架,并对接了常见的安全传输套件。只要硬件支持加解密模块,就能直接使用基于硬件加解密的安全传输套件,传输速度提升数倍。
+
+## CRYPTO 简介
+
+### 框架简介
+
+hwcrypto 是一个硬件加解密设备驱动框架。主要由硬件加解密驱动抽象层以及各种加解密 API 接口两部分构成。对于上层应用,可对接安全套件或直接使用,使用方式十分灵活。对于驱动,需要对接的接口少,功能单一,驱动开发简单快捷。下图是 Crypto 框架层次图:
+
+
+
+主要**特性**如下:
+
+- **设计轻薄,运行高效**
+
+硬件加解密驱动的最重要的一个功能就是接口转换,实现接口统一,方便上层应用使用硬件加解密。所以它被设计的十分轻薄。有着极低资源占用,ROM < 0.8K / RAM < 0.2K。
+
+加解密在运行速度上,也有着很高的要求。所以频繁调用的代码,细致考虑,把运行过程的步骤降到最少,让性能损失降到最小,如同直接操作硬件寄存器一般迅速。
+
+- **考虑周全,使用简单**
+
+在 API 设计上,从简单易用,功能齐全两个维度出发。首先是用户直接使用硬件加解密API,要求上手简单, 使用容易。为此前期针对众多软件接口进行了评估。亲身使用测试,最终定义出一套功能齐全,接口简单的 API。
+
+满足用户的使用需求的同时,在安全传输套件的对接上,也做了许多考虑。专门为安全传输套件增加了API。最终这套API用户使用或是对接安全传输套件,都能游刃有余。
+
+- **完全兼容,通用性强**
+
+驱动对接的接口设计上,也精心设计。前期对多家硬件厂商的加解密外设根据功能进行分类,进行分析整理,提取出一套功能单一,参数全面的驱动接口。该驱动接口可完全适配常规 MCU 加解密外设。现目前已在多个平台上做验证,例如联盛德 W60X 系列,STM32 系列等。
+
+### 功能简介
+
+硬件加解密框架目前已经支持 AES/DES/3DES/RC4/SHA1/SHA2/MD5/CRC/RNG/BIGNUM 等加解密相关的接口。
+
+将上述加解密算法按照不同的类型分成如下几个大类,每一类都有丰富的 API 可供使用。目前已经支持的类型如下:
+
+- hash: 散列算法
+- symmetric: 对称加解密算法
+- gcm: GMAC 消息认证码
+- crc: CRC 冗余校验
+- rng: 随机数发生器
+- bignum: 大数运算
+
+## hash 算法
+
+hash 算法是把任意长度的输入通过散列算法变换成固定长度的输出,是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。
+
+### 访问 hash 算法设备
+
+应用程序通过 RT-Thread 提供的 hash 算法设备管理接口来访问 hash 算法设备硬件,相关接口如下所示:
+
+| 函数 | 描述 |
+|-----------------------------|--------------------|
+| rt_hwcrypto_hash_create() | 创建 hash 上下文 |
+| rt_hwcrypto_hash_destroy() | 释放上下文 |
+| rt_hwcrypto_hash_finish() | 计算最终 hash 值 |
+| rt_hwcrypto_hash_update() | 处理一包数据 |
+| rt_hwcrypto_hash_cpy() | 复制上下文 |
+| rt_hwcrypto_hash_reset() | 重置上下文 |
+| rt_hwcrypto_hash_set_type() | 设置 hash 算法类型 |
+
+### 创建 hash 上下文
+
+应用程序根据 hash 设备的句柄,创建 hash 上下文,如下所示:
+
+```c
+struct rt_hwcrypto_ctx *rt_hwcrypto_hash_create(struct rt_hwcrypto_device *device,
+ hwcrypto_type type);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| device | 加解密设备句柄 |
+| type | hash 算法类型 |
+| **返回** | —— |
+| NULL | 失败 |
+| 其他 | 设备对象 |
+
+hash 算法常用类型及子类型如下
+
+```c
+ /* HASH Type */
+ HWCRYPTO_TYPE_MD5 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< MD5 */
+ HWCRYPTO_TYPE_SHA1 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< SHA1 */
+ HWCRYPTO_TYPE_SHA2 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< SHA2 */
+
+ /* SHA2 Subtype */
+ HWCRYPTO_TYPE_SHA224 = HWCRYPTO_TYPE_SHA2 | (0x01 << 8),
+ HWCRYPTO_TYPE_SHA256 = HWCRYPTO_TYPE_SHA2 | (0x02 << 8),
+ HWCRYPTO_TYPE_SHA384 = HWCRYPTO_TYPE_SHA2 | (0x03 << 8),
+ HWCRYPTO_TYPE_SHA512 = HWCRYPTO_TYPE_SHA2 | (0x04 << 8),
+
+```
+
+如果创建 MD5 类型的 hash 算法,使用示例如下
+
+```c
+ struct rt_hwcrypto_ctx *ctx;
+
+ ctx = rt_hwcrypto_hash_create(rt_hwcrypto_dev_default(), HWCRYPTO_TYPE_MD5);
+```
+
+- rt_hwcrypto_dev_default()函数原型为 `struct rt_hwcrypto_device *rt_hwcrypto_dev_default(void)`,返回默认的硬件加解密设备句柄。
+
+### 释放上下文
+
+应用程序根据 hash 的上下文,删除该上下文,并且释放资源,如下所示:
+
+```c
+void rt_hwcrypto_hash_destroy(struct rt_hwcrypto_ctx *ctx);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| ctx | 上下文 |
+| **返回** | —— |
+
+### 计算最终 hash 值
+
+应用程序根据 hash 的上下文,可以输出最后的 hash 值,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_hash_finish(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *output, rt_size_t length);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| ctx | 上下文 |
+| output | 输出数据 |
+| length | 数据长度 |
+| **返回** | —— |
+| RT_EOK | 计算成功 |
+| 其他 | 失败 |
+
+### 计算一包数据
+
+应用程序根据 hash 的上下文,输入一包数据,计算 hash 值,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_hash_update(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *input, rt_size_t length);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| ctx | 上下文 |
+| input | 输入数据 |
+| length | 输入数据长度 |
+| **返回** | —— |
+| RT_EOK | 计算成功 |
+| 其他 | 失败 |
+
+使用实例如下:
+
+```c
+int main(void)
+{
+ rt_uint8_t buf_in[32];
+ int i;
+ struct rt_hwcrypto_ctx *ctx;
+
+ /* 填充测试数据 */
+ for (i = 0; i < sizeof(buf_in); i++)
+ {
+ buf_in[i] = (rt_uint8_t)i;
+ }
+ /* 创建一个 SHA1/MD5 类型的上下文 */
+ ctx = rt_hwcrypto_hash_create(rt_hwcrypto_dev_default(), type);
+
+ /* 将输入数据进行 hash 运算 */
+ rt_hwcrypto_hash_update(ctx, in, 32);
+ /* 获得运算结果 */
+ rt_hwcrypto_hash_finish(ctx, out, 32);
+ /* 删除上下文,释放资源 */
+ rt_hwcrypto_hash_destroy(ctx);
+}
+```
+
+### 复制上下文
+
+应用程序根据源 hash 的上下文,复制到目标 hash 的上下文中,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_hash_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src);
+```
+
+| **参数** | **描述** |
+|----------|------------|
+| des | 目标上下文 |
+| src | 源上下文 |
+| **返回** | —— |
+| RT_EOK | 计算成功 |
+| 其他 | 失败 |
+
+### 重置上下文
+
+应用程序根据 hash 的上下文,重置 hash 上下文文本,如下所示:
+
+```c
+void rt_hwcrypto_hash_reset(struct rt_hwcrypto_ctx *ctx);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| ctx | 上下文 |
+| **返回** | —— |
+
+- 没有释放上下文之前,只有重置后才可以计算下一包数据
+
+### 设置 hash 算法类型
+
+应用程序根据 hash 的上下文,设置 hash 算法类型,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_hash_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type);
+```
+
+| **参数** | **描述** |
+|----------|---------------|
+| ctx | 上下文 |
+| type | hash 算法类型 |
+| **返回** | —— |
+| RT_EOK | 计算成功 |
+| 其他 | 失败 |
+
+- 常用 hash 算法类型,参考创建 hash 上下文中类型介绍
+- 设置类型仅在数据运算之前有效,运算中途更改类型,将导致运算结果错误
+
+## 对称加解密算法
+
+对称加解密指加密和解密使用相同密钥的加密算法,所以也称这种加密算法为秘密密钥算法或单密钥算法。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要。
+
+### 访问对称加解密设备
+
+应用程序通过 RT-Thread 提供的 symmetric 设备管理接口来访问 symmetric 算法设备硬件,相关接口如下所示:
+
+| 函数 | 描述 |
+|-----------------------------------|--------------------------------|
+| rt_hwcrypto_symmetric_create() | 创建对称加解密上下文 |
+| rt_hwcrypto_symmetric_destroy() | 释放上下文 |
+| rt_hwcrypto_symmetric_crypt() | 加解密操作 |
+| rt_hwcrypto_symmetric_setkey() | 设置加解密密钥 |
+| rt_hwcrypto_symmetric_getkey() | 获取加解密密钥 |
+| rt_hwcrypto_symmetric_setiv() | 设置对称加解密初始化向量 |
+| rt_hwcrypto_symmetric_getiv() | 获取对称加解密初始化向量 |
+| rt_hwcrypto_symmetric_set_ivoff() | 设置对称加解密初始化向量偏移值 |
+| rt_hwcrypto_symmetric_get_ivoff() | 获取对称加解密初始化向量偏移值 |
+| rt_hwcrypto_symmetric_cpy() | 复制上下文 |
+| rt_hwcrypto_symmetric_reset() | 重置上下文 |
+| rt_hwcrypto_symmetric_set_type() | 设置加解密类型 |
+
+### 创建对称加解密上下文
+
+应用程序根据对称加解密设备的句柄,创建对称加解密上下文,如下所示:
+
+```c
+struct rt_hwcrypto_ctx *rt_hwcrypto_symmetric_create(struct rt_hwcrypto_device *device, hwcrypto_type type);
+```
+
+| **参数** | **描述** |
+|----------|--------------------|
+| device | 加解密设备句柄 |
+| type | 对称加解密算法类型 |
+| **返回** | —— |
+| NULL | 失败 |
+| 其他 | 设备对象 |
+
+对称加解密算法常用类型及子类型如下
+
+```c
+ /* symmetric Type */
+ HWCRYPTO_TYPE_AES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< AES */
+ HWCRYPTO_TYPE_DES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< DES */
+ HWCRYPTO_TYPE_3DES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< 3DES */
+ HWCRYPTO_TYPE_RC4 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< RC4 */
+ HWCRYPTO_TYPE_GCM = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< GCM */
+
+ /* AES Subtype */
+ HWCRYPTO_TYPE_AES_ECB = HWCRYPTO_TYPE_AES | (0x01 << 8),
+ HWCRYPTO_TYPE_AES_CBC = HWCRYPTO_TYPE_AES | (0x02 << 8),
+ HWCRYPTO_TYPE_AES_CFB = HWCRYPTO_TYPE_AES | (0x03 << 8),
+ HWCRYPTO_TYPE_AES_CTR = HWCRYPTO_TYPE_AES | (0x04 << 8),
+ HWCRYPTO_TYPE_AES_OFB = HWCRYPTO_TYPE_AES | (0x05 << 8),
+
+ /* DES Subtype */
+ HWCRYPTO_TYPE_DES_ECB = HWCRYPTO_TYPE_DES | (0x01 << 8),
+ HWCRYPTO_TYPE_DES_CBC = HWCRYPTO_TYPE_DES | (0x02 << 8),
+
+ /* 3DES Subtype */
+ HWCRYPTO_TYPE_3DES_ECB = HWCRYPTO_TYPE_3DES | (0x01 << 8),
+ HWCRYPTO_TYPE_3DES_CBC = HWCRYPTO_TYPE_3DES | (0x02 << 8),
+```
+
+创建 AES-CBC 加密,如下所示
+
+```c
+ struct rt_hwcrypto_ctx *ctx;
+
+ ctx = rt_hwcrypto_symmetric_create(rt_hwcrypto_dev_default(), HWCRYPTO_TYPE_AES_CBC);
+```
+
+### 释放上下文
+
+应用程序根据对称加解密的上下文,删除该上下文,并且释放资源,如下所示:
+
+```c
+void rt_hwcrypto_symmetric_destroy(struct rt_hwcrypto_ctx *ctx);
+```
+
+| **参数** | **描述** |
+|----------|------------|
+| ctx | 上下文句柄 |
+| **返回** | —— |
+
+### 加解密数据
+
+应用程序根据对称加解密的上下文、加解密模式、输入数据及长度,可以输出最后计算后的值,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_symmetric_crypt(struct rt_hwcrypto_ctx *ctx, hwcrypto_mode mode, rt_size_t length, const rt_uint8_t *in, rt_uint8_t *out);
+
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| ctx | 上下文句柄 |
+| mode | 加密或者解密 |
+| length | 数据长度 |
+| in | 输入数据 |
+| out | 输出数据 |
+| **返回** | —— |
+| RT_EOK | 计算成功 |
+| 其他 | 失败 |
+
+加密或者解密模式如下
+
+```c
+typedef enum
+{
+ HWCRYPTO_MODE_ENCRYPT = 0x1, /**< Encryption operations */
+ HWCRYPTO_MODE_DECRYPT = 0x2, /**< Decryption operations */
+ HWCRYPTO_MODE_UNKNOWN = 0x7fffffff, /**< Unknown */
+} hwcrypto_mode;
+```
+
+### 设置加解密密钥
+
+应用程序根据对称加解密的上下文、密钥与密钥及长度,设置加解密密钥,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_symmetric_setkey(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *key, rt_uint32_t bitlen);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| ctx | 上下文句柄 |
+| key | 输入密钥 |
+| bitlen | 密钥长度 |
+| **返回** | —— |
+| RT_EOK | 设置密钥成功 |
+| 其他 | 失败 |
+
+### 获取密钥
+
+应用程序根据对称加解密的上下文、密钥、长度,获取密钥长度,如下所示:
+
+```c
+int rt_hwcrypto_symmetric_getkey(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *key, rt_uint32_t bitlen);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| ctx | 目标上下文 |
+| key | 密钥 |
+| bitlen | 密钥长度 |
+| **返回** | —— |
+| int | 复制密钥长度 |
+| 其他 | 失败 |
+
+### 设置对称加解密初始化向量
+
+应用程序根据对称加解密的上下文、初始化向量与向量长度,设置对称加解密初始化向量,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_symmetric_setiv(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *iv, rt_size_t len);
+```
+
+| **参数** | **描述** |
+|----------|------------------|
+| ctx | 上下文 |
+| iv | 初始化向量 |
+| len | 初始化向量的长度 |
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+| 其他 | 失败 |
+
+### 获取对称加解密初始化向量
+
+应用程序根据对称加解密的上下文,获取对称加解密初始化向量,如下所示:
+
+```c
+int rt_hwcrypto_symmetric_getiv(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *iv, rt_size_t len);
+```
+
+| **参数** | **描述** |
+|----------|------------------|
+| ctx | 上下文 |
+| iv | 将获取初始化向量 |
+| len | 初始化向量的长度 |
+| **返回** | —— |
+| int | 获取成功的长度 |
+| 其他 | 失败 |
+
+### 设置对称加解密初始化向量偏移
+
+应用程序根据对称加解密的上下文、初始化向量偏移值,设置对称加解密初始化向量的偏移值,如下所示:
+
+```c
+void rt_hwcrypto_symmetric_set_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t iv_off);
+```
+
+| **参数** | **描述** |
+|----------|--------------------|
+| ctx | 上下文 |
+| iv_off | 初始化向量的偏移值 |
+| **返回** | —— |
+
+### 获取对称加解密初始化向量偏移值
+
+应用程序根据对称加解密的上下文,获取对称加解密初始化向量的偏移值,如下所示:
+
+```c
+void rt_hwcrypto_symmetric_get_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t *iv_off);
+```
+
+| **参数** | **描述** |
+|----------|----------------------|
+| ctx | 上下文 |
+| iv_off | 初始化向量的偏移指针 |
+| **返回** | —— |
+
+### 复制对称加解密上下文
+
+应用程序根据源对称加解密的上下文,复制到目的上下文中,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_symmetric_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src);
+```
+
+| **参数** | **描述** |
+|----------|------------|
+| des | 目标上下文 |
+| src | 源上下文 |
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+| 其他 | 失败 |
+
+### 重置对称加解密上下文
+
+应用程序根据对称加解密的上下文,重置上下文,如下所示:
+
+```c
+void rt_hwcrypto_symmetric_reset(struct rt_hwcrypto_ctx *ctx);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| des | 上下文 |
+| **返回** | —— |
+
+### 设置加解密类型
+
+应用程序根据对称加解密的上下文,设置加密或者解密,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_symmetric_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type);
+```
+
+| **参数** | **描述** |
+|----------|------------|
+| ctx | 上下文 |
+| type | 加解密类型 |
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+| 其他 | 失败 |
+
+- 常用加解密类型,参考创建对称加解密上下文中类型介绍。
+
+采用应用 AES-CBC 进行加密,应用举例如下:
+
+```c
+/* 加密密钥 */
+static const rt_uint8_t key[16] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF};
+
+static void hw_aes_cbc(const rt_uint8_t in[32], rt_uint8_t out[32], hwcrypto_mode mode);
+
+int main(void)
+{
+ rt_uint8_t buf_in[32];
+ rt_uint8_t buf_out[32];
+ int i;
+
+ /* 填充测试数据 */
+ for (i = 0; i < sizeof(buf_in); i++)
+ {
+ buf_in[i] = (rt_uint8_t)i;
+ }
+
+ memset(buf_out, 0, sizeof(buf_out));
+ /* 对测试数据进行加密 */
+ hw_aes_cbc(buf_in, buf_out, HWCRYPTO_MODE_ENCRYPT);
+}
+
+static void hw_aes_cbc(const rt_uint8_t in[32], rt_uint8_t out[32], hwcrypto_mode mode)
+{
+ struct rt_hwcrypto_ctx *ctx;
+
+ /* 创建一个 AES-CBC 模式的上下文 */
+ ctx = rt_hwcrypto_symmetric_create(rt_hwcrypto_dev_default(), HWCRYPTO_TYPE_AES_CBC);
+ if (ctx == RT_NULL)
+ {
+ LOG_E("create AES-CBC context err!");
+ return;
+ }
+ /* 设置 AES-CBC 加密密钥 */
+ rt_hwcrypto_symmetric_setkey(ctx, key, 128);
+ /* 执行 AES-CBC 加密/解密 */
+ rt_hwcrypto_symmetric_crypt(ctx, mode, 32, in, out);
+ /* 删除上下文,释放资源 */
+ rt_hwcrypto_symmetric_destroy(ctx);
+}
+
+```
+
+## gcm 消息认证
+
+gcm 消息认证可以提供对消息的加密和完整性校验,也能够对其他附加消息的真实性进行检验。
+
+### 访问 gcm 设备
+
+应用程序通过 RT-Thread 提供的 gcm 设备管理接口来访问 gcm 算法设备硬件,相关接口如下所示:
+
+| 函数 | 描述 |
+|-----------------------------|--------------------|
+| rt_hwcrypto_gcm_create() | 创建上下文 |
+| rt_hwcrypto_gcm_destroy() | 释放上下文 |
+| rt_hwcrypto_gcm_start() | 传入附加值 |
+| rt_hwcrypto_gcm_finish() | 生成消息认证码 |
+| rt_hwcrypto_gcm_crypt() | 进行加解密 |
+| rt_hwcrypto_gcm_setkey() | 设置密钥 |
+| rt_hwcrypto_gcm_getkey() | 获取密钥 |
+| rt_hwcrypto_gcm_setiv() | 设置初始化向量 |
+| rt_hwcrypto_gcm_getiv() | 获取初始化向量 |
+| rt_hwcrypto_gcm_set_ivoff() | 设置初始化向量偏移 |
+| rt_hwcrypto_gcm_get_ivoff() | 获取初始化向量偏移 |
+| rt_hwcrypto_gcm_cpy() | 复制上下文 |
+| rt_hwcrypto_gcm_reset() | 重置上下文 |
+
+### 创建 gcm 上下文
+
+应用程序根据 gcm 消息认证的句柄,创建对称加解密上下文,如下所示:
+
+```c
+struct rt_hwcrypto_ctx *rt_hwcrypto_gcm_create(struct rt_hwcrypto_device *device,
+ hwcrypto_type crypt_type);
+```
+
+| **参数** | **描述** |
+|------------|----------------|
+| device | 加解密设备句柄 |
+| crypt_type | 加解密算法类型 |
+| **返回** | —— |
+| NULL | 失败 |
+| 其他 | 设备对象 |
+
+gcm 常用类型如下
+
+```c
+ /* symmetric Type */
+ HWCRYPTO_TYPE_AES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< AES */
+ HWCRYPTO_TYPE_DES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< DES */
+ HWCRYPTO_TYPE_3DES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< 3DES */
+ HWCRYPTO_TYPE_RC4 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< RC4 */
+ HWCRYPTO_TYPE_GCM = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< GCM */
+
+ /* AES Subtype */
+ HWCRYPTO_TYPE_AES_ECB = HWCRYPTO_TYPE_AES | (0x01 << 8),
+ HWCRYPTO_TYPE_AES_CBC = HWCRYPTO_TYPE_AES | (0x02 << 8),
+ HWCRYPTO_TYPE_AES_CFB = HWCRYPTO_TYPE_AES | (0x03 << 8),
+ HWCRYPTO_TYPE_AES_CTR = HWCRYPTO_TYPE_AES | (0x04 << 8),
+ HWCRYPTO_TYPE_AES_OFB = HWCRYPTO_TYPE_AES | (0x05 << 8),
+
+ /* DES Subtype */
+ HWCRYPTO_TYPE_DES_ECB = HWCRYPTO_TYPE_DES | (0x01 << 8),
+ HWCRYPTO_TYPE_DES_CBC = HWCRYPTO_TYPE_DES | (0x02 << 8),
+
+ /* 3DES Subtype */
+ HWCRYPTO_TYPE_3DES_ECB = HWCRYPTO_TYPE_3DES | (0x01 << 8),
+ HWCRYPTO_TYPE_3DES_CBC = HWCRYPTO_TYPE_3DES | (0x02 << 8),
+```
+
+### 释放上下文
+
+应用程序根据 gcm 的上下文,删除该上下文,并且释放资源,如下所示:
+
+```c
+void rt_hwcrypto_gcm_destroy(struct rt_hwcrypto_ctx *ctx);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| ctx | 上下文 |
+| **返回** | —— |
+
+### 传入附加值
+
+应用程序根据 gcm 的上下文,传入附加值初值进行加密,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_start(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *add,
+ rt_size_t add_len);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| ctx | 上下文 |
+| add | 附加消息 |
+| add_len | 附加消息长度 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 生成消息认证码
+
+应用程序根据 gcm 的上下文,对数据进行加密之后,输出 tag 值与长度,作为完整性与真实性检验认证,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_finish(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *tag, rt_size_t tag_len);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| ctx | 上下文 |
+| tag | 消息认证码 |
+| tag_len | 消息认证码长度 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 进行加解密
+
+应用程序根据 gcm 的上下文、加解密模式、数据长度以及输入数据后,对数据进行加密之后,输出数据缓存区,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_crypt(struct rt_hwcrypto_ctx *ctx, hwcrypto_mode mode,
+ rt_size_t length, const rt_uint8_t *in, rt_uint8_t *out);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| ctx | 上下文 |
+| mode | 加解密模式 |
+| length | 输入数据长度 |
+| in | 输入数据 |
+| out | 输出数据 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 设置密钥
+
+应用程序根据 gcm 的上下文、加解密密钥与长度,设置密钥,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_setkey(struct rt_hwcrypto_ctx *ctx,
+ const rt_uint8_t *key, rt_uint32_t bitlen);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| ctx | 上下文 |
+| key | 密钥 |
+| bitlen | 密钥长度 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 获取密钥
+
+应用程序根据 gcm 的上下文、加解密密钥与长度,获取密钥,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_getkey(struct rt_hwcrypto_ctx *ctx,
+ rt_uint8_t *key, rt_uint32_t bitlen);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| ctx | 上下文 |
+| key | 密钥 |
+| bitlen | 密钥长度 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 设置初始化向量
+
+应用程序根据 gcm 的上下文、初始化向量与长度,设置初始化向量,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_setiv(struct rt_hwcrypto_ctx *ctx,
+ const rt_uint8_t *iv, rt_size_t len);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| ctx | 上下文 |
+| iv | 初始化向量 |
+| len | 初始化向量长度 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 获取初始化向量
+
+应用程序根据 gcm 的上下文、初始化向量与长度,获取初始化向量,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_getiv(struct rt_hwcrypto_ctx *ctx,
+ rt_uint8_t *iv, rt_size_t len);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| ctx | 上下文 |
+| iv | 初始化向量 |
+| len | 初始化向量长度 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 设置初始化向量偏移
+
+应用程序根据 gcm 的上下文、初始化向量偏移,设置初始化向量偏移,如下所示:
+
+```c
+void rt_hwcrypto_gcm_set_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t iv_off);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| ctx | 上下文 |
+| iv_off | 初始化向量偏移 |
+| **返回** | —— |
+
+### 获取初始化向量偏移
+
+应用程序根据 gcm 的上下文、初始化向量偏移,获取初始化向量偏移,如下所示:
+
+```c
+void rt_hwcrypto_gcm_get_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t iv_off);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| ctx | 上下文 |
+| iv_off | 初始化向量偏移 |
+| **返回** | —— |
+
+### 复制上下文
+
+应用程序根据 gcm 的上下文,复制到目标上下文中,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_gcm_cpy(struct rt_hwcrypto_ctx *des,
+ const struct rt_hwcrypto_ctx *src);
+```
+
+| **参数** | **描述** |
+|----------|------------|
+| des | 目标上下文 |
+| src | 源上下文 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+## CRC 冗余校验
+
+循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。
+
+### 访问 CRC 设备
+
+应用程序通过 RT-Thread 提供的 CRC 设备管理接口来访问 CRC 算法设备硬件,相关接口如下所示:
+
+| 函数 | 描述 |
+|---------------------------|--------------------|
+| rt_hwcrypto_crc_create() | 创建 CRC 上下文 |
+| rt_hwcrypto_crc_destroy() | 释放上下文 |
+| rt_hwcrypto_crc_update() | 计算一包数据 |
+| rt_hwcrypto_crc_cfg() | 设置上下文计算数据 |
+
+### 创建 CRC 上下文
+
+应用程序根据 CRC 设备的句柄,创建 CRC 上下文,如下所示:
+
+```c
+struct rt_hwcrypto_ctx *rt_hwcrypto_crc_create(struct rt_hwcrypto_device *device,
+ hwcrypto_crc_mode mode);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| device | 将要设置的设备 |
+| mode | CRC 计算模式 |
+| **返回** | —— |
+| NULL | 失败 |
+| 其他 | 设备对象 |
+
+常用的 CRC 计算模式如下:
+
+```c
+typedef enum
+{
+ HWCRYPTO_CRC_CUSTOM, /**< Custom CRC mode */
+ HWCRYPTO_CRC_CRC8, /**< poly : 0x07 */
+ HWCRYPTO_CRC_CRC16, /**< poly : 0x8005 */
+ HWCRYPTO_CRC_CRC32, /**< poly : 0x04C11DB7 */
+ HWCRYPTO_CRC_CCITT, /**< poly : 0x1021 */
+ HWCRYPTO_CRC_DNP, /**< poly : 0x3D65 */
+} hwcrypto_crc_mode;
+```
+
+这些模式分别对应不同的计算多项式,如备注所示。
+
+### 释放上下文
+
+应用程序根据 CRC 的上下文,删除该上下文,并且释放资源,如下所示:
+
+```c
+void rt_hwcrypto_crc_destroy(struct rt_hwcrypto_ctx *ctx);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| ctx | 上下文 |
+| **返回** | —— |
+
+### 计算数据
+
+应用程序根据 CRC 的上下文、输入数据及其长度,计算 CRC 计算结果,如下所示:
+
+```c
+rt_uint32_t rt_hwcrypto_crc_update(struct rt_hwcrypto_ctx *ctx,
+ const rt_uint8_t *input, rt_size_t length);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| ctx | 上下文 |
+| input | 输入数据 |
+| length | 输入数据长度 |
+| **返回** | —— |
+| uint32_t | 计算结果 |
+| 0 | 失败 |
+
+### 设置配置
+
+应用程序根据 CRC 的上下文与配置信息,并且释放资源,如下所示:
+
+```c
+void rt_hwcrypto_crc_cfg(struct rt_hwcrypto_ctx *ctx,
+ struct hwcrypto_crc_cfg *cfg);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| ctx | 上下文 |
+| cfg | 加解密配置参数 |
+| **返回** | —— |
+
+CRC 配置结构体如下
+
+```c
+struct hwcrypto_crc_cfg
+{
+ rt_uint32_t last_val; /**< Last CRC value cache */
+ rt_uint32_t poly; /**< CRC polynomial */
+ rt_uint16_t width; /**< CRC value width */
+ rt_uint32_t xorout; /**< Result XOR Value */
+ rt_uint16_t flags; /**< Input or output data reverse. CRC_FLAG_REFIN or CRC_FLAG_REFOUT */
+};
+```
+
+计算 0 - 7 的数据校验,并且实现如下配置
+
+```c
+ struct hwcrypto_crc_cfg cfg =
+ {
+ .last_val = 0xFFFFFFFF,
+ .poly = 0x04C11DB7,
+ .width = 32,
+ .xorout = 0x00000000,
+ .flags = 0,
+ };
+```
+
+求取校验值,具体程序如下
+
+```c
+int main(void)
+{
+ rt_uint8_t temp[] = {0,1,2,3,4,5,6,7};
+ struct rt_hwcrypto_ctx *ctx;
+ rt_uint32_t result = 0;
+ struct hwcrypto_crc_cfg cfg =
+ {
+ .last_val = 0xFFFFFFFF,
+ .poly = 0x04C11DB7,
+ .width = 32,
+ .xorout = 0x00000000,
+ .flags = 0,
+ };
+
+ /* 创建设备的上下文 */
+ ctx = rt_hwcrypto_crc_create(rt_hwcrypto_dev_default(), HWCRYPTO_CRC_CRC32);/* 设置 CRC 配置 */
+ rt_hwcrypto_crc_cfg(ctx, &cfg);
+ /* 输入数据,获取其 CRC 计算值 */
+ result = rt_hwcrypto_crc_update(ctx, temp, sizeof(temp));
+ /* 打印结果 */
+ rt_kprintf("result: %x \n", result);
+ /* 释放 ctx */
+ rt_hwcrypto_crc_destroy(ctx);
+}
+```
+
+## 随机数发生器
+
+随机数发生(Random Numeral Generator,RNG)器所产生的数据,后面的数与前面的数毫无关系。
+
+### 访问 RNG 设备
+
+应用程序通过 RT-Thread 提供的 RNG 设备管理接口来访问 RNG 算法设备硬件,相关接口如下所示:
+
+| 函数 | 描述 |
+|--------------------------|--------------------|
+| rt_hwcrypto_rng_update() | 获取默认设备随机数 |
+
+### 获取默认设备随机数
+
+应用程序根据 RNG 默认设备的上下文,产生随机数,如下所示:
+
+```c
+rt_uint32_t rt_hwcrypto_rng_update(void);
+```
+
+| **参数** | **描述** |
+|-------------|--------------|
+| **返回** | —— |
+| rt_uint32_t | 产生的随机数 |
+| 其他 | 失败 |
+
+例如检测随机数产生 1000,000 次,统计产生奇数与偶数的数量,代码如下:
+
+```c
+void hw_rng(void)
+{
+ rt_uint32_t result=0;
+ int i, num0=0, num1 =0;
+ const int max_test = 1000 * 1000;
+
+
+ for (i = 0; i < max_test; i++)
+ {
+ result = rt_hwcrypto_rng_update();
+ result%2 ? num1++ : num0++;
+ }
+ LOG_I(" num1: %d, num0: %d ",num1, num0);
+}
+```
+
+## 大数运算
+
+由于编程语言提供的基本数值数据类型表示的数值范围有限,不能满足较大规模的高精度数值计算,因此需要利用其他方法实现高精度数值的计算。
+
+### 访问大数设备
+
+应用程序通过 RT-Thread 提供的大数设备管理接口来访问大数算法设备硬件,相关接口如下所示:
+
+| 函数 | 描述 |
+|---------------------------------|----------------------|
+| rt_hwcrypto_bignum_default() | 获取默认上下文 |
+| rt_hwcrypto_bignum_init() | 初始化大数对象 |
+| rt_hwcrypto_bignum_free() | 释放大数 |
+| rt_hwcrypto_bignum_get_len() | 获取大数长度 |
+| rt_hwcrypto_bignum_export_bin() | 以大端模式输出二进制 |
+| rt_hwcrypto_bignum_import_bin() | 以大端模式输入二进制 |
+| rt_hwcrypto_bignum_add() | 大数相加 |
+| rt_hwcrypto_bignum_sub() | 大数相减 |
+| rt_hwcrypto_bignum_mul() | 大数相乘 |
+| rt_hwcrypto_bignum_mulmod | 大数乘积取模 |
+| rt_hwcrypto_bignum_exptmod | 大数幂运算取模 |
+
+### 获取默认上下文
+
+应用程序根据大数的设备句柄,获取默认上下文,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_bignum_default(struct rt_hwcrypto_device *device);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| device | 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 初始化大数对象
+
+应用程序根据大数的上下文,初始化大数对象,如下所示:
+
+```c
+void rt_hwcrypto_bignum_init(struct hw_bignum_mpi *n);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| n | 初始化大数对象 |
+| **返回** | —— |
+
+大数对象的结构体如下:
+
+```c
+struct hw_bignum_mpi
+{
+ int sign; /**< integer sign */
+ rt_size_t total; /**< total of limbs */
+ rt_uint8_t *p; /**< pointer to limbs */
+};
+```
+
+### 释放大数
+
+应用程序释放大数对象,如下所示:
+
+```c
+void rt_hwcrypto_bignum_free(struct hw_bignum_mpi *n);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| n | 大数对象 |
+| **返回** | —— |
+
+### 获取大数长度
+
+应用程序获取大数的长度,如下所示:
+
+```c
+int rt_hwcrypto_bignum_get_len(const struct hw_bignum_mpi *n);
+```
+
+| **参数** | **描述** |
+|----------|------------------------|
+| n | 将要获取长度的大数对象 |
+| **返回** | —— |
+| int | 返回大数的长度 |
+| 其他 | 失败 |
+
+### 以大端模式输出二进制数
+
+应用程序根据大数的对象,以大端的模式输出二进制数,返回复制的长度,如下所示:
+
+```c
+int rt_hwcrypto_bignum_export_bin(struct hw_bignum_mpi *n, rt_uint8_t *buf, int len);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| n | 大数对象 |
+| buf | 将要输出数据 |
+| len | 数据长度 |
+| **返回** | —— |
+| int | 返回复制长度 |
+| 其他 | 失败 |
+
+### 以大端模式输入二进制数
+
+应用程序根据大数的句柄,以大端的模式输入二进制数,如下所示:
+
+```c
+rt_err_t rt_hwcrypto_bignum_import_bin(struct hw_bignum_mpi *n, rt_uint8_t *buf, int len);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| n | 大数对象 |
+| buf | 输入数据 |
+| len | 数据长度 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 大数相加
+
+应用程序将两个大数对象进行相加,结果赋值给 `x`,如下所示
+
+```c
+rt_err_t rt_hwcrypto_bignum_add(struct hw_bignum_mpi *x,
+ const struct hw_bignum_mpi *a,
+ const struct hw_bignum_mpi *b);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| x | 输出结果 |
+| a | 输入数据 |
+| b | 输入数据 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 大数相减
+
+应用程序将两个大数对象进行相减,结果赋值给 `x`,如下所示
+
+```c
+rt_err_t rt_hwcrypto_bignum_sub(struct hw_bignum_mpi *x,
+ const struct hw_bignum_mpi *a,
+ const struct hw_bignum_mpi *b);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| x | 输出结果 |
+| a | 输入数据 |
+| b | 输入数据 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 大数相乘
+
+应用程序将两个大数对象进行相乘,结果赋值给 `x`,如下所示
+
+```c
+rt_err_t rt_hwcrypto_bignum_mul(struct hw_bignum_mpi *x,
+ const struct hw_bignum_mpi *a,
+ const struct hw_bignum_mpi *b);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| x | 输出结果 |
+| a | 输入数据 |
+| b | 输入数据 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 乘积取模
+
+应用程序将大数对象进行乘积取模,结果赋值给 `x = a * b (mod c)`,如下所示
+
+```c
+rt_err_t rt_hwcrypto_bignum_mulmod(struct hw_bignum_mpi *x,
+ const struct hw_bignum_mpi *a,
+ const struct hw_bignum_mpi *b,
+ const struct hw_bignum_mpi *c);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| x | 输出结果 |
+| a | 输入数据 |
+| b | 输入数据 |
+| c | 输入数据 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+### 幂运算取模
+
+应用程序将大数对象进行指数取模,结果赋值给 `x = a ^ b (mod c)`,如下所示
+
+```c
+rt_err_t rt_hwcrypto_bignum_exptmod(struct hw_bignum_mpi *x,
+ const struct hw_bignum_mpi *a,
+ const struct hw_bignum_mpi *b,
+ const struct hw_bignum_mpi *c);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| x | 输出结果 |
+| a | 输入数据 |
+| b | 输入数据 |
+| c | 输入数据 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他 | 失败 |
+
+## crypto 设备使用示例
+
+通常硬件加解密使用流程大致分为 4 个步骤:第一步创建具体加解密类型的上下文;第二步对上下文进行配置,如设置密钥等操作;第三步执行相应的功能,获得处理后的结果;第四步删除上下文,释放资源。
+
+### 示例代码
+
+下面代码先使用 AES-CBC 将数据进行加密,然后对加密后的数据进行解密。解密完成后,使用 MD5 和 SHA1 两种散列算法生成信息摘要,同时输出一些日志信息(硬件加解密具体实现代码在 main() 函数下方)。
+
+```c
+
+/* 加密密钥 */
+static const rt_uint8_t key[16] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF};
+
+int main(void)
+{
+ rt_uint8_t buf_in[32];
+ rt_uint8_t buf_out[32];
+ int i;
+
+ /* 填充测试数据 */
+ for (i = 0; i < sizeof(buf_in); i++)
+ {
+ buf_in[i] = (rt_uint8_t)i;
+ }
+ /* 打印填充的数据 */
+ LOG_HEX("Data ", 8, buf_in, sizeof(buf_in));
+
+ memset(buf_out, 0, sizeof(buf_out));
+ /* 对测试数据进行加密 */
+ hw_aes_cbc(buf_in, buf_out, HWCRYPTO_MODE_ENCRYPT);
+
+ /* 打印加密后的数据 */
+ LOG_HEX("AES-enc", 8, buf_out, sizeof(buf_out));
+
+ memset(buf_in, 0, sizeof(buf_in));
+ /* 对加密数据进行解密 */
+ hw_aes_cbc(buf_out, buf_in, HWCRYPTO_MODE_DECRYPT);
+
+ /* 打印解密后的数据 */
+ LOG_HEX("AES-dec", 8, buf_in, sizeof(buf_in));
+
+ memset(buf_out, 0, sizeof(buf_out));
+ /* 对测试数据进行 MD5 运算 */
+ hw_hash(buf_in, buf_out, HWCRYPTO_TYPE_MD5);
+
+ /* 打印 16 字节长度的 MD5 结果 */
+ LOG_HEX("MD5 ", 8, buf_out, 16);
+
+ memset(buf_out, 0, sizeof(buf_out));
+ /* 对测试数据进行 SHA1 运算 */
+ hw_hash(buf_in, buf_out, HWCRYPTO_TYPE_SHA1);
+
+ /* 打印 20 字节长度的 SHA1 结果 */
+ LOG_HEX("SHA1 ", 8, buf_out, 20);
+
+ return 0;
+}
+```
+**1. AES-CBC 加解密**
+
+```c
+static void hw_aes_cbc(const rt_uint8_t in[32], rt_uint8_t out[32], hwcrypto_mode mode)
+{
+ struct rt_hwcrypto_ctx *ctx;
+
+ /* 创建一个 AES-CBC 模式的上下文 */
+ ctx = rt_hwcrypto_symmetric_create(rt_hwcrypto_dev_default(), HWCRYPTO_TYPE_AES_CBC);
+ if (ctx == RT_NULL)
+ {
+ LOG_E("create AES-CBC context err!");
+ return;
+ }
+ /* 设置 AES-CBC 加密密钥 */
+ rt_hwcrypto_symmetric_setkey(ctx, key, 128);
+ /* 执行 AES-CBC 加密/解密 */
+ rt_hwcrypto_symmetric_crypt(ctx, mode, 32, in, out);
+ /* 删除上下文,释放资源 */
+ rt_hwcrypto_symmetric_destroy(ctx);
+}
+```
+
+**2. HASH 信息摘要**
+
+```c
+static void hw_hash(const rt_uint8_t in[32], rt_uint8_t out[32], hwcrypto_type type)
+{
+ struct rt_hwcrypto_ctx *ctx;
+
+ /* 创建一个 SHA1/MD5 类型的上下文 */
+ ctx = rt_hwcrypto_hash_create(rt_hwcrypto_dev_default(), type);
+ if (ctx == RT_NULL)
+ {
+ LOG_E("create hash[%08x] context err!", type);
+ return;
+ }
+ /* 将输入数据进行 hash 运算 */
+ rt_hwcrypto_hash_update(ctx, in, 32);
+ /* 获得运算结果 */
+ rt_hwcrypto_hash_finish(ctx, out, 32);
+ /* 删除上下文,释放资源 */
+ rt_hwcrypto_hash_destroy(ctx);
+}
+```
+
+### 运行效果
+
+在正常运行后,终端输出信息如下:
+
+```shell
+ \ | /
+- RT - Thread Operating System
+ / | \ 4.0.1 build Jun 3 2019
+ 2006 - 2019 Copyright by rt-thread team
+D/HEX Data : 0000-0008: 00 01 02 03 04 05 06 07 ........
+ 0008-0010: 08 09 0A 0B 0C 0D 0E 0F ........
+ 0010-0018: 10 11 12 13 14 15 16 17 ........
+ 0018-0020: 18 19 1A 1B 1C 1D 1E 1F ........
+D/HEX AES-enc: 0000-0008: 0A 94 0B B5 41 6E F0 45 ....An.E
+ 0008-0010: F1 C3 94 58 C6 53 EA 5A ...X.S.Z
+ 0010-0018: 3C F4 56 B4 CA 48 8A A3 <.V..H..
+ 0018-0020: 83 C7 9C 98 B3 47 97 CB .....G..
+D/HEX AES-dec: 0000-0008: 00 01 02 03 04 05 06 07 ........
+ 0008-0010: 08 09 0A 0B 0C 0D 0E 0F ........
+ 0010-0018: 10 11 12 13 14 15 16 17 ........
+ 0018-0020: 18 19 1A 1B 1C 1D 1E 1F ........
+D/HEX MD5 : 0000-0008: B4 FF CB 23 73 7C EC 31 ...#s|.1
+ 0008-0010: 5A 4A 4D 1A A2 A6 20 CE ZJM... .
+D/HEX SHA1 : 0000-0008: AE 5B D8 EF EA 53 22 C4 .[...S".
+ 0008-0010: D9 98 6D 06 68 0A 78 13 ..m.h.x.
+ 0010-0018: 92 F9 A6 42 ...B
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/crypto/figures/framework.png b/rt-thread-version/rt-thread-standard/programming-manual/device/crypto/figures/framework.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab8df1607289c1c3322feb99d64173e5531fedb3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/crypto/figures/framework.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/dac/dac.md b/rt-thread-version/rt-thread-standard/programming-manual/device/dac/dac.md
new file mode 100644
index 0000000000000000000000000000000000000000..dcd1839139daab350c3b50d39f95e1658342ed4c
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/dac/dac.md
@@ -0,0 +1,266 @@
+# DAC 设备
+
+## DAC 简介
+
+DAC(Digital-to-Analogl Converter) 指数模转换器。是指把二进制数字量形式的离散数字信号转换为连续变化的模拟信号的器件。在数字世界中,要处理不稳定和动态的模拟信号并不容易,基于 DAC 的特性,在各种不同的产品中都可以找到它的身影。与之相对应的 ADC(Analog-to-Digital Converter)),它是 DAC 数模转换的逆向过程。DAC 主要应用于音频放大,视频编码,电机控制,数字电位计等。
+
+### 转换过程
+
+DAC 主要由数字寄存器、模拟电子开关、位权网络、求和运算放大器和基准电压源(或恒流源)组成。用存于数字寄存器的数字量的各位数码,分别控制对应位的模拟电子开关,使数码为 1 的位在位权网络上产生与其位权成正比的电流值,再由运算放大器对各电流值求和,并转换成电压值。DAC 的转换过程如下图所示:
+
+
+
+数模转换器是将数字信号转换为模拟信号的系统,一般用低通滤波即可以实现。数字信号先进行解码,即把数字码转换成与之对应的电平,形成阶梯状信号,然后进行低通滤波。
+
+### 分辨率
+
+分辨率是指 D/A 转换器能够转换的二进制位数,位数越多分辨率越高。
+
+### 转换时间
+
+建立时间是将一个数字量转换为稳定模拟信号所需的时间,也可以认为是转换时间。D/A 中常用建立时间来描述其速度,而不是 A/D 中常用的转换速率。一般地,电流输出 D/A 建立时间较短,电压输出 D/A 则较长。
+
+### 转换精度
+
+精度是指输入端加有最大数值量时,DAC 的实际输出值和理论计算值之差,它主要包括非线性误差、比例系统误差、失调误差。
+
+### 线性度
+
+理想的 D/A 转换器是线性的,实际上是有误差的。线性度是指数字量化时,D/A 转换器输出的模拟量按比例关系变化程度。
+
+## 访问 DAC 设备
+
+应用程序通过 RT-Thread 提供的 DAC 设备管理接口来访问 DAC 硬件,相关接口如下所示:
+
+| **函数** | **描述** |
+| ---------------- | ------------------------------------- |
+| rt_device_find() | 根据 DAC 设备名称查找设备获取设备句柄 |
+| rt_dac_enable() | 使能 DAC 设备 |
+| rt_dac_write() | 设置 DAC 设备输出值 |
+| rt_dac_disable() | 关闭 DAC 设备 |
+
+### 查找 DAC 设备
+
+应用程序根据 DAC 设备名称获取设备句柄,进而可以操作 DAC 设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | DAC 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到设备 |
+
+一般情况下,注册到系统的 DAC 设备名称为 dac1,dac2 等,使用示例如下所示:
+
+```
+#define DAC_DEV_NAME "dac1" /* DAC 设备名称 */
+rt_dac_device_t dac_dev; /* DAC 设备句柄 */
+/* 查找设备 */
+dac_dev = (rt_dac_device_t)rt_device_find(DAC_DEV_NAME);
+```
+
+### 使能 DAC 通道
+
+在设置 DAC 设备数据前需要先使能设备,通过如下函数使能设备:
+
+```c
+rt_err_t rt_dac_enable(rt_dac_device_t dev, rt_uint32_t channel);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------- |
+| dev | DAC 设备句柄 |
+| channel | DAC 通道 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| -RT_ENOSYS | 失败,设备操作方法为空 |
+| 其他错误码 | 失败 |
+
+使用示例如下所示:
+
+```c
+#define DAC_DEV_NAME "dac1" /* DAC 设备名称 */
+#define DAC_DEV_CHANNEL 1 /* DAC 通道 */
+rt_dac_device_t dac_dev; /* DAC 设备句柄 */
+/* 查找设备 */
+dac_dev = (rt_dac_device_t)rt_device_find(DAC_DEV_NAME);
+/* 使能设备 */
+rt_dac_enable(dac_dev, DAC_DEV_CHANNEL);
+```
+
+### 设置 DAC 通道输出值
+
+设置 DAC 通道输出值可通过如下函数完成:
+
+```c
+rt_uint32_t rt_dac_write(rt_dac_device_t dev, rt_uint32_t channel, rt_uint32_t value);
+```
+
+| | |
+| ---------- | ------------ |
+| **参数** | **描述** |
+| dev | DAC 设备句柄 |
+| channel | DAC 通道 |
+| value | DAC 输出值 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| -RT_ENOSYS | 失败 |
+
+使用 DAC 输出电压值的使用示例如下所示:
+
+```c
+#define DAC_DEV_NAME "dac1" /* DAC 设备名称 */
+#define DAC_DEV_CHANNEL 1 /* DAC 通道 */
+rt_dac_device_t dac_dev; /* DAC 设备句柄 */
+rt_uint32_t value = 1000; /* DAC 数据保持寄存器值 */
+/* 查找设备 */
+dac_dev = (rt_dac_device_t)rt_device_find(DAC_DEV_NAME);
+/* 使能设备 */
+rt_dac_enable(dac_dev, DAC_DEV_CHANNEL);
+/* 输出电压值 */
+rt_dac_write(dac_dev, DAC_DEV_CHANNEL, &value);
+/* 转换为对应电压值 */
+vol = value * REFER_VOLTAGE / CONVERT_BITS;
+rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
+```
+
+### 关闭 DAC 通道
+
+关闭 DAC 通道可通过如下函数完成:
+
+```c
+rt_err_t rt_dac_disable(rt_dac_device_t dev, rt_uint32_t channel);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------- |
+| dev | DAC 设备句柄 |
+| channel | DAC 通道 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| -RT_ENOSYS | 失败,设备操作方法为空 |
+| 其他错误码 | 失败 |
+
+使用示例如下所示:
+
+```
+#define DAC_DEV_NAME "dac1" /* DAC 设备名称 */
+#define DAC_DEV_CHANNEL 1 /* DAC 通道 */
+rt_dac_device_t dac_dev; /* DAC 设备句柄 */
+rt_uint32_t value = 1000; /* DAC 数据保持寄存器值 */
+/* 查找设备 */
+dac_dev = (rt_dac_device_t)rt_device_find(DAC_DEV_NAME);
+/* 使能设备 */
+rt_dac_enable(dac_dev, DAC_DEV_CHANNEL);
+/* 设置输出值 */
+rt_dac_write(dac_dev, DAC_DEV_CHANNEL, &value);
+/* 转换为对应电压值 */
+vol = value * REFER_VOLTAGE / CONVERT_BITS;
+rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
+/* 关闭通道 */
+rt_dac_disable(dac_dev, DAC_DEV_CHANNEL);
+```
+
+### FinSH 命令
+
+在使用设备前,需要先查找设备是否存在,可以使用命令 `dac probe` 后面跟注册的 DAC 设备的名称。如下所示:
+
+```c
+msh >dac probe dac1
+probe dac1 success
+```
+
+使能设备的某个通道可以使用命令 `dac enable` 后面跟通道号。
+
+```c
+msh >dac enable 1
+dac1 channel 1 enables success
+```
+
+设置 DAC 设备某个通道的输出值可以使用命令 `dac write` 后面跟通道号和输出值。
+
+```c
+msh >dac write 1 1000
+dac1 channel 1 write value is 1000
+msh >
+```
+
+关闭设备的某个通道可以使用命令 `dac disable` 后面跟通道号。
+
+```
+msh >dac disable 1
+dac1 channel 1 disable success
+msh >
+```
+
+## DAC 设备使用示例
+
+DAC 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 首先根据 DAC 设备名称 “dac1” 查找设备获取设备句柄。
+2. 使能设备后设置 dac1 设备对应的通道 1 的输出值,然后根据分辨率为 12 位,参考电压为 3.3V 计算实际的电压值。
+3. 若不使用 DAC 设备对应通道,则需要关闭该通道。
+
+运行结果:打印实际读取到的转换的原始数据和经过计算后的实际电压值。
+
+```
+/*
+ * 程序清单: DAC 设备使用例程
+ * 例程导出了 dac_sample 命令到控制终端
+ * 命令调用格式:dac_sample
+ * 程序功能:通过 DAC 设备将数字值转换为模拟量,并输出电压值。
+ * 示例代码参考电压为3.3V,转换位数为12位。
+*/
+
+#include
+#include
+
+#define DAC_DEV_NAME "dac1" /* DAC 设备名称 */
+#define DAC_DEV_CHANNEL 1 /* DAC 通道 */
+#define REFER_VOLTAGE 330 /* 参考电压 3.3V,数据精度乘以100保留2位小数*/
+#define CONVERT_BITS (1 << 12) /* 转换位数为12位 */
+
+static int dac_vol_sample(int argc, char *argv[])
+{
+ rt_dac_device_t dac_dev;
+ rt_uint32_t value, vol;
+ rt_err_t ret = RT_EOK;
+
+ /* 查找设备 */
+ dac_dev = (rt_dac_device_t)rt_device_find(DAC_DEV_NAME);
+ if (dac_dev == RT_NULL)
+ {
+ rt_kprintf("dac sample run failed! can't find %s device!\n", DAC_DEV_NAME);
+ return RT_ERROR;
+ }
+
+ /* 使能设备 */
+ ret = rt_dac_enable(dac_dev, DAC_DEV_NAME);
+
+ /* 设置输出值 */
+ value = atoi(argv[1]);
+ rt_dac_write(dac_dev, DAC_DEV_NAME, DAC_DEV_CHANNEL, &value);
+ rt_kprintf("the value is :%d \n", value);
+
+ /* 转换为对应电压值 */
+ vol = value * REFER_VOLTAGE / CONVERT_BITS;
+ rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
+
+ /* 关闭通道 */
+ ret = rt_dac_disable(dac_dev, DAC_DEV_CHANNEL);
+
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(dac_vol_sample, dac voltage convert sample);
+```
+
+## 常见问题
+
+### Q: menuconfig 找不到 DAC 设备的配置选项?
+
+**A:** 使用的源代码还不支持 DAC 设备驱动框架。建议更新源代码。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/dac/figures/dac-p.png b/rt-thread-version/rt-thread-standard/programming-manual/device/dac/figures/dac-p.png
new file mode 100644
index 0000000000000000000000000000000000000000..4d90608f6d964d62a1d5a4431aaaeb4bc1b392c9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/dac/figures/dac-p.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/device.md b/rt-thread-version/rt-thread-standard/programming-manual/device/device.md
new file mode 100644
index 0000000000000000000000000000000000000000..477982ac3067544c60123394bed6bd1a02679855
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/device.md
@@ -0,0 +1,495 @@
+# I/O 设备模型
+
+绝大部分的嵌入式系统都包括一些 I/O(Input/Output,输入 / 输出)设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的 Flash 或 SD 卡,以及网络设备的以太网接口等,都是嵌入式系统中容易找到的 I/O 设备例子。
+
+本章主要介绍 RT-Thread 如何对不同的 I/O 设备进行管理,读完本章,我们会了解 RT-Thread 的 I/O 设备模型,并熟悉 I/O 设备管理接口的不同功能。
+
+## I/O 设备介绍
+
+### I/O 设备模型框架
+
+RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。
+
+
+
+应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行数据(或控制)交互。
+
+I/O 设备管理层实现了对设备驱动程序的封装。应用程序通过 I/O 设备层提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在,双方只需关注各自的功能实现,从而降低了代码的耦合性、复杂性,提高了系统的可靠性。
+
+设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
+
+设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。它负责创建和注册 I/O 设备,对于操作逻辑简单的设备,可以不经过设备驱动框架层,直接将设备注册到 I/O 设备管理器中,使用序列图如下图所示,主要有以下 2 点:
+
+* 设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过 `rt_device_register()` 接口注册到 I/O 设备管理器中。
+
+* 应用程序通过 `rt_device_find()` 接口查找到设备,然后使用 I/O 设备管理接口来访问硬件。
+
+
+
+对于另一些设备,如看门狗等,则会将创建的设备实例先注册到对应的设备驱动框架中,再由设备驱动框架向 I/O 设备管理器进行注册,主要有以下几点:
+
+* 看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过 `rt_hw_watchdog_register()` 接口注册到看门狗设备驱动框架中。
+
+* 看门狗设备驱动框架通过 `rt_device_register()` 接口将看门狗设备注册到 I/O 设备管理器中。
+
+* 应用程序通过 I/O 设备管理接口来访问看门狗设备硬件。
+
+看门狗设备使用序列图:
+
+
+
+### I/O 设备模型
+
+RT-Thread 的设备模型是建立在内核对象模型基础之上的,设备被认为是一类对象,被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来,每个具体设备都可以继承其父类对象的属性,并派生出其私有属性,下图是设备对象的继承和派生关系示意图。
+
+
+
+设备对象具体定义如下所示:
+
+```c
+struct rt_device
+{
+ struct rt_object parent; /* 内核对象基类 */
+ enum rt_device_class_type type; /* 设备类型 */
+ rt_uint16_t flag; /* 设备参数 */
+ rt_uint16_t open_flag; /* 设备打开标志 */
+ rt_uint8_t ref_count; /* 设备被引用次数 */
+ rt_uint8_t device_id; /* 设备 ID,0 - 255 */
+
+ /* 数据收发回调函数 */
+ rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
+ rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
+
+ const struct rt_device_ops *ops; /* 设备操作方法 */
+
+ /* 设备的私有数据 */
+ void *user_data;
+};
+typedef struct rt_device *rt_device_t;
+
+```
+
+### I/O 设备类型
+
+RT-Thread 支持多种 I/O 设备类型,主要设备类型如下所示:
+
+```c
+RT_Device_Class_Char /* 字符设备 */
+RT_Device_Class_Block /* 块设备 */
+RT_Device_Class_NetIf /* 网络接口设备 */
+RT_Device_Class_MTD /* 内存设备 */
+RT_Device_Class_RTC /* RTC 设备 */
+RT_Device_Class_Sound /* 声音设备 */
+RT_Device_Class_Graphic /* 图形设备 */
+RT_Device_Class_I2CBUS /* I2C 总线设备 */
+RT_Device_Class_USBDevice /* USB device 设备 */
+RT_Device_Class_USBHost /* USB host 设备 */
+RT_Device_Class_SPIBUS /* SPI 总线设备 */
+RT_Device_Class_SPIDevice /* SPI 设备 */
+RT_Device_Class_SDIO /* SDIO 设备 */
+RT_Device_Class_Miscellaneous /* 杂类设备 */
+```
+
+其中字符设备、块设备是常用的设备类型,它们的分类依据是设备数据与系统之间的传输处理方式。字符模式设备允许非结构的数据传输,即通常数据传输采用串行的形式,每次一个字节。字符设备通常是一些简单设备,如串口、按键。
+
+块设备每次传输一个数据块,例如每次传输 512 个字节数据。这个数据块是硬件强制性的,数据块可能使用某类数据接口或某些强制性的传输协议,否则就可能发生错误。因此,有时块设备驱动程序对读或写操作必须执行附加的工作,如下图所示:
+
+
+
+当系统服务于一个具有大量数据的写操作时,设备驱动程序必须首先将数据划分为多个包,每个包采用设备指定的数据尺寸。而在实际过程中,最后一部分数据尺寸有可能小于正常的设备块尺寸。如上图中每个块使用单独的写请求写入到设备中,头 3 个直接进行写操作。但最后一个数据块尺寸小于设备块尺寸,设备驱动程序必须使用不同于前 3 个块的方式处理最后的数据块。通常情况下,设备驱动程序需要首先执行相对应的设备块的读操作,然后把写入数据覆盖到读出数据上,然后再把这个 “合成” 的数据块作为一整个块写回到设备中。例如上图中的块 4,驱动程序需要先把块 4 所对应的设备块读出来,然后将需要写入的数据覆盖至从设备块读出的数据上,使其合并成一个新的块,最后再写回到块设备中。
+
+## 创建和注册 I/O 设备
+
+驱动层负责创建设备实例,并注册到 I/O 设备管理器中,可以通过静态申明的方式创建设备实例,也可以用下面的接口进行动态创建:
+
+```c
+rt_device_t rt_device_create(int type, int attach_size);
+```
+
+|**参数** |**描述** |
+|-------------|-------------------------------------|
+| type | 设备类型,可取前面小节列出的设备类型值 |
+| attach_size | 用户数据大小 |
+|**返回** | —— |
+| 设备句柄 | 创建成功 |
+| RT_NULL | 创建失败,动态内存分配失败 |
+
+调用该接口时,系统会从动态堆内存中分配一个设备控制块,大小为 struct rt_device 和 attach_size 的和,设备的类型由参数 type 设定。设备被创建后,需要实现它访问硬件的操作方法。
+
+```c
+struct rt_device_ops
+{
+ /* common device interface */
+ rt_err_t (*init) (rt_device_t dev);
+ rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);
+ rt_err_t (*close) (rt_device_t dev);
+ rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
+ rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
+ rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
+};
+
+```
+
+各个操作方法的描述如下表所示:
+
+|**方法名称**|**方法描述** |
+|----|-----------------------|
+| init | 初始化设备。设备初始化完成后,设备控制块的 flag 会被置成已激活状态 (RT_DEVICE_FLAG_ACTIVATED)。如果设备控制块中的 flag 标志已经设置成激活状态,那么再运行初始化接口时会立刻返回,而不会重新进行初始化。 |
+| open | 打开设备。有些设备并不是系统一启动就已经打开开始运行,或者设备需要进行数据收发,但如果上层应用还未准备好,设备也不应默认已经使能并开始接收数据。所以建议在写底层驱动程序时,在调用 open 接口时才使能设备。 |
+| close | 关闭设备。在打开设备时,设备控制块会维护一个打开计数,在打开设备时进行 + 1 操作,在关闭设备时进行 - 1 操作,当计数器变为 0 时,才会进行真正的关闭操作。|
+| read | 从设备读取数据。参数 pos 是读取数据的偏移量,但是有些设备并不一定需要指定偏移量,例如串口设备,设备驱动应忽略这个参数。而对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。例如块设备的数据块大小是 512,而参数中 pos = 10, size = 2,那么驱动应该返回设备中第 10 个块 (从第 0 个块做为起始),共计 2 个块的数据。这个接口返回的类型是 rt_size_t,即读到的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。 |
+| write | 向设备写入数据。参数 pos 是写入数据的偏移量。与读操作类似,对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。这个接口返回的类型是 rt_size_t,即真实写入数据的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。 |
+| control | 根据 cmd 命令控制设备。命令往往是由底层各类设备驱动自定义实现。例如参数 RT_DEVICE_CTRL_BLK_GETGEOME,意思是获取块设备的大小信息。 |
+
+当一个动态创建的设备不再需要使用时可以通过如下函数来销毁:
+
+```c
+void rt_device_destroy(rt_device_t device);
+```
+
+|**参数**|**描述**|
+|----------|----------|
+| device | 设备句柄 |
+|**返回**| 无 |
+
+设备被创建后,需要注册到 I/O 设备管理器中,应用程序才能够访问,注册设备的函数如下所示:
+
+```c
+rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);
+```
+
+|**参数** |**描述** |
+|------------|-----------------------|
+| dev | 设备句柄 |
+| name | 设备名称,设备名称的最大长度由 rtconfig.h 中定义的宏 RT_NAME_MAX 指定,多余部分会被自动截掉 |
+| flags | 设备模式标志 |
+|**返回** | —— |
+| RT_EOK | 注册成功 |
+| -RT_ERROR | 注册失败,dev 为空或者 name 已经存在 |
+
+> [!NOTE]
+> 注:应当避免重复注册已经注册的设备,以及注册相同名字的设备。
+
+flags 参数支持下列参数 (可以采用或的方式支持多种参数):
+
+```c
+#define RT_DEVICE_FLAG_RDONLY 0x001 /* 只读 */
+#define RT_DEVICE_FLAG_WRONLY 0x002 /* 只写 */
+#define RT_DEVICE_FLAG_RDWR 0x003 /* 读写 */
+#define RT_DEVICE_FLAG_REMOVABLE 0x004 /* 可移除 */
+#define RT_DEVICE_FLAG_STANDALONE 0x008 /* 独立 */
+#define RT_DEVICE_FLAG_SUSPENDED 0x020 /* 挂起 */
+#define RT_DEVICE_FLAG_STREAM 0x040 /* 流模式 */
+#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收 */
+#define RT_DEVICE_FLAG_DMA_RX 0x200 /* DMA 接收 */
+#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中断发送 */
+#define RT_DEVICE_FLAG_DMA_TX 0x800 /* DMA 发送 */
+```
+
+设备流模式 RT_DEVICE_FLAG_STREAM 参数用于向串口终端输出字符串:当输出的字符是 `“\n”` 时,自动在前面补一个 `“\r”` 做分行。
+
+注册成功的设备可以在 FinSH 命令行使用 `list_device` 命令查看系统中所有的设备信息,包括设备名称、设备类型和设备被打开次数:
+
+```c
+msh />list_device
+device type ref count
+-------- -------------------- ----------
+e0 Network Interface 0
+sd0 Block Device 1
+rtc RTC 0
+uart1 Character Device 0
+uart0 Character Device 2
+msh />
+```
+
+当设备注销后的,设备将从设备管理器中移除,也就不能再通过设备查找搜索到该设备。注销设备不会释放设备控制块占用的内存。注销设备的函数如下所示:
+
+```c
+rt_err_t rt_device_unregister(rt_device_t dev);
+```
+
+|**参数**|**描述**|
+|----------|----------|
+| dev | 设备句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+下面代码为看门狗设备的注册示例,调用 `rt_hw_watchdog_register()` 接口后,设备通过 `rt_device_register()` 接口被注册到 I/O 设备管理器中。
+
+```c
+const static struct rt_device_ops wdt_ops =
+{
+ rt_watchdog_init,
+ rt_watchdog_open,
+ rt_watchdog_close,
+ RT_NULL,
+ RT_NULL,
+ rt_watchdog_control,
+};
+
+rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,
+ const char *name,
+ rt_uint32_t flag,
+ void *data)
+{
+ struct rt_device *device;
+ RT_ASSERT(wtd != RT_NULL);
+
+ device = &(wtd->parent);
+
+ device->type = RT_Device_Class_Miscellaneous;
+ device->rx_indicate = RT_NULL;
+ device->tx_complete = RT_NULL;
+
+ device->ops = &wdt_ops;
+ device->user_data = data;
+
+ /* register a character device */
+ return rt_device_register(device, name, flag);
+}
+
+```
+
+## 访问 I/O 设备
+
+应用程序通过 I/O 设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件。I/O 设备管理接口与 I/O 设备的操作方法的映射关系下图所示:
+
+
+
+### 查找设备
+
+应用程序根据设备名称获取设备句柄,进而可以操作设备。查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+|**参数**|**描述** |
+|----------|------------------------------------|
+| name | 设备名称 |
+|**返回**| —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+### 初始化设备
+
+获得设备句柄后,应用程序可使用如下函数对设备进行初始化操作:
+
+```c
+rt_err_t rt_device_init(rt_device_t dev);
+```
+
+|**参数**|**描述** |
+|----------|----------------|
+| dev | 设备句柄 |
+|**返回**| —— |
+| RT_EOK | 设备初始化成功 |
+| 错误码 | 设备初始化失败 |
+
+> [!NOTE]
+> 注:当一个设备已经初始化成功后,调用这个接口将不再重复做初始化 0。
+
+### 打开和关闭设备
+
+通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+|**参数** |**描述** |
+|------------|-----------------------------|
+| dev | 设备句柄 |
+| oflags | 设备打开模式标志 |
+|**返回** | —— |
+| RT_EOK | 设备打开成功 |
+|-RT_EBUSY | 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开 |
+| 其他错误码 | 设备打开失败 |
+
+oflags 支持以下的参数:
+
+```c
+#define RT_DEVICE_OFLAG_CLOSE 0x000 /* 设备已经关闭(内部使用)*/
+#define RT_DEVICE_OFLAG_RDONLY 0x001 /* 以只读方式打开设备 */
+#define RT_DEVICE_OFLAG_WRONLY 0x002 /* 以只写方式打开设备 */
+#define RT_DEVICE_OFLAG_RDWR 0x003 /* 以读写方式打开设备 */
+#define RT_DEVICE_OFLAG_OPEN 0x008 /* 设备已经打开(内部使用)*/
+#define RT_DEVICE_FLAG_STREAM 0x040 /* 设备以流模式打开 */
+#define RT_DEVICE_FLAG_INT_RX 0x100 /* 设备以中断接收模式打开 */
+#define RT_DEVICE_FLAG_DMA_RX 0x200 /* 设备以 DMA 接收模式打开 */
+#define RT_DEVICE_FLAG_INT_TX 0x400 /* 设备以中断发送模式打开 */
+#define RT_DEVICE_FLAG_DMA_TX 0x800 /* 设备以 DMA 发送模式打开 */
+```
+
+> [!NOTE]
+> 注:如果上层应用程序需要设置设备的接收回调函数,则必须以 RT_DEVICE_FLAG_INT_RX 或者 RT_DEVICE_FLAG_DMA_RX 的方式打开设备,否则不会回调函数。
+
+应用程序打开设备完成读写等操作后,如果不需要再对设备进行操作则可以关闭设备,通过如下函数完成:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+|**参数** |**描述** |
+|------------|------------------------------------|
+| dev | 设备句柄 |
+|**返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| \-RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+> [!NOTE]
+> 注:关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+### 控制设备
+
+通过命令控制字,应用程序也可以对设备进行控制,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+|**参数** |**描述** |
+|-------------|--------------------------------------------|
+| dev | 设备句柄 |
+| cmd | 命令控制字,这个参数通常与设备驱动程序相关 |
+| arg | 控制的参数 |
+|**返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+参数 cmd 的通用设备命令可取如下宏定义:
+
+```c
+#define RT_DEVICE_CTRL_RESUME 0x01 /* 恢复设备 */
+#define RT_DEVICE_CTRL_SUSPEND 0x02 /* 挂起设备 */
+#define RT_DEVICE_CTRL_CONFIG 0x03 /* 配置设备 */
+#define RT_DEVICE_CTRL_SET_INT 0x10 /* 设置中断 */
+#define RT_DEVICE_CTRL_CLR_INT 0x11 /* 清中断 */
+#define RT_DEVICE_CTRL_GET_INT 0x12 /* 获取中断状态 */
+```
+
+### 读写设备
+
+应用程序从设备中读取数据可以通过如下函数完成:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos,void* buffer, rt_size_t size);
+```
+
+|**参数** |**描述** |
+|--------------------|--------------------------------|
+| dev | 设备句柄 |
+| pos | 读取数据偏移量 |
+| buffer | 内存缓冲区指针,读取的数据将会被保存在缓冲区中 |
+| size | 读取数据的大小 |
+|**返回** | —— |
+| 读到数据的实际大小 | 如果是字符设备,返回大小以字节为单位,如果是块设备,返回的大小以块为单位 |
+| 0 | 需要读取当前线程的 errno 来判断错误状态 |
+
+调用这个函数,会从 dev 设备中读取数据,并存放在 buffer 缓冲区中,这个缓冲区的最大长度是 size,pos 根据不同的设备类别有不同的意义。
+
+向设备中写入数据,可以通过如下函数完成:
+
+```c
+rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos,const void* buffer, rt_size_t size);
+```
+
+|**参数** |**描述** |
+|--------------------|--------------------------------|
+| dev | 设备句柄 |
+| pos | 写入数据偏移量 |
+| buffer | 内存缓冲区指针,放置要写入的数据 |
+| size | 写入数据的大小 |
+|**返回** | —— |
+| 写入数据的实际大小 | 如果是字符设备,返回大小以字节为单位;如果是块设备,返回的大小以块为单位 |
+| 0 | 需要读取当前线程的 errno 来判断错误状态 |
+
+调用这个函数,会把缓冲区 buffer 中的数据写入到设备 dev 中,写入数据的最大长度是 size,pos 根据不同的设备类别存在不同的意义。
+
+### 数据收发回调
+
+当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达:
+
+```c
+rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
+
+```
+
+|**参数**|**描述** |
+|----------|--------------|
+| dev | 设备句柄 |
+| rx_ind | 回调函数指针 |
+|**返回**| —— |
+| RT_EOK | 设置成功 |
+
+该函数的回调函数由调用者提供。当硬件设备接收到数据时,会回调这个函数并把收到的数据长度放在 size 参数中传递给上层应用。上层应用线程应在收到指示后,立刻从设备中读取数据。
+
+在应用程序调用 `rt_device_write()` 入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后 (例如 DMA 传送完成或 FIFO 已经写入完毕产生完成中断时) 调用。可以通过如下函数设置设备发送完成指示,函数参数及返回值见:
+
+```c
+rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
+```
+
+|**参数**|**描述** |
+|----------|--------------|
+| dev | 设备句柄 |
+| tx_done | 回调函数指针 |
+|**返回**| —— |
+| RT_EOK | 设置成功 |
+
+调用这个函数时,回调函数由调用者提供,当硬件设备发送完数据时,由驱动程序回调这个函数并把发送完成的数据块地址 buffer 作为参数传递给上层应用。上层应用(线程)在收到指示时会根据发送 buffer 的情况,释放 buffer 内存块或将其作为下一个写数据的缓存。
+
+### 设备访问示例
+
+下面代码为用程序访问设备的示例,首先通过 `rt_device_find()` 口查找到看门狗设备,获得设备句柄,然后通过 `rt_device_init()` 口初始化设备,通过 `rt_device_control()` 口设置看门狗设备溢出时间。
+
+```c
+#include
+#include
+
+#define IWDG_DEVICE_NAME "iwg"
+
+static rt_device_t wdg_dev;
+
+static void idle_hook(void)
+{
+ /* 在空闲线程的回调函数里喂狗 */
+ rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
+ rt_kprintf("feed the dog!\n ");
+}
+
+int main(void)
+{
+ rt_err_t res = RT_EOK;
+ rt_uint32_t timeout = 1000; /* 溢出时间 */
+
+ /* 根据设备名称查找看门狗设备,获取设备句柄 */
+ wdg_dev = rt_device_find(IWDG_DEVICE_NAME);
+ if (!wdg_dev)
+ {
+ rt_kprintf("find %s failed!\n", IWDG_DEVICE_NAME);
+ return RT_ERROR;
+ }
+ /* 初始化设备 */
+ res = rt_device_init(wdg_dev);
+ if (res != RT_EOK)
+ {
+ rt_kprintf("initialize %s failed!\n", IWDG_DEVICE_NAME);
+ return res;
+ }
+ /* 设置看门狗溢出时间 */
+ res = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
+ if (res != RT_EOK)
+ {
+ rt_kprintf("set %s timeout failed!\n", IWDG_DEVICE_NAME);
+ return res;
+ }
+ /* 设置空闲线程回调函数 */
+ rt_thread_idle_sethook(idle_hook);
+
+ return res;
+}
+```
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/figures/block-dev.png b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/block-dev.png
new file mode 100644
index 0000000000000000000000000000000000000000..88e2c962f997fdf304b53b71f9fb2ed45d5d86e7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/block-dev.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-call.png b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-call.png
new file mode 100644
index 0000000000000000000000000000000000000000..d63fb26fec64c374258b0c2603662b1d4c49a463
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-call.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-dev.png b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-dev.png
new file mode 100644
index 0000000000000000000000000000000000000000..901577c64d7fcbbddaf85c39fbc888dc1afe4e14
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-dev.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-fun-call.png b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-fun-call.png
new file mode 100644
index 0000000000000000000000000000000000000000..3ad89dba68c0ca1a71064f1bcc4ce490b8ac92da
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-fun-call.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-parent.png b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-parent.png
new file mode 100644
index 0000000000000000000000000000000000000000..2800f9ebb582498dd08b206360affdc7d4606fc0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/io-parent.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/figures/wtd-uml.png b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/wtd-uml.png
new file mode 100644
index 0000000000000000000000000000000000000000..a25df0c5ffa7085bdbf2cf72f628ae3a989c2059
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/figures/wtd-uml.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md b/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md
new file mode 100644
index 0000000000000000000000000000000000000000..ee42ecada9578fefa9a1e9a813423fbfc435f67d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md
@@ -0,0 +1,409 @@
+# HWTIMER 设备
+
+
+## 定时器简介
+
+硬件定时器一般有 2 种工作模式,定时器模式和计数器模式。不管是工作在哪一种模式,实质都是通过内部计数器模块对脉冲信号进行计数。下面是定时器的一些重要概念。
+
+**计数器模式:**对外部输入引脚的外部脉冲信号计数。
+
+**定时器模式:**对内部脉冲信号计数。定时器常用作定时时钟,以实现定时检测,定时响应、定时控制。
+
+**计数器:**计数器可以递增计数或者递减计数。16位计数器的最大计数值为65535,32位的最大值为4294967295。
+
+**计数频率:**定时器模式时,计数器单位时间内的计数次数,由于系统时钟频率是定值,所以可以根据计数器的计数值计算出定时时间,定时时间 = 计数值 / 计数频率。例如计数频率为 1MHz,计数器计数一次的时间则为 1 / 1000000, 也就是每经过 1 微秒计数器加一(或减一),此时 16 位计数器的最大定时能力为 65535 微秒,即 65.535 毫秒。
+
+## 访问硬件定时器设备
+
+应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问硬件定时器设备,相关接口如下所示:
+
+| **函数** | **描述** |
+| -------------------- | ---------------------------------- |
+| rt_device_find() | 查找定时器设备 |
+| rt_device_open() | 以读写方式打开定时器设备 |
+| rt_device_set_rx_indicate() | 设置超时回调函数 |
+| rt_device_control() | 控制定时器设备,可以设置定时模式(单次/周期)/计数频率,或者停止定时器 |
+| rt_device_write() | 设置定时器超时值,定时器随即启动 |
+| rt_device_read() | 获取定时器当前值 |
+| rt_device_close() | 关闭定时器设备 |
+
+### 查找定时器设备
+
+应用程序根据硬件定时器设备名称获取设备句柄,进而可以操作硬件定时器设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | 硬件定时器设备名称 |
+| **返回** | —— |
+| 定时器设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到设备 |
+
+一般情况下,注册到系统的硬件定时器设备名称为 timer0,timer1等,使用示例如下所示:
+
+```c
+#define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */
+rt_device_t hw_dev; /* 定时器设备句柄 */
+/* 查找定时器设备 */
+hw_dev = rt_device_find(HWTIMER_DEV_NAME);
+```
+
+### 打开定时器设备
+
+通过设备句柄,应用程序可以打开设备。打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 硬件定时器设备句柄 |
+| oflags | 设备打开模式,一般以读写方式打开,即取值:RT_DEVICE_OFLAG_RDWR |
+| **返回** | —— |
+| RT_EOK | 设备打开成功 |
+| 其他错误码 | 设备打开失败 |
+
+使用示例如下所示:
+
+```c
+#define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */
+rt_device_t hw_dev; /* 定时器设备句柄 */
+/* 查找定时器设备 */
+hw_dev = rt_device_find(HWTIMER_DEV_NAME);
+/* 以读写方式打开设备 */
+rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
+```
+
+### 设置超时回调函数
+
+通过如下函数设置定时器超时回调函数,当定时器超时将会调用此回调函数:
+
+```c
+rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size))
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 设备句柄 |
+| rx_ind | 超时回调函数,由调用者提供 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+
+使用示例如下所示:
+
+```c
+#define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */
+rt_device_t hw_dev; /* 定时器设备句柄 */
+
+/* 定时器超时回调函数 */
+static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
+{
+ rt_kprintf("this is hwtimer timeout callback fucntion!\n");
+ rt_kprintf("tick is :%d !\n", rt_tick_get());
+
+ return 0;
+}
+
+static int hwtimer_sample(int argc, char *argv[])
+{
+ /* 查找定时器设备 */
+ hw_dev = rt_device_find(HWTIMER_DEV_NAME);
+ /* 以读写方式打开设备 */
+ rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
+ /* 设置超时回调函数 */
+ rt_device_set_rx_indicate(hw_dev, timeout_cb);
+}
+```
+
+### 控制定时器设备
+
+通过命令控制字,应用程序可以对硬件定时器设备进行配置,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| cmd | 命令控制字 |
+| arg | 控制的参数 |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+硬件定时器设备支持的命令控制字如下所示:
+
+| **控制字** | **描述** |
+| ---------------------- | ------------------------ |
+| HWTIMER_CTRL_FREQ_SET | 设置计数频率 |
+| HWTIMER_CTRL_STOP | 停止定时器 |
+| HWTIMER_CTRL_INFO_GET | 获取定时器特征信息 |
+| HWTIMER_CTRL_MODE_SET | 设置定时器模式 |
+
+获取定时器特征信息参数 arg 为指向结构体 struct rt_hwtimer_info 的指针,作为一个输出参数保存获取的信息。
+
+> [!NOTE]
+> 注:定时器硬件及驱动支持设置计数频率的情况下设置频率才有效,一般使用驱动设置的默认频率即可。
+
+设置定时器模式时,参数 arg 可取如下值:
+
+```c
+HWTIMER_MODE_ONESHOT 单次定时
+HWTIMER_MODE_PERIOD 周期性定时
+```
+
+设置定时器计数频率和定时模式的使用示例如下所示:
+
+```c
+#define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */
+rt_device_t hw_dev; /* 定时器设备句柄 */
+rt_hwtimer_mode_t mode; /* 定时器模式 */
+rt_uint32_t freq = 10000; /* 计数频率 */
+
+/* 定时器超时回调函数 */
+static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
+{
+ rt_kprintf("this is hwtimer timeout callback fucntion!\n");
+ rt_kprintf("tick is :%d !\n", rt_tick_get());
+
+ return 0;
+}
+
+static int hwtimer_sample(int argc, char *argv[])
+{
+ /* 查找定时器设备 */
+ hw_dev = rt_device_find(HWTIMER_DEV_NAME);
+ /* 以读写方式打开设备 */
+ rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
+ /* 设置超时回调函数 */
+ rt_device_set_rx_indicate(hw_dev, timeout_cb);
+
+ /* 设置计数频率(默认1Mhz或支持的最小计数频率) */
+ rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);
+ /* 设置模式为周期性定时器 */
+ mode = HWTIMER_MODE_PERIOD;
+ rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
+}
+```
+
+### 设置定时器超时值
+
+通过如下函数可以设置定时器的超时值:
+
+```c
+rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| pos | 写入数据偏移量,未使用,可取 0 值 |
+| buffer | 指向定时器超时时间结构体的指针 |
+| size | 超时时间结构体的大小 |
+| **返回** | —— |
+| 写入数据的实际大小 | |
+| 0 | 失败 |
+
+超时时间结构体原型如下所示:
+
+```c
+typedef struct rt_hwtimerval
+{
+ rt_int32_t sec; /* 秒 s */
+ rt_int32_t usec; /* 微秒 us */
+} rt_hwtimerval_t;
+```
+
+设置定时器超时值的使用示例如下所示:
+
+```c
+#define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */
+rt_device_t hw_dev; /* 定时器设备句柄 */
+rt_hwtimer_mode_t mode; /* 定时器模式 */
+rt_hwtimerval_t timeout_s; /* 定时器超时值 */
+
+/* 定时器超时回调函数 */
+static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
+{
+ rt_kprintf("this is hwtimer timeout callback fucntion!\n");
+ rt_kprintf("tick is :%d !\n", rt_tick_get());
+
+ return 0;
+}
+
+static int hwtimer_sample(int argc, char *argv[])
+{
+ /* 查找定时器设备 */
+ hw_dev = rt_device_find(HWTIMER_DEV_NAME);
+ /* 以读写方式打开设备 */
+ rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
+ /* 设置超时回调函数 */
+ rt_device_set_rx_indicate(hw_dev, timeout_cb);
+ /* 设置模式为周期性定时器 */
+ mode = HWTIMER_MODE_PERIOD;
+ rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
+
+ /* 设置定时器超时值为5s并启动定时器 */
+ timeout_s.sec = 5; /* 秒 */
+ timeout_s.usec = 0; /* 微秒 */
+ rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s));
+}
+```
+
+### 获取定时器当前值
+
+通过如下函数可以获取定时器当前值:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 定时器设备句柄 |
+| pos | 写入数据偏移量,未使用,可取 0 值 |
+| buffer | 输出参数,指向定时器超时时间结构体的指针 |
+| size | 超时时间结构体的大小 |
+| **返回** | —— |
+| 超时时间结构体的大小 | 成功 |
+| 0 | 失败 |
+
+使用示例如下所示:
+
+```c
+rt_hwtimerval_t timeout_s; /* 用于保存定时器经过时间 */
+/* 读取定时器经过时间 */
+rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
+```
+
+### 关闭定时器设备
+
+通过如下函数可以关闭定时器设备:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| dev | 定时器设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+ 关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+ 使用示例如下所示:
+
+```c
+#define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */
+rt_device_t hw_dev; /* 定时器设备句柄 */
+/* 查找定时器设备 */
+hw_dev = rt_device_find(HWTIMER_DEV_NAME);
+... ...
+rt_device_close(hw_dev);
+```
+
+> [!NOTE]
+> 注:可能出现定时误差。假设计数器最大值 0xFFFF,计数频率 1Mhz,定时时间 1 秒又 1 微秒。由于定时器一次最多只能计时到 65535us,对于 1000001us 的定时要求。可以 50000us 定时 20 次完成,此时将会出现计算误差 1us。
+
+## 硬件定时器设备使用示例
+
+硬件定时器设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 首先根据定时器设备名称 “timer0” 查找设备获取设备句柄。
+
+2. 以读写方式打开设备 “timer0” 。
+
+3. 设置定时器超时回调函数。
+
+4. 设置定时器模式为周期性定时器,并设置超时时间为 5 秒,此时定时器启动。
+
+5. 延时 3500ms 后读取定时器时间,读取到的值会以秒和微秒的形式显示。
+
+```c
+/*
+ * 程序清单:这是一个 hwtimer 设备使用例程
+ * 例程导出了 hwtimer_sample 命令到控制终端
+ * 命令调用格式:hwtimer_sample
+ * 程序功能:硬件定时器超时回调函数周期性的打印当前tick值,2次tick值之差换算为时间等同于定时时间值。
+*/
+
+#include
+#include
+
+#define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */
+
+/* 定时器超时回调函数 */
+static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
+{
+ rt_kprintf("this is hwtimer timeout callback fucntion!\n");
+ rt_kprintf("tick is :%d !\n", rt_tick_get());
+
+ return 0;
+}
+
+static int hwtimer_sample(int argc, char *argv[])
+{
+ rt_err_t ret = RT_EOK;
+ rt_hwtimerval_t timeout_s; /* 定时器超时值 */
+ rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
+ rt_hwtimer_mode_t mode; /* 定时器模式 */
+
+ /* 查找定时器设备 */
+ hw_dev = rt_device_find(HWTIMER_DEV_NAME);
+ if (hw_dev == RT_NULL)
+ {
+ rt_kprintf("hwtimer sample run failed! can't find %s device!\n", HWTIMER_DEV_NAME);
+ return RT_ERROR;
+ }
+
+ /* 以读写方式打开设备 */
+ ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("open %s device failed!\n", HWTIMER_DEV_NAME);
+ return ret;
+ }
+
+ /* 设置超时回调函数 */
+ rt_device_set_rx_indicate(hw_dev, timeout_cb);
+
+ /* 设置模式为周期性定时器 */
+ mode = HWTIMER_MODE_PERIOD;
+ ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("set mode failed! ret is :%d\n", ret);
+ return ret;
+ }
+
+ /* 设置定时器超时值为5s并启动定时器 */
+ timeout_s.sec = 5; /* 秒 */
+ timeout_s.usec = 0; /* 微秒 */
+
+ if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
+ {
+ rt_kprintf("set timeout value failed\n");
+ return RT_ERROR;
+ }
+
+ /* 延时3500ms */
+ rt_thread_mdelay(3500);
+
+ /* 读取定时器当前值 */
+ rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
+ rt_kprintf("Read: Sec = %d, Usec = %d\n", timeout_s.sec, timeout_s.usec);
+
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(hwtimer_sample, hwtimer sample);
+```
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..f3a5e74a3fd8b0a7a97e9bb8feaf7cb0681d33c4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c.vsdx differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c1.png b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c1.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b3b2d204d3a3a7a2e10c7328f5ee24f53bb8634
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c2.png b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c2.png
new file mode 100644
index 0000000000000000000000000000000000000000..efcaff5e735e5cc7d2b9032efab33d98c454927e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c3.png b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c3.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8ad1a1c9311ae814d35192a92bee230d2e0082d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/figures/i2c3.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c.md b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c.md
new file mode 100644
index 0000000000000000000000000000000000000000..50f5842fcf877ef28656997f2a9f49fbe4005676
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c.md
@@ -0,0 +1,307 @@
+# I2C 总线设备
+
+## I2C 简介
+
+I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA(serial data),另一根是双向时钟线 SCL(serial clock)。SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。
+
+I2C 和 SPI 一样以主从的方式工作,不同于 SPI 一主多从的结构,它允许同时有多个主设备存在,每个连接到总线上的器件都有唯一的地址,主设备启动数据传输并产生时钟信号,从设备被主设备寻址,同一时刻只允许有一个主设备。如下图所示:
+
+
+
+如下图所示为 I2C 总线主要的数据传输格式:
+
+
+
+当总线空闲时,SDA 和 SCL 都处于高电平状态,当主机要和某个从机通讯时,会先发送一个开始条件,然后发送从机地址和读写控制位,接下来传输数据(主机发送或者接收数据),数据传输结束时主机会发送停止条件。传输的每个字节为8位,高位在前,低位在后。数据传输过程中的不同名词详解如下所示:
+
+* **开始条件:** SCL 为高电平时,主机将 SDA 拉低,表示数据传输即将开始。
+
+* **从机地址:** 主机发送的第一个字节为从机地址,高 7 位为地址,最低位为 R/W 读写控制位,1 表示读操作,0 表示写操作。一般从机地址有 7 位地址模式和 10 位地址模式两种,如果是 10 位地址模式,第一个字节的头 7 位 是 11110XX 的组合,其中最后两位(XX)是 10 位地址的两个最高位,第二个字节为 10 位从机地址的剩下8位,如下图所示:
+
+
+
+* **应答信号:** 每传输完成一个字节的数据,接收方就需要回复一个 ACK(acknowledge)。写数据时由从机发送 ACK,读数据时由主机发送 ACK。当主机读到最后一个字节数据时,可发送 NACK(Not acknowledge)然后跟停止条件。
+
+* **数据:** 从机地址发送完后可能会发送一些指令,依从机而定,然后开始传输数据,由主机或者从机发送,每个数据为 8 位,数据的字节数没有限制。
+
+* **重复开始条件:** 在一次通信过程中,主机可能需要和不同的从机传输数据或者需要切换读写操作时,主机可以再发送一个开始条件。
+
+* **停止条件:** 在 SDA 为低电平时,主机将 SCL 拉高并保持高电平,然后在将 SDA 拉高,表示传输结束。
+
+## 访问 I2C 总线设备
+
+一般情况下 MCU 的 I2C 器件都是作为主机和从机通讯,在 RT-Thread 中将 I2C 主机虚拟为 I2C总线设备,I2C 从机通过 I2C 设备接口和 I2C 总线通讯,相关接口如下所示:
+
+| **函数** | **描述** |
+| --------------- | ---------------------------------- |
+| rt_device_find() | 根据 I2C 总线设备名称查找设备获取设备句柄 |
+| rt_i2c_transfer() | 传输数据 |
+
+### 查找 I2C 总线设备
+
+在使用 I2C 总线设备前需要根据 I2C 总线设备名称获取设备句柄,进而才可以操作 I2C 总线设备,查找设备函数如下所示,
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | I2C 总线设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+一般情况下,注册到系统的 I2C 设备名称为 i2c0 ,i2c1等,使用示例如下所示:
+
+```c
+#define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */
+struct rt_i2c_bus_device *i2c_bus; /* I2C总线设备句柄 */
+
+/* 查找I2C总线设备,获取I2C总线设备句柄 */
+i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
+```
+
+### 数据传输
+
+获取到 I2C 总线设备句柄就可以使用 `rt_i2c_transfer()` 进行数据传输。函数原型如下所示:
+
+```c
+rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,
+ struct rt_i2c_msg msgs[],
+ rt_uint32_t num);
+```
+
+| **参数** | **描述** |
+|--------------------|----------------------|
+| bus | I2C 总线设备句柄 |
+| msgs[] | 待传输的消息数组指针 |
+| num | 消息数组的元素个数 |
+| **返回** | —— |
+| 消息数组的元素个数 | 成功 |
+| 错误码 | 失败 |
+
+和 SPI 总线的自定义传输接口一样,I2C 总线的自定义传输接口传输的数据也是以一个消息为单位。参数 msgs[] 指向待传输的消息数组,用户可以自定义每条消息的内容,实现 I2C 总线所支持的 2 种不同的数据传输模式。如果主设备需要发送重复开始条件,则需要发送 2 个消息。
+
+> [!NOTE]
+> 注:此函数会调用 rt_mutex_take(), 不能在中断服务程序里面调用,会导致 assertion 报错。
+
+I2C 消息数据结构原型如下:
+
+```c
+struct rt_i2c_msg
+{
+ rt_uint16_t addr; /* 从机地址 */
+ rt_uint16_t flags; /* 读、写标志等 */
+ rt_uint16_t len; /* 读写数据字节数 */
+ rt_uint8_t *buf; /* 读写数据缓冲区指针 */
+}
+```
+
+从机地址 addr:支持 7 位和 10 位二进制地址,需查看不同设备的数据手册 。
+
+> [!NOTE]
+> 注:RT-Thread I2C 设备接口使用的从机地址均不包含读写位,读写位控制需修改标志 flags。
+
+标志 flags 可取值为以下宏定义,根据需要可以与其他宏使用位运算 “|” 组合起来使用。
+
+```c
+#define RT_I2C_WR 0x0000 /* 写标志 */
+#define RT_I2C_RD (1u << 0) /* 读标志 */
+#define RT_I2C_ADDR_10BIT (1u << 2) /* 10 位地址模式 */
+#define RT_I2C_NO_START (1u << 4) /* 无开始条件 */
+#define RT_I2C_IGNORE_NACK (1u << 5) /* 忽视 NACK */
+#define RT_I2C_NO_READ_ACK (1u << 6) /* 读的时候不发送 ACK */
+```
+
+使用示例如下所示:
+
+```c
+#define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */
+#define AHT10_ADDR 0x38 /* 从机地址 */
+struct rt_i2c_bus_device *i2c_bus; /* I2C总线设备句柄 */
+
+/* 查找I2C总线设备,获取I2C总线设备句柄 */
+i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
+
+/* 读传感器寄存器数据 */
+static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf)
+{
+ struct rt_i2c_msg msgs;
+
+ msgs.addr = AHT10_ADDR; /* 从机地址 */
+ msgs.flags = RT_I2C_RD; /* 读标志 */
+ msgs.buf = buf; /* 读写数据缓冲区指针 */
+ msgs.len = len; /* 读写数据字节数 */
+
+ /* 调用I2C设备接口传输数据 */
+ if (rt_i2c_transfer(bus, &msgs, 1) == 1)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+```
+
+## I2C 总线设备使用示例
+
+I2C 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 首先根据 I2C 设备名称查找 I2C 名称,获取设备句柄,然后初始化 aht10 传感器。
+
+2. 控制传感器的 2 的函数为写传感器寄存器 write_reg() 和读传感器寄存器 read_regs(),这两个函数分别调用了 rt_i2c_transfer() 传输数据。读取温湿度信息的函数 read_temp_humi() 则是调用这两个函数完成功能。
+
+```c
+/*
+ * 程序清单:这是一个 I2C 设备使用例程
+ * 例程导出了 i2c_aht10_sample 命令到控制终端
+ * 命令调用格式:i2c_aht10_sample i2c1
+ * 命令解释:命令第二个参数是要使用的I2C总线设备名称,为空则使用默认的I2C总线设备
+ * 程序功能:通过 I2C 设备读取温湿度传感器 aht10 的温湿度数据并打印
+*/
+
+#include
+#include
+
+#define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */
+#define AHT10_ADDR 0x38 /* 从机地址 */
+#define AHT10_CALIBRATION_CMD 0xE1 /* 校准命令 */
+#define AHT10_NORMAL_CMD 0xA8 /* 一般命令 */
+#define AHT10_GET_DATA 0xAC /* 获取数据命令 */
+
+static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */
+static rt_bool_t initialized = RT_FALSE; /* 传感器初始化状态 */
+
+/* 写传感器寄存器 */
+static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t *data)
+{
+ rt_uint8_t buf[3];
+ struct rt_i2c_msg msgs;
+
+ buf[0] = reg; //cmd
+ buf[1] = data[0];
+ buf[2] = data[1];
+
+ msgs.addr = AHT10_ADDR;
+ msgs.flags = RT_I2C_WR;
+ msgs.buf = buf;
+ msgs.len = 3;
+
+ /* 调用I2C设备接口传输数据 */
+ if (rt_i2c_transfer(bus, &msgs, 1) == 1)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+
+/* 读传感器寄存器数据 */
+static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf)
+{
+ struct rt_i2c_msg msgs;
+
+ msgs.addr = AHT10_ADDR;
+ msgs.flags = RT_I2C_RD;
+ msgs.buf = buf;
+ msgs.len = len;
+
+ /* 调用I2C设备接口传输数据 */
+ if (rt_i2c_transfer(bus, &msgs, 1) == 1)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+
+static void read_temp_humi(float *cur_temp, float *cur_humi)
+{
+ rt_uint8_t temp[6];
+
+ write_reg(i2c_bus, AHT10_GET_DATA, 0); /* 发送命令 */
+ rt_thread_mdelay(400);
+ read_regs(i2c_bus, 6, temp); /* 获取传感器数据 */
+
+ /* 湿度数据转换 */
+ *cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20);
+ /* 温度数据转换 */
+ *cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50;
+}
+
+static void aht10_init(const char *name)
+{
+ rt_uint8_t temp[2] = {0, 0};
+
+ /* 查找I2C总线设备,获取I2C总线设备句柄 */
+ i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
+
+ if (i2c_bus == RT_NULL)
+ {
+ rt_kprintf("can't find %s device!\n", name);
+ }
+ else
+ {
+ write_reg(i2c_bus, AHT10_NORMAL_CMD, temp);
+ rt_thread_mdelay(400);
+
+ temp[0] = 0x08;
+ temp[1] = 0x00;
+ write_reg(i2c_bus, AHT10_CALIBRATION_CMD, temp);
+ rt_thread_mdelay(400);
+ initialized = RT_TRUE;
+ }
+}
+
+static void i2c_aht10_sample(int argc, char *argv[])
+{
+ float humidity, temperature;
+ char name[RT_NAME_MAX];
+
+ humidity = 0.0;
+ temperature = 0.0;
+
+ if (argc == 2)
+ {
+ rt_strncpy(name, argv[1], RT_NAME_MAX);
+ }
+ else
+ {
+ rt_strncpy(name, AHT10_I2C_BUS_NAME, RT_NAME_MAX);
+ }
+
+ if (!initialized)
+ {
+ /* 传感器初始化 */
+ aht10_init(name);
+ }
+ if (initialized)
+ {
+ /* 读取温湿度数据 */
+ read_temp_humi(&temperature, &humidity);
+
+ rt_kprintf("read aht10 sensor humidity : %d.%d %%\n", (int)humidity, (int)(humidity * 10) % 10);
+ if( temperature >= 0 )
+ {
+ rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(temperature * 10) % 10);
+ }
+ else
+ {
+ rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(-temperature * 10) % 10);
+ }
+ }
+ else
+ {
+ rt_kprintf("initialize sensor failed!\n");
+ }
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(i2c_aht10_sample, i2c aht10 sample);
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pin/figures/pin.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/pin/figures/pin.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..9f05478fadbfd3be77e6a409c1dddbd9a36c298f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/pin/figures/pin.vsdx differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pin/figures/pin2.png b/rt-thread-version/rt-thread-standard/programming-manual/device/pin/figures/pin2.png
new file mode 100644
index 0000000000000000000000000000000000000000..79d7dfc94c42e763184e9b8edce8f9bf937cd85f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/pin/figures/pin2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin.md b/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin.md
new file mode 100644
index 0000000000000000000000000000000000000000..a7d764335f86599e40b98addde1bd69efc2a9a5c
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin.md
@@ -0,0 +1,353 @@
+# PIN 设备
+
+## 引脚简介
+
+芯片上的引脚一般分为 4 类:电源、时钟、控制与 I/O,I/O 口在使用模式上又分为 General Purpose Input Output(通用输入 / 输出),简称 GPIO,与功能复用 I/O(如 SPI/I2C/UART 等)。
+
+大多数 MCU 的引脚都不止一个功能。不同引脚内部结构不一样,拥有的功能也不一样。可以通过不同的配置,切换引脚的实际功能。通用 I/O 口主要特性如下:
+
+* 可编程控制中断:中断触发模式可配置,一般有下图所示 5 种中断触发模式:
+
+
+
+* 输入输出模式可控制。
+
+ * 输出模式一般包括:推挽、开漏、上拉、下拉。引脚为输出模式时,可以通过配置引脚输出的电平状态为高电平或低电平来控制连接的外围设备。
+
+ * 输入模式一般包括:浮空、上拉、下拉、模拟。引脚为输入模式时,可以读取引脚的电平状态,即高电平或低电平。
+
+## 访问 PIN 设备
+
+应用程序通过 RT-Thread 提供的 PIN 设备管理接口来访问 GPIO,相关接口如下所示:
+
+| **函数** | **描述** |
+| ---------------- | ---------------------------------- |
+| rt_pin_mode() | 设置引脚模式 |
+| rt_pin_write() | 设置引脚电平 |
+| rt_pin_read() | 读取引脚电平 |
+| rt_pin_attach_irq() | 绑定引脚中断回调函数 |
+| rt_pin_irq_enable() | 使能引脚中断 |
+| rt_pin_detach_irq() | 脱离引脚中断回调函数 |
+
+### 获取引脚编号
+
+RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。有2种方式可以获取引脚编号:使用宏定义或者查看PIN 驱动文件。
+
+#### 使用宏定义
+
+如果使用 `rt-thread/bsp/stm32` 目录下的 BSP 则可以使用下面的宏获取引脚编号:
+
+```c
+GET_PIN(port, pin)
+```
+
+获取引脚号为 PF9 的 LED0 对应的引脚编号的示例代码如下所示:
+
+```c
+#define LED0_PIN GET_PIN(F, 9)
+```
+
+#### 查看驱动文件
+
+如果使用其他 BSP 则需要查看 PIN 驱动代码 drv_gpio.c 文件确认引脚编号。此文件里有一个数组存放了每个 PIN 脚对应的编号信息,如下所示:
+
+```c
+static const rt_uint16_t pins[] =
+{
+ __STM32_PIN_DEFAULT,
+ __STM32_PIN_DEFAULT,
+ __STM32_PIN(2, A, 15),
+ __STM32_PIN(3, B, 5),
+ __STM32_PIN(4, B, 8),
+ __STM32_PIN_DEFAULT,
+ __STM32_PIN_DEFAULT,
+ __STM32_PIN_DEFAULT,
+ __STM32_PIN(8, A, 14),
+ __STM32_PIN(9, B, 6),
+ ... ...
+}
+```
+
+以`__STM32_PIN(2, A, 15)`为例,2 为 RT-Thread 使用的引脚编号,A 为端口号,15 为引脚号,所以 PA15 对应的引脚编号为 2。
+
+### 设置引脚模式
+
+引脚在使用前需要先设置好输入或者输出模式,通过如下函数完成:
+
+```c
+void rt_pin_mode(rt_base_t pin, rt_base_t mode);
+```
+
+| **参数** | **描述** |
+|----------|--------------|
+| pin | 引脚编号 |
+| mode | 引脚工作模式 |
+
+目前 RT-Thread 支持的引脚工作模式可取如所示的 5 种宏定义值之一,每种模式对应的芯片实际支持的模式需参考 PIN 设备驱动程序的具体实现:
+
+```c
+#define PIN_MODE_OUTPUT 0x00 /* 输出 */
+#define PIN_MODE_INPUT 0x01 /* 输入 */
+#define PIN_MODE_INPUT_PULLUP 0x02 /* 上拉输入 */
+#define PIN_MODE_INPUT_PULLDOWN 0x03 /* 下拉输入 */
+#define PIN_MODE_OUTPUT_OD 0x04 /* 开漏输出 */
+```
+
+使用示例如下所示:
+
+```c
+#define BEEP_PIN_NUM 35 /* PB0 */
+
+/* 蜂鸣器引脚为输出模式 */
+rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
+```
+
+### 设置引脚电平
+
+设置引脚输出电平的函数如下所示:
+
+```c
+void rt_pin_write(rt_base_t pin, rt_base_t value);
+```
+
+| **参数** | **描述** |
+|----------|-------------------------|
+| pin | 引脚编号 |
+| value | 电平逻辑值,可取 2 种宏定义值之一:PIN_LOW 低电平,PIN_HIGH 高电平 |
+
+使用示例如下所示:
+
+```c
+#define BEEP_PIN_NUM 35 /* PB0 */
+
+/* 蜂鸣器引脚为输出模式 */
+rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
+/* 设置低电平 */
+rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
+```
+
+### 读取引脚电平
+
+读取引脚电平的函数如下所示:
+
+```c
+int rt_pin_read(rt_base_t pin);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| pin | 引脚编号 |
+| **返回** | —— |
+| PIN_LOW | 低电平 |
+| PIN_HIGH | 高电平 |
+
+使用示例如下所示:
+
+```c
+#define BEEP_PIN_NUM 35 /* PB0 */
+int status;
+
+/* 蜂鸣器引脚为输出模式 */
+rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
+/* 设置低电平 */
+rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
+
+status = rt_pin_read(BEEP_PIN_NUM);
+```
+
+### 绑定引脚中断回调函数
+
+若要使用到引脚的中断功能,可以使用如下函数将某个引脚配置为某种中断触发模式并绑定一个中断回调函数到对应引脚,当引脚中断发生时,就会执行回调函数:
+
+```c
+rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
+ void (*hdr)(void *args), void *args);
+```
+
+| **参数** | **描述** |
+|----------|--------------------------------------|
+| pin | 引脚编号 |
+| mode | 中断触发模式 |
+| hdr | 中断回调函数,用户需要自行定义这个函数 |
+| args | 中断回调函数的参数,不需要时设置为 RT_NULL |
+| **返回** | —— |
+| RT_EOK | 绑定成功 |
+| 错误码 | 绑定失败 |
+
+中断触发模式 mode 可取如下 5 种宏定义值之一:
+
+```c
+#define PIN_IRQ_MODE_RISING 0x00 /* 上升沿触发 */
+#define PIN_IRQ_MODE_FALLING 0x01 /* 下降沿触发 */
+#define PIN_IRQ_MODE_RISING_FALLING 0x02 /* 边沿触发(上升沿和下降沿都触发)*/
+#define PIN_IRQ_MODE_HIGH_LEVEL 0x03 /* 高电平触发 */
+#define PIN_IRQ_MODE_LOW_LEVEL 0x04 /* 低电平触发 */
+```
+
+使用示例如下所示:
+
+```c
+#define KEY0_PIN_NUM 55 /* PD8 */
+/* 中断回调函数 */
+void beep_on(void *args)
+{
+ rt_kprintf("turn on beep!\n");
+
+ rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
+}
+static void pin_beep_sample(void)
+{
+ /* 按键0引脚为输入模式 */
+ rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
+ /* 绑定中断,上升沿模式,回调函数名为beep_on */
+ rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
+}
+```
+
+### 使能引脚中断
+
+绑定好引脚中断回调函数后使用下面的函数使能引脚中断:
+
+```c
+rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled);
+```
+
+| **参数** | **描述** |
+|----------|----------------|
+| pin | 引脚编号 |
+| enabled | 状态,可取 2 种值之一:PIN_IRQ_ENABLE(开启),PIN_IRQ_DISABLE(关闭) |
+| **返回** | —— |
+| RT_EOK | 使能成功 |
+| 错误码 | 使能失败 |
+
+使用示例如下所示:
+
+```c
+#define KEY0_PIN_NUM 55 /* PD8 */
+/* 中断回调函数 */
+void beep_on(void *args)
+{
+ rt_kprintf("turn on beep!\n");
+
+ rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
+}
+static void pin_beep_sample(void)
+{
+ /* 按键0引脚为输入模式 */
+ rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
+ /* 绑定中断,上升沿模式,回调函数名为beep_on */
+ rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
+ /* 使能中断 */
+ rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
+}
+```
+
+### 脱离引脚中断回调函数
+
+可以使用如下函数脱离引脚中断回调函数:
+
+```c
+rt_err_t rt_pin_detach_irq(rt_int32_t pin);
+```
+
+| **参数** | **描述** |
+|----------|----------|
+| pin | 引脚编号 |
+| **返回** | —— |
+| RT_EOK | 脱离成功 |
+| 错误码 | 脱离失败 |
+
+引脚脱离了中断回调函数以后,中断并没有关闭,还可以调用绑定中断回调函数再次绑定其他回调函数。
+
+```c
+#define KEY0_PIN_NUM 55 /* PD8 */
+/* 中断回调函数 */
+void beep_on(void *args)
+{
+ rt_kprintf("turn on beep!\n");
+
+ rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
+}
+static void pin_beep_sample(void)
+{
+ /* 按键0引脚为输入模式 */
+ rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
+ /* 绑定中断,上升沿模式,回调函数名为beep_on */
+ rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
+ /* 使能中断 */
+ rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
+ /* 脱离中断回调函数 */
+ rt_pin_detach_irq(KEY0_PIN_NUM);
+}
+```
+
+## PIN 设备使用示例
+
+PIN 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 设置蜂鸣器对应引脚为输出模式,并给一个默认的低电平状态。
+
+2. 设置按键 0 和 按键1 对应引脚为输入模式,然后绑定中断回调函数并使能中断。
+
+3. 按下按键 0 蜂鸣器开始响,按下按键 1 蜂鸣器停止响。
+
+```c
+/*
+ * 程序清单:这是一个 PIN 设备使用例程
+ * 例程导出了 pin_beep_sample 命令到控制终端
+ * 命令调用格式:pin_beep_sample
+ * 程序功能:通过按键控制蜂鸣器对应引脚的电平状态控制蜂鸣器
+*/
+
+#include
+#include
+
+/* 引脚编号,通过查看设备驱动文件drv_gpio.c确定 */
+#ifndef BEEP_PIN_NUM
+ #define BEEP_PIN_NUM 35 /* PB0 */
+#endif
+#ifndef KEY0_PIN_NUM
+ #define KEY0_PIN_NUM 55 /* PD8 */
+#endif
+#ifndef KEY1_PIN_NUM
+ #define KEY1_PIN_NUM 56 /* PD9 */
+#endif
+
+void beep_on(void *args)
+{
+ rt_kprintf("turn on beep!\n");
+
+ rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
+}
+
+void beep_off(void *args)
+{
+ rt_kprintf("turn off beep!\n");
+
+ rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
+}
+
+static void pin_beep_sample(void)
+{
+ /* 蜂鸣器引脚为输出模式 */
+ rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
+ /* 默认低电平 */
+ rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
+
+ /* 按键0引脚为输入模式 */
+ rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
+ /* 绑定中断,下降沿模式,回调函数名为beep_on */
+ rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
+ /* 使能中断 */
+ rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
+
+ /* 按键1引脚为输入模式 */
+ rt_pin_mode(KEY1_PIN_NUM, PIN_MODE_INPUT_PULLUP);
+ /* 绑定中断,下降沿模式,回调函数名为beep_off */
+ rt_pin_attach_irq(KEY1_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_off, RT_NULL);
+ /* 使能中断 */
+ rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE);
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(pin_beep_sample, pin beep sample);
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pulse_encoder/pulse_encoder.md b/rt-thread-version/rt-thread-standard/programming-manual/device/pulse_encoder/pulse_encoder.md
new file mode 100644
index 0000000000000000000000000000000000000000..286f2a5c08a581047ae7500adeb5ab64eb8559aa
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/pulse_encoder/pulse_encoder.md
@@ -0,0 +1,222 @@
+# Pulse Encoder 设备
+
+## 脉冲编码器简介
+
+脉冲编码器是利用光学、磁性或机械接点的方式感测位置,并将位置信息转换为电子信号后输出的传感器。其输出的电子信号一般被用作控制位置时的回授信号。
+
+脉冲编码器按照工作原理可分为增量式和绝对式两类。增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。绝对式编码器的每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。当代大多数的微控制器都提供了编码器外设用于接收存储脉冲编码器的信号。
+
+## 访问脉冲编码器设备
+
+应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问脉冲编码器设备,相关接口如下所示:
+
+| **函数** | **描述** |
+| -------------------- | ---------------------------------- |
+| rt_device_find() | 查找脉冲编码器设备 |
+| rt_device_open() | 打开脉冲编码器设备(仅支持只读的方式) |
+| rt_device_control() | 控制脉冲编码器设备,可以清空计数值、获取类型、使能设备。 |
+| rt_device_read() | 获取脉冲编码器当前值 |
+| rt_device_close() | 关闭脉冲编码器设备 |
+
+### 查找脉冲编码器设备
+
+应用程序根据脉冲编码器的设备名称获取设备句柄,进而可以操作脉冲编码器设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | 脉冲编码器的设备名称 |
+| **返回** | —— |
+| 脉冲编码器设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到设备 |
+
+一般情况下,注册到系统的脉冲编码器的设备名称为 pulse1,pulse2等,使用示例如下所示:
+
+```c
+#define PULSE_ENCODER_DEV_NAME "pulse1" /* 脉冲编码器名称 */
+rt_device_t pulse_encoder_dev; /* 脉冲编码器设备句柄 */
+/* 查找脉冲编码器设备 */
+pulse_encoder_dev = rt_device_find(PULSE_ENCODER_DEV_NAME);
+```
+
+### 打开脉冲编码器设备
+
+通过设备句柄,应用程序可以打开设备。打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 脉冲编码器设备句柄 |
+| oflags | 设备打开模式,一般以只读方式打开,即取值:RT_DEVICE_OFLAG_RDONLY |
+| **返回** | —— |
+| RT_EOK | 设备打开成功 |
+| 其他错误码 | 设备打开失败 |
+
+使用示例如下所示:
+
+```c
+#define PULSE_ENCODER_DEV_NAME "pulse1" /* 脉冲编码器名称 */
+rt_device_t pulse_encoder_dev; /* 脉冲编码器设备句柄 */
+/* 查找脉冲编码器设备 */
+pulse_encoder_dev = rt_device_find(PULSE_ENCODER_DEV_NAME);
+/* 以只读方式打开设备 */
+rt_device_open(pulse_encoder_dev, RT_DEVICE_OFLAG_RDONLY);
+```
+
+### 控制脉冲编码器设备
+
+通过命令控制字,应用程序可以对脉冲编码器设备进行设置,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| cmd | 命令控制字 |
+| arg | 控制的参数 |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+脉冲编码器设备支持的命令控制字如下所示:
+
+| **控制字** | **描述** |
+| ---------------------- | ------------------------ |
+| PULSE_ENCODER_CMD_GET_TYPE | 获取脉冲编码器类型 |
+| PULSE_ENCODER_CMD_ENABLE | 使能脉冲编码器 |
+| PULSE_ENCODER_CMD_DISABLE | 失能脉冲编码器 |
+| PULSE_ENCODER_CMD_CLEAR_COUNT | 清空编码器计数值 |
+
+### 读取脉冲编码器计数值
+
+通过如下函数可以读取脉冲编码器计数值:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| pos | 固定值为 0 |
+| buffer | rt_int32_t 类型变量的地址,用于存放脉冲编码器的值。 |
+| size | 固定值为 1 |
+| **返回** | —— |
+| 固定返回值 | 返回 1 |
+
+使用示例如下所示:
+
+```c
+#define PULSE_ENCODER_DEV_NAME "pulse1" /* 脉冲编码器名称 */
+rt_device_t pulse_encoder_dev; /* 脉冲编码器设备句柄 */
+rt_int32_t count;
+/* 查找脉冲编码器设备 */
+pulse_encoder_dev = rt_device_find(PULSE_ENCODER_DEV_NAME);
+/* 以只读方式打开设备 */
+rt_device_open(pulse_encoder_dev, RT_DEVICE_OFLAG_RDONLY);
+/* 读取脉冲编码器计数值 */
+rt_device_read(pulse_encoder_dev, 0, &count, 1);
+```
+
+### 关闭脉冲编码器设备
+
+通过如下函数可以关闭脉冲编码器设备:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| dev | 脉冲编码器设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+ 关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+ 使用示例如下所示:
+
+```c
+#define PULSE_ENCODER_DEV_NAME "pulse1" /* 脉冲编码器名称 */
+rt_device_t pulse_encoder_dev; /* 脉冲编码器设备句柄 */
+/* 查找定时器设备 */
+pulse_encoder_dev = rt_device_find(PULSE_ENCODER_DEV_NAME);
+... ...
+rt_device_close(pulse_encoder_dev);
+```
+
+## 脉冲编码器设备使用示例
+
+脉冲编码器设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 首先根据脉冲编码器的设备名称 “pulse1” 查找设备获取设备句柄。
+
+2. 以只读方式打开设备 “pulse1” 。
+
+3. 读取脉冲编码器设备的计数值。
+
+4. 清空脉冲编码器的计数值。(可选步骤)
+
+```c
+/*
+ * 程序清单:这是一个脉冲编码器设备使用例程
+ * 例程导出了 pulse_encoder_sample 命令到控制终端
+ * 命令调用格式:pulse_encoder_sample
+ * 程序功能:每隔 500 ms 读取一次脉冲编码器外设的计数值,然后清空计数值,将读取到的计数值打印出来。
+*/
+
+#include
+#include
+
+#define PULSE_ENCODER_DEV_NAME "pulse1" /* 脉冲编码器名称 */
+
+static int pulse_encoder_sample(int argc, char *argv[])
+{
+ rt_err_t ret = RT_EOK;
+ rt_device_t pulse_encoder_dev = RT_NULL; /* 脉冲编码器设备句柄 */
+
+ rt_int32_t count;
+
+ /* 查找脉冲编码器设备 */
+ pulse_encoder_dev = rt_device_find(PULSE_ENCODER_DEV_NAME);
+ if (pulse_encoder_dev == RT_NULL)
+ {
+ rt_kprintf("pulse encoder sample run failed! can't find %s device!\n", PULSE_ENCODER_DEV_NAME);
+ return RT_ERROR;
+ }
+
+ /* 以只读方式打开设备 */
+ ret = rt_device_open(pulse_encoder_dev, RT_DEVICE_OFLAG_RDONLY);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("open %s device failed!\n", PULSE_ENCODER_DEV_NAME);
+ return ret;
+ }
+
+ for(rt_uint32 i; i <= 10; i++)
+ {
+ rt_thread_mdelay(500);
+ /* 读取脉冲编码器计数值 */
+ rt_device_read(pulse_encoder_dev, 0, &count, 1);
+ /* 清空脉冲编码器计数值 */
+ rt_device_control(pulse_encoder_dev, PULSE_ENCODER_CMD_CLEAR_COUNT, RT_NULL);
+ rt_kprintf("get count %d\n",count);
+ }
+
+ rt_device_close(pulse_encoder_dev);
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(pulse_encoder_sample, pulse encoder sample);
+```
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm-f.png b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm-f.png
new file mode 100644
index 0000000000000000000000000000000000000000..a107af8c7dadb726ec4e0031d2ea20996095c451
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm-f.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm-l.png b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm-l.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1927459bb51ebf9d35dad5a1da5a8b9dad4dd39
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm-l.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..a3f03017f5b2d40c5256e7274bb999af8ba95649
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/figures/pwm.vsdx differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/pwm.md b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/pwm.md
new file mode 100644
index 0000000000000000000000000000000000000000..5b6c4e47e52245cc95ed12a809c10fb061a7bccb
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/pwm.md
@@ -0,0 +1,265 @@
+# PWM 设备
+
+## PWM 简介
+
+PWM(Pulse Width Modulation , 脉冲宽度调制) 是一种对模拟信号电平进行数字编码的方法,通过不同频率的脉冲使用方波的占空比用来对一个具体模拟信号的电平进行编码,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替所需要波形的设备。
+
+
+
+上图是一个简单的 PWM 原理示意图,假定定时器工作模式为向上计数,当计数值小于阈值时,则输出一种电平状态,比如高电平,当计数值大于阈值时则输出相反的电平状态,比如低电平。当计数值达到最大值是,计数器从0开始重新计数,又回到最初的电平状态。高电平持续时间(脉冲宽度)和周期时间的比值就是占空比,范围为0~100%。上图高电平的持续时间刚好是周期时间的一半,所以占空比为50%。
+
+一个比较常用的pwm控制情景就是用来调节灯或者屏幕的亮度,根据占空比的不同,就可以完成亮度的调节。PWM调节亮度并不是持续发光的,而是在不停地点亮、熄灭屏幕。当亮、灭交替够快时,肉眼就会认为一直在亮。在亮、灭的过程中,灭的状态持续时间越长,屏幕给肉眼的观感就是亮度越低。亮的时间越长,灭的时间就相应减少,屏幕就会变亮。
+
+
+
+## 访问 PWM 设备
+
+应用程序通过 RT-Thread 提供的 PWM 设备管理接口来访问 PWM 设备硬件,相关接口如下所示:
+
+| **函数** | **描述** |
+| ----------------- | ---------------------------------- |
+| rt_device_find() | 根据 PWM 设备名称查找设备获取设备句柄 |
+| rt_pwm_set() | 设置 PWM 周期和脉冲宽度 |
+| rt_pwm_enable() | 使能 PWM 设备 |
+| rt_pwm_disable() | 关闭 PWM 设备 |
+
+### 查找 PWM 设备
+
+应用程序根据 PWM 设备名称获取设备句柄,进而可以操作 PWM 设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到设备 |
+
+一般情况下,注册到系统的 PWM 设备名称为 pwm0,pwm1等,使用示例如下所示:
+
+```c
+#define PWM_DEV_NAME "pwm3" /* PWM 设备名称 */
+struct rt_device_pwm *pwm_dev; /* PWM 设备句柄 */
+/* 查找设备 */
+pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
+```
+
+### 设置 PWM 周期和脉冲宽度
+
+通过如下函数设置 PWM 周期和占空比:
+
+```c
+rt_err_t rt_pwm_set(struct rt_device_pwm *device,
+ int channel,
+ rt_uint32_t period,
+ rt_uint32_t pulse);
+```
+
+| **参数** | **描述** |
+| ---------- | ----------------- |
+| device | PWM 设备句柄 |
+| channel | PWM 通道 |
+| period | PWM 周期时间 (单位纳秒 ns) |
+| pulse | PWM 脉冲宽度时间 (单位纳秒 ns) |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| -RT_EIO | device 为空 |
+| -RT_ENOSYS | 设备操作方法为空 |
+| 其他错误码 | 执行失败 |
+
+PWM 的输出频率由周期时间 period 决定,例如周期时间为 0.5ms (毫秒),则 period 值为 500000ns(纳秒),输出频率为 2KHz,占空比为 pulse / period,pulse 值不能超过 period。
+
+使用示例如下所示:
+
+```c
+#define PWM_DEV_NAME "pwm3" /* PWM设备名称 */
+#define PWM_DEV_CHANNEL 4 /* PWM通道 */
+struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
+rt_uint32_t period, pulse;
+
+period = 500000; /* 周期为0.5ms,单位为纳秒ns */
+pulse = 0; /* PWM脉冲宽度值,单位为纳秒ns */
+/* 查找设备 */
+pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
+/* 设置PWM周期和脉冲宽度 */
+rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
+```
+
+### 使能 PWM 设备
+
+设置好 PWM 周期和脉冲宽度后就可以通过如下函数使能 PWM 设备:
+
+```c
+rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| device | PWM 设备句柄 |
+| channel | PWM 通道 |
+| **返回** | —— |
+| RT_EOK | 设备使能成功 |
+| -RT_ENOSYS | 设备操作方法为空 |
+| 其他错误码 | 设备使能失败 |
+
+使用示例如下所示:
+
+```c
+#define PWM_DEV_NAME "pwm3" /* PWM设备名称 */
+#define PWM_DEV_CHANNEL 4 /* PWM通道 */
+struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
+rt_uint32_t period, pulse;
+
+period = 500000; /* 周期为0.5ms,单位为纳秒ns */
+pulse = 0; /* PWM脉冲宽度值,单位为纳秒ns */
+/* 查找设备 */
+pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
+/* 设置PWM周期和脉冲宽度 */
+rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
+/* 使能设备 */
+rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
+```
+
+### 关闭 PWM 设备通道
+
+通过如下函数关闭 PWM 设备对应通道。
+
+```c
+rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| device | PWM 设备句柄 |
+| channel | PWM 通道 |
+| **返回** | —— |
+| RT_EOK | 设备关闭成功 |
+| -RT_EIO | 设备句柄为空 |
+| 其他错误码 | 设备关闭失败 |
+
+使用示例如下所示:
+
+```c
+#define PWM_DEV_NAME "pwm3" /* PWM设备名称 */
+#define PWM_DEV_CHANNEL 4 /* PWM通道 */
+struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
+rt_uint32_t period, pulse;
+
+period = 500000; /* 周期为0.5ms,单位为纳秒ns */
+pulse = 0; /* PWM脉冲宽度值,单位为纳秒ns */
+/* 查找设备 */
+pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
+/* 设置PWM周期和脉冲宽度 */
+rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
+/* 使能设备 */
+rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
+/* 关闭设备通道 */
+rt_pwm_disable(pwm_dev,PWM_DEV_CHANNEL);
+```
+
+
+
+## FinSH 命令
+
+设置 PWM 设备的某个通道的周期和占空比可使用命令`pwm_set pwm1 1 500000 5000`,第一个参数为命令,第二个参数为 PWM 设备名称,第 3 个参数为 PWM 通道,第 4 个参数为周期(单位纳秒),第 5 个参数为脉冲宽度(单位纳秒)。
+
+```c
+msh />pwm_set pwm1 1 500000 5000
+msh />
+```
+
+使能 PWM 设备的某个通道可使用命令`pwm_enable pwm1 1`,第一个参数为命令,第二个参数为 PWM 设备名称,第 3 个参数为 PWM 通道。
+
+```c
+msh />pwm_enable pwm1 1
+msh />
+```
+
+关闭 PWM 设备的某个通道可使用命令`pwm_disable pwm1 1`,第一个参数为命令,第二个参数为 PWM 设备名称,第 3 个参数为 PWM 通道。
+
+```c
+msh />pwm_disable pwm1 1
+msh />
+```
+
+## PWM 设备使用示例
+
+PWM 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 查找 PWM 设备获取设备句柄。
+
+2. 设置 PWM 周期和脉冲宽度。
+
+3. 使能 PWM 设备。
+
+4. while 循环里每 50 毫秒修改一次脉冲宽度。
+
+* 将 PWM通道对应引脚和 LED 对应引脚相连,可以看到 LED 不停的由暗变到亮,然后又从亮变到暗。
+
+```c
+/*
+ * 程序清单:这是一个 PWM 设备使用例程
+ * 例程导出了 pwm_led_sample 命令到控制终端
+ * 命令调用格式:pwm_led_sample
+ * 程序功能:通过 PWM 设备控制 LED 灯的亮度,可以看到LED不停的由暗变到亮,然后又从亮变到暗。
+*/
+
+#include
+#include
+
+#define PWM_DEV_NAME "pwm3" /* PWM设备名称 */
+#define PWM_DEV_CHANNEL 4 /* PWM通道 */
+
+struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */
+
+static int pwm_led_sample(int argc, char *argv[])
+{
+ rt_uint32_t period, pulse, dir;
+
+ period = 500000; /* 周期为0.5ms,单位为纳秒ns */
+ dir = 1; /* PWM脉冲宽度值的增减方向 */
+ pulse = 0; /* PWM脉冲宽度值,单位为纳秒ns */
+
+ /* 查找设备 */
+ pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
+ if (pwm_dev == RT_NULL)
+ {
+ rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
+ return RT_ERROR;
+ }
+
+ /* 设置PWM周期和脉冲宽度默认值 */
+ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
+ /* 使能设备 */
+ rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
+
+ while (1)
+ {
+ rt_thread_mdelay(50);
+ if (dir)
+ {
+ pulse += 5000; /* 从0值开始每次增加5000ns */
+ }
+ else
+ {
+ pulse -= 5000; /* 从最大值开始每次减少5000ns */
+ }
+ if (pulse >= period)
+ {
+ dir = 0;
+ }
+ if (0 == pulse)
+ {
+ dir = 1;
+ }
+
+ /* 设置PWM周期和脉冲宽度 */
+ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
+ }
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(pwm_led_sample, pwm sample);
+```
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/rtc/rtc.md b/rt-thread-version/rt-thread-standard/programming-manual/device/rtc/rtc.md
new file mode 100644
index 0000000000000000000000000000000000000000..fabb7ec675369e25b8dddf0c4b5a9ceb2dce3712
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/rtc/rtc.md
@@ -0,0 +1,199 @@
+# RTC 设备
+
+## RTC 简介
+
+RTC (Real-Time Clock)实时时钟可以提供精确的实时时间,它可以用于产生年、月、日、时、分、秒等信息。目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。有些时钟芯片为了在主电源掉电时还可以工作,会外加电池供电,使时间信息一直保持有效。
+
+RT-Thread 的 RTC设备为操作系统的时间系统提供了基础服务。面对越来越多的 IoT 场景,RTC 已经成为产品的标配,甚至在诸如 SSL 的安全传输过程中,RTC 已经成为不可或缺的部分。
+
+
+## 访问 RTC 设备
+
+应用程序通过 RTC 设备管理接口来访问 RTC 硬件,相关接口如下所示:
+
+| **函数** | **描述** |
+| ------------- | ---------------------------------- |
+| set_date() | 设置日期,年、月、日 |
+| set_time() | 设置时间,时、分、秒 |
+| time() | 获取当前时间 |
+
+### 设置日期
+
+通过如下函数设置 RTC 设备当前日期值:
+
+```c
+rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day)
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+|year |待设置生效的年份|
+|month |待设置生效的月份|
+|day |待设置生效的日|
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+| -RT_ERROR | 失败,没有找到 rtc 设备 |
+| 其他错误码 | 失败 |
+
+使用示例如下所示:
+
+```c
+/* 设置日期为2018年12月3号 */
+set_date(2018, 12, 3);
+```
+
+### 设置时间
+
+通过如下函数设置 RTC 设备当前时间值:
+
+```c
+rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second)
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+|hour |待设置生效的时|
+|minute |待设置生效的分|
+|second |待设置生效的秒|
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+| -RT_ERROR | 失败,没有找到 rtc 设备 |
+| 其他错误码 | 失败 |
+
+使用示例如下所示:
+
+```c
+/* 设置时间为11点15分50秒 */
+set_time(11, 15, 50);
+```
+
+### 获取当前时间
+
+使用到 C 标准库中的时间 API 获取时间:
+
+```c
+time_t time(time_t *t)
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+|t |时间数据指针 |
+| **返回** | —— |
+| 当前时间值 | |
+
+使用示例如下所示:
+
+```c
+time_t now; /* 保存获取的当前时间值 */
+/* 获取时间 */
+now = time(RT_NULL);
+/* 打印输出时间信息 */
+rt_kprintf("%s\n", ctime(&now));
+```
+
+> [!NOTE]
+> 注:目前系统内只允许存在一个 RTC 设备,且名称为 `"rtc"` 。
+
+## 功能配置
+
+### 启用 Soft RTC (软件模拟 RTC)
+
+在 menuconfig 中可以启用使用软件模拟 RTC 的功能,这个模式非常适用于对时间精度要求不高,没有硬件 RTC 的产品。配置选项如下所示:
+
+```c
+RT-Thread Components →
+ Device Drivers:
+ -*- Using RTC device drivers /* 使用 RTC 设备驱动 */
+ [ ] Using software simulation RTC device /* 使用软件模拟 RTC */
+```
+
+### 启用 NTP 时间自动同步
+
+如果 RT-Thread 已接入互联网,可启用 NTP 时间自动同步功能,定期同步本地时间。
+
+首先在 menuconfig 中按照如下选项开启 NTP 功能:
+
+```c
+RT-Thread online packages →
+ IoT - internet of things →
+ netutils: Networking utilities for RT-Thread:
+ [*] Enable NTP(Network Time Protocol) client
+```
+
+开启 NTP 后 RTC 的自动同步功能将会自动开启,还可以设置同步周期和首次同步的延时时间:
+
+```c
+RT-Thread Components →
+ Device Drivers:
+ -*- Using RTC device drivers /* 使用 RTC 设备驱动 */
+ [ ] Using software simulation RTC device /* 使用软件模拟 RTC */
+ [*] Using NTP auto sync RTC time /* 使用 NTP 自动同步 RTC 时间 */
+ (30) NTP first sync delay time(second) for network connect /* 首次执行 NTP 时间同步的延时。延时的目的在于,给网络连接预留一定的时间,尽量提高第一次执行 NTP 时间同步时的成功率。默认时间为 30S; */
+ (3600) NTP auto sync period(second) /* NTP 自动同步周期,单位为秒,默认一小时(即 3600S)同步一次。 */
+```
+
+## FinSH 命令
+
+输入 `date` 即可查看当前时间,大致效果如下:
+
+```c
+msh />date
+Fri Feb 16 01:11:56 2018
+msh />
+```
+
+同样使用 `date` 命令,在命令后面再依次输入 ` 年 ` ` 月 ` ` 日 ` ` 时 ` ` 分 ` ` 秒 ` (中间空格隔开, 24H 制),设置当前时间为 2018-02-16 01:15:30,大致效果如下:
+
+```c
+msh />date 2018 02 16 01 15 30
+msh />
+```
+
+## RTC 设备使用示例
+
+RTC 设备的具体使用方式可以参考如下示例代码,首先设置了年月日时分秒信息,然后延时 3 秒后获取当前时间信息。
+
+```c
+/*
+ * 程序清单:这是一个 RTC 设备使用例程
+ * 例程导出了 rtc_sample 命令到控制终端
+ * 命令调用格式:rtc_sample
+ * 程序功能:设置RTC设备的日期和时间,延时一段时间后获取当前时间并打印显示。
+*/
+
+#include
+#include
+
+static int rtc_sample(int argc, char *argv[])
+{
+ rt_err_t ret = RT_EOK;
+ time_t now;
+
+ /* 设置日期 */
+ ret = set_date(2018, 12, 3);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("set RTC date failed\n");
+ return ret;
+ }
+
+ /* 设置时间 */
+ ret = set_time(11, 15, 50);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("set RTC time failed\n");
+ return ret;
+ }
+
+ /* 延时3秒 */
+ rt_thread_mdelay(3000);
+
+ /* 获取时间 */
+ now = time(RT_NULL);
+ rt_kprintf("%s\n", ctime(&now));
+
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(rtc_sample, rtc sample);
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor.md b/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor.md
new file mode 100644
index 0000000000000000000000000000000000000000..f6a4a0ddd7651512d0f8cbc7b73cba8c1dc44fdd
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor.md
@@ -0,0 +1,453 @@
+# SENSOR 设备
+
+## SENSOR 简介
+
+Sensor(传感器)是物联网重要的一部分,“Sensor 之于物联网”就相当于“眼睛之于人类”。人类如果没有了眼睛就看不到这大千的花花世界,对于物联网来说也是一样。
+
+如今随着物联网的发展,已经有大量的 Sensor 被开发出来供开发者选择了,如:加速度计(Accelerometer)、磁力计(Magnetometer)、陀螺仪(Gyroscope)、气压计(Barometer/pressure)、湿度计(Humidometer)等。这些传感器,世界上的各大半导体厂商都有生产,虽然增加了市场的可选择性,同时也加大了应用程序开发的难度。因为不同的传感器厂商、不同的传感器都需要配套自己独有的驱动才能运转起来,这样在开发应用程序的时候就需要针对不同的传感器做适配,自然加大了开发难度。为了降低应用开发的难度,增加传感器驱动的可复用性,我们设计了 Sensor 设备。
+
+Sensor 设备的作用是:为上层提供统一的操作接口,提高上层代码的可重用性。
+
+### 传感器设备特性
+
+- 接口:标准 device 接口(open/close/read/control)
+- 工作模式:支持 轮询、中断、FIFO 三种模式
+- 电源模式:支持 掉电、普通、低功耗、高功耗 四种模式
+
+点击[传感器列表](sensor_list.md),查看当前支持的传感器
+
+## 访问传感器设备
+
+应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问传感器设备,相关接口如下所示:
+
+| **函数** | **描述** |
+| --------------------------- | ------------------------------------------ |
+| rt_device_find() | 根据传感器设备设备名称查找设备获取设备句柄 |
+| rt_device_open() | 打开传感器设备 |
+| rt_device_read() | 读取数据 |
+| rt_device_control() | 控制传感器设备 |
+| rt_device_set_rx_indicate() | 设置接收回调函数 |
+| rt_device_close() | 关闭传感器设备 |
+
+### 查找传感器
+
+应用程序根据传感器设备名称获取设备句柄,进而可以操作传感器设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | 传感器设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+使用示例如下所示:
+```c
+#define SENSOR_DEVICE_NAME "acce_st" /* 传感器设备名称 */
+
+static rt_device_t sensor_dev; /* 传感器设备句柄 */
+/* 根据设备名称查找传感器设备,获取设备句柄 */
+sensor_dev = rt_device_find(SENSOR_DEVICE_NAME);
+```
+
+### 打开传感器设备
+
+通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 设备句柄 |
+| oflags | 设备模式标志 |
+| **返回** | —— |
+| RT_EOK | 设备打开成功 |
+| -RT_EBUSY | 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开 |
+| -RT_EINVAL | 不支持的打开参数 |
+| 其他错误码 | 设备打开失败 |
+
+oflags 参数支持下列参数:
+
+```c
+#define RT_DEVICE_FLAG_RDONLY 0x001 /* 标准设备的只读模式,对应传感器的轮询模式 */
+#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收模式 */
+#define RT_DEVICE_FLAG_FIFO_RX 0x200 /* FIFO 接收模式 */
+```
+
+传感器数据接收和发送数据的模式分为 3 种:中断模式、轮询模式、FIFO 模式。在使用的时候,这 3 种模式只能**选其一**,若传感器的打开参数 oflags 没有指定使用中断模式或者 FIFO 模式,则默认使用轮询模式。
+
+FIFO(First Input First Output)即先进先出。 FIFO 传输方式需要传感器硬件支持,数据存储在硬件 FIFO 中,一次读取可以读取多个数据,这就节省了 CPU 的资源来做其他操作。在低功耗模式时非常有用。
+
+若传感器要使用 FIFO 接收模式,oflags 取值 RT_DEVICE_FLAG_FIFO_RX。
+
+以轮询模式打开传感器设备使用示例如下所示:
+
+```c
+#define SAMPLE_SENSOR_NAME "acce_st" /* 传感器设备名称 */
+int main(void)
+{
+ rt_device_t dev;
+ struct rt_sensor_data data;
+
+ /* 查找传感器设备 */
+ dev = rt_device_find(SAMPLE_SENSOR_NAME);
+ /* 以只读及轮询模式打开传感器设备 */
+ rt_device_open(dev, RT_DEVICE_FLAG_RDONLY);
+
+ if (rt_device_read(dev, 0, &data, 1) == 1)
+ {
+ rt_kprintf("acce: x:%5d, y:%5d, z:%5d, timestamp:%5d\n", data.data.acce.x, data.data.acce.y, data.data.acce.z, data.timestamp);
+ }
+ rt_device_close(dev);
+
+ return RT_EOK;
+}
+```
+### 控制传感器设备
+
+通过命令控制字,应用程序可以对传感器设备进行配置,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| cmd | 命令控制字,详细介绍见下面 |
+| arg | 控制的参数, 详细介绍见下面 |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+其中的 cmd 目前支持以下几种命令控制字
+
+```c
+#define RT_SENSOR_CTRL_GET_ID /* 读设备ID */
+#define RT_SENSOR_CTRL_GET_INFO /* 获取设备信息 */
+#define RT_SENSOR_CTRL_SET_RANGE /* 设置传感器测量范围 */
+#define RT_SENSOR_CTRL_SET_ODR /* 设置传感器数据输出速率,unit is HZ */
+#define RT_SENSOR_CTRL_SET_POWER /* 设置电源模式 */
+#define RT_SENSOR_CTRL_SELF_TEST /* 自检 */
+```
+#### 获取设备信息
+
+```c
+struct rt_sensor_info info;
+rt_device_control(dev, RT_SENSOR_CTRL_GET_INFO, &info);
+LOG_I("vendor :%d", info.vendor);
+LOG_I("model :%s", info.model);
+LOG_I("unit :%d", info.unit);
+LOG_I("intf_type :%d", info.intf_type);
+LOG_I("period_min:%d", info.period_min);
+```
+
+#### 读设备ID
+
+```c
+rt_uint8_t reg = 0xFF;
+rt_device_control(dev, RT_SENSOR_CTRL_GET_ID, ®);
+LOG_I("device id: 0x%x!", reg);
+```
+
+#### 设置测量范围
+
+设置传感器测量范围时的单位为设备注册时提供的单位。
+
+```c
+rt_device_control(dev, RT_SENSOR_CTRL_SET_RANGE, (void *)1000);
+```
+
+#### 设置数据输出速率
+
+设置输出速率为 100HZ,调用下面的接口。
+
+```c
+rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)100);
+```
+
+#### 设置电源模式
+
+```c
+/* 设置电源模式为掉电模式 */
+rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_DOWN);
+/* 设置电源模式为普通模式 */
+rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_NORMAL);
+/* 设置电源模式为低功耗模式 */
+rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_LOW);
+/* 设置电源模式为高性能模式 */
+rt_device_control(dev, RT_SENSOR_CTRL_SET_POWER, (void *)RT_SEN_POWER_HIGH);
+```
+
+#### 设备自检
+
+```c
+int test_res;
+/* 控制设备自检 并把结果返回回来,RT_EOK 自检成功, 其他自检失败 */
+rt_device_control(dev, RT_SENSOR_CTRL_SELF_TEST, &test_res);
+```
+
+### 设置接收回调函数
+
+可以通过如下函数来设置数据接收指示,当传感器收到数据时,通知上层应用线程有数据到达 :
+
+```c
+rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
+```
+
+| **参数** | **描述** |
+| -------- | ------------ |
+| dev | 设备句柄 |
+| rx_ind | 回调函数指针 |
+| dev | 设备句柄(回调函数参数)|
+| size | 缓冲区数据大小(回调函数参数)|
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+
+该函数的回调函数由调用者提供。若传感器以中断接收模式打开,当传感器接收到数据产生中断时,就会调用回调函数,并且会把此时缓冲区的数据大小放在 size 参数里,把传感器设备句柄放在 dev 参数里供调用者获取。
+
+一般情况下接收回调函数可以发送一个信号量或者事件通知传感器数据处理线程有数据到达。使用示例如下所示:
+
+```c
+#define SAMPLE_SENSOR_NAME "acce_st" /* 传感器设备名称 */
+static rt_device_t dev; /* 传感器设备句柄 */
+static struct rt_semaphore rx_sem; /* 用于接收消息的信号量 */
+
+/* 接收数据回调函数 */
+static rt_err_t sensor_input(rt_device_t dev, rt_size_t size)
+{
+ /* 传感器接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
+ rt_sem_release(&rx_sem);
+
+ return RT_EOK;
+}
+
+static int sensor_sample(int argc, char *argv[])
+{
+ dev = rt_device_find(SAMPLE_SENSOR_NAME);
+
+ /* 以中断接收及轮询发送模式打开传感器设备 */
+ rt_device_open(dev, RT_DEVICE_FLAG_INT_RX);
+ /* 初始化信号量 */
+ rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
+
+ /* 设置接收回调函数 */
+ rt_device_set_rx_indicate(dev, sensor_input);
+}
+
+```
+
+### 读取数据
+
+可调用如下函数读取传感器接收到的数据:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ------------------ | ---------------------------------------------- |
+| dev | 设备句柄 |
+| pos | 读取数据偏移量,此参数传感器未使用 |
+| buffer | 缓冲区指针,读取的数据将会被保存在缓冲区中 |
+| size | 读取数据的大小 |
+| **返回** | —— |
+| 读到数据的实际大小 | 返回读取到数据的个数 |
+| 0 | 需要读取当前线程的 errno 来判断错误状态 |
+
+传感器使用中断接收模式并配合接收回调函数的使用示例如下所示:
+
+```c
+static rt_device_t dev; /* 传感器设备句柄 */
+static struct rt_semaphore rx_sem; /* 用于接收消息的信号量 */
+
+/* 接收数据的线程 */
+static void sensor_irq_rx_entry(void *parameter)
+{
+ rt_device_t dev = parameter;
+ struct rt_sensor_data data;
+ rt_size_t res;
+
+ while (1)
+ {
+ rt_sem_take(rx_sem, RT_WAITING_FOREVER);
+
+ res = rt_device_read(dev, 0, &data, 1);
+ if (res == 1)
+ {
+ sensor_show_data(dev, &data);
+ }
+ }
+}
+
+```
+
+传感器使用 FIFO 接收模式并配合接收回调函数的使用示例如下所示:
+
+```c
+static rt_sem_t sensor_rx_sem = RT_NULL;
+rt_err_t rx_cb(rt_device_t dev, rt_size_t size)
+{
+ rt_sem_release(sensor_rx_sem);
+ return 0;
+}
+static void sensor_fifo_rx_entry(void *parameter)
+{
+ rt_device_t dev = parameter;
+ struct rt_sensor_data data;
+ rt_size_t res, i;
+
+ data = rt_malloc(sizeof(struct rt_sensor_data) * 32);
+
+ while (1)
+ {
+ rt_sem_take(sensor_rx_sem, RT_WAITING_FOREVER);
+
+ res = rt_device_read(dev, 0, data, 32);
+ for (i = 0; i < res; i++)
+ {
+ sensor_show_data(dev, &data[i]);
+ }
+ }
+}
+int main(void)
+{
+ static rt_thread_t tid1 = RT_NULL;
+ rt_device_t dev;
+ struct rt_sensor_data data;
+
+ sensor_rx_sem = rt_sem_create("sen_rx_sem", 0, RT_IPC_FLAG_FIFO);
+ tid1 = rt_thread_create("sen_rx_thread",
+ sensor_fifo_rx_entry, dev,
+ 1024,
+ 15, 5);
+ if (tid1 != RT_NULL)
+ rt_thread_startup(tid1);
+
+ dev = rt_device_find("acce_st");
+ rt_device_set_rx_indicate(dev, rx_cb);
+ rt_device_open(dev, RT_SEN_FLAG_FIFO);
+ return RT_EOK;
+}
+```
+
+### 关闭传感器设备
+
+当应用程序完成传感器操作后,可以关闭传感器设备,通过如下函数完成:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| dev | 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+## 传感器设备使用示例
+
+传感器设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 首先查找传感器设置获取设备句柄。
+
+2. 以轮询的方式打开传感器。
+
+3. 连续读取 5 次数据并打印出来。
+
+4. 关闭传感器。
+
+* 此示例代码不局限于特定的 BSP,根据 BSP 注册的传感器设备,输入不同的 dev_name 即可运行。
+
+```c
+/*
+ * 程序清单:这是一个 传感器 设备使用例程
+ * 例程导出了 sensor_sample 命令到控制终端
+ * 命令调用格式:sensor_sample dev_name
+ * 命令解释:命令第二个参数是要使用的传感器设备名称
+ * 程序功能:打开对应的传感器,然后连续读取 5 次数据并打印出来。
+*/
+
+#include "sensor.h"
+
+static void sensor_show_data(rt_size_t num, rt_sensor_t sensor, struct rt_sensor_data *sensor_data)
+{
+ switch (sensor->info.type)
+ {
+ case RT_SENSOR_CLASS_ACCE:
+ rt_kprintf("num:%3d, x:%5d, y:%5d, z:%5d, timestamp:%5d\n", num, sensor_data->data.acce.x, sensor_data->data.acce.y, sensor_data->data.acce.z, sensor_data->timestamp);
+ break;
+ case RT_SENSOR_CLASS_GYRO:
+ rt_kprintf("num:%3d, x:%8d, y:%8d, z:%8d, timestamp:%5d\n", num, sensor_data->data.gyro.x, sensor_data->data.gyro.y, sensor_data->data.gyro.z, sensor_data->timestamp);
+ break;
+ case RT_SENSOR_CLASS_MAG:
+ rt_kprintf("num:%3d, x:%5d, y:%5d, z:%5d, timestamp:%5d\n", num, sensor_data->data.mag.x, sensor_data->data.mag.y, sensor_data->data.mag.z, sensor_data->timestamp);
+ break;
+ case RT_SENSOR_CLASS_HUMI:
+ rt_kprintf("num:%3d, humi:%3d.%d%%, timestamp:%5d\n", num, sensor_data->data.humi / 10, sensor_data->data.humi % 10, sensor_data->timestamp);
+ break;
+ case RT_SENSOR_CLASS_TEMP:
+ rt_kprintf("num:%3d, temp:%3d.%dC, timestamp:%5d\n", num, sensor_data->data.temp / 10, sensor_data->data.temp % 10, sensor_data->timestamp);
+ break;
+ case RT_SENSOR_CLASS_BARO:
+ rt_kprintf("num:%3d, press:%5d, timestamp:%5d\n", num, sensor_data->data.baro, sensor_data->timestamp);
+ break;
+ case RT_SENSOR_CLASS_STEP:
+ rt_kprintf("num:%3d, step:%5d, timestamp:%5d\n", num, sensor_data->data.step, sensor_data->timestamp);
+ break;
+ default:
+ break;
+ }
+}
+
+static void sensor_sample(int argc, char **argv)
+{
+ rt_device_t dev = RT_NULL;
+ struct rt_sensor_data data;
+ rt_size_t res, i;
+
+ /* 查找系统中的传感器设备 */
+ dev = rt_device_find(argv[1]);
+ if (dev == RT_NULL)
+ {
+ rt_kprintf("Can't find device:%s\n", argv[1]);
+ return;
+ }
+
+ /* 以轮询模式打开传感器设备 */
+ if (rt_device_open(dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
+ {
+ rt_kprintf("open device failed!");
+ return;
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ /* 从传感器读取一个数据 */
+ res = rt_device_read(dev, 0, &data, 1);
+ if (res != 1)
+ {
+ rt_kprintf("read data failed!size is %d", res);
+ }
+ else
+ {
+ sensor_show_data(i, (rt_sensor_t)dev, &data);
+ }
+ rt_thread_mdelay(100);
+ }
+ /* 关闭传感器设备 */
+ rt_device_close(dev);
+}
+MSH_CMD_EXPORT(sensor_sample, sensor device sample);
+```
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor_list.md b/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor_list.md
new file mode 100644
index 0000000000000000000000000000000000000000..8448d36be17876e6f1f4f2562d70f7510abb5d04
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/sensor/sensor_list.md
@@ -0,0 +1,34 @@
+# 支持的传感器列表
+
+下面是一份已经对接到 RT-Therad Sensor 框架上的传感器的列表,点击传感器名称即可跳转到相应软件包主页。(本文档不定时更新,如要查看所有支持的传感器的列表,可以查看最新的[软件包索引](https://github.com/RT-Thread/packages/tree/master/peripherals/sensors)。
+
+| 厂商 | 传感器 | 备注 |
+| -------------- | ------------------------------------------------------------ | ------------------------ |
+| **BOSCH** | | |
+| | **[bma400](https://github.com/RT-Thread-packages/bma400)** | 加速度计、计步计 |
+| | **[bmi160](https://github.com/RT-Thread-packages/bmi160_bmx160)** | 加速计、陀螺仪 |
+| | **[bmx160](https://github.com/RT-Thread-packages/bmi160_bmx160)** | 加速计、陀螺仪、磁力计 |
+| | **[bme280](https://github.com/RT-Thread-packages/bme280)** | 气压计、湿度计、温度计 |
+| **Goertek** | | |
+| | **[spl0601](https://github.com/RT-Thread-packages/spl0601)** | 气压计、温度计 |
+| **ST** | | |
+| | **[lsm6dsl](https://github.com/RT-Thread-packages/lsm6dsl)** | 加速度计、陀螺仪、计步计 |
+| | **[lsm303agr](https://github.com/RT-Thread-packages/lsm303agr)** | 加速度计、磁力计 |
+| | **[hts221](https://github.com/RT-Thread-packages/hts221)** | 气压计、气温计 |
+| | **[lps22hb](https://github.com/RT-Thread-packages/lps22hb)** | 气压计、气温计 |
+| **MiraMEMS** | | |
+| | **[da270]()** | 加速度计 |
+| | **[df220]()** | 压力计 |
+| **ALPSALPINE** | | |
+| | **[hshcal001]()** | 湿度计、温度计 |
+| **MEAS** | | |
+| | **[MS5611]()** | 气压计、温度计 |
+| **invensense** | | |
+| | **[mpu6xxx(mpu6050/mpu6000/icm20608)]()** | 加速度计、陀螺仪 |
+| **ASAIR** | | |
+| | **[aht10]()** | 温度计、湿度计 |
+| **ROHM** | | |
+| | **[BH1750FVI]()** | 环境光照强度 |
+| **Richtek** | | |
+| | **[RT3020](http://packages.rt-thread.org/itemDetail.html?package=rt3020)** | 加速度计 |
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..42657aab19fc401a6226f149216183a5acda5eb0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi.vsdx differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi1.png b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi1.png
new file mode 100644
index 0000000000000000000000000000000000000000..2096701713f23a386b6827ec3740f86b206da5df
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi2.png b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi2.png
new file mode 100644
index 0000000000000000000000000000000000000000..975196d0797036a4b791ad319f8b5bb1dc3cbe92
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi5.png b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi5.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e71536451ce4c025d2370d67e07a46ccb140c01
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/figures/spi5.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi.md b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi.md
new file mode 100644
index 0000000000000000000000000000000000000000..41b14869564dd49a1958d5f5ba2988fe89154389
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi.md
@@ -0,0 +1,741 @@
+# SPI 设备
+
+## SPI 简介
+
+SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步通信总线,常用于短距离通讯,主要应用于 EEPROM、FLASH、实时时钟、AD 转换器、还有数字信号处理器和数字信号解码器之间。SPI 一般使用 4 根线通信,如下图所示:
+
+
+
+* MOSI –主机输出 / 从机输入数据线(SPI Bus Master Output/Slave Input)。
+
+* MISO –主机输入 / 从机输出数据线(SPI Bus Master Input/Slave Output)。
+
+* SCLK –串行时钟线(Serial Clock),主设备输出时钟信号至从设备。
+
+* CS –从设备选择线 (Chip select)。也叫 SS、CSB、CSN、EN 等,主设备输出片选信号至从设备。
+
+SPI 以主从方式工作,通常有一个主设备和一个或多个从设备。通信由主设备发起,主设备通过 CS 选择要通信的从设备,然后通过 SCLK 给从设备提供时钟信号,数据通过 MOSI 输出给从设备,同时通过 MISO 接收从设备发送的数据。
+
+如下图所示芯片有 2 个 SPI 控制器,SPI 控制器对应 SPI 主设备,每个 SPI 控制器可以连接多个 SPI 从设备。挂载在同一个 SPI 控制器上的从设备共享 3 个信号引脚:SCK、MISO、MOSI,但每个从设备的 CS 引脚是独立的。
+
+
+
+主设备通过控制 CS 引脚对从设备进行片选,一般为低电平有效。任何时刻,一个 SPI 主设备上只有一个 CS 引脚处于有效状态,与该有效 CS 引脚连接的从设备此时可以与主设备通信。
+
+从设备的时钟由主设备通过 SCLK 提供,MOSI、MISO 则基于此脉冲完成数据传输。SPI 的工作时序模式由 CPOL(Clock Polarity,时钟极性)和 CPHA(Clock Phase,时钟相位)之间的相位关系决定,CPOL 表示时钟信号的初始电平的状态,CPOL 为 0 表示时钟信号初始状态为低电平,为 1 表示时钟信号的初始电平是高电平。CPHA 表示在哪个时钟沿采样数据,CPHA 为 0 表示在首个时钟变化沿采样数据,而 CPHA 为 1 则表示在第二个时钟变化沿采样数据。根据 CPOL 和 CPHA 的不同组合共有 4 种工作时序模式:①CPOL=0,CPHA=0、②CPOL=0,CPHA=1、③CPOL=1,CPHA=0、④CPOL=1,CPHA=1。如下图所示:
+
+
+
+**QSPI:** QSPI 是 Queued SPI 的简写,是 Motorola 公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议)。使用该接口,用户可以一次性传输包含多达 16 个 8 位或 16 位数据的传输队列。一旦传输启动,直到传输结束,都不需要 CPU 干预,极大的提高了传输效率。与 SPI 相比,QSPI 的最大结构特点是以 80 字节的 RAM 代替了 SPI 的发送和接收数据寄存器。
+
+**Dual SPI Flash:** 对于 SPI Flash 而言全双工并不常用,可以发送一个命令字节进入 Dual 模式,让它工作在半双工模式,用以加倍数据传输。这样 MOSI 变成 SIO0(serial io 0),MISO 变成 SIO1(serial io 1),这样一个时钟周期内就能传输 2 个 bit 数据,加倍了数据传输。
+
+**Quad SPI Flash:** 与 Dual SPI 类似,Quad SPI Flash增加了两根 I/O 线(SIO2,SIO3),目的是一个时钟内传输 4 个 bit 数据。
+
+所以对于 SPI Flash,有标准 SPI Flash,Dual SPI Flash, Quad SPI Flash 三种类型。在相同时钟下,线数越多传输速率越高。
+
+## 挂载 SPI 设备
+
+SPI 驱动会注册 SPI 总线,SPI 设备需要挂载到已经注册好的 SPI 总线上。
+
+```C
+rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
+ const char *name,
+ const char *bus_name,
+ void *user_data)
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| device | SPI 设备句柄 |
+| name | SPI 设备名称 |
+| bus_name | SPI 总线名称 |
+| user_data | 用户数据指针 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他错误码 | 失败 |
+
+此函数用于挂载一个 SPI 设备到指定的 SPI 总线,并向内核注册 SPI 设备,并将 user_data 保存到 SPI 设备的控制块里。
+
+一般 SPI 总线命名原则为 spix, SPI 设备命名原则为 spixy ,如 spi10 表示挂载在 spi1 总线上的 0 号设备。user_data 一般为 SPI 设备的 CS 引脚指针,进行数据传输时 SPI 控制器会操作此引脚进行片选。
+
+若使用 rt-thread/bsp/stm32 目录下的 BSP 则可以使用下面的函数挂载 SPI 设备到总线:
+
+```c
+rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef* cs_gpiox, uint16_t cs_gpio_pin);
+```
+
+下面的示例代码挂载 SPI FLASH W25Q128 到 SPI 总线:
+
+```c
+static int rt_hw_spi_flash_init(void)
+{
+ __HAL_RCC_GPIOB_CLK_ENABLE();
+ rt_hw_spi_device_attach("spi1", "spi10", GPIOB, GPIO_PIN_14);
+
+ if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
+ {
+ return -RT_ERROR;
+ };
+
+ return RT_EOK;
+}
+/* 导出到自动初始化 */
+INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
+```
+
+## 配置 SPI 设备
+
+挂载 SPI 设备到 SPI 总线后需要配置 SPI 设备的传输参数。
+
+```c
+rt_err_t rt_spi_configure(struct rt_spi_device *device,
+ struct rt_spi_configuration *cfg)
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| device | SPI 设备句柄 |
+| cfg | SPI 配置参数指针 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+
+此函数会保存 cfg 指向的配置参数到 SPI 设备 device 的控制块里,当传输数据时会使用此配置参数。
+
+struct rt_spi_configuration 原型如下:
+
+```c
+struct rt_spi_configuration
+{
+ rt_uint8_t mode; /* 模式 */
+ rt_uint8_t data_width; /* 数据宽度,可取8位、16位、32位 */
+ rt_uint16_t reserved; /* 保留 */
+ rt_uint32_t max_hz; /* 最大频率 */
+};
+```
+
+**模式:** 包含 MSB/LSB、主从模式、 时序模式等,可取宏组合如下:
+
+```c
+/* 设置数据传输顺序是MSB位在前还是LSB位在前 */
+#define RT_SPI_LSB (0<<2) /* bit[2]: 0-LSB */
+#define RT_SPI_MSB (1<<2) /* bit[2]: 1-MSB */
+
+/* 设置SPI的主从模式 */
+#define RT_SPI_MASTER (0<<3) /* SPI master device */
+#define RT_SPI_SLAVE (1<<3) /* SPI slave device */
+
+/* 设置时钟极性和时钟相位 */
+#define RT_SPI_MODE_0 (0 | 0) /* CPOL = 0, CPHA = 0 */
+#define RT_SPI_MODE_1 (0 | RT_SPI_CPHA) /* CPOL = 0, CPHA = 1 */
+#define RT_SPI_MODE_2 (RT_SPI_CPOL | 0) /* CPOL = 1, CPHA = 0 */
+#define RT_SPI_MODE_3 (RT_SPI_CPOL | RT_SPI_CPHA) /* CPOL = 1, CPHA = 1 */
+
+#define RT_SPI_CS_HIGH (1<<4) /* Chipselect active high */
+#define RT_SPI_NO_CS (1<<5) /* No chipselect */
+#define RT_SPI_3WIRE (1<<6) /* SI/SO pin shared */
+#define RT_SPI_READY (1<<7) /* Slave pulls low to pause */
+```
+
+**数据宽度:** 根据 SPI 主设备及 SPI 从设备可发送及接收的数据宽度格式设置为8位、16位或者32位。
+
+**最大频率:** 设置数据传输的波特率,同样根据 SPI 主设备及 SPI 从设备工作的波特率范围设置。
+
+配置示例如下所示:
+
+```c
+ struct rt_spi_configuration cfg;
+ cfg.data_width = 8;
+ cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
+ cfg.max_hz = 20 * 1000 *1000; /* 20M */
+
+ rt_spi_configure(spi_dev, &cfg);
+```
+
+## 配置 QSPI 设备
+
+配置 QSPI 设备的传输参数可使用如下函数:
+
+```c
+rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| device | QSPI 设备句柄 |
+| cfg | QSPI 配置参数指针 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+
+此函数会保存 cfg 指向的配置参数到 QSPI 设备 device 的控制块里,当传输数据时会使用此配置参数。
+
+struct rt_qspi_configuration 原型如下:
+
+```c
+struct rt_qspi_configuration
+{
+ struct rt_spi_configuration parent; /* 继承自 SPI 设备配置参数 */
+ rt_uint32_t medium_size; /* 介质大小 */
+ rt_uint8_t ddr_mode; /* 双倍速率模式 */
+ rt_uint8_t qspi_dl_width ; /* QSPI 总线位宽,单线模式 1 位、双线模式 2 位,4 线模式 4 位 */
+};
+```
+
+## 访问 SPI 设备
+
+一般情况下 MCU 的 SPI 器件都是作为主机和从机通讯,在 RT-Thread 中将 SPI 主机虚拟为 SPI 总线设备,应用程序使用 SPI 设备管理接口来访问 SPI 从机器件,主要接口如下所示:
+
+| **函数** | **描述** |
+| -------------------- | ---------------------------------- |
+| rt_device_find() | 根据 SPI 设备名称查找设备获取设备句柄 |
+| rt_spi_transfer_message() | 自定义传输数据 |
+| rt_spi_transfer() | 传输一次数据 |
+| rt_spi_send() | 发送一次数据 |
+| rt_spi_recv() | 接受一次数据 |
+| rt_spi_send_then_send() | 连续两次发送 |
+| rt_spi_send_then_recv() | 先发送后接收 |
+
+> [!NOTE]
+> 注:SPI 数据传输相关接口会调用 rt_mutex_take(), 此函数不能在中断服务程序里面调用,会导致 assertion 报错。
+
+### 查找 SPI 设备
+
+在使用 SPI 设备前需要根据 SPI 设备名称获取设备句柄,进而才可以操作 SPI 设备,查找设备函数如下所示,
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+一般情况下,注册到系统的 SPI 设备名称为 spi10, qspi10等,使用示例如下所示:
+
+```c
+#define W25Q_SPI_DEVICE_NAME "qspi10" /* SPI 设备名称 */
+struct rt_spi_device *spi_dev_w25q; /* SPI 设备句柄 */
+
+/* 查找 spi 设备获取设备句柄 */
+spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);
+```
+
+### 自定义传输数据
+
+获取到 SPI 设备句柄就可以使用 SPI 设备管理接口访问 SPI 设备器件,进行数据收发。可以通过如下函数传输消息:
+
+```c
+struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device *device,struct rt_spi_message *message);
+```
+
+| **参数** | **描述** |
+|----------|--------------------------------------------|
+| device | SPI 设备句柄 |
+| message | 消息指针 |
+| **返回** | —— |
+| RT_NULL | 成功发送 |
+| 非空指针 | 发送失败,返回指向剩余未发送的 message 的指针 |
+
+此函数可以传输一连串消息,用户可以自定义每个待传输的 message 结构体各参数的数值,从而可以很方便的控制数据传输方式。struct rt_spi_message 原型如下:
+
+```c
+struct rt_spi_message
+{
+ const void *send_buf; /* 发送缓冲区指针 */
+ void *recv_buf; /* 接收缓冲区指针 */
+ rt_size_t length; /* 发送 / 接收 数据字节数 */
+ struct rt_spi_message *next; /* 指向继续发送的下一条消息的指针 */
+ unsigned cs_take : 1; /* 片选选中 */
+ unsigned cs_release : 1; /* 释放片选 */
+};
+```
+sendbuf 为发送缓冲区指针,其值为 RT_NULL 时,表示本次传输为只接收状态,不需要发送数据。
+
+recvbuf 为接收缓冲区指针,其值为 RT_NULL 时,表示本次传输为只发送状态,不需要保存接收到的数据,所以收到的数据直接丢弃。
+
+length 的单位为 word,即数据长度为 8 位时,每个 length 占用 1 个字节;当数据长度为 16 位时,每个 length 占用 2 个字节。
+
+参数 next 是指向继续发送的下一条消息的指针,若只发送一条消息,则此指针值为 RT_NULL。多个待传输的消息通过 next 指针以单向链表的形式连接在一起。
+
+cs_take 值为 1 时,表示在传输数据前,设置对应的 CS 为有效状态。cs_release 值为 1 时,表示在数据传输结束后,释放对应的 CS。
+
+> [!NOTE]
+> 注:* 当 send_buf 或 recv_buf 不为空时,两者的可用空间都不得小于 length。
+ * 若使用此函数传输消息,传输的第一条消息 cs_take 需置为 1,设置片选为有效,最后一条消息的 cs_release 需置 1,释放片选。
+
+使用示例如下所示:
+
+```c
+#define W25Q_SPI_DEVICE_NAME "qspi10" /* SPI 设备名称 */
+struct rt_spi_device *spi_dev_w25q; /* SPI 设备句柄 */
+struct rt_spi_message msg1, msg2;
+rt_uint8_t w25x_read_id = 0x90; /* 命令 */
+rt_uint8_t id[5] = {0};
+
+/* 查找 spi 设备获取设备句柄 */
+spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);
+/* 发送命令读取ID */
+struct rt_spi_message msg1, msg2;
+
+msg1.send_buf = &w25x_read_id;
+msg1.recv_buf = RT_NULL;
+msg1.length = 1;
+msg1.cs_take = 1;
+msg1.cs_release = 0;
+msg1.next = &msg2;
+
+msg2.send_buf = RT_NULL;
+msg2.recv_buf = id;
+msg2.length = 5;
+msg2.cs_take = 0;
+msg2.cs_release = 1;
+msg2.next = RT_NULL;
+
+rt_spi_transfer_message(spi_dev_w25q, &msg1);
+rt_kprintf("use rt_spi_transfer_message() read w25q ID is:%x%x\n", id[3], id[4]);
+```
+
+### 传输一次数据
+
+如果只传输一次数据可以通过如下函数:
+
+```c
+rt_size_t rt_spi_transfer(struct rt_spi_device *device,
+ const void *send_buf,
+ void *recv_buf,
+ rt_size_t length);
+```
+
+| **参数** | **描述** |
+|----------|----------------------|
+| device | SPI 设备句柄 |
+| send_buf | 发送数据缓冲区指针 |
+| recv_buf | 接收数据缓冲区指针 |
+| length | 发送/接收 数据字节数 |
+| **返回** | —— |
+| 0 | 传输失败 |
+| 非 0 值 | 成功传输的字节数 |
+
+此函数等同于调用`rt_spi_transfer_message()` 传输一条消息,开始发送数据时片选选中,函数返回时释放片选,message 参数配置如下:
+
+```c
+struct rt_spi_message msg;
+
+msg.send_buf = send_buf;
+msg.recv_buf = recv_buf;
+msg.length = length;
+msg.cs_take = 1;
+msg.cs_release = 1;
+msg.next = RT_NULL;
+```
+
+### 发送一次数据
+
+如果只发送一次数据,而忽略接收到的数据可以通过如下函数:
+
+```c
+rt_size_t rt_spi_send(struct rt_spi_device *device,
+ const void *send_buf,
+ rt_size_t length)
+```
+
+| **参数** | **描述** |
+|----------|--------------------|
+| device | SPI 设备句柄 |
+| send_buf | 发送数据缓冲区指针 |
+| length | 发送数据字节数 |
+| **返回** | —— |
+| 0 | 发送失败 |
+| 非 0 值 | 成功发送的字节数 |
+
+调用此函数发送 send_buf 指向的缓冲区的数据,忽略接收到的数据,此函数是对 `rt_spi_transfer()` 函数的封装。
+
+此函数等同于调用 `rt_spi_transfer_message()` 传输一条消息,开始发送数据时片选选中,函数返回时释放片选,message 参数配置如下:
+
+```c
+struct rt_spi_message msg;
+
+msg.send_buf = send_buf;
+msg.recv_buf = RT_NULL;
+msg.length = length;
+msg.cs_take = 1;
+msg.cs_release = 1;
+msg.next = RT_NULL;
+```
+
+### 接收一次数据
+
+如果只接收一次数据可以通过如下函数:
+
+```c
+rt_size_t rt_spi_recv(struct rt_spi_device *device,
+ void *recv_buf,
+ rt_size_t length);
+```
+
+| **参数** | **描述** |
+|----------|--------------------|
+| device | SPI 设备句柄 |
+| recv_buf | 接收数据缓冲区指针 |
+| length | 接收数据字节数 |
+| **返回** | —— |
+| 0 | 接收失败 |
+| 非 0 值 | 成功接收的字节数 |
+
+调用此函数接收数据并保存到 recv_buf 指向的缓冲区。此函数是对 `rt_spi_transfer()` 函数的封装。SPI 总线协议规定只能由主设备产生时钟,因此在接收数据时,主设备会发送数据 0XFF。
+
+此函数等同于调用 `rt_spi_transfer_message()` 传输一条消息,开始接收数据时片选选中,函数返回时释放片选,message 参数配置如下:
+
+```c
+struct rt_spi_message msg;
+
+msg.send_buf = RT_NULL;
+msg.recv_buf = recv_buf;
+msg.length = length;
+msg.cs_take = 1;
+msg.cs_release = 1;
+msg.next = RT_NULL;
+```
+
+### 连续两次发送数据
+
+如果需要先后连续发送 2 个缓冲区的数据,并且中间片选不释放,可以调用如下函数:
+
+```c
+rt_err_t rt_spi_send_then_send(struct rt_spi_device *device,
+ const void *send_buf1,
+ rt_size_t send_length1,
+ const void *send_buf2,
+ rt_size_t send_length2);
+```
+
+| **参数** | **描述** |
+|--------------|---------------------------|
+| device | SPI 设备句柄 |
+| send_buf1 | 发送数据缓冲区 1 指针 |
+| send_length1 | 发送数据缓冲区 1 数据字节数 |
+| send_buf2 | 发送数据缓冲区 2 指针 |
+| send_length2 | 发送数据缓冲区 2 数据字节数 |
+| **返回** | —— |
+| RT_EOK | 发送成功 |
+| -RT_EIO | 发送失败 |
+
+此函数可以连续发送 2 个缓冲区的数据,忽略接收到的数据,发送 send_buf1 时片选选中,发送完 send_buf2 后释放片选。
+
+本函数适合向 SPI 设备中写入一块数据,第一次先发送命令和地址等数据,第二次再发送指定长度的数据。之所以分两次发送而不是合并成一个数据块发送,或调用两次 `rt_spi_send()`,是因为在大部分的数据写操作中,都需要先发命令和地址,长度一般只有几个字节。如果与后面的数据合并在一起发送,将需要进行内存空间申请和大量的数据搬运。而如果调用两次 `rt_spi_send()`,那么在发送完命令和地址后,片选会被释放,大部分 SPI 设备都依靠设置片选一次有效为命令的起始,所以片选在发送完命令或地址数据后被释放,则此次操作被丢弃。
+
+此函数等同于调用 `rt_spi_transfer_message()` 传输 2 条消息,message 参数配置如下:
+
+```c
+struct rt_spi_message msg1,msg2;
+
+msg1.send_buf = send_buf1;
+msg1.recv_buf = RT_NULL;
+msg1.length = send_length1;
+msg1.cs_take = 1;
+msg1.cs_release = 0;
+msg1.next = &msg2;
+
+msg2.send_buf = send_buf2;
+msg2.recv_buf = RT_NULL;
+msg2.length = send_length2;
+msg2.cs_take = 0;
+msg2.cs_release = 1;
+msg2.next = RT_NULL;
+```
+
+### 先发送后接收数据
+
+如果需要向从设备先发送数据,然后接收从设备发送的数据,并且中间片选不释放,可以调用如下函数:
+
+```c
+rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device,
+ const void *send_buf,
+ rt_size_t send_length,
+ void *recv_buf,
+ rt_size_t recv_length);
+```
+
+| **参数** | **描述** |
+|-------------|--------------------------|
+| device | SPI 从设备句柄 |
+| send_buf | 发送数据缓冲区指针 |
+| send_length | 发送数据缓冲区数据字节数 |
+| recv_buf | 接收数据缓冲区指针 |
+| recv_length | 接收数据字节数 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| -RT_EIO | 失败 |
+
+此函数发送第一条数据 send_buf 时开始片选,此时忽略接收到的数据,然后发送第二条数据,此时主设备会发送数据 0XFF,接收到的数据保存在 recv_buf 里,函数返回时释放片选。
+
+本函数适合从 SPI 从设备中读取一块数据,第一次会先发送一些命令和地址数据,然后再接收指定长度的数据。
+
+此函数等同于调用 `rt_spi_transfer_message()` 传输 2 条消息,message 参数配置如下:
+
+```c
+struct rt_spi_message msg1,msg2;
+
+msg1.send_buf = send_buf;
+msg1.recv_buf = RT_NULL;
+msg1.length = send_length;
+msg1.cs_take = 1;
+msg1.cs_release = 0;
+msg1.next = &msg2;
+
+msg2.send_buf = RT_NULL;
+msg2.recv_buf = recv_buf;
+msg2.length = recv_length;
+msg2.cs_take = 0;
+msg2.cs_release = 1;
+msg2.next = RT_NULL;
+```
+
+SPI 设备管理模块还提供 `rt_spi_sendrecv8()` 和 `rt_spi_sendrecv16()` 函数,这两个函数都是对此函数的封装,`rt_spi_sendrecv8()` 发送一个字节数据同时收到一个字节数据,`rt_spi_sendrecv16()` 发送 2 个字节数据同时收到 2 个字节数据。
+
+## 访问 QSPI 设备
+
+QSPI 的数据传输接口如下所示:
+
+| **函数** | **描述** |
+| -------------------- | ----------------------------|
+| rt_qspi_transfer_message() | 传输数据 |
+| rt_qspi_send_then_recv() | 先发送后接收 |
+| rt_qspi_send() | 发送一次数据 |
+
+> [!NOTE]
+> 注:QSPI 数据传输相关接口会调用 rt_mutex_take(), 此函数不能在中断服务程序里面调用,会导致 assertion 报错。
+
+### 传输数据
+
+可以通过如下函数传输消息:
+
+```c
+rt_size_t rt_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message);
+```
+
+| **参数** | **描述** |
+|----------|--------------------------------------------|
+| device | QSPI 设备句柄 |
+| message | 消息指针 |
+| **返回** | —— |
+| 实际传输的消息大小 | |
+
+消息结构体 struct rt_qspi_message 原型如下:
+
+```c
+struct rt_qspi_message
+{
+ struct rt_spi_message parent; /* 继承自struct rt_spi_message */
+
+ struct
+ {
+ rt_uint8_t content; /* 指令内容 */
+ rt_uint8_t qspi_lines; /* 指令模式,单线模式 1 位、双线模式 2 位,4 线模式 4 位 */
+ } instruction; /* 指令阶段 */
+
+ struct
+ {
+ rt_uint32_t content; /* 地址/交替字节 内容 */
+ rt_uint8_t size; /* 地址/交替字节 长度 */
+ rt_uint8_t qspi_lines; /* 地址/交替字节 模式,单线模式 1 位、双线模式 2 位,4 线模式 4 位 */
+ } address, alternate_bytes; /* 地址/交替字节 阶段 */
+
+ rt_uint32_t dummy_cycles; /* 空指令周期阶段 */
+ rt_uint8_t qspi_data_lines; /* QSPI 总线位宽 */
+};
+```
+
+### 接收数据
+
+可以调用如下函数:
+
+```c
+rt_err_t rt_qspi_send_then_recv(struct rt_qspi_device *device,
+ const void *send_buf,
+ rt_size_t send_length,
+ void *recv_buf,
+ rt_size_t recv_length);
+```
+
+| **参数** | **描述** |
+|-------------|--------------------------|
+| device | QSPI 设备句柄 |
+| send_buf | 发送数据缓冲区指针 |
+| send_length | 发送数据字节数 |
+| recv_buf | 接收数据缓冲区指针 |
+| recv_length | 接收数据字节数 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他错误码 | 失败 |
+
+send_buf 参数包含了将要发送的命令序列。
+
+### 发送数据
+
+```c
+rt_err_t rt_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length)
+```
+
+| **参数** | **描述** |
+|-------------|--------------------------|
+| device | QSPI 设备句柄 |
+| send_buf | 发送数据缓冲区指针 |
+| length | 发送数据字节数 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 其他错误码 | 失败 |
+
+send_buf 参数包含了将要发送的命令序列和数据。
+
+## 特殊使用场景
+
+在一些特殊的使用场景,某个设备希望独占总线一段时间,且期间要保持片选一直有效,期间数据传输可能是间断的,则可以按照如所示步骤使用相关接口。传输数据函数必须使用 `rt_spi_transfer_message()`,并且此函数每个待传输消息的片选控制域 cs_take 和 cs_release 都要设置为 0 值,因为片选已经使用了其他接口控制,不需要在数据传输的时候控制。
+
+### 获取总线
+
+在多线程的情况下,同一个 SPI 总线可能会在不同的线程中使用,为了防止 SPI 总线正在传输的数据丢失,从设备在开始传输数据前需要先获取 SPI 总线的使用权,获取成功才能够使用总线传输数据,可使用如下函数获取 SPI 总线:
+
+```c
+rt_err_t rt_spi_take_bus(struct rt_spi_device *device);
+```
+
+| **参数** | **描述** |
+|----------|---------------|
+| device | SPI 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+| 错误码 | 失败 |
+
+### 选中片选
+
+从设备获取总线的使用权后,需要设置自己对应的片选信号为有效,可使用如下函数选中片选:
+
+```c
+rt_err_t rt_spi_take(struct rt_spi_device *device);
+```
+
+| **参数** | **描述** |
+|----------|---------------|
+| device | SPI 设备句柄 |
+| **返回** | —— |
+| 0 | 成功 |
+| 错误码 | 失败 |
+
+### 增加一条消息
+
+使用 `rt_spi_transfer_message()` 传输消息时,所有待传输的消息都是以单向链表的形式连接起来的,可使用如下函数往消息链表里增加一条新的待传输消息:
+
+```c
+void rt_spi_message_append(struct rt_spi_message *list,
+ struct rt_spi_message *message);
+```
+
+| **参数** | **描述** |
+|----------|----------------------|
+| list | 待传输的消息链表节点 |
+| message | 新增消息指针 |
+
+### 释放片选
+
+从设备数据传输完成后,需要释放片选,可使用如下函数释放片选:
+
+```c
+rt_err_t rt_spi_release(struct rt_spi_device *device);
+```
+
+| **参数** | **描述** |
+|----------|---------------|
+| device | SPI 设备句柄 |
+| 返回 | —— |
+| 0 | 成功 |
+| 错误码 | 失败 |
+
+### 释放总线
+
+从设备不在使用 SPI 总线传输数据,必须尽快释放总线,这样其他从设备才能使用 SPI 总线传输数据,可使用如下函数释放总线:
+
+```c
+rt_err_t rt_spi_release_bus(struct rt_spi_device *device);
+```
+
+| **参数** | **描述** |
+|----------|---------------|
+| device | SPI 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 成功 |
+
+## SPI 设备使用示例
+
+SPI 设备的具体使用方式可以参考如下的示例代码,示例代码首先查找 SPI 设备获取设备句柄,然后使用 rt_spi_transfer_message() 发送命令读取 ID信息。
+
+```c
+/*
+ * 程序清单:这是一个 SPI 设备使用例程
+ * 例程导出了 spi_w25q_sample 命令到控制终端
+ * 命令调用格式:spi_w25q_sample spi10
+ * 命令解释:命令第二个参数是要使用的SPI设备名称,为空则使用默认的SPI设备
+ * 程序功能:通过SPI设备读取 w25q 的 ID 数据
+*/
+
+#include
+#include
+
+#define W25Q_SPI_DEVICE_NAME "qspi10"
+
+static void spi_w25q_sample(int argc, char *argv[])
+{
+ struct rt_spi_device *spi_dev_w25q;
+ char name[RT_NAME_MAX];
+ rt_uint8_t w25x_read_id = 0x90;
+ rt_uint8_t id[5] = {0};
+
+ if (argc == 2)
+ {
+ rt_strncpy(name, argv[1], RT_NAME_MAX);
+ }
+ else
+ {
+ rt_strncpy(name, W25Q_SPI_DEVICE_NAME, RT_NAME_MAX);
+ }
+
+ /* 查找 spi 设备获取设备句柄 */
+ spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name);
+ if (!spi_dev_w25q)
+ {
+ rt_kprintf("spi sample run failed! can't find %s device!\n", name);
+ }
+ else
+ {
+ /* 方式1:使用 rt_spi_send_then_recv()发送命令读取ID */
+ rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5);
+ rt_kprintf("use rt_spi_send_then_recv() read w25q ID is:%x%x\n", id[3], id[4]);
+
+ /* 方式2:使用 rt_spi_transfer_message()发送命令读取ID */
+ struct rt_spi_message msg1, msg2;
+
+ msg1.send_buf = &w25x_read_id;
+ msg1.recv_buf = RT_NULL;
+ msg1.length = 1;
+ msg1.cs_take = 1;
+ msg1.cs_release = 0;
+ msg1.next = &msg2;
+
+ msg2.send_buf = RT_NULL;
+ msg2.recv_buf = id;
+ msg2.length = 5;
+ msg2.cs_take = 0;
+ msg2.cs_release = 1;
+ msg2.next = RT_NULL;
+
+ rt_spi_transfer_message(spi_dev_w25q, &msg1);
+ rt_kprintf("use rt_spi_transfer_message() read w25q ID is:%x%x\n", id[3], id[4]);
+
+ }
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(spi_w25q_sample, spi w25q sample);
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/touch/figures/touch.vsd b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/figures/touch.vsd
new file mode 100644
index 0000000000000000000000000000000000000000..321871365c8c014fdfbc1bf74e301a3728f994eb
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/figures/touch.vsd differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/touch/figures/touch_pro.png b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/figures/touch_pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..522b6f103c48c0863b270e3f67d92d3a4767bc02
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/figures/touch_pro.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/touch/touch.md b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/touch.md
new file mode 100644
index 0000000000000000000000000000000000000000..b0afb06aeb798c867fdb01f39fdabbb2c5e7be74
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/touch.md
@@ -0,0 +1,408 @@
+# TOUCH 设备
+
+## Touch 简介
+
+Touch(触摸芯片)是 UI 设计中进行人机交互重要的一部分,一个完整的 UI 设计应该包括输入信息和输出信息,LCD 等屏幕设备负责显示输出,那么 Touch 设备就负责触点信息采集作为信息输入。
+
+Touch 设备与主机通讯一般都是采用 I2C 总线协议来进行数据交互,所以一个 Touch 设备,就是一个标准的 I2C 从设备,而且为了提高接收 Touch 数据的实时性,触摸芯片都会提供中断支持,当有触摸事件(抬起,按下,移动)发生时,会触发中断通知 MCU 有触摸事件。主机可以通过中断回调函数去读取触摸点信息。
+
+Touch 设备与主机通讯连接如下图所示:
+
+ 
+
+RT-Thread 为了方便使用 Touch 设备,抽象出了 Touch 设备驱动框架,并且向上层提供统一的操作接口,提高上层代码的可重用性。
+
+### Touch 设备特性
+
+- 接口:标准 device 接口(open/close/read/control)。
+- 工作模式:支持和轮询两种模式。
+- 支持读取多点数据
+
+点击 [Touch 列表](touch_list.md),查看当前支持的 Touch 类型。
+
+
+## 访问 Touch 设备
+
+应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问 Touch 设备,相关接口如下所示:
+
+| **函数** | **描述** |
+| --------------------------- | ------------------------------------------ |
+| rt_device_find() | 根据 Touch 设备设备名称查找设备获取设备句柄 |
+| rt_device_open() | 打开 Touch 设备 |
+| rt_device_read() | 读取触点数据 |
+| rt_device_control() | 控制 Touch 设备 |
+| rt_device_set_rx_indicate() | 设置接收回调函数 |
+| rt_device_close() | 关闭 Touch 设备 |
+
+### 查找 Touch 设备
+
+应用程序根据 Touch 设备名称获取设备句柄,进而可以操作 Touch 设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | Touch 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+使用示例如下所示:
+```c
+#define TOUCH_DEVICE_NAME "touch_gt" /* Touch 设备名称 */
+
+static rt_device_t touch_dev; /* Touch 设备句柄 */
+/* 根据设备名称查找 Touch 设备,获取设备句柄 */
+touch_dev = rt_device_find(TOUCH_DEVICE_NAME);
+```
+
+### 打开 Touch 设备
+
+通过设备句柄,应用程序可以打开和关闭设备,通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 设备句柄 |
+| oflags | 设备模式标志 |
+| **返回** | —— |
+| RT_EOK | 设备打开成功 |
+| -RT_EBUSY | 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开 |
+| -RT_EINVAL | 不支持的打开参数 |
+| 其他错误码 | 设备打开失败 |
+
+oflags 参数支持下列参数:
+
+```c
+#define RT_DEVICE_FLAG_RDONLY 0x001 /* 标准设备的只读模式,对应 Touch 的轮询模式 */
+#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收模式 */
+```
+
+Touch 设备数据接收和发送数据的模式分为 2 种:中断模式和轮询模式。在使用的时候,这 2 种模式只能**选其一**,若 Touch 的打开参数 oflags 没有指定使用中断模式,则默认使用轮询模式。
+
+以轮询模式打开 Touch 设备使用示例如下所示:
+
+```c
+rt_device_open(touch_dev, RT_DEVICE_FLAG_RDONLY)
+```
+
+以中断模式打开 Touch 设备使用示例如下所示:
+```c
+rt_device_open(touch_dev, RT_DEVICE_FLAG_INT_RX)
+```
+
+### 控制 Touch 设备
+
+通过命令控制字,应用程序可以对 Touch 设备进行配置,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| cmd | 命令控制字,详细介绍见下面 |
+| arg | 控制的参数, 详细介绍见下面 |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+其中的 cmd 目前支持以下几种命令控制字
+
+```c
+#define RT_TOUCH_CTRL_GET_ID (0) /* 读设备ID */
+#define RT_TOUCH_CTRL_GET_INFO (1) /* 获取设备信息 */
+#define RT_TOUCH_CTRL_SET_MODE (2) /* 设置工作模式 */
+#define RT_TOUCH_CTRL_SET_X_RANGE (3) /* 设置 X 轴分辨率 */
+#define RT_TOUCH_CTRL_SET_Y_RANGE (4) /* 设置 Y 轴分辨率 */
+#define RT_TOUCH_CTRL_SET_X_TO_Y (5) /* 交换 X、Y 轴坐标 */
+#define RT_TOUCH_CTRL_DISABLE_INT (6) /* 使能中断 */
+#define RT_TOUCH_CTRL_ENABLE_INT (7) /* 失能中断 */
+```
+
+
+#### 读设备ID
+
+```c
+rt_uint8_t read_id[4];
+rt_device_control(touch_dev, RT_TOUCH_CTRL_GET_ID, read_id);
+LOG_I("id = %d %d %d %d \n", read_id[0] - '0', read_id[1] - '0', read_id[2] - '0', read_id[3] - '0');
+```
+
+#### 获取设备信息
+
+```c
+struct rt_touch_info info;
+rt_device_control(touch_dev, RT_TOUCH_CTRL_GET_INFO, &info);
+LOG_I("type :%d", info.type); /* 类型:电容型/电阻型*/
+LOG_I("vendor :%s", info.vendor); /* 厂商 */
+LOG_I("point_num :%d", info.point_num); /* 支持的触点个数 */
+LOG_I("range_x :%d", info.range_x); /* X 轴分辨率 */
+LOG_I("range_y :%d", info.range_y); /* Y 轴分辨率*/
+```
+#### 设置工作模式
+
+```c
+/* 设置工作模式为中断模式 */
+rt_device_control(touch_dev, RT_TOUCH_CTRL_SET_MODE, (void *)RT_DEVICE_FLAG_INT_RX);
+/* 设置工作模式为轮询模式 */
+rt_device_control(touch_dev, RT_TOUCH_CTRL_SET_MODE, (void *)RT_DEVICE_FLAG_RDONLY);
+```
+
+#### 设置 X 轴范围
+
+设置 Touch X 轴坐标的分辨率。
+
+```c
+rt_uint16_t x = 400;
+rt_device_control(touch_dev, RT_TOUCH_CTRL_SET_X_RANGE, &x);
+```
+
+#### 设置 Y 轴范围
+
+设置 Touch Y 轴坐标的分辨率。
+```c
+rt_uint16_t y = 400;
+rt_device_control(touch_dev, RT_TOUCH_CTRL_SET_Y_RANGE, &y);
+```
+
+#### 交换 X、Y 轴坐标
+
+```c
+rt_device_control(touch_dev, RT_TOUCH_CTRL_SET_X_TO_Y, RT_NULL);
+```
+
+#### 关闭 Touch 关闭中断
+
+```c
+rt_device_control(touch_dev, RT_TOUCH_CTRL_DISABLE_INT, RT_NULL);
+```
+
+#### 开启 Touch 设备中断
+
+```c
+rt_device_control(touch_dev, RT_TOUCH_CTRL_ENABLE_INT, RT_NULL);
+```
+
+当使用中断模式读取触点数据时,底层有触摸事件发生时会触发中断,由于中断触发的速度会大于 Touch 设备读取的速速(I2C 读取数据一般较慢),所以,在接收回调函数中需要关闭中断,然后再释放信号量,在触点信息读取线程中,请求到信号量之后会去读取数据,数据读取完成再去打开中断。注意打开中断接口和关闭中断接口需配对使用,打开一次中断对应要关闭一次中断,这样设备才能以正常的中断模式去读取数据。
+
+
+### 设置接收回调函数
+
+可以通过如下函数来设置数据接收指示,当 Touch 收到数据时,通知上层应用线程有数据到达 :
+
+```c
+rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
+```
+
+| **参数** | **描述** |
+| -------- | ------------ |
+| dev | 设备句柄 |
+| rx_ind | 回调函数指针 |
+| dev | 设备句柄(回调函数参数)|
+| size | 缓冲区数据大小(回调函数参数)|
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+
+该函数的回调函数由调用者提供。若 Touch 设备以中断接收模式打开,当 Touch 接收到数据产生中断时,就会调用回调函数,把 Touch 设备句柄放在 dev 参数里供调用者获取。
+
+### 读 Touch 设备触摸点信息
+
+#### 触点信息组成
+```c
+struct rt_touch_data
+{
+ rt_uint8_t event;
+ rt_uint8_t track_id;
+ rt_uint8_t width;
+ rt_uint16_t x_coordinate;
+ rt_uint16_t y_coordinate;
+ rt_tick_t timestamp;
+};
+```
+* event:触摸事件,包括抬起事件、按下事件和移动事件。
+* track_id:每个触摸点都有自己的触摸轨迹,这个数据用来保存触摸轨迹 ID。
+* width:触摸点宽度。
+* x_coordinate:触摸点 X 轴坐标。
+* y_coordinate:触摸点 Y 轴坐标。
+* timestamp:触摸事件时间戳。
+
+#### 读取触摸点信息接口
+
+可调用如下接口读取触摸点信息:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ------------------ | ---------------------------------------------- |
+| dev | 设备句柄 |
+| pos | 读取数据偏移量,此参数 Touch 未使用 |
+| buffer | 缓冲区指针,读取的数据将会被保存在缓冲区中 |
+| size | Touch 设备驱动框架根据这个参数来确定需要读取的触点个数 |
+| **返回** | —— |
+| 读到数据的实际大小 | 返回读取到的触点信息的个数 |
+| 0 | 需要读取当前线程的 errno 来判断错误状态 |
+
+读取一个触摸点信息的代码片段如下:
+```c
+struct rt_touch_data *read_data;
+read_data = (struct rt_touch_data *)rt_malloc(sizeof(struct rt_touch_data));
+
+if (rt_device_read(touch_dev, 0, read_data, 1) == 1)
+{
+ rt_kprintf("%d %d %d %d %d\n", read_data.track_id, read_data.x_coordinate, read_data.y_coordinate, read_data.timestamp, read_data.width);
+}
+```
+
+读取五个触摸点信息的代码片段如下:
+```c
+struct rt_touch_data *read_data;
+read_data = (struct rt_touch_data *)rt_malloc(sizeof(struct rt_touch_data) * 5);
+
+if (rt_device_read(dev, 0, read_data, 5) == 5)
+{
+ for (rt_uint8_t i = 0; i < 5; i++)
+ {
+ if (read_data[i].event == RT_TOUCH_EVENT_DOWN || read_data[i].event == RT_TOUCH_EVENT_MOVE)
+ {
+ rt_kprintf("%d %d %d %d %d\n",
+ read_data[i].track_id,
+ read_data[i].x_coordinate,
+ read_data[i].y_coordinate,
+ read_data[i].timestamp,
+ read_data[i].width);
+ }
+ }
+}
+```
+
+### 关闭 Touch 设备
+
+当应用程序完成 Touch 操作后,可以关闭 Touch 设备,通过如下函数完成:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| dev | 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+## Touch 设备使用示例
+
+### 中断方式读取五个点的触摸信息
+
+Touch 设备中断方式读取五个点的触摸信息的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 首先查找 Touch 设置获取设备句柄。
+
+2. 以中断接收数据的方式打开 Touch 设备。
+
+3. 设置中断接收回调。
+
+4. 创建读取 Touch 数据线程和信号量,在中断接收回调中释放该信号量,在数据接收线程中一直请求信号量,一旦请求到信号量就去读取 Touch 的数据。
+
+```c
+static rt_thread_t gt9147_thread = RT_NULL;
+static rt_sem_t gt9147_sem = RT_NULL;
+static rt_device_t dev = RT_NULL;
+static struct rt_touch_data *read_data;
+
+/* 读取数据线程入口函数 */
+static void gt9147_entry(void *parameter)
+{
+ struct rt_touch_data *read_data;
+ read_data = (struct rt_touch_data *)rt_malloc(sizeof(struct rt_touch_data) * 5);
+
+ while (1)
+ {
+ /* 请求信号量 */
+ rt_sem_take(gt9147_sem, RT_WAITING_FOREVER);
+ /* 读取五个点的触摸信息 */
+ if (rt_device_read(dev, 0, read_data, 5) == 5)
+ {
+ for (rt_uint8_t i = 0; i < 5; i++)
+ {
+ if (read_data[i].event == RT_TOUCH_EVENT_DOWN || read_data[i].event == RT_TOUCH_EVENT_MOVE)
+ {
+ rt_kprintf("%d %d %d %d %d\n",
+ read_data[i].track_id,
+ read_data[i].x_coordinate,
+ read_data[i].y_coordinate,
+ read_data[i].timestamp,
+ read_data[i].width);
+ }
+ }
+ }
+ /* 打开中断 */
+ rt_device_control(dev, RT_TOUCH_CTRL_ENABLE_INT, RT_NULL);
+ }
+}
+
+/* 接收回调函数 */
+static rt_err_t rx_callback(rt_device_t dev, rt_size_t size)
+{
+ /* 关闭中断 */
+ rt_device_control(dev, RT_TOUCH_CTRL_DISABLE_INT, RT_NULL);
+ /* 释放信号量 */
+ rt_sem_release(gt9147_sem);
+ return 0;
+}
+
+static int gt9147_sample(void)
+{
+ /* 查找 Touch 设备 */
+ dev = rt_device_find("touch");
+
+ if (dev == RT_NULL)
+ {
+ rt_kprintf("can't find device:%s\n", "touch");
+ return -1;
+ }
+ /* 以中断的方式打开设备 */
+ if (rt_device_open(dev, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
+ {
+ rt_kprintf("open device failed!");
+ return -1;
+ }
+ /* 设置接收回调 */
+ rt_device_set_rx_indicate(dev, rx_callback);
+ /* 创建信号量 */
+ gt9147_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);
+
+ if (gt9147_sem == RT_NULL)
+ {
+ rt_kprintf("create dynamic semaphore failed.\n");
+ return -1;
+ }
+ /* 创建读取数据线程 */
+ gt9147_thread = rt_thread_create("thread1",
+ gt9147_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY,
+ THREAD_TIMESLICE);
+ /* 启动线程 */
+ if (gt9147_thread != RT_NULL)
+ rt_thread_startup(gt9147_thread);
+
+ return 0;
+}
+MSH_CMD_EXPORT(gt9147_sample, gt9147 sample);
+
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/touch/touch_list.md b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/touch_list.md
new file mode 100644
index 0000000000000000000000000000000000000000..e872b24ab30653417e65458b2012b0de6ebdf406
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/touch/touch_list.md
@@ -0,0 +1,8 @@
+# 支持的 Touch 列表
+
+下面是一份已经对接到 RT-Therad Touch 框架上的 Touch 列表,点击 Touch 名称即可跳转到相应软件包主页。(本文档不定时更新,如要查看所有支持的 Touch 的列表,可以查看最新的[软件包索引](https://github.com/RT-Thread/packages/tree/master/peripherals/touch)。
+
+| 厂商 | 传感器 | 备注 |
+| -------------- | ------------------------------------------------------------ | ------------------------ |
+| **GT** | | |
+| | **[gt9147](https://github.com/RT-Thread-packages/gt9147)** | 支持 5 点触控
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-dma b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-dma
new file mode 100644
index 0000000000000000000000000000000000000000..56b46e9e688c7cd7272297b4384a31fa37b5a53c
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-dma
@@ -0,0 +1,24 @@
+@startuml
+
+actor û
+
+participant Ӧó
+participant ISR
+participant DMA
+participant û
+
+Ӧó->Ӧó: ʼϢ
+Ӧó->Ӧó: ýջص
+Ӧó->Ӧó: ݴ߳
+
+Ӧó->Ӧó: ݴ߳ȴϢ
+
+û->DMA: ûһַ
+
+DMA->ISR: DMAһַ뻺ж
+
+ISR->Ӧó: ýջصϢм߳
+
+Ӧó->Ӧó: ݴ̻߳ȡϢбӻȡһַ
+
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-dma.png b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-dma.png
new file mode 100644
index 0000000000000000000000000000000000000000..dcc008ef9285c57d6e2f6b2017dc44b884b60ea1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-dma.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-int b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-int
new file mode 100644
index 0000000000000000000000000000000000000000..a6f4c7c2ca7b9395a944b4796deea7521c0b16b9
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-int
@@ -0,0 +1,24 @@
+@startuml
+
+actor û
+
+participant Ӧó
+participant ISR
+participant
+participant û
+
+Ӧó->Ӧó: ʼź
+Ӧó->Ӧó: ýջص
+Ӧó->Ӧó: ݴ߳
+
+Ӧó->Ӧó: ݴ߳ȴź
+
+û->: ûһַ
+
+->ISR: յַڽж
+
+ISR->Ӧó: ISRݷ뻺,ڽջصзź߳
+
+Ӧó->Ӧó: ݴ̻߳ȡźӻȡһַ
+
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-int.png b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-int.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0ff56ad7c3174a3f3799e6be05d0485e9f76065
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart-int.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart.vsdx
new file mode 100644
index 0000000000000000000000000000000000000000..e98d48e2c0773249bca850e55ef40231500996c5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart.vsdx differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart1.png b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart1.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f1404b7522001a78fe2c051712daa7896707496
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/figures/uart1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart.md b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart.md
new file mode 100644
index 0000000000000000000000000000000000000000..2def8b4bf57eaf117eed501e2cca7bd7ac2aa433
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart.md
@@ -0,0 +1,789 @@
+# UART 设备
+
+## UART 简介
+
+UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器,UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。是在应用程序开发过程中使用频率最高的数据总线。
+
+UART 串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。UART 串口传输的数据格式如下图所示:
+
+
+
+* 起始位:表示数据传输的开始,电平逻辑为 “0” 。
+
+* 数据位:可能值有 5、6、7、8、9,表示传输这几个 bit 位数据。一般取值为 8,因为一个 ASCII 字符值为 8 位。
+
+* 奇偶校验位:用于接收方对接收到的数据进行校验,校验 “1” 的位数为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。
+
+* 停止位: 表示一帧数据的结束。电平逻辑为 “1”。
+
+* 波特率:串口通信时的速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数 bit/s(bps)。常见的波特率值有 4800、9600、14400、38400、115200等,数值越大数据传输的越快,波特率为 115200 表示每秒钟传输 115200 位数据。
+
+## 访问串口设备
+
+应用程序通过 RT-Thread提供的 I/O 设备管理接口来访问串口硬件,相关接口如下所示:
+
+| **函数** | **描述** |
+| --------------------------- | -------------------------- |
+| rt_device_find() | 查找设备 |
+| rt_device_open() | 打开设备 |
+| rt_device_read() | 读取数据 |
+| rt_device_write() |写入数据|
+| rt_device_control() | 控制设备 |
+| rt_device_set_rx_indicate() | 设置接收回调函数 |
+| rt_device_set_tx_complete() | 设置发送完成回调函数 |
+| rt_device_close() | 关闭设备|
+
+### 查找串口设备
+
+应用程序根据串口设备名称获取设备句柄,进而可以操作串口设备,查找设备函数如下所示,
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | 设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+一般情况下,注册到系统的串口设备名称为 uart0,uart1等,使用示例如下所示:
+
+```c
+#define SAMPLE_UART_NAME "uart2" /* 串口设备名称 */
+static rt_device_t serial; /* 串口设备句柄 */
+/* 查找串口设备 */
+serial = rt_device_find(SAMPLE_UART_NAME);
+```
+
+### 打开串口设备
+
+通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
+
+```c
+rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 设备句柄 |
+| oflags | 设备模式标志 |
+| **返回** | —— |
+| RT_EOK | 设备打开成功 |
+| -RT_EBUSY | 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开 |
+| 其他错误码 | 设备打开失败 |
+
+oflags 参数支持下列取值 (可以采用或的方式支持多种取值):
+
+```c
+#define RT_DEVICE_FLAG_STREAM 0x040 /* 流模式 */
+/* 接收模式参数 */
+#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收模式 */
+#define RT_DEVICE_FLAG_DMA_RX 0x200 /* DMA 接收模式 */
+/* 发送模式参数 */
+#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中断发送模式 */
+#define RT_DEVICE_FLAG_DMA_TX 0x800 /* DMA 发送模式 */
+```
+
+串口数据接收和发送数据的模式分为 3 种:中断模式、轮询模式、DMA 模式。在使用的时候,这 3 种模式只能**选其一**,若串口的打开参数 oflags 没有指定使用中断模式或者 DMA 模式,则默认使用轮询模式。
+
+DMA(Direct Memory Access)即直接存储器访问。 DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过 DMA 控制器为 RAM 与 I/O 设备开辟一条直接传送数据的通路,这就节省了 CPU 的资源来做其他操作。使用 DMA 传输可以连续获取或发送一段信息而不占用中断或延时,在通信频繁或有大段信息要传输时非常有用。
+
+> [!NOTE]
+> 注:* RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是 `"\n"` (对应 16 进制值为 0x0A)时,自动在前面输出一个 `"\r"`(对应 16 进制值为 0x0D) 做分行。
+
+流模式 RT_DEVICE_FLAG_STREAM 可以和接收发送模式参数使用或 “|” 运算符一起使用。
+
+以**中断接收及轮询发送模式**使用串口设备的示例如下所示:
+
+```c
+#define SAMPLE_UART_NAME "uart2" /* 串口设备名称 */
+static rt_device_t serial; /* 串口设备句柄 */
+/* 查找串口设备 */
+serial = rt_device_find(SAMPLE_UART_NAME);
+
+/* 以中断接收及轮询发送模式打开串口设备 */
+rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
+```
+
+若串口要使用 DMA 接收模式,oflags 取值 RT_DEVICE_FLAG_DMA_RX。以**DMA 接收及轮询发送模式**使用串口设备的示例如下所示:
+
+```c
+#define SAMPLE_UART_NAME "uart2" /* 串口设备名称 */
+static rt_device_t serial; /* 串口设备句柄 */
+/* 查找串口设备 */
+serial = rt_device_find(SAMPLE_UART_NAME);
+
+/* 以 DMA 接收及轮询发送模式打开串口设备 */
+rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);
+```
+
+### 控制串口设备
+
+通过控制接口,应用程序可以对串口设备进行配置,如波特率、数据位、校验位、接收缓冲区大小、停止位等参数的修改。控制函数如下所示:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------------------- |
+| dev | 设备句柄 |
+| cmd | 命令控制字,可取值:RT_DEVICE_CTRL_CONFIG |
+| arg | 控制的参数,可取类型: struct serial_configure |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+控制参数结构体 struct serial_configure 原型如下:
+
+```c
+struct serial_configure
+{
+ rt_uint32_t baud_rate; /* 波特率 */
+ rt_uint32_t data_bits :4; /* 数据位 */
+ rt_uint32_t stop_bits :2; /* 停止位 */
+ rt_uint32_t parity :2; /* 奇偶校验位 */
+ rt_uint32_t bit_order :1; /* 高位在前或者低位在前 */
+ rt_uint32_t invert :1; /* 模式 */
+ rt_uint32_t bufsz :16; /* 接收数据缓冲区大小 */
+ rt_uint32_t reserved :4; /* 保留位 */
+};
+```
+
+RT-Thread 提供的配置参数可取值为如下宏定义:
+
+```c
+/* 波特率可取值 */
+#define BAUD_RATE_2400 2400
+#define BAUD_RATE_4800 4800
+#define BAUD_RATE_9600 9600
+#define BAUD_RATE_19200 19200
+#define BAUD_RATE_38400 38400
+#define BAUD_RATE_57600 57600
+#define BAUD_RATE_115200 115200
+#define BAUD_RATE_230400 230400
+#define BAUD_RATE_460800 460800
+#define BAUD_RATE_921600 921600
+#define BAUD_RATE_2000000 2000000
+#define BAUD_RATE_3000000 3000000
+/* 数据位可取值 */
+#define DATA_BITS_5 5
+#define DATA_BITS_6 6
+#define DATA_BITS_7 7
+#define DATA_BITS_8 8
+#define DATA_BITS_9 9
+/* 停止位可取值 */
+#define STOP_BITS_1 0
+#define STOP_BITS_2 1
+#define STOP_BITS_3 2
+#define STOP_BITS_4 3
+/* 极性位可取值 */
+#define PARITY_NONE 0
+#define PARITY_ODD 1
+#define PARITY_EVEN 2
+/* 高低位顺序可取值 */
+#define BIT_ORDER_LSB 0
+#define BIT_ORDER_MSB 1
+/* 模式可取值 */
+#define NRZ_NORMAL 0 /* normal mode */
+#define NRZ_INVERTED 1 /* inverted mode */
+/* 接收数据缓冲区默认大小 */
+#define RT_SERIAL_RB_BUFSZ 64
+```
+
+接收缓冲区:当串口使用中断接收模式打开时,串口驱动框架会根据 RT_SERIAL_RB_BUFSZ 大小开辟一块缓冲区用于保存接收到的数据,底层驱动接收到一个数据,都会在中断服务程序里面将数据放入缓冲区。
+
+RT-Thread 提供的默认串口配置如下,即 RT-Thread 系统中默认每个串口设备都使用如下配置:
+
+```c
+#define RT_SERIAL_CONFIG_DEFAULT \
+{ \
+ BAUD_RATE_115200, /* 115200 bits/s */ \
+ DATA_BITS_8, /* 8 databits */ \
+ STOP_BITS_1, /* 1 stopbit */ \
+ PARITY_NONE, /* No parity */ \
+ BIT_ORDER_LSB, /* LSB first sent */ \
+ NRZ_NORMAL, /* Normal mode */ \
+ RT_SERIAL_RB_BUFSZ, /* Buffer size */ \
+ 0 \
+}
+```
+
+> [!NOTE]
+> 注:默认串口配置接收数据缓冲区大小为 RT_SERIAL_RB_BUFSZ,即 64 字节。若一次性数据接收字节数很多,没有及时读取数据,那么缓冲区的数据将会被新接收到的数据覆盖,造成数据丢失,建议调大缓冲区,即通过 control 接口修改。在修改缓冲区大小时请注意,缓冲区大小无法动态改变,只有在 open 设备之前可以配置。open 设备之后,缓冲区大小不可再进行更改。但除过缓冲区之外的其他参数,在 open 设备前 / 后,均可进行更改。
+
+若实际使用串口的配置参数与默认配置参数不符,则用户可以通过应用代码进行修改。修改串口配置参数,如波特率、数据位、校验位、缓冲区接收 buffsize、停止位等的示例程序如下:
+
+```c
+#define SAMPLE_UART_NAME "uart2" /* 串口设备名称 */
+static rt_device_t serial; /* 串口设备句柄 */
+struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */
+
+/* step1:查找串口设备 */
+serial = rt_device_find(SAMPLE_UART_NAME);
+
+/* step2:修改串口配置参数 */
+config.baud_rate = BAUD_RATE_9600; //修改波特率为 9600
+config.data_bits = DATA_BITS_8; //数据位 8
+config.stop_bits = STOP_BITS_1; //停止位 1
+config.bufsz = 128; //修改缓冲区 buff size 为 128
+config.parity = PARITY_NONE; //无奇偶校验位
+
+/* step3:控制串口设备。通过控制接口传入命令控制字,与控制参数 */
+rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
+
+/* step4:打开串口设备。以中断接收及轮询发送模式打开串口设备 */
+rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
+```
+
+### 发送数据
+
+向串口中写入数据,可以通过如下函数完成:
+
+```c
+rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 设备句柄 |
+| pos | 写入数据偏移量,此参数串口设备未使用 |
+| buffer | 内存缓冲区指针,放置要写入的数据 |
+| size | 写入数据的大小 |
+| **返回** | —— |
+| 写入数据的实际大小 | 如果是字符设备,返回大小以字节为单位; |
+| 0 | 需要读取当前线程的 errno 来判断错误状态 |
+
+调用这个函数,会把缓冲区 buffer 中的数据写入到设备 dev 中,写入数据的大小是 size。
+
+向串口写入数据示例程序如下所示:
+
+```c
+#define SAMPLE_UART_NAME "uart2" /* 串口设备名称 */
+static rt_device_t serial; /* 串口设备句柄 */
+char str[] = "hello RT-Thread!\r\n";
+struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 配置参数 */
+/* 查找串口设备 */
+serial = rt_device_find(SAMPLE_UART_NAME);
+
+/* 以中断接收及轮询发送模式打开串口设备 */
+rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
+/* 发送字符串 */
+rt_device_write(serial, 0, str, (sizeof(str) - 1));
+```
+
+### 设置发送完成回调函数
+
+在应用程序调用 `rt_device_write()` 写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后 (例如 DMA 传送完成或 FIFO 已经写入完毕产生完成中断时) 调用。可以通过如下函数设置设备发送完成指示 :
+
+```c
+rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
+```
+
+| **参数** | **描述** |
+| -------- | ------------ |
+| dev | 设备句柄 |
+| tx_done | 回调函数指针 |
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+
+调用这个函数时,回调函数由调用者提供,当硬件设备发送完数据时,由设备驱动程序回调这个函数并把发送完成的数据块地址 buffer 作为参数传递给上层应用。上层应用(线程)在收到指示时会根据发送 buffer 的情况,释放 buffer 内存块或将其作为下一个写数据的缓存。
+
+### 设置接收回调函数
+
+可以通过如下函数来设置数据接收指示,当串口收到数据时,通知上层应用线程有数据到达 :
+
+```c
+rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
+```
+
+| **参数** | **描述** |
+| -------- | ------------ |
+| dev | 设备句柄 |
+| rx_ind | 回调函数指针 |
+| dev | 设备句柄(回调函数参数)|
+| size | 缓冲区数据大小(回调函数参数)|
+| **返回** | —— |
+| RT_EOK | 设置成功 |
+
+该函数的回调函数由调用者提供。若串口以中断接收模式打开,当串口接收到一个数据产生中断时,就会调用回调函数,并且会把此时缓冲区的数据大小放在 size 参数里,把串口设备句柄放在 dev 参数里供调用者获取。
+
+若串口以 DMA 接收模式打开,当 DMA 完成一批数据的接收后会调用此回调函数。
+
+一般情况下接收回调函数可以发送一个信号量或者事件通知串口数据处理线程有数据到达。使用示例如下所示:
+
+```c
+#define SAMPLE_UART_NAME "uart2" /* 串口设备名称 */
+static rt_device_t serial; /* 串口设备句柄 */
+static struct rt_semaphore rx_sem; /* 用于接收消息的信号量 */
+
+/* 接收数据回调函数 */
+static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
+{
+ /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
+ rt_sem_release(&rx_sem);
+
+ return RT_EOK;
+}
+
+static int uart_sample(int argc, char *argv[])
+{
+ serial = rt_device_find(SAMPLE_UART_NAME);
+
+ /* 以中断接收及轮询发送模式打开串口设备 */
+ rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
+
+ /* 初始化信号量 */
+ rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
+
+ /* 设置接收回调函数 */
+ rt_device_set_rx_indicate(serial, uart_input);
+}
+
+```
+
+### 接收数据
+
+可调用如下函数读取串口接收到的数据:
+
+```c
+rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
+```
+
+| **参数** | **描述** |
+| ------------------ | ---------------------------------------------- |
+| dev | 设备句柄 |
+| pos | 读取数据偏移量,此参数串口设备未使用 |
+| buffer | 缓冲区指针,读取的数据将会被保存在缓冲区中 |
+| size | 读取数据的大小 |
+| **返回** | —— |
+| 读到数据的实际大小 | 如果是字符设备,返回大小以字节为单位 |
+| 0 | 需要读取当前线程的 errno 来判断错误状态 |
+
+读取数据偏移量 pos 针对字符设备无效,此参数主要用于块设备中。
+
+串口使用中断接收模式并配合接收回调函数的使用示例如下所示:
+
+```c
+static rt_device_t serial; /* 串口设备句柄 */
+static struct rt_semaphore rx_sem; /* 用于接收消息的信号量 */
+
+/* 接收数据的线程 */
+static void serial_thread_entry(void *parameter)
+{
+ char ch;
+
+ while (1)
+ {
+ /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
+ while (rt_device_read(serial, -1, &ch, 1) != 1)
+ {
+ /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
+ rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
+ }
+ /* 读取到的数据通过串口错位输出 */
+ ch = ch + 1;
+ rt_device_write(serial, 0, &ch, 1);
+ }
+}
+```
+
+### 关闭串口设备
+
+当应用程序完成串口操作后,可以关闭串口设备,通过如下函数完成:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| dev | 设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+## 串口设备使用示例
+
+### 中断接收及轮询发送
+
+示例代码的主要步骤如下所示:
+
+1. 首先查找串口设备获取设备句柄。
+
+2. 初始化回调函数发送使用的信号量,然后以读写及中断接收方式打开串口设备。
+
+3. 设置串口设备的接收回调函数,之后发送字符串,并创建读取数据线程。
+
+* 读取数据线程会尝试读取一个字符数据,如果没有数据则会挂起并等待信号量,当串口设备接收到一个数据时会触发中断并调用接收回调函数,此函数会发送信号量唤醒线程,此时线程会马上读取接收到的数据。
+
+* 此示例代码不局限于特定的 BSP,根据 BSP 注册的串口设备,修改示例代码宏定义 SAMPLE_UART_NAME 对应的串口设备名称即可运行。
+
+运行序列图如下图所示:
+
+
+
+
+```c
+/*
+ * 程序清单:这是一个 串口 设备使用例程
+ * 例程导出了 uart_sample 命令到控制终端
+ * 命令调用格式:uart_sample uart2
+ * 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
+ * 程序功能:通过串口输出字符串"hello RT-Thread!",然后错位输出输入的字符
+*/
+
+#include
+
+#define SAMPLE_UART_NAME "uart2"
+
+/* 用于接收消息的信号量 */
+static struct rt_semaphore rx_sem;
+static rt_device_t serial;
+
+/* 接收数据回调函数 */
+static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
+{
+ /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
+ rt_sem_release(&rx_sem);
+
+ return RT_EOK;
+}
+
+static void serial_thread_entry(void *parameter)
+{
+ char ch;
+
+ while (1)
+ {
+ /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
+ while (rt_device_read(serial, -1, &ch, 1) != 1)
+ {
+ /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
+ rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
+ }
+ /* 读取到的数据通过串口错位输出 */
+ ch = ch + 1;
+ rt_device_write(serial, 0, &ch, 1);
+ }
+}
+
+static int uart_sample(int argc, char *argv[])
+{
+ rt_err_t ret = RT_EOK;
+ char uart_name[RT_NAME_MAX];
+ char str[] = "hello RT-Thread!\r\n";
+
+ if (argc == 2)
+ {
+ rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
+ }
+ else
+ {
+ rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
+ }
+
+ /* 查找系统中的串口设备 */
+ serial = rt_device_find(uart_name);
+ if (!serial)
+ {
+ rt_kprintf("find %s failed!\n", uart_name);
+ return RT_ERROR;
+ }
+
+ /* 初始化信号量 */
+ rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
+ /* 以中断接收及轮询发送模式打开串口设备 */
+ rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
+ /* 设置接收回调函数 */
+ rt_device_set_rx_indicate(serial, uart_input);
+ /* 发送字符串 */
+ rt_device_write(serial, 0, str, (sizeof(str) - 1));
+
+ /* 创建 serial 线程 */
+ rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
+ /* 创建成功则启动线程 */
+ if (thread != RT_NULL)
+ {
+ rt_thread_startup(thread);
+ }
+ else
+ {
+ ret = RT_ERROR;
+ }
+
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(uart_sample, uart device sample);
+```
+
+### DMA 接收及轮询发送
+
+当串口接收到一批数据后会调用接收回调函数,接收回调函数会把此时缓冲区的数据大小通过消息队列发送给等待的数据处理线程。线程获取到消息后被激活,并读取数据。一般情况下 DMA 接收模式会结合 DMA 接收完成中断和串口空闲中断完成数据接收。
+
+* 此示例代码不局限于特定的 BSP,根据 BSP 注册的串口设备,修改示例代码宏定义 SAMPLE_UART_NAME 对应的串口设备名称即可运行。
+
+运行序列图如下图所示:
+
+
+
+```c
+/*
+ * 程序清单:这是一个串口设备 DMA 接收使用例程
+ * 例程导出了 uart_dma_sample 命令到控制终端
+ * 命令调用格式:uart_dma_sample uart3
+ * 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
+ * 程序功能:通过串口输出字符串"hello RT-Thread!",并通过串口输出接收到的数据,然后打印接收到的数据。
+*/
+
+#include
+
+#define SAMPLE_UART_NAME "uart3" /* 串口设备名称 */
+
+/* 串口接收消息结构*/
+struct rx_msg
+{
+ rt_device_t dev;
+ rt_size_t size;
+};
+/* 串口设备句柄 */
+static rt_device_t serial;
+/* 消息队列控制块 */
+static struct rt_messagequeue rx_mq;
+
+/* 接收数据回调函数 */
+static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
+{
+ struct rx_msg msg;
+ rt_err_t result;
+ msg.dev = dev;
+ msg.size = size;
+
+ result = rt_mq_send(&rx_mq, &msg, sizeof(msg));
+ if ( result == -RT_EFULL)
+ {
+ /* 消息队列满 */
+ rt_kprintf("message queue full!\n");
+ }
+ return result;
+}
+
+static void serial_thread_entry(void *parameter)
+{
+ struct rx_msg msg;
+ rt_err_t result;
+ rt_uint32_t rx_length;
+ static char rx_buffer[RT_SERIAL_RB_BUFSZ + 1];
+
+ while (1)
+ {
+ rt_memset(&msg, 0, sizeof(msg));
+ /* 从消息队列中读取消息*/
+ result = rt_mq_recv(&rx_mq, &msg, sizeof(msg), RT_WAITING_FOREVER);
+ if (result == RT_EOK)
+ {
+ /* 从串口读取数据*/
+ rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size);
+ rx_buffer[rx_length] = '\0';
+ /* 通过串口设备 serial 输出读取到的消息 */
+ rt_device_write(serial, 0, rx_buffer, rx_length);
+ /* 打印数据 */
+ rt_kprintf("%s\n",rx_buffer);
+ }
+ }
+}
+
+static int uart_dma_sample(int argc, char *argv[])
+{
+ rt_err_t ret = RT_EOK;
+ char uart_name[RT_NAME_MAX];
+ static char msg_pool[256];
+ char str[] = "hello RT-Thread!\r\n";
+
+ if (argc == 2)
+ {
+ rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
+ }
+ else
+ {
+ rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
+ }
+
+ /* 查找串口设备 */
+ serial = rt_device_find(uart_name);
+ if (!serial)
+ {
+ rt_kprintf("find %s failed!\n", uart_name);
+ return RT_ERROR;
+ }
+
+ /* 初始化消息队列 */
+ rt_mq_init(&rx_mq, "rx_mq",
+ msg_pool, /* 存放消息的缓冲区 */
+ sizeof(struct rx_msg), /* 一条消息的最大长度 */
+ sizeof(msg_pool), /* 存放消息的缓冲区大小 */
+ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */
+
+ /* 以 DMA 接收及轮询发送方式打开串口设备 */
+ rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);
+ /* 设置接收回调函数 */
+ rt_device_set_rx_indicate(serial, uart_input);
+ /* 发送字符串 */
+ rt_device_write(serial, 0, str, (sizeof(str) - 1));
+
+ /* 创建 serial 线程 */
+ rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
+ /* 创建成功则启动线程 */
+ if (thread != RT_NULL)
+ {
+ rt_thread_startup(thread);
+ }
+ else
+ {
+ ret = RT_ERROR;
+ }
+
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(uart_dma_sample, uart device dma sample);
+```
+
+### 串口接收不定长数据
+
+串口接收不定长数据需要用户在应用层进行处理,一般会有特定的协议,比如一帧数据可能会有起始标记位、数据长度位、数据、终止标记位等,发送数据帧时按照约定的协议进行发送,接收数据时再按照协议进行解析。
+
+以下是一个简单的串口接收不定长数据示例代码,仅做了数据的结束标志位 DATA_CMD_END,如果遇到结束标志,则表示一帧数据结束。示例代码的主要步骤如下所示:
+
+1. 首先查找串口设备获取设备句柄。
+2. 初始化回调函数发送使用的信号量,然后以读写及中断接收方式打开串口设备。
+3. 设置串口设备的接收回调函数,之后发送字符串,并创建解析数据线程。
+
+- 解析数据线程会尝试读取一个字符数据,如果没有数据则会挂起并等待信号量,当串口设备接收到一个数据时会触发中断并调用接收回调函数,此函数会发送信号量唤醒线程,此时线程会马上读取接收到的数据。在解析数据时,判断结束符,如果结束,则打印数据。
+- 此示例代码不局限于特定的 BSP,根据 BSP 注册的串口设备,修改示例代码宏定义 SAMPLE_UART_NAME 对应的串口设备名称即可运行。
+- 当一帧数据长度超过最大长度时,这将是一帧不合格的数据,因为后面接收到的字符将覆盖最后一个字符。
+
+```c
+/*
+ * 程序清单:这是一个串口设备接收不定长数据的示例代码
+ * 例程导出了 uart_dma_sample 命令到控制终端
+ * 命令调用格式:uart_dma_sample uart2
+ * 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
+ * 程序功能:通过串口 uart2 输出字符串"hello RT-Thread!",并通过串口 uart2 输入一串字符(不定长),再通过数据解析后,使用控制台显示有效数据。
+*/
+
+#include
+
+#define SAMPLE_UART_NAME "uart2"
+#define DATA_CMD_END '\r' /* 结束位设置为 \r,即回车符 */
+#define ONE_DATA_MAXLEN 20 /* 不定长数据的最大长度 */
+
+/* 用于接收消息的信号量 */
+static struct rt_semaphore rx_sem;
+static rt_device_t serial;
+
+/* 接收数据回调函数 */
+static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
+{
+ /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
+ if (size > 0)
+ {
+ rt_sem_release(&rx_sem);
+ }
+ return RT_EOK;
+}
+
+static char uart_sample_get_char(void)
+{
+ char ch;
+
+ while (rt_device_read(serial, 0, &ch, 1) == 0)
+ {
+ rt_sem_control(&rx_sem, RT_IPC_CMD_RESET, RT_NULL);
+ rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
+ }
+ return ch;
+}
+
+/* 数据解析线程 */
+static void data_parsing(void)
+{
+ char ch;
+ char data[ONE_DATA_MAXLEN];
+ static char i = 0;
+
+ while (1)
+ {
+ ch = uart_sample_get_char();
+ rt_device_write(serial, 0, &ch, 1);
+ if(ch == DATA_CMD_END)
+ {
+ data[i++] = '\0';
+ rt_kprintf("data=%s\r\n",data);
+ i = 0;
+ continue;
+ }
+ i = (i >= ONE_DATA_MAXLEN-1) ? ONE_DATA_MAXLEN-1 : i;
+ data[i++] = ch;
+ }
+}
+
+static int uart_data_sample(int argc, char *argv[])
+{
+ rt_err_t ret = RT_EOK;
+ char uart_name[RT_NAME_MAX];
+ char str[] = "hello RT-Thread!\r\n";
+
+ if (argc == 2)
+ {
+ rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
+ }
+ else
+ {
+ rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
+ }
+
+ /* 查找系统中的串口设备 */
+ serial = rt_device_find(uart_name);
+ if (!serial)
+ {
+ rt_kprintf("find %s failed!\n", uart_name);
+ return RT_ERROR;
+ }
+
+ /* 初始化信号量 */
+ rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
+ /* 以中断接收及轮询发送模式打开串口设备 */
+ rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
+ /* 设置接收回调函数 */
+ rt_device_set_rx_indicate(serial, uart_rx_ind);
+ /* 发送字符串 */
+ rt_device_write(serial, 0, str, (sizeof(str) - 1));
+
+ /* 创建 serial 线程 */
+ rt_thread_t thread = rt_thread_create("serial", (void (*)(void *parameter))data_parsing, RT_NULL, 1024, 25, 10);
+ /* 创建成功则启动线程 */
+ if (thread != RT_NULL)
+ {
+ rt_thread_startup(thread);
+ }
+ else
+ {
+ ret = RT_ERROR;
+ }
+
+ return ret;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(uart_data_sample, uart device sample);
+
+```
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/watchdog/watchdog.md b/rt-thread-version/rt-thread-standard/programming-manual/device/watchdog/watchdog.md
new file mode 100644
index 0000000000000000000000000000000000000000..eec93f2e5bbfa31e16019559075560cb6628136e
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/watchdog/watchdog.md
@@ -0,0 +1,229 @@
+# WATCHDOG 设备
+
+## WATCHDOG 简介
+
+硬件看门狗(watchdog timer)是一个定时器,其定时输出连接到电路的复位端。在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。
+
+当看门狗启动后,计数器开始自动计数,在计数器溢出前如果没有被复位,计数器溢出就会对 CPU 产生一个复位信号使系统重启(俗称 “被狗咬”)。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称“喂狗“),不让复位信号产生。如果系统不出问题,程序能够按时“喂狗”。一旦程序跑飞,没有“喂狗”,系统“被咬” 复位。
+
+一般情况下可以在 RT-Thread 的 idle 回调函数和关键任务里喂狗。
+
+## 访问看门狗设备
+
+应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问看门狗硬件,相关接口如下所示:
+
+| **函数** | **描述** |
+| ---------------- | ---------------------------------- |
+| rt_device_find() | 根据看门狗设备设备名称查找设备获取设备句柄 |
+| rt_device_init() | 初始化看门狗设备 |
+| rt_device_control() |控制看门狗设备 |
+| rt_device_close() | 关闭看门狗设备|
+
+### 查找看门狗
+
+应用程序根据看门狗设备名称获取设备句柄,进而可以操作看门狗设备,查找设备函数如下所示:
+
+```c
+rt_device_t rt_device_find(const char* name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------------------------- |
+| name | 看门狗设备名称 |
+| **返回** | —— |
+| 设备句柄 | 查找到对应设备将返回相应的设备句柄 |
+| RT_NULL | 没有找到相应的设备对象 |
+
+使用示例如下所示:
+
+```c
+#define WDT_DEVICE_NAME "wdt" /* 看门狗设备名称 */
+
+static rt_device_t wdg_dev; /* 看门狗设备句柄 */
+/* 根据设备名称查找看门狗设备,获取设备句柄 */
+wdg_dev = rt_device_find(WDT_DEVICE_NAME);
+```
+
+### 初始化看门狗
+
+使用看门狗设备前需要先初始化,通过如下函数初始化看门狗设备:
+
+```c
+rt_err_t rt_device_init(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------- |
+| dev | 看门狗设备句柄 |
+| **返回** | —— |
+| RT_EOK | 设备初始化成功 |
+| -RT_ENOSYS | 初始化失败,看门狗设备驱动初始化函数为空 |
+| 其他错误码 | 设备打开失败 |
+
+使用示例如下所示:
+
+```c
+#define WDT_DEVICE_NAME "wdt" /* 看门狗设备名称 */
+
+static rt_device_t wdg_dev; /* 看门狗设备句柄 */
+/* 根据设备名称查找看门狗设备,获取设备句柄 */
+wdg_dev = rt_device_find(WDT_DEVICE_NAME);
+
+/* 初始化设备 */
+rt_device_init(wdg_dev);
+```
+
+### 控制看门狗
+
+通过命令控制字,应用程序可以对看门狗设备进行配置,通过如下函数完成:
+
+```c
+rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
+```
+
+| **参数** | **描述** |
+| ---------- | ------------------------------------------ |
+| dev | 看门狗设备句柄 |
+| cmd | 命令控制字 |
+| arg | 控制的参数 |
+| **返回** | —— |
+| RT_EOK | 函数执行成功 |
+| -RT_ENOSYS | 执行失败,dev 为空 |
+| 其他错误码 | 执行失败 |
+
+命令控制字可取如下宏定义值:
+
+```c
+#define RT_DEVICE_CTRL_WDT_GET_TIMEOUT (1) /* 获取溢出时间 */
+#define RT_DEVICE_CTRL_WDT_SET_TIMEOUT (2) /* 设置溢出时间 */
+#define RT_DEVICE_CTRL_WDT_GET_TIMELEFT (3) /* 获取剩余时间 */
+#define RT_DEVICE_CTRL_WDT_KEEPALIVE (4) /* 喂狗 */
+#define RT_DEVICE_CTRL_WDT_START (5) /* 启动看门狗 */
+#define RT_DEVICE_CTRL_WDT_STOP (6) /* 停止看门狗 */
+```
+
+设置看门狗溢出时间使用示例如下所示:
+
+```c
+#define WDT_DEVICE_NAME "wdt" /* 看门狗设备名称 */
+
+rt_uint32_t timeout = 1; /* 溢出时间,单位:秒*/
+static rt_device_t wdg_dev; /* 看门狗设备句柄 */
+/* 根据设备名称查找看门狗设备,获取设备句柄 */
+wdg_dev = rt_device_find(WDT_DEVICE_NAME);
+/* 初始化设备 */
+rt_device_init(wdg_dev);
+
+/* 设置看门狗溢出时间 */
+rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, (void *)timeout);
+/* 设置空闲线程回调函数 */
+rt_thread_idle_sethook(idle_hook);
+```
+
+在空闲线程钩子函数里喂狗使用示例如下所示:
+
+```c
+static void idle_hook(void)
+{
+ /* 在空闲线程的回调函数里喂狗 */
+ rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
+}
+```
+
+### 关闭看门狗
+
+当应用程序完成看门狗操作后,可以关闭看门狗设备,通过如下函数完成:
+
+```c
+rt_err_t rt_device_close(rt_device_t dev);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| dev | 看门狗设备句柄 |
+| **返回** | —— |
+| RT_EOK | 关闭设备成功 |
+| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
+| 其他错误码 | 关闭设备失败 |
+
+关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
+
+## 看门狗设备使用示例
+
+看门狗设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
+
+1. 根据设备名称 “wdt” 查找设备获取设备句柄。
+2. 初始化设备后设置看门狗溢出时间。
+3. 启动看门狗。
+4. 喂狗:设置空闲线程回调函数,在空闲线程回调函数中喂狗。
+
+```c
+/*
+ * 程序清单:这是一个独立看门狗设备使用例程
+ * 例程导出了 wdt_sample 命令到控制终端
+ * 命令调用格式:wdt_sample wdt
+ * 命令解释:命令第二个参数是要使用的看门狗设备名称,为空则使用例程默认的看门狗设备。
+ * 程序功能:程序通过设备名称查找看门狗设备,然后初始化设备并设置看门狗设备溢出时间。
+ * 然后设置空闲线程回调函数,在回调函数里会喂狗。
+*/
+
+#include
+#include
+
+#define WDT_DEVICE_NAME "wdt" /* 看门狗设备名称 */
+
+static rt_device_t wdg_dev; /* 看门狗设备句柄 */
+
+static void idle_hook(void)
+{
+ /* 在空闲线程的回调函数里喂狗 */
+ rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
+ rt_kprintf("feed the dog!\n ");
+}
+
+static int wdt_sample(int argc, char *argv[])
+{
+ rt_err_t ret = RT_EOK;
+ rt_uint32_t timeout = 1; /* 溢出时间,单位:秒 */
+ char device_name[RT_NAME_MAX];
+
+ /* 判断命令行参数是否给定了设备名称 */
+ if (argc == 2)
+ {
+ rt_strncpy(device_name, argv[1], RT_NAME_MAX);
+ }
+ else
+ {
+ rt_strncpy(device_name, WDT_DEVICE_NAME, RT_NAME_MAX);
+ }
+ /* 根据设备名称查找看门狗设备,获取设备句柄 */
+ wdg_dev = rt_device_find(device_name);
+ if (!wdg_dev)
+ {
+ rt_kprintf("find %s failed!\n", device_name);
+ return RT_ERROR;
+ }
+
+ /* 设置看门狗溢出时间 */
+ ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("set %s timeout failed!\n", device_name);
+ return RT_ERROR;
+ }
+ /* 启动看门狗 */
+ ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_START, RT_NULL);
+ if (ret != RT_EOK)
+ {
+ rt_kprintf("start %s failed!\n", device_name);
+ return -RT_ERROR;
+ }
+ /* 设置空闲线程回调函数 */
+ rt_thread_idle_sethook(idle_hook);
+
+ return ret;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(wdt_sample, wdt sample);
+```
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_1.png b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..6686efbcfe1e423b479e1e0a47166981f99d1422
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_3.png b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_3.png
new file mode 100644
index 0000000000000000000000000000000000000000..42c276770c0d4e862150468b57b3493f108935f5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_3.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_4.png b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_4.png
new file mode 100644
index 0000000000000000000000000000000000000000..ed59def6122d3b7fed77b7e5372060b80b95a166
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_4.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_5.png b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_5.png
new file mode 100644
index 0000000000000000000000000000000000000000..660f77c0015ba8b4af3299fc01fc92b7e11077f7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/figures/an0026_5.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/wlan.md b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/wlan.md
new file mode 100644
index 0000000000000000000000000000000000000000..7292c19b887c48ac65a78ce32dbf288a2449c54e
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/wlan.md
@@ -0,0 +1,963 @@
+# WLAN 设备
+
+随着物联网快速发展,越来越多的嵌入式设备上搭载了 WIFI 无线网络设备。为了能够管理 WIFI 网络设备,RT-Thread 引入了 WLAN 设备管理框架。这套框架具备控制和管理 WIFI 的众多功能,为开发者使用 WIFI 设备提供许多便利。
+
+## WLAN 框架简介
+
+WLAN 框架是 RT-Thread 开发的一套用于管理 WIFI 的中间件。对下连接具体的 WIFI 驱动,控制 WIFI 的连接断开,扫描等操作。对上承载不同的应用,为应用提供 WIFI 控制,事件,数据导流等操作,为上层应用提供统一的 WIFI 控制接口。WLAN 框架主要由三个部分组成。DEV 驱动接口层,为 WLAN 框架提供统一的调用接口。Manage 管理层为用户提供 WIFI 扫描,连接,断线重连等具体功能。Protocol 协议负责处理 WIFI 上产生的数据流,可根据不同的使用场景挂载不同通讯协议,如 LWIP 等。具有使用简单,功能齐全,对接方便,兼容性强等特点。
+
+下图是 WIFI 框架层次图:
+
+
+
+第一部分 app 为应用层。是基于 WLAN 框架的具体应用,如 WiFi 相关的 Shell 命令。
+
+第二部分 airkiss、voice 为配网层。提供无线配网和声波配网等功能。
+
+第三部分 WLAN manager 为 WLAN 管理层。能够对 WLAN 设备进行控制和管理。具备设置模式、连接热点、断开热点、启动热点、扫描热点等 WLAN 控制相关的功能。还提供断线重连,自动切换热点等管理功能。
+
+第四部分 WLAN protocol 为协议层。将数据流递交给具体协议进行解析,用户可以指定使用不同的协议进行通信。
+
+第五部分 WLAN config 为参数管理层。管理连接成功的热点信息及密码,并写入非易失的存储介质中。
+
+第六部分 WLAN dev 为驱动接口层。对接具体 WLAN 硬件,为管理层提供统一的调用接口。
+
+### 功能简介
+
+* 自动连接:打开自动连接功能后,只要 WIFI 处在断线状态,就会自动读取之前连接成功的热点信息,连接热点。如果一个热点连接失败,则切换下一个热点信息进行连接,直到连接成功为止。自动连接使用的热点信息,按连接成功的时间顺序,依次尝试,优先使用最近连接成功的热点信息。连接成功后,将热点信息缓存在最前面,下次断线优先使用。
+
+* 参数存储:存储连接成功的 WIFI 参数,WIFI 参数会在内存中缓存一份,如果配置外部非易失存储接口,则会在外部存储介质中存储一份。用户可根据自己的实际情况,实现 `struct rt_wlan_cfg_ops` 这个结构体,将参数保存任何地方。缓存的参数主要给自动连接提供热点信息,wifi 处在未连接状态时,会读取缓存的参数,尝试连接。
+
+* WIFI 控制:提供完备的 WIFI 控制接口,扫描,连接,热点等。提供 WIFI 相关状态回调事件,断开,连接,连接失败等。为用户提供简单易用的 WIFI 管理接口。
+
+* Shell 命令:可在 Msh 中输入命令控制 WIFI 执行扫描,连接,断开等动作。打印 WIFI 状态等调试信息。
+
+### 配置选项
+
+在 ENV工具中使用 `menuconfig`命令按照以下菜单进入 WLAN 配置界面:
+
+```c
+RT-Thread Components -> Device Drivers -> Using WiFi ->
+```
+
+各个配置选项详细描述如下:
+
+```shell
+(wlan0) The device name for station /* Station 设备默认名字 */
+(wlan1) The device name for ap /* AP 设备默认名字 */
+(32) SSID maximum length /* SSID 最大长度 */
+(64) Password maximum length /* 密码最大长度 */
+(2) Driver events maxcount /* 事件最大注册数 */
+[*] Connection management Enable /* 连接管理功能使能 */
+(10000) Set scan timeout time(ms) /* 扫描超时时间 */
+(10000) Set connect timeout time(ms) /* 连接超时时间 */
+[*] Automatic sorting of scan results /* 扫描结果自动排序 */
+[*] MSH command Enable /* MSH 命令功能使能 */
+[*] Auto connect Enable /* 自动连接功能使能 */
+(2000) Auto connect period(ms) /* 断线检查周期 */
+-*- WiFi information automatically saved Enable /* 连接参数自动存储使能 */
+(3) Maximum number of WiFi information automatically saved /* 连接参数最大存储数量 */
+[*] Transport protocol manage Enable /* 传输协议管理功能使能 */
+(8) Transport protocol name length /* 传输协议名字最大长度 */
+(2) Transport protocol maxcount /* 传输协议类型数量 */
+(lwip) Default transport protocol /* 默认传输协议名字 */
+[*] LWIP transport protocol Enable /* LWIP 传输协议使能 */
+(lwip) LWIP transport protocol name /* LWIP 传输协议名字 */
+[ ] Forced use of PBUF transmission /* 强制使用 PBUF 交换数据 */
+-*- WLAN work queue thread Enable /* WLAN 线程使能 */
+(wlan) WLAN work queue thread name /* WLAN 线程名字 */
+(2048) WLAN work queue thread size /* WLAN 线程栈大小 */
+(15) WLAN work queue thread priority /* WLAN 线程优先级 */
+[ ] Enable WLAN Debugging Options ---> /* 打开调试日志 */
+```
+
+应用程序通过 WLAN 连接管理层相关 API 来访问硬件设备,相关接口如下所示:
+
+## WLAN 初始化
+
+| **函数** | **描述** |
+| --------------------- | ---------------------- |
+| rt_wlan_init() | 初始化连接管理器 |
+| rt_wlan_set_mode() | 设置工作模式 |
+| rt_wlan_get_mode() | 获取设备工作模式 |
+
+### 连接管理初始化
+
+`int rt_wlan_init(void)`
+
+初始化连接管理器需要的静态资源,如全局变量,线程,互斥锁等。支持自动初始化,无需用户调用。如果没用使能自动初始化,在使用 WLAN 相关 API 之前,需要手动调用进行初始化。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|无 | |
+| **返回值** | **描述** |
+| 0 | 执行成功 |
+
+### 设置设备模式
+
+`rt_err_t rt_wlan_set_mode(const char *dev_name, rt_wlan_mode_t mode)`
+
+设置 WLAN 设备的工作模。同一个设备,切换相同的模式无效,一种模式,只能存在一个设备,不能两个设备设置同一个模式。一般的,一个设备只支持一种模式。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|`dev_name` | 设备名字 |
+|`mode` | 工作模式 |
+| **返回值** | **描述** |
+| RT_EOK | 设置成功 |
+| -RT_ERROR | 设置失败 |
+
+WLAN 设备工作模式如下:
+
+```c
+typedef enum
+{
+ RT_WLAN_NONE, /* 停止工作模式 */
+ RT_WLAN_STATION, /* 无线终端模式 */
+ RT_WLAN_AP, /* 无线接入服务模式 */
+ RT_WLAN_MODE_MAX /* 无效 */
+} rt_wlan_mode_t;
+```
+
+系统默认会提供一个默认的 STA 设备名和 AP 设备名,下面示例将展示默认 STA 设备工作在无线终端模式:
+
+```c
+rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
+```
+
+### 获取设备模式
+
+`rt_wlan_mode_t rt_wlan_get_mode(const char *dev_name)`
+
+获得设备的工作模式。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|`dev_name` | 设备名字 |
+| **返回值** | **描述** |
+| RT_WLAN_NONE | 设备停止工作 |
+| RT_WLAN_STATION | 无线终端模式 |
+| RT_WLAN_AP | 无线接入服务模式 |
+
+## WLAN 连接
+
+| **函数** | **描述** |
+| ------------------------ | ---------------------------- |
+| rt_wlan_connect() | 连接热点 |
+| rt_wlan_connect_adv() | 无阻塞连接热点 |
+| rt_wlan_disconnect() | 断开热点 |
+| rt_wlan_is_connected() | 获取连接标志 |
+| rt_wlan_is_ready() | 获取就绪标志 |
+| rt_wlan_get_info() | 获取连接信息 |
+| rt_wlan_get_rssi() | 获取信号强度 |
+
+### 连接热点
+
+`rt_err_t rt_wlan_connect(const char *ssid, const char *password)`
+
+阻塞式连接热点。此 API调用的时间会比较长,连接成功或失败后才会返回。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|`ssid` | 热点的名字 |
+|`password` | 热点密码,无密码传空 |
+| **返回** | **描述** |
+| RT_EOK | 连接成功 |
+| -RT_ERROR | 连接失败 |
+
+WLAN 连接成功,还不能进行数据通讯,需要等待连接就绪才能通讯。
+
+### 无阻塞连接
+
+`rt_err_t rt_wlan_connect_adv(struct rt_wlan_info *info, const char *password)`
+
+非阻塞连接热点,连接参数可通过扫描获得或手动指定。一般用于连接特定热点或隐藏热点,返回值仅表示连接动作是否开始执行,是否连接成功需要主动查询或设置回调通知。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|`info` | 连接信息 |
+|`password` | 热点密码,无密码传空 |
+| **返回** | **描述** |
+| RT_EOK | 执行成功 |
+| -RT_ERROR | 执行失败 |
+
+连接信息必须配置的项有`security`、`ssid`。完整配置如下:
+
+```c
+struct rt_wlan_info
+{
+ rt_wlan_security_t security; /* 安全类型 */
+ rt_802_11_band_t band; /* 2.4G / 5G */
+ rt_uint32_t datarate; /* 连接速率 */
+ rt_int16_t channel; /* 通道 */
+ rt_int16_t rssi; /* 信号强度 */
+ rt_wlan_ssid_t ssid; /* 热点名称 */
+ rt_uint8_t bssid[RT_WLAN_BSSID_MAX_LENGTH]; /* 热点物理地址 */
+ rt_uint8_t hidden; /* 热点隐藏标志 */
+};
+```
+
+安全模式如下所示:
+
+```c
+typedef enum
+{
+ SECURITY_OPEN = 0, /* Open security */
+ SECURITY_WEP_PSK = WEP_ENABLED, /* WEP Security with open authentication */
+ SECURITY_WEP_SHARED = (WEP_ENABLED | SHARED_ENABLED), /* WEP Security with shared authentication */
+ SECURITY_WPA_TKIP_PSK = (WPA_SECURITY | TKIP_ENABLED), /* WPA Security with TKIP */
+ SECURITY_WPA_AES_PSK = (WPA_SECURITY | AES_ENABLED), /* WPA Security with AES */
+ SECURITY_WPA2_AES_PSK = (WPA2_SECURITY | AES_ENABLED), /* WPA2 Security with AES */
+ SECURITY_WPA2_TKIP_PSK = (WPA2_SECURITY | TKIP_ENABLED), /* WPA2 Security with TKIP */
+ SECURITY_WPA2_MIXED_PSK = (WPA2_SECURITY | AES_ENABLED | TKIP_ENABLED), /* WPA2 Security with AES & TKIP */
+ SECURITY_WPS_OPEN = WPS_ENABLED, /* WPS with open security */
+ SECURITY_WPS_SECURE = (WPS_ENABLED | AES_ENABLED), /* WPS with AES security */
+ SECURITY_UNKNOWN = -1, /* security is unknown. */
+} rt_wlan_security_t;
+```
+
+下面将展示使用指定连接信息执行连接。
+
+```c
+struct rt_wlan_info info;
+
+INVALID_INFO(&info); /* 初始化 info */
+SSID_SET(&info, "test_ap"); /* 设置热点名字 */
+info.security = SECURITY_WPA2_AES_PSK; /* 指定安全类型 */
+rt_wlan_connect_adv(&info, "12345678"); /* 执行连接动作 */
+while (rt_wlan_is_connected() == RT_FALSE); /* 等待连接成功 */
+```
+
+### 断开热点
+
+`rt_err_t rt_wlan_disconnect(void)`
+
+阻塞式断开连接,返回值表示是否成功断开。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| RT_EOK | 断开成功 |
+| -RT_ERROR | 断开失败 |
+
+执行断开之前,建议先查询是否已经连接,如果已经连接,再执行断开。示例代码如下:
+
+```c
+if (rt_wlan_is_connected()) /* 判断是否已经连接 */
+{
+ rt_wlan_disconnect(); /* 断开连接 */
+}
+```
+
+### 获取连接标志
+
+`rt_bool_t rt_wlan_is_connected(void)`
+
+查询是否连接到热点。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| RT_TRUE | 已经连接 |
+| RT_FALSE | 没有连接 |
+
+### 获取就绪标志
+
+`rt_bool_t rt_wlan_is_ready(void)`
+
+查询连接是否就绪。一般的,获取到 IP 表示已经准备就绪,可以传输数据。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| RT_TRUE | 已经就绪 |
+| RT_FALSE | 没有就绪 |
+
+### 获取连接信息
+
+`rt_err_t rt_wlan_get_info(struct rt_wlan_info *info)`
+
+获取详细的连接信息,可获取热点名字、通道、信号强度、安全类型等。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `info` | info 对象 |
+| **返回** | **描述** |
+| RT_EOK | 获取成功 |
+| -RT_ERROR | 获取失败 |
+
+### 获取信号强度
+
+`int rt_wlan_get_rssi(void);`
+
+> 获得信号强度。信号强度为负值,值越大信号越强。例如信号强度 -25 比 信号强度 -55 要好。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| 负数 | 信号强度 |
+| 0 | 未连接 |
+
+## WLAN 扫描
+
+| **函数** | **描述** |
+| ----------------------------- | ----------------------- |
+| rt_wlan_scan() | 异步扫描 |
+| rt_wlan_scan_sync() | 同步扫描 |
+| rt_wlan_scan_with_info() | 条件扫描 |
+| rt_wlan_scan_get_info_num() | 获取热点个数 |
+| rt_wlan_scan_get_info() | 拷贝热点信息 |
+| rt_wlan_scan_get_result() | 获取扫描缓存 |
+| rt_wlan_scan_result_clean() | 清理扫描缓存 |
+| rt_wlan_find_best_by_cache() | 查找最佳热点 |
+
+### 异步扫描
+
+`rt_err_t rt_wlan_scan(void)`
+
+异步扫描函数,扫描完成需要通过回调进行通知。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| RT_EOK | 启动扫描成功 |
+| -RT_ERROR | 启动扫描失败 |
+
+### 同步扫描
+
+`struct rt_wlan_scan_result *rt_wlan_scan_sync(void)`
+
+> 同步扫描函数,扫描全部热点信息,完成过直接返回扫描结果。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| 扫描结果 | 热点信息和数量 |
+| RT_NULL | 扫描失败 |
+
+该接口执行成功,会返回 `struct rt_wlan_scan_result` 类型的指针,包含了热点的详细信息和数量。结构体定义如下:
+
+```c
+struct rt_wlan_scan_result
+{
+ rt_int32_t num; /* 热点个数 */
+ struct rt_wlan_info *info; /* 热点信息 */
+};
+```
+
+结构体中的 info 是连续的内存块,可通过类似数组的形式进行访问。示例如下:
+
+```c
+result = rt_wlan_scan_sync(void); /* 获取扫描结果 */
+for (i = 0; i < result->num; i++) /* 根据扫描到的结果,进行遍历 */
+{
+ printf("SSID:%s\n", result->info[i].ssid.val); /* 使用数组的形式进行访问,打印扫描到的 SSID 信息 */
+}
+```
+
+### 条件扫描
+
+`struct rt_wlan_scan_result *rt_wlan_scan_with_info(struct rt_wlan_info *info)`
+
+同步条件扫描。根据参入的条件进行过滤,可用于扫描指定 SSID。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `info` | 通过 info 指定限定条件 |
+| **返回** | **描述** |
+| 扫描结果 | 热点信息和数量 |
+| RT_NULL | 扫描失败 |
+
+下面示例将展示扫描指定 SSID 的热点信息。
+
+```c
+struct rt_wlan_info info;
+
+INVALID_INFO(&info); /* 初始化 info */
+SSID_SET(&info, "test_ap"); /* 指定 SSID */
+result = rt_wlan_scan_with_info(&info); /* 开始同步扫描 */
+```
+
+### 获取热点个数
+
+`int rt_wlan_scan_get_result_num(void)`
+
+返回扫描到的热点数量。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| 数量 | 热点数量 |
+
+### 拷贝热点信息
+
+`int rt_wlan_scan_get_info(struct rt_wlan_info *info, int num)`
+
+拷贝热点信息。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `info` | info 缓存,用于保存拷贝结果 |
+| `num` | info 个数 |
+| **返回** | **描述** |
+| 数量 | 实际拷贝的个数 |
+
+下面代码片段将展示如何拷贝热点信息
+
+```c
+num = rt_wlan_scan_get_result_num(); /* 查询热点数量 */
+info = rt_malloc(sizeof(struct rt_wlan_info) * num); /* 分配内存 */
+rt_wlan_scan_get_info(info, num); /* 拷贝 */
+```
+
+### 获取扫描缓存
+
+`struct rt_wlan_scan_result *rt_wlan_scan_get_result(void)`
+
+返回扫描缓存。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| 扫描缓存指针 | 该指针不安全,仅作临时访问 |
+
+### 清理扫描缓存
+
+`void rt_wlan_scan_result_clean(void)`
+
+清理扫描缓存。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| 无 | |
+
+### 查找最佳热点
+
+`rt_bool_t rt_wlan_find_best_by_cache(const char *ssid, struct rt_wlan_info *info)`
+
+指定 SSID ,在扫描缓存中查找信号最好的热点信息。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `ssid` | 指定需要查询的 ssid |
+| `info` | 存查询到的热点信息 |
+| **返回** | **描述** |
+| RT_FALSE | 没有查到 |
+| RT_TRUE | 查到 |
+
+## WLAN 热点
+
+| **函数** | **描述** |
+| ----------------------------- | ----------------------- |
+| rt_wlan_start_ap() | 启动热点 |
+| rt_wlan_start_ap_adv() | 无阻塞启动热点 |
+| rt_wlan_ap_is_active() | 获取启动标志 |
+| rt_wlan_ap_stop() | 停止热点 |
+| rt_wlan_ap_get_info() | 获取热点信息 |
+
+### 启动热点
+
+`rt_err_t rt_wlan_start_ap(const char *ssid, const char *password)`
+
+阻塞式启动热点,返回值表示是否启动成功。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `ssid` | 热点名字 |
+| `password` | 热点密码。 |
+| **返回** | **描述** |
+| RT_EOK | 启动成功 |
+| -RT_ERROR | 启动失败 |
+
+### 非阻塞启动热点
+
+`rt_err_t rt_wlan_start_ap_adv(struct rt_wlan_info *info, const char *password)`
+
+非阻塞启动热点,可以指加密类型,通道等。热点是否启动需要手动查询或回调通知。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `info` | 热点信息 |
+| `password` | 热点密码,开放热点传空 |
+| **返回** | **描述** |
+| RT_EOK | 执行成功 |
+| -RT_ERROR | 执行失败 |
+
+### 获取启动标志
+
+`rt_bool_t rt_wlan_ap_is_active(void)`
+
+> 查询热点是否处于活动状态。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| RT_TRUE | 热点启动 |
+| -RT_FALSE | 热点未启动 |
+
+### 停止热点
+
+`rt_err_t rt_wlan_ap_stop(void)`
+
+> 阻塞式停止热点。停止之前先查询是否已经启动,已经启动后在停止。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| RT_EOK | 停止成功 |
+| -RT_ERROR | 停止失败 |
+
+### 获取热点信息
+
+`rt_err_t rt_wlan_ap_get_info(struct rt_wlan_info *info)`
+
+> 获取热点相关信息,如热点名字,通道等。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| info | 热点信息 |
+| **返回** | **描述** |
+| RT_EOK | 获取成功 |
+| -RT_ERROR | 获取失败 |
+
+## WLAN 自动重连
+
+| **函数** | **描述** |
+| ---------------------------------- | --------------------- |
+| rt_wlan_config_autoreconnect() | 启动/停止自动重连 |
+| rt_wlan_get_autoreconnect_mode() | 获取自动重连模式 |
+
+### 启动/停止自动重连
+
+`void rt_wlan_config_autoreconnect(rt_bool_t enable)`
+
+开启或关闭自动重连模式,当没有网络时,会自动进行重连。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `enable` | 开启或关闭 |
+| **返回** | **描述** |
+| 无 | 执行成功 |
+
+### 获取自动重连模式
+
+`rt_bool_t rt_wlan_get_autoreconnect_mode(void)`
+
+查询自动重连是否启动。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| RT_TRUE | 启动 |
+| RT_FALSE | 未启动 |
+
+## WLAN 事件回调
+
+| **函数** | **描述** |
+| ------------------------------------ | ----------------- |
+| rt_wlan_register_event_handler() | 事件注册 |
+| rt_wlan_unregister_event_handler() | 解除注册 |
+
+### 事件注册
+
+`rt_err_t rt_wlan_register_event_handler(rt_wlan_event_t event, rt_wlan_event_handler handler, void *parameter)`
+
+> 注册事件回调函数。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `event` | 事件类型 |
+| `handler` | 事件处理函数 |
+| `parameter` | 用户参数 |
+| **返回** | **描述** |
+| RT_EOK | 注册成功 |
+| -RT_ERROR | 注册失败 |
+
+WLAN 产生如下事件时,触发回调:
+
+```c
+typedef enum
+{
+ RT_WLAN_EVT_READY = 0, /* 网络就绪 */
+ RT_WLAN_EVT_SCAN_DONE, /* 扫描完成 */
+ RT_WLAN_EVT_SCAN_REPORT, /* 扫描到一个热点 */
+ RT_WLAN_EVT_STA_CONNECTED, /* 连接成功 */
+ RT_WLAN_EVT_STA_CONNECTED_FAIL, /* 连接失败 */
+ RT_WLAN_EVT_STA_DISCONNECTED, /* 断开连接 */
+ RT_WLAN_EVT_AP_START, /* 热点启动 */
+ RT_WLAN_EVT_AP_STOP, /* 热点停止 */
+ RT_WLAN_EVT_AP_ASSOCIATED, /* STA 接入 */
+ RT_WLAN_EVT_AP_DISASSOCIATED, /* STA 断开 */
+} rt_wlan_event_t;
+```
+
+WLAN 回调函数定义为: `void (*rt_wlan_event_handler)(int event, struct rt_wlan_buff *buff, void *parameter);`,其中 `buff` 根据事件的不同有不同的涵义。详细信息参考下表。
+
+| 事件 | 类型 | 描述 |
+| :----------------------------- | :--------------------------- | :---------------------- |
+| RT_WLAN_EVT_READY | ip_addr_t * | IP 地址 |
+| RT_WLAN_EVT_SCAN_DONE | struct rt_wlan_scan_result * | 扫描的结果 |
+| RT_WLAN_EVT_SCAN_REPORT | struct rt_wlan_info * | 扫描到的热点信息 |
+| RT_WLAN_EVT_STA_CONNECTED | struct rt_wlan_info * | 连接成功的 Station 信息 |
+| RT_WLAN_EVT_STA_CONNECTED_FAIL | struct rt_wlan_info * | 连接失败的 Station 信息 |
+| RT_WLAN_EVT_STA_DISCONNECTED | struct rt_wlan_info * | 断开连接的 Station 信息 |
+| RT_WLAN_EVT_AP_START | struct rt_wlan_info * | 启动成功的 AP 信息 |
+| RT_WLAN_EVT_AP_STOP | struct rt_wlan_info * | 启动失败的 AP 信息 |
+| RT_WLAN_EVT_AP_ASSOCIATED | struct rt_wlan_info * | 连入的 Station 信息 |
+| RT_WLAN_EVT_AP_DISASSOCIATED | struct rt_wlan_info * | 断开的 Station 信息 |
+
+### 解除注册
+
+`rt_err_t rt_wlan_unregister_event_handler(rt_wlan_event_t event)`
+
+事件解除注册。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `event` | 事件类型 |
+| **返回** | **描述** |
+| RT_EOK | 解除成功 |
+
+## WLAN 功耗管理
+
+| **函数** | **描述** |
+| --------------------------------- | --------------------- |
+| rt_wlan_set_powersave() | 设置功耗等级 |
+| rt_wlan_get_powersave() | 获取功耗等级 |
+
+### 设置功耗等级
+
+设置功耗等级,用于 station 模式。
+
+`rt_err_t rt_wlan_set_powersave(int level)`
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| `level` | 功耗等级 |
+| **返回** | **描述** |
+| RT_EOK | 设置成功 |
+| -RT_ERROR | 设置失败 |
+
+### 获取功耗等级
+
+`int rt_wlan_get_powersave(void)`
+
+获取当前工作功耗等级。
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+| 无 | |
+| **返回** | **描述** |
+| 功耗级别 | |
+
+## FinSH 命令
+
+使用 shell 命令,可以帮助我们快速调试 WiFi 相关功能。wifi 相关的 shell 命令如下:
+
+```shell
+wifi /* 打印帮助 */
+wifi help /* 查看帮助 */
+wifi join SSID [PASSWORD] /* 连接 wifi,SSDI 为空,使用配置自动连接 */
+wifi ap SSID [PASSWORD] /* 建立热点 */
+wifi scan /* 扫描全部热点 */
+wifi disc /* 断开连接 */
+wifi ap_stop /* 停止热点 */
+wifi status /* 打印 wifi 状态 sta + ap */
+wifi smartconfig /* 启动配网功能 */
+```
+
+### WiFi 扫描
+
+wifi 扫描命令为 `wifi scan`,执行 wifi 扫描命令后,会将周围的热点信息打印在终端上。通过打印的热点信息,可以看到 SSID,MAC 地址等多项属性。
+
+在 msh 中输入该命令,扫描结果如下所示:
+
+```shell
+wifi scan
+SSID MAC security rssi chn Mbps
+------------------------------- ----------------- -------------- ---- --- ----
+rtt_test_ssid_1 c0:3d:46:00:3e:aa OPEN -14 8 300
+test_ssid 3c:f5:91:8e:4c:79 WPA2_AES_PSK -18 6 72
+rtt_test_ssid_2 ec:88:8f:88:aa:9a WPA2_MIXED_PSK -47 6 144
+rtt_test_ssid_3 c0:3d:46:00:41:ca WPA2_MIXED_PSK -48 3 300
+```
+
+### WiFi 连接
+
+wifi 扫描命令为 `wifi join`,命令后面需要跟热点名称和热点密码,没有密码可不输入这一项。执行 WiFi 连接命令后,如果热点存在,且密码正确,开发板会连接上热点,并获得 IP 地址。网络连接成功后,可使用 socket 套接字进行网络通讯。
+
+wifi 连接命令使用示例如下所示,连接成功后,将在终端上打印获得的 IP 地址,如下所示:
+
+```shell
+wifi join ssid_test 12345678
+[I/WLAN.mgnt] wifi connect success ssid:ssid_test
+[I/WLAN.lwip] Got IP address : 192.168.1.110
+```
+
+### WiFi 断开
+
+wifi 断开的命令为 `wifi disc`,执行 WiFi 断开命令后,开发板将断开与热点的连接。
+
+WiFi 断开命令使用示例如下所示,断开成功后,将在终端上打印如下信息,如下所示
+
+```shell
+wifi disc
+[I/WLAN.mgnt] disconnect success!
+```
+
+## WLAN 设备使用示例
+
+### 扫描示例
+
+下面这段代码将展示 WiFi 同步扫描,然后我们将结果打印在终端上。先需要执行 WIFI 初始化,然后执行 WIFI 扫描函数 `rt_wlan_scan_sync`, 这个函数是同步的,函数返回的扫描的数量和结果。在这个示例中,会将扫描的热点名字打印出来。
+
+```c
+#include
+#include
+
+#include
+#include
+#include
+
+void wifi_scan(void)
+{
+ struct rt_wlan_scan_result *result;
+ int i = 0;
+
+ /* Configuring WLAN device working mode */
+ rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
+ /* WiFi scan */
+ result = rt_wlan_scan_sync();
+ /* Print scan results */
+ rt_kprintf("scan num:%d\n", result->num);
+ for (i = 0; i < result->num; i++)
+ {
+ rt_kprintf("ssid:%s\n", result->info[i].ssid.val);
+ }
+}
+
+int scan(int argc, char *argv[])
+{
+ wifi_scan();
+ return 0;
+}
+MSH_CMD_EXPORT(scan, scan test.);
+```
+
+运行结果如下:
+
+
+
+### 连接与断开示例
+
+下面这段代码将展示 WiFi 同步连接。需要先执行 WIFI 初始化,然后创建一个用于等待 `RT_WLAN_EVT_READY` 事件的信号量。注册需要关注的事件的回调函数,执行 `rt_wlan_connect` wifi 连接函数,函数返回表示是否已经连接成功。但是连接成功还不能进行通信,还需要等待网络获取 IP。使用事先创建的信号量等待网络准备好,网络准备好后,就能正常通信了。
+
+连接上 WIFI 后,等待一段时间后,执行 `rt_wlan_disconnect` 函数断开连接。断开操作是阻塞的,返回值表示是否断开成功。
+
+```c
+#include
+#include
+
+#include
+#include
+#include
+
+#define WLAN_SSID "SSID-A"
+#define WLAN_PASSWORD "12345678"
+#define NET_READY_TIME_OUT (rt_tick_from_millisecond(15 * 1000))
+
+static rt_sem_t net_ready = RT_NULL;
+
+static void
+wifi_ready_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+ rt_sem_release(net_ready);
+}
+
+static void
+wifi_connect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+ if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
+ {
+ rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
+ }
+}
+
+static void
+wifi_disconnect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+ if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
+ {
+ rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
+ }
+}
+
+static void
+wifi_connect_fail_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+ if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
+ {
+ rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
+ }
+}
+
+rt_err_t wifi_connect(void)
+{
+ rt_err_t result = RT_EOK;
+
+ /* Configuring WLAN device working mode */
+ rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
+ /* station connect */
+ rt_kprintf("start to connect ap ...\n");
+ net_ready = rt_sem_create("net_ready", 0, RT_IPC_FLAG_FIFO);
+ rt_wlan_register_event_handler(RT_WLAN_EVT_READY,
+ wifi_ready_callback, RT_NULL);
+ rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED,
+ wifi_connect_callback, RT_NULL);
+ rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED,
+ wifi_disconnect_callback, RT_NULL);
+ rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED_FAIL,
+ wifi_connect_fail_callback, RT_NULL);
+
+ /* connect wifi */
+ result = rt_wlan_connect(WLAN_SSID, WLAN_PASSWORD);
+
+ if (result == RT_EOK)
+ {
+ /* waiting for IP to be got successfully */
+ result = rt_sem_take(net_ready, NET_READY_TIME_OUT);
+ if (result == RT_EOK)
+ {
+ rt_kprintf("networking ready!\n");
+ }
+ else
+ {
+ rt_kprintf("wait ip got timeout!\n");
+ }
+ rt_wlan_unregister_event_handler(RT_WLAN_EVT_READY);
+ rt_sem_delete(net_ready);
+
+ rt_thread_delay(rt_tick_from_millisecond(5 * 1000));
+ rt_kprintf("wifi disconnect test!\n");
+ /* disconnect */
+ result = rt_wlan_disconnect();
+ if (result != RT_EOK)
+ {
+ rt_kprintf("disconnect failed\n");
+ return result;
+ }
+ rt_kprintf("disconnect success\n");
+ }
+ else
+ {
+ rt_kprintf("connect failed!\n");
+ }
+ return result;
+}
+
+int connect(int argc, char *argv[])
+{
+ wifi_connect();
+ return 0;
+}
+MSH_CMD_EXPORT(connect, connect test.);
+```
+
+运行结果如下
+
+
+
+### 自动连接示例
+
+先开启自动重连功能,使用命令行连接上一个热点 A 后,在连接上另一个热点 B。等待几秒后,将热点 B 断电,系统会自动重试连接 B 热点,此时 B 热点连接不上,系统自动切换热点 A 进行连接。连接成功 A 后,系统停止连接。
+
+```c
+#include
+#include
+
+#include
+#include
+#include
+
+static void
+wifi_ready_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+}
+
+static void
+wifi_connect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+ if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
+ {
+ rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
+ }
+}
+
+static void
+wifi_disconnect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+ if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
+ {
+ rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
+ }
+}
+
+static void
+wifi_connect_fail_callback(int event, struct rt_wlan_buff *buff, void *parameter)
+{
+ rt_kprintf("%s\n", __FUNCTION__);
+ if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
+ {
+ rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
+ }
+}
+
+int wifi_autoconnect(void)
+{
+ /* Configuring WLAN device working mode */
+ rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
+ /* Start automatic connection */
+ rt_wlan_config_autoreconnect(RT_TRUE);
+ /* register event */
+ rt_wlan_register_event_handler(RT_WLAN_EVT_READY,
+ wifi_ready_callback, RT_NULL);
+ rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED,
+ wifi_connect_callback, RT_NULL);
+ rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED,
+ wifi_disconnect_callback, RT_NULL);
+ rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED_FAIL,
+ wifi_connect_fail_callback, RT_NULL);
+ return 0;
+}
+
+int auto_connect(int argc, char *argv[])
+{
+ wifi_autoconnect();
+ return 0;
+}
+MSH_CMD_EXPORT(auto_connect, auto connect test.);
+```
+
+运行结果如下:
+
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/dlmodule/dlmodule.md b/rt-thread-version/rt-thread-standard/programming-manual/dlmodule/dlmodule.md
new file mode 100644
index 0000000000000000000000000000000000000000..9d1bdcb500bfed2d65e3b1f7e5268fe661a2fe40
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/dlmodule/dlmodule.md
@@ -0,0 +1,311 @@
+# 动态模块 #
+
+在传统桌面操作系统中,用户空间和内核空间是分开的,应用程序运行在用户空间,内核以及内核模块则运行于内核空间,其中内核模块可以动态加载与删除以扩展内核功能。`dlmodule` 则是 RT-Thread 下,在内核空间对外提供的动态模块加载机制的软件组件。在 RT-Thread v3.1.0 以前的版本中,这也称之为应用模块(Application Module),在 RT-Thread v3.1.0 及之后,则回归传统,以动态模块命名。
+
+`dlmodule` 组件更多的是一个 ELF 格式加载器,把单独编译的一个 elf 文件的代码段,数据段加载到内存中,并对其中的符号进行解析,绑定到内核导出的 API 地址上。动态模块 elf 文件主要放置于 RT-Thread 下的文件系统上。
+
+## 功能简介 ##
+
+动态模块为 RT-Thread 提供了动态加载程序模块的机制,因为也独立于内核编译,所以使用方式比较灵活。从实现上讲,这是一种将内核和动态模块分开的机制,通过这种机制,内核和动态模块可以分开编译,并在运行时通过内核中的模块加载器将编译好的动态模块加载到内核中运行。
+
+在 RT-Thread 的动态模块中,目前支持两种格式:
+
+* `.mo` 则是编译出来时以 `.mo` 做为后缀名的可执行动态模块;它可以被加载,并且系统中会自动创建一个主线程执行这个动态模块中的 `main` 函数;同时这个 `main(int argc, char**argv)` 函数也可以接受命令行上的参数。
+* `.so` 则是编译出来时以 `.so` 做为后缀名的动态库;它可以被加载,并驻留在内存中,并提供一些函数集由其他程序(内核里的代码或动态模块)来使用。
+
+当前 RT-Thread 支持动态模块的架构主要包括 ARM 类架构和 x86 架构,未来会扩展到 MIPS,以及 RISC-V 等架构上。RT-Thread 内核固件部分可使用多种编译器工具链,如 GCC, ARMCC、IAR 等工具链;但动态模块部分编译当前只支持 GNU GCC 工具链编译。因此编译 RT-Thread 模块需下载 GCC 工具,例如 CodeSourcery 的 arm-none-eabi 工具链。一般的,最好内核和动态模块使用一样的工具链进行编译(这样不会在 libc 上产生不一致的行为)。另外,动态模块一般只能加载到 RAM 中使用,并进行符号解析绑定到内核导出的 API 地址上,而不能基于 Flash 直接以 XIP 方式运行(因为 Flash 上也不能够再行修改其中的代码段)。
+
+## 使用动态模块 ##
+
+当要在系统中测试使用动态模块,需要编译一份支持动态模块的固件,以及需要运行的动态模块。下面将固件和动态模块的编译方式分为两部分进行介绍。
+
+### 编译固件 ###
+
+当要使用动态模块时,需要在固件的配置中打开对应的选项,使用 menuconfig 打开如下配置:
+
+```c
+ RT-Thread Components --->
+ POSIX layer and C standard library --->
+ [*] Enable dynamic module with dlopen/dlsym/dlclose feature
+```
+
+也要打开文件系统的配置选项:
+
+```c
+ RT-Thread Components --->
+ Device virtual file system --->
+ [*] Using device virtual file system
+```
+
+bsp 对应的 rtconfig.py 中设置动态模块编译时需要用到的配置参数:
+
+```Python
+M_CFLAGS = CFLAGS + ' -mlong-calls -fPIC '
+M_CXXFLAGS = CXXFLAGS + ' -mlong-calls -fPIC'
+M_LFLAGS = DEVICE + CXXFLAGS + ' -Wl,--gc-sections,-z,max-page-size=0x4' +\
+ ' -shared -fPIC -nostartfiles -nostdlib -static-libgcc'
+M_POST_ACTION = STRIP + ' -R .hash $TARGET\n' + SIZE + ' $TARGET \n'
+M_BIN_PATH = r'E:\qemu-dev310\fatdisk\root'
+```
+
+相关的解释如下:
+
+* M_CFLAGS - 动态模块编译时用到的 C 代码编译参数,一般此处以 PIC 方式进行编译(即代码地址支持浮动方式执行);
+* M_CXXFLAGS - 动态模块编译时用到的 C++ 代码编译参数,参数和上面的 `M_CFLAGS` 类似;
+* M_LFLAGS - 动态模块进行链接时的参数。同样是 PIC 方式,并且是按照共享库方式链接(部分链接);
+* M_POST_ACTIOn - 动态模块编译完成后要进行的动作,这里会对 elf 文件进行 strip 下,以减少 elf 文件的大小;
+* M_BIN_PATH - 当动态模块编译成功时,对应的动态模块文件是否需要复制到统一的地方;
+
+基本上来说,ARM9、Cortex-A、Cortex-M 系列的这些编译配置参数是一样的。
+
+内核固件也会通过 `RTM(function)` 的方式导出一些函数 API 给动态模块使用,这些导出符号可以在 msh 下通过命令:
+
+`list_symbols`
+
+列出固件中所有导出的符号信息。`dlmodule` 加载器也是把动态模块中需要解析的符号按照这里导出的符号表来进行解析,完成最终的绑定动作。
+
+这段符号表会放在一个专门的,名字是 RTMSymTab 的 section 中,所以对应的固件链接脚本也需要保留这块区域,而不会被链接器优化移除掉。可以在链接脚本中添加对应的信息:
+
+```text
+/* section information for modules */
+. = ALIGN(4);
+__rtmsymtab_start = .;
+KEEP(*(RTMSymTab))
+__rtmsymtab_end = .;
+```
+
+然后在 BSP 工程目录下执行 `scons` 正确无误地生成固件后,在 BSP 工程目录下执行一下命令:
+
+`scons --target=ua -s`
+
+来生成编译动态模块时需要包括的内核头文件搜索路径及全局宏定义。
+
+### 编译动态模块 ###
+
+在 github 上有一份独立仓库: [rtthread-apps](https://github.com/RT-Thread/rtthread-apps) ,这份仓库中放置了一些和动态模块,动态库相关的示例。
+
+其目录结构如下:
+
+| **目录名** | **说明** |
+| --- | ---------------- |
+| cxx | 演示了如何在动态模块中使用 C++ 进行编程 |
+| hello | 最简单的 `hello world` 示例 |
+| lib | 动态库的示例 |
+| md5 | 为一个文件产生 md5 码 |
+| tools | 动态模块编译时需要使用到的 Python/SConscript 脚本 |
+| ymodem | 通过串口以 YModem 协议下载一个文件到文件系统上 |
+
+可以把这份 git clone 到本地,然后在命令行下以 scons 工具进行编译,如果是 Windows 平台,推荐使用 RT-Thread/ENV 工具。
+
+进入控制台命令行后,进入到这个 rtthread-apps repo 所在的目录(同样的,请保证这个目录所在全路径不包含空格,中文字符等字符),并设置好两个变量:
+
+* RTT_ROOT - 指向到 RT-Thread 代码的根目录;
+* BSP_ROOT - 指向到 BSP 的工程目录;
+
+Windows 下可以使用 (假设使用的 BSP 是 qemu-vexpress-a9):
+
+```c
+set RTT_ROOT=d:\your_rtthread
+set BSP_ROOT=d:\your_rtthread\bsp\qemu-vexpress-a9
+```
+
+来设置对应的环境变量。然后使用如下命令来编译动态模块,例如 hello 的例子:
+
+`scons --app=hello`
+
+编译成功后,它会在 rtthread-apps/hello 目录下生成 hello.mo 文件。
+
+也可以编译动态库,例如 lib 的例子:
+
+`scons --lib=lib`
+
+编译成功后,它会在 rtthread-apps/lib 目录下生成 lib.so 文件。
+
+我们可以把这些 mo、so 文件放到 RT-Thread 文件系统下。在 msh 下,可以简单的以 `hello` 命令方式执行 `hello.mo` 动态模块:
+
+```c
+msh />ls
+Directory /:
+hello.mo 1368
+lib.so 1376
+msh />hello
+msh />Hello, world
+```
+
+调用 hello 后,会执行 hello.mo 里的 main 函数,执行完毕后退出对应的动态模块。其中 `hello/main.c` 的代码如下:
+
+```c
+#include
+
+int main(int argc, char *argv[])
+{
+ printf("Hello, world\n");
+
+ return 0;
+}
+```
+
+## RT-Thread 动态模块 API
+
+除了可以通过 msh 直接加载并执行动态模块外,也可以在主程序中使用 RT-Thread 提供的动态模块 API 来加载或卸载动态模块。
+
+### 加载动态模块
+
+```c
+struct rt_dlmodule *dlmodule_load(const char* pgname);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| pgname | 动态模块的路径 |
+|**返回**| —— |
+| 动态模块指针 | 成功加载 |
+| RT_NULL | 失败 |
+
+这个函数从文件系统中加载动态模块到内存中,若正确加载返回该模块的指针。这个函数并不会创建一个线程去执行这个动态模块,仅仅把模块加载到内存中,并解析其中的符号地址。
+
+### 执行动态模块
+
+```c
+struct rt_dlmodule *dlmodule_exec(const char* pgname, const char* cmd, int cmd_size);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| pgname | 动态模块的路径 |
+| cmd | 包括动态模块命令自身的命令行字符串 |
+| cmd_size | 命令行字符串大小 |
+|**返回**| —— |
+| 动态模块指针 | 成功运行 |
+| RT_NULL | 失败 |
+
+这个函数根据 `pgname` 路径加载动态模块,并启动一个线程来执行这个动态模块的 `main` 函数,同时 `cmd` 会作为命令行参数传递给动态模块的 `main` 函数入口。
+
+### 退出动态模块
+
+```c
+void dlmodule_exit(int ret_code);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| ret_code | 模块的返回参数 |
+
+这个函数由模块运行时调用,它可以设置模块退出的返回值 `ret_code`,然后从模块退出。
+
+### 查找动态模块
+
+```c
+struct rt_dlmodule *dlmodule_find(const char *name);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| name | 模块名称 |
+|**返回**| —— |
+| 动态模块指针 | 成功 |
+| RT_NULL | 失败 |
+
+这个函数以 `name` 查找系统中是否已经有加载的动态模块。
+
+### 返回动态模块
+
+```c
+struct rt_dlmodule *dlmodule_self(void);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+|**返回**| —— |
+| 动态模块指针 | 成功 |
+| RT_NULL | 失败 |
+
+这个函数返回调用上下文环境下动态模块的指针。
+
+### 查找符号
+
+```c
+rt_uint32_t dlmodule_symbol_find(const char *sym_str);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| sym_str | 符号名称 |
+|**返回**| —— |
+| 符号地址 | 成功 |
+| 0 | 失败 |
+
+这个函数根据符号名称返回符号地址。
+
+## 标准 POSIX 动态库 libdl API ##
+
+在 RT-Thread dlmodule 中也支持 POSIX 标准的 libdl API,类似于把一个动态库加载到内存中(并解析其中的一些符号信息),由这份动态库提供对应的函数操作集。libdl API 需要包含的头文件:
+
+`#include `
+
+### 打开动态库
+
+```c
+void * dlopen (const char * pathname, int mode);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| pathname | 动态库路径名称 |
+| mode | 打开动态库时的模式,在 RT-Thread 中并未使用 |
+|**返回**| —— |
+| 动态库的句柄 (`struct dlmodule` 结构体指针) | 成功 |
+| NULL | 失败 |
+
+这个函数类似 `dlmodule_load` 的功能,会从文件系统上加载动态库,并返回动态库的句柄指针。
+
+### 查找符号
+
+```c
+void* dlsym(void *handle, const char *symbol);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| handle | 动态库句柄,`dlopen` 的返回值 |
+| symbol | 要返回的符号地址 |
+|**返回**| —— |
+| 符号地址 | 成功 |
+| NULL | 失败 |
+
+这个函数在动态库 `handle` 中查找是否存在 `symbol` 的符号,如果存在返回它的地址。
+
+### 关闭动态库
+
+```
+int dlclose (void *handle);
+```
+
+|**参数**|**描述**|
+| ---- | ---- |
+| handle | 动态库句柄 |
+|**返回**| —— |
+| 0 | 成功 |
+| 负数 | 失败 |
+
+这个函数会关闭 `handle` 指向的动态库,从内存中卸载掉。需要注意的是,当动态库关闭后,原来通过 `dlsym` 返回的符号地址将不再可用。如果依然尝试去访问,可能会引起 fault 错误。
+
+## 常见问题
+
+### Env 工具的相关问题请参考 [《Env 用户手册》](../env/env.md)。
+
+### Q: 根据文档不能成功运行动态模块。
+
+**A:** 请更新 RT-Thread 源代码到 3.1.0 及以上版本。
+
+### Q: 使用 scons 命令编译工程,提示 undefined reference to __rtmsymtab_start。
+
+**A:** 请参考 qemu-vexpress-a9 BSP 的 GCC 链接脚本文件 link.lds,在工程的 GCC 链接脚本的 TEXT 段增加以下内容。
+
+```
+ /* section information for modules */
+ . = ALIGN(4);
+ __rtmsymtab_start = .;
+ KEEP(*(RTMSymTab))
+ __rtmsymtab_end = .;
+```
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/dfs2.txt b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/dfs2.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c88135ce95ba97421d0f71b2ebfac64016eb241a
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/dfs2.txt
@@ -0,0 +1,21 @@
+@startuml
+
+participant 初始化模块
+
+participant SPIFlash驱动
+
+participant SFUD模块
+
+participant IO设备管理器
+
+初始化模块->SPIFlash驱动: 初始化SPIFlash spiflash_init()
+
+SPIFlash驱动->IO设备管理器: 创建并注册SPI从设备 rt_device_register()
+
+初始化模块->SFUD模块: 探测设备rt_sfud_flash_probe()
+
+SFUD模块->IO设备管理器: 查找SPIFlash从设备rt_device_find()
+
+SFUD模块->IO设备管理器: 创建并注册块设备rt_device_register()
+
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/dfs4.txt b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/dfs4.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d314c7e0bed9323c8d741f691b227a5f8bfd5097
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/dfs4.txt
@@ -0,0 +1,22 @@
+@startuml
+
+participant Application
+participant DFS
+participant Device
+
+Application->DFS: dfs_mkfs()
+activate Application
+
+DFS-->Application
+deactivate Application
+activate DFS
+
+DFS->Device: rt_device_find()
+activate Device
+deactivate DFS
+
+Device-->DFS
+deactivate Device
+activate DFS
+
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-mkfs b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-mkfs
new file mode 100644
index 0000000000000000000000000000000000000000..5ebda076abef4b9e2433290068a713f347706790
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-mkfs
@@ -0,0 +1,18 @@
+@startuml
+
+participant Ӧó
+participant DFS
+participant elmFATļϵͳ
+participant IO豸
+participant SFUDģ
+participant SPIFlash
+
+Ӧó->DFS: ʽ dfs_mkfs()
+
+DFS->IO豸: 豸 rt_device_find()
+DFS->elmFATļϵͳ: FATʽ dfs_elm_mkfs()
+elmFATļϵͳ->IO豸: rt_device_write()
+IO豸->SFUDģ: rt_sfud_write()
+SFUDģ->SPIFlash: spiflash_write()
+
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-mkfs.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-mkfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..15113fee426344f3ccd8f7a030aa10430978630a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-mkfs.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-reg b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-reg
new file mode 100644
index 0000000000000000000000000000000000000000..10653ac734d717ff98bb54f0b655f8a15a0b7583
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-reg
@@ -0,0 +1,15 @@
+@startuml
+
+participant ʼģ
+
+participant DFS
+
+participant elmFATļϵͳ
+
+ʼģ->DFS: ʼDFS DFS_init()
+
+ʼģ->elmFATļϵͳ: ʼFATļϵͳ elm_init()
+
+elmFATļϵͳ->DFS: עFATļϵͳ dfs_register()
+
+@enduml
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-reg.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-reg.png
new file mode 100644
index 0000000000000000000000000000000000000000..71b7878403d6230da98ffeafa1c6d06efea6ab12
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/elm-fat-reg.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-dir-mg.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-dir-mg.png
new file mode 100644
index 0000000000000000000000000000000000000000..9df97048841e864bb5c878e3f459055126f7540f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-dir-mg.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-dir.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..14202280171c5a78742e2b3cc5790398a4740c2d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-dir.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-layer.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-layer.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0e0772cad4ea14dfb52d0b6274c2c21f1ddf5c7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-layer.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mdk.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..2496dee3e5570f8a3074d01eaabc964f8bae16e0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mdk.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mg.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mg.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa8505119fd2c8b0aeb5a9c1c8d57094a3da97fe
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mg.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mkfs.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mkfs.png
new file mode 100644
index 0000000000000000000000000000000000000000..71b7878403d6230da98ffeafa1c6d06efea6ab12
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-mkfs.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-reg-block.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-reg-block.png
new file mode 100644
index 0000000000000000000000000000000000000000..77869c78c4e61e07337ab5a0d2697f3a4ff6378e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-reg-block.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-reg.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-reg.png
new file mode 100644
index 0000000000000000000000000000000000000000..71b7878403d6230da98ffeafa1c6d06efea6ab12
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-reg.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-spi-flash.png b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-spi-flash.png
new file mode 100644
index 0000000000000000000000000000000000000000..8cf06f8bdf9204e831b025a12825868a2f738e25
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/figures/fs-spi-flash.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/filesystem/filesystem.md b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/filesystem.md
new file mode 100644
index 0000000000000000000000000000000000000000..bae47a3cda019e1d6cdcf870fd7a7925e4c8da27
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/filesystem/filesystem.md
@@ -0,0 +1,1049 @@
+# 虚拟文件系统
+
+在早期的嵌入式系统中,需要存储的数据比较少,数据类型也比较单一,往往使用直接在存储设备中的指定地址写入数据的方法来存储数据。然而随着嵌入式设备功能的发展,需要存储的数据越来越多,也越来越复杂,这时仍使用旧方法来存储并管理数据就变得非常繁琐困难。因此我们需要新的数据管理方式来简化存储数据的组织形式,这种方式就是我们接下来要介绍的文件系统。
+
+文件系统是一套实现了数据的存储、分级组织、访问和获取等操作的抽象数据类型 (Abstract data type),是一种用于向用户提供底层数据访问的机制。文件系统通常存储的基本单位是文件,即数据是按照一个个文件的方式进行组织。当文件比较多时,将导致文件繁多,不易分类、重名的问题。而文件夹作为一个容纳多个文件的容器而存在。
+
+本章讲解 RT-Thread 文件系统相关内容,带你了解 RT-Thread 虚拟文件系统的架构、功能特点和使用方式。
+
+## DFS 简介
+
+DFS 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格,目录结构如下图所示:
+
+
+
+在 RT-Thread DFS 中,文件系统有统一的根目录,使用 `/` 来表示。而在根目录下的 f1.bin 文件则使用 `/f1.bin` 来表示,2018 目录下的 `f1.bin` 目录则使用 `/data/2018/f1.bin` 来表示。即目录的分割符号是 `/`,这与 UNIX/Linux 完全相同,与 Windows 则不相同(Windows 操作系统上使用 `\` 来作为目录的分割符)。
+
+### DFS 架构
+
+RT-Thread DFS 组件的主要功能特点有:
+
+* 为应用程序提供统一的 POSIX 文件和目录操作接口:read、write、poll/select 等。
+
+* 支持多种类型的文件系统,如 FatFS、RomFS、DevFS 等,并提供普通文件、设备文件、网络文件描述符的管理。
+
+* 支持多种类型的存储设备,如 SD Card、SPI Flash、Nand Flash 等。
+
+DFS 的层次架构如下图所示,主要分为 POSIX 接口层、虚拟文件系统层和设备抽象层。
+
+
+
+### POSIX 接口层
+
+POSIX 表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写 POSIX),POSIX 标准定义了操作系统应该为应用程序提供的接口标准,是 IEEE 为要在各种 UNIX 操作系统上运行的软件而定义的一系列 API 标准的总称。
+
+POSIX 标准意在期望获得源代码级别的软件可移植性。换句话说,为一个 POSIX 兼容的操作系统编写的程序,应该可以在任何其它 POSIX 操作系统(即使是来自另一个厂商)上编译执行。RT-Thread 支持 POSIX 标准接口,因此可以很方便的将 Linux/Unix 的程序移植到 RT-Thread 操作系统上。
+
+在类 Unix 系统中,普通文件、设备文件、网络文件描述符是同一种文件描述符。而在 RT-Thread 操作系统中,使用 DFS 来实现这种统一性。有了这种文件描述符的统一性,我们就可以使用 poll/select 接口来对这几种描述符进行统一轮询,为实现程序功能带来方便。
+
+使用 poll/select 接口可以阻塞地同时探测一组支持非阻塞的 I/O 设备是否有事件发生(如可读,可写,有高优先级的错误输出,出现错误等等),直至某一个设备触发了事件或者超过了指定的等待时间。这种机制可以帮助调用者寻找当前就绪的设备,降低编程的复杂度。
+
+### 虚拟文件系统层
+
+用户可以将具体的文件系统注册到 DFS 中,如 FatFS、RomFS、DevFS 等,下面介绍几种常用的文件系统类型:
+
+* FatFS 是专为小型嵌入式设备开发的一个兼容微软 FAT 格式的文件系统,采用 ANSI C 编写,具有良好的硬件无关性以及可移植性,是 RT-Thread 中最常用的文件系统类型。
+
+* 传统型的 RomFS 文件系统是一种简单的、紧凑的、只读的文件系统,不支持动态擦写保存,按顺序存放数据,因而支持应用程序以 XIP(execute In Place,片内运行) 方式运行,在系统运行时, 节省 RAM 空间。
+
+* Jffs2 文件系统是一种日志闪存文件系统。主要用于 NOR 型闪存,基于 MTD 驱动层,特点是:可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃 / 掉电安全保护,提供写平衡支持等。
+
+* DevFS 即设备文件系统,在 RT-Thread 操作系统中开启该功能后,可以将系统中的设备在 /dev 文件夹下虚拟成文件,使得设备可以按照文件的操作方式使用 read、write 等接口进行操作。
+
+* NFS 网络文件系统(Network File System)是一项在不同机器、不同操作系统之间通过网络共享文件的技术。在操作系统的开发调试阶段,可以利用该技术在主机上建立基于 NFS 的根文件系统,挂载到嵌入式设备上,可以很方便地修改根文件系统的内容。
+
+* UFFS 是 Ultra-low-cost Flash File System(超低功耗的闪存文件系统)的简称。它是国人开发的、专为嵌入式设备等小内存环境中使用 Nand Flash 的开源文件系统。与嵌入式中常使用的 Yaffs 文件系统相比具有资源占用少、启动速度快、免费等优势。
+
+### 设备抽象层
+
+设备抽象层将物理设备如 SD Card、SPI Flash、Nand Flash,抽象成符合文件系统能够访问的设备,例如 FAT 文件系统要求存储设备必须是块设备类型。
+
+不同文件系统类型是独立于存储设备驱动而实现的,因此把底层存储设备的驱动接口和文件系统对接起来之后,才可以正确地使用文件系统功能。
+
+## 挂载管理
+
+文件系统的初始化过程一般分为以下几个步骤:
+
+1. 初始化 DFS 组件。
+2. 初始化具体类型的文件系统。
+3. 在存储器上创建块设备。
+4. 格式化块设备。
+5. 挂载块设备到 DFS 目录中。
+6. 当文件系统不再使用,可以将它卸载。
+
+### 初始化 DFS 组件
+
+DFS 组件的的初始化是由 dfs_init() 函数完成。dfs_init() 函数会初始化 DFS 所需的相关资源,创建一些关键的数据结构, 有了这些数据结构,DFS 便能在系统中找到特定的文件系统,并获得对特定存储设备内文件的操作方法。如果开启了自动初始化(默认开启),该函数将被自动调用。
+
+### 注册文件系统
+
+在 DFS 组件初始化之后,还需要初始化使用的具体类型的文件系统,也就是将具体类型的文件系统注册到 DFS 中。注册文件系统的接口如下所示:
+
+```c
+int dfs_register(const struct dfs_filesystem_ops *ops);
+```
+
+|**参数**|**描述** |
+|----------|------------------------------------|
+| ops | 文件系统的操作函数的集合 |
+|**返回**|**——** |
+| 0 | 文件注册成功 |
+| -1 | 文件注册失败 |
+
+该函数不需要用户调用,他会被不同文件系统的初始化函数调用,如 elm-FAT 文件系统的初始化函数`elm_init()`。开启对应的文件系统后,如果开启了自动初始化(默认开启),文件系统初始化函数也将被自动调用。
+
+`elm_init()` 函数会初始化 elm-FAT 文件系统,此函数会调用 `dfs_register(`) 函数将 elm-FAT 文件系统注册到 DFS 中,文件系统注册过程如下图所示:
+
+
+
+### 将存储设备注册为块设备
+
+因为只有块设备才可以挂载到文件系统上,因此需要在存储设备上创建所需的块设备。如果存储设备是 SPI Flash,则可以使用 “串行 Flash 通用驱动库 SFUD” 组件,它提供了各种 SPI Flash 的驱动,并将 SPI Flash 抽象成块设备用于挂载,注册块设备过程如下图所示:
+
+
+
+### 格式化文件系统
+
+注册了块设备之后,还需要在块设备上创建指定类型的文件系统,也就是格式化文件系统。可以使用 `dfs_mkfs()` 函数对指定的存储设备进行格式化,创建文件系统,格式化文件系统的接口如下所示:
+
+```c
+int dfs_mkfs(const char * fs_name, const char * device_name);
+```
+
+|**参数** |**描述** |
+|-------------|----------------------------|
+| fs_name | 文件系统类型 |
+| device_name | 块设备名称 |
+|**返回** |**——** |
+| 0 | 文件系统格式化成功 |
+| -1 | 文件系统格式化失败 |
+
+文件系统类型(fs_name)可取值及对应的文件系统如下表所示:
+
+|**取值** |**文件系统类型** |
+|-------------|----------------------------|
+| elm | elm-FAT 文件系统 |
+| jffs2 | jffs2 日志闪存文件系统 |
+| nfs | NFS 网络文件系统 |
+| ram | RamFS 文件系统 |
+| rom | RomFS 只读文件系统 |
+| uffs | uffs 文件系统 |
+
+以 elm-FAT 文件系统格式化块设备为例,格式化过程如下图所示:
+
+
+
+还可以使用 `mkfs` 命令格式化文件系统,格式化块设备 sd0 的运行结果如下所示:
+
+```c
+msh />mkfs sd0 # sd0 为块设备名称,该命令会默认格式化 sd0 为 elm-FAT 文件系统
+msh />
+msh />mkfs -t elm sd0 # 使用 -t 参数指定文件系统类型为 elm-FAT 文件系统
+```
+
+### 挂载文件系统
+
+在 RT-Thread 中,挂载是指将一个存储设备挂接到一个已存在的路径上。我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的路径上,然后通过这个路径来访问存储设备。挂载文件系统的接口如下所示:
+
+```c
+int dfs_mount(const char *device_name,
+ const char *path,
+ const char *filesystemtype,
+ unsigned long rwflag,
+ const void *data);
+```
+
+|**参数** |**描述** |
+|----------------|------------------------------|
+| device_name | 已经格式化的块设备名称 |
+| path | 挂载路径,即挂载点 |
+| filesystemtype | 挂载的文件系统类型,可取值见 dfs_mkfs() 函数描述 |
+| rwflag | 读写标志位 |
+| data | 特定文件系统的私有数据 |
+|**返回** | **——** |
+| 0 | 文件系统挂载成功 |
+| -1 | 文件系统挂载失败 |
+
+如果只有一个存储设备,则可以直接挂载到根目录 `/` 上。
+
+### 卸载文件系统
+
+当某个文件系统不需要再使用了,那么可以将它卸载掉。卸载文件系统的接口如下所示:
+
+```c
+int dfs_unmount(const char *specialfile);
+```
+
+|**参数** |**描述** |
+|-------------|--------------------------|
+| specialfile | 挂载路径 |
+|**返回** |**——** |
+| 0 | 卸载文件系统成功 |
+| -1 | 卸载文件系统失败 |
+
+## 文件管理
+
+本节介绍对文件进行操作的相关函数,对文件的操作一般都要基于文件描述符 fd,如下图所示:
+
+
+
+### 打开和关闭文件
+
+打开或创建一个文件可以调用下面的 open() 函数:
+
+```c
+int open(const char *file, int flags, ...);
+```
+
+|**参数** |**描述** |
+|------------|--------------------------------------|
+| file | 打开或创建的文件名 |
+| flags | 指定打开文件的方式,取值可参考下表 |
+|**返回** |**——** |
+| 文件描述符 | 文件打开成功 |
+| -1 | 文件打开失败 |
+
+一个文件可以以多种方式打开,并且可以同时指定多种打开方式。例如,一个文件以 O_RDONLY 和 O_CREAT 的方式打开,那么当指定打开的文件不存在时,就会先创建这个文件,然后再以只读的方式打开。文件打开方式如下表所示:
+
+|**参数**|**描述** |
+|----------|-----------------------|
+| O_RDONLY | 只读方式打开文件 |
+| O_WRONLY | 只写方式打开文件 |
+| O_RDWR | 以读写方式打开文件 |
+| O_CREAT | 如果要打开的文件不存在,则建立该文件 |
+| O_APPEND | 当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式添加到文件的尾部 |
+| O_TRUNC | 如果文件已经存在,则清空文件中的内容 |
+
+当使用完文件后若不再需要使用则可使用 `close()` 函数关闭该文件,而 `close()` 会让数据写回磁盘,并释放该文件所占用的资源。
+
+```
+int close(int fd);
+```
+
+|**参数**|**描述** |
+|----------|--------------|
+| fd | 文件描述符 |
+|**返回**|**——** |
+| 0 | 文件关闭成功 |
+| -1 | 文件关闭失败 |
+
+### 读写数据
+
+读取文件内容可使用 `read()` 函数:
+
+```
+int read(int fd, void *buf, size_t len);
+```
+
+|**参数**|**描述** |
+|----------|------------------------------------------|
+| fd | 文件描述符 |
+| buf | 缓冲区指针 |
+| len | 读取文件的字节数 |
+|**返回**|**——** |
+| int | 实际读取到的字节数 |
+| 0 | 读取数据已到达文件结尾或者无可读取的数据 |
+| -1 | 读取出错,错误代码查看当前线程的 errno |
+
+该函数会把参数 fd 所指的文件的 len 个字节读取到 buf 指针所指的内存中。此外文件的读写位置指针会随读取到的字节移动。
+
+向文件中写入数据可使用 `write()` 函数:
+
+```
+int write(int fd, const void *buf, size_t len);
+```
+
+|**参数**|**描述** |
+|----------|---------------------------------------|
+| Fd | 文件描述符 |
+| buf | 缓冲区指针 |
+| len | 写入文件的字节数 |
+|**返回**|**——** |
+| int | 实际写入的字节数 |
+| -1 | 写入出错,错误代码查看当前线程的 errno |
+
+该函数会把 buf 指针所指向的内存中 len 个字节写入到参数 fd 所指的文件内。此外文件的读写位置指针会随着写入的字节移动。
+
+### 重命名
+
+重命名文件可使用 `rename()` 函数:
+
+```
+int rename(const char *old, const char *new);
+```
+
+|**参数**|**描述** |
+|----------|--------------|
+| old | 旧文件名 |
+| new | 新文件名 |
+|**返回**|**——** |
+| 0 | 更改名称成功 |
+| -1 | 更改名称失败 |
+
+该函数会将参数 old 所指定的文件名称改为参数 new 所指的文件名称。若 new 所指定的文件已经存在,则该文件将会被覆盖。
+
+### 取得状态
+
+获取文件状态可使用下面的 `stat()` 函数:
+
+```
+int stat(const char *file, struct stat *buf);
+```
+
+|**参数**|**描述** |
+|----------|--------------------------------------------|
+| file | 文件名 |
+| buf | 结构指针,指向一个存放文件状态信息的结构体 |
+|**返回**|**——** |
+| 0 | 获取状态成功 |
+| -1 | 获取状态失败 |
+
+### 删除文件
+
+删除指定目录下的文件可使用 `unlink()` 函数:
+
+```
+int unlink(const char *pathname);
+```
+
+|**参数**|**描述** |
+|----------|------------------------|
+| pathname | 指定删除文件的绝对路径 |
+|**返回**|**——** |
+| 0 | 删除文件成功 |
+| -1 | 删除文件失败 |
+
+### 同步文件数据到存储设备
+
+同步内存中所有已修改的文件数据到储存设备可使用 `fsync()` 函数:
+
+```c
+int fsync(int fildes);
+```
+
+|**参数**|**描述** |
+|----------|--------------|
+| fildes | 文件描述符 |
+|**返回**|**——** |
+| 0 | 同步文件成功 |
+| -1 | 同步文件失败 |
+
+### 查询文件系统相关信息
+
+查询文件系统相关信息可使用 `statfs()` 函数:
+
+```c
+int statfs(const char *path, struct statfs *buf);
+```
+
+|**参数**|**描述** |
+|----------|----------------------------------|
+| path | 文件系统的挂载路径 |
+| buf | 用于储存文件系统信息的结构体指针 |
+|**返回**|**——** |
+| 0 | 查询文件系统信息成功 |
+| -1 | 查询文件系统信息失败 |
+
+### 监视 I/O 设备状态
+
+监视 I/O 设备是否有事件发生可使用 `select()` 函数:
+
+```c
+int select( int nfds,
+ fd_set *readfds,
+ fd_set *writefds,
+ fd_set *exceptfds,
+ struct timeval *timeout);
+```
+
+|**参数** |**描述** |
+|-----------|---------------------------------------------------------|
+| nfds | 集合中所有文件描述符的范围,即所有文件描述符的最大值加 1 |
+| readfds | 需要监视读变化的文件描述符集合 |
+| writefds | 需要监视写变化的文件描述符集合 |
+| exceptfds | 需要监视出现异常的文件描述符集合 |
+| timeout | select 的超时时间 |
+|**返回** |**——** |
+| 正值 | 监视的文件集合出现可读写事件或出错 |
+| 0 | 等待超时,没有可读写或错误的文件 |
+| 负值 | 出错 |
+
+使用 `select()` 接口可以阻塞地同时探测一组支持非阻塞的 I/O 设备是否有事件发生(如可读,可写,有高优先级的错误输出,出现错误等等),直至某一个设备触发了事件或者超过了指定的等待时间。
+
+## 目录管理
+
+本节介绍目录管理经常使用的函数,对目录的操作一般都基于目录地址,如下图所示:
+
+
+
+### 创建和删除目录
+
+创建目录可使用 `mkdir()` 函数:
+
+```c
+int mkdir(const char *path, mode_t mode);
+```
+
+|**参数**|**描述** |
+|----------|----------------|
+| path | 目录的绝对地址 |
+| mode | 创建模式 |
+|**返回**|**——** |
+| 0 | 创建目录成功 |
+| -1 | 创建目录失败 |
+
+该函数用来创建一个目录即文件夹,参数 path 为目录的绝对路径,参数 mode 在当前版本未启用,所以填入默认参数 0x777 即可。
+
+删除目录可使用 `rmdir()` 函数:
+
+```c
+int rmdir(const char *pathname);
+```
+
+|**参数**|**描述** |
+|----------|------------------------|
+| pathname | 需要删除目录的绝对路径 |
+|**返回**|**——** |
+| 0 | 目录删除成功 |
+| -1 | 目录删除错误 |
+
+### 打开和关闭目录
+
+打开目录可使用 `opendir()` 函数:
+
+```c
+DIR* opendir(const char* name);
+```
+
+|**参数**|**描述** |
+|----------|-----------------------------------------|
+| name | 目录的绝对地址 |
+|**返回**|**——** |
+| DIR | 打开目录成功,返回指向目录流的指针 |
+| NULL | 打开失败 |
+
+关闭目录可使用 `closedir()` 函数:
+
+```c
+int closedir(DIR* d);
+```
+
+|**参数**|**描述** |
+|----------|--------------|
+| d | 目录流指针 |
+|**返回**|**——** |
+| 0 | 目录关闭成功 |
+| -1 | 目录关闭错误 |
+
+该函数用来关闭一个目录,必须和 `opendir()` 函数配合使用。
+
+### 读取目录
+
+读取目录可使用 `readdir()` 函数:
+
+```c
+struct dirent* readdir(DIR *d);
+```
+
+|**参数**|**描述** |
+|----------|---------------------------------------|
+| d | 目录流指针 |
+|**返回**|**——** |
+| dirent | 读取成功返回指向目录条目的结构体指针 |
+| NULL | 已读到目录尾 |
+
+该函数用来读取目录,参数 d 为目录流指针。此外,每读取一次目录,目录流的指针位置将自动往后递推 1 个位置。
+
+### 取得目录流的读取位置
+
+获取目录流的读取位置可使用 `telldir()` 函数:
+
+```
+long telldir(DIR *d);
+```
+
+|**参数**|**描述** |
+|----------|------------------|
+| d | 目录流指针 |
+|**返回**|**——** |
+| long | 读取位置的偏移量 |
+
+该函数的返回值记录着一个目录流的当前位置,此返回值代表距离目录文件开头的 [偏移量](https://baike.baidu.com/item/%E5%81%8F%E7%A7%BB%E9%87%8F)。你可以在随后的 `seekdir()`[函数调用](https://baike.baidu.com/item/%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8) 中利用这个值来重置目录到当前位置。也就是说 `telldir()` 函数可以和 `seekdir()` 函数配合使用,重新设置目录流的读取位置到指定的偏移量。
+
+### 设置下次读取目录的位置
+
+设置下次读取目录的位置可使用 `seekdir()` 函数:
+
+```
+void seekdir(DIR *d, off_t offset);
+```
+
+|**参数**|**描述** |
+|----------|----------------------------|
+| d | 目录流指针 |
+| offset | 偏移值,距离本次目录的位移 |
+
+该用来设置参数 d 目录流的读取位置,在调用 readdir() 时便从此新位置开始读取。
+
+### 重设读取目录的位置为开头位置
+
+重设目录流的读取位置为开头可使用 `rewinddir()` 函数:
+
+```
+void rewinddir(DIR *d);
+```
+
+|**参数**|**描述** |
+|----------|------------|
+| d | 目录流指针 |
+
+该函数可以用来设置 d 目录流目前的读取位置为目录流的初始位置。
+
+## DFS 配置选项
+
+文件系统在 menuconfig 中具体配置路径如下:
+
+```c
+RT-Thread Components --->
+ Device virtual file system --->
+```
+
+配置菜单描述及对应的宏定义如下表所示:
+
+|**配置选项** |**对应宏定义**|**描述** |
+|-------------------------------|-------------------------------|----------------------|
+|[*] Using device virtual file system |RT_USING_DFS |开启DFS虚拟文件系统 |
+|[*] Using working directory |DFS_USING_WORKDIR |开启相对路径 |
+|(2) The maximal number of mounted file system |DFS_FILESYSTEMS_MAX |最大挂载文件系统的数量 |
+|(2) The maximal number of file system type |DFS_FILESYSTEM_TYPES_MAX |最大支持文件系统的数量 |
+|(4) The maximal number of opened files | DFS_FD_MAX|打开文件的最大数量 |
+|[ ] Using mount table for file system|RT_USING_DFS_MNTTABLE |开启自动挂载表 |
+|[*] Enable elm-chan fatfs |RT_USING_DFS_ELMFAT |开启 elm-FatFs 文件系统 |
+|[*] Using devfs for device objects |RT_USING_DFS_DEVFS | 开启 DevFS 设备文件系统|
+|[ ] Enable ReadOnly file system on flash |RT_USING_DFS_ROMFS |开启 RomFS 文件系统 |
+|[ ] Enable RAM file system |RT_USING_DFS_RAMFS |开启 RamFS 文件系统 |
+|[ ] Enable UFFS file system: Ultra-low-cost Flash File System |RT_USING_DFS_UFFS |开启 UFFS 文件系统 |
+|[ ] Enable JFFS2 file system |RT_USING_DFS_JFFS2 |开启 JFFS2 文件系统 |
+|[ ] Using NFS v3 client file system |RT_USING_DFS_NFS |开启 NFS 文件系统 |
+
+默认情况下,RT-Thread 操作系统为了获得较小的内存占用,并不会开启相对路径功能。当支持相对路径选项没有打开时,在使用文件、目录接口进行操作时应该使用绝对目录进行(因为此时系统中不存在当前工作的目录)。如果需要使用当前工作目录以及相对目录,可在文件系统的配置项中开启相对路径功能。
+
+选项 `[*] Using mount table for file system` 被选中之后,会使能相应的宏 `RT_USING_DFS_MNTTABLE`,开启自动挂载表功能。自动挂载表 `mount_table[]` 由用户在应用代码中提供,用户需在表中指定设备名称、挂载路径、文件系统类型、读写标志及私有数据等,之后系统便会遍历该挂载表执行挂载,需要注意的是挂载表必须以 {0} 结尾,用于判断表格结束。
+
+自动挂载表 `mount_table[]` 的示例如下所示,其中 `mount_table[0]` 的 5 个成员即函数 `dfs_mount()` 的 5 个参数,意思是将 elm 文件系统挂载到 flash0 设备的 / 路径下,rwflag 为 0 ,data 为 0 ; `mount_table[1]` 为 {0} 作为结尾,用于判断表格结束。
+
+```c
+const struct dfs_mount_tbl mount_table[] =
+{
+ {"flash0", "/", "elm", 0, 0},
+ {0}
+};
+```
+
+### elm-FatFs 文件系统配置选项
+
+在 menuconfig 中开启 elm-FatFs 文件系统后可对 elm-FatFs 做进一步配置,配置菜单描述及对应的宏定义如下表所示:
+
+|**配置选项** |**对应宏定义**|**描述** |
+|---------------------------------|-----------------------------------|-------------------|
+|(437) OEM code page |RT_DFS_ELM_CODE_PAGE |编码方式 |
+|[*] Using RT_DFS_ELM_WORD_ACCESS |RT_DFS_ELM_WORD_ACCESS | |
+|Support long file name (0: LFN disable) ---> |RT_DFS_ELM_USE_LFN |开启长文件名子菜单 |
+|(255) Maximal size of file name length |RT_DFS_ELM_MAX_LFN |文件名最大长度 |
+|(2) Number of volumes (logical drives) to be used. |RT_DFS_ELM_DRIVES |挂载 FatFs 的设备数量 |
+(4096) Maximum sector size to be handled. |RT_DFS_ELM_MAX_SECTOR_SIZE |文件系统扇区大小 |
+|[ ] Enable sector erase feature |RT_DFS_ELM_USE_ERASE | |
+|[*] Enable the reentrancy (thread safe) of the FatFs module |RT_DFS_ELM_REENTRANT |开启可重入性 |
+
+#### 长文件名
+
+默认情况下,FatFs 的文件命名有如下缺点:
+
+* 文件名(不含后缀)最长不超过8个字符,后缀最长不超过3个字符,文件名和后缀超过限制后将会被截断。
+
+* 文件名不支持大小写(显示为大写)
+
+如果需要支持长文件名,则需要打开支持长文件名选项。长文件名子菜单描述如下所示:
+
+|**配置选项** |**对应宏定义**|**描述** |
+|----------------------------------|-------------------------|---------------------|
+|( ) 0: LFN disable |RT_DFS_ELM_USE_LFN_0 |关闭长文件名 |
+|( ) 1: LFN with static LFN working buffer|RT_DFS_ELM_USE_LFN_1 |采用静态缓冲区支持长文件名,多线程操作文件名时将会带来重入问题 |
+|( ) 2: LFN with dynamic LFN working buffer on the stack |RT_DFS_ELM_USE_LFN_2 |采用栈内临时缓冲区支持长文件名。对栈空间需求较大 |
+|(X) 3: LFN with dynamic LFN working buffer on the heap |RT_DFS_ELM_USE_LFN_3 |使用heap(malloc申请)缓冲区存放长文件名,最安全(默认方式) |
+
+#### 编码方式
+
+当打开长文件名支持时,可以设置文件名的编码方式,RT-Thread/FatFs 默认使用 437 编码(美国英语)。如果需要存储中文文件名,可以使用 936 编码(GBK编码)。936 编码需要一个大约 180KB 的字库。如果仅使用英文字符作为文件,建议使用 437 编码(美国
+英语),这样就可以节省这 180KB 的 Flash 空间。
+
+FatFs所支持的文件编码如下所示:
+
+```c
+/* This option specifies the OEM code page to be used on the target system.
+/ Incorrect setting of the code page can cause a file open failure.
+/
+/ 1 - ASCII (No extended character. Non-LFN cfg. only)
+/ 437 - U.S.
+/ 720 - Arabic
+/ 737 - Greek
+/ 771 - KBL
+/ 775 - Baltic
+/ 850 - Latin 1
+/ 852 - Latin 2
+/ 855 - Cyrillic
+/ 857 - Turkish
+/ 860 - Portuguese
+/ 861 - Icelandic
+/ 862 - Hebrew
+/ 863 - Canadian French
+/ 864 - Arabic
+/ 865 - Nordic
+/ 866 - Russian
+/ 869 - Greek 2
+/ 932 - Japanese (DBCS)
+/ 936 - Simplified Chinese (DBCS)
+/ 949 - Korean (DBCS)
+/ 950 - Traditional Chinese (DBCS)
+*/
+```
+
+#### 文件系统扇区大小
+
+指定 FatFs 的内部扇区大小,需要大于等于实际硬件驱动的扇区大小。例如,某 spi flash 芯片扇区为 4096 字节,则上述宏需要修改为 4096,否则 FatFs 从驱动读入数据时就会发生数组越界而导致系统崩溃(新版本在系统执行时给出警告信息)。
+
+一般 Flash 设备可以设置为 4096,常见的 TF 卡和 SD 卡的扇区大小设置为 512。
+
+#### 可重入性
+
+FatFs 充分考虑了多线程安全读写安全的情况,当在多线程中读写 FafFs 时,为了避免重入带来的问题,需要打开上述宏。如果系统仅有一个线程操作文件系统,不会出现重入问题,则可以关闭此功能节省资源。
+
+#### 更多配置
+
+FatFs 本身支持非常多的配置选项,配置非常灵活。下面文件为 FatFs 的配置文件,可以修改这个文件来定制 FatFs。
+
+```c
+components/dfs/filesystems/elmfat/ffconf.h
+```
+
+## DFS 应用示例
+
+### FinSH 命令
+
+文件系统挂载成功后就可以进行文件和目录的操作了,文件系统操作常用的 FinSH 命令如下表所示:
+
+|**FinSH 命令** |**描述** |
+|--------|----------------------------------|
+| ls | 显示文件和目录的信息 |
+| cd | 进入指定目录 |
+| cp | 复制文件 |
+| rm | 删除文件或目录 |
+| mv | 将文件移动位置或改名 |
+| echo | 将指定内容写入指定文件,当文件存在时,就写入该文件,当文件不存在时就新创建一个文件并写入 |
+| cat | 展示文件的内容 |
+| pwd | 打印出当前目录地址 |
+| mkdir | 创建文件夹 |
+| mkfs | 格式化文件系统 |
+
+使用 `ls` 命令查看当前目录信息,运行结果如下所示:
+
+```c
+msh />ls # 使用 ls 命令查看文件系统目录信息
+Directory /: # 可以看到已经存在根目录 /
+```
+
+使用 `mkdir` 命令来创建文件夹,运行结果如下所示:
+
+```c
+msh />mkdir rt-thread # 创建 rt-thread 文件夹
+msh />ls # 查看目录信息如下
+Directory /:
+rt-thread
+```
+
+使用 `echo` 命令将输入的字符串输出到指定输出位置,运行结果如下所示:
+
+```c
+msh />echo "hello rt-thread!!!" # 将字符串输出到标准输出
+hello rt-thread!!!
+msh />echo "hello rt-thread!!!" hello.txt # 将字符串出输出到 hello.txt 文件
+msh />ls
+Directory /:
+rt-thread
+hello.txt 18
+msh />
+```
+
+使用 `cat` 命令查看文件内容,运行结果如下所示:
+
+```c
+msh />cat hello.txt # 查看 hello.txt 文件的内容并输出
+hello rt-thread!!!
+```
+
+使用 `rm` 命令删除文件夹或文件,运行结果如下所示:
+
+```c
+msh />ls # 查看当前目录信息
+Directory /:
+rt-thread
+hello.txt 18
+msh />rm rt-thread # 删除 rt-thread 文件夹
+msh />ls
+Directory /:
+hello.txt 18
+msh />rm hello.txt # 删除 hello.txt 文件
+msh />ls
+Directory /:
+msh />
+```
+
+### 读写文件示例
+
+文件系统正常工作后,就可以运行应用示例,在该示例代码中,首先会使用 `open()` 函数创建一个文件 text.txt,并使用 write() 函数在文件中写入字符串 `“RT-Thread Programmer!\n”`,然后关闭文件。再次使用 `open()` 函数打开 text.txt 文件,读出其中的内容并打印出来,最后关闭该文件。
+
+示例代码如下所示:
+
+```c
+#include
+#include /* 当需要使用文件操作时,需要包含这个头文件 */
+
+static void readwrite_sample(void)
+{
+ int fd, size;
+ char s[] = "RT-Thread Programmer!", buffer[80];
+
+ rt_kprintf("Write string %s to test.txt.\n", s);
+
+ /* 以创建和读写模式打开 /text.txt 文件,如果该文件不存在则创建该文件 */
+ fd = open("/text.txt", O_WRONLY | O_CREAT);
+ if (fd>= 0)
+ {
+ write(fd, s, sizeof(s));
+ close(fd);
+ rt_kprintf("Write done.\n");
+ }
+
+ /* 以只读模式打开 /text.txt 文件 */
+ fd = open("/text.txt", O_RDONLY);
+ if (fd>= 0)
+ {
+ size = read(fd, buffer, sizeof(buffer));
+ close(fd);
+ rt_kprintf("Read from file test.txt : %s \n", buffer);
+ if (size < 0)
+ return ;
+ }
+ }
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(readwrite_sample, readwrite sample);
+
+```
+
+### 更改文件名称示例
+
+本小节的示例代码展示如何修改文件名称,程序会创建一个操作文件的函数 `rename_sample()` 并导出到 msh 命令列表。该函数会调用 `rename()` 函数, 将名为 text.txt 的文件改名为 text1.txt。示例代码如下所示:
+
+```c
+#include
+#include /* 当需要使用文件操作时,需要包含这个头文件 */
+
+static void rename_sample(void)
+{
+ rt_kprintf("%s => %s", "/text.txt", "/text1.txt");
+
+ if (rename("/text.txt", "/text1.txt") < 0)
+ rt_kprintf("[error!]\n");
+ else
+ rt_kprintf("[ok!]\n");
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(rename_sample, rename sample);
+```
+
+在 FinSH 控制台运行该示例,运行结果如下:
+
+```c
+msh />echo "hello" text.txt
+msh />ls
+Directory /:
+text.txt 5
+msh />rename_sample
+/text.txt => /text1.txt [ok!]
+msh />ls
+Directory /:
+text1.txt 5
+```
+
+在示例展示过程中,我们先使用 echo 命令创建一个名为 text.txt 文件,然后运行示例代码将文件 text.txt 的文件名修改为 text1.txt。
+
+### 获取文件状态示例
+
+本小节的示例代码展示如何获取文件状态,程序会创建一个操作文件的函数 `stat_sample()` 并导出到 msh 命令列表。该函数会调用 `stat()` 函数获取 text.txt 文件的文件大小信息。示例代码如下所示:
+
+```c
+#include
+#include /* 当需要使用文件操作时,需要包含这个头文件 */
+
+static void stat_sample(void)
+{
+ int ret;
+ struct stat buf;
+ ret = stat("/text.txt", &buf);
+ if(ret == 0)
+ rt_kprintf("text.txt file size = %d\n", buf.st_size);
+ else
+ rt_kprintf("text.txt file not fonud\n");
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(stat_sample, show text.txt stat sample);
+```
+
+在 FinSH 控制台运行该示例,运行结果如下:
+
+```c
+msh />echo "hello" text.txt
+msh />stat_sample
+text.txt file size = 5
+```
+
+在示例运行过程中,首先会使用 `echo` 命令创建文件 text.txt,然后运行示例代码,将文件 text.txt 的文件大小信息打印出来。
+
+### 创建目录示例
+
+本小节的示例代码展示如何创建目录,程序会创建一个操作文件的函数 `mkdir_sample()` 并导出到 msh 命令列表,该函数会调用 `mkdir()` 函数创建一个名为 dir_test 的文件夹。示例代码如下所示:
+
+```c
+#include
+#include /* 当需要使用文件操作时,需要包含这个头文件 */
+
+static void mkdir_sample(void)
+{
+ int ret;
+
+ /* 创建目录 */
+ ret = mkdir("/dir_test", 0x777);
+ if (ret < 0)
+ {
+ /* 创建目录失败 */
+ rt_kprintf("dir error!\n");
+ }
+ else
+ {
+ /* 创建目录成功 */
+ rt_kprintf("mkdir ok!\n");
+ }
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(mkdir_sample, mkdir sample);
+```
+
+在 FinSH 控制台运行该示例,运行结果如下:
+
+```c
+msh />mkdir_sample
+mkdir ok!
+msh />ls
+Directory /:
+dir_test # 表示该目录的类型是文件夹
+```
+
+本例程演示了在根目录下创建名为 dir_test 的文件夹。
+
+### 读取目录示例
+
+本小节的示例代码展示如何读取目录,程序会创建一个操作文件的函数 `readdir_sample()` 并导出到 msh 命令列表,该函数会调用 `readdir()` 函数获取 dir_test 文件夹的内容信息并打印出来。示例代码如下所示:
+
+```c
+#include
+#include /* 当需要使用文件操作时,需要包含这个头文件 */
+
+static void readdir_sample(void)
+{
+ DIR *dirp;
+ struct dirent *d;
+
+ /* 打开 / dir_test 目录 */
+ dirp = opendir("/dir_test");
+ if (dirp == RT_NULL)
+ {
+ rt_kprintf("open directory error!\n");
+ }
+ else
+ {
+ /* 读取目录 */
+ while ((d = readdir(dirp)) != RT_NULL)
+ {
+ rt_kprintf("found %s\n", d->d_name);
+ }
+
+ /* 关闭目录 */
+ closedir(dirp);
+ }
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(readdir_sample, readdir sample);
+```
+
+在 FinSH 控制台运行该示例,运行结果如下:
+
+```c
+msh />ls
+Directory /:
+dir_test
+msh />cd dir_test
+msh /dir_test>echo "hello" hello.txt # 创建一个 hello.txt 文件
+msh /dir_test>cd .. # 切换到上级文件夹
+msh />readdir_sample
+found hello.txt
+```
+
+本示例中,首先进入到 dir_test 文件夹下创建 hello.txt 文件,然后退出 dir_test 文件夹。此时运行示例程序将 dir_test 文件夹中的内容打印出来。
+
+### 设置读取目录位置示例
+
+本小节的示例代码展示如何设置下次读取目录的位置,程序会创建一个操作文件的函数 `telldir_sample()` 并导出到 msh 命令列表。该函数会首先打开根目录,然后读取根目录下所有目录信息,并将这些目录信息打印出来。同时使用 `telldir()` 函数记录第三个目录项的位置信息。在第二次读取根目录下的目录信息前,使用 `seekdir()` 函数设置读取位置为之前记录的第三个目录项的地址,此时再次读取根目录下的信息,并将目录信息打印出来。示例代码如下所示:
+
+```c
+#include
+#include /* 当需要使用文件操作时,需要包含这个头文件 */
+
+/* 假设文件操作是在一个线程中完成 */
+static void telldir_sample(void)
+{
+ DIR *dirp;
+ int save3 = 0;
+ int cur;
+ int i = 0;
+ struct dirent *dp;
+
+ /* 打开根目录 */
+ rt_kprintf("the directory is:\n");
+ dirp = opendir("/");
+
+ for (dp = readdir(dirp); dp != RT_NULL; dp = readdir(dirp))
+ {
+ /* 保存第三个目录项的目录指针 */
+ i++;
+ if (i == 3)
+ save3 = telldir(dirp);
+
+ rt_kprintf("%s\n", dp->d_name);
+ }
+
+ /* 回到刚才保存的第三个目录项的目录指针 */
+ seekdir(dirp, save3);
+
+ /* 检查当前目录指针是否等于保存过的第三个目录项的指针. */
+ cur = telldir(dirp);
+ if (cur != save3)
+ {
+ rt_kprintf("seekdir (d, %ld); telldir (d) == %ld\n", save3, cur);
+ }
+
+ /* 从第三个目录项开始打印 */
+ rt_kprintf("the result of tell_seek_dir is:\n");
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
+ {
+ rt_kprintf("%s\n", dp->d_name);
+ }
+
+ /* 关闭目录 */
+ closedir(dirp);
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(telldir_sample, telldir sample);
+```
+
+本次演示示例中,需要手动在根目录下用 `mkdir` 命令依次创建从 hello_1 到 hello_5 这五个文件夹,确保根目录下有运行示例所需的文件夹目录。
+
+在 FinSH 控制台运行该示例,运行结果如下:
+
+```c
+msh />ls
+Directory /:
+hello_1
+hello_2
+hello_3
+hello_4
+hello_5
+msh />telldir_sample
+the directory is:
+hello_1
+hello_2
+hello_3
+hello_4
+hello_5
+the result of tell_seek_dir is:
+hello_3
+hello_4
+hello_5
+```
+
+运行示例程序后,可以看到第一次读取根目录信息时是从第一个文件夹开始读取,打印出了根目录下所有的目录信息。第二次打印目录信息时,由于使用了 `seekdir()` 函数设置读取的起始位置为第三个文件夹的位置,因此第二次读取根目录时,是从第三个文件夹开始读取直到最后一个文件夹,只打印出了从 hello_3 到 hello_5 的目录信息。
+
+## 常见问题
+
+### Q: 发现文件名或者文件夹名称显示不正常怎么办?
+
+ **A:** 检查是否开启了长文件名支持,DFS 功能配置小节。
+
+### Q: 文件系统初始化失败怎么办?
+
+ **A:** 检查文件系统配置项目中的允许挂载的文件系统类型和数量是否充足。
+
+### Q: 创建文件系统 mkfs 命令失败怎么办?
+
+ **A:** 检查存储设备是否存在,如果存在检查设备驱动是否可以通过功能测试,如果不能通过,则检查驱动错误。 检查 libc 功能是否开启。
+
+### Q: 文件系统挂载失败怎么办?
+
+ **A:**
+
+ * 检查指定的挂载路径是否存在。文件系统可以直接挂载到根目录(“/”),但是如果想要挂载到其他路径上,如 (“/sdcard”)。需要确保(“/sdcard”)路径是存在的,否则需要先在根目录创建 `sdcard` 文件夹才能挂载成功。
+
+ * 检查是否在存储设备上创建了文件系统,如果存储设备上没有文件系统,需要使用 `mkfs` 命令在存储器上创建文件系统。
+
+### Q: SFUD 探测不到 Flash 所使用的具体型号怎么办?
+
+ **A:**
+
+* 检查硬件引脚设置是否错误。
+
+* SPI 设备是否已经注册。
+
+* SPI 设备是否已经挂载到总线。
+
+* 检查在 `RT-Thread Components → Device Drivers -> Using SPI Bus/Device device drivers -> Using Serial Flash Universal Driver` 菜单下的 `Using auto probe flash JEDEC SFDP parameter` 和 `Using defined supported flash chip information table` 配置项是否选中,如果没有选中那么需要开启这两个选项。
+
+* 如果开启了上面的选项仍然无法识别存储设备,那么可以在 [SFUD](https://github.com/armink/SFUD) 项目中提出 issues。
+
+### Q: 存储设备的 benchmark 测试耗时过长是怎么回事?
+
+ **A:**
+
+* 可对比 `system tick` 为 1000 时的 [benchmark 测试数据](https://github.com/armink/SFUD/blob/master/docs/zh/benchmark.txt) 和本次测试所需的时长,如果耗时差距过大,则可以认为测试工作运行不正常。
+
+* 检查系统 tick 的设置,因为一些延时操作会根据 tick 时间来决定,所以需要根据系统情况来设置合适的 `system tick` 值。如果系统的 `system tick` 值不低于 1000,则需要使用逻辑分析仪检查波形确定通信速率正常。
+
+### Q: SPI Flash 实现 elmfat 文件系统,如何保留部分扇区不被文件系统使用?
+
+ **A:** 可以使用 RT-Thread 提供的 [partition](https://github.com/RT-Thread-packages/partition) 工具软件包为整个存储设备创建多个块设备,为创建的多个块设备分配不同的功能即可。
+
+### Q: 测试文件系统过程中程序卡住了怎么办?
+
+ **A:** 尝试使用调试器或者打印一些必要的调试信息,确定程序卡住的位置再提出问题。
+
+### Q: 如何一步步检查文件系统出现的问题?
+
+ **A:**
+
+* 可以采用从底层到上层的方法来逐步排查问题。
+
+* 首先检查存储设备是否注册成功,功能是否正常。
+
+* 检查存储设备中是否创建了文件系统。
+
+* 检查指定文件系统类型是否注册到 DFS 框架,经常要检查允许的文件系统类型和数量是否足够。
+
+* 检查 DFS 是否初始化成功,这一步的初始化操作是纯软件的,因此出错的可能性不高。需要注意的是如果开启了组件自动初始化,就无需再次手动初始化。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-hd.png b/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-hd.png
new file mode 100644
index 0000000000000000000000000000000000000000..c48840ff58b147534adcf961debb10b4bfea404c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-hd.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-mdk.png b/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-mdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f7bbb8941cd85558063f97e64cf16b47a2dc282
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-mdk.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-run.png b/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-run.png
new file mode 100644
index 0000000000000000000000000000000000000000..6939b0872b7c8fc7bc9717c8cadbcf005193b6fc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/finsh/figures/finsh-run.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/finsh/finsh.md b/rt-thread-version/rt-thread-standard/programming-manual/finsh/finsh.md
new file mode 100644
index 0000000000000000000000000000000000000000..b70b6d2b5abb04703296172e6829eab3cd47c85d
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/finsh/finsh.md
@@ -0,0 +1,552 @@
+# FinSH 控制台
+
+在计算机发展的早期,图形系统出现之前,没有鼠标,甚至没有键盘。那时候人们如何与计算机交互呢?最早期的计算机使用打孔的纸条向计算机输入命令,编写程序。后来随着计算机的不断发展,显示器、键盘成为计算机的标准配置,但此时的操作系统还不支持图形界面,计算机先驱们开发了一种软件,它接受用户输入的命令,解释之后,传递给操作系统,并将操作系统执行的结果返回给用户。这个程序像一层外壳包裹在操作系统的外面,所以它被称为 shell。
+
+嵌入式设备通常需要将开发板与 PC 机连接起来通讯,常见连接方式包括:串口、USB、以太网、Wi-Fi 等。一个灵活的 shell 也应该支持在多种连接方式上工作。有了 shell,就像在开发者和计算机之间架起了一座沟通的桥梁,开发者能很方便的获取系统的运行情况,并通过命令控制系统的运行。特别是在调试阶段,有了 shell,开发者除了能更快的定位到问题之外,也能利用 shell 调用测试函数,改变测试函数的参数,减少代码的烧录次数,缩短项目的开发时间。
+
+FinSH 是 RT-Thread 的命令行组件(shell),正是基于上面这些考虑而诞生的,FinSH 的发音为 [ˈfɪnʃ]。读完本章,我们会对 FinSH 的工作方式以及如何导出自己的命令到 FinSH 有更加深入的了解。
+
+## FinSH 简介
+
+FinSH 是 RT-Thread 的命令行组件,提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。它可以使用串口 / 以太网 / USB 等与 PC 机进行通信,硬件拓扑结构如下图所示:
+
+
+
+用户在控制终端输入命令,控制终端通过串口、USB、网络等方式将命令传给设备里的 FinSH,FinSH 会读取设备输入命令,解析并自动扫描内部函数表,寻找对应函数名,执行函数后输出回应,回应通过原路返回,将结果显示在控制终端上。
+
+当使用串口连接设备与控制终端时,FinSH 命令的执行流程,如下图所示:
+
+
+
+FinSH 支持权限验证功能,系统在启动后会进行权限验证,只有权限验证通过,才会开启 FinSH 功能,提升系统输入的安全性。
+
+FinSH 支持自动补全、查看历史命令等功能,通过键盘上的按键可以很方便的使用这些功能,FinSH 支持的按键如下表所示:
+
+|**按键**|**功能描述** |
+|----------|--------------|
+| Tab 键 | 当没有输入任何字符时按下 Tab 键将会打印当前系统支持的所有命令。若已经输入部分字符时按下 Tab 键,将会查找匹配的命令,也会按照文件系统的当前目录下的文件名进行补全,并可以继续输入,多次补全 |
+| ↑↓键 | 上下翻阅最近输入的历史命令 |
+| 退格键 | 删除符 |
+| ←→键 | 向左或向右移动标 |
+
+FinSH 支持两种输入模式,分别是传统命令行模式和 C 语言解释器模式。
+
+### 传统命令行模式
+
+此模式又称为 msh(module shell),msh 模式下,FinSH 与传统 shell(dos/bash)执行方式一致,例如,可以通过 `cd /` 命令将目录切换至根目录。
+
+msh 通过解析,将输入字符分解成以空格区分开的命令和参数。其命令执行格式如下所示:
+
+`command [arg1] [arg2] [...]`
+
+其中 command 既可以是 RT-Thread 内置的命令,也可以是可执行的文件。
+
+### C 语言解释器模式
+
+此模式又称为 C-Style 模式。C 语言解释器模式下,FinSH 能够解析执行大部分 C 语言的表达式,并使用类似 C 语言的函数调用方式访问系统中的函数及全局变量,此外它也能够通过命令行方式创建变量。在该模式下,输入的命令必须类似 C 语言中的函数调用方式,即必须携带 () 符号,例如,要输出系统当前所有线程及其状态,在 FinSH 中输入 `list_thread()` 即可打印出需要的信息。FinSH 命令的输出为此函数的返回值。对于一些不存在返回值的函数(void 返回值),这个打印输出没有意义。
+
+最初 FinSH 仅支持 C-Style 模式,后来随着 RT-Thread 的不断发展,C-Style 模式在运行脚本或者程序时不太方便,而使用传统的 shell 方式则比较方便。另外,C-Style 模式下,FinSH 占用体积比较大。出于这些考虑,在 RT-Thread 中增加了 msh 模式,msh 模式体积小,使用方便,推荐大家使用 msh 模式。
+
+如果在 RT-Thread 中同时使能了这两种模式,那它们可以动态切换,在 msh 模式下输入 exit 后回车,即可切换到 C-Style 模式。在 C-Style 模式输入 msh() 后回车,即可进入 msh 模式。两种模式的命令不通用,msh 命令无法在 C-Style 模式下使用,反之同理。
+
+## FinSH 内置命令
+
+在 RT-Thread 中默认内置了一些 FinSH 命令,在 FinSH 中输入 help 后回车或者直接按下 Tab 键,就可以打印当前系统支持的所有命令。C-Style 和 msh 模式下的内置命令基本一致,这里就以 msh 为例。
+
+msh 模式下,按下 Tab 键后可以列出当前支持的所有命令。默认命令的数量不是固定的,RT-Thread 的各个组件会向 FinSH 输出一些命令。例如,当打开 DFS 组件时,就会把 ls,cp,cd 等命令加到 FinSH 中,方便开发者调试。
+
+以下为按下 Tab 键后打印出来的当前支持的所有显示 RT-Thread 内核状态信息的命令,左边是命令名称,右边是关于命令的描述:
+
+```c
+RT-Thread shell commands:
+version - show RT-Thread version information
+list_thread - list thread
+list_sem - list semaphore in system
+list_event - list event in system
+list_mutex - list mutex in system
+list_mailbox - list mail box in system
+list_msgqueue - list message queue in system
+list_timer - list timer in system
+list_device - list device in system
+exit - return to RT-Thread shell mode.
+help - RT-Thread shell help.
+ps - List threads in the system.
+time - Execute command with time.
+free - Show the memory usage in the system.
+```
+
+这里列出输入常用命令后返回的字段信息,方便开发者理解返回的信息内容。
+
+### 显示线程状态
+
+使用 ps 或者 list_thread 命令来列出系统中的所有线程信息,包括线程优先级、状态、栈的最大使用量等。
+
+```c
+msh />list_thread
+thread pri status sp stack size max used left tick error
+-------- --- ------- ---------- ---------- ------ ---------- ---
+tshell 20 ready 0x00000118 0x00001000 29% 0x00000009 000
+tidle 31 ready 0x0000005c 0x00000200 28% 0x00000005 000
+timer 4 suspend 0x00000078 0x00000400 11% 0x00000009 000
+```
+list_thread 返回字段的描述:
+
+|**字段** |**描述** |
+|------------|----------------------------|
+| thread | 线程的名称 |
+| pri | 线程的优先级 |
+| status | 线程当前的状态 |
+| sp | 线程当前的栈位置 |
+| stack size | 线程的栈大小 |
+| max used | 线程历史中使用的最大栈位置 |
+| left tick | 线程剩余的运行节拍数 |
+| error | 线程的错误码 |
+
+### 显示信号量状态
+
+使用 list_sem 命令来显示系统中所有信号量信息,包括信号量的名称、信号量的值和等待这个信号量的线程数目。
+
+```c
+msh />list_sem
+semaphore v suspend thread
+-------- --- --------------
+shrx 000 0
+e0 000 0
+```
+
+list_sem 返回字段的描述:
+
+|**字段** |**描述** |
+|----------------|--------------------------|
+| semaphore | 信号量的名称 |
+| v | 信号量当前的值 |
+| suspend thread | 等待这个信号量的线程数目 |
+
+### 显示事件状态
+
+使用 list_event 命令来显示系统中所有的事件信息,包括事件名称、事件的值和等待这个事件的线程数目。
+
+```c
+msh />list_event
+event set suspend thread
+----- ---------- --------------
+```
+
+list_event 返回字段的描述:
+
+|**字段** |**描述** |
+|----------------|----------------------------------|
+| event | 事件集的名称 |
+| set | 事件集中当前发生的事件 |
+| suspend thread | 在这个事件集中等待事件的线程数目 |
+
+### 显示互斥量状态
+
+使用 list_mutex 命令来显示系统中所有的互斥量信息,包括互斥量名称、互斥量的所有者和所有者在互斥量上持有的嵌套次数等。
+
+```c
+msh />list_mutex
+mutex owner hold suspend thread
+-------- -------- ---- --------------
+fat0 (NULL) 0000 0
+sal_lock (NULL) 0000 0
+```
+
+list_mutex 返回字段的描述:
+
+|**字段** |**描述** |
+|----------------|------------------------------------|
+| mutxe | 互斥量的名称 |
+| owner | 当前持有互斥量的线程 |
+| hold | 持有者在这个互斥量上嵌套持有的次数 |
+| suspend thread | 等待这个互斥量的线程数目 |
+
+### 显示邮箱状态
+
+使用 list_mailbox 命令显示系统中所有的邮箱信息,包括邮箱名称、邮箱中邮件的数目和邮箱能容纳邮件的最大数目等。
+
+```c
+msh />list_mailbox
+mailbox entry size suspend thread
+-------- ---- ---- --------------
+etxmb 0000 0008 1:etx
+erxmb 0000 0008 1:erx
+```
+
+list_mailbox 返回字段的描述:
+
+|**字段** |**描述** |
+|----------------|----------------------------|
+| mailbox | 邮箱的名称 |
+| entry | 邮箱中包含的邮件数目 |
+| size | 邮箱能够容纳的最大邮件数目 |
+| suspend thread | 等待这个邮箱的线程数目 |
+
+### 显示消息队列状态
+
+使用 list_msgqueue 命令来显示系统中所有的消息队列信息,包括消息队列的名称、包含的消息数目和等待这个消息队列的线程数目。
+
+```c
+msh />list_msgqueue
+msgqueue entry suspend thread
+-------- ---- --------------
+```
+
+list_msgqueue 返回字段的描述:
+
+|**字段** |**描述** |
+|----------------|----------------------------|
+| msgqueue | 消息队列的名称 |
+| entry | 消息队列当前包含的消息数目 |
+| suspend thread | 等待这个消息队列的线程数目 |
+
+### 显示内存池状态
+
+使用 list_mempool 命令来显示系统中所有的内存池信息,包括内存池的名称、内存池的大小和最大使用的内存大小等。
+
+```c
+msh />list_mempool
+mempool block total free suspend thread
+------- ---- ---- ---- --------------
+signal 0012 0032 0032 0
+```
+
+list_mempool 返回字段的描述:
+
+|**字段** |**描述** |
+|----------------|--------------------|
+| mempool | 内存池名称 |
+| block | 内存块大小 |
+| total | 总内存块 |
+| free | 空闲内存块 |
+| suspend thread | 等待这个内存池的线程数目 |
+
+### 显示定时器状态
+
+使用 list_timer 命令来显示系统中所有的定时器信息,包括定时器的名称、是否是周期性定时器和定时器超时的节拍数等。
+
+```c
+msh />list_timer
+timer periodic timeout flag
+-------- ---------- ---------- -----------
+tshell 0x00000000 0x00000000 deactivated
+tidle 0x00000000 0x00000000 deactivated
+timer 0x00000000 0x00000000 deactivated
+```
+
+list_timer 返回字段的描述:
+
+|**字段**|**描述** |
+|----------|--------------------------------|
+| timer | 定时器的名称 |
+| periodic | 定时器是否是周期性的 |
+| timeout | 定时器超时时的节拍数 |
+| flag | 定时器的状态,activated 表示活动的,deactivated 表示不活动的 |
+
+### 显示设备状态
+
+使用 list_device 命令来显示系统中所有的设备信息,包括设备名称、设备类型和设备被打开次数。
+
+```c
+msh />list_device
+device type ref count
+------ ----------------- ----------
+e0 Network Interface 0
+uart0 Character Device 2
+```
+
+list_device 返回字段的描述:
+
+|**字段** |**描述** |
+|-----------|----------------|
+| device | 设备的名称 |
+| type | 设备的类型 |
+| ref count | 设备被打开次数 |
+
+### 显示动态内存状态
+
+使用 free 命令来显示系统中所有的内存信息。
+
+```c
+msh />free
+total memory: 7669836
+used memory : 15240
+maximum allocated memory: 18520
+```
+
+free 返回字段的描述:
+
+|**字段** |**描述** |
+|--------------------------|------------------|
+| total memory | 内存总大小 |
+| used memory | 已使用的内存大小 |
+| maximum allocated memory | 最大分配内存 |
+
+## 自定义 FinSH 命令
+
+除了 FinSH 自带的命令,FinSH 还也提供了多个宏接口来导出自定义命令,导出的命令可以直接在 FinSH 中执行。
+
+### 自定义 msh 命令
+
+自定义的 msh 命令,可以在 msh 模式下被运行,将一个命令导出到 msh 模式可以使用如下宏接口:
+
+`MSH_CMD_EXPORT(name, desc);`
+
+|**参数**|**描述** |
+|----------|----------------|
+| name | 要导出的命令 |
+| desc | 导出命令的描述 |
+
+这个命令可以导出有参数的命令,也可以导出无参数的命令。导出无参数命令时,函数的入参为 void,示例如下:
+
+```c
+void hello(void)
+{
+ rt_kprintf("hello RT-Thread!\n");
+}
+
+MSH_CMD_EXPORT(hello , say hello to RT-Thread);
+```
+
+导出有参数的命令时,函数的入参为 `int argc` 和 `char**argv`。argc 表示参数的个数,argv 表示命令行参数字符串指针数组指针。导出有参数命令示例如下:
+
+```c
+static void atcmd(int argc, char**argv)
+{
+ ……
+}
+
+MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd );
+```
+
+### 自定义 C-Style 命令和变量
+
+将自定义命令导出到 C-Style 模式可以使用如下接口:
+
+`FINSH_FUNCTION_EXPORT(name, desc);`
+
+|**参数**|**描述** |
+|----------|----------------|
+| name | 要导出的命令 |
+| desc | 导出命令的描述 |
+
+以下示例定义了一个 hello 函数,并将它导出成 C-Style 模式下的命令:
+
+```c
+void hello(void)
+{
+ rt_kprintf("hello RT-Thread!\n");
+}
+
+FINSH_FUNCTION_EXPORT(hello , say hello to RT-Thread);
+```
+
+按照类似的方式,也可以导出一个变量,可以通过如下接口:
+
+`FINSH_VAR_EXPORT(name, type, desc);`
+
+|**参数**|**描述** |
+|----------|----------------|
+| name | 要导出的变量 |
+| type | 变量的类型 |
+| desc | 导出变量的描述 |
+
+以下示例定义了一个 dummy 变量,并将它导出成 C-Style 模式下的变量命令:
+
+```c
+static int dummy = 0;
+FINSH_VAR_EXPORT(dummy, finsh_type_int, dummy variable for finsh)
+```
+### 自定义命令重命名
+
+FinSH 的函数名字长度有一定限制,它由 finsh.h 中的宏定义 FINSH_NAME_MAX 控制,默认是 16 字节,这意味着 FinSH 命令长度不会超过 16 字节。这里有个潜在的问题:当一个函数名长度超过 FINSH_NAME_MAX 时,使用 FINSH_FUNCTION_EXPORT 导出这个函数到命令表中后,在 FinSH 符号表中看到完整的函数名,但是完整输入执行会出现 null node 错误。这是因为虽然显示了完整的函数名,但是实际上 FinSH 中却保存了前 16 字节作为命令,过多的输入会导致无法正确找到命令,这时就可以使用 FINSH_FUNCTION_EXPORT_ALIAS 来对导出的命令进行重命名。
+
+`FINSH_FUNCTION_EXPORT_ALIAS(name, alias, desc);`
+
+|**参数**|**描述** |
+|----------|-------------------------|
+| name | 要导出的命令 |
+| alias | 导出到 FinSH 时显示的名字 |
+| desc | 导出命令的描述 |
+
+在重命名的命令名字前加 `__cmd_` 就可以将命令导出到 msh 模式,否则,命令会被导出到 C-Style 模式。以下示例定义了一个 hello 函数,并将它重命名为 ho 后导出成 C-Style 模式下的命令。
+
+```c
+void hello(void)
+{
+ rt_kprintf("hello RT-Thread!\n");
+}
+
+FINSH_FUNCTION_EXPORT_ALIAS(hello , ho, say hello to RT-Thread);
+```
+## FinSH 功能配置
+
+FinSH 功能可以裁剪,宏配置选项在 rtconfig.h 文件中定义,具体配置项如下表所示。
+
+|**宏定义** |**取值类型**|**描述** |**默认值**|
+|------------------------|----|------------|-------|
+| #define RT_USING_FINSH | 无 | 使能 FinSH | 开启 |
+| #define FINSH_THREAD_NAME | 字符串 | FinSH 线程的名字 | "tshell" |
+| #define FINSH_USING_HISTORY | 无 | 打开历史回溯功能 | 开启 |
+| #define FINSH_HISTORY_LINES | 整数型 | 能回溯的历史命令行数 | 5|
+| #define FINSH_USING_SYMTAB | 无 | 可以在 FinSH 中使用符号表 | 开启 |
+|#define FINSH_USING_DESCRIPTION | 无 | 给每个 FinSH 的符号添加一段描述 | 开启 |
+| #define FINSH_USING_MSH| 无 | 使能 msh 模式 | 开启 |
+| #define FINSH_USING_MSH_ONLY | 无 | 只使用 msh 模式 | 开启 |
+| #define FINSH_ARG_MAX | 整数型 | 最大输入参数数量 | 10 |
+| #define FINSH_USING_AUTH | 无 | 使能权限验证 | 关闭 |
+| #define FINSH_DEFAULT_PASSWORD | 字符串 | 权限验证密码 | 关闭 |
+
+rtconfig.h 中的参考配置示例如下所示,可以根据实际功能需求情况进行配置。
+
+```c
+/* 开启 FinSH */
+#define RT_USING_FINSH
+
+/* 将线程名称定义为 tshell */
+#define FINSH_THREAD_NAME "tshell"
+
+/* 开启历史命令 */
+#define FINSH_USING_HISTORY
+/* 记录 5 行历史命令 */
+#define FINSH_HISTORY_LINES 5
+
+/* 开启使用 Tab 键 */
+#define FINSH_USING_SYMTAB
+/* 开启描述功能 */
+#define FINSH_USING_DESCRIPTION
+
+/* 定义 FinSH 线程优先级为 20 */
+#define FINSH_THREAD_PRIORITY 20
+/* 定义 FinSH 线程的栈大小为 4KB */
+#define FINSH_THREAD_STACK_SIZE 4096
+/* 定义命令字符长度为 80 字节 */
+#define FINSH_CMD_SIZE 80
+
+/* 开启 msh 功能 */
+#define FINSH_USING_MSH
+/* 默认使用 msh 功能 */
+#define FINSH_USING_MSH_DEFAULT
+/* 最大输入参数数量为 10 个 */
+#define FINSH_ARG_MAX 10
+```
+
+## FinSH 应用示例
+
+### 不带参数的 msh 命令示例
+
+本小节将演示如何将一个自定义的命令导出到 msh 中,示例代码如下所示,代码中创建了 hello 函数,然后通过 MSH_CMD_EXPORT 命令即可将 hello 函数导出到 FinSH 命令列表中。
+
+```c
+#include
+
+void hello(void)
+{
+ rt_kprintf("hello RT-Thread!\n");
+}
+
+MSH_CMD_EXPORT(hello , say hello to RT-Thread);
+```
+
+系统运行起来后,在 FinSH 控制台按 tab 键可以看到导出的命令:
+
+```c
+msh />
+RT-Thread shell commands:
+hello - say hello to RT-Thread
+version - show RT-Thread version information
+list_thread - list thread
+……
+```
+
+运行 hello 命令,运行结果如下所示:
+
+```c
+msh />hello
+hello RT_Thread!
+msh />
+```
+
+### 带参数的 msh 命令示例
+
+本小节将演示如何将一个带参数的自定义的命令导出到 FinSH 中, 示例代码如下所示,代码中创建了 `atcmd()` 函数,然后通过 MSH_CMD_EXPORT 命令即可将 `atcmd()` 函数导出到 msh 命令列表中。
+
+```c
+#include
+
+static void atcmd(int argc, char**argv)
+{
+ if (argc < 2)
+ {
+ rt_kprintf("Please input'atcmd '\n");
+ return;
+ }
+
+ if (!rt_strcmp(argv[1], "server"))
+ {
+ rt_kprintf("AT server!\n");
+ }
+ else if (!rt_strcmp(argv[1], "client"))
+ {
+ rt_kprintf("AT client!\n");
+ }
+ else
+ {
+ rt_kprintf("Please input'atcmd '\n");
+ }
+}
+
+MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd );
+```
+
+系统运行起来后,在 FinSH 控制台按 tab 键可以看到导出的命令:
+
+```c
+msh />
+RT-Thread shell commands:
+hello - say hello to RT-Thread
+atcmd - atcmd sample: atcmd
+version - show RT-Thread version information
+list_thread - list thread
+……
+```
+
+运行 atcmd 命令,运行结果如下所示:
+
+```c
+msh />atcmd
+Please input 'atcmd '
+msh />
+```
+
+运行 atcmd server 命令,运行结果如下所示:
+
+```c
+msh />atcmd server
+AT server!
+msh />
+```
+
+运行 atcmd client 命令,运行结果如下所示:
+
+```c
+msh />atcmd client
+AT client!
+msh />
+```
+
+## FinSH 移植
+
+FinSH 完全采用 ANSI C 编写,具备极好的移植性;内存占用少,如果不使用前面章节中介绍的函数方式动态地向 FinSH 添加符号,FinSH 将不会动态申请内存。FinSH 源码位于 `components/finsh` 目录下。移植 FinSH 需要注意以下几个方面:
+
+* FinSH 线程:
+
+每次的命令执行都是在 FinSH 线程(即 tshell 线程)的上下文中完成的。当定义 RT_USING_FINSH 宏时,就可以在初始化线程中调用 finsh_system_init() 初始化 FinSH 线程。RT-Thread 1.2.0 之后的版本中可以不使用 `finsh_set_device(const char* device_name)` 函数去显式指定使用的设备,而是会自动调用 `rt_console_get_device()` 函数去使用 console 设备(RT-Thread 1.1.x 及以下版本中必须使用 `finsh_set_device(const char* device_name)` 指定 FinSH 使用的设备)。FinSH 线程在函数 `finsh_system_init()` 函数中被创建,它将一直等待 rx_sem 信号量。
+
+* FinSH 的输出:
+
+FinSH 的输出依赖于系统的输出,在 RT-Thread 中依赖 `rt_kprintf()` 输出。在启动函数 `rt_hw_board_init()` 中, `rt_console_set_device(const char* name)` 函数设置了 FinSH 的打印输出设备。
+
+* FinSH 的输入:
+
+FinSH 线程在获得了 rx_sem 信号量后,调用 `rt_device_read()` 函数从设备 (选用串口设备) 中获得一个字符然后处理。所以 FinSH 的移植需要 `rt_device_read()` 函数的实现。而 rx_sem 信号量的释放通过调用 `rx_indicate()` 函数以完成对 FinSH 线程的输入通知。通常的过程是,当串口接收中断发生时(即串口有输入),接受中断服务例程调用 `rx_indicate()` 函数通知 FinSH 线程有输入,而后 FinSH 线程获取串口输入最后做相应的命令处理。
\ No newline at end of file
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09fun1.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09fun1.png
new file mode 100644
index 0000000000000000000000000000000000000000..d854b3c5c11aaa076e1c8fe591e703389a391620
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09fun1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09fun2.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09fun2.png
new file mode 100644
index 0000000000000000000000000000000000000000..290601f4e52508523cedfd7fab63ec13c0a5ec99
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09fun2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_handle.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_handle.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb4195d2b106411f3f6902507a6d6963cf88713f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_handle.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..d1fa3ac3e5e7cbd43cace95613879ee9864748a7
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_reque.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_reque.png
new file mode 100644
index 0000000000000000000000000000000000000000..040ef96cb8301c3657ea9b29b31ba7199ab066bd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_reque.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_table.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_table.png
new file mode 100644
index 0000000000000000000000000000000000000000..213140a7e6b5c4e1c6d476f67f99141525df3e3f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_table.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..b91978c151132675843821170ffa99534238ed0f
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work_process.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work_process.png
new file mode 100644
index 0000000000000000000000000000000000000000..8d15d327b20ce710ee59abe010584b0a27c74f25
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work_process.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work_sta.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work_sta.png
new file mode 100644
index 0000000000000000000000000000000000000000..ceca462c6ef65eb2633d0768e3b1d96286eaeba3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09interrupt_work_sta.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09relation.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09relation.png
new file mode 100644
index 0000000000000000000000000000000000000000..3938dce4826e180358bc2abc7f151235361741b6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09relation.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09ths_switch.png b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09ths_switch.png
new file mode 100644
index 0000000000000000000000000000000000000000..f13fce62def649186d7b222f2dd64d6126b5c9e4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/figures/09ths_switch.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/interrupt/interrupt.md b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/interrupt.md
new file mode 100644
index 0000000000000000000000000000000000000000..13dddbcf2f75479465c2666bea3c19a8deca9b32
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/interrupt/interrupt.md
@@ -0,0 +1,576 @@
+# 中断管理
+
+什么是中断?简单的解释就是系统正在处理某一个正常事件,忽然被另一个需要马上处理的紧急事件打断,系统转而处理这个紧急事件,待处理完毕,再恢复运行刚才被打断的事件。生活中,我们经常会遇到这样的场景:
+
+当你正在专心看书的时候,忽然来了一个电话,于是记下书的页码,去接电话,接完电话后接着刚才的页码继续看书,这是一个典型的中断的过程。
+
+电话是老师打过来的,让你赶快交作业,你判断交作业的优先级比看书高,于是电话挂断后先做作业,等交完作业后再接着刚才的页码继续看书,这是一个典型的在中断中进行任务调度的过程。
+
+这些场景在嵌入式系统中也很常见,当 CPU 正在处理内部数据时,外界发生了紧急情况,要求 CPU 暂停当前的工作转去处理这个 [异步事件](https://baike.baidu.com/item/%E7%B4%A7%E6%80%A5%E4%BA%8B%E4%BB%B6)。处理完毕后,再回到原来被中断的地址,继续原来的工作,这样的过程称为中断。实现这一功能的系统称为 [中断系统](https://baike.baidu.com/item/%E4%B8%AD%E6%96%AD%E7%B3%BB%E7%BB%9F),申请 CPU 中断的请求源称为 [中断源](https://baike.baidu.com/item/%E4%B8%AD%E6%96%AD%E6%BA%90)。中断是一种异常,异常是导致处理器脱离正常运行转向执行特殊代码的任何事件,如果不及时进行处理,轻则系统出错,重则会导致系统毁灭性地瘫痪。所以正确地处理异常,避免错误的发生是提高软件鲁棒性(稳定性)非常重要的一环。如下图是一个简单的中断示意图。
+
+
+
+中断处理与 CPU 架构密切相关,所以,本章会先介绍 ARM Cortex-M 的 CPU 架构,然后结合 Cortex-M CPU 架构来介绍 RT-Thread 的中断管理机制,读完本章,大家将深入了解 RT-Thread 的中断处理过程,如何添加中断服务程序(ISR)以及相关的注意事项。
+
+## Cortex-M CPU 架构基础
+
+不同于老的经典 ARM 处理器(例如:ARM7, ARM9),ARM Cortex-M 处理器有一个非常不同的架构,Cortex-M 是一个家族系列,其中包括 Cortex M0/M3/M4/M7 多个不同型号,每个型号之间会有些区别,例如 Cortex-M4 比 Cortex-M3 多了浮点计算功能等,但它们的编程模型基本是一致的,因此本书中介绍中断管理和移植的部分都不会对 Cortex M0/M3/M4/M7 做太精细的区分。本节主要介绍和 RT-Thread 中断管理相关的架构部分。
+
+### 寄存器简介
+
+Cortex-M 系列 CPU 的寄存器组里有 R0\~R15 共 16 个通用寄存器组和若干特殊功能寄存器,如下图所示。
+
+通用寄存器组里的 R13 作为堆栈指针寄存器 (Stack Pointer,SP);R14 作为连接寄存器 (Link Register,LR),用于在调用子程序时,存储返回地址;R15 作为程序计数器 (Program Counter,PC),其中堆栈指针寄存器可以是主堆栈指针(MSP),也可以是进程堆栈指针(PSP)。
+
+
+
+特殊功能寄存器包括程序状态字寄存器组(PSRs)、中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI)、控制寄存器(CONTROL),可以通过 MSR/MRS 指令来访问特殊功能寄存器,例如:
+
+```
+MRS R0, CONTROL ; 读取 CONTROL 到 R0 中
+MSR CONTROL, R0 ; 写入 R0 到 CONTROL 寄存器中
+```
+
+程序状态字寄存器里保存算术与逻辑标志,例如负数标志,零结果标志,溢出标志等等。中断屏蔽寄存器组控制 Cortex-M 的中断除能。控制寄存器用来定义特权级别和当前使用哪个堆栈指针。
+
+如果是具有浮点单元的 Cortex-M4 或者 Cortex-M7,控制寄存器也用来指示浮点单元当前是否在使用,浮点单元包含了 32 个浮点通用寄存器 S0\~S31 和特殊 FPSCR 寄存器(Floating point status and control register)。
+
+### 操作模式和特权级别
+
+Cortex-M 引入了操作模式和特权级别的概念,分别为线程模式和处理模式,如果进入异常或中断处理则进入处理模式,其他情况则为线程模式。
+
+
+
+Cortex-M 有两个运行级别,分别为特权级和用户级,线程模式可以工作在特权级或者用户级,而处理模式总工作在特权级,可通过 CONTROL 特殊寄存器控制。工作模式状态切换情况如上图所示。
+
+Cortex-M 的堆栈寄存器 SP 对应两个物理寄存器 MSP 和 PSP,MSP 为主堆栈,PSP 为进程堆栈,处理模式总是使用 MSP 作为堆栈,线程模式可以选择使用 MSP 或 PSP 作为堆栈,同样通过 CONTROL 特殊寄存器控制。复位后,Cortex-M 默认进入线程模式、特权级、使用 MSP 堆栈。
+
+### 嵌套向量中断控制器
+
+Cortex-M 中断控制器名为 NVIC(嵌套向量中断控制器),支持中断嵌套功能。当一个中断触发并且系统进行响应时,处理器硬件会将当前运行位置的上下文寄存器自动压入中断栈中,这部分的寄存器包括 PSR、PC、LR、R12、R3-R0 寄存器。
+
+
+
+当系统正在服务一个中断时,如果有一个更高优先级的中断触发,那么处理器同样会打断当前运行的中断服务程序,然后把这个中断服务程序上下文的 PSR、PC、LR、R12、R3-R0 寄存器自动保存到中断栈中。
+
+### PendSV 系统调用
+
+PendSV 也称为可悬起的系统调用,它是一种异常,可以像普通的中断一样被挂起,它是专门用来辅助操作系统进行上下文切换的。PendSV 异常会被初始化为最低优先级的异常。每次需要进行上下文切换的时候,会手动触发 PendSV 异常,在 PendSV 异常处理函数中进行上下文切换。在下一章《内核移植》中会详细介绍利用 PendSV 机制进行操作系统上下文切换的详细流程。
+
+## RT-Thread 中断工作机制
+
+### 中断向量表
+
+中断向量表是所有中断处理程序的入口,如下图所示是 Cortex-M 系列的中断处理过程:把一个函数(用户中断服务程序)同一个虚拟中断向量表中的中断向量联系在一起。当中断向量对应中断发生的时候,被挂接的用户中断服务程序就会被调用执行。
+
+
+
+在 Cortex-M 内核上,所有中断都采用中断向量表的方式进行处理,即当一个中断触发时,处理器将直接判定是哪个中断源,然后直接跳转到相应的固定位置进行处理,每个中断服务程序必须排列在一起放在统一的地址上(这个地址必须要设置到 NVIC 的中断向量偏移寄存器中)。中断向量表一般由一个数组定义或在起始代码中给出,默认采用起始代码给出:
+
+```c
+ __Vectors DCD __initial_sp ; Top of Stack
+ DCD Reset_Handler ; Reset 处理函数
+ DCD NMI_Handler ; NMI 处理函数
+ DCD HardFault_Handler ; Hard Fault 处理函数
+ DCD MemManage_Handler ; MPU Fault 处理函数
+ DCD BusFault_Handler ; Bus Fault 处理函数
+ DCD UsageFault_Handler ; Usage Fault 处理函数
+ DCD 0 ; 保留
+ DCD 0 ; 保留
+ DCD 0 ; 保留
+ DCD 0 ; 保留
+ DCD SVC_Handler ; SVCall 处理函数
+ DCD DebugMon_Handler ; Debug Monitor 处理函数
+ DCD 0 ; 保留
+ DCD PendSV_Handler ; PendSV 处理函数
+ DCD SysTick_Handler ; SysTick 处理函数
+
+… …
+
+NMI_Handler PROC
+ EXPORT NMI_Handler [WEAK]
+ B .
+ ENDP
+HardFault_Handler PROC
+ EXPORT HardFault_Handler [WEAK]
+ B .
+ ENDP
+… …
+```
+
+请注意代码后面的 [WEAK] 标识,它是符号弱化标识,在 [WEAK] 前面的符号(如 NMI_Handler、HardFault_Handler)将被执行弱化处理,如果整个代码在链接时遇到了名称相同的符号(例如与 NMI_Handler 相同名称的函数),那么代码将使用未被弱化定义的符号(与 NMI_Handler 相同名称的函数),而与弱化符号相关的代码将被自动丢弃。
+
+以 SysTick 中断为例,在系统启动代码中,需要填上 SysTick_Handler 中断入口函数,然后实现该函数即可对 SysTick 中断进行响应,中断处理函数示例程序如下所示:
+
+```c
+void SysTick_Handler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ rt_tick_increase();
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+```
+
+### 中断处理过程
+
+RT-Thread 中断管理中,将中断处理程序分为中断前导程序、用户中断服务程序、中断后续程序三部分,如下图:
+
+
+
+#### 中断前导程序
+
+中断前导程序主要工作如下:
+
+1)保存 CPU 中断现场,这部分跟 CPU 架构相关,不同 CPU 架构的实现方式有差异。
+
+对于 Cortex-M 来说,该工作由硬件自动完成。当一个中断触发并且系统进行响应时,处理器硬件会将当前运行部分的上下文寄存器自动压入中断栈中,这部分的寄存器包括 PSR、PC、LR、R12、R3-R0 寄存器。
+
+2)通知内核进入中断状态,调用 rt_interrupt_enter() 函数,作用是把全局变量 rt_interrupt_nest 加 1,用它来记录中断嵌套的层数,代码如下所示。
+
+```c
+void rt_interrupt_enter(void)
+{
+ rt_base_t level;
+
+ level = rt_hw_interrupt_disable();
+ rt_interrupt_nest ++;
+ rt_hw_interrupt_enable(level);
+}
+```
+
+#### 用户中断服务程序
+
+在用户中断服务程序(ISR)中,分为两种情况,第一种情况是不进行线程切换,这种情况下用户中断服务程序和中断后续程序运行完毕后退出中断模式,返回被中断的线程。
+
+另一种情况是,在中断处理过程中需要进行线程切换,这种情况会调用 rt_hw_context_switch_interrupt() 函数进行上下文切换,该函数跟 CPU 架构相关,不同 CPU 架构的实现方式有差异。
+
+在 Cortex-M 架构中,rt_hw_context_switch_interrupt() 的函数实现流程如下图所示,它将设置需要切换的线程 rt_interrupt_to_thread 变量,然后触发 PendSV 异常(PendSV 异常是专门用来辅助上下文切换的,且被初始化为最低优先级的异常)。PendSV 异常被触发后,不会立即进行 PendSV 异常中断处理程序,因为此时还在中断处理中,只有当中断后续程序运行完毕,真正退出中断处理后,才进入 PendSV 异常中断处理程序。
+
+
+
+#### 中断后续程序
+
+中断后续程序主要完成的工作是:
+
+1 通知内核离开中断状态,通过调用 rt_interrupt_leave() 函数,将全局变量 rt_interrupt_nest 减 1,代码如下所示。
+
+```c
+void rt_interrupt_leave(void)
+{
+ rt_base_t level;
+
+ level = rt_hw_interrupt_disable();
+ rt_interrupt_nest --;
+ rt_hw_interrupt_enable(level);
+}
+```
+
+2 恢复中断前的 CPU 上下文,如果在中断处理过程中未进行线程切换,那么恢复 from 线程的 CPU 上下文,如果在中断中进行了线程切换,那么恢复 to 线程的 CPU 上下文。这部分实现跟 CPU 架构相关,不同 CPU 架构的实现方式有差异,在 Cortex-M 架构中实现流程如下图所示。
+
+
+
+### 中断嵌套
+
+在允许中断嵌套的情况下,在执行中断服务程序的过程中,如果出现高优先级的中断,当前中断服务程序的执行将被打断,以执行高优先级中断的中断服务程序,当高优先级中断的处理完成后,被打断的中断服务程序才又得到继续执行,如果需要进行线程调度,线程的上下文切换将在所有中断处理程序都运行结束时才发生,如下图所示。
+
+
+
+### 中断栈
+
+在中断处理过程中,在系统响应中断前,软件代码(或处理器)需要把当前线程的上下文保存下来(通常保存在当前线程的线程栈中),再调用中断服务程序进行中断响应、处理。在进行中断处理时(实质是调用用户的中断服务程序函数),中断处理函数中很可能会有自己的局部变量,这些都需要相应的栈空间来保存,所以中断响应依然需要一个栈空间来做为上下文,运行中断处理函数。中断栈可以保存在打断线程的栈中,当从中断中退出时,返回相应的线程继续执行。
+
+中断栈也可以与线程栈完全分离开来,即每次进入中断时,在保存完打断线程上下文后,切换到新的中断栈中独立运行。在中断退出时,再做相应的上下文恢复。使用独立中断栈相对来说更容易实现,并且对于线程栈使用情况也比较容易了解和掌握(否则必须要为中断栈预留空间,如果系统支持中断嵌套,还需要考虑应该为嵌套中断预留多大的空间)。
+
+RT-Thread 采用的方式是提供独立的中断栈,即中断发生时,中断的前期处理程序会将用户的栈指针更换到系统事先留出的中断栈空间中,等中断退出时再恢复用户的栈指针。这样中断就不会占用线程的栈空间,从而提高了内存空间的利用率,且随着线程的增加,这种减少内存占用的效果也越明显。
+
+在 Cortex-M 处理器内核里有两个堆栈指针,一个是主堆栈指针(MSP),是默认的堆栈指针,在运行第一个线程之前和在中断和异常服务程序里使用;另一个是线程堆栈指针(PSP),在线程里使用。在中断和异常服务程序退出时,修改 LR 寄存器的第 2 位的值为 1,线程的 SP 就由 MSP 切换到 PSP。
+
+### 中断的底半处理
+
+RT-Thread 不对中断服务程序所需要的处理时间做任何假设、限制,但如同其他实时操作系统或非实时操作系统一样,用户需要保证所有的中断服务程序在尽可能短的时间内完成(中断服务程序在系统中相当于拥有最高的优先级,会抢占所有线程优先执行)。这样在发生中断嵌套,或屏蔽了相应中断源的过程中,不会耽误嵌套的其他中断处理过程,或自身中断源的下一次中断信号。
+
+当一个中断发生时,中断服务程序需要取得相应的硬件状态或者数据。如果中断服务程序接下来要对状态或者数据进行简单处理,比如 CPU 时钟中断,中断服务程序只需对一个系统时钟变量进行加一操作,然后就结束中断服务程序。这类中断需要的运行时间往往都比较短。但对于另外一些中断,中断服务程序在取得硬件状态或数据以后,还需要进行一系列更耗时的处理过程,通常需要将该中断分割为两部分,即**上半部分**(Top Half)和**底半部分**(Bottom Half)。在上半部分中,取得硬件状态和数据后,打开被屏蔽的中断,给相关线程发送一条通知(可以是 RT-Thread 所提供的信号量、事件、邮箱或消息队列等方式),然后结束中断服务程序;而接下来,相关的线程在接收到通知后,接着对状态或数据进行进一步的处理,这一过程称之为**底半处理**。
+
+为了详细描述底半处理在 RT-Thread 中的实现,我们以一个虚拟的网络设备接收网络数据包作为范例,如下代码,并假设接收到数据报文后,系统对报文的分析、处理是一个相对耗时的,比外部中断源信号重要性小许多的,而且在不屏蔽中断源信号情况下也能处理的过程。
+
+这个例子的程序创建了一个 nwt 线程,这个线程在启动运行后,将阻塞在 nw_bh_sem 信号上,一旦这个信号量被释放,将执行接下来的 nw_packet_parser 过程,开始 Bottom Half 的事件处理。
+
+```c
+/*
+ * 程序清单:中断底半处理例子
+ */
+
+/* 用于唤醒线程的信号量 */
+rt_sem_t nw_bh_sem;
+
+/* 数据读取、分析的线程 */
+void demo_nw_thread(void *param)
+{
+ /* 首先对设备进行必要的初始化工作 */
+ device_init_setting();
+
+ /*.. 其他的一些操作..*/
+
+ /* 创建一个 semaphore 来响应 Bottom Half 的事件 */
+ nw_bh_sem = rt_sem_create("bh_sem", 0, RT_IPC_FLAG_FIFO);
+
+ while(1)
+ {
+ /* 最后,让 demo_nw_thread 等待在 nw_bh_sem 上 */
+ rt_sem_take(nw_bh_sem, RT_WAITING_FOREVER);
+
+ /* 接收到 semaphore 信号后,开始真正的 Bottom Half 处理过程 */
+ nw_packet_parser (packet_buffer);
+ nw_packet_process(packet_buffer);
+ }
+}
+
+int main(void)
+{
+ rt_thread_t thread;
+
+ /* 创建处理线程 */
+ thread = rt_thread_create("nwt",demo_nw_thread, RT_NULL, 1024, 20, 5);
+
+ if (thread != RT_NULL)
+ rt_thread_startup(thread);
+}
+```
+
+接下来让我们来看一下 demo_nw_isr 中是如何处理 Top Half,并开启 Bottom Half 的,如下例。
+
+```c
+void demo_nw_isr(int vector, void *param)
+{
+ /* 当 network 设备接收到数据后,陷入中断异常,开始执行此 ISR */
+ /* 开始 Top Half 部分的处理,如读取硬件设备的状态以判断发生了何种中断 */
+ nw_device_status_read();
+
+ /*.. 其他一些数据操作等..*/
+
+ /* 释放 nw_bh_sem,发送信号给 demo_nw_thread,准备开始 Bottom Half */
+ rt_sem_release(nw_bh_sem);
+
+ /* 然后退出中断的 Top Half 部分,结束 device 的 ISR */
+}
+```
+
+从上面例子的两个代码片段可以看出,中断服务程序通过对一个信号量对象的等待和释放,来完成中断 Bottom Half 的起始和终结。由于将中断处理划分为 Top 和 Bottom 两个部分后,使得中断处理过程变为异步过程。这部分系统开销需要用户在使用 RT-Thread 时,必须认真考虑中断服务的处理时间是否大于给 Bottom Half 发送通知并处理的时间。
+
+RT-Thread 中断管理接口
+---------------------
+
+为了把操作系统和系统底层的异常、中断硬件隔离开来,RT-Thread 把中断和异常封装为一组抽象接口,如下图所示:
+
+
+
+### 中断服务程序挂接
+
+系统把用户的中断服务程序 (handler) 和指定的中断号关联起来,可调用如下的接口挂载一个新的中断服务程序:
+
+```c
+rt_isr_handler_t rt_hw_interrupt_install(int vector,
+ rt_isr_handler_t handler,
+ void *param,
+ char *name);
+```
+
+调用 rt_hw_interrupt_install() 后,当这个中断源产生中断时,系统将自动调用装载的中断服务程序。下表描述了此函数的输入参数和返回值:
+
+ rt_hw_interrupt_install() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|--------------------------------------------------|
+| vector | vector 是挂载的中断号 |
+| handler | 新挂载的中断服务程序 |
+| param | param 会作为参数传递给中断服务程序 |
+| name | 中断的名称 |
+|**返回**| —— |
+| return | 挂载这个中断服务程序之前挂载的中断服务程序的句柄 |
+
+> [!NOTE]
+> 注:这个 API 并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。
+
+中断服务程序是一种需要特别注意的运行环境,它运行在非线程的执行环境下(一般为芯片的一种特殊运行模式(特权模式)),在这个运行环境中不能使用挂起当前线程的操作,因为当前线程并不存在,执行相关的操作会有类似打印提示信息,“Function [abc_func] shall not used in ISR”,含义是不应该在中断服务程序中调用的函数)。
+
+### 中断源管理
+
+通常在 ISR 准备处理某个中断信号之前,我们需要先屏蔽该中断源,在 ISR 处理完状态或数据以后,及时的打开之前被屏蔽的中断源。
+
+屏蔽中断源可以保证在接下来的处理过程中硬件状态或者数据不会受到干扰,可调用下面这个函数接口:
+
+```c
+void rt_hw_interrupt_mask(int vector);
+```
+
+调用 rt_hw_interrupt_mask 函数接口后,相应的中断将会被屏蔽(通常当这个中断触发时,中断状态寄存器会有相应的变化,但并不送达到处理器进行处理)。下表描述了此函数的输入参数:
+
+ rt_hw_interrupt_mask() 的输入参数
+
+|**参数**|**描述** |
+|----------|----------------|
+| vector | 要屏蔽的中断号 |
+
+> [!NOTE]
+> 注:这个 API 并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。
+
+为了尽可能的不丢失硬件中断信号,可调用下面的函数接口打开被屏蔽的中断源:
+
+```c
+void rt_hw_interrupt_umask(int vector);
+```
+
+调用 rt_hw_interrupt_umask 函数接口后,如果中断(及对应外设)被配置正确时,中断触发后,将送到到处理器进行处理。下表描述了此函数的输入参数:
+
+ rt_hw_interrupt_umask() 的输入参数
+
+|**参数**|**描述** |
+|----------|--------------------|
+| vector | 要打开屏蔽的中断号 |
+
+> [!NOTE]
+> 注:这个 API 并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。
+
+### 全局中断开关
+
+**全局中断开关也称为**中断锁,是禁止多线程访问临界区最简单的一种方式,即通过关闭中断的方式,来保证当前线程不会被其他事件打断(因为整个系统已经不再响应那些可以触发线程重新调度的外部事件),也就是当前线程不会被抢占,除非这个线程主动放弃了处理器控制权。当需要关闭整个系统的中断时,可调用下面的函数接口:
+
+```c
+rt_base_t rt_hw_interrupt_disable(void);
+```
+
+下表描述了此函数的返回值:
+
+ rt_hw_interrupt_disable() 的返回值
+
+|**返回**|**描述** |
+|----------|---------------------------------------------|
+| 中断状态 | rt_hw_interrupt_disable 函数运行前的中断状态 |
+
+恢复中断也称开中断。rt_hw_interrupt_enable()这个函数用于 “使能” 中断,它恢复了调用 rt_hw_interrupt_disable()函数前的中断状态。如果调用 rt_hw_interrupt_disable()函数前是关中断状态,那么调用此函数后依然是关中断状态。恢复中断往往是和关闭中断成对使用的,调用的函数接口如下:
+
+```c
+void rt_hw_interrupt_enable(rt_base_t level);
+```
+
+下表描述了此函数的输入参数:
+
+ rt_hw_interrupt_enable() 的输入参数
+
+|**参数**|**描述** |
+|----------|---------------------------------------------|
+| level | 前一次 rt_hw_interrupt_disable 返回的中断状态 |
+
+1)使用中断锁来操作临界区的方法可以应用于任何场合,且其他几类同步方式都是依赖于中断锁而实现的,可以说中断锁是最强大的和最高效的同步方法。只是使用中断锁最主要的问题在于,在中断关闭期间系统将不再响应任何中断,也就不能响应外部的事件。所以中断锁对系统的实时性影响非常巨大,当使用不当的时候会导致系统完全无实时性可言(可能导致系统完全偏离要求的时间需求);而使用得当,则会变成一种快速、高效的同步方式。
+
+例如,为了保证一行代码(例如赋值)的互斥运行,最快速的方法是使用中断锁而不是信号量或互斥量:
+
+```c
+ /* 关闭中断 */
+ level = rt_hw_interrupt_disable();
+ a = a + value;
+ /* 恢复中断 */
+ rt_hw_interrupt_enable(level);
+```
+
+在使用中断锁时,需要确保关闭中断的时间非常短,例如上面代码中的 a = a + value; 也可换成另外一种方式,例如使用信号量:
+
+```c
+ /* 获得信号量锁 */
+ rt_sem_take(sem_lock, RT_WAITING_FOREVER);
+ a = a + value;
+ /* 释放信号量锁 */
+ rt_sem_release(sem_lock);
+```
+
+这段代码在 rt_sem_take 、rt_sem_release 的实现中,已经存在使用中断锁保护信号量内部变量的行为,所以对于简单如 a = a + value; 的操作,使用中断锁将更为简洁快速。
+
+2)函数 rt_base_t rt_hw_interrupt_disable(void) 和函数 void rt_hw_interrupt_enable(rt_base_t level) 一般需要配对使用,从而保证正确的中断状态。
+
+在 RT-Thread 中,开关全局中断的 API 支持多级嵌套使用,简单嵌套中断的代码如下代码所示:
+
+ 简单嵌套中断使用
+
+```c
+#include
+
+void global_interrupt_demo(void)
+{
+ rt_base_t level0;
+ rt_base_t level1;
+
+ /* 第一次关闭全局中断,关闭之前的全局中断状态可能是打开的,也可能是关闭的 */
+ level0 = rt_hw_interrupt_disable();
+ /* 第二次关闭全局中断,关闭之前的全局中断是关闭的,关闭之后全局中断还是关闭的 */
+ level1 = rt_hw_interrupt_disable();
+
+ do_something();
+
+ /* 恢复全局中断到第二次关闭之前的状态,所以本次 enable 之后全局中断还是关闭的 */
+ rt_hw_interrupt_enable(level1);
+ /* 恢复全局中断到第一次关闭之前的状态,这时候的全局中断状态可能是打开的,也可能是关闭的 */
+ rt_hw_interrupt_enable(level0);
+}
+```
+
+这个特性可以给代码的开发带来很大的便利。例如在某个函数里关闭了中断,然后调用某些子函数,再打开中断。这些子函数里面也可能存在开关中断的代码。由于全局中断的 API 支持嵌套使用,用户无需为这些代码做特殊处理。
+
+### 中断通知
+
+当整个系统被中断打断,进入中断处理函数时,需要通知内核当前已经进入到中断状态。针对这种情况,可通过以下接口:
+
+```c
+void rt_interrupt_enter(void);
+void rt_interrupt_leave(void);
+```
+
+这两个接口分别用在中断前导程序和中断后续程序中,均会对 rt_interrupt_nest(中断嵌套深度)的值进行修改:
+
+每当进入中断时,可以调用 rt_interrupt_enter() 函数,用于通知内核,当前已经进入了中断状态,并增加中断嵌套深度(执行 rt_interrupt_nest++);
+
+每当退出中断时,可以调用 rt_interrupt_leave() 函数,用于通知内核,当前已经离开了中断状态,并减少中断嵌套深度(执行 rt_interrupt_nest
+--)。注意不要在应用程序中调用这两个接口函数。
+
+使用 rt_interrupt_enter/leave() 的作用是,在中断服务程序中,如果调用了内核相关的函数(如释放信号量等操作),则可以通过判断当前中断状态,让内核及时调整相应的行为。例如:在中断中释放了一个信号量,唤醒了某线程,但通过判断发现当前系统处于中断上下文环境中,那么在进行线程切换时应该采取中断中线程切换的策略,而不是立即进行切换。
+
+但如果中断服务程序不会调用内核相关的函数(释放信号量等操作),这个时候,也可以不调用 rt_interrupt_enter/leave() 函数。
+
+在上层应用中,在内核需要知道当前已经进入到中断状态或当前嵌套的中断深度时,可调用 rt_interrupt_get_nest() 接口,它会返回 rt_interrupt_nest。如下:
+
+```c
+rt_uint8_t rt_interrupt_get_nest(void);
+```
+
+下表描述了 rt_interrupt_get_nest() 的返回值
+
+|**返回**|**描述** |
+|----------|--------------------------------|
+| 0 | 当前系统不处于中断上下文环境中 |
+| 1 | 当前系统处于中断上下文环境中 |
+| 大于 1 | 当前中断嵌套层次 |
+
+中断与轮询
+----------
+
+当驱动外设工作时,其编程模式到底采用中断模式触发还是轮询模式触发往往是驱动开发人员首先要考虑的问题,并且这个问题在实时操作系统与分时操作系统中差异还非常大。因为轮询模式本身采用顺序执行的方式:查询到相应的事件然后进行对应的处理。所以轮询模式从实现上来说,相对简单清晰。例如往串口中写入数据,仅当串口控制器写完一个数据时,程序代码才写入下一个数据(否则这个数据丢弃掉)。相应的代码可以是这样的:
+
+```c
+/* 轮询模式向串口写入数据 */
+ while (size)
+ {
+ /* 判断 UART 外设中数据是否发送完毕 */
+ while (!(uart->uart_device->SR & USART_FLAG_TXE));
+ /* 当所有数据发送完毕后,才发送下一个数据 */
+ uart->uart_device->DR = (*ptr & 0x1FF);
+
+ ++ptr; --size;
+ }
+```
+
+在实时系统中轮询模式可能会出现非常大问题,因为在实时操作系统中,当一个程序持续地执行时(轮询时),它所在的线程会一直运行,比它优先级低的线程都不会得到运行。而分时系统中,这点恰恰相反,几乎没有优先级之分,可以在一个时间片运行这个程序,然后在另外一段时间片上运行另外一段程序。
+
+所以通常情况下,实时系统中更多采用的是中断模式来驱动外设。当数据达到时,由中断唤醒相关的处理线程,再继续进行后续的动作。例如一些携带 FIFO(包含一定数据量的先进先出队列)的串口外设,其写入过程可以是这样的,如下图所示:
+
+
+
+线程先向串口的 FIFO 中写入数据,当 FIFO 满时,线程主动挂起。串口控制器持续地从 FIFO 中取出数据并以配置的波特率(例如 115200bps)发送出去。当 FIFO 中所有数据都发送完成时,将向处理器触发一个中断;当中断服务程序得到执行时,可以唤醒这个线程。这里举例的是 FIFO 类型的设备,在现实中也有 DMA 类型的设备,原理类似。
+
+对于低速设备来说,运用这种模式非常好,因为在串口外设把 FIFO 中的数据发送出去前,处理器可以运行其他的线程,这样就提高了系统的整体运行效率(甚至对于分时系统来说,这样的设计也是非常必要)。但是对于一些高速设备,例如传输速度达到 10Mbps 的时候,假设一次发送的数据量是 32 字节,我们可以计算出发送这样一段数据量需要的时间是:(32 X 8) X 1/10Mbps = 25us。当数据需要持续传输时,系统将在 25us 后触发一个中断以唤醒上层线程继续下次传递。假设系统的线程切换时间是 8us(通常实时操作系统的线程上下文切换时间只有几个 us),那么当整个系统运行时,对于数据带宽利用率将只有 25/(25+8) =75.8%。但是采用轮询模式,数据带宽的利用率则可能达到 100%。这个也是大家普遍认为实时系统中数据吞吐量不足的缘故,系统开销消耗在了线程切换上(有些实时系统甚至会如本章前面说的,采用底半处理,分级的中断处理方式,相当于又拉长中断到发送线程的时间开销,效率会更进一步下降)。
+
+通过上述的计算过程,我们可以看出其中的一些关键因素:发送数据量越小,发送速度越快,对于数据吞吐量的影响也将越大。归根结底,取决于系统中产生中断的频度如何。当一个实时系统想要提升数据吞吐量时,可以考虑的几种方式:
+
+1)增加每次数据量发送的长度,每次尽量让外设尽量多地发送数据;
+
+2)必要情况下更改中断模式为轮询模式。同时为了解决轮询方式一直抢占处理机,其他低优先级线程得不到运行的情况,可以把轮询线程的优先级适当降低。
+
+全局中断开关使用示例
+--------------------
+
+这是一个中断的应用例程:在多线程访问同一个变量时,使用开关全局中断对该变量进行保护,如下代码所示:
+
+ 使用开关中断进行全局变量的访问
+
+```c
+#include
+#include
+
+#define THREAD_PRIORITY 20
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+
+/* 同时访问的全局变量 */
+static rt_uint32_t cnt;
+void thread_entry(void *parameter)
+{
+ rt_uint32_t no;
+ rt_uint32_t level;
+
+ no = (rt_uint32_t) parameter;
+ while (1)
+ {
+ /* 关闭全局中断 */
+ level = rt_hw_interrupt_disable();
+ cnt += no;
+ /* 恢复全局中断 */
+ rt_hw_interrupt_enable(level);
+
+ rt_kprintf("protect thread[%d]'s counter is %d\n", no, cnt);
+ rt_thread_mdelay(no * 10);
+ }
+}
+
+/* 用户应用程序入口 */
+int interrupt_sample(void)
+{
+ rt_thread_t thread;
+
+ /* 创建 t1 线程 */
+ thread = rt_thread_create("thread1", thread_entry, (void *)10,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ if (thread != RT_NULL)
+ rt_thread_startup(thread);
+
+
+ /* 创建 t2 线程 */
+ thread = rt_thread_create("thread2", thread_entry, (void *)20,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ if (thread != RT_NULL)
+ rt_thread_startup(thread);
+
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(interrupt_sample, interrupt sample);
+```
+
+仿真运行结果如下:
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 27 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >interrupt_sample
+msh >protect thread[10]'s counter is 10
+protect thread[20]'s counter is 30
+protect thread[10]'s counter is 40
+protect thread[20]'s counter is 60
+protect thread[10]'s counter is 70
+protect thread[10]'s counter is 80
+protect thread[20]'s counter is 100
+protect thread[10]'s counter is 110
+protect thread[10]'s counter is 120
+protect thread[20]'s counter is 140
+…
+```
+
+> [!NOTE]
+> 注:由于关闭全局中断会导致整个系统不能响应中断,所以在使用关闭全局中断做为互斥访问临界区的手段时,必须需要保证关闭全局中断的时间非常短,例如运行数条机器指令的时间。
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2abb7717fc2b97563d344d81dd3bd0b6d9a58a9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_use.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_use.png
new file mode 100644
index 0000000000000000000000000000000000000000..887b1823bdaae318238b15ad48bbf2171d2968e0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_use.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_work.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..a3969cf7cb8769b4093ffa7a51d48c7a4f5beb89
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06event_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06inter_ths_commu1.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06inter_ths_commu1.png
new file mode 100644
index 0000000000000000000000000000000000000000..59c29c255262ca76dd2ddf528963e0c415a5a928
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06inter_ths_commu1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06inter_ths_commu2.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06inter_ths_commu2.png
new file mode 100644
index 0000000000000000000000000000000000000000..ad1a9bf722b7d04e73d802d07a553aeedc801ec6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06inter_ths_commu2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06mutex_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06mutex_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..cbf7e6e3fbceef4041b69ed35e32b87594fa2089
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06mutex_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06mutex_work.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06mutex_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..b396b2da99d2d8201e46d923d3ed9d883c835626
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06mutex_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inherit.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inherit.png
new file mode 100644
index 0000000000000000000000000000000000000000..6822fc3572d4763cbd1820be606f828111923696
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inherit.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inversion.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inversion.png
new file mode 100644
index 0000000000000000000000000000000000000000..764052bbfcbe044970bf28483c6045ae6276eee9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inversion.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_lock.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_lock.png
new file mode 100644
index 0000000000000000000000000000000000000000..924265f5361c62877ea2a5fb5f00d1a6cbde9755
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_lock.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..671bc468b211fe4503fa8326747ce6be0b30594d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_work.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..d09ffc99ae8bbf2bfd859307db03191867f33e04
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06sem_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md
new file mode 100644
index 0000000000000000000000000000000000000000..799771f7ed7b981b65f2ab9abb6cac6b326e399a
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md
@@ -0,0 +1,1325 @@
+# 线程间同步
+
+在多线程实时系统中,一项工作的完成往往可以通过多个线程协调的方式共同来完成,那么多个线程之间如何 “默契” 协作才能使这项工作无差错执行?下面举个例子说明。
+
+例如一项工作中的两个线程:一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递:
+
+
+
+如果对共享内存的访问不是排他性的,那么各个线程间可能同时访问它,这将引起数据一致性的问题。例如,在显示线程试图显示数据之前,接收线程还未完成数据的写入,那么显示将包含不同时间采样的数据,造成显示数据的错乱。
+
+将传感器数据写入到共享内存块的接收线程 \#1 和将传感器数据从共享内存块中读出的线程 \#2 都会访问同一块内存。为了防止出现数据的差错,两个线程访问的动作必须是互斥进行的,应该是在一个线程对共享内存块操作完成后,才允许另一个线程去操作,这样,接收线程 \#1 与显示线程 \#2 才能正常配合,使此项工作正确地执行。
+
+同步是指按预定的先后次序进行运行,线程同步是指多个线程通过特定的机制(如互斥量,事件对象,临界区)来控制线程之间的执行顺序,也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间将是无序的。
+
+多个线程操作 / 访问同一块区域(代码),这块代码就称为临界区,上述例子中的共享内存块就是临界区。线程互斥是指对于临界区资源访问的排它性。当多个线程都要使用临界区资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。
+
+线程的同步方式有很多种,其核心思想都是:**在访问临界区的时候只允许一个 (或一类) 线程运行。**进入 / 退出临界区的方式有很多种:
+
+1)调用 rt_hw_interrupt_disable() 进入临界区,调用 rt_hw_interrupt_enable() 退出临界区;详见《中断管理》的全局中断开关内容。
+
+2)调用 rt_enter_critical() 进入临界区,调用 rt_exit_critical() 退出临界区。
+
+本章将介绍多种同步方式:**信号量**(semaphore)、**互斥量**(mutex)、和**事件集**(event)。学习完本章,大家将学会如何使用信号量、互斥量、事件集这些对象进行线程间的同步。
+
+## 信号量
+
+以生活中的停车场为例来理解信号量的概念:
+
+①当停车场空的时候,停车场的管理员发现有很多空车位,此时会让外面的车陆续进入停车场获得停车位;
+
+②当停车场的车位满的时候,管理员发现已经没有空车位,将禁止外面的车进入停车场,车辆在外排队等候;
+
+③当停车场内有车离开时,管理员发现有空的车位让出,允许外面的车进入停车场;待空车位填满后,又禁止外部车辆进入。
+
+在此例子中,管理员就相当于信号量,管理员手中空车位的个数就是信号量的值(非负数,动态变化);停车位相当于公共资源(临界区),车辆相当于线程。车辆通过获得管理员的允许取得停车位,就类似于线程通过获得信号量访问公共资源。
+
+### 信号量工作机制
+
+信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。
+
+信号量工作示意图如下图所示,每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)。
+
+
+
+### 信号量控制块
+
+在 RT-Thread 中,信号量控制块是操作系统用于管理信号量的一个数据结构,由结构体 struct rt_semaphore 表示。另外一种 C 表达方式 rt_sem_t,表示的是信号量的句柄,在 C 语言中的实现是指向信号量控制块的指针。信号量控制块结构的详细定义如下:
+
+```c
+struct rt_semaphore
+{
+ struct rt_ipc_object parent; /* 继承自 ipc_object 类 */
+ rt_uint16_t value; /* 信号量的值 */
+};
+/* rt_sem_t 是指向 semaphore 结构体的指针类型 */
+typedef struct rt_semaphore* rt_sem_t;
+```
+
+rt_semaphore 对象从 rt_ipc_object 中派生,由 IPC 容器所管理,信号量的最大值是 65535。
+
+### 信号量的管理方式
+
+信号量控制块中含有信号量相关的重要参数,在信号量各种状态间起到纽带的作用。信号量相关接口如下图所示,对一个信号量的操作包含:创建 / 初始化信号量、获取信号量、释放信号量、删除 / 脱离信号量。
+
+
+
+#### 创建和删除信号量
+
+当创建一个信号量时,内核首先创建一个信号量控制块,然后对该控制块进行基本的初始化工作,创建信号量使用下面的函数接口:
+
+```c
+ rt_sem_t rt_sem_create(const char *name,
+ rt_uint32_t value,
+ rt_uint8_t flag);
+```
+
+当调用这个函数时,系统将先从对象管理器中分配一个 semaphore 对象,并初始化这个对象,然后初始化父类 IPC 对象以及与 semaphore 相关的部分。在创建信号量指定的参数中,信号量标志参数决定了当信号量不可用时,多个线程等待的排队方式。当选择 RT_IPC_FLAG_FIFO(先进先出)方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择 RT_IPC_FLAG_PRIO(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。下表描述了该函数的输入参数与返回值:
+
+rt_sem_create() 的输入参数和返回值
+
+|**参数** |**描述** |
+|--------------------|-------------------------------------------------------------------|
+| name | 信号量名称 |
+| value | 信号量初始值 |
+| flag | 信号量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回** | —— |
+| RT_NULL | 创建失败 |
+| 信号量的控制块指针 | 创建成功 |
+
+系统不再使用信号量时,可通过删除信号量以释放系统资源,适用于动态创建的信号量。删除信号量使用下面的函数接口:
+
+```c
+rt_err_t rt_sem_delete(rt_sem_t sem);
+```
+
+调用这个函数时,系统将删除这个信号量。如果删除该信号量时,有线程正在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程(等待线程的返回值是 -
+RT_ERROR),然后再释放信号量的内存资源。下表描述了该函数的输入参数与返回值:
+
+ rt_sem_delete() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|----------------------------------|
+| sem | rt_sem_create() 创建的信号量对象 |
+|**返回**| —— |
+| RT_EOK | 删除成功 |
+
+#### 初始化和脱离信号量
+
+对于静态信号量对象,它的内存空间在编译时期就被编译器分配出来,放在读写数据段或未初始化数据段上,此时使用信号量就不再需要使用 rt_sem_create 接口来创建它,而只需在使用前对它进行初始化即可。初始化信号量对象可使用下面的函数接口:
+
+```c
+rt_err_t rt_sem_init(rt_sem_t sem,
+ const char *name,
+ rt_uint32_t value,
+ rt_uint8_t flag)
+```
+
+当调用这个函数时,系统将对这个 semaphore 对象进行初始化,然后初始化 IPC 对象以及与
+semaphore 相关的部分。信号量标志可用上面创建信号量函数里提到的标志。下表描述了该函数的输入参数与返回值:
+
+ rt_sem_init() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|-------------------------------------------------------------------|
+| sem | 信号量对象的句柄 |
+| name | 信号量名称 |
+| value | 信号量初始值 |
+| flag | 信号量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回**| —— |
+| RT_EOK | 初始化成功 |
+
+脱离信号量就是让信号量对象从内核对象管理器中脱离,适用于静态初始化的信号量。脱离信号量使用下面的函数接口:
+
+```c
+rt_err_t rt_sem_detach(rt_sem_t sem);
+```
+
+使用该函数后,内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离。原来挂起在信号量上的等待线程将获得 - RT_ERROR 的返回值。下表描述了该函数的输入参数与返回值:
+
+ rt_sem_detach() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------|
+| sem | 信号量对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 脱离成功 |
+
+#### 获取信号量
+
+线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1,获取信号量使用下面的函数接口:
+
+```c
+rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);
+```
+
+在调用这个函数时,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数 time 指定的时间内依然得不到信号量,线程将超时返回,返回值是 - RT_ETIMEOUT。下表描述了该函数的输入参数与返回值:
+
+ rt_sem_take() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|---------------------------------------------------|
+| sem | 信号量对象的句柄 |
+| time | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) |
+|**返回** | —— |
+| RT_EOK | 成功获得信号量 |
+| \-RT_ETIMEOUT | 超时依然未获得信号量 |
+| \-RT_ERROR | 其他错误 |
+
+#### 无等待获取信号量
+
+当用户不想在申请的信号量上挂起线程进行等待时,可以使用无等待方式获取信号量,无等待获取信号量使用下面的函数接口:
+
+```c
+rt_err_t rt_sem_trytake(rt_sem_t sem);
+```
+
+这个函数与 rt_sem_take(sem, 0) 的作用相同,即当线程申请的信号量资源实例不可用的时候,它不会等待在该信号量上,而是直接返回 - RT_ETIMEOUT。下表描述了该函数的输入参数与返回值:
+
+ rt_sem_trytake() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|------------------|
+| sem | 信号量对象的句柄 |
+|**返回** | —— |
+| RT_EOK | 成功获得信号量 |
+| \-RT_ETIMEOUT | 获取失败 |
+
+#### 释放信号量
+
+释放信号量可以唤醒挂起在该信号量上的线程。释放信号量使用下面的函数接口:
+
+```c
+rt_err_t rt_sem_release(rt_sem_t sem);
+```
+
+例如当信号量的值等于零时,并且有线程等待这个信号量时,释放信号量将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量;否则将把信号量的值加 1。下表描述了该函数的输入参数与返回值:
+
+ rt_sem_release() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------|
+| sem | 信号量对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功释放信号量 |
+
+### 信号量应用示例
+
+这是一个信号量使用例程,该例程创建了一个动态信号量,初始化两个线程,一个线程发送信号量,一个线程接收到信号量后,执行相应的操作。如下代码所示:
+
+信号量的使用:
+
+```c
+#include
+
+#define THREAD_PRIORITY 25
+#define THREAD_TIMESLICE 5
+
+/* 指向信号量的指针 */
+static rt_sem_t dynamic_sem = RT_NULL;
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread1_stack[1024];
+static struct rt_thread thread1;
+static void rt_thread1_entry(void *parameter)
+{
+ static rt_uint8_t count = 0;
+
+ while(1)
+ {
+ if(count <= 100)
+ {
+ count++;
+ }
+ else
+ return;
+
+ /* count 每计数 10 次,就释放一次信号量 */
+ if(0 == (count % 10))
+ {
+ rt_kprintf("t1 release a dynamic semaphore.\n");
+ rt_sem_release(dynamic_sem);
+ }
+ }
+}
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread2_stack[1024];
+static struct rt_thread thread2;
+static void rt_thread2_entry(void *parameter)
+{
+ static rt_err_t result;
+ static rt_uint8_t number = 0;
+ while(1)
+ {
+ /* 永久方式等待信号量,获取到信号量,则执行 number 自加的操作 */
+ result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("t2 take a dynamic semaphore, failed.\n");
+ rt_sem_delete(dynamic_sem);
+ return;
+ }
+ else
+ {
+ number++;
+ rt_kprintf("t2 take a dynamic semaphore. number = %d\n" ,number);
+ }
+ }
+}
+
+/* 信号量示例的初始化 */
+int semaphore_sample(void)
+{
+ /* 创建一个动态信号量,初始值是 0 */
+ dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);
+ if (dynamic_sem == RT_NULL)
+ {
+ rt_kprintf("create dynamic semaphore failed.\n");
+ return -1;
+ }
+ else
+ {
+ rt_kprintf("create done. dynamic semaphore value = 0.\n");
+ }
+
+ rt_thread_init(&thread1,
+ "thread1",
+ rt_thread1_entry,
+ RT_NULL,
+ &thread1_stack[0],
+ sizeof(thread1_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread1);
+
+ rt_thread_init(&thread2,
+ "thread2",
+ rt_thread2_entry,
+ RT_NULL,
+ &thread2_stack[0],
+ sizeof(thread2_stack),
+ THREAD_PRIORITY-1, THREAD_TIMESLICE);
+ rt_thread_startup(&thread2);
+
+ return 0;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(semaphore_sample, semaphore sample);
+```
+
+仿真运行结果:
+
+```c
+ \ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 27 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >semaphore_sample
+create done. dynamic semaphore value = 0.
+msh >t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 1
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 2
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 3
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 4
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 5
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 6
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 7
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 8
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 9
+t1 release a dynamic semaphore.
+t2 take a dynamic semaphore. number = 10
+```
+
+如上面运行结果:线程 1 在 count 计数为 10 的倍数时(count 计数为 100 之后线程退出),发送一个信号量,线程 2 在接收信号量后,对 number 进行加 1 操作。
+
+信号量的另一个应用例程如下所示,本例程将使用 2 个线程、3 个信号量实现生产者与消费者的例子。其中:
+
+3 个信号量分别为:①lock:信号量锁的作用,因为 2 个线程都会对同一个数组 array 进行操作,所以该数组是一个共享资源,锁用来保护这个共享资源。②empty:空位个数,初始化为 5 个空位。③full:满位个数,初始化为 0 个满位。
+
+2 个线程分别为:①生产者线程:获取到空位后,产生一个数字,循环放入数组中,然后释放一个满位。②消费者线程:获取到满位后,读取数组内容并相加,然后释放一个空位。
+
+生产者消费者例程
+
+```c
+#include
+
+#define THREAD_PRIORITY 6
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+
+/* 定义最大 5 个元素能够被产生 */
+#define MAXSEM 5
+
+/* 用于放置生产的整数数组 */
+rt_uint32_t array[MAXSEM];
+
+/* 指向生产者、消费者在 array 数组中的读写位置 */
+static rt_uint32_t set, get;
+
+/* 指向线程控制块的指针 */
+static rt_thread_t producer_tid = RT_NULL;
+static rt_thread_t consumer_tid = RT_NULL;
+
+struct rt_semaphore sem_lock;
+struct rt_semaphore sem_empty, sem_full;
+
+/* 生产者线程入口 */
+void producer_thread_entry(void *parameter)
+{
+ int cnt = 0;
+
+ /* 运行 10 次 */
+ while (cnt < 10)
+ {
+ /* 获取一个空位 */
+ rt_sem_take(&sem_empty, RT_WAITING_FOREVER);
+
+ /* 修改 array 内容,上锁 */
+ rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
+ array[set % MAXSEM] = cnt + 1;
+ rt_kprintf("the producer generates a number: %d\n", array[set % MAXSEM]);
+ set++;
+ rt_sem_release(&sem_lock);
+
+ /* 发布一个满位 */
+ rt_sem_release(&sem_full);
+ cnt++;
+
+ /* 暂停一段时间 */
+ rt_thread_mdelay(20);
+ }
+
+ rt_kprintf("the producer exit!\n");
+}
+
+/* 消费者线程入口 */
+void consumer_thread_entry(void *parameter)
+{
+ rt_uint32_t sum = 0;
+
+ while (1)
+ {
+ /* 获取一个满位 */
+ rt_sem_take(&sem_full, RT_WAITING_FOREVER);
+
+ /* 临界区,上锁进行操作 */
+ rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
+ sum += array[get % MAXSEM];
+ rt_kprintf("the consumer[%d] get a number: %d\n", (get % MAXSEM), array[get % MAXSEM]);
+ get++;
+ rt_sem_release(&sem_lock);
+
+ /* 释放一个空位 */
+ rt_sem_release(&sem_empty);
+
+ /* 生产者生产到 10 个数目,停止,消费者线程相应停止 */
+ if (get == 10) break;
+
+ /* 暂停一小会时间 */
+ rt_thread_mdelay(50);
+ }
+
+ rt_kprintf("the consumer sum is: %d\n", sum);
+ rt_kprintf("the consumer exit!\n");
+}
+
+int producer_consumer(void)
+{
+ set = 0;
+ get = 0;
+
+ /* 初始化 3 个信号量 */
+ rt_sem_init(&sem_lock, "lock", 1, RT_IPC_FLAG_FIFO);
+ rt_sem_init(&sem_empty, "empty", MAXSEM, RT_IPC_FLAG_FIFO);
+ rt_sem_init(&sem_full, "full", 0, RT_IPC_FLAG_FIFO);
+
+ /* 创建生产者线程 */
+ producer_tid = rt_thread_create("producer",
+ producer_thread_entry, RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY - 1,
+ THREAD_TIMESLICE);
+ if (producer_tid != RT_NULL)
+ {
+ rt_thread_startup(producer_tid);
+ }
+ else
+ {
+ rt_kprintf("create thread producer failed");
+ return -1;
+ }
+
+ /* 创建消费者线程 */
+ consumer_tid = rt_thread_create("consumer",
+ consumer_thread_entry, RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY + 1,
+ THREAD_TIMESLICE);
+ if (consumer_tid != RT_NULL)
+ {
+ rt_thread_startup(consumer_tid);
+ }
+ else
+ {
+ rt_kprintf("create thread consumer failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(producer_consumer, producer_consumer sample);
+```
+
+该例程的仿真结果如下:
+
+```
+\ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 27 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >producer_consumer
+the producer generates a number: 1
+the consumer[0] get a number: 1
+msh >the producer generates a number: 2
+the producer generates a number: 3
+the consumer[1] get a number: 2
+the producer generates a number: 4
+the producer generates a number: 5
+the producer generates a number: 6
+the consumer[2] get a number: 3
+the producer generates a number: 7
+the producer generates a number: 8
+the consumer[3] get a number: 4
+the producer generates a number: 9
+the consumer[4] get a number: 5
+the producer generates a number: 10
+the producer exit!
+the consumer[0] get a number: 6
+the consumer[1] get a number: 7
+the consumer[2] get a number: 8
+the consumer[3] get a number: 9
+the consumer[4] get a number: 10
+the consumer sum is: 55
+the consumer exit!
+```
+
+本例程可以理解为生产者生产产品放入仓库,消费者从仓库中取走产品。
+
+(1)生产者线程:
+
+1)获取 1 个空位(放产品 number),此时空位减 1;
+
+2)上锁保护;本次的产生的 number 值为 cnt+1,把值循环存入数组 array 中;再开锁;
+
+3)释放 1 个满位(给仓库中放置一个产品,仓库就多一个满位),满位加 1;
+
+(2)消费者线程:
+
+1)获取 1 个满位(取产品 number),此时满位减 1;
+
+2)上锁保护;将本次生产者生产的 number 值从 array 中读出来,并与上次的 number 值相加;再开锁;
+
+3)释放 1 个空位(从仓库上取走一个产品,仓库就多一个空位),空位加 1。
+
+生产者依次产生 10 个 number,消费者依次取走,并将 10 个 number 的值求和。信号量锁 lock 保护 array 临界区资源:保证了消费者每次取 number 值的排他性,实现了线程间同步。
+
+### 信号量的使用场合
+
+信号量是一种非常灵活的同步方式,可以运用在多种场合中。形成锁、同步、资源计数等关系,也能方便的用于线程与线程、中断与线程间的同步中。
+
+#### 线程同步
+
+线程同步是信号量最简单的一类应用。例如,使用信号量进行两个线程之间的同步,信号量的值初始化成 0,表示具备 0 个信号量资源实例;而尝试获得该信号量的线程,将直接在这个信号量上进行等待。
+
+当持有信号量的线程完成它处理的工作时,释放这个信号量,可以把等待在这个信号量上的线程唤醒,让它执行下一部分工作。这类场合也可以看成把信号量用于工作完成标志:持有信号量的线程完成它自己的工作,然后通知等待该信号量的线程继续下一部分工作。
+
+#### 锁
+
+锁,单一的锁常应用于多个线程间对同一共享资源(即临界区)的访问。信号量在作为锁来使用时,通常应将信号量资源实例初始化成 1,代表系统默认有一个资源可用,因为信号量的值始终在 1 和 0 之间变动,所以这类锁也叫做二值信号量。如下图所示,当线程需要访问共享资源时,它需要先获得这个资源锁。当这个线程成功获得资源锁时,其他打算访问共享资源的线程会由于获取不到资源而挂起,这是因为其他线程在试图获取这个锁时,这个锁已经被锁上(信号量值是 0)。当获得信号量的线程处理完毕,退出临界区时,它将会释放信号量并把锁解开,而挂起在锁上的第一个等待线程将被唤醒从而获得临界区的访问权。
+
+
+
+#### 中断与线程的同步
+
+信号量也能够方便地应用于中断与线程间的同步,例如一个中断触发,中断服务例程需要通知线程进行相应的数据处理。这个时候可以设置信号量的初始值是 0,线程在试图持有这个信号量时,由于信号量的初始值是 0,线程直接在这个信号量上挂起直到信号量被释放。当中断触发时,先进行与硬件相关的动作,例如从硬件的 I/O 口中读取相应的数据,并确认中断以清除中断源,而后释放一个信号量来唤醒相应的线程以做后续的数据处理。例如 FinSH 线程的处理方式,如下图所示。
+
+
+
+信号量的值初始为 0,当 FinSH 线程试图取得信号量时,因为信号量值是 0,所以它会被挂起。当 console 设备有数据输入时,产生中断,从而进入中断服务例程。在中断服务例程中,它会读取 console 设备的数据,并把读得的数据放入 UART buffer 中进行缓冲,而后释放信号量,释放信号量的操作将唤醒 shell 线程。在中断服务例程运行完毕后,如果系统中没有比 shell 线程优先级更高的就绪线程存在时,shell 线程将持有信号量并运行,从 UART buffer 缓冲区中获取输入的数据。
+
+> [!NOTE]
+> 注:中断与线程间的互斥不能采用信号量(锁)的方式,而应采用开关中断的方式。
+
+#### 资源计数
+
+信号量也可以认为是一个递增或递减的计数器,需要注意的是信号量的值非负。例如:初始化一个信号量的值为 5,则这个信号量可最大连续减少 5 次,直到计数器减为 0。资源计数适合于线程间工作处理速度不匹配的场合,这个时候信号量可以做为前一线程工作完成个数的计数,而当调度到后一线程时,它也可以以一种连续的方式一次处理多个事件。例如,生产者与消费者问题中,生产者可以对信号量进行多次释放,而后消费者被调度到时能够一次处理多个信号量资源。
+
+> [!NOTE]
+> 注:一般资源计数类型多是混合方式的线程间同步,因为对于单个的资源处理依然存在线程的多重访问,这就需要对一个单独的资源进行访问、处理,并进行锁方式的互斥操作。
+
+互斥量
+------
+
+互斥量又叫相互排斥的信号量,是一种特殊的二值信号量。互斥量类似于只有一个车位的停车场:当有一辆车进入的时候,将停车场大门锁住,其他车辆在外面等候。当里面的车出来时,将停车场大门打开,下一辆车才可以进入。
+
+### 互斥量工作机制
+
+互斥量和信号量不同的是:拥有互斥量的线程拥有互斥量的所有权,互斥量支持递归访问且能防止线程优先级翻转;并且互斥量只能由持有线程释放,而信号量则可以由任何线程释放。
+
+互斥量的状态只有两种,开锁或闭锁(两种状态值)。当有线程持有它时,互斥量处于闭锁状态,由这个线程获得它的所有权。相反,当这个线程释放它时,将对互斥量进行开锁,失去它的所有权。当一个线程持有互斥量时,其他线程将不能够对它进行开锁或持有它,持有该互斥量的线程也能够再次获得这个锁而不被挂起,如下图时所示。这个特性与一般的二值信号量有很大的不同:在信号量中,因为已经不存在实例,线程递归持有会发生主动挂起(最终形成死锁)。
+
+
+
+使用信号量会导致的另一个潜在问题是线程优先级翻转问题。所谓优先级翻转,即当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证。如下图所示:有优先级为 A、B 和 C 的三个线程,优先级 A> B > C。线程 A,B 处于挂起状态,等待某一事件触发,线程 C 正在运行,此时线程 C 开始使用某一共享资源 M。在使用过程中,线程 A 等待的事件到来,线程 A 转为就绪态,因为它比线程 C 优先级高,所以立即执行。但是当线程 A 要使用共享资源 M 时,由于其正在被线程 C 使用,因此线程 A 被挂起切换到线程 C 运行。如果此时线程 B 等待的事件到来,则线程 B 转为就绪态。由于线程 B 的优先级比线程 C 高,因此线程 B 开始运行,直到其运行完毕,线程 C 才开始运行。只有当线程 C 释放共享资源 M 后,线程 A 才得以执行。在这种情况下,优先级发生了翻转:线程 B 先于线程 A 运行。这样便不能保证高优先级线程的响应时间。
+
+
+
+在 RT-Thread 操作系统中,互斥量可以解决优先级翻转问题,实现的是优先级继承算法。优先级继承是通过在线程 A 尝试获取共享资源而被挂起的期间内,将线程 C 的优先级提升到线程 A 的优先级别,从而解决优先级翻转引起的问题。这样能够防止 C(间接地防止 A)被 B 抢占,如下图所示。优先级继承是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。
+
+
+
+> [!NOTE]
+> 注:在获得互斥量后,请尽快释放互斥量,并且在持有互斥量的过程中,不得再行更改持有互斥量线程的优先级。
+
+### 互斥量控制块
+
+在 RT-Thread 中,互斥量控制块是操作系统用于管理互斥量的一个数据结构,由结构体 struct rt_mutex 表示。另外一种 C 表达方式 rt_mutex_t,表示的是互斥量的句柄,在 C 语言中的实现是指互斥量控制块的指针。互斥量控制块结构的详细定义请见以下代码:
+
+```c
+struct rt_mutex
+ {
+ struct rt_ipc_object parent; /* 继承自 ipc_object 类 */
+
+ rt_uint16_t value; /* 互斥量的值 */
+ rt_uint8_t original_priority; /* 持有线程的原始优先级 */
+ rt_uint8_t hold; /* 持有线程的持有次数 */
+ struct rt_thread *owner; /* 当前拥有互斥量的线程 */
+ };
+ /* rt_mutext_t 为指向互斥量结构体的指针类型 */
+ typedef struct rt_mutex* rt_mutex_t;
+```
+
+rt_mutex 对象从 rt_ipc_object 中派生,由 IPC 容器所管理。
+
+### 互斥量的管理方式
+
+互斥量控制块中含有互斥相关的重要参数,在互斥量功能的实现中起到重要的作用。互斥量相关接口如下图所示,对一个互斥量的操作包含:创建 / 初始化互斥量、获取互斥量、释放互斥量、删除 / 脱离互斥量。
+
+
+
+#### 创建和删除互斥量
+
+创建一个互斥量时,内核首先创建一个互斥量控制块,然后完成对该控制块的初始化工作。创建互斥量使用下面的函数接口:
+
+```c
+rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);
+```
+
+可以调用 rt_mutex_create 函数创建一个互斥量,它的名字由 name 所指定。当调用这个函数时,系统将先从对象管理器中分配一个 mutex 对象,并初始化这个对象,然后初始化父类 IPC 对象以及与 mutex 相关的部分。互斥量的 flag 标志设置为 RT_IPC_FLAG_PRIO,表示在多个线程等待资源时,将由优先级高的线程优先获得资源。flag 设置为 RT_IPC_FLAG_FIFO,表示在多个线程等待资源时,将按照先来先得的顺序获得资源。下表描述了该函数的输入参数与返回值:
+
+ rt_mutex_create() 的输入参数和返回值
+
+|**参数** |**描述** |
+|------------|-------------------------------------------------------------------|
+| name | 互斥量的名称 |
+| flag | 互斥量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回** | —— |
+| 互斥量句柄 | 创建成功 |
+| RT_NULL | 创建失败 |
+
+当不再使用互斥量时,通过删除互斥量以释放系统资源,适用于动态创建的互斥量。删除互斥量使用下面的函数接口:
+
+```c
+rt_err_t rt_mutex_delete (rt_mutex_t mutex);
+```
+
+当删除一个互斥量时,所有等待此互斥量的线程都将被唤醒,等待线程获得的返回值是 -
+RT_ERROR。然后系统将该互斥量从内核对象管理器链表中删除并释放互斥量占用的内存空间。下表描述了该函数的输入参数与返回值:
+
+ rt_mutex_delete() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------|
+| mutex | 互斥量对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 删除成功 |
+
+#### 初始化和脱离互斥量
+
+静态互斥量对象的内存是在系统编译时由编译器分配的,一般放于读写数据段或未初始化数据段中。在使用这类静态互斥量对象前,需要先进行初始化。初始化互斥量使用下面的函数接口:
+
+```c
+rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);
+```
+
+使用该函数接口时,需指定互斥量对象的句柄(即指向互斥量控制块的指针),互斥量名称以及互斥量标志。互斥量标志可用上面创建互斥量函数里提到的标志。下表描述了该函数的输入参数与返回值:
+
+ rt_mutex_init() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|-------------------------------------------------------------------|
+| mutex | 互斥量对象的句柄,它由用户提供,并指向互斥量对象的内存块 |
+| name | 互斥量的名称 |
+| flag | 互斥量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回**| —— |
+| RT_EOK | 初始化成功 |
+
+脱离互斥量将把互斥量对象从内核对象管理器中脱离,适用于静态初始化的互斥量。脱离互斥量使用下面的函数接口:
+
+```c
+rt_err_t rt_mutex_detach (rt_mutex_t mutex);
+```
+
+使用该函数接口后,内核先唤醒所有挂在该互斥量上的线程(线程的返回值是 -RT_ERROR),然后系统将该互斥量从内核对象管理器中脱离。下表描述了该函数的输入参数与返回值:
+
+ rt_mutex_detach() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------|
+| mutex | 互斥量对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 获取互斥量
+
+线程获取了互斥量,那么线程就有了对该互斥量的所有权,即某一个时刻一个互斥量只能被一个线程持有。获取互斥量使用下面的函数接口:
+
+```c
+rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);
+```
+
+如果互斥量没有被其他线程控制,那么申请该互斥量的线程将成功获得该互斥量。如果互斥量已经被当前线程线程控制,则该互斥量的持有计数加
+1,当前线程也不会挂起等待。如果互斥量已经被其他线程占有,则当前线程在该互斥量上挂起等待,直到其他线程释放它或者等待时间超过指定的超时时间。下表描述了该函数的输入参数与返回值:
+
+rt_mutex_take() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|------------------|
+| mutex | 互斥量对象的句柄 |
+| time | 指定等待的时间 |
+|**返回** | —— |
+| RT_EOK | 成功获得互斥量 |
+| \-RT_ETIMEOUT | 超时 |
+| \-RT_ERROR | 获取失败 |
+
+#### 释放互斥量
+
+当线程完成互斥资源的访问后,应尽快释放它占据的互斥量,使得其他线程能及时获取该互斥量。释放互斥量使用下面的函数接口:
+
+```c
+rt_err_t rt_mutex_release(rt_mutex_t mutex);
+```
+
+使用该函数接口时,只有已经拥有互斥量控制权的线程才能释放它,每释放一次该互斥量,它的持有计数就减
+1。当该互斥量的持有计数为零时(即持有线程已经释放所有的持有操作),它变为可用,等待在该信号量上的线程将被唤醒。如果线程的运行优先级被互斥量提升,那么当互斥量被释放后,线程恢复为持有互斥量前的优先级。下表描述了该函数的输入参数与返回值:
+
+rt_mutex_release() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------|
+| mutex | 互斥量对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+### 互斥量应用示例
+
+这是一个互斥量的应用例程,互斥锁是一种保护共享资源的方法。当一个线程拥有互斥锁的时候,可以保护共享资源不被其他线程破坏。下面用一个例子来说明,有两个线程:线程 1 和线程 2,线程 1 对 2 个 number 分别进行加 1 操作;线程 2 也对 2 个 number 分别进行加 1 操作,使用互斥量保证线程改变 2 个 number 值的操作不被打断。如下代码所示:
+
+互斥量例程
+
+```c
+#include
+
+#define THREAD_PRIORITY 8
+#define THREAD_TIMESLICE 5
+
+/* 指向互斥量的指针 */
+static rt_mutex_t dynamic_mutex = RT_NULL;
+static rt_uint8_t number1,number2 = 0;
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread1_stack[1024];
+static struct rt_thread thread1;
+static void rt_thread_entry1(void *parameter)
+{
+ while(1)
+ {
+ /* 线程 1 获取到互斥量后,先后对 number1、number2 进行加 1 操作,然后释放互斥量 */
+ rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
+ number1++;
+ rt_thread_mdelay(10);
+ number2++;
+ rt_mutex_release(dynamic_mutex);
+ }
+}
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread2_stack[1024];
+static struct rt_thread thread2;
+static void rt_thread_entry2(void *parameter)
+{
+ while(1)
+ {
+ /* 线程 2 获取到互斥量后,检查 number1、number2 的值是否相同,相同则表示 mutex 起到了锁的作用 */
+ rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
+ if(number1 != number2)
+ {
+ rt_kprintf("not protect.number1 = %d, mumber2 = %d \n",number1 ,number2);
+ }
+ else
+ {
+ rt_kprintf("mutex protect ,number1 = mumber2 is %d\n",number1);
+ }
+
+ number1++;
+ number2++;
+ rt_mutex_release(dynamic_mutex);
+
+ if(number1>=50)
+ return;
+ }
+}
+
+/* 互斥量示例的初始化 */
+int mutex_sample(void)
+{
+ /* 创建一个动态互斥量 */
+ dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO);
+ if (dynamic_mutex == RT_NULL)
+ {
+ rt_kprintf("create dynamic mutex failed.\n");
+ return -1;
+ }
+
+ rt_thread_init(&thread1,
+ "thread1",
+ rt_thread_entry1,
+ RT_NULL,
+ &thread1_stack[0],
+ sizeof(thread1_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread1);
+
+ rt_thread_init(&thread2,
+ "thread2",
+ rt_thread_entry2,
+ RT_NULL,
+ &thread2_stack[0],
+ sizeof(thread2_stack),
+ THREAD_PRIORITY-1, THREAD_TIMESLICE);
+ rt_thread_startup(&thread2);
+ return 0;
+}
+
+/* 导出到 MSH 命令列表中 */
+MSH_CMD_EXPORT(mutex_sample, mutex sample);
+```
+
+线程 1 与线程 2 中均使用互斥量保护对 2 个 number 的操作(倘若将线程 1 中的获取、释放互斥量语句注释掉,线程 1 将对 number 不再做保护),仿真运行结果如下:
+
+```
+\ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 24 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >mutex_sample
+msh >mutex protect ,number1 = mumber2 is 1
+mutex protect ,number1 = mumber2 is 2
+mutex protect ,number1 = mumber2 is 3
+mutex protect ,number1 = mumber2 is 4
+…
+mutex protect ,number1 = mumber2 is 48
+mutex protect ,number1 = mumber2 is 49
+```
+
+线程使用互斥量保护对两个 number 的操作,使 number 值保持一致。
+
+互斥量的另一个例子见下面的代码,这个例子将创建 3 个动态线程以检查持有互斥量时,持有的线程优先级是否被调整到等待线程优先级中的最高优先级。
+
+防止优先级翻转特性例程
+
+```c
+#include
+
+/* 指向线程控制块的指针 */
+static rt_thread_t tid1 = RT_NULL;
+static rt_thread_t tid2 = RT_NULL;
+static rt_thread_t tid3 = RT_NULL;
+static rt_mutex_t mutex = RT_NULL;
+
+
+#define THREAD_PRIORITY 10
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+
+/* 线程 1 入口 */
+static void thread1_entry(void *parameter)
+{
+ /* 先让低优先级线程运行 */
+ rt_thread_mdelay(100);
+
+ /* 此时 thread3 持有 mutex,并且 thread2 等待持有 mutex */
+
+ /* 检查 thread2 与 thread3 的优先级情况 */
+ if (tid2->current_priority != tid3->current_priority)
+ {
+ /* 优先级不相同,测试失败 */
+ rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
+ rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
+ rt_kprintf("test failed.\n");
+ return;
+ }
+ else
+ {
+ rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
+ rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
+ rt_kprintf("test OK.\n");
+ }
+}
+
+/* 线程 2 入口 */
+static void thread2_entry(void *parameter)
+{
+ rt_err_t result;
+
+ rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
+
+ /* 先让低优先级线程运行 */
+ rt_thread_mdelay(50);
+
+ /*
+ * 试图持有互斥锁,此时 thread3 持有,应把 thread3 的优先级提升
+ * 到 thread2 相同的优先级
+ */
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+
+ if (result == RT_EOK)
+ {
+ /* 释放互斥锁 */
+ rt_mutex_release(mutex);
+ }
+}
+
+/* 线程 3 入口 */
+static void thread3_entry(void *parameter)
+{
+ rt_tick_t tick;
+ rt_err_t result;
+
+ rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
+
+ result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("thread3 take a mutex, failed.\n");
+ }
+
+ /* 做一个长时间的循环,500ms */
+ tick = rt_tick_get();
+ while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2)) ;
+
+ rt_mutex_release(mutex);
+}
+
+int pri_inversion(void)
+{
+ /* 创建互斥锁 */
+ mutex = rt_mutex_create("mutex", RT_IPC_FLAG_FIFO);
+ if (mutex == RT_NULL)
+ {
+ rt_kprintf("create dynamic mutex failed.\n");
+ return -1;
+ }
+
+ /* 创建线程 1 */
+ tid1 = rt_thread_create("thread1",
+ thread1_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY - 1, THREAD_TIMESLICE);
+ if (tid1 != RT_NULL)
+ rt_thread_startup(tid1);
+
+ /* 创建线程 2 */
+ tid2 = rt_thread_create("thread2",
+ thread2_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ if (tid2 != RT_NULL)
+ rt_thread_startup(tid2);
+
+ /* 创建线程 3 */
+ tid3 = rt_thread_create("thread3",
+ thread3_entry,
+ RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY + 1, THREAD_TIMESLICE);
+ if (tid3 != RT_NULL)
+ rt_thread_startup(tid3);
+
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(pri_inversion, prio_inversion sample);
+```
+
+仿真运行结果如下:
+
+```
+\ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 27 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >pri_inversion
+the priority of thread2 is: 10
+the priority of thread3 is: 11
+the priority of thread2 is: 10
+the priority of thread3 is: 10
+test OK.
+```
+
+例程演示了互斥量的使用方法。线程 3 先持有互斥量,而后线程 2 试图持有互斥量,此时线程 3 的优先级被提升为和线程 2 的优先级相同。
+
+> [!NOTE]
+> 注:需要切记的是互斥量不能在中断服务例程中使用。
+
+### 互斥量的使用场合
+
+互斥量的使用比较单一,因为它是信号量的一种,并且它是以锁的形式存在。在初始化的时候,互斥量永远都处于开锁的状态,而被线程持有的时候则立刻转为闭锁的状态。互斥量更适合于:
+
+(1)线程多次持有互斥量的情况下。这样可以避免同一线程多次递归持有而造成死锁的问题。
+
+(2)可能会由于多线程同步而造成优先级翻转的情况。
+
+事件集
+------
+
+事件集也是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。下面以坐公交为例说明事件,在公交站等公交时可能有以下几种情况:
+
+①P1 坐公交去某地,只有一种公交可以到达目的地,等到此公交即可出发。
+
+②P1 坐公交去某地,有 3 种公交都可以到达目的地,等到其中任意一辆即可出发。
+
+③P1 约另一人 P2 一起去某地,则 P1 必须要等到 “同伴 P2 到达公交站” 与“公交到达公交站”两个条件都满足后,才能出发。
+
+这里,可以将 P1 去某地视为线程,将 “公交到达公交站”、“同伴 P2 到达公交站” 视为事件的发生,情况①是特定事件唤醒线程;情况②是任意单个事件唤醒线程;情况③是多个事件同时发生才唤醒线程。
+
+### 事件集工作机制
+
+事件集主要用于线程间的同步,与信号量不同,它的特点是可以实现一对多,多对多的同步。即一个线程与多个事件的关系可设置为:其中任意一个事件唤醒线程,或几个事件都到达后才唤醒线程进行后续的处理;同样,事件也可以是多个线程同步多个事件。这种多个事件的集合可以用一个 32 位无符号整型变量来表示,变量的每一位代表一个事件,线程通过 “逻辑与” 或“逻辑或”将一个或多个事件关联起来,形成事件组合。事件的 “逻辑或” 也称为是独立型同步,指的是线程与任何事件之一发生同步;事件 “逻辑与” 也称为是关联型同步,指的是线程与若干事件都发生同步。
+
+RT-Thread 定义的事件集有以下特点:
+
+1)事件只与线程相关,事件间相互独立:每个线程可拥有 32 个事件标志,采用一个 32 bit 无符号整型数进行记录,每一个 bit 代表一个事件;
+
+2)事件仅用于同步,不提供数据传输功能;
+
+3)事件无排队性,即多次向线程发送同一事件 (如果线程还未来得及读走),其效果等同于只发送一次。
+
+在 RT-Thread 中,每个线程都拥有一个事件信息标记,它有三个属性,分别是 RT_EVENT_FLAG_AND(逻辑与),RT_EVENT_FLAG_OR(逻辑或)以及 RT_EVENT_FLAG_CLEAR(清除标记)。当线程等待事件同步时,可以通过 32 个事件标志和这个事件信息标记来判断当前接收的事件是否满足同步条件。
+
+
+
+如上图所示,线程 \#1 的事件标志中第 1 位和第 30 位被置位,如果事件信息标记位设为逻辑与,则表示线程 \#1 只有在事件 1 和事件 30 都发生以后才会被触发唤醒,如果事件信息标记位设为逻辑或,则事件 1 或事件 30 中的任意一个发生都会触发唤醒线程 \#1。如果信息标记同时设置了清除标记位,则当线程 \#1 唤醒后将主动把事件 1 和事件 30 清为零,否则事件标志将依然存在(即置 1)。
+
+### 事件集控制块
+
+在 RT-Thread 中,事件集控制块是操作系统用于管理事件的一个数据结构,由结构体 struct rt_event 表示。另外一种 C 表达方式 rt_event_t,表示的是事件集的句柄,在 C 语言中的实现是事件集控制块的指针。事件集控制块结构的详细定义请见以下代码:
+
+```c
+struct rt_event
+{
+ struct rt_ipc_object parent; /* 继承自 ipc_object 类 */
+
+ /* 事件集合,每一 bit 表示 1 个事件,bit 位的值可以标记某事件是否发生 */
+ rt_uint32_t set;
+};
+/* rt_event_t 是指向事件结构体的指针类型 */
+typedef struct rt_event* rt_event_t;
+```
+
+rt_event 对象从 rt_ipc_object 中派生,由 IPC 容器所管理。
+
+### 事件集的管理方式
+
+事件集控制块中含有与事件集相关的重要参数,在事件集功能的实现中起重要的作用。事件集相关接口如下图所示,对一个事件集的操作包含:创建 / 初始化事件集、发送事件、接收事件、删除 / 脱离事件集。
+
+
+
+#### 创建和删除事件集
+
+当创建一个事件集时,内核首先创建一个事件集控制块,然后对该事件集控制块进行基本的初始化,创建事件集使用下面的函数接口:
+
+```c
+rt_event_t rt_event_create(const char* name, rt_uint8_t flag);
+```
+
+调用该函数接口时,系统会从对象管理器中分配事件集对象,并初始化这个对象,然后初始化父类 IPC 对象。下表描述了该函数的输入参数与返回值:
+
+ rt_event_create() 的输入参数和返回值
+
+|**参数** |**描述** |
+|----------------|---------------------------------------------------------------------|
+| name | 事件集的名称 |
+| flag | 事件集的标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回** | —— |
+| RT_NULL | 创建失败 |
+| 事件对象的句柄 | 创建成功 |
+
+系统不再使用 rt_event_create() 创建的事件集对象时,通过删除事件集对象控制块来释放系统资源。删除事件集可以使用下面的函数接口:
+
+```c
+rt_err_t rt_event_delete(rt_event_t event);
+```
+
+在调用 rt_event_delete 函数删除一个事件集对象时,应该确保该事件集不再被使用。在删除前会唤醒所有挂起在该事件集上的线程(线程的返回值是 - RT_ERROR),然后释放事件集对象占用的内存块。下表描述了该函数的输入参数与返回值:
+
+rt_event_delete() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------|
+| event | 事件集对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 初始化和脱离事件集
+
+静态事件集对象的内存是在系统编译时由编译器分配的,一般放于读写数据段或未初始化数据段中。在使用静态事件集对象前,需要先行对它进行初始化操作。初始化事件集使用下面的函数接口:
+
+```c
+rt_err_t rt_event_init(rt_event_t event, const char* name, rt_uint8_t flag);
+```
+
+调用该接口时,需指定静态事件集对象的句柄(即指向事件集控制块的指针),然后系统会初始化事件集对象,并加入到系统对象容器中进行管理。下表描述了该函数的输入参数与返回值:
+
+ rt_event_init() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|---------------------------------------------------------------------|
+| event | 事件集对象的句柄 |
+| name | 事件集的名称 |
+| flag | 事件集的标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+系统不再使用 rt_event_init() 初始化的事件集对象时,通过脱离事件集对象控制块来释放系统资源。脱离事件集是将事件集对象从内核对象管理器中脱离。脱离事件集使用下面的函数接口:
+
+```c
+rt_err_t rt_event_detach(rt_event_t event);
+```
+
+用户调用这个函数时,系统首先唤醒所有挂在该事件集等待队列上的线程(线程的返回值是 - RT_ERROR),然后将该事件集从内核对象管理器中脱离。下表描述了该函数的输入参数与返回值:
+
+rt_event_detach() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------|
+| event | 事件集对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 发送事件
+
+发送事件函数可以发送事件集中的一个或多个事件,如下:
+
+```c
+rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
+```
+
+使用该函数接口时,通过参数 set 指定的事件标志来设定 event 事件集对象的事件标志值,然后遍历等待在 event 事件集对象上的等待线程链表,判断是否有线程的事件激活要求与当前 event 对象事件标志值匹配,如果有,则唤醒该线程。下表描述了该函数的输入参数与返回值:
+
+rt_event_send() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------------------------|
+| event | 事件集对象的句柄 |
+| set | 发送的一个或多个事件的标志值 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 接收事件
+
+内核使用 32 位的无符号整数来标识事件集,它的每一位代表一个事件,因此一个事件集对象可同时等待接收 32 个事件,内核可以通过指定选择参数 “逻辑与” 或“逻辑或”来选择如何激活线程,使用 “逻辑与” 参数表示只有当所有等待的事件都发生时才激活线程,而使用 “逻辑或” 参数则表示只要有一个等待的事件发生就激活线程。接收事件使用下面的函数接口:
+
+```c
+rt_err_t rt_event_recv(rt_event_t event,
+ rt_uint32_t set,
+ rt_uint8_t option,
+ rt_int32_t timeout,
+ rt_uint32_t* recved);
+```
+
+当用户调用这个接口时,系统首先根据 set 参数和接收选项 option 来判断它要接收的事件是否发生,如果已经发生,则根据参数 option 上是否设置有 RT_EVENT_FLAG_CLEAR 来决定是否重置事件的相应标志位,然后返回(其中 recved 参数返回接收到的事件);如果没有发生,则把等待的 set 和 option 参数填入线程本身的结构中,然后把线程挂起在此事件上,直到其等待的事件满足条件或等待时间超过指定的超时时间。如果超时时间设置为零,则表示当线程要接受的事件没有满足其要求时就不等待,而直接返回 - RT_ETIMEOUT。下表描述了该函数的输入参数与返回值:
+
+ rt_event_recv() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|----------------------|
+| event | 事件集对象的句柄 |
+| set | 接收线程感兴趣的事件 |
+| option | 接收选项 |
+| timeout | 指定超时时间 |
+| recved | 指向接收到的事件 |
+|**返回** | —— |
+| RT_EOK | 成功 |
+| \-RT_ETIMEOUT | 超时 |
+| \-RT_ERROR | 错误 |
+
+option 的值可取:
+
+```c
+/* 选择 逻辑与 或 逻辑或 的方式接收事件 */
+RT_EVENT_FLAG_OR
+RT_EVENT_FLAG_AND
+
+/* 选择清除重置事件标志位 */
+RT_EVENT_FLAG_CLEAR
+```
+
+### 事件集应用示例
+
+这是事件集的应用例程,例子中初始化了一个事件集,两个线程。一个线程等待自己关心的事件发生,另外一个线程发送事件,如代码清单 6-5 例所示:
+
+事件集的使用例程
+
+```c
+#include
+
+#define THREAD_PRIORITY 9
+#define THREAD_TIMESLICE 5
+
+#define EVENT_FLAG3 (1 << 3)
+#define EVENT_FLAG5 (1 << 5)
+
+/* 事件控制块 */
+static struct rt_event event;
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread1_stack[1024];
+static struct rt_thread thread1;
+
+/* 线程 1 入口函数 */
+static void thread1_recv_event(void *param)
+{
+ rt_uint32_t e;
+
+ /* 第一次接收事件,事件 3 或事件 5 任意一个可以触发线程 1,接收完后清除事件标志 */
+ if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),
+ RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER, &e) == RT_EOK)
+ {
+ rt_kprintf("thread1: OR recv event 0x%x\n", e);
+ }
+
+ rt_kprintf("thread1: delay 1s to prepare the second event\n");
+ rt_thread_mdelay(1000);
+
+ /* 第二次接收事件,事件 3 和事件 5 均发生时才可以触发线程 1,接收完后清除事件标志 */
+ if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),
+ RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER, &e) == RT_EOK)
+ {
+ rt_kprintf("thread1: AND recv event 0x%x\n", e);
+ }
+ rt_kprintf("thread1 leave.\n");
+}
+
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread2_stack[1024];
+static struct rt_thread thread2;
+
+/* 线程 2 入口 */
+static void thread2_send_event(void *param)
+{
+ rt_kprintf("thread2: send event3\n");
+ rt_event_send(&event, EVENT_FLAG3);
+ rt_thread_mdelay(200);
+
+ rt_kprintf("thread2: send event5\n");
+ rt_event_send(&event, EVENT_FLAG5);
+ rt_thread_mdelay(200);
+
+ rt_kprintf("thread2: send event3\n");
+ rt_event_send(&event, EVENT_FLAG3);
+ rt_kprintf("thread2 leave.\n");
+}
+
+int event_sample(void)
+{
+ rt_err_t result;
+
+ /* 初始化事件对象 */
+ result = rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("init event failed.\n");
+ return -1;
+ }
+
+ rt_thread_init(&thread1,
+ "thread1",
+ thread1_recv_event,
+ RT_NULL,
+ &thread1_stack[0],
+ sizeof(thread1_stack),
+ THREAD_PRIORITY - 1, THREAD_TIMESLICE);
+ rt_thread_startup(&thread1);
+
+ rt_thread_init(&thread2,
+ "thread2",
+ thread2_send_event,
+ RT_NULL,
+ &thread2_stack[0],
+ sizeof(thread2_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread2);
+
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(event_sample, event sample);
+```
+
+仿真运行结果如下:
+
+```c
+ \ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 24 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >event_sample
+thread2: send event3
+thread1: OR recv event 0x8
+thread1: delay 1s to prepare the second event
+msh >thread2: send event5
+thread2: send event3
+thread2 leave.
+thread1: AND recv event 0x28
+thread1 leave.
+```
+
+例程演示了事件集的使用方法。线程 1 前后两次接收事件,分别使用了 “逻辑或” 与“逻辑与”的方法。
+
+### 事件集的使用场合
+
+事件集可使用于多种场合,它能够在一定程度上替代信号量,用于线程间同步。一个线程或中断服务例程发送一个事件给事件集对象,而后等待的线程被唤醒并对相应的事件进行处理。但是它与信号量不同的是,事件的发送操作在事件未清除前,是不可累计的,而信号量的释放动作是累计的。事件的另一个特性是,接收线程可等待多种事件,即多个事件对应一个线程或多个线程。同时按照线程等待的参数,可选择是 “逻辑或” 触发还是 “逻辑与” 触发。这个特性也是信号量等所不具备的,信号量只能识别单一的释放动作,而不能同时等待多种类型的释放。如下图所示为多事件接收示意图:
+
+
+
+一个事件集中包含 32 个事件,特定线程只等待、接收它关注的事件。可以是一个线程等待多个事件的到来(线程 1、2 均等待多个事件,事件间可以使用 “与” 或者 “或” 逻辑触发线程),也可以是多个线程等待一个事件的到来(事件 25)。当有它们关注的事件发生时,线程将被唤醒并进行后续的处理动作。
+
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07mb_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07mb_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..26291ede7b0e01750f4d518986821019f208130c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07mb_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07mb_work.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07mb_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..150ac857635c21abce2c268a57fc44ee4292b92e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07mb_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_ops.jpg b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_ops.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..06988e38f7f2970e974040a9a531d60dc136255d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_ops.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac5969ad91d3fbdc7433c915c4e62e6fbf874dca
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_syn.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_syn.png
new file mode 100644
index 0000000000000000000000000000000000000000..6aa3e511aceb3e30029b48fbc73acf9420c699e6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_syn.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_work.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd3ef44310c7a248777d192f24517a5c7a499f69
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07msg_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07signal_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07signal_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8f466cf9b7cbd4db6174b5b46807cee1643c920
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07signal_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07signal_work.png b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07signal_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..49be0f6fe845806dac5ef297330ff4b220aca57b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07signal_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md
new file mode 100644
index 0000000000000000000000000000000000000000..824f4a942273677fd67b10a8b41a2742169d5f36
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md
@@ -0,0 +1,1068 @@
+# 线程间通信
+
+前面一章讲了线程间同步,提到了信号量、互斥量、事件集等概念;本章接着上一章的内容,讲解线程间通信。在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取,根据读取到的全局变量值执行相应的动作,达到通信协作的目的。RT-Thread 中则提供了更多的工具帮助在不同的线程中间传递信息,本章会详细介绍这些工具。学习完本章,大家将学会如何将邮箱、消息队列、信号用于线程间的通信。
+
+## 邮箱
+
+邮箱服务是实时操作系统中一种典型的线程间通信方法。举一个简单的例子,有两个线程,线程 1 检测按键状态并发送,线程 2 读取按键状态并根据按键的状态相应地改变 LED 的亮灭。这里就可以使用邮箱的方式进行通信,线程 1 将按键的状态作为邮件发送到邮箱,线程 2 在邮箱中读取邮件获得按键状态并对 LED 执行亮灭操作。
+
+这里的线程 1 也可以扩展为多个线程。例如,共有三个线程,线程 1 检测并发送按键状态,线程 2 检测并发送 ADC 采样信息,线程 3 则根据接收的信息类型不同,执行不同的操作。
+
+### 邮箱的工作机制
+
+RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针的大小即为 4 个字节,所以一封邮件恰好能够容纳一个指针)。典型的邮箱也称作交换消息,如下图所示,线程或中断服务例程把一封 4 字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。
+
+
+
+非阻塞方式的邮件发送过程能够安全的应用于中断服务中,是线程、中断服务、定时器向线程发送消息的有效手段。通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为 0 时,邮件收取过程将变成阻塞方式。在这类情况下,只能由线程进行邮件的收取。
+
+当一个线程向邮箱发送邮件时,如果邮箱没满,将把邮件复制到邮箱中。如果邮箱已经满了,发送线程可以设置超时时间,选择等待挂起或直接返回 - RT_EFULL。如果发送线程选择挂起等待,那么当邮箱中的邮件被收取而空出空间来时,等待挂起的发送线程将被唤醒继续发送。
+
+当一个线程从邮箱中接收邮件时,如果邮箱是空的,接收线程可以选择是否等待挂起直到收到新的邮件而唤醒,或可以设置超时时间。当达到设置的超时时间,邮箱依然未收到邮件时,这个选择超时等待的线程将被唤醒并返回 - RT_ETIMEOUT。如果邮箱中存在邮件,那么接收线程将复制邮箱中的 4 个字节邮件到接收缓存中。
+
+### 邮箱控制块
+
+在 RT-Thread 中,邮箱控制块是操作系统用于管理邮箱的一个数据结构,由结构体 struct rt_mailbox 表示。另外一种 C 表达方式 rt_mailbox_t,表示的是邮箱的句柄,在 C 语言中的实现是邮箱控制块的指针。邮箱控制块结构的详细定义请见以下代码:
+
+```c
+struct rt_mailbox
+{
+ struct rt_ipc_object parent;
+
+ rt_uint32_t* msg_pool; /* 邮箱缓冲区的开始地址 */
+ rt_uint16_t size; /* 邮箱缓冲区的大小 */
+
+ rt_uint16_t entry; /* 邮箱中邮件的数目 */
+ rt_uint16_t in_offset, out_offset; /* 邮箱缓冲的进出指针 */
+ rt_list_t suspend_sender_thread; /* 发送线程的挂起等待队列 */
+};
+typedef struct rt_mailbox* rt_mailbox_t;
+```
+
+rt_mailbox 对象从 rt_ipc_object 中派生,由 IPC 容器所管理。
+
+### 邮箱的管理方式
+
+邮箱控制块是一个结构体,其中含有事件相关的重要参数,在邮箱的功能实现中起重要的作用。邮箱的相关接口如下图所示,对一个邮箱的操作包含:创建 / 初始化邮箱、发送邮件、接收邮件、删除 / 脱离邮箱。
+
+
+
+#### 创建和删除邮箱
+
+动态创建一个邮箱对象可以调用如下的函数接口:
+
+```c
+rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);
+```
+
+创建邮箱对象时会先从对象管理器中分配一个邮箱对象,然后给邮箱动态分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4 字节)与邮箱容量的乘积,接着初始化接收邮件数目和发送邮件在邮箱中的偏移量。下表描述了该函数的输入参数与返回值:
+
+ rt_mb_create() 的输入参数和返回值
+
+|**参数** |**描述** |
+|----------------|------------------------------------------------------------------|
+| name | 邮箱名称 |
+| size | 邮箱容量 |
+| flag | 邮箱标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回** | —— |
+| RT_NULL | 创建失败 |
+| 邮箱对象的句柄 | 创建成功 |
+
+当用 rt_mb_create() 创建的邮箱不再被使用时,应该删除它来释放相应的系统资源,一旦操作完成,邮箱将被永久性的删除。删除邮箱的函数接口如下:
+
+```c
+rt_err_t rt_mb_delete (rt_mailbox_t mb);
+```
+
+删除邮箱时,如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是 -
+RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象。下表描述了该函数的输入参数与返回值:
+
+ rt_mb_delete() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|----------------|
+| mb | 邮箱对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 初始化和脱离邮箱
+
+初始化邮箱跟创建邮箱类似,只是初始化邮箱用于静态邮箱对象的初始化。与创建邮箱不同的是,静态邮箱对象的内存是在系统编译时由编译器分配的,一般放于读写数据段或未初始化数据段中,其余的初始化工作与创建邮箱时相同。函数接口如下:
+
+```c
+ rt_err_t rt_mb_init(rt_mailbox_t mb,
+ const char* name,
+ void* msgpool,
+ rt_size_t size,
+ rt_uint8_t flag)
+```
+
+初始化邮箱时,该函数接口需要获得用户已经申请获得的邮箱对象控制块,缓冲区的指针,以及邮箱名称和邮箱容量(能够存储的邮件数)。下表描述了该函数的输入参数与返回值:
+
+ rt_mb_init() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|-----------------------------------------------------------------|
+| mb | 邮箱对象的句柄 |
+| name | 邮箱名称 |
+| msgpool | 缓冲区指针 |
+| size | 邮箱容量 |
+| flag | 邮箱标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+这里的 size 参数指定的是邮箱的容量,即如果 msgpool 指向的缓冲区的字节数是 N,那么邮箱容量应该是 N/4。
+
+脱离邮箱将把静态初始化的邮箱对象从内核对象管理器中脱离。脱离邮箱使用下面的接口:
+
+```c
+rt_err_t rt_mb_detach(rt_mailbox_t mb);
+```
+
+使用该函数接口后,内核先唤醒所有挂在该邮箱上的线程(线程获得返回值是 - RT_ERROR),然后将该邮箱对象从内核对象管理器中脱离。下表描述了该函数的输入参数与返回值:
+
+ rt_mb_detach() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|----------------|
+| mb | 邮箱对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 发送邮件
+
+线程或者中断服务程序可以通过邮箱给其他线程发送邮件,发送邮件函数接口如下:
+
+```c
+rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);
+```
+
+发送的邮件可以是 32 位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 -RT_EFULL 的返回值。下表描述了该函数的输入参数与返回值:
+
+ rt_mb_send() 的输入参数和返回值
+
+|**参数** |**描述** |
+|------------|----------------|
+| mb | 邮箱对象的句柄 |
+| value | 邮件内容 |
+|**返回** | —— |
+| RT_EOK | 发送成功 |
+| \-RT_EFULL | 邮箱已经满了 |
+
+#### 等待方式发送邮件
+
+用户也可以通过如下的函数接口向指定邮箱发送邮件:
+
+```c
+rt_err_t rt_mb_send_wait (rt_mailbox_t mb,
+ rt_uint32_t value,
+ rt_int32_t timeout);
+```
+
+rt_mb_send_wait() 与 rt_mb_send() 的区别在于有等待时间,如果邮箱已经满了,那么发送线程将根据设定的 timeout 参数等待邮箱中因为收取邮件而空出空间。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。下表描述了该函数的输入参数与返回值:
+
+ rt_mb_send_wait() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|----------------|
+| mb | 邮箱对象的句柄 |
+| value | 邮件内容 |
+| timeout | 超时时间 |
+|**返回** | —— |
+| RT_EOK | 发送成功 |
+| \-RT_ETIMEOUT | 超时 |
+| \-RT_ERROR | 失败,返回错误 |
+
+#### 接收邮件
+
+只有当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回 RT_EOK 的返回值,否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。接收邮件函数接口如下:
+
+```c
+rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);
+```
+
+接收邮件时,接收者需指定接收邮件的邮箱句柄,并指定接收到的邮件存放位置以及最多能够等待的超时时间。如果接收时设定了超时,当指定的时间内依然未收到邮件时,将返回 - RT_ETIMEOUT。下表描述了该函数的输入参数与返回值:
+
+ rt_mb_recv() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|----------------|
+| mb | 邮箱对象的句柄 |
+| value | 邮件内容 |
+| timeout | 超时时间 |
+|**返回** | —— |
+| RT_EOK | 发送成功 |
+| \-RT_ETIMEOUT | 超时 |
+| \-RT_ERROR | 失败,返回错误 |
+
+### 邮箱使用示例
+
+这是一个邮箱的应用例程,初始化 2 个静态线程,一个静态的邮箱对象,其中一个线程往邮箱中发送邮件,一个线程往邮箱中收取邮件。如下代码所示:
+
+ 邮箱的使用例程
+
+```c
+#include
+
+#define THREAD_PRIORITY 10
+#define THREAD_TIMESLICE 5
+
+/* 邮箱控制块 */
+static struct rt_mailbox mb;
+/* 用于放邮件的内存池 */
+static char mb_pool[128];
+
+static char mb_str1[] = "I'm a mail!";
+static char mb_str2[] = "this is another mail!";
+static char mb_str3[] = "over";
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread1_stack[1024];
+static struct rt_thread thread1;
+
+/* 线程 1 入口 */
+static void thread1_entry(void *parameter)
+{
+ char *str;
+
+ while (1)
+ {
+ rt_kprintf("thread1: try to recv a mail\n");
+
+ /* 从邮箱中收取邮件 */
+ if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK)
+ {
+ rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str);
+ if (str == mb_str3)
+ break;
+
+ /* 延时 100ms */
+ rt_thread_mdelay(100);
+ }
+ }
+ /* 执行邮箱对象脱离 */
+ rt_mb_detach(&mb);
+}
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread2_stack[1024];
+static struct rt_thread thread2;
+
+/* 线程 2 入口 */
+static void thread2_entry(void *parameter)
+{
+ rt_uint8_t count;
+
+ count = 0;
+ while (count < 10)
+ {
+ count ++;
+ if (count & 0x1)
+ {
+ /* 发送 mb_str1 地址到邮箱中 */
+ rt_mb_send(&mb, (rt_uint32_t)&mb_str1);
+ }
+ else
+ {
+ /* 发送 mb_str2 地址到邮箱中 */
+ rt_mb_send(&mb, (rt_uint32_t)&mb_str2);
+ }
+
+ /* 延时 200ms */
+ rt_thread_mdelay(200);
+ }
+
+ /* 发送邮件告诉线程 1,线程 2 已经运行结束 */
+ rt_mb_send(&mb, (rt_uint32_t)&mb_str3);
+}
+
+int mailbox_sample(void)
+{
+ rt_err_t result;
+
+ /* 初始化一个 mailbox */
+ result = rt_mb_init(&mb,
+ "mbt", /* 名称是 mbt */
+ &mb_pool[0], /* 邮箱用到的内存池是 mb_pool */
+ sizeof(mb_pool) / 4, /* 邮箱中的邮件数目,因为一封邮件占 4 字节 */
+ RT_IPC_FLAG_FIFO); /* 采用 FIFO 方式进行线程等待 */
+ if (result != RT_EOK)
+ {
+ rt_kprintf("init mailbox failed.\n");
+ return -1;
+ }
+
+ rt_thread_init(&thread1,
+ "thread1",
+ thread1_entry,
+ RT_NULL,
+ &thread1_stack[0],
+ sizeof(thread1_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread1);
+
+ rt_thread_init(&thread2,
+ "thread2",
+ thread2_entry,
+ RT_NULL,
+ &thread2_stack[0],
+ sizeof(thread2_stack),
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ rt_thread_startup(&thread2);
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(mailbox_sample, mailbox sample);
+```
+
+仿真运行结果如下:
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 27 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >mailbox_sample
+thread1: try to recv a mail
+thread1: get a mail from mailbox, the content:I'm a mail!
+msh >thread1: try to recv a mail
+thread1: get a mail from mailbox, the content:this is another mail!
+…
+thread1: try to recv a mail
+thread1: get a mail from mailbox, the content:this is another mail!
+thread1: try to recv a mail
+thread1: get a mail from mailbox, the content:over
+```
+
+例程演示了邮箱的使用方法。线程 2 发送邮件,共发送 11 次;线程 1 接收邮件,共接收到 11 封邮件,将邮件内容打印出来,并判断结束。
+
+### 邮箱的使用场合
+
+邮箱是一种简单的线程间消息传递方式,特点是开销比较低,效率较高。在 RT-Thread 操作系统的实现中能够一次传递一个 4 字节大小的邮件,并且邮箱具备一定的存储功能,能够缓存一定数量的邮件数 (邮件数由创建、初始化邮箱时指定的容量决定)。邮箱中一封邮件的最大长度是 4 字节,所以邮箱能够用于不超过 4 字节的消息传递。由于在 32 系统上 4 字节的内容恰好可以放置一个指针,因此当需要在线程间传递比较大的消息时,可以把指向一个缓冲区的指针作为邮件发送到邮箱中,即邮箱也可以传递指针,例如:
+
+```c
+struct msg
+{
+ rt_uint8_t *data_ptr;
+ rt_uint32_t data_size;
+};
+```
+
+对于这样一个消息结构体,其中包含了指向数据的指针 data_ptr 和数据块长度的变量 data_size。当一个线程需要把这个消息发送给另外一个线程时,可以采用如下的操作:
+
+```c
+struct msg* msg_ptr;
+
+msg_ptr = (struct msg*)rt_malloc(sizeof(struct msg));
+msg_ptr->data_ptr = ...; /* 指向相应的数据块地址 */
+msg_ptr->data_size = len; /* 数据块的长度 */
+/* 发送这个消息指针给 mb 邮箱 */
+rt_mb_send(mb, (rt_uint32_t)msg_ptr);
+```
+
+而在接收线程中,因为收取过来的是指针,而 msg_ptr 是一个新分配出来的内存块,所以在接收线程处理完毕后,需要释放相应的内存块:
+
+```c
+struct msg* msg_ptr;
+if (rt_mb_recv(mb, (rt_uint32_t*)&msg_ptr) == RT_EOK)
+{
+ /* 在接收线程处理完毕后,需要释放相应的内存块 */
+ rt_free(msg_ptr);
+}
+```
+
+消息队列
+--------
+
+消息队列是另一种常用的线程间通讯方式,是邮箱的扩展。可以应用在多种场合:线程间的消息交换、使用串口接收不定长数据等。
+
+### 消息队列的工作机制
+
+消息队列能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。消息队列是一种异步的通信方式。
+
+如下图所示,线程或中断服务例程可以将一条或多条消息放入消息队列中。同样,一个或多个线程也可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常将先进入消息队列的消息先传给线程,也就是说,线程先得到的是最先进入消息队列的消息,即先进先出原则 (FIFO)。
+
+
+
+RT-Thread 操作系统的消息队列对象由多个元素组成,当消息队列被创建时,它就被分配了消息队列控制块:消息队列名称、内存缓冲区、消息大小以及队列长度等。同时每个消息队列对象中包含着多个消息框,每个消息框可以存放一条消息;消息队列中的第一个和最后一个消息框被分别称为消息链表头和消息链表尾,对应于消息队列控制块中的 msg_queue_head 和 msg_queue_tail;有些消息框可能是空的,它们通过 msg_queue_free 形成一个空闲消息框链表。所有消息队列中的消息框总数即是消息队列的长度,这个长度可在消息队列创建时指定。
+
+### 消息队列控制块
+
+在 RT-Thread 中,消息队列控制块是操作系统用于管理消息队列的一个数据结构,由结构体 struct rt_messagequeue 表示。另外一种 C 表达方式 rt_mq_t,表示的是消息队列的句柄,在 C 语言中的实现是消息队列控制块的指针。消息队列控制块结构的详细定义请见以下代码:
+
+```c
+struct rt_messagequeue
+{
+ struct rt_ipc_object parent;
+
+ void* msg_pool; /* 指向存放消息的缓冲区的指针 */
+
+ rt_uint16_t msg_size; /* 每个消息的长度 */
+ rt_uint16_t max_msgs; /* 最大能够容纳的消息数 */
+
+ rt_uint16_t entry; /* 队列中已有的消息数 */
+
+ void* msg_queue_head; /* 消息链表头 */
+ void* msg_queue_tail; /* 消息链表尾 */
+ void* msg_queue_free; /* 空闲消息链表 */
+
+ rt_list_t suspend_sender_thread; /* 发送线程的挂起等待队列 */
+};
+typedef struct rt_messagequeue* rt_mq_t;
+```
+
+rt_messagequeue 对象从 rt_ipc_object 中派生,由 IPC 容器所管理。
+
+### 消息队列的管理方式
+
+消息队列控制块是一个结构体,其中含有消息队列相关的重要参数,在消息队列的功能实现中起重要的作用。消息队列的相关接口如下图所示,对一个消息队列的操作包含:创建消息队列 - 发送消息 - 接收消息 - 删除消息队列。
+
+
+
+#### 创建和删除消息队列
+
+消息队列在使用前,应该被创建出来,或对已有的静态消息队列对象进行初始化,创建消息队列的函数接口如下所示:
+
+```c
+rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size,
+ rt_size_t max_msgs, rt_uint8_t flag);
+```
+
+创建消息队列时先从对象管理器中分配一个消息队列对象,然后给消息队列对象分配一块内存空间,组织成空闲消息链表,这块内存的大小 =[消息大小 + 消息头(用于链表连接)的大小]X 消息队列最大个数,接着再初始化消息队列,此时消息队列为空。下表描述了该函数的输入参数与返回值:
+
+ rt_mq_create() 的输入参数和返回值
+
+|**参数** |**描述** |
+|--------------------|-------------------------------------------------------------------------------|
+| name | 消息队列的名称 |
+| msg_size | 消息队列中一条消息的最大长度,单位字节 |
+| max_msgs | 消息队列的最大个数 |
+| flag | 消息队列采用的等待方式,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回** | —— |
+| RT_EOK | 发送成功 |
+| 消息队列对象的句柄 | 成功 |
+| RT_NULL | 失败 |
+
+当消息队列不再被使用时,应该删除它以释放系统资源,一旦操作完成,消息队列将被永久性地删除。删除消息队列的函数接口如下:
+
+```c
+rt_err_t rt_mq_delete(rt_mq_t mq);
+```
+
+删除消息队列时,如果有线程被挂起在该消息队列等待队列上,则内核先唤醒挂起在该消息等待队列上的所有线程(线程返回值是 - RT_ERROR),然后再释放消息队列使用的内存,最后删除消息队列对象。下表描述了该函数的输入参数与返回值:
+
+ rt_mq_delete() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|--------------------|
+| mq | 消息队列对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 初始化和脱离消息队列
+
+初始化静态消息队列对象跟创建消息队列对象类似,只是静态消息队列对象的内存是在系统编译时由编译器分配的,一般放于读数据段或未初始化数据段中。在使用这类静态消息队列对象前,需要进行初始化。初始化消息队列对象的函数接口如下:
+
+```c
+rt_err_t rt_mq_init(rt_mq_t mq, const char* name,
+ void *msgpool, rt_size_t msg_size,
+ rt_size_t pool_size, rt_uint8_t flag);
+```
+
+初始化消息队列时,该接口需要用户已经申请获得的消息队列对象的句柄(即指向消息队列对象控制块的指针)、消息队列名、消息缓冲区指针、消息大小以及消息队列缓冲区大小。如下图所示,消息队列初始化后所有消息都挂在空闲消息链表上,消息队列为空。下表描述了该函数的输入参数与返回值:
+
+ rt_mq_init() 的输入参数和返回值
+
+|**参数** |**描述** |
+|-----------|-------------------------------------------------------------------------------|
+| mq | 消息队列对象的句柄 |
+| name | 消息队列的名称 |
+| msgpool | 指向存放消息的缓冲区的指针 |
+| msg_size | 消息队列中一条消息的最大长度,单位字节 |
+| pool_size | 存放消息的缓冲区大小 |
+| flag | 消息队列采用的等待方式,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
+|**返回** | —— |
+| RT_EOK | 成功 |
+
+脱离消息队列将使消息队列对象被从内核对象管理器中脱离。脱离消息队列使用下面的接口:
+
+```c
+rt_err_t rt_mq_detach(rt_mq_t mq);
+```
+
+使用该函数接口后,内核先唤醒所有挂在该消息等待队列对象上的线程(线程返回值是 -RT_ERROR),然后将该消息队列对象从内核对象管理器中脱离。下表描述了该函数的输入参数与返回值:
+
+ rt_mq_detach() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|--------------------|
+| mq | 消息队列对象的句柄 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 发送消息
+
+线程或者中断服务程序都可以给消息队列发送消息。当发送消息时,消息队列对象先从空闲消息链表上取下一个空闲消息块,把线程或者中断服务程序发送的消息内容复制到消息块上,然后把该消息块挂到消息队列的尾部。当且仅当空闲消息链表上有可用的空闲消息块时,发送者才能成功发送消息;当空闲消息链表上无可用消息块,说明消息队列已满,此时,发送消息的的线程或者中断程序会收到一个错误码(-RT_EFULL)。发送消息的函数接口如下:
+
+```c
+rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size);
+```
+
+发送消息时,发送者需指定发送的消息队列的对象句柄(即指向消息队列控制块的指针),并且指定发送的消息内容以及消息大小。如下图所示,在发送一个普通消息之后,空闲消息链表上的队首消息被转移到了消息队列尾。下表描述了该函数的输入参数与返回值:
+
+ rt_mq_send() 的输入参数和返回值
+
+|**参数** |**描述** |
+|------------|------------------------------------------------------|
+| mq | 消息队列对象的句柄 |
+| buffer | 消息内容 |
+| size | 消息大小 |
+|**返回** | —— |
+| RT_EOK | 成功 |
+| \-RT_EFULL | 消息队列已满 |
+| \-RT_ERROR | 失败,表示发送的消息长度大于消息队列中消息的最大长度 |
+
+#### 等待方式发送消息
+
+用户也可以通过如下的函数接口向指定的消息队列中发送消息:
+
+```c
+rt_err_t rt_mq_send_wait(rt_mq_t mq,
+ const void *buffer,
+ rt_size_t size,
+ rt_int32_t timeout);
+```
+
+rt_mq_send_wait() 与 rt_mq_send() 的区别在于有等待时间,如果消息队列已经满了,那么发送线程将根据设定的 timeout 参数进行等待。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。下表描述了该函数的输入参数与返回值:
+
+ rt_mq_send_wait() 的输入参数和返回值
+
+|**参数** |**描述** |
+|------------|------------------------------------------------------|
+| mq | 消息队列对象的句柄 |
+| buffer | 消息内容 |
+| size | 消息大小 |
+| timeout | 超时时间 |
+|**返回** | —— |
+| RT_EOK | 成功 |
+| \-RT_EFULL | 消息队列已满 |
+| \-RT_ERROR | 失败,表示发送的消息长度大于消息队列中消息的最大长度 |
+
+#### 发送紧急消息
+
+发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂到队首,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。发送紧急消息的函数接口如下:
+
+```c
+rt_err_t rt_mq_urgent(rt_mq_t mq, void* buffer, rt_size_t size);
+```
+
+下表描述了该函数的输入参数与返回值:
+
+ rt_mq_urgent() 的输入参数和返回值
+
+|**参数** |**描述** |
+|------------|--------------------|
+| mq | 消息队列对象的句柄 |
+| buffer | 消息内容 |
+| size | 消息大小 |
+|**返回** | —— |
+| RT_EOK | 成功 |
+| \-RT_EFULL | 消息队列已满 |
+| \-RT_ERROR | 失败 |
+
+#### 接收消息
+
+当消息队列中有消息时,接收者才能接收消息,否则接收者会根据超时时间设置,或挂起在消息队列的等待线程队列上,或直接返回。接收消息函数接口如下:
+
+```c
+rt_err_t rt_mq_recv (rt_mq_t mq, void* buffer,
+ rt_size_t size, rt_int32_t timeout);
+```
+
+接收消息时,接收者需指定存储消息的消息队列对象句柄,并且指定一个内存缓冲区,接收到的消息内容将被复制到该缓冲区里。此外,还需指定未能及时取到消息时的超时时间。如下图所示,接收一个消息后消息队列上的队首消息被转移到了空闲消息链表的尾部。下表描述了该函数的输入参数与返回值:
+
+ rt_mq_recv() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|--------------------|
+| mq | 消息队列对象的句柄 |
+| buffer | 消息内容 |
+| size | 消息大小 |
+| timeout | 指定的超时时间 |
+|**返回** | —— |
+| RT_EOK | 成功收到 |
+| \-RT_ETIMEOUT | 超时 |
+| \-RT_ERROR | 失败,返回错误 |
+
+### 消息队列应用示例
+
+这是一个消息队列的应用例程,例程中初始化了 2 个静态线程,一个线程会从消息队列中收取消息;另一个线程会定时给消息队列发送普通消息和紧急消息,如下代码所示:
+
+ 消息队列的使用例程
+
+```c
+#include
+
+/* 消息队列控制块 */
+static struct rt_messagequeue mq;
+/* 消息队列中用到的放置消息的内存池 */
+static rt_uint8_t msg_pool[2048];
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread1_stack[1024];
+static struct rt_thread thread1;
+/* 线程 1 入口函数 */
+static void thread1_entry(void *parameter)
+{
+ char buf = 0;
+ rt_uint8_t cnt = 0;
+
+ while (1)
+ {
+ /* 从消息队列中接收消息 */
+ if (rt_mq_recv(&mq, &buf, sizeof(buf), RT_WAITING_FOREVER) == RT_EOK)
+ {
+ rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", buf);
+ if (cnt == 19)
+ {
+ break;
+ }
+ }
+ /* 延时 50ms */
+ cnt++;
+ rt_thread_mdelay(50);
+ }
+ rt_kprintf("thread1: detach mq \n");
+ rt_mq_detach(&mq);
+}
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread2_stack[1024];
+static struct rt_thread thread2;
+/* 线程 2 入口 */
+static void thread2_entry(void *parameter)
+{
+ int result;
+ char buf = 'A';
+ rt_uint8_t cnt = 0;
+
+ while (1)
+ {
+ if (cnt == 8)
+ {
+ /* 发送紧急消息到消息队列中 */
+ result = rt_mq_urgent(&mq, &buf, 1);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("rt_mq_urgent ERR\n");
+ }
+ else
+ {
+ rt_kprintf("thread2: send urgent message - %c\n", buf);
+ }
+ }
+ else if (cnt>= 20)/* 发送 20 次消息之后退出 */
+ {
+ rt_kprintf("message queue stop send, thread2 quit\n");
+ break;
+ }
+ else
+ {
+ /* 发送消息到消息队列中 */
+ result = rt_mq_send(&mq, &buf, 1);
+ if (result != RT_EOK)
+ {
+ rt_kprintf("rt_mq_send ERR\n");
+ }
+
+ rt_kprintf("thread2: send message - %c\n", buf);
+ }
+ buf++;
+ cnt++;
+ /* 延时 5ms */
+ rt_thread_mdelay(5);
+ }
+}
+
+/* 消息队列示例的初始化 */
+int msgq_sample(void)
+{
+ rt_err_t result;
+
+ /* 初始化消息队列 */
+ result = rt_mq_init(&mq,
+ "mqt",
+ &msg_pool[0], /* 内存池指向 msg_pool */
+ 1, /* 每个消息的大小是 1 字节 */
+ sizeof(msg_pool), /* 内存池的大小是 msg_pool 的大小 */
+ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */
+
+ if (result != RT_EOK)
+ {
+ rt_kprintf("init message queue failed.\n");
+ return -1;
+ }
+
+ rt_thread_init(&thread1,
+ "thread1",
+ thread1_entry,
+ RT_NULL,
+ &thread1_stack[0],
+ sizeof(thread1_stack), 25, 5);
+ rt_thread_startup(&thread1);
+
+ rt_thread_init(&thread2,
+ "thread2",
+ thread2_entry,
+ RT_NULL,
+ &thread2_stack[0],
+ sizeof(thread2_stack), 25, 5);
+ rt_thread_startup(&thread2);
+
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(msgq_sample, msgq sample);
+```
+
+仿真运行结果如下:
+
+```
+\ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 24 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh > msgq_sample
+msh >thread2: send message - A
+thread1: recv msg from msg queue, the content:A
+thread2: send message - B
+thread2: send message - C
+thread2: send message - D
+thread2: send message - E
+thread1: recv msg from msg queue, the content:B
+thread2: send message - F
+thread2: send message - G
+thread2: send message - H
+thread2: send urgent message - I
+thread2: send message - J
+thread1: recv msg from msg queue, the content:I
+thread2: send message - K
+thread2: send message - L
+thread2: send message - M
+thread2: send message - N
+thread2: send message - O
+thread1: recv msg from msg queue, the content:C
+thread2: send message - P
+thread2: send message - Q
+thread2: send message - R
+thread2: send message - S
+thread2: send message - T
+thread1: recv msg from msg queue, the content:D
+message queue stop send, thread2 quit
+thread1: recv msg from msg queue, the content:E
+thread1: recv msg from msg queue, the content:F
+thread1: recv msg from msg queue, the content:G
+…
+thread1: recv msg from msg queue, the content:T
+thread1: detach mq
+```
+
+例程演示了消息队列的使用方法。线程 1 会从消息队列中收取消息;线程 2 定时给消息队列发送普通消息和紧急消息。由于线程 2 发送消息 “I” 是紧急消息,会直接插入消息队列的队首,所以线程 1 在接收到消息 “B” 后,接收的是该紧急消息,之后才接收消息“C”。
+
+### 消息队列的使用场合
+
+消息队列可以应用于发送不定长消息的场合,包括线程与线程间的消息交换,以及中断服务例程中给线程发送消息(中断服务例程不能接收消息)。下面分发送消息和同步消息两部分来介绍消息队列的使用。
+
+#### 发送消息
+
+消息队列和邮箱的明显不同是消息的长度并不限定在 4 个字节以内;另外,消息队列也包括了一个发送紧急消息的函数接口。但是当创建的是一个所有消息的最大长度是 4 字节的消息队列时,消息队列对象将蜕化成邮箱。这个不限定长度的消息,也及时的反应到了代码编写的场合上,同样是类似邮箱的代码:
+
+```c
+struct msg
+{
+ rt_uint8_t *data_ptr; /* 数据块首地址 */
+ rt_uint32_t data_size; /* 数据块大小 */
+};
+```
+
+和邮箱例子相同的消息结构定义,假设依然需要发送这样一个消息给接收线程。在邮箱例子中,这个结构只能够发送指向这个结构的指针(在函数指针被发送过去后,接收线程能够正确的访问指向这个地址的内容,通常这块数据需要留给接收线程来释放)。而使用消息队列的方式则大不相同:
+
+```c
+void send_op(void *data, rt_size_t length)
+{
+ struct msg msg_ptr;
+
+ msg_ptr.data_ptr = data; /* 指向相应的数据块地址 */
+ msg_ptr.data_size = length; /* 数据块的长度 */
+
+ /* 发送这个消息指针给 mq 消息队列 */
+ rt_mq_send(mq, (void*)&msg_ptr, sizeof(struct msg));
+}
+```
+
+注意,上面的代码中,是把一个局部变量的数据内容发送到了消息队列中。在接收线程中,同样也采用局部变量进行消息接收的结构体:
+
+```c
+void message_handler()
+{
+ struct msg msg_ptr; /* 用于放置消息的局部变量 */
+
+ /* 从消息队列中接收消息到 msg_ptr 中 */
+ if (rt_mq_recv(mq, (void*)&msg_ptr, sizeof(struct msg), RT_WAITING_FOREVER) == RT_EOK)
+ {
+ /* 成功接收到消息,进行相应的数据处理 */
+ }
+}
+```
+
+因为消息队列是直接的数据内容复制,所以在上面的例子中,都采用了局部变量的方式保存消息结构体,这样也就免去动态内存分配的烦恼了(也就不用担心,接收线程在接收到消息时,消息内存空间已经被释放)。
+
+#### 同步消息
+
+在一般的系统设计中会经常遇到要发送同步消息的问题,这个时候就可以根据当时状态的不同选择相应的实现:两个线程间可以采用**[消息队列 + 信号量或邮箱]**的形式实现。发送线程通过消息发送的形式发送相应的消息给消息队列,发送完毕后希望获得接收线程的收到确认,工作示意图如下图所示:
+
+
+
+根据消息确认的不同,可以把消息结构体定义成:
+
+```c
+struct msg
+{
+ /* 消息结构其他成员 */
+ struct rt_mailbox ack;
+};
+/* 或者 */
+struct msg
+{
+ /* 消息结构其他成员 */
+ struct rt_semaphore ack;
+};
+```
+
+第一种类型的消息使用了邮箱来作为确认标志,而第二种类型的消息采用了信号量来作为确认标志。邮箱作为确认标志,代表着接收线程能够通知一些状态值给发送线程;而信号量作为确认标志只能够单一的通知发送线程,消息已经确认接收。
+
+信号
+----
+
+信号(又称为软中断信号),在软件层次上是对中断机制的一种模拟,在原理上,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。
+
+### 信号的工作机制
+
+信号在 RT-Thread 中用作异步通信,POSIX 标准定义了 sigset_t 类型来定义一个信号集,然而 sigset_t 类型在不同的系统可能有不同的定义方式,在 RT-Thread 中,将 sigset_t 定义成了 unsigned long 型,并命名为 rt_sigset_t,应用程序能够使用的信号为 SIGUSR1(10)和 SIGUSR2(12)。
+
+信号本质是软中断,用来通知线程发生了异步事件,用做线程之间的异常通知、应急处理。一个线程不必通过任何操作来等待信号的到达,事实上,线程也不知道信号到底什么时候到达,线程之间可以互相通过调用 rt_thread_kill() 发送软中断信号。
+
+收到信号的线程对各种信号有不同的处理方法,处理方法可以分为三类:
+
+第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。
+
+第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。
+
+第三种方法是,对该信号的处理保留系统的默认值。
+
+如下图所示,假设线程 1 需要对信号进行处理,首先线程 1 安装一个信号并解除阻塞,并在安装的同时设定了对信号的异常处理方式;然后其他线程可以给线程 1 发送信号,触发线程 1 对该信号的处理。
+
+
+
+当信号被传递给线程 1 时,如果它正处于挂起状态,那会把状态改为就绪状态去处理对应的信号。如果它正处于运行状态,那么会在它当前的线程栈基础上建立新栈帧空间去处理对应的信号,需要注意的是使用的线程栈大小也会相应增加。
+
+### 信号的管理方式
+
+对于信号的操作,有以下几种:安装信号、阻塞信号、阻塞解除、信号发送、信号等待。信号的接口详见下图:
+
+
+
+#### 安装信号
+
+如果线程要处理某一信号,那么就要在线程中安装该信号。安装信号主要用来确定信号值及线程针对该信号值的动作之间的映射关系,即线程将要处理哪个信号,该信号被传递给线程时,将执行何种操作。详细定义请见以下代码:
+
+```c
+rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t[] handler);
+```
+
+其中 rt_sighandler_t 是定义信号处理函数的函数指针类型。下表描述了该函数的输入参数与返回值:
+
+ rt_signal_install() 的输入参数和返回值
+
+|**参数** |**描述** |
+|-----------------------|--------------------------------------------------------|
+| signo | 信号值(只有 SIGUSR1 和 SIGUSR2 是开放给用户使用的,下同) |
+| handler | 设置对信号值的处理方式 |
+|**返回** | —— |
+| SIG_ERR | 错误的信号 |
+| 安装信号前的 handler 值 | 成功 |
+
+在信号安装时设定 handler 参数,决定了该信号的不同的处理方法。处理方法可以分为三种:
+
+1)类似中断的处理方式,参数指向当信号发生时用户自定义的处理函数,由该函数来处理。
+
+2)参数设为 SIG_IGN,忽略某个信号,对该信号不做任何处理,就像未发生过一样。
+
+3)参数设为 SIG_DFL,系统会调用默认的处理函数_signal_default_handler()。
+
+#### 阻塞信号
+
+信号阻塞,也可以理解为屏蔽信号。如果该信号被阻塞,则该信号将不会递达给安装此信号的线程,也不会引发软中断处理。调 rt_signal_mask() 可以使信号阻塞:
+
+```c
+void rt_signal_mask(int signo);
+```
+
+下表描述了该函数的输入参数:
+
+ rt_signal_mask() 函数参数
+
+|**参数**|**描述**|
+|----------|----------|
+| signo | 信号值 |
+
+#### 解除信号阻塞
+
+线程中可以安装好几个信号,使用此函数可以对其中一些信号给予 “关注”,那么发送这些信号都会引发该线程的软中断。调用 rt_signal_unmask() 可以用来解除信号阻塞:
+
+```c
+void rt_signal_unmask(int signo);
+```
+
+下表描述了该函数的输入参数:
+
+ rt_signal_unmask() 函数参数
+
+|**参数**|**描述**|
+|----------|----------|
+| signo | 信号值 |
+
+#### 发送信号
+
+当需要进行异常处理时,可以给设定了处理异常的线程发送信号,调用 rt_thread_kill() 可以用来向任何线程发送信号:
+
+```c
+int rt_thread_kill(rt_thread_t tid, int sig);
+```
+
+下表描述了该函数的输入参数与返回值:
+
+ rt_thread_kill() 的输入参数和返回值
+
+|**参数** |**描述** |
+|-------------|----------------|
+| tid | 接收信号的线程 |
+| sig | 信号值 |
+|**返回** | —— |
+| RT_EOK | 发送成功 |
+| \-RT_EINVAL | 参数错误 |
+
+#### 等待信号
+
+等待 set 信号的到来,如果没有等到这个信号,则将线程挂起,直到等到这个信号或者等待时间超过指定的超时时间 timeout。如果等到了该信号,则将指向该信号体的指针存入 si,如下是等待信号的函数。
+
+```c
+int rt_signal_wait(const rt_sigset_t *set,
+ rt_siginfo_t[] *si, rt_int32_t timeout);
+```
+
+其中 rt_siginfo_t 是定义信号信息的数据类型,下表描述了该函数的输入参数与返回值:
+
+ rt_signal_wait() 的输入参数和返回值
+
+|**参数** |**描述** |
+|---------------|----------------------------|
+| set | 指定等待的信号 |
+| si | 指向存储等到信号信息的指针 |
+| timeout | 指定的等待时间 |
+|**返回** | —— |
+| RT_EOK | 等到信号 |
+| \-RT_ETIMEOUT | 超时 |
+| \-RT_EINVAL | 参数错误 |
+
+### 信号应用示例
+
+这是一个信号的应用例程,如下代码所示。此例程创建了 1 个线程,在安装信号时,信号处理方式设为自定义处理,定义的信号的处理函数为 thread1_signal_handler()。待此线程运行起来安装好信号之后,给此线程发送信号。此线程将接收到信号,并打印信息。
+
+ 信号使用例程
+
+```c
+#include
+
+#define THREAD_PRIORITY 25
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+
+static rt_thread_t tid1 = RT_NULL;
+
+/* 线程 1 的信号处理函数 */
+void thread1_signal_handler(int sig)
+{
+ rt_kprintf("thread1 received signal %d\n", sig);
+}
+
+/* 线程 1 的入口函数 */
+static void thread1_entry(void *parameter)
+{
+ int cnt = 0;
+
+ /* 安装信号 */
+ rt_signal_install(SIGUSR1, thread1_signal_handler);
+ rt_signal_unmask(SIGUSR1);
+
+ /* 运行 10 次 */
+ while (cnt < 10)
+ {
+ /* 线程 1 采用低优先级运行,一直打印计数值 */
+ rt_kprintf("thread1 count : %d\n", cnt);
+
+ cnt++;
+ rt_thread_mdelay(100);
+ }
+}
+
+/* 信号示例的初始化 */
+int signal_sample(void)
+{
+ /* 创建线程 1 */
+ tid1 = rt_thread_create("thread1",
+ thread1_entry, RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+
+ if (tid1 != RT_NULL)
+ rt_thread_startup(tid1);
+
+ rt_thread_mdelay(300);
+
+ /* 发送信号 SIGUSR1 给线程 1 */
+ rt_thread_kill(tid1, SIGUSR1);
+
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(signal_sample, signal sample);
+```
+
+仿真运行结果如下:
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 24 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >signal_sample
+thread1 count : 0
+thread1 count : 1
+thread1 count : 2
+msh >thread1 received signal 10
+thread1 count : 3
+thread1 count : 4
+thread1 count : 5
+thread1 count : 6
+thread1 count : 7
+thread1 count : 8
+thread1 count : 9
+```
+
+例程中,首先线程安装信号并解除阻塞,然后发送信号给线程。线程接收到信号并打印出了接收到的信号:SIGUSR1(10)。
+
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08Memory_distribution.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08Memory_distribution.png
new file mode 100644
index 0000000000000000000000000000000000000000..434c04c9bc144cfa0ad766b03f0e3d1fe4f93422
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08Memory_distribution.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08heap_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08heap_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..e85cac976385bf6fb9f4f6f15dd004b37b163c7b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08heap_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08memheap.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08memheap.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc93ffe436bcbad46cf9083633b0aa34b3ef1725
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08memheap.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0abb093150948914c0d2a7c2691ca730e8fe71b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool_ops.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool_ops.png
new file mode 100644
index 0000000000000000000000000000000000000000..b49ed6586670edaafb80732da35b9b85810bf1aa
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool_ops.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool_work.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..db14bcd0f8c9e57a2866c9c906053c40e9ed04b0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08mempool_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08slab.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08slab.png
new file mode 100644
index 0000000000000000000000000000000000000000..a16da39406715bdd2464f23f5d62a3e621d50545
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08slab.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work.png
new file mode 100644
index 0000000000000000000000000000000000000000..1c7485fd45d27b2b078b609a43213e7510f35c1b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work2.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work2.png
new file mode 100644
index 0000000000000000000000000000000000000000..0bc17d6d0b58df2813c8f2f0421d764c06be5971
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work3.png b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work3.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2e6752fa05176da5059c6860ee3396a59a89c07
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work3.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/memory/memory.md b/rt-thread-version/rt-thread-standard/programming-manual/memory/memory.md
new file mode 100644
index 0000000000000000000000000000000000000000..81ca0637ec6fd564845574982270e1262dcfba59
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/memory/memory.md
@@ -0,0 +1,665 @@
+# 内存管理
+
+在计算系统中,通常存储空间可以分为两种:内部存储空间和外部存储空间。内部存储空间通常访问速度比较快,能够按照变量地址随机地访问,也就是我们通常所说的 RAM(随机存储器),可以把它理解为电脑的内存;而外部存储空间内所保存的内容相对来说比较固定,即使掉电后数据也不会丢失,这就是通常所讲的 ROM(只读存储器),可以把它理解为电脑的硬盘。
+
+计算机系统中,变量、中间数据一般存放在 RAM 中,只有在实际使用时才将它们从 RAM 调入到 CPU 中进行运算。一些数据需要的内存大小需要在程序运行过程中根据实际情况确定,这就要求系统具有对内存空间进行动态管理的能力,在用户需要一段内存空间时,向系统申请,系统选择一段合适的内存空间分配给用户,用户使用完毕后,再释放回系统,以便系统将该段内存空间回收再利用。
+
+这章主要介绍 RT-Thread 中的两种内存管理方式,分别是动态内存堆管理和静态内存池管理,学完本章,读者会了解 RT-Thread 的内存管理原理及使用方式。
+
+## 内存管理的功能特点
+
+由于实时系统中对时间的要求非常严格,内存管理往往要比通用操作系统要求苛刻得多:
+
+1)分配内存的时间必须是确定的。一般内存管理算法是根据需要存储的数据的长度在内存中去寻找一个与这段数据相适应的空闲内存块,然后将数据存储在里面。而寻找这样一个空闲内存块所耗费的时间是不确定的,因此对于实时系统来说,这就是不可接受的,实时系统必须要保证内存块的分配过程在可预测的确定时间内完成,否则实时任务对外部事件的响应也将变得不可确定。
+
+2)随着内存不断被分配和释放,整个内存区域会产生越来越多的碎片(因为在使用过程中,申请了一些内存,其中一些释放了,导致内存空间中存在一些小的内存块,它们地址不连续,不能够作为一整块的大内存分配出去),系统中还有足够的空闲内存,但因为它们地址并非连续,不能组成一块连续的完整内存块,会使得程序不能申请到大的内存。对于通用系统而言,这种不恰当的内存分配算法可以通过重新启动系统来解决
+(每个月或者数个月进行一次),但是对于那些需要常年不间断地工作于野外的嵌入式系统来说,就变得让人无法接受了。
+
+3)嵌入式系统的资源环境也是不尽相同,有些系统的资源比较紧张,只有数十 KB 的内存可供分配,而有些系统则存在数 MB 的内存,如何为这些不同的系统,选择适合它们的高效率的内存分配算法,就将变得复杂化。
+
+RT-Thread 操作系统在内存管理上,根据上层应用及系统资源的不同,有针对性地提供了不同的内存分配管理算法。总体上可分为两类:内存堆管理与内存池管理,而内存堆管理又根据具体内存设备划分为三种情况:
+
+第一种是针对小内存块的分配管理(小内存管理算法);
+
+第二种是针对大内存块的分配管理(slab 管理算法);
+
+第三种是针对多内存堆的分配情况(memheap 管理算法)
+
+## 内存堆管理
+
+内存堆管理用于管理一段连续的内存空间,在第三章中介绍过 RT-Thread 的内存分布情况,如下图所示,RT-Thread 将 “ZI 段结尾处” 到内存尾部的空间用作内存堆。
+
+
+
+内存堆可以在当前资源满足的情况下,根据用户的需求分配任意大小的内存块。而当用户不需要再使用这些内存块时,又可以释放回堆中供其他应用分配使用。RT-Thread
+系统为了满足不同的需求,提供了不同的内存管理算法,分别是小内存管理算法、slab 管理算法和 memheap 管理算法。
+
+小内存管理算法主要针对系统资源比较少,一般用于小于 2MB 内存空间的系统;而 slab 内存管理算法则主要是在系统资源比较丰富时,提供了一种近似多内存池管理算法的快速算法。除上述之外,RT-Thread 还有一种针对多内存堆的管理算法,即 memheap 管理算法。memheap 方法适用于系统存在多个内存堆的情况,它可以将多个内存 “粘贴” 在一起,形成一个大的内存堆,用户使用起来会非常方便。
+
+这几类内存堆管理算法在系统运行时只能选择其中之一或者完全不使用内存堆管理器,他们提供给应用程序的 API 接口完全相同。
+
+> [!NOTE]
+> 注:因为内存堆管理器要满足多线程情况下的安全分配,会考虑多线程间的互斥问题,所以请不要在中断服务例程中分配或释放动态内存块。因为它可能会引起当前上下文被挂起等待。
+
+### 小内存管理算法
+
+小内存管理算法是一个简单的内存分配算法。初始时,它是一块大的内存。当需要分配内存块时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来,如下图所示:
+
+
+
+每个内存块(不管是已分配的内存块还是空闲的内存块)都包含一个数据头,其中包括:
+
+**1)magic**:变数(或称为幻数),它会被初始化成
+0x1ea0(即英文单词 heap),用于标记这个内存块是一个内存管理用的内存数据块;变数不仅仅用于标识这个数据块是一个内存管理用的内存数据块,实质也是一个内存保护字:如果这个区域被改写,那么也就意味着这块内存块被非法改写(正常情况下只有内存管理器才会去碰这块内存)。
+
+**2)used**:指示出当前内存块是否已经分配。
+
+内存管理的表现主要体现在内存的分配与释放上,小型内存管理算法可以用以下例子体现出来。
+
+如下图所示的内存分配情况,空闲链表指针 lfree 初始指向 32 字节的内存块。当用户线程要再分配一个 64 字节的内存块时,但此 lfree 指针指向的内存块只有 32 字节并不能满足要求,内存管理器会继续寻找下一内存块,当找到再下一块内存块,128 字节时,它满足分配的要求。因为这个内存块比较大,分配器将把此内存块进行拆分,余下的内存块(52 字节)继续留在 lfree 链表中,如下图分配 64 字节后的链表结构所示。
+
+
+
+
+
+另外,在每次分配内存块前,都会留出 12 字节数据头用于 magic、used 信息及链表节点使用。返回给应用的地址实际上是这块内存块 12 字节以后的地址,前面的 12 字节数据头是用户永远不应该碰的部分(注:12 字节数据头长度会与系统对齐差异而有所不同)。
+
+释放时则是相反的过程,但分配器会查看前后相邻的内存块是否空闲,如果空闲则合并成一个大的空闲内存块。
+
+### slab 管理算法
+
+RT-Thread 的 slab 分配器是在 DragonFly BSD 创始人 Matthew Dillon 实现的 slab 分配器基础上,针对嵌入式系统优化的内存分配算法。最原始的 slab 算法是 Jeff Bonwick 为 Solaris 操作系统而引入的一种高效内核内存分配算法。
+
+RT-Thread 的 slab 分配器实现主要是去掉了其中的对象构造及析构过程,只保留了纯粹的缓冲型的内存池算法。slab 分配器会根据对象的大小分成多个区(zone),也可以看成每类对象有一个内存池,如下图所示:
+
+
+
+一个 zone 的大小在 32K 到 128K 字节之间,分配器会在堆初始化时根据堆的大小自动调整。系统中的 zone 最多包括 72 种对象,一次最大能够分配 16K 的内存空间,如果超出了 16K 那么直接从页分配器中分配。每个 zone 上分配的内存块大小是固定的,能够分配相同大小内存块的 zone 会链接在一个链表中,而 72 种对象的 zone 链表则放在一个数组(zone_array[])中统一管理。
+
+下面是内存分配器主要的两种操作:
+
+**(1)内存分配**
+
+假设分配一个 32 字节的内存,slab 内存分配器会先按照 32 字节的值,从 zone array 链表表头数组中找到相应的 zone 链表。如果这个链表是空的,则向页分配器分配一个新的 zone,然后从 zone 中返回第一个空闲内存块。如果链表非空,则这个 zone 链表中的第一个 zone 节点必然有空闲块存在(否则它就不应该放在这个链表中),那么就取相应的空闲块。如果分配完成后,zone 中所有空闲内存块都使用完毕,那么分配器需要把这个 zone 节点从链表中删除。
+
+**(2)内存释放**
+
+分配器需要找到内存块所在的 zone 节点,然后把内存块链接到 zone 的空闲内存块链表中。如果此时 zone 的空闲链表指示出 zone 的所有内存块都已经释放,即 zone 是完全空闲的,那么当 zone 链表中全空闲 zone 达到一定数目后,系统就会把这个全空闲的 zone 释放到页面分配器中去。
+
+### memheap 管理算法
+
+memheap 管理算法适用于系统含有多个地址可不连续的内存堆。使用 memheap 内存管理可以简化系统存在多个内存堆时的使用:当系统中存在多个内存堆的时候,用户只需要在系统初始化时将多个所需的 memheap 初始化,并开启 memheap 功能就可以很方便地把多个 memheap(地址可不连续)粘合起来用于系统的 heap 分配。
+
+> [!NOTE]
+> 注:在开启 memheap 之后原来的 heap 功能将被关闭,两者只可以通过打开或关闭 RT_USING_MEMHEAP_AS_HEAP 来选择其一
+
+memheap 工作机制如下图所示,首先将多块内存加入 memheap_item 链表进行粘合。当分配内存块时,会先从默认内存堆去分配内存,当分配不到时会查找 memheap_item 链表,尝试从其他的内存堆上分配内存块。应用程序不用关心当前分配的内存块位于哪个内存堆上,就像是在操作一个内存堆。
+
+
+
+### 内存堆配置和初始化
+
+在使用内存堆时,必须要在系统初始化的时候进行堆的初始化,可以通过下面的函数接口完成:
+
+```c
+void rt_system_heap_init(void* begin_addr, void* end_addr);
+```
+
+这个函数会把参数 begin_addr,end_addr 区域的内存空间作为内存堆来使用。下表描述了该函数的输入参数:
+
+ rt_system_heap_init() 的输入参数
+
+|**参数** |**描述** |
+|------------|--------------------|
+| begin_addr | 堆内存区域起始地址 |
+| end_addr | 堆内存区域结束地址 |
+
+在使用 memheap 堆内存时,必须要在系统初始化的时候进行堆内存的初始化,可以通过下面的函数接口完成:
+
+```c
+rt_err_t rt_memheap_init(struct rt_memheap *memheap,
+ const char *name,
+ void *start_addr,
+ rt_uint32_t size)
+```
+
+如果有多个不连续的 memheap 可以多次调用该函数将其初始化并加入 memheap_item 链表。下表描述了该函数的输入参数与返回值:
+
+ rt_memheap_init() 的输入参数与返回值
+
+|**参数** |**描述** |
+|------------|--------------------|
+| memheap | memheap 控制块 |
+| name | 内存堆的名称 |
+| start_addr | 堆内存区域起始地址 |
+| size | 堆内存大小 |
+|**返回** | —— |
+| RT_EOK | 成功 |
+
+### 内存堆的管理方式
+
+对内存堆的操作如下图所示,包含:初始化、申请内存块、释放内存,所有使用完成后的动态内存都应该被释放,以供其他程序的申请使用。
+
+
+
+#### 分配和释放内存块
+
+从内存堆上分配用户指定大小的内存块,函数接口如下:
+
+```c
+void *rt_malloc(rt_size_t nbytes);
+```
+
+rt_malloc 函数会从系统堆空间中找到合适大小的内存块,然后把内存块可用地址返回给用户。下表描述了该函数的输入参数与返回值:
+
+ rt_malloc() 的输入参数和返回值
+
+|**参数** |**描述** |
+|------------------|------------------------------------|
+| nbytes | 需要分配的内存块的大小,单位为字节 |
+|**返回** | —— |
+| 分配的内存块地址 | 成功 |
+| RT_NULL | 失败 |
+
+应用程序使用完从内存分配器中申请的内存后,必须及时释放,否则会造成内存泄漏,释放内存块的函数接口如下:
+
+```c
+void rt_free (void *ptr);
+```
+
+rt_free 函数会把待释放的内存还回给堆管理器中。在调用这个函数时用户需传递待释放的内存块指针,如果是空指针直接返回。下表描述了该函数的输入参数:
+
+ rt_free() 的输入参数
+
+|**参数**|**描述** |
+|----------|--------------------|
+| ptr | 待释放的内存块指针 |
+
+#### 重分配内存块
+
+在已分配内存块的基础上重新分配内存块的大小(增加或缩小),可以通过下面的函数接口完成:
+
+```c
+void *rt_realloc(void *rmem, rt_size_t newsize);
+```
+
+在进行重新分配内存块时,原来的内存块数据保持不变(缩小的情况下,后面的数据被自动截断)。下表描述了该函数的输入参数和返回值:
+
+ rt_realloc() 的输入参数和返回值
+
+|**参数** |**描述** |
+|----------------------|--------------------|
+| rmem | 指向已分配的内存块 |
+| newsize | 重新分配的内存大小 |
+|**返回** | —— |
+| 重新分配的内存块地址 | 成功 |
+
+#### 分配多内存块
+
+从内存堆中分配连续内存地址的多个内存块,可以通过下面的函数接口完成:
+
+```c
+ void *rt_calloc(rt_size_t count, rt_size_t size);
+```
+
+下表描述了该函数的输入参数与返回值:
+
+ rt_calloc() 的输入参数和返回值
+
+|**参数** |**描述** |
+|----------------------------|---------------------------------------------|
+| count | 内存块数量 |
+| size | 内存块容量 |
+|**返回** | —— |
+| 指向第一个内存块地址的指针 | 成功 ,并且所有分配的内存块都被初始化成零。 |
+| RT_NULL | 分配失败 |
+
+#### 设置内存钩子函数
+
+在分配内存块过程中,用户可设置一个钩子函数,调用的函数接口如下:
+
+```c
+void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size));
+```
+
+设置的钩子函数会在内存分配完成后进行回调。回调时,会把分配到的内存块地址和大小做为入口参数传递进去。下表描述了该函数的输入参数:
+
+ rt_malloc_sethook() 的输入参数
+
+|**参数**|**描述** |
+|----------|--------------|
+| hook | 钩子函数指针 |
+
+其中 hook 函数接口如下:
+
+```c
+void hook(void *ptr, rt_size_t size);
+```
+
+下表描述了 hook 函数的输入参数:
+
+ 分配钩子 hook 函数接口参数
+
+|**参数**|**描述** |
+|----------|----------------------|
+| ptr | 分配到的内存块指针 |
+| size | 分配到的内存块的大小 |
+
+在释放内存时,用户可设置一个钩子函数,调用的函数接口如下:
+
+```c
+void rt_free_sethook(void (*hook)(void *ptr));
+```
+
+设置的钩子函数会在调用内存释放完成前进行回调。回调时,释放的内存块地址会做为入口参数传递进去(此时内存块并没有被释放)。下表描述了该函数的输入参数:
+
+ rt_free_sethook() 的输入参数
+
+|**参数**|**描述** |
+|----------|--------------|
+| hook | 钩子函数指针 |
+
+其中 hook 函数接口如下:
+
+```c
+void hook(void *ptr);
+```
+
+下表描述了 hook 函数的输入参数:
+
+ 钩子函数 hook 的输入参数
+
+|**参数**|**描述** |
+|----------|--------------------|
+| ptr | 待释放的内存块指针 |
+
+### 内存堆管理应用示例
+
+这是一个内存堆的应用示例,这个程序会创建一个动态的线程,这个线程会动态申请内存并释放,每次申请更大的内存,当申请不到的时候就结束,如下代码所示:
+
+ 内存堆管理
+
+```c
+#include
+
+#define THREAD_PRIORITY 25
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+
+/* 线程入口 */
+void thread1_entry(void *parameter)
+{
+ int i;
+ char *ptr = RT_NULL; /* 内存块的指针 */
+
+ for (i = 0; ; i++)
+ {
+ /* 每次分配 (1 << i) 大小字节数的内存空间 */
+ ptr = rt_malloc(1 << i);
+
+ /* 如果分配成功 */
+ if (ptr != RT_NULL)
+ {
+ rt_kprintf("get memory :%d byte\n", (1 << i));
+ /* 释放内存块 */
+ rt_free(ptr);
+ rt_kprintf("free memory :%d byte\n", (1 << i));
+ ptr = RT_NULL;
+ }
+ else
+ {
+ rt_kprintf("try to get %d byte memory failed!\n", (1 << i));
+ return;
+ }
+ }
+}
+
+int dynmem_sample(void)
+{
+ rt_thread_t tid = RT_NULL;
+
+ /* 创建线程 1 */
+ tid = rt_thread_create("thread1",
+ thread1_entry, RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY,
+ THREAD_TIMESLICE);
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+
+ return 0;
+}
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(dynmem_sample, dynmem sample);
+```
+
+仿真运行结果如下:
+
+```
+\ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 24 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >dynmem_sample
+msh >get memory :1 byte
+free memory :1 byte
+get memory :2 byte
+free memory :2 byte
+…
+get memory :16384 byte
+free memory :16384 byte
+get memory :32768 byte
+free memory :32768 byte
+try to get 65536 byte memory failed!
+```
+
+例程中分配内存成功并打印信息;当试图申请 65536 byte 即 64KB 内存时,由于 RAM 总大小只有 64K,而可用 RAM 小于 64K,所以分配失败。
+
+内存池
+------
+
+内存堆管理器可以分配任意大小的内存块,非常灵活和方便。但其也存在明显的缺点:一是分配效率不高,在每次分配时,都要空闲内存块查找;二是容易产生内存碎片。为了提高内存分配的效率,并且避免内存碎片,RT-Thread 提供了另外一种内存管理方法:内存池(Memory Pool)。
+
+内存池是一种内存分配方式,用于分配大量大小相同的小内存块,它可以极大地加快内存分配与释放的速度,且能尽量避免内存碎片化。此外,RT-Thread 的内存池支持线程挂起功能,当内存池中无空闲内存块时,申请线程会被挂起,直到内存池中有新的可用内存块,再将挂起的申请线程唤醒。
+
+内存池的线程挂起功能非常适合需要通过内存资源进行同步的场景,例如播放音乐时,播放器线程会对音乐文件进行解码,然后发送到声卡驱动,从而驱动硬件播放音乐。
+
+
+
+如上图所示,当播放器线程需要解码数据时,就会向内存池请求内存块,如果内存块已经用完,线程将被挂起,否则它将获得内存块以放置解码的数据;
+
+而后播放器线程把包含解码数据的内存块写入到声卡抽象设备中 (线程会立刻返回,继续解码出更多的数据);
+
+当声卡设备写入完成后,将调用播放器线程设置的回调函数,释放写入的内存块,如果在此之前,播放器线程因为把内存池里的内存块都用完而被挂起的话,那么这时它将被将唤醒,并继续进行解码。
+
+### 内存池工作机制
+
+#### 内存池控制块
+
+内存池控制块是操作系统用于管理内存池的一个数据结构,它会存放内存池的一些信息,例如内存池数据区域开始地址,内存块大小和内存块列表等,也包含内存块与内存块之间连接用的链表结构,因内存块不可用而挂起的线程等待事件集合等。
+
+在 RT-Thread 实时操作系统中,内存池控制块由结构体 struct rt_mempool 表示。另外一种 C 表达方式 rt_mp_t,表示的是内存块句柄,在 C 语言中的实现是指向内存池控制块的指针,详细定义情况见以下代码:
+
+```c
+struct rt_mempool
+{
+ struct rt_object parent;
+
+ void *start_address; /* 内存池数据区域开始地址 */
+ rt_size_t size; /* 内存池数据区域大小 */
+
+ rt_size_t block_size; /* 内存块大小 */
+ rt_uint8_t *block_list; /* 内存块列表 */
+
+ /* 内存池数据区域中能够容纳的最大内存块数 */
+ rt_size_t block_total_count;
+ /* 内存池中空闲的内存块数 */
+ rt_size_t block_free_count;
+ /* 因为内存块不可用而挂起的线程列表 */
+ rt_list_t suspend_thread;
+ /* 因为内存块不可用而挂起的线程数 */
+ rt_size_t suspend_thread_count;
+};
+typedef struct rt_mempool* rt_mp_t;
+```
+
+#### 内存块分配机制
+
+内存池在创建时先向系统申请一大块内存,然后分成同样大小的多个小内存块,小内存块直接通过链表连接起来(此链表也称为空闲链表)。每次分配的时候,从空闲链表中取出链头上第一个内存块,提供给申请者。从下图中可以看到,物理内存中允许存在多个大小不同的内存池,每一个内存池又由多个空闲内存块组成,内核用它们来进行内存管理。当一个内存池对象被创建时,内存池对象就被分配给了一个内存池控制块,内存控制块的参数包括内存池名,内存缓冲区,内存块大小,块数以及一个等待线程队列。
+
+
+
+内核负责给内存池分配内存池控制块,它同时也接收用户线程的分配内存块申请,当获得这些信息后,内核就可以从内存池中为内存池分配内存。内存池一旦初始化完成,内部的内存块大小将不能再做调整。
+
+每一个内存池对象由上述结构组成,其中 suspend_thread 形成了一个申请线程等待列表,即当内存池中无可用内存块,并且申请线程允许等待时,申请线程将挂起在 suspend_thread 链表上。
+
+### 内存池的管理方式
+
+内存池控制块是一个结构体,其中含有内存池相关的重要参数,在内存池各种状态间起到纽带的作用。内存池的相关接口如下图所示,对内存池的操作包含:创建 / 初始化内存池、申请内存块、释放内存块、删除 / 脱离内存池,但不是所有的内存池都会被删除,这与设计者的需求相关,但是使用完的内存块都应该被释放。
+
+
+
+#### 创建和删除内存池
+
+创建内存池操作将会创建一个内存池对象并从堆上分配一个内存池。创建内存池是从对应内存池中分配和释放内存块的先决条件,创建内存池后,线程便可以从内存池中执行申请、释放等操作。创建内存池使用下面的函数接口,该函数返回一个已创建的内存池对象。
+
+```c
+rt_mp_t rt_mp_create(const char* name,
+ rt_size_t block_count,
+ rt_size_t block_size);
+```
+
+使用该函数接口可以创建一个与需求的内存块大小、数目相匹配的内存池,前提当然是在系统资源允许的情况下(最主要的是内存堆内存资源)才能创建成功。创建内存池时,需要给内存池指定一个名称。然后内核从系统中申请一个内存池对象,然后从内存堆中分配一块由块数目和块大小计算得来的内存缓冲区,接着初始化内存池对象,并将申请成功的内存缓冲区组织成可用于分配的空闲块链表。下表描述了该函数的输入参数与返回值:
+
+ rt_mp_create() 的输入参数和返回值
+
+|**参数** |**描述** |
+|--------------|--------------------|
+| name | 内存池名 |
+| block_count | 内存块数量 |
+| block_size | 内存块容量 |
+|**返回** | —— |
+| 内存池的句柄 | 创建内存池对象成功 |
+| RT_NULL | 创建失败 |
+
+删除内存池将删除内存池对象并释放申请的内存。使用下面的函数接口:
+
+```c
+rt_err_t rt_mp_delete(rt_mp_t mp);
+```
+
+删除内存池时,会首先唤醒等待在该内存池对象上的所有线程(返回 -RT_ERROR),然后再释放已从内存堆上分配的内存池数据存放区域,然后删除内存池对象。下表描述了该函数的输入参数与返回值:
+
+ rt_mp_delete() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|-----------------------------------|
+| mp | rt_mp_create 返回的内存池对象句柄 |
+|**返回**| —— |
+| RT_EOK | 删除成功 |
+
+#### 初始化和脱离内存池
+
+初始化内存池跟创建内存池类似,只是初始化内存池用于静态内存管理模式,内存池控制块来源于用户在系统中申请的静态对象。另外与创建内存池不同的是,此处内存池对象所使用的内存空间是由用户指定的一个缓冲区空间,用户把缓冲区的指针传递给内存池控制块,其余的初始化工作与创建内存池相同。函数接口如下:
+
+```c
+rt_err_t rt_mp_init(rt_mp_t mp,
+ const char* name,
+ void *start, rt_size_t size,
+ rt_size_t block size);
+```
+
+初始化内存池时,把需要进行初始化的内存池对象传递给内核,同时需要传递的还有内存池用到的内存空间,以及内存池管理的内存块数目和块大小,并且给内存池指定一个名称。这样,内核就可以对该内存池进行初始化,将内存池用到的内存空间组织成可用于分配的空闲块链表。下表描述了该函数的输入参数与返回值:
+
+ rt_mp_init() 的输入参数和返回值
+
+|**参数** |**描述** |
+|-------------|--------------------|
+| mp | 内存池对象 |
+| name | 内存池名 |
+| start | 内存池的起始位置 |
+| size | 内存池数据区域大小 |
+| block_size | 内存块容量 |
+|**返回** | —— |
+| RT_EOK | 初始化成功 |
+| \- RT_ERROR | 失败 |
+
+内存池块个数 = size / (block_size + 4 链表指针大小),计算结果取整数。
+
+例如:内存池数据区总大小 size 设为 4096 字节,内存块大小 block_size 设为 80 字节;则申请的内存块个数为 4096/ (80+4)= 48 个。
+
+脱离内存池将把内存池对象从内核对象管理器中脱离。脱离内存池使用下面的函数接口:
+
+```c
+rt_err_t rt_mp_detach(rt_mp_t mp);
+```
+
+使用该函数接口后,内核先唤醒所有等待在该内存池对象上的线程,然后将内存池对象从内核对象管理器中脱离。下表描述了该函数的输入参数与返回值:
+
+ rt_mp_detach() 的输入参数和返回值
+
+|**参数**|**描述** |
+|----------|------------|
+| mp | 内存池对象 |
+|**返回**| —— |
+| RT_EOK | 成功 |
+
+#### 分配和释放内存块
+
+从指定的内存池中分配一个内存块,使用如下接口:
+
+```c
+void *rt_mp_alloc (rt_mp_t mp, rt_int32_t time);
+```
+
+其中 time 参数的含义是申请分配内存块的超时时间。如果内存池中有可用的内存块,则从内存池的空闲块链表上取下一个内存块,减少空闲块数目并返回这个内存块;如果内存池中已经没有空闲内存块,则判断超时时间设置:若超时时间设置为零,则立刻返回空内存块;若等待时间大于零,则把当前线程挂起在该内存池对象上,直到内存池中有可用的自由内存块,或等待时间到达。下表描述了该函数的输入参数与返回值:
+
+ rt_mp_alloc() 的输入参数和返回值
+
+|**参数** |**描述** |
+|------------------|------------|
+| mp | 内存池对象 |
+| time | 超时时间 |
+|**返回** | —— |
+| 分配的内存块地址 | 成功 |
+| RT_NULL | 失败 |
+
+任何内存块使用完后都必须被释放,否则会造成内存泄露,释放内存块使用如下接口:
+
+```c
+void rt_mp_free (void *block);
+```
+
+使用该函数接口时,首先通过需要被释放的内存块指针计算出该内存块所在的(或所属于的)内存池对象,然后增加内存池对象的可用内存块数目,并把该被释放的内存块加入空闲内存块链表上。接着判断该内存池对象上是否有挂起的线程,如果有,则唤醒挂起线程链表上的首线程。下表描述了该函数的输入参数:
+
+ rt_mp_free() 的输入参数
+
+|**参数**|**描述** |
+|----------|------------|
+| block | 内存块指针 |
+
+### 内存池应用示例
+
+这是一个静态内内存池的应用例程,这个例程会创建一个静态的内存池对象,2 个动态线程。一个线程会试图从内存池中获得内存块,另一个线程释放内存块内存块,如下代码所示:
+
+ 内存池使用示例
+
+```c
+#include
+
+static rt_uint8_t *ptr[50];
+static rt_uint8_t mempool[4096];
+static struct rt_mempool mp;
+
+#define THREAD_PRIORITY 25
+#define THREAD_STACK_SIZE 512
+#define THREAD_TIMESLICE 5
+
+/* 指向线程控制块的指针 */
+static rt_thread_t tid1 = RT_NULL;
+static rt_thread_t tid2 = RT_NULL;
+
+/* 线程 1 入口 */
+static void thread1_mp_alloc(void *parameter)
+{
+ int i;
+ for (i = 0 ; i < 50 ; i++)
+ {
+ if (ptr[i] == RT_NULL)
+ {
+ /* 试图申请内存块 50 次,当申请不到内存块时,
+ 线程 1 挂起,转至线程 2 运行 */
+ ptr[i] = rt_mp_alloc(&mp, RT_WAITING_FOREVER);
+ if (ptr[i] != RT_NULL)
+ rt_kprintf("allocate No.%d\n", i);
+ }
+ }
+}
+
+/* 线程 2 入口,线程 2 的优先级比线程 1 低,应该线程 1 先获得执行。*/
+static void thread2_mp_release(void *parameter)
+{
+ int i;
+
+ rt_kprintf("thread2 try to release block\n");
+ for (i = 0; i < 50 ; i++)
+ {
+ /* 释放所有分配成功的内存块 */
+ if (ptr[i] != RT_NULL)
+ {
+ rt_kprintf("release block %d\n", i);
+ rt_mp_free(ptr[i]);
+ ptr[i] = RT_NULL;
+ }
+ }
+}
+
+int mempool_sample(void)
+{
+ int i;
+ for (i = 0; i < 50; i ++) ptr[i] = RT_NULL;
+
+ /* 初始化内存池对象 */
+ rt_mp_init(&mp, "mp1", &mempool[0], sizeof(mempool), 80);
+
+ /* 创建线程 1:申请内存池 */
+ tid1 = rt_thread_create("thread1", thread1_mp_alloc, RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY, THREAD_TIMESLICE);
+ if (tid1 != RT_NULL)
+ rt_thread_startup(tid1);
+
+
+ /* 创建线程 2:释放内存池 */
+ tid2 = rt_thread_create("thread2", thread2_mp_release, RT_NULL,
+ THREAD_STACK_SIZE,
+ THREAD_PRIORITY + 1, THREAD_TIMESLICE);
+ if (tid2 != RT_NULL)
+ rt_thread_startup(tid2);
+
+ return 0;
+}
+
+/* 导出到 msh 命令列表中 */
+MSH_CMD_EXPORT(mempool_sample, mempool sample);
+```
+
+仿真运行结果如下:
+
+```
+ \ | /
+- RT - Thread Operating System
+ / | \ 3.1.0 build Aug 24 2018
+ 2006 - 2018 Copyright by rt-thread team
+msh >mempool_sample
+msh >allocate No.0
+allocate No.1
+allocate No.2
+allocate No.3
+allocate No.4
+…
+allocate No.46
+allocate No.47
+thread2 try to release block
+release block 0
+allocate No.48
+release block 1
+allocate No.49
+release block 2
+release block 3
+release block 4
+release block 5
+…
+release block 47
+release block 48
+release block 49
+```
+
+本例程在初始化内存池对象时,初始化了 4096 /(80+4) = 48 个内存块。
+
+①线程 1 申请了 48 个内存块之后,此时内存块已经被用完,需要其他地方释放才能再次申请;但此时,线程 1 以一直等待的方式又申请了 1 个,由于无法分配,所以线程 1 挂起;
+
+②线程 2 开始执行释放内存的操作;当线程 2 释放一个内存块的时候,就有一个内存块空闲出来,唤醒线程 1 申请内存,申请成功后再申请,线程 1 又挂起,再循环一次②;
+
+③线程 2 继续释放剩余的内存块,释放完毕。
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/netdev/figures/netdev.jpg b/rt-thread-version/rt-thread-standard/programming-manual/netdev/figures/netdev.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..73559dda3b128b8a167d15163c58a04e6b638c60
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/netdev/figures/netdev.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev.md b/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev.md
new file mode 100644
index 0000000000000000000000000000000000000000..2cd28faa7dd3677e831cc96acd9564c55cdb57b5
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev.md
@@ -0,0 +1,607 @@
+# netdev 网卡
+
+## 介绍
+
+netdev(network interface device),即网络接口设备,又称网卡。每一个用于网络连接的设备都可以注册成网卡,为了适配更多的种类的网卡,避免系统中对单一网卡的依赖,RT-Thread 系统提供了 netdev 组件用于网卡管理和控制。
+
+netdev 组件主要作用是**解决设备多网卡连接时网络连接问题,用于统一管理各个网卡信息与网络连接状态,并且提供统一的网卡调试命令接口**。 其主要功能特点如下所示:
+
+- 抽象网卡概念,每个网络连接设备可注册唯一网卡。
+- 提供多种网络连接信息查询,方便用户实时获取当前网卡网络状态;
+- 建立网卡列表和默认网卡,可用于网络连接的切换;
+- 提供多种网卡操作接口(设置 IP、DNS 服务器地址,设置网卡状态等);
+- 统一管理网卡调试命令(ping、ifconfig、netstat、dns 等命令);
+
+## 工作原理
+
+### 网卡概念
+
+网卡概念介绍之前先了解协议栈相关概念,协议栈是指网络中各层协议的总和,每种协议栈反映了不同的网络数据交互方式,RT-Thread 系统中目前支持三种协议栈类型: lwIP 协议栈、AT Socket 协议栈、WIZnet TCP/IP硬件协议栈。每种协议栈对应一种协议簇类型(family),上述协议栈分别对应的协议簇类型为:AF_INET、AF_AT、AF_WIZ。
+
+网卡的初始化和注册建立在协议簇类型上,所以每种网卡对应唯一的协议簇类型。 Socket 套接字描述符的创建建立在 netdev 网卡基础上,所以每个创建的 Socket 对应唯一的网卡。协议簇、网卡和 socket 之间关系如下图所示:
+
+
+
+每个网卡对应唯一的网卡结构体对象,其中包含该网卡的主要信息和实时状态,用于后面网卡信息的获取和设置,如下为网卡结构体对象参数介绍:
+
+```c
+/* 网卡结构体对象 */
+struct netdev
+{
+ rt_slist_t list; /* 网卡列表 */
+
+ char name[RT_NAME_MAX]; /* 网卡名称 */
+ ip_addr_t ip_addr; /* IP 地址 */
+ ip_addr_t netmask; /* 子网掩码地址 */
+ ip_addr_t gw; /* 网关地址 */
+ ip_addr_t dns_servers[NETDEV_DNS_SERVERS_NUM]; /* DNS 服务器地址 */
+ uint8_t hwaddr_len; /* 硬件地址长度 */
+ uint8_t hwaddr[NETDEV_HWADDR_MAX_LEN]; /* 硬件地址 */
+
+ uint16_t flags; /* 网卡状态位 */
+ uint16_t mtu; /* 网卡最大传输单元 */
+ const struct netdev_ops *ops; /* 网卡操作回调函数 */
+
+ netdev_callback_fn status_callback; /* 网卡状态改变回调 */
+ netdev_callback_fn addr_callback; /* 网卡地址改变回调 */
+
+#ifdef RT_USING_SAL
+ void *sal_user_data; /* 网卡中协议簇相关参数数据 */
+#endif /* RT_USING_SAL */
+ void *user_data; /* 预留用户数据 */
+};
+
+```
+
+### 网卡状态
+
+netdev 组件提供对网卡网络状态的管理和控制,其类型主要包括下面四种:up/down、link_up/link_down、internet_up/internet_down、dhcp_enable/dhcp_disable。
+
+- **up/down:** 底层网卡初始化完成之后置为 up 状态,用于判断网卡开启还是禁用。
+- **link_up/link_down:** 用于判断网卡设备是否具有有效的链路连接,连接后可以与其他网络设备进行通信。该状态一般由网卡底层驱动设置。
+- **internet_up/internet_down:** 用于判断设备是否连接到因特网,接入后可以与外网设备进行通信。
+- **dhcp_enable/dhcp_disable:** 用于判断当前网卡设备是否开启 DHCP 功能支持。
+
+其中`up/down` 状态以及 `dhcp_enable/dhcp_disable` 状态可以通过 netdev 组件提供的接口设置,可以在应用层控制。其他状态是由网卡底层驱动或者 netdev 组件根据当前网卡网络连接情况自动设置。
+
+### 默认网卡和网卡列表
+
+为了方便网卡的管理和控制,netdev 组件中提供网卡列表用于统一管理各个网卡设备,系统中每个网卡在初始化是会创建和注册网卡对象到 netdev 组件网卡列表中。
+
+网卡列表中有且只有一个**默认网卡**,一般为系统中第一个注册的网卡,可以通过 `netdev_set_default()` 函数设置默认网卡,默认网卡的主要作用是确定优先使用的进行网络通讯的网卡类型,方便网卡的切换和网卡信息的获取。
+
+## 配置选项
+
+当我们使用 netdev 组件时需要在 `rtconfig.h` 中定义如下宏定义:
+
+| **宏定义** | **描述** |
+| ------------------------- | ---------------------------- |
+| RT_USING_NETDEV | 开启 netdev 功能 |
+| NETDEV_USING_IFCONFIG | 开启 ifconfig 命令支持 |
+| NETDEV_USING_PING | 开启 ping 命令支持 |
+| NETDEV_USING_NETSTAT | 开启 netstat 命令支持 |
+| NETDEV_USING_AUTO_DEFAULT | 开启默认网卡自动切换功能支持 |
+
+上面配置选项可以直接在 `rtconfig.h` 文件中添加使用,也可以通过组件包管理工具 ENV 配置选项加入,ENV 工具中具体配置路径如下:
+
+```c
+RT-Thread Components --->
+ Network --->
+ Network interface device --->
+ [*] Enable network interface device
+ [*] Enable ifconfig features
+ [*] Enable ping features
+ [*] Enable netstat features
+ [*] Enable default netdev automatic change features
+```
+
+配置完成可以通过 scons 命令重新生成功能,完成 netdev 组件的添加。
+
+## 使用方式
+
+### 头文件定义
+
+使用下面 netdev 网卡功能相关操作函数,需要包含如下头文件:
+
+```c
+#include /* 包含 ip_addr_t 等地址相关的头文件 */
+#include /* 包含全部的 netdev 相关操作接口函数 */
+```
+
+### 网卡注册和获取
+
+**注册网卡**
+
+每一个网卡在初始化完成之后,需要调用网卡注册函数注册网卡到网卡列表中,注册网卡的接口如下所示:
+
+```c
+int netdev_register(struct netdev *netdev, const char *name, void *user_data);
+```
+
+| **参数** | **描述** |
+| --------- | ------------ |
+| netdev | 网卡对象 |
+| name | 网卡名称 |
+| user_data | 用户使用数据 |
+| **返回** | **——** |
+| 0 | 网卡注册成功 |
+| -1 | 网卡注册失败 |
+
+该函数不需要在用户层调用,一般为网卡驱动初始化完成之后自动调用,如 esp8266 网卡的注册在 esp8266 设备网络初始化之后自动完成。
+
+**注销网卡**
+
+该函数可以在网卡使用时,注销网卡的注册,即从网卡列表中删除对应网卡,注销网卡的接口如下所示:
+
+```c
+int netdev_unregister(struct netdev *netdev);
+```
+
+| **参数** | **描述** |
+| -------- | ------------ |
+| netdev | 网卡对象 |
+| **返回** | **——** |
+| 0 | 网卡注销成功 |
+| -1 | 网卡注销失败 |
+
+**通过状态获取第一个匹配的网卡对象**
+
+如果想要通过指定传入状态匹配默认的网卡,可以调用如下函数:
+
+```c
+struct netdev *netdev_get_first_by_flags(uint16_t flags);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------- |
+| flags | 指定匹配的状态 |
+| **返回** | **——** |
+| != NULL | 获取网卡对象成功 |
+| NULL | 获取网卡对象失败 |
+
+可以用于匹配网卡的状态如下所示:
+
+| 状态 | 描述 |
+| ----------------------- | ---------------------- |
+| NETDEV_FLAG_UP | 网卡 up 状态 |
+| NETDEV_FLAG_LINK_UP | 网卡 link_up 状态 |
+| NETDEV_FLAG_INTERNET_UP | 网卡外网连接状态 |
+| NETDEV_FLAG_DHCP | 网卡 DHCP 功能开启状态 |
+
+**获取第一个指定协议簇类型的网卡对象**
+
+每个网卡对应唯一的协议簇类型(family),如下函数可以通过指定的协议簇类型获取网卡列表中第一个网卡对象:
+
+```c
+struct netdev *netdev_get_by_family(int family);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------- |
+| family | 协议簇类型 |
+| **返回** | **——** |
+| != NULL | 获取网卡对象成功 |
+| NULL | 获取网卡对象失败 |
+
+目前 RT-Thread 系统中支持的协议簇类型有如下几种:
+
+| 协议簇类型 | 介绍 |
+| ---------- | --------------------------------------- |
+| AF_INET | 用于使用 lwIP 协议栈的网卡 |
+| AF_AT | 用于使用 AT Socket 协议栈的网卡 |
+| AF_WIZ | 用于使用 WIZnet TCP/IP 硬件协议栈的网卡 |
+
+该函数主要用于指定协议簇网卡操作,以及多网卡环境下,同协议簇网卡之间的切换的情况。
+
+**通过 IP 地址获取网卡对象**
+
+每个网卡中都包含该网卡的基本信息如 :IP 地址,网关和子网掩码等,下面函数可以通过 IP 地址获取网卡对象:
+
+```c
+struct netdev *netdev_get_by_ipaddr(ip_addr_t *ip_addr);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------- |
+| ip_addr | IP 地址对象 |
+| **返回** | **——** |
+| != NULL | 获取网卡对象成功 |
+| NULL | 获取网卡对象失败 |
+
+该函数主要用于 bind 函数绑定指定 IP 地址时获取网卡状态信息的情况。
+
+> 可以通过 inet_aton () 函数将 IP 地址从字符串格式转化为 ip_addr_t 类型。
+
+**通过名称获取网卡对象**
+
+每个网卡中有唯一的网卡名称,可以通过网卡名称获取网卡对象:
+
+```c
+struct netdev *netdev_get_by_name(const char *name);
+```
+
+| **参数** | **描述** |
+| -------- | ---------------- |
+| name | 网卡名称 |
+| **返回** | **——** |
+| != NULL | 获取网卡对象成功 |
+| NULL | 获取网卡对象失败 |
+
+该函数为应用层常用获取网卡对象接口,当前网卡列表中名称可通过 `ifconfig` 命令查看。
+
+### 设置网卡信息
+
+**设置默认网卡**
+
+系统中注册的网卡通过统一网卡列表管理,并且提供唯一的默认网卡,下面函数可以用于切换默认网卡:
+
+```c
+void netdev_set_default(struct netdev *netdev);
+```
+
+**设置网卡 up/down 状态**
+
+网卡 up/down 状态用于控制网卡状态是否能使用。
+
+如下函数用于启用网卡:
+
+```c
+int netdev_set_up(struct netdev *netdev);
+```
+
+ 如下函数用于禁用网卡:
+
+```c
+int netdev_set_down(struct netdev *netdev);
+```
+
+禁用网卡之后,该网卡上对应的 Socket 套接字将无法进行数据通讯,并且将无法在该网卡上创建和绑定 Socket 套接字,直到网卡状态设置为 up 状态。
+
+**设置网卡 DHCP 功能状态**
+
+DHCP 即动态主机配置协议,如果开启该网卡 DHCP 功能将无法设置该网卡 IP 、网关和子网掩码地址等信息,如果关闭该功能则可以设置上述信息。下面函数可以用于控制开启或关闭网卡 DHCP 功能:
+
+```c
+int netdev_dhcp_enabled(struct netdev *netdev, rt_bool_t is_enabled);
+```
+
+| **参数** | **描述** |
+| ---------- | ---------------------------------- |
+| netdev | 网卡对象 |
+| is_enabled | 是否开启功能(RT_TRUE / RT_FALSE) |
+| **返回** | **——** |
+| 0 | 设置 DHCP 功能状态成功 |
+| < 0 | 设置 DHCP 功能状态失败 |
+
+部分网卡不支持设置 DHCP 状态功能,如 M26、EC20 等 GRPS 模块,在调用该函数时会报错提示。
+
+**设置网卡地址信息**
+
+下面函数可以用于设置指定网卡地址 IP 、网关和子网掩码地址,需要在网卡关闭 DHCP 功能状态使用。
+
+```c
+/* 设置网卡 IP 地址 */
+int netdev_set_ipaddr(struct netdev *netdev, const ip_addr_t *ipaddr);
+```
+
+```c
+/* 设置网卡网关地址 */
+int netdev_set_gw(struct netdev *netdev, const ip_addr_t *gw);
+```
+
+```c
+/* 设置网卡子网掩码地址 */
+int netdev_set_netmask(struct netdev *netdev, const ip_addr_t *netmask);
+```
+
+| **参数** | **描述** |
+| ----------------- | ------------------------------- |
+| netdev | 网卡对象 |
+| ipaddr/gw/netmask | 需要设置的 IP/网关/子网掩码地址 |
+| **返回** | **——** |
+| 0 | 设置地址信息成功 |
+| < 0 | 设置地址信息失败 |
+
+下面函数可以用于设置网卡 DNS 服务器地址,主要用于网卡域名解析功能。
+
+```c
+int netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, const ip_addr_t *dns_server);
+```
+| **参数** | **描述** |
+| ---------- | ------------------------- |
+| netdev | 网卡对象 |
+| dns_num | 需要设置的 DNS 服务器 |
+| dns_server | 需要设置的 DNS 服务器地址 |
+| **返回** | **——** |
+| 0 | 设置地址信息成功 |
+| < 0 | 设置地址信息失败 |
+
+**设置网卡回调函数**
+
+下面函数可以用于设备网卡状态改变时调用的回调函数,状态的改变包括:`up/down`、 `link_up/link_down`、`internet_up/internet_down`、`dhcp_enable/dhcp_disable` 等。
+
+```c
+typedef void (*netdev_callback_fn )(struct netdev *netdev, enum netdev_cb_type type);
+void netdev_set_status_callback(struct netdev *netdev, netdev_callback_fn status_callback);
+```
+
+| **参数** | **描述** |
+| --------------- | -------------------- |
+| netdev | 网卡对象 |
+| status_callback | 状态改变回调函数指针 |
+| **返回** | **——** |
+| 无 | 无 |
+
+下面函数可以用于设置网卡地址信息改变时调用的回调函数,地址的改变包括:IP、子网掩码、网关、DNS 服务器等地址。
+
+```c
+void netdev_set_addr_callback(struct netdev *netdev, netdev_callback_fn addr_callback)
+```
+
+| **参数** | **描述** |
+| ------------- | -------------------- |
+| netdev | 网卡对象 |
+| addr_callback | 地址改变回调函数指针 |
+| **返回** | **——** |
+| 无 | 无 |
+
+### 获取网卡信息
+
+**判断网卡是否为 up 状态**
+
+```c
+#define netdev_is_up(netdev)
+```
+
+**判断网卡是否为 link_up 状态**
+
+```c
+#define netdev_is_link_up(netdev)
+```
+
+**判断网卡是否为 internet_up 状态**
+
+```c
+#define netdev_is_internet_up(netdev)
+```
+
+**判断网卡 DHCP 功能是否开启**
+
+```c
+#define netdev_is_dhcp_enable(netdev)
+```
+
+### 默认网卡自动切换
+
+menuconfig 中配置开启如下选项,可使能默认网卡自动切换功能:
+
+```c
+[*] Enable default netdev automatic change features
+```
+
+ 单网卡模式下,开启和关闭默认网卡自动切换功能无明显效果。
+
+多网卡模式下,如果开启默认网卡自动切换功能,当前默认网卡状态改变为 down 或 link_down 时,默认网卡会切换到网卡列表中第一个状态为 up 和 link_up 的网卡。这样可以使一个网卡断开后快速切换到另一个可用网卡,简化用户应用层网卡切换操作。如果未开启该功能,则不会自动切换默认网卡。
+
+## 应用示例
+
+### FinSH 命令
+
+为了方便用户多网卡环境下网卡的管理和控制,netdev 组件提供多种网络调试命令,常用的 FinSH 命令如下表所示:
+
+| FinSH 命令 | 描述 |
+| ---------- | ------------------------------------------------------ |
+| ping | 用于检查网络是否连通 |
+| ifconfig | 用于显示和配置网卡信息,如设置 IP 、网关和子网掩码地址 |
+| dns | 用于显示和配置网卡 DNS 服务器地址 |
+| netstat | 用于查看各网卡网络连接信息和端口使用情况 |
+
+使用 `ping` 命令测试网络连接情况,运行结果如下所示:
+
+```c
+msh />ping rt-thread.org
+60 bytes from 118.31.15.152 icmp_seq=0 ttl=52 time=13 ms
+60 bytes from 118.31.15.152 icmp_seq=1 ttl=52 time=13 ms
+60 bytes from 118.31.15.152 icmp_seq=2 ttl=52 time=14 ms
+60 bytes from 118.31.15.152 icmp_seq=3 ttl=52 time=14 ms
+```
+
+- 该命令默认使用网卡列表中默认网卡发送 ping 协议数据,如果默认网卡状态为 down 或者 link_down,则使用网卡列表中第一个 up 和 link_up 状态的网卡发送 ping 协议请求数据。
+
+使用 `ifconfig ` 命令查询和设置网卡信息,运行结果如下所示:
+
+```c
+msh />ifconfig /* 显示全部网卡信息 */
+network interface device: w0 (Default)
+MTU: 1500
+MAC: 98 3b 16 55 9a 87
+FLAGS: UP LINK_UP INTERNET_UP DHCP_DISABLE ETHARP BROADCAST IGMP
+ip address: 192.168.12.92
+gw address: 192.168.10.1
+net mask : 255.255.0.0
+dns server #0: 192.168.10.1
+dns server #1: 223.5.5.5
+msh />
+msh />ifconfig w0 192.168.12.93 192.168.10.1 255.255.0.0 /* 设置指定网卡 IP 地址*/
+config : w0
+IP addr: 192.168.12.93
+Gateway: 192.168.10.1
+netmask: 255.255.0.0
+```
+
+- `ifconfig` 命令会顺序显示网卡列表中全部网卡的信息;
+- 网卡列表中默认网卡命令后会添加 `default` 标识;
+- `ifconfig` 命令设置网卡信息功能,只能在网卡 DHCP 功能关闭时调用,否则会报错。
+
+使用 `dns` 命令查询和设置网卡 DNS 服务器地址,运行结果如下所示:
+
+```c
+msh />dns /* 显示全部网卡 DNS 服务器地址 */
+network interface device: w0 (Default)
+dns server #0: 192.168.10.1
+dns server #1: 223.5.5.5
+msh />
+msh />dns w0 192.168.12.1 /* 设置指定网卡 DNS 服务器地址 */
+set network interface device(w0) dns server #0: 192.168.12.1
+```
+
+- `dns` 命令会顺序显示网卡列表中全部网卡的 DNS 服务器地址信息。
+- 目前每个网卡只同时支持 2 个 DNS 服务器地址。
+
+使用 `netstat` 命令可以网卡下网络连接状态和端口使用,运行效果如下所示:
+
+```c
+msh />netstat
+Active PCB states:
+#0 192.168.12.93:49153 <==> 183.230.40.39:6002
+Listen PCB states:
+TIME-WAIT PCB states:
+Active UDP PCB states:
+#0 4 0.0.0.0:68 <==> 0.0.0.0:67
+```
+
+- 该命令可以查看当前网卡上使用的 TCP/UDP 连接的地址和端口信息。
+
+### 切换默认网卡示例
+
+在多网卡连接的情况下,可以通过设置默认网卡切换网卡。创建 Socket 连接时,使用 gethostbyname 等域名解析相关的函数时,以及在完成 ping 网络功能时,都会优先选择默认网卡。
+
+如下示例,导出命令用于切换默认网卡:
+
+```c
+#include
+#include /* 当需要网卡操作是,需要包含这两个头文件 */
+
+static int netdev_set_default_test(int argc, char **argv)
+{
+ struct netdev *netdev = RT_NULL;
+
+ if (argc != 2)
+ {
+ rt_kprintf("netdev_set_default [netdev_name] --set default network interface device.\n");
+ return -1;
+ }
+
+ /* 通过网卡名称获取网卡对象,名称可以通过 ifconfig 命令查看 */
+ netdev = netdev_get_by_name(argv[1]);
+ if (netdev == RT_NULL)
+ {
+ rt_kprintf("not find network interface device name(%s).\n", argv[1]);
+ return -1;
+ }
+
+ /* 设置默认网卡对象 */
+ netdev_set_default(netdev);
+
+ rt_kprintf("set default network interface device(%s) success.\n", argv[1]);
+ return 0;
+}
+#ifdef FINSH_USING_MSH
+#include
+/* 导出命令到 FinSH 控制台 */
+MSH_CMD_EXPORT_ALIAS(netdev_set_default_test, netdev_set_default, set default network interface device);
+#endif /* FINSH_USING_MSH */
+```
+
+### 设置网卡 up/down 状态示例
+
+网卡 up/down 状态用于设置网卡是否被禁用,下面提供导出设置网卡状态命令:
+
+```c
+#include
+#include /* 当需要网卡操作是,需要包含这两个头文件 */
+
+int netdev_set_status_test(int argc, char **argv)
+{
+ struct netdev *netdev = RT_NULL;
+
+ if (argc != 3)
+ {
+ rt_kprintf("netdev_set_status [netdev_name] [up/down] --set network interface device status.\n");
+ return -1;
+ }
+
+ /* 通过名称获取网卡对象 */
+ netdev = netdev_get_by_name(argv[1]);
+ if (netdev == RT_NULL)
+ {
+ rt_kprintf("input network interface name(%s) error.\n", argv[1]);
+ return -1;
+ }
+
+ /* 设置网卡状态为 up 或 down */
+ if (strcmp(argv[2], "up") == 0)
+ {
+ netdev_set_up(netdev);
+ rt_kprintf("set network interface device(%s) up status.\n", argv[1]);
+ }
+ else if (strcmp(argv[2], "down") == 0)
+ {
+ netdev_set_down(netdev);
+ rt_kprintf("set network interface device(%s) down status.\n", argv[1]);
+ }
+ else
+ {
+ rt_kprintf("netdev_set_status [netdev_name] [up/down].\n");
+ return -1;
+ }
+
+ return 0;
+}
+#ifdef FINSH_USING_MSH
+#include
+/* 导出命令到 FinSH 控制台 */
+MSH_CMD_EXPORT_ALIAS(netdev_set_status_test, netdev_set_status, set network interface device status);
+#endif /* FINSH_USING_MSH */
+```
+
+### 设置网卡 IP 地址示例
+
+每个网卡对应唯一的 IP 地址,可以通过 netdev 提供接口设置网卡 IP 地址,如下所示:
+
+```c
+#include
+#include /* 当需要网卡操作是,需要包含这两个头文件 */
+
+int netdev_set_ipaddr_test(int argc, char **argv)
+{
+ struct netdev *netdev = RT_NULL;
+ ip_addr_t addr;
+
+ if (argc != 3)
+ {
+ rt_kprintf("netdev_set_status [netdev_name] [ip_addr] --set network interface device IP address.\n");
+ return -1;
+ }
+
+ /* 通过名称获取网卡对象 */
+ netdev = netdev_get_by_name(argv[1]);
+ if (netdev == RT_NULL)
+ {
+ rt_kprintf("input network interface name(%s) error.\n", argv[1]);
+ return -1;
+ }
+
+ /* 设置网卡 IP 地址*/
+ inet_aton(argv[2], &addr);
+ netdev_set_ipaddr(netdev, &addr);
+
+ return 0;
+}
+#ifdef FINSH_USING_MSH
+#include
+/* 导出命令到 FinSH 控制台 */
+MSH_CMD_EXPORT_ALIAS(netdev_set_ipaddr_test, netdev_set_ipaddr, set network interface device IP
+ address);
+#endif /* FINSH_USING_MSH */
+```
+
+## 常见问题
+
+### Q: 设置 IP地址、网关和子网掩码时报错无法设置?
+
+ **A:** 设置 IP地址、网关和子网掩码时,需要确定当前网卡 DHCP 状态,只有当网卡 DHCP 状态关闭时才可以设置,可以调用 `netdev_dhcp_enabled ()` 函数设置网卡 DHCP 状态。
+
+### Q: 多网卡情况下,ping 功能或者创建 socket 时调用的网卡不是想要使用的网卡怎么办?
+
+ **A:** 首先使用 `ifconfig` 命令查看当前网卡列表中网卡,确实想要使用的网卡名称,可是使用 `netdev_set_default()` 设置默认网卡为该网卡,然后确定该网卡状态为 UP 和 LINK_UP 状态。
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-hello.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-hello.png
new file mode 100644
index 0000000000000000000000000000000000000000..aec33b859c789b99083d059002550651894d4d7e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-hello.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-layer.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-layer.png
new file mode 100644
index 0000000000000000000000000000000000000000..597e7ef386d8f2212b8656d0a22d837d4d2f934a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-layer.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-mdk.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-mdk.png
new file mode 100644
index 0000000000000000000000000000000000000000..d1b19c4af769cc2f03cc79f67bcc0827100a8d0e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-mdk.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-osi.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-osi.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2659a07fe2138b2f454748fc9d7b23c25cde069
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-osi.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-recv.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-recv.png
new file mode 100644
index 0000000000000000000000000000000000000000..c36eb6d4f3c34304e17ce501c29e032f470d907d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-recv.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-send.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-send.png
new file mode 100644
index 0000000000000000000000000000000000000000..7805a39b8f4b5fe00f14270a3be60a1f0974c89a
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-send.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-tcp-s.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-tcp-s.png
new file mode 100644
index 0000000000000000000000000000000000000000..aca0900d4acc9862d00022c8dc25c61d1b04d94d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-tcp-s.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-tcp.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-tcp.png
new file mode 100644
index 0000000000000000000000000000000000000000..1abf4d284c2a2aa95a2e3347d3550bd446dcd2fd
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-tcp.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp-client.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp-client.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0e916b4217de1204589257d85349cf8e2e0bcbc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp-client.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp-server.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp-server.png
new file mode 100644
index 0000000000000000000000000000000000000000..093e160d05cd5269704fed8a72dda61c334413f4
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp-server.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp.png
new file mode 100644
index 0000000000000000000000000000000000000000..c2cdc6da8213c30778a13990e7e340ca5b64429d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-udp.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-wireless.png b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-wireless.png
new file mode 100644
index 0000000000000000000000000000000000000000..76e26e0b52a583afb71cc491524b03d21599b664
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/network/figures/net-wireless.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/network/network.md b/rt-thread-version/rt-thread-standard/programming-manual/network/network.md
new file mode 100644
index 0000000000000000000000000000000000000000..0092084a3ad9def5d2042fb82b3a8e8f5ce0f2d6
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/network/network.md
@@ -0,0 +1,782 @@
+# RT-Thread 网络框架
+
+随着网络的普及,人们的生活越来越依赖于网络的应用,越来越多的产品需要连接互联网,设备联网已经成为一种趋势。要实现设备和网络的连接,需要遵循 TCP/IP 协议,可以在设备运行网络协议栈来联网,也可以使用设备配合自带硬件网络协议栈的接口芯片来联网。
+
+当设备连接上网络,就犹如插上了翅膀,可以利用网络实时的上传数据,用户在十万八千里之外就可以看到设备现在的运行状态和采集到的数据,并远程控制设备完成特定的任务。也可以通过设备播放网络音乐、拨打网络电话、充当局域网存储服务器等。
+
+本章将讲解 RT-Thread 网络框架的相关内容,带你了解网络框架的概念、功能特点和使用方法,读完本章,大家将熟悉 RT-Thread 网络框架的概念和实现原理、熟悉使用 Socket API 进行网络编程。
+
+## TCP/IP 网络协议简介
+
+TCP/IP(Transmission Control Protocol/Internet Protocol)是传输控制协议和网络协议的简称,它不是单个协议,而是一个协议族的统称,里面包括了 IP 协议、ICMP 协议、TCP 协议、以及 http、ftp、pop3、https 协议等,它定义了电子设备如何连入因特网,以及数据在它们之间传输的标准。
+
+### OSI 参考模型
+
+OSI(Open System Interconnect),即开放式系统互联。一般都称为 OSI 参考模型,是 ISO(国际标准化组织)组织在 1985 年研究的网络互联模型。该体系结构标准定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层和应用层),即 ISO 开放系统互连参考模型。第一层到第三层属于 [OSI 参考模型](http://baike.baidu.com/view/38361.htm) 的低三层,负责创建网络通信连接的链路;第四层到第七层为 OSI 参考模型的高四层,具体负责端到端的数据通信。在这一框架下进一步详细规定了每一层的功能,以实现开放系统环境中的互连性、互操作性和应用的可移植性。
+
+### TCP/IP 参考模型
+
+TCP/IP 通讯协议采用了 4 层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。这 4 层分别为:
+
+* 应用层:不同类型的网络应用有不同的通信规则,因此应用层的协议是多种多样的,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
+
+* 传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP 和 UDP 给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
+
+* 网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
+
+* 网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如 Ethernet、Serial Line 等)来传送数据。
+
+### TCP/IP 参考模型和 OSI 参考模型的区别
+
+下图为 TCP/IP 参考模型与 OSI 参考模型图:
+
+
+
+OSI 参考模型与 TCP/IP 参考模型都采用了分层结构,都是基于独立的协议栈的概念。OSI 参考模型有 7 层,而 TCP/IP 参考模型只有 4 层,即 TCP/IP 参考模型没有了表示层和会话层,并且把数据链路层和物理层合并为网络接口层。不过,二者的分层之间有一定的对应关系。OSI 由于体系比较复杂,而且设计先于实现,有许多设计过于理想,不太方便软件实现,因而完全实现 OSI 参考模型的系统并不多,应用的范围有限。而 TCP/IP 参考模型最早在计算机系统中实现,在 UNIX、Windows 平台中都有稳定的实现,并且提供了简单方便的编程接口(API),可以在其上开发出丰富的应用程序,因此得到了广泛的应用。TCP/IP 参考模型已成为现在网际互联的国际标准和工业标准。
+
+### IP 地址
+
+IP 地址是指互联网协议地址(Internet Protocol Address,又译为网际协议地址),是 [IP 协议](https://baike.baidu.com/item/IP%E5%8D%8F%E8%AE%AE) 提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。常见的局域网 IP 地址为 192.168.X.X。
+
+### 子网掩码
+
+子网掩码 (subnet mask) 又叫网络掩码、地址掩码、子网络遮罩,它是一种用来指明一个 IP 地址的哪些位标识的是主机所在的子网,以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合 IP 地址一起使用。子网掩码只有一个作用,就是将某个 IP 地址划分成网络地址和主机地址两部分。子网掩码为 1 的位,对应的 IP 地址为网络地址,子网掩码为 0 的位,对应的 IP 地址为主机地址。以 IP 地址 192.168.1.10 和子网掩码 255.255.255.0 为例,子网掩码前 24 位(将 10 进制转换成 2 进制)为 1,所以 IP 地址的前 24 位 192.168.1 表示网络地址,剩下的 0 为主机地址。
+
+### MAC 地址
+
+MAC(figures Access Control 或者 Medium Access Control)地址,意译为媒体访问控制,或称为物理地址、硬件地址,用来定义 [网络设备](https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E8%AE%BE%E5%A4%87/7667828) 的位置。在 [OSI 模型](https://baike.baidu.com/item/OSI%E6%A8%A1%E5%9E%8B) 中,第三层 [网络层](https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E5%B1%82/4329439) 负责[IP 地址](https://baike.baidu.com/item/IP%E5%9C%B0%E5%9D%80),第二层数据链路层则负责 MAC 地址。一个主机至少会有一个 MAC 地址。
+
+## RT-Thread 网络框架简介
+
+RT-Thread 为了能够支持各种网络协议栈,开发了 SAL 组件,全称 Socket abstraction layer,即套接字抽象层,RT-Thread 通过它可以无缝接入各类协议栈,包括几种常用的 TCP/IP 协议栈,例如嵌入式开发中常用的 LwIP 协议栈以及 RT-Thread 开发的 AT Socket 协议栈组件等,这些协议栈完成数据从网络层到传输层的转化。
+
+RT-Thread 网络框架主要功能特点如下所示:
+
+* 支持标准网络套接字 BSD Socket API,支持使用 poll/select
+
+* 抽象、统一多种网络协议栈接口
+
+* 支持各类物理网卡、网络通讯模块硬件
+
+* 资源占用小,SAL 套接字抽象层组件资源占用为 ROM 2.8K 和 RAM 0.6K
+
+RT-Thread 的网络框架采用了分层设计,共四层,每层都有不同的职责,下图为 RT-Thread 网络框架结构图:
+
+
+
+网络框架向用户应用程序提供标准 BSD Socket 接口,开发者使用 BSD Socket 接口进行操作,无需关心网络底层如何实现,也无需关心网络数据是通过哪个网络协议栈,套接字抽象层为上层应用层提供的接口有:accept、connect、send、recv 等。
+
+SAL 层之下是协议栈层,当前网络框架中支持的几个主要协议栈如下:
+
+* LwIP 是一个开源的 TCP/IP 协议栈实现,它在保持 TCP/IP 协议主要功能的基础上减少了对 RAM 的占用,这使得 LwIP 协议栈很适合在嵌入式系统中使用。
+
+* AT Socket 是给支持 AT 指令的模块使用的组件。AT 命令采用标准串口进行数据收发,将复杂的设备通讯方式转换成简单的串口编程,大大简化了产品的硬件设计和软件开发成本,这使得几乎所有的网络模组如 GPRS、3G/4G、NB-IoT、蓝牙、WiFi、GPS 等模组都很方便的接入 RT-Thread 网络框架,通过标准的 BSD Socket 方式开发网络应用,极大程度地简化上层应用的开发难度。
+
+* Socket CAN 是 CAN 编程的一种方式,它简单易用,编程顺手。通过接入 SAL 层,开发者就可以在 RT-Thread 上实现 Socket CAN 编程了。
+
+协议栈层下面是抽象设备层,通过将硬件设备抽象成以太网设备或者 AT 设备,从而将硬件设备接入到各类网络协议栈中。
+
+最底层是各式各样的网络芯片或模块(例如:W5500/CH395 这类自带协议栈的以太网芯片,带 AT 指令的 WiFi 模块、GPRS 模块、NB-IoT 模块等等),这些硬件模块是真正进行网络通信功能的承载者,负责跟各类物理网络进行通信。
+
+总体来说,RT-Thread 网络框架使开发者只需要关心和使用标准 BSD Socket 网络接口进行网络应用开发,而无需关心底层具体网络协议栈类型和实现,极大的提高了系统的兼容性,方便开发者完成网络相关应用的开发,也极大地提升了 RT-Thread 在物联网领域对于不同网络硬件的兼容性。
+
+此外,基于网络框架,RT-Thread 提供了数量丰富的网络软件包,他们是基于 SAL 层的各种网络应用,例如 Paho MQTT、WebClient、cJSON、netutils 等等,可以从在线软件包管理中心获得。这些软件包都是网络应用利器,使用它们可以大大简化网络应用的开发难度,缩短网络应用开发周期。目前网络软件包数量达十几个,下表列出了目前 RT-Thread 支持的部分网络软件包,软件包的数量还在不断的增加中。
+
+|**软件包名称**|**描述** |
+|----------------|------------------------|
+| Paho MQTT | 基于 Eclipse 开源的 Paho MQTT,做了很多功能及性能优化,比如:增加了断线自动重连功能,采用 pipe 模型,支持非阻塞 API,支持 TLS 加密传输等等 |
+| WebClient | 简单易用的 HTTP 客户端,支持 HTTP GET/POST 等常见请求功能,支持 HTTPS,断点续传等功能 |
+| mongoose | 嵌入式 Web 服务器网络库,类似嵌入式世界里的 Nginx。授权许可不够友好,商业需要收费 |
+| WebTerminal | 可以在浏览器或手机端访问 Finsh/MSHShell 的软件包 |
+| cJSON | 超轻量级的 JSON 解析库 |
+| ljson | json 到 struct 的解析,输出库 |
+| ezXML | XML 文件解析库,目前还不支持解析 XML 数据 |
+| nanopb | Protocol Buffers 格式数据解析库,Protocol Buffers 格式比 JSON、XML 格式资源占用更少 |
+| GAgent | 接入机智云的软件包 |
+| Marvell WiFi | Marvell WiFi 驱动 |
+| Wiced WiFi | Wiced 接口的 WiFi 驱动 |
+| CoAP | 移植 libcoap 的 CoAP 通信软件包 |
+| nopoll | 移植的开源 WebSocket 通信软件包 |
+| netutils | 实用的网络调试小工具集合,包括:ping、TFTP、iperf、NetIO、NTP、Telnet 等 |
+| OneNet | 与中国移动 OneNet 云对接的软件包 |
+
+## 网络框架工作流程
+
+使用 RT-Thread 网络框架,首先需要初始化 SAL,然后注册各类网络协议簇,确保应用程序能够使用 socket 网络套接字接口进行通信,本节主要以 LwIP 作为示例进行讲解。
+
+### 网络协议簇注册
+
+首先使用 `sal_init()` 接口对组件中使用的互斥锁等资源进行初始化,接口如下所示:
+
+```c
+int sal_init(void);
+```
+
+SAL 初始化后,通过 `sal_proto_family_register()` 接口来注册网络协议簇,将 LwIP 网络协议簇注册到 SAL 中,示例代码如下:
+
+```c
+static const struct proto_family LwIP_inet_family_ops = {
+ "LwIP",
+ AF_INET,
+ AF_INET,
+ inet_create,
+ LwIP_gethostbyname,
+ LwIP_gethostbyname_r,
+ LwIP_freeaddrinfo,
+ LwIP_getaddrinfo,
+};
+
+int LwIP_inet_init(void)
+{
+ sal_proto_family_register(&LwIP_inet_family_ops);
+
+ return 0;
+}
+```
+
+AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF 是 “Address Family” 的简写,INET 是 “Internet” 的简写。
+
+其中 `sal_proto_family_register()` 接口定义如下所示:
+
+```
+int sal_proto_family_register(const struct proto_family *pf);
+```
+
+|**参数**|**描述** |
+|----------|------------------|
+| pf | 协议簇结构体指针 |
+|**返回**|**——** |
+| 0 | 注册成功 |
+| -1 | 注册失败 |
+
+### 网络数据接收流程
+
+LwIP 注册到 SAL 之后,应用程序可通过网络套接字接口进行网络数据收发。在 LwIP 中,创建了几个主要线程,分布是 tcpip 线程、erx 接收线程和 etx 发送线程,网络数据接收流程如下面图片所示,应用程序通过调用标准套接字接口 recv() 接收数据,以阻塞方式进行。当以太网硬件设备收到网络数据报文,将报文存放到接收缓冲区,然后通过以太网中断程序,发送邮件通知 erx 线程有数据到达,erx 线程会按照接收到的数据长度来申请 pbuf 内存块,并将数据放入 pbuf 的 payload 数据中,然后将 pbuf 内存块通过邮件发送给 tcpip 线程,tcpip 线程将数据返回给正在阻塞接收数据的应用程序。
+
+
+
+### 网络数据发送流程
+
+网络数据发送流程如下图所示。当有数据需要发送时,应用程序调用标准网络套接字接口 send() 将数据交给 tcpip 线程,tcpip 线程会发送一个邮件来唤醒 etx 线程,etx 线程先判断以太网是否正在发送数据,如果没有,那么将待发送的数据放入发送缓冲区,然后通过以太网设备将数据发送出去。如果正在发送数据,etx 线程会将自己挂起,直到以太网设备空闲后再发送数据出去。
+
+
+
+## 网络套接字编程
+
+应用程序使用 Socket(网络套接字) 接口编程来实现网络通信功能,Socket 是一组应用程序接口(API),屏蔽了各个协议的通信细节,使得应用程序无需关注协议本身,直接使用 socket 提供的接口来进行互联的不同主机间的通信。
+
+### TCP socket 通信流程
+
+TCP 是 Tranfer Control Protocol 的简称,是一种面向连接的保证数据可靠传输的协议。通过 TCP 协议传输,得到的是一个顺序的无差错的数据流。基于 TCP 的 socket 编程流程图见下图,发送方和接收方的两个 socket 之间必须建立连接,以便在 TCP 协议的基础上进行通信,当一个 socket(通常都是 server socket)等待建立连接时,另一个 socket 可以要求进行连接,一旦这两个 socket 连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。TCP 连接是可靠的连接,它能保证数据包按顺序到达,如果出现丢包,则会自动重发数据包。
+
+举个例子,TCP 相当于生活中打电话,当你打电话给对方时,必须要等待对方接听,只有对方接听了你的电话,和你建立了连接,双方才可以通话,互相传递信息。当然,这时候传递的信息是可靠地,因为对方听不清你说的内容可以要求你重新将内容复述一遍。当打电话的双方中的任何一方要结束本次通话时,会主动和对方告别,等到对方也和自己告别后,才会挂断电话,结束本次通讯。
+
+
+
+### UDP socket 通信流程
+
+UDP 是 User Datagram Protocol 的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址和目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的,基于 UDP 的 socket 编程流程如下图所示。
+
+
+
+举个例子,UDP 就相当于生活中的对讲机通讯,你设定好频道后就可以直接说你要表达的信息了,数据被对讲机发送了出去,但是你不知道你的消息有没有被别人接收到,除非别人也用对讲机回复你,因此这种方式是不可靠的。
+
+### 创建套接字
+
+在进行通信前,通信双方首先使用 `socket()` 接口创建套接字,根据指定的地址族、数据类型和协议来分配一个套接字描述符及其所用的资源。接口如下所示:
+
+```c
+int socket(int domain, int type, int protocol);
+```
+
+|**参数**|**描述** |
+|----------|--------------------------------------------------|
+| domain | 协议族 |
+| type | 指定通信类型,取值包括 SOCK_STREAM 和 SOCK_DGRAM。 |
+| protocol | protocol 允许为套接字指定一种协议,默认设为 0 |
+|**返回**|**——** |
+| >=0 | 成功,返回一个代表套接字描述符的整数 |
+| -1 | 失败 |
+
+**通信类型包括**SOCK_STREAM 和 SOCK_DGRAM 两种方式,**SOCK_STREAM**表示面向连接的 TCP 数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。
+
+**SOCK_DGRAM**表示无连接的 UDP 数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
+
+创建一个 TCP 类型的套接字的示例代码如下:
+
+```c
+ /* 创建一个 socket,类型是 SOCKET_STREAM,TCP 类型 */
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ /* 创建 socket 失败 */
+ rt_kprintf("Socket error\n");
+
+ return;
+ }
+```
+
+### 绑定套接字
+
+绑定套接字用于将端口号和 IP 地址绑定到指定套接字上。当使用 socket() 创造一个套接字时, 只是给定了协议族,并没有分配地址,在套接字接收来自其他主机的连接前,必须用 bind() 给它绑定一个地址和端口号。接口如下所示:
+
+```c
+int bind(int s, const struct sockaddr *name, socklen_t namelen);
+```
+
+|**参数**|**描述** |
+|----------|--------------------------------------------|
+| S | 套接字描述符 |
+| name | 指向 sockaddr 结构体的指针,代表要绑定的地址 |
+| namelen | sockaddr 结构体的长度 |
+|**返回**|**——** |
+| 0 | 成功 |
+| -1 | 失败 |
+
+### 建立 TCP 连接
+
+对于服务器端程序,使用 `bind()` 绑定套接字后,还需要使用 `listen()` 函数让套接字进入被动监听状态,再调用 `accept()` 函数,就可以随时响应客户端的请求了。
+
+#### 监听套接字
+
+监听套接字用于 TCP 服务器监听指定套接字连接。接口如下所示:
+
+```c
+int listen(int s, int backlog);
+```
+
+|**参数**|**描述** |
+|----------|--------------------------------|
+| s | 套接字描述符 |
+| backlog | 表示一次能够等待的最大连接数目 |
+|**返回**|**——** |
+| 0 | 成功 |
+| -1 | 失败 |
+
+#### 接受连接
+
+当应用程序监听来自其他客户端的连接时,要使用 `accept()` 函数初始化连接,它为每个连接创立新的套接字并从监听队列中移除这个连接。接口如下所示:
+
+```c
+int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+```
+
+|**参数**|**描述** |
+|----------|--------------------------------|
+| s | 套接字描述符 |
+| addr | 客户端设备地址信息 |
+| addrlen | 客户端设备地址结构体的长度 |
+|**返回**|**——** |
+| >=0 | 成功,返回新创建的套接字描述符 |
+| -1 | 失败 |
+
+#### 建立连接
+
+用于客户端与指定服务器建立连接。接口如下所示:
+
+```
+int connect(int s, const struct sockaddr *name, socklen_t namelen);
+```
+
+|**参数**|**描述** |
+|----------|------------------------|
+| s | 套接字描述符 |
+| name | 服务器地址信息 |
+| namelen | 服务器地址结构体的长度 |
+|**返回**|**描述** |
+| 0 | 成功 |
+| -1 | 失败 |
+
+客户端与服务端连接时,首先设置服务端地址,然后使用 `connect()` 函数进行连接,示例代码如下所示:
+
+```c
+struct sockaddr_in server_addr;
+/* 初始化预连接的服务端地址 */
+server_addr.sin_family = AF_INET;
+server_addr.sin_port = htons(port);
+server_addr.sin_addr = *((struct in_addr *)host->h_addr);
+rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
+
+/* 连接到服务端 */
+if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
+{
+ /* 连接失败 */
+ closesocket(sock);
+
+ return;
+}
+```
+
+### 数据传输
+
+TCP 和 UDP 的数据传输方式不同,TCP 需要建立连接后才能进行数据传输,使用 `send()` 函数进行数据发送,使用 `recv()` 函数进行数据接收,而 UDP 则不需要建立连接,使用 `sendto()` 函数进行数据发送,使用 `recvfrom()` 函数接收数据。
+
+#### TCP 数据发送
+
+TCP 连接建立以后,使用 `send()` 函数进行数据发送,接口如下所示:
+
+```c
+int send(int s, const void *dataptr, size_t size, int flags);
+```
+
+|**参数**|**描述** |
+|----------|----------------------------|
+| s | 套接字描述符 |
+| dataptr | 要发送的数据指针 |
+| size | 发送的数据长度 |
+| flags | 标志,一般为 0 |
+|**返回**|**——** |
+| >0 | 成功,返回发送的数据的长度 |
+| <=0 | 失败 |
+
+#### TCP 数据接收
+
+TCP 连接建立以后,使用 `recv()` 接收数据,接口如下所示:
+
+```c
+int recv(int s, void *mem, size_t len, int flags);
+```
+
+|**参数**|**描述** |
+|----------|----------------------------|
+| s | 套接字描述符 |
+| mem | 接收的数据指针 |
+| len | 接收的数据长度 |
+| flags | 标志,一般为 0 |
+|**返回**|**描述** |
+| >0 | 成功,返回接收的数据的长度 |
+| =0 | 目标地址已传输完并关闭连接 |
+| <0 | 失败 |
+
+#### UDP 数据发送
+
+在未建立连接的情况下,可以使用 `sendto()` 函数向指定的目标地址发送 UDP 数据,接口如下所示:
+
+```c
+int sendto(int s, const void *dataptr, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen);
+```
+
+|**参数**|**描述** |
+|----------|----------------------------|
+| s | 套接字描述符 |
+| dataptr | 发送的数据指针 |
+| size | 发送的数据长度 |
+| flags | 标志,一般为 0 |
+| to | 目标地址结构体指针 |
+| tolen | 目标地址结构体长度 |
+|**返回**|**——** |
+| >0 | 成功,返回发送的数据的长度 |
+| <=0 | 失败 |
+
+#### UDP 数据接收
+
+接收 UDP 数据则使用 `recvfrom()` 函数,接口如下所示:
+
+```c
+int recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen);
+```
+
+|**参数**|**描述** |
+|----------|----------------------------|
+| s | 套接字描述符 |
+| mem | 接收的数据指针 |
+| len | 接收的数据长度 |
+| flags | 标志,一般为 0 |
+| from | 接收地址结构体指针 |
+| fromlen | 接收地址结构体长度 |
+|**返回**|**——** |
+| >0 | 成功,返回接收的数据的长度 |
+| 0 | 接收地址已传输完并关闭连接 |
+| <0 | 失败 |
+
+### 关闭网络连接
+
+网络通信结束后,需要关闭网络连接,有两种方式,分别是使用 `closesocket()` 和 `shutdown()`。
+
+`closesocket()` 接口用来关闭已经存在的 socket 连接,释放 socket 资源,将套接字描述符从内存清除,之后再也不能使用该套接字,与该套接字相关的连接和缓存也失去了意义,TCP 协议会自动关闭连接。接口如下所示:
+
+```c
+int closesocket(int s);
+```
+
+|**参数**|**描述** |
+|----------|--------------|
+| s | 套接字描述符 |
+|**返回**|**——** |
+| 0 | 成功 |
+| -1 | 失败 |
+
+使用 `shutdown()` 函数也可以关闭网络连接。TCP 连接是全双工的,使用 `shutdown()` 函数可以实现半关闭,它可以关闭连接的读或者写操作,也可以两端都关闭,但它不释放 socket 资源,接口如下所示:
+
+```c
+int shutdown(int s, int how);
+```
+
+|**参数**|**描述** |
+|----------|-----------------------------|
+| s | 套接字描述符 |
+| how | SHUT_RD 关闭连接的接收端,不再接收数据 SHUT_WR 关闭连接的发送端,不再发送数据 SHUT_RDWR 两端都关闭 |
+|**返回**|**——** |
+| 0 | 成功 |
+| -1 | 失败 |
+
+## 网络功能配置
+
+网络框架的主要功能配置选项如下表所示,可以根据不同的功能需求进行配置:
+
+SAL 组件配置选项:
+
+|**宏定义** |**取值类型**|**描述** |
+|------------------------|--------------|--------------------|
+| RT_USING_SAL | 布尔 | 开启 SAL |
+| SAL_USING_LWIP | 布尔 | 开启 LwIP 组件 |
+| SAL_USING_AT | 布尔 | 开启 AT 组件 |
+| SAL_USING_POSIX | 布尔 | 开启 POSIX 接口支持 |
+| SAL_PROTO_FAMILIES_NUM | 整数 | 支持最大协议族数量 |
+
+LwIP 配置选项:
+
+|**宏定义** |**取值类型**|**描述** |
+|-----------------------------|--------------|----------------------|
+| RT_USING_LWIP | 布尔 | 开启 LwIP 组件 |
+| RT_USING_LWIP_IPV6 | 布尔 | 开启 IPV6 功能 |
+| RT_LWIP_IGMP | 布尔 | 开启 IGMP 协议 |
+| RT_LWIP_ICMP | 布尔 | 开启 ICMP 协议 |
+| RT_LWIP_SNMP | 布尔 | 开启 SNMP 协议 |
+| RT_LWIP_DNS | 布尔 | 开启 DNS 功能 |
+| RT_LWIP_DHCP | 布尔 | 开启 DHCP 功能 |
+| IP_SOF_BROADCAST | 整数 | IP 发送广播包过滤 |
+| IP_SOF_BROADCAST_RECV | 整数 | IP 接收广播包过滤 |
+| RT_LWIP_IPADDR | 字符串 | IP 地址 |
+| RT_LWIP_GWADDR | 字符串 | 网关地址 |
+| RT_LWIP_MSKADDR | 字符串 | 子网掩码 |
+| RT_LWIP_UDP | 布尔 | 启用 UDP 协议 |
+| RT_LWIP_TCP | 布尔 | 启用 TCP 协议 |
+| RT_LWIP_RAW | 布尔 | 启用 RAW API |
+| RT_MEMP_NUM_NETCONN | 整数 | 支持网络连接数 |
+| RT_LWIP_PBUF_NUM | 整数 | pbuf 内存块数量 |
+| RT_LWIP_RAW_PCB_NUM | 整数 | RAW 最大连接数量 |
+| RT_LWIP_UDP_PCB_NUM | 整数 | UDP 最大连接数量 |
+| RT_LWIP_TCP_PCB_NUM | 整数 | TCP 最大连接数量 |
+| RT_LWIP_TCP_SND_BUF | 整数 | TCP 发送缓冲区大小 |
+| RT_LWIP_TCP_WND | 整数 | TCP 滑动窗口大小 |
+| RT_LWIP_TCPTHREAD_PRIORITY | 整数 | TCP 线程的优先级 |
+| RT_LWIP_TCPTHREAD_MBOX_SIZE | 整数 | TCP 线程邮箱大小 |
+| RT_LWIP_TCPTHREAD_STACKSIZE | 整数 | TCP 线程栈大小 |
+| RT_LWIP_ETHTHREAD_PRIORITY | 整数 | 接收发送线程的优先级 |
+| RT_LWIP_ETHTHREAD_STACKSIZE | 整数 | 接收发送线程栈大小 |
+| RT_LwIP_ETHTHREAD_MBOX_SIZE | 整数 | 接收发送线程邮箱大小 |
+
+## 网络应用示例
+
+### 查看 IP 地址
+
+在控制台可使用 ifconfig 命令查看网络情况,可知 IP 地址 192.168.12.26,并且 FLAGS 状态是 LINK_UP,表示网络已经配置好:
+
+```c
+msh >ifconfig
+network interface: e0 (Default)
+MTU: 1500
+MAC: 00 04 a3 12 34 56
+FLAGS: UP LINK_UP ETHARP BROADCAST IGMP
+ip address: 192.168.12.26
+gw address: 192.168.10.1
+net mask : 255.255.0.0·
+dns server #0: 192.168.10.1
+dns server #1: 223.5.5.5
+```
+
+### Ping 网络测试
+
+使用 ping 命令进行网络测试:
+
+```c
+msh />ping rt-thread.org
+60 bytes from 116.62.244.242 icmp_seq=0 ttl=49 time=11 ticks
+60 bytes from 116.62.244.242 icmp_seq=1 ttl=49 time=10 ticks
+60 bytes from 116.62.244.242 icmp_seq=2 ttl=49 time=12 ticks
+60 bytes from 116.62.244.242 icmp_seq=3 ttl=49 time=10 ticks
+msh />ping 192.168.10.12
+60 bytes from 192.168.10.12 icmp_seq=0 ttl=64 time=5 ticks
+60 bytes from 192.168.10.12 icmp_seq=1 ttl=64 time=1 ticks
+60 bytes from 192.168.10.12 icmp_seq=2 ttl=64 time=2 ticks
+60 bytes from 192.168.10.12 icmp_seq=3 ttl=64 time=3 ticks
+msh />
+```
+
+得到以上的输出结果,表示连接网络成功!
+
+### TCP 客户端示例
+
+网络连接成功后就可以运行网络示例,先运行 TCP 客户端的示例。本示例将在 PC 上开启一个 TCP 服务器,在 IoT Board 板上开启一个 TCP 客户端,双方进行网络通信。
+
+在示例工程中已经有 TCP 客户端程序 tcpclient_sample.c,功能是实现一个 TCP 客户端,能够接收并显示从服务端发送过来的信息,如果接收到开头是'q' 或'Q'的信息,那么直接退出程序,关闭 TCP 客户端。该程序导出了 tcpclient 命令到 FinSH 控制台,命令调用格式是 tcpclient URL PORT,其中 URL 是服务器地址,PORT 是端口号。示例代码如下所示:
+
+```c
+
+/*
+ * 程序清单:tcp 客户端
+ *
+ * 这是一个 tcp 客户端的例程
+ * 导出 tcpclient 命令到控制终端
+ * 命令调用格式:tcpclient URL PORT
+ * URL:服务器地址 PORT::端口号
+ * 程序功能:接收并显示从服务端发送过来的信息,接收到开头是 'q' 或 'Q' 的信息退出程序
+*/
+#include
+#include /* 使用BSD socket,需要包含socket.h头文件 */
+#include
+#include
+#include
+
+#define BUFSZ 1024
+
+static const char send_data[] = "This is TCP Client from RT-Thread."; /* 发送用到的数据 */
+void tcpclient(int argc, char**argv)
+{
+ int ret;
+ char *recv_data;
+ struct hostent *host;
+ int sock, bytes_received;
+ struct sockaddr_in server_addr;
+ const char *url;
+ int port;
+
+ /* 接收到的参数小于 3 个 */
+ if (argc < 3)
+ {
+ rt_kprintf("Usage: tcpclient URL PORT\n");
+ rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
+ return ;
+ }
+
+ url = argv[1];
+ port = strtoul(argv[2], 0, 10);
+
+ /* 通过函数入口参数 url 获得 host 地址(如果是域名,会做域名解析) */
+ host = gethostbyname(url);
+
+ /* 分配用于存放接收数据的缓冲 */
+ recv_data = rt_malloc(BUFSZ);
+ if (recv_data == RT_NULL)
+ {
+ rt_kprintf("No memory\n");
+ return;
+ }
+
+ /* 创建一个 socket,类型是 SOCKET_STREAM,TCP 类型 */
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ /* 创建 socket 失败 */
+ rt_kprintf("Socket error\n");
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ return;
+ }
+
+ /* 初始化预连接的服务端地址 */
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+ server_addr.sin_addr = *((struct in_addr *)host->h_addr);
+ rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
+
+ /* 连接到服务端 */
+ if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
+ {
+ /* 连接失败 */
+ rt_kprintf("Connect fail!\n");
+ closesocket(sock);
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ return;
+ }
+
+ while (1)
+ {
+ /* 从 sock 连接中接收最大 BUFSZ - 1 字节数据 */
+ bytes_received = recv(sock, recv_data, BUFSZ - 1, 0);
+ if (bytes_received < 0)
+ {
+ /* 接收失败,关闭这个连接 */
+ closesocket(sock);
+ rt_kprintf("\nreceived error,close the socket.\r\n");
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ break;
+ }
+ else if (bytes_received == 0)
+ {
+ /* 打印 recv 函数返回值为 0 的警告信息 */
+ rt_kprintf("\nReceived warning,recv function return 0.\r\n");
+
+ continue;
+ }
+
+ /* 有接收到数据,把末端清零 */
+ recv_data[bytes_received] = '\0';
+
+ if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0)
+ {
+ /* 如果是首字母是 q 或 Q,关闭这个连接 */
+ closesocket(sock);
+ rt_kprintf("\n got a'q'or'Q',close the socket.\r\n");
+
+ /* 释放接收缓冲 */
+ rt_free(recv_data);
+ break;
+ }
+ else
+ {
+ /* 在控制终端显示收到的数据 */
+ rt_kprintf("\nReceived data = %s", recv_data);
+ }
+
+ /* 发送数据到 sock 连接 */
+ ret = send(sock, send_data, strlen(send_data), 0);
+ if (ret < 0)
+ {
+ /* 接收失败,关闭这个连接 */
+ closesocket(sock);
+ rt_kprintf("\nsend error,close the socket.\r\n");
+
+ rt_free(recv_data);
+ break;
+ }
+ else if (ret == 0)
+ {
+ /* 打印 send 函数返回值为 0 的警告信息 */
+ rt_kprintf("\n Send warning,send function return 0.\r\n");
+ }
+ }
+ return;
+}
+MSH_CMD_EXPORT(tcpclient, a tcp client sample);
+```
+
+运行该示例时,首先,在电脑上打开网络调试助手,开启一个 TCP 服务器。选择协议类型为 TCP
+Server, 填入本机 IP 地址和端口 5000,如下图所示。
+
+
+
+然后就在 FinSH 控制台输入以下命令启动 TCP 客户端来连接 TCP 服务器:
+
+```c
+msh />tcpclient 192.168.12.45 5000 // 按照实际情况输入
+Connect successful
+```
+
+当控制台输出 “Connect successful” 的日志信息,表示 TCP 连接被成功建立。接下来就可以进行数据通信了,在网络调试工具窗口,发送 Hello RT-Thread!,表示从 TCP 服务器发送一条数据给 TCP 客户端,如下图所示:
+
+
+
+FinSH 控制台上接收到数据后会输出相应的日志信息,可以看到:
+
+```c
+msh >tcpclient 192.168.12.130 5000
+Connect successful
+Received data = hello world
+Received data = hello world
+Received data = hello world
+Received data = hello world
+Received data = hello world
+ got a 'q' or 'Q',close the socket.
+msh >
+```
+
+上面的信息表示 TCP 客户端接收到了从服务器发送的 5 条 “hello world” 数据,最后,从 TCP 服务器接收到退出指令’q’,TCP 客户端程序退出运行, 返回到 FinSH 控制台。
+
+### UDP 客户端示例
+
+这是一个 UDP 客户端的示例,本示例将在 PC 上开启一个 UDP 服务器,在 IoT Board 板上开启一个 UDP 客户端,双方进行网络通信。在示例工程中已经实现了一个 UDP 客户端程序,功能是发送数据到服务器端,示例代码如下所示:
+
+```c
+/*
+ * 程序清单:udp 客户端
+ *
+ * 这是一个 udp 客户端的例程
+ * 导出 udpclient 命令到控制终端
+ * 命令调用格式:udpclient URL PORT [COUNT = 10]
+ * URL:服务器地址 PORT:端口号 COUNT:可选参数 默认为 10
+ * 程序功能:发送 COUNT 条数据到服务远端
+*/
+#include
+#include /* 使用BSD socket,需要包含sockets.h头文件 */
+#include
+#include
+#include
+
+const char send_data[] = "This is UDP Client from RT-Thread.\n"; /* 发送用到的数据 */
+
+void udpclient(int argc, char**argv)
+{
+ int sock, port, count;
+ struct hostent *host;
+ struct sockaddr_in server_addr;
+ const char *url;
+
+ /* 接收到的参数小于 3 个 */
+ if (argc < 3)
+ {
+ rt_kprintf("Usage: udpclient URL PORT [COUNT = 10]\n");
+ rt_kprintf("Like: tcpclient 192.168.12.44 5000\n");
+ return ;
+ }
+
+ url = argv[1];
+ port = strtoul(argv[2], 0, 10);
+
+ if (argc> 3)
+ count = strtoul(argv[3], 0, 10);
+ else
+ count = 10;
+
+ /* 通过函数入口参数 url 获得 host 地址(如果是域名,会做域名解析) */
+ host = (struct hostent *) gethostbyname(url);
+
+ /* 创建一个 socket,类型是 SOCK_DGRAM,UDP 类型 */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ {
+ rt_kprintf("Socket error\n");
+ return;
+ }
+
+ /* 初始化预连接的服务端地址 */
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+ server_addr.sin_addr = *((struct in_addr *)host->h_addr);
+ rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
+
+ /* 总计发送 count 次数据 */
+ while (count)
+ {
+ /* 发送数据到服务远端 */
+ sendto(sock, send_data, strlen(send_data), 0,
+ (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
+
+ /* 线程休眠一段时间 */
+ rt_thread_delay(50);
+
+ /* 计数值减一 */
+ count --;
+ }
+
+ /* 关闭这个 socket */
+ closesocket(sock);
+}
+```
+
+运行该示例时,首先,在电脑上打开网络调试助手,开启一个 UDP 服务器。选择协议类型为 UDP,填入本机 IP 地址和端口 5000,如下图所示。
+
+
+
+然后就可以在 FinSH 控制台输入以下命令来给 UDP 服务器发送数据,
+
+`msh />udpclient 192.168.12.45 1001 // 需按照真实情况输入 `
+
+服务器会收到 10 条 This is UDP Client from RT-Thread. 的消息,如下图所示:
+
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/mode.png b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/mode.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea224b7ac89f54dac2adc845c4c86e62d150bd60
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/mode.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_architecture.png b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_architecture.png
new file mode 100644
index 0000000000000000000000000000000000000000..45d7c8dc757408dbfeb397a18a563ee0daa87d2e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_architecture.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_description.png b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_description.png
new file mode 100644
index 0000000000000000000000000000000000000000..f686f5e2e3c2904fd5837708396bd0e3ca8e07cc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_description.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_sequence.png b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_sequence.png
new file mode 100644
index 0000000000000000000000000000000000000000..57cd0d2bf6e41b54b4230df70195bccd9f88dc29
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_sequence.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_system.png b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_system.png
new file mode 100644
index 0000000000000000000000000000000000000000..2f87e731bc3252830afda0b05c1e0d2c42313d8d
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/pm/figures/pm_system.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/pm/pm.md b/rt-thread-version/rt-thread-standard/programming-manual/pm/pm.md
new file mode 100644
index 0000000000000000000000000000000000000000..32a23a1cd6eaabd29253efd28e88d321c557ee9f
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/pm/pm.md
@@ -0,0 +1,377 @@
+# 电源管理组件
+
+嵌入式系统低功耗管理的目的在于满足用户对性能需求的前提下,尽可能降低系统能耗以延长设备待机时间。高性能与有限的电池能量在嵌入式系统中矛盾最为突出,硬件低功耗设计与软件低功耗管理的联合应用成为解决矛盾的有效手段。现在的各种 MCU 都或多或少的在低功耗方面提供了管理接口。比如对主控时钟频率的调整、工作电压的改变、总线频率的调整甚至关闭、外围设备工作时钟的关闭等。有了硬件上的支持,合理的软件设计就成为节能的关键,一般可以把低功耗管理分为三个类别:
+
+- 处理器电源管理
+主要实现方式:对 CPU 频率的动态管理,以及系统空闲时对工作模式的调整。
+
+- 设备电源管理
+主要实现方式:关闭个别闲置设备
+
+- 系统平台电源管理
+主要实现方式:针对特定系统平台的非常见设备具体定制。
+
+随着物联网 (IoT) 的兴起,产品对功耗的需求越来越强烈。作为数据采集的传感器节点通常需要在电池供电时长期工作,而作为联网的 SOC 也需要有快速的响应功能和较低的功耗。
+
+在产品开发的起始阶段,首先考虑是尽快完成产品的功能开发。在产品功能逐步完善之后,就需要加入电源管理 (Power Management,以下简称 PM) 功能。为了适应 IoT 的这种需求,RT-Thread 提供了电源管理组件。电源管理组件的理念是尽量透明,使得产品加入低功耗功能更加轻松。
+
+## PM 组件介绍
+
+RT-Thread 的 PM 组件采用分层设计思想,分离架构和芯片相关的部分,提取公共部分作为核心。在对上层提供通用的接口同时,也让底层驱动对组件的适配变得更加简单。
+
+
+
+### 主要特点
+
+RT-Thread PM 组件主要特点如下所示:
+
+- 基于模式来管理功耗,空闲时动态调整工作模式,支持多个等级的休眠。
+- 对应用透明,组件在底层自动完成电源管理。
+- 支持运行模式下动态变频,根据模式自动更新设备的频率配置,确保在不同的运行模式都可以正常工作。
+- 支持设备电源管理,根据模式自动管理设备的挂起和恢复,确保在不同的休眠模式下可以正确的挂起和恢复。
+- 支持可选的休眠时间补偿,让依赖 OS Tick 的应用可以透明使用。
+- 向上层提供设备接口,如果打开了 devfs 组件,那么也可以通过文件系统接口访问。
+
+### 工作原理
+
+低功耗的本质是系统空闲时 CPU 停止工作,中断或事件唤醒后继续工作。在 RTOS 中,通常包含一个 IDLE 任务,该任务的优先级最低且一直保持就绪状态,当高优先级任务未就绪时,OS 执行 IDLE 任务。一般地,未进行低功耗处理时,CPU 在 IDLE 任务中循环执行空指令。RT-Thread 的电源管理组件在 IDLE 任务中,通过对 CPU 、时钟和设备等进行管理,从而有效降低系统的功耗。
+
+
+
+在上图所示,当高优先级任务运行结束或被挂起时,系统将进入 IDLE 任务中。在 IDLE 任务执行后,它将判断系统是否可以进入到休眠状态(以节省功耗)。如果可以进入休眠,
+将根据芯片情况关闭部分硬件模块,OS Tick 也非常有可能进入暂停状态。此时电源管理框架会根据系统定时器情况,计算出下一个超时时间点,并设置低功耗定时器,让设备能够在这个时刻点唤醒,并进行后续的工作。当系统被(低功耗定时器中断或其他唤醒中断源)唤醒后,系统也需要知道睡眠时间长度是多少,并对OS Tick 进行补偿,让系统的OS tick值调整为一个正确的值。
+
+## 设计架构
+
+在 RT-Thread PM 组件中,外设或应用通过投票机制对所需的功耗模式进行投票,当系统空闲时,根据投票数决策出合适的功耗模式,调用抽象接口,控制芯片进入低功耗状态,从而降低系统功耗。当未进行进行任何投票时,会以默认模式进入(通常为空闲模式)。与应用不同,某些外设可能在进入低功耗状态时执行特定操作,退出低功耗时采取措施恢复,此时可以通过注册PM设备来实现。通过注册 PM 设备,在进入低功耗状态之前,会触发注册设备的 suspend 回调,开发者可在回调里执行自己的操作;类似地,从低功耗状态退出时,也会触发 resume 回调。
+
+
+
+## 低功耗状态和模式
+
+RT-Thread PM 组件将系统划分为两种状态:运行状态(RUN)和休眠状态(Sleep)。
+运行状态控制 CPU 的频率,适用于变频场景;休眠状态根据 SOC 特性实现休眠 CPU,以降低功耗。两种状态分别使用不同的 API 接口,独立控制。
+
+- 休眠状态
+
+休眠状态也就是通常意义上的低功耗状态,通过关闭外设、执行 SOC 电源管理接口,降低系统功耗。
+休眠状态又分为六个模式,呈现为金字塔的形式。随着模式增加,功耗逐级递减的特点。下面是休眠状态下模式的定义,开发者可根据具体的 SOC 实现相应的模式,但需要遵循功耗逐级降低的特点。
+
+| 模式 | 级别 | 描述 |
+| -- | -- | -- |
+| PM_SLEEP_MODE_NONE | 0 | 系统处于活跃状态,未采取任何的降低功耗状态 |
+| PM_SLEEP_MODE_IDLE | 1 | 空闲模式,该模式在系统空闲时停止 CPU 和部分时钟,任意事件或中断均可以唤醒 |
+| PM_SLEEP_MODE_LIGHT | 2 | 轻度睡眠模式,CPU 停止,多数时钟和外设停止,唤醒后需要进行时间补偿 |
+| PM_SLEEP_MODE_DEEP | 3 | 深度睡眠模式,CPU 停止,仅少数低功耗外设工作,可被特殊中断唤醒 |
+| PM_SLEEP_MODE_STANDBY | 4 | 待机模式,CPU 停止,设备上下文丢失(可保存至特殊外设),唤醒后通常复位 |
+| PM_SLEEP_MODE_SHUTDOWN | 5 | 关断模式,比 Standby 模式功耗更低, 上下文通常不可恢复, 唤醒后复位 |
+
+**注意:**因各家芯片差异,功耗管理的实现也不尽相同,上述的描述仅给出一些推荐的场景,并非一定需要实现所有。开发者可根据自身情况选择其中的几种进行实现,但是需要遵循级别越高,功耗越低的原则!
+
+- 运行状态
+
+运行状态通常用于改变 CPU 的运行频率,独立于休眠模式。当前运行状态划分了四个等级:高速、正常、中速、低速,如下:
+
+| 模式 | 描述 |
+| -- | -- |
+| PM_RUN_MODE_HIGH_SPEED | 高速模式,适用于一些超频的场景 |
+| PM_RUN_MODE_NORMAL_SPEED | 正常模式,该模式作为默认的运行状态 |
+| PM_RUN_MODE_MEDIUM_SPEED | 中速模式,降低 CPU 运行速度,从而降低运行功耗 |
+| PM_RUN_MODE_LOW_SPEED | 低速模式,CPU 频率进一步降低 |
+
+### 模式的请求和释放
+
+在 PM 组件里,上层应用可以通过请求和释放休眠模式主动参与功耗管理。应用可以根据场景请求不同的休眠模式,并在处理完毕后释放,只要有任意一个应用或设备请求高等级的功耗模式,就不会切换到比它更低的模式。因此,休眠模式的请求和释放的操作通常成对出现,可用于对某个阶段进行保护,如外设的 DMA 传输过程。
+
+### 对模式变化敏感的设备
+
+在 PM 组件里,切换到新的运行模式可能会导致 CPU 频率发生变化,如果外设和 CPU 共用一部分时钟,那外设的时钟就会受到影响;在进入新的休眠模式,大部分时钟源会被停止,如果外设不支持休眠的冻结功能,那么从休眠唤醒的时候,外设的时钟就需要重新配置外设。所以 PM 组件里支持了 PM 模式敏感的 PM 设备。使得设备在切换到新的运行模式或者新的休眠模式都能正常的工作。该功能需要底层驱动实现相关的接口并注册为对模式变化敏感的设备。
+
+## 调用流程
+
+
+
+首先应用设置进出休眠状态的回调函数,然后调用 rt_pm_request 请求休眠模式,触发休眠操作;PM 组件在系统空闲时检查休眠模式计数,根据投票数给出推荐的模式;接着 PM 组件调用 notfiy 通知应用,告知即将进入休眠模式;然后对注册的 PM 设备执行挂起操作,返回 OK 后执行 SOC 实现的的休眠模式,系统进入休眠状态(如果使能时间补偿,休眠之前会先启动低功耗定时器)。此时 CPU 停止工作,等待事件或者中断唤醒。当系统被唤醒后,由于全局中断为关闭状态,系统继续从该处执行,获取睡眠时间补偿系统的心跳,依次唤醒设备,通知应用从休眠模式退出。如此一个周期执行完毕,退出,等待系统下次空闲。
+
+## API 介绍
+
+- 请求休眠模式
+```c
+void rt_pm_request(uint8_t sleep_mode);
+```
+| 参数 | 模式 |
+| -- | -- |
+| sleep_mode | 请求的休眠模式等级 |
+
+sleep_mode 取以下枚举值:
+```c
+enum
+{
+ /* sleep modes */
+ PM_SLEEP_MODE_NONE = 0, /* 活跃状态 */
+ PM_SLEEP_MODE_IDLE, /* 空闲模式(默认) */
+ PM_SLEEP_MODE_LIGHT, /* 轻度睡眠模式 */
+ PM_SLEEP_MODE_DEEP, /* 深度睡眠模式 */
+ PM_SLEEP_MODE_STANDBY, /* 待机模式 */
+ PM_SLEEP_MODE_SHUTDOWN, /* 关断模式 */
+ PM_SLEEP_MODE_MAX,
+};
+```
+调用该函数会将对应的模式计数加1,并锁住该模式。此时如果请求更低级别的功耗模式,将无法进入,只有释放(解锁)先前请求的模式后,系统才能进入更低的模式;向更高的功耗模式请求则不受此影响。该函数需要和 rt_pm_release 配合使用,用于对某一阶段或过程进行保护。
+
+- 释放休眠模式
+```c
+void rt_pm_release(uint8_t sleep_mode);
+```
+| 参数 | 模式 |
+| -- | -- |
+| sleep_mode | 释放的休眠模式等级 |
+
+调用该函数会将对应的模式计数减1,配合 rt_pm_request 使用,释放先前请求的模式。
+
+- 设置运行模式
+```c
+int rt_pm_run_enter(uint8_t run_mode);
+```
+| 参数 | 模式 |
+| -- | -- |
+| run_mode | 设置的运行模式等级 |
+
+run_mode 可以取以下枚举值:
+```c
+enum
+{
+ /* run modes*/
+ PM_RUN_MODE_HIGH_SPEED = 0, /* 高速 */
+ PM_RUN_MODE_NORMAL_SPEED, /* 正常(默认) */
+ PM_RUN_MODE_MEDIUM_SPEED, /* 中速 */
+ PM_RUN_MODE_LOW_SPEED, /* 低速 */
+ PM_RUN_MODE_MAX,
+};
+```
+调用该函数改变 CPU 的运行频率,从而降低运行时的功耗。此函数只提供级别,具体的 CPU 频率应在移植阶段视实际情况而定。
+
+- 设置进入/退出休眠模式的回调通知
+```c
+void rt_pm_notify_set(void (*notify)(uint8_t event, uint8_t mode, void *data), void *data);
+```
+| 参数 | 模式 |
+| -- | -- |
+| notify | 应用的回调函数 |
+| data | 私有数据 |
+
+event 为以下两个枚举值,分别标识进入/退出休眠模式。
+```c
+enum
+{
+ RT_PM_ENTER_SLEEP = 0, /* 进入休眠模式 */
+ RT_PM_EXIT_SLEEP, /* 退出休眠模式 */
+};
+
+```
+
+## 使用说明
+
+- 设置低功耗等级
+
+如果系统需要进入指定指定等级的低功耗,可通过调用 rt_pm_request 实现。如进入深度睡眠模式:
+```c
+/* 请求进入深度睡眠模式 */
+rt_pm_request(PM_SLEEP_MODE_DEEP);
+```
+**注意:**如果程序的其他地方请求了更高的功耗模式,如 Light Mode 或者 Idle Mode,则需要释放相应的模式后,深度睡眠模式才能被进入。
+
+- 保护某个阶段或者过程
+
+特殊情况下,比如某个阶段并不允许系统进入更低的功耗模式,此时可以通过 rt_pm_request 和 rt_pm_release 对该过程进行保护。如 I2C 读取数据期间,不允许进入深度睡眠模式(可能会导致外设停止工作),因此可以做如下处理:
+```c
+/* 请求轻度睡眠模式(I2C外设该模式下正常工作) */
+rt_pm_request(PM_SLEEP_MODE_LIGHT);
+
+/* 读取数据过程 */
+
+/* 释放该模式 */
+rt_pm_release(PM_SLEEP_MODE_LIGHT);
+
+```
+
+- 改变 CPU 运行频率
+
+降低运行频率能有效减少系统的功耗,通过 rt_pm_run_enter 接口改变 CPU 的运行频率。一般地,降频率意味着 CPU 性能降低、处理速度减慢,可能会导致任务的执行时间增长,需要合理进行权衡。
+
+```c
+/* 进入中等速度运行模式 */
+rt_pm_run_enter(PM_RUN_MODE_MEDIUM_SPEED);
+```
+
+## 移植说明
+
+低功耗管理是一项十分细致的任务,开发者在移植时,不仅需要充分了解芯片本身的功耗管理,还需熟悉板卡的外围电路,进入低功耗状态时逐一处理,避免出现外围电路漏电拉升整体功耗的情况。RT-Thread PM 组件对各部分进行抽象,提供不同的 ops 接口供开发者适配。移植时需要关注的部分如下:
+
+```c
+/**
+ * low power mode operations
+ */
+struct rt_pm_ops
+{
+ /* sleep 接口用于适配芯片相关的低功耗特性 */
+ void (*sleep)(struct rt_pm *pm, uint8_t mode);
+ /* run 接口用于运行模式的变频和变电压 */
+ void (*run)(struct rt_pm *pm, uint8_t mode);
+ /* 以下三个接口用于心跳停止后启动低功耗定时器以补偿心跳 */
+ void (*timer_start)(struct rt_pm *pm, rt_uint32_t timeout);
+ void (*timer_stop)(struct rt_pm *pm);
+ rt_tick_t (*timer_get_tick)(struct rt_pm *pm);
+};
+
+/* 该 ops 用于管理外设的功耗 */
+struct rt_device_pm_ops
+{
+ /* 进入休眠模式之前挂起外设,返回非 0 表示外设未就绪,不能进入 */
+ int (*suspend)(const struct rt_device *device, uint8_t mode);
+ /* 从休眠模式退出后恢复外设 */
+ void (*resume)(const struct rt_device *device, uint8_t mode);
+ /* 运行状态下模式改变通知外设处理 */
+ int (*frequency_change)(const struct rt_device *device, uint8_t mode);
+};
+
+/* 注册一个 PM 设备 */
+void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops);
+```
+
+- 芯片自身的功耗特性
+```c
+void (*sleep)(struct rt_pm *pm, uint8_t mode);
+```
+各个芯片对低功耗模式的定义和管理不同,PM 组件将芯片相关的特性抽象为 sleep 接口。该接口适配芯片相关的低功耗管理,当要进入不同的休眠模式时,一些硬件相关的配置,保存等相关处理。
+
+- 休眠的时间补偿
+
+```c
+void (*timer_start)(struct rt_pm *pm, rt_uint32_t timeout);
+void (*timer_stop)(struct rt_pm *pm);
+rt_tick_t (*timer_get_tick)(struct rt_pm *pm);
+```
+
+在某些休眠模式下(Light Sleep 或 Deep Sleep),内核心跳定时器可能会被停止,此时需要对启动一个定时器对休眠的时间进行计量,唤醒后对心跳补偿。时间补偿的定时器必须在该模式下仍能够正常工作,并唤醒系统,否则没有意义!
+
+`timer_start` :启动低功耗定时器,入参为最近的下次任务就绪时间;
+`timer_get_tick` :获取系统被唤醒的睡眠时间;
+`timer_stop` :用于系统唤醒后停止低功耗定时器。
+
+**注意**:休眠模式的时间补偿需要在初始化阶段通过设置 timer_mask 的对应模式的 bit 控制开启。例如需要开启 Deep Sleep 模式下的时间补偿,在实现 timer 相关的 ops 接口后,初始化时设置相应的bit:
+
+```c
+rt_uint8_t timer_mask = 0;
+
+/* 设置 Deep Sleep 模式对应的 bit,使能休眠时间补偿 */
+timer_mask = 1UL << PM_SLEEP_MODE_DEEP;
+
+/* initialize system pm module */
+rt_system_pm_init(&_ops, timer_mask, RT_NULL);
+```
+
+- 运行模式变频
+
+```
+void (*run)(struct rt_pm *pm, uint8_t mode);
+```
+运行模式的变频通过适配 rt_pm_ops 中的 run 接口实现,根据使用场景选择合适的频率。
+
+- 外设的功耗管理
+
+外设的功耗处理是低功耗管理系统的一个重要部分,在进入某些级别的休眠模式时,通常需要对一些外设进行处理,如清空 DMA,关闭时钟或是设置 IO 为复位状态;并在退出休眠后进行恢复。
+此类情况可以通过 `rt_pm_device_register` 接口注册 PM 设备,在进入/退出休眠模式时,会执行注册设备的 `suspend` 和 `resume` 回调;运行模式下的频率改变同样会触发设备的 `frequency_change` 回调。
+
+更详细的移植案例可以参考 RT-Thread 仓库中的 stm32l476-nucleo bsp。
+
+## MSH 命令
+
+### 请求休眠模式
+
+可以使用 `pm_request` 命令请求模式,使用示例如下所示:
+
+```c
+msh />pm_request 0
+msh />
+```
+
+参数取值为 0-5,分别对应以下枚举值:
+
+```c
+enum
+{
+ /* sleep modes */
+ PM_SLEEP_MODE_NONE = 0, /* 活跃状态 */
+ PM_SLEEP_MODE_IDLE, /* 空闲模式(默认) */
+ PM_SLEEP_MODE_LIGHT, /* 轻度睡眠模式 */
+ PM_SLEEP_MODE_DEEP, /* 深度睡眠模式 */
+ PM_SLEEP_MODE_STANDBY, /* 待机模式 */
+ PM_SLEEP_MODE_SHUTDOWN, /* 关断模式 */
+ PM_SLEEP_MODE_MAX,
+};
+```
+
+### 释放休眠模式
+
+可以使用 `pm_release` 命令释放模式,参数取值为 0-5,使用示例如下所示:
+
+```c
+msh />pm_release 0
+msh />
+```
+
+### 设置运行模式
+
+可以使用 `pm_run` 命令切换运行模式,参数取值 0-3,使用示例如下所示
+```c
+msh />pm_run 2
+msh />
+```
+参数取值 0-3
+```
+enum
+{
+ /* run modes*/
+ PM_RUN_MODE_HIGH_SPEED = 0,
+ PM_RUN_MODE_NORMAL_SPEED,
+ PM_RUN_MODE_MEDIUM_SPEED,
+ PM_RUN_MODE_LOW_SPEED,
+ PM_RUN_MODE_MAX,
+};
+```
+### 查看模式状态
+
+可以使用 `pm_dump` 命令查看 PM 组件的模式状态,使用示例如下所示:
+
+```c
+msh >
+msh >pm_dump
+| Power Management Mode | Counter | Timer |
++-----------------------+---------+-------+
+| None Mode | 0 | 0 |
+| Idle Mode | 0 | 0 |
+| LightSleep Mode | 1 | 0 |
+| DeepSleep Mode | 0 | 1 |
+| Standby Mode | 0 | 0 |
+| Shutdown Mode | 0 | 0 |
++-----------------------+---------+-------+
+pm current sleep mode: LightSleep Mode
+pm current run mode: Normal Speed
+msh >
+```
+
+在 `pm_dump` 的模式列表里,休眠模式的优先级是从高到低排列,`Counter` 一栏标识请求的计数值,图中表明 LightSleep 模式被请求一次,因此当前工作在轻度休眠状态;`Timer`
+ 一栏标识是否开启睡眠时间补偿,图中仅深度睡眠(DeepSleep)模式进行时间补偿。
+最下面分别标识当前所处的休眠模式及运行模式等级。
+
+## 常见问题及调试方法
+
+- 系统进入低功耗模式后功耗偏高
+
+根据外围电路,检查设备是否处于合理状态,避免出现外设漏电的情况;
+根据产品自身情况,关闭相应休眠模式期间不使用的外设和时钟。
+
+- 无法进入更低等级的功耗
+
+检查是否未释放高等级的功耗模式,RT-Thread 的 PM 组件使用 `rt_pm_request` 请求休眠模式,当请求高功耗模式后,未进行释放,系统将无法切换至更低等级的功耗。例如,在请求 Light Sleep 模式后,接着请求 Deep Sleep 模式,此时系统仍然处于 Light Sleep 模式。通过调用接口 `rt_pm_request` 释放 Light Sleep 模式,系统会自动切换到 Deep Sleep 模式。
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10IoTboard.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10IoTboard.png
new file mode 100644
index 0000000000000000000000000000000000000000..3771fedd8c7371c9adb32f0b750102cfc7b61400
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10IoTboard.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10IoTboard1.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10IoTboard1.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c685019ad49a68b9133125cf655ec7e07f16969
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10IoTboard1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10board2.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10board2.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0273858b5a0435feeb3ac86f06f5ccbc47e16fa
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10board2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10path.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10path.png
new file mode 100644
index 0000000000000000000000000000000000000000..e60cc1da2d2215897e22d760cc9b906b83dd7b4b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10path.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10pendsv.jpg b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10pendsv.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..c97cc11879585fa2549e696699f474d30f266c68
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10pendsv.jpg differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10project1.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10project1.png
new file mode 100644
index 0000000000000000000000000000000000000000..7258f0b4dadfe71fe92ec74efcc7c8108e178a99
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10project1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10project2.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10project2.png
new file mode 100644
index 0000000000000000000000000000000000000000..057ee5129d9feed81c3b1d356bd1990f97eba3bc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10project2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty1.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty1.png
new file mode 100644
index 0000000000000000000000000000000000000000..26557242eaa380ea104cc257cf5400bcc2cafef9
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty2.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty2.png
new file mode 100644
index 0000000000000000000000000000000000000000..e53a66f542de0a8acce316019ba4eb675d42ba1e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty3.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty3.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5518ac3e9ccbf478a480d0a8d68b08adc7586e8
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty3.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty4.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty4.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a3422b894e74ac714a4c8ec713b371060d199a5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10putty4.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10stack.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10stack.png
new file mode 100644
index 0000000000000000000000000000000000000000..029cb880ee58cb2828dbe209cb366688f4da75de
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10stack.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10switch.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10switch.png
new file mode 100644
index 0000000000000000000000000000000000000000..20a19c94cf35c31d2a6bb4a1e5ced90fa57549c5
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10switch.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10switch2.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10switch2.png
new file mode 100644
index 0000000000000000000000000000000000000000..7422766b58572026f44c9391eef06cc81b66c2c0
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10switch2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10ths_env1.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10ths_env1.png
new file mode 100644
index 0000000000000000000000000000000000000000..68caec49baef4a6fca2528590bd7c82cd84420e1
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10ths_env1.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10ths_env2.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10ths_env2.png
new file mode 100644
index 0000000000000000000000000000000000000000..fed5ee1d880456b2d48cd4755a2d499dac52fb8c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/10ths_env2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/252bfe8648541f2036d0a2aa040776cf.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/252bfe8648541f2036d0a2aa040776cf.png
new file mode 100644
index 0000000000000000000000000000000000000000..057ee5129d9feed81c3b1d356bd1990f97eba3bc
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/252bfe8648541f2036d0a2aa040776cf.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/681450fc51f107be479c393c956c313a.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/681450fc51f107be479c393c956c313a.png
new file mode 100644
index 0000000000000000000000000000000000000000..e60cc1da2d2215897e22d760cc9b906b83dd7b4b
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/681450fc51f107be479c393c956c313a.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/6c08b8b3928e21053abb9b09db333e25.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/6c08b8b3928e21053abb9b09db333e25.png
new file mode 100644
index 0000000000000000000000000000000000000000..29bf2a505d6ec58c2e9d68c54e78d4fc057b2439
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/6c08b8b3928e21053abb9b09db333e25.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/83ea9f351b86425e32343d28a8e2179b.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/83ea9f351b86425e32343d28a8e2179b.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b1fcf784ce284cd655cfcdb37707bdc3680ebe6
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/83ea9f351b86425e32343d28a8e2179b.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/c0fde6c0e89981fefdff6cf0d523ec21.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/c0fde6c0e89981fefdff6cf0d523ec21.png
new file mode 100644
index 0000000000000000000000000000000000000000..f297af7bc0ea305b5103a595836f12ea3855f9e3
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/c0fde6c0e89981fefdff6cf0d523ec21.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/c70cb10dd23808162907cf993e3b53f2.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/c70cb10dd23808162907cf993e3b53f2.png
new file mode 100644
index 0000000000000000000000000000000000000000..fab107038e4d1ea06b20706922cb06d881cda32c
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/c70cb10dd23808162907cf993e3b53f2.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/df52bd3e28126182d945bd487af0debf.png b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/df52bd3e28126182d945bd487af0debf.png
new file mode 100644
index 0000000000000000000000000000000000000000..e53a66f542de0a8acce316019ba4eb675d42ba1e
Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/porting/figures/df52bd3e28126182d945bd487af0debf.png differ
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/porting/porting.md b/rt-thread-version/rt-thread-standard/programming-manual/porting/porting.md
new file mode 100644
index 0000000000000000000000000000000000000000..d84266532e0d25c426507d50c030ede6e7d1d554
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/porting/porting.md
@@ -0,0 +1,379 @@
+# 内核移植
+
+经过前面内核章节的学习,大家对 RT-Thread 也有了不少的了解,但是如何将 RT-Thread 内核移植到不同的硬件平台上,很多人还不一定熟悉。内核移植就是指将 RT-Thread 内核在不同的芯片架构、不同的板卡上运行起来,能够具备线程管理和调度,内存管理,线程间同步和通信、定时器管理等功能。移植可分为 CPU 架构移植和 BSP(Board support package,板级支持包)移植两部分。
+
+本章将展开介绍 CPU 架构移植和 BSP 移植,CPU 架构移植部分会结合 Cortex-M CPU 架构进行介绍,因此有必要回顾下上一章[《中断管理》](../interrupt/interrupt.md)介绍的 “Cortex-M CPU 架构基础” 的内容,本章最后以实际移植到一个开发板的示例展示 RT-Thread 内核移植的完整过程,读完本章,我们将了解如何完成 RT-Thread 的内核移植。
+
+## CPU 架构移植
+
+在嵌入式领域有多种不同 CPU 架构,例如 Cortex-M、ARM920T、MIPS32、RISC-V 等等。为了使 RT-Thread 能够在不同 CPU 架构的芯片上运行,RT-Thread 提供了一个 libcpu 抽象层来适配不同的 CPU 架构。libcpu 层向上对内核提供统一的接口,包括全局中断的开关,线程栈的初始化,上下文切换等。
+
+RT-Thread 的 libcpu 抽象层向下提供了一套统一的 CPU 架构移植接口,这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、Cache 等等内容。下表是 CPU 架构移植需要实现的接口和变量。
+
+ libcpu 移植相关 API
+
+|**函数和变量** |**描述** |
+|--------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
+| rt_base_t rt_hw_interrupt_disable(void); | 关闭全局中断 |
+| void rt_hw_interrupt_enable(rt_base_t level); | 打开全局中断 |
+| rt_uint8_t \*rt_hw_stack_init(void \*tentry, void \*parameter, rt_uint8_t \*stack_addr, void \*texit); | 线程栈的初始化,内核在线程创建和线程初始化里面会调用这个函数 |
+| void rt_hw_context_switch_to(rt_uint32 to); | 没有来源线程的上下文切换,在调度器启动第一个线程的时候调用,以及在 signal 里面会调用 |
+| void rt_hw_context_switch(rt_uint32 from, rt_uint32 to); | 从 from 线程切换到 to 线程,用于线程和线程之间的切换 |
+| void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to); | 从 from 线程切换到 to 线程,用于中断里面进行切换的时候使用 |
+| rt_uint32_t rt_thread_switch_interrupt_flag; | 表示需要在中断里进行切换的标志 |
+| rt_uint32_t rt_interrupt_from_thread, rt_interrupt_to_thread; | 在线程进行上下文切换时候,用来保存 from 和 to 线程 |
+
+### 实现全局中断开关
+
+无论内核代码还是用户的代码,都可能存在一些变量,需要在多个线程或者中断里面使用,如果没有相应的保护机制,那就可能导致临界区问题。RT-Thread 里为了解决这个问题,提供了一系列的线程间同步和通信机制来解决。但是这些机制都需要用到 libcpu 里提供的全局中断开关函数。它们分别是:
+
+```c
+/* 关闭全局中断 */
+rt_base_t rt_hw_interrupt_disable(void);
+
+/* 打开全局中断 */
+void rt_hw_interrupt_enable(rt_base_t level);
+```
+
+下面介绍在 Cortex-M 架构上如何实现这两个函数,前文中曾提到过,Cortex-M 为了快速开关中断,实现了 CPS 指令,可以用在此处。
+
+```c
+CPSID I ;PRIMASK=1, ; 关中断
+CPSIE I ;PRIMASK=0, ; 开中断
+```
+
+#### 关闭全局中断
+
+在 rt_hw_interrupt_disable() 函数里面需要依序完成的功能是:
+
+1). 保存当前的全局中断状态,并把状态作为函数的返回值。
+
+2). 关闭全局中断。
+
+基于 MDK,在 Cortex-M 内核上实现关闭全局中断,如下代码所示:
+
+ 关闭全局中断
+
+```c
+;/*
+; * rt_base_t rt_hw_interrupt_disable(void);
+; */
+rt_hw_interrupt_disable PROC ;PROC 伪指令定义函数
+ EXPORT rt_hw_interrupt_disable ;EXPORT 输出定义的函数,类似于 C 语言 extern
+ MRS r0, PRIMASK ; 读取 PRIMASK 寄存器的值到 r0 寄存器
+ CPSID I ; 关闭全局中断
+ BX LR ; 函数返回
+ ENDP ;ENDP 函数结束
+```
+
+上面的代码首先是使用 MRS 指令将 PRIMASK 寄存器的值保存到 r0 寄存器里,然后使用 “CPSID I” 指令关闭全局中断,最后使用 BX 指令返回。r0 存储的数据就是函数的返回值。中断可以发生在 “MRS r0, PRIMASK” 指令和 “CPSID I” 之间,这并不会导致全局中断状态的错乱。
+
+关于寄存器在函数调用的时候和在中断处理程序里是如何管理的,不同的 CPU 架构有不同的约定。在 ARM 官方手册《Procedure Call Standard for the ARM ® Architecture》里可以找到关于 Cortex-M 的更详细的介绍寄存器使用的约定。
+
+#### 打开全局中断
+
+在 rt_hw_interrupt_enable(rt_base_t level) 里,将变量 level 作为需要恢复的状态,覆盖芯片的全局中断状态。
+
+基于 MDK,在 Cortex-M 内核上的实现打开全局中断,如下代码所示:
+
+ 打开全局中断
+
+```c
+;/*
+; * void rt_hw_interrupt_enable(rt_base_t level);
+; */
+rt_hw_interrupt_enable PROC ; PROC 伪指令定义函数
+ EXPORT rt_hw_interrupt_enable ; EXPORT 输出定义的函数,类似于 C 语言 extern
+ MSR PRIMASK, r0 ; 将 r0 寄存器的值写入到 PRIMASK 寄存器
+ BX LR ; 函数返回
+ ENDP ; ENDP 函数结束
+```
+
+上面的代码首先是使用 MSR 指令将 r0 的值寄存器写入到 PRIMASK 寄存器,从而恢复之前的中断状态。
+
+### 实现线程栈初始化
+
+在动态创建线程和初始化线程的时候,会使用到内部的线程初始化函数_rt_thread_init(),_rt_thread_init() 函数会调用栈初始化函数 rt_hw_stack_init(),在栈初始化函数里会手动构造一个上下文内容,这个上下文内容将被作为每个线程第一次执行的初始值。上下文在栈里的排布如下图所示:
+
+
+
+下代码是栈初始化的代码:
+
+ 在栈里构建上下文
+
+```c
+rt_uint8_t *rt_hw_stack_init(void *tentry,
+ void *parameter,
+ rt_uint8_t *stack_addr,
+ void *texit)
+{
+ struct stack_frame *stack_frame;
+ rt_uint8_t *stk;
+ unsigned long i;
+
+ /* 对传入的栈指针做对齐处理 */
+ stk = stack_addr + sizeof(rt_uint32_t);
+ stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
+ stk -= sizeof(struct stack_frame);
+
+ /* 得到上下文的栈帧的指针 */
+ stack_frame = (struct stack_frame *)stk;
+
+ /* 把所有寄存器的默认值设置为 0xdeadbeef */
+ for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
+ {
+ ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
+ }
+
+ /* 根据 ARM APCS 调用标准,将第一个参数保存在 r0 寄存器 */
+ stack_frame->exception_stack_frame.r0 = (unsigned long)parameter;
+ /* 将剩下的参数寄存器都设置为 0 */
+ stack_frame->exception_stack_frame.r1 = 0; /* r1 寄存器 */
+ stack_frame->exception_stack_frame.r2 = 0; /* r2 寄存器 */
+ stack_frame->exception_stack_frame.r3 = 0; /* r3 寄存器 */
+ /* 将 IP(Intra-Procedure-call scratch register.) 设置为 0 */
+ stack_frame->exception_stack_frame.r12 = 0; /* r12 寄存器 */
+ /* 将线程退出函数的地址保存在 lr 寄存器 */
+ stack_frame->exception_stack_frame.lr = (unsigned long)texit;
+ /* 将线程入口函数的地址保存在 pc 寄存器 */
+ stack_frame->exception_stack_frame.pc = (unsigned long)tentry;
+ /* 设置 psr 的值为 0x01000000L,表示默认切换过去是 Thumb 模式 */
+ stack_frame->exception_stack_frame.psr = 0x01000000L;
+
+ /* 返回当前线程的栈地址 */
+ return stk;
+}
+```
+
+### 实现上下文切换
+
+在不同的 CPU 架构里,线程之间的上下文切换和中断到线程的上下文切换,上下文的寄存器部分可能是有差异的,也可能是一样的。在 Cortex-M 里面上下文切换都是统一使用 PendSV 异常来完成,切换部分并没有差异。但是为了能适应不同的 CPU 架构,RT-Thread 的 libcpu 抽象层还是需要实现三个线程切换相关的函数:
+
+1) rt_hw_context_switch_to():没有来源线程,切换到目标线程,在调度器启动第一个线程的时候被调用。
+
+2) rt_hw_context_switch():在线程环境下,从当前线程切换到目标线程。
+
+3) rt_hw_context_switch_interrupt ():在中断环境下,从当前线程切换到目标线程。
+
+在线程环境下进行切换和在中断环境进行切换是存在差异的。线程环境下,如果调用 rt_hw_context_switch() 函数,那么可以马上进行上下文切换;而在中断环境下,需要等待中断处理函数完成之后才能进行切换。
+
+由于这种差异,在 ARM9 等平台,rt_hw_context_switch() 和 rt_hw_context_switch_interrupt() 的实现并不一样。在中断处理程序里如果触发了线程的调度,调度函数里会调用 rt_hw_context_switch_interrupt() 触发上下文切换。中断处理程序里处理完中断事务之后,中断退出之前,检查 rt_thread_switch_interrupt_flag 变量,如果该变量的值为 1,就根据 rt_interrupt_from_thread 变量和 rt_interrupt_to_thread 变量,完成线程的上下文切换。
+
+在 Cortex-M 处理器架构里,基于自动部分压栈和 PendSV 的特性,上下文切换可以实现地更加简洁。
+
+线程之间的上下文切换,如下图表示:
+
+
+
+硬件在进入 PendSV 中断之前自动保存了 from 线程的 PSR、PC、LR、R12、R3-R0 寄存器,然后 PendSV 里保存 from 线程的 R11\~R4 寄存器,以及恢复 to 线程的 R4\~R11 寄存器,最后硬件在退出 PendSV 中断之后,自动恢复 to 线程的 R0\~R3、R12、LR、PC、PSR 寄存器。
+
+中断到线程的上下文切换可以用下图表示:
+
+
+
+硬件在进入中断之前自动保存了 from 线程的 PSR、PC、LR、R12、R3-R0 寄存器,然后触发了 PendSV 异常。在 PendSV 异常处理函数里保存 from 线程的 R11\~R4 寄存器,以及恢复 to 线程的 R4\~R11 寄存器,最后硬件在退出 PendSV 中断之后,自动恢复 to 线程的 R0\~R3、R12、PSR、PC、LR 寄存器。
+
+显然,在 Cortex-M 内核里 rt_hw_context_switch() 和 rt_hw_context_switch_interrupt() 功能一致,都是在 PendSV 里完成剩余上下文的保存和回复。所以我们仅仅需要实现一份代码,简化移植的工作。
+
+#### 实现 rt_hw_context_switch_to()
+
+rt_hw_context_switch_to() 只有目标线程,没有来源线程。这个函数里实现切换到指定线程的功能,下图是流程图:
+
+
+
+在 Cortex-M3 内核上的 rt_hw_context_switch_to() 实现(基于 MDK),如下代码所示:
+
+ MDK 版 rt_hw_context_switch_to() 实现
+
+```c
+;/*
+; * void rt_hw_context_switch_to(rt_uint32 to);
+; * r0 --> to
+; * this fucntion is used to perform the first thread switch
+; */
+rt_hw_context_switch_to PROC
+ EXPORT rt_hw_context_switch_to
+ ; r0 的值是一个指针,该指针指向 to 线程的线程控制块的 SP 成员
+ ; 将 r0 寄存器的值保存到 rt_interrupt_to_thread 变量里
+ LDR r1, =rt_interrupt_to_thread
+ STR r0, [r1]
+
+ ; 设置 from 线程为空,表示不需要从保存 from 的上下文
+ LDR r1, =rt_interrupt_from_thread
+ MOV r0, #0x0
+ STR r0, [r1]
+
+ ; 设置标志为 1,表示需要切换,这个变量将在 PendSV 异常处理函数里切换的时被清零
+ LDR r1, =rt_thread_switch_interrupt_flag
+ MOV r0, #1
+ STR r0, [r1]
+
+ ; 设置 PendSV 异常优先级为最低优先级
+ LDR r0, =NVIC_SYSPRI2
+ LDR r1, =NVIC_PENDSV_PRI
+ LDR.W r2, [r0,#0x00] ; read
+ ORR r1,r1,r2 ; modify
+ STR r1, [r0] ; write-back
+
+ ; 触发 PendSV 异常 (将执行 PendSV 异常处理程序)
+ LDR r0, =NVIC_INT_CTRL
+ LDR r1, =NVIC_PENDSVSET
+ STR r1, [r0]
+
+ ; 放弃芯片启动到第一次上下文切换之前的栈内容,将 MSP 设置启动时的值
+ LDR r0, =SCB_VTOR
+ LDR r0, [r0]
+ LDR r0, [r0]
+ MSR msp, r0
+
+ ; 使能全局中断和全局异常,使能之后将进入 PendSV 异常处理函数
+ CPSIE F
+ CPSIE I
+
+ ; 不会执行到这里
+ ENDP
+```
+
+#### 实现 rt_hw_context_switch()/ rt_hw_context_switch_interrupt()
+
+函数 rt_hw_context_switch() 和函数 rt_hw_context_switch_interrupt() 都有两个参数,分别是 from 线程和 to 线程。它们实现从 from 线程切换到 to 线程的功能。下图是具体的流程图:
+
+
+
+在 Cortex-M3 内核上的 rt_hw_context_switch() 和 rt_hw_context_switch_interrupt() 实现(基于 MDK),如下代码的所示:
+
+ rt_hw_context_switch()/rt_hw_context_switch_interrupt() 实现
+
+```c
+;/*
+; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
+; * r0 --> from
+; * r1 --> to
+; */
+rt_hw_context_switch_interrupt
+ EXPORT rt_hw_context_switch_interrupt
+rt_hw_context_switch PROC
+ EXPORT rt_hw_context_switch
+
+ ; 检查 rt_thread_switch_interrupt_flag 变量是否为 1
+ ; 如果变量为 1 就跳过更新 from 线程的内容
+ LDR r2, =rt_thread_switch_interrupt_flag
+ LDR r3, [r2]
+ CMP r3, #1
+ BEQ _reswitch
+ ; 设置 rt_thread_switch_interrupt_flag 变量为 1
+ MOV r3, #1
+ STR r3, [r2]
+
+ ; 从参数 r0 里更新 rt_interrupt_from_thread 变量
+ LDR r2, =rt_interrupt_from_thread
+ STR r0, [r2]
+
+_reswitch
+ ; 从参数 r1 里更新 rt_interrupt_to_thread 变量
+ LDR r2, =rt_interrupt_to_thread
+ STR r1, [r2]
+
+ ; 触发 PendSV 异常,将进入 PendSV 异常处理函数里完成上下文切换
+ LDR r0, =NVIC_INT_CTRL
+ LDR r1, =NVIC_PENDSVSET
+ STR r1, [r0]
+ BX LR
+```
+
+#### 实现 PendSV 中断
+
+在 Cortex-M3 里,PendSV 中断处理函数是 PendSV_Handler()。在 PendSV_Handler() 里完成线程切换的实际工作,下图是具体的流程图:
+
+
+
+如下代码是 PendSV_Handler 实现:
+
+```c
+; r0 --> switch from thread stack
+; r1 --> switch to thread stack
+; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
+PendSV_Handler PROC
+ EXPORT PendSV_Handler
+
+ ; 关闭全局中断
+ MRS r2, PRIMASK
+ CPSID I
+
+ ; 检查 rt_thread_switch_interrupt_flag 变量是否为 0
+ ; 如果为零就跳转到 pendsv_exit
+ LDR r0, =rt_thread_switch_interrupt_flag
+ LDR r1, [r0]
+ CBZ r1, pendsv_exit ; pendsv already handled
+
+ ; 清零 rt_thread_switch_interrupt_flag 变量
+ MOV r1, #0x00
+ STR r1, [r0]
+
+ ; 检查 rt_thread_switch_interrupt_flag 变量
+ ; 如果为 0,就不进行 from 线程的上下文保存
+ LDR r0, =rt_interrupt_from_thread
+ LDR r1, [r0]
+ CBZ r1, switch_to_thread
+
+ ; 保存 from 线程的上下文
+ MRS r1, psp ; 获取 from 线程的栈指针
+ STMFD r1!, {r4 - r11} ; 将 r4~r11 保存到线程的栈里
+ LDR r0, [r0]
+ STR r1, [r0] ; 更新线程的控制块的 SP 指针
+
+switch_to_thread
+ LDR r1, =rt_interrupt_to_thread
+ LDR r1, [r1]
+ LDR r1, [r1] ; 获取 to 线程的栈指针
+
+ LDMFD r1!, {r4 - r11} ; 从 to 线程的栈里恢复 to 线程的寄存器值
+ MSR psp, r1 ; 更新 r1 的值到 psp
+
+pendsv_exit
+ ; 恢复全局中断状态
+ MSR PRIMASK, r2
+
+ ; 修改 lr 寄存器的 bit2,确保进程使用 PSP 堆栈指针
+ ORR lr, lr, #0x04
+ ; 退出中断函数
+ BX lr
+ ENDP
+```
+
+### 实现时钟节拍
+
+有了开关全局中断和上下文切换功能的基础,RTOS 就可以进行线程的创建、运行、调度等功能了。有了时钟节拍支持,RT-Thread 可以实现对相同优先级的线程采用时间片轮转的方式来调度,实现定时器功能,实现 rt_thread_delay() 延时函数等等。
+
+libcpu 的移植需要完成的工作,就是确保 rt_tick_increase() 函数会在时钟节拍的中断里被周期性的调用,调用周期取决于 rtconfig.h 的宏 RT_TICK_PER_SECOND 的值。
+
+在 Cortex M 中,实现 SysTick 的中断处理函数即可实现时钟节拍功能。
+
+```c
+void SysTick_Handler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ rt_tick_increase();
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+```
+
+BSP 移植
+-------
+
+相同的 CPU 架构在实际项目中,不同的板卡上可能使用相同的 CPU 架构,搭载不同的外设资源,完成不同的产品,所以我们也需要针对板卡做适配工作。RT-Thread 提供了 BSP 抽象层来适配常见的板卡。如果希望在一个板卡上使用 RT-Thread 内核,除了需要有相应的芯片架构的移植,还需要有针对板卡的移植,也就是实现一个基本的 BSP。主要任务是建立让操作系统运行的基本环境,需要完成的主要工作是:
+
+1)初始化 CPU 内部寄存器,设定 RAM 工作时序。
+
+2)实现时钟驱动及中断控制器驱动,完善中断管理。
+
+3)实现串口和 GPIO 驱动。
+
+4)初始化动态内存堆,实现动态堆内存管理。
+
+
+
diff --git a/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md b/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md
new file mode 100644
index 0000000000000000000000000000000000000000..4ca47f6ede6acf422c1377a15cc07d60ae782274
--- /dev/null
+++ b/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md
@@ -0,0 +1,2632 @@
+# POSIX 接口
+
+## Pthreads 简介
+
+POSIX Threads 简称 Pthreads,POSIX 是 “Portable Operating System
+Interface”(可移植操作系统接口) 的缩写,POSIX 是 IEEE Computer
+Society 为了提高不同操作系统的兼容性和应用程序的可移植性而制定的一套标准。Pthreads 是线程的 POSIX 标准,被定义在 POSIX.1c,
+Threads extensions (IEEE
+Std1003.1c-1995)标准里,该标准定义了一套 C 程序语言的类型、函数和常量。定义在 pthread.h 头文件和一个线程库里,大约有 100 个 API,所有 API 都带有 “pthread_” 前缀,可以分为 4 大类:
+
+- 线程管理(Thread
+ management):包括线程创建(creating)、分离(detaching)、连接(joining)及设置和查询线程属性的函数等。
+
+- 互斥锁(Mutex):“mutual
+ exclusion” 的缩写,用了限制线程对共享数据的访问,保护共享数据的完整性。包括创建、销毁、锁定和解锁互斥锁及一些用于设置或修改互斥量属性等函数。
+
+- 条件变量(Condition
+ variable):用于共享一个互斥量的线程间的通信。包括条件变量的创建、销毁、等待和发送信号(signal)等函数。
+
+- 读写锁(read/write
+ lock)和屏障(barrier):包括读写锁和屏障的创建、销毁、等待及相关属性设置等函数。
+
+- POSIX 信号量(semaphore)和 Pthreads 一起使用,但不是 Pthreads 标准定义的一部分,被定义在 POSIX.1b,
+ Real-time extensions (IEEE
+ Std1003.1b-1993)标准里。因此信号量相关函数的前缀是 “sem_” 而不是“pthread_”。
+
+- 消息队列(Message
+ queue)和信号量一样,和 Pthreads 一起使用,也不是 Pthreads 标准定义的一部分,被定义在 IEEE
+ Std 1003.1-2001 标准里。消息队列相关函数的前缀是 “mq_”。
+
+| 函数前缀 | 函数组 |
+|----------------------|----------------------|
+| `pthread_ ` | 线程本身和各种相关函数 |
+| `pthread_attr_` | 线程属性对象 |
+| `Pthread_mutex_` | 互斥锁 |
+| `pthread_mutexattr_` | 互斥锁属性对象 |
+| `pthread_cond_ ` | 条件变量 |
+| `pthread_condattr_` | 条件变量属性对象 |
+| `pthread_rwlock_` | 读写锁 |
+| `pthread_rwlockattr_` | 读写锁属性对象 |
+| `pthread_spin_` | 自旋锁 |
+| `pthread_barrier_ ` | 屏障 |
+| `pthread_barrierattr_` | 屏障属性对象 |
+| `sem_` | 信号量 |
+| `mq_ ` | 消息队列 |
+
+绝大部分 Pthreads 的函数执行成功则返回 0 值,不成功则返回一个包含在 `errno.h` 头文件中的错误代码。很多操作系统都支持 Pthreads,比如 Linux、MacOSX、 Android
+和 Solaris,因此使用 Pthreads 的函数编写的应用程序有很好的可移植性,可以在很多支持 Pthreads 的平台上直接编译运行。
+
+### 在 RT-Thread 中使用 POSIX
+
+在 RT-Thread 中使用 POSIX API 接口包括几个部分:libc(例如 newlib),filesystem,pthread 等。需要在 rtconfig.h 中打开相关的选项:
+
+``` c
+#define RT_USING_LIBC
+#define RT_USING_DFS
+#define RT_USING_DFS_DEVFS
+#define RT_USING_PTHREADS
+```
+
+RT-Thread 实现了 Pthreads 的大部分函数和常量,按照 POSIX 标准定义在 pthread.h、mqueue.h、semaphore.h 和 sched.h 头文件里。Pthreads 是 libc 的一个子库,RT-Thread 中的 Pthreads 是基于 RT-Thread 内核函数的封装,使其符合 POSIX 标准。后续章节会详细介绍 RT-Thread 中实现的 Pthreads 函数及相关功能。
+
+## 线程
+
+### 线程句柄
+
+``` c
+typedef rt_thread_t pthread_t;
+```
+
+pthread_t 是 rt_thread_t 类型的重定义,定义在 pthread.h 头文件里。rt_thread_t 是 RT-Thread 的线程句柄(或线程标识符),是指向线程控制块的指针。在创建线程前需要先定义一个 pthread_t 类型的变量。每个线程都对应了自己的线程控制块,线程控制块是操作系统用于控制线程的一个数据结构,它存放了线程的一些信息,例如优先级,线程名称和线程堆栈地址等。线程控制块及线程具体信息在 RT-Thread 编程手册的线程调度与管理一章有详细的介绍。
+
+### 创建线程
+
+``` c
+int pthread_create (pthread_t *tid,
+ const pthread_attr_t *attr,
+ void *(*start) (void *), void *arg);
+```
+
+| **参数** | **描述** |
+|----------|------------------------------------------------------|
+| tid | 指向线程句柄 (线程标识符) 的指针,不能为 NULL |
+| attr | 指向线程属性的指针,如果使用 NULL,则使用默认的线程属性 |
+| start | 线程入口函数地址 |
+| arg | 传递给线程入口函数的参数 |
+|**返回**| —— |
+| 0 | 成功 |
+| EINVAL | 参数无效 |
+| ENOMEM | 动态分配内存失败 |
+
+此函数创建一个 pthread 线程。此函数会动态分配 POSIX 线程数据块和 RT-Thread 线程控制块,并把线程控制块的起始地址(线程 ID)保存在参数 tid 指向的内存里,此线程标识符可用于在其他线程中操作此线程;并把 attr 指向的线程属性、start 指向的线程入口函数及入口函数参数 arg 保存在线程数据块和线程控制块里。如果线程创建成功,线程立刻进入就绪态,参与系统的调度,如果线程创建失败,则会释放之前线程占有的资源。
+
+关于线程属性及相关函数会在线程高级编程一章里有详细介绍,一般情况下采用默认属性就可以。
+
+> [!NOTE]
+> 注:创建出 pthread 线程后,如果线程需要重复创建使用,需要设置 pthread 线程为 detach 模式,或者使用 pthread_join 等待创建后的 pthread 线程结束。
+
+#### 创建线程示例代码
+
+以下程序会初始化 2 个线程,它们拥有共同的入口函数,但是它们的入口参数不相同。其他的,它们具备相同的优先级,并以时间片进行轮转调度。
+
+``` c
+#include
+#include
+#include
+
+/* 线程控制块 */
+static pthread_t tid1;
+static pthread_t tid2;
+
+/* 函数返回值检查 */
+static void check_result(char* str,int result)
+{
+ if (0 == result)
+ {
+ printf("%s successfully!\n",str);
+ }
+ else
+ {
+ printf("%s failed! error code is %d\n",str,result);
+ }
+}
+
+/* 线程入口函数 */
+static void* thread_entry(void* parameter)
+{
+ int count = 0;
+ int no = (int) parameter; /* 获得线程的入口参数 */
+
+ while (1)
+ {
+ /* 打印输出线程计数值 */
+ printf("thread%d count: %d\n", no, count ++);
+
+ sleep(2); /* 休眠 2 秒 */
+ }
+}
+
+/* 用户应用入口 */
+int rt_application_init()
+{
+ int result;
+
+ /* 创建线程 1, 属性为默认值,入口函数是 thread_entry,入口函数参数是 1 */
+ result = pthread_create(&tid1,NULL,thread_entry,(void*)1);
+ check_result("thread1 created", result);
+
+ /* 创建线程 2, 属性为默认值,入口函数是 thread_entry,入口函数参数是 2 */
+ result = pthread_create(&tid2,NULL,thread_entry,(void*)2);
+ check_result("thread2 created", result);
+
+ return 0;
+}
+```
+
+### 脱离线程
+
+``` c
+int pthread_detach (pthread_t thread);
+```
+
+| **参数** | **描述** |
+|------|----------------------|
+| thread | 线程句柄(线程标识符) |
+|**返回**| —— |
+| 0 | 成功 |
+
+调用此函数,如果 pthread 线程没有结束,则将 thread 线程属性的分离状态设置为 detached;当 thread 线程已经结束时,系统将回收 pthread 线程占用的资源。
+
+使用方法:子线程调用 pthread_detach(pthread_self())(pthread_self() 返回当前调用线程的线程句柄),或者其他线程调用 pthread_detach(thread_id)。关于线程属性的分离状态会在后面详细介绍。
+
+> [!NOTE]
+> 注:一旦线程属性的分离状态设置为 detached,该线程不能被 pthread_join() 函数等待或者重新被设置为 detached。
+
+#### 脱离线程示例代码
+
+以下程序会初始化 2 个线程,它们拥有相同的优先级,并按照时间片轮转调度。2 个线程都会被设置为脱离状态,2 个线程循环打印 3 次信息后自动退出,退出后系统将会自动回收其资源。
+
+``` c
+#include
+#include
+#include
+
+/* 线程控制块 */
+static pthread_t tid1;
+static pthread_t tid2;
+
+/* 函数返回值检查 */
+static void check_result(char* str,int result)
+{
+ if (0 == result)
+ {
+ printf("%s successfully!\n",str);
+ }
+ else
+ {
+ printf("%s failed! error code is %d\n",str,result);
+ }
+}
+
+/* 线程 1 入口函数 */
+static void* thread1_entry(void* parameter)
+{
+ int i;
+
+ printf("i'm thread1 and i will detach myself!\n");
+ pthread_detach(pthread_self()); /* 线程 1 脱离自己 */
+
+ for (i = 0;i < 3;i++) /* 循环打印 3 次信息 */
+ {
+ printf("thread1 run count: %d\n",i);
+ sleep(2); /* 休眠 2 秒 */
+ }
+
+ printf("thread1 exited!\n");
+ return NULL;
+}
+
+/* 线程 2 入口函数 */
+static void* thread2_entry(void* parameter)
+{
+ int i;
+
+ for (i = 0;i < 3;i++) /* 循环打印 3 次信息 */
+ {
+ printf("thread2 run count: %d\n",i);
+ sleep(2); /* 休眠 2 秒 */
+ }
+
+ printf("thread2 exited!\n");
+ return NULL;
+}
+/* 用户应用入口 */
+int rt_application_init()
+{
+ int result;
+
+ /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable,
+ * 入口函数是 thread1_entry,入口函数参数为 NULL */
+ result = pthread_create(&tid1,NULL,thread1_entry,NULL);
+ check_result("thread1 created",result);
+
+ /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable,
+ * 入口函数是 thread2_entry,入口函数参数为 NULL */
+ result = pthread_create(&tid2,NULL,thread2_entry,NULL);
+ check_result("thread2 created",result);
+
+ pthread_detach(tid2); /* 脱离线程 2 */
+
+ return 0;
+}
+```
+
+### 等待线程结束
+
+``` c
+int pthread_join (pthread_t thread, void**value_ptr);
+```
+
+| **参数** | **描述** |
+|----------|----------------------|
+| thread | 线程句柄(线程标识符) |
+| value_ptr | 用户定义的指针,用来存储被等待线程的返回值地址,可由函数 pthread_join() 获取 |
+|**返回**| —— |
+| 0 | 成功 |
+| EDEADLK | 线程 join 自己 |
+| EINVAL | join 一个分离状态为 detached 的线程 |
+| ESRCH | 找不到 thread 线程 |
+
+此函数会使调用该函数的线程以阻塞的方式等待线程分离属性为 joinable 的 thread 线程运行结束,并获得 thread 线程的返回值,返回值的地址保存在 value_ptr 里,并释放 thread 线程占用的资源。
+
+pthread_join() 和 pthread_detach() 函数功能类似,都是在线程结束后用来回收线程占用的资源。线程不能等待自己结束,thread 线程的分离状态必须是 joinable,一个线程只对应一次 `pthread_join()` 调用。分离状态为 joinable 的线程仅当有其他线程对其执行了 `pthread_join()` 后,它所占用的资源才会释放。因此为了避免内存泄漏,所有会结束运行的线程,分离状态要么已设为 detached,要么使用 pthread_join() 来回收其占用的资源。
+
+#### 等待线程结束示例代码
+
+以下程序代码会初始化 2 个线程,它们拥有相同的优先级,相同优先级的线程是按照时间片轮转调度。2 个线程属性的分离状态为默认值 joinable,线程 1 先开始运行,循环打印 3 次信息后结束。线程 2 调用 pthread_join() 阻塞等待线程 1 结束,并回收线程 1 占用的资源,然后线程 2 每隔 2 秒钟会打印一次信息。
+
+``` c
+#include
+#include