В этом тексте я написал про то как можно отлаживать интерфейс 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 - правый канал.
![](https://habrastorage.org/getpro/habr/upload_files/cef/7b9/70e/cef7b970e8fa9caa46305c7d5186b1f2.png)
Как отлаживать 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.
![](https://habrastorage.org/getpro/habr/upload_files/e77/ced/f92/e77cedf9287240ad2348ba404b06da1c.png)
Теперь нужен логический анализатор. Вот что я получил на экране логического анализатора:
![](https://habrastorage.org/getpro/habr/upload_files/ff7/9dd/629/ff79dd6297acb3471d932c3c7b943fc2.png)
Тут SCL=3,125 MGz WS=45,9kHz. Отношение частот получается 69,7 раз. Что-то не то. Сделаем скриншот в векторном редакторе, проанализируем осциллограмму в бесплатной программе Inkscape.
![](https://habrastorage.org/getpro/habr/upload_files/2df/061/763/2df0617630a502828dbff2ad8d47953f.png)
Что-то не сходится с ожидаемым 0x87654321. Непорядок. Несоответствие. Я ожидал на экране логического анализатора увидеть это 0b1000_0111_0110_0101_0100_0011_0010_0001, а получилось какое-то 0x7AF5EBDx. В чем же дело?
Первое, что бросается в глаза, это битовая скорость SCK, которая не поспевает за частотой дискретизации WS. При этом частота SCK не является непрерывной, а пуляет пачками по 7 тактов. Это очень-очень странно. Это как если бы автомобиль-такси ездил по свободной автомагистрали рывками.
Вернулся в код и увеличил на всякий случай частоту тактирования провода SCK. Выяснилось, что картинка слегка улучшилась. Теперь хотя бы данные стали чуть более похожи на правду. Но тем не менее появляются какие-то фантомные такты, которые не отображаются на осциллограмме. И если бы фантомные такты были, то тестовый паттерн был бы похож на правду.
![](https://habrastorage.org/getpro/habr/upload_files/2fd/ba1/708/2fdba17083453a25d490b8995d359b7d.png)
У меня появилось подозрение, что логический анализатор не работает. Я решил измерить частоту на проводе SCK обыкновенным осциллографом и увидел, что осцилл как раз показывает, что частота SCK вполне себе непрерывная. Отличная частота.
![](https://habrastorage.org/getpro/habr/upload_files/c7a/749/8e0/c7a7498e05acb8a0ec44b915c8df0b8f.png)
Это значит, что логический анализатор Saleae (сало) как-то некорректно настроен. Заходим заново в программу логического анализатора (не перепутайте со Slack(ом)):
![](https://habrastorage.org/getpro/habr/upload_files/6db/ae0/a90/6dbae0a90fa0dacdee2fe91ae3960a8d.png)
Заходим в настройки каналов
![](https://habrastorage.org/getpro/habr/upload_files/e49/9b2/0e3/e499b20e3bef8ab67e0c9c915e49f668.png)
Выставляем настройки как показано на скриншоте. Крайне важно поставить максимальную частоту дискретизации. Для данной модели это 500 000 000 семплов в секунду. Надеюсь, это даст нам четкую ясную картинку трафика на шине I2S
![](https://habrastorage.org/getpro/habr/upload_files/c43/cae/e27/c43caee27fb5195d07815b5e7afb5612.png)
Вот теперь с корректными настройками картинка добротная, четкая. Мне уже начинает нравится логический анализатор Saleae.
![](https://habrastorage.org/getpro/habr/upload_files/f44/66b/f4e/f4466bf4e32daa6a232927d2d9ce465a.png)
эта картинка отлично анализируется
![](https://habrastorage.org/getpro/habr/upload_files/77c/ba5/85e/77cba585ee5820b636b7222f0d1c4291.png)
Битовая частота 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.
![](https://habrastorage.org/getpro/habr/upload_files/163/326/009/163326009e76f80edd51d5e0d580069e.png)
А на осциллограмме видно, что байты идут в инвертированном порядке по отношению к тому, как байты следуют в RAM памяти микроконтроллера. Это значит, что сначала посылаются младший байт, затем старший. Вот и теперь еще раз получается, что контроллер I2S делает инверсию байт, что поданы как аргумент для I2S трансивера.
![](https://habrastorage.org/getpro/habr/upload_files/b20/5fb/204/b205fb204925e15b4c3362f1cb89548c.png)
То есть микроконтроллер NRF5340 посылает qword слова не так как они на самом деле лежать в RAM памяти, а в инверсном порядке байт! Одновременно с этим по стандарту I2S надо посылать в формате big-endian. Порядок байт не совпадает. Порядок же бит в пределах байта - корректный.
Что из этого следует?
Получается, что перед отправкой семплы не надо разворачивать побайтово. Обычное дело когда перед тобой микросхема DAC у которой разрядность меньше 32 бит. Например 24 бита или вообще 12 бит. При этом я хочу воспроизвести тестовую синусоиду 800 Hz по предварительно расчитанной LUT таблице, чтобы проверить аналоговый тракт. Расcчитал пару периодов. Сохранил семплы в LUT. В столбце Tx значение, которое надо отправить. В столбце sample RAM подготовленное к отправке число в RAM памяти. Поэтому например -38=0xFFFFFFDA будет в массиве RAM cохранено как 0x00DAFFFF. Тогда оно будет отправлено 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 семплы
![Вот он переход PCM семплов через 0 на I2S осциллограмме Вот он переход PCM семплов через 0 на I2S осциллограмме](https://habrastorage.org/getpro/habr/upload_files/aa8/501/e3d/aa8501e3d314b58a23a4b9305d51461e.png)
Что на выходе DAC? Тут все просто. Надо увидеть пару синусов.
![](https://habrastorage.org/getpro/habr/upload_files/51b/ab6/147/51bab6147581826f3500f17ed998a3e5.png)
Теперь и вы умеете отлаживать интерфейс I2S и можете учить других.
Вывод
В инженерном деле и в разработке Hi-Tech электроники в частности очень важно уметь проверить одно и то же как минимум двумя способами. Так можно найти и исключить баг в самих средствах измерения.
Может показаться странно, но программистам микроконтроллеров также важно уметь рисовать, причем в векторных рисовалках типа программы Inkscape.
Комментарии (2)
esher
13.11.2022 13:58+1А с 16bit right justified работали? Там же биты L/R ровно-ровно в пределах своего WS должны быть?
Gromin
А чем sigrok хуже для анализа цифровых сигналов? Тем более I2S декодер там есть.