» » » Программная реализация протокола I2C на AVR в CodeVisionAVR

 
 
 
9

Программная реализация протокола I2C на AVR в CodeVisionAVR

Разместил Electronik83 27 августа 2016. Просмотров: 3 082

Программная реализация протокола I2C на AVR в CodeVisionAVR Пару лет назад, изучая Atmega8, я захотел программно реализовать работу с устройствами на шине i2c, но как-то не задалось. С аппаратным i2c, напротив, не было никаких проблем и затея как-то забылась.

Но недавно я прочитал статью на Датагоре «Несколько функций для програмной реализации протокола I2C на AVR», в которой автор выложил свой пример программной реализации протокола под IAR. Я в тот же миг переписал всё в CodeVision, но, к сожалению, у меня опять не заработало как надо.
Решил набраться терпения и разобраться сам.

Разбираемся с протоколом i2c

Любой протокол можно разобрать по кирпичикам. Так вот, в данном протоколе таким «кирпичиком» является передача/приём одного бита. Схематично передачу одного бита я изобразил на рисунке.


На этом рисунке я изобразил поведение линии SCL при передаче одного бита и длиной в один такт, а также пояснил некоторые понятия. С SCL вроде бы всё ясно, надо разобраться с SDA.
Так вот, при записи бита, надо на линии SDA выставлять значение, в соответствии с содержанием этого бита. Причём делать это лучше всего в середине паузы. Сразу появился вот такой код:
// пишем бит в шину
void i2c_wr_bit(char b) {
delay_us(i2c_time/2); // формируем первую половину паузы
if (b) wrsda; else rdsda;  // пишем бит...    
delay_us(i2c_time/2); // завершаем паузу
rdscl;                // выдаем фронт в линию SCL     
delay_us(i2c_time);   // формируем сам импульс
wrscl;                // выдаем спад в линию SCL
}


Далее рассмотрим чтение бита из шины. Нужно точно также протактировать линию SCL и прочитать значение линии SDA сразу после фронта импульса SCL. Вот код:
// читаем бит из шины
char i2c_rd_bit() {
char ret=1;          // временая переменная
rdsda;               // SDA - на чтение
delay_us(i2c_time);  // формируем паузу между импульсами  
rdscl;               // выдаем фронт SCL    
if (i2c_read) ret=0; // читаем SDA                                 
delay_us(i2c_time);  // формируем фронт
wrscl;               // формируем спад SCL
return ret;          // вернули, что прочитали...
}


Теперь пора рассмотреть все дефайны, которых, как известно, мало не бывает:
#include <mega328p.h>  // нужно для Атмегушки
#include <delay.h>     // нужно для задержек
// задержка в микросекундах (2 задержки на такт передачи)  
#define i2c_time 10 
//i2c порт
#define sdaprt PORTB.0 // порт для SDA         
#define sdaddr DDRB.0  // направлене для SDA 
#define sdapin PINB.0  // для чтения SDA
#define sclprt PORTB.1 // порт для SCL
#define sclddr DDRB.1  // направление для SCL                           
// управление битами шины
#define clrsda sdaprt=0; // переводим в 0 линию SDA
//#define setsda sdaprt=1; // оставил на всякий случай
#define clrscl sclprt=0; // переводим в 0 линию SCL 
//#define setscl sclprt=1; // оставил на всякий случай
#define rdscl  sclddr=0; // переводим SCL линию на вход
#define wrscl  sclddr=1; // переводим SCL линию на выход
#define rdsda  sdaddr=0; // переводим SDA линию на вход
#define wrsda  sdaddr=1; // переводим SDA линию на выход 
#define i2c_read sdapin  // для чтения SDA


Далее нужно научиться передавать байт. Тут ничего сложного:
// передача байта на шину
char i2c_wr(char data)  {
  //Функция возвращает 1, если есть ответ ACK
  char i; // счетчик для цикла. Если объявлять глобально, могут быть глюки
  for (i=0; i<8; i++) {         // выдаем в шину 8 бит
    i2c_wr_bit((data&0x80)==0); // отдаем бит
    data<<=1;  }                // двигаем байт
  // читаем «акнолидж» (подтверждение приёма)
  return i2c_rd_bit();          // вернули, что прочитали  
}


Хотелось бы пояснить, что шина i2c устроена так, что при передаче байта мы должны отправить восемь бит данных и потом прочитать бит — так называемый «акнолидж» (ack). Это такой бит подтверждения, посылаемый устройством на шине, который сигнализирует нам, что устройство благополучно «проглотило» наш байт.
При работе с устройствами этот бит, как правило, говорит устройству о конце/продолжении передачи при чтении нескольких байт из него. Так же иногда этот «акнолидж» надо иногда передавать устройству, особенно при чтении данных с него.

С записью байта понятно, разберемся с чтением. Тут ситуация в точности наоборот — мы сначала читаем восемь бит байта, а потом выдаем этот «акнолидж».
Реализация приема байта:
// прием байта с шины
char i2c_rd(char a)  {
  //если ack=1, то выдается подтверждение ack в шину
  char i, data=0;                // счетчик и принимаемый байт
  for (i=0; i<8; i++) {          // цикл приема бит
    if (!i2c_rd_bit()) data++;   // читаем бит
    if(i!=7) data<<=1;  }        // двигаем байт
  i2c_wr_bit(a);  // выдаем «акнолидж» (подтверждение приема)
  return data;    // вернули, что прочли
}


Теперь пропишем пресловутые функции старт/стоп, как требует протокол:
// старт i2c
void i2c_go(void)  {
  delay_us(i2c_time);  // ждем, пока предидущие сигналы устаканятся
  rdsda; rdscl;        // отпускаем шину
  delay_us(i2c_time);  // формируем паузу:)
  wrsda;  clrsda;      // давим SDA и ставим 0
  delay_us(i2c_time);  // задержка...
  wrscl;  clrscl;      // давим SCL и ставим 0
  delay_us(i2c_time);  // задержка...
}
// стоп i2c
void i2c_end(void)  {
  wrsda;               // давим SDA
  delay_us(i2c_time);  // ждем..
  rdscl;               // давим SCL
  delay_us(i2c_time);  // ждем..
  rdsda;               // отдаем SDA
}


Проверим код с помощью записи/считывания AT24C08

Для проверки я решил использовать Ардуино Nano и микросхему памяти AT24C08. Обратите внимание, аналоги, например чип ST24C08, имеют немного другую распиновку, сверяйтесь с даташитами.
Для контроля работы сначала хотел использовать последовательный порт, но потом решил, что это лишнее и решил обойтись обычным светодиодом, ведь он уже распаян на Ардуинке. Работу с микросхемой AT24C08 расписывать не буду, всё есть в документации к ней.

Идея проверки состоит в том, что мы сначала записываем в микросхему два байта кода, а потом читаем их и сравниваем с оригиналом. Если записанные и считанные байты совпадают, то мы зажигаем светодиод. Это и будет говорить о полной работоспособности протокола.


Теперь код:
// главная функция
void main(void)  {
DDRB.5=1;
delay_ms(100);   // ждем, пока питание устаканится
while (1)  {
char t=0;
delay_ms(100); // ждем немного
PORTB.5=0; // гасим тестовый светодиод
// пишем байт 0xF7 в память микросхемы по адресу 05
i2c_go();
i2c_wr(0xA0);  // i2c адрес микросхемы
i2c_wr(0x05);  // адрес в памяти
i2c_wr(0xF7);  // данные
i2c_end();
// пишем байт 0x3B в память микросхемы по адресу 06
i2c_go();
i2c_wr(0xA0);  // i2c адрес микросхемы
i2c_wr(0x06);  // адрес в памяти
i2c_wr(0x3B);  // данные
i2c_end();
// читаем память..
i2c_go();
i2c_wr(0xA0);  // i2c адрес микросхемы
i2c_wr(0x05);  // адрес для чтения
i2c_go();      // так надо........
i2c_wr(0xA1);  // запуск чтения
if (i2c_rd(1)==0xF7) t++; // принимаем данные с линии
if (i2c_rd(0)==0x3B) t++; // аск передаем 0, т.к. это последний байт для чтения
i2c_end();
// если все данные прочитаны верно
if (t==2) PORTB.5=1;  // включаем тестовый светодиод
}
}


Написанный код я сначала тестирую в Протеусе (ISIS 7 Proteus). Привожу осциллограмму работы:


На осциллограмме видно, что синхросигнал линии SCL получен ровный, данные на линии SDA выставляются верно. Но есть и минус: всплески сигнала на линии SDA. Я долго выяснял, откуда они взялись. Оказалось, что этот всплеск возникает сразу после передачи/приёма бита «акнолидж» (ACK). Устранить этот недостаток мне так и не удалось, оставил, как есть. На работу данный всплеск не должен влиять.
Может на досуге ещё подумаю над этим или кто-нибудь из опытных читателей подскажет в комментариях.

Тестируем протокол в железе

Переходим к тесту в железе. Воткнул в беспаечную макетную плату (breadboard) платку ARDUINO NANO, микросхему AT24C08 и соединил всё проводками. Я для прошивки Ардуинки  использую стандартный Ардуиновский бутлоадер (загрузчик) и программу XLoader, а вы можете использовать привычный вам способ.

При первом запуске у меня пошел дым. Что-то не так! Оказалось, что я перепутал распиновку микросхемы памяти AT24C08. Обидно. В Ардуино у меня сгорел диод по питанию и микросхема памяти вышла из строя. Я заменил и то и другое.

Прошил, включаю: светодиод «молчит». Потыкал тестером и понял, что совершенно забыл про подтягивающие резисторы. Воткнул их. Молчок. Немного подумав и покурив даташит на AT24C08, подал на вывод памяти WP (Write Protect — защита записи, pin 7) не ноль, а единицу.

И, вуаля, всё заработало:


Итого

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

Всем удачи, а меня с почином. Это первая моя статья.
В скором времени планирую написать статью о протоколе SPI.
Павел Сумароков (Electronik83)
РФ, Северодвинск
Профиль Electronik83
Радиоэлектроника, программирование.
 

Понравилось? Палец вверх!

  • всего лайков: 32

Поделись с друзьями!

Связанные материалы:


Схема на Датагоре. Новая статья Программная реализация протокола SPI на AVR в CodeVisionAVR... Всем коллегам и согражданам привет! Увлёкся я изучением протоколов. Про реализацию протокола I2C...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 2. TFT дисплей 1.8" (128х160) на ST7735... Следующий из рассматриваемых нами модулей обладает полноцветным дисплеем под управлением...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 1. OLED дисплей 0.96" (128х64) на SSD1306... Добрый день, друзья! Эта статья открывает цикл, посвящённый средствам визуального отображения...
Схема на Датагоре. Новая статья Беспроводной канал связи 2,4 ГГц на базе трансивера nRF24L01+ от Nordic Semiconductor. Часть 1... Доброго вам дня, уважаемые граждане и гости Датагор.ру - этого замечательного сообщества...
Схема на Датагоре. Новая статья Несколько функций для програмной реализации протокола I2C на AVR... Добрый день, дорогие друзья! Решил поделиться с вами несколькими функция для работы по протоколу...
Схема на Датагоре. Новая статья Немного про шину 1-Wire и цифровой термометр DS18b20... Фотка от www.150cc.ru Здравствуйте, друзья. Хочу предложить вашему вниманию несколько простых...
Схема на Датагоре. Новая статья Цифровой регулятор громкости с опторазвязкой цифровой и аналоговой части... Фото 1. Собранный регулятор Думаю, каждый, кто занимался сборкой усилителя, сталкивался с выбором...
Схема на Датагоре. Новая статья Блок управления аудиоусилителем с лестничным регулятором громкости и ДУ... Сделал я усилитель SE на ГУ-50 и как обычно встал вопрос о регуляторе громкости. Ставить обычный СП...
Схема на Датагоре. Новая статья Библиотеки для подключения COG-индикаторов TIC-55 и TIC-3321 к микроконтроллерам AVR... …простите, а вы не подскажите, как пройти в библиотеку? Небольшая предысторияПрежде, я уже имел...
Схема на Датагоре. Новая статья Цифровое телевидение, ч.1... Говорят про это много, но техническую сторону вопроса освещают нечасто. Поскольку работа моя...
Схема на Датагоре. Новая статья Проект DMX512. Микроконтроллер управляет профессиональным шоу. Ч.1... DMX512 (англ. Digital Multiplex) - стандарт, описывающий метод цифровой передачи данных между...
Схема на Датагоре. Новая статья Операционные усилители. Ч.3 Компараторы, триггеры, мультивибраторы.... Источник: Радиокот.ру И, наконец, третья, заключительная часть статьи про Operational Amplifiers!...
<
  • Главный редактор
28 августа 2016 12:24

Игорь Петрович Котов / Datagor

  • С нами с 25.02.2011
  • Ушёл в реал Пользователь offline
  • 1 631 комментарий
  • 261 публикация
 
  • 0
Павел, действительно с почином! handshake
Подробно, понятно, с хорошей практической частью.

<
  • Гражданин
28 августа 2016 12:38

Ербол / erbol

  • С нами с 11.12.2014
  • Ушёл в реал Пользователь offline
  • 59 комментариев
  • 8 публикаций
 
  • +1
Спасибо, Павел! handshake
Знания, которые приходят, когда "набираешься терпения и разбираешься сам" остаются если не навсегда, то очень надолго. Да и объяснения таких знаний всегда понятны для других, как в этой статье smile

<
  • Гражданин
28 августа 2016 16:11

Радик / galrad

  • С нами с 23.08.2011
  • Ушёл в реал Пользователь offline
  • 84 комментария
  • 12 публикаций
 
  • 0
Хорошая статья, частично закрывающая пробел в освоении механизмов программирования микроконтроллеров. Самое главное - простота изложения материала, с возможностью практического применения! Для последовательной передачи данных нужно освоить 3 протокола - SPI, I2C (не путайте с I2S), UART (USART). Отдельным пунктом можно назвать USB и CAN, но это уже для особо продвинутых... smile
Самый быстрый (и самый легкий - протокол SPI, протокол I2C не самый быстрый но, более селективный, позволяющий работать одновременно нескольким устройствам, UART - это привычный COM порт).
Очень надеюсь, что Павел не обойдет стороной указанные протоколы последовательной передачи данных.
Павел, статья зачетная, Спасибо!

<
  • Главный редактор
28 августа 2016 22:53

Игорь Петрович Котов / Datagor

  • С нами с 25.02.2011
  • Ушёл в реал Пользователь offline
  • 1 631 комментарий
  • 261 публикация
 
  • 0
В работе вторая статья Павла, об SPI протоколе.

<
  • Гражданин
29 августа 2016 01:58

Ербол / erbol

  • С нами с 11.12.2014
  • Ушёл в реал Пользователь offline
  • 59 комментариев
  • 8 публикаций
 
  • 0
Павел, Вы не поднимали апноут AVR156? Возможно, там найдётся подсказка по всплескам SDA

<
  • Подписчик
31 августа 2016 10:09

Игорь / StalKer-NightMan

  • С нами с 15.03.2012
  • Ушёл в реал Пользователь offline
  • 90 комментариев
  • 1 публикация
 
  • 0
Павел, спасибо, хорошо изложен материал.
Ждем продолжения.

<
  • Кандидат
4 сентября 2016 04:10

Павел Сумароков / Electronik83

  • С нами с 29.08.2015
  • Ушёл в реал Пользователь offline
  • 4 комментария
  • 2 публикации
 
  • 0
Я не знаю, что такое апноут..

<
  • Главный редактор
4 сентября 2016 22:00

Игорь Петрович Котов / Datagor

  • С нами с 25.02.2011
  • Ушёл в реал Пользователь offline
  • 1 631 комментарий
  • 261 публикация
 
  • 0
Павел, апноут = appnote = application note. Типа, заметка о применении. Нырок в практику.
Выпускаются всеми производителями на равне с даташитами.

Ищется по фразе "appnote + номер"
Atmel AVR156: TWI Master Bit Bang Driver
//www.atmel.com/images/doc42010.pdf

<
  • Гражданин
5 сентября 2016 00:24

Ербол / erbol

  • С нами с 11.12.2014
  • Ушёл в реал Пользователь offline
  • 59 комментариев
  • 8 публикаций
 
  • 0
Павел, к пояснениям Игоря могу добавить следующее. Если зайти на сайт Atmel и в поиске набрать интересующую тему (например, twi, uart, spi или любую другую), то получите список материалов, которыми компания располагает по теме - даташиты, апноуты (заметки) и пр. В этих апноутах достаточно подробно, с приложением кода на Си или ассемблере, излагается тот или иной вопрос. Очень полезная штука, на мой взгляд

Информация
Вы не можете участвовать в комментировании. Вероятные причины:
— Администратор остановил комментирование этой статьи.
— Вы не авторизовались на сайте. Войдите с паролем.
— Вы не зарегистрированы у нас. Зарегистрируйтесь.
— Вы зарегистрированы, но имеете низкий уровень доступа. Получите полный доступ.