ARM Mbed OS for Raspberry Pi Pico RP2040: Code Examples (Part 4)#
เนื้อหาในส่วนนี้สาธิตการเขียนโค้ดโดยใช้ Mbed OS และ Arduino IDE สำหรับบอร์ดไมโครคอนโทรลเลอร์ที่ใช้ชิป RP2040 เช่น Raspberry Pi Pico โดยนำเสนอเป็น ตอนที่ 4 ต่อจาก ตอนที่ 1 | ตอนที่ 2 | ตอนที่ 3
Keywords: Mbed OS, Raspberry Pi Pico, RP2040, Mbed-enabled Platforms, RTOS Programming
- การใช้ไอซี I2C PCF8574 สำหรับเอาต์พุต
- การใช้งานโมดูล I2C PCF8574 สำหรับอินพุต-เอาต์พุต
- การใช้งานโมดูล I2C Display LCD16x2
- การใช้งานโมดูล I2C MCP4725 DAC
▷ การใช้ไอซี I2C PCF8574 สำหรับเอาต์พุต#
ไอซี PCF8574 และ PCF8574A ทำหน้าที่เป็นอุปกรณ์ประเภท I2C Slave สำหรับการเพิ่มขา I/O จำนวน 8 ขา (8-bit I/O Expander) ให้กับไมโครคอนโทรลเลอร์ที่ทำหน้าที่เป็น I2C Master
ไอซีตัวนี้มีขา A2
, A1
, A0
เพื่อกำหนดค่าให้สามบิตแรกของแอดเดรส 7 บิต ของอุปกรณ์
ดังนั้นถ้านำไอซีชนิดเดียวกันนี้มาต่อเข้ากับบัส I2C
จะต้องตั้งค่าให้แอดเดรสของไอซีแตกต่างกัน และได้สูงสุด 8 แอดเดรสที่แตกต่างกัน
รูป: ตัวอย่างโมดูล PCF8574 Breakout Board และโมดูล 8x LED Bar (Active-low)
โค้ดตัวอย่างถัดไปสาธิตการใช้คำสั่งของ Pico SDK และเขียนโค้ด Arduino Sketch
เพื่อตรวจสอบดูว่า มีอุปกรณ์ I2C Slave ใดบ้างที่เชื่อมต่ออยู่กับบัส I2C หรือไม่
(ตรวจสอบและแสดงหมายเลขแอดเดรสของอุปกรณ์เหล่านั้น)
โดยเลือกใช้ขา GP6 และ GP7 สำหรับ SDA และ SCL ของบัส I2C1
ตามลำดับ
#include "pico.h"
#include "hardware/i2c.h"
#define SDA_PIN (6)
#define SCL_PIN (7)
#define PICO_I2C (i2c1) // use either i2c0 or i2c1
void i2c_scan( i2c_inst_t *i2c ) {
uint8_t val = 0x00;
char sbuf[8];
size_t num_devices_found = 0;
std::string strbuf = "Found: ";
for ( uint8_t addr=1; addr <= 0x7f; addr++ ) {
int ret = i2c_read_blocking( i2c, addr, &val, 1, false );
if (ret >= 0) {
sprintf( sbuf, "0x%02x", addr );
if ( num_devices_found > 0 ) {
strbuf += ", ";
}
strbuf += sbuf;
num_devices_found++;
}
}
if (num_devices_found==0) {
strbuf += "none";
} else {
sprintf( sbuf, " (%i)", num_devices_found );
strbuf += sbuf;
}
SerialUSB.println( strbuf.c_str() );
}
void pico_i2c_init() {
_i2c_init( PICO_I2C, 100000 );
gpio_set_function( SDA_PIN, GPIO_FUNC_I2C );
gpio_set_function( SCL_PIN, GPIO_FUNC_I2C );
gpio_pull_up( SDA_PIN );
gpio_pull_up( SCL_PIN );
}
void setup() {
SerialUSB.begin(115200);
while(!SerialUSB){}
SerialUSB.println("Pico RP2040 Demo...");
SerialUSB.flush();
pico_i2c_init( );
}
void loop() {
SerialUSB.println("I2C Scanning...");
SerialUSB.flush();
i2c_scan( PICO_I2C );
delay(2000);
}
ถ้าตรวจพบอุปกรณ์ I2C Slave ที่ต่อเข้ากับระบบบัส I2C จะแสดงค่าแอดเดรสเป็นเลขฐานสิบหกของอุปกรณ์เหล่านั้น ยกตัวอย่างเช่น
0x20
ตรงกับอุปกรณ์ PCF8574T (ต่อขา A2=A1=A0=0 ไปยัง GND)0x40
ตรงกับอุปกรณ์ HTU21D0x48
ตรงกับอุปกรณ์ LM75A
รูป: ตัวอย่างข้อความเอาต์พุตเมื่อตรวจสอบอุปกรณ์ที่เชื่อมต่อเข้ากับบัส I2C
ถัดไปเป็นตัวอย่างโค้ดสาธิตการเขียนข้อมูลขนาดหนึ่งไบต์ไปยังไอซี PCF8574T และใช้ P0..P7 ของอุปกรณ์ดังกล่าวในทิศทางเอาต์พุตทั้ง 8 ขา
ข้อมูลที่ถูกส่งไปนั้น จะถูกใช้เพื่อกำหนดสถานะบิตที่ขาเอาต์พุตซึ่งต่อกับวงจร LED จำนวน 8 ชุด และทำงานแบบ Active-Low โค้ดในตัวอย่างนี้จะทำให้ LED เพียงหนึ่งดวงเท่านั้นที่อยู่ในสถานะ ON และเลื่อนตำแหน่งไปทางซ้าย (จาก P0 ไปยัง P7) แล้ววนซ้ำไปเรื่อย ๆ
ข้อสังเกต: ถ้าใช้ขาของไอซี PCF8574 ในทิศทางเอาต์พุต ควรจะใช้แบบ Current Sink และไม่เหมาะกับ Current Source ดังนั้นถ้าจะทำให้ LED สว่าง จะต้องต่อวงจร LED แบบ Active-Low และกำหนดค่าบิตสำหรับเอาต์พุตให้เป็น 0 จึงจะทำให้ LED สว่าง
#include "Wire.h"
#define I2C_ADDR (0x20) // I2C address of PCF8574T
void setup() {
SerialUSB.begin(115200);
while(!SerialUSB){}
Wire.begin(); // initialize the Wire object
Wire.setClock( 100000 ); // set I2C bus speed
}
void pcf8574_write_byte( uint8_t addr, uint8_t data ) {
Wire.beginTransmission( addr );
Wire.write( data ); // write a data byte
Wire.endTransmission();
}
uint8_t data = 0x01;
void loop() {
// send the inverted data byte to the PCF8574 device
pcf8574_write_byte( I2C_ADDR, data^0xff );
// rotate-shift-left
data =(data << 1) | (data >> 7);
delay(125);
}
รูป: ตัวอย่างการต่อวงจโมดูล PCF8574 เพื่อกำหนดสถานะเอาต์พุตของ 8-bit LED bar
▷ การใช้งานโมดูล I2C PCF8574 สำหรับอินพุต-เอาต์พุต#
ในตัวอย่างนี้สาธิตการใช้งานโมดูล PCF8574 (8-bit I/O Expander) โดยใช้ขา P7..P4 ของไอซีสำหรับการรับอินพุตจากวงจรปุ่มกด (Active-Low Switch) จำนวน 4 ชุด และใช้ขา P3..P0 เป็นเอาต์พุตสำหรับ LED (Active-Low) จำนวน 4 ชุด ดังนั้นจึงมีอุปกรณ์จับคู่กันแบบ I/O จำนวน 4 ชุด ตามลำดับ
การทำงานของโค้ดตัวอย่างนี้ เมื่อเกิดอินเทอร์รัพท์ จะคอยตรวจสอบดูว่า มีการเปลี่ยนแปลงสถานะของอินพุต (4 บิต) หรือไม่ ถ้าไม่มีการกดปุ่มใด ขาอินพุตของปุ่มกดนั้น จะมีค่าบิตเท่ากับ 1 แต่ถ้ามีกดปุ่มค้างไว้ในขณะนั้นจะได้ 0 เมื่อตรวจสอบพบว่า มีการกดปุ่มแล้วปล่อยในแต่ละครั้ง จะสลับสถานะ (Bit Toggle) ของ LED ในะตำแหน่งที่เกี่ยวข้องกับปุ่มดังกล่าว
ไอซี PCF8574 สร้างสัญญาณ IRQ (Interrupt Request) ที่ขา INT แบบ Active-Low เมื่อมีการเปลี่ยนแปลงที่ขา I/O ใด ๆ ของไอซีดังกล่าว และจะเคลียร์อินเทอร์รัพท์ได้โดยอัตโนมัติ เมื่อไอซีมีการเขียนหรืออ่านค่าในครั้งถัดไป ดังนั้นจึงสามารถนำมาใช้ในการตรวจสอบการเปลี่ยนแปลงของสถานะลอจิกที่ขา P7..P4 สำหรับปุ่มกดได้
#include "Wire.h"
#define I2C_ADDR (0x20) // I2C address of PCF8574T
#define IRQ_PIN (15) // use GP15 for interrupt
volatile bool isr_flag = false;
uint8_t update( ); // forward declaration
void setup() {
SerialUSB.begin(115200);
while(!SerialUSB){}
Wire.begin(); // initialize the Wire object
Wire.setClock( 100000 ); // set I2C bus speed
// enable interrupt (falling-edge) on IRQ_PIN
attachInterrupt( IRQ_PIN, [](){ isr_flag = true; }, FALLING );
pinMode( IRQ_PIN, INPUT_PULLUP ); // enable pullup on IRQ pin
update(); // update the I/O pins of PCF8574
}
bool pcf8574_transfer( uint8_t addr, uint8_t wdata, uint8_t *rdata ) {
Wire.beginTransmission( addr );
Wire.write( wdata ); // write one byte from PCF8574
Wire.endTransmission( false );
Wire.requestFrom( addr, 1, true ); // read one byte
bool ok = false;
if ( Wire.available() == 1 ){ // one incoming byte ?
*rdata = Wire.read(); // read the received byte
ok = true;
}
Wire.endTransmission();
return ok;
}
char sbuf[32];
static uint8_t leds = 0x0F;
const char *TO_BIN[] = {
// used to convert a nibble (0..15) to a bin string
"0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111",
};
typedef enum { ST_S0=0, ST_S1 } state_t;
static state_t state = ST_S0;
static uint8_t new_inputs, inputs;
uint8_t update( ) {
uint8_t wdata, rdata;
wdata = 0xF0 | leds;
bool ok = pcf8574_transfer( I2C_ADDR, wdata, &rdata );
return (ok) ? ((rdata >> 4) & 0x0F) : 0x0F;
}
void loop() {
switch (state) { // implements a finite-state machine
case ST_S0:
if ( isr_flag ) { // the ISR flag is set
isr_flag = false; // clear the ISR flag
inputs = update(); // read the button-input pins
if ( inputs==0x0E || inputs==0x0D ||
inputs==0x0B || inputs==0x07 ) {
state = ST_S1;
new_inputs = inputs; // save current inputs
sprintf( sbuf, "[%8lu] Button pressed : %s",
millis(), TO_BIN[inputs] );
SerialUSB.println( sbuf );
delay(50);
}
}
break;
case ST_S1:
inputs = update(); // read the button-input pins
if ( inputs == 0x0F ) { // no buttons pressed
isr_flag = false;
state = ST_S0;
leds ^= ~(0xF0 | new_inputs); // toggle LED bits
update(); // update output (leds)
sprintf( sbuf, "[%8lu] Button released: %s",
millis(), TO_BIN[inputs] );
Serial.println( sbuf );
}
break;
default:
state = ST_S0;
} // end-of-switch
}
รูป: การต่อวงจรทดลองบนเบรดบอร์ดเพื่อใช้งานโมดูล PCF8574 I/O
อีกตัวอย่างหนึ่ง คือการเปลี่ยนวงจร LED แบบ 4 บิต ที่ใช้แสดงสถานะเอาต์พุตเป็นโมดูลรีเลย์ (Relay Module) จำนวน 4 ช่องสัญญาณ แต่จะต้องใช้โมดูลรีเลย์ที่รับสัญญาณควบคุบและทำงานแบบ Active-Low
โมดูลรีเลย์ที่ได้เลือกมาทดลองใช้งานนั้น มี Jumpers (S1..S4) ให้เลือกได้ว่า จะใช้สัญญาณควบคุบแบบ Active-High (เลือกตำแหน่ง H) หรือ Active-Low (เลือกตำแหน่ง L)
รูป: แสดงตำแหน่งของ Jumper (S1..S4) สำหรับเลือกโหมดการทำงานแบบ Active-Low / Active-High
และ Terminal Blocks สำหรับสัญญาณอินพุต 4 ช่อง (IN1..IN4) และการป้อนแรงดันไฟเลี้ยง DC
รูป: โมดูลรีเลย์แบบ 4 ช่องสัญญาณเอาต์พุต (3-position Terminal Blocks)
โมดูลรีเลย์นี้ใช้แรงดันไฟเลี้ยง +5V สำหรับการทำงานของขดลวดภายใน (Coil) เพื่อสร้างสนามแม่เหล็กไฟฟ้าให้เปลี่ยนตำแหน่งของหน้าสัมผัสที่เป็นโลหะ และแยกสัญญาณภาคอินพุตจากวงจรรีเลย์โดยใช้ไอซี Optocoupler สำหรับแต่ละช่องสัญญาณ (มี 4 ช่อง)
#include "Wire.h"
#define I2C_ADDR (0x20) // I2C address of PCF8574T
#define IRQ_PIN (15) // use GP15 for interrupt
volatile bool isr_flag = false;
uint8_t update( ); // forward declaration
void setup() {
SerialUSB.begin(115200);
while(!SerialUSB){}
Wire.begin(); // initialize the Wire object
Wire.setClock( 100000 ); // set I2C bus speed
// enable interrupt (falling-edge) on IRQ_PIN
attachInterrupt( IRQ_PIN, [](){ isr_flag = true; }, FALLING );
pinMode( IRQ_PIN, INPUT_PULLUP ); // enable pullup on IRQ pin
update(); // update the I/O pins of PCF8574
}
bool pcf8574_transfer( uint8_t addr, uint8_t wdata, uint8_t *rdata ) {
Wire.beginTransmission( addr );
Wire.write( wdata ); // write one byte from PCF8574
Wire.endTransmission( false );
Wire.requestFrom( addr, 1, true ); // read one byte
bool ok = false;
if ( Wire.available() == 1 ){ // one incoming byte ?
*rdata = Wire.read(); // read the received byte
ok = true;
}
Wire.endTransmission();
return ok;
}
char sbuf[32];
static uint8_t leds = 0x0F;
const char *TO_BIN[] = {
// used to convert a nibble (0..15) to a bin string
"0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111",
};
typedef enum { ST_S0=0, ST_S1 } state_t;
static state_t state = ST_S0;
static uint8_t new_inputs, inputs;
uint8_t update( ) {
uint8_t wdata, rdata;
wdata = 0xF0 | leds;
bool ok = pcf8574_transfer( I2C_ADDR, wdata, &rdata );
return (ok) ? ((rdata >> 4) & 0x0F) : 0x0F;
}
void loop() {
switch (state) { // implements a finite-state machine
case ST_S0:
if ( isr_flag ) { // the ISR flag is set
isr_flag = false; // clear the ISR flag
inputs = update(); // read the button-input pins
if ( inputs==0x0E || inputs==0x0D ||
inputs==0x0B || inputs==0x07 ) {
state = ST_S1;
new_inputs = inputs; // save current inputs
sprintf( sbuf, "[%8lu] Button pressed : %s",
millis(), TO_BIN[inputs] );
SerialUSB.println( sbuf );
delay(50);
}
}
break;
case ST_S1:
inputs = update(); // read the button-input pins
if ( inputs == 0x0F ) { // no buttons pressed
isr_flag = false;
state = ST_S0;
leds ^= ~(0xF0 | new_inputs); // toggle LED bits
update(); // update output (leds)
sprintf( sbuf, "[%8lu] Button released: %s",
millis(), TO_BIN[inputs] );
Serial.println( sbuf );
}
break;
default:
state = ST_S0;
} // end-of-switch
}
รูป: การต่อวงจรทดลองเพื่อใช้งานโมดูล PCF8574
ตรวจสอบสถานะของปุ่มกดและควบคุมการทำงานของรีเลย์ 4 ช่องสัญญาณ
▷ การใช้งานโมดูล I2C Display LCD16x2#
โมดูล LCD ที่สามารถแสดงผลแบบ 16 ตัวอักษระในหนึ่งแถว และมี 2 สองแถว (LCD16x2) มีขาทั้งหมด 16 ขา แต่ถ้านำมาใช้ร่วมกับโมดูล PCF8574 Adapter จะใช้วิธีเชื่อมต่อกับไมโครคอนโทรลเลอร์ด้วยบัส I2C ทำให้ช่วยประหยัดการใช้สายไฟในการเชื่อมต่อ และสะดวกต่อการต่อวงจรใช้งาน
รูป: โมดูล LCD 16x2 และ PCF8574 I2C Adapter
รูป: โมดูล LCD 16x2 เมื่อเสียบขาใช้งานร่วมกับโมดูล PCF8574 I2C Adapter
ตัวอย่างโค้ดถัดไปสาธิตการสร้างคลาส C++ ชื่อ LCD_I2C
เพื่อใช้งานโมดูล
LCD16x2 ร่วมกับ PCF8574 Adapter (มีแอดเดรสเท่ากับ 0x3f
)
และสาธิตการใช้งานคลาสดังกล่าว โดยนำมาใช้แสดงค่าตัวเลขที่อ่านได้จากวงจรเซ็นเซอร์วัดอุณหภูมิภายใน RP2040
// file LCD_I2C.h
#ifndef __LCD_I2C__
#define __LCD_I2C__
#include "Wire.h"
#include "hardware/adc.h"
// PCF8574 P7..P0 = D7 | D6 | D5 | D4 | BL | EN | RW | RS
class LCD_I2C {
public:
LCD_I2C( arduino::MbedI2C *_i2c, uint8_t _addr )
: addr(_addr), i2c(_i2c), bl_mask(0x08) {
// empty
}
void begin() {
write4bits( 0x03 << 4 ); // 1. set 8-bit data interface
delay(5);
write4bits( 0x03 << 4 ); // 2. set 8-bit data interface
delayMicroseconds(120);
write4bits( 0x03 << 4 ); // 3. set 8-bit data interface
write4bits( 0x02 << 4 ); // 4. change to 4-bit data interface
lcd_cmd( 0x28 ); // function set: 2-line, 5x8 dots
lcd_cmd( 0x0c ); // display ctrl: display on, cursor off
lcd_cmd( 0x80 );
}
void clear() {
lcd_cmd( 0x01 );
}
void home() {
lcd_cmd( 0x02 );
}
void setCursor( int line, int pos=0 ) {
uint8_t val = (line==0) ? (0x80 + pos) : (0xC0 + pos);
lcd_cmd( val );
}
void print( const char *s ) {
uint8_t len = strlen(s);
for ( uint8_t i=0; i < len; i++ ) {
lcd_data( s[i] );
}
}
void backlight( bool onoff ){
bl_mask = (onoff) ? 0x08 : 0x00;
}
protected:
void i2c_write_u8( uint8_t data ) {
data |= bl_mask;
i2c->beginTransmission( addr );
i2c->write( data ); // write a data byte
i2c->endTransmission();
}
void write4bits( uint8_t wdata ) {
i2c_write_u8( wdata | (1<<2) ); // EN=1
delayMicroseconds(800);
i2c_write_u8( wdata ); // EN=0
delayMicroseconds(1200);
}
void lcd_write( uint8_t wdata, uint8_t cmd_mode ) {
uint8_t high_nibble = wdata & 0xf0;
uint8_t low_nibble = (wdata << 4) & 0xf0;
write4bits( high_nibble | cmd_mode );
write4bits( low_nibble | cmd_mode );
}
void lcd_data( uint8_t wdata ) {
lcd_write( wdata, 1 ); // RS=1
}
void lcd_cmd( uint8_t wdata ) {
lcd_write( wdata, 0 ); // RS=0
}
private:
arduino::MbedI2C *i2c;
uint8_t addr;
uint8_t bl_mask; // backlight bitmask
};
#endif
ตัวอย่างโค้ดทดสอบการทำงานของคลาส LCD_I2C
#include "LCD_I2C.h"
#define I2C_ADDR (0x3f)
#define SDA_PIN p6 // p6
#define SCL_PIN p7 // p7
#define ADC_VREF (3.3f)
#define CONVERSION_FACTOR (ADC_VREF/(1<<ADC_RESOLUTION))
void pico_adc_init();
float pico_adc_read_temp();
arduino::MbedI2C i2c( SDA_PIN, SCL_PIN );
LCD_I2C lcd( &i2c, I2C_ADDR );
char sbuf[32];
void setup() {
SerialUSB.begin(115200);
while(!SerialUSB){}
SerialUSB.println("Pico RP2040 Demo...");
SerialUSB.flush();
pico_adc_init(); // initialize the ADC
i2c.begin(); // initialize the I2C
i2c.setClock( 100000 ); // set the I2C speed
lcd.begin(); // initalize the LCD
lcd.clear(); // clear the LCD
char *lines[] = {"Pico RP2040", "LCD16x2 PCF8574A"};
for ( int i=0; i < 2; i++ ) { // foreach of two lines
lcd.setCursor(i, (16-strlen(lines[i]))/2 );
lcd.print( lines[i] ); // show the text line
delay(1000);
}
delay(1000);
lcd.clear(); // clear the LCD
}
void loop() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print( "Pico RP2040 ADC" );
lcd.setCursor(1, 0);
sprintf( sbuf, "Temp: %.1f deg.C", pico_adc_read_temp() );
lcd.print( sbuf );
Serial.println( sbuf );
delay(2000);
}
void pico_adc_init() {
adc_init();
adc_set_temp_sensor_enabled(true);
adc_select_input(4); // use ADC4
delay(100);
}
float pico_adc_read_temp() {
uint32_t value = 0;
// read ADC value four times and accumulate values
for ( int i=0; i < 4; i++ ) {
value += adc_read();
}
value >>= 2; // use the average value
// convert the ADC value to voltage and
// the corresponding temperature
float volt = value * CONVERSION_FACTOR;
float temp = (27 - (volt - 0.706)/0.001721);
return temp;
}
รูปแบบการเชื่อมต่อมีดังนี้
Pico LCD16x2_I2C
GND ---- GND
3.3V ---- VCC
GP6 <--> SDA
GP7 <--> SCL
รูป: การต่อวงจรเพื่อใช้งานโมดูล LCD16x2 I2C
▷ การใช้งานโมดูล I2C MCP4725 DAC#
ไอซี MCP4725 เป็นอุปกรณ์ประเภท DAC (Digital-to-Analog Converter) มีวงจรอิเล็กทรอนิกส์อยู่ภายในสำหรับทำหน้าที่แปลงข้อมูลดิจิทัลให้เป็นสัญญาณแอนะล็อก และส่งออกที่ขา VOUT
ข้อมูลมีขนาด 12 บิต (0..4096) และจะถูกแปลงแบบเชิงเส้น ให้เป็นแรงดันไฟฟ้าในช่วง 0..VCC โดยที่แรงดันไฟเลี้ยง VCC เลือกใช้งานได้ในช่วง +2.7V .. +5.5V
เนื่องจากไอซีนี้ทำงานเป็นอุปกรณ์ประเภท I2C Slave ดังนั้นการสื่อสารระหว่างไมโครคอนโทรลเลอร์กับไอซี
MCP4725 จะต่อวงจรเพื่อสัญญาณด้วยบัส I2C
และเพื่อความสะดวกในการต่อวงจรทดลอง ได้เลือกใช้โมดูล MCP4725 Breakout Board
(หมายเลขแอดเดรสตรงกับ 0x62
)
ตัวอย่างโค้ดต่อไปนี้ สาธิตการใช้ค่าของตัวนับ dac_value
ที่มีค่าเพิ่มขึ้นจาก 0 ถึง 4095 (แล้วเริ่มใหม่ที่ 0) เป็นข้อมูลเพื่อส่งไปยังโมดูล MCP4725 และใช้เป็นค่าเอาต์พุต
#include <Wire.h>
// The A0 address pin of MCP4725 is left unconnected.
#define I2C_ADDR (0x62)
void writeDAC( uint8_t addr, uint16_t value );
void setup() {
SerialUSB.begin(115200);
while(!SerialUSB){}
analogReadResolution( 12 );
Wire.begin(); // initialze the I2C1 bus
Wire.setClock( 400000 ); // set I2C bus speed
writeDAC( I2C_ADDR, 0 );
}
const uint16_t MAX_VALUE = (1<<12);
char sbuf[32];
void loop() {
uint16_t sum, dac_value, adc_value;
for ( uint16_t i=0; i < MAX_VALUE; i+=8 ) {
dac_value = i;
writeDAC( I2C_ADDR, dac_value );
delayMicroseconds(10);
adc_value = analogRead( A0 );
sprintf( sbuf, "%lu, %lu", dac_value, adc_value );
SerialUSB.println( sbuf );
}
delay(5000);
}
// Use Fast Mode / Normal (No PowerDown)
// MSB..LSB = 0, 0, PD1=0, PD0=0, D11,..., D0
void writeDAC( uint8_t addr, uint16_t value ) {
value &= 0x0fff;
Wire.beginTransmission( addr );
Wire.write( (value >> 8) & 0xff ); // high byte
Wire.write( value & 0xff ); // low byte
Wire.endTransmission();
}
รูปแบบการเชื่อมต่อมีดังนี้
Pico MCP4725
3.3V ---- VCC
GND ---- GND
GP7 <--> SCL
GP6 <--> SDA
A0 (Not Connected)
GP26 <--- VOUT
สัญญาณเอาต์พุตที่ขา VOUT ของ MCP4725
จะถูกเชื่อมต่อด้วยสายไปกลับไปยังขา GP26 / ADC0 บนบอร์ด Pico
เพื่ออ่านค่าแรงดันอินพุตแล้วนำค่าที่ได้แปลงเป็นตัวเลข เก็บไว้ในตัวแปร adc_value
และส่งเป็นข้อความที่มีค่าของตัวแปรทั้งสอง ผ่านทาง SerialUSB
ไปยังคอมพิวเตอร์ของผู้ใช้ เพื่อแสดงเป็นรูปกราฟ (มีกราฟสองเส้นเปรียบเทียบกัน)
รูป: การต่อวงจรทดลองใช้งานโมดูล MCP4725 I2C DAC
รูป: การแสดงข้อมูลที่ได้รับมาในรูปของกราฟโดยใช้ Arduino Serial Plotter
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Created: 2021-12-16 | Last Updated: 2021-12-18