主要内容

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

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

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

硬件配置和原理图

  • 任何支持的具有时钟DIO通道的国家仪器™DAQ设备都可以使用(例如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 = inthwinfo (“i2c”“豚”);获取已连接I2C主机的信息Tmp102 = i2c(“豚”0 hex2dec (48岁的));创建I2C对象连接到TMP102tmp102。PullupResistors =“两个”使用主机适配器上拉电阻fopen (tmp102);打开连接Data8 = fread(tmp102, 2,“uint8”);读取2字节数据一个LSB等于0.0625摄氏度温度=...(double(bitshift(int16(data8(1)), 4)) +...Double (bitshift(int16(data8(2)), -4))) * 0.0625;参考TMP102数据表根据收到的数据计算温度流(TMP102传感器记录的温度为:%s℃\nnum2str(温度));文件关闭(tmp102);
TMP102传感器记录的温度为:27.625℃

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

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

在您的DAQ设备的0号端口,第0行获取SDA数据。在DAQ设备的第1行0端口上获取SCL数据。

Dd = daq(“倪”);addinput (dd,“Dev4”“port0 \ line0”“数字”);% sdaaddinput (dd,“Dev4”“port0 \ line1”“数字”);% sci

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

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

pgChan = addoutput(dd,“Dev4”“ctr1”),“PulseGeneration”);dd.Rate = 1e6;pgChan。频率= dd.Rate;

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

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

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

在后台从SDA和标准及校正实验所数码线获取数据。

  • 在后台模式启动DataAcquisition

  • 启动I2C操作

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

开始(dd,“连续”);fopen (tmp102);Data8 = fread(tmp102, 2,“uint8”);一个LSB等于0.0625摄氏度温度= (double(bitshift(int16(data8(1)), 4)) +...Double (bitshift(int16(data8(2)), -4))) * 0.0625;文件关闭(tmp102);暂停(0.1);停止(dd);myData = read(dd,“所有”);
警告:触发器和时钟不会影响计数器输出通道。

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

图(“名称”“原始数据”);次要情节(2,1,1);情节(myData (: 1));ylim ([-0.2, 1.2]);Ax = gca;斧子。YTick = [0,1];斧子。YTickLabel = {“低”“高”};标题(“串行数据(SDA)”);次要情节(2,1,2);情节(myData (:, 2));ylim ([-0.2, 1.2]);Ax = gca;斧子。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(sclflipps ==1);

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

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

通过检查,观察到闲置期间SCL已高超过100 us。因为扫描速率= 1MS/s,所以每个样本代表1个我们。idlePeriodIndices表示I2C通信中的活动周期。

idlePeriodIndices = find(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 = gca;斧子。YTick = [0,1];斧子。YTickLabel = {“低”“高”};标题(“串行数据(SDA)”);次要情节(2,1,2);情节(sci (range1));ylim ([-0.2, 1.2]);Ax = gca;斧子。YTick = [0,1];斧子。YTickLabel = {“低”“高”};标题(“串行时钟(SCL)”);

分析总线性能指标

作为一个简单的例子,分析了启动和停止条件度量,以及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包含翻转到低流(“sci: % s \ n”sprintf (' % d 'sci (sdaLowIndex-1: sclLowIndex)));%从sdaLowIndex减去1,以查看在翻转之前的sda值流('启动条件持续时间:%d秒。\n\n', startConditionDuration);%计数5次脉冲,5个我们。
sda: 1 000000 scl: 1 1 1 1 1 1 0启动条件持续时间:5.000000e-06 sec。

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

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

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

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

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

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

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

解码获得的比特流

Adr_rw = {' W '“R”};Ack_nack = {“消”“纳”};地址= bitStream(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});startBit = 10 + iData*9;endBit = startBit + 7;ackBit = endBit + 1;data = bitStream(startBit:endBit);流('已解码数据%d: %s(0x%s) %d(%s)\n'...iData + 1,...sprintf (' % d '数据),...binaryVectorToHex(数据),...比特流(ackBit),...ACK_NACK{比特流(ackBit) + 1});结束
解码的数据a1: 00011011(0x1B) 0(ACK)解码的数据a2: 10100000(0xA0) 1(NACK)

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

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

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