Доброго времени суток , разбираюсь в работе протокола 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'); } } Где затупил?
Сам делал или по примеру? Апноуты читал? В любом случае без симуляции будет очень тяжко и долго. Мутил когда-то такое http://forum.amperka.ru/threads/Суперэкономичный-беспроводной-монитор-Т-и-rh.17221/ С аппаратным не мутил. Буду вместе с тобой разбираца Вот это что такое?
Просто хочу сам реализовать, чтобы понять лучше Читал разные статьи, но там почти всегда всплывают какие-нибудь библиотеки. Свой скейтч писал по чужому примеру работы с EEPROM. "Вот это" это настройка скорости I2C,то есть TWBR и два бита TWSR устанавливают скорость работы шины(буду пробывать ее менять может именно в этом дело), но я знаю что выполнение моей программы останавливается когда avr выставляет на I2C ACK.
С таким же успехом в этот регистр можно ничего не писать. Вот это устанавливает биты в единицу, а затем происходит их инверсия, т.е. они обнулились. Остальные биты в этом байте наоборот стали единицами. Поэтому у меня сразу возник вопрос. Писать нули на то место, где и так нули после сброса - для чего? Остальные биты в регистре - это биты статуса и они read only. I2C - это синхронный интерфейс, поэтому скорость передачи данных не сильно важна до тех пор, пока она не превышает допустимую для слейвов. опыт работы с EEPROM сюда вообще не надо вплетать. тормозится программа может либо здесь либо здесь я в таких случаях (если нет внутрисхемной отладки) расставляю контрольные точки (1, 2, 3 и т.д.) и вывожу их наружу. Таким образом, я понимаю, где торможусь.
Скорее всего тормозит тут, но это не точно: Код (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 порт
ну так смотри. Ты отправляешь адрес слейва потом ты должен получить от него (слейва) квитанцию ACK. Если её получил, то продолжаешь дальше, если квитанцию не получил, то уходишь или в ошибку общения или пробуешь ещё раз. А где это в коде?
У меня при отправке байта возвращается 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, то есть никто не откликнулся }
То есть не байта, а после ret = twi(TWI_TRANSMIT); функция возвращает 48, то есть ни кто не откликнулся
Код (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.
Смотри, как здесь реализовано https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591239 https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591794