LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

ISIX-RTOS – przykład 3 – obsługa przerwań na przykładzie zegara RTC z magistralą I2C

Kontroler I2C działa na zasadzie zdarzeń, zatem procedura obsługi przerwania sprowadza się do odczytania zdarzenia, a następnie wykonania określonej akcji przypisanej dla tego zdarzenia. W przypadku transmisji danych do układu podłączonego do magistrali I2C, po wysłaniu sekwencji start otrzymujemy zdarzenie informującej o przejęciu arbitrażu nad magistralą I2C przez kontroler. W wyniku wystąpienia zdarzenia I2C_EVENT_MASTER_MODE_SELECTED wywołujemy funkcjesend_7bit_addr(), co powoduje przesłanie adresu sprzętowego dla urządzenia. W wyniku wysłania adresu sprzętowego dostajemy zdarzenie I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, po którym możemy rozpocząć przesyłać dane. Wraz z każdym przesłanym bajtem otrzymujemy zdarzenie I2C_EVENT_MASTER_BYTE_TRANSMITTED. Oba zdarzenia obsługiwane są przez ten sam fragment programu, którego zadaniem jest wysłanie danych z bufora. W przypadku, przesłania ostatniego bajtu, jeżeli nie ma konieczności odbierania danych, jest wywoływana metoda powodująca wygenerowanie sekwencji stop, następnie jest podnoszony semafor sem_irq informujący wątek o zakończeniu obsługi przerwania. Realizacja podnoszenia semafora odbywa się za pomocą metody sem_signal_isr(), która jest przeznaczona do wywołania z procedur obsługi przerwań. Jeżeli po zakończeniu nadawania konieczne jest odbieranie danych z urządzenia, generowany jest ponownie bit startu oraz przesyłany jest adres sprzętowy, z najmłodszym bitem ustawionym do odczytu. W wyniku przesłania adresu sprzętowego otrzymujemy zdarzenie I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, w którym sprawdzamy, czy mamy do odebrania jeden bajt. Jeżeli tak jest, to wyłączamy generowanie bitu ACK, a następnie przechodzimy do odbioru kolejnych danych. W przypadku odebrania bajtu otrzymujemy zdarzenie I2C_EVENT_MASTER_BYTE_RECEIVED, gdzie realizujemy przepisywanie bajtów do bufora odbiorczego. Gdy skończymy odbieranie przedostatniego bajtu zgodnie ze specyfikacją I2C wyłączamy generowanie bitu potwierdzenia ACK, a po odebraniu ostatniego bajtu wysyłamy polecenie wygenerowania sekwencji stop oraz podnosimy semafor sem_isr informujący o zakończeniu procedury odbioru. Po wygenerowaniu sekwencji stop magistrala I2C zostaje zwolniona.
Klasa sterownika i2c wykorzystywana jest przez klasęrtc_reader, której zadaniem jest odczytanie bieżącej godziny z zegara RTC oraz przygotowanie i przesłanie komunikatów do obiektu serwera wyświetlacza. Działanie wątku odpowiedzialnego za to zadanie realizowane jest przez metodę wirtualną pokazaną na list. 7.

List. 7. Metoda wirtualna przygotowująca i przesyłająca komunikaty do obiektu serwera wyświetlacza

//Main rtc reader core task
void rtc_reader::main()
{
 static const uint8_t pgm_regs[] =
  {
  0x01,  //Sec
  0x02,  //Min
  0x03,  //Hour
  0x04,  //Day num
  0x05,  //day
  0x06,  //Month
  0x07,  //Year
  0x00   //Config
  };

  //Software address
  static const uint8_t sw_addr = 0;
  static uint8_t buf[3];
  static time_msg tmsg( 3,2 );
  int status;
  
  //Send configuration registgers
  i2c_bus.i2c_transfer_7bit( I2C_RTC_ADDR, pgm_regs, sizeof( pgm_regs), NULL, 0);
  //Main task loop
  for(;;)
  {
    //Send configuration registgers
    status = i2c_bus.i2c_transfer_7bit( I2C_RTC_ADDR, &sw_addr, sizeof(sw_addr), buf, sizeof(buf) );

   if(status>=0)
   {
   //If no error display time
     tmsg.set_time( buf[2]&0x3f, buf[1]&0x7f, buf[0]&0x7f );
   }
   else
   {
   //If error display it
   tmsg.set_text("I2C ERR");
   }
   //Send message to the i2c device
   disp_srv.send_message(tmsg);
   //Refresh screen delay
   isix::isix_wait(200);
  }
}

Pierwszą czynnością jest ustawienie wartości początkowej daty i czasu. Wartości początkowe zdefiniowane są w tablicy pgm_regs. W rzeczywistej aplikacji należało by zadbać o możliwość odczytania danych z interfejsu użytkownika. Przesłanie wartości początkowych realizowane jest za pomocą funkcji i2c_transfer_7bit. Następnie wchodzimy do pętli głównej programu, gdzie realizowany jest odczyt bieżącej godziny z zegara RTC. Zgodnie z dokumentacją układu najpierw należy zapisać adres pierwszego rejestru do odczytu, następnie należy przesłać ponownie adres sprzętowy z ustawionym bitem read, a następnie odczytać rejestry zegara. Cała procedura odbywa się poprzez wywołanie pojedynczej metody i2c_transfer7bit(). Po odczytaniu danych z magistrali I2C sprawdzany jest status błędu. W przypadku wystąpienia błędu wysyłany jest komunikat tekstowy. Jeżeli odczyt danych z magistrali I2C wykonano pomyślnie, jest tworzony komunikat klasy time_msg, zawierający informację o czasie w formie tekstowej, który następnie przesyłany jest do serwera wyświetlania. Klasa time_msg dziedziczy z klasy text_msg, zatem może być wysłana bezpośrednio do serwera wyświetlania:

List. 8.

class time_msg : public text_msg
{
public:
   time_msg( short xpos=0, short ypos=0)
    :text_msg("",xpos,ypos)
    {
     }
   //Set text
   void set_time( short h, short m, short s)
   {
      conv_hex(sbuf,h,2);
      sbuf[2] = ':';
      conv_hex( &sbuf[3],m,2);
      sbuf[5] = ':';
      conv_hex( &sbuf[6],s,2);
      set_text( sbuf);
   }

private:
  void strrev( char *str, int len);
  const char* conv_hex( char *txt, unsigned value, int zeros);

private:
        char sbuf[9];
};

Utworzenie komunikatu tekstowego zawierającego aktualny czas odbywa się przez wywołanie metody set_time(), która jako argumenty przyjmuje aktualną godzinę minutę i sekundę w formacie BCD. W wyniku wykonania tej metody powstaje komunikat tekstowy, który jest wykorzystywany przez serwer wyświetlania.