Sida 1 av 1

Skicka och ta emot hela strängar på USART

Postat: 19 mars 2007, 13:38:39
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".

Postat: 19 mars 2007, 13:56:55
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;
    }
  }
}

Postat: 19 mars 2007, 14:02:05
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

Postat: 19 mars 2007, 14:10:27
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?

Postat: 19 mars 2007, 16:05:09
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.

Postat: 19 mars 2007, 16:16:49
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?

Postat: 19 mars 2007, 16:21:20
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.

Postat: 19 mars 2007, 16:27:58
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.

Postat: 19 mars 2007, 16:36:42
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.