PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Xmega I²C PCF8547



Kampi
03.10.2012, 14:50
Hallo Forum,

ich möchte über den I²C einen PCF8574 ansteuern.
Zu Testzwecken habe ich den XMega128A1 mit dem PCF verbunden und an P0 vom PCF hängt eine LED die aufleuchten soll sobald der Pin gegen GND schaltet.
Ich verwende folgenden Code:

Main:


/*
* XMega_I2C.c
*
* Created: 02.10.2012 15:47:40
* Author: Daniel
*/






#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include <stddef.h> // Makro "offsetof" einbinden
#include <avr/pgmspace.h> // Funktionen zum auslesen
#include <stdlib.h>
#include "PCF8574.h"


void PCF8574_SetOutput(char adress, char data);

int main(void)
{
Clock_init();
Int_init();
TimerC0_init();
UART_init();
Port_init();


while(1)
{
//TODO:: Please write your application code
}
}


void Clock_init(void)
{
OSC.CTRL |= OSC_RC32MEN_bm; // Oszillator auf 32Mhz stellen
while(!(OSC.STATUS & OSC_RC32MEN_bm)); // Warten bis der Oszillator bereit ist
CCP = CCP_IOREG_gc;
CLK.CTRL = CLK_SCLKSEL_RC32M_gc; // Clock auf 32MHz stellen // Prescaler A = CLK/2, Prescaler B/C = CLK/1
}


void Int_init(void)
{
PMIC.CTRL |= PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm; // Interrupts (Highlevel, Mediumlevel und Lowlevel freigeben)
sei(); // Globale Interruptfreigabe
}


void TimerC0_init()
{
TCC0.CTRLA = TC_CLKSEL_DIV1024_gc; // Vorteiler einstellen
TCC0.CTRLB = 0x00; // Timer in Normalmodus stellen
TCC0.INTCTRLA = 0x03; // Interrupt konfigurieren
}


void UART_init(void)
{
USARTC0.BAUDCTRLB = 0; // BSCALE = 0
USARTC0.BAUDCTRLA = 0x68; // Baudrate 19200 @ 41MHz
USARTC0.CTRLA = USART_RXCINTLVL_HI_gc; // Interrupts mit Highlevel aktivieren
USARTC0.CTRLB = USART_TXEN_bm | USART_RXEN_bm; // RX+TX Enable CLK
USARTC0.CTRLC = USART_CHSIZE_8BIT_gc; // Async, no parity, 8 bit data, 1 stop bit
}


void Port_init(void)
{
PORTC.DIR = 0x08; // Pins für den UART C0 einstellen
}


void Send_UART(char data[])
{
char Counter;
char lenght = 0x00;

lenght = strlen(data);

while(Counter < lenght)
{
while (!(USARTC0.STATUS & USART_DREIF_bm));
USARTC0.DATA = data[Counter];
Counter++;
}

Counter = 0x00;
while (!( USARTC0.STATUS & USART_DREIF_bm));
USARTC0.DATA = 0x0A;
while (!( USARTC0.STATUS & USART_DREIF_bm));
USARTC0.DATA = 0x0D;
}


ISR(TCC0_OVF_vect)
{
PCF8574_send_byte (&TWIF, 0);
}


void PCF8574_SetOutput(char address, char data)
{
TWI_MasterInit(&TWIF);
PCF8574_send_add_rw (&TWIF, 0x20, 0);
PCF8574_send_byte (&TWIF, 1);
}


PCF8574.h:


/*
* TWI_Master.h
*
* Created: 02.10.2012 23:34:10
* Author: Daniel
*/


#ifndef TWI PCF8574_H_
#define TWI PCF8574_H_


#define CPU_SPEED 32000000
#define BAUDRATE 400000
#define TWI_BAUD(F_SYS, F_TWI) ((F_SYS / (2 * F_TWI)) - 5)
#define TWI_BAUDSETTING TWI_BAUD(CPU_SPEED, BAUDRATE)


void TWI_MasterInit(TWI_t *twi)
{
twi->MASTER.BAUD = TWI_BAUDSETTING;
twi->MASTER.CTRLA = TWI_MASTER_ENABLE_bm;
twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
}


void PCF8574_send_add_rw(TWI_t *twi, unsigned char Adresse, unsigned char RW)
{
char PCF8574_Add = 0x00;


PCF8574_Add = Adresse << 1;
PCF8574_Add |= RW; // RW setzen

_delay_ms(5);

twi->MASTER.ADDR = PCF8574_Add;

_delay_ms(5);
}


PCF8574_send_byte (TWI_t *twi, unsigned char byte)
{
twi->MASTER.DATA = byte;
}


#endif /* PCF8574_H_ */


Nur leider passiert da nichts, sprich die LED bleibt dunkel.
Als Pull-Up Widerstände verwende ich 3,3k und bei dem Board handelt es sich um ein AVR XPlained128.
Kann mir einer sagen wo ich den Fehler gemacht habe?
Danke schonmal!

Klebwax
03.10.2012, 16:49
Der Code ist ziemlich unübersichtlich. Ich kann nicht wirklich erkennen, was passieren soll. Da die Mainloop leer ist, muß es wohl im Interrupt sein. Für einen ersten Versuch ist das unglücklich, weil wesentlich schwerer zu verstehen und zu debuggen. Ein paar Kommentare wären nützlich. Noch eine Anmerkung, Code gehört nur in Ausnahmefällen in einen Header-File.

Für das Schreiben vom I2C Master zum Slave braucht man einen Ablauf noch folgendem Muster

i2c-Start
write AddressByte
write DataByte // möglicherweise mehrmals
i2c-Stop

Das kann ich aber nicht finden.

MfG Klebwax

Kampi
03.10.2012, 17:08
Hey,

ja da hast du nicht ganz unrecht....hab ihn mal etwas verschönert und schon funktionierte es ^-^
Hab mich wohl irgendwie verrannt....
Danke trotzdem für die Hilfe :)
Hier ist der funktionierende Code:



/*
* XMega_I2C.c
*
* Created: 02.10.2012 15:47:40
* Author: Daniel
*/


#define F_CPU 32000000
#define CPU_SPEED 32000000
#define BAUDRATE 400000
#define TWI_BAUD(F_SYS, F_TWI) ((F_SYS / (2 * F_TWI)) - 5)
#define TWI_BAUDSETTING TWI_BAUD(CPU_SPEED, BAUDRATE)


#define PCF8574 0x20 // PCF8574 Adresse definieren


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include <stddef.h> // Makro "offsetof" einbinden
#include <avr/pgmspace.h> // Funktionen zum auslesen
#include <stdlib.h>

int main(void)
{
Clock_init(); // Clock mit 32MHz initialisieren
Int_init(); // Interrupts aktivieren
TimerC0_init();
UART_init();
Port_init();
TWI_MasterInit(&TWIF); // TWI initialisieren


while(1)
{
PCF8574_send_byte(&TWIF, 0); // Eine 0 zum PCF senden
_delay_ms(1000); // 1 Sekunde warten
PCF8574_send_byte(&TWIF, 1);
_delay_ms(1000);
}
}


void Clock_init(void)
{
OSC.CTRL |= OSC_RC32MEN_bm; // Oszillator auf 32Mhz stellen
while(!(OSC.STATUS & OSC_RC32MEN_bm)); // Warten bis der Oszillator bereit ist
CCP = CCP_IOREG_gc;
CLK.CTRL = CLK_SCLKSEL_RC32M_gc; // Clock auf 32MHz stellen // Prescaler A = CLK/2, Prescaler B/C = CLK/1
}


void Int_init(void)
{
PMIC.CTRL |= PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm; // Interrupts (Highlevel, Mediumlevel und Lowlevel freigeben)
sei(); // Globale Interruptfreigabe
}


void TimerC0_init()
{
TCC0.CTRLA = TC_CLKSEL_DIV1024_gc; // Vorteiler einstellen
TCC0.CTRLB = 0x00; // Timer in Normalmodus stellen
TCC0.INTCTRLA = 0x03; // Interrupt konfigurieren
}


void UART_init(void)
{
USARTC0.BAUDCTRLB = 0; // BSCALE = 0
USARTC0.BAUDCTRLA = 0x68; // Baudrate 19200 @ 41MHz
USARTC0.CTRLA = USART_RXCINTLVL_HI_gc; // Interrupts mit Highlevel aktivieren
USARTC0.CTRLB = USART_TXEN_bm | USART_RXEN_bm; // RX+TX Enable CLK
USARTC0.CTRLC = USART_CHSIZE_8BIT_gc; // Async, no parity, 8 bit data, 1 stop bit
}


void Port_init(void)
{
PORTC.DIR = 0x08; // Pins für den UART C0 einstellen
}


void TWI_MasterInit(TWI_t *twi)
{
twi->MASTER.BAUD = TWI_BAUDSETTING;
twi->MASTER.CTRLA = TWI_MASTER_ENABLE_bm;
twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
}


void Send_UART(char data[])
{
char Counter;
char lenght = 0x00;

lenght = strlen(data);

while(Counter < lenght)
{
while (!(USARTC0.STATUS & USART_DREIF_bm));
USARTC0.DATA = data[Counter];
Counter++;
}

Counter = 0x00;
while (!( USARTC0.STATUS & USART_DREIF_bm));
USARTC0.DATA = 0x0A;
while (!( USARTC0.STATUS & USART_DREIF_bm));
USARTC0.DATA = 0x0D;
}


ISR(TCC0_OVF_vect)
{

}


void Send_Address(TWI_t *twi, unsigned char Adresse, unsigned char RW)
{
char Add = 0x00;


Add = Adresse << 1; // Adresse um eine Stelle nach links schieben
Add |= RW; // RW setzen
twi->MASTER.ADDR = Add; // Adresse in das Adressregister schreiben

_delay_ms(5);
}


void PCF8574_send_byte(TWI_t *twi, unsigned char byte)
{
Send_Address(&TWIF, PCF8574, 0);
twi->MASTER.DATA = byte; // Byte ins Datenregister schreiben
}