Ich vermisse diese Möglichkeit auch beim TWI Treiber von AVRco.
Genau aus diesem Grund konnte ich damals keine Daten aus einem ADAU1701 lesen und musste mir diesen Zugriff selber implementieren. (Bestimmte EEproms benötigen diese Zugriffsart auch)
Warnung:
Ich möchte aber unbedingt darauf hinweisen, dass meine Function 'twiCombinedRead' nicht umfangreich auf Robustheit getestet ist und nur zusammen mit einem importierten TWIE Treiber funktioniert. Die Schwierigkeit beim I2C ist eine gute und robuste Außnahmebehandlung. Meine Funktion 'twiCombinedRead' implementiert aber keine einzige Ausnahmebehandlung, sondern lediglich einen Timeout als Notausstieg. In meiner Applikation verlasse ich mich hoffnungsvoll
auf TwiOutE, dass Ausnahmesituation gelöst werden.
Timeout:
Hier benutze ich einen SysTimer8.
Meine Applikation verwendet kein Multithreading, so dass ein timeout von 20 ms bei einer I2C baudrate von 200kbaud hier okay ist.
function dspReadCapture:
Das ist ADAU1701 spezifisch. Zuerst muss ein bestimmter Wert in ein Register geschrieben werden, um anschließend daraus einen bestimmten Laufzeitwert lesen zu können.
Ausnahmesituationen bei I2C (soweit mir bekannt):
1.) bus state Logik hat fälschlicherweise eine Startbedingung detektiert. Die ist möglich bei einer EMV Störung. Hier hilft ein Reset der bus state logik.
2.) Ein Reset bei der Master-CPU während eines Lesezugriffs kann dazu führen, dass eine SCL Leitung vom Slave durch clock stretching dauerhaft auf 0-Pegel gezogen wird. Der Master hat dann keine Möglichkeit, eine neue Startbedingung zu erzeugen. Hier muss man die clock Leitung toggeln, bis diese wieder frei wird.
3.) bus error / arbitration error: Fehler, die durch EMV auftreten könnten könnten. In diesen Fällen würde der Master auf eine Stopp-Bedingung warten. Hier hilft wahrscheinlich die Kombination toggeln der SCL Leitung + bus state logic reset.
4.) Dauerkurzschluss auf einer Leitung:
Nach Aufhebung eines Kurzschlusses sollte I2C wieder funktionieren
Angst:
Meine größte Angst bei I2C sind Ausnahmefälle, die im schlimmsten Fall dazu führen, das die I2C Kommunikation für immer ausfällt oder sogar eine Endlosschleifenbedingung entsteht.
Beides Fälle, die selbst Hersteller von Mikrocontrollern in Ihren eigenen Treibern nicht im Griff haben. Ich glaube, dass deswegen auch die Slave Treiber beim xmega noch nicht implementiert wurden. Diese musste ich mir auch selber implementieren und im Sinne der Robustheit oft nachbessern. Ein guter Test ist das unkontrollierte Kurzschließen der I2C Leitungen SDA und SCL mit Litze. Prellt schön. Wenn die Kommunikation danach weiter funktioniert, ist man schon weit.
Codestyle:
Soweit ich mich erinnere ist mein Code lediglich eine 1:1 'user manual' Umsetzung für einen xmega32A4. Ohne user manual verstehe ich auch nur Bahnhof und fie Kommentare sind auch nicht hilfreich.
Schlussbemerkung:
Ich verwende meinen Code in einem Produkt mit einer zusätzlichen Vorsorgemaßnahmen. Im Falle eines I2C-Kommunikationsausfalls schaltet ich, einfach gesagt, ein Fehlerausgang.
Code
program UDSPM;
Device = xmega32A4, VCC=3.3;
Define_Fuses // set bits are zero
Override_Fuses;
NoteBook = A;
Supply = 3.3, 0; // no supply
SPIclk = 2000000; // programmer clock 2MHz
COMport = USB;
LockBits0 = [];
FuseBits0 = [];
FuseBits1 = [];
FuseBits2 = [];
FuseBits5 = [BODLEVEL0, BODLEVEL2, BODACT0]; // BOD = 2.9V if XMEGA A
//FuseBits5 = [BODLEVEL0, BODLEVEL1, BODLEVEL2, BODACT0]; //BOD = 3V if XMEGA AU
Import Systick, TWI_E;
from system import longword;
Define
//OSCtype = int32MHz, PLLmul=4, prescA=1, prescB=1, prescC=1;
OSCtype = extXTAL=16000000, PLLmul=2, prescA=1, prescB=1, prescC=1;
SysTick = 10; {msec}
StackSize = 128, iData;
FrameSize = 128, iData;
TWIprescE = 75; // TWI speed = 200kbit/s @32 MHz
// TWIpresc = f_sys / (2*f_TWI) - 5;
// 100 kbit/s @32Mhz -> TWIpresc = 155
// 200 kbit/s @32Mhz -> TWIpresc = 75
// 400 kbit/s @32Mhz -> TWIpresc = 35
Implementation
const
DSPADDR : byte = $68 shr 1;
DATACAPTURE0 : word = $081A; // data capture register 0
var
twitimeout : sysTimer8;
data : longword;
{$IDATA}
function twiCombinedRead (subaddress : Word; pData : pointer; bytesToRead : byte) : boolean;
begin
SetSysTimer(twitimeout, 2); //
// write I2C address Byte + RW = 0
TWIEMASTERADDR := DSPADDR shl 1;
repeat
until Bit(TWIEMASTERSTATUS, 6) or isSysTimerZero(twitimeout); // wif?
if TWIEMASTERSTATUS <> $62 then // return, if incorrect STATUS
Return (false);
endif;
// write subaddress
TWIEMASTERDATA := HI(subaddress); // write highbyte subaddress
repeat
until Bit(TWIEMASTERSTATUS, 6) or isSysTimerZero(twitimeout); // wif?
if TWIEMASTERSTATUS <> $62 then // return, if incorrect STATUS
Return (false);
endif;
TWIEMASTERDATA := LO(subaddress); // write lowbyte subaddress
repeat
until Bit(TWIEMASTERSTATUS, 6) or isSysTimerZero(twitimeout); // wif
if TWIEMASTERSTATUS <> $62 then // return, if incorrect STATUS
Return (false);
endif;
// bytesToRead?
if bytesToRead = 0 then
TWIEMASTERCTRLC := $07; // stop
Return(true);
endif;
// read first byte (read access with repeated start condition)
TWIEMASTERADDR := (DSPADDR shl 1) or $01; // write I2C address + R/W = 1
repeat
until Bit(TWIEMASTERSTATUS, 7) or isSysTimerZero(twitimeout); // rif?
if TWIEMASTERSTATUS <> $A2 then // return, if incorrect STATUS
Return (false);
endif;
pData^++ := TWIEMASTERDATA;
dec(bytesToRead);
// read next bytes
while bytesToRead > 0 do
TWIEMASTERCTRLC := $02; // ack + read more byte
repeat
until Bit(TWIEMASTERSTATUS, 7) or isSysTimerZero(twitimeout); // rif?
if TWIEMASTERSTATUS <> $A2 then // return, if incorrect STATUS
Return (false);
endif;
pData^++ := TWIEMASTERDATA;
dec(bytesToRead);
endwhile;
// send stop condition
TWIEMASTERCTRLC := $07;
Return(true);
end;
{------------------------------------------------------------------------------}
function dspReadCapture(address : word; var data : LongWord) : boolean;
begin
if not TwiOutE(DSPADDR, DATACAPTURE0, swap(address)) then
Return (false);
endif;
if not twiCombinedRead(DATACAPTURE0, @data, 3) then
Return (false);
endif;
data := swapLong(data);
data := data shr 8;
Return (true);
end DspReadCapture;
{------------------------------------------------------------------------------}
{ Main Program }
begin
loop
dspReadCapture($0123, data);
endloop;
end.