<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Zeray Rice]]></title><description><![CDATA[is a ____________]]></description><link>http://b.zr.is/</link><generator>Ghost v0.4.1</generator><lastBuildDate>Sat, 26 Apr 2014 04:54:05 GMT</lastBuildDate><atom:link href="http://b.zr.is/rss/" rel="self" type="application/rss+xml"/><author><![CDATA[Zeray Rice]]></author><ttl>60</ttl><item><title><![CDATA[Arduino 的定时器中断]]></title><description><![CDATA[<p><img src='http://b.zr.is/content/images/2014/Mar/12386133923_47084a801d_k.jpg'  alt="img-post-cover" /></p>

<p>今天是拿到 Arduino 的第一天.. 研究了一晚上这玩意儿，基本上是读<a href='http://arduinodiy.wordpress.com/2012/02/28/timer-interrupts/' >这篇文章</a>学来的，重新把写的程序解释一遍，整理下思路。</p>

<pre><code class="prettyprint lang-c">#include &lt;avr/io.h&gt;
#include &lt;avr/interrupt.h&gt; 
#include "Arduino.h"

#define LED_PIN LED_BUILTIN

void setup() {  
    pinMode(LED_PIN, OUTPUT);

    // set timer interrupts
    cli();
</code></pre>

<p>解释一下 <code>cli()</code> ，以及下面的 <code>sei()</code>。</p>

<p>在 <code>Interrupt.h</code> 的注释中，可以看到这两个函数是直接被编译成汇编命令的，字面意思是启用或禁用定时器中断这功能，但是根据在<a href='http://forum.arduino.cc/index.php/topic,27057.0.html' >这个帖子</a>的讨论来看，作用应该是防止在设定定时器参数的过程中，其他的线程(?)来干扰设置，姑且我理解成了一种锁之类的东西(不确定对不对)。</p>

<pre><code class="prettyprint lang-c">    TCCR1A = 0;
    TCCR1B = 0;
</code></pre>

<p><code>TCCRxA</code> 和 <code>TCCRxB</code> 分别控制编号为 x 的定时器的选项。一般的 Arduino 会有 3 个定时器，不过据说一些 AVR 定时器会有额外的三个 16bit 的定时器。</p>

<p>顺便说下几个定时器的用处：</p>

<ul>
<li>0 号定时器是系统默认使用的定时器，<code>loop()</code> 和 <code>millis()</code> 都依赖这个定时器的运转。所以最好不要动 0 号定时器.. 除非你想干一些什么奇怪的事情。啊对了，这个定时器是 8bit 的，也就是说它的最大值是 255 (2^8 - 1)。</li>
<li>1 号定时器是 16bit 的，最大值就是 65535 了。这个定时器就是一般供开发者使用的了。</li>
<li>2 号定时器基本和 0 号定时器是差不多的，Arduino 的 <code>tone()</code> 函数依赖于这个定时器。</li>
</ul>

<pre><code class="prettyprint lang-c    ">    OCR1A = 15624;
</code></pre>

<p><code>OCR1A</code> 是 CTC 模式下的一个重要参数，不过要先说一下在没有启用 CTC 模式下的运行情况。</p>

<p>在 CTC 模式未启用的情况下，ISR (Interrupt Service Routine 就是处理各种中断事件的服务）只会对定时器触发一个溢出的中断事件。至于溢出，就是上面提到的 8bit 和 16bit 的问题了，每个时钟周期，这些寄存器的值会自增，当自增到满的时候 (255/65535) ，ISR 会触发一个 <code>TIMER1_OVF_vect</code> 的事件，不过，在频率为 16MHz 芯片的运行中，每时钟周期自增一，即使是 16bit 的寄存器，也仅仅需要约 0.0041 秒即可跑完一个定时器周期，触发一次溢出中断。这样的时间太短，远远无法满足现实需要。</p>

<p>为了解决上面的问题，于是引入了 <code>prescaler</code> 这玩意儿，效果就是感觉上像是放慢芯片的运行频率，导致一单位定时器周期变成了原有的 8/64/256/1024 倍(具体如何实现的暂时不明)，这样每定时器周期花费的时间就会变成原先的 8 倍，64 倍，256 倍，1024 倍。也就是说，一单位定时器周期最慢可以达到约 4.194 秒。</p>

<p>但是，在实际应用中，极少的情况下会需要用到 4.194 一秒的定时器，CTC 就是为了解决这个问题，获得更自由的定时器而被引入的。</p>

<p>CTC 的原理很简单，就是每时钟周期比较一下寄存器内的值，看是不是设定的那个值，如果是，则触发一次定时器中断。</p>

<p>但是，我们需要计算寄存器内的值是什么的时候才触发这个中断，计算公式如下：</p>

<blockquote>
  <p>(目标时间) = (定时器频率) * (定时器计数 + 1)</p>
</blockquote>

<p>目标时间：我们这里设置为 1 秒。</p>

<p>定时器频率：在 16 MHz 的芯片中，应该是 <code>1 / (16 * 10^6)</code>，但是在这里设置了 <code>prescaler</code> ，所以需要在此基础上再放慢 1024 倍，即为 <code>1 / (16 * 10^6 / 1024)</code> (6.4e-5 秒)。</p>

<p>所以结果很简单：</p>

<blockquote>
  <p>(定时器计数) = (目标时间) / (定时器频率) - 1</p>
  
  <p>15624 = 1 / 6.4e-5 - 1</p>
</blockquote>

<p>所以，在上面的代码里，我们把 <code>OCR1A</code> 设置为 15624 。</p>

<p>至于为什么要减去一，因为，在 CTC 发现达到预定的值的时候，会把寄存器重设为 0 。虽然在一般情况下，在这里多一少一十分之不明显，但是如果你在做什么十分重视时间的东西的情况下，这个一，就显得会很重要了。</p>

<pre><code class="prettyprint lang-c">    TCCR1B |= (1 &lt;&lt; WGM12);
    TCCR1B |= (1 &lt;&lt; CS10);
    TCCR1B |= (1 &lt;&lt; CS12);
    TIMSK1 = (1 &lt;&lt; OCIE1A);
</code></pre>

<p><code>WGM12</code> 表示设置为 CTC 模式运行。</p>

<p><code>CS10</code> 和 <code>CS12</code> 一起设置之后表示 <code>prescaler</code> 为 1024 倍。</p>

<p><code>TIMSKx</code> 似乎是控制了中断事件的触发情况，这个暂时没有了解，<code>OCIE1A</code> 表示了启用 CTC 模式的中断。</p>

<pre><code class="prettyprint lang-c">    sei();

    Serial.begin(9600);

    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}

ISR(TIMER1_COMPA_vect)  
{
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    Serial.println("triggered!\n");
}
</code></pre>

<p><code>ISR</code> 就是上面提到的 <code>Interrupt Service Routine</code>。</p>

<p><code>TIMER1_COMPA_vect</code> 表示这个函数是处理定时器一的 CTC 中断事件的。</p>

<p>另外，<code>TIMER1_OVF_vect</code> 是表示处理定时器一的溢出中断事件。</p>

<pre><code class="prettyprint lang-c">void loop() {  
}
</code></pre>

<p>大概是解释清楚了吧，不负责差评不负责退货 XD~</p>

<p>* Cover 照片来自 
Arnaud Boudou 的<a href='http://www.flickr.com/photos/aboudou/12386133923/' >照片</a>，遵循 CC-BY-SA 协议使用。</p>]]></description><link>http://b.zr.is/2014/03/23/arduino-timer-interrupt/</link><guid isPermaLink="false">1323ae3c-fe5c-4ea0-8b15-e17e1fd89896</guid><category><![CDATA[Arduino]]></category><dc:creator><![CDATA[Zeray Rice]]></dc:creator><pubDate>Sun, 23 Mar 2014 19:00:35 GMT</pubDate></item><item><title><![CDATA[设置 V2EX DNS 仅为 Apple Store 加速]]></title><description><![CDATA[<pre><code>sudo mkdir /etc/resolver/
sudo vim /etc/resolver/apple.com
</code></pre>

<p>写入:</p>

<pre><code>nameserver 199.91.73.222
</code></pre>

<p>或者 </p>

<pre><code>nameserver 178.79.131.110
</code></pre>

<p>（根据你的网络 ping 下看看速度..）</p>

<p>设置之后：</p>

<pre><code>sudo killall -HUP mDNSResponder
</code></pre>

<p>重启 DNS 服务，即可生效。</p>]]></description><link>http://b.zr.is/2014/03/20/speed-up-apple-store-using-v2ex-dns/</link><guid isPermaLink="false">f2a19a81-546f-4ec6-9afd-cd1e83932af0</guid><dc:creator><![CDATA[Zeray Rice]]></dc:creator><pubDate>Thu, 20 Mar 2014 16:58:28 GMT</pubDate></item><item><title><![CDATA[在不同 Root 的 Git 库之间同步 Commit]]></title><description><![CDATA[<p>最近遇到一个蛋疼的问题，需要在不同 root 的 git 仓库之间同步提交，简单搜了下发现基本都是在说 Remote 和 Local 之间的 Git 仓库同步，最后找到了一篇说在无网络的情况下进行同步的才得以解决这个问题。</p>

<p>基本思路就是在仓库 A 执行 <code>git format-patch &lt;from_sha&gt;...&lt;to_sha&gt;</code> ，然后把得到的 patch 文件再在仓库 B 中通过 <code>git am</code> 进行合并。</p>

<p>然后需要说的一个点是，当进行 <code>git am</code> 的时候，如果发生冲突的解决方法：</p>

<p>在发生冲突的时候， <code>git am</code> 会提示 <code>Patch failed</code> ，并提示把 patch 文件放到了 <code>.git/rebase-apply/patch</code> 中，这时候就需要手工来解决冲突了。</p>

<p>执行 <code>git apply --reject .git/rebase-apply/patch</code> 通过 <code>--reject</code> 选项使在合并冲突的时候自动生成以 <code>.rej</code> 结尾的 patch 文件，里面仅包含了合并冲突时的 diff 信息，通过查看这个文件可以知道哪几处出了问题，手工解决它，然后 <code>git add</code>。</p>

<p>这里有一个小坑就是在解决完冲突之后记得检查下 <code>git status</code> 中的其他文件，成功被合并的文件不会生成 <code>.rej</code> 文件，但是也不会被自动添加到 Stage Area ，所以要记得把这些文件也 add 进去。</p>

<p>搞定冲突之后，<code>git am --resolved</code> 就完整的把 Commit 给转过来了~</p>]]></description><link>http://b.zr.is/2013/11/07/share-commit-between-git-repositories/</link><guid isPermaLink="false">f8af2b10-eef3-4a81-9760-ac4447098fc6</guid><category><![CDATA[git]]></category><category><![CDATA[Tricks]]></category><dc:creator><![CDATA[Zeray Rice]]></dc:creator><pubDate>Thu, 07 Nov 2013 21:25:21 GMT</pubDate></item></channel></rss>