本文主要介绍数字电路的实现方法。使用的包为

。(首次出现,标准拼写以示尊敬)主要信息来自于官方文档以及一些TikZ的使用经验。

注意:circuitikz在2020年4月19日发布了1.1.0版本,提供了IEEE标准的逻辑门。对于数字电路绘制帮助极大,但未包含在TeX Live 2020发行版中,请务必记得更新。具体更新方法因发行版和系统不同而有差异。

一、导入Circuitikz

导言区写usepackage{circuitikz}即可。 同时还可以在导言区做一些全局设置,比如:

ctikzset{logic ports=ieee}     %所有逻辑门使用IEEE标准
usetikzlibrary{calc}           %使用TikZ中的计算功能

在文档中需要插入电路图的地方,使用环境circuitikz

begin{circuitikz}%绘制指令
end{circuitikz}

也可以在开始环境时声明一个缩放比例。因为CircuiTikZ提供的逻辑门普遍偏大(个人感觉),所以我会把整个图缩小到0.7倍。

begin{circuitikz}[scale=0.7, transform shape] %必须添加transform shape选项才能正常缩放CircuiTikZ%绘制指令
end{circuitikz}

二、TikZ基础

介绍CircuiTikZ之前,有必要介绍TikZ中的一些基础功能。CircuiTikZ只不过是TikZ的一个包装,所以所有下述的的TikZ指令都可以在CircuiTikZ中使用。

TikZ的图形主要由三部分构成:坐标(coordinate),路径(path),节点(node)。它们大致的关系如下:

  • 节点是位于一个坐标的二维结构;
  • 路径是连接若干个坐标的一维结构。

每一条TikZ的指令大概都长这样:

command[options] <other information>; %一定要有结尾的分号

1、坐标

要声明一个坐标很简单,(x,y)就表示了这个坐标。但是,我们很少单独使用坐标本身,所以它的声明往往是包含在一个路径或节点的声明中的。我们可以先用坐标生成一个辅助的网格:

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);
end{circuitikz}

此处还用了draw指令,但暂时不必在意,只需知道我们生成了一个从(0,0)(5,5),间隔为1的网格即可。注意到这个指令给三个选项赋了值:网格线的间距step,以及两个绘制选项grayvery thin,表示颜色和线的粗细。

声明坐标时,我们实际上做的是把TikZ中的”笔尖“移到了那个位置,从那里开始绘制图形。在笔尖确定的情况下,我们也可以使用相对坐标,根据上一个坐标的偏移量来声明下一个坐标。如果笔尖位于(x,y),那么下一个坐标可以用+(a,b)或者++(a,b)来表示,意为”将笔尖向右移动a,向上移动b“,即(x+a,y+b)。一个加号和两个加号的区别在于两个加号声明的相对坐标会变成新的”笔尖位置“,而一个加号的坐标不会影响”笔尖位置“,笔尖仍然留在(x,y)上——但这只会在声明第二个相对坐标时有区别。下面的代码会生成一样的网格图案:

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid +(5,5); %或者++(5,5)
end{circuitikz}

2、路径

一个路径是连接若干个点的线。最简单的声明路径的方法(也是在CircuiTikZ中少数派得上用场的方法之一):

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);path (1,1) -- (4,5);
end{circuitikz}

在这个指令中,path表示一个路径,--连接了两个坐标(1,1)(4,5)。这样,就在这两个点之间连接了一条线段。 但是,现在只是声明了路径,为了让TikZ真的去画它,还需要显式地给一个draw

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);path[draw] (1,1) -- (4,5);
end{circuitikz}

路径一般的声明方式是:

path[options] <cord 1> <connection> <cord 2>;

除了--,还有|--|两种常用连接选项,前者会先垂直,再水平地从第一个点走到第二个点;后者则相反,先水平,再垂直。我们一直使用的grid,也是一种路径连接方式。

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);path[draw] (1,1) -- (4,5);path[draw,red] (1,1) -| (4,5);path[draw,blue] (1,1) |- (4,5);
end{circuitikz}

一条路径可以连续连接多个坐标,在最后使用-- cycle会使得路径闭合。同时,一直用path[draw]显得太过累赘。我们可以简单地用draw替换。

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (1,1) -- (4,5) -- (3,2) -- cycle;
end{circuitikz}

为什么要说(1,1) -- (4,5) -- (3,2) -- cycle而不说(1,1) -- (4,5) -- (3,2) -- (1,1)?可以放大一下两者生成的图片:

左边是-- cycle,右边是简单的”起点=终点“的路径。可以看到,前者闭合得更加优雅。

3、节点

一个节点是一个微型的TikZ图形。一般我们用它添加文字,也可以用它画一些小图形。节点是一个二维图形,它会按预先设置好的一个锚点来对齐设定的坐标。

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);node at (2,2) {A};    %锚点默认在节点中央
end{circuitikz}

每个节点都自带四个锚点:northsoutheastwest。如果我们在设置节点时,用south锚点锚定坐标(2,2),那么节点的下端就会对齐(2,2),这个节点就会出现在(2,2)上方。

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);node[anchor=south] at (2,2) {A};
end{circuitikz}

当然,south表示“上方”实在太反人类,所以有这样的简写方式:

"above" == "anchor=south", "below" == "anchor=north",

"left" == "anchor=east", "right" == "anchor=west"

节点的一般声明方式是:

node[options] (name) at <cord> {text};

除了用node显式声明节点,还可以在路径定义中为涉及的坐标绑定一个节点:

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (1,1) node[below] {A} -- (4,5);
end{circuitikz}

设置节点时,还可以为它指定名字,并把它作为一个坐标调用。

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (1,1) node[below] (a) {A} -- (4,5);draw (a.north) -- ++(2,1);
end{circuitikz}

要注意的是,要用(a.north)而不只是(a),因为后者所代表的坐标是在"A"的中央,而前者才是之前声明的(1,1)这个点。

利用节点,可以绘制一些我们并不清楚真实坐标的点。但是,如果用相对坐标,就需要我们同时知道横纵坐标的偏移量。如果我们只知道一个偏移量,而知道另一个的绝对坐标,又如何处理?此时需要使用tikz librarycalc 中的let语法:

draw let p<数字> = (节点) in ...

并在其后用x<数字>y<数字>调用这个节点的横纵坐标。比如:

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (1,1) node[below] (a) {A} -- (4,5);draw let p1=(a.north) in (x1,y1) -- (4,y1);
end{circuitikz}

三、CircuiTikZ中的元件

CircuiTikZ中有两类元件:“路径类”和“节点类”。基本所有的数字电路元件都属于节点式,所以只举一例路径类的绘制方法。

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (3,3) to[R] (1,3);      %从(3,3)到(1,3)的路径,内容是一个电阻
end{circuitikz}

所有的路径类元件都使用to[]连接。方括号中的参数除了指定元件(如电阻R和导线short),还可以指定末端的连接方式。下文中会出现一些例子。

对于节点,我们一般先声明节点本身,再从节点的各个锚点向外绘制。

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (3,3) node[and port] (and1) {};    %或者node[and port] (and1) at (3,3) {};
end{circuitikz}

还要注意,每个节点都需要有一个大括号围起的标签,哪怕是空的。接下来,就可以从各个锚点出发,绘制其他导线了。一个逻辑门有out和若干in锚点;比如下面的例子:

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (3,3) node[and port] (and1) {};draw (and1.out) -- ++(1,0);draw (and1.in 1) -| (1,5);draw (and1.in 2) -| (1,1);
end{circuitikz}

可以设置不止两个接口的逻辑门,只需添加选项number inputs,同时也可以通过inner inputs指定有多少个位于逻辑门内部:

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (3,3) node[and port, number inputs=10, inner inputs=2] (and1) {};draw (and1.out) -- ++(1,0);draw (and1.in 1) -| (1,5);draw (and1.in 10) -| (1,1);
end{circuitikz}

对于表示否定的“小圆圈”,如果是输出端,那可以使用nand等表示否定的门;不然,就需要手动在接口处添加一个notcirc节点。注意,inout等锚点在“针脚”的末端,如果要设置在起点,要在前面加b,变成binbout

begin{circuitikz}[scale=0.7, transform shape]draw[step=1,gray,very thin] (0,0) grid (5,5);draw (3,3) node[and port, number inputs=10, inner inputs=2] (and1) {};draw (and1.out) -- ++(1,0);draw (and1.in 1) -| (1,5);draw (and1.in 10) -| (1,1);draw (and1.bin 3) node[notcirc,left] {};
end{circuitikz}

以下是所有常用逻辑门的节点名:

  • 与门:and port
  • 或门:or port
  • 非门:not port
  • 异或门:xor port
  • 同或门:xnor port
  • 与非门:nand port
  • 或非门:nor port
  • 施密特电路:schmitt port
  • 反施密特电路:inv schmitt port
  • 缓冲门:buffer port

更多的关于电路绘制的技术细节可以见CircuiTikZ官方文档:

CircuiTikZ文档​mirrors.sjtug.sjtu.edu.cn

四、实例

以一位比较器为例:

首先,确定各个门电路的位置。

begin{circuitikz}[scale=0.7, transform shape]draw (3,5.5) node[and port, anchor=out] (and1) {};draw (3.5,4) node[xnor port, anchor=out] (xor1) {};draw (3,2.5) node[and port, anchor=out] (and2) {};
end{circuitikz}

坐标是随意设的,只要符合审美即可。使用anchor=out也只是因为这样容易设置坐标。

接下来,要为两个与门添加圆圈。

begin{circuitikz}[scale=0.7, transform shape]draw (3,5.5) node[and port, anchor=out] (and1) {};draw (3.5,4) node[xnor port, anchor=out] (xor1) {};draw (3,2.5) node[and port, anchor=out] (and2) {};draw (and1.bin 1) node[notcirc,left] {};draw (and2.bin 1) node[notcirc,left] {};
end{circuitikz}

然后是最繁琐的接线部分。可以先接连接两个与门的线:

begin{circuitikz}[scale=0.7, transform shape]draw (3,5.5) node[and port, anchor=out] (and1) {};draw (3.5,4) node[xnor port, anchor=out] (xor1) {};draw (3,2.5) node[and port, anchor=out] (and2) {};draw (and1.bin 1) node[notcirc,left] {};draw (and2.bin 1) node[notcirc,left] {};draw (and2.in 1) |- (and1.in 2);draw (and1.in 1) -- ++(-0.5,0) |- (and2.in 2);
end{circuitikz}

对于同或门向后的两根线,我们知道与它们横纵坐标相同的几个节点,但不知道它们的具体坐标,因此,我们将要使用let语法。

begin{circuitikz}[scale=0.7, transform shape]draw (3,5.5) node[and port, anchor=out] (and1) {};draw (3.5,4) node[xnor port, anchor=out] (xor1) {};draw (3,2.5) node[and port, anchor=out] (and2) {};draw (and1.bin 1) node[notcirc,left] {};draw (and2.bin 1) node[notcirc,left] {};draw (and2.in 1) |- (and1.in 2);draw (and1.in 1) -- ++(-0.5,0) |- (and2.in 2);draw let p1=(xor1.in 1),p2=(and1.in 1) in (x1,y1) to[short,-*] (x2,y1) node (a) {};draw let p1=(xor1.in 2),p2=(and2.in 1) in (x1,y1) to[short,-*] ({x2-0.5cm},y1) node (b) {};
end{circuitikz}

其中还用了一个小语法:在大括号之间的内容会被TikZ的数学引擎计算。所以{x2-0.5cm}有类似(x2,y2)+(-0.5,0)的效果。to中包含的-*选项会在线路末端生成一个小黑圆表示连接点。这是CircuiTikZ提供的连接方式,而不是TikZ的,所以只有在用to时才能使用,如果用--则不行。

最后,延长导线,添加输入输出标签。对于文字标签,有一个注意:由于在最前面设置了scale=0.7,会导致文字偏小;所以我们要手动再把文字比例调回来。(这是CircuiTikZ中一个不合理的地方,TikZ的缩放不影响文字) 调整方法是再为文字节点添加scale={1/0.7}

begin{circuitikz}[scale=0.7, transform shape]draw (3,5.5) node[and port, anchor=out] (and1) {};draw (3.5,4) node[xnor port, anchor=out] (xor1) {};draw (3,2.5) node[and port, anchor=out] (and2) {};draw (and1.bin 1) node[notcirc,left] {};draw (and2.bin 1) node[notcirc,left] {};draw (and2.in 1) |- (and1.in 2);draw (and1.in 1) -- ++(-0.5,0) |- (and2.in 2);draw let p1=(xor1.in 1),p2=(and1.in 1) in (x1,y1) to[short,-*] (x2,y1) node (a) {};draw let p1=(xor1.in 2),p2=(and2.in 1) in (x1,y1) to[short,-*] ({x2-0.5cm},y1) node (b) {};draw let p1=(a) in (x1,y1) -- (-1,y1) node[left,scale={1/0.7}] {$A$};draw let p1=(b) in (x1,y1) -- (-1,y1) node[left,scale={1/0.7}] {$B$};draw let p1=(and1.out) in (x1,y1) -- (4,y1) node[right,scale={1/0.7}] {$Y_{A>B}$};draw let p1=(xor1.out) in (x1,y1) -- (4,y1) node[right,scale={1/0.7}] {$Y_{A=B}$};draw let p1=(and2.out) in (x1,y1) -- (4,y1) node[right,scale={1/0.7}] {$Y_{A<B}$};
end{circuitikz}

就这样,我们完成了此图的绘制。

echart 坐标数字间隔_用LaTeX优雅地绘制数字电路相关推荐

  1. 数字信号处理_ 第2个编程实例

    配套的讲解视频详见数字信号处理_第2个Matlab编程实例_哔哩哔哩_bilibili 方波信号合成与分解的实例代码如下: %% //数字信号第2个Matlab程序:方波信号的分解 %% //作者:g ...

  2. tcp 接收端优雅的写法_如何更优雅地接收设计反馈

    tcp 接收端优雅的写法 重点 (Top highlight) It's rare to meet a designer that doesn't take pride in their work. ...

  3. 数字信号处理_第1个编程实例

    最近准备开一个关于<数字信号处理>的课程,课程的视频在b站分享(数字信号处理_引言_哔哩哔哩_bilibili),大概会保持每周一更的频率,希望对<数字信号处理>感兴趣的同学多 ...

  4. 正则表达式 匹配中文,英文字母和数字及_的写法!同时控制长度

    匹配中文:[\u4e00-\u9fa5] 英文字母:[a-zA-Z] 数字:[0-9] 匹配中文,英文字母和数字及_:  ^[\u4e00-\u9fa5_a-zA-Z0-9]+$ 同时判断输入长度: ...

  5. 正则表达式 匹配中文,英文字母和数字及_长度详解

    http://www.juapk.com/thread-2472-1-1.html 匹配中文:[\u4e00-\u9fa5] 英文字母:[a-zA-Z]  数字:[0-9] 匹配中文,英文字母和数字及 ...

  6. 【转】正则表达式 匹配中文,英文字母和数字及_的写法!同时控制长度

    [转]正则表达式 匹配中文,英文字母和数字及_的写法!同时控制长度 using System.Text.RegularExpressions; 匹配中文:[\u4e00-\u9fa5] 英文字母:[a ...

  7. 英文字符和数字间隔突然变大

    输入法中英文字符和数字间隔突然变大 看的很变扭,还很常见,但自己经常忘了处理办法 这里记录下,只需要shift+空格键就可以处理了

  8. numpy快速生成图像各点坐标并间隔取点

    numpy快速生成图像各点坐标并间隔取点 x = np.linspace(0,train_data.lie,60,endpoint=False) # 加密y = np.linspace(0,train ...

  9. 【C#】正则表达式匹配中文,英文字母和数字及_写法!并控制长度

    using System.Text.RegularExpressions; 匹配中文:[\u4e00-\u9fa5] 英文字母:[a-zA-Z] 数字:[0-9] 匹配中文,英文字母和数字及_: ^[ ...

最新文章

  1. 最新县及县以上行政区划代码(截止2010年12月31日)
  2. 2021年春季学期-信号与系统-第十四次作业参考答案-第四小题参考答案
  3. Android之GSON解析JSON
  4. 【Android 安装包优化】WebP 图片格式兼容与性能 ( Android 中的 WebP 图片格式兼容问题 | Android 中的 WebP 图片格式性能 )
  5. CAN总线基础(三)
  6. Linux MySQL Connector/C++ 编程实例
  7. python构建区块链_用python构建区块链(1)---基本结构
  8. BugKuCTF WEB 变量1
  9. python检测文件夹中新增文件_python检测文件夹变化,并拷贝有更新的文件到对应目录的方法...
  10. List(Map(String, Object))转为Fastjson JSONArray
  11. [Java]Thinking in Java 练习2.2
  12. Flex4 初始化过慢解决方法
  13. 如何revert一个merged branch上所有的改动
  14. php程序员述职材料_php程序员述职报告(精选多篇)
  15. 时间排序处理在微信小程序和真机调试没问题,发布体验版后未执行
  16. 在线java面试题库_Java笔试题库
  17. 前端vue生成二维码,再合成海报图片
  18. Adobe Dreamweaver CS6快捷键使用
  19. Gary Gygax的倒台,密切关注Linux端口等
  20. 爷爷:啥是佩奇?佩奇:Python 10 秒做出来,你看像不像?

热门文章

  1. 不要伤害指针(3)--指针和结构类型的关系
  2. 扩展语法检查(SLIN检查)
  3. 使用timer定时器,防止事件重入
  4. 使用jqprint插件完成页面打印
  5. tomcat的缺少tcnative-1.dll的解决
  6. 新闻系统(3)内容保护的探索
  7. Django_前端显示Matplotlib画的图(亲测)
  8. kafka(一)-为什么使用kafka
  9. 在Linux环境下mysql的root密码忘记解决方法(三种)
  10. TP返回原生SQL语句:fetchSql