Учебный курс AVR. Использования TWI модуля. Работа с DS1307. Статусные коды. Ч4

08/11/2013 - 15:15 Павел Бобков

Введение

В составе 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   

# StrangerPNZ 2013-11-08 21:00
Молодчина, так держать!!!!! Интересно пишешь!!!!
# foxit 2013-11-10 07:21
Спасибо.
Ждем проекты для winavr.

Интересует точность хода часов.
Как организовать коррекцию(ручну ю и автоматическую)?
Как привязать часы к времени UTC?
# Bonio 2013-11-10 10:38
А на прерываниях будут примеры?
# JoJo 2013-11-10 13:08
В конце написано
Quote:
Следущая часть - работа с TWI модулем через прерывания.
# Кирилл 2016-04-18 19:49
Вот это статья! Я - новичок в СИ и в микроконтроллер ах. И мне все понятно. Каждая строка - на вес золота. Экономит кучу времени. Спасибо!

У вас недостаточно прав для комментирования.