Forum: Mikrocontroller und Digitale Elektronik I2C und STM8


von Ralph S. (jjflash)


Lesenswert?

... ich werde noch mit dem Bobbes verrückt. Ich bin am "zusammenbasteln" 
einer (wie ich glaube benutzerfreundlichen) Lib für den ultrabilligen 
STM8S103F3P6 (Extrembilligboard aus China für einen Euro).

Leider muss ich hier (weil es eine offene Toolchain sein soll) den SDCC 
3.5.0 verwenden wobei ich allerdings glaube, dass das nicht mein Problem 
ist.

Ich kämpfe mich nun schon für die verschiedenen Peripherieeinheiten 
durchs Datenblatt und hänge nun (nachdem ich Clock, GPIO, USART, SPI, 
ADC ansprechen kann) am I2C Bus.

Will einfach nicht. Ich bekomme das Teil irgendwie nicht richtig 
konfiguriert:

Werte aus meiner Headerdatei für den STM8S103F3:
1
// ------------------- I2C -------------------
2
#define I2C_CR1 *(unsigned char*)0x5210
3
#define I2C_CR2 *(unsigned char*)0x5211
4
#define I2C_FREQR *(unsigned char*)0x5212
5
#define I2C_OARL *(unsigned char*)0x5213
6
#define I2C_OARH *(unsigned char*)0x5214
7
8
#define I2C_DR *(unsigned char*)0x5216
9
#define I2C_SR1 *(unsigned char*)0x5217
10
#define I2C_SR2 *(unsigned char*)0x5218
11
#define I2C_SR3 *(unsigned char*)0x5219
12
#define I2C_ITR *(unsigned char*)0x521A
13
#define I2C_CCRL *(unsigned char*)0x521B
14
#define I2C_CCRH *(unsigned char*)0x521C
15
#define I2C_TRISER *(unsigned char*)0x521D
16
#define I2C_PECR *(unsigned char*)0x521E
17
18
// I2C - Bitadressen
19
20
#define I2C_CR1_PE          ( 1 << 0 )
21
#define I2C_CR1_NOSTRETCH   ( 1 << 7 )
22
#define I2C_CR2_START       ( 1 << 0 )
23
#define I2C_CR2_STOP        ( 1 << 1 )
24
#define I2C_CR2_ACK         ( 1 << 2 )
25
#define I2C_CR2_POS         ( 1 << 3 )
26
#define I2C_CR2_SWRST       ( 1 << 7 )
27
#define I2C_ITR_ITEVTEN     ( 1 << 1 )
28
#define I2C_OARH_ADDCONF    ( 1 << 6 )

und die C-Sequenz, die erst einmal nur zum Testen sein soll um zu sehen, 
ob die STM8 Peripherie das korrekt wegsendet (0x90 ist Adresse eines 
LM75)
1
  I2C_CR1 = 0;                 // Power disable, clock stretching enable
2
  I2C_FREQR = 16;              // 16 MHZ F_CPU
3
  I2C_CCRL = 16;
4
  I2C_CCRH = 00;
5
  I2C_TRISER = 17;
6
  I2C_OARH |= I2C_OARH_ADDCONF;
7
  regread= I2C_SR1;
8
9
  I2C_CR1 |= I2C_CR1_PE;       // I2C Power On
10
  I2C_CR2 |= I2C_CR2_ACK;      // ACK nach Transmit abfragen
11
  I2C_CR2 |= I2C_CR2_START;    // I2C Bus - Startkondition
12
13
  I2C_DR = 0x90;               // zu schickende I2C-Bus Adresse (LM75)
14
15
  I2C_CR2 |= I2C_CR2_STOP;     // I2C Bus - Stop

... auf dem Speicherscope kann ich nur die Startkondition sehen, danach 
2,5 µS auf SDA und SCL 0-Pegel gefolgt von der Stopkondition.

Was muss ich anderst machen, damit die Takte und die SDA Leitung etwas 
ausspucken (lach , ich überleg immer wieder ob der Chip hinüber ist... 
ob die Portpins defekt sind, was ja nicht sein kann, sonst würden die 
Start-Stop Konditionen nicht korrekt abgefahren werden.

Was muss ich tun, damit der Chip zum Beispiel den Wert 0x90 sendet ?

Gruß,
Ralph

PS: Bitbanging ist für mich keine Option, denn das hab ich schon am 
Funktionieren auf dem STM8 gehabt (icht möchts schon in Hardware 
haben)... und ich freue mich auf das nächste Problem das ich haben 
werde, wenn ich den CAN-Bus implementieren möchte.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Ich habe hier gerade ein Beispiel, da steht z.B. noch:
1
  CLK->PCKENR1 |= 8;            // I2C clock enable

HTH Torsten

von Ralph S. (jjflash)


Lesenswert?

... damit schaltet man den Takt auf Peripherieeinheiten des STM8 (und 
das ist schon beim Initialisieren des Systems geschehen (es werden alle 
Peripherieeinheiten mit Takt versorgt).

Abgesehen davon denke ich, dass dann auch nicht die Start-Stop Dinge auf 
dem I2C Bus funktionieren würden...

Die Initialisierung der PCKENR ist bei mir im sys_init wie folgt:

CLK_PCKENR1 = 0xff;
CLK_PCKENR2 = 0xff;

von Max M. (maxmicr)


Lesenswert?

Juhuu, ich bin mit meinem Problem nicht alleine. Ich versuche zur Zeit 
auch, das Chinaboard mit dem STM8 zu verstehen.

Ich hab das CCRL bzw. CCRH Register irgendwie anders verstanden, hier 
mein Versuch einer init-Funktion:
1
  uint8_t freq = 1; //1MHz
2
  uint16_t period_time = (1/freq) * 1000; //period_time in ns
3
  uint16_t result = 5000 / period_time;
4
5
  I2C->FREQR = freq;
6
  I2C->TRISER = (freq+1);
7
  I2C->CCRL = (uint8_t)result;
8
  I2C->CCRH = (uint8_t)result & 0x0F; //first 4 bits of CCR-value
9
10
  I2C->OARL = 0xA0; //own Address
11
  I2C->OARH = 0x40; //6th bit must be set by software
12
  
13
  I2C->CR1 = 0x01; //enable peripheral

Funktioniert allerdings auch nicht. Ich hatte gehofft, wenigstens einen 
Clock auf der Leitung zu haben, aber irgendwie passiert gar nichts.

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

Max M. schrieb:
> wenigstens einen
> Clock auf der Leitung zu haben, aber irgendwie passiert gar nichts.

Schmunzeln muss: Wenn es den Clock geben würde, würde es fast schon 
sicherlich auch die Daten geben. Also ist das "wenigstens" schon etwas 
stark untertrieben, das wäre schon verdammt viel !

von Ralph S. (jjflash)


Lesenswert?

... so, die erste Erkenntnis: der I2C Bus ist nach der Startkondition 
nicht sofort frei und kann das zu sendende Kommando nicht sofort 
uebernehmen ...

Also muss noch eine Abfrage her, wann der Bus "frei" ist...

von Ralph S. (jjflash)


Lesenswert?

... fuer Max MMM jetzt:
1
  I2C_CR1 = 0;                 // Power disable, clock stretching enable
2
  I2C_FREQR = 16;              // 16 MHZ F_CPU
3
  I2C_CCRL = 16;
4
  I2C_CCRH = 0x0b;             // I2C Busfrequenz etwas langsamer und Standardmode
5
  I2C_TRISER = 2;
6
  I2C_CR1 |= I2C_CR1_PE;       // I2C Power On
7
  I2C_CR2 |= I2C_CR2_ACK;      // ACK nach Transmit abfragen
8
9
  I2C_CR2 |= I2C_CR2_START;         // I2C Bus - Startkondition
10
  while (!(I2C_SR1 & I2C_SR1_SB));  // warten bis Bus Startkondition erkannt hat
11
12
  I2C_DR = 0x90;                    // zu schickende I2C-Bus Adresse (LM75)
13
14
  I2C_CR2 |= I2C_CR2_STOP;          // I2C Bus - Stop

"Entscheident" hier war die Abfrage, ob der I2C-Bus die Startaktion 
schon erkannt hat !!  while (!(I2C_SR1 & I2C_SR1_SB));

von Max M. (maxmicr)


Lesenswert?

Ralph S. schrieb:
> "Entscheident" hier war die Abfrage, ob der I2C-Bus die Startaktion
> schon erkannt hat !!  while (!(I2C_SR1 & I2C_SR1_SB));

Danke für den Hinweis, bei mir bleibt das Programm da leider hängen und 
mag nicht mehr weiter :(

von Ralph S. (jjflash)


Lesenswert?

Hat bei mir so funktioniert (und tut es noch).... aaaalerdings: 
überprüfe einmal, ob dein Headerfile in dem die ganzen Adressen 
deklariert sind so auch stimmen.

Mein Ausgangsheader gehört fälschlicherweise (auch wenn der Dateiname 
etwas anderes gesagt hat) zu einem STM8L und nicht zu einem STM8S ... 
und da waren einige Adressen schlicht anderst als sie zu einem STM8S 
passen. Habe ich also die relevanten Daten frisch aus dem Datenblatt 
abgetippt (wobei ich mir fast sicher bin, dass ich bestimmt auch 
irgendwo Tippfehler habe, weil ich immer irgendwo irgendwelche 
Tippfehler habe).

Leider kann ich dir nicht weiterhelfen als zu sagen dass das so bei mir 
funktioniert.

So wie das aussieht (und wie du die einzelnen Bits ansprichst) ist das 
wohl nicht der SDCC ... (den ich nicht so wirklich mag, der aber 
scheinbar der einzig frei verfügbare Compiler ist).

Wenn ich mal alles fertig habe werde ich es hier einstellen. Für den 
ersten Moment ist dir vielleicht geholfen, wenn anstelle der Abfrage ob 
die Startcondition schon angenommen wurde eine Delay-Schleife einfügst 
(die abhängig vom Takt des I2C Busses ist) solltest du das Versenden auf 
einem Oszi sehen können (das war bei mir der erste Versuch).

Nächste Woche... wenn ich mein Bastelequipment wieder zur Verfügung hab, 
kann ich weiteres einstellen.... Smile, vielleicht magst ja dann mal das 
ganze Paket (dann leider mittels SDCC und eigenem Makefile 
ausprobieren).

Gruß zum Wochenfende,

Ralph

von Philipp Klaus K. (pkk)


Lesenswert?

Ralph S. schrieb:
> So wie das aussieht (und wie du die einzelnen Bits ansprichst) ist das
> wohl nicht der SDCC ... (den ich nicht so wirklich mag, der aber
> scheinbar der einzig frei verfügbare Compiler ist).


Gerade mit dem aktuellen SDCC ausprobiert: Beides geht, und es wird 
sogar der gleiche Code erzeugt:
1
#define I2C_DR (*(volatile unsigned char*)0x5216)
2
#define I2C_SR1 (*(volatile unsigned char*)0x5217)
3
// etc
4
5
struct I2C_impl
6
{
7
  unsigned char DR;
8
  unsigned char SR1;
9
  // etc
10
};
11
12
#define I2C ((volatile struct I2C_impl *)0x5216)
13
14
void f1(void)
15
{
16
  I2C_DR = 17;
17
}
18
19
void f2(void)
20
{
21
  I2C->DR = 17;
22
}

beide Zuweisungen werden zu
1
mov  0x5216+0, #0x11

kompiliert.

Wenn man bitfields verwendet, kann man das auch auf Ebene der einzelnen 
Bits statt der Bytes machen.

Philipp

: Bearbeitet durch User
von Philipp Klaus K. (pkk)


Lesenswert?

Ralph S. schrieb:
> SDCC ... (den ich nicht so wirklich mag, der aber
> scheinbar der einzig frei verfügbare Compiler ist).

Welche Version von SDCC verwendest Du, und was daran magst Du nicht?

Philipp

von Jim M. (turboj)


Lesenswert?

Ralph S. schrieb:
> I2C_CR2 |= I2C_CR2_START;    // I2C Bus - Startkondition
>
>   I2C_DR = 0x90;               // zu schickende I2C-Bus Adresse (LM75)
>
>   I2C_CR2 |= I2C_CR2_STOP;     // I2C Bus - Stop
>
> ... auf dem Speicherscope kann ich nur die Startkondition sehen, danach
> 2,5 µS auf SDA und SCL 0-Pegel gefolgt von der Stopkondition.

Was hast Du denn sonst erwartet? Da fehlen sämtliche Wartezyklen 
zwischendurch. Die sollte Dir ein korrekt funktionierendes Peripherial 
mittels Statusregister anzeigen.

von Ralph S. (jjflash)


Lesenswert?

Jim M. schrieb:
> Was hast Du denn sonst erwartet? Da fehlen sämtliche Wartezyklen
> zwischendurch. Die sollte Dir ein korrekt funktionierendes Peripherial
> mittels Statusregister anzeigen.

... das Problem hat sich ja (wie oben schon gemeldet) bereits erledigt 
indem ich die Statusregister abfrage...

Philipp Klaus K. schrieb:
> Welche Version von SDCC verwendest Du, und was daran magst Du nicht?
>
> Philipp

Das ist eine rein subjektive Einstellung. Ich hatte den SDCC früher in 
Verwendung zur Programmierung von MCS-51 Controllern. Jetzt habe ich 
sehr lange mit GCC und deren Ports nach AVR und ARM-NONE-EABI-GCC 
gewerkelt und natürlich sind da dann manche Dinge etwas anderst.

Prinzipiell habe ich das Gefühl, dass der SDCC beim Linken extrem 
langsamer als der GCC ist.

Natürlich will ich die Verwendbarkeit des SDCC's nicht schmälern, es 
steckt immens viel Arbeit drin. Ich hätte nur nicht gedacht gehabt, dass 
ich den SDCC noch einmal anfasse.

Die Sache mit den Bitfields funktioniert natürlich, allerdings ist mir 
die Methode Read-Manipulate-Write back sehr in Fleisch und Blut 
übergegangen.

By the way: woran liegt es, dass es von GCC keinen Port für STM8 und 
MCS-51 gibt?
Liegt es daran, dass es für den MCS-51 schon den SDCC als Quasistandard 
gab und sich das nicht "rentiert" hat, dass vielleicht Atmel die 
Gemeinde für den GCC gesponsert hat und dasselbe eventuell für die ARM 
Controller gilt ? Technisch gesehen wäre ein "MCS51-GCC" doch machbar 
gewesen, genauso wie ein "STM8-GCC" oder ein "PIC18-GCC".

Lag es daran, dass das Interesse der freien Gemeinde einfach nicht groß 
genug war / ist ?

(nein, ich würde mich nicht in er Lage fühlen, den GCC selbst zu 
portieren und selbst wenn, würde ich wohl nicht die Zeit dazu finden)

von Ralph S. (jjflash)


Lesenswert?

Oha .... Asche über mein Haupt: einen GCC für Pic - Controller gibt es 
(smile, dann sollte ich mir auch mal einen Pic Controller beschaffen)

von Philipp Klaus K. (pkk)


Lesenswert?

Ralph S. schrieb:
> By the way: woran liegt es, dass es von GCC keinen Port für STM8 und
> MCS-51 gibt?
> Liegt es daran, dass es für den MCS-51 schon den SDCC als Quasistandard
> gab und sich das nicht "rentiert" hat, dass vielleicht Atmel die
> Gemeinde für den GCC gesponsert hat und dasselbe eventuell für die ARM
> Controller gilt ? Technisch gesehen wäre ein "MCS51-GCC" doch machbar
> gewesen, genauso wie ein "STM8-GCC" oder ein "PIC18-GCC".

GCC und SDCC unterscheiden sich unter anderem in der Registerallokation 
und im Verhältnis von Instruction Scheduling und Registerallokation 
sehr. Dadurch ist der GCC besser für RISC-Architekturen mit relativ 
vielen eher gleichartigen Registern geeignet (z.B. ARM, AVR). SDCC kann 
dagegen gut mit Architekturen mit wenigen, sehr unterschiedlichen 
Registern umgehen (z.B. STM8, S08, Z80).

Philipp

: Bearbeitet durch User
von honk (Gast)


Lesenswert?

Kurze Info: Der Cosmic-Compiler ist seit kurzem ebenfalls kostenlos und 
ohne Limitierung verfügbar.

von Philipp Klaus K. (pkk)


Lesenswert?

Ralph S. schrieb:
> Prinzipiell habe ich das Gefühl, dass der SDCC beim Linken extrem
> langsamer als der GCC ist.

Hmm, ich könnte mir die Geschwindigkeit beim Linken 'mal ansehen. Aber 
eigentlich verbringt SDCC die meiste Zeit in Registerallokator und 
Peephole Optimizer.

Philipp

von Ralph S. (jjflash)


Lesenswert?

Philipp Klaus K. schrieb:
> Hmm, ich könnte mir die Geschwindigkeit beim Linken 'mal ansehen. Aber
> eigentlich verbringt SDCC die meiste Zeit in Registerallokator und
> Peephole Optimizer.

Hmmm, wenn ich das so von dir lese: Bist du bei der Entwicklung vom SDCC 
mit involviert? (und ich "gestehe" jetzt, dass ich deinen Namen 
gegoogelt hab).

von Philipp Klaus K. (pkk)


Lesenswert?

Ralph S. schrieb:
> Philipp Klaus K. schrieb:
>> Hmm, ich könnte mir die Geschwindigkeit beim Linken 'mal ansehen. Aber
>> eigentlich verbringt SDCC die meiste Zeit in Registerallokator und
>> Peephole Optimizer.
>
> Hmmm, wenn ich das so von dir lese: Bist du bei der Entwicklung vom SDCC
> mit involviert? (und ich "gestehe" jetzt, dass ich deinen Namen
> gegoogelt hab).

Ja. Und ich habe gerade fünfmal Coremark auf meinen Notebook für STM8 
kompiliert, mit make -j 5. Das benötige jeweils zwischen 9.562 s und 
10.507 s insgesamt, einschließlich Linker.

Dann habe Linkeraufruf, der die Dateien des Coremark zusammen linkt, das 
nötige aus der Standardbibliothek heraussucht, und eine Intel-Hex-Datei 
daraus macht, zehnmal einzeln ausgeführt. Das benötigte jeweils zwischen 
0.013 und 0.014 s.

Alle Zeitangaben wurden per time gemessen.

Philipp

P.S.: Ich hatte bisher  zwar nie den Eindruck, dass der Linker langsam 
sei. Aber es könnte trotzdem irgendwelche Fälle geben, in denen er es 
ist.

von Ralph S. (jjflash)


Lesenswert?

... dann auf diesem Wege vielen Dank dass es den SDCC gibt. Es freut 
mich immer wenn etwas frei verfügbar ist und solche Dinge der 
Allgemeinheit zur Verfügung gestellt wird.

Auch wenn ich "GCC Verseucht bin": Ohne den SDCC könnte ich mit dem STM8 
so nicht "spielen"

Vielen Dank

von Philipp Klaus K. (pkk)


Lesenswert?

Ralph S. schrieb:
> Oha .... Asche über mein Haupt: einen GCC für Pic - Controller
> gibt es
> (smile, dann sollte ich mir auch mal einen Pic Controller beschaffen)

Wo?

Es gibt die 32-Bit PICs, die haben einen MIPS-Kern, was für GCC passt. 
Aber für die älteren (14- und 16-Bit) wüßte ich nichts. Da kenne es nur 
SDCC ("work in progress", halbwegs brauchbar für 16-Bit, aber bei weitem 
nicht so gut wie die anderen SDCC-Backends).
Es gibt da auf Sourceforge ein Projekt "GCC toolchain for Microchip 
PIC", das ist aber seit 2006 noch in der Phase "planning", und seit 2013 
tat sich da wohl gar nichts mehr.

Philipp

von Philipp Klaus K. (pkk)


Lesenswert?

Ralph S. schrieb:
> Auch wenn ich "GCC Verseucht bin": Ohne den SDCC könnte ich mit dem STM8
> so nicht "spielen"

Nun, der GCC ist SDCC natürlich in mancher Hinsicht weit voraus. Aber im 
Vergleich zu den anderen Compilern für STM8 steht SDCC nicht so schlecht 
da: http://colecovision.eu/stm8/compilers.shtml

Philipp

P.S.: Falls Du den SDCC 3.5.0 verwendest: Demnächst gibt es SDCC 3.6.0, 
da hat sich insbesondere für STM8 und bezüglich der Unterstützung der 
C-Standards nochmal einiges getan.

von Max M. (maxmicr)


Lesenswert?

Ich schaffs einfach nicht, was mach ich falsch?
1
void initI2C(){
2
  float freq = 16; //16MHz
3
  float period_time = (1/freq) * 1000; //period_time in ns
4
  uint16_t result = 5000 / period_time;
5
  
6
  GPIOB->DDR = (1<<SCL) | (1<<SDA); //set SCL and SDA as Output
7
  GPIOB->CR1 = (1<<SCL) | (1<<SDA); //set SCL and SDA as PuhsPull
8
  GPIOB->ODR = (1<<SCL) | (1<<SDA); //set SCL and SDA HIGH
9
  
10
  I2C->FREQR = freq;
11
  I2C->TRISER = (freq+1);
12
  I2C->CCRL = result;
13
  I2C->CCRH = (result & 0x0F); //first 4 bits of CCR-value
14
  
15
  I2C->OARL = 0xA0; //own Address
16
  I2C->OARH = (1<<6); //6th bit must be set by software
17
  
18
  I2C->CR1 = 0x01; //enable peripheral
19
  I2C->CR2 = (1<<2) | (1<<0); //ack enabled & start generation
20
  while(!(I2C->SR1 & (1<<0))); //check if device received start generation
21
}

Mein Program bleibt im while hängen und es passiert nichts auf SDA und 
SCK. Ich finde I2C teilweise etwas komisch, fängt schon damit an, dass 
man gezwungen wird, ein Register zu lesen nur damit etwas per Hardware 
gesetzt werden kann? Gibts doch bei SPI und UART auch nicht.

Muss man noch irgendwas anderes setzen? Wenn es zu dem STM8 wenigstens 
mehr Doku geben würde...Ich hab versucht, die ST I2C Bibliothek zu 
kopieren, hat aber auch nicht funktioniert, wobei die meiner Ansicht 
nach das selbe machen.

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

jetzt hab ich meine Sachen nicht zur Verfügung (Wochenende) ..... 
aaaaber GPIO hab ich definitiv nicht gesetzt gehabt... und weil die 
Register ResetValue 0 sind, sind die auf Input....

Hmmmm vielleicht sollte ich vorab mein sehr chaotisches Zeug mal hier 
einstellen.... aaaaaber, ich hab bald Urlaub

von STM8_I2C (Gast)


Lesenswert?

Hallo Max,

ich habe folgende Quelle benutzt (nur den Basis-Treiber für I2C: 
i2c_drv.c/.h) und auf meinen STM8S105S4 adaptiert:

http://hamlab.net/mcu/stm8/ds1307.html (russisch, aber durchaus 
selbsterklärend)

Mein Ergebnis zeige ich lieber nicht, ich bin ein mehr als lausiger 
Programmierer. Aber es läuft mit einem DS3231M, was allerdings nur dem 
unbekannten russischen Autor und seiner übersichtlichen software zu 
verdanken ist.

von STM8_I2C (Gast)


Lesenswert?

STM8_I2C schrieb:
> ... (nur den Basis-Treiber für I2C:
> i2c_drv.c/.h)

Also nur das hier:

http://hamlab.net/uploads/projects/stm8/stm8s003f_i2c_iar.rar

(sorry, kann den vorherigen Beitrag nicht editieren)

von Ralph S. (jjflash)


Lesenswert?

Ohne jetzt den STM8105L zu kennen oder einen anderen. Quellen im Netz 
gibts viele... und viele fehlerhafte. Meine Sachen dürften auch genügend 
Fehler haben. Grundsätzlich Fehler 1 war: Adressdefinitionen für einen L 
Typ passen nicht in allem für einen S Typ. Wenn also irgendwo eine 
Quelle eine Datei #include stm8l.h einbindet, dann sind Fehler für stm8s 
vorprogramiert. Diese Datei dann dem Datenblatt entsprechend 
modifizieren.

Bsp. ist da schon das initialisieren des Systems fehlerhaft und der 
Controller läuft mit 4 anstelle mit 16 MHz.

Wie gesagt, ich versuch dass ich das am Montag noch schaff hier 
einzustellen. Programme sind für SDCC 3.5

von Max M. (maxmicr)


Lesenswert?

STM8_I2C schrieb:
> Also nur das hier:

Danke, das hat mir schon einmal sehr geholfen. Was ich vom Code her 
nicht ganz verstehe, ist die Funktion zum Lesen des Registers. In dem 
Datenblatt des ICs, den ich auslesen will, steht:
1
the 7-bit I2C address for the device is 0x1D, followed by the R/W bit

Also müsste ich doch das hier zuerst ins DR-Register schieben?
1
I2C->DR = (slaveAddress | 0x01)

In dem russischen Code wird aber seltsamerweise 2 mal die Adresse des 
Slave gesendet. Müsste das nicht so aussehen:
1
I2C->DR = (slaveAddress | 0x01); //hier wird dem Slave mitgeteilt, das wir etwas von ihm lesen wollen
2
//das ganze while usw. hier
3
I2C->DR = registerAddress; //hier wird dem Slave das Register mitgeteilt, das wir lesen wollen
4
//das ganze while etc. wieder hier
5
return ((uint8_t)I2C->DR);

Oder verstehe ich da was falsch?

von Christopher B. (chrimbo) Benutzerseite


Lesenswert?

Du musst erst einen Schreibzugriff initieren und ihm die Adresse des 
Registers mitteilen. Nun musst du eine Repeated Start Condition setzen 
und ihm diesmal seine Adresse mit Lesezugriff senden. Erst dann kannst 
du die vorher geschrieben Adresse lesen.

von Max M. (maxmicr)


Lesenswert?

Ah okay.
Immerhin bekomme ich jetzt etwas auf die Leitung. Ich hab versucht, den 
russischen Code 1 zu 1 zu kopieren. Leider hängt mein Programm wieder in 
einer while-Schleife fest:
1
void initI2C(){
2
  float freq = 16; //16MHz
3
  long ccr = freq / (2*100000); //100kHz I2C clock
4
  
5
  I2C->FREQR = freq;
6
  I2C->TRISER = (freq+1);
7
  I2C->CCRL = ccr;
8
  I2C->CCRH = (ccr >> 8) & 0x0F;
9
  
10
  I2C->OARL = 0xA0; //own Address
11
  I2C->OARH = (1<<6); //6th bit must be set by software
12
  
13
  I2C->CR1 = 0x01; //enable peripheral
14
  I2C->CR2 |= (1<<2); //enable ack
15
}

Und zwar hier beim 3. while:
1
void I2C7WriteToRegister(uint8_t slaveAddress, uint8_t regAddr, uint8_t data){
2
  while(I2C->SR3 & (1<<1)); //wait for not busy
3
  I2C->CR2 |= (1<<0); //generate Start
4
  while(!(I2C->SR1 & (1<<0))); //check if device received start generation
5
  I2C->DR = slaveAddress & 0xFE;
6
  while(!(I2C->SR1 & (1<<1))); //check addr
7
  I2C->SR3;
8
  while(!(I2C->SR1 & (1<<7))); //wait for TX to be empty
9
  I2C->DR = regAddr; //send register we want to write
10
  while(!(I2C->SR1 & (1<<7))); //wait for TX to be empty
11
  I2C->DR = data; //send data
12
  while(!(I2C->SR1 & ((1<<7) | (1<<2)))); //wait for TX to be empty and BTF to finish byte transfer
13
  I2C->CR2 |= (1<<1); //generate stop
14
  while(I2C->CR2 & (1<<1)); //wait for stop
15
}

Habt ihr eine Idee, wo mein Fehler liegt?

von Ralph S. (jjflash)


Lesenswert?

... wie gesagt, ich habe meine Sachen nicht hier (und den Registersatz 
hab ich jetzt nicht auswendig im Kopf), aaaaaaaber:

Bevor es bei mir lief hab ich einen (sehr dummen) Fehler gemacht gehabt:

Wenn du einen Lesezugriff auf ein I2C Device mittels des STM8 machst, 
schickt der Controller zuerst die Adresse (mit gesetztem) Lesebit und 
(jetzt kommts): Er geht auch her und taktet schön weiter und liest auch 
gleich etwas. Verrückterweise (und ich weiß noch nicht warum) liest er 2 
Byte !!!!! .

Diese(s) Byte(s) wird erst dann mittels variable= I2C->DR aus dem Buffer 
geholt.

Auf dem Oszilloskop kann man sehen, dass er hierbei aber GRUNDSÄTZLICH 9 
!!!!! Taktimpulse sendet (für eine ACK-Abfrage).

Wenn du beim Lesen des DR Registers eine grundsätzliche ACK by Master 
sendest, der Slave aber kein ACK erwartet, dann ist dieser eine Impuls 
für den Slave schon ein neuer Taktimpuls.

Viele I2C Devices erwarten beim letzten gesendeten kein ACK vom Master.

Du solltest also einen Lesezugriff auf den Slave beim letzten gelesenen 
Byte im Stile von I2C_read_nack durchführen.

von Ralph S. (jjflash)


Lesenswert?

... so, Datenblatt angesehen:

(wenn ich das richtig im Kopf habe):

I2C_CR2_ACK

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Danke für deine Antwort, ich hoffe, es ist okay, wenn ich deinen Thread 
für meine Fragen benutze?

Ralph S. schrieb:
> Wenn du einen Lesezugriff auf ein I2C Device mittels des STM8 machst

In meinem Problem gehts erstmal nur darum, dass ich versuche, ein 
Register des I2C Devices zu beschreiben um es zu initialisieren.

Ralph S. schrieb:
> Viele I2C Devices erwarten beim letzten gesendeten kein ACK vom Master.

Ich weiß nicht, ob ich das richtig interpretiere, aber meines 
anscheinend schon (siehe Anhang)?

von Ralph S. (jjflash)


Lesenswert?

Nein, dem Diagramm nach erwartet dein Slave Device wenn von ihm gelesen 
wurde KEIN Ack vom Master.

Und schmunzeln muß:  ich wußte nicht, dass jemandem ein Thread gehören 
kann.

von Ralph S. (jjflash)


Angehängte Dateien:

Lesenswert?

Jetzt aber:

Das hier ist meine Implementierung von I2C mit dem STM8S103F3P6 (Minimal 
Evaluation Board aus China).

Ich hoffe dass der Quelltext selbsterklärend ist, ich habe so 
ausführlich Kommentare eingefügt wie ich konnte.

Das Zip-File enthält alle zum Übersetzen benötigten Software-Module 
inkl. einem Makefile

----------------------------------------------------------------------
Makefile

  der im Makefile verwendete Compiler ist SDCC 3.5.0. Es wird eine
  Linux-Distribution vorrausgesetzt. Das Makefile ist mit Kommentaren
  versehen und es sollte leicht möglich sein es für eigene Zwecke
  zu modifizieren, vllt. in Verwendung mit einer IDE ?!?)

  Optionen:

  make
    eine Intel-Hexdatei (mit der Endung .ihx) wird erstellt

  make clean
    "räumt auf", insbesondere .rel Dateien (das sind Objektdateien für
    den SDCC) werden gelöscht

  make flash
    bei angeschlossenem (Chinaclone) STLINK/V2 wird die erzeugte .ihx
    Datei in den STM8S übertragen

-----------------------------------------------------------------
Quelldateien:

- stm8s.h
    Deklaritonen der Register- und Bitadressen für den STM8S103F3P6

- stm8_init.h / stm8_init.c
    initialisiert den Systemtakt (16 MHz) und schaltet Takt für die
    Peripherieeinheiten ein

- stm8_gpio.h
    stellt Makros zu Verwendung mit GPIO Pins zur Verfügung
    (setzen als Ein- oder Ausgang, setzen auf 0 oder 1 ( Ausgang ) oder
    lesen wenn als Eingang verwendet)

- uart.h / uart.c
    Verwendung int. Peripherieeinheit zur Verwendung als serielle
    Schnittstelle

- my_printf.h / my_printf.c
    eigene Printf-Funktion die auf eine eigene Ausgabefunktion 
umgeleitet +
    werden muss (sehr spartanisch und "abgespeckt" damit der erzeugte
    Code im erträglichen Maße bleibt)

----------------------------------------------------------

i2c_explore.c

Das "Hauptanwendungsprogramm". Demonstriert die Verwendung des 
I2C-Busses.
Benötigt eine serielle Schnittstelle zur Bedienung.

Ist eine Verbindung mit einem seriellen Monitorprogramm hergestellt 
(Protokoll 19200 Baud, 8N1) so kann der I2C Bus "untersucht" werden. Der 
Bus ist durch eingebbare Kommandos steuerbar:

start
  es wird die Startkondition des I2C Busses erzeugt

write
  der Bus wird beschrieben. Nach dem "write" Komamndo erfolgt eine
  Eingabeaufforderung zur Eingabe des Bytes, welches auf dem Bus
  geschrieben werden soll.

read
  der Bus wird gelesen und der STM8 sendet nach dem Lesen ein ACK

rnack
  der Bus wird gelesen, aber der STM8 sendet nach dem Lesen KEIN ACK.
  Dieses ist oft dann der Fall, wenn das letzte Byte aus einem Daten-
  frame gelesen wird

scan
  der Bus wird nach angeschlosssenen I2C Devices abgesucht. Die Adressen
  der angeschlossenen Devices werden angezeigt

Extrakommandos:
---------------

lm75
  ist ein LM75 (mit Adresse 0x90) im Bus vorhanden, wird die
  Temperatur angezeigt

time
  ist ein DS1307 RTC-Chip im Bus vorhanden, wird das Datum und die
  Uhrzeit ausgegeben

settime
  ist ein DS1307 RTC-Chip im Bus vorhanden, so kann Datum und Uhrzeit
  eingestellt werden

----------------------------------------------------------------------

stm8_i2c.gif
  Schaltplan für den Anschluß von I2C - Devices inklusive Level-Shifter
  zur Anpassung von 3,3V und 5V Bus - Devices

------------------------------------------------------------------------

Ich hoffe ich konnte Max (und enventuell anderen) hiermit helfen.

Gruß,

Ralph

von Max M. (maxmicr)


Lesenswert?

Vielen Dank, dass du hier deinen Code bereitstellst! Was ich nicht ganz 
verstehe, ist das switch-case in der I2C Initialisierung insbesondere da 
der gleiche Kommentar hinter jeder Zeile steht aber doch andere Werte 
geschrieben werden?

von Ralph S. (jjflash)


Lesenswert?

Oh, dann hab ich Dokumentationsfehler, sorry, kann ich aber eine Woche 
lamg nicht beantworten.... weil: Urlaub und nue Handy

von Ralph S. (jjflash)


Lesenswert?

Okay, in Zip Dateu konnte ich reingucken. Das war original kopiert für 
unterschiedliche Bustakte... 0= 100 KHz, 1= 50 KHz, 2= 15 KHz... Die 
Register CCRL und CVRH sind Taktteiler....

von Ralph S. (jjflash)


Lesenswert?

CCRH muß es heißen...

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Es funktioniert immer noch nicht :(
Ich hab mal eine Zeile eingefügt, um zu überprüfen, ob der TX-Buffer 
leer wird, aber selbst in diesem while bleibt das Programm stehen:
1
void I2C7WriteToRegister(uint8_t slaveAddress, uint8_t regAddr, uint8_t data){
2
  while(I2C->SR3 & (1<<1)); //wait for not busy
3
  I2C->CR2 |= (1<<0); //generate Start
4
  while(!(I2C->SR1 & (1<<0))); //check if device received start generation
5
  I2C->DR = slaveAddress;
6
  while(!(I2C->SR1 & (1<<7)));
7
  while(!(I2C->SR1 & (1<<1))); //check addr
8
  I2C->SR3;
9
  while(!(I2C->SR1 & (1<<7))); //wait for TX to be empty
10
  I2C->DR = regAddr; //send register we want to write
11
  while(!(I2C->SR1 & (1<<7))); //wait for TX to be empty
12
  I2C->DR = data; //send data
13
  while(!(I2C->SR1 & ((1<<7) | (1<<2)))); //wait for TX to be empty and BTF to finish byte transfer
14
  I2C->CR2 |= (1<<1); //generate stop
15
  while(I2C->CR2 & (1<<1)); //wait for stop
16
}

Mit meinem Logic Analyzer hab ich aber überprüft, das definitiv die 
Adresse (0x3A) gesendet wird (siehe Anhang), warum da jetzt 9 
Flankenwechsel sind, weiß ich nicht.

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

... das neunte ist der Takt für das Acknowledge !

Wenn du den Bus vom Master beschreibst, antwortet der Slave mit einem 
Acknowledge, dass er sich für die weiteren Aktionen auf dem Bus 
verantwortlich fühlt.

Wenn du vom Bus liest, mußt du über das CR2 Register festlegen, ob der 
STM8 ein Acknowledge lesen soll oder nicht.

Der STM8 generiert auch dann den 9ten Taktimpuls, wenn er dem Slave KEIN 
Acknowledge geben soll (er gibt dann halt beim 9ten Impuls einfach kein 
ACK).

Seh dir doch einfach den Code von oben an (i2c_write).

Deinem Logicanalyzer zufolge antwortet übrigens dein Slave mit der 
Adresse 34h scheinbar nicht, denn sonst würde auf der SDA-Leitung beim 
9ten Impuls ein Low-Pegel liegen ! (Hardware überprüfen, Pop-Up 
Widerstände drin ? )

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.