2012年2月28日 星期二

StencylWorks--"Scratch-like"App開發工具

StencylWorks--"Scratch-like"App開發工具
http://jimmyscratchlab.blogspot.com/2012/02/stencyl-scratch-like-tool.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FSHxmZ+%28Jimmy%E6%A9%9F%E5%99%A8%E4%BA%BA%E7%A9%8D%E6%9C%A8%E8%BB%9F%E9%AB%94%E5%AF%A6%E9%A9%97%E5%AE%A4%29
http://www.stencyl.com/
http://www.stencyl.com/help/istencyl/

Netduino 與 Android 藍芽通訊


Android 自從發表 ADK 後,與 Arduino已經變成好兄弟了,但是 Netduino 該如何與 Android 通訊呢?
參考網路上有個 Amarino計畫,是使用藍芽模組完成 Android 與 Arduino之間通訊的一組程式庫,將這個程式庫改寫一下應該也可讓 Netduino 與 Android 變成麻吉吧。
image
我所使用的藍芽模組是 sparkfun 的 Bluetooth mate Gold (WRL09358),有六個接腳,由左而右依序是DTS-0、RX-1、TX-0、VCC、CTS-1、GND。產品特色如下,特色第一點就表明這是為 Arduino 所設計的...,Netduino應該也可用吧?
Designed to work directly with Arduino Pro's and LilyPad main boards
FCC Approved Class 1 BluetoothR Radio Modem
Very robust link both in integrity and transmission distance (100m) - no more buffer overruns!
Low power consumption : 25mA avg
Hardy frequency hopping scheme - operates in harsh RF environments like WiFi, 802.11g, and Zigbee
Encrypted connection
Frequency: 2.4~2.524 GHz
Operating Voltage: 3.3V-6V
Serial communications: 2400-115200bps
Operating Temperature: -40 ~ +70C
Built-in antenna
image image
手機端是使用 Amarino 範例改寫的一個簡單程式(如下圖左側),手機畫面左上角的 Button 按鈕可以將按鈕下面的數值(圖片中的數字1)不斷增加兩倍,並且透過無線藍芽送出到藍芽模組,再轉送給 Netduino,進而控制接在 pin9 的LED的亮度。(1~最暗、128~最亮)
image
Netduino 控制板的 analog Input 0 連接一個可變電阻,選轉可變電阻可以改變類比輸入電壓,進而轉換成數位值(介於0~1023),手機端左上角中間的數字(圖片中的537),就是接收 Netduino 經由藍芽模組發送過來的數值資料,手機下方的白底紅線圖是這個數值資料在時間軸上所畫出來的折線圖。
下圖是實驗線路,
image     

接下來就是影片時間了,影片中先試著連續按下Button,可看到數值不斷變大,同時 Netduino 連接的LED也不斷變亮,再來就是旋轉可變電阻,可以觀察到手機程式的數值也不斷變化,同時也畫出了圖形。過程都是透過藍芽無線傳輸。

2012年2月21日 星期二

HTC desire HD 透過ADK與 Arduino 通訊

經過一番努力,我的HTC desire HD終於順利執行ADK內的demokit程式,與Teemino控制板(Arduino 2560 ADK相容板)雙向通訊,辛苦總算有代價

剛開始連接Teemino控制板,HTC總是會跳出如下圖的選擇連線類型,不論選哪一個都會出問題,

image

後來才知道要先將 "詢問USB連線類型" 關掉,方法是: 點選設定->連線至PC->USB連線類型->詢問 (取消打勾)

imageimageimage

接下來才能看到 demokit 的起始畫面,但是剛開始試著將Teemino控制板連上時,demokit 總是無法感應到已經有arduino相容的控制板連上線,讓我一直以為可能是線路有問題,或是硬體有問題,

image

後來才知道要修改 Arduino 端的測試程式, 原因應該是 Teemino控制板上面並沒有內建小搖桿,所以原本測試程式執行到有關搖桿的程式,就會有問題。
修正程式後,終於看到令人感動的畫面。

image

測試程式內容包含
1.可以接收 Arduino 端3個digital input 按鈕,當按下按鈕時,Android 螢幕上對應的 B1 B2 B3 也會隨之點亮。
2.可以接收 Arduino 端2個analog input。

image

3.可以控制 digital output,按下 Relay1、Relay2,可以看到麵包板上的LED燈被點亮
4.可以控制 Arduino 的PWM輸出,例如捲動Servo0捲軸,可以看到伺服馬達隨之轉動
   同樣的拉動led1的捲軸,麵包板上對應的LED的亮度,也會隨之改變。

以上測試大致成功,但是在手機上送出控制訊號到Arduino,直到看到反應,時間拖的有點長,反應很遲緩,我是否該換手機了??

2012年2月16日 星期四

如何讓 Arduion 1.0 能夠編譯 ADK 範例韌體 demokit.ino

 

ADK (Android Open Accessory Development Kit)裡面附了一個 Arduino 的範例程式,可以用來與 Android 平台測試連結,但是只能在 Arduino-0023下順利編譯,如果放到Arduino 1.0就會出現錯誤訊息,因此必須修改原始程式才行。修改步驟如下:

1.下載、安裝 Arduino 1.0。

2.下載ADK(http://developer.android.com/guide/topics/usb/adk.html),解壓縮得到兩個子目錄(firmware/arduino_libs/AndroidAccessory 以及 firmware/arduino_libs/USB_Host_Shield)複製到 <arduino_installation_root>/libraries/ 之下。

3.下載位置CapSense (http://www.arduino.cc/playground/Main/CapSense)
解壓縮CapSense得到子資料夾,複製資料夾CapSense 到<arduino_installation_root>/libraries/ 之下

4.修改以下檔案的內容

<arduino_installation_root>\libraries\USB_Host_Shield\Max3421e.h Line37
        #include "WProgram.h" 改成=> #include " Arduino.h "

<arduino_installation_root>\libraries\AndroidAccessory \AndroidAccessory.h Line20
       #include "WProgram.h" 改成=> #include " Arduino.h "

<arduino_installation_root>\libraries\USB_Host_Shield\Max_LCD.cpp Line38
       #include "WProgram.h" 改成=> #include " Arduino.h "

       inline void Max_LCD::write(uint8_t value) { 改成=> inline size_t Max_LCD::write(uint8_t value) {

<arduino_installation_root>\libraries\USB_Host_Shield\Max_LCD.h Line101
      virtual void write(uint8_t); 改成=> virtual size_t write(uint8_t);

PS.如果在測試程式內出現Wire.receive改成=> Wire.read
PS.如果在測試程式內出現Wire.send 改成=> Wire.write

5.啟動Arduino1.0,選擇正確的Serial port 以及 Board代號(Arduino Mega 2560 or Mega ADK)
clip_image002

6. 開啟 demokit.ino ,編譯應該可以成功,若仍然有問題就將程式內有關 joystick 相關的程式碼通通disable,(修改完畢的程式  http://xduino.blogspot.com/2012/02/apk-demokitino-arduion-10.html)

7.接下來下載程式到 Arduino Mega ADK控制板上(我是用 Teemino),就可以成功與Android平台連結了。

image

2012年2月15日 星期三

ADK範例韌體 demokit.ino --- 修正後可在 Arduion 1.0 編譯通過


#include <Wire.h>
#include <Servo.h>
#include <Max3421e.h>
#include <Usb.h>
#include <AndroidAccessory.h>
#include <CapSense.h>
#define  LED3_RED       2
#define  LED3_GREEN     4
#define  LED3_BLUE      3
#define  LED2_RED       5
#define  LED2_GREEN     7
#define  LED2_BLUE      6
#define  LED1_RED       8
#define  LED1_GREEN     10
#define  LED1_BLUE      9
#define  SERVO1         11
#define  SERVO2         12
#define  SERVO3         13
#define  TOUCH_RECV     14
#define  TOUCH_SEND     15
#define  RELAY1         A0
#define  RELAY2         A1
#define  LIGHT_SENSOR   A2
#define  TEMP_SENSOR    A3
#define  BUTTON1        A6
#define  BUTTON2        A7
#define  BUTTON3        A8
/*
#define  JOY_SWITCH     A9      // pulls line down when pressed
#define  JOY_nINT       A10     // active low interrupt input
#define  JOY_nRESET     A11     // active low reset output
*/
AndroidAccessory acc("Google, Inc.",
             "DemoKit",
             "DemoKit Arduino Board",
             "1.0",
             "http://www.android.com",
             "0000000012345678");
Servo servos[3];
// 10M ohm resistor on demo shield
CapSense   touch_robot = CapSense(TOUCH_SEND, TOUCH_RECV);
void setup();
void loop();
void init_buttons()
{
    pinMode(BUTTON1, INPUT);
    pinMode(BUTTON2, INPUT);
    pinMode(BUTTON3, INPUT);
/*
    pinMode(JOY_SWITCH, INPUT);
*/
    // enable the internal pullups
    digitalWrite(BUTTON1, HIGH);
    digitalWrite(BUTTON2, HIGH);
    digitalWrite(BUTTON3, HIGH);
/*
    digitalWrite(JOY_SWITCH, HIGH);
*/
}

void init_relays()
{
    pinMode(RELAY1, OUTPUT);
    pinMode(RELAY2, OUTPUT);
}

void init_leds()
{
    digitalWrite(LED1_RED, 1);
    digitalWrite(LED1_GREEN, 1);
    digitalWrite(LED1_BLUE, 1);
    pinMode(LED1_RED, OUTPUT);
    pinMode(LED1_GREEN, OUTPUT);
    pinMode(LED1_BLUE, OUTPUT);
    digitalWrite(LED2_RED, 1);
    digitalWrite(LED2_GREEN, 1);
    digitalWrite(LED2_BLUE, 1);
    pinMode(LED2_RED, OUTPUT);
    pinMode(LED2_GREEN, OUTPUT);
    pinMode(LED2_BLUE, OUTPUT);
    digitalWrite(LED3_RED, 1);
    digitalWrite(LED3_GREEN, 1);
    digitalWrite(LED3_BLUE, 1);
    pinMode(LED3_RED, OUTPUT);
    pinMode(LED3_GREEN, OUTPUT);
    pinMode(LED3_BLUE, OUTPUT);
}
/*
void init_joystick(int threshold);
*/
byte b1, b2, b3, b4, c;
void setup()
{
    Serial.begin(115200);
    Serial.print("\r\nStart");
    init_leds();
    init_relays();
    init_buttons();
/*
    init_joystick( 5 );
*/
    // autocalibrate OFF
    touch_robot.set_CS_AutocaL_Millis(0xFFFFFFFF);
    servos[0].attach(SERVO1);
    servos[0].write(90);
    servos[1].attach(SERVO2);
    servos[1].write(90);
    servos[2].attach(SERVO3);
    servos[2].write(90);

    b1 = digitalRead(BUTTON1);
    b2 = digitalRead(BUTTON2);
    b3 = digitalRead(BUTTON3);
/*
    b4 = digitalRead(JOY_SWITCH);
*/
    c = 0;
    acc.powerOn();
}
void loop()
{
    byte err;
    byte idle;
    static byte count = 0;
    byte msg[3];
    long touchcount;
    if (acc.isConnected()) {
        int len = acc.read(msg, sizeof(msg), 1);
        int i;
        byte b;
        uint16_t val;
        int x, y;
        char c0;
        if (len > 0) {
            // assumes only one command per packet
            if (msg[0] == 0x2) {
                if (msg[1] == 0x0)
                    analogWrite(LED1_RED, 255 - msg[2]);
                else if (msg[1] == 0x1)
                    analogWrite(LED1_GREEN, 255 - msg[2]);
                else if (msg[1] == 0x2)
                    analogWrite(LED1_BLUE, 255 - msg[2]);
                else if (msg[1] == 0x3)
                    analogWrite(LED2_RED, 255 - msg[2]);
                else if (msg[1] == 0x4)
                    analogWrite(LED2_GREEN, 255 - msg[2]);
                else if (msg[1] == 0x5)
                    analogWrite(LED2_BLUE, 255 - msg[2]);
                else if (msg[1] == 0x6)
                    analogWrite(LED3_RED, 255 - msg[2]);
                else if (msg[1] == 0x7)
                    analogWrite(LED3_GREEN, 255 - msg[2]);
                else if (msg[1] == 0x8)
                    analogWrite(LED3_BLUE, 255 - msg[2]);
                else if (msg[1] == 0x10)
                    servos[0].write(map(msg[2], 0, 255, 0, 180));
                else if (msg[1] == 0x11)
                    servos[1].write(map(msg[2], 0, 255, 0, 180));
                else if (msg[1] == 0x12)
                    servos[2].write(map(msg[2], 0, 255, 0, 180));
            } else if (msg[0] == 0x3) {
                if (msg[1] == 0x0)
                    digitalWrite(RELAY1, msg[2] ? HIGH : LOW);
                else if (msg[1] == 0x1)
                    digitalWrite(RELAY2, msg[2] ? HIGH : LOW);
            }
        }
        msg[0] = 0x1;
        b = digitalRead(BUTTON1);
        if (b != b1) {
            msg[1] = 0;
            msg[2] = b ? 0 : 1;
            acc.write(msg, 3);
            b1 = b;
        }
        b = digitalRead(BUTTON2);
        if (b != b2) {
            msg[1] = 1;
            msg[2] = b ? 0 : 1;
            acc.write(msg, 3);
            b2 = b;
        }
        b = digitalRead(BUTTON3);
        if (b != b3) {
            msg[1] = 2;
            msg[2] = b ? 0 : 1;
            acc.write(msg, 3);
            b3 = b;
        }
/*
        b = digitalRead(JOY_SWITCH);
        if (b != b4) {
            msg[1] = 4;
            msg[2] = b ? 0 : 1;
            acc.write(msg, 3);
            b4 = b;
        }
*/
        switch (count++ % 0x10) {
        case 0:
            val = analogRead(TEMP_SENSOR);
            msg[0] = 0x4;
            msg[1] = val >> 8;
            msg[2] = val & 0xff;
            acc.write(msg, 3);
            break;
        case 0x4:
            val = analogRead(LIGHT_SENSOR);
            msg[0] = 0x5;
            msg[1] = val >> 8;
            msg[2] = val & 0xff;
            acc.write(msg, 3);
            break;
        case 0x8:
/*
            read_joystick(&x, &y);
            msg[0] = 0x6;
            msg[1] = constrain(x, -128, 127);
            msg[2] = constrain(y, -128, 127);
            acc.write(msg, 3);
*/
            break;
        case 0xc:
            touchcount = touch_robot.capSense(5);
            c0 = touchcount > 750;
            if (c0 != c) {
                msg[0] = 0x1;
                msg[1] = 3;
                msg[2] = c0;
                acc.write(msg, 3);
                c = c0;
            }
            break;
        }
    } else {
        // reset outputs to default values on disconnect
        analogWrite(LED1_RED, 255);
        analogWrite(LED1_GREEN, 255);
        analogWrite(LED1_BLUE, 255);
        analogWrite(LED2_RED, 255);
        analogWrite(LED2_GREEN, 255);
        analogWrite(LED2_BLUE, 255);
        analogWrite(LED3_RED, 255);
        analogWrite(LED3_GREEN, 255);
        analogWrite(LED3_BLUE, 255);
        servos[0].write(90);
        servos[0].write(90);
        servos[0].write(90);
        digitalWrite(RELAY1, LOW);
        digitalWrite(RELAY2, LOW);
    }
    delay(10);
}
// ==============================================================================
// Austria Microsystems i2c Joystick
/*
void init_joystick(int threshold)
{
    byte status = 0;
    pinMode(JOY_SWITCH, INPUT);
    digitalWrite(JOY_SWITCH, HIGH);
    pinMode(JOY_nINT, INPUT);
    digitalWrite(JOY_nINT, HIGH);
    pinMode(JOY_nRESET, OUTPUT);
    digitalWrite(JOY_nRESET, 1);
    delay(1);
    digitalWrite(JOY_nRESET, 0);
    delay(1);
    digitalWrite(JOY_nRESET, 1);
    Wire.begin();
    do {
        status = read_joy_reg(0x0f);
    } while ((status & 0xf0) != 0xf0);
    // invert magnet polarity setting, per datasheet
    write_joy_reg(0x2e, 0x86);
    calibrate_joystick(threshold);
}

int offset_X, offset_Y;
void calibrate_joystick(int dz)
{
    char iii;
    int x_cal = 0;
    int y_cal = 0;
    // Low Power Mode, 20ms auto wakeup
    // INTn output enabled
    // INTn active after each measurement
    // Normal (non-Reset) mode
    write_joy_reg(0x0f, 0x00);
    delay(1);
    // dummy read of Y_reg to reset interrupt
    read_joy_reg(0x11);
    for(iii = 0; iii != 16; iii++) {
        while(!joystick_interrupt()) {}
        x_cal += read_joy_reg(0x10);
        y_cal += read_joy_reg(0x11);
    }
    // divide by 16 to get average
    offset_X = -(x_cal>>4);
    offset_Y = -(y_cal>>4);
    write_joy_reg(0x12, dz - offset_X);  // Xp, LEFT threshold for INTn
    write_joy_reg(0x13, -dz - offset_X);  // Xn, RIGHT threshold for INTn
    write_joy_reg(0x14, dz - offset_Y);  // Yp, UP threshold for INTn
    write_joy_reg(0x15, -dz - offset_Y);  // Yn, DOWN threshold for INTn
    // dead zone threshold detect requested?
    if (dz)
        write_joy_reg(0x0f, 0x04);
}

void read_joystick(int *x, int *y)
{
    *x = read_joy_reg(0x10) + offset_X;
    *y = read_joy_reg(0x11) + offset_Y;  // reading Y clears the interrupt
}
char joystick_interrupt()
{
    return digitalRead(JOY_nINT) == 0;
}

#define  JOY_I2C_ADDR    0x40
char read_joy_reg(char reg_addr)
{
    char c;
    Wire.beginTransmission(JOY_I2C_ADDR);
    Wire.write(reg_addr);
    Wire.endTransmission();
    Wire.requestFrom(JOY_I2C_ADDR, 1);
    while(Wire.available())
        c = Wire.read();
    return c;
}
void write_joy_reg(char reg_addr, char val)
{
    Wire.beginTransmission(JOY_I2C_ADDR);
    Wire.write(reg_addr);
    Wire.write(val);
    Wire.endTransmission();
}
*/

2012年1月30日 星期一

Netduino--紅外線動作感測器(PIR Motion Sensor)

紅外線動作感測器(Passive Infrared Sensor; PIR)

紅外線動作感測器 (PIR Motion Sensor) 是屬於被動式的紅外線裝置,感應器本身不會發射紅外線光束。可用來偵測人體運動、路口轉角的亮燈、警報、或機器人移動檢測。

以下使用Parallax 的 PIR 模組測試

image

模組的接腳圖,包含兩種模式(H、L),可接受+3.3V~+5V的電壓輸入,有一個輸出接腳(out)
Vdd : +3.3V ~ +5V

clip_image002

下圖是物體移動狀態(detection state)與感測輸出值sensor output)之間的變化

clip_image002[5]

1.PIR sensor 啟動時,需要有一段時間做 Calibration,依據不同的 sensor 有不同的時間,大約 10~60 秒,這個時間所輸出的值不具參考值。
2.沒有任何物體被偵測到移動時,輸出為 LOW。
3.在 H 模式,當有物體被偵測到移動時,輸出為 HIGH,然後又回到 LOW。
4.在 L 模式,如果有物體不斷的移動,輸出就會變成 HIGH LOW 不停變換,而不是維持在 HIGH 喔。

實驗線路圖

image

以下程式偵測 PIR 是否有產生 high 的訊號(代表偵測到生物移動),若有則點亮 Netduino 控制板上的 LED

範例程式

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;

namespace PIR_test
{
    public class Program
    {
        public static void Main()
        {
            // write your code here
            OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);

            InputPort PIR = new InputPort(Pins.GPIO_PIN_D3, false, Port.ResistorMode.Disabled);
            bool PIRState = false, preState = false;


            while (true)
            {
                PIRState = PIR.Read();    //取得PIR讀取的值  H:偵測到物體移動  L:物體靜止
                if (PIRState != preState)  
                {
                    led.Write(PIRState); 
                    preState = PIRState;
                    Debug.Print(PIRState.ToString());
                }
                Thread.Sleep(100);
            }
        }
    }
}

2012年1月11日 星期三

E4P 光學編碼器 (TETRIX)

這是搭配 TETRIX 內附馬達的編碼器套件

http://www.usdigital.com/products/encoders/incremental/rotary/kit/e4p

image

拆開包裝後到出所有的零配件,

image

其中有個像碟盤的東東還用個透明小手提箱裝著,似乎很寶貝的樣子,上面還有一圈小字

image 

近拍一張,應該是規格說明吧,另外還有一條條的細線,原廠網站規格說明共有360條

image

把電路部分裝到馬達軸上

image

再放入圓碟盤,最後蓋上蓋子,大功告成
四條電線照顏色(紅藍棕黃)依序是(+ A G B)
原理應該是馬達旋轉帶動軸上的碟盤,再由編碼器上的感應器偵測碟盤上的細線,產生脈衝訊號於A、B輸出

image

E4P的技術資料

image

image

image

image

馬達通電開始旋轉後,觀測A、B點的訊號變化就可以知道馬達向,計算每秒鐘接收到的pulse數量,就可知道轉速

正轉(A先 B後)

clip_image002[8]

反轉((A後 B先)

clip_image002

2012年1月8日 星期日

Netduino--I2C控制PCF8574A產生霹靂燈效果

I2C是由philips公司發展的通訊協定,使用兩條訊號線(SDA,SCL)再加一條地線,就可以串接將各種周邊裝置串接在一起,其最大的特色就是簡單容易實作,因此許多微控制器與周邊設備都採用I2C作為通訊。

支援I2C的裝置非常多,比較有名的例如記憶晶片EEPROM 24C64、溫濕度感應器SHT75、或是PCF8574A等,以下就以PCF8574A實驗,由Netduino經由I2C通訊控制連接於PCF8574A的8個LED,產生霹靂燈的效果。下圖是PCF8574A接腳說明。

clip_image002[5]

線路圖如下

clip_image002

程式如下

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;

namespace PCF8574A_test02
{
    public class Program
    {
        public static void Main()
        {
            I2CDevice.Configuration PCF8574A = new I2CDevice.Configuration(0x38, 400); //(address=0x38, ClockRate=400);
            I2CDevice MyI2CBus = new I2CDevice(PCF8574A);
            I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[1];
            Thread.Sleep(1000);

            int val = 0x01; 
            byte[] cmd = new byte[1]; 
            MyI2CBus.Config = PCF8574A;
            bool dir = true;

            while (true)
            {
                cmd[0] = (byte)(val ^ 0xffff);
                xActions[0] = I2CDevice.CreateWriteTransaction(cmd);
                MyI2CBus.Execute(xActions, 1000);  // (  , TransactionTimeout )

                // 產生霹靂燈效果
                if (val >= 0x80) dir = false;
                if (val <= 0x01) dir = true;
                if (dir)
                    val <<= 1;
                else
                    val >>= 1;
                Thread.Sleep(50);
            }
        }
    }
}

完成後會看到LED產生霹靂燈效果

PCF8574A I2C Netduino霹靂燈測試

2012年1月5日 星期四

Netduino--同時控制多個伺服馬達

netduino 有四個PWM接腳,分別是 pin5、6、9、10

若能同時控制這四個接腳輸出訊號,就可以同時控制四個伺服馬達

(PS.如果還要控制更多馬達,可能就要搭配74HC595來設計程式)

下圖為實驗線路圖

clip_image002

 

以下是完整程式

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;


namespace PWM_Server_Test02
{
    public class Program
    {
        const int TimeSlot = 10;
        const int ServoNum = 4;
        const int DefaultMinPos = 750;
        const int DefaultMaxPos = 2250;
        const int DefaultPos = 1500;

        struct s_stat
        {
            public int curr_pos, cmd_pos, step_pos, max_pos, min_pos;
            public bool stopflag;
            public PWM servo;
        }

        static s_stat[] servo_stat = new s_stat[ServoNum];  // curr_pos, cmd_pos, step_pos
        static Thread cth;

        //檢查伺服器位置是否超過極限邊界值
        static int CheckRange(int X, int min_pos, int max_pos)
        {
            return X < min_pos ? min_pos : (X > max_pos ? max_pos : X);
        }

        //初始化資料
        private static void InitialData()
        {
            servo_stat[0].servo = new PWM(Pins.GPIO_PIN_D5);  //指定 channel 0 馬達 就是接腳 pin5 所連的馬達
            servo_stat[1].servo = new PWM(Pins.GPIO_PIN_D6);  //指定 channel 1 馬達 就是接腳 pin6 所連的馬達
            servo_stat[2].servo = new PWM(Pins.GPIO_PIN_D9);  //指定 channel 2 馬達 就是接腳 pin9 所連的馬達
            servo_stat[3].servo = new PWM(Pins.GPIO_PIN_D10); //指定 channel 3 馬達 就是接腳 pin10 所連的馬達

            for (int i = 0; i < ServoNum; i++)
            {
                servo_stat[i].curr_pos = DefaultPos;   //curr_pos, cmd_pos, step_pos
                servo_stat[i].cmd_pos = DefaultPos;
                servo_stat[i].step_pos = 0;
                servo_stat[i].max_pos = DefaultMaxPos;
                servo_stat[i].min_pos = DefaultMinPos;
                servo_stat[i].stopflag = true;

                servo_stat[i].servo.SetPulse((uint)20000, 1500);  //回歸原點
            }

            // 建立多緒負責執行驅動核心
            cth = new Thread(new ThreadStart(control_core)); 
            cth.Start();
        }

        //多緒程式 驅動核心
        static void control_core()
        {
            int _step_pos;
            while (true)
            {
                for (int i = 0; i < ServoNum; i++)
                {
                    if (!servo_stat[i].stopflag)
                    {
                        if (servo_stat[i].curr_pos != servo_stat[i].cmd_pos)  //判斷目前位置  是否已經到達期望位置
                        {   //若尚未到達,則計算還剩多遠
                            _step_pos = servo_stat[i].cmd_pos - servo_stat[i].curr_pos;
                           //與原先設定的 step 距離相比,取較小者
                            if (System.Math.Abs(servo_stat[i].step_pos) < System.Math.Abs(_step_pos))
                                _step_pos = servo_stat[i].step_pos;
                            //計算下一步要前進的位置
                            servo_stat[i].curr_pos += _step_pos;
                      //檢查是否超出範圍
                      servo_stat[i].curr_pos = CheckRange(servo_stat[i].curr_pos, servo_stat[i].min_pos, servo_stat[i].max_pos);
                            servo_stat[i].servo.SetPulse((uint)20000, (uint)servo_stat[i].curr_pos);
                        }
                        else
                            servo_stat[i].stopflag = true;   //設定停止旗標
                    }

                }
                Thread.Sleep(TimeSlot);
            }
        }

        //設定伺服器位置
        public static void ServoMove(int channel, int pos, int speed)  //伺服器編號, 指定位置, 轉速
        {
            //檢查是否 out of range
            pos = CheckRange(pos, servo_stat[channel].min_pos, servo_stat[channel].max_pos);

            servo_stat[channel].cmd_pos = pos;
            int delta_pos = servo_stat[channel].cmd_pos - servo_stat[channel].curr_pos;
            int _step_pos = delta_pos / (speed / TimeSlot);
            servo_stat[channel].step_pos = _step_pos;
            servo_stat[channel].curr_pos += _step_pos;
        }

        //主測試程式
        public static void Main()
        {
            // write your code here
            InitialData();

            while (true)
            {
                ServoMove(0, 2000, 20);  //設定 channel 0 的 servo 快速轉動(速度20)至位置2000
                ServoMove(1, 1000, 40);
                ServoMove(2, 2000, 100); 
                ServoMove(3, 1000, 200);  //設定 channel 3 的 servo 慢速轉動(速度20)至位置1000
                Thread.Sleep(2000);

                ServoMove(0, 1000, 20); 
                ServoMove(1, 2000, 40);
                ServoMove(2, 1000, 100); 
                ServoMove(3, 2000, 200);
                Thread.Sleep(2000);
            }
        }
    }
}

以下是同時控制伺服器的影片

 

說實話,我覺得有點怪怪的,不過看起來勉強可以用啦

2012年1月3日 星期二

Netduino--伺服馬達控制

遙控模性中常見的伺服馬達(servo)的,連接線包含三條線,黑色接地,紅色接正電源(大多介於4.8V~7.2V之間,依伺服器規格而定),另一條控制線則接收控制板送來的訊號,轉動伺服器的擺臂至固定角度 。

image

大部分伺服器接收的訊號為20ms的週期脈波,藉由操控高電位的長度(工作週期duty cycle),可讓伺服器知道該讓擺臂轉到哪個角度,例如下圖表說明了當duty cycle=1.0ms(1000us)時,擺臂位置位於-45度,當duty cycle=1.5ms(1500us)時,擺臂位置位於0度。

clip_image002

伺服器能接受的duty cycle值,多介於750us~2250之前,依伺服器規格而定。

以下程式片段控制接在pin9的伺服馬達,設定duty cycle=1500,可讓伺服器擺臂轉動至中間位置

static PWM servo = new PWM(Pins.GPIO_PIN_D9);

servo.SetPulse((uint)20000, pos); // (全週期, 工作週期)

實驗線路如下圖

clip_image002[6]

完整程式如下表,程式執行後會看到擺臂不斷的在左-中-右-中-左來回擺動。

namespace PWM_伺服馬達測試
{
    public class Program
    {
        static PWM servo = new PWM(Pins.GPIO_PIN_D9);

        public static void ServoMove(uint pos)
        {
            servo.SetPulse((uint)20000, pos); // (全週期, 工作週期)
            Thread.Sleep(1000);
        }
      
        public static void Main()
        {
            while (true)
            {
                ServoMove(1500);  //工作週期
                ServoMove(2250);
                ServoMove(1500);  //工作週期
                ServoMove( 750);
            }
        }
    }
}

2012年1月1日 星期日

Netduino--串列週邊介面(SPI)控制 LED

Netduino提供串列週邊介面功能(SPI),可原先並列資料傳輸,轉換成用串列資料傳輸方式,這可以節省 Netduino 使用的接腳數,

當控制器的接腳不夠用時,就可以使用SPI功能,將資料傳輸到移位暫存器(例如74HC595),達到擴充腳位的效果

下圖是移位暫存器 74HC595 的工作範例圖,

clip_image002

由圖可看出來自於微控器的序並資料(10010111)由SER接腳進入74HC595,資料會拆散依序放在QA~QH,變成並列資料輸出,

以下是實驗電路圖,

clip_image002

 

程式如下,可以控制LED產生霹靂燈的效果

namespace T74HC595_TEST02
{
    public class Program
    {
        public static void Main()
        {
            SPI.Configuration config =
                new SPI.Configuration(ChipSelect_Port: Pins.GPIO_PIN_D9,  
                                      ChipSelect_ActiveState: false,
                                      ChipSelect_SetupTime: 0,
                                      ChipSelect_HoldTime: 0,
                                      Clock_IdleState: true,
                                      Clock_Edge: false,
                                      Clock_RateKHz: 10000,
                                      SPI_mod: SPI.SPI_module.SPI1);

            SPI _spi = new SPI(config);
            byte[] data = new byte[1];

            // 產生霹靂燈效果
            while (true)
            {
                data[0] = 1;
                for (int i=0; i<7; i++)  //燈向左移動
                {
                    _spi.Write(data);
                    data[0] <<= 1;
                    Thread.Sleep(100);
                }
                for (int i = 0; i < 8; i++) //燈向右移動
                {
                    _spi.Write(data);
                    data[0] >>= 1;
                    Thread.Sleep(100);
                }
            }
        }
    }
}