-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
451 lines (272 loc) · 165 KB
/
atom.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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Santos</title>
<link href="/atom.xml" rel="self"/>
<link href="https://wangzipai.github.io/"/>
<updated>2019-07-14T16:04:35.080Z</updated>
<id>https://wangzipai.github.io/</id>
<author>
<name>wangyaoqin</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>802.11无线网络</title>
<link href="https://wangzipai.github.io/posts/8965/"/>
<id>https://wangzipai.github.io/posts/8965/</id>
<published>2019-07-14T09:31:56.000Z</published>
<updated>2019-07-14T16:04:35.080Z</updated>
<content type="html"><![CDATA[<p>有线->无线优点:</p><p>移动性</p><p>弹性(快速部署)</p><p>成本</p><p>无线网络特点:对有线网络的互补而非取代</p><p>MAC:介质访问控制</p><p>指派给802.11网络卡的MAC地址与以太网来自同一地址库,对外面的网络而言,没有区别。802.11的MAC地址也会出现在ARP列表中</p><p>只要时802网络就必然会同时具备MAC与PHY两种元件,MAC用于决定如何访问介质与传输介质的规则,PHY负责传送与接收的细节。</p><p>CSMA/CD:载波侦听/碰撞检测</p><p>CSMA/CA:载波侦听/冲突避免 虚拟载波侦听由网络分配矢量( Network Allocation Vector,简称 NAV)所提供 </p><p>LLC:逻辑链路控制层</p><p>802.11两种物理层:调频展频FHSS 直接序列展频DSSS</p><p>BSS:Basic Service Set,基本服务集。802.11 网络基本组件,每个 BSS 包含一组逻辑上彼此关联的工作站。 </p><p>DS:分布式系统(Distribution System),用来串联接入点的一组服务。逻辑上包含有线骨干网络和桥接功能。</p><p>MIC:消息完整性校验码(message integrity code)。针对一组欲保护数据计算出的散列值,用以防止数据遭篡改。 </p><p>IV:初始化向量(Initialization Vector),加密标头中公开的密钥材料。 </p><p>ICV:完整性校验值(Integrity Check Value),数据帧的校验码,用于防止数据遭篡改。 </p><p>Michael:数据完整性的校验算法,由 TKIP 规范。 </p><p>TLS:传输层安全协议(TLS)是确保互联网上通信应用和其用户隐私的协议(RFC 2246)。当服务器和客户机进行通信,TLS 确保没有第三方能窃听或盗取信息。TLS 是安全套接字层(SSL)的后继协议。 </p><p>无线局域网的基本构成单位是基本业务集 BSS,它提供一个覆盖区域,使 BSS 中的站点保存充分连接。 BSS 支持两种拓扑结构:<br>独立基本服务集 IBSS:称为特别网络(ad hoc network)。是一个独立的 BSS,没有中枢链路基础结构,一个最小的 IBSS 最少包含两个工作站( STA),工作站之间可以直接通信。这种操作模式能够形成灵活的组网机制,且不需要事先规划。<br> 基础结构型基本服务集 Infostructure BSS: STA 必须与 AP 建立关联,才能取得网络服务。 </p><p>多个 BSS 可以构成一个扩展网络(ESS 网络)。 ESS 内部的 STA 可以互相通信,但是 STA通信是必须通过 AP 进行。连接多个 AP(BSS 的组件)称为分布系统( DS)。 </p><p>无线介质本身也可以做为传输系统。此种无线传输系统(wireless distribution system,简称 WDS)的配置通常称为「无线桥接器」 (wireless bridge)配置,因为它允许网络工程师在链路层连接两个局域网络。无线桥接器可用来快速连接不同的网段,十分适合访问供应商( access provider)使用。 </p><p>TPC 是在 802.11h 所定义的新服务。 欧洲标准要求作业于 5 GHz 频段的工作站必须能够控制电波的传输功率,避免干扰其他同样使用 5 GHz 频段的用户。传输功率控制也有助于避免干扰其他无线局域网络。传输距离是传输功率的函数;工作站的传输功率愈高,传输距离就愈远,也就愈容易干扰邻近的网络。如果可以 将传输功率调到“刚刚好”( just right),就可以避免干扰到邻近的工作站。 </p><p>动态选频(DFS),开发的目的主要是为了在欧洲地区避免干扰 5GHz 频段的雷达系统。 </p><p>就 802.11 而言,基站之间可能出现三种转换:</p><p>不转换 </p><p>BSS 转换 </p><p>ESS 转换 (未支持)</p><p>和其他链路层协议不同,802.11 采用正面回应机制。所有传送出去的帧都必须得到回应,只要任何一个环节失败,该帧即视为已经丢掉。 在无线网络中,由隐藏节点所导致的碰撞问题相当难以监听,因为无线收发器通常是半双工工作模式, 即无法同时收发数据。 为了防止碰撞发生, 802.11 允许工作站使用请求发送( RTS)和允许发送(CTS)帧来清空传送区域。由于 RTS 与 CTS 帧会延长数据交易过程,因此 RTS<br>帧、 CTS 帧、数据帧以及最后的应答帧均被视为相同基本连接的一部分。 DCF 是标准 CSMA/CA 访问机制的基础。和以太网一样,在传送数据之前,它会先检查无线链路是否处于空闲状态。为了避免冲突发生,当某个传送者占据频道时,工作站会随机为每个帧选定一段延后时间。在某些情况之下, DCF 可利用 CTS/RTS 空闲技术,进一步减少碰撞发<br>生的可能性。<br>点协调功能提供的是免竞争服务。 称为点协调者的特殊工作站可以确保不必通过竞争即可使用介质。点协调者位于基站,因此只有基础型网络才会使用 PCF。为了赋予比标准竞争式还高的优先性,PCF 允许工作站经过一段较短的时间即可传送帧。PCF 在实际上并不常见<br>有些应用需要尽力传达更高一级的服务质量,却又不需要用到 PCF 那么严格的管控。 HCF允许工作站维护多组服务队列,针对需要更高服务品质的应用,则提拔更多的介质访问机会。HCF 尚未完全标准化,不过最终将成为802.11 标准的一部分。 </p><p>802.11 会用到四种不同的帧间隔:</p><p>SIFS <PIFS <DIFS <EIFS </p><p>定期发送 Beacon,主要是用来声明某个网络的存在。STA 根据 Beacon 得知某个网络存在,从而调整加入该网络所必须的参数。 </p><p>控制帧和管理帧:</p><ol><li>控制帧主要用于协助数据帧的传递,可用于管理无线媒介的访问、提供MAC层的可靠性。以下只讲帧类型,不讲帧结构。<br>1.1 RTS帧:用来取得媒介的控制权,用于传送分段帧,分段由网卡驱动程序中的RTS threshold阀值确定。<br>1.2 CTS帧:用于回复RTS帧,没有RTS就没有CTS。<br>1.3 ACK帧:MAC及任何数据的传送(包括一般传送,RTS/CTS交换之前的帧、帧片段)所需要的肯定确认。<br>1.4 PS-POLL帧:移动式工作站从省电模式苏醒后,回向AP传送一个PS-POLL帧用于获得缓存帧。</li><li>管理帧:802.11的管理过程分为3步,首先移动式工作站找出可供访问的兼容无线网络,其次网络系统对移动式工作站进行验证,最后移动式工作站与AP之间建立关联。帧主体有固定字段(长度固定的字段)和信息元素(长度不定的数据块) </li><li>管理帧的类型<br>3.1 Beacon(信标)帧:主要用于声明某个网络的存在<br>3.2 Probe Request 帧:移动式工作站用于扫描所在区域内有哪些802.11网络。<br>3.3 Probe Resonse帧:如果Probe Request所检查的网络与之兼容,该网络用Probe Request响应。<br>3.4 IBSS ATIM帧:IBSS without AP,无法依赖AP缓存帧,IBSS工作站为处于休眠状态的接收者缓存帧,就会在传递期间送出一个ATIM帧通知对方有消息待传。<br>3.5 Disassociation帧与Deauthentication帧:用于结束一段认证关系<br>3.6 Association Request帧:移动式工作站找到兼容网络并通过身份验证,便发送Association Request帧以试图加入网络。<br>3.7 Reassociation Request 帧:位于相同扩展服务区域,但在不同基本服务区域间游走的移动式工作站,再次使用分布式系统时,必须与网络重新关联。<br>3.8 Association Response帧与Reassociation Response帧:<br>3.9 Authentication帧:<br>3.10 Aciton 帧:用于触发测量动作。</li></ol><p>802.11 工作站可以关闭无线电波收发器,并且定期进入休眠状态,以维持最长的电池使用时间。在这段期间,基站会为每部处于休眠状态的工作站暂存帧。若有暂存帧,基站会在后续的 Beacon 帧中告知工作站。由省电状态唤醒的工作站可以使用 PS-Poll 帧取得这些暂存帧。 </p><p>ACK 或 CTS 之类的应答帧必须以基本速率组合所包含的速率传送, 但不能高于这次传输所使用的起始帧。应答帧必须使用与起始帧相同的调制方式(DSSS、CCK或OFDM)。</p><p>无线基站的核心,其实就是桥接器,负责在无线与有线介质之间转换帧。 </p><p>WEP 用来保护数据的 RC4 密码锁属于对称性密钥串流密码锁 </p><p> 无线加密的多种方法及其区别(WEP WPA TKIP EAP)<br>无线网络的安全性由认证和加密来保证。<br>认证允许只有被许可的用户才能连接到无线网络;<br>加密的目的是提供数据的保密性和完整性(数据在传输过程中不会被篡改)。<br>802.11标准最初只定义了两种认证方法:<br> 开放系统认证(Open System Authentication)<br> 共享密钥认证(Shared Key Authentication)<br>以及一种加密方法:<br> 有线等效保密(Wired Equivalent Privacy – WEP)<br>对于开放系统认证,在设置时也可以启用WEP,此时,WEP用于在传输数据时加密,对认证没有任何作用。<br>对于共享密钥认证,必须启用WEP,WEP不仅用于认证,也用于在传输数据时加密。<br>WEP使用对称加密算法(即发送方和接收方的密钥是一致的),WEP使用40位或104位密钥和24位初始化向量(Initialization Vector – IV,随机数)来加密数据。<br>注:使用初始化变量(IV)的目的是避免在加密的信息中出现相同的数据。例如:在数据传输中,源地址总是相同的,如果只是单纯的加密(WEP使用静态密码),这样在加密的信息中会出现相同的数据,有可能被恶意地破解。由于初始化变量(IV)是随机数,可以避免这种情况的出现。<br>在配置无线网络的安全性时,一般将40位/104位密钥写成密钥长度:64位(40+24)/128位(104+24)<br>由于WEP有一些严重缺陷,如初始化向量的范围有限,而且是使用明文传送……,802.11使用802.1x来进行认证、授权和密钥管理,另外,IEEE开始制订802.11i标准,用于增强无线网络的安全性。<br>同时,Wi-Fi联盟与IEEE一起开发了Wi-Fi受保护的访问(Wi-Fi Protected Access – WPA)以解决WEP的缺陷<br>WPA<br>WPA不同于WEP,WPA同时提供认证(基于802.1x可扩展认证协议 – Extensible Authentiation Protocl - EAP的认证)和加密(临时密钥完整性协议 – Temporal Key Integrity Protocol – TKIP)。<br>WPA的认证 – 802.1x<br>802.1x最初设计用于有线网络,但对无线网络也适用。802.1x使用认证服务器在无线网卡和无线访问点(AP)之间提供基于端口的访问控制和相互认证。<br>802.1x体系结构包括下列3部分:<br>(1)Supplicant:要访问网络的设备,通常是802.11客户端<br>(2)Authenticator:客户端和认证服务器的中间设备,在客户端和认证服务器之间传递信息。对于无线网络来说通常为无线访问点(AP)。<br>(3)Authentication Server(认证服务器):对Supplicant进行实际身份验证的设备,通常是远程认证拨号用户服务(Remote Authentication Dial-In User Service - RADIUS)服务器<br>韩梅梅预约好了去拜访某公司老总,李雷也要去。到了公司门口,保安要求两人出示身份证明,保安通过电话与老总联系后,老总确认后通知可以让韩梅梅进来,李雷不能进去。保安根据老总的指示,对韩梅梅放行,拒绝李雷进入。<br>韩梅梅和李雷相当于802.1x体系中的supplicant,保安相当于authenticator,只起一个传送信息的作用,而不是进行实际的身份验证,老总相当于authentication server,进行实际的身份验证。<br>Supplicant、authenticator、authentication server三者之间需要通信。<br>Supplicant和authenticator之间使用EAPoL通信,authenticator将EAP封装在其他高层协议(如RADIUS)中与authentciation server通信或authenticator将EAPoL转换为其他认证协议(如RADIUS)传递给authentication server。<br>EAP和EAPoL<br>我对EAP和EAPoL的区别一直很模糊。这次好好看了些资料,总结如下:<br>EAP<br>可扩展认证协议(EAP)是一个认证框架,而不是一种特定的认证机制,EAP提供一些公共的功能,并且允许协商认证机制(EAP方法)。EAP规定如何传输和使用由EAP方法产生的密钥材料(如密钥、证书等等)和参数。<br>无线网络中常用的EAP方法(认证机制)包括EAP-TLS、EAP-SIM、EAP-AKA、LEAP和EAP-TTLS。<br>EAPoL(EAP over LAN)<br>EAP只定义了消息格式,每种使用EAP的协议必须定义一种将EAP消息封装到该协议消息中的方法。<br>802.1x中定义了将EAP消息封装到802中的方法,这种方法称为EAP over LAN – EAPoL。所以EAPoL实际上是一种传送机制。实际的认证方法是EAP方法来指定。<br>802.1x认证的核心是authenticator上端口的打开与关闭。对于合法用户(认证通过的用户)接入时,端口打开,可以自由访问网络;对于没有用户接入或非法用户(认证未通过的用户)接入时,端口关闭,这样就不能访问网络。</p><p>802.1x认证过程中还使用了动态密钥以加密数据。<br>WPA的加密 – TKIP<br>临时密钥完整性协议(Temporal Key Integrity Protocol – TKIP)也是对称加密方法,使用RC4算法,TKIP使用128位临时密钥和48位初始化向量(IV)<br>总结<br>WPA的优点:<br>(1)WPA利用802.1x认证提供强力的访问控制;<br>(2)TKIP使用动态密钥<br>WPA缺点:<br>(1)由于TKIP使用RC4算法,安全隐患<br>(2)复杂的认证和加密导致性能降低 </p>]]></content>
<summary type="html">
<p>有线-&gt;无线优点:</p>
<p>移动性</p>
<p>弹性(快速部署)</p>
<p>成本</p>
<p>无线网络特点:对有线网络的互补而非取代</p>
<p>MAC:介质访问控制</p>
<p>指派给802.11网络卡的MAC地址与以太网来自同一地址库,对外面的网
</summary>
<category term="无线" scheme="https://wangzipai.github.io/tags/%E6%97%A0%E7%BA%BF/"/>
</entry>
<entry>
<title>AP和STA模式</title>
<link href="https://wangzipai.github.io/posts/2039/"/>
<id>https://wangzipai.github.io/posts/2039/</id>
<published>2019-06-01T14:52:41.000Z</published>
<updated>2019-06-01T15:01:25.250Z</updated>
<content type="html"><![CDATA[<p>wifi模块包括两种工作模式AP和STA。</p><p>AP:(Access Point)无线接入点。</p><p>STA:(Station)每一个连接到无线网络中的中断都可称为一个站点。</p><p><strong>工作在AP模式</strong></p><p>工作在AP模式下,手机、PAD、电脑等设备可以直接连上模块,可以很方便对用户设备进行控制</p><p><strong>工作在STA模式</strong></p><p>这是一种基木的组网方式,由一个AP和许多STA组成,如下图。其特点是AP处于中心地位,STA之间的相互通信都通过AP转发完成。该模式下,WIFI模块工作在STA(CLIENT)模式。通过适当的设置,COM的数据与WIFI的网路数据相互转换。</p><p><strong>AP+STA</strong></p><p>感觉就是把路由当作中继使用了。</p>]]></content>
<summary type="html">
<p>wifi模块包括两种工作模式AP和STA。</p>
<p>AP:(Access Point)无线接入点。</p>
<p>STA:(Station)每一个连接到无线网络中的中断都可称为一个站点。</p>
<p><strong>工作在AP模式</strong></p>
<p>工
</summary>
<category term="AP" scheme="https://wangzipai.github.io/tags/AP/"/>
</entry>
<entry>
<title>点运算符和箭头运算符的区别</title>
<link href="https://wangzipai.github.io/posts/48012/"/>
<id>https://wangzipai.github.io/posts/48012/</id>
<published>2019-05-10T16:00:48.000Z</published>
<updated>2019-05-11T06:57:06.427Z</updated>
<content type="html"><![CDATA[<p>写链表的时候突然想到.和->的区别是什么,之前纠结了很久,但是因为不用C语言也就没去查找。</p><p>点运算符“.”应用于实际的对象,箭头运算符“->”与一个指针对象的指针一起使用。</p><p>对于一个结构体:</p><a id="more"></a><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">A</span>{</span></span><br><span class="line"> <span class="keyword">int</span> a;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>如果有一个变量<code>A n</code>,那么使用其中的成员元素时:</p><p><code>n.a = 1;</code></p><p>但如果用指针的方法访问,<code>A *n</code>,那么使用他的元素时也要用指针</p><p><code>(*n).a = 1;</code></p><p>或者直接:</p><p><code>n->a = 1;</code></p><p>所以,箭头的左边必须为指针,点号的左边必须为实体。</p>]]></content>
<summary type="html">
<p>写链表的时候突然想到.和-&gt;的区别是什么,之前纠结了很久,但是因为不用C语言也就没去查找。</p>
<p>点运算符“.”应用于实际的对象,箭头运算符“-&gt;”与一个指针对象的指针一起使用。</p>
<p>对于一个结构体:</p>
</summary>
<category term="C语言" scheme="https://wangzipai.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
</entry>
<entry>
<title>错题(1)</title>
<link href="https://wangzipai.github.io/posts/61141/"/>
<id>https://wangzipai.github.io/posts/61141/</id>
<published>2019-05-09T02:07:40.000Z</published>
<updated>2019-05-11T06:56:32.655Z</updated>
<content type="html"><![CDATA[<p>题目:输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。</p><a id="more"></a><p>开始没用指针做,觉得还挺简单的。后来发现参考程序是用指针写的,就想着也用指针写一下,然后就发现了一堆错误。。</p><p>第一次代码:</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="keyword">void</span> input(<span class="keyword">int</span> a[]);</span><br><span class="line"><span class="keyword">void</span> output(<span class="keyword">int</span> a[]);</span><br><span class="line"><span class="keyword">void</span> max_min(<span class="keyword">int</span> a[]);</span><br><span class="line"><span class="keyword">int</span> main(){</span><br><span class="line"><span class="keyword">int</span> a[<span class="number">5</span>];</span><br><span class="line">input(a);</span><br><span class="line">max_min(a);</span><br><span class="line">output(a);</span><br><span class="line"><span class="built_in">return</span> <span class="number">0</span>;</span><br><span class="line">} </span><br><span class="line"><span class="keyword">void</span> input(<span class="keyword">int</span> a[]){</span><br><span class="line"><span class="built_in">for</span>( <span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++ ){</span><br><span class="line">scanf( <span class="string">"%d"</span>, &a[i] );</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">void</span> output(<span class="keyword">int</span> *a){</span><br><span class="line"><span class="keyword">int</span> *p;</span><br><span class="line"><span class="built_in">for</span>( p = a; p < a+<span class="number">5</span>; p++ ){</span><br><span class="line">printf( <span class="string">"%d,"</span>, *p );</span><br><span class="line">} </span><br><span class="line">}</span><br><span class="line"><span class="keyword">void</span> max_min(<span class="keyword">int</span> a[]){</span><br><span class="line"><span class="keyword">int</span> *p;</span><br><span class="line"><span class="keyword">int</span> *<span class="built_in">max</span>, *<span class="built_in">min</span>, *t1, *t2;</span><br><span class="line"><span class="built_in">max</span> = <span class="built_in">min</span> = a;</span><br><span class="line"><span class="built_in">for</span>( p = a; p < a+<span class="number">5</span>; p++ ){</span><br><span class="line"><span class="built_in">if</span>( *<span class="built_in">max</span> < *p ){</span><br><span class="line"><span class="built_in">max</span> = p;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">if</span>( *<span class="built_in">min</span> > *p ){</span><br><span class="line"><span class="built_in">min</span> = p;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="built_in">if</span>( *a != *<span class="built_in">max</span> ){</span><br><span class="line">*t1 = *a;</span><br><span class="line">*a = *<span class="built_in">max</span>;</span><br><span class="line">*<span class="built_in">max</span> = *t1;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">if</span>( *(a+<span class="number">4</span>) != *<span class="built_in">min</span> ){</span><br><span class="line">*t1 = *(a+<span class="number">4</span>);</span><br><span class="line">*(a+<span class="number">4</span>) = *<span class="built_in">min</span>;</span><br><span class="line">*<span class="built_in">min</span> = *t1;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行程序发现,输出结果错误。注释掉 max_min 函数的后面两个if语句,打印此时的 <em>max 和 </em>min 发现最大值和最小值无误。所以问题在于后面的两个if语句中。观察代码发现,因为我输入的5个数是1,2,3,4,5.所以此时max指向的内存地址为a+5的地址,而min指向的地址为a的地址,而我直接赋值 <em>a 就导致了前面定义的所有指向a内存空间的指针里的值发生了改变,这时 min 指向 a ,所以 </em>min的值也发生了改变。</p><p>第二次代码:</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> max_min(<span class="keyword">int</span> a[]){</span><br><span class="line"><span class="keyword">int</span> *p;</span><br><span class="line"><span class="keyword">int</span> *<span class="built_in">max</span>, *<span class="built_in">min</span>, *t1 ;</span><br><span class="line"><span class="built_in">max</span> = <span class="built_in">min</span> = a;</span><br><span class="line"><span class="keyword">int</span> k, l;</span><br><span class="line"><span class="built_in">for</span>( p = a; p < a+<span class="number">5</span>; p++ ){</span><br><span class="line"><span class="built_in">if</span>( *<span class="built_in">max</span> < *p ){</span><br><span class="line"><span class="built_in">max</span> = p;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">if</span>( *<span class="built_in">min</span> > *p ){</span><br><span class="line"><span class="built_in">min</span> = p;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">k = *<span class="built_in">max</span>;</span><br><span class="line">l = *<span class="built_in">min</span>;</span><br><span class="line"><span class="built_in">if</span>( *a != k ){</span><br><span class="line">*t1 = *a;</span><br><span class="line">*a = k;</span><br><span class="line">*<span class="built_in">max</span> = *t1;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">if</span>( *(a+<span class="number">4</span>) != l ){</span><br><span class="line">*t1 = *(a+<span class="number">4</span>);</span><br><span class="line">*(a+<span class="number">4</span>) = l;</span><br><span class="line">*<span class="built_in">min</span> = *t1;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行程序还是错误,仔细观察代码发现,在函数中只是声明了 *t1 这个指针,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间,指针变量并未被初始化为指向任何现有的内存空间,又因为他是自动变量,所以无法被初始化。然后将t1指向a的内存空间,犯了第一次的错误。。</p><p>第三次代码:</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> max_min(<span class="keyword">int</span> a[]){</span><br><span class="line"><span class="keyword">int</span> *p;</span><br><span class="line"><span class="keyword">int</span> *<span class="built_in">max</span>, *<span class="built_in">min</span>;</span><br><span class="line"><span class="built_in">max</span> = <span class="built_in">min</span> = a;</span><br><span class="line"><span class="keyword">int</span> j, k, l;</span><br><span class="line"><span class="built_in">for</span>( p = a; p < a+<span class="number">5</span>; p++ ){</span><br><span class="line"><span class="built_in">if</span>( *<span class="built_in">max</span> < *p ){</span><br><span class="line"><span class="built_in">max</span> = p;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">if</span>( *<span class="built_in">min</span> > *p ){</span><br><span class="line"><span class="built_in">min</span> = p;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">k = *<span class="built_in">max</span>;</span><br><span class="line">l = *<span class="built_in">min</span>;</span><br><span class="line"><span class="built_in">if</span>( *a != k ){</span><br><span class="line">j = *a;</span><br><span class="line">*a = k;</span><br><span class="line">*<span class="built_in">max</span> = j;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">if</span>( *(a+<span class="number">4</span>) != l ){</span><br><span class="line">j = *(a+<span class="number">4</span>);</span><br><span class="line">*(a+<span class="number">4</span>) = l;</span><br><span class="line">*<span class="built_in">min</span> = j;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>成功完成题目。。</p>]]></content>
<summary type="html">
<p>题目:输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。</p>
</summary>
<category term="C语言" scheme="https://wangzipai.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
<category term="错题" scheme="https://wangzipai.github.io/tags/%E9%94%99%E9%A2%98/"/>
</entry>
<entry>
<title>C语言关键字</title>
<link href="https://wangzipai.github.io/posts/41598/"/>
<id>https://wangzipai.github.io/posts/41598/</id>
<published>2019-05-06T15:37:05.000Z</published>
<updated>2019-05-07T08:42:17.796Z</updated>
<content type="html"><![CDATA[<p>C语言最早有32个关键字。其中auto,register,extern,static这4个提供4种存储类别。四种存储类型中有两种存储期:自动存储期和静态存储期。</p><a id="more"></a><p>其中auto和register对应自动存储期,具有自动存储期的变量在进入声明该变量的程序块是被建立,在该程序块活动的时候存在,退出该程序块时撤销。实际上,自动变量是一个局部变量,其作用域为包含他的代码块。代码块是被包含在花括号中的一段代码。</p><p>自动变量通常存储在栈中。这意味着执行代码块时,其中的变量依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量。</p><p>extern,static对应静态存储期,静态存储在整个程序执行期都存在。分为两块<a href="https://blog.csdn.net/u014170207/article/details/79104262" target="_blank" rel="noopener"><strong>参考</strong></a>:.date 已初始化的全局变量或静态变量 .bss 存放未初始化<strong><em>或初始值为0</em></strong>的全局或者静态变量。静态变量的初值是在编译时就进行了初始化,用static修饰的变量赋过数值的话就保存为他的初值,如果没有初始化的话就赋值为零,且整个程序只初始化一次。</p><p>new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap)。该内存池同用于静态变量和自动变量的内存是分开的。new和delete让您能够在一个函数中分配内存,而在另一个函数中释放它。因此,数据的声明周期不完全收程序或函数的生命时间控制。与使用常规变量相比,使用new和delete让程序员对程序如何使用内存有更大的控制权。然而,内存管理也更复杂了。在栈中,自动添加和删除机制使得占用的内存总是连续的,单new和delete的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难。</p><h3 id="auto"><a href="#auto" class="headerlink" title="auto"></a>auto</h3><p>C语言和C++中auto关键字的使用有很大区别。在C语言中使用auto关键字声明一个变量为自动变量,是C语言中应用最广泛的一种类型,在函数内定义变量时,如果没有被声明为其他类型的变量都是自动变量,也就是说,省去类型说明符auto的都是自动变量。当省略数据类型,只使用auto修饰变量,在C语言中默认变量为int型</p><h3 id="register"><a href="#register" class="headerlink" title="register"></a>register</h3><p>如果一个变量被register来修辞,就意味着,该变量会作为一个寄存器变量,让该变量的访问速度达到最快。比如:一个程序逻辑中有一个很大的循环,循环中有几个变量要频繁进行操作,这些变量可以声明为register类型。</p><p>寄存器变量:寄存器变量是指一个变量直接引用寄存器,也就是对变量名的操作的结果是直接对寄存器进行访问。寄存器是CPU的亲信,CPU操作的每个操作数和操作结果,都由寄存器来暂时保存,最后才写入到内存或从内存中读出。也就是说,变量的值通常保存在内存中,CPU对变量进行读取先是将变量的值从内存中读取到寄存器中,然后进行运算,运算完将结果写回到内存中。</p><p>在使用寄存器变量时,请注意:</p><ul><li>待声明为寄存器变量类型应该是CPU寄存器所能接受的类型,意味着寄存器变量是单个变量,变量长度应该小于等于寄存器长度</li><li>不能对寄存器变量使用取地址符“&”,因为该变量没有内存地址</li><li>尽量在大量频繁的操作时使用寄存器变量,且声明的变量个数应该尽量的少</li></ul><h3 id="static"><a href="#static" class="headerlink" title="static"></a>static</h3><p>静态全局变量:当进程中一个全局变量被static修饰后(被声明为static),则该全局变量被称为静态全局变量。它的作用域是仅在它的源文件内,其它源文件都无法访问它。</p><p>全局变量:隐式被static修饰的全局变量,作用域也是仅在它的源文件内,不能被其它源文件访问。但与静态全局变量(显式被static修饰的全局变量)不同的是全局变量在其它源文件中可以通过extern声明后访问,而静态全局变量则无法访问。</p><p>静态局部变量:局部变量被static修饰后,则称为静态局部变量。静态局部变量则被存放在data段内(定义时,如果用户没有初始化,编译器会自动将其初始化为0),而且整个进程周期中,只定义和初始化一次,每次调用局部函数时,静态局部变量都会维持最后一次修改的值,作用域是局部代码段。</p><p>static函数:被关键字static修饰的函数。static函数作用域是源文件,即其他源文件无法调用该函数,类似的private函数。</p><p>代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="keyword">int</span> a=<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">f</span><span class="params">(<span class="keyword">int</span> c)</span></span></span><br><span class="line"><span class="function"></span>{ </span><br><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> a=<span class="number">2</span>;</span><br><span class="line">c++;</span><br><span class="line"><span class="keyword">return</span> (a++)+c; </span><br><span class="line">}</span><br><span class="line">main()</span><br><span class="line">{ </span><br><span class="line"><span class="keyword">int</span> i,k=<span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(i=<span class="number">0</span>;i<<span class="number">2</span>;i++)</span><br><span class="line">{ </span><br><span class="line"><span class="keyword">int</span> a=<span class="number">0</span>; </span><br><span class="line">k+=f(a); </span><br><span class="line">}</span><br><span class="line">k+=a;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>,k); </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>此时,变量a作为全局变量、局部静态变量和局部变量存在。但a作为不同变量存在时作用域也不同,并且没有联系。(ps: a++:先执行表达式后再自增,执行表达式时使用的是a的原值.++a:先自增再执行表达示,执行表达式时使用的是自增后的a。)然而,实际开发中,这么写真的不会被打死吗。。</p><h3 id="extern"><a href="#extern" class="headerlink" title="extern"></a>extern</h3><p>extern声明变量:表明该变量在其他源文件里已经被定义,此处需要使用。extern声明的变量必须是在其他源文件内的非静态的全局变量。</p><h3 id="volatile"><a href="#volatile" class="headerlink" title="volatile"></a>volatile</h3><p>volatile字面意思是易挥发,易变化的意思,它修辞的变量表示该变量的值很容易由于外部因素发生改变,强烈请求编译器要老老实实的在每次对变量进行访问时去内存里读取。</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">int a</span> = 10;</span><br><span class="line"></span><br><span class="line"><span class="attribute">int b</span> = a;</span><br><span class="line"></span><br><span class="line"><span class="attribute">int c</span> = a;</span><br></pre></td></tr></table></figure><p>编译器扫描了代码发现上面,第一行代码在将10赋给了整形变量a,之后a变量的值没有再发生改变。在后面第二行中,将a变量里的值取出来赋给b变量。在第三行代码里将a变量的值赋给c的时候,因为CPU访问内存速度较慢,编译器为了提高效率,直接将10赋给了c。上述代码如果运行在多线程中,在一个线程上下文中没有改变它的值,但是我们不能保证变量的值没有被其它线程改变。为了防止在类似的情况下,编译器玩省事,可以将这些变量声明为volatile,这样,不管它的值有没有变化,每次对其值进行访问时,都会从内存里,寄存器里读取,从而保证数据的一致,做到表里如一。</p>]]></content>
<summary type="html">
<p>C语言最早有32个关键字。其中auto,register,extern,static这4个提供4种存储类别。四种存储类型中有两种存储期:自动存储期和静态存储期。</p>
</summary>
<category term="C语言" scheme="https://wangzipai.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
</entry>
<entry>
<title>尾调用和尾递归</title>
<link href="https://wangzipai.github.io/posts/5759/"/>
<id>https://wangzipai.github.io/posts/5759/</id>
<published>2019-05-02T02:33:23.000Z</published>
<updated>2019-05-06T12:38:52.665Z</updated>
<content type="html"><![CDATA[<h3 id="尾调用"><a href="#尾调用" class="headerlink" title="尾调用"></a>尾调用</h3><p>尾调用是指一个函数的最后一个动作是一个函数调用的情况,即这个调用的返回值直接被当前函数返回。调用函数<code>foo</code>之后,还有别的操作,所以不属于尾调用,即使语义一样。调用后也有别的操作,所以不属于尾调用,即使写在同一行。</p><a id="more"></a><h3 id="栈帧"><a href="#栈帧" class="headerlink" title="栈帧"></a>栈帧</h3><p>栈帧将栈分割成几个记录块,每一块大小不相同。这些记录块是编译器用来实现函数调用的数据结构,用于记录每次函数调用所涉及的相关信息的记录单元。栈帧是函数的执行环境,它包括函数的参数,函数的局部变量,函数执行完之后要返回的位置等。</p><h3 id="尾调用的优化"><a href="#尾调用的优化" class="headerlink" title="尾调用的优化"></a>尾调用的优化</h3><p>函数调用会在栈上形成一个栈帧,如果函数A调用函数B,那么在A的栈帧下方,还会形成B的栈帧。等到B函数的返回,B的栈帧才会消失。如果函数B又调用函数C,那么B的下方又会形成C的栈帧。以此类推,所有的栈帧堆叠起来,就形成一个“调用栈”。</p><p>由于尾调用是外层函数的最后一步操作,尾调用返回后,外层函数也就返回了。执行尾调用的时候,外层函数栈帧中保存的调用位置、内部变量等信息都不会再用到了,所以,可以用内层函数(即尾调用函数)的栈帧覆盖外层函数的栈帧,而不是在外层函数栈帧下面再新开一个,这样可以节省占空间。这就叫尾调用优化。</p><h3 id="尾递归"><a href="#尾递归" class="headerlink" title="尾递归"></a>尾递归</h3><p>若一个函数在尾位置调用自身,就是尾递归。尾递归是递归的一种特殊情况。每一次递归时return的内容不再是一个表达式,而是这个函数本身。这样使用一个栈帧就可以了,这个函数有着良好的栈帧复用性。</p>]]></content>
<summary type="html">
<h3 id="尾调用"><a href="#尾调用" class="headerlink" title="尾调用"></a>尾调用</h3><p>尾调用是指一个函数的最后一个动作是一个函数调用的情况,即这个调用的返回值直接被当前函数返回。调用函数<code>foo</code>之后,还有别的操作,所以不属于尾调用,即使语义一样。调用后也有别的操作,所以不属于尾调用,即使写在同一行。</p>
</summary>
<category term="C语言" scheme="https://wangzipai.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
</entry>
<entry>
<title>C语言显示拓展的ascii码</title>
<link href="https://wangzipai.github.io/posts/8475/"/>
<id>https://wangzipai.github.io/posts/8475/</id>
<published>2019-04-28T15:25:43.000Z</published>
<updated>2019-05-06T12:38:28.022Z</updated>
<content type="html"><![CDATA[<p>今天做题时,发现原本应该显示图案变成了乱码。</p><a id="more"></a><p>源码:</p><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#include "stdio.h"</span></span><br><span class="line"><span class="keyword">int</span> main(void)</span><br><span class="line">{</span><br><span class="line">char a=<span class="number">176</span>,b=<span class="number">219</span>;</span><br><span class="line"><span class="keyword">printf</span>(<span class="string">"%c%c%c%c%c\n"</span>,b,a,a,a,b);</span><br><span class="line"><span class="keyword">printf</span>(<span class="string">"%c%c%c%c%c\n"</span>,a,b,a,b,a);</span><br><span class="line"><span class="keyword">printf</span>(<span class="string">"%c%c%c%c%c\n"</span>,a,a,b,a,a);</span><br><span class="line"><span class="keyword">printf</span>(<span class="string">"%c%c%c%c%c\n"</span>,a,b,a,b,a);</span><br><span class="line"><span class="keyword">printf</span>(<span class="string">"%c%c%c%c%c\n"</span>,b,a,a,a,b);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="//wangzipai.github.io/posts/8475/TIM截图20190428233248.png" alt></p><p>网上查了一下原因,176的16进制是B0,219的16进制是DB,0xB0DB是“佰”字的内码,所以输出的就是“佰”了。主要原因是文件信息的代码页不同,我们所使用的操作系统中文状态下的代码页,要显示扩展的ASCII码需要在437 OEM-美国这个下面显示,这样就可以显示出你所希望的。</p><p>修改方法:</p><p> 1.右击运行界面上边框,选择默认值一项</p><p> 2.选中使用旧版控制台,重启窗口</p><p> 3.修改默认代码页,936(ANSI/OEM-简体中文GBK)为437 OEM-美国</p><p> 4.关闭后重新运行一下即可</p><p>修改后:</p><p><img src="//wangzipai.github.io/posts/8475/TIM截图20190428233944.png" alt></p>]]></content>
<summary type="html">
<p>今天做题时,发现原本应该显示图案变成了乱码。</p>
</summary>
<category term="C语言" scheme="https://wangzipai.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
</entry>
<entry>
<title>打印</title>
<link href="https://wangzipai.github.io/posts/36499/"/>
<id>https://wangzipai.github.io/posts/36499/</id>
<published>2019-04-22T13:45:24.000Z</published>
<updated>2019-05-04T14:53:17.588Z</updated>
<content type="html"><![CDATA[<figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">#include <stdio.h></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line">printf ( "<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::::::./<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ::::::::::: FUCK YOU<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ..:::::::::::'<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> '::::::::::::'<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::::::::<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> '::::::::::::::..<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ..::::::::::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ``::::::::::::::::<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ::::``:::::::::' .:::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ::::' ':::::' .::::::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::' :::: .:::::::'::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .:::' ::::: .:::::::::' ':::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::' :::::.:::::::::' ':::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::' ::::::::::::::' ``::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ...::: ::::::::::::' ``::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ```` ':. ':::::::::' ::::..<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> '.:::::' ':'````..<span class="tag">\<span class="name">n</span></span>");</span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><a id="more"></a><p>以上是一段打印的代码,两点需要注意:</p><ol><li><p>转义字符</p><p>在 C 语言中,用双引号括起来的内容我们称之为字符串,也就是我们平时所说的文本。</p><p>字符串可以由可见字符和转义字符组成,可见字符就是你输入什么,显示出来就是什么。</p><p>而你如果想将一个字符串分为两行来显示,那么你就需要使用到转义字符。</p><p>转义字符一般是表示特殊含义的非可见字符,以反斜杠开头: </p><p><img src="//wangzipai.github.io/posts/36499/转移字符.jpg" alt></p></li><li><p>反斜杠</p><p>在字符串中反斜杠 + 字符是转义字符,表示特殊含义。</p><p>但反斜杠如果后边不带任何字符(直接换行),表示我们希望 C 语言将该行以及下一行看做是一个整体。</p></li></ol>]]></content>
<summary type="html">
<figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">#include &lt;stdio.h&gt;</span><br><span class="line">int main()</span><br><span class="line">&#123;</span><br><span class="line"> printf ( "<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::::::./<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ::::::::::: FUCK YOU<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ..:::::::::::'<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> '::::::::::::'<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::::::::<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> '::::::::::::::..<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ..::::::::::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ``::::::::::::::::<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ::::``:::::::::' .:::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ::::' ':::::' .::::::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::::' :::: .:::::::'::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .:::' ::::: .:::::::::' ':::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::' :::::.:::::::::' ':::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> .::' ::::::::::::::' ``::::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ...::: ::::::::::::' ``::.<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> ```` ':. ':::::::::' ::::..<span class="tag">\<span class="name">n</span></span><span class="tag">\</span></span><br><span class="line"> '.:::::' ':'````..<span class="tag">\<span class="name">n</span></span>");</span><br><span class="line"> return 0;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
</summary>
<category term="C语言" scheme="https://wangzipai.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
</entry>
<entry>
<title>输入捕获</title>
<link href="https://wangzipai.github.io/posts/36750/"/>
<id>https://wangzipai.github.io/posts/36750/</id>
<published>2019-04-17T14:46:51.000Z</published>
<updated>2019-05-04T14:53:17.588Z</updated>
<content type="html"><![CDATA[<p>stm32的输入捕获:通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。</p><a id="more"></a><p><img src="//wangzipai.github.io/posts/36750/输入捕获工作过程.png" alt></p><h3 id="步骤1:设置输入捕获滤波器(通道1为例)"><a href="#步骤1:设置输入捕获滤波器(通道1为例)" class="headerlink" title="步骤1:设置输入捕获滤波器(通道1为例)"></a>步骤1:设置输入捕获滤波器(通道1为例)</h3><p>输入捕获寄存器IC1F[3:0],这个用来设置输入采样频率和数字滤波器长度。其中,fCK_INT是定时器的输入频率(TIMxCLK),一般为72MHz,而fDTS则是根据TIMx_CR1的CKD[1:0]的设置来确定的,如果CDK[1:0]设置为00,那么fDTS=fCK_INT。N值就是滤波长度,举个简单的例子:假设IC1F[3:0]=0011,并设置IC1映射到通道1上,且为上升沿触发,那么在捕获到上升沿的时候,在以fCK_INT的频率,连续采到8次通道1的电平,如果都是高电平,则说明却是一个有效的触发,就会触发输入捕获中断。这样就可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤波的效果。</p><p><img src="//wangzipai.github.io/posts/36750/步骤2.png" alt></p><h3 id="步骤2:设置输入捕获极性(通道1为例)"><a href="#步骤2:设置输入捕获极性(通道1为例)" class="headerlink" title="步骤2:设置输入捕获极性(通道1为例)"></a>步骤2:设置输入捕获极性(通道1为例)</h3><p>CC1P:输入/捕获1输出极性</p><p>CC1通道配置为输出:</p><p>0:OC1高电平有效</p><p>1:OC1低电平有效</p><p>CC1通道配置为输入:</p><p>该位选择是IC1还是IC1的相反信号作为触发或捕获信号。</p><p>0:不反相:捕获发生在IC1的上升沿:当用作外部触发器时,IC1不反相。</p><p>1:反相: 捕获发生在IC1的下降沿:当用作外部触发器时,IC1反相。</p><h3 id="步骤三:设置输入捕获映射通道(通道1为例)"><a href="#步骤三:设置输入捕获映射通道(通道1为例)" class="headerlink" title="步骤三:设置输入捕获映射通道(通道1为例)"></a>步骤三:设置输入捕获映射通道(通道1为例)</h3><p><img src="//wangzipai.github.io/posts/36750/步骤3.png" alt></p><p>CC1S[1:0]:捕获/比较1选择</p><p>这2位定义通道的方向(输入/输出),及输入脚的选择:</p><p>00:CC1通道被配置为输出</p><p>01:CC1通道被配置为输入,IC1映射在TI1上</p><p>10:CC1通道被配置为输入,IC1映射在TI2上</p><p>11:CC1通道被配置为输入,IC1映射在TRC上。此模式仅工作在内部触发器输入被选中时(由TIMx_SMCR寄存器的TS位选择)</p><p>CC1S仅在通道关闭时(TIMx_CCER寄存器的CC1E=‘0’)才是可写的。</p><h3 id="步骤四:设置输入捕获分频器(通道1为例)"><a href="#步骤四:设置输入捕获分频器(通道1为例)" class="headerlink" title="步骤四:设置输入捕获分频器(通道1为例)"></a>步骤四:设置输入捕获分频器(通道1为例)</h3><p><img src="//wangzipai.github.io/posts/36750/步骤4.png" alt></p><p>IC1PSC[1:0]输入/捕获1预分频器</p><p>这2位定义了CC1输入(IC1)的预分频系数,一旦CC1E=‘0’时(TIMx_CCER寄存器中),则预分频器复位。(分别位1、2、4、8触发一次捕获)</p><p>CC1E:输入/捕获1输出使能</p><p>CC1通道配置为输出</p><p>0:关闭——OC1禁止输出</p><p>1:开启——OC1信号输出到对应的输出引脚</p><p>CC1通道配置为输入</p><p>该位决定了计数器的值是否能捕获TIMx_CCR1寄存器</p><p>0:禁止</p><p>1:使能</p><h3 id="步骤五:捕获到有效信号可以开启中断"><a href="#步骤五:捕获到有效信号可以开启中断" class="headerlink" title="步骤五:捕获到有效信号可以开启中断"></a>步骤五:捕获到有效信号可以开启中断</h3><p><img src="//wangzipai.github.io/posts/36750/捕获中断.png" alt></p>]]></content>
<summary type="html">
<p>stm32的输入捕获:通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
<entry>
<title>C语言学习随笔(1)</title>
<link href="https://wangzipai.github.io/posts/48322/"/>
<id>https://wangzipai.github.io/posts/48322/</id>
<published>2019-04-16T15:53:05.000Z</published>
<updated>2019-05-11T06:58:26.289Z</updated>
<content type="html"><![CDATA[<ol><li><p>用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)</p><figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#define SECONDS_PER_YEAR (<span class="number">60</span> * <span class="number">60</span> * <span class="number">24</span> * <span class="number">365</span>)UL</span><br></pre></td></tr></table></figure><p>UL(表示无符号长整型),如果不写UL后缀,系统默认为:int, 即,有符号整数。</p><a id="more"></a></li></ol><p>1.数值常数有:整型常数、浮点常数;<br>2.只有数值常数才有后缀说明;<br>3.数值常数后缀不区分字母大小写。<br>(1)整型常数的表示形式有:十进制形式、以0开头的八进制形式、以0x开头的十六进制形式,无二进制形式。<br>整型常数默认是signed int的。<br>对整型常数进行类型转换的后缀只有:u或U(unsigned)、l或L(long)、u/U与l/L的组合(如:ul、lu、Lu等)。例:100u; -123u; 0x123l;<br>(2)浮点常数的表示形式有:科学计数形式和小数点形式。<br>浮点常数默认是double的。<br>对浮点常数进行类型转换的后缀只有:f或F(单精度浮点数)、l或L(长双精度浮点数)。(注:因浮点型常数总是有符号的,故没有u或U后缀)。例:1.23e5f; 1.23l; -123.45f;</p><p>数据类型</p><p>C 中的类型可分为以下几种:<img src="//wangzipai.github.io/posts/48322/nodejs\blog\source\_posts\C语言学习随笔\C 中的类型.png" alt></p><ol start="2"><li><p>三元运算符耗时比if-else少<a href="https://blog.csdn.net/prestonzzh/article/details/52538541" target="_blank" rel="noopener">原博</a></p><p>原理:</p><p>CPU处理模式:</p><p>CPU是通过流水线处理来获得高性能的。所谓流水线,简单来说就是当CPU在处理当前指令的时候,后面已经有N条指令在后面排队等待你去执行了。这样,当你要执行下一条指令的时候,你不用再去找那条指令,它已经乖乖跟在前一条指令的屁股后面等你去执行了。</p><p>if…else…处理模式:</p><p>那问题就在于,后面的指令需要确认一个排队顺序。如果程序员也是简单的编写流水线式的代码,对于CPU来说指令排序很容易。但是if…else…就不一样了。if…else…简单来说就是:当我满足条件,那我就执行A,如果我不满足条件,我就执行B。但是对于给指令排队的CPU来说,它还没执行到判断条件这一步,不知道你满不满足呀!这样它就不好给指令排队了。假设它按照你满足条件,把A指令排在你后面。那么当执行到最后发现你不满足条件,那么就得把之前排好的队列清空,重新给你把B指令排到后面给你执行。这种预测错误的惩罚将会导致CPU处理时间更长。假设预测准确的话,每次调用函数大概13个时钟周期,而只要预测错误,可能就需要大约44个时钟周期了。</p><p>三元运算处理模式:</p><p>对于三元运算符,它的处理方法就不同了。<br>x=y>0?A:B;<br>当CPU要给这条指令排队时,它会将两种结果都进行排队,也就是表达式A和表达式B都会被CPU进行处理,算出结果。计算对CPU来说反而是它更喜欢的事情,你只要能排队排好了,让它能流水线模式来执行,对于它来说总体速度反而更快。CPU会将两种结果A和B都给计算出来(这跟if…else…不同,其只会计算一种结果),最后再判断y>0?。如果y>0,则选择A,抛弃B; 否则就选择B,抛弃A。</p></li><li><p>iostream与iostream.h的区别详细解析<a href="https://blog.csdn.net/wcq3692012/article/details/21225827" target="_blank" rel="noopener">原帖</a></p><p>有两种用法:<br>A:<br>include<iostream.h><br>include<fstream.h></p><p>B:</p><p>include<iostream><br>include<fstream></fstream></iostream></p><p>A是标准用法,B是老式用法。<br>如果用了<iostream>,则一定要引入命名空间,即”using namespace std;”.<br>如果用了<iostream.h>,则不要引入命名空间,否则会引起编译错误,提示找不到命名空间</iostream></p></li><li><p>题目:输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。</p><p>代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">char</span> a[<span class="number">20</span>];</span><br><span class="line">gets(a); </span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> i, num=<span class="number">0</span>, cha=<span class="number">0</span>, space=<span class="number">0</span>, total;</span><br><span class="line">total = <span class="built_in">strlen</span>(a);</span><br><span class="line"><span class="keyword">for</span>( i=<span class="number">0</span>; a[i]!=<span class="string">'\0'</span>; i++ ){</span><br><span class="line"><span class="keyword">if</span>( a[i]>=<span class="string">'0'</span> && a[i]<=<span class="string">'9'</span> ) num++;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>( (a[i]>=<span class="string">'a'</span> && a[i]<=<span class="string">'z'</span>) || (a[i]>=<span class="string">'A'</span> && a[i]<=<span class="string">'Z'</span>) ) cha++;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>( a[i]==<span class="string">' '</span> ) space++;</span><br><span class="line">} </span><br><span class="line"><span class="built_in">cout</span> << <span class="string">"数字:"</span> << num << <span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">cout</span> << <span class="string">"字母:"</span> << cha << <span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">cout</span> << <span class="string">"空格:"</span> << space << <span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">cout</span> << <span class="string">"其他:"</span> << total-num-cha-space << <span class="built_in">endl</span>;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>1)iostream和iostream.h头文件的问题</p><p>2)C语言不存在“string”数据类型,字符串就是一串以NUL字节结尾的字符。(’\n’)</p><p>3) 代码里的数字不是真的数字,而是‘0’~‘9’的字符</p><p>4)测试代码时,发现虽然定义的数组长度只有20,但输入超过20个字符时测试结果无误,猜测是因为数组是传址的缘故(C语言是不会检查你下标是否越界的。数组在内存中是一段连续的空间,当你使用下标 0 访问,访问到的是第一个元素,使用 1 访问得到第二个,如此这般。你定义一个3长度的数组,却访问到第9个单位去,这个地方已经不属于你定义的范围了。如果你修改的这个范围外的元素有在其它地方被使用到,就可能发生错误。)</p></li><li><p>题目:两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。</p><p>代码(我):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">char</span> i, j, k;</span><br><span class="line"><span class="keyword">for</span>( i=<span class="string">'x'</span>; i<=<span class="string">'z'</span>; i++ ){</span><br><span class="line"><span class="keyword">for</span>( j=<span class="string">'x'</span>; j<=<span class="string">'z'</span>; j++ ){</span><br><span class="line"><span class="keyword">for</span>( k=<span class="string">'x'</span>; k<=<span class="string">'z'</span>; k++ ){</span><br><span class="line"><span class="keyword">if</span>( i!=j && i!=k && j!=k ){</span><br><span class="line"><span class="keyword">if</span>( i!=<span class="string">'x'</span> && k!=<span class="string">'x'</span> && k!=<span class="string">'z'</span> ){</span><br><span class="line"><span class="built_in">cout</span><<<span class="string">'a'</span><<<span class="string">':'</span><<i<<<span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">cout</span><<<span class="string">'b'</span><<<span class="string">':'</span><<j<<<span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">cout</span><<<span class="string">'c'</span><<<span class="string">':'</span><<k<<<span class="built_in">endl</span>;</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><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>代码(答案):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line">main()</span><br><span class="line">{</span><br><span class="line"><span class="keyword">char</span> i,j,k;<span class="comment">/*i是a的对手,j是b的对手,k是c的对手*/</span></span><br><span class="line"><span class="keyword">for</span>(i=<span class="string">'x'</span>;i<=<span class="string">'z'</span>;i++)</span><br><span class="line"> <span class="keyword">for</span>(j=<span class="string">'x'</span>;j<=<span class="string">'z'</span>;j++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(i!=j)</span><br><span class="line"> <span class="keyword">for</span>(k=<span class="string">'x'</span>;k<=<span class="string">'z'</span>;k++)</span><br><span class="line"> { <span class="keyword">if</span>(i!=k&&j!=k)</span><br><span class="line"> { <span class="keyword">if</span>(i!=<span class="string">'x'</span>&&k!=<span class="string">'x'</span>&&k!=<span class="string">'z'</span>)</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"order is a--%c\tb--%c\tc--%c\n"</span>,i,j,k);</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>比较了两种代码,我写的被完虐。答案的代码进行比较的次数比我的少了一个量级,看了一下两次运行的时间,一个0.38秒,一个0.01821秒,差距大的不正常。把IO流改成格式化输出,0.38秒变成0.01855秒,恢复正常。在数据量较大的时候,两种比较的差距才会出现。IO流比格式化输出耗时。</p></li><li><p>题目:利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来。</p><p>这道题刚开始不知道用递归怎么做,因为感觉传入数组递归的话不太好写终止语句。看了下答案才发现这道题的目的是为了让我们更好的理解递归的本质,递归的时候,每次调用函数,计算机都会为这个函数分配新的空间,这就是说,当被调函数返回的时候,调用函数中的变量依然会保持原先的值,且从后往前执行,也就是调用了栈。</p><p>最后,<a href="https://blog.csdn.net/longintchar/article/details/78998081" target="_blank" rel="noopener">栈,递归,尾递归</a>。</p></li><li><p>C语言并不执行数组下标的有效性检查。你觉得为什么这个明显的安全手段会从语言中省略?</p><p>在C语言中数组就是指针,它只保存地址。这就造成无法检查是否越界,但也给指针和数组的交互操作提供极大的便利。</p></li></ol><ol start="8"><li><p>为什么C语言中a[3]==3[a]?</p><p>C规定,[]这个符号的作用就是a[b]==*(a+b),ab部分前后。</p></li><li><p>题目:取一个整数a从右端开始的4~7位</p><p>答案的代码</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">main()</span><br><span class="line">{</span><br><span class="line">unsigned a,b,c,d;</span><br><span class="line">scanf(<span class="string">"%o"</span>,&a);</span><br><span class="line"><span class="attribute">b</span>=a>>4;</span><br><span class="line"><span class="attribute">c</span>=~(~0<<4);</span><br><span class="line"><span class="attribute">d</span>=b&c;</span><br><span class="line">printf(<span class="string">"%o\n%o\n"</span>,a,d);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但运行源码后发现不对,看了程序分析后,先使a右移4位,设置一个低4位全为1,其余全为0的数。可用~(~0<<4)。将上面二者进行&运算。思路是对的,但是这应该放在2进制的代码中,当输入的是代码中的8进制时,因为一个8进制位可以转化为3个2进制位,所以最后取得不是输入数字的4~7位而是转化成2进制数时的4~7位,在转化成8进制后就只剩下1~2位数了。</p></li></ol>]]></content>
<summary type="html">
<ol>
<li><p>用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)</p>
<figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#define SECONDS_PER_YEAR (<span class="number">60</span> * <span class="number">60</span> * <span class="number">24</span> * <span class="number">365</span>)UL</span><br></pre></td></tr></table></figure>
<p>UL(表示无符号长整型),如果不写UL后缀,系统默认为:int, 即,有符号整数。</p></li></ol>
</summary>
<category term="C语言" scheme="https://wangzipai.github.io/tags/C%E8%AF%AD%E8%A8%80/"/>
</entry>
<entry>
<title>PWM输出</title>
<link href="https://wangzipai.github.io/posts/7876/"/>
<id>https://wangzipai.github.io/posts/7876/</id>
<published>2019-04-15T12:45:42.000Z</published>
<updated>2019-04-17T13:43:19.264Z</updated>
<content type="html"><![CDATA[<p>PWM在电力电子技术中占据着重要的地位,被广泛的应用在逆变电路中。</p><a id="more"></a><p>STM32中PWM工作过程</p><p><img src="//wangzipai.github.io/posts/7876/PWM工作过程.png" alt></p><p>在调用PWM时,首先要使能TIM3外设的时钟,并对TIM3通道相应的GPIO引脚做相应的配置。配置好GPIO后,还要时基初始化、输出模式初始化和装载捕获/比较寄存器的数值。</p><p>时基配置:</p><p>TIM_Period:定时周期,实质是存储到重载寄存器TIMx_ARR大的数值。</p><p>TIM_Prescaler:对定时器时钟TIMxCLK的预分频值,分频后作为脉冲计数器的驱动时钟。</p><p>TIM_ClockDivision:时钟分频因子。怎么又出现一个配置时钟分频的呢?要注意这个TIM_ClockDivision与TIM_Prescaler不同。TIM_Prescaler预分频是对TIMxCLK进行分频,分频后的时钟被输出到脉冲计数器中,而TIM_ClockDivision虽然也是对TIMxCLK进行分频,但被输出到定时器的ETPR数字滤波器部分,会影响滤波器的采样频率。ETPR数字滤波器的作用是对外部时钟TIMxETR进行滤波。本实验中无意义。</p><p>TIM_CounterMode:本成员配置的为脉冲计数器的计数模式。(向上、向下、中央对齐)</p><p>输出模式:</p><p>通过定时器的输出模式由TIM_OCInitTypeDef类型结构体的成员配置。</p><p>TIM_OCMode:输出模式配置,主要使用的为PWM1和PWM2模式。</p><p>PWM1模式:向上计数时,当TIMx_CNT<TIMx_CCRn时,通道n输出为有效电平,否则为无效电平;向下计数时,当TIMx_CNT>TIMxCCRn时通道n为无效电平,否则为有效电平。PWM2模式与PWM1模式相反。其中有效电平和无效电平是不固定的,也就是需要(TIM_OCPolarite)配置的。</p><p>TIM_OCPolarite:有效电平极性。</p><p><img src="//wangzipai.github.io/posts/7876/PWM工作过程通道1.png" alt></p><p>CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。<br>CCMR1: OC1M[2:0]位:<br> 对于PWM方式下,用于设置PWM模式1【110】或者PWM模式2【111】<br>CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。<br>CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。</p><p>代码</p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">void TIM1_PWM_Init(u16 arr,u16 psc)</span><br><span class="line">{ </span><br><span class="line"> GPIO_InitTypeDef GPIO_InitStructure;</span><br><span class="line">TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;</span><br><span class="line">TIM_OCInitTypeDef TIM_OCInitStructure;</span><br><span class="line"></span><br><span class="line">RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);<span class="comment">// </span></span><br><span class="line"> RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); <span class="comment">//使能GPIO外设时钟使能</span></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> <span class="comment">//设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形</span></span><br><span class="line">GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; <span class="comment">//TIM_CH1</span></span><br><span class="line">GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; <span class="comment">//复用推挽输出</span></span><br><span class="line">GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;</span><br><span class="line">GPIO_Init(GPIOA, <span class="meta">&GPIO_InitStructure);</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">TIM_TimeBaseStructure.TIM_Period = arr; <span class="comment">//设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K</span></span><br><span class="line">TIM_TimeBaseStructure.TIM_Prescaler =psc; <span class="comment">//设置用来作为TIMx时钟频率除数的预分频值 不分频</span></span><br><span class="line">TIM_TimeBaseStructure.TIM_ClockDivision = <span class="number">0</span>; <span class="comment">//设置时钟分割:TDTS = Tck_tim</span></span><br><span class="line">TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; <span class="comment">//TIM向上计数模式</span></span><br><span class="line">TIM_TimeBaseInit(TIM1, <span class="meta">&TIM_TimeBaseStructure); <span class="comment">//根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位</span></span></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; <span class="comment">//选择定时器模式:TIM脉冲宽度调制模式2</span></span><br><span class="line">TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; <span class="comment">//比较输出使能</span></span><br><span class="line">TIM_OCInitStructure.TIM_Pulse = <span class="number">0</span>; <span class="comment">//设置待装入捕获比较寄存器的脉冲值</span></span><br><span class="line">TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; <span class="comment">//输出极性:TIM输出比较极性高</span></span><br><span class="line">TIM_OC1Init(TIM1, <span class="meta">&TIM_OCInitStructure); <span class="comment">//根据TIM_OCInitStruct中指定的参数初始化外设TIMx</span></span></span><br><span class="line"></span><br><span class="line"> TIM_CtrlPWMOutputs(TIM1,ENABLE);<span class="comment">//MOE 主输出使能</span></span><br><span class="line"></span><br><span class="line">TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); <span class="comment">//CH1预装载使能 </span></span><br><span class="line"></span><br><span class="line">TIM_ARRPreloadConfig(TIM1, ENABLE); <span class="comment">//使能TIMx在ARR上的预装载寄存器,允许或禁止在定时器工作时向ARR的缓冲器中写入新值,以便在更新事件发生时载入覆盖以前的值</span></span><br><span class="line"></span><br><span class="line">TIM_Cmd(TIM1, ENABLE); <span class="comment">//使能TIM1 </span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>预装载用于精确波形控制,PWM输出,一般用不到精确波形控制。故开不开都问题不大。<br>在定时器的输出比较模式下,TIMx_CCRx寄存器能够在任何时候通过软件进行更新以控制波形,这个通过软件写入控制波形的值是立即生效呢还是在定时器发生下一次更新事件时被更新的,是由TIM_OCxPreloadConfig(TIMx, TIM_OCPreload_Enable)这条语句决定的!Enable就是下一次更新事件时被更新;Disable是立即生效</p><p>如果是高级定时器,则还需要配置:刹车和死区寄存器(TIMx_BDTR),该寄存器,我们只需要关注最高位:MOE位,要想高级定时器的PWM正常输出,则必须设置MOE位为1,否则不会有输出。注意:通用定时器不需要配置这个。</p><p>主函数</p><figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"> int main(void)</span><br><span class="line"> {</span><br><span class="line">u16 led0pwmval=<span class="number">0</span>; </span><br><span class="line">u8 dir=<span class="number">1</span>;</span><br><span class="line">delay_init(); <span class="comment">//延时函数初始化 </span></span><br><span class="line">LED_Init(); <span class="comment">//初始化与LED连接的硬件接口</span></span><br><span class="line">TIM1_PWM_Init(<span class="number">899</span>,<span class="number">0</span>);<span class="comment">//不分频。PWM频率=72000/(899+1)=80Khz </span></span><br><span class="line"> while(<span class="number">1</span>)</span><br><span class="line">{</span><br><span class="line"> delay_ms(<span class="number">10</span>); </span><br><span class="line">if(dir)led0pwmval++;</span><br><span class="line">else led0pwmval--; </span><br><span class="line"> if(led0pwmval><span class="number">300</span>)dir=<span class="number">0</span>;</span><br><span class="line">if(led0pwmval==<span class="number">0</span>)dir=<span class="number">1</span>; </span><br><span class="line">TIM_SetCompare1(TIM1,led0pwmval); </span><br><span class="line">} </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>虽然TIM1计数器中的计数值要与76比较,但是这一个周期的时间十分短暂,导致低电平时间过短,达到人眼无法分辨的频率,所以我们觉得LED一直是亮着的,这个时候决定LED变化的只有主函数中最后一段代码中的函数TIM_SetCompare1(TIM1,led0pwmval)中的led0pwmval这个参数决定的占空比,从而影响电压进而影响电流大小,最后导致了LED的亮度的改变。</p><h3 id="关于STM32影子寄存器和预装载寄存器和TIM-ARRPreloadConfig"><a href="#关于STM32影子寄存器和预装载寄存器和TIM-ARRPreloadConfig" class="headerlink" title="关于STM32影子寄存器和预装载寄存器和TIM_ARRPreloadConfig"></a>关于STM32影子寄存器和预装载寄存器和TIM_ARRPreloadConfig</h3><p>1、有影子寄存器的有3个:分频寄存器PSC,自动重装载ARR,自动捕获CCRx,注意,PSC,ARR,CCRx不是影子寄存器,而是它们对应的“预装载寄存器”;</p><p>2、影子寄存器才是真正起作用的寄存器,但是ST没有提供这个寄存器出来,只是提供出与之相对应的预装载寄存器,分别为“PSC,ARR,CCRx”</p><p>3、我们用户能接触到,能修改或读取的都是预装载寄存器,ST只是把它们开放出来(影子寄存器并没有开放给用户),其实就是ARR寄存器,如:TIM1->ARR</p><p>4、从预装载寄存器ARR传送到影子寄存器,有两种方式,一种是立刻更新,一种是等触发事件之后更新;这两种方式主要取决于寄存器TIMx->CR1中的“APRE”位;</p><pre><code>4.1 , APRE=0,当ARR值被修改时,同时马上更新影子寄存器的值;4.2 , APRE=1,当ARR值被修改时,必须在下一次事件UEV发生后才能更新影子寄存器的值;</code></pre><p>5、怎么样马上立刻更改影子寄存器的值,而不是下一个事件;方法如下:</p><pre><code>5.1 、将ARPE=0,TIM_ARRPreloadConfig(ch1_Master_Tim, DISABLE );5.2 在ARPE=1,TIM_ARRPreloadConfig(ch1_Master_Tim, ENABLE); 我们更改完预装载寄存器后,立刻设置UEV事件,即更改EGR寄存的UG位,如下: TIM1->ARR = period-1; //设置周期 TIM1->CCR1 = period>>1; //设置占空比 50% TIM_GenerateEventTIM1,TIM_EventSource_Update); //主动发生UEV事件,UG=1</code></pre>]]></content>
<summary type="html">
<p>PWM在电力电子技术中占据着重要的地位,被广泛的应用在逆变电路中。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
<entry>
<title>死区时间</title>
<link href="https://wangzipai.github.io/posts/57562/"/>
<id>https://wangzipai.github.io/posts/57562/</id>
<published>2019-04-14T12:42:24.000Z</published>
<updated>2019-04-14T13:25:36.503Z</updated>
<content type="html"><![CDATA[<p>在H桥、三相桥的PWM驱动电路中,上下两个桥臂的PWM驱动信号是互补的,即上下桥臂轮流导通,但实际上<strong><em>为了防止出现上下两个臂同时导通(会短路),在上下两臂切换时留一段时间,上下臂都施加关闭信号,这个上下臂都断的时间就是死区时间。</em></strong>高级定时器可以配置出输出互补的PWM信号,并且在这个PWM信号中加入死区时间,为电机的控制提供了极大的便利。</p><a id="more"></a><p>关于死区时间,看到有这两种理解,一种是电机电感需要释放能量,一种是功率器件以及电路的延迟需要等待。个人理解他们都是死区时间的考虑因素。</p><ol><li><p>电机的各相输入的控制信号之间设定的互锁时间,留一段时间让储能电感放电,以免造成短路。</p></li><li><p>PWM输出时的Dead Zone(死区)作用是在电平翻转时插入一个时间间隔,更具体的理解是,通常大功率电机、变频器等,末端都是由大功率管、IGBT等元件组成的H桥或3相桥。每个桥的上半桥和下半桥是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制端时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。</p></li></ol><p><img src="//wangzipai.github.io/posts/57562/死区时间.jpg" alt></p><p><strong><em>在保证不出现短路的情况下,死区时间越短越好。</em></strong></p>]]></content>
<summary type="html">
<p>在H桥、三相桥的PWM驱动电路中,上下两个桥臂的PWM驱动信号是互补的,即上下桥臂轮流导通,但实际上<strong><em>为了防止出现上下两个臂同时导通(会短路),在上下两臂切换时留一段时间,上下臂都施加关闭信号,这个上下臂都断的时间就是死区时间。</em></strong>高级定时器可以配置出输出互补的PWM信号,并且在这个PWM信号中加入死区时间,为电机的控制提供了极大的便利。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
<entry>
<title>stm32定时器</title>
<link href="https://wangzipai.github.io/posts/50823/"/>
<id>https://wangzipai.github.io/posts/50823/</id>
<published>2019-04-13T13:48:52.000Z</published>
<updated>2019-04-14T14:04:40.895Z</updated>
<content type="html"><![CDATA[<p>stm32一共有8个16位的定时器,其中TIM6、TIM7是基本定时器,TIM2~5是通用定时器,TIM1和TIM8是高级定时器。这些定时器使STM32具有定时、信号频率测量、信号的PWM测量、PWM输出、三相6步电机控制及编码接口等功能,都是专门为工业控制领域量身定做的。</p><a id="more"></a><p><img src="//wangzipai.github.io/posts/50823/三种STM32定时器区别.png" alt></p><h3 id="基本定时器"><a href="#基本定时器" class="headerlink" title="基本定时器"></a>基本定时器</h3><p>基本定时器只具备最基本的定时功能,就是累加的时钟脉冲超过预定时,能触发中断或DMA请求。由于在芯片内部与DAC外设相连,可以通过接触输出驱动DAC,也可以作为其他通用定时器的时钟基准。</p><p>工作时,脉冲计数器TIMx_CNT由时钟触发进行计数,当TIMx_CNT的计数值等于重载寄存器TIMx_ARR中保存的数值N时,产生溢出事件,可触发中断或DMA请求。然后TIMx_CNT的值重新被置为0,重新向上计数。</p><h3 id="通用定时器(Tout(溢出时间)-(ARR-1-PSC-1-Tclk)"><a href="#通用定时器(Tout(溢出时间)-(ARR-1-PSC-1-Tclk)" class="headerlink" title="通用定时器(Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk)"></a>通用定时器(Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk)</h3><p>除了基本的定时时,它主要用于测量脉冲的频率、脉冲宽与PWM脉冲的场合,还具有编码器的接口。</p><p>4 个独立通道(TIMx_CH1~4),这些通道可以用来作为: </p><p>① 输入捕获 </p><p>② 输出比较</p><p>③ PWM 生成(边缘或中间对齐模式) </p><p>④ 单脉冲模式输出 </p><h4 id="捕获-比较寄存器"><a href="#捕获-比较寄存器" class="headerlink" title="捕获/比较寄存器"></a>捕获/比较寄存器</h4><p>通用定时器的基本计时功能与基本定时器的工作方式一样,同样把时钟源经过预分频器输出到脉冲计数器TIMx_CNT累加,溢出时就产生中断或DMA请求。而通用定时器比基本定时器多出的强大功能,就是因为通用定时器多出了一种捕获/比较寄存器TIMx——CCR,它在输入时被用于捕获输入脉冲在电平翻转时脉冲计数器TIMx_CNT的当前计数值,从而实现脉冲的频率测量。在输出时被用来存储一个脉冲数值,把这个数值用于与脉冲计数器TIMx_CNT的当前计数值进行比较,根据比较结果进行不同的电平输出。</p><h4 id="PWM输出过程分析"><a href="#PWM输出过程分析" class="headerlink" title="PWM输出过程分析"></a>PWM输出过程分析</h4><p>通用定时器可以利用GPIO引脚进行脉冲输出,在配置为比较输出、PWM输出功能时,捕获/比较寄存器TIMx_CCR被用作比较功能。</p><p>若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR被配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累积,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0并重新计数。而<strong><em>在TIMx_CNT计数的同时,TIMx_CNT的计数值X会与比较寄存器TIMx_CCR预先存储的数值A进行比较。当脉冲计数器的数值X小于计较寄存器的值A时,输出高电平(或低电平)。相反的,X大于A时,输出低电平(或高电平)。</em></strong>如此循环,得到的输出脉冲周期就为重载寄存器存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为A/(N+1)。</p><h4 id="PWM输入过程分析"><a href="#PWM输入过程分析" class="headerlink" title="PWM输入过程分析"></a>PWM输入过程分析</h4><p>当定时器被配置为输入功能时,可以用于检测输入到GPIO引脚的信号,此时捕获/比较寄存器TIMx_CCR被用作捕获功能。</p><p><img src="//wangzipai.github.io/posts/50823/PWM输入过程分析.jpg" alt></p><p>要测量的PWM脉冲通过GPIO引脚输入到定时器的脉冲检测通道,其时序为图中TI1。把脉冲计数器配置为向上计数,重载寄存器的N值配置为足够大。在输入脉冲TI1的上升沿到达时,触发IC1和IC2的输入捕获中断,这时把脉冲计数器TIMx_CNT的计数值复位为0,于是TIMx_CNT的计数值X在TIMx_CLK的驱动下从0开始累加直到TI1出现下降沿,触发IC2捕获事件,此时捕获寄存器把脉冲计数器的当前值2存储起来(TIMx_CCR2),而脉冲计数器继续累加,直到TI1出现第二个上升沿,触发IC1的捕获事件,此时TIMx_CNT的当前计数值4被保存到TIMx_CCR1。</p><p>TIMx_CCR1(加1)的值乘以TIMxCLK的周期即为PWM输入脉冲周期,TIMx_CCR2(加1)的值乘以TIMxCLK的周期即为PWM的高电平时间,进而可以计算出PWM脉冲的频率、占空比。</p><h4 id="定时器的时钟源"><a href="#定时器的时钟源" class="headerlink" title="定时器的时钟源"></a>定时器的时钟源</h4><p>从时钟源方面来说,通用定时器比基本定时器多了一个选择,它可以使用外部脉冲作为定时器的时钟源。</p><p>计数器时钟可以由下列时钟源提供:<br>内部时钟(CK_INT)<br>外部时钟模式1:外部输入脚(TIx)<br>外部时钟模式2:外部触发输入(ETR)<br>内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。</p><p>使用外部时钟源时,要使用寄存器进行触发边沿、滤波器带宽的配置。如果选择内部时钟源的话则与基本定时器一样,也为TIMxCLK。但要注意的是,所有定时器使用内部时钟时,定时器的时钟源都被称为TIMxCLK,但TIMxCLK的时钟源并不是完全一样的。</p><p>TIM2~7也就是基本定时器和通用定时器,TIMxCLK的时钟源是APB1预分频器的输出。<strong><em>当APB1的分频系数为1时,则TIM2~7的TIMxCLK直接等于该APB1预分频器的输出,而APB1的分频系数不为1时,TIM2~7的TIMxCLK则为APB1的2倍。</em></strong></p><p>在常见配置中,AHB=72MHz,而APB1预分频器的分频系数被配置为2,则PCLK1刚好达到最大值32MHz,而此时APB1的分频系数不为1,则TIM2~7的时钟TIMxCLK=(AHB/2)*2=72MHz。</p><p>而对于TIM1和TIM8这两个高级定时器,TIMxCLK的时钟来源则是APB2预分频器的输出,同样它也根据分频系数分成2中情况。</p><p>常见的配置中AHB=72MHz,APB2预分频器的分频系数被配置为1,此时PCLK2刚好达到最大值72MHz,而TIMxCLK则直接等于APB2分频器的输出,即TIM1和TIM8的时钟TIMxCLK=AHB=72MHz。</p><p><strong><em>虽然这种配置下最终TIMxCLK的时钟频率相等,但必须清楚他们的时钟来源时有区别的。</em></strong>还要强调的是:TIMxCLK是定时器内部的时钟源,但<strong><em>在时钟输出到脉冲计时器前,还经过了一个预分频器PSC,最终用于驱动脉冲计数器的时钟根据预分频器PSC的配置而定。</em></strong></p><h4 id="定时器中断实现步骤"><a href="#定时器中断实现步骤" class="headerlink" title="定时器中断实现步骤"></a>定时器中断实现步骤</h4><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">使能定时器时钟。</span><br><span class="line"> <span class="selector-tag">RCC_APB1PeriphClockCmd</span>();</span><br><span class="line">初始化定时器,配置<span class="selector-tag">ARR</span>,<span class="selector-tag">PSC</span>。</span><br><span class="line"> <span class="selector-tag">TIM_TimeBaseInit</span>();</span><br><span class="line">开启定时器中断,配置<span class="selector-tag">NVIC</span>。</span><br><span class="line"> <span class="selector-tag">void</span> <span class="selector-tag">TIM_ITConfig</span>();</span><br><span class="line"> <span class="selector-tag">NVIC_Init</span>();</span><br><span class="line">使能定时器。</span><br><span class="line"> <span class="selector-tag">TIM_Cmd</span>();</span><br><span class="line">编写中断服务函数。</span><br><span class="line"> <span class="selector-tag">TIMx_IRQHandler</span>();</span><br></pre></td></tr></table></figure><p><img src="//wangzipai.github.io/posts/50823/通用定时器工作过程.png" alt></p><h3 id="高级定时器"><a href="#高级定时器" class="headerlink" title="高级定时器"></a>高级定时器</h3><p>TIM1和TIM8是两个高级定时器,他们具有基本、通用定时器的所有功能,还具有三相6步电机的接口、刹车功能及用于PWM驱动电路的死区时间控制等,使他非常适用于电机控制。相比于通用定时器,高级定时器多出了BRK、DTG两个结构,因而具有<a href="https://wangzipai.github.io/posts/57562/"><strong>死区时间</strong></a>的控制功能。</p>]]></content>
<summary type="html">
<p>stm32一共有8个16位的定时器,其中TIM6、TIM7是基本定时器,TIM2~5是通用定时器,TIM1和TIM8是高级定时器。这些定时器使STM32具有定时、信号频率测量、信号的PWM测量、PWM输出、三相6步电机控制及编码接口等功能,都是专门为工业控制领域量身定做的。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
<entry>
<title>190413</title>
<link href="https://wangzipai.github.io/posts/28547/"/>
<id>https://wangzipai.github.io/posts/28547/</id>
<published>2019-04-13T07:49:12.000Z</published>
<updated>2019-04-13T10:33:57.208Z</updated>
<content type="html"><![CDATA[<p>不管怎么说现在学习的热情已经越来越低了,看了下时间,大概坚持了一个月的时间,自觉性就不再了。我觉得规律是一个很重要的原因。之前完全靠自觉,没有定制完备的作息。而这两天中午莫名的失眠,直接导致了晚上学习时精力的不足,加上看的书籍过于晦涩,于是很难集中注意力在书上。可我又是一个不喜欢让太过死板的制度限制住的人,就简单制定一下每天的目标,而不是具体的每个小时要做的事吧。</p><p>首先是周一到周五,由于白天有课,学习的时间只有到晚饭后了。每天看一讲视频和对应的章节的书,然后写一下相应的代码与总结。由于stm32的视频快看完了,以及我的几本C的书籍难度超微有点大,后续的视频改成C的。先看C的视频再去啃书。最后每天研究一题嵌入式的面试题吧。</p><p>然后是周末的时间,因为死宅睡醒就是中午了,吃完午饭后可以看会书,看到想睡就睡一觉。醒来后研究一下平时的课程到吃晚饭,然后重复工作日的作息。</p>]]></content>
<summary type="html">
<p>不管怎么说现在学习的热情已经越来越低了,看了下时间,大概坚持了一个月的时间,自觉性就不再了。我觉得规律是一个很重要的原因。之前完全靠自觉,没有定制完备的作息。而这两天中午莫名的失眠,直接导致了晚上学习时精力的不足,加上看的书籍过于晦涩,于是很难集中注意力在书上。可我又是一个
</summary>
<category term="hide" scheme="https://wangzipai.github.io/tags/hide/"/>
</entry>
<entry>
<title>看门狗</title>
<link href="https://wangzipai.github.io/posts/59401/"/>
<id>https://wangzipai.github.io/posts/59401/</id>
<published>2019-04-11T15:52:24.000Z</published>
<updated>2019-04-13T08:48:12.478Z</updated>
<content type="html"><![CDATA[<h3 id="为什么要看门狗"><a href="#为什么要看门狗" class="headerlink" title="为什么要看门狗"></a>为什么要看门狗</h3><p>在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。</p><a id="more"></a><h3 id="看门狗解决的问题是什么"><a href="#看门狗解决的问题是什么" class="headerlink" title="看门狗解决的问题是什么"></a>看门狗解决的问题是什么</h3><p>在启动正常运行的时候,系统不能复位。在系统跑飞(程序异常执行)的情况,系统复位,程序重新执行。</p><h3 id="独立看门狗"><a href="#独立看门狗" class="headerlink" title="独立看门狗"></a>独立看门狗</h3><p>独立看门狗(IWDG)由专用的低速时钟(LSI)驱动,<strong><em>即使主时钟发生故障它仍有效。</em></strong>独立看门狗适合应用于需要看门狗作<strong><em>为一个在主程序之外能够完全独立工作</em></strong>,并且对时间精度要求低的场合。</p><h4 id="独立看门狗功能描述"><a href="#独立看门狗功能描述" class="headerlink" title="独立看门狗功能描述"></a>独立看门狗功能描述</h4><p>在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。<br>无论何时,只要在键值寄存器IWDG_KR中写入0xAAAA(通常说的喂狗), 自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。<br>如果程序异常,就无法正常喂狗,从而系统复位。</p><p><strong><em>溢出时间计算:Tout=((4×2^prer) ×rlr) /40 (M3)</em></strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">IWDG_WriteAccessCmd</span><span class="params">(<span class="keyword">uint16_t</span> IWDG_WriteAccess)</span></span>;<span class="comment">//取消写保护:0x5555使能</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">IWDG_SetPrescaler</span><span class="params">(<span class="keyword">uint8_t</span> IWDG_Prescaler)</span></span>;<span class="comment">//设置预分频系数:写PR</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">IWDG_SetReload</span><span class="params">(<span class="keyword">uint16_t</span> Reload)</span></span>;<span class="comment">//设置重装载值:写RLR</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">IWDG_ReloadCounter</span><span class="params">(<span class="keyword">void</span>)</span></span>;<span class="comment">//喂狗:写0xAAAA到KR</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">IWDG_Enable</span><span class="params">(<span class="keyword">void</span>)</span></span>;<span class="comment">//使能看门狗:写0xCCCC到KR</span></span><br><span class="line"><span class="function">FlagStatus <span class="title">IWDG_GetFlagStatus</span><span class="params">(<span class="keyword">uint16_t</span> IWDG_FLAG)</span></span>;<span class="comment">//状态:重装载/预分频 更新</span></span><br></pre></td></tr></table></figure><h3 id="窗口看门狗"><a href="#窗口看门狗" class="headerlink" title="窗口看门狗"></a>窗口看门狗</h3><p>窗口看门狗由从APB1时钟分频后得到时钟驱动。通过可配置的时间窗口来检测应用程序非正常的过迟或过早操作。窗口看门狗最适合那些要求看门狗在精确计时窗口起作用的程序。</p><p>之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),你可以通过设定相关寄存器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。</p><p>而独立看门狗限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。</p><p><img src="//wangzipai.github.io/posts/59401/图片1.png" alt></p><p>STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:<br>1 当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。当计数器的数值从0x40减到0x3F时【T6位跳变到0】 。<br>2 如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位。</p><p><strong><em>超时时间:Twwdg = ( 4096 </em> 2 ^ WDGTB <em> ( T [5:0] + 1)) / Fpclk1</em></strong></p><p>WDGTB:WWDG的预分频系数</p><p>Fpclk1:APB1的时钟频率</p><p>T [5:0] : 窗口看门狗计数器底6位</p><h4 id="为什么要窗口看门狗?"><a href="#为什么要窗口看门狗?" class="headerlink" title="为什么要窗口看门狗?"></a>为什么要窗口看门狗?</h4><p>对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但这有一个隐患,有可能程序跑乱了又跑回到正常的地方,或跑乱的程序正好执行了刷新看门狗操作,这样的情况下一般的看门狗就检测不出来了;<br>如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序没有按照正常的路径运行非正常地跳过了某些程序段的情况。</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//使能看门狗时钟:</span></span><br><span class="line"> <span class="selector-tag">RCC_APB1PeriphClockCmd</span>();</span><br><span class="line"><span class="comment">//设置分频系数:</span></span><br><span class="line"> <span class="selector-tag">WWDG_SetPrescaler</span>();</span><br><span class="line"><span class="comment">//设置上窗口值:</span></span><br><span class="line"> <span class="selector-tag">WWDG_SetWindowValue</span>();</span><br><span class="line"><span class="comment">//开启提前唤醒中断并分组(可选):</span></span><br><span class="line"> <span class="selector-tag">WWDG_EnableIT</span>(); </span><br><span class="line"> <span class="selector-tag">NVIC_Init</span>();</span><br><span class="line"><span class="comment">//使能看门狗:</span></span><br><span class="line"> <span class="selector-tag">WWDG_Enable</span>();</span><br><span class="line"><span class="comment">//喂狗:</span></span><br><span class="line"> <span class="selector-tag">WWDG_SetCounter</span>();</span><br><span class="line"><span class="comment">//编写中断服务函数</span></span><br><span class="line"> <span class="selector-tag">WWDG_IRQHandler</span>();</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h3 id="为什么要看门狗"><a href="#为什么要看门狗" class="headerlink" title="为什么要看门狗"></a>为什么要看门狗</h3><p>在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
<category term="嵌入式" scheme="https://wangzipai.github.io/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"/>
</entry>
<entry>
<title>stm32的printf函数</title>
<link href="https://wangzipai.github.io/posts/11595/"/>
<id>https://wangzipai.github.io/posts/11595/</id>
<published>2019-04-09T13:58:54.000Z</published>
<updated>2019-04-09T14:58:32.783Z</updated>
<content type="html"><![CDATA[<p>在之前的USART实验中调用了printf函数来打印发送的信息。printf()函数用法和在纯C语言编程中看起来好像没有区别,但实际上在调用printf()函数的时候,我们需要把printf()重新定向到串口中。重定向是指用户可以自己重新写C的库函数,当连接器检查到用户编写了与C库函数相同的名字的函数时,优先采用用户编写的函数,这样用户就可以实现对库的修改了。</p><a id="more"></a><p>为了实现重定向printf()函数,我们需要重写fputc()这个C标准库函数,因为<strong><em>printf()在C标准函数中实际是一个宏,最终是调用了fputc()这个函数。</em></strong></p><figure class="highlight zephir"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> fputc(<span class="keyword">int</span> ch, FILE *f)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">while</span>((USART1->SR&<span class="number">0X40</span>)==<span class="number">0</span>);<span class="comment">//循环发送,直到发送完毕</span></span><br><span class="line">USART1->DR = (u8) ch;</span><br><span class="line"><span class="keyword">return</span> ch;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>重定向时,我们把fputc()的形参ch,作为串口将要发送的数据,也就是说,当使用printf()时,他先调用这个fputc()函数,然后将ch的值赋值给USART1的数据寄存器,触发串口向PC发送一个相应的数据。(当我们需要发送数据时,内核或DMA外设把数据从内存写入发送数据寄存器TDR后,发送控制寄存器将适时的自动把数据从TDR加载到发送移位寄存器,然后通过串口线Tx,把数据一位一位的发送出去,当数据从TDR转移到移位寄存器时,会产生发送寄存器TDR已空事件TXE,当数据从移位寄存器全部发送出去时,会产生数据发送完成事件TC,这些事件可以在状态寄存器中查询到。)</p><p><strong><em>若使用C标准输出库函数,需要在main.c文件中把stdio.h这个头文件包含进来,还要在编译器中设置一个选项UseMicroLIB (使用微库)</em></strong>,这个微库是Keil MDK为嵌入式应用量身定做的C库,我们要先具有库才能重定向,勾选使用后,就可以使用printf()这个函数了。</p><p>除了重定向的方法,我们还可以自己编写格式输入输出函数,功能和重定向之后的printf()函数类似。</p>]]></content>
<summary type="html">
<p>在之前的USART实验中调用了printf函数来打印发送的信息。printf()函数用法和在纯C语言编程中看起来好像没有区别,但实际上在调用printf()函数的时候,我们需要把printf()重新定向到串口中。重定向是指用户可以自己重新写C的库函数,当连接器检查到用户编写了与C库函数相同的名字的函数时,优先采用用户编写的函数,这样用户就可以实现对库的修改了。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
<entry>
<title>c与指针读书笔记(1)</title>
<link href="https://wangzipai.github.io/posts/54171/"/>
<id>https://wangzipai.github.io/posts/54171/</id>
<published>2019-04-08T15:52:33.000Z</published>
<updated>2019-05-11T06:57:36.182Z</updated>
<content type="html"><![CDATA[<h3 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h3><p>在C语言中有两种注释方式:</p><p>一种是以<code>/*</code>开始、以<code>*/</code>结束的块注释(block comment),</p><p>另一种是以<code>//</code>开始、以换行符结束的单行注释(line comment)。</p><a id="more"></a><p>预处理器仅通过检查注释的结束符来检测注释中的字符,因此,不能嵌套块注释。在有些语言中,注释有时用于把一段代码“注释掉”,也就是使这段代码在程序中不起作用,<strong><em>但并不是将其真正从源文件中删除</em></strong>。在C语言中,如果用<code>/*</code>和<code>*/</code>来注释掉原先就有注释的代码,会出现问题。要从逻辑上删除一段C代码,更好的办法是使用#if指令。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">if</span> 0</span></span><br><span class="line">statements</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>在#if和#endif之间的程序段就可以有效的从程序中去除了,这是一种更为安全的方法。</p><h3 id="预处理"><a href="#预处理" class="headerlink" title="预处理"></a>预处理</h3><figure class="highlight dts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span> </span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_COLS 20 <span class="comment">//所能处理的最大列号 </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_INPUT 1000<span class="comment">//每个输入行的最大长度</span></span></span><br></pre></td></tr></table></figure><p>这5行就是预处理指令,因为他们是由预处理器解释的。<strong><em>预处理器读入源码,根据预处理指令对其修改,然后把修改过的源码交给编译器。</em></strong></p>]]></content>
<summary type="html">
<h3 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h3><p>在C语言中有两种注释方式:</p>
<p>一种是以<code>/*</code>开始、以<code>*/</code>结束的块注释(block comment),</p>
<p>另一种是以<code>//</code>开始、以换行符结束的单行注释(line comment)。</p>
</summary>
<category term="c语言" scheme="https://wangzipai.github.io/tags/c%E8%AF%AD%E8%A8%80/"/>
<category term="c与指针" scheme="https://wangzipai.github.io/tags/c%E4%B8%8E%E6%8C%87%E9%92%88/"/>
</entry>
<entry>
<title>串行通信原理梳理</title>
<link href="https://wangzipai.github.io/posts/24857/"/>
<id>https://wangzipai.github.io/posts/24857/</id>
<published>2019-04-02T13:26:15.000Z</published>
<updated>2019-04-09T14:46:50.735Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>虽然之前在一些课程中已经学习过关于串行通信原理的知识,但一直没有总结,很多知识也都忘记了。今天就重新梳理一下。</p><a id="more"></a><p>妈蛋,发送端的跳线帽坏了,害我白浪费好长的时间排错。</p><p>清明放假(荒废)3天。。</p><h2 id="背景知识"><a href="#背景知识" class="headerlink" title="背景知识"></a>背景知识</h2><h3 id="处理器与外部设备通信的两种方式"><a href="#处理器与外部设备通信的两种方式" class="headerlink" title="处理器与外部设备通信的两种方式"></a>处理器与外部设备通信的两种方式</h3><h4 id="并行通信"><a href="#并行通信" class="headerlink" title="并行通信"></a>并行通信</h4><p>传输原理:数据各个位同时传输。<br>优点:速度快<br>缺点:占用引脚资源多</p><h4 id="串行通信"><a href="#串行通信" class="headerlink" title="串行通信"></a>串行通信</h4><p>传输原理:数据按位顺序传输。<br>优点:占用引脚资源少<br>缺点:速度相对较慢</p><h5 id="按照数据传送方向,分为"><a href="#按照数据传送方向,分为" class="headerlink" title="按照数据传送方向,分为"></a>按照数据传送方向,分为</h5><p>单工:数据传输只支持数据在一个方向上传输<br>半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;<br>全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。 </p><h5 id="串行通信的通信方式"><a href="#串行通信的通信方式" class="headerlink" title="串行通信的通信方式"></a>串行通信的通信方式</h5><p>同步通信:带时钟同步信号传输。<br> -SPI(全双工),IIC(半双工)通信接口<br>异步通信:不带时钟同步信号。<br> -UART(通用异步收发器),单总线(USART:通用同步异步收发器,USART可用作UART使用)</p><h5 id="UART异步通信方式特点"><a href="#UART异步通信方式特点" class="headerlink" title="UART异步通信方式特点"></a>UART异步通信方式特点</h5><p>全双工异步通信。<br>分数波特率发生器系统,提供精确的波特率。-发送和接受共用的可编程波特率,最高可达4.5Mbits/s<br>可编程的数据字长度(8位或者9位);<br>可配置的停止位(支持1或者2位停止位);<br>可配置的使用DMA多缓冲器通信。<br>单独的发送器和接收器使能位。<br>检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志<br>多个带标志的中断源。触发中断。<br>其他:校验控制,四个错误检测标志。</p><h5 id="STM32串口异步通信需要定义的参数:"><a href="#STM32串口异步通信需要定义的参数:" class="headerlink" title="STM32串口异步通信需要定义的参数:"></a>STM32串口异步通信需要定义的参数:</h5><p> 起始位<br> 数据位(8位或者9位)<br> 奇偶校验位(第9位)<br> 停止位(1,15,2位)<br> 波特率设置</p><p>通过对时钟的控制可以改变波特率。在配置波特率时,向USART_BRR波特率寄存器写入参数,修改串口时钟的分频值USARTDIV。USART_BRR包括2部分(整数部分和小数部分)。</p><p><strong><em>对于USART1,由于挂载在APB2总线上,所以他的时钟源为fPCLK2;而USART2,USART3挂载在APB1上,时钟源为fPCLK1。</em></strong></p><h3 id="直通线与交叉线"><a href="#直通线与交叉线" class="headerlink" title="直通线与交叉线"></a>直通线与交叉线</h3><p>串口线主要分为2种:直通线与交叉线。假如PC与板子之间要实现全双工串口通信,必然是PC的Tx针脚要连接到板子的Rx脚,PC的Rx针脚要连接到板子的Tx脚。由于板子和电脑的串口接法是相同的,就要使用交叉线来连接了。设计板子时尽量采用与PC相同的标准串口接法。</p><h2 id="常用的串口相关寄存器"><a href="#常用的串口相关寄存器" class="headerlink" title="常用的串口相关寄存器"></a>常用的串口相关寄存器</h2><p>USART_SR状态寄存器<br>USART_DR数据寄存器<br>USART_BRR波特率寄存器 </p><p>当我们需要发送数据时,内核或DMA外设把数据从内存写入发送数据寄存器TDR后,发送控制寄存器将适时的自动把数据从TDR加载到发送移位寄存器,然后通过串口线Tx,把数据一位一位的发送出去,当数据从TDR转移到移位寄存器时,会产生发送寄存器TDR已空事件TXE,当数据从移位寄存器全部发送出去时,会产生数据发送完成事件TC,这些事件可以在状态寄存器中查询到。</p><p><img src="//wangzipai.github.io/posts/24857/图片1.png" alt></p><h2 id="串口通信"><a href="#串口通信" class="headerlink" title="串口通信"></a>串口通信</h2><h3 id="串口操作相关库函数"><a href="#串口操作相关库函数" class="headerlink" title="串口操作相关库函数"></a>串口操作相关库函数</h3><p>void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能<br>void USART_Cmd();//使能串口<br>void USART_ITConfig();//使能相关中断</p><p>void USART_SendData();//发送数据到串口,DR<br>uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据</p><p>FlagStatus USART_GetFlagStatus();//获取状态标志位<br>void USART_ClearFlag();//清除状态标志位<br>ITStatus USART_GetITStatus();//获取中断状态标志位<br>void USART_ClearITPendingBit();//清除中断状态标志位</p><h3 id="串口配置一般步骤"><a href="#串口配置一般步骤" class="headerlink" title="串口配置一般步骤"></a>串口配置一般步骤</h3><h4 id="硬件连接"><a href="#硬件连接" class="headerlink" title="硬件连接"></a>硬件连接</h4><p>PA9,PA10(串口1)连接到了USB串口电路。</p><h4 id="串口配置的一般步骤"><a href="#串口配置的一般步骤" class="headerlink" title="串口配置的一般步骤"></a>串口配置的一般步骤</h4><p>1串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();<br>2串口复位:USART_DeInit(); 这一步不是必须的<br>3GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP<br>4串口参数初始化:USART_Init();</p><p> <strong><em>硬件流:功能表现为:当外设硬件处于准备好的状态时,硬件启动自动控制,不需要软件再进行干预。</em></strong></p><p>5开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)<br> NVIC_Init();<br> USART_ITConfig();<br>6使能串口:USART_Cmd(); (<strong><em>使用外设时,不仅要使用其时钟,还要调用此函数使能外设</em></strong>)<br>7编写中断处理函数:USARTx_IRQHandler();<br>8串口数据收发:<br>void USART_SendData();//发送数据到串口,DR<br>uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据<br>9串口传输状态获取:<br>FlagStatus USART_GetFlagStatus(USART_TypeDef<em> USARTx, uint16_t USART_FLAG);<br>void USART_ClearITPendingBit(USART_TypeDef</em> USARTx, uint16_t USART_IT);</p><hr><h2 id="USART代码分析"><a href="#USART代码分析" class="headerlink" title="USART代码分析"></a>USART代码分析</h2><h3 id="头文件"><a href="#头文件" class="headerlink" title="头文件"></a>头文件</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> USART_REC_LEN 200 <span class="comment">//定义最大接收字节数 200</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> EN_USART1_RX 1<span class="comment">//使能(1)/禁止(0)串口1接收</span></span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">extern</span> u8 USART_RX_BUF[USART_REC_LEN]; <span class="comment">//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 </span></span><br><span class="line"><span class="keyword">extern</span> u16 USART_RX_STA; <span class="comment">//接收状态标记</span></span><br><span class="line"><span class="comment">//如果想串口中断接收,请不要注释以下宏定义</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">uart_init</span><span class="params">(u32 bound)</span></span>;</span><br></pre></td></tr></table></figure><h3 id="串口1中断服务程序"><a href="#串口1中断服务程序" class="headerlink" title="串口1中断服务程序"></a>串口1中断服务程序</h3><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">void <span class="built_in">USART1_IRQHandler</span>(void) <span class="comment">//串口1中断服务程序</span></span><br><span class="line">{</span><br><span class="line">u8 Res;</span><br><span class="line"><span class="meta">#if SYSTEM_SUPPORT_OS <span class="comment">//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.</span></span></span><br><span class="line"><span class="built_in">OSIntEnter</span>();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">USART_GetITStatus</span>(USART1, USART_IT_RXNE) != RESET) <span class="comment">//接收中断(接收到的数据必须是0x0d 0x0a结尾)</span></span><br><span class="line">{</span><br><span class="line">Res =<span class="built_in">USART_ReceiveData</span>(USART1);<span class="comment">//(USART1->DR); //读取接收到的数据</span></span><br><span class="line"><span class="keyword">if</span>((USART_RX_STA&<span class="number">0x8000</span>)==<span class="number">0</span>)<span class="comment">//接收未完成</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span>(USART_RX_STA&<span class="number">0x4000</span>)<span class="comment">//接收到了0x0d</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span>(Res!=<span class="number">0x0a</span>)<span class="built_in">USART_RX_STA</span>=<span class="number">0</span>;<span class="comment">//接收错误,重新开始</span></span><br><span class="line"><span class="keyword">else</span> USART_RX_STA|=<span class="number">0x8000</span>;<span class="comment">//接收完成了 </span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> <span class="comment">//还没收到0X0D</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span>(Res==<span class="number">0x0d</span>)<span class="built_in">USART_RX_STA</span>|=<span class="number">0x4000</span>;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line">USART_RX_BUF[USART_RX_STA&<span class="number">0X3FFF</span>]=Res ;<span class="comment">//接收缓冲</span></span><br><span class="line">USART_RX_STA++;</span><br><span class="line"><span class="keyword">if</span>(USART_RX_STA>(USART_REC_LEN<span class="number">-1</span>))<span class="built_in">USART_RX_STA</span>=<span class="number">0</span>;<span class="comment">//接收数据错误,重新开始接收</span></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 class="meta">#if SYSTEM_SUPPORT_OS <span class="comment">//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.</span></span></span><br><span class="line"><span class="built_in">OSIntExit</span>(); </span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>USART_RX_STA接收状态标记,bit15 接收完成标志,bit14 接收到0x0d,bit13~0,接收到的有效字节数目。</p><p>程序要求,发送的字符是以回车换行结束(0x0D,0x0A)。</p><p> 当接收到从电脑发过来的数据,把接收到的数据保存在 USART_RX_BUF 中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数,当收到回车(回车的表示由 2 个字节组成:0X0D 和 0X0A)的第一个字节 0X0D 时,计数器将不再增加,等待0X0A 的到来,而如果 0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。如果顺利接收到 0X0A,则标记 USART_RX_STA 的第 15 位,这样完成一次接收,并等待该位被其他程序清除,从而开始下一次的接收,而如果迟迟没有收到 0X0D,那么在接收数据超过 USART_REC_LEN 的时候,则会丢弃前面的数据,重新接收。</p><p> 计算机向串口发送一串字符,一般不止一个,例如发送”abcdefg回车“。那么<strong><em>串口中断函数会执行9次</em></strong>,回车要执行两次串口中断。当串口中断函数第一次执行时,USART1->DR里面装的是字符a,下面以串口第一次执行来分析这个串口中断函数。if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)这是判断读数据寄存器是否空,因为接受到了a,所以不是空的,这个判断成立。Res =USART_ReceiveData(USART1);//(USART1->DR); 既然接受到了字符a,那么就要把他读取出来。 if((USART_RX_STA&0x8000)==0) 因为现在接受的是第一个字符,所以接收肯定没有完成,USART_RX_STA还是它的初始化值,于是第15位还是0,这个判断语句成立。于是要执行下面这句话 if(USART_RX_STA&0x4000) USART_RX_STA的第14位仍然是0,所以这个判断不成立,所以会执行下面这句话。if(Res==0x0d) 当然这个判断也不成立,所以要执行下面这句话。USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;计算出接收的是第几个字符,然后装到缓存里面。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>虽然之前在一些课程中已经学习过关于串行通信原理的知识,但一直没有总结,很多知识也都忘记了。今天就重新梳理一下。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
<entry>
<title>EXTI外部中断</title>
<link href="https://wangzipai.github.io/posts/13433/"/>
<id>https://wangzipai.github.io/posts/13433/</id>
<published>2019-03-31T06:31:35.000Z</published>
<updated>2019-04-07T16:17:14.291Z</updated>
<content type="html"><![CDATA[<p>EXTI(External Intertupt)就是指外部中断,通过GPIO检测输入脉冲,引起中断事件,打断原来的代码执行流程,进入中断服务函数中进行处理,处理完后再回到中断之前的函数执行。CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。STM32F103系列上面,又只有60个可屏蔽中断。</p><a id="more"></a><p>stm32的所有GPIO都引入到EXTI外部中断线上,使得所有的GPIO都能作为外部中断的输入源。<br>STM32的中断控制器支持19个外部中断/事件请求:<br>线0~15:对应外部IO口的输入中断。<br>线16:连接到PVD输出。<br>线17:连接到RTC闹钟事件。<br>线18:连接到USB唤醒事件。<br>每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。<strong><em>PAx~PGx端口的中断事件都连接到EXTIx</em></strong>,即同一时刻EXTIx只能响应一个端口的事件触发,不能同一时间响应所有的GPIO端口的事件,但可以分时复用。</p><p><img src="//wangzipai.github.io/posts/13433/图片1.png" alt></p><h3 id="AFIO"><a href="#AFIO" class="headerlink" title="AFIO"></a>AFIO</h3><p>AFIO指GPIO端口的复用功能,当把GPIO用作EXTI外部中断或使用重映射功能时,必须开启AFIO时钟,而再使用默认复用功能的时候,不必开启AFIO时钟。</p><h3 id="EXTI初始化配置"><a href="#EXTI初始化配置" class="headerlink" title="EXTI初始化配置"></a>EXTI初始化配置</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">uint32_t</span> EXTI_Line; <span class="comment">//指定要配置的中断线 </span></span><br><span class="line"> EXTIMode_TypeDef EXTI_Mode; <span class="comment">//模式:事件 OR中断</span></span><br><span class="line"> EXTITrigger_TypeDef EXTI_Trigger;<span class="comment">//触发方式:上升沿/下降沿/双沿触发</span></span><br><span class="line"> FunctionalState EXTI_LineCmd; <span class="comment">//使能 OR失能</span></span><br><span class="line">}EXTI_InitTypeDef;</span><br></pre></td></tr></table></figure><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">EXTI_InitStructure.EXTI_Line=EXTI_Line2;</span> </span><br><span class="line"><span class="keyword">EXTI_InitStructure.EXTI_Mode </span>= <span class="keyword">EXTI_Mode_Interrupt;</span></span><br><span class="line"><span class="keyword">EXTI_InitStructure.EXTI_Trigger </span>= <span class="keyword">EXTI_Trigger_Falling;</span></span><br><span class="line"><span class="keyword">EXTI_InitStructure.EXTI_LineCmd </span>= ENABLE<span class="comment">;</span></span><br><span class="line"><span class="keyword">EXTI_Init(&EXTI_InitStructure);</span></span><br></pre></td></tr></table></figure><h3 id="编写中断服务函数"><a href="#编写中断服务函数" class="headerlink" title="编写中断服务函数"></a>编写中断服务函数</h3><p>stm32f10x_it.c文件是专门用来存放中断服务函数的。文件中默认只有几个关于系统异常的中断服务函数,且都是空函数,在需要的时候自行编写。(不可以自定义函数名,中断服务函数名必须与启动文件startup_stm32f10x_hd.s中的中断向量表定义一致)</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">DCD <span class="keyword">EXTI0_IRQHandler </span> <span class="comment">; EXTI Line 0</span></span><br><span class="line">DCD <span class="keyword">EXTI1_IRQHandler </span> <span class="comment">; EXTI Line 1</span></span><br><span class="line">DCD <span class="keyword">EXTI2_IRQHandler </span> <span class="comment">; EXTI Line 2</span></span><br><span class="line">DCD <span class="keyword">EXTI3_IRQHandler </span> <span class="comment">; EXTI Line 3</span></span><br><span class="line">DCD <span class="keyword">EXTI4_IRQHandler </span> <span class="comment">; EXTI Line 4</span></span><br><span class="line">DCD DMA1_Channel1_IRQHandler <span class="comment">; DMA1 Channel 1</span></span><br><span class="line">DCD DMA1_Channel2_IRQHandler <span class="comment">; DMA1 Channel 2</span></span><br><span class="line">DCD DMA1_Channel3_IRQHandler <span class="comment">; DMA1 Channel 3</span></span><br><span class="line">DCD DMA1_Channel4_IRQHandler <span class="comment">; DMA1 Channel 4</span></span><br><span class="line">DCD DMA1_Channel5_IRQHandler <span class="comment">; DMA1 Channel 5</span></span><br><span class="line">DCD DMA1_Channel6_IRQHandler <span class="comment">; DMA1 Channel 6</span></span><br><span class="line">DCD DMA1_Channel7_IRQHandler <span class="comment">; DMA1 Channel 7</span></span><br><span class="line">DCD ADC1_2_IRQHandler <span class="comment">; ADC1 & ADC2</span></span><br><span class="line">DCD USB_HP_CAN1_TX_IRQHandler <span class="comment">; USB High Priority or CAN1 TX</span></span><br><span class="line">DCD USB_LP_CAN1_RX0_IRQHandler <span class="comment">; USB Low Priority or CAN1 RX0</span></span><br><span class="line">DCD CAN1_RX1_IRQHandler <span class="comment">; CAN1 RX1</span></span><br><span class="line">DCD CAN1_SCE_IRQHandler <span class="comment">; CAN1 SCE</span></span><br><span class="line">DCD <span class="keyword">EXTI9_5_IRQHandler </span> <span class="comment">; EXTI Line 9..5</span></span><br><span class="line">DCD TIM1_BRK_IRQHandler <span class="comment">; TIM1 Break</span></span><br><span class="line">DCD TIM1_UP_IRQHandler <span class="comment">; TIM1 Update</span></span><br><span class="line">DCD TIM1_TRG_COM_IRQHandler <span class="comment">; TIM1 Trigger and Commutation</span></span><br><span class="line">DCD TIM1_CC_IRQHandler <span class="comment">; TIM1 Capture Compare</span></span><br><span class="line">DCD TIM2_IRQHandler <span class="comment">; TIM2</span></span><br><span class="line">DCD TIM3_IRQHandler <span class="comment">; TIM3</span></span><br><span class="line">DCD TIM4_IRQHandler <span class="comment">; TIM4</span></span><br><span class="line">DCD I2C1_EV_IRQHandler <span class="comment">; I2C1 Event</span></span><br><span class="line">DCD I2C1_ER_IRQHandler <span class="comment">; I2C1 Error</span></span><br><span class="line">DCD I2C2_EV_IRQHandler <span class="comment">; I2C2 Event</span></span><br><span class="line">DCD I2C2_ER_IRQHandler <span class="comment">; I2C2 Error</span></span><br><span class="line">DCD SPI1_IRQHandler <span class="comment">; SPI1</span></span><br><span class="line">DCD SPI2_IRQHandler <span class="comment">; SPI2</span></span><br><span class="line">DCD USART1_IRQHandler <span class="comment">; USART1</span></span><br><span class="line">DCD USART2_IRQHandler <span class="comment">; USART2</span></span><br><span class="line">DCD USART3_IRQHandler <span class="comment">; USART3</span></span><br><span class="line">DCD <span class="keyword">EXTI15_10_IRQHandler </span> <span class="comment">; EXTI Line 15..10</span></span><br></pre></td></tr></table></figure><p><strong><em>中断线在5之后就不能像0~4那样只有单独一个函数名,必须写成<code>EXTI9_5_IRQHandler</code> 和<code>EXTI15_10_IRQHandler</code>。</em></strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EXTI9_5_IRQHandler</span> <span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (LED1==<span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> LED1 = !LED1;</span><br><span class="line"> EXTI_ClearITPendingBit(EXTI_Line5);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>EXTI(External Intertupt)就是指外部中断,通过GPIO检测输入脉冲,引起中断事件,打断原来的代码执行流程,进入中断服务函数中进行处理,处理完后再回到中断之前的函数执行。CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。STM32F103系列上面,又只有60个可屏蔽中断。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
<entry>
<title>NVIC中断优先级管理</title>
<link href="https://wangzipai.github.io/posts/32250/"/>
<id>https://wangzipai.github.io/posts/32250/</id>
<published>2019-03-31T05:44:11.000Z</published>
<updated>2019-04-07T16:21:35.414Z</updated>
<content type="html"><![CDATA[<p>stm32对Cortex内核中断向量表进行重新编排,把编号从-3到6的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,如-3复位(Reset),-2不可屏蔽中断(NMI),-1硬错误(Hardfault)。编号从7开始为外部中断,优先级可自行设置。</p><a id="more"></a><p><img src="//wangzipai.github.io/posts/32250/1.png" alt></p><p>由于中断太多,配置困难,因此需要中断控制寄存器NVIC。NVIC属于Cortex内核器件,不可屏蔽中断和外部中断都由它处理,<strong><em>systick不是由他控制的</em></strong>。</p><h4 id="中断管理方法"><a href="#中断管理方法" class="headerlink" title="中断管理方法"></a>中断管理方法</h4><p>首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。由于NVIC只能配置16种中断向量的优先级,也就是说抢占优先级和响应优先级的数量由一个4位的数字来决定,把这个4位数字的位数配置成抢占优先级和响应优先级有5组分配式。分组配置是在寄存器SCB->AIRCR中配置。一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。</p><h5 id="中断优先级分组函数"><a href="#中断优先级分组函数" class="headerlink" title="中断优先级分组函数"></a>中断优先级分组函数</h5><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">NVIC_PriorityGroupConfig</span><span class="params">(<span class="keyword">uint32_t</span> NVIC_PriorityGroup)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));</span><br><span class="line"> SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">NVIC_PriorityGroupConfig(<span class="name">NVIC_PriorityGroup_2</span>)<span class="comment">;</span></span><br></pre></td></tr></table></figure><h3 id="中断优先级设置"><a href="#中断优先级设置" class="headerlink" title="中断优先级设置"></a>中断优先级设置</h3><p>分组设置好之后,设置单个中断的抢占优先级和响应优先级</p><h4 id="中断设置相关寄存器"><a href="#中断设置相关寄存器" class="headerlink" title="中断设置相关寄存器"></a>中断设置相关寄存器</h4><p>__IO uint8_t IP[240]; //中断优先级控制的寄存器组</p><p>__IO uint32_t ISER[8]; //中断使能寄存器组。用来使能中断。</p><p>__IO uint32_t ICER[8]; //中断失能寄存器组。作用:用来失能中断。</p><p>__IO uint32_t ISPR[8]; //中断挂起寄存器组。作用:用来挂起中断。</p><p>__IO uint32_t ICPR[8]; //中断解挂寄存器组。作用:用来解挂中断。</p><p>__IO uint32_t IABR[8]; //中断激活标志位寄存器组。作用:只读,通过它可以知道当前在执行的中断是哪一个,如果对应位为1,说明该中断正在执行。</p><h4 id="MDK中NVIC寄存器结构体"><a href="#MDK中NVIC寄存器结构体" class="headerlink" title="MDK中NVIC寄存器结构体"></a>MDK中NVIC寄存器结构体</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> __IO <span class="keyword">uint32_t</span> ISER[<span class="number">8</span>]; </span><br><span class="line"> <span class="keyword">uint32_t</span> RESERVED0[<span class="number">24</span>]; </span><br><span class="line"> __IO <span class="keyword">uint32_t</span> ICER[<span class="number">8</span>]; </span><br><span class="line"> <span class="keyword">uint32_t</span> RSERVED1[<span class="number">24</span>]; </span><br><span class="line"> __IO <span class="keyword">uint32_t</span> ISPR[<span class="number">8</span>]; </span><br><span class="line"> <span class="keyword">uint32_t</span> RESERVED2[<span class="number">24</span>]; </span><br><span class="line"> __IO <span class="keyword">uint32_t</span> ICPR[<span class="number">8</span>]; </span><br><span class="line"> <span class="keyword">uint32_t</span> RESERVED3[<span class="number">24</span>]; </span><br><span class="line"> __IO <span class="keyword">uint32_t</span> IABR[<span class="number">8</span>]; </span><br><span class="line"> <span class="keyword">uint32_t</span> RESERVED4[<span class="number">56</span>]; </span><br><span class="line"> __IO <span class="keyword">uint8_t</span> IP[<span class="number">240</span>]; </span><br><span class="line"> <span class="keyword">uint32_t</span> RESERVED5[<span class="number">644</span>]; </span><br><span class="line"> __O <span class="keyword">uint32_t</span> STIR; </span><br><span class="line">} NVIC_Type;</span><br></pre></td></tr></table></figure><p>中断优先级控制的寄存器组:IP[240] 。作用:设置每个中断优先级。全称是:Interrupt Priority Registers。240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F10x系列一共60个可屏蔽中断,使用IP[59]~IP[0]。每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。</p><p>中断使能寄存器组:ISER[8]。作用:用来使能中断。32位寄存器,每个位控制一个中断的使能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ISER[0]和ISER[1]。ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59;</p><p>中断失能寄存器组:ICER[8]。作用:用来失能中断。32位寄存器,每个位控制一个中断的失能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断32~59;配置方法跟ISER一样。</p><h4 id="中断参数初始化函数"><a href="#中断参数初始化函数" class="headerlink" title="中断参数初始化函数"></a>中断参数初始化函数</h4><p><code>void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="keyword">uint8_t</span> NVIC_IRQChannel; <span class="comment">//设置中断通道</span></span><br><span class="line"> <span class="keyword">uint8_t</span> NVIC_IRQChannelPreemptionPriority;<span class="comment">//设置响应优先级</span></span><br><span class="line"> <span class="keyword">uint8_t</span> NVIC_IRQChannelSubPriority; <span class="comment">//设置抢占优先级</span></span><br><span class="line"> FunctionalState NVIC_IRQChannelCmd; <span class="comment">//使能/使能</span></span><br><span class="line">} NVIC_InitTypeDef;</span><br></pre></td></tr></table></figure><figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">NVIC_InitTypeDef NVIC_InitStructure;</span><br><span class="line">NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;<span class="comment">//串口1中断</span></span><br><span class="line">NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=<span class="number">1</span> ;<span class="comment">// 抢占优先级为1</span></span><br><span class="line">NVIC_InitStructure.NVIC_IRQChannelSubPriority = <span class="number">2</span>;<span class="comment">// 子优先级位2</span></span><br><span class="line">NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;<span class="comment">//IRQ通道使能</span></span><br><span class="line">NVIC_Init(&NVIC_InitStructure);//根据上面指定的参数初始化NVIC寄存器</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>stm32对Cortex内核中断向量表进行重新编排,把编号从-3到6的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,如-3复位(Reset),-2不可屏蔽中断(NMI),-1硬错误(Hardfault)。编号从7开始为外部中断,优先级可自行设置。</p>
</summary>
<category term="stm32" scheme="https://wangzipai.github.io/tags/stm32/"/>
</entry>
</feed>