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);
                }
            }
        }
    }
}