2020年6月21日 星期日

VBA7 64-bit Declare FTDI D2XX DLL注意事項 PtrSafe and LongPtr



  

        FTDI是一家生產USB to GPIO/UART/I2C/SPI 等的轉換IC的公司,很容易在開發版上面看到它的身影,FTDI網站上也有提供一些example code,[1] "Our thanks go to Bob Freeth for providing this VB6 example of using the FT2232C MPSSE for SPI communication with a MAX187 ADC",因為Excel VBA本身也是VB核心,應該是可以直接拿來參考使用。

        直接複製是可以使用正常在VBA上面執行,但後來發現他無法在Excel 64x上面執行,再引入D2XXX.DLL Declare陳述句中會出現紅色的錯誤。



'==============================
'CLASSIC INTERFACE DECLARATIONS
'==============================
Public Declare Function FT_ListDevices Lib "FTD2XX.DLL" ( _
                                    ByVal arg1 As Long, _
                                    ByVal arg2 As String, _
                                    ByVal dwFlags As Long) As Long
                                    
Public Declare Function FT_GetNumDevices Lib "FTD2XX.DLL" Alias "FT_ListDevices" ( _
                                    ByRef arg1 As Long, _
                                    ByVal arg2 As String, _
                                    ByVal dwFlags As Long) As Long
                                    
Public Declare Function FT_Open Lib "FTD2XX.DLL" ( _
                                    ByVal intDeviceNumber As Integer, _
                                    ByRef lngHandle As Long) As Long

PtrSafe 大部分情況下工作正常

        上網查了一下[2] "64 位元 Visual Basic for Applications 概觀"原來Excel 64位元一些變數宣告需要修改變更一下,看了一些其他網頁的資料,大部分時候只需要在Sub or Function前面加入PtrSafe就可以正常執行,至少編譯器不會顯示錯誤。
'==============================
'CLASSIC INTERFACE DECLARATIONS
'==============================
#If VBA7 Then
Public Declare PtrSafe Function FT_ListDevices Lib "FTD2XX.DLL" ( _
                                    ByVal arg1 As Long, _
                                    ByVal arg2 As String, _
                                    ByVal dwFlags As Long) As Long
#Else
Public Declare Function FT_ListDevices Lib "FTD2XX.DLL" ( _
                                    ByVal arg1 As Long, _
                                    ByVal arg2 As String, _
                                    ByVal dwFlags As Long) As Long
#End If

        但改完發現結果不對,一樣的程序在Excel VBA 32-bit上面跑是正常的,但在64-bit上面就是不正常,在看了一下網路上的資料竟然有書專門在談論這個[3Excel 2019 Power Programming with VBA,但從Microsfot的網站上[2]提到指標Pointer控制代碼Handle在VBA7 64-bit下可能會因為資料長度不一樣無法正確執行。

由於 VBA 第 6 版或更早的版本並沒有指標控制代碼的特定資料類型,因此會使用 Long 資料類型 ( 32 位元 4 個位元組的資料類型),來參考指標和控制代碼。 在 64 位元環境中,指標和控制代碼是 8 位元組 64 位元的數量。 32 位元資料類型中,無法保存這些 64 位元數量。
        
        查一下[4] D2XX Programmer's Guide先把Handle部分找出來並把變數型態由原本Long改成LongPtr,例如以下兩個函數FT_Open and FT_Write,修改後跑起來就正常了,但每個有用到的都要去修改檢查。


Public Declare PtrSafe Function FT_Open Lib "FTD2XX.DLL" ( _
                                    ByVal intDeviceNumber As Integer, _
                                    ByRef lngHandle As LongPtr) As Long.

Public Declare PtrSafe Function FT_Write Lib "FTD2XX.DLL" ( _
                                    ByVal lngHandle As LongPtr, _
                                    ByVal lpszBuffer As String, _
                                    ByVal lngBufferSize As Long, _
                                    ByRef lngBytesWritten As Long) As Long
.


Update FTDI Driver and Summary
        
        後來在不同電腦交叉測試發現,到FTDI 下載新的Driver ( setup executable),一樣是64-bit VBA7但跑起來是正常的,但說實在只有跑幾個函數不知道是不是其它函數需要修改就是了,所以最安全與最艱單的做法是安裝32-bit的版本,MicroSoft的官方指南中也提到[5] "選擇 64 位元或 32 位元版的 Office" ,兼容之前的ActiveX or COM元件的確是每個軟體選擇要不要進版的一個原因,但這也算是一種人性,改了如果動不了又可能程式已經是好久好久的前輩弄得,早就不知道消失到哪去了,突然改了不會動找誰求救阿。

選擇 32 位元版本的理由

  • 您的 32 位元 COM 增益集沒有對應的 64 位元增益集。 您可以繼續在 64 位元 Windows 的 32 位元 Office 中執行 32 位元 COM 增益集, 也可以嘗試連絡 COM 增益集廠商,要求對方提供 64 位元版本。

  • 您使用的 32 位元控制項沒有對應的 64 位元控制項。 您可以在 32 位元 Office 中繼續執行 32 位元控制項,例如 Microsoft Windows 通用控制項 (Mscomctl.ocx、comctl.ocx) 或任何現有的協力廠商 32 位元控制項。

  • 您的 VBA 程式碼使用 Declare 陳述式。 除非您針對指標和控點以 Declare 陳述式呼叫 long 等使用 32 位元資料類型的 Windows API,否則無論您使用的是 64 位元或 32 位元,通常都不需要變更 VBA 程式碼。 在大部分情況下,將 PtrSafe 新增到 Declare,並以 LongPtr 取代 long,就可讓 Declare 陳述式同時與 32 位元和 64 位元相容。 不過,如果是沒有可用於 Declare 的 64 位元 API 的少數情況,您就無法這麼做。 如需 VBA 要在 64 位元 Office 上執行所需之必要變更的詳細資訊,請參閱 64 位元 Visual Basic for Applications 概觀



Reference

[1] Our thanks go to Bob Freeth for providing this VB6 example of using the FT2232C MPSSE for SPI communication with a MAX187 ADC.
[2] 64 位元 Visual Basic for Applications 概觀
[3] Excel 2019 Power Programming with VBA
[4] D2XX Programmer's Guide
[5] 選擇 64 位元或 32 位元版的 Office

2020年6月16日 星期二

食記 新竹 飛龍肉圓 推薦指數 機車10分鐘內


        走在新竹西門街不由自主地哼著歌,看到一座不知道會有吸血鬼還是殭屍的中西建築融合的教堂,太陽很大天空很藍,走著走著哼著歌前進

人在江湖身不由已,人生在世只有兩字,一字情,一字義..................飛龍........飛龍.......飛上天



        說時遲那是快,一個衝突的配色銀河飛龍........肉圓貢丸專賣店出現在眼簾,其實來新竹查一下"美食"大多時候都會找到飛龍肉圓,自己也吃過幾次,但因為離住的地方比較遠,所以都不會特地的過來吃,但今天剛好中午在這附近處理事情,就順路過來一下囉。


        飛龍肉圓菜單非常簡單,就只有肉圓跟貢丸湯 (20200616的價目表)

        份量來說肉圓非常小顆,一份兩顆大概是就是一顆彰化肉圓的份量,跟名字很接近的玉龍肉圓份量少了些,但如果是路過當個解饞的點心是還蠻剛好的,不會太多。


        這裡的肉圓內餡是紅槽肉跟一顆"栗子",也是飛龍肉圓的特色,雖然我個人覺得有沒有放這一顆吃起來沒有差很多,肉圓皮Q紅色的醬汁甜中帶點辣。


        貢丸湯不錯,貢丸很Q彈,不知道用哪一家的但值得買回家煮的等級,湯雖然沒有濃郁的大骨味,但靠貢丸本身煮出來的味道在配上芹菜喝起來非常爽口。


        飛龍肉圓口味不錯,份量不多對於不想吃太的人是個不錯的選擇,但店門口不方便停車要停遠百貨隔壁的停車場,但總體上配合整個新竹火車站附近景點(城隍廟)是個可以繞過來吃的店。

推薦指數 機車10分鐘內



2020年6月6日 星期六

如何用Excel VBA控制COM Port / EXCEL VBA RS-232/UART Control Via VISA COM Library

 
RS-232/UART

       RS-232 UART是電腦最古早的通訊介面之一,即使現在買桌上型電腦都還會有保留RS-232,而且現在很多MCU的控制介面,UART都還是基本會支援的標準協定,差異主要是差異在電位的高低,最早期的RS-232為12V, 現在MCU可能都是3.3V or 1.8V,如果想知道Serial Communication可以參考MIPI RFFE Introduction and  two wire communication/serial communication UART, I2C

VISA Virtual Instrumentation Software Architecture

        工作上常常需要控制儀器與待測物來完成工作,早期的儀器通訊介面大多是RS-232或GPIB IEEE 488.2,而且為了讓每使用者在使用不同儀器都有類似的語法都會支援SCPI Standard Commands for Programmable Instruments指令,安裝VISA COM(Virtual Instrumentation Software Architecture) Library大部分常見的程式語言就可以使用這個'VISA COM Library來進行儀器控制。

Excel VBA RS-232 Control via VISA

        Excel本身有內建VBA (VB6的語法結構),平常工作我經常使用Excel VBA來進行儀器控制,這可以在Keysight或NI的網站上找到不少資源,另外VISA COM因為相容之前RS-232所以也可以透過VISA COM直接對RS-232進行控制,因為VBA(VB6)不能使用類似C# or VB.NET直接使用SYSTEM.IO.PORTS來對COM Port進行存取,網路上還有一些文章有教學如何用Excel VBA來取存RS232,但有COM 編號要在4以下的限制,而且內容較複雜,這裡我介紹如何用Excel VBA + VISA COM Library進行UART傳輸跟一些小技巧

       首先要安裝VISA COM Library,可以安裝Keysight[2] or NI[3]都可以,細節部分[4] 7 steps to using VBA in Excel to control routing in test automation ,安裝我們在Excel拉出一個Command Button然後輸入以下程式碼。

Private Sub CommandButton1_Click()
    'myCOM為虛擬儀器資源
    Dim myCOM As VisaComLib.FormattedIO488
    're_myCOM是對應的控制介面管理
    Dim rm_myCOM As VisaComLib.ResourceManager
    Set myCOM = New VisaComLib.FormattedIO488
    Set rm_myCOM = New VisaComLib.ResourceManager
    ' 開啟COM PORT資源 這個範例是4, 但可以到99沒問題
    Set myCOM.IO = rm_myCOM.Open("ASRL4::INSTR")
    ' RS232是VISA COM對應Serial Port資源
    Dim RS232 As ISerial
    Set RS232 = myCOM.IO
    '設定BaudRate還有硬體控制
    RS232.BaudRate = 9600
    RS232.FlowControl = ASRL_FLOW_NONE
    myCOM.IO.TerminationCharacter = 10
    myCOM.IO.TerminationCharacterEnabled = True
    '定義傳送的變數
    Dim in_str As String
    Dim out_str As String
    out_str = Chr(72) & Chr(69) & Chr(76) & Chr(76) & Chr(79)
    '傳送字串 H, E, L, L, O
    myCOM.WriteString out_str
    '讀取字串
    in_str = myCOM.ReadString
    MsgBox in_str & " Len = " & Len(in_str) & " last byte dec =" & Asc(Mid(in_str, Len(in_str), 1))
    RS232.Close
End Sub
        我們將UART TX與RX用Jumper接起來測試看看執行結果,這個結果我們是發送了out_str這字串,字串的內容是由ASCII編碼組成,傳送72, 69, 76, 76, 79 這5個bytes的資料,ASCII編碼就是HELLO。
out_str = Chr(72) & Chr(69) & Chr(76) & Chr(76) & Chr(79)
但回傳了字串Strin長度卻是6 bytes,最後一各byte為DEC 10,對應的ASCII為換行字元,有點算是如果你用超級終端機Hyper Terminal 輸入HELLO後按下的Enter鍵的行為。
        因為VISA COM WriteString這函數原本就是設計給儀器控制用的,而儀器控制會以換行字元LF來判定指令是否結束或資料是否結束,所以WriteString會自動帶上LF字元,另外ReadString如果沒有讀到LF字元在一定時間內會認為讀取失敗。
        但有些MCU指令為了節省指令長度,會以HEX檔案格式傳輸,而不是以文字傳輸,舉例來說我要對MCU發送一個255 0xFF的訊息,如果傳輸byte格式只需要一個byte就可以完成,但如果以文字傳輸就需要FF兩個字元也就是兩個bytes才能完成。
        這樣的傳輸模式就不會以換行字元LF或其他特定的字元當作訊息,因為LF 0x0A需要拿來當作資料的訊息,不然會無法分辨0x0A是指令結束還是資料內容。
        我們將Write的部分用myCOM.IO.WriteString取代原本myCOM.Writestring,讀取部分也是從原本myCOM.ReadString改成myCOM.IO.ReadString。
    'Write
    Dim i As Integer
    For i = 1 To Len(out_str)
        myCOM.IO.WriteString Mid(out_str, i, 1)
    Next i
    'Read
    Sleep 10
    Dim buffer_count As Integer
    '確認RX Buffer上是否有值與多少個Bytes
    buffer_count = RS232.BytesAvailable
    in_str = myCOM.IO.ReadString(buffer_count)

    執行結果就可以正確HELLO沒有LF的字串。        
Send Any Byte Data

        但是這樣傳輸還是會遇到一個問題,myCOM.IO.WriteString的輸入資料是對照ASCII,所以有些字元傳送會發生問題,例如0x00空字元。

        這個問題在某一天晚上讓我花了不少時間,也是我想寫這一篇的原因,寫入部分用myCOM.IO.Write (bytes(),1)來傳送,傳送0, 8, 128, 255,主要關鍵在讀取的部分,因為傳送是用BYTE,所以讀取應該也是用Byte的資料格式讀取,但一開始用Byte無法得到正確的結果,後來發現要用Variant or String的格式才能正確把資料讀入,然後再用AscB()函數轉成Byte的資料

    Dim in_str As String
    Dim out_str As String
    Dim out_byte(3) As Byte
    Dim write_buf(0) As Byte
    Dim i As Integer
    Dim w_index As Long
    out_byte(0) = 0
    out_byte(1) = 8
    out_byte(2) = 128
    out_byte(3) = 255
    For i = 0 To UBound(out_byte)
        write_buf(0) = out_byte(i)
        w_index = myCOM.IO.Write(write_buf, 1)
    Next i
    'Read
    Sleep 20
    Dim buffer_count As Integer
    Dim read_bf As String
    Dim test_byte(10) As Byte
    Dim in_bytes() As Byte
    '確認RX Buffer上是否有值與多少個Bytes
    buffer_count = RS232.BytesAvailable
    ReDim in_bytes(buffer_count)
    For i = 1 To buffer_count
        ' 讀取資料必須要為String or Variant
        read_bf = myCOM.IO.Read(1)
        'ASCB將字元轉回Byte
        in_bytes(i) = AscB(read_bf)
        in_str = in_str & i & " = " & in_bytes(i) & " : "
    Next i
    MsgBox in_str
 
         執行結果傳送0x00, 0x10, 0x80, 0xFF四個bytes,然後正確讀取顯示。
        Serial Port (UART)現在都還蠻常見的,很多程式語言也都有支援,但大多時候不需要太複雜的自動控制,要同時控制儀器,UART Device與數據處理,Excel VBA是個很好的工具,因為Excel內建很多實用切熟悉的函數可以使用,而且每個欄位都是一個變數空間,而且電腦幾乎都有Excel可以針對不同的需求直接修改內容,另外如果對Excel VBA儀器控制有興趣也可以參考[7]Keysight的網站也有很多相關資源。

Reference

完整程式碼

'Copy and Paste Below Code to your Excel
Private Sub CommandButton3_Click()

    Dim myCOM As VisaComLib.FormattedIO488
    Dim rm_myCOM As VisaComLib.ResourceManager
    
    Set myCOM = New VisaComLib.FormattedIO488
    Set rm_myCOM = New VisaComLib.ResourceManager
    Set myCOM.IO = rm_myCOM.Open("ASRL4::INSTR")
    
    Dim RS232 As ISerial
    Set RS232 = myCOM.IO
    
    
    RS232.BaudRate = 9600
    RS232.FlowControl = ASRL_FLOW_NONE

    myCOM.IO.TerminationCharacter = 10
    myCOM.IO.TerminationCharacterEnabled = True
    Dim in_str As String
    Dim out_str As String
    Dim out_byte(3) As Byte
    Dim write_buf(0) As Byte
    Dim i As Integer
    Dim w_index As Long
    out_byte(0) = 0
    out_byte(1) = 8
    out_byte(2) = 128
    out_byte(3) = 255
    For i = 0 To UBound(out_byte)
        write_buf(0) = out_byte(i)
        w_index = myCOM.IO.Write(write_buf, 1)
    Next i
    'Read
    Sleep 20
    Dim buffer_count As Integer
    Dim read_bf As String
    Dim test_byte(10) As Byte
    Dim in_bytes() As Byte
    '確認RX Buffer上是否有值與多少個Bytes
    buffer_count = RS232.BytesAvailable
    ReDim in_bytes(buffer_count)
    For i = 1 To buffer_count
        ' 讀取資料必須要為String or Variant
        read_bf = myCOM.IO.Read(1)
        'ASCB將字元轉回Byte
        in_bytes(i) = AscB(read_bf)
        in_str = in_str & i & " = " & in_bytes(i) & " : "
    Next i
    MsgBox in_str
    RS232.Close

End Sub

熱門文章