PDA

Visualizza versione completa : [c] inviare interi alla porta seriale


Gergio
13-03-2008, 18.01.05
Ciao, sto realizzando un'applicazione che permetta a un dispositivo di comunicare, attraverso la porta seriale, con una macchina linux. Il produttore del dispositivo mi ha fornito anche della documentazione in cui e' specificato il protocollo di comunicazione. La porta dovrebbe essere impostata con una velocita' di 9600, senza bit di parita', 8 bit dati, 2 bit di stop, no crc.
E fin qui dovrei esserci...
Poi viene specificato che il formato deve essere binario e qui sono un po' in difficolta'. Infatti dovrei spedire sulla porta seriale dei dati numeri, ma con il comando write riesco a spedire caratteri.
Mi spiego meglio, con un esempio. Dovrei inviare la sequenza 0, 1 invece invio 48 e 49 (codice ascii corrispondente).

Ho trovato del codice gia' pronto per la gestione della seriale e l'ho modificato un po'
apertura seriale:
int init(char *comname)
{
int com; // Il file per la seriale
struct termios tattr; // Struttura per impostare le caratteristiche della seriale

com = open (comname,O_RDWR | O_SYNC); // Apre il dispositivo (Lettura/scrittura, sincrono)

if (com == -1) // Open ritorna errore
{
perror ("Non posso aprire la seriale"); // Stampa il messaggio di errore
exit (2); // E termina drasticamente con errore
}

/* Impostazione della seriale
* fonte info libc->"Low-Level Terminal Interface"->"Noncanonical Mode Example" */
tcgetattr (com, &tattr); // Recupera gli attributi correnti della seriale

// Modi di ingresso - spegne i flag indicati
tattr.c_iflag &= ~(INLCR|IGNCR|ICRNL|IXON | IXOFF | IXANY);


// Modi di uscita - spegne i flag indicati
tattr.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
/* Modi di controllo - imposta 8 bit (CS8), abilita ricezione (CREAD),
*ignora segnali di controllo (CLOCAL) */
tattr.c_cflag = CS8 | CREAD | CLOCAL | CSTOPB;
tattr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // elimina traduzioni di carateri (ICANON) ed ECHO.
tattr.c_cc[VMIN] = 1; // restituisce dati dopo aver ricevuto 1 carattere
tattr.c_cc[VTIME] = 0; // nessun timeout

cfsetispeed (&tattr, B9600); // Imposta la velocita' di ricezione
cfsetospeed (&tattr, B9600); // Imposta la velocita' di trasmissione

tcsetattr (com, TCSAFLUSH, &tattr); // Imposta i nuovo attributi

return com; // Ritrona l'handle del file aperto
}

e ci scrivo banalmente con write(com,ZERO,1) (Ho definito ZERO come 00)

avete suggerimenti?

Sbavi
13-03-2008, 18.05.03
Tempo fa ho lavorato a qualcosa del genere. Stasera a casa ci do uno sguardo e ti faccio sapere ;)

Gergio
13-03-2008, 23.44.01
grazie :)

LoryOne
14-03-2008, 08.13.09
Forse non ho capito cosa intendi, ma in realtà tu non spedisci 48 e 49, bensì 00110000 e 00110001.
Come ben sai, il tipo dati char impone di allocare 8 bit (1 byte) per carattere ed il buffer della seriale, prima dell'inoltro in accordo con i segnali di controllo di flusso, viene riempito con 8 bit.
E' vero che tu leggi 48 e 49, ma questi sono gli ASCII code di due char.
Di quanti byte si compone un intero ?
Una volta identificati i byte high e low di un int, quest' ultimo può essere inteso come sequenza di char.
Se tu volessi spedire la seguente sequenza: 01001, potresti intenderla come inoltro del char 0,char 1,char 0,char 0,char 1.
In questo caso non avresti inviato un bit per volta, bensì 8 per volta, sebbene il risultato finale possa essere inteso come il valore 1001 binario (I bit meno significativi sono omessi)

Gergio
14-03-2008, 10.43.39
Come ben sai,parole grosse... :p

Seriamente: ok un char sono 8 bit e un intero sono 2 byte.
Ho un foglio di istruzioni, che riporto integralmente: baud: 9600 - no parity - 8 bit - 2 stop bit - formato binarioe poi c'e' questo esempio di comando: 0, 1, 5A(h). Quindi io dovrei inviare sulla seriale 6 byte, corretto?
0 ---> 00 --> 00000000 00000000
1 ---> 01 --> 00000000 00000001
5A --> 00000101 00001010

Il comando write mi accetta il tipo char => cosa invio? Per esempio se invio "Z" (carattere ascii corrispondente a 5A) e' corretto? No perche' sono solo 8 bit? Si', perche' io so che sono 8 bit, ma in realta' viene inviato 5A che sono 16 bit? Nel caso, come faccio a inviare i corrispondenti di 0 e 1 (NUL e SOH)?

LoryOne
14-03-2008, 13.42.37
"Si', perche' io so che sono 8 bit, ma in realta' viene inviato 5A che sono 16 bit?"
Come 16 bit ?
5A è uguale a 90 in decimale e sta tutto in 8 bit che al max possono valere 255
no, quel 5A verrà trasformato in binario.

Gergio
15-03-2008, 18.59.59
mmm ho un po' di confusione: se l'intero sono 2 byte, perche' se io invio l'intero "90", sono solo 8 bit?

=> cmq mi sono accorto dovrei lavorare meno e dormire di piu'...
La soluzione era banale e sotto gli occhi:
- devo inviare un numero sulla seriale
- ho a disposizione una funzione che scrive solo caratteri
- se il numero corrisponde a un carattere ascii stampabile ero arrivato a capire che inviando il carattere corrispondente come codice ascii al numero stesso, sulla seriale avevo la sequenza di bit che mi serviva
- il mio dubbio era: ma se non e' un carattere stampabile? es: come invio il carattere di ESC (ascii 27)?

=>
int c = 27;
write (com, &c, 1)

:wall:

LoryOne
16-03-2008, 14.23.29
"mmm ho un po' di confusione: se l'intero sono 2 byte, perche' se io invio l'intero "90", sono solo 8 bit?"


Non ci siamo per più di una ragione.
Prima di tutto, non è detto che il tipo intero sia di due bytes
Poi devi considerare se è stato dichiarato signed oppure unsigned.
Per conoscere le dimensioni in byte di un tipo in C, io ti consiglio sempre di utilizzare sizeof(tipo).
Lo stesso codice su un hardware a 16 bit ti indicherà 2 bytes per il tipo intero, mentre su un 32 bit lo stesso tipo ti fornirà 4 byte.
Più tardi inserirò un po di codice per farti capire un po meglio, soprattutto come sia possibile che il valore di un int possa essere impostato in base al valore di 2 o più char ;)
Ti sarà chiaro in seguito anche perchè si è scelto il byte come unità di misura di allocazione, sebbene sia comunque possibile ricavare i due nibble di cui si compone agendo sulla manipolazione di bit.

LoryOne
16-03-2008, 16.36.15
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i=0;
char *l,uno,due,tre,quattro;;

l=&i;

l[0]='y';
l[1]='r';
l[2]='o';
l[3]='L';

uno=l[3];
due=l[2];
tre=l[1];
quattro=l[0];

printf("%i necessita di %i bytes\n",i,sizeof(i));
printf("%s","Il mio nome in un IP address ...\n");
printf("%i.%i.%i.%i\n",uno,due,tre,quattro);
printf("%c.%c.%c.%c\n",uno,due,tre,quattro);

system("PAUSE");
return 0;
}

carachi83
08-04-2008, 21.07.03
ciao,
scusate ma sono un principiante del c :(
io dovrei creare una cosa simile a questa!!
io voglio creare un programmino in c con il quale mi collego ad un modem e gli mando dei parametri e lui mi dovrebbe rispondere OK / ERROR / o il valore corrispondente.. in modo da poter modificare i parametri in modo automatico!
non so che librerie usare e come gestire la connessione alla porta COM o USB !!
cambia qualcosa gestirla in Linux o in Windows?
Grazie

Gergio
09-04-2008, 10.52.33
per aprire la seriale puoi usare il codice presente nel primo post (e' una funziona che anche io ho trovato cercando su web e ho riutilizzato per il mio programma), magari modificando, se necessario, i vari parametri.

Per una spiegazione dei vari parametri, guarda qui (http://www.lilik.it/~mirko/gapil/gapilsu163.html#x400-21000010.2.2)

Gergio
09-04-2008, 11.02.57
una differenza che noterai tra windows e linux e' la gestione del "ritorno a capo":
- windows usa \r\n cioe' Carriage Return e Line Feed (ascii 13 e ascii 10)
- Linux usa solo \n (ascii 10)

altre differenze potrebbero esserci se non usi librerie standard

carachi83
10-04-2008, 16.15.08
ho provato sia su win che su linux!
su linux ho provato il primo esempio della pagina e quando vado a compilarlo mi da' errore nel compilatore dicendo:

file not recognized: File format not recognized
collect2: ld returned 1 exit status

mentre su win sembra che inizi la comunicazione con la porta ma non riesco a scriverci sopra pure trattandola come un semplice file!! :(



#include <stdio.h>

int main()
{

/* dichiara lo stream e il prototipo della funzione fopen */
FILE *stream, *fopen();

/* apre lo stream del file */
stream = fopen("com1:", "r");

/* controlla se il file viene aperto */
if (stream == NULL)
{
printf("Non posso aprire il file ");
exit(1);
}else
printf("il file viene aperto correttamente");


}





:((
Hai dei consigli? Grazie

LoryOne
11-04-2008, 08.04.44
Nessun problema, a parte il fatto che sei in lettura e non in scrittura sulla seriale identificata dal BIOS come com1:
Una domanda: Stai per caso emulando Wndows sotto Linux ?