Смотрим на шину I2C через осциллограф

от автора

Подразумевается, что читатель уже имеет некоторое представление о шине 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.

Рисунок 1

Рисунок 1

Рассмотрим их поближе.

  • HAL_I2C_Master_Transmit

Рисунок 2

Рисунок 2

Посылка начинается со стартового бита. Затем идут 7 бит адреса (1010000), затем бит r/w (лог 0) (его формирует функция HAL_I2C_Master_Transmit). Затем девятым битом микросхема подтверждает свое присутствие, прижимая SDA к нулю.  Мастер, понимая, что устройство отвечает, продолжает посылку, отправляя в шину число 5 (0b00000101). Снова девятым битом устройство прижимает линию SDA к нулю. Так как в функции мы отправляли всего одно число (4й аргумент), то посылка завершается стоповым битом.

  • HAL_I2C_Master_Receive

Рисунок 3

Рисунок 3

Посылка начитается со стартового бита. Затем идут 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);
Рисунок 4. Нет такого адреса на шине

Рисунок 4. Нет такого адреса на шине

Рассмотрим поближе

  • HAL_I2C_Master_Transmit

Рисунок 5

Рисунок 5
  • HAL_I2C_Master_Receive

Рисунок 6

Рисунок 6

Как видим в обоих случаях указан неверный адрес и 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 раз.

Рисунок 7

Рисунок 7

На картинке 9й «импульс» это уже чтение из первой части статьи (см Рисунок 1).
Раскроем покрупнее первую посылку записи.

Рисунок 8. Запись 8 байт в микросхему eeprom начиная с адреса 0

Рисунок 8. Запись 8 байт в микросхему eeprom начиная с адреса 0

1 – обращение к slave-устройству с адресом 1010000 в режиме записи (бит R/W̅ в лог. 0).
2 – установка адреса 0 (с адреса 0 будет далее писать 8 байт)
3 – запись в текущий адрес числа 0
… после занесения числа внутренний указатель адреса увеличивается на единицу, поэтому следующая запись будет по адресу 1
4 – запись в текущий адрес (1) числа 1

10 – запись в текущий адрес (7) числа 7

Раскроем покрупнее запись числа 5 (шаг 8 на рисунке 8)

Рисунок 9

Рисунок 9

Посмотрим на запись числа 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.

Буду рад критике.


ссылка на оригинал статьи https://habr.com/ru/articles/872254/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *