PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : i2c Probleme;



Chattychan
20.06.2007, 15:15
Hallo allerseits,

ich möchte über den i2c Bus die Entfernung des SRF02 zur Wand messen
und diese auf dem KeyLCD ausgeben.

Ich benutze die Lib vom P. Fleury. Hab allerdings auch eigene i2c Methoden
geschrieben. Beide gleicher Fehler.

Mal vorweg: Muss ich auch beim RN-Control zwischen SDA und SCL
4,7k Pullups löten oder nur die internen Pullups aktivieren ? Oder
weder noch ?

Hab erstmal alles einzeln angesteuert und dabei gabs folgende Probleme:
Ich kann mit meinen i2c Methoden nur 2 Zeichen ans LCD senden.Weitere werden einfach nicht angezeigt. Mit der Lib von P. Fleury sogar nur eins.

Beim SRF02 siehts so aus das mit der Lib von P. Fleury immer der gleiche
Wert ausgegeben wird obwohl das i2c_write keinen error ausgibt.
Quasi schreiben geht. Lesen nur Konstante.

Hab in der i2cmaster.S die Ports angepasst (PORTC 0SCL 1SDA)sowie die delay routine auf
80 zyklen erweitert da ich einen 16mhz quarz habe und mit 100khz senden will. Ist doch richtig so oder ?

ich finde einfach nicht den fehler.

Hier mal mein KOMPLETTER Code im nächsten Post:

Danke für eure Hilfe !!

Chattychan
20.06.2007, 15:20
Erst die i2cmaster.S:


;************************************************* ************************
; Title : I2C (Single) Master Implementation
; Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
; based on Atmel Appl. Note AVR300
; File: $Id: i2cmaster.S,v 1.11 2003/10/16 18:16:07 peter Exp $
; Software: AVR-GCC 3.3
; Target: any AVR device
;
; DESCRIPTION
; Basic routines for communicating with I2C slave devices. This
; "single" master implementation is limited to one bus master on the
; I2C bus.
;
; Based on the Atmel Application Note AVR300, corrected and adapted
; to GNU assembler and AVR-GCC C call interface
; Replaced the incorrect quarter period delays found in AVR300 with
; half period delays.
;
; USAGE
; These routines can be called from C, refere to file i2cmaster.h.
; See example test_i2cmaster.c
; Adapt the SCL and SDA port and pin definitions and eventually
; the delay routine to your target !
; Use 4.7k pull-up resistor on the SDA and SCL pin.
;
; NOTES
; The I2C routines can be called either from non-interrupt or
; interrupt routines, not both.
;
;************************************************* ************************

#if (__GNUC__ * 100 + __GNUC_MINOR__) < 303
#error "This library requires AVR-GCC 3.3 or later, update to newer AVR-GCC compiler !"
#endif


#include <avr/io.h>



;***** Adapt these SCA and SCL port and pin definition to your target !!
;
#define SDA 1 // SDA Port D, Pin 4
#define SCL 0 // SCL Port D, Pin 5
#define SDA_PORT PORTC // SDA Port D
#define SCL_PORT PORTC // SCL Port D

;******

;-- map the IO register back into the IO address space
#define SDA_DDR (_SFR_IO_ADDR(SDA_PORT) - 1)
#define SCL_DDR (_SFR_IO_ADDR(SCL_PORT) - 1)
#define SDA_OUT _SFR_IO_ADDR(SDA_PORT)
#define SCL_OUT _SFR_IO_ADDR(SCL_PORT)
#define SDA_IN (_SFR_IO_ADDR(SDA_PORT) - 2)
#define SCL_IN (_SFR_IO_ADDR(SCL_PORT) - 2)


#ifndef __tmp_reg__
#define __tmp_reg__ 0
#endif


.section .text

;************************************************* ************************
; delay half period
; For I2C in normal mode (100kHz), use T/2 > 5us
; For I2C in fast mode (400kHz), use T/2 > 1.3us
;************************************************* ************************
.stabs "",100,0,0,i2c_delay_T2
.stabs "i2cmaster.S",100,0,0,i2c_delay_T2
.func i2c_delay_T2 ; delay 5.0 microsec with 16 Mhz crystal
i2c_delay_T2: ; 4 cycles
rjmp 1f ; 2 "
1: rjmp 2f ; 2 "
2: rjmp 3f ; 2 " 10
3: rjmp 4f ; 2 "
4: rjmp 5f ; 2 "
5: rjmp 6f ; 2 "
6: rjmp 7f ; 2 "
7: rjmp 8f ; 2 " 20
8: rjmp 9f ; 2 "
9: rjmp 10f ; 2 "
10: rjmp 11f ; 2 "
11: rjmp 12f ; 2 "
12: rjmp 13f ; 2 " 30
13: rjmp 14f ; 2 "
14: rjmp 15f ; 2 "
15: rjmp 16f ; 2 "
16: rjmp 17f ; 2 "
17: rjmp 18f ; 2 " 40
18: rjmp 19f ; 2 "
19: rjmp 20f ; 2 "
20: rjmp 21f ; 2 "
21: rjmp 22f ; 2 "
22: rjmp 23f ; 2 " 50
23: rjmp 24f ; 2 "
24: rjmp 25f ; 2 "
25: rjmp 26f ; 2 "
26: rjmp 27f ; 2 "
27: rjmp 28f ; 2 " 60
28: rjmp 29f ; 2 "
29: rjmp 30f ; 2 "
30: rjmp 31f ; 2 "
31: rjmp 32f ; 2 "
32: rjmp 33f ; 2 " 70
33: rjmp 34f ; 2 "
34: rjmp 35f ; 2 "
35: rjmp 36f ; 2 "
36: nop ; 1 "
ret ; 3 "
.endfunc ; total 80 cyles = 5.0 microsec with 16 Mhz crystal


;************************************************* ************************
; Initialization of the I2C bus interface. Need to be called only once
;
; extern void i2c_init(void)
;************************************************* ************************
.global i2c_init
.func i2c_init
i2c_init:
cbi SDA_DDR,SDA ;release SDA
cbi SCL_DDR,SCL ;release SCL
cbi SDA_OUT,SDA
cbi SCL_OUT,SCL
ret
.endfunc


;************************************************* ************************
; Issues a start condition and sends address and transfer direction.
; return 0 = device accessible, 1= failed to access device
;
; extern unsigned char i2c_start(unsigned char addr);
; addr = r24, return = r25(=0):r24
;************************************************* ************************

.global i2c_start
.func i2c_start
i2c_start:
sbi SDA_DDR,SDA ;force SDA low
rcall i2c_delay_T2 ;delay T/2

rcall i2c_write ;write address
ret
.endfunc


;************************************************* ************************
; Issues a repeated start condition and sends address and transfer direction.
; return 0 = device accessible, 1= failed to access device
;
; extern unsigned char i2c_rep_start(unsigned char addr);
; addr = r24, return = r25(=0):r24
;************************************************* ************************

.global i2c_rep_start
.func i2c_rep_start
i2c_rep_start:
sbi SCL_DDR,SCL ;force SCL low
rcall i2c_delay_T2 ;delay T/2
cbi SDA_DDR,SDA ;release SDA
rcall i2c_delay_T2 ;delay T/2
cbi SCL_DDR,SCL ;release SCL
rcall i2c_delay_T2 ;delay T/2
sbi SDA_DDR,SDA ;force SDA low
rcall i2c_delay_T2 ;delay T/2

rcall i2c_write ;write address
ret
.endfunc


;************************************************* ************************
; Issues a start condition and sends address and transfer direction.
; If device is busy, use ack polling to wait until device is ready
;
; extern void i2c_start_wait(unsigned char addr);
; addr = r24
;************************************************* ************************

.global i2c_start_wait
.func i2c_start_wait
i2c_start_wait:
mov __tmp_reg__,r24
i2c_start_wait1:
sbi SDA_DDR,SDA ;force SDA low
rcall i2c_delay_T2 ;delay T/2
mov r24,__tmp_reg__
rcall i2c_write ;write address
tst r24 ;if device not busy -> done
breq i2c_start_wait_done
rcall i2c_stop ;terminate write operation
rjmp i2c_start_wait1 ;device busy, poll ack again
i2c_start_wait_done:
ret
.endfunc


;************************************************* ************************
; Terminates the data transfer and releases the I2C bus
;
; extern void i2c_stop(void)
;************************************************* ************************

.global i2c_stop
.func i2c_stop
i2c_stop:
sbi SCL_DDR,SCL ;force SCL low
sbi SDA_DDR,SDA ;force SDA low
rcall i2c_delay_T2 ;delay T/2
cbi SCL_DDR,SCL ;release SCL
rcall i2c_delay_T2 ;delay T/2
cbi SDA_DDR,SDA ;release SDA
rcall i2c_delay_T2 ;delay T/2
ret
.endfunc


;************************************************* ************************
; Send one byte to I2C device
; return 0 = write successful, 1 = write failed
;
; extern unsigned char i2c_write( unsigned char data );
; data = r24, return = r25(=0):r24
;************************************************* ************************
.global i2c_write
.func i2c_write
i2c_write:
sec ;set carry flag
rol r24 ;shift in carry and out bit one
rjmp i2c_write_first
i2c_write_bit:
lsl r24 ;if transmit register empty
i2c_write_first:
breq i2c_get_ack
sbi SCL_DDR,SCL ;force SCL low
brcc i2c_write_low
nop
cbi SDA_DDR,SDA ;release SDA
rjmp i2c_write_high
i2c_write_low:
sbi SDA_DDR,SDA ;force SDA low
rjmp i2c_write_high
i2c_write_high:
rcall i2c_delay_T2 ;delay T/2
cbi SCL_DDR,SCL ;release SCL
rcall i2c_delay_T2 ;delay T/2
rjmp i2c_write_bit

i2c_get_ack:
sbi SCL_DDR,SCL ;force SCL low
cbi SDA_DDR,SDA ;release SDA
rcall i2c_delay_T2 ;delay T/2
cbi SCL_DDR,SCL ;release SCL
i2c_ack_wait:
sbis SCL_IN,SCL ;wait SCL high (in case wait states are inserted)
rjmp i2c_ack_wait

clr r24 ;return 0
sbic SDA_IN,SDA ;if SDA high -> return 1
ldi r24,1
rcall i2c_delay_T2 ;delay T/2
clr r25
ret
.endfunc



;************************************************* ************************
; read one byte from the I2C device, send ack or nak to device
; (ack=1, send ack, request more data from device
; ack=0, send nak, read is followed by a stop condition)
;
; extern unsigned char i2c_read(unsigned char ack);
; ack = r24, return = r25(=0):r24
; extern unsigned char i2c_readAck(void);
; extern unsigned char i2c_readNak(void);
; return = r25(=0):r24
;************************************************* ************************
.global i2c_readAck
.global i2c_readNak
.global i2c_read
.func i2c_read
i2c_readNak:
clr r24
rjmp i2c_read
i2c_readAck:
ldi r24,0x01
i2c_read:
ldi r23,0x01 ;data = 0x01
i2c_read_bit:
sbi SCL_DDR,SCL ;force SCL low
cbi SDA_DDR,SDA ;release SDA (from previous ACK)
rcall i2c_delay_T2 ;delay T/2

cbi SCL_DDR,SCL ;release SCL
rcall i2c_delay_T2 ;delay T/2

clc ;clear carry flag
sbic SDA_IN,SDA ;if SDA is high
sec ; set carry flag

rol r23 ;store bit
brcc i2c_read_bit ;while receive register not full

i2c_put_ack:
sbi SCL_DDR,SCL ;force SCL low
cpi r24,1
breq i2c_put_ack_low ;if (ack=0)
cbi SDA_DDR,SDA ; release SDA
rjmp i2c_put_ack_high
i2c_put_ack_low: ;else
sbi SDA_DDR,SDA ; force SDA low
i2c_put_ack_high:
rcall i2c_delay_T2 ;delay T/2
cbi SCL_DDR,SCL ;release SCL
i2c_put_ack_wait:
sbis SCL_IN,SCL ;wait SCL high
rjmp i2c_put_ack_wait
rcall i2c_delay_T2 ;delay T/2
mov r24,r23
clr r25
ret
.endfunc



Hier Code nur für KeyLCD und SRF02 wegen bessere Lesbarkeit:



#include "i2cmaster.h"
#include den anderen Kram ;-)

//*********** Meine I2C Methoden ***************
// PORTC0 = SCL ; PORTC1 = SDA

void sda_ein(void){
PORTC |= (1 << PC1);
}

void sda_aus(void){
PORTC &= ~(1 << PC1);
}

void scl_ein(void){
PORTC |= (1 << PC0);
}

void scl_aus(void){
PORTC &= ~(1 << PC0);
}

void i2cstart(){

sda_ein();
asm volatile("NOP");
scl_ein();
asm volatile("NOP");
sda_aus();
asm volatile("NOP");
scl_aus();
asm volatile("NOP");

}

void i2cstop(){

sda_aus();
asm volatile("NOP");
scl_ein();
asm volatile("NOP");
sda_ein();
asm volatile("NOP");

}

char i2c_byte_lesen(unsigned char ack){
//---------------------------------------------------------------------------------
// Liest ein Byte vom i2cbus und prueft das ACK

unsigned char i,wert=0;
DDRC &= ~(1 << DDC1); //DATA Schreibrechte an Empfaenger uebergeben
//sda_ein(); //Aktiviert Pullup da Port als Eingang
for (i=0x80;i>0;i/=2) //shift bit for masking
{ scl_ein(); //bustakt

if (PINC & (1<<PINC1))
{ UART_transmit_string("1");
wert=(wert | i); //read bit
}
else UART_transmit_string("0");

scl_aus();
}
DDRC |= (1 << DDC1); //DATA Schreibrechte an µC uebergeben
if(ack) sda_aus();
else sda_ein();
asm volatile("NOP");
scl_ein(); //clk #9 for ack
asm volatile("NOP"); //Pulsbreite ca. 5 us
asm volatile("NOP");
asm volatile("NOP");
scl_aus();

DDRC &= ~(1 << DDC1); //DATA Schreibrechte an Empfaenger uebergeben
return wert;
}

char i2c_byte_schreiben(unsigned char wert){
//----------------------------------------------------------------------------------
// Schreibt ein Byte auf den Sensirion 2-Wirebus und prüft das ACK

unsigned char i,error=0;
DDRC |= (1 << DDC1); //DATA Schreibrechte an µC uebergeben
for (i=0x80;i>0;i/=2) //shift bit for masking
{ if (i & wert) {
sda_ein(); //masking value with i , write to i2cbus
UART_transmit_string("1");
}
else {
sda_aus();
UART_transmit_string("0");
}
scl_ein(); //Takt fuer i2cBUS
asm volatile("NOP"); //Pulsbreite ca. 5 us
asm volatile("NOP");
asm volatile("NOP");
scl_aus();
}

DDRC &= ~(1 << DDC1); //DATA Schreibrechte an Empfaenger uebergeben
//Hier eventuell auf ACK warten weil KeyLcd zu langsam ? waitms 100 oder so
//sda_ein(); //Aktiviert Pullup da Port als Eingang
scl_ein(); //clk #9 for ack

if(PINC & (1<<PINC1)) error=1; //prueft ACK (PINC1 wird vom Empfaenger auf Low gezogen)
else error=0;

scl_aus();
//sda_aus(); //Deaktiviert Pullup
return error; //error=1 im Falle von keinem Acknowledge
}

int main (void)
{
unsigned char a[10];
unsigned char b[10];
unsigned char c[10];
unsigned char d,e;
unsigned int taste = 0;
unsigned int sum = 0;

value humi_val,temp_val;
unsigned int tempo;
unsigned int humi;
unsigned int cm;
float tau_punkt;
unsigned char error,checksum,hib,lob;
unsigned long i;
error=2;


DDRC = 0xFF;
hib=0;
DDRC &= ~(1 << DDC0); // PIN als EINGANG
DDRC &= ~(1 << DDC1); // PIN als EINGANG
scl_ein(); // Interne Pullups aktivieren
sda_ein(); // Interne Pullups aktivieren




UART_init(); // UART-Init Funktion ausfhren
i2c_init(); //P. Fleury i2cmaster init




/* Hier USS Code mit Lib vom P. Fleury
i2c_start_wait(0xE0);
error=i2c_write(0x00);
error=i2c_write(0x51);
i2c_stop();

for (i=0;i<100000000;i++); //Auf Messungende warten ca 65ms

i2c_start_wait(0xE0);
error=i2c_write(0x02);
i2c_rep_start(0xE1);
hib=i2c_readAck();
lob=i2c_readNak();
i2c_stop();

cm= (hib*256)+lob;

itoa(cm,a,10);
UART_transmit_string(a);

if(error==1) UART_transmit_string("Error beim schreiben");
else UART_transmit_string("Kein Error beim schreiben");
/*


// Hier Code SRF02 mit meinen Methoden
/*
i2cstart();
i2c_byte_schreiben(0xE0); //Standard Adresse vom USS
UART_transmit_string(" ");
i2c_byte_schreiben(0x00); //Register 0
UART_transmit_string(" ");
i2c_byte_schreiben(0x51); //Messbefehl in Zentimeter
UART_transmit_string(" ");
i2cstop();

i2cstart();
i2c_byte_schreiben(0xE0); //Standard Adresse vom USS
UART_transmit_string(" ");
i2c_byte_schreiben(0x02); //Register 2
UART_transmit_string(" ");
i2cstop();

i2cstart();
i2c_byte_schreiben(0xE1); //Slaveadresse + 1 da lesen wollen
UART_transmit_string(" ");
hib=i2c_byte_lesen(ACK);
UART_transmit_string(" ");
lob=i2c_byte_lesen(noACK);
UART_transmit_string(" ");
i2cstop();
*/


//Hier Code KEYLCD mit Lib P. Fleury
//So kann ich nur ein Zeichen ausgeben und dann error=1
i2c_start_wait(0x40);
error=i2c_write(0x68);
error=i2c_write(0x61); //AB Hier error
error=i2c_write(0x6C);
error=i2c_write(0x6C);
error=i2c_write(0x6F);
i2c_stop();

if(error==1) UART_transmit_string("Error beim schreiben");
else UART_transmit_string("Kein Error beim schreiben");



/* Hier nur KEYLCD Code mit meinen Methoden
So kann ich 2 Zeichen nacheinander korrekt ausgeben, dritte dann fehler.

i2cstart();
i2c_byte_schreiben(ADD);

error=i2c_byte_schreiben(0x5A);
if(error==1) UART_transmit_string("Error1");

error=i2c_byte_schreiben(0x4A);
if(error==1) UART_transmit_string("Error2");

error=i2c_byte_schreiben(0x57);
if(error==1) UART_transmit_string("Error3");

i2cstop();
*/


}


Ist doch voll komisch oder ?!

Hey vielen Dank für eure Hilfe. Ehrlich !! Ich kann einfach keinen
Fehler finden. Immerhin funktioniert die Kommunikation ja halbwegs.
Daher ist es im Ansatz richtig.
Makefile ist OK. Kompilieren klappt auch ohne Error. Ein paar Warnungen , aber die sind egal. Ist ja nur zum experimentieren.

Viele Grüsse
Thomas

Chattychan
20.06.2007, 16:09
Hallo,

hab noch was ausprobiert (RN-KEYLCD):

i2c_start_wait(0x40);
error=i2c_write(0x68);
error=i2c_write(0x61);
error=i2c_write(0x68);
error=i2c_write(0x61);
i2c_stop();

Klappt NICHT !! bzw das erste immer richtig der rest falsch
also h . . . oder h . . . .

Wenn ich jedoch es so umschreibe:

i2c_start_wait(0x40);
error=i2c_write(0x68);
i2c_stop();
i2c_start_wait(0x40);
error=i2c_write(0x61);
i2c_stop();
i2c_start_wait(0x40);
error=i2c_write(0x68);
i2c_stop();
i2c_start_wait(0x40);
error=i2c_write(0x61);
i2c_stop();

Erscheint auf dem LCD wie gewünscht: haha

Ist doch voll komisch.

Freue mich über jede Hilfe !!

Viele Grüsse
Thomas

Tenorm
07.02.2008, 12:12
Ich hab auch´n Problem mit dem srf02, die Programme sind zwar Logik- und Syntaxfehlerfrei, hängen sich aber beim Befehl I2C_Start(); auf => Fehler liegt beim Sensor. Ich vermute mal das es `n Problem mit dem Hochfahren des Sensors gibt, dass er erst nen BEfehl braucht, um in den I2C_Modus zu wechseln.
Wenn ich mehr Weiss, schreib ich´s dir :cheesy:

Gruß icon_cheesygrin.gif

Tenorm
08.02.2008, 10:00
Nochmal zu oben; Du musst für den Srf02 einen Pull-up_widerstand haben(ich glaub1,8kO, schau einfach in der Anleitung nach, steht irgendwo drin...), ob du die internen Widerstände abschaltest und welche hinlötest oder einfach die internen aktivierst, ist eigendlich egal...

DerMaddin
02.03.2008, 18:03
Hi, ich hab auch ein Problem mit dem SRF02 am I2C Bus:
Sobald ich die SDA Leitung wie in der Anleitung beschrieben an Pin2 des Erweiterungsboards klemme, leuchtet die entsprechende Diode halb so hell wie die anderen. Das Programm läuft endlos in der Funktion I2C_Start() an der Zeile
while(!(TWCR & (1<<TWINT)));

Mein Controllerboard hat einen ATMega32 (ich glaub RN-Control) und ich benutze die I2C Bibliothek vom Peter Fleury. Ich hab keine Widerstäne gelötet sondern den Aufbau genauso wie im Datenblatt vom SRF02 (Seite 9).

Was mach ich falsch?

Danke!!!

Tenorm
03.03.2008, 18:39
Bei I2C braucht man generell irgendwo pullups.....

Tenorm
03.03.2008, 18:39
Beim srf02 1.8kO

DerMaddin
03.03.2008, 21:33
Naja, laut BoardSpec sind ja die internen Pullups immer an, oder nicht? Und anscheinend sind die 10k schwer ;)
...
vlt hab ich wirklich den Sensor gebraten... :(
Wenn es das Budget zuläßt, kommt ein neuer her... in der Zwischenzeit hab ich auch noch andre Baustellen
...
trotzdem danke *schnief

sechsrad
08.03.2008, 08:43
Ich hab auch´n Problem mit dem srf02, die Programme sind zwar Logik- und Syntaxfehlerfrei, hängen sich aber beim Befehl I2C_Start(); auf => Fehler liegt beim Sensor. Ich vermute mal das es `n Problem mit dem Hochfahren des Sensors gibt, dass er erst nen BEfehl braucht, um in den I2C_Modus zu wechseln.


das ist schmarren.
ich progge den srf02 mit bascom einmal im i2c-modus und einmal im seriellen modus. funktioniert super !!! da gibt es keine hänger...lol....

mfg

sechsrad
08.03.2008, 08:45
nimm 4,7k pullups . dann klappt es, nur wenn dein programm funktioniert.

Frank
10.08.2012, 08:17
Anmerken muss man auch das die Lib von Peter Fleury kein I2C Clockstretching unterstützt, daher hat diese mit vielen Modulen Probleme! Am besten eigene I2C Routinen schreiben. Oder Bascom nutzen ;-)