Свяжитесь с устройствами I2C и анализируйте сигналы шины Используя цифровой IO

Свяжитесь с инструментами и устройствами на слое протокола, а также физическом уровне. Используйте функцию 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.

Свяжите с датчиком TMP102 Используя хост-адаптер I2C и считанные температурные данные

Обеспечьте электричеством датчик и проверьте коммуникацию к нему с помощью объекта 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

Получите соответствующие сигналы физического уровня I2C Используя устройство DAQ

Используйте сверхдискретизированный, синхронизировал цифровые каналы от 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

Получите сигналы I2C Используя синхронизированные цифровые каналы

Получите данные в фоновом режиме из 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

Извлеките сигналы физического уровня 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)

Проверьте, что декодируемые данные Используя DAQ совпадают с чтением данных Используя ICT

Два 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