-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
1484 lines (1411 loc) · 273 KB
/
search.xml
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
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>AcWing语法基础课错题记录</title>
<url>/2022/01/27/AcWing%E8%AF%AD%E6%B3%95%E5%9F%BA%E7%A1%80%E8%AF%BE%E9%94%99%E9%A2%98%E8%AE%B0%E5%BD%95/</url>
<content><![CDATA[<h1 id="AcWing-604-圆的面积"><a href="#AcWing-604-圆的面积" class="headerlink" title="AcWing 604. 圆的面积"></a>AcWing 604. 圆的面积</h1><p>错误代码:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> float PI=3.14159;</span><br><span class="line"> float R;</span><br><span class="line"> cin>>R;</span><br><span class="line"> cout<<"A="<< PI*R*R<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>测试输入 </p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">10.16</span><br></pre></td></tr></table></figure>
<p>测试输出</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">A=324.293</span><br></pre></td></tr></table></figure>
<p>标准答案</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">A=324.2925</span><br></pre></td></tr></table></figure>
<p>分析原因:题目说了要保留四位小数,不用printf的话,用</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">fixed<<setprecisions(4)<<PI*R*R</span><br></pre></td></tr></table></figure>
<p>试试,记得加头文件<iomanip><br>答案仍错,把float换为double试试<br>测试成功,下面为正确代码</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">#include <iomanip></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> double PI=3.14159;</span><br><span class="line"> double R;</span><br><span class="line"> cin>>R;</span><br><span class="line"> cout<<"A="<<fixed<<setprecision(4)<<PI*R*R<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="AcWing-616-两点间的距离"><a href="#AcWing-616-两点间的距离" class="headerlink" title="AcWing 616.两点间的距离"></a>AcWing 616.两点间的距离</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">#include <iomanip></span><br><span class="line">#include <cmath></span><br><span class="line">using namespace std;</span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> double x1,y1,x2,y2;</span><br><span class="line"> cin>>x1>>y1;</span><br><span class="line"> cin>>x2>>y2;</span><br><span class="line"> cout<<fixed<<setprecision(4)<<sqrt((x1-x2)^2+(y1-y2)^2);</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>报错a.cpp:10:22: error: invalid operands of types ‘float’ and ‘float’ to binary ‘operator^’<br>百度原因是因为cpp不支持平方操作<br>修改为</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cout<<fixed<<setprecision(4)<<sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));</span><br></pre></td></tr></table></figure>
<h1 id="AcWing-617-距离"><a href="#AcWing-617-距离" class="headerlink" title="AcWing 617. 距离"></a>AcWing 617. 距离</h1><p>题目:两辆汽车在同一地点,同时,沿同一方向前进。<br>一辆车的速度为 60 km/h,另一辆车的速度为 90 km/h。<br>显然,快车与慢车的距离会不断拉开,每过一个小时(60 分钟),两车的距离就拉开 30 公里。<br>现在,告诉你两车之间的距离为 L 公里,请你求出两车已经行驶了多长时间?<br>错误代码:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> int L;</span><br><span class="line"> cin >> L;</span><br><span class="line"> cout<<L/(90-60)*60<<" minutos"<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>错误原因:没有考虑到/是整除,L可能不是30的整数倍,此时应该如何解决问题呢?<br>修改:快车与慢车每小时拉开30KM,每分钟拉开0.5KM<br>直接把单位换算到分钟,不用小时</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> int L;</span><br><span class="line"> cin >> L;</span><br><span class="line"> cout<<L/0.5<<" minutos"<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>仍然错误;此时可以看到除了0.5,变成浮点数了,数字太大的话会变成科学技术法,与标准输出不同,我们强制转换为int;</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> int L;</span><br><span class="line"> cin >> L;</span><br><span class="line"> cout<<(int)(L/0.5)<<" minutos"<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>成功</p>
<h1 id="AcWing-656-钞票和硬币"><a href="#AcWing-656-钞票和硬币" class="headerlink" title="AcWing 656. 钞票和硬币"></a>AcWing 656. 钞票和硬币</h1><p>错误在于,对小数进行区域操作是不可行的;<br>我们把小数部分扩大数量级至整数部分就能正确地使用取余操作了;<br>但是此处出现了问题<br>源代码:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> double number;</span><br><span class="line"> cin>>number;</span><br><span class="line"> int a,b,c,d,e,f,g,h,i,j,k,l;</span><br><span class="line"> a=number/100;</span><br><span class="line"> b=(number-a*100)/50;</span><br><span class="line"> c=(number-a*100-b*50)/20;</span><br><span class="line"> d=(number-a*100-b*50-c*20)/10;</span><br><span class="line"> e=(number-a*100-b*50-c*20-d*10)/5;</span><br><span class="line"> f=(number-a*100-b*50-c*20-d*10-e*5)/2;</span><br><span class="line"> cout<<"NOTAS:"<<endl;</span><br><span class="line"> cout<<a<<" nota(s) de R$ 100.00"<<endl;</span><br><span class="line"> cout<<b<<" nota(s) de R$ 50.00"<<endl;</span><br><span class="line"> cout<<c<<" nota(s) de R$ 20.00"<<endl;</span><br><span class="line"> cout<<d<<" nota(s) de R$ 10.00"<<endl;</span><br><span class="line"> cout<<e<<" nota(s) de R$ 5.00"<<endl;</span><br><span class="line"> cout<<f<<" nota(s) de R$ 2.00"<<endl;</span><br><span class="line"> cout<<"MOEDAS:"<<endl;</span><br><span class="line"> g=(number-a*100-b*50-c*20-d*10-e*5-f*2)/1;</span><br><span class="line"> h=(number-a*100-b*50-c*20-d*10-e*5-f*2-g*1)*100/50;</span><br><span class="line"> i=(number-a*100-b*50-c*20-d*10-e*5-f*2-g*1-h*0.5)*100/25;</span><br><span class="line"> j=(number-a*100-b*50-c*20-d*10-e*5-f*2-g*1-h*0.5-i*0.25)*100/10;</span><br><span class="line"> k=(number-a*100-b*50-c*20-d*10-e*5-f*2-g*1-h*0.5-i*0.25-j*0.1)*100/5;</span><br><span class="line"> l=(number-a*100-b*50-c*20-d*10-e*5-f*2-g*1-h*0.5-i*0.25-j*0.1-k*0.05)*100/1;</span><br><span class="line"> cout<<g<<" moeda(s) de R$ 1.00"<<endl;</span><br><span class="line"> cout<<h<<" moeda(s) de R$ 0.50"<<endl;</span><br><span class="line"> cout<<i<<" moeda(s) de R$ 0.25"<<endl;</span><br><span class="line"> cout<<j<<" moeda(s) de R$ 0.10"<<endl;</span><br><span class="line"> cout<<k<<" moeda(s) de R$ 0.05"<<endl;</span><br><span class="line"> cout<<l<<" moeda(s) de R$ 0.01"<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>输入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">463.01</span><br></pre></td></tr></table></figure>
<p>输出</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">NOTAS:</span><br><span class="line">4 nota(s) de R$ 100.00</span><br><span class="line">1 nota(s) de R$ 50.00</span><br><span class="line">0 nota(s) de R$ 20.00</span><br><span class="line">1 nota(s) de R$ 10.00</span><br><span class="line">0 nota(s) de R$ 5.00</span><br><span class="line">1 nota(s) de R$ 2.00</span><br><span class="line">MOEDAS:</span><br><span class="line">1 moeda(s) de R$ 1.00</span><br><span class="line">0 moeda(s) de R$ 0.50</span><br><span class="line">0 moeda(s) de R$ 0.25</span><br><span class="line">0 moeda(s) de R$ 0.10</span><br><span class="line">0 moeda(s) de R$ 0.05</span><br><span class="line">0 moeda(s) de R$ 0.01</span><br></pre></td></tr></table></figure>
<p>标准答案:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">NOTAS:</span><br><span class="line">4 nota(s) de R$ 100.00</span><br><span class="line">1 nota(s) de R$ 50.00</span><br><span class="line">0 nota(s) de R$ 20.00</span><br><span class="line">1 nota(s) de R$ 10.00</span><br><span class="line">0 nota(s) de R$ 5.00</span><br><span class="line">1 nota(s) de R$ 2.00</span><br><span class="line">MOEDAS:</span><br><span class="line">1 moeda(s) de R$ 1.00</span><br><span class="line">0 moeda(s) de R$ 0.50</span><br><span class="line">0 moeda(s) de R$ 0.25</span><br><span class="line">0 moeda(s) de R$ 0.10</span><br><span class="line">0 moeda(s) de R$ 0.05</span><br><span class="line">1 moeda(s) de R$ 0.01</span><br></pre></td></tr></table></figure>
<p>很明显最后一行的值没对上,l的值是有问题的,为啥捏??<br>单独输出一行</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cout<<(number-a*100-b*50-c*20-d*10-e*5-f*2-g*1-h*0.5-i*0.25-j*0.1-k*0.05)*100/1;</span><br></pre></td></tr></table></figure>
<p>结果是</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1</span><br></pre></td></tr></table></figure>
<p><strong>我直接黑人问号????what’s your problem ??</strong><br>想了想,l是int型变量,而(number-a<em>100-b</em>50-c<em>20-d</em>10-e<em>5-f</em>2-g<em>1-h</em>0.5-i<em>0.25-j</em>0.1-k*0.05)*100/1应该是float型;<br>强制转换一下输出</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cout<<(int)((number-a*100-b*50-c*20-d*10-e*5-f*2-g*1-h*0.5-i*0.25-j*0.1-k*0.05)*100/1);</span><br></pre></td></tr></table></figure>
<p>结果为</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">0</span><br></pre></td></tr></table></figure>
<p>好家伙,问题果然出在这儿;<br>float的1转换为int居然变成0了?为啥啊??应该是储存的问题,原谅我不考计组==<br>那么精度丢失只影响最后一位。我们不定义l;直接输出l试试:<br>测试成功<br>这会儿倒是没问题了,但是又有新的问题出现了==<br>样例输入:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">2214.9</span><br></pre></td></tr></table></figure>
<p>输出:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">NOTAS:</span><br><span class="line">22 nota(s) de R$ 100.00</span><br><span class="line">0 nota(s) de R$ 50.00</span><br><span class="line">0 nota(s) de R$ 20.00</span><br><span class="line">1 nota(s) de R$ 10.00</span><br><span class="line">0 nota(s) de R$ 5.00</span><br><span class="line">2 nota(s) de R$ 2.00</span><br><span class="line">MOEDAS:</span><br><span class="line">0 moeda(s) de R$ 1.00</span><br><span class="line">1 moeda(s) de R$ 0.50</span><br><span class="line">1 moeda(s) de R$ 0.25</span><br><span class="line">1 moeda(s) de R$ 0.10</span><br><span class="line">1 moeda(s) de R$ 0.05</span><br><span class="line">9.09411e-12 moeda(s) de R$ 0.01</span><br></pre></td></tr></table></figure>
<p>最后一行直接芜湖起飞好吧==一看就是精度的问题<br>人麻了==我直接大换血,整数部分不变,小数部分开刀</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> double number;</span><br><span class="line"> cin>>number;</span><br><span class="line"> int a,b,c,d,e,f,g,h,i,j,k,l;</span><br><span class="line"> a=number/100;</span><br><span class="line"> b=(number-a*100)/50;</span><br><span class="line"> c=(number-a*100-b*50)/20;</span><br><span class="line"> d=(number-a*100-b*50-c*20)/10;</span><br><span class="line"> e=(number-a*100-b*50-c*20-d*10)/5;</span><br><span class="line"> f=(number-a*100-b*50-c*20-d*10-e*5)/2;</span><br><span class="line"> cout<<"NOTAS:"<<endl;</span><br><span class="line"> cout<<a<<" nota(s) de R$ 100.00"<<endl;</span><br><span class="line"> cout<<b<<" nota(s) de R$ 50.00"<<endl;</span><br><span class="line"> cout<<c<<" nota(s) de R$ 20.00"<<endl;</span><br><span class="line"> cout<<d<<" nota(s) de R$ 10.00"<<endl;</span><br><span class="line"> cout<<e<<" nota(s) de R$ 5.00"<<endl;</span><br><span class="line"> cout<<f<<" nota(s) de R$ 2.00"<<endl;</span><br><span class="line"> cout<<"MOEDAS:"<<endl;</span><br><span class="line"> g=(number-a*100-b*50-c*20-d*10-e*5-f*2)/1;</span><br><span class="line"> h=(number*100-a*10000-b*5000-c*2000-d*1000-e*500-f*200-g*100)/50;</span><br><span class="line"> i=(number*100-a*10000-b*5000-c*2000-d*1000-e*500-f*200-g*100-h*50)/25;</span><br><span class="line"> j=(number*100-a*10000-b*5000-c*2000-d*1000-e*500-f*200-g*100-h*50-i*25)/10;</span><br><span class="line"> k=(number*100-a*10000-b*5000-c*2000-d*1000-e*500-f*200-g*100-h*50-i*25-j*10)/5;</span><br><span class="line"> l=(number*100-a*10000-b*5000-c*2000-d*1000-e*500-f*200-g*100-h*50-i*25-j*10-k*5)/1;</span><br><span class="line"> cout<<g<<" moeda(s) de R$ 1.00"<<endl;</span><br><span class="line"> cout<<h<<" moeda(s) de R$ 0.50"<<endl;</span><br><span class="line"> cout<<i<<" moeda(s) de R$ 0.25"<<endl;</span><br><span class="line"> cout<<j<<" moeda(s) de R$ 0.10"<<endl;</span><br><span class="line"> cout<<k<<" moeda(s) de R$ 0.05"<<endl;</span><br><span class="line"> cout<<l<<" moeda(s) de R$ 0.01"<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>测试成功,<em><strong>血的教训!!不要TM玩小数,能用整数的地方坚决避免出现小数!!</strong></em></p>
<h1 id="AcWing-725-完全数"><a href="#AcWing-725-完全数" class="headerlink" title="AcWing 725. 完全数"></a>AcWing 725. 完全数</h1><p>源代码:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include<iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> int n,sum=0;</span><br><span class="line"> long long a;</span><br><span class="line"> cin>>n;</span><br><span class="line"> for(int i=0;i<n;i++)</span><br><span class="line"> {</span><br><span class="line"> cin>>a;</span><br><span class="line"> for(long long j=1;j<a;j++)</span><br><span class="line"> {</span><br><span class="line"> if(a%j==0)</span><br><span class="line"> sum+=j;</span><br><span class="line"> }</span><br><span class="line"> if(sum==a)</span><br><span class="line"> cout<<a<<" is perfect"<<endl;</span><br><span class="line"> else</span><br><span class="line"> cout<<a<<" is not perfect"<<endl;</span><br><span class="line"> sum=0;</span><br><span class="line"> }</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>测试输入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">3</span><br><span class="line">6</span><br><span class="line">5</span><br><span class="line">28</span><br></pre></td></tr></table></figure>
<p>输出</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">6 is perfect</span><br><span class="line">5 is not perfect</span><br><span class="line">28 is perfect</span><br></pre></td></tr></table></figure>
<p>没毛病,提交后显示Time Limit Exceeded ,超时了,第一次遇到,估计是两层嵌套时间复杂度太高了<br>看看讲解<br>如果d能整除x,即d%x==0;则d/x也能整除x,即d%(d/x)==0;<br>例如12%2==0;12%(12/2)==12%6==0;<br>所以<br>约数从小往大取,3是12的一个约数,12/3==4是另一个约数<br>假设i是一个约数,则n/i>=i<br>即i²<=n<br>即i<=sqrt(n)</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include<iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> int a,n,sum=0;</span><br><span class="line"> </span><br><span class="line"> cin>>n;</span><br><span class="line"> for(int i=0;i<n;i++)</span><br><span class="line"> {</span><br><span class="line"> cin>>a;</span><br><span class="line"> if(a==1)</span><br><span class="line"> cout<<"1 is not perfect"<<endl;</span><br><span class="line"> else </span><br><span class="line"> {</span><br><span class="line"> for(int j=1;j*j<=a;j++)</span><br><span class="line"> {</span><br><span class="line"> if(a%j==0)</span><br><span class="line"> sum=j+(a/j)+sum;</span><br><span class="line"> }</span><br><span class="line"> if(sum==a*2)</span><br><span class="line"> cout<<a<<" is perfect"<<endl;</span><br><span class="line"> else</span><br><span class="line"> cout<<a<<" is not perfect"<<endl;</span><br><span class="line"> sum=0;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面是正确代码,说一下需要注意的点<br>本题的思想是:找到一个约数就等于找到了两个约数,这样可以减小时间复杂度<br>比如看28是不是完全数,找约数:<br>1,28<br>2,14<br>4,7<br>找三次就能找到所有约数,再加起来,注意这里把所有的约数加上的话把本身28也加上去了,所以最后要除2</p>
]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
<tag>错题</tag>
</tags>
</entry>
<entry>
<title>AcWing语法基础课笔记</title>
<url>/2022/01/27/AcWing%E8%AF%AD%E6%B3%95%E5%9F%BA%E7%A1%80%E8%AF%BE%E7%AC%94%E8%AE%B0/</url>
<content><![CDATA[<h1 id="1、编译环境"><a href="#1、编译环境" class="headerlink" title="1、编译环境"></a><strong>1、编译环境</strong></h1><p>采用线上编译,地址:<br><a href="https://www.acwing.com/file_system/file/content/whole/index/content/172575/">https://www.acwing.com/file_system/file/content/whole/index/content/172575/</a></p>
<p>输出一个hello world</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> cout <<"Hello World" << endl;</span><br><span class="line"></span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>先保存代码再点调试运行</p>
<h1 id="2、C-代码一般格式"><a href="#2、C-代码一般格式" class="headerlink" title="2、C++代码一般格式"></a><strong>2、C++代码一般格式</strong></h1><h2 id="2-1常用头文件"><a href="#2-1常用头文件" class="headerlink" title="2.1常用头文件"></a><strong>2.1常用头文件</strong></h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <cstdio></span><br><span class="line">#include <iostream></span><br></pre></td></tr></table></figure>
<p>cstdio中包含printf和scanf<br>iostream中包含 cin和cout</p>
<h2 id="2-2、using-namespace-std使用std命名空间"><a href="#2-2、using-namespace-std使用std命名空间" class="headerlink" title="2.2、using namespace std使用std命名空间"></a><strong>2.2、using namespace std使用std命名空间</strong></h2><p>删掉这一句编译会报错</p>
<h2 id="2-3、int-main-函数入口"><a href="#2-3、int-main-函数入口" class="headerlink" title="2.3、int main()函数入口"></a><strong>2.3、int main()函数入口</strong></h2><h2 id="2-4、最后要return-0"><a href="#2-4、最后要return-0" class="headerlink" title="2.4、最后要return 0"></a><strong>2.4、最后要return 0</strong></h2><h1 id="3、作业和提交"><a href="#3、作业和提交" class="headerlink" title="3、作业和提交"></a><strong>3、作业和提交</strong></h1><p>作业要刷完嗷<br><a href="https://www.acwing.com/activity/content/punch_the_clock/21/">https://www.acwing.com/activity/content/punch_the_clock/21/</a></p>
<h1 id="4、C-基本语法"><a href="#4、C-基本语法" class="headerlink" title="4、C++基本语法"></a><strong>4、C++基本语法</strong></h1><p>变量类型:<br>bool(true=1和false=0) 1B<br>char ‘c’,’a’,’’,’\n’ 1B<br>int (-2^31至+2^31-1) 4B<br>float 1.23 1.23e2(支持科学计数法) 有效位数6~7位 4B<br>double 15至16位有效数字 8B<br>long long (-2^63至263-1) 8B<br>long double 18至19位 12B/16B<br>1Byte=8bit</p>
<h2 id="4-1定义变量"><a href="#4-1定义变量" class="headerlink" title="4.1定义变量"></a>4.1定义变量</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int a,b=2,c=b;</span><br><span class="line">float d=1.5,e=1,f=1.235;</span><br><span class="line">bool g=true,h=false;</span><br><span class="line">char j='a',k ='b';</span><br><span class="line">long long 1=1231341;</span><br><span class="line">long double m=123.45;</span><br></pre></td></tr></table></figure>
<h2 id="4-2变量输出"><a href="#4-2变量输出" class="headerlink" title="4.2变量输出"></a>4.2变量输出</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int a, b;</span><br><span class="line">cin >> a >> b;//输入</span><br><span class="line">cout << a+b <<endl;</span><br></pre></td></tr></table></figure>
<p>注意,提交的时候要先输入两个数据,再点运行<br>Accepted通过<br>Wrong Answer 答案错误<br>Time Limit Error 超时<br>Memory Limit Error 超内存<br>Segmentation Fault 数组越界<br>Compile Error 编译错误<br>Presentation Error 表示错误<br>注意打卡界面是markdown语法代码前面记得加4个`</p>
<h3 id="4-2-1输出多个变量"><a href="#4-2-1输出多个变量" class="headerlink" title="4.2.1输出多个变量"></a>4.2.1输出多个变量</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int a, b;</span><br><span class="line">cin >> a >> b;//输入</span><br><span class="line">cout << a+b << ' ' << a*b <<endl;</span><br></pre></td></tr></table></figure>
<h2 id="4-3-scanf和printf"><a href="#4-3-scanf和printf" class="headerlink" title="4.3 scanf和printf"></a>4.3 scanf和printf</h2><p>这两个函数是在 cstdio.h中的,记得加头文件</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <cstdio></span><br><span class="line">#include <iostream></span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line">int main ()</span><br><span class="line">{</span><br><span class="line"> int a,b;</span><br><span class="line"> scanf("%d%d",&a,&b);</span><br><span class="line"> printf("a+b=%d\na*b=%d\n",a+b,a*b);</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>整数%d,浮点数%f,%.2f表示保留两位小数<br>字符%c,double %lf, long long %ll</p>
<h2 id="4-4关于空格的读入"><a href="#4-4关于空格的读入" class="headerlink" title="4.4关于空格的读入"></a>4.4关于空格的读入</h2><p>1.cin</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cin >> a >> b;</span><br><span class="line">printf ("%c %c",a,b);</span><br><span class="line">//输入a c输出a c</span><br></pre></td></tr></table></figure>
<p>2.scanf</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">scanf("%c%c",&a,&b);</span><br><span class="line">printf("%c %c",a,b);</span><br><span class="line">//输入ac输出a c</span><br><span class="line">//输入a c输出a </span><br></pre></td></tr></table></figure>
<p>可见cin可以自动过滤空格,且cin、cout的效率比scanf、printf低</p>
<h2 id="4-5四则运算"><a href="#4-5四则运算" class="headerlink" title="4.5四则运算"></a>4.5四则运算</h2><p>加法a+b<br>出发a-b<br>乘法a*b<br>除法a/b<br>取余a%b 注意取余结果的符号只和前面有关 </p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int a=5,b=-2;</span><br><span class="line">printf("%d",a%b);</span><br><span class="line">return 0;</span><br></pre></td></tr></table></figure>
<p>结果为1</p>
<h2 id="4-6变量类型强制转换"><a href="#4-6变量类型强制转换" class="headerlink" title="4.6变量类型强制转换"></a>4.6变量类型强制转换</h2><p>int→float<br>float→int(下取整,损失精度)<br>int←→char(ASCII码)</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">char c='A';</span><br><span class="line">cout << (char)(c+32)<<endl; </span><br></pre></td></tr></table></figure>
<p>输出结果为a</p>
<p>两个不同类型的变量之间运算,低精度会向精度高的一方转换</p>
<h1 id="5、printf和c-中的判断结构"><a href="#5、printf和c-中的判断结构" class="headerlink" title="5、printf和c++中的判断结构"></a><strong>5、printf和c++中的判断结构</strong></h1><h2 id="5-1-printf的用法"><a href="#5-1-printf的用法" class="headerlink" title="5.1 printf的用法"></a>5.1 printf的用法</h2><p>printf的格式化输出:想让每个输出占的宽度一定该怎么办?</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> int a=1;</span><br><span class="line"> int b=12;</span><br><span class="line"> int c=123;</span><br><span class="line"> int d=123456;</span><br><span class="line"> printf("%5d\n",a);</span><br><span class="line"> printf("%5d\n",b);</span><br><span class="line"> printf("%5d\n",c);</span><br><span class="line"> printf("%5d\n",d);</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>输出结果:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"> 1</span><br><span class="line"> 12</span><br><span class="line"> 123</span><br><span class="line">123456</span><br></pre></td></tr></table></figure>
<p>可以发现结果是不足五个字符的话前面补空格,超出了不管,全部输出</p>
<p>如果想要左对齐怎么办?<br>将</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">printf("%5d\n",a);</span><br></pre></td></tr></table></figure>
<p>修改为:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">printf("%-5d\n",a);</span><br></pre></td></tr></table></figure>
<p>输出结果:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1 </span><br><span class="line">12 </span><br><span class="line">123 </span><br><span class="line">123456</span><br></pre></td></tr></table></figure>
<p>现在是不足五位补空格,如果我们想补0该怎么办?<br>把</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">printf("%5d\n",a);</span><br></pre></td></tr></table></figure>
<p>改为:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">printf("%05d\n",a);</span><br></pre></td></tr></table></figure>
<p>输出结果:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">00001</span><br><span class="line">00012</span><br><span class="line">00123</span><br><span class="line">123456</span><br></pre></td></tr></table></figure>
<h2 id="5-2浮点数如何固定长度?"><a href="#5-2浮点数如何固定长度?" class="headerlink" title="5.2浮点数如何固定长度?"></a>5.2浮点数如何固定长度?</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">double f = 12.34;</span><br><span class="line">printf("%05.1lf",f);</span><br></pre></td></tr></table></figure>
<p>输出为:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">012.3</span><br></pre></td></tr></table></figure>
<p>%05.1lf中的这一句,0表示的是不足5位的占零;5表示要输出5位;.1表示的是小数点后保留一位,lf是double对应的输出;<br>注意:小数点要占一位</p>
<h2 id="5-3-if语句"><a href="#5-3-if语句" class="headerlink" title="5.3 if语句"></a>5.3 if语句</h2><p>基本的if语法:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> int score;</span><br><span class="line"> cin >> score;</span><br><span class="line"> if (score>=60)</span><br><span class="line"> {</span><br><span class="line"> cout << "PASS" << endl;;</span><br><span class="line"> }</span><br><span class="line"> else</span><br><span class="line"> {</span><br><span class="line"> cout << "FAILED" << endl;</span><br><span class="line"> }</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>注意,if和else之后不要加分号,加了有空语句<br>if和else后面只有一个句子的时候可以省略空格;</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">if (score>=60)</span><br><span class="line"> cout << "PASS" << endl;</span><br><span class="line"> else</span><br><span class="line"> cout << "FAILED" << endl;</span><br><span class="line"> return 0;</span><br></pre></td></tr></table></figure>
<h2 id="5-4判断语句的表示方式"><a href="#5-4判断语句的表示方式" class="headerlink" title="5.4判断语句的表示方式"></a>5.4判断语句的表示方式</h2><p>大于等于:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">大于 ></span><br><span class="line">小于 <</span><br><span class="line">大于等于 >=</span><br><span class="line">小于等于 <=</span><br><span class="line">等于 ==</span><br><span class="line">不等于 !=</span><br></pre></td></tr></table></figure>
<h2 id="5-5-else-if-的由来"><a href="#5-5-else-if-的由来" class="headerlink" title="5.5 else if 的由来"></a>5.5 else if 的由来</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cin score;</span><br><span class="line">if (a > 90)</span><br><span class="line"> cout << "great" ;</span><br><span class="line">else</span><br><span class="line"> if (a > 80)</span><br><span class="line"> cout << "good" ;</span><br><span class="line"> else</span><br><span class="line"> if (a > 70) </span><br><span class="line"> cout << "normal" ;</span><br><span class="line"> else</span><br><span class="line"> if (a > 60)</span><br><span class="line"> cout << "pass" ;</span><br><span class="line"> else</span><br><span class="line"> cout << "failed" ;</span><br></pre></td></tr></table></figure>
<p>删去不必要的空格就能得到 if else</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cin score;</span><br><span class="line">if (a > 90)</span><br><span class="line"> cout << "great" ;</span><br><span class="line">else if (a > 80)</span><br><span class="line"> cout << "good" ;</span><br><span class="line">else if (a > 70) </span><br><span class="line"> cout << "normal" ;</span><br><span class="line">else if (a > 60)</span><br><span class="line"> cout << "pass" ;</span><br><span class="line">else</span><br><span class="line"> cout << "failed" ;</span><br></pre></td></tr></table></figure>
<h2 id="5-6-一道练习题"><a href="#5-6-一道练习题" class="headerlink" title="5.6 一道练习题"></a>5.6 一道练习题</h2><pre><code>简单计算器输入两个数,以及一个运算符+-*/
输出两个数运算的结果
运算符为/且被除数为0时输出 Divided by zero !
运算符不是+-*/时输出 Invalid Operator
</code></pre>
<p>源代码:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> double a,b;</span><br><span class="line"> char c;</span><br><span class="line"> cin >> a >> c >> b;</span><br><span class="line"> if (c=='+')</span><br><span class="line"> printf("%.1lf+%.1lf=%.1lf",&a,&b,&(a+b));</span><br><span class="line"> else if (c=='-')</span><br><span class="line"> printf("%.1lf-%.1lf=%.1lf",&a,&b,&(a-b));</span><br><span class="line"> else if (c=='*')</span><br><span class="line"> printf("%.1lf*%.1lf=%.1lf",&a,&b,&(a*b));</span><br><span class="line"> else if (c=='/')</span><br><span class="line"> {</span><br><span class="line"> if (b!=0)</span><br><span class="line"> printf("%.1lf/%.1lf=%.1lf",&a,&b,&(a/b));</span><br><span class="line"> else</span><br><span class="line"> printf("Divided by zero !");</span><br><span class="line"> }</span><br><span class="line"> else</span><br><span class="line"> printf("Invalid Operator");</span><br><span class="line"> </span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>出现报错:<br>a.cpp:11:43: error: lvalue required as unary ‘&’ operand<br> 11 | printf(“%.1lf+%.1lf=%.1lf”,&a,&b,&(a+b));<br>报错是因为 & 只能作用在变量上,不能作用在表达式上<br>我们多定义一个变量d来存放运算值</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> double a,b,d;</span><br><span class="line"> char c;</span><br><span class="line"> cin >> a >> c >> b;</span><br><span class="line"> if (c=='+')</span><br><span class="line"> {</span><br><span class="line"> d=a+b;</span><br><span class="line"> printf("%.1lf+%.1lf=%.1lf",&a,&b,&d);</span><br><span class="line"> }</span><br><span class="line"> else if (c=='-')</span><br><span class="line"> {</span><br><span class="line"> d=a-b;</span><br><span class="line"> printf("%.1lf-%.1lf=%.1lf",&a,&b,&d);</span><br><span class="line"> }</span><br><span class="line"> else if (c=='*')</span><br><span class="line"> {</span><br><span class="line"> d=a*b;</span><br><span class="line"> printf("%.1lf*%.1lf=%.1lf",&a,&b,&d);</span><br><span class="line"> }</span><br><span class="line"> else if (c=='/')</span><br><span class="line"> {</span><br><span class="line"> if (b!=0)</span><br><span class="line"> {</span><br><span class="line"> d=a/b;</span><br><span class="line"> printf("%.1lf/%.1lf=%.1lf",&a,&b,&d);</span><br><span class="line"> }</span><br><span class="line"> else</span><br><span class="line"> printf("Divided by zero !");</span><br><span class="line"> }</span><br><span class="line"> else</span><br><span class="line"> printf("Invalid Operator");</span><br><span class="line"> </span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>输入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">12/41</span><br></pre></td></tr></table></figure>
<p>输出:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">0.0/0.0=0.0</span><br></pre></td></tr></table></figure>
<p>感觉是cin出了问题,用scanf看看</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">scanf("%lf%c%lf",&a,&c,&b);</span><br></pre></td></tr></table></figure>
<p>输出还是一样<br>我们在scanf后面加一句</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cout<<a<<c<<b<<endl;</span><br></pre></td></tr></table></figure>
<p>输入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">12+14</span><br></pre></td></tr></table></figure>
<p>输出结果</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">12+14</span><br><span class="line">0.0+0.0=0.0</span><br></pre></td></tr></table></figure>
<p>那问题知道出在哪儿了,就在printf的语句上,重新看了一下printf语法,···printf的变量不用&,···全部搞忘了,cout害人不浅啊,重新修改如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> double a,b;</span><br><span class="line"> char c;</span><br><span class="line"> scanf("%lf%c%lf",&a,&c,&b);</span><br><span class="line"> if (c=='+')</span><br><span class="line"> printf("%.1lf+%.1lf=%.1lf",a,b,a+b);</span><br><span class="line"> else if (c=='-')</span><br><span class="line"> printf("%.1lf-%.1lf=%.1lf",a,b,a-b);</span><br><span class="line"> else if (c=='*')</span><br><span class="line"> printf("%.1lf*%.1lf=%.1lf",a,b,a*b);</span><br><span class="line"> else if (c=='/')</span><br><span class="line"> {</span><br><span class="line"> if (b!=0)</span><br><span class="line"> printf("%.1lf/%.1lf=%.1lf",a,b,a/b);</span><br><span class="line"> else</span><br><span class="line"> printf("Divided by zero !");</span><br><span class="line"> }</span><br><span class="line"> else</span><br><span class="line"> printf("Invalid Operator");</span><br><span class="line"> </span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>输入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1.21334*14134.51532</span><br></pre></td></tr></table></figure>
<p>输出</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1.2*14134.5=17150.0</span><br></pre></td></tr></table></figure>
<h1 id="6、循环语句"><a href="#6、循环语句" class="headerlink" title="6、循环语句"></a><strong>6、循环语句</strong></h1><p>斐波那契数列<br>f(1)=1,f(2)=1<br>f(n)=f(n-1)+f(n-2)<br>求f(n)<br>令a=f(1),b=f(2),c=f(3)=a+b,b+c作为f(4)的值代替a+b<br>从(a,b)→(b,c)需要:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">a=b;b=c;</span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="center">循环次数</th>
<th align="center">a</th>
<th align="center">b</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">f(1)</td>
<td align="center">f(2)</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">f(2)</td>
<td align="center">f(3)</td>
</tr>
<tr>
<td align="center">2</td>
<td align="center">f(3)</td>
<td align="center">f(4)</td>
</tr>
<tr>
<td align="center">…</td>
<td align="center">…</td>
<td align="center">…</td>
</tr>
<tr>
<td align="center">n-1</td>
<td align="center">f(n)</td>
<td align="center">f(n+1)</td>
</tr>
</tbody></table>
<p><strong>求斐波那契数列的第n项</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> int a=1,b=1;</span><br><span class="line"> int n;</span><br><span class="line"> cin>>n;</span><br><span class="line"> int i=0</span><br><span class="line"> while(i<n-1)</span><br><span class="line"> {</span><br><span class="line"> int c=a+b;</span><br><span class="line"> a=b;</span><br><span class="line"> b=c;</span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line"> cout<<a<<endl;</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>循环的效率比递归更高</p>
<h2 id="6-1-break和continue"><a href="#6-1-break和continue" class="headerlink" title="6.1 break和continue"></a>6.1 break和continue</h2><p>break是直接跳过整个循环开始执行下面的语句,continue是跳过本次循环的后面内容,开始下次循环</p>
<h2 id="6-2-打印菱形程序的快速方法"><a href="#6-2-打印菱形程序的快速方法" class="headerlink" title="6.2 打印菱形程序的快速方法"></a>6.2 打印菱形程序的快速方法</h2><p>打印</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"> * </span><br><span class="line"> *** </span><br><span class="line">*****</span><br><span class="line"> *** </span><br><span class="line"> * </span><br></pre></td></tr></table></figure>
<p>整个图形其实是一个n*n的正方形,有n²个元素,找到中心点<br>两点之间的距离是sqrt((x1-x2)²+(y1-y2)²)<br>曼哈顿距离:|x1-x2|+|y1-y2| (横坐标差值绝对值+纵坐标差值绝对值)<br>我们可以看到所有要打印的菱形距离中心点的曼哈顿距离都<=(n/2),n为阶数</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <iostream></span><br><span class="line">using namespace std;</span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> int n;</span><br><span class="line"> cin>>n;</span><br><span class="line"> int cx = n/2;</span><br><span class="line"> int cy = n/2;</span><br><span class="line"> for(int i = 0; i < n ; i++)</span><br><span class="line"> { </span><br><span class="line"> for(int j = 0; j < n ; j++)</span><br><span class="line"> {</span><br><span class="line"> if ((abs(i-cx)+abs(j-cy))<=n/2)</span><br><span class="line"> cout << "*" ;</span><br><span class="line"> else </span><br><span class="line"> cout << " "> ; </span><br><span class="line"> }</span><br><span class="line"> cout<<endl;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="6-4-while-if-for语句的多语句-和cin混搭"><a href="#6-4-while-if-for语句的多语句-和cin混搭" class="headerlink" title="6.4 while;if;for语句的多语句(和cin混搭)"></a>6.4 while;if;for语句的多语句(和cin混搭)</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">while(cin>>n,n<=0>)</span><br><span class="line">if(cin>>n,n<=0>)</span><br><span class="line">for(;cin>>n,n<=0>;)</span><br></pre></td></tr></table></figure>
<p>像这行代码,判断句里面有两个语句,最终是由最后一个语句来判断条件<br>所以可以一直执行输入数据的操作<br>cin只要有输入就会返回true</p>
<h2 id="6-4-scanf在读入数据时不会自动过滤空格"><a href="#6-4-scanf在读入数据时不会自动过滤空格" class="headerlink" title="6.4 scanf在读入数据时不会自动过滤空格"></a>6.4 scanf在读入数据时不会自动过滤空格</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cin>>a>>b;</span><br><span class="line">scanf("%d%d",&a,&b);</span><br></pre></td></tr></table></figure>
<p>输入数据为</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">6 1</span><br></pre></td></tr></table></figure>
<p>cin输入的是6和1<br>scanf输入的其实是6和空格,所以需要调整</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">scanf("%d %d",&a,&b);</span><br></pre></td></tr></table></figure>
<h2 id="6-5scanf的格式化读入"><a href="#6-5scanf的格式化读入" class="headerlink" title="6.5scanf的格式化读入"></a>6.5scanf的格式化读入</h2><p>f是format的意思,例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">scanf("A=%d,B=%d",&a,&b);</span><br><span class="line">cout<<a<<""<<b;</span><br></pre></td></tr></table></figure>
<p>输入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">A=3,B=2</span><br></pre></td></tr></table></figure>
<p>输出</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">3 2</span><br></pre></td></tr></table></figure>
<h1 id="7、数组"><a href="#7、数组" class="headerlink" title="7、数组"></a><strong>7、数组</strong></h1><h2 id="7-1数组初始化"><a href="#7-1数组初始化" class="headerlink" title="7.1数组初始化"></a>7.1数组初始化</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int a[3]={0,1,2};</span><br><span class="line">int b[]={0,1,2};</span><br><span class="line">int c[5]={0,1,1};//定义了一个长度为5的数组,前三个为0,1,1后面的为0</span><br><span class="line">char d[]={'a','b','c'};</span><br><span class="line">int f[10]={0};//常用的初始化方式</span><br></pre></td></tr></table></figure>
<h2 id="7-2数组长度过长的问题"><a href="#7-2数组长度过长的问题" class="headerlink" title="7.2数组长度过长的问题"></a>7.2数组长度过长的问题</h2><p>函数内部的变量(数组)会放进栈空间,栈空间一般默认为1M<br>如果在main函数内部定义int a[1000000]<br>则会占用 1000000*4=4000000B≈4000KB≈4MB<br>栈空间不足会报错<br>解决方法:定义在main函数外面,此时占用的是堆空间,没有限制</p>
<h2 id="7-3旋转数组"><a href="#7-3旋转数组" class="headerlink" title="7.3旋转数组"></a>7.3旋转数组</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int a[5]={0,1,2,3,4};</span><br></pre></td></tr></table></figure>
<p>反转两次后的得到{3,4,0,1,2}<br>如何反转最快?<br>长度为n的数组翻转k次<br>把原数组分为a[0]~a[n-1-k]和a[n-k]到a[n-1]两个部分<br>再把整个数组翻转得到a[n-1] a[n-2] a[n-3]…a[2] a[1] a[0]<br>再分别反转两个部分 得到 a[n-k] a[n-k+1]…a[n-1] a[0] a[1]…a[n-1-k]<br>reverse函数在algorithm头文件里面,reverse(a,b)是翻转从a到b的数 </p>
<h2 id="7-4浮点数的比较"><a href="#7-4浮点数的比较" class="headerlink" title="7.4浮点数的比较"></a>7.4浮点数的比较</h2><p>浮点数的存储不是精确的,一般认为x和y相差小于1e-6则相等</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include<iostream></span><br><span class="line">using namespace std;</span><br><span class="line">const double eps = 1e-6</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> double x=1.23456789;</span><br><span class="line"> double a=x*x;</span><br><span class="line"> double b=sqrt(a);</span><br><span class="line"> if (fabs(x-b)<eps) puts("相等");//fabs是取绝对值的函数</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="7-5求2的n次方存在数组中"><a href="#7-5求2的n次方存在数组中" class="headerlink" title="7.5求2的n次方存在数组中"></a>7.5求2的n次方存在数组中</h2><p>比如把一个数 1234567891234 存进数组中,一般把从低往高存,这样方便加减乘运算时的进位运算<br>所以实际上应该是对应a[12]…a[4]a[3]a[2]a[1]a[0]<br>假如A[]={4,3,2,1,9,8,7,6,5,4,3,2,1}<br>数组可以很方便的进行大数据的加减乘<br>比如1234567891234<em>2的操作<br>可以对每一位分别</em>2,用一个新数组B[]来存储结果</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">B[0]=A[0]*2%10;</span><br></pre></td></tr></table></figure>
<p>用一个变量t来存储进位 </p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">t=A[0]*2/10;</span><br></pre></td></tr></table></figure>
<p>则</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">B[1]=(A[1]*2%+t)%10;</span><br><span class="line"> t=(B[1]*2+t)/10;</span><br></pre></td></tr></table></figure>
<p>直到</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">B[n-1]=(A[n-1]*2%+t)%10;</span><br><span class="line"> t=(B[n-1]*2+t)/10;</span><br></pre></td></tr></table></figure>
<p>最后还有可能会进位</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">B[n]=t;</span><br></pre></td></tr></table></figure>
<h2 id="7-4-高精度2的N次幂"><a href="#7-4-高精度2的N次幂" class="headerlink" title="7.4 高精度2的N次幂"></a>7.4 高精度2的N次幂</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include<iostream></span><br><span class="line">using namespace std;</span><br><span class="line">const int N =3010</span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> </span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>算法</tag>
</tags>
</entry>
<entry>
<title>Hexo,Next主题优化</title>
<url>/2022/01/26/Hexo-Next%E4%B8%BB%E9%A2%98%E4%BC%98%E5%8C%96/</url>
<content><![CDATA[<h1 id="设置hexo首页只显示部分摘要(不显示全文)"><a href="#设置hexo首页只显示部分摘要(不显示全文)" class="headerlink" title="设置hexo首页只显示部分摘要(不显示全文)"></a>设置hexo首页只显示部分摘要(不显示全文)</h1><blockquote>
<p>摘自<a href="https://blog.csdn.net/yueyue200830/article/details/104470646?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164319697016780271536156%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164319697016780271536156&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-104470646.first_rank_v2_pc_rank_v29&utm_term=hexo%E9%A6%96%E9%A1%B5%E4%B8%8D%E6%98%BE%E7%A4%BA%E6%96%B0%E6%96%87%E7%AB%A0&spm=1018.2226.3001.4187">https://blog.csdn.net/yueyue200830/article/details/104470646?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164319697016780271536156%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164319697016780271536156&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-104470646.first_rank_v2_pc_rank_v29&utm_term=hexo%E9%A6%96%E9%A1%B5%E4%B8%8D%E6%98%BE%E7%A4%BA%E6%96%B0%E6%96%87%E7%AB%A0&spm=1018.2226.3001.4187</a></p>
</blockquote>
<p>本文针对Next主题,不确保对于其它主题有效(但从修改模式来看,是有效的)<br>Next默认是会显示全文的,这样显然很不方便,因此需要一些方法去只显示前面一部分</p>
<h1 id="修改配置"><a href="#修改配置" class="headerlink" title="修改配置"></a>修改配置</h1><p>首先需要在Next主题的_config.yml中把设置打开:(默认安装时就打开了)</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># Automatically excerpt description in homepage as preamble text.</span><br><span class="line">excerpt_description: true</span><br></pre></td></tr></table></figure>
<h1 id="Two-methods"><a href="#Two-methods" class="headerlink" title="Two methods"></a>Two methods</h1><h2 id="方法一:写概述"><a href="#方法一:写概述" class="headerlink" title="方法一:写概述"></a>方法一:写概述</h2><p>在文章的front-matter中添加description,其中description中的内容就会被显示在首页上,其余一律不显示。<br>exp:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">title: 让首页显示部分内容</span><br><span class="line">date: 2020-02-23 22:55:10</span><br><span class="line">description: 这是显示在首页的概述,正文内容均会被隐藏。</span><br></pre></td></tr></table></figure>
<p>测试成功</p>
<h2 id="方法二:文章截断"><a href="#方法二:文章截断" class="headerlink" title="方法二:文章截断"></a>方法二:文章截断</h2><p>在需要截断的地方加入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><!--more--></span><br></pre></td></tr></table></figure>
<p>首页就会显示这条以上的所有内容,隐藏接下来的所有内容。</p>
]]></content>
<categories>
<category>博客搭建</category>
</categories>
<tags>
<tag>Hexo</tag>
<tag>博客搭建</tag>
</tags>
</entry>
<entry>
<title>MarkDown语法</title>
<url>/2022/01/25/Markdown%E8%AF%AD%E6%B3%95/</url>
<content><![CDATA[<h1 id="标题"><a href="#标题" class="headerlink" title="标题"></a><strong>标题</strong></h1><p>有几级标题用几个#</p>
<h1 id="字体"><a href="#字体" class="headerlink" title="字体"></a><strong>字体</strong></h1><h2 id="加粗-要加粗的文字左右分别用两个-号包起来"><a href="#加粗-要加粗的文字左右分别用两个-号包起来" class="headerlink" title="加粗 要加粗的文字左右分别用两个*号包起来"></a>加粗 要加粗的文字左右分别用两个*号包起来</h2><p>未加粗 <strong>加粗</strong> </p>
<h2 id="斜体-要倾斜和加粗的文字左右分别用三个-号包起来"><a href="#斜体-要倾斜和加粗的文字左右分别用三个-号包起来" class="headerlink" title="斜体 要倾斜和加粗的文字左右分别用三个*号包起来"></a>斜体 要倾斜和加粗的文字左右分别用三个*号包起来</h2><p>非斜体 <em>斜体</em></p>
<h2 id="套娃"><a href="#套娃" class="headerlink" title="套娃"></a>套娃</h2><p>三个*号连用就是斜体加粗了 <em><strong>斜体加粗</strong></em></p>
<h1 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h1><p>要应用的话前面加个>符号,引用完了空一行<br>Test</p>
<blockquote>
<p>搞咩啊?</p>
</blockquote>
<p>测试成功。但是感觉这个功能有点多余,毕竟我是尊贵的白嫖党,拿来主义</p>
<h1 id="插入代码段"><a href="#插入代码段" class="headerlink" title="插入代码段"></a><strong>插入代码段</strong></h1><p>How to insert a code segement?<br>在Markdown中添加代码只需要在代码的前边一行与后边一行分别添加````,并且在前边一行添加语言标识的关键字。(键盘左上角,ESC下面那个键~)<br>Test:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Printf("Hello World!")</span><br></pre></td></tr></table></figure>
<p>测试成功</p>
<h1 id="插入表格"><a href="#插入表格" class="headerlink" title="插入表格"></a>插入表格</h1><p>Markdown 制作表格使用 | 来分隔不同的单元格,使用 - 来分隔表头和其他行。</p>
<p>语法格式如下:</p>
<h2 id="极简模式"><a href="#极简模式" class="headerlink" title="极简模式"></a>极简模式</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">水果名称| 价格 | 数量 </span><br><span class="line">-|-|-</span><br><span class="line">香蕉 | $1 | 5 |</span><br><span class="line">苹果 | $1 | 6 |</span><br><span class="line">草莓 | $1 | 7 |</span><br></pre></td></tr></table></figure>
<p>效果:</p>
<table>
<thead>
<tr>
<th>水果名称</th>
<th>价格</th>
<th>数量</th>
</tr>
</thead>
<tbody><tr>
<td>香蕉</td>
<td>$1</td>
<td>5</td>
</tr>
<tr>
<td>苹果</td>
<td>$1</td>
<td>6</td>
</tr>
<tr>
<td>草莓</td>
<td>$1</td>
<td>7</td>
</tr>
</tbody></table>
<h2 id="加上对齐方式"><a href="#加上对齐方式" class="headerlink" title="加上对齐方式"></a>加上对齐方式</h2><p>我们可以设置表格的对齐方式:</p>
<p>-: 设置内容和标题栏居右对齐。<br>:- 设置内容和标题栏居左对齐。<br>:-: 设置内容和标题栏居中对齐。<br>注意前后得有空格<br>测试:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">name | 111 | 222 | 333 | 444</span><br><span class="line">- | :-: | :-: | :-: | -:</span><br><span class="line">aaa | bbb | ccc | ddd | eee| </span><br><span class="line">fff | ggg| hhh | iii | 000|</span><br></pre></td></tr></table></figure>
<p>效果:</p>
<table>
<thead>
<tr>
<th>name</th>
<th align="center">111</th>
<th align="center">222</th>
<th align="center">333</th>
<th align="right">444</th>
</tr>
</thead>
<tbody><tr>
<td>aaa</td>
<td align="center">bbb</td>
<td align="center">ccc</td>
<td align="center">ddd</td>
<td align="right">eee</td>
</tr>
<tr>
<td>fff</td>
<td align="center">ggg</td>
<td align="center">hhh</td>
<td align="center">iii</td>
<td align="right">000</td>
</tr>
</tbody></table>
<p>注意:表格前后一定要空一行<br>测试结果失败;发现还是不能居中居右;暂未解决</p>
<h2 id="表格中如果需要出现-符号怎么办"><a href="#表格中如果需要出现-符号怎么办" class="headerlink" title="表格中如果需要出现|符号怎么办"></a>表格中如果需要出现|符号怎么办</h2><p>在|符号前加一个转义符\</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">|test|test|</span><br><span class="line">|-|-|</span><br><span class="line">|\||\||</span><br></pre></td></tr></table></figure>
<p>效果:</p>
<table>
<thead>
<tr>
<th>test</th>
<th>test</th>
</tr>
</thead>
<tbody><tr>
<td>|</td>
<td>|</td>
</tr>
</tbody></table>
<h1 id="更换字体颜色"><a href="#更换字体颜色" class="headerlink" title="更换字体颜色"></a>更换字体颜色</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><font color=blue>此处为文本内容</font></span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>博客搭建</category>
</categories>
<tags>
<tag>博客搭建</tag>
</tags>
</entry>
<entry>
<title>STM32学习</title>
<url>/2022/04/19/STM32%E5%AD%A6%E4%B9%A0/</url>
<content><![CDATA[<h1 id="开发板资源"><a href="#开发板资源" class="headerlink" title="开发板资源"></a>开发板资源</h1><p>◆ CPU:STM32F103ZET6,LQFP144,FLASH:512K,SRAM:64K; ◆ 外扩 SPI FLASH:W25Q128,16M 字节<br>◆ 1 个电源指示灯(蓝色)<br>◆ 2 个状态指示灯(DS0:红色,DS1:绿色)<br>◆ 1 个红外接收头,并配备一款小巧的红外遥控器<br>◆ 1 个 EEPROM 芯片,24C02,容量 256 字节<br>◆ 1 个光敏传感器<br>◆ 1 个无线模块接口(可接 NRF24L01/RFID 模块等)<br>◆ 1 路 CAN 接口,采用 TJA1050 芯片<br>◆ 1 路 485 接口,采用 SP3485 芯片<br>◆ 1 路数字温湿度传感器接口,支持 DS18B20 /DHT11 等 ◆ 1 个 ATK 模块接口,支持 ALIENTEK 蓝牙/GPS 模块/MPU6050 模块等<br>◆ 1 个标准的 2.4/2.8/3.5/4.3/7 寸 LCD 接口,支持触摸屏<br>◆ 1 个摄像头模块接口<br>◆ 1 个 OLED 模块接口(与摄像头接口共用)<br>◆ 1 个 USB 串口,可用于程序下载和代码调试(USMART 调试)<br>◆ 1 个 USB SLAVE 接口,用于 USB 通信<br>◆ 1 个有源蜂鸣器<br>◆ 1 个 RS485 选择接口<br>◆ 1 个 CAN/USB 选择接口<br>◆ 1 个串口选择接口<br>◆ 1 个 SD 卡接口(在板子背面,SDIO 接口)<br>◆ 1 个标准的 JTAG/SWD 调试下载口<br>◆ 1 组 AD/DA 组合接口(DAC/ADC/ TPAD) ◆ 1 组 5V 电源供应/接入口<br>◆ 1 组 3.3V 电源供应/接入口<br>◆ 1 个直流电源输入接口(输入电压范围:6~24V) ◆ 1 个启动模式选择配置接口<br>◆ 1 个 RTC 后备电池座,并带电池<br>◆ 1 个复位按钮,可用于复位 MCU 和 LCD<br>◆ 3 个功能按钮,其中 KEY_UP 兼具唤醒功能<br>◆ 1 个电容触摸按键<br>◆ 1 个电源开关,控制整个板的电源<br>◆ 独创的一键下载功能<br>◆ 除晶振占用的 IO 口外,其余所有 IO 口全部引出</p>
<h2 id="1-WIRELESS-模块接口"><a href="#1-WIRELESS-模块接口" class="headerlink" title="1. WIRELESS 模块接口"></a>1. WIRELESS 模块接口</h2><pre><code>这是开发板板载的无线模块接口(U2),可以外接 NRF24L01/RFID 等无线模块。从而实现
</code></pre>
<p>无线通信等功能。注意:接 NRF24L01 模块进行无线通信的时候,必须同时有 2 个模块和 2 个<br>板子,才可以测试,单个模块/板子例程是不能测试的。</p>
<h2 id="2-W25Q128-128Mbit-FLASH"><a href="#2-W25Q128-128Mbit-FLASH" class="headerlink" title="2. W25Q128 128Mbit FLASH"></a>2. W25Q128 128Mbit FLASH</h2><pre><code>这是开发板外扩的 SPI FLASH 芯片(U8),容量为 128Mbit,也就是 16M 字节,可用于存
</code></pre>
<p>储字库和其他用户数据,满足大容量数据存储要求。当然如果觉得 16M 字节还不够用,你可以<br>把数据存放在外部 SD 卡。</p>
<h2 id="3-SD卡接口"><a href="#3-SD卡接口" class="headerlink" title="3. SD卡接口"></a>3. SD卡接口</h2><pre><code>这是开发板板载的一个标准 SD 卡接口(SD_CARD),该接口在开发板的背面,采用大 SD
</code></pre>
<p>卡接口(即相机卡,也可以是 TF 卡+卡套的形式),SDIO 方式驱动,有了这个 SD 卡接口,就<br>可以满足海量数据存储的需求。</p>
<h2 id="4-CAN-USB-选择口"><a href="#4-CAN-USB-选择口" class="headerlink" title="4. CAN/USB 选择口"></a>4. CAN/USB 选择口</h2><pre><code>这是一个 CAN/USB 的选择接口(P6),因为 STM32 的 USB 和 CAN 是共用一组 IO(PA11
</code></pre>
<p>和 PA12),所以我们通过跳线帽来选择不同的功能,以实现 USB/CAN 的实验。</p>
<h2 id="5-USB-串口-串口-1"><a href="#5-USB-串口-串口-1" class="headerlink" title="5.USB 串口/串口 1"></a>5.USB 串口/串口 1</h2><pre><code>这是 USB 串口同 STM32F103ZET6 的串口 1 进行连接的接口(P3),标号 RXD 和 TXD 是
</code></pre>
<p>USB 转串口的 2 个数据口(对 CH340G 来说),而 PA9(TXD)和 PA10(RXD)则是 STM32 的串口<br>1 的两个数据口(复用功能下)。他们通过跳线帽对接,就可以和连接在一起了,从而实现 STM32<br>的程序下载以及串口通信。<br>设计成 USB 串口,是出于现在电脑上串口正在消失,尤其是笔记本,几乎清一色的没有串<br>口。所以板载了 USB 串口可以方便大家下载代码和调试。而在板子上并没有直接连接在一起,<br>则是出于使用方便的考虑。这样设计,你可以把 ALIENTEK 精英 STM32F103 当成一个 USB<br>转 TTL 串口,来和其他板子通信,而其他板子的串口,也可以方便地接到 ALIENTEK 精英<br>STM32F103 开发板上。</p>
<h2 id="6-JTAG-SWD-接口"><a href="#6-JTAG-SWD-接口" class="headerlink" title="6. JTAG/SWD 接口"></a>6. JTAG/SWD 接口</h2><pre><code>这是 ALIENTEK 精英 STM32F103 板载的 20 针标准 JTAG 调试口(JTAG),该 JTAG 口直
</code></pre>
<p>接可以和 ULINK、JLINK 或者 STLINK 等调试器(仿真器)连接,同时由于 STM32 支持 SWD<br>调试,这个 JTAG 口也可以用 SWD 模式来连接。<br> 用标准的 JTAG 调试,需要占用 5 个 IO 口,有些时候,可能造成 IO 口不够用,而用 SWD<br>则只需要 2 个 IO 口,大大节约了 IO 数量,但他们达到的效果是一样的,所以我们强烈建议仿<br>真器使用 SWD 模式!</p>
<h2 id="7-24C02-EEPROM"><a href="#7-24C02-EEPROM" class="headerlink" title="7. 24C02 EEPROM"></a>7. 24C02 EEPROM</h2><pre><code>这是开发板板载的 EEPROM 芯片(U9),容量为 2Kb,也就是 256 字节。用于存储一些掉
</code></pre>
<p>电不能丢失的重要数据,比如系统设置的一些参数/触摸屏校准数据等。有了这个就可以方便的<br>实现掉电数据保存。</p>
<h2 id="8-USB-SLAVE"><a href="#8-USB-SLAVE" class="headerlink" title="8. USB SLAVE"></a>8. USB SLAVE</h2><pre><code>这是开发板板载的一个 MiniUSB 头(USB_SLAVE),用于 USB 从机(SLAVE)通信,一
</code></pre>
<p>般用于 STM32 与电脑的 USB 通信。通过此 MiniUSB 头,开发板就可以和电脑进行 USB 通信<br>了。<br> 开发板总共板载了 2 个 MiniUSB 头,一个(USB_232)用于 USB 转串口,连接 CH340G<br>芯片;另外一个(USB_SLAVE)用于 STM32 内带的 USB。同时开发板可以通过此 MiniUSB<br>头供电,板载两个 MiniUSB 头(不共用),主要是考虑了使用的方便性,以及可以给板子提供<br>更大的电流(两个 USB 都接上)这两个因素。</p>
<h2 id="9-USB-转串口"><a href="#9-USB-转串口" class="headerlink" title="9. USB 转串口"></a>9. USB 转串口</h2><pre><code>这是开发板板载的另外一个 MiniUSB 头(USB_232),用于 USB 连接 CH340G 芯片,从而
</code></pre>
<p>实现 USB 转 TTL 串口。同时,此 MiniUSB 接头也是开发板电源的主要提供口。</p>
<h2 id="10-后备电池接口"><a href="#10-后备电池接口" class="headerlink" title="10. 后备电池接口"></a>10. 后备电池接口</h2><pre><code>这是 STM32 后备区域的供电接口(BAT),可安装 CR1220 电池(默认安装了),可以用来给
</code></pre>
<p>STM32 的后备区域提供能量,在外部电源断电的时候,维持后备区域数据的存储,以及 RTC<br>的运行。</p>
<h2 id="11-OLED-摄像头模块接口"><a href="#11-OLED-摄像头模块接口" class="headerlink" title="11. OLED/摄像头模块接口"></a>11. OLED/摄像头模块接口</h2><pre><code>这是开发板板载的一个 OLED/摄像头模块接口(P4),如果是 OLED 模块,靠左插即可(右
</code></pre>
<p>边两个孔位悬空)。如果是摄像头模块(ALIENTEK 提供),则刚好插满。通过这个接口,可以<br>分别连接 2 种外部模块,从而实现相关实验。</p>
<h2 id="12-有源蜂鸣器"><a href="#12-有源蜂鸣器" class="headerlink" title="12. 有源蜂鸣器"></a>12. 有源蜂鸣器</h2><pre><code>这是开发板的板载蜂鸣器(BEEP),可以实现简单的报警/闹铃等功能。
</code></pre>
<h2 id="13-红外接收器"><a href="#13-红外接收器" class="headerlink" title="13. 红外接收器"></a>13. 红外接收器</h2><pre><code>这是开发板的红外接收头(U6),可以实现红外遥控功能,通过这个接收头,可以接受市
</code></pre>
<p>面常见的各种遥控器的红外信号,大家甚至可以自己实现万能红外解码。当然,如果应用得当,<br>该接收头也可以用来传输数据。</p>
<h2 id="14-DS18B20-DHT11-接口"><a href="#14-DS18B20-DHT11-接口" class="headerlink" title="14. DS18B20/DHT11 接口"></a>14. DS18B20/DHT11 接口</h2><pre><code>这是开发板的一个复用接口(U4),该接口由 4 个镀金排孔组成,可以用来接
</code></pre>
<p>DS18B20/DS1820 等数字温度传感器。也可以用来接 DHT11 这样的数字温湿度传感器。实现一<br>个接口,2 个功能。不用的时候,大家可以拆下上面的传感器,放到其他地方去用,使用上是<br>十分方便灵活的。</p>
<h2 id="15-2-个-LED"><a href="#15-2-个-LED" class="headerlink" title="15. 2 个 LED"></a>15. 2 个 LED</h2><pre><code>这是开发板板载的两个 LED 灯(DS0 和 DS1),DS0 是红色的,DS1 是绿色的,主要是方
</code></pre>
<p>便大家识别。这里提醒大家不要停留在 51 跑马灯的思维,搞这么多灯,除了浪费 IO 口,实在<br>是想不出其他什么优点。<br>我们一般的应用 2 个 LED 足够了,在调试代码的时候,使用 LED 来指示程序状态,是非<br>常不错的一个辅助调试方法。精英 STM32F103 几乎每个实例都使用了 LED 来指示程序的运行<br>状态。</p>
<h2 id="16-启动选择端口"><a href="#16-启动选择端口" class="headerlink" title="16. 启动选择端口"></a>16. 启动选择端口</h2><pre><code>这是开发板板载的启动模式选择端口(BOOT),STM32 有 BOOT0(B0)和 BOOT1(B1)
</code></pre>
<p>两个启动选择引脚,用于选择复位后 STM32 的启动模式,作为开发板,这两个是必须的。在<br>开发板上,我们通过跳线帽选择 STM32 的启动模式。</p>
<h2 id="17-触摸按钮"><a href="#17-触摸按钮" class="headerlink" title="17. 触摸按钮"></a>17. 触摸按钮</h2><pre><code>这是开发板板载的一个电容触摸输入按键(TPAD),利用电容充放电原理,实现触摸按键
</code></pre>
<p>检测。</p>
<h2 id="18-电源指示灯"><a href="#18-电源指示灯" class="headerlink" title="18. 电源指示灯"></a>18. 电源指示灯</h2><pre><code>这是开发板板载的一颗蓝色的 LED 灯(PWR),用于指示电源状态。在电源开启的时候(通
</code></pre>
<p>过板上的电源开关控制),该灯会亮,否则不亮。通过这个 LED,可以判断开发板的上电情况。</p>
<h2 id="19-复位按钮"><a href="#19-复位按钮" class="headerlink" title="19. 复位按钮"></a>19. 复位按钮</h2><pre><code>这是开发板板载的复位按键(RESET),用于复位 STM32,还具有复位液晶的功能,因为
</code></pre>
<p>液晶模块的复位引脚和 STM32 的复位引脚是连接在一起的,当按下该键的时候,STM32 和液<br>晶一并被复位。</p>
<h2 id="20-3个按键"><a href="#20-3个按键" class="headerlink" title="20. 3个按键"></a>20. 3个按键</h2><pre><code>这是开发板板载的 3 个机械式输入按键(KEY0、KEY1 和 KEY_UP),其中 KEY_UP 具有
</code></pre>
<p>唤醒功能,该按键连接到 STM32 的 WAKE_UP(PA0)引脚,可用于待机模式下的唤醒,在不<br>使用唤醒功能的时候,也可以做为普通按键输入使用。<br> 其他 2 个是普通按键,可以用于人机交互的输入,这 2 个按键是直接连接在 STM32 的 IO<br>口上的。这里注意 KEY_UP 是高电平有效,而 KEY0 和 KEY1 是低电平有效,大家在使用的时<br>候留意一下。</p>
<h2 id="21-STM32F103ZET6"><a href="#21-STM32F103ZET6" class="headerlink" title="21. STM32F103ZET6"></a>21. STM32F103ZET6</h2><pre><code>这是开发板的核心芯片(U1),型号为:STM32F103ZET6。该芯片具有 64KB SRAM、512KB
</code></pre>
<p>FLASH、2 个基本定时器、4 个通用定时器、2 个高级定时器、2 个 DMA 控制器(共 12 个通道)、3 个 SPI、2 个 IIC、5 个串口、1 个 USB、1 个 CAN、3 个 12 位 ADC、1 个 12 位 DAC、1 个SDIO 接口、1 个 FSMC 接口以及 112 个通用 IO 口。</p>
<h2 id="22-AD-DA-组合接口"><a href="#22-AD-DA-组合接口" class="headerlink" title="22. AD/DA 组合接口"></a>22. AD/DA 组合接口</h2><pre><code>这是 1 个由 4 个排针组成的一个组合接口(P7)。可以实现 AD 采集、DA 输出和板载电容
</code></pre>
<p>触摸按键(TPAD)检测的功能。</p>
<h2 id="23-ATK-模块接口"><a href="#23-ATK-模块接口" class="headerlink" title="23. ATK 模块接口"></a>23. ATK 模块接口</h2><pre><code>这是开发板板载的一个 ALIENTEK 通用模块接口(U3),目前可以支持 ALIENTEK 开发
</code></pre>
<p>的 GPS 模块、蓝牙模块和 MPU6050 模块等,直接插上对应的模块,就可以进行开发。后续我<br>们将开发更多兼容该接口的其他模块,实现更强大的扩展性能。</p>
<h2 id="24-3-3V-电源输入-输出"><a href="#24-3-3V-电源输入-输出" class="headerlink" title="24. 3.3V 电源输入/输出"></a>24. 3.3V 电源输入/输出</h2><pre><code>这是开发板板载的一组 3.3V 电源输入输出排针(2*3)(VOUT1),用于给外部提供 3.3V
</code></pre>
<p>的电源,也可以用于从外部接 3.3V 的电源给板子供电。<br> 大家在实验的时候可能经常会为没有 3.3V 电源而苦恼不已,有了 ALIENTEK 精英<br>STM32F103,你就可以很方便的拥有一个简单的 3.3V 电源(USB 供电的时候,最大电流不能<br>超过 500mA,外部供电的时候,最大可达 1000mA)</p>
<h2 id="25-5V-电源输入-输出"><a href="#25-5V-电源输入-输出" class="headerlink" title="25. 5V 电源输入/输出"></a>25. 5V 电源输入/输出</h2><pre><code>这是开发板板载的一组 5V 电源输入输出排针(2*3)(VOUT2),该排针用于给外部提供
</code></pre>
<p>5V 的电源,也可以用于从外部接 5V 的电源给板子供电。<br> 同样大家在实验的时候可能经常会为没有 5V 电源而苦恼不已,ALIENTEK 充分考虑到了<br>大家需求,有了这组 5V 排针,你就可以很方便的拥有一个简单的 5V 电源(USB 供电的时候,<br>最大电流不能超过 500mA,外部供电的时候,最大可达 1000mA)。</p>
<h2 id="26-电源开关"><a href="#26-电源开关" class="headerlink" title="26. 电源开关"></a>26. 电源开关</h2><pre><code>这是开发板板载的电源开关(K1)。该开关用于控制整个开发板的供电,如果切断,则整
</code></pre>
<p>个开发板都将断电,电源指示灯(PWR)会随着此开关的状态而亮灭。</p>
<h2 id="27-DC6-24V-电源输入"><a href="#27-DC6-24V-电源输入" class="headerlink" title="27. DC6~24V 电源输入"></a>27. DC6~24V 电源输入</h2><pre><code>这是开发板板载的一个外部电源输入口(DC_IN),采用标准的直流电源插座。开发板板载
</code></pre>
<p>了 DC-DC 芯片(MP2359),用于给开发板提供高效、稳定的 5V 电源。由于采用了 DC-DC 芯<br>片,所以开发板的供电范围十分宽,大家可以很方便的找到合适的电源(只要输出范围在<br>DC 6~24V的基本都可以)来给开发板供电。在耗电比较大的情况下,比如用到 4.3 屏/7 寸屏的<br>时候,建议使用外部电源供电,可以提供足够的电流给开发板使用.</p>
<h2 id="28-RS485-选择接口"><a href="#28-RS485-选择接口" class="headerlink" title="28. RS485 选择接口"></a>28. RS485 选择接口</h2><pre><code>这是开发板板载的 RS485 选择接口(P5),MAX3485 通过这个接口来决定是否连接到
</code></pre>
<p>STM32 的串口 2(USART2),当这里断开的时候:串口 2 可以用来做普通串口使用,而 RS485<br>则可以用来实现 RS485 转 TTL 的功能;当这里接上时:串口 2 连接 MAX3485,就可以实现<br>RS485 通信。 </p>
<h2 id="29-引出-IO-口(共-2-组)"><a href="#29-引出-IO-口(共-2-组)" class="headerlink" title="29. 引出 IO 口(共 2 组)"></a>29. 引出 IO 口(共 2 组)</h2><pre><code>这是开发板 IO 引出端口,总共有 2 组主 IO 引出口:P1 和 P2。它们采用 2*27 排针引出,
</code></pre>
<p>总共引出 106 个 IO 口。而 STM32F103ZET6 总共只有 112 个 IO,除去 RTC 晶振占用的 2 个 IO<br>,还剩下 110 个,这 2 组排针,总共引出 106 个 IO,剩下的 4 个 IO 分别通过:P3 和 P5 引出。</p>
<h2 id="30-LCD-接口"><a href="#30-LCD-接口" class="headerlink" title="30. LCD 接口"></a>30. LCD 接口</h2><pre><code>这是开发板板载的 LCD 模块接口,该接口兼容 ALIENTEK 全系列 TFTLCD 模块,包括:
</code></pre>
<p>2.4 寸、2.8 寸、3.5 寸、4.3 寸和 7 寸等 TFTLCD 模块,并且支持电阻/电容触摸功能。</p>
<h2 id="31-光敏传感器"><a href="#31-光敏传感器" class="headerlink" title="31. 光敏传感器"></a>31. 光敏传感器</h2><pre><code>这是开发板板载的一个光敏传感器(LS1),通过该传感器,开发板可以感知周围环境光线
</code></pre>
<p>的变化,从而可以实现类似自动背光控制的应用。</p>
<h2 id="32-RS485-接口"><a href="#32-RS485-接口" class="headerlink" title="32. RS485 接口"></a>32. RS485 接口</h2><pre><code>这是开发板板载的 RS485 总线接口(RS485),通过 2 个端口和外部 485 设备连接。这里提
</code></pre>
<p>醒大家,RS485 通信的时候,必须 A 接 A,B 接 B。否则可能通信不正常!另外,开发板自带<br>了终端电阻(120Ω)。</p>
<h2 id="33-CAN-接口"><a href="#33-CAN-接口" class="headerlink" title="33. CAN 接口"></a>33. CAN 接口</h2><pre><code>这是开发板板载的 CAN 总线接口(CAN),通过 2 个端口和外部 CAN 总线连接,即 CANH
</code></pre>
<p>和 CANL。这里提醒大家:CAN 通信的时候,必须 CANH 接 CANH,CANL 接 CANL,否则<br>可能通信不正常!</p>
<h1 id="STM32-初探"><a href="#STM32-初探" class="headerlink" title="STM32 初探"></a>STM32 初探</h1><h2 id="1-为什么选择STM32"><a href="#1-为什么选择STM32" class="headerlink" title="1. 为什么选择STM32"></a>1. 为什么选择STM32</h2><pre><code>·STM32 是基于ARM内核的32位的 Cortex-M内核
·具有标准的ARM框架
·好性能、低功耗、低电压、创新的内核及外设
·简单易用、低风险
Cortex-M3内核属于ARMv7架构
ARMv7架构定义了三大分工明确的系列:
"A"系列:面向尖端的基于虚拟内存的操作系统和用户应用
"R"系列:针对实时系统
"M”系列:对微控制器
STM32F1属于Cortex-M系列中的Cortex-M3内核,采用ARMv7-M架构。
STM32F4属于Cortex-M4系列采用ARMv7-ME架构。
Cortex-A5/A8采用ARMv7-A架构。
传统的ARM7系列采用的是ARMv4T架构。
详细对比见PPT的表格
总之Cortex-M3比ARM7好多了
</code></pre>
<h2 id="3-M3系列的优点"><a href="#3-M3系列的优点" class="headerlink" title="3. M3系列的优点"></a>3. M3系列的优点</h2><pre><code>1. 高性能Cortex-M内核
采用ARM公司流行的标准内核Cortex-M3
低动态功耗上实现的高性能
哈佛结构上实现1.25DMIPS/MHZ,功耗只有0.19mv/MHZ,比ARM7TDMI改进了30%
单周期的乘法和硬件除法
不可分的位操作,实现对RAM,I/O和寄存器的最优访问。
2. 最佳的代码密度
Thumb-2指令集以16位指令的密度实现32位指令性能(与ARM7TDMI的ARM模式比减少了30%-45%的代码量)
3. 可预见的运行时间
中断控制器嵌在内核之中,中断之间的间隔最少可达6个CPU周期。
从低功耗模式唤醒只需6个CPU周期
4.改进的调试功能
串行单步调试和JTAG调试
</code></pre>
<h2 id="2-STM32的命名规则"><a href="#2-STM32的命名规则" class="headerlink" title="2. STM32的命名规则"></a>2. STM32的命名规则</h2><pre><code>见PPT
</code></pre>
<h2 id="3-STM32的优势总结"><a href="#3-STM32的优势总结" class="headerlink" title="3. STM32的优势总结"></a>3. STM32的优势总结</h2><pre><code>1)极高的性能: 主流的Cortex内核。
2)丰富合理的外设,合理的功耗,合理的价格。
3)强大的软件支持:丰富的软件包。
4)全面丰富的技术文档。
5)芯片型号种类多,覆盖面广。
6)强大的用户基础:最先成功试水CM3芯片的公司,积累了大批的用户群体,为其领先做铺垫。
</code></pre>
<h1 id="STM32-芯片解读"><a href="#STM32-芯片解读" class="headerlink" title="STM32 芯片解读"></a>STM32 芯片解读</h1><pre><code>STM32F1数据手册:STM32F103ZET6.pdf
STM32F1中文手册:STM32F1xx中文参考手册.pdf
开发板原理图:XXX STM32F1_Vxx_SCH.pdf
</code></pre>
<h2 id="1-芯片有哪些资源?"><a href="#1-芯片有哪些资源?" class="headerlink" title="1.芯片有哪些资源?"></a>1.芯片有哪些资源?</h2><pre><code>STM32F103ZET6
内核:
32位 高性能ARM Cortex-M3处理器
时钟:高达72M,实际还可以超频一点点
单周期乘法和硬件除法
IO口:
STM32F103ZET6: 144引脚 112个IO(16×7可以分为7组,PA~PG)
大部分IO口都耐5V(模拟通道除外)
支持调试:SWD和JTAG,SWD只要2根数据线
存储器容量:
STM32F103ZET6: 512K FLASH,64K SRAM
时钟,复位和电源管理:
①:2.0~3.6V电源和IO电压(一般用3.3V)
②:上电复位,掉电复位和可编程的电压监控
③:强大的时钟系统:
4~16M的外部高速晶振(一般用8M)
内部8MHz的高速RC振荡器
内部40KHz低速RC振荡器,看门狗时钟
内部锁相环(PLL,倍频),一般系统时钟都是外部或者内部高速时钟经过PLL倍频后得到(8M不够就倍频)
外部低速32.768K的晶振,主要做RTC时钟源
低功耗:
睡眠,停止和待机三种低功耗模式
可用电池为RTC和备份寄存器供电
AD:
3个12位AD【多达21个外部测量通道】
转换范围:0~3.6(电源电压)(一般用3.3V)
内部通道可以用于内部温度测量
内置参考电压
DA:
2个12位DA
DMA(直接存储器访问):
12个DMA通道(7+5=12; 7通道DMA1,5通道DMA2)支持外设:定时器,ADC,DAC,SDIO,I2S,SPI,I2C,和USART
定时器(11个):
4个通用定时器
2个基本定时器
2个高级定时器
1个系统定时器
2个看门狗定时器
通信接口(13个):
2个I2C接口
5个串口
3个SPI接口
1个CAN2.0
1个USB FS
1个SDIO
</code></pre>
<h2 id="2-芯片的内部结构"><a href="#2-芯片的内部结构" class="headerlink" title="2. 芯片的内部结构"></a>2. 芯片的内部结构</h2><pre><code>看PPT
</code></pre>
<h2 id="3-STM32的最小系统"><a href="#3-STM32的最小系统" class="headerlink" title="3. STM32的最小系统"></a>3. STM32的最小系统</h2><pre><code> 供电(VDD VSS VDDA VSSA都是3.3V VDDA和VSSA都是模拟的,对纹波的处理要求较高)
复位(一般是一个按键一个电容,按键没按的时候通过上拉电阻,保持高电压3.3V,有按键按下的时候接地变为0V,可以通过电容充电)
时钟:外部晶振(2个)(有两个晶振;一个是外部高速晶振,另一个是低速晶振)
Boot启动模式选择 (BOOT1和BOOT2两个引脚通过一个段子连接在一起,跳线帽,通过一键下载电路可以跳过这个步骤)
下载电路(串口(串口1,通过PA9和PA10接到USB转232的口再下载)/JTAG/SWD)
后备电池
其他地方还有一些滤波电容,一般选104
</code></pre>
<h1 id="开发环境搭建"><a href="#开发环境搭建" class="headerlink" title="开发环境搭建"></a>开发环境搭建</h1><h2 id="MDK安装"><a href="#MDK安装" class="headerlink" title="MDK安装"></a>MDK安装</h2><pre><code>MDK是什么?
RealView MDK是Keil公司开发的,为基于Cortex 、ARM7、ARM9等处理器设备提供的一个完整的开发环境。
参考资料:
ALIENTEK xxSTM32开发板入门资料\MDK5安装手册.pdf
</code></pre>
<h2 id="USB串口作用:"><a href="#USB串口作用:" class="headerlink" title="USB串口作用:"></a>USB串口作用:</h2><pre><code>(1)可以当串口使用。
(2)如果USB串口连接到STM32的串口1(STM32ISP下载只能是串口1)的话,那么可以用来串口下载程序。
(3)因为要连接到USB,所以可以用来USB供电。
</code></pre>
<h2 id="USB串口驱动安装"><a href="#USB串口驱动安装" class="headerlink" title="USB串口驱动安装"></a>USB串口驱动安装</h2><pre><code>USB串口驱动芯片型号:CH340
串口下载工具:mcuisp(FlyMcu)
</code></pre>
<h2 id="STM32的ISP下载"><a href="#STM32的ISP下载" class="headerlink" title="STM32的ISP下载"></a>STM32的ISP下载</h2><pre><code>只能使用串口1,也就是对应串口发送接收引脚PA9,PA10。不能使用其他串口(例如串口2:PA2,PA3)用来ISP下载。
</code></pre>
<h1 id="GPIO基本原理与寄存器配置"><a href="#GPIO基本原理与寄存器配置" class="headerlink" title="GPIO基本原理与寄存器配置"></a>GPIO基本原理与寄存器配置</h1><h2 id="GPIO基本结构和工作方式"><a href="#GPIO基本结构和工作方式" class="headerlink" title="GPIO基本结构和工作方式"></a>GPIO基本结构和工作方式</h2><pre><code>STM32F103ZET6
- 一共有7组IO口
- 每组IO口有16个IO
- 一共16X7=112个IO
- 分别是GPIOA GPIOB GPIOC GPIOD GPIOE GPIOF GPIOG
- GPIOA有 PA0 PA1 PA2 PA3 PA4 PA5 PA6 PA7 PA8 PA9 PA10 PA11 PA12 PA13 PA14 PA15
STM32的大部分引脚除了当GPIO使用外,还可以复用为外设功能引脚(比如串口)。
GPIO的工作方式:
4种输入模式:
输入浮空
输入上拉
输入下拉
模拟输入
4种输出模式:
开漏输出:只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)
开漏复用功能
推挽式输出:可以输出强高低电平,连接数字器件
推挽式复用功能
3种最大翻转速度:
-2MHZ
-10MHz
-50MHz
</code></pre>
<h2 id="每组GPIO端口的寄存器"><a href="#每组GPIO端口的寄存器" class="headerlink" title="每组GPIO端口的寄存器"></a>每组GPIO端口的寄存器</h2><pre><code>包括:
两个32位配置寄存器(GPIOx_CRL ,GPIOx_CRH) ,
两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR),
一个32位置位/ 复位寄存器(GPIOx_BSRR),
一个16位复位寄存器(GPIOx_BRR),
一个32位锁定寄存器(GPIOx_LCKR)。
每个I/O端口位可以自由编程,然而I/O端口寄存器必须按32位字被访问(不允许半字或字节访问) 。
每组IO口含下面7个寄存器。也就是7个寄存器,一共可以控制一组GPIO的16个IO口。
- GPIOx_CRL :端口配置低寄存器
- GPIOx_CRH:端口配置高寄存器
- GPIOx_IDR:端口输入寄存器
- GPIOx_ODR:端口输出寄存器
- GPIOx_BSRR:端口位设置/清除寄存器
- GPIOx_BRR :端口位清除寄存器
- GPIOx_LCKR:端口配置锁存寄存器
</code></pre>
<h2 id="端口复用功能"><a href="#端口复用功能" class="headerlink" title="端口复用功能"></a>端口复用功能</h2><p> STM32的大部分端口都具有复用功能。<br> 所谓复用,就是一些端口不仅仅可以做为通用IO口,还可以复用为一<br> 些外设引脚,比如PA9,PA10可以复用为STM32的串口1引脚。<br> 作用:最大限度的利用端口资源</p>
<h2 id="端口重映射功能"><a href="#端口重映射功能" class="headerlink" title="端口重映射功能"></a>端口重映射功能</h2><p> 就是可以把某些功能引脚映射到其他引脚。<br> 比如串口1默认引脚是PA9,PA10可以通过配置重映射映<br> 射到PB6,PB7<br> 作用:方便布线</p>
<h1 id="跑马灯程序"><a href="#跑马灯程序" class="headerlink" title="跑马灯程序"></a>跑马灯程序</h1><h2 id="硬件连接及GPIO库函数说明"><a href="#硬件连接及GPIO库函数说明" class="headerlink" title="硬件连接及GPIO库函数说明"></a>硬件连接及GPIO库函数说明</h2><pre><code> LED0接PB5
LED1接PE5
GPIO采用推挽输出的方式(可以输出高低电平)
头文件:stm32f10x_gpio.h
源文件:stm32f10x_gpio.c
要使用GPIO的话
stm32f10x_gpio.c
stm32f10x_rcc.c
misc.c
是必要的文件
</code></pre>
<h2 id="初始化函数:"><a href="#初始化函数:" class="headerlink" title="初始化函数:"></a>初始化函数:</h2><p>void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);<br>第一个参数 GPIO_TypeDef* GPIOx 表示选择第几个GPIO<br>第二个参数 GPIO_InitTypeDef* GPIO_InitStruct 中的GPIO_InitTypeDef结构体中的成员有:</p>
<p>GPIO_Pin用来指定IO口<br>GPIO_Speed用来指定速度<br>GPIO_Mode用来指定模式 </p>
<p>GPIO_Init函数初始化样例:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_InitTypeDef GPIO_InitStructure; //定义结构体</span><br><span class="line">GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置</span><br><span class="line">GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出</span><br><span class="line">GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz</span><br><span class="line">GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5</span><br></pre></td></tr></table></figure>
<h2 id="2个读取输入电平函数:"><a href="#2个读取输入电平函数:" class="headerlink" title="2个读取输入电平函数:"></a>2个读取输入电平函数:</h2><p>uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);<br>作用:读取某<strong>个</strong>GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。<br>例如:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平</span><br></pre></td></tr></table></figure>
<p>uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);<br>作用:读取某<strong>组</strong>GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。<br>例如:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平</span><br></pre></td></tr></table></figure>
<h2 id="2个读取输出电平函数:"><a href="#2个读取输出电平函数:" class="headerlink" title="2个读取输出电平函数:"></a>2个读取输出电平函数:</h2><p>uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);<br>作用:读取某个GPIO的输出电平。实际操作的是GPIO_ODR寄存器。<br>例如:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平</span><br></pre></td></tr></table></figure>
<p>uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);<br>作用:读取某组GPIO的输出电平。实际操作的是GPIO_ODR寄存器。<br>例如:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平</span><br></pre></td></tr></table></figure>
<h2 id="4个设置输出电平函数:"><a href="#4个设置输出电平函数:" class="headerlink" title="4个设置输出电平函数:"></a>4个设置输出电平函数:</h2><p>void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);<br>作用:设置某个IO口输出为高电平(1)。实际操作BSRR寄存器</p>
<p>void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);<br>作用:设置某个IO口输出为低电平(0)。实际操作的BRR寄存器。</p>
<p>void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);<br>void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);<br> 这两个函数不常用,也是用来设置IO口输出电平。</p>
<h2 id="程序"><a href="#程序" class="headerlink" title="程序"></a>程序</h2><p>步骤:<br> 使能IO口时钟。调用函数RCC_APB2PeriphColckCmd();<br> 不同的IO组,调用的时钟使能函数不一样。<br> 初始化IO口模式。调用函数GPIO_Init();<br> 操作IO口,输出高低电平。<br> GPIO_SetBits();<br> GPIO_ResetBits();</p>
<h3 id="1-在template模板中新建HARDWARE(硬件)分组;"><a href="#1-在template模板中新建HARDWARE(硬件)分组;" class="headerlink" title="1.在template模板中新建HARDWARE(硬件)分组;"></a>1.在template模板中新建HARDWARE(硬件)分组;</h3><h3 id="2-在相应的目录中新建HARDWARE文件夹;"><a href="#2-在相应的目录中新建HARDWARE文件夹;" class="headerlink" title="2.在相应的目录中新建HARDWARE文件夹;"></a>2.在相应的目录中新建HARDWARE文件夹;</h3><h3 id="3-在HARDWARE文件夹中再新建LED等外设文件夹"><a href="#3-在HARDWARE文件夹中再新建LED等外设文件夹" class="headerlink" title="3.在HARDWARE文件夹中再新建LED等外设文件夹"></a>3.在HARDWARE文件夹中再新建LED等外设文件夹</h3><h3 id="4-每个外设文件夹中建立一个对应的-h和-c的文件"><a href="#4-每个外设文件夹中建立一个对应的-h和-c的文件" class="headerlink" title="4.每个外设文件夹中建立一个对应的.h和.c的文件"></a>4.每个外设文件夹中建立一个对应的.h和.c的文件</h3><h3 id="5-在-h中预编译防止重新引用"><a href="#5-在-h中预编译防止重新引用" class="headerlink" title="5.在.h中预编译防止重新引用"></a>5.在.h中预编译防止重新引用</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#ifndef __LED_H</span><br><span class="line">#define __LED_H</span><br><span class="line">void LED_Init(void); //声明LED初始化函数</span><br><span class="line">#endif</span><br></pre></td></tr></table></figure>
<h3 id="6-在-c(源文件)中定义函数"><a href="#6-在-c(源文件)中定义函数" class="headerlink" title="6.在.c(源文件)中定义函数"></a>6.在.c(源文件)中定义函数</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "led.h"</span><br><span class="line">void LED_Init(void)</span><br><span class="line">{</span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="7-按步骤调用函数RCC-APB2PeriphColckCmd"><a href="#7-按步骤调用函数RCC-APB2PeriphColckCmd" class="headerlink" title="7.按步骤调用函数RCC_APB2PeriphColckCmd();"></a>7.按步骤调用函数RCC_APB2PeriphColckCmd();</h3><p>写入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "led.h"</span><br><span class="line">void LED_Init(void)</span><br><span class="line">{</span><br><span class="line"> RCC_APB2PeriphClockCmd();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>后右键RCC_APB2PeriphClockCmd();点击go to definition<br>找到定义</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)</span><br><span class="line">{</span><br><span class="line"> /* Check the parameters */</span><br><span class="line"> assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));</span><br><span class="line"> assert_param(IS_FUNCTIONAL_STATE(NewState));</span><br><span class="line"> if (NewState != DISABLE)</span><br><span class="line"> {</span><br><span class="line"> RCC->APB2ENR |= RCC_APB2Periph;</span><br><span class="line"> }</span><br><span class="line"> else</span><br><span class="line"> {</span><br><span class="line"> RCC->APB2ENR &= ~RCC_APB2Periph;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>第一个参数RCC_APB2Periph 第二个参数NewState<br>右键IS_RCC_APB2_PERIPH点击go to definition查看有效值<br>找到</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001)</span><br><span class="line">#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)</span><br><span class="line">#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)</span><br><span class="line">#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)</span><br><span class="line">#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020)</span><br><span class="line">#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040)</span><br><span class="line">#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080)</span><br><span class="line">#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100)</span><br><span class="line">#define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200)</span><br><span class="line">#define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400)</span><br><span class="line">#define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800)</span><br><span class="line">#define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000)</span><br><span class="line">#define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000)</span><br><span class="line">#define RCC_APB2Periph_USART1 ((uint32_t)0x00004000)</span><br><span class="line">#define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000)</span><br><span class="line">#define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000)</span><br><span class="line">#define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000)</span><br><span class="line">#define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000)</span><br><span class="line">#define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000)</span><br><span class="line">#define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000)</span><br><span class="line">#define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000)</span><br><span class="line"></span><br><span class="line">#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))</span><br></pre></td></tr></table></figure>
<p>即可查看有效值<br>我们所使用的是GPIOB和GPIOE,所以<br>RCC_APB2PeriphColckCmd()的第一个输入参数是RCC_APB2Periph_GPIOB</p>
<p>同理右键IS_FUNCTIONAL_STATE点击go to definition查看有效值<br>找到</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))</span><br></pre></td></tr></table></figure>
<p>发现有效值是DISABLE或者ENABLE<br>我们选择ENABLE,所以<br>RCC_APB2PeriphColckCmd()的第一个输入参数是ENABLE</p>
<p>此时的led.h内容为</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "led.h"</span><br><span class="line">void LED_Init(void)</span><br><span class="line">{</span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>编译后有两个error显示RCC_APB2Periph_GPIOB和ENABLE未定义<br>我们引用固件头文件stm32f10x.h后就OK了<br>此时的led.h内容为</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "led.h"</span><br><span class="line">#include "stm32f10x.h"</span><br><span class="line">void LED_Init(void)</span><br><span class="line">{</span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>此时GPIOB的时钟就已经启动了;</p>
<h3 id="8-初始化IO口模式。调用函数GPIO-Init"><a href="#8-初始化IO口模式。调用函数GPIO-Init" class="headerlink" title="8.初始化IO口模式。调用函数GPIO_Init();"></a>8.初始化IO口模式。调用函数GPIO_Init();</h3><p>复制GPIO_Init()到led.c中;右键go to definition查看具体参数<br>或者到gpio.h中找声明<br>声明中</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); </span><br></pre></td></tr></table></figure>
<p>有两个个参数分别是GPIOx和GPIO_InitStruct<br>GPIOx此时是GPIOB和GPIOE<br>GPIO_InitStruct是一个GPIO_InitTypeDef类型的结构体;<br>所以我们在前面先定义这个结构体</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_InitTypeDef GPIO_InitStruct;</span><br></pre></td></tr></table></figure>
<p>定义完后注意赋值,发现结构体有<br> GPIO_InitStruct.GPIO_Mode<br> GPIO_InitStruct.GPIO_Pin<br> GPIO_InitStruct.GPIO_Speed<br>三个变量;通过上面的方法找到有效值如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;</span><br><span class="line">GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;</span><br><span class="line">GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;</span><br></pre></td></tr></table></figure>
<p><strong>注意在引用的时候要加&符号</strong></p>
<p>即</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_Init(GPIOB,&GPIO_InitStruct);</span><br></pre></td></tr></table></figure>
<p>同理可设置GPIOB的值,这一步完成后的led.c代码为:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "led.h"</span><br><span class="line">#include "stm32f10x.h"</span><br><span class="line">void LED_Init(void)</span><br><span class="line">{</span><br><span class="line"> GPIO_InitTypeDef GPIO_InitStruct;</span><br><span class="line"> </span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);</span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);</span><br><span class="line"> </span><br><span class="line"> GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;</span><br><span class="line"> GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;</span><br><span class="line"> GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;</span><br><span class="line"> GPIO_Init(GPIOB,&GPIO_InitStruct);</span><br><span class="line"> </span><br><span class="line"> GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;</span><br><span class="line"> GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;</span><br><span class="line"> GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;</span><br><span class="line"> GPIO_Init(GPIOE,&GPIO_InitStruct);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="9-操作IO口,输出高低电平"><a href="#9-操作IO口,输出高低电平" class="headerlink" title="9.操作IO口,输出高低电平"></a>9.操作IO口,输出高低电平</h3><p>一般默认初始化之后灯先不点亮,所以需要设置为高电平,需要用到<br> GPIO_SetBits();<br> GPIO_ResetBits();</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GPIO_SetBits(GPIOB,GPIO_Pin_5);</span><br><span class="line">GPIO_SetBits(GPIOE,GPIO_Pin_5);</span><br></pre></td></tr></table></figure>
<p>此时led.c的内容为:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "led.h"</span><br><span class="line">#include "stm32f10x.h"</span><br><span class="line">void LED_Init(void)</span><br><span class="line">{</span><br><span class="line"> GPIO_InitTypeDef GPIO_InitStruct;</span><br><span class="line"> </span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);</span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);</span><br><span class="line"> </span><br><span class="line"> GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;</span><br><span class="line"> GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;</span><br><span class="line"> GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;</span><br><span class="line"> GPIO_Init(GPIOB,&GPIO_InitStruct);</span><br><span class="line"> GPIO_SetBits(GPIOB,GPIO_Pin_5);</span><br><span class="line"> </span><br><span class="line"> GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;</span><br><span class="line"> GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;</span><br><span class="line"> GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;</span><br><span class="line"> GPIO_Init(GPIOE,&GPIO_InitStruct);</span><br><span class="line"> GPIO_SetBits(GPIOE,GPIO_Pin_5);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="从主函数开始写程序"><a href="#从主函数开始写程序" class="headerlink" title="从主函数开始写程序"></a>从主函数开始写程序</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include"stm32f10x.h"</span><br><span class="line">#include"led.h"</span><br><span class="line">#include"delay.h"</span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line"> LED_Init();</span><br><span class="line"> delay_init();</span><br><span class="line"> while(1)</span><br><span class="line"> {</span><br><span class="line"> GPIO_SetBits(GPIOB,GPIO_Pin_5);</span><br><span class="line"> GPIO_SetBits(GPIOE,GPIO_Pin_5); //设为高电平</span><br><span class="line"></span><br><span class="line"> delay_ms(500);</span><br><span class="line"></span><br><span class="line"> GPIO_ResetBits(GPIOB,GPIO_Pin_5);</span><br><span class="line"> GPIO_ResetBits(GPIOE,GPIO_Pin_5); //设为低电平</span><br><span class="line"></span><br><span class="line"> delay_ms(500);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="蜂鸣器实验"><a href="#蜂鸣器实验" class="headerlink" title="蜂鸣器实验"></a>蜂鸣器实验</h1><p>蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、<br>复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣<br>器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。<br>这里的有源不是指电源的“源”,而是指有没有自带震荡电路,有源蜂鸣器自带了震荡电路,<br>一通电就会发声;无源蜂鸣器则没有自带震荡电路,必须外部提供 2~5Khz 左右的方波驱动,<br>才能发声。<br>我们用到一个 NPN 三极管(S8050)来驱动蜂鸣器,R33 主要用于防止蜂鸣器的误发<br>声。当 PB.8 输出高电平的时候,蜂鸣器将发声,当 PB.8 输出低电平的时候,蜂鸣器停止发声。<br>电流从三极管基极进入放大后才能驱动蜂鸣器。由于STM32单片机复位后BEEP位是浮空的,即该IO口的状态是不确定的,所以我们需要接一个R38(10KΩ)再接地,把小电流引到地,这样可以防止误发声。</p>
<h2 id="实验步骤"><a href="#实验步骤" class="headerlink" title="实验步骤"></a>实验步骤</h2><p>使能IO口时钟。调用函数RCC_APB2PeriphColckCmd();<br> 不同的IO组,调用的时钟使能函数不一样。<br>初始化IO口模式。调用函数BEEP_Init();<br>操作IO口,输出高低电平。</p>
<h1 id="按键输入实验"><a href="#按键输入实验" class="headerlink" title="按键输入实验"></a>按键输入实验</h1><h2 id="按键的硬件连接"><a href="#按键的硬件连接" class="headerlink" title="按键的硬件连接"></a>按键的硬件连接</h2><p>有三个按键 WK UP、KEY_0、KEY_1<br>WK UP接PA0 另一端接Vcc<br>KEY_0接PE4 另一端接GND<br>KEY_1接PE3 另一端接GND</p>
<h2 id="按键输入操作说明"><a href="#按键输入操作说明" class="headerlink" title="按键输入操作说明"></a>按键输入操作说明</h2><p>读取IO口输入电平调用库函数为:<br>uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);</p>
<p>读取IO口输入电平操作寄存器为<br>GPIOx_IDR:端口输入寄存器</p>
<p>使用位带操作读取IO口输入电平:<br>PEin(4) -读取GPIOE.4口电平<br>PEin(n) -读取GPIOE.n口电平</p>
<h2 id="按键输入实验步骤"><a href="#按键输入实验步骤" class="headerlink" title="按键输入实验步骤"></a>按键输入实验步骤</h2><p>使能按键对应IO口时钟。调用函数:<br> RCC_APB2PeriphClockCmd();<br>初始化IO模式:上拉/下拉输入。调用函数:<br> GPIO_Init();<br>扫描IO口电平(库函数/寄存器/位操作) </p>
<h2 id="C语言关键字-:static"><a href="#C语言关键字-:static" class="headerlink" title="C语言关键字 :static"></a>C语言关键字 :static</h2><p>Static申明的局部变量,存储在静态存储区。<br>它在函数调用结束之后,不会被释放。它的值会一直保留下来。<br>所以可以说static申明的局部变量,具有记忆功能。<br>比如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int getValue(void)</span><br><span class="line">{</span><br><span class="line"> int flag=0;</span><br><span class="line"> flag++;</span><br><span class="line"> return flag;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>和</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int getValue(void)</span><br><span class="line">{</span><br><span class="line"> static int flag=0;</span><br><span class="line"> flag++;</span><br><span class="line"> return flag;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="按键扫描(支持连续按)的一般思路"><a href="#按键扫描(支持连续按)的一般思路" class="headerlink" title="按键扫描(支持连续按)的一般思路"></a>按键扫描(支持连续按)的一般思路</h2><p>如果我要实现:按键按下,没有松开,只能算按下一次,这个函数无法实现。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">u8 KEY_Scan(void)</span><br><span class="line"> {</span><br><span class="line"> if(KEY按下)</span><br><span class="line"> {</span><br><span class="line"> delay_ms(10);//延时10-20ms,防抖。</span><br><span class="line"> if(KEY确实按下)</span><br><span class="line"> {</span><br><span class="line"> return KEY_Value;</span><br><span class="line"> }</span><br><span class="line"> return 无效值;</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h2 id="按键扫描(不支持连续按)的一般思路"><a href="#按键扫描(不支持连续按)的一般思路" class="headerlink" title="按键扫描(不支持连续按)的一般思路"></a>按键扫描(不支持连续按)的一般思路</h2><p>不支持连续按:就是说,按键按下了,没有松开,只能算一次。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">u8 KEY_Scan(void)</span><br><span class="line"> {</span><br><span class="line"> static u8 key_up=1; //key_up==1表示前一次是松开的;key_up==0表示前一次是闭合的</span><br><span class="line"> if(key_up && KEY按下)</span><br><span class="line"> {</span><br><span class="line"> delay_ms(10);//延时,防抖</span><br><span class="line"> key_up=0;//标记这次key已经按下</span><br><span class="line"> if(KEY确实按下)</span><br><span class="line"> {</span><br><span class="line"> return KEY_VALUE;</span><br><span class="line"> }</span><br><span class="line"> }else if(KEY没有按下) key_up=1;</span><br><span class="line"> return 没有按下</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h2 id="按键扫描(两种模式合二为一)的一般思路"><a href="#按键扫描(两种模式合二为一)的一般思路" class="headerlink" title="按键扫描(两种模式合二为一)的一般思路"></a>按键扫描(两种模式合二为一)的一般思路</h2><p>多了一个入口参数mode;mode==1表示支持连续按 mode==0表示不支持连续按 </p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">u8 KEY_Scan(u8 mode)</span><br><span class="line"> {</span><br><span class="line"> static u8 key_up=1;</span><br><span class="line"> if(mode==1) key_up=1;//支持连续按</span><br><span class="line"> if(key_up && KEY按下)</span><br><span class="line"> {</span><br><span class="line"> delay_ms(10);//延时,防抖</span><br><span class="line"> key_up=0;//标记这次key已经按下</span><br><span class="line"> if(KEY确实按下)</span><br><span class="line"> {</span><br><span class="line"> return KEY_VALUE;</span><br><span class="line"> }</span><br><span class="line"> }else if(KEY没有按下) key_up=1;</span><br><span class="line"> return 没有按下</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h1 id="C语言复习和MDK中寄存器地址名称映射分析"><a href="#C语言复习和MDK中寄存器地址名称映射分析" class="headerlink" title="C语言复习和MDK中寄存器地址名称映射分析"></a>C语言复习和MDK中寄存器地址名称映射分析</h1><h2 id="位操作:6种位操作运算符"><a href="#位操作:6种位操作运算符" class="headerlink" title="位操作:6种位操作运算符"></a>位操作:6种位操作运算符</h2><table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
<th>运算符</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>&</td>
<td>按位与</td>
<td>~</td>
<td>取反</td>
</tr>
<tr>
<td>|</td>
<td>按位或</td>
<td><<</td>
<td>左移</td>
</tr>
<tr>
<td>^</td>
<td>按位异或</td>
<td>>></td>
<td>右移</td>
</tr>
</tbody></table>
<p>GPIOA->CRL&=0XFFFFFF0F; //将第4-7位清0<br>GPIOA->CRL|=0X00000040; //设置相应位的值,不改变其他位的值<br>GPIOA->ODR|=1<<5;<br>TIMx->SR = (uint16_t)~TIM_FLAG;</p>
<p>左移和右移都是补0</p>
<h2 id="define宏定义关键词"><a href="#define宏定义关键词" class="headerlink" title="define宏定义关键词"></a>define宏定义关键词</h2><p>define是C语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供方便。<br>常见的格式:<br>#define 标识符 字符串<br>“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。<br>例如:<br>#define SYSCLK_FREQ_72MHz 72000000<br>定义标识符SYSCLK_FREQ_72MHz的值为72000000。</p>
<h2 id="ifdef条件编译"><a href="#ifdef条件编译" class="headerlink" title="ifdef条件编译"></a>ifdef条件编译</h2><p>单片机程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。条件编译命令最常见的形式为:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#ifdef 标识符 </span><br><span class="line">程序段1 </span><br><span class="line">#else </span><br><span class="line">程序段2 </span><br><span class="line">#endif </span><br></pre></td></tr></table></figure>
<p>例如:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#ifdef STM32F10X_HD</span><br><span class="line">大容量芯片需要的一些变量定义</span><br><span class="line">#end</span><br></pre></td></tr></table></figure>
<h2 id="extern变量申明"><a href="#extern变量申明" class="headerlink" title="extern变量申明"></a>extern变量申明</h2><p>Main.c文件</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">u8 id;//定义只允许一次</span><br><span class="line">main()</span><br><span class="line">{</span><br><span class="line">id=1;</span><br><span class="line">printf("d%",id);//id=1</span><br><span class="line">test();</span><br><span class="line">printf("d%",id);//id=2</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>test.c文件</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">extern u8 id;//此处声明id这个变量,不是定义</span><br><span class="line">void test(void){</span><br><span class="line">id=2;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="typedef类型别名"><a href="#typedef类型别名" class="headerlink" title="typedef类型别名"></a>typedef类型别名</h2><p>定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。</p>
<p>typedef unsigned char uint8_t;<br>typedef unsigned short int uint16_t;<br>typedef unsigned int uint32_t;<br>typedef unsigned __int64 uint64_t;</p>
<h2 id="结构体:构造类型"><a href="#结构体:构造类型" class="headerlink" title="结构体:构造类型"></a>结构体:构造类型</h2><p>Struct 结构体名{<br>成员列表1;<br>成员变量2;<br>…<br>}变量名列表;</p>
<p>在结构体申明的时候可以定义变量,也可以申明之后定义,方法是:<br>Struct 结构体名字 结构体变量列表 ;</p>
<p>结构体作用:<br> 同一个类型可以用数组,不同类型可以用结构体组织。</p>
<pre><code>结构体可扩展性强。
举例说明:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
</code></pre>
<h2 id="STM32中操作:"><a href="#STM32中操作:" class="headerlink" title="STM32中操作:"></a>STM32中操作:</h2><p>GPIOA->ODR=0x00000000; </p>
<p>值0x00000000是怎么赋值给了GPIOA的ODR寄存器地址的呢?</p>
<p>也就是说GPIOA->ODR这种写法,是怎么与GPIOA的ODR寄存器地址映射起来的?<br>#define PERIPH_BASE ((uint32_t)0x40000000)<br>#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)<br>#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)<br>#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)<br>typedef struct<br>{<br> __IO uint32_t CRL;<br> __IO uint32_t CRH;<br> __IO uint32_t IDR;<br> __IO uint32_t ODR;<br> __IO uint32_t BSRR;<br> __IO uint32_t BRR;<br> __IO uint32_t LCKR;<br>} GPIO_TypeDef;</p>
<h1 id="时钟系统"><a href="#时钟系统" class="headerlink" title="时钟系统"></a>时钟系统</h1><p>几个重要的时钟:</p>
<p>SYSCLK(系统时钟) :<br> AHB总线时钟<br> APB1总线时钟(低速): 速度最高36MHz<br> APB2总线时钟(高速): 速度最高72MHz<br> PLL时钟</p>
<p>参考资料:<br><a href="http://www.openedv.com/posts/list/302.htm">http://www.openedv.com/posts/list/302.htm</a></p>
<ol>
<li><p>STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。<br> ①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。<br> ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz<del>16MHz。③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。<br> ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。<br> ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2</del>16倍,但是其输出频率最大不得超过72MHz。</p>
</li>
<li><p>系统时钟SYSCLK可来源于三个时钟源:<br> ①、HSI振荡器时钟<br> ②、HSE振荡器时钟<br> ③、PLL时钟</p>
</li>
<li><p>STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL<br> 输出的2分频、HSI、HSE、或者系统时钟。</p>
</li>
<li><p>任何一个外设在使用之前,必须首先使能其相应的时钟。</p>
</li>
</ol>
<h2 id="系统时钟初始化函数:"><a href="#系统时钟初始化函数:" class="headerlink" title="系统时钟初始化函数:"></a>系统时钟初始化函数:</h2><pre><code>SystemInit();
</code></pre>
<p> 使用V3.5版本的库函数,该函数在系统启动之后会自动调用:<br> startup_stm32f10x_xx.s文件中:<br> ; Reset handler<br> Reset_Handler PROC<br> EXPORT Reset_Handler [WEAK]<br> IMPORT __main<br> IMPORT SystemInit<br> LDR R0, =SystemInit<br> BLX R0<br> LDR R0, =__main<br> BX R0<br> ENDP</p>
<h1 id="SysTick定时器"><a href="#SysTick定时器" class="headerlink" title="SysTick定时器"></a>SysTick定时器</h1><p>Systick定时器,是一个简单的定时器,对于CM3,CM4内核芯片,都有Systick定时器。</p>
<p>Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。</p>
<p>Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。</p>
<p>SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。<br>Systick中断的优先级也可以设置。</p>
<p>4个Systick寄存器</p>
<pre><code>CTRL SysTick 控制和状态寄存器
LOAD SysTick 自动重装载除值寄存器
VAL SysTick 当前值寄存器
CALIB SysTick 校准值寄存器
</code></pre>
<p>对于STM32,<br> 外部时钟源是 HCLK(AHB总线时钟)的1/8<br> 内核时钟是 HCLK时钟<br> 配置函数:SysTick_CLKSourceConfig();</p>
<p>固件库中的Systick相关函数:</p>
<p>SysTick_CLKSourceConfig() //Systick时钟源选择 misc.c文件中</p>
<p>SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK,并开启中断<br> //core_cm3.h/core_cm4.h文件中<br>Systick中断服务函数:</p>
<p> void SysTick_Handler(void);</p>
<h1 id="端口复用"><a href="#端口复用" class="headerlink" title="端口复用"></a>端口复用</h1><p>STM32有很多的内置外设,这些外设的外部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用。</p>
<p>例如串口1 的发送接收引脚是PA9,PA10,当我们把PA9,PA10不用作GPIO,而用做复用功能串口1的发送接收引脚的时候,叫端口复用。</p>
<p>端口复用配置过程:<br>以PA9,PA10配置为串口1为例<br>1.GPIO端口时钟使能。<br> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);</p>
<p>2.复用外设时钟使能。<br> 比如你要将端口PA9,PA10复用为串口,所以要使能串口时钟。<br> RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);</p>
<p>3.端口模式配置。 GPIO_Init()函数。<br> 查表:<br> 《STM32中文参考手册V10》P110的表格“8.1.11外设的GPIO配置”</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //①IO时钟使能</span><br><span class="line"></span><br><span class="line">RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //②外设时钟使能</span><br><span class="line"></span><br><span class="line">//③初始化IO为对应的模式</span><br><span class="line">GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9//复用推挽输出</span><br><span class="line">GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;</span><br><span class="line">GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; </span><br><span class="line">GPIO_Init(GPIOA, &GPIO_InitStructure);</span><br><span class="line"> </span><br><span class="line">GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 PA.10 浮空输入</span><br><span class="line">GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入</span><br><span class="line">GPIO_Init(GPIOA, &GPIO_InitStructure); </span><br></pre></td></tr></table></figure>
<h1 id="中断优先级管理NVIC"><a href="#中断优先级管理NVIC" class="headerlink" title="中断优先级管理NVIC"></a>中断优先级管理NVIC</h1><p>CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。<br>STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。<br>STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。<br>STM32F103系列上面,又只有60个可屏蔽中断(在107系列才有68个)</p>
<h2 id="中断管理方法:"><a href="#中断管理方法:" class="headerlink" title="中断管理方法:"></a>中断管理方法:</h2><p>首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。</p>
<p>分组配置是在寄存器SCB->AIRCR中配置:</p>
<h2 id="抢占优先级-amp-响应优先级区别:"><a href="#抢占优先级-amp-响应优先级区别:" class="headerlink" title="抢占优先级 & 响应优先级区别:"></a>抢占优先级 & 响应优先级区别:</h2><p>高抢占优先级可以打断正在进行的低抢占优先级中断的。<br>抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。<br>抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。<br>如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;</p>
<p>假定设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为2,响应优先级为1。 中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。<br>那么这3个中断的优先级顺序为:中断7>中断3>中断6.</p>
<h2 id="特别说明:"><a href="#特别说明:" class="headerlink" title="特别说明:"></a>特别说明:</h2><p>一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。</p>
<h2 id="中断优先级分组函数"><a href="#中断优先级分组函数" class="headerlink" title="中断优先级分组函数:"></a>中断优先级分组函数:</h2><p>void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);</p>
<p>void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)<br>{<br> assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));<br> SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;<br>}</p>
<p>NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);</p>
<h2 id="怎么设置单个中断的抢占优先级和响应优先级?"><a href="#怎么设置单个中断的抢占优先级和响应优先级?" class="headerlink" title="怎么设置单个中断的抢占优先级和响应优先级?"></a>怎么设置单个中断的抢占优先级和响应优先级?</h2><p>中断设置相关寄存器<br>__IO uint8_t IP[240]; //中断优先级控制的寄存器组</p>
<p>__IO uint32_t ISER[8]; //中断使能寄存器组<br>__IO uint32_t ICER[8]; //中断失能寄存器组<br>__IO uint32_t ISPR[8]; //中断挂起寄存器组<br>__IO uint32_t ICPR[8]; //中断解挂寄存器组<br>__IO uint32_t IABR[8]; //中断激活标志位寄存器组</p>
<p>MDK中NVIC寄存器结构体:<br>typedef struct<br>{<br> __IO uint32_t ISER[8];<br> uint32_t RESERVED0[24];<br> __IO uint32_t ICER[8];<br> uint32_t RSERVED1[24];<br> __IO uint32_t ISPR[8];<br> uint32_t RESERVED2[24];<br> __IO uint32_t ICPR[8];<br> uint32_t RESERVED3[24];<br> __IO uint32_t IABR[8];<br> uint32_t RESERVED4[56];<br> __IO uint8_t IP[240];<br> uint32_t RESERVED5[644];<br> __O uint32_t STIR;<br>} NVIC_Type; </p>
<h2 id="对于每个中断怎么设置优先级?"><a href="#对于每个中断怎么设置优先级?" class="headerlink" title="对于每个中断怎么设置优先级?"></a>对于每个中断怎么设置优先级?</h2><p>中断优先级控制的寄存器组:IP[240]<br>全称是:Interrupt Priority Registers</p>
<p>240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F10x系列一共60个可屏蔽中断,使用IP[59]~IP[0]。</p>
<p>每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。</p>
<p>void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);</p>
<h2 id="中断使能寄存器组:ISER-8"><a href="#中断使能寄存器组:ISER-8" class="headerlink" title="中断使能寄存器组:ISER[8]"></a>中断使能寄存器组:ISER[8]</h2><p>作用:用来使能中断<br>32位寄存器,每个位控制一个中断的使能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ISER[0]和ISER[1]。</p>
<p>ISER[0]的bit0<del>bit31分别对应中断0</del>31。ISER[1]的bit0<del>27对应中断32</del>59;</p>
<p>void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);</p>
<h2 id="中断失能寄存器组:ICER-8"><a href="#中断失能寄存器组:ICER-8" class="headerlink" title="中断失能寄存器组:ICER[8]"></a>中断失能寄存器组:ICER[8]</h2><p>作用:用来失能中断<br>32位寄存器,每个位控制一个中断的失能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。</p>
<p>ICER[0]的bit0<del>bit31分别对应中断0</del>31。ICER[1]的bit0<del>27对应中断32</del>59;</p>
<p>配置方法跟ISER一样。</p>
<p>void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);</p>
<h2 id="中断挂起控制寄存器组:ISPR-8"><a href="#中断挂起控制寄存器组:ISPR-8" class="headerlink" title="中断挂起控制寄存器组:ISPR[8]"></a>中断挂起控制寄存器组:ISPR[8]</h2><p>作用:用来挂起中断</p>
<h2 id="中断解挂控制寄存器组:ICPR-8"><a href="#中断解挂控制寄存器组:ICPR-8" class="headerlink" title="中断解挂控制寄存器组:ICPR[8]"></a>中断解挂控制寄存器组:ICPR[8]</h2><p>作用:用来解挂中断<br>static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);<br>static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn);<br>static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)</p>
<h2 id="中断参数初始化函数"><a href="#中断参数初始化函数" class="headerlink" title="中断参数初始化函数"></a>中断参数初始化函数</h2><p>void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);</p>
<p>typedef struct<br>{<br> uint8_t NVIC_IRQChannel; //设置中断通道<br> uint8_t NVIC_IRQChannelPreemptionPriority;//设置响应优先级<br> uint8_t NVIC_IRQChannelSubPriority; //设置抢占优先级<br> FunctionalState NVIC_IRQChannelCmd; //使能/使能<br>} NVIC_InitTypeDef;</p>
<p>NVIC_InitTypeDef NVIC_InitStructure;<br>NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断<br>NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1<br>NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2<br>NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能<br>NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器</p>
<h2 id="中断优先级设置步骤"><a href="#中断优先级设置步骤" class="headerlink" title="中断优先级设置步骤"></a>中断优先级设置步骤</h2><p>系统运行后先设置中断优先级分组。调用函数:</p>
<ol>
<li>void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);<br>整个系统执行过程中,只设置一次中断分组。</li>
<li>针对每个中断,设置对应的抢占优先级和响应优先级:<br>void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);</li>
<li>如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可</li>
</ol>
<h1 id="串口通信"><a href="#串口通信" class="headerlink" title="串口通信"></a>串口通信</h1><h2 id="通信接口背景知识"><a href="#通信接口背景知识" class="headerlink" title="通信接口背景知识"></a>通信接口背景知识</h2><p>处理器与外部设备通信的两种方式:<br>并行通信<br> -传输原理:数据各个位同时传输。<br> -优点:速度快<br> -缺点:占用引脚资源多</p>
<p>串行通信<br> -传输原理:数据按位顺序传输。<br> -优点:占用引脚资源少<br> -缺点:速度相对较慢</p>
<p>串行通信:<br>按照数据传送方向,分为:<br>单工:<br> 数据传输只支持数据在一个方向上传输<br>半双工:<br> 允许数据在两个方向上传输,但是,在某一时刻,只允许数<br> 据在一个方向上传输,它实际上是一种切换方向的单工通信;<br>全双工:<br> 允许数据同时在两个方向上传输,因此,全双工通信是两个<br> 单工通信方式的结合,它要求发送设备和接收设备都有独立<br> 的接收和发送能力。 </p>
<p>同步通信:带时钟同步信号传输。<br> SPI,IIC通信接口<br>异步通信:不带时钟同步信号。<br> UART(通用异步收发器),单总线(要约定波特率)</p>
<p>常见的串行通信接口:</p>
<table>
<thead>
<tr>
<th>通信标准</th>
<th>引脚说明</th>
<th>通信方式</th>
<th>通信方向</th>
</tr>
</thead>
<tbody><tr>
<td>UART(通用异步收发器)</td>
<td>TXD:发送端 RXD:接受端 GND:公共地</td>
<td>异步通信</td>
<td>全双工</td>
</tr>
<tr>
<td>单总线(1-wire)</td>
<td>DQ:发送/接受端</td>
<td>异步通信</td>
<td>半双工</td>
</tr>
<tr>
<td>SPI</td>
<td>SCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入</td>
<td>同步通信</td>
<td>全双工</td>
</tr>
<tr>
<td>I²C</td>
<td>SCL:同步时钟 SDA:数据输入/输出端</td>
<td>同步通信</td>
<td>半双工</td>
</tr>
</tbody></table>
<p>STM32的串口通信接口:</p>
<p>UART:通用异步收发器<br>USART:通用同步异步收发器<br>大容量STM32F10x系列芯片,包含3个USART和2个UART</p>
<p>UART异步通信方式引脚:<br>-RXD:数据输入引脚。数据接受。<br>-TXD:数据发送引脚。数据发送。</p>
<table>
<thead>
<tr>
<th>串口号</th>
<th>RXD</th>
<th>TXD</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>PA10</td>
<td>PA9</td>
</tr>
<tr>
<td>2</td>
<td>PA3</td>
<td>PA2</td>
</tr>
<tr>
<td>3</td>
<td>PB11</td>
<td>PB10</td>
</tr>
<tr>
<td>4</td>
<td>PC11</td>
<td>PC10</td>
</tr>
<tr>
<td>5</td>
<td>PD2</td>
<td>PC12</td>
</tr>
</tbody></table>
<p>UART异步通信方式特点:<br>全双工异步通信。<br>分数波特率发生器系统,提供精确的波特率。<br>-发送和接受共用的可编程波特率,最高可达4.5Mbits/s<br>可编程的数据字长度(8位或者9位);<br>可配置的停止位(支持1或者2位停止位);<br>可配置的使用DMA多缓冲器通信。<br>单独的发送器和接收器使能位。<br>检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志<br>多个带标志的中断源。触发中断。<br>其他:校验控制,四个错误检测标志。</p>
<p>STM32串口异步通信需要定义的参数:<br> 起始位<br> 数据位(8位或者9位)<br> 奇偶校验位(第9位)<br> 停止位(1,15,2位)<br> 波特率设置</p>
<h2 id="STM32串口常用寄存器和库函数"><a href="#STM32串口常用寄存器和库函数" class="headerlink" title="STM32串口常用寄存器和库函数"></a>STM32串口常用寄存器和库函数</h2><p>常用的串口相关寄存器:<br>USART_SR状态寄存器<br>USART_DR数据寄存器<br>USART_BRR波特率寄存器<br>串口操作相关库函数(省略入口参数):</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能</span><br><span class="line">void USART_Cmd();//使能串口</span><br><span class="line">void USART_ITConfig();//使能相关中断</span><br><span class="line"></span><br><span class="line">void USART_SendData();//发送数据到串口,DR</span><br><span class="line">uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据</span><br><span class="line"></span><br><span class="line">FlagStatus USART_GetFlagStatus();//获取状态标志位</span><br><span class="line">void USART_ClearFlag();//清除状态标志位</span><br><span class="line">ITStatus USART_GetITStatus();//获取中断状态标志位</span><br><span class="line">void USART_ClearITPendingBit();//清除中断状态标志位</span><br></pre></td></tr></table></figure>
<h3 id="串口初始化函数"><a href="#串口初始化函数" class="headerlink" title="串口初始化函数"></a>串口初始化函数</h3><p>找到初始化函数的声明:<br>void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)<br>两个入口参数分别是<br>串口标号USARTx和一个结构体USART_InitStruct 找到结构体的定义</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">typedef struct</span><br><span class="line">{</span><br><span class="line"> uint32_t USART_BaudRate; //波特率 </span><br><span class="line"></span><br><span class="line"> uint16_t USART_WordLength; //字长 </span><br><span class="line"> uint16_t USART_StopBits; //停止位 </span><br><span class="line"></span><br><span class="line"> uint16_t USART_Parity; //奇偶校验位 </span><br><span class="line"> uint16_t USART_Mode; //发送接收使能位</span><br><span class="line"></span><br><span class="line"> uint16_t USART_HardwareFlowControl; //硬件流设置</span><br><span class="line">} USART_InitTypeDef;</span><br></pre></td></tr></table></figure>
<h3 id="串口配置的一般步骤"><a href="#串口配置的一般步骤" class="headerlink" title="串口配置的一般步骤"></a>串口配置的一般步骤</h3><p>串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();<br>串口复位:USART_DeInit(); 这一步不是必须的<br>GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP<br>串口参数初始化:USART_Init();<br>开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)<br> NVIC_Init();<br> USART_ITConfig();<br>⑥使能串口:USART_Cmd();<br>⑦编写中断处理函数:USARTx_IRQHandler();<br>⑧串口数据收发:<br>void USART_SendData();//发送数据到串口,DR<br>uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据<br>⑨串口传输状态获取:<br>FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);<br>void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);</p>
<p>PA9 PA10复用为串口1</p>
<h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void My_USART1_Init(void)</span><br><span class="line">{</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> GPIO_InitTypeDef GPIO_InitStruct; //定义一个GPIO结构体</span><br><span class="line"> USART_InitTypeDef USART_InitStruct; //定义一个串口结构体</span><br><span class="line"> NVIC_InitTypeDef NVIC_InitStruct; //定义一个中断结构体</span><br><span class="line"> </span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //GPIO时钟使能</span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //串口时钟使能</span><br><span class="line"> </span><br><span class="line"> //初始化GPIO结构体和GPIO引脚</span><br><span class="line"> GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //推挽输出</span><br><span class="line"> GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //输出引脚</span><br><span class="line"> GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;</span><br><span class="line"> GPIO_Init(GPIOA,&GPIO_InitStruct); //GPIOA9初始化</span><br><span class="line"> /*************************************************************************/</span><br><span class="line"> GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入</span><br><span class="line"> GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; //输出引脚</span><br><span class="line"> GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;</span><br><span class="line"> GPIO_Init(GPIOA,&GPIO_InitStruct); //GPIOA10初始化</span><br><span class="line"> /*************************************************************************/</span><br><span class="line"> </span><br><span class="line"> //初始化串口</span><br><span class="line"> USART_InitStruct.USART_BaudRate = 115200; //设置波特率</span><br><span class="line"> USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不使用硬件流</span><br><span class="line"> USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //使能发送和接受</span><br><span class="line"> USART_InitStruct.USART_Parity = USART_Parity_No; //不用奇偶校验</span><br><span class="line"> USART_InitStruct.USART_StopBits = USART_StopBits_1; //停止位设为1</span><br><span class="line"> USART_InitStruct.USART_WordLength = USART_WordLength_8b; //字长设为8</span><br><span class="line"> USART_Init(USART1,&USART_InitStruct);</span><br><span class="line"> </span><br><span class="line"> //串口使能函数</span><br><span class="line"> USART_Cmd(USART1,ENABLE);</span><br><span class="line"> </span><br><span class="line"> //打开接收中断</span><br><span class="line"> USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);</span><br><span class="line"> </span><br><span class="line"> //设置相应中断</span><br><span class="line"> NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //设置通道</span><br><span class="line"> NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //开启通道</span><br><span class="line"> NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //中断优先级分组为2</span><br><span class="line"> //抢占优先级为0.1.2,3都可</span><br><span class="line"> NVIC_InitStruct.NVIC_IRQChannelSubPriority =1; //子优先级</span><br><span class="line"> NVIC_Init(&NVIC_InitStruct);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">void USART1_IRQHandler(void) //中断服务函数</span><br><span class="line">{</span><br><span class="line"> u8 res;</span><br><span class="line"> if(USART_GetITStatus(USART1,USART_IT_RXNE)) //接收到数据</span><br><span class="line"> {</span><br><span class="line"> res=USART_ReceiveData(USART1); //读取数据</span><br><span class="line"> USART_SendData(USART1,res); //发送出去</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"> int main(void)</span><br><span class="line">{</span><br><span class="line"> //要使用中断的话要使用NVIC_PriorityGroupConfig函数</span><br><span class="line"> NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设优先级分组为2</span><br><span class="line"> My_USART1_Init();</span><br><span class="line"> while(1);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><p>先将程序下载进开发板<br>打开串口调试助手 XCOM V2.6<br>测试成功</p>
<h1 id="外部中断实验"><a href="#外部中断实验" class="headerlink" title="外部中断实验"></a>外部中断实验</h1><h2 id="外部中断概述"><a href="#外部中断概述" class="headerlink" title="外部中断概述"></a>外部中断概述</h2><p>STM32的每个IO都可以作为外部中断输入。<br>STM32的中断控制器支持19个外部中断/事件请求:<br>线0~15:对应外部IO口的输入中断。<br>线16:连接到PVD输出。<br>线17:连接到RTC闹钟事件。<br>线18:连接到USB唤醒事件。</p>
<p>每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。</p>
<p>从上面可以看出,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),<br>STM32F103RCT6(51),那么中断线怎么跟io口对应呢?</p>
<p>GPIOx.0映射到EXTI0<br>GPIOx.1映射到EXTI1<br>…<br>GPIOx.15映射到EXTI15</p>
<p>对于每个中断线,我们可以设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能。</p>
<p>是不是16个中断线就可以分配16个中断服务函数呢?</p>
<p>IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数<br>外部中断线0<del>4各分配一个中断向量,共五个服务函数<br>外部中断线5</del>9分配一个中断向量,共用一个服务函数<br>外部中断线10~15分配一个中断向量,共用一个中断服务函数。</p>
<p>中断服务函数列表:<br>EXTI0_IRQHandler<br>EXTI1_IRQHandler<br>EXTI2_IRQHandler<br>EXTI3_IRQHandler<br>EXTI4_IRQHandler<br>EXTI9_5_IRQHandler<br>EXTI15_10_IRQHandler </p>
<h2 id="外部中断常用库函数"><a href="#外部中断常用库函数" class="headerlink" title="外部中断常用库函数"></a>外部中断常用库函数</h2><p>①void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);<br>//设置IO口与中断线的映射关系</p>
<p>exp: GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);</p>
<p>②void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);<br> //初始化中断线:触发方式等</p>
<p>③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);<br>//判断中断线中断状态,是否发生</p>
<p>④void EXTI_ClearITPendingBit(uint32_t EXTI_Line);<br>//清除中断线上的中断标志位</p>
<h3 id="EXTI-Init函数"><a href="#EXTI-Init函数" class="headerlink" title="EXTI_Init函数"></a>EXTI_Init函数</h3><p>void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">typedef struct</span><br><span class="line">{</span><br><span class="line"> uint32_t EXTI_Line; //指定要配置的中断线 </span><br><span class="line"> EXTIMode_TypeDef EXTI_Mode; //模式:事件 OR中断</span><br><span class="line"> EXTITrigger_TypeDef EXTI_Trigger;//触发方式:上升沿/下降沿/双沿触发</span><br><span class="line"> FunctionalState EXTI_LineCmd; //使能 OR失能</span><br><span class="line">}EXTI_InitTypeDef;</span><br></pre></td></tr></table></figure>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">EXTI_InitStructure.EXTI_Line=EXTI_Line2; </span><br><span class="line">EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; </span><br><span class="line">EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;</span><br><span class="line">EXTI_InitStructure.EXTI_LineCmd = ENABLE;</span><br><span class="line">EXTI_Init(&EXTI_InitStructure);</span><br></pre></td></tr></table></figure>
<h2 id="外部中断的一般配置步骤:"><a href="#外部中断的一般配置步骤:" class="headerlink" title="外部中断的一般配置步骤:"></a>外部中断的一般配置步骤:</h2><p>1.初始化IO口为输入。<br> GPIO_Init();</p>
<p>2.开启IO口复用时钟。<br> RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);</p>
<p>3.设置IO口与中断线的映射关系。<br> void GPIO_EXTILineConfig();</p>
<p>4.初始化线上中断,设置触发条件等。<br> EXTI_Init();</p>
<p>5.配置中断分组(NVIC),并使能中断。<br> NVIC_Init();</p>
<p>6.编写中断服务函数。<br> EXTIx_IRQHandler();</p>
<p>7.清除中断标志位<br> EXTI_ClearITPendingBit();</p>
<h2 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "led.h"</span><br><span class="line">#include "delay.h"</span><br><span class="line">#include "key.h"</span><br><span class="line">#include "sys.h"</span><br><span class="line">#include "beep.h"</span><br><span class="line">#include "usart.h"</span><br><span class="line">#include "exti.h"</span><br><span class="line"></span><br><span class="line"> int main(void)</span><br><span class="line">{</span><br><span class="line"> delay_init();</span><br><span class="line"> NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);</span><br><span class="line"> uart_init(115200); //串口初始化为115200</span><br><span class="line"> LED_Init(); //初始化LED</span><br><span class="line"> BEEP_Init(); //初始化蜂鸣器</span><br><span class="line"> KEY_Init(); //初始化按键</span><br><span class="line"> EXTIX_Init(); //初始化外部中断</span><br><span class="line"> LED0=0; //点亮LED</span><br><span class="line"> while(1)</span><br><span class="line"> {</span><br><span class="line"> printf("OK\n\r");</span><br><span class="line"> delay_ms(1000);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h1 id="看门狗实验"><a href="#看门狗实验" class="headerlink" title="看门狗实验"></a>看门狗实验</h1><h2 id="独立看门狗概述"><a href="#独立看门狗概述" class="headerlink" title="独立看门狗概述"></a>独立看门狗概述</h2><p>为什么要看门狗?<br>在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。</p>
<p>看门狗解决的问题是什么?<br>在启动正常运行的时候,系统不能复位。<br>在系统跑飞(程序异常执行)的情况,系统复位,程序重新执行。</p>
<p>STM32内置两个看门狗,提供了更高的安全性,时间的精确性和使用的灵活性。两个看门狗设备(独立看门狗/窗口看门狗)可以用来检测和解决由软件错误引起的故障。当计数器达到给定的超时值时,触发一个中断(仅适用窗口看门狗)或者产生系统复位。<br>独立看门狗(IWDG)由专用的低速时钟(LSI)驱动,即使主时钟发生故障它仍有效。<br>独立看门狗适合应用于需要看门狗作为一个在主程序之外 能够完全独立工作,并且对时间精度要求低的场合。<br>窗口看门狗由从APB1时钟分频后得到时钟驱动。通过可配置的时间窗口来检测应用程序非正常的过迟或过早操作。<br>窗口看门狗最适合那些要求看门狗在精确计时窗口起作用的程序。</p>
<p>独立看门狗功能描述:<br>1.在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。<br>2.无论何时,只要在键值寄存器IWDG_KR中写入0xAAAA(通常说的喂狗), 自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。<br>3.如果程序异常,就无法正常喂狗,从而系统复位。</p>
<p>键值寄存器IWDG_KR: 0<del>15位有效<br>预分频寄存器IWDG_PR:0</del>2位有效。具有写保护功能,要操作先取消写保护<br>重装载寄存器IWDG_RLR:0<del>11位有效。具有写保护功能,要操作先取消写保护。<br>状态寄存器IWDG_SR:0</del>1位有效</p>
<p>取消写保护:写入0x5555表示允许访问IWDG_PR和IWDG_RLR寄存器</p>
<h3 id="独立看门狗超时时间"><a href="#独立看门狗超时时间" class="headerlink" title="独立看门狗超时时间"></a>独立看门狗超时时间</h3><p>溢出时间计算:<br> Tout=((4×2^prer) ×rlr) /40 (M3)</p>
<h2 id="IWDG独立看门狗操作库函数"><a href="#IWDG独立看门狗操作库函数" class="headerlink" title="IWDG独立看门狗操作库函数"></a>IWDG独立看门狗操作库函数</h2><p>void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);//取消写保护:0x5555使能<br>void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); //设置预分频系数:写PR<br>void IWDG_SetReload(uint16_t Reload); //设置重装载值:写RLR<br>void IWDG_ReloadCounter(void); //喂狗:写0xAAAA到KR<br>void IWDG_Enable(void); //使能看门狗:写0xCCCC到KR<br>FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG); //状态:重装载/预分频 更新</p>
<p>独立看门狗操作步骤:<br>① 取消寄存器写保护:<br> IWDG_WriteAccessCmd();<br>② 设置独立看门狗的预分频系数,确定时钟:<br> IWDG_SetPrescaler();<br>③ 设置看门狗重装载值,确定溢出时间:<br> IWDG_SetReload();<br>④ 使能看门狗<br> IWDG_Enable();<br>⑤ 应用程序喂狗:<br> IWDG_ReloadCounter();</p>
<p>溢出时间计算:<br> Tout=((4×2^prer) ×rlr) /40 (M3)</p>
<h2 id="程序代码"><a href="#程序代码" class="headerlink" title="程序代码"></a>程序代码</h2><p>#include “led.h”<br>#include “delay.h”<br>#include “key.h”<br>#include “sys.h”<br>#include “beep.h”<br>#include “usart.h”<br>#include “exti.h”<br>#include “wdg.h”<br> int main(void)<br>{<br> delay_init();<br> LED_Init();<br> KEY_Init();<br> BEEP_Init();</p>
<pre><code>IWDG_Init(4,625);//1s
delay_ms(200);
LED0=0;
while(1)
{
if(KEY_Scan(0)==WKUP_PRES)
{
IWDG_ReloadCounter();
}
}
</code></pre>
<p>}</p>
<h1 id="TFTLCD显示实验"><a href="#TFTLCD显示实验" class="headerlink" title="TFTLCD显示实验"></a>TFTLCD显示实验</h1><h2 id="TFTLCD驱动原理-TFTLCD简介"><a href="#TFTLCD驱动原理-TFTLCD简介" class="headerlink" title="TFTLCD驱动原理-TFTLCD简介"></a>TFTLCD驱动原理-TFTLCD简介</h2><p>TFTLCD即薄膜晶体管液晶显示器。它与无源TN-LCD、STN-LCD的简单矩阵不同,它在液晶显示屏的每一个像素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。</p>
<p>TFTLCD具有:亮度好、对比度高、层次感强、颜色鲜艳等特点。是目前最主流的LCD显示器。广泛应用于电视、手机、电脑、平板等各种电子产品。</p>
<p>ATK-4.3寸 TFTLCD模块<br>分辨率:480*800,驱动IC:NT35510,电容触摸屏,16位并口驱动</p>
<h3 id="ALINETEK-2-8寸-TFTLCD接口说明(16位80并口):"><a href="#ALINETEK-2-8寸-TFTLCD接口说明(16位80并口):" class="headerlink" title="ALINETEK 2.8寸 TFTLCD接口说明(16位80并口):"></a>ALINETEK 2.8寸 TFTLCD接口说明(16位80并口):</h3><p>注意:DB1<del>DB8,DB10</del>DB17,总是按顺序连接MCU的D0~D15</p>
<p>LCD_CS:LCD片选信号<br>LCD_WR:LCD写信号<br>LCD_RD:LCD读信号<br>DB[17:1]:16位双向数据线。<br>LCD_RST:硬复位LCD信号<br>LCD_RS:命令/数据标志<br> (0:命令,1:数据)<br>BL_CTR:背光控制信号<br>T_MISO/T_MOSI/T_PEN/T_CS/T_CLK,触摸屏接口信号</p>
<h3 id="ALINETEK-2-8寸-TFTLCD-16位80并口驱动简介"><a href="#ALINETEK-2-8寸-TFTLCD-16位80并口驱动简介" class="headerlink" title="ALINETEK 2.8寸 TFTLCD 16位80并口驱动简介"></a>ALINETEK 2.8寸 TFTLCD 16位80并口驱动简介</h3><p>模块的8080并口读/写的过程为:<br>先根据要写入/读取的数据的类型,设置RS为高(数据)/低(命令),然后拉低片选,选中ILI9341,接着我们根据是读数据,还是要写数据置RD/WR为低,然后:<br>1.读数据:在RD的上升沿, 读取数据线上的数据(D[15:0]);<br>2.写数据:在WR的上升沿,使数据写入到ILI9341里面</p>