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

 

Урок 16

Часть 6

 

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

 

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

А сегодня мы уже это проверим точно, считав данные из микросхемы. Соответственно здесь всё будет не совсем просто. Функция и вообще процедура чтения данных по шине I2C не совсем простая, есть свои нюанся. Но, думаю, используя наш предыдущий опыт, мы обязательно с ней справимся.

Откроем следующую таблицу в технической документации на микросхему

 

image22

 

Это диаграмма считывания из определённой ячейки памяти одного байта.

Она чем-то похожа на процедуру записи, но не совсем это так.

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

Затем, как ни странно, опять условие СТАРТ. Именно так. Затем уже передаём адрес устройства с битом чтения (1), подтверждение, затем после подтверждения читаем данные из регистра данных, а затем уже подтвержденя не ждём, то есть генерируем бит без подтверждения, то есть шина данных становится в логический 1, а затем условие СТОП. Вот так.

Так что, как видите, здесь не совсем всё так просто.

И это всё при условии, что надо нам считать только один байт. Это удобно, если мы тестируем память на случайное чтение, постоянно читая какой-то случайный бит, поэтому и диаграмма названа Random Read.

Но в большинстве случаев нам требуется сразу считать несколько байт, расположенных последовательно в ячейках памяти. Пример из жизни: данный тип микросхем очень широко применяется в спутниковых рессиверах и T2-приёмниках и у нас, например глюканула эта микросхема, что происходит отнюдь не редко. А в памяти данной микросхемы находятся все практически настройки. Мы берем живую микросхему из другого такого же устройства и считываем оттуда прошивку. Вот такая процедура тут уже и пригодится. Затем уже мы эту считанную прошивку либо загружаем в микросхему со сбойными данными, либо в новую, на которую сбойную потом меняем.

Для такой процедуры существует другая диаграмма в даташите (нажмите на картинку для увеличения размера)

 

image23_0500

 

Данная диаграмма нам показывает, как нужно читать байты целыми блоками.

Начало не отличается от предыдущей процедуры, а затем мы начинаем считывать каждый байт, после каждого считанного байта ждём подтверждение, а вот после последнего уже не ждём, то есть генерируем условие NO ASK. Как это проделать, разберёмся по мере написания функций.

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

Скопируем в данную процедуру то, что будет вообще без изменения

 

//Чтение

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

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

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

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

I2C_SendByte(0);//передаем старшую часть адреса ячейки памяти

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

I2C_SendByte(0);//передаем младшую часть адреса ячейки памяти

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

 

После передачи младшего байта адреса памяти мы генерируем условие СТАРТ

 

I2C_SendByte(0);//передаем младшую часть адреса ячейки памяти

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

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

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

 

Затем ещё раз передаём адрес устройства, но уже с установленным битом чтения (1)

 

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

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

I2C_SendByte(0b10100001);//передаем адрес устройства и бит чтения (1)

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

 

Далее мы должны будем уже читать байты из шины. Только, к сожалению, у нас для этого пока нет функции.

 

 

Но не будем этого бояться и напишем данную функцию, так как она будет подобна функции, применяемой для записи, за некоторым отличием. Напишем данную функцию в файле eepromext.c.

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

 

#include «eepromext.h»

char err1=0;// сюда вернем ошибку

 

Теперь непосредственно сама функция чтения

 

unsigned char EE_ReadByte(void)

{

  err1=0;

  TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);//включим прием данных

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

  if ((TWSR & 0xF8) != TW_MR_DATA_ASK) err1=1;

  else err1=0;

  return TWDR;

}

 

Главное отличие данной функции от функции чтения состоит в том, что мы теперь не заносим никаких данных в регистр TWDR, а, наоборот возвращаем считанное значение из него в качестве возвращаемого аргумента функции.

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

Также затем мы ждём установки флага в 0 по окончанию заполнения регистра TWDR, затем проверяем статус и возвращаем значение регистра данных TWDR.

Вот, в принципе и вся функция. Но данная функция нам подходит для чтения не всех байтов. Байт последний, как мы знаем читается по-особому, поэтому для него будет похожая, но несколько другая функция

 

unsigned char EE_ReadLastByte(void)

{

  TWCR = (1<<TWINT)|(1<<TWEN);//включим прием данных

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

  if ((TWSR & 0xF8) != TW_MR_DATA_NASK) err1=1;

  else err1=0;

  return TWDR;

}

 

Здесь практически всё один в один, за исключением лишь того, что мы не включаем бит TWEA, разрешающий подтверждение от ведомого, а также статус отслеживаем тоже другой — 0x58.

 

 

Ну вот. Соответственно, на все наши функции должны быть прототипы в файле eepromext.h

 

int EE_WriteByte(unsigned char c);

unsigned char EE_ReadByte(void);

unsigned char EE_ReadLastByte(void);

#endif /* EEPROMEXT_H_ */

 

Идём теперь в функцию main() и продолжим код.

Мы также здесь можем использовать цикл, но в нём мы считаем только 31 байт, так как 32-й мы считываем другой функцией

 

I2C_SendByte(0b10101111);//передаем адрес устройства и бит чтения (1)

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

for(i=0;i<=30;i++)

{

  bt[i] = EE_ReadByte(); //прочитаем байт из микросхемы

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

}

 

Также мы будем сначала смотреть статусы операций, а не сами считанные байты, которые мы, как видим, складываем в наш массив.

Считаем последний байт, используя соответствующую функцию

 

  bt[i] = EE_ReadByte(); //прочитаем байт из микросхемы

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

}

bt[31] = EE_ReadLastByte(); //прочитаем байт из микросхемы

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

 

Ну и теперь сгенерируем условие СТОП

 

bt[31] = EE_ReadLastByte(); //прочитаем байт из микросхемы

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

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

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

 

Давайте пока не будем смотреть принятые байты, а, запустив терминальную програму, запустив в ней соединение, собрав код и прошив контроллер, посмотрим статусы

 

image24

 

Давайте найдём все новые статусы в таблице. 0x08, 0x18, 0x28 и 0xF8 мы уже знаем из прошлой части занятия. Осталось нам разобраться со статусами 0x10, 0x40, 0x50 и 0x58.

Для этого соберём их из таблицы даташита нашего контроллера

 

image25

 

0x10 — Повторное условие СТАРТ передано

0x40 — Адрес ведомого устойства плюс бит чтения передан и подтверддён ведущим

0x50 — Байт данных из ведущего был принят, подтверждение возвращено

0x58 — Байт данных из ведущего был принят, возвращено «нет подтверждения».

Как видим, всё у нас правильно, всё соответстсвует техничегской документации. Осталось нам только посмотреть принятые байты

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

 

for(i=0;i<=30;i++)

{

  bt[i] = EE_ReadByte(); //прочитаем байт из микросхемы

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

}

bt[31] = EE_ReadLastByte(); //прочитаем байт из микросхемы

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

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

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

for(i=0;i<=31;i++)

{

  USART_Transmit(bt[i]); //отправим считанные байты в ПК

}

 

Соберём код, прошьём контроллер и прочитаем результат чтения в терминале

 

image26

 

Мы видим здесь все наши байты, то есть процедура чтения также прошла удачно, а также она показала и удачность процедуры записи.

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

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

 

 

 

Исходный код

 

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

 

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

Программатор USBASP USBISP с адаптером USBASP USBISP 3.3 с адаптером

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

 

 

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

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

 

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

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

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

  1. Здравствуйте! Стало интересно, как еепромка «узнает», какой байт считывания должен стать последним?! Я пробую прочитать всю страницу памяти (32байта), в результате читается только первые 28… Байты начина с адреса 0х1с(28) можно считать, если начало чтение установить с этого адреса! Что за магия?

  2. О… Дайте мне яду! Если в Вашем исходнике, в массиве записать числа от 0 до 31, то результат будет такой, как я писал выше! Кстати, исправьте массив с индексом 14(два раза повторяется).

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

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

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

*