Учебный курс AVR. Использования TWI модуля как ведущего I2C устройства. Работа на прерываниях. Ч5

21/11/2013 - 08:08 Pavel Bobkov

Введение

Все операции, выполняемые TWI модулем, завершаются установкой бита прерывания (TWINT бит регистра TWCR) и соответствующего статусного кода (старшие шесть разрядов регистра TWSR). Это позволяет организовать обмен данными по шине с помощью прерываний. 

Суть подхода в следующем. Мы формируем сообщение для передачи, затем инициируем ее старт и разрешаем прерывания. Дальнейшую работу выполняет автоматически вызываемый обработчик прерывания TWI модуля, а микроконтроллер может заниматься другими задачами. 

Такой подход экономит время микроконтроллера, потому что не нужно ожидать установки бита прерывания в цикле, чтобы продолжить процесс обмена данными. 

В этой части мы рассмотрим работу ведущего TWI устройства на прерываниях. Код для этого материала основан на руководстве фирмы Atmel - AVR315: Using TWI module as I2C master. В интернете есть его перевод. Загуглите в Яндексе, если интересно. 

Я сделал свой аутентичный перевод этого руководства, скоро его выложу.

Обработчик прерывания TWI модуля

Благодаря наличию статусных кодов в TWI модуле, обработчик прерывания органично реализуется в виде автомата (state machine), где каждое состояние - это один статусный код.

Традиционный способ построения автоматов основан на использовании оператора ветвления- switch. Начальный код для обработчика прерывания TWI выглядит так.


//буфер для сообщения
volatile static uint8_t twiBuf[TWI_BUFFER_SIZE]; 

//сколько байт нужно передать
volatile static uint8_t twiMsgSize;

//статус модуля
volatile static uint8_t twiState = TWI_NO_STATE; 

...


#pragma vector=TWI_vect
__interrupt void TWI_ISR(void)
{
//берем статусный код модуля
uint8_t stat = TWSR & TWSR_MASK;

//обрабатываем его
switch (stat){

  // состояние START сформировано
  case TWI_START:

  // состояние повторный START сформировано
  case TWI_REP_START:

  // был передан пакет SLA+W и получено подтверждение
  case TWI_MTX_ADR_ACK:

  // был передан байт данных и получено подтверждение
  case TWI_MTX_DATA_ACK:

  //байт данных принят и передано подтверждение
  case TWI_MRX_DATA_ACK:

  //был передан пакет SLA+R и получено подтверждение
  case TWI_MRX_ADR_ACK:

  //был принят байт данных без подтверждения
  case TWI_MRX_DATA_NACK:

  //был потерян приоритет
  case TWI_ARB_LOST:

  // был передан пакет SLA+W и не получено подтверждение
  case TWI_MTX_ADR_NACK:

  // был передан пакет SLA+R и не получено подтверждение
  case TWI_MRX_ADR_NACK:

  // был передан байт данных и не получено подтверждение
  case TWI_MTX_DATA_NACK:

  // ошибка на шине из-за некорректных состояний СТАРТ или СТОП
  case TWI_BUS_ERROR:

  default:
  }
}


Теперь для состояний автомата нужно добавить какой-то код. Разберемся, что нужно делать в каждом состоянии.

Состояния TWI_START и TWI_REP_START по сути одинаковые. Передача данных только началась, нужно установить указатель на первый элемент массива twiBuf, загрузить его в регистр данных TWDR, инкрементировать указатель и сбросить флаг прерывания.

TWI_MTX_ADR_ACK - передан адресный пакет SLA+W и получено подтверждение. Можно продолжать передачу данных, если это не единственный байт сообщения. Загружаем байт данных в регистр TWDR, увеличиваем указатель и сбрасываем флаг прерывания. Если это единственный байт сообщения, формируем состояние СТОП, сбрасываем флаг прерывания и запрещаем его. И еще устанавливаем статус окончания передачи данных.

TWI_MTX_DATA_ACK - передан байт данных и получено подтверждение. Можно продолжать передачу данных, если это был не последний байт сообщения. Загружаем байт данных в регистр данных, увеличиваем указатель и сбрасываем флаг прерывания. Если это был последний байт сообщения, формируем состояние СТОП, сбрасываем флаг прерывания, запрещаем его и устанавливаем статус окончания передачи данных.

В четырех состояниях выполняются практически одинаковые действия, поэтому их можно объединить. Код для этой части обработчика будет таким.


case TWI_START:
case TWI_REP_START:
   ptr = 0; //индексная переменная для массива
case TWI_MTX_ADR_ACK:
case TWI_MTX_DATA_ACK:
   if (ptr < twiMsgSize){ //если не все передано

      //загружаем байт сообщения
      TWDR = twiBuf[ptr]; 

      //сбрасываем флаг TWINT
      TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);

      //увеличиваем индексную переменную
      ptr++; 
   }
   else{ //если передано все

      //устанавливаем состояние, что данные переданы
      twiState = TWI_SUCCESS; 

      //формируем СТОП, запрещаем прерывания
      TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO)|(0<<TWIE);
   }
break;


Продолжаем.

TWI_MRX_DATA_ACK - принят байт данных и передано подтверждение. В регистре данных TWDR лежит принятый байт, мы должны скопировать его в массив и увеличить указатель. Затем нужно проверить, какой этот байт по счету? Если он предпоследний, то для приема последнего байта нам не нужно формировать подтверждение, если нет, то нужно. Далее мы сбрасываем бит прерывания и устанавливаем бит подтверждения в зависимости от ситуации.

TWI_MRX_ADR_ACK - передан пакет SLA-R и получено подтверждение. Ведущий отозвался на свой адрес и можно получить от него данные. Проверяем, следующий байт будет последним или нет. Если да, то формировать подтверждение не нужно, если нет, то нужно. Далее мы сбрасываем бит прерывания и устанавливаем бит подтверждения в зависимости от ситуации.

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


case TWI_MRX_DATA_ACK: 
   twiBuf[ptr] = TWDR; //сохраняем байт данных
   ptr++;


case TWI_MRX_ADR_ACK:
   if (ptr < (twiMsgSize-1)){ //это предпоследний байт?
       //нет, формируем подтверждение
      TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA);
   }
   else {
      //да, подтверждение не формируем
     TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT); 
   }
   break;



TWI_MRX_DATA_NACK - принят байт без подтверждения. Значит это последний байт, который мы должны принять. Копируем его из регистра данных в свой массив. Устанавливаем какой-нибудь флаг, что прием данных окончен. Формируем состояние СТОП, сбрасываем флаг прерывания и запрещаем его.


case TWI_MRX_DATA_NACK:
   twiBuf[ptr] = TWDR;
   twiState = TWI_SUCCESS;
   TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO);
   break;


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


case TWI_ARB_LOST:
   TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
   break;


Далее идут статусные коды, связанные с неполадками на шине.

TWI_MTX_ADR_NACK - передан пакет SLA+W и не получено подтверждение
TWI_MRX_ADR_NACK - передан пакет SLA+R и не получено подтверждение
TWI_MTX_DATA_NACK - передан байт данных и не получено подтверждение
TWI_BUS_ERROR - ошибка на шине из-за некорректных состояний СТАРТ или СТОП

Такие коды могут возникнуть, если ведомое устройство не отвечает или прекратило отвечать. В этом случае нужно сохранить ошибочный статусный код и запретить прерывание TWI модуля.


case TWI_MTX_ADR_NACK:
case TWI_MRX_ADR_NACK:
case TWI_MTX_DATA_NACK:
case TWI_BUS_ERROR:
default:

   //сохраняем статусный код
   twiState = stat; 

   //запрещаем прерывания модуля
   TWCR = (1<<TWEN)|(0<<TWIE);  


Полный код обработчика прерывания TWI модуля выглядит так.


//буфер для сообщения
volatile static uint8_t twiBuf[TWI_BUFFER_SIZE]; 

//сколько байт нужно передать
volatile static uint8_t twiMsgSize; 

//статус модуля
volatile static uint8_t twiState = TWI_NO_STATE; 

...

#pragma vector=TWI_vect
__interrupt void TWI_ISR(void)
{
   static uint8_t ptr;
   uint8_t stat = TWSR & TWSR_MASK;

   switch (stat){
      case TWI_START:
      case TWI_REP_START:
         ptr = 0;
      case TWI_MTX_ADR_ACK:
      case TWI_MTX_DATA_ACK:
         if (ptr < twiMsgSize){
            TWDR = twiBuf[ptr];
            TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
            ptr++;
        }
        else{
            twiState = TWI_SUCCESS;
            TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO)|(0<<TWIE);
        }
        break;

   case TWI_MRX_DATA_ACK:
      twiBuf[ptr] = TWDR;
      ptr++;

   case TWI_MRX_ADR_ACK:
      if (ptr < (twiMsgSize-1)){
         TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA);
      }
      else {
         TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
      }
      break;

   case TWI_MRX_DATA_NACK:
      twiBuf[ptr] = TWDR;
      twiState = TWI_SUCCESS;
      TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO);
      break;

   case TWI_ARB_LOST:
      TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
      break;

   case TWI_MTX_ADR_NACK:
   case TWI_MRX_ADR_NACK:
   case TWI_MTX_DATA_NACK:
   case TWI_BUS_ERROR:
   default:
      twiState = stat;
      TWCR = (1<<TWEN);
   }
}


Для окончательной ясности привожу блок схему работы обработчика прерывания TWI модуля, которую я взял из руководства AVR315. Картинка кликабельна. 


Как запускается обработчик прерывания

Обработчик прерывания есть, но как его запустить? Очень просто. Записать во внутренний буфер (twiBuf) сообщение и количество байт для передачи (twiMsgSize), сформировать состояние СТАРТ и разрешить прерывание TWI модуля. Когда модуль сформирует на шине состояние СТАРТ, установится бит TWINT и запустится обработчик прерывания. Всю дальнейшую работу по передаче сообщения выполнит он.

Функция передачи сообщения выглядит так. 


void TWI_SendData(uint8_t *msg, uint8_t msgSize)
{
   uint8_t i;

   /*ждем ,когда TWI модуль освободится*/
   while(TWCR & (1<<TWIE));

   /*сохр. количество байт для передачи
   и первый байт сообщения*/
   twiMsgSize = msgSize;
   twiBuf[0] = msg[0];

   /*если первый байт типа SLA+W, то
   остальное сообщение тоже сохраняем*/
   if (!(msg[0] & (TRUE<<TWI_READ_BIT))){
      for (i = 1; i < msgSize; i++){
         twiBuf[i] = msg[i];
      }
   }

   twiState = TWI_NO_STATE ;

   /*разрешаем прерывание и формируем состояние старт */
   TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
}

Драйвер TWI модуля

Я объяснил принцип работы с TWI по прерываниям, теперь расскжу о драйвере, в котором объединено все выше описанное. 

Драйвер состоит из двух файлов twim.h и twim.c. В заголовочном файле twim.h определена частота микроконтроллера и размер передающего/приемного буфера TWI модуля, статусные коды для ведущего устройства и прототипы функций. В сишной файле twim.c описана реализация пользовательских функций и обработчик прерывания TWI модуля.

Пользовательские функции драйвера.

uint8_t TWI_MasterInit(uint16_t fr) - инициализирует TWI модуль и устанавливает частоту SCL сигнала. Возвращает TWI_SUCCESS, если требуемую скорость удалось установить и 0, если не удалось. Скорость задается в килогерцах (от 1 до 400). Вызывается в начале main функции.

Пример:


/*установить скорость обмена 100 кГц*/
TWI_MasterInit(100); 


void TWI_SendData(uint8_t *msg, uint8_t msgSize) - сохраняет сообщение и инициирует передачу/прием данных по TWI/I2C шине, формируя состояние СТАРТ и разрешая прерывания. В качестве параметров принимает указатель на буфер, в котором хранится сообщение, и количество байтов, которые нужно передать/принять. Первый байт сообщения для функции должен содержать адрес ведомого устройства и бит R/W.

Функция может вызываться после функции инициализации и глобального разрешения прерываний.

Пример:

uint8_t buf[8];
...

TWI_MasterInit(100);
__enable_interrupt();

/*подготавливаем сообщение*/
buf[0] = (DS1307_ADR<<1)|0;
buf[1] = 0;
buf[2] = 0;
buf[3] = 0;
buf[4] = 0;

/*отправляем его*/
TWI_SendData(buf, 5);


uint8_t TWI_GetData(uint8_t *msg, uint8_t msgSize) - получить принятые данные. Она просто копирует принятые ранее данные в буфер msg. Переменная msgSize определяет сколько байт нужно скопировать. Функция возвращает 0, если сообщение не было принято и 1 если сообщение получено.

Эта функцию нужно вызывать после функции TWI_SendData(..), которой был передан адресный пакет SLA-R.

Пример:

/*подготавливаем сообщение*/
buf[0] = (DS1307_ADR<<1)|1; //бит R/W установлен в 1
buf[1] = 0;

/*отправляем его*/
TWI_SendData(buf, 5);
//в байты buf[1]..buf[4] запишется принятое сообщение

/*копируем данные из буфера драйвера в свой*/
TWI_GetData(buf, 5);


uint8_t TWI_GetState(void) - возвращает статус TWI модуля.

TWI_NO_STATE - неопределенное состояние
TWI_SUCCESS - сообщение передано/получено
TWI_MTX_ADR_NACK - передан пакет SLA+W и не получено подтверждение
TWI_MRX_ADR_NACK - передан пакет SLA+R и не получено подтверждение
TWI_MTX_DATA_NACK - передан байт данных и не получено подтверждение
TWI_BUS_ERROR - ошибка на шине из-за некорректных состояний СТАРТ или СТОП

Пример использования драйвера ведущего TWI устройства смотрите в тестовых проектах. 

Файлы

DS1307-iar-3.rar
DS1307-proteus.rar
DS1307-AS6-3.rar
DS1307-winavr-3.rar
DS1307-cv-3.rar


Комментарии   

# foxit 22.11.2013 14:34
Спасибо.
Ждем проекты для winavr.

Интересует точность хода часов.
Как организовать коррекцию(ручну ю и автоматическую)?
Как привязать часы к времени UTC?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 23.11.2013 20:20
Цитата:
Интересует точность хода часов.
Как организовать коррекцию(ручну ю и автоматическую)?
Как привязать часы к времени UTC?
Это уже не по теме TWI. С этим я не разбирался.
Ответить | Ответить с цитатой | Цитировать
# Ferrocen 17.12.2013 19:14
Код:/*подготавливаем сообщение*/
buf[0] = (DS1307_ADR<<1)|1; //бит R/W установлен в 1
buf[1] = 0;

/*отправляем его*/
TWI_SendData(buf, 5);
//в байты buf[1]..buf[4] запишется принятое сообщение

/*копируем данные из буфера драйвера в свой*/
TWI_SendData(buf, 5);

Подправьте,пожалуйста, последнюю строку в соответствии с приемом данных.

PS: пардон за первых два "кривых" сообщения – проглядел теги Код: .
Ответить | Ответить с цитатой | Цитировать
# Pashgan 20.12.2013 01:48
А что с ней не так?
Ответить | Ответить с цитатой | Цитировать
# Ferrocen 24.12.2013 17:38
Как я понимаю должно быть:
Код:/*копируем данные из буфера драйвера в свой*/
TWI_GetData(buf, 5);
Ответить | Ответить с цитатой | Цитировать
# Pashgan 25.12.2013 15:03
Да, точно. Спасибо. Поправил.
Ответить | Ответить с цитатой | Цитировать
# Aleksei 01.01.2014 10:38
Может и глупый вопрос, но как правильно прочесть данные из регистра, хранящего 16-битное знаковое значение? Аппаратно предусмотрено два 8-битных регистра для хранения старшего и младшего байтов.
Ответить | Ответить с цитатой | Цитировать
# JoJo 01.01.2014 13:51
Читаешь по очереди, потом склеиваешь.
Наверное так
int tmp = ((int)hi_reg
Ответить | Ответить с цитатой | Цитировать
# JoJo 01.01.2014 13:52
Код:
int tmp = ((int)hi_reg<<8)|(low_reg);
Ответить | Ответить с цитатой | Цитировать
# dmitr-panov 12.01.2014 03:58
упс - не туда
Ответить | Ответить с цитатой | Цитировать
# dmitr-panov 12.01.2014 05:49
Код:
/*подготавливаем сообщение*/
buf[0] = (DS1307_ADR<<1)|0; //адресный пакет
buf[1] = 0; //адрес регистра
buf[2] = (5<<4)|5; //значение секунд
buf[3] = (5<<4)|9; //значение минут
buf[4] = 0; //значение часов

/*отправляем его*/
TWI_SendData(buf, 5);

А как отправить число месяц и год?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 14.01.2014 08:32
Задать адрес регистра DS1307 - 0x04. Записать в остальные байты буфера число месяц и год.
Код:
/*подготавливаем сообщение*/
buf[0] = (DS1307_ADR<<1)|0; //адресный пакет
buf[1] = 0x04; //адрес регистра дней
buf[2] = (1<<4)|4; //день - 14
buf[3] = (0<<4)|1; //месяц - 01
buf[4] = (1<<4)|4; //год - 14

/*отправляем его*/
TWI_SendData(buf, 5);
Ответить | Ответить с цитатой | Цитировать
# dmitr-panov 15.01.2014 12:50
спасибо
Ответить | Ответить с цитатой | Цитировать
# Plotnik 19.01.2014 07:59
Работаю в AS6,подключил 7сегм.индикатор 4разряда. Вывожу секунды-ds1307 считает так-1,2,3,4,5,6 ,7,8,9,16,17,18 ... до 87,т.е. в каждом десятке-пропуск ается по 7 секунд.Да и минуты "уходят" ...77 и более.Подключен о:"twi.h","indi cator_2f.h".Как -то странно...
Ответить | Ответить с цитатой | Цитировать
# dmitr-panov 20.01.2014 03:31
ds1307 хранит время в двоично-десятич ном коде.
Я так делаю:
Код:
IND_Time(hour, min);

void IND_Time(unsigned char n1, unsigned char n2)
{
data[0]=number[n2&0xf]; //единицы
data[1]=number[n2>>4]; //десятки
data[2]=number[n1&0xf];//единицы
data[3]=number[n1>>4]; //десятки
}
Ответить | Ответить с цитатой | Цитировать
# dim 15.04.2014 14:56
а будут примеры на AS или WinAvr?
а то IAR многим не знаком
Ответить | Ответить с цитатой | Цитировать
# Pashgan 16.04.2014 01:36
Если потребность есть, могу сделать.
Ответить | Ответить с цитатой | Цитировать
# dim 16.04.2014 12:28
Если не трудно, конечно)
Единственно не разобрался как часть библиотеки twim " Обработчик прерывания TWI модуля" переписать под winavr, поэтому спросил.
Учимся на твоих статьях!
Ответить | Ответить с цитатой | Цитировать
# Pashgan 16.04.2014 15:52
Завтра залью.
Ответить | Ответить с цитатой | Цитировать
# dim 18.04.2014 12:08
Благодарю, все проще, чем думалось
Ответить | Ответить с цитатой | Цитировать
# Дмитрий Е 10.06.2014 06:23
Здравствуйте. Спасибо за статью. Запрограммирова л atmega328P на плате arduino. Все работает нормально опрашиваются данные с ds1307. Но через какое-то время контроллер шину SDA подтягивает в ноль и перестает идти тики SCL. Контроллер уходит в нокаут. У кого-нибудь была такая проблема?
Ответить | Ответить с цитатой | Цитировать
# Дмитрий Е 10.06.2014 16:48
На одной печатной плате у меня установлены ds1307 и ds1621. C термометром плата общается на ура, а вот с часами от 20 до 150 опросов параметров и все контроллер обваливается... .
Ответить | Ответить с цитатой | Цитировать
# Pashgan 14.06.2014 09:50
У меня такого не было. Я бы сделал вывод отладочной информации на комп и посмотреть в какой момент происходит "обвал".
Ответить | Ответить с цитатой | Цитировать
# foton6 10.05.2015 16:46
Было(есть) такое. Иногда почему то не ставится бит TWINT и естественно прога дальше не может работать, покрайней мере та часть которая отвечает за TWI. Никак не пойму почему бит не ставится, сижу осцилом на шине, вроде все ок. Организjвал что-то типо WDT.
P.S.
Своевременный ответ^^
Ответить | Ответить с цитатой | Цитировать
# DmAlex 29.06.2014 06:15
Спасибо за драйвер.
Но при компиляции у меня ошибки, правда я драйвер этот засунул в RTOS. Вроде работает, но глюки при сравнении значении с датчика
../GCC-RTOS.c:90:2: warning: implicit declaration of function 'TWI_SendData'
../GCC-RTOS.c:97:2: warning: implicit declaration of function 'TWI_GetData'
../GCC-RTOS.c:150:1: warning: implicit declaration of function 'TWI_MasterInit '
Ответить | Ответить с цитатой | Цитировать
# DmAlex 29.06.2014 06:30
а точней, вытаскиваю данные с датчика
hourOutDS1306 = (((buf[3] & 0xF0) >> 4)*10)+(buf[3] & 0x0F);
затем сравниваю
if (hourOutDS1306
Ответить | Ответить с цитатой | Цитировать
# loki_amorf 22.11.2014 16:54
Спасибо за познавательный код.
Павел, подскажите как можно модернизировать код чтобы при отсутствии связи с подчиненным устройством, контроллер не зависал. Какой-то тайм аут отсутствия ответа.
Ответить | Ответить с цитатой | Цитировать
# Рустем 18.12.2014 04:31
как то так
Код: time=0;
while (!(TWCR & (1<<TWINT))&&time<255)time++; //ждем


а вообще конечно лучше через прерывание запускать тви
Ответить | Ответить с цитатой | Цитировать
# Рустик 18.12.2014 04:02
Почему нигде нету такого кода для ведомого?
Был бы очень признателен, если бы кто нибудь ткнул носом :)
Ответить | Ответить с цитатой | Цитировать
# _Артём_ 18.12.2014 07:41
Цитирую Рустик:
Почему нигде нету такого кода для ведомого?
Был бы очень признателен, если бы кто нибудь ткнул носом :)


На сайте Атмела есть: AVR311: Using the TWI module as I2C slave on tinyAVR and megaAVR devices
Ответить | Ответить с цитатой | Цитировать
# Рустик 18.12.2014 08:04
Спасибо!, посмотрю.
Ответить | Ответить с цитатой | Цитировать
# Andreas 31.01.2015 12:59
Спасибо ОГРОМНОЕ !!! Очень помогло.
Ответить | Ответить с цитатой | Цитировать
# loki_amorf 12.02.2015 21:37
Пробовал прикрутить библиотеку для работы с внешней I2C EEPROM, оказалось что средствами библиотеки не удается читать нулевой байт buf[0], функция TWI_GetData в buf[0] возвращает ранее переданный адрес. Для часов это конечно же неважно.
Ответить | Ответить с цитатой | Цитировать
# loki_amorf 13.02.2015 07:16
Цитирую loki_amorf:
Пробовал прикрутить библиотеку для работы с внешней I2C EEPROM, оказалось что средствами библиотеки не удается читать нулевой байт buf[0], функция TWI_GetData в buf[0] возвращает ранее переданный адрес. Для часов это конечно же неважно.

Потрите пожалуйста сообщение, все работает EEPROM читается, датчик температуры LM75 читается. Видимо заработался вчера вечером. :)
Ответить | Ответить с цитатой | Цитировать
# Алексей_жиленков 22.05.2015 07:21
Почему в функции TWI_SendData в цикли
for (i = 1; i < msgSize; i++){
twiBuf = msg;
}
переменная i начинается с 1 ?
Ответить | Ответить с цитатой | Цитировать
# Рустем 22.05.2015 07:28
наверное потому что 0 байт идет с адресом и направлением
Ответить | Ответить с цитатой | Цитировать
# Алексей_жиленков 22.05.2015 07:39
то есть нулевой байт адреса нужно отправлять отдельно?
Ответить | Ответить с цитатой | Цитировать
# Алексей 27.08.2015 09:33
Как можно понять когда передан последней байт по twi? При использовании прерывания twi.
Ответить | Ответить с цитатой | Цитировать
# Григорий11 26.01.2016 17:57
Когда ptr == msgSize
Ответить | Ответить с цитатой | Цитировать
# бухомуха 19.12.2015 14:39
ребята, какие вы умные
Ответить | Ответить с цитатой | Цитировать
# ПавелCC 17.11.2016 14:05
Всем привет. я только начинающий поэтому прошу объяснить мне. Я использую код из этой статьи для подключения дисплея

и если ставлю строку /*ждем ,когда TWI модуль освободится*/
while(TWCR & (1
Ответить | Ответить с цитатой | Цитировать
# ПавелCC 17.11.2016 14:07
[quote name="ПавелCC"] Всем привет. я только начинающий поэтому прошу объяснить мне. Я использую код из этой статьи для подключения дисплея

и если ставлю строку /*ждем ,когда TWI модуль освободится*/
Код: /*ждем ,когда TWI модуль освободится*/
while(TWCR & (1<<TWIE));
Ответить | Ответить с цитатой | Цитировать
# ПавелCC 17.11.2016 14:10
Всем привет. я только начинающий поэтому прошу объяснить мне. Я использую код из этой статьи для подключения дисплея

и если ставлю строку
Код: /*ждем ,когда TWI модуль освободится*/
while(TWCR & (1<<TWIE));


то дисплей не включается, а если ее закоментить то все работает.

всем заранее спасибо.
Ответить | Ответить с цитатой | Цитировать

Добавить комментарий

Защитный код
Обновить