接客周·编程养生·带薪休假与菊部养生

it2023-01-22  53

余大师偶尔会跟我探讨起光模块测试板接口芯片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时序原理,连接下来的上厕所蹲坑都畅快多了,毕竟这也算是碎片式带薪休假与菊部养生嘛。
最新回复(0)