LinkedIn YouTube Facebook
Szukaj

Newsletter

Proszę czekać.

Dziękujemy za zgłoszenie!

Wstecz
Artykuły

[4] JAVA i STM32 – ekspresowy kurs programowania z MicroEJ – zegar z budzikiem i obsługą touch-panela

Klasa zawiera prywatne zmienne time oraz alarm time przechowujące odpowiednio: aktualną godzinę oraz godzinę alarmu w kodzie BCD, tak aby najmłodszy bajt zawierał sekundy, kolejny minuty a trzeci godziny. Do ustawiania wartości tych dwóch czasów służą metody setTime i setAlarmTime, natomiast metoda createTimeString tworzy łańcuch znaków odpowiadający aktualnej godzinie w formacie ”hh:mm:ss”. Dodatkowo klasa posiada metodę checkAlarm, która sprawdza, czy w momencie jej wywołania powinien zostać włączony alarm. Konstruktor oraz metoda readTime pozostają na razie puste i zostaną wykorzystane podczas komunikacji z modułem sprzętowym.

 

Teraz można dodać obiekt RTC do metody main i zintegrować go z interfejsem graficznym. Z uwagi na to, że w przykładzie dostęp do obiektu RTC będzie niezbędny wewnątrz obiektów słuchaczy interfejsu, został on zadeklarowany jako prywatny obiekt statyczny klasy Main. Mimo, że nie jest to rozwiązanie najlepsze, jest ono najprostsze. Obiekt RTC jest tworzony na początku metody main i aktualizowany po przyciśnięciu przycisków timeButton i alarmButton. Do każdego z nich dodano słuchacza aktualizującego odpowiedni czas w obiekcie RTC.

Na koniec dodany został obiekt Timer, który raz na sekundę odczytuje aktualną godzinę z modułu sprzętowego i aktualizuje wyświetlany czas. Sprawdza on też czy należy uruchomić alarm – jeżeli tak, do zapala diodę metodą Leds.setLedOn(0). Dioda jest gaszona przy następnym ustawieniu alarmu.

Aby sprawdzić czy wszystko działa jak należy można tymczasowo w metodzie readTime klasy RTC dodać linię:

this.time++;

Spowoduje to, ze raz na sekundę czas będzie aktualizowany, a zmiana ta powinna być widoczna w interfejsie graficznym. Podczas symulacji można również ustawić alarm i sprawdzić czy dioda zapala się w odpowiednim momencie.

Kod obsługi przycisków oraz użycia obiektu Timer znajduje się na listingu 5.

List. 5. Kod obsługi przycisków oraz użycia obiektu Timer

    Button timeButton = new Button("Set Time");
    timeButton.setListener(new Listener() {
      
      @Override
      public void performAction(int value, Object object) {
        rtc.setTime(secondWheel.getValue(), minuteWheel.getValue(), hourWheel.getValue());
        String time = rtc.createTimeString();
        Main.clockLabel.setText(time);
      }
      
      @Override
      public void performAction(int value) {
      }
      
      @Override
      public void performAction() {
      }
    });
    bgc.add(timeButton);
    
    Button alarmButton = new Button("Set Alarm");
    alarmButton.setListener(new Listener() {
      
      @Override
      public void performAction(int value, Object object) {
        rtc.setAlarmTime(secondWheel.getValue(), minuteWheel.getValue(), hourWheel.getValue());
        Leds.setLedOff(0);
      }
      
      @Override
      public void performAction(int value) {
      }
      
      @Override
      public void performAction() {
      }
    });
    bgc.add(alarmButton);
    
    Timer tim = new Timer();
    tim.schedule(new TimerTask() {
      
      @Override
      public void run() {
        rtc.readTime();
        String time = rtc.createTimeString();
        Main.clockLabel.setText(time);
        if(rtc.checkAlarm())
          Leds.setLedOn(0);
      }
    }, 0, 1000);
  }
}

Obsługa sprzętowego modułu RTC

Ostatnią zmianą w aplikacji Javy jest dodanie wywołań natywnych, które zostaną zimplementowane w C. Są to trzy funkcje:

static native void RTCInit();

static native int RTCGetTime();

static native void RTCSetTime(int time);

Zostały one zadeklarowane wewnątrz klasy RTC i wywołane odpowiednio w konstruktorze i metodach readTime oraz setTime. Po tym kroku aplikacja Javy jest już gotowa i można zabrać się za podłączenie modułu oraz implementację funkcji natywnych.

W przykładzie użyto modułu Tiny RTC z układem DS1307Z, który komunikuje się z procesorem za pośrednictwem magistrali I2C. Z uwagi na to, że w zestawie STM32F429I-DISCO nie ma wolnych portów I2C, należy podłączyć się do używanej przez inne układy magistrali I2C3 wyprowadzonej na piny PA8 (SCL), oraz PC9 (SDA). Niestety z uwagi na to, że magistrala ta posiada już rezystory podciągające obie linie do zasilania, należy usunąć ich odpowiedniki z modułu Tiny RTC. Sprowadza się to do wylutowania rezystorów R2 i R3, co nie powinno stanowić większego problemu. Oczywiście moduł ten można zastąpić także każdym innym modułem RTC – może to jednak wymagać zmian w kodzie.

List. 6. Kod funkcji RTCGetTime

int Java_app_RTC_RTCGetTime(void) {
  int time;
  uint8_t sec;
  uint8_t min;
  uint8_t hr;
  
  
  I2C_GenerateSTART(I2C3, ENABLE);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_MODE_SELECT)) __NOP();
  I2C_Send7bitAddress(I2C3, 0x68<<1, I2C_Direction_Transmitter);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) __NOP();
  I2C_SendData(I2C3, 0);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  
  I2C_GenerateSTART(I2C3, ENABLE);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_MODE_SELECT)) __NOP();
  I2C_Send7bitAddress(I2C3, 0x68<<1, I2C_Direction_Receiver);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) __NOP();
  
  I2C_AcknowledgeConfig(I2C3, ENABLE);
  I2C_GenerateSTOP(I2C3, DISABLE);
  while( !I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_RECEIVED)) __NOP();
  sec = I2C_ReceiveData(I2C3);
  
  I2C_AcknowledgeConfig(I2C3, ENABLE);
  I2C_GenerateSTOP(I2C3, DISABLE);
  while( !I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_RECEIVED)) __NOP();
  min = I2C_ReceiveData(I2C3);

  I2C_AcknowledgeConfig(I2C3, DISABLE);
  I2C_GenerateSTOP(I2C3, ENABLE);
  while( !I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_RECEIVED)) __NOP();
  hr = I2C_ReceiveData(I2C3);
  
  time = hr<<16 | min<<8 | sec;
  return time;
}

Obsługa sprzętowego RTC sprowadza się do trzech funkcji znajdujących się na listingach: 6, 7 i 8. Ich nazwy mają są charakterystyczne dla wszystkich funkcji natywnych – posiadają pełną ścieżkę do klasy zawierającej ich deklaracje w aplikacji Javy.

List. 7. Kod funkcji RTCSetTime


void Java_app_RTC_RTCSetTime(int time) {
  I2C_GenerateSTART(I2C3, ENABLE);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_MODE_SELECT)) __NOP();
  I2C_Send7bitAddress(I2C3, 0x68<<1, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) __NOP(); I2C_SendData(I2C3, time & 0x0); while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C3, time & 0x7F); while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C3, (time >> 8) & 0x7F);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  I2C_SendData(I2C3, (time >> 16) & 0x3F);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  I2C_GenerateSTOP(I2C3, ENABLE);
}

List. 8. Kod funkcji RTCInit

void Java_app_RTC_RTCInit(void) {
  //Read sec register to check if clock is enabled
  I2C_GenerateSTART(I2C3, ENABLE);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_MODE_SELECT)) __NOP();
  I2C_Send7bitAddress(I2C3, 0x68<<1, I2C_Direction_Transmitter);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) __NOP();
  I2C_SendData(I2C3, 0);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  
  I2C_GenerateSTART(I2C3, ENABLE);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_MODE_SELECT)) __NOP();
  I2C_Send7bitAddress(I2C3, 0x68<<1, I2C_Direction_Receiver);
  while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) __NOP();
  
  I2C_AcknowledgeConfig(I2C3, DISABLE);
  I2C_GenerateSTOP(I2C3, ENABLE);
  while( !I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_RECEIVED)) __NOP();
  uint8_t sec = I2C_ReceiveData(I2C3);

  if(sec & 0x80) {
  //start oscillator (clear bit 7)
    I2C_GenerateSTART(I2C3, ENABLE);
    while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_MODE_SELECT)) __NOP();
    I2C_Send7bitAddress(I2C3, 0x68<<1, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) __NOP();
    I2C_SendData(I2C3, sec & 0x0);
    while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    I2C_SendData(I2C3, sec & 0x7F);
    while (!I2C_CheckEvent(I2C3, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    I2C_GenerateSTOP(I2C3, ENABLE);
  }
}

Warto zauważyć, że w żadnej z funkcji nie ma kodu inicjalizującego magistralę. Kod ten jest niepotrzebny, ponieważ magistrala jest używana przez inne układy i została już wcześniej skonfigurowana – podczas konfiguracji systemu. Mimo to, została zdefiniowana funkcja RTCInit, która odczytuje rejestr 0x00 układu DS1307Z w celu sprawdzenia, czy zegar nie został wstrzymany, po czym w razie potrzeby zeruje najstarszy bit.

Funkcje RTCGetTime i RTCSetTime odczytują, lub zapisują rejestry 0x00, 0x01 i 0x02. W rejestrach tych znajdują się aktualne wartości sekund, minut i godzin, zapisane jednobajtowo w kodzie BCD.

Po dodaniu kodu można skompilować projekty w MicroEJ oraz w Keil/ARM MDK i uruchomić przykład.