Введение
В составе TWI модуля микроконтроллеров AVR есть регистр TWSR. Старшие 6 разрядов этого регистра содержат статусный код, а младшие - управляющие разряды, которые задают коэффициент деления частоты SCL сигнала. Я сейчас говорю про atmega16, в некоторых микроконтроллерах этих разрядов нет, но не суть.
Статусный код отражает результат последней выполненной операции TWI модуля. По нему можно судить, завершилась ли она успешно или что-то пошло не так, стоит ли продолжать передачу данных или ее пора прекращать.
В предыдущей части был рассмотрен топорный вариант работы с DS1307, в котором статусные коды TWI модуля просто игнорировались. В этом материале разобран пример работы с TWI модулем без прерываний, но с использованием статусных кодов.
Статусные коды TWI модуля
Для каждого режима и операции в TWI модуле есть свои статусные коды. Их много, но знать их все необязательно, достаточно иметь представление.
Сначала уточнение по терминологии, которое встретится ниже.
Ведущий, мастер - устройство инициирующее обмен по двухпроводной шине.
Ведомый, слейв - устройство отвечающее на запросы ведущего.
Передатчик - устройство (ведомый или ведущий) передающее данные.
Приемник - устройство (ведомый или ведущий), принимающее данные.
SLA-W - адресный пакет, после которого ведущий передает данные ведомому.
SLA -R - адресный пакет, после которого ведущий принимает данные от ведомого.
Подтверждение, ACK - однобитный ответ устройства (ведущего или ведомого) на прием данных. Соответствует низкому уровню на двухпроводной шине.
Неподтверждение, NACK - соответствует высокому уровню на шине. Ответ устройства (ведущего или ведомого), когда оно не хочет или не может продолжать обмен данными. Если устройства с требуемым адресом на шине нет, ведущий тоже получит "неподтверждение".
Ну а теперь статусные коды для ведущего устройства.
Общие статусные коды ведущего
0x08 - состояние СТАРТ успешно передано
0x10 - состояние повторный СТАРТ спешно передано
0x38 - потеря приоритета (это когда один или несколько ведущих начали передачу данных одновременно с нами и кто-то из них в итоге занял шину)
Статусные коды для ведущего, который передает данные (ведущего передатчика)
0x18 - был передан адресный пакет SLA-W и получено подтверждение от ведомого
0x20 - был передан адресный пакет SLA-W без подтверждения ведомого
0x28 - был передан байт данных и получено подтверждение от ведомого
0x30 - был передан байт данных без подтверждения ведомого
Статусные коды ведущего, который принимает данные (ведущего приемника)
0x40 - был передан адресный пакет SLA+R и получено подтверждение от ведомого
0x48 - был передан адресный пакет SLA+R без подтверждения ведомого
0x50 - был принят байт данных и отправлено подтверждение ведомому
0x58 - был принят байт данных без передачи подтверждения ведомому
Коды неопределенных состояний
0xf8 - информация о состоянии не доступна, бит TWINT = 0
0x00 - ошибка шины из-за некорректных состояний СТАРТ или СТОП
Статусные коды для ведомого приводить не буду, потому что работа в режиме ведомого устройства будет рассмотрена отдельно.
Как использовать статусные коды
Суть использования статусных кодов заключается в их проверке после каждой базовой операции с TWI модулем. Например для операции "сформировать состояние СТАРТ" код может выглядеть так.
/*маска, чтобы обрезать младшие биты регистра TWSR*/
#define TWSR_MASK 0xfc
/*код успешного выполнения операции*/
#define TWI_SUCCESS 0xff
/*статусные коды*/
#define TWI_START 0x08
#define TWI_REP_START 0x10
/*ожидание завершения операции*/
static void TWI_WaitInt(void)
{
while (!(TWCR & (1<<TWINT)));
}
/*сформировать состояние СТАРТ*/
uint8_t TWI_Start(void)
{
uint8_t status = TWI_SUCCESS;
TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTA);
TWI_WaitInt();
status = TWSR & TWSR_MASK;
if ((status != TWI_START) && (status != TWI_REP_START)){
return status;
}
return TWI_SUCCESS;
}
После установки значения TWCR, мы ждем завершения операции. Затем читаем статусный регистр TWSR и сравниваем с заданными кодами. Если состояние СТАРТ успешно сформировано, функция возвращает TWI_SUCCESS, в противном случае возвращается статусный код.
Аналогичным образом можно написать функцию передачи адресного пакета.
/*статусные коды*/
#define TWI_MTX_ADR_ACK 0x18
#define TWI_MRX_ADR_ACK 0x40
/*передача адресного пакета*/
uint8_t TWI_SendAdr(uint8_t adr)
{
uint8_t status;
TWI_WaitInt();
TWDR = adr;
TWCR = (1<<TWINT)|(1<<TWEN);
TWI_WaitInt();
status = TWSR & TWSR_MASK;
if ((status != TWI_MTX_ADR_ACK) && (status != TWI_MRX_ADR_ACK)){
return status;
}
return TWI_SUCCESS;
}
И функцию передачи данных.
/*статусный код*/
#define TWI_MTX_DATA_ACK 0x28
/*послать байт данных*/
static uint8_t TWI_SendByte(uint8_t data)
{
uint8_t status;
TWI_WaitInt();
TWDR = data;
TWCR = (1<<TWINT)|(1<<TWEN);
TWI_WaitInt();
status = TWSR & TWSR_MASK;
if (status != TWI_MTX_DATA_ACK){
return status;
}
return TWI_SUCCESS;
}
Думаю принцип ясен, остальные функции расписывать не буду.
Тестовый проект с DS1307
Для этого проекта я описал базовые операции с TWI модулем в виде отдельных функций и перенес их в файл twi.c. Это реализация драйвера ведущего TWI устройства без прерываний. Не самая оптимальная, но использовать можно.
В twi.c три пользовательские фукнции.
uint8_t TWI_SetFreq(uint32_t fr) - устанавливает скорость обмена TWI модуля. Возвращает 0, если не удалось подобрать значение для регистра TWBR и именованную константу TWI_SUCCESS, если скорость установлена. Функция сделана из-за лени рассчитывать значение для TWBR вручную.
uint8_t TWI_SendData(uint8_t *buf, uint8_t num) - передает несколько байт из буфера. buf - указатель на массив, num - количество байт, которые нужно передать. Первым байтом массива buf должен быть адрес устройства, к которому обращается ведущий.
Возвращает TWI_SUCCESS, если удалось передать данные и статусный код, если что-то пошло не так.
uint8_t TWI_ReadData(uint8_t *buf, uint8_t num) - принимает несколько байт в буфер. buf - указатель на массив, куда будут сохраняться данные. Первый элемент массива должен содержать адрес устройства. num - количество байт который нужно принять.
Возвращает TWI_SUCCESS, если удалось передать данные и статусный код, если что-то пошло не так.
Примеры использования функций смотрите в проекте. Я взял проект из предыдущей статьи и реализовал обмен с DS1307 с помощью функций программного модуля twi.c. Ну и добавил простую обработку ошибок.
Файлы
DS1307-iar-2.rar
DS1307-proteus-2.rar
Остальные проекты на подходе. Следущая часть - работа с TWI модулем через прерывания. Группа вконтакте.
Comments
Ждем проекты для winavr.
Интересует точность хода часов.
Как организовать коррекцию(ручну ю и автоматическую)?
Как привязать часы к времени UTC?
Quote:
RSS feed for comments to this post