lauantai 25. helmikuuta 2017

Tosi lyhyt matikka!

Ohjelma kysyy käynnityksen jälkeen ensimmäistä lukua ja kehoittaa painamaan ENTteriä. Kun tämä on tehty näytetään kehoitus toisen luvun antamiseen. Toisen ENTterin jälkeen ohjelma laskee neljä laskutehtävää (summa, erotus, tulo ja osamäärä) ja näytön alarivin hallinta siirtyy ylös- ja alas-painikkeille. Näillä voidaan siirtyä edestakaisin noiden tulosten katselussa. Tästä tilasta paistutaan painamalla MENU-painiketta, jolloin ohjelma siirtyy kysymään ensimmäistä numeroa uudestaan. Suositeltavaa on käyttää lukualuetta välillä 1 .. 99, sillä ohjelma on ajateltu tällaista suoritusta varten. Rajoituksia ja tarkastuksia ei ohjelmassa ole, mutta kahden suuren luvun kertominen (tulo > 2exp15 = 32768) antaa tulokseksi negatiivisen luvun, koska käytetyt muuttujat ovat int-tyyppiä.
Tässä sovelluksessa sarjaluenta kehittyy hiukan pidemmälle. Sitä sovelletaan nelilaskimena. Hyviä laskimia voi ostaa edullisesti, joten jos nelilaskin olisi päämäärä, ei siinä olisi juuri järkeä. Tavoite onkin synnyttää luotettava luenta ja huolimatta pitkästä painamisesta (siis millisekuntiskaalassa), siihen reagoidaan ainoataan yhden ohjelmakierroksen ajan. Jos tätä toteutusta hiukan muuttamalla haluaisi käyttää johonkin ”järkevään”, voisi sen tehdä tulostamaan sattumanvaraisesti sekä oikeita sanoja että epäsanoja. Käyttäjä painaisi heti tulkinnan jälkeen kummasta on kyse. Aivoilta menee nimittäin kauemmin epäsanan kuin tutun sanan tulkitsemiseen. Myöhemmin voisi tulostaa aikaerot. Kyse on samasta ilmiöstä kun PUNAINEN kirjoitetaan vihreällä värillä ja sana pitäisi lukea ääneen, aiheuttaa se hiukan hankaluutta.
Näppäimistön painikejärjestys.
     Aluksi ajattelin tehdä kytkinvärähtelyn eliminoimisen huolella, mutta satojen painamisten yhteydessä ei syntynyt ainuttakaan virhetulkintaa, joten jätin sen pois kasvattamasta ohjelmaa. Se voitaisiin tehdä seuraaavasti: Kun ohjelma havaitsee painalluksen, tehdään uusi luenta vaikkapa viiden (5) millisekunnin kulutta, ja verrataan näin saatua kahta taulukkoa toisiinsa. Jos ne ovat yhteneväiset, hyväksytään tulkinta. Jos eivät, toistetaan uusi luenta, kunnes ne ovat samat.
     Toinen tavoite oli laajentaa kaksirivisen näytön mahdollisuuksia lisäämällä MENU-ominaisuus. Listaa voi selata ylös- ja alaspainikkeilla. Sivuttaisnuolet eivät ole käytössä tässä ohjelmassa.
     Ohjelma on hyvin aliohjelmapainotteinen. Pääohjelma ei käsitä juurikaan muuuta kuin aliohjelmakutsuja. Ensimmäisenä tunnistetaan, että jotain on painettu (Int_Nappain > 0). Sen jälkeen suljetaan uusi tunnistaminen (= painaminen jatkuu). Sitten ohjelma katsoo, onko kyse numerosta (Näppäin välillä 1 .. 10). Seuraaavaksi tunnistetaan muut painikkeet ja sijoitetaan niihin koodi. Tämän ryhmän viimeisenä suljetaan uusi luenta (Bol_NappVapa = false), jolloin uusi luenta estyy. Suraavaksi jäädään odottamaan painikkeiden vapautusta (ja eston vapauttamista). Pääohjelman lopuksi suoritetaan halutut aliohjelmaketjut (aliohjelma kutsuu toista aliohjelmaa).


Ohjelma kysyy käynnityksen jälkeen ensimmäistä lukua ja kehoittaa painamaan ENTteriä. Kun tämä on tehty näytetään kehoitus toisen luvun antamiseen. Toisen ENTterin jälkeen ohjelma laskee neljä laskutehtävää (summa, erotus, tulo ja osamäärä) ja näytön alarivin hallinta siirtyy ylös- ja alas-painikkeille. Näillä voidaan siirtyä edestakaisin noiden tulosten katselussa. Tästä tilasta paistutaan painamalla MENU-painiketta, jolloin ohjelma siirtyy kysymään ensimmäistä numeroa uudestaan. Suositeltavaa on käyttää lukualuetta välillä 1 .. 99, sillä ohjelma on ajateltu tällaista suoritusta varten. Rajoituksia ja tarkastuksia ei ohjelmassa ole, mutta kahden suuren luvun kertominen (tulo > 2exp15 = 32768) antaa tulokseksi negatiivisen luvun, koska käytetyt muuttujat ovat int-tyyppiä.

Aliohjelmat ovat tällä sivulla kokonaisina, sillä niitä on siten helpompi kopioida ja muokatta kuin etsiskellä eri paikoista. LCD-aliohjeman parametreihin EN ole lisännyt liukulukua, vaan osamäärän tulostus näytölle tapahtuu suoraan Fun_Laskenta-aliohjelman askeleesta kahdeksan (8). Saman aliohjelman askel neljä (4) voi halutessa tulostaa laskutoimitukset ohjelmointityökalun ruudulle testejä varten. Painikkeiden tunnistamista varten on oma aliohjelma (Fun_NappTulkki(int askel){). Sitä kutsutaan pääohjelmasta siten, että kutsuparametri ohjaa painiketta vastaavaan sekvenssin askeleeseen. Numerot (0 .. 9) käsitellään askeleessa 1 ja muille painikkeille on omat askeleensa. Aliohjelmien toimintaketjut lopetetaan syöttämällä sekvenssin valinta-arvoksi nolla (0).





OHJELMA 38
/*************************
* Ohjelma 38
* 23.02.2017
* Tosi lyhyt matikka
*************************/

// MÄÄRITTELYT:
// LCD-näyttö
#include <LiquidCrystal.h>
LiquidCrystal lcd(3, 2, 7,6,5,4);

// Muut määrittelyt
const int Con_Pit = 16; // Datataulukon pituus
int Int_Nappain = 0;
int Int_Numero = 0;
boolean Bol_NappPain = false;
boolean Bol_NappVapa = true;
int Int_LaskVaihe = 1; //Laskentatapahtuman vaihe
int Seq_Laskenta = 0;
int Seq_NappOhjaus = 0; // Näppäinten ohjaussekvenssi
int Int_NappOhjaus = 0; // Näppäimen numero
int Int_Luku1 = 0;
int Int_Luku2 = 0;
int Int_Summa = 0;
int Int_Erotus = 0;
int Int_Tulo = 0;
float Flo_Osamaara = 0.0;
float L1 = 0.0; float L2 = 0.0;

// ALIOHJELMAT
// Funktio Parallel IN serial OUT (Rinnan sisään, sarjassa ulos)
// SarjaIN_Ali_v3.No2Hel17
   int Fun_LueRsSu(int PL, int CP, int Data, int Pituus){
      int NappainNumero = 0;
      int i = 0;
      boolean Taulukko[Pituus];
      boolean Bitti = false;
   pinMode(PL, OUTPUT); // Rinnan -> sarja siirto, pinnin numero
   pinMode(CP, OUTPUT); // Kellotus, pinnin numero
   pinMode(Data, INPUT);// Dataluennan pinnin numero
// Rinnakkaisdatan siirto
   digitalWrite(PL, LOW);
   digitalWrite(PL, HIGH);
      for( i = 0; i < Pituus; i++){ // Sarjadatan talletus
      Taulukko[i] = digitalRead(Data);
   digitalWrite(CP, HIGH);
   digitalWrite(CP, LOW);
}// sarjaluennan loppu
   for( i = 0; i < Pituus; i++){ // Onko taulukossa 1-bitti
      if(Taulukko[i] == true){
         NappainNumero = Pituus - i; // "bittien kääntö"
      }// if oli 1-bitti loppu
   }// Näppäin numeron tulkinta 0 == 1
return NappainNumero;
}// Funtion loppu

// Lukujen käsittelyfunktio
void Fun_Laskenta(int askel, int painike){
   Seq_Laskenta = askel;
   switch (Seq_Laskenta) {
   case 1:
      Int_Luku1 = Int_Luku1 * 10 + painike - 1;
         if(Int_Luku1 < 10){
           Fun_LCD(11,1,10,Int_Luku1);
         }else{
            Fun_LCD(11,1,9,Int_Luku1);
         } // if Luvun pituusvalinnan loppu
   Seq_Laskenta = 0;
   break;
   case 2:
      Int_Luku2 = Int_Luku2 * 10 + painike - 1;
         if(Int_Luku2 < 10){
            Fun_LCD(11,1,10,Int_Luku2);
         }else{
            Fun_LCD(11,1,9,Int_Luku2);
         } // if Luvun pituusvalinnan loppu
   Seq_Laskenta = 0;
   break;
   case 3:
      Int_Summa = Int_Luku1 + Int_Luku2;
      Int_Erotus = Int_Luku1 - Int_Luku2;
      Int_Tulo = Int_Luku1 * Int_Luku2;
      L1 = Int_Luku1; L2 = Int_Luku2;
      Flo_Osamaara = L1 / L2;
   lcd.clear();
   Seq_Laskenta = 0;
   break;
      case 4: // Tämä askel on vain testiä varten, ei suorituksessa
      Serial.print("Summa =: "); Serial.println(Int_Summa);
      Serial.print("Erotus =: "); Serial.println(Int_Erotus);
      Serial.print("Tulo =: "); Serial.println(Int_Tulo);
      Serial.print("Osamaara =: "); Serial.println(Flo_Osamaara);
      Seq_Laskenta = 0;
   break;
   case 5: // Summa näyttöön
      Fun_LCD(4,0,0,0);
      Fun_LCD(11,0,8,Int_Luku1);
      Fun_LCD(5,0,11,0);
      Fun_LCD(11,0,14,Int_Luku2);
      Fun_LCD(6,1,0,0);
      Fun_LCD(11,1,10,Int_Summa);
   Seq_Laskenta = 0;
   break;
   case 6: // Erotus näyttöön
      Fun_LCD(23,1,0,0);
      Fun_LCD(7,1,0,0);
      Fun_LCD(11,1,11,Int_Erotus);
   Seq_Laskenta = 0;
   break;
   case 7: // Tulo näyttöön
      Fun_LCD(23,1,0,0);
      Fun_LCD(8,1,0,0);
      Fun_LCD(11,1,11,Int_Tulo);
   Seq_Laskenta = 0;
   break;
   case 8: // Osamäärä näyttöön
      Fun_LCD(23,1,0,0);
      Fun_LCD(9,1,0,0);
         lcd.setCursor(15, 1);
         lcd.print(Flo_Osamaara);
   Seq_Laskenta = 0;
   break;
   }// sekvenssin loppu
}// Laskentafunktion loppu

// Nappain_Ali_v1.No2Hel17
   void Fun_NappTulkki(int askel){
    Seq_NappOhjaus = askel;
    switch (Seq_NappOhjaus) {
   case 1: // Numerot 0 .. 9
      Fun_Laskenta(Int_LaskVaihe, Int_Numero);
      Seq_NappOhjaus = 0;
   break;
   case 2: // ENTER no: 10
      if(Int_LaskVaihe == 1){Fun_LCD(2,1,0,0);}
         Fun_LCD(22,1,9,0); // Luvun syöttöpaikan tyhjennys
         Int_LaskVaihe++;
      if(Int_LaskVaihe == 3){
         Fun_Laskenta(3, 0);
         Fun_Laskenta(5, 0);
         Int_LaskVaihe = 5;
      } // if Numeroiden sijoitus näyttöön
   Seq_NappOhjaus = 0;
   break;
   case 3: // Nuoli oikealle no: 11 (ei nyt käytössä)
      Seq_NappOhjaus = 0;
   break;
   case 4: // Nuoli ylös no: 12 (paluu tulostusmenussa)
      Fun_LCD(23,1,0,0);
      Int_LaskVaihe--;
         if(Int_LaskVaihe < 5){Int_LaskVaihe = 5;} // Rajoitus
         if(Int_LaskVaihe >= 5){Fun_Laskenta(Int_LaskVaihe, 0);}
            Seq_NappOhjaus = 0;
   break;
   case 5: // Nuoli vasemmalle no: 13 (ei nyt käytössä)
      Seq_NappOhjaus = 0;
   break;
   case 6: // Nuoli alas no: 14 (eteenpäin tulostusmenussa)
      Fun_LCD(23,1,0,0);
      Int_LaskVaihe++;
      if(Int_LaskVaihe > 8){Int_LaskVaihe = 8;}
      if(Int_LaskVaihe < 9){Fun_Laskenta(Int_LaskVaihe, 0);}
   Seq_NappOhjaus = 0;
   break;
   case 7: // MENU, keskipainike, no: 15 (paluu alkuun)
      Int_LaskVaihe = 1;
      Int_Luku1 = 0;
      Int_Luku2 = 0;
      Fun_LCD(3,0,0,0);
      Fun_LCD(23,1,0,0);
      Fun_LCD(1,1,0,0);
   Seq_NappOhjaus = 0;
   break;
   }// Näppäinohjaus loppu
}// Näppäimenkäsittelyfunktion loppu

// LCD aliohjelman määrittelyt
void Fun_LCD(int start, int rivi, int paikka, int data){
// Näytön valintasekvenssi
   int Seq_LCD = start;
   switch (Seq_LCD){
   case 1:
      lcd.setCursor(paikka, rivi);
      lcd.print("Luku 1:");
      Seq_LCD = 0;
   break;
   case 2:
      lcd.setCursor(paikka, rivi);
      lcd.print("Luku 2:");
   Seq_LCD = 0;
   break;
   case 3:
      lcd.setCursor(paikka, rivi);
      lcd.print("ANNA LUKU ja ENT!");
   Seq_LCD = 0;
   break;
   case 4:
      lcd.setCursor(paikka, rivi);
      lcd.print("LUKUJEN");
   Seq_LCD = 0;
   break;
   case 5:
      lcd.setCursor(paikka, rivi);
      lcd.print("ja");
   Seq_LCD = 0;
   break;
   case 6:
      lcd.setCursor(paikka, rivi);
      lcd.print("SUMMA ON:");
   Seq_LCD = 0;
   break;
   case 7:
      lcd.setCursor(paikka, rivi);
      lcd.print("EROTUS ON:");
   Seq_LCD = 0;
   break;
   case 8:
      lcd.setCursor(paikka, rivi);
      lcd.print("TULO ON:");
   Seq_LCD = 0;
   break;
   case 9:
      lcd.setCursor(paikka, rivi);
      lcd.print("OSAMAARA ON:");
   Seq_LCD = 0;
   break;
   case 11: // Lukujen tulostus(paitsi liukuluku)
      lcd.setCursor(paikka, rivi);
      lcd.print(data);
   Seq_LCD = 0;
   break;
   case 21:
      lcd.setCursor(paikka, rivi);
      lcd.print("   ");
   Seq_LCD = 0;
   break;
   case 22:
      lcd.setCursor(paikka, rivi);
      lcd.print("     ");
   Seq_LCD = 0;
   break;
   case 23:
      lcd.setCursor(paikka, rivi);
      lcd.print("                    ");
   Seq_LCD = 0;
   break;
   case 24: // Koko näytön tyhjennys
      lcd.clear();
   Seq_LCD = 0;
   break;
   }// Valintasekvenssin loppu
}// Funktion LCD loppu

// ASETUKSET:
void setup(){
Serial.begin(9600);
lcd.begin(20,2);
lcd.clear();
// Yläteksti näyttöön, suoritetaan kerran
Fun_LCD(3,0,0,0);
Fun_LCD(1,1,0,0);
}// Asetuksen loppu

// PÄÄLOOPPI
void loop(){

Int_Nappain = Fun_LueRsSu(8, 9, 10, Con_Pit); // luetaan näppäimistö
if(Int_Nappain > 0){Bol_NappPain = true;} // tunnistetaan painaminen
if(Bol_NappPain == true && Bol_NappVapa == true){ // suoritetaan
if(Int_Nappain > 0 && Int_Nappain < 11){
   Int_NappOhjaus = 1;
   Int_Numero = Int_Nappain;
}// Numerot loppu
   if(Int_Nappain == 11){Int_NappOhjaus = 2; }
   if(Int_Nappain == 12){Int_NappOhjaus = 3; }
   if(Int_Nappain == 13){Int_NappOhjaus = 4; }
   if(Int_Nappain == 14){Int_NappOhjaus = 5; }
   if(Int_Nappain == 15){Int_NappOhjaus = 6; }
   if(Int_Nappain == 16){Int_NappOhjaus = 7; }
Fun_NappTulkki(Int_NappOhjaus);
Bol_NappVapa = false;
}// Näppäinten hallinta loppu
if(Int_Nappain == 0){ // == ei mitään painettu
Bol_NappPain = false;
Bol_NappVapa = true;
}// if vapautuksen loppu
if(Seq_Laskenta == 3){ // Suoritetaan nelilaskenta
Int_LaskVaihe = 5;
Fun_Laskenta(Int_LaskVaihe, 0);
} // Laskennan loppu
delay(1);
} // Pääohjelma LOPPU


sunnuntai 12. helmikuuta 2017

KEHITYSTÄ IV

Nyt uskoin tuolle joutilaalle Arduino MICROlle löytäväni käyttöä tässä hiukan laajemmassa näppäinprojektissa. Minulla oli tavoitteena liittää yhteen Arduino, 16 painikkeen näppäimistö ja LCD-näyttö. Tavoitteena oli tehdä kiinteämpi kytkentä ja toteutus, ajatuksena käyttää sitä myöhemmin ja monipuolisemmin.
     Ensimmäinen testikytkentähässäkkä oheisessa kuvassa.
     Sinisen valon oikealla puolella oleva pieni printti on Arduino Micro. Jonkin aikaa näytti hyvältä, mutta sitten tämä mikrokontrolleri sai kyllikseen. Mitä ilmeisimmin syynä oli tuon LCD-näyton liika kuormitus. (Kohta siitä lisää.)
  
Siirryin sitten Arduino LEONARDOon, ja sellaiseen malliin, missä ei liittimiä ole valmiina, vaan kaikki ulkoiset kytkennät tehdään juottamalla kontrollerin piirilevyyn. 
     Testeissä nousi esiin, että 5V regulaattori kävi varsin kuumana, joten tuo arvio Micron rikkoutumisesta lienee oikeaan osunut. LCD-näyttö imi noin 160mA virran 5 voltin jännitteestä. Toisessa kuvassa valmis toteutus näkyy vinosti takaa. Jouduin nimittäin lisäämään laitteen takaosaan alumiinilevyn juuri tuon 5V:n regulaattorin jäähdytystä varten.

Regulaattoripiirit (7809 9V ja 7805 5V) sekä niihin liittyvät suodatinelektrolyyttikondensaattorit ovat alimmalla Vero-levyllä. Samoin rinnan-/sarjapiirit (74HCT165), kuten myös liittimet ulkoisia kytkentöjä varten. Tämän levyn päälle liittyy liittimien välityksellä Arduino LEONARDO ja ylimpänä toinen koelevy, missä on 16 kpl painikkeita sekä kaksi 8 x 10kohm:in vastushybridipiiriä digitaalitulojen kuormitusta varten

Arduino on alaspäin olevilla piikeillä pohjalevyn liittimissä
Päällekkäisrakenne ilmenee oheisesta kuvasta. Noita pieniä liittimiä olen saanut tuhoamistani Arduinoista. Aina tulee sähellettyä! Onneksi kamaa voi kierrättää.
     Vasemmalla näkyvässä vihreän liittimen kautta on laitteen syöttö (12 VDC) ja kolmantena 5 V:n lähtö joitain muita (tulevaisuudessa) tuota syöttöä tarvitsevia laitteita varten. 
     Seuraavana kaksi urospiikkiä, jotka on liitetty Arduinon sarjaliikennepinneihin (D0 RX ja D1 TX). Tässä olen samalla tehnyt ristikytkennän, joten liitettäessä tämä laite toiseen  
     Arduinoon, voi käyttää suoraa parijohdinta. Sitten on kolme naaraspinniä, jotka on liitetty vapaana oleviin digitali I/O:n (D11, D12 ja D13). Viimeisenä neljä (4) naaraspinniä analogiatuloihin (A0 .. A3). Vain neljä on liitetty, koska sopivaa kuuden (6) pinnin liitintä ei ollut. Noilla neljälläkin pärjää aika pitkälle.

Lopputulos näyttää seuraavalta: ihan oikealta ITEprototyypiltä.

Näytön yläriville Arduino on ilmaissut asenteensa tätä minun blogiprojektiani kohtaan ja alarivilla vasemmalla painikkeen numero (1 .. 16) ja oikealla vastaava bittijono (bitin paikka). YouTube-videolla (antinarduvideo37.youtube.com) on pieni esittely näppäimistöstä. Siinä numerot vaihtuvat välillä1 .. 16, eli painikkeen numero näkyy vasemmalla ja vastaavasti paikka bittijonossa oikealla.



 
Painikeryhmän kaavio ohessa. Siihen on merkitty kunkin painikkeen numeerinen arvo (0 .. 15). Tässä toteutuksessa ne ovat ainoastaan painikkeita, jotka voidaan erottaa toisistaan. Myöhemmin ne saavat varsinaisia toimintoja. Seuraavassa koko hässähän kytkentä:
Ja lopuksi tämän kertaisen sovelluksen ohjelma. Tästä ohjelmasta olen jättänyt pois kaiken sellaisen, mikä ei liity juuri tähän toteutukseen.

OHJELMA 37
/***************************************
* Ohjelma 37
* 09.02.2017
* Bitit ja painikkeen numero
**************************************/

// MÄÄRITTELYT:
// LCD-näyttö
#include <LiquidCrystal.h>
LiquidCrystal lcd(3, 2, 7,6,5,4);
boolean Arr_DATA[16];
int Int_Numero = 0;

// ALIOHJELMAT
// LCD aliohjelman määrittelyt
void Fun_LCD(int start, int rivi, int paikka, int data){
// Näytön valintasekvenssi
    int Seq_LCD = start;
    switch (Seq_LCD){
case 2:
    lcd.setCursor(paikka, rivi);
    lcd.print("LAITAN TAAS HANTTIIN");
    Seq_LCD = 0;
break;
case 11:
    lcd.setCursor(paikka, rivi);
    lcd.print(data);
    Seq_LCD = 0;
break;
}// Valintasekvenssin loppu
}// Funktion LCD loppu

// Funktio Parallel IN serial OUT (Rinnan sisään, sarjassa ulos)
// SarjaIN_Ali_v1.No4Tam17
void Fun_LueRsSu(int PL, int CP, int Data){
    pinMode(PL, OUTPUT); // pinnin numero kutsussa
    pinMode    (CP, OUTPUT); // pinnin numero kutsussa
    pinMode    (Data, INPUT);// pinnin numero kutsussa
// Rinnakkaisdatan siirto
    digitalWrite(PL, LOW);
    digitalWrite    (PL, HIGH);
    for(int i = 0; i < 16; i ++){Arr_DATA[i] = 0;}// tyjennys
        for(int j = 0; j < 16; j++){ // luku
        Arr_DATA[j] = digitalRead(Data);
        digitalWrite(CP, HIGH);
        digitalWrite(CP, LOW);
    }// sarjaluennan loppu
}// Funtion loppu

// ASETUKSET:
void setup(){
    Serial.begin(9600);
    lcd.begin(20,2);
    lcd.clear();
// Tekstit näyttöön, suoritetaan kerran
    Fun_LCD(2,0,0,0);
}// Asetuksen loppu

// PÄÄLOOPPI
void loop(){
    Fun_LueRsSu(8, 9, 10);
    for(int No = 0; No < 16; No++){ // Numeron näyttö
        if(Arr_DATA[15-No] == 1){
        Int_Numero = No + 1;
            if(Int_Numero < 10){ // Numerot 0 .. 9
                Fun_LCD(11,1,1,Int_Numero);
                lcd.setCursor(0,1);
                lcd.print(" "); // Tyjennetään kymppien paikka
            }else{
                Fun_LCD(11,1,0,Int_Numero);
            } // else if loppu
            }// if loppu
    }// for loppu
    for(int No = 0; No < 16; No++){ // bittien näyttö
        lcd.setCursor(No + 4, 1);
        lcd.print(Arr_DATA[No]);
    }// for loppu

    delay(1);
} // Pääohjelma LOPPU