2017年9月30日 星期六

[Arduino]MCP4728 修改I2C Address

MCP4728簡介如下:
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 。 

把現有的位置和新的位置依照上面填空後,就是4個byte的指令,接下來要接上

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:

}
執行完畢後,請記得將Arduino 斷電,重新上電後用I2C Scanner 範例程式來確認是否有改成功。

參考來源:
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

沒有留言:

張貼留言