
Применение
I²c находит применение в устройствах, предусматривающих простоту разработки и низкую себестоимость изготовления при относительно неплохой скорости работы.
Список возможных применений:
доступ к модулям памяти NVRAM, к низкоскоростным ЦАП/АЦП;
регулировка контрастности, насыщенности и цветового баланса мониторов;
регулировка звука в динамиках;
управление светодиодами, в том числе в мобильных телефонах;
чтение информации с датчиков мониторинга и диагностики оборудования, например, термостат центрального процессора или скорость вращения вентилятора охлаждения;
чтение информации с часов реального времени (кварцевых генераторов);
управление включением/выключением питания системных компонент;
информационный обмен между микроконтроллерами;
Робота з twi на avr
шина I2C глазами микроконтроллера 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)*4TWPS). И это единственное, что нужно сделать при инициализации.
Вся работа TWI сводится к алгоритму:
Записать значение в регистр TWCR (а при передачи данных предварительно поместить байтик в TWDR)
Дождаться флага TWINT в том же регистре TWCR (при работе с прерываниями — этот флаг вызовет прерывание по-вектору TWI)
Получить статус из регистра TWSR — в зависимости от статуса, что-то делать или не делать дальше
.
Биты регистра TWCR имеют следующее назначение: TWEN – бит разрешения работы модуля TWI. При установке бита в состояние 1 выводы SDA, SCL подключаются к внешним выводам микроконтроллера РС1, РСО соответственно для всех ранее названных моделей микроконтроллеров, кроме ATmegaSx (выводы РС4, РС5) и Atmega 64х, 128x(PDl, PDO);
TWSTA – флаг состояния Start. При установке бита в единичное состояние микроконтроллер может сформировать состояние Start, если шина свободна. Если шина занята, модуль ожидает появления состояния Stop и лишь после этого захватывает шину, формируя состояние Start;
TWSTO – флаг состояния Stop. Установка флага в состояние 1 приводит к формированию на шине состояния Stop;
TWEA – бит разрешения подтверждения. При установке бита в 1 устройство формирует сигнал подтверждения, когда это необходимо;
TWIE и TWINT – флаги разрешения и прерывания от модуля TWI. Запрос прерывания генерируется, если TWINT = 1 и TWIE = = 1. Сброс флага TWINT может быть осуществлен только при записи в него логической 1;
TWWC – флаг, устанавливаемый в 1 при попытке записи в регистр данных TWDR, когда флаг прерывания TWINT сброшен.
Биты регистра TWSR:
TWS7…TWS3 – код состояния модуля TWI;
TWPS1:TWPS0 – коэффициент деления предделителя контроллера скорости передачи.
При работе модуля в качестве ведомого скорость обмена зависит от импульсной последовательности, поступающей в микроконтроллер через вывод SCL.
Модуль TWI может работать в следующих режимах:
- ведущий с передачей байтов данных;
- ведущий с приемом байтов данных;
- ведомый с приемом байтов данных;
- ведомый с передачей байтов данных.
Процедура передачи одного байта данных от ведущего к ведомому представляет собой определенную последовательность действий.
1. В регистр ТWCR выводится команда для формирования состояния Start. Для этого необходимо установить в 1 бит TWSTA (TWSTA = 1), сбросить флаг TWINT (TWINT = 1) и установить бит активизации модуля TWI (TWEN = 1). После формирования состояния Start флаг TWINT устанавливается в 1. Для перехода к следующему действию выполняется проверка кода состояния $08 в регистре TWSR. (Ожидание установки флага TWINT можно заменить обработкой запроса прерывания. – Прим. авт.) Программная реализация функции старта представлена далее в программе 5.6 процедурой Send Start.
2. Содержимое пакета с адресом и нулевым значением бита направления записывается в регистр TWDR. Передача инициализируется сбросом флага TWINT. После завершения передачи адреса и получения бита подтверждения флаг TWINT устанавливается в 1, а в регистре TWSR устанавливается код статуса $18. Программная реализация функции записи представлена в программе процедурой SendAdr.
3. После проверки кода статуса в регистр данных TWDR загружается байт данных для передачи и сбрасывается флаг TWINT. По окончании передачи данных и получения бита подтверждения флаг TWINT устанавливается в 1, а в регистре TWSR устанавливается код статуса $28. Эти действия обеспечивают передачу от ведущего устройства ведомому как команд, так и данных. Программная реализация функции вывода данных (а также команд) представлена в программе процедурой Send Сот Data.
4. После успешного завершения передачи выполняются команды для формирования состояния Stop. Программная реализация функции представлена в программе процедурой Stop.
Кратко определим последовательность действий, выполняемых при приеме одного байта данных ведущим устройством:
1) в регистр TWCR выводится команда для формирования состояния Start; после формирования состояния Start флаг TWINT устанавливается в 1. Для перехода к следующему действию выполняется проверка кода состояния ($08) в регистре TWSR. (Ожидание установки флага TWINT также можно заменить обработкой запроса прерывания. – Прим. авт.);
2) содержимое пакета с адресом и единичным значением бита направления записывается в регистр TWDR и инициализируется передача. После завершения передачи адреса и получения бита подтверждения флаг TWINT устанавливается в 1, а в регистре TWSR устанавливается код статуса ($40);
3) после сброса флага TWINT (разрешения приема) и получения байта данных от ведомого данные из регистра TWDR переписываются в один из регистров общего назначения. При успешном приеме код статуса в регистре TWSR принимает значение $50. При необходимости формируется бит подтверждения приема;
4) после завершения приема выполняется команда для формирования состояния Stop.
Последовательность действий, выполняемых при приеме одного байта данных ведомым устройством, можно определить следующим образом:
1) в регистр TWCR выводится команда, сбрасывающая флаг TWINT;
2) после приема первого байта с адресом ведомого устройства формируется запрос прерывания, проверяется код статуса в регистре TWSR; если он равен $60, прием собственного адреса выполнен успешно;
3) передается бит подтверждения путем установки TWEN = 1 и выполняется сброс флага TWINT;
4) проверяется код статуса в регистре TWSR (если он равен $80, значит в регистр TWDR принят байт данных); передается бит подтверждения (TWEN = 1) и выполняется сброс флага TWINT;
Действия 4-го шага повторяются, пока не будет принято все сообщение. При поступлении состояния Stop в регистре TWSR формируется код статуса $А0 (конец пакета).
Подобным образом можно представить действия ведомого устройства при передаче данных. Подробное описание всех вариантов обмена и формируемые при этом значения статусных кодов можно найти в технической документации применяемой модели микроконтроллера.
Вся работа TWI сводится к алгоритму:
Записать значение в регистр TWCR (а при передачи данных предварительно поместить байтик в TWDR)
Дождаться флага TWINT в том же регистре TWCR (при работе с прерываниями — этот флаг вызовет прерывание по-вектору TWI)
Получить статус из регистра TWSR — в зависимости от статуса, что-то делать или не делать дальше.
К этой не хитрой последовательности сводится вся логика работы шины, формирование стартов-рестартов-стопов, передача байта, прием байта и формирование ответа ACK.
Для простоты понимания, что и когда надо записывать в TWCR оформим небольшую функцию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#define TWI_START 0 #define TWI_RESTART 1 #define TWI_STOP 2 #define TWI_TRANSMIT 3 #define TWI_RECEIVE_ACK 4 #define TWI_RECEIVE_NACK 5
uint8_t twi(uint8_t action){ switch(action){ case TWI_START: case TWI_RESTART: TWCR = _BV(TWSTA) | _BV(TWEN) | _BV(TWINT) | _BV(TWIE); break; case TWI_STOP: TWCR = _BV(TWSTO) | _BV(TWEN) | _BV(TWINT) | _BV(TWIE); break; case TWI_TRANSMIT: TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWIE); break; case TWI_RECEIVE_ACK: TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA)| _BV(TWIE); break; case TWI_RECEIVE_NACK: TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWIE); break; } if(action != TWI_STOP)while (!(TWCR & _BV(TWINT))); return (TWSR & 0xF8); } |
Функция получает требуемое действие в качестве аргумента, дожидается его выполнения и возвращает результат выполнения. Коды возврата довольно непонятно описаны в Datasheet, и очень хорошо у DI HALT’a (ссылка выше по тексту). Данные для передачи необходимо заранее загрузить в регистр TWDR перед выполнением
twi(TW_TRANSMIT) |
После успешного получения байта с помощью
twi(TW_RECEIVE_ACK) |
или
twi(TW_RECEIVE_NACK) |
так же следует напрямую прочитать из TWDR Пример чтения (без всяких проверок и контроля выполнения) 128 байтов из EEPROM 24C01S:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
TWSR &= ~(_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<127;i++) { twi(TWI_RECEIVE_ACK); arr[i]=TWDR; } twi(TWI_RECEIVE_NACK);//Читаем последний байт, формируем NACK - больше данные не нужны arr[127] = TWDR; twi(TWI_STOP); //Формируем сигнал STOP |
Это простейший и неправильный пример. Неправильного в нем — после каждого выполнения функции twi() необходимо проверять код возврата. После выполнения контроллером любого действия код выполнения записывается в регистр статуса: TWSR, из-за наличия в этом же регистре настроек предделителя необходимо замаскировать первые три бита:
uint8_t result = TWSR & 0b11111000; |
или
uint8_t result = TWSR & 0xF8; |
Для удобства (или неудобства, как посмотреть), в Datasheet (и соответственно во всех остальных статьях и документах) коды возврата указаны с замаскированными 3 битами. (т.е. можно сравнивать TWSR & 0xF8 со значениями в Datasheet, сдвигать ничего никуда не нужно) Каждое действие может вернуть несколько кода:
START/RESTART:
0×08 — сигнал START передан
0×10 — сигнал REPEATED START передан
Передача адреса (первого байта после START/RESTART):
0×18 — Адрес для записи передан, ответ (ACK) получен
0×20 — Адрес для записи передан, устройство не откликнулось
0×38 — Контроллер потерял шину (вылез еще один контроллер)
0×40 — Адрес для чтения передан, ответ (ACK) получен
0×48 — Адрес для чтения передан, устройство не откликнулось
Передача данных (второго и последующих байтов) возвращает:
0×28 — Байт отправлен, ACK получен
0×30 — Байт отправлен, ACK не получен
0×38 — Потеря шины