Отладка интерфейса I2S
В этом тексте я написал про то как можно отлаживать интерфейс I2S.
I2S-это по сути труба для чисел в аудиоустройствах. Это проводной последовательный синхронный полнодуплексный интерфейс физического уровня для аудио ЦАП (ов) и АЦП. Интерфейс для связи в пределах одной печатной платы. Битовые частоты под 3MHz поэтому эту технологию можно причислить к Hi Load. Топология Master-Slave. Мастер выдает частоту синхронизации SCL и частоту дискретизации WS. Стандарт определяет битовую частоту до 2,7 MHz. Вся спека I2S насчитывает всего-навсего 7 страничек.
Что нам понадобится из железа?
Оборудование | Назначение |
осциллограф Tektronix TDS 2014C | для измерения сигнала |
логический анализатор Saleae и полный комплект цепляш для него. | для измерения сигнала |
Android смартфон | для скриншотов и диагностический приложений |
USB-Flash (ка) | для скриншотов c осциллографа |
устройство с микроконтроллером, который поддерживает I2S | исследуемый прибор |
4 щупа для осциллографа | для измерения сигнала |
Что нужно из софтвера?
Программа | Назначение |
Inkscape | Векторный графический редактор для анализа осциллограмм |
Snipping Tool | Win (довая) утилита для селективных скриншотов |
Audizr | Android приложение, которое показывает спектр аудиосигнала по данным с микрофона |
Logic 2 | Утилита от вендора для управления логическим анализатором |
В теории правила обмена по I2S весьма простые:
1--Нужно минимум 3 провода: SCL, SW, SD
2--Формат передачи это two«s complement. То есть I2S передает знаковые целые числа и разрядность может быть абсолютно любой. Зависит от производителя микросхемы DAC. Для отладки придется научиться читать и интерпретировать бинарные представления типов данных int7_t, int13_t, int24_t и прочее.
3--Положительный перепад защелкивает данные.
4--Семпл передается старшим битом вперед BigEndiag.
5--Первый бит от предыдущего фрейма
6--WS 0V — левый канал WS 3.3V правый канал.
Как отлаживать I2S на очередном устройстве?
Можно постоянно отправлять в шину тестовый семпл 0×87654321. Этот семпл 0×87654321 очень удобно так как позволяет определить порядок байт и даже порядок бит в кадре. Можно добавить к модульным тестам I2S вот такой модульный тест.
static uint32_t array_tx[I2S_BLOCK_SIZE];
static bool test_i2s_write_one(uint8_t i2s_num) {
LOG_INF(I2S, "%s() I2sNum %u", __FUNCTION__, i2s_num);
uint32_t s = 0;
for(s=0; s < ARRAY_SIZE(array_tx); s++){
array_tx[s]=0x87654321;
}
ASSERT_TRUE(i2s_api_write(i2s_num, array_tx, I2S_BLOCK_SIZE));
return true;
}
Так как микроконтроллер LittleEndian, то в RAM памяти семпл 0x87654321 будет лежать так 0×21436587. Запустить тест можно по команде из UART-CLI.
Теперь нужен логический анализатор. Вот что я получил на экране логического анализатора
Тут SCL=3,125 MGz WS=45,9kHz. Отношение частот получается 69.7 раз. Что-то нет то. Сделаем скриншот в векторном редакторе проанализируем осциллограмму в бесплатной программе Inkscape.
Что то не сходится с ожидаемым 0×87654321. Непорядок. Несоответствие. Я ожидал на экране логического анализатора увидеть это 0b1000_0111_0110_0101_0100_0011_0010_0001 аполучилось какое-то 0×7AF5EBDx. В чем же дело?
Первое, что бросается в глаза это битовая скорость SCK, которая не поспевает за частотой дискретизации WS. При этом частота SCK не является непрерывной, а пуляет пачками по 7 тактов. Это очень-очень странно. Это как если бы такси-автомобиль по свободной автомагистрали бы ездил рывками.
Вернулся в код и увеличил на всякий случай частоту тактирования провода SCK. Выяснилось, что картинка слегка улучшилась. Теперь хотя бы данные стали чуть более похожи на правду. Но тем не менее появляются какие-то фантомные клоки, которые не отображаются на осциллограмме. Но если бы они были, то тестовый паттерн был бы похож на правду.
У меня появилось подозрение, что логический анализатор не работает. Я решил измерить частоту на проводе SCK обыкновенным осциллографом и увидел, что осцилл как раз показывает, что частота SCK вполне себе непрерывная. Отличная частота.
Это значит, что логический анализатор Saleae (сало) как-то некорректно настроен. Заходим заново в программу логического анализатора (не перепутайте со Slack (ом))
Заходим в настройки каналов
выставляем настройки как показано на скриншоте. Крайне важно поставить максимальную частоту дискретизации. Для данное модели это 500 000 000 семплов в секунду. Надеюсь, это даст нам четкую картинку трафика на шине I2S
вот теперь с корректными настройками картинка добротная, четкая
эта картинка отлично анализируется
Битовая частота 2941000 Hz, частота дискретизации на проводе WS 45821 Hz. Как и положено битовая частота в 64 раза превышает частоту дискретизации.
Данные в I2S должны передаваться в формате BigEndian (старший байт вперед). То есть в привычной для человека форме. Сам же микроконтроллер хранит qword (ы) в Little Endian (младший байт вперед).
Проведем еще один тест. Сделаем явно чтобы в RAM памяти данные шли в порядке 0×87654321.
static uint8_t array_tx[I2S_BLOCK_SIZE];
static bool test_i2s_write_one(uint8_t i2s_num) {
LOG_INFO(TEST, "%s() I2sNum %u", __FUNCTION__,i2s_num);
uint32_t s=0;
for(s=0; s
Запускаем тест. В RAM памяти семпл лежит в формате 0×87654321. Сначала старшие байты потом младшие. Так как действия происходят на LittleEndian процессоре, то uint32_t переменная на этой памяти примет значение 0×21436587.
А на осциллограмме видно, что байты идут в инвертированном порядке по отношению к тому как байты следуют в RAM памяти микроконтроллера. Это значит что сначала посылаются младший байт затем старший. Вот и теперь еще раз получается, что контроллер I2C делает инверсию байт, что поданы как аргумент для I2S трансивера.
То есть микроконтроллер nrf5340 посылает qword слова не так как они на самом деле лежать в RAM памяти, а в инверсном порядке байт! Одновременно с этим по стандарту I2S надо посылать в формате BigEndian. Порядок байт не совпадает. Порядок же бит в пределах байта корректный.
Что из этого следует?
Получается что перед отправкой семплы не надо разворачивать побайтово. Есть DAC у которых разрядность меньше 32 бит. Например 24 бита. Допустим я хочу воспроизвести синусоиду 800 Hz по предварительно расчитанной LUT таблице. Расcчитал пару периодов. Сохранил семплы в LUT. В столбце Tx значение, которое надо отправить. В столбце sample RAM подготовленное к отправке число в RAM памяти. Поэтому например -38=0xFFFFFFDA будет в массиве RAM cохранено как 0×00DAFFFF. Тогда оно будет отправлено I2S модулем как 0xFFFFDA и I2S DAC корректно распознает такой семпл.
LookUpTable (LUT) for 24-bit DAC
+-----+------------+------------+------------+
| No | sample RAM | sample | sample hex |
+-----+------------+------------+------------+
| 1 | 0x00000000 | 0 | 0x00000000 |
| 2 | 0x000d0000 | 13 | 0x0000000d |
| 3 | 0x001a0000 | 26 | 0x0000001a |
| 4 | 0x00250000 | 37 | 0x00000025 |
| 5 | 0x002d0000 | 45 | 0x0000002d |
| 6 | 0x00310000 | 49 | 0x00000031 |
| 7 | 0x00310000 | 49 | 0x00000031 |
| 8 | 0x002d0000 | 45 | 0x0000002d |
| 9 | 0x00260000 | 38 | 0x00000026 |
| 10 | 0x001b0000 | 27 | 0x0000001b |
| 11 | 0x000e0000 | 14 | 0x0000000e |
| 12 | 0x00000000 | 0 | 0x00000000 |
| 13 | 0x00f3ffff | -13 | 0xfffffff3 |
| 14 | 0x00e6ffff | -26 | 0xffffffe6 |
| 15 | 0x00dbffff | -37 | 0xffffffdb |
| 16 | 0x00d3ffff | -45 | 0xffffffd3 |
| 17 | 0x00cfffff | -49 | 0xffffffcf |
| 18 | 0x00cfffff | -49 | 0xffffffcf |
| 19 | 0x00d3ffff | -45 | 0xffffffd3 |
| 20 | 0x00daffff | -38 | 0xffffffda |
| 21 | 0x00e5ffff | -27 | 0xffffffe5 |
| 22 | 0x00f2ffff | -14 | 0xfffffff2 |
| 23 | 0x00ffffff | -1 | 0xffffffff |
| 24 | 0x000d0000 | 13 | 0x0000000d |
| 25 | 0x001a0000 | 26 | 0x0000001a |
| 26 | 0x00240000 | 36 | 0x00000024 |
| 27 | 0x002c0000 | 44 | 0x0000002c |
| 28 | 0x00310000 | 49 | 0x00000031 |
| 29 | 0x00310000 | 49 | 0x00000031 |
| 30 | 0x002e0000 | 46 | 0x0000002e |
| 31 | 0x00260000 | 38 | 0x00000026 |
| 32 | 0x001c0000 | 28 | 0x0000001c |
| 33 | 0x000f0000 | 15 | 0x0000000f |
| 34 | 0x00010000 | 1 | 0x00000001 |
| 35 | 0x00f4ffff | -12 | 0xfffffff4 |
| 36 | 0x00e7ffff | -25 | 0xffffffe7 |
| 37 | 0x00dcffff | -36 | 0xffffffdc |
| 38 | 0x00d4ffff | -44 | 0xffffffd4 |
| 39 | 0x00cfffff | -49 | 0xffffffcf |
| 40 | 0x00cfffff | -49 | 0xffffffcf |
| 41 | 0x00d2ffff | -46 | 0xffffffd2 |
| 42 | 0x00daffff | -38 | 0xffffffda |
| 43 | 0x00e4ffff | -28 | 0xffffffe4 |
| 44 | 0x00f1ffff | -15 | 0xfffffff1 |
+-----+------------+------------+------------+
^
|
first byte in I2S
Что на входе DAC? При отладке I2S важно убедиться, что на осциллограмме есть переход от отрицательных PCM семплов в положительные PCM семплы
переход через O на I2S осцилограмме
Что на выходе DAC? Тут все просто. Надо увидеть синус.
Теперь и вы умеете отлаживать интерфейс I2S и можете учить других.
Вывод
В инженерном деле и в разработке Hi-Tech электроники в частности очень важно уметь проверить одно и то же как минимум 2 мя способами. Так можно найти и исключить баг в самих средствах измерения.
Может показаться странно, но программистам микроконтроллеров также важно уметь рисовать причем в векторных рисовалках типа программы inkscape.