Arduino Leaning

วันพฤหัสบดีที่ 30 พฤศจิกายน พ.ศ. 2560

Arduino Web Sever (ควบคุม LED ด้วย Label) บทที่ 2

Arduino Web Sever (ควบคุม LED ด้วย Label)

cradit by :myarduino.com
Arduino Web Sever (ควบคุม LED ด้วย Label)
ในบทความนี้จะสอนการใช้งาน Label ควบคุมการเปิดปิดไฟ LED ครับ โดยการเขียนภาษา PHPและHTML ลงไปใน arduino ควรศึกษาภาษา PHPและHTML ก่อนนะครับ จะได้ใช้ง่ายขึ้น เมื่อเรากด Label มันจะส่งค่ากลับมาทาง URL เราก็จะเอาค่านั้นมาใช้ครับ Ethernet Shild W5100

 
ตัวอย่าง Code
#include "SPI.h"
#include "Ethernet.h"
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
EthernetServer server(80); //server port
String readString; 
//////////////////////
void setup(){
  pinMode(5, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac);

}
void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        //read char by char HTTP request
        if (readString.length() < 100) {
          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 
        //if HTTP request has ended
        if (c == '\n') {
          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();
          client.println("");
          client.println("");
          client.println("Myarduino Control LED");
          client.println("");
          client.println("");
          client.println("

Myarduino Control LED

");
          // DIY buttons
          client.println("

ON

"); 
          client.println("

OFF

"); 
          client.println("");
          client.println("");
          client.println("");
          client.println("");
          delay(1);
          //stopping client
          client.stop();
          ///////////////////// control arduino pin
          if(readString.indexOf("on1") >0)//checks for on
          {
            digitalWrite(5, HIGH);    // set pin 5 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off2") >0)//checks for off
          {
            digitalWrite(5, LOW);    // set pin 5 low
            Serial.println("Led Off");
          }
          readString="";
        }
      }
    }
  }
}
ตัวอย่าง Vdieo

Arduino Web Server with Ethernet Shield W5100 (อธิบายการทำงานเบื่องต้น) บทที่ 1

Arduino Web Sever (อธิบายการทำงานเบื่องต้น)


      เราจะรวบคุม Arduino ผ่านเน็ตได้เราต้องมี Ethernet Shild W5100  ต่อกับ Arduino แล้วเราก็เอาสายแลนจาก Router มาต่อกับ Ethernet Shild หลักการง่ายมากครับ เหมือนเราใช้มือถือ หรือ คอมพิวเตอร์ ต่อกับ Router แล้ว Router มันจะแจก IP addressให้เราครับ IP addressจะเปลี่ยนไปทุกครั้งที่เราเชื่อมต่อ เมื่อเราได้ IP address Arduino แล้ว เราก็เอา IP address Arduino มาวาง ที่วางลิ้ง  สมมุติ ผมจะเข้า google ผมก็เอา www.google.com มาวางในที่ใส่ URL แต่ถ้าผมต้องการเข้า Arduino ผ่านเว็บผมก็เอา URL ของ Arduinoมาวาง เช่น 192.168.1.1 
    เมื่อเราได้ IP address ของ Arduino มาแล้ว เราต้องเขียนหน้าเว็บใส่ไปใน Arduino ด้วยครับ  เราสามารถเอาค่าจาก Arduinoมาขึ้นที่เว็บได้ หรือ จะเอาคำสั่งที่เรากดในเว็บ ส่งมาให้ Arduino ทำงานก็ได้ครับ
 ตรวจสอบ IP address  Arduino
สิ่งแรกที่เราต้องทำคือ เราต้องรู้ก่อนว่า Router แจก IP address อะไรให้ Arduino เราโดยใช้ Codeนี้ตรวจสอบ

/*
* Simple Client to display IP address obtained from DHCP server
* Arduino 1.0 version
*/
#include "SPI.h"
#include "Ethernet.h"

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte server[] = { 173,194,126,119 }; // www.google.co.th

EthernetClient client;

void setup()
{
Serial.begin(9600);
if(Ethernet.begin(mac) == 0) { // start ethernet using mac & DHCP
Serial.println("Failed to configure Ethernet using DHCP");
while(true) // no point in carrying on, so stay in endless loop:
;
}
delay(1000); // give the Ethernet shield a second to initialize

Serial.print("This IP address: ");
IPAddress myIPAddress = Ethernet.localIP();
Serial.print(myIPAddress);
if(client.connect(server, 80)>0) {
Serial.println(" connected");
client.println("GET /search?q=arduino HTTP/1.0");
client.println();
} else {
Serial.println("connection failed");
}
}

void loop()
{
if (client.available()) {
char c = client.read();
// uncomment the next line to show all the received characters
// Serial.print(c);
}

if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
for(;;)
;
}
}

เราจะได้ IP address Arduino มดูใน Serial Monitor จะได้ตามนี้ครับ
 


การส่งค่าจาก Arduino มาแสดงบน Web Broswer
ต่อมาเราจะอ่านค่า Analog read  จากArduino มาแสดงใน web Broswer
คำสั่งหลักๆ ที่ใช้คือ  client.print() และ  client.println() ซึ่งจะเป็นการส่งข้อความ
แบบ HTMLไปแสดงบน web Broswer ครับ ลักษณะการเขียน ถ้าสังเกตดูจะเป็นการ
เขียนโปรแกรนบน Arduino IDE เพื่อสร้าง HTML CODE ครับ

 
/*
Web Server

A simple web server that shows the value of the analog input pins.
using an Arduino Wiznet Ethernet shield.

Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
* Analog inputs attached to pins A0 through A5 (optional)

created 18 Dec 2009
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe

*/

#include
#include

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);

void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}


// start the Ethernet connection and the server:
Ethernet.begin(mac);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}


void loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("");
client.println("");
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("analog input ");
client.print(analogChannel);
client.print(" is ");
client.print(sensorReading);
client.println("
");
}
client.println("");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}
 

เมื่อใส่ Ip address Arduino ลงไปในช่อง URL จะเจอหน้าเว็บนี้ครับ เป็นหน้าเว็บที่เราเขียนใส่ลงไปใน Arduino อ่านค่า Analog read


การเขียนโปรแกรมเบื้องต้นกับ Arduino (การส่งข้อมูลแบบ SPI)

การสื่อสารระหว่าง SPI Master และ Slave โดยใช้บอร์ด Arduino

บทความนี้กล่าวถึงการเขียนโปรแกรมเพื่อทำให้บอร์ด Arduino จำนวน 2 บอร์ด สามารถสื่อสารกันด้วยบัส SPI โดยที่บอร์ดหนึ่งทำหน้าที่เป็นอุปกรณ์ SPI Master และอีกบอร์ดหนึ่งทำหน้าที่เป็น SPI Slave
คำสำคัญ / Keywords: Arduino, SPI Interfacing, SPI Master / Slave, RC Servo Output
SPI Bus (Serial Peripheral Interface Bus) เป็นรูปแบบหนึ่งของการสื่อสารข้อมูลระหว่างอุปกรณ์แบบดิจิทัลที่พบเห็นได้บ่อย และใช้กับอุปกรณ์ได้มากกว่าสองขึ้นไปและนำมาต่อกันเป็นบัส (Bus)   บัส SPI ส่งและรับข้อมูลทีละบิต (Bit Serial) และใช้สัญญาณ Clock เป็นตัวกำหนดจังหวะการทำงาน (ดังนั้นจึงเรียกว่า Synchronous, Bit-Serial Data Communication) มีการกำหนดบทบาทในการทำงานของอุปกรณ์ในระบบบัส แบ่งเป็น SPI Masterและ SPI Slave โดยที่ SPI Master เป็นฝ่ายเริ่มการสื่อสารข้อมูล และสร้างสัญญาณ Clock (มักใช้ชื่อสัญญาณว่า SCK) มากำหนดจังหวะการส่งและรับข้อมูล และด้าน SPI Slave จะเป็นฝ่ายคอยตอบสนอง และในระบบบัส SPI อาจมีอุปกรณ์ที่เป็น SPI Slaveได้มากกว่าหนึ่ง (Single-Master, Multi-Slave)

SPI ใช้สัญญาณ 4 เส้น (ใช้งานในแบบที่เรียกว่า 4-Wire SPI) ได้แก่
  • SCK (Serial Clock) — เป็นสัญญาณ CLK ที่ถูกสร้างโดยอุปกรณ์ที่เป็น SPI Master
  • MOSI (Master-Out Slave-In) — เป็นสัญญาณสำหรับส่งข้อมูลบิตออกจาก SPI Master ไปยัง SPI Slave
  • MISO (Master-In Slave-Out) — เป็นสัญญาณสำหรับส่งข้อมูลบิตออกจาก SPI Slave ไปยัง SPI Master
  • /SS (Slave Select, Active-Low) — เป็นสัญญาณที่สร้างโดย SPI Master เพื่อใช้ระบุว่า ต้องการสื่อสารกับ SPI Slave หรือไม่ — ในกรณีที่มีอุปกรณ์ SPI Slave มากกว่าหนึ่งชุด จะต้องมีสัญญาณ Slave Select มากกว่าหนึ่งเส้น และแยกสำหรับแต่ละอุปกรณ์
เมื่อจะส่ง-รับข้อมูลผ่านบัส SPI (เรียกว่า SPI Data Transfer) สัญญาณ Slave Select (/SS) จะต้องเปลี่ยนจาก HIGH เป็น LOW จากนั้นข้อมูลหนึ่งไบต์จะถูกเลื่อนบิตและส่งออกไปทีละบิตจาก SPI Master ตามจังหวะของ SCK และเลือกได้ว่าจะให้บิต MSB (Most-Significant Bit) หรือ LSB (Least-Significant Bit) ถูกส่งออกไปก่อน และในขณะเดียวกันก็จะรับข้อมูลทีละบิตจาก SPI Slave จนได้ครบหนึ่งไบต์ (หรือกล่าวได้ว่า Data Frame = 8 บิต) ดังนั้นเมื่อ SPI Master ส่งข้อมูลจำนวนหนึ่งไบต์ไปยัง SPI Slave ก็จะได้ข้อมูลหนึ่งไบต์จาก SPI Slave เช่นกัน ในช่วงเวลาที่สัญญาณ /SS เป็น LOW อาจมีการส่ง-รับข้อมูลได้มากกว่าหนึ่งไบต์ (Multi-byte SPI transfer)
การทำงานของ SPI มี 4 โหมด จำแนกตามพารามิเตอร์สองตัวที่เรียกว่า CPOL (Clock Polarity) และ CPHA (Clock Phase) ซึ่งจะเป็นตัวกำหนดลักษณะการทำงานอย่างเช่น จะส่ง-รับบิตที่ขอบขาขึ้นหรือลงของสัญญาณ CLK และสัญญาณ CLK จะอยู่ที่ลอจิก HIGH หรือ LOW เมื่อไม่อยู่ในช่วงของการส่งข้อมูลใดๆในบัส SPI (ช่วงที่เรียกว่า Idle) แต่โดยทั่วไปจะใช้ SPI Mode 0

การเปรียบเทียบระหว่าง SPI และ I2C ในการใช้งาน

I2C เป็นอีกรูปแบบการสื่อสารข้อมูลแบบดิจิทัลในประเภทที่เรียกว่า Synchronous, Bit-Serial Data Communication นิยมใช้งานอย่างแพร่หลายเช่นเดียวกับ SPI ลองมาดูตัวอย่างการเปรียบเทียบประเด็นในการใช้งานของบัสทั้งสองแบบ

ประเด็นSPII2C
การรับส่งข้อมูลสองทิศทางfull-duplexhalf-duplex
ความเร็วโดยทั่วไป100kHz หรือ 400kHzได้สูงกว่า 10MHz
จำนวนสัญญาณเพื่อเชื่อมต่อ4 หรือมากกว่า2 (SCL และ SDA)
การเลือกสื่อสารกับ Slaveใช้สัญญาณ Slave Selectระบุ Device Address
การตอบกลับเมื่อได้รับข้อมูลไม่มี Acknowledgeใช้ ACK/NACK bit
ขนาด Data Frame8 บิต8 บิต

ถ้าลองสำรวจอุปกรณ์หรือไอซีบางประเภท ก็จะพบว่า สามารถสื่อสารข้อมูลได้ทั้งแบบ SPI และ I2C อย่างเช่น ไอซีเซนเซอร์วัดความเร่งหรือความเร็วเชิงมุม

แหล่งข้อมูลอ้างอิงและศึกษาเพิ่มเติม:

Arduino Sketch สำหรับตัวอย่างแรก

ตัวอย่างแรกสาธิตการเขียนโปรแกรมให้บอร์ด Arduino (เช่น Arduino Uno) ให้ทำหน้าที่เป็น SPI Master และอีกบอร์ดหนึ่งเป็น SPI Slave และสาธิตการส่ง-รับข้อมูลหลายไบต์ในช่วงที่สัญญาณ /SS เป็น LOW   เมื่อ SPI Master ได้ส่งข้อมูลหนึ่งไบต์ไปยัง SPI Slave ได้แล้ว ข้อมูลไบต์นี้จะถูกส่งกลับไปยัง SPI Master ในการส่งและรับข้อมูลไบต์ครั้งถัดไป ในตัวอย่างนี้ได้เลือกใช้ความเร็ว SCK เท่ากับ 2MHz (โดยการเลือกตัวหารความถี่เท่ากับ 8 และบอร์ด Arduino ใช้ความถี่เท่ากับ 16MHz)
[Wiring Diagram for Arduino Uno boards]

  Arduino Master  Arduino Slave
  D13 = SCK  ---- SCK  = D13
  D12 = MISO ---- MISO = D12
  D11 = MOSI ---- MOSI = D11
  D10 = SS   ---- SS   = D10
        GND  ---- GND
การเชื่อมต่อระหว่างบอร์ด Arduino ทั้งสองบอร์ดให้สื่อสารกแบบ SPI ได้นั้น ให้ต่อสายสัญญาณตาม Wiring Diagram ที่ให้ไว้ (สำหรับ Arduino Uno, 328P, 5V / 16MHz) ได้แก่สัญญาณ SCK, MISO, MOSI, SS และอย่าลืมต่อขา GND ร่วมกัน
ถ้าดูจากโค้ดตัวอย่าง Arduino ที่เป็น SPI Master จะส่งข้อมูลเป็นชุดๆละ 6 ไบต์ โดยไบต์แรกจะเป็นค่าของตัวนับขนาด 8 บิต (เพิ่มค่าทีละหนึ่ง) แล้วตามด้วยไบต์ 0x55, 0x3C, 0xC3, 0xFF, 0x00 ตามลำดับ และเมื่อส่งจนครบ 6 ไบต์ ก็จะได้ข้อมูลจำนวน 6 ไบต์จาก SPI Slave ตามลำดับต่อไปนี้ (?? หมายถึง ไบต์ที่เป็นค่าของตัวนับขนาด 8 บิต)
  Master to Slave: [??][55][3C][C3][F0][00]
  Slave to Master: [00][??][55][3C][C3][FF]

Sourcecode: spi-test-master.ino
////////////////////////////////////////////////////////////////////////
// Author: RSP @ Embedded Systems Lab (ESL), KMUTNB
// Date: 22-Apr-2014
// Target Board: Arduino Uno (ATmega328P, 5V, 16MHz)
// Arduino IDE: version 1.0.5
// Description: This Arduino sketch shows how to use an Arduino Uno board
//   as an SPI master device which sends and receives a block of data bytes 
//   periodically through the SPI. The SCK frequency is set to 2MHz.
////////////////////////////////////////////////////////////////////////

// SPI Master

#include <SPI.h> // use the Arduino SPI library
/*
  SPI Pins for Arduino Uno:
    D13 = SCK, D12 = MISO, D11 = MOSI, D10 = SS
  SPI Pins for Arduino Mega:
    D50 = MISO, D51 = MOSI, D52 = SCK, D53 = SCK
 */

#define SS          (10)
#define BUTTON_PIN  (2)
#define LED_PIN     (3)

void setup() {
  Serial.begin( 115200 );          // use Serial, baudrate = 115200
  pinMode( BUTTON_PIN, INPUT );
  digitalWrite( BUTTON_PIN, HIGH ); // enable internal pull-up for button input
  pinMode( LED_PIN, OUTPUT );
  digitalWrite( LED_PIN, LOW );
  
  SPI.begin();
  SPI.setDataMode( SPI_MODE0 );
  SPI.setBitOrder( MSBFIRST );
  // set clock divider for SCK 
  // use SPI_CLOCK_DIVx, where x=4,8,16,32,64,128
  SPI.setClockDivider( SPI_CLOCK_DIV8 ); // -> 16MHz/8 = 2MHz
  digitalWrite( SS, HIGH );
  Serial.println( "SPI Master..." );
  delay( 1000 );
}

#define SPI_TX_DELAY  (10)      // in microseconds

void spi_transferx( uint8_t *wdata, uint8_t *rdata, uint8_t num ) {
  digitalWrite( SS, LOW );
  for ( uint8_t i=0; i < num; i++ ) {
     rdata[i] = SPI.transfer( wdata[i] ); 
     delayMicroseconds( SPI_TX_DELAY ); // give the SPI slave some time to process
  }
  digitalWrite( SS, HIGH );
}

boolean state = false;

const int BUF_SIZE = 6; // set the buffer size 
const uint8_t TEST_DATA_BYTES[] = { 0x55, 0x3C, 0xC3, 0xF0 }; 
uint8_t data_buf[ BUF_SIZE ];
uint8_t count = 0;
char sbuf[32]; // used for sprintf()

void loop() {
  if ( digitalRead( BUTTON_PIN ) == LOW ) { // check whether the button is pressed
    while ( digitalRead( BUTTON_PIN ) == LOW ) ; // wait until the button is released
    // write data bytes to the data buffer
    memset( data_buf, 0x00, BUF_SIZE );
    data_buf[0] = count++;
    memcpy( data_buf+1, TEST_DATA_BYTES, sizeof( TEST_DATA_BYTES ) );
    // write/read the SPI bus
    spi_transferx( data_buf, data_buf, BUF_SIZE );
    Serial.print( "Read: " );
    for ( uint8_t i=0; i < BUF_SIZE; i++) { // show the received data bytes
      sprintf( (sbuf+4*i), "%02Xh ", data_buf[i] ); 
    }
    Serial.println( sbuf ); 
    state = !state; // toggle state for LED
  }
  digitalWrite( LED_PIN, state ); // update LED output
  delay(10);
}

///////////////////////////////////////////////////////////////////////

Sourcecode: spi-test-slave.ino
////////////////////////////////////////////////////////////////////////
// Author: RSP @ Embedded Systems Lab (ESL), KMUTNB
// Date: 22-Apr-2014
// Target Board: Arduino Uno (ATmega328P, 5V, 16MHz)
// Arduino IDE: version 1.0.5
// Description:
//   This Arduino Sketch shows how to use Arduino Uno as an SPI 
//   slave device which receives/sends a block of data bytes 
//   through the SPI bus. Each received data byte will be sent back
//   to the SPI master in the next-byte transfer.
////////////////////////////////////////////////////////////////////////

// SPI Slave

#include <SPI.h> // use the Arduino SPI library
#include <Servo.h>

#define SS  (10)  // specify the SPI Slave Slect pin

volatile uint8_t data = 0;

ISR(SPI_STC_vect) { // ISR for 'SPI reception complete'
  data = SPDR;
  SPDR = data;
} 

void setup() {
  Serial.begin( 115200 );     // use Serial port and baudrate=115200
  pinMode( MISO, OUTPUT );    // configure the MISO pin as output
  digitalWrite( SS, HIGH );   // enable pull-up on the SS pin
  SPCR |= _BV(SPE);           // set SPE bit in SPCR to enable SPI
  //SPI.attachInterrupt();    // enable SPI interrupt
  SPCR |= _BV(SPIE);
  // In Slave mode the MSTR bit in SPCR is clear.
  Serial.println( "SPI Slave..." );
}

void loop() {
  if ( digitalRead( SS ) ) {
    SPDR = 0x00; 
  }
  delay(10);
}

////////////////////////////////////////////////////////////////////////


รูปแสดงตัวอย่างการต่อวงจรโดยใช้บอร์ด Arduino จำนวน 2 บอร์ด
เชื่อมต่อเข้าด้วยกันแบบ SPI


รูปแสดงคลื่นสัญญาณ SCK (1) และ MOSI (2)
ทำงานใน Mode 0, MSB First

รูปแสดงคลื่นสัญญาณ SCK ซึ่งได้ความถี่ 2MHz
อ่านค่าของไบต์ที่ถูกส่งผ่าน MOSI=0x55

รูปแสดงคลื่นสัญญาณ SCK (1) และ MOSI (2)
อ่านค่าข้อมูลที่ถูกส่งผ่าน MOSI=0x3C

รูปแสดงคลื่นสัญญาณ SCK (1) และ /SS (2)
แสดงช่วงที่ /SS เปลี่ยนจาก High เป็น LOW

รูปแสดงคลื่นสัญญาณ MOSI (1) และ MISO (2)
อ่านค่าไบต์: MOSI=0x55 และ MISO=0x01

รูปแสดงคลื่นสัญญาณ MOSI (1) และ MISO (2)
อ่านค่าไบต์: MOSI=0x55 และ MISO=0x05

Arduino Sketch สำหรับตัวอย่างที่สอง

ตัวอย่างที่สองสาธิตการทำให้บอร์ด Arduino ทำหน้าที่เป็น SPI Slave และสร้างสัญญาณ R/C Servo จำนวน 4 ช่อง (50Hz คงที่) สัญญาณ R/C Servo ทุกช่องมีค่า Duty Cycle เริ่มต้นเป็น 1500μsec แต่สามารถปรับเปลี่ยนค่าได้ โดยรับคำสั่งจากบอร์ด Arduino อีกบอร์ดหนึ่งที่ทำหน้าที่เป็น SPI Master เพื่อกำหนดความกว้างช่วงที่เป็น HIGH ได้ในหน่วยเป็นไมโครวินาที (μsec) นอกจากนั้นยังสามารถอ่านค่า Duty Cycle ของแต่ละช่องได้เช่นกัน
รูปแบบของคำสั่งและข้อมูลมีดังนี้ กรณีแรกเป็นการกำหนดค่า Duty Cycle (12 บิต) ให้ช่องสัญญาณที่เลือก จะต้องส่งข้อมูลไบต์จำนวน 2 ไบต์ และไม่สนใจข้อมูลไบต์ที่ได้รับกลับมา
Write Operation (Bytes sent by SPI Master):
   First Byte:
     Bit   7 = R/W bit = '1'
     Bit 6-5 = Channel Number
     Bit   4 = don't care
     Bit 3-0 = DutyCycle (Bit 11..8)
   Second Byte: 
     Bit 7-0 = DutyCycle (Bit 7..0)

กรณีที่สองเป็นการอ่านค่า Duty Cycle ของช่องสัญญาณที่เลือก จะต้องส่งข้อมูลไบต์ทั้งหมด 3 ไบต์ ไม่สนใจไบต์แรกที่ได้รับมา และค่า Duty Cycle ที่ได้จะอยู่ในข้อมูลไบต์ 2 ไบต์สุดท้าย
Read Operation (Bytes sent by SPI Master):
   First Byte:
     Bit   7 = R/W bit = '0'
     Bit 6-5 = Channel Number
     Bit 4-0 = don't care
   Second Byte:
     Bit 7-0 = don't care
   Third Byte:
     Bit 7-0 = don't care
โค้ดตัวอย่างนี้สาธิตการเปลี่ยนค่า Duty Cycle ของสัญญาณ R/C Servo 4 ช่อง เมื่อมีการกดปุ่มแต่ละครั้ง และสลับไปมาระหว่างสองกรณี โดยที่กรณีแรก ให้ทุกช่องมีค่า 1500 μsec และในอีกกรณีให้สัญญาณ 4 ช่อง มีค่า 1000,1250,1750,2000 μsec ตามลำดับ

Sourcecode: spi-servo-master.ino
////////////////////////////////////////////////////////////////////////
// Author: RSP @ Embedded Systems Lab (ESL), KMUTNB
// Date: 22-Apr-2014
// Target Board: Arduino Uno (ATmega328P, 5V, 16MHz)
// Arduino IDE: version 1.0.5
// Description: This Arduino sketch shows how to use the Arduino Uno 
//   as SPI master to communicate with another Arduino Uno which
//   operates as SPI slave. The SPI slave device also generates four 
//   R/C servo signals with adjustable duty cycles.
//   The SPI master can set/get the duty cycle of the selected R/C signal
//   via the SPI bus.
////////////////////////////////////////////////////////////////////////

// SPI Master

#include <SPI.h> // use the Arduino SPI library
/*
  SPI Pins for Arduino Uno:
    D13 = SCK, D12 = MISO, D11 = MOSI, D10 = SS
  SPI Pins for Arduino Mega:
    D50 = MISO, D51 = MOSI, D52 = SCK, D53 = SCK
 */

#define SS             (10)
#define BUTTON_PIN     (2)
#define LED_PIN        (3)
#define NUM_CHANNELS   (4)

const uint16_t DEFAULT_PULSE_WIDTHS[ NUM_CHANNELS ] = { 1500,1500,1500,1500, };
const uint16_t TEST_PULSE_WIDTHS[ NUM_CHANNELS ]    = { 1000,1250,1750,2000, };
const uint16_t *pulse_widths;

void set_pulse_widths();

void setup() {
  Serial.begin( 115200 ); // use Serial, baudrate = 115200
  pinMode( BUTTON_PIN, INPUT );
  digitalWrite( BUTTON_PIN, HIGH ); // enable internal pull-up
  pinMode( LED_PIN, OUTPUT );
  digitalWrite( LED_PIN, LOW );
  
  SPI.begin();
  SPI.setDataMode( SPI_MODE0 );
  SPI.setBitOrder( MSBFIRST );
  // set clock divider for SCK -> use 16MHz/8 = 2MHz
  SPI.setClockDivider( SPI_CLOCK_DIV8 ); // SPI_CLOCK_DIVx, where x=4,8,16,32,64,128
  digitalWrite( SS, HIGH );
  pulse_widths = DEFAULT_PULSE_WIDTHS;
  set_pulse_widths();
  
  Serial.println( "SPI Master..." );
  delay( 100 );
}

#define SPI_WAIT_DELAY  (16)  // in microseconds

void set_pulse_widths() {
  for ( uint8_t i=0; i < NUM_CHANNELS; i++ ) {
    uint16_t pulse_width = pulse_widths[i];
    digitalWrite( SS, LOW );
    // write operation the i-th PWM channel
    SPI.transfer( 0x80 | (i << 5) | ((pulse_width >> 8) & 0x0f) );
    delayMicroseconds( SPI_WAIT_DELAY );
    SPI.transfer( pulse_width & 0xff ); // write high byte
    delayMicroseconds( SPI_WAIT_DELAY );
    digitalWrite( SS, HIGH );           // write low byte
    delayMicroseconds( 30 ); // give the SPI slave some time to process
  }
}

void get_pulse_widths( uint16_t *values ) {
  uint8_t lo_byte, hi_byte;
  for ( uint8_t i=0; i < NUM_CHANNELS; i++ ) {
    digitalWrite( SS, LOW );
    SPI.transfer( (i << 5) ); // read operation for i-th PWM channel
    delayMicroseconds( SPI_WAIT_DELAY );
    hi_byte = SPI.transfer( 0x00 );
    delayMicroseconds( SPI_WAIT_DELAY );
    lo_byte = SPI.transfer( 0x00 );
    delayMicroseconds( SPI_WAIT_DELAY );
    digitalWrite( SS, HIGH );
    values[i] = (hi_byte & 0x0f);
    values[i] = (values[i] << 8) | lo_byte;
    delayMicroseconds( 30 ); // give the SPI slave some time to process
  }
}

boolean state = false;
char sbuf[32];  // used for sprintf()

void loop() {
  if ( digitalRead( BUTTON_PIN ) == LOW ) { // check whether the button is pressed
    while ( digitalRead( BUTTON_PIN ) == LOW ) ; // wait until the button is released
    uint16_t values[ NUM_CHANNELS ];
    pulse_widths = (state) ? TEST_PULSE_WIDTHS : DEFAULT_PULSE_WIDTHS;
    set_pulse_widths(); // update the pulse widths of all servos
    get_pulse_widths( values );
    Serial.print( "Pulse widths (usec): " );
    for ( uint8_t i=0; i < NUM_CHANNELS; i++ ) {
      sprintf( sbuf+i*5, "%5u", values[i] );
    }
    Serial.println( sbuf );
    state = !state; // toggle LED state
  }
  digitalWrite( LED_PIN, state );
  delay(10);
}
///////////////////////////////////////////////////////////////////////

Sourcecode: spi-servo-slave.ino
////////////////////////////////////////////////////////////////////////
// Author: RSP @ Embedded Systems Lab (ESL), KMUTNB
// Date: 22-Apr-2014
// Target Board: Arduino Uno (ATmega328P, 5V, 16MHz)
// Arduino IDE: version 1.0.5
// Description: This Arduino sketch is an example of how to
//   make an Arduino Uno board an SPI slave which generates
//   four R/C servo signals and allows the SPI master to change
//   the duty cycle of each R/C signal.
////////////////////////////////////////////////////////////////////////

// SPI Slave

#include <SPI.h> // use the Arduino SPI library
#include <Servo.h>

#define SS      (10)
#define LED_PIN (3)

const uint8_t PWM_PINS[4] = {5,6,7,8};
Servo servos[4];
uint16_t servo_pulse_widths[4];

volatile uint8_t  byte_count  = 0;
volatile uint8_t  channel     = 0;
volatile uint16_t pulse_width = 0;
volatile boolean  update_flag = false;

ISR(SPI_STC_vect) { // SPI reception complete
  static uint8_t rw_bit = 0;
  static uint8_t data;
  digitalWrite( LED_PIN, HIGH );
  data = SPDR;
  if ( byte_count == 0 ) {
     rw_bit  = (data >> 7) & 1;
     channel = (data >> 5) & 3;
     if ( rw_bit == 1 ) { // write operation
       pulse_width = (uint16_t)(data & 0x0f);
       SPDR = 0x00; // don't care
     } else { // read operation
       pulse_width = servo_pulse_widths[ channel ];
       SPDR = (pulse_width >> 8) & 0x0f; 
     }
     byte_count++;
  } 
  else if ( byte_count == 1 ) {
     if ( rw_bit == 1 ) { // write operation
       pulse_width = (pulse_width << 8) | (((uint16_t)data) & 0x00ff);
       SPDR = 0x00; // don't care
       update_flag = true;
       byte_count = 0;
     } else { // read operation
       SPDR = pulse_width & 0xff;
       byte_count++;
     }
  }
  else {
     byte_count = 0;
     SPDR = 0x00;
  }
  digitalWrite( LED_PIN, LOW );
}

void setup() {
  pinMode( LED_PIN, OUTPUT );
  digitalWrite( LED_PIN, LOW );
  Serial.begin( 115200 );     // use Serial port and baudrate=115200
  pinMode( MISO, OUTPUT );    // configure the MISO pin as output
  digitalWrite( SS, HIGH );   // enable pull-up on the SS pin
  for ( uint8_t i=0; i < 4; i++) {
     servo_pulse_widths[i] = 1500;
     servos[i].attach( PWM_PINS[i] );
     servos[i].writeMicroseconds( servo_pulse_widths[i] );
  }
  SPCR |= _BV(SPE);           // set SPE bit in SPCR to enable SPI
  //SPI.attachInterrupt();    // enable SPI interrupt
  SPCR |= _BV(SPIE);
  // In Slave mode the MSTR bit in SPCR is clear.
  Serial.println( "SPI Slave..." );
}


void loop() {
  static uint8_t _channel;
  static uint16_t _pulse_width;
  if ( update_flag ) { // update 
    _pulse_width = pulse_width;
    _channel = channel;
    byte_count = 0;
    update_flag = false;
    servo_pulse_widths[ _channel ] = _pulse_width;
    servos[ _channel ].writeMicroseconds( servo_pulse_widths[ _channel ] ); 
  }
  if ( digitalRead( SS ) ) { 
    byte_count = 0;
    update_flag = false;
  }
}

////////////////////////////////////////////////////////////////////////


รูปแสดงคลื่นสัญญาณช่อง 0 และ 1 (default)
1500μsec และ 1500μsec ตามลำดับ

รูปแสดงคลื่นสัญญาณช่อง 0 และ 1
1000μsec และ 1250μsec ตามลำดับ

รูปแสดงคลื่นสัญญาณช่อง 2 และ 3 (default)
1500μsec และ 1500μsec ตามลำดับ

รูปแสดงคลื่นสัญญาณช่อง 2 และ 3
1750μsec และ 2000μsec ตามลำดับ


รูปแสดงคลื่นสัญญาณ SCK (1) และ LED (2)
ในกรณีของการกำหนดค่า (ข้อมูล 2 ไบต์)

รูปแสดงคลื่นสัญญาณ SCK (1) และ LED (2)
ในกรณีของการอ่านค่า (ข้อมูล 3 ไบต์)
** ในตัวอย่างนี้ใช้สัญญาณ LED เพื่อดูพฤติกรรมการทำงานของ SPI Slave เมื่อสัญญาณ LED เป็น HIGH แสดงถึงช่วงการทำงาน (active) ของ ISR(SPI_STC_vect) และจะถูกเรียกให้ทำงานทุกครั้งที่ได้รับข้อมูลหนึ่งไบต์จาก SPI Master


รูปแสดงข้อความที่ปรากฏใน Serial Monitor ของ Arduino IDE
หลังจากที่ได้กดปุ่มหลายครั้งเพื่อลองเปลี่ยนและอ่านค่า Duty Cycle ของสัญญาณ

การเขียนโปรแกรมเบื้องต้นกับ Arduino (การส่งข้อมูลแบบ I2C)

การสื่อสารข้อมูลระหว่างบอร์ด Arduino ด้วยบัส I2C

บทความนี้กล่าวถึง การทดลองใช้บอร์ด Arduino (ATmega328P, 5V/16MHz) จำนวน 2 ชุด เพื่อสื่อสารข้อมูลกัน ด้วยระบบบัส I2C โดยโปรแกรมให้ Arduino ทำหน้าที่เป็นอุปกรณ์ที่เรียกว่า I2C Master และ I2C Slave อย่างละชุด และในการเขียนโปรแกรม ได้ใช้คำสั่งจากไลบรารี่ที่ชื่อว่า Wire ของ Arduino

การสื่อสารข้อมูลด้วยระบบบัส I2C

โดยทั่วไป มักพบเห็นได้ว่า มีการนำบอร์ดไมโครคอนโทรลเลอร์ อย่างเช่น Arduino มาเชื่อมต่อกับอุปกรณ์อื่น เช่น โมดูลเซนเซอร์หลากหลายประเภท โดยใช้วิธีการสื่อสารข้อมูลตามรูปแบบของบัส I2C โดยฝ่ายหนึ่งจะทำหน้าที่ตามบทบาทที่เรียกว่า I2C Master ซึ่งโดยส่วนใหญ่ ก็จะเป็นไมโครคอนโทรลเลอร์ และอีกฝ่ายหนึ่งจะเป็น I2C Slave ซึ่งอาจมีได้หลายชุด
การสื่อสารผ่านบัส I2C เป็นการสื่อสารแบบ Synchronous & Serial (หมายถึง การส่งข้อมูลทีละบิต และใช้สัญญาณ Clock ในการกำหนดจังหวะการส่งข้อมูล) ข้อดีของการสื่อสารข้อมูลแบบบัส I2C คือ ใช้สายสัญญาณเพียง 2 เส้น คือ SCL (สายสัญญาณ Serial Clock) และ SDA (สายสัญญาณข้อมูล Serial Data) และเป็นสัญญาณแบบ 2 ทิศทาง (Bidirectional) มีวงจรภายในสำหรับ I/O แบบ Open-Drain/Open-Collector (เวลาใช้งานต้องมีตัวต้านทานแบบ Pull-up Resistors ต่ออยู่ด้วย)
บัส I2C สามารถพ่วงอุปกรณ์ได้หลายอุปกรณ์ แต่ละอุปกรณ์จะมีหมายเลขที่อยู่ (Device Address) ที่ต้องไม่ซ้ำกัน โดยทั่วไปจะใช้หมายเลขที่อยู่ขนาด 7 บิต (7-bit Device Address) ซึ่งระบุได้ถึง 128 อุปกรณ์ หรือถ้ามีมากกว่านั้น จะเป็น 10 บิต (10-bit Device Address)
อุปกรณ์ที่ทำหน้าที่เป็น I2C Master จะเป็นฝ่ายเริ่มการสื่อสารข้อมูล และสร้างสัญญาณ SCL มาควบคุมจังหวะ มีอัตราการส่งข้อมูลอยู่ที่ 100kHz และ 400kHz (บางกรณี ได้สูงกว่า 1MHz) เมื่อไม่มีการสื่อสารใดๆ สถานะลอจิกของ SCL และ SDA จะเป็น 1 หรือ HIGH เมื่อบัส I2C เริ่มต้นสื่อสาร อุปกรณ์ I2C Master จะส่งบิต Start (หรือเรียกว่า Start Condition) ตามด้วยการส่งไบต์ควบคุม (Control Byte) ออกไปก่อน ซึ่งจะเป็นการระบุหมายเลขของอุปกรณ์ Slave ที่อุปกรณ์ Master ต้องการจะสื่อสารด้วย และในไบต์ดังกล่าวจะมีบิตที่เรียกว่า Read/Write (R/W) Bit สำหรับระบุว่า จะเป็นการเขียนหรืออ่านข้อมูลต่อจากนั้น ถ้าเป็นบิตเขียน (R/W Bit = 0) อุปกรณ์ Master จะส่งข้อมูลไบต์ไปยังอุปกรณ์ Slave เท่านั้น แต่ถ้าเป็นบิตอ่าน (R/W Bit = 1) ต่อไปจะเป็นการรับข้อมูลไบต์จากอุปกรณ์ Slave เพียงอย่างเดียว   นอกจากนั้นในการรับส่งข้อมูลแต่ละไบต์ ฝ่ายรับจะต้องทำการส่งบิตที่เรียกว่า ACK (Acknowledge) Bit ซึ่งจะต้องเป็นลอจิก 0 (ดึงสัญญาณ SDA ลง GND) เมื่อ SCL เป็น 1 เพื่อแจ้งให้ฝ่ายส่งทราบว่า ได้รับข้อมูลไบต์แล้วและพร้อมจะทำงานต่อไป ถ้าจบการสื่อสาร ก็จะต้องส่งบิต Stop (หรือเรียกว่า Stop Condition)
การเขียนโปรแกรมสำหรับ Arduino เพื่อใช้สื่อสารข้อมูลผ่านบัส I2C ก็ทำได้ไม่ยาก เพราะสามารถเรียกใช้คำสั่งจากไลบรารี่ของ Arduino (ใช้ซอฟต์แวร์ตามเวอร์ชัน 1.0.x) ที่ชื่อว่า Wire และสามารถใช้งานได้ทั้งกรณี I2C Master หรือ I2C Slave ความเร็วในการรับส่งข้อมูลจะอยู่ที่ 100kHz (default) และมีการใช้งานตัวต้านทาน pull-up ที่อยู่ภายในชิปไมโครคอนโทรลเลอร์ของ Arduino อีกด้วย

Arduino Sketch

โค้ดตัวอย่างต่อไปนี้ ใช้สำหรับสาธิตการทำงานของบอร์ด Arduino (ใช้ชิป ATmega328P, 5V/16MHz) ซึ่งแบ่งเป็นสองกรณีคือ ทำงานแบบ I2C Master และ I2C Slave โดยต้องกำหนด #define MASTER หรือ #define SLAVE ในโค้ดดังกล่าวก่อนทำขั้นตอน Build   ในตัวอย่างนี้ ได้ใช้ความเร็วที่ 400kHz แทนที่จะเป็น 100kHz นอกจากนั้น ยังปิดการใช้งานตัวต้านทานภายในแบบ Pull-up (Interal pull-up resistors) ของชิป และจะต้องต่อตัวต้านทานเพิ่ม เช่น เลือกใช้ค่าที่ 2.2kΩ
โดยสรุป พฤติกรรมการทำงานของ I2C Master และ I2C Slave มีดังนี้ I2C Master ส่งข้อมูลหนึ่งไบต์ ไปยัง I2C Slave เพื่อเก็บไว้ภายในหน่วยความจำ (ตัวแปร) จากนั้น I2C Master ก็จะทำการร้องขอและอ่านข้อมูลหนึ่งไบต์จาก I2C Slave ซึ่งจะเป็นข้อมูลไบต์ที่ได้รับล่าสุด แล้วเว้นระยะเวลาประมาณ 500 มิลลิวินาที ก่อนทำขั้นตอนซ้ำ โดยเปลี่ยนค่าของข้อมูลไบต์ (เพิ่มค่าทีละหนึ่ง) และในกรณีของ I2C Master จะมีการแสดงข้อความทาง Serial ด้วย
//////////////////////////////////////////////////////////////////////////
// Author: RSP @ ESL (Embedded System Lab), KMUTNB
// Date: 2014-APR-10
// Target Board: Arduino (ATmega328P, 5V, 16MHz)
// Arduino IDE: version 1.0.5
// Description:  
//   This Arduino sketch demonstates how to implement either
//   a master or a slave device for the I2C bus using Arduino boards.
//   Two Arduino boards are required, one for the master and
//   one for the slave.
//   To build the Arduino sketch for the I2C master, MASTER must be 
//   defined in the code. For the I2C slave, SLAVE must be defined.
//   This example uses 0x74 as the I2C slave address. 
//   The speed rate is at 400kHz. Internal pull-up resistors are
//   disabled. External pull-up registers (4.7k or 2.2k) are required.
//
//////////////////////////////////////////////////////////////////////////

#include <Wire.h> // use the Wire library

//-----------------------------------------------------------------

//#define SLAVE

#define I2C_ADDR  (0x74) /* 7-bit I2C address of the slave device */
#define LED_PIN   (13)   // LED pin

#if !defined(SLAVE) && !defined(MASTER)
 #define MASTER
#endif

#if defined(SLAVE) && defined(MASTER)
 #error "Please do not define both MASTER and SLAVE."
#endif

//-----------------------------------------------------------------
#ifdef SLAVE

byte data = 0;

// This function will be called when the slave device receives
// a transmission from a master.
void i2c_recv_event( int numBytes ) {
  // expect only one byte to be read
  data = Wire.read(); // read only byte
}

// This function will be called when a master requests data from this slave device.
void i2c_send_event( ) {
  digitalWrite( LED_PIN, HIGH );
  Wire.write( data ); // send only one byte
  digitalWrite( LED_PIN, LOW );
}

// Uno or 328P:  A4 for SDA and A5 for the SCL line of the I2C bus.
void setup() {
  pinMode( LED_PIN, OUTPUT );
  Serial.begin( 115200 ); // set baudrate
  Wire.begin( I2C_ADDR );  
  TWBR = 12; // use 400kHz instead of 100kHz
  
  // Note: The default Wire library enables the internal pullup resistors. 
  // Disable internal pull-up resistors on SDA and SCL.
  //pinMode( SDA, INPUT );
  //pinMode( SCL, INPUT );
  digitalWrite(SDA, 0);
  digitalWrite(SCL, 0);
  
  // register the handlers for I2C events
  Wire.onReceive( i2c_recv_event );
  Wire.onRequest( i2c_send_event ); 
}

void loop() {
  // empty
}

#endif // SLAVE

//----------------------------------------------------------------
#ifdef MASTER

byte data = 0; 
char sbuf[32];

void i2c_scan() {
  int count = 0;
  Serial.println( "Scanning I2C slave devices..." );
  for( byte addr=0x01; addr <= 0x7f; addr++ ) {
     Wire.beginTransmission( addr ); 
     if ( Wire.endTransmission() == 0 ) {
       sprintf( sbuf, "I2C device found at 0x%02X.", addr );
       Serial.println( sbuf );
       count++;
    }
  }
  if (count > 0 ) {
    sprintf( sbuf, "Found %d I2C devices.", count );
  } else {
    sprintf( sbuf, "No I2C device found." );
  }
  Serial.println( sbuf );
}

void test_write_read( byte value ) {
  sprintf( sbuf, "Master: 0x%02X sent.", data );
  Serial.println( sbuf );

  Wire.beginTransmission( I2C_ADDR );
  Wire.write( value ); // write one byte
  Wire.endTransmission();
  
  delayMicroseconds(10);

  digitalWrite( LED_PIN, HIGH );
  Wire.beginTransmission( I2C_ADDR );
  Wire.requestFrom( (uint16_t)I2C_ADDR, 1 ); // read one byte
  digitalWrite( LED_PIN, LOW );

  if ( Wire.available() == 1 ) {
    byte data = Wire.read();
    sprintf( sbuf, "Master: 0x%02X received.", data );
    Serial.println( sbuf );
  } else {
    Serial.println( "Master: error!" );
  }
  Wire.endTransmission();
}

// Uno or 328P:  A4 for SDA and A5 for the SCL line of the I2C bus.
void setup() {
  Serial.begin( 115200 );
  Wire.begin();  
  TWBR = 12; // use 400kHz instead of 100kHz
  pinMode( LED_PIN, OUTPUT );
  
  // Note: The default Wire library enables the internal pullup resistors.
  // Disable internal pull-up resistors on SDA and SCL
  //pinMode( SDA, INPUT );
  //pinMode( SCL, INPUT );
  digitalWrite(SDA, 0);
  digitalWrite(SCL, 0);
  delay(100);
  //i2c_scan(); // perform I2C device scanning
  delay(2000);
}

void loop() {
  test_write_read( data );
  data++;
  delay(500);
}

#endif // MASTER

//////////////////////////////////////////////////////////////////////////

Wiring Diagram
   Master        Slave  
   A4 (SDA) ---  A4 (SDA), 2.2k or 4.7k pull-up to VCC
   A5 (SCL) ---  A5 (SCL), 2.2k or 4.7k pull-up to VCC
   GND      ---  GND 



รูปแสดงการต่อวงจรโดยใช้บอร์ด Arduino จำนวน 2 ชุด เชื่อมต่อกับแบบบัส I2C
มีการต่อตัวต้านทาน Pull-up ที่สัญญาณ SCL และ SDA ไปยัง VCC


รูปแสดงข้อความที่ปรากฏใน Serial Monitor ที่ได้รับจากบอร์ด Arduino ที่เป็น I2C Master


รูปแสดงคลื่นสัญญาณ SCL (1) และ SDA (2) เมื่อวัดด้วยออสซิลโลสโคป
(400kHz, pull-up 4.7kΩ to 5V)


รูปแสดงคลื่นสัญญาณ SCL (1) และ SDA (2) เมื่อวัดด้วยออสซิลโลสโคป
(400kHz, pull-up 2.2kΩ to 3.3V)


รูปแสดงคลื่นสัญญาณ SCL (1) และ LED (2) เมื่อวัดด้วยออสซิลโลสโคป
ช่วง HIGH ของ LED (Master) หมายถึง ช่วงที่ I2C Master ร้องขอและอ่านข้อมูลหนึ่งไบต์จาก I2C Slave


รูปแสดงคลื่นสัญญาณ SCL (1) และ LED (2) เมื่อวัดด้วยออสซิลโลสโคป
ช่วง HIGH ของ LED (Slave) หมายถึง ช่วงที่ I2C Slave ส่งข้อมูลไบต์ไปยัง I2C Master
ซึ่งตรงกับช่วงเวลาการทำงานของฟังก์ชัน i2c_send_event( )


แหล่งข้อมูลอ้างอิงและศึกษาเพิ่มเติม (Reference)

การเขียนโปรแกรมเบื้องต้นกับ Arduino (การส่งค่าสัญญาณ PWM)

PWM(Pulse Width Modulation) คือ อะไร

PWM คือเทคนิดการส่งสัญญาณแบบสวิต หรือ ส่งค่าดิจิตอล 0-1 โดยให้สัญญาณความถี่คงที่ การควบคุมระยะเวลาสัญญาณสูงและสัญญาณต่ำ ที่ต่างกัน ก็จะทำให้ค่าแรงดันเฉลี่ยของสัญญาณสวิต ต่างกันด้วย อย่างเช่น ดูภาพประกอบ
pwm
  • ถ้าอยากให้ Vout ออกมา 0 โวลต์ เราก็ให้ระยะเวลาสัญญาณ LOW เต็มคาบเวลา
  • ถ้าอยากให้ Vout ออกมา 25% เราก็ให้สัญญาณ HIGH 25 % และ สัญญาณ LOW 75%
  • และ ถ้าอยากให้ Vout ออกมา 50% เราให้ระยะเวลาของสัญญาณ HIGH และ LOW เท่ากัน ก็จะได้ค่าเป็น 2.5 โวลต์
  • เช่นเดียวกัน ถ้าเราอยากให้ Vout ออกมา 75% เราให้ระยะสัญญาณ HIGH เป็น 75%  และ สัญญาณ LOW 25%
  • ซึ่งสุดท้าย ถ้าเราอยากให้ Vout ออกมา 5 โวลต์ เราเปิดสัญญาณ HIGH เต็มคาบเวลา
สำหรับโมดุล PWM ของ Arduino มีความละเอียด 8 bit หรือ ปรับได้ 255 ระดับ ดังนั้นค่าสัญญาณ 0 โวลต์ถึง 5 โวลต์ จะถูกแสดงเป็นสัญญาณแบบดิจิตอล จะได้ 0 ถึง 255 ซึ่งเราสามารถเทียบสัดส่วนคำนวนจากเลขจริง เป็น เลขทางดิจิตอลได้

<Lab 1> การหรี่ไฟ (Fade LED)

การหรี่ไฟ LED เราเทคนิดของ PWM เพื่อควบคุมการจ่ายไฟ โดยเราจะใช้คำสั่ง analogWrite(pin ,[0 – 255] ) โดยบอร์ด FunBasic จะต่อ LED ใช้งานในขา 6,9,10,11 นะครับ
// สำหรับบอร์ด Arduino ช่อง PWM บนบอร์ดสามารถใช้ช่อง 3,5,6,9,10,11
// บน Funbasic I/O Shiled ใช้ได้ช่อง 6,9,10,11
int ledPin = 6;
void setup() {
  // put your setup code here, to run once:
  pinMode(ledPin, OUTPUT); // Set ledPin to an output
}
void loop() {
  // put your main code here, to run repeatedly:
  // ตั้งค่าให้ Value = 0 ไฟจะออกประมาณ 0 โวลต์ ทำให้ LED ดับ
  analogWrite(ledPin, 0); // Sets voltage to 0V (0/255 * 5V). LED is off.
  delay(1000); // Wait a second
  // ตั้งค่าให้ Value = 64 ไฟจะออกประมาณ 1.25 โวลต์ ทำให้ LED สว่างเล็กน้อย
  analogWrite(ledPin, 64); // Sets voltage to ~1.25V (64/255 * 5V). Pretty dim.
  delay(1000); // Wait a second
  // ตั้งค่าให้ Value = 128 ไฟจะออกประมาณ 2.5 โวลต์ ทำให้ LED สว่างปานกลาง
  analogWrite(ledPin, 128); // Set voltage to ~2.5V. Half-bright.
  delay(1000); // Wait a second
  // ตั้งค่าให้ Value = 192 ไฟจะออกประมาณ 3.75 โวลต์ ทำให้ LED สว่างเกือบจะสุด
  analogWrite(ledPin, 192); // Set voltage to ~3.75V. Getting brighter!
  delay(1000); // Wait a second
  // ตั้งค่าให้ Value = 255 ไฟจะออกประมาณ 5 โวลต์ ทำให้ LED สว่างสุด
  analogWrite(ledPin, 255); // Set voltage to 5V. Fully on (turn down for what!?).
  delay(1000); // Wait a second
}
จากโค๊ด เราสามารถทดลองเล่นเปลี่ยน LED ไป หรี่ที่ดวงอื่นได้ โดยการเปลี่ยนค่าในตัวแปร ledPin นะครับ ทดลองเล่นดูครับ

<Lab 2> การหรี่ไฟแบบนุ่มนวล (Smooth Fade LED)

จะเห็นได้ว่า ในการทดลองครั้งแรก LED เราจะค่อยๆ หรี่ลงมาเป็น step ที่จริงเราสามารถ ประยุกต์ใช้ for-loop มาช่วยทำได้ มาลองกันดู
// สำหรับบอร์ด Arduino ช่อง PWM บนบอร์ดสามารถใช้ช่อง 3,5,6,9,10,11
// บน Funbasic I/O Shield ใช้ได้ช่อง 6,9,10,11
int ledPin = 6;    // LED connected to digital pin 6
void setup() {
  // nothing happens in setup
}
void loop() {
  // fade in from min to max in increments of 5 points:
  // เราใช้ for-loop เพื่อ เพิ่มค่าที่ล่ะ 5
  for (int fadeValue = 0 ; fadeValue <= 255; fadeValue += 5) {
    // sets the value (range from 0 to 255):
    analogWrite(ledPin, fadeValue);
    // wait for 30 milliseconds to see the dimming effect
    delay(30);
  }
  // fade out from max to min in increments of 5 points:
  // เราใช้ for-loop เพื่อ ลดค่าที่ล่ะ 5 
  for (int fadeValue = 255 ; fadeValue >= 0; fadeValue -= 5) {
    // sets the value (range from 0 to 255):
    analogWrite(ledPin, fadeValue);
    // wait for 30 milliseconds to see the dimming effect
    delay(30);
  }
}
สิ่งที่สามารถนำ PWM หรือ การใช้งาน analogWrite() ไปประยุกต์ได้ เช่น ความเร็วมอเตอร์ ความสว่างของไฟ ระดับความร้อน เป็นต้น ถ้ามีส่วนใดบทความ หรือ มีความคิดเห็นประการใด ติชมได้ครับ