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

Εφαρμογές με μικροεπεξεργαστές, ανάπτυξη προγραμμάτων, ιδέες και τεχνικά σημειώματα.
Απάντηση
Άβαταρ μέλους
manolena
Δημοσιεύσεις: 43
Εγγραφή: 02 Φεβ 2014, 16:45
Ονομα: Μάνος
Επικοινωνία:

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

Δημοσίευση από manolena »

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

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

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

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

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

Κώδικας: Επιλογή όλων

#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 του τύπου

Εικόνα

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

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

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

Κώδικας: Επιλογή όλων


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

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

Κώδικας: Επιλογή όλων

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

Κώδικας: Επιλογή όλων

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

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

Κώδικας: Επιλογή όλων

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

Κώδικας: Επιλογή όλων

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

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

Κώδικας: Επιλογή όλων

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 παρουσιάζεται σαν ένας απλός ακέραιος αριθμός.
Παράδειγμα χρήσης:

Κώδικας: Επιλογή όλων

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

Κώδικας: Επιλογή όλων

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

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

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

Κώδικας: Επιλογή όλων

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);
Έτσι, μπορούμε να φτιάξουμε μια συνάρτηση και καλώντας την, να μας επιστρέφει τον αριθμό που της εισάγουμε
εις τα "εξ΄ων συνετέθη..."

Κώδικας: Επιλογή όλων

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 για κάθε ψηφίο το μέγεθος.
Τελευταία επεξεργασία από το μέλος manolena την 15 Οκτ 2015, 00:23, έχει επεξεργασθεί 1 φορά συνολικά.
My way of learning is trial and error.
Άβαταρ μέλους
GeorgeVita
Διαχειριστής
Δημοσιεύσεις: 624
Εγγραφή: 04 Σεπ 2013, 21:51
Ονομα: Γιώργος
Επικοινωνία:

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

Δημοσίευση από GeorgeVita »

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

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

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

Δημοσίευση από 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.
Άβαταρ μέλους
manolena
Δημοσιεύσεις: 43
Εγγραφή: 02 Φεβ 2014, 16:45
Ονομα: Μάνος
Επικοινωνία:

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

Δημοσίευση από 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 αυτούς πίνακες

Κώδικας: Επιλογή όλων

#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) ακολούθως φτιάχνουμε τους πίνακες

Κώδικας: Επιλογή όλων

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

Κώδικας: Επιλογή όλων

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)).
Παράδειγμα:

Κώδικας: Επιλογή όλων

  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.
Απάντηση

Επιστροφή στο “Μικροεπεξεργαστές”