2016-11-01 320 views
4

我試圖模擬校準研究儀器的複合動作電位。目標是以250 Hz輸出某個10μV信號。低電壓將在稍後處理,對我來說主要問題是頻率。下面的圖片顯示了我正在嘗試製作的系統的概況。樹莓派上的Python延遲

enter image description here

通過從活的動物的數據採集,以及處理在MATLAB中的數據,我已經由低噪聲信號,並在12位格式789倍的值。然後我使用Git克隆了以csv格式將其存儲到Raspberry Pi的存儲庫。以下是我在RPi上編寫的Python腳本。您可以跳到腳本中的def main來查看功能。

#!/usr/bin/python 

import spidev 
from time import sleep 
import RPi.GPIO as GPIO 
import csv 
import sys 
import math 

DEBUG = False 
spi_max_speed = 20 * 1000000 
V_Ref = 5000 
Resolution = 2**12 
CE = 0 

spi = spidev.SpiDev() 
spi.open(0,CE) 
spi.max_speed_hz = spi_max_speed 

LDAQ = 22 
GPIO.setmode(GPIO.BOARD) 
GPIO.setup(LDAQ, GPIO.OUT) 
GPIO.output(LDAQ,GPIO.LOW) 

def setOutput(val): 
    lowByte = val & 0b11111111 #Make bytes using MCP4921 data sheet info 
    highByte = ((val >> 8) & 0xff) | 0b0 << 7 | 0b0 << 6 | 0b1 << 5 | 0b1 << 4 
    if DEBUG : 
     print("Highbyte = {0:8b}".format(highByte)) 
     print("Lowbyte = {0:8b}".format(lowByte)) 
    spi.xfer2([highByte, lowByte]) 

def main(): 
    with open('signal12bit.csv') as signal: 
     signal_length = float(raw_input("Please input signal length in ms: ")) 
     delay = float(raw_input("Please input delay after signal in ms: ")) 
     amplitude = float(raw_input("Please input signal amplitude in mV: ")) 
     print "Starting Simulant with signal length %.1f ms, delay %.1f ms and amplitude %.1f mV." % (signal_length, delay, amplitude) 
     if not DEBUG : print "Press ctrl+c to close." 
     sleep (1) #Wait a sec before starting 
     read = csv.reader(signal, delimiter=' ', quotechar='|') 
     try: 
      while(True): 
       signal.seek(0) 
       for row in read: #Loop csv file rows 
        if DEBUG : print ', '.join(row) 
        setOutput(int(row)/int((V_Ref/amplitude))) #Adjust amplitude, not super necessary to do in software 
        sleep (signal_length/(data_points*1000) #Divide by 1000 to make into ms, divide by length of data 
       sleep (delay/1000) 
     except (KeyboardInterrupt, Exception) as e: 
      print(e) 
      print "Closing SPI channel" 
      setOutput(0) 
      GPIO.cleanup() 
      spi.close() 

if __name__ == '__main__': 
    main() 

該腳本幾乎按預期工作。將MCP4921 DAC的輸出引腳連接到示波器可以很好地再現信號,並正確輸出後續延遲。

不幸的是,數據點比我需要的要多得多。我能夠將信號塞入的最短時間約爲79毫秒。這是因爲在休眠功能中除以789000,我知道這對Python和Pi來說太多了,因爲讀取csv文件需要時間。但是,如果我嘗試手動創建數組,並將這些值取出而不是讀取csv文件,我可以實現6 kHz以上的頻率而不會丟失。

我的問題是

我怎樣才能得到這個信號出現在250 Hz的頻率,並從用戶的輸入可靠地降低呢?我想過在腳本中手動將789值寫入數組,然後將SPI速度更改爲適合250 Hz的值。這可以消除慢速csv閱讀器功能,但是不能減少用戶輸入的頻率。無論如何,消除對csv.read的需求將會有很大的幫助。謝謝!

+0

您可以從您的csv閱讀器中創建一個列表,以便將其緩存在內存中。 –

+0

我試過這樣做,但它似乎沒有任何區別。在這個腳本之前,我從來沒有用Python進行過編程,你能否具體解釋一下如何只讀取一次csv,然後循環緩存的列表呢? – Tachyon

+0

'250Hz'=您是否意味着每個789個樣本之間有4ms? – barny

回答

0

今天早些時候發現,所以我想我會在這裏發表一個答案,以防將來有人遇到類似的問題。

由於多種原因,數據點之間的內部延遲問題無法用sleep()解決。我落得這樣做以下

  • 移動所有數學和函數調用超過了臨界循環
  • 請在花費無延遲傳輸值時間的線性迴歸分析
  • 將CSV文件中的數據點數量增加到MATLAB中的「很多」(9600)
  • 計算滿足用戶所需信號長度所需的點數
  • 從現在較大的CSV文件中取平均分離的條目以適應儘可能接近點數。
  • 在臨界循環

新的代碼計算這些值,然後計算出SPI明確字節

  • 保存兩個字節清單,並將其輸出直接,具有位輸入的檢查,是低於

    #!/usr/bin/python 
    
    import spidev 
    from time import sleep 
    import RPi.GPIO as GPIO 
    import sys 
    import csv 
    import ast 
    
    spi_max_speed = 16 * 1000000 # 16 MHz 
    V_Ref = 5000 # 5V in mV 
    Resolution = 2**12 # 12 bits for the MCP 4921 
    CE = 0 # CE0 or CE1, select SPI device on bus 
    total_data_points = 9600 #CSV file length 
    
    spi = spidev.SpiDev() 
    spi.open(0,CE) 
    spi.max_speed_hz = spi_max_speed 
    
    LDAQ=22 
    GPIO.setmode(GPIO.BOARD) 
    GPIO.setup(LDAQ, GPIO.OUT) 
    GPIO.output(LDAQ,GPIO.LOW) 
    
    def main(): 
    
        #User inputs and checking for digits 
        signalLengthU = raw_input("Input signal length in ms, minimum 4: ") 
        if signalLengthU.isdigit(): 
         signalLength = signalLengthU 
        else: 
         signalLength = 4 
    
        delayU = raw_input("Input delay after signal in ms: ") 
        if delayU.isdigit(): 
         delay = delayU 
        else: 
         delay = 0 
    
        amplitudeU = raw_input("Input signal amplitude in mV, between 1 and 5000: ") 
        if amplitudeU.isdigit(): 
         amplitude = amplitudeU 
        else: 
         amplitude = 5000 
    
        #Calculate data points, delay, and amplitude 
        data_points = int((1000*float(signalLength)-24.6418)/12.3291) 
        signalDelay = float(delay)/1000 
        setAmplitude = V_Ref/float(amplitude) 
    
        #Load and save CSV file 
        datain = open('signal12bit.csv') 
        read = csv.reader(datain, delimiter=' ', quotechar='|') 
        signal = [] 
        for row in read: 
         signal.append(ast.literal_eval(row[0])) 
    
        #Downsampling to achieve desired signal length 
        downsampling = int(round(total_data_points/data_points)) 
        signalSpeed = signal[0::downsampling] 
        listlen = len(signalSpeed) 
    
        #Construction of SPI bytes, to avoid calling functions in critical loop 
        lowByte = [] 
        highByte = [] 
        for i in signalSpeed: 
         lowByte.append(int(i/setAmplitude) & 0b11111111) 
         highByte.append(((int(i/setAmplitude) >> 8) & 0xff) | 0b0 << 7 | 0b0 << 6 | 0b1 << 5 | 0b1 << 4) 
    
        print "Starting Simulant with signal length %s ms, delay %s ms and amplitude %s mV." % (signalLength, delay, amplitude) 
        print "Press ctrl+c to stop." 
        sleep (1) 
    
        try: 
         while(True): #Main loop 
          for i in range(listlen): 
           spi.xfer2([highByte[i],lowByte[i]]) #Critical loop, no delay! 
          sleep (signalDelay) 
        except (KeyboardInterrupt, Exception) as e: 
         print e 
         print "Closing SPI channel" 
         lowByte = 0 & 0b11111111 
         highByte = ((0 >> 8) & 0xff) | 0b0 << 7 | 0b0 << 6 | 0b1 << 5 | 0b1 << 4 
         spi.xfer2([highByte, lowByte]) 
         GPIO.cleanup() 
         spi.close() 
    
    if __name__ == '__main__': 
        main() 
    

    結果正是我想要的。以下是信號長度爲5 ms的示波器示例; 200赫茲。謝謝你的幫助,夥計們!

    Oscilloscope reading