<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>凛星的随笔</title><description>摸爬滚打着前进的工科生</description><link>https://rinx.nz/</link><language>zh_CN</language><item><title>速通数电</title><link>https://rinx.nz/posts/digital-electronics-note/</link><guid isPermaLink="true">https://rinx.nz/posts/digital-electronics-note/</guid><description>时间紧任务重！速通数字电子技术基础</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：参考文献&lt;/strong&gt;
&lt;a href=&quot;https://www.bilibili.com/video/BV1kZ4y1w7dy&quot;&gt;数电-这个ximo不太冷&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;数制与码制&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;此处省略了一部分知识点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;格雷码&lt;/h2&gt;
&lt;h3&gt;定义&lt;/h3&gt;
&lt;p&gt;又称 “循环BCD码” 或 “Gray码”。&lt;/p&gt;
&lt;p&gt;定义：&lt;code&gt;G = B \oplus (B \gg 1)&lt;/code&gt;。表现为：&lt;strong&gt;相邻的编码只有一位不同&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;来源&lt;/h3&gt;
&lt;p&gt;常规的二进制在不断加一的过程中，可能会出现多位同时翻转的情况，例如从 3 加到 4 的过程中，最低的三位全都变了（011 -&amp;gt; 100）。而 格雷码希望“每次只变一位”。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;格雷码在编码器、计数器、位置检测等场景中更稳定，不容易因为多位同时翻转而出错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为了得到 格雷码，我们需要先观察二进制进位时发生的现象。从 3 到 4 时，最后两位从 11 变成了 00, 第三位则从 0 变成了 1。&lt;br /&gt;
这个过程中，没有变的是 &lt;strong&gt;“最后两位相同”&lt;/strong&gt;。而利用异或，恰好可以抹去这种 “11” 到 “00” 的变化。&lt;/p&gt;
&lt;p&gt;换句话说，格雷码记录的实际上是：&lt;strong&gt;二进制的相邻两位之间有没有发生变化&lt;/strong&gt;。&lt;br /&gt;
这使得当二进制数加 1 时，虽然底层可能有多个位翻转，但这些翻转在“相邻位关系”里最终只会体现为一个位置的变化，于是 格雷码就只变 1 位。&lt;/p&gt;
&lt;p&gt;例如数字 3，计算：&lt;code&gt;011 \oplus (011 \gg 1) = 010&lt;/code&gt;；数字 4，计算：&lt;code&gt;100 \oplus (100 \gg 1) = 110&lt;/code&gt;。两者只差了一位&lt;/p&gt;
&lt;h3&gt;逆变换&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;b_n&lt;/code&gt; 为二进制数，&lt;code&gt;g_n&lt;/code&gt; 为格雷码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;b_n = g_n&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;b_i = b_{i+1} \oplus g_i&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;也就是：最高位不变，后续位分别与左侧的结果进行异或。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;逻辑代数基础&lt;/h1&gt;
&lt;h2&gt;与、或、非、异或的特殊用途&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;与运算 AND：可将指定的位 置0&lt;/li&gt;
&lt;li&gt;或运算 OR：可将指定的位 置1&lt;/li&gt;
&lt;li&gt;非运算 NOT：整体取反&lt;/li&gt;
&lt;li&gt;异或运算 XOR：可将指定位取反。与 0 异或不变，与 1 异或取反&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
异或运算（ &lt;code&gt;A \oplus B&lt;/code&gt; ）也可以表示为 &lt;code&gt;A&apos;B + AB&apos;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;基础定理&lt;/h2&gt;
&lt;h3&gt;分配率公式&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;A(B + C) = AB + AC&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;A + BC = (A + B)(A + C)&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
上述公式的推导为：&lt;br /&gt;
&lt;code&gt;(A + B)(A + C) = A + AB + AC + BC = A(1 + B + C) + BC = A + BC&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;反演率 (德摩根定律)&lt;/h3&gt;
&lt;p&gt;这是 “与” 和 “或” 互换的基石。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
与的非等于非的或，或的非等于非的与&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;(AB)&apos; = A&apos; + B&apos;&lt;/code&gt; - 对应：A 与 B 中至少有一个为假&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(A + B)&apos; = A&apos;B&apos;&lt;/code&gt; - 对应：A 与 B 都为假&lt;/p&gt;
&lt;h3&gt;反演定律&lt;/h3&gt;
&lt;p&gt;两条基本公式：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\overline{A \cdot B} = \overline{A} + \overline{B}&lt;/code&gt; - 对应：A 和 B 不同时为真&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\overline{A + B} = \overline{A} \cdot \overline{B}&lt;/code&gt; - 对应：A 和 B 都不为真&lt;/p&gt;
&lt;h3&gt;多余项定律&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;AB + A&apos;C + (BC...) = AB + A&apos;C&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;对偶定理&lt;/h3&gt;
&lt;p&gt;若两逻辑表达式相等，则&lt;strong&gt;它们的“对偶式”也相等&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;对偶式的书写规则：&lt;strong&gt;与 ↔ 或； 0 ↔ 1； 变量和反变量不变&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;公式法化简&lt;/h2&gt;
&lt;p&gt;利用基础定理进行化简，注意&lt;strong&gt;慎用&lt;/strong&gt;“消去率”&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;待补充&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;波形图转逻辑函数式&lt;/h2&gt;
&lt;h3&gt;通法&lt;/h3&gt;
&lt;p&gt;观察所有 Y = 1 的部分，假设某一个时刻：&lt;br /&gt;
&lt;code&gt;Y = 1, A = 1, B = 0, C = 1&lt;/code&gt;&lt;br /&gt;
则有：&lt;code&gt;Y = AB&apos;C + ...&lt;/code&gt;&lt;br /&gt;
以此类推，按顺序写完所有情况。也就是每组输入变量取值的组合对应一个乘积项。&lt;/p&gt;
&lt;h3&gt;最小项&lt;/h3&gt;
&lt;p&gt;在上面的通法中，&lt;code&gt;AB&apos;C&lt;/code&gt; 就是一个最小项。使其值为 1 的变量取值就是它的编号（在某一类输入下为 1），此处为 &lt;code&gt;(101)_B = 5&lt;/code&gt;，即 &lt;code&gt;m_4&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;全体最小项之和为 1；任意两个最小值之积为 0；两个逻辑相邻的最小项之和可以合并，消去一对因子。&lt;/p&gt;
&lt;h3&gt;最大项&lt;/h3&gt;
&lt;p&gt;例如 &lt;code&gt;A + B + C&apos;&lt;/code&gt;。使其值为 0 的变量取值就是它的编号（在某一类输入下为 0），此处为 &lt;code&gt;(001)_B = 1&lt;/code&gt;，即 &lt;code&gt;M_1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;全体最大项之积为 0；任何两个最大项之和为 1；&lt;/p&gt;
&lt;h3&gt;最小项与最大项的关系&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;最小项是从单个输入开始，不断寻找更多的输入来作为限制条件&lt;br /&gt;
最大项是从所有输入开始，不断排除无关的输入&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;单个最大项与最小项存在联系，&lt;code&gt;M_1&apos; = \overline{A + B + \overline{C}} = A&apos;B&apos;C = m_1&lt;/code&gt;，最大项与最小项相反的编号原则恰好凑上了反演定律。&lt;/p&gt;
&lt;p&gt;而在 n = 3 时，&lt;code&gt;2^3 - 1 = 5&lt;/code&gt;，所以：&lt;code&gt;M_1&apos; = m_1 = m_5&apos;&lt;/code&gt;，即 &lt;code&gt;M_1 = m_5&lt;/code&gt;，两者的下表集合是互补的。&lt;/p&gt;
&lt;h2&gt;卡诺图化简法&lt;/h2&gt;
&lt;h3&gt;卡诺图的绘制&lt;/h3&gt;
&lt;p&gt;卡诺图，本质是用格雷码列出&lt;strong&gt;最小项&lt;/strong&gt;矩阵，&lt;strong&gt;将逻辑上的相邻表现为几何图形的相邻&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;理想的卡诺图应该是一个 n 维的立方体，我们只能尽量将它投影到纸面上。&lt;br /&gt;
使用格雷码而非二进制数，是因为格雷码能保证逻辑上相邻&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;例如三个变量时：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A\BC&lt;/th&gt;
&lt;th&gt;00&lt;/th&gt;
&lt;th&gt;01&lt;/th&gt;
&lt;th&gt;11&lt;/th&gt;
&lt;th&gt;10&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;m_6&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;四个变量时（表中的数字为最小项编号）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AB\CD&lt;/th&gt;
&lt;th&gt;00&lt;/th&gt;
&lt;th&gt;01&lt;/th&gt;
&lt;th&gt;11&lt;/th&gt;
&lt;th&gt;10&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;00&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;重要：卡诺图中的相邻&lt;/strong&gt;
卡诺图更接近与一个闭合的图形，由于格雷码的特性，&lt;strong&gt;两个轴对称的方块也是相邻的&lt;/strong&gt;（注意不是中心对称），有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;左右边界相邻&lt;/li&gt;
&lt;li&gt;上下边界相邻&lt;/li&gt;
&lt;li&gt;四个角可以组成一组&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意：下文中的“相邻”指的都是逻辑上的相邻&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;函数式转卡诺图&lt;/h3&gt;
&lt;p&gt;例如 &lt;code&gt;AB + AC&lt;/code&gt;，遵循以下步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AB：在同时满足 A = 1 与 B = 1 的方格内填 1&lt;/li&gt;
&lt;li&gt;AC：在同时满足 A = 1 与 C = 1 的方格内填 1&lt;/li&gt;
&lt;li&gt;其余表格填 0&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
这里本质上是用了 &lt;code&gt;AB = AB(C + C&apos;)&lt;/code&gt; 的公式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;对于更复杂的 &lt;code&gt;Y = A&apos;BC&apos;D + B&apos;C&apos;D&apos; + A&apos;C&lt;/code&gt;，则有：（空白格子为 0）&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AB\CD&lt;/th&gt;
&lt;th&gt;00&lt;/th&gt;
&lt;th&gt;01&lt;/th&gt;
&lt;th&gt;11&lt;/th&gt;
&lt;th&gt;10&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;00&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;卡诺图转最简式&lt;/h3&gt;
&lt;p&gt;在卡诺图上，“相邻”的两个方格对应一个可以消去的因子，“相邻”的 2*2 共4个方格对应两个可以消去的因子，&lt;code&gt;2^n&lt;/code&gt; 个格子对应 &lt;code&gt;n&lt;/code&gt; 个可以消去的因子……&lt;br /&gt;
不断寻找这种“相邻”关系，并将它们圈起来作为标记，就可以据此写出对应的最简式。&lt;br /&gt;
同时，&lt;strong&gt;圈的个数越少越好，单个圈越大越好&lt;/strong&gt;。这样才能保证最简。&lt;/p&gt;
&lt;p&gt;需要注意的是，&lt;strong&gt;每个方格都是可以重复利用的&lt;/strong&gt;，逻辑上的并集并不影响正确性。&lt;br /&gt;
例如：上图中右上四个可以合并，最左侧的两个可以合并，第二排的中间两个也可以合并。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
卡诺图中六个相邻的方格不可能一次性化简完毕，至少分为两组&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;卡诺图转最简与或式&lt;/h4&gt;
&lt;p&gt;最简与或式：包含的乘积项最少，且每个乘积项里的因子也不能再减少。或者也可以说是最小项的和。&lt;br /&gt;
结合最小项的定义，应该在卡诺图中&lt;strong&gt;将所有 1 的格子圈起来&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;对于上图，可得 &lt;code&gt;Y = A&apos;C + A&apos;BD + B&apos;C&apos;D&apos;&lt;/code&gt;，相比原式更简单了。&lt;/p&gt;
&lt;h4&gt;卡诺图转最简或与式&lt;/h4&gt;
&lt;p&gt;或与式，也就是最大项的&lt;strong&gt;乘积&lt;/strong&gt;。&lt;br /&gt;
结合最大项的定义，应该在卡诺图中&lt;strong&gt;将所有 0 的格子圈起来&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;同时需要注意：最大项的编号原则与最小项相反。&lt;/p&gt;
&lt;h4&gt;证明逻辑等式&lt;/h4&gt;
&lt;p&gt;可以将卡诺图视作“满足逻辑相邻”的真值表，因此也可以用它来证明逻辑等式成立。&lt;/p&gt;
&lt;h3&gt;嵌套逻辑函数的化简&lt;/h3&gt;
&lt;p&gt;对于较为复杂的嵌套型逻辑函数，例如：&lt;br /&gt;
&lt;code&gt;Y = (A&apos;D&apos; + C&apos;D + CD&apos;) \oplus (AC&apos;D&apos; + ABC + A&apos;D + CD)&lt;/code&gt;&lt;br /&gt;
针对左右两个与或式，可以记为 Y1 与 Y2, 分别画出卡诺图，然后对两张卡诺图相同位置的方块做异或，结果就是 Y 的卡诺图。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
此处如果不使用卡诺图来计算，将非常复杂&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;含有无关项的化简&lt;/h2&gt;
&lt;p&gt;通常以最小项之和的形式讨论无关项，无关项分为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;约束项：使其取值为 1 的输入组合是不允许出现的，因此（实际上）约束项的值始终为 0。即使函数式中包括了约束项，也不会影响最终结果&lt;/li&gt;
&lt;li&gt;任意项：在特定的（可能出现的）输入组合下，无需关心其输出，值为 0 或 1 均可&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
实际上这俩区别不大，都表现为：在某些输入组合下，不需要关心输出。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;两种无关项的共同点是：&lt;strong&gt;在特定的输入组合下，函数输出未被规定，取 0 或 1 皆可&lt;/strong&gt;。&lt;br /&gt;
其在卡诺图中通常用 &lt;code&gt;\times&lt;/code&gt; 表示，化简时可以依据具体需要，将 &lt;code&gt;\times&lt;/code&gt; 当作 0 或 1.&lt;/p&gt;
&lt;p&gt;在函数式中，无关项通常用 &lt;code&gt;d&lt;/code&gt; 表示 (don&apos;t care)，表示无关项对应的编号集合，例如：&lt;br /&gt;
&lt;code&gt;Y = \Sigma{m(\cdot\cdot\cdot)} + d(1, 2, 5)&lt;/code&gt;&lt;br /&gt;
表示：&lt;code&gt;m_1, m_2, m_5&lt;/code&gt; 对应的输入组合是无关项。&lt;/p&gt;
&lt;h2&gt;逻辑函数的其他变换形式&lt;/h2&gt;
&lt;h3&gt;与非-与非形式&lt;/h3&gt;
&lt;p&gt;将 &lt;strong&gt;与或式&lt;/strong&gt; 取两次反，也就是利用反演率:&lt;br /&gt;
&lt;code&gt;Y = AB + AC = \overline{\overline{AB + AC}} = \overline{\overline{AB} \cdot \overline{AC} }&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;与或非形式&lt;/h3&gt;
&lt;p&gt;针对 &lt;code&gt;Y&apos;&lt;/code&gt; 的卡诺图进行化简，写出 &lt;strong&gt;与或式&lt;/strong&gt; 后取反：
&lt;code&gt;Y = AB + AC = (A&apos; + B&apos;C&apos;)&apos;&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;或与形式&lt;/h3&gt;
&lt;p&gt;对 &lt;strong&gt;与或非式&lt;/strong&gt; 应用反演率：&lt;br /&gt;
&lt;code&gt;Y = (A&apos; + B&apos;C&apos;)&apos; = (A&apos;&apos;)((B&apos;C&apos;)&apos;) = A(B + C)&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
也可以直接由卡诺图得到&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;或非-或非形式&lt;/h3&gt;
&lt;p&gt;对 &lt;strong&gt;与或非式&lt;/strong&gt; 中的“与”应用反演率，例：&lt;br /&gt;
&lt;code&gt;Y = (A&apos; + B&apos;C&apos;)&apos; = (A&apos; + (B&apos;C&apos;)&apos;&apos;)&apos; = (A&apos; + (B + C)&apos;)&apos;&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;重要&lt;/strong&gt;
上述形式即使化到最简，也不一定是唯一的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;证明题&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;用公式变换化简&lt;/li&gt;
&lt;li&gt;两边取对偶式，证明对偶式相等&lt;/li&gt;
&lt;li&gt;写出真值表 / 画出卡诺图（写成最小项之和）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;门电路&lt;/h1&gt;
&lt;h2&gt;门电路概述&lt;/h2&gt;
&lt;h3&gt;定义&lt;/h3&gt;
&lt;p&gt;门电路就是用来实现基本逻辑运算的电子电路。&lt;/p&gt;
&lt;h3&gt;电平标准&lt;/h3&gt;
&lt;p&gt;一般选择零电平附近的一个值作为低电平，一般选择电源 &lt;code&gt;V_DD / V_CC&lt;/code&gt; 附近的一个值作为高电平&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：关于下标简写&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I: Input, 输入电平&lt;/li&gt;
&lt;li&gt;O: Output, 输出电平&lt;/li&gt;
&lt;li&gt;L: Low, 低电平&lt;/li&gt;
&lt;li&gt;H: High, 高电平&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;V_{IL(max)}&lt;/code&gt; ：低于此值则认为是低电平输入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V_{IH(min)}&lt;/code&gt; ：高于此值则认为是高电平输入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V_{OL(max)}&lt;/code&gt; ：低于此值则认为是低电平输出&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V_{OH(min)}&lt;/code&gt; ：高于此值则认为是高电平输出&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;其实就是四个阈值。设计电路时，需要特别输入电压能否满足要求&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;重要：器件设计的电平原则&lt;/strong&gt;
&lt;strong&gt;接收低质量 0/1，输出高质量 0/1&lt;/strong&gt;&lt;br /&gt;
例如 3.3 的门电路，0.7V 是低质量的 0 信号，0.2V 是高质量的 0 信号&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;晶体管电路基础&lt;/h2&gt;
&lt;p&gt;在数电中，晶体管通常工作在&lt;strong&gt;饱和区&lt;/strong&gt;与&lt;strong&gt;倒置状态&lt;/strong&gt;（功率较小）&lt;/p&gt;
&lt;h3&gt;进入饱和区的条件&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;饱和的本质条件是：&lt;strong&gt;发射结正偏，集电结也正偏&lt;/strong&gt;；也可以理解成，基极驱动过强，&lt;strong&gt;集电极回路限制了电流，三极管无法达到应有的放大倍率&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;集电极电阻 &lt;code&gt;R_C&lt;/code&gt; 越大，则 &lt;code&gt;R_C&lt;/code&gt; 上的压降越大，越可能进入饱和区（集电极无法达到应有的电流）&lt;/li&gt;
&lt;li&gt;基极电流越大，越可能饱和；&lt;/li&gt;
&lt;li&gt;电流放大系数 &lt;code&gt;\beta&lt;/code&gt; 越大，越可能饱和。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;晶体管的倒置状态&lt;/h3&gt;
&lt;p&gt;倒置，也就是在电路中把 c 和 e 接反。&lt;br /&gt;
此时集电区“发射”电子，发射区“收集”电子。由于集电区参杂浓度低，且发射区面积小，通过集电极的电流将较小，三极管失去放大能力，或者说 &lt;code&gt;\beta &amp;lt; 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这种状态常用于 TTL 的输入级。&lt;/p&gt;
&lt;h2&gt;MOS 管电路基础&lt;/h2&gt;
&lt;p&gt;在数电中，MOS 管通常工作在&lt;strong&gt;截止区&lt;/strong&gt;与&lt;strong&gt;可变电阻区&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;下文中的 MOS 管一般指 &lt;strong&gt;N沟道增强型 MOSFET&lt;/strong&gt;（绝缘栅型场效应管）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;伏安特性&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;夹断区：不存在导电沟道，可能有微小的漏电流&lt;/li&gt;
&lt;li&gt;可变电阻区（线性区）：漏极电流不仅取决于 &lt;code&gt;U_{GS}&lt;/code&gt;，还和 &lt;code&gt;U_{DS}&lt;/code&gt; 成线性关系。&lt;code&gt;U_{GS}&lt;/code&gt; 越高，导通电阻越小，越接近理想短路。&lt;/li&gt;
&lt;li&gt;恒流区（饱和区）：漏极电流仅受 &lt;code&gt;U_{GS}&lt;/code&gt; 控制，与 &lt;code&gt;U_{DS}&lt;/code&gt; 无关&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;半导体二极管门电路&lt;/h2&gt;
&lt;h3&gt;与门&lt;/h3&gt;
&lt;p&gt;可由 &lt;strong&gt;共阳&lt;/strong&gt;二极管与&lt;strong&gt;上拉电阻&lt;/strong&gt; 组成；&lt;br /&gt;
有一个输入低电平，输出就会被钳位在低电平&lt;/p&gt;
&lt;h3&gt;或门&lt;/h3&gt;
&lt;p&gt;可由 &lt;strong&gt;共阴&lt;/strong&gt;二极管与&lt;strong&gt;下拉电阻&lt;/strong&gt; 组成；&lt;br /&gt;
有一个输入高电平，输出就会被钳位在高电平&lt;/p&gt;
&lt;h3&gt;存在的问题&lt;/h3&gt;
&lt;p&gt;功耗大、电平易偏移、驱动能力差。&lt;br /&gt;
同时功率和驱动能力是互相矛盾的，无法同时优化。&lt;/p&gt;
&lt;h2&gt;CMOS 门电路&lt;/h2&gt;
&lt;p&gt;CMOS，即&lt;strong&gt;互补式&lt;/strong&gt; MOS 门电路&lt;/p&gt;
&lt;h3&gt;CMOS 反相器&lt;/h3&gt;
&lt;h4&gt;组成&lt;/h4&gt;
&lt;p&gt;由一个 PMOS 和一个 NMOS 组成，NMOS 负责下拉（在地侧），PMOS 负责上拉（在电源侧）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入高电平，NMOS 导通，下拉&lt;/li&gt;
&lt;li&gt;输入低电平，PMOS 导通，上拉&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;重要：简记&lt;/strong&gt;
&lt;strong&gt;NMOS 是高电平导通，PMOS 是低电平导通&lt;/strong&gt;
这也可以从衬底所连接的电平看出来，NMOS 的衬底连接低电平&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所有的门电路其实都可以看到反相器的身影。&lt;/p&gt;
&lt;h4&gt;输入端噪声容限&lt;/h4&gt;
&lt;p&gt;指：保证输出的高低电平基本不变时，允许的输入的高低电平的波动范围&lt;br /&gt;
以高电平为例，&lt;code&gt;V_{NH} = V_{OH(min)} - V_{IH(min)}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;多级电路中，每一级电路都要做到“接收低质量 0/1，输出高质量 0/1”，这个“质量差”就是噪声容限。&lt;/p&gt;
&lt;p&gt;电源电压越高，输入噪声容限越大（近似成比例），但同时也会增加功耗。&lt;/p&gt;
&lt;h4&gt;静态输入特性&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;静态特性：指电路达到稳定状态时，即输出固定的高/低电平；不考虑电平跃变的转折区特性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;输入特性：输入电压和输入电流的关系，即从信号源索取电流的能力&lt;br /&gt;
在 CMOS 中，由于 MOS 管的绝缘栅特性，正常工作时输入电流近似为零；而保护电路作用时，输入电流受信号源内阻影响&lt;/p&gt;
&lt;h4&gt;静态输出特性&lt;/h4&gt;
&lt;p&gt;输出特性：输出电压与输出电流的关系，即&lt;strong&gt;带负载的能力&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;灌电流：CMOS 输出低电平时，流向内部的电流；&lt;code&gt;I_{OL(max)}&lt;/code&gt;：最大灌电流&lt;/li&gt;
&lt;li&gt;拉电流：CMOS 输出高电平时，流向外部的电流；&lt;code&gt;I_{OH(min)}&lt;/code&gt;：最大拉电流&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;输出&lt;strong&gt;低电平&lt;/strong&gt;时，由 NMOS 负责下拉，同时工作在可变电阻区，此时输入电平越高，导通电阻越小&lt;br /&gt;
也就是说随着输出功率的升高，输出电平也会同步升高，最终可能超过阈值电压&lt;/p&gt;
&lt;p&gt;输出&lt;strong&gt;高电平&lt;/strong&gt;时，由 PMOS 负责上拉，同理。&lt;/p&gt;
&lt;h4&gt;动态特性&lt;/h4&gt;
&lt;p&gt;即高低电平切换时的特性，有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传输延迟：输出滞后于输入&lt;/li&gt;
&lt;li&gt;交流噪声容限：输出能发生改变时，输入信号的最小变化幅度&lt;/li&gt;
&lt;li&gt;动态功耗：输出电压跃变时的附加功耗&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;设计 CMOS 门电路&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;依据逻辑输出为 0 的输入组合设计下拉电路&lt;/li&gt;
&lt;li&gt;依据逻辑输出为 1 的输入组合设计上拉电路&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>NixOS 连接过时的企业Wi-Fi</title><link>https://rinx.nz/posts/nixos-connect-legacy-enterprise-wifi/</link><guid isPermaLink="true">https://rinx.nz/posts/nixos-connect-legacy-enterprise-wifi/</guid><description>解决部分 802.1X 企业网络配置过旧导致无法通过系统的安全检测</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;问题来源&lt;/h1&gt;
&lt;p&gt;如果你在连接一些企业 Wi-Fi 时，看到了包含以下字眼的错误：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无法激活连接：确定 AP 安全信息失败&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CA 验证失败&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TLS renegotiation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么本文或许对你有用。&lt;/p&gt;
&lt;h1&gt;解决方案&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;警告&lt;/strong&gt;
这只是凑合用的手段，会增加网络被攻击的风险。如果可以，请让相关管理部门升级网络基础设施。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;更改相关的 OpenSSL 配置&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;/etc/nixos&lt;/code&gt; 下的 &lt;code&gt;configuration.nix&lt;/code&gt; 中增加以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nix&quot;&gt;  # 修复：无法连接不遵循规范的的WPA2企业网络
  systemd.services.wpa_supplicant.environment.OPENSSL_CONF = pkgs.writeText &quot;openssl.cnf&quot; &apos;&apos;
    openssl_conf = openssl_init

    [openssl_init]
    ssl_conf = ssl_sect

    [ssl_sect]
    system_default = system_default_sect

    [system_default_sect]
    Options = UnsafeLegacyRenegotiation
    CipherString = Default:@SECLEVEL=0
  &apos;&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段配置的大致行为是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;修改了 &lt;code&gt;wpa_supplicant&lt;/code&gt; 的 &lt;code&gt;OpenSSL&lt;/code&gt; 配置&lt;/li&gt;
&lt;li&gt;允许不安全的旧式 TLS renegotiation（理论上会增加被中间人利用旧协商缺陷的风险）&lt;/li&gt;
&lt;li&gt;把 OpenSSL 的加密强度门槛降到最低兼容模式（允许弱加密配置）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后记得&lt;strong&gt;重建系统&lt;/strong&gt;（如果没有使用 flake 就删掉 &lt;code&gt;--flake&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nixos-rebuild switch --flake .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;手动添加连接&lt;/h2&gt;
&lt;p&gt;由于 &lt;code&gt;nmtui&lt;/code&gt; 等工具会拒绝连接加密强度低的网络，需要手动增加连接。运行下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nmcli connection add type wifi \
  con-name &quot;&amp;lt;连接名称(一般和SSID相同)&amp;gt;&quot; \
  ifname &quot;*&quot; \
  ssid &quot;&amp;lt;网络的SSID&amp;gt;&quot; \
  wifi-sec.key-mgmt wpa-eap \
  802-1x.eap peap \
  802-1x.identity &quot;&amp;lt;账号&amp;gt;&quot; \
  802-1x.password &quot;&amp;lt;密码&amp;gt;&quot; \
  802-1x.phase2-auth mschapv2
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
请将命令中所有被 &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; 包裹的部分根据实际情况做出修改。&lt;br /&gt;
以下命令适用于使用 WPA-EAP 与 MSCHAPV2 的网络，如果你不确定网络的配置，可以先在手机上尝试登陆。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;执行后就可以在“已保存的连接”中看到了。&lt;/p&gt;
</content:encoded></item><item><title>Gitleaks 基础使用 - 不要再广播密钥啦</title><link>https://rinx.nz/posts/gitleaks/</link><guid isPermaLink="true">https://rinx.nz/posts/gitleaks/</guid><description>从 Gitleaks 检查，到抹除某一行密钥内容，逐行教你如何在项目开源前确保脱敏</description><pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;这是个什么？&lt;/h1&gt;
&lt;p&gt;Gitleaks 是一个开源 SAST（静态应用安全测试）命令行工具，用于检测 Git 仓库以防止把密码、API 密钥和访问令牌等机密信息硬编码到代码中。&lt;/p&gt;
&lt;p&gt;简单来说，就是&lt;strong&gt;防止你把密钥公之于众，然后被刷爆&lt;/strong&gt;。&lt;/p&gt;
&lt;h1&gt;如何安装&lt;/h1&gt;
&lt;h2&gt;Ubuntu/Debian&lt;/h2&gt;
&lt;p&gt;APT 似乎没有 Gitleaks 的包，可以选择 &lt;a href=&quot;https://github.com/gitleaks/gitleaks/releases&quot;&gt;下载&lt;/a&gt; 对应架构的二进制包，解压后直接运行即可（也可移至 &lt;code&gt;/usr/local/bin&lt;/code&gt; 以便全局使用&lt;/p&gt;
&lt;h2&gt;NixOS&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I use NixOS, btw.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nixpkgs 实在是一个伟大的发明，你只需要运行这行命令：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nix-shell -p gitleaks
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就可以获得一个临时的，拥有 Gitleaks 软件包的 shell。当然也可以将其加入 &lt;code&gt;systemPackages&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Github Action&lt;/h2&gt;
&lt;p&gt;顺带一提，官方提供了 &lt;a href=&quot;https://github.com/marketplace/actions/gitleaks&quot;&gt;Action&lt;/a&gt; ，可以直接关联仓库并检查（虽然我没用过）&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;如何使用&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gitleaks git . -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该行命令会扫描当前目录下的 git 仓库，并提示可能的泄露
这里我建了一个临时的仓库来实验，并提交了一些commit，然后使用 gitleaks 检查：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://rinx.nz/assets/images/gitleaks-2.webp&quot; alt=&quot;测试结果&quot; /&gt;&lt;/p&gt;
&lt;p&gt;虽然 gitleaks 成功检测到了可能的泄露，但此时我们已经提交了很多commit, 一个个 revert 是有些不太现实的。当然，你可以选择直接吊销/轮换密钥。
这里假设密钥和账户强绑定，无法吊销；或者项目曾经是闭源的，没有在意这方面，那就需要强制修改 git 历史了。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;强制修改 git 历史的方法&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;
请小心谨慎地修改 git 历史，并在 &lt;code&gt;git push&lt;/code&gt; 后确保在所有设备上拉取一遍以保证同步&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;git 原生方法&lt;/h2&gt;
&lt;h3&gt;常规修改&lt;/h3&gt;
&lt;p&gt;如何泄露的影响还算不大，可以使用 Git 内置的交互式变基 (interactive rebase) 来逐个更改提交内容。
例如上图，需要变基到 &lt;code&gt;2e8121e&lt;/code&gt; 才能修改之后的 &lt;code&gt;d9df113&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git rebase -i 2e8121e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://rinx.nz/assets/images/gitleaks-3.webp&quot; alt=&quot;变基操作界面&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;我们需要修改 &lt;code&gt;d9df113&lt;/code&gt;，因此将第一行的&quot;pick&quot;改成&quot;edit&quot;并保存，开始交互式变基；&lt;/li&gt;
&lt;li&gt;通过编辑器删除泄露的密钥；&lt;/li&gt;
&lt;li&gt;执行 &lt;code&gt;git add .&lt;/code&gt; 将更改提交到暂存区，执行 &lt;code&gt;git status&lt;/code&gt; 手动二次确认；&lt;/li&gt;
&lt;li&gt;确定修补完成后，执行 &lt;code&gt;git commit --amend&lt;/code&gt;，在这里可以修改 commit 内容，也可以不修改，但必须手动保存一次文件；&lt;/li&gt;
&lt;li&gt;这一轮修复就算成功了，执行 &lt;code&gt;git rebase --continue&lt;/code&gt;，git将开始自动合并，直到出现冲突。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;处理合并冲突&lt;/h3&gt;
&lt;p&gt;遇到合并冲突时，使用 &lt;code&gt;git status&lt;/code&gt; 查看，并打开冲突的文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dotenv&quot;&gt;❯ cat .env
&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
=======
API_KEY=yAxfgL1GnYhk0UYXZdt2eADqnrNXezeK
TIME_ZONE=Asia/Shanghai
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; 72fd120 (feat: 又实现了一个功能)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些格式的意思是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从 &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD&lt;/code&gt; 到 &lt;code&gt;=======&lt;/code&gt; 的部分：当前分支的内容（也就是修改后的）&lt;/li&gt;
&lt;li&gt;从 &lt;code&gt;=======&lt;/code&gt; 到 &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; 72fd120&lt;/code&gt; 的部分：旧分支的内容（修改前）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此处需要删除API_KEY，因此将文件修改成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dotenv&quot;&gt;TIME_ZONE=Asia/Shanghai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，&lt;code&gt;git add .&lt;/code&gt;，&lt;code&gt;git commit --amend&lt;/code&gt;，&lt;code&gt;git rebase --continue&lt;/code&gt;。&lt;strong&gt;如此循环，直到变基全部完成&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;最终效果&lt;/h3&gt;
&lt;p&gt;再次运行 &lt;code&gt;gitleaks git . -v&lt;/code&gt;，就可以看到一片绿啦~~
&lt;img src=&quot;https://rinx.nz/assets/images/gitleaks-4.webp&quot; alt=&quot;修复完成&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后 force push 就可以强制修改远端内容：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git push --force --all
git push --force --tags
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;git filter-branch 方法&lt;/h2&gt;
&lt;h3&gt;将某个文件从所有历史中移除&lt;/h3&gt;
&lt;p&gt;先筛选并执行移除：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git filter-branch --force --index-filter \
&apos;git rm --cached --ignore-unmatch &amp;lt;文件路径&amp;gt;&apos; \
--prune-empty --tag-name-filter cat -- --all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再清理备份引用，执行垃圾回收：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now --aggressive
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;将文件中的某一部分从所有历史中移除&lt;/h3&gt;
&lt;p&gt;在这里使用&lt;code&gt;sed&lt;/code&gt;作为示例，可以让AI编写适合你的替换命令：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git filter-branch --force --tree-filter &apos;
grep -rl &quot;LEAKED_KEY_PART&quot; . | while read -r f; do
  sed -i &quot;s/LEAKED_KEY_PART/REDACTED/g&quot; &quot;$f&quot;
done
&apos; --tag-name-filter cat -- --all
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
这会在每个提交 checkout 一次，仓库大时将非常慢。
替换模式尽量写具体，避免误伤正常内容。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后同样是 force push：
然后 force push 就可以强制修改远端内容：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git push --force --all
git push --force --tags
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>[STM32] EXTI 外部中断</title><link>https://rinx.nz/posts/stm32-exti/</link><guid isPermaLink="true">https://rinx.nz/posts/stm32-exti/</guid><description>关于外部中断与状态机设计</description><pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：声明&lt;/strong&gt;
该文档没有使用任何AIGC内容，主要参考了STM32F10xxx的参考手册。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;硬件结构&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://rinx.nz/assets/images/stm32-exti_block.webp&quot; alt=&quot;外部中断框图&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;外设配置&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;GPIO：使用IPU模式即可&lt;/li&gt;
&lt;li&gt;AFIO：选择连接到中断线的具体引脚&lt;/li&gt;
&lt;li&gt;EXTI：配置触发方式&lt;/li&gt;
&lt;li&gt;NVIC：配置中断优先级&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;中断函数触发流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;由输入线经过边沿检测电路，依据EXTI的配置输出对应的触发信号&lt;/li&gt;
&lt;li&gt;进入请求挂起寄存器, 将对应位置1 (处理完中断后需要手动清除)&lt;/li&gt;
&lt;li&gt;若配置了NVIC对应的中断函数，则中断屏蔽寄存器对应位将为1, 信号将送至NVIC中断控制器&lt;/li&gt;
&lt;li&gt;NVIC中断控制器将根据优先级来自动处理函数调用&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;软件实现&lt;/h1&gt;
&lt;h2&gt;优先级分组&lt;/h2&gt;
&lt;p&gt;NVIC 将中断的优先级分为“抢占优先级”与“响应优先级”两种，中断的优先级通过 4bit 的寄存器来存储。
通过设置优先级分组，可以划分这两种优先级使用的位数。分组编号就对应了抢占位。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;下面的表格中，“抢占位”指抢占式优先级使用的位数，“抢占级数”指最多有几个不同的抢占优先级&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;分组&lt;/th&gt;
&lt;th&gt;抢占位&lt;/th&gt;
&lt;th&gt;响应位&lt;/th&gt;
&lt;th&gt;抢占级数&lt;/th&gt;
&lt;th&gt;响应级数&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;组0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;没有中断嵌套&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;组1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;组2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;通用&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;组3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;组4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;复杂的中断嵌套&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;基于标准库的代码模板&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void EXTI_Config(){
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 配置 PA1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &amp;amp;GPIO_InitStructure);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    // Line1 中断线使用来自 GPIOA 的信号
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);

    EXTI_InitStructure.EXTI_Line    = EXTI_Line1;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;  // EXTI 中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 按下时触发
    EXTI_Init(&amp;amp;EXTI_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级
    NVIC_Init(&amp;amp;NVIC_InitStructure);
}

// EXTI_Line1 的中断处理函数，可从 startup_stm32xxx.s 中查到
void EXTI1_IRQHandler(){
    // 避免错误进入中断
    if(EXTI_GetITStatus(EXTI_Line1) != RESET){
        // 处理中断, 不要在这儿写复杂的运算或是Delay

        // 清除请求挂起寄存器对应位
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

int main(void){
  // 务必配置中断优先级分组
  NVIC_SetPriorityGrouping(2);
  EXTI_Config();

  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;关于状态机&lt;/h2&gt;
&lt;p&gt;可以声明一个全局 state 变量，仅在中断函数处理状态转移的逻辑，在main函数中编写具体的动作&lt;/p&gt;
</content:encoded></item><item><title>一些可能有用的资源</title><link>https://rinx.nz/posts/resource/</link><guid isPermaLink="true">https://rinx.nz/posts/resource/</guid><description>啥都有</description><pubDate>Mon, 29 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;工具网站类&lt;/h1&gt;
&lt;h2&gt;下载解析&lt;/h2&gt;
&lt;h3&gt;闪链&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能：解析&lt;strong&gt;百度网盘&lt;/strong&gt;和&lt;strong&gt;夸克&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;官网链接：&lt;a href=&quot;https://mf.dp.wpurl.cc/user/parse&quot;&gt;https://mf.dp.wpurl.cc/user/parse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;记录于 2025/12/29&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结合 &lt;a href=&quot;https://motrix.app/&quot;&gt;Motrix&lt;/a&gt; 等多线程下载器可以获得更好的体验。&lt;/p&gt;
&lt;h2&gt;镜像站&lt;/h2&gt;
&lt;h3&gt;GitHub Proxy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;发布站链接：&lt;a href=&quot;https://ghproxy.link/&quot;&gt;https://ghproxy.link/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;目前可用链接：&lt;a href=&quot;https://ghfast.top/&quot;&gt;https://ghfast.top&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;记录于 2025/12/21&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;工具软件类&lt;/h1&gt;
&lt;h2&gt;Android&lt;/h2&gt;
&lt;h3&gt;MacroDroid&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一款功能强大的&lt;strong&gt;任务自动化&lt;/strong&gt;和配置应用程序&lt;/li&gt;
&lt;li&gt;破解版链接 (果核)：&lt;a href=&quot;https://www.ghxi.com/macrodroid.html&quot;&gt;https://www.ghxi.com/macrodroid.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;记录于 2025/12/30&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[STM32] TIM 定时器</title><link>https://rinx.nz/posts/stm32-tim/</link><guid isPermaLink="true">https://rinx.nz/posts/stm32-tim/</guid><description>较为系统的记录了通用定时器的硬件组成与运行逻辑</description><pubDate>Wed, 24 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：声明&lt;/strong&gt;
该文档没有使用任何AIGC内容，主要参考了STM32F10xxx的参考手册。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;硬件结构&lt;/h1&gt;
&lt;h2&gt;核心：时基单元&lt;/h2&gt;
&lt;p&gt;该部分是实现&quot;定时&quot;功能的基础，虽然名字是&quot;时基&quot;，但应该被看作是一个&lt;strong&gt;计数器&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;其输入是来自触发控制器的时钟信号，可暂时先视作系统时钟信号。&lt;/p&gt;
&lt;h3&gt;硬件组成&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;组件&lt;/th&gt;
&lt;th&gt;运行逻辑&lt;/th&gt;
&lt;th&gt;相关组件&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;16位预分频器 &lt;strong&gt;PSC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;遇到上升沿时输出一个脉冲给CNT，然后等待 N 个脉冲 (不输出)&lt;/td&gt;
&lt;td&gt;对应的影子寄存器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16位计数器 &lt;strong&gt;CNT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每遇到一个上升沿，寄存器就执行一次计数 (有多种模式)&lt;/td&gt;
&lt;td&gt;&quot;触发控制器&quot;的CNT控制器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16位自动重装载寄存器 &lt;strong&gt;ARR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;根据向上/向下/中央对齐模式操作CNT，并产生更新事件与更新中断&lt;/td&gt;
&lt;td&gt;对应的影子寄存器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;例如，有一个 72MHz 的时钟信号作为输入。若设置为向上计数，PSC = (7200-1)，ARR = (10000-1)。则预分频器会输出&lt;code&gt;\frac{72\text{MHz}}{7200} = 10000\text{Hz}&lt;/code&gt; 的信号，又由于ARR会在 CNT==(10000-1) 的时候产生更新事件，所以最终的效果就是每秒产生一次更新。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：关于中断和事件&lt;/strong&gt;
在此处，更新事件只会影响 CNT 与硬件电路（如 TRGO），而各个中断可以配置 NVIC 或 DMA 来处理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;影子寄存器&lt;/strong&gt;(预加载寄存器)：在定时器运行的过程中，可能需要对PSC或ARR做修改，而计数器仍在运行，这可能会诱发不可预料的错误。而影子寄存器可以先记录要修改的目标值，在&lt;strong&gt;下一次触发更新事件&lt;/strong&gt;时，再将修改同步到实际寄存器&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：影子寄存器需要手动激活&lt;/strong&gt;
例如ARR的影子寄存器，需要使用&lt;code&gt;TIM_ARRPreloadConfig()&lt;/code&gt;函数使能&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;向上计数模式&lt;/h3&gt;
&lt;p&gt;计数器从 0 开始向上计数到 ARR，然后产生一个更新事件并将 CNT 置 0&lt;/p&gt;
&lt;h3&gt;向下计数模式&lt;/h3&gt;
&lt;p&gt;计数器从 ARR 开始向下计数到 0，然后产生一个更新事件并将 CNT 置 ARR&lt;/p&gt;
&lt;h3&gt;中央对齐模式&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;ToDo&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;通用定时器 的结构与运行逻辑&lt;/h2&gt;
&lt;p&gt;对于通用定时器，应该将其视为一个“&lt;strong&gt;可编程的通用计数器&lt;/strong&gt;”。其主要结构如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://rinx.nz/assets/images/stm32-tim-general-purpose_timer_block.webp&quot; alt=&quot;通用定时器框图 (英文版)&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;触发器信号输入部分&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;内部时钟 &lt;strong&gt;CK_INT&lt;/strong&gt;：来自RCC的 TIMxCLK&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ETRF&lt;/strong&gt; (处理后的外部时钟信号)：由 ETR (外部触发引脚) 的信号经过 极性选择、边沿检测、预分频 (Prescaler) 得到 &lt;strong&gt;ETRP&lt;/strong&gt;，再经过滤波得到 ETRF，发送到&lt;strong&gt;触发控制器&lt;/strong&gt;用于外部时钟模式2（&lt;strong&gt;直接输入时基单元&lt;/strong&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ITR&lt;/strong&gt; (Internal Trigger 内部触发信号)：由四路 &lt;strong&gt;ITRx&lt;/strong&gt; 信号经过 MUX (多路复用器)得到&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TRC&lt;/strong&gt; (Timer Remote Control 定时器遥控信号)：由 ITR 与 TI1F_ED (TI1信号经过滤波和双边沿检测，常用于 三相无刷直流电机) 经过 MUX 得到&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TRGI&lt;/strong&gt; (Trigger Input 触发器输入信号)：由 ETRF、TRC、TI1FP1、TI2FP2 经过 触发选择器(&lt;strong&gt;TS&lt;/strong&gt;) 得到，作为从模式的输入信号&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;触发控制器&lt;/h3&gt;
&lt;p&gt;作为整个定时器的&lt;strong&gt;信号路由&lt;/strong&gt;，负责处理输入与输出，并控制CNT的工作。可以选择以下时钟源：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内部时钟模式&lt;/strong&gt;：（&lt;strong&gt;默认状态&lt;/strong&gt;）使用系统时钟，&lt;strong&gt;CK_INT&lt;/strong&gt; 信号。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外部时钟模式2&lt;/strong&gt;：直接使用 &lt;strong&gt;ETRF&lt;/strong&gt; 信号，注意&lt;strong&gt;不是从 TRGI 得到的&lt;/strong&gt;信号。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所选的时钟源的信号将被&lt;strong&gt;直接发送到CK_PSC&lt;/strong&gt;，而不经过从模式控制器。这意味着它可以&lt;strong&gt;与从模式中的复位、门控、触发模式一起使用&lt;/strong&gt;，这也是两种外部时钟模式的区别。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：时钟源的配置&lt;/strong&gt;
初始化定时器时，默认会使用内部时钟作为时钟源。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;触发控制器&lt;strong&gt;可以同时开启&lt;/strong&gt;两种模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;从模式&lt;/strong&gt; (Slave controller mode)：&lt;strong&gt;接收&lt;/strong&gt;外部信号 &lt;strong&gt;TRGI&lt;/strong&gt;，控制CNT&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;外部时钟模式1&lt;/strong&gt;：来自&lt;strong&gt;TRGI&lt;/strong&gt;的信号将被发送到 &lt;strong&gt;CK_PSC&lt;/strong&gt;，作为时基单元的预分频器的输入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;复位模式&lt;/strong&gt;：&lt;strong&gt;每次&lt;/strong&gt;收到信号，CNT将被&lt;strong&gt;清零&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;门控模式&lt;/strong&gt;：只有在&lt;strong&gt;收到信号时&lt;/strong&gt;，CNT才会&lt;strong&gt;计数&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;触发模式&lt;/strong&gt;：收到信号，CNT将被&lt;strong&gt;使能&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编码器模式&lt;/strong&gt;：这是一种特殊的从模式，开启后从模式控制器将自动控制 CNT 进行自增或自减。相当于使用了一个带有方向选择的外部时钟。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;编码器模式1&lt;/strong&gt;：2倍频。只在 A 相跳变时计数，看 B 相电平判方向&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编码器模式2&lt;/strong&gt;：2倍频。只在 B 相跳变时计数，看 A 相电平判方向&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编码器模式3&lt;/strong&gt;：4倍频 (&lt;strong&gt;最常用&lt;/strong&gt;)。AB 两相跳变时都计数，看另一相电平判方向，精度最高&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：关于编码器模式下的ARR&lt;/strong&gt;
通常将ARR设置为65535，这样就可以利用补码的特性来很好的处理正反转
例如，编码器接口收到了两个反转信号，CNT 变为65534。又收到两个正转信号，CNT 先加到65535，再加一次就是0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主模式&lt;/strong&gt; (Master controller mode)：将内部的事件&lt;strong&gt;输出&lt;/strong&gt;为外部信号 &lt;strong&gt;TRGO&lt;/strong&gt;（发往其它定时器实现级联，或发往 DAC/ADC）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;复位&lt;/strong&gt;：复位事件发生时，发送一个正脉冲&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使能&lt;/strong&gt;：将 &lt;strong&gt;CNT_EN&lt;/strong&gt; 作为 TRGO，只要计数器在运行就是高电平&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：当更新事件发生时，发送一个正脉冲&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;比较脉冲&lt;/strong&gt;：在输入通道1 (&lt;strong&gt;IC1&lt;/strong&gt;) 发生一次捕获或一次比较成功时，将要设置 &lt;strong&gt;CC1IF&lt;/strong&gt; 标志时，发送一个正脉冲&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;四种比较模式&lt;/strong&gt;：对应四个通道 &lt;strong&gt;ICx&lt;/strong&gt;，将 &lt;strong&gt;OCxREF&lt;/strong&gt; (&lt;strong&gt;Output Compare x Reference Signal&lt;/strong&gt; 输出比较 x 参考信号) 作为 TRGO&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;输入/输出部分&lt;/h3&gt;
&lt;p&gt;该部分有四路，每路有一个输入捕获单元和一个输出比较单元（只能选择一个开启）。该部分的核心是 &lt;strong&gt;CCRx&lt;/strong&gt;（捕获/比较寄存器），输入与输出都要操作该寄存器。
&lt;strong&gt;CCRx&lt;/strong&gt; 有对应的影子寄存器（预装载寄存器），可配置开启。&lt;/p&gt;
&lt;h3&gt;输入捕获单元&lt;/h3&gt;
&lt;p&gt;以第一路通道举例。信号从 TIMx_CH1 外部引脚输入，经过滤波和边沿检测得到 &lt;strong&gt;TI1&lt;/strong&gt;，之后分为两路。&lt;strong&gt;TI1FP1&lt;/strong&gt; 通往 &lt;strong&gt;IC1&lt;/strong&gt; 的 MUX，&lt;strong&gt;TI1FP2&lt;/strong&gt; 通往 &lt;strong&gt;IC2&lt;/strong&gt; 的 MUX；经过 &lt;strong&gt;预分频&lt;/strong&gt; 后得到触发信号，触发时 &lt;strong&gt;CNT 将被复制到 CCR1&lt;/strong&gt; (捕获/比较寄存器)。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：关于编码器模式&lt;/strong&gt;
而在编码器模式中，信号&lt;strong&gt;不经过边沿检测器&lt;/strong&gt;就直接进入了编码器接口，因此 TIM_ICPolarity 在这里指的是 &lt;strong&gt;是否要对输入信号做反相处理&lt;/strong&gt;。如果发现编码器接口中得到的数值与实际正好相反，就可以改变某一个 IC 的 TIM_ICPolarity，省去了换线的麻烦。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;输出比较单元&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;对于 OCx&lt;/strong&gt; (OC1 ~ OC4)，它会 &lt;strong&gt;根据 CNT、CCRx 与设定的输出比较模式&lt;/strong&gt;，将结果输出到 &lt;strong&gt;OCxREF&lt;/strong&gt;，并触发 &lt;strong&gt;CCxI&lt;/strong&gt; 中断，若触发该中断时，中断标志位非0 (即之前的中断未被处理完)，会将 &lt;strong&gt;CC1OF&lt;/strong&gt; 置1（表示系统未能及时处理捕获信号）&lt;/p&gt;
&lt;p&gt;有下面几种&lt;strong&gt;输出比较模式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;冻结&lt;/strong&gt;：REF 保持原状态&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;匹配时置有效电平&lt;/strong&gt;：CNT==CCRx 时，REF 置有效电平（不适合输出连续信号）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：有效电平&lt;/strong&gt;
在通用计时器中，有效电平即为高电平。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;匹配时置无效电平&lt;/strong&gt;：CNT==CCRx 时，REF 置无效电平（不适合输出连续信号）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;匹配时电平翻转&lt;/strong&gt;：CNT==CCRx 时，REF 电平翻转&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：输出一个占空比始终为50%的波形&lt;/strong&gt;
设置CNT=0，定时器就会在每次更新时翻转电平，因此周期为ARR的两倍，&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;强制为无效电平&lt;/strong&gt;：REF 强制为无效电平&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;强制为有效电平&lt;/strong&gt;：REF 强制为有效电平&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PWM模式1&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;向上计数&lt;/strong&gt;：&lt;code&gt;\text{CNT} &amp;lt; \text{CCRx}&lt;/code&gt; 时，REF 置有效电平；&lt;code&gt;\text{CNT} \geq \text{CCRx}&lt;/code&gt; 时， REF 置无效电平&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;向下计数&lt;/strong&gt;：&lt;code&gt;\text{CNT} \leq \text{CCRx}&lt;/code&gt; 时，REF 置有效电平；&lt;code&gt;\text{CNT} &amp;gt; \text{CCRx}&lt;/code&gt; 时， REF 置无效电平&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PWM模式2&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;向上计数&lt;/strong&gt;：&lt;code&gt;\text{CNT} &amp;lt; \text{CCRx}&lt;/code&gt; 时，REF 置有效电平；&lt;code&gt;\text{CNT} \geq \text{CCRx}&lt;/code&gt; 时， REF 置无效电平&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;向下计数&lt;/strong&gt;：&lt;code&gt;\text{CNT} \leq \text{CCRx}&lt;/code&gt; 时，REF 置有效电平；&lt;code&gt;\text{CNT} &amp;gt; \text{CCRx}&lt;/code&gt; 时， REF 置无效电平&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：关于PWM模式&lt;/strong&gt;
模式2 与 模式1 的输出完全反相；一般较少使用向下计数。对于模式1的向上计数，有如下参数：
PWM频率：　　Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比：　Duty = CCR / (ARR + 1)
PWM分辨率：　Reso = 1 / (ARR + 1)，也就是占空比最小变化步距，越小越好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：快速使能模式&lt;/strong&gt;
&lt;strong&gt;仅当开启从模式并由外部信号触发，且使用PWM输出时&lt;/strong&gt;，该模式才有用。
开启该模式后，会跳过部分同步逻辑，缩短信号的输出延迟（牺牲微小的逻辑同步稳定性）
使用&lt;code&gt;TIM_OCxFastConfig&lt;/code&gt;开启&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;重要：注意&lt;/strong&gt;
输入捕获 和 输出比较 &lt;strong&gt;不能同时开启&lt;/strong&gt;，因为它们共用 CCRx&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;输出控制&lt;/h3&gt;
&lt;p&gt;负责将 REF 转化为 GPIO 的输出&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;极性选择&lt;/strong&gt;：设置&lt;strong&gt;有效电平&lt;/strong&gt;极性（为高电平或低电平），参数为  &lt;code&gt;TIM_OCPolarity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输出使能&lt;/strong&gt;：参数为 &lt;code&gt;TIM_OutputState&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;基本定时器 的结构与运行逻辑&lt;/h2&gt;
&lt;p&gt;基本定时器由一个最简单的触发控制器和一个时基单元组成，大概只能处理简单的定时任务。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://rinx.nz/assets/images/stm32-tim-basic_timer_block.webp&quot; alt=&quot;基本定时器框图 (英文版)&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;高级定时器 的结构与运行逻辑&lt;/h2&gt;
&lt;p&gt;相比通用定时器，其在时基单元后连接了 &lt;strong&gt;重复次数计数器&lt;/strong&gt;；在输出部分增加了 &lt;strong&gt;DTG 死区生成电路&lt;/strong&gt;；输出引脚变为了两个&lt;strong&gt;互补的输出&lt;/strong&gt;，可用来控制三相无刷电机；接入了&lt;strong&gt;刹车输入&lt;/strong&gt;与&lt;strong&gt;时钟安全系统&lt;/strong&gt;，用于在意外情况切断输出。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ToDo&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h1&gt;基于标准库的代码&lt;/h1&gt;
&lt;h2&gt;初始化一个最简单的通用定时器&lt;/h2&gt;
&lt;p&gt;以下代码用于初始化使用&lt;strong&gt;内部时钟&lt;/strong&gt;的，每秒触发一次中断的TIM2。&lt;/p&gt;
&lt;p&gt;流程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化 TIM2 时钟&lt;/li&gt;
&lt;li&gt;初始化 TIM2&lt;/li&gt;
&lt;li&gt;打开 TIM2 的更新中断&lt;/li&gt;
&lt;li&gt;配置 NVIC 处理更新中断&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void TIM_Config(){
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    TIM_InternalClockConfig(TIM2);                                    // 配置TIM2使用内部时钟
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;       // 对定时器时钟的分频操作
    TIM_TimeBaseInitStructure.TIM_CounterMode   = TIM_CounterMode_Up; // 向上计数
    TIM_TimeBaseInitStructure.TIM_Prescaler     = 0;                  // 预分频器
    TIM_TimeBaseInitStructure.TIM_Period        = 10 - 1;             // 自动重装载寄存器的值
    TIM_TimeBaseInit(TIM2, &amp;amp;TIM_TimeBaseInitStructure);

    TIM_ClearFlag(TIM2, TIM_IT_Update);  // 避免启用后立刻触发的中断
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&amp;amp;NVIC_InitStructure);

    TIM_Cmd(TIM2, ENABLE);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：注意设置NVIC优先级分组&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;初始化一个通用定时器，用于输出PWM&lt;/h2&gt;
&lt;p&gt;该示例可用于驱动SG90舵机（让PA1输出周期为20ms的PWM）&lt;/p&gt;
&lt;p&gt;流程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开启 GPIO 与 TIM 的时钟&lt;/li&gt;
&lt;li&gt;配置时基单元&lt;/li&gt;
&lt;li&gt;配置对应通道的输出比较单元&lt;/li&gt;
&lt;li&gt;初始化 GPIO&lt;/li&gt;
&lt;li&gt;开启 TIM&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void PWM_Config(){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // 72MHz * 0.020 = 72*20000
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;       // 配置时钟分频，此处设置为不分频
    TIM_TimeBaseInitStructure.TIM_CounterMode   = TIM_CounterMode_Up; // 向上计数
    TIM_TimeBaseInitStructure.TIM_Prescaler     = 72-1;               // 预分频 (PSC)
    TIM_TimeBaseInitStructure.TIM_Period        = 20000-1;            // 周期   (ARR)
    TIM_TimeBaseInit(TIM2, &amp;amp;TIM_TimeBaseInitStructure);

    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode      = TIM_OCMode_PWM1;        // PWM1模式
    TIM_OCInitStructure.TIM_OCPolarity  = TIM_OCPolarity_High;    // 有效电平为高电平
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 输出使能
    TIM_OCInitStructure.TIM_Pulse       = 1500;                   // CCR初始值为1500 (高电平宽度 1.5ms)
    TIM_OC2Init(TIM2, &amp;amp;TIM_OCInitStructure);                      // 配置通道2 (输出比较单元2)

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &amp;amp;GPIO_InitStructure);

    TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); // 开启CCR的影子寄存器
    TIM_ARRPreloadConfig(TIM2, ENABLE);               // 开启ARR的影子寄存器

    TIM_Cmd(TIM2, ENABLE);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;初始化一个通用定时器，用测周法测量 PWM&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;测周法：在两个上升沿内，以标准频率 &lt;code&gt;f_c&lt;/code&gt; 记次，得到 N，则频率 &lt;code&gt;f_x = \frac{f_c}{N}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;该示例中，&lt;code&gt;f_c = \frac{72\text{MHz}}{72} = 1\text{MHz}&lt;/code&gt;。
每个上升沿，IC1 都会将 CNT 复制到 CCR1。随即从模式控制器将 CNT 复位。因此只需要读取CCR1，就可以得到最新的N。
周期值（注意单位换算）：&lt;code&gt;TIM_GetCapture1(TIM3)+1&lt;/code&gt;
频率值：&lt;code&gt;(1000000)/(TIM_GetCapture1(TIM3)+1)&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;警告：注意核算可测量的最小频率值&lt;/strong&gt;
此处的最小频率为 &lt;code&gt;\frac{1000000}{65535} \approx 15.26\text{Hz}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同理，将 TI1FP2 信号输入 IC2，并在每个下降沿将 CNT 复制到 CCR2，就可以得到高电平在一个周期中的占比，通过计算可以得到占空比（此处计算结果为千分值） &lt;code&gt;(TIM_GetCapture2(TIM3)+1)*1000/(TIM_GetCapture1(TIM3)+1)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;流程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开启 GPIO 与 TIM 的时钟&lt;/li&gt;
&lt;li&gt;配置时基单元&lt;/li&gt;
&lt;li&gt;配置输入捕获单元&lt;/li&gt;
&lt;li&gt;选择从模式-复位模式&lt;/li&gt;
&lt;li&gt;选择输入触发信号的来源 (TRGI)&lt;/li&gt;
&lt;li&gt;初始化 GPIO&lt;/li&gt;
&lt;li&gt;开启 TIM&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void PWMInput_Config(){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPD; // 注意此处使用了下拉输入
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &amp;amp;GPIO_InitStructure);

    // 72MHz = 72*1000000 (1s)
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;       // 配置时钟分频，此处设置为不分频
    TIM_TimeBaseInitStructure.TIM_CounterMode   = TIM_CounterMode_Up; // 向上计数
    TIM_TimeBaseInitStructure.TIM_Prescaler     = 72-1;             // 预分频 (PSC)
    TIM_TimeBaseInitStructure.TIM_Period        = 65536-1;            // 周期   (ARR)
    TIM_TimeBaseInit(TIM3, &amp;amp;TIM_TimeBaseInitStructure);

    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;                // 配置IC1
    TIM_ICInitStructure.TIM_ICFilter = 0xF;                         // 不启用滤波
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;     // 上升沿触发
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;           // 不分频
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // TI1 连接到 IC1（直连）
    TIM_ICInit(TIM3, &amp;amp;TIM_ICInitStructure);

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
    TIM_ICInitStructure.TIM_ICFilter = 0xF;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
    TIM_ICInit(TIM3, &amp;amp;TIM_ICInitStructure);

    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); // 选择从模式-复位模式
    TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);    // 选择 TI1FP1 作为 TRGI

    TIM_ARRPreloadConfig(TIM3, ENABLE);

    TIM_Cmd(TIM3, ENABLE);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;初始化一个通用定时器，用标准库的PWMI模式测PWM&lt;/h2&gt;
&lt;p&gt;PWMI模式&lt;strong&gt;在硬件层面是不存在的&lt;/strong&gt;，是标准库为了简化程序而引入的函数。如果使用它，则上节代码中配置输入捕获的部分可以改为：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICFilter = 0xF;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_PWMIConfig(TIM3, &amp;amp;TIM_ICInitStructure);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：关于 &lt;code&gt;TIM_PWMIConfig()&lt;/code&gt;&lt;/strong&gt;
只需要传入一个通道的初始化结构体，函数会自动将另一个通道设置成对应的配置。&lt;/p&gt;
&lt;p&gt;例如：传入 &lt;strong&gt;通道1，直连，上升沿&lt;/strong&gt;。函数就会自动配置 &lt;strong&gt;通道2，交叉，下降沿&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;但注意&lt;strong&gt;不能传入通道3或通道4&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;初始化一个通用定时器，读取编码器&lt;/h2&gt;
&lt;p&gt;流程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开启 GPIO 与 TIM 的时钟&lt;/li&gt;
&lt;li&gt;初始化 GPIO 为输入模式&lt;/li&gt;
&lt;li&gt;配置时基单元（不分频）&lt;/li&gt;
&lt;li&gt;配置输入捕获单元&lt;/li&gt;
&lt;li&gt;配置编码器接口模式&lt;/li&gt;
&lt;li&gt;开启 TIM&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void EncoderInput_Config(){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU; // 注意此处使用了上拉输入
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &amp;amp;GPIO_InitStructure);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseStructInit(&amp;amp;TIM_TimeBaseInitStructure);
    TIM_TimeBaseInitStructure.TIM_Prescaler     = 0;       // 预分频 (PSC)
    TIM_TimeBaseInitStructure.TIM_Period        = 0xFFFF;  // 周期   (ARR)
    TIM_TimeBaseInit(TIM3, &amp;amp;TIM_TimeBaseInitStructure);

    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICStructInit(&amp;amp;TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICFilter = 0xF;   // 滤波拉满
    TIM_ICInit(TIM3, &amp;amp;TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
    TIM_ICInitStructure.TIM_ICFilter = 0xF;   // 滤波拉满
    TIM_ICInit(TIM3, &amp;amp;TIM_ICInitStructure);

    // 开启TIM3的编码器接口，模式3，两个通道均不反相
    TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

    TIM_Cmd(TIM3, ENABLE);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示：如何测速&lt;/strong&gt;
由于硬件限制，是无法做到硬件层面的测速的。最佳的方法就是再配置一个定时器，定时去读取编码器测量的结果，并计算速度。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Gemini-CLI 登录失败的解决方案以及使用技巧</title><link>https://rinx.nz/posts/gemini-cli/</link><guid isPermaLink="true">https://rinx.nz/posts/gemini-cli/</guid><description>Powershell的设置给我坑惨了</description><pubDate>Tue, 29 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;代理软件的设置&lt;/h2&gt;
&lt;p&gt;开启系统代理，开启TUN模式，确保所使用的节点能正常访问 &lt;a href=&quot;https://gemini.google.com/app&quot;&gt;Gemini&lt;/a&gt; （注意部分送中节点可能无法解锁Gemini）&lt;/p&gt;
&lt;p&gt;开启PowerShell，推荐使用&lt;a href=&quot;https://apps.microsoft.com/detail/9n8g5rfz9xk3?hl=zh-CN&amp;amp;gl=CN&quot;&gt;终端预览&lt;/a&gt;，cd到项目目录&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;$env:https_proxy=&quot;http://127.0.0.1:7897&quot;
gemini
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
请根据你代理软件的设置，修改上述代码中的端口&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;使用技巧&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;注意 Gemini-CLI 不会主动读取文件的更新，因此在手动修改代码后，需要在下一次交互时提醒Gemini文件已经更新&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>Mindows 在低电量时自动切换到 Android 系统</title><link>https://rinx.nz/posts/mindows-low_battery-auto_switch/</link><guid isPermaLink="true">https://rinx.nz/posts/mindows-low_battery-auto_switch/</guid><description>解决在windows系统下意外关机·并且充电功率极低的 尴尬场景</description><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前景提要&lt;/h2&gt;
&lt;p&gt;阴差阳错地购买了 小米平板5，又阴差阳错的发现了 WOA 和 Mindows，于是有了接下来的故事。&lt;/p&gt;
&lt;p&gt;经过一番折腾，编写了一个bat脚本，成功解决了 在windows系统下意外关机·并且充电功率极低的 尴尬场景。虽然是个小东西，但苦于找不到资料，也并不算轻松&lt;/p&gt;
&lt;h2&gt;功能&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;检测到电量较低时，发出警告通知&lt;/li&gt;
&lt;li&gt;检测到电量过低时，发出警告并在一分钟后自动重启到 Android 系统&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bat&quot;&gt;@echo off
:loop
for /f &quot;skip=1 tokens=2 delims==&quot; %%i in (&apos;wmic Path Win32_Battery Get EstimatedChargeRemaining /value&apos;) do set batteryLevel=%%i

if %batteryLevel% leq 10 (
    start /min powershell -NoProfile -NonInteractive -WindowStyle Hidden -Command &quot;[Reflection.Assembly]::LoadWithPartialName(&apos;System.Windows.Forms&apos;);$obj=New-Object Windows.Forms.NotifyIcon;$obj.Icon = [drawing.icon]::ExtractAssociatedIcon($PSHOME + &apos;\powershell.exe&apos;);$obj.Visible = $True;$obj.ShowBalloonTip(60000, &apos;Mindows 低电量警告&apos;,&apos;电量过低！将在 1 分钟后 强制 切换到Android！&apos;,3)&quot;
    timeout /t 75
    :reboot
    call &quot;c:\\Mindows工具包\\Mindows一键切换\\Mindows一键切换.bat&quot;
    
    timeout /t 20
    start /min powershell -NoProfile -NonInteractive -WindowStyle Hidden -Command &quot;[Reflection.Assembly]::LoadWithPartialName(&apos;System.Windows.Forms&apos;);$obj=New-Object Windows.Forms.NotifyIcon;$obj.Icon = [drawing.icon]::ExtractAssociatedIcon($PSHOME + &apos;\powershell.exe&apos;);$obj.Visible = $True;$obj.ShowBalloonTip(60000, &apos;Mindows 系统切换 失败！&apos;,&apos;脚本运行失败，将在三十秒内重试&apos;,3)&quot;
    timeout /t 45
    goto reboot
) else if %batteryLevel% leq 20 (
    start /min powershell -NoProfile -NonInteractive -WindowStyle Hidden -Command &quot;[Reflection.Assembly]::LoadWithPartialName(&apos;System.Windows.Forms&apos;);$obj=New-Object Windows.Forms.NotifyIcon;$obj.Icon = [drawing.icon]::ExtractAssociatedIcon($PSHOME + &apos;\powershell.exe&apos;);$obj.Visible = $True;$obj.ShowBalloonTip(200000, &apos;Mindows 低电量警告&apos;,&apos;电量低！请保存数据并准备切换到 Android！&apos;,2)&quot;
    timeout /t 300
    goto loop
)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
请根据实际情况替换程序中的路径 (指这行命令 &lt;code&gt;call &apos;C:\Mindows工具包\Mindows一键切换\Mindows一键切换.bat&apos;&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;重要&lt;/strong&gt;
&lt;strong&gt;请保存为 ANSI 编码&lt;/strong&gt;，如果使用记事本创建该脚本，则可以在 &lt;strong&gt;另存为&lt;/strong&gt; 处找到该功能&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;接下来，通过一个vbs来实现静默启动。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bat&quot;&gt;set ws=WScript.CreateObject(&quot;WScript.Shell&quot;)
ws.Run &quot;c:\Mindows工具包\RinStel-低电量警告.bat&quot;,0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;随后，将其移动到 &lt;code&gt;C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp&lt;/code&gt; (系统的自启动文件夹)，顺便双击运行测试。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;
请根据实际情况替换程序中的路径&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/junyidcf/article/details/122445851&quot;&gt;windows cmd balloon notification&lt;/a&gt;
&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.notifyicon.showballoontip?view=windowsdesktop-8.0&quot;&gt;NotifyIcon.ShowBalloonTip Method - microsoft&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/plasma007/article/details/128259287&quot;&gt;powershell实现发送win10系统通知&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>解决 MTP传输后中文文件名乱码 的问题</title><link>https://rinx.nz/posts/chinese-filename-garbled-after-mtp-transfer/</link><guid isPermaLink="true">https://rinx.nz/posts/chinese-filename-garbled-after-mtp-transfer/</guid><description>需要从 UTF-8 编码转换至 GBK</description><pubDate>Fri, 03 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;病症&lt;/h2&gt;
&lt;p&gt;文件自手机传输至电脑后，原本正常的文件名中的汉字会变成乱码&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ ls

101_澹板満澧炵泭.xml         103_閫氱敤褰遍煶浼樺寲.xml   105_浜哄０浼樺寲+澹板満澧炵泭_闊充箰.xml
102_浣庨煶+澹板満澧炵泭.xml  104_浜哄０浼樺寲_鐢靛奖.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解决方案&lt;/h2&gt;
&lt;p&gt;需要将文件名从 UTF-8 编码转换至 GBK&lt;/p&gt;
&lt;h3&gt;Linux&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ sudo apt-get install convmv
$ cd &amp;lt;TargetPath&amp;gt;
$ convmv -f UTF-8 -t GBK --notest *
mv &quot;./101_澹板満澧炵泭.xml&quot;     &quot;./101_声场增益.xml&quot;
mv &quot;./102_浣庨煶+澹板満澧炵泭.xml&quot;      &quot;./102_低音+声场增益.xml&quot;
mv &quot;./103_閫氱敤褰遍煶浼樺寲.xml&quot;       &quot;./103_通用影音优化.xml&quot;
mv &quot;./104_浜哄０浼樺寲_鐢靛奖.xml&quot;      &quot;./104_人声优化_电影.xml&quot;
mv &quot;./105_浜哄０浼樺寲+澹板満澧炵泭_闊充箰.xml&quot; &quot;./105_人声优化+声场增益_音乐.xml&quot;
Ready! I converted 5 files in 0 seconds.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Windows&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;# 切换到目标目录 (例如：cd C:\Your\Files)
$targetPath = Get-Location

Get-ChildItem -Path $targetPath | ForEach-Object {
    $originalName = $_.Name
    try {
        # 假设文件名是 UTF-8 编码，但被系统错误地显示为 ANSI/GBK
        # 1. 将 Unicode 字符串（Windows显示的文件名）编码成 UTF-8 字节数组
        $utf8Bytes = [System.Text.Encoding]::UTF8.GetBytes($originalName)

        # 2. 将 UTF-8 字节数组解码成 GBK 字符串
        #    注意：这里是为了生成一个GBK兼容的字符串，用于重命名。
        #    如果原始文件名是“正常的”Unicode，并且你只是想创建一个GBK版本的文件名，
        #    那么可能会有更好的方法。
        $gbkString = [System.Text.Encoding]::GetEncoding(&quot;GBK&quot;).GetString($utf8Bytes)

        # 3. 如果需要，可以进一步处理 $gbkString，例如替换一些特殊字符

        # 检查是否需要重命名
        if ($originalName -ne $gbkString) {
            $oldPath = $_.FullName
            $newPath = Join-Path $_.Directory.FullName $gbkString
            Write-Host &quot;尝试重命名: &apos;$originalName&apos; -&amp;gt; &apos;$gbkString&apos;&quot;
            Rename-Item -Path $oldPath -NewName $gbkString -ErrorAction Stop
            Write-Host &quot;成功重命名: &apos;$originalName&apos; -&amp;gt; &apos;$gbkString&apos;&quot;
        } else {
            Write-Host &quot;文件名 &apos;$originalName&apos; 无需转换或转换后相同。&quot;
        }
    }
    catch {
        Write-Warning &quot;处理文件名 &apos;$originalName&apos; 时出错: $_&quot;
    }
}

Write-Host &quot;文件名转换完成。&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>