|
|
|
||||||||
| Arcade | Registrazione | Blogs | Regolamento | Feedback | FAQ | Lista Utenti | Calendario | Segna come Letti |
| Ultimi 5 blog pubblicati su PcTuner Blog | ||||||
|
||||||
![]() |
|
|
Strumenti Discussione | Modalità Visualizzazione |
|
|
#1 |
|
Registered User
|
PIC 18F4520 - problemi con la seriale
Ciao a tutti, mi sono appena iscritto e premetto che mi sono dato da poco alla programmazione PIC...
Mi sono inceppato in un paio di problemi che non riesco a risolvere nonostante una ricerca in rete approfondita... Sto programmando un PIC 18F4520 in C (compilatore CCS) con lo scopo di interpretare delle stringhe ricevute via RS232 da un computer e attivare o disabilitare delle uscite in funzione di queste stringhe. ********** Problema 1: su un pc con porta COM integrata la comunicazione funziona perfettamente nei due sensi, mentre sul mio portatile con adattatore USB-RS232 (link al modello) riesco solamente a ricevere sul pc ma non è possibile mandare niente al pic. Solo saltuariamente quando premo ripetutamente dei caratteri nell'HyperTerminal il PIC riceve qualcosa ma sbagliato... Le impostazione dell'HyperTerminal sono sicuro che sono giuste, infatti sul pc con COM integrata funziona ![]() Qualcuno sa da cosa può essere causato? ********** Problema 2: Presupponiamo che la COM del problema 1 funzioni... Dal mio programma sul pc invio delle stringhe di 11 caratteri, sul pic controllo che questa stringa è uguale a dei comandi definiti ed attiva in funzione del risultato delle uscite. In principio è un programma banale!! Con l'HyperTerminal funziona tutto alla grande, mentre se invece faccio partire il mio programma sul pc il PIC mi si blocca dopo aver ricevuto 2 o 3 caratteri ![]() Ora, il programma dal pc invia le stringhe in maniera successiva (8 stringhe da 11 caratteri) e da quello che ho capito il buffer del PIC si "intasa" e tutto si blocca ![]() Sapreste darmi un consiglio? ********** Codice provvisorio Codice:
#include <18F452.h>
#include <string.h>
#use delay(clock=4000000) // prima di #use rs232!!
#use rs232(baud=9600, xmit=pin_C6, rcv=pin_C7, ERRORS) // imposta rs232
#include <lcd_new.c>
#define TIMER_RELAOD (65536-2500) // set timer to 2.0us * 2500 = 5 ms
#define MAX 11 // lunghezza massima della stringa (senza \0)
#use fast_io(B)
#byte portB = 0xF81
char stringa[MAX+1]; // stringa contenente i caratteri ricevuti
int index = 0; // indice del prossimo carattere da ricevere
int string_ready = 0; // a 1 quando la stringa è completa con MAX caratteri
int received = 0;
long counter = 0;
int char_old2 = 0; // penultimo carattere letto
int char_old1 = 0; // ultimo carattere letto
int char_old0 = 0; // carattere attuale
#INT_TIMER0 // la funzione che segue è quella che viene chiamata dall'interrupt
interrupt(){
set_timer0(TIMER_RELAOD); // reinizializza il contatore a (65536-2500)
counter++;
if(counter == 200){ // 200 x 5ms = 1s
printf("%d",RS232_ERRORS);
counter = 0;
}
}
#INT_RDA
receive(){
stringa[index] = getc();
received = 1;
}
main(){
char stringa_1[12] = {"sez01chiuso"};
char stringa_2[12] = {"sez01aperto"};
char stringa_3[12] = {"sez02chiuso"};
char stringa_4[12] = {"sez02aperto"};
char stringa_5[12] = {"sez03chiuso"};
char stringa_6[12] = {"sez03aperto"};
char stringa_7[12] = {"sez04chiuso"};
char stringa_8[12] = {"sez04aperto"};
char stringa_9[12] = {"sc00deviata"};
char stringa_10[12] = {"sc00diritto"};
char stringa_11[12] = {"sc01deviata"};
char stringa_12[12] = {"sc01diritto"};
int i;
set_tris_B(0x00);
portB = 0x00;
lcd_init();
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_2); //velocità clock interrupt: RTCC_DIV_2 -> f = 4MHz/4/2 = 0.5MHz -> T = 2.0us
set_timer0(TIMER_RELAOD); // ogni volta che TIMER_RELAOD passa i 65536 viene chiamata la funzione interrupt()
enable_interrupts(INT_TIMER0); // attiva l'interrupt Timer0
enable_interrupts(INT_RDA); // attiva l'interrupt RDA
enable_interrupts(GLOBAL); // abilita tutti gli interrupt, senza di questo non ne va neanche uno.
printf(lcd_putc,"\fALIVE");
printf("ALIVE");
while(1){
//-----------------------------
// creazione stringa
//-----------------------------
if(received == 1){
received = 0;
if(index == 0)
string_ready = 0;
stringa[index+1] = '\0';
printf(lcd_putc,"\f%s",stringa);
char_old2 = char_old1;
char_old1 = char_old0;
char_old0 = stringa[index];
if(char_old0 == char_old1 && char_old0 == char_old2){ // RESET
index = 0;
printf("RESET // ");
printf(lcd_putc,"\fRESET");
char_old2 = 0;
char_old1 = 0;
char_old0 = 0;
}
else
if(index<(MAX-1)){
index++;
}
else{
index = 0;
string_ready = 1;
printf("Stringa = %s // ",stringa);
printf(lcd_putc,"\nCompleted");
}
}
//-----------------------------
// comparazione stringa
//-----------------------------
if(string_ready == 1){
if(strcmp(stringa, stringa_1)==0) // sezionamento 1
output_bit(PIN_B0, 0x01);
else if(strcmp(stringa, stringa_2)==0)
output_bit(PIN_B0, 0x00);
else if(strcmp(stringa, stringa_3)==0) // sezionamento 2
output_bit(PIN_B1, 0x01);
else if(strcmp(stringa, stringa_4)==0)
output_bit(PIN_B1, 0x00);
else if(strcmp(stringa, stringa_5)==0) // sezionamento 3
output_bit(PIN_B2, 0x01);
else if(strcmp(stringa, stringa_6)==0)
output_bit(PIN_B2, 0x00);
else if(strcmp(stringa, stringa_7)==0) // sezionamento 4
output_bit(PIN_B3, 0x01);
else if(strcmp(stringa, stringa_8)==0)
output_bit(PIN_B3, 0x00);
string_ready = 0;
}
}
}
|
|
|
|
|
|
#2 |
|
Registered User
|
Per il problema 1 non so che dirti...
__________________Per il secondo, quando invii i caratteri da hyperterminal c'è un'ampia pausa tra uno e l'altro, mentre nella tua applicazione i caratteri vengono iniviati in rapida successione; evidentemente il tuo programma non riesce a star dietro a questa velocità. Non capisco bene quale sia il meccanismo con cui funziona il tuo programma, ma tanto per cominciare, non vedo perché non incrementi "index" direttamente nella ISR, in modo tale che se arriva un altro carattere questo venga accodato nell'interrupt. Se le stringhe sono tutte di pari lunghezza, ad esempio, potresti effettuare un rapido controllo nella ISR che la stringa sia completamente ricevuta e disabilitare l'interrupt. L'importante è che la ISR abbia una durata tale da garantire la ricezione di caratteri in rapida successione. Un'altra cosa che personalmente eviterei, è l'uso di stringhe come "chiuso", "aperto", ecc. dal momento che il PIC non capisce l'italiano, questo comporta soltanto uno spreco di memoria e tempo di computazione; a seconda del numero di comandi, potrebbero bastare semplicemente uno o due caratteri. Volendo potresti usare i meccanismi di sincronizzazione hardware della seriale (CTS/RTS) |
|
|
|
|
|
#3 |
|
Registered User
|
Grazie dei consigli!
Il programma non fa altro che ricevere dei caratteri e collocarli nella prossima posizione della stringa. Se ci sono 3 caratteri uguali di fila esegue un reset della stringa stessa (mette l'index a 0). Quando la stringa è completa (sono tutte lunghe uguali) le compara con le altre e attiva le rispettive uscite. Effettivamente le stringhe potrei anche accorciarle, ho messo dei testi in italiano per renderle più maneggiabili durante lo sviluppo del programma su pc. All'interno della ISR ho messo solo lo stretto indispensabile per velocizzare il tutto, ma mi sa che non serve a niente perchè comunque il numero di cicli da fare (sia nella ISR che nel while(1)) per comparare la stringa sono sempre gli stessi ![]() Al momento uso un quarzo da 4MHz, che mi pare che permetta 208 cicli tra la ricezione di un carattere e l'altro a 9600 baud... Cosa intendi "meccanismi di sincronizzazione hardware"? P.S: esistono forse metodi più diretti per collocare i caratteri ricevuti in una stringa?? |
|
|
|
|
|
#4 |
|
Registered User
|
Trovo sbagliatissimo incrementare la variabile index nel main, infatti nel ciclo infinito hai messo un sacco di cose, tra cui la scrittura sul display LCD che richiede molto tempo, perciò, da quando il carattere viene ricevuto a quando la variabile viene incrementata, possono passare diversi millisecondi, e quindi i byte ricevuti non vengono memorizzati nella locazione giusta.
__________________Per sincronizzazione intendevo l'uso dei segnali RTS/CTS della RS232 che permettono di segnalare al PC se il PIC può ricevere e viceversa. |
|
|
|
|
|
#5 |
|
Registered User
|
Non sto ad analizzare tutto ma di passo alcune dritte per velocizzare le cose.
__________________index, perché definirlo come int quando come char è più che sufficiente. Questo vale anche per altre variabili in cui non sia strettamente necessario l'uso dell'intero (es. received). MAX, anche se ti occorrono solo 11 posizioni è meglio dimensionarlo come un quadrato di 2 per facilitare il controllo di sconfinamento. Codice:
Io faccio così per non scordarmi di questo. #define MAX (2*2*2*2) // lunghezza massima della stringa (senza \0) Codice:
++index;
index &= MAX-1; // Per non sconfinare.
Camillo
Internet ti fa vedere tutto ma non ti fa toccare niente. (Camillo Ferrari) |
|
|
|
|
|
#6 |
|
Registered User
|
Grazie Camillo, ma non ho mica capito perché usare una potenza di 2
![]() Come si fa ad usare due buffer? L'interrupt #INT_RDA rimane sempre uno solo? Ho cercato un po' in giro ma non ho trovato niente (non so neanche con che parole chiave cercare...). Scusate, ma sono veramente alle prime armi con i microprocessori ![]() P.S: int e char non sono tutti e due di 8 bit? |
|
|
|
|
|
#7 | |
|
Registered User
|
Quote:
Un uso potrebbe essere: finito di ricevere il pacchetto nel primo buffer lo copio sul secondo così lascio il primo libero per ricevere un nuovo pacchetto mentre analizzo i dati nel secondo. Un'altro uso è quello alternativo (si possono usare anche più buffer) metto i dati nel primo buffer e quando ricevo il terminatore carico la ricezione sul secondo e vado ad analizzare il primo. L'int è generalmente da 16 bit anche se dipende dal compilatore. Sarebbe meglio utilizzare "unsigned int" a meno che non si vada negativi. Occorre anche assicurarsi che il compilatore utilizzi i char non segnati.
Camillo
Internet ti fa vedere tutto ma non ti fa toccare niente. (Camillo Ferrari) |
|
|
|
|
|
|
#8 |
|
Registered User
|
Nel mio compilatore "int" è di 8bit (ccs), "char" non lo so, dovrei controllare...
Quello che mi proponevi di fare con i due buffer è chiaro, quello di cui non ho la minima idea è come creare e gestire un buffer nel pic O magari si tratta semplicemente di un array? In C su un computer so come creare ad esempio un FIFO, ma sono almeno una 20ina di righe di codice, e se il mio scopo è quello di velocizzare il tutto non mi sembra che abbia molto senso... |
|
|
|
|
|
#9 | |
|
Registered User
|
Quote:
Per buffer intendo array monodimensionale visto che deve contenere dei caratteri o dei byte. Il FIFO è una buona idea a patto che sia abbastanza profondo da non mangiarsi la coda. Non penso che ci vogliano una 20ina di righe ma penso che mezza dozzina bastino. In C il concetto di riga è piuttosto labile, tutto un programma per il compilatore è una sola riga. La velocità di esecuzione è legata anche all'efficienza di traduzione del compilatore per cui non sempre quello che potrebbe parere corto è anche più veloce. Per capirne qualcosa si deve analizzare il listato generato dal compilatore.
Camillo
Internet ti fa vedere tutto ma non ti fa toccare niente. (Camillo Ferrari) |
|
|
|
|
|
|
#10 | |
|
Registered User
|
Quote:
Oggi ho introdotto un intervallo di 10ms tra l'invio di un byte e l'altro nel mio programma nel pc per lasciare il tempo al pic di elaborare i dati in arrivo, ma va ancora tutto in tilt ![]() |
|
|
|
|
|
|
#11 | |
|
Registered User
|
Quote:
Non ho mai usato il tipo 'long' ma solo char e int segnato e non segnato. Non ho mai utilizzato, finora, frequenze oltre i 4MHz. Non ho mai utilizzato le routine di Delay() per tempi che ritengo troppo lunghi. Come fai a dire che una variabile va in overflow? Se 10ms non ti bastano (a 4MHz sono 10000 istruzioni) hai un problema profondo o nella tua logica o nel tuo compilatore.
Camillo
Internet ti fa vedere tutto ma non ti fa toccare niente. (Camillo Ferrari) |
|
|
|
|
|
|
#12 | |
|
Registered User
|
Se la aumento sempre di uno a 128 diventa -127... (signed int)
Quote:
![]() |
|
|
|
|
|
|
#13 | |
|
Registered User
|
Quote:
Non penso di dar la colpa all'implementatore del compilatore, se è così sarebbe troppo!
Camillo
Internet ti fa vedere tutto ma non ti fa toccare niente. (Camillo Ferrari) |
|
|
|
|
|
|
#14 |
|
Registered User
|
lo leggo con un printf sull'hyperterminal. Sono sicuro che pure il mio docente me l'aveva spiegato così. Non so se definirla "colpa del compilatore", se lo so faccio attenzione...
Un altro esempio è nel codice che ho postato nel primo post: Codice:
#INT_TIMER0 // la funzione che segue è quella che viene chiamata dall'interrupt
interrupt(){
set_timer0(TIMER_RELAOD); // reinizializza il contatore a (65536-2500)
counter++;
if(counter == 200){ // 200 x 5ms = 1s
printf("%d",RS232_ERRORS);
counter = 0;
}
}
|
|
|
|
|
|
#15 | |
|
Registered User
|
Quote:
Rimango convinto che l'int è da 16 bit. Quel printf all'interno dell'interrupt ci sta proprio male. Negli interrupt bisogna starci il meno possibile.
Camillo
Internet ti fa vedere tutto ma non ti fa toccare niente. (Camillo Ferrari) |
|
|
|
|
|
|
#16 |
|
Registered User
|
Il manuale del compilatore dice che char è unsigned 8 bit, int è 8bit e long 16bit
__________________ |
|
|
|
|
|
#18 | |
|
Registered User
|
Quote:
Con queste premesse non oso pensare all'ottimizzazione del codice. Non dico ANSI ma almeno seguire K&R.
Camillo
Internet ti fa vedere tutto ma non ti fa toccare niente. (Camillo Ferrari) |
|
|
|
|
|
|
#19 |
|
Registered User
|
Stamattina ho guardato il manuale... effettivamente è così!
![]() Tornando al mio problema... ieri sera miracolosamente ha funzionato! Il problema è che non so come mai adesso funziona e prima no, il programma è praticamente ancora quello del primo post ![]() Mi rimane solo il problema del RS232-converter, che proprio non c'è verso di risolvere... |
|
|
|
|
|
#20 |
|
Registered User
|
Che neeervi
![]() Adesso il pic non si blocca più, ma se dal computer invio 2 stringhe di fila, la seconda non viene letta correttamente, nonostante tra una stringa e l'eltro invio una stringa "xxx" che azzera la stringa ricevuta nel pic (per evitare che un errore si trascini all'infinito). Il motivo è che la variabile "string_ready" rimane a 1 fino a quando la stringa precedente non viene elaborata, e durante questo tempo tutto quello che arriva va perso... Nella funzione in C++ sul computer che invia i segnali sulla COM ho modificato i parametri "sDcb.fDtrControl" e "sDcb.fRtsControl" impostandoli su "disabilitato", mentre il parametro "sTo.WriteTotalTimeoutMultiplier= 12000/Baud+1;" ho provato ad aumentarlo fino a "sTo.WriteTotalTimeoutMultiplier= 12000/Baud+100;", ma non cambia niente ![]() Adesso sto pensando di inserire una funzione "wait()" nel mio programma in C, ma non mi sembra una soluzione molto pulita... Avete altre proposte o suggerimenti? Adesso sto provando ad aumentare il quarzo da 4 a 20MHz... Questo è il mio codice, migliorato rispetto all'originale: Codice:
#include <18F452.h>
#include <string.h>
#use delay(clock=4000000) // prima di #use rs232!!
#use rs232(baud=9600, xmit=pin_C6, rcv=pin_C7, ERRORS) // imposta rs232
#include <lcd_new_20mhz.c>
#define MAX 7 // lunghezza massima della stringa (senza \0)
#use fast_io(B)
#byte portB = 0xF81
char string[MAX+1]; // stringa contenente i caratteri ricevuti
int index = 0; // indice del prossimo carattere da ricevere
int string_ready = 0; // a 1 quando la stringa è completa con MAX caratteri
int char_old2 = 1; // penultimo carattere letto
int char_old1 = 2; // ultimo carattere letto
int char_old0 = 3; // carattere attuale
#INT_RDA
receive(){
string[index] = getc();
char_old2 = char_old1;
char_old1 = char_old0;
char_old0 = string[index];
if(index<(MAX-1) && string_ready == 0){
index++;
}
else{
string[index+1] = '\0';
index = 0;
string_ready = 1;
}
}
main(){
char string_received[8];
char string_1[8] = {"sc00dir"};
char string_2[8] = {"sc00dev"};
char string_3[8] = {"sc01dir"};
char string_4[8] = {"sc01dev"};
int i;
set_tris_B(0x00);
portB = 0x00;
lcd_init();
enable_interrupts(INT_RDA); // attiva l'interrupt RDA
enable_interrupts(GLOBAL); // abilita tutti gli interrupt, senza di questo non ne va neanche uno.
printf(lcd_putc,"\fREADY...");
printf("READY...");
while(1){
//--------------------------------------------------
// reset con 3 caratteri uguali di seguito
//--------------------------------------------------
if(char_old0 == char_old1 && char_old0 == char_old2){
index = 0;
printf("READY...");
printf(lcd_putc,"\nREADY...");
char_old2 = 1;
char_old1 = 2;
char_old0 = 3;
string_ready = 0;
}
//--------------------------------------------------
// comparazione stringa
//--------------------------------------------------
if(string_ready == 1){
strcpy(string_received,string);
printf(lcd_putc,"\f%s",string_received);
string_ready = 0;
if(strcmp(string_received, string_1)==0){ // scambio 00 diritto
output_bit(PIN_B0, 0x01);
output_bit(PIN_B1, 0x00);
}
else if(strcmp(string_received, string_2)==0){ // scambio 00 deviata
output_bit(PIN_B0, 0x00);
output_bit(PIN_B1, 0x01);
}
else if(strcmp(string_received, string_3)==0){ // scambio 01 diritto
output_bit(PIN_B2, 0x01);
output_bit(PIN_B3, 0x00);
}
else if(strcmp(string_received, string_4)==0){ // scambio 01 deviata
output_bit(PIN_B2, 0x00);
output_bit(PIN_B3, 0x01);
}
}
}
}
Codice:
//*****************************************************************************
//*
//* ComOpen
//*
//*****************************************************************************
// Öffnet eine serielle Verbindung
// Nr : Ist die Nummer des Com-Ports (0=COM1 1=COM2 ...)
// Baud : Ist die Bautrate
// Parity : 0 = kein Parity Bit
// 1 = gerade
// 2 = ungerade
// 3 = immer 0
// 4 = immer 1
// Stopbits : 0 = Ein Stopbit
// 1 = Ein/einhalb Stopbits
// 2 = Zwei Stopbits
// Bits : 0 = 7 Datenbits
// 1 = 8 Datenbits
// 7 = 7 Datenbits
// 8 = 8 Datenbits
// Ergibt 1 wenn eine Schnittstelle geöffnet wurde
int ComOpen(unsigned Nr,int Baud,int Parity,int Stopbits,int Databits){
static const int iPMode[]={NOPARITY,EVENPARITY,ODDPARITY,SPACEPARITY,MARKPARITY};
static const int iSMode[]={ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS,ONESTOPBIT};
char cName[]="\\\\.\\COM1";
HANDLE hFile;
COMMTIMEOUTS sTo;
DCB sDcb;
if(Nr>=MAX_COM_PORTS)return 0;
if(bIsOpen[Nr])return 0;
cName[7]='1'+Nr;
hFile= CreateFile(cName,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if(hFile==INVALID_HANDLE_VALUE)
{
hFile=0;
return 0;
}
if(Databits==7)Databits=0;
memset(&sDcb,0,sizeof(sDcb));
sDcb.DCBlength=sizeof(sDcb);
sDcb.BaudRate = Baud;
sDcb.fParity = (Parity!=0)? TRUE:FALSE;
sDcb.fBinary = TRUE;
sDcb.Parity = iPMode[Parity];
sDcb.StopBits = iSMode[Stopbits&3];
sDcb.fOutxCtsFlow = FALSE;
sDcb.fOutxDsrFlow = FALSE;
sDcb.fDtrControl = DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE ????????? (originale DTR_CONTROL_ENABLE)
sDcb.fRtsControl = DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE ????????? (originale DTR_CONTROL_ENABLE)
sDcb.fDsrSensitivity= FALSE;
sDcb.fAbortOnError = FALSE;
sDcb.ByteSize = (Databits)? 8:7;
if(!SetCommState(hFile,&sDcb))
{
CloseHandle(hFile);
return 0;
}
sTo.ReadIntervalTimeout = MAXDWORD; // 0 ms Read-Timeout
sTo.ReadTotalTimeoutMultiplier = 0;
sTo.ReadTotalTimeoutConstant = 0;
//sTo.WriteTotalTimeoutMultiplier= 12000/Baud+1; // ? ms Write timeout per byte
sTo.WriteTotalTimeoutMultiplier= 12000/Baud+10; // 10.25 ms Write timeout per byte
sTo.WriteTotalTimeoutConstant = sTo.WriteTotalTimeoutMultiplier+1;
if(!SetCommTimeouts((HANDLE)hFile,&sTo))
{
CloseHandle(hFile);
return 0;
}
hComFile[Nr]=hFile;
bIsOpen [Nr]=TRUE;
return 1;
}
Codice:
//*****************************************************************************
//*
//* ComWrite
//*
//*****************************************************************************
// Mehrere Zeichen schreiben
// Nr : Ist die Nummer des Com-Ports (0=COM1 1=COM2 ...)
// Buffer : Buffer in dem die Zeichen gespeichert werden
// Count : Anzahl der zu sendenden Zeichen
// Ergibt die Anzahl der gesendeten Zeichen
int ComWrite(unsigned Nr,void *Buffer,int Count)
{
DWORD dwCount;
if(Nr>=MAX_COM_PORTS)return 0;
if(!bIsOpen[Nr])return 0;
WriteFile(hComFile[Nr],Buffer,Count,&dwCount,0);
return dwCount;
}
Ultima Modifica di Cahapo : 19-12-2008 14.47.27. |
|
|
|
![]() |
Per le vostre immagini su questo forum potete usare PcTunerUp! Iscriviti gratuitamente alla nostra newsletter. |
| Utenti attualmente attivi che stanno leggendo questa discussione: 1 (0 utenti e 1 visitatori) | |
| Strumenti Discussione | |
| Modalità Visualizzazione | |
|
|