Code:
;*************************************************************************
; 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:
Code:
#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 ?!
Lesezeichen