主要内容

与I2C设备通信并使用数字IO分析总线信号

与协议层以及物理层的仪器和设备进行通信。利用仪器控制工具箱的I2C特性与TMP102温度传感器通信,同时利用数据采集工具箱的时钟数字IO特性分析物理层I2C总线通信。

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

硬件配置和原理图

  • 任何得到万博1manbetx支持的国家文书™ 可以使用带有时钟DIO通道的DAQ设备(例如,NI Elvis II)

  • TotalPhase Aardvark I2C/SPI主机适配器

  • TMP102带双线串行接口的数字温度传感器

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

其他选项包括:

  • 使用外部电源。

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

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

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

aa = instrhwinfo (“i2c”,“土豚”);%获取有关已连接I2C主机的信息tmp102=i2c(“土豚”,0,hex2dec('48'));%创建一个I2C对象以连接到TMP102tmp102.PullupResistors=“两个”;%使用主机适配器上拉电阻器fopen (tmp102);%打开连接数据8=fread(tmp102,2,‘uint8’);%读取2字节数据%一个LSB等于0.0625摄氏度温度=...(双精度(位移位(int16(data8(1)),4))+...双(位移位(int16(data8(2)),-4))*0.0625;%根据收到的数据计算温度,请参阅TMP102数据表fprintf(TMP102传感器记录的温度为:%s deg. C\n',num2str(温度);fclose(tmp102);
TMP102传感器记录的温度为:27.625℃

使用DAQ设备获取相应的I2C物理层信号

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

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

dd =采集(“倪”);附加输入(dd,“Dev4”,“端口0\line0”,“数字”);%sda附加输入(dd,“Dev4”,“端口0\line1”,“数字”);%症状自评量表

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

NI DAQ设备上的数字子系统没有自己的时钟;它们必须与模拟子系统共享时钟或从外部子系统导入时钟。使用PulseGeneration计数器输出,并将输入扫描速率设置为匹配。

pgChan=添加输出(dd,“Dev4”,“ctr1”),“脉冲发生器”);dd.Rate = 1 e6;pgChan。频率= dd.Rate;

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

disp (pgChan.Terminal);addclock (dd,“扫描时钟”,“外部”,[“Dev4/”pgChan.Terminal]);
PFI13

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

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

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

  • 启动I2C操作

  • I2C操作完成后,停止DataAcquisition

开始(dd,“连续的”);fopen(tmp102);data8=fread(tmp102,2,“uint8”);%一个LSB等于0.0625摄氏度温度= (double(bitshift(int16(data8(1)), 4)) +...double(位移位(int16(data8(2)),-4)))*0.0625;fclose(tmp102);pause(0.1);stop(dd);myData=read(dd,“全部”);
警告:触发器和时钟不会影响计数器输出通道。

绘制原始数据以查看采集的信号。请注意,在空闲期间,线路保持在高位。下一节说明如何找到启动/停止条件位,并使用它们隔离I2C通信中的相关区域。

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

分析I2C物理层总线通信

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

sda=myData(:,1');scl=myData(:,2');

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

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

从时钟指数计算时钟周期

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

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

idlePeriodIndices=查找(SClPeriods>100);

放大I2C总线上的第一个活动周期。为了便于查看,在每个绘图的前端和末尾包含30个空闲活动示例。

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

分析总线性能指标

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

  • 启动条件持续时间定义为SDA降低后SCL降低所需的时间。

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

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

启动条件:先SDA低,再SCL低

sclLowIndex=SCLLIPINDEX(idlePeriodIndices(1));sdaLowIndex=find(sda(1:sclLowIndex)=1,1,“最后一次”) + 1;%+1,翻转是上一个高点之后的下一个值startConditionDuration=(SCLowIndex-SDLowIndex)*1/s.速率;fprintf(“sda: % s \ n”sprintf (“%d”,sda(sdaLowIndex-1:sclowindex));%索引指向下一个变化,因此sclLowIndex包括翻转到低fprintf('scl:%s\n'sprintf (“%d”sci (sdaLowIndex-1: sclLowIndex)));%从sdaLowIndex中减去1以查看翻转前的sda值fprintf('开始条件持续时间:%d秒。\n\n',开始条件持续时间);%数5次脉冲,5次脉冲。
sda:100 scl:10开始条件持续时间:5.000000e-06秒。

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

%在进入空闲状态之前翻转是我们想要的sclHighIndex = sclFlipIndexes (idlePeriodIndices (2) 1);sdaHighIndex = sda(sclHighIndex:end)== 1,1,“第一”) + sclHighIndex - 1;stopConditionDuration = (sdaHighIndex - sclHighIndex) * 1/s.Rate;fprintf(“sda: % s \ n”sprintf (“%d”sda (sclHighIndex-1: sdaHighIndex)));fprintf('scl:%s\n'sprintf (“%d”,scl(sclHighIndex-1:sdahightindex));fprintf('停止条件持续时间:%d秒\n\n', stopConditionDuration);
sda:0 0 0 0 1 scl:0 1 1停止条件持续时间:5.000000e-06秒。

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

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

通过在上升沿上采样来查找位流

这个sclFlipIndexes矢量是使用XOR创建的,因此包含上升沿和下降沿。从上升沿开始,使用两步跳过下降沿。

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

解码获得的比特流

ADR_RW={' W ',“R”};ACK_NACK = {“确认”,“NACK”};地址=比特流(1:7);%7位地址fprintf(“\ nDecoded地址:% d % d % d % d % d % d % d (0 x % 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=bitStream(startBit:endBit);fprintf('已解码数据%d:%s(0x%s)%d(%s)\n',...iData+1,...sprintf (“%d”数据),...binaryVectorToHex(数据),...比特流(ackBit),...ACK_NACK{比特流(ackBit)+1});结束
Decoded Address: 1001000(0x48) 1(R) 0(ACK) Decoded Data1: 00011011(0x1B) 0(ACK) Decoded Data2: 10100000(0xA0) 1(NACK)

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

uint8字节被读取,使用从文件中读,从I2C总线输入变量数据8.这些值的十六进制转换应与上述总线解码结果相匹配。

fprintf('从I2C对象获取的数据:0x%s\n'dec2hex (data8) ');fprintf(温度:%2.2f deg. C\n\n'温度);
从I2C对象获取的数据:0x1BA0温度:27.63摄氏度