SRF02. Wie ohne Timer Messwert-Abfrage steuern
Hallo alle,
ich habe meine Probleme, aktuell mit dem SRF02, meinem Englisch und der Bedienungsanleitung des SRF02. Dort steht nämlich zu Checking for Completion of Ranging:
Zitat:
Zitat von robot-electronics-co-uk
... ... Therefore, if you try to read from the SRF02 ... then you will get 255 (0xFF) whilst ranging ... As soon as the ranging is complete ... its not 255 (0xFF) anymore ... ...
Also programmiere ich unbedarft:
Code:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Messung starten auf Sensor Sadd
// - - - - - - - - - - - - - - -
if (!(i2c_start( Sadd + I2C_WRITE ))) // Slave bereit zum schreiben?
{ // dann Werte schreiben
i2cdmy = i2c_write ( 00 ); // Buffer Startadresse setzen
i2cdmy = i2c_write ( 0x51 ); // Startbefehl SRF02
i2c_stop(); // Zugriff beenden
} //
else // Weiter mit if (!(i2c_start( Sadd + I2C_WRITE )))
{ //
uputs0("\r\n\t## SRF_rdat\r\n");
wms ( 1000);
return 9901; // Return mit Fehlercode
} // Ende if (!(i2c_start( Sadd + I2C_WRITE )))
cli(); tmr1 = 0 ; sei();
while ( 1 )
{ //
i2c_start( Sadd + I2C_WRITE ); // Starte Slave lesen
i2c_write( 0x00 ); // write address = das angepeilte Byte
i2c_stop(); //
i2c_start ( Sadd + I2C_READ ); // Slave bereit zum Lesen?
byte_0 = i2c_readAck(); // Byte 0 lesen... ab laddr = 0
byte_1 = i2c_readNak(); // Byte 1 lesen... NAK
i2c_stop(); //
if ( byte_0 == 0 ) break; // Messung beendet
} // Ende while ( 1 )
cli(); Timer1 = tmr1; sei();
uputs0 ("\r\t####\t Timer1 [tupsi] = "); uputs0u ( Timer1 );
uputs0 ("\r\tbyte_0 = "); uputs0u ( byte_0 );
... um die Messung abzuwarten UND um mir zu Testzwecken den Timerwert für die Messdauer ausgeben zu lassen. Pustekuchen. Je nach Abwandlung des Codes : der Controller hängt sich auf, eine Variation der Abfrage ergibt den WErt 6 - vermutlich die Versionsnummer (so stehts in der Dokumentation) - aber eine Abfrage z.B. auf 255 führt zum Koma. Wenn ich in einer Variante die Abfrage hinkriege, dann steht der Timer auf 0, das heißt bei diesem Timer: Zeitbedarf <50 µs -- bei einem Messabstand von rund 150 cm nicht glaubhaft weil viel zu wenig. Ich kann also den SRF02 nicht ohne Timer betreiben, wie es in der Bedienungsanleitung in Aussicht gestellt wird.
Fragen:
Erkennt jemand meine Fehler?
Hat jemand ne Lösung mit der er den Busystatus - oder den Readystatus - eines SRF02 abfragen kann?
Danke im Voraus für die Hilfe.
Busverfügbarkeit, die zweite
Zitat:
Zitat von
Klebwax
@oberallgeier ... Du mußt den Returnwert von "i2c_start (Sadd + I2C_WRITE)" auswerten ...
Wie oben geschrieben, das geschieht normalerweise. Und jetzt ist (wieder mal) Anlass darüber nachzudenken. Grundlage ist ordnungsgemäßes Programmieren von sicherem und schnellen Code für eine Singel-Master-I²C-Übertragung.
MUSS ich - und wenn ja, WARUM, beim I²C-Master zu Beginn einer I²C-Übertragung den Returnwert auswerten? ICH (also mein Controller) sendet. Als Master. Da hat doch niemand den Bus lahmzulegen und ich müsste die früheren Aktionen ordnungsgemäß abgeschlossen haben. Ausnahme ist Mister Murphy-Law, der schon mal, wie sein Kollege Wackel-Kontakt und andere, dreinpfuschen kann. Aber normalerweise sollte doch der I²C-Bus zu Beginn der Übertragung nicht besetzt sein - ist ja kein Multimaster.
Beim anschließenden Schreiben oder Lesen wird/kann das sinnvoll sein. Aber auch da überlege ich noch.
Nicht dass es mir auf die paar zusätzlichen Maschinenbefehle für das "if" ankäme. Ich finde nur überflüssige Befehle sind ebenso unschönes Programmieren wie falsche.
Liste der Anhänge anzeigen (Anzahl: 3)
Anhang 29147
Anhang 29148
Anhang 29150
Seltsamerweise läst mich das Forum keine cpp Dateien hochladen. Es benennt auch meine i2c.c Datei nach i.c um...
Die Funktion handle_irq() wird aus dem IRQ Handler aufgerufen. Ganz unten in "i.c" bzw. i2c.cpp.
Mein Makefile sieht so aus:
Code:
############################################################
# Projekteinstellungen
############################################################
# MCU
MCU=atmega328p
F_CPU=16000000
LFUSE=0xde
HFUSE=0xd7
EFUSE=0x01
# Die Sources
SOURCES = util.cpp eeprom.cpp i2c.cpp \
menu.cpp main.cpp \
timer1.cpp scheduler.cpp \
rtc.cpp display.cpp adc.cpp relay.cpp storage.cpp r-to-t.cpp \
core.cpp
# temporary deactivated parser.cpp rs232.cpp
# Die Zieldateien
ELF = heat.elf
HEX = heat.hex
MAP = heat.map
############################################################
# Compiler Flags. Muss vermutlich nicht angepasst werden
############################################################
CFLAGS=-g -DF_CPU=$(F_CPU) -Wall -Os --std=c++11 -mcall-prologues
LFLAGS=-lprintf_flt
############################################################
# Die Toolchain. Muss vermutlich nicht angepasst werden
# Die AVRDUDE Zeile muss an den Programmer und
# die Ziel CPU angepasst werden
############################################################
AVRDUDE=avrdude -p m328p -c avrispmkii -P usb
CC=avr-g++
RM=rm -f
OBJCOPY=avr-objcopy
AVRSIZE=avr-size
OBJDUMP=avr-objdump
############################################################
# Ab hier muss nichts mehr angepasst werden
############################################################
# OBJ = Sources mit ersetzter Dateiendung
OBJ=$(SOURCES:%.cpp=%.o)
all: hex
hex: elf
$(OBJCOPY) -R .eeprom -O ihex $(ELF) $(HEX)
elf: $(OBJ)
$(CC) -mmcu=$(MCU) $(CFLAGS) $(LFLAGS) -o $(ELF) -Wl,-Map,$(MAP) -Wl,-u,vfprintf $(OBJ)
%.o: %.cpp
$(CC) -mmcu=$(MCU) $(CFLAGS) -c $<
.phony: flash
flash: hex
$(AVRDUDE) -e -U flash:w:$(HEX)
# Ziele ohne Abhängigkeiten
clean:
$(RM) $(OBJ) $(ELF) $(HEX) $(MAP)
readfuse:
$(AVRDUDE) lfuse:r:-:i -v
writefuse:
$(AVRDUDE) -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
size: $(ELF)
$(AVRSIZE) -B $(ELF)
dump:
$(OBJDUMP) -d -S --demangle $(ELF)
read-eeprom:
$(AVRDUDE) -U eeprom:r:eeprom.bin:i
- - - Aktualisiert - - -
Benutzen tu ich das ganze so:
Code:
#include "project.h"
#include "i2c.h"
extern avr::project sys;
extern avr::i2c twi;
extern bool check_twi(unsigned char& state);
// -----------------------------------------------------
// relay
// -----------------------------------------------------
namespace relay {
unsigned char state = 0;
bool
handle()
{
switch (state)
{
case 0:
g_buffer[0] = ~sys.relay_state;
twi.write_to_slave(avr::relay_address, g_buffer, 1);
state++;
return false;
// check that twi transmitted successful
case 1:
return check_twi(state);
case 2:
state = 0;
return true;
// 100: Error
case 100:
// reenter. try again
state = 0;
break;
default:
break;
}
return true;
}
}; // namespace relay
Code:
// -----------------------------------------------------
// helper functions for statemachines
// -----------------------------------------------------
bool
check_twi(unsigned char& state)
{
if (twi.is_busy())
return false; // not yet done
else if (twi.is_error() != avr::i2c::no_error)
{
state = 100; // we have an error
}
else
{
// everything ok
state++;
}
return true;
}
- - - Aktualisiert - - -
Der Scheduler ruft das ganze dann so auf:
Code:
// -----------------------------------------------------
// scheduler
// -----------------------------------------------------
namespace scheduler {
unsigned char state_i2c = 0;
unsigned char state_spi = 0;
unsigned char state_uart = 0;
void
i2c()
{
bool next_step = true;
switch (state_i2c)
{
case 0:
next_step = rtc::handle();
break;
case 1:
next_step = storage::handle();
break;
case 2:
next_step = menu::handle();
break;
case 3:
next_step = relay::handle();
break;
case 4:
next_step = display::handle();
break;
}
if (next_step) state_i2c += 1;;
if (state_i2c >= 5) state_i2c = 0;
}
}; // namespace scheduler
Der Scheduler selbst hängt wieder im timer1 IRQ.
Code:
/*
Copyright (c) 2014 "Georg Gast <georg@schorsch-tech.de>"
This file is part of thermocontrol.
thermocontrol is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <avr/interrupt.h>
#include "units.h"
#include "timer1.h"
#include "project.h"
#include "scheduler.h"
extern avr::project sys;
// we count 250 cycles until TOV1 (normal mode)
static const int start_tcnt = 65536-250;
namespace avr {
namespace timer1 {
void
init()
{
// start timer with 1 ms interval
// normal mode
TCCR1A = 0;
TCCR1B = (1 << CS11) | (1 << CS10); // prescale factor 64
TIMSK1 |= (1 << TOIE1);
TCNT1 = start_tcnt;
}
} // namespace timer1
} // namespace avr
// -----------------------------------------------------
// IRQ Handler are outside of our namespace
// -----------------------------------------------------
ISR(TIMER1_OVF_vect)
{
/*
Timer 1, Normal mode, runs up and
and then TOV1 irq when 0xffff to 0x0000
(Timer Counter Overflow 1) irq
*/
// to measure time, set let when we are in here
TCNT1 = start_tcnt;
sys.elapsed_time++;
scheduler::i2c();
scheduler::spi();
scheduler::uart();
}
Liste der Anhänge anzeigen (Anzahl: 1)
Irgendwie hab ich Probleme, ein Zitat im Zitat zu erzeugen, hier mein Versuch:
Zitat:
Zunächst habe ich ein Zitat aus der mir vorliegenden Philips-I2C-Spec.: ( Ver. 2.1, Jan. 2000 )
Zitat:
I2C-bus compatible devices must reset their bus logic
on receipt of a START or repeated START condition
such that they all anticipate the sending of a slave
address, even if these START conditions are not
positioned according to the proper format.
Das besagt ja, dass eine START Condition -auch zu Unzeit angewendet- eine Resetierung der Buslogik im konformen I2C-Slave bewirkt.
Über den Busreset habe ich dort mit dem Suchbegriff "reset" nichts Einschlägiges gefunden,
dafür aber bei Freescale folgendes:
Zitat:
The sequence for a Bus Reset is as follows:
• Disable the host MCU IIC controller
• Create a START condition
• Clock SCL for at least nine clocks
• Check for SDA high
• Create a STOP condition
• Enable the host MCU IIC controller
Dieser Vorschlag legt nahe, dass der Reset des Slaves auch beim STOP erfolgt.
Hier wird mehrfach das erzeugen einer Start/Stop condition genannt. Das Problem dabei ist, daß das nicht geht, wenn der Slave SDA auf low hält. Ansonsten klingt der Freescale Vorschlag bekannt. Ich bild mir aber ein, ähnliches auch bei NXP gesehen zu haben.
Nach meiner praktischen Erfahrung gibt es zwei Probleme, die sich mit den gängigen I2C Funktionen (start(), stop(), write(), readACK(),readNAK() ) nicht leicht auflösen lassen:
- Lesen von einem Slave, der das Lesen von mehreren Bytes zulässt, ohne das letzte Byte mit einem NAK (sondern einem ACK) zu quitieren
- Abbruch eines READ durch einen Reset/Debugger/Absturz/... im Master
In beiden Fällen gewinnt der Slave die Kontrolle über SDA und der Master kann, wenn SDA low ist, weder Start noch Stop erzeugen.
Anhang 29167
Deswegen ist
Zitat:
• Create a START condition
etwas fragwürdig. Bisher bin ich mit der Strategie: SDA freigeben, SCL 8 mal takten gut gefahren. Bevor es dann richtig losgeht, kommt pflichtgemäß sowieso ein Start.
Zitat:
Beliebig viele weitere Clocks sind auch nicht statthaft, das kann kollidieren mit einer reservierten Sonderadresse (1111 1111).
Reserviert heißt ja nicht, daß die Adresse nicht auf dem Bus sein darf. Es heißt ja nur, daß kein Slave sie verwenden darf. Und es wird sich daher auch keiner angesprochen fühlen, das passt doch. Es sei denn, du hast eines dieser "Reserved for future purposes" Devices am Bus. Es geht ja hier nicht um einen gültigen I2C Datentransfer sondern um das Auflösen einer Fehlersituation ohne Poweronreset.
Zitat:
Eines ist schon eines zuviel.
Das wirst du nie vermeiden können. Es gibt sicher einige tausend I2C Devices, und daher ebensoviele oder mehr Entwickler, die die Spec für sich interpretiert haben.
Zitat:
Ich gehe diesen Thread durch und suche mir in meiner Datenblattsammlung die entsprechenden Passagen der Atmel-Application-docs, in Datenblättern, Wikis und so. Nachgucken, versuchen zu verstehen oder zu begreifen. Das bringt schon immer wieder so ein kleines "Aha!". Dann springt man - springe ich - auch mal in die THE I2C-BUS SPECIFICATION von Philips, VERSION 2.1 JANUARY 2000. Da gibts dann Dinge wie:
[CODE]Code:
1.3 Version 2.1 - 2000
Version 2.1 of the I2C-bus specification includes the following minor modifications:
· After a repeated START condition in Hs-mode, it is possible to stretch the clock signal SCLH
(see Section 13.2 and Figs 22, 25 and 32).[/CODE)
@oberallgeier
Das wird aus zwei Gründen keine wirkliche Bedeutung haben: Hs-mode wird nicht vorkommen, und die Änderung ist "minor".
Aber zusätzlich zum Lesen hilft (nicht nur hier) manchmal: Machen und Messen. Ich hab mal I2C in SW gemacht, ging zuerst garnicht. Dann in fremder SW geschmökert und neu gemacht. Und dabei erkannt, worauf es ankommt. Wenn man dann die Spec noch mal liest, versteht man sie besser. Heute mit den billigen DSOs oder LAs und den eingebauten Protokolldekodern geht das noch leichter.
MfG Klebwax