主要内容

利用数字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);%打开连接data8 = fread(tmp102,2,“uint8”);%读取2字节数据%一个LSB​​等于0.0625°。C温度=...(双(bitshift (int16 (data8 (1)), 4)) +...双(BitsHift(INT16(DATA8(2)),-4)))* 0.0625;%根据收到的数据计算温度,请参阅TMP102数据表流(TMP102传感器记录的温度为:%s deg. C\n'num2str(温度));文件关闭(tmp102);
TMP102传感器记录的温度为:27.625℃

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

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

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

dd =采集(“倪”);addinput (dd,“dev4”“port0 \ line0”“数字”);%SDA.addinput (dd,“dev4”“port0 \ line1”“数字”);%scl.

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

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

pgchan = addoutput(dd,“dev4”“ctr1”),“脉冲变量”);dd.Rate = 1 e6;pgChan。频率= dd.Rate;

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

disp (pgChan.Terminal);addclock (dd,“ScanClock”“外部”,[“dev4 /”pgChan.Terminal]);
PFI13

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

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

  • 在后台模式下启动DataAcquisition

  • 启动I2C操作

  • I2C操作完成后,停止DataAcquisition

开始(dd,“连续”);fopen (tmp102);data8 = fread(tmp102,2,“uint8”);%一个LSB​​等于0.0625°。C温度= (double(bitshift(int16(data8(1)), 4)) +...双(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 =;ax.ytick = [0,1];ax.yticklabel = {“低”“高”};标题(“串行数据(SDA)”);次要情节(2,1,2);情节(myData (:, 2));ylim ([-0.2, 1.2]);甘氨胆酸ax =;ax.ytick = [0,1];ax.yticklabel = {“低”“高”};标题(“串行时钟(sci)”);

分析I2C物理层总线通信

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

sda = mydata(:,1)';scl = 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个空闲活动样本包含在每个图的前部和末端。

range1 = sclFlipIndexes(idlePeriodIndices(1)) - 30: sclFlipIndexes(idlePeriodIndices(2) - 1) + 30;图(“姓名”“I2C通信数据”);次要情节(2,1,1);情节(sda (range1));ylim ([-0.2, 1.2]);甘氨胆酸ax =;ax.ytick = [0,1];ax.yticklabel = {“低”“高”};标题(“串行数据(SDA)”);次要情节(2,1,2);情节(sci (range1));ylim ([-0.2, 1.2]);甘氨胆酸ax =;ax.ytick = [0,1];ax.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”sprintf ('%d',SDA(SdalowIndex-1:ScllowIndex))));%索引指向下一个变化,因此sclLowIndex包括翻转到低流('scl:%s \ n'sprintf ('%d'sci (sdaLowIndex-1: sclLowIndex)));%从sdaLowIndex中减去1以查看翻转前的sda值流('开始条件持续时间:%d sec。\ 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);sdaHighIndex = sda(sclHighIndex:end)== 1,1,“第一”) + sclHighIndex - 1;stopConditionDuration = (sdaHighIndex - sclHighIndex) * 1/s.Rate;流(“sda: % s \ n”sprintf ('%d'sda (sclHighIndex-1: sdaHighIndex)));流('scl:%s \ n'sprintf ('%d'sci (sclHighIndex-1: sdaHighIndex)));流('停止条件持续时间:%d秒\n\n', stopConditionDuration);
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

通过在上升沿上采样找到比特流

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

% idlePeriodIndices(1)+1是启动条件后的第一个上升时钟边。用两步跳过下降边,只看上升边。%IdlePiodIndics(2)-1是停止条件的上升沿的索引。% idlePeriodIndices(2)-3是位流中最后一个上升的时钟边缘%解码。Bitstream = SDA(SCLFLIPINDEXES(IDLEPEIDINDICES(1)+1:2:IDLEPEIDINDICES(2)-3));流('从I2C物理层信号提取的原始比特流:%s\n\n'sprintf ('%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”};ACK_NACK = {'ACK'“纳”};地址=比特流(1:7);%7位地址流(“\ 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 =比特流(startBit: endBit);流('解码数据%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总线进入变量data8.这些值的十六进制转换应该与上面显示的总线解码的结果相匹配。

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