Go Down

Topic: modificare impostazione comunicazione seriale cambiando il clock (Read 2393 times) previous topic - next topic

aureliusss

ciao a tutti,
sto usando arduino uno rev3 con atmega328p-pu per comunicare con un riflettometro via seriale.
il baud a cui è impostato lo strumento è 57600, e allo stesso ho impostato arduino.
il riflettometro tdr100 con cui sto lavorando, in risposta mi invia stringhe abbastanza corpose(1000-2000 caratteri).
poichè leggendo la risposta con arduino mi ritornano dei garbage character, ho fatto due test:
collegamento computer-strumento , baud 57600-----> funziona correttamente
collegamente arduino-computer, baud 57600 ------> funziona correttamente
collegamento arduino-strumento, baud 57600 -----> non funziona correttamente poichè lo strumento mi restituisce caratteri spazzatura

{
OSSERVAZIONE.viste le tabelle a pagina 179 del datasheet dell atmega ubrr = (FOSC/(16*baud)) -1
mi sembra diversa la formula usata nella libreria hardware serial di arduino : baud_setting = (F_CPU / 8 / baud - 1) / 2;
}

per abbassare l'errore percentuale sul baud, ho raddoppiato la frequenza di clock settando a 1 l'U2x0 del registro UCSR0A del microcontrollore, e poi al posto della Serial.begin ho inizializzato la comunicazione con questo codice:

Code: [Select]
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define FOSC 16000000
#define BAUD 57600
#define MYUBRR FOSC/8/BAUD
/*
MYUBRR should be calcolated as FOSC/8/BAUD-1 = 33,
but in the datasheet(pag 203) UBRR=34 for FOSC = 16M BAUD=57600 U2Xn=1,
thus i took away -1 from the formula
*/

void setup(){
  sbi(UCSR0A,U2X0); // double clock's speed setting u2x0 to 1
  USART_Init(MYUBRR);  //equal to Serial.begin
  ...
}
void loop(){
...fai cose...
}
void USART_Init(unsigned int ubrr){
  //set baud rate
  UBRR0H = (unsigned char)(ubrr>>8); //set UBRR
  UBRR0L = (unsigned char)ubrr;
 
  UCSR0B = (1<<RXEN0)|(1<<TXEN0); //enable tx and rx
 
  UCSR0C = (1<<USBS0)|(3<<UCSZ00); //set data format 8databit+2stopbit
}



il programma parte, ma se prima, con il serial.begin riuscivo a leggere ciò che mi mandava lo strumento(anche se non totalemente esatto), ora non risponde più.

dunque, dopo questo ampio preambolo, vi chiedo: cosa sbaglio? oltre ad inizializzare la seriale in modo diverso devo anche usare le altre funzioni della seriale in un altra maniera?

grazie


leo72

La libreria Serial di Arduino non imposta solo la UART interna ma crea anche tutta la parte software necessaria alla ricezione/trasmissione dei dati in 2 distinti buffer, operazioni che vengono poi agganciate a degli interrupt.
Probabilmente modificando la UART alteri anche il comportamento della lib nei confronti della periferica.

1000/2000 caratteri sono tanti, non vorrei che l'Arduino non ce la facesse a gestirli. Esso infatti memorizza i dati in arrivo in un buffer circolare. Se questo viene riempito, i caratteri in eccesso sovrascrivono quelli già presenti.
Prova  raddoppiando i buffer: apri il file /hardware/arduino/cores/arduino/hardwareserial.cpp, alla riga 59 trovi
#define SERIAL_BUFFER_SIZE 64

64 è il numero di byte per buffer. Prova mettendo 128 (ricordati che i buffer sono 2 per cui il consumo di ram passa a 128*2=256 byte).

Se anche così non risolvi prova raddoppiando ancora. Ma poi non vorrei che ti andasse in saturazione la ram.

aureliusss

ok, e se per caso avessi già messo come buffer size 256 e cmq non bastasse? qual è il massimo che posso impostare?
cmq, questo è il mio codice, le funzioni che non vedete definite non hanno grande importanza per questo problema
Code: [Select]

#include <AltSoftSerial.h>
// AltSoftSerial always uses these pins:
//
// Board          Transmit  Receive   PWM Unusable
// -----          --------  -------   ------------
// Arduino Uno        9         8         10
// Arduino Mega      46        48       44, 45

AltSoftSerial monitor;
int pnt=0;
int lostDataCounter;
int cs;//checksum value

//char array_response[8220];
float settings[7];
String command;
String response;
boolean flag;
boolean isTypeOneCommand, isTypeTwoCommand;

void setup(){
  Serial.begin(57600);
  monitor.begin(57600);
  delay(20);
  setDefaultValues();
  pinMode(13,OUTPUT);
  pinMode(2,OUTPUT);
  pinMode(11,OUTPUT);

}

void loop(){
  flag = false;
  isTypeOneCommand = false;
  isTypeTwoCommand = false;

  union{
    char byteData[4];
    float floatDatum;
  }
  datum;

  digitalWrite(13,LOW);
  digitalWrite(11,LOW);
  digitalWrite(2,LOW);
  command = "";
  response = "";
  cs = 0;
  lostDataCounter = 0;
  /*
  for (int i=0;i<220;i++){ //initialezes each element of arrayresponse to null character
   array_response[i] = 0x00;
   }
   */

  // k=0;//counter for array_response
  while(monitor.available()){
    command = monitor.readStringUntil('\r');
    command.trim();

    if (command.startsWith(":SPNT")){
      pnt = getPoints(command);
    }     

    if(command.charAt(5) == 32){
      isTypeOneCommand = true;
    }
    else if(command.charAt(1) == 'D' || command.charAt(1) == 'G'){
      isTypeTwoCommand = true;
    }

    cs = checkSum(command);
    monitor.print(command);       // type1commands are f
ollowed by hex(cs)
    monitor.print(cs,HEX);
    monitor.print("\r");

    Serial.print(command); 
    Serial.print(cs,HEX);
    Serial.print("\r");
    flag = true;
  }//---end first while loop
------------------------------------------------------------------
------unica parte che secondo me crea problemi---------
-----------------------------------------------------------------
  while(Serial.available() || flag){
    digitalWrite(13,HIGH);
    char inChar= Serial.read();
    delayMicroseconds(300);
    if(inChar != -1 ){ //tdr.read() returns -1 when it reads nothing
      response += inChar;
      delayMicroseconds(100);
      flag=false; 
    }
    if(inChar == 13) {
      flag = false;
      digitalWrite(13,LOW);
    }
  }
--------------------------------------------------------------------
--------------------------------------------------------------------
-------------------------------------------------------------------
  if(response!="")
    monitor.println(response);
  if(isTypeTwoCommand && command == ":DUMP"){
    digitalWrite(2,HIGH);
    int j=0;   
    for (int i = 6; i < 33; i+=4){
      datum.byteData[3] = response.charAt(i);
      datum.byteData[2]= response.charAt(i+1);
      datum.byteData[1]= response.charAt(i+2);
      datum.byteData[0]= response.charAt(i+3);
      delay(5);
      settings[j] = datum.floatDatum;
      j++;
    }
    for (int i=0;i<7;i++){
      monitor.println(settings[i],3);
    }
    digitalWrite(2,LOW);
  }
  if(isTypeTwoCommand && command == ":GWAV"){
    digitalWrite(11,HIGH);
    float Data[pnt];
    int j=0;
    for (int i = 6; i< response.length() - 6 ;i+=4){

      delayMicroseconds(100);
      datum.byteData[3] = response.charAt(i);
      delayMicroseconds(100);
      datum.byteData[2]= response.charAt(i+1);
      delayMicroseconds(100);
      datum.byteData[1]= response.charAt(i+2);
      delayMicroseconds(100);
      datum.byteData[0]= response.charAt(i+3);
      delay(5);
      Data[j] = datum.floatDatum;
     
      if( (Data[j] > 1) || (Data[j] < -1) ){
        digitalWrite(2,HIGH);
        lostDataCounter+=1;
        digitalWrite(2,LOW);
      }
      j++;
    }
    digitalWrite(11,LOW);
    for (int i=0; i<pnt;i++){
      delay(50);
      monitor.println(Data[i]);
    }
    monitor.print(lostDataCounter);
    monitor.println(" data have been lost during the communication");
  }
}//----end void loop()


la seriale hardware è collegata allo strumento mentre quella software è collegata al monitor del pc per vedere i dati

astrobeed


mentre quella software è collegata al monitor del pc per vedere i dati


Il problema è questo, Arduino non riesce a gestire in tempo reale un flusso dati a 57.6 in ingresso e altrettanti in uscita su una seriale software.

aureliusss

Ma le due cose, stampa su monitor e acquisizione, sono due Processi separati: la stampa su monitor avviene solo quando termina l'acquisizione dei dati.
Prima acquisisco i byte, storandoli nella stringa response, poi stampo i ibyte acquisiti, dopo che li ho processati e interpretati.

Il ciclo while(Serial.available||flag) termina solo quando l'acquisizione è terminata

A questo punto come dice leo72 penso sia un problema di ram che va in saturazione e non riesce a svuotare il buffer abbastanza velocemente, dunque dovrei velocizzare la funzione serial.read(), ma come si fa?

leo72

La SRAM di un Atmega328 è di 2048 byte (2 kB), lì dentro ci va tutto quello che serve al chip per eseguire il tuo programma, dallo stack alle variabili ecc..
Se metti 2 buffer da 256 byte l'uno hai già impiegato 512 byte. A questi devi aggiungerci i buffer della seriale software (che non so quanto siano, indaga guardando i codici della lib che usi, questa AltSoftSerial non la conosco).

Per provare ad ovviare al problema della saturazione dei buffer, prova a svuotarlo più velocemente di quanto si riempie.
Tu, da quel che ho capito, hai questa configurazione:
PC  <--57600 sw ser --> Arduino  <-- 57600 hw ser --> sensore

Se è così, prova a mettere la sw seriale a 115200, per vedere se riesci a star dietro ai dati in arrivo.

aureliusss

Avevo penasato a questa soluzione ma la altsoftserial funziona affidabilmente fino ai 57600, mentre a quanto leggo dalla guida, la softserial funziona fino  a 9600

leo72

La SoftwareSerial integrata nell'IDE 1.0.x supporta velocità fino a 115200 bps:
http://arduino.cc/en/Reference/SoftwareSerial

Prova ad usare questa al posto della AltSoftSerial

astrobeed


La SoftwareSerial integrata nell'IDE 1.0.x supporta velocità fino a 115200 bps:
http://arduino.cc/en/Reference/SoftwareSerial


Che supporta i 115kbps è un conto, che poi funzioni sul serio con un flusso dati a quella velocità è un altro paio di maniche.
La seriale software va bene solo per velocità molto basse, max 19200 bps, e con flussi dati molto leggeri, p.e. può andare bene per acquisire le sentenze NMEA di un GPS, tipicamente lavorano a 4800-9600 bps e trasmettono i dati solo una volta al secondo.

leo72


Che supporta i 115kbps è un conto, che poi funzioni sul serio con un flusso dati a quella velocità è un altro paio di maniche.

Io non l'ho provata a 115200, ma se è come dici tu siamo allora di fronte ad un altro caso di documentazione da rivedere?  :smiley-sweat:

astrobeed


Io non l'ho provata a 115200, ma se è come dici tu siamo allora di fronte ad un altro caso di documentazione da rivedere?  :smiley-sweat:


La documentazione riporta le caratteristiche tecniche, e sono corrette, ovvero sicuramente la soft serial riesce a funzionare a 115kbps, però non riporta i reali limiti operativi, p.e. la banda utile massima, quindi più che da rivedere è da integrare.
Tieni presente che questi limiti della seriale software non sono di Arduino, si applicano a tutti i micro e quanto valgono dipende dalle prestazioni del micro stesso.
P.e. ho una mia applicazione commerciale che gira su un AT91CAN dove le due seriali hardware sono impegnate con dei flussi dati ad alta velocità (240 kbps) non continui, arrivano circa 5 volte al secondo pacchetti da 32 byte, e una seriale software a 9600 bps che acquisisce 1 volta al secondo le sentenze NMEA di un GPS, avevamo provato a portare il GPS a 19200 bps con frequenza a  5 Hz e la seriale software si perdeva per strada quasi il 30% dei byte inviati.

aureliusss

http://arduino.cc/en/Tutorial/SoftwareSerial
qui c'è scritto espressamente che la softSerial funziona ''reliably'' solo a 4800,9600.
http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
questo è invece il link per la libreria altSoftSerial che riesce a sostenere la comunicazione a 57600

cmq, considerando che dovrei acquisire una stringa di circa 9000 byte, cosa mi consigliate di fare?
P.S. sono 9000 byte perchè la forma d'onda che devo acquisire è composta da 2048 float + una ventina di caratteri di controllo

astrobeed


cmq, considerando che dovrei acquisire una stringa di circa 9000 byte, cosa mi consigliate di fare?


Cambiare micro  :smiley-mr-green:
Scherzi a parte, 9000 byte su Arduino non puoi metterli visto che la ram è solo 2k, di cui una fetta è impegnata dai vari buffer e variabili di sistema.

aureliusss

nemmeno su arduino2?
cmq considera che la stampa dei valori sul monitor è diciamo il primo passo,di debug diciamo.
il progetto consiste nell'inviare i dati via bluetooth ad uno smartphone android su cui io possa fare tutto il postprocessing dei dati

leo72


http://arduino.cc/en/Tutorial/SoftwareSerial
qui c'è scritto espressamente che la softSerial funziona ''reliably'' solo a 4800,9600.

Questo tutorial mi pare vecchio. Si parla di una versione 0007 dell'IDE.
Una volta la Software Serial non era quella di adesso, era diversa e non pilotata da interrupt com'è invece l'attuale.


Go Up