Шина данных на avr

Easy_I2C AVR ASM практикум начало

ОТРЕДАКТИРОВАНО 22.12.2013 благодаря комментариям.
I2C это достаточно важный раздел который я не изучил в AVR Mega на ASMе. Вот и решил восполнить этот пробел. В этом посте не будет прикольных графиков, фоток, схем зато будет много унылого кода на ASMe. Врать не буду но времени ушло на изучение больше 2-х месяцев. Причин много — это и работа, и работа, и работа, и (ремонт на кухне)*100500, и изготовление отладочной платы с PCF8574+PCF8574+MCP23016+DS1307+AT24C512+AT24C512+PCF8591 поскольку многое хотелось попробовать.
Писать программный I2C я не решился поэтому все строилось на прерываниях. У работы на прерываниях есть много преимуществ и легко прикручивается к диспетчеру заданий(RTOS). За основу были взяты статьи учебного курса DIhalta и его помощников. Ну а куда же деться от книги А. В. Евстифеева которая стала настольной заменив Камасутру.
У DiHalta есть пример, но он написан на Си, а мне это мало о чем говорит, но тем не менее я от туда кое какие имена стащил.
Данная статья рассчитана на тех кто уже начал изучать I2C, на тех кто разобрался что такое sda scl ack nack TWCR TWDR TWBR. В общем это статья о зародыше практического применения I2C
И еще кое что, но не сочтите за рекламу. Если у вас много времени то конечно можно обратиться на местный форум за помощью и кто-то на вроде AlexPM даст достаточно грамотные советы (я искренне удивляюсь знаниям многих форумчан и даже завидую), а если времени нет то можно все быстро отладить самому имея недорогой логический анализатор (который покупается 1 раз а помогает ну очень сильно). Я сам его просто купил. У меня LOGIC-U.
Пока прерывание написано только для условий когда контроллер выступает в роли Мастера.
Теперь поясню какие задания на мой взгляд должно выполнять прерывание — 2 группы: отправка и прием данных
Каждая группа имеет 3 варианта:
1) простое обращение к устройству (например PCF8574)
2) обращение к устройству с предварительной записью номера регистра/страницы размером 1byte (например MCP23016 DS1307)
3) обращение к устройству с предварительной записью номера регистра/страницы размером 2byte (например AT24Cxx)
Итак начинаю с регистров оперативы которые обязательны для работы прерывания

Дальше стоит описать макрос который запускает прием или передачу данных по шине для наглядного понимания приведу сразу строки из проекта
I2C_GO_START_in и I2C_GO_START_out это названия макросов
DS1307 и PCF8574led адреса устройств к которым мы обращаемся (у меня это 0b11010000 и 0b01000110) здесь необходимо сказать что все адреса обязательно прописываются с младшим битом =0 те на запись а в прерывании все поправится как надо само сабой
i2c_sawsarp и i2c_sawp это задание по которому работает прерывание (тут у нас 2 группы по 3 варианта i2c_sarp, i2c_sawsarp, i2c_saw2sarp, i2c_sawp, i2c_sawsawp, i2c_saw2sawp)
Time_status и Led_reg это названия регистров в оперативке которые принимают/отправляют данные.
Следующий параметр это кол-во байт на прием/отправку
Последний параметр указывает на скорость с которой мы будем гонять биты по шине в кГц формула там незамысловатая и при частоте кварца 8 МГц имеет ограничение на скорость ниже 16 кГц те меньше 16 задать нельзя. Вообще я гонял пока что до 480 кГц (именно 480 кГц по показаниям лог анализатора) причем DS1307 про которую в даташите указано only 100 нормально тянет на этой скорости.
И еще один важный момент: если идет чтение/запись с предварительной записью номера регистра/страницы размером 1byte/2byte то перед макросом должна быть запись в регистр Х того самого номера регистра/страницы к которому мы обращаемся те для чтения например минут и часов из DS1307 в буфер оперативы под именем Time_status нам надо сделать так

Имя и размер буфера(ов) на прием/передачу можно сделать каким угодно но размещен он должен быть в оперативке
Идем дальше. Теперь расскажу как я организовал разрешение на запуск шины. У меня в оператве есть регистр

Таким образом у меня 8 бит — 8 флагов на запрос включения шины. Нулевой бит(флаг) я закрепил за PCF8574key, второй за PCF8574led а третий бит за DS1307. Дальше я опрашиваю каждый бит i2c_want_job и если бит =1 то сначала проверяется регистр i2c_busy (который отвечает за то что шина занята на прием/передачу, устанавливается и сбрасывается он самостоятельно и что-то в него записывать не надо ), если i2c_busy=0 то тогда можно запускать задание на прием/передачу. Поскольку PCF8574key достаточно важная микросхема то именно ей я и отдал самый младший бит а это значит самый высокий приоритет. Кстати этот бит ставится по внешнему прерыванию INT0 от самой PCF8574key — очень удобно
Теперь надо сказать то что я держу в файле Job_name_I2C

Читайте также:  Низкое давление в шинах что это

А теперь само прерывание размером 400 байт(примерно) но при желании можно урезать если понимать что надо урезать

Писать в этом посту наверное больше нечего
архив с файлами удалил поскольку нашлось пара ошибок но в посту их исправил. В следующих статьях файлы уже с исправлениями
В ближайшее время напишу как это все работает с реальными примерами на вышеупомянутых микросхемах
Если есть вопросы или пожелания то пишите, как смогу отвечу… Пошел я дальше ремонт на кухне делать.

Источник

Аппаратный I2C (TWI) в микроконтроллерах AVR

При наличии на борту AVR аппаратной реализации I 2 C почему-то многие предпочитают программные реализации. Хотя, на мой скромный взгляд — использование железного варианта проще, стабильнее и удобнее.

Применение встроенного интерфейса и подразумевает работу на прерываниях, но сегодня мы обойдемся без оных. Для понимания работы контроллера это несколько проще, а переложить код на использование прерываний не составит труда.

Описывать шину I 2 C не имеет смысла, исчерпывающие описание можно найти на википедии, Казусе и конечно у DI HALT’a. Последняя ссылка заслуживает особого внимания, там основное внимание уделяется именно AVR, но в качестве примеров используется RTOS, что несколько абстрагирует от последовательности работы. Именно для того, чтобы дополнить статью DI HALT’a (а так же, чтобы не забыть что и как самому) и была написана эта небольшая заметка.

Итака, шина I 2 C глазами микроконтроллера AVR как всегда представляет собой несколько регистров, а именно:

  • TWBR — TWI Bit Rate Register: В этом регистре настраивается частота (скорость) шины, так же на частоту влияет биты TWPS0..1 в регистре TWSR
  • TWCR — TWI Control Register: Через этот регистр происходит все управление шиной
  • TWSR — TWI Status Register: За исключением первых трех бит (TWPS0..1 и зарезервированного) — регистр отражает состояние шины
  • TWDR — TWI Data Register: Как не сложно догадаться — регистр данных. Именно из него данные уходят по шине, и именно в него контроллер помещает полученные байты.

Частота шины рассчитывается по формуле: FSCL = FCPU/(16+2(TWBR)*4 TWPS ). И это единственное, что нужно сделать при инициализации.

Вся работа TWI сводится к алгоритму:

  1. Записать значение в регистр TWCR (а при передачи данных предварительно поместить байтик в TWDR)
  2. Дождаться флага TWINT в том же регистре TWCR (при работе с прерываниями — этот флаг вызовет прерывание по-вектору TWI)
  3. Получить статус из регистра TWSR — в зависимости от статуса, что-то делать или не делать дальше.

К этой не хитрой последовательности сводится вся логика работы шины, формирование стартов-рестартов-стопов, передача байта, прием байта и формирование ответа ACK.

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

Функция получает требуемое действие в качестве аргумента, дожидается его выполнения и возвращает результат выполнения. Коды возврата довольно непонятно описаны в Datasheet, и очень хорошо у DI HALT’a (ссылка выше по тексту). Данные для передачи необходимо заранее загрузить в регистр TWDR перед выполнением

После успешного получения байта с помощью

так же следует напрямую прочитать из TWDR
Пример чтения (без всяких проверок и контроля выполнения) 128 байтов из EEPROM 24C01S:

(_BV(TWPS0)|_BV(TWPS1)); // биты предделителя TWBR=114; //Настраиваем частоту шины //(при 8MHz F_CPU получаем 8000000/(2*(16+114)) = 32kHz) uint8_t arr[128]; twi(TWI_START); //формируем сигнал START TWDR=0xA0; //Загружаем адрес EEPROM+W twi(TWI_TRANSMIT); //Передаем адрес TWDR=0x00; //Загружаем адрес байта twi(TWI_TRANSMIT); //Передаем twi(TWI_RESTART); //Формируем рестарт (RESTART) TWDR=0xA1; //Загружаем адрес EEPROM+R twi(TWI_TRANSMIT); //Передаем uint8_t i; //Принимаем 127 байт из EEPROM, формируя после каждого ответ ACK for(i=0;i 2 C после передачи каждого байта информации, предусмотрено окно в 1 такт, для того чтобы устройство, которое выполняет прием данных могло откликнуться. Этот сигнал назван ACK и для его передачи принимающее устройство должно прижать линию SDA к земле в этом такте. Иногда, так же рассматривают сигнал NACK: NOT ACK (принимающее устройство не прижало SDA к земле на 9м такте передачи). Но с моей скромной точки зрения, как сигнал NACK рассматривать нельзя: во-первых это путает, во-вторых если на шине нет никаких устройств из-за подтяжки SDA к питанию передающий, прочитав 9й такт примет его за сигнал NACK — хотя сигнала никакого не было. Проще ACK считать за отклик, а NACK — за отсутствие отклика. Мастер передал адрес в шину, если адресуемое устройство присутствует на линии — получили ACK (отклик), нет устройства — нет отклика.
Так же ACK очень часто используется в передачи данных: например в случае для многобайтного чтения из 24CXX — хотим получить следующий байт формируем ACK, не хотим — не формируем.

Читайте также:  Standard load на шинах

Источник

Введение

В составе некоторых микроконтроллеров AVR есть модуль двухпроводного последовательного интерфейса (Two -wire Serial Interface или TWI). Модуль предназначен для организации обмена данными между микросхемами, подключенными к двухпроводной шине. Основное преимущество этого интерфейса перед SPI и UART`ом — это возможность организации сети из нескольких устройств.

Двухпроводный интерфейс является аналогом I2C интерфейса фирмы Philips, только в случае AVR, не поддерживает высокие скорости передачи данных (свыше 400 kbit/s). Соображения, по которым Atmel использует другое названия, видимо связано с нежеланием платить Philips за лицензию.

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

Далее я буду опираться на даташит atmega16 и спецификацию I2C.

Подключение устройств к I2C шине

I2C шина представляет собой две двунаправленные линии связи — SDA (Serial Data Line) и SCL (Serial Clock Line). По SDA передаются данные, по SCL тактовый сигнал. Обе линии подтянуты через резисторы к плюсу питания.

Устройства на шине подразделяются на ведущих (master) и ведомых (slave). Ведущие запускают и завершают передачу данных, ведомые — отвечают на запросы. Одни — начальники, другие — подчиненные. Микроконтроллеры могут совмещать в себе обе эти функции.

Выходные каскады устройств, поддерживающих I2C, выполнены как структуры с открытым коллектором/стоком. При подключении к шине, они образуют схему монтажного «И». Это значит, что любое устройство, установившее низкий логический уровень на линии шины, «проваливает» высокий уровень остальных устройств.

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

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

Передача данных по I2C

Данные передаются поразрядно (спасибо кэп!), старшим разрядом вперед. Каждый разряд сопровождается тактовым сигналом.

Начало и окончание передачи данных сопровождается специальными состояниями шины — СТАРТ и СТОП. Эти состояния формирует ведущее устройство. Также ведущий может сформировать состояние повторного старта до формирования состояния СТОП.

Данные передаются по I2C шине со служебной информацией. Все вместе это называется пакетом. Существуют адресные пакеты и пакеты данных. Адресные пакеты состоят из 7-и разрядного адреса, управляющего бита R/W и бита квитирования (весь пакет — 9 бит). Адресный пакет нужен, чтобы обратиться к конкретному устройству.

R/W — определяет последующее направление передачи данных. Бит квитирования — это ответ ведомого устройства на принятый адрес. Если адрес распознан, ведомый выдает на линию SDA низкий уровень. В противном случае на линии удерживается высокий уровень.

Под адрес отведено 7 разрядов, что как бы намекает на возможность адресации 128 устройств, но это не совсем так. Во-первых существует ряд служебных адресов, например, 0000000 — это адрес общего вызова (обращение ко всем ведомым устройствам). Во-вторых есть режим 10-и разрядной адресации, когда адрес передается двумя пакетами. Не буду углубляться в эту тему, только скажу, что адреса из диапазона 1111000. 1111111 не рекомендуется использовать в TWI модуле AVR (курим даташит).

Пакеты данных состоят из байта данных и бита квитирования, то есть тоже имеют длину 9 бит. После приема каждого байта данных, принимающее устройство (приемник) отвечает передающему устройству (передатчику), устанавливая на линии SDА низкий уровень (это и есть бит квитирования). Если принимающее устройство получило последний байт или больше не может продолжать прием данных, оно должно «оставить» на линии SDA высокий уровень.

В общем случае, полный цикл обмена по I2C состоит из следующих шагов:

— формирование состояния СТАРТ,
— передача адресного пакета,
— передача пакетов данных,
— прием пакетов данных,
— формирование состояния СТОП.

Источник

Урок 24.1 Знакомство с I2C

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

Вначале нужно познакомить читателя с тем, о чем дальше пойдет речь. Для обмена данными с периферией существуют различные интерфейсы, например мы уже ранее рассматривали SPI. Его недостаток — для обмена данными используются четыре ножки, если задействовано несколько устройств, то ножку CS — chip select нужно тянуть для каждого устройства отдельную. Таким образом, если используются три внешних устройства, то у микроконтроллера будет задействовано шесть ножек.

Ножки это важный ресурс и высокая скорость, которой обладает SPI не всегда нужна, поэтому альтернативой является интерфейс I2C, который работает со скоростью до 400кГц и обладает 7 битным адресным пространством и теоретически позволяет подключать до 128 ведомых устройств. Ну и самое главное, для передачи данных используется всего две ножки микроконтроллера и не важно сколько у вас сидит устройств на шине данных 1, 2 или 128.

Читайте также:  Как надо мерить давление в шинах

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

Теперь о самом главном, как микроконтроллер узнает с каким устройством он общается? Для этого у каждого устройства есть свой адрес. Адрес можно узнать в даташите на устройство. Кроме того, обычно несколько бит адреса микросхемы позволено выставлять пользователю, дабы тот мог повесить несколько микросхем одного типа на одну I2C шину, например если одной внешней 64 кБ EEPROM не хватило и разработчик решил повесить 8 таких. После отсылки адреса чипа на шину, если микросхема с таковым там была и все подключено верно — именно она будет воспринимать следующие байты, остальные информацию на шине проигнорируют.

В atmega8 есть два варианта передачи данных по протоколу I2c — использование аппаратного модуля TWI(two wire interface) или программного ногодрыга. Программная реализация достаточно проста в использовании, однако имеет недостаток — после отсылки данных микроконтроллером, нужно тупить и ждать пока внешнее устройство ответит, в случае с использования аппаратной шины прием можно организовать в прерывании. Однако, для микроконтроллеров не имеющих на своем борту аппаратного модуля это единственное решение. Кроме того, шину можно повесить на любой порт, что может быть удобно при разводке плат.

Для начального ознакомления мы будем использовать программную библиотеку, в Codevision вкладка визарда называется I2C, а библиотека i2c.h. Функций в библиотеке присутствует не так много: i2c_init() — необходима для инициализации; i2c_start() — инициализирует начало передачи данных, также возвращает 1, если шина свободна и 0, если шина занята; i2c_stop() — окончание передачи; i2c_read() — принять данные; i2c_write() — передать данные.

В функциях приема и передачи есть маленькая хитрость, называется ack — работает очень просто: после завершения приема/передачи 8-ми бит, следующий такт периферия сама подтягивает линию данных к земле, контроллер при этом только «слушает» линию, и если там действительно 0 (т.е. периферия удачно приняла/отдала байт и сигнализирует об этом мастеру), то процесс продолжается. Если нет, то случился какой-то сбой. Чтобы при чтении получать подтверждение приема, отправляем i2c_read(1), если не хотим i2c_read(0).

Одной теорией сыт не будешь, хорошо бы еще научиться конкретно опрашивать периферию! Какой она бывает? Компасы-магнитометры, гироскопы-акселерометры, термометры, датчики влажности, освещенности, атмосферного давления, внешние АЦП, внешнюю EEPROM, аудиопроцессоры, эквалайзеры, экранчики, ИК-дальномеры, и еще много всякого вкусного. В общем понятно думаю, что не умея работать с I2C — вы многое теряете.

Наконец обещанный экшн, протоколы связи из даташита на 24LC512.


Подробно рассмотрим 2 основных фичи, к остальному при желании функции написать будет не проблема. Основные в данном случае- это запись произвольного байта и чтение, тоже произвольного(byte write и random read). Адресация в 24LC512 не страничная, а побайтовая, работать с такой системой очень удобно. Проще всего записывать данные, смотрим сейчас одним глазом на диаграмму из даташита и пытаемся разобраться, что же там происходит:
1. Посылаем на шину команду «старт». На практике это просто подтягивание шины на 1 такт, после этого все подключенные к ней устройства начинают «слушать» следующий байт, указывающий адрес микросхемы, с которой мы хотим работать. Старт- отдельная функция их i2c.h либы;
2. Посылаем байт с адресом.

Как видим, первый бит адресного байта(читается справа-налево) указывает, будем мы работать с 24LC512 в режиме чтения или записи. Запись — это 0. Далее идут 3 адресных бита, которые может выставить пользователь, подтянув ножки 1, 2, и 3 нашей 24LC512 к земле или питанию. Для простоты подтягиваем все к земле. Нужно это, дабы можно было подключить на I2C шину не одну EEPROM этого типа. Следующие 4 бита- это уже заводской адерс конкретно этой микросхемы. Выходит, для обращения к микрухе в режиме записи нужно кинуть на шину число «0b10100000».
3. и 4. Теперь бросаем 2 байта с адерсом того байта, который мы хотим записать. 24LC512 имеет 64 кБ памяти и для адресации каждого из них нам нужно в аккурат 16-битное число(2^16=65536, что с поправкой на тот факт, что килобайт- это 1024 байта дает нам в аккурат 64 кБ).
5. Бросаем собственно то, что хотели записать.
6. Ну, и команда «стоп»!

Источник

Поделиться с друзьями
Шинбург