Home > Uncategorized > … enn um kóða

… enn um kóða

Áður hefur verið fjallað um kóðann sem les bókstafi úr Hershey stafrófinu og þýðir færslu milli tveggja punkta í kartesísku hnitakerfi yfir á form sem lýsir færslu í kerfinu. Nú er komið að því að fjalla stuttlega um hvernig þær upplýsingar eru túlkaðar til að framkvæma sjálfa færsluna. Sú túlkun fer fram í kóða sem er keyrður á arduino örtölvunni. (Neðst í færslunni eru hlekkir sem vísa beint á allan kóða sem notaður var í verkefninu)

Virkni kóðans er í stórum dráttum tvískipt:

1) Í aðal-lykkju forritsins er tekið á móti skipunum frá notanda og gengið úr skugga um að þær skipanir séu á réttu sniði

2) Innri skeiðklukka keyrir reglulega interrupt lykkju sem stýrir því hvernig mótorar eru púlsaðir.

Fyrst þarf að hlaða inn söfnum af kóða frá þriðja aðila. Þau eru: math.h, servo.h og mstimer2.h.

#include <MsTimer2.h>  // timer library, fyrir interrupt adgerdina
#include <math.h>      // math library, fyrir reikningsadgerdir
#include <Servo.h>     // servo library, fyrir servo motor sem styrir armi
Servo penna_servo;     // skapa tilvik af Servo klasanum: penna_servo

Einu nauðsynlegu pinnarnir eru eftirfarandi:
*einn pinni fyrir servó mótorinn sem færir penna af og á töflu
*pinnar sem stýra skrefi og stefnu fyrir hvorn stepper-mótor (samtals fjórir pinnar)
Útgangar örtölvunnar eru skilgreindir hér:

int STEPV = 38;                   // pinni 38 = skref vinstri motor
int DIRV = 36;                    // pinni 36 = stefna vinstri motor
int STEPH = 42;                   // pinni 42 = skref haegri motor
int DIRH = 40;                    // pinni 40 = stefna haegri motor
int SERVO_PIN = 9;                // pinni 9 = servo motor sem styrir penna

Skilgreindar eru (global) breytur:

int dir1, rm1, dir2, rm2, pen;    //geyma serial skilabod fra tolvu
int err;    //villumelding fyrir serial samskipti (1 ef villa, 0 annars)
int pennastada=0;    //"pennastada" man i hvada stodu penni er (0 er uppi)
int fast_motor;    //motor sem skal snuast fleiri skref (0: vinstri, 1: haegri)
volatile int stada=0;    //telur fjolda skrefa sem fast_motor hefur tekid
int skref=0;    //fjoldi skrefa motors sem skal taka fleiri skref en hinn
int stutt=0;    //fjoldi skrefa motors sem skal taka faerri skref en hinn
long stuttskref=0;
long fyrra_stuttskref=0;
int idle=1;    //gefur til kynna hvort taeki se ad framkvaema skipun eda ekki

Breyturnar „stuttskref“ og „fyrra_stuttskref“ eru skilgreindar fyrir interrupt aðgerðina (sjá síðar).

Þá eru gerðar eftirfarandi frumstillingar:
*allir pinnar skilgreindir sem úttak
*allir pinnar settir LOW í upphafi
*interrupt skeiðklukka er stillt, þannig að fallið timerfcn() mun keyrast með tveggja millisekúndna millibili eftir að hún er ræst.

void setup() {
  Serial.begin(115200);           // Serial samskipti um USB, baud rate
                                  // baud rate: 115200 bitar a sekundu.
  pinMode(STEPV,OUTPUT);          // Utgangar fyrir styringu stepper motora.
  pinMode(DIRV,OUTPUT);           // ...
  pinMode(STEPH,OUTPUT);          // ...
  pinMode(DIRH,OUTPUT);           // ...
  digitalWrite(STEPV,LOW);        // Allir utgangar eru LOW i upphafi.
  digitalWrite(DIRV,LOW);         // ...
  digitalWrite(STEPH,LOW);        // ...
  digitalWrite(DIRH, LOW);        // ...

  penna_servo.attach(SERVO_PIN);  // skilgreini pinna fyrir servo-motor
  penna_servo.write(90);          // penni er uppi i upphafi

  // stilla skeidklukku
  MsTimer2::set(2, timerfcn);     // MsTimer2::set(lota i millisekundum, interrupt adgerd)
}

Lítum þá á aðal-lykkju forritsins sem les sífellt af serial-porti til að taka á móti nýjum skipunum. Í upphafi hverrar umferðar er sett „err=0;“ sem þýðir „í upphafi aflesturs er engin villa“. Þá er gerð tilraun til að lesa af serial-porti streng sem hefur formið “fnnnnfnnnnu” (útskýrt í síðasta pósti).

Föllin formerki(), tolustaftur() og uppnidur() sjá til þess að réttar upplýsingar séu skráðar í hvert svið (að formerki komi þar sem búist er við formerki og svo framvegis), eða annars mun vera gefin villumelding (err=1;). Þegar aflestri er lokið er athugað hvort einhver  villumelding hafi orðið.

Ef err=1, þýðir það að ekki hefur tekst að lesa streng  á réttu formi og í því tilfelli er sent til baka spurningarmerki til að gefa notandanum tækifæri á að senda skipunina aftur og forritið fer aftur á byrjunarreit lykkjunnar. Það skal tekið fram, að ef serial helst tómur verða gefin villuskilaboð, og röð spurningamerkja send til notanda.

Ef err=0 við lok lesturs, merkir það að tekist hefur að taka á móti skipun sem er á skiljanlegu formi. Í því tilfelli eru skilaboðin send inn í fallið motors.

Ef serial buffer er að fyllast, er sendur til baka bókstafurinn K, til að láta notanda vita að örtölvan þurfi tíma til að vinna úr því sem þegar hefur verið sent.

void loop() {
  err=0;                          // adur en lestur hefst er engin villumelding !

  // serial lestur hefst    
  dir1=formerki();	          // 1=rettsaelis, -1=rangsaelis, 0=error
  rm1=tolustafur()*1000;          // thusundir skrefa a vinstri motor
  rm1+=tolustafur()*100;          // hundrud skrefa a vinstri motor
  rm1+=tolustafur()*10;           // tugir skrefa a vinstri motor
  rm1+=tolustafur()*1;            // einstok skref a vinstri motor

  dir2=formerki();                // 1=rettsaelis, -1=rangsaelis, 0=error
  rm2=tolustafur()*1000;          // thusundir skrefa a haegri motor
  rm2+=tolustafur()*100;          // hundrud skrefa a haegri motor
  rm2+=tolustafur()*10;           // tugir skrefa a haegri motor
  rm2+=tolustafur()*1;            // einstok skref a haegri motor

  pen=uppnidur();                 // 1=penni upp, 0=penni nidur / error

  if (idle&&err) {	          // ef plotter er idle og error ...
    Serial.println("?");          // ... senda "?" a serial.
  } else {		  	  // Annars hreyfa motora
    idle=0;                       // Plotterinn er ekki lengur laus
    motors(rm1,rm2,dir1,dir2,pen); // Skilgreina hreyfinguna
  }

  if (Serial.available()>=64) {  // ef serial buffer er ad fyllast
    Serial.println("K");         // ... senda 'K' a serial
  }

  delay(10);                     // leyfa arduino ad 'anda'
}

Hér eru föllin formerki(), tolustafur() og uppnidur() sem ganga úr skugga um að strengurinn sem er lesinn inn sé á réttu formi.

// skipun les formerki (+/-)
int formerki(void) {
  char c;
  if (err) {
    return 0;                    // ef komin villa: skila 0
  }
  c=Serial.read();
  if (c=='+') {                  // ef lesinn er "+": skila 1
    return 1;
  }
  if (c=='-') {                  // ef lesinn er "-": skila -1
    return -1;
  }
  err=1;                         // annars er komin villa
  return 0;                      // => skila 0
}

// skipun les tolustaf (0 til 9)
int tolustafur(void) {
  int c;
  if (err) {              
    return 0;                    // ef komin villa: skila 0
  }
  c=Serial.read()-48;
  if ((c<0) || (c>10)) {         // ef lesid er eitthad annad en tolustafur
    err=1;                       // er komin villa
    return 0;                    // => skila 0
  }
  return c;                      // annars skila tolustafnum (0 til 9)
}

// skipun les 'U' eda 'N'
int uppnidur() {
 char c;
 if (err) {                  
   return 0;                     // ef komin villa: skila 0
 } 
 c=Serial.read();
 if (c=='u' || c=='U') {         // ef lesid er "u" eda "U"
   return 1;                     // skila 1
 }
 if (c=='n' || c=='N') {         // ef lesid er "n" eda "N"
   return 0;                     // skila 0
 }
 err=1;                          // annars er komin villa
 return 0;                       // => skila 0
}

Hér á undan var minnst á fallið motors. Fallið stillir snúningsátt hvors mótors og sér til þess að penninn sé í réttri stöðu. Fallið merkir sérstaklega hvor mótorinn eigi að fara fleiri skref (fast_motor=0 ef vinstri motor fer fleiri skref, annars 1), og skráir hærri fjölda skrefa í breytuna skref, en lægri skrefafjölda í breytuna stutt. Að lokum er sjálfvirka interrupt lykkjan, sem stýrir púlsun mótoranna, ræst

void step_motors(int trm1,int trm2,int tdir1,int tdir2, int penni) {
  // Her eru breytur stilltar fyrir interrupt adgerdina. Interrupt adgerdin
  // mun sja til thess a motorar eru hreyfdir a medan adal hluti forritsins
  // getur tekid a moti serial skilabodum fra tolvu.
  // Fyrst tharf ad stilla penna (penni upp eda penni nidur)
  
  // Ef penni skal vera uppi OG penni var adur nidri
  if ((penni==1)&&(penni!=pennastada)) {
    for (int T = 0; T < 25; T++) {          // lyfta penna rolega upp
       penna_servo.write(40+T);        
       delay(50);
       }
       pennastada=penni;                    // uppfaera stodu pennans
  }
  
  // Ef penni skal vera nidri OG penni var adur uppi
  if ((penni==0)&&(penni!=pennastada)) {
     for (int T = 0; T < 25; T++) {         // setja penna rolega nidur
        penna_servo.write(65-T);
        delay(20+T*2);
     }
     pennastada=penni;                      // uppfaera stodu pennans
  }
  
  // Skilgreina fjolda skrefa og snuningsatt a hvorum motor fyrir sig
  rm1=trm1; rm2=trm2; dir1=tdir1; dir2=tdir2;
  
  stada=0;                     // i upphafi hefur ekkert skref verid tekid
  if (trm1>trm2) {             // ef skref fyrir vinstri motor eru fleiri
    skref=trm1;                // er "skref" skilgreint sem sa fjoldi
    stutt=trm2;                // og "stutt" sem fjoldi skrefa a haegri motor
    fast_motor=0;              // => vinstri motor snyst hradar
  } else {                     // annars... alveg ofugt
    skref=trm2;
    stutt=trm1;
    fast_motor=1;              // => haegri motor snyst hradar
  }
  
  if (dir1==1) {               // stilla snuningsatt vinstri motors
    digitalWrite(DIRV,LOW);
  } else {
    digitalWrite(DIRV,HIGH);
  }
  
  if (dir2==1) {               // stilla snuningsatt haegri motors
    digitalWrite(DIRH,LOW);
  } else {
    digitalWrite(DIRH,HIGH);
  }
  
  MsTimer2::start();           // raesa interrupt skeidklukku (hefja hreyfingu)
}

Ef vitað er hvor mótorinn eigi að fara fleiri skref, er hægt að láta þann mótor snúast á hámarkshraða, en láta hinn mótorinn sem snýst færri skref, snúast hlutfallslega hægar. Eftir að sjálfvirka interrupt lykkjan hefur verið gangsett, mun mótorinn sem snýst lengra því vera púlsaður í hverri umferð, en í hverri umferð er einnig athugað hvort að kominn sé tími til að púlsa hægari mótorinn. Það er gert með inerpóleringu (línulegri brúun), sem er innbyggt fall í arduino tungumálinu (map). Það er notað þannig:

 stuttskref=map(stada,1,skref,1,stutt);
// eða almennt: y=map(x,x0,x1,y0,y1);

Þá lítur interrupt callback fallið „timerfcn“ þannig út:

void timerfcn() {
  // mappa stodugildi hradari motorsins yfir a haegari motor
  // til ad vita hvort kominn se timi til ad taka skref a motornum
  // sem snyst haegar
  stuttskref=map(stada,1,skref,1,stutt);
  long t=stuttskref-fyrra_stuttskref;      // mismunur fra tvhi sidast (ath. mun aldrei verda >1)
  if (t>=1) {
     fyrra_stuttskref=stuttskref;
     skref_motor(!fast_motor);
  }
  skref_motor(fast_motor);
  
  stada++;
  
  if (stada==skref) {            // ef hreyfingu er lokid...
    fyrra_stuttskref=0;          // 
    idle=1;                      // nu er plotterinn laus
    MsTimer2::stop();            // stodva interrupt skeidklukku
  }
}

Eini tilgangur fallsins skref_motor er að púlsa viðkomandi mótor.

void skref_motor(char m) {
  int i;
  if (m==0) {
    digitalWrite(STEPV, LOW);    // Thessi breyting fra LOW i HIGH myndar pulsinn sem
    digitalWrite(STEPV, HIGH);   // segir motorstyringingunni ad taka skref
    } else {
    digitalWrite(STEPH, LOW);    // Thessi breyting fra LOW i HIGH myndar pulsinn sem
    digitalWrite(STEPH, HIGH);   // segir motorstyringingunni ad taka skref
  } 
}

Þá er umfjöllun um kóðann lokið, svo höfum þetta nóg í bili.

Hér er hægt að sækja í arduino kóðann (*.pde og *.pdf).
Hér er hægt að sækja MATLAB kóðann (*.m)

Advertisements
Categories: Uncategorized
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: