2019年1月23日 星期三

DSP TI TMS320F28335 PWM設置




Time Base(TB)時間基礎方塊

TBPRD:Time base period,也就是計數的周期是多少TBCLK。
TBCLK:Time base clock是由SYSCLKOUT prescale,做為TB的CLOCK,在
                 TBCTL.CLKDIV去設置。
TBCTR:Time base counter計數暫存器,可以向上也可以向下計數。
TBPHS:Time base phase相位暫存器,用來同步不同的ePWM模組。在觸發
                 ePWMxSYNCI事件,各個ePWM模組就將各自TBPHS裡的值載入到
                TBCTR。這個功能可以在TBCTL.PHSEN開啟或關閉。
TBCTL:Time base control 控制暫存器,用來設置TB裡的參數,如Clcok
                 prescale, 計數器是,向上計數/向下計數/向上向下計數方式,active
                與shadow模式的選擇。

在TBPRD、CMPA、CMPB有分成兩種模式,分別是active與shadow,在設置為
active mode的情況下,在更新TBPRD暫存器後,就會立即影響硬體,偵測到觸
發事件就會立即響應。在shadow mode則有緩衝,在更新TBPRD後,要等到
TBCTR為0,才會將TBPDR在shadown暫存器裡的值,更新到active暫存器,也
才會偵測硬體事件。這樣可以避免在軟體配置暫存器時,與硬體不同步而造成
系統崩潰或故障。






EPWMxSYNCI,同步信號脈衝,讓計數直接載入(跳到)TBPHS所設定的值。





Counter ccompare(CC)計數比較方塊

CMPA:Counter Compare A resigster計數比較暫存器A,在這個暫存器設定一個數
               值,與TBCTR的值做比較,用來設定PWM訊號的Duty。
CMPB:Counter Compare B resigster計數比較暫存器B
CMPCTL:Counter compare control計算比較的控制暫存器,裡面包括了CMPA/B要
                    被設為shadow或active模式。在shadow模式下,CMPA/B在更新後,是
                    要在CTR=PRD or CTR=0才去載入更新後的值。








Action Qualifier(AQ)動作限定方塊

AQCTLA:AQ控制暫器器A,在接收到TB送來的觸發信號CTR=PRT, CTR=ZERO,
與CC送來的觸發信號CTR=CMPA,CTR=CMPB,由這4個信號來控制EPPWMA的輸出。
設置AQCSFRC是在active模式還是shadow模式。
AQCTLB:AQ控制暫器器B
AQSFRC:AQ software force軟體強制暫存器,由軟體強制EPWM輸出為low/high/toggle
,toggle就是反轉,改變到相反準位。
AQCSFRC:AQ continute software force連續軟體強制





向上計數的範例,從AQCTLA/B去設置TBCTR=0,EPWMxA輸出high。TBCTR=CA,
EPWMxA輸出low。TBCTR=TBPRD,EPWMxA輸出high。




Dead-Bamd Generator(DB)死區方塊

DBCTL:Dead Band control死區控制暫存器,如下圖Figure31, BTCTL控制IN_MODE
POLSEL、OUT_MODE的開關。由IN_Mode
DBRED:Dead Band Rising Edge Delay死區上升邊緣暫存器
DBFED:Dead Band Falling Edge Delay死區下降邊緣暫存器







從上面的真值表來看,再對應Figure 32的波形,可以看出不同模式下EPWMxA與
EPWMxB的變化。DBCTL(IN_MODE)為"00",也就是從EPWMxA信號源來做為
EPWMxA與EPWMxB輸出。

Mode1:DBCTL(OUT_MODE)為00,表示Dead Band不動作。
Mode6:DBCTL(OUT_MODE)為01,作為FED,只有Falling Edge Delay方塊動作。
Mode7:DBCTL(OUT_MODE)為10,作為RED,只有Rising Edge Delay方塊動作。
Mode 2~5:DBCTL(OUT_MODE)為11,表示Rise edge與Falling edge delay方塊都
                    動作。EPWMxA的輸出經過上升延遲,EPWMxB的輸出經過下降延
                    遲delay。由DBCTL(OUT_POLSEL)來設置是否EPWMxA/B信號是否反
                    相。
Mode2:DBCTL(OUT_POLSEL)=10,EPWMxA/B互補,EPWMxB輸出反相, 與
               EPWMxA輸出相反。
Mode3:DBCTL(OUT_POLSEL)=01,EPWMxA/B互補,EPWMxA輸出反相, 與
               EPWMxB輸出相反。
Mode4:DBCTL(OUT_POLSEL)=00,EPWMxA/B信號同相。
Mode5:DBCTL(OUT_POLSEL)=11,EPWMxA/B信號都進行反相,所以兩個信
               號還是同相。



PWM-Chopper(PC)PWM斬波方塊

PWM斬波的功能如下
1.可由程式設置斬波頻率
2.可由程式設置第一個脈衝寬度
3.可由程式設置第2個脈衝以及後面脈衝的占空比(Duty cycle)
4.也可以旁路掉(Bypass)斬波功能

由於大多數功率器件如MOSFET,開通通電要比維持電流大得多,所以第一
個脈衝需要比較大的脈寬來導通MOSFET,後來的脈衝就可以用比較小的脈
寬來維持MOSFET的導通。

PCCTL:PWM Chopper control斬波控制暫存器
PCCTL[OSHTWTH]:設置第一個脈衝的脈寬。可設定1~16倍SYSCLKOUT/8
                                      的時間寬度。
PCCTL[CHPDUTY]:設置脈衝的占空比,從12.5%~87.5%
PCCTL[CHPFREQ]:除頻設置,從不除頻到8除頻。假設SYSCLKOUT為
                                    100MHz,在經過"/8"後,變成12.5MHz,再經過CHFREQ
                                    設置的除頻,"8除頻",PSCLK就為1.56MHz。
在第一次脈衝會由one-shot輸出,經由OSHT控制EPWM輸出,第一次脈衝後,
就由PSCLK來控制EPWM輸出。






Trip-Zone錯誤聯防方塊

Trip-Zone的功能如下
1.有輸入腳TZ1~TZ6對應到6組ePWM模組
2.在錯誤事件發生後,可以強制EPWMxA/B為高、低、高阻抗、無動作共4種
   動作。
3.在短路或過電流事件發生時,支援一次性錯誤聯防觸發(OSHT)。
4.在限流操作時,支援周期性錯誤聯防觸發(CBC)。
5.每個TZ腳位,都可以設置成OSHT或CBC,也都可以設置為中斷來源。
6.支援軟體強制錯誤聯防。也可以將錯誤聯防關閉。


TZSEL:TZ選置暫存器,個別設置TZ1~TZ6開啟或關閉錯誤聯防功能,並且為
                 一次性或周期性錯誤聯防,
TZCTL:TZ控制暫存器,設置錯誤聯防事件發生後,ePWMxA與ePWMxB各進
                 行這四個動作邏輯"高、低、高阻抗、無動作"的那一個。
TZINT:TZ中斷致能暫存器,設置一次性(OSHT)與周期性(CBC)事件發生後,
                 是否觸發EPWMx_TZINT中斷。
TZFLG:TZ中斷旗標暫存器,裡面的TZFLG.OST,TZFLG.CBC表示一次
                性(OSHT)與周期性(CBC)事件 是否 發生。當OST或CBC其中一個事件
                發生,並且PIE中斷也被致能,就會設置TZFLG.INT,表示
                 EPWMx_TZINT中斷。
TZCLR:當TZFLG裡的OSHT或CBC被設為"1"表示發生事件,就設置TZCLR裡
                 的OST與CBC為"1",來將TZFLG的旗標清除。當TZFLG.INT為"1",就
                 要設置TZCLR.INT來將TZFLG.INT清除為"0"。
TZFRC:錯誤聯防中斷強制暫存器,可以設置TZFRC.OST 或 TZFRC.CBC來強
                制TZFLG.OST與TZFLG.CBC為"1"。


一次性錯誤聯防發生後,需要軟體去將這個事件旗標(flag)清除,而周期性錯誤
聯防的事件旗標會自動被清除。








Reference

2018年1月15日 星期一

Python GUI to show Arduino ADC input and Sine waveform.

這篇文章的程式,是從底下Plotting-real-time-sound-sensor-data-from-Arduino
-using-Python的github去修改的,總共有下面兩種程式語言,修改後的程式
在我的github內。
https://github.com/zsnes631/Python-Arduino


1.寫Arduino程式,將ADC0的輸入電壓, 透過Uart Port輸出到PC。
2.寫Python GUI程式,接收到的數值,還有產生Sine波,依照時間軸動態顯示。

Python GUI程式
1,代入相關的模塊。
   "serial"是用在UART相關的命令
    "numpy"是用在數值運算,Sine波就是由它所計算出的。
    "matplotlib"是用來顯示(plot)圖面。
    "drawnow" 甩來即時更新圖面。
     "serial.tools.list_port"用來確認Serial port裝置的資訊。









2,設定變數,values為序列,plt.ion()是顯示出matlotlib plt的圖。
   Fs、f則是Sine波的參數。











3,尋找Arduino在那個UART Port,由於我用的Arduino是用USB to UART converter IC
   CH340,所以確認PC上的那個Uart Port的描述有CH340,就表示該Port是接到Arduino。










4.定義一個函式,用來設定GUI視窗plot plt的參數,包括它的title、XY的Label,
    XY軸的數值範圍(axis)。









5.values是用來存放Arduino ADC接收到的數值,values1是sine波的數值。我設定
  這些數值的Buffer到100個。所以我們可以看到現在的數值,跟之前的的99個數
  值。append是先用0填入Buffer 0~100,當作初始值。





6.用"serial"模塊的函數,當PC的Uart port有接收到資料,就存到變數valueRead。
    這個資料,就是Arduino傳過來的ADC數值。







7.利用try-except來做例外處理,確認從arduino接收到的數值,是在0~1024。
   每接收到一次數值,就存到values序列裡,並將序列裡,最舊的那筆資料
    移出(pop)。valueX是sine的計算式。f=4, Fs=100,可以得出每25(100/4)
    為一次sine波周期。300是峰值,Sine波的峰對峰pk-pk為600。最後面的
    400是level,等於是將sine wave上移了400個單位。



8.執行程式後,可以看到Arduino傳送來的數值values,跟values1 Sine波
   的波形。



Arduino程式
Arduino程式,每300ms讀取ADC pin A0的數值,再從Uart Port以Baudrate 19200bps
傳送出去。Arduino的ADC是10bits,輸入電壓範圍從0~5V,5V讀進來的值為1023。

,


Reference
https://github.com/DeligenceTechnologies/Plotting-real-time-sound-sensor-data-from-Arduino-using-Python

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)