Компания Saleae производит логические анализаторы и предоставляет программу Saleae Logic 2 для отображения, анализа и логгирования данных. Logic 2 поддерживает разнообразные интерфейсы и протоколы и их декодирование.
Для расширения функций анализатора в Logic 2 появилась возможность создания своих обработчиков и декодеров протоколов.
В представляемом материале рассматривается создание своего декодера - анализатора верхнего уровня (HLA).

Зачем?
Например, есть последовательность передаваемых по SPI байт. Стандартно, при правильной настройке, вы увидите значения этих байт. Но, может возникнуть вопрос удобной интерпретации полученных данных.
Декодер может помочь в выводе данных в удобном виде и/или упростить анализ (reverse engineering) неизвестного протокола.

Как?
Saleae Logic 2 позволяет подключать программные расширения - написанные на Python модули, которые добавляют возможностей по отладке и анализу данных. В настоящий момент поддерживается три типа расширений:
- Высокоуровневый анализатор (High-Level Analyzers, HLA)
- Аналоговые измерения (Analog Measurements)
- Цифровые измерения (Digital Measurements)
Далее рассмотрим создание HLA, который может анализировать данные, получаемые от низкоуровневых протоколов (USART, I2C, SPI, CAN, LIN, Manchester и др.) В нашем примере будем декодировать передаваемые данные в ЖК-индикатор TIC33 и ТDO905. Оба индикатора имеют идентичный SPI-подобный интерфейс, разница заключается в разводке сегментов, а значит в декодере. Поэтому наш HLA-анализатор будет иметь функцию выбора декодера. Декодированные данные будем собирать в строку, признак новой строки - таймаут в передаваемых данных.
Для создания Расширения нажимаем на иконку Extension и далее нажимаем на Create Extension

Либо в опциях расширений выбрать Create New Extension (тут же можно загрузить кастомные расширения)


Вам предложат задать имя расширению, описание и авторство, а так же, выбрать путь и название к создаваемому Расширению. Logic 2 создаст несколько файлов-шаблонов: Extension.json HighLevelAnalyzer.py README.md
extension.json: { "name": "TIC33 & ТDO905 decoder", "apiVersion": "1.0.0", "author": "my hided name", "version": "0.0.1", "description": "SPI байтики декодируем в символы, отображаемые на дисплее TIC32 или ТDO905", "extensions": { "TIC LCD decoder": { "type": "HighLevelAnalyzer", "entryPoint": "HighLevelAnalyzer.Hla" } } }
Тут можно изменить имя, добавить описание и тогда наше расширение будет выглядеть примерно так:

Рассмотрим содержимое модифицированного файла HighLevelAnalyzer.py
Первым делом импортируются необходимые данные:from saleae.analyzers import HighLevelAnalyzer, AnalyzerFrame, ChoicesSetting, NumberSetting
from saleae.data import GraphTimeDelta
Далее создается декодер - какому значению байта в SPI потоке соответствует отображаемый декодером символ: # декодеры-словари
spitolcdDecoder_TIC33 = { # charset decoder for TIC33
0xFA: '0', 0x0A: '1', 0xB6: '2', 0x9E: '3',
0x4E: '4', 0xDC: '5', 0xFC: '6', 0x8A: '7',
0xFE: '8', 0xDE: '9', 0xEE: 'A', 0x7C: 'b',
0xF0: 'C', 0x3E: 'd', 0xF4: 'E', 0xE4: 'F',
0x00: ' ', 0x04: '-', 0x10: '_', 0x80: '¯',
0xC6: '⁰', 0x34: 'c', 0xC4: 'ᶜ', 0x14: '₌',
0x84: '⁼', 0x94: '≡', 0x3C: 'o', 0x6C: 'h',
0x2C: 'n', 0x38: 'u', 0x74: 't', 0xE6: 'P',
}
spitolcdDecoder_TD0905 = { # charset decoder for ТDO905
0x5F: '0', 0x50: '1', 0x6D: '2', 0x79: '3',
0x72: '4', 0x3B: '5', 0x3F: '6', 0x51: '7',
0x7F: '8', 0x7B: '9', 0x77: 'A', 0x3E: 'b',
0x0F: 'C', 0x7C: 'd', 0x2F: 'E', 0x27: 'F',
0x00: ' ', 0x20: '-', 0x08: '_', 0x01: '¯',
0x63: '⁰', 0x2C: 'c', 0x23: 'ᶜ', 0x28: '₌',
0x21: '⁼', 0x29: '≡', 0x3C: 'o',
}
При добавлении расширения нам нужно будет выбрать опции: тип ЖКИ (интерфейса, декодера), линию SPI с данными для декодера и таймаут - минимальное время между байтами которое будет идентифицировать новую порцию данных на ЖКИ.
N.B.: Вообще, драйвер ЖКИ имеет сигнал LOAD и в качестве разделителя фреймов можно использовать его.class Hla(HighLevelAnalyzer):
my_choices_setting = ChoicesSetting(choices=('TIC33', 'ТDO905'))
spiLine_Choise = ChoicesSetting(label='SPI Line', choices=('MOSI', 'MISO'))
packet_timeout = NumberSetting(label='Packet Timeout [s]', min_value=1e-6, max_value=1e4)
Результатом работы декодера будет выводимая в анализатор строка result_types = {
'LCD': {
'format': '{{data.string}}',
}
}
Инициализация. В зависимости от выбранного интерфейса используем соответствующую таблицу декодера. В каждом байте, помимо информации о 7-и сегментах, присутствует информация о разделительной точке, поэтому задаем шаблон выявления точки def init(self):
self.current_string = ''
self.start_time = None
self.last_time = None
self.patterns = spitolcdDecoder_TIC33 if self.my_choices_setting == 'TIC33' else spitolcdDecoder_ТDO905
self.dotmask = 0x01 if self.my_choices_setting == 'TIC33' else 0x80
def reset_state(self):
self.current_start_time = None
self.last_end_time = None
def decode(self, frame: AnalyzerFrame):
result_frames = []
if frame.data['mosi' if self.spiLine_Choise == 'MOSI' else 'miso'] is None:
return
maximum_delay = GraphTimeDelta(second=self.packet_timeout or 0.5E-3)
Декодируем текущий символ, данные берем с выбранной линии MOSI/MISO интерфейса SPI. Если в байте присутствует бит разделительной точки на дисплее - добавляем её в вывод. ch = int.from_bytes(frame.data['mosi' if self.spiLine_Choise == 'MOSI' else 'miso'], 'big')
symbol = self.patterns.get(ch & 0xFE, '?') + '.' if ch & self.dotmask else self.patterns.get(ch & 0xFE, '?')
Далее складываем символы в строку и выводим результат:
# Если это первый символ
if not self.current_string:
self.current_start_time = frame.start_time
self.current_string = symbol
self.last_end_time = frame.end_time
return
# Проверяем превышение таймаута
if self.last_end_time + maximum_delay < frame.start_time:
# Сохраняем предыдущую строку
result_frames.append(AnalyzerFrame(
'LCD',
self.current_start_time,
self.last_end_time,
{'string': self.current_string}
))
# Начинаем новую строку
self.current_string = symbol
self.current_start_time = frame.start_time
else:
# Продолжаем текущую строку
self.current_string += symbol
self.last_end_time = frame.end_time
return result_frames
def finalize(self):
if not self.current_string:
return
return AnalyzerFrame(
'LCD',
self.current_start_time,
self.last_end_time,
{'string': self.current_string}
)
На этом все, остается добавить/обновить наше расширение в Saleae Logic 2.
При добавлении декодера будет предложено выбрать источник данных - данные из SPI, декодируемый интерфейс (тип индикатора), линию данных SPI и разделяющий пакеты интервал времени.

Теперь наши данные получили "лицо" и, средствами логического анализатора Saleae, могут быть представлены в декодированном, более читаемом (user-friendly) виде.



Итоги
Наверное, читатель может сказать: «а зачем смотреть анализатором то, что и так видно на индикаторе?». Соглашусь, но данный пример служит лишь демонстрацией возможностей. На сайте Saleae можно найти более полезные примеры декодеров: для работы с различными I2C, SPI, NFC микросхемами, радиотрансиверами и специфическими протоколами. Даже если вы не найдете нужный для себя декодер, то сможете создать собственный под конкретную микросхему, периферию или при исследовании и проведении reverse engineering.
Полезные ссылки для интересующихся:
Saleae Support. Software Extensions: https://support.saleae.com/extensions
Пользовательские, комьюнити расширения. Shared High Level Analyzers (HLAs): https://support.saleae.com/extensions/high-level-analyzer-extensions/shared-high-level-analyzers-hlas
Комментарии (6)
NutsUnderline
22.05.2025 21:48для анализа протоколов можно прикрутить wireshark, в нем довольно удобно сделано сделан разбор пакетов данных по структурам а данные в него можно тянуть самыми разными путями, а верхний уровень можно реализовать скрипnом на .LUA https://stackoverflow.com/questions/44965474/creating-lua-dissector-in-wireshark-for-non-ethernet-data
более того можно использовать и куда как более дешевое железо, есть вообще готовое решение https://github.com/frank-zago/ch341-i2c-spi-gpio
checkpoint
А почему не сделали свой плагин для PulseView (sigrok) ? Почему завязались на проприетарное решение ?
ariz0na Автор
Основная причина - исторически больше привык к Saleae, хотя у меня есть и DSLogic и с DSView и универсальной PulseView давно знаком, но вот как-то не задумывался про создание плагина для него.
Второй резон - посмотрел поиском и нашел подробную статью на Хабре про написание декодера под sigrok, так что нового тут рассказать нечего ))
checkpoint
Если бы Вы сделали плагин для PulseView от этого было бы больше пользы. У меня тоже есть прибор от Saleae, но использую его исключительно с PulseView.
ariz0na Автор
Для PulseView получается вот так:
или нужно именно описание как добавлять свой плагин для PulseView?
checkpoint
Я не имел опыта добавления своих плагинов в PulseView, только пользовался тем что уже есть. Лишяя инструкция с живым примером никогда не помешает.