ISIX-RTOS. Obsługa przerwań
Środa, 01 Wrzesień 2010
Wątki w ISIX-RTOS mogą komunikować się ze sobą za pomocą
semaforów lub kolejek komunikatów. Korzystanie z nich
może powodować usypianie procesu (sleep state) w wyniku
oczekiwania na pozyskanie zasobu. W przypadku przerwań
uśpienie procedury obsługi przerwania nie jest możliwe z uwagi
na to, że przerwania nie są wykonywane w kontekście procesu.
100 ELEKTRONIKA PRAKTYCZNA 9/2010
NOTATNIK KONSTRUKTORA
Ustawienie zwór adresowych J1?
J3 na rysunku 1 ma wpływ tylko na adres
pamięci EEPROM zintegrowanej w układzie
M41T56C64, adres zegara RTC jest
niezmienny.
W związku z tym dla procedur obsługi
przerwań należy wywołać tylko metody nie-
blokujące. W systemie ISIX, w kontekście
obsługi przerwań mogą być wywoływane je-
dynie metody z sufiksem _isr. Sposób obsłu-
gi komunikacji pomiędzy zadaniami a proce-
durami obsługi przerwań pokażemy na przy-
kładzie obsługi magistrali I2
C z podłączonym
scalonym zegarem RTC M41T56C64 (na
przykład z modułu KAmodRTC). Działanie
aplikacji sprowadzać się będzie do odczyta-
nia godziny z zegara RTC, przetworzenia go
na komunikat oraz przesłania do serwera wy-
świetlania, zaprezentowanego w poprzed-
nim przykładzie. Kolejny niezależny wątek
podobnie jak w poprzednim przykładzie
będzie migał diodą LED D1 zamontowaną
w zestawie STM32Butterfly.
W przykładzie zastosowano także wy-
świetlacz od Nokii3310 (KAmodLCD1). Spo-
sób dołączenia wyświetlacza i układu RTC
do mikrokontrolera pokazano na rysunku 1.
Układ M41T56C64 firmy ST jest bardzo
interesującym opracowaniem, integrującym
w jednej obudowie zegar czasu rzeczywi-
stego RTC z wbudowanym rezonatorem
kwarcowym 32768 Hz oraz pamięć EEPROM
AT24C64. Układ charakteryzuje się poborem
prądu rzędu 400 nA, co umożliwia mu pracę
z małej baterii litowej nawet 10 lat. Dzięki
zintegrowaniu w układzie rezonatora kwar-
cowego nie musimy dołączać z zewnątrz
praktycznie żadnych elementów, dodatkowo
rezonator ten jest kalibrowany w procesie
produkcji przez firmę ST, która gwarantuje
dokładność rzędu ?5 ppm.
Przykładowy program pokazuje na wy-
świetlaczu LCD aktualną godzinę oraz miga
diodą LED D1 znajdującą się na płytce STM-
32Butterfly. Sposób działania aplikacji z po-
działem na wątki przedstawiono na rysun-
ku 2.
Wątek obsługi LED pracuje zupełnie nie-
zależnie od pozostałych wątków, co pozwala
pokazać ich wzajemną niezależność. Wątek
obsługi LCD, który z punktu widzenia pozo-
stałych wątków jest serwerem wyświetlania,
odpowiada za odbiór rozkazów oraz odpo-
Dodatkowe materiały
na CD i FTPISIX-RTOS
Obsługa przerwań
Wątki w ISIX-RTOS mogą komunikować się ze sobą za pomocą
semaforów lub kolejek komunikatów. Korzystanie z nich
może powodować usypianie procesu (sleep state) w wyniku
oczekiwania na pozyskanie zasobu. W przypadku przerwań
uśpienie procedury obsługi przerwania nie jest możliwe z uwagi
na to, że przerwania nie są wykonywane w kontekście procesu.
wiednie sterowanie wyświetlaczem.
Wątek RTC jest odpowiedzialny za od-
czytanie aktualnej godziny z zegara RTC
poprzez interfejs I2
C, sformatowanie
tekstu, następnie wygenerowanie i prze-
słanie komunikatu dla serwera wyświet-
lania. Aplikacja została napisana w spo-
sób obiektowy w języku C++, hierarchię
klas przedstawiono na rysunku 3.
Hierarchia klas jest bardzo podobna
do przykładu 2, ponieważ wykorzysta-
no opisaną w nim architekturę serwera
wyświetlania. Klasa the_applica-
tion jest klasą aplikacji, w której
zawarto wszystkie pozosta-
łe obiekty. Klasa
led_blink jest
odpowiedzialna
za cykliczne miganie
diodą LED i jest dziedziczona
z klasy isix::task_base implementu-
jącej obsługę wątków. Klasa display_server
jest odpowiedzialna za odbiór komunikatów
z kolejki FIFO oraz fizyczne sterowanie kon-
trolerem wyświetlacza za pomocą klasy no-
kia_display. Klasa i2c_host jest uniwersalną
klasą sterownika, implementującą obsługę
magistrali I2
C z wykorzystaniem sprzętowego
kontrolera I2C1 w trybie 7-bitowym. Została
ona napisana w taki sposób, aby można było
ją rozwinąć o obsługę dodatkowych sprzęto-
wych kontrolerów I2
C, występujących w mi-
krokontrolerach rodziny STM32F. Deklaracja
klasy znajduje się w pliku i2c_host.hpp (li-
stingu 1).
Klasa została zaprzyjaźniona z funkcją
i2c1_ev_isr_vector stanowiącą wektor obsłu-
gi przerwania od kontrolera I2C1. Wszystkie
procedury obsługi przerwań muszą mieć
linkowanie typu C (extern ?C?). Funkcje
wywoływane są w momencie wystąpienia
przerwania bez żadnych dodatkowych pa-
rametrów, co wymusza istnienie dostępu do
instancji klasy kontrolera I2
C poprzez wskaź-
nik lub referencję globalną. Wskaźnik do
obiektu i2c umożliwiający dostęp do obiektu
kontrolera I2
C przez funkcję obsługi przerwa-
nia umieszczono w nienazwanej przestrzeni
nazw w pliku implementacji klasy (i2c_host.
cpp), przez co dostęp do niej jest możliwy
tylko w obrębie tego modułu.
Zadeklarowanie przyjaźni funkcji umoż-
liwia wywoływanie dowolnych metod chro-
nionych klasy z funkcji zaprzyjaźnionej, co
zostało wykorzystane do wywołania metody
isr() stanowiącą wektor obsługi przerwa-
nia. Do synchronizacji wątku z procedurą
obsługi przerwania wykorzystano semafor
sem_irq, natomiast semafor sem_lock jest
wykorzystywany do blokowania sterownika
w przypadku, gdy jest on zajęty obsługą ma-
gistrali I2
C.
Konstruktor klasy obiektu i2c_host przyj-
muje dwa argumenty: adres wybranego kon-
trolera I2
C (np. I2C1, I2C2) oraz szybkość tak-
towania magistrali I2
C wyrażoną w hercach,
101ELEKTRONIKA PRAKTYCZNA 9/2010
Obsługa przerwań
której domyślna wartość wynosi 100000.
Zadaniem konstruktora jest inicjalizacja kon-
trolera sprzętowego I2
C, uruchomienie prze-
rwań oraz przypisanie wskaźnika this utwo-
rzonego obiektu do wskaźnika wykorzysty-
wanego przez procedurę obsługi przerwania.
Na początku sprawdzany jest numer
kontrolera I2
C (obecnie zaimplementowana
jest tylko obsługa kontrolera I2C1), następ-
nie konfigurowane są porty GPIO, tak aby
pełniły funkcję wyjścia układu peryferyjne-
go. Następnie konfigurowany jest sprzętowy
kontroler I2
C, tak aby pełnił rolę sterowni-
ka master z 7-bitowym adresowaniem. Do
wskaźnika na obiekt wykorzystanego przez
procedurę obsługi przerwania przypisywa-
ny jest wskaźnik this aktualnie tworzonego
obiektu. Przyjęto założenie, że istnieje tylko
jedna instancja klasy i2c_host, ponieważ
do danego kontrolera magistrali może być
przypisany tylko jeden sterownik. Na zakoń-
czenie włączane są przerwania w kontrole-
rze I2
C oraz konfigurowane są przerwania
w kontrolerze NVIC.
Jedyną metodą interfejsu użytkownika
klasy sterownika I2
C jest metoda i2c_transfe-
r_7bit (listing 3) umożliwiająca przeprowa-
dzenie na magistrali transakcji I2
C.
Jako parametry przyjmuje ona kolejno
adres sprzętowy układu I2
C, z którym chce-
my przeprowadzić transakcję, wskaźnik
do bufora z danymi oraz długość bufora,
z którego dane chcemy przesłać magistralą
I2
C. Następnie przekazujemy wskaźnik do
bufora, gdzie będą przesłane dane odebra-
ne z magistrali I2
C oraz liczba danych, jaką
chcemy odczytać. Jeśli chcemy wykonać
tylko pojedynczy zapis danych lub odczyt,
do nieużywanego wskaźnika na bufor nale-
ży przypisać wartość NULL. Metoda blokuje
wykonanie bieżącego wątku do chwili za-
kończenia transakcji I2
C oraz w przypadku
powodzenia zwraca wartość ERR_OK. Na
początku funkcji wywoływana jest metoda
wait() na głównym semaforze blokującym,
w wyniku czego proces może zostać zabloko-
wany, jeżeli sterownik jest zajęty przez inny
proces. Do zmiennych klasy przypisywane
są wskaźniki do buforów oraz wielkości tych
buforów i jest generowany sekwencja start.
Następnie oczekujemy na semaforze sygna-
lizującym zakończenie pracy przez proce-
durę obsługi przerwania. Cała obsługa cyklu
magistrali wykonywana jest w przerwaniu.
Oczekiwanie na semafor kończy się w mo-
mencie sygnalizacji przez procedurę obsłu-
gi przerwania lub w wyniku przekroczenia
czasu oczekiwania, co informuje nas o wy-
stąpieniu błędu. W przypadku powodzenia
zwracany jest status ERR_OK oraz podnoszo-
ny jest semafor blokujący sem_lock za po-
mocą wywołania metody sem_lock.signal().
Generacja bitu startu powoduje rozpoczęcie
działania kontrolera I2
C. W wyniku wystą-
pienia poszczególnych zdarzeń zgłaszane
Rysunek 1. Sposób dołączenia zegara RTC i wyświetlacza LCD do zestawu
STM32Butterfly
Rysunek 3. Hierarchia klas projektu
Rysunek 2. Podział aplikacji na wątki
102 ELEKTRONIKA PRAKTYCZNA 9/2010
NOTATNIK KONSTRUKTORA
jest przerwanie, którego obsługa realizowa-
na jest przez procedurę i2c1_ev_isr_vector
(listing 4).
Funkcja ta jest zaprzyjaźniona z klasą
i2c_host. Na rzecz obiektu klasy i2c_host jest
wywoływana funkcja isr(), która jest właści-
wą procedurą obsługi przerwania (listing 5).
Kontroler I2
C działa na zasadzie zdarzeń,
zatem procedura obsługi przerwania sprowa-
dza się do odczytania zdarzenia, a następnie
wykonania określonej akcji przypisanej dla
tego zdarzenia. W przypadku transmisji da-
nych do układu podłączonego do magistrali
I2
C, po wysłaniu sekwencji start otrzymujemy
zdarzenie informującej o przejęciu arbitrażu
nad magistralą I2
C przez kontroler. W wyni-
ku wystąpienia zdarzenia I2C_EVENT_MA-
STER_MODE_SELECTED? wywołujemy
funkcję? send_7bit_addr(),? co? powoduje
przesłanie adresu sprzętowego dla urządze-
nia. W wyniku wysłania adresu sprzętowe-
go dostajemy zdarzenie I2C_EVENT_MA-
STER_TRANSMITTER_MODE_SELECTED,
po którym możemy rozpocząć przesyłać
dane. Wraz z każdym przesłanym bajtem
otrzymujemy zdarzenie I2C_EVENT_MA-
STER_BYTE_TRANSMITTED. Oba zdarze-
nie 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 wy-
woł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 podno-
szenia 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ńcze-
niu nadawania konieczne jest
odbieranie danych z urządze-
nia, generowany jest ponownie
bit startu oraz przesyłany jest
adres sprzętowy, z najmłod-
szym bitem ustawionym do
odczytu. W wyniku przesłania
adresu sprzętowego otrzymuje-
my zdarzenie I2C_EVENT_MA-
STER_RECEIVER_MODE_SE-
LECTED, 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_RE-
CEIVED, gdzie realizujemy przepisywanie
bajtów do bufora odbiorczego. Gdy skończy-
my odbieranie przedostatniego bajtu zgodnie
ze specyfikacją I2
C wyłączamy, generowanie
bitu potwierdzenia ACK, a po odebraniu
ostatniego bajtu wysyłamy polecenie wyge-
Listing 1.
//I2c host class
class i2c_host
{
//Friend interrupt class
friend void i2c1_ev_isr_vector(void);
public:
enum errno
{
ERR_OK = 0, //All
is ok
ERR_BUS = -5000, //Bus error
ERR_ARBITRATION_LOST = -5001,
ERR_ACK_FAILURE = -5002,
ERR_OVERRUN = - 5003,
ERR_PEC = - 5004, //Parity check
error
ERR_BUS_TIMEOUT = -5005, //Bus timeout
ERR_TIMEOUT = - 5006, //timeout error
ERR_UNKNOWN = - 5007
};
//Default constructor
i2c_host(I2C_TypeDef * const _i2c, unsigned clk_speed=100000);
//I2c transfer main function
int i2c_transfer_7bit(uint8_t addr, const void* wbuffer, short wsize,
void* rbuffer, short rsize);
private:
//Interrupt service routine
void isr();
//Configuration data
static const unsigned TRANSFER_TIMEOUT = 1000;
static const unsigned IRQ_PRIO = 1;
static const unsigned IRQ_SUB = 7;
//Rest of the data
static const unsigned CR1_ACK_BIT = 0x0400;
static const unsigned CR1_START_BIT = 0x0100;
static const unsigned CR1_STOP_BIT = 0x0200;
static const uint16_t I2C_IT_BUF = 0x0400;
static const uint16_t I2C_IT_EVT = 0x0200;
static const uint16_t I2C_IT_ERR = 0x0100;
static const uint16_t CR1_PE_SET = 0x0001;
//Get last i2c event
uint32_t get_last_event()
{
static const uint32_t sflag_mask = 0x00FFFFFF;
return ( static_cast(i2c->SR1) |
static_cast(i2c->SR2)<<16 )
& sflag_mask;
}
//Send 7 bit address on the i2c bus
void send_7bit_addr(uint8_t addr)
{
i2c->DR = addr;
}
//Send data on the bus
void send_data(uint8_t data)
{
i2c->DR = data;
}
//Read data from the bus
uint8_t receive_data()
{
return i2c->DR;
}
//CR1 reg enable disable
void cr1_reg(unsigned bit, bool en)
{
if(en) i2c->CR1 |= bit;
else i2c->CR1 &= ~bit;
}
//ACK ON control
void ack_on(bool on)
{
cr1_reg( CR1_ACK_BIT, on );
}
//Generate start
void generate_start(bool en=true)
{
cr1_reg( CR1_START_BIT, en );
}
//Generate stop
void generate_stop(bool en=true)
{
cr1_reg( CR1_STOP_BIT, en );
}
//Clear data flags (dummy read)
void clear_flags()
{
static_cast(static_cast(i2c->SR1));
static_cast(static_cast(i2c->DR));
}
//Control enabling disabling int in the device
void devirq_on(bool en=true)
{
if(en)
/* Enable I2C interrupt */
i2c->CR2 |= I2C_IT_EVT| I2C_IT_ERR;
else
/* diasable I2C interrupt */
i2c->CR2 &= ~(I2C_IT_EVT | I2C_IT_ERR);
}p
//Set bus speed
void set_speed(unsigned speed);
//Translate error to the error code
Listing 1. c.d.
int get_hwerror();
private: //Data
//I2c device number
I2C_TypeDef *i2c;
//Tx buffer pointer
const uint8_t *tx_buf;
//Rx buffer pointer
uint8_t *rx_buf;
//Busy semaphore
isix::semaphore sem_lock;
//Read semaphore
isix::semaphore sem_irq;
//Bus address
volatile uint8_t bus_addr;
//Bus error flags
volatile uint8_t err_flag;
//Tx counter
volatile short tx_bytes;
//Rx counter
volatile short rx_bytes;
//Position in the buffer
volatile short buf_pos;
private: //Noncopyable
i2c_host(i2c_host &);
i2c_host& operator=(const
i2c_host&);
};
103ELEKTRONIKA PRAKTYCZNA 9/2010
Obsługa przerwań
Listing 3.
int i2c_host::i2c_transfer_7bit(uint8_t addr, const void* wbuffer, short
wsize, void* rbuffer, short rsize)
{
int ret;
if( (ret=sem_lock.wait(isix::ISIX_TIME_INFINITE))<0 )
{
return ret;
}
//Disable I2C irq
devirq_on(false);
if(wbuffer)
{
bus_addr = addr & ~I2C_BUS_RW_BIT;
}
else if(rbuffer)
{
bus_addr = addr | I2C_BUS_RW_BIT;
}
tx_buf = static_cast(wbuffer);
rx_buf = static_cast(rbuffer);
tx_bytes = wsize;
rx_bytes = rsize;
buf_pos = 0;
//ACK config
ack_on(true);
//Enable I2C irq
devirq_on();
//Send the start
generate_start();
//Sem read lock
if( (ret=sem_irq.wait(TRANSFER_TIMEOUT)) <0 )
{
if(ret==isix::ISIX_ETIMEOUT)
{
sem_irq.signal();
sem_lock.signal();
return ERR_TIMEOUT;
}
else
{
sem_irq.signal();
sem_lock.signal();
return ret;
}
}
Listing 2.
// TODO Add configuration for i2c2 device support
if(_i2c==I2C1)
{
//GPIO configuration
RCC->APB2ENR |= I2C1_GPIO_ENR;
io_config(I2C1_PORT,I2C1_SDA_PIN,GPIO_MODE_50MHZ,GPIO_CNF_ALT_OD);
io_config(I2C1_PORT,I2C1_SCL_PIN,GPIO_MODE_50MHZ,GPIO_CNF_ALT_OD);
io_set(I2C1_PORT,I2C1_SCL_PIN);
io_set(I2C1_PORT,I2C1_SDA_PIN);
//I2C module configuration
RCC->APB1ENR |= I2C1_ENR;
}
/* Enable I2C module*/
i2c->CR1 |= CR1_PE_SET;
/* Reset the i2c device */
i2c->CR1 |= CR1_SWRST;
nop();
i2c->CR1 &= ~CR1_SWRST;
uint16_t tmpreg = i2c->CR2;
/* Clear frequency FREQ[5:0] bits */
tmpreg &= CR2_FREQ_RESET;
tmpreg |= static_cast(config::PCLK1_HZ/1000000);
i2c->CR2 = tmpreg;
//Set speed
set_speed(clk_speed);
/* CR1 configuration */
/* Get the I2Cx CR1 value */
tmpreg = i2c->CR1;
/* Clear ACK, SMBTYPE and SMBUS bits */
tmpreg &= CR1_CLEAR_MASK;
/* Configure I2Cx: mode and acknowledgement */
/* Set SMBTYPE and SMBUS bits according to I2C_Mode value */
/* Set ACK bit according to I2C_Ack value */
tmpreg |= I2C_MODE_I2C | I2C_ACK_ENABLE;
/* Write to I2Cx CR1 */
i2c->CR1 = tmpreg;
/* Set I2Cx Own Address1 and acknowledged address */
i2c->OAR1 = I2C_AcknowledgedAddress_7bit;
i2c->SR1 = 0; i2c->SR2 = 0;
/* Enable I2C interrupt */
devirq_on();
//Assign as the global object
if(_i2c==I2C1)
{
i2c1_obj = this;
}
/* Enable interrupt controller */
nvic_set_priority( I2C1_EV_IRQn, IRQ_PRIO, IRQ_SUB);
nvic_irq_enable(I2C1_EV_IRQn,true);
}
Listing 3. c.d.
if( (ret=get_hwerror()) )
{
err_flag = 0;
sem_lock.signal();
return ret;
}
sem_lock.signal();
return ERR_OK;
}
104 ELEKTRONIKA PRAKTYCZNA 9/2010
NOTATNIK KONSTRUKTORA
Listing 5.
//I2c interrupt handler
void i2c_host::isr()
{
uint32_t event = get_last_event();
switch( event )
{
//Send address
case I2C_EVENT_MASTER_MODE_SELECT: //EV5
send_7bit_addr(bus_addr);
break;
//Send bytes in tx mode
case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: //EV6
case I2C_EVENT_MASTER_BYTE_TRANSMITTED: //EV8
if(tx_bytes>0)
{
send_data(tx_buf[buf_pos++]);
tx_bytes--;
}
if(tx_bytes==0)
{
if(rx_buf)
{
//Change address to read only
bus_addr |= I2C_BUS_RW_BIT;
ack_on(true);
generate_start();
buf_pos = 0;
}
else
{
generate_stop();
sem_irq.signal_isr();
}
}
break;
//Master mode selected
case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: //EV7
if(rx_bytes==1)
{
ack_on(false);
}
break;
//Master byte rcv
case I2C_EVENT_MASTER_BYTE_RECEIVED:
if(rx_bytes>0)
{
rx_buf[buf_pos++] = receive_data();
rx_bytes--;
}
if(rx_bytes==1)
{
ack_on(false);
generate_stop();
}
else if(rx_bytes==0)
{
generate_stop();
sem_irq.signal_isr();
}
break;
//Stop generated event
default:
if(event & EVENT_ERROR_MASK)
{
err_flag = event >> 8;
i2c->SR1 &= ~EVENT_ERROR_MASK;
sem_irq.signal_isr();
}
else
{
clear_flags();
}
break;
}
}
Listing 4.
//Call to the global c function
extern ?C?
{
void i2c1_ev_isr_vector(void) __attribute__ ((interrupt));
void i2c1_ev_isr_vector(void)
{
if(i2c1_obj) i2c1_obj->isr();
}
nerowania sekwencji stop oraz podnosimy
semafor sem_isr informujący o zakończeniu
procedury odbioru. Po wygenerowaniu se-
kwencji stop magistrala I2
C 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 komu-
nikatów do obiektu serwera wyświetlacza.
Działanie wątku odpowiedzialnego za to za-
danie realizowane jest przez metodę wirtual-
ną pokazaną na listingu 6.
105ELEKTRONIKA PRAKTYCZNA 9/2010
Obsługa przerwań
Listing 6.
//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);
}
}
Listing 7.
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];
};
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łoby
zadbać o możliwość odczytania danych z in-
terfejsu użytkownika. Przesłanie wartości
początkowych jest realizowane za pomocą
funkcji i2c_transfer_7bit. Następnie wcho-
dzimy do pętli głównej programu, gdzie
realizowany jest odczyt bieżącej godziny
z zegara RTC. Procedura odbywa się poprzez
wywołanie pojedynczej metody i2c_transfer-
7bit(). Po odczytaniu danych z magistrali I2
C
sprawdzany jest status błędu i w przypadku
jego wystąpienia jest wysyłany komunikat
tekstowy. Jeżeli odczyt danych z magistra-
li I2
C wykonano pomyślnie, jest tworzony
komunikat klasy time_msg, zawierający in-
formację o czasie w formie tekstowej, który
następnie przesyłany jest do serwera wy-
świetlania. Klasa time_msg dziedziczy z kla-
sy text_msg, zatem może być wysłana bezpo-
średnio do serwera wyświetlania (listing 7).
Utworzenie komunikatu tekstowego za-
wierającego aktualny czas odbywa się przez
wywołanie metody set_time(), która jako ar-
gumenty przyjmuje aktualną godzinę, minu-
tę i sekundę w formacie BCD.
Lucjan Bryndza, EP
lucck@boff.pl
Zobacz więcej w kategorii Notatnik konstruktora