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

 

 

 

 

Урок 16

Часть 4

 

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

 

 

В предыдущей части занятия мы поближе познакомились со всеми практически регистрами шины TWI и уже написали код отправки в шину условия СТАРТ. Причем не только написали, а ещё и проверили отправку, считав данные из регистра статуса через порт USART. Также мы бегло ознакомились с протоколом передачи и приема. Если нам впоследствии что-то потребуется, то мы к нему ещё вернёмся.

Будем теперь думать, как нам ещё что-нибудь в шину I2C отправить. Для этого будут ещё нужны некоторые функции.

Раз уж есть функция для условия СТАРТ, то, конечно же, нужна функция для передачи условия СТОП.

Поэтому зайдём в файл twi.c и напишем её, скопировав полностью предыдущую функцию и немного её подправив

 

void I2C_StopCondition(void)

{

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

}

 

Здесь только изменился бит TWSTA на TWSTO, а также в этом случае мы уже никакого подтверждения от ведомого не ждём.

Также нам нужно написать функцию для передачи байта в шину I2C

 

void I2C_SendByte(unsigned char c)

{

  TWDR = c;//запишем байт в регистр данных

  TWCR = (1<<TWINT)|(1<<TWEN);//включим передачу байта

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

}

 

Здесь мы сначала записываем байт, предназначенный для отправки, в регистр TWDR, затем запустим передачу байта, установив биты TWINT и TWEN и здесь мы уже ждём подтверждения от ведомого устройства. То есть, если мы передаем адрес устройства, то если среди всех присутствующих на шине найдётся именно владелец данного адреса, то он и установит шину в низкое состояние, после чего контроллер, поняв это, установит бит TWINT в ноль, ну, или сбросит его.

Также напоминаю о необходимости добавления прототипов на данные функции в файле twi.h.

Теперь продолжим, используч вышенаписанные функции, писать код в функцию main(), чтобы нам удостовериться, что у нас шина I2C исправна и устройство, подключенное к ней, нормально откликается.

Также для достижения данной цели нам неохдодимо изучить, как именно нужно передавать данные нашей микросхеме EEPROM. Для этого зайдём в техническую документацию и посмотрим следующую картинку

 

image17

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

Только нам вовсе не интересно в данную память передавать по одному байту данных, поэтому существует ещё одна картинка в даташите

 

image18

 

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

 

 

Ну, давайте же что-то попытаемся записать в микросхему. Также после каждой операции будем смотреть статус её выполнения. Напишем следующий код в функцию main()

 

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

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

I2C_SendByte(0b10100000); //передаем адрес и бит записи (0)

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

I2C_SendByte(0);//переходим на 0x0000 — старший байт адреса памяти

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

I2C_SendByte(0); // — младший байт адреса памяти

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

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

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

while(1)

 

Чем занимается данный код, прекрасно видно из комментариев.

Соберём код и прошьём контроллер.

Глянем в терминальную программу

 

image19

 

И теперь с помощью таблицы будем разбираться, что же нам "рассказал" регистр TWSR

 

image21

 

Вот коды, которые мы получали

0x08 — условие СТАРТ было передано

0x18 — адрес и бит записи был передан и подтверждение получено

0x28 — байт данных был передан и подтверждение получено

Адрес ячейки памяти для нашего контроллера таковым не считается, это для него обычные данные.

А 0xF8 — это типа ошибки. Ну нам уже это не важно. Скорей всего это потому, что после СТОП ведомый уже перед нами не отчитывается и флаг также не сбросится.

 

В следующей части занятия мы постараемся уже что-то в память микросхемы записать.

 

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

 

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

 

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

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

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

 

 

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

 

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

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

    А почему мы не используем прерывания? Ведь если у нас ведомый в процессе приёма отвалится, то мы навечно подвесим МК в этом цикле:

    while (!(TWCR & (1<<TWINT)));

    Но даже если ведомый примет посылку, получается, что всё  время передачи, несмотря на наличие аппаратного TWI наш микроконтроллер будет работать с частотой 100 кГц.

     

    Или я чего-то не понимаю?

  2. Сергей:

    Сделал все как указано, но у меня вместо 18 и 28 получаются соответственно 20 и 30, т.е. у меня отправляется не сигнал ACK, а сигнал NOT ACK. Что я делаю не так?

    • Флаг должен быть правильный установлен. Посмотрите урок по внешнему EEPROM, там мы используем и первый и другой вариант (и с подтверждением и без).

  3. Сергей:

    Добавление к предыдущему вопросу. Модуль спаял сам, с ардуинкой работаем без проблем — время показывает. Но сигнал возвращается именно NO ACK.

    • Сергей:

      Нашел проблему. Если NO ACK указывает, что по данному адресу устройство не обнаружено. Запустил на ардуинке i2c_scaner, который показал адрес ds1307 есть 0х68.  перевел в двоичное — 1101000, добавил 0 как флак записи. Т.е. получилось  I2C_SendByte(0b11010000); После этого получил долгожданные 18 и 28.

       

  4. Андрей:

    while (!(TWCR & (1<<TWINT)));получается что ожидания флага можно не делать?

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

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

*