Forum: Mikrocontroller und Digitale Elektronik ESP8266 - Software I2C - Dauernd Resets


von Max M. (maxmicr)


Lesenswert?

Guten Abend,

ich versuche aktuell auf einem ESP8266 eine Art Software I2C zu 
implementieren um damit später den BMP290 auslesen zu können. Aktuell 
passiert noch nicht viel und trotzdem funktioniert es nicht. Der ESP8266 
resettet sich regelmäßig ohne ersichtlichen Grund, das Einzige, was ich 
mache, ist ein paar Pins zu toggeln, mein Code:
1
/*
2
includes...
3
*/
4
5
#define SCK BIT5
6
#define SDI BIT4
7
#define SDO BIT2 //is always high
8
9
os_timer_t callTimer;
10
11
void initI2CPins(){
12
  gpio_init();
13
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
14
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
15
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
16
17
        //Set Pins high
18
  gpio_output_set(SCK, 0, SCK, 0);
19
  gpio_output_set(SDI, 0, SDI, 0);
20
  gpio_output_set(SDO, 0, SDO, 0);
21
}
22
23
void sendByte(uint8_t dataByte){
24
  uint8_t initBitCounter;
25
  for(initBitCounter = 7; initBitCounter >= 0; initBitCounter--){
26
    uint8_t bitToWrite = (dataByte >> initBitCounter) & 1;
27
    if(bitToWrite == 1){
28
      gpio_output_set(SDI, 0, SDI, 0); //Write 1
29
    } else {
30
      gpio_output_set(0, SDI, SDI, 0); //Write 0
31
    }
32
    gpio_output_set(SCK, 0, SCK, 0);
33
    os_delay_us(1);
34
    gpio_output_set(0, SCK, SCK, 0);
35
  }
36
}
37
38
void I2CWrite(uint8_t slaveAddr, uint8_t regAddr, uint8_t data){
39
  uint8_t slAddr = (slaveAddr << 1); //add RW-Bit
40
  uint8_t initBitCounter;
41
42
  //Start Condition
43
  gpio_output_set(0, SDI, SDI, 0);
44
  os_delay_us(1);
45
  gpio_output_set(0, SCK, SCK, 0);
46
47
  sendByte(slAddr);
48
49
  //Stop Condition
50
  gpio_output_set(SCK, 0, SCK, 0);
51
  os_delay_us(1);
52
  gpio_output_set(SDI, 0, SDI, 0);
53
}
54
55
void user_init(void){
56
  uint8_t slaveAddr = 0b01110111;
57
  uint8_t regAddr = 0xA0;
58
  uint8_t data = 0x72;
59
  initI2CPins();
60
61
  os_timer_setfn(&callTimer,(os_timer_func_t*)I2CWrite,slaveAddr,regAddr,data);
62
  os_timer_arm(&callTimer,1000,1); //recall function every 1 second
63
}

Hat jemand eine Idee, woran das liegen könnte? Ist der ESP8266 zu "high 
level" um noch vernünftig Pins schnell aus und einschalten zu können?

von Oliver S. (phetty)


Lesenswert?

Pack mal einen dicken Kondensator über die Spannungsversorgung, der ESP 
ist ziemlich zickig bei Spannungseinbrüchen.

Übrigens: Es gibt einige fertige Libs für I2C.

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Oliver S. schrieb:
> Pack mal einen dicken Kondensator über die Spannungsversorgung, der ESP
> ist ziemlich zickig bei Spannungseinbrüchen.

Ich hab 100µF in 0805 zwischen VCC und GND (und den obligatorischen 
100nF). Ist das zu wenig? Ich hab mit dem ESP8266 bereits eine 7-Segment 
Anzeige + WLan betrieben, das benötigt doch eindeutig mehr Strom als 2 
Pins aus und ein zu schalten, oder?

Oliver S. schrieb:
> Übrigens: Es gibt einige fertige Libs für I2C.

Ja, ich will das aber selber machen.

von Joachim S. (oyo)


Lesenswert?

Ich habe auf den ersten Blick zwar nicht ganz den Sinn von SDO 
verstanden, aber daran sollte es ja nicht liegen.

Meine persönlich vage Vermutung, wo das Problem liegt:
Du togglest SCK und SDI soweit ich das sehen kann immer zwischen "output 
high" und "output low".

Ich vermute aber, Du musst beide Pins stattdessen zwischen "input" und 
"output low" togglen; der high-level wird bei I2C m.W.n. doch über extra 
Pull-Up-Widerstände erzeugt, und nicht von den einzelnen Bus-Knoten.

Kurzum, ich vermute: überall, wo Du
gpio_output_set(x, 0, x, 0)
stehen hast, müsste stattdessen stehen
gpio_output_set(0, 0, 0, x)

von e-d (Gast)


Lesenswert?

Max M. schrieb:
> Der ESP8266
> resettet sich regelmäßig ohne ersichtlichen Grund

Beim esp8266 läuft Wifi auch im Hintergrund.
Bei längeren Abarbeitungszyklen schlägt da gerne mal reset zu, wenn 
nicht durch Maßnahmen wie einfügen von yield() oder delay(0) der wdog 
zurückgepfiffen wird..

von ESP-Nerd (Gast)


Lesenswert?

Wie lang ist die Zeit, in welcher der Wdog bedient werden muss, ich habe 
ähnliche Probleme, dass ab und zu der ESP neustartet.

@Max MMM
Der ESP hat einen Hardware-I2C und es gibt auch fertige Libs dafür. Mal 
drübergeschaut, sehe ich keinen Fehler in Deinem Code.

von e-d (Gast)


Lesenswert?

Hier schrieb:
http://www.esp8266.com/viewtopic.php?f=13&t=12567#p58284
etwas von 100ms.
Dies in Zusammenhang mit Spannungseinbrüchen, die zum resetten führen..

von Max M. (maxmicr)


Lesenswert?

Joachim S. schrieb:
> Ich habe auf den ersten Blick zwar nicht ganz den Sinn von SDO
> verstanden, aber daran sollte es ja nicht liegen.

Der Sensor BMP280 hat sowohl ein SPI als auch ein I2C Interface, laut 
Datenblatt muss der SDO-Pin bei I2C entweder mit GND oder Vcc verbunden 
werden, je nach dem ergibt sich dann sie Slave-Adresse:

"The I²C interface uses the following pins:
SCK: serial clock (SCL)
SDI: data (SDA)
SDO: Slave address LSB (GND = ‘0’, VDDIO = ‘1’)"

Joachim S. schrieb:
> Ich vermute aber, Du musst beide Pins stattdessen zwischen "input" und
> "output low" togglen; der high-level wird bei I2C m.W.n. doch über extra
> Pull-Up-Widerstände erzeugt, und nicht von den einzelnen Bus-Knoten.

Hm, mit meiner 7-Segment LED hat das an/aus der Pins wie ich es jetzt 
mache wunderbar funktioniert. Aufm Logic Analyzer kommen die Signale 
auch an, nur der Controller resettet sich dauernd :(

e-d schrieb:
> Beim esp8266 läuft Wifi auch im Hintergrund.
> Bei längeren Abarbeitungszyklen schlägt da gerne mal reset zu, wenn
> nicht durch Maßnahmen wie einfügen von yield() oder delay(0) der wdog
> zurückgepfiffen wird..

Welchen Grund hat das? Ich hab nun im Code verhindert, dass sich der 
Controller automatisch mit dem zuletzt verbundenen Wifi verbindet:
1
void user_init(void){
2
  wifi_station_set_auto_connect(0);
3
  uint8_t slaveAddr = 0b01110111;
4
  uint8_t regAddr = 0xA0;
5
  uint8_t data = 0x72;
6
  initI2CPins();
7
8
  os_timer_setfn(&callTimer,(os_timer_func_t*)I2CWrite,slaveAddr,regAddr,data);
9
  os_timer_arm(&callTimer,1000,1); //recall function every 1 second
10
}

Zudem hab ich in meiner I2C-Write Funktion ein "yield()" am Ende 
eingefügt:
1
void I2CWrite(uint8_t slaveAddr, uint8_t regAddr, uint8_t data){
2
  uint8_t slAddr = (slaveAddr << 1); //add RW-Bit
3
  uint8_t initBitCounter;
4
5
  //Start Condition
6
  gpio_output_set(0, SDI, SDI, 0);
7
  os_delay_us(1);
8
  gpio_output_set(0, SCK, SCK, 0);
9
10
  sendByte(slAddr);
11
12
  //Stop Condition
13
  gpio_output_set(SCK, 0, SCK, 0);
14
  os_delay_us(1);
15
  gpio_output_set(SDI, 0, SDI, 0);
16
  yield();
17
}

Bringt alles nichts :(

e-d schrieb:
> Dies in Zusammenhang mit Spannungseinbrüchen, die zum resetten führen..

Reichen denn 100uF nicht?

von Joachim S. (oyo)


Lesenswert?

Max M. schrieb:
> Joachim S. schrieb:
>> Ich habe auf den ersten Blick zwar nicht ganz den Sinn von SDO
>> verstanden, aber daran sollte es ja nicht liegen.
>
> Der Sensor BMP280 hat sowohl ein SPI als auch ein I2C Interface, laut
> Datenblatt muss der SDO-Pin bei I2C entweder mit GND oder Vcc verbunden
> werden, je nach dem ergibt sich dann sie Slave-Adresse:

Ah, verstehe.

> Joachim S. schrieb:
>> Ich vermute aber, Du musst beide Pins stattdessen zwischen "input" und
>> "output low" togglen; der high-level wird bei I2C m.W.n. doch über extra
>> Pull-Up-Widerstände erzeugt, und nicht von den einzelnen Bus-Knoten.
>
> Hm, mit meiner 7-Segment LED hat das an/aus der Pins wie ich es jetzt
> mache wunderbar funktioniert. Aufm Logic Analyzer kommen die Signale
> auch an, nur der Controller resettet sich dauernd :(

Den Zusammenhang mit der 7-Segment-Anzeige habe ich jetzt ehrlich gesagt 
nicht ganz verstanden. Dass auf deinem Logic Analyzer das Signal richtig 
aussieht, ist meiner Meinung nach aber sogar ganz normal.
Hast Du meinen Lösungsvorschlag denn wenigstens mal ausprobiert? Ich bin 
zu ca. 75% sicher, dass das Problem damit zusammenhängt.

von Klaus (Gast)


Lesenswert?

Max M. schrieb:
> Oliver S. schrieb:
>> Übrigens: Es gibt einige fertige Libs für I2C.
>
> Ja, ich will das aber selber machen.

Max M. schrieb:
> Hm, mit meiner 7-Segment LED hat das an/aus der Pins wie ich es jetzt
> mache wunderbar funktioniert.

Ich würd mal mit dem Lesen und verstehen der I2C Spec anfangen. Dabei 
könnte man sich dann auch überlegen was passiert, wenn der selbst 
verkorkste Master ein Signal aktiv auf High zieht, währen ein Slave ihn 
nach Low treibt.

MfG Klaus

von Max M. (maxmicr)


Lesenswert?

Joachim S. schrieb:
> Hast Du meinen Lösungsvorschlag denn wenigstens mal ausprobiert? Ich bin
> zu ca. 75% sicher, dass das Problem damit zusammenhängt.

Ich hab meine Code dahingehend abgeändert:
1
void initI2CPins(){
2
  gpio_init();
3
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
4
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
5
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
6
7
  //gpio_output_set(SCK, 0, SCK, 0);
8
  //gpio_output_set(SDI, 0, SDI, 0);
9
  //gpio_output_set(SDO, 0, SDO, 0);
10
  gpio_output_set(0,0,0,SCK);
11
  gpio_output_set(0,0,0,SDI);
12
  gpio_output_set(0,0,0,SDO);
13
}
14
15
void sendByte(uint8_t dataByte){
16
  uint8_t initBitCounter;
17
  for(initBitCounter = 7; initBitCounter >= 0; initBitCounter--){
18
    uint8_t bitToWrite = (dataByte >> initBitCounter) & 1;
19
    if(bitToWrite == 1){
20
      //gpio_output_set(SDI, 0, SDI, 0); //Write 1
21
      gpio_output_set(0,0,0,SDI);
22
    } else {
23
      gpio_output_set(0, SDI, SDI, 0); //Write 0
24
    }
25
    //gpio_output_set(SCK, 0, SCK, 0);
26
    gpio_output_set(0,0,0,SCK);
27
    os_delay_us(1);
28
    gpio_output_set(0, SCK, SCK, 0);
29
  }
30
}
31
32
void I2CWrite(uint8_t slaveAddr, uint8_t regAddr, uint8_t data){
33
  uint8_t slAddr = (slaveAddr << 1); //add RW-Bit
34
  uint8_t initBitCounter;
35
36
  //Start Condition
37
  gpio_output_set(0, SDI, SDI, 0);
38
  os_delay_us(1);
39
  gpio_output_set(0, SCK, SCK, 0);
40
41
  sendByte(slAddr);
42
43
  //Stop Condition
44
  //gpio_output_set(SCK, 0, SCK, 0);
45
  gpio_output_set(0,0,0,SCK);
46
  os_delay_us(1);
47
  //gpio_output_set(SDI, 0, SDI, 0);
48
  gpio_output_set(0,0,0,SDI);
49
  yield();
50
}

Leider resettet der Controller immer noch.

Klaus schrieb:
> Ich würd mal mit dem Lesen und verstehen der I2C Spec anfangen. Dabei
> könnte man sich dann auch überlegen was passiert, wenn der selbst
> verkorkste Master ein Signal aktiv auf High zieht, währen ein Slave ihn
> nach Low treibt.

Ich versuche aktuell erstmal, die Flanken und die Daten richtig  zu 
erzeugen, wohin dann welcher Pin gezogen wird, darum kümmere ich mich 
später. Aktuell hängt der Sensor noch nicht dran.

von Klaus (Gast)


Lesenswert?

Max M. schrieb:
> Ich versuche aktuell erstmal, die Flanken und die Daten richtig  zu
> erzeugen, wohin dann welcher Pin gezogen wird, darum kümmere ich mich
> später. Aktuell hängt der Sensor noch nicht dran.

Prima Anfang, erst mal kräftig Strom fließen lassen. Und sollte dir der 
Slave deine Daten und Flanken doch mal durcheinander bringen, nimm 
einfach einen stärkeren Treiber.

MfG Klaus

von Max M. (maxmicr)


Lesenswert?

Wie schaltet man beim ESP8266 einen Pin als Open-Drain?

von Klaus (Gast)


Lesenswert?

Max M. schrieb:
> Wie schaltet man beim ESP8266 einen Pin als Open-Drain?

Joachim S. schrieb:
> Meine persönlich vage Vermutung, wo das Problem liegt:
> Du togglest SCK und SDI soweit ich das sehen kann immer zwischen "output
> high" und "output low".
>
> Ich vermute aber, Du musst beide Pins stattdessen zwischen "input" und
> "output low" togglen; der high-level wird bei I2C m.W.n. doch über extra
> Pull-Up-Widerstände erzeugt, und nicht von den einzelnen Bus-Knoten.

Oder die eingebaute HW benutzen

http://download.arduino.org/products/UNOWIFI/0A-ESP8266-Datasheet-EN-v4.3.pdf

Section 3.4

MfG Klaus

von Max M. (maxmicr)


Lesenswert?

Danke für den Hinweis Klaus, da war ich etwas falsch gewickelt. Ich 
dachte I2C funktioniert so (ähnlich) wie SPI.

Ich hab meinen Code abermals umgeschrieben:
1
void pinHigh(uint32_t PIN){
2
  gpio_output_set(0,0,0,PIN);
3
}
4
5
void pinLow(uint32_t PIN){
6
  gpio_output_set(0,PIN,PIN,0);
7
}
8
9
void initI2CPins(){
10
  gpio_init();
11
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
12
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
13
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
14
15
  pinHigh(SDI);
16
  pinHigh(SCK);
17
  pinHigh(SDO);
18
}
19
20
void sendByte(uint8_t dataByte){
21
  uint8_t initBitCounter;
22
  for(initBitCounter = 7; initBitCounter >= 0; initBitCounter--){
23
    uint8_t bitToWrite = (dataByte >> initBitCounter) & 1;
24
    if(bitToWrite == 1){
25
      pinHigh(SDI);
26
    } else {
27
      pinLow(SDI);
28
    }
29
    pinHigh(SCK);
30
    os_delay_us(1);
31
    pinLow(SCK);
32
  }
33
}
34
35
void I2CWrite(uint8_t slaveAddr, uint8_t regAddr, uint8_t data){
36
  uint8_t slAddr = (slaveAddr << 1); //add RW-Bit
37
  uint8_t initBitCounter;
38
39
  //Start Condition
40
  pinLow(SDI);
41
  os_delay_us(1);
42
  pinLow(SCK);
43
44
  sendByte(slAddr);
45
46
  //Stop Condition
47
  pinHigh(SCK);
48
  os_delay_us(1);
49
  pinHigh(SDI);
50
  yield();
51
}
52
53
void user_init(void){
54
  wifi_station_set_auto_connect(0);
55
  uint8_t slaveAddr = 0b01110111;
56
  uint8_t regAddr = 0xA0;
57
  uint8_t data = 0x72;
58
  initI2CPins();
59
60
  os_timer_setfn(&callTimer,(os_timer_func_t*)I2CWrite,slaveAddr,regAddr,data);
61
  os_timer_arm(&callTimer,1000,1); //recall function every 1 second
62
}

Leider meint der ESP8266, sich weiterhin resetten zu müssen. Heißt das 
nun, dass die Ursache dafür möglicherweise der zu kleine Kondensator 
ist?

von Joachim S. (oyo)


Lesenswert?

Der ESP8266 meldet doch beim Booten/resetten über die serielle 
Schnittstelle den "rst cause" und den "boot mode". Diese Informationen 
sollten helfen zu verstehen, aus welchem Grund der ESP überhaupt 
resettet.

Ansonsten wäre mal interessant, wie deine Schaltung derzeit überhaupt 
konkret aussieht, vielleicht wird man dann etwas schlauer.

p.s.: So ganz habe ich doch noch nicht den Sinn des GPIO-Pins für SDO 
verstanden. Bzw. ich verstehe nicht ganz, warum Du den entsprechenden 
Pin des BMP280 nicht einfach fest mit GND oder VCC verdrahtest, wenn 
dadurch eh nur die Slave-Adresse konfiguriert wird. Du wirst ja wohl 
kaum die Slave-Adresse während des Betriebs ändern, warum also einen 
kostbaren GPIO-Pin des ESP8266 verbrauchen?

von Resepp (Gast)


Lesenswert?

Ist das eigentlich vom Mosul abhängig, wie stabil die Spannung sein 
muss?
Wollte mir dieses da besorgen:
http://www.ramser-elektro.at/shop/module-sensoren-adapter-und-co/esp8266-esp12f-wlan-modul/

Aber ich lese immer von stabiler Spannungsquelle und Problemen damit.
Gibt es Alernativen zu den ESP Dingern, welche "besser" (tolleranter) 
funktionieren?

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Joachim S. schrieb:
> Du wirst ja wohl
> kaum die Slave-Adresse während des Betriebs ändern, warum also einen
> kostbaren GPIO-Pin des ESP8266 verbrauchen?

Da hast du eigentlich Recht. Auf die Idee kam ich nur nicht ^^

Joachim S. schrieb:
> Diese Informationen
> sollten helfen zu verstehen, aus welchem Grund der ESP überhaupt
> resettet.

Hm, sagt mir nicht so viel:
1
SDK ver: 1.5.4(baaeaebb) compiled @ May 17 2016 19:23:54
2
phy ver: 972, pp ver: 10.1
3
mode : sta(5c:cf:7f:1c:0a:10)
4
¢@*r…Ä(±±Kít¥dmXK
5
nzAl
6
±#Nørf[112] : 03
7
rf[113] : 00
8
rf[114] : 01

Joachim S. schrieb:
> Ansonsten wäre mal interessant, wie deine Schaltung derzeit überhaupt
> konkret aussieht, vielleicht wird man dann etwas schlauer.

Siehe Anhang, 22uF wurden durch 100uF ersetzt.

von Joachim S. (oyo)


Lesenswert?

Resepp schrieb:
> Ist das eigentlich vom Mosul abhängig, wie stabil die Spannung sein
> muss?
> Wollte mir dieses da besorgen:
> 
http://www.ramser-elektro.at/shop/module-sensoren-adapter-und-co/esp8266-esp12f-wlan-modul/

Nicht dass ich wüsste. Neben kompletten "NodeMCU DevKit"-Boards benutze 
ich persönlich auch nur die ESP-12F-Module. Direkt neben mir habe ich 
gerade eins auf einem Breadboard, zufällig auch genau mit einem 
100µF-Stützkondensator. Da hatte ich bislang auch noch keine Probleme. 
(Aber vielleicht wäre es testweise mal nicht schlecht, ein anderes 
Netzteil zu benutzen, nur um das als Fehlerquelle auszuschliessen?)

Dein Schaltplan sieht ansonsten für mich auch absolut okay aus, da 
könnte ich jetzt auf Anhieb keinen Fehler entdecken.

Was die Ausgabe über die serielle Schnittstelle betrifft: Ich fürchte, 
dass der "reset cause" ausgerechnet in den unleserlichen Zeichen in der 
Mitte steht.
Der reset cause etc. wird mit der etwas ungewöhnlichen Baudrate von 
74880 geschickt - versuche mal, diese Baudrate einzustellen, wenn's 
klappt dann sollte beim booten/reset eine Debug-Nachricht ähnlich der 
folgenden zu lesen sein:
1
ets Jan  8 2013,rst cause:1, boot mode:(3,7)
2
3
load 0x40100000, len 25336, room 16
4
tail 8
5
chksum 0x1f
6
load 0x3ffe8000, len 2544, room 0
7
tail 0
8
chksum 0xb8
9
load 0x3ffe89f0, len 1044, room 8
10
tail 12
11
chksum 0x7f
12
csum 0x7f

von Max M. (maxmicr)


Lesenswert?

Joachim S. schrieb:
> versuche mal, diese Baudrate einzustellen, wenn's
> klappt dann sollte beim booten/reset eine Debug-Nachricht ähnlich der
> folgenden zu lesen sein:

Okay, hier ist die Ausgabe:
1
ets Jan  8 2013,rst cause:2, boot mode:(3,0)
2
3
load 0x40100000, len 24404, room 16 
4
tail 4
5
chksum 0x04
6
load 0x3ffe8000, len 884, room 4 
7
tail 0
8
chksum 0x81
9
load 0x3ffe8380, len 168, room 8 
10
tail 0
11
chksum 0xba
12
csum 0xba
13
ø
14
 ets Jan  8 2013,rst cause:2, boot mode:(3,7)
15
16
load 0x40100000, len 24404, room 16 
17
tail 4
18
chksum 0x04
19
load 0x3ffe8000, len 884, room 4 
20
tail 0
21
chksum 0x81
22
load 0x3ffe8380, len 168, room 8 
23
tail 0
24
chksum 0xba
25
csum 0xba
26
rf[112] : 03
27
rf[113] : 00
28
rf[114] : 01

Die kommt aber nur, wenn ich den ESP8266 manuell neu starte (Strom 
ab/an) und auch nur einmal. Ansonsten erhalte ich auch mit einer Baud 
Rate von 74880 nur nichtssagende Zeichen wenn er automatisch resetet.

: Bearbeitet durch User
von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Ich hab meinen Code nochmal auf die Basics reduziert:
1
#include "ets_sys.h"
2
#include "osapi.h"
3
#include "gpio.h"
4
#include "os_type.h"
5
6
#define SCK BIT2
7
#define SDA BIT0
8
9
static volatile os_timer_t timer;
10
11
void setHigh(uint32_t GPIO){
12
  gpio_output_set(0,0,0,GPIO);
13
}
14
15
void setLow(uint32_t GPIO){
16
  gpio_output_set(0,GPIO,GPIO,0);
17
}
18
19
void setGPIOs(){
20
  gpio_init();
21
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
22
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);
23
}
24
25
void transferByte(uint8_t byte){
26
  uint8_t bitCycle;
27
  for(bitCycle = 7; bitCycle >= 0; bitCycle--){
28
    uint8_t currentBit = (byte >> bitCycle) & 1;
29
    if(currentBit == 1)
30
      setHigh(SDA);
31
    else
32
      setLow(SDA);
33
    setHigh(SCK);
34
    setLow(SCK);
35
  }
36
}
37
38
void user_init()
39
{
40
  uint8_t byte = 0b10101010;
41
  setGPIOs();
42
  os_timer_setfn(&timer,(os_timer_func_t*)transferByte,byte);
43
  os_timer_arm(&timer,5000,1);
44
}

Wenn ich GPIO2 (also SCK) mit meinem Logic Analyzer verbinde, sehe ich, 
dass der ESP viel mehr Pin Toggles macht, als er nach der Schleife 
sollte. Das Aufrufen der Funktion alle 5 Sekunden klappt. Ich frag mich 
nur, was da vor sich geht, Eigentlich sollten da doch höchstens 8 mal 
ein Pin getoggelt werden. Stattdessen passiert das aber fast 2 Sekunden 
lang!?

von Stefan E. (sternst)


Lesenswert?

1
  uint8_t bitCycle;
2
  for(bitCycle = 7; bitCycle >= 0; bitCycle--){
Ein uint8_t ist immer >=0.

von Max M. (maxmicr)


Lesenswert?

Viele Dank! Da sieht man den Wald vor lauter Bäumen nicht, unglaublich. 
Danke für die Lösung des Problems! :)

von Klaus (Gast)


Lesenswert?

Max M. schrieb:
>     if(currentBit == 1)
>       setHigh(SDA);
>     else
>       setLow(SDA);
>     setHigh(SCK);
>     setLow(SCK);
>   }

Ans Clock stretching gedacht? Und wie siehts mit ACK/NAK aus?

MfG Klaus

von Max M. (maxmicr)


Lesenswert?

Klaus schrieb:
> Ans Clock stretching gedacht? Und wie siehts mit ACK/NAK aus?

Das kommt alles noch, wobei ich hoffe, dass mein Sensor kein Clock 
stretching macht sonst wirds echt kompliziert (für mich) zum 
programmieren, wenn nötig, mach ich die Übertragung so langsam wie 
nötig. Ich bin froh, dass der ESP (erstmal) nicht mehr zickt. Lustig, 
dass der Fehler dazu geführt hat, dass ich mir das SDK auf meinem 
Linux-PC installiert hab. Klappt dort wesentlich besser und hab auch 
keine Probleme mehr mit dem aktuellen SDK 2.0.

: Bearbeitet durch User
von bitCycle (Gast)


Lesenswert?

Max M. schrieb:
> Viele Dank! Da sieht man den Wald vor lauter Bäumen nicht,
> unglaublich.
> Danke für die Lösung des Problems! :)

bitCycle++
bitCycle--

diese Doppelpostings in allen Foren
und dann auch noch unterschiedlich abgeschrieben:
man sollte sich mal entscheiden?

http://www.esp8266.com/viewtopic.php?f=6&t=12776&p=59024#p59024
1
void transferByte(uint8_t byte){
2
  uint8_t bitCycle;
3
  for(bitCycle = 7; bitCycle >= 0; bitCycle++){
4
    uint8_t currentBit = (byte >> bitCycle) & 1;
5
    if(currentBit == 1)
6
      setHigh(SDA);
7
    else
8
      setLow(SDA);
9
    setHigh(SCK);
10
    os_delay_us(3);
11
    setLow(SCK);
12
  }
13
  os_delay_us(10);
14
}

von Max M. (maxmicr)


Lesenswert?

Wie gesagt, ich war an einer schnellen Problemlösung interessiert, in 
Zukunft benutze ich ausschließlich dieses Forum, ich hoffe, dass nimmt 
mir keiner übel.

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Ich bin gerade dabei zu überprüfen, ob der Slave ein ACK gesendet hat. 
Meiner Information nach, passiert das, wenn die Datenleitung (SDA) auf 
Low gezogen wird. Nun bin ich mir nicht ganz sicher, wie ich denn beim 
ESP den Status eines GPIOs auslese. In der SDK Api Reference gibt es die 
Funktion GPIO_INPUT_GET(gpio_no). Muss da als Parameter nun das Bit (so 
wie bei gpio_output_set ) oder die tatsächliche Nummer (also 0,1,2 
usw). rein?

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Okay, anscheinend muss man hier direkt mit der Nummer des Ports 
arbeiten. Noch eine Frage:

Wie ermittle ich, ob der Slave sein ACK gesendet hat? Nach dieser Seite:

http://www.cc-zwei.de/wiki/index.php?title=I2C_Bus_Hintergrundwissen

wird bei einem Slave-ACK die Datenleitung auf low gezogen. Was würde 
aber nun passieren, wenn das letzte übertragene Bit eine 0 ist, dann 
wäre SDA schon standardmäßig auf low und ich würde ich eine 0 lesen 
obwohl der Slave möglicherweise gar nichts gesendet hat?

von Max M. (maxmicr)


Lesenswert?

Ich hab nun versucht, das ganze fertig zu bauen, so sieht mein aktueller 
Stand aus:
1
#include "include/ets_sys.h"
2
#include "include/osapi.h"
3
#include "include/gpio.h"
4
#include "include/os_type.h"
5
#include "include/uart.c"
6
7
#define SCK BIT13
8
#define SDA BIT12
9
10
static volatile SLAVE_WRITE = 0b11101110;
11
static volatile SLAVE_READ = 0b11101111;
12
static volatile os_timer_t timer;
13
14
void initUART(){
15
  uart_init(BIT_RATE_115200,BIT_RATE_115200);
16
}
17
18
void uart0_sendInt(uint32_t decimal){
19
  char s[15];
20
  os_sprintf(s,"%ld",decimal);
21
  uart0_sendStr(s);
22
}
23
24
void setHigh(uint32_t GPIO){
25
  gpio_output_set(0,0,0,GPIO);
26
}
27
28
void setLow(uint32_t GPIO){
29
  gpio_output_set(0,GPIO,GPIO,0);
30
}
31
32
void setGPIOs(){
33
  gpio_init();
34
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U,FUNC_GPIO13);
35
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U,FUNC_GPIO12);
36
  setHigh(SCK);
37
  setHigh(SDA);
38
}
39
40
void I2CStart(){
41
  setLow(SDA);
42
  os_delay_us(2);
43
  setLow(SCK);
44
}
45
46
void I2CStop(){
47
  setHigh(SCK);
48
  os_delay_us(2);
49
  setHigh(SDA);
50
}
51
52
void I2CMasterAck(){
53
  setLow(SDA);
54
}
55
56
void I2CMasterNAck(){
57
  setHigh(SDA);
58
}
59
60
void transferByte(uint8_t byte){
61
  int16_t bitCycle;
62
  for(bitCycle = 7; bitCycle >= 0; bitCycle--){
63
    uint8_t currentBit = (byte >> bitCycle) & 1;
64
    if(currentBit == 1){
65
      setHigh(SDA);
66
    }else{
67
      setLow(SDA);
68
    }
69
    setHigh(SCK);
70
    os_delay_us(2);
71
    setLow(SCK);
72
  }
73
}
74
75
uint8_t readByte(){
76
  int16_t bitCounter;
77
  uint8_t receivedByte = 0;
78
  for(bitCounter = 7; bitCounter >= 0; bitCounter--){
79
    setHigh(SCK);
80
    os_delay_us(2);
81
    if(GPIO_INPUT_GET(12) == 1){
82
      bitCounter |= (1<<bitCounter);
83
    }
84
    setLow(SCK);
85
  }
86
  return receivedByte;
87
}
88
89
bool checkSlaveClkReceived(){
90
  os_delay_us(5); //wait to receive slave clk
91
  return GPIO_INPUT_GET(13);
92
}
93
94
bool sendByteWithSlaveAck(uint8_t byte){
95
  transferByte(SLAVE_WRITE);
96
  return checkSlaveClkReceived();
97
}
98
99
void I2CSetupBMP280(){
100
  I2CStart();
101
102
  if(sendByteWithSlaveAck(SLAVE_WRITE)){
103
    if(sendByteWithSlaveAck(0xF4)){
104
      if(sendByteWithSlaveAck(0b00100011)){
105
        uart0_sendStr("Setup BMP280 successfully.\r\n");
106
      } else{
107
        uart0_sendStr("Didn't receive 0xF4 Data ack.\r\n");
108
      }
109
    } else {
110
      uart0_sendStr("Didn't receive 0xF4 Reg ack.\r\n");
111
    }
112
  } else{
113
    uart0_sendStr("Didn't receive slave address ack.\r\n");
114
  }
115
116
  I2CStop();
117
}
118
119
uint16_t I2CReadBMP280Temperature(){
120
  uint8_t msb = 0;
121
  uint8_t lsb = 0;
122
  I2CStart();
123
  if(sendByteWithSlaveAck(SLAVE_WRITE)){
124
    if(sendByteWithSlaveAck(0xFA)){ //tell slave the register we want to read from
125
      I2CStart();
126
      if(sendByteWithSlaveAck(SLAVE_READ)){
127
          msb = readByte(); //data from 0xFA
128
          if(checkSlaveClkReceived()){
129
            I2CMasterAck();
130
            lsb = readByte(); //data from 0xFB
131
            if(checkSlaveClkReceived()){
132
              I2CMasterNAck();
133
            }
134
          } else {
135
            uart0_sendStr("Didn't receive slave clk after msb read\r\n");
136
          }
137
      } else {
138
        uart0_sendStr("Didn't receive slave read address ack (Read)\r\n");
139
      }
140
    } else {
141
      uart0_sendStr("Didn't receive slave register ack (Read)\r\n");
142
    }
143
  } else {
144
    uart0_sendStr("Didn't receive slave write address ack (Read)\r\n");
145
  }
146
  I2CStop();
147
  return (msb << 8) | lsb;
148
}
149
150
void BMP280Management(){
151
  uint16_t tempValue;
152
  I2CSetupBMP280();
153
  tempValue = I2CReadBMP280Temperature();
154
  if(tempValue > 0)
155
    uart0_sendInt(tempValue);
156
}
157
158
void user_init()
159
{
160
  wifi_station_set_auto_connect(0); //prevent auto connect wo wifi
161
162
  setGPIOs();
163
  initUART();
164
  os_timer_setfn(&timer,(os_timer_func_t*)BMP280Management,NULL);
165
  os_timer_arm(&timer,500,1);
166
}

Ich bin mir nicht ganz sicher, ob meine Vorgehensweise so richtig ist:

Nach jedem gesendeten Byte überprüfe ich mit "checkSlaveClkReceived", ob 
die Clock-Leitung high ist (nach meiner Information müsste der Slave ein 
Clock Signal für das Ack erzeugen), ist das so richtig? Ich bin für jede 
Hilfe zu diesem Thema dankbar.

von Klaus (Gast)


Lesenswert?

Max M. schrieb:
> (nach meiner Information müsste der Slave ein
> Clock Signal für das Ack erzeugen)

Nein, der Master muß einen 9tes Clocksignal erzeugen und dabei SDA 
beobachten. Als ACK zieht der Slave SDA auf Low. Wenn der Slave für das 
ACK etwas länger braucht, stretched er die Clock ein wenig. Ist er gar 
nicht da (oder die Adresse falsch) bleibt SDA durch den Pullup einfach 
HIGH, also NAK.

Die I2C Spec lesen und sie verstehen hilft ungemein. Auch eine 
Umgebung, wo man mit LA und Debugger sich die ganze Sache Bit für Bit 
ansehen kann, hilft beim Verstehen. Eine Erkenntnis, die man daraus 
gewinnen kann, ist das das Handshaking der Clockleitung zwischen Master 
und Slave, auch Clock-Stretching genannt, mandantory für das 
Funktionieren von I2C ist.

MfG Klaus

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Klaus schrieb:
> Nein, der Master muß einen 9tes Clocksignal erzeugen und dabei SDA
> beobachten. Als ACK zieht der Slave SDA auf Low.

Hier ist das Diagramm vom Logic Analyzer, sieht doch eigentlich okay 
aus, oder?
In diesem Fall (das letzte Bit ist 0 ergo ist auch SDA 0) könnte ich 
doch beim Ack gar nicht unterscheiden zwischen einer "0" vom Slave und 
einer "0" vom letzten Bit?

: Bearbeitet durch User
von Klaus (Gast)


Lesenswert?

Max M. schrieb:
> In diesem Fall (das letzte Bit ist 0 ergo ist auch SDA 0) könnte ich
> doch beim Ack gar nicht unterscheiden zwischen einer "0" vom Slave und
> einer "0" vom letzten Bit?

Da lässt der Master SDA beim letzten Bit nicht los. Der I2C Bus ist 
Opencollektor getrieben. Nur für das Bit, das gerade in Arbeit ist, darf 
einer, Master oder Slave den Bus auf Low ziehen.

Klaus schrieb:
> Die I2C Spec lesen und sie verstehen hilft ungemein.

MfG Klaus

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Danke für deine Hilfe, Klaus.

Klaus schrieb:
> Da lässt der Master SDA beim letzten Bit nicht los. Der I2C Bus ist
> Opencollektor getrieben. Nur für das Bit, das gerade in Arbeit ist, darf
> einer, Master oder Slave den Bus auf Low ziehen.

Sieht das nun so besser aus? Wenn der Slave kein Ack senden würde, gäbe 
es aber doch kein gültiges Stop-Signal (oder ist das dann schon "egal")?

von Max M. (maxmicr)


Lesenswert?

Also inzwischen bin ich schon so weit, dass ich Acks vom Slave empfange 
(wenn ich die SDO-Leitung vom BMP280 abziehe, bekomme ich kein Ack mehr, 
ebenso wenn ich SCK, SDA oder GND abziehe, ergo kann ich wahrscheinlich 
einen Software-Fehler ausschließen).

Das Lesen der Register-Werte funktioniert noch nicht so ganz. Wie im 
Datenblatt des BMP280 zu lesen ist gibt man das Start-Register, von dem 
man lesen möchte, an und danach wird nach jedem Ack vom Master die 
Register-Adresse auto-inkrementiert. So wie ich das aktuell sehe, ist 
ein Master Ack, wenn bei einem zusätzlichen clk-Impuls die SDA-Leitung 
auf low gezogen wird. Beim Master Nack wäre stattdessen die SDA-Leitung 
auf high.

Das ist aktuell die Funktion, die das realisieren soll:
1
uint32_t I2CReadBMP280Temperature(){
2
  uint8_t msb = 0;
3
  uint8_t lsb = 0;
4
  uint8_t xlsb = 0;
5
  I2CStart();
6
  if(sendByteWithSlaveAck(SLAVE_WRITE)){
7
    if(sendByteWithSlaveAck(0xFA)){ //tell slave the register we want to read from
8
      I2CStart();
9
      if(sendByteWithSlaveAck(SLAVE_READ)){
10
        uart0_sendStr("Received slave's last read ack.\r\n");
11
        msb = readByte(); //data from 0xFA
12
        I2CMasterAck();
13
        lsb = readByte();
14
        I2CMasterAck();
15
        xlsb = readByte();
16
        I2CMasterNAck();
17
      }
18
    }
19
  }
20
  I2CStop();
21
  return ((msb << 12) | lsb) | xlsb;
22
}

Meine Read-Funktion:
1
uint8_t readByte(){
2
  int16_t bitCounter;
3
  uint8_t receivedByte = 0;
4
  for(bitCounter = 7; bitCounter >= 0; bitCounter--){
5
    setHigh(SCK);
6
    os_delay_us(4);
7
    if(GPIO_INPUT_GET(12) == 1){
8
      receivedByte |= (1<<bitCounter);
9
    }
10
    setLow(SCK);
11
  }
12
  return receivedByte;
13
}

Master Ack + Master Nack:
1
void I2CMasterAck(){
2
  setHigh(SCK);
3
  setLow(SDA);
4
  os_delay_us(2);
5
  setLow(SCK);
6
  os_delay_us(2);
7
  setHigh(SDA); //release SDA
8
}
9
10
void I2CMasterNAck(){
11
  setHigh(SCK);
12
  setHigh(SDA);
13
  os_delay_us(2);
14
  setLow(SCK);
15
}

Auf meiner Konsole bekomme ich:
1
Setup BMP280 successfully.
2
Received slave's last read ack.
3
1044735

Also ich schaffe es schon einmal in die innerste Anweisung. Allerdings 
bekomme ich immer die oben stehende Zahl, normalerweise müsste die sich 
bei jedem Lesen etwas ändern (ich lese alle 500ms), oder?

Hat jemand noch einen Tipp für mich?

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Könnte das Problem vielleicht die 2. Start Kondition beim Lesen sein? Um 
eine Startkondition erkennen zu können, müssen doch sowohl SDA als auch 
SCK von high auf low gehen? D.h. ich muss irgendwie sicher stellen das 
SDA und SCK high sind. Wie mache ich das? Kann ich SCK und SDA einfach 
auf high setzen oder wird das vom Sensor möglicherweise falsch 
interpretiert?

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Ich hab nun noch mal den Read-Vorgang durch meinen Analyzer laufen 
lassen, diesmal setze ich SCK explizit auf High bevor die 2. 
Start-Bedingung kommt, eigentlich sieht alles gut aus, sogar die Logic 
Analyzer Software erkennt, um was es sich bei der Bitfolge handelt. Hat 
noch jemand Ideen dazu?

: Bearbeitet durch User
von Planlos (Gast)


Lesenswert?

Max M. schrieb:
> diesmal setze ich SCK explizit auf High bevor die 2.
> Start-Bedingung kommt

Darfst du eigentlich nicht.
Der Master darf SCK nur auf Low ziehen, nie auf High.
Und der Master sollte eigentlich, wenn er SCK "loslässt", also nicht 
mehr auf LOW zieht, die SCK-Leitung auf die L->H Flanke überwachen, und 
auf diese Flanke Reagieren.
Sonst kann das Device den SCK-Clock nicht verlangsamen, wenn der Master 
zu schnell ist.

von Max M. (maxmicr)


Lesenswert?

Planlos schrieb:
> Der Master darf SCK nur auf Low ziehen, nie auf High.
> Und der Master sollte eigentlich, wenn er SCK "loslässt", also nicht
> mehr auf LOW zieht, die SCK-Leitung auf die L->H Flanke überwachen, und
> auf diese Flanke Reagieren.

Kann es sein, dass du hier SDA und SCK verwechselst? Beim Lesevorgang 
muss doch der Master den Clock generieren damit der Slave die Bits über 
SDA rausschiebt, oder?

von Max M. (maxmicr)


Lesenswert?

Okay, ich habs nun endlich hinbekommen. Das Auslesen der Device ID 
funktioniert auf jeden Fall, bei dem Temperatur-Register kommt ein Wert 
raus, der sich erhöht, wenn ich den Sensor berühre und verringert, wenn 
ich wieder loslasse. Außerdem ändert er sich bei jedem Auslesen etwas, 
von dem her denke ich mal, das müsste passen. Jetzt ist nur das Problem, 
dass ich nicht ganz durch die Formel steige, mit der man die Temperatur 
berechnet. Im Datenblatt 
(https://cdn-shop.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf) auf 
S.22 ff ist die Formel zu finden. Anscheinend muss ich noch zusätzliche 
Werte auslesen (dig_T1, dig_T2, dig_T3). In der Formel kommt allerdings 
auch ein Wert "adc_T" vor. Woher bekomme ich den?

: Bearbeitet durch User
von Planlos (Gast)


Lesenswert?

Max M. schrieb:
> Kann es sein, dass du hier SDA und SCK verwechselst?

Nein. Sowohl für SDA als auch für SCL gilt: der einzige "Busteilnehmer", 
der die Leitung auf HIGH stellen darf, ist der Pull-Up.

Das ist das Problem mit den Logic-Analyzer-Auswertungen:
Du siehst, was am Bus ist, aber nicht, wer es verursacht.
Das "Wer" ist bei I²C aber wichtig.

von STler (Gast)


Lesenswert?

Max M. schrieb:
> S.22 ff ist die Formel zu finden. Anscheinend muss ich noch zusätzliche
> Werte auslesen (dig_T1, dig_T2, dig_T3). In der Formel kommt allerdings
> auch ein Wert "adc_T" vor. Woher bekomme ich den?


Schau besser hier rein:
https://github.com/BoschSensortec/BMP280_driver

von Max M. (maxmicr)


Lesenswert?

Planlos schrieb:
> Nein. Sowohl für SDA als auch für SCL gilt: der einzige "Busteilnehmer",
> der die Leitung auf HIGH stellen darf, ist der Pull-Up.

Okay, mit high meinte ich, wenn keiner der Busteilnehmer die Leitung auf 
low zieht (wenn also der portpin als Eingang geschaltet ist). SCK wird 
der slave ja wohl kaum auf low ziehen, oder?

von Michael U. (amiga)


Lesenswert?

Hallo,

Max M. schrieb:
> Okay, mit high meinte ich, wenn keiner der Busteilnehmer die Leitung auf
> low zieht (wenn also der portpin als Eingang geschaltet ist). SCK wird
> der slave ja wohl kaum auf low ziehen, oder?

eben doch! Ein Slave darf SCK auf Low halten wenn er noch Zeit braucht, 
eben Clock Stretching.
Der Master muß also auch bei SCK nach dem freigenen von SCK dieses 
einlesen und muß warten, bis SCK wirklich wirder auf high ist.
Es sind relativ wenige Slave-Devices, die es nutzen.

Den Fehler hatte selbst Atmel in seinem allerersten I2C Beispielcode 
drin.
Mein MAS3507 hatte damals garkein Verständnis dafür...

Gruß aus Berlin
Michael

von Klaus (Gast)


Lesenswert?

Planlos schrieb:
> Der Master darf SCK nur auf Low ziehen, nie auf High.
> Und der Master sollte eigentlich, wenn er SCK "loslässt", also nicht
> mehr auf LOW zieht, die SCK-Leitung auf die L->H Flanke überwachen, und
> auf diese Flanke Reagieren.

Max M. schrieb:
> SCK wird der slave ja wohl kaum auf low ziehen, oder?

Aber sicher doch. Manchmal so kurz, daß du es nicht bemerkst. manchmal 
auch länger. Ein sauber implementierter Slave legt SCL auf Low, sobald 
er merkt, daß der Master es auf Low gelegt hat. So kann ihm dieser Takt 
nicht mehr durch die Lappen gehen. Dann tut er was nötig ist und lässt 
SCL wieder los. Ist er damit schneller als der Master, merkst du das 
nicht. Ist er langsamer, muß der Master länger auf das High warten.

MfG Klaus

Ich schreib zu langsam

von Michael U. (amiga)


Lesenswert?

Hallo,

Klaus schrieb:
> Ich schreib zu langsam

dafür mit weniger Tippfehlern als ich... ;-)

Es ist bei eigenen I2C-Routinen dann gern der nächste Fehler, in dieser 
Abfrage von SCK beim Master kein Timeout einzubauen. Eine Störung oder 
ein hängender Slave sorgt dann sicher dafür, daß sich garnichts mehr 
rührt.

Gruß aus Berlin
Michael

von Joachim S. (oyo)


Lesenswert?

Michael U. schrieb:
> Es ist bei eigenen I2C-Routinen dann gern der nächste Fehler, in dieser
> Abfrage von SCK beim Master kein Timeout einzubauen. Eine Störung oder
> ein hängender Slave sorgt dann sicher dafür, daß sich garnichts mehr
> rührt.

Das verstehe ich gerade nicht ganz, kannst Du das nochmal etwas 
ausführlicher erklären?
Meinst Du damit, dass der Master in der Routine, in der er die SCK-Line 
auf eventuelles Clock-Stretching durch den Slave überprüft (indem er 
darauf wartet, dass SCK wieder high-level hat), einen Timeout benutzen 
sollte?

Falls ja, verstehe ich nämlich gerade nicht, was der Vorteil des 
Timeouts sein soll - denn solange SCK durch den Slave oder was auch 
immer auf low gezogen wird, ist der Bus doch eh komplett blockiert? Der 
Master wüsste im Falle eines Timeouts dann zwar vielleicht, dass etwas 
nicht stimmt, aber ändern kann er daran dann doch auch nichts?

von Klaus (Gast)


Lesenswert?

Joachim S. schrieb:
> aber ändern kann er daran dann doch auch nichts?

Aber er kann ne rote Lampe blinken lassen oder alles resetten oder ...

MfG Klaus

von merkste was (Gast)


Lesenswert?

Joachim S. schrieb:
..
> Das verstehe ich gerade nicht ganz, kannst Du das nochmal etwas
> ausführlicher erklären?
> Meinst Du damit, ..
> Falls ja, verstehe ich nämlich gerade nicht, was der Vorteil des
> Timeouts sein soll - .. ist der Bus doch eh komplett blockiert? ..
> Der Master wüsste im Falle eines Timeouts dann zwar vielleicht,
> dass etwas nicht stimmt

"errorlevel" auswerten

> aber ändern kann er daran dann doch auch nichts?

handlen kann er aber:

"SLAVE Reseten" kann er dann, wenn er hängt.
"STÖRUNG" anzeigen kann er nach fruchtlosen Versuchen
"..." ...

von Joachim S. (oyo)


Lesenswert?

Ich sehe ein, dass es potentiell sinnvoll und nützlich sein könnte, eine 
zu lange "gestretchte" SCK-Line per Timeout zu erkennen.
Auch, dass der Master man mit gewissen Vorkehrungen (Slave resetten) 
unter Umständen sogar das Problem beheben kann.

Aber ich würde jetzt vermuten, dass Vorkehrungen wie "Slave Resetten 
wenn Clock zu lange gestretcht" in der Praxis doch nur sehr selten 
eingesetzt werden.

Von daher hätte ich das jetzt eher als rein optionale, 
"kann-man-machen"-Massnahme betrachtet; dass amiga den Verzicht auf 
diese Massnahme stattdessen als "Fehler" bezeichnet, verwundert mich 
halt ein bisschen.

von Klaus (Gast)


Lesenswert?

Joachim S. schrieb:
> Von daher hätte ich das jetzt eher als rein optionale,
> "kann-man-machen"-Massnahme betrachtet;

Und dann kommt so eine Frage wie: "Mein Baustein resettet sich ...." Tja 
weil der Watchdog nicht bedient wurde, weil der Prozessor aus einer 
Schleife nicht mehr raus kam

MfG Klaus

von Michael U. (amiga)


Lesenswert?

Hallo,

Joachim S. schrieb:
> Von daher hätte ich das jetzt eher als rein optionale,
> "kann-man-machen"-Massnahme betrachtet; dass amiga den Verzicht auf
> diese Massnahme stattdessen als "Fehler" bezeichnet, verwundert mich
> halt ein bisschen.

gut, sagen wir es so: normalerweise passiert das nicht.
Ein abgestürzter Slave, der SCK auf Low festhält, ist normalweise nicht 
mehr anzusprechen. PowerOn-Reset ist dann normalerweise nötig.
Es muß auch nicht dort abgesichert werden, wenn man den genauen Fehler 
nicht wissen muß. Es sollte nur darauf hinweisen, daß man die ganze 
I2C-Routine per TimeOut o.ä. dagegen absichern sollte.
Ein einzelner Sensor, der den ganzen µC lahmlegt, ist nicht die ideale 
Lösung.

Gruß aus Berlin
Michael

von Robert M. (robbb)


Lesenswert?

Ich weiß nicht, ob dir das hilft, aber ich bin auch schon mal fast 
verrückt geworden, weil mein ESP-12F sich immer wieder mal resettete. 
Bei mir war die Lösung, dass ich keine oder zu geringe Delays zwischen 
dem Ein/Ausschalten eines GPIOs hatte (brauchte das für eine 
Schrittmotorsteuerung. Sobald ich dazwischen ein tmr.delay(100) baute 
(ich nutze die NodeMCU Firmware, heißt bei dir wohl anders) 
funktionierte alles total stabil und es gab seitdem überhaupt keine 
Resets mehr.

von Michael U. (amiga)


Lesenswert?

Hallo,

dem ESP ist es egal, wie schnell Di die GPIOs umschaltest,
aber als Beispiel:
1
  for (int i =0; i<100; i++)
2
  {
3
    digitalWrite(GPIO,LOW);
4
    digitalWrite(GPIO,HIGH);
5
  }

ist blockierend, der ESP kann während der Laufzeit der for-Schleife 
nichts anderes machen. Da die WALN-Routinen usw. aber zum Zug kommen 
müssen und damit auch der WatchDog zurückgesetzt werden muß, schlägt der 
zu, wenn solch eine blockierende Schleife zu lange dauert.
1
  for (int i =0; i<100; i++)
2
  {
3
    yield();
4
    digitalWrite(GPIO,LOW);
5
    digitalWrite(GPIO,HIGH);
6
  }

Ein yield(); (oder delay(0);) behebt das Problem, da dort die Routinen 
abgearbeitet werden wenn nötig.

Gruß aus Berlin
Michael

von Max M. (maxmicr)


Lesenswert?

Wer hat sich denn die **** Formel fürs Temperatur umrechnen ausgedacht? 
Nicht nur, dass man da ohne Ende doubles benutzen muss, was nicht gerade 
speicherplatzschonend ist, nein, man muss auch noch wie wild schieben so 
dass man sich gar nicht mehr auskennt. Auf GitHub erkennt man vor lauter 
Klammern nicht mehr, welcher Wert denn nun wie oft wo hin geschoben wird 
und mit welchem Operator er mit welchen anderen Werten verbunden ist. 
20-bit Temperaturauflösung schön und gut, aber das ist doch Mist...Kein 
normaler Mensch erkennt den Hintergrund der Formel. Anscheinend braucht 
man dafür einen 32-bit ARM mit FPU um das auszurechnen.

Hat jemand zufällig eine Idee, wie das einfach(er) funktioniert bzw. hat 
schon mal jemand mit dem BMP280 gearbeitet?

: Bearbeitet durch User
von Michael U. (amiga)


Lesenswert?

Hallo,

Max M. schrieb:
> Hat jemand zufällig eine Idee, wie das einfach(er) funktioniert bzw. hat
> schon mal jemand mit dem BMP280 gearbeitet?

naja, die Bibliotheken, die ich kenne, halten sich nahezu 1:1 an das 
Datenblatt des BME280.
Und die gehen auch auf einem Mega328 problemlos...

Gruß aus Berlin
Michael

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Hab ich die Formel so richtig entziffert?
Edit: Nächster Knaller, der Sensor ist so ausgelegt, dass er die Adresse 
des Registers nach jedem Lesevorgang automatisch inkrementiert, so dass 
man mit einem Rutsch mehrere nach einander folgende Register lesen kann. 
Bei der Temperatur klappt das noch, da zuerst MSB (aus Register "x"), 
dann LSB (aus x+1) dann XLSB (aus x+2). Bei den Korrekturwerten dig_T1 
ist es jedoch genau umgedreht, also es kommt zuerst LSB, dann MSB, frag 
mich nicht warum!

: Bearbeitet durch User
von Michael U. (amiga)


Lesenswert?

Hallo,

wenn der Hersteller die Register so angeordnet hat, hat er das eben...
Zu den Formeln: im Datenblatt sind doch Beispiel-Source drin, in 
Ganzzahl-Arithmetik usw. usw.
Die funktionieren auch in der Realität.

Ich habe das Spielchen vor Jahren mit dem FOST HP03S durchexerziert, 
ohne wirkliche Beispiele usw. Hat etwas halt gedauert, bis alle 
Wertebereiche passten usw. Läuft heute noch auch einem Mega in ASM 
geschrieben.

Bei den BME280 habe ich mit der Erfahrung zugegebenermaßen lieber auf 
die Arbeit anderer zurückgegriffen...

Gruß aus Berlin
Michael

von Max M. (maxmicr)


Lesenswert?

Michael U. schrieb:
> Zu den Formeln: im Datenblatt sind doch Beispiel-Source drin, in
> Ganzzahl-Arithmetik usw. usw.
> Die funktionieren auch in der Realität.

Danke für den Hinweis, hab ich zu spät gesehen. Ich frage mich gerade, 
wie ich über I2C einen signed-Wert lesen kann (für dig_T3 und dig_T2 
benötigt) und was passiert, wenn ich den shifte? Mein Ansatz:
1
int8_t readSignedByte(){
2
  int16_t bitCounter;
3
  int8_t receivedByte = 0;
4
  for(bitCounter = 7; bitCounter >= 0; bitCounter--){
5
    setHigh(SCK);
6
    os_delay_us(DELAY);
7
    if(GPIO_INPUT_GET(12) == 1){
8
      receivedByte |= (1<<bitCounter);
9
    }
10
    setLow(SCK);
11
  }
12
  return receivedByte;
13
}
14
15
int32_t I2CReadBMP280Signed(uint8_t regAddr, int8_t amount){
16
  int32_t result = 0;
17
  I2CStart();
18
  if(sendByteWithSlaveAck(SLAVE_WRITE)){
19
    if(sendByteWithSlaveAck(regAddr)){
20
      os_delay_us(DELAY);
21
      setHigh(SCK);
22
      os_delay_us(DELAY);
23
      I2CStart();
24
      if(sendByteWithSlaveAck(SLAVE_READ)){
25
        while(amount > 0){
26
          result |= (readSignedByte() << (amount-1)*8);
27
          amount--;
28
          if(amount == 0){
29
            I2CMasterNAck();
30
          } else {
31
            I2CMasterAck();
32
            setHigh(SDA);
33
          }
34
        }
35
      }
36
    }
37
  }
38
  I2CStop();
39
  return result;
40
}
41
42
void BMP280Management(){
43
  I2CSetupBMP280();
44
  uint16_t t1 = (I2CReadBMP280(0x89,1) | I2CReadBMP280(0x88,1));
45
  int16_t t2 = (I2CReadBMP280Signed(0x8B,1) | I2CReadBMP280Signed(0x8A,1));
46
  int16_t t3 = (I2CReadBMP280Signed(0x8D,1) | I2CReadBMP280Signed(0x8C,1));
47
}

Funktioniert nicht wirklich, bekomme 111,-9 und 50 raus. Jemand ne Idee?

: Bearbeitet durch User
von Michael U. (amiga)


Lesenswert?

Hallo,

ich habe jetzt nicht im Detail geschaut, aber:
über I2C kommen 8 Bit-Werte.

{code]
 int daten;
 daten = I2CreadByte() << 8; // wenn h zuerst kommt
 daten = I2CreadByte();
[/code]

Ob Du das dann als signed oder unsigned behandelst hängt nur von der 
Definiton von daten ab.
Hoffentlich keinen krassen fFehler reingetippt...

Gruß aus Berlin
Michael

von Max M. (maxmicr)


Lesenswert?

Muss meine Funktion readByte einen int8_t oder uint8_t zurückgeben, 
damit ich in der  I2CReadBMP280Signed-Funktion auch einen passenden 
signed Wert zusammenbekomme, immerhin wird da auch geschoben? Oder ist 
das egal?

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Heureca, ich habs nun endlich geschafft. Mit der Ganzzahl-Formel bekomme 
ich valide Werte (~2387 = 23.87°C). Falls es jemanden interessiert, hier 
der gesamte Code für den ESP8266:
1
#include "include/ets_sys.h"
2
#include "include/osapi.h"
3
#include "include/gpio.h"
4
#include "include/os_type.h"
5
#include "include/uart.c"
6
7
#define SCK BIT13
8
#define SDA BIT12
9
10
#define DELAY 5
11
12
static volatile uint8_t SLAVE_WRITE = 0b11101110;
13
static volatile uint8_t SLAVE_READ = 0b11101111;
14
static volatile os_timer_t timer;
15
static volatile uint16_t dig_T1;
16
static volatile int16_t dig_T2;
17
static volatile int16_t dig_T3;
18
19
void initUART(){
20
  uart_init(BIT_RATE_115200,BIT_RATE_115200);
21
}
22
23
void uart0_sendInt(int32_t decimal){
24
  char s[15];
25
  os_sprintf(s,"%ld",decimal);
26
  uart0_sendStr(s);
27
}
28
29
void setHigh(uint32_t GPIO){
30
  gpio_output_set(0,0,0,GPIO);
31
}
32
33
void setLow(uint32_t GPIO){
34
  gpio_output_set(0,GPIO,GPIO,0);
35
}
36
37
void setGPIOs(){
38
  gpio_init();
39
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U,FUNC_GPIO13);
40
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U,FUNC_GPIO12);
41
  setHigh(SCK);
42
  setHigh(SDA);
43
}
44
45
void I2CStart(){
46
  setLow(SDA);
47
  os_delay_us(DELAY);
48
  setLow(SCK);
49
}
50
51
void I2CStop(){
52
  setHigh(SCK);
53
  os_delay_us(DELAY);
54
  setHigh(SDA);
55
}
56
57
void I2CMasterAck(){
58
  setLow(SDA);
59
  setHigh(SCK);
60
  os_delay_us(DELAY);
61
  setLow(SCK);
62
  setHigh(SDA);
63
}
64
65
void I2CMasterNAck(){
66
  setHigh(SDA);
67
  setHigh(SCK);
68
  os_delay_us(DELAY);
69
  setLow(SCK);
70
}
71
72
void transferByte(uint8_t byte){
73
  int16_t bitCycle;
74
  for(bitCycle = 7; bitCycle >= 0; bitCycle--){
75
    uint8_t currentBit = (byte >> bitCycle) & 1;
76
    if(currentBit == 1){
77
      setHigh(SDA);
78
    }else{
79
      setLow(SDA);
80
    }
81
    setHigh(SCK);
82
    os_delay_us(DELAY);
83
    setLow(SCK);
84
  }
85
  setHigh(SDA); //releaseLine
86
}
87
88
uint8_t readByte(){
89
  int16_t bitCounter;
90
  uint8_t receivedByte = 0;
91
  for(bitCounter = 7; bitCounter >= 0; bitCounter--){
92
    setHigh(SCK);
93
    os_delay_us(DELAY);
94
    if(GPIO_INPUT_GET(12) == 1){
95
      receivedByte |= (1<<bitCounter);
96
    }
97
    setLow(SCK);
98
  }
99
  return receivedByte;
100
}
101
102
bool checkSlaveClkReceived(){
103
  uint8_t sdaValue;
104
  setHigh(SCK);
105
  os_delay_us(DELAY); //wait to receive slave ack
106
  sdaValue = GPIO_INPUT_GET(12);
107
  setLow(SCK);
108
  return sdaValue == 0;
109
}
110
111
bool sendByteWithSlaveAck(uint8_t byte){
112
  transferByte(byte);
113
  return checkSlaveClkReceived();
114
}
115
116
uint32_t I2CReadBMP280(uint8_t regAddr, int8_t amount){
117
  uint32_t result = 0;
118
  I2CStart();
119
  if(sendByteWithSlaveAck(SLAVE_WRITE)){
120
    if(sendByteWithSlaveAck(regAddr)){
121
      os_delay_us(DELAY);
122
      setHigh(SCK);
123
      os_delay_us(DELAY);
124
      I2CStart();
125
      if(sendByteWithSlaveAck(SLAVE_READ)){
126
        while(amount > 0){
127
          result |= (readByte() << (amount-1)*8);
128
          amount--;
129
          if(amount == 0){
130
            I2CMasterNAck();
131
          } else {
132
            I2CMasterAck();
133
            setHigh(SDA);
134
          }
135
        }
136
      }
137
    }
138
  }
139
  I2CStop();
140
  return result;
141
}
142
143
void I2CSetupBMP280(){
144
  I2CStart();
145
146
  if(sendByteWithSlaveAck(SLAVE_WRITE)){
147
    if(sendByteWithSlaveAck(0xF4)){
148
      if(sendByteWithSlaveAck(0b00100011)){
149
      }
150
    }
151
  }
152
153
  I2CStop();
154
155
  dig_T1 = (I2CReadBMP280(0x89,1) << 8) | I2CReadBMP280(0x88,1);
156
  dig_T2 = (I2CReadBMP280(0x8B,1) << 8) | I2CReadBMP280(0x8A,1);
157
  dig_T3 = (I2CReadBMP280(0x8D,1) << 8) | I2CReadBMP280(0x8C,1);
158
}
159
160
int32_t calculateTemperature(uint32_t adc_T){
161
  int32_t part1 = (adc_T>>3) - (dig_T1<<1);
162
  int32_t var1  = (part1 * dig_T2) >> 11;
163
  int32_t part2 = (adc_T>>4) - dig_T1;
164
  int32_t var2  = (((part2 * part2) >> 12) * dig_T3 ) >> 14;
165
  int32_t t_fine = var1 + var2;
166
  return (t_fine * 5 + 128) >> 8;
167
}
168
169
void BMP280Management(){
170
  uint32_t T = I2CReadBMP280(0xFA,3) >> 4;
171
  uart0_sendInt(calculateTemperature(T));
172
  uart0_sendStr("\r\n");
173
}
174
175
void user_init()
176
{
177
  wifi_station_set_auto_connect(0); //prevent auto connect wo wifi
178
179
  setGPIOs();
180
  initUART();
181
  I2CSetupBMP280();
182
  os_timer_setfn(&timer,(os_timer_func_t*)BMP280Management,NULL);
183
  os_timer_arm(&timer,42,1);
184
}

: Bearbeitet durch User
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.