Обсуждаем контроллеры компании Atmel.
Ответить

i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:05:16

Здравствуйте, хочу написать функцию на прерываниях для i2c. Формирую условие старта . в прерываниях ловлю код 0х08 ,далее в регистр данных посылаю адрес , но в этом месте все зависает в прерывании ... что не так? Контроллер ATMEGA 328 AVRStudio 7. все по даташиту дела.


void i2c_transmit(void) // функция передачи инициирует условие старта далее все должно происходить в прерывани.
{
TWCR= (1<<TWINT)|(1<<TWEN)|(1<<TWSTA)|(1<<TWIE);

}


вектор прерывания

ISR (TWI_vect) // прерывание по i2c
{
switch((TWSR & 0xF8))
{
case 8: // если старт прошел зависает на этом кейсе
{
TWDR = i2c_SlaveAddress; // в дата регистр пишем адрес подчиненного (он уже сдвинут на 1 в лево!)
TWCR = 1<<TWINT|0<<TWSTA|0<<TWSTO|1<<TWEN|1<<TWIE; // команда напередачу
break;
}

case 0x18: // если аск от ведомого получен
{
TWDR=200; // передаем например 200
TWCR = (1<<TWINT)|(1<<TWEN);
}
}

}

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:16:47

vdv22 писал(а):TWCR= (1<<TWINT)|(1<<TWEN)|(1<<TWSTA)|(1<<TWIE);


vdv22 писал(а):TWCR = 1<<TWINT|0<<TWSTA|0<<TWSTO|1<<TWEN|1<<TWIE; // команда напередачу


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

Добавлено after 4 minutes 49 seconds:
а, TWCR-у всё заново присваивается... ну. так некрасиво. Это портит логику работы.

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:19:57

по даташиту TWCR = (1<<TWINT) | (1<<TWEN); так тоже не работает

А разность написания это все результат экспериментов- целый день убил на это . Без прерываний все отлично работает.

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:26:01

В AVR-ах ни бум-бум, но так делать, пожалуй, не стоит:
Код:
switch((TWSR & 0xF8))
Вы уверены, что в регистре будет стоять только один этот бит
Код:
case 8:
А то что не выходите из прерывания - какой-то флаг обработки его не сбрасываете.

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:44:06

Здравствуйте, хочу написать функцию на прерываниях для i2c. Формирую условие старта . в прерываниях ловлю код 0х08

Я в Си не понимаю, но вот что бросилось в глаза... пишете что ловите 0х08 а в коде пишете 0хF8... :dont_know:
switch((TWSR & 0xF8))

а ведь нужно проверять после команды старт именно 0x08 а не 0хF8 :)
Последний раз редактировалось VNS Ср авг 17, 2022 11:46:29, всего редактировалось 1 раз.

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:47:13

если перед выходом из прерывания добавить TWCR = (1<<TWINT); то зависания нет ,но адрес упороно не передается (по осциллнографу и лог анализатору.

SR (TWI_vect) // прерывание по i2c
{

switch((TWSR & 0xF8))
{
case 8: // если старт прошел
{ out=(TWSR & 0xF8);
TWDR = i2c_SlaveAddress; // в дата регистр пишем адрес подчиненного (он уже сдвинут на 1 в лево!)
TWCR = (1<<TWINT)|(0<<TWSTA)|(0<<TWSTO)|(1<<TWEN); // команда напередачу
break;
}

case 0x18: // если аск от ведомого получен
{
TWDR=200; // передаем например 200
TWCR = (1<<TWINT)|(1<<TWEN);
}
}

TWCR = (1<<TWINT); // если добавить сброс флага ,то зависания нет ,но адрес упороно не передается (по осциллнографу и лог анализатору.)
}

Добавлено after 1 minute 6 seconds:
Здравствуйте, хочу написать функцию на прерываниях для i2c. Формирую условие старта . в прерываниях ловлю код 0х08

Я в Си не понимаю, но вот что бросилось в глаза... пишете что ловите 0х08 а в коде пишете 0хF8... :dont_know:
switch((TWSR & 0xF8))


F8 это маска . она накладывается на регистр и смотрим какое число получилось

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:53:24

F8 это маска

Теперь понял... извиняюсь что отвлёк... :)

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 11:56:40

Ну если вы так упорствуете:
vdv22 писал(а):case 8: // если старт прошел
case 0x18: // если аск от ведомого получен
То где кейсы для 0x38, 0x48, 0x58 ... 0xF8? Каждый флаг должен обрабатываться.

В моём драйвере для силикон-лабовского кристалла обработчик выглядит так:
Спойлер
Код:
void I2C_IRQHandler(void) {

  if ((I2C_DEV->IEN & I2C_IEN_ARBLOST) && (I2C_DEV->IF & I2C_IF_ARBLOST)) {// Arbitration lost; Interrupt Flag: UCALIFG; Interrupt
      i2c_error_code = I2C_ERROR;
      i2c_state = i2c_idle;
      I2C_DEV->IFC = I2C_IFC_ARBLOST;
  }
  if ((I2C_DEV->IEN & I2C_IEN_NACK) && (I2C_DEV->IF & I2C_IF_NACK)) { // Not acknowledgment; Interrupt Flag: UCNACKIFG
      i2c_error_code = I2C_ERROR;
      i2c_state = i2c_stop;
      I2C_DEV->IFC = I2C_IFC_NACK;
      BUS_RegMaskedClear(&I2C_DEV->IEN, I2C_IEN_TXBL);
      I2C_DEV->CMD = I2C_CMD_STOP;
  }
  if ((I2C_DEV->IEN & I2C_IEN_START) && (I2C_DEV->IF & I2C_IF_START)) { // Start condition received; Interrupt Flag: UCSTTIFG
      I2C_DEV->IFC = I2C_IFC_START;
      I2C_DEV->TXDATA = i2c_addr;
      if (i2c_state != rcv_data) {
          BUS_RegMaskedSet(&I2C_DEV->IEN, I2C_IEN_TXBL);
      }
  }
  if ((I2C_DEV->IEN & I2C_IEN_RXFULL) && (I2C_DEV->IF & I2C_IF_RXFULL)) { // Check errata race condition
      if (((I2C_DEV->STATUS & I2C_STATUS_RXDATAV) == 0) && (I2C_DEV->STATUS & I2C_STATUS_RXFULL)) {
          I2C_DEV->RXDATA; // make dummy read.
      }
  }
итд...

И, заметьте, после каждого обработанного флага, соответствующий флаг сбрасывается (в регистре IFC). А вы сбрасывая глобальный флаг, возможно оставдяете, что-то необработанным.

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 12:06:38

Ждете, пока шина будет готова, проверьте код состояния для отправленного условия запуска:
Код:
while(!(TWCR & (1<<TWINT)));

Было бы хорошо обработать все возможные состояния.

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 12:11:06

Обработчик всех флагов будет, но мне сейчас дадо чтоб он хоть адрес отправит - я в свиче обычно делаю default чтоб другое не нужное отсечь. Сейчас в прерывание он заходит только один раз (отслеживаю через uart ) то есть после получения 0x08.

Добавлено after 1 minute 45 seconds:
Ждете, пока шина будет готова, проверьте код состояния для отправленного условия запуска:
Код:
while(!(TWCR & (1<<TWINT)));

Было бы хорошо обработать все возможные состояния.

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

Re: i2c на прерываниях не отправляется адрес.

Ср авг 17, 2022 12:30:23

Отключите возможности для прерывание в прерывании.
Обработайте все возможные команды/состояния.
Поставьте break; след второй выбор.
Спойлер
Код:
Вариант:
----------
ISR(TWI_vect){
  static unsigned char state = 0, lastVal = 0;
  // Disable Global Interrupt
  cli();
  switch(TW_STATUS){
    case TW_START:  // 0x08
    case TW_REP_START: // 0x10
    case TW_MT_SLA_ACK: // 0x18
    case TW_MT_SLA_NACK: // 0x20
    case TW_MT_DATA_ACK: // 0x28
    case TW_MT_DATA_NACK: // 0x30
    case TW_MR_SLA_ACK:   // 0x40
    case TW_MR_SLA_NACK:   // 0x48
    case TW_MR_DATA_ACK:  // 0x50
    case TW_MR_DATA_NACK:  //  0x58
    case TW_ST_ARB_LOST_SLA_ACK:  //  0xB0
    case TW_SR_ARB_LOST_SLA_ACK: //  0x68
    case TW_SR_GCALL_ACK:    //0x70
    case TW_SR_ARB_LOST_GCALL_ACK:  //  0x78
    case TW_SR_GCALL_DATA_ACK:   // 0x90
    case TW_SR_GCALL_DATA_NACK:  //  0x98
    case TW_NO_INFO:   // 0xF8
      TWCR |= (1<<TWINT);    // Clear TWINT Flag
      break;
    case TW_SR_SLA_ACK:      // 0x60: SLA+W received, ACK returned
      TWCR |= (1<<TWINT);    // Clear TWINT Flag
      break;
    case TW_SR_DATA_ACK:     // data received
      state = TWDR;
      if(lastVal == 'S'){
         targetTemp = state;
         state = 0;
      }
      lastVal = state;
      TWCR |= (1<<TWINT);    // Clear TWINT Flag
      break;
    case TW_SR_STOP:         // 0xA0: stop or repeated start condition received while selected
      TWCR |= (1<<TWINT);    // Clear TWINT Flag
      break;
    case TW_ST_SLA_ACK:      // 0xA8: SLA+R received, ACK returned
       if(state == 'T'){
            TWDR = temperature;           // Fill TWDR register whith the data to be sent
         }else if(state == 'H'){
            TWDR = humidity;
         }else{
            TWDR = -1;
         }
      TWCR = ((1 << TWEA) | (1 << TWINT) | (1 << TWEN) | (1 << TWIE));   // Enable TWI, Clear TWI interrupt flag
      break;
    case TW_ST_DATA_ACK:     // 0xB8: data transmitted, ACK received
      TWCR |= (1<<TWINT);    // Clear TWINT Flag
      break;
    case TW_ST_DATA_NACK:    // 0xC0: data transmitted, NACK received
      TWCR |= (1<<TWINT);    // Clear TWINT Flag
      break;
    case TW_ST_LAST_DATA:    // 0xC8: last data byte transmitted, ACK received
    case TW_BUS_ERROR:       // 0x00: illegal start or stop condition
    default:
      TWCR |= (1<<TWINT);    // Clear TWINT Flag
      break;
  }

  sei();
}
Ответить