Dodatki‎ > ‎

Serwer UART

...czyli oprogramowanie działające na uC, które za pośrednictwem portu szeregowego udostępnia CLI (command line interface). Można w ten sposób przesyłać komendy do mikrokontrolera bezpośrednio z programu terminala uruchomionego pod Windows lub Linuks. Zamiast terminala można użyć innej aplikacji, także własnej, napisanej w C++, C#, Python, ...

Poniżej prawie kompletny kod źródłowy. Miejscami celowo napisany bardziej rozwlekle niż trzeba - aby można było łatwiej zrozumieć, co się w nim właściwie dzieje. Uwaga! w kodzie nie została uwzględniona kontrola przepełnienia tablicy (bufora) - to zadanie dla was :)
Zakładamy, że w ProcessorExpert został dodany komponent Init_SCI1 i odpowiednio skonfigurowany. Sugerowana konfiguracja portu: 115200,8,N,1. Przy magistrali 24 MHz należy ustawić dzielnik 13, aby otrzymać zbliżoną wartość. Zmiana dzielnika powoduje automatyczne przeliczanie wynikowej prędkości (baudrate), więc łatwo można dobrać także inne wartości. Pamiętaj o wybraniu Enable dla nadajnika i odbiornika - chodzi o opcje na samym dole okna konfiguracyjnego komponentu.

Obsługiwane jest 1 źródło przerwania - funkcja (ISR) uart_rx obsługuje odebranie pojedynczego znaku.

//ta definicja jest dodana dla polepszenia czytelności kodu w dalszej części - od razu widać, 
//że chodzi o pusty wskaźnik, a nie zwykłe 0
#define NULL 0

//wysłanie pojedynczego znaku przez SCI1
void send_char(byte c) {
  while(!SCI1S1_TDRE){} //czekaj, jeśli rejestr danych nadajnika nie jest pusty 
                        //(tzn. trwa jeszcze wysyłanie poprzedniego znaku)
  SCI1D = c;  //wyślij znak (doczekaliśmy się pustego rejestru danych nadajnika)
}

//wysłanie ciągu znaków - typowa funkcja w C.
//założenie: musi to być poprawny napis ASCIIZ (z zerem na końcu)
void send_str(char *s) {
  while(*s != '\0') {    
    send_char(*s);
    s++;
  }
}

//struktura łącząca logicznie wskaźnik znakowy (nazwę komendy) oraz 
//wskaźnik funkcyjny (funkcję, która tę komendę fizycznie wykonuje)
struct t_command {
  char *cmd;  
  void (*function)();
};

//wyświetlenie skromnej pomocy - niech użytkownik chociaż wie, jakie są dostępne polecenia
//todo: zautomatyzować wyświetlanie listy komend na podstawie zawartości tablicy [commands]
void cmd_help() {    
  send_str(
    "help - pomoc\r\n"
    "leds_on - wlacza diody\r\n"
    "leds_off - wylacza diody\r\n");
}

//włącz 4 diody
void cmd_leds_on() {
  leds_PutVal(0b1111); //wartość w systemie dwójkowym
}

//wyłącz 4 diody
void cmd_leds_off() {
  leds_PutVal(0b0000);
}

//tablica będzie miała tyle elementów, ile zostało wpisane + ostatni element złożony 
//z 2 pustych wskaźników
struct t_command commands[] = { 
  {"help", cmd_help},
  {"leds_on", cmd_leds_on},
  {"leds_off", cmd_leds_off},   
  { NULL, NULL }    //tak oznaczam koniec tablicy - analogicznie jak znak '\0' w ASCIIZ
};

//Funkcja analizująca odebrany ciąg znaków. 
//Przeszukiwana jest tablica [commands], w przypadku trafienia na komendę identyczną 
//jak wpisany ciąg znaków wywoływana jest stowarzyszona z nią funkcja.
//Elementy magii stosowanej: wskaźniki funkcyjne w C.
void cmd(char *s) {
  int i = 0;
  while(commands[i].cmd != NULL) 
  {
    if(strcmp(s, commands[i].cmd) == 0) { //wykryto pasującą nazwę komendy
      commands[i].function(); //wywołaj stowarzyszoną z tą komendą funkcję C
      break; //przerwij dalsze przeglądanie tablicy komend
    }
    i++; //kolejny element tablicy komend
  }
}

//bufor przechowujący odebrane znaki, zabezpiecz go przed przepełnieniem!
byte buf[100] = { 0 };

//aktualna pozycja, na którą będzie wpisany odebrany znak, zaczynamy od zera.
int idx = 0; 

//funkcja obsługi przerwania SCI (odebranie znaku)
__interrupt void uart_rx()
{
  byte Status = SCI1S1; //nieużywane w tym kodzie
  byte Data = SCI1D; //odebrany bajt (znak)
  send_char(Data); //odeślij kopię (echo)
  
  if(Data >= ' ') { //spacja albo więcej 
    buf[idx] = Data; //dopisz do tablicy
    idx++; //przesuń bieżący indeks na kolejną pozycję
    buf[idx] = '\0'; //wstaw zero za ciągiem zgromadzonych znaków (powstanie napis ASCIIZ)
  }
  
  if(Data == 13) { //wykryto znak CR (ENTER/RETURN)
    if(buf[0] != '\0') //czy w ogóle coś mamy w buforze?
      cmd((char*)buf); //przekaż to coś do interpretera komend
    idx = 0; //po przetworzeniu zaczynamy zabawę od nowa - początek bufora
    buf[0] = '\0'; //...zgodność z ASCIIZ
  }
  leds_NegBit(0); //wesołe mruganie diodą 0 - zmiana stanu po odebraniu każdego znaku
}