Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

В το πρώτο μέρος Προσπάθησα να πω στους χόμπι μηχανικούς ηλεκτρονικών που μεγάλωσαν από τα παντελόνια Arduino πώς και γιατί πρέπει να διαβάζουν φύλλα δεδομένων και άλλη τεκμηρίωση για μικροελεγκτές. Το κείμενο αποδείχθηκε μεγάλο, οπότε υποσχέθηκα να δείξω πρακτικά παραδείγματα σε ξεχωριστό άρθρο. Λοιπόν, αποκαλούσε τον εαυτό του μανιτάρι γάλακτος...

Σήμερα θα σας δείξω πώς να χρησιμοποιείτε φύλλα δεδομένων για να λύσετε αρκετά απλά, αλλά απαραίτητα για πολλά έργα, εργασίες σε ελεγκτές STM32 (Blue Pill) και STM8. Όλα τα έργα επίδειξης είναι αφιερωμένα στα αγαπημένα μου LED, θα τα ανάψουμε σε μεγάλες ποσότητες, για τα οποία θα πρέπει να χρησιμοποιήσουμε κάθε λογής ενδιαφέροντα περιφερειακά.

Το κείμενο και πάλι αποδείχθηκε τεράστιο, οπότε για ευκολία φτιάχνω το περιεχόμενο:

STM32 Blue Pill: 16 LED με πρόγραμμα οδήγησης DM634
STM8: Ρύθμιση έξι ακίδων PWM
STM8: 8 RGB LED σε τρεις ακίδες, διακοπές

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

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

Ελπίζω ότι το άρθρο μου θα βοηθήσει κάποιον σε παρόμοιο στάδιο βύθισης στο χόμπι.

STM32

16 LED με DM634 και SPI

Ένα μικρό έργο που χρησιμοποιεί Blue Pill (STM32F103C8T6) και πρόγραμμα οδήγησης LED DM634. Χρησιμοποιώντας φύλλα δεδομένων, θα καταλάβουμε το πρόγραμμα οδήγησης, τις θύρες STM IO και θα διαμορφώσουμε το SPI.

DM634

Ταϊβανέζικο τσιπ με 16 εξόδους PWM 16-bit, μπορεί να συνδεθεί σε αλυσίδες. Το low-end μοντέλο 12-bit είναι γνωστό από ένα εγχώριο έργο Lightpack. Κάποτε, επιλέγοντας μεταξύ του DM63x και του γνωστού TLC5940, επέλεξα το DM για διάφορους λόγους: 1) Το TLC στο Aliexpress είναι σίγουρα ψεύτικο, αλλά αυτό δεν είναι. 2) Η DM έχει αυτόνομο PWM με δική της γεννήτρια συχνοτήτων. 3) θα μπορούσε να αγοραστεί φθηνά στη Μόσχα, αντί να περιμένει ένα δέμα από τον Ali. Και, φυσικά, ήταν ενδιαφέρον να μάθετε πώς να ελέγχετε το τσιπ μόνοι σας, αντί να χρησιμοποιείτε μια έτοιμη βιβλιοθήκη. Τα τσιπ παρουσιάζονται πλέον κυρίως στη συσκευασία SSOP24· είναι εύκολο να συγκολληθούν σε έναν προσαρμογέα.

Δεδομένου ότι ο κατασκευαστής είναι Ταϊβανέζος, φύλλο δεδομένων το τσιπ είναι γραμμένο στα κινέζικα αγγλικά, που σημαίνει ότι θα είναι διασκεδαστικό. Πρώτα κοιτάμε το pinout (Σύνδεση Pin) για να καταλάβετε με ποιο πόδι να συνδέσετε και μια περιγραφή των ακίδων (Περιγραφή καρφίτσας). 16 καρφίτσες:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Πηγές νεροχύτη DC (Ανοιχτή αποχέτευση)

Νεροχύτης / Έξοδος ανοιχτής αποστράγγισης – αποστράγγιση πηγή εισροής ρεύματος. η έξοδος συνδέεται με τη γείωση σε ενεργή κατάσταση - τα LED συνδέονται με τον οδηγό μέσω καθόδων. Ηλεκτρικά, αυτό, φυσικά, δεν είναι μια "ανοικτή αποχέτευση" (ανοιχτό σιφώνι), αλλά στα φύλλα δεδομένων αυτή η ονομασία για τις ακίδες σε λειτουργία αποστράγγισης βρίσκεται συχνά.

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Εξωτερικές αντιστάσεις μεταξύ REXT και GND για να ορίσετε την τιμή του ρεύματος εξόδου

Μια αντίσταση αναφοράς είναι εγκατεστημένη μεταξύ της ακίδας REXT και της γείωσης, η οποία ελέγχει την εσωτερική αντίσταση των εξόδων, δείτε το γράφημα στη σελίδα 9 του φύλλου δεδομένων. Στο DM634, αυτή η αντίσταση μπορεί επίσης να ελεγχθεί από λογισμικό, ρυθμίζοντας τη συνολική φωτεινότητα (παγκόσμια φωτεινότητα) Δεν θα μπω σε λεπτομέρειες σε αυτό το άρθρο, απλώς θα βάλω εδώ μια αντίσταση 2.2 - 3 kOhm.

Για να κατανοήσουμε πώς να ελέγχετε το τσιπ, ας δούμε την περιγραφή της διεπαφής της συσκευής:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

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

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
... Απαιτούνται μόνο τρεις ακίδες για την εισαγωγή δεδομένων στη συσκευή. Η ανερχόμενη άκρη του σήματος SCLK μετατοπίζει τα δεδομένα από τον ακροδέκτη SIN στον εσωτερικό καταχωρητή. Αφού φορτωθούν όλα τα δεδομένα, ένα σύντομο σήμα υψηλής XLAT ασφαλίζει τα διαδοχικά μεταφερόμενα δεδομένα στους εσωτερικούς καταχωρητές. Οι εσωτερικοί καταχωρητές είναι πύλες που ενεργοποιούνται από το επίπεδο σήματος XLAT. Όλα τα δεδομένα μεταδίδονται πρώτα το πιο σημαντικό bit.

Μάνταλο – μάνδαλο/μάνδαλο/κλείδωμα.
Ανερχόμενη άκρη – προπορευόμενο άκρο του παλμού
MSB πρώτα – το πιο σημαντικό (αριστερό) κομμάτι προς τα εμπρός.
για ρολόι δεδομένων – μετάδοση δεδομένων διαδοχικά (bit bit).

Λόγος μάνταλο βρίσκεται συχνά στην τεκμηρίωση για μάρκες και μεταφράζεται με διάφορους τρόπους, οπότε για λόγους κατανόησης θα επιτρέψω στον εαυτό μου

μικρό εκπαιδευτικό πρόγραμμαΟ οδηγός LED είναι ουσιαστικά ένας καταχωρητής αλλαγής ταχυτήτων. "Βάρδια" (αλλαγή) στην ονομασία - κατά bit κίνηση των δεδομένων μέσα στη συσκευή: κάθε νέο bit που εισάγεται μέσα σπρώχνει ολόκληρη την αλυσίδα προς τα εμπρός μπροστά της. Καθώς κανείς δεν θέλει να παρατηρήσει χαοτικό αναβοσβήσιμο των LED κατά τη διάρκεια της βάρδιας, η διαδικασία λαμβάνει χώρα σε καταχωρητές buffer που χωρίζονται από τους καταχωρητές εργασίας με έναν αποσβεστήρα (μάνταλο) είναι ένα είδος αίθουσας αναμονής όπου τα κομμάτια είναι διατεταγμένα με την επιθυμητή σειρά. Όταν όλα είναι έτοιμα, το κλείστρο ανοίγει και τα κομμάτια λειτουργούν, αντικαθιστώντας την προηγούμενη παρτίδα. Λέξη μάνταλο στην τεκμηρίωση για μικροκυκλώματα σχεδόν πάντα υπονοείται ένας τέτοιος αποσβεστήρας, ανεξάρτητα από τους συνδυασμούς που χρησιμοποιείται.

Έτσι, η μεταφορά δεδομένων στο DM634 πραγματοποιείται ως εξής: ρυθμίστε την είσοδο DAI στην τιμή του πιο σημαντικού bit της μακρινής λυχνίας LED, τραβήξτε το DCK πάνω και κάτω. Ρυθμίστε την είσοδο DAI στην τιμή του επόμενου bit, τραβήξτε το DCK. και ούτω καθεξής μέχρι να μεταδοθούν όλα τα bit (ρολόι μέσα), μετά από το οποίο τραβάμε LAT. Αυτό μπορεί να γίνει χειροκίνητα (bit-bang), αλλά είναι καλύτερο να χρησιμοποιήσετε μια διεπαφή SPI ειδικά προσαρμοσμένη για αυτό, καθώς παρουσιάζεται στο STM32 μας σε δύο αντίγραφα.

Μπλε χάπι STM32F103

Εισαγωγικά: Οι ελεγκτές STM32 είναι πολύ πιο περίπλοκοι από το Atmega328 από ό,τι μπορεί να φαίνονται τρομακτικοί. Επιπλέον, για λόγους εξοικονόμησης ενέργειας, σχεδόν όλα τα περιφερειακά είναι απενεργοποιημένα στην εκκίνηση και η συχνότητα ρολογιού είναι 8 MHz από την εσωτερική πηγή. Ευτυχώς, οι προγραμματιστές STM έγραψαν κώδικα που φέρνει το τσιπ στα «υπολογισμένα» 72 MHz και οι συντάκτες όλων των IDE που γνωρίζω τον συμπεριέλαβαν στη διαδικασία προετοιμασίας, οπότε δεν χρειάζεται να χρονομετρήσουμε (αλλά μπορείς αν θέλεις πραγματικά). Θα πρέπει όμως να ενεργοποιήσετε τα περιφερειακά.

Τεκμηρίωση: Το Blue Pill είναι εξοπλισμένο με το δημοφιλές τσιπ STM32F103C8T6, υπάρχουν δύο χρήσιμα έγγραφα για αυτό:

Στο φύλλο δεδομένων μπορεί να μας ενδιαφέρει:

  • Pinouts – chip pinouts – σε περίπτωση που αποφασίσουμε να φτιάξουμε τις σανίδες μόνοι μας.
  • Χάρτης μνήμης – χάρτης μνήμης για ένα συγκεκριμένο τσιπ. Το Εγχειρίδιο Αναφοράς έχει έναν χάρτη για ολόκληρη τη γραμμή και αναφέρει καταχωρητές που δεν διαθέτει ο δικός μας.
  • Πίνακας ορισμών καρφίτσας – παρατίθενται οι κύριες και οι εναλλακτικές λειτουργίες των ακίδων. για το "μπλε χάπι" μπορείτε να βρείτε πιο βολικές φωτογραφίες στο Διαδίκτυο με μια λίστα με καρφίτσες και τις λειτουργίες τους. Ως εκ τούτου, αναζητούμε αμέσως το Blue Pill pinout και κρατάμε αυτή την εικόνα στη διάθεσή σας:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Σημείωση: υπήρχε ένα σφάλμα στην εικόνα από το Διαδίκτυο, το οποίο σημειώθηκε στα σχόλια, σας ευχαριστώ για αυτό. Η εικόνα έχει αντικατασταθεί, αλλά αυτό είναι ένα μάθημα - είναι καλύτερο να ελέγχετε πληροφορίες όχι από φύλλα δεδομένων.

Αφαιρούμε το φύλλο δεδομένων, ανοίγουμε το Εγχειρίδιο αναφοράς και στο εξής χρησιμοποιούμε μόνο αυτό.
Διαδικασία: ασχολούμαστε με τυπική είσοδο/έξοδο, διαμορφώνουμε το SPI, ενεργοποιούμε τα απαραίτητα περιφερειακά.

Εισόδου-εξόδου

Στο Atmega328, το I/O υλοποιείται εξαιρετικά απλά, γι' αυτό η πληθώρα επιλογών STM32 μπορεί να προκαλέσει σύγχυση. Τώρα χρειαζόμαστε μόνο συμπεράσματα, αλλά ακόμη και αυτά έχουν τέσσερις επιλογές:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
ανοιχτή αποστράγγιση, ώθηση-έλξη, εναλλακτική ώθηση-έλξη, εναλλακτική ανοιχτή αποστράγγιση

"Τραβήξτε-σπρώξτε" (σπρώχνω τραβώ) είναι η συνηθισμένη έξοδος από το Arduino, η ακίδα μπορεί να πάρει την τιμή είτε HIGH είτε LOW. Αλλά με "ανοιχτή αποχέτευση" υπάρχουν δυσκολίες, αν και στην πραγματικότητα όλα είναι απλά εδώ:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Διαμόρφωση εξόδου / όταν η θύρα έχει αντιστοιχιστεί στην έξοδο: / ενεργοποίηση του buffer εξόδου: / – ανοιχτή λειτουργία αποστράγγισης: "0" στον καταχωρητή εξόδου ενεργοποιεί το N-MOS, το "1" στον καταχωρητή εξόδου αφήνει τη θύρα σε λειτουργία Hi-Z ( Το P-MOS δεν είναι ενεργοποιημένο ) / – λειτουργία push-pull: «0» στον καταχωρητή εξόδου ενεργοποιεί το N-MOS, το «1» στον καταχωρητή εξόδου ενεργοποιεί το P-MOS.

Όλη η διαφορά μεταξύ ανοιχτής αποχέτευσης (ανοιχτό σιφώνι) από το "push-pull" (σπρώχνω τραβώ) είναι ότι στην πρώτη ακίδα δεν μπορεί να δεχτεί την κατάσταση HIGH: όταν γράφετε ένα στον καταχωρητή εξόδου, μεταβαίνει σε λειτουργία υψηλής αντίστασης (υψηλή αντίσταση, Γεια-Ζ). Όταν γράφετε μηδέν, η ακίδα συμπεριφέρεται το ίδιο και στις δύο λειτουργίες, τόσο λογικά όσο και ηλεκτρικά.

Στην κανονική λειτουργία εξόδου, ο ακροδέκτης απλώς μεταδίδει τα περιεχόμενα του καταχωρητή εξόδου. Στην «εναλλακτική» ελέγχεται από τα αντίστοιχα περιφερειακά (βλ. 9.1.4):

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Εάν ένα bit θύρας έχει διαμορφωθεί ως ακροδέκτης εναλλακτικής λειτουργίας, ο καταχωρητής ακροδεκτών απενεργοποιείται και ο ακροδέκτης συνδέεται με τον περιφερειακό ακροδέκτη.

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

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Εάν πολλά περιφερειακά χρησιμοποιούν την ίδια ακίδα, για να αποφευχθεί η σύγκρουση μεταξύ εναλλακτικών λειτουργιών, θα πρέπει να χρησιμοποιείται μόνο ένα περιφερειακό κάθε φορά, με εναλλαγή χρησιμοποιώντας το bit ενεργοποίησης του περιφερειακού ρολογιού (στον κατάλληλο καταχωρητή RCC).

Τέλος, οι ακίδες στη λειτουργία εξόδου έχουν επίσης ταχύτητα ρολογιού. Αυτό είναι ένα άλλο χαρακτηριστικό εξοικονόμησης ενέργειας· στην περίπτωσή μας, απλώς το ρυθμίζουμε στο μέγιστο και το ξεχνάμε.

Άρα: χρησιμοποιούμε SPI, που σημαίνει ότι δύο ακίδες (με δεδομένα και με σήμα ρολογιού) θα πρέπει να είναι «εναλλακτική λειτουργία ώθησης» και μια άλλη (LAT) θα πρέπει να είναι «κανονική ώθηση». Αλλά πριν τους αναθέσουμε, ας ασχοληθούμε με το SPI.

SPI

Άλλο ένα μικρό εκπαιδευτικό πρόγραμμα

Το SPI ή Serial Peripheral Interface (σειριακή περιφερειακή διασύνδεση) είναι μια απλή και πολύ αποτελεσματική διεπαφή για τη σύνδεση ενός MK με άλλα MK και τον έξω κόσμο γενικότερα. Η αρχή της λειτουργίας του έχει ήδη περιγραφεί παραπάνω, σχετικά με το κινεζικό πρόγραμμα οδήγησης LED (στο εγχειρίδιο αναφοράς, βλέπε ενότητα 25). Το SPI μπορεί να λειτουργήσει σε λειτουργία master ("master") και slave ("slave"). Το SPI έχει τέσσερα βασικά κανάλια, από τα οποία δεν μπορούν να χρησιμοποιηθούν όλα:

  • MOSI, Master Output / Slave Input: αυτή η ακίδα μεταδίδει δεδομένα στην κύρια λειτουργία και λαμβάνει δεδομένα σε λειτουργία υποτελούς λειτουργίας.
  • MISO, Master Input / Slave Output: αντίθετα, λαμβάνει στο master και εκπέμπει στο slave.
  • SCK, Σειριακό ρολόι: ορίζει τη συχνότητα μετάδοσης δεδομένων στην κύρια μονάδα ή λαμβάνει ένα σήμα ρολογιού στην υποτελή. Ουσιαστικά χτύπημα κτυπά?
  • SS, Slave Select: με τη βοήθεια αυτού του καναλιού, ο σκλάβος ξέρει ότι κάτι ζητείται από αυτόν. Στο STM32 ονομάζεται NSS, όπου N = αρνητικό, δηλ. ο ελεγκτής γίνεται σκλάβος εάν υπάρχει γείωση σε αυτό το κανάλι. Συνδυάζεται καλά με τη λειτουργία Open Drain Output, αλλά αυτό είναι μια άλλη ιστορία.

Όπως όλα τα άλλα, το SPI στο STM32 είναι πλούσιο σε λειτουργικότητα, γεγονός που το καθιστά κάπως δύσκολο να το κατανοήσουμε. Για παράδειγμα, μπορεί να λειτουργήσει όχι μόνο με το SPI, αλλά και με μια διεπαφή I2S, και στην τεκμηρίωση οι περιγραφές τους αναμειγνύονται, είναι απαραίτητο να αποκοπεί η περίσσεια εγκαίρως. Η αποστολή μας είναι εξαιρετικά απλή: χρειάζεται απλώς να στείλουμε δεδομένα χρησιμοποιώντας μόνο MOSI και SCK. Πηγαίνουμε στην ενότητα 25.3.4 (επικοινωνία μισής διπλής όψης, επικοινωνία μισής διπλής όψης), όπου βρίσκουμε 1 ρολόι και 1 καλώδιο δεδομένων μονής κατεύθυνσης (1 σήμα ρολογιού και 1 ροή δεδομένων μονής κατεύθυνσης):

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Σε αυτήν τη λειτουργία, η εφαρμογή χρησιμοποιεί το SPI σε λειτουργία μόνο μετάδοσης ή λήψης. / Η λειτουργία μόνο μετάδοσης είναι παρόμοια με τη λειτουργία διπλής όψης: τα δεδομένα μεταδίδονται στον ακροδέκτη μετάδοσης (MOSI σε κύρια λειτουργία ή MISO σε λειτουργία υποτελούς λειτουργίας) και η ακίδα λήψης (MISO ή MOSI αντίστοιχα) μπορεί να χρησιμοποιηθεί ως κανονική ακίδα εισόδου/εξόδου . Σε αυτήν την περίπτωση, η εφαρμογή χρειάζεται μόνο να αγνοήσει την προσωρινή μνήμη Rx (αν διαβαστεί, δεν θα μεταφερθούν δεδομένα εκεί).

Ωραία, η ακίδα MISO είναι δωρεάν, ας συνδέσουμε το σήμα LAT σε αυτήν. Ας δούμε το Slave Select, το οποίο στο STM32 μπορεί να ελεγχθεί μέσω προγραμματισμού, κάτι που είναι εξαιρετικά βολικό. Διαβάζουμε την ομώνυμη παράγραφο στην ενότητα 25.3.1 SPI Γενική περιγραφή:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Έλεγχος λογισμικού NSS (SSM = 1) / Οι πληροφορίες επιλογής υποτελών περιέχονται στο bit SSI του καταχωρητή SPI_CR1. Ο εξωτερικός ακροδέκτης NSS παραμένει ελεύθερος για άλλες ανάγκες εφαρμογής.

Ήρθε η ώρα να γράψετε στα μητρώα. Αποφάσισα να χρησιμοποιήσω το SPI2, αναζητήστε τη βασική του διεύθυνση στο φύλλο δεδομένων - στην ενότητα 3.3 Χάρτης μνήμης:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Λοιπόν, ας ξεκινήσουμε:

#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))

Ανοίξτε την ενότητα 25.3.3 με τον αυτονόητο τίτλο "Διαμόρφωση SPI σε λειτουργία Master":

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

1. Ρυθμίστε τη συχνότητα σειριακού ρολογιού με bits BR[2:0] στον καταχωρητή SPI_CR1.

Τα μητρώα συλλέγονται στην ενότητα του εγχειριδίου αναφοράς με το ίδιο όνομα. Μετατόπιση διεύθυνσης (Μετατόπιση διεύθυνσης) για CR1 – 0x00, από προεπιλογή όλα τα bit διαγράφονται (Επαναφορά τιμής 0x0000):

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Τα bit BR ρυθμίζουν το διαχωριστικό ρολογιού του ελεγκτή, καθορίζοντας έτσι τη συχνότητα στην οποία θα λειτουργεί το SPI. Η συχνότητά μας STM32 θα είναι 72 MHz, το πρόγραμμα οδήγησης LED, σύμφωνα με το φύλλο δεδομένων του, λειτουργεί με συχνότητα έως και 25 MHz, επομένως πρέπει να διαιρέσουμε με το τέσσερα (BR[2:0] = 001).

#define _SPI_CR1 0x00

#define BR_0        0x0008
#define BR_1        0x0010
#define BR_2        0x0020

_SPI2_ (_SPI_CR1) |= BR_0;// pclk/4

2. Ρυθμίστε τα bit CPOL και CPHA για να καθορίσετε τη σχέση μεταξύ μεταφοράς δεδομένων και χρονισμού σειριακού ρολογιού (δείτε διάγραμμα στη σελίδα 240)

Επειδή διαβάζουμε ένα φύλλο δεδομένων εδώ και δεν εξετάζουμε σχηματικά, ας ρίξουμε μια πιο προσεκτική ματιά στην περιγραφή κειμένου των bit CPOL και CPHA στη σελίδα 704 (Γενική περιγραφή SPI):

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Φάση ρολογιού και πολικότητα
Χρησιμοποιώντας τα bit CPOL και CPHA του καταχωρητή SPI_CR1, μπορείτε να επιλέξετε μέσω προγραμματισμού τέσσερις σχέσεις χρονισμού. Το bit CPOL (πολικότητα ρολογιού) ελέγχει την κατάσταση του σήματος ρολογιού όταν δεν μεταδίδονται δεδομένα. Αυτό το bit ελέγχει τις λειτουργίες master και slave. Εάν γίνει επαναφορά του CPOL, ο ακροδέκτης SCK είναι χαμηλός στη λειτουργία ηρεμίας. Εάν έχει ρυθμιστεί το bit CPOL, η ακίδα SCK είναι ψηλά κατά τη λειτουργία ηρεμίας.
Όταν έχει ρυθμιστεί το bit CPHA (φάση ρολογιού), το στροβοσκόπιο υψηλής παγίδας bit είναι το δεύτερο άκρο του σήματος SCK (πέφτει εάν το CPOL είναι καθαρό, αυξάνεται εάν έχει ρυθμιστεί το CPOL). Τα δεδομένα συλλαμβάνονται από τη δεύτερη αλλαγή στο σήμα ρολογιού. Εάν το bit CPHA είναι καθαρό, το στροβοσκόπιο παγίδας υψηλών δυαδικών ψηφίων είναι η ανερχόμενη άκρη του σήματος SCK (φθίνουσα ακμή εάν έχει ρυθμιστεί CPOL, ανερχόμενη άκρη εάν διαγραφεί η CPOL). Τα δεδομένα καταγράφονται με την πρώτη αλλαγή στο σήμα ρολογιού.

Έχοντας απορροφήσει αυτή τη γνώση, καταλήγουμε στο συμπέρασμα ότι και τα δύο bit πρέπει να παραμείνουν μηδενικά, γιατί Θέλουμε το σήμα SCK να παραμένει χαμηλό όταν δεν χρησιμοποιείται και τα δεδομένα να μεταδίδονται στην ανερχόμενη άκρη του παλμού (βλ. Rising Edge στο φύλλο δεδομένων DM634).

Παρεμπιπτόντως, εδώ συναντήσαμε για πρώτη φορά ένα χαρακτηριστικό του λεξιλογίου στα φύλλα δεδομένων ST: σε αυτά είναι γραμμένη η φράση "επαναφέρετε το bit στο μηδέν" για να επαναφέρω λίγοαλλά όχι για να καθαρίσω λίγο, όπως, για παράδειγμα, το Atmega.

3. Ρυθμίστε το bit DFF για να προσδιορίσετε εάν το μπλοκ δεδομένων είναι 8-bit ή 16-bit

Πήρα συγκεκριμένα ένα DM16 634 bit για να μην ασχοληθώ με τη μετάδοση δεδομένων PWM 12 bit, όπως το DM633. Είναι λογικό να ορίσετε το DFF σε ένα:

#define DFF         0x0800

_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode

4. Διαμορφώστε το bit LSBFIRST στον καταχωρητή SPI_CR1 για να προσδιορίσετε τη μορφή μπλοκ

Το LSBFIRST, όπως υποδηλώνει το όνομά του, διαμορφώνει τη μετάδοση πρώτα με το λιγότερο σημαντικό bit. Αλλά το DM634 θέλει να λαμβάνει δεδομένα ξεκινώντας από το πιο σημαντικό bit. Επομένως, το αφήνουμε να μηδενιστεί.

5. Σε λειτουργία υλικού, εάν απαιτείται είσοδος από τον ακροδέκτη NSS, εφαρμόστε ένα υψηλό σήμα στον ακροδέκτη NSS κατά τη διάρκεια ολόκληρης της ακολουθίας μεταφοράς byte. Στη λειτουργία λογισμικού NSS, ορίστε τα bit SSM και SSI στον καταχωρητή SPI_CR1. Εάν η ακίδα NSS πρόκειται να χρησιμοποιηθεί ως έξοδος, πρέπει να οριστεί μόνο το bit SSOE.

Εγκαταστήστε SSM και SSI για να ξεχάσετε τη λειτουργία υλικού NSS:

#define SSI         0x0100
#define SSM         0x0200

_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high

6. Τα bit MSTR και SPE πρέπει να ρυθμιστούν (παραμένουν ρυθμισμένα μόνο εάν το σήμα NSS είναι υψηλό)

Στην πραγματικότητα, με αυτά τα bit ορίζουμε το SPI μας ως κύριο και το ενεργοποιούμε:

#define MSTR        0x0004
#define SPE         0x0040

_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;

Το SPI έχει ρυθμιστεί, ας γράψουμε αμέσως συναρτήσεις που στέλνουν byte στο πρόγραμμα οδήγησης. Συνεχίστε να διαβάζετε 25.3.3 «Διαμόρφωση SPI σε λειτουργία κύριας λειτουργίας»:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Εντολή μεταφοράς δεδομένων
Η μετάδοση ξεκινά όταν ένα byte γραφτεί στο buffer Tx.
Το byte δεδομένων φορτώνεται στον καταχωρητή shift στο παράλληλο λειτουργία (από τον εσωτερικό δίαυλο) κατά τη μετάδοση του πρώτου bit, μετά την οποία μεταδίδεται στο ακολουθητικός Λειτουργία pin MOSI, πρώτο ή τελευταίο bit προς τα εμπρός ανάλογα με τη ρύθμιση του bit LSBFIRST στον καταχωρητή CPI_CR1. Η σημαία TXE ορίζεται μετά τη μετάδοση δεδομένων από την προσωρινή μνήμη Tx στον καταχωρητή μετατόπισης, και δημιουργεί επίσης μια διακοπή εάν έχει οριστεί το bit TXEIE στον καταχωρητή CPI_CR1.

Τόνισα μερικές λέξεις στη μετάφραση για να επιστήσω την προσοχή σε ένα χαρακτηριστικό της υλοποίησης SPI στους ελεγκτές STM. Στο Atmega η σημαία TXE (Tx Empty, Το Tx είναι κενό και έτοιμο για λήψη δεδομένων) ορίζεται μόνο μετά την αποστολή ολόκληρου του byte προς τα έξω. Και εδώ αυτή η σημαία ορίζεται αφού το byte έχει εισαχθεί στον εσωτερικό καταχωρητή μετατόπισης. Δεδομένου ότι πιέζεται εκεί με όλα τα bit ταυτόχρονα (παράλληλα), και στη συνέχεια τα δεδομένα μεταδίδονται διαδοχικά, το TXE ορίζεται πριν ολοκληρωθεί η αποστολή του byte. Αυτό είναι σημαντικό γιατί στην περίπτωση του οδηγού LED μας, πρέπει να τραβήξουμε τον πείρο LAT μετά την αποστολή όλα δεδομένα, δηλ. Η σημαία TXE από μόνη της δεν θα μας αρκεί.

Αυτό σημαίνει ότι χρειαζόμαστε άλλη σημαία. Ας δούμε την 25.3.7 - "Σημαίες κατάστασης":

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
<…>
Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
σημαία BUSY
Η σημαία BSY ορίζεται και διαγράφεται από το υλικό (η εγγραφή σε αυτήν δεν έχει κανένα αποτέλεσμα). Η σημαία BSY υποδεικνύει την κατάσταση του επιπέδου επικοινωνίας SPI.
Επαναφέρει:
όταν ολοκληρωθεί η μεταφορά (εκτός από την κύρια λειτουργία εάν η μεταφορά είναι συνεχής)
όταν το SPI είναι απενεργοποιημένο
όταν παρουσιάζεται σφάλμα κύριας λειτουργίας (MODF=1)
Εάν η μεταφορά δεν είναι συνεχής, η σημαία BSY διαγράφεται μεταξύ κάθε μεταφοράς δεδομένων

Εντάξει, αυτό θα σας φανεί χρήσιμο. Ας μάθουμε πού βρίσκεται το buffer Tx. Για να το κάνετε αυτό, διαβάστε το "SPI Data Register":

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Bits 15:0 DR[15:0] Καταχωρητής δεδομένων
Δεδομένα που λαμβάνονται ή δεδομένα προς μετάδοση.
Ο καταχωρητής δεδομένων χωρίζεται σε δύο buffer - ένα για εγγραφή (buffer μετάδοσης) και ένα για ανάγνωση (receive buffer). Η εγγραφή στον καταχωρητή δεδομένων εγγράφει στην προσωρινή μνήμη Tx και η ανάγνωση από τον καταχωρητή δεδομένων θα επιστρέψει την τιμή που περιέχεται στην προσωρινή μνήμη Rx.

Λοιπόν, και το μητρώο κατάστασης, όπου βρίσκονται οι σημαίες TXE και BSY:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Γράφουμε:

#define _SPI_DR  0x0C
#define _SPI_SR  0x08

#define BSY         0x0080
#define TXE         0x0002

void dm_shift16(uint16_t value)
{
    _SPI2_(_SPI_DR) = value; //send 2 bytes
    while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}

Λοιπόν, αφού πρέπει να μεταδώσουμε 16 φορές δύο byte, σύμφωνα με τον αριθμό των εξόδων του προγράμματος οδήγησης LED, κάπως έτσι:

void sendLEDdata()
{
    LAT_low();
    uint8_t k = 16;
    do
    {   k--;
        dm_shift16(leds[k]);
    } while (k);

    while (_SPI2_(_SPI_SR) & BSY); // finish transmission

    LAT_pulse();
}

Αλλά δεν ξέρουμε ακόμα πώς να τραβήξουμε τον ακροδέκτη LAT, οπότε θα επιστρέψουμε στο I/O.

Εκχώρηση ακίδων

Στο STM32F1, οι καταχωρητές που είναι υπεύθυνοι για την κατάσταση των ακίδων είναι αρκετά ασυνήθιστοι. Είναι σαφές ότι υπάρχουν περισσότερα από αυτά από το Atmega, αλλά είναι επίσης διαφορετικά από άλλα τσιπ STM. Ενότητα 9.1 Γενική περιγραφή του GPIO:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Κάθε μία από τις θύρες I/O γενικής χρήσης (GPIO) έχει δύο καταχωρητές διαμόρφωσης 32 bit (GPIOx_CRL και GPIOx_CRH), δύο καταχωρητές δεδομένων 32 bit (GPIOx_IDR και GPIOx_ODR), έναν καταχωρητή set/reset 32 ​​bit (GPIOx_BSRR), έναν καταχωρητή επαναφοράς 16 bit (GPIOx_BRR) και έναν καταχωρητής αποκλεισμού bit (GPIOx_LCKR).

Οι δύο πρώτοι καταχωρητές είναι ασυνήθιστοι, και επίσης αρκετά άβολοι, επειδή οι 16 ακίδες θυρών είναι διάσπαρτες σε αυτούς σε μορφή «τέσσερα bit ανά αδελφό». Εκείνοι. Οι ακίδες μηδέν έως επτά βρίσκονται σε CRL και οι υπόλοιπες είναι σε CRH. Ταυτόχρονα, οι υπόλοιποι καταχωρητές περιέχουν επιτυχώς τα bit όλων των ακίδων της θύρας - συχνά παραμένοντας κατά το ήμισυ "δεσμευμένα".

Για απλότητα, ας ξεκινήσουμε από το τέλος της λίστας.

Δεν χρειαζόμαστε μητρώο αποκλεισμού.

Οι καταχωρητές ρύθμισης και επαναφοράς είναι αρκετά αστείοι καθώς αντιγράφουν εν μέρει ο ένας τον άλλον: μπορείτε να γράψετε τα πάντα μόνο στο BSRR, όπου τα υψηλότερα 16 bit θα μηδενίσουν τον ακροδέκτη και τα χαμηλότερα θα οριστούν στο 1 ή μπορείτε επίσης χρησιμοποιήστε το BRR, τα χαμηλότερα 16 bit εκ των οποίων επαναφέρουν μόνο τον ακροδέκτη . Μου αρέσει η δεύτερη επιλογή. Αυτοί οι καταχωρητές είναι σημαντικοί επειδή παρέχουν ατομική πρόσβαση στις ακίδες:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Ατομική ρύθμιση ή επαναφορά
Δεν χρειάζεται να απενεργοποιήσετε τις διακοπές κατά τον προγραμματισμό του GPIOx_ODR σε επίπεδο bit: ένα ή περισσότερα bit μπορούν να αλλάξουν με μία λειτουργία ατομικής εγγραφής APB2. Αυτό επιτυγχάνεται γράφοντας ένα "1" στον καταχωρητή set/reset (GPIOx_BSRR ή, μόνο για επαναφορά, GPIOx_BRR) του bit που πρέπει να αλλάξει. Τα άλλα bits θα παραμείνουν αμετάβλητα.

Οι καταχωρητές δεδομένων έχουν αρκετά αυτονόητα ονόματα - IDR = Εισαγωγή Μητρώο κατεύθυνσης, μητρώο εισόδου. ODR = Παραγωγή Καταχωρητής κατεύθυνσης, καταχωρητής εξόδου. Δεν θα τους χρειαστούμε στο τρέχον έργο.

Και τέλος, καταχωρήσεις ελέγχου. Δεδομένου ότι μας ενδιαφέρουν οι δεύτεροι ακροδέκτες SPI, δηλαδή PB13, PB14 και PB15, εξετάζουμε αμέσως το CRH:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Και βλέπουμε ότι θα χρειαστεί να γράψουμε κάτι σε bit από 20 έως 31.

Έχουμε ήδη καταλάβει παραπάνω τι θέλουμε από τις ακίδες, οπότε εδώ θα κάνω χωρίς στιγμιότυπο οθόνης, απλά θα πω ότι το MODE καθορίζει την κατεύθυνση (είσοδος αν και τα δύο bit έχουν ρυθμιστεί στο 0) και την ταχύτητα καρφίτσας (χρειαζόμαστε 50 MHz, π.χ. Και οι δύο ακίδες στο "1"), και το CNF ορίζει τη λειτουργία: κανονικό "push-pull" – 00, "alternative" - ​​10. Από προεπιλογή, όπως βλέπουμε παραπάνω, όλες οι ακίδες έχουν το τρίτο bit από το κάτω μέρος (CNF0). τους θέτει σε λειτουργία αιωρούμενη είσοδος.

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

Κάπως έτσι

#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000

#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000

Οι ακίδες μας βρίσκονται στη θύρα Β (διεύθυνση βάσης – 0x40010C00), κωδικός:

#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))

#define _BRR  0x14
#define _BSRR 0x10
#define _CRL  0x00
#define _CRH  0x04

//используем стандартный SPI2: MOSI на B15, CLK на B13
//LAT пусть будет на неиспользуемом MISO – B14

//очищаем дефолтный бит, он нам точно не нужен
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);

//альтернативные функции для MOSI и SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;

//50 МГц, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;

Και, κατά συνέπεια, μπορείτε να γράψετε ορισμούς για LAT, οι οποίοι θα συσπώνται από τους καταχωρητές BRR και BSRR:

/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)

#define LAT_low() _PORTB_(_BRR) = (1<<14)

(LAT_low μόνο από αδράνεια, ήταν πάντα έτσι, ας μείνει)

Τώρα όλα είναι υπέροχα, αλλά δεν λειτουργεί. Επειδή αυτό είναι STM32, εξοικονομούν ηλεκτρική ενέργεια, πράγμα που σημαίνει ότι πρέπει να ενεργοποιήσετε το ρολόι των απαιτούμενων περιφερειακών.

Ενεργοποιήστε το ρολόι

Το ρολόι, γνωστό και ως Ρολόι, είναι υπεύθυνο για το ρολόι. Και μπορούσαμε ήδη να παρατηρήσουμε τη συντομογραφία RCC. Το αναζητούμε στην τεκμηρίωση: αυτό είναι το Reset and Clock Control.

Όπως ειπώθηκε και παραπάνω, ευτυχώς, το πιο δύσκολο κομμάτι του θέματος του χρονισμού έγινε για εμάς από άτομα του STM, για το οποίο τους ευχαριστούμε πολύ (για άλλη μια φορά θα δώσω έναν σύνδεσμο για Ο ιστότοπος του Ντι Χαλτ, για να καταστεί σαφές πόσο μπερδεμένο είναι). Χρειαζόμαστε μόνο καταχωρητές που είναι υπεύθυνοι για την ενεργοποίηση του περιφερειακού χρονισμού (Peripheral Clock Enable Registers). Αρχικά, ας βρούμε τη διεύθυνση βάσης του RCC, βρίσκεται στην αρχή του "Χάρτης μνήμης":

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))

Και, στη συνέχεια, κάντε κλικ στον σύνδεσμο όπου προσπαθείτε να βρείτε κάτι στο πιάτο ή, πολύ καλύτερα, διαβάστε τις περιγραφές των μητρώων ενεργοποίησης από τις ενότητες σχετικά με ενεργοποιήστε τους καταχωρητές. Πού θα βρούμε τα RCC_APB1ENR και RCC_APB2ENR:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Και, κατά συνέπεια, περιέχουν bit που περιλαμβάνουν το χρονισμό του SPI2, IOPB (I/O Port B) και εναλλακτικές λειτουργίες (AFIO).

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001

//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;

//включаем  тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;

Μπορείτε να βρείτε τον τελικό κωδικό εδώ.

Εάν έχετε την ευκαιρία και την επιθυμία να δοκιμάσετε, συνδέστε το DM634 ως εξής: DAI στο PB15, DCK στο PB13, LAT στο PB14. Τροφοδοτούμε τον οδηγό από 5 βολτ, μην ξεχάσετε να συνδέσετε τη γείωση.

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

STM8 PWM

PWM στο STM8

Όταν μόλις σχεδίαζα αυτό το άρθρο, αποφάσισα, για παράδειγμα, να προσπαθήσω να κατακτήσω κάποια λειτουργικότητα ενός άγνωστου τσιπ χρησιμοποιώντας μόνο ένα φύλλο δεδομένων, έτσι ώστε να μην καταλήξω σε έναν τσαγκάρη χωρίς μπότες. Το STM8 ήταν ιδανικό για αυτόν τον ρόλο: πρώτον, είχα μερικές κινεζικές πλακέτες με το STM8S103 και, δεύτερον, δεν είναι πολύ δημοφιλές και επομένως ο πειρασμός να διαβάσετε και να βρείτε μια λύση στο Διαδίκτυο βασίζεται στην έλλειψη αυτών των λύσεων.

Το τσιπ έχει επίσης φύλλο δεδομένων и εγχειρίδιο αναφοράς RM0016, στο πρώτο υπάρχουν διευθύνσεις pinout και μητρώου, στο δεύτερο - όλα τα άλλα. Το STM8 είναι προγραμματισμένο σε C σε τρομερό IDE ST Visual Develop.

Clocking και I/O

Από προεπιλογή, το STM8 λειτουργεί σε συχνότητα 2 MHz, αυτό πρέπει να διορθωθεί αμέσως.

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Ρολόι HSI (High Speed ​​Internal).
Το σήμα ρολογιού HSI προέρχεται από έναν εσωτερικό ταλαντωτή RC 16 MHz με προγραμματιζόμενο διαιρέτη (1 έως 8). Ρυθμίζεται στον καταχωρητή διαιρέτη ρολογιού (CLK_CKDIVR).
Σημείωση: στην αρχή, ένας ταλαντωτής HSI RC με διαιρέτη 8 επιλέγεται ως η κύρια πηγή του σήματος ρολογιού.

Βρίσκουμε τη διεύθυνση μητρώου στο φύλλο δεδομένων, την περιγραφή στο refman και βλέπουμε ότι το μητρώο πρέπει να διαγραφεί:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Εφόσον πρόκειται να εκτελέσουμε PWM και να συνδέσουμε τα LED, ας δούμε το pinout:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Το τσιπ είναι μικρό, πολλές λειτουργίες αναστέλλονται στις ίδιες ακίδες. Αυτό που βρίσκεται σε αγκύλες είναι "εναλλακτική λειτουργικότητα", αλλάζει με "byte επιλογής" (byte επιλογής) – κάτι σαν ασφάλειες Atmega. Μπορείτε να αλλάξετε τις τιμές τους μέσω προγραμματισμού, αλλά δεν είναι απαραίτητο, γιατί Η νέα λειτουργία ενεργοποιείται μόνο μετά από επανεκκίνηση. Είναι πιο εύκολο να χρησιμοποιήσετε το ST Visual Programmer (που έχει ληφθεί με το Visual Develop), το οποίο μπορεί να αλλάξει αυτά τα byte. Το pinout δείχνει ότι οι ακίδες CH1 και CH2 του πρώτου χρονοδιακόπτη είναι κρυμμένες σε αγκύλες. είναι απαραίτητο να ρυθμίσετε τα bit AFR1 και AFR0 στο STVP και το δεύτερο θα μεταφέρει επίσης την έξοδο CH1 του δεύτερου χρονοδιακόπτη από το PD4 στο PC5.

Έτσι, 6 ακίδες θα ελέγχουν τα LED: PC6, PC7 και PC3 για το πρώτο χρονόμετρο, PC5, PD3 και PA3 για το δεύτερο.

Η ρύθμιση των ίδιων των ακίδων I/O στο STM8 είναι απλούστερη και πιο λογική από ό,τι στο STM32:

  • εξοικειωμένος από τον καταχωρητή κατεύθυνσης δεδομένων Atmega DDR (Μητρώο Κατεύθυνσης Δεδομένων): 1 = έξοδος;
  • ο πρώτος καταχωρητής ελέγχου CR1, όταν βγαίνει, ρυθμίζει τη λειτουργία ώθησης-έλξης (1) ή ανοιχτή αποστράγγιση (0). αφού συνδέω τα LED στο τσιπ με καθόδους, αφήνω μηδενικά εδώ?
  • ο δεύτερος καταχωρητής ελέγχου CR2, όταν βγαίνει, ορίζει την ταχύτητα ρολογιού: 1 = 10 MHz

#define PA_DDR     *(volatile uint8_t *)0x005002
#define PA_CR2     *(volatile uint8_t *)0x005004
#define PD_DDR     *(volatile uint8_t *)0x005011
#define PD_CR2     *(volatile uint8_t *)0x005013
#define PC_DDR     *(volatile uint8_t *)0x00500C
#define PC_CR2     *(volatile uint8_t *)0x00500E

PA_DDR = (1<<3); //output
PA_CR2 |= (1<<3); //fast
PD_DDR = (1<<3); //output
PD_CR2 |= (1<<3); //fast
PC_DDR = ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //output
PC_CR2 |= ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //fast

Ρύθμιση PWM

Αρχικά, ας ορίσουμε τους όρους:

  • Συχνότητα PWM – συχνότητα με την οποία χτυπά ο χρονοδιακόπτης.
  • Αυτόματη επαναφόρτωση, AR – τιμή αυτόματης φόρτωσης μέχρι την οποία θα μετράει ο χρονοδιακόπτης (περίοδος παλμού).
  • Ενημέρωση εκδήλωσης, UEV – ένα συμβάν που συμβαίνει όταν ο χρονοδιακόπτης έχει μετρήσει σε AR.
  • Κύκλος λειτουργίας PWM – Κύκλος λειτουργίας PWM, που συχνά ονομάζεται «συντελεστής εργασίας».
  • Λήψη/Σύγκριση τιμής – τιμή για σύλληψη/σύγκριση, στην οποία έχει μετρήσει το χρονόμετρο θα κάνει κάτι (στην περίπτωση του PWM, αντιστρέφει το σήμα εξόδου).
  • Τιμή προφόρτωσης – προφορτωμένη τιμή. Συγκρίνετε αξία δεν μπορεί να αλλάξει ενώ χτυπάει ο χρονοδιακόπτης, διαφορετικά ο κύκλος PWM θα σπάσει. Επομένως, οι νέες μεταδιδόμενες τιμές τοποθετούνται σε μια προσωρινή μνήμη και αφαιρούνται όταν ο χρονοδιακόπτης φτάσει στο τέλος της αντίστροφης μέτρησης και μηδενιστεί.
  • Ευθυγραμμισμένο με τις άκρες и Λειτουργίες στοίχισης στο κέντρο – ευθυγράμμιση κατά μήκος των συνόρων και στο κέντρο, ίδια με του Atmel Γρήγορη PWM и PWM σωστής φάσης.
  • OCiREF, Σήμα αναφοράς σύγκρισης εξόδου – το σήμα εξόδου αναφοράς, στην πραγματικότητα, αυτό που εμφανίζεται στην αντίστοιχη ακίδα στη λειτουργία PWM.

Όπως είναι ήδη σαφές από το pinout, δύο χρονόμετρα έχουν δυνατότητες PWM – το πρώτο και το δεύτερο. Και τα δύο είναι 16-bit, το πρώτο έχει πολλά πρόσθετα χαρακτηριστικά (συγκεκριμένα, μπορεί να μετρήσει και πάνω και κάτω). Χρειαζόμαστε και τα δύο για να δουλεύουμε εξίσου, γι' αυτό αποφάσισα να ξεκινήσω με το φανερά φτωχότερο δεύτερο, για να μην χρησιμοποιήσω κατά λάθος κάτι που δεν υπάρχει. Κάποιο πρόβλημα είναι ότι η περιγραφή της λειτουργίας PWM όλων των χρονομέτρων στο εγχειρίδιο αναφοράς βρίσκεται στο κεφάλαιο σχετικά με τον πρώτο χρονοδιακόπτη (17.5.7 Λειτουργία PWM), επομένως πρέπει να πηδάτε εμπρός και πίσω σε όλο το έγγραφο όλη την ώρα.

Το PWM στο STM8 έχει ένα σημαντικό πλεονέκτημα έναντι του PWM στο Atmega:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
PWM ευθυγραμμισμένο με όρια
Διαμόρφωση λογαριασμού από κάτω προς τα πάνω
Η μέτρηση από κάτω προς τα πάνω είναι ενεργή εάν διαγραφεί το bit DIR στον καταχωρητή TIM_CR1
Παράδειγμα
Το παράδειγμα χρησιμοποιεί την πρώτη λειτουργία PWM. Το σήμα αναφοράς PWM OCiREF διατηρείται ψηλά όσο TIM1_CNT < TIM1_CCRi. Διαφορετικά παίρνει χαμηλό επίπεδο. Εάν η τιμή σύγκρισης στον καταχωρητή TIM1_CCRi είναι μεγαλύτερη από την τιμή αυτόματης φόρτωσης (καταχωρητής TIM1_ARR), το σήμα OCiREF διατηρείται στο 1. Εάν η τιμή σύγκρισης είναι 0, το OCiREF διατηρείται στο μηδέν....

Χρονοδιακόπτης STM8 κατά τη διάρκεια εκδήλωση ενημέρωσης ελέγχους πρώτα συγκρίνετε την αξία, και μόνο τότε παράγει ένα σήμα αναφοράς. Το χρονόμετρο του Atmega πρώτα βιδώνει και μετά συγκρίνει, με αποτέλεσμα compare value == 0 η έξοδος είναι μια βελόνα, η οποία πρέπει να αντιμετωπιστεί με κάποιο τρόπο (για παράδειγμα, με προγραμματική αντιστροφή της λογικής).

Λοιπόν, τι θέλουμε να κάνουμε: 8-bit PWM (AR == 255), μετρώντας από κάτω προς τα πάνω, ευθυγράμμιση κατά μήκος του περιγράμματος. Δεδομένου ότι οι λαμπτήρες συνδέονται με το τσιπ με καθόδους, το PWM θα πρέπει να δίνει 0 (LED αναμμένο) μέχρι συγκρίνετε την αξία και 1 μετά.

Έχουμε ήδη διαβάσει για μερικά Λειτουργία PWM, οπότε βρίσκουμε τον απαιτούμενο καταχωρητή του δεύτερου χρονοδιακόπτη αναζητώντας στο εγχειρίδιο αναφοράς αυτή τη φράση (18.6.8 - TIMx_CCMR1):

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
110: Πρώτη λειτουργία PWM – όταν μετράτε από κάτω προς τα πάνω, το πρώτο κανάλι είναι ενεργό ενώ TIMx_CNT < TIMx_CCR1. Διαφορετικά, το πρώτο κανάλι είναι ανενεργό. [Περαιτέρω στο έγγραφο υπάρχει μια λανθασμένη αντιγραφή-επικόλληση από το χρονόμετρο 1] 111: Δεύτερη λειτουργία PWM – κατά τη μέτρηση από κάτω προς τα πάνω, το πρώτο κανάλι είναι ανενεργό ενώ το TIMx_CNT < TIMx_CCR1. Διαφορετικά, το πρώτο κανάλι είναι ενεργό.

Δεδομένου ότι τα LED συνδέονται με το MK με καθόδους, η δεύτερη λειτουργία μας ταιριάζει (και η πρώτη, αλλά δεν το γνωρίζουμε ακόμα).

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Bit 3 OC1PE: Ενεργοποίηση προφόρτωσης pin 1
0: Ο καταχωρητής προφόρτωσης στο TIMx_CCR1 είναι απενεργοποιημένος. Μπορείτε να γράψετε στο TIMx_CCR1 ανά πάσα στιγμή. Η νέα αξία λειτουργεί αμέσως.
1: Ο καταχωρητής προφόρτωσης στο TIMx_CCR1 είναι ενεργοποιημένος. Οι λειτουργίες ανάγνωσης/εγγραφής έχουν πρόσβαση στον καταχωρητή προφόρτισης. Η προφορτωμένη τιμή TIMx_CCR1 φορτώνεται στον σκιώδη καταχωρητή κατά τη διάρκεια κάθε συμβάντος ενημέρωσης.
*Σημείωση: Για να λειτουργεί σωστά η λειτουργία PWM, πρέπει να είναι ενεργοποιημένοι οι καταχωρητές προφόρτισης. Αυτό δεν είναι απαραίτητο στη λειτουργία ενός σήματος (το bit OPM έχει ρυθμιστεί στον καταχωρητή TIMx_CR1).

Εντάξει, ας ενεργοποιήσουμε όλα όσα χρειαζόμαστε για τα τρία κανάλια του δεύτερου χρονοδιακόπτη:

#define TIM2_CCMR1 *(volatile uint8_t *)0x005307
#define TIM2_CCMR2 *(volatile uint8_t *)0x005308
#define TIM2_CCMR3 *(volatile uint8_t *)0x005309

#define PWM_MODE2   0x70 //PWM mode 2, 0b01110000
#define OCxPE       0x08 //preload enable

TIM2_CCMR1 = (PWM_MODE2 | OCxPE);
TIM2_CCMR2 = (PWM_MODE2 | OCxPE);
TIM2_CCMR3 = (PWM_MODE2 | OCxPE);

Το AR αποτελείται από δύο καταχωρητές οκτώ bit, όλα είναι απλά:

#define TIM2_ARRH  *(volatile uint8_t *)0x00530F
#define TIM2_ARRL  *(volatile uint8_t *)0x005310

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Το δεύτερο χρονόμετρο μπορεί να μετρήσει μόνο από κάτω προς τα πάνω, ευθυγράμμιση κατά μήκος του περιγράμματος, τίποτα δεν χρειάζεται να αλλάξει. Ας ορίσουμε τον διαιρέτη συχνότητας, για παράδειγμα, στο 256. Για το δεύτερο χρονόμετρο, ο διαιρέτης τίθεται στον καταχωρητή TIM2_PSCR και είναι ισχύς δύο:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Το μόνο που μένει είναι να ενεργοποιήσουμε τα συμπεράσματα και το ίδιο το δεύτερο χρονόμετρο. Το πρώτο πρόβλημα λύνεται με καταχωρητές Λήψη/Σύγκριση Ενεργοποίηση: υπάρχουν δύο, τρία κανάλια διάσπαρτα σε αυτά ασύμμετρα. Εδώ μπορούμε επίσης να μάθουμε ότι είναι δυνατή η αλλαγή της πολικότητας του σήματος, δηλ. κατ 'αρχήν, ήταν δυνατή η χρήση PWM Mode 1. Γράφουμε:

#define TIM2_CCER1 *(volatile uint8_t *)0x00530A
#define TIM2_CCER2 *(volatile uint8_t *)0x00530B

#define CC1E  (1<<0) // CCER1
#define CC2E  (1<<4) // CCER1
#define CC3E  (1<<0) // CCER2

TIM2_CCER1 = (CC1E | CC2E);
TIM2_CCER2 = CC3E;

Και τέλος, ξεκινάμε το χρονόμετρο στον καταχωρητή TIMx_CR1:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Ας γράψουμε ένα απλό ανάλογο του AnalogWrite(), το οποίο θα μεταφέρει τις πραγματικές τιμές στο χρονόμετρο για σύγκριση. Τα μητρώα ονομάζονται προβλέψιμα Καταγραφή/Σύγκριση καταχωρητών, υπάρχουν δύο από αυτά για κάθε κανάλι: τα 8 bit χαμηλής τάξης στο TIM2_CCRxL και τα υψηλής τάξης στο TIM2_CCRxH. Εφόσον έχουμε δημιουργήσει ένα PWM 8-bit, αρκεί να γράψουμε μόνο τα λιγότερο σημαντικά bit:

#define TIM2_CCR1L *(volatile uint8_t *)0x005312
#define TIM2_CCR2L *(volatile uint8_t *)0x005314
#define TIM2_CCR3L *(volatile uint8_t *)0x005316

void setRGBled(uint8_t r, uint8_t g, uint8_t b)
{
    TIM2_CCR1L = r;
    TIM2_CCR2L = g;
    TIM2_CCR3L = b;
}

Ο προσεκτικός αναγνώστης θα παρατηρήσει ότι έχουμε ένα ελαφρώς ελαττωματικό PWM, που δεν μπορεί να παράγει 100% γέμισμα (σε μέγιστη τιμή 255, το σήμα αναστρέφεται για έναν κύκλο χρονοδιακόπτη). Για τα LED αυτό δεν έχει σημασία και ο προσεκτικός αναγνώστης μπορεί ήδη να μαντέψει πώς να το διορθώσει.

Το PWM στο δεύτερο χρονόμετρο λειτουργεί, ας περάσουμε στο πρώτο.

Ο πρώτος χρονοδιακόπτης έχει ακριβώς τα ίδια bit στους ίδιους καταχωρητές (απλώς αυτά τα bit που παρέμειναν "δεσμευμένα" στο δεύτερο χρονόμετρο χρησιμοποιούνται ενεργά στον πρώτο για κάθε είδους προηγμένα πράγματα). Επομένως, αρκεί να βρείτε τις διευθύνσεις των ίδιων μητρώων στο φύλλο δεδομένων και να αντιγράψετε τον κωδικό. Λοιπόν, αλλάξτε την τιμή του διαιρέτη συχνότητας, γιατί... το πρώτο χρονόμετρο θέλει να λάβει όχι ισχύ δύο, αλλά ακριβή τιμή 16 bit σε δύο καταχωρητές Prescaler High и Χαμηλός. Κάνουμε τα πάντα και... το πρώτο χρονόμετρο δεν λειτουργεί. Τι συμβαίνει?

Το πρόβλημα μπορεί να λυθεί μόνο κοιτάζοντας ολόκληρη την ενότητα σχετικά με τους καταχωρητές ελέγχου του χρονοδιακόπτη 1, όπου αναζητούμε αυτόν που δεν έχει το δεύτερο χρονόμετρο. Θα είναι 17.7.30 Μητρώο διακοπής (TIM1_BKR), όπου υπάρχει αυτό το bit:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Ενεργοποίηση κύριας εξόδου

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Αυτό είναι σίγουρο τώρα, ο κωδικός εκεί.

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

STM8 Multiplex

Πολυπλεξία σε STM8

Το τρίτο μίνι έργο είναι να συνδέσετε οκτώ RGB LED στο δεύτερο χρονόμετρο σε λειτουργία PWM και να τα κάνετε να δείχνουν διαφορετικά χρώματα. Βασίζεται στην έννοια της πολυπλεξίας LED, η οποία είναι ότι αν ανάψετε και απενεργοποιήσετε τα LED πολύ, πολύ γρήγορα, θα μας φανεί ότι είναι συνεχώς αναμμένα (επιμονή της όρασης, αδράνεια οπτικής αντίληψης). Κάποτε το έκανα κάτι τέτοιο στο Arduino.

Ο αλγόριθμος εργασίας μοιάζει με αυτό:

  • Συνέδεσε την άνοδο του πρώτου LED RGB.
  • το άναψε, στέλνοντας τα απαραίτητα σήματα στις κάθοδοι.
  • περίμενε μέχρι το τέλος του κύκλου PWM.
  • συνδέθηκε η άνοδος του δεύτερου LED RGB.
  • Άναψέ το...

Λοιπόν, κλπ. Φυσικά, για όμορφη λειτουργία απαιτείται να είναι συνδεδεμένη η άνοδος και ταυτόχρονα να «αναφλέγεται» το LED. Λοιπόν, ή σχεδόν. Σε κάθε περίπτωση, πρέπει να γράψουμε έναν κωδικό που θα εξάγει τιμές σε τρία κανάλια του δεύτερου χρονοδιακόπτη, θα τις αλλάζει όταν φτάσει το UEV και ταυτόχρονα θα αλλάζει το τρέχον ενεργό LED RGB.

Δεδομένου ότι η εναλλαγή LED είναι αυτόματη, πρέπει να δημιουργήσουμε μια "μνήμη βίντεο" από την οποία θα λαμβάνει δεδομένα ο χειριστής διακοπών. Αυτός είναι ένας απλός πίνακας:

uint8_t colors[8][3];

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

uint8_t cnt;

Demux

Για σωστή πολυπλεξία, χρειαζόμαστε, παραδόξως, έναν αποπολυπλέκτη CD74HC238. Αποπολυπλέκτης - ένα τσιπ που υλοποιεί τον χειριστή στο υλικό <<. Μέσω τριών ακίδων εισόδου (bit 0, 1 και 2) του τροφοδοτούμε έναν αριθμό τριών bit X και σε απόκριση ενεργοποιεί τον αριθμό εξόδου (1<<X). Οι υπόλοιπες είσοδοι του τσιπ χρησιμοποιούνται για την κλιμάκωση ολόκληρου του σχεδίου. Χρειαζόμαστε αυτό το τσιπ όχι μόνο για να μειώσουμε τον αριθμό των κατειλημμένων ακίδων του μικροελεγκτή, αλλά και για ασφάλεια - ώστε να μην ανάψουμε κατά λάθος περισσότερα LED από ό,τι είναι δυνατόν και να μην καεί το MK. Το τσιπ κοστίζει μια δεκάρα και πρέπει πάντα να φυλάσσεται στο ντουλάπι φαρμάκων του σπιτιού σας.

Το CD74HC238 μας θα είναι υπεύθυνο για την παροχή τάσης στην άνοδο του επιθυμητού LED. Σε ένα πλήρες πολυπλέκτη, θα τροφοδοτούσε τάση στη στήλη μέσω ενός P-MOSFET, αλλά σε αυτήν την επίδειξη είναι δυνατό απευθείας, επειδή αντλεί 20 mA, σύμφωνα με απόλυτες μέγιστες βαθμολογίες στο φύλλο δεδομένων. Από Φύλλο δεδομένων CD74HC238 χρειαζόμαστε pinouts και αυτό το cheat sheet:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
H = στάθμη υψηλής τάσης, L = χαμηλή στάθμη τάσης, X – μην σε νοιάζει

Συνδέουμε E2 και E1 στη γείωση, E3, A0, A1 και A3 στις ακίδες PD5, PC3, PC4 και PC5 του STM8. Δεδομένου ότι ο παραπάνω πίνακας περιέχει τόσο χαμηλά όσο και υψηλά επίπεδα, διαμορφώνουμε αυτές τις ακίδες ως ακίδες push-pull.

PWM

Το PWM στο δεύτερο χρονόμετρο έχει ρυθμιστεί με τον ίδιο τρόπο όπως στην προηγούμενη ιστορία, με δύο διαφορές:

Αρχικά, πρέπει να ενεργοποιήσουμε τη διακοπή Ενημέρωση εκδήλωσης (UEV) που θα καλέσει μια συνάρτηση που εναλλάσσει το ενεργό LED. Αυτό γίνεται αλλάζοντας το bit Ενημέρωση διακοπής Ενεργοποίηση σε ένα μητρώο με ένα ενδεικτικό όνομα

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
Εγγραφή ενεργοποίησης διακοπής

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Η δεύτερη διαφορά σχετίζεται με το φαινόμενο της πολυπλεξίας, όπως π.χ ghosting – παρασιτική λάμψη διόδων. Στην περίπτωσή μας, μπορεί να εμφανιστεί λόγω του γεγονότος ότι ο χρονοδιακόπτης, έχοντας προκαλέσει διακοπή στο UEV, συνεχίζει να χτυπά και ο χειριστής διακοπής δεν έχει χρόνο να αλλάξει το LED προτού το χρονόμετρο αρχίσει να γράφει κάτι στις ακίδες. Για να το καταπολεμήσετε αυτό, θα πρέπει να αντιστρέψετε τη λογική (0 = μέγιστη φωτεινότητα, 255 = τίποτα δεν είναι αναμμένο) και να αποφύγετε τις ακραίες τιμές κύκλου λειτουργίας. Εκείνοι. βεβαιωθείτε ότι μετά το UEV τα LED σβήνουν εντελώς για έναν κύκλο PWM.

Αλλαγή πολικότητας:

//set polarity 
    TIM2_CCER1 |= (CC1P | CC2P);
    TIM2_CCER2 |= CC3P;

Αποφύγετε τη ρύθμιση των r, g και b στο 255 και θυμηθείτε να τα αντιστρέψετε όταν τα χρησιμοποιείτε.

Διακόπτει

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

Όταν δημιουργήσαμε για πρώτη φορά ένα έργο στο ST Visual Develop, επιπλέον main.c λάβαμε ένα παράθυρο με ένα μυστηριώδες αρχείο stm8_interrupt_vector.c, συμπεριλαμβάνεται αυτόματα στο έργο. Σε αυτό το αρχείο, εκχωρείται μια συνάρτηση σε κάθε διακοπή NonHandledInterrupt. Πρέπει να δεσμεύσουμε τη συνάρτησή μας στην επιθυμητή διακοπή.

Το φύλλο δεδομένων έχει έναν πίνακα διανυσμάτων διακοπής, όπου βρίσκουμε αυτά που χρειαζόμαστε:

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8
13 Ενημέρωση/υπερχείλιση TIM2
14 Λήψη/σύγκριση TIM2

Πρέπει να αλλάξουμε το LED στο UEV, επομένως χρειαζόμαστε διακοπή #13.

Αντίστοιχα, πρώτον, στο φάκελο stm8_interrupt_vector.c αλλάξτε το προεπιλεγμένο όνομα της συνάρτησης που είναι υπεύθυνη για τη διακοπή Νο. 13 (IRQ13) στο δικό σας:

{0x82, TIM2_Overflow}, /* irq13 */

Δεύτερον, θα πρέπει να δημιουργήσουμε ένα αρχείο main.h με το ακόλουθο περιεχόμενο:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void TIM2_Overflow (void);
#endif

Και τέλος, γράψτε αυτή τη συνάρτηση στο δικό σας main.c:

@far @interrupt void TIM2_Overflow (void)
{
    PD_ODR &= ~(1<<5); // вырубаем демультиплексор
    PC_ODR = (cnt<<3); // записываем в демультиплексор новое значение
    PD_ODR |= (1<<5); // включаем демультиплексор

    TIM2_SR1 = 0; // сбрасываем флаг Update Interrupt Pending

    cnt++; 
    cnt &= 7; // двигаем счетчик LED

    TIM2_CCR1L = ~colors[cnt][0]; // передаем в буфер инвертированные значения
    TIM2_CCR2L = ~colors[cnt][1]; // для следующего цикла ШИМ
    TIM2_CCR3L = ~colors[cnt][2]; // 

    return;
}

Το μόνο που μένει είναι να ενεργοποιηθούν οι διακοπές. Αυτό γίνεται χρησιμοποιώντας την εντολή assembler rim - θα πρέπει να το ψάξεις Εγχειρίδιο προγραμματισμού:

//enable interrupts
_asm("rim");

Μια άλλη εντολή assembler είναι sim – απενεργοποιεί τις διακοπές. Πρέπει να απενεργοποιηθούν κατά την εγγραφή νέων τιμών στη «μνήμη βίντεο», έτσι ώστε μια διακοπή που προκαλείται σε λάθος στιγμή να μην χαλάσει τη συστοιχία.

Όλος ο κωδικός - στο GitHub.

Διαβάστε τα φύλλα δεδομένων 2: SPI στο STM32. PWM, χρονόμετρα και διακοπές στο STM8

Αν τουλάχιστον κάποιος βρίσκει αυτό το άρθρο χρήσιμο, τότε δεν το έγραψα μάταια. Θα χαρώ να λάβω σχόλια και παρατηρήσεις, θα προσπαθήσω να απαντήσω σε όλα.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο