Liste der Anhänge anzeigen (Anzahl: 1)
@SlyD oh.. auch nicht schlecht... da bin ich mit dem Prozessordatenblatt und den vielen halb implementierten Sources die im Web rumgeistern (incl. Atmels Sourcecode) ja noch gut dran :) Im Web zu googlen lohnt sich für mich nicht mehr.. da finde ich inzwischen meine eigenen Beiträge hier :) Interssanter Weise finden sich die besten Infos immer noch hier im RN-Wissen.
Hallo,
damit euch nicht langweilig wird, hab ich für die Betatester was.
Die Geschichte ist noch nicht stabil aber zumindest sollten sich Master und Slave unterhalten können. (bei mir tuen sie das :) )
Zunächst das modifizierte Testprogramm von Dirk:
Die Base/Slave, ich poste nur die main
Code:
int main(void)
{
initRobotBase();
setLEDs(0b111111);
mSleep(500);
setLEDs(0b000000);
I2CTWI_initSlave(RP6BASE_I2C_SLAVE_ADR);
while (true)
{
/* asks TWI for waiting Jobs, textblock may be a "task_slave" in rp6 c notation?
**
*/ if (I2CTWI_needAction()) {
/*
** yessssss ... doit!
*/
// get data from slave receiver mode register
controlbyte = I2CTWI_writeRegisters[0]; // Read control byte
I2CTWI_writeRegisters[0] = 0; // and reset it (!!!)
lowbyte = I2CTWI_writeRegisters[1]; // Read lowbyte
highbyte = I2CTWI_writeRegisters[2]; // Read highbyte
// copy data to slave sender mode register
I2CTWI_readableRegisters[0] = controlbyte; //copy controlbyte
I2CTWI_readableRegisters[1] = lowbyte; // copy lowbyte
I2CTWI_readableRegisters[2] = highbyte; // copy highbyte
// this is controlled by I2CTWI_needAction()
// if (controlbyte == CMD_SHOW_DATA) { //
cnt = ((highbyte << 8) | lowbyte); // Restore 16-bit value
#ifdef TEXT_OUTPUT
writeString_P("Received: "); // and show it
writeInteger(cnt, DEC);
#endif
if (!cnt) error_cnt = 0; // Reset error counter
else error_cnt++; // Error counter + 1
if (cnt != error_cnt) {
writeString_P(" ERROR!\n");
error_cnt = cnt;
}
#ifdef TEXT_OUTPUT
else writeChar('\n');
#endif
controlbyte = 0;
// }
/* signals Job as "done" and free the receiver for next Data
**
*/ I2CTWI_doneAction();
/*
** done!
*/
}
/*
** blink like fireworks, attract girls by danci'n circles or lying around an drink ... gear oil
*/
}
return 0;
}
Dann der Master auf der M32
Code:
int main(void)
{
initRP6Control();
initLCD();
I2CTWI_initSlave(12); // Initialize the TWI Module for Master operation
// with 100kHz SCL Frequency
// Register the event handlers:
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError);
setLEDs(0b1111);
mSleep(500);
setLEDs(0b0000);
while(true)
{
if (!I2CTWI_isBusy()) {
cnt++; // TEST: COUNTER!!!
if (cnt > 255) cnt = 0;
buffer[0] = 0;
buffer[1] = CMD_SHOW_DATA;
buffer[2] = cnt;
buffer[3] = (cnt >> 8);
I2CTWI_transmitBytes(RP6BASE_I2C_SLAVE_ADR, buffer, 4);
mSleep(1000); //the slave works...
buffer[0] = 0; //reset buffer
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
I2CTWI_readRegisters(RP6BASE_I2C_SLAVE_ADR, 0, buffer, 4);
if (cnt == ((buffer[2] << 8) | buffer[1])) {
#ifdef TEXT_OUTPUT
setCursorPosLCD(0, 0);
writeStringLCD_P("I2C has moved:"); // What was sent?
setCursorPosLCD(1, 0);
writeIntegerLCD(cnt, DEC);
#endif
} else {
setCursorPosLCD(0, 0);
writeStringLCD_P("Error!"); // What was sent?
}
//if (cnt==3) while(true); //countertrap
}
}
return 0;
}
Es hat sich in soweit etwas verändert, das der Master die Daten auf den Slave schreibt, dieser kopiert die Daten vom Schreib- ins Leseregister und prüft, eine Zeit lang später (hier 1 Sek. weil ich am debuggen bin) liest der Master die Daten zurück um sie mit dem Original zu vergleichen. Sinnvoll wäre hier ein Statusbyte einzuplanen das der Master abfragen kann. Dafür müsste der Master aber ins Leseregister schreiben da der slave nicht mitbekommt ob der Master liest. Ließe sich aber irgendwie anders einbauen. Ich vermute aber das man in Dirks Programm locker auf 1ms runter gehen kann, der Slave copiert ja nur bissel in der Gegend rum bis die Daten bereit sind. Hier wie in der Schreibfunktion zu nacken macht aber keinen Sinn da es den Master lesend blockiert. Das ist mit einem Statusbyte besser gelöst
Es sind also alle 4 Modi mit Slave Receive/Transmit und Master Receive/Transmit beteiligt. Die ISR sperrt nach einem schreibenden Durchgang auf den Slave weitere Schreibversuche durch NACKen. Erst wenn der Slave fertig ist schaltet er die ISR frei. I2CTWI_needAction() und insbesondere I2CTWI_doneAction() erledigen das.
Eine Abfrage von Statusbytes in den Registern erledigt sich damit, allerdings muss damit jede Schreibaktion vom Slave quitiert werden sonst wartet sich der Master einen Wolf.
Die aktuelle twi_target.h sieht bei mir so aus (hier von der Base):
Code:
/**/
#ifndef TARGET_H //Include once
#define TARGET_H
/* this define Hardware to selecting the right includes
** use one of them, never both
*/
#define M32
//#define BASE
/* this define TWI Modus to stip obsolete Slave code in Mastermode
** use one of them, never both
*/
//#define TWIMASTER
#define TWISLAVE
/*
** use LOWLEVEL I2C Functions without interupts too
*/
//#define TWILOW
/* this TWI define Speedsetup at 100 or 400 KHz Standart
** use one of them
*/
#define TWISPEED 100
/*
** use Generic calls, not supported yet
*/
//#define GENADR
/*
** if set, Base can use I2CLCD like LCD on M32
*/
//#define I2C_LCD
/*
** internal use, hide warnings
*/
#define UNUSED(x) ((void)(x)) //help optimizing
#define DEBUG
#include "RP6I2CTWI.h"
#ifdef BASE
#include <RP6RobotBase.h>
#ifdef DEBUG
#include <RP6RobotBaseLib.h>
#endif
#ifdef I2C_LCD
#include <RP6RobotBaseLib.h>
#include <lcd.h>
#define writeLCDCommand writeI2CLCDCommand
#define initLCD initI2CLCD
#define clearLCD clearI2CLCD
#define clearPosLCD clearPosI2CLCD
#define writeCharLCD writeCharI2CLCD
#define writeStringLCD_P writeStringI2CLCD_P
#define writeStringLengthLCD writeStringLengthI2CLCD
#define writeStringLCD writeStringI2CLCD
#define writeNStringLCD_P writeNStringI2CLCD_P
#define _showScreenLCD_P _showScreenI2CLCD_P
#define writeIntegerLCD writeIntegerI2CLCD
#define writeIntegerLengthLCD writeIntegerLengthI2CLCD
#define setCursorPosLCD setCursorPosI2CLCD
#define setlightLCD setlightI2CLCD
#endif
#else
#ifdef M32
#include <RP6Control.h>
#ifdef DEBUG
#include <RP6ControlLib.h>
#endif
#ifdef I2C_LCD
#include <RP6ControlLib.h>
#include <lcd.h>
#endif
#endif
#endif
#endif
Ihr werdet nicht alles brauchen, allerdings sind die Optionen im oberen Bereich schon interessant da sie ggf. Code einsparen helfen. Achtung, DEBUG ist Aktiv... unten stricke ich mir u.a. das i2c-lcd für die Base als normals M32 Display zurecht.... macht sich gut wenn man die gleichen Programme auf der Base UND der M32 testen will.
In der TWI hat sich viel getan, u.a. habe ich einige Variablen abgeschaltet, die versuchten die ISR zu monitoren. Sowas z.B.
/* don't use, obsolte, try I2CTWI_isBusy() instead
volatile uint8_t I2CTWI_genCallCMD;
volatile uint8_t I2CTWI_dataWasRead = 1;
volatile uint8_t I2CTWI_dataReadFromReg = 1;
volatile uint8_t I2CTWI_readBusy = 0;
volatile uint8_t I2CTWI_writeBusy = 0;
*/
Die Funktionalität (war eigentlich keine) ist mit den Action Befehlen und isBusy besser gelöst, der C Preprozessor hilft hier beim anpassen älterer Projekte ungemein ohne das man viel umschreiben muss. Allerdings funktioniert das jetzt so nicht mehr so einfach mit dem Slave Programm für die Base. Aber zum testen reicht Dirks Programm und Fabian wird den Slave sicher anpassen können. Sonst mach ich das irgendwann. Ich sehe jedenfalls nicht ein, Code zu pflegen der obsolet ist, nur weil er da ist. So viel Ram und Flash hat der RP6 auch nicht...
Evtl. mache ich das NACKen nach schreiben auf den Slave optional, derzeit kann man sich gut mit dem bedingungslosen Befehl I2CTWI_doneAction(); in der äusseren While Loop helfen wenn der Slave möglichst nicht nacken soll - allerdings werden dann halt die Register überschrieben. Die gencall Geschichte ist z.Z. komplett abgeschaltet, da konnte bisher eh nur ein Byte mit empfangen werden. Da hab ich noch was in der Todo stehen :) für irgendwann mal...
Das ganze System hat immer noch viele Macken, es ist noch nicht auf Multimaster umgestellt aber man kann aus dem Slavemode herraus zumindest Masterfunktionen ansprechen. Ein paar Macken sind leider systembedingt - z.B. werden zusammenhängende Befehlsketten wie Setzen einer Registeradresse und anschließendes Register lesen in 2 Zyklen ausgeführt was nicht nötig wäre, was aber z.B. dazu führt, das die ISR für kurze Zeiten "frei" ist obwohl sie eigentlich mitten in einer Transaktion steckt. Ein folgender Befehl kann hier die noch arbeitende ISR stören. Das scheint auch für Buserrors zu sorgen. Sowas ist unschön! Ob ich das beheben kann, weis ich noch nicht. Es stört mich auch, das in einer ansich Interruotgesteuerten Umgebung blockende Befehle stecken, das mindert den Sinn einer ISR doch gewaltig. Vermutlich stimmen auch einige Reaktionen auf die TWI-State Engine noch nicht, was wohl auch zu Buserrors führt, da bin ich aber dran... alles in allem also noch sehr Beta. Aber wieder mal ein Stück weiter hoffe ich.
Mein nächstes Ziel ist zum einen die Geschichte stabiler zu machen, aus irgendeinem Grund bleibt der Transport hängen, zum anderen möchte ich die Programme von Dirk zusammen legen, zu einem "Masterslave" so das sich Master und Slave des jeweiligen Boards abwechseln und gegenseitig Daten zuwerfen und prüfen. Also bidirektionaler/intermitierender Master/Slave Betrieb auf Base und M32.
Mir erschließt sich noch nicht, welch tieferer Sinn sich dahinter verbirgt, das man z.B. mit Readregisters nur Speicherplätze ab 1 im Buffer lesen, aber mit anderen Funktionen ab 0 beschreiben kann. Sowas werde ich wohl auch ändern wenns keine großen Einsprüche gibt.
Benutzt man die Geschichte nur im Mastermode mit nur einem Prozessor (per twi_target einstellen), kann man den Slavecode komplett einsparen, benutzt man auch den Slave, hat man beide Modi zur Auswahl. Die default Werte für
#define I2CTWI_SLAVE_WRITE_REGISTERS 16
#define I2CTWI_SLAVE_READ_REGISTERS 48
sind etwas zu hoch gewählt, hier würden im normalen Slaveprogramm auch 8/32 reichen wenn man ein paar Byte sparen will. Ich habs aber mal so gelassen.
EDIT:
Und wieder eine kleine Änderung in der RP6I2CTWI.c
Um die Buffer gehören Präprozessorabfragen zum TWISLAVE, keine funktionelle Änderung aber es spart Platz im Mastermode. In der nächsten Fassung ist das mit drin. In der .h war es schon eingetragen.
#ifdef TWISLAVE
uint8_t I2CTWI_readableRegisters[I2CTWI_SLAVE_READ_REGISTERS];
volatile uint8_t I2CTWI_writeRegisters[I2CTWI_SLAVE_WRITE_REGISTERS];
#endif
Dann noch was zum NACKen und Buffergrenzen überfahren, es wäre z.Z. wohl denkbar, das der Master hinter die Buffergrenze liest was der Slave nacken müsste worauf hin der Master abbrechen soll. Ob das klappt weis ich nicht, muss ich noch testen. Ich überlege sowieso, alternativ 1 R+W oder 2 R/W Registersets per twi_target.h anzubieten, das macht den Slave universeller. Und demnächst wirds zusätzlich ein Satz ISR-lose Funktionen geben, ähnlich der von Peter Fleury oder der AVR-LIBC aber angepasst an die Abläufe und Vorgaben der aktuellen TWI Steuerung.
EDIT: Hm.. ich glaube da stimmt was noch nicht... der Master hängt sich auf laut LEDs.. aber morgen wirds wieder ne Beta Version geben denk ich mal. NACK als Option ist drin, die Lowlevelfunktionen sind schon drin, jede Menge Code cleanup über #ifdef...
LG Rolf