2012/02/08

Arduino Sketch基本架構與序列埠輸出

上一篇我購買了Arduino Uno板子,並且安裝設定好Arduino軟體開發環境,成功編譯、上傳到板子上,讓標示著L的LED燈不停閃爍。

這一篇要說明Arduino的sketch基本架構,並且以序列埠輸出文字到電腦上。所謂的sketch(程式碼、草稿碼),就是Arduino的程式啦。

開啟Arduino軟體開發環境後,選「File」-「Examples」-「1.Basics」-「BareMinimum」,這支範例程式含有最基本的架構,如下:

void setup() {
  // put your setup code here, to run once:
 
}

void loop() {
  // put your main code here, to run repeatedly:
 
}
在整支程式啟動時,會呼叫setup()這支函式,只會執行一次。然後就會不斷地呼叫loop()、不斷地一再執行。

現在,讓我們寫些程式碼,將一些文字輸出到序列埠,並在電腦上觀看。

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello World!");
}
首先利用Serial.begin(115200)開啟序列傳輸並設定傳輸鮑率,會以0、1針腳進行接收與傳送,我在這裡只示範傳送。

以Serial.println("Hello World!")輸出Hello World!字串。

static int g_counter = 0;
void loop() {
  // put your main code here, to run repeatedly:
  Serial.println(g_counter);
  g_counter++;
  delay(1000);
}
設定全域靜態變數g_counter,初始為0。
每次執行loop()時,以Serial.println(g_counter)輸出g_counter的數值、並且加1,以delay(1000)暫停一秒(單位為千分之一秒),然後程式會再次執行loop()。

編譯成功並上傳板子後,以「Tools」-「Serial Monitor」打開序列埠輸出監控視窗觀看,把右下角的傳輸鮑率設定成跟Serial.begin(115200)所設定的數值一樣。(若115200不行的話,可以試試看9600。)

然後就應該看到如下的畫面了。


每當以針腳1傳出資料時,板子上標示著TX的LED都會閃一下。
每次開啟Serial Monitor時,板子都會重置(重新開機),所以會從頭從0開始數。

不過Arduino內建的函式print與println不強,沒辦法像一般C語言的printf,底下有個簡易型的函式,可以輸出格式字串。

void pf(const char *fmt, ... ){
    char tmp[128]; // resulting string limited to 128 chars
    va_list args;
    va_start (args, fmt );
    vsnprintf(tmp, 128, fmt, args);
    va_end (args);
    Serial.print(tmp);
}
不過, 最後輸出的字串必須少於128個字元。

有了這個函式後,就可以這麼寫了:
static int g_counter = 0;
void loop() {
    pf("g_counter is %d, %s\n", g_counter, g_counter%2 == 0 ? "even" : "odd");
    g_counter++;
    delay(1000);
}

21 comments:

  1. 各種基本的電路連接,淺顯易懂。
    http://www.pighixxx.com/abc-english-version/

    ReplyDelete
  2. 你好 您的文章給對於剛入門的有我很大的幫助
    但想請問pinMode() 這個內建的副函式放在Arduino資料夾的哪裡?我去找只有在Arduino.h中看到pinMode()的函式宣告,卻無法找到pinMode它是如何撰寫的。因為想知道底層AVR是如何做設定,所以有這個疑問,可否請您替我解答。

    ReplyDelete
    Replies
    1. 在\hardware\arduino\cores\arduino\wiring_digital.c裡。需知道AVR的register的用法,才能寫出pinMode()。

      Delete
    2. 謝謝,我照您提供的路徑找到了,我再去翻DATASHEET對照看一下,再次感謝。

      Delete
    3. 想用較低階的方式撰寫AVR程式的話,可參考Arduino Internals這本書。我個人都是用Arduino提供的程式庫,沒辦法跟你討論。

      Delete
  3. 謝謝你提供參考的書籍。我想還是會先摸熟Arduino提供的程式庫再往低階研究。(繼續參考你的分享 XD)

    ReplyDelete
    Replies
    1. 除了我的部落格,也可以參閱Cooper Maa寫的入門文章http://coopermaa2nd.blogspot.tw/2011/05/arduino.html

      Arduino的書很多,中文的可看「超圖解Arduino互動設計入門」。

      Delete
  4. 你好,想請問一下,小弟最近再用一塊血壓量測用的模組,那塊模組是uart的,不知道arduino要來控制那塊模組,然後接收他的量測值,我的作法是跟這篇範例依樣嗎??求解 ,以下附上了那塊量測模組的完整資料,還有那塊模組他已經自己做完類比轉數位了
    https://drive.google.com/uc?id=0B64QFv8eVz7DdGw4UFFqS1RsbWs&export=download

    ReplyDelete
    Replies
    1. 目前已經下單要購買擬推薦的超圖解arduino,不過還是先問問看

      Delete
    2. 通訊介面是UART,然後必須知道通訊內容的格式,在生理訊號量測平台使用手冊V2.0.pdf只有模組傳出的封包格式,在最後面幾頁。
      至於從主機(電腦、Arduino等)傳給模組的封包格式,似乎沒有記載在文件中,須自行參考範例裡的程式碼,範例裡以類似下列的程式碼傳出指令,代表切斷連線:
      cData[0] = 0x41; // A
      cData[1] = 0x54; // T
      cData[2] = 0x23; // #
      cData[3] = 0x44; // D
      cData[4] = 0x53; // S
      cData[5] = 0x43; // C
      cData[6] = 0x0D; // CR
      cData[7] = 0x0A; // LF

      pSerial->SendData(cData , 8);

      另外由類似底下的程式碼在接收後抽取出需要的資料(心跳、血壓等),
      iBreath_Data = ucData[i+1];
      iBreath_Data = iBreath_Data * 256 + ucData[i+2];
      iBreath_Data = iBreath_Data * 256 + ucData[i+3];
      iBreath_Data /= 6710 * 4;

      使用Arduino的話,也是差不多的動作,但要改用Arduino的API。

      另外,關於Arduino的入門書,也請參考拙作 Arduino輕鬆入門:範例分析與實作設計(http://yehnan.blogspot.tw/2014/02/arduino_21.html)。

      Delete
    3. 比較不知道改由arduino控制該怎麼打程式,會跟原本差很多嗎

      Delete
    4. 準備欲傳送資料的部份、抽取接受到的資料的部份,應該都可套用範例裡的程式碼。
      其他部分,應該都不能用吧,必須改為Arduino的API。

      Delete
    5. 大概了解 感謝你

      Delete
  5. 大大您好~方便留個連絡方式嗎 因為我寫了一個藍芽遙控俱紅外線避碰的程式,但是發現一啟動 它好像會跑一個退後停止的迴圈,且無法用藍芽操控

    ReplyDelete
    Replies
    1. 有問題請留言,盡量回答。

      Delete
  6. 那我是不是可以把程式po上來給你看看是不是哪裡不恰當呢?

    ReplyDelete
    Replies
    1. 請。
      另外也放到http://pastebin.com/,比較容易閱讀。

      Delete
  7. #include
    #include

    Servo servoRight;
    Servo servoLeft;

    SoftwareSerial BTSerial(0, 1); // RX, TX

    //byte dis[6]; //distance package
    //int t,i;
    int val; //temp value
    int BTControl = 1;

    void setup(){
    pinMode(10,INPUT);pinMode(9,OUTPUT); //左邊紅外線發射接收器
    pinMode(3,INPUT);pinMode(2,OUTPUT); //右邊紅外線發射接收器
    tone(4,3000,1000); //蜂鳴器響起一秒
    delay(1000);

    Serial.begin(9600);
    BTSerial.begin(57600); //BTSerial 9600,19200,38400,57600
    servoRight.attach(12); //伺服馬達分別連接12與13腳位
    servoLeft.attach(13);
    //dis[0] = 65; //check value
    }

    void loop()
    {
    int irLeft=irDetect(9,10,38000); //呼叫紅外線副程式(左)
    int irRight=irDetect(2,3,38000); //呼叫紅外線副程式(右)
    if(irLeft == 0){ //左方有障礙物
    Down(); //後退一秒
    Stop();
    BTControl=0;}
    if(irRight == 0){ //右方有障礙物
    Down(); //後退一步
    Stop();
    BTControl=0;}
    //else{
    //if(Serial.available()>0)
    //{
    val = Serial.read(); //藍芽讀值
    if(BTControl == 0){ //避碰程式作用後,BTControl值為0
    val = 's'; //將藍芽控制變數設為s
    BTControl = 1; //更改BTControl變數為1(藍芽控制模式)
    }
    else if(BTControl == 2){ //藍芽程式左右轉後進入此迴圈
    val = 'f'; //將藍芽控制變數設為f,轉彎後繼續直走
    BTControl = 1; //更改BTControl變數為1(藍芽控制模式)
    }
    else{
    switch(val)
    {
    case 'f': // 前進
    Up();
    break;
    case 'b': // 後退
    Down();
    break;
    case 'l': // 左轉後前進
    Left();
    Up();
    BTControl=2; //下次迴圈將val變數更改為f,繼續向前走
    break;
    case 'r': // 右轉後前進 
    Right();
    Up();
    BTControl=2; //下次迴圈將val變數更改為f,繼續向前走
    break;
    case 's': // 停止
    Stop();
    break;
    }
    }
    //}
    //}
    }

    int irDetect(int irLedPin,int irReceiverPin,long frequency){ //從主程式讀取各參數值
    tone(irLedPin,frequency,8); //紅外線發射器頻率38000
    delay(1); //持續1ms
    int ir=digitalRead(irReceiverPin); //偵測紅外線接收器狀態
    delay(1); //持續1ms
    return ir; //回傳感測器狀態(1or0)
    }

    int Up(void){
    servoRight.writeMicroseconds(1300);
    servoLeft.writeMicroseconds(1700);
    delay(1000);
    }
    int Down(void){
    servoRight.writeMicroseconds(1700);
    servoLeft.writeMicroseconds(1300);
    delay(1000); //視情況調整
    }
    int Right(void){
    servoRight.writeMicroseconds(1700);
    servoLeft.writeMicroseconds(1700);
    delay(1000); //視情況調整
    }
    int Left(void){
    servoRight.writeMicroseconds(1300);
    servoLeft.writeMicroseconds(1300);
    delay(1000); //視情況調整
    }
    int Stop(void){
    servoRight.writeMicroseconds(1500);
    servoLeft.writeMicroseconds(1500);
    delay(1000); //視情況調整
    }

    ReplyDelete
    Replies
    1. 你藍牙連接的腳位是0跟1,這不就是內建的序列埠嗎?
      SoftwareSerial BTSerial(0, 1); // RX, TX
      可是兩者的傳輸速率又不一樣?
      Serial.begin(9600);
      BTSerial.begin(57600);

      然後卻又不使用BTSerial讀值?
      val = Serial.read(); //藍芽讀值


      > 但是發現一啟動 它好像會跑一個退後停止的迴圈,且無法用藍芽操控
      請詳細說明執行時的情況。

      建議你把Serial跟BTSerial分開,
      當BTSerial收到東西時,也可以送到Serial,便可從序列埠監控視窗查看。

      Delete
  8. 請問點選Tools(工具),確認Serial Port時, 沒辦法點選。編譯好的程式無法上傳到板子。檢查裝置管理員, Arduino Uno (COM3), 出現 "這個裝置無法啟動。 (代碼 10)" 自行更換Serial Port也不work, 請問該怎麼辦? 謝謝。

    ReplyDelete
    Replies
    1. 應該是驅動程式沒安裝成功,到裝置管理員裡,在該裝置上頭按滑鼠右鍵,更新驅動程式,路徑指向Arduino軟體的子資料夾drivers。

      Delete