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.

Technologie End of Life i bezpieczeństwo sieci – wyzwania Europy związane z tzw. długiem technologicznym
Najczęstsze błędy firm przy wyborze dostawcy energii i jak ich uniknąć
Fotorezystor, czyli czujnik światła dwojakiego działania. Przykład innowacji w automatyce i elektronice możliwej dzięki technologii fotooporników 



