Урок 16
Часть 3
Интерфейс TWI (I2C)
В предыдущей части занятия мы продолжили знакомство с шиной I2C и уже создали проект для того, чтобы занятия проработать на практике. Также мы познакомились с интересой микросхемой EEPROM AT24C32, работающей на данной шине.
Продолжаем изучение шины.
Шину мы инициализировали в проекте, выставив ей скрость с помощью регистра TWBR, теперь давайте поближе познакомимся с управляющим регистром – TWCR
Как мы видим, в данном регистре присутствует насколько битов.
TWINT – это бит прерываний. Можно его назвать битом не управляющим, а статусным, так как устанавливается он аппаратно в тот момент, когда определённое задание на шине завершится и будет ожидаться реакция программы. А вот сбрасывается данный флаг не аппаратно, а только программно – записью в него логической 1.
TWEA – бит или флаг, разрешающий подтверждение. Если мы его не установим, то мы не будем просить подтверждение от ведомого устройства, а в случае, если контроллер наоборот является ведомым устройством, то с очередной посылкой мы не отправим бит подтверждения в конце какой-то посылки.
TWSTA – бит установки или генерирования условия "Старт".
TWSTO – бит установки или генерирования условия "Стоп".
TWWC – бит ошибочной записи. Устанавливается при попытки записи в адресный буфер, когда флаг TWINT ещё не установился. Ещё называется он флагом коллизий. Данный бит сбросится, когда TWINT будет равен 1.
TWEN – бит, активирующий шину I2C. Если мы его устанавливаем, то шина I2C начинает пытаться выполнять задание в зависимости от условий.
TWIE – бит, который разрешает прерывания.
Теперь статусный регистр TWSR
Пять старших битов регистра содержат код статуса операции, которая выполнялась перед тем, как мы читаем регистр. Как правило мы с помощью маскирования сбрасываем три младших бита в 0, ну конечно не в самом регистре, а в переменной, в которую мы его считали и затем уже исследуем полученный результат. В даташите на контроллер содержится перечень в виде нескольких таблиц различных кодов статуса. Несколько их потому, что есть несколько условий – ведущее или ведомое устройство и чтение или запись происходила.
Ну а два младших бита – TWPS1 и TWPS0 – это биты для делителя частоты шины, с которыми мы уже знакомились немного в 1 части занятия.
Вот такие вот могут быть варианты комбинаций данных битов и зависимость делителя от этих комбинаций
Займёмся теперь непосредственно шиной.
Как же всё работает:
Нам для начала любой посылки необходимо сгенерировать условие СТАРТ, чтобы ведущие устройства "проснулись" и начали приём, а потом уже "думали", не их ли адрес к ним пришёл. Условие СТАРТ генерируется путём перехода из высокого логического состояния шины SDA в низкое (отрицательного фронта), а затем черз некоторое время должно то же самое произойти и с шиной SCL. Вот тогда ведущий и поймёт, что по шине началась какая-то передача. А если контроллер у нас ведомый, то мы наоборот должны отследить данный процесс на наших проводах.
Но у нас шина аппаратная и париться на этот счёт нам не нужно, ибо всё контроллер сделает сам.
Соответственно, как мы видим из графика наверху, условие СТОП генерируется наоборот. Сначала положительный фронт на шине SCL, а затем на SDA.
В случае, если контроллер у нас ведомый, то мы, для того, чтобы сгенерировать условие СТАРТ, должны сделать следующее.
Вот таким вот образом у меня подключен модуль с микросхемой
Вернёмся в проект и напишем для генерации условия СТАРТ отдельную функцию в файле 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, соберём код и прошьём контроллер.
И вот мы что там видим
Посмотрим в таблице данный статус
То есть условие СТАРТ у нас сгенерировано и отправлено.
Остальные эксперементы с шиной I2C мы будем проделывать в следующей части.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация на микросхему AT24C32
Программатор и модуль RTC DS1307 с микросхемой памяти можно приобрести здесь:
Программатор (продавец надёжный) USBASP USBISP 2.0
Модуль RTC DS1307 с микросхемой памяти
Смотреть ВИДЕОУРОК (нажмите на картинку)
Согласно кода TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); мы получаем TWCR = 10100100, а в строке while(!(TWCR & (1 << TWINT))); получается 10100100 & 10000000 даст результат 10000000, а это true и знак "!" даст false, что мы ничего ждать не будем, а вообще пропустим цикл. Получается лишний знак "!" или что то я тоже не так понимаю.
Здравствуйте! Писал код буква в букву, но при запуске в терминал ничего не выводит (USART работает корректно). Убираю цикл while(!(TWCR&(1<<TWINT))); и в терминал выводит F8. Не могу понять в чем проблема. Все вроде работает, но он почему то не выходит из этого цикла. Пробовал на разных мк (все atmega8) и с разными программаторами.
Разобрался благодаря вашему комментарию на ютубе. Нужно был подключить переферию)