В этом тексте я написал про то как можно отлаживать интерфейс I2S.
I2S - это по сути труба для чисел в аудиоустройствах. Это проводной последовательный синхронный полнодуплексный интерфейс физического уровня для аудио ЦАП(ов) и АЦП. Интерфейс для связи в пределах одной печатной платы. Битовые частоты под 3 MHz поэтому эту технологию можно причислить к 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, WS, SD.
2--Формат передачи это two’s complement. То есть I2S передает знаковые целые числа и разрядность может быть абсолютно любой. Зависит от производителя микросхемы DAC. Для отладки придется научиться читать и интерпретировать бинарные представления типов данных int7_t, int13_t, int24_t и прочее.
3--Положительный перепад защелкивает данные.
4--Семпл передается старшим битом вперед MSB и в big-endiag.
5--Первый бит от предыдущего фрейма. Это самый младший бит от предыдущего фрейма LSB.
6--WS 0V - левый канал, WS 3.3V - правый канал.
Как отлаживать I2S на очередном устройстве?
Можно постоянно отправлять в шину тестовый семпл 0x87654321. Этот семпл 0x87654321 очень удобен, так как позволяет определить порядок байт и даже порядок бит в кадре. Можно добавить к модульным тестам 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 будет лежать так 0x21436587. Запустить тест можно по команде из UART-CLI.
Теперь нужен логический анализатор. Вот что я получил на экране логического анализатора:
Тут SCL=3,125 MGz WS=45,9kHz. Отношение частот получается 69,7 раз. Что-то не то. Сделаем скриншот в векторном редакторе, проанализируем осциллограмму в бесплатной программе Inkscape.
Что-то не сходится с ожидаемым 0x87654321. Непорядок. Несоответствие. Я ожидал на экране логического анализатора увидеть это 0b1000_0111_0110_0101_0100_0011_0010_0001, а получилось какое-то 0x7AF5EBDx. В чем же дело?
Первое, что бросается в глаза, это битовая скорость SCK, которая не поспевает за частотой дискретизации WS. При этом частота SCK не является непрерывной, а пуляет пачками по 7 тактов. Это очень-очень странно. Это как если бы автомобиль-такси ездил по свободной автомагистрали рывками.
Вернулся в код и увеличил на всякий случай частоту тактирования провода SCK. Выяснилось, что картинка слегка улучшилась. Теперь хотя бы данные стали чуть более похожи на правду. Но тем не менее появляются какие-то фантомные такты, которые не отображаются на осциллограмме. И если бы фантомные такты были, то тестовый паттерн был бы похож на правду.
У меня появилось подозрение, что логический анализатор не работает. Я решил измерить частоту на проводе SCK обыкновенным осциллографом и увидел, что осцилл как раз показывает, что частота SCK вполне себе непрерывная. Отличная частота.
Это значит, что логический анализатор Saleae (сало) как-то некорректно настроен. Заходим заново в программу логического анализатора (не перепутайте со Slack(ом)):
Заходим в настройки каналов
Выставляем настройки как показано на скриншоте. Крайне важно поставить максимальную частоту дискретизации. Для данной модели это 500 000 000 семплов в секунду. Надеюсь, это даст нам четкую ясную картинку трафика на шине I2S
Вот теперь с корректными настройками картинка добротная, четкая. Мне уже начинает нравится логический анализатор Saleae.
эта картинка отлично анализируется
Битовая частота 2941000 Hz, частота дискретизации на проводе WS 45821 Hz. Как и положено, битовая частота в 64 раза превышает частоту дискретизации. Отлично.
Данные в I2S должны передаваться в формате big-endian (старший байт вперед). То есть в привычной для человека форме. Сам же микроконтроллер хранит qword(ы) в little-endian (младший байт вперед).
Проведем еще один тест. Сделаем явно чтобы в RAM памяти данные шли в порядке 0x87654321.
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<ARRAY_SIZE(array_tx)/4; s+=4){
array_tx[s]=0x87;
array_tx[s+1]=0x65;
array_tx[s+2]=0x43;
array_tx[s+3]=0x21;
}
LOG_INFO(TEST, "Sample[0] 0x%08x", *((uint32_t *)&array_tx[0]) );
ASSERT_TRUE(i2s_api_write(i2s_num, array_tx, I2S_BLOCK_SIZE/4));
ASSERT_TRUE(i2s_send(i2s_num, true));
return true;
}
Запускаем тест. В RAM памяти семпл лежит в формате 0x87654321. Сначала старшие байты потом младшие. Так как действия происходят на LittleEndian процессоре, то uint32_t переменная на этой памяти примет значение 0x21436587.
А на осциллограмме видно, что байты идут в инвертированном порядке по отношению к тому, как байты следуют в RAM памяти микроконтроллера. Это значит, что сначала посылаются младший байт, затем старший. Вот и теперь еще раз получается, что контроллер I2S делает инверсию байт, что поданы как аргумент для I2S трансивера.
То есть микроконтроллер NRF5340 посылает qword слова не так как они на самом деле лежат в RAM памяти, а в инверсном порядке байт. И этой инверсией занимается аппаратура. Одновременно с этим по стандарту I2S надо посылать в формате big-endian. Порядок байт не совпадает. Порядок же бит в пределах байта - корректный.
Что из этого следует?
Получается, что перед отправкой семплы не надо разворачивать побайтово в ПО. Обычное дело когда перед тобой микросхема DAC у которой разрядность меньше 32 бит. Например 24 (WM8731) бита 16 бит (MAX9860) или вообще 12 бит. При этом я хочу воспроизвести тестовую синусоиду 800 Hz по предварительно расчитанной LUT таблице, чтобы проверить аналоговый тракт. Расcчитал пару периодов. Сохранил семплы в LUT. В столбце Tx значение, которое надо отправить. В столбце sample RAM подготовленное к отправке число в RAM памяти. Поэтому например -38=0xFFFFFFDA будет в массиве RAM cохранено как 0x00DAFFFF. Тогда оно будет отправлено I2S модулем как 0xFFFFDA00 и 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 семплы
Что на выходе DAC? Тут все просто. Надо увидеть пару синусов.
Теперь и вы умеете отлаживать интерфейс I2S и можете учить других.
Вывод
В инженерном деле и в разработке Hi-Tech электроники в частности очень важно уметь проверить одно и то же как минимум двумя способами. Так можно найти и исключить баг в самих средствах измерения.
Может показаться странно, но программистам микроконтроллеров также важно уметь рисовать, причем в векторных рисовалках типа программы Inkscape для разбора цифровых сигналов на скриншотах.