Обзор клона логического анализатора сигналов Saleae logic

Купил анализатор логических сигналов. Думаю пригодится. Хорошая вещь, может помочь при исследовании сигналов и поиске неисправностей в логических схемах. Устройство является клоном Saleae logic, оригинал которого стоит не одну сотню долларов.

Характеристики:

  • Каналов: 8
  • Частота выборки: до 24 млн/с
  • Входное сопротивление: 100 кОм

Устройство представляет из себя небольшую коробочку, размером примерно со спичечный коробок. С одной стороны разъём miniUSB для подключения к компьютеру. С другой — гребёнка из 10 контактов (8 каналов + две земли). Посмотрим, что внутри.

Микроконтроллер Cypress CY7C68013A, EEPROM Atmel 24C028N и буфер 74HC245.

С другой стороны:

Для работы с устройством качаем программу Logic.

В центре находятся 8 полос-каналов, на которых будет выводиться график сигнала. Слева находится кнопка Start и кнопки настроек каналов. Справа — окна измерений и анализа. Рассмотрим настройки:


Здесь можно выбрать частоту выборки (24 млн/с) и длительность:

Чем больше частота и длительность, тем больше потребуется памяти для сбора и хранения данных.

Для каждого канала можно выбрать ширину полосы, настроить фильтр помех или вообще скрыть канал:

Для одного из каналов можно настроить триггер: по фронту сигнала, по спаду, по ширине высокого или низкого импульса. Тогда при нажатии кнопки Start отсчёт времени записи начнётся только после срабатывания триггера.


Теперь для интереса рассмотрим различные сигналы с ардуинки.
Поморгаем светодиодом с паузой в 100 мс. Такой код:

void loop() {
  digitalWrite(13, HIGH);
  delay(100);
  digitalWrite(13, LOW);
  delay(100);
}

Сигнал:

Видим, что период получился 0.2 с, и частота 5 Гц. Всё верно.
Теперь уменьшим задержки до 1 мс:

void loop() {
  digitalWrite(13, HIGH);
  delay(1);
  digitalWrite(13, LOW);
  delay(1);
}

Увеличим:

Пока всё как мы и ожидали. Период чуть больше 2 мс, частота около 500 Гц.

Теперь совсем уберём задержку:

void loop() {
  digitalWrite(13, HIGH);
  digitalWrite(13, LOW);
}

Здесь мы видим, что ширина импульса примерно 5 мкс, за это время выполняется одна команда digitalWrite. И частоту получили около 95 кГц. Предел? Как бы не так. Попробуем избавиться от функций и обратимся к портам напрямую:

void loop() {
  PORTB = B11111111;
  PORTB = B00000000;
}

Ого, частота возросла больше, чем в 10 раз и теперь составляет примерно 1 МГц!

Но что с шириной импульсов? Почему ширина высокого уровня значительно короче, чем низкого? Ведь запись в регистр должна производиться с одинаковой скоростью независимо от содержимого. Есть что-то ещё, что выполняется после функции loop()?

Попробуем избавиться от стандартного перехода по функции loop и вставим бесконечный цикл:

void loop() {
  while (1) 
  {
    PORTB = B11111111;
    PORTB = B00000000;
  }
}

Уже лучше. И частота возросла до 4 МГц! Часть тактов теряется на прохождение сравнения в цикле (1 — истина?). Ну и теперь попробуем чередовать несколько одинаковых команд друг за другом:

void loop() {
  while (1) {
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
    PORTB = B11111111;
    PORTB = B00000000;
  }
}

Частота возросла до 8 МГц! То есть по одной операции на такт. Но при этом почему-то уменьшилась в два раза (с 83 нс до 41 нс) ширина положительного импульса. Вероятно фронт сигнала на выходе не совсем вертикальный и не успевает принять величины, при которых анализатор распознаёт его как логическую единицу.

Работа с регистрами напрямую очень необходима при работе с устройствами, которые критичны к синхронности импульсов на разных выводах. Посмотрим для примера, как различается импульс на двух контактах. Когда мы ставим друг за другом две команды digitalWrite для разных контактов, то мы считаем, что сигнал появится одновременно и там и там. Но конечно же это не так:

void loop() {
  while (1) {
    digitalWrite(12, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(12, LOW);
    digitalWrite(11, LOW);
  }
}

Прекрасно видно, что разница во времени между импульсами на разных выводах составляет 6.5 мкс:

А это было при работе с регистрами напрямую, сигналы одномоментны:

Если софт мог бы только показывать сигналы, то это уже полезно. Но он может больше! Он может показывать какие там передаются данные. Приведу внушительный список доступных протоколов:

  • Async Serial
  • I2C
  • SPI
  • 1-Wire
  • Atmel SWI
  • BiSS C
  • CAN
  • DMX-512
  • HD44780
  • HDLC
  • HDMI CEC
  • I2S / PCM
  • JTAG
  • LIN
  • MDIO
  • Manchester
  • MIDI
  • Modbus
  • PS/2 Keyboard/Mouse
  • UNI/O
  • USB LS и FS

Давайте для примера посмотрим на сигналы UART с ардуины. Будем отправлять по последовательному соединению фразу «Hello, world!»:

void loop() {
  Serial.print("Hello, world!");
  delay(200);
}

В программе в окне Analyzers добавим протокол «Async Serial»:


Теперь мы видим, что именно передаётся по проводу:

Можно увеличить, и посмотреть каждый распознанный бит:

Вот такой полезный девайс 🙂