Skicka och ta emot hela strängar på USART

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Skicka och ta emot hela strängar på USART

Inlägg av ankan »

Sitter och kodar i MikroC.

Det finns färdiga rutiner för att skicka och ta emot en byte i taget och för att kolla om det finns någon data på ingången.

Att sätta ihop en funktion som plockar in en sträng och spottar ut den på USARTen är inga problem. Men hur gör jag när jag vill kolla om jag får tillbaka en viss sträng?

Kod: Markera allt

char * Usart_Read_String(unsigned short langd)  {
  char *text, *indata;
  unsigned short i;
  
  for(i=0;i<langd;i++) {
    while(!Usart_Data_Ready()) {}
    IntToStr(Usart_Read(), indata);
    strcat(text, indata);
  }
  return text;
}
Ovanstående kod fungerar tror jag om jag får in minst rätt längd data. Men om jag inte får någon data alls så låser den sig.

Jag vill kolla om svaret på ett kommando jag skickar till en extern enhet över RS232 är tex "OK".
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Hittade en trevlig liten kod som jag tänkte testa sedan när jag har fått exprimentkortet:

Kod: Markera allt

char * Usart_Read_String(void) {
  unsigned long counter = 0;
  const unsigned long MAXCOUNT = 10000;
  const unsigned short MAXLENGTH = 180;
  char buffer[180];
  unsigned short s_count = 0;

  while(!Usart_data_ready()) {
    counter ++;                  // data not received, increment timeout counter
    if (counter>MAXCOUNT) {      // timeout reached
      buffer[s_count] = 0x00;    // s_count has been incremented in the function, set the end of string
        return buffer;
    }

    counter = 0;                      // data received before timeout occurred
    buffer[s_count++] = Usart_read(); // read the character and place in the buffer
    if (s_count==MAXLENGTH-1) {       // buffer overflow check
      buffer[s_count] = 0x00;        // in this case, terminate the string and exit
      return buffer;
    }
  }
}
Användarvisningsbild
speakman
Inlägg: 4838
Blev medlem: 18 augusti 2004, 23:03:32
Ort: Ånge

Inlägg av speakman »

Rent generellt: gör en loop som läser in en byte i taget och trigga på radbrytning.
Sedan ser du till att det som svarar (t.ex. "OK") alltid avslutas med en radbrytning.
Man bör dock alltid ha en timer som löser ut om ingen sträng dyker upp inom rimlig tid.

Mvh
speakman
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

PICen ska kommunicera med ett RF-modem så jag är tveksam om den skickar radbrytning. Tyvärr inget jag kan styra i alla fall.

Borde jag köra en timer i stället för räknaren jag har gjort?
Användarvisningsbild
vfr
EF Sponsor
Inlägg: 3515
Blev medlem: 31 mars 2005, 17:55:45
Ort: Kungsbacka

Inlägg av vfr »

Jag skulle nog säga att man bör ha någon form av "riktig" tid för sin timeout och inte bara loopvarv.

Däremot är det inte nödvändigt att dedikera en hel hårdvarutimer bara för detta. När jag skriver inbyggda applikationer så har jag oftast någon form av systemtimer som ger "ticks" med 1-10ms intervall baserad på en hårdvarutimer. Räcker oftast med en 8-bitars timer. Sedan kan man använda det till en massa saker som behöver tidsreferenser, t.ex kommunikationstimeout.
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Lät bra men jag förstår inte riktigt hur du menar.

Är det inte en timer du använder då för att ticka? Och hur använder du den sedan i koden?
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Jag vill kolla om svaret på ett kommando jag skickar till en extern enhet över RS232 är tex "OK".

Är det du får verkligen "OK" som *text* ?
Eller är "OK" bara symboliskt använt, så att det som kommer
i verkligeheten är någon slags kod som betyder "OK" ?

Om vi antar att det kommer som en text (och jag antar att det
även kan komma andra texter !?) så skulle man nog göra så här :

- Skapa en interrupt rutin som triggar när det kommer ett tecken.
- Varje gång det kommer ett tecken, lägg in det i en buffert och sätt en
flagga ett "det har kommit ett tecken".
- I main-loopen kollas flaggan ovan regelbundet och om det är "satt" så
kollas om det ligger "OK" i bufferten. Eler något annat, eller inget speciellt...

Om det aldrig kommer annat än just "OK" så blir det ju enklare.
Vänta bara tills det har kommit två tecken och kolla om det var "OK".
Fortfarande bör naturligtsvis mottagningen vara interruptstyrd, annars blir
koden snabbt helt omöjlig att ha någon slags ordning på...

Undvik "spinn-locks", d.v.s där koden ligger fast i en loop och väntar
på att något ska hända (om det inte gäller väldigt korta tider).
Gör tvärtom, låt händelserna istället avbryta processorn via interrupt.

"vfr" har helt rätt i lösningen med en "systemklocka" byggd på ett
timerinterrupt. Från detta konstruerar man sedan (via olika räknare)
de olika tider/fördröjningar/timeouts/whatever som man behöver.
Systemklockan ska naturligstvis gå med minsta gemensamma nämnare
för de olika tider som man behöver i applikationen.

Så bygg först en bas i applikationen som övriga funktioner sedan kan
bygga på. En slags infrastruktur med timers, flaggor, buffers o.s.v. Sedan
blir det ganska enkelt att "jacka" på de olika rutinerna/funktioerna.
Kopplingarna/interfacen mellan rutinerna kommer till stösta delen att
bestå av dessa buffers, flaggor m.m.
Användarvisningsbild
vfr
EF Sponsor
Inlägg: 3515
Blev medlem: 31 mars 2005, 17:55:45
Ort: Kungsbacka

Inlägg av vfr »

Jo det är en riktig timer. Grejen är att du kan använda den till många saker och inte låser en hårdvarutimer enbart till kommunikationen. För att detta skall fungera så måste timern vara frigående, d.v.s man återstartar den aldrig utan låter den "slå runt".

Man använder den ungefär på samma sätt som man använder capture-modulen till en timer. När du startar mätning så lagrar du värdet av timervariabeln. När timervärdet uppnått det lagrade värdet plus din inställda tid så är du klar. Man tar nuvarande timervärde minus det lagrade värdet och får på så sätt fram hur lång tid som förflutit sedan timern startades. Att timern slår runt påverkar egentligen inte värdet så länge den bara tillåts att slå runt en gång under din mätperiod.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Till mina saker brukar jag ha en 10Hz interrupt.

volatile unsigned char Delay_Counter, Display_Timer, Valid_Input_Block;

Sedan i timer-ISR gör jag:
...
if(Delay_Counter) Delay_Counter--;
if(Display_Timer) Display_Timer--;
if(Valid_Input_Block) Valid_Input_Block--;
...

När jag sedan använder detta:
Delay_Counter = 100;
while(Delay_Counter); // Vänta på att Delay_Counter ska bli noll

När man ska ta emot okänd längd strängar kan man t.ex. göra så att för varje char som kommer in omstartar man en timer.

Kod: Markera allt

Valid_Input_Block = 10; // Timeout = 1 sek
while(Valid_Input_Block)
  {
  if(incoming_char_present)
    {
    // Inkommande char -> buffer
    Valid_Input_Block = 10; // Återställ till 1 sek time-out
    }
  }
Då kommer den att ta emot char's till de inte har kommit i 1 sek, eller om man lägger till det, till buffern är full.
Skriv svar