kowala's home

kowala's home
這裡是我的學習筆記,陸續增加中。
http://kowala21.blogspot.com

2016-06-06

BCB6 - 2D實驗室 移動畫素

上一篇 2D實驗室,簡單介紹了畫素產生,我們使用了圖紙物件 TBitmap 來存放畫素。本篇接著介紹簡單地處理畫素移動,電腦螢幕的座標系統,是以左上為原點(0,0),向右x遞增,向下y遞增,如下圖左上角所示。


若我們想要圖形由右下朝左上移動,如上圖,水平x減少80個畫素,垂直y減少80個畫素,這樣就可以做到由右下朝左上移動。

好了,知道原理後該如何做?我希望在我們的實驗室中,按下move按鈕,圖形會朝左上移動,按下back按鈕,圖形會跑回來,如下圖。

那我們必須在move按鈕的按下事件中,寫右下朝左上移動,然後在back按鈕的按下事件中,寫左上朝右下移動。
觀察上圖,我們要移動綠色框所圈起來的畫素,那我們要知道綠色框的左上角點與圖紙的原點(左上角) 偏移多少畫素,綠色框的寬與高是多少畫素,然後每個畫素x-80,y-80,把它寫回圖紙物件 myLab (TBitmap物件),再把 myLab 指給Image1物件,這樣就會顯示移動的樣子了。

上述說明可以知道,為了移動這些像素,我們要知道好多參數,
綠色框偏移值
綠色框的寬與高
要移動多少畫素
...

    int ori_w;    //原圖寬
    int ori_h;    //原圖高
    int off_x;    //矩形對原點偏移x(左上點)
    int off_y;    //矩形對原點偏移y
    int box_w;    //框寬度限制
    int box_h;    //框高度限制
    int mov_x;    //tar x  矩形移動到(左上點)
    int mov_y;     //tar y

我不想直接在按鈕的事件中撰寫代碼,這會使程式看起來凌亂,我想要在事件中只寫參數,然後把參數傳遞給我們的LIB去處理,我們可以使用結構體來簡化參數的傳遞,也就是我只需傳遞結構體的指標就可以了。做法如下,

1.在 mylib.h中宣告結構體
struct coordinate{ //參數結構體宣告
    int ori_w;    //原圖寬
    int ori_h;    //原圖高
    int off_x;    //矩形對原點偏移x(左上點)
    int off_y;    //矩形對原點偏移y
    int box_w;    //框寬度限制
    int box_h;    //框高度限制
    int mov_x;    //tar x  矩形移動到(左上點)
    int mov_y;     //tar y
};

2.順便宣告結構體指標
coordinate *coor;

3.在mainfrm.cpp中,新增一個結構體變數
//new一個結構體參數
coor = new coordinate;

4.在按鈕的事件中給予座標值,呼叫函式moveTo(),把結構體指標 coor 及圖紙物件指標 myLab 傳進去,移動處理後,再把 myLab 指給Image1物件。

//move 按鈕的事件 右下朝左上
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    coor->ori_w=640;
    coor->ori_h=480;
    coor->off_x=240;    //矩形對原點偏移x(左上點)
    coor->off_y=180;    //矩形對原點偏移y
    coor->box_w=160;
    coor->box_h=120;
    coor->mov_x=160;    //tar x  = off_x-80
    coor->mov_y=100;     //tar y = off_y-80
    moveTo(myLab,coor);
    Image1->Picture->Bitmap=myLab;
}

//back 按鈕的事件 左上朝右下
void __fastcall TForm1::Button3Click(TObject *Sender)
{
    coor->ori_w=640;
    coor->ori_h=480;
    coor->off_x=160;    //矩形對原點偏移x(左上點)
    coor->off_y=100;    //矩形對原點偏移y
    coor->box_w=160;
    coor->box_h=120;
    coor->mov_x=240;    //tar x  = off_x+80
    coor->mov_y=180;     //tar y = off_y+80
    moveTo(myLab,coor);
    Image1->Picture->Bitmap=myLab;
}

在 mylib.h中,移動處理函式moveTo()代碼。
//move to
void moveTo(Graphics::TBitmap *scr,coordinate *co){
    Graphics::TBitmap *tmp;//開一塊 mem
    tmp = new Graphics::TBitmap();
    tmp->PixelFormat = pf24bit; //bmp
    tmp->Height = co->ori_h;//圖紙 h
    tmp->Width = co->ori_w;//圖紙 w
    int i,j;
    int len=co->ori_w*3;//line = (byte*) {B,G,R},{B,G,R}...
    //設定底色
    for(j=0;j<co->ori_h;j++){//initial value
        memset(tmp->ScanLine[j],clBlack,len);//clBlack=0=0x00,clWhite=255=0xff
    }
    //開始複製 moveto tmp
    for(i=0;i<co->box_w;i++){
        for(j=0;j<co->box_h;j++){
            tmp->Canvas->Pixels[i+co->mov_x][j+co->mov_y] =
            scr->Canvas->Pixels[i+co->off_x][j+co->off_y];
        }
    }
    //覆蓋回去 scr
    for(j=0;j<co->ori_h;j++){
        memcpy(scr->ScanLine[j],tmp->ScanLine[j],len);
    }
    delete tmp;
}

這樣就完成了

範例程式 IMGLab.rar

2016-04-05

BCB6 - 2D實驗室

本篇就是開一塊記憶體,隨便塗鴉,然後顯示出來,主要是做個環境來測試一些繪圖演算法,當然,你要當作秀圖機,開圖檔也可以的。

首先用 BCB 開一個 vcl 專案,詳細請參考前面文章
BCB6 視窗程式入門
http://kowala21.blogspot.tw/2013/01/bcb6.html

接著新增兩個按鈕,及一個 TImage 物件

把 TImage 物件屬性改成
Height 480
Width 640
這就是我們的工作區,但它現在是空的,我們還要新增TBitmap 物件,並指給它。
我們先新增個 mylib.h ,這樣會使程式看起來較簡潔。
 在專案右鍵,然後新增個 h檔,再改檔名為 mylib.h

把它引入, #include "mylib.h"
我們可以新增個圖紙物件 TBitmap,並指給TImage 物件,並為它設定屬性,指定底色。
看起來就像這樣。
這樣看起來很單調,為它加一些其他東西, 我希望能在 mylib.h 中完成,所以必須寫個函式,把 TBitmap 物件傳進去。

 然後我們可以在 mylib.h 中為所欲為,我為它加了框,畫了X,順便寫幾個字。

mylib.h

code如下
void initSCR(Graphics::TBitmap *buf){
    int w,h,cx,cy,left, top, right, bottom;
    //先在中間畫個框 160*120 TRect(left, top, right, bottom)
    buf->Canvas->Pen->Color = clGreen;//綠色
    buf->Canvas->Brush->Style = bsClear;//框中間透明 Rectangle
    h=buf->Height;//480 工作區寬度
    w=buf->Width;//640 工作區高度
    cx=w/2;    cy=h/2;    //320*240 中心點
    left=cx-(160/2);//240
    top=cy-(120/2);//180
    right=cx+(160/2);//400
    bottom=cy+(120/2);//300
    buf->Canvas->Rectangle(TRect(left,top,right,bottom));//開始畫
    //中間畫 X,用描點的畫
    buf->Canvas->Pixels[cx-3][cy-3] = clWhite;
    buf->Canvas->Pixels[cx-2][cy-2] = clWhite;
    buf->Canvas->Pixels[cx-1][cy-1] = clWhite;
    buf->Canvas->Pixels[cx][cy] = clWhite;//center point (320,240)
    buf->Canvas->Pixels[cx+1][cy+1] = clWhite;
    buf->Canvas->Pixels[cx+2][cy+2] = clWhite;
    buf->Canvas->Pixels[cx+3][cy+3] = clWhite;

    buf->Canvas->Pixels[cx-3][cy+3] = clWhite;
    buf->Canvas->Pixels[cx-2][cy+2] = clWhite;
    buf->Canvas->Pixels[cx-1][cy+1] = clWhite;
    buf->Canvas->Pixels[cx][cy] = clWhite;
    buf->Canvas->Pixels[cx+1][cy-1] = clWhite;
    buf->Canvas->Pixels[cx+2][cy-2] = clWhite;
    buf->Canvas->Pixels[cx+3][cy-3] = clWhite;
    //再加個字
    buf->Canvas->Brush->Color = clGreen;//background color
    buf->Canvas->Font->Color = clWhite;//font color
    buf->Canvas->Font->Size=10;
    buf->Canvas->TextOutA(left+30,top+20,"再加個字");
    buf->Canvas->Brush->Color = clBlack;//background color
    buf->Canvas->Font->Color = clYellow;//font color
    buf->Canvas->TextOutA(left+50,top+80,"不要底色");
}
mainfrm.cpp
code 如下
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "mainfrm.h"
#include "mylib.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
Graphics::TBitmap *myLab;//宣告圖紙,當工作區
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    myLab = new Graphics::TBitmap();//開一塊圖紙,當工作區
    myLab->PixelFormat = pf24bit; //bmp
    myLab->Height = 480;//圖紙 h
    myLab->Width = 640;//圖紙 w
    //設定底色
    myLab->Canvas->Brush->Color = clBlack;//background = clBlack
    myLab->Canvas->FloodFill(0,0,clBlack,fsBorder);//fsSurface,fsBorder

    //把物件傳進去
    initSCR(myLab);

    //指給Image1物件
    Form1->Image1->Picture->Bitmap=myLab;

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    this->Close();
}
//---------------------------------------------------------------------------

這樣就是一個簡單實驗室了

2016-03-07

BCB6 - 在 Windows8.1 安裝 mscomm32.ocx

很多時候,我們會需要與 RS232 通訊,這時我們就需要 com 通訊元件。
通訊元件有很多第三方元件及微軟的 mscomm32,
本例是在Wwindows8.1下取得 mscomm32 與手動安裝元件。

下載  mscomm32.ocx
http://originaldll.com/file/mscomm32.ocx/7698.html



上圖右下角按鈕,兩個都可以,按下後輸入隨機碼下載。


解壓縮可得到一個元件,只有一個。

mscomm32.ocx

接著是安裝 mscomm32 元件到 Windows8.1

1.複製 mscomm32.ocx
開檔案總管,複製 mscomm32.ocx 到 C:\Windows\SysWOW64


2.安裝 mscomm32.ocx
在視窗中,開啟目錄 C:\Windows\SysWOW64
找到 cmd.exe ,右鍵,以管理員身分執行,
一定要用管理員身分,否則權限不夠,會失敗。

執行下列命令
cd C:\Windows\SysWOW64

先移除註冊
regsvr32 /u MSCOMM32.OCX
再註冊
regsvr32 MSCOMM32.OCX
完成,試一下,OK

Dropbox 下載 mscomm32.ocx

2016-02-13

BCB6 EditorLineEnds.ttr issue

今天看到一個不錯的小程式範例 ColorFormat,可以查顏色代碼,很方便,哪裡知道,windows8.1很不給力,BCB一直出問題,最後,終於解決了,雖不滿意,尚可接受。

可愛的小程式  ColorFormat (click to see source code)
http://www.functionx.com/bcb/applications/colorformat.htm


上面附上已完成的 ColorFormat,調好顏色,按下按鈕,就會把顏色代碼複製到剪貼簿,然後按 Ctrl + v就可以叫出來貼上,也可以右邊 Numeric 數值輸入,0-255,真的很方便。

My Question
好了,推完這支程式之後,來說說我遭遇到甚麼困難,搞了一整天。
我先安裝 BCB 在 Win8.1,完成之後,開個專案,關掉 BCB,要再開啟時,悲劇了,一直錯誤,無法開啟,跳出如下對話。



重新開機又可執行一次 BCB,經過 Google 查詢,發現很多人都有這問題,指向微軟更新包的錯誤,而且到現在還沒改善。

BCB執行時會建立一個檔 EditorLineEnds.ttr,在 windows temp 目錄中,奇怪的是,它會被 System 開啟,然後就不放手,以至於 BCB 要再次執行時,無法再建立該檔,就一直產生錯誤,刪也刪不掉,因為 INUSE。


這很可惡,總不能執行一次就重開機一次吧,後來繼續找解決方案,發現大家的解法就是改檔名,但仍然刪不掉,不過卻可以執行 BCB 而不用重開機了。

國外有人是寫成批次檔來改檔名,也有寫成執行檔供人下載,但我試過,改檔名部分有一些日期會產生非法字元" / "會失敗,所以繼續研究一下,在批次檔中如何改檔名可以不重複,還是要取日期+時間,到分鐘就好,改寫如下,它主要就是把那個作怪的檔

C:\Users\myCon\AppData\Local\Temp\EditorLineEnds.ttr

改檔名

EditorLineEnds2016-2-13.4.33.ttr

然後做一個目錄,全部集中丟裏頭,這樣 BCB 就可以重複執行了,就是執行前,先執行這個批次檔,處理掉這個作怪的檔,然後再執行 BCB。

批次檔 bcb.bat 請自行存成 bcb.bat,並用最高權限執行
SET /A yy=%date:~0,4%
SET /A mm=%date:~5,2%
SET /A dd=%date:~8,2%
SET dt=%yy%-%mm%-%dd%
@echo %date% = %dt%

SET /A Hou=%time:~0,2%
SET /A Min=%time:~3,2%
SET /A Sec=%time:~6,2%
SET /A Ms=%time:~9,2%*10
SET tt=%Hou%.%Min%
@echo 時間 %time% = %tt%

SET dttt=%dt%.%tt%
@echo 日期+時間 %dttt%

C:
cd %Temp%

if exist "EditorLineEnds.ttr" (
   ren EditorLineEnds.ttr EditorLineEnds%dttt%.ttr
) else (  
   goto runbcb  
)
if exist "dzEditorLineEndsFix" (
   MOVE "EditorLineEnds%dttt%.ttr" "dzEditorLineEndsFix"
) else (
   md "dzEditorLineEndsFix"
   MOVE "EditorLineEnds%dttt%.ttr" "dzEditorLineEndsFix"
)
:runbcb
mshta "javascript:alert('SUCCESS MOVE EditorLineEnds.ttr that you can run BCB fine.');close()"


這是在 Win8.1環境下測試,其他板沒測過。

說明:

SET /A yy=%date:~0,4%
SET /A mm=%date:~5,2%
SET /A dd=%date:~8,2%
SET dt=%yy%-%mm%-%dd%
@echo %date% = %dt%

這是取系統日期,然後改成合法字元組合
/A 表示數值,這樣可以解決空白問題

date = 2016/02/13 週六

經過重新組合後,會變成

2016-2-13

接著是時間

SET /A Hou=%time:~0,2%
SET /A Min=%time:~3,2%
SET /A Sec=%time:~6,2%
SET /A Ms=%time:~9,2%*10
SET tt=%Hou%.%Min%
@echo 時間 %time% = %tt%

time =   5:40:29.85

經過重新組合後,會變成

5.40

再把這兩個合起來

SET dttt=%dt%.%tt%

2016-2-13.5.40

然後改檔名

EditorLineEnds.ttr

EditorLineEnds2016-2-13.5.40.ttr

接著檢查存放目錄是否存在?不存在就做一個
然後搬移進去,這樣就完成了。

不過,還是刪不掉,不曉得要怎麼把 inuse 解除,如果你知道的話,請留言告訴我怎麼做,謝謝!

2016-02-03

小三通筆記

小三通就是台灣到金門到大陸,去大陸走小三通,金門跟廈門真的很近,都是渡輪運輸,便宜快速,我是到廈門五通碼頭,所以本筆記介紹都以此為終點。

路線圖

台灣到金門只有航空,自己訂票比較省、有彈性,如果怕麻煩的話,可以買套票,就是機票--機場接送--船票一起包,我是自己訂票,所以做個筆記。

一、訂機票金門尚義機場
可以上易遊網國內線訂票,要先加入會員,然後選票,下訂單,當天完成付款。這時你會得到一組電子機票號碼,到時去機場該航空公司櫃檯,給她身分證,它就會給你登機證了。
錯開假日,比較容易訂票,也可以訂到優惠票,一般越早越好,較多選擇,遇到優惠票機率比較高。
易遊網 http://www.eztravel.com.tw/?in=tb0



二、機場到碼頭
機場外面就有排班計程車,行情是300元,跳表是305元,大約15-20分鐘車程,有認識的可以另外叫車,可以事先請她幫忙去街上買東西,比較便宜,告訴她飛機到達時間,要買甚麼,(砲彈菜刀,貢糖,一條根,陳高...),請她報價,然後錢匯過去,就行了,真的很方便,如果東西太多,那就請她幫你用寄的。



三、買船票
到水頭碼頭後,拿護照去櫃台買船票,假日的話,最好是網上預訂,順便買保險。因為廈門很近,航程約30分鐘左右,船內很寬敞,並排座位有14個,船航行很穩,天氣好的話,幾乎感覺不到海浪的存在。



四、廈門五通碼頭
到碼頭後,出關要驗台胞證,新版ID卡台胞證可以快速通關,但要先申請填表後才行,我只好排隊人工驗通關,通關後,大廳可以換 RMB,要買船票,這有張名片,可以先諮詢與訂票,外面打的(搭 TAXI)有叫車跟排班的,叫車的是沒牌的,會出事的都是這種,要搭排班的那種,有牌的。



2014-11-08

BCB6 畢氏定理計算機

最近小孩正在學畢氏定理,說簡單還算簡單,但是要用計算機按半天,還要再開根號,蠻麻煩的,就寫了這個小程式,下面有部分程式碼,為何是部分?因為視窗程式很大部分是視窗元件,是BCB自動產生的,我們多半不需管它,只需專注於核心功能寫作就行了。

下載
https://www.dropbox.com/s/5qx8xpz8zmhm69h/BisTheory1.rar?dl=0



程式說明
這隻程式主要有兩個部分,一是三角形輸入幾個邊,要求哪個邊判定,第二是浮點數計算問題,程式碼如下

一、狀態判定,填入幾個邊了
int chkState(){
    UnicodeString s1,s2,s3;
    s1=edit1->Text;//1
    s2=edit2->Text;//3
    s3=edit3->Text;//5
    if(s1.Length()>0)mystate[1]=1;else mystate[1]=0;
    if(s2.Length()>0)mystate[2]=3;else mystate[2]=0;
    if(s3.Length()>0)mystate[3]=5;else mystate[3]=0;
    mystate[0]=mystate[1]+mystate[2]+mystate[3];
    return mystate[0];
}

這裡只要文字框產生 change()事件啟動,會檢查三個邊,字串長度>0,並分別給定其代表值,最後加總該值傳回,
a=1
b=3
c=5
如果傳回 8 ,代表b+c=8,那就知道要求的是 a 了。

二、浮點數計算
浮點數計算比較麻煩,因為有精度問題,動不動就會溢位,這裡沒專門處理這問題,只是簡單的提供精度設定,溢位就會變成負值,你就手動把精度調低些就好了。
精度就是靠四捨五入函式來完成,計算就是依據狀態判定來求相應的值。

四捨五入,使用整數法
double round(double src,int n){
    double res;
    int i,k=1;
    for(i=0;i<n;i++)k*=10;
    res=int(src*k+0.5);
    res/=k;
    return res;
}

計算
這裡沒用sqrt()函數,因為溢位時,會跳出一個對話 sqrt DOMAIN error,有點煩,所以改用求1/2平方來代替,溢位就是負值。

double calculate(){
    a=uStrToDouble(edit1->Text);
    b=uStrToDouble(edit2->Text);
    c=uStrToDouble(edit3->Text);
    switch (mystate[0]){
        case 8://a=sqrt(c^2-b^2);
            b2=pow(b,2);c2=pow(c,2);
            a2=c2-b2;
            //result=sqrt(round(a2,8));
            result=pow(a2,0.5);
            break;
        case 6://b=sqrt(c^2-a^2);
            a2=pow(a,2);c2=pow(c,2);
            b2=c2-a2;
            //result=sqrt(round(b2,8));
            result=pow(b2,0.5);
            break;
        case 4://c=sqrt(a^2+b^2);
            a2=pow(a,2);b2=pow(b,2);
            c2=a2+b2;
            //result=sqrt(round(c2,8));
            result=pow(c2,0.5);
            break;
    }
    return result;
}

2014-07-21

萬有引力從何而來?

萬有引力從何而來?這是現在最大的謎,解開了話,UFO動力問題也解開了。

簡單的說,目前磁力可以被製造,馬達就是了,但萬有引力沒辦法被製造,只能觀察,
例如你在地球上70KG,上月球就變成42KG了

如果:
我是說如果,我們已經發現萬有引力的機制,那我們可以任意產生個引力,

假如我就產生個>70KG的引力,方向與地球相反

那我就飛起來了,不用噴氣,不用磁場(也許要,也許磁場跟引力有關),不用螺旋槳。

那我就是UFO了,而且好處不只這些,我根本不用考慮慣性問題,不必管那幾個G。

因為我的重力參考原點是我自己,不是地球,所以我的G是我自己控制,根本不用管地球的G

那我就可以任意大於90度轉向,而不會血管爆裂。

這樣說夠迷人吧,這就是解開重力之謎的好處。

2014-07-11

Arduino GY-271 HMC5883L 三軸電子羅盤傳感器測試

下面是 GY-271 廠商的描述
用途:測量地磁方向,測量物體靜止時候的方向,測量傳感器周圍磁力線的方向。註意,測量地磁時候容易受到周圍磁場影響,主芯片HMC5883三軸磁阻傳感器特點:

1,數字量輸出:I2C數字量輸出接口,設計使用非常方便。
2,尺寸小: 3x3x0.9mm LCC封裝,適合大規模量產使用。
3,精度高:1-2度,內置12位A/D,OFFSET, SET/RESET 電路,不會出現磁飽和現象,不會有累加誤差。
4,支持自動校準程序,簡化使用步驟,終端產品使用非常方便。
5,內置自測試電路,方便量產測試,無需增加額外昂貴的測試設備。
6,功耗低:供電電壓1.8V, 功耗睡眠模式-2.5uA 測量模式-0.6mA



連接:
只要連接VCC,GND,SDA,SCL四條線。

Arduino  GY-271
-------------------------
VCC         VCC
GND        GND
A4            SDA
A5            SCL

P.S.我這塊 GY-271 是可以輸入 5V DC,但有的只能輸入3.3V DC,這個要看廠家的說明。

請注意:
HMC5883L.cpp 中必須更改下列字句,不然編譯會出錯,這是新版Arduino IDE變動的關係。
WProgram.h  ->  Arduino.h
Wire.send()  -> Wire.wirte()
Wire.receice()  -> Wire.read()

程式碼:

/*
HMC5883L_Example.pde - Example sketch for integration with an HMC5883L triple axis magnetomerwe.
Copyright (C) 2011 Love Electronics (loveelectronics.co.uk)

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

// Reference the I2C Library
#include <Wire.h>
// Reference the HMC5883L Compass Library
#include "HMC5883L.h"

// Store our compass as a variable.
HMC5883L compass;
// Record any errors that may occur in the compass.
int error = 0;

// Out setup routine, here we will configure the microcontroller and compass.
void setup(){
  // Initialize the serial port.
  Serial.begin(9600);
  Serial.println("Starting the I2C interface.");
  Wire.begin(); // Start the I2C interface.
  Serial.println("Constructing new HMC5883L");
  compass = HMC5883L(); // Construct a new HMC5883 compass.   
  Serial.println("Setting scale to +/- 1.3 Ga");
  error = compass.SetScale(1.3); // Set the scale of the compass.
  // If there is an error, print it out.
  if(error != 0) Serial.println(compass.GetErrorText(error));
  // Set the measurement mode to Continuous
  Serial.println("Setting measurement mode to continous.");
  error = compass.SetMeasurementMode(Measurement_Continuous);
  // If there is an error, print it out.
  if(error != 0) Serial.println(compass.GetErrorText(error));
}

// Our main program loop.
void loop(){
  // Retrive the raw values from the compass (not scaled).
  MagnetometerRaw raw = compass.ReadRawAxis();
  // Retrived the scaled values from the compass (scaled to the configured scale).
  MagnetometerScaled scaled = compass.ReadScaledAxis();
 
  // Values are accessed like so:
  int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)

  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(scaled.YAxis, scaled.XAxis);
 
  // Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
  // Find yours here: http://www.magnetic-declination.com/
  // Mine is: 2� 37' W, which is 2.617 Degrees, or (which we need) 0.0456752665 radians, I will use 0.0457
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = 0.0457;
  heading += declinationAngle;
 
  // Correct for when signs are reversed.
  if(heading < 0)    heading += 2*PI;
   
  // Check for wrap due to addition of declination.
  if(heading > 2*PI)    heading -= 2*PI;
  
  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180/M_PI;

  // Output the data via the serial port.
  Output(raw, scaled, heading, headingDegrees);

  // Normally we would delay the application by 66ms to allow the loop
  // to run at 15Hz (default bandwidth for the HMC5883L).
  // However since we have a long serial out (104ms at 9600) we will let
  // it run at its natural speed.
  // delay(66);
}

// Output the data down the serial port.
void Output(MagnetometerRaw raw, MagnetometerScaled scaled, float heading, float headingDegrees){
   Serial.print("Raw:(x,y,z)=( ");
   Serial.print(raw.XAxis);
   Serial.print(", ");  
   Serial.print(raw.YAxis);
   Serial.print(", ");  
   Serial.print(raw.ZAxis);
   Serial.print(") Scaled:(x,y,z)=( ");  
   Serial.print(scaled.XAxis);
   Serial.print(", ");  
   Serial.print(scaled.YAxis);
   Serial.print(", ");  
   Serial.print(scaled.ZAxis);
   Serial.print(") Heading: ");
   Serial.print(heading);
   Serial.print(" Radians: ");
   Serial.print(headingDegrees);
   Serial.println(" Degrees.");
   delay(500);
}


執行結果

它只負責讀出三軸方向磁場強度,而角度要自己去算。

下載 HMC5883L LIB
https://www.dropbox.com/s/h062nnde78zts1l/HMC5883L.zip

參考資料
Arduino連接HMC5883L三軸電子羅盤傳感器
http://www.geek-workshop.com/thread-105-1-1.html

使用 Arduino Pro Mini + 驅動板 來驅動步進馬達 28BYJ-48 5VDC


這個是參考別人的,只是測試資料,做個筆記
例中一次轉一圈,停5秒,再反轉一圈

影片

驅動板及步進馬達
 程式碼

#include "myStepper.h"
#define STEPS 2048
Stepper stepper(STEPS,9,10,11,12);
void setup() {
  stepper.SetSpeed(5);
}
void loop() {
  stepper.Step(2048);
  delay(5000);
  stepper.Step(-2048);
  delay(5000); 
}


接線

Arduino   驅動板
--------------------------
D9             IN1
D10           IN2
D11           IN3
D12           IN4
VCC          +
GND          -

p.s.驅動板+-旁兩個JMP要CLOSE,驅動板最好獨立供電,避免 Arduino 燒毀,我只是測試會不會動,所以就直接接電用了XD

說明

原本應該是

#include <Stepper.h>

但我的 Arduino IDE v1.0.5 可能是內建了同名稱 LIB 導致 Link 會一直出現錯誤
所以就把它改個名子,放在同一個路徑下,這樣就可以正確編譯了

#include "myStepper.h"

 
下載
https://www.dropbox.com/s/e469pclk84fsbq3/myStepper.rar

參考資料
http://www.geek-workshop.com

2014-06-13

Arduino 有關 PWM 的參考資料

PWM 全名叫做脈衝寬度調變( Pulse Width Modulation,PWM)[1],是將類比信號 轉換為脈波的一種技術,一般轉換後脈波的週期固定,但脈波的占空比會依類比信號的大小而改變。
PWM 技術是一種對類比信號電位的數字編碼方法,通過使用高解析度計數器(調製頻率)調製方波的占空比,從而實現對一個類比信號的電位進行編碼。
它是一種數位控制訊號,抗干擾性較強,應用很廣,電壓和電流可直接用來進行控制對象,例如家用電器設備中的音量開關控制、採用鹵素燈泡燈具的亮度控制等等。

輸出電壓的控制
PWM透過對佔空比 DUTY_CYCLE 的波形產生,可以對輸出電壓達到控制目的。

圖一、DUTY_CYCLE
輸出電壓 Output_Voltage 就是佔空比乘以輸入電壓

Output_Voltage = Duty_Cycle * Input_Voltage


如圖一,輸入電壓是 0V-5V,佔空比是25%,輸出電壓就是

 Output_Voltage = 0.25 * 5 = 1.25V

也就是高電位 5V 的加總平均值


如輸入電壓是 0V-5V,佔空比是80%,輸出電壓就是

 Output_Voltage = 0.8 * 5 = 4V

所以我們只需控制輸出波形,就可以決定輸出電壓了。


以下討論對象是目前比較被廣泛採用的晶片 ATmega328,參考它的Datasheet [2]可知,它有三個時脈 Timer,分別是 Time0, Time1, Time2,我們可以通過對 Timer 的設定,來達到控制脈波輸出的特性。

以下是三個時脈的設定說明,節錄自 QEEWiki [3],這是要對Timer設定的重要參考,它已經被整理成表格狀,還附有一個範例程式,非常方便使用。

圖二、Time0

圖三、Time1

圖四、Time2




註
[1].http://zh.wikipedia.org/wiki/脈衝寬度調變
[2].http://www.atmel.com/Images/doc8161.pdf
[3].https://sites.google.com/site/qeewiki/books/avr-guide/pwm-on-the-atmega328

2014-05-29

巧治糖尿病

剛剛看到 YouTube 上一支關於糖尿病的影片,就順手把它貼過來了,我的親人飽受此病之苦,相信也有不少人有同樣的問題,大家可以看看。
其實,肥胖還是糖尿病的主因,就是要多運動,只要瘦下去,自然可以不藥而癒,但老年人,或是工作忙碌之人,可能就很困難有運動習慣了,那另一種方法就是飲食控制,若克制不了欲望,最後只好做胃繞道手術,挨一刀,來減少養分的吸收了。


2014-05-21

Arduino LCD12864 ST7920 parallel test...

這是 LCD12864 ST7920 並行測試,從官網下載一個範例程式,然後修改了一小部分程式碼,來方便理解其運作,自己加上了一個 VR,修改顯示字串等等。



線路配置(並行)



RS = 17; Analog Pin3
RW = 16; Analog Pin2
EN = 18; Analog Pin4
D0  = 8;
D1  = 9;
D2  = 10;
D3  = 11;
D4  = 4;
D5  = 5;
D6  = 6;
D7  = 7;
PIN15 PSB = 5V;(並行)

VR (3k) 配置
+ 5v
Analog Pin1
GND

官方12864網頁
http://playground.arduino.cc/Code/LCD12864

使用 Lib
LCD12864
http://playground.arduino.cc/uploads/Code/LCD12864.zip

官方原始碼
http://playground.arduino.cc/uploads/Code/pongLCD.zip

參考原始碼
//http://playground.arduino.cc/Code/LCD12864
#include <LCD12864.h>
int score = 0;//分數
boolean lost = false;//遊戲結束
int relive = 0;//復活讀秒
int top=128,down=159,left=0,right=63;
int Xpos = 0;//球座標 x
int Ypos = 133;//球座標 y
int momentumX = 2;//x增量
int momentumY = 1;//y增量
boolean momr = true;//球向右(x)
boolean momd = true;//球向下(y)
int PaddlePos = 139;// 130<PaddlePos<148 (128<y<160)
void setup() {
    LCDA.Initialise(); // INIT SCREEN
    delay(500); // Some delay
    LCDA.Render(); // Start the Rendering process
    Title(); // 畫 ASCII 字元
    delay(500); // Some Delay
}
void loop(){
    int tmp;
    tmp = analogRead(1);// 讀取VR值 0-1023
    PaddlePos = map(tmp,0,1023,129,149);// 把 0-1023 值轉為 130-148
    run();
}
void AI(){// BALL MOVEMENT 球移動
    //left=0,right=63
    if (Xpos+momentumX < right && momr == true) {
        Xpos+=momentumX;//x->
    }else if (Xpos+momentumX >= right && momr == true) {
        Xpos-=momentumX;//x->反彈
        momr = false;//<-x
    }else if (Xpos-momentumX > left && momr == false){
        Xpos-=momentumX;//<-x
    }else if (Xpos-momentumX <= left && momr == false){//反彈<-x
        if (Ypos+1 >= PaddlePos && Ypos < PaddlePos+10) {//擊中
            Xpos+=momentumX;//反彈||<-x
            momr = true;//x->           
            scored();//計分
            // 變化球,棒子兩端+-3
            if (Ypos < PaddlePos+3 || Ypos-1 > PaddlePos+7) {
                if (momentumY < 4)  momentumY +=2;
            }else{
                if (momentumY > 1)  momentumY -=1;
            }
        }else{// 沒擊中
            lost = true;
        }
    }
    //top=128,down=159
    if (Ypos+momentumY < down && momd == true) {//向下
        Ypos+=momentumY;
    }else if (Ypos+momentumY >= down && momd == true) {//向下反彈
        Ypos-=momentumY;
        momd = false;
    }else if (Ypos-momentumY > top && momd == false){//向上
        Ypos-=momentumY;
    }else if (Ypos-momentumY <= top && momd == false){//向上反彈
        Ypos+=momentumY;
        momd = true;
    }
}
void run() {//循環
    if (lost == false) {
        AI(); 
        LCDA.DumpScreenBuffer(0,0,8,32);//清空記憶體資料(X,Y,maxX,maxY)
        // Draw Room (x,y,w,h),畫框(0<=X<=63),(128<=Y<=159)
        //top=128,down=159,left=0,right=63
        LCDA.FillRectangle(left,top,right,1);
        LCDA.FillRectangle(right,top,1,32);//60,128
        LCDA.FillRectangle(left,down,right,1);//0,158
        // Draw Ball 畫球
        LCDA.FillRectangle(Xpos,Ypos,2,2);// 2*3 球
        // Draw Paddle 畫板子
        LCDA.FillRectangle(1,PaddlePos,2,10);// 2*10 板子
        LCDA.RenderScreenBuffer(0,0,8,32);//送出記憶體資料
    }else{
        relive++;
        if(relive>3000){//復活讀秒=3秒
            lost = false;
            relive=0;
            score=0;
        }
    }
}
// 秀數字
void numbertobinary(char num) { //RS=1,RW=0,寫資料到內部 RAM (RS,RW,D7,D6,D5,D4,D3,D2,D1,D0)
    if (num == 0)         LCDA.setPins(1,0,0,0,1,1,0,0,0,0); //30h=48=0
    else if (num == 1)    LCDA.setPins(1,0,0,0,1,1,0,0,0,1); //31h=49=1
    else if (num == 2)    LCDA.setPins(1,0,0,0,1,1,0,0,1,0); //32h=50=2
    else if (num == 3)    LCDA.setPins(1,0,0,0,1,1,0,0,1,1); //33h=51=3
    else if (num == 4)    LCDA.setPins(1,0,0,0,1,1,0,1,0,0); //34h=52=4
    else if (num == 5)    LCDA.setPins(1,0,0,0,1,1,0,1,0,1); //35h=53=5
    else if (num == 6)    LCDA.setPins(1,0,0,0,1,1,0,1,1,0); //36h=54=6
    else if (num == 7)    LCDA.setPins(1,0,0,0,1,1,0,1,1,1); //37h=55=7
    else if (num == 8)    LCDA.setPins(1,0,0,0,1,1,1,0,0,0); //38h=56=8
    else if (num == 9)    LCDA.setPins(1,0,0,0,1,1,1,0,0,1); //39h=57=9
}
void Title() { // 秀 ASCII 字元
    LCDA.Draw(false,25,0);//(sw,x,?)
    delay(1);
    byte title[] = {
        0,1,1,0,1,0,1,1,//0x6b=k
        0,1,1,0,1,1,1,1,//0x6f=o
        0,1,1,1,0,1,1,1,//0x77=w
        0,1,1,0,0,0,0,1,//0x61=a
        0,1,1,0,1,1,0,0,//0x6f=l
        0,1,1,0,0,0,0,1,//0x61=a
        0,0,1,0,0,1,1,1,//0x27='
        0,1,1,1,0,0,1,1,//0x73=s
        0,0,1,0,0,0,0,0,//0x20=
        0,1,1,0,1,0,0,0,//0x68=h
        0,1,1,0,1,1,1,1,//0x6f=o
        0,1,1,0,1,1,0,1,//0x6d=m
        0,1,1,0,0,1,0,1 //0x65=e
    };
    int count = 0;
    int counter = 0;
    while (count < sizeof(title)/sizeof(byte)) {
        for (int i = count; i < count+8; i++) {
            LCDA.temp[counter] = title[i];
            counter++;
        }
        LCDA.setPins(1,0,
            LCDA.temp[0],LCDA.temp[1],LCDA.temp[2],LCDA.temp[3],
            LCDA.temp[4],LCDA.temp[5],LCDA.temp[6],LCDA.temp[7]);
        count += 8;
        counter = 0;
    }
    LCDA.Draw(true,0,0);
}
void scored() { // 計分+1
    LCDA.Draw(false,12,20);
    delay(1);
    score++;
    momentumX = random(3, 6);
    if (score < 10) {
        numbertobinary('0');
        numbertobinary('0');
        numbertobinary(score);
    }else if (score < 100) {
        int res = score/10;
        int lef = score%10;
        numbertobinary('0');
        numbertobinary(res);
        numbertobinary(lef);
    }else if (score < 1000) {
        int res = score/100;
        int lef = score%100;
        int res2 = lef/10;
        int lef2 = lef%10;
        numbertobinary(res);
        numbertobinary(res2);
        numbertobinary(lef2);
    }
    LCDA.Draw(true,0,0);
}
 補充說明

void Title() { // 秀 ASCII 字元
    LCDA.Draw(false,25,0);//(sw,x,?)

在文字顯示中,X座標指定,是從左上角,由0開始,分成四行,一直到右下角為止,如下表所示。

 ASCII 字碼輸入

 LCDA.setPins(1,0,0,0,1,1,0,0,0,0); //30h=48=0

 setPins() 的位元如下

 (RS,RW,D7,D6,D5,D4,D3,D2,D1,D0)

 RS=1,RW=0,時,為寫資料到內部 DRAM,D0-D7 為 ASCII Code,請參考下表

例如我們要在螢幕中央顯示 7,那 X 就是 12,然後先關掉繪圖,並指定 X

LCDA.Draw(false,12,0);

接著輸入 7 的 ASCII CODE,查上表為 0x37 ,二進位碼  00110111 ,可由計算機轉換


然後寫入,頭兩個位元必須是 RS=1,RW=0,後面就是二進位碼  00110111,如下

 LCDA.setPins(1,0,0,0,1,1,0,1,1,1);

最後就是把繪圖打開,就會顯示了

 LCDA.Draw(true,0,0);

2014-04-21

AutoCad 物件任意角轉正

有時,會用ROTATE把物件旋轉,但轉了之後,又想把圖轉正,卻轉不回來了,我想了好久,終於被我搞出來了,做個筆記。

筆記,物件轉正,及另一個對齊任意線。


實例

先做兩條輔助線,水平線跟機頭中心線對水平線的鏡射線,就是大約30度那條。

選機頭物件,使用ROTATE指令,以上述兩線交點為基準點,旋轉至大約30度那條端點。

清掉輔助線,完成。