主要内容

利用数字IO与I2C器件通信,分析总线信号

在协议层和物理层与仪表和设备通信。使用仪表控制工具箱的I2C功能与TMP102温度传感器通信,同时使用数据采集工具箱的时钟数字IO功能分析物理层I2C总线通信。

需要数据采集工具箱和仪表控制工具箱。

硬件配置及原理图

  • 任何支持金宝app的国家仪器™DAQ设备与时钟DIO通道都可以使用(例如,NI Elvis II)

  • TotalPhase Aardvark I2C/SPI主机适配器

  • TMP102数字温度传感器,两线串行接口

TMP102需要3.3 V电源。使用线性LDO (LP2950-33)从DAQ设备的5v电源线生成3.3 V电源。

备选方案包括:

  • 使用外部电源。

  • 使用DAQ设备的模拟输出通道。

使用I2C主机适配器连接到TMP102传感器并读取温度数据

连接传感器并使用仪表控制工具箱中的I2C对象验证与传感器的通信。

aa=instrhwinfo(“i2c”,“豚”);%获取已连接的I2C主机信息tmp102 = i2c (“豚”0 hex2dec (48岁的));%创建连接到TMP102的I2C对象tmp102。PullupResistors =“都是”%使用主适配器上拉电阻fopen(tmp102);%打开连接dat8 = fread(tmp102, 2,)“uint8”);%读取2字节数据% 1 LSB = 0.0625℃温度=...(双(bitshift (int16 (data8 (1)), 4)) +...Double (bitshift(int16(data8(2)), -4)) * 0.0625;%参考TMP102数据表,根据接收到的数据计算温度流('TMP102传感器记录的温度为:%s摄氏度\n'num2str(温度));文件关闭(tmp102);
TMP102传感器记录的温度为:27.625℃

通过DAQ设备获取相应的I2C物理层信号

使用NI Elvis的过采样时钟数字通道(Dev4)获取和分析I2C总线上的物理层通信。

在数据采集设备的0行0端口上获取SDA数据。在DAQ设备的第1行端口0上获取SCL数据。

dd=数据采集卡(“倪”);附加输入(dd,“Dev4”,“port0 \ line0”,“数字”);% sdaaddinput (dd,“Dev4”,“port0 \ line1”,“数字”);% sci

生成用于数字子系统的时钟信号

NI DAQ设备上的数字子系统没有自己的时钟;它们必须与模拟子系统共享时钟,或者从外部子系统导入时钟。产生50%占空比时钟在1 MHz使用脉冲发生器计数器输出,并设置输入扫描速率与之匹配。

pgChan = addoutput (dd,“Dev4”,“ctr1”),“PulseGeneration”);折旧率=1e6;折旧频率=折旧率;

时钟在pgChan上生成。终端引脚,允许与其他设备同步,并在示波器上查看时钟。计数器输出脉冲信号作为时钟信号输入。

显示(pgChan.终端);添加时钟(dd,“ScanClock”,“外部”, (“Dev4 /”pgChan.Terminal]);
PFI13

利用时钟数字通道获取I2C信号

在后台从SDA和SCL数字线路获取数据。

  • 在后台模式下启动数据采集

  • 启动I2C操作

  • I2C操作完成后停止数据采集

开始(dd,“连续”);fopen (tmp102);dat8 = fread(tmp102, 2,)“uint8”);% 1 LSB = 0.0625℃温度=(双精度(位移位(int16(data8(1)),4))+...Double (bitshift(int16(data8(2)), -4)) * 0.0625;文件关闭(tmp102);暂停(0.1);停止(dd);myData =阅读(dd,“所有”);
警告:触发器和时钟不会影响计数器输出通道。

绘制原始数据以查看获得的信号。请注意,在空闲期间,线路处于高位。下一节将展示如何找到启动/停止条件位,并使用它们来隔离I2C通信中感兴趣的区域。

身材(“名称”,“原始数据”);次要情节(2,1,1);情节(myData (: 1));ylim ([-0.2, 1.2]);甘氨胆酸ax =;斧子。YTick = [0, 1];斧子。YTickLabel = {“低”,“高”};头衔(“串行数据(SDA)”);子批次(2,1,2);绘图(myData(:,2));ylim([-0.2,1.2]);ax=gca;ax.YTick=[0,1];ax.YTickLabel={“低”,“高”};头衔(“串行时钟(sci)”);

分析I2C物理层总线通信

提取SDA和SCL线路上的I2C物理层信号。

sda = myData(: 1)”;sci = myData (:, 2) ';

找到所有上升和下降的时钟边缘。

sclFlips = xor(scl(1:end-1), scl(2:end));sclFlips = [1 sclFlips 1];sclFlipIndexes =找到(sclFlips = = 1);

根据时钟索引计算时钟周期

sclFlipPeriods = sclFlipIndexes(1:end-1) -[1 sclFlipIndexes(1:end-1)];

通过检查,观察到闲置期间SCL高超过100 us。由于扫描速率= 1MS/s,每个样品代表1个us。idlePeriodIndices表示I2C通信中活动的周期。

idlePeriodIndices =找到(sclFlipPeriods > 100);

放大I2C总线上的第一个活动周期。为了便于查看,在每个地块的前端和末端包括30个空闲活动的样本。

范围1=sclFlipIndexes(idlePeriodIndices(1))-30:sclFlipIndexes(idlePeriodIndices(2)-1)+30;图(“名称”,“I2C通信数据”);子批次(2,1,1);绘图(sda(范围1));ylim([-0.2,1.2]);ax=gca;ax.YTick=[0,1];ax.YTickLabel={“低”,“高”};头衔(“串行数据(SDA)”);次要情节(2,1,2);情节(sci (range1));ylim ([-0.2, 1.2]);甘氨胆酸ax =;斧子。YTick = [0, 1];斧子。YTickLabel = {“低”,“高”};头衔(“串行时钟(sci)”);

分析总线性能指标

作为一个简单的例子,分析启动和停止条件度量,以及I2C比特率计算。

  • 开始条件持续时间定义为SDA变低后SCL变低所需的时间。

  • 停止条件持续时间定义为SCL升高后SDA升高所需的时间。

  • 通过取两个上升时钟边缘之间的时间倒数来计算比特率。

启动条件:先是SDA低,然后是SCL低

sclLowIndex = sclFlipIndexes (idlePeriodIndices (1));sdaLowIndex = find(sda(1:sclLowIndex)== 1,1,“最后”) + 1;% +1,翻转是最后一个高之后的下一个值startConditionDuration = (sclLowIndex - sdaLowIndex) * 1/s.Rate;流('sda:%s\n',斯普林特(' % d 'sda (sdaLowIndex-1: sclLowIndex)));%索引指向下一个更改,因此sclLowIndex包含flip to low流(“sci: % s \ n”,斯普林特(' % d '、症状自评量表(sdaLowIndex-1:sclLowIndex));%从sdaLowIndex中减去1,以查看翻转前的sda值流('启动条件持续时间:%d秒\n\n', startConditionDuration);%数5个脉冲,5个美国。
sda: 1 00000 scl: 1 1 1 1 1 1 0启动条件持续时间:5.000000e-06秒。

停止条件:先是SCL高,然后是SDA高

进入idle之前的% flip是我们想要的sclHighIndex=sclFlipIndexes(idlePeriodIndices(2)-1);sdahightIndex=find(sda(sclHighIndex:end)=1,1,“第一”)+SCLHIGHTINDEX-1;停止条件持续时间=(SDAHIGHTINDEX-SCLHIGHTINDEX)*1/s.速率;fprintf('sda:%s\n',斯普林特(' % d ',sda(sclHighIndex-1:sdahightindex));fprintf(“sci: % s \ n”,斯普林特(' % d 'sci (sclHighIndex-1: sdaHighIndex)));流('停止条件持续时间:%d秒。\n\n',持续时间);
sda: 00000 1 scl: 0 1 1 1 1 1 1停止条件持续时间:5.000000e-06秒

比特率:SCL线上两个上升边之间的时间的倒数

startConditionIndex = idlePeriodIndices (1);firstRisingClockIndex = startConditionIndex + 2;second drisingclockindex = firstRisingClockIndex + 2;clockPeriodInSamples = sclFlipIndexes(secondRisingClockIndex) - sclFlipIndexes(firstRisingClockIndex);clockPeriodInSeconds = clockPeriodInSamples * 1/s rate;比特率= 1 / clockPeriodInSeconds;流('DAQ计算比特率=%d;实际I2C对象比特率=%dKHz\n',...比特率,...tmp102.BitRate);
DAQ计算比特率= 1.000000e+05;实际I2C对象比特率= 100KHz

通过对上升边采样找到比特流

SCL索引矢量是使用异或创建的,因此包含上升和下降边缘。从上升边开始,用两步跳过下降边。

% idlePeriodIndices(1)+1是启动条件后的第一个上升时钟边。用两步跳过下降边,只看上升边。% idlePeriodIndices(2)-1是停止条件上升沿的索引。%idlePeriodIndices(2)-3是要删除的位流中的最后一个上升时钟边缘%解码。比特流= sda (sclFlipIndexes (idlePeriodIndices (1) + 1:2: idlePeriodIndices (2) 3));流('从I2C物理层信号提取的原始比特流:%s\n\n',斯普林特(' % d ',比特流);
从I2C物理层信号提取的原始比特流:1 0 0 1 0 0 1 0 0 1 1 0 1 1 0 1 1 0 1 0 0 1

对获取的比特流进行解码

ADR_RW = {“W”,“R”};确认NACK={“消”,“纳”};地址=比特流(1:7);% 7位地址流(“\n编码地址:%d%d%d%d%d%d(0x%s)%d(%s)%d(%s)\n”,...地址,...binaryVectorToHex(地址),...比特流(8),...ADR_RW{比特流(8)+1},...比特流(9),...ACK_NACK{比特流(9)+1});对于iData = 0:1 startBit = 10 + iData*9;endBit = startBit + 7;ackBit = endBit + 1;data =比特流(startBit: endBit);流('解码数据%d: %s(0x%s) %d(%s)\n',...iData + 1,...斯普林特(' % d ',数据),...binaryVectorToHex(数据),...比特流(ackBit),...ACK_NACK{比特流(ackBit) + 1});终止
解码地址:1001000(0x48)1(R)0(ACK)解码数据1:0001011(0x1B)0(ACK)解码数据2:10100000(0xA0)1(NACK)

验证使用DAQ解码的数据与使用ICT读取的数据是否匹配

两个uint8字节被读取,使用弗瑞德,从I2C总线到变量data8.这些值的十六进制转换应该与上面显示的总线解码的结果相匹配。

流('从I2C对象获取的数据:0x%s\n',dec2hex(数据8)';fprintf('温度:%2.2f摄氏度\n\n'、温度);
从I2C对象获取的数据:0x1BA0温度:27.63℃