代码拉取完成,页面将自动刷新
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<!-- 2022-12-11 Sun 00:26 -->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>iGEM 2022 冬季学期培训内容</title>
<meta name="author" content="凉凉" />
<meta name="generator" content="Org Mode" />
<style>
#content { max-width: 60em; margin: auto; }
.title { text-align: center;
margin-bottom: .2em; }
.subtitle { text-align: center;
font-size: medium;
font-weight: bold;
margin-top:0; }
.todo { font-family: monospace; color: red; }
.done { font-family: monospace; color: green; }
.priority { font-family: monospace; color: orange; }
.tag { background-color: #eee; font-family: monospace;
padding: 2px; font-size: 80%; font-weight: normal; }
.timestamp { color: #bebebe; }
.timestamp-kwd { color: #5f9ea0; }
.org-right { margin-left: auto; margin-right: 0px; text-align: right; }
.org-left { margin-left: 0px; margin-right: auto; text-align: left; }
.org-center { margin-left: auto; margin-right: auto; text-align: center; }
.underline { text-decoration: underline; }
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
p.verse { margin-left: 3%; }
pre {
border: 1px solid #e6e6e6;
border-radius: 3px;
background-color: #f2f2f2;
padding: 8pt;
font-family: monospace;
overflow: auto;
margin: 1.2em;
}
pre.src {
position: relative;
overflow: auto;
}
pre.src:before {
display: none;
position: absolute;
top: -8px;
right: 12px;
padding: 3px;
color: #555;
background-color: #f2f2f299;
}
pre.src:hover:before { display: inline; margin-top: 14px;}
/* Languages per Org manual */
pre.src-asymptote:before { content: 'Asymptote'; }
pre.src-awk:before { content: 'Awk'; }
pre.src-authinfo::before { content: 'Authinfo'; }
pre.src-C:before { content: 'C'; }
/* pre.src-C++ doesn't work in CSS */
pre.src-clojure:before { content: 'Clojure'; }
pre.src-css:before { content: 'CSS'; }
pre.src-D:before { content: 'D'; }
pre.src-ditaa:before { content: 'ditaa'; }
pre.src-dot:before { content: 'Graphviz'; }
pre.src-calc:before { content: 'Emacs Calc'; }
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
pre.src-fortran:before { content: 'Fortran'; }
pre.src-gnuplot:before { content: 'gnuplot'; }
pre.src-haskell:before { content: 'Haskell'; }
pre.src-hledger:before { content: 'hledger'; }
pre.src-java:before { content: 'Java'; }
pre.src-js:before { content: 'Javascript'; }
pre.src-latex:before { content: 'LaTeX'; }
pre.src-ledger:before { content: 'Ledger'; }
pre.src-lisp:before { content: 'Lisp'; }
pre.src-lilypond:before { content: 'Lilypond'; }
pre.src-lua:before { content: 'Lua'; }
pre.src-matlab:before { content: 'MATLAB'; }
pre.src-mscgen:before { content: 'Mscgen'; }
pre.src-ocaml:before { content: 'Objective Caml'; }
pre.src-octave:before { content: 'Octave'; }
pre.src-org:before { content: 'Org mode'; }
pre.src-oz:before { content: 'OZ'; }
pre.src-plantuml:before { content: 'Plantuml'; }
pre.src-processing:before { content: 'Processing.js'; }
pre.src-python:before { content: 'Python'; }
pre.src-R:before { content: 'R'; }
pre.src-ruby:before { content: 'Ruby'; }
pre.src-sass:before { content: 'Sass'; }
pre.src-scheme:before { content: 'Scheme'; }
pre.src-screen:before { content: 'Gnu Screen'; }
pre.src-sed:before { content: 'Sed'; }
pre.src-sh:before { content: 'shell'; }
pre.src-sql:before { content: 'SQL'; }
pre.src-sqlite:before { content: 'SQLite'; }
/* additional languages in org.el's org-babel-load-languages alist */
pre.src-forth:before { content: 'Forth'; }
pre.src-io:before { content: 'IO'; }
pre.src-J:before { content: 'J'; }
pre.src-makefile:before { content: 'Makefile'; }
pre.src-maxima:before { content: 'Maxima'; }
pre.src-perl:before { content: 'Perl'; }
pre.src-picolisp:before { content: 'Pico Lisp'; }
pre.src-scala:before { content: 'Scala'; }
pre.src-shell:before { content: 'Shell Script'; }
pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
/* additional language identifiers per "defun org-babel-execute"
in ob-*.el */
pre.src-cpp:before { content: 'C++'; }
pre.src-abc:before { content: 'ABC'; }
pre.src-coq:before { content: 'Coq'; }
pre.src-groovy:before { content: 'Groovy'; }
/* additional language identifiers from org-babel-shell-names in
ob-shell.el: ob-shell is the only babel language using a lambda to put
the execution function name together. */
pre.src-bash:before { content: 'bash'; }
pre.src-csh:before { content: 'csh'; }
pre.src-ash:before { content: 'ash'; }
pre.src-dash:before { content: 'dash'; }
pre.src-ksh:before { content: 'ksh'; }
pre.src-mksh:before { content: 'mksh'; }
pre.src-posh:before { content: 'posh'; }
/* Additional Emacs modes also supported by the LaTeX listings package */
pre.src-ada:before { content: 'Ada'; }
pre.src-asm:before { content: 'Assembler'; }
pre.src-caml:before { content: 'Caml'; }
pre.src-delphi:before { content: 'Delphi'; }
pre.src-html:before { content: 'HTML'; }
pre.src-idl:before { content: 'IDL'; }
pre.src-mercury:before { content: 'Mercury'; }
pre.src-metapost:before { content: 'MetaPost'; }
pre.src-modula-2:before { content: 'Modula-2'; }
pre.src-pascal:before { content: 'Pascal'; }
pre.src-ps:before { content: 'PostScript'; }
pre.src-prolog:before { content: 'Prolog'; }
pre.src-simula:before { content: 'Simula'; }
pre.src-tcl:before { content: 'tcl'; }
pre.src-tex:before { content: 'TeX'; }
pre.src-plain-tex:before { content: 'Plain TeX'; }
pre.src-verilog:before { content: 'Verilog'; }
pre.src-vhdl:before { content: 'VHDL'; }
pre.src-xml:before { content: 'XML'; }
pre.src-nxml:before { content: 'XML'; }
/* add a generic configuration mode; LaTeX export needs an additional
(add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
pre.src-conf:before { content: 'Configuration File'; }
table { border-collapse:collapse; }
caption.t-above { caption-side: top; }
caption.t-bottom { caption-side: bottom; }
td, th { vertical-align:top; }
th.org-right { text-align: center; }
th.org-left { text-align: center; }
th.org-center { text-align: center; }
td.org-right { text-align: right; }
td.org-left { text-align: left; }
td.org-center { text-align: center; }
dt { font-weight: bold; }
.footpara { display: inline; }
.footdef { margin-bottom: 1em; }
.figure { padding: 1em; }
.figure p { text-align: center; }
.equation-container {
display: table;
text-align: center;
width: 100%;
}
.equation {
vertical-align: middle;
}
.equation-label {
display: table-cell;
text-align: right;
vertical-align: middle;
}
.inlinetask {
padding: 10px;
border: 2px solid gray;
margin: 10px;
background: #ffffcc;
}
#org-div-home-and-up
{ text-align: right; font-size: 70%; white-space: nowrap; }
textarea { overflow-x: auto; }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00; }
.org-info-js_info-navigation { border-style: none; }
#org-info-js_console-label
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
.org-info-js_search-highlight
{ background-color: #ffff00; color: #000000; font-weight: bold; }
.org-svg { }
</style>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
displayAlign: "center",
displayIndent: "0em",
"HTML-CSS": { scale: 100,
linebreaks: { automatic: "false" },
webFont: "TeX"
},
SVG: {scale: 100,
linebreaks: { automatic: "false" },
font: "TeX"},
NativeMML: {scale: 100},
TeX: { equationNumbers: {autoNumber: "AMS"},
MultLineWidth: "85%",
TagSide: "right",
TagIndent: ".8em"
}
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_HTML"></script>
</head>
<body>
<div id="content" class="content">
<h1 class="title">iGEM 2022 冬季学期培训内容</h1>
<div id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#org84001d9">1. 意义和作用 </a></li>
<li><a href="#org0ab7b4e">2. 简单的基础知识 </a>
<ul>
<li><a href="#orge0ad72e">2.1. Terminal Usage </a>
<ul>
<li><a href="#org3148810">2.1.1. 基本的知识</a></li>
<li><a href="#orge36689f">2.1.2. 基本的文件操作</a></li>
</ul>
</li>
<li><a href="#org8265918">2.2. Git Version Control </a></li>
<li><a href="#orgea46661">2.3. Editor </a>
<ul>
<li><a href="#org3a9412a">2.3.1. (Neo)Vim</a></li>
<li><a href="#org82482d9">2.3.2. Emacs</a></li>
</ul>
</li>
<li><a href="#org84ae5f5">2.4. Read Code </a></li>
<li><a href="#orgccd0f33">2.5. Write Code </a>
<ul>
<li><a href="#org58ca803">2.5.1. C </a></li>
<li><a href="#orge9ca600">2.5.2. Python </a></li>
<li><a href="#orgffe674f">2.5.3. Ruby </a></li>
<li><a href="#org3d0bad0">2.5.4. Lisp </a></li>
</ul>
</li>
<li><a href="#orgbe6497a">2.6. Write Document </a>
<ul>
<li><a href="#org8128174">2.6.1. 标记性语言</a></li>
<li><a href="#org7d76aa2">2.6.2. 文档需要有什么</a></li>
<li><a href="#org253db30">2.6.3. 重新思考标记性语言 </a></li>
</ul>
</li>
<li><a href="#orgb373a38">2.7. Others </a></li>
</ul>
</li>
<li><a href="#org9db23f9">3. 单片机基础知识 </a>
<ul>
<li><a href="#org15d0f26">3.1. 教学方式</a></li>
<li><a href="#org8c91c86">3.2. 简单的知识 </a>
<ul>
<li><a href="#org8b5a6d7">3.2.1. 零件表</a></li>
<li><a href="#org9e509c7">3.2.2. 原理图</a></li>
<li><a href="#org9145c20">3.2.3. 焊接方法</a></li>
<li><a href="#orgf87036b">3.2.4. 电路基本知识</a></li>
<li><a href="#orgb03f148">3.2.5. 计算机的运行 </a></li>
</ul>
</li>
<li><a href="#orgc4e7346">3.3. 第一个任务: 焊接 51 单片机</a>
<ul>
<li><a href="#orgc31e142">3.3.1. Step 0: 做好准备并了解大概的内容</a></li>
<li><a href="#org6c3c0f7">3.3.2. Step 1: 了解焊接顺序并焊接</a></li>
</ul>
</li>
<li><a href="#org50e6d73">3.4. 第二个任务: 从闪烁到声音</a></li>
<li><a href="#orgc88c384">3.5. Others</a></li>
</ul>
</li>
<li><a href="#org7d44b61">4. CAD 基础知识 </a>
<ul>
<li><a href="#org1084d85">4.1. Autodesk</a>
<ul>
<li><a href="#org7d76700">4.1.1. AutoCAD</a></li>
<li><a href="#orgc7f32b0">4.1.2. Fusion 360</a></li>
</ul>
</li>
<li><a href="#orge3806fd">4.2. Solidworks</a></li>
<li><a href="#org683127d">4.3. KiCAD </a></li>
</ul>
</li>
<li><a href="#org0f24c33">5. 比较高级一点的部分 </a>
<ul>
<li><a href="#org65bb713">5.1. 两种编程的方式, 以 "狗屁不同废话生成器" 为例 </a>
<ul>
<li><a href="#org4d7e705">5.1.1. 代码是过程的规则</a></li>
<li><a href="#org4261731">5.1.2. 代码是规则的过程</a></li>
</ul>
</li>
<li><a href="#orgb123b7a">5.2. 一个执行简单指令的虚拟机器 </a></li>
</ul>
</li>
<li><a href="#org9d238a5">6. 作业等其他 </a>
<ul>
<li><a href="#orgb14ce6f">6.1. 作业提交</a></li>
<li><a href="#org6e4d2af">6.2. 关于 51 单片机的资料</a></li>
<li><a href="#orgdcb8b12">6.3. 关于疫情之下的课程调整</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-org84001d9" class="outline-2">
<h2 id="org84001d9"><span class="section-number-2">1.</span> 意义和作用 <a id="org661a4bd"></a></h2>
<div class="outline-text-2" id="text-1">
<p>
虽然毫无结果便开始讨论意义, 看起来并不是很高明.
但是在一段路程将尽, 仍不知自己为何走这段路.
不仅感觉十分的空虚, 就是老板来问你的时候,
也是十分难以回答而内心满是焦急.
</p>
<p>
所以在每一步的时候, 都需要知道自己在干什么,
以及为什么走这一步.
</p>
<p>
最后, 哪怕你真的什么意义也不想管, 那么, 至少,
在老板问你的时候, 希望能会讲.
</p>
</div>
</div>
<div id="outline-container-org0ab7b4e" class="outline-2">
<h2 id="org0ab7b4e"><span class="section-number-2">2.</span> 简单的基础知识 <a id="org837e3cf"></a></h2>
<div class="outline-text-2" id="text-2">
<div id="org402d1cd" class="figure">
<p><img src="./pic/basic.svg" alt="basic.svg" class="org-svg" width="61.8%" />
</p>
<p><span class="figure-number">Figure 1: </span>Basic Concepts</p>
</div>
<p>
这些基础知识虽然很重要, 但是一般不会特别花时间去学.
如果你想要理论地去学的话, 一般会花很多的时间.
</p>
<p>
虽然不是很建议, 但是如果真的像严肃一点地去学的话,
建议参考 <a href="https://missing.csail.mit.edu">The Missing Semester of Your CS Education</a>.
</p>
<p>
(虽然我没有看完过, 平时都是随用随查的. )
</p>
</div>
<div id="outline-container-orge0ad72e" class="outline-3">
<h3 id="orge0ad72e"><span class="section-number-3">2.1.</span> Terminal Usage <a id="org59becf2"></a></h3>
<div class="outline-text-3" id="text-2-1">
<blockquote id="org98be188">
<p>
The Terminal is a tool, not an application,
meaning that it can be regarded as a lifestyle.
</p>
<p>
from <a href="https://dev.to/nrobinson2000/why-you-need-terminal-bpd">Why you need Terminal</a>
</p>
</blockquote>
<p>
(注: Windows 下, 建议使用 <a href="https://learn.microsoft.com/en-us/windows/wsl/install">WSL</a>, 在我看来是 Windows
除了游戏, 某些工业软件之外的唯一真香的东西了. )
</p>
<p>
(注: 尽管我就在使用 macOS, 但是我的建议是,
能整一个正经的 linux 环境还是整一个比较好.
省得出现一些 macOS 独占的 bug. )
</p>
</div>
<div id="outline-container-org3148810" class="outline-4">
<h4 id="org3148810"><span class="section-number-4">2.1.1.</span> 基本的知识</h4>
<div class="outline-text-4" id="text-2-1-1">
<ul class="org-ul">
<li><p>
<b>stdin</b> 和 <b>stdout</b> 以及 <b>stderr</b>
程序通过 stdout 来读取标准输入, stdin 来输出标准输出,
stderr 则是一个标准的错误输出. 这些叫做 stream.
</p>
<p>
可以想象成不同名称和用途的管道, 其中不同的管道里面,
会根据不同的需求输出不同的信息.
</p>
<p>
当然, 可以使用管道符 <code>|</code> 等方式来重定向输入和输出.
</p></li>
<li><p>
<b>ARGV</b>, <b>ARGF</b>
command name, options, flag
</p>
<p>
这里提供一些例子来帮助认识:
</p>
<div class="org-src-container">
<pre class="src src-bash">echo "I am Lucky"
</pre>
</div>
<p>
上面就可以看到, <code>echo</code> 命令传入了一个字符串作为参数.
</p>
<div class="org-src-container">
<pre class="src src-bash">ls -l
</pre>
</div>
<p>
这里 <code>ls</code> 命令传入了一个 flag <code>-l</code> 来作为选项.
</p>
<p>
当然, 还有更加复杂的. 请看文档.
</p></li>
<li><b><b>man</b></b>
使用 <code>man command</code> 就可以用来查看 <code>command</code> 的帮助文档.
并且实际上帮助信息非常的详细.</li>
<li>相对路径, 绝对路径, 文件通配符</li>
</ul>
</div>
</div>
<div id="outline-container-orge36689f" class="outline-4">
<h4 id="orge36689f"><span class="section-number-4">2.1.2.</span> 基本的文件操作</h4>
<div class="outline-text-4" id="text-2-1-2">
<p>
这里是非常随便地列出了一些我可能记得的命令,
你可以到网上去查找 Linux Command Cheat Sheet 之类的,
比如: <a href="https://www.loggly.com/wp-content/uploads/2015/05/Linux-Cheat-Sheet-Sponsored-By-Loggly.pdf">Loggly Linux Cheat Sheet</a>, 或者 <a href="https://www.guru99.com/linux-commands-cheat-sheet.html">guru99</a> 等.
</p>
<ul class="org-ul">
<li>查看当前文件夹下有什么东西: <code>ls</code></li>
<li>打开文件夹: <code>cd path-name</code></li>
<li>删除文件: <code>rm file-name</code></li>
<li>移动文件: <code>mv from to</code></li>
<li>复制文件: <code>cp from to</code></li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org8265918" class="outline-3">
<h3 id="org8265918"><span class="section-number-3">2.2.</span> Git Version Control <a id="orgf64e241"></a></h3>
<div class="outline-text-3" id="text-2-2">
<blockquote>
<p>
Your life is much easier if this is baked into your workflow,
as opposed to being a separate process that you dread or neglect.
</p>
<p>
form <a href="https://happygitwithr.com/big-picture.html">Happy Git and GitHub for the useR</a>
</p>
</blockquote>
<ul class="org-ul">
<li>初始化仓库: <code>git init</code></li>
<li>从远程克隆仓库: <code>git clone git@gitee.com:lucky-magic/2023-i-gem-hardware-new-member-tutorial.git</code></li>
<li>从远程拉取更新仓库: <code>git pull</code></li>
<li>查看当前的状态: <code>git status</code></li>
<li>添加内容: <code>git add file</code>
撤销添加: <code>git reset</code></li>
<li>添加注释: <code>git commit -m "你的注释内容"</code></li>
<li>推送本地到远程: <code>git push</code></li>
<li>开辟新的分支: <code>git branch 分支名称</code></li>
<li>切换到分支: <code>git switch -c 分支名称</code></li>
<li>合并分支: <code>git merge 分支名称</code></li>
</ul>
<p>
虽然上面的一堆命令看起来非常的可怕.
但是实际上用到的没有那么多, 就只有少数几个.
</p>
<p>
你可以在 <a href="https://training.github.com/downloads/zh_CN/github-git-cheat-sheet/">Git Cheat Sheet</a> 中找到主要的指令的参考,
或者是在 <a href="https://learngitbranching.js.org">Learn Git Branching</a> 里面学习一下主要的 Git 操作,
(类似的游戏还有 <a href="https://ohmygit.org">Oh my Git</a> )
或者是使用图形化的 <a href="https://desktop.github.com">Github Desktop</a> 来减少对指令的记忆.
</p>
<p>
最后, 请善用 Git 的版本管理来方便自己的项目开发吧.
</p>
<p>
(虽然我也不能说自己很会了, 多学学吧. )
</p>
<p>
一个可以参考的项目: <a href="https://gitee.com/ucas-sem-work-2/simple-operation-git">Simple Operation GitDesktop From czx</a>, 以及 <a href="https://gitee.com/gitee-community/opensource-guide/tree/master">开源指北</a>.
</p>
</div>
</div>
<div id="outline-container-orgea46661" class="outline-3">
<h3 id="orgea46661"><span class="section-number-3">2.3.</span> Editor <a id="org104af69"></a></h3>
<div class="outline-text-3" id="text-2-3">
<p>
文本编辑器的作用虽然不是很大, 也不必有什么宗教,
什么三六九等的区分. 如果你啥也不会,
建议无脑入 VSCode.
</p>
<p>
这里推荐两个除了 VSCode 外比较好用的编辑器:
</p>
<ul class="org-ul">
<li>(<a href="https://neovim.io">Neo</a>)<a href="https://www.vim.org">Vim</a></li>
<li><a href="https://www.gnu.org/software/emacs/">Emacs</a></li>
</ul>
</div>
<div id="outline-container-org3a9412a" class="outline-4">
<h4 id="org3a9412a"><span class="section-number-4">2.3.1.</span> (Neo)Vim</h4>
<div class="outline-text-4" id="text-2-3-1">
<blockquote id="orgc8704ae">
<p>
Any Linux machine has it. Vim has a small footprint,
low latency, fast startup, allows for more screen space,
customizable and most importantly, once the muscle-memory
has been ingrained, it's nearly impossible to switch to
something else.
</p>
<p>
form <a href="https://stackoverflow.blog/2020/11/09/modern-ide-vs-vim-emacs/">Modern IDEs are magic.
Why are so many coders still using Vim and Emacs?</a>
</p>
</blockquote>
<p>
Vim 在处理简单的代码, 以及处理一些简单的文件编辑和预览的时候,
非常的方便快捷. 而 Neovim 则是 Vim 基础上的一个更加现代化的一个分支.
</p>
<p>
VIM 系中, 最重要的就是一个 Edit Mode 的概念. 在 Normal 模式下,
通过 <code>hjkl</code> 来进行光标的左, 下, 上, 右的移动. 在任何的模式下,
通过按 <code>ESC</code> 键就能够回到 Normal 模式. 在 Normal 模式下,
按下 <code>i</code> 进入 Insert 模式, 于是就能够进行编辑文本了.
</p>
<p>
最后, 在 Normal 模式下, 输入 <code>:w</code> 来保存文件, 输入 <code>:q</code> 来退出.
</p>
<p>
在命令行中:
</p>
<div class="org-src-container">
<pre class="src src-bash">nvim file-name # neovim
vim file-name # vim
</pre>
</div>
<p>
打开叫做 <code>file-name</code> 的文件.
</p>
<p>
可以参考的学习资料:
</p>
<ul class="org-ul">
<li><a href="https://vim-adventures.com">Vim Adventures</a> 一个学习 Vim 的游戏, 免费部分就够了</li>
<li><a href="https://vim.rtorr.com/lang/zh_cn">Vim Cheat Sheet</a> 虽然也很少用就是了</li>
<li><a href="https://github.com/wsdjeg/vim-galore-zh_cn">Vim 从入门到精通</a></li>
<li>Vim 自带的教程: 通过命令行 <code>vimtutor</code> 来玩.</li>
</ul>
</div>
</div>
<div id="outline-container-org82482d9" class="outline-4">
<h4 id="org82482d9"><span class="section-number-4">2.3.2.</span> Emacs</h4>
<div class="outline-text-4" id="text-2-3-2">
<blockquote id="orgf0fb6ef">
<p>
While any text editor can save your files,
only Emacs can save your soul
</p>
<p>
form Emacs
</p>
</blockquote>
<p>
这么说吧, Emacs 就是一个操作系统. 或者说,
你大可以将其用成一个操作系统. 因为 Emacs 可以让你自由地定义.
</p>
<p>
(不过没准, 可能会因为自由度太高而不知所措了. )
</p>
<p>
可以参考的资料:
</p>
<ul class="org-ul">
<li><a href="https://emacs-china.org">Emacs-China</a> 中文论坛, 其中有一个很好的教程.</li>
<li><a href="https://github.com/li-yiyang/.emacs.d">li-yiyang/.emacs.d</a> 我的 Emacs 配置.</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org84ae5f5" class="outline-3">
<h3 id="org84ae5f5"><span class="section-number-3">2.4.</span> Read Code <a id="org4587b41"></a></h3>
<div class="outline-text-3" id="text-2-4">
<p>
一般来说, 硬件组的工作的代码量和代码强度肯定是不太高的.
甚至你是高级 Ctrl-C/V 程序员估计都没有什么太大的问题.
</p>
<p>
但是至少, 至少我们需要会读懂代码里面写的是什么吧?
</p>
<p>
一般来说, 我们可以从两个方面来了解一个语言和代码的知识.
</p>
<ul class="org-ul">
<li><b>语法形式</b>
虽然我们不必像编译原理那样来搞清楚语法的形式 (比如 <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form">ebnf</a>,
可以参考一下下面的 <a href="#orgd4fa617">重新思考标记性语言</a> ),
只需要有一个大概的了解. 比如了解 <code>f(x)</code> 应该是一个函数调用,
<code>[1, 2, 3]</code> 或者 <code>{1, 2, 3}</code> 是一个数组的形式等等就可以了.</li>
<li><b>逻辑过程</b>
实际上, 大部分的编程语言, 他们之间的差别可能就是语言带的环境, 库,
语言的小小的语法之类的差别. 本质上编程的逻辑部分还是相通的.</li>
<li><b>语言特性</b>
不同的语言, 虽然编程的逻辑是差不多的,
但是如果你知道这些语言里面提供了哪些小操作, 或者这些语言有什么特性,
那么就可以让编程变快, 以及让程序更快.</li>
</ul>
<p>
比如下面的这个例子:
</p>
<div class="org-src-container">
<pre class="src src-asm" id="org602a8b5"> .section __TEXT,__text,regular,pure_instructions
.build_version macos, 13, 0 sdk_version 13, 0
.globl _main ; -- Begin function main
.p2align 2
_main: ; @main
.cfi_startproc
; %bb.0:
sub sp, sp, #16
.cfi_def_cfa_offset 16
str wzr, [sp, #12]
str wzr, [sp, #8]
mov w8, #10
str w8, [sp, #4]
b LBB0_1
LBB0_1: ; =>This Inner Loop Header: Depth=1
ldr w8, [sp, #4]
subs w8, w8, #0
b.le LBB0_3
b LBB0_2
LBB0_2: ; in Loop: Header=BB0_1 Depth=1
ldr w8, [sp, #8]
mov w9, #10
mul w8, w8, w9
str w8, [sp, #8]
ldr w8, [sp, #4]
mov w9, #2
sdiv w10, w8, w9
mul w10, w10, w9
subs w10, w8, w10
ldr w8, [sp, #8]
add w8, w8, w10
str w8, [sp, #8]
ldr w8, [sp, #4]
sdiv w8, w8, w9
str w8, [sp, #4]
b LBB0_1
LBB0_3:
ldr w0, [sp, #8]
add sp, sp, #16
ret
.cfi_endproc
; -- End function
.subsections_via_symbols
</pre>
</div>
<p>
那么其逻辑是什么呢? 下面的是源代码:
</p>
<div class="org-src-container">
<pre class="src src-c">int main() {
int x, y;
x = 0;
y = 10;
while (y > 0) {
x *= 10;
x += y % 2;
y /= 2;
}
return x;
}
</pre>
</div>
<p>
(Note: 实际上, 如果你开了优化来编译的话,
你可能啥也得不到. 因为编译器会自动帮你计算完常数来提高效率.)
</p>
<div class="org-src-container">
<pre class="src src-bash">gcc -S -fverbose-asm ./classes/class-01/asm-expample.c -o ./classes/class-01/asm-expample.s
</pre>
</div>
<p>
实际上, 这段代码里面包含了赋值, 运算, 判断, 循环,
某种意义上来说, 已经就是你要学的所有的编程了. (bushi)
</p>
<p>
当然, 我们不需要学汇编, 但是如果我们未来想要做 生物编译器 的话,
可能需要往那个方向去了解一下.
</p>
<p>
并且大部分时候, 写代码的时候依赖的更多的是文档,
以及其中对 API 的一个介绍.
</p>
</div>
</div>
<div id="outline-container-orgccd0f33" class="outline-3">
<h3 id="orgccd0f33"><span class="section-number-3">2.5.</span> Write Code <a id="org90fd138"></a></h3>
<div class="outline-text-3" id="text-2-5">
<p>
如何写程序: 这里参考的是 <a href="https://github.com/norvig/paip-lisp">PAIP</a> 中介绍的 <a href="https://en.wikipedia.org/wiki/General_Problem_Solver">GPS</a> 的方法:
</p>
<ol class="org-ol">
<li>Describe</li>
<li>Specify</li>
<li>Implement</li>
<li>Test</li>
<li>Debug And Analyze</li>
</ol>
<p>
以及如何处理大规模的项目 (这个我还在学,
有兴趣的同学可以参考: Code Complete. )
</p>
<p>
而对于具体的编程语言, 在这里仅会形式地介绍
C, Python, Ruby, 以及 Lisp.
</p>
<ul class="org-ul">
<li>其中 C 语言将会和底层的汇编相对应, 是我们主要编程所需要的工具.</li>
<li>Python 和 Ruby 将会作为一个较为方便容易写程序的高级语言,
其中将会通过 Ruby 来介绍 OOP 的编程思想.</li>
<li>Lisp 则属于是一种对函数式编程思想的简单介绍.</li>
</ul>
<p>
(注: 因为硬件组的重点不在软件编程, 所以这部分会非常简要地介绍和略过.
如果需要了解更加详细的内容, 请参考 <a href="https://gitee.com/igem_ucas_china/2023-i-gem-software-new-members-tutorial">iGEM Software Tutorial</a>)
</p>
</div>
<div id="outline-container-org58ca803" class="outline-4">
<h4 id="org58ca803"><span class="section-number-4">2.5.1.</span> C <a id="org195e3f3"></a></h4>
<div class="outline-text-4" id="text-2-5-1">
<p>
该部分推荐的参考书为 C Programming Language by K&R,
以及 C: A Referrence Manual. 不过我对 C 语言也不是很熟练,
所以如果文档中有任何问题, 请在 issue 中提出.
</p>
<p>
在该部分的 C 语言中, 我们主要使用的是 C 和汇编的对应来进行简单的教学.
不过请注意, 这样的教学仅仅只是最基础的简单教学.
</p>
<p>
(注: 其中汇编的代码为 <code>gcc -S -fverbose code.c</code> 编译得到,
编译的环境为 Ubuntu 21.10 (GNU/Linux 5.13.0-48-generic x86<sub>64</sub>),
如果你不想要复杂的输出的话, 可以选择去掉 <code>-fverbose</code> flag,
通过 <code>gcc -S code.c</code> 来编译. )
</p>
</div>
<ol class="org-ol">
<li><a id="orgc19c05a"></a>变量, 表达式, 值, 数组, 结构<br />
<ol class="org-ol">
<li><a id="org623857f"></a>变量 <a id="org7ccfbf5"></a><br />
<div class="outline-text-6" id="text-2-5-1-1-1">
<p>
我们可以用 <code><value-type-option> <variable-name> (= <variable-value>){0,1};</code>
的方式来为一个变量赋予名字. 这就像是在计算机中找了一个空间来储存变量名字
所对应的变量的值.
</p>
<p>
其中 <code><value-type-option></code> 为变量的值的类型,
我们将会在下面简单介绍, 目前你可以简单认为有如下的形式:
</p>
<div class="org-src-container">
<pre class="src src-text"><assignment> ::= <type-opt> (<equation>)+;
<type-opt> ::= "int" /* 整型 */
| "float" /* 浮点数 */
| "double" /* 双精度 */
| "char" /* 字符值 */
/* 不完整的规则 */
<equation> ::= <variable-name> (= <variable-value>){0,1}
</pre>
</div>
<p>
(思考题: 请在阅读后文的 <a href="#orgba0d4d8">值的类型</a> 一部分后写出更加规范的形式定义. )
</p>
<p>
比如:
</p>
<div class="org-src-container">
<pre class="src src-C">int x = 3;
</pre>
</div>
<p>
在汇编中, 我们可以看到这样的操作对应了下面的代码:
</p>
<div class="org-src-container">
<pre class="src src-asm"># main.c:3: int x = 3;
movl $3, -4(%rbp) #, x
</pre>
</div>
<p>
我们会发现, 变量 <code>x</code> 被放在了相对 <code>rbp</code> 位置为 <code>-4</code> 的栈上.
(关于变量和栈, 我们在 <a href="#org50041ad">计算机的运行</a> 中介绍了大概的基本知识.
如果你觉得这部分比较困难, 请认为 <code>rbp</code> 就是一个记录局部变量在哪里的标志,
有点像是局部坐标的坐标原点, 而 <code>-4</code> 则是一个变量的坐标. )
</p>
</div>
</li>
<li><a id="orge34605c"></a>表达式 <a id="orgc350530"></a><br />
<div class="outline-text-6" id="text-2-5-1-1-2">
<p>
我们可以通过像数学一样来写简单的表达式:
</p>
<p>
比如:
</p>
<div class="org-src-container">
<pre class="src src-C">int x = 3;
int y;
y = (x * x + 1) / 10; // y = 1
</pre>
</div>
<p>
一个简单的表达式的形式如下:
</p>
<div class="org-src-container">
<pre class="src src-text"><exp> ::= <binary-exp> | <single-exp>
<binary-exp> ::= (<exp> <binary-operator> <exp>) | <val>
<single-exp> ::= (<prefix-op> <val>) | (<val> <subfix-op>)
<binary-operator> ::= "+" | "-" | "*" | "/" /* 四则运算 */
| "&" | "^" | "|" | "<<" | ">>" /* 位运算 */
| "&&" | "||" | "<=" | ">=" | "==" | "!=" /* 逻辑运算 */
/* 其他, 留作课后作业 */
<prefix-op> ::= "++" | "--" /* 运算后取值 */
| "&" | "*" /* 取地址和取指向的值 */
/* 其他, 留作课后作业 */
<val> ::= /* 变量名, 字面值, 函数返回值 */
</pre>
</div>
<p>
我们自然可以像数学一样使用函数来作为返回值: <code>f(x)</code>.
具体的内容见后面的函数调用.
</p>
</div>
</li>
<li><a id="org9364c8f"></a>值 <a id="orgba0d4d8"></a><br />
<div class="outline-text-6" id="text-2-5-1-1-3">
<p>
在 C 语言中有不同的值的类型. (其实为什么会有不同的值的类型的原因非常简单,
工程上来说, 是因为不同的值在计算机中的储存和表示方式不同;
或者也可以数学一些来说, 就是其表示的域的不同, 对于考虑的问题的不同,
选择不同的精度和值的类型. )
</p>
<p>
(注: 这里是工程, 所以是会有误差的. 所以在编程语言中的值, 大多数是数值近似值,
而不是一个解析的准确值. )
</p>
<blockquote>
<p>
狄拉克: 工程学对我最大的启发就是工程学能够容忍误差.
</p>
<p>
from 赵亚溥 力学
</p>
</blockquote>
<p>
可以参考 <a href="https://en.wikipedia.org/wiki/C_data_types">维基百科</a> 中对 C Data Type 的说明, 我们可以发现,
在 C 语言中有四种主要的类型:
</p>
<ul class="org-ul">
<li>char 字符, 在形式上为单引号括起来的一个字符, 在计算机储存的时候,
实际上仍然是用数的形式储存, 只是通过表格映射到字符的形状.</li>
<li>int 整型, 在形式上为整数</li>
<li>float 浮点数, 在形式上为小数, 或者写成整数的形式也不是不行</li>
<li>double 双精度, 就顾名思义.</li>
</ul>
<p>
在这四种类型之上, 可以通过添加修饰符来定义值的更加细节的表现:
</p>
<ul class="org-ul">
<li>unsigned, signed, 是否有符号位, 实际上可以和计算机中的值的表示方式一起学习</li>
<li>long, long long, 在原本的基础上进行拓展</li>
</ul>
<p>
不过实际上, 基本目前我们可能就只会使用非常少的几种类型.
大家只需要了解如何写就好了. (意思就是说, 更加细节的部分可以自学,
或者可以在 issue 中提出等. )
</p>
</div>
</li>
<li><a id="org624811d"></a>数组<br />
<div class="outline-text-6" id="text-2-5-1-1-4">
<p>
在 <a href="#org7ccfbf5">变量</a> 一部分中, 我们提到了定义的变量会在栈上通过一个相对 <code>bp</code> 的位移来定位,
假如我们想要定义一系列有序的变量, 比如 <code>a1</code>, <code>a2</code>, <code>a3</code> 等等.
除了直接通过 <code>int a1, a2, a3, ...</code> 的方式来定义, (这样定义的变量,
应该是在栈上依次排布的, 于是一个非常简单的想法就是直接用相对位移来访问,
如: 令 <code>a1</code> 的地址为 <code>&a1</code>, 于是加上一个相对位移 <code>&a1 + delta</code>,
就可以得到 <code>&a2</code> 的地址… 于是以次类推. )
</p>
<p>
这样利用相对位移的方式来定义一系列的有序变量的方式, 就是一种简单的数组的形式.
</p>
<p>
那么在形式上, 数组的定义像这样 <code><type-opt> <array-name>[<array-size>];</code>,
或者 <code><type-opt> <array-name>={<array-values>};</code>:
</p>
<div class="org-src-container">
<pre class="src src-C">int a[5];
int b[] = {1, 2, 3, 4, 5};
</pre>
</div>
<p>
于是我们就能够通过 <code><array-name>[<index>]</code> 的方式像变量一样去访问对应的元素.
</p>
<p>
其中, <code><type-opt></code> 定义了数组中的元素的值的类型,
而 <code><array-size></code> 定义了数组的大小.
</p>
<p>
在 C 语言中, 字符串也就是一种数组:
</p>
<div class="org-src-container">
<pre class="src src-C">char s1[] = "I am Lucky";
char s2[] = {'L', 'u', 'c', 'k', 'y', '\0'};
</pre>
</div>
<p>
只是通过 <code>'\0'</code> 符号来标记字符串的结束.
</p>
<p>
那么说到数组, 就不得不提起指针. 目前我们仅仅从形式上来理解指针的定义和调用,
在 <a href="#org220036d">函数</a> 一节, 我会介绍一些我们为什么要使用指针以及指针该如何去理解的知识.
</p>
<p>
我们用符号 <code>*</code> 来表示这个是一个指向某某的指针, 用 <code>&</code> 来表示这个是某某的地址.
</p>
<p>
简单来说, 假如我们有这样的一个 Excel 表格:
</p>
<!-- This HTML table template is generated by emacs 28.2 -->
<table border="1">
<tr>
<td align="left" valign="top">
index
</td>
<td align="left" valign="top">
0
</td>
<td align="left" valign="top">
1
</td>
<td align="left" valign="top">
2
</td>
<td align="left" valign="top">
3
</td>
<td align="left" valign="top">
4
</td>
<td align="left" valign="top">
5
</td>
<td align="left" valign="top">
6
</td>
<td align="left" valign="top">
...
</td>
</tr>
<tr>
<td align="left" valign="top">
value
</td>
<td align="left" valign="top">
%
</td>
<td align="left" valign="top">
)
</td>
<td align="left" valign="top">
+
</td>
<td align="left" valign="top">
-
</td>
<td align="left" valign="top">
<
</td>
<td align="left" valign="top">
>
</td>
<td align="left" valign="top">
?
</td>
<td align="left" valign="top">
...
</td>
</tr>
</table>
<p>
我们用序号去访问值, 比如在序号为 6 的地方是一个 <code>?</code>,
那么我们便可以用这样的方式说, 对于 <code>?</code>,
我们不妨说它就是我们名叫 <code>question_mark</code> 的变量的地址,
于是 <code>&question_mark</code> 的值就应该是 <code>6</code>.
我们不妨将其用一个变量 <code>point_to_question_mark</code> 来表示,
即: <code>point_to_question_mark = &question_mark</code>.
</p>
<p>
同理, 对于上面的例子, 我们也能够说: <code>*point_to_question_mark</code>
就是再说, 我们要去 <code>index</code> 为 <code>point_to_question_mark</code> 的值的地方,
也就是 <code>index</code> 为 6 的地方去找到一个值, 也就是 <code>?</code>.
</p>
<p>
这就是取地址和读地址的简单的理解. 那么具体有什么用呢?
<del>让你头晕脑花</del>.
</p>
</div>
</li>
<li><a id="orgb62781f"></a>结构<br />
<div class="outline-text-6" id="text-2-5-1-1-5">
<p>
一个数组中的所有元素的类型都是一样的. 相当于在一个名字下,
按序组织了相同类型的元素.
</p>
<p>
那如果我们不太想要顺序性, 但是想要在一个名字底下, 存放不同类型的元素呢?
(这里就是放弃顺序性而获得了更多的能力了. )
</p>
<blockquote>
<p>
那么, 古尔丹, 代价是什么呢?
</p>
</blockquote>
<p>
在 C 语言中, 这样的方式就是结构体:
</p>
<div class="org-src-container">
<pre class="src src-C">struct namingspace {
char c;
int x;
int a[5];
};
struct namingspace example;
example.c = 'L';
example.x = 1;
example.a[0] = 2;
</pre>
</div>
<p>
在形式上, 通过 <code>struct <name> { <c-assignment>+ };</code> 的形式来定义一个
struct 类型. 使用 <code>struct <struct-name> <v-name></code> 来定义一个结构体变量.
</p>
</div>
</li>
<li><a id="org96d6f1f"></a>小练习<br />
<div class="outline-text-6" id="text-2-5-1-1-6">
<ol class="org-ol">
<li>尝试描述这些定义的形式语言</li>
<li>在看完后文中的程序结构后尝试编写一些程序来验证定义
<ul class="org-ul">
<li>定义一个变量 <code>pi</code>, 其值为 <code>3.14159265354</code>, 思考应该用什么类型</li>
<li>定义一个结构, 该结构可以用来定义一个:
<ul class="org-ul">
<li>二维平面上的一个点 \((x, y)\) 或者 \((\rho, \theta)\) 以及其标签 <code>label</code></li>
<li>某一个时间点 <code>t</code> 采集的温度 <code>temp</code>, 流量 <code>q</code>, 成品率 <code>present</code> 等的数据</li>
</ul></li>
<li>(较难) 定义一个 <a href="https://en.wikipedia.org/wiki/Linked_list">Linked List</a></li>
</ul></li>
<li>定义一个 <a href="https://www.geeksforgeeks.org/multidimensional-arrays-c-cpp/">二维数组</a>, 用来储存一个平面格点上的不同点位中的菌体的温度.
思考该如何定位? (画饼) 思考, 如何通过红外摄像头来捕捉?
请设计一个简单的系统来描述.</li>
</ol>
</div>
</li>
</ol>
</li>
<li><a id="orgc18c854"></a>控制流<br />
<div class="outline-text-5" id="text-2-5-1-2">
<p>
其实在 <a href="#org50041ad">计算机的运行</a> 中我们已经接触过了控制流了.
</p>
<p>
其实基本的思想很简单, 就是如何让本来应该是线性的程序进行分支和循环.
</p>
</div>
<ol class="org-ol">
<li><a id="orgbe4d47a"></a>分支<br />
<div class="outline-text-6" id="text-2-5-1-2-1">
<p>
在流程图中, 我们可以将分支画成下面这样:
</p>
<div id="org8e3d7d2" class="figure">
<p><img src="pic/programming-branching-fig.png" alt="programming-branching-fig.png" />
</p>
</div>
<p>
但是我们的代码肯定是一个线性的代码, 没有办法表现出像这样的丰富的网状结构.
但是一个非常简单的做法: 在程序中划出一些代码块, 如果条件成立,
就执行代码块, 否则就不执行代码块. 于是我们就有了 <code>if</code> 条件判断.
</p>
<div class="org-src-container">
<pre class="src src-text"><if_branching> ::= "if (" <condition> ")" <code_block>
<condition> ::= <exp>
<code_block> ::= <line_exp> | "{" <line_exp>* "}"
<line_exp> ::= <exp> ";"
<exp> ::= /* 应该不用写很多了吧? */
</pre>
</div>
<p>
(注: 代码块的形式上的定义: <code>{ <exp>; }</code>)
</p>
<p>
举个例子:
</p>
<div class="org-src-container">
<pre class="src src-C">int x = 1;
if (x > 0)
printf("x is greater than 0");
</pre>
</div>
<p>
当然, 除了单纯的 <code>if</code>, 还能够写更多的形式, 如双分支:
<code>if (condition) {} else {}</code>,
以及多条件分支: <code>if (condition) {} else if {} else {}</code>.
以及多条件分支选择: <code>switch (exp) { case val: break; }</code>.
(不过可以看作是一种语法糖. )
</p>
</div>
</li>
<li><a id="org5a60525"></a>循环<br />
<div class="outline-text-6" id="text-2-5-1-2-2">
<p>
在计算机的运行中, 我们介绍过了如何让程序循环.
同样的, 在 C 语言里面, 也有循环的结构:
</p>
<div class="org-src-container">
<pre class="src src-C">while (condition) {
}
</pre>
</div>
<p>
你可以将其理解为:
</p>
<div class="org-src-container">
<pre class="src src-asm">.while_loop
cmp condition
je .out
;;; do something
jmp .while_loop
.out
</pre>
</div>
<p>
一般来说, 我们的循环都会有一个朴实而无华的想法, 就是历遍.
比如让传感器编号从 0 到 9, 检测每个传感器上的温度:
</p>
<div class="org-src-container">
<pre class="src src-c">int sensor_index = 0;
while (sensor_index < 10) {
read_sensor_tempture_at_index(sensor_index);
sensor_index++;
}
</pre>
</div>
<p>
而这样的代码因为太过常见. <i>思考题</i>: 请给出代码的一个形式结构.
并解释这样的形式结构为何能够用后面的 <code>for</code> 来简化书写.
解答会在 <code>for</code> 的语法结构解释完后给出.
</p>
<p>
于是人们便创造出了一个语法叫 <code>for</code> 来简化书写.
</p>
<div class="org-src-container">
<pre class="src src-c">for (int sensor_index = 0; sensor_index < 10; sensor_index++) {
read_sensor_tempture_at_index(sensor_index);
}
</pre>
</div>
<p>
我们可以用一个图来描述上面的过程:
</p>
<div id="org8ad3eff" class="figure">
<p><img src="pic/programming-loop-fig.png" alt="programming-loop-fig.png" />
</p>
</div>
<p>
前面思考题的解答
</p>
<div class="org-src-container">
<pre class="src src-c"><initial_code>;
while (<final_condition>) {
// do something
<step_code>;
}
for (<initial_code>; <final_condition>; <step_code>) {
// do something
}
</pre>
</div>
</div>
</li>
<li><a id="orgaa0ff8d"></a>小练习<br />
<div class="outline-text-6" id="text-2-5-1-2-3">
<ol class="org-ol">
<li>写一个条件判断来进行对输入信号的筛选. 比如我们有这样的一个热敏电阻:
<ul class="org-ul">
<li>在 \(10 \sim 30^\circ C\) 的时候, 电压和温度的关系近似有线性关系:
\(u = u_{10} + \lambda_1 (t - t_{10})\);</li>
<li>在 \(30 \sim 50^\circ C\) 的时候, 电压和温度的关系近似有二次关系:
\(u = u_{20} + \lambda_2 (t - t_{20}) + \mu_2 (t - t_{20})^2\)</li>
</ul></li>
<li>写一个简单的循环模型, 读取一个热敏电阻传感器阵列上每个温度传感器的数值,
然后打印出来.</li>
<li>请写一个 PID 控制, 使用简单的循环, 不必写得很规范.
<ul class="org-ul">
<li><p>
关于 <a href="https://zh.wikipedia.org/wiki/PID%E6%8E%A7%E5%88%B6%E5%99%A8">PID</a>, 我们可以简单这样理解:
其包含比例 (Proportional), 积分 (Integral) 以及微分 (Derivative) 组成,
在数学上的公式如下:
</p>
<p>
\[u(t) = K_p e(t) + K_i \int_0^t e(\tau) \mathrm{d}\tau + K_d \frac{\mathrm{d}}{\mathrm{d}t}e(t)\]
</p>
<p>
当然, 光看数学公式没什么鸟用, 实际上其中的参数十分好理解.
</p></li>
<li><p>
\(K_p e(t)\) 项:
</p>
<p>
我们用一个简单的例子来理解: 烧菜, 咸了加水, 淡了加盐.
用 \(e(t)\) 来量化表示我们的菜的咸度, 若 \(e(t) > 0\), 那么就是咸了,
反之就是淡了. 我们要做的机器人通过一个控制信号 \(u(t)\) 来决定是加盐
或者加水的量:
</p>
<div class="org-src-container">
<pre class="src src-C">void p_control(int expect_flavor) {
int u;
for (int e = taste_soup - expect_flavor; ; e = taste_soup - expect_flavor) {
if (e > 0) {
add_salt(k_p * e);
} else {
add_water(k_p * e);
}
}
}
</pre>
</div>
<p>
但是一个简单的问题就是, 往往这样只会得到一个振荡的不衰减的信号.
因为它和我们的简谐振动方程冥冥中有那么一丝相像.
</p>
<p>
\[u(t) = K_p e(t) \Leftrightarrow \boldsymbol{F} = - k \Delta\boldsymbol{x}\]
</p></li>
<li><p>
\(K_d\) 项:
</p>
<p>
前文刚说了, PID 方程中的 P 项很像我们的简谐振动方程,
并且有一个信号变化幅度很难衰减, 也就是有会一直摆动的缺点.
那么一个自然的一个想法就是像阻尼振动的问题一样, 为其添加一个阻尼项.
</p>
<p>
于是, 我们就得到了 \(K_d \frac{\mathrm{d}}{\mathrm{d}t}e(t)\) 项.
</p>
<p>
或者, 我们可以用一个更加玄学的感觉来说,
\(K_d\) 项就像是一种扼制未来发展趋势的项:
假如信号有增加的趋势, \(e'(t) > 0\),
那么我们就要使用控制信号来将其减少.
</p>
<p>
举一个例子, 在温度控制中, 如果我们开始加热, 看到 \(e(t) = T_{expect} - T_{now}\)
逐渐减少, 但是 \(e'(t)\) 却仍然很大, 就像是开车到了红灯的十字路口,
在衡量了一下相差的距离 \(e(t)\) 和当前的速度 \(e'(t)\),
也许很自然的结论就是, 我们应该要降低一下车速, 也就是 \(K_d e'(t)\) 的作用.
</p></li>
<li><p>
\(K_i\) 项:
</p>
<p>
既然我们已经知道了 P 和 D 的作用, 那么 I 又是什么呢?
</p>
<p>
简单, 假设我们现在有一个电压输入转输出信号线性转换器
(我也不知道这个是什么, 我们可以先假装有那么一个东西),
但是由于我们用的是一个奇怪的神奇二极管,
导致我们输出的电压始终和我们预期的电压有一个 \(0.3V\) 的压降, 这个时候,
我们希望能够调节输入 \(u\) 来使得输入和期望的差 \(e\) 能够减小到零.
</p>
<p>
但是很遗憾, 这个时候, 只靠 P 和 D 难以将这个偏差修改,
因为这就像是斜面上的阻尼振动, 我们的偏差值在平均中被 P 项忽略了,
而 D 项因为是变化项, 压根就不会理会常量偏差.
</p>
<p>
所以, 通过加入对信号的时间积分, 我们可以去修正这个常量的偏差.
直观上来看, 除非 \(\bar{e}\) 为零, 否则 I 项就会参与到控制信号中.
</p></li>
<li>那么, 请思考该如何用一个循环来简单地实现类似的算法呢?
假设我们已经知道了其中所有的参数.</li>
<li>注: 对于上面的方程, 更好的处理方式是对其使用傅里叶变换,
或者 Laplace 变换, 然后就能够比较方便地计算.</li>
</ul></li>
</ol>
</div>
</li>
</ol>
</li>
<li><a id="org50fec50"></a>函数, IO, 系统调用<br />
<ol class="org-ol">
<li><a id="org947331c"></a>函数 <a id="org220036d"></a><br />
<div class="outline-text-6" id="text-2-5-1-3-1">
<p>
和数学的函数不一样的地方在于, C 语言中的函数更像是一种子过程
(sub-progress).
</p>
<p>
(注: 如果有兴趣的话, 可以参考一下 <a href="#orge3d038c">Lisp</a> 中的一些思想. )
</p>
<p>
我们先从形式上来看函数的定义以及函数的调用:
</p>
<p>
函数的定义:
</p>
<div class="org-src-container">
<pre class="src src-text"><define_function> ::= <return_type> " " <function_name> "(" <args> ")" <function_block>
<return_type> ::= <value_type>
<function_name> ::= <vaild_name>
<function_block> ::= "{" "}" /* code */
<args> ::= <value_type> " " <vaild_name>
<vaild_name> ::= ([a-z] | [A-Z]) ([a-z] | [A-Z] | [0-9] | "_" )*
<value_type> ::= "char" | "int" | "float" | "double"
</pre>
</div>
<p>
函数的调用:
</p>
<div class="org-src-container">
<pre class="src src-text"><function_call> ::= <function_name> "(" <function_args_value> ")"
</pre>
</div>
<p>
那么拿一个例子来看即可:
</p>
<div class="org-src-container">
<pre class="src src-c">int puls_one(int x) {
return x + 1;
}
</pre>
</div>
<p>
这里我们就定义了一个加一函数.
</p>
<p>
函数的执行过程:
</p>
<ul class="org-ul">
<li>我们可以将函数看作是一个子过程 (subprogress),
之所以这么叫, 是因为和所谓的 "主" 过程 (main progress)相比,
子过程十分类似于主过程</li>
<li><p>
一个过程是 "局域" 的, 或者说, 这个过程是有极限的 (bushi).
这意味着这样的一个过程只能够掌握它周围一小部分区域的信息,
而如果想要了解更多, <del>得加钱</del>, 就会跳出这个过程.
</p>
<p>
于是我们就会面对许多的概念: 局部变量以及相对应的全局变量,
栈, 堆, 进程等等的概念.
</p>
<ul class="org-ul">
<li><p>
<b>栈</b>
</p>
<p>
在 <a href="#org50041ad">之前</a>, 我们已经了解过栈是如何存放变量的:
我们通过用相对位置的方式来记录相对不同的变量.
就像是我们用相对原点的坐标来记录不同的点一样.
</p>
<p>
那么既然是相对位置, 我们还能够定义相对位置的相对位置:
这就像是物理里面的相对参考系. 于是应该不难这样构造:
我们认为, 子过程可以是相对主过程有一个位置,
然后对于过程来说, 自己所知道的变量都是相对自己为原点的.
</p></li>
<li><p>
于是我们就知道了局部变量, 也就是相对自己的变量,
这些变量是自己所能够掌握的.
这就像是每个家庭里面都有的 "dad", "mom" 变量,
但是这些变量对于不同的家庭并不一定具有相同的值.
</p>
<p>
(注: 这个例子, 在之后 Ruby 的 OOP (面向对象编程) 里面,
我们应该会再一次提到, 不过需要注意区分这个例子使用的目的的不同. )
</p>
<p>
而全局变量更像是一种经过约定之后, 大家都知道它在哪里的变量.
</p></li>
</ul></li>
<li><p>
<b>参数传递</b>
</p>
<p>
那么既然我们已经知道了, 一个过程是 "局域" 的这个事实,
那么一个简单的想法就是, 一个局域的过程是如何看到外界告诉它的信息呢?
</p>
<p>
这方法就是参数传递. 囿于篇幅有限, 以及能力有限,
这里我们只是给出一个可能的传递参数的方式: 通过栈来传递.
至于其他的传递参数的方式, 可以作为有兴趣的同学的拓展了解内容.
</p>
<p>
譬如我们一开始的局部坐标都是 \(O+index = var\), 但是为何不换一种思路,
让我们用负坐标来表示传入的参数, 如 \(O-index = arg\),
于是我们就实现了简单的参数传递.
</p>
<p>
一个简单的例子:
</p>
<!-- This HTML table template is generated by emacs 28.2 -->
<table border="1">
<tr>
<td align="left" valign="top">
arg 2
</td>
<td align="left" valign="top">
arg 1
</td>
<td align="left" valign="top">
bp
</td>
<td align="left" valign="top">
var1
</td>
<td align="left" valign="top">
var2
</td>
<td align="left" valign="top">
sp
</td>
</tr>
<tr>
<td align="left" valign="top">
-2
</td>
<td align="left" valign="top">
-1
</td>
<td align="left" valign="top">
0
</td>
<td align="left" valign="top">
1
</td>
<td align="left" valign="top">
2
</td>
<td align="left" valign="top">
3
</td>
</tr>
</table>
<p>
大概是这个样子. 当然, 实际绝对不是这样的就是了.
</p>
<p>
那么很明显, 你就会发现, 如果我们写了一个递归函数:
</p>
<div class="org-src-container">
<pre class="src src-C">int sub_to_zero(int x) {
if (x > 0) {
return sub_to_zero(x - 1);
} else {
return x;
}
}
</pre>
</div>
<p>
那么当我们传入一个很大的参数 <code>x</code> 的时候,
我们的栈中就会堆积一堆的看起来很没有用的值:
<code>x - 1</code>, <code>x - 2</code>, … 这样是很愚蠢的.
</p>
<ul class="org-ul">
<li><p>
TOC 不过现代的编译器会使用一种叫做尾递归的方式,
将这种显然可以用循环来简化的函数用循环的方式来编译,
大大提高了程序的效率.
</p>
<p>
这种方法我们可能会在之后介绍.
目前我们可以暂时默认现代编译器已经为我们想了很多了.
</p></li>
<li><p>
指针, 就像是全局变量一样, 我们可以使用指针来告诉被递归调用的子函数,
你并不需要得到传入参数的完整的拷贝, 你只需要知道在哪里找到这个值就好了.
</p>
<p>
(就像是现代社会下, 不少老师会拿这个开刷学生: 你们不能说知道知识在哪里可以查到,
要背下来记在脑子里. 不过我持反对意见, 毕竟我连在哪里我都说不上来. )
</p>
<p>
这样的指针尤其是在传递数组, 结构的时候尤为方便,
因为于其将一整个数组完全拷贝到栈上作为参数传递,
肯定是直接传递指针来得更加方便.
</p>
<p>
并且传递指针, 而不是拷贝的值备份, 还能够满足我们想要修改栈上的数据的愿望.
</p></li>
</ul></li>
<li><p>
去哪里执行代码啊?
</p>
<p>
我们已经了解了这么多, 但是还有一个比较麻烦的小问题,
既然我们的程序是读取一个指针所指向的命令, 那么在跳转到一个函数的子过程的时候,
我们该怎么跳转呢? 总不能每次都将同一个函数放在下一条命令吧? 那样也太蠢了.
</p>
<p>
实际上答案就在上面的那段话里面了.
</p>
<p>
提示: 跳转.
</p></li>
<li><p>
那么知道这些有什么用呢?
</p>
<p>
我们可以根据这样简单的东西来写一个非常简单的小程序,
来模拟单核处理器是如何实现多进程的. 见小练习:
</p></li>
</ul>
</div>
</li>
<li><a id="org2e51f84"></a>小练习 - 虚假的多进程<br />
<div class="outline-text-6" id="text-2-5-1-3-2">
<ol class="org-ol">
<li><p>
假设我们已经有了一个神奇的函数 <code>compile</code>,
能够将我们输入的代码变成一个神奇的指针 <code>p</code>,
指向我们代码的单步指令:
</p>
<div class="org-src-container">
<pre class="src src-C">command *compile(char *code) {
// break the code into simple step code
return pointer_to_simple_code;
}
</pre>
</div>
<p>
以及一个简单的指令 <code>eval</code>, 能够执行 <code>p</code> 指向位置的简单命令:
</p>
<div class="org-src-container">
<pre class="src src-C">void eval(command *p) {
execute_command_at(p);
}
</pre>
</div>
<p>
如果你有兴趣的话, 可以先实现 <code>eval</code> 函数,
假设我们的 <code>simple_code</code> 的格式就是 asm code:
</p>
<div class="org-src-container">
<pre class="src src-text"><asm_code> ::= <cmd> <args>
</pre>
</div></li>
<li><p>
我们打算有一个栈结构: (通过链表来实现)
</p>
<div class="org-src-container">
<pre class="src src-C">struct stack {
int val;
struct stack *previous;
};
</pre>
</div>
<p>
请在阅读 <a href="https://www.geeksforgeeks.org/what-is-linked-list/">链接</a> 后思考该如何实现一个 <code>push</code> 和一个 <code>pop</code> 的操作:
</p>
<div class="org-src-container">
<pre class="src src-C">int push(int val, struct stack *s) {
struct stack *next = (struct stack *)malloc(sizeof(struct stack));
next->val = val;
next->previous = s;
s = next;
return val;
}
int pop(struct stack *s) {
int val = s->val;
s = s->previous;
return val;
}
</pre>
</div>
<p>
(注: 代码不一定正确, 并且请了解如何使用 <code>typeof</code> 来让上面的代码变得更美观. )
</p>
<p>
假设我们已经拥有了一个完美的栈 <code>stack</code>, 而不是上面的代码.
</p></li>
<li><p>
我们现在维护一个数组, 作为进程池,
其中每个元素都是一个指向我们子进程的一个运行指针.
</p>
<p>
那么思考该如何让他们同时, 或者说, 看起来同时进行?
提示: 周游历遍.
</p></li>
<li>请在上面的提示的基础上, 给一个最粗略的多线程的例子.
并且了解程序的 interrupt. 给出上面的多线程的例子有什么不足之处,
以及可以如何改进. (optional)</li>
</ol>
</div>
</li>
</ol>
</li>
<li><a id="org8946f33"></a>程序的基本结构<br />
<div class="outline-text-5" id="text-2-5-1-4">
<p>
我们以一个简单的程序为例:
</p>
<div class="org-src-container">
<pre class="src src-text"><c_code> ::= <preprocess>*
<function_declaration>*
<main_function>
<function_definition>*
</pre>
</div>
</div>
</li>
<li><a id="org57f5256"></a>Preprocess<br />
<div class="outline-text-5" id="text-2-5-1-5">
<p>
也就是预处理, 在这一部分, 我们会遇到引用头文件, 宏定义,
预编译等等的知识.
</p>
<p>
在 C 语言里, 宏虽然是叫 marco, 实际上和 Lisp 的 marco 系统比起来,
更像是一种字符串替换的系统.
</p>
<ul class="org-ul">
<li><p>
<code>define</code>
</p>
<div class="org-src-container">
<pre class="src src-C">#define TRUE 1
#define FALSE 0
#define MAX(a, b) (a > b ? a : b)
</pre>
</div>
<p>
实际上就是在编译的时候, 提前替换程序中所有和定义的字符串,
或者定义的形式相同的文本, 然后假装啥也没发生. 比如下面的代码,
</p>
<div class="org-src-container">
<pre class="src src-C">if (MAX(x, y) > 3) {
return TRUE;
} else {
return FALSE;
}
</pre>
</div>
<p>
经过替换后会变成:
</p>
<div class="org-src-container">
<pre class="src src-C">if ((x > y ? x : y) > 3) {
return 1;
} else {
return 0;
}
</pre>
</div>
<p>
之所以会这样做, 是为了减少我们在代码中使用不必要的魔法数字,
比如后者的 <code>1</code> 和 <code>0</code> 就会让人不知道他们的含义. 甚至如果你定义
<code>TRUE</code> 为 <code>666</code>, <code>FALSE</code> 为 <code>999</code>, 那么如果只看数字值的话,
你的代码就是一个很好的混淆的代码了.
</p></li>
<li><p>
<code>if</code> 判断
</p>
<p>
在预编译的时候, 你可能还会接触到宏条件判断,
比如如果没有定义宏 <code>HELLO</code>, 那么定义宏 <code>HELLO</code>,
就可以用来防止重复定义:
</p>
<div class="org-src-container">
<pre class="src src-C">#ifndef HELLO
#define HELLO
#endif
</pre>
</div>
<p>
类似的就不多展开了.
</p></li>
<li><p>
<code>include</code>
</p>
<p>
我们可能还想要在代码中引用其他已经写好了的库,
于是通过 <code>#include <lib_name></code> 即可引用相应的库.
比如标准输入和输出的库:
</p>
<div class="org-src-container">
<pre class="src src-C">#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
</pre>
</div></li>
</ul>
</div>
</li>
<li><a id="org0206c22"></a>Main Function and Function Definition<br />
<div class="outline-text-5" id="text-2-5-1-6">
<p>
一般而言, 一个 C 程序以 <code>main</code> 函数作为程序入口.
正如我们之前所介绍的子程序的例子一样.
我们可以将一个系统看作是一个大的主程序, 那么想要运行一个程序的时候,
就会将我们的程序先载入到内存中,
就像是古早的游戏机插上了游戏卡带 (实际并不是) 一样.
</p>
<p>
然后系统就会像是对待一个子程序一样对待我们的程序:
为其开辟栈来存放变量, 为其提供函数调用, 等等.
</p>
<p>
(Note: 因为编译器是线性读取代码的, 所以在处理函数跳转的位置的时候,
如果你的代码定义是在调用后的话, 编译器会不知道你在说什么,
或者说, 它不知道自己要跳转到哪里. 所以你会经常看见 C 代码里面会有
将定义写在前面的, 或者只写一句话定义的形式. )
</p>
<div class="org-src-container">
<pre class="src src-C">#include <stdio.h>
void foo();
int main() {
foo();
}
void foo() {
puts("foo");
}
</pre>
</div>
</div>
</li>
<li><a id="orga6af9d8"></a>Final<br />
<div class="outline-text-5" id="text-2-5-1-7">
<p>
其实还有很多需要学, 不过目前只是提供了一个简单的形式框架.
可以参考:
</p>
<ul class="org-ul">
<li><a href="https://www.geeksforgeeks.org">GeeksforGeeks</a></li>
<li><a href="https://www.tutorialspoint.com/index.htm">TutorialsPoint</a></li>
<li><a href="https://www.runoob.com/cprogramming/c-tutorial.html">菜鸟教程</a></li>
<li>等等</li>
</ul>
</div>
</li>
</ol>
</div>
<div id="outline-container-orge9ca600" class="outline-4">
<h4 id="orge9ca600"><span class="section-number-4">2.5.2.</span> Python <a id="orgb64ff44"></a></h4>
<div class="outline-text-4" id="text-2-5-2">
<p>
这部分请参考 <a href="https://gitee.com/igem_ucas_china/2023-i-gem-software-new-members-tutorial">软件组的教程</a>.
</p>
</div>
</div>
<div id="outline-container-orgffe674f" class="outline-4">
<h4 id="orgffe674f"><span class="section-number-4">2.5.3.</span> Ruby <a id="org28aeb44"></a></h4>
</div>
<div id="outline-container-org3d0bad0" class="outline-4">
<h4 id="org3d0bad0"><span class="section-number-4">2.5.4.</span> Lisp <a id="orge3d038c"></a></h4>
<div class="outline-text-4" id="text-2-5-4">
</div>
<ol class="org-ol">
<li><a id="orgd68bed7"></a>Lisp 的语法<br />
<div class="outline-text-5" id="text-2-5-4-1">
<p>
因为 Lisp 的语法非常的简单, 可以说是简单到语法即 AST (抽象语法树).
不过也因为这样的语法, 导致了其被饱受吐槽的括号问题.
</p>
</div>
</li>
</ol>
</div>
</div>
<div id="outline-container-orgbe6497a" class="outline-3">
<h3 id="orgbe6497a"><span class="section-number-3">2.6.</span> Write Document <a id="org1edcb70"></a></h3>
<div class="outline-text-3" id="text-2-6">
</div>
<div id="outline-container-org8128174" class="outline-4">
<h4 id="org8128174"><span class="section-number-4">2.6.1.</span> 标记性语言</h4>
<div class="outline-text-4" id="text-2-6-1">
<p>
虽然在很早的时候, 大部分的东西都是一个纯文本,
但是纯文本没有格式上的直观区分, 在阅读的时候会比较累.
所以就会诞生一个对带文本格式的语言的需求.
</p>
<p>
(注: 附上一个我觉得比较好的 <a href="https://speakerdeck.com/chloerei/xie-zuo-ji-bian-cheng">beamer</a>, 不过对于已经熟练的同学,
我们可以不必关心那么多. 因为我们大部分时候,
只是对这些当作一个工具来使用, 没有必要太敬畏它们. )
</p>
<ul class="org-ul">
<li><a href="https://daringfireball.net/projects/markdown/syntax">Markdown</a> 是一个比较流行的标记语言</li>
<li><a href="https://orgmode.org">Org-Mode</a> 这个文档就是用 org-mode 来编写的.</li>
<li>其他</li>
</ul>
</div>
</div>
<div id="outline-container-org7d76aa2" class="outline-4">
<h4 id="org7d76aa2"><span class="section-number-4">2.6.2.</span> 文档需要有什么</h4>
<div class="outline-text-4" id="text-2-6-2">
<p>
这个, 大家可以去调研一下. 或者在大学写作中提问一下.
</p>
</div>
</div>
<div id="outline-container-org253db30" class="outline-4">
<h4 id="org253db30"><span class="section-number-4">2.6.3.</span> 重新思考标记性语言 <a id="orgd4fa617"></a></h4>
<div class="outline-text-4" id="text-2-6-3">
<p>
思考, 这些标记性语言是否都是有相同的东西?
在我有限的知识里面, 我可以这样解释:
</p>
<p>
是的, 它们都有共同之处.
</p>
<p>
比如我们可以看到, 比如 Markdown:
</p>
<div class="org-src-container">
<pre class="src src-markdown"># 标题
## 次级标题
**加粗**, _斜体_, `代码`
* 列表
1. 列表
</pre>
</div>
<p>
你会发现, 某些语法是通过包裹的形式来,
有的语法是前导的方式. 这些语言形式,
我们是否有可能将其形式化地处理?
</p>
<p>
比如我们这里介绍一个叫做 <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form">EBNF</a> 的东西.
(你可以在 <a href="https://bnfplayground.pauliankline.com">这个网站</a> 来尝试一下)
</p>
<p>
我们可以写出一个简单的东西:
</p>
<div class="org-src-container">
<pre class="src src-text"><style> ::= <wrapped_style> | <prefixed_style>
<prefixed_style> ::= <prefix> <content>
<wrapped_style> ::= <bold> | <italic>
<prefix> ::= ("*" | <number> ".") " "
<number> ::= [0-9]
<bold> ::= "**" <content> "**" | "__" <content> "__"
<italic> ::= "*" <content> "*" | "_" <content> "_"
<content> ::= ([0-9] | [a-z] | [A-Z])
</pre>
</div>
<p>
不过我这个写得不是很好, 这里建议可以参考 <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form">Wikipedia</a> 上的
Pascal-like Programming Language 例子:
</p>
<div class="org-src-container">
<pre class="src src-text">(* a simple program syntax in EBNF - Wikipedia *)
program = 'PROGRAM', white space, identifier, white space,
'BEGIN', white space,
{ assignment, ";", white space },
'END.' ;
identifier = alphabetic character, { alphabetic character | digit } ;
number = [ "-" ], digit, { digit } ;
string = '"' , { all characters - '"' }, '"' ;
assignment = identifier , ":=" , ( number | identifier | string ) ;
alphabetic character = "A" | "B" | "C" | "D" | "E" | "F" | "G"
| "H" | "I" | "J" | "K" | "L" | "M" | "N"
| "O" | "P" | "Q" | "R" | "S" | "T" | "U"
| "V" | "W" | "X" | "Y" | "Z" ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
white space = ? white space characters ? ;
all characters = ? all visible characters ? ;
</pre>
</div>
<p>
你可以发现, 我们就可以像这样来形式上地了解一门语言.
并且我们可以用这样的方式来进行构建我们自己的语言.
不过这个目前我们并不需要太了解.
在之后我们会有相应的一个介绍.
</p>
<p>
感兴趣的同学可以先看一下下面的 <a href="#org9a34236">一个例子</a>.
</p>
</div>
</div>
</div>
<div id="outline-container-orgb373a38" class="outline-3">
<h3 id="orgb373a38"><span class="section-number-3">2.7.</span> Others <a id="org6649a37"></a></h3>
<div class="outline-text-3" id="text-2-7">
<ul class="org-ul">
<li><a href="https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md">提问的艺术</a></li>
<li>搜索引擎的使用</li>
<li><p>
<a href="https://www.cs.virginia.edu/~evans/cs216/guides/x86.html">简单的汇编知识</a>
(注: 因为我们不需要用汇编来写程序, 所以没有必要非常了解,
大概能做到看得懂就可以了. )
</p>
<p>
(注: 可以参考一下 <a href="https://li-yiyang.github.io/ruby/ri-lang/">Computer Toys</a> 是我之前计科导做的一个小玩具,
算是一个简单的编译器的实现. )
</p></li>
<li><a href="https://ruby-china.org/topics/41296">三点几嚟,饮茶先啦 —— 将大马饮料名编译成汉语</a> <a id="org9a34236"></a></li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org9db23f9" class="outline-2">
<h2 id="org9db23f9"><span class="section-number-2">3.</span> 单片机基础知识 <a id="org22a62c4"></a></h2>
<div class="outline-text-2" id="text-3">
<div id="org1db30cf" class="figure">
<p><img src="./pic/8052.svg" alt="8052.svg" class="org-svg" width="61.8%" />
</p>
<p><span class="figure-number">Figure 2: </span>8052</p>
</div>
<p>
我们使用的是 51 单片机作为焊接, 以及单片机学习的一个对象.
</p>
</div>
<div id="outline-container-org15d0f26" class="outline-3">
<h3 id="org15d0f26"><span class="section-number-3">3.1.</span> 教学方式</h3>
<div class="outline-text-3" id="text-3-1">
<p>
目前的想法是通过项目的形式来进行学习.
如果有好的项目想法的话, 就都可以用来练手.
</p>
<ul class="org-ul">
<li><p>
<b>焊接一台 51 单片机</b>
</p>
<p>
我们这一次的硬件组的想法是能够将整个机器用一个完整的 PCB
来封装起来, 去年这个伟大的愿景因为我个人能力不足所以只成为了照骗,
即使用渲染软件来生成骗鬼的照片. 实在是可惜.
</p>
<p>
所以这次我们希望通过焊接 51 单片机的一个方式,
来了解一个单片机需要有哪些组成. 为之后设计 PCB,
以及组装一个原形机 (焊接等技术) 来做准备.
</p></li>
<li><p>
<b>在 51 单片机的基础上做一个音乐盒</b>
</p>
<p>
其中需要包含:
</p>
<ul class="org-ul">
<li>点灯: 如何将一个引脚 (寄存器) 置为高电位</li>
<li>播放声音: 循环和函数</li>
<li>播放特定的声音: 条件判断</li>
<li>电脑控制: 串口和多线程</li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-org8c91c86" class="outline-3">
<h3 id="org8c91c86"><span class="section-number-3">3.2.</span> 简单的知识 <a id="orge436627"></a></h3>
<div class="outline-text-3" id="text-3-2">
<blockquote>
<p>
孩子们在田野里玩, 一个伙伴对我说: "嘿, 看那只鸟. 那是什么鸟?"
我说: "我对这种鸟一无所知." 他说: "这是褐喉画眉", 又说,
"你爸爸什么也没有告诉你. "
</p>
<p>
事实恰恰相反, 我父亲当然教过我.
</p>
<p>
看着一只鸟, 父亲说: "知道这是什么鸟吗? 这是褐喉画眉;
但是在葡萄牙, 它的名字是… 在意大利, 名字是…",
他说, "在中国, 名字是… 在日本, 名字是…" 等等.
</p>
<p>
"喏," 他说, "各种语言中你都想知道它的名字叫什么,
但是当你知道了所有这些名字之后, 你其实对这鸟还是一无所知.
你所知道的, 仅仅是不同地方的人怎么称呼这种鸟而已.
"现在," 他说: "我们来 '看' 这只鸟. "
</p>
<p>
通过这些事, 父亲教导我: 要去"观察"事物.
</p>
<p>
from Richard Feynman
</p>
</blockquote>
<p>
在接下来, 我们会接触到各种各样的元件, 以及他们的名字.
然而这些名字并不是什么具有魔力的真名,
你没法直接通过仅仅知道他们的名字来控制他们.
</p>
<p>
也就是说, 除了名字之外, 我们还需要知道他们的作用.
</p>
</div>
<div id="outline-container-org8b5a6d7" class="outline-4">
<h4 id="org8b5a6d7"><span class="section-number-4">3.2.1.</span> 零件表</h4>
<div class="outline-text-4" id="text-3-2-1">
<p>
需要认识并且能够辨认基本的元件以及了解其作用和性质.
</p>
<div id="org7a618d7" class="figure">
<p><img src="./pic/solder-iron.jpg" alt="solder-iron.jpg" width="61.8%" />
</p>
<p><span class="figure-number">Figure 3: </span>电烙铁</p>
</div>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-left" />
</colgroup>
<tbody>
<tr>
<td class="org-left">零件名称</td>
<td class="org-left">零件用途</td>
</tr>
<tr>
<td class="org-left">外热式电烙铁</td>
<td class="org-left">加热, 焊接元件</td>
</tr>
</tbody>
</table>
<p>
<a href="https://zh.wikipedia.org/wiki/%E7%83%99%E9%90%B5">电烙铁</a>: 所谓的外热式电烙铁, 就是电热丝在烙铁头外面的电烙铁.
相对应的有内热式的电烙铁. <i>思考: 内热式和外热式的区别?</i>
</p>
<p>
在电烙铁加热的过程中, <b>绝对不要</b> 用手去碰电烙铁前端的金属部分,
也 <b>绝对不要</b> 让电烙铁离开你的注意力范围外,
<b>绝对不要</b> 让电烙铁的金属部分接触除了焊接元件以外的东西.
</p>
<p>
注: 因为为了省钱, 所以我们使用的电烙铁是那种非常廉价的电烙铁.
没有温控也没有很多的功能. 不过请放心, 这个应该是够用的.
</p>
<p>
(可以参考的 <a href="https://www.bilibili.com/video/BV1yv41167ky">焊接教学视频 bilibili</a>. )
</p>
<div id="org2e4f98f" class="figure">
<p><img src="./pic/solder-iron-kits.jpg" alt="solder-iron-kits.jpg" width="61.8%" />
</p>
<p><span class="figure-number">Figure 4: </span>电烙铁的其他元件</p>
</div>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-left" />
</colgroup>
<tbody>
<tr>
<td class="org-left">零件名称</td>
<td class="org-left">零件用途</td>
</tr>
<tr>
<td class="org-left">高温海绵</td>
<td class="org-left">擦拭烙铁头, 去除多余的焊锡</td>
</tr>
<tr>
<td class="org-left">松香</td>
<td class="org-left">助焊剂, 可以防止因为氧化而难以上锡</td>
</tr>
<tr>
<td class="org-left">支撑架</td>
<td class="org-left">使用时将其撑起, 暂时放置电烙铁</td>
</tr>
<tr>
<td class="org-left">烙铁芯</td>
<td class="org-left">加热丝</td>
</tr>
<tr>
<td class="org-left">烙铁头</td>
<td class="org-left">被加热物体, 焊接的主要的东西, 有不同形状的头</td>
</tr>
<tr>
<td class="org-left">焊锡丝</td>
<td class="org-left">忘拍了</td>
</tr>
</tbody>
</table>
<p>
在焊接前, 将高温海绵用少许水浸润, 在使用的时候,
建议不要经常去擦拭, 因为会对烙铁头有一定的影响.
(不过无所谓, 毕竟便宜货. ) 常见的去除多余焊锡的方法还有:
黄铜丝, 以及直接用手敲掉. (注: 来自网络, 不能确定是否可靠,
但是在规范里面是写的用海绵来清洁. )
</p>
<p>
电烙铁蘸一点松香有助于焊接方便以及焊点明亮,
但是因为松香会有烟尘颗粒, 实际上吸入后并不是很健康,
所以建议不要闻太多. (可以带上口罩, 尽管医用口罩没有太多用)
</p>
<p>
电烙铁支撑架只是临时搁置电烙铁的东西,
<b>不要</b> 将电烙铁放在上面长时间工作 (空载),
一个对电烙铁不好, 一个是万一忘了, 人碰到会很痛苦.
</p>
<div id="orgf93b99d" class="figure">
<p><img src="./pic/8052-assembly.jpg" alt="8052-assembly.jpg" width="61.8%" />
</p>
<p><span class="figure-number">Figure 5: </span>单片机的主要部分</p>
</div>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-left" />
</colgroup>
<tbody>
<tr>
<td class="org-left">零件名称</td>
<td class="org-left">零件用途</td>
</tr>
<tr>
<td class="org-left">电阻</td>
<td class="org-left">emmm, 用来限流, 或者产生电压, 用色环法来读数值</td>
</tr>
<tr>
<td class="org-left">可调电阻</td>
<td class="org-left">emmm, 可以调电阻大小的电阻</td>
</tr>
<tr>
<td class="org-left">IC 芯片</td>
<td class="org-left">嗯, 就是芯片</td>
</tr>
<tr>
<td class="org-left">排插</td>
<td class="org-left">一排的插座</td>
</tr>
<tr>
<td class="org-left">三极管</td>
<td class="org-left">判别三极管的方式是将平面对准自己, 根据是 NPN 还是 PNP 来读引脚</td>
</tr>
<tr>
<td class="org-left">发光二极管</td>
<td class="org-left">一般是长脚为正, 短脚为负</td>
</tr>
<tr>
<td class="org-left">二极管</td>
<td class="org-left">有单向导通的特性, 一般是用一条环带来表示负极 (流出端)</td>
</tr>
<tr>
<td class="org-left">晶振</td>
<td class="org-left">加电压后会产生振荡, 振荡频率为定值</td>
</tr>
<tr>
<td class="org-left">电容</td>
<td class="org-left">emmm, 有瓷片电容 (小红片) 和电解电容 (黑白)</td>
</tr>
<tr>
<td class="org-left">触点开关</td>
<td class="org-left">按下闭合</td>
</tr>
<tr>
<td class="org-left">开关</td>
<td class="org-left">嗯, 开关</td>
</tr>
<tr>
<td class="org-left">数字显示</td>
<td class="org-left">原则上就是一堆发光二极管</td>
</tr>
<tr>
<td class="org-left">继电器</td>
<td class="org-left">用小电流和弱信号来控制一个大电流和强一点的电路</td>
</tr>
<tr>
<td class="org-left">蜂鸣器</td>
<td class="org-left">这里是无源蜂鸣器, 通过输入信号来激励发出声音</td>
</tr>
<tr>
<td class="org-left">其他</td>
<td class="org-left">不一一列举了, 在图上标了, 有兴趣的可以来帮忙完善</td>
</tr>
</tbody>
</table>
<p>
对于 <a href="https://zh.wikipedia.org/wiki/%E9%9B%BB%E9%98%BB%E5%99%A8">电阻器</a>, 或者说, 色环电阻, 其阻值大小需要通过色环法来读取.
</p>
<div id="orgd7b9382" class="figure">
<p><img src="./pic/resister.png" alt="resister.png" width="61.8%" />
</p>
<p><span class="figure-number">Figure 6: </span>色环法</p>
</div>
<p>
不过, 对于这样的东西, 为什么要自己读数呢?
网络上有很多的读数程序 (比如 <a href="https://www.digikey.sg/zh/resources/conversion-calculators/conversion-calculator-resistor-color-code">这个</a>), 所以我们不会读也是没有问题的.
</p>
<p>
对于 <a href="https://zh.wikipedia.org/wiki/%E7%94%B5%E5%AE%B9%E5%99%A8">电容</a>, 我们会用到的电容类型有瓷片电容和电解电容,
两者在外貌上非常容易区分, 并且在功能上, 电解电容存在极性,
即有正负引脚之分, <b>不能够</b> 反接, 否则会导致损坏.
电解电容的负极在外壳有一条白色的条带来标记.
</p>
<p>
<a href="https://zh.wikipedia.org/wiki/%E4%BA%8C%E6%A5%B5%E9%AB%94">二极管</a> 有单向导通的特性, 这是大家都知道的东西,
从一端流入, 称为正极; 从另一端流出, 成为负极.
在电路图上, 是一个带横线的箭头, 而在实物上,
也有一条黑线作为标记负极的位置.
</p>
<p>
对于 <a href="https://zh.wikipedia.org/wiki/%E7%99%BC%E5%85%89%E4%BA%8C%E6%A5%B5%E7%AE%A1">发光二极管</a> 来说, 可以通过引脚来判断正负, 长正短负.
但是如果被剪了脚, 那么可以通过看电极来判断正负:
</p>
<div id="orgb0e4e90" class="figure">
<p><img src="./pic/LED,_5mm,_green_(en).svg.png" alt="LED,_5mm,_green_(en).svg.png" width="61.8%" />
</p>
<p><span class="figure-number">Figure 7: </span>LED 电极</p>
</div>
<p>
(图片来自 <a href="https://zh.wikipedia.org/zh-hans/%E7%99%BC%E5%85%89%E4%BA%8C%E6%A5%B5%E7%AE%A1">维基百科</a>)
</p>
<p>
实际上, 数字显示 (正式名字忘了叫啥, 后面的同学可以补充一下) 编码器,
其实也就是一堆 LED 按照一定顺序排列起来.
</p>
<p>
<a href="https://zh.wikipedia.org/wiki/%E5%8F%8C%E6%9E%81%E6%80%A7%E6%99%B6%E4%BD%93%E7%AE%A1">三极管</a> 主要有 NPN 和 PNP 两种, 可以简单认为三极管的作用是放大电流,
或者作为开关来工作. (当然, 不是那么简单的啦.
这里建议电子系的同学来介绍一下. )
</p>
<p>
触点开关, 类似于两个导电薄片, 按下时闭合.
</p>
<p>
<a href="https://en.wikipedia.org/wiki/Buzzer">蜂鸣器</a> 这个是我们之后会用到的一个元件, 通过压电效应来产生振动,
发出声音. 我们常见的扬声器则是通过产生磁场, 振动膜片的方式来实现的.
(哦, 好像有动圈, 动铁的等等. ), 存在一定的极性, 在蜂鸣器上有标记.
</p>
<p>
<a href="https://zh.wikipedia.org/wiki/%E7%9F%B3%E8%8B%B1%E6%99%B6%E4%BD%93%E8%B0%90%E6%8C%AF%E5%99%A8">晶振</a> 同样是利用 (石英的) 压电效应. 不过不必了解太多,
只需要知道晶振可以提供一个按一定频率振动的一个信号.
</p>
<blockquote>
<p>
\(11.0592 MHz = 115,200 \times 96 = 1.88432 MHz \times 6\)
便於精確產生UART工作頻率, 且正好是原始的 8051 微控制器工作頻率上限.
</p>
<p>
from <a href="https://zh.wikipedia.org/wiki/%E7%9F%B3%E8%8B%B1%E6%99%B6%E4%BD%93%E8%B0%90%E6%8C%AF%E5%99%A8#%E7%9F%B3%E8%8B%B1%E6%99%B6%E9%AB%94%E5%B8%B8%E8%A6%8B%E7%9A%84%E9%A0%BB%E7%8E%87">维基百科-石英晶体常见的工作频率</a>
</p>
</blockquote>
<p>
<a href="https://zh.wikipedia.org/wiki/%E9%9B%86%E6%88%90%E7%94%B5%E8%B7%AF">IC 芯片</a> 就是集成电路啦.
</p>
<p>
<a href="https://zh.wikipedia.org/wiki/%E5%8D%B0%E5%88%B6%E7%94%B5%E8%B7%AF%E6%9D%BF">PCB</a> 也就是印刷电路板, 就是 <a href="#orgf93b99d">上图</a> 中间那块蓝色的板子.
其中可以看到在电路板上有用白色涂料标记了各个孔位对应的元件标识,
以及元件信息. 这样的白色印刷, 我们称为丝印.
用来标记帮助印刷. 我们会在之后的 <a href="#orgd0054b4">KiCad 课程</a> 中学习如何绘制原理图,
以及如何设计 PCB 并交给加工厂去生产.
</p>
<div id="org7abb711" class="figure">
<p><img src="./pic/8052-assembly-kit.jpg" alt="8052-assembly-kit.jpg" width="61.8%" />
</p>
<p><span class="figure-number">Figure 8: </span>单片机的其他零件</p>
</div>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-left" />
</colgroup>
<tbody>
<tr>
<td class="org-left">零件名称</td>
<td class="org-left">零件用途</td>
</tr>
<tr>
<td class="org-left">杜邦线</td>
<td class="org-left">连接电路, 有公头和母座之分</td>
</tr>
<tr>
<td class="org-left">USB 数据线</td>
<td class="org-left">USB Type-B 类型的数据线, 用来连接电脑和传输数据</td>
</tr>
</tbody>
</table>
<p>
就是导线啦, 原则上是这样的.
</p>
</div>
</div>
<div id="outline-container-org9e509c7" class="outline-4">
<h4 id="org9e509c7"><span class="section-number-4">3.2.2.</span> 原理图</h4>
<div class="outline-text-4" id="text-3-2-2">
<p>
目前没有搞到高清的原理图, 先用店家淘宝上的宣传图看看吧:
</p>
<div id="org8c6e295" class="figure">
<p><img src="./pic/8052.jpg" alt="8052.jpg" width="61.8%" />
</p>
<p><span class="figure-number">Figure 9: </span>原理图 (暂)</p>
</div>
<p>
通过原理图, 可以知道单片机的引脚和外接的设备的对应关系.
</p>
</div>
</div>
<div id="outline-container-org9145c20" class="outline-4">
<h4 id="org9145c20"><span class="section-number-4">3.2.3.</span> 焊接方法</h4>
<div class="outline-text-4" id="text-3-2-3">
<p>
注意: 焊接有风险, 操作需谨慎, 通风很重要,
焊铅有毒的, 没事别乱吸.
</p>
<p>
不过, 其实焊接就是一种技术,
不过我们肯定是没法和电子厂的人比就是了.
</p>
<p>
一些简单的知识:
</p>
<ul class="org-ul">
<li>焊锡受热会熔化, 熔点大概是在 200 摄氏度左右,
冷却后凝固, 在两块导体之间形成连接固定作用.</li>
<li>焊笔的作用就是用来熔化焊锡, 所以它会很烫.
(根据我之前用的焊笔, 大概有 200 到 500 度可调)
<b>所以请不要用手摸</b></li>
<li>众所周知, 加热会加速氧化, 所以 <b>不要</b> 长时间加热焊锡
以及 <b>不要</b> 长时间加热焊笔, 因为它们会在表面形成一个氧化层,
其熔点高, 且不容易和金属相连接.</li>
<li>为了防止氧化, 或者去除氧化层, 我们会使用助焊剂 (松香),
以及使用蘸水的高温海绵或者黄铜丝来助焊或去除焊头的氧化层.</li>
<li>为了让被焊的金属和焊锡更加好地结合, 在焊接的时候,
先加热被焊元件, 再将焊锡移动到被焊元件上.</li>
<li>元件也怕烫的, 所以不要加热太久.
(并且, 拿这元件的手也会被传递的热烫到. )</li>
<li>但是我知道的也不多, 并且技术也很烂, 所以还是请自学吧:
<ul class="org-ul">
<li><a href="https://www.bilibili.com/video/BV1eJ411K7rM/">bilibili</a> 虽然我觉得去搜索一下更好.
(可以参考的 <a href="https://www.bilibili.com/video/BV1yv41167ky">焊接教学视频 bilibili</a>. )</li>
<li>上手去试, 网上有那种 10 块钱 5 片的焊接考试板,
甚至还有更加多的焊接板, 可以去买过来练练.
不过一般不需要那么练的.</li>
</ul></li>
</ul>
<p>
(注: 虽然我们也可以使用面包板, 杜邦线这样可以减少焊接麻烦的东西,
但是在最后的成品里面, 我们还是需要使用焊接的, 并且如果有可能的话,
我们之后可以尝试使用更加方便的加热台等方式来焊接贴片元件. )
</p>
<p>
最后, 附上可供参考的文档 (突然发现自己学的并不标准,
b 站上的视频也不一定规范, 所以就随便焊吧, 毕竟是便宜货,
不要有太大的心理压力):
</p>
<ul class="org-ul">
<li><a href="http://www.radtek.cn/pdf/4aa1654c319f56fc93191d419bf7f587.pdf">元件焊接质量检验规范</a></li>
<li><a href="http://xmgxjd.com/Upload/ueditor/files/2017-08-07/%E9%99%84%E4%BB%B6%EF%BC%9A2%E6%89%8B%E5%B7%A5%E7%84%8A%E6%8E%A5%E8%A6%81%E6%B1%82%E5%8F%8A%E9%AA%8C%E6%94%B6%E6%A0%87%E5%87%86-9d32f3f2-d2ea-42f0-8cc6-e255f5f1b77c.pdf">手工焊接要求及验收标准</a></li>
<li><a href="http://fjdzzyk.cavtc.cn/__local/F/27/AA/62F1F23003171E42977926C7B1D_455035F4_97E9A9.pdf">IPC-7711/7721</a></li>
</ul>
</div>
</div>
<div id="outline-container-orgf87036b" class="outline-4">
<h4 id="orgf87036b"><span class="section-number-4">3.2.4.</span> 电路基本知识</h4>
<div class="outline-text-4" id="text-3-2-4">
<p>
实际上就是一些高中物理和通用技术的知识,
可以由电子系的同学来进行补充.
</p>
</div>
<ol class="org-ol">
<li><a id="org5471493"></a>数字电路的简单知识<br />
<div class="outline-text-5" id="text-3-2-4-1">
<ul class="org-ul">
<li>高电平和低电平 (<a href="https://zh.wikipedia.org/wiki/%E9%80%BB%E8%BE%91%E7%94%B5%E5%B9%B3">逻辑电平</a>)</li>
<li><p>
<a href="https://zh.wikipedia.org/wiki/%E9%82%8F%E8%BC%AF%E9%96%98">逻辑门</a>
</p>
<p>
可以理解为 <a href="https://zh.wikipedia.org/wiki/%E6%95%B0%E7%90%86%E9%80%BB%E8%BE%91">数理逻辑</a>, 可以参考国科大开设的离散数学的课程.
或者是逻辑学课程. 我们目前不必了解太多, 因为目前应该用不到.
(或者参考我的 <a href="https://li-yiyang.github.io/notes/discreate-mathematic/">笔记</a>)
</p></li>
<li><p>
<a href="https://zh.wikipedia.org/wiki/%E8%A7%A6%E5%8F%91%E5%99%A8">触发器</a>
</p>
<div id="org97f45b9" class="figure">
<p><img src="./pic/440px-R-S_mk2.gif" alt="440px-R-S_mk2.gif" width="61.8%" />
</p>
<p><span class="figure-number">Figure 10: </span>RS 触发器</p>
</div>
<p>
可以理解为一个可以记忆前一状态的元件.
</p></li>
</ul>
<p>
一些可以拓展阅读的资料:
</p>
<ul class="org-ul">
<li>去找一本电子学的书可以看看</li>
<li>有一本书叫做 <a href="https://book.douban.com/subject/4822685/">编码</a>, 写得非常通俗易懂.</li>
<li>MC 里面的 <a href="https://minecraft.fandom.com/zh/wiki/%E6%95%99%E7%A8%8B/%E7%BA%A2%E7%9F%B3%E8%AE%A1%E7%AE%97%E6%9C%BA?variant=zh">红石计算机</a>, 但是我不是很会就是了.</li>
<li><a href="https://www.bilibili.com/video/BV1BJ411R7s5">8 比特计算机</a>, 网上冲浪的时候看到的, 感觉很有意思,
就是元件太费钱了.</li>
</ul>
</div>
</li>
<li><a id="orgbd96837"></a>模拟电路的基本知识<br /></li>
</ol>
</div>
<div id="outline-container-orgb03f148" class="outline-4">
<h4 id="orgb03f148"><span class="section-number-4">3.2.5.</span> 计算机的运行 <a id="org50041ad"></a></h4>
<div class="outline-text-4" id="text-3-2-5">
<p>
相比大家都接触过 GalGame (误), 那么计算机就像是 GalGame 一样,
我们可以将其理解为一串的指令, 对应着 GalGame 中的文本,
处理器就像是读者 (没错 GalGame 我记得应该是叫做视觉小说),
将指令一条一条地读取并执行.
</p>
<p>
就像是我们在基本知识中遇到的 <a href="#org602a8b5">代码</a> 一样. 我们的代码是一个 "线性" 的组织.
我们可以说, 第一行的代码, 第二行的代码等等.
</p>
<p>
于是我们不妨将计算机代码想象成一列排成行的盒子, 每个盒子都标上了编号,
而我们的计算机就像是一个行走在盒子上的机器人, 每到达一个盒子,
就会读取盒子里面的信息, 然后按照信息来执行代码, 比如处理这个盒子的信息,
然后移动到下一个盒子.
</p>
<p>
(注: 这就是 <a href="https://zh.wikipedia.org/zh-hans/%E5%9B%BE%E7%81%B5%E6%9C%BA">图灵机</a> 了. 不过应该没有人想要通过图灵机来编程的吧?
关于图灵机, 我的想法是, 反正到时候计科导要学, 没有必要现在理解. )
</p>
<p>
如果我们用 <code>PS</code> 来记录当前所在的盒子的编号,
(这样可以记录数据的东西, 我们叫做 <a href="https://zh.wikipedia.org/zh-hans/%E5%AF%84%E5%AD%98%E5%99%A8">寄存器 Register</a>)
于是我们的机器的逻辑如下:
</p>
<p>
如果你喜欢的话, 可以用 C 的形式来写:
</p>
<div class="org-src-container">
<pre class="src src-C">void machine(int ps) {
// for example, if we read `CMD A, B`, then do machine_CMD
evaluate(read(PS));
machine(PS++);
}
</pre>
</div>
<p>
实际上, 这就是一个解释器. 我们会在 <a href="#orgb66c8aa">之后</a> 的部分里面来介绍如何实现一个简单的玩具,
来实现这个简单的玩具, 以及进一步理解为什么计算机是这样动的.
</p>
<p>
于是我们就能够实现简单的跳转:
</p>
<div class="org-src-container">
<pre class="src src-C">void machine_jmp(int PS) {
machine(PS);
}
</pre>
</div>
<p>
比如说, 代码是这样的:
</p>
<div class="org-src-container">
<pre class="src src-text">1: PUSH_STONE_TO_THE_TOP_OF_MONTAIN
2: DROP_STONE_DOWN_TO_MOUNTAIN_FOOT
3: JMP 1
</pre>
</div>
<p>
于是我们就得到了一个西西弗斯的 <b>循环</b> 程序了 (并没有).
</p>
<p>
当然, 我们更像要一些条件跳转, 一个想法就是用运算的结果作为条件,
比如结果为真, 结果为假之类的. 于是我们就需要在真的时候跳转,
或者在假的时候跳转. 于是我们就想要再来一个能够储存结果的寄存器:
</p>
<p>
比如说我们有一个叫做 <code>FLAG</code> 的寄存器, 能够储存 <code>CMP</code> 运算的结果,
于是在执行 <code>J+CONDITION</code> 的时候, 就会根据 <code>FLAG</code> 中的值来选择是否跳转.
</p>
<p>
于是我们就能够用其来写出分支结构了.
</p>
<div class="org-src-container">
<pre class="src src-text">1: CMP TODAY, WEEKEND ;; while (1) {
2: JMP_IF_EQUAL 6 ;; if (TODAY == WEEKEND) {
3: WORKING ;; HAVING_FUN();
5: JMP 7 ;; } else {
6: HAVING_FUN ;; WORKING();
7: END_OF_THE_DAY ;; }
8: JMP 1 ;; END_OF_THE_DAY();
;; }
</pre>
</div>
<p>
不难发现, 我们通过寄存器的方式, 可以记录很多信息, 并且使用起来非常的方便.
但是我们并不能在所有的程序里面都使用寄存器. 比如说,
我们没法随意地引入非常多的各种寄存器来表示各种变量.
但是退而求其次, 我们可以在计算机的内存中放入较多的数据和变量.
</p>
<p>
(注: 这里我觉得讲得有点不太严谨, 可能会有一些误导的成分.
如果你不打算严格地学习的话, 你可以将前文所说的内存想象成一列装东西的箱子,
把寄存器想象成一个写了一些数据的小本本, 后者比前者访问的速度要快一点,
但是装的东西的数量少很多. )
</p>
<p>
比如说, 在一列数据里面, 我们要装一些数据:
</p>
<div class="org-src-container">
<pre class="src src-text"> ||
\/
+-----------------------------------+
| 0 | 1 | 1 | 0 | 1 | 0 | ... | *** |
+-----------------------------------+
#0 #1 #2 #3 #4 #5 ...
</pre>
</div>
<p>
这些数据有自己的编号 (也就是地址), 通过地址来访问他们.
</p>
<p>
但是还有一个问题, 我们该如何记住自己的数据放在哪里.
以及我们该如何处理这些数据. 一个简单的想法就是第 \(i\) 个变量
放在第 \(i\) 个位置. 然而这样的方法的坏处就是空间难以复用,
并且需要管理和记忆非常多的一个变量. 所以一个自然的想法就是 <a href="https://zh.wikipedia.org/wiki/%E5%A0%86%E6%A0%88">栈</a>.
</p>
<p>
我们可以用 <code>BP</code> 来表示指向当前栈底部位置的一个量.
(用 <code>SP</code> 来记录当前上一个栈的 <code>BP</code> 的位置. )
用相对 <code>BP</code> 的偏移量来记录栈上的变量.
比如我们可能有一个函数叫做 \(f(x)\), 它的功能就是:
\(f(x) = x + a, a = 1\).
</p>
<p>
于是, 我们的栈可能长的像这样:
</p>
<div class="org-src-container">
<pre class="src src-text"> SP x a BP
|| || || ||
\/ \/ \/ \/
+-----------------------------------+
| 0 | 1 | 1 | 0 | 1 | 0 | ... | *** |
+-----------------------------------+
#0 #1 #2 #3 #4 #5 ...
</pre>
</div>
<p>
<i>思考题</i>: 如何用上面的方式来实现一个函数调用.
</p>
<p>
可以参考的知识:
</p>
<ul class="org-ul">
<li><a href="https://zhuanlan.zhihu.com/p/25816426">栈溢出</a></li>
<li><a href="https://www.zhihu.com/question/22444939">计算机函数调用的栈</a></li>
</ul>
<p>
想不出来没有关系, 反正对我们来说 (暂时) 没有那么重要.
</p>
<p>
如果在上面已经实现了函数调用, 那么思考能否拓展这个结构,
将其变成一个能够 "同时" 执行多个函数的东西呢?
</p>
</div>
</div>
</div>
<div id="outline-container-orgc4e7346" class="outline-3">
<h3 id="orgc4e7346"><span class="section-number-3">3.3.</span> 第一个任务: 焊接 51 单片机</h3>
<div class="outline-text-3" id="text-3-3">
<p>
注意: 在焊接前, <b>请务必仔细阅读</b> 前面的 <a href="#orge436627">简单知识</a> 的部分,
<b>尤其是电烙铁部分</b>.
</p>
</div>
<div id="outline-container-orgc31e142" class="outline-4">
<h4 id="orgc31e142"><span class="section-number-4">3.3.1.</span> Step 0: 做好准备并了解大概的内容</h4>
<div class="outline-text-4" id="text-3-3-1">
<ol class="org-ol">
<li>洗手</li>
<li>了解焊接顺序</li>
<li>焊接元件</li>
<li>检验焊接并对有问题的焊接进行排查修改</li>
<li>上电验机</li>
</ol>
</div>
</div>
<div id="outline-container-org6c3c0f7" class="outline-4">
<h4 id="org6c3c0f7"><span class="section-number-4">3.3.2.</span> Step 1: 了解焊接顺序并焊接</h4>
<div class="outline-text-4" id="text-3-3-2">
<div id="org363f380" class="figure">
<p><img src="./pic/8051-done.png" alt="8051-done.png" width="61.8%" />
</p>
<p><span class="figure-number">Figure 11: </span>8052 完成图</p>
</div>
<p>
来自卖家秀的图片, 等焊接完了之后, 我们可以替换一下这个图片.
</p>
<p>
实际上, 我们会发现, 不同的元件的高度大小不一, 他们的焊接方法也不同,
所以我们就需要了解如何焊接以及用什么样的顺序来焊接.
(防止出现不好焊, 焊太久把焊盘给烧坏了, 焊太久把芯片给烧了的悲剧. )
</p>
<ol class="org-ol">
<li>贴片: 两个芯片, 可能会觉得很难焊接,
因为我们没有很好的工具.</li>
<li>过孔件: 正面固定后, 在背面焊接, 较为便利.</li>
</ol>
<p>
(Note: 可以参考一下网上的焊接顺序的介绍. )
</p>
<p>
不过作为一个焊接教学的课程, 我们应该没法一开始就焊接贴片件,
所以我们可以做一个小小的折中:
</p>
<ol class="org-ol">
<li><p>
首先焊接流水灯模块, 以及电阻, LED, 电容和二极管这些长管脚的元件
</p>
<p>
(这是因为这些长管脚的元件比较好焊接, 可以快速上手)
</p></li>
<li>其次焊接矩阵键盘等触点按钮</li>
<li>焊接贴片芯片, 如果不确定的话, 可以先焊接电机驱动芯片, (因为我们没有电机)
最后焊接电源芯片. 其中因为焊头的形状以及焊接的技术, 这里建议不要拖焊.
(容易连焊, 因为功率太小了. )</li>
<li>焊接插座等大体型的元件.</li>
</ol>
<p>
总体上来说, 按照先低后高的顺序焊接.
</p>
</div>
</div>
</div>
<div id="outline-container-org50e6d73" class="outline-3">
<h3 id="org50e6d73"><span class="section-number-3">3.4.</span> 第二个任务: 从闪烁到声音</h3>
<div class="outline-text-3" id="text-3-4">
<p>
据说学单片机一般的操作都是点灯, 闪灯, 然后弃坑.
所以我们也就是不太完美地复刻一下这个学习路径.
</p>
<p>
在 <a href="#org50041ad">计算机的运行</a> 这一部分里面, 介绍到了一个叫做寄存器的东西,
作为一个微型的计算机, 51 单片机的一些引脚就是其寄存器.
观察 <a href="#org8c6e295">电路原理图</a>, 我们可以发现芯片的引脚上有 <code>P20</code> 这样的引脚,
这些引脚就像是我们之间介绍的寄存器. (没错,
这就是 51 单片机的几个寄存器. )
</p>
<p>
那么我们只需要将寄存器拉高即可让该引脚输出高电平.
</p>
<p>
(注: 在 <a href="https://www.keil.com">Keil</a> 和 <a href="https://sdcc.sourceforge.net">sdcc</a> 编译器中的寄存器记法是不同的.
本文档中的写法主要以 sdcc 中的写法为主. )
</p>
<p>
比如我们定义引脚为原理图中的 <code>P20</code>:
</p>
<div class="org-src-container">
<pre class="src src-C">#define THE_PIN P2_0 // in Keil, it should be P2^0
</pre>
</div>
<p>
我们就能够通过使其处于高电平的方式来使该引脚输出高电平:
</p>
<div class="org-src-container">
<pre class="src src-C">THE_PIN = 1;
</pre>
</div>
<p>
(注: 一般来说, 会用宏定义 <code>#define HIGH 1</code>, <code>#define LOW 0</code>,
来让代码更加好懂. 之后的代码会默认做了这样的宏定义. )
</p>
<p>
但是如果我们想要让单片机执行一段代码后等待一段时间呢?
那么我们该如何让单片机等一等呢?
</p>
<p>
一个非常粗暴的方法: 因为执行代码的时候, 并不是一瞬间的事情,
而是需要一段时间的, 于是我们就能够通过这样的方式来等待一段时间:
</p>
<div class="org-src-container">
<pre class="src src-C">void delay(int millisecond) {
while (millisecond--) ;
}
</pre>
</div>
<p>
于是我们的代码就变成如下:
</p>
<div class="org-src-container">
<pre class="src src-C">#include <8052.h> // if you are using Keil, it should be reg52.h or so
#define HIGH 1
#define LOW 0
#define LED P2_0 // replace it with your LED pin number
void main() {
for(;;) { // same as while (1), but just telling you could do so
LED = HIGH;
delay(1000);
LED = LOW;
delay(1000);
}
}
</pre>
</div>
<p>
OK, 如果不出意外的话, 我们应该就能够看到 LED 现在亮起来了.
闪起来了. :P
</p>
<p>
<i>思考题</i>: 众所周知, 声音就是一个波动的信号.
那么该如何生成这样的信号呢?
</p>
</div>
</div>
<div id="outline-container-orgc88c384" class="outline-3">
<h3 id="orgc88c384"><span class="section-number-3">3.5.</span> Others</h3>
<div class="outline-text-3" id="text-3-5">
<p>
可以参考的 51 单片机的一些资料:
</p>
<ul class="org-ul">
<li><a href="https://li-yiyang.github.io/igem/STC89C52RC/">STC89C52RC</a> 我的一个在 macOS 上的 51 配置的教程.
大概覆盖了需要的内容.</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org7d44b61" class="outline-2">
<h2 id="org7d44b61"><span class="section-number-2">4.</span> CAD 基础知识 <a id="org87bd6fd"></a></h2>
<div class="outline-text-2" id="text-4">
<blockquote>
<p>
Computer-aided design (CAD) is the use of computers
(or workstations) to aid in the creation, modification,
analysis, or optimization of a design.
</p>
<p>
form <a href="https://en.wikipedia.org/wiki/Computer-aided_design">Wikipedia</a>
</p>
</blockquote>
<p>
目前的想法是用这个做一个外壳, 然后用切割机来完成.
具体外壳没有想法.
</p>
</div>
<div id="outline-container-org1084d85" class="outline-3">
<h3 id="org1084d85"><span class="section-number-3">4.1.</span> Autodesk</h3>
<div class="outline-text-3" id="text-4-1">
<p>
用教育网可以免费认证 <a href="https://www.autodesk.com.cn/education/edu-software">Autodesk</a>. (一个 <a href="https://zhuanlan.zhihu.com/p/401899796">激活教程</a>)
一个好处就是在 macOS 和 Windows 下都有能用的.
</p>
</div>
<div id="outline-container-org7d76700" class="outline-4">
<h4 id="org7d76700"><span class="section-number-4">4.1.1.</span> AutoCAD</h4>
<div class="outline-text-4" id="text-4-1-1">
<p>
这个比较老, 但是挺强的. 目前没有想法去教, 建议是自学.
</p>
</div>
</div>
<div id="outline-container-orgc7f32b0" class="outline-4">
<h4 id="orgc7f32b0"><span class="section-number-4">4.1.2.</span> Fusion 360</h4>
<div class="outline-text-4" id="text-4-1-2">
<p>
和 Solidworks 对标, 高级的版本是 Inventor Professional,
其中包含了完善的工作流程. 你可以参考 <a href="https://help.autodesk.com/view/fusion360/CHS/">Fusion 360 的官方教程</a>,
其中的内容十分的丰富.
</p>
<p>
我们将会以去年的 iGEM 的外壳作为例子来进行教学,
该部分的目的在于让硬件组能够通过设计一些外部结构来和外界沟通交流.
</p>
</div>
</div>
</div>
<div id="outline-container-orge3806fd" class="outline-3">
<h3 id="orge3806fd"><span class="section-number-3">4.2.</span> Solidworks</h3>
<div class="outline-text-3" id="text-4-2">
<p>
另外一个同学来教.
</p>
</div>
</div>
<div id="outline-container-org683127d" class="outline-3">
<h3 id="org683127d"><span class="section-number-3">4.3.</span> KiCAD <a id="orgd0054b4"></a></h3>
<div class="outline-text-3" id="text-4-3">
<p>
PCB 以及电路原理图的绘制, 开源软件.
不过也可以试试其他的.
</p>
</div>
</div>
</div>
<div id="outline-container-org0f24c33" class="outline-2">
<h2 id="org0f24c33"><span class="section-number-2">5.</span> 比较高级一点的部分 <a id="org62f3146"></a></h2>
<div class="outline-text-2" id="text-5">
</div>
<div id="outline-container-org65bb713" class="outline-3">
<h3 id="org65bb713"><span class="section-number-3">5.1.</span> 两种编程的方式, 以 "狗屁不同废话生成器" 为例 <a id="org2ec6d49"></a></h3>
<div class="outline-text-3" id="text-5-1">
</div>
<div id="outline-container-org4d7e705" class="outline-4">
<h4 id="org4d7e705"><span class="section-number-4">5.1.1.</span> 代码是过程的规则</h4>
<div class="outline-text-4" id="text-5-1-1">
<div class="org-src-container">
<pre class="src src-scheme">#lang scheme
;; Choose one of the list randomly
(define (one-of list)
(if (or (empty? (cdr list))
(= (random 2) 0))
(car list)
(one-of (cdr list))))
;; A sentence is made up with:
;; <object> -- <verb> -- <subject>
(define (sentence)
(list (object) (verb) (subject)))
(define (object)
(one-of '(MAN WOMAN BOY GIRL CAT DOG)))
(define (verb)
(one-of '(IS LIKES EATS BEATS)))
(define (subject)
(one-of '(MAN WOMAN BOY GIRL CAT DOG)))
;; if you would add more support for the generater
;; like add adjective words or other words to
;; beautify the sentence.
;; (define (object)
;; (list (adjective) (noun))
(define (noun)
(one-of '(MAN WOMAN BOY GIRL CAT DOG)))
(define (adjective)
(one-of '(LUCKY SAD HAPPY)))
(define (adverb)
(one-of '(SERIOUSLY CONTIDENTLY)))
(define (object-re)
(list (adjective) (noun)))
(define (verbs)
(one-of '(IS LIKES EATS BEATS)))
(define (subject-re)
(list (adjective) (noun)))
(define (verb-re)
(list (verbs) (adverb)))
(define (sentence-re)
(flatten (list (object-re) (verb-re) (subject-re))))
;; However, you might find that this program is hard to maintain
;; and if you want to add more functions to the program, it would
;; be a difficult thing as you go along.
;; so what if you change it into another way?
</pre>
</div>
</div>
</div>
<div id="outline-container-org4261731" class="outline-4">
<h4 id="org4261731"><span class="section-number-4">5.1.2.</span> 代码是规则的过程</h4>
<div class="outline-text-4" id="text-5-1-2">
<div class="org-src-container">
<pre class="src src-scheme">#lang scheme
;; if you are still familiar with the ebnf we mentioned
;; in the first class, it may not be very hard for you
;; to recall the rule syntax:
;; <sentence> ::= <noun-phrase> <verb-phrase>
;; <noun-phrase> ::= <article> <noun>
;; <verb-phrase> ::= <verb> <noun-phrase>
;; <article> :: "the" | "a"
;; <noun> ::= "man" | "ball" | "woman" | "table"
;; <verb> ::= "hit" | "took" | "saw" | "liked"
(define rules
'((sentence ::= noun-phrase verb-phrase)
(noun-phrase ::= Article Noun)
(verb-phrase ::= Verb noun-phrase)
(Article ::= (the a))
(Noun ::= (man ball woman table))
(Verb ::= (hit took saw liked))))
(define (generate-by rules)
(define (one-of list)
(if (or (empty? (cdr list))
(= (random 2) 0))
(car list)
(one-of (cdr list))))
;; find the tag
;; the tag is the first item of the list:
;; ((tag ::= ...) ...)
(define (get-tag rule tag)
(let ((the-tag (caar rule)))
(if (eq? the-tag tag)
(car rule)
(get-tag (cdr rule) tag))))
;; evaluation by the tag
;; ... ::= tag (list)
;; eval the tag and choose one of the list
;; the rule could be more complex,
;; say that the one choose could also be
;; a tag of the syntax...
(define (eval-tag tag)
(let ((exp (cddr (get-tag rules tag))))
(map
(lambda (item)
(if (list? item)
(one-of item)
(eval-tag item)))
exp)))
(flatten (eval-tag (car (car rules)))))
;; generate-code-process-the-rules.rkt> (generate-by rules)
;; (the man took the ball)
</pre>
</div>
<p>
(注: 我觉得这个名字起得不太好, 看起来就让人有一种白马非马的混乱,
但是这个时候我的脑子也不太好使, 所以就随便起名字吧. )
</p>
</div>
</div>
</div>
<div id="outline-container-orgb123b7a" class="outline-3">
<h3 id="orgb123b7a"><span class="section-number-3">5.2.</span> 一个执行简单指令的虚拟机器 <a id="orgb66c8aa"></a></h3>
</div>
</div>
<div id="outline-container-org9d238a5" class="outline-2">
<h2 id="org9d238a5"><span class="section-number-2">6.</span> 作业等其他 <a id="org679c4d2"></a></h2>
<div class="outline-text-2" id="text-6">
</div>
<div id="outline-container-orgb14ce6f" class="outline-3">
<h3 id="orgb14ce6f"><span class="section-number-3">6.1.</span> 作业提交</h3>
<div class="outline-text-3" id="text-6-1">
<p>
在前面的 <a href="#orgf64e241">2.2</a> 中就已经介绍了如何使用 Git 来进行版本管理.
同样的, 我们将会采取 Git 仓库的方式来进行作业提交,
文件分发的功能.
</p>
<p>
不过请参考 <code>README.md</code> 中的文件组织方式来规范文件放置.
这里引用一下:
</p>
<div class="org-src-container">
<pre class="src src-text">2023 iGEM Hardware New Member Tutorial
|-- classes ;; 存放对应课程资源, 如代码等
| |
| |- class-01 ;; 命名格式: class-number
|
|-- assignment ;; 作业提交和保存
| |
| |- xing-ming ;; 文件夹命名格式: 拼音, 通过 `-` 来分隔姓和名
|
|-- pic ;; 用来存放 tutorial.org 中用到的图片
|
|-- tutorial.org ;; 教程内容
|
|-- README.org ;; README 文档
|
|-- LICENSE ;; MIT 开源协议
</pre>
</div>
<p>
在个人文件夹 (上面的) 中的文件内容按照个人喜好安排.
或者可以参考示例的文件夹亦可.
</p>
<p>
不过这个作业并不会用来作为评价标准就是了.
基本上只是用一个交没交的情况来评价而已.
</p>
</div>
</div>
<div id="outline-container-org6e4d2af" class="outline-3">
<h3 id="org6e4d2af"><span class="section-number-3">6.2.</span> 关于 51 单片机的资料</h3>
<div class="outline-text-3" id="text-6-2">
<p>
因为 51 单片机的资料是购买店家提供的,
并且其中有很多 "学生版" 的资源.
所以还是不要放到这个仓库里面了.
毕竟没有人会把自己购买的 Microsoft Office 开源分发的,
是吧. 除非是在做慈善和 "学生版". 乐.
</p>
<p>
(如果有需要的话, 在课程上我们会进行分发和讲解. )
</p>
</div>
</div>
<div id="outline-container-orgdcb8b12" class="outline-3">
<h3 id="orgdcb8b12"><span class="section-number-3">6.3.</span> 关于疫情之下的课程调整</h3>
<div class="outline-text-3" id="text-6-3">
<p>
因为疫情的原因, 我们随时有可能会因为封校和寝室大摆烂而停止线下培训.
所以我们的上课内容将会从实践为主变成理论为主.
</p>
</div>
</div>
</div>
</div>
<div id="postamble" class="status">
<p class="author">Author: 凉凉</p>
<p class="date">Created: 2022-12-11 Sun 00:26</p>
<p class="validation"><a href="https://validator.w3.org/check?uri=referer">Validate</a></p>
</div>
</body>
</html>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。