Подразумевается, что читатель уже имеет некоторое представление о шине i2c. Например, такие вещи как подключение устройств к шине, что устройства должны иметь уникальные адреса, что устройств может быть не более 127, что нужны подтягивающие резисторы, что посылка начинается с состояния старт, а заканчивается состоянием стоп. Но немного не хватает практики в части визуального контроля и понимания процессов. В этой статье я постараюсь лишить читателя этого самого практического пробела в знаниях.
Итак, рассмотрим поведение шины I2C при работе с микроконтроллером stm32 с использованием библиотеки HAL. На шине I2C1 подсоединена микросхема EEPROM 24LC21.
Согласно описанию на микросхему ее адрес на шине – 1010ххх(7бит), где ХХХ – не имеет значения, хоть ноль, хоть единицы. Следом за адресом устройства идет 8й бит R/W̅. Этот бит говорит slave-устройству, что нужно будет делать дальше – принимать или отдавать данные. Если лог 0, то slave-устройство будет принимать данные (write), то есть запись в slave-устройство. Если лог 1, то slave-устройство будет отправлять данные (read), то есть чтение из slave-устройства. Когда slave-устройство видит на шине свой адрес, то на 9м тактовом импульсе (на линии SCL) оно прижимает линию SDAв ноль, сообщая master-устройству о своем присутствии на линии. Если мастер выдал адрес, но устройства с таким адресом на шине не обнаружилось, то на 9м тактовом импульсе линия SDAбудет в единице. Это девятый бит называют битом подтверждения, acknowledge bit (А, АСК). Каждый обмен сообщениями обрамляется стартовым и стоповым битом. Стартовый бит это состояние когда при высоком состоянии линии SCL линия SDA прижимается к нулю. Стоповый бит это состояние когда при высоком состоянии линии SCL линия SDA прижимается к единице.
Выписка из описания на микросхему
Напишем простую программу чтения из микросхемы трех байт с адреса 5. Для этого нужно сперва записать в микросхему число 5. Это число внутри микросхемы поместится в указатель адреса. А затем считать 3 байта.
В качестве адреса HAL-овские функции просят адрес устройства (взятый из описания) предварительно сдвинуть влево. Об этом указано в описании функций библиотеки HAL. «User manual. Description of STM32F7 HAL and low-layer drivers. UM1905» https://www.st.com/resource/en/user_manual/DM00189702-.pdf (стр. 493) «DevAddress: Target device address: The device 7 bits address value in datasheet must be shifted to the left before calling the interface». Поэтому к 7битному адресу просто добавляем ноль.
txData[0] = 5; // 1
while (1)
{
HAL_I2C_Master_Transmit(&hi2c1, 0B10100000, txData, 1, 100);
HAL_I2C_Master_Receive (&hi2c1, 0B10100000, rxData, 3, 100);
HAL_Delay(1000);
}
В результате работы получаем такую осциллограмму на шине i2c. Левая часть это работы функции HAL_I2C_Master_Transmit,правая – HAL_I2C_Master_Receive.
Рассмотрим их поближе.
HAL_I2C_Master_Transmit
Посылка начинается со стартового бита. Затем идут 7 бит адреса (1010000), затем бит r/w (лог 0) (его формирует функция HAL_I2C_Master_Transmit). Затем девятым битом микросхема подтверждает свое присутствие, прижимая SDA к нулю. Мастер, понимая, что устройство отвечает, продолжает посылку, отправляя в шину число 5 (0b00000101). Снова девятым битом устройство прижимает линию SDA к нулю. Так как в функции мы отправляли всего одно число (4й аргумент), то посылка завершается стоповым битом.
HAL_I2C_Master_Receive
Посылка начитается со стартового бита. Затем идут 7 бит адреса (1010000), затем бит R/W (лог 1) (его формирует функция HAL_I2C_Master_Receive). Затем девятым битом микросхема подтверждает свое присутствие, прижимая SDA к нулю. Мастер, понимая, что устройство отвечает, продолжает генерировать тактовые импульсы на линии SCL. Но теперь линией SDA будет управлять slave-устройство, то есть в нашем случае микросхема EEPROM. Именно она формирует данные (в нашем случае нули, так как в микросхему действительно записаны нули). Теперь master их считывает и выставляет девятый бит в ноль или единицу. Так как в функции HAL_I2C_Master_Receive мы указали считать три байта (4й аргумент функции), то после получения третьего байта, master выставляет 9й бит в лог 1, тем самым сообщая slave-устройству что больше от него не требуется выдавать данные, и заканчивает посылку выставляя стоповый бит. Таким образом, функция Receive (прием) сперва фактически делает обращение (запись) к устройству, а затем уже чтение из устройства.
А давайте посмотрим что будет если указать неверный адрес
HAL_I2C_Master_Transmit(&hi2c1, 0B10010000, txData, 1, 100);
HAL_I2C_Master_Receive (&hi2c1, 0B10110000, rxData, 3, 100);
Рассмотрим поближе
HAL_I2C_Master_Transmit
HAL_I2C_Master_Receive
Как видим в обоих случаях указан неверный адрес и slave-устройство не откликнулось, не прижало линию SDA к нулю на 9м бите. И в обоих случаях текущая посылка прерывается мастером выдачей стопового бита.
Теперь давайте считаем какие-нибудь данные отличные от нуля.Но для этого их надо туда записать.
Для записи данных пишем такой код.
void erase_eeprom (){
uint8_t txData[9] ={0};
for (uint8_t j=0; j<8; j++){
for (uint8_t i=0; i<8; i++) {txData[i+1] = txData[0] + i;}
HAL_I2C_Master_Transmit(&hi2c1, 0xA0, txData, 9, 100);
HAL_Delay(11);
txData[0] += 8;
}
}
Данный код запишет в ячейки памяти число равное адресу этой ячейки. Пишем постранично, режим 4.2.PageWrite (см. описание на микросхему), по 8 байт за один раз. После того как отправили 8 байт данных в микросхему, они там размещаются во внутреннем буфере и после стопового бита происходит процесс записи непосредственно в ячейки памяти. Процесс записи это «долгий» процесс. Занимает 10 мс (см. описание на микросхему), поэтому делаем паузу между записями. Отправили 8 байт, подождали 11 мс. Отправили еще 8 байт, снова ждем 11 мс. И так 8 раз.
На картинке 9й «импульс» это уже чтение из первой части статьи (см Рисунок 1).
Раскроем покрупнее первую посылку записи.
1 – обращение к slave-устройству с адресом 1010000 в режиме записи (бит R/W̅ в лог. 0).
2 – установка адреса 0 (с адреса 0 будет далее писать 8 байт)
3 – запись в текущий адрес числа 0
… после занесения числа внутренний указатель адреса увеличивается на единицу, поэтому следующая запись будет по адресу 1
4 – запись в текущий адрес (1) числа 1
...
10 – запись в текущий адрес (7) числа 7
Раскроем покрупнее запись числа 5 (шаг 8 на рисунке 8)
Посмотрим на запись числа 20 в ячейку 20 – (третий "импульс" на рисунке 7)
1 – обращение к slave-устройству с адресом 1010000 в режиме записи (бит R/W̅ в лог. 0).
2 – установка адреса 16 (с адреса 16 будет далее писать 8 байт)
7 – запись в текущий адрес (20) числа 20
Теперь в EEPROM сохранили данные. Самое время их считать.
На осциллограмме результат работы функции HAL_I2C_Master_Receive (&hi2c1, 0B10100000, rxData, 3, 100);
Видим как устанавливается адрес slave-устройства 1010000, бит R/W̅ устанавливается в лог.1. И затем идут три числа считанные из EEPROM – 5, 6, 7.
На этом пока всё. Также планирую изучить (и написать статью) работу с шиной i2c в режиме прерываний и DMA.
Буду рад критике.
Комментарии (23)
VBKesha
08.01.2025 18:39Было время делали такое. Если нет логического анализатора то это лучше чем ничего.
Но даже самый дешевый китайский анализатор, удобней чем такой метод с офцилографом.
fx1979
08.01.2025 18:39Сейчас ослик декодирует эту шину.Сам не пробовал, только читал в описании.
VBKesha
08.01.2025 18:39Пробовал. Стоит MSO5074 полностью разлоченный. Скажем так это удобней чем самому руками по клокам, но анализатору сливает. В плане удобства очень сильно(ну по крайней мере на приборах такого класса).
Предназначение у осциллографа всё таки другое.
MinimumLaw
08.01.2025 18:39А можно еще UART и SPI. А при наличии хорошего осцила можно даже HDMI, LVDS, MIPI, Ethernet, USB, PCI-e... И даже без декодирования шины это бывает полезно.
В частности на ваших осцилограммах куда как информативнее ФОРМА сигнала, нежели его содержание. Но эта информация куда как более актуальна для схемотехника, чем для программиста. А для схемотехника осцил - родной инструмент.
nikolz
08.01.2025 18:39Для анализа сигналов цифровых интерфейсов, в частности I2C, рекомендую пользоваться не осциллографом, а логическим анализатором, который к тому же позволяет расшифровать протокол и получить все тайминги .
Можно взять самый простой и дешевый (сам его использую лет двадцать) , например, такой:
Спецификация:
Максимальная скорость дискретизации: 24 Мбит/с
Диапазон входного напряжения: 0 ~ 5 В
Цифровой канал: 8
Входной низкий уровень: <0,8 В
Входной высокий уровень:> 1,4 В
Протокол поддержки: SPI, IIC, UART, SMBus, I2S, CAN, параллельный, пользовательский, поиск, Async, 1-Wite, PS/2
Входное сопротивление: 1 МОм | 10 пФ (типичное, приблизительное)
Кристалл: ± 20ppm, 24 МГц
Ошибка/точность: измерение ширины импульса: ± 42 НС (при 24 МГц)
5erG0 Автор
08.01.2025 18:39Да, спасибо. Есть такой. В этой статье можно было бы и им обойтись. Зато на осцилле прекрасно видно как работает сигнал АСК. Когда мастер его выставляет, и слейв его тут же роняет на землю.
andi123
08.01.2025 18:39Статье нехватает акцента на то, что погромисту важно понимать, что будет происходить в физическом мире при выполнении его программ. Что бы за строчкой кода чтения байта из еепромки, было понимание, что на scl/sda будут электрические сигналы. Чтобы было понимание важности между намерением что-то сделать и тем, что в реальности происходит. И что самое главное, это стремление к тому чтобы не было никакого отличия между намерением и происходящим. И вот на этом пути и нужен инструмент для "разглядывания" физического мира. И очень важно, особенно на первых шагах, физически убедится, что выставляя адрес на шине он в реальности там именно такой, что ACK на него получен.
А дальше нужно разобраться, что важно, а что второстепенно. И под эту важность подбирать инструмент. Если разово поймать один ACK - убедиться что переферия отвечает, то и осцилла хватит. А если нужно весь обмен посмотреть и найти неутченный глитч на десятом мегабайте обмена, тут может и ЛА с потоковой записью на комп придется расчехлять.
5erG0 Автор
08.01.2025 18:39Спасибо за комментарий. Вы правильно описали. Именно это и была цель - увидеть живьём что происходит в реальной шине в результате работы программиста.
Spider55
08.01.2025 18:39Тема Старт/Стоп/Рестарт практически не затронута. Надо бы привести как выглядят на осцилографе эти события и что такое рестарт.
DarkWolf13
08.01.2025 18:39картинки красивые, но не помешало бы рассмотреть что твориться на шине когда преобразователи питания начинают добавлять проблем, или опять же пакеты начинают теряться при переключении нагрузки....т.е реальны как выглядит реально сигнал с выбросами и затянувшимися спадами..в свое время сталкивался что на стенде устройство работало замечательно, а когда пошли предсерийные сборки начинались какие проблемы с обменом - оказалось трансформаторы от общих преобразователей с соседнего "мезонина" иногда доставали либо до процессора либо до шины обмена процессора и все изделие наглухо висло....
JIexa21
Только один вопрос: зачем?
fx1979
В ремонте пригождается, можно увидеть какой адрес не отвечает и что за схема сидит на этом адресе.
xSVPx
А что еще делать если собрал схему, а оно не работает ? Всё проверил, а оно не пашет. И совершенно непонятно в чём проблема, толи МК не те сигналы дает, толи периферия не отвечает. Как еще разобраться то ?
JIexa21
Ниже уже дали пример анализатора, который делает это на порядок лучше осциллографа...
И у меня была проблема с 24LC01. Разобрался по исходникам библиотек - у 24lc01 и 02 адрес передается одним байтом, а не двумя. Причем этой информации не нашел и в документации. И чем бы тут помог осциллограф?
DrGluck07
Чаще всего нет задачи "сделать на порядок лучше", есть задача "собрали макет, но что-то I2C-дисплей ничего не показывает" (например). Подключаешь осциллограф и смотришь что мы хотя бы что-то передаём. Это для начала.
xSVPx
На осцилографе вы как минимум видите, что идут импульсы и их форму. И если они треугольные :) то что-то пошло не так...
Ну т.е. анализатор это тоже хорошо, но в зависимости от конкретной проблемы может оказаться, что осцилографом даже быстрее найдете(им попутно можно питание проверить итд итп). Хотя, лучше бы иметь и то и другое.
У кого что под рукой, тот это что-то и втыкает..... Но хоть что-то многократно лучше, чем ничего...
5erG0 Автор
Ну справедливости ради, осцил или же ЛА помогает при аппаратных проблемах. Вы же описали программную проблему. При этом, в вашем случае на экране осциллографа (ой, ну ладно - ЛА) вы бы увидели что на шине присутствует ровно то, что вы отправляете в программе.
JIexa21
Ну накосячить с двумя проводами и двумя резисторами это надо постараться... Тогда да - наверное без осциллографа никуда )))