2011年12月31日 星期六

Netduino--播放音樂

聲音如何產生 ?

聲音是音頻振動的效果,音頻的範圍是20Hz到200kHz,

人耳可辨識的範圍約是200Hz到20kHz。喇叭或蜂鳴器可將電器信號轉換成聲音,一般音響電路是以正弦波信號驅動喇叭,以產生悅耳音樂。

clip_image002

在數位電路裡,則是以脈波信號驅動蜂鳴器,產生聲音。

clip_image004

如何讓喇叭發出聲音呢 ?

將電池電源正負極分別接到喇叭的兩端,就會聽到”喀、喀”,那是電流通過線圈產生磁力,與中央帶有磁性的鐵塊產生互斥(或相吸),帶動黑色的喇叭紙盆前後震動所發出的聲音

clip_image006 clip_image008

如果要發出特定音調(例如低音 Do 頻率是262Hz),只要利用程式控制某個輸出腳位的輸出電壓不斷的0、1 變化(也就是0V、5V變化),若變化的頻率與低音Do相同,就可以聽到了。

如何控制輸出腳位產生0、1 變化,讓喇叭發出音調 ?

clip_image010

例如下列程式片段可以控制第9號輸出腳位,產生周期為1000us=1ms=0.001秒的方波(相當於頻率為1 / 0.001=1000Hz),約等於中音Si。

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

    speaker.SetPulse(1000,500);   // (全週期, 半週期)

 

 

實驗線路圖如下

clip_image013[4]

 

程式如下,可以演奏一小段旋律

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

namespace MyMusic
{
    public class Program
    {

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

        static char[] music = "geefddcdefggg geefddceggc ".ToCharArray();  //音樂簡譜
        static int[] beats = { 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 0,   //音樂節拍表
                               1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 4, 8 };
        static int tempo = 300;  //單一拍的時間長度

        /// <summary>
        /// 控制喇叭發出聲音
        /// </summary>
        /// <param name="tone">音調半周期時間</param>
        /// <param name="duration">聲音長度</param>
        static void mTone(int tone, int time)
        {   // SetPulse (uint period_ns, unit hightime_ns);
            speaker.SetPulse((uint)( tone * 2), (uint) tone);  // (全週期, 半週期)
            Thread.Sleep(time);
        }

        /// <summary>
        /// 控制喇叭發出聲音
        /// </summary>
        /// <param name="note">音調符號</param>
        /// <param name="duration">聲音長度</param>
        static void mTone(char ch, int time)
        {
            int tone;
            switch (ch) {  //定義聲音PWM的半周期時間(ns)
                case 'c': tone = 1915; break;
                case 'd': tone = 1700; break;
                case 'e': tone = 1519; break;
                case 'f': tone = 1432; break;
                case 'g': tone = 1275; break;
                case 'a': tone = 1136; break;
                case 'b': tone = 1014; break;
                case 'C': tone =  956; break;
                default: tone = 0; break;
            }
            mTone(tone, time);
        }

        //播放音樂
        public static void Main()
        {
            while (true)
            {
                for (int i=0; i<music.Length; i++)
                {
                    mTone(music[i], beats[i] * tempo);
                    mTone(0, tempo/10);  //每音符之後斷音一下子
                }
            }
        }
    }
}

 

2011年12月30日 星期五

Netduino--控制LCD螢幕

Szymon Kobalczyk 在網路上發表了LCD程式庫--μLiquidCrystal,支援晶片組為HD44780的LCD螢幕。

藉由程式庫我們可以很方便的完成LCD顯示功能的專題。

將程式庫下載回來後,解壓縮後會產生 LiquidCrystal.dll 檔案,以及範例程式

我們可以設計一個小時鐘程式,實驗線路非常簡易,如下圖

clip_image002 

程式內容如下

 

using Garmf;

namespace LCDTest
{
    public class Program
    {
        public static void Main()
        {           
            // 初始畫LCD的對應腳位
            LiquidCrystal lcd = new LiquidCrystal(
                Pins.GPIO_PIN_D2,       // rs       lcd.pin4
                Pins.GPIO_PIN_D4,       // enable   lcd.pin6
                Pins.GPIO_PIN_D8,       // db4     lcd.pin8
                Pins.GPIO_PIN_D9,       // db5     lcd.pin9
                Pins.GPIO_PIN_D10,      // db6     lcd.pin10
                Pins.GPIO_PIN_D11);     // db7     lcd.pin11
               
            lcd.Begin(16, 2);  //設定螢幕為 16字 x 2列
            lcd.Clear();

            lcd.SetCursor(0, 0);   //設定游標位置 col=0, row=0
            lcd.Print("Hello World!");  //顯示訊息

            while (true)
            {
                lcd.SetCursor(0, 1);  //設定游標位置 col=0, row=1
                lcd.Print(DateTime.Now.ToString("hh:mm:ss"));  //顯示目前時間
            }
        }
    }
}

編譯時如果出現錯誤,可能是沒有將 LiquidCrystal.dll 加入參考,

需要將LiquidCrystal.dll加入方案總管內的[參考]之下

clip_image003

點選  專案名稱->滑鼠右鍵->加入參考->瀏覽->找到LiquidCrystal.dll->確定

clip_image005

最後記得在程式開頭加上 using Garmf; 就可成功編譯。

執行結果如下圖,這就是一個小時鐘程式

clip_image004

2011年12月29日 星期四

Netduino--PWM電子調光器

脈波寬度調整 PWM(Pulse-width Modulation) 可用來調整輸出端的平均電壓,它的原理是藉由調整脈波的工作周期,造成平均電壓值的變化,例如圖一的脈波的高電位約占整個周期的 10%,圖二的高電位佔周期的50%,圖三的高電位佔周期的90%,因此圖三的平均電壓較高,若接LED也會比較亮。

image

image

image

藉由調整duty cycle 的比例值,就可控制電壓高低,也就是LED的亮度
例如下列電路及程式,LED會逐漸變亮,瞬間變暗,不斷重複
image

//測試 PWM 控制 LED
//LED正極接 pin 6,負極接地
    public class Program
    {
        static PWM red = new PWM(Pins.GPIO_PIN_D6);

        public static void Main()
        {
            // write your code here
            int Intensity = 0;
            while (true)
            {
                Intensity = (Intensity + 1) % 100;  //遞增Intensity值,若超過99自動歸0
                red.SetDutyCycle((uint)Intensity);  //設定PWM脈波寬度比例 0~99

                Thread.Sleep(1);
            }
        }
    }

2011年12月28日 星期三

Netduino--讀取按鈕狀態控制LED亮(或滅)--事件觸發


若使用迴圈的方式來偵測是否按下按鈕(如下程式片段)
  while (true)
  {
      buttonState = button.Read();
      led.Write(!buttonState);
  }
這代表CPU會花很多時間在空轉,這種方法也稱為輪詢(polling)
若能改為由系統偵測是否按下按鈕發出中斷 Interrupt,然後在中斷服務常式內撰寫程式,可以使系統執行更有效率,也讓程式更為簡潔
改寫程式如下,這樣就比較接近 event-driven 的架構 
public class Program
    {
        static OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);
        public static void Main()
        {
            InterruptPort button = new InterruptPort(Pins.ONBOARD_SW1, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
            button.OnInterrupt += new NativeEventHandler(button_OnInterrupt);  //向系統註冊,若button發生interrupt時,就執行button_OnInterrupt函式
            Thread.Sleep(Timeout.Infinite);
        }
        static void button_OnInterrupt(uint data1, uint data2, DateTime time) //自行定義中斷服務常式        {
            if (data2 == 0)  //若按鈕被按下
                   led.Write(true);
        }
    }
大部分的時間CPU都在休息,但是只要按下按鈕,系統送出按鈕中斷,觸發使用者定義的中斷服務常式,就執行函數內的程式

2011年12月26日 星期一

Netduino--讀取按鈕狀態控制LED亮(或滅)


netduino控制板上內建一個按鈕,還有一個LED可供程式控制
image
例如以下程式,就可以讀取按鈕狀態,按下按鈕時LED會亮,放開按鈕LED熄滅
 
public static void Main()
        {
            // write your code here
            OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);
            InputPort button = new InputPort(Pins.ONBOARD_SW1, false, Port.ResistorMode.Disabled);
            bool buttonState = false;
            while (true)
            {
                buttonState = button.Read();
                led.Write(!buttonState);
            }
        }

2011年12月25日 星期日

安裝 netduino

新玩具netduino到手,這是一塊類似 Arduino 的實驗板,搭配 Vusial StudionC# express 2010 作為程式開發環境

clip_image001

一、下載安裝以下軟體
1. Microsoft Visual C# Express 2010
http://www.microsoft.com/express/downloads/
2. Microsoft .NET Micro Framework v4.1 SDK
http://www.netduino.com/downloads/MicroFrameworkSDK.msi
3. Netduino SDK v4.1
http://www.netduino.com/downloads/netduinosdk_32bit.exe (for 32-bit Windows)
http://www.netduino.com/downloads/netduinosdk_64bit.exe (for 64-bit Windows)
二、安裝完畢後接上 netduino 的USB連接線,Win7 會自動抓到驅動程式,認得板子

三、啟動 Visual Studio 2010
New Project -> Visual C# -> Micro Framework -> Netduino Application
clip_image001

四、在右側的方案總管,Double-click program.cs,撰寫閃光燈程式

clip_image002

public static void Main()
{
    // write your code here
    OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);
    while (true)
    {
        led.Write(true); //點亮LED
        Thread.Sleep(250); //等候 250ms
        led.Write(false); //熄滅LED
        Thread.Sleep(250); //等候 250ms
    }
}

五、更正專案設定,點選專案 -> 專案屬性 -> NET Micro Framework -> Transport (改成 USB)

clip_image003

六、按下F5執行程式,就會看到netduino上的板子的小燈開始閃爍

 

七、若按下ctrl+alt+break 可以暫停程式進行除錯,點選F10單步執行程式可以觀察執行流程