Features
• 12-Bit Voltage Output DAC with Four Buffered Outputs
• On-Board Nonvolatile Memory (EEPROM) for DAC Codes and I2C™ Address Bits
• Internal or External Voltage Reference Selection
• Output Voltage Range:
- Using Internal VREF (2.048V):
0.000V to 2.048V with Gain Setting = 1
0.000V to 4.096V with Gain Setting = 2
- Using External VREF (VDD):
0.000V to VDD • ±0.2 Least Significant Bit (LSB) Differential Nonlinearity (DNL) (typical)
• Fast Settling Time: 6 µs (typical)
• Normal or Power-Down Mode
• Low Power Consumption
• Single-Supply Operation: 2.7V to 5.5V
• I2C Interface: - Address bits: User Programmable to EEPROM
- Standard (100 kbps), Fast (400 kbps) and High Speed (HS) Mode (3.4 Mbps) • 10-Lead MSOP Package • Extended Temperature Range: -40°C to +125°C
資料來源:
http://ww1.microchip.com/downloads/en/DeviceDoc/22187E.pdf
它是一顆擁有四個類比輸出的DAC,透過I2C介面做控制。
12Bit 解析度,可以把輸出的電壓分成4096等份,例如輸出0~5V, 則可以將電壓的精準度
達到 0.001。
可有以下幾種模式(透過指令即可變更):
使用外部參考電壓,例如5V 。
使用內部參考電壓 Gain =1
0.000V to 2.048V
使用內部參考電壓 Gain =2
0.000V to 4.096V
如果要求精準,建議使用內部參考電壓 Gain =1 ,並透過穩壓IC供電給DAC。
如果要使用多組DAC在電路中,則需要改變MCP4728的I2C位置,我們先來看一下
datasheet中的說明
MCP4728預設的I2C位置為0x61 , 也就是1100001 ,也就是A2=0 , A1=0 ,A0=1 。
如果今天我要把它改成0x60 (1100000), New Address bit A2=0, A1=0 , A0=0 。
LDAC這支Pin腳,它需要在第2個byte傳送到第8個bit時,將LDAC由HIGH轉LOW,
否則怎麼傳送指令都會失敗,注意必須在MCP4728回傳ACK 之前就要轉LOW,
所以用Arduino內建的Wire.Write會失敗,弄了2天,請教了Firmware朋友,得到的
答案建議自己去控制I2C通訊時的HIGH ,LOW ,才能在第八個bit時將LDAC轉LOW,
等到ACK回來就來不了。來看一下接線
Arduino UNO MCP4728
VCC VDD
GND VSS
2 SCL
3 SDA
8 LDAC
接下來是我去網路上找的一份程式碼,整理之後,確認可以成功。
將預設的0x61位置改成0x60
// R/W direction bit to OR with address for start or restart #define I2C_READ 1 #define I2C_WRITE 0 #define SOFT_I2C_MASTER #define I2C_DELAY_USEC 10 #define ldac_pin 8 uint8_t sclPin_; uint8_t sdaPin_; unsigned char OldAddr = 0x1; //0x61 default I2C Address unsigned char NewAddr = 0x0; //0x60 new I2C Address //I2C Init void init(uint8_t sclPin, uint8_t sdaPin) { sclPin_ = sclPin; sdaPin_ = sdaPin; pinMode(sclPin_, OUTPUT); digitalWrite(sdaPin_, HIGH); //Mark_H fix pinMode(sdaPin_, OUTPUT); digitalWrite(sclPin_, HIGH); digitalWrite(sdaPin_, HIGH); } //------------------------------------------------------------------------------ // read a byte and send Ack if last is false else Nak to terminate read uint8_t read(uint8_t last) { uint8_t b = 0; // make sure pullup enabled digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, INPUT); // read byte for (uint8_t i = 0; i < 8; i++) { // don't change this loop unless you verify the change with a scope b <<= 1; delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, HIGH); if (digitalRead(sdaPin_)) b |= 1; digitalWrite(sclPin_, LOW); } // send Ack or Nak digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, OUTPUT); digitalWrite(sdaPin_, last); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); digitalWrite(sdaPin_, HIGH); return b; } //------------------------------------------------------------------------------ // send new address and read/write without stop uint8_t restart(uint8_t addressRW) { digitalWrite(sclPin_, HIGH); return start(addressRW); } //------------------------------------------------------------------------------ // issue a start condition for i2c address with read/write bit uint8_t start(uint8_t addressRW) { digitalWrite(sdaPin_, LOW); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); return write(addressRW); } //------------------------------------------------------------------------------ // issue a stop condition void stop(void) { digitalWrite(sdaPin_, LOW); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sdaPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); } //------------------------------------------------------------------------------ // write byte and return true for Ack or false for Nak uint8_t write(uint8_t b) { // write byte for (uint8_t m = 0X80; m != 0; m >>= 1) { digitalWrite(sdaPin_, m & b); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); } // get Ack or Nak digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, INPUT); digitalWrite(sclPin_, HIGH); b = digitalRead(sdaPin_); digitalWrite(sclPin_, LOW); digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, OUTPUT); return b == 0; } //------------------------------------------------------------------------------ // write byte and return true for Ack or false for Nak uint8_t ldacwrite(uint8_t b, uint8_t ldacpin) { // write byte for (uint8_t m = 0X80; m != 0; m >>= 1) { // don't change this loop unless you verivy the change with a scope digitalWrite(sdaPin_, m & b); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); } // get Ack or Nak digitalWrite(ldacpin, LOW); digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, INPUT); digitalWrite(sclPin_, HIGH); // digitalWrite(bsy_pin,LOW); b = digitalRead(sdaPin_); digitalWrite(sclPin_, LOW); digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, OUTPUT); return b == 0; } void setup() { Serial.begin(9600); pinMode(ldac_pin,OUTPUT); digitalWrite(ldac_pin,HIGH); delay(10); init (2,3); uint8_t data1=(unsigned char)(0B11000000 | (OldAddr << 1)); uint8_t data2=(unsigned char)(0B01100001 | (OldAddr << 2)); uint8_t data3=(unsigned char)(0B01100010 | (NewAddr << 2)); uint8_t data4=(unsigned char)(0B01100011 | (NewAddr << 2)); uint8_t ack1=start(data1); uint8_t ack2=ldacwrite(data2,ldac_pin); uint8_t ack3=write(data3); uint8_t ack4=write( data4); stop(); Serial.println(data1); Serial.println(data2); Serial.println(data3); Serial.println(data4); Serial.println(ack1); Serial.println(ack2); Serial.println(ack3); Serial.println(ack4); delay(10); Serial.println("FINISH"); } void loop() { // put your main code here, to run repeatedly: }
參考來源:
https://forum.pjrc.com/threads/28424-Programming-the-I2C-address-of-a-MCP4728-4-channel-DAC-using-i2c_t3
https://github.com/TrippyLighting/SoftI2cMaster/blob/master/SoftI2cMaster.cpp