exponenta event banner

Взаимодействие с I2C устройствами и анализ сигналов шины с помощью цифрового ввода-вывода

Взаимодействие с приборами и устройствами на уровне протокола, а также на физическом уровне. Используйте функцию I2C панели инструментов управления приборами для связи с датчиком температуры TMP102 и одновременно проанализируйте связь физического уровня I2C шины с помощью функции цифрового ввода-вывода с синхронизацией панели инструментов сбора данных.

Требуются инструментарий сбора данных и инструментарий управления приборами.

Конфигурация оборудования и схема

  • Может использоваться любое поддерживаемое устройство National Instruments™ DAQ с синхронизированными каналами DIO (например, NI Elvis II)

  • Адаптер хоста TotalPhase Aardvark I2C/SPI

  • TMP102 Цифровой датчик температуры с двухпроводным последовательным интерфейсом

Для TMP102 требуется источник питания 3,3 В. Используйте линейный LDO (LP2950-33) для генерации питания 3,3 В из линии питания 5 В устройства DAQ.

Альтернативные варианты включают в себя:

  • Используйте внешний источник питания.

  • Используйте аналоговый выходной канал устройства DAQ.

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

Подключите датчик и проверьте связь с ним с помощью объекта I2C из панели инструментов управления приборами.

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 Elvis (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

Генерация тактового сигнала для использования с цифровой подсистемой

Цифровые подсистемы на устройствах NI DAQ не имеют собственных часов; они должны совместно использовать часы с аналоговой подсистемой или импортировать часы из внешней подсистемы. Генерация 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.

  • Запуск сбора данных в фоновом режиме

  • Запуск операций I2C

  • Остановить получение данных после завершения 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)];

При проверке убедитесь, что периоды простоя имеют высокий уровень вероятности нежелательной почты для более чем 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 вычисление скорости передачи битов.

  • Длительность условия запуска определяется как время, необходимое для снижения вероятности нежелательной почты после снижения ПДД.

  • Продолжительность состояния остановки определяется как время, необходимое для повышения ПДД после повышения вероятности нежелательной почты.

  • Скорость передачи битов вычисляется путем изменения времени между 2 возрастающими фронтами синхронизации.

Условие запуска: сначала низкий уровень SDA, затем низкий уровень вероятности нежелательной почты

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.

Условие остановки: сначала высокий уровень вероятности нежелательной почты, затем высокий уровень ПДД

% 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