Andun
25.10.2007, 23:41
Also mein Problem ist folgendes:
Ich lese für eine Tastaturmatrix einen PCF8574 aus an welchem über Pullups an allen 8 Eingängen 5V hängen. (Schaltplan ist denke ich nicht wichtig, falls doch gibts Bilder)
Wird nun eine Taste gedrückt geht eine Leitung irgendwann auf GND, wenn die Zeilen nacheinander alle durchprobiert werden.
Ich lese nun den PCF8574 aus und erhalte also ein Byte voll einsen und ein Bit ist ne 0. Die 0 zeigt mir die Spalte der gedrückten Taste an. Das funktioniert auch wunderbar, solange es nicht das 8. Bit ist. Dann stürzt mir der Controller ab, bzw. die Schleife wird einfach nicht fortgesetzt.
Hier mal mein Programmcode:
DDRD &= ~((1<<PD6)|(1<<PD7)); //PD6 und PD7 als Eingang definieren
uart_init();
//sei(); /* egal .... */
cli(); /* egal .... */
uint16_t input = 0;
uint8_t zeilen_zaehler = 1;
uart_puts(" Lese Tastatur...\r\n");
while(1){ //Hauptprogrammschleife
for(zeilen_zaehler=1; zeilen_zaehler; zeilen_zaehler = zeilen_zaehler<<1) {
//zeilen_zaehler hat also eine 1 im 1. bit, welches bei jedem Schleifendurchlauf eins nach links wandert bis es hinten raus fällt. Dann bricht die Schleife ab.
input = 0;
pcf8574_set_outputs(I2C_Z,~zeilen_zaehler);
//Ich setze alle Pins auf 5V, bis auf die zu testende Leitung, die auf GND, deswegen ~
input = ((PIND&((1<<PD6)|(1<<PD7)))<<2);
//diese beiden Kanäle les ich auch aus, da ich insgesamt 10 Spalten einlse
/*PD6 und PD7 auf 1 setzen. Durch das & werden nur noch 1 an PD6 oder PD7 behalten. <<2 um es auf die ersten beiden Bits des nächsten Bytes zu legen. */
input |= pcf8574_get_inputs(I2C_SP);
//es wird das niedrige Byte beschrieben.
if (input==895) //entspricht einer 0 auf dem 8. Bit und auf den anderen Bits 1-10 eine 1.
{uart_puts(".bl2.");Msleep(750);uart_puts(".blub.");}
/*Debugmeldung um einen Spannungseinbruch beim Tastendruck oder ähnliches zu ermitteln. Auch nach der Wartezeit(oder ohne) kommen alle ausgaben bis zum ende dieses schleifendurchlaufs*/
if(input != 1023) { //falls eine Leitung auf GND liegt, diese ausgeben.
uart_puts("Z.Nr.: ");
PrintInt(zeilen_zaehler);
uart_puts(" Lese Tastatur: ");
PrintInt(input);
//PrintInt(~(input|0xfc00)); //die höchsten 6 Bits ausblenden und den Wert invertiert ausgeben, weil einfacher zu lesen
uart_puts("\r\n");
}
if (input==895) {uart_puts(".bl3.");}
}
//uart_puts("..");
}
So, hoffentlich kann man den Code überhaupt lesen ....
Egal welchen Eingang ich auf GND ziehe, es funktioniert. Wenn ich den Eingang unten halte, dann kommt ganz schnell nacheinander im Terminal die Zeile und Spalte (also zeilen_zaehler und input) auf den Bildschirm.
Ziehe ich jedoch das 8. Bit des PCF8574 auf GND wird der Schleifendurchlauf beendet (wie mir das Terminal zeigt) aber danach startet einfach kein weiterer und erst ein Reset des Controllers schafft hier abhilfe.
Kann mir jemand bei diesem sehr speziellen Problem helfen?
Hier noch der Code zum auslesen des PCF8574:
/*pcf8574.c*/
#include "pcf8574.h"
#include "uart.h"
void pcf8574_init (void)
{
/*set bus speed*/
TWBR = 0x10;
}
unsigned char pcf8574_send_start (void)
{
/*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
/*wait, until start condition has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}
void pcf8574_send_stop (void)
{
/*writing a one to TWINT clears it, TWSTO=Stop, TWEN=TWI-enable*/
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
}
unsigned char pcf8574_send_add_rw (unsigned char address, unsigned char rw)
{
/*address can be 0 .. 8; rw=0 --> write, rw=1 --> read*/
unsigned char addr_byte = 0;
/*shift address one bit left*/
addr_byte = address << 1;
/*set RW-Bit, if necessary*/
addr_byte |= rw;
/*0b0100xxx0 --> address of Expander*/
addr_byte |= 0b01000000;
/*TWDR contains byte to send*/
TWDR = addr_byte;
/*send content of TWDR*/
TWCR = (1<<TWINT) | (1<<TWEN);
/*wait, until address has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}
unsigned char pcf8574_send_byte (unsigned char byte)
{
/*TWDR contains byte to send*/
TWDR = byte;
/*send content of TWDR*/
TWCR = (1<<TWINT) | (1<<TWEN);
/*wait, until byte has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}
unsigned char pcf8574_read_byte (void)
{
/*send content of TWDR; TWEA = enable ACK*/
TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);
/*wait, until byte has been received --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWDR;
}
unsigned char pcf8574_get_inputs (unsigned char address)
{
pcf8574_init ();
pcf8574_send_start ();
pcf8574_send_add_rw (address, 1);
unsigned char input = pcf8574_read_byte ();
pcf8574_send_stop ();
if (input==127) {uart_puts(".bl.");}
return input;
}
void pcf8574_set_outputs (unsigned char address, unsigned char byte)
{
pcf8574_init ();
pcf8574_send_start ();
pcf8574_send_add_rw (address, 0);
pcf8574_send_byte (byte);
pcf8574_send_stop ();
}
/*pcf8574.h*/
#ifndef _PCF8574_H
#define _PCF8574_H
#include <avr/io.h>
void pcf8574_init (void);
unsigned char pcf8574_send_start (void);
void pcf8574_send_stop (void);
unsigned char pcf8574_send_add_rw (unsigned char address, unsigned char rw);
unsigned char pcf8574_send_byte (unsigned char byte);
unsigned char pcf8574_read_byte (void);
extern unsigned char pcf8574_get_inputs (unsigned char address);
extern void pcf8574_set_outputs (unsigned char address, unsigned char byte);
#endif
Vielen Dank
Andun
Ich lese für eine Tastaturmatrix einen PCF8574 aus an welchem über Pullups an allen 8 Eingängen 5V hängen. (Schaltplan ist denke ich nicht wichtig, falls doch gibts Bilder)
Wird nun eine Taste gedrückt geht eine Leitung irgendwann auf GND, wenn die Zeilen nacheinander alle durchprobiert werden.
Ich lese nun den PCF8574 aus und erhalte also ein Byte voll einsen und ein Bit ist ne 0. Die 0 zeigt mir die Spalte der gedrückten Taste an. Das funktioniert auch wunderbar, solange es nicht das 8. Bit ist. Dann stürzt mir der Controller ab, bzw. die Schleife wird einfach nicht fortgesetzt.
Hier mal mein Programmcode:
DDRD &= ~((1<<PD6)|(1<<PD7)); //PD6 und PD7 als Eingang definieren
uart_init();
//sei(); /* egal .... */
cli(); /* egal .... */
uint16_t input = 0;
uint8_t zeilen_zaehler = 1;
uart_puts(" Lese Tastatur...\r\n");
while(1){ //Hauptprogrammschleife
for(zeilen_zaehler=1; zeilen_zaehler; zeilen_zaehler = zeilen_zaehler<<1) {
//zeilen_zaehler hat also eine 1 im 1. bit, welches bei jedem Schleifendurchlauf eins nach links wandert bis es hinten raus fällt. Dann bricht die Schleife ab.
input = 0;
pcf8574_set_outputs(I2C_Z,~zeilen_zaehler);
//Ich setze alle Pins auf 5V, bis auf die zu testende Leitung, die auf GND, deswegen ~
input = ((PIND&((1<<PD6)|(1<<PD7)))<<2);
//diese beiden Kanäle les ich auch aus, da ich insgesamt 10 Spalten einlse
/*PD6 und PD7 auf 1 setzen. Durch das & werden nur noch 1 an PD6 oder PD7 behalten. <<2 um es auf die ersten beiden Bits des nächsten Bytes zu legen. */
input |= pcf8574_get_inputs(I2C_SP);
//es wird das niedrige Byte beschrieben.
if (input==895) //entspricht einer 0 auf dem 8. Bit und auf den anderen Bits 1-10 eine 1.
{uart_puts(".bl2.");Msleep(750);uart_puts(".blub.");}
/*Debugmeldung um einen Spannungseinbruch beim Tastendruck oder ähnliches zu ermitteln. Auch nach der Wartezeit(oder ohne) kommen alle ausgaben bis zum ende dieses schleifendurchlaufs*/
if(input != 1023) { //falls eine Leitung auf GND liegt, diese ausgeben.
uart_puts("Z.Nr.: ");
PrintInt(zeilen_zaehler);
uart_puts(" Lese Tastatur: ");
PrintInt(input);
//PrintInt(~(input|0xfc00)); //die höchsten 6 Bits ausblenden und den Wert invertiert ausgeben, weil einfacher zu lesen
uart_puts("\r\n");
}
if (input==895) {uart_puts(".bl3.");}
}
//uart_puts("..");
}
So, hoffentlich kann man den Code überhaupt lesen ....
Egal welchen Eingang ich auf GND ziehe, es funktioniert. Wenn ich den Eingang unten halte, dann kommt ganz schnell nacheinander im Terminal die Zeile und Spalte (also zeilen_zaehler und input) auf den Bildschirm.
Ziehe ich jedoch das 8. Bit des PCF8574 auf GND wird der Schleifendurchlauf beendet (wie mir das Terminal zeigt) aber danach startet einfach kein weiterer und erst ein Reset des Controllers schafft hier abhilfe.
Kann mir jemand bei diesem sehr speziellen Problem helfen?
Hier noch der Code zum auslesen des PCF8574:
/*pcf8574.c*/
#include "pcf8574.h"
#include "uart.h"
void pcf8574_init (void)
{
/*set bus speed*/
TWBR = 0x10;
}
unsigned char pcf8574_send_start (void)
{
/*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
/*wait, until start condition has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}
void pcf8574_send_stop (void)
{
/*writing a one to TWINT clears it, TWSTO=Stop, TWEN=TWI-enable*/
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
}
unsigned char pcf8574_send_add_rw (unsigned char address, unsigned char rw)
{
/*address can be 0 .. 8; rw=0 --> write, rw=1 --> read*/
unsigned char addr_byte = 0;
/*shift address one bit left*/
addr_byte = address << 1;
/*set RW-Bit, if necessary*/
addr_byte |= rw;
/*0b0100xxx0 --> address of Expander*/
addr_byte |= 0b01000000;
/*TWDR contains byte to send*/
TWDR = addr_byte;
/*send content of TWDR*/
TWCR = (1<<TWINT) | (1<<TWEN);
/*wait, until address has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}
unsigned char pcf8574_send_byte (unsigned char byte)
{
/*TWDR contains byte to send*/
TWDR = byte;
/*send content of TWDR*/
TWCR = (1<<TWINT) | (1<<TWEN);
/*wait, until byte has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}
unsigned char pcf8574_read_byte (void)
{
/*send content of TWDR; TWEA = enable ACK*/
TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);
/*wait, until byte has been received --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWDR;
}
unsigned char pcf8574_get_inputs (unsigned char address)
{
pcf8574_init ();
pcf8574_send_start ();
pcf8574_send_add_rw (address, 1);
unsigned char input = pcf8574_read_byte ();
pcf8574_send_stop ();
if (input==127) {uart_puts(".bl.");}
return input;
}
void pcf8574_set_outputs (unsigned char address, unsigned char byte)
{
pcf8574_init ();
pcf8574_send_start ();
pcf8574_send_add_rw (address, 0);
pcf8574_send_byte (byte);
pcf8574_send_stop ();
}
/*pcf8574.h*/
#ifndef _PCF8574_H
#define _PCF8574_H
#include <avr/io.h>
void pcf8574_init (void);
unsigned char pcf8574_send_start (void);
void pcf8574_send_stop (void);
unsigned char pcf8574_send_add_rw (unsigned char address, unsigned char rw);
unsigned char pcf8574_send_byte (unsigned char byte);
unsigned char pcf8574_read_byte (void);
extern unsigned char pcf8574_get_inputs (unsigned char address);
extern void pcf8574_set_outputs (unsigned char address, unsigned char byte);
#endif
Vielen Dank
Andun