commit 0f28637aedffb7b62c103e8632915e0e2bfcb33d Author: Christos Choutouridis Date: Sun May 24 14:56:52 2020 +0300 Init commit - Import assignment 1 files and repo - Assignment 2 code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c22a5aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# General +doc/ +hex/ +micro2019/ +deliver/ + +# Eclipse-Atollic related +.project +.cproject +.settings/ +*.ld +Debug/ +Release/ + +# Keil related +Keil/ +*.gz +*.zip diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..37b878c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "assignment_1/report/config"] + path = assignment_1/report/config + url = https://git.hoo2.net/hoo2/LaTeX_confing.git +[submodule "assignment_2/report/config"] + path = assignment_2/report/config + url = https://git.hoo2.net/hoo2/LaTeX_confing.git +[submodule "assignment_2/Libraries/STM32CubeF4"] + path = assignment_2/Libraries/STM32CubeF4 + url = https://github.com/hoo2/STM32CubeF4.git diff --git a/assignment_1/main.c b/assignment_1/main.c new file mode 100644 index 0000000..2acf522 --- /dev/null +++ b/assignment_1/main.c @@ -0,0 +1,121 @@ +/*! + * @file + * main.c + * @brief + * A palindrome implementation and testing application + * for A.U.TH. Microprocessors and peripherals Lab. + * + * Created on: May 1, 2020 + * Author: Christos Choutouridis AEM: 8997 + * email : + */ +#include + +/*! + * The RAM address to use for palindrome result. + * @note: + * Select a RAM location above `__initial_sp`. See your startup_xx.s for details. + * Keil's RAM layout is: + * + * RAM: |<-Heap_Size-><-Stack_Size->|<- WASTED RAM SPACE ->| + * ^ ^ ^ + * | | | + * Start:0x20000000 __initial_sp End of RAM + * + */ +#define RESULT 0x20001000 + + +/*! + * String length calculation. + * @arg src{R0} Pointer to string + * @return {R0} String length, -1 if src is NULL + */ +__asm uint32_t strLength (const char* src) { + // R0: length, return value + // R1: src + // R2: v = *src + MOV R1, R0 // Get variable + CMP R1, #0 // if (src{R1} == NULL) + MOVEQ R0, #-1 // return -1; + BXEQ LR + MOV R0, #0 // length{R0} =0; +len_loop // do { + LDRB R2, [R1], #1 // v{R2} = *src++; + CMP R2, #0 // if(v) + ADDNE R0, R0, #1 // ++length{R0}; + BNE len_loop // } while (v); + BX LR // return length; +} + +/*! + * A string palindrome predicate. + * @note + * We use a simple O(n), no stack algorithm. + * @note + * This function also writes to RAM address @ref RESULT the return value (which is nasty). + * @arg src{R0} Pointer to string + * @return {R0} True if palindrome, false if not or src is NULL pointer + * @note + * We consider an empty string "" to be a palindrome, as we can see it the same way both + * from the front and the back. + */ +__asm uint32_t isPalindrome (const char* src) { + // R0: src[] + // R1: i + // R2: j + // R3: v1 = src[i] + // R4: v2 = src[j] + PUSH {R4, LR} + CMP R0, #0 // if (src{R0} == NULL) + BEQ pal_false // return false; + MOV R4, R0 // save R0 + BL strLength // j{R2} = strLength(src) - 1 + SUB R2, R0, #1 + MOV R0, R4 // src{R0} + MOV R1, #0 // i{R1} =0; + +pal_loop + CMP R1, R2 // while (i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + End of RAM + + + 0x20000000 + + data + bss Heap_Size Stack_Size + + + __initial_sp + Free + + diff --git a/assignment_1/report/images/Untitled Diagram.drawio b/assignment_1/report/images/Untitled Diagram.drawio new file mode 100644 index 0000000..8d62ffd --- /dev/null +++ b/assignment_1/report/images/Untitled Diagram.drawio @@ -0,0 +1 @@ +7Vtbc+I2FP41zLSdYceSLyGPIZfdtjTT5to8KlgxzgqLlQWB/vpKtnyTDHgTwCQhM5lYx5IsnXO+7xxd0rFPx/OvDE1Gf1Efkw60/HnHPutACEDPFX+kZJFKPFsJAhb6qlIhuA7/w0poKek09HFcqcgpJTycVIVDGkV4yCsyxBh9qVZ7oqT61QkKsCG4HiJiSu9Dn49SaQ8eFfJvOAxG2ZeBd5y+GaOssppJPEI+fSmJ7POOfcoo5enTeH6KiVReppf73xf3ZPDd+/rHP/EPdNv/8+byrpt2dvEzTfIpMBzxV3c9H0b88f7h7vLqidyh2zk9WQy6wFFz44tMYdgX+lNFyviIBjRC5LyQ9hmdRj6W3VqiVNQZUDoRQiCEz5jzhXIGNOVUiEZ8TNRbPA/5v7L5F1eVHkpvzuaq56SwyAoRZ4tSI1l8KL8rmiWlrF06PzkpzR/WKFPVi+mUDfGKep7yacQCvLI/mLuMwBqmYyxGKRoyTBAPZ9XRIeX0QV6vMKx4ULb9CTs7Vpt2BiUrFzZfY2dQsXJh9P2xcz2iem+0s2r6Nw3FuKGliNjNLKho2O1Z1S7SgalWhbecMIYWpWoTWSFe8R1Y/U7GfhcN62fhoXDWdASF6+Y6aeTNq2wxQ2Sq9BmzoRDIZ1m0Lm8HA8Ph2YiOH6dCD/2XUcjx9QQlBn8Rca/qtjnRS796ohG/QOOQyAl+w2SGeThE6oXyfIFtu49IGESiMBT+hpnshjP6PQ86Scfik2EUiJJXlG4SMHWd3IebuuwMM47nJX8ynS5761aNBI5V+aWIiCBzsFEpGnqak22Mj0CrfPQR4g7Ikql1hPRWPmpq55WjLEG1Az0ixtv3w5l4DHiiq1QkISVzPKRm7v2YyuSqX8AuF2VNZ0DhXqhMcIDblyOSIvcs61UMPO24+jEh3tAQoDmE5zVD0Kkp92ywnp504gkIimPldTWcUyazbRGMDY++uBWKcYBrUozlmhTjbo1i7APFvJFiYNOcp1WKgQbFhDkgrQyBj0xHegn8zwWAORvgKOCjXwSUf5UTqyeLD4ZfPUGAvbYTBPuA3ubordWgZYJ3eTLd1rrUMrDbx0EYmbYnJJzEeD24lnh+U70uR8iRlkKDGoRYNQjZXnxz20TIq5b0VqetJf3ysNVk62ZX8a12lLB3YMKN27le00dt2rnRSinJbKCHxpL2VGYiFxvNspR3sgGyffZ2rIbsre+ybQ7VreY3H4G9e00XJ60mONA7sPeO7Lyrja5Voyyx9wx0ku3oGTwQcWmjSNsmqtuJBrtcaMJW0+gqQK2VAN16mvRWoqw/IYLaCZGtm3JDJ1dOz639zqZOolboTMvahKVkoqZvPHW7zyYT7HTjaPN4djwtsTo2t33dXa6K7aNW4fyavKq9g+4VcbRBwLWdNgMuMCMuw3zKROA7EeIra9Ue8G9X59e3g5tylQ8GTFs/l687j4E1yHS2FmjNvb4HHBuKF3rgWhKUaPCUEipymbOIRhKyTyEhmshIe6RWRYZETtSLcej7ZNkeYpUDtmUWqC9EoZn/1O2zw61Zxdx2+HxWcTSwuDXbAzu1ir1HSen7XDVm9zv3PIplw1wfxcD6KAYMp3nvUQxo6aVbQ5e7jWK2GcUu6Sejy/xudxbEemZusVu6NIPYpzOKrWUWrtt2DHMMo9QcaJxHfsPTi707AvZaPwK263YfSiq2MsctLq0R/MTNK2z6cmgufi0xF6uhbd4OrWRc+wksLTnMY9K6uzHbQ5a59K1B1h1iIXoUyoNWV6YSOAhjwV7x6vuPJVF3+c/yW1VrexUpTZLaJDe2m7YBaZuweQuYtlh6NGq2sNMWs6V3vswmjmoCm+vjEwMH2g2BA7aGHOew2pWX+/TLurDt9a5j5gqfLoHTs2q4vQROFIt/W0yPQop//rTP/wc= \ No newline at end of file diff --git a/assignment_1/report/images/isPalindrome.png b/assignment_1/report/images/isPalindrome.png new file mode 100644 index 0000000..8c5864e Binary files /dev/null and b/assignment_1/report/images/isPalindrome.png differ diff --git a/assignment_1/report/report.pdf b/assignment_1/report/report.pdf new file mode 100644 index 0000000..b7aab26 Binary files /dev/null and b/assignment_1/report/report.pdf differ diff --git a/assignment_1/report/report.tex b/assignment_1/report/report.tex new file mode 100644 index 0000000..7fbd52a --- /dev/null +++ b/assignment_1/report/report.tex @@ -0,0 +1,119 @@ +% +% Report description +% +% authors: +% Χρήστος Χουτουρίδης ΑΕΜ 8997 +% cchoutou@ece.auth.gr + + +% Document configuration +\newcommand{\ClassName}{Μικροεπεξεργαστές και Περιφερειακά} +\newcommand{\DocTitle}{1η Εργασία} +\newcommand{\InstructorName}{Παπαευσταθίου Ιωάννης} +\newcommand{\InstructorMail}{ygp@ece.auth.gr} +\newcommand{\CurrentDate}{\today} + +\input{config/AuthReportConfig.tex} + +%\renewcommand{\AuthorName}{Χρήστος Χουτουρίδης} +%\renewcommand{\AuthorMail}{cchoutou@ece.auth.gr} +%\renewcommand{\AuthorAEM}{8997} + +\setFancyHeadLR{\ClassName}{\DocTitle} +%\setFancyHeadLERO{\ClassName}{\DocTitle} + +% Document +% ================= +\begin{document} + +\FirstPage + +%\tableofcontents +%\listoffigures +%\listoftables + +\section{Εισαγωγή} +Στην παρούσα εργασία το ζητούμενο είναι η υλοποίηση μιας ρουτίνας σε γλώσσα \eng{assemblly ARM,} που να ελέγχει αν ένα αλφαριθμητικό είναι παλίνδρομο, αλλά και η ενσωμάτωση αυτής σε ένα πρόγραμμα σε γλώσσα \eng{C.} +Για την εργασία χρησιμοποιήσαμε το εργαλείο \eng{Keil uVision} και μιας και δεν αντιμετωπίσαμε κάποιο ιδιαίτερο πρόβλημα, η αναφορά αυτή θα αναλωθεί κυρίως στην υλοποίηση και τον έλεγχο του προγράμματος. +Τον κώδικα της εργασίας αλλά και της παρούσας αναφοράς μπορείτε να βρείτε ακόμα στο προσωπικό \href{https://git.hoo2.net/hoo2/micro_assign_1}{αποθετήριο της εργασίας}. + +\section{Παραδοτέα} +Στο παραδοτέο \eng{.zip} αρχείο μπορείτε να βρείτε: +\begin{itemize} + \item Το αρχείο \textbf{\eng{main.c}} που περιέχει όλο τον ζητούμενο κώδικα της εργασίας. + \item Το αρχείο \textbf{\eng{report.pdf}} που είναι παρούσα αναφορά. + \item Τον κατάλογο \textbf{\eng{Keil}} που περιέχει το \eng{project} στο \eng{Keil} που χρησιμοποιήσαμε.\\ + Στο \eng{project} αυτό περιέχονται επιπλέων οι ρυθμίσεις καθώς και τα αρχεία από τη βιβλιοθήκη \eng{CMSIS} που χρειάστηκαν για την ορθή αρχικοποίηση και αλληλεπίδραση με τον επεξεργαστή. +\end{itemize} + +\section{Υλοποίηση} +Η υλοποίηση του ελέγχου αν ένα αλφαριθμητικό είναι παλίνδρομο έγινε σε δύο συναρτήσεις, την \eng{\textit{isPalindrome()}} και την \eng{\textit{strLength()},} που λειτουργεί ως βοηθητική συνάρτηση. +Οι συναρτήσεις αυτές είναι υλοποιημένες σε γλώσσα \eng{assemblly} αλλά η κλήση τους από το κυρίως πρόγραμμα δεν διαφέρει καθόλου από την κλήση μιας οποιασδήποτε άλλης συνάρτησης σε γλώσσα \eng{C.} +Για τον λόγο αυτό δεν θα ασχοληθούμε περαιτέρω με το κύριο πρόγραμμα (συνάρτηση \eng{main).} + +\subsection{Συνάρτηση \eng{\textit{isPalindrome()}}} +\WrapFigure{0.42}{r}{fig:isPalindrome}{isPalindrome.png}{Διάγραμμα ροής της συνάρτησης \eng{\textit{isPalindrome()}}} +Για τη συνάρτηση αυτή χρησιμοποιήσαμε ένα απλό \eng{O(n)} σειριακό αλγόριθμο, ο οποίος απαιτεί το μήκος του αλφαριθμητικού εισόδου προτού εισέλθει στον κύριο βρόχο επανάληψης. +Για να βρούμε το μήκος χρησιμοποιούμε μια ξεχωριστή συνάρτηση. +Αυτή την υλοποιήσαμε από την αρχή ώστε να μην χρειαστεί να χρησιμοποιηθεί η συνάρτηση \eng{\textit{strlen()}} της \eng{\textit{libc}.} + +\par Όπως φαίνεται και στο διάγραμμα του σχήματος \ref{fig:isPalindrome}, στον κύριο βρόχο της συνάρτησης χρησιμοποιούμε δύο απαριθμητές \eng{i, j} χρησιμοποιώντας τους καταχωρητές \eng {R1, R2} αντίστοιχα και ένα δείκτη στη διεύθυνση του αλφαριθμητικού χρησιμοποιώντας τον καταχωρητή \eng{R0.} +Ο ένας δείκτης εκκινεί από την αρχή του αλφαριθμητικού και ο άλλος από το τέλος. +Σε κάθε επανάληψη διαβάζουμε από το αλφαριθμητικό τα \eng{src[i]} και \eng{src[j]} και ελέγχουμε αν είναι ίσα. +Αν δεν είναι τότε το αλφαριθμητικό δεν είναι παλίνδρομο. +Αν είναι τότε μεταθέτουμε το \eng{i} μία θέση δεξιά και το \eng{j} μία θέση αριστερά και επανεκτελούμε τον βρόχο. +Ο βρόχος συνεχίζει όσο το \eng{i} είναι μικρότερο από το \eng{j.} +Αν το \eng{i} γίνει ίσο ή μεγαλύτερο από το \eng{j,} τότε ο βρόχος τερματίζει. +Σε αυτή την περίπτωση το αλφαριθμητικό είναι παλίνδρομο. + +\par Αν το αλφαριθμητικό έχει μηδενικό μήκος, τότε ο βρόχος δεν θα εκτελεστεί ούτε μία φορά και η συνάρτηση θα επιστρέψει ότι το αλφαριθμητικό είναι παλίνδρομο. +Έχουμε κάνει δηλαδή την θεώρηση ότι \textbf{\textit{τα μηδενικού μήκους αλφαριθμητικά είναι παλίνδρομα}}, καθώς είναι τα ίδια είτε τα κοιτάς από μπροστά είτε τα κοιτάς από πίσω. +Στην περίπτωση που η είσοδος της συνάρτησης είναι εκτός πεδίου ορισμού, δηλαδή ο \eng{NULL pointer,} τότε η συνάρτηση επιστρέφει ψευδές αποτέλεσμα. +Ο έλεγχος του δείκτη εισόδου γίνεται στην αρχή της συνάρτησης πριν καν γίνει η κλήση για το μήκος, προστατεύοντας έτσι κάποιο \eng{exception} από το \eng{dereference} του δείκτη στην εντολή \eng{LDR.} + +\subsection{Συνάρτηση \eng{\textit{strLength()}}} +Η υλοποίηση της \eng{\textit{strLength()}} είναι επίσης ένας σειριακός αλγόριθμος. +Εδώ χρησιμοποιούμε τον καταχωρητή \eng{R1} ώς δείκτη στο αλφαριθμητικό και με αυτόν κάνουμε ανάγνωση της μνήμης, αυξάνοντάς τον ταυτόχρονα μετά από κάθε ανάγνωση. +Η συνάρτηση επιστρέφει τον αριθμό των αναγνώσεων που χρειάστηκαν μέχρι να λάβουμε το νούμερο '0', το οποίο σηματοδοτεί το τέλος του αλφαριθμητικού. + +\par Ομοίως και εδώ κάνουμε έλεγχο της εισόδου ώστε αυτή να μην είναι ο \eng{NULL pointer.} +Φυσικά αυτό στη δική μας περίπτωση δεν είναι απαραίτητο καθώς υπάρχει ο αντίστοιχος έλεγχος στην \eng{\textit{isPalindrome()}} προτού την κλήση. +Παρόλα αυτά το θεωρούμε καλή πρακτική, καθώς αυτό καθιστά την \eng{\textit{strLength()}} πιο ολοκληρωμένη συνάρτηση και μια χρήση της στο μέλλον δεν θα προκαλέσει εκπλήξεις. + +\subsection{Εγγραφή του αποτελέσματος στη μνήμη} +\WrapFigure{0.23}{l}{fig:ram_layout}{RAM_layout.png}{Διάταξη της μνήμης} +Η συνάρτηση \eng{\textit{isPalindrome()}} αφού εκτελεστεί, επιστρέφει μια τιμή στον \eng{caller.} +Στα ζητούμενα όμως της εργασίας ήταν η συνάρτηση αυτή να γράφει το αποτέλεσμα και σε μία θέση μνήμης της επιλογής μας. +Αυτό υπό κανονικές συνθήκες είναι ανεπίτρεπτο. +Για τα πλαίσια όμως της εργασίας προσπαθήσαμε να ανταπεξέλθουμε με ένα σχετικά ασφαλή τρόπο. + +\par Όπως φαίνεται και στο σχήμα \ref{fig:ram_layout}, o συνδέτης \eng{(linker)} του εργαλείου \eng{keil} δεν τοποθετεί τον \eng{SP} στο τέλος της μνήμης. +Για την ακρίβεια, χρησιμοποιώντας δύο τιμές από το \eng{configuration} του \eng{startup} αρχείου για το μέγεθος του σωρού και της στοίβας, υπολογίζει τη τιμή \eng{\textit{\textbf{\_\_initial\_sp}}.} +Αυτή την τιμή χρησιμοποιεί έπειτα σαν πρώτο όρισμα στο \eng{vector table} και άρα αυτή την τιμή έχει ο \eng{SP} κατά την εκκίνηση. + +\par Έχοντας αυτό σαν δεδομένο μπορούμε να επιλέξουμε μια τιμή στην ελεύθερη περιοχή, έχοντας πάντα στο μυαλό μας ότι αλλάζοντας τα δεδομένα του προγράμματος η τιμή αυτή μπορεί να αλλάξει. +Στη δική μας περίπτωση η επιλεγμένη τιμή ήταν το \eng{\textbf{0x20001000}.} +Φυσικά αυτή η τιμή μπορεί να αλλάξει εύκολα αλλάζοντας απλώς την τιμή από το \eng{\textbf{\#define RESULT}.} +Μάλιστα αυτό είναι και κάτι που προτρέπουμε κάνει ο οποιονδήποτε θέλει να τρέξει τον κώδικά μας, ώστε να βεβαιωθεί πως η τιμή αυτή είναι στην ασφαλή ζώνη. + + +\section{Έλεγχος} +Για τον έλεγχο της ορθότητας του κώδικα χρησιμοποιήσαμε τον \eng{debuger} του εργαλείου κάνοντας ταυτόχρονη χρήση του \eng{simulator.} +Καθώς η εκφώνηση ανέφερε την δημιουργία μιας \eng{main,} θεωρήσαμε ότι είναι ευκαιρία να χρησιμοποιήσουμε την \eng{main} για να επιτύχουμε ένα είδους \eng{debug-mode unit testing.} +Έτσι στο εσωτερικό της δηλώσαμε αλφαριθμητικά για έλεγχο και για αυτά καλέσαμε την \eng{\textit{isPalindrome()}.} +Έπειτα εκτελώντας τον \eng{debuger} καταφέραμε να κάνουμε την όποια αποσφαλμάτωση που χρειάστηκε. + +\section{Επίλογος} +Θεωρούμε ότι η παρούσα εργασία ήταν μια καλή πρώτη επαφή με την μίξη κώδικα \eng{C - assemblly,} καθώς και των όποιων απαιτήσεων από μεριάς \eng{assemblly} ώστε αυτή να είναι συμβατή με το πρότυπο που ακολουθεί ο \eng{compiler (AAPCS).} +Η μόνη παραφωνία σε αυτή τη συνεργασία ήταν η επιλογή μιας ελεύθερης θέσης μνήμης για την επιστροφή του αποτελέσματος. +Αυτό γιατί μέχρι τη σύνταξη του παρόντος δεν βρήκαμε τρόπο να διαβάσουμε από διαφορετικό αρχείο(πχ το \eng{main.c}) την τιμή \eng{\_\_initial\_sp,} ώστε η επιλογή της θέσης να αυτοματοποιηθεί. + +% References +% ============================ +%\begin{thebibliography}{100} +% +%\bibitem{item}item... + +%\end{thebibliography} + +\end{document} \ No newline at end of file diff --git a/assignment_2/Libraries/STM32CubeF4 b/assignment_2/Libraries/STM32CubeF4 new file mode 160000 index 0000000..e005301 --- /dev/null +++ b/assignment_2/Libraries/STM32CubeF4 @@ -0,0 +1 @@ +Subproject commit e00530150fa6d390ff68bd6fafe6c611c39d8cd7 diff --git a/assignment_2/report/config b/assignment_2/report/config new file mode 160000 index 0000000..ddb6624 --- /dev/null +++ b/assignment_2/report/config @@ -0,0 +1 @@ +Subproject commit ddb6624bbf227f98db5d25ae7b2a5698719fc17b diff --git a/assignment_2/report/images/experiment-1.png b/assignment_2/report/images/experiment-1.png new file mode 100755 index 0000000..7b393c7 Binary files /dev/null and b/assignment_2/report/images/experiment-1.png differ diff --git a/assignment_2/report/images/experiment-2.png b/assignment_2/report/images/experiment-2.png new file mode 100755 index 0000000..2a30d46 Binary files /dev/null and b/assignment_2/report/images/experiment-2.png differ diff --git a/assignment_2/src/NUCLEO_F401RE.c b/assignment_2/src/NUCLEO_F401RE.c new file mode 100644 index 0000000..8d64613 --- /dev/null +++ b/assignment_2/src/NUCLEO_F401RE.c @@ -0,0 +1,289 @@ +/*! + * \file + * NUCLEO_F401RE.h + * \brief + * Nucleo F401RE port file. This file contain the implementation of driver + * calls for F401RE board. + * + * Created on: May 23, 2020 + * Author: Christos Choutouridis AEM: 8997 + * email : + */ +#include "NUCLEO_F401RE.h" + +/* + * =============== System =============== + */ + +static clock_t volatile __ticks; //!< CPU time +static time_t volatile __now; //!< Time in UNIX seconds past 1-Jan-70 +static clock_t volatile __sys_freq; //!< The CPU's time frequency (SysTick freq) + +/*! + * \brief + * This is the SysTick ISR, micro-system time base service for CPU time. + * \note + * This service implements the SysTick callback function in order + * to provide micro system - os like functionalities to an application + * without RTOS + */ +void SysTick_Handler(void) { + // Time + ++__ticks; + if ( !(__ticks % __sys_freq ) ) + ++__now; // Do not update __now when we have external time system +} + + +/*! + * \brief This function configures the source of the time base. + * The time source is configured to have 1ms time base with a dedicated + * Tick interrupt priority. + * \param sf Tick interrupt frequency. + * \retval HAL status + */ +__weak HAL_StatusTypeDef HAL_SysTick_Init(clock_t sf) { + SystemCoreClockUpdate (); + + /* Configure the SysTick to have interrupt in sf time basis */ + if (SysTick_Config (SystemCoreClock/sf) != 0) + return HAL_ERROR; + __sys_freq = sf; + + /*Configure the SysTick IRQ priority */ + NVIC_SetPriority (SysTick_IRQn, 3U); + + /* Return function status */ + return HAL_OK; +} + +/*! + * Select the system frequency without calling the Setting functionality + * \param sf The desired value + * \return The desired value (enable chaining) + */ +__INLINE clock_t HAL_SelectSysTickFreq (clock_t sf){ + return __sys_freq =sf; +} + +/*! + * \brief Get the __sys_freq. + */ +__INLINE clock_t HAL_GetSysTickFreq (void){ + return __sys_freq; +} + + +/*! + * \brief Reconfigure the SysTick and update __sys_freq + * \param sf Tick interrupt frequency (CPU time) + * \return status of the operation + * \arg 0 Success + * \arg 1 Fail + */ +int HAL_SetSysTickFreq (clock_t sf) { + /*Configure the SysTick to have interrupt in sf time basis*/ + if (__sys_freq != sf) { + // Time base configuration + SystemCoreClockUpdate (); + if (SysTick_Config ( (SystemCoreClock>>3)/sf) != 0) + return 1; + else { + __sys_freq = sf; + return 0; + } + } + return 0; +} + +// Take over control of SysTick from HAL library + +//! disable HAL_InitTick implementation +HAL_StatusTypeDef + HAL_InitTick(uint32_t TickPriority) { return HAL_OK; } + +//! Chain GetTick to our implementation +uint32_t HAL_GetTick(void) { return clock(); } + +/*! + * \brief This function provides minimum delay (in CPU time) based + * on variable incremented. + * \param Delay specifies the delay time length, in CPU time. + * \note + * uint32_t is implicitly convertible to clock_t and vice versa. + */ +void HAL_Delay(uint32_t Delay) { + uint32_t tickstart = clock(); + + while((clock() - tickstart) < Delay) + ; +} + +/* + * ======== OS like Functionalities ============ + */ + + +//! SysTick frequency getter +__INLINE clock_t get_freq (void) { + return __sys_freq; +} + +//! SysTick frequency setter +//! \return True on failure +int set_freq (clock_t sf) { + return HAL_SetSysTickFreq (sf); +} + +/*! + * \brief + * determines the processor time. + * \return + * the implementation's best approximation to the processor time + * used by the program since program invocation. The time in + * seconds is the value returned divided by the value of the macro + * CLK_TCK or CLOCKS_PER_SEC + */ +__INLINE clock_t clock (void) { + return (clock_t) __ticks; +} + +/*! + * \brief + * Set the processor time used. + * \param c The new CPU time value + * \return + * The implementation's best approximation to the processor time + * used by the program since program invocation. The time in + * seconds is the value returned divided by the value of the macro + * CLK_TCK or CLOCKS_PER_SEC + */ +clock_t setclock (clock_t c) { + return __ticks = c; +} + +/*! + * \brief + * determines the current calendar time. The encoding of the value is + * unspecified. + * \return + * The implementations best approximation to the current calendar + * time. If timer is not a null pointer, the return value + * is also assigned to the object it points to. + */ +time_t time (time_t *timer) { + if (timer) + *timer = (time_t)__now; + return (time_t)__now; +} + +/*! + * \brief + * Sets the system's idea of the time and date. The time, + * pointed to by t, is measured in seconds since the Epoch, 1970-01-01 + * 00:00:00 +0000 (UTC). + * \param t Pointer to new system's time and date. + * \return On success, zero is returned. On error, -1 is returned + */ +int settime (const time_t *t) { + if (t) { + __now = *t; + return 0; + } + else + return -1; +} + + +/* + * ============== Cycle count ============== + */ +/*! + * Initialize CPU cycle measurement functionality based on DBG + * \return The status of the operation + * \arg LLD_OK Success + * \arg LLD_ERROR Failure + */ +LLD_Status_en CYCLE_Init (void) { + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // enable trace + //DWT->LAR = 0xC5ACCE55; // <-- added unlock access to DWT (ITM, etc.)registers + DWT->CYCCNT = 0; // clear DWT cycle counter + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // enable DWT cycle counter + return LLD_OK; +} + +//! CPU cycle getter +__INLINE clock_t CYCLE_Get (void) { + return (clock_t)DWT->CYCCNT; +} + +/* + * =============== Digital I/O =============== + * BTN -- PC13 + * LED -- PA5 (SB42 is in place) [SB29: PB13] + */ + +//! Helper digital input pin getter +static __INLINE uint8_t _DINx (GPIO_TypeDef *port, uint32_t pin) { + return ((port->IDR & pin) != 0) ? 1:0; +} + +//! Helper digital output pin setter +static __INLINE uint8_t _DOUTx (GPIO_TypeDef *port, uint32_t pin, uint8_t st) { + if (st) port->BSRR = (uint32_t)pin; + else port->BSRR = (uint32_t)pin << 16; + return st; +} + +/*! + * Initialize GPIO port pins for Nucleo Board + * \return The status of the operation + * \arg LLD_OK Success + * \arg LLD_ERROR Failure + */ +LLD_Status_en Port_Init (void) { + GPIO_InitTypeDef GPIO_InitType; + + // Enable Port clock + __HAL_RCC_GPIOA_CLK_ENABLE (); + __HAL_RCC_GPIOC_CLK_ENABLE (); + + // BTN port configuration + GPIO_InitType.Mode = GPIO_MODE_INPUT; + GPIO_InitType.Pin = GPIO_PIN_13; + GPIO_InitType.Pull = GPIO_NOPULL; + + HAL_GPIO_Init(GPIOC, &GPIO_InitType); + + GPIO_InitType.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitType.Speed = GPIO_SPEED_LOW; + GPIO_InitType.Pin = GPIO_PIN_5; + + HAL_GPIO_Init(GPIOA, &GPIO_InitType); + + return LLD_OK; +} + +//! Nucleo's user button reader +uint8_t BTN (void) { + return _DINx (GPIOC, GPIO_PIN_13); +} + +//! Nucleo's LD2 led setter +void LED (uint8_t on) { + _DOUTx(GPIOA, GPIO_PIN_5, on); +} + +/*! Low level driver init functionality + * \return The status of the operation + * \arg LLD_OK Success + * \arg LLD_ERROR Failure + */ +LLD_Status_en LLD_Init (clock_t sys_freq) { + HAL_Init(); + HAL_SysTick_Init (sys_freq); + CYCLE_Init (); + Port_Init (); + + return LLD_OK; +} diff --git a/assignment_2/src/NUCLEO_F401RE.h b/assignment_2/src/NUCLEO_F401RE.h new file mode 100644 index 0000000..dcab44c --- /dev/null +++ b/assignment_2/src/NUCLEO_F401RE.h @@ -0,0 +1,137 @@ +/*! + * \file + * NUCLEO_F401RE.h + * \brief + * Nucleo F401RE port file. This file contain the implementation of driver + * calls for F401RE board. + * + * Created on: May 23, 2020 + * Author: Christos Choutouridis AEM: 8997 + * email : + */ + +#ifndef NUCLEO_F401RE_H_ +#define NUCLEO_F401RE_H_ + +#include +#include +#include + +/* + * ========= Data types ======== + */ + +//! Driver status return type +typedef enum { + LLD_OK = 0, //!< Indicate successful operation + LLD_ERROR //!< Indicate Error +}LLD_Status_en; + +typedef uint8_t din_t; +//typedef int adc_t; + +#define OFF (0) +#define ON (!OFF) +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + + +/* + * =============== System =============== + */ +#if defined ( __GNUC__ ) && !defined (__CC_ARM) +#include +#endif +#include + +/* + * Also defined in types.h + */ +#ifndef _CLOCK_T_ +#define _CLOCK_T_ unsigned long /* clock() */ +typedef _CLOCK_T_ clock_t; /*!< CPU time type */ +#endif +#ifndef _TIME_T_ +#define _TIME_T_ long /* time() */ +typedef _TIME_T_ time_t; /*!< date/time in unix secs past 1-Jan-70 type for 68 years*/ +#endif + + +/* + * Helper macros + */ +#define _CLOCK_T_MAX_VALUE_ (ULONG_MAX) //!< Helper macro for maximum signed CPU time calculations + +/*! + * Calculate the positive time difference of _t2_ and _t1_, where + * _t1_, _t2_ are clock_t values + * \note + * _t2_ event comes is AFTER _t1_ + * + * ex: + * 0 1 2 3 4 5 6 7 8 9 + * ^ ^ + * | | + * a b + * + * if : t1=a, t2=b then dt = b-a = t2 - t1 + * if : t1=b, t2=a then dt = 9 - (b-a) + 1 = UMAX - (t1-t2) + 1 + * + */ +#define _CLOCK_DIFF(_t2_, _t1_) ( ((_t2_)>(_t1_)) ? ((_t2_)-(_t1_)) : (_CLOCK_T_MAX_VALUE_ - ((_t1_) - (_t2_)) + 1) ) + +/* + * CPU time macros + */ +#define msec2CPUtime(_ms_) (((_ms_) * get_freq()) / 1000) +#define sec2CPUtime(_s_) ((_s_) * get_freq()) + +#define CPUtime2msec(_t_) (((_t_) * 1000) / get_freq()) +#define CPUtime2sec(_t_) ((_t_) / get_freq()) + +HAL_StatusTypeDef HAL_SysTick_Init(clock_t sf); + +clock_t HAL_SelectSysTickFreq (clock_t sf); +clock_t HAL_GetSysTickFreq (void); +int HAL_SetSysTickFreq (clock_t sf); + +/* + * OS like Functionalities + */ +clock_t get_freq (void); +int set_freq (clock_t sf); + +clock_t clock (void); +clock_t setclock (clock_t c); + +time_t time (time_t *timer); +int settime (const time_t *t); + + +/* + * ============== Cycle count ============== + */ +LLD_Status_en CYCLE_Init (void); +clock_t CYCLE_Get (void); + +/* + * =============== Digital I/O =============== + * BTN -- PC13 + * LED -- PA5 (SB42 is in place) [SB29: PB13] + */ +LLD_Status_en Port_Init (void); + +uint8_t BTN (void); +void LED (uint8_t on); + + +/* + * ============= Board Init ============== + */ +LLD_Status_en LLD_Init (clock_t sys_freq); + +#endif /* NUCLEO_F401RE_H_ */ diff --git a/assignment_2/src/assign2_impl.h b/assignment_2/src/assign2_impl.h new file mode 100644 index 0000000..a56bd83 --- /dev/null +++ b/assignment_2/src/assign2_impl.h @@ -0,0 +1,63 @@ +/*! + * \file + * assign2_impl.h + * \brief + * Assignment 2 application header + * + * Created on: May 23, 2020 + * Author: Christos Choutouridis AEM: 8997 + * email : + */ + +#ifndef ASSIGN2_IMPL_H_ +#define ASSIGN2_IMPL_H_ + +#include "NUCLEO_F401RE.h" +#include +#include + +/* + * ============= User defines =============== + */ +#define SYSTICK_FREQ (1000) //!< 1000Hz => 1msec accuracy + +#define MODE_LEADING_EDGE (1) //!< Start counting as soon as the led is switched on +#define MODE_TRAILING_EDGE (2) //!< Start counting as soon as the led is switched off (Motor sport style) + +//! If there is no Pre-define MODE, select one here +#ifndef MODE +#define MODE MODE_TRAILING_EDGE +#endif + +#define MEASUREMENTS (5) //!< The number of measurements for each experiment + +//! elect the maximum waiting time before the visual trigger. +#define MAX_WAIT_TIME sec2CPUtime(10) + +//! Select if we need cycle counting also. +#define CYCLE_COUNTING (1) + +/* + * ============= Data types =============== + */ +//! Select the application wide accuracy of the floating point type. +typedef float fp_data_t; //!< floating point data alias. + +/*! + * Statistical data structure + */ +typedef struct { + fp_data_t average; //!< The average response time of the experiment + fp_data_t median; //!< The median of the times + fp_data_t std_dev; //!< Standard deviation +} stats_t; + +fp_data_t average (const clock_t *t, size_t n); +fp_data_t median (const clock_t *t, size_t n); +fp_data_t std_deviation (const clock_t* t, size_t n); + +void leading (clock_t *out, size_t n); +void trailing (clock_t *out, size_t n); + + +#endif /* ASSIGN2_IMPL_H_ */ diff --git a/assignment_2/src/main.c b/assignment_2/src/main.c new file mode 100644 index 0000000..09fd390 --- /dev/null +++ b/assignment_2/src/main.c @@ -0,0 +1,145 @@ +/*! + * \file + * main.c + * \brief + * Main application file + * + * Created on: May 23, 2020 + * Author: Christos Choutouridis AEM: 8997 + * email : + */ +#include "assign2_impl.h" + +/* + * Global data + */ +stats_t stats; + +/*! + * Compare functionality for qsort + * \param a left hand site + * \param b right hand site + * \return stdlib requirements + * \arg -1 ab + */ +static int cmpfunc (const void * a, const void * b) { + fp_data_t v = *(fp_data_t*)a - *(fp_data_t*)b; + return (v < 0) ? -1 : (v > 0) ? 1 : 0; +} + +/*! + * Calculates and return the average of an array of measurements + * \param t Pointer to measurements + * \param n Size of measurements array + * \return The average + */ +fp_data_t average (const clock_t *t, size_t n) { + fp_data_t ret =0; + for (size_t i=0 ; i