Ich versuche, I2C mit einem AVR-Controller ohne die ausgewiesenen internen TWI-Register zu implementieren.
Das I2C-Protokoll erfordert, dass das Master-Gerät zunächst die SDA- und SCL-Leitungen ansteuert, um eine Kommunikationstransaktion zu adressieren und zu beginnen. Das Problem, das ich habe, ist, dass Sie, nachdem eine Adresse über die serielle Leitung gesendet wurde, die Datenleitung "schweben" lassen müssen (dh vom Pull-up-Widerstand hochgezogen werden), damit der Slave die Leitung niedrig treibt für ein ACK-Signal.
Ich kann das I2C-Gerät erfolgreich adressieren, aber wenn ich versuche, die Leitung "schweben" zu lassen, habe ich ein paar Probleme. Ich hatte den Eindruck, dass in einem Eingangs-HIGH-Z-Zustand (Tri-State, dh DDRX 0, PORTX 1 für einen bestimmten Pin) im Wesentlichen wie folgt modelliert würde:
Der Zustand der SDA-Leitung bleibt jedoch hoch, obwohl das I2C-Gerät mit einem ACK-Bit antwortet, indem es die Leitung auf niedrig treibt. Ich weiß, dass dies wahr ist, weil ich eine beträchtliche Verzögerung eingestellt habe, nachdem ich den Pin als Eingang festgelegt habe. Während dieser Verzögerung bleibt die Leitung hoch. Wenn ich jedoch das Kabel entferne, das die MCU mit der SDA-Leitung verbindet, fällt es aufgrund des Slave-Geräts auf LOW.
Kurz gesagt, wie gehen Sie mit dem „Loslassen“ einer Leitung um sicherzustellen, dass ihr Potenzial von den Geräten auf Ihrer I2C-Datenleitung angetrieben wird und nicht von der MCU beeinflusst wird?
/* Pin and direction register manipulations */
#define I2C_CLKDEL 10 //10uS
#define I2C_PORT PORTB
#define SDA PB0
#define SCL PB1
#define set_high(port, pin) (port |= (1<<pin))
#define set_low(port, pin) (port &= ~(1<<pin))
#define set_in(portDDR, pin) (portDDR &= ~(1<<pin))
#define set_out(portDDR, pin) (portDDR |= (1<<pin))
void clkStrobe(void){
set_high(I2C_PORT, SCL);
_delay_us(I2C_CLKDEL);
set_low(I2C_PORT, SCL);
_delay_us(I2C_CLKDEL);
}
uint8_t sendByte(uint8_t byte){ //MSB first
uint8_t count = 8, ack;
set_low(PORTB, SCL);
while( count-- ){
if( byte & 0x80 )
set_high(PORTB, SDA);
else
set_low(PORTB, SDA);
byte <<= 1;
_delay_ms(I2C_CLKDEL);
clkStrobe();
}
//set as input and read in the I2C port data
set_in(I2C_PORT, SDA);
_delay_ms(1000);
ack = PINB;
set_out(I2C_PORT, SDA);
clkStrobe(); //clock in the ACK bit
return (ack & (1<<SDA)); //return ACK bit
}
Eine große Verzögerung wurde eingefügt, nachdem SDA auf der MCU als Eingang zum Testen festgelegt wurde. Die Linie bleibt hoch. Wenn die SDA-Leitung von der MCU entfernt wird (Draht nicht mehr angeschlossen), wird die SDA-Leitung durch das adressierte I2C-Gerät auf Low gesetzt (Gerät sendet ACK). Offensichtlich mache ich hier etwas falsch.
Für set_in übergeben Sie I2C_PORT
, was PORTB
, aber Sie müssen übergeben DDRB
.
Matt Jung
sherrelbc
Matt Jung
sherrelbc
Dzarda
sherrelbc
Dzarda