接客周·程式設計養生·帶薪休假與菊部養生

qq_15084199發表於2020-10-20
餘大師偶爾會跟我探討起光模組測試板介面晶片CH341A不穩定,會導致ATE軟體異常。換一顆新的USB轉I2C介面晶片,剛需,拖久了就成了一種夙願。 困擾了我整整兩個工作日再加一個雙休日的基於MCP2221的USB轉I2C的DLL呼叫問題終於在週一得到破解。我已經用最常用的隱式呼叫DLL的方式,成功呼叫《mcp2221_dll_um_x86.dll》中尚還可用的2個函式,實現了對光模組EEPROM的I2C隨機讀/寫訪問(即指定暫存器偏移量的I2C匯流排操作)。原廠提供的這個V2.2.1 DLL的奇葩之處在於,部分函式比如Mcp2221_GetLastError()在原始碼編譯時就會報錯missing prototype即匯入庫lib檔案中壓根兒就沒有這個函式入口,另外還有部分函式比如Mcp2221_SmbusBlockWrite()和Mcp2221_SmbusBlockRead()總是返回錯誤程式碼但我曾用示波器看過SMB讀時序又不像有問題的樣子,至少下位機該ACK時都正常響應了的。 要理解我對MCP2221晶片的奇葩DLL的破解之道,需要首先從EEPROM的I2C時序說起。和溫度感測器這類從I2C裝置不一樣,EEPROM是有暫存器偏移量概念的。打個比方,從溫度感測器回讀當前取樣的溫度值,那麼我們直接去成都模具園D2棟的迪譜光電隨便找個有問必答的銷售人員問一下就可以,但是要訪問EEPROM的各個暫存器,就像菜地裡的一個蘿蔔一個坑,每個坑位都是順序編號的,哪個序號坑位的蘿蔔有多重,是需要事先指定好坑位的,這個坑位序號,就是暫存器偏移量,即偏離零點的距離,範圍0~255。由於EEPROM暫存器定址是8bit的,這意味著一片菜地(比如AT24C02)可以有2^8即256個蘿蔔(Byte),這一片菜地以A0h作為I2C從裝置寫地址,以A1h作為I2C從裝置讀地址。當然一個菜園(比如AT24C04)可以有2片菜地,第一片菜地以A0h作為寫地址,以A1h作為讀地址,第二片菜地以A2h作為寫地址,以A3h作為讀地址。 讓我們來先看看典型的EEPROM型號為AT24C02的datasheet,對EEPROM的一些I2C專業術語的定義: (……此處省略1800字) 既然能指定坑位號的Mcp2221_Smbusxxx函式不好用,那我們就用不能指定坑位號但幸好還能正常使用的Mcp2221_I2cxxx函式來自己搭建合適的蔬菜大棚!我最開始還不是這樣想的,我想退回到基於2014年的老版本DLL的上位機C#例程《MCP2221DLL_2015_06_17\Unmanaged\Example Code\MCP2221DLL-UM_CSExampleCode》,來嘗試突破。很遺憾,C#我不熟,CVI顯式呼叫DLL我也沒整成功,反而是看到2014年的老版本DLL的SmbReadBlock函式,比SmbWriteBlock函式,多了一個引數是BYTE readRegIndex。我就在想,為啥SMB的block讀函式,就要指定坑位號,而SMB的block寫函式,就可以不指定坑位號呢?當時似乎是靈光乍現,回頭去看page write時序,實際上坑位號和蘿蔔並沒有本質不同,都是前後緊挨著的8bit資料,所以SMB的block寫函式,發出的第一個8bit資料,完全可以當成是坑位號告訴給菜地嘛,所以才可以不像SMB的block讀函式那樣需要明示坑位號。於是乎,Mcp2221_I2cWrite的unsigned char* i2cTxData引數,第一個8bit資料,我就讓它等於坑位號;從第二個8bit到第x+1個8bit,我就給它順序賦值成x個新蘿蔔,這樣一旦執行完一次Mcp2221_I2cWrite函式,就相當於是指定了坑位號再種下了x個新蘿蔔,完美! 再回頭去看Sequential Read時序,需要先寫坑位號再發起立即讀x個老蘿蔔的操作,於是可以先呼叫Mcp2221_I2cWrite函式只發出1個byte當成是坑位號,然後再呼叫Mcp2221_I2cRead函式連續挖出x個老蘿蔔即可。注意這裡的Mcp2221_I2cWrite函式會發出一個STOP結尾,但是標準的讀時序是沒有這個STOP的,不過實踐證實,Re-START前面有沒有這個額外的STOP,下位機都可以正常應對。本來我是準備呼叫Mcp2221_I2cWriteNoStop這個函式來下發不帶額外STOP的坑位號的,但是執行完這個函式SCL就一直拖低,沒法進行下一步操作,就是這個奇葩DLL倒逼著我這個老工程師,連蒙帶猜地採取迂迴戰術才得以成功訪問到EEPROM,一旦想通了I2C時序原理,連線下來的上廁所蹲坑都暢快多了,畢竟這也算是碎片式帶薪休假與菊部養生嘛。

相關文章