1. Normal Mode: 100Kbps
2. Fast Mode: 400Kbps
3. Fast Mode Plus: 1Mbps
4. High Speed Mode: 3.4 Mbps
ตัวอย่างการต่อใช้งาน I2C
จากภาพแสดงให้เห็นว่าในการใช้งาน I2C สามารถต่อใช้งานกับอุปกรณ์ได้มากกว่าหนึ่งตัวบนสายสัญญาณ 1 ชุด (2 เส้น) โดยเลือกติดต่อกับอุปกรณ์ใดได้ด้วยการกำหนดแอดเดรสทางฮาร์ดแวร์ให้กับ อุปกรณ์ตัวนั้นและต้องมีการ Pull-Up ให้กับสายสัญญาณทั้ง 2 เส้น
รูปแบบสถานะในการรับ-ส่งข้อมูล
Start เป็นสถานะที่บอกเริ่มต้นการรับ-ส่งข้อมูล โดยการเปลี่ยนสัญญาณของ SDA จาก High ไปเป็น Low โดยที่ SCL ยังคงเป็น High อยู่
Control Byte ประกอบไปด้วย 3 ส่วน คือ
- ID ของอุปกรณ์จำนวน 4 บิต ซึ่งถูกกำหนดมาโดยผู้ผลิต IC หรืออุปกรณ์ I2C
- Device Address ขนาด 3 บิต สามารถกำหนดได้เองจากการจ่าย Logic หรือต่อขาให้กับ IC
- Mode ขนาด 1 บิต ใช้กำหนดว่าเป็นการ Read หรือ Write Data กับอุปกรณ์ IC
ACK หรือ Acknowledge เป็นบิตที่ใช้บอกว่า IC มีการตอบสนองต่อคำสั่งที่ได้รับมาแล้ว
DATA คือ ข้อมูลที่ต้องการเขียนหรืออ่านออกมาจาก IC ขึ้นกับ Mode ที่เราได้ตั้งค่าเอาไว้
STOP เป็นสถานะที่บอกให้อุปกรณ์รู้ว่าสิ้นสุดการรับส่งข้อมูลแล้ว โดย SDA จะเปลี่ยนจาก Low เป็น High ในขณะที่ SCL ยังเป็น High อยู่
แนะนำโมดูล PCF8591 (EFDV221 PCF8591 Module)
PCF8591 เป็นชิพที่ใช้สำหรับแปลงสัญญาณ Analog to Digital และ Digital to Analog ทั้ง 2 ทางขนาด 8-bit สั่งงานผ่าน I2C มีรูปแบบการใช้งานที่ไม่ซับซ้อนมากนัก เนื่องจากบนบอร์ด Raspberry Pi ไม่มีขาสัญญาณสำหรับอ่านค่า Analog to Digital โมดูลตัวนี้จึงเหมาะที่จะนำมาทดแทนความสามารถที่ขาดไปนี้ได้ นอกจากนี้บนตัวโมดูลยังมีอุปกรณ์อื่นๆ เช่น Thermister LDR และ Potentiometer ซึ่งให้เอาท์พุตเป็นค่า Analog ทำให้สามารถทดลองอ่านค่าเหล่านี้ได้โดยไม่ต้องไปหาอุปกรณ์เพิ่มเติม
วงจรของโมดูล PCF8591
วิธีตั้งค่าจัมพ์เปอร์กับอุปกรณ์บนบอร์ด
อุปกรณ์บนบอร์ดสามารถเลือกเชื่อมต่อได้ด้วยการต่อจัมพ์เปอร์ ดังนี้
- P4 ใช้ต่อ Thermistor เข้ากับช่อง AIN1
- P5 ใช้ต่อ LDR เข้ากับช่อง AIN0
- P6 ใช้ต่อ Potentiometer เข้ากับช่อง AIN3
ดังนั้น หากต้องการอ่านค่าจากภายนอกที่ช่อง AIN0 AIN1 และ AIN2 ให้ถอด P4 P5 และ P6 ออก
ค่า Device Address ของโมดูล PCF8591
จากแผนผังวงจรจะเห็นว่าขา A0 A1 และ A2 ของ PCF8591 ต่อลง GND เอาไว้ทั้ง 3 ขา ทำให้ค่าในบิต Device Address ของชิพ คือ 0 ทั้ง 3 บิต และ Datasheet ของ PCF8591 ได้กำหนด ID มาจากผู้ผลิตคือ 0b1001
ดังนั้น Slave Address ของโมดูลถูกกำหนดตายตัวไว้ที่ 0b100100 (0x48 ที่ 7 บิต)
- หากกำหนดสถานะ Write Data ไปยังโมดูล บิตที่ 0 (LSB) จะถูกกำหนดเป็น 0 ทำให้ Slave address ทั้ง 8 บิต มีค่าเป็น 0b1001000 (0x90)
- หากกำหนดสถานะ Read Data จากโมดูล บิตที่ 0 (LSB) จะถูกกำหนดเป็น 1 ทำให้ Slave address ทั้ง 8 บิต มีค่าเป็น 0b1001001 (0x91)
Bus Protocol สำหรับอ่านและเขียนข้อมูลของ PCF 8591
- Bus Protocol สำหรับ Write Data
o S = เริ่มติดต่อ I2C
o Address = Slave Address ที่กล่าวถึงในหัวข้อก่อนหน้า สำหรับโมดูลนี้คือ 0x48(7Bits) จากรูปจะเห็นว่าบิตที่ 0 (LSB) ถูกกำหนดให้เป็นลอจิก 0 คือ Write Data เมื่อรวมกับ 0x48 แล้วจึงมีค่า = 0x90 ตามที่อธิบายไปแล้วข้างต้น
o A = Acknowledge ที่ PCF8591 ตอบรับ Slave Address ที่เราส่งไป
o Control Byte = ไบต์ที่ใช้ควบคุมการทำงานของ PCF8591 ซึ่งมีรูปแบบการกำหนดดังนี้
บิตที่ 0 และ 1 สำหรับกำหนดช่องของ A/D ที่ต้องการ
บิตที่ 2 กำหนด AUTO-INCREMENT FLAG
บิตที่ 3 กำหนดเป็น 0 เสมอ
บิตที่ 4 และ 5 สำหรับเลือกรูปแบบ Analog Input
Bit ที่ 6 สำหรับเปิดใช้งาน Analog Output
Bit ที่ 7 กำหนดเป็น 0 เสมอ
o Data Byte คือ ข้อมูลที่เราต้องการส่งไปให้โมดูล
o P/S หมายถึง Stop หรือ Start
- Bus Protocol สำหรับ Read mode
o S = เริ่มติดต่อ I2C
o Address = Slave Address ที่กล่าวถึงในหัวข้อก่อนหน้า สำหรับโมดูลนี้คือ 0x48(7Bits) จากรูปจะเห็นว่าบิตที่ 0 (LSB) ถูกกำหนดให้เป็นลอจิก 1 คือ Read Data เมื่อรวมกับ 0x48 แล้วจึงมีค่า = 0x91 ตามที่อธิบายไปแล้วข้างต้น
o A = Acknowledge ที่ PCF8591 ตอบรับ Slave Address ที่เราส่งไป
o Data Byte = ข้อมูลที่โมดูลส่งกลับออกมา
o P หมายถึง Stop
การต่อ Module PCF8591 เข้ากับ Board Raspberry Pi
ทดสอบ I2C Driver
ในขั้นตอนนี้เป็นการทดสอบว่าโมดูลหรืออุปกรณ์ที่สื่อสารผ่าน I2C ที่นำมาเชื่อมต่อกับ Raspberry Pi สามารถติดต่อสื่อสารกันได้จริงโดยสามารถลิสต์ Slave Address ของโมดูลที่ต่ออยู่บนบัส I2C มาแสดงได้
- เปิดโปรแกรม LXTerminal
- โหลด Linux Kernel I2C Module พิมพ์คำสั่ง gpio load i2c
ในขั้นตอนนี้เป็นการทดสอบว่าโมดูลหรืออุปกรณ์ที่สื่อสารผ่าน I2C ที่นำมาเชื่อมต่อกับ Raspberry Pi สามารถติดต่อสื่อสารกันได้จริงโดยสามารถลิสต์ Slave Address ของโมดูลที่ต่ออยู่บนบัส I2C มาแสดงได้
- เปิดโปรแกรม LXTerminal
- โหลด Linux Kernel I2C Module พิมพ์คำสั่ง gpio load i2c
คำสั่ง gpio i2c load สามารถใส่พารามิเตอร์กำหนดความเร็วในการรับ-ส่งข้อมูลเพิ่มเติมได้ เช่น gpio i2c load 1000 หมายถึงกำหนดให้รับ-ส่งด้วยความเร็ว 1000 Kbps
- ลิสต์ Slave address ของโมดูลที่ต่ออยู่บนบัส I2C มาแสดง พิมพ์คำสั่ง gpio i2cd
- จากรูปผลการแสดง Slave address จะเห็นว่ามีโมดูลที่ต่ออยู่บนบัส I2C ที่มี Address = 0x48 นั่นก็คือ Address ของ PCF8591 ตามที่ได้อธิบายไว้ในหัวข้อ ค่า Device Address ของโมดูล PCF8591
*** ก่อนใช้งาน I2C ทุกครั้งต้องแน่ใจว่ามีการโหลด Linux Kernel I2C Module แล้วจึงจะใช้งานได้
ทดลองเขียนโปรแกรมติดต่อกับ Module PCF8591 ผ่าน I2C
- เปิดโปรแกรม LXTerminal แล้วเปิดโปรแกรม Qt Creator พิมพ์คำสั่ง sudo qtcreator
- สร้าง Project ใหม่ชื่อ PCF8951 และเปิดไฟล์ PCF8951.pro
- เพิ่มโค้ดเรียกใช้ไลบรารี่ WiringPi ลงไปดังนี้
LIBS += -L/usr/local/lib -lwiringPi -lwiringPiDev
INCLUDEPATH += /usr/local/include
- ออกแบบหน้าตาโปรแกรมให้มี Widget ดังนี้
o Pushbutton 2 อัน โดยแสดงข้อความบน Button ดังนี้
“Start”
“Stop”
คลิกขวาที่ PushButton ทีละอัน แล้วสร้าง Slot เลือก Signal = clicked ให้ครบทั้ง 2 อัน
o LCD Number
o Label
ลักษณะหน้าตาโปรแกรมที่ให้ออกแบบ
- ไปที่ไฟล์ mainwindows.cpp เพิ่มไฟล์ include และประกาศตัวแปล
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <QTimer>
int pcf_add = 0x48;
int fd;
Qtimer *timer;
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <QTimer>
int pcf_add = 0x48;
int fd;
Qtimer *timer;
- เพิ่มโค้ดลงในฟังก์ชั่น MainWindow::MainWindow(QWidget *parent):QmainWindow (parent),ui(new Ui::MainWindow) ดังนี้
บรรทัดที่ 17 เปิดใช้งาน I2C ด้วยฟังก์ชั่น wiringPiI2CSetup (int devId) โดยส่งค่า Slave Address (0x48) ให้กับฟังก์ชั่นแล้ว ตรวจสอบการเปิดใช้งาน
บรรทัดที่ 18 หากไม่สามารถเปิดใช้งาน I2C ได้ ให้แสดงข้อความ “Unable to open I2C Device” ที่ label
บรรทัดที่ 23 หากเปิดใช้งานสำเร็จให้แสดง “Open I2C Device” ใน Text Labelบรรทัดที่ 24 สืบทอดคลาส Qtimer โดยตั้งชื่อว่า timer
บรรทัดที่ 25 สร้าง Signal กับ Slot ให้ timer โดยกำหนดให้เมื่อครบเวลาตามกำหนด timeout() จะเกิด Signal ให้เข้าไปทำงานใน Slot และสร้างฟังก์ชั่น interval() มารองรับ เมื่อครบเวลาตามกำหนดจะกระโดดเข้ามาทำงานในฟังก์ชั่น read_ADC() ทุกครั้ง
- สร้างฟังก์ชั่น read_ADC() แล้วเขียนโค้ดการทำงานดังนี้
บรรทัดที่ 38 ประกาศตัวแปร int adc;
บรรทัดที่ 39 ส่ง Control Byte ออกไปสั่ง PCF8591 โดยในการทดลองนี้ต้องการอ่านค่า Potentiometer บนโมดูลซึ่งต่ออยู่ที่ช่อง AIN3 กำหนด Control Byte ให้มีค่าเท่ากับ 0x43
ซึ่งมีความหมายดังนี้
o บิตที่ 0 และ 1 กำหนดให้อ่าน ADC จาก AIN3
o บิตที่ 2 ปิด Auto Increment
o บิตที่ 3 เป็นลอจิก 0 เสมอ
o บิตที่ 4 และ 5 กำหนดโหมดการใช้งาน Analog Input แบบ Four Single-end
o บิตที่ 6 เปิดใช้ Analog Output
o บิตที่ 7 เป็นลอจิก 0 เสมอ
บรรทัดที่ 40 อ่านค่า ADC ครั้งแรก (ไม่ได้ใช้งาน)
บรรทัดที่ 41 อ่านค่า ADC เก็บในตัวแปร int adc;
บรรทัดที่ 42 นำค่า ADC ที่อ่านได้มาคำนวณกลับเป็นแรงดัน (Voltage)
บรรทัดที่ 43 นำค่าที่คำนวณเป็นแรงดันแล้วไปแสดงใน LCD Number
- ไปที่ไฟล์ mainwindows.h แล้วเพิ่มชื่อฟังก์ชั่น void read_ADC(); ไว้ภายใต้ private slots:
- กลับมาที่ไฟล์ mainwindows.cpp
- เพิ่มโค้ดลงในฟังก์ชั่น void MainWindow::on_pushButton_clicked() ดังนี้
บรรทัดที่ 48 สั่งให้ Qtimer ทำงานมี Interval ทุกๆ 10ms
- เพิ่มโค้ดลงในฟังก์ชั่น void MainWindow::on_pushButton_2_clicked() ดังนี้
บรรทัดที่ 54 ปิด Qtimer
- ก่อนทดลอง Run โปรแกรมเพื่อให้แน่ใจว่าโหลด Linux Kernel I2C Module แล้วให้สั่งโหลด Linux Kernel I2C Module อีกครั้งโดยเปิดโปรแกรม LXTerminal พิมพ์คำสั่ง gpio load i2c
- ทดลอง Run โปรแกรม
o คลิกที่ปุ่ม Start แล้วหมุน Potentiometer บนโมดูล PCF8591 ดูการเปลี่ยนแปลง
แนะนำการใช้งาน QProcess
จากการทดลองจะเห็นว่าก่อนการใช้งาน I2C ของ WiringPi เมื่อเปิดบอร์ด Raspberry Pi ขึ้นมาใหม่ทุกครั้งจำเป็นจะต้องโหลด Linux Kernel I2C Module ขึ้นมาก่อนจึงจะใช้งาน I2C ได้ จึงอยากแนะนำคลาส QProcess ใน Qt ซึ่งใช้สำหรับเปิดโปรแกรมและติดต่อสื่อสารกับโปรแกรมภายนอกได้
ตัวอย่างการใช้งาน
ในตัวอย่างการใช้งาน QProcess นี้จะขออ้างอิงตัวอย่างการใช้งานโมดูล PCF8591 ก่อนหน้าดังนี้
- เพิ่ม #include<Qprocess> และ #include<qdebug.h>
- แก้ไขโค้ดเพิ่มเติมในฟังก์ชั่น MainWindow::MainWindow(QWidget *parent):Qmain Window(parent),ui(new Ui::MainWindow) ดังนี้
บรรทัดที่ 20 สืบทอดคลาส QProcress
บรรทัดที่ 21 โหลด Linux Kernel I2C Module ด้วยคำสั่ง gpio load i2c
บรรทัดที่ 22 รอจนคำสั่งที่เรียก Process ทำงานเสร็จ
บรรทัดที่ 24 ส่งคำสั่งไปลิสต์ Slave Address ของโมดูลที่ต่ออยู่บนบัส I2C
บรรทัดที่ 25 รอจนคำสั่งที่เรียก Process ทำงานเสร็จ
บรรทัดที่ 27 อ่านค่าตอบกลับจากการสั่งงานผ่าน QProcess นำมาเก็บในตัวแปร QByteArray byteArray
บรรทัดที่ 28 แปลง byteArray ที่เป็น QByteArray มาเก็บในตัวแปร QStringList โดยแบ่งเป็นบรรทัด
บรรทัดที่ 29 วนรอบอ่านข้อความจาก strline มาเก็บในตัวแปน Qstring line
บรรทัดที่ 31 แสดงผลข้อความออกทาง qDebug
- ทดลอง Run โปรแกรม ในส่วนเอาท์พุตจะแสดงผลการโหลดและลิสต์ I2C แล้วรันโปรแกรมขึ้นมา
PCF8591 Extention Module (EFDV221 PCF8591 Module)
WiringPi ได้สร้าง Extention Module เอาไว้แล้วสำหรับ PCF8591 เพื่อเป็น ADC และ DAC ให้กับ Raspberry Pi ช่วยใช้งานได้ง่ายขึ้น มีชุดคำสั่งที่สามารถควบคุม Analog Output และ Analog Input ได้ด้วยชุดคำสั่งที่สั้นและใช้งานง่าย
ตัวอย่างการใช้งาน
- ต่อโมดูล PCF8591 เข้ากับบอร์ด Raspberry Pi
- ถอดจัมพ์เปอร์ P6 ออก
- บนโมดูล PCF8591 ต่อสายที่ AOUT เข้ากับ AIN3
- การทดลองนี้เป็นการสั่งงาน Analog Out ต่อกลับเข้ามาที่ Analog Input เพื่ออ่านค่า Analog
- เปิดโปรแกรม LXTerminal แล้วเปิดโปรแกรม Qt Creator ด้วยคำสั่ง sudo qtcreator
- สร้าง Project ใหม่ชื่อ PCF8591_Ext
- เปิดไฟล์ PCF8591_Ext.pro
- เพิ่มโค้ดเรียกใช้ไลบรารี่ WiringPi ลงไปดังนี้
LIBS += -L/usr/local/lib -lwiringPi -lwiringPiDev
INCLUDEPATH += /usr/local/include
- ออกแบบหน้าตาโปรแกรมโดยมี Widget ดังนี้
o Pushbutton โดยแสดงข้อความบน Button “Set Output” แล้วคลิกขวาที่ PushButton แล้วสร้าง Slot เลือก Signal = clicked
o LCD Number
o Line Edit
- ไปที่ไฟล์ mainwindows.cpp
- เพิ่มโค้ดเรียกใช้ไลบรารี่และประกาศตัวแปลดังนี้
#include <wiringPi.h>;
#include <pcf8591.h>;
#include <QTimer>;
#include <QProcess>;
Qtimer *timer;
int input_base = 100;
int i2caddress = 0x48;
- เพิ่มโค้ดลงในฟังก์ชั่น MainWindow::MainWindow(QWidget *parent) :QmainWindow (parent),ui(new Ui::MainWindow) ดังนี้
บรรทัดที่ 18 สืบทอดคลาส QProcress
บรรทัดที่ 19 โหลด Linux Kernel I2C Module ด้วยคำสั่ง gpio load i2c
บรรทัดที่ 20 รอจน Process คำสั่งเสร็จ
บรรทัดที่ 21 เริ่มต้นใช้งาน PCF 8591 ด้วยฟังก์ชั่น pcf8591Setup (int pinBase, int i2cAddress)
o int pinBase คือค่า pinbase ซึ่งเราสามารถกำหนดเป็นค่าอะไรก็ได้ แต่ต้องเป็นค่าที่มากกว่า 64 (ในที่นี้กำหนด เป็น 100)
o int i2cAddress คือค่า Slave address
บรรทัดที่ 22 สืบทอดคลาส QTimer
บรรทัดที่ 23 สร้าง Signal กับ Slot ให้ timer โดยกำหนดให้เมื่อครบเวลาตามกำหนด timeout() จะเกิด Signal ให้เข้าไปทำงาน ใน Slot และสร้างฟังก์ชั่น interval() มารองรับ เมื่อครบเวลาตามกำหนดจะกระโดดเข้ามาทำงานในฟังก์ชั่น read_ADC() ทุกครั้ง
บรรทัดที่ 24 สั่งให้ Qtimer ทำงาน interval ทุกๆ 10ms
- สร้างฟังก์ชั่น read_ADC() และเขียนโค้ดการทำงานดังนี้
บรรทัดที่ 33 อ่านค่า Analog ด้วยฟังก์ชั่น analogRead(int pin) โดยค่า Pin จะอ้างอิงจากค่า int pinBase จากฟังก์ชั่น pcf8591Setup ดังนี้
o pinBase = AIN0
o pinBase+1 = AIN1
o pinBase+2 = AIN2
o pinBase+3 = AIN3
จากวงจรต่อ AOUT เข้ากับ AIN3 จากโค้ดจึงอ้าง pin=Input_base+3 หรือ 100+3
บรรทัดที่ 34 แปลงค่า Analog ที่รับเข้ามาขนาด 8 บิตให้เป็นแรงดัน
บรรทัดที่ 35 แสดงค่า Analog ที่แปลงเป็นแรงดันแล้วใน LCD Number
- ไปที่ไฟล์ mainwindow.h
- ประกาศชื่อฟังก์ชั่น void read_ADC() ไว้ภายใต้ private slot:
- ไปที่ไฟล์ mainwindows.cpp
- เพิ่มโค้ดลงในฟังก์ชั่น void MainWindow::on_pushButton_clicked() ดังนี้
บรรทัดที่ 40 นำค่าจาก Line Edit ที่เป็นตัวแปรชนิด QString มาแปลงเป็นตัวแปร float เป็นค่าแรงดันมา คำนวณย้อนกลับเป็นค่าที่ อยู่ในช่วง 0 ถึง 255
บรรทัดที่ 41 สั่งให้ PCF8591 จ่ายแรงดันออกไปที่ขา AOUT ด้วยฟังก์ชั่น analogWrite(int pin, int value) โดยค่า Pin อ้างอิง จาก int pinBase ในฟังก์ชั่น pcf8591Setup เนื่องจาก Analog Output มีเพียงช่องเดียว ดังนั้น ค่า pinbase = input_base คือ 100 value คือค่า 0 ถึง 255 ที่ใช้กำหนด Voltage Output ที่ขา AOUT เป็น ค่าที่อ่านเข้ามาแล้วส่งกลับไป
- ทดลอง Run โปรแกรม
ไม่มีความคิดเห็น:
แสดงความคิดเห็น