lauantai 2. syyskuuta 2017

KORKEAMPI JÄNNITE I

Arduinon nopeutettua digitaaliohjausta voi hyödyntää pumppaamalla kondensaattorien ja diodien alulla jännitettä korkeammaksi. Tässä hyödynnetään ns. Dicksonin pumppu tai kertoja (Dickson charge pump, or Dickson multiplier ). Tällä menetelmällä voidaan tasajännitelähteen jännite nostaa korkeammaksi.

Tässä esimerkissä kytkentä on suoraan Arduinon digitaalilähdöissä ja syöttöjännitteenä on mikrokontrollerin +5 V:n lähtö. Jos lähtöjännitteenä on esim. 12 V, tarvitaan digitaalilähtöjen ja ”pumppupiirin” väliin erilliset kytkimet. 
    Suuria tehoja ei tällä (esimerkissä olevalla) kytkennällä voi saada aikaan. 
    Digitaalilähtöjen maksimi kuorma saa olla noin 40 mA. Saatu jännite on noin lähtöjännite kerrottuna diodi / kondensaattoriketjujen lukumäärällä ja vähennettynä diodien myötäjännitteen aiheuttamalla häviöllä. 


    Kuvassa mittari näyttö hiukan tuon ylikin, sillä viimeinen suodatuskondensaattori latautuu piirissä olevaan häiriöpiikkiin, sillä piiri on tässä tapauksessa kuormittamaton.
Kytkentä on varsin yksinkertainen, samoin ohjelma. Tässä ohjelmassa olen ”pumppausvaiheen” sijoittanut aliohjemaan kuvatakseni sitä, että pääohjelmassa voi tehdä jotain muuta tarvittaessa.
Arduino UNO:ssa sisäiset muistipaikat sijoittuvat siten, että digitaalipinnejä 0 .. 7 vastaa PORTD, pinnejä 8 .. 13 PORTB ja analogiapinnejä 0 .. 5 sekä samalla digitaalipinnejä 14 .. 19 PORTA. Fyysisiä analogiapinnejä voidaan käyttää myös digitaalituloina ja -lähtöinä.

OHJELMA 47
/*******************************
* Ohjelma OhjelmaTest_47v2
* 30.8.2017
* Jännitteen pumppaus korkeammaksi.
*
*/

// Määrittelyt
   const int Con_Pinni1 = 2;
   const int Con_Pinni2 = 3;

// Aliohjelmat
   void Fun_Muunnin(){
      int i = 0;
      for (i = 0; i < 10; i++){
         bitSet(PORTD,Con_Pinni1);
         delayMicroseconds(3);
         bitClear(PORTD,Con_Pinni1);
         delayMicroseconds(2);
         bitSet(PORTD,Con_Pinni2);
         delayMicroseconds(3);
         bitClear(PORTD,Con_Pinni2);
         delayMicroseconds(2);
      }
   }// Jännitteen nostajan loppu

// Asetukset
void setup() {
   pinMode(Con_Pinni1, OUTPUT);
   pinMode(Con_Pinni2, OUTPUT);
   Serial.begin(9600);
}

void loop() {
   Fun_Muunnin();
   delay(1);
}// Pääohjelman loppu

tiistai 29. elokuuta 2017

SERVO III

Useampaakin servoa on mahdollista ohjata samanaikaisesti. Tällöin kirjastomoduuli (#include <Servo.h>) on käyttökelvoton, joten sitä ei tarvitse liittää mukaan ohjelmaan. Myös digitaalipinnin ohjauskäsky (digitalWrite()) on liian hidas tähän ohjaukseen. Nykyisiä servoja ohjataan pulssin pituudella, jolloin pulssin kesto 1 ms vastaa 0-asteen asentoa ja 2 ms vastaavasti 180 asteen asentoa. Muut pituudet siltä väliltä.
    Tässä ohjelmassa pulssin kestoajat on muodostettu viiveellä (delayMicroseconds(kesto us);). Jotta päästäisiin tarvittaviin nopeuksiin, ei voida ohjata lähtöbittiä suoraan, vaan suoraan kovopuolella lähtöbittiä käsittelevää kanavaa. Käskyt ovat (bitSet(PORTD,pinni); ja bitClear(PORTD,pinni);). Nuo käskyt eivät ole funktioita, vaan itse asiassa makroja, jotka ohjaavat toimintaa suoraan kovopuolella. Makro bitSet asettaa lähdön ykköseksi (1, +5V) ja bitClear luonnollisesti palauttaa lähdön nollaan (0). PORTD on se tavu, missä sijaitsee lähtöbittien 0 .. 7 ohjaukset. (Näistä hiukan enemmän seuraavassa osiossa.)
Tämä ei taida enää olla ihan viimeistä huutoa?
     Tässä sovelluksessa hain kokeilemalla kääntymäkulmien ääriarvot (pulssin pituuden arvot välillä 1 .. 2 ms), joiksi sain nolla (0) astetta noin 600 us ja 180 astetta noin 2500 us. Todellisia pulssien pituuksia en mitannut. Ainoastaan tarkastelin kääntymäkulmia. Miksi näyttäisi olevan noin paljon heittoa, en siis tiedä.
    Kokeilu ja ohjaus tapahtui siten, että muutin pulssin pituusarvoa ( Uin_Luku ) ja latasin ohjelman Arduinoon, jolloin latauksen mentyä molemmat servot kääntyivät samanaikaisesti.

OHJELMA 46
/*************************************
* Ohjelma Servo_46
* 29.8.2017
* Kahden servon samanaikainen ohjaus
*
*/

// Määrittelyt
   const int Con_Pinni1 = 3;
   const int Con_Pinni2 = 2;
   unsigned int Uin_Luku = 1500;

void setup() {
   pinMode(Con_Pinni1, OUTPUT);
   pinMode(Con_Pinni2, OUTPUT);
   Serial.begin(9600);
}// Määrittelyt loppu

void loop() {

   bitSet(PORTD,Con_Pinni1);
   delayMicroseconds(Uin_Luku);
   bitClear(PORTD,Con_Pinni1);

   bitSet(PORTD,Con_Pinni2);
   delayMicroseconds(Uin_Luku);
   bitClear(PORTD,Con_Pinni2);

delay(10);
}// Pääohjelma loppu

lauantai 26. elokuuta 2017

SERVO II

Sauvat alussa parkissa
Tämän ohjelman nimi kertoo (sattumalta), että tässä ohjataan kahta servoa ”samanaikaisesti”. Tuo määrittely lainausmerkeissä, sillä samanaikaisuus on näennäistä, koska aliohjelma on yhteinen molemmille, toisen pitää ehtiä kääntymään paikalleen, ennen kuin naapuri saa lähteä liikkeelle. Servo-objektin nimenä on nyt Taulu. Tässä ei määrittelyissä kytketä tuota nimeä kirjasto-ohjelmalle (#include <Servo.h>; Servo Taulu; Taulu.attach(pinni), vaan ohjauksen aliohjelmassa, koska kummallakin servolla on oma ja eri ohjauspinninsä.
    Tämä sovellus ulottuu vielä Suomen itsenäisyyttäkin varhaisempaan aikaan. Onneksi ei sentään syntynyt savumerkkejä, vaan tällainen kirjaintaulu, missä kahden varren risteyskohdalla voidaan ”kirjoittaa langattomasti”; kunhan vain näköyhteys on voimassa. Aikoinaan tällaisia oli käytössä viesti-ihmisillä lippujen ja valojen lisäksi.
    Ohjelman käynnistyessä varret menevät odottamaan ”parkkipaikalle”. Käynnistyspainikkeen painaminen saa varret kääntymään vaakasuoraan (START) hetkeksi ja aloittamaan sen jälkeen merkkien poiminnan. Sanojen välillä varret myöskin viivähtävät parkissa. Kun sanottava on tullut kerrotuksi, hymyilee laite itselleen.

Sanomaa en tuo tässä esiin. Se on helpoiten nähtävissä YouTube-videosta. Google ei ole erityisen innokas haeskelemaan näitä minun videoitani, joten helpoiten ne löytyvät avaamalla YouTube ja kopioimalla videon osoite hakuriville (antinarduvideo45.yotube.com). Sieltä ne ponnahtavat tarjolle aiemmatkin pätkät.
Lopussa on hyvä hymyillä!
    
Pienoismallien ohjauksen lisäksi servoille on paljonkin käyttöä. Twitterissä huomasin jokin aika sitten hauskan idean: automaattinen roskiksen kansi. Siinä oli Arduinoon liitetty liiketunnistin, ja kun käsi lähestyi, painoi servo jalkakytkintä ja avasi kannen. Ja tietysti sulki sen jälkeen, kun käsi poistui tunnistealueelta. Kahdella servolla voisi tehdä vaikkapa piirturin. Toinen servo piirtää paperille ja toinen nykii paperia eteenpäin.
   Ohjelmallisesti tässä ei; tuon dynaamisen kirjasto-ohjelmamäärittelyn lisäksi ole mitään erityistä. Pohdin sitä, että olisiko ollut järkevää viiveet suorittaa aliohjelmassa, mutta en nähnyt sen juurikaan typistävän ohjelman mittaa. Tässä ajastuksen perustana on sisäisen kellon käsky (millis();). Jokaisella ohjelmakierroksella luetaan sen hetkinen arvo. Jos se on saavuttanut seuraavan aikaportaan (Int_AikaEro = 100;), hypätään viiveen aliohjemaan, missä asetetaan seuraava aikaporras (ero on aina 100 millisekuntia) ja asetetaan todeksi boolean muuttuja (Bol_Aika = true;). Tällä vähennetään viivelaskureita sekvenssin askelissa. Pääohjelman lopussa asetetaan tämä tilabitti nollaksi (Bol_Aika = false;).

OHJELMA 45
/*******************************
* Ohjelma 45
* 25.08.2017
* Kahden servon ohjaus
********************************/
 #include <Servo.h> // Servon kirjasto-ohjelma
Servo Taulu; // Kehittää servo-objektin
    int kulma = 0;
    const int Con_ServoOikea = 8;
    const int Con_ServoVasen = 9;
    const int Con_Start = 10;
    boolean Bol_Start = false;
    const int Con_TestLED = 13;

// Kirjaintaulukot
   int Arr_Vas[14] = {41,61,71,77,26,45,55,65,16,34,45,55,2,93};
   int Arr_Oik[14] = {9,11,18,35,20,26,37,56,30,37,48,63,80,0};

// Kellon määrittelyt:
   unsigned long Ulo_MilliSek = 0;
   int Int_AikaEro = 100;
   unsigned long Ulo_UusiMilliSek = Int_AikaEro;
   boolean Bol_Aika = false;

// Ohjausseqvenssi
   int Seq_Servo = 1;
   const int Con_MerkkiVali = Int_AikaEro/5;
   int Int_MerkkiVali = Con_MerkkiVali;
   const int Con_SanaVali = Int_AikaEro * 15;
   int Int_SanaVali = Con_SanaVali;
   const int Con_SARTviive = Int_AikaEro/5;
   int Int_STARTviive = Con_SARTviive;
   int Int_MerkkiViive = Int_AikaEro * 3;

// Aliojelmat
   void Fun_Ohjaus( int kulma){
      Taulu.attach(Con_ServoOikea);
      Taulu.write(Arr_Oik[kulma]);
        delay(400);
      Taulu.attach(Con_ServoVasen);
      Taulu.write(Arr_Vas[kulma]);
         delay(400);
   }// Ohjauksen funktion loppu

   void Fun_Viive(){
      Ulo_UusiMilliSek = Ulo_UusiMilliSek + Int_AikaEro;
      Bol_Aika = true;
   }// Viive-funktion loppu

//Asetukset
   void setup() {
   Serial.begin(9600);
   pinMode(Con_Start, INPUT);
   }// Asetusten loppu

//PÄÄOHJELMA
   void loop() {
// Kellotus:
      Ulo_MilliSek = millis();
         if(Ulo_MilliSek >= Ulo_UusiMilliSek){
         Fun_Viive();}
// Käynnistyskytkimen luku
      Bol_Start = digitalRead(Con_Start);
// Servojen ohjaussekvenssi
   switch (Seq_Servo) {
      case 1: // Alkuasento PPP
         Fun_Ohjaus(12);
        delay (Int_SanaVali);
      Seq_Servo = 2;
   break ;
   case 2: // Starttipainike
     if (Bol_Start == true){Seq_Servo = 3;}
   break ;
   case 3: // Ohjaus Starttiin
      Fun_Ohjaus(13);
     delay (Int_AikaEro * 2);
      Seq_Servo = 4;
   break ;
   case 4: // Viipyminen Startissa
     if (Bol_Aika == true){ Int_STARTviive--; }
        if (Int_STARTviive == 0){
           Int_STARTviive = Con_SARTviive;
           Seq_Servo = 5;}
   break ;
   case 5:
      Fun_Ohjaus(8);
     delay (Int_MerkkiViive);
      Seq_Servo = 6;
   break ;
   case 6:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
           Int_MerkkiVali = Con_MerkkiVali;
           Seq_Servo = 7;}
   break ;
   case 7:
      Fun_Ohjaus(10);
     delay (Int_MerkkiViive);
      Seq_Servo = 8;
   break ;
   case 8:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 9;}
   break ;
   case 9:
      Fun_Ohjaus(3);
     delay (Int_MerkkiViive);
      Seq_Servo = 10;
   break ;
   case 10:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 11;}
   break ;
   case 11:
      Fun_Ohjaus(10);
      delay(Int_MerkkiViive);
      Seq_Servo = 12;
   break ;
   case 12:
      Fun_Ohjaus(12);
     delay (Int_SanaVali);
      Seq_Servo = 13;
   break ;
   case 13:
      Fun_Ohjaus(5);
     delay (Int_MerkkiViive);
      Seq_Servo = 14;
   break ;
   case 14:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 15;}
   break ;
   case 15:
      Fun_Ohjaus(4);
     delay (Int_MerkkiViive);
      Seq_Servo = 16;
   break ;
   case 16:
      Fun_Ohjaus(12);
     delay (Int_SanaVali);
      Seq_Servo = 17;
   break ;
   case 17:
      Fun_Ohjaus(7);
     delay (Int_MerkkiViive);
      Seq_Servo = 18;
   break ;
   case 18:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 19;}
   break ;
   case 19:
      Fun_Ohjaus(1);
     delay (Int_MerkkiViive);
      Seq_Servo = 20;
   break ;
   case 20:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
         Int_MerkkiVali = Con_MerkkiVali;
         Seq_Servo = 21;}
   break ;
   case 21:
      Fun_Ohjaus(6);
     delay (Int_MerkkiViive);
      Seq_Servo = 22;
   break ;
   case 22:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 23;}
   break ;
   case 23:
      Fun_Ohjaus(9);
     delay (Int_MerkkiViive);
      Seq_Servo = 24;
   break ;
   case 24:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 25;}
   break ;
   case 25:
      Fun_Ohjaus(5);
     delay (Int_MerkkiViive);
      Seq_Servo = 26;
   break ;
   case 26:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
           if (Int_MerkkiVali == 0){
               Int_MerkkiVali = Con_MerkkiVali;
               Seq_Servo = 27;}
   break ;
   case 27:
      Fun_Ohjaus(7);
     delay (Int_MerkkiViive);
      Seq_Servo = 28;
   break ;
   case 28:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 29;}
   break ;
   case 29:
      Fun_Ohjaus(1);
     delay (Int_MerkkiViive);
      Seq_Servo = 30;
   break ;
   case 30:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 31;}
   break ;
   case 31:
      Fun_Ohjaus(3);
     delay (Int_MerkkiViive);
      Seq_Servo = 32;
   break ;
   case 32:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 33;}
   break ;
   case 33:
      Fun_Ohjaus(0);
     delay (Int_MerkkiViive);
      Seq_Servo = 34;
   break ;
   case 34:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 35;}
   break ;
   case 35:
      Fun_Ohjaus(2);
     delay (Int_MerkkiViive);
      Seq_Servo = 36;
   break ;
   case 36:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 37;}
   break ;
   case 37:
      Fun_Ohjaus(5);
     delay (Int_MerkkiViive);
      Seq_Servo = 38;
   break ;
   case 38:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 39;}
   break ;
   case 39:
      Fun_Ohjaus(6);
     delay (Int_MerkkiViive);
      Seq_Servo = 40;
   break ;
   case 40:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 41;}
   break ;
   case 41:
      Fun_Ohjaus(11);
     delay (Int_MerkkiViive);
      Seq_Servo = 42;
   break ;
   case 42:
     if (Bol_Aika == true){ Int_MerkkiVali--; }
        if (Int_MerkkiVali == 0){
            Int_MerkkiVali = Con_MerkkiVali;
            Seq_Servo = 43;}
   break ;
   case 43: // Uusi startti
     if (Bol_Start == true){Seq_Servo = 1;}
   break;
}// Ohjaussekvenssi loppu

Bol_Aika = false;
delay(1);
}//Pääohjelma Loppu