AT91-TWI: Unterschied zwischen den Versionen
K (Textersetzung - „</c>“ durch „<syntaxhighlight>“) |
K (Textersetzung - „<c>“ durch „<syntaxhighlight lang="c">“) |
||
Zeile 36: | Zeile 36: | ||
Die LIB funktioniert mit: (bitte ergänzen) | Die LIB funktioniert mit: (bitte ergänzen) | ||
<c> | <syntaxhighlight lang="c"> | ||
+AT91SAM7S64 | +AT91SAM7S64 | ||
+AT91SAM7S256 | +AT91SAM7S256 | ||
Zeile 43: | Zeile 43: | ||
Die Lib funktioniert (noch) nicht mit: | Die Lib funktioniert (noch) nicht mit: | ||
<c> | <syntaxhighlight lang="c"> | ||
+AT91SAM9260 | +AT91SAM9260 | ||
<syntaxhighlight> | <syntaxhighlight> | ||
Zeile 53: | Zeile 53: | ||
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. | 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 <i>crazy</i> aussieht. | Ich habe bisher leider erst eine Möglichkeit gefunden dies zu tun, welche ein wenig <i>crazy</i> aussieht. | ||
<c> | <syntaxhighlight lang="c"> | ||
*AT91C_TWI_IDR = (unsigned int) -1; | *AT91C_TWI_IDR = (unsigned int) -1; | ||
*AT91C_TWI_IER = (1<<0); | *AT91C_TWI_IER = (1<<0); | ||
Zeile 61: | Zeile 61: | ||
Nachdem auf TX_COMP bei dem setzen der Adresse gewartet wird hatte ich das Problem, dass ich kein NACK bekommen habe. | Nachdem auf TX_COMP bei dem setzen der Adresse gewartet wird hatte ich das Problem, dass ich kein NACK bekommen habe. | ||
mit einer kleinen wartefunktion. | mit einer kleinen wartefunktion. | ||
<c> | <syntaxhighlight lang="c"> | ||
#pragma optimize=none | #pragma optimize=none | ||
void twi_nack_wait(){ | void twi_nack_wait(){ | ||
Zeile 75: | Zeile 75: | ||
===#define=== | ===#define=== | ||
Hier werden nur diverse Rückgabewerte definiert (unsigned char). | Hier werden nur diverse Rückgabewerte definiert (unsigned char). | ||
<c> | <syntaxhighlight lang="c"> | ||
#define TWI_NACK (1<<0) | #define TWI_NACK (1<<0) | ||
#define TWI_TXTIMEOUT (1<<1) | #define TWI_TXTIMEOUT (1<<1) | ||
Zeile 88: | Zeile 88: | ||
===Kleines Beispiel=== | ===Kleines Beispiel=== | ||
<c> | <syntaxhighlight lang="c"> | ||
int main( void ) { | int main( void ) { | ||
unsigned char adresse[0xff], config[0xff]; //Variablen Initalisieren | unsigned char adresse[0xff], config[0xff]; //Variablen Initalisieren | ||
Zeile 103: | Zeile 103: | ||
Stop Condition senden und warten bis die Übertragung vollständig ist. | 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. | Des weiteren wird dem AIC gesagt, dass der IRQ gelöscht werden soll und ein weiterer "BUG" umgangen. | ||
<c> | <syntaxhighlight lang="c"> | ||
*AT91C_TWI_IDR = (unsigned int) -1; | *AT91C_TWI_IDR = (unsigned int) -1; | ||
*AT91C_TWI_IER = (1<<0); | *AT91C_TWI_IER = (1<<0); | ||
<syntaxhighlight> | <syntaxhighlight> | ||
<c> | <syntaxhighlight lang="c"> | ||
//*---------------------------------------------------------------------------- | //*---------------------------------------------------------------------------- | ||
//* \fn twi_read | //* \fn twi_read | ||
Zeile 138: | Zeile 138: | ||
Die Funktionen müssen noch etwas verändert werden. | Die Funktionen müssen noch etwas verändert werden. | ||
Desweiteren muss eine | Desweiteren muss eine | ||
<c> | <syntaxhighlight lang="c"> | ||
void dummy_irq(void){} | void dummy_irq(void){} | ||
<syntaxhighlight> | <syntaxhighlight> | ||
funktion erstellt werden um dem AIC einen Handler zu geben.. | funktion erstellt werden um dem AIC einen Handler zu geben.. | ||
<c> | <syntaxhighlight lang="c"> | ||
//*========================================================= | //*========================================================= | ||
//* INIT | //* INIT | ||
Zeile 194: | Zeile 194: | ||
===twi_read=== | ===twi_read=== | ||
<c> | <syntaxhighlight lang="c"> | ||
//*---------------------------------------------------------------------------- | //*---------------------------------------------------------------------------- | ||
//* \fn twi_read | //* \fn twi_read | ||
Zeile 242: | Zeile 242: | ||
===twi_write=== | ===twi_write=== | ||
<c> | <syntaxhighlight lang="c"> | ||
//*---------------------------------------------------------------------------- | //*---------------------------------------------------------------------------- | ||
//* \fn twi_write | //* \fn twi_write | ||
Zeile 289: | Zeile 289: | ||
===twi_scan=== | ===twi_scan=== | ||
<c> | <syntaxhighlight lang="c"> | ||
//*---------------------------------------------------------------------------- | //*---------------------------------------------------------------------------- | ||
//* \fn twi_scan | //* \fn twi_scan | ||
Zeile 326: | Zeile 326: | ||
===twi_read_lm75=== | ===twi_read_lm75=== | ||
<c> | <syntaxhighlight lang="c"> | ||
//*---------------------------------------------------------------------------- | //*---------------------------------------------------------------------------- | ||
//* \fn twi_read_lm75 | //* \fn twi_read_lm75 |
Version vom 23. Juni 2013, 21:12 Uhr
- Aktualisierung
- Im AT91 Software Package zum jeweiligen Controller findet man TWI Beispielcode inkl. leicht wiederverwendbarer Treiber, in denen die Besonderheiten und gegf. auch (alle?) Fehler der Hardware berücksichtigt werden. Siehe z. B. das TWI-Eeprom-Beispiel im Software Package Version 1.5 zum AT91SAM7-EK. Weiterhin bieten die Treiber aus dem Software-Package die nützliche Möglichkeit, einen I²C/TWI-Transfer asynchron (also "im Interrupt") ablaufen zu lassen. (Aktualisierung von Benutzer:mthomas)
von Benutzer:Jens
Entstanden sind diese Beispiele bevor die AP-Notes von Atmel veröffentlicht worden. Scheinbar hatte Atmel bei mir abgeschaut . Vielen dank dennoch an das Support Center von Atmel, den ich allen ans Herz legen kann.
Diese Beispiele beziehen sich warscheinlich auf alle ARM Controller von Atmel. Ich hätte die Beschreibungen von diversen SAM7 und RM92xx Controllern verglichen und konnte keinen Unterschied finden. Getestet habe ich die Lib mit dem AT91SAM7S64 und AT91SAM7S256. Die neuen kleineren AT91SAM7S Controller haben ein neues TWI Interface.
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 zum TWI genau durchlesen, was mich viel Zeit gekostet hat war das hier.
- TWI: NACK Status Bit Lost
- 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.
- Problem Fix/Workaround
- 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.
Getestete Controller
Die LIB funktioniert mit: (bitte ergänzen) <syntaxhighlight lang="c"> +AT91SAM7S64 +AT91SAM7S256 +AT91RM9200 <syntaxhighlight>
Die Lib funktioniert (noch) nicht mit: <syntaxhighlight lang="c"> +AT91SAM9260 <syntaxhighlight>
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. <syntaxhighlight lang="c">
*AT91C_TWI_IDR = (unsigned int) -1; *AT91C_TWI_IER = (1<<0);
<syntaxhighlight>
Nachtrag: Nachdem auf TX_COMP bei dem setzen der Adresse gewartet wird hatte ich das Problem, dass ich kein NACK bekommen habe. mit einer kleinen wartefunktion. <syntaxhighlight lang="c">
- pragma optimize=none
void twi_nack_wait(){
unsigned short timeout = MCK * 3 / AT91C_TWI_CLOCK; while(timeout--);
} <syntaxhighlight> konnte ich das Problem beheben. das Pragma sollte Selbsterklärend sein..
Funktionen
Hier sind nun die Funktionen und deren Beschreibung
#define
Hier werden nur diverse Rückgabewerte definiert (unsigned char). <syntaxhighlight lang="c">
- define TWI_NACK (1<<0)
- define TWI_TXTIMEOUT (1<<1)
- define TWI_TXTIMEOUT_READ (1<<2)
- define TWI_IRQIMEOUT_CLEAR (1<<3)
- define TWI_STOPTIMEOUT_CR (1<<3)
- define TWI_TXTIMEOUT_WRITE (1<<4)
- define TWI_IRQTIMEOUT_CLEAR (1<<5)
- define TWI_TIMEOUT_STOP (1<<6)
<syntaxhighlight>
Kleines Beispiel
<syntaxhighlight lang="c"> int main( void ) {
unsigned char adresse[0xff], config[0xff]; //Variablen Initalisieren AT91F_TWI_Open(); //TWI Initalisieren twi_scan(data); //Slaves am Bus finden (zum Schreiben und Lesen) twi_read(0x50, 0x00, config, 0xf0); //Daten aus einem EEProm lesen (IIC Adresse, DatenStart, SpeicherPointer, Bytemenge) twi_write(0x52, 0x00, config, 0xf0); //Daten in ein anderes EEProm schreiben (IIC Adresse, DatenStart, SpeicherPointer, Bytemenge) while(1);
} <syntaxhighlight>
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. <syntaxhighlight lang="c">
*AT91C_TWI_IDR = (unsigned int) -1; *AT91C_TWI_IER = (1<<0);
<syntaxhighlight> <syntaxhighlight lang="c"> //*---------------------------------------------------------------------------- //* \fn twi_read //* \brief Read one or more byte from a TWI device //*---------------------------------------------------------------------------- unsigned char twi_stop(void){
int timeout; //Set the timeout value int dummy; *AT91C_TWI_IDR = 0xffffffff; //Disable TWI IRQs *AT91C_TWI_CR = AT91C_TWI_STOP; //Send stop condition timeout = 50000; while((*AT91C_AIC_IPR & (1<<AT91C_ID_TWI)) && timeout--) //Wait for cleared AT91C_ID_TWI in AT91C_AIC_IPR *AT91C_AIC_ICCR = (1<<AT91C_ID_TWI); //Clear IRQ in AIC *AT91C_TWI_IER = (1<<0); if(timeout == 0) return TWI_IRQTIMEOUT_CLEAR; //IRQ not clear dummy = *AT91C_TWI_RHR; dummy = dummy; timeout = 10000000; while(!(*AT91C_TWI_SR & AT91C_TWI_TXCOMP) && timeout--); //Wait for TXCOMP in TWI_SR dummy = *AT91C_TWI_SR; dummy = dummy; if(timeout == 0) return TWI_TIMEOUT_STOP; //Exit on TWI error return 0;
} <syntaxhighlight>
twi_init / AT91F_SetTwiClock
Die Funktionen müssen noch etwas verändert werden. Desweiteren muss eine <syntaxhighlight lang="c"> void dummy_irq(void){} <syntaxhighlight> funktion erstellt werden um dem AIC einen Handler zu geben.. <syntaxhighlight lang="c"> //*========================================================= //* INIT //*========================================================= //*---------------------------------------------------------------------------- //* \fn AT91F_SetTwiClock //* \brief Initialization //*---------------------------------------------------------------------------- void AT91F_SetTwiClock(void){
int sclock; /* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6)*/
sclock = (10*MCK /AT91C_TWI_CLOCK); if (sclock % 10 >= 5) sclock = (sclock /10) - 5; else sclock = (sclock /10)- 6; sclock = (sclock + (4 - sclock %4)) >> 2; // div 4 AT91C_BASE_TWI->TWI_CWGR = ( 1<<16 ) | (sclock << 8) | sclock;
}
//*----------------------------------------------------------------------------
//* \fn AT91F_TWI_Open
//* \brief Initializes TWI device
//*----------------------------------------------------------------------------
void AT91F_TWI_Open(void){
// Configure TWI PIOs AT91F_TWI_CfgPIO (); // Configure PMC by enabling TWI clock AT91F_TWI_CfgPMC ();
// Configure TWI in master mode AT91F_TWI_Configure (AT91C_BASE_TWI);
// Set TWI Clock Waveform Generator Register AT91F_SetTwiClock(); //TWI NACK Bug workaround AT91F_AIC_ConfigureIt ( AT91C_BASE_AIC, AT91C_ID_TWI, 7, AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE, dummy_irq); *AT91C_TWI_IER = (1<<0); *AT91C_TWI_CR = AT91C_TWI_MSEN;
}
<syntaxhighlight>
twi_read
<syntaxhighlight lang="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; timeout = 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))){ //TX Complete TWI irq polling timeout++; if(timeout >= 10000000){ twi_stop(); return TWI_TXTIMEOUT; //Exit on TXCOMP timeout } } twi_nack_wait(); if(*AT91C_TWI_SR & AT91C_TWI_NACK){ //Slave exist and send ACK? twi_stop(); return TWI_NACK; //Exit on 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(); return TWI_TXTIMEOUT_READ; //Exit on RXRDY timeout } } *(data+(counter++)) = *AT91C_TWI_RHR; //Read data } twi_stop(); return 0;
} <syntaxhighlight>
twi_write
<syntaxhighlight lang="c"> //*---------------------------------------------------------------------------- //* \fn twi_write //* \brief Read one or more byte from a TWI device //*---------------------------------------------------------------------------- unsigned char twi_write(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; timeout = 0; *AT91C_TWI_CR = AT91C_TWI_MSEN; //Enable the TWI Master Mode *AT91C_TWI_IADR = mem_adr; //Slave address *AT91C_TWI_MMR = ((dev_adr<<16) //Set the TWI Slave memory address | AT91C_TWI_IADRSZ_1_BYTE); //Master read mode *AT91C_TWI_CR = AT91C_TWI_START; //Send the start condition slave address and set write mode while(!(*AT91C_AIC_IPR & (1<<AT91C_ID_TWI))){ //TX Complete TWI irq polling timeout++; if(timeout >= 100000){ twi_stop(); return TWI_TXTIMEOUT; //Exit on TXCOMP timeout } } twi_nack_wait(); if(*AT91C_TWI_SR & AT91C_TWI_NACK){ //Slave exist and send ACK? twi_stop(); return TWI_NACK; } counter = 0; while(n_byte--){ //Writeloop timeout = 0; *AT91C_TWI_THR = *(data+(counter++)); //Write data while((!(*AT91C_TWI_SR & AT91C_TWI_TXRDY))){ //Wait for txready timeout++; if(timeout>=1000000){ twi_stop(); return TWI_TXTIMEOUT_READ; //Exit on RXRDY timeout } } } twi_stop(); return 0;
} <syntaxhighlight>
twi_scan
<syntaxhighlight lang="c"> //*---------------------------------------------------------------------------- //* \fn twi_scan //* \scan the bus for twi slaves //*---------------------------------------------------------------------------- unsigned char twi_scan(unsigned char *data){ // datapointer for the return data
unsigned int timeout = 0; unsigned char mem_adr = 0x01; unsigned char i, counter; for(i = 0; i <= 0xFE; i++){ timeout = 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 = ((i<<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))){ //TX Complete TWI irq polling timeout++; if(timeout >= 10000000){ twi_stop(); return TWI_TXTIMEOUT; //Exit on TXCOMP timeout } } twi_nack_wait(); if(*AT91C_TWI_SR & AT91C_TWI_NACK){ //Slave exist and send ACK? counter++; *(data+counter) = i; } twi_stop(); } return 0;
} <syntaxhighlight>
twi_read_lm75
<syntaxhighlight lang="c"> //*---------------------------------------------------------------------------- //* \fn twi_read_lm75 //* \brief Read the temprature of an lm75 sensor //*---------------------------------------------------------------------------- unsigned char twi_read_lm75(unsigned char dev_adr, // slave address
unsigned char dev_res, // device resulution unsigned char *data){ // datapointer for the return data
unsigned char error, temp[3]; unsigned int milli; data[0] = dev_res; if(error = twi_write(dev_adr, 0x1, data, 1)) return error; if(error = twi_read(dev_adr, 0x0, temp, 2)) return error; temp[2] = temp[0];
milli = (((temp[1] & 0xf0) >> 4) * 625)/100;
data[20]= temp[2]; data[21]= milli; sprintf((char *) data, "%s%i,%02i°C ", (temp[2]&0x40)?"-":"+", temp[0] & 0x7f, milli); return 0;
} <syntaxhighlight>