- LiFePO4 Speicher Test         
Ergebnis 1 bis 10 von 10

Thema: Porterweiterung für ASURO über zweiten ATmega8 als I2C-Slave

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    23.05.2006
    Beiträge
    62

    Porterweiterung für ASURO über zweiten ATmega8 als I2C-Slave

    Anzeige

    LiFePo4 Akku selber bauen - Video
    In der letzten Zeit wird hier viel über den I2C-Bus und seine Einsatzmöglichkeiten gesprochen. Ich möchte euch als Beispiel eine Erweiterung mit einem als I2C-Slave programmierten ATmega8 vorstellen. Damit erweitert man den ASURO um 4 ADC-Kanäle und 16 digitale I/O's. Der Slave arbeitet mit dem Hardware-I2C-Interface (SDA an Pin27, SCL an Pin28 ).

    Wie funktioniert das?
    Als erstes muss man sich eine Art Befehlssatz ausdenken die der Master (ASURO) dem Slave schickt und die dieser dann ausführt.
    Die header-Datei twi_register.h mit dem Befehlssatz:

    Code:
    #define sbi(port,bit) port|=(1<<(bit))
    #define cbi(port,bit) port&=(~(1<<(bit)))
    #define BIT(n) (1<<(n))
    
    #define MAX_REG		   	255
    #define FALSE 0
    #define TRUE 1
    
    /* ic2-slave-adresse */
    
    #define TWI_ADR 0x80
    
    /* set-befehle */
    
    #define TWI_SET_DDRB 1	// definiere über das ddr eingänge und ausgänge von portb
    #define TWI_SET_PORTB 2	// setze pull up widerstände an portb
    #define TWI_SET_PINB	3	// setze ein einzelnes bit an portb
    #define TWI_RES_PINB	4	// rücksetze ein einzelnes bit an portb
    
    #define TWI_SET_DDRC 5	// wie portb
    #define TWI_SET_PORTC 6
    #define TWI_SET_PINC	7
    #define TWI_RES_PINC	8
    
    #define TWI_SET_DDRD	9	// wie portb
    #define TWI_SET_PORTD 10
    #define TWI_SET_PIND 11
    #define TWI_RES_PIND 12
    
    /* get-befehle */
    
    #define TWI_GET_PINB 	13	// lese das pinregister von portb
    #define TWI_GET_PINC	14
    #define TWI_GET_PIND	15
    
    
    #define TWI_GET_ADC0	16	// lese adc0
    #define TWI_GET_ADC1	17
    #define TWI_GET_ADC2	18
    #define TWI_GET_ADC3	19
    Da gibt es Set- Res- und Get-Befehle. Set-Befehle zum setzen von Bits in den Datenrichtungs-Registern, von Ports oder einzelnen Port-Bits und natürlich auch zum zurücksetzen einzelner Bits (Res-Befehl). Mit den Get-Befehlen kann man den Zustand der Pinregister (digitale Eingänge) abfragen oder einen ADC-Kanal einlesen (2x8Bit) den man vorher mit einem Set-Befehl selektiert hat.
    Ein Befehl besteht immer aus zwei Byte, dem Befehl und einem Parameter. Wenn ein Befehl keinen Parameter benötigt, z.B. TWI_GET_ADC0, müssen aber trotzdem zwei Byte gesendet werden. Dabei ist es egal welchen Wert der Parameter hat, da er ja nicht ausgewertet wird.

    Das ist die I2C-Interrupt-Routine I2C_Slave.c für den ATmega8-Slave:
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <compat/twi.h>
    #include "twi_register.h"
    
    volatile unsigned char reg = MAX_REG;
    volatile unsigned char twi_comm = FALSE;
    
    void twi_init( unsigned char adr )
    {
        TWAR = adr;	
        TWCR = BIT(TWINT) | BIT(TWEN) | BIT(TWEA) | BIT(TWIE);
    	sei();
    }
    
    SIGNAL(SIG_2WIRE_SERIAL)
    {
        switch (TWSR) {
    
    /* SLAVE RECEIVER */
    
    	case TW_SR_DATA_ACK:
    	case TW_SR_DATA_NACK:
    	
    			if( reg < MAX_REG ) 
    			{
    				switch( reg )
    				{
    					case TWI_SET_DDRB:	DDRB  = TWDR;
    					break;
    					case TWI_SET_PORTB:	PORTB = TWDR;
    					break;
    					case TWI_SET_PINB:	sbi(PORTB, TWDR);
    					break;
    					case TWI_RES_PINB:	cbi(PORTB, TWDR);
    					break;
    					case TWI_SET_DDRC:	DDRC  = TWDR;
    					break;
    					case TWI_SET_PORTC:	PORTC = TWDR;
    					break;
    					case TWI_SET_PINC:	sbi(PORTC, TWDR);
    					break;
    					case TWI_RES_PINC:	cbi(PORTC, TWDR);
    					break;
    					case TWI_SET_DDRD:	DDRD  = TWDR;
    					break;
    					case TWI_SET_PORTD:	PORTD = TWDR;
    					break;
    					case TWI_SET_PIND:	sbi(PORTD, TWDR);
    					break;
    					case TWI_RES_PIND:	cbi(PORTD, TWDR);
    					break;
    					case TWI_GET_ADC0: 	ADMUX = BIT(REFS0) + 0x00;
    	    								ADCSRA |= BIT(ADSC);
    					break;
    					case TWI_GET_ADC1: 	ADMUX = BIT(REFS0) + 0x01;
    	    								ADCSRA |= BIT(ADSC);
    					break;
    					case TWI_GET_ADC2: 	ADMUX = BIT(REFS0) + 0x02;
    	    								ADCSRA |= BIT(ADSC);
    					break;
    					case TWI_GET_ADC3: 	ADMUX = BIT(REFS0) + 0x03;
    	    								ADCSRA |= BIT(ADSC);
    					break;
    				}
    
    				reg = MAX_REG;
    			} 
    			else
    			{
    				reg = TWDR;
    				twi_comm = reg;
    			}
    
    			TWCR = BIT(TWINT) | BIT(TWEN) | BIT(TWEA) | BIT(TWIE);
    	break;
    
    /* SLAVE TRANSMITTER */
    
    	case TW_ST_SLA_ACK:		// twi-adresse und read empfangen
    
    				switch( twi_comm )
    				{
    					case TWI_GET_PINB: TWDR = PINB;
    					break;
    					case TWI_GET_PINC: TWDR = PINC;
    					break;
    					case TWI_GET_PIND: TWDR = PIND;
    					break;
    					case TWI_GET_ADC0: 	
    					case TWI_GET_ADC1: 	
    					case TWI_GET_ADC2: 	
    					case TWI_GET_ADC3: 	
    										while (!(ADCSRA & BIT(ADIF)))
    										;
    										TWDR = ADCL;
    					break;
    				}
    
    			TWCR = BIT(TWINT) | BIT(TWEN) | BIT(TWEA) | BIT(TWIE); // transmit first byte
    	break;
    
    	case TW_ST_DATA_ACK:	// erstes daten-byte des adc übertragen und ack empfangen
    							// sende jetzt das zweite daten-byte des adc
    
    				switch( twi_comm )
    				{
    					case TWI_GET_ADC0: 
    					case TWI_GET_ADC1: 
    					case TWI_GET_ADC2: 
    					case TWI_GET_ADC3: 
    										TWDR = ADCH;
    				}
    
    			 TWCR = BIT(TWINT) | BIT(TWEN) | BIT(TWEA) | BIT(TWIE); // sende zweites byte
    	break;
    
    	default: TWCR = BIT(TWINT) | BIT(TWEN) | BIT(TWEA) | BIT(TWIE);
    	break;
    
        }
    
    }
    Leider kommt das mit den Tabs beim kopieren nicht so 100%tig hin.

    Das ist die Initialisierungsfunktion M8_I2C_Slave.c (main):
    Code:
    /*
    	autor:		Peter Wilbert
    	version:	1.0
    	datum:		27.01.07
    
    	atmega8 als i2c-slave erweitert den asuro
    	um 4 adc's und 16 digitale i/o's
    */
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include "twi_register.h"
    
    extern void twi_init( unsigned char );
    
    /*  *** hauptprogramm ***
    
    	initialisiert den i2c-bus und verweilt dann in einer 
    	endlosschleife. das eigentliche programm ist die twi-
    	interruptroutine in der die eingehenden slave-befehle 
    	direkt ausgeführt werden 
    */
    
    int main(void)
    {
    /*
    	adc init
    */
    	ADCSRA = BIT(ADEN) + BIT(ADPS2) + BIT(ADPS1);	// init adc prescale 64
    	ADMUX  = BIT(REFS0) + 0x00;						// adc einmal anstossen
    	ADCSRA |= BIT(ADSC);							// start adc
    	while (!(ADCSRA & BIT(ADIF)))					// warte bis ok
    	;
    /*
    	twi initialisieren
    */
    	twi_init(TWI_ADR);						
    
    /*
    	for ever
    */
    	for(;;)
    	;
    			
    	return(0);
    }
    Mit dieser Erweiterung sollte man erst einmal genügend I/O-Pins und ADC-Kanäle für Erweiterungen zur Verfügung haben! Den Befehlssatz kann man ja für andere Funktionen erweitern.

    Peter (Ronny10)

  2. #2
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    27.11.2006
    Ort
    Hamburg
    Alter
    35
    Beiträge
    184
    Wow, danke für den Post!!!!
    Gruß Raid

    Der ^^ Mann

  3. #3
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    27.11.2006
    Ort
    Hamburg
    Alter
    35
    Beiträge
    184
    Hallo ROnny,

    Was ist TWSR und TWDR?
    Gruß Raid

    Der ^^ Mann

  4. #4
    Benutzer Stammmitglied
    Registriert seit
    23.05.2006
    Beiträge
    62
    Dazu solltest du dir unbedingt die Doku (PDF) zum ATmega8 von ATMEL durchlesen, da das TWI noch einige andere Register hat die man auch kennen muss!

    TWDR = TWI Data Register
    TWSR = TWI Status Register
    ...

    Peter (Ronny10)

  5. #5
    Benutzer Stammmitglied
    Registriert seit
    23.05.2006
    Beiträge
    62
    Ein Beispiel, wie die Master-Funktionen aussehen:

    Code:
    #include <avr/io.h>
    #include "i2cmaster.h"
    #include "twi_register.h"
    
    /***************************************
    	sendet einen befehl plus parameter
    	zum m8-slave
    ****************************************/
    void twi_send( unsigned char befehl, unsigned char parameter )
    {
    	i2c_start_wait(TWI_ADR+I2C_WRITE);			// verbindung aufbauen
    	i2c_write( befehl );						// befehl für den slave
    	i2c_write( parameter );						// parameter des befehls
    	i2c_stop();									// verbindung beenden
    }
    
    /**********************************************
    	liest ein port-pinregister vom m8-slave
    ***********************************************/
    unsigned int twi_get( unsigned char befehl )
    {
    unsigned char c;
    
    	i2c_start_wait(TWI_ADR+I2C_WRITE);			// verbindung aufbauen
    	i2c_write( befehl );						// befehl für den slave
    	i2c_write( 0 );						        // befehl benötigt keinen parameter
    	i2c_rep_start(TWI_ADR+I2C_READ);			// wiederhole start + read
    	c = i2c_readNak();							// read byte
    	i2c_stop();									// verbindung beenden
    	return(c);
    }
    
    /**********************************************
    	liest einen adc-kanal (2Byte) vom m8-slave
    ***********************************************/
    unsigned int twi_get_adc( unsigned char befehl )
    {
    unsigned int i,j;
    
    	i2c_start_wait(TWI_ADR+I2C_WRITE);			// verbindung aufbauen
    	i2c_write( befehl );						// befehl für den slave
    	i2c_write( 0 );						        // befehl benötigt keinen parameter
    	i2c_rep_start(TWI_ADR+I2C_READ);			// wiederhole start + read
    	i = (unsigned int)i2c_readAck();			// lower byte
    	j = (unsigned int)i2c_readNak(); 			// upper byte
    	i2c_stop();									// verbindung beenden
    
    	i += (j <<= 8);
    
    	return(i);
    }
    Für den Master benutze ich die Software i2cmaster.s von Peter Fleury's Homepage (man muss ja nicht immer das Rad neu erfinden).

    http://jump.to/fleury

    Peter (Ronny10)

  6. #6
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    27.11.2006
    Ort
    Hamburg
    Alter
    35
    Beiträge
    184
    Ok danke für den Tipps. Brauch man auch für den Slave ne 8MHz Resonator?
    Gruß Raid

    Der ^^ Mann

  7. #7
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    13.12.2006
    Ort
    Saarland
    Alter
    44
    Beiträge
    314
    GEnau das was ich meinte zu meinem display und dem link den ich bei meinem thema gepostet habe (zimlich zum schluss).Befasse mich richtig damit heute mittag erst weil ich hatte nachtschicht und gehe jetzt schlafen.
    THX Ronny

  8. #8
    Benutzer Stammmitglied
    Registriert seit
    23.05.2006
    Beiträge
    62
    Ich betreibe meine ATmega8 Mikrocontroller immer mit 8MHz (auch beim ASURO) und dem internen Oszillator. Damit habe ich die beiden CLK-Pins frei und benötige keine externen Bauteile zur Takterzeugung. Um die Bits für die Configuration und Security zu verändern, benötigst du PonyProg2000 oder was ähnliches, ein ISP-Interface und ein entsprechendes Programmierkabel.

    Der ATmega8 wird von ATMEL mit einer Werkseinstellung ausgeliefert: 1MHz CLk die mit dem internen Oszillator erzeugt wird! Wenn du einen "neuen" ATmega8 verwendest, muss du also erst die "Werkseinstellung" ändern, sonst arbeitet der ATmega8 nur mit 1MHz!

    Peter (Ronny10)

  9. #9
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    27.11.2006
    Ort
    Hamburg
    Alter
    35
    Beiträge
    184
    Ja, ich benutze AVR dude mit myprogrammer. was soll man als füsebits setzen, damit man interne oszillator mit 8Mhz betreiben kann?
    Gruß Raid

    Der ^^ Mann

  10. #10
    Benutzer Stammmitglied
    Registriert seit
    23.05.2006
    Beiträge
    62
    Bitte unbedingt zusätzlich die Doku dazu befragen! Wenn du eine falsche Einstellung vornimmst kann es sein, dass du deinen ATmega8 nicht mehr programmieren kannst!

    Das ist die Werkseinstellung für die CLK-Bits des ATmega8 (1MHz + int. Osz.), wie er von ATMEL ausgeliefert wird:

    CKSEL0 = 1
    CKSEL1 = 0
    CKSEL2 = 0
    CKSEL3 = 0

    Das ist die Einstellung für 8MHz CLK und internem Oszillator:

    CKSEL0 = 0
    CKSEL1 = 0
    CKSEL2 = 1
    CKSEL3 = 0

    Peter (Ronny10)

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

12V Akku bauen