2017年8月1日 星期二

如何透過Python去建立一個GUI視窗,使用Ardunio的I2C功能

事先工作
1.在PC上灌Python
2.準備一台Arduino
3.將Arduino接到PC USB port,但它們是透過UART COM port在通訊。

開始Arduino程式
1.在Adruino裡,寫程式,用來接受、辨識PC所傳來的字元。
   在loop迴圈裡等待,Serial port的字元,假設是6個字以內。  

void loop() {
   delay(200);
   while(Serial.available())
    {
      if (a<6)
      {
      inchar=Serial.read();      
      Strtest[a]=inchar;      
      a++;
      Strtest[a]='\0';
      serialdone=1;
      }          
    }

2.接著將字元解讀出來, 假設收到的第0個字元是W,就代表I2C Write,
   如果是R,就代表I2C Read。而第1,2個字元就代表register addres。第
   3,4個字元代表data。如果第0個字元不是W也不是R,就代表不是I2C
   操作,就會出現Wrong的訊息。但大家也可以定義其他的命令。
   請參考3的函式說明,我們需要將接收到的1,2,3,4字元,由ASCII碼轉
   成十六進制。用writeReg將address與data寫入I2C裝置。用readReg將
   address寫入I2C裝置後,I2C裝置就會回覆value,再將這個value用
   Serial.println(value,HEX);回傳給PC。PC的Python就可以顯示這個資
   料了。

if (serialdone==1)
    {
      Serial.print(Strtest[0]);
      Serial.print(Strtest[1]);
      Serial.print(Strtest[2]);
      Serial.print(Strtest[3]);
      Serial.print(Strtest[4]);
      int x,y,z;
      y=int(Strtest[2]);
      z=ASCtoHEX(y);  
      y=int(Strtest[1]);
      z=ASCtoHEX(y)*16+z;  
      if (Strtest[0]== 'W')
      {
      y=int(Strtest[4]);
      x=ASCtoHEX(y);  
      y=int(Strtest[3]);
      x=ASCtoHEX(y)*16+x;
       Serial.print("Write");
       writeReg(z,x);  
      }
      else if (Strtest[0]== 'R')
      {  
       Serial.print("Read");
       Serial.print(z,HEX);
       Serial.print(readReg(z),HEX);    
      }
      else
      Serial.print("Wrong");
      serialdone=0;
      setcom=1;
      a=0;  
   }
}

3.在loop迴圈以外,還定義了3個函式,用來做資料轉換。
 
   ASCtoHEX:由於從PC接收到的字元,都是ASCII碼,所以用這個
                          函式來將它轉成16進制碼。
   writeReg:要對I2C裝置寫入資料,需要address(reg)與data(valuein)
                      的數值。而i2cAddress是I2C裝置的Slave address,我是
                      設成固定的,不是從PC上輸入。
    readReg:要對I2C裝置讀出資料,只需要填address(reg)的值,然
                      後I2C裝置就會回傳data(value),這就是那個reg裡的值。                                                                                                

int ASCtoDec(int asc)
{
 if (asc>47 && asc <58)
 asc=asc-48;
 else if (asc>64 && asc<71)
 asc=asc-55;
 else if (asc>96 && asc<103)
 asc=asc-87;
 return asc;
}

void writeReg(int reg, word valuein){    
    Wire.beginTransmission(i2cAddress);  
    Wire.write(reg);
     highbyte = valuein >>8;
     lowbyte = valuein&0x00FF;
     Wire.write(lowbyte);   //send low byte first      
     Wire.write(highbyte);
    Wire.endTransmission();
}

int readReg(int reg){
    Wire.beginTransmission(i2cAddress);  
    Wire.write(reg);
    Wire.endTransmission(false);
    Wire.beginTransmission(i2cAddress);
    Wire.requestFrom(i2cAddress, 2);
    while(Wire.available()) // ensure all the data comes in
    {
    lowbyte=Wire.read();
    highbyte=Wire.read();
    }
    value = highbyte * 256;
    value = value + lowbyte;
    Serial.println(value,HEX);
    return value;
}



開始Python程式

1.建立COM port,這個"COM10"要是接上Arduino上的那個port。
   請打開電腦的裝置管理員,確認Arduino是接到那個port,再去
   改程式裡的"COM__"。
## Serial port setting.
ser =serial.Serial("COM10",  timeout=2)
def SerialWrite(command):
 ser.write(command)
 rv=ser.readline()
 #print (rv) # Read the newest output from the Arduino
 print (rv.decode("utf-8"))
 sleep(1) # Delay for one tenth of a second
 ser.flushInput()


2.這是在GUI裡的I2C Write操作介面,用來送出字元給Arduino。
   這段程式會送出W字元,還有送出address與data字元。
##===When button be pressed then Write I2C to device ============
def I2Cwrite():
  print("Write I2C slaver address %s" % (e1.get()))
  print("Write I2C register address %s" % (e2.get()))    
  print("Write I2C data %s" % (e3.get()))  
  reg_write="W"+e2.get()+e3.get()
  cmd=reg_write.encode("utf-8")
  ##print(cmd)
  SerialWrite(cmd)


2.這是在GUI裡的I2C Read操作介面,用來送出字元給Arduino。
   這段程式會送出R字元還有address字元。
##===When button be pressed then Read I2C from device============
def I2Cread():
   print("Read I2C slaver address %s" % (e1.get()))
   print("Read I2C register address %s" % (e2.get()))    
   print("Read I2C data %s" % (e3.get()))  
   ##print (rv.decode("utf-8"))  
   reg_read="R"+e2.get()
   cmd=reg_read.encode("utf-8")
   SerialWrite(cmd)
   ##ser.close()


2.這是用來建立Serial COM port連線,並顯示連線狀態。在連線過程
   中,Aduino要回傳"Ready", PC的Python要接收到這個字元,表示
   連線已經成功。

##==Serial connect and Get arduino  Ready================
def Serial_Connect():
 global Ready_flag
 for i in range (1,10):
  print("Checking...")
  #Show to LabelA----------------
  Str_index=""
  str_check="th Checking...\n"
  Str_index=str(i)
  Label_msg="The " +Str_index +str_check
  LabelA.config(fg='green',)
  LabelA.config(text=Label_msg)
  LabelA.update_idletasks()
##  buttonStart.config(state="disabled")
  master.update()
  rv=ser.readline()
  #Debug print (rv) # Read the newest output from the Arduino
  print (rv.decode("utf-8"))
  ser.flushInput()
  sleep(0.5) # Delay
  Str_Message=rv.decode("utf-8")
  #Debug print(Str[0:5])
  if Str_Message[0:5]=="Ready":
   Ready_flag=1
   print("Get Arduino Ready !")
   #Show to LabelA----------------
   LabelA.config(text="Get Arduino connecting Ready !")
   LabelA.update_idletasks()
   master.update()
   break
  elif  i==9:
   Ready_flag=0
   LabelA.config(fg='red',)
   LabelA.config(text="Can't connnect to Arduino successfully!")
##   buttonStart.config(state="active")
   LabelA.update_idletasks()
   master.update()
   sleep(1) # Delay


3.這段程式用來建立GUI介面,定義了各個Label與空白輸入格的位置。
## Windows setting
master = Tk()
master.title("Auduino control interface")
master.minsize(600,400)

#label.grid(row=0, column=0, columnspan=2, rowspan=2, padx=5, pady=5)
LabelA=Label(master,bg='white',fg='blue',text="Press 'Connect' Button to connect Arduino",width=60,height=4,)
LabelA.grid(row=0 ,columnspan=3)
Label(master, text="Slave address").grid(row=12)
Label(master, text="Data address").grid(row=16)
Label(master, text="Data").grid(row=18)
e1 = Entry(master)
e2 = Entry(master)
e3 = Entry(master)
e1.insert(5,"18")
e1.grid(row=12, column=1,columnspan=1)
e2.grid(row=16, column=1,columnspan=1)
e3.grid(row=18, column=1,columnspan=1)
Button(master,text="Connect",command=Serial_Connect).grid(row=3 ,column=0, sticky=W, pady=1)
Button(master, text='write', command=I2Cwrite).grid(row=22, column=2, sticky=W, pady=1)
Button(master, text='Read', command=I2Cread).grid(row=22, column=1 ,sticky=W, pady=1)



2017年2月20日 星期一

USB PD 3.0 Programmable Power Supply(PPS)

2017/1 USB協會在USB PD 3.0的規範上,加了PPS協議。這個協議讓
讓高通的QC, MTK的PE+,OPPO的VOOC等快充標準,將來可以透過
PPS,兼容於USB協會定義的USB PD。

為什麼PPS可以相容這些主流的快充標準呢? 因為它可以達到3V~21V
輸出。即可以輸出高壓低電流,也可以輸出低壓高電流(5A)。










在USB PD 3.0的規範,多了Alert、Battery Status, Get_Country_Info的
資訊可以溝通。























在Source Capability裡,多了APDO,這用來表示Power source它可以
輸出的電壓範圍與最大電流。例如你可以設定Source的APDO為
3V~5.5V/4A,那Sink就可以發PRDO去要求3V~5.5V之間的任何一個
電壓點,最小以20mV為一個間隔。






















































Reference:
1. http://www.usb.org/developers/docs/usb_31_021517.zip
    => "USB_PD_R3_0 V1.1 20170112.pdf"



2017年2月13日 星期一

WPC Wireless Power Qi control flow (Qi控制流程)


WPC的Qi 定義了Power transmitter (TX)是如何偵測到Power Receiver(RX),到如何
建立起協議(Contract),進而供電。下面就是TX與RX的控制流程。



1. Selection
    在這個階段,TX會去偵測是否有物體被放置。物體有兩種,一種是RX,
    另外就是異物。TX會透過Ping來偵測是否物體是RX,RX在接收到TX給
    Digital ping後,就有足夠的電力來傳送自身的資訊給TX,TX再跟據進行
    identification & Configuration。
    

2. Ping
    Ping有兩種,一種是Analog Ping,另一種是Digital Ping。 為了降低TX在偵
    測物體放置的功耗,TX會先傳送耗電比較小的Analog Ping來偵測TX線圈
    上的電壓或電流。Analog Ping是加一段較低電壓的弦波電壓在TX線圈上,
    如果有異物或RX的線圈被放靠近TX線圈,這個弦波電壓就會衰減。TX
    由此判斷有物體在TX線圈上,這時TX再傳送Digital Ping,就是一個較大
    電壓振幅的訊號,RX線圈在接收到這個訊號後,這個訊號能提供電力讓
    RX IC啟動,並立即回覆"信號強度"的封包給TX,TX在接收到這個封包
    後,才會持續提供電力給RX。如果RX沒有回覆,TX就會停止提供電力。
    

3. Identification & Configuration
   當RX回覆""信號強度給TX後,RX便進到Identification & Configuration,這
   時RX會再回送"Identification"與"Configuration"封包給TX,讓TX得知RX的
   資訊。 Identification這個封包包括了RX是符合WPC spec的版本,RX制造商
   的WPC編號、RX的基本Device編號。如果"Ext" bit為"1",就表示RX會再多
   傳一個"Extended Device  Identifier"
   另外Configuration封包,包括了RX需要的maximum power value,  Window平
   均的參數。

Identification packet
  
Configuration packet


4.Power transfer 
   TX會基於RX傳送來的"Identification"& "Configuration"來建立Power transfer 
   contract。如果RX需要的Power小於等於5W,TX與RX就會直接進到Power 
   transfer mode,在這 個mode,TX就會開始供電給RX,RX可以直接輸出
   Power給它的負載。在這個階段,RX不停地偵測目前的情況,並回報下面
   這些命令給TX,TX就可以因應。

§ Control Error Packet.   
   RX回報Control Error的值給TX,TX就根據這個值來改變TX線圈的電流,
   調整TX輸出的 能量,直到Control Error的值為"0"。
§ Received Power Packet.   
   TX需要得知Power Loss,才能去判斷是否有FO(異物),如下面的公式,PT
   是TX的輸出功率,PR是RX接收到功率,這個值相減,就是Loss損失功率
   。所以RX要不停去回報PR,TX才能去更新Loss功率值。



§ Charge Status Packet.  
   如果RX裝置有搭載可充式能源儲存零件,例如電池。RX可以用這個封包來
   回報電池充電的狀態,0~100%。
§ End Power Transfer Packet.
   RX發送End Power Transfer封包給TX,要求結束Power transfer。此外也可以
   註明RX要求結束的原因,例如 Over current, Over Temperature, Charge 
   complete... 
§ Renegotiate Packet.
   RX發送Renegotiate封包給TX,來要求調整Power transfer contract。當TX同意
   後,就會進到Renegotiate階段。


Power transfer contract


5.Negotiation
  在Power transfer contract,如果RX需要超過5W的電力,就需要進行
   Negotiation。RX進到Negotiation階段後,可以送出Request來更新Power
   transfer contract,例如將Guaranteed Power從5W改到10W。在這個階
   段,RX也會傳送下面這些的命令,讓TX、RX可以得知彼此的資訊。

§ Specific Request Packet    
   這個Request用來改變Power transfer contract裡的spec。
§ General Request Packet    
   這個Rquest用來得知TX的資訊。
§ FOD Status Packet   
   RX發送這個命令,用來確認是否TX有偵測到FOD(異物)。
§ Proprietary Packet   
   這個廠商可以自行定義的,當RX得到TX的ID,並且能辨別到TX。 RX
   就可以用這個封包來   傳送特定的命令或資訊,此外TX也要建立這個
   封包的資訊,才能辨別這個封包。
§ WPID Packet   
   RX可以發送WPID(Wireless Power Identifier)給TX,這是獨一無二的辨
   識碼,讓TX可以辨別這個RX。
  

6.Calibration
   在Negotiation結束後,就會進到Calibration階段。在Power transfer階段,有提
   到Power loss的計算,這個階段主要是用來增進Power Loss估算的能力,可以
   讓FO的偵測更準確。由於各家TX、RX的機構設計、電路設計不同,使得TX
   傳送能量的能力與RX接收能量的能力也不同,這使得Power Loss計算的條件
   也不同。為了讓Power Loss的標準統一,就需要去校正PT、PR的功率值。校
   正的方式是分別在輕載(Light Load)與重載(Connected Load)來得到PT與PR,
   再用線性內插法(Linear interpolation)來得到校正常數a,b。在進到Power transfer
   mode後,PT、PR的值再用a,b值來校正,再去計算Ploss。TX才能判斷是否是
   FO。這部分的數學式可以去看Qi V1.2.2 spec 11.4.3 Calibration。























Reference:
1. WPC spec 1.2.2->   https://www.wirelesspowerconsortium.com/downloads/
    download-wireless-power-specification.html .