ΑΝΩΤΑΤΟ ΕΚΠΑΙΔΕΥΤΙΚΟ ΙΔΡΥΜΑ ΠΕΙΡΑΙΑ ΤΕΧΝΟΛΟΓΙΚΟΥ ΤΟΜΕΑ ΤΜΗΜΑ ΜΗΧΑΝΙΚΩΝ ΑΥΤΟΜΑΤΙΣΜΟΥ Τ.Ε. ΤΟΜΕΑΣ ΙΙΙ ΣΥΣΤΗΜΑΤΩΝ ΑΥΤΟΜΑΤΟΥ ΕΛΕΓΧΟΥ Π. Ράλλη & Θηβών 250, 12244 Αιγάλεω CodingStyle1710.doc Οδηγός Σύνταξης Κώδικα C/C++ για μικροελεγκτές 0. Γενικά Οι οδηγίες κατωτέρω αφορούν στη σύνταξη και τεκμηρίωση προγραμματισμού (κώδικα) ο οποίος εντάσσεται σε εφαρμογές μικρο-ελεγκτών (με). Τα σύγχρονα συστήματα με χρησιμοποιούν γλώσσα C, σε κοντινή αντιστοιχία με τα διαδεδομένα πρότυπα όπως ANSI-C, C99 ή C11. Επιπλέον, η βασική γλώσσα προγραμματισμού συνοδεύεται από πολυάριθμες κλήσεις βιβλιοθήκης για την αξιοποίηση των εξειδικευμένων κυκλωμάτων και δυνατοτήτων του με. Ακόμη, συχνά είναι και εμπλουτισμένη με στοιχεία αντικειμενοστραφούς προγραμματισμού, δηλαδή γλώσσας C++ (π.χ. σύστημα Arduino). Τυπικά παραδείγματα είναι τα περιβάλλοντα ανάπτυξης εφαρμογών για μικροελεγκτές τύπου AVR, ή PIC, ή ST και πολλά ανάλογα συστήματα. Το σύνολο της τεκμηρίωσης ενός προγράμματος (σχόλια) γράφεται με απλούς λατινικούς χαρακτήρες (ASCII) και διατυπώνεται στην αγγλική γλώσσα (όχι greeklish) όπως, εξ άλλου και το σύνολο του κώδικα. Όλο το κείμενο του κώδικα (εντολές, δηλώσεις και σχόλια) περιορίζεται σε εύρος 80 χαρακτήρων (στηλών). 1. Επικεφαλίδα προγράμματος Στην αρχή του προγράμματος τοποθετούνται σχόλια με τυποποιημένη δομή και μορφή, τα οποία πληροφορούν για τη λειτουργία (σκοπό) του κώδικα, τη σύνδεση του μικροελεγκτή με εξωτερικά στοιχεία, τη μέθοδο επεξεργασίας και τη χρησιμοποιούμενη υποστήριξη από λογισμικό (όπως βιβλιοθήκες ή πρόσθετος κώδικας) και στοιχεία αναφοράς όπως η θεματική ενότητα, ο συντάκτης, η έκδοση (version), η ημερομηνία. /********** LED flasher with adjustable duty cycle * The mc (Arduino) turns on and off an external LED, with * variable duty cycle that is set by a potentiometer. * The anode of the LED connects to a digital pin. The cathode * of the LED connects to GND through a resistor (typical values * 100-470). The wiper of the potentiometer connects to an analog * pin. The high and low terminals of the potentionmeter connect * to 5V and GND (physical pins #21 and #23 on the ProMini) resp. * Uses Arduino standard library calls analogread(), millis(). * v2.1, G. Chamilothoris, Sept. 2017. **********/
Ως διευκόλυνση στον οπτικό εντοπισμό κατά την ανάγνωση, στην αρχή της πρώτης γραμμής, το σύμβολο έναρξης σχολίου /* ακολουθείται από έναν αριθμό αστερίσκων (τυπικά 5-10). Οι ενδιάμεσες γραμμές αρχίζουν με αστερίσκο * ή με το σύμβολο της διαίρεσης / (απλὀ ή διπλό). Στην τελευταία γραμμή τοποθετείται μια «συμμετρική» σειρά συμβόλων, δηλαδή ο ίδιος αριθμός αστερίσκων όπως στην πρώτη γραμμή, και το σύμβολο λήξης σχολίου */. Το κείμενο περιλαμβάνει τουλάχιστον τις ακόλουθες ενότητες: Purpose περιγράφει συνοπτικά το σκοπό του προγράμματος, Hardware περιγράφει τη σύνδεση του με με εξωτερικά εξαρτήματα (κυκλώματα διαστρωμάτωσης, όργανα κλπ.), Software περιγράφει τη χρήση ειδικών μεθόδων υπολογισμού και κλήσεων βιβλιοθήκης. Επίσης, εφόσον χρειάζεται, προστίθενται ενότητες διαμορφωμένες με ανάλογο τρόπο, π.χ. Limitations για τυχόν περιορισμούς εφαρμογής του προγράμματος, ή Safety για να επισημάνει τυχόν αναγκαία μέτρα ασφάλειας κατά τη χρήση του κώδικα. Τελευταία τοποθετείται η υποχρεωτική ενότητα Reference η οποία αναφέρει το συντάκτη, την έκδοση και, ενδεχομένως, πρόσθετα στοιχεία αρχειοθέτησης του κώδικα, όπως το όνομα του αρχείου, το περιβάλλον στο οποίο συντάχθηκε κλπ. Οι ενότητες διαχωρίζονται με επικεφαλίδες και εσοχές, όπως στο παράδειγμα. Η ίδια συνολική τυποποίηση εφαρμόζεται και στην αρχή κώδικα βιβλιοθήκης (π.χ. στα τμήματα ενός αρχείου.h το οποίο εντάσσεται με την οδηγία προεπεξεργασίας #include). 2. Σχόλια κώδικα Σχόλια τυποποιημένης μορφής τοποθετούνται, ανάλογα με τη λογική διάρθρωση του κώδικα, στην αρχή μιας ομάδας εντολών ή δηλώσεων οι οποίες αφορούν παρόμοια εργασία ή σκοπό (π.χ. πριν από έναν υπολογισμό που εκτελείται από μια δομή for ή while, ή πριν τον ορισμό ακίδων για είσοδο/έξοδο). Τα σχόλια αυτού του τύπου περικλείονται από τα σύμβολα έναρξης σχολίου /* και λήξης σχολίου */. Εάν τα σχόλια περιλαμβάνουν πρόσθετες γραμμές, αυτές αρχίζουν με αστερίσκο * ή με το σύμβολο της διαίρεσης / (απλὀ ή διπλό). Η αριστερή πλευρά του κειμένου των σχολίων ακολουθεί την ανάλογη εσοχή (indent) του κώδικα τον οποίο περιγράφει, δηλαδή ευθυγραμμίζεται κατακόρυφα με την αριστερή πλευρά της πρώτης εντολής που ακολουθεί. Ακόμη, όπου απαιτείται, για την κατανόηση ενός σημείου του προγράμματος, προστίθεται σχόλιο στη συνέχεια της αντίστοιχης γραμμής κώδικα. Εάν το εύρος δεν επαρκεί, το σχόλιο συνεχίζεται με τον ίδιο τρόπο στην αμέσως επόμενη γραμμή, σε κατακόρυφη ευθυγράμμιση κατά το δυνατόν κοντινή με το σχόλιο στην προηγούμενη γραμμή. Το σχόλιο μπορεί να ορίζεται με έναν από δύο τρόπους: με τα σύμβολα έναρξης και λήξης, όπως παραπάνω, ή με διπλό σύμβολο διαίρεσης και αλλαγή γραμμής. Σε κάθε περίπτωση, όμως, η σύμβαση που θα επιλεγεί εφαρμόζεται ενιαία στο σύνολο του κώδικα. class MovAvg { /* an object that is a member of this class includes the variables * defined here below */ int *x; /* points to the variable containing the samples */ /* whose recursive average is calculated */ int avg; /* stores the current average value */ -2-
3. Ονόματα Γενικά, τα ονόματα των οντοτήτων σχετίζονται με τη χρήση του αντίστοιχου αντικειμένου (π.χ. μεταβλητής, τιμής, συνάρτησης κλπ.) στο πρόγραμμα και διατυπώνονται στην αγγλική γλώσσα. Τα ονόματα τα οποία αποδίδονται στις μεταβλητές γράφονται με πεζούς χαρακτήρες. Στην περίπτωση σύνθετων ονομάτων, κεφαλαίοι χαρακτήρες χρησιμοποιούνται για να διαχωρίσουν τις λέξεις που αποτελούν το όνομα («τυπογραφία καμήλας» - camelcase). Η ίδια τυποποίηση εφαρμόζεται και για τα ονόματα των συναρτήσεων. int photoresistorpin // pin to which the photoresistor is attached float xstate[nsteps] // state x(k) for k=0,1,...,nsteps int turnleft(void) {... // sends command for a left turn void activatecoil(char state) {... // turns the coil on or off Τα ονόματα τα οποία αποδίδονται σε τύπους αντικειμένων γράφονται με κεφαλαίο αρχικό και πεζούς χαρακτήρες. Στην περίπτωση σύνθετων ονομάτων, κεφαλαίοι χαρακτήρες χρησιμοποιούνται για να διαχωρίσουν τις λέξεις που αποτελούν το όνομα. Σε αυτήν την κατηγορία ανήκουν οι δομές (struct), οι απαριθμήσεις (enum) και οι κλάσσεις (class), είτε ορίζονται άμεσα ως περιεχόμενα είτε δηλώνονται ως νέοι τύποι με typedef. typedef struct MechanismLink {... // contains DH parameters for a link class Flasher {... // controls the flashing of a LED enum MotorStates {... // different modes of operation of the motor Τα ονόματα τα οποία αποδίδονται σε οριζόμενες σταθερές γράφονται με κεφαλαίους χαρακτήρες. Στην περίπτωση σύνθετων ονομάτων, χρησιμοποιείται χαρακτήρας υπογράμμισης _ (underscore) για να διαχωρίσει τις λέξεις που αποτελούν το όνομα. const int POT1_PIN =A2; #define OPT_STEPS 200 // first potentiometer attaches to this pin // number of steps for the optimization Τα ονόματα των μελών μιας απαρίθμησης, ακολουθούν τη σύμβαση για τα ονόματα των μεταβλητών (αρχικό πεζό, γραφή με πεζούς χαρακτήρες, διαχωρισμός των λέξεων με κεφαλαία). Ως παράδειγμα, το σύστημα Arduino εφαρμόζει τη σύμβαση για τις σταθερές συστήματος (π.χ. HIGH, INPUT_PULLUP, LED_BUILTIN), αλλά ορίζει σταθερές true και false με πεζούς χαρακτήρες, ως μέρη τού τύπου απαρίθμησης boolean. typedef enum PumpOperation = {running, idle, fault}; enum MoveCommand = { forward, back, left, right, stop }; if (x=true) {... // Arduino system constant 4. Εσοχές (indent) Οι δομές διακρίνονται με χρήση «εσοχών», δηλαδή με την κατακόρυφη ευθυγράμμιση της αριστερής πλευράς κάθε γραμμής κώδικα η οποία ανήκει στην ίδια λογική δομή προγραμματισμού. Κάθε εσοχή έχει πλάτος λίγων χαρακτήρων τυπικά δύο ή τρεις όπως π.χ. στο περιβάλλον καταχώρησης κώδικα (editor) Arduino, DevC++, CodeBlocks κλπ. -3-
Ειδικότερα, η εσοχή χρησιμοποιείται για να ξεχωρίσει οπτικά ο κώδικας που αποτελεί το «περιεχόμενο» μιας συνάρτησης (function). Όμοια σύμβαση ακολουθείται και στις δομές ελέγχου της εκτέλεσης if, for, while και switch. Επίσης και στις δομές ορισμού σύνθετων μεταβλητών ή αντικειμένων, όπως πινάκων (array), struct, class κλπ. /* function to add two numbers */ int addtwointegers (int y, int x){ // definition starts here int z; // define a temporary variable /* loop for doing 10 times nothing */ for (i=0; i<10; i++) { // a for-loop starts here z=z; // do-nothing code } // for-loop code ends here z= x+y; // add the arguments return z; // and return their sum } // function code ends here Όπως δείχνει το παράδειγμα, οι αγκύλη έναρξης { μιας δομής τοποθετείται στην ίδια γραμμή με τον ορισμό της (π.χ. int foo(void) { ) όμως οι αγκύλη τέλους } τοποθετείται μόνη της σε ξεχωριστή γραμμή, και σε κατακόρυφη ευθυγράμμιση με την εσοχή του ορισμού της δομής (δηλαδή την εσοχή που βρίσκεται αμέσως πιο «έξω» από το περιεχόμενο της δομής). 5. Επικεφαλίδα συνάρτησης (function) Στην αρχή μιας συνάρτησης (function) τοποθετούνται σχόλια με τυποποιημένη δομή και μορφή, ανάλογη με την επικεφαλίδα του προγράμματος (βλ. σχετική ενότητα ανωτέρω). /******** function movingaverage * Calculates the weighted, single-step recursive average of * successive samples. * Arguments * x Value of the incoming, most recent sample (float, by-value). * alpha Weighting factor (const float, by-value). Can vary between * 0.0 (past samples only) and 1.0 (recent sample only). * Results * - Returns the weighted sum of the recent sample and the previous * average (float). Initializes to the value of the first sample. * - Turns on the on-board LED when the average over 100 or more * successive samples does not change. * None. * Uses a static variable to store the current result between calls. * V1.0, GCham, Sept.2017 ********/ float movingaverage(float x, const float alpha) {... Στην επικεφαλίδα μιας συνάρτησης, το κείμενο περιλαμβάνει επιπρόσθετα τις ακόλουθες ενότητες: Arguments περιγράφει, για κάθε παράμετρο κλήσης (όρισμα) της συνάρτησης, τον τύπο δεδομένων στον οποίο ανήκει, την επίδραση της παραμέτρου στο αποτέλεσμα της -4-
κλήσης, το αποδεκτό πεδίο τιμών της κλπ., Results περιγράφει την επιστρεφόμενη τιμή από την κλήση, ως τύπο δεδομένων, περιεχόμενο, πεδίο τιμών κλπ. και, επίσης, τυχόν έμμεσα αποτελέσματα όπως π.χ. η αλλαγής της κατάστασης των περιφερειακών κυκλωμάτων του με ή η αλλαγή της τιμής μιας μεταβλητής μέσω δεικτοδότησης (pointer). 6. Επικεφαλίδα κλάσσης (class) Στην αρχή μιας κλάσης (class) τοποθετούνται σχόλια με τυποποιημένη δομή και μορφή, ανάλογη με την επικεφαλίδα του προγράμματος (βλ. σχετική ενότητα ανωτέρω). /***** class movavg * calculates the weighted, single-step recursive (moving) average * of a series of samples stored in a variable. * Specifiers * - a pointer to the variable containing the samples whose average * value is calculated (*int). * - weighting factor (const float, by-value). Can vary between * 0.0 (past samples only) and 1.0 (recent sample only). * Methods * - updatenow() updates the current average value. returns the weighted * sum of the most recent value and the previous average. Initializes * to the value of the first sample. * - stepback() removes the last sample entered and restores the average * to its previous value. * none. * uses the Arduino Class mechanism. * v1.0, GCham, Aug.2017 ********/ class MovAvg {... Στην επικεφαλίδα μιας κλάσης, το κείμενο περιλαμβάνει επιπρόσθετα τις ακόλουθες ενότητες: Specifiers περιγράφει κάθε παράμετρο που απαιτείται για την εξατομίκευση ενός αντικειμένου μέλους της κλάσης - τυπικά όπως αυτή χρησιμοποιείται από τη συνάρτηση κατασκευής (constructor) της κλάσης, Methods περιγράφει κάθε συνάρτηση που εφαρμόζεται στα αντικείμενα της κλάσης, συμπεριλαμβανόμενων και τυχόν συναρτήσεων που μεταφέρονται στην κλάση από άλλες κλάσεις μέσω μηχανισμού κληρονόμησης (inheritance). -5-