-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
906 lines (438 loc) · 800 KB
/
search.xml
File metadata and controls
906 lines (438 loc) · 800 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Hello, World!</title>
<link href="/2026/03/15/hello-world/"/>
<url>/2026/03/15/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo new <span class="token string">"My New Post"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo server<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo generate<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo deploy<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
</entry>
<entry>
<title>SolidWorks 基础操作笔记</title>
<link href="/2026/01/26/2026-01-26-solidworks-basic-notes/"/>
<url>/2026/01/26/2026-01-26-solidworks-basic-notes/</url>
<content type="html"><![CDATA[<h2 id="基础操作"><a href="#基础操作" class="headerlink" title="基础操作"></a>基础操作</h2><p>草图处绘制草图,智能尺寸标注并编辑尺寸</p><p>特征处拉伸凸台/实体</p><p>按住滚轮拖动<strong>改变视角</strong></p><p>按 <code>F</code> 可以<strong>找回图形</strong>,如果被拖得找不到了</p><p>按住 ctrl,按住滚轮拖动可以<strong>移动</strong></p><p>用小图标横向排列那里的视图定向可以定向在某个平面看</p><p><img src="/assets/blog/25_26_Autumn/SW/image-20260126213811075.png" alt="image-20260126213811075"></p><p>或者选中要正视的平面,点视图定向——正视于</p><p>每一步操作都会排列在左边的设计树</p><p><code>esc</code> 取消标注尺寸</p><p><code>ctrl</code>+<code>tab</code> 在打开的文件之间切换</p><h2 id="草图"><a href="#草图" class="headerlink" title="草图"></a>草图</h2><h3 id="草图几何关系"><a href="#草图几何关系" class="headerlink" title="草图几何关系"></a>草图几何关系</h3><ol><li>水平</li><li>垂直</li><li>重合</li><li>…</li></ol><p>把一个线变成构造线可以关于它对称</p><h3 id="编辑草图"><a href="#编辑草图" class="headerlink" title="编辑草图"></a>编辑草图</h3><p>裁剪实体,常用:</p><ol><li>强力裁剪:按住 shift 还可以延长</li><li>裁剪到最近端</li></ol><p>最常用第一个强力裁剪</p><h3 id="转换实体引用"><a href="#转换实体引用" class="headerlink" title="转换实体引用"></a>转换实体引用</h3><p>可以把几何里面已有的线段提取到当前绘制的草图</p><h3 id="等距实体"><a href="#等距实体" class="headerlink" title="等距实体"></a>等距实体</h3><p>移动固定间距</p><h3 id="草图绘制思路"><a href="#草图绘制思路" class="headerlink" title="草图绘制思路"></a>草图绘制思路</h3><p>先把所有的线段都绘制出来,再标注尺寸使其完全定义。过定义的尺寸可以改成从动尺寸</p><p>要是报错太多可以都删了重来</p><h3 id="草图镜像"><a href="#草图镜像" class="headerlink" title="草图镜像"></a>草图镜像</h3><p>被激活的选项框会显示淡蓝色,可以通过 Tab 键切换选项框</p><h3 id="草图文字"><a href="#草图文字" class="headerlink" title="草图文字"></a>草图文字</h3><p><img src="/assets/blog/25_26_Autumn/SW/image-20260129143858593.png" alt="image-20260129143858593"></p><p>第二行最右边字母 A 是草图文字功能</p><h3 id="草图图片"><a href="#草图图片" class="headerlink" title="草图图片"></a>草图图片</h3><p>进入草图编辑,在最上边工具</p><p><img src="/assets/blog/25_26_Autumn/SW/image-20260129144211186.png" alt="image-20260129144211186"></p><p>草图工具——草图图片——选择图片——调整透明度、大小.</p><p>但是这样只能照着描,还有一个方法。先用 R2V 软件矢量化</p><p>矢量化步骤:</p><ol><li>图像——转换——24 位 RGB-灰度</li><li>矢量——自动矢量化</li><li>文件——输出矢量——选择 <code>.dxf</code> 格式</li></ol><p>在 SolidWorks 先选中要加图形的面,然后插入——DXF/DWG,不勾选导入为参考,然后点设计树里面新导入那个草图,调整大小和位置即可。</p><blockquote><p>[!NOTE]</p><p>有时候图会非常大,导致看不到之前的零件了,但是其实都是在一个文件里,缩小就看到了</p></blockquote><h3 id="3D-草图"><a href="#3D-草图" class="headerlink" title="3D 草图"></a>3D 草图</h3><p>无需基准面即可绘制草图</p><h3 id="草图注意"><a href="#草图注意" class="headerlink" title="草图注意"></a>草图注意</h3><ol><li>无法在曲面新建草图,但是可以新建与曲面相切的基准面,在其上绘制草图</li><li>构造线不能为特征提供成型参考</li></ol><h2 id="特征"><a href="#特征" class="headerlink" title="特征"></a>特征</h2><h3 id="拉伸凸台基体"><a href="#拉伸凸台基体" class="headerlink" title="拉伸凸台基体"></a>拉伸凸台基体</h3><p>距离草图一段距离开始拉伸:修改从那里,左边那两个箭头可以改变方向</p><p><img src="/assets/blog/25_26_Autumn/SW/image-20260127222636941.png" alt="image-20260127222636941"></p><h3 id="旋转凸台基体"><a href="#旋转凸台基体" class="headerlink" title="旋转凸台基体"></a>旋转凸台基体</h3><p>先画好草图和构造线,标注好尺寸,然后这个命令会自动识别构造线和旋转对象</p><p>也可以手动选择旋转轴</p><h3 id="拉伸切除"><a href="#拉伸切除" class="headerlink" title="拉伸切除"></a>拉伸切除</h3><p>用草图去切割几何体</p><h3 id="参考几何体"><a href="#参考几何体" class="headerlink" title="参考几何体"></a>参考几何体</h3><p>可以绘制基准面,绘制的时候选择参考面,更改距离即可。</p><p>设计树中,蓝色基准面表示显示;灰色基准面表示不显示,可以通过左键找到眼睛图标更改</p><p>如何把基准面精准地放到两个面中心,如果不知道两个面之间的距离:</p><p>把两个面分别作为第一参考和第二参考,然后点两侧对称</p><p><img src="/assets/blog/25_26_Autumn/SW/image-20260128134845314.png" alt="image-20260128134845314"></p><h3 id="设计树"><a href="#设计树" class="headerlink" title="设计树"></a>设计树</h3><p>左键某个步骤会出现编辑该特征的选项,特征对应的草图也是同理。编辑这些特征会影响之后对该特征的引用</p><blockquote><p>比如修改已经被拉伸过的草图,拉伸后的形状也会改变</p></blockquote><h3 id="扫描"><a href="#扫描" class="headerlink" title="扫描"></a>扫描</h3><p>将草图沿着另一草图为路径生成实体</p><p>分为草图轮廓扫描成型和圆形轮廓扫描成型</p><p>特征里面的曲线可以生成螺旋线,配合圆形轮廓扫描成型可以生成弹簧</p><p><img src="/assets/blog/25_26_Autumn/SW/image-20260127225433381.png" alt="image-20260127225433381"></p><h3 id="添加螺纹"><a href="#添加螺纹" class="headerlink" title="添加螺纹"></a>添加螺纹</h3><p>插入——注解——装饰螺纹线</p><h3 id="旋转切除"><a href="#旋转切除" class="headerlink" title="旋转切除"></a>旋转切除</h3><p>与旋转凸台基体相似,注意在合适的位置建立基准面</p><h3 id="异型孔向导"><a href="#异型孔向导" class="headerlink" title="异型孔向导"></a>异型孔向导</h3><p>标准要选 GB,类型选内六角圆柱头螺钉</p><p>打螺纹孔:</p><p>孔类型是直螺纹孔 (第二行第一个)——标准是 GB——类型是底部螺纹孔</p><h3 id="线性阵列"><a href="#线性阵列" class="headerlink" title="线性阵列"></a>线性阵列</h3><p>与草图中的类似,可以把不需要的添加到可跳过的阵列里面</p><h3 id="圆周阵列"><a href="#圆周阵列" class="headerlink" title="圆周阵列"></a>圆周阵列</h3><p>注意点击等间距,角度改成 360°,才能排一圈</p><h3 id="修改模型的尺寸"><a href="#修改模型的尺寸" class="headerlink" title="修改模型的尺寸"></a>修改模型的尺寸</h3><p>找到要修改的特征对应的草图,打开草图的显示,双击草图上要修改的尺寸修改为需要的,然后</p><p><code>ctrl</code>+<code>B</code> 刷新模型即可</p><h3 id="报错应对方法"><a href="#报错应对方法" class="headerlink" title="报错应对方法"></a>报错应对方法</h3><p>哪个特征报错,就在设计树编辑哪个特征</p><h3 id="放样"><a href="#放样" class="headerlink" title="放样"></a>放样</h3><p>将两个或以上轮廓连接在一起</p><p>开始/结束约束都选成与面相切就可以平滑过渡</p><p>引导线可以约束放样路径,绘制引导线可以用 3D 草图</p><h2 id="评估"><a href="#评估" class="headerlink" title="评估"></a>评估</h2><p>设计树里面右键材料可以更改材料,POM 和 3D 打印材料一样</p><h3 id="质量属性"><a href="#质量属性" class="headerlink" title="质量属性"></a>质量属性</h3><p>可以观看质量等</p><pre class="line-numbers language-none"><code class="language-none">手机支架 的质量属性 配置: 默认 坐标系: -- 默认 --密度 = 0.000 千克 / 立方毫米质量 = 0.064 千克体积 = 46368.766 立方毫米表面积 = 24286.771 平方毫米重心 : ( 毫米 )X = -35.237Y = 25.870Z = 0.000<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="测量"><a href="#测量" class="headerlink" title="测量"></a>测量</h3><p>可以测量距离</p><h2 id="装配体"><a href="#装配体" class="headerlink" title="装配体"></a>装配体</h2><p>将若干个零件或者子装配体通过配合组合在一起的文件</p><p>设计树里面是零件、子装配体、配合关系;零件和子装配体也可以点开</p><h3 id="进入装配体界面"><a href="#进入装配体界面" class="headerlink" title="进入装配体界面"></a>进入装配体界面</h3><p>新建——装配体</p><p>按住 <code>ctrl</code> 来拖动零件可以复制,从设计树拖动或者画布拖动都可以</p><blockquote><p>[!NOTE]</p><p>第一个固定零件的坐标系最好与装配体的坐标系相同,在插入零件的窗口直接点击确定即可做到这一点</p></blockquote><blockquote><p>[!WARNING]</p><p>在装配体中编辑最好点击零件,然后选择编辑零件,而不是用装配体特征菜单中的操作来编辑</p></blockquote><h3 id="基本操作"><a href="#基本操作" class="headerlink" title="基本操作"></a>基本操作</h3><p>选中一个零件,按住鼠标右键拖动可以进行旋转</p><p><code>ctrl</code>+<code>tab</code> 可以在打开的文件里面切换</p><p>在设计树中右键——编辑零件来对相应的零件进行编辑</p><p>设计树中右键零件可以改变零件的固定/浮动</p><p><code>tab</code> 可以隐藏选中的零件</p><h3 id="配合"><a href="#配合" class="headerlink" title="配合"></a>配合</h3><h4 id="标准配合"><a href="#标准配合" class="headerlink" title="标准配合"></a>标准配合</h4><h4 id="高级配合"><a href="#高级配合" class="headerlink" title="高级配合"></a>高级配合</h4><h5 id="宽度"><a href="#宽度" class="headerlink" title="宽度"></a>宽度</h5><p>配合类型是宽度的高级配合是常用的配合,可以让某个实体位于另一个实体的中心:在宽度选择选要移动的实体的两个面;在薄片选择选参考的实体的两个面即可</p><h5 id="距离"><a href="#距离" class="headerlink" title="距离"></a>距离</h5><p>选中两个面,距离配合可以控制这两个面距离的最大和最小值</p>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> Solidworks </tag>
<tag> CAD </tag>
<tag> 3D-modeling </tag>
</tags>
</entry>
<entry>
<title>智能物联网(AIot)系统设计大作业实验报告</title>
<link href="/2025/11/14/2025-11-14-aiot-project-report/"/>
<url>/2025/11/14/2025-11-14-aiot-project-report/</url>
<content type="html"><![CDATA[<h2 id="团队成员分工与贡献"><a href="#团队成员分工与贡献" class="headerlink" title="团队成员分工与贡献"></a>团队成员分工与贡献</h2><p>本项目由团队成员协作完成,具体分工与贡献说明如下:</p><table><thead><tr><th align="left">成员</th><th align="left">主要负责模块</th><th align="left">具体工作内容</th><th align="left">贡献度</th></tr></thead><tbody><tr><td align="left"><strong>苗翔宇</strong></td><td align="left"><strong>ESP32 物联网终端 & 云平台</strong></td><td align="left">1. 负责整体硬件方案设计、PCB绘制与制作。<br>2. 负责ESP32主控固件的全部功能开发,包括时钟、传感器数据采集、外设驱动等。<br>3. 负责华为云IoTDA平台的对接与调试。<br>4. 负责配套微信小程序的设计与开发,实现完整的云端控制链路。</td><td align="left">60%</td></tr><tr><td align="left"><strong>何辰阳</strong></td><td align="left"><strong>香橙派边缘视觉系统</strong></td><td align="left">1. 负责香橙派的系统环境配置与开发环境搭建。<br>2. 负责实现基于YOLOv5的实时目标检测功能。<br>3. 负责视觉模块与系统的集成、调试及最终效果呈现。</td><td align="left">40%</td></tr></tbody></table><hr><h2 id="课题背景"><a href="#课题背景" class="headerlink" title="课题背景"></a>课题背景</h2><p>在信息化与智能化高度发展的时代背景下,人们的工作与生活节奏不断加快,长期处于高强度、快节奏的工作环境中。随之而来的注意力管理、心理健康等问题逐渐受到大众的关注。现代社会中,办公与学习场景高度依赖电子设备,但传统桌面工具功能单一、缺乏情绪关怀,难以在高效工作的同时提供舒缓与陪伴作用。随着物联网、人工智能与云计算技术的成熟,桌面智能助手的出现为这一现实需求提供了可行的解决方案。此类设备不仅能集成时间管理、信息提醒、环境监测等实用功能,还可多模态交互方式,营造舒适的办公氛围,帮助用户缓解心理压力、提高专注度与情绪稳定性。研究与开发面向多场景应用的桌面智能助手,不仅具有明显的实用意义,也契合了当前人们追求高效、健康、舒适生活方式的社会需求。</p><hr><h2 id="产品目的"><a href="#产品目的" class="headerlink" title="产品目的"></a>产品目的</h2><p>探索桌面智能助手在信息服务与人机交互领域的创新实现方式。项目的核心目标是构建一个集时间管理、信息获取、环境感知与情绪调节于一体的智能化系统,以满足现代人对高效工作、健康生活和便捷交互的综合需求。通过硬件与云端平台的协同运行,实现数据的实时传输与多功能集成,使桌面设备不仅具备信息展示与提醒功能,还能通过多样化的交互方式提升用户体验与使用乐趣。</p><hr><h2 id="实物展示"><a href="#实物展示" class="headerlink" title="实物展示"></a>实物展示</h2><p>整体</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%95%B4%E4%BD%93.png" alt="整体"></p><p>正面</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%AD%A3%E9%9D%A2.jpg" alt="正面"></p><p>背面</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%83%8C%E9%9D%A2.jpg" alt="背面"></p><hr><h2 id="功能概述"><a href="#功能概述" class="headerlink" title="功能概述"></a>功能概述</h2><h3 id="环境感知"><a href="#环境感知" class="headerlink" title="环境感知"></a>环境感知</h3><p>通过光敏传感器、温度传感器对环境的温度等信息进行感知,并可以通过华为云物联网平台在微信小程序上进行查看。该功能旨在使使用者实现更为便利的环境相关信息获取。通过相机获取实时图像,在PC端显示实时视频。并通过yolo v5实现对环境中物体的实时预测。该功能旨在将人工智能手段介入嵌入式物联网系统,使使用者可以远程查看办公桌面情况。Yolo预测功能给正在办公的使用者提供情绪价值并可以在出现危险物品的特殊情况下做出预警。(预警功能因时间原因与python代码的华为云连接等问题最终未能实现,希望在未来得到完善)</p><h3 id="舒缓心情"><a href="#舒缓心情" class="headerlink" title="舒缓心情"></a>舒缓心情</h3><p>该产品考虑了现代办公人群在长期高压环境下的心理调节需求。桌面助手通过音乐播放、动态灯光、趣味动画以及简易游戏等功能,能够在用户繁忙的工作间隙提供轻松愉悦的体验。例如,音乐功能可通过柔和旋律和可视化频谱动画帮助使用者舒缓情绪、缓解紧张;LED灯光系统可根据用户喜好调整颜色与亮度,营造舒适的桌面氛围;小游戏模块则为用户提供了短暂放松与情绪释放的途径。</p><h3 id="助力办公"><a href="#助力办公" class="headerlink" title="助力办公"></a>助力办公</h3><p>本项目在设计中充分考虑了办公与学习场景下的效率需求,通过多种智能化功能的集成实现了对时间管理与信息获取的优化。设备具备时钟、番茄钟、GPU&CPU检测等多种时间管理功能,能够帮助使用者科学规划工作、检测电脑,提升专注度与自律性;同时,通过网络接口获取新闻、英语词汇等实时信息,使用户在不依赖手机或电脑的情况下即可获得所需数据,减少工作中的信息切换与干扰。多项功能的有机结合,使该智能桌面助手不仅是信息展示终端,更是工作流程中的高效辅助工具,能有效提升任务执行效率与环境感知能力,为现代化智能办公提供了新的解决思路。</p><h3 id="具体介绍"><a href="#具体介绍" class="headerlink" title="具体介绍"></a>具体介绍</h3><h4 id="视觉部分"><a href="#视觉部分" class="headerlink" title="视觉部分"></a>视觉部分</h4><p><img src="D:\files\25_26_Autumn\AIoT_final\img\视觉.png" alt="视觉"></p><p>实时显示摄像头画面,并通过Yolo v5预测画面内的物品。旨在使使用者可以远程监控桌面情况。Yolo的预测结果可以给正在办公的使用者提供情绪价值,并可以在出现危险物品的特殊情况下做出预警。(预警功能的设想是在预测的同时实时对识别的物体进行记录,并上传到华为云物联网平台,如出现家中有猫或狗上桌等情况,可能破坏桌面电子设备,则由华为云平台通过微信小程序进行预警,但由于时间原因最终未能实现)</p><h4 id="ESP32部分"><a href="#ESP32部分" class="headerlink" title="ESP32部分"></a>ESP32部分</h4><ol><li><a href="#%E6%97%B6%E9%92%9F">时钟</a></li><li><a href="#%E9%9F%B3%E4%B9%90">音乐</a></li><li><a href="#%E4%BA%92%E8%81%94%E7%BD%91">互联网</a></li><li><a href="#%E5%A4%AA%E7%A9%BA">太空</a></li><li><a href="#%E9%97%B9%E9%92%9F">闹钟</a></li><li><a href="#%E5%80%92%E8%AE%A1%E6%97%B6">倒计时</a></li><li><a href="#%E7%95%AA%E8%8C%84%E9%92%9F">番茄钟</a></li><li><a href="#%E7%A7%92%E8%A1%A8">秒表</a></li><li><a href="#%E7%AE%80%E6%B4%81%E9%9F%B3%E4%B9%90">简洁音乐</a></li><li><a href="#%E7%94%B5%E8%84%91%E6%80%A7%E8%83%BD%E7%9B%91%E6%B5%8B">电脑性能监测</a></li><li><a href="#%E6%B8%A9%E5%BA%A6%E7%9B%91%E6%B5%8B">温度监测</a></li><li><a href="#%E5%8A%A8%E7%94%BB">动画</a></li><li><a href="#%E6%B8%B8%E6%88%8F">游戏</a></li><li><a href="#%E5%85%89%E7%85%A7%E7%9B%91%E6%B5%8B">光照监测</a></li><li><a href="#led%E8%AE%BE%E7%BD%AE">LED设置</a></li></ol><p>下面逐一进行介绍。</p><h5 id="时钟"><a href="#时钟" class="headerlink" title="时钟"></a>时钟</h5><p>通过NTP协议连接阿里云NTP服务器<code>ntp.aliyun.com</code>同步网络时间,并由RTC模块维持走时。此外,设备能通过高德地图API获取实时天气信息,并提供了十七个不同风格的表盘可供用户选择。</p><h5 id="音乐"><a href="#音乐" class="headerlink" title="音乐"></a>音乐</h5><p>使用板载无源蜂鸣器播放预置的单音轨音乐。播放界面可显示时长进度条、当前音符的频率和时长,以及频率随时间变化的动态图像。设备背面的彩色LED会同步呈现彩虹灯效。</p><p>用户不仅可通过旋转编码器进行本地操作(如暂停/继续、切换播放模式),还可以通过微信小程序远程点播歌曲。小程序通过华为云平台下发指令,允许用户从88首内置乐曲中选择任意一首进行播放,并可指定播放时屏幕的UI模式(全功能/简洁/无UI)。设备内置的播放模式包括单曲循环、列表循环和随机播放。设备内置88首乐曲,列表如下:</p><table><thead><tr><th>ID</th><th>歌名</th><th>演唱者 / 作曲者</th></tr></thead><tbody><tr><td>0</td><td>爱你</td><td>王心凌</td></tr><tr><td>1</td><td>爱情讯息</td><td>陈奕迅</td></tr><tr><td>2</td><td>啊朋友再见</td><td>无</td></tr><tr><td>3</td><td>把回忆拼好给你</td><td>王贰浪</td></tr><tr><td>4</td><td>保卫黄河</td><td>冼星海</td></tr><tr><td>5</td><td>不得不爱</td><td>潘玮柏</td></tr><tr><td>6</td><td>不再犹豫</td><td>Beyond</td></tr><tr><td>7</td><td>猜不透</td><td>丁当</td></tr><tr><td>8</td><td>卡萨布兰卡</td><td>Bertie Higgins</td></tr><tr><td>9</td><td>测谎</td><td>薛之谦</td></tr><tr><td>10</td><td>成都</td><td>赵雷</td></tr><tr><td>11</td><td>虫儿飞</td><td>黑鸭子</td></tr><tr><td>12</td><td>Counting Stars</td><td>OneRepublic</td></tr><tr><td>13</td><td>倒数</td><td>蔡依林</td></tr><tr><td>14</td><td>春娇与志明</td><td>五月天</td></tr><tr><td>15</td><td>大海</td><td>张雨生</td></tr><tr><td>16</td><td>稻香</td><td>周杰伦</td></tr><tr><td>17</td><td>东方红</td><td>李有源</td></tr><tr><td>18</td><td>东方之珠</td><td>罗大佑</td></tr><tr><td>19</td><td>东西</td><td>薛之谦</td></tr><tr><td>20</td><td>梦中的婚礼</td><td>Richard Clayderman</td></tr><tr><td>21</td><td>多远都要在一起</td><td>邓紫棋</td></tr><tr><td>22</td><td>反方向的钟</td><td>周杰伦</td></tr><tr><td>23</td><td>Fate</td><td>Ludwig van Beethoven</td></tr><tr><td>24</td><td>Five Hundred Miles</td><td>The Innocence Mission</td></tr><tr><td>25</td><td>For Elise</td><td>Ludwig van Beethoven</td></tr><tr><td>26</td><td>For Ya</td><td>来自网络</td></tr><tr><td>27</td><td>富士山下</td><td>陈奕迅</td></tr><tr><td>28</td><td>告白气球</td><td>周杰伦</td></tr><tr><td>29</td><td>刚好遇见你</td><td>李玉刚</td></tr><tr><td>30</td><td>歌唱祖国</td><td>王莘</td></tr><tr><td>31</td><td>光辉岁月</td><td>Beyond</td></tr><tr><td>32</td><td>过火</td><td>张信哲</td></tr><tr><td>33</td><td>海阔天空</td><td>Beyond</td></tr><tr><td>34</td><td>荷塘月色</td><td>凤凰传奇</td></tr><tr><td>35</td><td>红豆</td><td>王菲</td></tr><tr><td>36</td><td>红色高跟鞋</td><td>蔡健雅</td></tr><tr><td>37</td><td>后来</td><td>刘若英</td></tr><tr><td>38</td><td>花海</td><td>周杰伦</td></tr><tr><td>39</td><td>江南</td><td>林俊杰</td></tr><tr><td>40</td><td>开始懂了</td><td>孙燕姿</td></tr><tr><td>41</td><td>可不可以</td><td>张紫豪</td></tr><tr><td>42</td><td>兰亭序</td><td>周杰伦</td></tr><tr><td>43</td><td>梁祝</td><td>何占豪 陈钢</td></tr><tr><td>44</td><td>绿色</td><td>陈雪凝</td></tr><tr><td>45</td><td>明天会更好</td><td>群星</td></tr><tr><td>46</td><td>南方姑娘</td><td>赵雷</td></tr><tr><td>47</td><td>你若三冬</td><td>无</td></tr><tr><td>48</td><td>怒放的生命</td><td>汪峰</td></tr><tr><td>49</td><td>Ode to Joy</td><td>Ludwig van Beethoven</td></tr><tr><td>50</td><td>朋友</td><td>周华健</td></tr><tr><td>51</td><td>琵琶行</td><td>奇然</td></tr><tr><td>52</td><td>平凡之路</td><td>朴树</td></tr><tr><td>53</td><td>起风了</td><td>买辣椒也用券</td></tr><tr><td>54</td><td>七里香</td><td>周杰伦</td></tr><tr><td>55</td><td>青花瓷</td><td>周杰伦</td></tr><tr><td>56</td><td>晴天</td><td>周杰伦</td></tr><tr><td>57</td><td>Radetzky Marsch</td><td>Johann Strauss I</td></tr><tr><td>58</td><td>若水三千</td><td>无</td></tr><tr><td>59</td><td>山丘</td><td>李宗盛</td></tr><tr><td>60</td><td>Shape Of You</td><td>Ed Sheeran</td></tr><tr><td>61</td><td>十年</td><td>陈奕迅</td></tr><tr><td>62</td><td>送别</td><td>李叔同</td></tr><tr><td>63</td><td>素颜</td><td>许嵩</td></tr><tr><td>64</td><td>Sugar</td><td>Maroon 5</td></tr><tr><td>65</td><td>Take Me Hand</td><td>无</td></tr><tr><td>66</td><td>天黑黑</td><td>孙燕姿</td></tr><tr><td>67</td><td>童年</td><td>罗大佑</td></tr><tr><td>68</td><td>Turkish March</td><td>Wolfgang Amadeus Mozart</td></tr><tr><td>69</td><td>我怀念的</td><td>孙燕姿</td></tr><tr><td>70</td><td>我是一只小小鸟</td><td>赵传</td></tr><tr><td>71</td><td>喜欢你</td><td>Beyond</td></tr><tr><td>72</td><td>下个路口见</td><td>李宇春</td></tr><tr><td>73</td><td>小苹果</td><td>筷子兄弟</td></tr><tr><td>74</td><td>心墙</td><td>郭静</td></tr><tr><td>75</td><td>修炼爱情</td><td>林俊杰</td></tr><tr><td>76</td><td>演员</td><td>薛之谦</td></tr><tr><td>77</td><td>一生有你</td><td>水木年华</td></tr><tr><td>78</td><td>隐形的翅膀</td><td>张韶涵</td></tr><tr><td>79</td><td>有点甜</td><td>汪苏泷</td></tr><tr><td>80</td><td>遇见</td><td>孙燕姿</td></tr><tr><td>81</td><td>夜曲</td><td>周杰伦</td></tr><tr><td>82</td><td>鸳鸯戏</td><td>来自网络</td></tr><tr><td>83</td><td>这世界那么多人</td><td>莫文蔚</td></tr><tr><td>84</td><td>真的爱你</td><td>Beyond</td></tr><tr><td>85</td><td>追光者</td><td>岑宁儿</td></tr><tr><td>86</td><td>最后一页</td><td>江语晨</td></tr><tr><td>87</td><td>Windows XP</td><td>Microsoft</td></tr></tbody></table><h5 id="互联网"><a href="#互联网" class="headerlink" title="互联网"></a>互联网</h5><p>此功能模块集成了20个子菜单,通过调用各类公共API获取并展示各种有趣的信息,包括:</p><ol><li><p>土味情话</p></li><li><p>每日英语</p></li><li><p>每日运势</p></li><li><p>随机诗词</p></li><li><p>随机对联</p></li><li><p>USD/RMB汇率</p></li><li><p>随机单词</p></li><li><p>每日一言</p></li><li><p>励志短句</p></li><li><p>优美诗句</p></li><li><p>天气诗词</p></li><li><p>EMO话语</p></li><li><p>脑筋急转弯</p></li><li><p>健康小贴士</p></li><li><p>Github热榜</p></li><li><p>历史上的今天</p></li><li><p>十万个为什么</p></li><li><p>详细天气</p></li><li><p>股票行情 (AMD, Apple, NVIDIA, TESLA, Microsoft, Intel)</p><p><img src="/..%5Cimg%5C%E8%82%A1%E7%A5%A8.jpg" alt="股票"></p></li><li><p>全球汇率 (API暂时存在问题)</p></li></ol><h5 id="太空"><a href="#太空" class="headerlink" title="太空"></a>太空</h5><p>从公共API获取与太空相关的数据,包括:</p><ul><li>当前国际空间站(ISS)上的宇航员总人数及姓名。</li><li>当前国际空间站的实时经纬度坐标。</li></ul><h5 id="闹钟"><a href="#闹钟" class="headerlink" title="闹钟"></a>闹钟</h5><p>支持用户在设备上直接设置闹钟,<strong>也支持通过微信小程序经由华为云平台远程下发指令进行设置</strong>。在设备时间已经通过NTP同步的前提下,到达设定时间后,无论此时处在那个菜单,全局标志位都会将这个菜单强制退出,然后蜂鸣器会响起,播放音乐,同时屏幕展示闹钟响铃界面。</p><h5 id="倒计时"><a href="#倒计时" class="headerlink" title="倒计时"></a>倒计时</h5><p>标准倒计时功能。</p><h5 id="番茄钟"><a href="#番茄钟" class="headerlink" title="番茄钟"></a>番茄钟</h5><p>遵循标准的番茄工作法:25分钟工作,5分钟短休息,4个循环后进行一次长休息。</p><h5 id="秒表"><a href="#秒表" class="headerlink" title="秒表"></a>秒表</h5><p>标准秒表功能。</p><h5 id="简洁音乐"><a href="#简洁音乐" class="headerlink" title="简洁音乐"></a>简洁音乐</h5><p>与“音乐”功能类似,但UI界面更为简洁,专注于核心的音乐播放体验。</p><h5 id="电脑性能监测"><a href="#电脑性能监测" class="headerlink" title="电脑性能监测"></a>电脑性能监测</h5><p>设备通过USB串口与电脑连接,接收并显示电脑的性能数据(CPU温度与负载、GPU温度与负载、内存占用率),同时将ESP32芯片自身的温度数据一同上报至华为云平台,<strong>并可在微信小程序上实时查看</strong>。</p><h5 id="温度监测"><a href="#温度监测" class="headerlink" title="温度监测"></a>温度监测</h5><p>使用DS18B20温度传感器获取环境温度,并通过<code>TFT_eWidget</code>库组件在屏幕上以仪表盘形式展示。数据同时会上报至华为云平台,<strong>并可在微信小程序上实时查看</strong>。</p><h5 id="动画"><a href="#动画" class="headerlink" title="动画"></a>动画</h5><p>一个移植并改编自<code>TFT_eSPI</code>库官方例程的动画效果展示。</p><h5 id="游戏"><a href="#游戏" class="headerlink" title="游戏"></a>游戏</h5><p>内置了4款经典小游戏:</p><ol><li>康威生命游戏</li><li>反应力小游戏</li><li>掐时间小游戏</li><li>Flappy Bird</li></ol><h5 id="光照监测"><a href="#光照监测" class="headerlink" title="光照监测"></a>光照监测</h5><p>通过光敏电阻检测环境光照强度,使用<code>TFT_eWidget</code>库组件的UI进行展示,并同时将数据上报至华为云平台,<strong>并可在微信小程序上实时查看</strong>。</p><h5 id="LED设置"><a href="#LED设置" class="headerlink" title="LED设置"></a>LED设置</h5><p>允许用户直接在设备上设置背面WS2812 RGB LED的颜色和亮度,<strong>更核心的是,支持通过微信小程序经由华为云平台进行远程控制</strong>,可实现单灯、全灯、彩虹模式等多种灯效。</p><hr><h2 id="微信小程序与华为云端交互"><a href="#微信小程序与华为云端交互" class="headerlink" title="微信小程序与华为云端交互"></a>微信小程序与华为云端交互</h2><p>本项目通过微信小程序实现了对智能时钟的远程监控与控制。小程序作为人机交互的移动端,通过调用华为云物联网平台(IoTDA)提供的API,实现了与设备的双向通信。用户在小程序上的操作会转换成标准的MQTT命令或设备影子请求,经由华为云平台安全、可靠地传达给ESP32设备,从而实现远程操作;同时,设备上报的传感器数据也会通过华为云存储在设备影子中,供小程序随时拉取展示。</p><h3 id="微信小程序与华为云交互架构图"><a href="#微信小程序与华为云交互架构图" class="headerlink" title="微信小程序与华为云交互架构图"></a>微信小程序与华为云交互架构图</h3><pre class="line-numbers language-mermaid" data-language="mermaid"><code class="language-mermaid"><span class="token keyword">sequenceDiagram</span> <span class="token keyword">participant</span> User as 用户 <span class="token keyword">participant</span> MiniProgram as 微信小程序 <span class="token keyword">participant</span> IAM as 华为云 IAM <span class="token keyword">participant</span> IoTDA as 华为云 IoTDA <span class="token keyword">participant</span> ESP32 as ESP32 设备 User<span class="token arrow operator">->></span>MiniProgram<span class="token operator">:</span> 1. 用户操作 <span class="token text string">(操作UI)</span> MiniProgram<span class="token arrow operator">->></span>IAM<span class="token operator">:</span> 2. 获取 Token <span class="token text string">(HTTPS API 调用)</span> IAM<span class="token arrow operator">-->></span>MiniProgram<span class="token operator">:</span> 3. 返回 Token MiniProgram<span class="token arrow operator">->></span>IoTDA<span class="token operator">:</span> 4. 发送命令/查询影子 <span class="token text string">(HTTPS API 调用)</span> IoTDA<span class="token arrow operator">-->></span>ESP32<span class="token operator">:</span> 5. MQTT 命令下发/影子数据返回 ESP32<span class="token arrow operator">-->></span>IoTDA<span class="token operator">:</span> 7. ESP32 上报数据 <span class="token text string">(MQTT)</span> IoTDA<span class="token arrow operator">-->></span>IoTDA<span class="token operator">:</span> 8. IoTDA 更新设备影子 IoTDA<span class="token arrow operator">->></span>ESP32<span class="token operator">:</span> 9. IoTDA 下发命令 <span class="token text string">(MQTT)</span> IoTDA<span class="token arrow operator">-->></span>MiniProgram<span class="token operator">:</span> 6. 返回命令结果/影子数据 MiniProgram<span class="token arrow operator">->></span>User<span class="token operator">:</span> <span class="token text string">(数据展示)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>下面将详细介绍小程序各个核心功能的实现逻辑。</p><h3 id="主控面板-设备数据监控"><a href="#主控面板-设备数据监控" class="headerlink" title="主控面板 (设备数据监控)"></a>主控面板 (设备数据监控)</h3><p>主控面板是小程序的核心监控界面,它实时展示了从设备采集的各项关键数据。</p><p><strong>实现逻辑:</strong></p><ol><li><strong>用户认证:</strong> 用户在进入页面时,首先需要获取操作华为云API所需的认证令牌(Token)。小程序通过HTTPS请求华为云的身份认证服务(IAM),提交预设的域名、用户名和密码,获取有时效性的<code>X-Auth-Token</code>。<ul><li><strong>API 端点示例:</strong> <code>POST https://iam.cn-east-3.myhuaweicloud.com/v3/auth/tokens</code></li><li><strong>请求体示例 (JSON):</strong><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"auth"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"identity"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"methods"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"password"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token property">"password"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"user"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Wechat"</span><span class="token punctuation">,</span> <span class="token property">"password"</span><span class="token operator">:</span> <span class="token string">"your_password"</span><span class="token punctuation">,</span> <span class="token property">"domain"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"GT-xiaomiao123"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"scope"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"project"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"cn-north-4"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li></ul></li><li><strong>数据获取:</strong> 认证成功后,小程序会定时(频率可由用户在界面上调节)向华为云IoTDA平台的设备影子API发起GET请求。<ul><li><strong>API 端点示例:</strong> <code>GET https://827782c6ea.st1.iotda-app.cn-east-3.myhuaweicloud.com/v5/iot/{projectId}/devices/{deviceId}/shadow</code></li><li><strong>请求头:</strong> <code>X-Auth-Token: <your_token></code></li></ul></li><li><strong>数据显示:</strong> 设备影子返回一个包含设备上报的所有属性的JSON对象。小程序解析该对象,提取出温度、光照强度(Lux)、电脑CPU/GPU/RAM负载、ESP32芯片温度等数据,并将其以环形进度条等可视化的方式展现在界面上。<ul><li><strong>设备上报属性示例 (通过MQTT上报到云端):</strong><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"Temperature"</span><span class="token operator">:</span> <span class="token number">25.5</span><span class="token punctuation">,</span> <span class="token property">"Lux"</span><span class="token operator">:</span> <span class="token number">500</span><span class="token punctuation">,</span> <span class="token property">"GPULoad"</span><span class="token operator">:</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token property">"CPULoad"</span><span class="token operator">:</span> <span class="token number">45</span><span class="token punctuation">,</span> <span class="token property">"RAMLoad"</span><span class="token operator">:</span> <span class="token number">60.2</span><span class="token punctuation">,</span> <span class="token property">"ESP32Temp"</span><span class="token operator">:</span> <span class="token number">40.1</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>这些属性由ESP32设备通过MQTT协议上报至华为云IoTDA平台,平台自动更新设备影子。</li></ul></li></ol><p>通过这种方式,用户可以随时随地通过手机查看设备所在环境的状态以及所连接电脑的性能指标。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedpage1.jpg" alt="page1"></p><h3 id="LED灯光控制"><a href="#LED灯光控制" class="headerlink" title="LED灯光控制"></a>LED灯光控制</h3><p>该功能允许用户通过小程序远程控制设备背部的WS2812 RGB LED灯带,实现个性化的灯光效果。</p><p><strong>实现逻辑:</strong></p><p>用户在小程序界面上通过颜色滑块选择颜色,或点击预设的模式按钮。点击“发送命令”后,小程序会向华为云IoTDA平台的命令下发API发起POST请求。请求的Body中包含了具体的服务ID(<code>Property</code>)、命令名称(<code>RGB</code>)以及一个参数(<code>paras</code>)对象,用于描述灯光控制的细节。</p><ul><li><strong>API 端点示例:</strong> <code>POST https://827782c6ea.st1.iotda-app.cn-east-3.myhuaweicloud.com/v5/iot/{projectId}/devices/{deviceId}/commands</code></li><li><strong>请求头:</strong> <code>X-Auth-Token: <your_token></code></li></ul><p>支持的控制模式及其请求体示例包括:</p><ul><li><strong>单灯模式 (<code>single</code>):</strong> 控制指定索引的单个LED灯珠显示特定RGB颜色。<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"service_id"</span><span class="token operator">:</span> <span class="token string">"Property"</span><span class="token punctuation">,</span> <span class="token property">"command_name"</span><span class="token operator">:</span> <span class="token string">"RGB"</span><span class="token punctuation">,</span> <span class="token property">"paras"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"mode"</span><span class="token operator">:</span> <span class="token string">"single"</span><span class="token punctuation">,</span> <span class="token property">"index"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token property">"Red"</span><span class="token operator">:</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token property">"Green"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token property">"Blue"</span><span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><strong>全灯模式 (<code>all</code>):</strong> 控制所有LED灯珠显示同一个RGB颜色。<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"service_id"</span><span class="token operator">:</span> <span class="token string">"Property"</span><span class="token punctuation">,</span> <span class="token property">"command_name"</span><span class="token operator">:</span> <span class="token string">"RGB"</span><span class="token punctuation">,</span> <span class="token property">"paras"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"mode"</span><span class="token operator">:</span> <span class="token string">"all"</span><span class="token punctuation">,</span> <span class="token property">"Red"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token property">"Green"</span><span class="token operator">:</span> <span class="token number">255</span><span class="token punctuation">,</span> <span class="token property">"Blue"</span><span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><strong>彩虹模式 (<code>rainbow</code>):</strong> 触发设备执行预设的彩虹流动灯效。<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"service_id"</span><span class="token operator">:</span> <span class="token string">"Property"</span><span class="token punctuation">,</span> <span class="token property">"command_name"</span><span class="token operator">:</span> <span class="token string">"RGB"</span><span class="token punctuation">,</span> <span class="token property">"paras"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"mode"</span><span class="token operator">:</span> <span class="token string">"rainbow"</span><span class="token punctuation">,</span> <span class="token property">"speed"</span><span class="token operator">:</span> <span class="token number">20</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><strong>关闭模式 (<code>off</code>):</strong> 关闭所有LED灯。<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"service_id"</span><span class="token operator">:</span> <span class="token string">"Property"</span><span class="token punctuation">,</span> <span class="token property">"command_name"</span><span class="token operator">:</span> <span class="token string">"RGB"</span><span class="token punctuation">,</span> <span class="token property">"paras"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"mode"</span><span class="token operator">:</span> <span class="token string">"off"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>设备端的固件在接收到这些由平台转换而来的MQTT命令后,会调用相应的函数来驱动LED,从而实现远程灯光调节。</li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedpage2.jpg" alt="page2"></p><h3 id="音乐播放控制"><a href="#音乐播放控制" class="headerlink" title="音乐播放控制"></a>音乐播放控制</h3><p>该功能允许用户远程点播设备内置的蜂鸣器音乐。</p><p><strong>实现逻辑:</strong></p><p>小程序界面展示了设备内置的88首音乐列表。用户选择一首歌曲和期望的播放UI模式(全功能/简洁/无UI)后,点击发送。小程序会向华为云IoTDA平台下发一个名为<code>play_song</code>的命令。该命令的参数<code>paras</code>包含了<code>Song_index</code>(歌曲索引)和<code>UI</code>(UI模式)。设备接收到命令后,即可开始播放指定的音乐。</p><ul><li><strong>API 端点示例:</strong> <code>POST https://827782c6ea.st1.iotda-app.cn-east-3.myhuaweicloud.com/v5/iot/{projectId}/devices/{deviceId}/commands</code></li><li><strong>请求头:</strong> <code>X-Auth-Token: <your_token></code></li><li><strong>请求体示例 (JSON):</strong><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"service_id"</span><span class="token operator">:</span> <span class="token string">"Property"</span><span class="token punctuation">,</span> <span class="token property">"command_name"</span><span class="token operator">:</span> <span class="token string">"play_song"</span><span class="token punctuation">,</span> <span class="token property">"paras"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"Song_index"</span><span class="token operator">:</span> <span class="token number">16</span><span class="token punctuation">,</span> <span class="token property">"UI"</span><span class="token operator">:</span> <span class="token string">"Full"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedpage3.jpg" alt="page3"></p><h3 id="远程闹钟设置"><a href="#远程闹钟设置" class="headerlink" title="远程闹钟设置"></a>远程闹钟设置</h3><p>用户可以通过小程序为设备设置一个闹钟,而无需物理接触设备。</p><p><strong>实现逻辑:</strong></p><p>用户在界面上通过时间选择器、星期选择器设定好闹钟参数后,点击发送。小程序会向华为云IoTDA平台下发一个名为<code>Alarm</code>的命令。该命令的参数<code>paras</code>包含了用户设置的<code>Hour</code>(小时)、<code>Minute</code>(分钟)、<code>Week</code>(星期)以及<code>On</code>(是否开启)等信息。ESP32设备接收到此命令后,会解析参数并更新本地的闹钟设定。</p><ul><li><strong>API 端点示例:</strong> <code>POST https://827782c6ea.st1.iotda-app.cn-east-3.myhuaweicloud.com/v5/iot/{projectId}/devices/{deviceId}/commands</code></li><li><strong>请求头:</strong> <code>X-Auth-Token: <your_token></code></li><li><strong>请求体示例 (JSON):</strong><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"service_id"</span><span class="token operator">:</span> <span class="token string">"Property"</span><span class="token punctuation">,</span> <span class="token property">"command_name"</span><span class="token operator">:</span> <span class="token string">"Alarm"</span><span class="token punctuation">,</span> <span class="token property">"paras"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"Hour"</span><span class="token operator">:</span> <span class="token number">7</span><span class="token punctuation">,</span> <span class="token property">"Minute"</span><span class="token operator">:</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token property">"Week"</span><span class="token operator">:</span> <span class="token string">"Monday"</span><span class="token punctuation">,</span> <span class="token property">"On"</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedpage4.jpg" alt="page4"></p><hr><h2 id="实现框架"><a href="#实现框架" class="headerlink" title="实现框架"></a>实现框架</h2><p><img src="D:\files\25_26_Autumn\AIoT_final\img\框架.png" alt="框架"></p><p>功能实现概述:</p><p>本项目的整体实现框架基于“云—边—端”协同的物联网体系结构,通过多层设备的互联实现数据采集、上传、处理与可视化展示。底层以ESP32为核心的嵌入式系统负责环境与设备数据的采集、执行控制与状态监测,并通过MQTT协议与上层的华为云物联网平台进行数据交互。中间层由运行Ubuntu系统的Orangepi5 Ultra单板构成,承担本地计算与数据中转的核心任务:它一方面利用OpenCV调用摄像头实现实时图像采集与YOLO目标检测,另一方面将检测结果与系统运行信息上传至云端,实现边缘智能与云端协同处理。华为云物联网平台作为系统的中心节点,负责设备的统一管理、数据存储与命令分发,并与PC端和移动端(微信小程序)保持实时通信。PC端主要用于显示与调试,能直观呈现YOLO检测结果和系统状态;移动端则通过微信小程序实现远程访问、命令下发与信息可视化,增强了系统的人机交互体验。整体架构实现了从数据采集、边缘处理到云端管理与多终端展示的完整闭环,体现了物联网系统的高集成度、可扩展性与实用性。</p><hr><h2 id="硬件选型"><a href="#硬件选型" class="headerlink" title="硬件选型"></a>硬件选型</h2><h3 id="视觉部分-1"><a href="#视觉部分-1" class="headerlink" title="视觉部分"></a>视觉部分</h3><h4 id="相机"><a href="#相机" class="headerlink" title="相机"></a>相机</h4><p>HD 720p摄像头</p><h4 id="开发板"><a href="#开发板" class="headerlink" title="开发板"></a>开发板</h4><p>Orangepi5 Ultra</p><h4 id="Linux系统"><a href="#Linux系统" class="headerlink" title="Linux系统"></a>Linux系统</h4><p>Ubuntu24.04</p><h4 id="架构"><a href="#架构" class="headerlink" title="架构"></a>架构</h4><p>ARM</p><h4 id="代码环境"><a href="#代码环境" class="headerlink" title="代码环境"></a>代码环境</h4><p>conda下的python 3.10虚拟环境</p><h3 id="ESP32部分-1"><a href="#ESP32部分-1" class="headerlink" title="ESP32部分"></a>ESP32部分</h3><h4 id="主控"><a href="#主控" class="headerlink" title="主控"></a>主控</h4><p>考虑到项目对体积、性能和连接性的要求,主控选用 <strong>ESP32-C3-SuperMini</strong> 开发板。该板尺寸紧凑,自带 Wi-Fi 和蓝牙功能,性能足以满足本项目需求。为了方便开发和复用,设计中采用了排母连接,可将主控板模块化插拔。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedESP32C3SUPERMINI.jpg" alt="ESP32C3SUPERMINI"><br><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedESP32C3.png" alt="ESP32C3"></p><h4 id="屏幕"><a href="#屏幕" class="headerlink" title="屏幕"></a>屏幕</h4><p>项目初期曾尝试使用FPC软排线连接的裸屏,但由于FPC座焊接难度高且不易固定,最终更换为带排针的 <strong>1.69寸TFT屏幕模块</strong>,方便连接和安装。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedTFT.jpg" alt="TFT"></p><h4 id="传感器"><a href="#传感器" class="headerlink" title="传感器"></a>传感器</h4><ul><li><strong>温湿度传感器</strong>: 原计划使用 <strong>AHT20</strong>,但因其焊盘过小且位于模块下方,手工焊接困难,最终未能成功。</li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedAHT20.jpg" alt="AHT20"></p><ul><li><strong>温度传感器</strong>: 作为备用方案,采用了 <strong>DS18B20</strong> 单总线数字温度传感器,成功集成到电路中。</li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedDS18B20.jpg" alt="DS18B20"></p><ul><li><strong>光敏电阻</strong>: 采用了贴片光敏电阻GT36528</li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%85%89%E6%95%8F%E7%94%B5%E9%98%BB%E5%AE%9E%E7%89%A9.png" alt="光敏电阻实物"></p><h4 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h4><p>选用 <strong>EC11旋转编码器</strong> 作为主要输入设备,其旋转和按压操作提供了良好的人机交互体验。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedEncoder.JPEG" alt="Encoder"></p><h4 id="电源管理"><a href="#电源管理" class="headerlink" title="电源管理"></a>电源管理</h4><p>原计划包含锂电池供电、<strong>TP4056</strong> 充电管理及USB/电池自动切换电路。但为降低项目复杂度和PCB尺寸,最终仅保留了原理图设计,实物采用USB直接供电。</p><h4 id="无源蜂鸣器"><a href="#无源蜂鸣器" class="headerlink" title="无源蜂鸣器"></a>无源蜂鸣器</h4><p>考虑到体积限制,选用 <strong>MLT-7525</strong> 贴片无源蜂鸣器。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%9C%82%E9%B8%A3%E5%99%A8.jpg" alt="蜂鸣器"></p><h4 id="RGB"><a href="#RGB" class="headerlink" title="RGB"></a>RGB</h4><p>选用 <strong>WS2812</strong> 可编程RGB LED,用于实现丰富的灯光效果。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedRGB.jpg" alt="RGB"></p><h2 id="源代码"><a href="#源代码" class="headerlink" title="源代码"></a>源代码</h2><p>本项目所有源代码,包括视觉部分、硬件固件和微信小程序,均已开源托管于 GitHub。也同时包括在上传的压缩包内。</p><p><strong>GitHub 仓库地址: <a href="https://github.com/xiaomiao321/Aiot-project-ESP32C3">xiaomiao321/Aiot-project-ESP32C3</a></strong></p><h3 id="视觉部分-2"><a href="#视觉部分-2" class="headerlink" title="视觉部分"></a>视觉部分</h3><p>代码实现思路:</p><p>实现的核心思路是通过模型的加载、推理与后处理流程完成实时视频流中的目标识别与可视化显示。系统首先利用Rockchip的RKNN工具加载并量化训练好的ONNX模型,以适配RK3588平台的硬件环境,从而在嵌入式设备上实现高效推理。程序运行后,通过OpenCV调用摄像头实时采集图像帧,并对输入图像进行尺寸缩放与信号归一化处理,确保模型输入的统一性与准确性。在推理阶段,系统使用RKNN API执行YOLOv5网络的前向计算,输出多尺度特征图。随后通过自定义的后处理函数进行边界框解码、置信度筛选与非极大值抑制(NMS)操作,以获得最终的检测结果。检测结果包括目标类别、置信度与位置信息,并通过OpenCV绘制矩形框和标签的方式叠加显示在视频帧上,实现了实时检测效果的可视化。此外,程序在主循环中设置了检测时间间隔控制,以平衡检测精度与运行速度,从而保证在普通嵌入式设备上依旧具备流畅的检测性能。整体系统能够自动识别摄像头画面中的多个目标,并在控制台输出检测数量信息,如“Detected x objects”,实现了从模型加载、图像采集、推理计算到结果展示的完整流程,体现了在嵌入式环境中部署深度学习目标检测算法的可行性与实用性。</p><h3 id="ESP32部分-2"><a href="#ESP32部分-2" class="headerlink" title="ESP32部分"></a>ESP32部分</h3><p>代码结构简介</p><ul><li><strong><code>Hardware/</code></strong>: 存放所有硬件相关的 PlatformIO 项目。<ul><li><code>Weather_Clk_PlatformIO/</code>: <strong>主固件项目</strong>,实现了设备的全部功能。</li><li><code>Test_*/</code>: 开发过程中用于测试特定模块(如WIFI、屏幕、传感器)的独立项目。</li></ul></li><li><strong><code>SmallProgramme/</code></strong>: 存放微信小程序的前端代码。<ul><li><code>Huawei_IOT_Wechat/</code>: 与硬件配套的微信小程序项目。</li></ul></li></ul><p>更详细的项目结构和编译指南,请参考仓库根目录下的 <code>README.md</code> 文件。</p><hr><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本项目旨在应对现代办公与学习场景下的复合需求,成功设计并实现了一款集信息服务、环境感知、效率辅助与情绪关怀于一体的AIoT智能桌面助手。</p><p>我们通过整合ESP32与香橙派(Orangepi5),构建了“云-边-端”协同的系统架构。端侧,ESP32负责多样的本地功能实现与传感器数据采集;边侧,香橙派利用其NPU实现yolov5模型的边缘推理,进行实时视频分析;云侧,我们利用华为云物联网平台(IoTDA)作为核心枢纽,不仅实现了设备的数据上报与状态监控,更通过其开放的API,配合微信小程序,打通了设备的远程控制链路。</p><p>最终,本项目不仅实现了一个功能丰富的硬件原型,更完整地实践了AIoT系统的设计、开发、部署与应用全流程。它验证了将边缘计算与云平台相结合,能够有效提升物联网设备的智能化水平与用户体验,为未来智能桌面产品的开发提供了有价值的参考。</p><hr><h2 id="遇到的挑战及未来展望"><a href="#遇到的挑战及未来展望" class="headerlink" title="遇到的挑战及未来展望"></a>遇到的挑战及未来展望</h2><h3 id="遇到的挑战"><a href="#遇到的挑战" class="headerlink" title="遇到的挑战"></a>遇到的挑战</h3><ol><li><strong>硬件选型与焊接:</strong> 项目初期,在传感器(AHT20)和屏幕(FPC裸屏)的选择上遇到了挑战。AHT20的焊盘过小且位于模块底部,手工焊接难度极高,多次尝试后未能成功,最终更换为DS18B20方案。同样,FPC裸屏的连接座焊接也对设备和技巧有较高要求,为保证项目进度和稳定性,最终更换为带排针的模块。这说明在原型制作阶段,选用易于焊接和调试的模块化组件至关重要。</li><li><strong>Python环境与依赖:</strong> 在香橙派上配置视觉识别部分的环境时,conda与特定Python版本(3.10)的兼容性、yolov5依赖库的安装(如onnx、rknn-toolkit2)等都遇到了一些问题。尤其是在ARM架构的Linux系统上,部分库的编译和安装比x86平台更为复杂,需要耐心解决依赖冲突。</li><li><strong>功能整合与稳定性:</strong> 项目包含了视觉、网络、传感器、显示、控制等多个模块,将它们整合到同一个ESP32固件中,并保证长时间稳定运行是一个巨大的挑战。需要仔细管理内存使用、处理好任务间的同步与通信,避免出现死锁或崩溃。</li></ol><h3 id="未来展望"><a href="#未来展望" class="headerlink" title="未来展望"></a>未来展望</h3><ol><li><strong>完善AI预警功能:</strong> 目前视觉部分仅实现了物体识别和画面展示。未来的关键工作是完成预警功能的闭环:将yolov5识别出的危险或异常物体(如出现在桌面的猫、危险品)的信息,通过Orangepi5实时上传到华为云平台。云平台再根据预设规则,通过微信小程序向用户推送预警通知,实现真正意义上的智能监控。</li><li><strong>增强数据可视化:</strong> 当前小程序仅展示了实时数据。未来可以利用云平台的数据存储能力,将传感器数据(温度、光照等)进行历史归档,并在小程序端增加历史数据图表展示功能,帮助用户分析环境变化趋势。</li><li><strong>扩展第三方服务接入:</strong> 可以探索接入更多的第三方API,例如将日程管理(如Google Calendar)、待办事项(如Microsoft To-Do)等信息同步到设备屏幕上,使其成为一个更强大的个人信息中心。</li><li><strong>优化交互体验:</strong> 探索更自然的交互方式,例如增加语音控制模块,允许用户通过语音命令来控制音乐播放、查询天气、设置闹钟等,进一步解放双手。</li><li><strong>产品化设计:</strong> 对外壳结构、PCB布局进行进一步优化,使其更紧凑、更美观,并考虑电池供电方案,使其成为一个可以脱离USB线独立使用的桌面产品。</li></ol><hr>]]></content>
<categories>
<category> Project </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
<tag> AIoT </tag>
</tags>
</entry>
<entry>
<title>韦东山STM32HAL库笔记</title>
<link href="/2025/10/08/2025-10-08-WeiDongshan-STM32-note/"/>
<url>/2025/10/08/2025-10-08-WeiDongshan-STM32-note/</url>
<content type="html"><![CDATA[<h2 id="C语言补充"><a href="#C语言补充" class="headerlink" title="C语言补充"></a>C语言补充</h2><h3 id="字节序"><a href="#字节序" class="headerlink" title="字节序"></a>字节序</h3><p>假设“int a = 0x12345678;”, 16 进制数中每位数值占据 4 bit;在内存中,是以 8个 bit 作为 1byte。因此 0x12345678 中每两位作为 1byte, 其中 0x78 是低 byte, 0x12 是高 byte。 </p><p>在内存中的存储方式有两种 </p><p> <img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%AD%97%E8%8A%82%E5%BA%8F.png" alt="字节序"></p><p>第一种,低位存在低地址,叫做小字节序(Little endian);第二种,高位存在低地址,叫做大字节序(Big endian)</p><p>一般的 arm 芯片都是小字节序,有些处理器,可以置某个寄存器,让整个系统使用大字节序或小字节序 </p><p>用C语言判断字节序</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><stdint.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><stdio.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">uint32_t</span> a <span class="token operator">=</span> <span class="token number">0x12345678</span><span class="token punctuation">;</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token operator">&</span>a<span class="token punctuation">;</span> <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"%x"</span><span class="token punctuation">,</span> <span class="token operator">*</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="位操作"><a href="#位操作" class="headerlink" title="位操作"></a>位操作</h3><ul><li>移位</li><li>取反</li><li>按位与</li><li>按位或</li></ul><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> a <span class="token operator">=</span> <span class="token number">0x123</span><span class="token punctuation">;</span><span class="token comment">/*把bit7,bit8设置为1*/</span><span class="token keyword">int</span> b <span class="token operator">=</span> <span class="token punctuation">(</span>a <span class="token operator">|</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token operator"><<</span><span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token operator"><<</span><span class="token number">8</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> a <span class="token operator">=</span> <span class="token number">0x123</span><span class="token punctuation">;</span><span class="token comment">/*把bit7,bit8设置为0*/</span><span class="token keyword">int</span> b <span class="token operator">=</span> <span class="token punctuation">(</span>a <span class="token operator">&</span> <span class="token operator">~</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token operator"><<</span><span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&</span> <span class="token operator">~</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token operator"><<</span><span class="token number">8</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="GPIO的工作模式"><a href="#GPIO的工作模式" class="headerlink" title="GPIO的工作模式"></a>GPIO的工作模式</h2><p>一组GPIO有0-15共16个引脚,组数取决于管脚数</p><p>对于某个GPIO</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedGPIO%E6%A1%86%E5%9B%BE.png" alt="GPIO框图"></p><h3 id="输入模式"><a href="#输入模式" class="headerlink" title="输入模式"></a>输入模式</h3><p>输入模式有四种:上拉输入、下拉输入、浮空输入、模拟输入 </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%82%AC%E7%A9%BA%E8%BE%93%E5%85%A5.png" alt="悬空输入"></p><p>上图的 2 个按键 K1、 K2,它不被按下时,电平是多少?<br>答案是:不知道! PIN1、 PIN2 就像一个悬空的电线,它跟参考点“GND”没有电气连接,无法确定 PIN1、 PIN2 的电平是多少。<br>正确的电路如下(PIN1 增加下拉电阻、 PIN2 增加上拉电阻): </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%8A%E4%B8%8B%E6%8B%89.png" alt="上下拉"></p><p>上下拉电阻芯片内部有,可以被使能,也可以不用(浮空输入,用于ADC)</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%A7%A6%E5%8F%91%E5%99%A8.png" alt="触发器"></p><p>TTL触发器:保证寄存器稳定,越过绿线/红线的时候才会变</p><h3 id="输出模式"><a href="#输出模式" class="headerlink" title="输出模式"></a>输出模式</h3><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%BE%93%E5%87%BA%E6%A1%86%E5%9B%BE.png" alt="输出框图"></p><p>输出模式有四种:<br>推挽输出、开漏输出、复用推挽输出、复用开漏输出。 </p><p>开漏:</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%BC%80%E6%BC%8F.png" alt="开漏"></p><p>如果两个芯片都是推挽输出,一个输出0,一个输出1,则$V_{DD}$和$V_{SS}$会短接,导致烧毁</p><p>而开漏的时候,芯片想输出高电平的时候,P-MOS不起作用,相当于在内部是悬空的</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%8E%A8%E6%8C%BD2.png" alt="推挽2"></p><p>对方如果接地就也接地,而如果对方也是开漏输出高电平,那么二者都悬空,可以外部上拉到高电平</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%BC%80%E6%BC%8F2.png" alt="开漏2"></p><p>可以实现双向通信</p><p>IC1想发数据,先输出1,表明该GPIO由外部决定,再读引脚,如果是0,表明IC2驱动了这个引脚,如果是1,表明IC2没驱动这个引脚,IC1就可以驱动这个引脚,可用于IIC</p><h3 id="操作GPIO"><a href="#操作GPIO" class="headerlink" title="操作GPIO"></a>操作GPIO</h3><ol><li>设置方向</li><li>设置模式<ol><li>输入:上、下拉</li><li>输出:推挽、开漏</li></ol></li><li>读值或者写值</li><li>设置速率</li></ol><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedGPIO%E5%AF%84%E5%AD%98%E5%99%A81.png" alt="GPIO寄存器1"></p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedGPIO%E5%AF%84%E5%AD%98%E5%99%A82.png" alt="GPIO寄存器2"></p><p>分别设置每个引脚的CNF和MODE寄存器。速率高的时候对外界电磁干扰比较大</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedGPIO%E5%AF%84%E5%AD%98%E5%99%A83.png" alt="GPIO寄存器3"></p><p>每一位对应一个GPIO引脚</p><h3 id="GPIO-HAL源码"><a href="#GPIO-HAL源码" class="headerlink" title="GPIO HAL源码"></a>GPIO HAL源码</h3><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedGPIO_HAL.png" alt="GPIO_HAL"></p><p><code>HAL_GPIO_Init</code></p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedGPIO%E6%BA%90%E7%A0%811.png" alt="GPIO源码1"></p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedGPIO%E6%BA%90%E7%A0%812.png" alt="GPIO源码2"></p><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><h4 id="使用CubeMX"><a href="#使用CubeMX" class="headerlink" title="使用CubeMX"></a>使用CubeMX</h4><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> GPIO_PinState status <span class="token operator">=</span> GPIO_PIN_SET<span class="token punctuation">;</span> status <span class="token operator">=</span> <span class="token function">HAL_GPIO_ReadPin</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span> GPIO_PIN_14<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>status <span class="token operator">==</span> GPIO_PIN_RESET<span class="token punctuation">)</span> <span class="token comment">/*按下了*/</span> <span class="token punctuation">{</span> <span class="token function">HAL_GPIO_TogglePin</span><span class="token punctuation">(</span>GPIOC<span class="token punctuation">,</span> GPIO_PIN_13<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="手动配置"><a href="#手动配置" class="headerlink" title="手动配置"></a>手动配置</h4><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/*Configure GPIO pin : PC13 */</span>GPIO_InitStruct<span class="token punctuation">.</span>Pin <span class="token operator">=</span> GPIO_PIN_13<span class="token punctuation">;</span>GPIO_InitStruct<span class="token punctuation">.</span>Mode <span class="token operator">=</span> GPIO_MODE_OUTPUT_PP<span class="token punctuation">;</span>GPIO_InitStruct<span class="token punctuation">.</span>Pull <span class="token operator">=</span> GPIO_NOPULL<span class="token punctuation">;</span>GPIO_InitStruct<span class="token punctuation">.</span>Speed <span class="token operator">=</span> GPIO_SPEED_FREQ_LOW<span class="token punctuation">;</span><span class="token function">HAL_GPIO_Init</span><span class="token punctuation">(</span>GPIOC<span class="token punctuation">,</span> <span class="token operator">&</span>GPIO_InitStruct<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/*Configure GPIO pin : PB14 */</span>GPIO_InitStruct<span class="token punctuation">.</span>Pin <span class="token operator">=</span> GPIO_PIN_14<span class="token punctuation">;</span>GPIO_InitStruct<span class="token punctuation">.</span>Mode <span class="token operator">=</span> GPIO_MODE_INPUT<span class="token punctuation">;</span>GPIO_InitStruct<span class="token punctuation">.</span>Pull <span class="token operator">=</span> GPIO_NOPULL<span class="token punctuation">;</span><span class="token function">HAL_GPIO_Init</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span> <span class="token operator">&</span>GPIO_InitStruct<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="光敏电阻-蜂鸣器"><a href="#光敏电阻-蜂鸣器" class="headerlink" title="光敏电阻&蜂鸣器"></a>光敏电阻&蜂鸣器</h2><h3 id="光敏电阻"><a href="#光敏电阻" class="headerlink" title="光敏电阻"></a>光敏电阻</h3><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%85%89%E6%95%8F%E7%94%B5%E9%98%BB.png" alt="光敏电阻"></p><p>LM393 是比较器,它的“+”极接到光敏电阻,“-”极接到可调电阻。当“+”极的电压大于“-”极的电压, DO 输出高电平, LED 被熄灭;当“+”极的电压小于“-”极的电压,DO 输出低电平, LED 被点亮。<br>调整可调电阻,就是调整“+”极的电压,也就是调整光敏电阻分压电路的比较电压。<br>当光照比较强烈时,光敏电阻阻值下降,“+”极电压下降低于“-”极电压时, DO 输出低电平, LED 被点亮。<br>当光照比较强烈时,光敏电阻阻值上升,“+”极电压上升大于“-”极电压时, DO 输出高电平, LED 被熄灭。</p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">static</span> <span class="token keyword">int</span> <span class="token function">isDark</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token function">HAL_GPIO_ReadPin</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span> GPIO_PIN_13<span class="token punctuation">)</span> <span class="token operator">==</span> GPIO_PIN_SET<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">Beep</span><span class="token punctuation">(</span><span class="token keyword">int</span> on<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>on<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">HAL_GPIO_WritePin</span><span class="token punctuation">(</span>GPIOA<span class="token punctuation">,</span> GPIO_PIN_8<span class="token punctuation">,</span> GPIO_PIN_RESET<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">HAL_GPIO_WritePin</span><span class="token punctuation">(</span>GPIOA<span class="token punctuation">,</span> GPIO_PIN_8<span class="token punctuation">,</span> GPIO_PIN_SET<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">LEDControl</span><span class="token punctuation">(</span><span class="token keyword">int</span> on<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>on<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">HAL_GPIO_WritePin</span><span class="token punctuation">(</span>GPIOC<span class="token punctuation">,</span> GPIO_PIN_13<span class="token punctuation">,</span> GPIO_PIN_RESET<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">HAL_GPIO_WritePin</span><span class="token punctuation">(</span>GPIOC<span class="token punctuation">,</span> GPIO_PIN_13<span class="token punctuation">,</span> GPIO_PIN_SET<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* USER CODE END WHILE */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDark</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">Beep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">LEDControl</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">Beep</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">LEDControl</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="GPIO中断"><a href="#GPIO中断" class="headerlink" title="GPIO中断"></a>GPIO中断</h2><h3 id="中断概念的引入"><a href="#中断概念的引入" class="headerlink" title="中断概念的引入"></a>中断概念的引入</h3><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD.png" alt="中断"></p><p>平时高电平,被按下低电平</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> pre_val <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> val <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> val <span class="token operator">=</span> <span class="token function">HAL_GPIO_ReadPin</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span>GPIO_PIN_7<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>val<span class="token operator">!=</span>pre_val<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>val <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"按下"</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"松开"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> pre_val <span class="token operator">=</span> val<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*...某些耗时操作...*/</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行那些耗时操作的时候,无法读取按键状态,可能会丢失按键操作</p><h3 id="中断示例"><a href="#中断示例" class="headerlink" title="中断示例"></a>中断示例</h3><p>你在看书,门铃响了:这就是一个中断。你会插入书签(保存现场),开门取快递(处理中断),回来从书签位置处继续看书(恢复现场)。 </p><p>有中断函数<code>pin_isr()</code>,ISR=Interrupt Service Routine</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">char</span> buf<span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span><span class="token keyword">char</span> cnt <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token keyword">void</span> <span class="token function">pin_isr</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">/*边沿触发*/</span><span class="token punctuation">{</span> buf<span class="token punctuation">[</span>cnt<span class="token operator">++</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">HAL_GPIO_ReadPin</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span>GPIO_PIN_7<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/*读buf*/</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedCPU%E5%BC%82%E5%B8%B8.png" alt="CPU异常"></p><p>CPU 在运行的过程中,也会被各种“异常”打断。这些“异常”有:<br>① 指令未定义<br>② 指令、数据访问有问题<br>③ SWI(软中断)<br>④ 中断<br>中断也属于一种“异常”,导致中断发生的情况有很多,比如:<br>① 按键<br>② 定时器<br>③ ADC 转换完成<br>④ UART 发送完数据、收到数据等等<br>这些众多的“中断源”,汇集到“中断控制器”,由“中断控制器”选择优先级最高的中断并通知 CPU。</p><p>CPU收到中断/异常信号后会跳到某个地址去执行对应的指令(提供的函数) </p><h3 id="中断处理流程"><a href="#中断处理流程" class="headerlink" title="中断处理流程"></a>中断处理流程</h3><p>arm 芯片对异常(中断)处理过程:<br>① 初始化:<br>设置中断源,让它可以产生中断<br>设置中断控制器(可以屏蔽某个中断,优先级)<br>设置 CPU 总开关(使能中断)<br>② 然后就可以执行其他程序了,比如 main 函数<br>③ 产生中断:比如按下按键—>中断控制器—>CPU<br>④ CPU 每执行完一条指令都会检查有无中断/异常产生<br>⑤ CPU 发现有中断/异常产生,开始处理。<br>⑥ 发生中断后, 对于不同的异常,跳去不同的地址执行程序。<br>这地址上,只是一条跳转指令,跳去执行某个函数(地址),这个就是异常向量。 ③④⑤都是硬件做的。<br>这些函数做什么事情?<br>① 保存现场(保存被中断瞬间的各个寄存器的值)<br>② 处理异常(中断):分辨中断源, 再调用不同的处理函数<br>③ 恢复现场(就是从被中断的地方继续运行) </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E6%B5%81%E7%A8%8B.png" alt="中断流程"></p><h3 id="STM32中断体系结构"><a href="#STM32中断体系结构" class="headerlink" title="STM32中断体系结构"></a>STM32中断体系结构</h3><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E7%BB%93%E6%9E%84.png" alt="中断结构"></p><p>EXIT: External interrupt/event controller,外部中断/事件控制器。<br>NVIC: Nested vectored interrupt controller, 嵌套向量中断控制器,识别ID并发给CPU,使用ID在NVIC的向量表里面找到对应函数 </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E6%A1%86%E5%9B%BE.png" alt="中断框图"></p><h4 id="配置GPIO来源"><a href="#配置GPIO来源" class="headerlink" title="配置GPIO来源"></a>配置GPIO来源</h4><p>PA0~PG0 这多个 0 号引脚中,只能有一个连接到 EXTI0,即它们中同一时间只能有一个被用作中断引脚, 可以在 AFIO_EXTICR1 寄存器中选择某个引脚。 </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedEXTI0.png" alt="EXTI0"></p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="中断寄存器"></p><p>可以配置EXTI0-15的外部中断源对应的哪组GPIO,同一时刻同一个EXTI只能有一组GPIO发出中断</p><h4 id="配置EXTI"><a href="#配置EXTI" class="headerlink" title="配置EXTI"></a>配置EXTI</h4><p>对外部中断进行更详细的配置 </p><p>EXTI一共19条线路,16个GPIO的,3个内部的</p><p>EXTI框图:</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedEXTI.png" alt="EXTI"></p><p>在 EXTI 内部,可以配置 EXTI0 的中断触发方式(上升沿触发、下<br>降沿触发)、使能(是否允许 EXTI0 传输到 NVIC)、读取它的当前状态(是否有待处理的中断) </p><p>寄存器如下: </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedEXTI_Reg.png" alt="EXTI_Reg"></p><h5 id="屏蔽-使能寄存器"><a href="#屏蔽-使能寄存器" class="headerlink" title="屏蔽/使能寄存器"></a>屏蔽/使能寄存器</h5><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E4%BD%BF%E8%83%BD%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="中断使能寄存器"></p><p>每一位对应一个外部中断,控制能否发给下一级的NVIC</p><h5 id="边沿触发寄存器"><a href="#边沿触发寄存器" class="headerlink" title="边沿触发寄存器"></a>边沿触发寄存器</h5><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%BE%B9%E6%B2%BF%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="边沿寄存器"></p><p>每一位对应一个外部中断,控制能否上升/下降沿触发</p><h5 id="软件触发寄存器"><a href="#软件触发寄存器" class="headerlink" title="软件触发寄存器"></a>软件触发寄存器</h5><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%BD%AF%E4%BB%B6%E8%A7%A6%E5%8F%91%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="软件触发寄存器"></p><h5 id="中断待处理寄存器"><a href="#中断待处理寄存器" class="headerlink" title="中断待处理寄存器"></a>中断待处理寄存器</h5><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%BE%85%E5%A4%84%E7%90%86%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="待处理寄存器"></p><h4 id="配置NVIC"><a href="#配置NVIC" class="headerlink" title="配置NVIC"></a>配置NVIC</h4><p>管理多个中断源(使能、优先级),提供“异常向量表”在 NVIC 里,每一个异常、中断,都有一个编号: </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E5%90%91%E9%87%8F%E8%A1%A8.png" alt="中断向量表"></p><p>对于这些中断,都可以单独设置:<br>① 使能/屏蔽:是否允许它传输到 CPU<br>② 优先级 </p><p>当各类中断进入 NVIC 后, NVIC 里还有开关:这些中断能否发给 CPU?<br>假设有些中断是同时发生的, 哪一个能优先传给 CPU?在 NVIC 里,可以设置中断优先级、 中断向量表(当发生中断时,去这个表里找到、调用对应的处理函数)。<br>NVIC 里使能中断的寄存器如下(优先级相关寄存器看后面)</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedNVIC%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="NVIC寄存器"></p><h4 id="配置CPU"><a href="#配置CPU" class="headerlink" title="配置CPU"></a>配置CPU</h4><p>CPU: CPU内部也有一个开关,是否使能CPU处理中断。<br>CPU 内部,有一个“PRIMASK”寄存器,“Priority mask”, 往它写入 1 可以禁止所有中断;往它写入 0,可以使能中断。 代码如下: </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedCPU%E9%85%8D%E7%BD%AE%E4%B8%AD%E6%96%AD.png" alt="CPU配置中断"></p><p>当各部件都设置、使能好了之后,某个中断发生了,比如 PA0 的电平从高变低了,就会触发中断。 CPU 接收到中断后,就会跳转到 NVIC 中设置的“异常向量表”,根据 EXTI0 的 ID 找到表里对应的函数,执行它。 </p><h4 id="异常向量表"><a href="#异常向量表" class="headerlink" title="异常向量表"></a>异常向量表</h4><p>在<code>startup_stm32f103xe.s</code>,可以看到如下代码 </p><pre class="line-numbers language-assembly" data-language="assembly"><code class="language-assembly">__Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler ; External Interrupts DCD WWDG_IRQHandler ; Window Watchdog DCD PVD_IRQHandler ; PVD through EXTI Line detect DCD TAMPER_IRQHandler ; Tamper DCD RTC_IRQHandler ; RTC DCD FLASH_IRQHandler ; Flash DCD RCC_IRQHandler ; RCC DCD EXTI0_IRQHandler ; EXTI Line 0 DCD EXTI1_IRQHandler ; EXTI Line 1 DCD EXTI2_IRQHandler ; EXTI Line 2 DCD EXTI3_IRQHandler ; EXTI Line 3 DCD EXTI4_IRQHandler ; EXTI Line 4 DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 DCD ADC1_2_IRQHandler ; ADC1_2 DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 DCD CAN1_RX1_IRQHandler ; CAN1 RX1 DCD CAN1_SCE_IRQHandler ; CAN1 SCE DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 DCD TIM1_BRK_IRQHandler ; TIM1 Break DCD TIM1_UP_IRQHandler ; TIM1 Update DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare DCD TIM2_IRQHandler ; TIM2 DCD TIM3_IRQHandler ; TIM3 DCD TIM4_IRQHandler ; TIM4 DCD I2C1_EV_IRQHandler ; I2C1 Event DCD I2C1_ER_IRQHandler ; I2C1 Error DCD I2C2_EV_IRQHandler ; I2C2 Event DCD I2C2_ER_IRQHandler ; I2C2 Error DCD SPI1_IRQHandler ; SPI1 DCD SPI2_IRQHandler ; SPI2 DCD USART1_IRQHandler ; USART1 DCD USART2_IRQHandler ; USART2 DCD USART3_IRQHandler ; USART3 DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend__Vectors_End<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="中断优先级"><a href="#中断优先级" class="headerlink" title="中断优先级"></a>中断优先级</h4><p> 在 NVIC 里,对于每一个中断, 都有一个 8 位的寄存器被用来表示它的优先级。这个 8位的寄存器,被分为 2 部分,分别表示: 分组优先级(也叫抢占优先级)、 子优先级 </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E4%BC%98%E5%85%88%E7%BA%A7%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="中断优先级寄存器"></p><p>分组优先级被用来判断:当前正在处理的中断,能否被打断。比如当前正在处理 EXT0中断,它的分组优先级为 3;如果这时候发生了 EXT1 中断,它的分组优先级为 4(数值越高,优先级越低),那么 EXT1 的中断就无法打断 EXT0,等 EXT0 的中断处理完毕, EXT1 的中断才能被处理。 但是,如果 EXT1 的分组优先级为 2,那么当前的 EXT0 中断就被“抢占”,先执行 EXT1 的中断处理函数,再继续执行“被抢占的 EXT0”中断函数。</p><p>子优先级被用来判断:两个中断同时发生时,谁先被处理。还是以 EXT0、 EXT1 为例,如果它们同时发生了,那么分组优先级高的中断先被处理;如果分组优先级相同,那么子优先级高的先被处理;如果连子优先级也相同,那么编号小的 EXT0 先被处理。注意:如果 EXT0、 EXT1 的分组优先级相同,是不会发生“抢占”的。比如 EXT0 中断正在被处理, EXT1 紧接着被触发了,即使 EXT1 的子优先级高于 EXT0, EXT1 也不会抢占EXI0。当 EXT0 被处理完毕,才轮到 EXT1 被处理 </p><p>那么,在每个中断的 8 位优先级状态寄存器里,有 2 个问题:<br>① 这 8 位都实现了吗?<br>不一定,比如 STM32F103 系列芯片里只实现了 4 位。实现越多位数,硬件设计越复杂,<br>功耗越高。实现多少位,这是由芯片公司决定的。<br>② 这 8 位里,哪几位表示分组优先级(剩下的就表示子优先级)?<br>这是可配置的, NVIC 内部有一个“优先级分组的配置寄存器”,通过它可以设置所有<br>、中断的“8 位优先级寄存器里,哪几位表示分组优先级”。如下图所示: </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E4%BC%98%E5%85%88%E7%BA%A7%E5%AE%9A%E4%B9%89.png" alt="中断优先级定义"></p><blockquote><p>[!NOTE]</p><p>抢占优先级相同的中断不会抢占对方</p></blockquote><h3 id="GPIO中断编程"><a href="#GPIO中断编程" class="headerlink" title="GPIO中断编程"></a>GPIO中断编程</h3><h4 id="CubeMX配置"><a href="#CubeMX配置" class="headerlink" title="CubeMX配置"></a>CubeMX配置</h4><p>GPIO设成GPIO_EXTI14——GPIO Mode可以选中断触发方式,在NVIC勾选对应的中断的Enabled,然后可以在NVIC栏配置优先级</p><h4 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h4><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">HAL_GPIO_EXTI_IRQHandler</span><span class="token punctuation">(</span><span class="token keyword">uint16_t</span> GPIO_Pin<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">/* EXTI line interrupt detected */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__HAL_GPIO_EXTI_GET_IT</span><span class="token punctuation">(</span>GPIO_Pin<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0x00u</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">__HAL_GPIO_EXTI_CLEAR_IT</span><span class="token punctuation">(</span>GPIO_Pin<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">HAL_GPIO_EXTI_Callback</span><span class="token punctuation">(</span>GPIO_Pin<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span>__weak <span class="token keyword">void</span> <span class="token function">HAL_GPIO_EXTI_Callback</span><span class="token punctuation">(</span><span class="token keyword">uint16_t</span> GPIO_Pin<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">/* Prevent unused argument(s) compilation warning */</span> <span class="token function">UNUSED</span><span class="token punctuation">(</span>GPIO_Pin<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* NOTE: This function Should not be modified, when the callback is needed, the HAL_GPIO_EXTI_Callback could be implemented in the user file */</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>重新定义回调函数即可</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">HAL_GPIO_EXTI_Callback</span><span class="token punctuation">(</span><span class="token keyword">uint16_t</span> GPIO_Pin<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>GPIO_Pin <span class="token operator">==</span> GPIO_PIN_14<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/*分辨是上升还是下降沿*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">HAL_GPIO_ReadPin</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span> GPIO_PIN_14<span class="token punctuation">)</span> <span class="token operator">==</span> GPIO_PIN_RESET<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/*按下,点亮LED*/</span> <span class="token function">HAL_GPIO_WritePin</span><span class="token punctuation">(</span>GPIOC<span class="token punctuation">,</span> GPIO_PIN_13<span class="token punctuation">,</span> GPIO_PIN_RESET<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">/*松开,熄灭LED*/</span> <span class="token function">HAL_GPIO_WritePin</span><span class="token punctuation">(</span>GPIOC<span class="token punctuation">,</span> GPIO_PIN_13<span class="token punctuation">,</span> GPIO_PIN_SET<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="代码解读"><a href="#代码解读" class="headerlink" title="代码解读"></a>代码解读</h4><h5 id="设置-GPIO-和-EXTI"><a href="#设置-GPIO-和-EXTI" class="headerlink" title="设置 GPIO 和 EXTI"></a>设置 GPIO 和 EXTI</h5><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/*Configure GPIO pin : PB14 */</span>GPIO_InitStruct<span class="token punctuation">.</span>Pin <span class="token operator">=</span> GPIO_PIN_14<span class="token punctuation">;</span>GPIO_InitStruct<span class="token punctuation">.</span>Mode <span class="token operator">=</span> GPIO_MODE_IT_RISING_FALLING<span class="token punctuation">;</span>GPIO_InitStruct<span class="token punctuation">.</span>Pull <span class="token operator">=</span> GPIO_NOPULL<span class="token punctuation">;</span><span class="token function">HAL_GPIO_Init</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span> <span class="token operator">&</span>GPIO_InitStruct<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/*同时涉及了GPIO模块和EXTI*/</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">GPIO_MODE_IT_RISING</span> <span class="token expression"><span class="token number">0x10110000u</span> </span><span class="token comment">/*!< External Interrupt Mode with Rising edge trigger detection */</span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">GPIO_MODE_IT_FALLING</span> <span class="token expression"><span class="token number">0x10210000u</span> </span><span class="token comment">/*!< External Interrupt Mode with Falling edge trigger detection */</span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">GPIO_MODE_IT_RISING_FALLING</span> <span class="token expression"><span class="token number">0x10310000u</span> </span><span class="token comment">/*!< External Interrupt Mode with Rising/Falling edge trigger detection */</span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">GPIO_MODE_EVT_RISING</span> <span class="token expression"><span class="token number">0x10120000u</span> </span><span class="token comment">/*!< External Event Mode with Rising edge trigger detection */</span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">GPIO_MODE_EVT_FALLING</span> <span class="token expression"><span class="token number">0x10220000u</span> </span><span class="token comment">/*!< External Event Mode with Falling edge trigger detection */</span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">GPIO_MODE_EVT_RISING_FALLING</span> <span class="token expression"><span class="token number">0x10320000u</span> </span><span class="token comment">/*!< External Event Mode with Rising/Falling edge trigger detection */</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>HAL_GPIO_Init()</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/*--------------------- EXTI Mode Configuration ------------------------*/</span><span class="token comment">/* Configure the External Interrupt or event for the current IO */</span><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>GPIO_Init<span class="token operator">-></span>Mode <span class="token operator">&</span> EXTI_MODE<span class="token punctuation">)</span> <span class="token operator">==</span> EXTI_MODE<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">/* Enable AFIO Clock */</span> <span class="token function">__HAL_RCC_AFIO_CLK_ENABLE</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> temp <span class="token operator">=</span> AFIO<span class="token operator">-></span>EXTICR<span class="token punctuation">[</span>position <span class="token operator">>></span> <span class="token number">2u</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token function">CLEAR_BIT</span><span class="token punctuation">(</span>temp<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0x0Fu</span><span class="token punctuation">)</span> <span class="token operator"><<</span> <span class="token punctuation">(</span><span class="token number">4u</span> <span class="token operator">*</span> <span class="token punctuation">(</span>position <span class="token operator">&</span> <span class="token number">0x03u</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">SET_BIT</span><span class="token punctuation">(</span>temp<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token function">GPIO_GET_INDEX</span><span class="token punctuation">(</span>GPIOx<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator"><<</span> <span class="token punctuation">(</span><span class="token number">4u</span> <span class="token operator">*</span> <span class="token punctuation">(</span>position <span class="token operator">&</span> <span class="token number">0x03u</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> AFIO<span class="token operator">-></span>EXTICR<span class="token punctuation">[</span>position <span class="token operator">>></span> <span class="token number">2u</span><span class="token punctuation">]</span> <span class="token operator">=</span> temp<span class="token punctuation">;</span> <span class="token comment">/* Enable or disable the rising trigger */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>GPIO_Init<span class="token operator">-></span>Mode <span class="token operator">&</span> RISING_EDGE<span class="token punctuation">)</span> <span class="token operator">==</span> RISING_EDGE<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">SET_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>RTSR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">CLEAR_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>RTSR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Enable or disable the falling trigger */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>GPIO_Init<span class="token operator">-></span>Mode <span class="token operator">&</span> FALLING_EDGE<span class="token punctuation">)</span> <span class="token operator">==</span> FALLING_EDGE<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">SET_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>FTSR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">CLEAR_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>FTSR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Configure the event mask */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>GPIO_Init<span class="token operator">-></span>Mode <span class="token operator">&</span> GPIO_MODE_EVT<span class="token punctuation">)</span> <span class="token operator">==</span> GPIO_MODE_EVT<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">SET_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>EMR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">CLEAR_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>EMR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Configure the interrupt mask */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>GPIO_Init<span class="token operator">-></span>Mode <span class="token operator">&</span> GPIO_MODE_IT<span class="token punctuation">)</span> <span class="token operator">==</span> GPIO_MODE_IT<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">SET_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>IMR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">CLEAR_BIT</span><span class="token punctuation">(</span>EXTI<span class="token operator">-></span>IMR<span class="token punctuation">,</span> iocurrent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>至此,EXTI能把中断信号发给NVIC</p><h5 id="中断-IT-和事件-EVT-的区别"><a href="#中断-IT-和事件-EVT-的区别" class="headerlink" title="中断(IT)和事件(EVT)的区别"></a>中断(IT)和事件(EVT)的区别</h5><p>中断到达CPU,事件到达其他模块(如ADC),可以用硬件模块通过事件驱动其他模块</p><h5 id="设置NVIC"><a href="#设置NVIC" class="headerlink" title="设置NVIC"></a>设置NVIC</h5><p>要做3个事</p><ol><li>设置优先级</li><li>使能中断</li><li>把向量表告诉NVIC</li></ol><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token function">HAL_NVIC_SetPriority</span><span class="token punctuation">(</span>EXTI15_10_IRQn<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">HAL_NVIC_EnableIRQ</span><span class="token punctuation">(</span>EXTI15_10_IRQn<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>这里没设置向量表,使用的默认的汇编文件的向量表</p><h2 id="OLED调试"><a href="#OLED调试" class="headerlink" title="OLED调试"></a>OLED调试</h2><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><p><code>HAL_I2C_Init</code>会先调用<code>HAL_I2C_MspInit</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">HAL_I2C_MspInit</span><span class="token punctuation">(</span>I2C_HandleTypeDef<span class="token operator">*</span> i2cHandle<span class="token punctuation">)</span><span class="token punctuation">{</span> GPIO_InitTypeDef GPIO_InitStruct <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>i2cHandle<span class="token operator">-></span>Instance<span class="token operator">==</span>I2C1<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* USER CODE BEGIN I2C1_MspInit 0 */</span> <span class="token comment">/* USER CODE END I2C1_MspInit 0 */</span> <span class="token function">__HAL_RCC_GPIOB_CLK_ENABLE</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/**I2C1 GPIO Configuration PB6 ------> I2C1_SCL PB7 ------> I2C1_SDA */</span> GPIO_InitStruct<span class="token punctuation">.</span>Pin <span class="token operator">=</span> GPIO_PIN_6<span class="token operator">|</span>GPIO_PIN_7<span class="token punctuation">;</span> GPIO_InitStruct<span class="token punctuation">.</span>Mode <span class="token operator">=</span> GPIO_MODE_AF_OD<span class="token punctuation">;</span> GPIO_InitStruct<span class="token punctuation">.</span>Speed <span class="token operator">=</span> GPIO_SPEED_FREQ_HIGH<span class="token punctuation">;</span> <span class="token function">HAL_GPIO_Init</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span> <span class="token operator">&</span>GPIO_InitStruct<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* I2C1 clock enable */</span> <span class="token function">__HAL_RCC_I2C1_CLK_ENABLE</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE BEGIN I2C1_MspInit 1 */</span> <span class="token comment">/* USER CODE END I2C1_MspInit 1 */</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="添加驱动程序"><a href="#添加驱动程序" class="headerlink" title="添加驱动程序"></a>添加驱动程序</h3><p>去<code>D:\files\25-26Autumn\weidongshan\HAL\my_programme\OLED_Driver</code>复制,但是不能添加到项目的<code>\Drivers\OLED_Driver</code></p><blockquote><p>[!WARNING]</p><p>使用CubeMX的时候一定要选中<code>Copy only the necessary files</code>,否则构建会出问题</p></blockquote><h3 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h3><p>OLED 的分辨率是 128X64, 要显示 8X16 点阵字符时,每行最多可以显示 16 个字符,所以下面罗列的函数里, X 坐标的取值为 0<del>15。最多同时能显示 4 行字符,按理说 Y 坐标取值应该为 0</del>3,但是为了更细致地调整字符的 Y 坐标,我们把 Y 坐标的取值范围设定为 0~7。下图里,分别在坐标(0, 0)、(0,1)位置显示了字符“A”;在坐标(0,2)、(0,3)显示了字符“B”;在坐标(0,4)、(0,5)显示了字符“C”;在坐标(0,6)显示了字符“D” </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedOLED.png" alt="OLED"></p><h2 id="按键驱动程序改进"><a href="#按键驱动程序改进" class="headerlink" title="按键驱动程序改进"></a>按键驱动程序改进</h2><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%8A%96%E5%8A%A8.png" alt="抖动"></p><p>按下或松开一个按键,它的 GPIO 电平会反复变化,最后才稳定。一般是 5~10 毫秒才会稳定。<br>如果不处理抖动的话,用户只操作一次按键,中断程序可能会上报多个数据。怎么处理? </p><p>① 在按键中断程序中,可以循环判断几十亳秒,发现电平稳定之后再上报<br>② 使用定时器<br>显然第 1 种方法太耗时,违背“中断要尽快处理”的原则,你的系统会很卡。怎么使<br>用定时器?看下图: </p><p><img src="/.%5Cimg%5C%E4%B8%AD%E6%96%AD%E5%AE%9A%E6%97%B6%E5%99%A8.png" alt="中断定时器"></p><p>在 GPIO 中断中并不立刻记录按键值,而是修改定时器超时时间, 10ms 后再处理。如果 10ms 内又发生了 GPIO 中断,那就认为是抖动,这时再次修改超时时间为10ms。<br>只有 10ms 之内再无 GPIO 中断发生,那么定时器的函数才会被调用。在定时器函数中<br>记录按键值。 </p><h3 id="验证按键抖动"><a href="#验证按键抖动" class="headerlink" title="验证按键抖动:"></a>验证按键抖动:</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">HAL_GPIO_EXTI_Callback</span><span class="token punctuation">(</span><span class="token keyword">uint16_t</span> GPIO_Pin<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>GPIO_Pin <span class="token operator">==</span> GPIO_PIN_14<span class="token punctuation">)</span> <span class="token punctuation">{</span> g_key_cnt<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token function">HAL_GPIO_TogglePin</span><span class="token punctuation">(</span>GPIOC<span class="token punctuation">,</span> GPIO_PIN_13<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token function">OLED_Init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">OLED_Clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE END 2 */</span> <span class="token comment">/* Infinite loop */</span> <span class="token comment">/* USER CODE BEGIN WHILE */</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* USER CODE END WHILE */</span> <span class="token function">OLED_PrintString</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token string">"KEY ISR cnt = "</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">OLED_PrintSignedVal</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> g_key_cnt<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE BEGIN 3 */</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="定时器消除抖动"><a href="#定时器消除抖动" class="headerlink" title="定时器消除抖动"></a>定时器消除抖动</h3><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E4%B8%AD%E6%96%AD%E5%AE%9A%E6%97%B6%E5%99%A8.png" alt="中断定时器"></p><p>SysTick—系统定时器是属于CM4内核中的一个外设,内嵌在NVIC中。系统定时器是一个24bit的向下递减的计数器, 计数器每计数一次的时间为1/SYSCLK,一般我们设置系统时钟SYSCLK等于180M。当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复。</p><p>因为SysTick是属于CM4内核的外设,所以所有基于CM4内核的单片机都具有这个系统定时器,使得软件在CM4单片机中可以很容易的移植。 系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。</p><p><code>SysTick_Handler</code>是系统滴答定时器中断处理函数,默认1ms触发一次中断,这个中断处理函数会增加计数值,计数值可通过<code>HAL_GetTick</code>获得</p><p>可以自己实现一个定时器,在这个函数里面判断定时器是否超时<code>check_timer()</code>,然后在外部中断推迟定时器10ms<code>mod_timer(10)</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token class-name">soft_timer</span> <span class="token punctuation">{</span> <span class="token keyword">uint32_t</span> timeout<span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token operator">*</span>arg<span class="token punctuation">;</span> <span class="token comment">/*给函数的参数*/</span> <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>func<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span> myTimer<span class="token punctuation">;</span>myTimer key_timer <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">~</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token comment">/*超时时间初始化为一个很大的数*/</span> <span class="token constant">NULL</span><span class="token punctuation">,</span> key_timeout_func<span class="token punctuation">}</span><span class="token punctuation">;</span><span class="token keyword">void</span> <span class="token function">key_timeout_func</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span>arg<span class="token punctuation">)</span> <span class="token punctuation">{</span> g_key_cnt<span class="token operator">++</span><span class="token punctuation">;</span> key_timer<span class="token punctuation">.</span>timeout <span class="token operator">=</span> <span class="token operator">~</span><span class="token number">0</span><span class="token punctuation">;</span><span class="token comment">/*这里如果不设置每1毫秒都会增加*/</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">HAL_GPIO_EXTI_Callback</span><span class="token punctuation">(</span><span class="token keyword">uint16_t</span> GPIO_Pin<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>GPIO_Pin <span class="token operator">==</span> GPIO_PIN_14<span class="token punctuation">)</span> <span class="token punctuation">{</span> g_key_cnt<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token function">mod_timer</span><span class="token punctuation">(</span><span class="token operator">&</span>key_timer<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">mod_timer</span><span class="token punctuation">(</span>myTimer <span class="token operator">*</span>pTimer<span class="token punctuation">,</span> <span class="token keyword">uint32_t</span> time_offset<span class="token punctuation">)</span> <span class="token punctuation">{</span> pTimer<span class="token operator">-></span>timeout <span class="token operator">=</span> <span class="token function">HAL_GetTick</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> time_offset<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">SysTick_Handler</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* USER CODE BEGIN SysTick_IRQn 0 */</span> <span class="token comment">/* USER CODE END SysTick_IRQn 0 */</span> <span class="token function">HAL_IncTick</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE BEGIN SysTick_IRQn 1 */</span> <span class="token keyword">extern</span> <span class="token keyword">void</span> <span class="token function">check_timer</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">check_timer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE END SysTick_IRQn 1 */</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">check_timer</span><span class="token punctuation">(</span>myTimer <span class="token operator">*</span>pTimer<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pTimer<span class="token operator">-></span>timeout <span class="token operator"><=</span> <span class="token function">HAL_GetTick</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pTimer<span class="token operator">-></span><span class="token function">func</span><span class="token punctuation">(</span>pTimer<span class="token operator">-></span>arg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="环形缓冲区"><a href="#环形缓冲区" class="headerlink" title="环形缓冲区"></a>环形缓冲区</h3><p>即使使用中断函数或者定时器函数记录按键,如果只能记录一个键值的话,如果不能及时读走出来,再次发生中断时新值就会覆盖旧值。要解决数据被覆盖的问题,可以使用一个稍微大点的缓冲区,这就涉及数据的写入、读出,可以使用环形缓冲区。 </p><p>环形缓冲区特别适合这种场景:</p><p>一方写 buffer,另一方读 buffer。 环形缓冲区实际上还是一维数组,假设有 N 个数组项,从第 0 个数组项开始遍历,访问完第 N-1 个数组项后,再从 0 开始——这就是“环形”的含义, </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E7%8E%AF%E5%BD%A2.png" alt="环形"></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> buf<span class="token punctuation">[</span><span class="token number">8</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">int</span> r <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token keyword">int</span> w <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token comment">/*r,w分别表示下次读、写的位置*/</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p><code>r == w</code>表明缓冲区空</p><p><code>下一个w位置==r</code>表明缓冲区满</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> next_w <span class="token operator">=</span> w <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token keyword">if</span><span class="token punctuation">(</span>next_w <span class="token operator">==</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">{</span> next_w <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>写数据:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">if</span><span class="token punctuation">(</span>未满<span class="token punctuation">)</span><span class="token comment">/*if(next_w != r)*/</span><span class="token punctuation">{</span> buf<span class="token punctuation">[</span>w<span class="token punctuation">]</span> <span class="token operator">=</span> val<span class="token punctuation">;</span> w<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>w <span class="token operator">==</span> <span class="token number">8</span><span class="token punctuation">)</span> w <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>读数据:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">if</span><span class="token punctuation">(</span>r <span class="token operator">!=</span> w<span class="token punctuation">)</span><span class="token comment">/*表明有数据*/</span><span class="token punctuation">{</span> val <span class="token operator">=</span> buf<span class="token punctuation">[</span>r<span class="token punctuation">]</span><span class="token punctuation">;</span> r<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>r <span class="token operator">==</span> <span class="token number">8</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> r <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><circle_buffer.h></span></span><span class="token keyword">void</span> <span class="token function">circle_buf_init</span><span class="token punctuation">(</span>circle_buf <span class="token operator">*</span>pCircleBuf<span class="token punctuation">,</span> <span class="token keyword">uint32_t</span> len<span class="token punctuation">,</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>buf<span class="token punctuation">)</span> <span class="token punctuation">{</span> pCircleBuf<span class="token operator">-></span>r <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> pCircleBuf<span class="token operator">-></span>w <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> pCircleBuf<span class="token operator">-></span>len <span class="token operator">=</span> len<span class="token punctuation">;</span> pCircleBuf<span class="token operator">-></span>buf <span class="token operator">=</span> buf<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">int</span> <span class="token function">circle_buf_read</span><span class="token punctuation">(</span>circle_buf <span class="token operator">*</span>pCircleBuf<span class="token punctuation">,</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>pVal<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pCircleBuf<span class="token operator">-></span>r <span class="token operator">!=</span> pCircleBuf<span class="token operator">-></span>w<span class="token punctuation">)</span> <span class="token comment">/*缓冲区非空*/</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pVal <span class="token operator">=</span> pCircleBuf<span class="token operator">-></span>buf<span class="token punctuation">[</span>pCircleBuf<span class="token operator">-></span>r<span class="token punctuation">]</span><span class="token punctuation">;</span> pCircleBuf<span class="token operator">-></span>r<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pCircleBuf<span class="token operator">-></span>r <span class="token operator">==</span> pCircleBuf<span class="token operator">-></span>len<span class="token punctuation">)</span> <span class="token punctuation">{</span> pCircleBuf<span class="token operator">-></span>r <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">int</span> <span class="token function">circle_buf_write</span><span class="token punctuation">(</span>circle_buf <span class="token operator">*</span>pCircleBuf<span class="token punctuation">,</span> <span class="token keyword">uint8_t</span> val<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">uint32_t</span> next_w<span class="token punctuation">;</span> next_w <span class="token operator">=</span> pCircleBuf<span class="token operator">-></span>w <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>next_w <span class="token operator">==</span> pCircleBuf<span class="token operator">-></span>len<span class="token punctuation">)</span> <span class="token punctuation">{</span> next_w <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>next_w <span class="token operator">!=</span> pCircleBuf<span class="token operator">-></span>r<span class="token punctuation">)</span> <span class="token comment">/*没满*/</span> <span class="token punctuation">{</span> pCircleBuf<span class="token operator">-></span>buf<span class="token punctuation">[</span>pCircleBuf<span class="token operator">-></span>w<span class="token punctuation">]</span> <span class="token operator">=</span> val<span class="token punctuation">;</span> pCircleBuf<span class="token operator">-></span>w <span class="token operator">=</span> next_w<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>修改<code>key_timeout_func</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">key_timeout_func</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span>arg<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">uint8_t</span> key_val <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">/*按下是0x1,松开是0x81*/</span> g_key_cnt<span class="token operator">++</span><span class="token punctuation">;</span> key_timer<span class="token punctuation">.</span>timeout <span class="token operator">=</span> <span class="token operator">~</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">/*读GPIO*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">HAL_GPIO_ReadPin</span><span class="token punctuation">(</span>GPIOB<span class="token punctuation">,</span> GPIO_PIN_14<span class="token punctuation">)</span> <span class="token operator">==</span> GPIO_PIN_RESET<span class="token punctuation">)</span> <span class="token comment">/*按下触发的中断*/</span> <span class="token punctuation">{</span> key_val <span class="token operator">=</span> <span class="token number">0x01</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> key_val <span class="token operator">=</span> <span class="token number">0x81</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*把按键值放入缓冲区*/</span> <span class="token function">circle_buf_write</span><span class="token punctuation">(</span><span class="token operator">&</span>g_key_buf<span class="token punctuation">,</span> key_val<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>main函数</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* USER CODE END WHILE */</span> <span class="token keyword">int</span> len <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token function">OLED_PrintString</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token string">"KEY ISR cnt = "</span><span class="token punctuation">)</span><span class="token punctuation">;</span> len <span class="token operator">=</span> <span class="token function">OLED_PrintString</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"Keyval:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">OLED_PrintSignedVal</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> g_key_cnt<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">uint8_t</span> key_val <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">circle_buf_read</span><span class="token punctuation">(</span><span class="token operator">&</span>g_key_buf<span class="token punctuation">,</span> <span class="token operator">&</span>key_val<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">/*成功读取*/</span> <span class="token punctuation">{</span> <span class="token function">OLED_ClearLine</span><span class="token punctuation">(</span>len<span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">OLED_PrintHex</span><span class="token punctuation">(</span>len<span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> key_val<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* USER CODE BEGIN 3 */</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="UART硬件结构"><a href="#UART硬件结构" class="headerlink" title="UART硬件结构"></a>UART硬件结构</h2><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedUART%E6%A1%86%E5%9B%BE.png" alt="UART框图"></p><p>设置:波特率、数据位</p><p>发送:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">unsigned</span> <span class="token keyword">int</span> <span class="token operator">*</span>p <span class="token operator">=</span> TDR的地址<span class="token punctuation">;</span><span class="token comment">/*TDR=Transmit Data Register*/</span><span class="token operator">*</span>p <span class="token operator">=</span> <span class="token number">0x78</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>0x78被写入TDR寄存器,会自动被移动到移位寄存器(Transmit Shift Register),移位寄存器会自动一位一位地发送数据</p><p>什么时候能写下一个数据?等到0x78被移动到移位寄存器</p><p>有状态寄存器</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedUART%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="UART寄存器"></p><p>TXE=1表明TDR寄存器空,数据被转到了移位寄存器,TC=1表明已经发送完毕。二者都由硬件设置</p><p>读数据</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">unsigned</span> <span class="token keyword">int</span> <span class="token operator">*</span>p <span class="token operator">=</span> RDR地址<span class="token punctuation">;</span>val <span class="token operator">=</span> <span class="token operator">*</span>p<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>分辨状态:RXNE=1表明非空</p><p>上面的办法都是查询模式,还可以用中断</p><p>设置中断</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedUART%E8%AE%BE%E7%BD%AE%E5%AF%84%E5%AD%98%E5%99%A8.png" alt="UART设置寄存器"></p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedUART%E8%AE%BE%E7%BD%AE%E5%AF%84%E5%AD%98%E5%99%A82.png" alt="UART设置寄存器2"></p><p>一般用TXEIE(发送寄存器空中断)和RXNEIE(接收寄存器非空中断)</p><h2 id="UART-查询方式-polling-mode"><a href="#UART-查询方式-polling-mode" class="headerlink" title="UART 查询方式(polling mode)"></a>UART 查询方式(polling mode)</h2><p>要发送数据时,先把数据写入 TDR 寄存器,然后判断 TDR 为空再返回。当然也可以先判断 TDR 为空,再写入。<br>要读取数据时,先判断 RDR 非空,再读取 RDR 得到数据。 </p><h3 id="CubeMX配置-1"><a href="#CubeMX配置-1" class="headerlink" title="CubeMX配置"></a>CubeMX配置</h3><p>Connectivity-USART1——使能——异步,其他默认即可</p><h3 id="代码-2"><a href="#代码-2" class="headerlink" title="代码"></a>代码</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"> <span class="token keyword">char</span> <span class="token operator">*</span>tx_buffer <span class="token operator">=</span> <span class="token string">"Hello,world!"</span><span class="token punctuation">;</span> <span class="token keyword">char</span> c<span class="token punctuation">;</span> <span class="token function">HAL_UART_Transmit</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> tx_buffer<span class="token punctuation">,</span> <span class="token function">strlen</span><span class="token punctuation">(</span>tx_buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> HAL_MAX_DELAY<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE END 2 */</span> <span class="token comment">/* Infinite loop */</span> <span class="token comment">/* USER CODE BEGIN WHILE */</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token function">HAL_UART_Receive</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>c<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> HAL_MAX_DELAY<span class="token punctuation">)</span> <span class="token operator">!=</span> HAL_OK<span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">/*等到收到数据*/</span> c <span class="token operator">+=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token function">HAL_UART_Transmit</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>c<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> HAL_MAX_DELAY<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE END WHILE */</span> <span class="token comment">/* USER CODE BEGIN 3 */</span> <span class="token punctuation">}</span> <span class="token comment">/* USER CODE END 3 */</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="HAL-UART-Transmit"><a href="#HAL-UART-Transmit" class="headerlink" title="HAL_UART_Transmit()"></a><code>HAL_UART_Transmit()</code></h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">while</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>TxXferCount <span class="token operator">></span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">UART_WaitOnFlagUntilTimeout</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_FLAG_TXE<span class="token punctuation">,</span> RESET<span class="token punctuation">,</span> tickstart<span class="token punctuation">,</span> Timeout<span class="token punctuation">)</span> <span class="token operator">!=</span> HAL_OK<span class="token punctuation">)</span><span class="token comment">/*等待发送寄存器空标志位=1*/</span> <span class="token punctuation">{</span> huart<span class="token operator">-></span>gState <span class="token operator">=</span> HAL_UART_STATE_READY<span class="token punctuation">;</span> <span class="token keyword">return</span> HAL_TIMEOUT<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pdata8bits <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token operator">*</span>pdata16bits <span class="token operator">&</span> <span class="token number">0x01FFU</span><span class="token punctuation">)</span><span class="token punctuation">;</span> pdata16bits<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token operator">*</span>pdata8bits <span class="token operator">&</span> <span class="token number">0xFFU</span><span class="token punctuation">)</span><span class="token punctuation">;</span> pdata8bits<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> huart<span class="token operator">-></span>TxXferCount<span class="token operator">--</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="HAL-UART-Receive"><a href="#HAL-UART-Receive" class="headerlink" title="HAL_UART_Receive"></a><code>HAL_UART_Receive</code></h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">while</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>RxXferCount <span class="token operator">></span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">UART_WaitOnFlagUntilTimeout</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_FLAG_RXNE<span class="token punctuation">,</span> RESET<span class="token punctuation">,</span> tickstart<span class="token punctuation">,</span> Timeout<span class="token punctuation">)</span> <span class="token operator">!=</span> HAL_OK<span class="token punctuation">)</span> <span class="token punctuation">{</span> huart<span class="token operator">-></span>RxState <span class="token operator">=</span> HAL_UART_STATE_READY<span class="token punctuation">;</span> <span class="token keyword">return</span> HAL_TIMEOUT<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pdata8bits <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata16bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token number">0x01FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> pdata16bits<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_9B<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_8B<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>Parity <span class="token operator">==</span> UART_PARITY_NONE<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata8bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token number">0x00FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata8bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token number">0x007F</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> pdata8bits<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> huart<span class="token operator">-></span>RxXferCount<span class="token operator">--</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>缺点:数据容易丢失,因为<code>HAL_UART_Transmit</code>打印消耗时间,接收数据寄存器数据无法被及时读走,导致被之后的数据覆盖</p><h2 id="UART中断方式"><a href="#UART中断方式" class="headerlink" title="UART中断方式"></a>UART中断方式</h2><p>使用中断方式,效率更高,并且可以在接收数据时避免数据丢失。<br>要发送数据时,使能“TXE”中断(发送寄存器空中断)。在 TXE 中断处理函数里,从程序的发送 buffer 里取出一个数据,写入 TDR。等再次发生 TXE 中断时,再从程序的发送buffer 里取出下一个数据写入 TDR。<br>对于接收数据,在一开始就使能“RXNE”中断(接收寄存器非空)。这样, UART 接收到一个数据就会触发中断,在中断程序里读取 RDR 得到数据,存入程序的接收 buffer。当程序向读取串口数据时,它直接读取接收 buffer 即可。<br>这里涉及的“发送 buffer”、“接收 buffer”,特别适合使用“环形 buffer”。 </p><p>在CubeMX使能串口中断</p><h3 id="发送"><a href="#发送" class="headerlink" title="发送"></a>发送</h3><p>发送数据先调用<code>HAL_UART_Transmit_IT</code>,这个函数只会使能TX完成中断</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">HAL_StatusTypeDef <span class="token function">HAL_UART_Transmit_IT</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token keyword">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">/* Check that a Tx process is not already ongoing */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>gState <span class="token operator">==</span> HAL_UART_STATE_READY<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>pData <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span>Size <span class="token operator">==</span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_ERROR<span class="token punctuation">;</span> <span class="token punctuation">}</span> huart<span class="token operator">-></span>pTxBuffPtr <span class="token operator">=</span> pData<span class="token punctuation">;</span> huart<span class="token operator">-></span>TxXferSize <span class="token operator">=</span> Size<span class="token punctuation">;</span> huart<span class="token operator">-></span>TxXferCount <span class="token operator">=</span> Size<span class="token punctuation">;</span> huart<span class="token operator">-></span>ErrorCode <span class="token operator">=</span> HAL_UART_ERROR_NONE<span class="token punctuation">;</span> huart<span class="token operator">-></span>gState <span class="token operator">=</span> HAL_UART_STATE_BUSY_TX<span class="token punctuation">;</span> <span class="token comment">/* Enable the UART Transmit data register empty Interrupt */</span> <span class="token function">__HAL_UART_ENABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_TXE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> HAL_OK<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_BUSY<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>发送完一个字节,会触发一次TXE中断,调用<code>HAL_UART_IRQHandler</code>,进而调用<code>UART_Transmit_IT</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">static</span> HAL_StatusTypeDef <span class="token function">UART_Transmit_IT</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">uint16_t</span> <span class="token operator">*</span>tmp<span class="token punctuation">;</span> <span class="token comment">/* Check that a Tx process is ongoing */</span> <span class="token comment">/*不是最后一个数据*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>gState <span class="token operator">==</span> HAL_UART_STATE_BUSY_TX<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_9B<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>Parity <span class="token operator">==</span> UART_PARITY_NONE<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> tmp <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">uint16_t</span> <span class="token operator">*</span><span class="token punctuation">)</span> huart<span class="token operator">-></span>pTxBuffPtr<span class="token punctuation">;</span> huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token operator">*</span>tmp <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token number">0x01FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> huart<span class="token operator">-></span>pTxBuffPtr <span class="token operator">+=</span> <span class="token number">2U</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token operator">*</span>huart<span class="token operator">-></span>pTxBuffPtr<span class="token operator">++</span> <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token number">0x00FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token comment">/*是最后一个数据*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">--</span>huart<span class="token operator">-></span>TxXferCount <span class="token operator">==</span> <span class="token number">0U</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Disable the UART Transmit Data Register Empty Interrupt */</span> <span class="token function">__HAL_UART_DISABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_TXE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Enable the UART Transmit Complete Interrupt */</span> <span class="token function">__HAL_UART_ENABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_TC<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> HAL_OK<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_BUSY<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>所以发送多少字节,就一共发生多少次TXE中断,用来把数据放到移位寄存器;发生一次TC中断,表明数据全都发出,会调用<code>void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)</code>回调函数</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"> <span class="token keyword">char</span> <span class="token operator">*</span>tx_buffer <span class="token operator">=</span> <span class="token string">"Hello,world!"</span><span class="token punctuation">;</span> <span class="token keyword">char</span> c<span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token keyword">volatile</span> g_tx_cplt <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE END 2 */</span> <span class="token comment">/* Infinite loop */</span> <span class="token comment">/* USER CODE BEGIN WHILE */</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/*使能中断*/</span> <span class="token function">HAL_UART_Transmit_IT</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> tx_buffer<span class="token punctuation">,</span> <span class="token function">strlen</span><span class="token punctuation">(</span>tx_buffer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">Wait_tx_cplt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* USER CODE END WHILE */</span> <span class="token comment">/* USER CODE BEGIN 3 */</span> <span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">HAL_UART_TxCpltCallback</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">)</span> <span class="token punctuation">{</span> g_tx_cplt <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">Wait_tx_cplt</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>g_tx_cplt <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">/*等待中断修改标志位*/</span> g_tx_cplt <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">/*重置标志位*/</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="接收"><a href="#接收" class="headerlink" title="接收"></a>接收</h3><p>调用<code>HAL_UART_Receive_IT</code>,只是使能中断</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">HAL_StatusTypeDef <span class="token function">HAL_UART_Receive_IT</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">,</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token keyword">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">/* Check that a Rx process is not already ongoing */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>RxState <span class="token operator">==</span> HAL_UART_STATE_READY<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>pData <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span>Size <span class="token operator">==</span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_ERROR<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Set Reception type to Standard reception */</span> huart<span class="token operator">-></span>ReceptionType <span class="token operator">=</span> HAL_UART_RECEPTION_STANDARD<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token function">UART_Start_Receive_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> pData<span class="token punctuation">,</span> Size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_BUSY<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span>HAL_StatusTypeDef <span class="token function">UART_Start_Receive_IT</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">,</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token keyword">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">{</span> huart<span class="token operator">-></span>pRxBuffPtr <span class="token operator">=</span> pData<span class="token punctuation">;</span> huart<span class="token operator">-></span>RxXferSize <span class="token operator">=</span> Size<span class="token punctuation">;</span> huart<span class="token operator">-></span>RxXferCount <span class="token operator">=</span> Size<span class="token punctuation">;</span> huart<span class="token operator">-></span>ErrorCode <span class="token operator">=</span> HAL_UART_ERROR_NONE<span class="token punctuation">;</span> huart<span class="token operator">-></span>RxState <span class="token operator">=</span> HAL_UART_STATE_BUSY_RX<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>Parity <span class="token operator">!=</span> UART_PARITY_NONE<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Enable the UART Parity Error Interrupt */</span> <span class="token function">__HAL_UART_ENABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_PE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */</span> <span class="token function">__HAL_UART_ENABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_ERR<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Enable the UART Data Register not empty Interrupt */</span> <span class="token function">__HAL_UART_ENABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_RXNE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> HAL_OK<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>接收到一个字节,会触发RXNE中断,调用<code>HAL_UART_IRQHandler</code>,进而调用<code>UART_Receive_IT</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">static</span> HAL_StatusTypeDef <span class="token function">UART_Receive_IT</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>pdata8bits<span class="token punctuation">;</span> <span class="token keyword">uint16_t</span> <span class="token operator">*</span>pdata16bits<span class="token punctuation">;</span> <span class="token comment">/* Check that a Rx process is ongoing */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>RxState <span class="token operator">==</span> HAL_UART_STATE_BUSY_RX<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_9B<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>Parity <span class="token operator">==</span> UART_PARITY_NONE<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pdata8bits <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span> pdata16bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span> <span class="token operator">*</span><span class="token punctuation">)</span> huart<span class="token operator">-></span>pRxBuffPtr<span class="token punctuation">;</span> <span class="token operator">*</span>pdata16bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token number">0x01FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> huart<span class="token operator">-></span>pRxBuffPtr <span class="token operator">+=</span> <span class="token number">2U</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> pdata8bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span> <span class="token operator">*</span><span class="token punctuation">)</span> huart<span class="token operator">-></span>pRxBuffPtr<span class="token punctuation">;</span> pdata16bits <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_9B<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_8B<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>Parity <span class="token operator">==</span> UART_PARITY_NONE<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata8bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token number">0x00FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata8bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token number">0x007F</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> huart<span class="token operator">-></span>pRxBuffPtr <span class="token operator">+=</span> <span class="token number">1U</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">--</span>huart<span class="token operator">-></span>RxXferCount <span class="token operator">==</span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token comment">/*收完了*/</span> <span class="token punctuation">{</span> <span class="token comment">/* Disable the UART Data Register not empty Interrupt */</span> <span class="token function">__HAL_UART_DISABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_RXNE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Disable the UART Parity Error Interrupt */</span> <span class="token function">__HAL_UART_DISABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_PE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */</span> <span class="token function">__HAL_UART_DISABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_ERR<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Rx process is completed, restore huart->RxState to Ready */</span> huart<span class="token operator">-></span>RxState <span class="token operator">=</span> HAL_UART_STATE_READY<span class="token punctuation">;</span> <span class="token comment">/* Initialize type of RxEvent to Transfer Complete */</span> huart<span class="token operator">-></span>RxEventType <span class="token operator">=</span> HAL_UART_RXEVENT_TC<span class="token punctuation">;</span> <span class="token comment">/* Check current reception Mode : If Reception till IDLE event has been selected : */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>ReceptionType <span class="token operator">==</span> HAL_UART_RECEPTION_TOIDLE<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Set reception type to Standard */</span> huart<span class="token operator">-></span>ReceptionType <span class="token operator">=</span> HAL_UART_RECEPTION_STANDARD<span class="token punctuation">;</span> <span class="token comment">/* Disable IDLE interrupt */</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR1<span class="token punctuation">,</span> USART_CR1_IDLEIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Check if IDLE flag is set */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__HAL_UART_GET_FLAG</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_FLAG_IDLE<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Clear IDLE flag in ISR */</span> <span class="token function">__HAL_UART_CLEAR_IDLEFLAG</span><span class="token punctuation">(</span>huart<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">if</span> <span class="token expression"><span class="token punctuation">(</span>USE_HAL_UART_REGISTER_CALLBACKS <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span></span></span> <span class="token comment">/*Call registered Rx Event callback*/</span> huart<span class="token operator">-></span><span class="token function">RxEventCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> huart<span class="token operator">-></span>RxXferSize<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span></span> <span class="token comment">/*Call legacy weak Rx Event callback*/</span> <span class="token function">HAL_UARTEx_RxEventCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> huart<span class="token operator">-></span>RxXferSize<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span> <span class="token comment">/* USE_HAL_UART_REGISTER_CALLBACKS */</span></span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">/* Standard reception API called */</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">if</span> <span class="token expression"><span class="token punctuation">(</span>USE_HAL_UART_REGISTER_CALLBACKS <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span></span></span> <span class="token comment">/*Call registered Rx complete callback*/</span> huart<span class="token operator">-></span><span class="token function">RxCpltCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span></span> <span class="token comment">/*Call legacy weak Rx complete callback*/</span> <span class="token function">HAL_UART_RxCpltCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span> <span class="token comment">/* USE_HAL_UART_REGISTER_CALLBACKS */</span></span> <span class="token punctuation">}</span> <span class="token keyword">return</span> HAL_OK<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> HAL_OK<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_BUSY<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>调用回调函数</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">HAL_UART_RxCpltCallback</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果有耗时操作,由于无法及时使能中断也会造成数据丢失,因为<code>UART_Receive_IT</code>会执行</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token function">__HAL_UART_DISABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_RXNE<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>来关闭相应的中断,只有再次调用<code>HAL_UART_Receive_IT</code>执行</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token function">__HAL_UART_ENABLE_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_IT_RXNE<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>才能再次打开</p><h2 id="UART中断模式改造"><a href="#UART中断模式改造" class="headerlink" title="UART中断模式改造"></a>UART中断模式改造</h2><ol><li><p>一开始就使能接收中断</p></li><li><p>在接收中断里把数据存入环形Buf,并且重新使能中断</p> <pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">Start_UART_RX</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">HAL_UART_Receive_IT</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>g_rx_data<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">circle_buf_init</span><span class="token punctuation">(</span><span class="token operator">&</span>g_rx_circle_buffer<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> g_rx_buffer<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">HAL_UART_RxCpltCallback</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">circle_buf_write</span><span class="token punctuation">(</span><span class="token operator">&</span>g_rx_circle_buffer<span class="token punctuation">,</span> g_rx_data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">HAL_UART_Receive_IT</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>g_rx_data<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">int</span> <span class="token function">UART_get_char</span><span class="token punctuation">(</span><span class="token keyword">uint8_t</span> <span class="token operator">*</span>pVal<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">circle_buf_read</span><span class="token punctuation">(</span><span class="token operator">&</span>g_rx_circle_buffer<span class="token punctuation">,</span> <span class="token operator">*</span>pVal<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li></ol><h2 id="UART-DMA方式"><a href="#UART-DMA方式" class="headerlink" title="UART DMA方式"></a>UART DMA方式</h2><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedDMA%E6%A1%86%E5%9B%BE.png" alt="DMA框图"></p><p>使用中断方式时,在传输、接收数据时,会发生中断,还需要 CPU 执行中断处理函数,从RAM的buf读数据写到串口模块,就会消耗CPU资源,影响main函数。 </p><p>DMA会从内存读数据,再写给TDR寄存器,串口发送完成会产生DMA请求,让DMA搬运下一个数据;从RDR寄存器读数据并存入内存。指定量的数据传输完成后,才会产生中断,让程序介入</p><p>设置好 DMA(源、目的、地址增减方向、每次读取数据的长度、读取次数)后, DMA 就会自动地在 SRAM 和 UART 之间传递数据:<br>① 发送时: DMA 从 SRAM 得到数据,写入 UART 的 TDR 寄存器<br>② 接收时: DMA 从 UART 的 RDR 寄存器得到数据,写到 SRAM 去<br>③ 指定的数据传输完毕后,触发 DMA 中断;在数据传输过程中,没有中断, CPU 无需处理。 </p><p>函数如下:</p><table><thead><tr><th></th><th>查询方式</th><th>中断方式</th><th>DMA 方式</th></tr></thead><tbody><tr><td>发送</td><td>HAL_UART_Transmit</td><td>HAL_UART_Transmit_IT HAL_UART_TxCpltCallback</td><td>HAL_UART_Transmit_DMA HAL_UART_TxHalfCpltCallback HAL_UART_TxCpltCallback</td></tr><tr><td>接收</td><td>HAL_UART_Receive</td><td>HAL_UART_Receive_IT HAL_UART_RxCpltCallback</td><td>HAL_UART_Receive_DMA HAL_UART_RxHalfCpltCallback HAL_UART_RxCpltCallback</td></tr><tr><td>错误</td><td></td><td>HAL_UART_ErrorCallback</td><td>HAL_UART_ErrorCallback</td></tr></tbody></table><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedDMA.png" alt="DMA"></p><p>步骤:</p><ol><li><p>调用<code>HAL_UART_Transmit_DMA </code>开启传输</p></li><li><p>CPU做别的事</p></li><li><p>中断调用<code>XferCpltCallback</code>,它已经被设置成<code>UART_DMATransmitCplt</code></p> <pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">huart<span class="token operator">-></span>hdmatx<span class="token operator">-></span>XferCpltCallback <span class="token operator">=</span> UART_DMATransmitCplt<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p> 而<code>UART_DMATransmitCplt</code>又被设置成<code>HAL_UART_TxCpltCallback(huart);</code>,和之前的IT方式使用方法一样,可以在其中设置某个变量表明传输完成</p> <pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">UART_DMATransmitCplt</span><span class="token punctuation">(</span>DMA_HandleTypeDef <span class="token operator">*</span>hdma<span class="token punctuation">)</span><span class="token punctuation">{</span> UART_HandleTypeDef <span class="token operator">*</span>huart <span class="token operator">=</span> <span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span>DMA_HandleTypeDef <span class="token operator">*</span><span class="token punctuation">)</span>hdma<span class="token punctuation">)</span><span class="token operator">-></span>Parent<span class="token punctuation">;</span> <span class="token comment">/* DMA Normal mode*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>hdma<span class="token operator">-></span>Instance<span class="token operator">-></span>CCR <span class="token operator">&</span> DMA_CCR_CIRC<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0U</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> huart<span class="token operator">-></span>TxXferCount <span class="token operator">=</span> <span class="token number">0x00U</span><span class="token punctuation">;</span> <span class="token comment">/* Disable the DMA transfer for transmit request by setting the DMAT bit in the UART CR3 register */</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR3<span class="token punctuation">,</span> USART_CR3_DMAT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Enable the UART Transmit Complete Interrupt */</span> <span class="token function">ATOMIC_SET_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR1<span class="token punctuation">,</span> USART_CR1_TCIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* DMA Circular mode */</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">if</span> <span class="token expression"><span class="token punctuation">(</span>USE_HAL_UART_REGISTER_CALLBACKS <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span></span></span> <span class="token comment">/*Call registered Tx complete callback*/</span> huart<span class="token operator">-></span><span class="token function">TxCpltCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span></span> <span class="token comment">/*Call legacy weak Tx complete callback*/</span> <span class="token function">HAL_UART_TxCpltCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span> <span class="token comment">/* USE_HAL_UART_REGISTER_CALLBACKS */</span></span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li></ol><h2 id="UART-DMA-IDLE"><a href="#UART-DMA-IDLE" class="headerlink" title="UART DMA+IDLE"></a>UART DMA+IDLE</h2><p>DMA读数据:不知道发来多少数据,没法指定Size参数</p><p>如果DMA发现一定时间内串口电平没变化,就会发送<code>IDLE空闲中断</code> </p><p>例如,设置DMA接收1000字节,完成标志有三种</p><ol><li>收到了1000字节</li><li>收到了IDLE中断</li><li>错误</li></ol><p>IDLE, 空闲的定义是: 总线上在一个字节的时间内没有再接收到数据。<br>UART 的 IDLE 中断何时发生? RxD 引脚一开始就是空闲的啊,难道 IDLE 中断一直产生?<br>不是的。当我们使能 IDLE 中断后,它并不会立刻产生,而是:至少收到 1 个数据后,发现在一个字节的时间里,都没有接收到新数据,才会产生 IDLE 中断。<br>我们使用 DMA 接收数据时,确实可以提高 CPU 的效率,但是“无法预知要接收多少数据”,而我们想尽快处理接收到的数据。怎么办?比如我想读取 100 字节的数据,但是接收到 60 字节后对方就不再发送数据了,怎么办?我们怎么判断数据传输中止了?可以使用IDLE 中断。 在这种情况下, DMA 传输结束的条件有 3:<br>① 接收完指定数量的数据了,比如收到了 100 字节的数据了, HAL_UART_RxCpltCallback<br>被调用<br>② 总线空闲了: HAL_UARTEx_RxEventCallback 被调用<br>③ 发生了错误: HAL_UART_ErrorCallback 被调用 </p><p>使用 IDLE 状态来接收的函数有:</p><table><thead><tr><th></th><th>函数</th><th>回调函数</th></tr></thead><tbody><tr><td>查询方式</td><td>HAL_UARTEx_ReceiveToIdle</td><td>根据返回参数 RxLen 判断是否接收完毕,还是因为空闲而返回</td></tr><tr><td>中断方式</td><td>HAL_UARTEx_ReceiveToIdle_IT</td><td>完毕: HAL_UART_RxCpltCallback <br />因为空闲而中止: HAL_UARTEx_RxEventCallback</td></tr><tr><td>DMA 方式</td><td>HAL_UARTEx_ReceiveToIdle_DMA</td><td>传输一半: HAL_UART_RxHalfCpltCallback <br />完毕: HAL_UART_RxCpltCallback <br />因为空闲而中止: HAL_UARTEx_RxEventCallback</td></tr><tr><td>错误</td><td></td><td>HAL_UART_ErrorCallback</td></tr></tbody></table><p>查询方式</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">while</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>RxXferCount <span class="token operator">></span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">/* Check if IDLE flag is set */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__HAL_UART_GET_FLAG</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_FLAG_IDLE<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Clear IDLE flag in ISR */</span> <span class="token function">__HAL_UART_CLEAR_IDLEFLAG</span><span class="token punctuation">(</span>huart<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* If Set, but no data ever received, clear flag without exiting loop */</span> <span class="token comment">/* If Set, and data has already been received, this means Idle Event is valid : End reception */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">*</span>RxLen <span class="token operator">></span> <span class="token number">0U</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> huart<span class="token operator">-></span>RxEventType <span class="token operator">=</span> HAL_UART_RXEVENT_IDLE<span class="token punctuation">;</span> huart<span class="token operator">-></span>RxState <span class="token operator">=</span> HAL_UART_STATE_READY<span class="token punctuation">;</span> <span class="token keyword">return</span> HAL_OK<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">/* Check if RXNE flag is set */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__HAL_UART_GET_FLAG</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> UART_FLAG_RXNE<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pdata8bits <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata16bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token number">0x01FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> pdata16bits<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_9B<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>WordLength <span class="token operator">==</span> UART_WORDLENGTH_8B<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>Init<span class="token punctuation">.</span>Parity <span class="token operator">==</span> UART_PARITY_NONE<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata8bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token number">0x00FF</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token operator">*</span>pdata8bits <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>DR <span class="token operator">&</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span><span class="token punctuation">)</span><span class="token number">0x007F</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> pdata8bits<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Increment number of received elements */</span> <span class="token operator">*</span>RxLen <span class="token operator">+=</span> <span class="token number">1U</span><span class="token punctuation">;</span> huart<span class="token operator">-></span>RxXferCount<span class="token operator">--</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>中断方式</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">HAL_StatusTypeDef <span class="token function">HAL_UARTEx_ReceiveToIdle_IT</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">,</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token keyword">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">{</span> HAL_StatusTypeDef status<span class="token punctuation">;</span> <span class="token comment">/* Check that a Rx process is not already ongoing */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>RxState <span class="token operator">==</span> HAL_UART_STATE_READY<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>pData <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span>Size <span class="token operator">==</span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_ERROR<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Set Reception type to reception till IDLE Event*/</span> huart<span class="token operator">-></span>ReceptionType <span class="token operator">=</span> HAL_UART_RECEPTION_TOIDLE<span class="token punctuation">;</span> huart<span class="token operator">-></span>RxEventType <span class="token operator">=</span> HAL_UART_RXEVENT_TC<span class="token punctuation">;</span> status <span class="token operator">=</span> <span class="token function">UART_Start_Receive_IT</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> pData<span class="token punctuation">,</span> Size<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Check Rx process has been successfully started */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>status <span class="token operator">==</span> HAL_OK<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>ReceptionType <span class="token operator">==</span> HAL_UART_RECEPTION_TOIDLE<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">__HAL_UART_CLEAR_IDLEFLAG</span><span class="token punctuation">(</span>huart<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">ATOMIC_SET_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR1<span class="token punctuation">,</span> USART_CR1_IDLEIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">/* In case of errors already pending when reception is started, Interrupts may have already been raised and lead to reception abortion. (Overrun error for instance). In such case Reception Type has been reset to HAL_UART_RECEPTION_STANDARD. */</span> status <span class="token operator">=</span> HAL_ERROR<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> status<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> HAL_BUSY<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>↑使能IDLE中断</p><p>IDLE中断发生后,如果使用DMA,就取消DMA;如果使用中断,就禁止接收中断,最后调用回调函数<code>HAL_UARTEx_RxEventCallback</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"> <span class="token comment">/* Check if DMA mode is enabled in UART */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">HAL_IS_BIT_SET</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR3<span class="token punctuation">,</span> USART_CR3_DMAR<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* DMA mode enabled */</span> <span class="token comment">/* Check received length : If all expected data are received, do nothing, (DMA cplt callback will be called). Otherwise, if at least one data has already been received, IDLE event is to be notified to user */</span> <span class="token keyword">uint16_t</span> nb_remaining_rx_data <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span> <span class="token function">__HAL_DMA_GET_COUNTER</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>hdmarx<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>nb_remaining_rx_data <span class="token operator">></span> <span class="token number">0U</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>nb_remaining_rx_data <span class="token operator"><</span> huart<span class="token operator">-></span>RxXferSize<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Reception is not complete */</span> huart<span class="token operator">-></span>RxXferCount <span class="token operator">=</span> nb_remaining_rx_data<span class="token punctuation">;</span> <span class="token comment">/* In Normal mode, end DMA xfer and HAL UART Rx process*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>hdmarx<span class="token operator">-></span>Init<span class="token punctuation">.</span>Mode <span class="token operator">!=</span> DMA_CIRCULAR<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Disable PE and ERR (Frame error, noise error, overrun error) interrupts */</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR1<span class="token punctuation">,</span> USART_CR1_PEIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR3<span class="token punctuation">,</span> USART_CR3_EIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Disable the DMA transfer for the receiver request by resetting the DMAR bit in the UART CR3 register */</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR3<span class="token punctuation">,</span> USART_CR3_DMAR<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* At end of Rx process, restore huart->RxState to Ready */</span> huart<span class="token operator">-></span>RxState <span class="token operator">=</span> HAL_UART_STATE_READY<span class="token punctuation">;</span> huart<span class="token operator">-></span>ReceptionType <span class="token operator">=</span> HAL_UART_RECEPTION_STANDARD<span class="token punctuation">;</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR1<span class="token punctuation">,</span> USART_CR1_IDLEIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Last bytes received, so no need as the abort is immediate */</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token function">HAL_DMA_Abort</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>hdmarx<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Initialize type of RxEvent that correspond to RxEvent callback execution; In this case, Rx Event type is Idle Event */</span> huart<span class="token operator">-></span>RxEventType <span class="token operator">=</span> HAL_UART_RXEVENT_IDLE<span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">if</span> <span class="token expression"><span class="token punctuation">(</span>USE_HAL_UART_REGISTER_CALLBACKS <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span></span></span> <span class="token comment">/*Call registered Rx Event callback*/</span> huart<span class="token operator">-></span><span class="token function">RxEventCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>RxXferSize <span class="token operator">-</span> huart<span class="token operator">-></span>RxXferCount<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span></span> <span class="token comment">/*Call legacy weak Rx Event callback*/</span> <span class="token function">HAL_UARTEx_RxEventCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> <span class="token punctuation">(</span>huart<span class="token operator">-></span>RxXferSize <span class="token operator">-</span> huart<span class="token operator">-></span>RxXferCount<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span> <span class="token comment">/* USE_HAL_UART_REGISTER_CALLBACKS */</span></span> <span class="token punctuation">}</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">/* DMA mode not enabled */</span> <span class="token comment">/* Check received length : If all expected data are received, do nothing. Otherwise, if at least one data has already been received, IDLE event is to be notified to user */</span> <span class="token keyword">uint16_t</span> nb_rx_data <span class="token operator">=</span> huart<span class="token operator">-></span>RxXferSize <span class="token operator">-</span> huart<span class="token operator">-></span>RxXferCount<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>RxXferCount <span class="token operator">></span> <span class="token number">0U</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>nb_rx_data <span class="token operator">></span> <span class="token number">0U</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* Disable the UART Parity Error Interrupt and RXNE interrupts */</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR1<span class="token punctuation">,</span> <span class="token punctuation">(</span>USART_CR1_RXNEIE <span class="token operator">|</span> USART_CR1_PEIE<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR3<span class="token punctuation">,</span> USART_CR3_EIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Rx process is completed, restore huart->RxState to Ready */</span> huart<span class="token operator">-></span>RxState <span class="token operator">=</span> HAL_UART_STATE_READY<span class="token punctuation">;</span> huart<span class="token operator">-></span>ReceptionType <span class="token operator">=</span> HAL_UART_RECEPTION_STANDARD<span class="token punctuation">;</span> <span class="token function">ATOMIC_CLEAR_BIT</span><span class="token punctuation">(</span>huart<span class="token operator">-></span>Instance<span class="token operator">-></span>CR1<span class="token punctuation">,</span> USART_CR1_IDLEIE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Initialize type of RxEvent that correspond to RxEvent callback execution; In this case, Rx Event type is Idle Event */</span> huart<span class="token operator">-></span>RxEventType <span class="token operator">=</span> HAL_UART_RXEVENT_IDLE<span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">if</span> <span class="token expression"><span class="token punctuation">(</span>USE_HAL_UART_REGISTER_CALLBACKS <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span></span></span> <span class="token comment">/*Call registered Rx complete callback*/</span> huart<span class="token operator">-></span><span class="token function">RxEventCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> nb_rx_data<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span></span> <span class="token comment">/*Call legacy weak Rx Event callback*/</span> <span class="token function">HAL_UARTEx_RxEventCallback</span><span class="token punctuation">(</span>huart<span class="token punctuation">,</span> nb_rx_data<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span> <span class="token comment">/* USE_HAL_UART_REGISTER_CALLBACKS */</span></span> <span class="token punctuation">}</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="接收-1"><a href="#接收-1" class="headerlink" title="接收"></a>接收</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">Start_UART_RX</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">HAL_UARTEx_ReceiveToIdle_DMA</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>g_rx_temp_buf<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">circle_buf_init</span><span class="token punctuation">(</span><span class="token operator">&</span>g_rx_circle_buffer<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> g_rx_buffer<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">/* 数据暂时发完了,存入环形缓冲区,收到了Size个数据 */</span><span class="token keyword">void</span> <span class="token function">HAL_UARTEx_RxEventCallback</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">,</span> <span class="token keyword">uint16_t</span> Size<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> Size<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">circle_buf_write</span><span class="token punctuation">(</span><span class="token operator">&</span>g_rx_circle_buffer<span class="token punctuation">,</span> g_rx_temp_buf<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">HAL_UARTEx_ReceiveToIdle_DMA</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>g_rx_temp_buf<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">/*接收完毕还是调用这个回调函数*/</span><span class="token keyword">void</span> <span class="token function">HAL_UART_RxCpltCallback</span><span class="token punctuation">(</span>UART_HandleTypeDef <span class="token operator">*</span>huart<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">circle_buf_write</span><span class="token punctuation">(</span><span class="token operator">&</span>g_rx_circle_buffer<span class="token punctuation">,</span> g_rx_temp_buf<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">HAL_UARTEx_ReceiveToIdle_DMA</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>g_rx_temp_buf<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">/* use dma to tx */</span> <span class="token function">HAL_UART_Transmit_DMA</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> str2<span class="token punctuation">,</span> <span class="token function">strlen</span><span class="token punctuation">(</span>str2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* wait for tc */</span> <span class="token function">Wait_Tx_Complete</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//while (HAL_OK != HAL_UART_Receive(&huart1, &c, 1, 100));</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">0</span> <span class="token operator">!=</span> <span class="token function">UART1GetChar</span><span class="token punctuation">(</span><span class="token operator">&</span>c<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> c <span class="token operator">=</span> c<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token function">HAL_UART_Transmit</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token operator">&</span>c<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">HAL_UART_Transmit</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token string">"\r\n"</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="printf重定向"><a href="#printf重定向" class="headerlink" title="printf重定向"></a>printf重定向</h2><p>勾选<code>use micro lib</code></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token keyword">int</span> ch<span class="token punctuation">,</span> FILE<span class="token operator">*</span> stream<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">HAL_UART_Transmit</span><span class="token punctuation">(</span><span class="token operator">&</span>huart1<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">uint8_t</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token operator">&</span>ch<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> ch<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">static</span> <span class="token keyword">int</span> g_last_char<span class="token punctuation">;</span><span class="token keyword">static</span> <span class="token keyword">int</span> g_backspace <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token keyword">int</span> <span class="token function">fgetc</span><span class="token punctuation">(</span>FILE <span class="token operator">*</span>f<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">int</span> ch<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>g_backspace<span class="token punctuation">)</span> <span class="token punctuation">{</span> g_backspace <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">return</span> g_last_char<span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">0</span> <span class="token operator">!=</span> <span class="token function">UART1GetChar</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">uint8_t</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token operator">&</span>ch<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> g_last_char <span class="token operator">=</span> ch<span class="token punctuation">;</span> <span class="token keyword">return</span> ch<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">int</span> <span class="token function">__backspace</span><span class="token punctuation">(</span>FILE <span class="token operator">*</span>stream<span class="token punctuation">)</span><span class="token punctuation">{</span> g_backspace <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="I2C"><a href="#I2C" class="headerlink" title="I2C"></a>I2C</h2><h3 id="I2C程序层次"><a href="#I2C程序层次" class="headerlink" title="I2C程序层次"></a>I2C程序层次</h3><p>半双工,因为只有一跟数据线,发送的同时不能接收</p><p>一主多从</p><h4 id="硬件原理图"><a href="#硬件原理图" class="headerlink" title="硬件原理图"></a>硬件原理图</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210171955849.png" alt="image-20260210171955849"></p><h4 id="硬件框图"><a href="#硬件框图" class="headerlink" title="硬件框图"></a>硬件框图</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210172106323.png" alt="image-20260210172106323"></p><p>编写程序,通过I2C初始化SSD1306,然后把数据写到芯片里面的显存,芯片会把显存的数据刷到屏幕上</p><h4 id="软件层次"><a href="#软件层次" class="headerlink" title="软件层次"></a>软件层次</h4><p>要在 OLED 上显示字符串,涉及这几个层次的程序: </p><p>① 应用程序:我来决定“在哪个位置、显示什么字符” </p><p>② 库函数(字符/图片显示):我收集了字符点阵,知道发送点阵数据才能显示字符 </p><p><code>OLED_PrintString</code></p><p>③ OLED(SSD1306)取出程序:我知道发送什么 I2C 数据(先设置地址、再发送数据),才能 把点阵数据写入显存 </p><p>④ I2C 控制器驱动(HAL):我可以发送 I2C 数据</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210172348161.png" alt="image-20260210172348161"></p><h3 id="I2C协议"><a href="#I2C协议" class="headerlink" title="I2C协议"></a>I2C协议</h3><p>I2C(Inter-Integrated Circuit,又称 IIC)总线是一种由 PHILIPS 公司开发的串行总 线,用于连接微控制器及其外围设备,它具有如下特点: </p><ul><li>只有两条总线线路:一条串行数据线(SDA),一条串行时钟线(SCL)。 </li><li>每个连接到总线的器件都可以使用软件根据它的惟一的地址来识别。</li><li>传输数据的设备间是简单的主/从关系。</li><li>主机可以用作主机发送器或主机接收器。</li><li>它是一个真正的多主机总线,两个或多个主机同时发起数据传输时,可以通过冲 突检测和仲裁来防止数据被破坏。</li><li>串行的 8 位双向数据传输,位速率在标准模式下可达 100kbit/s,在快速模式下 可达 400kbit/s,在高速模式下可达 3.4Mbit/s。</li><li>片上的滤波器可以增加抗干扰功能,保证数据的完整。</li><li>连接到同一总线上的 IC 数量只受到总线的最大电容 400pF 的限制。</li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210172803542.png" alt="image-20260210172803542"></p><p>先说明一些术语,</p><ul><li>发送器:发送数据到总线的器件</li><li>接收器:从总线接收数据的器件</li><li>主机:发起/停止数据传输、提供时钟信号的器件</li><li>从机:被主机寻址的器件</li><li>多主机:可以有多个主机试图去控制总线,但是不会破坏数据</li><li>仲裁:当多个主机试图去控制总线时,通过仲裁可以使得只有一个主机获得总线 控制权,并且它传输的信息不被破坏</li><li>同步:多个器件同步时钟信号的过程</li></ul><h4 id="传输数据类比"><a href="#传输数据类比" class="headerlink" title="传输数据类比"></a>传输数据类比</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210173202435.png" alt="image-20260210173202435"></p><p>体育老师:可以把球发给学生,也可以把球从学生中接过来。 ⚫ </p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210173332101.png" alt="image-20260210173332101"></p><p>都是老师主动发起,学生被动接受</p><p>依照这个简单的例子,可以引入 IIC 的传输协议: </p><ol><li>老师说开始了,表示开始信号(start)</li><li>老师提醒某个学生要发球,表示发送地址和方向(address/read/write)</li><li>老师发球/接球,表示数据的传输</li><li>到球要回应:回应信号(ACK)</li><li>老师说结束,表示 IIC 传输结束(P)</li></ol><h4 id="数据格式"><a href="#数据格式" class="headerlink" title="数据格式"></a>数据格式</h4><h5 id="写操作"><a href="#写操作" class="headerlink" title="写操作"></a>写操作</h5><ol><li>主芯片发出start信号</li><li>发出设备地址,方向(0表示写;1表示读)</li><li>从设备回应(用来确定这个设备是否存在),然后就能传输数据</li><li>主设备发送8bit数据给从设备,并等待回应</li><li>每传输一字节数据,接收方要有一个回应信号(确认数据是否接受完成),然后再传输下一个数据</li><li>数据发送完成后,主芯片会发送一个停止信号</li></ol><p>白色背景是主->从,灰色背景是从->主</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210173604130.png" alt="image-20260210173604130"></p><h5 id="读操作"><a href="#读操作" class="headerlink" title="读操作"></a>读操作</h5><ol><li>主芯片发出start信号</li><li>发出设备地址,方向(0表示写;1表示读)</li><li>从设备回应(用来确定这个设备是否存在),然后就能传输数据</li><li>从设备发送8bit数据给主设备,并等待回应</li><li>每传输一字节数据,接收方要有一个回应信号(确认数据是否接受完成),然后再传输下一个数据</li><li>数据发送完成后,主芯片会发送一个停止信号</li></ol><p>白色背景是主->从,灰色背景是从->主</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210174332886.png" alt="image-20260210174332886"></p><p>无论读写,都要先发设备地址和方向</p><h4 id="I2C信号"><a href="#I2C信号" class="headerlink" title="I2C信号"></a>I2C信号</h4><p>I2C 总线在传送数据过程中共有三种类型信号:开始信号、结束信号和响应信号。</p><ol><li><p>开始信号(S):SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。</p></li><li><p>结束信号(P):SCL 为低电平时,SDA 由低电平向高电平跳变,结束传送数据。</p></li><li><p>响应信号(ACK):接收器在接收到 8 位数据后,在第 9 个时钟周期,拉低 SDA 电平。 </p><p> 它们的波形如图所示</p></li></ol><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210174702250.png" alt="image-20260210174702250"></p><blockquote><p>[!NOTE]</p><p>SDA 上传输的数据必须在 SCL 为高电平期间保持稳定,SDA 上的数据只能在 SCL 为低电 平期间变化</p></blockquote><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210174754053.png" alt="image-20260210174754053"></p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210175328251.png" alt="image-20260210175328251"></p><p>例如,写:0b1010100</p><p>SCL低电平,SDA可以改变;SCL高电平,SDA保持稳定</p><p>第一个字节前7位是地址,第八位表示方向。第八位发完,主设备释放SDA,从设备驱动SDA,拉低进行回应</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210180549842.png" alt="image-20260210180549842"></p><p>SDA线、SCL线都是开漏接法</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210180646793.png" alt="image-20260210180646793"></p><p>二者的漏极都连接到总线,默认高电平,这样的话如果一方拉低,不会造成短路</p><p>SCL线也可以由双方拉低,双方任意一方觉得自己没准备好,要等一下就可以拉低SCL</p><p>协议并没有规定数据表示什么</p><p>例如,读写存储器,可以先发写入数据的地址,然后再发很多要写入的数据</p><h3 id="STM32I2C硬件结构"><a href="#STM32I2C硬件结构" class="headerlink" title="STM32I2C硬件结构"></a>STM32I2C硬件结构</h3><p>GPIO模拟IIC:根据波形图编写函数,如发出Start信号、Stop信号、发送给一个字节等函数</p><h4 id="硬件框图-1"><a href="#硬件框图-1" class="headerlink" title="硬件框图"></a>硬件框图</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210181455100.png" alt="image-20260210181455100"></p><p>发数据:数据写入右上角的数据寄存器</p><p>读数据:读取寄存器即可</p><p>配置:</p><ol><li>CR1,CR2设置基准时钟T</li><li>CCR设置高低电平时间,多少个T</li><li>状态寄存器是只读的</li></ol><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210182046587.png" alt="image-20260210182046587"></p><p>根据手册,设置控制寄存器1(CR1)的第八位START信号为1,即可产生start信号。读取SR1的第0位表示结束了。然后把设备地址写入data register寄存器</p><p>如何判断得到回应了吗:SR1的某一位表明</p><h3 id="I2C-HAL-编程"><a href="#I2C-HAL-编程" class="headerlink" title="I2C HAL 编程"></a>I2C HAL 编程</h3><h4 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h4><p>读B:</p><ol><li>A把地址发给B</li><li>B把数据发给A</li></ol><p>读数据的时候,发送和接受放在一个函数里面实现:mem模式(只是一个软件上的概念)</p><h4 id="CubeMX-配置"><a href="#CubeMX-配置" class="headerlink" title="CubeMX 配置"></a>CubeMX 配置</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260210183149203.png" alt="image-20260210183149203"></p><h4 id="查询方式"><a href="#查询方式" class="headerlink" title="查询方式"></a>查询方式</h4><h5 id="Master模式"><a href="#Master模式" class="headerlink" title="Master模式"></a>Master模式</h5><pre class="line-numbers language-c" data-language="c"><code class="language-c">HAL_StatusTypeDef <span class="token function">HAL_I2C_Master_Transmit</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> DevAddress<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">,</span> <span class="token class-name">uint32_t</span> Timeout<span class="token punctuation">)</span><span class="token punctuation">;</span> HAL_StatusTypeDef <span class="token function">HAL_I2C_Master_Receive</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> DevAddress<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">,</span> <span class="token class-name">uint32_t</span> Timeout<span class="token punctuation">)</span><span class="token punctuation">;</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>参数说明: </p><ol><li>I2C_HandleTypeDef *hi2c 使用哪个 I2C 控制器</li><li>uint16_t DevAddress 设备地址,需要把 7 位地址左移 1 位</li><li>uint8_t *pData 数据 buffer </li><li>uint16_t Size 数据个数 </li><li>uint32_t Timeout 超时时间,单位是 Tick,一般是 1ms </li><li>返回值 HAL_OK:成功 HAL_ERROR:错误 HAL_BUSY:总线忙 HAL_TIMEOUT:超时</li></ol><h5 id="读写MPU6050"><a href="#读写MPU6050" class="headerlink" title="读写MPU6050"></a>读写MPU6050</h5><ol><li>写</li></ol><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260215234041812.png" alt="image-20260215234041812"></p><ol start="2"><li>读</li></ol><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260215234142030.png" alt="image-20260215234142030"></p><ol start="3"><li>地址</li></ol><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260215234215164.png" alt="image-20260215234215164"></p><ol start="4"><li>寄存器</li></ol><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260215234242553.png" alt="image-20260215234242553"></p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token class-name">uint8_t</span> reg <span class="token operator">=</span> <span class="token number">0x75</span><span class="token punctuation">;</span><span class="token class-name">uint8_t</span> val <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token function">HAL_I2C_Master_Transmit</span><span class="token punctuation">(</span><span class="token operator">&</span>hi2c1<span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token number">0x68</span> <span class="token operator"><<</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token operator">&</span>reg<span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">10000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">HAL_I2C_Master_Transmit</span><span class="token punctuation">(</span><span class="token operator">&</span>hi2c1<span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token number">0x68</span> <span class="token operator"><<</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token operator">&</span>val<span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">10000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h5 id="MEM模式"><a href="#MEM模式" class="headerlink" title="MEM模式"></a>MEM模式</h5><p>MEM 模式只是软件上的概念,仍然是作为 Master 读写数据。 上述函数只能发起单向的数据传输,但是在实际编程中,比如要“读存储地址 addr 上 的数值”,涉及 2 个方向的数据传输:要向 I2C 设备发起写操作把“存储地址 addr”发给 他,然后向 I2C 设备发起读操作得到数值。 还 有 一 种 情 况 ,“ 把 数 值 val 写 到 存 储 地 址 addr ” 上 , 使 用“HAL_I2C_Master_Transmit”函数时,需要在数据 buffer 里填入 addr 和 val,不太直观。 这时可以使用下面的函数:</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">HAL_StatusTypeDef <span class="token function">HAL_I2C_Mem_Read</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> DevAddress<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> MemAddress<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> MemAddSize<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">,</span> <span class="token class-name">uint32_t</span> Timeout<span class="token punctuation">)</span><span class="token punctuation">;</span> HAL_StatusTypeDef <span class="token function">HAL_I2C_Mem_Write</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> DevAddress<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> MemAddress<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> MemAddSize<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">,</span> <span class="token class-name">uint32_t</span> Timeout<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p> 参数说明: </p><ol><li>I2C_HandleTypeDef *hi2c 使用哪个 I2C 控制器 </li><li>uint16_t DevAddress 设备地址,需要把 7 位地址左移 1 位 </li><li>uint16_t MemAddress 存储地址,即 I2C 设备上的寄存器地址或 存储地址 </li><li>uint16_t MemAddSize 存储地址的大小,有 2 种取值: <ol><li>I2C_MEMADD_SIZE_8BIT:存储地址为 1 字 节,就是 MemAddress 的低字节</li><li>I2C_MEMADD_SIZE_16BIT:存储地址为 2 字 节,先发送 MemAddress 的高字节,再发送 低字节</li></ol></li><li>uint8_t *pData 数据 buffer</li><li>uint16_t Size 数据个数 </li><li>uint32_t Timeout 超时时间,单位是 Tick,一般是 1ms </li><li>返回值 HAL_OK:成功 HAL_ERROR:错误 HAL_BUSY:总线忙 HAL_TIMEOUT:超时</li></ol><pre class="line-numbers language-c" data-language="c"><code class="language-c">val <span class="token operator">=</span> <span class="token number">0x55</span><span class="token punctuation">;</span><span class="token function">HAL_I2C_Mem_Write</span><span class="token punctuation">(</span><span class="token operator">&</span>hi2c1<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0x68</span><span class="token operator"><<</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0x31</span><span class="token punctuation">,</span> I2C_MEMADD_SIZE_8BIT<span class="token punctuation">,</span> <span class="token operator">&</span>val<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>val <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token function">HAL_I2C_Mem_Read</span><span class="token punctuation">(</span><span class="token operator">&</span>hi2c1<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0x68</span><span class="token operator"><<</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0x31</span><span class="token punctuation">,</span> I2C_MEMADD_SIZE_8BIT<span class="token punctuation">,</span> <span class="token operator">&</span>val<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="中断模式"><a href="#中断模式" class="headerlink" title="中断模式"></a>中断模式</h4><h5 id="Master模式-1"><a href="#Master模式-1" class="headerlink" title="Master模式"></a>Master模式</h5><p>使用中断方式写 I2C 设备、读 I2C 设备函数原型如下: </p><pre class="line-numbers language-c" data-language="c"><code class="language-c">HAL_StatusTypeDef <span class="token function">HAL_I2C_Master_Transmit_IT</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> DevAddress<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">;</span> HAL_StatusTypeDef <span class="token function">HAL_I2C_Master_Receive_IT</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> DevAddress<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p> 参数说明:</p><ol><li><p>I2C_HandleTypeDef *hi2c 使用哪个 I2C 控制器</p></li><li><p>uint16_t DevAddress 设备地址,需要把 7 位地址左移 1 位 </p></li><li><p>uint8_t *pData 数据 buffer *</p></li><li><p>*uint16_t Size 数据个数 </p></li><li><p>返回值 HAL_OK:成功 HAL_BUSY:总线忙</p></li></ol><blockquote><p>[!NOTE]</p><p>这两个函数只是发出 Start 信号,后续完全由中断程序来传输后续数据。这两个函数 返回后,并不表示传输已经完成,需要在中断回调函数里判断。</p></blockquote><p>要使用中断方式,还需要使用 STM32CubeMX 配置 I2C 中断,如下:</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260215235800555.png" alt="image-20260215235800555"></p><p> 如果传输完成或出错,如下回调函数会被调用: </p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">/* 主设备发送完成回调函数 */</span> <span class="token keyword">void</span> <span class="token function">HAL_I2C_MasterTxCpltCallback</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 主设备接收完成回调函数 */</span> <span class="token keyword">void</span> <span class="token function">HAL_I2C_MasterRxCpltCallback</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 出错回调函数 */</span> <span class="token keyword">void</span> <span class="token function">HAL_I2C_ErrorCallback</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">)</span> <span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>可以在这些回调函数里设置变量,表示传输已经完成。</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">static</span> <span class="token keyword">volatile</span> <span class="token keyword">int</span> g_i2c1_tx_complete <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token keyword">static</span> <span class="token keyword">volatile</span> <span class="token keyword">int</span> g_i2c1_rx_complete <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token comment">/* 主设备发送完成回调函数 */</span><span class="token keyword">void</span> <span class="token function">HAL_I2C_MasterTxCpltCallback</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">if</span> <span class="token punctuation">(</span>hi2c <span class="token operator">==</span> <span class="token operator">&</span>hi2c1<span class="token punctuation">)</span><span class="token punctuation">{</span>g_i2c1_tx_complete <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">Wait_I2C1Tx_Complete</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">while</span> <span class="token punctuation">(</span>g_i2c1_tx_complete <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>g_i2c1_tx_complete <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">/* 主设备接收完成回调函数 */</span><span class="token keyword">void</span> <span class="token function">HAL_I2C_MasterRxCpltCallback</span><span class="token punctuation">(</span>I2C_HandleTypeDef <span class="token operator">*</span>hi2c<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">if</span> <span class="token punctuation">(</span>hi2c <span class="token operator">==</span> <span class="token operator">&</span>hi2c1<span class="token punctuation">)</span><span class="token punctuation">{</span>g_i2c1_rx_complete <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">Wait_I2C1Rx_Complete</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">while</span> <span class="token punctuation">(</span>g_i2c1_rx_complete <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>g_i2c1_rx_complete <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="SPI"><a href="#SPI" class="headerlink" title="SPI"></a>SPI</h2><h3 id="SPI程序层次"><a href="#SPI程序层次" class="headerlink" title="SPI程序层次"></a>SPI程序层次</h3><h4 id="硬件原理图-1"><a href="#硬件原理图-1" class="headerlink" title="硬件原理图"></a>硬件原理图</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216000254948.png" alt="image-20260216000254948"></p><h4 id="硬件框图-2"><a href="#硬件框图-2" class="headerlink" title="硬件框图"></a>硬件框图</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216000316653.png" alt="image-20260216000316653"></p><h4 id="软件层次-1"><a href="#软件层次-1" class="headerlink" title="软件层次"></a>软件层次</h4><p>要操作 Flash,涉及这几个层次的程序:</p><ol><li>应用程序:我来决定“在哪个位置、读写什么数据”</li><li>Flash 驱动:我知道发送什么格式的 SPI 数据才能读、写、擦除 Flash</li><li>SPI 控制器驱动(HAL):我可以发送 SPI 数据</li></ol><h3 id="SPI协议"><a href="#SPI协议" class="headerlink" title="SPI协议"></a>SPI协议</h3><h4 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h4><p>SPI(Serial Peripheral Interface,串行外设接口)是由摩托罗拉(Motorola)在 1980 前后提出的一种<strong>全双工同步串行</strong>通信接口,它用于 MCU 与各种外围设备以串行方式进 行通信以交换信息,通信速度最高可达 25MHz 以上。 </p><p>SPI 接口主要应用在 EEPROM、FLASH、实时时钟、网络控制器、OLED 显示驱动器、AD 转换器,数字信号处理器、数字信号解码器等设备之间。 </p><p>SPI 通常由四条线组成,一条主设备输出与从设备输入(Master Output Slave Input, MOSI),一条主设备输入与从设备输出(Master Input Slave Output,MISO),一条时钟信 号(Serial Clock,SCLK),一条从设备使能选择(Chip Select,CS)。 </p><p>SPI 可以一个主机连接单个或多个从机,每个从机都使用一个引脚进行片选,物理连 接示意图如图所示:</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216000649280.png" alt="image-20260216000649280"></p><p>在 SCK 时钟周期的驱动下,主机把数据驱动到 MOSI 上传给从机,从机把数据驱动到 MISO 上传给主机,主机发送 N 字节给从机时,必定能接收到 N 字节,至于接收到的 N 字节是否有意义由 从机决定。如果主机只想对从机进行写操作,主机只需忽略接收的从机数据即可。如果主 机只想读取从机数据,它也要发送数据给从机(发送的数据可以是空数据)。</p><p>时钟线和数据线是共用的,主控芯片通过片选引脚选择与谁通信,同一时间只能选择一个</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216000958789.png" alt="image-20260216000958789"></p><p>问题</p><ol><li>SCK初始电平(CPOL——极性)</li><li>SCK的第1/2个边沿传输数据(CPHA——相位)</li></ol><p>SPI 有四种传输模式,如下表所示,主要差别在于 CPOL 和 CPHA 的不同。 </p><ol><li>CPOL(Clock Polarity,时钟极性)表示 SCK 在空闲时为高电平还是低电平。当 CPOL=0,SCK 空闲时为低电平,当 CPOL=1,SCK 空闲时为高电平。 </li><li>CPHA(Clock Phase,时钟相位)表示 SCK 在第几个时钟边缘采样数据。当 CPHA=0, 在 SCK 第一个边沿采样数据,当 CPHA=1,在 SCK 第二个边沿采样数据。</li></ol><table><thead><tr><th>模式</th><th>CPOL</th><th>CPHA</th><th>时钟空闲状态</th><th>数据采样边沿</th><th>数据变化边沿</th><th>典型应用场景</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>0</td><td>低电平</td><td>第一个边沿(上升沿)</td><td>下降沿</td><td>大多数传感器(如BME280)、Flash(如W25Q64)、SD卡</td></tr><tr><td>1</td><td>0</td><td>1</td><td>低电平</td><td>第二个边沿(下降沿)</td><td>上升沿</td><td>部分ADC芯片(如MCP3008)</td></tr><tr><td>2</td><td>1</td><td>0</td><td>高电平</td><td>第一个边沿(下降沿)</td><td>上升沿</td><td>较少见,某些射频模块</td></tr><tr><td>3</td><td>1</td><td>1</td><td>高电平</td><td>第二个边沿(上升沿)</td><td>下降沿</td><td>EEPROM(如AT25系列)、部分RF模块、MPU6050</td></tr></tbody></table><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216001337738.png" alt="image-20260216001337738"></p><h3 id="STM32-SPI-硬件结构"><a href="#STM32-SPI-硬件结构" class="headerlink" title="STM32 SPI 硬件结构"></a>STM32 SPI 硬件结构</h3><h4 id="硬件框图-3"><a href="#硬件框图-3" class="headerlink" title="硬件框图"></a>硬件框图</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216001623773.png" alt="image-20260216001623773"></p><p>在 SPI 传输里,有 2 个角色:Master、Slave。我们常用 Master,它主动发起传输、 产生时钟信号。 对于 STM32,如何使用 SPI Master? </p><p>先初始化:</p><p>① 配置 SPI_CR1 寄存器(Control Register):配置波特率 </p><p>② 配置 SPI_CR1 寄存器(Control Register):配置 CPOL、CPHA </p><p>③ 配置 SPI_CR1 寄存器(Control Register):配置 LSBFIRT 以决定先传输最高位还是最 低位 </p><p>④ 配置 SPI_CR1 寄存器(Control Register):配置 NSS 使用软件控制,一般使用 GPIO 来 选择其他 Slave 设备,不是 NSS。 </p><p>⑤ 配置 SPI_CR1 寄存器(Control Register):选择 Master 模式、使能 SPI。 </p><p>如何发送数据呢?把数据写入“SPI_DR”即可,这个数据会被放入放入“Tx Buffer”, 进而放入“shift register”,它就会一位一位地出现在 MOSI 信号线上,发送给 Slave 设 备。当发送完毕后,TXE 标记位被设置。可以使用查询方式或中断方式监测到 TXE 被置位。 </p><p>如何接收数据呢?即使我们只想接收数据,也要把一个数组写入“SPI_DR”,在发送数 据的同时就会接收到数据:MISO 信号线上的数据被一位一位地移入“shift register”, 然后被放入“Rx Buffer”并且 RXNE 标记位被置一。可以使用查询方式或中断方式监测到 RXNE 被置位,然后读取“SPI_DR”得到数据。</p><h3 id="SPL-HAL库编程"><a href="#SPL-HAL库编程" class="headerlink" title="SPL HAL库编程"></a>SPL HAL库编程</h3><h4 id="CubeMX配置-2"><a href="#CubeMX配置-2" class="headerlink" title="CubeMX配置"></a>CubeMX配置</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216160721212.png" alt="image-20260216160721212"></p><h4 id="查询方式-1"><a href="#查询方式-1" class="headerlink" title="查询方式"></a>查询方式</h4><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">/* 发送同时接收数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_TransmitReceive</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pTxData<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span><span class="token operator">*</span>pRxData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">,</span> <span class="token class-name">uint32_t</span> Timeout<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 发送数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_Transmit</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">,</span><span class="token class-name">uint32_t</span> Timeout<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 接收数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_Receive</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">,</span><span class="token class-name">uint32_t</span> Timeout<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>参数说明 </p><ol><li>SPI_HandleTypeDef *hspi 使用哪个 SPI 控制器 </li><li>uint8_t *pTxData 或 uint8_t *pData 要发送的数据的 buffer</li><li>uint8_t *pRxData 或 uint8_t *pData 存储接收到数据的 buffer</li><li>uint16_t Size 数据个数 </li><li>uint32_t Timeout 超时时间,单位是 Tick,一般是 1ms </li><li>返回值 HAL_OK:成功 HAL_ERROR:错误 HAL_BUSY:总线忙 HAL_TIMEOUT:超时</li></ol><h4 id="中断方式"><a href="#中断方式" class="headerlink" title="中断方式"></a>中断方式</h4><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">/* 发送同时接收数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_TransmitReceive_IT</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pTxData<span class="token punctuation">,</span><span class="token class-name">uint8_t</span> <span class="token operator">*</span>pRxData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 发送数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_Transmit_IT</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span>Size<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 接收数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_Receive_IT</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>参数说明 </p><ol><li>SPI_HandleTypeDef *hspi 使用哪个 SPI 控制器</li><li>uint8_t *pTxData 或 uint8_t *pData 要发送的数据的 buffer </li><li>uint8_t *pRxData 或 uint8_t *pData 存储接收的数据的 buffer</li><li>uint16_t Size 数据个数 </li><li>uint32_t Timeout 超时时间,单位是 Tick,一般是 1ms </li><li>返回值 HAL_OK:成功 HAL_ERROR:错误 HAL_BUSY:总线忙 HAL_TIMEOUT:超时</li></ol><blockquote><p>[!NOTE]</p><p>这几个函数只是启动 SPI 传输,后面完全由中断程序来传输后续数据。这两个函数返回后,并不表示传输已经完成,需要在中断回调函数里判断。</p></blockquote><p> 要使用中断方式,还需要使用 STM32CubeMX 配置 SPI 中断,如下:</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216160938567.png" alt="image-20260216160938567"></p><p>如果传输完成或出错,如下回调函数会被调用:</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">/* 发送、接收完成回调函数 */</span><span class="token keyword">void</span> <span class="token function">HAL_SPI_TxRxCpltCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 发送完成回调函数 */</span><span class="token keyword">void</span> <span class="token function">HAL_SPI_TxCpltCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 接收完成回调函数 */</span><span class="token keyword">void</span> <span class="token function">HAL_SPI_RxCpltCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span><span class="token comment">/* 出错回调函数 */</span><span class="token keyword">void</span> <span class="token function">HAL_SPI_ErrorCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以在这些回调函数里设置变量,表示传输已经完成。</p><h4 id="DMA模式"><a href="#DMA模式" class="headerlink" title="DMA模式"></a>DMA模式</h4><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">/* 发送同时接收数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_TransmitReceive_DMA</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pTxData<span class="token punctuation">,</span><span class="token class-name">uint8_t</span> <span class="token operator">*</span>pRxData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span> Size<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 发送数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_Transmit_DMA</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span>Size<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/* 接收数据 */</span>HAL_StatusTypeDef <span class="token function">HAL_SPI_Receive_DMA</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">,</span> <span class="token class-name">uint8_t</span> <span class="token operator">*</span>pData<span class="token punctuation">,</span> <span class="token class-name">uint16_t</span>Size<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>[!NOTE]</p><p>这几个函数只是启动 SPI 传输,以后完全由 DMA 来传输后续数据。这两个函数返回后, 并不表示传输已经完成,需要在回调函数里判断。 </p></blockquote><p>要使用 DMA 方式,还需要使用 STM32CubeMX 配置 DMA,如下:</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216165619929.png" alt="image-20260216165619929"></p><p>如果传输完成或出错,如下回调函数会被调用: </p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">/* 发送、接收完成回调函数 */</span> <span class="token keyword">void</span> <span class="token function">HAL_SPI_TxRxCpltCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 发送完成回调函数 */</span> <span class="token keyword">void</span> <span class="token function">HAL_SPI_TxCpltCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 接收完成回调函数 */</span> <span class="token keyword">void</span> <span class="token function">HAL_SPI_RxCpltCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span> <span class="token comment">/* 出错回调函数 */</span> <span class="token keyword">void</span> <span class="token function">HAL_SPI_ErrorCallback</span><span class="token punctuation">(</span>SPI_HandleTypeDef <span class="token operator">*</span>hspi<span class="token punctuation">)</span><span class="token punctuation">;</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以在这些回调函数里设置变量,表示传输已经完成。</p><h2 id="TIM"><a href="#TIM" class="headerlink" title="TIM"></a>TIM</h2><p>STM32 系列单片机有系统定时器(SysTick)、还有 2 个高级定时器 TIM1 和 TIM8,4 个 通用定时器 TIM2/3/4/5,2 个基本定时器 TIM6 和 TIM7。就功能方面,高级定时器>通用定 时器>基本定时器。理解了高级定时器,就能理解其他两种定时器。</p><p>定时器最基础的功能,就是“计数”,以下框图来自“高级定时器”:</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216171820473.png" alt="image-20260216171820473"></p><p>暂且不理会其它电路,只看红框里面的①②。先看②,它是一个 16 位的计数器:每进 来一个时钟(CK_CNT),里面的计数值“CNT counter”就累加,可以从 0 一直累加到 0xFFFF。如果时钟频率是 72MHz,那么从 0 累加到 0xFFFF 的时间是 65535/72=910us。 </p><p>如果想让计数器②的计时时间更长点,就要把时钟频率降下来,这需要使用到分频器 (图中①)。分频器也是一个计数器,假设输入的时钟“CK_PSC”是 72MHz,分频器的分频 系数设置为 2,它就会计数——每进来 2 个时钟,只输出 1 个时钟。这样,后面的计数器 ②从 0 累加到 0xFFFF 的时间就增加了一倍。 </p><p>对于计数器②,可以设置让它从 0 累加到某个数值——“Auto-reload register”的 数值(这被称为重载值)时发出中断,然后再从 0 再次累加。根据“CK_PSC”的频率、计 数器①的分频系数,就可以设置恰当的重载值,让定时器②周期性地产生中断,这就是系 统时钟,比如 HAL 库里使用的“HAL_Delay”就是基于系统时钟实现的</p><p>对于计数器①,它的时钟来源有多种选择,如下图所示: </p><p>① 内部时钟:用作普通的定时器,比如系统定时器 </p><p>② ETR(External Trigger Input),外部触发的输入信号:可以统计外部触发信号 </p><p>③ ITR0<del>ITR3(Internal Trigger0</del>3),内部触发信号:内部触发信号来自其他定时器的 “TRGO”信号,用于定时器的级联 </p><p>④ TIMx_CH1/2/3/4:也可以作为外部输入信号</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedimage-20260216172100094.png" alt="image-20260216172100094"></p>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> STM32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> STM32 </tag>
<tag> HAL </tag>
</tags>
</entry>
<entry>
<title>C++ 基础学习笔记</title>
<link href="/2025/09/15/2025-09-15-cpp-basic-notes/"/>
<url>/2025/09/15/2025-09-15-cpp-basic-notes/</url>
<content type="html"><![CDATA[<h2 id="g-基本命令和选项"><a href="#g-基本命令和选项" class="headerlink" title="g++ 基本命令和选项"></a>g++ 基本命令和选项</h2><p>基本用法:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">g++ <span class="token punctuation">[</span>options<span class="token punctuation">]</span> source_files <span class="token parameter variable">-o</span> output_file<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li><code>options</code>:编译器的选项参数</li><li><code>source_files</code>:要编译的源代码文件</li><li><code>-o output_file</code>:生成的可执行文件的名称</li></ul><h3 id="可选项"><a href="#可选项" class="headerlink" title="可选项"></a>可选项</h3><ul><li><code>-c</code> 只进行编译,生成.o 文件,不进行链接操作</li><li><code>-E</code> 只进行预处理,生成预处理后的.i 文件</li><li><code>-S</code> 只编译到汇编代码,生成汇编文件 (.s)</li><li><code>-save-temps</code> 保存中间文件 (预处理、汇编、目标文件)</li><li><code>-std=c++XX</code> 指定要使用的 C++ 标准版本,如 <code>-std=c++11</code>、<code>-std=c++14</code> 等</li><li><code>-I dir</code> 指定头文件目录</li><li><code>-L dir</code> 指定链接库目录</li><li><code>-l library</code> 指定要链接的库文件</li><li><code>-O level</code> 指定优化级别,常见的有 <code>-O0</code>(无优化)、<code>-O1</code>、<code>-O2</code>、<code>-O3</code>(最高级别的优化),以及 <code>-Os</code>(针对空间优化)等</li><li><code>-g</code> 生成调试信息</li><li><code>-Wall</code> 启用所有警告信息</li><li><code>-Werror</code> 将警告视为错误</li></ul><h2 id="C-是如何工作的"><a href="#C-是如何工作的" class="headerlink" title="C++ 是如何工作的"></a>C++ 是如何工作的</h2><p><strong>编译器</strong>:为每个.cpp 文件生成.obj(linux 下是.o) 文件</p><p><strong>链接器</strong>:把所有.obj 文件合并成一个.exe 文件</p><p><strong>声明</strong>:只给出函数的名称,参数,返回值,如:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">void log(const char* str); // 在 main.cpp 文件中<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这是编译器会相信有这个函数的定义从而不会报错,可以在另一个文件中实现这个函数,也就是函数的定义:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include<iostream>void log(const char* str){ std::cout << str << std::endl;}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="C-编译器是如何工作的"><a href="#C-编译器是如何工作的" class="headerlink" title="C++ 编译器是如何工作的"></a>C++ 编译器是如何工作的</h2><p>首先处理预处理语句 (所有预处理语句都以 <code>#</code> 开头),然后把所有的代码转换成常量的数据或指令</p><p>每一个.cpp 文件都会生成一个.o 文件</p><p>在编译阶段报错的错误代码以 C 开头,如语法错误</p><h2 id="C-链接器是如何工作的"><a href="#C-链接器是如何工作的" class="headerlink" title="C++ 链接器是如何工作的"></a>C++ 链接器是如何工作的</h2><p>必须有 main 函数 (入口点,不一定叫 main,默认是 main)</p><p>在链接阶段报错的错误代码以 LNK 开头,如无 main 函数,找不到已被声明函数的定义</p><p>在函数名之前加上 <code>static</code> 关键字表明这个函数只作用在当前文件中:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include <iostream>void log(const char* str);static int Multiply(int a,int b){ log("Multiply"); return a * b;}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>即便 <code>Multiply</code> 没有被调用,如果没有找到 <code>log</code>,链接器也会报错。但是在 <code>Multiply</code> 前面加上 <code>static</code> 就不会报错。</p><blockquote><p>[!NOTE]</p><p>函数的定义与声明的函数名、返回类型、参数必须完全相同才不会链接错误</p></blockquote><p>在头文件里只放函数的声明也是为了防止函数的重复定义</p><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><table><thead><tr><th>类型</th><th>大小</th></tr></thead><tbody><tr><td><code>char</code></td><td>1 字节</td></tr><tr><td><code>bool</code></td><td>1 字节</td></tr><tr><td><code>short</code></td><td>2 字节</td></tr><tr><td><code>int</code></td><td>4 字节</td></tr><tr><td><code>long</code></td><td>通常 4 字节</td></tr><tr><td><code>float</code></td><td>4 字节,定义时要在数字最后加 <code>f</code> 或 <code>F</code></td></tr><tr><td><code>double</code></td><td>8 字节</td></tr><tr><td><code>long long</code></td><td>8 字节</td></tr></tbody></table><p><code>cout</code> 会把 <code>char</code> 看成字符:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">char a = 65;std::cout << a << std::endl; // 输出 Aa = 'A';std::cout << a << std::endl; // 输出 A<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><p>主要目的是防止代码重复</p><p>指定了返回值的函数必须有返回值,除了 <code>main</code> 函数。</p><p>把函数声明写在 <code>.h</code> 文件,函数的定义写在 <code>.cpp</code> 文件</p><h2 id="头文件"><a href="#头文件" class="headerlink" title="头文件"></a>头文件</h2><p><code>#include</code> 的作用就是复制粘贴</p><p>头文件通常用于声明某种类型的函数,声明是告诉编译器函数是存在的,只是在别的地方定义</p><p>c++ 头文件的开头会有 <code>#pragma once</code>,本质上是发送到编译器的预处理指令,为了防止单个头文件被同一个 <code>.cpp</code> 文件多次包含,尤其是为了防止头文件中的结构体重复定义</p><p>另一种防止重复包含的方法:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#ifndef _LOG_H#define _LOG_H...#endif<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>即检查 <code>_LOG_H</code> 是否被定义了,如果没有定义就在编译中包含下面的代码,如果已经被定义了就都不会包含</p><p>可以在 include 的时候指定头文件路径,如:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include "../include/log.h"<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>C 标准库一般有 <code>.h</code> 扩展名,C++ 标准库一般没有</p><h2 id="在-VScode-里面打断点调试"><a href="#在-VScode-里面打断点调试" class="headerlink" title="在 VScode 里面打断点调试"></a>在 VScode 里面打断点调试</h2><p>点击最左边带虫子图案的按钮,点绿色三角,会提示创建 <code>launch.json</code> 文件。</p><p>保存之后,运行命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">g++ <span class="token operator"><</span>要编译的文件名<span class="token operator">></span> <span class="token parameter variable">-g</span> <span class="token parameter variable">-o</span> <span class="token operator"><</span>目标文件名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>来生成带有调试信息的可执行文件,然后点击行号来在对应的行打断点,点击绿色小三角即可开始调试。</p><h2 id="指针"><a href="#指针" class="headerlink" title="指针"></a>指针</h2><p>指针只是存储地址的整数</p><p>C++ 动态内存分配:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">// 分配 8 个字节的内存,buffer 是指向那块内存开始的指针char* buffer = new char[8];// 用给定的数据填充内存块memset(buffer,0,8);delete[] buffer; // 回收这块内存<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>用 <code>new</code> 动态分配的内存空间,一定要用 <code>delete</code> 运算符进行释放</p><h2 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h2><p>是指针的伪装,更易理解,但是必须引用已经存在的变量,不能为空,且并不是新的变量,并不占用内存,没有真正的存储空间,某个变量的引用,等价于这个变量,相当于该变量的一个别名。</p><blockquote><p>[!NOTE]</p><ul><li>定义引用时一定要将其初始化成引用某个变量</li><li>初始化后,它就一直引用该变量,不会再引用别的变量了</li><li>引用只能引用变量,不能引用常量和表达式</li></ul></blockquote><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">int n = 4;int & r = n; // r 引用了 n, r 的类型是 int&<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>比较好的用法——用引用简化指针操作:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">// 用指针实现void Increment(int* value){ (*value)++;}// 用引用实现:更简洁void Increment(int& value){ value++;}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="类"><a href="#类" class="headerlink" title="类"></a>类</h2><p>面向对象的程序设计方法:将某类客观事物共同特点(属性)归纳出来,形成一个数据结构(可以用多个变量描述事物的属性);将这类事物所能进行的行为也归纳出来,形成一个个函数,这些函数可以用来操作数据结构 (这一步叫”抽象”)。然后,通过某种语法形式,将数据结构和操作该数据结构的函数”捆绑”在一起,形成一个”类”,从而使得数据结构和操作该数据结构的算法呈现出显而易见的紧密关系,这就是**”封装”**。</p><p>面向对象的程序设计具有”抽象”、”封装”、”继承”、”多态”四个基本特点。</p><p>实际上,”类”看上去就像”带函数的结构”。类内的函数称为方法。</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">class Rectangle{public: int w,h; int Area() // 算面积 { return w * h; } int Perimeter() // 算周长 { return 2 * (w + h); } void Init(int w_,int h_) { w = w_; h = h_; }}; // 注意这里的分号<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="Static-关键字"><a href="#Static-关键字" class="headerlink" title="Static 关键字"></a>Static 关键字</h2><h3 id="在类和结构体外部使用-static"><a href="#在类和结构体外部使用-static" class="headerlink" title="在类和结构体外部使用 static"></a>在类和结构体外部使用 static</h3><p>被声明为 static 的符号只对当前文件可见(只会在当前文件内部链接)</p><p>主要是为了防止使用全局变量</p><h3 id="在类和结构体内部使用-static"><a href="#在类和结构体内部使用-static" class="headerlink" title="在类和结构体内部使用 static"></a>在类和结构体内部使用 static</h3><ul><li>普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享</li><li>普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象</li><li>因此静态成员不需要通过对象就能访问</li><li>静态成员变量本质上是类内的全局变量,哪怕一个对象都不存在,类的静态成员变量也存在</li><li>静态成员函数本质上是类内的全局函数</li></ul><h3 id="在局部作用域使用-static"><a href="#在局部作用域使用-static" class="headerlink" title="在局部作用域使用 static"></a>在局部作用域使用 static</h3><p>变量的生存期:在被删除之前,变量实际存在于内存的时间</p><p>变量的作用域:我们可以访问变量的范围</p><p>局部静态:生存期和整个程序一样,作用范围被限制在某个作用域(如函数)</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">void Function(){ static int i; i++; std::cout << i << std::endl;}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="枚举"><a href="#枚举" class="headerlink" title="枚举"></a>枚举</h2><p>枚举是一种命名值的方法,主要用于使用整数来表示某些状态或者数值时,默认从 0 开始</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">enum Example{ A, B, C // 默认 A=0,B=1,C=2};enum Example : unsigned char // 类型只能是整数的{ A=5, B, C // A=5,B=6,C=7};<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h2><p>构造函数是一种特殊的函数,每次构造一个对象的时候都会调用,名字与类名相同,可以有参数,不能有返回值,作用是对对象进行初始化,如给成员变量赋初值。如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数。</p><h2 id="析构函数"><a href="#析构函数" class="headerlink" title="析构函数"></a>析构函数</h2><p>在销毁对象时被调用,没有参数和返回值,一个类最多只能有一个析构函数。析构函数对象消亡时即自动被调用。可以定义析构函数来在对象消亡前做善后工作,比如释放分配的空间等。</p><p>名称是 <code>~<类名></code></p><h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>在定义一个新的类 B 时,如果该类与某个已有的类 A 相似 (指的是 B 拥有 A 的全部特点),那么就可以把 A 作为一个基类,而把 B 作为基类的一个派生类 (也称子类)。</p><p>派生类拥有基类的全部成员函数和成员变量。</p><p>写法:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">class 派生类名:public 基类名{ // ...};<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="虚函数"><a href="#虚函数" class="headerlink" title="虚函数"></a>虚函数</h2><p>基类里面的虚函数在子类里面可以修改成别的功能</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">class Entity{public: virtual std::string GetName(){return "Entity:";}};class Player:public Entity{private: std::string m_Name;public: Player(const std::string& name):m_Name(name) {} std::string GetName() override {return m_Name;}};<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="面向对象"><a href="#面向对象" class="headerlink" title="面向对象"></a>面向对象</h2><p>对象就是东西,以变量形式出现</p><p>对象 = 属性 (数据)+ 服务 (函数)</p><p>只能通过提供的服务来操作数据</p><p><strong>面向对象编程的原则</strong>:</p><ul><li>所有东西都是对象</li><li>程序运行的时候,对象互相告诉对方去做什么(而不是如何去做)</li><li>每个对象有自己的内存,自己的内存由其他对象组成</li><li>每一个对象都有一个类型</li><li>所有可以接受相同消息的对象可归为一类</li><li>对象以接口和外界打交道</li></ul><p>Class(类)是概念,Objects(对象)是实体</p><p>类定义了对象长什么样,对象是类的实例</p>]]></content>
<categories>
<category> Learning </category>
<category> Language </category>
<category> Cpp </category>
</categories>
<tags>
<tag> cpp </tag>
<tag> programming </tag>
</tags>
</entry>
<entry>
<title>旋转编码器驱动 - 状态机精准处理 EC11</title>
<link href="/2025/09/06/2025-09-06-rotary-encoder-driver/"/>
<url>/2025/09/06/2025-09-06-rotary-encoder-driver/</url>
<content type="html"><![CDATA[<h2 id="一、引言"><a href="#一、引言" class="headerlink" title="一、引言"></a>一、引言</h2><p>在嵌入式人机交互领域,旋转编码器因其无限的旋转范围、清晰的段落感以及集成按压功能,成为了许多项目的输入首选。</p><p>这里,我们将深入剖析一个用于多功能时钟项目的旋转编码器驱动模块。这段代码不仅稳定可靠地读取旋转方向,更实现了单击、带视觉反馈的长按、以及双击功能。</p><h2 id="二、增量式编码器-EC11"><a href="#二、增量式编码器-EC11" class="headerlink" title="二、增量式编码器 EC11"></a>二、增量式编码器 EC11</h2><h3 id="2-1-概述"><a href="#2-1-概述" class="headerlink" title="2.1 概述"></a>2.1 概述</h3><p>EC11 的旋转与按下不同,不能当作一个简单的按键来进行处理,它是一种增量式旋转编码器,在原理图中,可以看到 ABC 三个输出通道,其中 C 相接入 GND,AB 两相输出正交信号,相位差为 90°。</p><h3 id="2-2-电路连接"><a href="#2-2-电路连接" class="headerlink" title="2.2 电路连接"></a>2.2 电路连接</h3><p>可参考 <a href="https://wiki.lceda.cn/zh-hans/course-projects/microcontroller/ch32-multimeter/ec11coder-test.html">EC11 旋转编码器实验 | 嘉立创教育与开源文档中心</a></p><p>这里面加入了滤波电容,而我为了简单,把编码器的接口直接连到了单片机的 IO 口,也可正常使用</p><h3 id="2-3-输出信号"><a href="#2-3-输出信号" class="headerlink" title="2.3 输出信号"></a>2.3 输出信号</h3><p>旋转编码器内部相当于两个开关,其 CLK 和 DT 引脚会输出一种叫做格雷码的序列。格雷码的特点是任意两个相邻的数字其二进制表示只有一位不同,这使得它在旋转过程中不会产生严重的误码。</p><p>一次完整的旋转(一格)会产生 4 个状态。假设从静止开始:</p><p><strong>顺时针旋转(CW)一格的典型序列:</strong></p><ol><li>状态 <code>11</code> (CLK=1, DT=1) -> 起始状态</li><li>状态 <code>10</code> (CLK=1, DT=0) -> CLK 保持高,DT 变为低</li><li>状态 <code>00</code> (CLK=0, DT=0) -> CLK 变为低,DT 保持低</li><li>状态 <code>01</code> (CLK=0, DT=1) -> CLK 保持低,DT 变为高</li><li>状态 <code>11</code> (CLK=1, DT=1) -> 回到起始状态,完成一格</li></ol><p><strong>逆时针旋转(CCW)一格的典型序列:</strong></p><ol><li>状态 <code>11</code> (CLK=1, DT=1) -> 起始状态</li><li>状态 <code>01</code> (CLK=0, DT=1) -> CLK 变为低,DT 保持高</li><li>状态 <code>00</code> (CLK=0, DT=0) -> CLK 保持低,DT 变为低</li><li>状态 <code>10</code> (CLK=1, DT=0) -> CLK 变为高,DT 保持低</li><li>状态 <code>11</code> (CLK=1, DT=1) -> 回到起始状态,完成一格</li></ol><h2 id="三、模块概述"><a href="#三、模块概述" class="headerlink" title="三、模块概述"></a>三、模块概述</h2><p>本模块的核心功能是驱动一个标准的增量式旋转编码器,并读取其旋转方向和集成按键的状态。它实现了:</p><ol><li><strong>旋转检测</strong>:确识别顺时针/逆时针旋转,并处理抖动。</li><li><strong>单击检测</strong>:识别用户的轻按操作。</li><li><strong>长按检测</strong>:识别用户的长按操作(如 2 秒),并在屏幕上提供可视化进度条反馈,提升用户体验。</li><li><strong>双击检测</strong>:识别用户在短时间内快速的连续两次单击操作,用于触发快捷功能。</li></ol><h2 id="四、实现思路"><a href="#四、实现思路" class="headerlink" title="四、实现思路"></a>四、实现思路</h2><h3 id="4-1-旋转检测"><a href="#4-1-旋转检测" class="headerlink" title="4.1 旋转检测"></a>4.1 旋转检测</h3><h4 id="法一:状态变化法"><a href="#法一:状态变化法" class="headerlink" title="法一:状态变化法"></a>法一:状态变化法</h4><p>这个方法很简单,在 CLK 引脚的电平发生变化时(上升沿或下降沿),去检查 DT 引脚的电平状态。</p><p>当 CLK 从高变低(下降沿)时:</p><ul><li>如果 <strong>DT 为低电平</strong>,则是<strong>顺时针</strong>旋转。</li><li>如果 <strong>DT 为高电平</strong>,则是<strong>逆时针</strong>旋转。</li></ul><h4 id="法二:状态机查表法"><a href="#法二:状态机查表法" class="headerlink" title="法二:状态机查表法"></a>法二:状态机查表法</h4><p><strong>状态编码与存储</strong></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">uint8_t</span> currentEncoded <span class="token operator">=</span> <span class="token punctuation">(</span>clk <span class="token operator"><<</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">|</span> dt<span class="token punctuation">;</span> <span class="token comment">// 组合新状态</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这行代码将两个引脚的状态组合成一个 2 位的数字。</p><ul><li>例如:<code>CLK=1, DT=1</code> -> <code>(1 << 1) | 1 = 3</code> (二进制 <code>0b11</code>)</li><li><code>CLK=1, DT=0</code> -> <code>(1 << 1) | 0 = 2</code> (二进制 <code>0b10</code>)</li></ul><p>这样,<code>lastEncoded</code> 和 <code>currentEncoded</code> 就可以各表示 4 种状态(0, 1, 2, 3)。</p><p><strong>查找表</strong></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">static</span> <span class="token keyword">const</span> <span class="token keyword">int8_t</span> transitionTable<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这个数组包含了所有 16 种可能的状态转换结果。<strong>索引的计算方式是:<code>(lastEncoded << 2) | currentEncoded</code></strong>。</p><ul><li><code>lastEncoded</code> 是旧状态(0-3),左移 2 位后变成高 2 位(0, 4, 8, 12)。</li><li><code>currentEncoded</code> 是新状态(0-3),作为低 2 位。</li><li>这样组合出的索引范围是 0 到 15,正好对应数组的 16 个元素。</li></ul><p><strong>这个表是如何工作的?</strong></p><p>让我们用顺时针序列来验证:</p><ol><li>从 <code>11</code> (3) 转到 <code>10</code> (2):索引 = <code>(3 << 2) | 2 = 14</code> -> <code>transitionTable[14] = 1</code> (顺时针)</li><li>从 <code>10</code> (2) 转到 <code>00</code> (0):索引 = <code>(2 << 2) | 0 = 8</code> -> <code>transitionTable[8] = 1</code> (顺时针)</li><li>从 <code>00</code> (0) 转到 <code>01</code> (1):索引 = <code>(0 << 2) | 1 = 1</code> -> <code>transitionTable[1] = 1</code> (顺时针)</li><li>从 <code>01</code> (1) 转到 <code>11</code> (3):索引 = <code>(1 << 2) | 3 = 7</code> -> <code>transitionTable[7] = 1</code> (顺时针)</li></ol><p>逆时针序列也会产生连续的 <code>-1</code>。而无效的跳变(如由抖动引起)在表中对应的值大多是 <code>0</code>。</p><p><strong>累加器与滤波</strong></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">deltaSum <span class="token operator">+=</span> deltaValue<span class="token punctuation">;</span> <span class="token comment">// 来自查表的值 (-1, 0, 1)</span><span class="token keyword">if</span> <span class="token punctuation">(</span>deltaSum <span class="token operator">>=</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token comment">// 触发顺时针</span> deltaSum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 重置</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>deltaSum <span class="token operator"><=</span> <span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token comment">// 触发逆时针</span> deltaSum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 重置</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="4-2-按键检测:状态机与时间戳"><a href="#4-2-按键检测:状态机与时间戳" class="headerlink" title="4.2 按键检测:状态机与时间戳"></a>4.2 按键检测:状态机与时间戳</h3><p>按键处理是一个复杂的状态机问题。我们需要通过记录按下时刻的时间戳和状态转换来清晰区分<strong>单击</strong>、<strong>长按</strong>和<strong>双击</strong>。</p><p><strong>关键难点在于如何无歧义地区分三者?</strong></p><p>我们的策略是使用一个四状态的状态机 (<code>ButtonState</code>):</p><ul><li><strong>IDLE (空闲)</strong>:等待第一次按下。</li><li><strong>PRESSED (第一次按下)</strong>:记录按下时间,等待释放。在此状态下,<code>readButtonLongPress()</code> 函数会检查是否达到长按阈值。</li><li><strong>RELEASED (第一次释放)</strong>:此时启动一个定时器(<code>doubleClickTimeout</code>),在此窗口内:<ul><li>如果用户再次按下,则进入 <code>DOUBLE_PRESSED</code> 状态。</li><li>如果定时器超时,则判定这是一次<strong>单击</strong>,并触发单击事件。</li></ul></li><li><strong>DOUBLE_PRESSED (第二次按下)</strong>:等待第二次释放,一旦释放,立即标记<strong>双击</strong>事件。</li></ul><p><strong>状态机工作流程:</strong></p><pre class="line-numbers language-none"><code class="language-none">[BTN_IDLE] | (按下) v[BTN_PRESSED] <---[长按发生]---> (触发长按) | (释放) v[BTN_RELEASED] ---(超时)---> [BTN_IDLE] & 触发单击 | (再次按下) v[BTN_DOUBLE_PRESSED] | (释放) v[BTN_IDLE] & 标记双击<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种设计确保了长按优先于双击(因为长按在第一次按下期间就已判定),并且单击是双击的超时后备,逻辑清晰,无歧义。</p><p>为消除长按时的焦虑,代码在按住 1 秒后(<code>progressBarStartTime</code>)开始在 TFT 屏幕上绘制一个逐渐增长的进度条,直观地提示还需按住多久才能触发长按功能。</p><h2 id="五、核心代码"><a href="#五、核心代码" class="headerlink" title="五、核心代码"></a>五、核心代码</h2><h3 id="旋转编码器初始化"><a href="#旋转编码器初始化" class="headerlink" title="旋转编码器初始化"></a>旋转编码器初始化</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">initRotaryEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">pinMode</span><span class="token punctuation">(</span>ENCODER_CLK<span class="token punctuation">,</span> INPUT_PULLUP<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">pinMode</span><span class="token punctuation">(</span>ENCODER_DT<span class="token punctuation">,</span> INPUT_PULLUP<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">pinMode</span><span class="token punctuation">(</span>ENCODER_SW<span class="token punctuation">,</span> INPUT_PULLUP<span class="token punctuation">)</span><span class="token punctuation">;</span> lastEncoded <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token function">digitalRead</span><span class="token punctuation">(</span>ENCODER_CLK<span class="token punctuation">)</span> <span class="token operator"><<</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token function">digitalRead</span><span class="token punctuation">(</span>ENCODER_DT<span class="token punctuation">)</span><span class="token punctuation">;</span> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Rotary Encoder Initialized."</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="读取旋转方向(状态变化法)"><a href="#读取旋转方向(状态变化法)" class="headerlink" title="读取旋转方向(状态变化法)"></a>读取旋转方向(状态变化法)</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">readEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">int</span> lastCLK_State <span class="token operator">=</span> HIGH<span class="token punctuation">;</span> <span class="token keyword">int</span> currentCLK_State <span class="token operator">=</span> <span class="token function">digitalRead</span><span class="token punctuation">(</span>ENCODER_CLK<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> delta <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentCLK_State <span class="token operator">!=</span> lastCLK_State<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">delayMicroseconds</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 消抖</span> currentCLK_State <span class="token operator">=</span> <span class="token function">digitalRead</span><span class="token punctuation">(</span>ENCODER_CLK<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> currentDT_State <span class="token operator">=</span> <span class="token function">digitalRead</span><span class="token punctuation">(</span>ENCODER_DT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentCLK_State <span class="token operator">==</span> LOW<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentDT_State <span class="token operator">==</span> LOW<span class="token punctuation">)</span> <span class="token punctuation">{</span> delta <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// 顺时针</span> encoderPos<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> delta <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// 逆时针</span> encoderPos<span class="token operator">--</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> lastCLK_State <span class="token operator">=</span> currentCLK_State<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> delta<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="按键状态机核心"><a href="#按键状态机核心" class="headerlink" title="按键状态机核心"></a>按键状态机核心</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">readButton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> currentButtonState <span class="token operator">=</span> <span class="token function">digitalRead</span><span class="token punctuation">(</span>ENCODER_SW<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">bool</span> singleClickTriggered <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span> currentTime <span class="token operator">=</span> <span class="token function">millis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span>buttonFSM<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> BTN_IDLE<span class="token operator">:</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentButtonState <span class="token operator">==</span> LOW<span class="token punctuation">)</span> <span class="token punctuation">{</span> buttonFSM <span class="token operator">=</span> BTN_PRESSED<span class="token punctuation">;</span> buttonPressStartTime <span class="token operator">=</span> currentTime<span class="token punctuation">;</span> longPressHandled <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> clickHandled <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> doubleClickDetected <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> BTN_PRESSED<span class="token operator">:</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentButtonState <span class="token operator">==</span> HIGH<span class="token punctuation">)</span> <span class="token punctuation">{</span> firstReleaseTime <span class="token operator">=</span> currentTime<span class="token punctuation">;</span> buttonFSM <span class="token operator">=</span> BTN_RELEASED<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> BTN_RELEASED<span class="token operator">:</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentTime <span class="token operator">-</span> firstReleaseTime <span class="token operator">></span> doubleClickTimeout<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>clickHandled<span class="token punctuation">)</span> <span class="token punctuation">{</span> singleClickTriggered <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> clickHandled <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> buttonFSM <span class="token operator">=</span> BTN_IDLE<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentButtonState <span class="token operator">==</span> LOW<span class="token punctuation">)</span> <span class="token punctuation">{</span> buttonFSM <span class="token operator">=</span> BTN_DOUBLE_PRESSED<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> BTN_DOUBLE_PRESSED<span class="token operator">:</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentButtonState <span class="token operator">==</span> HIGH<span class="token punctuation">)</span> <span class="token punctuation">{</span> doubleClickDetected <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> buttonFSM <span class="token operator">=</span> BTN_IDLE<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> singleClickTriggered<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="长按检测"><a href="#长按检测" class="headerlink" title="长按检测"></a>长按检测</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">bool</span> <span class="token function">readButtonLongPress</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">bool</span> triggered <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>buttonFSM <span class="token operator">==</span> BTN_PRESSED <span class="token operator">&&</span> <span class="token operator">!</span>longPressHandled<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span> currentHoldTime <span class="token operator">=</span> <span class="token function">millis</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> buttonPressStartTime<span class="token punctuation">;</span> <span class="token comment">// 显示进度条</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentHoldTime <span class="token operator">>=</span> progressBarStartTime<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">float</span> progress <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span><span class="token punctuation">(</span>currentHoldTime <span class="token operator">-</span> progressBarStartTime<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token punctuation">(</span>longPressThreshold <span class="token operator">-</span> progressBarStartTime<span class="token punctuation">)</span><span class="token punctuation">;</span> progress <span class="token operator">=</span> <span class="token punctuation">(</span>progress <span class="token operator">></span> <span class="token number">1.0</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token number">1.0</span> <span class="token operator">:</span> progress<span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">drawRect</span><span class="token punctuation">(</span>BAR_X<span class="token punctuation">,</span> BAR_Y<span class="token punctuation">,</span> BAR_WIDTH<span class="token punctuation">,</span> BAR_HEIGHT<span class="token punctuation">,</span> TFT_WHITE<span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span>BAR_X <span class="token operator">+</span> <span class="token number">2</span><span class="token punctuation">,</span> BAR_Y <span class="token operator">+</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span>BAR_WIDTH <span class="token operator">-</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">*</span> progress<span class="token punctuation">)</span><span class="token punctuation">,</span> BAR_HEIGHT <span class="token operator">-</span> <span class="token number">4</span><span class="token punctuation">,</span> TFT_BLUE<span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">pushSprite</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 长按触发</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentHoldTime <span class="token operator">>=</span> longPressThreshold<span class="token punctuation">)</span> <span class="token punctuation">{</span> triggered <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> longPressHandled <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> clickHandled <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 清理进度条</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">digitalRead</span><span class="token punctuation">(</span>ENCODER_SW<span class="token punctuation">)</span> <span class="token operator">==</span> HIGH<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>buttonPressStartTime <span class="token operator">!=</span> <span class="token number">0</span> <span class="token operator">&&</span> <span class="token punctuation">(</span><span class="token function">millis</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> buttonPressStartTime<span class="token punctuation">)</span> <span class="token operator">></span> progressBarStartTime<span class="token punctuation">)</span> <span class="token punctuation">{</span> menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span>BAR_X<span class="token punctuation">,</span> BAR_Y<span class="token punctuation">,</span> BAR_WIDTH<span class="token punctuation">,</span> BAR_HEIGHT<span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">pushSprite</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> triggered<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="双击检测"><a href="#双击检测" class="headerlink" title="双击检测"></a>双击检测</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">bool</span> <span class="token function">readDoubleClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>doubleClickDetected<span class="token punctuation">)</span> <span class="token punctuation">{</span> doubleClickDetected <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="六、使用示例"><a href="#六、使用示例" class="headerlink" title="六、使用示例"></a>六、使用示例</h2><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">"RotaryEncoder.h"</span></span><span class="token keyword">void</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">initRotaryEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">loop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> clickEvent <span class="token operator">=</span> <span class="token function">readButton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> rotation <span class="token operator">=</span> <span class="token function">readEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>rotation <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>rotation <span class="token operator">==</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token string">"CW"</span> <span class="token operator">:</span> <span class="token string">"CCW"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>clickEvent<span class="token punctuation">)</span> <span class="token punctuation">{</span> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Single Click"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">readButtonLongPress</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Long Press"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">readDoubleClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Double Click!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">delay</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="七、改进方向"><a href="#七、改进方向" class="headerlink" title="七、改进方向"></a>七、改进方向</h2><ol><li><strong>中断方式</strong>:当前是轮询方式。对于主循环任务繁重的系统,可以将 CLK/DT 引脚接入外部中断,在中断服务程序中快速处理状态变化,进一步提高响应速度。</li><li><strong>回调函数机制</strong>:使用回调函数来替代轮询检测,可以进一步简化主循环逻辑。</li></ol>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> ESP32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
</tags>
</entry>
<entry>
<title>ESP32-C3 读取芯片内部温度</title>
<link href="/2025/09/03/2025-09-03-esp32-internal-temperature/"/>
<url>/2025/09/03/2025-09-03-esp32-internal-temperature/</url>
<content type="html"><![CDATA[<h1 id="【Arduino】ESP32-C3-读取芯片内部温度"><a href="#【Arduino】ESP32-C3-读取芯片内部温度" class="headerlink" title="【Arduino】ESP32-C3 读取芯片内部温度"></a>【Arduino】ESP32-C3 读取芯片内部温度</h1><h2 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h2><p>根据文档,ESP32-C3 的温度传感器可产生随温度变化的电压。该电压通过内部 ADC 转换为数字值。</p><p>温度传感器的测量范围为–40°C 至 125°C。它主要用于检测芯片内部的温度变化。温度值取决于微控制器时钟频率或 I/O 负载等因素。通常,芯片的内部温度会高于其工作环境温度。</p><blockquote><p>[!NOTE]<br>这个温度传感器主要用于检测芯片内部的温度变化,而非精确测量环境温度。如需测量环境温度,建议使用外部温度传感器如 DS18B20、AHT20 等。</p></blockquote><h2 id="二、代码"><a href="#二、代码" class="headerlink" title="二、代码"></a>二、代码</h2><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include <Arduino.h>void setup() { Serial.begin(115200); while (!Serial) ; Serial.println("ESP32-C3 温度测试开始");}void loop() { float temp = temperatureRead(); char buf[32]; snprintf(buf, sizeof(buf), "ESP32C3_TEMP:%.1f\n", temp); Serial.print(buf); delay(1000);}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="代码说明"><a href="#代码说明" class="headerlink" title="代码说明"></a>代码说明</h3><ol><li><strong><code>temperatureRead()</code> 函数</strong>:这是 ESP32 Arduino 核心提供的内置函数,直接读取芯片内部温度传感器的值</li><li><strong>串口输出</strong>:通过串口监视器可以实时查看温度数据</li><li><strong>采样间隔</strong>:示例中设置为 1 秒,可根据实际需求调整</li></ol><h3 id="运行结果"><a href="#运行结果" class="headerlink" title="运行结果"></a>运行结果</h3><p>打开 Arduino IDE 的串口监视器(波特率 115200),可以看到类似输出:</p><pre class="line-numbers language-none"><code class="language-none">ESP32-C3 温度测试开始ESP32C3_TEMP:45.2ESP32C3_TEMP:45.3ESP32C3_TEMP:45.1...<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="三、注意事项"><a href="#三、注意事项" class="headerlink" title="三、注意事项"></a>三、注意事项</h2><ol><li><strong>温度偏差</strong>:芯片内部温度通常比环境温度高 10-20°C</li><li><strong>影响因素</strong>:<ul><li>CPU 负载</li><li>WIFI 使用情况</li><li>外设工作状态</li></ul></li><li><strong>应用场景</strong>:<ul><li>监测芯片过热情况</li><li>温度补偿算法</li><li>粗略的环境温度估计</li></ul></li></ol>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> ESP32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
<tag> Arduino </tag>
<tag> sensor </tag>
</tags>
</entry>
<entry>
<title>ESP32 驱动无源蜂鸣器播放音乐</title>
<link href="/2025/09/03/2025-09-03-esp32-buzzer-music/"/>
<url>/2025/09/03/2025-09-03-esp32-buzzer-music/</url>
<content type="html"><![CDATA[<h1 id="【Arduino】ESP32-驱动无源蜂鸣器并播放音乐"><a href="#【Arduino】ESP32-驱动无源蜂鸣器并播放音乐" class="headerlink" title="【Arduino】ESP32 驱动无源蜂鸣器并播放音乐"></a>【Arduino】ESP32 驱动无源蜂鸣器并播放音乐</h1><h2 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h2><h3 id="1-1-无源蜂鸣器"><a href="#1-1-无源蜂鸣器" class="headerlink" title="1.1 无源蜂鸣器"></a>1.1 无源蜂鸣器</h3><p>有关蜂鸣器的介绍可参考</p><ul><li><a href="https://blog.csdn.net/chenhuanqiangqiangnihao/article/details/113384991">有源蜂鸣器与无源蜂鸣器的驱动方式详解</a></li><li><a href="https://blog.csdn.net/qq_36347513/article/details/121474727">电子电路学习笔记(17)——蜂鸣器</a></li></ul><p>市面上的无源蜂鸣器有很多种,有直插的,也有 SMD 贴片的,出于尺寸考虑,我选择了贴片的</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%9C%82%E9%B8%A3%E5%99%A8.jpg" alt="蜂鸣器"><br><em>无源蜂鸣器 MLT-7525</em></p><p>驱动电路如<img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E8%9C%82%E9%B8%A3%E5%99%A8%E9%A9%B1%E5%8A%A8.png" alt="蜂鸣器驱动">图,其中 BUZZER 接到了 ESP32-C3 的 GPIO5 上</p><p><em>蜂鸣器驱动电路</em></p><h3 id="1-2-ESP32-C3-驱动蜂鸣器"><a href="#1-2-ESP32-C3-驱动蜂鸣器" class="headerlink" title="1.2 ESP32-C3 驱动蜂鸣器"></a>1.2 ESP32-C3 驱动蜂鸣器</h3><p>Arduino IDE 中,驱动无源蜂鸣器十分简单,使用函数</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">tone(BUZZER_PIN, freq, time)<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>即可,第一个参数是 GPIO 编号,第二个是频率,第三个是持续时间,下面是测试程序</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#define BUZZER_PIN 5 // GPIO5 作为蜂鸣器引脚void setup() { pinMode(BUZZER_PIN, OUTPUT); // 设置引脚为输出模式 Serial.begin(115200); // 串口初始化,用于调试 Serial.println("无源蜂鸣器测试程序启动"); // 输出中文提示}void loop() { // 测试简单音阶:Do-Re-Mi-Fa-So-La-Si-Do tone(BUZZER_PIN, 262, 500); // Do (C4),持续 500ms delay(500); tone(BUZZER_PIN, 294, 500); // Re (D4) delay(500); tone(BUZZER_PIN, 330, 500); // Mi (E4) delay(500); tone(BUZZER_PIN, 349, 500); // Fa (F4) delay(500); tone(BUZZER_PIN, 392, 500); // So (G4) delay(500); tone(BUZZER_PIN, 440, 500); // La (A4) delay(500); tone(BUZZER_PIN, 494, 500); // Si (B4) delay(500; tone(BUZZER_PIN, 523, 500); // Do (C5) delay(500); noTone(BUZZER_PIN); // 停止蜂鸣 Serial.println("一轮音阶测试完成"); // 调试输出 delay(2000); // 每轮测试后暂停 2 秒}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="二、播放音乐"><a href="#二、播放音乐" class="headerlink" title="二、播放音乐"></a>二、播放音乐</h2><h3 id="2-1-音乐数据获取"><a href="#2-1-音乐数据获取" class="headerlink" title="2.1 音乐数据获取"></a>2.1 音乐数据获取</h3><h4 id="根据简谱编写数组"><a href="#根据简谱编写数组" class="headerlink" title="根据简谱编写数组"></a>根据简谱编写数组</h4><p>简谱可以上网找,一般都可以找到,这里以《遇见》为例</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E9%81%87%E8%A7%81.jpg" alt="遇见"><br><em>《遇见》简谱</em></p><p>我的乐理知识很匮乏,所以只简单的说明如何根据简谱获取每个音的频率和时间</p><h5 id="频率"><a href="#频率" class="headerlink" title="频率"></a>频率</h5><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#define P0 0// 休止符频率#define L1 262 // 低音频率#define L2 294#define L3 330#define L4 349#define L5 392#define L6 440#define L7 494#define M1 523 // 中音频率#define M2 587#define M3 659#define M4 698#define M5 784#define M6 880#define M7 988#define H1 1047 // 高音频率#define H2 1175#define H3 1319#define H4 1397#define H5 1568#define H6 1760#define H7 1976<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>一个音符的频率取决于数字的大小和上下点的情况</p><ul><li>数字上下都没有点的,频率是 M1-M7,取决于具体数字,是几就是 M 几</li><li>数字上面有点的,频率是 H1-H7(H=High),取决于具体数字,是几就是 H 几</li><li>数字下面有点的,频率是 L1-L7(L=Low),取决于具体数字,是几就是 L 几</li></ul><h5 id="时间"><a href="#时间" class="headerlink" title="时间"></a>时间</h5><p>$\frac{4}{4}$ 表示 4 分音符为 1 拍,每小节 4 拍</p><p>由每分钟 92 拍,推算每拍的时长为</p><p>60*1000 / 92 = 652ms</p><ul><li>全音符:X—,四拍</li><li>半音符:X-,两拍</li><li>四分音符:X,一拍</li><li>数字下面加上一个横线时长就减一半</li><li>浮点:中间的点,延长前面的一半</li></ul><p>由此,可以推算每个音符的时长和频率,填入数组</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">const int melody_yu_jian[] PROGMEM={ M5,M3,L1,L5,M5,M2,//......}const int durations_yu_jian[] PROGMEM={ 652/2,652/2,652/2,652/2,652/2,652/2}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>PROGMEM</code>=<strong>Program Memory</strong>,为了节省 SRAM 来将数据存储到 FLASH 里面</p><p>可以参考视频 <a href="https://www.bilibili.com/video/BV1M1421k7gu/">零基础简谱入门教学</a>,进行更深度的乐理知识学习</p><h4 id="把-MIDI-文件转换成数组"><a href="#把-MIDI-文件转换成数组" class="headerlink" title="把 MIDI 文件转换成数组"></a>把 MIDI 文件转换成数组</h4><p>上面的方法实在是过于繁琐且枯燥,还容易出错,所以我了解到另外一种方法</p><h5 id="MIDI-文件简介"><a href="#MIDI-文件简介" class="headerlink" title="MIDI 文件简介"></a>MIDI 文件简介</h5><p>MIDI 文件不包含真实的音频信息,而是记录了乐器演奏的指令,比如什么时候哪个键被按下,弹奏的是哪个音符,音量有多大等等。这就像是一个乐谱,告诉乐器应该如何表演,但没有实际的声音。这正好能满足使用蜂鸣器播放的需求</p><h5 id="MIDI-文件获取"><a href="#MIDI-文件获取" class="headerlink" title="MIDI 文件获取"></a>MIDI 文件获取</h5><p>下面这个网站可以免费获取一首,然后支持五块钱可以在 5 天内免费下载 20 首,差不多够用了</p><ul><li><a href="https://www.midishow.com/">MidiShow - 领先的 MIDI 音乐交流与下载网站</a></li></ul><p>如果不够用的话,可以在油猴脚本里面找一找免费下载的脚本</p><p><a href="https://greasyfork.org/zh-CN/scripts/544111-midishowdownload">https://greasyfork.org/zh-CN/scripts/544111-midishowdownload</a></p><h5 id="歌曲的选择"><a href="#歌曲的选择" class="headerlink" title="歌曲的选择"></a>歌曲的选择</h5><blockquote><p>[!TIP]<br>注意蜂鸣器播放声音和喇叭差别还是很大的,尽量选择旋律好听的歌曲</p></blockquote><h5 id="MIDI-文件编辑"><a href="#MIDI-文件编辑" class="headerlink" title="MIDI 文件编辑"></a>MIDI 文件编辑</h5><p>有了 MIDI 文件之后,会发现大部分文件都有好几个音轨,而一个蜂鸣器在同一个时间只能播放一种声音,所以需要使用在线编辑工具 <a href="https://onlinesequencer.net/">Online Sequencer</a>,来保证每一时间只有一个音符,否则会导致导出数组时本来应该在同一时间播放的多个音符变成了按顺序播放,造成蜂鸣器播放的混乱。</p><p>因为同一时间只有一个音符,所以需要找到主旋律,一般来讲最高的是主旋律,但也不一定,需要根据实际情况调整,这一步往往最为麻烦</p><p>右键删除音符,在选择模式下可以批量选中</p><h5 id="MIDI-文件转换成-C-数组"><a href="#MIDI-文件转换成-C-数组" class="headerlink" title="MIDI 文件转换成 C 数组"></a>MIDI 文件转换成 C 数组</h5><p>Python 脚本 (由 DeepSeek 生成,实测可用)</p><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">import</span> mido<span class="token keyword">import</span> os<span class="token keyword">import</span> re<span class="token keyword">def</span> <span class="token function">sanitize_c_identifier</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token triple-quoted-string string">"""将字符串转换为合法的 C 标识符"""</span> name <span class="token operator">=</span> re<span class="token punctuation">.</span>sub<span class="token punctuation">(</span><span class="token string">r'[^a-zA-Z0-9_]'</span><span class="token punctuation">,</span> <span class="token string">'_'</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span> <span class="token keyword">if</span> name <span class="token keyword">and</span> name<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>isdigit<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> name <span class="token operator">=</span> <span class="token string">'_'</span> <span class="token operator">+</span> name <span class="token keyword">return</span> name<span class="token keyword">def</span> <span class="token function">midi_to_c_array</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">try</span><span class="token punctuation">:</span> mid <span class="token operator">=</span> mido<span class="token punctuation">.</span>MidiFile<span class="token punctuation">(</span>filename<span class="token punctuation">)</span> <span class="token keyword">except</span> Exception <span class="token keyword">as</span> e<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Error opening MIDI file </span><span class="token interpolation"><span class="token punctuation">{</span>filename<span class="token punctuation">}</span></span><span class="token string">: </span><span class="token interpolation"><span class="token punctuation">{</span>e<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token keyword">return</span> melody <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token comment"># 频率数组,0 表示停顿</span> durations <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token comment"># 持续时间(毫秒)</span> ticks_per_beat <span class="token operator">=</span> mid<span class="token punctuation">.</span>ticks_per_beat <span class="token keyword">if</span> mid<span class="token punctuation">.</span>ticks_per_beat <span class="token keyword">else</span> <span class="token number">480</span> tempo <span class="token operator">=</span> <span class="token number">500000</span> <span class="token comment"># 默认 120 BPM</span> <span class="token comment"># 获取第一个 set_tempo 事件</span> <span class="token keyword">for</span> track <span class="token keyword">in</span> mid<span class="token punctuation">.</span>tracks<span class="token punctuation">:</span> <span class="token keyword">for</span> msg <span class="token keyword">in</span> track<span class="token punctuation">:</span> <span class="token keyword">if</span> msg<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">'set_tempo'</span> <span class="token keyword">and</span> msg<span class="token punctuation">.</span>is_meta<span class="token punctuation">:</span> tempo <span class="token operator">=</span> msg<span class="token punctuation">.</span>tempo <span class="token keyword">break</span> <span class="token keyword">if</span> tempo <span class="token operator">!=</span> <span class="token number">500000</span><span class="token punctuation">:</span> <span class="token keyword">break</span> <span class="token comment"># 合并所有 track 的消息,并按时间排序</span> events <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token keyword">for</span> track <span class="token keyword">in</span> mid<span class="token punctuation">.</span>tracks<span class="token punctuation">:</span> absolute_time <span class="token operator">=</span> <span class="token number">0</span> <span class="token keyword">for</span> msg <span class="token keyword">in</span> track<span class="token punctuation">:</span> absolute_time <span class="token operator">+=</span> msg<span class="token punctuation">.</span>time <span class="token keyword">if</span> msg<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token keyword">in</span> <span class="token punctuation">(</span><span class="token string">'note_on'</span><span class="token punctuation">,</span> <span class="token string">'note_off'</span><span class="token punctuation">)</span> <span class="token keyword">or</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">'note_on'</span> <span class="token keyword">and</span> msg<span class="token punctuation">.</span>velocity <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">:</span> events<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string">'time'</span><span class="token punctuation">:</span> absolute_time<span class="token punctuation">,</span> <span class="token string">'message'</span><span class="token punctuation">:</span> msg<span class="token punctuation">}</span><span class="token punctuation">)</span> events<span class="token punctuation">.</span>sort<span class="token punctuation">(</span>key<span class="token operator">=</span><span class="token keyword">lambda</span> x<span class="token punctuation">:</span> x<span class="token punctuation">[</span><span class="token string">'time'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment"># 按时间排序</span> active_notes <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment"># (note, channel) -> start_tick</span> current_time <span class="token operator">=</span> <span class="token number">0</span> <span class="token comment"># 当前时间(ticks)</span> first_note_played <span class="token operator">=</span> <span class="token boolean">False</span> <span class="token comment"># 标志:是否已播放第一个音符</span> <span class="token keyword">for</span> event <span class="token keyword">in</span> events<span class="token punctuation">:</span> tick <span class="token operator">=</span> event<span class="token punctuation">[</span><span class="token string">'time'</span><span class="token punctuation">]</span> msg <span class="token operator">=</span> event<span class="token punctuation">[</span><span class="token string">'message'</span><span class="token punctuation">]</span> <span class="token comment"># === 计算当前时间和上一个事件之间的空白 ===</span> gap_ticks <span class="token operator">=</span> tick <span class="token operator">-</span> current_time <span class="token keyword">if</span> gap_ticks <span class="token operator">></span> <span class="token number">0</span> <span class="token keyword">and</span> <span class="token builtin">len</span><span class="token punctuation">(</span>active_notes<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span> gap_ms <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>mido<span class="token punctuation">.</span>tick2second<span class="token punctuation">(</span>gap_ticks<span class="token punctuation">,</span> ticks_per_beat<span class="token punctuation">,</span> tempo<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span> <span class="token comment"># 只有在第一个音符之后,且时长 ≤ 5000ms 才记录空白</span> <span class="token keyword">if</span> first_note_played <span class="token keyword">and</span> gap_ms <span class="token operator"><=</span> <span class="token number">5000</span><span class="token punctuation">:</span> melody<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> durations<span class="token punctuation">.</span>append<span class="token punctuation">(</span>gap_ms<span class="token punctuation">)</span> <span class="token comment"># 如果还没开始,且有空白,跳过(不记录开头空白)</span> current_time <span class="token operator">=</span> tick <span class="token comment"># === 处理 note_on ===</span> <span class="token keyword">if</span> msg<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">'note_on'</span> <span class="token keyword">and</span> msg<span class="token punctuation">.</span>velocity <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">:</span> key <span class="token operator">=</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>note<span class="token punctuation">,</span> msg<span class="token punctuation">.</span>channel<span class="token punctuation">)</span> <span class="token keyword">if</span> key <span class="token keyword">not</span> <span class="token keyword">in</span> active_notes<span class="token punctuation">:</span> active_notes<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> tick <span class="token comment"># === 处理 note_off 或 note_on velocity=0 ===</span> <span class="token keyword">elif</span> msg<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">'note_off'</span> <span class="token keyword">or</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">'note_on'</span> <span class="token keyword">and</span> msg<span class="token punctuation">.</span>velocity <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">:</span> key <span class="token operator">=</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>note<span class="token punctuation">,</span> msg<span class="token punctuation">.</span>channel<span class="token punctuation">)</span> <span class="token keyword">if</span> key <span class="token keyword">in</span> active_notes<span class="token punctuation">:</span> start_tick <span class="token operator">=</span> active_notes<span class="token punctuation">.</span>pop<span class="token punctuation">(</span>key<span class="token punctuation">)</span> note_duration_ticks <span class="token operator">=</span> tick <span class="token operator">-</span> start_tick note_duration_ms <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>mido<span class="token punctuation">.</span>tick2second<span class="token punctuation">(</span>note_duration_ticks<span class="token punctuation">,</span> ticks_per_beat<span class="token punctuation">,</span> tempo<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span> frequency <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span><span class="token number">440</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">**</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>msg<span class="token punctuation">.</span>note <span class="token operator">-</span> <span class="token number">69</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> melody<span class="token punctuation">.</span>append<span class="token punctuation">(</span>frequency<span class="token punctuation">)</span> durations<span class="token punctuation">.</span>append<span class="token punctuation">(</span>note_duration_ms<span class="token punctuation">)</span> <span class="token comment"># 标记已播放第一个音符</span> <span class="token keyword">if</span> <span class="token keyword">not</span> first_note_played<span class="token punctuation">:</span> first_note_played <span class="token operator">=</span> <span class="token boolean">True</span> <span class="token keyword">if</span> <span class="token keyword">not</span> melody<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Warning: No notes or valid rests found in </span><span class="token interpolation"><span class="token punctuation">{</span>filename<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token comment"># === 生成唯一数组名 ===</span> base_name <span class="token operator">=</span> os<span class="token punctuation">.</span>path<span class="token punctuation">.</span>splitext<span class="token punctuation">(</span>os<span class="token punctuation">.</span>path<span class="token punctuation">.</span>basename<span class="token punctuation">(</span>filename<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> safe_name <span class="token operator">=</span> sanitize_c_identifier<span class="token punctuation">(</span>base_name<span class="token punctuation">.</span>lower<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> melody_array_name <span class="token operator">=</span> <span class="token string-interpolation"><span class="token string">f"melody_</span><span class="token interpolation"><span class="token punctuation">{</span>safe_name<span class="token punctuation">}</span></span><span class="token string">"</span></span> durations_array_name <span class="token operator">=</span> <span class="token string-interpolation"><span class="token string">f"durations_</span><span class="token interpolation"><span class="token punctuation">{</span>safe_name<span class="token punctuation">}</span></span><span class="token string">"</span></span> <span class="token comment"># === 写入头文件 ===</span> output_filename <span class="token operator">=</span> <span class="token string-interpolation"><span class="token string">f"</span><span class="token interpolation"><span class="token punctuation">{</span>base_name<span class="token punctuation">}</span></span><span class="token string">.h"</span></span> <span class="token keyword">try</span><span class="token punctuation">:</span> <span class="token keyword">with</span> <span class="token builtin">open</span><span class="token punctuation">(</span>output_filename<span class="token punctuation">,</span> <span class="token string">'w'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"// Generated from </span><span class="token interpolation"><span class="token punctuation">{</span>filename<span class="token punctuation">}</span></span><span class="token string">\n"</span></span><span class="token punctuation">)</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"// Song length: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token builtin">len</span><span class="token punctuation">(</span>melody<span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token string"> events (notes + rests)\n"</span></span><span class="token punctuation">)</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"// Skipped leading rests and rests > 5000ms\n\n"</span></span><span class="token punctuation">)</span> <span class="token comment"># 旋律数组</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"static const int </span><span class="token interpolation"><span class="token punctuation">{</span>melody_array_name<span class="token punctuation">}</span></span><span class="token string">[] PROGMEM = {{\n"</span></span><span class="token punctuation">)</span> <span class="token keyword">for</span> i<span class="token punctuation">,</span> note <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>melody<span class="token punctuation">)</span><span class="token punctuation">:</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f" </span><span class="token interpolation"><span class="token punctuation">{</span>note<span class="token punctuation">}</span></span><span class="token string">,"</span></span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">10</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">)</span> <span class="token keyword">else</span><span class="token punctuation">:</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">" "</span><span class="token punctuation">)</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">"\n};\n\n"</span><span class="token punctuation">)</span> <span class="token comment"># 时长数组</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"const int </span><span class="token interpolation"><span class="token punctuation">{</span>durations_array_name<span class="token punctuation">}</span></span><span class="token string">[] PROGMEM = {{\n"</span></span><span class="token punctuation">)</span> <span class="token keyword">for</span> i<span class="token punctuation">,</span> duration <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>durations<span class="token punctuation">)</span><span class="token punctuation">:</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f" </span><span class="token interpolation"><span class="token punctuation">{</span>duration<span class="token punctuation">}</span></span><span class="token string">,"</span></span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">10</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">)</span> <span class="token keyword">else</span><span class="token punctuation">:</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">" "</span><span class="token punctuation">)</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">"\n};\n"</span><span class="token punctuation">)</span> <span class="token comment"># 长度宏</span> f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"\n#define MELODY_</span><span class="token interpolation"><span class="token punctuation">{</span>safe_name<span class="token punctuation">.</span>upper<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token string">_LENGTH </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token builtin">len</span><span class="token punctuation">(</span>melody<span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token string">\n"</span></span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"✅ Successfully wrote: </span><span class="token interpolation"><span class="token punctuation">{</span>output_filename<span class="token punctuation">}</span></span><span class="token string"> (</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token builtin">len</span><span class="token punctuation">(</span>melody<span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token string"> events)"</span></span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f" Arrays: </span><span class="token interpolation"><span class="token punctuation">{</span>melody_array_name<span class="token punctuation">}</span></span><span class="token string">[], </span><span class="token interpolation"><span class="token punctuation">{</span>durations_array_name<span class="token punctuation">}</span></span><span class="token string">[]"</span></span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token keyword">not</span> <span class="token builtin">any</span><span class="token punctuation">(</span>melody<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment"># 全是 0?</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">" ⚠️ Warning: melody contains only rests or low notes"</span><span class="token punctuation">)</span> <span class="token keyword">except</span> Exception <span class="token keyword">as</span> e<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"❌ Error writing </span><span class="token interpolation"><span class="token punctuation">{</span>output_filename<span class="token punctuation">}</span></span><span class="token string">: </span><span class="token interpolation"><span class="token punctuation">{</span>e<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span><span class="token comment"># ========================</span><span class="token comment"># Main Execution</span><span class="token comment"># ========================</span><span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span> current_dir <span class="token operator">=</span> <span class="token string">"."</span> midi_files <span class="token operator">=</span> <span class="token punctuation">[</span>f <span class="token keyword">for</span> f <span class="token keyword">in</span> os<span class="token punctuation">.</span>listdir<span class="token punctuation">(</span>current_dir<span class="token punctuation">)</span> <span class="token keyword">if</span> f<span class="token punctuation">.</span>lower<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>endswith<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token string">'.mid'</span><span class="token punctuation">,</span> <span class="token string">'.midi'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token keyword">if</span> <span class="token keyword">not</span> midi_files<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"No MIDI files found in the current directory."</span><span class="token punctuation">)</span> <span class="token keyword">else</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Found </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token builtin">len</span><span class="token punctuation">(</span>midi_files<span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token string"> MIDI file(s):"</span></span><span class="token punctuation">)</span> <span class="token keyword">for</span> f <span class="token keyword">in</span> midi_files<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f" → </span><span class="token interpolation"><span class="token punctuation">{</span>f<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"-"</span> <span class="token operator">*</span> <span class="token number">40</span><span class="token punctuation">)</span> <span class="token keyword">for</span> midi_file <span class="token keyword">in</span> midi_files<span class="token punctuation">:</span> midi_to_c_array<span class="token punctuation">(</span>midi_file<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>把 <code>.mid</code> 文件和这个脚本放入同一目录,把该脚本保存为 <code>midi_to_c_array.py</code>,按照教程安装 Python,打开 cmd,输入</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">pip <span class="token function">install</span> midopython midi_to_c_array.py<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>即可在当前目录查看生成的 <code>.h</code> 文件,有两个数组,包含每个音符的频率和时间数据</p><h3 id="2-2-播放音乐"><a href="#2-2-播放音乐" class="headerlink" title="2.2 播放音乐"></a>2.2 播放音乐</h3><p>每次都调用 <code>tone</code> 函数播放每个音符的频率和时间即可</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include "Music_processed/cai_bu_tou.h"#include "Music_processed/cheng_du.h"#include "Music_processed/hai_kuo_tian_kong.h"#include "Music_processed/hong_dou.h"#include "Music_processed/hou_lai.h"#include "Music_processed/kai_shi_dong_le.h"#include "Music_processed/lv_se.h"#include "Music_processed/qing_hua_ci.h"#include "Music_processed/xin_qiang.h"#include "Music_processed/you_dian_tian.h"#include "Music_processed/chun_jiao_yu_zhi_ming.h"#include "Music_processed/Windows.h"#include "Music_processed/mi_ren_de_wei_xian.h"// 定义蜂鸣器连接的引脚#define BUZZER_PIN 5 // 请根据您的硬件连接修改此引脚号// 定义 Song 结构体typedef struct { const char* name; const int* melody; const int* durations; int length;} Song;const Song songs[] PROGMEM= { { "Cai Bu Tou", melody_cai_bu_tou, durations_cai_bu_tou, sizeof(melody_cai_bu_tou) / sizeof(melody_cai_bu_tou[0]), 0 }, {"Chun Jiao Yu Zhi Ming",melody_chun_jiao_yu_zhi_ming,durations_chun_jiao_yu_zhi_ming,sizeof(melody_chun_jiao_yu_zhi_ming)/sizeof(melody_chun_jiao_yu_zhi_ming[0]),1}, { "Cheng Du", melody_cheng_du, durations_cheng_du, sizeof(melody_cheng_du)/sizeof(melody_cheng_du[0]), 2 }, {"Hai Kuo Tian Kong",melody_hai_kuo_tian_kong,durations_hai_kuo_tian_kong, sizeof(melody_hai_kuo_tian_kong)/sizeof(melody_hai_kuo_tian_kong[0]), 3 }, { "Hong Dou", melody_hong_dou, durations_hong_dou, sizeof(melody_hong_dou)/sizeof(melody_hong_dou[0]), 4 }, { "Hou Lai", melody_hou_lai, durations_hou_lai, sizeof(melody_hou_lai)/sizeof(melody_hou_lai[0]), 0 }, { "Kai Shi Dong Le", melody_kai_shi_dong_le, durations_kai_shi_dong_le, sizeof(melody_kai_shi_dong_le)/sizeof(melody_kai_shi_dong_le[0]), 1 }, { "Lv Se", melody_lv_se, durations_lv_se, sizeof(melody_lv_se)/sizeof(melody_lv_se[0]), 2 }, {"Mi Ren De Wei Xian",melody_mi_ren_de_wei_xian,durations_mi_ren_de_wei_xian,sizeof(melody_mi_ren_de_wei_xian)/sizeof(melody_mi_ren_de_wei_xian[0]),3}, { "Qing Hua Ci", melody_qing_hua_ci, durations_qing_hua_ci, sizeof(melody_qing_hua_ci)/sizeof(melody_qing_hua_ci[0]), 3 }, { "Xin Qiang", melody_xin_qiang, durations_xin_qiang,sizeof(melody_xin_qiang)/sizeof(melody_xin_qiang[0]), 4 }, { "You Dian Tian", melody_you_dian_tian, durations_you_dian_tian, sizeof(melody_you_dian_tian)/sizeof(melody_you_dian_tian[0]), 0 }, {"Windows XP",melody_windows,durations_windows,sizeof(melody_windows) / sizeof(melody_windows[0]),3},};const int numSongs = sizeof(songs) / sizeof(songs[0]);// 播放音乐函数void playSong(int songIndex) { // 检查索引是否有效 if (songIndex < 0 || songIndex >= numSongs) { Serial.print("Invalid song index: "); Serial.println(songIndex); return; } Song song; memcpy_P(&song, &songs[songIndex], sizeof(Song)); Serial.print("Playing song: "); Serial.println(song.name); // 播放旋律 for (int i = 0; i < song.length; i++) { // 从 PROGMEM 读取音符和持续时间 int note = pgm_read_word(&(song.melody[i])); int duration = pgm_read_word(&(song.durations[i])); Serial.print("Note: "); Serial.print(note); Serial.print(", Duration: "); Serial.println(duration); // 播放音符 tone(BUZZER_PIN, note, duration); // 等待音符播放完成,并留一点间隔 delay(duration * 1.1); // 增加 10% 间隔时间 // 确保蜂鸣器在下一个音符前关闭 noTone(BUZZER_PIN); } Serial.print("Finished playing song: "); Serial.println(song.name);}void setup() { // 初始化串口监视器 Serial.begin(115200); Serial.println("Buzzer Music Player Started"); // 设置蜂鸣器引脚为输出 pinMode(BUZZER_PIN, OUTPUT);}void loop() { // 循环播放所有歌曲 for (int i = 0; i < numSongs; i++) { playSong(i); delay(1000); // 每首歌之间暂停 1 秒 } Serial.println("All songs played once. Restarting..."); delay(2000); // 全部播放完后暂停 2 秒再重新开始}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> ESP32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
<tag> Arduino </tag>
<tag> music </tag>
<tag> midi </tag>
</tags>
</entry>
<entry>
<title>ESP32 使用 Adafruit_AHTX0 库读取 AHT20 温湿度数据</title>
<link href="/2025/09/03/2025-09-03-esp32-aht20-arduino/"/>
<url>/2025/09/03/2025-09-03-esp32-aht20-arduino/</url>
<content type="html"><![CDATA[<h1 id="ESP32-使用-Adafruit-AHTX0-库读取-AHT20-温湿度数据并通过串口输出"><a href="#ESP32-使用-Adafruit-AHTX0-库读取-AHT20-温湿度数据并通过串口输出" class="headerlink" title="ESP32 使用 Adafruit_AHTX0 库读取 AHT20 温湿度数据并通过串口输出"></a>ESP32 使用 Adafruit_AHTX0 库读取 AHT20 温湿度数据并通过串口输出</h1><h2 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h2><h3 id="1-1-AHT20"><a href="#1-1-AHT20" class="headerlink" title="1.1 AHT20"></a>1.1 AHT20</h3><h4 id="AHT20-简介"><a href="#AHT20-简介" class="headerlink" title="AHT20 简介"></a>AHT20 简介</h4><p>AHT20 是温湿度传感器,配有一个全新设计的 ASIC 专用芯片、一个经过改进的 MEMS 半导体电容式湿度传感元件和一个标准的片上温度传感元件,其性能已经大大提升甚至超出了前一代传感器的可靠性水平,新一代温湿度传感器,经过改进使其在恶劣环境下的性能更稳定。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedAHT20.jpg" alt="AHT20"><br><em>AHT20 传感器</em></p><h4 id="AHT20-外观"><a href="#AHT20-外观" class="headerlink" title="AHT20 外观"></a>AHT20 外观</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedAHT20%E5%A4%96%E8%A7%82.png" alt="AHT20外观"></p><p><em>AHT20 外观</em></p><p>可以发现,AHT20 尺寸很小,而且焊盘在底下而没有伸出来,导致只能使用加热台进行焊接,而几乎不可能用烙铁,不过市面上有带排针的 AHT20 模块</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedAHT20%E6%A8%A1%E5%9D%97.jpg" alt="AHT20模块"><br><em>带排针的 AHT20 模块</em></p><h4 id="AHT20-引脚定义"><a href="#AHT20-引脚定义" class="headerlink" title="AHT20 引脚定义"></a>AHT20 引脚定义</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedAHT20%E5%BC%95%E8%84%9A.png" alt="AHT20引脚"><br><em>AHT20 引脚定义</em></p><h4 id="AHT20-电气特性"><a href="#AHT20-电气特性" class="headerlink" title="AHT20 电气特性"></a>AHT20 电气特性</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedAHT20%E7%94%B5%E6%B0%94.png" alt="AHT20电气"><br><em>AHT20 电气特性</em></p><h4 id="AHT20-通讯"><a href="#AHT20-通讯" class="headerlink" title="AHT20 通讯"></a>AHT20 通讯</h4><p>有关 IIC 通讯的部分网上有详尽的教程,可参考</p><ul><li><a href="https://blog.csdn.net/qq_39829913/article/details/104718185">基础通信协议之 IIC (I2C) 详细讲解</a></li><li><a href="https://www.bilibili.com/video/BV1dg4y1H773/">4 分钟看懂!I2C 通讯协议 最简单的总线通讯!</a></li></ul><h5 id="启动传感器"><a href="#启动传感器" class="headerlink" title="启动传感器"></a>启动传感器</h5><p>第一步,将传感器上电,电压为所选择的 VDD 电源电压 (范围介于 2.2V 与 5.5V 之间)。上电后传感器需要不少于 100ms 时间(此时 SCL 为高电平) 以达到空闲状态即做好准备接收由主机 (MCU) 发送的命令</p><h5 id="启动-停止时序"><a href="#启动-停止时序" class="headerlink" title="启动/停止时序"></a>启动/停止时序</h5><p>每个传输序列都以 Start 状态作为开始并以 Stop 状态作为结束</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%BC%80%E5%A7%8B%E5%81%9C%E6%AD%A2.png" alt="开始停止"><br><em>启动/停止时序</em></p><h5 id="发送命令"><a href="#发送命令" class="headerlink" title="发送命令"></a>发送命令</h5><p>在启动传输后,随后传输的 I2C 首字节包括 7 位的 I2C 设备地址 0x38 和一个 SDA 方向位 x(读 R:’1’,写 W: ‘0’)。在第 8 个 SCL 时钟下降沿之后,通过拉低 SDA 引脚 (ACK 位),指示传感器数接收正常。在发送测量命令 0xAC 之后,MCU 必须等到测量完成</p><h5 id="读取温湿度"><a href="#读取温湿度" class="headerlink" title="读取温湿度"></a>读取温湿度</h5><p>具体细节可参考</p><ul><li><a href="https://blog.csdn.net/gyui21g/article/details/138686408">ESP32 驱动 AHT20 温湿度传感器源代码</a></li><li><a href="https://item.szlcsc.com/515997.html">AHT20 中文资料_最新报价_数据手册下载</a></li></ul><h3 id="1-2-ESP32-C3-的-SuperMini-的-IIC"><a href="#1-2-ESP32-C3-的-SuperMini-的-IIC" class="headerlink" title="1.2 ESP32-C3 的 SuperMini 的 IIC"></a>1.2 ESP32-C3 的 SuperMini 的 IIC</h3><p>按照引脚图,</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedESP32C3.png" alt="ESP32C3"><br><em>ESP32-C3 SuperMini 引脚图</em></p><p>默认 SDA 是 GPIO8,SCL 是 GPIO9,实际可以更改为任意 GPIO 进行软件模拟 IIC 时序,我使用的是 GPIO20 为 SDA,GPIO21 为 SCL,只需在程序中加入</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">//Wire.begin(I2C_SDA, I2C_SCL);Wire.begin(20, 21);<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>即可,ESP32-C3 的 IIC 资源详细介绍可参考</p><ul><li><a href="https://blog.csdn.net/RMDYBW/article/details/131761502">ESP32C3 学习&开发之路——最全的 IIC 协议</a></li></ul><h2 id="二、Arduino-程序讲解"><a href="#二、Arduino-程序讲解" class="headerlink" title="二、Arduino 程序讲解"></a>二、Arduino 程序讲解</h2><p>首先,到 Arduino IDE 的库管理——Adafruit_AHTX0,Arduino IDE 会自动安装相关依赖,我使用的是 2.0.5 版本</p><p>该库的使用十分简单</p><table><thead><tr><th align="left"><strong>API/对象</strong></th><th align="left"><strong>参数</strong></th><th align="left"><strong>返回值</strong></th><th align="left"><strong>说明</strong></th></tr></thead><tbody><tr><td align="left"><code>Adafruit_AHTX0</code></td><td align="left">无</td><td align="left">无</td><td align="left">传感器对象构造函数。</td></tr><tr><td align="left"><code>bool begin()</code></td><td align="left">无</td><td align="left">bool</td><td align="left">初始化传感器。成功返回 true,失败返回 false。通常需要检查返回值以确保传感器正常工作。</td></tr><tr><td align="left"><code>void getEvent()</code></td><td align="left"><code>sensors_event_t* humidity</code> <code>sensors_event_t* temp</code></td><td align="left">无</td><td align="left"><strong>核心方法</strong>。获取最新的温湿度数据,数据填充到传入的 <code>humidity</code> 和 <code>temp</code> 对象中。</td></tr><tr><td align="left"><code>sensors_event_t</code></td><td align="left">无</td><td align="left"></td><td align="left">用于存储传感器事件数据的结构体,包含测量值、时间戳等信息。</td></tr><tr><td align="left"><code>float temperature</code></td><td align="left">无</td><td align="left">float</td><td align="left">从 <code>sensors_event_t</code> 结构体中提取温度值(单位:摄氏度)。</td></tr><tr><td align="left"><code>float relative_humidity</code></td><td align="left">无</td><td align="left">float</td><td align="left">从 <code>sensors_event_t</code> 结构体中提取湿度值(单位:%RH)。</td></tr></tbody></table><h3 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h3><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include <Adafruit_AHTX0.h>#include <Wire.h>Adafruit_AHTX0 aht;void setup() { //Wire.begin(I2C_SDA, I2C_SCL); Wire.begin(20, 21); Serial.begin(115200); Serial.println("Adafruit AHT10/AHT20 demo!"); if (! aht.begin()) { Serial.println("Could not find AHT? Check wiring"); while (1) delay(10); } Serial.println("AHT10 or AHT20 found");}void loop() { sensors_event_t humidity, temp; aht.getEvent(&humidity, &temp); Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" degrees C"); Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println("% rH"); delay(500);}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="三、效果展示"><a href="#三、效果展示" class="headerlink" title="三、效果展示"></a>三、效果展示</h2><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%95%88%E6%9E%9C%E5%B1%95%E7%A4%BA.png" alt="效果展示"><br><em>串口输出温湿度数据</em></p>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> ESP32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
<tag> Arduino </tag>
<tag> sensor </tag>
</tags>
</entry>
<entry>
<title>ESP32-C3 SuperMini 驱动 ST7789 TFT 屏幕教程</title>
<link href="/2025/09/02/2025-09-02-esp32-tft-st7789/"/>
<url>/2025/09/02/2025-09-02-esp32-tft-st7789/</url>
<content type="html"><![CDATA[<h1 id="ESP32-C3-SuperMini-驱动-ST7789-TFT-屏幕"><a href="#ESP32-C3-SuperMini-驱动-ST7789-TFT-屏幕" class="headerlink" title="ESP32-C3 SuperMini 驱动 ST7789 TFT 屏幕"></a>ESP32-C3 SuperMini 驱动 ST7789 TFT 屏幕</h1><h2 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h2><p>最近做的东西要用到 ESP32,但是又不想自己做最小系统,无意间看到了 ESP32-C3 SuperMini 开发板,顿时被它小巧的外观、强大的功能吸引,用起来体验很不错。有人说它的 WIFI 信号不好,一开始我也有这种感觉,后来发现似乎修改一些 WIFI 设置可以有明显改善,这个之后再说。关于这块开发板的介绍可以查看 <a href="https://www.nologo.tech/product/esp32/esp32c3/esp32c3supermini/esp32C3SuperMini.html#%E4%BA%A7%E5%93%81%E5%8F%82%E6%95%B0">ESP32C3 SuperMini 入门</a></p><p>驱动屏幕的过程中遇到了很多坑,最后是参考了 <a href="https://esp32.com/viewtopic.php?t=37617&start=20">ESP3-C3 Super Mini wiring to ST7789 - ESP32 Forum</a> 论坛才得以解决,希望分享出来能够帮助大家</p><h2 id="二、硬件准备"><a href="#二、硬件准备" class="headerlink" title="二、硬件准备"></a>二、硬件准备</h2><ul><li>ESP32C3-SuperMini 开发板</li></ul><p><img src="https://i-blog.csdnimg.cn/direct/c7df299feb5f432cabde120c81109782.jpeg" alt="ESP32C3-SuperMini 开发板"><br><em>ESP32-C3 SuperMini 开发板</em></p><ul><li>TYPE-C 数据线</li><li>1.3 寸 ST7789 驱动的 240*240 TFT 屏幕模块 (某些屏幕的 SDA 标为 MOSI)</li></ul><p><img src="https://i-blog.csdnimg.cn/direct/ea184f9dbb304d04b19d5224b311fd41.jpeg" alt="ST7789 TFT 屏幕"><br><em>1.3 寸 ST7789 TFT 屏幕模块</em></p><ul><li>一台安装了 Arduino IDE 的电脑</li></ul><p>关于 Arduino IDE 如何进行 ESP32 的开发,网上有详尽的教程,可参考 <a href="https://blog.csdn.net/qq-28877125/article/details/107122264">Arduino–Arduino IDE 上安装 ESP32 开发环境(两种方法)</a></p><p>可以先烧录这段代码测试一下</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> led <span class="token operator">=</span> <span class="token number">8</span><span class="token punctuation">;</span><span class="token keyword">void</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">pinMode</span><span class="token punctuation">(</span>led<span class="token punctuation">,</span> OUTPUT<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">loop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">digitalWrite</span><span class="token punctuation">(</span>led<span class="token punctuation">,</span> HIGH<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// turn the LED off</span> <span class="token function">delay</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// wait for a second</span> <span class="token function">digitalWrite</span><span class="token punctuation">(</span>led<span class="token punctuation">,</span> LOW<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// turn the LED on</span> <span class="token function">delay</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// wait for a second</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>对于 ESP32-C3 SuperMini 来说,板载 LED 连接在 GPIO8,烧录成功会看到板载蓝色 LED 闪烁</p><h2 id="三、硬件接线"><a href="#三、硬件接线" class="headerlink" title="三、硬件接线"></a>三、硬件接线</h2><p>ESP32C3 与 TFT 屏幕的接线如下:</p><table><thead><tr><th>ESP32C3</th><th>TFT</th></tr></thead><tbody><tr><td>GPIO06</td><td>SDA (MOSI)</td></tr><tr><td>GPIO04</td><td>SCL (SCLK)</td></tr><tr><td>GPIO08</td><td>DC</td></tr><tr><td>GPIO09</td><td>RST</td></tr><tr><td>3V3/NC</td><td>BLK</td></tr><tr><td>3V3</td><td>VCC</td></tr><tr><td>GND</td><td>GND</td></tr></tbody></table><p>其中,BLK 为背光引脚,可以接 3V3 或任一 GPIO 或不接,我在这里选择接入 3V3 以始终开启背光</p><h2 id="四、软件准备"><a href="#四、软件准备" class="headerlink" title="四、软件准备"></a>四、软件准备</h2><h3 id="4-1-安装-TFT-eSPI-库"><a href="#4-1-安装-TFT-eSPI-库" class="headerlink" title="4.1 安装 TFT_eSPI 库"></a>4.1 安装 TFT_eSPI 库</h3><p>在 Arduino IDE——库管理中搜索 TFT_eSPI,第一个就是,安装即可</p><h3 id="4-2-更改配置文件"><a href="#4-2-更改配置文件" class="headerlink" title="4.2 更改配置文件"></a>4.2 更改配置文件</h3><p>在 <code>D:\documents\Arduino\libraries\TFT_eSPI</code> 里找到 <code>User_Setup.h</code></p><blockquote><p>[!NOTE]<br><code>D:\documents\</code> 区别于自己电脑”文档”目录的位置,默认在 C 盘</p></blockquote><p>更改如下:</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#define USER_SETUP_INFO "User_Setup"#define ST7789_DRIVER#define TFT_RGB_ORDER TFT_BGR#define TFT_WIDTH 240#define TFT_HEIGHT 240#define TFT_MISO 5#define TFT_MOSI 6#define TFT_SCLK 4#define TFT_CS -1 // Chip select control pin#define TFT_DC 8 // Data Command control pin#define TFT_RST 9 // Reset pin (could connect to RST pin)#define TOUCH_CS -1 // Chip select pin (T_CS) of touch screen#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts#define SPI_FREQUENCY 27000000<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后更改 <code>D:\documents\Arduino\libraries\TFT_eSPI\Processors\TFT_eSPI_ESP32_C3.h</code>,替换第 71 行的</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#define SPI_PORT SPI2_HOST<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>为</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#if ESP_ARDUINO_VERSION_MAJOR < 3#define SPI_PORT SPI2_HOST#else#define SPI_PORT 2#endif<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="4-3-更改主程序代码"><a href="#4-3-更改主程序代码" class="headerlink" title="4.3 更改主程序代码"></a>4.3 更改主程序代码</h3><p>更改 <code>main.ino</code> 为</p><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include <TFT_eSPI.h>TFT_eSPI tft = TFT_eSPI();void setup() { tft.init(); tft.setRotation(0); tft.fillScreen(TFT_RED); tft.setTextColor(TFT_WHITE, TFT_RED); tft.drawString("Hello ESP32-C3 Super Mini!", 10, 10, 2);}void loop() {}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后在工具——开发板选择 ESP32 C3 Dev Module,烧录代码</p><h2 id="五、常见问题"><a href="#五、常见问题" class="headerlink" title="五、常见问题"></a>五、常见问题</h2><h3 id="5-1-烧录后屏幕无显示"><a href="#5-1-烧录后屏幕无显示" class="headerlink" title="5.1 烧录后屏幕无显示"></a>5.1 烧录后屏幕无显示</h3><p>首先检查接线</p><table><thead><tr><th>ESP32C3</th><th>TFT</th></tr></thead><tbody><tr><td>GPIO06</td><td>SDA (MOSI)</td></tr><tr><td>GPIO04</td><td>SCL (SCLK)</td></tr><tr><td>GPIO08</td><td>DC</td></tr><tr><td>GPIO09</td><td>RST</td></tr><tr><td>3V3/NC</td><td>BLK</td></tr><tr><td>3V3</td><td>VCC</td></tr><tr><td>GND</td><td>GND</td></tr></tbody></table><p>然后检查上面的步骤是否一致,我用的 TFT_eSPI 库是 2.5.43 版本,可以尝试更改成这个版本</p><blockquote><p>[!TIP]</p><ul><li>如果烧录失败,按住 BOOT 按键,按下 RST 按键,松开 RST 按键,再松开 BOOT 按键进入下载模式再烧录</li><li>如果烧录成功,可能需要按一下 RST 按键才能运行</li></ul></blockquote><h3 id="5-2-烧录成功却黑屏"><a href="#5-2-烧录成功却黑屏" class="headerlink" title="5.2 烧录成功却黑屏"></a>5.2 烧录成功却黑屏</h3><p>这是我一直遇到的问题,重复上电和复位好多次才成功,后来发现可能是启动模式 Strapping Pins 的问题,后来避开了图上的引脚,换了几个肯定没问题的引脚 (忘了具体是啥了,反正避开这几个似乎都可以),就没有这个问题了。但是我的板子已经按照最初的接线画完而且打出来了,不想再改了,就想着再用之前的接线试试,结果居然到现在都没出现类似的问题,特别奇怪。</p><p>如果有大佬知道原因的可以在评论区交流,谢谢!♪(・ω・) ノ</p><h2 id="六、效果展示"><a href="#六、效果展示" class="headerlink" title="六、效果展示"></a>六、效果展示</h2><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%B1%8F%E5%B9%95%E7%82%B9%E4%BA%AE.jpg" alt="屏幕点亮"></p><h2 id="额外:移植到PlatformIO"><a href="#额外:移植到PlatformIO" class="headerlink" title="额外:移植到PlatformIO"></a>额外:移植到PlatformIO</h2><p>Arduino编译实在是太慢了,而且几乎每次都要重头编译所有的库,对于文件稍微多一些的项目就要编译将近十分钟,非常不便于调试,所以我一直有把ArduinoIDE上的项目移植到PlatformIO的想法,但是苦于TFT_eSPI库移植只有屏幕一直无法点亮,似乎一直在重启。只能一直搁置,饱受缓慢编译速度的折磨。<br>直到今天,看到了一篇文章<a href="https://mbb.eet-china.com/blog/3924658-463999.html">【原创奖励】esp32s3使用platformio 点亮1.69寸TFT历程-面包板社区</a></p><p>其中提到,在platformIO设置<code>platformio.ini</code>配置文件为以下内容</p><pre class="line-numbers language-ini" data-language="ini"><code class="language-ini"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">env:esp32-c3-devkitc-02</span><span class="token punctuation">]</span></span><span class="token key attr-name">platform</span> <span class="token punctuation">=</span> <span class="token value attr-value">espressif32@6.5.0</span><span class="token key attr-name">board</span> <span class="token punctuation">=</span> <span class="token value attr-value">esp32-c3-devkitc-02</span><span class="token key attr-name">framework</span> <span class="token punctuation">=</span> <span class="token value attr-value">arduino</span><span class="token key attr-name">build_flags</span> <span class="token punctuation">=</span> <span class="token key attr-name">-D ARDUINO_USB_MODE</span><span class="token punctuation">=</span><span class="token value attr-value">1</span> <span class="token key attr-name">-D ARDUINO_USB_CDC_ON_BOOT</span><span class="token punctuation">=</span><span class="token value attr-value">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其中<code>@6.5.0</code>似乎是最关键的部分,更高版本的无法兼容,修改后成功点亮</p><p>测试代码:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><Arduino.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><TFT_eSPI.h></span> </span>TFT_eSPI tft <span class="token operator">=</span> <span class="token function">TFT_eSPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">void</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> tft<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> tft<span class="token punctuation">.</span><span class="token function">setRotation</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> tft<span class="token punctuation">.</span><span class="token function">fillScreen</span><span class="token punctuation">(</span>TFT_RED<span class="token punctuation">)</span><span class="token punctuation">;</span> tft<span class="token punctuation">.</span><span class="token function">setTextColor</span><span class="token punctuation">(</span>TFT_WHITE<span class="token punctuation">,</span> TFT_RED<span class="token punctuation">)</span><span class="token punctuation">;</span> tft<span class="token punctuation">.</span><span class="token function">drawString</span><span class="token punctuation">(</span><span class="token string">"Hello ESP32-C3 Super Mini!"</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">void</span> <span class="token function">loop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> ESP32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
<tag> Arduino </tag>
<tag> display </tag>
</tags>
</entry>
<entry>
<title>ESP32 光敏电阻 ADC 采集与 TFT 显示</title>
<link href="/2025/09/02/2025-09-05-esp32-adc-light-sensor/"/>
<url>/2025/09/02/2025-09-05-esp32-adc-light-sensor/</url>
<content type="html"><![CDATA[<h1 id="ESP32-光敏电阻-ADC-采集"><a href="#ESP32-光敏电阻-ADC-采集" class="headerlink" title="ESP32 光敏电阻 ADC 采集"></a>ESP32 光敏电阻 ADC 采集</h1><blockquote><p>在现代物联网应用中,环境光照监测是一个常见且重要的功能。本文将详细介绍如何基于 ESP32 微控制器构建一个智能光照监测系统,通过 ADC 采集光敏电阻数据,并转换为直观的 Lux 值显示在 TFT 屏幕上。</p></blockquote><h2 id="零、准备工作"><a href="#零、准备工作" class="headerlink" title="零、准备工作"></a>零、准备工作</h2><h3 id="硬件准备"><a href="#硬件准备" class="headerlink" title="硬件准备"></a>硬件准备</h3><ul><li>一块 ESP32 开发板,我使用的是 ESP32-C3 Super Mini 开发板,引脚定义如下</li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedESP32C3.png" alt="ESP32C3 引脚图"><br><em>ESP32-C3 SuperMini 引脚定义</em></p><h2 id="一、系统概述"><a href="#一、系统概述" class="headerlink" title="一、系统概述"></a>一、系统概述</h2><p>本系统基于 ESP32 平台,结合光敏电阻和分压电路,实现了环境光照强度的精确测量。系统采用 TFT 显示屏提供直观的用户界面,包含模拟表盘和数字显示两种数据呈现方式,并通过进度条直观展示光照强度变化。</p><h2 id="二、硬件设计"><a href="#二、硬件设计" class="headerlink" title="二、硬件设计"></a>二、硬件设计</h2><p>系统使用了一个简单的分压电路,其中光敏电阻作为可变电阻与固定电阻串联。当环境光照变化时,光敏电阻的阻值随之改变,从而导致分压点电压变化。ESP32 的 ADC 模块检测这一电压变化,并通过特定算法将其转换为光照强度值 (Lux)。</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%85%89%E6%95%8F%E7%94%B5%E9%98%BB%E5%8E%9F%E7%90%86.png" alt="光敏电阻原理"><br><em>光敏电阻分压电路原理图</em></p><p>硬件参数:</p><ul><li>固定电阻值:20kΩ</li><li>光敏电阻参数:</li></ul><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%85%89%E6%95%8F%E7%94%B5%E9%98%BB%E5%8F%82%E6%95%B0.png" alt="光敏电阻参数"><br><em>光敏电阻特性参数</em></p><ul><li>ADC 配置:12 位精度,12dB 衰减</li></ul><h2 id="三、软件架构解析"><a href="#三、软件架构解析" class="headerlink" title="三、软件架构解析"></a>三、软件架构解析</h2><h3 id="3-1-ADC-初始化与校准"><a href="#3-1-ADC-初始化与校准" class="headerlink" title="3.1 ADC 初始化与校准"></a>3.1 ADC 初始化与校准</h3><p>系统首先对 ESP32 的 ADC 模块进行初始化和校准,这是确保测量精度的关键步骤:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">setupADC</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">adc1_config_width</span><span class="token punctuation">(</span>ADC_WIDTH<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">adc1_config_channel_atten</span><span class="token punctuation">(</span>ADC_CHANNEL<span class="token punctuation">,</span> ADC_ATTEN<span class="token punctuation">)</span><span class="token punctuation">;</span> cali_enable <span class="token operator">=</span> <span class="token function">adc_calibration_init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>校准函数检查 ESP32 的 eFuse 中存储的校准值,如果可用则使用这些值提高 ADC 测量精度:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">static</span> <span class="token keyword">bool</span> <span class="token function">adc_calibration_init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> esp_err_t ret <span class="token operator">=</span> <span class="token function">esp_adc_cal_check_efuse</span><span class="token punctuation">(</span>ESP_ADC_CAL_VAL_EFUSE_TP<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret <span class="token operator">==</span> ESP_ERR_NOT_SUPPORTED<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret <span class="token operator">==</span> ESP_ERR_INVALID_VERSION<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret <span class="token operator">==</span> ESP_OK<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">esp_adc_cal_characterize</span><span class="token punctuation">(</span>ADC_UNIT_1<span class="token punctuation">,</span> ADC_ATTEN<span class="token punctuation">,</span> ADC_WIDTH<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span>adc1_chars<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="3-2-数据采集与处理"><a href="#3-2-数据采集与处理" class="headerlink" title="3.2 数据采集与处理"></a>3.2 数据采集与处理</h3><p>系统创建了一个独立任务负责 ADC 数据的采集和处理:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token function">xTaskCreatePinnedToCore</span><span class="token punctuation">(</span>ADC_Task<span class="token punctuation">,</span> <span class="token string">"ADC_Task"</span><span class="token punctuation">,</span> <span class="token number">4096</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在 ADC 任务中,系统采用多次采样取平均的方法提高测量稳定性:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">uint32_t</span> sum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token keyword">const</span> <span class="token keyword">int</span> samples <span class="token operator">=</span> <span class="token number">50</span><span class="token punctuation">;</span><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> samples<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> sum <span class="token operator">+=</span> <span class="token function">adc1_get_raw</span><span class="token punctuation">(</span>ADC_CHANNEL<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">delay</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span>sum <span class="token operator">/=</span> samples<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="3-3-电压转换算法"><a href="#3-3-电压转换算法" class="headerlink" title="3.3 电压转换算法"></a>3.3 电压转换算法</h3><p>根据 ADC 校准状态,系统采用不同的电压转换方法:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">float</span> voltage_v <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token keyword">if</span> <span class="token punctuation">(</span>cali_enable<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">uint32_t</span> voltage_mv <span class="token operator">=</span> <span class="token function">esp_adc_cal_raw_to_voltage</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> <span class="token operator">&</span>adc1_chars<span class="token punctuation">)</span><span class="token punctuation">;</span> voltage_v <span class="token operator">=</span> voltage_mv <span class="token operator">/</span> <span class="token number">1000.0f</span><span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> voltage_v <span class="token operator">=</span> <span class="token punctuation">(</span>sum <span class="token operator">*</span> <span class="token number">3.3</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">4095.0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="3-4-Lux-值计算原理"><a href="#3-4-Lux-值计算原理" class="headerlink" title="3.4 Lux 值计算原理"></a>3.4 Lux 值计算原理</h3><p>系统通过光电特性公式将电压值转换为光照强度值 (Lux):</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">float</span> r_photo <span class="token operator">=</span> <span class="token punctuation">(</span>voltage_v <span class="token operator">*</span> R_FIXED<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token punctuation">(</span><span class="token number">3.3f</span> <span class="token operator">-</span> voltage_v<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">float</span> lux <span class="token operator">=</span> <span class="token function">pow</span><span class="token punctuation">(</span><span class="token punctuation">(</span>r_photo <span class="token operator">/</span> R10<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">1.0f</span> <span class="token operator">/</span> <span class="token operator">-</span>GAMMA<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">10.0f</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>这一计算基于光敏电阻的非线性特性,使用 Gamma 系数进行校正,确保在不同光照条件下都能获得准确的测量结果。</p><h2 id="四、UI-设计"><a href="#四、UI-设计" class="headerlink" title="四、UI 设计"></a>四、UI 设计</h2><p>系统采用双显示区域设计,上方为模拟表盘,下方为数字信息显示区域。</p><h3 id="4-1-模拟表盘实现"><a href="#4-1-模拟表盘实现" class="headerlink" title="4.1 模拟表盘实现"></a>4.1 模拟表盘实现</h3><p>使用 TFT_eWidget 库创建模拟电压表盘:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">MeterWidget volts <span class="token operator">=</span> <span class="token function">MeterWidget</span><span class="token punctuation">(</span><span class="token operator">&</span>tft<span class="token punctuation">)</span><span class="token punctuation">;</span>volts<span class="token punctuation">.</span><span class="token function">analogMeter</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">3.3f</span><span class="token punctuation">,</span> <span class="token string">"V"</span><span class="token punctuation">,</span> <span class="token string">"0"</span><span class="token punctuation">,</span> <span class="token string">"0.8"</span><span class="token punctuation">,</span> <span class="token string">"1.6"</span><span class="token punctuation">,</span> <span class="token string">"2.4"</span><span class="token punctuation">,</span> <span class="token string">"3.3"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>实时更新表针位置反映当前电压值:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">volts<span class="token punctuation">.</span><span class="token function">updateNeedle</span><span class="token punctuation">(</span>voltage_v<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="4-2-数字信息显示"><a href="#4-2-数字信息显示" class="headerlink" title="4.2 数字信息显示"></a>4.2 数字信息显示</h3><p>采用 Sprite 技术实现无闪烁更新:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">menuSprite<span class="token punctuation">.</span><span class="token function">fillSprite</span><span class="token punctuation">(</span>TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">setTextSize</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">setTextColor</span><span class="token punctuation">(</span>TFT_WHITE<span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 显示 Lux 值</span>menuSprite<span class="token punctuation">.</span><span class="token function">setCursor</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"LUX: "</span><span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>luxStr<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 显示电压值</span>menuSprite<span class="token punctuation">.</span><span class="token function">setCursor</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">35</span><span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"VOL: "</span><span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>voltStr<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 显示 ADC 原始值</span>menuSprite<span class="token punctuation">.</span><span class="token function">setCursor</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"ADC: "</span><span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>sum<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="4-3-进度条可视化"><a href="#4-3-进度条可视化" class="headerlink" title="4.3 进度条可视化"></a>4.3 进度条可视化</h3><p>通过进度条直观展示光照强度:</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">float</span> constrainedLux <span class="token operator">=</span> <span class="token function">constrain</span><span class="token punctuation">(</span>lux<span class="token punctuation">,</span> <span class="token number">0.0f</span><span class="token punctuation">,</span> <span class="token number">1000.0f</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">int</span> barWidth <span class="token operator">=</span> <span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">long</span><span class="token punctuation">)</span>constrainedLux<span class="token punctuation">,</span> <span class="token number">0L</span><span class="token punctuation">,</span> <span class="token number">100L</span><span class="token punctuation">,</span> <span class="token number">0L</span><span class="token punctuation">,</span> <span class="token number">200L</span><span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">drawRect</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">85</span><span class="token punctuation">,</span> <span class="token number">202</span><span class="token punctuation">,</span> <span class="token number">22</span><span class="token punctuation">,</span> TFT_WHITE<span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">21</span><span class="token punctuation">,</span> <span class="token number">86</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span>menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">21</span><span class="token punctuation">,</span> <span class="token number">86</span><span class="token punctuation">,</span> barWidth<span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> TFT_GREEN<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="五、完整代码"><a href="#五、完整代码" class="headerlink" title="五、完整代码"></a>五、完整代码</h2><pre class="line-numbers language-c++" data-language="c++"><code class="language-c++">#include <Arduino.h>#include "driver/adc.h"#include "esp_adc_cal.h"#include <TFT_eSPI.h>#include <TFT_eWidget.h>// TFT 显示屏配置TFT_eSPI tft = TFT_eSPI();TFT_eSprite menuSprite = TFT_eSprite(&tft);// 模拟表盘MeterWidget volts = MeterWidget(&tft);// ADC 配置#define ADC_PIN 34#define ADC_CHANNEL ADC1_CHANNEL_6 // GPIO34 对应 ADC1_CHANNEL_6#define ADC_ATTEN ADC_ATTEN_DB_12#define ADC_WIDTH ADC_WIDTH_BIT_12// 光敏电阻参数const float R_FIXED = 20000.0f; // 固定电阻 20kΩconst float R10 = 8000.0f; // 参考电阻 8kΩconst float GAMMA = 0.6f; // Gamma 系数// ADC 校准static esp_adc_cal_characteristics_t adc1_chars;bool cali_enable = false;// 函数声明static bool adc_calibration_init();void setupADC();float readADCVoltage();float calculateLux(float voltage_v);void updateDisplay(float voltage_v, uint32_t adc_value, float lux);void setup() { Serial.begin(115200); // 初始化 TFT 显示屏 tft.init(); tft.setRotation(3); tft.fillScreen(TFT_BLACK); // 创建菜单精灵 menuSprite.createSprite(240, 100); // 初始化 ADC setupADC(); // 绘制模拟表盘 volts.analogMeter(0, 0, 3.3f, "V", "0", "0.8", "1.6", "2.4", "3.3"); Serial.println("ESP32 光敏电阻监测系统启动完成"); Serial.println("==================================");}void loop() { // 读取 ADC 电压值 float voltage_v = readADCVoltage(); // 读取原始 ADC 值(用于显示) uint32_t adc_value = adc1_get_raw(ADC_CHANNEL); // 计算 Lux 值 float lux = calculateLux(voltage_v); // 更新表盘显示 volts.updateNeedle(voltage_v, 0); // 更新数字信息显示 updateDisplay(voltage_v, adc_value, lux); // 串口输出调试信息 Serial.printf("ADC: %4d, Voltage: %.3fV, Lux: %.1f\n", adc_value, voltage_v, lux); // 延时 100ms delay(100);}// ADC 校准初始化static bool adc_calibration_init() { esp_err_t ret = esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP); if (ret == ESP_ERR_NOT_SUPPORTED) { Serial.println("ADC 校准:eFuse 不支持"); return false; } if (ret == ESP_ERR_INVALID_VERSION) { Serial.println("ADC 校准:eFuse 版本无效"); return false; } if (ret == ESP_OK) { esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN, ADC_WIDTH, 0, &adc1_chars); Serial.println("ADC 校准:使用 eFuse 校准值"); return true; } return false;}// 设置 ADCvoid setupADC() { Serial.println("初始化 ADC..."); // 配置 ADC adc1_config_width(ADC_WIDTH); adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN); // 尝试初始化校准 cali_enable = adc_calibration_init(); if (!cali_enable) { Serial.println("ADC 校准:使用软件校准"); } pinMode(ADC_PIN, INPUT); // 设置 ADC 引脚为输入模式}// 读取 ADC 电压值float readADCVoltage() { uint32_t sum = 0; const int samples = 50; // 多次采样取平均 for (int i = 0; i < samples; i++) { sum += adc1_get_raw(ADC_CHANNEL); delay(1); } sum /= samples; float voltage_v = 0; if (cali_enable) { // 使用硬件校准 uint32_t voltage_mv = esp_adc_cal_raw_to_voltage(sum, &adc1_chars); voltage_v = voltage_mv / 1000.0f; } else { // 使用软件计算 voltage_v = (sum * 3.3) / 4095.0; } return voltage_v;}// 计算 Lux 值float calculateLux(float voltage_v) { // 避免除零错误 if (voltage_v >= 3.3f) { return 0.0f; } // 计算光敏电阻阻值 float r_photo = (voltage_v * R_FIXED) / (3.3f - voltage_v); // 计算 Lux 值(使用光电特性公式) float lux = pow((r_photo / R10), (1.0f / -GAMMA)) * 10.0f; return lux;}// 更新显示void updateDisplay(float voltage_v, uint32_t adc_value, float lux) { // 清空精灵 menuSprite.fillSprite(TFT_BLACK); menuSprite.setTextSize(2); menuSprite.setTextColor(TFT_WHITE, TFT_BLACK); // 显示 Lux 值 char luxStr[10]; dtostrf(lux, 4, 1, luxStr); menuSprite.setCursor(20, 10); menuSprite.print("LUX: "); menuSprite.print(luxStr); // 显示电压值 char voltStr[10]; dtostrf(voltage_v, 4, 2, voltStr); menuSprite.setCursor(20, 35); menuSprite.print("VOL: "); menuSprite.print(voltStr); // 显示 ADC 原始值 menuSprite.setCursor(20, 60); menuSprite.print("ADC: "); menuSprite.print(adc_value); // 绘制进度条 float constrainedLux = constrain(lux, 0.0f, 1000.0f); int barWidth = map((long)constrainedLux, 0L, 1000L, 0L, 200L); menuSprite.drawRect(20, 85, 202, 22, TFT_WHITE); menuSprite.fillRect(21, 86, 200, 20, TFT_BLACK); menuSprite.fillRect(21, 86, barWidth, 20, TFT_GREEN); // 更新显示 menuSprite.pushSprite(0, 130);}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> ESP32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
<tag> Arduino </tag>
<tag> sensor </tag>
<tag> adc </tag>
</tags>
</entry>
<entry>
<title>卡尔曼滤波学习笔记</title>
<link href="/2025/08/27/2025-08-27-kalman-filter-notes/"/>
<url>/2025/08/27/2025-08-27-kalman-filter-notes/</url>
<content type="html"><![CDATA[<h2 id="入门"><a href="#入门" class="headerlink" title="入门"></a>入门</h2><h3 id="引入"><a href="#引入" class="headerlink" title="引入"></a>引入</h3><p><strong>滤波</strong>:去掉噪声,使信号更趋于真实值</p><p><strong>卡尔曼滤波</strong>是有多个不确定的结果,经过分析、推理和计算,获得相对准确的结果。</p><ul><li><strong>多个</strong>是指数据来源可以是模型推理得出,也可以是通过仪器测量获得。</li><li><strong>不确定</strong>是指由于模型本身是一种近似,或者是测量仪器本身的精度误差,或者测量过程不可避免地引入了噪声,甚至因为所需要的特征无法直接获取,只能间接推导获得。</li><li><strong>分析、推理和计算</strong>,则指的是卡尔曼滤波算法。</li><li><strong>相对准确</strong>,指的是经过卡尔曼滤波算法获得的结果,比原有的多个不确定的结果更逼近客观真实值,但依然存在误差。</li></ul><h3 id="适用系统"><a href="#适用系统" class="headerlink" title="适用系统"></a>适用系统</h3><p><strong>线性高斯系统</strong></p><ul><li><p><strong>线性</strong></p><ul><li>叠加性 —— $f(a+b) = f(a)+f(b)$</li><li>齐次性 —— $f(kx) = kf(x)$</li></ul></li><li><p><strong>高斯</strong>——噪声满足正态分布</p></li></ul><h3 id="宏观意义"><a href="#宏观意义" class="headerlink" title="宏观意义"></a>宏观意义</h3><p><strong>滤波即加权</strong></p><p>理想状态:信号 x1 + 噪声 x0</p><p>卡尔曼滤波:估计值 xX + 观测值 xY</p><p>因为估计值和观测值都有一定的误差,所以二者加权能够相对准确,权重取决于二者的方差,方差大的说明不可靠,权重应该给的小一点,反之亦然。</p><h2 id="进阶"><a href="#进阶" class="headerlink" title="进阶"></a>进阶</h2><h3 id="状态空间表达式"><a href="#状态空间表达式" class="headerlink" title="状态空间表达式"></a>状态空间表达式</h3><p><strong>基本假设</strong>:</p><ol><li>当前时刻状态只和<strong>上一时刻</strong>状态有关。</li><li>模型和系统均满足<strong>线性</strong>关系。</li><li>引入的噪声符合<strong>高斯</strong>分布。</li></ol><p><strong>状态方程</strong></p><p>$$<br>x_k = A x_{k-1} + B u_{k-1} + w_k<br>$$</p><ul><li>$x_k$:k 时刻的真实值,待估计的</li><li>$x_{k-1}$:k-1 时刻的真实值</li><li>$u_{k-1}$:k-1 时刻的控制输入量</li><li>$w_k$:表示过程噪声,且符合均值为 0,协方差矩阵为 Q 的高斯噪声分布</li></ul><p><strong>观测方程</strong></p><p>$$<br>y_k = C x_k + v_k<br>$$</p><ul><li>$y_k$:k 时刻的<strong>观测值</strong></li><li>$v_k$:测量噪声,且符合均值为 0,协方差矩阵为 R 的高斯噪声分布</li></ul><p>例如,煮水,$x_k$ 是水的温度,$y_k$ 是温度计显示的水温,则 $y_k = x_k + v_k$</p><p>$$<br>x_k = x_{k-1} + \Delta T + w_k<br>$$</p><h3 id="参数分析"><a href="#参数分析" class="headerlink" title="参数分析"></a>参数分析</h3><h4 id="噪声"><a href="#噪声" class="headerlink" title="噪声"></a>噪声</h4><h4 id="方差"><a href="#方差" class="headerlink" title="方差"></a>方差</h4><p>多维方差:协方差矩阵</p><blockquote><p>[!NOTE]</p><p>本文档为学习笔记摘要,建议参考完整的卡尔曼滤波教材获取更详细的内容。</p></blockquote>]]></content>
<categories>
<category> Learning </category>
<category> Algorithm </category>
</categories>
<tags>
<tag> kalman-filter </tag>
<tag> algorithm </tag>
</tags>
</entry>
<entry>
<title>PID 控制算法学习笔记</title>
<link href="/2025/08/27/2025-08-27-pid-control-notes/"/>
<url>/2025/08/27/2025-08-27-pid-control-notes/</url>
<content type="html"><![CDATA[<h2 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h2><h3 id="引入"><a href="#引入" class="headerlink" title="引入"></a>引入</h3><p>保持水龙头流量稳定,还能改变流量</p><h3 id="适用系统"><a href="#适用系统" class="headerlink" title="适用系统"></a>适用系统</h3><p>PID 控制是一种经典的反馈控制算法,广泛应用于工业控制系统中。</p><blockquote><p>[!NOTE]</p><p>本文档内容较少,建议参考其他 PID 控制相关资料获取更详细的内容。</p></blockquote><h2 id="PID-控制器组成"><a href="#PID-控制器组成" class="headerlink" title="PID 控制器组成"></a>PID 控制器组成</h2><p>PID 控制器由三个部分组成:</p><ul><li><strong>P(比例)</strong>:与当前误差成正比</li><li><strong>I(积分)</strong>:与误差的积分(累积)成正比</li><li><strong>D(微分)</strong>:与误差的变化率成正比</li></ul><p>公式:</p><p>$$<br>u(t) = K_p e(t) + K_i \int_0^t e(\tau)d\tau + K_d \frac{de(t)}{dt}<br>$$</p><p>其中:</p><ul><li>$e(t)$ 为误差信号</li><li>$K_p$、$K_i$、$K_d$ 分别为比例、积分、微分系数</li></ul>]]></content>
<categories>
<category> Learning </category>
<category> Algorithm </category>
</categories>
<tags>
<tag> pid </tag>
<tag> control-theory </tag>
</tags>
</entry>
<entry>
<title>多功能时钟菜单系统设计</title>
<link href="/2025/08/16/2025-08-16-menu-system-design/"/>
<url>/2025/08/16/2025-08-16-menu-system-design/</url>
<content type="html"><![CDATA[<h2 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h2><p>菜单系统是多功能时钟的核心交互界面,它为用户提供了一个统一的入口来访问设备的所有功能,如时钟、倒计时、天气等。本模块的目标是实现一个响应快速、滚动平滑、易于扩展的横向菜单。用户通过旋转编码器在不同功能间切换,并通过旋转编码器的按键确认选择。</p><h2 id="二、实现思路"><a href="#二、实现思路" class="headerlink" title="二、实现思路"></a>二、实现思路</h2><h3 id="2-1-表驱动设计"><a href="#2-1-表驱动设计" class="headerlink" title="2.1 表驱动设计"></a>2.1 表驱动设计</h3><p>为了达成设计目标,我们首先解决了菜单项的数据结构问题。需求是需要一种统一的方式来管理每个菜单项的名称、图标和对应的执行逻辑,对此我们采用了 <code>struct</code> 数组结合函数指针的方案。这种”表驱动”的方法非常适合嵌入式项目,它将数据和行为绑定,同时保持了高度的模块化,可维护性很高。</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">MenuItem</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>name<span class="token punctuation">;</span> <span class="token comment">// 菜单项名称</span> <span class="token keyword">const</span> <span class="token keyword">uint16_t</span> <span class="token operator">*</span>image<span class="token punctuation">;</span> <span class="token comment">// 指向图标图像数据的指针</span> <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>action<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 指向功能入口函数的指针</span><span class="token punctuation">}</span><span class="token punctuation">;</span><span class="token keyword">const</span> MenuItem menuItems<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">{</span><span class="token string">"Clock"</span><span class="token punctuation">,</span> Weather<span class="token punctuation">,</span> <span class="token operator">&</span>weatherMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Countdown"</span><span class="token punctuation">,</span> Timer<span class="token punctuation">,</span> <span class="token operator">&</span>CountdownMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Alarm"</span><span class="token punctuation">,</span> alarm_img<span class="token punctuation">,</span> <span class="token operator">&</span>AlarmMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Pomodoro"</span><span class="token punctuation">,</span> tomato<span class="token punctuation">,</span> <span class="token operator">&</span>PomodoroMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Stopwatch"</span><span class="token punctuation">,</span> Timer<span class="token punctuation">,</span> <span class="token operator">&</span>StopwatchMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Music"</span><span class="token punctuation">,</span> Music<span class="token punctuation">,</span> <span class="token operator">&</span>BuzzerMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Music Lite"</span><span class="token punctuation">,</span> music_lite<span class="token punctuation">,</span> <span class="token operator">&</span>MusicMenuLite<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Performance"</span><span class="token punctuation">,</span> Performance<span class="token punctuation">,</span> <span class="token operator">&</span>performanceMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Temperature"</span><span class="token punctuation">,</span>Temperature<span class="token punctuation">,</span> <span class="token operator">&</span>DS18B20Menu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Animation"</span><span class="token punctuation">,</span>LED<span class="token punctuation">,</span> <span class="token operator">&</span>AnimationMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Games"</span><span class="token punctuation">,</span> Games<span class="token punctuation">,</span> <span class="token operator">&</span>GamesMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"LED"</span><span class="token punctuation">,</span> LED<span class="token punctuation">,</span> <span class="token operator">&</span>LEDMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"ADC"</span><span class="token punctuation">,</span> ADC<span class="token punctuation">,</span> <span class="token operator">&</span>ADCMenu<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="2-2-双缓冲渲染"><a href="#2-2-双缓冲渲染" class="headerlink" title="2.2 双缓冲渲染"></a>2.2 双缓冲渲染</h3><p>为了在单片机有限的刷新率下保证画面无闪烁,我们使用了 <code>TFT_eSPI</code> 库提供的精灵(Sprite)功能。它本质上是一个内存中的画布,所有绘制操作都先在内存中完成,然后一次性将最终画面传送到物理屏幕,这种双缓冲机制能完全避免视觉闪烁问题。</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">drawMenuIcons</span><span class="token punctuation">(</span><span class="token keyword">int16_t</span> offset<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 清除图标和三角形绘制的区域</span> menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> ICON_Y_POS<span class="token punctuation">,</span> SCREEN_WIDTH<span class="token punctuation">,</span> SCREEN_HEIGHT <span class="token operator">-</span> ICON_Y_POS<span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> SCREEN_WIDTH<span class="token punctuation">,</span> <span class="token number">40</span><span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 绘制三角形指示器</span> <span class="token keyword">int16_t</span> triangle_x <span class="token operator">=</span> offset <span class="token operator">+</span> <span class="token punctuation">(</span>picture_flag <span class="token operator">*</span> ICON_SPACING<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span>ICON_SIZE <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">fillTriangle</span><span class="token punctuation">(</span>triangle_x<span class="token punctuation">,</span> SCREEN_HEIGHT <span class="token operator">-</span> <span class="token number">25</span><span class="token punctuation">,</span> triangle_x <span class="token operator">-</span> <span class="token number">12</span><span class="token punctuation">,</span> SCREEN_HEIGHT <span class="token operator">-</span> <span class="token number">5</span><span class="token punctuation">,</span> triangle_x <span class="token operator">+</span> <span class="token number">12</span><span class="token punctuation">,</span> SCREEN_HEIGHT <span class="token operator">-</span> <span class="token number">5</span><span class="token punctuation">,</span> TFT_WHITE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 绘制图标(带视口裁剪)</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> MENU_ITEM_COUNT<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int16_t</span> x <span class="token operator">=</span> offset <span class="token operator">+</span> <span class="token punctuation">(</span>i <span class="token operator">*</span> ICON_SPACING<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>x <span class="token operator">>=</span> <span class="token operator">-</span>ICON_SIZE <span class="token operator">&&</span> x <span class="token operator"><</span> SCREEN_WIDTH<span class="token punctuation">)</span> <span class="token punctuation">{</span> menuSprite<span class="token punctuation">.</span><span class="token function">pushImage</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> ICON_Y_POS<span class="token punctuation">,</span> ICON_SIZE<span class="token punctuation">,</span> ICON_SIZE<span class="token punctuation">,</span> menuItems<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>image<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 绘制文字</span> menuSprite<span class="token punctuation">.</span><span class="token function">setTextColor</span><span class="token punctuation">(</span>TFT_WHITE<span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">setTextSize</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">setTextDatum</span><span class="token punctuation">(</span>TC_DATUM<span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">drawString</span><span class="token punctuation">(</span>menuItems<span class="token punctuation">[</span>picture_flag<span class="token punctuation">]</span><span class="token punctuation">.</span>name<span class="token punctuation">,</span> SCREEN_WIDTH <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">pushSprite</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 推送到屏幕</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="2-3-缓动动画"><a href="#2-3-缓动动画" class="headerlink" title="2.3 缓动动画"></a>2.3 缓动动画</h3><p>为了实现平滑的滚动动画,我们应用了缓动函数(Easing Function),具体为 <code>easeOutBack</code>。相比于速度恒定的线性动画,缓动函数通过非线性的方式计算动画进程,可以模拟出带有弹性的物理效果,使交互体验更佳。</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">float</span> <span class="token function">easeOutBack</span><span class="token punctuation">(</span><span class="token keyword">float</span> t<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">float</span> c1 <span class="token operator">=</span> <span class="token number">1.70158f</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token keyword">float</span> c3 <span class="token operator">=</span> c1 <span class="token operator">+</span> <span class="token number">1.0f</span><span class="token punctuation">;</span> <span class="token keyword">float</span> t_minus_1 <span class="token operator">=</span> t <span class="token operator">-</span> <span class="token number">1.0f</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">1.0f</span> <span class="token operator">+</span> c3 <span class="token operator">*</span> t_minus_1 <span class="token operator">*</span> t_minus_1 <span class="token operator">*</span> t_minus_1 <span class="token operator">+</span> c1 <span class="token operator">*</span> t_minus_1 <span class="token operator">*</span> t_minus_1<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">// 动画循环</span><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> ANIMATION_STEPS<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">float</span> t <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span>i <span class="token operator">/</span> ANIMATION_STEPS<span class="token punctuation">;</span> <span class="token keyword">float</span> eased_t <span class="token operator">=</span> <span class="token function">easeOutBack</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">;</span> display <span class="token operator">=</span> start_display <span class="token operator">+</span> <span class="token punctuation">(</span>target_display <span class="token operator">-</span> start_display<span class="token punctuation">)</span> <span class="token operator">*</span> eased_t<span class="token punctuation">;</span> <span class="token function">drawMenuIcons</span><span class="token punctuation">(</span>display<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">vTaskDelay</span><span class="token punctuation">(</span><span class="token function">pdMS_TO_TICKS</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="三、核心代码"><a href="#三、核心代码" class="headerlink" title="三、核心代码"></a>三、核心代码</h2><h3 id="菜单项定义"><a href="#菜单项定义" class="headerlink" title="菜单项定义"></a>菜单项定义</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">MenuItem</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>name<span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token keyword">uint16_t</span> <span class="token operator">*</span>image<span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>action<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">;</span><span class="token keyword">const</span> MenuItem menuItems<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">{</span><span class="token string">"Clock"</span><span class="token punctuation">,</span> Weather<span class="token punctuation">,</span> <span class="token operator">&</span>weatherMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">"Countdown"</span><span class="token punctuation">,</span> Countdown<span class="token punctuation">,</span> <span class="token operator">&</span>CountdownMenu<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">// ... 更多菜单项</span><span class="token punctuation">}</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="UI-渲染与视口裁剪"><a href="#UI-渲染与视口裁剪" class="headerlink" title="UI 渲染与视口裁剪"></a>UI 渲染与视口裁剪</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">drawMenuIcons</span><span class="token punctuation">(</span><span class="token keyword">int16_t</span> offset<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 清理 Sprite 画布</span> menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> ICON_Y_POS<span class="token punctuation">,</span> SCREEN_WIDTH<span class="token punctuation">,</span> SCREEN_HEIGHT <span class="token operator">-</span> ICON_Y_POS<span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span> menuSprite<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> SCREEN_WIDTH<span class="token punctuation">,</span> <span class="token number">40</span><span class="token punctuation">,</span> TFT_BLACK<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 遍历所有菜单项</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> MENU_ITEM_COUNT<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int16_t</span> x <span class="token operator">=</span> offset <span class="token operator">+</span> <span class="token punctuation">(</span>i <span class="token operator">*</span> ICON_SPACING<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 视口裁剪:只绘制在屏幕可视范围内的图标</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>x <span class="token operator">>=</span> <span class="token operator">-</span>ICON_SIZE <span class="token operator">&&</span> x <span class="token operator"><</span> SCREEN_WIDTH<span class="token punctuation">)</span> <span class="token punctuation">{</span> menuSprite<span class="token punctuation">.</span><span class="token function">pushImage</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> ICON_Y_POS<span class="token punctuation">,</span> ICON_SIZE<span class="token punctuation">,</span> ICON_SIZE<span class="token punctuation">,</span> menuItems<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>image<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 绘制标题和指示器</span> menuSprite<span class="token punctuation">.</span><span class="token function">pushSprite</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="动画循环与插值"><a href="#动画循环与插值" class="headerlink" title="动画循环与插值"></a>动画循环与插值</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">if</span> <span class="token punctuation">(</span>direction <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> current_state <span class="token operator">=</span> ANIMATING<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>direction <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> picture_flag <span class="token operator">=</span> <span class="token punctuation">(</span>picture_flag <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">%</span> MENU_ITEM_COUNT<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> picture_flag <span class="token operator">=</span> <span class="token punctuation">(</span>picture_flag <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">?</span> MENU_ITEM_COUNT <span class="token operator">-</span> <span class="token number">1</span> <span class="token operator">:</span> picture_flag <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">int16_t</span> start_display <span class="token operator">=</span> display<span class="token punctuation">;</span> <span class="token keyword">int16_t</span> target_display <span class="token operator">=</span> INITIAL_X_OFFSET <span class="token operator">-</span> <span class="token punctuation">(</span>picture_flag <span class="token operator">*</span> ICON_SPACING<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">uint8_t</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> ANIMATION_STEPS<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">float</span> t <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">float</span><span class="token punctuation">)</span>i <span class="token operator">/</span> ANIMATION_STEPS<span class="token punctuation">;</span> <span class="token keyword">float</span> eased_t <span class="token operator">=</span> <span class="token function">easeOutBack</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">;</span> display <span class="token operator">=</span> start_display <span class="token operator">+</span> <span class="token punctuation">(</span>target_display <span class="token operator">-</span> start_display<span class="token punctuation">)</span> <span class="token operator">*</span> eased_t<span class="token punctuation">;</span> <span class="token function">drawMenuIcons</span><span class="token punctuation">(</span>display<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">vTaskDelay</span><span class="token punctuation">(</span><span class="token function">pdMS_TO_TICKS</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> display <span class="token operator">=</span> target_display<span class="token punctuation">;</span> <span class="token function">drawMenuIcons</span><span class="token punctuation">(</span>display<span class="token punctuation">)</span><span class="token punctuation">;</span> current_state <span class="token operator">=</span> MAIN_MENU<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="四、总结与展望"><a href="#四、总结与展望" class="headerlink" title="四、总结与展望"></a>四、总结与展望</h2><p>该菜单系统通过表驱动设计、双缓冲渲染和缓动动画三大核心技术,成功构建了一个用户体验良好且易于维护的 UI 框架。</p><p><strong>未来的可扩展方向:</strong></p><ol><li>为设置等复杂模块构建层级更深的二级菜单</li><li>根据系统状态(如是否联网)动态显示或隐藏某些菜单项</li><li>将颜色、字体等 UI 元素配置化,以支持一键换肤</li></ol>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> ESP32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> ESP32 </tag>
<tag> animation </tag>
<tag> Arduino </tag>
</tags>
</entry>
<entry>
<title>正点原子 STM32 学习笔记</title>
<link href="/2025/07/21/2025-07-21-stm32-notes/"/>
<url>/2025/07/21/2025-07-21-stm32-notes/</url>
<content type="html"><![CDATA[<h2 id="STM32-基础"><a href="#STM32-基础" class="headerlink" title="STM32 基础"></a>STM32 基础</h2><p>STM32 是 ST 公司推出的基于 ARM Cortex-M 内核的 32 位微控制器系列。</p><h2 id="引脚定义"><a href="#引脚定义" class="headerlink" title="引脚定义"></a>引脚定义</h2><h3 id="供电脚"><a href="#供电脚" class="headerlink" title="供电脚"></a>供电脚</h3><ul><li>VDDA:模拟电源</li></ul><h3 id="IO-口"><a href="#IO-口" class="headerlink" title="IO 口"></a>IO 口</h3><p>灰色的为通用 IO 口</p><h3 id="状态控制脚"><a href="#状态控制脚" class="headerlink" title="状态控制脚"></a>状态控制脚</h3><ul><li>NRST:复位引脚,低电平重启</li><li>BOOT0:启动模式选择</li></ul><h4 id="BOOT-引脚配置"><a href="#BOOT-引脚配置" class="headerlink" title="BOOT 引脚配置"></a>BOOT 引脚配置</h4><ol><li>BOOT1=x BOOT0=0:从用户闪存启动,这是正常的工作模式</li><li>BOOT1=0 BOOT0=1:从系统存储器启动,可进行初始化</li><li>BOOT1=1 BOOT0=1:从内置 SRAM 启动,这种模式可以用于调试</li></ol><h2 id="SWD-调试接口"><a href="#SWD-调试接口" class="headerlink" title="SWD 调试接口"></a>SWD 调试接口</h2><ul><li>SWDIO(双向数据线)</li><li>SWCLK(时钟线)</li></ul><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><p>不小心把 PA13、PA14 设成普通 GPIO 怎么办?使用 BOOT 引脚进入系统存储器模式进行初始化。</p><blockquote><p>[!NOTE]</p><p>本文档为学习笔记摘要,建议参考 STM32 官方参考手册获取更详细的内容。</p></blockquote>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> STM32 </category>
</categories>
<tags>
<tag> embedded </tag>
<tag> STM32 </tag>
</tags>
</entry>
<entry>
<title>JavaScript 基础学习笔记</title>
<link href="/2025/07/16/2025-07-16-javascript-basic-notes/"/>
<url>/2025/07/16/2025-07-16-javascript-basic-notes/</url>
<content type="html"><![CDATA[<p>JavaScript 用于做交互式网页,用 JavaScript 引擎运行。</p><p>把 <code><script></code> 标签放在 <code><body></code> 标签的末尾,在 <code><body></code> 中所有其他标签之后,因为浏览器解析是自顶向下的。</p><h2 id="基本示例"><a href="#基本示例" class="headerlink" title="基本示例"></a>基本示例</h2><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Document<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'hello world'</span><span class="token punctuation">)</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>分成不同文件:</p><p><strong>index.js</strong></p><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'hello world'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>index.html</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Document<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>index.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><ol><li><code>var</code>:全局作用域,一般不用</li><li><code>let</code>:值可以被修改</li><li><code>const</code>:值无法被修改</li></ol><h2 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h2><h3 id="显示数据"><a href="#显示数据" class="headerlink" title="显示数据"></a>显示数据</h3><ul><li>使用 <code>window.alert()</code> 弹出警告框。</li><li>使用 <code>document.write()</code> 方法将内容写到 HTML 文档中。</li><li>使用 <code>innerHTML</code> 写入到 HTML 元素。</li><li>使用 <code>console.log()</code> 写入到浏览器的控制台</li></ul><h4 id="使用-window-alert"><a href="#使用-window-alert" class="headerlink" title="使用 window.alert()"></a>使用 window.alert()</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript">window<span class="token punctuation">.</span><span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"Hello World!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="操作-HTML-元素"><a href="#操作-HTML-元素" class="headerlink" title="操作 HTML 元素"></a>操作 HTML 元素</h4><p>如需从 JavaScript 访问某个 HTML 元素,您可以使用 <code>document.getElementById(id)</code> 方法。</p><p>请使用 “id” 属性来标识 HTML 元素,并 <code>innerHTML</code> 来获取或插入元素内容:</p><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token operator"><</span><span class="token operator">!</span><span class="token constant">DOCTYPE</span> html<span class="token operator">></span><span class="token operator"><</span>html<span class="token operator">></span><span class="token operator"><</span>body<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>我的第一个 Web 页面<span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>p id<span class="token operator">=</span><span class="token string">"demo"</span><span class="token operator">></span>我的第一个段落<span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"demo"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">"段落已修改。"</span><span class="token punctuation">;</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>body<span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>html<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Language </category>
<category> JavaScript </category>
</categories>
<tags>
<tag> javascript </tag>
<tag> web </tag>
<tag> frontend </tag>
</tags>
</entry>
<entry>
<title>HTML 与 CSS 基础入门教程</title>
<link href="/2025/07/16/2025-07-16-html-css-basic-tutorial/"/>
<url>/2025/07/16/2025-07-16-html-css-basic-tutorial/</url>
<content type="html"><![CDATA[<h1 id="HTML-与-CSS-基础入门教程"><a href="#HTML-与-CSS-基础入门教程" class="headerlink" title="HTML 与 CSS 基础入门教程"></a>HTML 与 CSS 基础入门教程</h1><h2 id="一、HTML-与-CSS-简介"><a href="#一、HTML-与-CSS-简介" class="headerlink" title="一、HTML 与 CSS 简介"></a>一、HTML 与 CSS 简介</h2><p>HTML(HyperText Markup Language)是标记语言,决定了网页有哪些内容;CSS(Cascading Style Sheets)负责美化,决定了图片样式、文字排版等视觉效果。</p><p>网站首页一般为 <code>index.html</code>。</p><h2 id="二、HTML-基本结构"><a href="#二、HTML-基本结构" class="headerlink" title="二、HTML 基本结构"></a>二、HTML 基本结构</h2><p>HTML 使用成对出现的标签来决定内容,<code><html></code> 和 <code></html></code> 决定了整个网站的开始与结束。</p><h3 id="2-1-基本文档结构"><a href="#2-1-基本文档结构" class="headerlink" title="2.1 基本文档结构"></a>2.1 基本文档结构</h3><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Document<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li><code><head></code>:不会显示在网页中,主要用于设置文件的字符编码、关键字、程式脚本等元数据</li><li><code><body></code>:用户看到的内容都放在这里</li></ul><blockquote><p>[!TIP]<br>在编辑器中输入 <code>!</code> 然后按 <code>Tab</code> 键,可以自动生成基本 HTML 结构</p></blockquote><h2 id="三、常用-HTML-标签"><a href="#三、常用-HTML-标签" class="headerlink" title="三、常用 HTML 标签"></a>三、常用 HTML 标签</h2><h3 id="3-1-标题(Heading)"><a href="#3-1-标题(Heading)" class="headerlink" title="3.1 标题(Heading)"></a>3.1 标题(Heading)</h3><p><code><h1></code> 到 <code><h6></code> 共六级标题:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hello, this is main title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>this is 2nd heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>this is 3rd heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h4</span><span class="token punctuation">></span></span>this is 4th heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h4</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h5</span><span class="token punctuation">></span></span>this is 5th heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h5</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h6</span><span class="token punctuation">></span></span>this is 6th heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h6</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="3-2-段落"><a href="#3-2-段落" class="headerlink" title="3.2 段落"></a>3.2 段落</h3><p><code><p></code>:一般内容,每一个代表独立段落</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>这是一个段落<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>这是另一个段落<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><blockquote><p>[!NOTE]<br><code><h1></code> 到 <code><h6></code> 和 <code><p></code> 不管内容和字数有多少,都会占一整行,称为<strong>区块元素</strong></p></blockquote><h3 id="3-3-行内元素"><a href="#3-3-行内元素" class="headerlink" title="3.3 行内元素"></a>3.3 行内元素</h3><p><strong>粗体</strong>:<code><strong></code></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>strong</span><span class="token punctuation">></span></span>who are you<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>strong</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>斜体</strong>:<code><em></code></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>em</span><span class="token punctuation">></span></span>are you who<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>em</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="3-4-链接"><a href="#3-4-链接" class="headerlink" title="3.4 链接"></a>3.4 链接</h3><p><code><a></code>:建立网页的超链接</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>查看更多<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果还没有外部链接,可以在 <code>href</code> 后面写 <code>#</code>:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>临时链接<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="3-5-列表"><a href="#3-5-列表" class="headerlink" title="3.5 列表"></a>3.5 列表</h3><p><strong>有序列表</strong>(Ordered List):</p><p>以 <code><ol></code> 开头,清单内容使用 <code><li></code> 标签,每组 <code><li></code> 都会显示为清单中的一个项目</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ol</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>项目 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>项目 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>项目 3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ol</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>显示效果:</p><ol><li>项目 1</li><li>项目 2</li><li>项目 3</li></ol><p><strong>无序列表</strong>(Unordered List):</p><p>使用 <code><ul></code> 可以制作没有数字的列表</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>项目 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>项目 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>项目 3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>显示效果:</p><ul><li>项目 1</li><li>项目 2</li><li>项目 3</li></ul><blockquote><p>[!TIP]<br>列表主要用于导览列、主选单等场景</p></blockquote><h3 id="3-6-引用"><a href="#3-6-引用" class="headerlink" title="3.6 引用"></a>3.6 引用</h3><p><code><blockquote></code> 可以引用别人的话:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>blockquote</span> <span class="token attr-name">cite</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>www.baidu.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>"上等的食材。。。"<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>blockquote</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="3-7-换行"><a href="#3-7-换行" class="headerlink" title="3.7 换行"></a>3.7 换行</h3><p><code><br></code> 可以换行:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup">hello hello <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span>你好<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span>我是<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="3-8-图片"><a href="#3-8-图片" class="headerlink" title="3.8 图片"></a>3.8 图片</h3><p><code><img></code> 可以插入图片:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>图片未正常显示时显示的内容<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="3-9-语义化标签"><a href="#3-9-语义化标签" class="headerlink" title="3.9 语义化标签"></a>3.9 语义化标签</h3><table><thead><tr><th>标签</th><th>用途</th></tr></thead><tbody><tr><td><code><header></code></td><td>logo 等页首信息</td></tr><tr><td><code><nav></code></td><td>网站导览列、主选单</td></tr><tr><td><code><main></code></td><td>主题内容区域</td></tr><tr><td><code><footer></code></td><td>页尾、版权信息</td></tr><tr><td><code><section></code></td><td>内容分块</td></tr><tr><td><code><div></code></td><td>通用容器,用于划分模块</td></tr></tbody></table><h3 id="3-10-表单"><a href="#3-10-表单" class="headerlink" title="3.10 表单"></a>3.10 表单</h3><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>输入您的 Email<span class="token punctuation">"</span></span> <span class="token attr-name">required</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>提交<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="四、CSS-样式设置"><a href="#四、CSS-样式设置" class="headerlink" title="四、CSS 样式设置"></a>四、CSS 样式设置</h2><h3 id="4-1-内联样式(不推荐)"><a href="#4-1-内联样式(不推荐)" class="headerlink" title="4.1 内联样式(不推荐)"></a>4.1 内联样式(不推荐)</h3><p>直接写在 HTML 标签里:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">color</span><span class="token punctuation">:</span>red</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>你好<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>[!WARNING]<br>缺点:多个标签使用时耗时费力,不利于维护</p></blockquote><h3 id="4-2-内部样式表"><a href="#4-2-内部样式表" class="headerlink" title="4.2 内部样式表"></a>4.2 内部样式表</h3><p>统一写到 <code><head></code> 标签内:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Document<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"> <span class="token selector">h2</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> aqua<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> x-large<span class="token punctuation">;</span> <span class="token punctuation">}</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>[!WARNING]<br>缺点:对多个网页不方便,样式无法复用</p></blockquote><h3 id="4-3-外部样式表(推荐)"><a href="#4-3-外部样式表(推荐)" class="headerlink" title="4.3 外部样式表(推荐)"></a>4.3 外部样式表(推荐)</h3><p>把 CSS 单独分离出来成为样式表文件:</p><p><strong>style.css</strong></p><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token selector">h2</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> aqua<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> x-large<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 HTML 中链接:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Document<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>style.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="五、常用-CSS-属性"><a href="#五、常用-CSS-属性" class="headerlink" title="五、常用 CSS 属性"></a>五、常用 CSS 属性</h2><h3 id="5-1-边框(border)"><a href="#5-1-边框(border)" class="headerlink" title="5.1 边框(border)"></a>5.1 边框(border)</h3><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token property">border</span><span class="token punctuation">:</span> 边框粗细 颜色 样式<span class="token punctuation">;</span><span class="token selector">header, section, footer</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> aqua<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> 5px blue solid<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>边框样式:</p><ul><li><code>solid</code>:实线</li><li><code>dashed</code>:虚线</li><li><code>dotted</code>:点线</li></ul><h3 id="5-2-内边距(padding)"><a href="#5-2-内边距(padding)" class="headerlink" title="5.2 内边距(padding)"></a>5.2 内边距(padding)</h3><p>控制内容与边框之间的空隙大小,按<strong>上、右、下、左</strong>的顺序设置:</p><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token property">padding</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span> <span class="token comment">/* 四周都是 10px */</span><span class="token property">padding</span><span class="token punctuation">:</span> 10px 20px<span class="token punctuation">;</span> <span class="token comment">/* 上下 10px,左右 20px */</span><span class="token property">padding</span><span class="token punctuation">:</span> 10px 20px 30px<span class="token punctuation">;</span> <span class="token comment">/* 上 10px,左右 20px,下 30px */</span><span class="token property">padding</span><span class="token punctuation">:</span> 10px 20px 30px 40px<span class="token punctuation">;</span> <span class="token comment">/* 上 10px,右 20px,下 30px,左 40px */</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h3 id="5-3-外边距(margin)"><a href="#5-3-外边距(margin)" class="headerlink" title="5.3 外边距(margin)"></a>5.3 外边距(margin)</h3><p>控制不同网页元素之间的距离,用法同 <code>padding</code></p><h3 id="5-4-分组与类(class)"><a href="#5-4-分组与类(class)" class="headerlink" title="5.4 分组与类(class)"></a>5.4 分组与类(class)</h3><p>在标签内加上 <code>class</code> 属性:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>news<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token selector">.news</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 95vh<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="六、Flexbox-布局"><a href="#六、Flexbox-布局" class="headerlink" title="六、Flexbox 布局"></a>六、Flexbox 布局</h2><p>Flexbox 是一种强大的布局方式:</p><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token selector">.news</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 95vh<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token comment">/* 水平居中 */</span> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token comment">/* 垂直居中 */</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="七、模块划分"><a href="#七、模块划分" class="headerlink" title="七、模块划分"></a>七、模块划分</h2><p>使用 <code><div></code> 标签划分 HTML 模块,减少模块个数:</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>info<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>dwsdskasdasl<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="八、固定定位"><a href="#八、固定定位" class="headerlink" title="八、固定定位"></a>八、固定定位</h2><p>让导览列等元素固定不动:</p><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token selector">nav</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="九、响应式设计"><a href="#九、响应式设计" class="headerlink" title="九、响应式设计"></a>九、响应式设计</h2><p>使用媒体查询(Media Query)实现响应式布局:</p><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 480px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> <span class="token selector">body</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> lightgreen<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>通用语法:</p><pre class="line-numbers language-css" data-language="css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token keyword">not</span>|<span class="token keyword">only</span> mediatype <span class="token keyword">and</span> <span class="token punctuation">(</span>expressions<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> CSS 代码...<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="十、总结"><a href="#十、总结" class="headerlink" title="十、总结"></a>十、总结</h2><table><thead><tr><th>概念</th><th>说明</th></tr></thead><tbody><tr><td>HTML</td><td>决定网页内容和结构</td></tr><tr><td>CSS</td><td>负责样式和布局</td></tr><tr><td>区块元素</td><td>占一整行,如 <code><h1></code>、<code><p></code></td></tr><tr><td>行内元素</td><td>只占所需宽度,如 <code><strong></code>、<code><em></code></td></tr><tr><td>外部样式表</td><td>推荐的 CSS 编写方式</td></tr><tr><td>Flexbox</td><td>现代布局方案</td></tr><tr><td>响应式设计</td><td>适配不同屏幕尺寸</td></tr></tbody></table>]]></content>
<categories>
<category> Learning </category>
<category> Language </category>
</categories>
<tags>
<tag> web </tag>
<tag> frontend </tag>
<tag> html </tag>
<tag> css </tag>
</tags>
</entry>
<entry>
<title>Docker 容器技术学习笔记</title>
<link href="/2025/07/15/2025-07-15-docker-container-notes/"/>
<url>/2025/07/15/2025-07-15-docker-container-notes/</url>
<content type="html"><![CDATA[<h2 id="什么是-Docker"><a href="#什么是-Docker" class="headerlink" title="什么是 Docker"></a>什么是 Docker</h2><p><strong>Docker</strong> 使用 Google 公司推出的 <strong>Go 语言</strong> 进行开发实现,属于操作系统层面的虚拟化技术。</p><p><strong>Docker</strong> 通过将源代码、环境、第三方依赖等打包成一个<strong>镜像</strong>,就可以在任何地方依据镜像生成并运行<strong>容器</strong>。这样可以有效避免仅提供源码但环境不同、配置不同及第三方依赖的版本不同导致的兼容问题。轻松实现跨平台、跨服务器的运行,简化了从开发、调试到生成的迁移问题。</p><h3 id="对比传统虚拟机"><a href="#对比传统虚拟机" class="headerlink" title="对比传统虚拟机"></a>对比传统虚拟机</h3><table><thead><tr><th>特性</th><th>容器</th><th>虚拟机</th></tr></thead><tbody><tr><td>启动</td><td>秒级</td><td>分钟级</td></tr><tr><td>硬盘使用</td><td>一般为 MB</td><td>一般为 GB</td></tr><tr><td>性能</td><td>接近原生</td><td>弱于原生</td></tr><tr><td>系统支持量</td><td>单机支持上千个容器</td><td>一般几十个</td></tr></tbody></table><h2 id="Docker-基本概念"><a href="#Docker-基本概念" class="headerlink" title="Docker 基本概念"></a>Docker 基本概念</h2><h3 id="镜像-Image"><a href="#镜像-Image" class="headerlink" title="镜像 (Image)"></a>镜像 (Image)</h3><p>Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如环境变量等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变,相当于是一个只读的模板。</p><h3 id="容器-Container"><a href="#容器-Container" class="headerlink" title="容器 (Container)"></a>容器 (Container)</h3><p>镜像和容器的关系,就像是面向对象程序设计中的 <strong>类</strong> 和 <strong>对象</strong> 一样,镜像是静态的定义,容器是镜像运行时的实体。</p><p>容器的实质是进程,但与直接在宿主执行的进程不同,容器可以拥有自己的文件系统、网络配置、进程空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。</p><h3 id="仓库-Repository"><a href="#仓库-Repository" class="headerlink" title="仓库 (Repository)"></a>仓库 (Repository)</h3><p>通常一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <strong><仓库名>:<标签></strong> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 <code>latest</code> 作为默认标签。</p><h3 id="Docker-Registry"><a href="#Docker-Registry" class="headerlink" title="Docker Registry"></a>Docker Registry</h3><p>Docker Registry 是一个集中的存储、分发镜像的公开服务。这类公开服务允许用户免费上传、下载公开的镜像。</p><p>最常使用的 Registry 公开服务是官方的 <strong>Docker Hub</strong>,这也是默认的 Registry,并拥有大量的高质量的官方镜像。</p><h2 id="使用镜像"><a href="#使用镜像" class="headerlink" title="使用镜像"></a>使用镜像</h2><p>Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。</p><h3 id="获取镜像"><a href="#获取镜像" class="headerlink" title="获取镜像"></a>获取镜像</h3><p>搜索 Docker Hub 中的镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> search <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>从 Docker 镜像仓库获取镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> pull <span class="token operator"><</span>仓库名<span class="token operator">></span>:<span class="token operator"><</span>标签<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>Docker Registry 地址格式一般是 <code><域名>/<用户名>/<镜像名></code>。默认地址是 Docker Hub。</p><p>仓库名一般为 <code><用户名>/<镜像名></code>。对于 Docker Hub,如果不给出用户名,则默认为官方镜像。</p><p>查看已经下载下来的镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> images<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="删除镜像"><a href="#删除镜像" class="headerlink" title="删除镜像"></a>删除镜像</h3><p>删除本地的镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> rmi <span class="token operator"><</span>镜像<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>其中,<code><镜像></code> 可以是镜像短 ID、镜像长 ID、镜像名或者镜像摘要。</p><h2 id="操作容器"><a href="#操作容器" class="headerlink" title="操作容器"></a>操作容器</h2><p>查看当前所有容器:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> <span class="token function">ps</span> <span class="token parameter variable">-a</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="启动容器"><a href="#启动容器" class="headerlink" title="启动容器"></a>启动容器</h3><p>启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态的容器重新启动。</p><h4 id="新建并启动"><a href="#新建并启动" class="headerlink" title="新建并启动"></a>新建并启动</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--rm</span> <span class="token operator"><</span>镜像名<span class="token operator">></span> /bin/bash<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li><code>-i</code>:交互式操作</li><li><code>-t</code>:终端</li><li><code>--rm</code>:容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除。</li><li><code><镜像名></code>:用镜像为基础来启动容器</li><li><code>/bin/bash</code>:放在镜像名后的是<strong>命令</strong>,这里希望有个交互式 Shell</li></ul><p>当利用 <code>docker run</code> 来创建容器时,Docker 在后台运行的标准操作包括:</p><ol><li>检查本地是否存在指定的镜像,不存在就从公有仓库下载</li><li>利用镜像创建并启动一个容器</li><li>分配一个文件系统,并在只读的镜像层外面挂载一层可读写层</li><li>从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去</li><li>从地址池配置一个 IP 地址给容器</li><li>执行用户指定的应用程序</li><li>执行完毕后容器被终止</li></ol><h4 id="启动已终止容器"><a href="#启动已终止容器" class="headerlink" title="启动已终止容器"></a>启动已终止容器</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> start <span class="token operator"><</span>容器 ID<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 <code>ps</code> 或 <code>top</code> 来查看进程信息。</p><h4 id="后台运行"><a href="#后台运行" class="headerlink" title="后台运行"></a>后台运行</h4><p>如果需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 <code>-d</code> 参数来实现:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果不使用 <code>-d</code> 参数运行容器,容器会把输出的结果打印到宿主机上面。如果使用了 <code>-d</code> 参数运行容器,此时容器会在后台运行。</p><p>容器是否会长久运行,与 <strong>docker run</strong> 指定的命令有关,和 <code>-d</code> 参数无关。</p><p>获取容器的输出信息:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> logs <span class="token operator"><</span>容器 ID<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="终止容器"><a href="#终止容器" class="headerlink" title="终止容器"></a>终止容器</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> stop <span class="token operator"><</span>容器 ID<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>此外,当 Docker 容器中指定的应用终结时,容器也自动终止。</p><p>处于终止状态的容器,可以通过 <code>docker start</code> 命令来启动。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> restart <span class="token operator"><</span>容器 ID<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><code>docker restart</code> 会将一个运行态的容器终止,然后再重新启动它。</p><h3 id="进入容器"><a href="#进入容器" class="headerlink" title="进入容器"></a>进入容器</h3><p>某些时候需要进入后台运行的容器进行操作,包括使用 <code>docker attach</code> 命令或 <code>docker exec</code> 命令,推荐使用 <code>docker exec</code> 命令。</p><p><code>docker attach</code> 命令可以附加到一个正在运行的容器上,可以看到容器的输出,退出容器的时候,容器会停止运行。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> <span class="token builtin class-name">exec</span> <span class="token parameter variable">-it</span> <span class="token operator"><</span>容器 ID<span class="token operator">></span> /bin/bash<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><code>docker exec</code> 命令用于进入一个正在运行的容器。退出容器后,容器会继续运行。</p><h3 id="删除容器"><a href="#删除容器" class="headerlink" title="删除容器"></a>删除容器</h3><p>删除一个处于终止状态的容器:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> container <span class="token function">rm</span> <span class="token operator"><</span>容器 ID<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果要删除一个运行中的容器,可以添加 <code>-f</code> 参数。Docker 会发送 <code>SIGKILL</code> 信号给容器。</p><p>删除所有处于终止状态的容器:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> container prune<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="数据管理"><a href="#数据管理" class="headerlink" title="数据管理"></a>数据管理</h2><h3 id="数据卷-Volume"><a href="#数据卷-Volume" class="headerlink" title="数据卷 (Volume)"></a>数据卷 (Volume)</h3><p>数据卷是一个可供一个或多个容器使用的特殊目录,可以提供很多有用的特性:</p><ul><li>数据卷可以在容器之间共享和重用</li><li>对数据卷的修改会立马生效</li><li>对数据卷的更新,不会影响镜像</li><li>数据卷默认会一直存在,即使容器被删除</li></ul><p>创建一个数据卷:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> volume create <span class="token operator"><</span>卷名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>查看所有的数据卷:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> volume <span class="token function">ls</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>查看指定数据卷的信息:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> volume inspect <span class="token operator"><</span>卷名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在用 <code>docker run</code> 命令的时候,使用 <code>-v</code> 标记来将数据卷挂载到容器里。一次可以挂载多个数据卷:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-v</span> <span class="token operator"><</span>卷名<span class="token operator">></span>:<span class="token operator"><</span>容器内路径<span class="token operator">></span> <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>查看容器中数据卷的具体信息:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> inspect <span class="token operator"><</span>容器 ID<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>删除数据卷:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> volume <span class="token function">rm</span> <span class="token operator"><</span>卷名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷。如果需要在删除容器的同时移除数据卷,可以在删除容器的时候使用:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> <span class="token function">rm</span> <span class="token parameter variable">-v</span> <span class="token operator"><</span>容器 ID<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>删除所有无主的数据卷:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> volume prune<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="挂载主机目录"><a href="#挂载主机目录" class="headerlink" title="挂载主机目录"></a>挂载主机目录</h3><p>使用 <code>-v</code> 标记可以指定挂载一个本地主机的目录到容器中去:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">-P</span> <span class="token punctuation">\</span><span class="token parameter variable">--name</span> web <span class="token punctuation">\</span><span class="token parameter variable">--mount</span> <span class="token assign-left variable">type</span><span class="token operator">=</span>bind,source<span class="token operator">=</span><span class="token environment constant">$HOME</span>/test,target<span class="token operator">=</span>/root/test,readonly <span class="token punctuation">\</span>ubuntu:22.04 <span class="token punctuation">\</span><span class="token function">bash</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>本地目录的路径必须是绝对路径。</p><p>Docker 挂载主机目录的默认权限是读写,用户也可以通过增加 <code>readonly</code> 指定为只读。</p><p><code>--mount</code> 标记也可以从主机挂载单个文件到容器中。例如想要记录在容器中输入过的命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">--rm</span> <span class="token parameter variable">-it</span> <span class="token punctuation">\</span><span class="token parameter variable">--mount</span> <span class="token assign-left variable">type</span><span class="token operator">=</span>bind,source<span class="token operator">=</span><span class="token environment constant">$HOME</span>/.bash_history,target<span class="token operator">=</span>/root/.bash_history <span class="token punctuation">\</span>ubuntu:22.04 <span class="token punctuation">\</span><span class="token function">bash</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h2><p>Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。</p><h3 id="外部访问容器"><a href="#外部访问容器" class="headerlink" title="外部访问容器"></a>外部访问容器</h3><p>容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 <code>-P</code> 或 <code>-p</code> 参数来指定端口映射。</p><p>当使用 <code>-P</code> 标记时,Docker 会随机映射一个 <code>49000~49900</code> 的端口到内部容器开放的网络端口。</p><p><code>-p</code> 则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有:</p><ul><li><code><主机端口>:<容器端口></code></li><li><code><主机 IP>:<主机端口>:<容器端口></code></li><li><code><主机端口>:<容器端口>/<协议></code></li></ul><p>可以多次使用 <code>-p</code> 来绑定多个端口。</p><p>映射到指定地址的指定端口:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-p</span> <span class="token number">127.0</span>.0.1:80:8080 <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>映射到指定地址的随机端口:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-p</span> <span class="token number">127.0</span>.0.1::8080 <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>映射到所有地址的指定端口:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-p</span> <span class="token number">80</span>:8080 <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="容器互联"><a href="#容器互联" class="headerlink" title="容器互联"></a>容器互联</h3><p>创建一个 Docker 网络:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> network create <span class="token parameter variable">-d</span> bridge <span class="token operator"><</span>网络名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>参数指定 Docker 网络类型,有 <code>bridge</code>、<code>overlay</code>、<code>host</code>。</p><p>在一个终端运行一个容器并连接到网络:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--network</span> <span class="token operator"><</span>网络名<span class="token operator">></span> <span class="token parameter variable">--name</span> test1 <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在另一个终端运行另一个容器连接到网络:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--network</span> <span class="token operator"><</span>网络名<span class="token operator">></span> <span class="token parameter variable">--name</span> test2 <span class="token operator"><</span>镜像名<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在 <code>test2</code> 可以 ping 通 <code>test1</code>,反之亦然。</p><h2 id="构建镜像"><a href="#构建镜像" class="headerlink" title="构建镜像"></a>构建镜像</h2><h3 id="Docker-commit"><a href="#Docker-commit" class="headerlink" title="Docker commit"></a>Docker commit</h3><p>例如:以 <code>ubuntu:22.04</code> 为基础,构建一个拥有 <code>vim</code> 的新镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> ubuntu:22.04 /bin/bash<span class="token function">apt-get</span> update<span class="token function">apt-get</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> <span class="token function">vim</span><span class="token builtin class-name">exit</span><span class="token function">docker</span> commit <span class="token parameter variable">-m</span> <span class="token string">"Add vim"</span> <span class="token operator"><</span>容器 ID<span class="token operator">></span> my-ubuntu:vim<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="Dockerfile"><a href="#Dockerfile" class="headerlink" title="Dockerfile"></a>Dockerfile</h3><p>使用 <code>docker commit</code> 创建镜像的方式不够灵活。更好的方式是使用 <code>Dockerfile</code> 来构建镜像。</p><p><code>Dockerfile</code> 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。通过定义一系列命令和参数,<code>Dockerfile</code> 指导 Docker 构建一个自定义的镜像。</p><p>Docker 执行的过程:</p><ol><li>从基础镜像运行一个容器</li><li>执行一条指令并对容器做出修改</li><li>执行类似 <code>docker commit</code> 的操作,提交生成一个新的镜像层</li><li>再基于刚才新的镜像运行一个新容器</li><li>执行下一条指令,直到所有指令都执行完成</li></ol><p>对于上面的例子,可以使用 <code>Dockerfile</code> 来构建一个拥有 <code>vim</code> 的新镜像:</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">FROM</span> ubuntu:22.04</span><span class="token instruction"><span class="token keyword">RUN</span> apt-get update && apt-get install -y vim</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="FROM"><a href="#FROM" class="headerlink" title="FROM"></a>FROM</h4><p><code>FROM</code> 指令的作用是指定基础镜像。一个 <code>Dockerfile</code> 中是必备的指令,并且必须是第一条指令。</p><p>除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像 <code>scratch</code>。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。</p><p>如果以 <code>scratch</code> 开始为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。</p><h4 id="RUN"><a href="#RUN" class="headerlink" title="RUN"></a>RUN</h4><p><code>RUN</code> 指令是用来执行命令行命令的。其格式有两种:</p><ul><li><strong>shell 格式</strong>:<code>RUN <命令></code></li><li><strong>exec 格式</strong>:<code>RUN ["可执行文件", "参数 1", "参数 2"]</code></li></ul><p><code>Dockerfile</code> 中每一个指令都会建立一层。每一个 <code>RUN</code> 指令都会新建立一层,commit 这一层的修改,构成新的镜像。因此,不要这样写:</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">FROM</span> debian:stretch</span><span class="token instruction"><span class="token keyword">RUN</span> apt-get update</span><span class="token instruction"><span class="token keyword">RUN</span> apt-get install -y gcc libc6-dev make wget</span><span class="token instruction"><span class="token keyword">RUN</span> wget -O redis.tar.gz <span class="token string">"http://download.redis.io/releases/redis-5.0.3.tar.gz"</span></span><span class="token instruction"><span class="token keyword">RUN</span> mkdir -p /usr/src/redis</span><span class="token instruction"><span class="token keyword">RUN</span> tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1</span><span class="token instruction"><span class="token keyword">RUN</span> make -C /usr/src/redis</span><span class="token instruction"><span class="token keyword">RUN</span> make -C /usr/src/redis install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>应该这样写:</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">FROM</span> debian:stretch</span><span class="token instruction"><span class="token keyword">RUN</span> buildDeps=<span class="token string">'gcc libc6-dev make wget'</span> <span class="token operator">\</span>&& apt-get update <span class="token operator">\</span>&& apt-get install -y <span class="token variable">$buildDeps</span> <span class="token operator">\</span>&& wget -O redis.tar.gz <span class="token string">"http://download.redis.io/releases/redis-5.0.3.tar.gz"</span> <span class="token operator">\</span>&& mkdir -p /usr/src/redis <span class="token operator">\</span>&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 <span class="token operator">\</span>&& make -C /usr/src/redis <span class="token operator">\</span>&& make -C /usr/src/redis install <span class="token operator">\</span>&& rm -rf /var/lib/apt/lists/* <span class="token operator">\</span>&& rm redis.tar.gz <span class="token operator">\</span>&& rm -r /usr/src/redis <span class="token operator">\</span>&& apt-get purge -y --auto-remove <span class="token variable">$buildDeps</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>之前的 7 层简化为了 1 层。同时每一层的东西并不会在下一层被删除,会一直跟随着镜像。镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。如上面的 <code>rm</code>。</p><h4 id="WORKDIR"><a href="#WORKDIR" class="headerlink" title="WORKDIR"></a>WORKDIR</h4><p><code>WORKDIR</code> 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,<code>WORKDIR</code> 会建立目录。</p><p>格式为:</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">WORKDIR</span> <工作目录路径></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="COPY"><a href="#COPY" class="headerlink" title="COPY"></a>COPY</h4><p><code>COPY</code> 指令将从构建上下文目录中 <code><源路径></code> 的文件/目录复制到新的一层的镜像内的 <code><目标路径></code> 位置。</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">COPY</span> <源路径>... <目标路径></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><code><源路径></code> 可以是多个,也可以是通配符表达式。</p><p><code><目标路径></code> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径。</p><p>使用该指令通过加上 <code>--chown=<用户>:<用户组></code> 选项来改变文件的所属用户及所属组。</p><h4 id="CMD"><a href="#CMD" class="headerlink" title="CMD"></a>CMD</h4><p><code>CMD</code> 指令用于指定默认的容器主进程的启动命令。和 <code>RUN</code> 相似,也是两种格式:</p><ul><li><strong>shell 格式</strong>:<code>CMD <命令></code></li><li><strong>exec 格式</strong>:<code>CMD ["可执行文件", "参数 1", "参数 2"...]</code></li></ul><p><code>RUN</code> 是在 <code>docker build</code> 的时候运行,而 <code>CMD</code> 是在 <code>docker run</code> 的时候运行。</p><h4 id="ENTRYPOINT"><a href="#ENTRYPOINT" class="headerlink" title="ENTRYPOINT"></a>ENTRYPOINT</h4><p><code>ENTRYPOINT</code> 的格式和 <code>RUN</code> 指令格式一样,分为 <strong>exec 格式</strong> 和 <strong>shell 格式</strong>。</p><p><code>ENTRYPOINT</code> 的目的和 <code>CMD</code> 一样,都是在指定容器启动程序及参数。</p><p>当指定了 <code>ENTRYPOINT</code> 后,<code>CMD</code> 的含义就发生了改变,不再是直接的运行其命令,而是将 <code>CMD</code> 的内容作为参数传给 <code>ENTRYPOINT</code> 指令。实际执行时,将变为:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token operator"><</span>ENTRYPOINT<span class="token operator">></span> <span class="token operator"><</span>CMD<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>假设,通过这个 <code>Dockerfile</code> 构建了一个镜像 <code>my-image</code>:</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">FROM</span> ubuntu:22.04</span><span class="token instruction"><span class="token keyword">ENTRYPOINT</span> [<span class="token string">"echo"</span>, <span class="token string">"Hello"</span>]</span><span class="token instruction"><span class="token keyword">CMD</span> [<span class="token string">"World"</span>]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>那么,不传参运行这个镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run my-image<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>容器内会默认运行以下命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">echo</span> Hello World<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果传参运行这个镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run my-image Universe<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>容器内会运行以下命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">echo</span> Hello world Universe<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="ENV"><a href="#ENV" class="headerlink" title="ENV"></a>ENV</h4><p><code>ENV</code> 指令用来设置环境变量。无论是后面的其它指令,如 <code>RUN</code>,还是运行时的应用,都可以直接使用这里定义的环境变量。</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">ENV</span> <key> <value></span><span class="token instruction"><span class="token keyword">ENV</span> <key1>=<value1> <key2>=<value2>...</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="ARG"><a href="#ARG" class="headerlink" title="ARG"></a>ARG</h4><p><code>ARG</code> 与 <code>ENV</code> 作用类似,但是作用域不同。<code>ARG</code> 只有在构建镜像时才有用,构建好的镜像不存在这个变量。</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">ARG</span> <参数名>[=<默认值>]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="VOLUME"><a href="#VOLUME" class="headerlink" title="VOLUME"></a>VOLUME</h4><p>容器运行时应该尽量保持容器存储层不发生写操作,对于需要保存动态数据的应用,其数据库文件应该保存于卷 (volume) 中。</p><p><code>VOLUME</code> 指令事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。</p><pre class="line-numbers language-docker" data-language="docker"><code class="language-docker"><span class="token instruction"><span class="token keyword">VOLUME</span> <路径></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这里的 <code><路径></code> 目录就会在运行时自动挂载为匿名卷,任何向 <code><路径></code> 中写入的信息都不会记录进容器存储层。</p><h4 id="运行-Dockerfile"><a href="#运行-Dockerfile" class="headerlink" title="运行 Dockerfile"></a>运行 Dockerfile</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token punctuation">[</span>选项<span class="token punctuation">]</span> <span class="token operator"><</span>上下文路径/URL/-<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>比如:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> myapp:1.0 <span class="token builtin class-name">.</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li><code>-t</code>:指定标签</li><li><code><上下文路径></code>:表示上下文 (Context) 路径。这里是当前目录。当我们进行镜像构建的时候,经常会需要将一些本地文件复制进镜像,比如 <code>COPY</code> 指令等。而 <code>docker build</code> 命令构建镜像,其实并非在本地构建,而是在服务端中构建的。</li></ul><p><code>docker build</code> 命令得知这个上下文路径后,会将路径下的所有内容打包,然后上传到服务端。</p><p><code>Dockerfile</code> 的文件名并不要求必须为 <code>Dockerfile</code>,而且并不要求必须位于上下文目录中。可以这样写:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> myapp:1.0 <span class="token parameter variable">-f</span> /path/to/a/DockeRFilE <span class="token builtin class-name">.</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> Docker </tag>
</tags>
</entry>
<entry>
<title>通信协议学习笔记</title>
<link href="/2025/07/14/2025-07-14-communication-protocol-notes/"/>
<url>/2025/07/14/2025-07-14-communication-protocol-notes/</url>
<content type="html"><![CDATA[<h2 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h2><h3 id="TCP-IP-模型"><a href="#TCP-IP-模型" class="headerlink" title="TCP/IP 模型"></a>TCP/IP 模型</h3><table><thead><tr><th>OSI 模型(国际标准化组织 ISO 提出)</th><th>功能</th></tr></thead><tbody><tr><td>物理层</td><td>传输⽐特流</td></tr><tr><td>数据链路层</td><td>节点之间传输帧</td></tr><tr><td>⽹络层</td><td>报⽂分组、路由选择</td></tr><tr><td>传输层</td><td>端到端传输和流量控制</td></tr><tr><td>会话层</td><td>建⽴、管理和终⽌会话</td></tr><tr><td>表⽰层</td><td>负责数据格式转换和加密</td></tr><tr><td>应⽤层</td><td>提供⽹络服务</td></tr></tbody></table><table><thead><tr><th>TCP/IP 模型</th><th>功能</th></tr></thead><tbody><tr><td>应⽤层</td><td>包含⾼层协议,如 HTTP、FTP、SMTP 等</td></tr><tr><td>传输层</td><td>提供端到端通信,包含传输控制协议(TCP)和⽤⼾数据报协议(UDP)</td></tr><tr><td>互联⽹层</td><td>对应 OSI 模型的⽹络层,负责 IP 地址和路由选择,包含 IP 协议</td></tr><tr><td>⽹络接⼊层</td><td>相当于 OSI 模型的物理层和数据链路层</td></tr></tbody></table><p>TCP/IP 模型是对 OSI 模型的简化</p><h3 id="IP-地址"><a href="#IP-地址" class="headerlink" title="IP 地址"></a>IP 地址</h3><p>互联⽹协议的⽬的是解决实现⼤规模、异构⽹络的互联问题。IP 地址是⽹络上的设备的地址标识。IP 地址分为 IPv4 和 IPv6</p><h4 id="IPv4"><a href="#IPv4" class="headerlink" title="IPv4"></a>IPv4</h4><p>IPv4 是 32 位(4 字节)地址,因此⼀共可以存储 $2^{32}=4294967296$ 个地址,但是实际使⽤中保留了很多特<br>殊⽤途的地址,如专⽤⽹络(约 1800 万个地址)和多播地址(约 2.7 亿个地址)。</p><p>为⽅便表⽰,IPv4 采⽤点分⼗进制形式表⽰,4 个字节分别⽤⼗进制写出,中间使⽤点分割。如 <code>192.168.43.14</code></p><p>2011 年,IPv4 主要地址池⽤尽;2019 年,所有 IPv4 地址分配完毕</p><h4 id="IPv6"><a href="#IPv6" class="headerlink" title="IPv6"></a>IPv6</h4><p>IPv6 采⽤ 128bit 地址,每 16bit 为⼀组,中间使⽤冒号隔开:<code>ABCD:EF01:2345:6789:ABCD:EF01:2345:6789</code></p><p>IPv6 可以存储 $2^{128}=3.4\times10^{38}$ 个地址,解决了 IPv4 资源不⾜的问题。</p><h3 id="域名"><a href="#域名" class="headerlink" title="域名"></a>域名</h3><p>IP 地址可以唯⼀的标记⽹络上的计算机,但是数字冗⻓,难以记忆;于是有了字符型的地址⽅案——域名地址</p><p>IP 地址和域名地址是⼀⼀对应的,<code>www.baidu.com</code> 就是域名地址</p><h3 id="端口"><a href="#端口" class="headerlink" title="端口"></a>端口</h3><p>计算机有多个进程并⾏执⾏,游览器需要双向传输信息,微信也需要双向传输信息,QQ 也需要双向传输信息。那么,当主机接收到数据包后,应该转发给哪个进程?</p><p>由此,引⼊端⼝的概念区分不同进程。⼀个 IP 地址允许有 65536 个端⼝(编号 0-65535)。0-1023 编号的端⼝称为周知端⼝,⼤多数固定分配给某个服务,例如 80 端⼝分配给 www 服务(包含 http)。</p><h3 id="HTTP:超⽂本传输协议"><a href="#HTTP:超⽂本传输协议" class="headerlink" title="HTTP:超⽂本传输协议"></a>HTTP:超⽂本传输协议</h3><p>http 是万维⽹ www 数据通信的基础</p><p>http 是经典的 server-client 模式:</p><ol><li>客⼾与服务器建⽴连接</li><li>客⼾向服务器发出请求(request)</li><li>服务器接受请求,返回相应⽂件作为应答(response)</li><li>客⼾和服务器关闭连接</li></ol><p>http 连接是⼀次性连接,每次连接只能发送 1 个请求,服务器返回应答后连接⽴即关闭</p><h4 id="HTTP-请求格式"><a href="#HTTP-请求格式" class="headerlink" title="HTTP 请求格式"></a>HTTP 请求格式</h4><p>请求⾏–请求头–空⾏–请求体</p><h5 id="请求⾏"><a href="#请求⾏" class="headerlink" title="请求⾏"></a>请求⾏</h5><p>包括 HTTP ⽅法、请求的 URI、HTTP 版本</p><pre class="line-numbers language-none"><code class="language-none">GET /index.html HTTP/1.1<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>经典的 HTTP ⽅法有:</p><ul><li><strong>GET</strong>:请求指定资源</li><li><strong>POST</strong>:向资源提交/上传数据</li><li><strong>PUT</strong>:向指定资源上传其最新内容</li><li><strong>DELETE</strong>:删除资源</li></ul><h5 id="请求头"><a href="#请求头" class="headerlink" title="请求头"></a>请求头</h5><p>包括⼀系列的键值对,例如:</p><ul><li><strong>Host</strong>:服务器域名</li><li><strong>User-Agent</strong>:客⼾端的软件信息</li><li><strong>Content-Type</strong>:请求体的媒体类型</li><li><strong>Content-Length</strong>:请求体的⻓度</li><li><strong>Accept</strong>:⽤⼾可以接受的内容类型</li></ul><pre class="line-numbers language-none"><code class="language-none">Host: www.example.comUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>空⾏⽤于分隔头部和请求体</p><p>请求体⼀般在 POST、PUT 请求中,⼀般代表需要上传的数据。请求体可以是 JSON 或者 XML 格式:</p><pre class="line-numbers language-none"><code class="language-none">POST /submit-form HTTP/1.1Host: www.example.comContent-Type: application/x-www-form-urlencodedContent-Length: 27{"name": "John Doe","age": 30}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="HTTP-响应格式"><a href="#HTTP-响应格式" class="headerlink" title="HTTP 响应格式"></a>HTTP 响应格式</h4><p>状态⾏–响应头部–空⾏–响应体</p><p>状态⾏包括 HTTP 版本、状态码和状态描述:</p><pre class="line-numbers language-none"><code class="language-none">HTTP/1.1 200 OK<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>常见状态码:</p><ul><li><strong>200 OK</strong> 请求成功(此时响应体是 GET 和 POST 请求的应答⽂档)</li><li><strong>204 No Content</strong> 请求成功,没有返回⽂档</li><li><strong>400 Bad Request</strong> 服务器未能理解请求</li><li><strong>401 Unauthorized</strong> 被请求的界⾯需要⽤⼾名和密码</li><li><strong>403 Forbidden</strong> 对⻚⾯的访问被禁⽌</li><li><strong>404 Not Found</strong> 服务器⽆法找到请求⻚⾯</li><li><strong>408 Request Timeout</strong> 请求超时</li></ul><h2 id="Protobuf"><a href="#Protobuf" class="headerlink" title="Protobuf"></a>Protobuf</h2><h3 id="序列化协议"><a href="#序列化协议" class="headerlink" title="序列化协议"></a>序列化协议</h3><p>通信传输过程中⾯临的问题:不同的编程语⾔使⽤不同的数据结构,传输不⽅便</p><p>序列化协议定义了数据的编码和解码⽅式,确保发送⽅和接收⽅都能正确理解和处理数据。通过将复杂的数据结构(如对象、数组等)转换成线性的字节流,序列化协议消除了潜在的兼容性问题。</p><p>常⻅的序列化协议有:JSON、XML、protobuf</p><p>例如,创建⼀个姓名是 John Doe,年龄 30 的⼈:</p><h4 id="JSON"><a href="#JSON" class="headerlink" title="JSON"></a>JSON</h4><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span><span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"John Doe"</span><span class="token punctuation">,</span><span class="token property">"age"</span><span class="token operator">:</span> <span class="token number">30</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>优点:易读、易写、⼴泛⽀持;序列化、反序列化速度较慢</p><h4 id="XML"><a href="#XML" class="headerlink" title="XML"></a>XML</h4><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>person</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>name</span><span class="token punctuation">></span></span>John Doe<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>name</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>age</span><span class="token punctuation">></span></span>30<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>age</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>person</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>优点:可扩展、可⾃定义标签、⼴泛⽀持;序列化、反序列化速度较慢</p><h4 id="Protobuf-1"><a href="#Protobuf-1" class="headerlink" title="Protobuf"></a>Protobuf</h4><p>protobuf 的优点:序列化后为⼆进制格式,⾼效,⽀持多种语⾔,向后兼容</p><p>向后兼容:新版本可以打开老的文件</p><p>protobuf 需要预先定义 proto ⽂件:</p><pre class="line-numbers language-protobuf" data-language="protobuf"><code class="language-protobuf"><span class="token keyword">syntax</span> <span class="token operator">=</span> <span class="token string">"proto3"</span><span class="token punctuation">;</span><span class="token keyword">message</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span> <span class="token builtin">string</span> name <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// 编号是 1</span> <span class="token builtin">int32</span> age <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment">// 编号是 2</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>随后,使⽤ protoc 编译 proto ⽂件:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">protoc person.proto <span class="token parameter variable">--python_out</span><span class="token operator">=</span>.<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>会生成 <code>person_pb2.py</code>,可以直接调用其中的东西</p><p>随后,创建⼀个新的 python ⽂件:</p><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> person_pb2 <span class="token keyword">import</span> Personperson <span class="token operator">=</span> Person<span class="token punctuation">(</span><span class="token punctuation">)</span>person<span class="token punctuation">.</span>name <span class="token operator">=</span> <span class="token string">'Alice'</span>person<span class="token punctuation">.</span>age <span class="token operator">=</span> <span class="token number">25</span>data <span class="token operator">=</span> person<span class="token punctuation">.</span>SerializeToString<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">" "</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"</span><span class="token interpolation"><span class="token punctuation">{</span>byte<span class="token punctuation">:</span><span class="token format-spec">02x</span><span class="token punctuation">}</span></span><span class="token string">"</span></span> <span class="token keyword">for</span> byte <span class="token keyword">in</span> data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span>person<span class="token punctuation">.</span>age<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>运行结果:</p><pre class="line-numbers language-none"><code class="language-none">b'\n\x05Alice\x10\x19'0a 05 41 6c 69 63 65 10 1925<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>第⼀⾏的表⽰⽅法是 Python 中字节串的表⽰,第⼆⾏是 16 进制表⽰。1 字节(byte)= 8bit</p><p>16 进制 0a 化为 2 进制是 0000 1010,右 3 位 010 表⽰线类型是字符串,其他位 00001 代表字段是 1</p><p>protobuf 标记格式:<code>(字段号 << 3) | 线类型</code></p><p>常⻅线类型:</p><ul><li><strong>0</strong>:Varint(整数类型),长度可变</li><li><strong>1</strong>:64 位</li><li><strong>2</strong>:⻓度前缀的字符串或字节数组</li><li><strong>3</strong>:开始嵌套消息</li><li><strong>4</strong>:结束嵌套消息</li><li><strong>5</strong>:32 位</li></ul><p>05 表⽰字符串的⻓度是 5 个字节,41 是 A 的 UTF-8 编码</p><h3 id="protobuf-其他⽤法"><a href="#protobuf-其他⽤法" class="headerlink" title="protobuf 其他⽤法"></a>protobuf 其他⽤法</h3><ul><li><code>//</code>:添加注释</li><li><strong>package</strong>:包声明,防⽌不同项⽬之间命名冲突</li></ul><p><strong>数据类型</strong></p><p>常⻅的数据类型:bool、int32、int64、float、double、string、enum(枚举)</p><p>message(消息)可以嵌套 message</p><p>“=1”、”=2”的数字表⽰该字段的唯⼀标签,1-15 ⽤于最频繁使⽤的字段(占⽤空间少)</p><p><strong>数据类型之前的修饰符有</strong>:</p><ul><li><strong>optional</strong>:该字段是可选的,可以设置它的值,也可以不设置它的值。如果不设置它的值,那么在访问字段的值时返回默认值</li><li><strong>repeated</strong>:这个字段可以重复多次(可以是 0 次),保存了重复的先后顺序,repeated 是⼀个动态数组</li></ul><pre class="line-numbers language-protobuf" data-language="protobuf"><code class="language-protobuf"><span class="token keyword">syntax</span> <span class="token operator">=</span> <span class="token string">"proto3"</span><span class="token punctuation">;</span><span class="token keyword">package</span> tutorial<span class="token punctuation">;</span><span class="token keyword">message</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span> <span class="token keyword">optional</span> <span class="token builtin">string</span> name <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">optional</span> <span class="token builtin">int32</span> id <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token keyword">optional</span> <span class="token builtin">string</span> email <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span> <span class="token keyword">enum</span> <span class="token class-name">PhoneType</span> <span class="token punctuation">{</span> PHONE_TYPE_UNSPECIFIED <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> PHONE_TYPE_MOBILE <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> PHONE_TYPE_HOME <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> PHONE_TYPE_WORK <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">message</span> <span class="token class-name">PhoneNumber</span> <span class="token punctuation">{</span> <span class="token keyword">optional</span> <span class="token builtin">string</span> number <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">optional</span> <span class="token positional-class-name class-name">PhoneType</span> type <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">repeated</span> <span class="token positional-class-name class-name">PhoneNumber</span> phones <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">message</span> <span class="token class-name">AddressBook</span> <span class="token punctuation">{</span> <span class="token keyword">repeated</span> <span class="token positional-class-name class-name">Person</span> people <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> protocol </tag>
</tags>
</entry>
<entry>
<title>TikZ 绘图学习笔记</title>
<link href="/2025/07/14/2025-07-14-tikz-drawing-notes/"/>
<url>/2025/07/14/2025-07-14-tikz-drawing-notes/</url>
<content type="html"><![CDATA[<h2 id="基本环境"><a href="#基本环境" class="headerlink" title="基本环境"></a>基本环境</h2><p>插入图片快捷键 BFI,在图片环境下:</p><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\begin</span><span class="token punctuation">{</span><span class="token keyword">figure</span><span class="token punctuation">}</span><span class="token punctuation">[</span>htp<span class="token punctuation">]</span> <span class="token function selector">\centering</span> <span class="token function selector">\begin</span><span class="token punctuation">{</span><span class="token keyword">tikzpicture</span><span class="token punctuation">}</span><span class="token punctuation">[</span>>=latex<span class="token punctuation">]</span> <span class="token comment">% 绘图命令</span> <span class="token function selector">\end</span><span class="token punctuation">{</span><span class="token keyword">tikzpicture</span><span class="token punctuation">}</span> <span class="token function selector">\caption</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token function selector">\end</span><span class="token punctuation">{</span><span class="token keyword">figure</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>[>=latex]</code> 是箭头样式,对于 <code>\begin{tikzpicture}</code> 到 <code>\end{tikzpicture}</code> 之间的所有箭头生效,还有一种是 <code>[>=stealth]</code></p><h2 id="基本命令"><a href="#基本命令" class="headerlink" title="基本命令"></a>基本命令</h2><h3 id="画直线"><a href="#画直线" class="headerlink" title="画直线"></a>画直线</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span> (0,0)--(5,0); <span class="token comment">% 直线不带箭头</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 带箭头</span><span class="token function selector">\draw</span><span class="token punctuation">[</span><-<span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 带箭头</span><span class="token function selector">\draw</span><span class="token punctuation">[</span><-><span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 带箭头</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>|<->|<span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 带箭头</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>dashed<span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 虚线</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>dotted<span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 点线</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>dashdotted<span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 点划线</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>dashed,thick,-><span class="token punctuation">]</span> (0,0)--(5,0); <span class="token comment">% 选项可以组合,还有 very thick, ultra thick, thin 等</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="添加标识"><a href="#添加标识" class="headerlink" title="添加标识"></a>添加标识</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span> <span class="token punctuation">[</span>dashed<span class="token punctuation">]</span> (0,0)--node<span class="token punctuation">[</span>above=3mm<span class="token punctuation">]</span><span class="token punctuation">{</span>5cm<span class="token punctuation">}</span>(5,0); <span class="token comment">% 在直线上方添加标识</span><span class="token function selector">\draw</span> <span class="token punctuation">[</span>dashed<span class="token punctuation">]</span> (0,0)node<span class="token punctuation">[</span>above=3mm<span class="token punctuation">]</span><span class="token punctuation">{</span>5cm<span class="token punctuation">}</span>--(5,0); <span class="token comment">% node 位置可以更改</span><span class="token function selector">\draw</span> <span class="token punctuation">[</span>dashed<span class="token punctuation">]</span> (0,0)--node<span class="token punctuation">[</span>fill=white<span class="token punctuation">]</span><span class="token punctuation">{</span>5cm<span class="token punctuation">}</span>(5,0); <span class="token comment">% 这样标识不会被直线遮挡</span><span class="token function selector">\draw</span> <span class="token punctuation">[</span>dashed<span class="token punctuation">]</span> (0,0)--node<span class="token punctuation">[</span>fill=red!20!white<span class="token punctuation">]</span><span class="token punctuation">{</span>5cm<span class="token punctuation">}</span>(5,0); <span class="token comment">% 20% 的红色,80% 的白色</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>[!NOTE]</p><p>写 node 的位置是要放的对象之后</p></blockquote><h3 id="给点加标签"><a href="#给点加标签" class="headerlink" title="给点加标签"></a>给点加标签</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\node</span> at (-0.2,-0.2)<span class="token punctuation">{</span><span class="token equation string">$O$</span><span class="token punctuation">}</span>; <span class="token comment">% 给原点加标签</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="给坐标轴加刻度"><a href="#给坐标轴加刻度" class="headerlink" title="给坐标轴加刻度"></a>给坐标轴加刻度</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span> (1,0)node<span class="token punctuation">[</span>below<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$1$</span><span class="token punctuation">}</span>--(1,0.1);<span class="token function selector">\draw</span> (0,1)node<span class="token punctuation">[</span>left<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$1$</span><span class="token punctuation">}</span>--(0.1,1);<span class="token function selector">\foreach</span> <span class="token function selector">\x</span> in <span class="token punctuation">{</span>1,2,...,6<span class="token punctuation">}</span><span class="token punctuation">{</span> <span class="token function selector">\draw</span> (0,<span class="token function selector">\x</span>)node<span class="token punctuation">[</span>left<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$<span class="token equation-command regex">\x</span>$</span><span class="token punctuation">}</span>--(.1,<span class="token function selector">\x</span>);<span class="token punctuation">}</span><span class="token function selector">\foreach</span> <span class="token function selector">\x</span> in <span class="token punctuation">{</span>1,2,...,6<span class="token punctuation">}</span><span class="token punctuation">{</span> <span class="token function selector">\draw</span> (0,<span class="token function selector">\x*</span>0.5)node<span class="token punctuation">[</span>left<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$<span class="token equation-command regex">\x</span>$</span><span class="token punctuation">}</span>--(.1,<span class="token function selector">\x*</span>0.5); <span class="token function selector">\draw</span> (<span class="token function selector">\x*</span>0.5,0)node<span class="token punctuation">[</span>below<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$<span class="token equation-command regex">\x</span>$</span><span class="token punctuation">}</span>--(<span class="token function selector">\x*</span>0.5,.1);<span class="token punctuation">}</span> <span class="token comment">% 更精细的刻度</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="绘制几何图形"><a href="#绘制几何图形" class="headerlink" title="绘制几何图形"></a>绘制几何图形</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token comment">% 绘制菱形并填充</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>fill=magenta<span class="token punctuation">]</span> (-4,0)node<span class="token punctuation">[</span>above<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$C$</span><span class="token punctuation">}</span>--(0,3)node<span class="token punctuation">[</span>right<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$B$</span><span class="token punctuation">}</span>--(4,0)node<span class="token punctuation">[</span>above<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$A$</span><span class="token punctuation">}</span>--(0,-3)node<span class="token punctuation">[</span>right<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$D$</span><span class="token punctuation">}</span>--(-4,0);<span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span>(-5,0)--(5,0)node<span class="token punctuation">[</span>right<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$x$</span><span class="token punctuation">}</span>;<span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span>(0,-5)--(0,5)node<span class="token punctuation">[</span>right<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$y$</span><span class="token punctuation">}</span>;<span class="token function selector">\node</span> at (-0.3,-0.3)<span class="token punctuation">{</span><span class="token equation string">$O$</span><span class="token punctuation">}</span>; <span class="token comment">% 先画图再画坐标轴来防止遮挡</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="画矩形"><a href="#画矩形" class="headerlink" title="画矩形"></a>画矩形</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span> (0,0) rectangle (4,3); <span class="token comment">% (0,0) 和 (4,3) 是对角线两点</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>fill=cyan<span class="token punctuation">]</span>(0,0) rectangle (4,3); <span class="token comment">% 这样会有黑色边框</span><span class="token function selector">\fill</span> <span class="token punctuation">[</span>cyan<span class="token punctuation">]</span> (0,-5) rectangle (4,-2); <span class="token comment">% 这样没有边框</span><span class="token function selector">\fill</span> <span class="token punctuation">[</span>cyan,draw=black<span class="token punctuation">]</span> (0,-5) rectangle (4,-2); <span class="token comment">% 可以设置边框颜色</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h3 id="使用极坐标"><a href="#使用极坐标" class="headerlink" title="使用极坐标"></a>使用极坐标</h3><p>$(\theta:r)$,$\theta$ 是角度制下的角度,r 是与原点的距离</p><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span> (0,0)--(45:4);<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="画圆形和圆弧"><a href="#画圆形和圆弧" class="headerlink" title="画圆形和圆弧"></a>画圆形和圆弧</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\fill</span> <span class="token punctuation">[</span>cyan<span class="token punctuation">]</span> (0,0) circle (1.5); <span class="token comment">% (0,0) 是圆心,1.5 是半径</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span> (2,0) arc (0:45:2); <span class="token comment">% (2,0) 是起点,0 是起始角度,45 是终止角度,2 是半径</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="打阴影"><a href="#打阴影" class="headerlink" title="打阴影"></a>打阴影</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token comment">% 引言部分</span><span class="token function selector">\usetikzlibrary</span><span class="token punctuation">{</span>patterns<span class="token punctuation">}</span><span class="token function selector">\draw</span><span class="token punctuation">[</span>pattern=north east lines<span class="token punctuation">]</span>(8,8) rectangle (10,10);<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="画函数图像"><a href="#画函数图像" class="headerlink" title="画函数图像"></a>画函数图像</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span> <span class="token punctuation">[</span>domain=-4:4, samples=1000, thick<span class="token punctuation">]</span> <span class="token comment">% 定义域,取点的个数</span> plot(<span class="token function selector">\x</span>, <span class="token punctuation">{</span>0.5*<span class="token function selector">\x*</span><span class="token function selector">\x</span><span class="token punctuation">}</span>); <span class="token comment">% y 要加大括号</span> <span class="token function selector">\node</span> at (4.5,6)<span class="token punctuation">{</span><span class="token equation string">$y=<span class="token equation-command regex">\frac</span>{1}{2}x^2$</span><span class="token punctuation">}</span>;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="画三角函数图像"><a href="#画三角函数图像" class="headerlink" title="画三角函数图像"></a>画三角函数图像</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span><span class="token punctuation">[</span>domain=-pi/6:13*pi/6,samples=1000,dashed<span class="token punctuation">]</span>plot(<span class="token function selector">\x</span>,<span class="token punctuation">{</span>sin(<span class="token function selector">\x</span> r)<span class="token punctuation">}</span>); <span class="token comment">% 加一个 r 是改成弧度制</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="画框图"><a href="#画框图" class="headerlink" title="画框图"></a>画框图</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\node</span> (s1) at (0,0)<span class="token punctuation">[</span>rectangle,draw=blue!50,fill=blue!20<span class="token punctuation">]</span><span class="token punctuation">{</span>均方收敛<span class="token punctuation">}</span>;<span class="token comment">% rectangle 可以改成 circle</span><span class="token function selector">\node</span> (s2) at (4,0)<span class="token punctuation">[</span>rectangle,draw=blue!50,fill=blue!20<span class="token punctuation">]</span><span class="token punctuation">{</span>以概率 1 收敛<span class="token punctuation">}</span>;<span class="token function selector">\node</span> (s3) at (2,-2)<span class="token punctuation">[</span>rectangle,draw=blue!50,fill=blue!20<span class="token punctuation">]</span><span class="token punctuation">{</span>依概率收敛<span class="token punctuation">}</span>;<span class="token function selector">\node</span> (s4) at (2,-4)<span class="token punctuation">[</span>rectangle,draw=blue!50,fill=blue!20<span class="token punctuation">]</span><span class="token punctuation">{</span>依分布收敛<span class="token punctuation">}</span>;<span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span>(s1)--(s3);<span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span>(s2)--(s3);<span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span>(s3)--(s4);<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="在循环中创建节点"><a href="#在循环中创建节点" class="headerlink" title="在循环中创建节点"></a>在循环中创建节点</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\foreach</span> <span class="token function selector">\x</span>/<span class="token function selector">\xtext</span> in <span class="token punctuation">{</span>0/0,1/1,2/2,3/<span class="token function selector">\cdots</span>,4/s,5/<span class="token function selector">\cdots</span><span class="token punctuation">}</span><span class="token punctuation">{</span> <span class="token function selector">\node</span> (s<span class="token function selector">\x</span>) at (<span class="token function selector">\x</span>,0) <span class="token punctuation">{</span><span class="token equation string">$<span class="token equation-command regex">\xtext</span>$</span><span class="token punctuation">}</span>;<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h3 id="画弯曲的箭头"><a href="#画弯曲的箭头" class="headerlink" title="画弯曲的箭头"></a>画弯曲的箭头</h3><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\draw</span><span class="token punctuation">[</span>-><span class="token punctuation">]</span>(s0)to <span class="token punctuation">[</span>bend left=25<span class="token punctuation">]</span> node<span class="token punctuation">[</span>above<span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token equation string">$<span class="token equation-command regex">\lambda</span>$</span><span class="token punctuation">}</span>(s1); <span class="token comment">% 25 是角度,可正可负</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="circuitikz-画电路图"><a href="#circuitikz-画电路图" class="headerlink" title="circuitikz 画电路图"></a>circuitikz 画电路图</h2><p>与 tikz 一样,使用 circuitikz 也要先输入 bfi 来快捷创建图片环境,然后在图片环境中 <code>\begin{circuitikz}</code></p><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\begin</span><span class="token punctuation">{</span><span class="token keyword">circuitikz</span><span class="token punctuation">}</span><span class="token punctuation">[</span>european<span class="token punctuation">]</span> <span class="token function selector">\draw</span> (0,4) to <span class="token punctuation">[</span>battery2<span class="token punctuation">]</span> (0,0)--(3,0) to <span class="token punctuation">[</span>cute open switch<span class="token punctuation">]</span> (3,2) to <span class="token punctuation">[</span>R=<span class="token equation string">$R$</span><span class="token punctuation">]</span>(3,4)--(0,4); <span class="token comment">% 起点 to [元件] 终点</span> <span class="token comment">% t=mV 表示电表上的字;*-*表示给导线的连接点加黑点</span> <span class="token function selector">\draw</span> (1.5,0) to <span class="token punctuation">[</span>rmeter,t=mV,*-*<span class="token punctuation">]</span>(1.5,4);<span class="token function selector">\end</span><span class="token punctuation">{</span><span class="token keyword">circuitikz</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="缩放"><a href="#缩放" class="headerlink" title="缩放"></a>缩放</h2><pre class="line-numbers language-latex" data-language="latex"><code class="language-latex"><span class="token function selector">\begin</span><span class="token punctuation">{</span><span class="token keyword">tikzpicture</span><span class="token punctuation">}</span><span class="token punctuation">[</span>>=latex,scale=0.5<span class="token punctuation">]</span> <span class="token comment">% scale=0.5 可以缩小整个图,但是文字不会缩放</span> <span class="token comment">% 绘图命令</span><span class="token function selector">\end</span><span class="token punctuation">{</span><span class="token keyword">tikzpicture</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> TikZ </tag>
<tag> LaTex </tag>
</tags>
</entry>
<entry>
<title>Linux 入门教程</title>
<link href="/2025/07/14/2025-07-14-linux-tutorial/"/>
<url>/2025/07/14/2025-07-14-linux-tutorial/</url>
<content type="html"><![CDATA[<h1 id="Linux-入门教程"><a href="#Linux-入门教程" class="headerlink" title="Linux 入门教程"></a>Linux 入门教程</h1><h1 id="Linux"><a href="#Linux" class="headerlink" title="Linux"></a>Linux</h1><h2 id="Shell的简要介绍"><a href="#Shell的简要介绍" class="headerlink" title="Shell的简要介绍"></a>Shell的简要介绍</h2><p>操作系统主要负责系统内存管理、软件程序管理、硬件设备管理和文件系统管理。在用户与操作系统之间需要应用程序作为桥梁,帮助用户与操作系统进行交互,实现各种功能。<br> 应用程序与用户的交互有两种表现形式,一种是我们常见的 GUI(Graphical User Interface),即图形用户界面,可以用鼠标点击操作;另一种是 CLI(Command Line Interface),即命令行界面。<br> Terminal,终端,是人与计算机进行交互的接口。一台大型机可以连接很多终端机,用户在这些终端机上跟计算机进行交互,这些早期终端一般是电传打字机(TTY)。随着个人计算机的普及和进化,大型机时代的专门的终端硬件早已消失,我们只用自己的键盘、鼠标来控制计算机。但是终端的思想保留了下来,变成了操作系统中的软件——终端模拟器(如Win32控制台)。终端模拟器可以捕获你的键盘输入,发送至命令行程序,拿到输出结果后再调用图形接口,将输出渲染至屏幕上。由于人们除了在博物馆再也见不到TTY这样的终端硬件了,于是就直接称终端模拟器为终端。<br> Shell,即“壳层”,是帮助用户与内核交互的一类程序。操作系统内核直接负责管理计算机硬件,处于操作系统最底层,由于其重要性是不能轻易由用户直接操作的,因此需要有Shell程序来作为用户操作系统的入口。Shell是一类程序,可以有CLI形式的(如sh、bash、zsh;cmd、powershell等),也可以有GUI形式的(如Windows资源管理器explorer.exe等)。<br> 用户所看见的界面通常是终端,在用户用键盘和鼠标输入后,终端(Terminal)将这些输入发送给你选用的壳层(Shell),Shell解析你的命令发送给操作系统内核去执行,并把执行结果返回给终端,终端调用图形接口将结果显示到屏幕上。 </p><h2 id="Linux系统目录结构"><a href="#Linux系统目录结构" class="headerlink" title="Linux系统目录结构"></a>Linux系统目录结构</h2><h3 id="系统文件"><a href="#系统文件" class="headerlink" title="系统文件"></a>系统文件</h3><p><code>/boot</code>存放着启动 Linux 时使用的内核文件,包括连接文件以及镜像文件。<br><code>/etc</code>存放所有的系统需要的配置文件和子目录列表。<br><code>/lib</code>存放基本代码库(比如 c++库),几乎所有的应用程序都需要用到这些共享库。<br><code>/sys</code> 该目录下安装了 Linux2.6 内核中新出现的一个文件系统 sysfs ,当一个内核对象被创建的时候,对应的文件和目录也在内核对象子系统中。</p><h3 id="指令-程序(文件)"><a href="#指令-程序(文件)" class="headerlink" title="指令/程序(文件)"></a>指令/程序(文件)</h3><p><code>/bin</code>存放着最常用的程序和指令。<br><code>/sbin</code>只有系统管理员能使用的程序和指令。</p><h3 id="外部设备(文件)"><a href="#外部设备(文件)" class="headerlink" title="外部设备(文件)"></a>外部设备(文件)</h3><p><code>/dev</code>Device 的缩写,存放的是 Linux 的外部设备。</p><blockquote><p>[!NOTE]<br>在 Linux 中访问设备和访问文件的方式是相同</p></blockquote><p><code>/media</code>类 windows 的**其他设备,**例如 U 盘、光驱等等,识别后 linux 会把设备放到这个目录下。<br><code>/mnt</code>用于临时挂载别的文件系统.</p><h3 id="临时文件"><a href="#临时文件" class="headerlink" title="临时文件"></a>临时文件</h3><p><code>/run</code>是一个临时文件系统,存储系统启动以来的信息。<br><code>/lost+found</code>一般情况下为空的,系统非法关机后,这里就存放一些文件。<br><code>/tmp</code>这个目录是用来存放一些临时文件的。</p><h3 id="账户文件"><a href="#账户文件" class="headerlink" title="账户文件"></a>账户文件</h3><p><code>/root</code>系统管理员的用户主目录。<br><code>/home</code>用户的主目录,以用户的账号命名。<br><code>/usr</code>用户的很多应用程序和文件都放在这个目录下,类似于 Windows 下的 program files 目录。<br><code>/usr/bin</code>系统用户使用的应用程序与指令。<br><code>/usr/sbin</code>超级用户使用的比较高级的管理程序和系统守护程序。<br><code>/usr/src</code>内核源代码默认的放置目录。</p><h3 id="程序运行中文件"><a href="#程序运行中文件" class="headerlink" title="程序运行中文件"></a>程序运行中文件</h3><p><code>/var</code>存放经常修改的数据,比如程序运行的日志文件(/var/log 目录下)。<br><code>/proc</code>管理内存空间虚拟的目录,是系统内存的映射,我们可以直接访问这个目录来获取系统信息。这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件来做修改。 </p><h2 id="Linux文件操作"><a href="#Linux文件操作" class="headerlink" title="Linux文件操作"></a>Linux文件操作</h2><h3 id="文件绝对路径"><a href="#文件绝对路径" class="headerlink" title="文件绝对路径"></a>文件绝对路径</h3><p>在Windows中,会见到这样的文件路径:<code>C:\Windows\System32\drivers\etc\hosts</code>,它指向位于C盘中,Windows\System32\drivers\etc\文件夹下的hosts文件<br> 而在Linux中,文件系统为树状结构,文件路径中并没有驱动器盘符,而是以虚拟目录的形式,将计算机所有的存储设备中的文件路径纳入统一的单一目录下,该目录称之为根目录。 </p><h3 id="ls命令"><a href="#ls命令" class="headerlink" title="ls命令"></a>ls命令</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">ls</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>directory name<span class="token punctuation">]</span>列出directory name下的文件和目录,按照字母排序options:<span class="token parameter variable">-a</span> 显示所有文件及目录<span class="token punctuation">(</span>“.”开头的隐藏文件也会列出<span class="token punctuation">)</span><span class="token parameter variable">-l</span> 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出<span class="token punctuation">(</span>详见<span class="token punctuation">[</span>权限部分<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token comment">#ls))</span><span class="token parameter variable">-r</span> 将文件以相反次序显示<span class="token punctuation">(</span>原定依英文字母次序<span class="token punctuation">)</span><span class="token parameter variable">-t</span> 将文件依建立时间之先后次序列出<span class="token parameter variable">-A</span> 同 <span class="token parameter variable">-a</span> ,但不列出 <span class="token string">"."</span> <span class="token punctuation">(</span>目前目录<span class="token punctuation">)</span> 及 <span class="token string">".."</span> <span class="token punctuation">(</span>父目录<span class="token punctuation">)</span><span class="token parameter variable">-F</span> 在列出的文件名称后加一符号;例如可执行档则加 <span class="token string">"*"</span>, 目录则加 <span class="token string">"/"</span><span class="token parameter variable">-R</span> 若目录下有文件,则以下之文件亦皆依序列出 <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在控制台中输入ls /,可以看到根目录下有许多子目录.在虚拟目录支持下,Linux下文件路径如下: <code>/etc/apt/sources.list</code>,它指向<code>/etc/apt/</code>文件夹下的sources.list,这个文件是apt的更新源配置文件,通常情况下需要将该文件配置为国内源,以加快Ubuntu系统的更新速度 </p><p>例如,设置成清华镜像站</p><pre class="line-numbers language-none"><code class="language-none"># 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble main restricted universe multiverse# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble main restricted universe multiversedeb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble-updates main restricted universe multiverse# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble-updates main restricted universe multiversedeb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble-backports main restricted universe multiverse# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble-backports main restricted universe multiverse# 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换deb http://security.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse# deb-src http://security.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse# 预发布软件源,不建议启用# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble-proposed main restricted universe multiverse# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ noble-proposed main restricted universe multiverse<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>[!NOTE]</p><p>设置之前注意备份</p></blockquote><h3 id="其他文件路径"><a href="#其他文件路径" class="headerlink" title="其他文件路径"></a>其他文件路径</h3><p>上面讲到了文件的绝对路径,在实际使用中还常常用到相对路径,除目录分隔符不同外,相对路径语法全系统通用,<code>.</code>表示当前目录,<code>..</code>表示父目录。考虑当前目录为<code>/etc/apt/</code>,则<code>./sources.list=/etc/apt/sources.list</code>,<code>../hosts=/etc/hosts</code>。<br>除此之外,当前用户的个人文件夹(通常为/home/[username]/)可以用特殊字符<code>~</code>表示。</p><p><code>cd [path]</code>指令修改当前目录到path指定的位置,无参数时path默认值为~。使用TAB键可以自动补全目录名称。<br><code>pwd</code>指令可以打印当前目录的绝对路径。<br><code>which [command]</code>指令可以打印命令对应可执行文件的路径 </p><h3 id="目录与文件操作"><a href="#目录与文件操作" class="headerlink" title="目录与文件操作"></a>目录与文件操作</h3><h4 id="创建操作"><a href="#创建操作" class="headerlink" title="创建操作"></a>创建操作</h4><p><code>mkdir [-p] [directory name]</code>: 创建目录,-p指令可以自动创建缺失的父文件夹。<br><code>touch [file name]</code>: 创建空文件,如果已经存在,则更新文件修改时间为当前时间。</p><h4 id="复制与移动操作"><a href="#复制与移动操作" class="headerlink" title="复制与移动操作"></a>复制与移动操作</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">cp</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>source<span class="token punctuation">]</span> <span class="token punctuation">[</span>destination<span class="token punctuation">]</span>复制source到destinationoptions:-f: 冲突时强制覆盖目标-n: 冲突时不覆盖目标-u: 冲突时覆盖修改时间较旧的目标-i: 发生冲突时询问是否覆盖-r: 递归复制<span class="token punctuation">(</span>用于复制目录<span class="token punctuation">)</span>-s: 创建符号链接<span class="token punctuation">(</span>可以理解为快捷方式<span class="token punctuation">)</span>而非复制<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>-n比较安全 </p><p><code>mv</code>指令用于移动,[options]与cp指令类似,当然除-s外,文件都移动走了创建符号链接也没什么意义。</p><h4 id="删除操作"><a href="#删除操作" class="headerlink" title="删除操作"></a>删除操作</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">rm</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>target<span class="token punctuation">]</span>: 删除targetoptions:-f:不需要确认,直接删除-i:系统会在删除每个文件之前提示用户确认是否真的要删除该文件-r:用于删除文件夹-v: 详细报告删除过程--:表示参数结束,之后的所有参数都被视为文件名,即使它们以 - 开头。这对于删除名称以 - 开头的文件非常有用。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>[!TIP]</p><p>多个命令选项可以合写,如rm -rf=rm -r -f</p></blockquote><blockquote><p>[!WARNING]<br>shell中没有回收站,文件一旦删除将无法恢复!谨慎使用rm -rf!</p></blockquote><h4 id="查找操作"><a href="#查找操作" class="headerlink" title="查找操作"></a>查找操作</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">find</span> <span class="token punctuation">[</span>directories<span class="token punctuation">]</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span>在指定目录下查找符合要求的文件options:<span class="token parameter variable">-name</span> <span class="token punctuation">[</span>name<span class="token punctuation">]</span>: 文件名查找,支持通配符,-iname会忽略大小写<span class="token parameter variable">-type</span> <span class="token punctuation">[</span>type<span class="token punctuation">]</span>: 按文件类型查找,f<span class="token punctuation">(</span>普通文件<span class="token punctuation">)</span>,d<span class="token punctuation">(</span>目录<span class="token punctuation">)</span>等<span class="token parameter variable">-path</span> <span class="token punctuation">[</span>p<span class="token punctuation">]</span>: 路径符合p的文件<span class="token parameter variable">-amin</span> <span class="token punctuation">[</span>-n<span class="token punctuation">]</span>: 在过去n分钟内被读取过的文件<span class="token parameter variable">-size</span> <span class="token punctuation">[</span>+nM<span class="token punctuation">]</span>: 大小大于nM的文件 <span class="token parameter variable">-size</span> <span class="token punctuation">[</span>-nM<span class="token punctuation">]</span>: 大小小于nM的文件 <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="文件显示与编辑"><a href="#文件显示与编辑" class="headerlink" title="文件显示与编辑"></a>文件显示与编辑</h3><p> 在shell中,显示文件通常使用cat、more和less指令。<code>cat [filename]</code>会将文件内所有内容打印到控制台,在云服务器和管道处理场景下更加便捷。而more和less有着更优秀的互动机制,在日常本机使用中更加推荐使用less。<br> vim有三种模式: 命令模式、输入模式和底线命令模式。使用vim指令打开文件时默认进入命令模式,在该模式下可以输入命令,如:输入i将切换到输入模式,对文件进行编辑;输入:将切换到底线命令模式,该模式下输入wq保存并退出,输入q!放弃修改并退出。在其他两种模式下输入Esc将回到命令模式。 </p><h3 id="vim的使用"><a href="#vim的使用" class="headerlink" title="vim的使用"></a>vim的使用</h3><p><code>xdg-open</code> 找到合适的程序打开一个文件</p><p>在vim里面,在normal模式下,</p><p>按<code>i</code>进入插入模式</p><p>按<code>R</code>进入替换模式</p><p>按<code>V</code>进入查看模式</p><p>按<code>Shift</code>+<code>V</code>进入逐行查看模式</p><p><code>F</code>+<code>字母</code>光标跳转到第一个出现这个字母的地方</p><p><code>x</code>删除字母</p><p><code>A</code>追加字母</p><p><code>d</code>+<code>$</code>删除到行尾</p><p><code>dd</code>删除整行</p><p><code>w</code>下一个单词开始</p><p><code>b</code>上一个单词</p><p><code>3w</code>走三个单词</p><p><code>e</code>这个单词结束</p><p><code>0</code>回到开头</p><p><code>Ctrl</code>+<code>R</code>重做</p><p><code>u</code>撤销</p><p><code>U</code>撤销整行</p><p><code>p</code>粘贴刚刚删掉的行</p><p><code>r</code>+<code>任意字符</code>将被光标选中的替换为<code>任意字符</code></p><p><code>ce</code>删除并替换单词</p><p><code>c$</code>删除并替换到行末</p><p><code>Ctrl</code>+<code>G</code>查看当前位置</p><p><code>G</code>到文章结尾</p><p><code>gg</code>到文章开头</p><p><code>数字</code>+<code>G</code>到<code>数字</code>那行</p><p><code>/+字符串</code>查找,<code>n</code>找到的下一个,<code>N</code>找到的上一个,<code>?+字符串</code>从末尾开始查找</p><p>光标在(,[,{上时按<code>%</code>跳转到和它配对的那个</p><p>替换:<code>:s/thee/the</code>把光标之后的第一个<code>thee</code>变成<code>the</code></p><p><code>:s/thee/the/g</code>把光标那一行的<code>thee</code>变成<code>the</code></p><p><code>1,3s/thee/the/g</code>1-33行的替换</p><p><code>%s/thee/the/g</code>全文的都替换</p><p><code>%s/thee/the/gc</code>全文每一个都询问是否替换</p><p>vim中运行命令:<code>:!+命令</code></p><p><code>:w TEST</code>把当前文件存入TEST</p><p><code>v</code>进入选择模式,选中后按<code>:</code>,<code>w</code>+<code>文件名</code>把选中部分写入文件</p><p><code>:r +文件名或者!指令</code>可以把内容读进来</p><p><code>R</code>进入替换模式</p><p><code>y</code>复制选中的</p><p><code>p</code>粘贴</p><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token list punctuation">1.</span> 输入小写的 o 可以在光标下方打开新的一行并进入插入模式。输入大写的 O 可以在光标上方打开新的一行。<span class="token list punctuation">2.</span> 输入小写的 a 可以在光标所在位置之后插入文本。 输入大写的 A 可以在光标所在行的行末之后插入文本。<span class="token list punctuation">3.</span> e 命令可以使光标移动到单词末尾。<span class="token list punctuation">4.</span> 操作符 y 复制文本,p 粘贴先前复制的文本。<span class="token list punctuation">5.</span> 输入大写的 R 将进入替换模式,直至按 <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ESC</span><span class="token punctuation">></span></span> 键回到正常模式。<span class="token list punctuation">6.</span> 输入 :set xxx 可以设置 xxx 选项。一些有用的选项如下:'ic' 'ignorecase'查找时忽略字母大小写'is' 'incsearch'查找短语时显示部分匹配'hls' 'hlsearch'高亮显示所有的匹配短语选项名可以用完整版本,也可以用缩略版本。<span class="token list punctuation">7.</span> 在选项前加上 no 可以关闭选项::set noic<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>补全功能:</p><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token list punctuation">1.</span> 请确保 Vim 不是在以兼容模式运行: :set nocp<span class="token list punctuation">2.</span> 查看一下当前目录下已经存在哪些文件,输入: :!ls 或者 :!dir<span class="token list punctuation">3.</span> 现在输入一个目录的起始部分,例如输入: :e<span class="token list punctuation">4.</span> 接着按 CTRL-D 键,Vim 会显示以 e 开始的命令的列表。<span class="token list punctuation">5.</span> 然后按 <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TAB</span><span class="token punctuation">></span></span> 键,Vim 会补全命令为 :edit 。<span class="token list punctuation">6.</span> 现在添加一个空格,以及一个已有文件的文件名的起始部分,例如: :edit FIL<span class="token list punctuation">7.</span> 接着按 <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TAB</span><span class="token punctuation">></span></span> 键,Vim 会补全文件名(如果它是惟一匹配的)。提示:补全对于许多命令都有效。只需尝试按 CTRL-D 和 <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TAB</span><span class="token punctuation">></span></span>。它对于 :help 命令非常有用。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="Linux用户管理"><a href="#Linux用户管理" class="headerlink" title="Linux用户管理"></a>Linux用户管理</h2><p>Linux是一个多用户系统,在多用户同时使用服务器时这一点体现尤为明显。每个用户账号都拥有一个唯一的用户名和密码。用户在登录时键入正确的用户名和密码后,会进入自己的主目录。<br> 每个用户都有独立的文件访问权限。为了方便管理,可以将共享部分权限的用户分为一组,用户未特别指定的权限继承所在用户组权限。每个用户的UID和用户组的GID都是唯一的。一般情况下,Linux中有一个超级用户root,该用户拥有最高权限。</p><h3 id="用root权限执行命令"><a href="#用root权限执行命令" class="headerlink" title="用root权限执行命令"></a>用root权限执行命令</h3><p> 对用户和用户组的操作都是敏感操作,本小节用到的所有指令都无法在用户正常权限下执行,需要保证当前用户在sudoer组内(一般情况下均满足)并使用root权限执行命令。命令格式为:sudo [command]。输入该命令后需要输入用户密码以进一步操作。如果觉得sudo过于繁琐,也可以直接登录root进行操作,不过请谨慎使用root权限以避免系统的损坏。<br> 应当注意的是,用户需要至少在sudo组(或admin组)中才可以使用sudo命令。</p><h3 id="用户操作"><a href="#用户操作" class="headerlink" title="用户操作"></a>用户操作</h3><h4 id="su"><a href="#su" class="headerlink" title="su"></a>su</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">su</span> <span class="token punctuation">[</span>username<span class="token punctuation">]</span>: 切换用户<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="useradd"><a href="#useradd" class="headerlink" title="useradd"></a>useradd</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">useradd</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>username<span class="token punctuation">]</span>: 创建用户options:-m: 创建用户主目录<span class="token punctuation">(</span>配合-k <span class="token punctuation">[</span>path<span class="token punctuation">]</span>指定创建模板<span class="token punctuation">)</span><span class="token parameter variable">-d</span> <span class="token punctuation">[</span>dir<span class="token punctuation">]</span>: 指定用户主目录位置<span class="token parameter variable">-e</span> <span class="token punctuation">[</span>YYYY-MM-DD<span class="token punctuation">]</span>: 指定到期时间<span class="token parameter variable">-g</span> <span class="token punctuation">[</span>GID/groupname<span class="token punctuation">]</span>: 指定用户登录组<span class="token parameter variable">-G</span> <span class="token punctuation">[</span>GIDs/groupnames<span class="token punctuation">]</span>: 指定用户所在的附加组<span class="token parameter variable">-p</span> <span class="token punctuation">[</span>password<span class="token punctuation">]</span>: 指定默认密码<span class="token parameter variable">-s</span> <span class="token punctuation">[</span>shell<span class="token punctuation">]</span>: 指定用户登录的shell<span class="token parameter variable">-c</span> <span class="token punctuation">[</span>comment<span class="token punctuation">]</span>: 指定用户备注-D: 查看默认设置<span class="token punctuation">(</span>单独使用<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="passwd"><a href="#passwd" class="headerlink" title="passwd"></a>passwd</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">passwd</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span>: 修改自己密码,需要旧密码<span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">passwd</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>username<span class="token punctuation">]</span>: 修改别人密码options:-l/-u: 锁定/解锁密码;-d: 删除密码-f: 下次登录时强制修改密码<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="usermod"><a href="#usermod" class="headerlink" title="usermod"></a>usermod</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">usermod</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> username修改用户属性,options和useradd类似如果需要让用户进入新的组,请使用-aG <span class="token punctuation">[</span>GIDs/groupnames<span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="userdel"><a href="#userdel" class="headerlink" title="userdel"></a>userdel</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">userdel</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> username删除用户,-r以删除用户主目录<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>在/etc/passwd文件中可以查看当前系统中的全部用户和属性,每行用户信息以:为分隔符,具体格式如下:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">username:password<span class="token punctuation">(</span>hidden<span class="token punctuation">)</span>:user id<span class="token punctuation">(</span><span class="token environment constant">UID</span><span class="token punctuation">)</span>:group id<span class="token punctuation">(</span>GID<span class="token punctuation">)</span>:comment:home path:default shell<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>使用(sudo) visudo以编辑/etc/sudoer,可以配置哪些用户(组)可以使用sudo指令。权限配置格式如下:</p><pre class="line-numbers language-none"><code class="language-none">[用户名] [允许登录的主机]=([可使用的用户身份]:[可使用的组]) [可使用的命令]例如%sudo ALL=(ALL:ALL) ALL<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>在这里还可以配置一些有趣的东西,比如添加Defaults pwfeedback可以让sudo密码输入时显示为星号。</p><h3 id="用户组操作"><a href="#用户组操作" class="headerlink" title="用户组操作"></a>用户组操作</h3><p> 一个组中可以有多个用户,一个用户也可以在多个组中。 一个用户同时是多个组的成员时,用户主组(登录组)在/etc/passwd中记录,而其他组(附加组)在/etc/group中记录。后者数据格式如下:</p><pre class="line-numbers language-none"><code class="language-none">groupname:password(通常为空):GID:UIDs(仅显示附加组成员)<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>有关用户组的操作,常用的有groupadd、groupmod、groupdel。 newgrp [group]可以切换当前活动用户的主组,加上-选项后切换将引起shell重加载。<br>groups [username]查看用户所在的所有组(主组第一个)</p><pre class="line-numbers language-none"><code class="language-none">gpasswd [options] group: 管理附加组options:-a/-d [username]: 添加/删除用户到/从指定附加组-M [usernames]: 为该附加组成员赋值<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h3 id="文件与用户-用户组权限"><a href="#文件与用户-用户组权限" class="headerlink" title="文件与用户/用户组权限"></a>文件与用户/用户组权限</h3><p> 在上面提到ls命令时,我们提了一嘴ls -l命令,现在我们来仔细查看ls -l返回的数据。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">drwxr-xr-x <span class="token number">2</span> dream admin <span class="token number">4096</span> Jul <span class="token number">19</span> <span class="token number">21</span>:05 <span class="token builtin class-name">test</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>第一段drwxr-xr-x表示文件类型和权限,d表示该文件为目录文件(l链接,-普通文件),<br>r(read)/m(modify)/x(execute)每三位一组,分别代表属主权限/属组权限/其他用户权限。<br>第二段2表示有两个硬链接(参见第一条注释)指向该文件对应的节点。第三、四段分别表示文件属主和属组,剩余部分为</p><p><code>[file size]</code> </p><p><code>[modify time]</code></p><p><code>[file name]</code></p><h4 id="chmod"><a href="#chmod" class="headerlink" title="chmod"></a>chmod</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">chmod</span> <span class="token punctuation">[</span>-R<span class="token punctuation">]</span> <span class="token punctuation">[</span>u/g/o/a<span class="token punctuation">]</span> <span class="token punctuation">[</span>+/-/<span class="token operator">=</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>r/w/x<span class="token punctuation">]</span> <span class="token punctuation">[</span>filename<span class="token punctuation">]</span>修改指定文件的权限。-R用于目录文件,递归修改每个子文件。u<span class="token punctuation">(</span>user<span class="token punctuation">)</span>/g<span class="token punctuation">(</span>group<span class="token punctuation">)</span>/o<span class="token punctuation">(</span>others<span class="token punctuation">)</span>/a<span class="token punctuation">(</span>all<span class="token punctuation">)</span>指定修改权限的对象。+/-/<span class="token operator">=</span>分别对应增加/减小/赋值权限。如果把rwx看作3位2进制数,则可以通过以下方式快速赋值:<span class="token function">chmod</span> <span class="token number">775</span> file,修改后权限变为rwxrwxr-x<span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">chown</span> <span class="token punctuation">[</span>-R<span class="token punctuation">]</span> <span class="token punctuation">[</span>owner<span class="token punctuation">]</span><span class="token punctuation">[</span>:group<span class="token punctuation">]</span> <span class="token punctuation">[</span>file<span class="token punctuation">]</span>: 修改文件属主<span class="token punctuation">(</span>及属组<span class="token punctuation">)</span><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">chgrp</span> <span class="token punctuation">[</span>-R<span class="token punctuation">]</span> <span class="token punctuation">[</span>group<span class="token punctuation">]</span> <span class="token punctuation">[</span>file<span class="token punctuation">]</span>: 仅修改属组<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="应用实例"><a href="#应用实例" class="headerlink" title="应用实例"></a>应用实例</h3><p>新建一个用户并赋予超级用户权限,指定/home/sub/[用户名]/为home目录,设置默认shell为bash,并设置在输入密码时显示星号 </p><p>首先执行</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">useradd</span> <span class="token parameter variable">-d</span> /home/sub/user <span class="token parameter variable">-s</span> /usr/bin/bash user1<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>然后执行命令</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> visudo<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>加入</p><pre class="line-numbers language-none"><code class="language-none">%user1 ALL=(ALL:ALL) ALLDefaults pwfeedback<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h2 id="Linux系统和磁盘管理"><a href="#Linux系统和磁盘管理" class="headerlink" title="Linux系统和磁盘管理"></a>Linux系统和磁盘管理</h2><h3 id="软件管理"><a href="#软件管理" class="headerlink" title="软件管理"></a>软件管理</h3><p>在Debian和Ubuntu发行版当中,apt是一个软件包管理工具。我们可以通过它方便地实现软件包的安装、更新、删除等操作。运行apt需要超级用户权限。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> install: 安装软件包<span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> update: 列出可更新的软件清单<span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> upgrade: 升级可升级的软件包<span class="token punctuation">(</span>缺省则为全部<span class="token punctuation">)</span><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> list --installed/--all-versions: 列出所有已安装软件包<span class="token punctuation">(</span>及版本信息<span class="token punctuation">)</span><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> show: 显示指定软件包具体信息<span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> search: 搜索软件包<span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> remove: 移除指定软件包<span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">apt</span> autoremove: 清除不再使用的依赖和库文件<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="系统管理"><a href="#系统管理" class="headerlink" title="系统管理"></a>系统管理</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">top</span> <span class="token punctuation">[</span>-d secs<span class="token punctuation">]</span> <span class="token punctuation">[</span>-n max<span class="token punctuation">]</span> <span class="token punctuation">[</span>-u user<span class="token punctuation">]</span>: 指定刷新率工作的进程CPU与内存占用显示界面<span class="token function">ps</span> <span class="token punctuation">[</span>-a<span class="token punctuation">]</span> <span class="token punctuation">[</span>-u<span class="token punctuation">]</span> <span class="token punctuation">[</span>-x<span class="token punctuation">]</span>: 列出<span class="token punctuation">(</span>全部用户<span class="token punctuation">)</span>的进程<span class="token punctuation">(</span>及详细信息<span class="token punctuation">)</span><span class="token punctuation">(</span>即使进程没有控制终端<span class="token punctuation">)</span><span class="token function">kill</span> <span class="token punctuation">[</span>-s signal<span class="token punctuation">]</span> <span class="token punctuation">[</span>-u username<span class="token punctuation">]</span>/PID: 向指定进程/指定用户所有进程发送信号<span class="token punctuation">(</span>默认为15-SIGTERM,也可为9-SIGKILL,可通过kill -l查看<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h3 id="磁盘管理"><a href="#磁盘管理" class="headerlink" title="磁盘管理"></a>磁盘管理</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">df</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>path<span class="token punctuation">]</span>: 列出path所在文件系统的整体磁盘使用量options:-h: 以GB/MB/KB等格式呈现,更易阅读-a: 列出所有的文件系统-T: 显示文件系统格式<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">du</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>path<span class="token punctuation">]</span>: 列出path的磁盘空间使用量options: -h/-a同df,-s对目录仅显示总大小而非递归显示<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">fdisk</span> <span class="token punctuation">[</span>-l<span class="token punctuation">]</span> <span class="token punctuation">[</span>device name<span class="token punctuation">]</span>: 磁盘分区工具<span class="token punctuation">(</span>根据引导即可<span class="token punctuation">)</span>,-l列出所有分区<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>sudo<span class="token punctuation">)</span> <span class="token function">mkfs</span> <span class="token punctuation">[</span>-t filesystem<span class="token punctuation">]</span> <span class="token punctuation">[</span>device name<span class="token punctuation">]</span>: 格式化为指定文件系统格式<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">mount</span> <span class="token punctuation">[</span>device name<span class="token punctuation">]</span> <span class="token punctuation">[</span>directory<span class="token punctuation">]</span>: 将文件系统挂载在指定目录下unmount <span class="token punctuation">[</span>device name or directory<span class="token punctuation">]</span>: 卸载已挂载的文件系统<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h2 id="Shell常用操作"><a href="#Shell常用操作" class="headerlink" title="Shell常用操作"></a>Shell常用操作</h2><h3 id="输入输出重定向"><a href="#输入输出重定向" class="headerlink" title="输入输出重定向"></a>输入输出重定向</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">command</span> <span class="token operator"><</span> file: 将文件内容作为命令输入<span class="token builtin class-name">command</span> <span class="token punctuation">[</span><span class="token number">1</span>/2/<span class="token operator">&</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token operator">></span>/<span class="token operator">>></span><span class="token punctuation">]</span> file: 将标准输出流/错误流/两者输出到文件,<span class="token operator">></span>会覆盖已有文件,<span class="token operator">>></span>会追加已有文件。如果不需要相关数据,file可以设为/dev/null<span class="token builtin class-name">command</span> <span class="token operator"><span class="token file-descriptor important">2</span>></span><span class="token file-descriptor important">&1</span>: 将标准错误流重定向到标准输出流<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h3 id="管道"><a href="#管道" class="headerlink" title="管道"></a>管道</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">command1 <span class="token operator">|</span> command2将command1的输出作为command2的输入<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="一些常用指令"><a href="#一些常用指令" class="headerlink" title="一些常用指令"></a>一些常用指令</h3><h4 id="grep"><a href="#grep" class="headerlink" title="grep"></a>grep</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">grep</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token string">'patterns'</span><span class="token builtin class-name">:</span> 从标准输入流过滤行options:-A/-B/-C <span class="token punctuation">[</span>n<span class="token punctuation">]</span>: 除匹配行外额外向后<span class="token punctuation">(</span>after<span class="token punctuation">)</span>/向前<span class="token punctuation">(</span>before<span class="token punctuation">)</span>/前后<span class="token punctuation">(</span>contest<span class="token punctuation">)</span>显示n行-c: 计算匹配行数-r: 递归查找目录-l: 列出匹配成功的文件名-E: 使用拓展的正则表达式-i: 忽略大小写-n: 标出行号-v: 反向选择<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="tr"><a href="#tr" class="headerlink" title="tr"></a>tr</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">tr</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> SET1 SET2: 将匹配SET1的字符修改为SET2options:-c: 反选字符集-d: 删除匹配字符而非修改-s: 缩减连续重复的字符成指定的单个字符-t: 削减SET1范围使SET1和SET2长度相等SET特殊值:CHAR1-CHAR2 :字符范围从 CHAR1 到 CHAR2 的指定,范围的指定以 ASCII 码的次序为基础,只能由小到大,不能由大到小。<span class="token punctuation">[</span>CHAR*<span class="token punctuation">]</span> :这是 SET2 专用的设定,功能是重复指定的字符到与 SET1 相同长度为止<span class="token punctuation">[</span>CHAR*REPEAT<span class="token punctuation">]</span> :这也是 SET2 专用的设定,功能是重复指定的字符到设定的 REPEAT 次数为止<span class="token punctuation">(</span>REPEAT 的数字采 <span class="token number">8</span> 进位制计算,以 <span class="token number">0</span> 为开始<span class="token punctuation">)</span><span class="token punctuation">[</span>:alnum:<span class="token punctuation">]</span> :所有字母字符与数字<span class="token punctuation">[</span>:alpha:<span class="token punctuation">]</span> :所有字母字符<span class="token punctuation">[</span>:blank:<span class="token punctuation">]</span> :所有水平空格<span class="token punctuation">[</span>:cntrl:<span class="token punctuation">]</span> :所有控制字符<span class="token punctuation">[</span>:digit:<span class="token punctuation">]</span> :所有数字<span class="token punctuation">[</span>:graph:<span class="token punctuation">]</span> :所有可打印的字符<span class="token punctuation">(</span>不包含空格符<span class="token punctuation">)</span><span class="token punctuation">[</span>:lower:<span class="token punctuation">]</span> :所有小写字母<span class="token punctuation">[</span>:print:<span class="token punctuation">]</span> :所有可打印的字符<span class="token punctuation">(</span>包含空格符<span class="token punctuation">)</span><span class="token punctuation">[</span>:punct:<span class="token punctuation">]</span> :所有标点字符<span class="token punctuation">[</span>:space:<span class="token punctuation">]</span> :所有水平与垂直空格符<span class="token punctuation">[</span>:upper:<span class="token punctuation">]</span> :所有大写字母<span class="token punctuation">[</span>:xdigit:<span class="token punctuation">]</span> :所有 <span class="token number">16</span> 进位制的数字例如: <span class="token function">cat</span> tfile <span class="token operator">|</span> <span class="token function">tr</span> a-z A-Z <span class="token operator">></span> tout,将tfile中小写字符替换为大写字符并输出到tout<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="bc"><a href="#bc" class="headerlink" title="bc"></a>bc</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">bc: 任意精度计算器,对标准输入的数学表达式进行计算。数学表达式中可以包含以下内容:<span class="token string">"scale=2;"</span><span class="token builtin class-name">:</span> 指定小数位为2位<span class="token string">"ibase=2;obase=16;"</span><span class="token builtin class-name">:</span> 指定输入为2进制数,输出为16进制数<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="ssh"><a href="#ssh" class="headerlink" title="ssh"></a>ssh</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">ssh</span> user@hostname <span class="token parameter variable">-p</span> port: 登录到SSH远程服务器<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>SSH = Secure Shell,即安全外壳协议,是为远程登录会话提供安全性的协议,一般用作远程登录到服务器 </p><h2 id="Shell脚本"><a href="#Shell脚本" class="headerlink" title="Shell脚本"></a>Shell脚本</h2><p>Shell脚本是一种在Shell中运行的脚本程序,由Shell作为脚本解释器,Shell脚本的一行就相当于一个命令,<br>也可以用分号分隔一行之中的不同命令。 脚本后缀名通常为*.sh,在脚本第一行,通常需要注明脚本解释器的路径,如#!/bin/bash。下面是一个小示例 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token comment"># 我是注释</span><span class="token assign-left variable">a</span><span class="token operator">=</span><span class="token number">1</span> <span class="token comment"># 定义变量,名称和值之间不能有空格</span><span class="token builtin class-name">echo</span> <span class="token variable">$a</span> <span class="token comment"># $用于引用变量,也可用于引用环境变量</span><span class="token builtin class-name">unset</span> a <span class="token comment"># 删除变量</span><span class="token assign-left variable">array</span><span class="token operator">=</span><span class="token punctuation">(</span><span class="token number">0</span> <span class="token number">1</span> <span class="token number">2</span> <span class="token number">3</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token comment"># 定义数组</span><span class="token builtin class-name">echo</span> <span class="token variable">${array<span class="token punctuation">[</span>0<span class="token punctuation">]</span>}</span> <span class="token comment"># 读取数组元素</span><span class="token builtin class-name">echo</span> <span class="token variable">${array<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span> <span class="token comment"># 读取数组所有元素</span><span class="token keyword">if</span> condition1 <span class="token keyword">then</span>command1<span class="token keyword">elif</span> condition2 <span class="token keyword">then</span>command2<span class="token keyword">else</span>commandN<span class="token keyword">fi</span><span class="token keyword">for</span> <span class="token for-or-select variable">var</span> <span class="token keyword">in</span> array<span class="token keyword">do</span><span class="token builtin class-name">command</span><span class="token keyword">done</span><span class="token comment"># 仿C风格</span><span class="token keyword">for</span> <span class="token variable"><span class="token punctuation">((</span> i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">))</span></span><span class="token keyword">do</span><span class="token builtin class-name">command</span><span class="token keyword">done</span><span class="token keyword">while</span> condition<span class="token keyword">do</span><span class="token builtin class-name">command</span> <span class="token comment"># 可以使用break, continue</span><span class="token keyword">done</span><span class="token keyword">function</span> <span class="token function-name function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token builtin class-name">echo</span> <span class="token string">"参数1: <span class="token variable">${1}</span>"</span> <span class="token comment"># 通过${n}获取传入的第n个参量</span><span class="token builtin class-name">return</span> <span class="token variable"><span class="token variable">$((</span> ${<span class="token number">1</span>}<span class="token operator">+</span>${<span class="token number">10</span>} <span class="token variable">))</span></span><span class="token punctuation">}</span><span class="token builtin class-name">test</span> <span class="token number">1</span> <span class="token number">2</span> <span class="token number">3</span><span class="token builtin class-name">echo</span> <span class="token variable">$?</span> <span class="token comment"># $?指代函数的返回值或上条指令的输出结果</span>./sub1.sh <span class="token comment"># 直接调用sub1.sh</span><span class="token function">bash</span> ./sub2.sh <span class="token comment"># 用bash作为解释器调用sub1.sh</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="if-else语句用法"><a href="#if-else语句用法" class="headerlink" title="if else语句用法"></a>if else语句用法</h3><h4 id="if-then-fi语句"><a href="#if-then-fi语句" class="headerlink" title="if [];then fi语句"></a>if [];then fi语句</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token assign-left variable">a</span><span class="token operator">=</span><span class="token variable">$1</span><span class="token assign-left variable">b</span><span class="token operator">=</span><span class="token variable">$2</span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token variable">$a</span> <span class="token operator">==</span> <span class="token variable">$b</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"a and b is equal"</span><span class="token keyword">fi</span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token variable">$a</span> <span class="token operator">!=</span> <span class="token variable">$b</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"a and b is not equal"</span><span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行命令<code>sh test.sh 2 3</code>给参数$1和$2赋值2和3,输出结果<code>a and b is not equal</code><br>不加else的if语句表达式成立执行then后面的语句,表达式不成立则不执行任何命令。</p><h4 id="if-then-else-fi语句"><a href="#if-then-else-fi语句" class="headerlink" title="if [];then else fi语句"></a>if [];then else fi语句</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token keyword">if</span> <span class="token punctuation">[</span> expression <span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">then</span> executed Statement_expression_true <span class="token keyword">else</span> executed Statement_expression_false<span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>[!NOTE]</p><p>expression表达式 和方括号<code>[ ]</code>之间必须有<strong>空格</strong>,否则会有语法错误</p></blockquote><p>如果表达式成立,then后面的语句将会被执行;如果表达式不成立则执行else后面的语句。</p><h4 id="if-then-elif-then-else-fi语句"><a href="#if-then-elif-then-else-fi语句" class="headerlink" title="if [];then elif []; then else fi语句"></a>if [];then elif []; then else fi语句</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token assign-left variable">a</span><span class="token operator">=</span><span class="token variable">$1</span><span class="token assign-left variable">b</span><span class="token operator">=</span><span class="token variable">$2</span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token variable">$a</span> <span class="token operator">==</span> <span class="token variable">$b</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"a and b is equal"</span><span class="token keyword">elif</span> <span class="token punctuation">[</span> <span class="token variable">$a</span> <span class="token parameter variable">-lt</span> <span class="token variable">$b</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"a less than b"</span><span class="token keyword">else</span> <span class="token builtin class-name">echo</span> <span class="token string">"a bigger than b"</span> <span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>哪个expression表达式成立则执行哪个then后面的语句,否则执行else后面的语句。</p><p>if … else 语句也经常与 test 命令结合使用,test 命令用于检查某个条件是否成立,与方括号<code>[ ]</code>功能类似</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token assign-left variable">a</span><span class="token operator">=</span><span class="token variable">$1</span><span class="token assign-left variable">b</span><span class="token operator">=</span><span class="token variable">$2</span><span class="token keyword">if</span> <span class="token builtin class-name">test</span> <span class="token variable">$a</span> <span class="token operator">==</span> <span class="token variable">$b</span><span class="token punctuation">;</span><span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"a and b is equal"</span><span class="token keyword">else</span> <span class="token builtin class-name">echo</span> <span class="token string">"a and b is not equal"</span> <span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>例如上述脚本,其中<code>if test $a == $b;</code>与<code>if [ $a == $b ];</code>效果一样。</p><h3 id="if语句常用命令选项"><a href="#if语句常用命令选项" class="headerlink" title="if语句常用命令选项"></a>if语句常用命令选项</h3><p><code>==</code> or <code>=</code>: 等于<br><code>-eq</code> : 等于<br><code>-ne</code> :不等于<br><code>-gt</code> :大于<br><code>-ge</code> :大于等于<br><code>-lt</code> :小于<br><code>-le</code> :小于等于</p><table><thead><tr><th>命令选项</th><th>描述</th></tr></thead><tbody><tr><td>if [ a = b ]</td><td>a与b是否相等 (a、b是数值)</td></tr><tr><td>if [ a -ge b ]</td><td>a 是否大于等于 b</td></tr><tr><td>if [ a -gt b ]</td><td>a 是否大于 b</td></tr><tr><td>if [ a -le b ]</td><td>a 是否小于等于 b</td></tr><tr><td>if [ a -lt b ]</td><td>a 是否小于 b</td></tr><tr><td>if [ a -ne b ]</td><td>a 是否不等于 b</td></tr><tr><td>if [ str1 = str2 ]</td><td>str1是否与str2相同(str1、str2是字符串)</td></tr><tr><td>if [ str1 != str2 ]</td><td>str1是否与str2不同</td></tr><tr><td>if [ str1 < str2 ]</td><td>str1是否小于str2</td></tr><tr><td>if [ str1 > str2 ]</td><td>str1是否大于str2</td></tr><tr><td>if [ -n str ]</td><td>判断str长度是否非零</td></tr><tr><td>if [ -z str ]</td><td>str长度是否为0</td></tr><tr><td>if [ -d file ]</td><td>判断file是否为一个目录</td></tr><tr><td>if [ -e file ]</td><td>判断file是否存在</td></tr><tr><td>if [ -f file ]</td><td>检查file文件是否存在</td></tr><tr><td>if [ -r file ]</td><td>判断file是否存在并可读</td></tr><tr><td>if [ -s file ]</td><td>判断file是否存在并非空</td></tr><tr><td>if [ -w file ]</td><td>判断file是否存在并可写</td></tr><tr><td>if [ -x file ]</td><td>判断file是否存在并可执行</td></tr></tbody></table><p>例如下面脚本判断压缩包文件是否存在</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash </span> <span class="token assign-left variable">Day</span><span class="token operator">=</span><span class="token variable"><span class="token variable">`</span><span class="token function">date</span> <span class="token parameter variable">-d</span> yesterday +%Y%m%d<span class="token variable">`</span></span> <span class="token assign-left variable">FILE</span><span class="token operator">=</span>access_<span class="token variable">${Day}</span>.log.tgz <span class="token assign-left variable">WORK_DIR</span><span class="token operator">=</span> /data/nginx/logs <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token parameter variable">-f</span> <span class="token variable">${WORK_DIR}</span>/<span class="token variable">${FILE}</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"OK"</span> <span class="token keyword">else</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">${FILE}</span> backup fail"</span> <span class="token operator">></span> error.log <span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>编写shell脚本,求出1到100的奇数和,并将结果重定向到result.txt </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token assign-left variable">result</span><span class="token operator">=</span><span class="token number">0</span><span class="token keyword">for</span> <span class="token variable"><span class="token punctuation">((</span>i<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span> i<span class="token operator"><=</span><span class="token number">100</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">))</span></span><span class="token keyword">do</span> <span class="token keyword">if</span> <span class="token variable"><span class="token punctuation">((</span>i <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">))</span></span><span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token assign-left variable">result</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$((</span>result <span class="token operator">+</span> i<span class="token variable">))</span></span> <span class="token keyword">fi</span><span class="token keyword">done</span><span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$result</span>"</span> <span class="token operator">>></span> result.txt<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="Shell环境变量"><a href="#Shell环境变量" class="headerlink" title="Shell环境变量"></a>Shell环境变量</h2><p>环境变量是存储有关 shell 的工作环境的数据,以便程序或 shell 中运行的脚本能够访问到它们。系统会创建好一些环境变量,我们在安装新程序等操作时也要留意是否需要设置环境变量,否则会出现”command not found”的错误。 </p><h3 id="查看环境变量"><a href="#查看环境变量" class="headerlink" title="查看环境变量"></a>查看环境变量</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">env</span> <span class="token comment"># 查看全部全局变量</span><span class="token function">printenv</span> <span class="token environment constant">HOME</span> <span class="token comment"># 查看个别全局变量(HOME)</span><span class="token builtin class-name">echo</span> <span class="token environment constant">$HOME</span> <span class="token comment"># 查看个别全局变量(HOME</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>常用环境变量:</p><ul><li>HOME: 当前用户主目录</li><li>PATH:shell查找命令的目录列表,由:分隔;</li><li>PWD:当前工作目录</li></ul><h3 id="设置环境变量"><a href="#设置环境变量" class="headerlink" title="设置环境变量"></a>设置环境变量</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token assign-left variable">a</span><span class="token operator">=</span><span class="token number">1</span><span class="token builtin class-name">export</span> a <span class="token comment"># 将子进程中变量公开给父进程(此处为shell)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>然而,这种设置是临时的,一旦父进程退出,变量就会消失。为了设置持久的环境变量,我们就需要修改系统启动时用于加载环境变量的文件。这些文件有不同功能 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">/etc/profile <span class="token comment"># 系统级,登录shell当中加载</span>/etc/bashrc <span class="token comment"># 系统级,可认为都会被加载</span>~/.profile <span class="token comment"># 用户级,登录shell当中加载</span>~/.bashrc <span class="token comment"># 用户级,可认为都会被加载</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>一般情况下只需要修改~/.bashrc即可。修改PATH时一般采用追加导出的方式,即在文件最后一行加上export PATH=[path to add]:$PATH。设置完成后,可以重启shell使设置生效,也可以使用source [file modified]重置当前shell全部环境变量 </p>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> Linux </tag>
</tags>
</entry>
<entry>
<title>电路基础与通信协议详解</title>
<link href="/2025/07/07/2025-07-07-circuit-basics-communication/"/>
<url>/2025/07/07/2025-07-07-circuit-basics-communication/</url>
<content type="html"><![CDATA[<h1 id="电路基础与通信协议详解"><a href="#电路基础与通信协议详解" class="headerlink" title="电路基础与通信协议详解"></a>电路基础与通信协议详解</h1><h2 id="一、电路结构和电路调试"><a href="#一、电路结构和电路调试" class="headerlink" title="一、电路结构和电路调试"></a>一、电路结构和电路调试</h2><h3 id="1-1-常见缩写"><a href="#1-1-常见缩写" class="headerlink" title="1.1 常见缩写"></a>1.1 常见缩写</h3><table><thead><tr><th>缩写</th><th>全称</th><th>说明</th></tr></thead><tbody><tr><td>GND</td><td>Ground</td><td>地,整个电路的参考点,决定了电压基准</td></tr><tr><td>VCC</td><td>Circuit</td><td>接入板子正向电源的电压</td></tr><tr><td>VDD</td><td>Device</td><td>集成 IC 的电压</td></tr><tr><td>VEE</td><td>Emitter</td><td>在电路指代电源负极</td></tr><tr><td>VSS</td><td>Series</td><td>公共连接点,约等于 GND</td></tr><tr><td>Vpp</td><td>Peak</td><td>电压峰峰值,最大值 - 最小值</td></tr><tr><td>VBAT</td><td>Battery</td><td>电源电压</td></tr></tbody></table><blockquote><p>[!NOTE]<br>直流电源分为单电源和双电源:</p><ul><li><strong>单电源</strong>:电压只有高于 GND 的部分</li><li><strong>双电源</strong>:既有正电压又有负电压</li></ul></blockquote><h3 id="1-2-电路结构"><a href="#1-2-电路结构" class="headerlink" title="1.2 电路结构"></a>1.2 电路结构</h3><h4 id="IO-口连接方式"><a href="#IO-口连接方式" class="headerlink" title="IO 口连接方式"></a>IO 口连接方式</h4><h5 id="杜邦线与排针"><a href="#杜邦线与排针" class="headerlink" title="杜邦线与排针"></a>杜邦线与排针</h5><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%9D%9C%E9%82%A6%E7%BA%BF.jpg" alt="杜邦线" style="zoom:150%;" />_杜邦线与排针_<ul><li>简单但是连接不可靠,且传输大电流发烫严重、损耗大</li><li>只适用于简单的东西</li></ul><h5 id="SMA-同轴线缆"><a href="#SMA-同轴线缆" class="headerlink" title="SMA(同轴线缆)"></a>SMA(同轴线缆)</h5><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedSMA.jpg" alt="SMA 连接器"><br><em>SMA 同轴线缆</em></p><ul><li>用于高频或对信号要求比较高的场景</li></ul><h5 id="FPC-连接器"><a href="#FPC-连接器" class="headerlink" title="FPC 连接器"></a>FPC 连接器</h5><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedFPC.jpg" alt="FPC 连接器"><br><em>FPC 连接器</em></p><ul><li>用于两块板子的连接,集成度高、简单、信号稳定性比较好</li></ul><h4 id="供电管理"><a href="#供电管理" class="headerlink" title="供电管理"></a>供电管理</h4><blockquote><p>[!NOTE]<br>通常板子的供电和芯片所需电压不一样</p></blockquote><h5 id="LDO-低压差线性稳压器"><a href="#LDO-低压差线性稳压器" class="headerlink" title="LDO(低压差线性稳压器)"></a>LDO(低压差线性稳压器)</h5><p>LDO 是低压差线性稳压器</p><ul><li><strong>优势</strong>:伺服电路结构简单、输出电压较稳定</li><li><strong>劣势</strong>:效率低,发热量大(压差损耗功率全部在 LDO 芯片上转化为热量),能够承载的功率低,一般只做降压电路</li></ul><h5 id="DC-DC-转换器"><a href="#DC-DC-转换器" class="headerlink" title="DC-DC 转换器"></a>DC-DC 转换器</h5><ul><li><strong>原理</strong>:通过快速开启和关闭开关元件(MOSFET 晶体管)输出 PWM 波来调节电压或电流的平均值</li><li><strong>优势</strong>:效率高,能承载较大电流</li><li><strong>劣势</strong>:<ol><li>电路结构复杂</li><li>有开关高频信号,干扰信号传输</li></ol></li></ul><blockquote><p>[!TIP]<br>电流较大一般用 DC-DC</p></blockquote><h3 id="1-3-电路调试"><a href="#1-3-电路调试" class="headerlink" title="1.3 电路调试"></a>1.3 电路调试</h3><h4 id="常用工具"><a href="#常用工具" class="headerlink" title="常用工具"></a>常用工具</h4><ol><li><strong>万用表</strong>:蜂鸣档在短路的时候会响</li><li><strong>信号发生器</strong>:产生测试信号</li><li><strong>学生电源</strong>:第一次上电把限流调的低一些,防止由于短路烧毁</li><li><strong>示波器</strong>:调试之前先确认电气连接</li></ol><h4 id="调试步骤"><a href="#调试步骤" class="headerlink" title="调试步骤"></a>调试步骤</h4><ol><li>先检查是否短路,蜂鸣档测 VCC 和 GND,显示 OL 说明没短路</li><li>测量时保证不要同时碰到芯片的两个角</li><li>触发电平在波形范围内才能让波形稳定显示</li></ol><h2 id="二、GPIO详解"><a href="#二、GPIO详解" class="headerlink" title="二、GPIO详解"></a>二、GPIO详解</h2><p><strong>GPIO</strong> = General Purpose Input Output(通用输入输出)</p><h3 id="2-1-输出模式"><a href="#2-1-输出模式" class="headerlink" title="2.1 输出模式"></a>2.1 输出模式</h3><h4 id="推挽输出"><a href="#推挽输出" class="headerlink" title="推挽输出"></a>推挽输出</h4><p>两个 MOS 管:强上拉强下拉,在高低两种电平下都具有驱动 (输出电流) 能力。但是驱动能力受芯片输出能力的限制</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E6%8E%A8%E6%8C%BD%E8%BE%93%E5%87%BA.png" alt="推挽输出"><br><em>推挽输出电路</em></p><h4 id="开漏输出"><a href="#开漏输出" class="headerlink" title="开漏输出"></a>开漏输出</h4><p>一个 MOS 管:强下拉,外部需要接上拉电阻,高电平输出能力由 VCC 提供,输出能力强</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined%E5%BC%80%E6%BC%8F%E8%BE%93%E5%87%BA.png" alt="开漏输出"><br><em>开漏输出电路</em></p><blockquote><p>[!TIP]<br>VCC 是天花板,GND 是地板,推挽输出看做一只手把物块推到上面或者拉到下面<br>上拉电阻类似弹簧,不拉就会收回去</p></blockquote><h3 id="2-2-输入模式"><a href="#2-2-输入模式" class="headerlink" title="2.2 输入模式"></a>2.2 输入模式</h3><h4 id="上拉输入"><a href="#上拉输入" class="headerlink" title="上拉输入"></a>上拉输入</h4><p>输入接上拉电阻,悬空时输入高电平,防止悬空时电压不可控(有效信号是低电平,常态高电平)</p><h4 id="下拉输入"><a href="#下拉输入" class="headerlink" title="下拉输入"></a>下拉输入</h4><p>与上拉输入相反(有效信号是高电平,常态低电平)</p><blockquote><p>例如按键有效电平是低电平,应该采用上拉输入</p></blockquote><h2 id="三、USB-和电源简介"><a href="#三、USB-和电源简介" class="headerlink" title="三、USB 和电源简介"></a>三、USB 和电源简介</h2><h3 id="3-1-USB-接口"><a href="#3-1-USB-接口" class="headerlink" title="3.1 USB 接口"></a>3.1 USB 接口</h3><p><strong>USB</strong> = Universal Serial Bus(通用串行总线)</p><ul><li><strong>串行</strong>:所有信号从一根线传输按照时间先后顺序传输数据,稳定性比并行的强一些</li><li><strong>总线</strong>:所有设备挂在总线上,可以减少对引脚资源的占用</li></ul><h4 id="USB-Type-A-2-0"><a href="#USB-Type-A-2-0" class="headerlink" title="USB Type-A(2.0)"></a>USB Type-A(2.0)</h4><p>四根线:</p><ul><li>VCC</li><li>GND</li><li>DP(数据正线)</li><li>DN(数据负线)</li></ul><p>差模信号的电压取决于 DP 和 DN 之间的电压(差分传输),防止环境噪声干扰。</p><blockquote><p>[!NOTE]<br>差分走线必须等长、等宽、紧密靠近、在同一层面</p></blockquote><h4 id="USB-Type-C"><a href="#USB-Type-C" class="headerlink" title="USB Type-C"></a>USB Type-C</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedtype-C.jpg" alt="USB Type-C"><br><em>USB Type-C 接口</em></p><h5 id="USB-C-引脚定义"><a href="#USB-C-引脚定义" class="headerlink" title="USB-C 引脚定义"></a>USB-C 引脚定义</h5><p>由于对称(除了 CC1、CC2)可以正负反着插</p><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedTYPEC.png" alt="USB-C 引脚"><br><em>USB-C 引脚定义</em></p><table><thead><tr><th>引脚</th><th>说明</th></tr></thead><tbody><tr><td>D+/D-</td><td>差分信号线,需要等长、等宽</td></tr><tr><td>RX/TX</td><td>串行总线信号线</td></tr><tr><td>VBUS</td><td>总线电压,为 USB-C 供电</td></tr><tr><td>CC1/CC2</td><td>PD 充电协议握手线</td></tr><tr><td>TH</td><td>外壳,接地即可</td></tr></tbody></table><h5 id="CC-引脚功能"><a href="#CC-引脚功能" class="headerlink" title="CC 引脚功能"></a>CC 引脚功能</h5><ul><li>确定是同向插进去还是反向插进去</li><li>确定供电和收电端</li><li>确定从机和主机能接受的电压</li></ul><h3 id="3-2-电源管理"><a href="#3-2-电源管理" class="headerlink" title="3.2 电源管理"></a>3.2 电源管理</h3><h4 id="LDO-工作原理"><a href="#LDO-工作原理" class="headerlink" title="LDO 工作原理"></a>LDO 工作原理</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedLDO.png" alt="LDO 原理"><br><em>LDO 工作原理</em></p><p>若比基准电压高,MOS 关断一点,反之打开一点</p><p>由基准电压和电阻分压公式可计算 $V_{out}$</p><p>耗散功率 = $|V_{OUT}-V_{IN}| \cdot I_{LOAD}$</p><p>常用的 LDO:</p><ul><li><strong>AMS1117-3.3/AMS1117-5.0</strong>:输出电压不可调</li><li><strong>AMS1117-ADJ</strong>:输出电压可调</li></ul><h4 id="DC-DC-工作原理"><a href="#DC-DC-工作原理" class="headerlink" title="DC-DC 工作原理"></a>DC-DC 工作原理</h4><p>类似于 PWM,MOS 要么完全导通(MOS 上电压=0),要么完全截止(MOS 上电流=0),这两种状态 MOS 上都不会有功耗,效率高</p><p>常用型号:PS5430</p><h4 id="应用举例:给-STM32-供电"><a href="#应用举例:给-STM32-供电" class="headerlink" title="应用举例:给 STM32 供电"></a>应用举例:给 STM32 供电</h4><p>用 TPS5430 把 12V 降到 5V,再用 AMS1117 把 5V 降到 3.3V</p><p><strong>原因</strong>:</p><ul><li>直接用 AMS1117:功率过高,发热严重</li><li>直接用 TPS5430:会有波动导致工作不稳定</li></ul><h2 id="四、MCU-及其伺服电路"><a href="#四、MCU-及其伺服电路" class="headerlink" title="四、MCU 及其伺服电路"></a>四、MCU 及其伺服电路</h2><h3 id="4-1-STM32-引脚"><a href="#4-1-STM32-引脚" class="headerlink" title="4.1 STM32 引脚"></a>4.1 STM32 引脚</h3><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefined32.png" alt="STM32 引脚"><br><em>STM32 引脚图</em></p><ul><li><strong>VDDA</strong>:模拟电源(连续值而非离散值)</li><li><strong>NRST</strong>:复位引脚,用于重启单片机(一般低电平重启)</li><li><strong>BOOT0</strong>:启动模式选择</li></ul><h3 id="4-2-启动模式"><a href="#4-2-启动模式" class="headerlink" title="4.2 启动模式"></a>4.2 启动模式</h3><table><thead><tr><th>BOOT1</th><th>BOOT0</th><th>启动模式</th><th>说明</th></tr></thead><tbody><tr><td>X</td><td>0</td><td>用户闪存启动</td><td>正常工作模式</td></tr><tr><td>0</td><td>1</td><td>系统存储器启动</td><td>可用于初始化</td></tr><tr><td>1</td><td>1</td><td>内置 SRAM 启动</td><td>用于调试</td></tr></tbody></table><h3 id="4-3-SWD-调试接口"><a href="#4-3-SWD-调试接口" class="headerlink" title="4.3 SWD 调试接口"></a>4.3 SWD 调试接口</h3><ul><li><strong>SWDIO</strong>:双向数据线</li><li><strong>SWCLK</strong>:时钟线</li></ul><h2 id="五、通信协议基础"><a href="#五、通信协议基础" class="headerlink" title="五、通信协议基础"></a>五、通信协议基础</h2><h3 id="5-1-串口-UART"><a href="#5-1-串口-UART" class="headerlink" title="5.1 串口 (UART)"></a>5.1 串口 (UART)</h3><p><strong>UART</strong> = Universal Asynchronous Receiver/Transmitter</p><p><strong>串行、异步、全双工</strong>的通信协议</p><h4 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h4><ul><li><strong>异步通信</strong>:两个 uart 设备之间通信不需要时钟线,需要指定相同的传输速率</li><li><strong>波特率</strong>:每秒钟传送码元数量,波特率 = $\frac{比特率}{log_2N}$</li></ul><h4 id="报文格式"><a href="#报文格式" class="headerlink" title="报文格式"></a>报文格式</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedUART%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F.png" alt="UART数据格式"><br><em>UART 报文格式</em></p><p>串口报文由以下部分组成:</p><ol><li><strong>起始位</strong>(1bit):逻辑”0”低电平,表示数据传输开始</li><li><strong>数据位</strong>(5~8bit):有效数据位数</li><li><strong>奇偶校验位</strong>(1bit):<ul><li>奇校验:有效数据和校验位中”1”的个数为奇数</li><li>偶校验:有效数据和校验位中”1”的个数为偶数</li><li>0 校验:校验位始终为 0</li><li>1 校验:校验位始终为 1</li></ul></li><li><strong>停止位</strong>(1~1.5bit):逻辑”1”高电平,表示传输结束</li><li><strong>空闲位</strong>:发送完一组报文后,总线自动拉高到逻辑”1”</li></ol><blockquote><p>[!NOTE]<br>接线的时候 TX 和 RX 要对着接</p></blockquote><h4 id="RS485-与-RS232"><a href="#RS485-与-RS232" class="headerlink" title="RS485 与 RS232"></a>RS485 与 RS232</h4><p>二者都是串口的衍生:</p><ul><li><strong>RS232</strong>:电平转换芯片把 TTL 电平转化为 232 电平,增强抗干扰能力,传输距离更长</li><li><strong>RS485</strong>:把输入的串口信号转化为差分信号,A>B 表示 0,反之表示 1,抗干扰能力强,半双工</li></ul><h3 id="5-2-CAN-总线"><a href="#5-2-CAN-总线" class="headerlink" title="5.2 CAN 总线"></a>5.2 CAN 总线</h3><p><strong>CAN</strong> = Controller Area Network(控制器局域网),多用于汽车</p><p>单片机信号经过 CAN 收发芯片转化为差分信号</p><h4 id="CAN-数据帧"><a href="#CAN-数据帧" class="headerlink" title="CAN 数据帧"></a>CAN 数据帧</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedCAN.png" alt="CAN 数据帧"><br><em>CAN 数据帧结构</em></p><table><thead><tr><th>字段</th><th>说明</th></tr></thead><tbody><tr><td>起始位</td><td>必须是逻辑 0</td></tr><tr><td>识别码</td><td>11 位,表明发给哪个设备,也代表优先级</td></tr><tr><td>RTR 位</td><td>区分数据帧 (0) 和远程请求帧 (1)</td></tr><tr><td>控制码</td><td>6 位,控制数据长度</td></tr><tr><td>CRC 码</td><td>16 位,循环冗余校验位</td></tr><tr><td>ACK 码</td><td>确认应答</td></tr><tr><td>结束位</td><td>7 位 1</td></tr></tbody></table><h3 id="5-3-I2C-总线"><a href="#5-3-I2C-总线" class="headerlink" title="5.3 I2C 总线"></a>5.3 I2C 总线</h3><p><strong>I2C</strong> = Inter-Integrated Circuit</p><p>一主多从,两根线:</p><ul><li><strong>SCL</strong>:时钟线</li><li><strong>SDA</strong>:数据线</li></ul><p>两根线都要加上拉电阻,空闲状态二者都是高电平</p><h4 id="I2C-数据帧"><a href="#I2C-数据帧" class="headerlink" title="I2C 数据帧"></a>I2C 数据帧</h4><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinediic.png" alt="I2C 数据帧"><br><em>I2C 数据帧</em></p><ol><li><strong>起始位</strong>:SCL 为高电平时,SDA 由高到低(下降沿)</li><li><strong>7 位设备地址码</strong></li><li><strong>读/写位</strong>:写=0,读=1</li><li><strong>应答信号</strong>:从机发送给主机。收到=0,未收到/读取接收完成=1</li><li><strong>八位寄存器地址</strong></li><li><strong>应答信号</strong></li><li><strong>八位数据</strong></li><li><strong>应答信号</strong></li><li><strong>停止位</strong>:SCL 为高时,SDA 由低到高</li></ol><h3 id="5-4-SPI-总线"><a href="#5-4-SPI-总线" class="headerlink" title="5.4 SPI 总线"></a>5.4 SPI 总线</h3><p><strong>SPI</strong> = Serial Peripheral Interface(串行外设接口),高速、全双工</p><p>一主多从,四根线:</p><table><thead><tr><th>信号线</th><th>说明</th></tr></thead><tbody><tr><td>SS</td><td>片选,确定哪一个芯片通信,大多数低电平有效</td></tr><tr><td>SCK</td><td>时钟信号线,有空闲高/低,时钟上升/下降沿采集,共 4 种模式</td></tr><tr><td>MOSI</td><td>主设备输出,从设备输入</td></tr><tr><td>MISO</td><td>主设备输入,从设备输出</td></tr></tbody></table><p><img src="https://photos-1355819942.cos.ap-shanghai.myqcloud.com/undefinedSPI%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F.png" alt="SPI数据格式"></p><blockquote><p>[!TIP]<br><strong>同步</strong>:依赖于时钟<br><strong>异步</strong>:不依赖于时钟,而是依赖于二者约定好的频率</p></blockquote>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Circuit </category>
</categories>
<tags>
<tag> circuit </tag>
<tag> communication </tag>
</tags>
</entry>
<entry>
<title>Altium Designer 基本使用</title>
<link href="/2025/07/04/2025-07-04-ad-basic-usage/"/>
<url>/2025/07/04/2025-07-04-ad-basic-usage/</url>
<content type="html"><![CDATA[<h2 id="基本操作"><a href="#基本操作" class="headerlink" title="基本操作"></a>基本操作</h2><ul><li>填充器件:放置 - 多边形</li><li>按 <code>G</code> 可以改网格宽度</li><li>改原理图文本处:放置 - 文字即可</li><li>原理图下 - 设计-Update PCB Document</li><li>走线的时候按 <code>space</code> 可以改变不同连线方式</li><li>切板子:先选到 keep-outlayer(禁止布线层),画框,然后选中画的线,设计 - 板子形状 - 按照选择对象定义</li><li>报告 - 测量 (ctrl+M) 可以测量</li><li>设计 - 规则可以编辑规则</li><li>导出 Gerber 文件:文件 - 制造输出-Gerber files</li></ul><h2 id="AD-的工程组成及创建"><a href="#AD-的工程组成及创建" class="headerlink" title="AD 的工程组成及创建"></a>AD 的工程组成及创建</h2><p>工程文件:</p><ul><li>原理图库</li><li>原理图</li><li>PCB 库</li><li>PCB</li><li>生产文件</li></ul><h2 id="元件库介绍和电阻容模型的创建"><a href="#元件库介绍和电阻容模型的创建" class="headerlink" title="元件库介绍和电阻容模型的创建"></a>元件库介绍和电阻容模型的创建</h2><p>元件符号:元件在原理图上的表示</p><ul><li>原件边框</li><li>管脚<ul><li>管脚序号</li><li>管脚名称</li></ul></li><li>元件名称</li><li>原件说明</li></ul><p>管脚带四个点的那一端是有电气属性的,应该冲外面</p><p>视图 - 栅格 - 切换可视栅格</p><p>选中一个元件,按住 <code>shift</code> 拖动可以复制</p><h2 id="IC-类元件模型的创建"><a href="#IC-类元件模型的创建" class="headerlink" title="IC 类元件模型的创建"></a>IC 类元件模型的创建</h2><p>字母的前后都加上 <code>\</code> 可以使其上面出现横线,如 <code>\E\N\</code></p><h3 id="快捷键"><a href="#快捷键" class="headerlink" title="快捷键"></a>快捷键</h3><ul><li>水平分布 <code>Num1</code></li><li>垂直分布 <code>Num2</code></li><li><code>TC</code> 新建元器件</li><li><code>PR</code> 放置矩形</li><li><code>ctrl</code>+<code>R</code> 画矩形来选择接触到的器件</li><li><code>ctrl</code>+<code>space</code> 切换输入法</li><li><code>shift</code>+ 右键在 3D 视图下改变视角</li><li>按住 <code>shift</code> 多选</li><li><code>shift</code>+<code>R</code> 忽略障碍物走线</li></ul><p>多个管脚的时候,先复制一个,然后编辑 - 阵列式粘贴,主增量是编号增量,次增量是名称增量</p><h2 id="光耦和二极管元件模型的创建"><a href="#光耦和二极管元件模型的创建" class="headerlink" title="光耦和二极管元件模型的创建"></a>光耦和二极管元件模型的创建</h2><p>放置 - 多边形</p><h2 id="现有元件库的调用"><a href="#现有元件库的调用" class="headerlink" title="现有元件库的调用"></a>现有元件库的调用</h2><p>原理图中,设计 - 生成原理图库,选 default 即可生成原理图库,可以把别人的复制到自己的库</p><h2 id="封装管理"><a href="#封装管理" class="headerlink" title="封装管理"></a>封装管理</h2><p>工具 - 封装管理器可以统一管理封装</p><h2 id="CHIP-封装"><a href="#CHIP-封装" class="headerlink" title="CHIP 封装"></a>CHIP 封装</h2><p>封装组成:</p><ul><li>PCB 焊盘</li><li>管脚序号</li><li>丝印</li><li>阻焊:防止绿油覆盖,必须要有,比焊盘大一点点,否则可能焊接不良</li><li>1 脚标识</li></ul><p>做封装的时候,焊盘直接用最大值</p><p>快速创建封装:工具-IPC…Wizard</p>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> Altium-Designer </tag>
<tag> EDA </tag>
<tag> PCB </tag>
</tags>
</entry>
<entry>
<title>树莓派热点设置教程</title>
<link href="/2025/07/03/2025-07-03-raspberry-pi-hotspot/"/>
<url>/2025/07/03/2025-07-03-raspberry-pi-hotspot/</url>
<content type="html"><![CDATA[<h2 id="开启热点"><a href="#开启热点" class="headerlink" title="开启热点"></a>开启热点</h2><p>要先关闭 NetworkManager:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> systemctl stop NetworkManager<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h3><h4 id="1-安装必要的软件包"><a href="#1-安装必要的软件包" class="headerlink" title="1. 安装必要的软件包"></a>1. 安装必要的软件包</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt-get</span> update<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> hostapd dnsmasq<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="2-配置网络接口"><a href="#2-配置网络接口" class="headerlink" title="2. 配置网络接口"></a>2. 配置网络接口</h4><p>编辑 <code>/etc/dhcpcd.conf</code> 文件来设置静态 IP 地址给你的热点:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">nano</span> /etc/dhcpcd.conf<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>添加以下内容:</p><pre class="line-numbers language-none"><code class="language-none">interface wlan0static ip_address=192.168.4.1/24nohook wpa_supplicant<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="3-配置-DHCP-服务器"><a href="#3-配置-DHCP-服务器" class="headerlink" title="3. 配置 DHCP 服务器"></a>3. 配置 DHCP 服务器</h4><p>编辑 <code>/etc/dnsmasq.d/custom.conf</code> 文件:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">nano</span> /etc/dnsmasq.d/custom.conf<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>增加以下内容:</p><pre class="line-numbers language-none"><code class="language-none">interface=wlan0dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="4-配置-hostapd"><a href="#4-配置-hostapd" class="headerlink" title="4. 配置 hostapd"></a>4. 配置 hostapd</h4><p>创建并编辑 <code>/etc/hostapd/hostapd.conf</code> 文件:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">nano</span> /etc/hostapd/hostapd.conf<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>输入以下内容,并根据需要修改 SSID 和密码:</p><pre class="line-numbers language-none"><code class="language-none">interface=wlan0driver=nl80211ssid=YourHotspotNamehw_mode=gchannel=7wmm_enabled=0macaddr_acl=0auth_algs=1ignore_broadcast_ssid=0wpa=2wpa_passphrase=YourHotspotPasswordwpa_key_mgmt=WPA-PSKrsn_pairwise=CCMP<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后编辑 <code>/etc/default/hostapd</code> 来指定配置文件的位置:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">nano</span> /etc/default/hostapd<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>找到 <code>#DAEMON_CONF=""</code> 并更改为:</p><pre class="line-numbers language-none"><code class="language-none">DAEMON_CONF="/etc/hostapd/hostapd.conf"<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-重启服务"><a href="#5-重启服务" class="headerlink" title="5. 重启服务"></a>5. 重启服务</h4><p>最后,重启 <code>dhcpcd</code>、<code>dnsmasq</code> 和 <code>hostapd</code> 服务来应用更改:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> systemctl restart dhcpcd<span class="token function">sudo</span> systemctl restart dnsmasq<span class="token function">sudo</span> systemctl start hostapd<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="设置开机自启"><a href="#设置开机自启" class="headerlink" title="设置开机自启"></a>设置开机自启</h2><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> hostapd<span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> dnsmasq<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Embedded </category>
<category> Platform </category>
<category> RaspberryPi </category>
</categories>
<tags>
<tag> raspberry-pi </tag>
<tag> embedded </tag>
<tag> Linux </tag>
</tags>
</entry>
<entry>
<title>CMake 学习笔记</title>
<link href="/2025/04/06/2025-04-06-cmake-notes/"/>
<url>/2025/04/06/2025-04-06-cmake-notes/</url>
<content type="html"><![CDATA[<h2 id="CMake-基本命令"><a href="#CMake-基本命令" class="headerlink" title="CMake 基本命令"></a>CMake 基本命令</h2><h3 id="cmake-minimum-required"><a href="#cmake-minimum-required" class="headerlink" title="cmake_minimum_required"></a>cmake_minimum_required</h3><p>指定使用的 cmake 的最低版本</p><p>可选,非必须,如果不加可能会有警告</p><h3 id="project"><a href="#project" class="headerlink" title="project"></a>project</h3><p>定义工程名称,并可指定工程的版本、工程描述、web 主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token comment"># PROJECT 指令的语法是:</span><span class="token keyword">project</span><span class="token punctuation">(</span><PROJECT-<span class="token property">NAME</span><span class="token punctuation">></span> [<language-name<span class="token punctuation">></span>...]<span class="token punctuation">)</span><span class="token keyword">project</span><span class="token punctuation">(</span><PROJECT-<span class="token property">NAME</span><span class="token punctuation">></span> [<span class="token property">VERSION</span> <major<span class="token punctuation">></span>[.<minor<span class="token punctuation">></span>[.<patch<span class="token punctuation">></span>[.<tweak<span class="token punctuation">></span>]]]] [DESCRIPTION <project-description-string<span class="token punctuation">></span>] [HOMEPAGE_URL <url-string<span class="token punctuation">></span>] [LANGUAGES <language-name<span class="token punctuation">></span>...]<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="add-executable"><a href="#add-executable" class="headerlink" title="add_executable"></a>add_executable</h3><p>定义工程会生成一个可执行程序</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">add_executable</span><span class="token punctuation">(</span>可执行程序名 源文件名称<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这里的可执行程序名和 project 中的项目名没有任何关系</p><p>源文件名可以是一个也可以是多个,如有多个可用空格或 <code>;</code> 间隔</p><h3 id="完整的-CMakeLists-txt-示例"><a href="#完整的-CMakeLists-txt-示例" class="headerlink" title="完整的 CMakeLists.txt 示例"></a>完整的 CMakeLists.txt 示例</h3><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">cmake_minimum_required</span><span class="token punctuation">(</span><span class="token property">VERSION</span> <span class="token number">3.0</span><span class="token punctuation">)</span><span class="token keyword">project</span><span class="token punctuation">(</span>CALC<span class="token punctuation">)</span><span class="token keyword">add_executable</span><span class="token punctuation">(</span>app add.c div.c main.c mult.c sub.c<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>编写好后,执行:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">cmake <span class="token operator"><</span>CMakeLists.txt 所在路径<span class="token operator">></span><span class="token function">make</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>但是这样的话中间文件太多了,可以新建一个 <code>build</code> 目录,在里面运行:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">cmake <span class="token punctuation">..</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="源文件列表管理"><a href="#源文件列表管理" class="headerlink" title="源文件列表管理"></a>源文件列表管理</h2><p>要是文件名太多,可以合成一个:</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token comment"># 方式 1: 各个源文件之间使用空格间隔</span><span class="token comment"># set(SRC_LIST add.c div.c main.c mult.c sub.c)</span><span class="token comment"># 方式 2: 各个源文件之间使用分号 ; 间隔</span><span class="token keyword">set</span><span class="token punctuation">(</span>SRC_LIST add.c;div.c;main.c;mult.c;sub.c<span class="token punctuation">)</span><span class="token keyword">add_executable</span><span class="token punctuation">(</span>app <span class="token punctuation">${</span>SRC_LIST<span class="token punctuation">}</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="指定输出路径"><a href="#指定输出路径" class="headerlink" title="指定输出路径"></a>指定输出路径</h2><p>在 CMake 中指定可执行程序输出的路径,也对应一个宏,叫做 <code>EXECUTABLE_OUTPUT_PATH</code>,它的值还是通过 set 命令进行设置:</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">set</span><span class="token punctuation">(</span>HOME /home/robin/Linux/Sort<span class="token punctuation">)</span><span class="token keyword">set</span><span class="token punctuation">(</span><span class="token variable">EXECUTABLE_OUTPUT_PATH</span> <span class="token punctuation">${</span>HOME<span class="token punctuation">}</span>/bin<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>第一行:定义一个变量用于存储一个绝对路径<br>第二行:将拼接好的路径值设置给 <code>EXECUTABLE_OUTPUT_PATH</code> 宏。如果这个路径中的子目录不存在,会自动生成,无需自己手动创建</p><p>由于可执行程序是基于 cmake 命令生成的 makefile 文件然后再执行 make 命令得到的,所以如果此处指定可执行程序生成路径的时候使用的是相对路径 <code>./xxx/xxx</code>,那么这个路径中的 <code>./</code> 对应的就是 makefile 文件所在的那个目录。</p><h2 id="搜索文件"><a href="#搜索文件" class="headerlink" title="搜索文件"></a>搜索文件</h2><p>如果一个项目里边的源文件很多,在编写 CMakeLists.txt 文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在 CMake 中为我们提供了搜索文件的命令,可以使用 <code>aux_source_directory</code> 命令或者 <code>file</code> 命令。</p><h3 id="方式-1:aux-source-directory"><a href="#方式-1:aux-source-directory" class="headerlink" title="方式 1:aux_source_directory"></a>方式 1:aux_source_directory</h3><p>在 CMake 中使用 <code>aux_source_directory</code> 命令可以查找某个路径下的所有源文件,命令格式为:</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">aux_source_directory</span><span class="token punctuation">(</span><dir<span class="token punctuation">></span> <variable<span class="token punctuation">></span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li><code>dir</code>:要搜索的目录</li><li><code>variable</code>:将从 dir 目录下搜索到的源文件列表存储到该变量中</li></ul><p>例如:</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">cmake_minimum_required</span><span class="token punctuation">(</span><span class="token property">VERSION</span> <span class="token number">3.0</span><span class="token punctuation">)</span><span class="token keyword">project</span><span class="token punctuation">(</span>CALC<span class="token punctuation">)</span><span class="token keyword">include_directories</span><span class="token punctuation">(</span><span class="token punctuation">${</span><span class="token variable">PROJECT_SOURCE_DIR</span><span class="token punctuation">}</span>/include<span class="token punctuation">)</span><span class="token comment"># 搜索 src 目录下的源文件</span><span class="token keyword">aux_source_directory</span><span class="token punctuation">(</span><span class="token punctuation">${</span><span class="token variable">CMAKE_CURRENT_SOURCE_DIR</span><span class="token punctuation">}</span>/src SRC_LIST<span class="token punctuation">)</span><span class="token keyword">add_executable</span><span class="token punctuation">(</span>app <span class="token punctuation">${</span>SRC_LIST<span class="token punctuation">}</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="方式-2:file"><a href="#方式-2:file" class="headerlink" title="方式 2:file"></a>方式 2:file</h3><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">file</span><span class="token punctuation">(</span>GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li><strong>GLOB</strong>: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。</li><li><strong>GLOB_RECURSE</strong>:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。</li></ul><p>例如,搜索当前目录的 src 目录下所有的源文件,并存储到变量中:</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">file</span><span class="token punctuation">(</span>GLOB MAIN_SRC <span class="token punctuation">${</span><span class="token variable">CMAKE_CURRENT_SOURCE_DIR</span><span class="token punctuation">}</span>/src/*.cpp<span class="token punctuation">)</span><span class="token keyword">file</span><span class="token punctuation">(</span>GLOB MAIN_HEAD <span class="token punctuation">${</span><span class="token variable">CMAKE_CURRENT_SOURCE_DIR</span><span class="token punctuation">}</span>/include/*.h<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><strong>注意</strong>:<code>CMAKE_CURRENT_SOURCE_DIR</code> 宏表示当前访问的 CMakeLists.txt 文件所在的路径。</p><h2 id="指定头文件路径"><a href="#指定头文件路径" class="headerlink" title="指定头文件路径"></a>指定头文件路径</h2><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">include_directories</span><span class="token punctuation">(</span>headpath<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>例如:</p><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">cmake_minimum_required</span><span class="token punctuation">(</span><span class="token property">VERSION</span> <span class="token number">3.0</span><span class="token punctuation">)</span><span class="token keyword">project</span><span class="token punctuation">(</span>CALC<span class="token punctuation">)</span><span class="token keyword">set</span><span class="token punctuation">(</span><span class="token variable">CMAKE_CXX_STANDARD</span> <span class="token number">11</span><span class="token punctuation">)</span><span class="token keyword">set</span><span class="token punctuation">(</span>HOME /home/robin/Linux/calc<span class="token punctuation">)</span><span class="token keyword">set</span><span class="token punctuation">(</span><span class="token variable">EXECUTABLE_OUTPUT_PATH</span> <span class="token punctuation">${</span>HOME<span class="token punctuation">}</span>/bin/<span class="token punctuation">)</span><span class="token keyword">include_directories</span><span class="token punctuation">(</span><span class="token punctuation">${</span><span class="token variable">PROJECT_SOURCE_DIR</span><span class="token punctuation">}</span>/include<span class="token punctuation">)</span><span class="token keyword">file</span><span class="token punctuation">(</span>GLOB SRC_LIST <span class="token punctuation">${</span><span class="token variable">CMAKE_CURRENT_SOURCE_DIR</span><span class="token punctuation">}</span>/src/*.cpp<span class="token punctuation">)</span><span class="token keyword">add_executable</span><span class="token punctuation">(</span>app <span class="token punctuation">${</span>SRC_LIST<span class="token punctuation">}</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>PROJECT_SOURCE_DIR</code> 宏对应的值就是我们在使用 cmake 命令时,后面紧跟的目录,一般是工程的根目录。</p><h2 id="CMake-基本结构"><a href="#CMake-基本结构" class="headerlink" title="CMake 基本结构"></a>CMake 基本结构</h2><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">cmake_minimum_required</span><span class="token punctuation">(</span><span class="token property">VERSION</span> <span class="token number">3.0</span><span class="token punctuation">)</span><span class="token keyword">project</span><span class="token punctuation">(</span>ProjectName<span class="token punctuation">)</span><span class="token keyword">add_executable</span><span class="token punctuation">(</span>target source1.cpp source2.cpp<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h2><ul><li><code>cmake_minimum_required</code>: 指定最低版本</li><li><code>project</code>: 定义项目名称</li><li><code>add_executable</code>: 添加可执行文件</li><li><code>add_library</code>: 添加库文件</li><li><code>target_link_libraries</code>: 链接库</li><li><code>include_directories</code>: 包含头文件目录</li><li><code>add_subdirectory</code>: 添加子目录</li></ul><h2 id="变量和缓存"><a href="#变量和缓存" class="headerlink" title="变量和缓存"></a>变量和缓存</h2><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">set</span><span class="token punctuation">(</span>VAR_NAME value<span class="token punctuation">)</span><span class="token keyword">set</span><span class="token punctuation">(</span>VAR_NAME value <span class="token variable">CACHE</span> <span class="token property">TYPE</span> <span class="token string">"description"</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h2 id="条件判断"><a href="#条件判断" class="headerlink" title="条件判断"></a>条件判断</h2><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">if</span><span class="token punctuation">(</span>CONDITION<span class="token punctuation">)</span> <span class="token comment"># ...</span><span class="token keyword">elseif</span><span class="token punctuation">(</span>ANOTHER_CONDITION<span class="token punctuation">)</span> <span class="token comment"># ...</span><span class="token keyword">else</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># ...</span><span class="token keyword">endif</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="循环"><a href="#循环" class="headerlink" title="循环"></a>循环</h2><pre class="line-numbers language-cmake" data-language="cmake"><code class="language-cmake"><span class="token keyword">foreach</span><span class="token punctuation">(</span>item <span class="token punctuation">${</span>ITEM_LIST<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment"># ...</span><span class="token keyword">endforeach</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">while</span><span class="token punctuation">(</span>CONDITION<span class="token punctuation">)</span> <span class="token comment"># ...</span><span class="token keyword">endwhile</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> CMake </tag>
</tags>
</entry>
<entry>
<title>Makefile 学习笔记</title>
<link href="/2025/03/24/2025-03-24-makefile-notes/"/>
<url>/2025/03/24/2025-03-24-makefile-notes/</url>
<content type="html"><![CDATA[<blockquote><p>[!NOTE]</p><p>本文档为原始笔记,内容待补充。建议参考 Makefile 官方文档获取更详细的内容。</p></blockquote><h2 id="Makefile-基本结构"><a href="#Makefile-基本结构" class="headerlink" title="Makefile 基本结构"></a>Makefile 基本结构</h2><pre class="line-numbers language-makefile" data-language="makefile"><code class="language-makefile"><span class="token target symbol">target</span><span class="token punctuation">:</span> prerequisitescommand<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><ul><li><code>target</code>: 目标文件</li><li><code>prerequisites</code>: 依赖文件</li><li><code>command</code>: 构建命令(必须以 Tab 开头)</li></ul><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><pre class="line-numbers language-makefile" data-language="makefile"><code class="language-makefile">CC <span class="token operator">=</span> gccCFLAGS <span class="token operator">=</span> -Wall -g<span class="token target symbol">target</span><span class="token punctuation">:</span> main.o utils.o<span class="token variable">$</span><span class="token punctuation">(</span>CC<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span>CFLAGS<span class="token punctuation">)</span> -o target main.o utils.o<span class="token target symbol">main.o</span><span class="token punctuation">:</span> main.c<span class="token variable">$</span><span class="token punctuation">(</span>CC<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span>CFLAGS<span class="token punctuation">)</span> -c main.c<span class="token target symbol">utils.o</span><span class="token punctuation">:</span> utils.c<span class="token variable">$</span><span class="token punctuation">(</span>CC<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span>CFLAGS<span class="token punctuation">)</span> -c utils.c<span class="token target symbol">clean</span><span class="token punctuation">:</span>rm -f *.o target<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="常用变量"><a href="#常用变量" class="headerlink" title="常用变量"></a>常用变量</h2><ul><li><code>$@</code>: 目标文件名</li><li><code>$<</code>: 第一个依赖文件名</li><li><code>$^</code>: 所有依赖文件名</li><li><code>$?</code>: 比目标新的依赖文件</li></ul><h2 id="伪目标"><a href="#伪目标" class="headerlink" title="伪目标"></a>伪目标</h2><pre class="line-numbers language-makefile" data-language="makefile"><code class="language-makefile"><span class="token builtin-target builtin">.PHONY</span><span class="token punctuation">:</span> clean all<span class="token target symbol">clean</span><span class="token punctuation">:</span>rm -f *.o target<span class="token target symbol">all</span><span class="token punctuation">:</span> target<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> Makefile </tag>
</tags>
</entry>
<entry>
<title>Git 版本控制完全指南</title>
<link href="/2025/03/17/2025-07-13-git-version-control-tutorial/"/>
<url>/2025/03/17/2025-07-13-git-version-control-tutorial/</url>
<content type="html"><![CDATA[<h1 id="Git-版本控制完全指南"><a href="#Git-版本控制完全指南" class="headerlink" title="Git 版本控制完全指南"></a>Git 版本控制完全指南</h1><h2 id="一、什么是版本控制"><a href="#一、什么是版本控制" class="headerlink" title="一、什么是版本控制"></a>一、什么是版本控制</h2><p>版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。</p><h2 id="二、创建版本库"><a href="#二、创建版本库" class="headerlink" title="二、创建版本库"></a>二、创建版本库</h2><h3 id="2-1-创建空目录"><a href="#2-1-创建空目录" class="headerlink" title="2.1 创建空目录"></a>2.1 创建空目录</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">mkdir</span> learngit<span class="token builtin class-name">cd</span> learngit<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="2-2-初始化为-Git-仓库"><a href="#2-2-初始化为-Git-仓库" class="headerlink" title="2.2 初始化为 Git 仓库"></a>2.2 初始化为 Git 仓库</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> init<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="2-3-把文件添加到仓库"><a href="#2-3-把文件添加到仓库" class="headerlink" title="2.3 把文件添加到仓库"></a>2.3 把文件添加到仓库</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> <span class="token function">add</span> README.txt<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="2-4-把文件提交到仓库"><a href="#2-4-把文件提交到仓库" class="headerlink" title="2.4 把文件提交到仓库"></a>2.4 把文件提交到仓库</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> commit <span class="token parameter variable">-m</span> <span class="token string">"wrote a readme file"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>[!TIP]<br><code>-m</code> 后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。</p></blockquote><p><code>commit</code> 可以一次提交很多文件,所以你可以多次 <code>add</code> 不同的文件:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> <span class="token function">add</span> file1.txt<span class="token function">git</span> <span class="token function">add</span> file2.txt file3.txt<span class="token function">git</span> commit <span class="token parameter variable">-m</span> <span class="token string">"add 3 files."</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><blockquote><p>[!NOTE]<br><code>git add</code> 命令实际上就是把要提交的所有修改放到暂存区(Stage),然后执行 <code>git commit</code> 就可以一次性把暂存区的所有修改提交到分支。</p></blockquote><p>每次修改,如果不用 <code>git add</code> 到暂存区,那就不会加入到 <code>commit</code> 中。</p><p><strong>工作流程</strong>:第一次修改 → <code>git add</code> → 第二次修改 → <code>git add</code> → <code>git commit</code></p><h2 id="三、时光机穿梭"><a href="#三、时光机穿梭" class="headerlink" title="三、时光机穿梭"></a>三、时光机穿梭</h2><h3 id="3-1-查看仓库状态"><a href="#3-1-查看仓库状态" class="headerlink" title="3.1 查看仓库状态"></a>3.1 查看仓库状态</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> status<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>可以查看仓库当前状态</p><h3 id="3-2-查看文件修改"><a href="#3-2-查看文件修改" class="headerlink" title="3.2 查看文件修改"></a>3.2 查看文件修改</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> <span class="token function">diff</span> README.txt<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>可以查看做了什么修改</p><h3 id="3-3-查看历史记录"><a href="#3-3-查看历史记录" class="headerlink" title="3.3 查看历史记录"></a>3.3 查看历史记录</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> log<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>可以查看历史记录(从最近到最远)</p><p>在 Git 中,用 <code>HEAD</code> 表示当前版本,也就是最新的提交:</p><ul><li><code>HEAD^</code>:上一个版本</li><li><code>HEAD^^</code>:上上一个版本</li><li><code>HEAD~100</code>:往上 100 个版本</li></ul><h3 id="3-4-版本回退"><a href="#3-4-版本回退" class="headerlink" title="3.4 版本回退"></a>3.4 版本回退</h3><p>把当前版本回退到上一个版本:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> reset <span class="token parameter variable">--hard</span> HEAD^<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>[!NOTE]<br><code>--hard</code> 参数会回退到上个版本的已提交状态</p><ul><li><code>--soft</code>:会回退到上个版本的未提交状态</li><li><code>--mixed</code>:会回退到上个版本已添加但未提交的状态</li></ul></blockquote><h3 id="3-5-回到未来"><a href="#3-5-回到未来" class="headerlink" title="3.5 回到未来"></a>3.5 回到未来</h3><p>找到要回到的版本号(之前执行 <code>git log</code> 时显示的):</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> reset <span class="token parameter variable">--hard</span> 1094a<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>要是找不到了,可以使用 <code>git reflog</code> 查看之前的命令历史。</p><h3 id="3-6-撤销工作区修改"><a href="#3-6-撤销工作区修改" class="headerlink" title="3.6 撤销工作区修改"></a>3.6 撤销工作区修改</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> checkout -- readme.txt<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>命令 <code>git checkout -- readme.txt</code> 意思就是,把 <code>readme.txt</code> 文件在工作区的修改全部撤销。</p><p><strong>两种情况</strong>:</p><ol><li><code>readme.txt</code> 自修改后还没有被放到暂存区,撤销修改就回到和版本库一模一样的状态</li><li><code>readme.txt</code> 已经添加到暂存区后,又作了修改,撤销修改就回到添加到暂存区后的状态</li></ol><p>总之,就是让这个文件回到最近一次 <code>git commit</code> 或 <code>git add</code> 时的状态。</p><blockquote><p>[!WARNING]<br><code>git checkout -- file</code> 命令中的 <code>--</code> 很重要,没有 <code>--</code>,就变成了”切换到另一个分支”的命令</p></blockquote><h3 id="3-7-撤销暂存区修改"><a href="#3-7-撤销暂存区修改" class="headerlink" title="3.7 撤销暂存区修改"></a>3.7 撤销暂存区修改</h3><p>用命令 <code>git reset HEAD <file></code> 可以把暂存区的修改撤销掉(unstage),重新放回工作区。</p><h2 id="四、分支管理"><a href="#四、分支管理" class="headerlink" title="四、分支管理"></a>四、分支管理</h2><h3 id="4-1-创建和切换分支"><a href="#4-1-创建和切换分支" class="headerlink" title="4.1 创建和切换分支"></a>4.1 创建和切换分支</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> branch <span class="token operator"><</span>branch-name<span class="token operator">></span> <span class="token comment"># 创建分支</span><span class="token function">git</span> checkout <span class="token operator"><</span>branch-name<span class="token operator">></span> <span class="token comment"># 切换分支</span><span class="token function">git</span> checkout <span class="token parameter variable">-b</span> <span class="token operator"><</span>branch-name<span class="token operator">></span> <span class="token comment"># 创建并切换分支</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="4-2-合并分支"><a href="#4-2-合并分支" class="headerlink" title="4.2 合并分支"></a>4.2 合并分支</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> merge <span class="token operator"><</span>branch-name<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="4-3-删除分支"><a href="#4-3-删除分支" class="headerlink" title="4.3 删除分支"></a>4.3 删除分支</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> branch <span class="token parameter variable">-d</span> <span class="token operator"><</span>branch-name<span class="token operator">></span> <span class="token comment"># 删除本地分支</span><span class="token function">git</span> branch <span class="token parameter variable">-D</span> <span class="token operator"><</span>branch-name<span class="token operator">></span> <span class="token comment"># 强制删除</span><span class="token function">git</span> push origin <span class="token parameter variable">--delete</span> <span class="token operator"><</span>branch-name<span class="token operator">></span> <span class="token comment"># 删除远程分支</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="五、远程仓库"><a href="#五、远程仓库" class="headerlink" title="五、远程仓库"></a>五、远程仓库</h2><h3 id="5-1-关联远程仓库"><a href="#5-1-关联远程仓库" class="headerlink" title="5.1 关联远程仓库"></a>5.1 关联远程仓库</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> remote <span class="token function">add</span> origin git@server-name:path/repo-name.git<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>[!NOTE]<br>关联一个远程库时必须给远程库指定一个名字,<code>origin</code> 是默认习惯命名</p></blockquote><h3 id="5-2-推送内容"><a href="#5-2-推送内容" class="headerlink" title="5.2 推送内容"></a>5.2 推送内容</h3><p>第一次推送 <code>master</code> 分支的所有内容:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> push <span class="token parameter variable">-u</span> origin master<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>此后,每次本地提交后,只要有必要,就可以使用命令推送最新修改:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> push origin master<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="5-3-查看远程仓库"><a href="#5-3-查看远程仓库" class="headerlink" title="5.3 查看远程仓库"></a>5.3 查看远程仓库</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> remote <span class="token parameter variable">-v</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>示例输出:</p><pre class="line-numbers language-none"><code class="language-none">origin git@github.com:michaelliao/learn-git.git (fetch)origin git@github.com:michaelliao/learn-git.git (push)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="5-4-删除远程仓库"><a href="#5-4-删除远程仓库" class="headerlink" title="5.4 删除远程仓库"></a>5.4 删除远程仓库</h3><p>如果添加的时候地址写错了,或者就是想删除远程库,可以用 <code>git remote rm <name></code> 命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> remote <span class="token function">rm</span> origin<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>[!NOTE]<br>此处的”删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到 GitHub,在后台页面找到删除按钮再删除。</p></blockquote><h2 id="六、常用命令速查表"><a href="#六、常用命令速查表" class="headerlink" title="六、常用命令速查表"></a>六、常用命令速查表</h2><table><thead><tr><th>命令</th><th>说明</th></tr></thead><tbody><tr><td><code>git init</code></td><td>初始化仓库</td></tr><tr><td><code>git add <file></code></td><td>添加文件到暂存区</td></tr><tr><td><code>git commit -m "message"</code></td><td>提交到仓库</td></tr><tr><td><code>git status</code></td><td>查看仓库状态</td></tr><tr><td><code>git log</code></td><td>查看提交历史</td></tr><tr><td><code>git log --oneline</code></td><td>简洁显示历史记录</td></tr><tr><td><code>git log --graph</code></td><td>图形化显示历史记录</td></tr><tr><td><code>git diff <file></code></td><td>查看文件修改</td></tr><tr><td><code>git reset --hard HEAD^</code></td><td>回退到上一版本</td></tr><tr><td><code>git reset --hard <commit></code></td><td>回退到指定版本</td></tr><tr><td><code>git checkout -- <file></code></td><td>撤销工作区修改</td></tr><tr><td><code>git reset HEAD <file></code></td><td>撤销暂存区修改</td></tr><tr><td><code>git remote add origin <url></code></td><td>关联远程仓库</td></tr><tr><td><code>git push -u origin master</code></td><td>首次推送到远程</td></tr><tr><td><code>git push origin master</code></td><td>推送到远程</td></tr><tr><td><code>git remote -v</code></td><td>查看远程仓库</td></tr><tr><td><code>git remote rm <name></code></td><td>删除远程仓库</td></tr><tr><td><code>git reflog</code></td><td>查看命令历史</td></tr><tr><td><code>git branch <name></code></td><td>创建分支</td></tr><tr><td><code>git checkout -b <name></code></td><td>创建并切换分支</td></tr><tr><td><code>git merge <name></code></td><td>合并分支</td></tr><tr><td><code>git branch -d <name></code></td><td>删除分支</td></tr><tr><td><code>git revert <commit></code></td><td>撤销提交</td></tr></tbody></table><h2 id="七、最佳实践"><a href="#七、最佳实践" class="headerlink" title="七、最佳实践"></a>七、最佳实践</h2><ol><li><strong>频繁提交</strong>:每次完成一个小功能就提交一次</li><li><strong>有意义的提交信息</strong>:清楚地描述这次提交做了什么</li><li><strong>使用分支</strong>:开发新功能时使用新分支</li><li><strong>推送前拉取</strong>:推送前先拉取最新代码避免冲突</li><li><strong>备份重要代码</strong>:及时推送到远程仓库</li></ol>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> git </tag>
<tag> github </tag>
</tags>
</entry>
<entry>
<title>MATLAB 基础学习笔记</title>
<link href="/2025/02/18/2025-02-18-matlab-basic-notes/"/>
<url>/2025/02/18/2025-02-18-matlab-basic-notes/</url>
<content type="html"><![CDATA[<h2 id="基本命令"><a href="#基本命令" class="headerlink" title="基本命令"></a>基本命令</h2><ul><li><code>clc</code> 清屏</li><li><code>clear + [变量名]</code> 清除变量</li><li><code>a:[b]:c</code> 从 a 开始,公差为 b,最多到 c</li><li><code>save 文件名 [变量名]</code> 保存变量</li><li><code>load 文件名 [变量名]</code> 导入变量</li><li><code>inf</code> 无穷大</li><li>语句后加 <code>;</code> 不显示结果</li><li><code>who</code> 列出变量</li><li><code>whos</code> 列出变量和类型</li></ul><h2 id="矩阵操作"><a href="#矩阵操作" class="headerlink" title="矩阵操作"></a>矩阵操作</h2><ul><li>矩阵用 <code>[]</code> 包围,<code>;</code> 分列,<code>,</code> 分行</li><li>数组索引:一个数字,两个数字均可如 <code>A(2),A(1,2)</code></li><li>矩阵除法:<code>A\B = A^-1^*B</code>(左除) <code>A/B = A*B^-1^</code></li><li>矩阵乘法:<code>A*B</code></li><li><code>A.*B</code>:相对应位置的元素运算</li><li><code>A./B</code>,<code>A.^B</code>,<code>A.\B</code> 同理</li></ul><h2 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h2><p>注释前加 <code>%</code></p><h2 id="绘图"><a href="#绘图" class="headerlink" title="绘图"></a>绘图</h2><ul><li><code>plot(x,y)</code> x 当横轴,y 当纵轴</li><li><code>plot(x,y,'r--','linewidth',2)</code> 红色虚线,线宽为 2</li><li><code>hold on</code> 在现有的图窗中添加曲线</li><li><code>plot(x,y2,'b-')</code> 蓝色实线</li><li><code>legend('sin','cos')</code> 添加图例</li><li><code>xlabel('111')</code> 标记 x 轴</li><li><code>ylabel('222')</code> 标记 y 轴</li><li><code>title('TITLE')</code> 添加标题</li><li><code>set(gca,'fontsize',12)</code> 设定坐标区字体大小</li></ul><p>画完图后复制为向量图(鼠标放在图像上方会显示),记得写注释,注释要对齐</p>]]></content>
<categories>
<category> Learning </category>
<category> Language </category>
</categories>
<tags>
<tag> MATLAB </tag>
</tags>
</entry>
<entry>
<title>Markdown 语法学习笔记</title>
<link href="/2025/01/25/2025-01-25-markdown-syntax-notes/"/>
<url>/2025/01/25/2025-01-25-markdown-syntax-notes/</url>
<content type="html"><![CDATA[<h2 id="一、基本语法"><a href="#一、基本语法" class="headerlink" title="一、基本语法"></a>一、基本语法</h2><h3 id="1-标题"><a href="#1-标题" class="headerlink" title="1. 标题"></a>1. 标题</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token title important"><span class="token punctuation">#</span> 一级标题</span><span class="token title important"><span class="token punctuation">##</span> 二级标题</span><span class="token title important"><span class="token punctuation">###</span> 三级标题</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="2-加粗"><a href="#2-加粗" class="headerlink" title="2. 加粗"></a>2. 加粗</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token bold"><span class="token punctuation">**</span><span class="token content">要加粗的</span><span class="token punctuation">**</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>要加粗的</strong></p><p>没加粗的</p><p>或者按下 <code>control</code> + <code>B</code></p><h3 id="3-斜体"><a href="#3-斜体" class="headerlink" title="3. 斜体"></a>3. 斜体</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token italic"><span class="token punctuation">*</span><span class="token content">要斜体的</span><span class="token punctuation">*</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><em>要斜体的</em></p><p>没斜体的</p><p>或者按下 <code>control</code> + <code>I</code></p><h3 id="4-删除线"><a href="#4-删除线" class="headerlink" title="4. 删除线"></a>4. 删除线</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token strike"><span class="token punctuation">~~</span><span class="token content">要删除的</span><span class="token punctuation">~~</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><del>要删除的</del></p><h3 id="5-高亮"><a href="#5-高亮" class="headerlink" title="5. 高亮"></a>5. 高亮</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown">==要高亮的==<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>==要高亮的==</p><h3 id="6-代码"><a href="#6-代码" class="headerlink" title="6. 代码"></a>6. 代码</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token code-snippet code keyword">`一个代码`</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><code>一个代码</code></p><h3 id="7-代码框"><a href="#7-代码框" class="headerlink" title="7. 代码框"></a>7. 代码框</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token code"><span class="token punctuation">```</span><span class="token code-language">c</span><span class="token code-block language-c">int main(){ for(int i = 0;i<n;i++) return 1;}</span><span class="token punctuation">```</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>esc</code> 下面那个按键</p><h3 id="8-引用"><a href="#8-引用" class="headerlink" title="8. 引用"></a>8. 引用</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token blockquote punctuation">></span> 引用文本 (可以嵌套)<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>引用文本</p></blockquote><blockquote><blockquote><p>111</p></blockquote></blockquote><h3 id="9-无序列表"><a href="#9-无序列表" class="headerlink" title="9. 无序列表"></a>9. 无序列表</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token list punctuation">-</span> 第一行<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li>第一行</li><li>按下 <code>enter</code> 后自动出列表黑点<ul><li>按下 <code>tab</code> 后变成空心的</li></ul></li><li>按下 <code>shift</code>+<code>tab</code> 又可以变回来</li></ul><h3 id="10-有序列表(数字-”-”-空格)"><a href="#10-有序列表(数字-”-”-空格)" class="headerlink" title="10. 有序列表(数字+”.”+空格)"></a>10. 有序列表(数字+”.”+空格)</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token list punctuation">1.</span> 111<span class="token list punctuation">2.</span> 222<span class="token list punctuation">3.</span> 333<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><ol><li>111</li><li>222</li><li>333</li></ol><h3 id="11-上标"><a href="#11-上标" class="headerlink" title="11. 上标"></a>11. 上标</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown">x^上标^<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>x^上标^</p><h3 id="12-下标"><a href="#12-下标" class="headerlink" title="12. 下标"></a>12. 下标</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown">x<span class="token strike"><span class="token punctuation">~</span><span class="token content">下标</span><span class="token punctuation">~</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>x<del>下标</del></p><h3 id="13-注释"><a href="#13-注释" class="headerlink" title="13. 注释"></a>13. 注释</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown">正文 [^1][^1]:注释<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>正文 [^1]<br>[^1]:注释(应该写在文档最后)</p><h3 id="14-链接(常用”-”-”-”-分别包围文本和链接)"><a href="#14-链接(常用”-”-”-”-分别包围文本和链接)" class="headerlink" title="14. 链接(常用”[]”+”()” 分别包围文本和链接)"></a>14. 链接(常用”[]”+”()” 分别包围文本和链接)</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token url">[<span class="token content">百度</span>](<span class="token url">www.baidu.com</span>)</span><span class="token url">[<span class="token content">1. 标题</span>](<span class="token url">#1.标题</span>)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><a href="www.baidu.com">百度</a></p><p><a href="#1.%E6%A0%87%E9%A2%98">1. 标题</a></p><p>支持网页链接和文内跳转,按住 <code>control</code> + <code>单击鼠标左键</code> 即可跳转</p><h3 id="15-任务列表-“-”-空格"><a href="#15-任务列表-“-”-空格" class="headerlink" title="15. 任务列表 (“- [ ]” + 空格)"></a>15. 任务列表 (“- [ ]” + 空格)</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown"><span class="token list punctuation">-</span> [ ] 111<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li><input disabled="" type="checkbox"> 111</li></ul><h3 id="16-表格(用”-“-绘制表格边框)"><a href="#16-表格(用”-“-绘制表格边框)" class="headerlink" title="16. 表格(用”|“ 绘制表格边框)"></a>16. 表格(用”|“ 绘制表格边框)</h3><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown">| 学号 | 姓名 | 年龄 || :--- | :---: | ---: |(引号的位置代表左对齐、居中、右对齐)第一行为表头,并由第二行分割线决定对齐方式与长度,第三行之后即表格数据<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><table><thead><tr><th align="center">学号</th><th align="left">姓名</th><th align="center">年龄</th></tr></thead><tbody><tr><td align="center">111</td><td align="left">明太鱼</td><td align="center">111</td></tr><tr><td align="center">222</td><td align="left">鲈鱼</td><td align="center">222</td></tr></tbody></table><p>分割线:<code>---</code></p><hr><h3 id="17-Emoji"><a href="#17-Emoji" class="headerlink" title="17. Emoji"></a>17. Emoji</h3><p>🥳🥳</p><pre class="line-numbers language-markdown" data-language="markdown"><code class="language-markdown">:clown_face:<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>:clown_face:</p><p>在这个网站上有可复制的:<a href="https://emojipedia.org/">emoji</a></p><h2 id="二、进阶"><a href="#二、进阶" class="headerlink" title="二、进阶"></a>二、进阶</h2><h3 id="1-目录"><a href="#1-目录" class="headerlink" title="1. 目录"></a>1. 目录</h3><p>文档开头输入 <code>[TOC]</code> 即可</p>]]></content>
<categories>
<category> Learning </category>
<category> Tools </category>
</categories>
<tags>
<tag> Markdown </tag>
</tags>
</entry>
</search>