Forum: Mikrocontroller und Digitale Elektronik Atmega8, TWI und Master Receive


von Lukas Lücking (Gast)


Lesenswert?

Hallo Mikrocontroller-Gemeinde =)

Ich verzweifel an einem Problem und hoffe auf ein bisschen Hilfe! =)

Zu meinem Problem:

Ich habe einen Atmega8 (Master) über TWI an einen TAS5518 (Slave) 
angeschlossen. Dass es jetzt ein TAS5518 ist, ist wohl irrelevant... TWI 
Clock ist 100kHz. Bei Schreibbefehlen tritt keinerlei Fehler auf, nach 
jedem Byte bekomme ich ein ACK. Nun möchte ich die Speicherstelle 0x02 
des TAS5518 auslesen. (ist ein Fehler-Status-byte)

Grundlage ist die lib von Peter Fleury...

Dazu muss ich die Addresse des TAS5518 senden(00110110=0x36) mit einem 
Write, dann 0x02 senden, danach ein rep_start machen, danach wieder die 
Adresse des TAS5518 aber mit einem Read-bit... Hier der mit Oszi 
abgelesene Datenstrom:

00110110    0   00000010  0  restart  00110111   0
Adr.Write  ACK   0x02    ACK  cond.   Adr.Read  ACK

Hier sieht man dass der Slave brav nach jedem byte ACK sendet. Die 
ersten beiden ACKs erkennt der Atmega auch noch. Z.B. steht nach dem 
senden von 0x02 im TWSR 0x28 (Databyte gesendet und Ack empfangen)....
Leider steht nach dem Senden vom Adresse-Read-Byte 0x48 im TWSR, was ja 
bedeutet dass KEIN ACK empfangen wurde... Das ACK kommt aber (laut 
Oszi)... Woran kann das liegen??

Ich bin wirklich dankbar für jeden Hinweis!


Hier noch die Aufrufe die ich verwende:

Hauptprogramm:
i2c_start_wait(TAS5518+I2C_WRITE);
i2c_write(0x02);
i2c_rep_start(TAS5518+I2C_READ);
ret = i2c_readNak();    //Hier bleibt der Controller hängen

Hier die i2c_readNack():
unsigned char i2c_readNak(void)
{
  TWCR = (1<<TWINT) | (1<<TWEN);

  while(!(TWCR & (1<<TWINT)));

    return TWDR;

}/* i2c_readNak */

Da im TWSR steht dass kein ACK empfangen wurde fängt die TWI-Einheit 
nicht an zu laufen beim Versuch TWEN zu setzen, so dass TWINT nie 0 
gesetzt wird.


Danke, Lukas



Hier noch alle anderen Routinen:
/*********************************************************************** 
**
* Title:    I2C master library using hardware TWI interface
* Author:   Peter Fleury <pfleury@gmx.ch>  http://jump.to/fleury
* File:     $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
* Target:   any AVR device with hardware TWI
* Usage:    API compatible with I2C Software Library i2cmaster.h
************************************************************************ 
**/
#include <inttypes.h>
#include <compat/twi.h>
#include <util/delay.h>

#include "i2cmaster.h"


/* define CPU frequency in Mhz here if not defined in Makefile */
#ifndef F_CPU
#define F_CPU 1000000UL
#endif

/* I2C clock in Hz */
#define SCL_CLOCK  100000L


/*********************************************************************** 
**
 Initialization of the I2C bus interface. Need to be called only once
************************************************************************ 
*/
void i2c_init(void)
{
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */

  TWSR = 0;                         /* no prescaler */
  TWBR = ((F_CPU/SCL_CLOCK)-16)/2;  /* must be > 10 for stable operation 
*/

}/* i2c_init */


/*********************************************************************** 
**
  Issues a start condition and sends address and transfer direction.
  return 0 = device accessible, 1= failed to access device
************************************************************************ 
*/
unsigned char i2c_start(unsigned char address)
{
    uint8_t   twst;

  // send START condition
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

  // wait until transmission completed
  while(!(TWCR & (1<<TWINT)));

  // check value of TWI Status Register. Mask prescaler bits.
  twst = TW_STATUS & 0xF8;
  if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

  // send device address
  TWDR = address;
  TWCR = (1<<TWINT) | (1<<TWEN);

  // wail until transmission completed and ACK/NACK has been received
  while(!(TWCR & (1<<TWINT)));

  // check value of TWI Status Register. Mask prescaler bits.
  twst = TW_STATUS & 0xF8;
  if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

  return 0;

}/* i2c_start */


/*********************************************************************** 
**
 Issues a start condition and sends address and transfer direction.
 If device is busy, use ack polling to wait until device is ready

 Input:   address and transfer direction of I2C device
************************************************************************ 
*/
void i2c_start_wait(unsigned char address)
{
    uint8_t   twst;


    while ( 1 )
    {
      // send START condition
      TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

      // wait until transmission completed
      while(!(TWCR & (1<<TWINT)));

      // check value of TWI Status Register. Mask prescaler bits.
      twst = TW_STATUS & 0xF8;
      if ( (twst != TW_START) && (twst != TW_REP_START)) continue;

      // send device address
      TWDR = address;
      TWCR = (1<<TWINT) | (1<<TWEN);

      // wail until transmission completed
      while(!(TWCR & (1<<TWINT)));

      // check value of TWI Status Register. Mask prescaler bits.
      twst = TW_STATUS & 0xF8;
      if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) )
      {
          /* device busy, send stop condition to terminate write 
operation */
          TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

          // wait until stop condition is executed and bus released
          while(TWCR & (1<<TWSTO));

          continue;
      }
      //if( twst != TW_MT_SLA_ACK) return 1;
      break;
     }

}/* i2c_start_wait */


/*********************************************************************** 
**
 Issues a repeated start condition and sends address and transfer 
direction

 Input:   address and transfer direction of I2C device

 Return:  0 device accessible
          1 failed to access device
************************************************************************ 
*/
unsigned char i2c_rep_start(unsigned char address)
{
    return i2c_start( address );
}/* i2c_rep_start */


/*********************************************************************** 
**
 Terminates the data transfer and releases the I2C bus
************************************************************************ 
*/
void i2c_stop(void)
{
    /* send stop condition */
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

  // wait until stop condition is executed and bus released
  while(TWCR & (1<<TWSTO));

}/* i2c_stop */


/*********************************************************************** 
**
  Send one byte to I2C device

  Input:    byte to be transfered
  Return:   0 write successful
            1 write failed
************************************************************************ 
*/
unsigned char i2c_write( unsigned char data )
{
    uint8_t   twst;

  // send data to the previously addressed device
  TWDR = data;
  TWCR = (1<<TWINT) | (1<<TWEN);

  // wait until transmission completed
  while(!(TWCR & (1<<TWINT)));

  // check value of TWI Status Register. Mask prescaler bits
  twst = TW_STATUS & 0xF8;
  if( twst != TW_MT_DATA_ACK) return 1;

  return 0;

}/* i2c_write */


/*********************************************************************** 
**
 Read one byte from the I2C device, request more data from device

 Return:  byte read from I2C device
************************************************************************ 
*/
unsigned char i2c_readAck(void)
{
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
  while(!(TWCR & (1<<TWINT)));

    return TWDR;

}/* i2c_readAck */


/*********************************************************************** 
**
 Read one byte from the I2C device, read is followed by a stop condition

 Return:  byte read from I2C device
************************************************************************ 
*/
unsigned char i2c_readNak(void)
{
  TWCR = (1<<TWINT) | (1<<TWEN);

  while(!(TWCR & (1<<TWINT)));

    return TWDR;

}/* i2c_readNak */

von A. F. (artur-f) Benutzerseite


Lesenswert?

Probier mal folgende Varianten aus, was da passiert:

A):

i2c_start_wait(TAS5518);
i2c_write(0x02);
i2c_stop();
i2c_rep_start(TAS5518 | 0x01);
ret = i2c_readNak();    //Hier bleibt der Controller hängen
i2c_stop();



i2c_start_wait(TAS5518);
i2c_write(0x02);
i2c_stop();
i2c_start_wait(TAS5518 | 0x01);
ret = i2c_readAkc();    //Hier bleibt der Controller hängen
i2c_stop();

von Lukas Lücking (Gast)


Lesenswert?

Hi Artur!

Ist ne gute Idee gewesen =) Leider bleibt der controller jedes mal in 
der while()-Schleife sowohl beim readNak oder readAck "hängen". Auch mit 
nem "richtigem" Stop, und start... Die Funktion 
i2c_start_wait(TAS5518|0x01) wird ausgeführt, sprich die Funktion 
erkennt dass ein Ack empfangen wurde, nur leider steht beim Aufruf der 
I2c_readNak() im TWSR 0x48, also KEIN Ack empfangen...

Danke trozdem!
Lukas

von Jean P. (fubu1000)


Lesenswert?

Hi,
mach mal vor dem Repeated Start ne 50µs Pause (oder weniger, ich war 
aber zu faul weiter zu testen, funzte ja), dann hat es bei mir geklappt 
mit nem TDA 7318.

Also so:

i2c_start_wait(TAS5518+I2C_WRITE);
i2c_write(0x02);
_delay_us(50);
i2c_rep_start(TAS5518+I2C_READ);
ret = i2c_readNak();

Gruß und viel Erfolg.

von Gerhard (Gast)


Lesenswert?

hallo!

ich habe eine ähnliche konstellation.möchte den SMB380 
Beschleunigungssensor von bosch mit einem ATmega1281 im 
Master-Receive-Mode auslesen.mein problem ist,dass sich der µC in einem 
funkmodul befindet und ich aufgrund dessen,die twi-schnittstelle über 
die API´s des funkprotokolls verwenden muss.
eigentlich ist aber der ablauf genau wie sonst beim TWI-protokoll.

damit ich sehen kann ob überhaupt etwas passiert,wollte ich zuerst eimal 
ein register des SMB380 auslesen (genauerer gesagt die register address 
0x00 die mir die chip-id liefert).

dafür hat das funkprotokoll die funktion 
"HAL_ReadI2cPacket(HAL_I2cParams_t *reparams)"

die typedef struct sieht dabei so aus:

typedef struct
{
  /** \brief slave address */
  uint8_t id;
  /** \brief number of bytes to be written to the bus */
  uint16_t length;
  /** \brief pointer to the data (for avr internal address include to 
data field)*/
  uint8_t *data;
  /** \brief callback function */
  void (*f)(bool result);
} HAL_I2cParams_t;


meine frage geht vor allem in die richtung,was ich bei "uint8_t *data" 
reinschreibe?
wie mache ich der Read-Funktion klar,dass sie der Adresse ausgibt,die 
ich auslesen will?

wenn ich der struktur folgende werte übergebe:

accessI2c.id = 0x38; // slave-adresse des SMB380
accessI2c.length = 2; // 2byte schreiben
accessI2c.data = (uint8_t *) & ?????;
accessI2c.f = f;
HAL_ReadI2cPacket(&accessI2c);

liefert mir das statusregister TWSR anfangs 0x08 zurück und dann 
0x50.also wurden daten erfolgreich empfangen.
wie konfigurier ich das nun für den wert,den ich aus einem register 
lesen will?

von Gerhard (Gast)


Lesenswert?

ach so: access.f = f; ist die callback-funktion

von Gerhard (Gast)


Lesenswert?

kann mir hier keiner helfen?
mir geht es hauptsächlich darum,was ich für die definition:

/** \brief pointer to the data (for avr internal address include to
data field)*/

in mein programm schreibe! ich dachte mir halt nach folgendem code
accessI2c.data = (uint8_t *) & ?????;

das ich für "?????" evtl ein feld rein schreibe,worin die 
registeradresse 0x00 die ich auslesen will drin steht?

bin für jede hilfe dankbar!

Im datenblatt des SMB380 steht folgendes:
"To be able to access registers in read mode,first address has to be 
send in write mode.Then a stop and a start condition are issued and data 
bytes are transferred with automatic address increment."

von tommy776 (Gast)


Lesenswert?

Hallo Gemeinde.

Wäre gut,wenn mir jemand helfen könnte.habe ein ähnliches Problem,nur 
anders herum:

Ich nutze auch die lib von Peter Fleury und lese damit einen Sensor aus.
Ich will auch erstmal probieren,um Register ausgelesen werden können.

Im AVR-Studio zeigt es mir das erfolgreiche Auslesen des Register 
an,d.h. wenn ich Register "0x00" auslesen will,steht dann auch im TWDR 
"0x02".

Das Problem was ich habe ist,dass mir der Datenstrom im Oszi einen 
völlig anderen Registerinhalt anzeigt!!Anstatt der 0x02 steht dann 
meistens ein 0x07 oder 0x05.

Komischerweise sehe ich auch keinen ACK-Impuls nach dem repeated 
start,aber debugge ich mein Programm bis dahin gibt mir das 
TWI-Statusregister trotzdem 0x40 aus,was heißt: "SLA+R has been 
transmitted;ACK has been received"

Kann mir jemand helfen?

Hier noch mein Code:

void startInclinationSensor(void)
{
 uint8_t inclination;
 DDRC = 0x00;
 initI2c();
 while(1)
 {
 startI2c(0x70);
 writeI2c(0x0B);
 rep_startI2c(0x71);
 inclination = readAckI2c();
 PORTC = inclination;
 stopI2c();
 }
}

von tommy776 (Gast)


Lesenswert?

noch ein kleiner Nachtrag:

Wenn ich while-Schleife weglasse zeigt es mir auch dann im AVR-Studio 
meistens den falschen Registerinhalt (also die 0x05 oder 0x07) an...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.