аппаратный I2C в atmega328p

Тема в разделе "Микроконтроллеры AVR", создана пользователем Danil_2002, 3 июл 2019.

  1. Danil_2002

    Danil_2002 Гик

    Доброго времени суток , разбираюсь в работе протокола I2C. Задача прочитать значение любого регистра в модуле bmp180(gy-68).

    Написал вот такой код, но он выводит только один раз символ S:
    Код (C++):
    #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

    void F_TWI()
    {
      TWSR &= ~(_BV(TWPS0)|_BV(TWPS1)); // биты предделителя
      TWBR=114;
    }
    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);
    }
    void USART_init(void) // Настраиваем скорость передачи данных
    {
      UBRR0L = 103;
      UBRR0H = 0;
      UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
      UCSR0C = (1<<USBS0)|(3<<UCSZ00);
    }
    void USART_TransmitByte( unsigned char data )
    {
      while ( !( UCSR0A & (1<<UDRE0)) );
      UDR0 = data;
    }
    void transmit_byte_I2C(uint8_t data)
    {
      TWDR = data;
      twi(TWI_TRANSMIT);
    }
    void write_byte_I2C(uint8_t addr_sensor,uint8_t addr_register, uint8_t data)
    {
      twi(TWI_START);
      transmit_byte_I2C(addr_sensor);
      transmit_byte_I2C(addr_register);
      transmit_byte_I2C(data);
      twi(TWI_STOP);
    }
    uint8_t read_byte_I2C(uint8_t addr_sensor, uint8_t addr_register)
    {
      uint8_t fixed_addr_sensor = addr_sensor^(1 << 0); // Менияем первый бит на 1
      twi(TWI_START);
      transmit_byte_I2C(addr_sensor);
      twi(TWI_RECEIVE_ACK); // Здесь выполнение программы останавливается
      transmit_byte_I2C(addr_register);
      twi(TWI_RECEIVE_ACK);
      twi(TWI_RESTART);
      transmit_byte_I2C(fixed_addr_sensor);
      twi(TWI_RECEIVE_ACK);
      while(1&(TWCR << 3)); // Пока установлен флаг TWWC Slave пишет данные в наш регистр TWDR
      twi(TWI_RECEIVE_NACK);
      twi(TWI_STOP);
      return TWDR;
    }
    int main()
    {
      F_TWI();
      USART_init();
      while(1)
      {
        USART_TransmitByte('S');
        uint8_t data = read_byte_I2C(0x77, 0xF6);
        USART_TransmitByte(data);
        USART_TransmitByte('E');
      }
    }
    Где затупил?
     
  2. parovoZZ

    parovoZZ Гуру

    Сам делал или по примеру? Апноуты читал? В любом случае без симуляции будет очень тяжко и долго.

    Мутил когда-то такое
    http://forum.amperka.ru/threads/Суперэкономичный-беспроводной-монитор-Т-и-rh.17221/

    С аппаратным не мутил. Буду вместе с тобой разбираца

    Вот это что такое?
     
  3. DetSimen

    DetSimen Guest

    Класс Wire тебе не устроит?

    Или ты хочешь попробовать неблокирующее чонить написать?
     
    Последнее редактирование модератором: 8 июл 2019
  4. Danil_2002

    Danil_2002 Гик

    Просто хочу сам реализовать, чтобы понять лучше

    Читал разные статьи, но там почти всегда всплывают какие-нибудь библиотеки. Свой скейтч писал по чужому примеру работы с EEPROM.

    "Вот это" это настройка скорости I2C,то есть TWBR и два бита TWSR устанавливают скорость работы шины(буду пробывать ее менять может именно в этом дело), но я знаю что выполнение моей программы останавливается когда avr выставляет на I2C ACK.
     
    Последнее редактирование модератором: 8 июл 2019
  5. parovoZZ

    parovoZZ Гуру

    С таким же успехом в этот регистр можно ничего не писать. Вот это
    устанавливает биты в единицу, а затем происходит их инверсия, т.е. они обнулились. Остальные биты в этом байте наоборот стали единицами. Поэтому у меня сразу возник вопрос. Писать нули на то место, где и так нули после сброса - для чего? Остальные биты в регистре
    - это биты статуса и они read only.

    I2C - это синхронный интерфейс, поэтому скорость передачи данных не сильно важна до тех пор, пока она не превышает допустимую для слейвов.

    опыт работы с EEPROM сюда вообще не надо вплетать.

    тормозится программа может либо здесь
    либо здесь
    я в таких случаях (если нет внутрисхемной отладки) расставляю контрольные точки (1, 2, 3 и т.д.) и вывожу их наружу. Таким образом, я понимаю, где торможусь.
     
  6. Danil_2002

    Danil_2002 Гик

    Скорее всего тормозит тут, но это не точно:
    Код (C++):
    if(action != TWI_STOP)while (!(TWCR & _BV(TWINT)));
    После выполнения twi(TWI_RECEIVE_ACK) все останавливается, и даже ни каких значений не возвращает
    Код (C++):
    uint8_t fixed_addr_sensor = addr_sensor^(1 << 0); // Менияем первый бит на 1
      twi(TWI_START);
      //USART_TransmitByte('O'); // Тут все ок
      transmit_byte_I2C(addr_sensor);
      twi(TWI_RECEIVE_ACK);
      USART_TransmitByte('E'); // Тут ничего не выводится
     
    P.S. Использую Arduino IDE, потому что работаю с готовыми платами и единственный способ отладки это COM порт
     
  7. parovoZZ

    parovoZZ Гуру

    ну так смотри. Ты отправляешь адрес слейва
    потом ты должен получить от него (слейва) квитанцию ACK. Если её получил, то продолжаешь дальше, если квитанцию не получил, то уходишь или в ошибку общения или пробуешь ещё раз. А где это в коде?
     
  8. Danil_2002

    Danil_2002 Гик

    Кажется понял
     
  9. Danil_2002

    Danil_2002 Гик

    У меня при отправке байта возвращается 0x48, то есть на запрос ни кто не откликнулся.

    Сразу говорю что я сделал так только для того чтобы найти где ошибка не больше
    Код (C++):
    uint8_t read_byte_I2C(uint8_t addr_sensor, uint8_t addr_register)
    {
      uint8_t addr = addr_sensor << 1; // Первые семь бит адрес датчика 8-ой бит R/W = 0 по даташиту к bmp180
      uint8_t ret = 0;
      ret = twi(TWI_START);
      Serial.println(ret, HEX); // Здесь выводит 10, то есть повторный старт
      TWDR = addr_sensor;
      ret = twi(TWI_TRANSMIT);
      Serial.println(ret, HEX); //Здесь выводит 48, то есть никто не откликнулся
    }
     
  10. parovoZZ

    parovoZZ Гуру

    какого байта?
     
  11. Danil_2002

    Danil_2002 Гик

    То есть не байта, а после ret = twi(TWI_TRANSMIT); функция возвращает 48, то есть ни кто не откликнулся
     
  12. parovoZZ

    parovoZZ Гуру

    А что запрашиваешь у датчика? Можно подсмотреть как это реализовано в библиотеке от адафрит.
     
  13. Danil_2002

    Danil_2002 Гик

    Я хотел запросить значение регистра, любого.
     
  14. parovoZZ

    parovoZZ Гуру

    так надо сперва адрес отправить, затем через повторный старт запросить значение.
     
  15. Danil_2002

    Danil_2002 Гик

    Код (C++):
    uint8_t read_byte_I2C(uint8_t addr_sensor, uint8_t addr_register)
    {
      uint8_t fixed_addr_sensor = addr_sensor^(1 << 0); // Менияем первый бит на 1
      twi(TWI_START);
      transmit_byte_I2C(addr_sensor);
      twi(TWI_RECEIVE_ACK); // Здесь выполнение программы останавливается
      transmit_byte_I2C(addr_register);
      twi(TWI_RECEIVE_ACK);
      twi(TWI_RESTART);
      transmit_byte_I2C(fixed_addr_sensor);
      twi(TWI_RECEIVE_ACK);
      while(1&(TWCR << 3)); // Пока установлен флаг TWWC Slave пишет данные в наш регистр TWDR
      twi(TWI_RECEIVE_NACK);
      twi(TWI_STOP);
      return TWDR;
    }
    Я так и делал, просто выполение программы останавливалось в этой функции и я решил по строчно разобрать код и установил то что никто не отвлекается на по I2C.
     
  16. parovoZZ

    parovoZZ Гуру

  17. Danil_2002

    Danil_2002 Гик

    Ок, спасибо
     
  18. Danil_2002

    Danil_2002 Гик

    NikitOS нравится это.