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

2017年9月16日 星期六

「LinkIt Samrt 7688][Arduino] 7688與Arduino Nano USB通訊

最近有一個需求,讓7688與Arduino透過USB通訊,想到的解法是Arduino本身的USB

預設為USB to Serial,只要能讓7688上的OpenWRT支援USB to Serial晶片驅動即可。

以下做了個簡單的Demo,附上Arduino及7688上python code做個Echo 測試。

檔案_000.jpeg




















這次用的是Arduino Nano,它使用的USB to Serial是 CH340  (我這塊不是原版的)

首先我需要安裝usbutils , 讓我可以使用lsusb來詢連接的USB裝置。(可以不裝)

opkg install usbutils


接下來安裝CH340驅動程式

opkg install kmod-usb-serial-ch341


Arduino程式碼

一開始連通Serial Port時會輸出hello form Arduino

之後會回應所收到的值,做一個Echo


接下來是7688上的python程式碼

CH340裝置的名稱會是 /dev/ttyUSBx ,像我是/dev/ttyUSB0 ,換成自己裝置的名字。

一連Serial Port會傳送Hello from python ,之後會每隔一秒傳送目前時間,並印出收到的訊息。



執行結果

「Android] Android 6.0 Wi-Fi and Networking Change

This release introduces the following behavior changes to the Wi-Fi and networking APIs.
  • Your apps can now change the state of WifiConfiguration objects only if you created these objects. You are not permitted to modify or deleteWifiConfiguration objects created by the user or by other apps.
  • Previously, if an app forced the device to connect to a specific Wi-Fi network by using enableNetwork() with the disableAllOthers=true setting, the device disconnected from other networks such as cellular data. In This release, the device no longer disconnects from such other networks. If your app’s targetSdkVersion is “20” or lower, it is pinned to the selected Wi-Fi network. If your app’s targetSdkVersion is “21” or higher, use the multinetwork APIs (such as openConnection()bindSocket(), and the new bindProcessToNetwork() method) to ensure that its network traffic is sent on the selected network.

以上是Android 6.0 Wi-Fi及網路進行的變更,中文的意思如下(若有翻錯請告知,謝謝):

  • 在過去APP可以進行修改手機中已連接過的Wi-FI設定值,也就是WifiConfiguration ,可以刪除/修改,但是在Android 6.0開始,您只能夠刪除/修改由APP本身建立的WifiConfiguration ,意思是說如果我想要把修改手機內某一個SSID的密碼,並自動連上它,是不會有效的,除非它是由您的APP新增而來的,若是手機中由使用者去手動輸入密碼的,或是其他APP建立的,都不行。
  • 過去,當您的手機連上Wi-Fi後,4G連線會被強制中斷,但在這次的6.0釋出中,裝置將不會與4G保持長時間的中斷,當您的APP targetSdkVersion <21時,它將會保持在您所選的Wi-Fi中,但targetSdkVersion >=21時,需要使用multinetwork APIs 來保證您所要傳輸的資料,會由您所選擇的網路(Wi-Fi / 4G)傳輸出去,例如openConnection()bindSocket()或是bindProcessToNetwork() 方法。