AVR Урок 16. Интерфейс TWI (I2C). Часть 3



 

Урок 16

Часть 3

 

Интерфейс TWI (I2C)

 

В предыдущей части занятия мы продолжили знакомство с шиной I2C и уже создали проект для того, чтобы занятия проработать на практике. Также мы познакомились с интересной микросхемой EEPROM AT24C32, работающей на данной шине.

Продолжаем изучение шины.

Шину мы инициализировали в проекте, выставив ей скрость с помощью регистра TWBR, теперь давайте поближе познакомимся с управляющим регистром — TWCR

 

image09

 

Как мы видим, в данном регистре присутствует насколько битов.

 

TWINT — это бит прерываний. Можно его назвать битом не управляющим, а статусным, так как устанавливается он аппаратно в тот момент, когда определённое задание на шине завершится и будет ожидаться реакция программы. А вот сбрасывается данный флаг не аппаратно, а только программно — записью в него логической 1.

TWEA — бит или флаг, разрешающий подтверждение. Если мы его не установим, то мы не будем просить подтверждение от ведомого устройства, а в случае, если контроллер наоборот является ведомым устройством, то с очередной посылкой мы не отправим бит подтверждения в конце какой-то посылки.

TWSTA — бит установки или генерирования условия «Старт».

TWSTO — бит установки или генерирования условия «Стоп».

TWWC — бит ошибочной записи. Устанавливается при попытки записи в адресный буфер, когда флаг TWINT ещё не установился. Ещё называется он флагом коллизий. Данный бит сбросится, когда TWINT будет равен 1.

TWEN — бит, активирующий шину I2C. Если мы его устанавливаем, то шина I2C начинает пытаться выполнять задание в зависимости от условий.

TWIE — бит, который разрешает прерывания.

 

Теперь статусный регистр TWSR

 

image10

 

Пять старших битов регистра содержат код статуса операции, которая выполнялась перед тем, как мы читаем регистр. Как правило мы с помощью маскирования сбрасываем три младших бита в 0, ну конечно не в самом регистре, а в переменной, в которую мы его считали и затем уже исследуем полученный результат. В даташите на контроллер содержится перечень в виде нескольких таблиц различных кодов статуса. Несколько их потому, что есть несколько условий — ведущее или ведомое устройство и чтение или запись происходила.

 

 

Ну а два младших бита — TWPS1 и TWPS0 — это биты для делителя частоты шины, с которыми мы уже знакомились немного в 1 части занятия.

Вот такие вот могут быть варианты комбинаций данных битов и зависимость делителя от этих комбинаций

 

image11

 

Займёмся теперь непосредственно шиной.

Как же всё работает:

 

image12

 

Нам для начала любой посылки необходимо сгенерировать условие СТАРТ, чтобы ведущие устройства «проснулись» и начали приём, а потом уже «думали», не их ли адрес к ним пришёл. Условие СТАРТ генерируется путём перехода из высокого логического состояния шины SDA в низкое (отрицательного фронта), а затем черз некоторое время должно то же самое произойти и с шиной SCL. Вот тогда ведущий и поймёт, что по шине началась какая-то передача. А если контроллер у нас ведомый, то мы наоборот должны отследить данный процесс на наших проводах.

Но у нас шина аппаратная и париться на этот счёт нам не нужно, ибо всё контроллер сделает сам.

Соответственно, как мы видим из графика наверху, условие СТОП генерируется наоборот. Сначала положительный фронт на шине SCL, а затем на SDA.

 

 

В случае, если контроллер у нас ведомый, то мы, для того, чтобы сгенерировать условие СТАРТ, должны сделать следующее.

Вот таким вот образом у меня подключен модуль с микросхемой

 

image13

 

Вернёмся в проект и напишем для генерации условия СТАРТ отдельную функцию в файле twi.c

 

void I2C_StartCondition(void)

{

  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

  while(!(TWCR&(1<<TWINT)));//подождем пока установится TWIN

}

 

Вот такая вот интересная функция. Что же здесь происходит?

А происходит следующее.

Мы сначала устанавливаем определённые биты в регистре управленя, говоря при этом шине о том, что мы посылаем условие СТАРТ (TWSTA), а также запускаем шину (TWEN). Ну а бит TWINT мы соответственно устанавливаем в единицу. А в ноль он, соответственно установится тогда, когда данное задание закончится. Вот для этого и существует вторая строка, где мы висим в цикле, пока он, собственно, и не установится. Именно в ноль! А не в единицу. Об этом говорит восклицательный знак в условии.

Давайте сразу и воспользуемся данной функцией, соответственно сначала создав на неё прототип, а затем вызвав в функции main(). Сначала мы, конечно, будем во внешнюю память EEPROM писать, так как читать из неё ещё нечего. Поэтому начнём в main() писать следующий код

 

I2C_Init();

 

//Чтение

I2C_StartCondition(); //Отправим условие START

 

Ну и, раз уж у нас есть чем, то давайте считаем статус операции и посмотрим успешно ли всё у нас прошло.

Для этого мы просто отправим значение статусного регистра в шину USART. Младшие три бита мы маскировать не будем, они у нас и так все в нулях.

 

I2C_StartCondition(); //Отправим условие START

USART_Transmit(TWSR);//читаем статусный регистр

 

Запустим терминальную программу, нажмем там Connect, соберём код и прошьём контроллер.

И вот мы что там видим

 

image15

 

Посмотрим в таблице данный статус

 

image16

 

То есть условие СТАРТ у нас сгенерировано и отправлено.

Остальные эксперементы с шиной I2C мы будем проделывать в следующей части.

 

 

Предыдущая часть Программирование МК AVR Следующая часть

 

Техническая документация на микросхему AT24C32

 

Программатор и модуль RTC DS1307 с микросхемой памяти можно приобрести здесь:

Программатор (продавец надёжный) USBASP USBISP 2.0

Модуль RTC DS1307 с микросхемой памяти

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR Интерфейс TWI (I2C)

6 комментариев на “AVR Урок 16. Интерфейс TWI (I2C). Часть 3
  1. Иван:

    Согласно кода TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); мы получаем TWCR = 10100100, а в строке while(!(TWCR & (1 << TWINT))); получается 10100100 & 10000000 даст результат 10000000, а это true и знак "!" даст false, что мы ничего ждать не будем, а вообще пропустим цикл. Получается лишний знак "!" или что то я тоже не так понимаю.

  2. Андрей:

    Здравствуйте! Писал код буква в букву, но при запуске в терминал ничего не выводит (USART работает корректно). Убираю цикл while(!(TWCR&(1<<TWINT))); и в терминал выводит F8. Не могу понять в чем проблема. Все вроде работает, но он почему то не выходит из этого цикла. Пробовал на разных мк (все atmega8) и с разными программаторами.

  3. TWWC – бит устанавливается при попытке записи в регистр данных TWDR, а не в адресный буфер.

    См. фирменную документацию:

    The TWWC bit is set when attempting to write to the TWI Data Register – TWDR when
    TWINT is low.

    И ещё, к стати! Там же — «при попыткЕ», а не «при попыткИ».

  4. И ещё! Вот тут:

    ЦИТАТА
    Ну а бит TWINT мы соответственно устанавливаем в единицу. А в ноль он, соответственно установится тогда, когда данное задание закончится. Вот для этого и существует вторая строка, где мы висим в цикле, пока он, собственно, и не установится. Именно в ноль! А не в единицу.
    КОНЕЦ ЦИТАТЫ

    Смотрим документацию:
    ЦИТАТА
    This bit is set by hardware when the TWI has finished its current job…
    The TWINT Flag must be cleared by software by writing a logic one to it.
    КОНЕЦ ЦИТАТЫ

    ПЕРЕВОД:
    Этот бит устанавливается аппаратно, когда TWI закончит свою работу…
    Флаг TWINT очищается программным спососом с помощью записи в него логической единицы.
    КОНЦ ПЕРЕВОДА

    Программный код правильный. Не правильное только его толкование.
    Ну, как же Вы так!

  5. Гарик:

    А в случае если к мк подключено несколько устройств по I2C. Как определить что подключено определённое устройство? Например у меня три микросхемы памяти 24сХХХ и мне нужно сначала проверить подключена ли одна из них к I2C.

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

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

*