Pracowałem ostatnimi dniami nad małym kontrolerkiem, sterowanym przez RS232. Obsługa niektórych poleceń, wydawanych do niego, trwała nawet po kilka milisekund i… okazało się, że użyty przeze mnie 16F628A ma bufor wejściowy USARTa rozmiaru… 2 bajtów. Tak, to nie pomyłka 😐 Dlatego postanowiłem napisać obsługę cyklicznego bufora wejściowego – o rozmiarze wg uznania 🙂 Poniżej przedstawiam wam, jak coś takiego zrealizować. Na początku, zmienne globalne:

#define RX_BUFOR_MAX        32
unsigned char buforRX[RX_BUFOR_MAX];
unsigned char *buforRX_head, *buforRX_end, *RXreadstart, RXbajt;

i podczas inicjalizacji programu:

    // ustawiamy bufor odbioru danych z UARTa
    buforRX_head = buforRX;
    buforRX_end = buforRX + RX_BUFOR_MAX;
    RXreadstart = buforRX;

Tyle przygotowań. Proces odbioru danych i składowanie ich w cyklicznym buforze zrealizujemy w przerwaniu. Najpierw odpalamy przerwanie:

   STATUS.RP0 = 1;
   PIE1.RCIE = 1; // przerwanie odbioru danych z UARTa
   STATUS.RP0 = 0;
   PIR1 = 0;
   INTCON.GIE = 1;
   INTCON.PEIE = 1;

I definiujemy obsługę:

void    interrupt(void)
{
  // przyszedł znak z UARTa
  if (PIR1.RCIF)
  {
      // cykliczny bufor z użyciem "indirect addressing"
      asm {
          movf RCREG,W
          movwf _RXbajt
          movf _buforRX_head, W
          movwf FSR
          movf _RXbajt, W
          movwf INDF
          incf _buforRX_head, f
      }
      if (buforRX_head == buforRX_end) // koniec buforu,
          buforRX_head = buforRX;         // zawijamy ogon.
         
      PIR1.RCIF = 0; // koniec przerwania
  }
}

Pozostało nam napisanie funkcji korzystających z owego bufora. Wpierw funkcja badająca, czy w buforze czeka na nas jakiś nieprzetworzony znak:

// czy w cyklicznym buforze czekają dane do odczytania?
unsigned char BUFRS_Data_Ready()
{
    if (RXreadstart == buforRX_head)
        return 0;
    else
        return 1;
}

No i funkcja odczytująca kolejny znak:

// odczyt znaku z cyklicznego buforu
unsigned char BUFRS_Read()
{
    unsigned char bajt;
    asm {
        movf _RXreadstart, W
        movwf FSR
        movf INDF, W
        movwf BUFRS_Read_bajt_L0
        incf _RXreadstart, f
    }
    if (RXreadstart == buforRX_end)
        RXreadstart = buforRX;
    return bajt;
}

Na koniec przedstawię jeszcze moją małą funkcję odczytującą znak z określonym timeoutem operacji:

void BUFRS_Read_Timeout(unsigned char *bajt, unsigned char timeout)
{
    unsigned char tout, read;
    tout = 0;
    read = 1;
    *bajt = 0;
    while ((read == 1) && (tout < timeout))
    {
        if (BUFRS_Data_Ready() > 0)
        {
            *bajt = BUFRS_Read();
            read = 0;
        }
        else
            tout++;
    }
}

To tyle. U mnie – jak już pisałem, obsługa niektórych znaków zajmuje kilka milisekund, niektórych kilka mikrosekund – całość działa poprawnie przy 19200 (kwarc 20MHz). Oczywiście, powyższe działa tylko na mikrokontrolerach wyposażonych w sprzętowy moduł USART. Powodzenia.