Προς το περιεχόμενο

serial port, C language


dimitris_pa

Προτεινόμενες αναρτήσεις

καλησπερα, ξερεις κανεις πως θα χρησιμοποιησω την serial port στην C ? κυριως την θελω για διαφορες ηλεκτρονικες κατασκευες.

ευχαριστω εκ των πρωτερων!!!

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

καλησπερα, ξερεις κανεις πως θα χρησιμοποιησω την serial port στην C ? κυριως την θελω για διαφορες ηλεκτρονικες κατασκευες.

ευχαριστω εκ των πρωτερων!!!

 

fopen (Αν εισαι σε win καλυτερα CreateFile)

px

>fopen("\\\\.\\COM1","rb");

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

fopen (Αν εισαι σε win καλυτερα CreateFile)

px

>fopen("\\\\.\\COM1","rb");

ευχαριστω, μονο αυτο ? κανενα αλλο παραδειγμα μπορεις να κανεις ? βασικα αυτο δεν ειναι για να περνω δεδομενα ?

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Να μια μαλακιούλα που είχα φτιάξει για να κάνει decode κάποια data από ένα πολύμετρο.

 

>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#define BAUDRATE B2400
#define DEVICE "/dev/ttyUSB0"
#define _POSIX_SOURCE 1
#define FALSE 0
#define TRUE 1
#define NO_BITS 4

volatile int STOP=FALSE;

void quitproc(int sig);
char * calc_bits (unsigned char *bytes, int no_bytes);
void int2char (int bits[], char *s);
void stream_decode(char *s);
int digit(char * digit);
int dot(char *dot1, char *dot2, char *dot3);
char * flags (char ac, char dc, char aut, char dio_tst, char con_chk, char cap, char rel, char hold, char min, char max);
char * units(char micro, char nano, char kilo, char mili, char mega, char percent, char ohm, char amp, char volt, char hz, char celcius);


int main()
{
   int fd, no_bytes;
   struct termios oldtio,newtio;
   unsigned char *buf;
   char *s;

   buf=malloc(256);

   signal(SIGINT,quitproc);

   fd = open(DEVICE, O_RDWR | O_NOCTTY );
   if (fd <0) {perror(DEVICE); exit(-1); }

   tcgetattr(fd,&oldtio); /* save current port settings */

   bzero (&newtio, sizeof(newtio));
   newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
   newtio.c_iflag = IGNPAR;
   newtio.c_oflag = 0;

   /* set input mode (non-canonical, no echo,...) */
   newtio.c_lflag = 0;

   newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
   newtio.c_cc[VMIN]     = 5;   /* blocking read until 5 chars received */

   tcflush(fd, TCIFLUSH);
   tcsetattr(fd,TCSANOW,&newtio);


   while (STOP==FALSE)
   {
       no_bytes = read(fd,buf,14);

       s=calc_bits(buf,no_bytes);

       stream_decode(s);


       //tcflush(fd, TCIFLUSH);

   }
   tcsetattr(fd,TCSANOW,&oldtio);

   close(fd);

   return 0;
}

void stream_decode(char *s)
{
   char ac, dc, aut, un1, minus, micro, nano, kilo, dio_tst, mili, percent,
        mega, con_chk, cap, ohm, rel ,hold, amp, volt, hz, un4, min, un5, celcius, max;
   char * digi1, * digi2, * digi3, * digi4, * dot1, * dot2,* dot3;

   int sign;
   float val;

   digi1=malloc(7);
   digi2=malloc(7);
   digi3=malloc(7);
   digi4=malloc(7);
   dot1=malloc(1);
   dot2=malloc(1);
   dot3=malloc(1);

   ac=s[0];
   dc=s[1];
   aut=s[2];
   un1=s[3];
   minus=s[4];

   s+=5;
   strncpy(digi1,s,7);
   s+=7;
   strncpy(dot1,s,1);
   s+=1;
   strncpy(digi2,s,7);
   s+=7;
   strncpy(dot2,s,1);
   s+=1;
   strncpy(digi3,s,7);
   s+=7;
   strncpy(dot3,s,1);
   s+=1;
   strncpy(digi4,s,7);
   s+=7;

   micro=s[0];
   nano=s[1];
   kilo=s[2];
   dio_tst=s[3];
   mili=s[4];
   percent=s[5];
   mega=s[6];
   con_chk=s[7];
   cap=s[8];
   ohm=s[9];
   rel=s[10];
   hold=s[11];
   amp=s[12];
   volt=s[13];
   hz=s[14];
   un4=s[15];
   min=s[16];
   un5=s[17];
   celcius=s[18];
   max=s[19];

   sign=(minus=='0')?1:-1;

   val = ((digit(digi1)*1000 + digit(digi2)*100 + digit(digi3)*10 + digit(digi4))/(float)dot(dot1,dot2,dot3))*sign;

   printf("%9.3f %s %s\n",val, units(micro,nano,kilo,mili,mega,percent,ohm,amp,volt,hz,celcius), flags (ac,dc,aut,dio_tst,con_chk, cap, rel,hold,min,max));
}

char * units(char micro, char nano, char kilo, char mili, char mega, char percent, char ohm, char amp, char volt, char hz, char celcius)
{
   char * micro_, * nano_, * kilo_, * mili_, * mega_, * percent_, * ohm_, * amp_, * volt_, * hz_, * celcius_;
   char * unit;

   unit=malloc(15);

   micro_=(micro=='0')?"":"u";
   nano_=(nano=='0')?"":"n";
   kilo_=(kilo=='0')?"":"k";
   mili_=(mili=='0')?"":"m";
   mega_=(mega=='0')?"":"M";
   percent_=(percent=='0')?"":"%";
   ohm_=(ohm=='0')?"":"ohm";
   amp_=(amp=='0')?"":"A";
   volt_=(volt=='0')?"":"V";
   hz_=(hz=='0')?"":"Hz";
   celcius_=(celcius=='0')?"":"C";

   strcpy(unit,micro_);
   strcat(unit,nano_);
   strcat(unit,kilo_);
   strcat(unit,mili_);
   strcat(unit,mega_);
   strcat(unit,percent_);
   strcat(unit,ohm_);
   strcat(unit,amp_);
   strcat(unit,volt_);
   strcat(unit,hz_);
   strcat(unit,celcius_);

   return unit;
}

char * flags (char ac, char dc, char aut, char dio_tst, char con_chk, char cap, char rel, char hold, char min, char max)
{
   char * ac_, * dc_, * aut_, * dio_tst_, * con_chk_, * cap_, * rel_, * hold_, * min_, * max_;
   char * flag;

   flag=malloc(100);

   ac_=(ac=='0')?"":"AC ";
   dc_=(dc=='0')?"":"DC ";
   aut_=(aut=='0')?"":"auto ";
   dio_tst_=(dio_tst=='0')?"":"Diode test ";
   con_chk_=(con_chk=='0')?"":"Conductivity test ";
   cap_=(cap=='0')?"":"Capacitance ";
   rel_=(rel=='0')?"":"Relative ";
   hold_=(hold=='0')?"":"Hold ";
   min_=(min=='0')?"":"Min ";
   max_=(max=='0')?"":"Max ";

   strcpy(flag,ac_);
   strcat(flag,dc_);
   strcat(flag,aut_);
   strcat(flag,dio_tst_);
   strcat(flag,con_chk_);
   strcat(flag,cap_);
   strcat(flag,rel_);
   strcat(flag,hold_);
   strcat(flag,min_);
   strcat(flag,max_);

   return flag;
}

int dot(char *dot1, char *dot2, char *dot3)
{
   int dot;

   dot=atoi(dot1)*1000 + atoi(dot2)*100 + atoi(dot3)*10;

   if(!dot)
   {
       dot=1;
   }

   return dot;
}

int digit(char * digit)
{
   int i,num;

   char *segm[10]={"1111101","0000101","1011011","0011111","0100111",
                   "0111110","1111110","0010101","1111111","0111111"};

   num=0;

   for (i=0;i<10;i++)
   {
       if (!strcmp(digit,segm[i]))
       {
           num=i;
           i=10;
       }
   }
   return num;
}


char * calc_bits (unsigned char *bytes, int no_bytes)
{
   int i,j,div;
   int bits[8]={0};
   char *s;
   char *bit_stream;

   bit_stream=malloc(14*4);
   s=malloc(8);
   i=0;


   for (j=0;j<no_bytes;j++)
   {
       div=bytes[j];

       while (div != 0)
       {
           bits[i++] = div % 2;
           div = div/2;
       }

       i=0;

       int2char(bits,s);
       strcat(bit_stream,s);
   }

   return bit_stream;
}

void int2char (int bits[], char *s)
{
   int i;

   for (i=0;i<NO_BITS;i++)
   {
       if (bits[i]==0)
       {
           s[NO_BITS-i-1]='0';
       }else
       {
           s[NO_BITS-i-1]='1';
       }
   }
}

void quitproc(int sig)
{
   printf("\n\nCtrl-C pressed...\n");
   STOP=TRUE;
}

 

 

Και ακόμη ένα που στέλνει ΑΤ εντολές σε modem. Δεν θυμάμαι λεπτομέρειες.

 

>#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <time.h>

#define BUFSIZE (65536+100)
unsigned char readbuf[bUFSIZE];

static struct termios term;
static struct termios gOriginalTTYAttrs;
int InitConn(int speed);

void SendCmd(int fd, void *buf, size_t size)
{

 if(write(fd, buf, size) == -1) {
   fprintf(stderr, "SendCmd error. %s\n", strerror(errno));
   exit(1);
 }
}

void SendStrCmd(int fd, char *buf)
{
 fprintf(stderr,"Sending command to modem: %s\n",buf);
 SendCmd(fd, buf, strlen(buf));
}

int ReadResp(int fd)
{
 int len = 0;
 struct timeval timeout;
 int nfds = fd + 1;
 fd_set readfds;
 int select_ret;

 FD_ZERO(&readfds);
 FD_SET(fd, &readfds);

 // Wait a second
 timeout.tv_sec = 1;
 timeout.tv_usec = 500000;

 fprintf(stderr,"-");
 while (select_ret = select(nfds, &readfds, NULL, NULL, &timeout) > 0)
 {
   fprintf(stderr,".");
   len += read(fd, readbuf + len, BUFSIZE - len);
   FD_ZERO(&readfds);
   FD_SET(fd, &readfds);
   timeout.tv_sec = 0;
   timeout.tv_usec = 500000;
 }
 if (len > 0) {
   fprintf(stderr,"+\n");
 }
 readbuf[len] = 0;
 fprintf(stderr,"%s",readbuf);
 return len;
}

int InitConn(int speed)
{
 int fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY);

 if(fd == -1) {
   fprintf(stderr, "%i(%s)\n", errno, strerror(errno));
   exit(1);
 }

 ioctl(fd, LOCK_EX);
 fcntl(fd, F_SETFL, 0);

 tcgetattr(fd, &term);
 gOriginalTTYAttrs = term;

 cfmakeraw(&term);
 cfsetspeed(&term, speed);
 term.c_cflag = CS8 | CLOCAL | CREAD;
 term.c_iflag = 0;
 term.c_oflag = 0;
 term.c_lflag = 0;
 term.c_cc[VMIN] = 0;
 term.c_cc[VTIME] = 0;
 tcsetattr(fd, TCSANOW, &term);

 return fd;
}
void CloseConn(int fd)
{
   tcdrain(fd);
   tcsetattr(fd, TCSANOW, &gOriginalTTYAttrs);
   close(fd);
}

void SendAT(int fd)
{
 char cmd[5];

 //  SendStrCmd(fd, "AT\r");
 sprintf(cmd,"AT\r");
 SendCmd(fd, cmd, strlen(cmd));
}

void AT(int fd)
{
 fprintf(stderr, "Sending command to modem: AT\n");
 SendAT(fd);
 for (; {
   if(ReadResp(fd) != 0) {
     if(strstr((const char *)readbuf,"OK") != NULL)
     {
break;
     }
   }
   SendAT(fd);
 }
}

int main(int argc, char **argv)
{
 int fd;
 char cmd[1024]; 
 if(argc < 2)
 {
fprintf(stderr,"usage: %s <at command>\n",argv[0]);
fprintf(stderr,"examples:\t%s \"AT+XSIMSTATE=1\"\n",argv[0]);
fprintf(stderr,"\t\t%s \"AT+XGENDATA\"\n",argv[0]);
fprintf(stderr,"\t\t%s \"AT+CLCK=\\\"SC\\\",2\"\n",argv[0]);
exit(1); 
 }
 fd = InitConn(115200);

 AT(fd);
 sprintf(cmd,"%s\r",argv[1]);
 SendStrCmd(fd,cmd);
 ReadResp(fd);
 CloseConn(fd);
 return 0;
}

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

ευχαριστω, γινεται να ρωτησω απο που το εμαθες (π.χ. απο την σχολη, απο βιβλιο) ? τιποτα σε πιο μικρο υπαρχη ? θελω να χρησιμοποιησω μονο το pin 2 receive data (rd)

ξες πως να το κανω ? και τα λινκ δεν με πειραζουν αρκει να ειναι λιτα και να μπαινει στο θεμα.....

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Όταν χρειάστηκα να χρησιμοποιήσω την σειριακή έκανα μία αναζήτηση στο Google και βρήκα πολλά. Μετά άρχισα να διαβάζω τα manual (π.χ. του termios).

 

http://www.easysw.com/~mike/serial/serial.html

http://slackware.osuosl.org/slackware-3.3/docs/mini/Serial-Port-Programming

http://www.comptechdoc.org/os/linux/programming/c/linux_pgcserial.html

http://www.linux-tutorial.info/modules.php?name=Howto&pagename=Serial-Programming-HOWTO/x115.html

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Συμπληρώνοντας τα παραπάνω παραθέτω C-Win32 κώδικα που ανοίγει την σειριακή COM1, σε READ-WRITE mode με

9600 baudrate, 8 data bits, one stop bit, without parity:

 

>
static DCB dcbSerialParams = {0};
static DCB olddcbSerialParams = {0};

extern HANDLE
OpenSerial ( void )
{
   HANDLE hSerial;
   /* open serial port COM1 */
   hSerial = CreateFile("COM1",
                        GENERIC_READ | GENERIC_WRITE,
                        0,
                        0,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        0);
   if(hSerial == INVALID_HANDLE_VALUE) {
       if((error_no = GetLastError()) == ERROR_FILE_NOT_FOUND) {
           fprintf(stderr, "Error: COM1 port not found\n");
       } else {
           fprintf(stderr, "Error: %d\n", error_no);
       }
       perror("CreateFile:");
       system("pause");
       exit (-1);
   } else {
       fprintf(stdout, "COM1 port openned!\n");
   }
   /* set serial communication parameters */
   dcbSerialParams.DCBlength = sizeof(DCB);
   olddcbSerialParams.DCBlength = sizeof(DCB);
   if (!GetCommState(hSerial, &dcbSerialParams)) {
       perror("GetCommState:");
       CloseHandle(hSerial);
       system("pause");
       exit (-1);
   } else {
       olddcbSerialParams = dcbSerialParams;
   }

   dcbSerialParams.BaudRate = CBR_9600;   /* set baudrate */
   dcbSerialParams.ByteSize = 8;          /* select 8 data bits */
   dcbSerialParams.StopBits = ONESTOPBIT; /* set one stop bit */
   dcbSerialParams.Parity = NOPARITY;     /* disable parity */
   
   if(!SetCommState(hSerial, &dcbSerialParams)){
       perror("SetCommState:");
       CloseHandle(hSerial);
       system("pause");
       exit (-1);
   }
   return hSerial;
}

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Σας ευχαριστω!!!

δηλαδη ποιος ειναι ο τροπος ωστε να παρω δεδομενα και να τα επεξεργαστω ?

δηλαδη αν μπορειτε να βαλετε και κανα σχολιο....

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Όταν ανοίξεις την σειριακή καλείς την:

 

Windows:

>
   unsigned char data[32];
   DWORD dwBytesRead;

   if (!ReadFile(hSerial, data, 32, &dwBytesRead, NULL)) /* hSerial serial port file handle */
   {
       fprintf(stderr, "ReadFile error"\n);
   }
   else 
   {
       /* process dwBytesRead bytes stored in data */
   }

 

Unix:

>
   int bytesRead = 0;
   unsigned char data[32];

   bytesRead = read(fd_in, (void*)data, 32); /* fd_in serial port file descriptor */
   if (bytesRead < 0)
   {
       fprintf(stderr, "readerror"\n);
   }
   else 
   {
       /* process bytesRead bytes stored in data */
   }

 

Προσοχή: Στον δικό μου κώδικα όλα τα IO operations είναι blocked (waiting infinite timeout for input). Στο πρώτο κώδικα του firewalker το πρόγραμα περιμένει για ακριβώς 5 χαρακτήρες στο input, ενώ στον δεύτερο κώδικα του firewalker υπάρχει timeout = 0:

 

>
 newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */ 
 newtio.c_cc[VMIN]     = 5;   /* blocking read until 5 chars received */ 

 term.c_cc[VMIN] = 0; 
 term.c_cc[VTIME] = 0; 

Στην δεύτερη περίπτωση, γίνεται και η select (και αυτό με timeout 1 second) πριν καλεστεί η read, για να δοκιμάσει εάν υπάρχουν data στο καλώδιο:

 

>
while (select_ret = select(nfds, &readfds, NULL, NULL, &timeout) > 0) 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

ευχαριστω :) τι κανω include ? εχει κανενα define ?

οταν λες blocked εννοεις οτι ειναι υπο προυποθεσεις (blocking read until 5 chars received, δηλαδη αν ισχυει αυτο κανε αυτο)?

αυτα αν τα κανω 0 παλι ισχυει ?

newtio.c_cc[VTIME] = 0

newtio.c_cc[VMIN] = 5;

 

term.c_cc[VMIN] = 0;

term.c_cc[VTIME] = 0;

 

η βαλω αυτο ?

 

 

while (select_ret = select(nfds, &readfds, NULL, NULL, &timeout) > 0)

 

για να επεξεργαστω τα δεδομενα που πηρα απο την θυρα θα γραψω κωδικα κατω απο την else ?

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Τα includes είναι αυτά που έχει o firewalker στους κώδικές του. Για το αν θα χρησιμοποιήσεις select έιναι δικό σου θέμα αναλόγως το πως θέλεις να υλοποιήσεις και τι θέλεις να κάνει το πρόγραμμα σου. Σημαντικό ρόλο παίζει και το άλλο άκρο της σειριακής επικοινωνίας. Πώς περιμένει τα δεδομένα, με ποιά ταχύτητα κ.λ.π.Διάβασε πρώτα και κανένα tutorial από αυτά που παρέθεσε o firewalker παραπάνω.

 

term.c_cc[VMIN]

term.c_cc[VTIME]

 

VMIN > 0, VTIME > 0:

Εάν πάρεις VMIN χαρακτήρες πρίν λήξει ο χρόνος VTIME, τότε η read επιστρέφει με VMIN χαρακτήρες. Εάν περάσει VTIME χρόνος τότε η read επιστρέφει με όσους χαρακτήρες έχει διαβάσει.

 

VMIN > 0, VTIME = 0:

Η read δεν επιστρέφει εάν δεν έχουν διαβάστεί VMIN χαρακτήρες.

 

VMIN = 0, VTIME > 0:

Η read επιστρέφει όταν πάρει έναν χαρακτήρα ή όταν περάσει VTIME χρόνος (με όσους χαρακτήρες πήρε, μπορεί και κανέναν)

 

VMIN = 0, VTIME = 0:

Η read επιστρέφει αμέσως, με όσους χαρακτήρες βρήκε εκείνη τη στιγμή (μπορεί και κανέναν)

 

Υ.Γ. Ναι μετά την else επεξεργάζεσαι τα δεδομένα.

 

Υ.Γ.2 Η select έχει άλλη φιλοσοφία: δοκιμάζει να δεί εάν υπάρχουν δεδομένα στο καλώδιο, και άν υπάρχουν τότε καλείς την read.

 

Υ.Γ.3 VTIME είναι σε δέκατα του δευτερολέπτου.

 

Άλλο ένα παράδειγμα σε UNIX

>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>
#include <termios.h>

#define ERROR (-1)
#define OK (0)

int main(void)
{
 int i;
 int result;
 struct termios oldtio, newtio;
 int serialport = 0;
 char buff [32] = "qwertyuiopasdfghjklzxcvbnm123456";
 ssize_t n, k;

 serialport = open("/dev/ttya", O_RDWR | O_NOCTTY);
 if (serialport == ERROR) {
   perror("open:");
 }
 result = tcgetattr(serialport, &oldtio); /* save current port settings */
 bzero(&newtio, sizeof(newtio));
 if (result == ERROR) {
   perror("tcgetattr:");
   close(serialport);
   return (-1);
 }
 fprintf(stdout, "/dev/ttya port oppened!\n");

 result = cfsetispeed(&newtio, B9600);
 if (result == ERROR) {
   perror("cfsetispeed:");
   close(serialport);
   return (-1);
 }
 result = cfsetospeed(&newtio, B9600);
 if (result == ERROR) {
   perror("cfsetospeed:");
   close(serialport);
   return (-1);
 }

 newtio.c_cflag &= ~PARENB; /* disable parity */
 newtio.c_cflag &= ~CSTOPB; /* set one stop bit */
 newtio.c_cflag &= ~CSIZE;  /* mask the character size bits */
 newtio.c_cflag |= CS8;     /* select 8 data bits */
 newtio.c_cflag |= CLOCAL;  /* set device locally attached */
 newtio.c_cflag |= CREAD;   /* enable receiver */

 /* newtio.c_cflag &= ~(CRTSCTS); */
 /* newtio.c_iflag &= ~(IXON | IXOFF | IXANY); */

 /* newtio.c_iflag |= CRTS_IFLOW; */
 /* newtio.c_iflag |= (IXON | IXOFF | IXANY); */

 newtio.c_iflag = IGNPAR; /* ignore parity */

 newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* raw input */

 newtio.c_cc[VTIME] = 0;
 newtio.c_cc[VMIN] = 1;

 result = tcsetattr(serialport, TCSANOW, &newtio);
 if (result == ERROR) {
   perror("tcsetattr:");
   close(serialport);
   return (-1);
 }
 n = write(serialport, (const void *)buff, 32);
 if (n < 0) {
   perror("write:");
 } else {
   fprintf(stdout, "bytes written %d\n", n);
   n = read(serialport, (void *)buff, 32);
   if (n < 0 )  {
     perror("read:");
   } else {
     fprintf(stdout, "bytes read %d\n", n);
     for(i=0;i<n;i++)
       fprintf(stdout, "%c", buff[i]);
     fprintf(stdout, "\n");
   }
 }
 /* restore old serial port options */
 result = tcsetattr(serialport, TCSANOW, &oldtio);
 if (result == ERROR) {
   perror("tcsetattr:");
 }
 close(serialport);
 fprintf(stdout, "/dev/ttya port closed!\n");
 return (0);
}

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Αρχειοθετημένο

Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.

  • Δημιουργία νέου...