星期日, 11月 16, 2008

今年夏天最大的成就

我不曾請過游泳教練,所有的泳式就靠著親朋好友的教導完成自由式、蛙式以及立泳,好歹練成丟到水池撐個幾十分鐘淹不死的程度。至於姿勢好不好看、速度快不快、游起來效率高不高就不在要求範圍內了。

今年夏天,尤於老婆在社區的泳池內請救生員,我就趁這個機會去晨泳,晨泳時經常只有我一個人獨享整個泳池。於是我就請救生員泳姿的問題。救生員(其實是老闆)相當在行,一下就指出我姿勢的錯誤地方,使得我原先游的相當慢的自由式大有進步。原先我的自由式只要游個五十公尺就覺得很喘了,經過指導後,游個二百公尺大概不成問題,不過因為還沒有養成習慣的關係,很多時候會到了要換氣的時機而忘了換氣而導致換氣不順。這個問題我覺得不大,畢竟是習慣的養成,只要多加練習就可以了,總比之前是連方法都不曉得的好太多。

二弟將今年夏天的後期的我的自由式拍了下來,說實在的,我自己都覺得進步非常多,很得意呢。

Treo 650

Palm這間不長進的公司多年前推出Treo 180 PDA phone (單色160*160 resolution), Treo 270 (彩色160*160 resolution),演化到Treo 600 (彩色160*160 resolution, 不可更換電池)。我一直都不曾心動過,一直到推出這支劃時代的手機,可說是改進了所有的缺點。 彩色320 *320 解析度,可更換的電池,再加上超有涷頭的電池,我充一次電大約可以使用四天。所以雖然拿起來像磚頭,可是反應速度快,功能強大。剛上市時價位約在台幣二萬二左右,我在2005年三月份參加Palmislife的團購以入手價18000購得。

三年半過去後,新的手機如雨後春筍般的冒出,Palm這間不長進的公司只再推出了因為Treo 650是含鉛製程的無法在歐盟販售的Treo 680以及2008年推出的低價小巧版本Centro。我看著外殼越來越舊的Treo 650,心中完全沒有想要換機的念頭,只覺得這支手機怎麼這麼耐用又好用。這在拍賣網站上可以證明,只要是堪用的Treo 650手機,拍賣價沒有低於五千元的。這在電子產業裡真是不可思議的高價。

沒想到因為原廠皮套的損壞,讓我的外殼急速損毀,等到我查覺到是因為皮套破損的關係導致外殼的過度磨擦後,外殼已經破裂了,還好以前的同事讚助我一組外殼。結果我在換完外殼後,一時腦殘手賤又拿起WD-40往LCD screen噴,這一噴整個LCD screen就被我噴成暗暗的。外殼雖然換新,但LCD確變成無亮度,隨後再網上四處尋找有無可換的LCD,甚至曾經想要送原廠維修花上USD170美金更換良品確因為這支當初是美規電信業者機而不納入Palm的維修體系內而作罷。最終於大陸託小弟買得取代零件。但是這大陸賣的零件不是很好,會有觸控偏移的問題(果然是黑心商品的集散地啊),再加上因為嘗試拆裝LCD module可能過於多次,我的Treo 650終於宣告掛點,電話雖然能接能打,但螢幕已經變成完全無輸出了。所有的努力在這裡終於劃下句點,我的Treo 650搶救大作戰計畫終告失敗。

後續:面對這支服役我整整三年半的手機,心中說真的還是有感情在的。為了能讓它能繼續發揚光大,我在PalmIsLife網站上以殺肉機的名養義賣出800元價位,捐款就直接充做網站的經費了。

Treo 650 LCD module
剛換完新殼但是LCD暗淡的Treo 650

再補上之前外殼已經不行的老Treo 650

KICX0899

星期二, 8月 19, 2008

Identify USB/IDE/SD Mass Storage Device (Disk Drivers)

Mass Storage Device會被認成是一個Disk,如果要知道這顆Disk是屬於USB或是SATA/IDE Interface,透過標準C Library或是File Management或是Disk Management都無法得知,這些資訊被隱藏起來了。

我寫了一個C++ Class利用Windows Setup API取得系統內有幾個USB Type的Disk。如要判斷是否為Secure Digital或是IDE可自行另行判斷。

massstorage.h

#pragma once

/**
This is the Mass Storage Device Class Library.

Support Help Function to Mass Storage Class.
*/
class CMassStorage
{
public:
CMassStorage(void);
~CMassStorage(void);
/**
Identify a volumn name is exist or not.
\param volname input volumn name
\return true if exist, 0 do not exist.
*/
int isVolumnNameExist(const CString & volname);
/**
Overloaded help function, to supply more argument
\param volman to be identify
\param drv driver letter if the identified voluman exist.
\return true if exist, 0 do not exist.
*/
int isVolumnNameExist(const CString & volname, CString & drv);
int getUSBMsc(void);
};




massstroage.cpp

#include "StdAfx.h"
#include "MassStorage.h"

#include
#include
#include

CMassStorage::CMassStorage(void)
{
}

CMassStorage::~CMassStorage(void)
{
}


int CMassStorage::isVolumnNameExist(const CString & volname)
{
CString dummyDrv;
return isVolumnNameExist(volname, dummyDrv);
}

int CMassStorage::isVolumnNameExist(const CString & volname, CString & drv)
{
TCHAR disk[MAX_PATH]= _T("C:\\");
TCHAR drvltr; // driver letter
TCHAR buf[MAX_PATH];

// scan the disk from C to Z
for (drvltr = 'C'; drvltr <= 'Z'; drvltr++) {
disk[0] = drvltr;
DWORD dummy;
if (GetVolumeInformation(disk, buf, MAX_PATH, &dummy, &dummy, &dummy, NULL, 0)) {
// the driver exist, check the logical volname.
if (volname.CompareNoCase(buf) == 0) {
drv = disk;
return TRUE;
}
}
}
return FALSE;
}

/**
Return the USB mass storage device number
If there are any errors, the returned USB number is still 0.
This function check the device identify if prefixed by "USBSTOR"
\return number of USB mass storage device.
*/

/**
For other GuidClass, search Microsoft for "System-Supplied Device Setup Classes"
*/
DEFINE_GUID(driverGuidConst, 0x4d36e967, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);


int CMassStorage::getUSBMsc(void)
{
HDEVINFO hdevinfo;
GUID driverGuid = driverGuidConst;
int nousb = 0;
const static WCHAR USB_MASS[]=_T("USBSTORE");

// Get the device setup class handler, for disk driver.
if (INVALID_HANDLE_VALUE != (hdevinfo = SetupDiGetClassDevs(&driverGuid, NULL, NULL, DIGCF_PRESENT ))) {
// Retrieve every instance of this class.
int index = 0;
SP_DEVINFO_DATA data;
data.cbSize = sizeof(SP_DEVINFO_DATA);

while (SetupDiEnumDeviceInfo(hdevinfo, index, &data)) {
ULONG buflen;
// Get the size of device ID buffer.
if (CR_SUCCESS == CM_Get_Device_ID_Size(&buflen, data.DevInst, 0)) {
// if the device is exist, then buflen > 0.
if (buflen > 0) {
WCHAR * buf = new WCHAR[buflen];
CM_Get_Device_ID(data.DevInst, buf, buflen, 0);
if (0 == wcsncmp(USB_MASS, buf, sizeof(USB_MASS)/sizeof(WCHAR))) {
nousb++;
}
delete buf;
}
}
index++;
}
// must free the handle to device information.
SetupDiDestroyDeviceInfoList(hdevinfo);
}

return nousb;
}


星期三, 8月 13, 2008

如何在Blogger裡內嵌程式碼

忘了那裡看到的,原始程式的出處在http://code.google.com/p/google-code-prettify/

使用方法很簡單,可以參考http://google-code-prettify.googlecode.com/svn/trunk/README.html

簡略介紹如下:< >
  1. Download source code, 放在一個固定伺服器,目前我有一個Hinet Myweb 伺服空間,在可預見的未來我大概都不會取消,所以你可以直接使用,位置在http://double.myweb.hinet.net/web/prettify.css and http://double.myweb.hinet.net/web/prettify.js
  2. 在你的Google Template裡,加上


  3. <link href="http://double.myweb.hinet.net/web/prettify.css"
    type="text/css" rel="stylesheet" />
    <script type="text/javascript"
    src="http://double.myweb.hinet.net/web/prettify.js">
    </script>


    在你template裡,尋找<body>,改成

    <body onload="prettyPrint()"> ......</body>


  4. 當要post code時,直接使用以下格式,其中的lang-html可以為lang-cpp, lang-html....等格式。


  5. <pre class="prettyprint lang-html">...</pre>



目前支援的語言格式有"c", "cc", "cpp", "cs", "cyc", "java", "bsh", "csh", "sh", "cv", "py", "perl", "pl", "pm", "rb", "js", "html", "html", "xhtml", "xml", "xsl"。 對我來說已經相當夠用了。

星期一, 6月 23, 2008

nop? 為什麼程式裡有nop?

今天心情不太好,就多寫一些。

常寫Firmware(韌體)的人應該很常看到這個指令。不是以組合語言的指令nop,就是以c函數_nop_()的方式存在。nop望文生義,是no operation的縮寫。可是電腦的存在就是要來計算的,什麼時候要用到nop呢?

第一個情況是用來做短暫的delay,尤於這種delay在不同時脈上很難計算,所以通常適用於短期的等候上,這種情形不在今天的討論內。

我想要提的是,尤於firmware經常要存取硬體暫存器(register),其實通常在先人或前輩的慘痛教訓之後,你會看到如下的程式。

int rega _at_ 0x800; // 這是fixed address hardware register
rega = 0x55;
_nop_();
rega = 0x80;


這程式是要對某個register下一連串的指令,但是為什麼會中間加一段nop?
先人慘痛教訓千萬不能隨意修改,看不懂的code決對不要去動,你如果覺得這段code毫無義意就輕易拿掉你就會發覺原本會跑的程式不會動了。甚至你拿起LA仔細量起timing都不會發覺為什麼有需要在兩行指令內加上delay,硬體的速度並沒有那麼慢,那這是為什麼呢?


rega = 0x55;
rega = 0x80;


大部份的原因是因為compiler是optimize過的,它認為rega一個記憶體變數,你先寫0x55, 下一行指令又直接設成0x80,夠聰明的編譯器就自動省略第一行指令。變成

rega = 0x80;


那當然不會運作,於是工程師就加上nop指令,讓不是那麼聰明的編譯器不要將這行指令優化省略掉。就是你看到的結果。

故事到這裡還沒有結束

對於解決這種問題加上nop並不是正解,那怎麼辦呢?你會碰到的問題聰明的工程師早就有人跟你遇到同樣的問題。C/C++ 為語言設計了volatile modifier。這是修飾data storage的保留字,宣告變數為volatile就是宣稱此變數為揮發性的,也就是它的值是有可能隨外在環境改變,相對一般變數,你只要一存入,不去改它它會一直保持住,這種變數是non-volatile,編譯器可以任意優化不用怕出錯。而針對這種可能會隨著時間變動的register,你可以宣告成volatile,強制編譯器每次看到此變數一定要去做動作,這樣才不會出錯。

所以我們的程式最終的結果變成如此

volatile int rega _at_ 0x800; // 這是fixed address hardware register
rega = 0x55;
rega = 0x80;


volatile modifier不只用在硬體變數,只要有變數在你的thread內執行期間內有可能被改變(或者是被另一個thread),都適用加上這個volatile關鍵字來確保你程式的正確執行。