Свяжитесь с инструментами и устройствами на слое протокола, а также физическом уровне. Используйте функцию I2C Instrument Control Toolbox, чтобы связаться с датчиком температуры TMP102, и одновременно анализировать коммуникации шины физического уровня I2C, использующие синхронизированную цифровую функцию IO Data Acquisition Toolbox.
Data Acquisition Toolbox и Instrument Control Toolbox требуются.
Любое поддерживаемое Национальное устройство DAQ Instruments™ с синхронизированными каналами DIO может использоваться (e.g., NI Элвис II)
Муравьед TotalPhase Хост-адаптер I2C/SPI
TMP102 Цифровой Датчик температуры с двухпроводным последовательным интерфейсом
TMP102 требует 3,3-вольтового предоставления. Используйте линейный LDO (LP2950-33), чтобы сгенерировать 3,3-вольтовое предоставление от линии предоставления 5 В устройства DAQ.
Альтернативные опции включают:
Используйте внешний источник питания.
Используйте канал аналогового выхода от своего устройства DAQ.
Обеспечьте электричеством датчик и проверьте коммуникацию к нему с помощью объекта I2C из Instrument Control Toolbox.
aa = instrhwinfo('i2c', 'aardvark'); % Get information about connected I2C hosts tmp102 = i2c('aardvark',0,hex2dec('48')); % Create an I2C object to connect to the TMP102 tmp102.PullupResistors = 'both'; % Use host adaptor pull-up resistors fopen(tmp102); % Open the connection data8 = fread(tmp102, 2, 'uint8'); % Read 2 byte data % One LSB equals 0.0625 deg. C temperature = ... (double(bitshift(int16(data8(1)), 4)) +... double(bitshift(int16(data8(2)), -4))) * 0.0625; % Refer to TMP102 data sheet to calculate temperature from received data fprintf('The temperature recorded by the TMP102 sensor is: %s deg. C\n',num2str(temperature)); fclose(tmp102);
The temperature recorded by the TMP102 sensor is: 27.625 deg. C
Используйте сверхдискретизированный, синхронизировал цифровые каналы от NI Элвис (Dev4
) получать и анализировать коммуникации физического уровня на шине I2C.
Получите данные SDA по порту 0, линия 0 из вашего устройства DAQ. Получите данные SCL по порту 0, линия 1 из вашего устройства DAQ.
dd = daq("ni"); addinput(dd,"Dev4","port0\line0","Digital"); % sda addinput(dd,"Dev4","port0\line1","Digital"); % scl
Цифровые подсистемы на устройствах DAQ NI не имеют своих собственных часов; они должны совместно использовать часы с аналоговой подсистемой или импортировать часы из внешней подсистемы. Сгенерируйте 50%-е часы рабочего цикла на уровне 1 МГц с помощью PulseGeneration
встречный выход и набор входная частота развертки, чтобы соответствовать.
pgChan = addoutput(dd,"Dev4","ctr1"),"PulseGeneration"); dd.Rate = 1e6; pgChan.Frequency = dd.Rate;
Часы сгенерированы на контакте 'pgChan.Terminal', позволив синхронизацию с другими устройствами и просмотрев часы на осциллографе. Встречный выходной сигнал импульса импортируется как сигнал часов.
disp(pgChan.Terminal); addclock(dd,"ScanClock","External",["Dev4/" pgChan.Terminal]);
PFI13
Получите данные в фоновом режиме из SDA и цифровых линий SCL.
Запустите DataAcquisition в фоновом режиме
Запустите операции I2C
Остановитесь операции DataAcquisition after I2C завершены
start(dd, "continuous"); fopen(tmp102); data8 = fread(tmp102, 2, "uint8"); % One LSB equals 0.0625 deg. C temperature = (double(bitshift(int16(data8(1)), 4)) +... double(bitshift(int16(data8(2)), -4))) * 0.0625; fclose(tmp102); pause(0.1); stop(dd); myData = read(dd, "all");
Warning: Triggers and Clocks will not affect counter output channels.
Отобразите необработанные данные на графике, чтобы видеть полученные сигналы. Заметьте, что линии сохранены высоко в течение периодов неактивности. Следующий раздел показывает, как найти запустить/остановить биты условия и использовать их, чтобы изолировать сферы интересов в коммуникации I2C.
figure("Name", "Raw Data"); subplot(2,1,1); plot(myData(:,1)); ylim([-0.2, 1.2]); ax = gca; ax.YTick = [0,1]; ax.YTickLabel = {'Low','High'}; title("Serial Data (SDA)"); subplot(2,1,2); plot(myData(:,2)); ylim([-0.2, 1.2]); ax = gca; ax.YTick = [0,1]; ax.YTickLabel = {'Low','High'}; title("Serial Clock (SCL)");
Извлеките сигналы физического уровня I2C на SDA и линиях SCL.
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 нас. Начиная с частоты развертки = 1MS/s, каждая выборка представляет 1 нас. idlePeriodIndices
укажите на периоды периодов действия в рамках коммуникации I2C.
idlePeriodIndices = find(sclFlipPeriods>100);
Масштабируйте в первый период действия на шине I2C. Для простоты просмотра включайте 30 выборок неактивного действия к передней стороне и концу каждого графика.
range1 = sclFlipIndexes(idlePeriodIndices(1)) - 30 : sclFlipIndexes(idlePeriodIndices(2) - 1) + 30; figure("Name", "I2C Communication Data"); subplot(2,1,1); plot(sda(range1)); ylim([-0.2, 1.2]); ax = gca; ax.YTick = [0,1]; ax.YTickLabel = {'Low','High'}; title("Serial Data (SDA)"); subplot(2,1,2); plot(scl(range1)); ylim([-0.2, 1.2]); ax = gca; ax.YTick = [0,1]; ax.YTickLabel = {'Low','High'}; title("Serial Clock (SCL)");
Как простой пример анализируют, запускают и останавливают метрики условия и вычисление битрейта I2C.
Запустите длительность условия задана как время, которое требуется для SCL, чтобы пойти низко после того, как SDA идет низко.
Остановитесь длительность условия задана как время, которое требуется для SDA, чтобы пойти высоко после того, как SCL идет высоко.
Битрейт вычисляется путем взятия инверсии времени между 2 возрастающими фронтами синхроимпульса.
Запустите Условие: Первый SDA низко, затем SCL низко
sclLowIndex = sclFlipIndexes(idlePeriodIndices(1)); sdaLowIndex = find(sda(1:sclLowIndex)==1, 1, "last") + 1; % +1, flip is next value after last high startConditionDuration = (sclLowIndex - sdaLowIndex) * 1/s.Rate; fprintf('sda: %s\n', sprintf('%d ', sda(sdaLowIndex-1:sclLowIndex))); % Indexes point to next change, hence sclLowIndex includes flip to low fprintf('scl: %s\n', sprintf('%d ', scl(sdaLowIndex-1:sclLowIndex))); % subtract 1 from sdaLowIndex to see sda value prior to flip fprintf('Start condition duration: %d sec.\n\n', startConditionDuration); % count 5 pulses, 5 us.
sda: 1 0 0 0 0 0 0 scl: 1 1 1 1 1 1 0 Start condition duration: 5.000000e-06 sec.
Условие остановки: Первый SCL высоко, затем SDA высоко
% flip prior to going into idle is the one we want sclHighIndex = sclFlipIndexes(idlePeriodIndices(2)-1); sdaHighIndex = find(sda(sclHighIndex:end)==1, 1, 'first') + 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:sdaHighIndex))); fprintf('Stop condition duration: %d sec.\n\n', stopConditionDuration);
sda: 0 0 0 0 0 0 1 scl: 0 1 1 1 1 1 1 Stop condition duration: 5.000000e-06 sec.
Битрейт: Инверсия времени между 2 возрастающими ребрами на линии SCL
startConditionIndex = idlePeriodIndices(1); firstRisingClockIndex = startConditionIndex + 2; secondRisingClockIndex = firstRisingClockIndex + 2; clockPeriodInSamples = sclFlipIndexes(secondRisingClockIndex) - sclFlipIndexes(firstRisingClockIndex); clockPeriodInSeconds = clockPeriodInSamples * 1/s.Rate; bitRate = 1/clockPeriodInSeconds; fprintf('DAQ calculated bit rate = %d; Actual I2C object bit rate = %dKHz\n', ... bitRate,... tmp102.BitRate);
DAQ calculated bit rate = 1.000000e+05; Actual I2C object bit rate = 100KHz
sclFlipIndexes
вектор был создан с помощью XOR и следовательно содержит и возрастающие и падающие ребра. Начните с возрастающего ребра и используйте шаг два, чтобы пропустить падающие ребра.
% idlePeriodIndices(1)+1 is first rising clock edge after start condition. % Use a step of two to skip falling edges and only look at rising edges. % idlePeriodIndices(2)-1 is the index of the rising edge of the stop condition. % idlePeriodIndices(2)-3 is the last rising clock edge in the bit stream to be % decoded. bitStream = sda(sclFlipIndexes(idlePeriodIndices(1)+1:2:idlePeriodIndices(2)-3)); fprintf('Raw bit stream extracted from I2C physical layer signal: %s\n\n', sprintf('%d ', bitStream));
Raw bit stream extracted from I2C physical layer signal: 1 0 0 1 0 0 0 1 0 0 0 0 1 1 0 1 1 0 1 0 1 0 0 0 0 0 1
ADR_RW = {'W', 'R'}; ACK_NACK = {'ACK', 'NACK'}; address = bitStream(1:7); % 7 bit address fprintf('\nDecoded Address: %d%d%d%d%d%d%d(0x%s) %d(%s) %d(%s)\n', ... address,... binaryVectorToHex(address),... bitStream(8),... ADR_RW{bitStream(8)+1},... bitStream(9),... ACK_NACK{bitStream(9)+1}); for iData = 0:1 startBit = 10 + iData*9; endBit = startBit + 7; ackBit = endBit + 1; data = bitStream(startBit:endBit); fprintf('Decoded Data%d: %s(0x%s) %d(%s)\n', ... iData+1,... sprintf('%d', data),... binaryVectorToHex(data),... bitStream(ackBit),... ACK_NACK{bitStream(ackBit)+1}); end
Decoded Address: 1001000(0x48) 1(R) 0(ACK) Decoded Data1: 00011011(0x1B) 0(ACK) Decoded Data2: 10100000(0xA0) 1(NACK)
Два uint8
байты были считаны, с помощью fread
, от I2C соединяют шиной в переменную data8
. Шестнадцатеричное преобразование этих значений должно соответствовать, результаты шины декодируют показанный выше.
fprintf('Data acquired from I2C object: 0x%s\n', dec2hex(data8)'); fprintf('Temperature: %2.2f deg. C\n\n', temperature);
Data acquired from I2C object: 0x1BA0 Temperature: 27.63 deg. C