PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : I2C Kommunikation zwischen Nucleo STM32F103RBT6 und Sensor Honeywell HIH9120



Klacknack
02.06.2015, 05:30
Hallo zusammen,

ich versuche ein Temperatur- und Feuchtigkeitssensor mit dem Entwicklungsboard Nucleo STM32 zu verbinden. Dieser Sensor lässt sich mit I2C ansprechen und die Adresse ist laut Datenblatt 0x27. Nun arbeite ich mit dem mbed Compiler und habe folgendes Programm zur Kommunikation.

Mein Programmcode sieht wie folgt aus:
#include "mbed.h"
I2C i2c(PB_11, PB_10);

const int addr = 0x27;

int main() {
char cmd[2];
while (1) {
cmd[0] = 0x01;
cmd[1] = 0x00;
i2c.write(addr, cmd, 1);

wait_ms(10);
}
}

Mit einem Logic Analyzer nehme ich die Kommunikation auf um mein Problem zu beheben und stelle folgendes fest:
- Bei einer eingegebenen Adresse addr=0x26 spricht der Mikrocontroller eine Adresse 0x26 an
- Bei einer eingegebenen Adresse addr=0x27 spricht der Mikrocontroller eine Adresse 0x28 an
- Bei einer eingegebenen Adresse addr=0x28 spricht der Mikrocontroller eine Adresse 0x28 an

Das ist in meinen Augen extrem merkwürdig und ich kann mir da nicht weiterhelfen. Hat jemand Erfahrung mit dem Entwicklungsboard und kann mir bei dem Fehler auf die Sprünge helfen?

Danke für Hilfestellungen im voraus!

Angehangen das Screenshot des Logic Analyzers bei addr=0x27

30223

Mxt
02.06.2015, 07:17
Hallo,

eigentlich hat eine I2C-Adresse nur 7 Bit, das 8. zeigt an, ob gelesen oder geschrieben wird. Die mbed Implementierungen von read und write mit mehreren Parametern erwarten eine 8-Bit Adresse. Da steht ja auch in der Doku

The mbed API uses 8 bit addresses, so make sure to take that 7 bit address and left shift it by 1 before passing it.
bei read

address 8-bit I2C slave address [ addr | 1 ]
und write

address 8-bit I2C slave address [ addr | 0 ]

Wenn du rohe 8-Bit Werte schreiben willst, kannst du die write Funktion mit einem Parameter verwenden und die Übertragung Byte für Byte in einer for-Schleife machen.

Klacknack
08.06.2015, 03:53
Hallo,

danke erstmal für die Antwort und entschuldigung dass ich eine Woche darauf nicht reagiert habe, war aber durch andere Baustellen leider völlig eingenommen. Also deine Antwort erklärt das Problem sehr gut. Nun habe ich mir gedacht ich mache es ganz simpel, indem erstmal beginne den Sensor lediglich einen write befehl zu geben. Der Code ist recht simpel und wie folgt:

#include"mbed.h"

I2C i2c(PB_11, PB_10); //sda, scl
const int addr = 0x4E; //Adresse 0x27 um ein Bit nach links geschoben
double result=0;

int main() {
while(1){
i2c.start();
i2c.write(addr);
i2c.stop();
}
}

Das Ergebnis mit dem Logic Analyzer gemessen ist angehangen. Meiner Meinung nach sieht die I2C Kommunikation des Mikrocontrollers richtig aus, jedoch bleibt das Acknowledge des Sensors aus. Ich habe die Beschaltung gefühlt 100 mal kontrolliert und ich kann mir nicht vorstellen dass der Sensor kaputt ist. Kann mir jemand mit Erfahrung in der Kommunikation weiterhelfen und erkennt da einen Fehler?

Danke

fredred
08.06.2015, 12:14
Hallo Klacknack,

kenne Entwicklungsboard Nucleo STM32 nicht. Somit nur eine allgemeine Antwort.

Bin ein begeisterter Fan von I²C Bus. Die häufigsten Fehler liegen beim Bustakt und der H-Pegel der Leidungen.
Mit einem Takt bis 100kHz sollten ziemlich alle Sensoren funktionieren. Wichtig sind die Abschlusswiderstände von SDA und SCL zu den Sensoren. Meine Erfahrungen 4,7 k auf Vcc sind bis ca. 20 Meter Leitungslänge OK.
Leider programmiere ich „nur“ noch mit BASCOM. Hatte schon mal ein Programm eingestellt wie viele unterschiedliche I²C –Teilnehmer am Bus angeschossen und geprüft werden können.
Wie gesagt, sehr oft liegt es am Aufbau der Hardware, wenn solche Fehler auftreten.
Softwaremäßig ist es wie bei allen Bussysteme nötig, hat sich der Teilnehmer gemeldet und die Informationen geschickt den Bus wieder frei zu geben[Start ansprechen, lesen oder schreiben Stop].

Mit freundlichen Grüßen
fredred

Wsk8
08.06.2015, 14:44
Bau dir doch mal einen I2C Scanner (Arduino Beispiel):





void loop()
{
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");
nDevices++;
}
else if (error==4)
{
Serial.print("Unknow error at address 0x");
if (address<16)
Serial.print("0");
Serial.println(address,HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
delay(5000); // wait 5 seconds for next scan
}




mfg

fredred
08.06.2015, 15:59
Hallo Wsk8 (https://www.roboternetz.de/community/threads/members/36359-Wsk8),

Ein Scanner wie du schreibst ist okay. Mein Hinweis sollte nur Vermitteln, ist die Hardware „Dumm“, kann eine Software, nicht den Fehler eindeutig erkennen. Die vielen guten Softwarelösungen gehen ja immer davon aus, es ist ein Fehler im Programm. Oft sind es aber nur kleine Fehler, wenn ein neues Modul nicht korrekt angeschlossen wird.

Gruß fredred

Klacknack
10.06.2015, 21:16
Danke für beide Vorschläge.

Zur Hardware: Ich habe auch neben 100kHz andere Frequenzen ausgetestet, jedoch stelle ich da kein Unterschied fest. Datenblatt des Sensors sagt aus, dass alles zwischen 100kHz und 400kHz in Ordnung ist. Die Pull-Up Widerstände betragen 1,8kOhm, nach Datenblatt sollten es 2,2kOhm sein. Da hab ich auch schon variiert. Jedoch wiederum kein Unterschied. Die Entstör-Kondensatoren betragen den verlangten Wert und ich kann mir wirklich nicht vorstellen, dass es daran liegt. Der Aufbau ist auf einem Breadboard und ich überlege momentan ob ich den Testaufbau auf eine Lochrasterplatine löten soll. Mir fällt kein präziser Grund ein, denn beim Kontrollieren mit Multimeter oder Logic Analyzer waren alle Pegel auf dem Breadboard richtig, nur aus Erfahrung weiß ich, dass die Dinger nicht immer 100% zuverlässig sind. Als letztes fällt mir noch ein, einen anderen Sensor auszutesten. Vllt habe ich diesen beim Testen in irgendeiner Weise zerstört.

Dann werde ich mich als nächstes mal mit der Software befassen und mir den I2C Scanner ansehen. Zurzeit programmiere ich das Nucleo Board online mit dem mbed Compiler, weil ich eine recht kleine Aufgabe zu implementieren habe und ich keine Lust hatte mir eine IDE auf dem PC einzurichten. Vielleicht sollte ich dann doch dazu übergehen.

Ich halte euch auf dem laufenden was die Ergebnisse der nächsten Tage ergeben.

Danke, Gruß!

Mxt
11.06.2015, 10:02
An sich funktioniert I2C mit mbed ziemlich gut. Ich habe zwar nicht genau den Nucleo aus dem Titel, sondern einen F401, aber da sollte kein großer Unterschied sein.

Mit der normalen 400 kHz Einstellung habe ich verschiedene Sensoren und auch EEPROMs an verschiedenen mbed im Einsatz.

fredred
11.06.2015, 16:51
Hallo Klacknack,

wie geschrieben, ist die Hardware (Busleitungen okay), kannst Du deine Sensoren kaum zerschießen.
Mein Vorschlag ist.
Teste den I²C erst mal mit einem Digital- IC z B. PCF 8574. Der muss ja nicht erst Messdaten aufbereiten um Status „ich bin fertig“ zu generieren damit es weiter geht. Wenn sich IC meldet weist du erst mal, der Bus funktioniert. Rest ist die Softwareanpassung für die unterschiedlichen Teilnehmer.
Viele Sensoren sind meist für ein bestimmtes System entwickelt wurden. Somit sind Laufzeiten und Flanken zu beachten.
Das I²C Prodokoll ist zwar kaum noch in der Industrie im Einsatz, aber für Amateure wie ich es bin, leicht zu handhaben.
Beispiel: Meine Testplatine hat 40 Sockel für Test der I²C IC’s.
Bei voller Bestückung..


16x Digital IC 64 Ports zum Schreiben und 64 zum Lesen ein IC ist Master für LCD-Anzeige 20x4 eingestellt.(Hat keinen I²C Anschluss). Also Modus abbilden
8x Analog IC gleich 32 Ports
8x Digital Poti 16 mal analog Out
4x EEprom (256 k) für Datenlogger.


Werden alle Ereignisse mit ATmega 644P (Prozessortakt 16 MHz) generiert, ist die Laufzeit < 1 Sekunde.
Muss sagen für viele Anwendungen ist dies ausreichend.
Natürlich sind in der Software die Auswertungen, Anzeigen und Berechnungen für Laufzeiten zu beachten.

Gruß
fredred

Mxt
12.06.2015, 08:03
Hallo,

ich habe in meiner Sammlung ein kleines Beispiel für einen 24LC256 EEPROM, ist allerdings für den LPC1768 konfiguriert. Man muss die Pin Bezeichnungen für einen Nucleo anpassen, auch hat der nur eine LED. Aber es ist das schöne an mbed, dass die Programme mit so wenigen Änderungen auf Cortex-M verschiedener Hersteller laufen.

Headerdatei


#ifndef I2C_EEPROM_H
#define I2C_EEPROM_H

#include <vector>

//
// Klasse für 24LC256 usw.
//
class I2C_Memory {
private:
int m_Address;
I2C m_i2c;
bool m_Ok;

//
// Übertragungsfehler merken
//
void DoWrite(int value) {
if (!m_i2c.write(value))
m_Ok = false;
}

public:

//
// Konstruktor
//
I2C_Memory(int address, PinName sda, PinName scl) : m_Address(address), m_i2c(sda, scl), m_Ok(true)
{}

//
// Schreiben
//
bool Write(unsigned short start_address, const std::vector<uint8_t>& data) {
m_Ok = true;

char hb = (start_address & 0xff00) >> 8;
char lb = start_address & 0xff;

m_i2c.start();

DoWrite(m_Address);
DoWrite(hb);
DoWrite(lb);

for(unsigned int i = 0; i < data.size(); i++) {
DoWrite(data[i]);
}

m_i2c.stop();

return m_Ok;
}

//
// Lesen
//
bool Read(unsigned int start_address, unsigned int count, std::vector<uint8_t>& data) {
m_Ok = true;
data.clear();

char hb = (start_address & 0xff00) >> 8;
char lb = start_address & 0xff;

m_i2c.start();

DoWrite(m_Address);
DoWrite(hb);
DoWrite(lb);

m_i2c.start();

DoWrite(m_Address + 1);

for(unsigned int i = 0; i < count - 1; i++) {
data.push_back(m_i2c.read(1));
}
if ( count > 0 ) {
data.push_back(m_i2c.read(0)); // Letztes Lesen mit NACK quittieren
}

m_i2c.stop();

return m_Ok;
}
};

#endif


Beispielprogramm


#include "mbed.h"
#include "I2C_EEPROM.h"

I2C i2c(p9, p10);

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);

int main() {
I2C_Memory memory(0xa0, p9, p10);

while(1) {
led1 = 0;
led2 = 0;
led3 = 0;

wait(1.0);

std::vector<uint8_t> data;

if ( memory.Read(0, 256, data) ) {
led1 = 1;

for(int i = 0; i < data.size(); i++) {
printf("%0.2x ", data[i]);
}
printf("\r\n\r\n");
}
else {
led2 = 1;
}

wait(5.0);
}
}

Klacknack
15.06.2015, 00:22
Hallo,

also nachdem ich mich nochmal sehr intensiv daran gesetzt habe und wieder keine Kommunikation zustande gekommen ist. Habe ich mir meine Schaltung und Bauteilliste zum 100sten mal angesehen und den Fehler gefunden. Und der ist wirklich peinlich! Ich habe in der Bestellnummer die letzte Zahl falsch eingegeben und den Sensor anstatt mit I2C Kommunikation einen mit SPI-Kommunikation bestellt. Die Schaltung ist geändert und die Software zu SPI Kommunikation geschrieben und es funktioniert einwandfrei.

Weiß noch nicht ob ich mich über den gefundenen Fehler freuen oder ärgern soll :D Jedenfalls bedanke ich mich bei allen für die mithilfe. Ich denke die I2C Kommunikation meines µC hat funktioniert und weswegen die Antwort des Sensors ausblieb weiß ich nun.

Gruß und Danke