AT91-TWI: Unterschied zwischen den Versionen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche
Keine Bearbeitungszusammenfassung
Zeile 48: Zeile 48:
</c>
</c>


===twi_clear_irq===
===twi_stop===
Mit dieser Funktion wird dem AIC gesagt, dass der IRQ gelöscht werden soll
Stop Condition senden und warten bis die Übertragung vollständig ist.
Des weiteren wird dem AIC gesagt, dass der IRQ gelöscht werden soll und ein weiterer "BUG" umgangen.
<c>
<c>
//*----------------------------------------------------------------------------
   *AT91C_TWI_IDR = (unsigned int) -1;
//* \fn    twi_clear_irq
   *AT91C_TWI_IER = (1<<0);
//* \brief Clear the TWI IRQ and wait for finish
//*----------------------------------------------------------------------------
void twi_clear_irq(void){
  int timeout = 1000000;
   *AT91C_AIC_ICCR = (1<<AT91C_ID_TWI);
   while((*AT91C_AIC_IPR & (1<<AT91C_ID_TWI))&& timeout--); //Wait for release
  if(timeout == 0)
    return TWI_IRQTIMEOUT_CLEAR;
}
</c>
</c>
===twi_stop===
Stop Condition senden und warten bis die Übertragung vollständig ist.
Könnte man sicher mit der twi_clear_irq() verbinden.
<c>
<c>
//*----------------------------------------------------------------------------
//*----------------------------------------------------------------------------
//* \fn    twi_stop
//* \fn    twi_stop
//* \brief Send the stop condition
//* \brief Send the stop condition and clear IRQ
//*----------------------------------------------------------------------------
//*----------------------------------------------------------------------------
void twi_stop(void){
 
   int timeout = 1000000;
 
===twi_init===
unsigned char twi_stop(void){
   int timeout = 500000;
  *AT91C_TWI_IDR = (unsigned int) -1;
   *AT91C_TWI_CR = AT91C_TWI_STOP;
   *AT91C_TWI_CR = AT91C_TWI_STOP;
   while(!(*AT91C_TWI_SR & AT91C_TWI_TXCOMP) && timeout--);         //Wait for trans. comp.
  *AT91C_AIC_ICCR = (1<<AT91C_ID_TWI);
  while((*AT91C_AIC_IPR & (1<<AT91C_ID_TWI)) && timeout--); 
  *AT91C_TWI_IER = (1<<0);
  if(timeout == 0)
    return TWI_IRQTIMEOUT_CLEAR;
  timeout = 500000;
   while(!(*AT91C_TWI_SR & AT91C_TWI_TXCOMP) && timeout--);
   if(timeout == 0)
   if(timeout == 0)
     return TWI_TIMEOUT_STOP;
     return TWI_TIMEOUT_STOP;
   *AT91C_TWI_CR = AT91C_TWI_MSDIS;
   return 0;
}
}
</c>
</c>
===twi_init===


===twi_read===
===twi_read===

Version vom 24. April 2007, 19:19 Uhr

Diese Beispiele beziehen sich warscheinlich auf alle ARM Controller von Atmel. Ich htte die Beschreibungen von diversen SAM7 und RM92xx Controllern verglichen und konnte keinen Unterschied finden.

Einführung

Kurze Einführung und Erläuterungen zu den mir bekannten TWI BUGs. Mit dem TWI hat sich ATMEL bei ihren ARM Controllern ziemlich ins Bein geschossen und einige BUGs eingebaut, wo man teilweise keine Infos zu findet.

Als erstes sollte man sich das ERRATA zu den TWI genau durchlesen, was mich viel Zeit gekostet hat war das hier.

  1. TWI: NACK Status Bit Lost
  2. During a master frame, if TWI_SR is read between the Non Acknowledge condition detection and the TXCOMP bit rising in the TWI_SR, the NACK bit is not set.
  3. Problem Fix/Workaround
  4. The user must wait for the TXCOMP status bit by interrupt and must not read the TWI_SR as long as transmission is not completed. TXCOMP and NACK fields are set simultaneously and the NACK #field is reset after the read of the TWI_SR.

Da ich keine globale Variable nutzen wollte, die in einem IRQ Handler gesetzt wird habe ich mich für das manuelle auslesen des AIC_IPR Registers entschieden (was neue Probleme mit sich zieht dazu später mehr).

Evtl. für die nicht so erfahrenen, wenn man das Programm über JTAG im SRAM laufen lässt muss man vorher den Controller Hardware reseten, da der AIC sonst nicht bei einem neuem IRQ reagiert.

Ziel war es die Funktionen und Möglichkeiten den I²C soweit, wie möglich zu nutzen und das heißt nicht das „Not Acknowledge“ links liegen lassen, sondern auch dieses zu nutzen.

Die Sourcen von Atmel haben entweder nur 1 Byte eingelesen womit sehr viel Zeit für das lesen mehrerer Bytes verschwendet wurde.


Zusammenfassung der BUGs:

TWI_SR darf nicht gelesen werden, wenn TXCOMP in dem Register noch nicht gesetzt wird und man das NACK Bit auswerten will. Um dies zu umgehen lasse ich in den TWI Controller den TXCOMP IRQ auslösen (*AT91C_TWI_IER = (1<<0);) sobald der Controller die TWI Daten übertragen hat. Dazu muss man dem Advanced Interrupt Controller einen Dummy IRQ Handler zuweisen, und Einrichten (siehe twi_init()). Wenn man mit dem Interrupt Clear Command Register (AIC_ICCR) den IRQ löschen will muss man warten, das TWI_ID Bit in dem Interrupt Polling Register (AIC_IPR) gelöscht ist. Dabei wird jedoch nicht der IRQ dem TWI Controller bestätigt. Ich habe bisher leider erst eine Möglichkeit gefunden dies zu tun, welche ein wenig crazy aussieht. <c>

 *AT91C_TWI_IDR = (unsigned int) -1;
 *AT91C_TWI_IER = (1<<0);

</c>

Funktionen

Hier sind nun die Funktionen und deren Beschreibung

#define

Hier werden nur diverse Rückgabewerte definiert (unsigned char). <c>

  1. define TWI_NACK (1<<0)
  2. define TWI_TXTIMEOUT (1<<1)
  3. define TWI_TXTIMEOUT_READ (1<<2)
  4. define TWI_IRQIMEOUT_CLEAR (1<<3)
  5. define TWI_STOPTIMEOUT_CR (1<<3)
  6. define TWI_TXTIMEOUT_WRITE (1<<4)
  7. define TWI_IRQTIMEOUT_CLEAR (1<<5)
  8. define TWI_TIMEOUT_STOP (1<<6)

</c>

twi_stop

Stop Condition senden und warten bis die Übertragung vollständig ist. Des weiteren wird dem AIC gesagt, dass der IRQ gelöscht werden soll und ein weiterer "BUG" umgangen. <c>

 *AT91C_TWI_IDR = (unsigned int) -1;
 *AT91C_TWI_IER = (1<<0);

</c> <c> //*---------------------------------------------------------------------------- //* \fn twi_stop //* \brief Send the stop condition and clear IRQ //*----------------------------------------------------------------------------


twi_init

unsigned char twi_stop(void){

 int timeout = 500000;
 *AT91C_TWI_IDR = (unsigned int) -1;
 *AT91C_TWI_CR = AT91C_TWI_STOP;
 *AT91C_AIC_ICCR = (1<<AT91C_ID_TWI);
 while((*AT91C_AIC_IPR & (1<<AT91C_ID_TWI)) && timeout--);  
 *AT91C_TWI_IER = (1<<0);
 if(timeout == 0)
    return TWI_IRQTIMEOUT_CLEAR;
 timeout = 500000;
 while(!(*AT91C_TWI_SR & AT91C_TWI_TXCOMP) && timeout--);
 if(timeout == 0)
   return TWI_TIMEOUT_STOP;
 return 0;

} </c>

twi_read

<c> //*---------------------------------------------------------------------------- //* \fn twi_read //* \brief Read one or more byte from a TWI device //*---------------------------------------------------------------------------- unsigned char twi_read(unsigned char dev_adr, // slave address

                      unsigned char mem_adr,    // internal memory address
                      unsigned char *data,      // datapointer for the return data
                      unsigned char n_byte){    // count of bytes to read
 unsigned int timeout = 0;
 unsigned char counter;
 *AT91C_TWI_IDR = (unsigned int) -1;            //ACK the last TWI IRQ and enable
 *AT91C_TWI_IER = (1<<0);
 *AT91C_TWI_CR = AT91C_TWI_MSEN;                //Enable the TWI Master Mode
 *AT91C_TWI_IADR = mem_adr;                     //Set the TWI Slave memory address
 *AT91C_TWI_MMR = ((dev_adr<<16)                //Slave address
                   | AT91C_TWI_MREAD            //Master read mode
                   | AT91C_TWI_IADRSZ_1_BYTE);  //Slave internal addtess site 1 byte
 *AT91C_TWI_CR = AT91C_TWI_START;               //Send the start condition slave address and set read mode
 while(!(*AT91C_AIC_IPR & (1<<AT91C_ID_TWI))){  //Wait for TXCOMP irq (polling)
   timeout++;
   if(timeout >= 10000000){
     twi_stop();
     twi_clear_irq();
     return TWI_TXTIMEOUT;                      //Exit on TXCOMP timeout
   }
 }
 if(*AT91C_TWI_SR & AT91C_TWI_NACK){            //Slave exist and send ACK?
   twi_stop();
   twi_clear_irq();
   return TWI_NACK;
 }
 counter = 0;
 while(n_byte--){                               //Recive
   timeout = 0;
   while((!(*AT91C_TWI_SR & AT91C_TWI_RXRDY))){ //Wait for data redady
     timeout++;
     if(timeout>=1000000){
       twi_stop();
       twi_clear_irq();
       return TWI_TXTIMEOUT_READ;               //Exit on RXRDY timeout
     }
   }
   *(data+(counter++)) = *AT91C_TWI_RHR;        //Read data
 }
 twi_stop();
 twi_clear_irq();                               //Clear the IRQ
 return 0;

} </c>

twi_write

twi_scan