-
hallo,
ich verstehe jetzt deinen Code nicht mehr -
ist es noch so wie in meinem Ausgangs-Code,
- dass der Raspi im 10ms-Takt (künftig schneller) zum Arduino 32 Bytes schickt und dann, wenn sie angekommen sid, 32 neue Bytes von ihm zurück liest
- und dass der Arduino so schnell wie möglich (also mindestens im 1ms- Takt) seinen Sende-Array zum Auslesen aktualisiert und bereitstellt?
Der Arduino muss dies automatisch tun, so schnell wie möglich, es darf keine Anforderung vom Raspi nötig sein außer ein onRequest und/oder ein onReceive(), um sie zu empfangen oder weg zu "senden".
Später in der "richtigen" Anwendung wird der Arduino seine digitalen und anlalogen Pins per Timer Interrupt (AVR) oder Task (DUE) (100µs) ständig (quasi im Hintergund bzw. per Parallel-Task) aktualisieren, damit sie beim Abruf immer in der am weitest aktualisiertesten Version zur Verfügung stehen. Kommt dann ein onRequest, kann abgerufen werden, was bis dahin bekannt ist.
Genauso muss jederzeit alles angenommen werden können, was vom Master gesendet wurde, per onReceive Event. Eine gesonderte Funktion (ebenfalls per Timer-Interrupt oder Task) verarbeitet sie dann weiter.
Der Arduino-Code soll dazu möglichst unverändert bleiben (außer clock-stretching vermeiden)
und der Raspi-Code ebenfalls, außer dass er clock-stretching besser verträgt, d.h. die normalen read() und write() Befehle für I2C müssten durch andere, clock-tolerantere, erstetzt werden.
Raspi:
Code:
// Raspberry Pi Master code to send/receive byte arrays
// to an Arduino as an I2C slave
//
// ver. 0.002b
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <errno.h>
#include <string.h>
#define MSGSIZE 32
#define ARDU1_SLV_ADDR 0x04
uint8_t calcchecksum( uint8_t array[]) {
int32_t sum=0;
for(int i=2; i<MSGSIZE; ++i) sum+=(array[i]);
return (sum & 0x00ff);
}
int main (void)
{
int fd, i ;
uint8_t test=0;
uint8_t data [MSGSIZE] ;
if ((fd = wiringPiI2CSetup ( ARDU1_SLV_ADDR) ) < 0)
{
fprintf (stderr, "Can't open RTC: %s\n", strerror (errno)) ;
exit (EXIT_FAILURE) ;
}
for (;;)
{
memset(data, 0, sizeof(data) );
data[0]= 0xff; // init for transmission error check
read (fd, data, MSGSIZE) ;
if( data[1] != calcchecksum( data ) ) {
// handle transmission error !
}
else {
printf (" read: ");
for (i = 0 ; i < 7 ; ++i)
printf (" %3d", data [i]) ;
//printf ("\n") ;
delay(10) ;
memset(data, 0, sizeof(data) );
data[5]= test++;
data[0]= 0xff;
data[MSGSIZE-1]= ARDU1_SLV_ADDR ;
data[1] = calcchecksum( data );
write(fd, data, MSGSIZE) ;
printf (" write: ");
for (i = 0 ; i < 7; ++i)
printf (" %3d", data [i]) ;
printf ("\n\n") ;
delay(10) ;
}
}
return 0 ;
}
Arduino:
Code:
// Arduino code to send/receive byte arrays
// Arduino as an I2C slave
//
// ver. 0.002b
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
#define MSGSIZE 32
byte recvarray[MSGSIZE]; // 0=0xff; 1=chksum; ...data...; MSGSIZE-1=SLAVE_ADDRESS
byte sendarray[MSGSIZE];
volatile int8_t flag=0;
//=====================================================================================
//=====================================================================================
void setup() {
int32_t i=0;
// Serial terminal window
i=115200;
Serial.begin(i);
Serial.print("Serial started, baud=");
Serial.println(i);
// Wire (i2c)
Wire.begin(SLAVE_ADDRESS); // start Arduino as a I2C slave, addr=0x04 (7-bit coded)
Wire.onReceive(receiveData ); // event when master array is sent
Wire.onRequest(sendData ); // event when master requests array to read
memset(sendarray, 0, sizeof(sendarray) ); // init send- and recv arrays
memset(recvarray, 0, sizeof(recvarray) );
Serial.print("I2C init: my slave address= ");
Serial.println(SLAVE_ADDRESS);
Serial.println("I2C init: done.");
Serial.println();
Serial.println("setup(): done.");
}
//=====================================================================================
uint8_t calcchecksum(uint8_t array[]) {
int32_t sum=0;
for(int i=2; i<MSGSIZE; ++i) sum+=(array[i]);
return (sum & 0x00ff);
}
//=====================================================================================
void loop()
{
char sbuf[128];
Serial.println(); Serial.println();
// do something with the received data
// and then do something to build the sendarray [3]...[MSG_SIZE-2]
if (flag==1) {
//debug
sendarray[4] +=1;
}
sendarray[0] = 0xff; // 0 = start: 0xff == msg start flag
sendarray[2] = flag; // 2 = send back msg error flag
sendarray[MSGSIZE-1] = SLAVE_ADDRESS; // end of array: ID check
sendarray[1] = calcchecksum(sendarray); // 1 = calc new chksum
flag=0;
// debug output
sprintf(sbuf, "Sendarr[4]=%4d, [5]=%4d, Recvarr[4]=%4d, [5]=%4d",
sendarray[4], sendarray[5], recvarray[4], recvarray[5]) ;
Serial.println(sbuf);
delay(1); // short break for the cpu and the bus
}
//=====================================================================================
void receiveData(int byteCount) {
int32_t i;
byte val;
while(Wire.available()<MSGSIZE) ; // wait for all bytes to complete
i=0; // init counter var
while(Wire.available()&& (i<MSGSIZE) ) // read all recv array bytes
{
val=Wire.read();
recvarray[i++]=val;
}
// check for transmission error
if( (recvarray[0] == 0xff)
&& (recvarray[1] == calcchecksum(recvarray))
&& (recvarray[MSGSIZE-1] == SLAVE_ADDRESS ) )
flag=1; // data ok
else
flag=127; // data faulty => handle rcv-error => flag =127
}
//=====================================================================================
void sendData(){
// Wire.write writes data from a slave device in response to a request from a master
Wire.write(sendarray, MSGSIZE); // send own byte array back to master..
-
Hallo,
ich habe nun die Funktion verstanden und den BitBAng Modus zum laufen zu bekommen.
Die Ergebnisse sind ernüchternd. Daten zum AVR bekomme ich hin. Beim Lesen hakt es noch arg. 1 Byte kein Problem. Bei mehreren steht dann der Bus.
Der Resync nach einem Fehler klappt nun nicht mehr.
-
hi,
Mist.
Ich denke fast, wir sollten es aufgeben, zuviel Aufwand. Vllt gibt ja irgendwann nen neuen Jessie-Kernel, der clock-stretching unterstützt, wie Gordon Henderson schrieb... irgendwann halt...
Solange muss ich dann mit meinen DUEs vorlieb nehmen.
- - - Aktualisiert - - -
angeblich gibt es sogar schon etwas Bewegung in dieser Sache:
https://github.com/raspberrypi/linux/pull/1241
-
Hallo,
es sieht nicht so gut aus. Anbei meine aktuellen Testcodes, wo 240 Bytes hin und zurück geschickt und verglichen werden.
MEGA:
Code:
// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>
// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this
// Created 29 March 2006
// This example code is in the public domain.
#include <Wire.h>
#define REGSIZE 240
byte rspeicher[REGSIZE];
byte wspeicher[REGSIZE];
byte puffer[32];
byte Register=1;
byte rdblock=0;
byte wrblock=0;
byte pos=0;
byte fertig=0;
long dg=0;
long timeout=0;
void setup() {
Wire.begin(4); // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register event
Serial.begin(57600); // start serial for output
Serial.println();
Serial.println("Starte I2C-Test");
}
void loop() {
byte i=0;
delay(100);
timeout++;
if (timeout==90){
timeout=0;
Wire.begin(4);
Serial.println("TimeOut!");
dg=0;
}
if (fertig !=0){
fertig=0;
timeout=0;
dg++;
Serial.println(dg);
for (i=0;i<REGSIZE;i++){
wspeicher[i]=rspeicher[i];
/* Serial.print(rspeicher[i]);
Serial.print (" ");*/
}
// Serial.println();
}
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
byte x=0;
if(howMany == 2){
rdblock = Wire.read();
if (rdblock<=240){
rspeicher[rdblock]=Wire.read();
}
if (rdblock==REGSIZE-1){
fertig=1;
}
}else{
if (howMany == 1){
x=Wire.read();
if (x >=10 && x <=250){
wrblock=x-10;
/* Serial.print("Lesebyte: ");
Serial.println(wrblock);*/
}
}else{
Serial.print ("Anzahl Daten: ");
Serial.println(howMany);
while (0 < Wire.available()) { //überflüssiges lesen
x=Wire.read();
}
}
}
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
//Wire.write(speicher[Register],1);
puffer[0]=wrblock;
puffer[1]=wspeicher[puffer[0]];
Wire.write(puffer,2);
/* Serial.print("Sendebyte: ");
Serial.println(puffer[0]);
/* Serial.print(", Wert: ");
Serial.println(puffer[1]);*/
}
Raspi HW:
Code:
// gcc -Wall -pthread -o /var/scripte/i2cmaster01 /var/scripte/i2cmaster01.c -lpigpio -lrt
// /var/scripte/i2cmaster01
//I2C mit pipgio
//peterfido
//V0.0.2
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pigpio.h>
#define ADDRESS 0x04
#define MSGSIZE 240
char empfdata[MSGSIZE]; //
char senddata[MSGSIZE];
char commando[32];
int handle=0;
int handles=-1;
unsigned int laenge=0;
volatile sig_atomic_t done = 0;
void term(int signum)
{
done = 1;
/*
int res=0;
int dl=0;
if (handles >= 0){
for (dl=0;dl<handles;dl++){
res=i2cClose(dl);
printf("%d geschlossen: %d\n",dl, res);
}
}
gpioTerminate();
printf( "\nSIGINT wurde augeloest - Programm wird beendet\n" );
exit( 1 );*/
}
void schreiben() {
int res=0;
// int i=0;
int wrblock=0;
int Fehler=0;
for (wrblock=0;wrblock<MSGSIZE;wrblock++){
commando[0]=wrblock;
commando[1]=senddata[wrblock];
res=i2cWriteDevice(handle,commando,2);
if (res!=0){
printf("S I2C-Fehler: %d, Block: %d\n", res,wrblock);
Fehler++;
/* wrblock--;
usleep(15000);
if (Fehler==10){
break;
}*/
break;
}
usleep(15000);
}
/* printf("Gesendet:\n");
for(i=0;i<MSGSIZE;i++){
printf("%d ",senddata[i]);
}
printf("\n");
*/
}
void lesen() {
int res=0;
// int i=0;
int rdblock=0;
int Fehler=0;
for (rdblock=0;rdblock<MSGSIZE;rdblock++){
commando[0]=rdblock+10;
res=i2cWriteDevice(handle,commando,1);
if (res==0){
usleep(1000);
res=i2cReadDevice(handle,commando,2);
if (res==2){
// printf("Empfangene Daten: %d, Block: %d\n",res,rdblock);
if (rdblock==commando[0]){
empfdata[rdblock]=commando[1];
}
}else{
printf("E Fehler beim Blockeinlesen: %d, Block: %d\n", res,rdblock);
Fehler++;
rdblock--;
usleep(15000);
if (Fehler==10){
break;
}
break;
}
}else{
printf("E Fehler beim Leseblock schreiben %d, Block: %d\n", res,rdblock);
break;
}
}
/*
printf("Empfangen:\n");
for(i=0;i<MSGSIZE;i++){
printf("%d ",empfdata[i]);
}
printf("\n");
*/
}
int vergleichen()
{
unsigned int i=0;
int ret=0;
for (i=0;i<MSGSIZE;i++){
if (empfdata[i]!=senddata[i]){
ret=-1;
break;
}
}
return ret;
}
int main(int argc, char **argv)
{
int res=0;
int i=0;
long dg=1;
long maxdg=0;
unsigned int Fehler=0;
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = term;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
if (gpioInitialise() < 0)
{
printf("Fehler beim Initialisieren von gpio!");
gpioTerminate();
}else{
handle=i2cOpen(1,ADDRESS,0);
if(handle>=0){
handles ++;
printf("\n\n\nStarte I2C-Test (%d)\n\n",handle);
while (!done)
{
srand(time(NULL));
for(i=0;i<MSGSIZE;i++){
senddata[i]=rand() % 255;
}
schreiben();
usleep(100000);
lesen();
usleep(100000);
if (vergleichen()==0){
printf("Daten OK %ld (%ld)\n",dg,maxdg);
Fehler=0;
dg++;
}else{
printf("***************************** Daten fehlerhaft %ld *****************************\n",dg);
Fehler++;
dg=0;
if (dg>maxdg){
maxdg=dg;
}
sleep(1);
if (Fehler==100){
break;
}
}
}
}else{
printf("E Fehler beim Oeffnen: %d\n", handle);
}
}
res=i2cClose(handle);
if (res!=0){
printf("E Fehler beim Schliessen: %d\n", res);
}else{
handles--;
printf("E Schliessen OK %d\n",handle);
}
gpioTerminate();
return 0;
}
Raspi BitBang:
Code:
// gcc -Wall -pthread -o /var/scripte/bbi2cmaster /var/scripte/bbi2cmaster.c -lpigpio -lrt
// /var/scripte/bbi2cmaster
//I2C mit pipgio
//peterfido
//V0.0.2
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pigpio.h>
#define SDA 2
#define SCL 3
#define TAKT 100000
#define PAUSEN 5000
#define ADDRESS 0x04
#define MSGSIZE 240
char empfdata[MSGSIZE]; //
char senddata[MSGSIZE];
char commando[64];
char puffer[12];
unsigned int laenge=0;
volatile sig_atomic_t done = 0;
void term(int signum)
{
done = 1;
}
int I2CResetBus(void)
{
char i;
int res=0;
if(gpioRead(SDA) && gpioRead(SCL)) {
return 0;
}
gpioSetMode(SCL, PI_INPUT);
gpioSetMode(SDA, PI_INPUT);
gpioSetPullUpDown(SDA, PI_PUD_UP); // Sets SDA pull-up.
gpioSetPullUpDown(SCL, PI_PUD_UP); // Sets SDA pull-up.
usleep(50);
if(!gpioRead(SCL)) {
return -1; /* SCL wird extern auf Low gehalten, nix zu machen */
}
for(i = 0; i<9; i++) {
gpioSetMode(SCL, PI_OUTPUT);
gpioWrite(SCL, 0);
usleep(50);
gpioSetMode(SCL, PI_INPUT);
gpioSetPullUpDown(SCL, PI_PUD_UP); // Sets SDA pull-up.
usleep(50);
if(gpioRead(SDA)) {
break; /* SDA ist high STOP */
}
} /* for */
if(!gpioRead(SDA)) {
return -1;
}
commando[0]= 3; //stop
commando[1]= 0; // Fertig
res=bbI2CZip(SDA,commando,2,NULL,0);
if (res!=0){
printf("Fehler beim Reset!\n");
}
if(gpioRead(SDA) && gpioRead(SCL)){
return 0;
}else{
return -1;
}
}
void schreiben() {
int res=0;
// int i=0;
int wrblock=0;
int Fehler=0;
for (wrblock=0;wrblock<MSGSIZE;wrblock++){
commando[0]= 4; //'Adresse setzen ankündigen
commando[1]= ADDRESS; //'Adresse übergeben
commando[2]= 2; //start
commando[3]= 7; //'Bytes senden
commando[4]= 2; //'Anzahl
commando[5]= wrblock; //Block
commando[6]= senddata[wrblock]; //Wert
commando[7]= 3; //stop
commando[8]= 0; //keine weiteren Parameter
res=I2CResetBus();
res=bbI2CZip(SDA,commando,9,NULL,0);
res=I2CResetBus();
if (res!=0){
printf("S I2C-Fehler: %d, Block: %d\n", res,wrblock);
Fehler++;
wrblock--;
usleep(PAUSEN*3);
if (Fehler==10){
break;
}
}
usleep(PAUSEN);
}
/*
printf("Gesendet:\n");
for(i=0;i<MSGSIZE;i++){
printf("%d ",senddata[i]);
}
printf("\n");
*/
}
void lesen() {
int res=0;
// int i=0;
int rdblock=0;
int Fehler=0;
for (rdblock=0;rdblock<MSGSIZE;rdblock++){
commando[0]= 4; //'Adresse setzen ankündigen
commando[1]= 4; //'Adresse übergeben
commando[2]= 2; //start
commando[3]= 7; //'Bytes senden
commando[4]= 1; //'Anzahl
commando[5]= rdblock+10; //Byte
commando[6]= 2; //start
commando[7]= 6; //Bytes lesen
commando[8]= 2; //Anzahl
commando[9]= 3; //stop
commando[10]= 0; //keine weiteren Parameter
res=I2CResetBus();
res=bbI2CZip(SDA,commando,11,puffer,12);
res=I2CResetBus();
if (res==0){
if (puffer[0]==rdblock){
empfdata[rdblock]=puffer[1];
Fehler=0;
}else{
printf("E Falsche Blocknummer. Erwartet: %d, bekommen: %d, Wert: %d\n",rdblock,puffer[0],puffer[1]);
Fehler++;
rdblock--;
if (Fehler==10){
break;
}
}
}else{
printf("E Fehler beim Blockeinlesen. Block: %d\n",rdblock);
Fehler++;
rdblock--;
if (Fehler==10){
break;
}
}
usleep(PAUSEN);
}
printf("Empfangen:\n");
/* for(i=0;i<MSGSIZE;i++){
printf("%d ",empfdata[i]);
}
printf("\n");
*/
}
int vergleichen()
{
unsigned int i=0;
int ret=0;
for (i=0;i<MSGSIZE;i++){
if (empfdata[i]!=senddata[i]){
ret=-1;
break;
}
}
return ret;
}
int main(int argc, char **argv)
{
int res=0;
int i=0;
long dg=1;
long maxdg=0;
unsigned int Fehler=0;
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = term;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
if (gpioInitialise() < 0)
{
printf("Fehler beim Initialisieren von gpio!");
gpioTerminate();
}else{
res=bbI2COpen(SDA,SCL,TAKT);
res=res+gpioSetMode(SDA, PI_INPUT); // Set SDA as input.
res=res+gpioSetMode(SCL, PI_INPUT); // Set SDA as input.
res=res+gpioSetPullUpDown(SDA, PI_PUD_UP); // Sets SDA pull-up.
res=res+gpioSetPullUpDown(SCL, PI_PUD_UP); // Sets SDA pull-up.
if(res==0){
printf("\n\n\nStarte I2C-Test\n\n");
while (!done)
{
srand(time(NULL));
for(i=0;i<MSGSIZE;i++){
senddata[i]=rand() % 255;
}
schreiben();
usleep(5000);
lesen();
usleep(5000);
if (vergleichen()==0){
printf("Daten OK %ld (%ld)\n",dg,maxdg);
Fehler=0;
dg++;
}else{
printf("***************************** Daten fehlerhaft %ld *****************************\n",dg);
Fehler++;
dg=0;
if (dg>maxdg){
maxdg=dg;
}
sleep(1);
if (Fehler==100){
break;
}
}
}
}else{
printf("E Fehler beim Oeffnen\n");
}
}
res=bbI2CClose(SDA);
if (res!=0){
printf("E Fehler beim Schliessen: %d\n", res);
}else{
printf("E Schliessen OK\n");
}
gpioTerminate();
return 0;
}
Mein Fazit:
Ich bleibe bei UART.
-
hallo,
da hast du es ja immerhin ein ganz gewaltiges Stück weiter geschafft als ich! :)
Danke die aber ganz herzlich für dein Engagement und deine ganzen Posts, schon an deinen letzten Codes kann man noch ein Riesen Stück lernen! :cool:
Gruß
Helmut
-
Hallo,
Danke. Ich lass die Codes mal drin, obwohl nicht optimiert und aufgeräumt.
Meinem LA nach scheint der MEGA bzw. der Wire-Code vom ARDUINO Slave mindestens ein bestimmtes Problem nicht zu erkennen und abzufangen. Evtl. läuft es ohne Pegelwandler stabiler.? Das habe ich nicht mehr probiert.
Der BitBang-Code eignet sich gut, um die Toleranz des ARDUINO-Codes, bzw. des MEGA seiner TWI-Einheit auszutesten. Wenn man sich die Bytes anzeigen lässt, indem man den auskommentierten Code wieder aktiviert, dann liegt bei einem Takt von 100.000 oft nur ein Bit von den 240 Bytes quer. Spielt man mit dem Taktwert, dann steigt die Fehlerquote. Man könnte noch einen langsameren Takt probieren, wenn der ARDUINO-Code das unterstützt. Die weiter Oben erwähnten Flanken kann ich ohne Oszi leider nicht analysieren.
Beim Testen kam mir die Ausgabe vom ARDUINO-MEGA etwas verzögert vor. Kann aber auch an den vielen Bytes liegen. Auch möglich, dass da Interrupts zwischen funken. Da wird viel von der Wire-Bibliothek gemacht. Da das wohl seit 10 Jahren unverändert ist, kann man entweder nicht weiter optimieren oder es hat noch keiner wirklich für Dauereinsätze gebraucht. Im ARDUINO-Forum habe ich nicht gestöbert.
Interessant wäre der Bascom-Slave Code. Allerdings kostet die Bibliothek extra. Das macht für einen reinen kurzen Test für mich keinen Sinn. Oder halt die Datenblätter durchstöbern und selbst was programmieren.
-
Hallo,
es geht nur zuverlässig mit der BitBang-Methode.
Folgende Codes laufen bei mir seit einiger Zeit fehlerfrei durch:
MEGA
Code:
// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>
// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this
// Created 29 March 2006
// This example code is in the public domain.
//
#include <Wire.h>
#define REGSIZE 240
byte rspeicher[REGSIZE];
byte wspeicher[REGSIZE];
byte puffer[5];
byte Register=1;
byte block=0;
byte pos=0;
byte fertig=0;
long dg=0;
long timeout=0;
void setup() {
Wire.begin(4); // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register event
Serial.begin(57600); // start serial for output
Serial.println();
Serial.println("Starte I2C-Test");
}
byte chksum(byte arr[], byte anzahl)
{
byte i;
byte sum=0;
for(i = 0; i < anzahl; i++){
sum+=arr[i];
}
return sum;
}
void loop() {
byte i=0;
delay(100);
timeout++;
if (timeout==90){
timeout=0;
Wire.begin(4);
Serial.println("TimeOut!");
dg=0;
}
if (fertig !=0){
fertig=0;
timeout=0;
dg++;
Serial.println(dg);
for (i=0;i<REGSIZE;i++){
Serial.print(rspeicher[i]);
Serial.print (" ");
}
Serial.println();
}
}
void TWIRESET(){
Wire.begin(4);
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
byte x[4];
byte i;
if(howMany == 4){
for (i=0;i<4;i++){
x[i]=Wire.read();
Serial.print (x[i]);
Serial.print (" ");
}
if (x[0]==137){
if (chksum(x,3)==x[3]){
if (x[1]<REGSIZE){
block=x[1];
rspeicher[block]=x[2];
wspeicher[block]=rspeicher[block];
}
if (x[1]==REGSIZE-1){
fertig=1;
}
Serial.println(" - Summe OK");
}else{
Serial.println(" - Summe fehlerhaft");
}
}
}else{
if (howMany==2){
for (i=0;i<2;i++){
x[i]=Wire.read();
if (x[0]==87 && x[1]==56){
TWIRESET();
}
}
}else{
Serial.print ("Anzahl Daten: ");
Serial.println(howMany);
while (0 < Wire.available()) { //überflüssiges lesen
x[4]=Wire.read();
}
}
}
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
byte i;
puffer[0]=block;
puffer[1]=wspeicher[block];
puffer[2]=chksum(puffer,2);
Wire.write(puffer,3);
for (i=0;i<3;i++){
Serial.print (puffer[i]);
Serial.print (" ");
}
Serial.println();
}
Raspi:
Code:
// gcc -Wall -pthread -o /var/scripte/bbi2cmaster1012 /var/scripte/bbi2cmaster1012.c -lpigpio -lrt
// /var/scripte/bbi2cmaster1012
//I2C mit pipgio
//peterfido
//V1.012
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pigpio.h>
#define SDA 2
#define SCL 3
#define TAKT 100000
#define PAUSEN 5000
#define ADDRESS 0x04
#define MSGSIZE 240
char empfdata[MSGSIZE]; //
char senddata[MSGSIZE];
char commando[20];
char puffer[12];
unsigned int ARDUPOS=0;
unsigned int laenge=0;
volatile sig_atomic_t done = 0;
void term(int signum)
{
done = 1;
}
int I2CResetBus(void)
{
char i;
int res=0;
if(gpioRead(SDA) && gpioRead(SCL)) {
return 0;
}
gpioSetMode(SCL, PI_INPUT);
gpioSetMode(SDA, PI_INPUT);
gpioSetPullUpDown(SDA, PI_PUD_UP); // Sets SDA pull-up.
gpioSetPullUpDown(SCL, PI_PUD_UP); // Sets SDA pull-up.
usleep(50);
if(!gpioRead(SCL)) {
return -1; /* SCL wird extern auf Low gehalten, nix zu machen */
}
for(i = 0; i<9; i++) {
gpioSetMode(SCL, PI_OUTPUT);
gpioWrite(SCL, 0);
usleep(50);
gpioSetMode(SCL, PI_INPUT);
gpioSetPullUpDown(SCL, PI_PUD_UP); // Sets SDA pull-up.
usleep(50);
if(gpioRead(SDA)) {
break; /* SDA ist high STOP */
}
} /* for */
if(!gpioRead(SDA)) {
return -1;
}
commando[0]= 3; //stop
commando[1]= 0; // Fertig
res=bbI2CZip(SDA,commando,2,NULL,0);
if (res!=0){
printf("Fehler beim Reset!\n");
}
if(gpioRead(SDA) && gpioRead(SCL)){
return 0;
}else{
return -1;
}
}
char chksum(char arr[], int anzahl)
{
int i;
char sum=0;
for(i = 0; i < anzahl; i++){
sum+=arr[i];
}
return sum;
}
int syncen() {
int erg=0;
int res=0;
int i=0;
commando[0]= 4; //'Adresse setzen ankündigen
commando[1]= 4; //'Adresse übergeben
commando[2]= 2; //start
commando[3]= 7; //'Bytes senden
commando[4]= 4; //'Anzahl
commando[5]= 137; //Position
commando[6]= ARDUPOS; //Position
commando[7]= senddata[ARDUPOS]; //Position
commando[8]= chksum(&commando[5],3); //Position
commando[9]= 3; //stop
commando[10]= 0; //keine weiteren Parameter
printf("Gesendet: ");
for(i=6;i<8;i++){
printf("%d ",commando[i]);
}
printf("\n");
res=I2CResetBus();
res=bbI2CZip(SDA,commando,20,puffer,12);
usleep(50000);
commando[0]= 4; //'Adresse setzen ankündigen
commando[1]= 4; //'Adresse übergeben
commando[2]= 2; //start
commando[3]= 6; //Bytes lesen
commando[4]= 3; //Anzahl
commando[5]= 3; //stop
commando[6]= 0; //keine weiteren Parameter
res=I2CResetBus();
res=bbI2CZip(SDA,commando,20,puffer,12);
printf("Empfangen:");
for(i=0;i<2;i++){
printf("%d ",puffer[i]);
}
if (puffer[2] == chksum(puffer,2)){
printf(" - Summe OK");
}else{
printf(" - Summe fehlerhaft %d",chksum(puffer,2));
}
if (res==3){
if (puffer[0]==ARDUPOS && puffer[2] == chksum(puffer,2)){
empfdata[ARDUPOS]=puffer[1];
erg=0;
}else{
printf(" - E Falsche Blocknummer. Erwartet: %d, bekommen: %d, Wert: %d\n",ARDUPOS,puffer[0],puffer[1]);
erg=1;
}
}else{
printf(" - E Fehler beim Blockeinlesen. Block: %d, Fehler: %d\n",ARDUPOS,res);
erg=1;
}
return erg;
}
void ARDURESET(){
commando[0]= 4; //'Adresse setzen ankündigen
commando[1]= 4; //'Adresse übergeben
commando[2]= 2; //start
commando[3]= 7; //'Bytes senden
commando[4]= 2; //'Anzahl
commando[5]= 87; //RESET-Sequenz
commando[6]= 56; //RESET-Sequenz
commando[7]= 3; //stop
commando[8]= 0; //keine weiteren Parameter
I2CResetBus();
bbI2CZip(SDA,commando,20,NULL,0);
usleep(50000);
}
int main(int argc, char **argv)
{
int res=0;
long dg=1;
long maxdg=0;
unsigned int Fehler=0;
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = term;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
if (gpioInitialise() < 0)
{
printf("Fehler beim Initialisieren von gpio!");
gpioTerminate();
}else{
res=bbI2COpen(SDA,SCL,TAKT);
res=res+gpioSetMode(SDA, PI_INPUT); // Set SDA as input.
res=res+gpioSetMode(SCL, PI_INPUT); // Set SDA as input.
res=res+gpioSetPullUpDown(SDA, PI_PUD_UP); // Sets SDA pull-up.
res=res+gpioSetPullUpDown(SCL, PI_PUD_UP); // Sets SDA pull-up.
srand(time(NULL));
if(res==0){
printf("\n\n\nStarte I2C-Test\n\n");
while (!done)
{
senddata[ARDUPOS]=rand() % 255;
res=syncen();
if (senddata[ARDUPOS]==empfdata[ARDUPOS]){
printf(" - Daten OK %ld (%ld)\n",dg,maxdg);
Fehler=0;
dg++;
}else{
printf(" - Daten fehlerhaft ****************************************** %ld \n",dg);
// ARDURESET();
Fehler++;
dg=0;
if (dg>maxdg){
maxdg=dg;
}
if (Fehler==100){
break;
}
}
if (res==0){
ARDUPOS++;
if (ARDUPOS >= MSGSIZE){
ARDUPOS=0;
//usleep(500000);//Dem ARDUINO Zeit für die Anzeige geben
//sleep(2);//Dem ARDUINO Zeit für die Anzeige geben
}
}
usleep(5000);
}
}else{
printf("E Fehler beim Oeffnen\n");
}
}
res=bbI2CClose(SDA);
if (res!=0){
printf("E Fehler beim Schliessen: %d\n", res);
}else{
printf("E Schliessen OK\n");
}
gpioTerminate();
return 0;
}
Es läuft hier mit Pegelwandler.
Es wird in der Sub 'Sync' (Evtl. sollte diese noch einen anderen Namen bekommen. Der Name Sync ist bei HardwareI2C schon belegt...) bei jedem Aufruf nur ein DatenByte geschrieben und gelesen. Es gibt ein Startbyte und eine Prüfsumme um Kommunikationsfehler zu erkennen.
Wie es sich mit Interrupts auf dem MEGA verträgt, habe ich nicht getestet. Ob die TWIRESET Sub auf dem Mega immer zuverlässig läuft, kann ich nicht sagen. Evtl. kann man die Pausen verkürzen, wenn der MEGA nix weiter zu tun hat oder sie müssen verlängert werden, wenn Fehler z.B. wegen vieler Interrupts auftreten.
Hardware I2C verträgt sich mit dem (meinem) ARDUINO MEGA irgendwie nicht.
-
hallo,
danke für's Update!
das ist aber jetzt noch immer nicht mein ursprünglicher i2c-Job mit je 32 bytes vom Raspi aus abwechselnd vom Arduino lesen / an Arduino schreiben, mit je 10ms delay dazwischen, oder?
Mein Arduino-Slave code muss unbedingt so bleiben wie er war!
Code:
// Arduino code to send/receive byte arrays
// Arduino as an I2C slave
//
// ver. 0.002b
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
#define MSGSIZE 32
byte recvarray[MSGSIZE]; // 0=0xff; 1=chksum; ...data...; MSGSIZE-1=SLAVE_ADDRESS
byte sendarray[MSGSIZE];
volatile int8_t flag=0;
//=====================================================================================
//=====================================================================================
void setup() {
int32_t i=0;
// Serial terminal window
i=115200;
Serial.begin(i);
Serial.print("Serial started, baud=");
Serial.println(i);
// Wire (i2c)
Wire.begin(SLAVE_ADDRESS); // start Arduino as a I2C slave, addr=0x04 (7-bit coded)
Wire.onReceive(receiveData ); // event when master array is sent
Wire.onRequest(sendData ); // event when master requests array to read
memset(sendarray, 0, sizeof(sendarray) ); // init send- and recv arrays
memset(recvarray, 0, sizeof(recvarray) );
Serial.print("I2C init: my slave address= ");
Serial.println(SLAVE_ADDRESS);
Serial.println("I2C init: done.");
Serial.println();
Serial.println("setup(): done.");
}
//=====================================================================================
uint8_t calcchecksum(uint8_t array[]) {
int32_t sum=0;
for(int i=2; i<MSGSIZE; ++i) sum+=(array[i]);
return (sum & 0x00ff);
}
//=====================================================================================
void loop()
{
char sbuf[128];
Serial.println(); Serial.println();
// do something with the received data
// and then do something to build the sendarray [3]...[MSG_SIZE-2]
if (flag==1) {
//debug
sendarray[4] +=1;
}
sendarray[0] = 0xff; // 0 = start: 0xff == msg start flag
sendarray[2] = flag; // 2 = send back msg error flag
sendarray[MSGSIZE-1] = SLAVE_ADDRESS; // end of array: ID check
sendarray[1] = calcchecksum(sendarray); // 1 = calc new chksum
flag=0;
// debug output
sprintf(sbuf, "Sendarr[4]=%4d, [5]=%4d, Recvarr[4]=%4d, [5]=%4d",
sendarray[4], sendarray[5], recvarray[4], recvarray[5]) ;
Serial.println(sbuf);
delay(1); // short break for the cpu and the bus
}
//=====================================================================================
void receiveData(int byteCount) {
int32_t i;
byte val;
while(Wire.available()<MSGSIZE) ; // wait for all bytes to complete
i=0; // init counter var
while(Wire.available()&& (i<MSGSIZE) ) // read all recv array bytes
{
val=Wire.read();
recvarray[i++]=val;
}
// check for transmission error
if( (recvarray[0] == 0xff)
&& (recvarray[1] == calcchecksum(recvarray))
&& (recvarray[MSGSIZE-1] == SLAVE_ADDRESS ) )
flag=1; // data ok
else
flag=127; // data faulty => handle rcv-error => flag =127
}
//=====================================================================================
void sendData(){
// Wire.write writes data from a slave device in response to a request from a master
Wire.write(sendarray, MSGSIZE); // send own byte array back to master..
-
Hallo,
nein. Das ist ein alternatives Protokoll. Getestet mit 240 Nutzbytes. Das kannst Du an Deine Zwecke anpassen und testen. Ich hatte es so verstanden, dass 30 Bytes nicht ausreichen.
Evtl. läuft es mit 32 Bytes immer noch fehlerfrei. Wobei Du die Slave-Adresse nicht mitschicken brauchst, da er ja nur auf seine Slave-Adresse reagiert bzw. reagieren sollte. Aufpassen musst Du darauf, dass nach der Prüfsummenbildung die Bytes nicht mehr verändert werden. Das ist bei dem Testcode nicht der Fall, kann später, wenn der MEGA ein paar Bytes mit anderen ICs austauscht, Zeitprobleme mitbringen. Wenn alles bis zur nächsten senddata-Bildung noch zwischengespeichert werden muss. Da lässt sich Zeit sparen, wenn die Prüfsumme gleich beim Array-Zusammenbau mit berechnet wird.
Ich habe mich mal über den DUE informiert. Dieser spielt in einer ganz anderen Liga als der MEGA. Wenn Du für den ARDUINO ein Display vorgesehen hast, werden die 10 ms-Lücken zwischen I2C-Datenaustausch für Display, die Ansteuerung weiterer IC´s über evtl. UART ISP und I2C sowie Speicherkopiererei und evtl. TimerInterrupts bei den 16MHz des MEGA etwas knapp.
Ich mache es immer umgekehrt. Der Raspi ist der Kommunikative und dient dem AVR per UART als Gateway zu allen sonstiges Gerätchen. Vor Allem Ethernet bzw. WLAN fehlen den AVRs. Leider auch dem DUE, obwohl es der Mikrocontroller quasi mitbringt. Dann übernimmt der Raspi neben dem Webinterface oft noch Lautsprecher und die farbige Anzeige auf Bilderrahmen über lcd4linux per USB.
Deine Projekt kenne ich nicht und weiß nicht, was da zeitkritisches vor sich gehen soll, bzw. warum das Timing so brisant ist.
Das schnellste ist ganz klar SPI. Danach kommt UART, wenn man ein Baudratenquartz hat, danach dann I2C. Parallel ist theoretisch 8 mal so schnell wie UART, wird aber von der Hardware nicht unterstützt und läuft ohne Puffer nicht ganz so zuverlässig. Benötigt aber auch die meisten PortPins. Bei SPI geht Dein Protokoll aber nicht, da bei jedem Senden auch gleichzeitig empfangen wird.
-
achso - sogar 240 statt 32 Bytes - das ist ntl ein dicker Pluspunkt, und dass das SPI-Display hier ein Zeitproblemwerden könnte, stimmt auch, das habe ich noch gar nicht ausgetestet. Ich wollte die ILI9341_due lib verwenden, die soll angeblich auch mit dem Mega laufen - selber gemacht habe ich es damit aber nicht, nur mit dem Due. Wäre in der Tat ein weiterer Knackpunkt.
Danke nochmal für die Erklärungen und Tipps!
- - - Aktualisiert - - -
es geht im Prinzip darum, Arduinos sowohl als Sensor-Multiplexer als auch für Fernsteuerungen zu nutzen, und darum, gleichzeitig auch noch weitere spezielle I2c und UART-Sensoren an den Raspi anzuschließen (IMU- GPS, RTC - vllt sogar mal nen LIDAR).
UART alleine reicht dazu nicht aus, weil nur ein "slave" angeschlossen werden kann, bei i2c dagegen dutzende am selben Port. Je mehr Geräte am Bus hängen, desto schneller muss aber jedes einzelne Gerät arbeiten und gesteuert werden können.