<?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" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[~rix]]></title><description><![CDATA[drwxr-xr-x]]></description><link>https://rix.li/</link><generator>Ghost 0.9</generator><lastBuildDate>Fri, 17 Apr 2026 15:38:07 GMT</lastBuildDate><atom:link href="https://rix.li/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Oh My Chocolate - Willy Wonka's Keycaps]]></title><description><![CDATA[<p>Finally got this <a href="https://www.massdrop.com/buy/the-amazing-chocolatier-custom-sa-keycap-set">Amazing Chocolatier Custom SA Keycap Set</a>. Enjoy it!</p>

<p><img src="https://rix.li/content/images/2017/04/9M6A4077-1.jpg" alt="">
<img src="https://rix.li/content/images/2017/04/9M6A4080-1.jpg" alt="">
<img src="https://rix.li/content/images/2017/04/9M6A4084-2.jpg" alt=""></p>]]></description><link>https://rix.li/oh-my-chocolate-willy-wonkas-keycaps/</link><guid isPermaLink="false">14d6c9e4-f297-4a7e-a59f-f947d567d5b1</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Sat, 01 Apr 2017 23:28:35 GMT</pubDate><content:encoded><![CDATA[<p>Finally got this <a href="https://www.massdrop.com/buy/the-amazing-chocolatier-custom-sa-keycap-set">Amazing Chocolatier Custom SA Keycap Set</a>. Enjoy it!</p>

<p><img src="https://rix.li/content/images/2017/04/9M6A4077-1.jpg" alt="">
<img src="https://rix.li/content/images/2017/04/9M6A4080-1.jpg" alt="">
<img src="https://rix.li/content/images/2017/04/9M6A4084-2.jpg" alt=""></p>]]></content:encoded></item><item><title><![CDATA[Useful Commands Snippets for Subtitle Files]]></title><description><![CDATA[<h3 id="bulkrenaming">Bulk Renaming</h3>

<p>You may have a list of subtitle files having different naming schemes than those video files to be matched. We can use Regex to rename these subtitle files.</p>

<h4 id="unixshell">Unix Shell</h4>

<p>With Perl <code>File::Rename</code> utility:</p>

<pre><code class="language-shell">rename 's/^.*?\[(\d+)\].*(\.\w+)$/$1$2/' *.ass  
</code></pre>

<h4 id="powershell">Powershell</h4>

<pre><code class="language-powershell">Get-ChildItem .\* -Include *.ass | Rename-Item</code></pre>]]></description><link>https://rix.li/useful-commands-snippets-for-subtitle-files/</link><guid isPermaLink="false">2ba171b0-b2b1-4c40-876e-c497133d93ce</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Mon, 27 Mar 2017 07:25:41 GMT</pubDate><content:encoded><![CDATA[<h3 id="bulkrenaming">Bulk Renaming</h3>

<p>You may have a list of subtitle files having different naming schemes than those video files to be matched. We can use Regex to rename these subtitle files.</p>

<h4 id="unixshell">Unix Shell</h4>

<p>With Perl <code>File::Rename</code> utility:</p>

<pre><code class="language-shell">rename 's/^.*?\[(\d+)\].*(\.\w+)$/$1$2/' *.ass  
</code></pre>

<h4 id="powershell">Powershell</h4>

<pre><code class="language-powershell">Get-ChildItem .\* -Include *.ass | Rename-Item -NewName { $_.Name -replace '^.*?(\d+).*(\.\w+)$','$1$2' }  
</code></pre>

<h3 id="encodingconversion">Encoding Conversion</h3>

<h4 id="unixshell">Unix Shell</h4>

<p>With <code>enconv</code>:</p>

<pre><code class="language-shell">#!/bin/bash

find "$0" -type f -name "*.ass" -o -name "*.srt" -o -name "*.ssa" -o -name "*.sub" -o -name "*.sbv" -exec enconv -L chinese -x UTF8 "{}" \;  
</code></pre>

<p>With <code>iconv</code>:</p>

<pre><code class="language-shell">#!/bin/bash

if [ -z "$1" ]; then  
    SRC="."
else  
    SRC="$1"
fi

if [ -z "$2" ]; then  
    ENCODING="gb18030"
else  
    ENCODING="$2"
fi

if [ -z "$3" ]; then  
    DESTI="UTF-8"
else  
    DESTI="$3"
fi

mkdir -p "${SRC}/out"

find "${SRC}/"* -maxdepth 0 -type f -name "*.ass" -exec sh -c "iconv -f \"${ENCODING}\" -t \"${DESTI}\" \"{}\" &gt; \"${SRC}/out/{}\" " \;  
</code></pre>

<h4 id="powershell">Powershell</h4>

<pre><code class="language-powershell">ForEach ($f in Get-ChildItem .\* -Include *.ass) {[System.IO.File]::WriteAllText($f, [System.IO.File]::ReadAllText($f, [System.Text.Encoding]::GetEncoding('gb2312')), [System.Text.Encoding]::GetEncoding('utf-8'))}  
</code></pre>

<p>List of encoding names:</p>

<pre><code class="language-powershell">[System.Text.Encoding]::GetEncodings()
</code></pre>]]></content:encoded></item><item><title><![CDATA[有弾幕（YouDanMu）v0.1.0]]></title><description><![CDATA[<p>海外用戶的福利來啦！有弾幕 v0.1.0 發佈，支持 YouTube 主站視頻外掛 Bilibili 彈幕。</p>

<p>下載地址：
<a href="https://github.com/YouDanMu/YouDanMu/releases/tag/v0.1.0">https://github.com/YouDanMu/YouDanMu/releases/tag/v0.1.0</a></p>

<p>提交 Issues：<a href="https://github.com/YouDanMu/YouDanMu/issues">https://github.com/YouDanMu/YouDanMu/issues</a></p>

<p><img src="https://rix.li/content/images/2017/03/snipaste20170324_010207.png" alt=""></p>

<p><img src="https://rix.li/content/images/2017/03/snipaste20170326_033715.png" alt=""></p>]]></description><link>https://rix.li/you-dan-mu-youdanmu-v0-1-0/</link><guid isPermaLink="false">98468fbc-5bcf-421b-bb37-ba309e4f34d4</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Sun, 26 Mar 2017 07:43:00 GMT</pubDate><content:encoded><![CDATA[<p>海外用戶的福利來啦！有弾幕 v0.1.0 發佈，支持 YouTube 主站視頻外掛 Bilibili 彈幕。</p>

<p>下載地址：
<a href="https://github.com/YouDanMu/YouDanMu/releases/tag/v0.1.0">https://github.com/YouDanMu/YouDanMu/releases/tag/v0.1.0</a></p>

<p>提交 Issues：<a href="https://github.com/YouDanMu/YouDanMu/issues">https://github.com/YouDanMu/YouDanMu/issues</a></p>

<p><img src="https://rix.li/content/images/2017/03/snipaste20170324_010207.png" alt=""></p>

<p><img src="https://rix.li/content/images/2017/03/snipaste20170326_033715.png" alt=""></p>]]></content:encoded></item><item><title><![CDATA[用 Squid 搭建本地 CDN 緩存代理爲網頁提速]]></title><description><![CDATA[<p>身處海外，經常遇到訪問國內網站速度緩慢。而且網站不對靜態文件進行瀏覽器緩存優化，每次刷新頁面都要重新加載所有資源文件，而且很多網站內嵌很多個 iframe，每一個都調用一下 jQuery 之類的重量級腳本，再加上 TCP 掉包失連頻繁發生，整體的加載效率之慢可想而知。</p>

<p>所以特地針對這種情況折騰了一個解決方案：在本地用 <a href="http://www.squid-cache.org/">Squid</a> 搭建一個 HTTP 緩存代理服務器，編寫緩存規則自動將常用網站的靜態文件長期緩存，第一次經過該代理加載成功後，Squid 會把資源緩存在硬盤，之後每次的相同請求都會直接將硬盤中的緩存返回給瀏覽器，不需要再向源地址請求資源。說白了就是本地搭建了一個 CDN 緩存服務器罷了。</p>

<p>Squid 在 Linux/Unix 下的安裝過程我就不講了。Windows 下的安裝過程有些坑要提一提。</p>

<p>首先下載 <a href="http://squid.diladele.com/">Squid for Windows</a>，安裝完後運行 Squid Server Tray，會在系統托盤出現 Squid 圖標。右鍵 Stop Squid Service，</p>]]></description><link>https://rix.li/squid-as-a-local-cdn-proxy-to-speedup-http/</link><guid isPermaLink="false">0a6eeec4-8de7-4412-ba9c-9d254692d9f5</guid><category><![CDATA[Squid]]></category><category><![CDATA[HTTP]]></category><category><![CDATA[Proxy]]></category><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Wed, 08 Feb 2017 08:04:30 GMT</pubDate><content:encoded><![CDATA[<p>身處海外，經常遇到訪問國內網站速度緩慢。而且網站不對靜態文件進行瀏覽器緩存優化，每次刷新頁面都要重新加載所有資源文件，而且很多網站內嵌很多個 iframe，每一個都調用一下 jQuery 之類的重量級腳本，再加上 TCP 掉包失連頻繁發生，整體的加載效率之慢可想而知。</p>

<p>所以特地針對這種情況折騰了一個解決方案：在本地用 <a href="http://www.squid-cache.org/">Squid</a> 搭建一個 HTTP 緩存代理服務器，編寫緩存規則自動將常用網站的靜態文件長期緩存，第一次經過該代理加載成功後，Squid 會把資源緩存在硬盤，之後每次的相同請求都會直接將硬盤中的緩存返回給瀏覽器，不需要再向源地址請求資源。說白了就是本地搭建了一個 CDN 緩存服務器罷了。</p>

<p>Squid 在 Linux/Unix 下的安裝過程我就不講了。Windows 下的安裝過程有些坑要提一提。</p>

<p>首先下載 <a href="http://squid.diladele.com/">Squid for Windows</a>，安裝完後運行 Squid Server Tray，會在系統托盤出現 Squid 圖標。右鍵 Stop Squid Service，但後 Open Squid Configuration，按照下面的配置模板寫好 Squid 配置。</p>

<pre><code># Squid 監聽的 HTTP Proxy 端口
http_port 3128

# 緩存目錄。參數：
# cache_dir Type Directory Mbytes L1 L2 [options]
#   - Type: 緩存數據格式，建議使用 aufs
#   - Directory: 目錄地址，因爲 Squid for Windows 是運行
#     在 Cygwin 虛擬 POSIX 環境中，如果你想要設置的目錄是
#     D:\Squid\cache，則寫成 /cygdrive/d/Squid/cache
#   - Mbytes: 緩存大小限制（MB）
#   - L1, L2: 緩存目錄一級二級文件夾數目，建議保持默認值
cache_dir aufs /cygdrive/d/Squid/cache 20480 16 256

# 在這部分添加常用網站的緩存規則。參數：
# refresh_pattern [-i] regex min percent max [options]
#   - -i: 正則匹配忽略大小寫
#   - regex: 緩存規則對應的正則表達式，用來匹配請求 URL，
#     如果匹配成功則使用該規則處理請求
#   - min: 該規則對資源緩存至少多少分鐘，對動態內容建議
#     設爲 0，靜態內容則越大越好
#   - percent: 該資源至少緩存上次生命週期的百分比
#   - max: 該規則對資源緩存最大多少分鐘
# 對於 options，推薦使用 override-expire ignore-no-store
# ignore-private ignore-reload 的組合強制進行靜態資源緩存
# - 參考 http://www.luocheng.cn/article-view-209.html

refresh_pattern ^https?://...\.bdstatic\.com/ 43200 90% 432000 override-expire ignore-no-store ignore-private ignore-reload  
refresh_pattern ^https?://assets\.xxxxx\.com 43200 90% 432000 override-expire ignore-no-store ignore-private ignore-reload  
refresh_pattern . 0 20% 4320

# 定義一個叫 localnet 的 IP 組，包含所有內網地址
acl localnet src 10.0.0.0/8     # RFC1918 possible internal network  
acl localnet src 172.16.0.0/12  # RFC1918 possible internal network  
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network  
acl localnet src fc00::/7       # RFC 4193 local private network range  
acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines

# 允許本地連接通過代理
http_access allow localhost manager localnet  
http_access allow localnet  
http_access allow localhost

# 防止 Squid 在 HTTP Request Header 中亂添加東西
# 導致某些網站檢測到 Proxy 特徵而禁止訪問
via off  
forwarded_for delete  
log_mime_hdrs on  
header_access User-Agent allow all

# 崩潰時 coredumps 目錄，建議保留默認
coredump_dir /var/cache/squid

# 設置 DNS 服務器
dns_nameservers 8.8.8.8 208.67.222.222

# 限制 Squid 的文件句柄數，Windows 有一定限制，不要改動
max_filedescriptors 3200  
</code></pre>

<p>請務必將所有中文註釋刪掉，然後將該配置文件保存爲 ANSI 文本文件，Squid 不支持 Unicode 配置文件。然後運行桌面上的 Squid Terminal 程序，執行 <code>squid -z</code> 初始化緩存目錄。如果出現類似</p>

<pre><code>FATAL: Ipc::Mem::Segment::create failed to shm_open(/squid-cf__metadata.shm): (17) File exists  
</code></pre>

<p>這樣的錯誤，請到 Squid 安裝目錄，刪除 <code>dev/shm/</code> 目錄下的所有文件再重新執行上述命令。</p>

<p>然後再 Start Squid Service，緩存代理服務器就應該架好了。</p>

<p>接下來在你的瀏覽器配置 HTTP 代理，感受一下緩存加速的效果吧。</p>]]></content:encoded></item><item><title><![CDATA[Axiom Keyboard Update 17/11/2016]]></title><description><![CDATA[<p>OK folks, sorry for keeping you in suspense. Here are some updates for my <a href="https://rix.li/axiom-keyboard/">Axiom Keyboard</a> project.</p>

<h3 id="layoutprototype">Layout Prototype</h3>

<p>First, I had the metal frame laser'd and a full set of the basic Axiom layout keycaps 3D printed. My partner Peng Bo sent me these results from China.</p>

<p><img src="https://rix.li/content/images/2016/11/30932466722_c2690c78bd_o.jpg" alt=""></p>

<p><img src="https://rix.li/content/images/2016/11/30932463702_e32aca5854_o.jpg" alt=""></p>

<p><img src="https://rix.li/content/images/2016/11/30932463822_d0c09da9fe_o.jpg" alt=""></p>

<p><img src="https://rix.li/content/images/2016/11/31074830205_3b55b96d36_o-1.jpg" alt=""></p>

<p><img src="https://rix.li/content/images/2016/11/30609814312_187ed7e8cb_o.jpg" alt=""></p>

<p><img src="https://rix.li/content/images/2016/11/891449376322633397-1.jpg" alt=""></p>

<p><img src="https://rix.li/content/images/2016/11/1133072354@chatroom_1479442014056_37-2.jpg" alt=""></p>

<p>Since the</p>]]></description><link>https://rix.li/axiom-keyboard-update-17-11-2016/</link><guid isPermaLink="false">72769925-c5d5-4b23-8c86-25d8ca2b5c62</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Fri, 18 Nov 2016 05:35:11 GMT</pubDate><media:content url="https://rix.li/content/images/2016/11/1133072354@chatroom_1479442014056_37-3.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://rix.li/content/images/2016/11/1133072354@chatroom_1479442014056_37-3.jpg" alt="Axiom Keyboard Update 17/11/2016"><p>OK folks, sorry for keeping you in suspense. Here are some updates for my <a href="https://rix.li/axiom-keyboard/">Axiom Keyboard</a> project.</p>

<h3 id="layoutprototype">Layout Prototype</h3>

<p>First, I had the metal frame laser'd and a full set of the basic Axiom layout keycaps 3D printed. My partner Peng Bo sent me these results from China.</p>

<p><img src="https://rix.li/content/images/2016/11/30932466722_c2690c78bd_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/30932463702_e32aca5854_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/30932463822_d0c09da9fe_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/31074830205_3b55b96d36_o-1.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/30609814312_187ed7e8cb_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/891449376322633397-1.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/1133072354@chatroom_1479442014056_37-2.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p>Since the keycaps are 3D printed, error may occur in spacing, and you may notice those alignment issues in each row. This is a common issue with 3D printing that I cannot help with. But don't worry since the final production would be using plastic injection molding, and we will make them as accurate as possible.</p>

<h3 id="bluetooth">Bluetooth</h3>

<p>My other sincere partner Yu Qianshan is in Jilin, China. He is currently messing with the Bluetooth stack and building our very first demo that would support Bluetooth 4.1 HID over GATT Profile (HOGP) with up to 10 simultaneous connections.</p>

<p><img src="https://rix.li/content/images/2016/11/30586798722_7d03d505d2_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p>However, since TI doesn't provide the complete source code of their Bluetooth stack, we have to explore around to get the puppy running. Currently we have already ported a small SPP demo to our development platform. I think we are near to the goal.</p>

<h3 id="firmware">Firmware</h3>

<p>I'm designing the fundamental kernel of the firmware. My aim is to create a kernel that consumes the lowest power possible and provides the most significant latency optimization for real time response. Thus I decided to integrate an event driven kernel to our firmware. Specifically, I'm considering using the ultra-lightweight and lightening fast <a href="http://www.state-machine.com/qpn/">QP-nano</a> framework that comes with a combination of cooperative and preemptive kernel and a state chart UML programming framework for state machine abstraction. This allows me using their awesome <a href="http://www.state-machine.com/qm/">QM</a> state chart design and code generation automation tool to build reliable and real time firmware.</p>

<p>However this also means we have to rewrite most of the drivers to integrate with our state charts. I think this is good because the TI's "official" driver is a pile of GARBAGE! It looks exactly like written by some interns who have no idea what is good API and code design. Rewriting them means better code style, better organization and integrations, reduced redundancies and repetitions. But it may also be tragic to me since TI doesn't provide all its drivers with full source codes, like the CC2564 Bluetooth chip. This means I have to do A LOT OF reverse engineering to hack their object codes, and rebuild the portion we need. That's why you saw the crazy logic analyzer I was using in the photo above.</p>

<p>Therefore the whole firmware will be a very time consuming sub-project I will be working on for a very long time. I hope Yu can bring me some good news in the meanwhile so that we may have an idea of the whole system functioning.</p>

<h3 id="matrixscannerchip">Matrix Scanner Chip</h3>

<p>Oh I have to say I LOVE THIS CHIP! Its footprint is much smaller than I expected.</p>

<p><img src="https://rix.li/content/images/2016/11/30562337632_6eac24fcac_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p>More importantly, it's AMAZINGLY powerful and easy to use. The I<sup>2</sup>C interface works like a charm. I had it tested on my Raspberry Pi, and everything worked in the first compilation!</p>

<p><img src="https://rix.li/content/images/2016/11/30045596733_4a8a2b4e4a_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/30591134241_595316d8e2_o-1.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<h3 id="powerpack">Power Pack</h3>

<p>The power management is also from TI's chip. It supports smooth transition from USB power to battery power and vice versa. The battery will also be charged when connected to USB. Just look at all these TI BoosterPack stacked up! What a double sandwich!</p>

<p><img src="https://rix.li/content/images/2016/11/30045594493_d4148b1baf_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<h3 id="ledpwmchip">LED PWM Chip</h3>

<p>After communicating with Panasonic reps., the 288 channel chip was a joke, and we were limited to 144 channel instead. That's alright since we have the powerful I<sup>2</sup>C interface to handle multiple chips. What makes it difficult is that there's absolutely no stock on Earth for the 144 channel chip. This is probably because Corsair is eating up the supply of this specific chip. The reps. told me the general lead time would be 15-20 weeks. CRAZY!</p>

<p>So instead of letting him order me samples for a quarter, I decided to use the weaker model ships from the same series that only has a total of 9x9 channels and provides a relatively prototyping friendly TSSOP package compare to the exclusive QFN SMD packages for other models in the same series. It has the exact same I<sup>2</sup>C interface like all the other models, so the code working on this model should also work on other models with some minor changes of the number of channels.</p>

<p>However, TSSOP is still too small for manual soldering. Thus I spent some time and found a nice test socket that can snap the pins on the chip and export them to solder friendly footprints. So I bought some of these sockets and made an adapter myself.</p>

<p><img src="https://rix.li/content/images/2016/11/25380934479_2be5958f24_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/30715280240_31aac84b4b_o.png" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/30932466152_e82a4290ac_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p>This way I can easily connect the chip with my development platforms. <del>I may start testing this chip tomorrow, and would expect some more updates after that.</del></p>

<p><strong>Update on 18/11/2016:</strong></p>

<p>As promised, I tested my own adapter with the Panasonic LED PWM Matrix chip today. It took me a while to connect the wires on the breadboard since this chip is much more powerful than the key scanner chip. Anyway, I had it setup and everything worked fine so far. I can manually program it through the I<sup>2</sup>C interface from my Raspberry Pi, and light up any single LED on the matrix with any PWM level I choose.</p>

<p><img src="https://rix.li/content/images/2016/11/30293482224_6153ba3c15_o.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p><img src="https://rix.li/content/images/2016/11/30735153830_4f5e09428d_o-1.jpg" alt="Axiom Keyboard Update 17/11/2016"></p>

<p>To clarify, I'm using an uni-color LED matrix board because I couldn't find a RGB 3-color independent channels LED matrix board online. But since RGB is just grouping every 3 LEDs together to create colors, I can simulate the underlining logic on the uni-color board as well.</p>

<p>Although nothing fancy to show you guys right now, this demo should still be a successful proof of concept to me. I will work on some eye candy demo during the weekend, and let's see what this puppy is capable of.</p>

<p>More to come...</p>]]></content:encoded></item><item><title><![CDATA[Axiom Keycap Prototype 3D Printed!]]></title><description><![CDATA[<p>Here it comes updates from my partner Peng Bo in China. He helped me 3D printed and tested my Axiom keycap prototypes. Here are some footage he sent me.</p>

<video autoplay loop>  
    <source src="https://zippy.gfycat.com/MaleEnormousAztecant.webm" type="video/webm">
    <source src="https://zippy.gfycat.com/MaleEnormousAztecant.mp4" type="video/mp4">
    <img title="Sorry, your browser doesn't support HTML5 video." src="https://thumbs.gfycat.com/MaleEnormousAztecant-poster.jpg">
</video>

<video autoplay loop>  
    <source src="https://zippy.gfycat.com/BronzeLegalBluejay.webm" type="video/webm">
    <source src="https://zippy.gfycat.com/BronzeLegalBluejay.mp4" type="video/mp4">
    <img title="Sorry, your browser doesn't support HTML5 video." src="https://thumbs.gfycat.com/BronzeLegalBluejay-poster.jpg">
</video>

<video autoplay loop>  
    <source src="https://zippy.gfycat.com/ChillySparseAtlanticsharpnosepuffer.webm" type="video/webm">
    <source src="https://zippy.gfycat.com/ChillySparseAtlanticsharpnosepuffer.mp4" type="video/mp4">
    <img title="Sorry, your browser doesn't support HTML5 video." src="https://thumbs.gfycat.com/ChillySparseAtlanticsharpnosepuffer-poster.jpg">
</video>

<p>And more to come soon...</p>]]></description><link>https://rix.li/axiom-keycap-prototype-3d-printed/</link><guid isPermaLink="false">b3237ef7-6969-4257-9656-42886a6afbb0</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Tue, 25 Oct 2016 15:40:25 GMT</pubDate><content:encoded><![CDATA[<p>Here it comes updates from my partner Peng Bo in China. He helped me 3D printed and tested my Axiom keycap prototypes. Here are some footage he sent me.</p>

<video autoplay loop>  
    <source src="https://zippy.gfycat.com/MaleEnormousAztecant.webm" type="video/webm">
    <source src="https://zippy.gfycat.com/MaleEnormousAztecant.mp4" type="video/mp4">
    <img title="Sorry, your browser doesn't support HTML5 video." src="https://thumbs.gfycat.com/MaleEnormousAztecant-poster.jpg">
</video>

<video autoplay loop>  
    <source src="https://zippy.gfycat.com/BronzeLegalBluejay.webm" type="video/webm">
    <source src="https://zippy.gfycat.com/BronzeLegalBluejay.mp4" type="video/mp4">
    <img title="Sorry, your browser doesn't support HTML5 video." src="https://thumbs.gfycat.com/BronzeLegalBluejay-poster.jpg">
</video>

<video autoplay loop>  
    <source src="https://zippy.gfycat.com/ChillySparseAtlanticsharpnosepuffer.webm" type="video/webm">
    <source src="https://zippy.gfycat.com/ChillySparseAtlanticsharpnosepuffer.mp4" type="video/mp4">
    <img title="Sorry, your browser doesn't support HTML5 video." src="https://thumbs.gfycat.com/ChillySparseAtlanticsharpnosepuffer-poster.jpg">
</video>

<p>And more to come soon...</p>]]></content:encoded></item><item><title><![CDATA[Change Adobe Reader Default Behaviors]]></title><description><![CDATA[<p>When viewing PDF files in Adobe Reader, some PDFs have specified zoom level, and Adobe Reader will use that to display the file. However, I want to stay in "fit width" zoom level all the time by default. And Adobe Reader always open the useless "Tools panel" for no reason,</p>]]></description><link>https://rix.li/change-adobe-reader-default-behaviors/</link><guid isPermaLink="false">0e7e31e0-dafe-45de-ba64-70b62b0208f4</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Sun, 23 Oct 2016 20:34:17 GMT</pubDate><content:encoded><![CDATA[<p>When viewing PDF files in Adobe Reader, some PDFs have specified zoom level, and Adobe Reader will use that to display the file. However, I want to stay in "fit width" zoom level all the time by default. And Adobe Reader always open the useless "Tools panel" for no reason, which is very annoying. I want to utilize my screen for MORE CONTENTS! Moreover, some PDFs have bookmarks that redirect to a page AND RESET THE ZOOM LEVEL TO "FIT PAGE", and disable continuous scrolling, which is EXTREMELY ANNOYING that I have to manually set it back to "fit width" every time I click a bookmark link!</p>

<p>These problems haunted me for very long time. Recently I finally found the way to solve all of them, so I decided to share these solutions.</p>

<h3 id="disabletoolpanels">Disable Tool Panels</h3>

<p>In Adobe Reader, go to menu "Edit -> Preferences". Under "Documents", uncheck "Open tools panel for each document."</p>

<p>I also prefer to check "Restore last view setting when reopening documents." It remembers the page I left when reopening a file, which is very handy.</p>

<h3 id="defaultfitwidth">Default "Fit Width"</h3>

<p>In Adobe Reader, go to menu "Edit -> Preferences". Under "Accessibility", check "Always use Page Layout Style," and choose "Single Page Continuous." Also Check "Always use Zoom Setting," and choose "Fit Width."</p>

<h3 id="fixbookmarks">Fix Bookmarks</h3>

<p>It's impossible to prevent the bookmark reset zoom operation in Adobe Reader, which is a TOTAL FAIL. However, I still found a workaround, which requires you to modify your PDF file with Adobe Acrobat Pro, with a plugin called PubHelper.</p>

<p>Download the <a href="http://www.mediafire.com/?unyjspxhcnnuq4y">PubHelper plugin</a>. Unzip the <code>PubHelper 0.991.api</code> file to <code>&lt;Adobe Acrobat Pro&gt;/Acrobat/plug_ins</code>.</p>

<p>Then open your targeting PDF in Adobe Acrobat Pro. Click on any bookmark item, then press Ctrl-A to select all of them. Go to menu "PubHelper -> Bookmarks -> To Inherit Zoom". Now clicking any bookmark won't reset your zoom level.</p>

<p>I also prefer to run "PubHelper -> Bookmarks -> Expand all" to make all levels of bookmarks expanded by default.</p>

<p>Now save the modified PDF file. You can now switch back to Adobe Reader to view the file.</p>

<p>PS: I wish there's a GhostScript or pdfmark script that can substitue the use of Adobe Acrobat Pro and the PubHelper plugin to modify PDF files. If you know how please tell me in the comment section.</p>]]></content:encoded></item><item><title><![CDATA[HHKB Topre to Cherry MX Adapter (Open Source!)]]></title><description><![CDATA[<p>I previously crafted my own version of HHKB Topre to MX adapter in summer 2016. It was originally inspired by the CM Storm NovaTouch TKL's MX compatible stems. Then the famous keyboard enthusiast matt3o took the idea to make <a href="http://matt3o.com/topre-to-cherry-mx-adapter/">his versions of the adapters</a>.</p>

<p>I tried to print matt3o's versions</p>]]></description><link>https://rix.li/topre-to-cherry-mx-adapter/</link><guid isPermaLink="false">28de8452-68b0-4db9-8157-abb9d44f4fcf</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Sat, 22 Oct 2016 07:00:12 GMT</pubDate><media:content url="https://rix.li/content/images/2016/10/9M6A2739-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://rix.li/content/images/2016/10/9M6A2739-2.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"><p>I previously crafted my own version of HHKB Topre to MX adapter in summer 2016. It was originally inspired by the CM Storm NovaTouch TKL's MX compatible stems. Then the famous keyboard enthusiast matt3o took the idea to make <a href="http://matt3o.com/topre-to-cherry-mx-adapter/">his versions of the adapters</a>.</p>

<p>I tried to print matt3o's versions out, but somehow they have some tiny dimension errors. Especially for the 2.0 beta version, the base room is too small that stuck the rubber dome when pressed. Moreover, he only made the 1u adapters, so I can't replace the whole keyboard with MX keycaps.</p>

<p>Here it comes <a href="https://github.com/rixtox/Topre-to-Cherry-MX-Adapter">my improved version of Topre to MX adapters. (Open source on GitHub!)</a> Everything is re-calculated and designed from scratch without basing on matt3o's design. I made the 1u, 2.25u adapters, and recently the stabilizers for 6.25u spacebar. I've 3D printed, tested, and improvised these for many times. They are very mature and stable at this stage. You can confidently replace all your HHKB keycaps using my design.</p>

<p>This project is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>. You can download and use these designs for personal use for free. If you want to support this project, you can buy these parts from my <a href="https://www.shapeways.com/shops/axiom-1">Shapeways shop</a> where I make 10% fixed profits, or you can donate to <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=Y925WPT8QVTTU&amp;lc=US&amp;item_name=Axiom%20Keyboard&amp;currency_code=USD&amp;bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">my PayPal</a> or BitCoin wallet:</p>

<pre><code>14VSt78bfgMx9pWXV9qbMMsxter7KYHCRd
</code></pre>

<p>Here is the standard 1u/1.5u/1.75u plunger.</p>

<p><img src="https://rix.li/content/images/2016/10/untitled-34.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p>Here is the 2.25u plunger. It's designed for compatibility with the HHKB 2.25u metal wire.</p>

<p><img src="https://rix.li/content/images/2016/11/Plunger-2-25x.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><del>The only problem left was the space bar. I wasn't able to remove the two stabilizers from the case to measure their dimensions. But even I did so, it's nearly impossible to find suitable 6u space bars on the market. Therefore my solution is to buy a PBT Topre space bar in your preferred color on <a href="https://www.massdrop.com/buy/20595">Massdrop</a> or <a href="https://world.taobao.com/item/533666598241.htm">Taobao</a> (Chinese Seller). Moreover, I also included a printed and tested 3D model in my repository in case you couldn't buy those, you could still 3D print it, but you may lose the good of PBT.</del></p>

<p>(More about spacebar in update below)</p>

<p>Here is a replicated 6u Topre spacebar model before I came up with a solution for MX spacebar.</p>

<p><img src="https://rix.li/content/images/2016/10/untitled-37.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2016/10/Spacebar-6x-Bottom.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p>Recently some friends of mine requested for a dozen of adapters for their HHKB MX keycaps mods. I helped them install the adapters. They tried it with their MX keycaps, and they were all satisfied with the new stems' feeling. "It's different," said my friend, "but I kinda like it." Indeed, the typing sound was more clear and the touching is more ... it's hard to describe, but personally I prefer the new feeling. It's more like <em>typing</em>. I think it also depends on the material. I 3D printed these in SLA material. I would recommend it if you want to print them.</p>

<p>I took some photos while installing them on my friends' HHKBs.</p>

<p><img src="https://rix.li/content/images/2016/10/9M6A2722.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2016/10/9M6A2728-1.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2016/10/9M6A2729.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2016/10/9M6A2730-1.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2016/10/28747098964_2488f2315e_o.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2016/10/9M6A2731.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2016/10/9M6A2739-1.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<h6 id="16042017update">16/04/2017 Update</h6>

<p>Recently I received the <a href="https://rix.li/oh-my-chocolate-willy-wonkas-keycaps/">chocolate SA keycap set</a>, and come up with an idea to install the more available 6.25u MX spacebar on HHKB. I replaced the two keys next to the HHKB spacebar with two 1.25u keycaps from the Novelties set from the group buy drop. This left 0.125u space each between those keycaps and the ALT keys next to them. It's acceptable and even unnoticeable to many people. In addition there are also 0.125u space each between them and the original HHKB 6u spacebar. That is, the spacebar can now extend 2*0.125u=0.25u to 6.25u.</p>

<p><img src="https://rix.li/content/images/2017/04/9M6A4077-2.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p>From my experience, there are many 1.25u modifier keycaps on the market. The most selling spacebar dimension on the market is the so called "Universal Spacebar". It's 6.25u with 6 stems positioned like this:</p>

<p><img src="https://rix.li/content/images/2017/04/Universal-Spacebar-Dimension.png" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p>Unlike most 6u MX keycaps, the 6.25u Universal Spacebar has a centered stem which makes it possible to be installed on HHKB. What need to be solved are the two stabilizers, and you would probably guessed it correct: I made some new adapters!</p>

<p>After several trials and tests, I eventually came up with this:</p>

<p><img src="https://rix.li/content/images/2017/04/Stabilizers.png" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p><img src="https://rix.li/content/images/2017/04/33898454782_4e59ae1390_o.jpg" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<p>The current version is confidently stable, and is also open-sourced on my <a href="https://github.com/rixtox/Topre-to-Cherry-MX-Adapter">GitHub repository</a>.</p>

<h6 id="25122018update">25/12/2018 Update</h6>

<p>Topre to MX 6u Center Stemed Spacebar Stabilizer set is now available!</p>

<p>If you want to use these stabilizers, you have to 3D print all the 4 different parts: a left/right pair of housings, and a left/right pair of plungers. The metal wire came from the original HHKB's wire, and of course you will need the standard 1u Topre to MX plunger at the center. You CAN ONLY use these stabilizers with centered stem 6u MX spacebar with (with the two outer stems 95.25mm apart). If you want to use 6.25u Universal Spacebar, please check out Topre to 6.25u MX Spacebar Stabilizer set above.</p>

<p><img src="https://rix.li/content/images/2018/12/6u-Stabilizers.png" alt="HHKB Topre to Cherry MX Adapter (Open Source!)"></p>

<h5 id="importantnote">IMPORTANT NOTE</h5>

<p><strong>When installing MX spacebar,</strong> first replace the housings. Then insert the stabilizer plungers into the spacebar stems, push them tight. Then insert the 1u plunger from the bottom, and remember to put the spring on the top of the plunger. Then put the spacebar in place, firstly insert the center 1u plunger into the center stem on the spacebar, then let the stabilizer plungers fall into the holes on the housings' top. Finally push and click the metal wire in place.</p>

<p><strong>When uninstalling MX spacebar,</strong> reverse the installing procedures in the <strong>EXACT ORDER!</strong> First open up the case, remove PCB, until you face the bottom of the plungers like the picture above, then pop and remove the metal wire. Then use standard MX keycap puller tool to remove the spacebar from the center 1u plunger. Without the metal wire, the stabilizer plungers should be pulled out from the housing together with the spacebar. This is important because if you don't remove the metal wire, it will stop the stabilizer plungers from pulling out. If the linkage between the stabilizer plungers and the stems are too strong there could be great chance the pulling force would break and permanently damage the stabilizer plungers. When the spacebar and stabilizer plungers are pulled out together, manually pull the stabilizer plungers out by pinching and pulling the large body part of the stabilizer plungers. It protects the part from breaking.</p>]]></content:encoded></item><item><title><![CDATA[雙色注塑透光鍵帽工藝及設計分析]]></title><description><![CDATA[<p><a href="https://rix.li/axiom-keyboard/">Axiom Keyboard</a> 是一款 RGB LEB 鍵盤，所以鍵帽要設計成透光的。目前市面上大部分便宜的透光鍵帽用的是 ABS 材質，然後遮罩浸染上色把不需要透光的部分塗黑製成的。這種工藝成本低，但是手感很差。對於追求體驗的高端鍵盤玩家，PBT 材質的手感才能滿足他們的需求。可惜 PBT 本身並不透光，如果只是要非透光的 PBT 鍵帽，圖案可以用激光刻蝕的方式印上去，但是如果要透光的話就只能使用雙色注塑工藝了。</p>

<p><img src="https://rix.li/content/images/2016/10/2-Shot-Keycap.jpg" alt=""></p>

<p>雙色注塑，又稱 Two-Shot Injection / Double-Shot Injection / Co-Injection，原理是將兩種不同的塑料依次注塑成型，使得兩種材料相互粘連。我們知道 PBT 材料不透光，所以我們必須要配合 ABS 材料進行兩者結合。首先利用 ABS 材料的透光性鑄成圖案部分，其他的表面部分再用不透光的 PBT 材料進行填充。然後要確保光線能順利通過 ABS 介質漫射到表面的圖案部分。這種工藝看似簡單，但是對雙色結構的設計要求非常高。</p>]]></description><link>https://rix.li/double-shot-keycaps/</link><guid isPermaLink="false">eda201bd-4eaa-404e-b4ba-ef53d4614160</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Sat, 15 Oct 2016 03:48:02 GMT</pubDate><media:content url="https://rix.li/content/images/2016/10/Filling-Animation.gif" medium="image"/><content:encoded><![CDATA[<img src="https://rix.li/content/images/2016/10/Filling-Animation.gif" alt="雙色注塑透光鍵帽工藝及設計分析"><p><a href="https://rix.li/axiom-keyboard/">Axiom Keyboard</a> 是一款 RGB LEB 鍵盤，所以鍵帽要設計成透光的。目前市面上大部分便宜的透光鍵帽用的是 ABS 材質，然後遮罩浸染上色把不需要透光的部分塗黑製成的。這種工藝成本低，但是手感很差。對於追求體驗的高端鍵盤玩家，PBT 材質的手感才能滿足他們的需求。可惜 PBT 本身並不透光，如果只是要非透光的 PBT 鍵帽，圖案可以用激光刻蝕的方式印上去，但是如果要透光的話就只能使用雙色注塑工藝了。</p>

<p><img src="https://rix.li/content/images/2016/10/2-Shot-Keycap.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>雙色注塑，又稱 Two-Shot Injection / Double-Shot Injection / Co-Injection，原理是將兩種不同的塑料依次注塑成型，使得兩種材料相互粘連。我們知道 PBT 材料不透光，所以我們必須要配合 ABS 材料進行兩者結合。首先利用 ABS 材料的透光性鑄成圖案部分，其他的表面部分再用不透光的 PBT 材料進行填充。然後要確保光線能順利通過 ABS 介質漫射到表面的圖案部分。這種工藝看似簡單，但是對雙色結構的設計要求非常高。下面我們來探討一下其中的一些設計問題。</p>

<p>雙色注塑需要對兩種材料的部分分別設計模具，注塑時，會先注塑內部小一些的部分。比如下面這個凸出的透明字母「A」：</p>

<p><img src="https://rix.li/content/images/2016/10/render-12.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>注塑完字母部分後更換外層模具，注塑外部大一點的部分：</p>

<p><img src="https://rix.li/content/images/2016/10/render-10.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>這樣兩種材料就粘合在一起了。因為文字部分連著整個底座都是透明的，所以當下方有光源的時候文字能良好地透光。但是這兩張圖展現了一個問題：觀察字母「A」的結構，它的內部有一個分離的三角形，黑色的材料在填充時是從正方形邊緣區域流入的，中間會遇到文字的阻擋，材料是無法填充到三角形區域的。</p>

<p><img src="https://rix.li/content/images/2016/10/Non-Fill-Injection.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>這個時候常見的一種解決方法是從下方的材料打洞：</p>

<p><img src="https://rix.li/content/images/2016/10/Improved-Cut.png" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/render2-14.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>這種填充方式一般只用於不透光鍵帽，因爲正如上圖展現的一樣，這種方案如果用在透光鍵帽上會帶來一定的弊端：黑色材料經過的部分會擋住透光文字，對透光效果造成干擾。</p>

<p>其次，這種方案需要設計兩套模具，下半部分注塑好後需要跟上下模具都分開來，放到一套新的模具中進行第二次填充注塑。這個流程很多時候是由人工完成的，所以成本較高。使用這種工藝的代表是 Signature Plastic，比如這是他們注塑好的下半部分：</p>

<p><img src="https://rix.li/content/images/2016/10/Double_shot_2nd_color.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>上面這張圖不是很清楚，仔細看的話可以看到文字的閉合區域或比較接近閉合的區域都在下方留有開口。下面這張圖右邊的也是一個半成品，可以看到整個平面鋪滿了孔洞，這樣第二次注塑的材料才能比較均勻地滲透到表層各處。</p>

<p><img src="https://rix.li/content/images/2016/10/custom-keycaps-1.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>然後他們會像這樣把這半成品部分放到一組新的模具裏面進行第二次注塑：</p>

<p><img src="https://rix.li/content/images/2016/10/Relegendable.png" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>這個過程可以看<a href="https://youtu.be/GOpj2Zgk710">這個視頻</a>感受一下(請把音量關小)。下面兩張圖展示了第二次注塑中間時候的內部狀態：</p>

<p><img src="https://rix.li/content/images/2016/10/Double_shot_capslock1.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/Double_shot_capslock2.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>這種工藝做出來的鍵帽底部都是兩色相間的：</p>

<p><img src="https://rix.li/content/images/2016/10/799px-Signature_Plastics_novelty_doubleshot.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>鑑於這種方案不利於透光，且人工成本較高，人們又想出了另一種解決辦法：<strong>設計一套沒有閉合空間的專用字體！</strong>於是就有了這樣的鍵帽設計：</p>

<p><img src="https://rix.li/content/images/2016/10/103741a0t1z6icnwp01foj.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>可以看到很多原本要閉合的文字區域都設置了開口，這種字形沒有閉合空間，只要注塑時塑料流動的方向和壓力適合就能不留空隙地鋪滿整個空間，而且由於沒有下方開孔的設計，第一次注塑後半成品不需要從第一組模具中完全取出來，只要把上層模具替換掉就能直接進行第二次注塑了，可以完全運用機械化操作。這種工藝做出來的鍵帽底部都是單色的：</p>

<p><img src="https://rix.li/content/images/2016/10/103724csoxfn35xtfto5l2.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>Axiom Keyboard 的鍵帽也將使用类似的無封閉字體設計，進行 PBT+ABS 雙色注塑加工，爭取帶來最好的輸入體驗。</p>

<p><img src="https://rix.li/content/images/2016/10/Font-Design-1.png" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>目前還沒有確定字體的設計方案，我還在做很多嘗試中。如果你有不錯的想法歡迎跟我聯繫。目前已經將之前設計的鍵帽外形改造成了雙色注塑 Multi-Body 模型。只要字體確認下來，注塑模擬通過就能聯繫廠家開模然後進行生產了。</p>

<p>先放一些渲染圖吧，由於渲染用的軟件是 CPU Based 的，渲染非常耗時，就只用 R1 Profile 渲染了一組圖，之後全套鍵帽設計定稿了會再花時間把全套鍵帽進行渲染的。</p>

<p><img src="https://rix.li/content/images/2016/10/Keycap-Render-25.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/Keycap-Render-17.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/Keycap-Render-26.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/Keycap-Render-21.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/Keycap-Render-20.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/Keycap-Render-30.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/Keycap-Render-28-1.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p><img src="https://rix.li/content/images/2016/10/untitled-41.jpg" alt="雙色注塑透光鍵帽工藝及設計分析"></p>

<p>最後再放上一個 Double-Shot Injection Simulation 的演示：</p>

<p><video autoplay loop src="https://zippy.gfycat.com/BrightEssentialFowl.webm"></video></p>]]></content:encoded></item><item><title><![CDATA[Axiom Keyboard 開坑啦]]></title><description><![CDATA[<p>認識我的人都知道我對鍵盤有着相當的執着。在經過將近一年時間的構思和積累後，我決定正式開坑做鍵盤了。我將它命名爲 Axiom Keyboard，它滿足以下條件：</p>

<ol>
<li>便攜且實用，70% Layout  </li>
<li>無線+有線，支持多設備藍牙快速切換  </li>
<li>內置 USB 充電電池，有效續航  </li>
<li>有線供電下運載 16M 色域 RGB LED  </li>
<li>PBT 雙色注塑定製透光鍵帽，採用 HHKB 曲面 Profile  </li>
<li>支援全新 Cherry MX RGB 系列軸體（要等 Corsair 獨占解禁）  </li>
<li>全鍵盤無衝（N-Key Rollover + USB 6-Key Workaround）</li>
</ol>

<h3 id="70layout">70% Layout</h3>

<p><a href="http://www.keyboard-layout-editor.com/#/gists/327e1d2ab75857c3b8f2b9599a875244">Axiom Keyboard Layout</a> 基於 60% 的</p>]]></description><link>https://rix.li/axiom-keyboard/</link><guid isPermaLink="false">7e9a1dbc-6dea-4dda-a679-560a521c5c7d</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Sun, 02 Oct 2016 08:14:14 GMT</pubDate><content:encoded><![CDATA[<p>認識我的人都知道我對鍵盤有着相當的執着。在經過將近一年時間的構思和積累後，我決定正式開坑做鍵盤了。我將它命名爲 Axiom Keyboard，它滿足以下條件：</p>

<ol>
<li>便攜且實用，70% Layout  </li>
<li>無線+有線，支持多設備藍牙快速切換  </li>
<li>內置 USB 充電電池，有效續航  </li>
<li>有線供電下運載 16M 色域 RGB LED  </li>
<li>PBT 雙色注塑定製透光鍵帽，採用 HHKB 曲面 Profile  </li>
<li>支援全新 Cherry MX RGB 系列軸體（要等 Corsair 獨占解禁）  </li>
<li>全鍵盤無衝（N-Key Rollover + USB 6-Key Workaround）</li>
</ol>

<h3 id="70layout">70% Layout</h3>

<p><a href="http://www.keyboard-layout-editor.com/#/gists/327e1d2ab75857c3b8f2b9599a875244">Axiom Keyboard Layout</a> 基於 60% 的 HHKB，增加了一些常用功能鍵，包括方向鍵、Home、End、Page Up、Page Down，一共有 68 鍵，所以大概是一個 70% 的 Layout。</p>

<p><img src="https://rix.li/content/images/2016/10/keyboard-layout.png" alt="Axiom Keyboard Layout"></p>

<p>可以跟 <a href="http://www.keyboard-layout-editor.com/#/gists/b65ae9407dcbc744ac0f2ec216fd2586">HHKB Layout</a> 對比一下：</p>

<p><img src="https://rix.li/content/images/2016/10/hhkb-keyboard-layout.png" alt="HHKB Keyboard Layout"></p>

<p>可以看到 Axiom Keyboard 對最底部一行的空間利用得更加充分，並且對不習慣 Caps-Ctrl Exchange 的人保留了傳統的左下角 Ctrl 鍵位。增加的方向鍵同樣能提升用戶友好度。最右邊增設的一列功能鍵可以由用戶設定成自定義功能鍵，能給經常使用 IDE 環境工作的開發者帶來很多便利。</p>

<h3 id="">藍牙連接</h3>

<p>我之前成功將 HHKB 改裝成了藍牙鍵盤，用的都是一些比較零散的模塊，而且對底層的控制也不是很深。經過一段時間的探索，我已經找到了一個大致的方向，這次我將選用 Texas Instruments 的 CC2564 藍牙芯片，它能提供完全符合 Bluetooth 4.0 標準的 HCI 控制接口，對底層的控制將更加靈活，希望在近期內能用它做出多藍牙設備快速切換的原型。</p>

<p><img src="https://rix.li/content/images/2016/10/boost-cc2564moda_boost-cc2564moda.jpg" alt="CC2564"></p>

<h3 id="rgbled">RGB LED</h3>

<p>受最近 Corsair RGB 系列鍵盤的啓發，我打算給我的鍵盤也加上 16M 色域的 RGB LED 支持。對於電子電路來說，LED 的電壓控制是通過 <a href="https://en.wikipedia.org/wiki/Pulse-width_modulation">PWM</a> 實現的，一般的 MCU 只會配備很少的甚至完全沒有硬件支持的 PWM 模塊，如果要靠軟件模擬 PWM 將會消耗巨大的計算資源，幾乎無法實現。所以我們只能選擇外部的 LED Matrix PWM Driver IC 來完成這項工作。事實證明，Corsair 在它們的新產品中也選用了這種芯片：</p>

<p><img src="https://rix.li/content/images/2016/10/210339lkabkhzjjjbfjyzo.jpg" alt="AN32181B"></p>

<p>可以看到 Corsair 使用了三塊 Panasonic AN32181B 芯片來控制多達 122 個 RGB LED。通過查看這塊芯片的 <a href="http://www.semicon.panasonic.co.jp/ds4/AN32181B_E.pdf">Data Sheet</a> 我們可以看到，一塊這樣的芯片能輸出 144 個 PWM 信號，而一個 RGB LED 需要紅、黃、藍三個 PWM 信號，所以一塊芯片能控制 48 個 RGB LED。因此 Corsair 必須貼三塊這樣的芯片才能滿足控制 122 個 RGB LED，也就是 366 個 PWM Channels 的需求。</p>

<p><img src="https://rix.li/content/images/2016/10/210341brx1yuf1adx2x9e9.jpg" alt="RGB LED"></p>

<p>如果把 AN32181B 應用到 Axiom Keyboard 上，我們需要貼兩片才能控制 68 個 RGB LED。然而在我進一步的調查中發現，儘管在電子市場上沒有出售，但是 Panasonic 在它的<a href="https://na.industrial.panasonic.com/sites/default/pidsa/files/panasonic_an3x_new_product_introduction.pdf">一篇廣告文案</a>中提到：</p>

<blockquote>
  <p>The AN3x Series is a single chip solution which can drive from 3 to 288
  single color LEDs or from 1 to 96 RGB LEDs.</p>
</blockquote>

<p>說明 Panasonic 有可能有未公開銷售的芯片模組擁有控制 96 個 RGB LED 的能力。對此我也在向松下的銷售人員求證，如果能夠搞到這樣的芯片，我們可能只需要一塊貼片就足以。</p>

<p><strong>Oct 20 更新：</strong>松下的 Rep 回覆</p>

<blockquote>
  <p>Our product group advised:</p>
  
  <p>It looks like this is misprint. Maximum number of LEDs with this series driver is 144.</p>
</blockquote>

<p>所以這個系列芯片最多只支持 144 通道。不過也是，如果有更多通道的芯片的話 Corsair 沒理由不用呀。於是我們目前看來需要兩塊 AN32181B 芯片驅動我們的鍵盤 LED 矩陣。好在這款芯片支持 400kHz I<sup>2</sup>C 通訊協議，而且能選擇 4 個物理地址，也就是說我的 MCU 只需要用一個 I<sup>2</sup>C 接口就能同時控制最多 4 塊並聯在一起的 AN32181B 芯片，以及更多不同物理地址的其他 I<sup>2</sup>C 設備。</p>

<h3 id="">鍵帽</h3>

<p>鍵帽的質量直接決定了鍵盤的使用舒適程度。我覺得做得最好的鍵帽當屬 HHKB。如果你仔細對比過 Cherry 原廠的鍵帽和 HHKB 的鍵帽你會發現，原廠鍵帽的每個側面都是平面，而 HHKB 的左右兩面其實是曲面。仔細觀察下面這個 HHKB R2 鍵帽模型的右側面：</p>

<p><img src="https://rix.li/content/images/2016/10/HHKB-Curved-Surface-View.png" alt="HHKB Curved Surface View"></p>

<p>你可以看到後面的一角是突出來的，說明這一面並不是平面。HHKB 這樣設計的目的是爲了讓頂部的觸摸面在不同的行高和斜度下都能保持同樣的規格，所以 HHKB 鍵帽整體看起來十分美觀，而且高度和斜度的變化也十分適合打字的手指運動。因此我打算以 HHKB Profile 爲基礎重新設計一套適用於 Cherry MX 的鍵帽。</p>

<p><img src="https://rix.li/content/images/2016/10/R1-R4-in-one-row.JPG" alt="R1-R4"></p>

<p>鍵帽的製作工藝我打算使用 PBT 雙色注塑（Two-Shot Injection Molding），透光的圖案部分採用 ABS 材質，這兩種材質的粘合性非常好，國內有很多雙色注塑鍵帽用的也是這種工藝：</p>

<p><img src="https://rix.li/content/images/2016/10/Two-Shot-Material-Compatibility-Chart.jpg" alt="Two Shot Material Compatibility Chart"></p>

<p><img src="https://rix.li/content/images/2016/10/Two-Shot-Injection-Keycaps-1.jpg" alt="Two-Shot Injection Keycaps 1"></p>

<p><img src="https://rix.li/content/images/2016/10/Two-Shot-Injection-Keycaps-2.jpg" alt="Two-Shot Injection Keycaps 2"></p>

<p>因爲是定製的模型，所以需要製作模具，中國東莞有很多注塑和模具的生產廠家，預計製作這樣一套模具將花費一萬到三萬人民幣不等。所以能否做出來還要看這套鍵盤能不能衆籌到相應的價錢。</p>

<h3 id="">鍵盤矩陣掃描芯片</h3>

<p>熟悉鍵盤固件的人應該都知道矩陣掃描的工作原理。目前市面上絕大多數的鍵盤都是採用運載於 MCU 的軟件驅動的掃描程序來檢測按鍵事件的。然而 MCU 輪詢矩陣的過程中還要處理別的事情，比如 USB 通訊等等，所以需要分配計算資源，所以軟件驅動的矩陣掃描的頻率非常低，是跟着處理器頻率走的，MCU 的資源緊張的時候響應速度往往都在 5-10ms 級別。一般人難以察覺，但是像我這種對延遲異常敏感的人還是能察覺得到的。</p>

<p>爲了加快按鍵響應速度，我打算採用 Texas Instruments 的 TCA8418E 鍵盤矩陣掃描芯片。這款芯片能夠在 25μs 內掃描一個 8x10 的矩陣並做出響應。通過 I<sup>2</sup>C 端口通信，MCU 只需要在有按鍵事件的時候處理 Interrupt 就行了，並不需要分配資源進行矩陣掃描，效率將大大提高。這樣一來 MCU 甚至能夠進入完全休眠的節能狀態，只留下這塊芯片不停地掃描 Matrix，直到產出一個按鍵事件的 Interrupt 才進行 Wake Up，能大大節省休眠時期的能耗。</p>

<p><img src="https://rix.li/content/images/2016/10/TCA8418E-EVM.jpg" alt="TCA8418E-EVM"></p>

<h3 id="mcu">主控 MCU</h3>

<p>大部分的 DIY 鍵盤爲了方便，都是用的 Hasu 的 AVR 固件，然而 AVR 在各方面來說都比較弱，當鍵盤的功能變多的時候，我們希望能有速度更快、內存更多、功耗不大的選擇。現在很多商業鍵盤裏面都是用的 NXP 的 ARM Cortex-M 系列 MCU。然而就個人而言，我覺得 ARM 架構對於一個 USB 設備而言太過複雜了，開發環境不容易搭建，代碼也比較複雜，很多功能都用不到，而且功耗也比較大。因此這次我想嘗試使用 Texas Instruments 專門爲 USB 產品設計的 MSP430 系列 MCU。</p>

<p><img src="https://rix.li/content/images/2016/10/MSP430F5529-LaunchPad.jpg" alt="MSP430F5529 LaunchPad"></p>

<p>MSP430 雖然沒有 ARM 那麼強大，但是作爲鍵盤的主控芯片是綽綽有餘了，而且價格便宜功耗小。再加上 TI 豐富的開發資源跟開源的軟硬件工具鏈，開發過程相當容易。</p>

<h3 id="">總結</h3>

<p>Axiom Keyboard 將是一款從頭到尾重新設計過的鍵盤，所有的部件都是以商業產品爲目標進行設計的。它的價格也同樣不會便宜，我預計它將會成爲高端玩家的新寵。讓我們拭目以待。</p>]]></content:encoded></item><item><title><![CDATA[How does MSP430 GCC handle interrupt function attribute?]]></title><description><![CDATA[<p>When using MSP430's free and open-source GCC toolchain, we can define interrupt handlers with <code>__attribute__(interrupt(INTERRUPT_NAME_MACRO))</code>. For example:</p>

<pre><code class="language-clike">__attribute__ ( ( interrupt( TIMER1_A0_VECTOR ) ) )  
void TIMER1_A0_ISR( void )  
{
    //Toggle P1.0
    GPIO_toggleOutputOnPin(
        GPIO_PORT_P1,
        GPIO_PIN0
    );
}
</code></pre>

<p>But how exactly does this <code>interrupt</code> magical attribute works?</p>]]></description><link>https://rix.li/how-does-msp430-gcc-handle-interrupt-function-attribute/</link><guid isPermaLink="false">aa7ad32d-cac7-490d-9f95-aeea12ac8e0f</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Tue, 06 Sep 2016 00:35:17 GMT</pubDate><content:encoded><![CDATA[<p>When using MSP430's free and open-source GCC toolchain, we can define interrupt handlers with <code>__attribute__(interrupt(INTERRUPT_NAME_MACRO))</code>. For example:</p>

<pre><code class="language-clike">__attribute__ ( ( interrupt( TIMER1_A0_VECTOR ) ) )  
void TIMER1_A0_ISR( void )  
{
    //Toggle P1.0
    GPIO_toggleOutputOnPin(
        GPIO_PORT_P1,
        GPIO_PIN0
    );
}
</code></pre>

<p>But how exactly does this <code>interrupt</code> magical attribute works? How does the compiler know where exactly to place the interrupt handler's entry address inside the memory, defined by the interrupt vector table? Let's dig it up.</p>

<h3 id="interruptvectoraddresses">Interrupt Vector Addresses</h3>

<p>First of all, let's connect the dots. We know from the MSP430 specification <em><a href="http://www.ti.com/lit/pdf/slau208">SLAU208O, 1.3.6 Interrupt Vectors</a></em> that the interrupt vectors are located in the address range <code>0xFFFF</code> to <code>0xFF80</code>, each takes 2 bytes, or 16 bits. Each interrupt vector's value is actually a 16-bit memory address that points to somewhere stores the code to be executed when the corresponding interrupt was raised.</p>

<p>For example, for the previous <code>TIMER1_A0_VECTOR</code> interrupt, it actually corresponds to the <em>TA1CCR0</em> interrupt flag if you understands the timer module. Thus we can look up the actual interrupt vector address from <a href="http://www.ti.com/lit/gpn/msp430f5529"><em>SLAS590M, 6.3 Interrupt Vector Addresses, Table 6-1</em></a> (assuming we are using MSP430F552X or MSP430F551X family MCUs). It shows us the interrupt vector address of <code>TIMER1_A0_VECTOR</code> is indeed <code>0xFFE2</code>.</p>

<p>Specifically, it means at the memory address of <code>0xFFE2</code> on our MCU's flash, stores the 16-bit value of the memory address of the <code>TIMER1_A0_ISR</code> function, which will be calculated in the linking process by the linker tool. When the timer module raised an interrupt, the MCU will always read in the 16-bit value from <code>0xFFE2</code>, stop and save the current job, and resume the CPU from the address just read in, which transfer the execution to the <code>TIMER1_A0_ISR</code> function.</p>

<p>Therefore, there have to be some sort of means to tell the linker to store the specific function's (future-calculated) address into a (pre-defined) memory address space. Thus a <a href="https://sourceware.org/binutils/docs-2.21/ld/Scripts.html"><em>Linker Script</em></a> can be used to assist the job.</p>

<h3 id="linkerscript">Linker Script</h3>

<p>A linker script is actually a memory and sections definition file to tell the linker how to map the input sections from the input object files to the output sections of the single output object file. It can also export symbols (<a href="https://sourceware.org/binutils/docs-2.21/ld/Source-Code-Reference.html"><em>with only addresses but no values</em></a>) to the C programs sources.</p>

<p>When compiling with MSP430 GCC, a linker script is passed to the linker with <code>-T</code> command line option defined in your Makefile. For example in my case I'm including a linker script with</p>

<pre><code>msp430-elf-gcc -L $(MSP430_GCC_DIR)/include -T msp430f5529.ld ...  
</code></pre>

<p>So the linker script is called <code>msp430f5529.ld</code> resides in the path <code>$(MSP430_GCC_DIR)/include</code>. Actually, all these MCU specific linker scripts were shipped with the <a href="http://www.ti.com/tool/msp430-gcc-opensource">full version of the MSP430 GCC (msp430-gcc-full-*)</a>, or you can download them separately with <code>msp430-gcc-support-files-*.zip</code> also listed in the download page.</p>

<p>If you open the linker script file, you can see the full layout of the device memory including all the interrupt vectors inside a code block called <code>MEMORY</code>. So let's find out where it defines <code>0xFFEA</code>:</p>

<pre><code>MEMORY {  
  ...
  VECT50 : ORIGIN = 0xFFE2, LENGTH = 0x0002
  ...
}
</code></pre>

<p>This line of code simply defines a memory space called <code>VECT50</code>. The number "50" here is just part of a name, like "a", "b", "c" etc. nothing special. What makes it meaningful is the next part. <code>ORIGIN = 0xFFE2</code> specifies the start of the memory space at <code>0xFFE2</code> as we expected, and <code>LENGTH = 0x0002</code> tells the linker this memory space only takes 2 bytes, so that if we put more than that into the space, some errors may occur during linking.</p>

<p>But <code>VECT50</code> is only a memory space reference, like a C pointer. We have to "assign" something to the memory value to make it meaningful. This is done in the next code block <code>SECTIONS</code>.</p>

<pre><code>SECTIONS {  
  ...
  __interrupt_vector_50 : {
    KEEP (*(__interrupt_vector_50))
    KEEP (*(__interrupt_vector_timer1_a0))
  } &gt; VECT50
  ...
}
</code></pre>

<p>WOW, it looks terrifyingly complex. Let's break it down. So the <code>SECTIONS</code> block defines and assigns values to a set of named sections of the output object file. Practically you can use <code>msp430-elf-objdump</code> to dump the section table of the compiled ELF object file:</p>

<pre><code>$ msp430-elf-objdump -h output.elf

Sections:  
Idx Name                  Size      VMA       LMA       File off  Algn  
  0 __interrupt_vector_50 00000002  0000ffe2  0000ffe2  00000572  2**0
                          CONTENTS, ALLOC,    LOAD,     READONLY, CODE
  1 __reset_vector        00000002  0000fffe  0000fffe  00000576  2**0
                          CONTENTS, ALLOC,    LOAD,     READONLY, DATA
...
</code></pre>

<p>You can see after linking, a section named <code>__interrupt_vector_50</code> is exported to the output object file. Its Virtual Memory Address (VMA) and Logical Memory Address (LMA) are both <code>0xFFE2</code>. It explained part of what the above linker script code did. The code <code>__interrupt_vector_50 : {} &gt; VECT50</code> simply declares an output section <code>__interrupt_vector_50</code> to be stored inside memory space defined by <code>VECT50</code>, which starts at <code>0xFFE2</code> and has 2 bytes in size.</p>

<p>What makes it confusing is the inner part. We only defined a section called <code>__interrupt_vector_50</code>, but it's yet empty. To put something inside, we have to map some input sections into this output section.</p>

<p>The term "input section" may be confusing. So let me explain it a little bit. When we compile a C project, the compiler can generate object files for every C source file, such as "a.o" for "a.c", and "b.o" for "b.c" etc. In each object file, code and data are separated into a common set of sections, such as <code>.data</code>, <code>.bss</code>, <code>.heap</code>, <code>text</code> etc. There may also be some compiler defined and even user defined sections in some object files. When we link the intermediate object files, their sections were passed onto the linker as "input sections". The linker uses a set of rules defined by default or through a linker script to map the input sections from their original files to a set of newly defined sections. And the new sections, called "output sections", will be written to the output object file.</p>

<p>So basically, the inner statement <code>*(__interrupt_vector_50)</code> is a wildcard expression that includes input sections named <code>__interrupt_vector_50</code> in all the input object files to the enclosing <code>__interrupt_vector_50</code> output section. Please don't be confused by the naming. The outer <code>__interrupt_vector_50</code> is the name of the output section, and the inner one is for the input section. The function <code>KEEP()</code> informs the compiler's optimizer to keep the mapped section data since it's not referenced by any C code and may be otherwise considered redundant and optimized out.</p>

<p>We can see there's one more statement that includes input sections <code>__interrupt_vector_timer1_a0</code> from all input object files. So basically it works like an alias for input sections <code>__interrupt_vector_50</code> in application level.</p>

<p>Now we know the linker puts the contents in the <code>__interrupt_vector_50</code> or <code>__interrupt_vector_timer1_a0</code> input sections into the output section called <code>__interrupt_vector_50</code> which resides at the interrupt vector at address <code>0xFFE2</code> and takes 2 bytes in space. So the next step is to find out what's actually inside the input sections <code>__interrupt_vector_50</code> or <code>__interrupt_vector_timer1_a0</code>.</p>

<h3 id="exploringwithobjdump">Exploring with <code>objdump</code></h3>

<p>Before we dive deeper into the compiler implementation, let's pause and see what we can get using the <code>msp430-elf-objdump</code> utility. First let's see what's actually inside the interrupt vector address <code>0xFFE2</code>.</p>

<pre><code>$ msp430-elf-objdump -s --start-address=0xFFE2 --stop-address=0xFFE4 output.elf

Contents of section __interrupt_vector_50:  
 ffe2 fe46                                 .F
</code></pre>

<p>We can see that at <code>0xFFE2</code> there stores a 16-bit value <code>0xFE46</code>. As we already know, it's the memory address of the <code>TIMER1_A0_ISR</code> interrupt handler function. Since MSP430 processors are little-endian, the actual address should be <code>0x46FE</code>. Now let's see what's inside that address.</p>

<pre><code>$ msp430-elf-objdump -S --start-address=0x46FE --stop-address=0x4700 output.elf

Disassembly of section .text:

000046fe &lt;TIMER1_A0_ISR&gt;:  
__attribute__( ( interrupt( TIMER1_A0_VECTOR ) ) )  
void TIMER1_A0_ISR( void )  
{
    46fe:          bf 15           pushm   #12,    r15     ;16-bit words
    ...
</code></pre>

<p>As we expected, it leads us to the interrupt handler function. But if you can recall, the value stored in <code>0xFFE2</code> is actually the contents of input sections <code>__interrupt_vector_50</code> or <code>__interrupt_vector_timer1_a0</code>. In this case, it means the value of this input section is indeed <code>0x46FE</code>, and is the memory address of the <code>TIMER1_A0_ISR</code> interrupt handler function.</p>

<p>As I mentioned before, the address of a specific function can only be calculated in the linking process, so it's impossible to hard-code it inside C code, nor in linker scripts. So the only way to put the function address into the memory section's content is through the magical <code>interrupt</code> function attribute implemented in the MSP430 GCC. To understand it, we have to look inside the <a href="http://www.ti.com/lit/gpn/msp430f5529">MSP430 GCC source code</a>. After some quick digging, I found the implementation in <code>gcc/gcc/config/msp430/msp430.c</code>:</p>

<pre><code class="language-clike">void  
msp430_start_function ( FILE *file, const char *name, tree decl )  
{
  tree int_attr;

  int_attr = lookup_attribute ( "interrupt", DECL_ATTRIBUTES ( decl ) );
  if ( int_attr != NULL_TREE )
  {
    tree intr_vector = TREE_VALUE ( int_attr );

    if ( intr_vector != NULL_TREE )
    {
      char buf[101];

      intr_vector = TREE_VALUE ( intr_vector );

      /* The interrupt attribute has a vector value.  Turn this into a
         section name, switch to that section and put the address of
         the current function into that vector slot.  Note msp430_attr()
         has already verified the vector name for us.  */
      if ( TREE_CODE ( intr_vector ) == STRING_CST )
        sprintf ( buf, "__interrupt_vector_%.80s",
                  TREE_STRING_POINTER ( intr_vector ) );
      else /* TREE_CODE (intr_vector) == INTEGER_CST */
        sprintf ( buf, "__interrupt_vector_%u",
                  ( unsigned int ) TREE_INT_CST_LOW ( intr_vector ) );

      switch_to_section ( get_section ( buf, SECTION_CODE, decl ) );
      fputs ( "\t.word\t", file );
      assemble_name ( file, name );
      fputc ( '\n', file );
      fputc ( '\t', file );
    }
  }

  switch_to_section ( function_section ( decl ) );
  ASM_OUTPUT_TYPE_DIRECTIVE( file, name, "function" );
  ASM_OUTPUT_FUNCTION_LABEL ( file, name, decl );
}
</code></pre>

<p>As the comment above suggests, the interrupt attribute takes the interrupt name as its argument. In our example, <code>__attribute__ ( ( interrupt( TIMER1_A0_VECTOR ) ) )</code> takes <code>TIMER1_A0_VECTOR</code> as the interrupt name. It's actually a macro defined in the MSP430 driver library:</p>

<pre><code class="language-clike">#define TIMER1_A0_VECTOR   (50)   /* 0xFFE2 Timer1_A3 CC0 */
</code></pre>

<p>Then the <code>interrupt</code> attribute will create a section with the name prefixing <code>__interrupt_vector_</code> to the interrupt name. So in our example, the created section is called <code>__interrupt_vector_50</code>. As you may recall, this is indeed the input section name we were expecting to pass onto the linker. Finally the <code>interrupt</code> attribute puts the address of the current annotating function, i.e. <code>0x64FE</code> for <code>TIMER1_A0_ISR</code> in our case, into the newly created section.</p>

<h3 id="summary">Summary</h3>

<p>Now we've connected all the dots. Let's wrap it up. Firstly we use <code>__attribute__(interrupt(INTERRUPT_NAME_MACRO))</code> to tell the compile generate a new section named <code>__interrupt_vector_INTERRUPT_NAME_MACRO</code>, storing the memory address of the annotating interrupt handler function. Then during the linking process, we use the TI provided linker script to map all the <code>interrupt</code> generated sections into correct memory spaces in the interrupt vector table defined by the MSP430 specifications.</p>]]></content:encoded></item><item><title><![CDATA[有弾幕（YouDanMu）開坑]]></title><description><![CDATA[<p>跟小夥伴在飯桌上聊起彈幕，突然心血來潮想寫一個能給 YouTube 加上彈幕的 Chrome 插件，於是 YouDanMu 這個坑就誕生了。</p>

<p>後來想了想，我并不希望讓這個插件僅局限于 YouTube，考慮到現在很多 ACG 内容也會推到臉書上，我希望 YouDanMu 能實現一種通用的彈幕插件接口，把大部分視頻網站的播放器抽象到我們内部的通用播放器接口上，還能接入 Bilibili、AcFun 等主流彈幕網站的彈幕數據，甚至跟這些網站的用戶系統進行對接等等。再考慮到現在 Web Extension 規範的流行，我們甚至能同時提供 Chrome、FireFox、Opera 等等的跨瀏覽器支持。</p>

<p>這麽一想，這坑還確實不小，但是深入瞭解一下還是能發現不少前人已經填好的坑，我們衹需要踩著別人填好的坑繼續往前跳就好了。</p>

<p>項目地址：<a href="https://github.com/YouDanMu/YouDanMu">GitHub/YouDanMu</a> <br>
（現有的代碼寫在 <a href="https://github.com/YouDanMu/YouDanMu/tree/experiment/Chrome">experiment/Chrome</a> 這個 branch 上）</p>]]></description><link>https://rix.li/youdanmu-kai-keng/</link><guid isPermaLink="false">3f986666-60fa-434d-8107-d00be616523f</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Fri, 06 May 2016 23:00:51 GMT</pubDate><content:encoded><![CDATA[<p>跟小夥伴在飯桌上聊起彈幕，突然心血來潮想寫一個能給 YouTube 加上彈幕的 Chrome 插件，於是 YouDanMu 這個坑就誕生了。</p>

<p>後來想了想，我并不希望讓這個插件僅局限于 YouTube，考慮到現在很多 ACG 内容也會推到臉書上，我希望 YouDanMu 能實現一種通用的彈幕插件接口，把大部分視頻網站的播放器抽象到我們内部的通用播放器接口上，還能接入 Bilibili、AcFun 等主流彈幕網站的彈幕數據，甚至跟這些網站的用戶系統進行對接等等。再考慮到現在 Web Extension 規範的流行，我們甚至能同時提供 Chrome、FireFox、Opera 等等的跨瀏覽器支持。</p>

<p>這麽一想，這坑還確實不小，但是深入瞭解一下還是能發現不少前人已經填好的坑，我們衹需要踩著別人填好的坑繼續往前跳就好了。</p>

<p>項目地址：<a href="https://github.com/YouDanMu/YouDanMu">GitHub/YouDanMu</a> <br>
（現有的代碼寫在 <a href="https://github.com/YouDanMu/YouDanMu/tree/experiment/Chrome">experiment/Chrome</a> 這個 branch 上）</p>]]></content:encoded></item><item><title><![CDATA[Tor + ZeroNet + IPFS + NameCoin 將成為下一代 Anti-Censorship 的核心技術]]></title><description><![CDATA[<p>我朝網絡的 Censorship 越來越強力，有的人因為不斷地被剝奪獲取信息的權利而變得麻木，但是仍有一部分人是清醒的，這些清醒的人現在非常需要先進的 Anti-Censorship 技術支持來幫助他們保持清醒。</p>

<p>因此我對未來可能成為 Anti-Censorship 核心的技術進行了一些簡單的預測。</p>

<h3 id="tor">Tor</h3>

<p>能夠隱藏用戶 IP 地址的 P2P 加密網絡。以後很有可能所有的 Anti-Censorship 技術都需要基於 Tor Service 來隱藏用戶的 IP 地址，這將成為新的網絡協議層。</p>

<p>但光靠 Tor 是無法對抗 Censorship 的，要知道最嚴厲的 Censorship 是打擊犯罪。連 Silk Road、Silk Road 2 的管理員都陸續落網，證明 Tor 網絡是有缺陷的。<sup><a href="https://leaksource.info/2014/11/09/feds-shut-down-silk-road-2-0-alleged-operator-blake-benthall-aka-defcon-arrested-silk-road-reloaded-appears-within-hours/">[1]</a></sup> 雖然 Tor 能隱藏用戶的身份，但是 Tor</p>]]></description><link>https://rix.li/tor-zeronet-ipfs-namecoin-will-become-the-next-generation-of-anti-censorship-technologies/</link><guid isPermaLink="false">bb1b039e-e40d-48be-a902-b601a7fa1f55</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Mon, 09 May 2016 02:43:50 GMT</pubDate><content:encoded><![CDATA[<p>我朝網絡的 Censorship 越來越強力，有的人因為不斷地被剝奪獲取信息的權利而變得麻木，但是仍有一部分人是清醒的，這些清醒的人現在非常需要先進的 Anti-Censorship 技術支持來幫助他們保持清醒。</p>

<p>因此我對未來可能成為 Anti-Censorship 核心的技術進行了一些簡單的預測。</p>

<h3 id="tor">Tor</h3>

<p>能夠隱藏用戶 IP 地址的 P2P 加密網絡。以後很有可能所有的 Anti-Censorship 技術都需要基於 Tor Service 來隱藏用戶的 IP 地址，這將成為新的網絡協議層。</p>

<p>但光靠 Tor 是無法對抗 Censorship 的，要知道最嚴厲的 Censorship 是打擊犯罪。連 Silk Road、Silk Road 2 的管理員都陸續落網，證明 Tor 網絡是有缺陷的。<sup><a href="https://leaksource.info/2014/11/09/feds-shut-down-silk-road-2-0-alleged-operator-blake-benthall-aka-defcon-arrested-silk-road-reloaded-appears-within-hours/">[1]</a></sup> 雖然 Tor 能隱藏用戶的身份，但是 Tor 提供的 Onion Net 網站協議卻是一種傳統的中心化網站模式，所有 Onion 網站都必須有一台中央服務器提供服務，而且有一批運維團隊要對這台服務器進行維護，也就是說存在所謂的「頭號人物」。FBI 就是從這些「頭號人物」入手，派遣特工滲透到這批管理層內部，從人員方面攻破了這些犯罪組織，然後在法庭上再用 Parallel Reconstruction 重現證據鏈。雖然有傳言說 CMU 研究出了某種方法能追蹤到 Tor 網絡中用戶的身份，甚至在法庭上用這種技術提供了取證。<sup><a href="https://www.techdirt.com/articles/20160225/07295633707/silk-road-20-court-docs-show-us-government-paid-carnegie-mellon-researchers-to-unmask-tor-users.shtml">[2]</a></sup> 但是這還是基於 Tor 服務存在中央服務器的事實才能做到的。</p>

<p>新一代的 Anti-Censorship 技術會繼續使用 Tor 的網絡層，但不會沿用 Onion Net 層。基於 Tor 之上建立的服務將是完全去中心化的 P2P 應用，將不存在中央服務器，也不再會有「頭號人物」一說，就算把所有開發者都抓起來，這種分佈式的網絡仍然能自主運轉，只是沒有了技術支持停止更新了，而不像 Silk Road 那樣人一抓服務器一查就渣都不剩了。</p>

<p>現在已經有這種 Decentralized P2P Anonymous E﹣Commerce 平台出現了，比如 OpenBazaar。<sup><a href="https://openbazaar.org/">[3]</a></sup> 目前這個平台還處於 Beta 版本，所以內容還是很和諧的，但是考慮到現在非法交易在 Onion Net 網絡中沒有一個安穩的棲息之地，這片淨土估計很快就會被污染掉。到時候 FBI 要打擊這種完全沒有中央服務器、沒有領導班子的平台估計會相當頭疼。</p>

<h3 id="zeronet">ZeroNet</h3>

<p>具有動態內容接口的 P2P 分佈式網站框架。沒錯這就是我預測的今後將代替 Onion Net 層的去中心化 P2P Web 服務協議。這個協議本身不考慮對用戶的身份信息進行保護，但是它的設計就是為了能和 Tor 一起搭配使用的匿名分佈式網絡系統，所以用戶想要隱藏身份是可以很輕鬆地進行配置的。</p>

<p>在 ZeroNet 中每個用戶都有一個公鑰加密身份，可以作為用戶的賬戶進行動態內容發佈。ZeroNet 提供了一套動態內容接口，以前的 FreeNet 也是一套去中心化的 Web 層，但是只支持靜態頁面，ZeroNet 的動態網頁支持完美彌補了 Onion Net 和 FreeNet 的兩大缺陷。配合 Tor 網絡層進行匿名處理后就已經能挑戰非常嚴峻的 Censorship 了。</p>

<h3 id="ipfs">IPFS</h3>

<p>提高 P2P 分佈式網絡數據分發效率的 CDN 協議。這套協議的初衷跟 Censorship 沒有什麼關係，它解決的是目前網絡數據分發中存在的現實問題：比如一棟樓裡面有5個人同時觀看一個 YouTube 視頻，但是 YouTube 卻必須把同樣的視頻數據發送5次給這5個人。IPFS 能大大減少這種網絡開銷，它利用 P2P 網絡的原理實現了一個分佈式文件系統，在數據結構上跟 UNIX 的 INODE 文件存儲方式非常類似，每一個 Block 的 Hash 值是固定的，於是就能依靠 P2P 網絡進行分發，這樣同一份視頻數據理論上只要發送一次就能提供同一棟樓的5個用戶觀看，乃至更多。</p>

<p>這種 P2P CDN 協議能為新一代 Anti-Censorship 網絡解決速度和容量的問題。對於 ZeroNet 來說並不適合直接把大文件像多媒體文件存在分佈式節點上，這樣會增加很多不必要的網絡開銷。IPFS 能夠將這些大文件進行按需分配、並且能保證良好的速度。我覺得將來的 P2P Streaming 會使用這種技術。</p>

<h3 id="namecoin">NameCoin</h3>

<p>以比特幣核心技術為基礎的區塊鏈 DNS 系統。我朝最喜歡搞的就是 DNS 污染了，再加上最近出台的域名政策名正言順地給 DNS 污染安上了法律的擋箭牌。這樣一套分佈式的 DNS 系統很好地解決了這個問題。ZeroNet 直接支持 NameCoin 的 .bit 域名解析，整合起來十分方便。再加上 .bit 的設計非常簡單，價格公道，我覺得將來能成為 Anti-Censorship 網絡流行的域名解決辦法。</p>

<h3 id="conclusion">Conclusion</h3>

<p>還有很多 Anti-Censorship 的技術正在如火如荼地開發當中，這其中還有很多更理論的東西，比如更加完善的密碼學套件支持等等。DuckDuckGo 今年捐了 225,000 USD 給這些優秀的 Anti-Censorship 項目。<sup><a href="https://duck.co/blog/post/303/2016-foss-donations-announcement">[4]</a></sup> 如果你不希望活在一個沒有信息自由的世界中，也請偶爾為這些項目捐獻一杯咖啡吧。</p>]]></content:encoded></item><item><title><![CDATA[JavaScript 之「偷天換日術」]]></title><description><![CDATA[<p>在開始填 YouDanMu 這個坑之前我就已經知道了遲早要碰上這種問題，趁著這個機會我來詳細地講一講如何偷到閉包裏面的私有變量。即 JavaScript 之「偷天換日術」。</p>

<p>爲了簡化問題，請考慮下面這段代碼：</p>

<pre><code class="language-javascript">(function(global){
    function GUID(secret) {
        this.secret = secret;
    }
    GUID.prototype.generate = function() {
        return Math.floor((1 + this.secret) * 0x10000000000000).toString(16).toUpperCase();
    };
    GUID.history = [];
    GUID.guid = function() {
        var id;
        do {
            id = (new GUID(Math.random())).generate();
        } while (GUID.history.indexOf(</code></pre>]]></description><link>https://rix.li/steal-the-private-object-in-javascript/</link><guid isPermaLink="false">146ff97b-975b-4048-98bf-c1cc02494130</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Sat, 07 May 2016 00:01:53 GMT</pubDate><content:encoded><![CDATA[<p>在開始填 YouDanMu 這個坑之前我就已經知道了遲早要碰上這種問題，趁著這個機會我來詳細地講一講如何偷到閉包裏面的私有變量。即 JavaScript 之「偷天換日術」。</p>

<p>爲了簡化問題，請考慮下面這段代碼：</p>

<pre><code class="language-javascript">(function(global){
    function GUID(secret) {
        this.secret = secret;
    }
    GUID.prototype.generate = function() {
        return Math.floor((1 + this.secret) * 0x10000000000000).toString(16).toUpperCase();
    };
    GUID.history = [];
    GUID.guid = function() {
        var id;
        do {
            id = (new GUID(Math.random())).generate();
        } while (GUID.history.indexOf(id) &gt;= 0);
        GUID.history.push(id);
        return id;
    };
    global.guid = GUID.guid;
})(window);
</code></pre>

<p>這是一個簡單的 GUID 生成器（爲了簡單并沒有按照 RFC 4122 規範來寫，請不要使用）。他會向外部暴露一個 <code>window.guid()</code> 函數，每次調用時它會生成一個隨機數作爲種子， <code>new</code> 一個 <code>GUID</code> 對象，產生一段 GUID 碼，如果這段 GUID 碼存在于 <code>GUID.history</code> 中則重新生成，以保證生成 GUID 碼的唯一性（爲了簡單這個唯一性衹在這段程序的生命周期中有效）。</p>

<p>我們現在要寫一個瀏覽器插件，在不改動上面這段代碼的情況下，我們可以在這段代碼執行前後的全局作用域中添加我們的代碼，我們最終的目的是拿到閉包中 <code>GUID</code> 私有對象（Function）的讀寫權限。即我們能操縱 <code>GUID.history</code>，比如能夠刪掉之前生成過的 GUID 碼記錄讓唯一性失效等等。</p>

<p>這個簡化版的問題正是我在對 YouTube 内部 API 進行劫持時遇到的等同問題，如果我們能解決這個簡化版的問題，那麽也就能成功劫持到 YouTube 的内部 API 對象了。</p>

<h3 id="">「偷天換日術」</h3>

<p>我們先來看看這個「偷天換日術」的實現代碼：</p>

<pre><code class="language-javascript">/* 「偷天換日術」的實現部分 */
Object.defineProperty(Function.prototype, 'guid', {  
    __proto__: null,
    enumerable: true,
    configurable: false,
    get: function _getter() {
        return this._guid;
    },
    set: function _setter(value) {
        value._this = this;
        this._guid = value;
    }
});
/* 這是原來的代碼 */
(function(global){
    function GUID(secret) {
        this.secret = secret;
    }
    GUID.prototype.generate = function() {
        return Math.floor((1 + this.secret) * 0x10000000000000).toString(16).toUpperCase();
    };
    GUID.history = [];
    GUID.guid = function() {
        var id;
        do {
            id = (new GUID(Math.random())).generate();
        } while (GUID.history.indexOf(id) &gt;= 0);
        GUID.history.push(id);
        return id;
    };
    global.guid = GUID.guid;
})(window);
/* 結果演示 */
window.guid();  
console.log(window.guid._this.history);  
</code></pre>

<p>可以先在瀏覽器裏面跑一跑體驗一下，然後可以跟蹤調試一下并嘗試理解。在 _getter 和 _setter 的函數裏面設置斷點，在中斷的時候追溯一下 Call Stack 嘗試理解一下工作原理。</p>

<p>通過跟蹤我們能發現，這個「偷天換日術」最關鍵的一步發生在 <code>GUID.guid = function() ...</code> 這裏。我們知道，<code>GUID</code> 是一個 Function，不，準確來説是 Function 類的一個實例，相當於 <code>new Function()</code> 的結果，所以 <code>GUID</code> 應該繼承了 Function 這個類的 <code>prototype</code>，也就是說 <code>GUID.__proto__ === Function.prototype</code>。如果這裏不理解，請溫習一下 <a href="http://javascript.ruanyifeng.com/oop/encapsulation.html">JavaScript 的面嚮對象編程以及封裝方法（by 阮一峰）</a>。</p>

<p>其次我們知道，在 JavaScript 中對一個對象 <code>x</code> 的屬性 <code>y</code> 進行賦值的流程是這樣的：</p>

<ol>
<li>如果在當前對象的直屬屬性中存在名爲 <code>y</code> 的屬性，則調用 <code>y</code> 相應的 setter 將新的值傳進去，同時設置這個 setter 的 this 爲 <code>x</code>；如果該屬性存在但是沒有定義 setter 則直接賦值在它的 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Description">Property Descriptor</a> 的 <code>value</code> 屬性上。  </li>
<li>如果在當前對象的直屬屬性中找不到名爲 <code>y</code> 的屬性，則訪問當前對象的 <code>__proto__</code> 屬性，如果 <code>__proto__ != undefined</code> 則把當前對象替換爲該 <code>__proto__</code> 屬性，然後重複步驟 1。  </li>
<li>如果 <code>__proto__</code> 訪問發生循環，或最後訪問到的 <code>__proto__ == undefined</code>，則終止 <code>__proto__</code> 鏈訪問。這種情況下，JavaScript 才會重新回到最開始的對象 <code>x</code> 上面，在它的直屬屬性中定義一個新的名爲 <code>y</code> 的屬性，并賦值在它的 Property Descriptor 的 <code>value</code> 屬性上面。</li>
</ol>

<p>我們把這種遞歸式的屬性查詢訪問稱爲 Prototype Chain。我們可以把整個流程用 JavaScript 重現一遍：</p>

<pre><code class="language-javascript">function assignProperty(object, property, value) {  
    var current = object;
    var protoChain = [current];
    while (true) {
        if (current.hasOwnProperty(property)) {
            var descriptor = Object.getOwnPropertyDescriptor(current, property);
            if (typeof descriptor.set === 'function') {
                descriptor.set.call(object, value);
            } else {
                descriptor.value = value;
                Object.defineProperty(current, property, descriptor);
            }
            break;
        } else {
            if (current.__proto__ != undefined
                &amp;&amp; protoChain.indexOf(current.__proto__) &lt; 0) {
                current = current.__proto__;
                protoChain.push(current);
            } else {
                Object.defineProperty(object, property, {
                    configurable: true,
                    enumerable: true,
                    value: value,
                    writable: true
                });
                break;
            }
        }
    }
}
</code></pre>

<p>這段代碼將讓 <code>x.y = 'value'</code> 等價于 <code>assignProperty(x, 'y', 'value')</code>，可以試試。</p>

<p>看到這裏，我想很多人都已經明白了這個「偷天換日術」的秘密了。最關鍵的一點，在於 Prototype Chain 賦值操作中，一個屬性擁有 setter 的時候，<strong>調用 setter 的同時會將 <code>this</code> 設置爲最上級的對象</strong>。也就是説，如果我們能在一個對象 <code>x</code> 的某一級 <code>__proto__</code> 中定義一個帶有我們自己設置的 setter 的屬性 <code>y</code>，所有對 <code>x.y</code> 進行的賦值操作，除了會把新的值傳給我們的 setter 之外，還會把 setter 調用的 <code>this</code> 設成 <code>x</code>，所以我們在 setter 中訪問 <code>this</code> 就是在訪問 <code>x</code>。明白了這點，這個「偷天換日術」就不是什麽秘密了。</p>

<p>最後的問題就是如何在 <code>GUID</code> 的某層 <code>__proto__</code> 中插入一個我們提前制定好了 setter 的 <code>guid</code> 屬性。之前我們講到了，<code>GUID</code> 繼承了 <code>Function.prototype</code>，用 JavaScript 重現一遍 <code>function GUID() {...}</code> 的内部實現就是：</p>

<pre><code class="language-javascript">var GUID = Function('/* function body ... */');  
GUID.__proto__ = Function.prototype;  
// 真正的内部實現還有很多別的内容，此處簡化省略
</code></pre>

<p>所以你看，我們衹要修改一下 <code>Function.prototype</code> 這個全局變量，就能在 <code>GUID</code> 的 Prototype Chain 上面隨意地添加「陷阱」了。好了我們再回頭看看我們的「偷天換日術」的實現：</p>

<pre><code class="language-javascript">/* 「偷天換日術」的實現部分 */
Object.defineProperty(Function.prototype, 'guid', {  
    __proto__: null,
    enumerable: true,
    configurable: false,
    get: function _getter() {
        return this._guid;
    },
    set: function _setter(value) {
        value._this = this;
        this._guid = value;
    }
});
</code></pre>

<p>可以看到，我在 <code>Function.prototype</code> 上面定義了一個同時帶有 setter 和 getter 的屬性 <code>'guid'</code>。於是在原來代碼執行到 <code>GUID.guid = function() ...</code> 這裏的時候，就會執行我們定義的 setter，把新的值（一個 Function）作爲參數傳進來，<strong>同時把 setter 的 <code>this</code> 設置成 <code>GUID</code></strong>。於是在這個 setter 中我們就能進行「偷天」：把 <code>this</code>，也就是 <code>GUID</code> 放在 <code>value</code> 的 <code>_this</code> 屬性上，注意這個 <code>value</code> 最後會被暴露到全局作用域上，我們就能通過它把 <code>GUID</code> 一起帶出去。其實我這麽寫衹是爲了好看，我們完全可以在這個 setter 裏面直接把 <code>this</code> 賦值給一個全局變量，比如 <code>window.GUID = this</code>，就直接偷到 <code>GUID</code> 了。爲了優雅，我還加上了「換日」：setter 會把傳進來的值存在一個別的屬性 <code>_guid</code> 上，然後我寫了一個 getter，會把 <code>_guid</code> 返回回去。所以在原來的代碼 <code>global.guid = GUID.guid</code> 這裏我們得到的 <code>guid</code> 實際上來自於 <code>GUID._guid</code>，而且上面還帶著我們「偷天」回來的 <code>_this</code> 屬性，一起被傳出了全局作用域。</p>

<p>那麽一式 JavaScript 之「偷天換日術」就是這樣了。</p>]]></content:encoded></item><item><title><![CDATA[FRDM-K22F: Debugging with External Segger J-Link Debugger]]></title><description><![CDATA[<p>This article explains how you can connect and debug the FRDM-K22F board with an external <a href="https://www.segger.com/jlink-debug-probes.html"><strong>Segger J-Link</strong></a> debugger.</p>

<h4 id="1disconnectj10andj13">1. Disconnect J10 and J13</h4>

<p>From the <a href="http://cache.freescale.com/files/microcontrollers/doc/user_guide/FRDMK22FUG.pdf"><strong>FRDM-K22F Freedom board for Kinetis K22 Hardware</strong></a> document Section 13, it points out that</p>

<blockquote>
  <p>The debug interface on the MK22FN512VDC12 MCU is a serial wire</p></blockquote>]]></description><link>https://rix.li/frdm-k22f-debugging-with-external-segger-j-link-debugger/</link><guid isPermaLink="false">11c966a2-8459-45f7-9068-9c638f151b47</guid><dc:creator><![CDATA[Rix]]></dc:creator><pubDate>Wed, 16 Dec 2015 23:21:00 GMT</pubDate><content:encoded><![CDATA[<p>This article explains how you can connect and debug the FRDM-K22F board with an external <a href="https://www.segger.com/jlink-debug-probes.html"><strong>Segger J-Link</strong></a> debugger.</p>

<h4 id="1disconnectj10andj13">1. Disconnect J10 and J13</h4>

<p>From the <a href="http://cache.freescale.com/files/microcontrollers/doc/user_guide/FRDMK22FUG.pdf"><strong>FRDM-K22F Freedom board for Kinetis K22 Hardware</strong></a> document Section 13, it points out that</p>

<blockquote>
  <p>The debug interface on the MK22FN512VDC12 MCU is a serial wire debug (SWD) port with trace output capability. There are two debug interfaces on the FRDM-K22F: an onboard OpenSDAv2 circuit (J5) and a K22F direct SWD connection via a 10-pin header (J11).To use an external debugger, such as J-Link on J11, you may need to disconnect the OpenSDAv2 SWD circuit from the K22F by removing jumpers J10 and J13.</p>
</blockquote>

<p>Therefore, to use with external J-Link debugger, we need to disconnect the J10 and J13 jumpers to disable the on-board OpenSDA debugging functionality, but instead allowing external connections.</p>

<p><img src="https://rix.li/content/images/2015/12/FRDM-K22F_J10_J13.jpg" alt="FRDM-K22F J10 and J13 Locations"></p>

<p>On the board we can find the two jumpers J10 and J13 connected by two <a href="https://en.wikipedia.org/wiki/Jumper_%28computing%29"><strong>jumper shunts</strong></a>.</p>

<p><img src="https://rix.li/content/images/2015/12/FRDM-K22F_unplug_shunt.jpg" alt="FRDM-K22F Unplug J10 and J13"></p>

<p>We can simply unplug the jumper shunts to disconnect J10 and J13.</p>

<h4 id="2connectjlinktok22fswddebuginterface">2. Connect J-Link to K22F SWD Debug Interface</h4>

<p><img src="https://rix.li/content/images/2015/12/FRDM-K22F_SWD_interface.jpg" alt="FRDM-K22F SWD Interface Location"></p>

<p>From the <a href="https://cache.freescale.com/files/soft_dev_tools/doc/user_guide/FRDM-K22F-QSG.pdf"><strong>FRDM-K22F Quick Start Guide</strong></a> pg.2, we can find out the SWD interfaced located on the board.</p>

<p><img src="https://rix.li/content/images/2015/12/FRDM-K22F_SWD_numbering.jpg" alt="FRDM-K22F SWD Interface Numbering"></p>

<p>And then, on the bottom side of the board, we can find out the pin numbering of the SWD interface.</p>

<p><img src="https://rix.li/content/images/2015/12/FRDM-K22F_SWD_schematic.jpg" alt="FRDM-K22F SWD Schematic"></p>

<p>From the <a href="http://cache.freescale.com/files/microcontrollers/doc/user_guide/FRDMK22FUG.pdf"><strong>FRDM-K22F Freedom board for Kinetis K22 Hardware</strong></a> document Section 3.2.1 we can find out the pin assignment schematic of the SWD interface.</p>

<p><img src="https://rix.li/content/images/2015/12/FRDM-K22F_SWD_pins.jpg" alt="FRDM-K22F SWD Pin Connection"></p>

<p>So here is the pin assignment for J-Link SWD connection port, numbers in orange are the pin numbers of the SWD interface on the FRDM-K22F board.</p>

<p><img src="https://rix.li/content/images/2015/12/FRDM-K22F_J-Link-_connection.jpg" alt="FRDM-K22F J-Link SWD connection"></p>

<p>You can connect these pins manually to your J-Link debugger, but the simplest way is to buy <a href="https://www.adafruit.com/products/2094"><strong>this J-Link adapter</strong></a> to connect them directly.</p>

<p>Just be careful at the direction of the connector plugging on the board as there's no indication slot for you.</p>

<h4 id="3debuggingwithjlinkdebugger">3. Debugging with J-Link Debugger</h4>

<p>Now plugin your J-Link device to your computer, and launch the J-Link Debugger program.</p>

<p><img src="https://rix.li/content/images/2015/12/Screen-Shot-2015-12-16-at-6-08-07-PM.png" alt="J-Link Debugger New Project Wizard"></p>

<p>In the new project wizard window, click the "..." button under "Device" option.</p>

<p><img src="https://rix.li/content/images/2015/12/Screen-Shot-2015-12-16-at-6-08-42-PM.png" alt="J-Link Debugger Device Selection"></p>

<p>In the popped up window, search and select <strong><em>Freescale MK22FN512xxx12</em></strong> then click "OK". Then click "Next".</p>

<p><img src="https://rix.li/content/images/2015/12/Screen-Shot-2015-12-16-at-6-08-49-PM.png" alt="J-Link Debugger Connection Setting"></p>

<p>In the <em>Connection Setting</em> window, select <strong><em>Target Interface</em></strong> as <strong><em>SWD</em></strong>. Type your J-Link device's <strong><em>Serial No</em></strong> which you can find on the bottom of the device. Then click "Next".</p>

<p><img src="https://rix.li/content/images/2015/12/Screen-Shot-2015-12-16-at-6-08-56-PM.png" alt="J-Link Debugger Data File"></p>

<p>Click "Finish".</p>

<p><img src="https://rix.li/content/images/2015/12/Screen-Shot-2015-12-16-at-6-09-20-PM.png" alt=""></p>

<p>Now you have successfully connected your FRDM-K22F board with an external Segger J-Link debugger. Try load a program and start debugging.</p>]]></content:encoded></item></channel></rss>