"Ανακύκλωση" κώδικα

Εφαρμογές με μικροεπεξεργαστές, ανάπτυξη προγραμμάτων, ιδέες και τεχνικά σημειώματα.
Post Reply
User avatar
manolena
Posts: 43
Joined: 02 Feb 2014, 16:45
Ονομα: Μάνος
Contact:

"Ανακύκλωση" κώδικα

Post by manolena »

Καλημέρα σας.

Η σημερινή εποχή έχει διαμορφώσει καινούριες ιδέες και συμπεριφορές σε πολλές πτυχές της ζωής μας, οι οποίες τελικά, έπρεπε να διέλθουν
μέσα από μια μακροχρόνια εξέλιξη ώστε να μπορέσουμε να κατανοήσουμε την πολυσχιδή έννοια και εφαρμογή τους στην καθημερινή μας ζωή.
Μια τέτοια, είναι η έννοια της ανακύκλωσης. Σε πολλούς τομείς της ζωής μας, έχει πια αποδειχθεί οτι πρέπει να υφίσταται ώστε συγκεντρωτικά,
να έχει οφέλη που διαφαίνονται βραχυπρόθεσμα αλλά και μακροπρόθεσμα.

Για κάποιους που ασχολούνται με την ανάπτυξη λογισμικού είτε απο επαγγελματικής είτε απο ερασιτεχνικής πλευράς, η ανακύκλωση έχει
άριστη εφαρμογή, προσθέτοντας στο έργο τους πλεονεκτήματα εμπορικότητας, μείωσης χρόνου ανάπτυξης και σιγουριάς στη λειτουργία
του τελικού αποτελέσματος.

Στόχος λοιπόν αυτού του θέματος είναι η παράθεση μερικών γραμμών κώδικα που μπορούν να χρησιμοποιηθούν ξανά και ξανά, βασισμένων σε μια
ανώτερη γλώσσα προγραμματισμού όπως η C που μπορούν να χρησιμοποιηθούν απο πολλούς "compilers" μοντέρνων μικροελεγκτών. Για
να είμαι ειλικρινής, επειδή ασχολούμαι τα τελευταία χρόνια με τέτοιους της Atmel, αναφέρομαι ειδικότερα σε αυτούς και ακόμα
πιο ειδικά στις πλατφόρμες και το περιβάλλον του Arduino. Αυτό δεν σημαίνει όμως οτι ξεχνάω τις μεγάλες μου αγάπες της Microchip που
πάνω τους έμαθα.

1. Ήχος με το πάτημα ενός κουμπιού
Πολλές φορές, χρειαζόμαστε για ένα keyboard user interface μια ηχητική επιβεβαίωση όταν πατάμε το πλήκτρο εισαγωγής κάποιας εντολής.
Μπορεί λοιπόν άνετα, να εισαχθεί ένα τμήμα κώδικα που έχει παραμετροποιηθεί κατά συχνότητα και διάρκεια, ώστε όταν η συνάρτησή του
καλείται οπουδήποτε, να προκαλεί την ηχητική επιβεβαίωση:

Code: Select all

#define BUZZER_PIN xx //όπου χχ ο αριθμός pin (digital)

pinMode(BUZZER_PIN, OUTPUT); //ορισμός του pin σαν ψηφιακή έξοδο.
digitalWrite(BUZZER_PIN, LOW); // καθήλωση της εξόδου σε λογικό 0.

void beepBuzzer(unsigned long hz, unsigned long ms)
{  
  unsigned long us = (750000 / hz);  
  unsigned long rep = (ms * 500L) / us; 
  
  for (int i = 0; i < rep; i++) 
  {  
    digitalWrite(BUZZER_PIN, HIGH);  
    delayMicroseconds(us);  
    digitalWrite(BUZZER_PIN, LOW);  
    delayMicroseconds(us);  
  }  
}
Όπως παρατηρεί κάποιος, μπορεί να καλέσει τη συνάρτηση ορίζοντας τη συχνότητα ταλάντωσης σε Hz του pin εξόδου
και τη χρονική διάρκεια της ταλάντωσης αυτής σε ms:

beepBuzzer(8000,50); // συχνότητα στα 8KHz και διάρκεια "μπιπ" 50ms.

Σημείωση (αρκετά ενδιαφέρουσα): όλοι οι buzzer του τύπου

Image

...έχουν εκ κατασκευής μια ιδιοσυχνότητα ηχείου στης οποίας τη συχνότητα (σε Hz), το παραγόμενο ηχητικό αποτέλεσμα
είναι το πιο δυνατό. Πρέπει λοιπόν στο όρισμα unsigned long Hz της παραπάνω συνάρτησης, να μπεί (με δοκιμή και σφάλμα)
η συχνότητα αυτή. Τότε λοιπόν θα έχει κάποιος ένα πολύ όμορφο "μπιπ" κάθε φορά που πιέζει ένα κουμπί του πληκτρολογίου του
ή άλλου κουμπιού.

2. Μετατροπή δεκαδικού σε BCD (Binary Coded Decimal)

Σε πολλές συσκευές, όπως για παράδειγμα τα ρολόγια RTC, είναι απαραίτητο τα αποτελέσματα που επεξεργαζόμαστε στον κώδικα
απο πράξεις για τα λεπτά, τα δευτερόλεπτα ή ακόμα τους μήνες, τις ημερομηνίες και γενικά όλους τους χρονικούς καταχωρητές
να το αποθηκεύσουμε σε αυτούς στην απαιτούμενη μορφή που είναι σε κώδικα BCD. Ο BCD κώδικας είναι η έκφραση δεκαδικών
ψηφίων τέτοια ώστε κάθε δεκαδικό ψηφίο να αντιπροσωπεύεται απο έναν προκαθορισμένο αριθμό δυαδικών ψηφίων, συνήθως 4 ή 8.

Code: Select all


Decimal
Digit	BCD

0	0 0 0 0
1	0 0 0 1
2	0 0 1 0
3	0 0 1 1
4	0 1 0 0
5	0 1 0 1
6	0 1 1 0
7	0 1 1 1
8	1 0 0 0
9	1 0 0 1

Για να αυτοματοποιήσουμε αυτή την μετατροπή, μπορούμε να καλέσουμε το όρισμα της παρακάτω συνάρτησης:

Code: Select all

byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}
...η οποία μας επιστρέφει τον δεκαδικό αριθμό που της ορίζουμε σε μορφή byte, σε BCD. Για παράδειγμα, θέλουμε να
εγγράψουμε στον καταχωρητή ωρών ενός M41T80 ρολογιού πραγματικού χρόνου της STM την τιμή 10:

Code: Select all

Wire.write(decToBcd(hours));
Υποτίθεται οτι έχουμε αρχικοποιήσει τον RTC και χρησιμοποιούμε τη βιβλιοθήκη Wire. O καταχωρητής hours
έχει την δεκαδική τιμή που εμείς έχουμε ορίσει.

3. Μετατροπή BCD σε δεκαδικό
Αντίθετα τώρα, όταν πρόκειται να "διαβάσουμε" έναν RTC του παραπάνω τύπου, θα πρέπει -για να κάνουμε τις
πράξεις μας πιο ανθρώπινα- να μετατρέψουμε το περιεχόμενο κάποιου απο τους καταχωρητές του σε δεκαδικό αριθμό.
Εδώ μπορούμε να χρησιμοποιήσουμε την εξής συνάρτηση:

Code: Select all

byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}
Το όρισμα εδώ είναι ένας BCD αριθμός και η επιστροφή είναι σε δεκαδικό. Για παράδειγμα:

Code: Select all

minutes = bcdToDec(Wire.read()); // ο καταχωρητής minutes "φορτώνεται" με την τιμή που επιστρέφει η συνάρτηση  bcdToDec 
4. Παρουσίαση μεταβλητής float με προκαθορισμένο αριθμό δεκαδικών μετά την υποδιαστολή

Πολλές φορές, είναι αναγκαίο να "εκτυπώνουμε" διάφορες μεταβλητές σε οθόνες LCD, GLCD, σειριακά τερματικά, ασύρματες συσκευές.
Ειδικότερα για τις μεταβλητές δεκαδικού με υποδιαστολή (floats), πολλοί compilers εμφανίζουν ως και 8 δεκαδικά ψηφία, πράγμα που είναι
μερικές φορές κουραστικό. Η παρακάτω συνάρτηση μπορεί να "εκτυπώσει π.χ. σε μια οθόνη LCD μια float με προκαθορισμένο απο
τον χρήστη αριθμό ψηφίων μετά την υποδιαστολή, ορίζοντας έναν αριθμό σαν όρισμα:

Code: Select all

void printFloat(float value, int places) //value η τιμή της float, places ο αριθμός δεκαδικών μετά την υποδιαστολή.
{  
  int digit;
  float tens = 0.1;
  int tenscount = 0;
  int i;
  float tempfloat = value;
  float d = 0.5;
  if (value < 0) d *= -1.0;
  for (i = 0; i < places; i++) d/= 10.0;    
  tempfloat +=  d;
  if (value < 0) tempfloat *= -1.0;
  while ((tens * 10.0) <= tempfloat) 
  {
    tens *= 10.0;
    tenscount += 1;
  }
  if (value < 0) lcd.print('-');
  if (tenscount == 0) lcd.print(0, DEC);
  for (i=0; i< tenscount; i++) 
  {
    digit = (int) (tempfloat/tens);
    lcd.print(digit, DEC);
    tempfloat = tempfloat - ((float)digit * tens);
    tens /= 10.0;
  }
  if (places <= 0) return;
  lcd.print('.');  
  for (i = 0; i < places; i++) 
  {
    tempfloat *= 10.0;
    digit = (int) tempfloat;
    lcd.print(digit,DEC);  
    tempfloat = tempfloat - (float) digit;
  }
}
Αν το όρισμα places = 0, τότε η float παρουσιάζεται σαν ένας απλός ακέραιος αριθμός.
Παράδειγμα χρήσης:

Code: Select all

printFloat(0.252443, 2)
Στην προκειμένη περίπτωση, η μεταβλητή float (εδώ δεν είναι μεταβλητή, αλλά σταθερά) θα φαίνεται σε μια οθόνη ως 0.25
Εννοείται πως για την εντολή lcd.print(x,y), ισχύει η σειρά και η στήλη που θέλουμε να εμφανιστεί το αποτέλεσμα, η οποία
έχει οριστεί ενωρίτερα στον κώδικα. Το πιο πάνω παράδειγμα θα γίνει ως εκ τούτου:

Code: Select all

lcd.setCursor(0,0);
printFloat(0.252443, 2)
...και στη θέση 0,0 της οθόνης (πρώτη στήλη, πρώτη γραμμή) θα εμφανιστεί ο αριθμός 0.25

5. "Σπασιμο" ενός 4ψήφιου αριθμού (integer) σε χιλιάδες, εκατοντάδες, δεκάδες και μονάδες

Η μεταβλητή του αριθμού που θέλουμε να "σπάσουμε": input_number
Οι μεταβλητές thousands, hundrends, tenths, ones θα περιέχουν τον "απο-δομημένο" τετραψήφιο.

Code: Select all

int input_number, thousands, hundrends, tenths, ones;

ones = (input_number%10);
tens = ((input_number/10)%10);
hundreds = ((input_number/100)%10);
thousands = (input_number/1000);
Έτσι, μπορούμε να φτιάξουμε μια συνάρτηση και καλώντας την, να μας επιστρέφει τον αριθμό που της εισάγουμε
εις τα "εξ΄ων συνετέθη..."

Code: Select all

void disassembly_integer(int input_number)
{
  ones = (input_number%10);
  tens = ((input_number/10)%10);
  hundreds = ((input_number/100)%10);
  thousands = (input_number/1000);
}
Η παραπάνω, είναι πολύ χρήσιμη όταν φτιάχνουμε σε μενού περιήγησης μιας LCD οθόνης την ρύθμιση της ώρας για παράδειγμα
ή αν θέλουμε να ρυθμίσουμε με ξεχωριστό για κάθε ψηφίο τρόπο π.χ. τον αριθμό στον οποίο θέλουμε μια ειδοποίηση, ελέγχοντας
με μια OR για κάθε ψηφίο το μέγεθος.
Last edited by manolena on 15 Oct 2015, 00:23, edited 1 time in total.
My way of learning is trial and error.
User avatar
GeorgeVita
Διαχειριστής
Posts: 629
Joined: 04 Sep 2013, 21:51
Ονομα: Γιώργος
Contact:

Re: "Ανακύκλωση" κώδικα

Post by GeorgeVita »

Μάνο σ' ευχαριστώ που μοιράζεσαι την εμπειρία σου! Ωραίος και εκπαιδευτικός ο τρόπος παρουσίασης!
Προφανώς με τον όρο "ανακύκλωση κώδικα" περιγράφεις το "modular programming" που είναι ένα βήμα πιο πάνω από τις βιβλιοθήκες και εξελίσσοντάς το μπορείς να δημιουργήσεις εξειδικευμένες ή απλοποιημένες (για τους αρχάριους) γλώσσες υψηλού επιπέδου.

Εχεις ασχοληθεί και με την δημιουργία του "προγράμματος κορμού" για να είναι προβλέψιμος ο εσωτερικός χρονισμός;
Εννοώ κάτι σαν state machine ή real time operating system.
User avatar
manolena
Posts: 43
Joined: 02 Feb 2014, 16:45
Ονομα: Μάνος
Contact:

Re: "Ανακύκλωση" κώδικα

Post by manolena »

Όχι Γιώργο, δεν έχω ακόμα ασχοληθεί με RTOS ή καταμερισμό εργασιών σε states. To real time OS εργάζεται πολύ ωραία με μεγαλύτερες ταχύτητες χρονισμού,
που σημαίνει οτι είναι εξαιρετική προσέγγιση για όσους ασχολούνται με πυρήνες ARM.

Για χαμηλότερες ταχύτητες και 8bit επεξεργαστές στο προκείμενο περιβάλλον του Arduino, υπάρχει όμως η παρακάτω βιβλιοθήκη:

http://playground.arduino.cc/Code/TimedAction

...με τη βοήθεια της οποίας μπορεί κάποιος να ορίσει συγκεκριμένα χρονικά intervals στα οποία να εκτελεί κάποιες εργασίες, χωρίς να εμπλέκεται κάποιου είδους
διακοπή απο timer. Με τη δημιουργία ξεχωριστού class για κάθε εργασία, μπορείς να τα "τρέχεις" όλα στον κύριο βρόγχο του προγράμματος.
My way of learning is trial and error.
User avatar
manolena
Posts: 43
Joined: 02 Feb 2014, 16:45
Ονομα: Μάνος
Contact:

Re: "Ανακύκλωση" κώδικα

Post by manolena »

6. Πώς "γράφουμε" ελληνικά (κεφαλαία) σε μια οθόνη χαρακτήρων με βάση τον controller HD44780

Σε οθόνες βασισμένες σε πυρήνα HD44780 της Hitachi, τα εμπορικά part numbers δεν υποστηρίζουν χαρακτήρες
ελληνικής αλφαβήτου, εκτός απο διεθνώς αναγνωρισμένους στα Μαθηματικά και την Φυσική, όπως το Σ και το Θ.
Αυτό σημαίνει πως συμβατικά, δεν είναι δυνατόν να συνταχθούν λέξεις της ελληνικής γλώσσας με απλές εντολές
προς την οθόνη του τύπου lcd.print() (για Arduino) ή lcd_putch(char c) (π.χ. PICC). Όμως, έχει προβλεφθεί απο
τον κατασκευαστή η δυνατότητα χρήσης 8 κενών θέσεων στη μνήμη χαρακτήρων της οθόνης, τις οποίες μπορούμε
να "γεμίσουμε" με τις φιγούρες ελληνικών χαρακτήρων σε 5x7 καρέ. Κατά σύμπτωση, οι ελληνικοί -μή όμοιοι με
λατινικούς- χαρακτήρες που είναι τα γράμματα Γ, Δ, Λ, Ξ, Π, Φ, Ψ, Ω είναι 8, οπότε μπορούμε να τους σχεδιάσουμε
και να τους καταχωρήσουμε στην μνήμη χαρακτήρων χρήστη (CGRAM).

Μπορούμε λοιπόν να φτιάξουμε πίνακες 5χ8 στοιχείων (τόσα είναι τα διατιθέμενα pixels για κάθε χαρακτήρα) και
να χρησιμοποιήσουμε συγκεκριμένες εντολές εγγραφής στη CGRAM αυτών των πινάκων:

i) ορίζουμε πρώτα το όνομα κάθε ενός απο τους 8 αυτούς πίνακες

Code: Select all

#define fi       0
#define psi      1
#define omega    2
#define gamma    3
#define delta    4
#define lamda    5
#define ksi      6
#define pi       7
ii) ακολούθως φτιάχνουμε τους πίνακες

Code: Select all

byte FI[8] = {
  B01110,
  B10101,
  B10101,
  B10101,
  B01110,
  B00100,
  B00100,
  B00000
};
byte PSI[8] = {
  B10101,
  B10101,
  B10101,
  B01110,
  B00100,
  B00100,
  B00100,
  B00000
};
byte OMEGA[8] = {
  B01110,
  B10001,
  B10001,
  B10001,
  B01110,
  B00000,
  B11111,
  B00000
};
byte GAMMA[8] = {
  B11111,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B00000
};
byte DELTA[8] = {
  B00100,
  B01010,
  B10001,
  B10001,
  B10001,
  B10001,
  B11111,
  B00000
};
byte LAMDA[8] = {
  B00100,
  B01010,
  B10001,
  B10001,
  B10001,
  B10001,
  B10001,
  B00000
};
byte KSI[8] = {
  B11111,
  B00000,
  B00000,
  B01110,
  B00000,
  B00000,
  B11111,
  B00000
};
byte PEE[8] = {
  B11111,
  B10001,
  B10001,
  B10001,
  B10001,
  B10001,
  B10001,
  B00000
};
iii) στο setup function και λίγο μετά απο το initialization της οθόνης, "γράφουμε" τους χαρακτήρες στη CGRAM

Code: Select all

void stup()
{
  lcd.begin(COLUMNS, ROWS); 
  ...
  
  lcd.createChar(fi, FI);
  lcd.createChar(psi, PSI);
  lcd.createChar(omega, OMEGA);
  lcd.createChar(gamma, GAMMA);
  lcd.createChar(delta, DELTA);
  lcd.createChar(lamda, LAMDA);
  lcd.createChar(ksi, KSI);
  lcd.createChar(pi, PEE);
  ...
  
Για να γράψουμε ελληνικά (πάντα κεφαλαία) πρέπει να χρησιμοποιήσουμε συνδυασμό εντολών
lcd.print() για λατινικούς ASCII χαρακτήρες και lcd.write(byte(xx)) για ελληνικούς (όπου (χχ) το αντίστοιχο
γράμμα της παραγράφου i)).
Παράδειγμα:

Code: Select all

  lcd.print(F("KA")); 
  lcd.write(lamda); 
  lcd.print(F("HMEPA A")); 
  lcd.write(pi); 
  lcd.print(F("O TON MAN")); 
  lcd.write(omega);
  lcd.write(lamda); 
  lcd.print(F("H!!!")); 
ΚΑΛΗΜΕΡΑ ΑΠΟ ΤΟΝ ΜΑΝΩΛΗ!!!

Για τα γράμματα που ήδη υπάρχουν στην μνήμη χαρακτήρων της οθόνης, χρησιμοποιούμε τη διεύθυνση
του χαρακτήρα που χρειαζόμαστε (π.χ. Σ, Θ) και την στέλνουμε με την εντολή lcd.write(xxx), όπου χχχ η
διεύθυνση του χαρακτήρα.
My way of learning is trial and error.
Post Reply

Return to “Μικροεπεξεργαστές”