В статье "Часть 12. STM32 и C++. I2C в Cortex-M4" я описал, как работать с I2C на ядрах Cortex-M4. Продолжая работать с I2C на других чипах, я столкнулся с тем что данный способ общения с I2C корректен не для всех ядер. Описанный способ точно подходит для STM32F405xx и STM32F407xx, вполне возможно что подходит также и для STM32F415, STM32F417 и STM32F43x. Проверить это утверждение я не могу, так как нет необходимых МК. Что точно могу сказать, для серии STM32F411xx этот метод точно не походит, но к этому МК вернёмся чуть позже.
Сейчас рассмотрим STM32F030x4/x6/x8/xC и STM32F070x6/xB (про серию STM32F0x1/STM32F0x2/STM32F0x8 пока сказать ничего не могу, позже проверю и в комментариях отпишу). У него, да и у многих других МК, в регистре CR2 есть два интересных поля: NBYTES[7:0]
- в который пишется количество байт/слов которое мы хотим передать и SADD[9:0]
- в который пишется адрес устройства, к которому мы хотим обратиться. А так как не нужны пляски с бубном, как у F407, здесь код получается попроще. Я не буду приводить длиннющие куски кода, просто опишу алгоритм, а как это оформлено можно посмотреть в библиотеке.
Алгоритм чтения.
- Ждём флаг BUSY
- В регистре CR2:
- Сбрасываем:
- SADD
- NBYTES
- RELOAD
- AUTOEND
- RD_WRN
- START
- STOP
- Устанавливаем:
- SADD
- NBYTES
- AUTOEND или RELOAD
- READ
- START
- Сбрасываем:
- Ждём флаг RXNE
- В цикле из регистра RXDR считываем данные
- Ждём флаг STOP
- В регистре статуса ICR сбрасываем флаг STOPCF
- В регистре CR2 сбрасываем:
- SADD
- HEAD10R
- NBYTES
- RELOAD
- RD_WRN
- Выходим
Вроде всё просто, но есть нюансы.
Шаг 2 - я написал флаги и регистры по порядку, но не всё так просто. Есть МК, у которых эти команды можно выполнить пошагово, на F030CCx я столкнулся с тем, что необходимо считать регистр целиком, сбросить необходимые биты, установить необходимые биты и записать это всё обратно. Как только запишем получившуюся команду, I2C выплюнет на линию адрес с запросом на чтение и будет ждать ответа. Ещё один нюанс, на обычных I2C устройствах этот шаг не критичен, а при работе с микросхемами памяти - критичен. Если при работе с памятью мы не выполним это условие, то зависнем на третьем шаге.
Шаг 3 - интереса не представляет.
Шаг 4 - в шаге 2.2.3 указано два флага, а не один. Эти флаги влияют на количество принимаемых байт. Регистр NBYTES
имеет длину 8 бит. Соответственно, мы можем передать только 255 байт/слов, а мы хотим принять 100500. Для приёма всей информации нам нужно разбить её на блоки по 255 байт. Мы записываем в NBYTES
число 255, устанавливаем указатель на начало буфера и во втором шаге поднимаем флаг RELOAD
. Интерфейс сгенерирует все необходимые сигналы и примет первую порцию байт/слов. И так нужно делать, пока в буфере не отстанется меньше 255 байт/слов. В этом случае необходимо установить флаг AUTOEND
и интерфейс примет оставшуюся информацию и сгенерирует STOP.
Дальнейшие шаги интереса не представляют.
Здесь есть небольшой момент. Мы стираем даже те флаги, которые в процессе работы этого алгоритма могут и не подняться, но нужно учитывать, что мы в эту функцию можем прийти из другой, где эти флаги могут оказаться задействованными, поэтому лучше подстраховаться.
Алгоритм записи
Практически ничем не отличается от алгоритма чтения за исключением:
- Шаг 2.2.4: нужно поднять флаг WRITE
- Шаг 2.2.5: исключаем
- Шаг 3: ждём флаг TXIS
- Шаг 4: кидаем данные в регистр TXDR
Все остальное точно также.
Алгоритм чтения EEPROM, например AT24C02.
- Ждём флаг BUSY
- В регистре CR2:
- Сбрасываем:
- SADD
- NBYTES
- RELOAD
- AUTOEND
- RD_WRN
- START
- STOP
- Устанавливаем:
- SADD
- NBYTES
- WRITE
- START
- Сбрасываем:
- Ждём флаг TXIS
- Если адрес
- 8 бит
- Кидаем его в TXDR
- 16 бит
- Кидаем младший байт в TXDR
- Ждём флаг TXIS
- Кидаем старший байт в TXDR
- 8 бит
- Ждём флаг TC
- Далее как в алгоритме чтения данных
Алгоритм записи EEPROM.
- Ждём флаг BUSY
- В регистре CR2:
- Сбрасываем:
- SADD
- NBYTES
- RELOAD
- AUTOEND
- RD_WRN
- START
- STOP
- Устанавливаем:
- SADD
- NBYTES
- WRITE
- RELOAD
- START
- Сбрасываем:
- Ждём флаг TXIS
- Если адрес
- 8 бит
- Кидаем его в TXDR
- 16 бит
- Кидаем младший байт в TXDR
- Ждём флаг TXIS
- Кидаем старший байт в TXDR
- 8 бит
- Ждём флаг TCR
- Далее как в записи обычных данных за некоторыми исключениями.
- Шаг 2.2.4: нужно поднять флаг WRITE
- Шаг 2.2.5: исключаем
- Шаг 3: ждём флаг TXIS
- Шаг 4: кидаем данные в регистр TXDR
Под конец пару слов о том хитром регистре CR2
, и почему так с ним нужно управляться. На картинке показаны сигналы, в случае последовательной записи в этот регистр:
А на этой - при правильной записи в регистр:
Как видно, после того, как адрес ячейки EEPROM выкинули на шину, идёт команда "Рестарт", в случае же последовательной инициализации регистра, на этом месте "Стоп" и "Старт", что сносит крышу у EEPROM.
На этом пока всё.
Примечание: сделайте или приобретите логический анализатор, без него, как без рук. Такие нюансы я бы долго искал.