perjantai 6. toukokuuta 2016

LCD-kello, versiot 2 .. 5

Motto toteutui taas kerran. Kyllä tuo Ohjelma 18 toimii, mutta ei tyydyttävästi. Myöhemmissä versioissa muutos on tullut lähinnä ajan käsittelyyn. Ensimmäisessä versiossa CoL_EroSekunti = 999; jätätti aivan liikaa. Arvo 985 jätätti hiukan, mutta liikaa ollakseen kello. Arvo 984 edisti liikaa. Oli siis tehtävä jotain.

Epäilin noita ajanvalintanauhassa (Seq_YlaRivi) olevia viiveitä delay(20). Syy niiden olemassa olemiseen johtui ylärivin tekstien välkkymisestä. Näyttö rauhoittui osin, mutta kaikki ei ollut kunnossa kuitenkaan. Välkkyminen johtui siitä, että jokaisella ohjelmakierroksella näytön ylärivi tyhjennettiin (lcd.print("                ");) ja heti perään teksti. Versiossa 2, jo käynnistettäessä (void setup(){ .. Fun_YlaRivi(1, Int_UT);) kutsutaan aliohjelmaa kerran ja kirjoitetaan UT-otsikko. Myöhemmin kirjoitetaan ainoastaan kerran ja vain silloin, kun otsikko muuttuu. Näyttö on rauhallinen laajasta katselukulmasta.

Kuten versioiden määrästä voi päätellä, niin ihan putkeen ei heti mennyt. Välillä jätätti parikin minuuttia vuorokaudessa ja välillä oli kuin katutrokarin kello: ” Osta!, tää on hyvä kello, tää ei kauaa tuntia huitase!” Versiossa 5 päädyin jakamaan ohjelman suorituksen nauhoille, jolloin mitään tarpeetonta tarkastelua (if) ei suoriteta. Tässä minuutit päivitetään vasta silloin, kun sekunnit tulevat täyteen. Samoin käsitellään tunnit minuuttien tullessa täyteen.

Tälle kellolle on tarkoitus järjestää pidempiaikainen käyttö. Siksi kiinnitin sen alumiiniseen kulmaan ja samalla kytkin jännitteen stabilisaattorin kautta. Kellon käyttö tulee olemaan osana radioamatööriasemaa. Siellä on 12V akkusyöttö. Siksi myös tuo kansainvälinen aika, mitä tuo harrastus käyttää. Akkusyöttö ei kuitenkaan suoraan käy Arduinolle, joten alla 9V stabilisaattorin kytkentä.
Tarjolla tähän sovellukseen on myös kirjasto-ohjelma Time.h, mutta sitä en tunne (ainakaan vielä). Se käsittää ajan tulostuksen vuositasolta sekuntiin saakka.
Jos tämän valitsemani ratkaisun tarkkuus ei riitä, on olemassa yksi hyvä vaihtoehto: voi mennä kirpparille ja ostaa paristokäyttöisen herätyskellon, missä on myös sekuntiviisari. Tämä on tärkeää, sillä silloin sekunnin pulssi on saatavissa valmiina. Tällä toteutuksella pääsee jo erittäin hyvään tarkkuuteen. Mutta tässä pieni ongelma on se, että kelloa syötetään tavallisesti 1,5V:in jännitteellä. Tällöin on kaksi vaihtoehtoa: joko vahvistaa kellon pulssi 5V:iin ja digitaalitulolle sopivaksi, tai lukea tätä pulssia analogiatulon kautta. 

Tässä viimeisessä ohjelmaversiossa (_v5) CoL_EroSekunti = arvolla 998 antoi jo melko pienen jätätyksen = 48s/vrk. On huomattavasti helpompi ohjelmallisesti käsitellä jättävää kuin edistävää ”kelloa”. Nyt vuorokauden vaihtuessa voi sekuntien lähtöarvoksi ladata 48, jolloin ensimmäinen minuutti on tynkä, mutta ei kai sillä yösydännä ole niin väliä. Jos laittaa ajan oikeaksi päivällä kello 12:00, on ajan näyttö hiukan edellä ennen puolta päivää ja jätättää iltapäivän edistyessä. Eikä tämä muutenkaan aivan täydellinen ole. Koska aika käsitellään UT:na, ollaan kesäaikana 3 tuntia edellä. Näyttö kuitenkin rajoitetaan 24:ään, jolloin näyttö jumittuu keskiyötä lähestyttäessä tuohon lukemaan. Talviaikaan tuntia myöhemmin. Aamuyöstä näyttö palautuu normaaliksi. Tokko tuollakaan suurta merkitystä, sillä toivottavasti silloin ollaan jo unten mailla. Se on korjattavissa haluttaessa ehdollisella vähennyslaskulla. Jos UT on kesäaikaan > 20, vähennetään 24 ja talviaikaan suurempi kuin 21, tehdään sama vähennys.

Ohjelma 18v5
/*****************************************
 * Ohjelma 18v5
 * 05.05.2016
 * LCD-näyttö kellonajoille:
 * Universal Time, talviaika ja kesäaika
 *****************************************/

#include <LiquidCrystal.h>
// Näytön pinnit
LiquidCrystal lcd(8, 9, 4, 5,6,7);

int lcd_key = 0;
int adc_key_in = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
 
// MÄÄRITTELYT:
// Kellon määrittley
unsigned long Ulo_MilliSek = 0;
unsigned long Ulo_UusiMilliSek = 0;
const long CoL_EroSekunti = 998;
int Int_Sekunti = 0;
int Int_Minuutti = 0;
int Int_Tunti = 0;
int Seq_Aika = 0;
boolean Bol_Tulosta = false;

int Seq_YlaRivi = 1;
int Int_UT = 0;
int Int_Talvi = 1;
int Int_Kesa = 2;
//int Int_Otsikko = 1;
int Seq_TuntiAsetus = 1;
boolean Kep_TuntiAs = false;
int Seq_MinAsetus = 1;
boolean Kep_MinAs = false;
int Seq_AjanEtAsetus = 1;
int Seq_AjanTaAsetus = 1;

// ASETUKSET:
void setup(){                
 Serial.begin(9600);         
 lcd.begin(16,2);
 Fun_YlaRivi(1, Int_UT);
}// Asetuksen loppu

// PÄÄLOOPPI                 
void loop(){
// Sisäisen kellon käyttö
Ulo_MilliSek = millis();
  if(Ulo_MilliSek - Ulo_UusiMilliSek > CoL_EroSekunti){
    Ulo_UusiMilliSek = Ulo_MilliSek;
    Seq_Aika = 1;
    if(Bol_Tulosta == true){Fun_Tulostus();}
  }

//Ajan hallintasekvenssi
switch (Seq_Aika){
  case 1:
    Int_Sekunti++;
    if(Int_Sekunti > 59){  
      Int_Minuutti++;     
      Seq_Aika = 2;
    }else{
      Seq_Aika = 0;
    }
    break;
  case 2:
    Int_Sekunti = 0;
    if(Int_Minuutti > 59){  
      Int_Tunti++;
      Int_Minuutti = 0;
      Seq_Aika = 3;
    }else{
      Seq_Aika = 0;
    }
    break;
  case 3:
    if(Int_Tunti > 23){  
      Int_Tunti = 0;
      Seq_Aika = 0;
      Int_Sekunti = 48;
    }
    break;
}

// Näytön valintasekvenssi
lcd_key = read_LCD_buttons(); // Näytön painikkeet
switch (Seq_YlaRivi) {
  case 1:
    if(lcd_key == 4){
      Fun_YlaRivi(3, Int_Talvi);
      Int_Tunti = Int_Tunti +2;
      Seq_YlaRivi = 2;}
    break;
  case 2:
    if(lcd_key == 5){Seq_YlaRivi = 3;}
    break;
  case 3:
    if(lcd_key == 4){
      Fun_YlaRivi(3, Int_Kesa);
      Int_Tunti = Int_Tunti +1;
      Seq_YlaRivi = 4;}
    break;
  case 4:
    if(lcd_key == 5){Seq_YlaRivi = 5;}
    break;
  case 5:
    if(lcd_key == 4){
      Fun_YlaRivi(1, Int_UT);
      Int_Tunti = Int_Tunti -3;
      Seq_YlaRivi = 6;}
    break;
  case 6:
    if(lcd_key == 5){Seq_YlaRivi = 1;}
    break;
   }// Näytön valintojen loppu

lcd.setCursor(4, 1);
 if(Int_Tunti < 10){ 
  lcd.print("0");
  lcd.print(Int_Tunti);
 }else{lcd.print(Int_Tunti);}
  lcd.print(":");
 if(Int_Minuutti < 10){ 
  lcd.print("0");
  lcd.print(Int_Minuutti);
 }else{lcd.print(Int_Minuutti);}
lcd.print(":");
 if(Int_Sekunti < 10){ 
  lcd.print("0");
  lcd.print(Int_Sekunti);
 }else{lcd.print(Int_Sekunti);}

// Tunnin asetus
switch (Seq_TuntiAsetus) {
  case 1:
    if(lcd_key == 3){
      Kep_TuntiAs = true;
      lcd.setCursor(0, 1);
      lcd.print("t");
      Seq_MinAsetus = 1; Kep_MinAs = false;
      Seq_TuntiAsetus = 2;}
    break;
  case 2:
    if(lcd_key == 5){Seq_TuntiAsetus = 3;}
    break;
  case 3:
    if(lcd_key == 3){
      Kep_TuntiAs = false;
      lcd.setCursor(0, 1);
      lcd.print(" ");     
      Seq_TuntiAsetus = 4;}
    break;
  case 4:
    if(lcd_key == 5){Seq_TuntiAsetus = 1;}
    break;
} // Tuntiasetus loppu

// Minuutin asetus
switch (Seq_MinAsetus) {
  case 1:
    if(lcd_key == 0){
      Kep_MinAs = true;
      lcd.setCursor(0, 1);
      lcd.print("m");
      Seq_TuntiAsetus = 1; Kep_TuntiAs = false;
      Seq_MinAsetus = 2;}
    break;
  case 2:
    if(lcd_key == 5){Seq_MinAsetus = 3;}
    break;
  case 3:
    if(lcd_key == 0){
      Kep_MinAs = false;
      lcd.setCursor(0, 1);
      lcd.print(" ");     
      Seq_MinAsetus = 4;}
    break;
  case 4:
    if(lcd_key == 5){Seq_MinAsetus = 1;}
    break;
} // Minuuttiasetus loppu

// Ajan asetus eteenpäin
switch (Seq_AjanEtAsetus) {
  case 1:
    if(lcd_key == 1){
      if(Kep_TuntiAs == true){Int_Tunti++;}
      if(Kep_MinAs == true){Int_Minuutti++;} 
      if(Kep_TuntiAs == false && Kep_MinAs == false){
       Int_Sekunti++;  
      }
      Seq_AjanEtAsetus = 2;}
    break;
  case 2:
    if(lcd_key == 5){Seq_AjanEtAsetus = 1;}
    break;
} // Ajan eteenpäin asetuksen loppu

// Ajan asetus taaksepäin
switch (Seq_AjanTaAsetus) {
  case 1:
    if(lcd_key == 2){
      if(Kep_TuntiAs == true){Int_Tunti--;}
      if(Kep_MinAs == true){Int_Minuutti--;} 
      if(Kep_TuntiAs == false&& Kep_MinAs == false){
       Int_Sekunti--;  
      }
      Seq_AjanTaAsetus = 2;}
    break;
  case 2:
    if(lcd_key == 5){Seq_AjanTaAsetus = 1;}
    break;
} // Ajan taaksepäin asetuksen loppu

Int_Sekunti = constrain(Int_Sekunti, 0, 60);
Int_Minuutti = constrain(Int_Minuutti, 0, 60);
Int_Tunti = constrain(Int_Tunti, 0, 24);
//delay(1);
} // Pääohjelma LOPPU

// FUNKTIOT
void Fun_Tulostus(){
//Serial.print("Teksti :");  Serial.println(Muuttuja);

}
// Funktio Lue painikkeet:
int read_LCD_buttons(){
  adc_key_in = analogRead(0);
  if(adc_key_in > 1000)   return btnNONE;
  if(adc_key_in < 50)    return btnRIGHT;
  if(adc_key_in < 195)   return btnUP;
  if(adc_key_in < 380)   return btnDOWN;
  if(adc_key_in < 555)   return btnLEFT;
  if(adc_key_in < 790)   return btnSELECT;
  return btnNONE;
}
// Funktio päivitä yläotsikko
void Fun_YlaRivi(int Paikka,int Otsikko){
  char* Otsake[] = {"UNIVERSAL TIME","TALVIAIKA","KESA-AIKA"};
    lcd.setCursor(0, 0);
    lcd.print("                ");
    lcd.setCursor(Paikka, 0);
     lcd.print(Otsake[Otsikko]);

} // Yärivi loppu

Ei kommentteja:

Lähetä kommentti