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

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

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

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


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


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

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

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

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

main_channels

Здесь можно выбрать частоту выборки (24 млн/с) и длительность:
main_settings
Чем больше частота и длительность, тем больше потребуется памяти для сбора и хранения данных.
Для каждого канала можно выбрать ширину полосы, настроить фильтр помех или вообще скрыть канал:
main_channel_settings

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

main_channels_trigger

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

1
2
3
4
5
6
void loop() {
  digitalWrite(13, HIGH);
  delay(100);
  digitalWrite(13, LOW);
  delay(100);
}

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

1
2
3
4
5
6
void loop() {
  digitalWrite(13, HIGH);
  delay(1);
  digitalWrite(13, LOW);
  delay(1);
}

signal_1ms
Увеличим:
signal_1ms-2
Пока всё как мы и ожидали. Период чуть больше 2 мс, частота около 500 Гц.
Теперь совсем уберём задержку:

1
2
3
4
void loop() {
  digitalWrite(13, HIGH);
  digitalWrite(13, LOW);
}
signal_0ms
Здесь мы видим, что ширина импульса примерно 5 мкс, за это время выполняется одна команда digitalWrite. И частоту получили около 95 кГц. Предел? Как бы не так. Попробуем избавиться от функций и обратимся к портам напрямую:
1
2
3
4
void loop() {
  PORTB = B11111111;
  PORTB = B00000000;
}
signal_0ms_ports
Ого, частота возросла больше, чем в 10 раз и теперь составляет примерно 1 МГц!
Но что с шириной импульсов? Почему ширина высокого уровня значительно короче, чем низкого? Ведь запись в регистр должна производиться с одинаковой скоростью независимо от содержимого. Есть что-то ещё, что выполняется после функции loop()?
Попробуем избавиться от стандартного перехода по функции loop и вставим бесконечный цикл:
1
2
3
4
5
6
7
void loop() {
  while (1) 
  {
    PORTB = B11111111;
    PORTB = B00000000;
  }
}
signal_0ms_ports_while
Уже лучше. И частота возросла до 4 МГц! Часть тактов теряется на прохождение сравнения в цикле (1 — истина?). Ну и теперь попробуем чередовать несколько одинаковых команд друг за другом:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
  }
}
signal_0ms_ports_while_8mhz
Вот этой аномалии я немного не понял. Частота возросла до 8 МГц! То есть по одной операции на такт. Но при этом почему-то уменьшилась в два раза (с 83 нс до 41 нс) ширина положительного импульса. Что на это повлияло я объяснить не готов. Надо пробовать ковырять HEX-файл в дизассемблере.
Работа с регистрами напрямую очень необходима при работе с устройствами, которые критичны к синхронности импульсов на разных выводах. Посмотрим для примера, как различается импульс на двух контактах. Когда мы ставим друг за другом две команды digitalWrite для разных контактов, то мы считаем, что сигнал появится одновременно и там и там. Но конечно же это не так:
1
2
3
4
5
6
7
8
void loop() {
  while (1) {
    digitalWrite(12, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(12, LOW);
    digitalWrite(11, LOW);
  }
}

Прекрасно видно, что разница во времени между импульсами на разных выводах составляет 6.5 мкс:
signal_2ch_notsync
А это было при работе с регистрами напрямую, сигналы одномоментны:
signal_0ms_ports

Если софт мог бы только показывать сигналы, то это уже полезно. Но он может больше! Он может показывать какие там передаются данные. Приведу внушительный список доступных протоколов:
  • 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!»:
1
2
3
4
void loop() {
  Serial.print("Hello, world!");
  delay(200);
}

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

main_protocol_serial

Теперь мы видим, что именно передаётся по проводу:
signal_serial_helloworld
Можно увеличить, и посмотреть каждый распознанный бит:
signal_serial_helloworld2
Вот такой полезный девайс 🙂

Похожие записи:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *