Top.Mail.Ru

Часть 20. I2C. Дополнение.

В статье "Часть 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, здесь код получается попроще. Я не буду приводить длиннющие куски кода, просто опишу алгоритм, а как это оформлено можно посмотреть в библиотеке.

Алгоритм чтения.

  1. Ждём флаг BUSY
  2. В регистре CR2:
    1. Сбрасываем:
      1. SADD
      2. NBYTES
      3. RELOAD
      4. AUTOEND
      5. RD_WRN
      6. START
      7. STOP
    2. Устанавливаем:
      1. SADD
      2. NBYTES
      3. AUTOEND или RELOAD
      4. READ
      5. START
  3. Ждём флаг RXNE
  4. В цикле из регистра RXDR считываем данные
  5. Ждём флаг STOP
  6. В регистре статуса ICR сбрасываем флаг STOPCF
  7. В регистре CR2 сбрасываем:
    1. SADD
    2. HEAD10R
    3. NBYTES
    4. RELOAD
    5. RD_WRN
  8. Выходим

Вроде всё просто, но есть нюансы.

Шаг 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.

  1. Ждём флаг BUSY
  2. В регистре CR2:
    1. Сбрасываем:
      1. SADD
      2. NBYTES
      3. RELOAD
      4. AUTOEND
      5. RD_WRN
      6. START
      7. STOP
    2. Устанавливаем:
      1. SADD
      2. NBYTES
      3. WRITE
      4. START
  3. Ждём флаг TXIS
  4. Если адрес
    1. 8 бит
      1. Кидаем его в TXDR
    2. 16 бит
      1. Кидаем младший байт в TXDR
      2. Ждём флаг TXIS
      3. Кидаем старший байт в TXDR
  5. Ждём флаг TC
  6. Далее как в алгоритме чтения данных

Алгоритм записи EEPROM.

  1. Ждём флаг BUSY
  2. В регистре CR2:
    1. Сбрасываем:
      1. SADD
      2. NBYTES
      3. RELOAD
      4. AUTOEND
      5. RD_WRN
      6. START
      7. STOP
    2. Устанавливаем:
      1. SADD
      2. NBYTES
      3. WRITE
      4. RELOAD
      5. START
  3. Ждём флаг TXIS
  4. Если адрес
    1. 8 бит
      1. Кидаем его в TXDR
    2. 16 бит
      1. Кидаем младший байт в TXDR
      2. Ждём флаг TXIS
      3. Кидаем старший байт в TXDR
  5. Ждём флаг TCR
  6. Далее как в записи обычных данных за некоторыми исключениями.
  • Шаг 2.2.4: нужно поднять флаг WRITE
  • Шаг 2.2.5: исключаем
  • Шаг 3: ждём флаг TXIS
  • Шаг 4: кидаем данные в регистр TXDR

Под конец пару слов о том хитром регистре CR2, и почему так с ним нужно управляться. На картинке показаны сигналы, в случае последовательной записи в этот регистр:

А на этой - при правильной записи в регистр:

Как видно, после того, как адрес ячейки EEPROM выкинули на шину, идёт команда "Рестарт", в случае же последовательной инициализации регистра, на этом месте "Стоп" и "Старт", что сносит крышу у EEPROM.

На этом пока всё.

Примечание: сделайте или приобретите логический анализатор, без него, как без рук. Такие нюансы я бы долго искал.

Подписаться
Уведомить о
guest

4 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Sith
Sith
7 месяцев назад

Огромное спасибо за статью, очень помогла. Не является ли ошибкой сброс флага в шаге 3 записи TXIS, так бы мы делали если работали через прерывание, поидее надо вот этот TXE: Transmit data register empty (transmitters), пока не проверил.

Sith
Sith
Ответ на комментарий  Эдуард
7 месяцев назад

Я ошибся, у вас все верно. Провозился долго с шиной, перебрал все регистры, а все потому что на самом датчике не правильно был выставлен режим с помощью порта IO.

4
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x