Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

В éischten Deel Ech hu probéiert Hobby Elektronik Ingenieuren ze soen, déi aus Arduino Hosen opgewuess sinn, wéi a firwat se Dateblieder an aner Dokumentatioun fir Mikrokontroller solle liesen. Den Text huet sech als grouss erausgestallt, also hunn ech versprach, praktesch Beispiller an engem separaten Artikel ze weisen. Gutt, hien huet sech selwer Mëllechpilz genannt ...

Haut wäert ech Iech weisen wéi d'Dateblieder benotze fir ganz einfach ze léisen, awer néideg fir vill Projeten, Aufgaben op STM32 (Blue Pill) a STM8 Controller. All Demo-Projete si fir meng Liiblings-LEDs gewidmet, mir beliichten se a grousse Quantitéiten, fir déi mir all Zorte vun interessanten Peripherieger benotze mussen.

Den Text huet sech erëm als enorm erausgestallt, also fir d'Bequemlechkeet maachen ech den Inhalt:

STM32 Blue Pëll: 16 LED mat DM634 Chauffer
STM8: Astelle sechs PWM Pins
STM8: 8 RGB LEDs op dräi Pins, ënnerbrach

Verzichterklärung: Ech sinn keen Ingenieur, ech maachen net wéi wann ech déif Wëssen an der Elektronik hunn, den Artikel ass fir Amateure wéi ech geduecht. Tatsächlech hunn ech mech virun zwee Joer als Zielpublikum ugesinn. Wann een mir dann gesot hätt, datt d'Dateblieder op engem onbekannten Chip net grujeleg wieren ze liesen, hätt ech net vill Zäit verbruecht fir e puer Coden um Internet ze sichen an d'Krüche mat enger Schéier a Klebeband ze erfannen.

De Fokus vun dësem Artikel ass op Dateblieder, net Projeten, sou datt de Code vläicht net ganz ordentlech an dacks knapp ass. D'Projete selwer sinn ganz einfach, obwuel gëeegent fir eng éischt Bekannten mat der neier Chip.

Ech hoffen, datt mäin Artikel een an enger ähnlecher Etapp vun Tauche am Hobby hëlleft.

STM32 Fotoen

16 LEDs mat DM634 an SPI

E klenge Projet benotzt Blue Pille (STM32F103C8T6) an DM634 LED Chauffer. Mat Hëllef vun Datenblieder wäerte mir de Chauffer erausfannen, STM IO Ports a SPI konfiguréieren.

DM634

Taiwanesesch Chip mat 16 16-Bit PWM Ausgänge, kann a Ketten verbonne ginn. Den Low-End 12-Bit Modell ass bekannt aus engem Hausprojet Lightpack. Zu enger Zäit, tëscht dem DM63x an dem bekannten TLC5940, hunn ech DM aus verschiddene Grënn gewielt: 1) TLC op Aliexpress ass definitiv gefälscht, awer dëst ass net; 2) DM huet en autonomen PWM mat sengem eegene Frequenzgenerator; 3) et kéint zu Moskau bëlleg kaaft ginn, anstatt e Pak vum Ali ze waarden. An natierlech war et interessant ze léieren wéi Dir den Chip selwer kontrolléiert, anstatt eng fäerdeg Bibliothéik ze benotzen. Chips ginn elo haaptsächlech am SSOP24 Package präsentéiert; si sinn einfach op en Adapter ze solderen.

Well den Hiersteller taiwanesesch ass, Datenblat den Chip ass op Chinesesch Englesch geschriwwen, dat heescht datt et Spaass gëtt. Als éischt kucke mir de Pinout (Pin Verbindung) fir ze verstoen mat wéi engem Been ze verbannen mat wat, an eng Beschreiwung vun de Pins (Pin Beschreiwung). 16 Pins:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
DC Sink Quellen (Open Drain)

Sink / Open-Drain Ausgang - Drain; Quell vun inflowing aktuell; den Ausgang ass mam Buedem am aktive Staat ugeschloss - d'LEDs si mam Chauffer duerch Kathoden verbonnen. Elektresch ass dëst natierlech keen "oppenen Drain" (oppen Drain), awer an Datenblieder gëtt dës Bezeechnung fir Pins am Drainmodus dacks fonnt.

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Extern Widderstänn tëscht REXT an GND fir den Ausgangsstroumwäert ze setzen

E Referenzresistor gëtt tëscht dem REXT Pin an dem Buedem installéiert, deen d'intern Resistenz vun den Ausgänge kontrolléiert, kuckt d'Grafik op Säit 9 vum Dateblat. Am DM634 kann dës Resistenz och vu Software kontrolléiert ginn, d'Gesamthellegkeet festleeën (global Hellegkeet); Ech ginn net an Detailer an dësem Artikel, ech setzen just en 2.2 - 3 kOhm Widderstand hei.

Fir ze verstoen wéi den Chip ze kontrolléieren, kucke mer d'Beschreiwung vum Apparat Interface:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

Jo, hei ass et, Chinesesch Englesch an all senger Herrlechkeet. Dës Iwwersetzung ass problematesch, Dir kënnt et verstoen wann Dir wëllt, awer et gëtt en anere Wee - kuckt wéi d'Verbindung mat der funktionell ähnlecher TLC5940 am Dateblat beschriwwe gëtt:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
... Nëmmen dräi Pins sinn néideg fir Daten an den Apparat anzeginn. De Rising Rand vum SCLK Signal verännert d'Donnéeë vum SIN Pin an den internen Register. Nodeems all Daten gelueden sinn, hält e kuerzen héije XLAT-Signal déi sequentiell transferéiert Daten an d'intern Registere. Intern Registere sinn Paarte ausgeléist vum XLAT Signalniveau. All Daten sinn bedeitendst bëssen éischt iwwerdroen.

Latch - gespaarten / gespaarten / gespaarten.
Rising Rand - Virsprong Rand vun der Pulsatiounsperiod
MSB éischt - bedeitendsten (lénks) bëssen no vir.
Donnéeën ze Auer - Daten sequenziell iwwerdroen (bit fir bëssen).

D 'Wuert Spär ass dacks an der Dokumentatioun fir Chips fonnt a gëtt op verschidde Manéieren iwwersat, also fir Verständnis wäert ech mir erlaben

e klenge pädagogesche ProgrammDen LED Chauffer ass wesentlech e Verréckelungsregister. "Shift" (Verréckelung) am Numm - bitwise Beweegung vun Daten am Apparat: all neie Bit, deen dobannen gedréckt gëtt, dréckt d'ganz Kette vir virun. Well kee chaotesch Blénken vun den LEDen während der Verréckelung wëll beobachten, fënnt de Prozess a Pufferregistre statt, getrennt vun den Aarbechtsregistere mat engem Dämpfer (Spär) ass eng Zort Waardezëmmer wou d'Bits an der gewënschter Sequenz arrangéiert sinn. Wann alles fäerdeg ass, mécht de Shutter op an d'Bits ginn op d'Aarbecht, ersetzt déi viregt Batch. Wuert Spär an der Dokumentatioun fir Mikrokreesser implizéiert bal ëmmer sou e Dämpfer, egal a wéi eng Kombinatioun et benotzt gëtt.

Also, Datenübertragung op den DM634 gëtt esou ausgeführt: Setzt den DAI-Input op de Wäert vum bedeitendsten Bit vun der wäiter LED, zitt DCK erop an erof; setzen den DAI Input op de Wäert vun der nächster bëssen, zéien DCK; an sou weider bis all Bits iwwerdroe sinn (ageklemmt), no deem mir LAT zéien. Dëst kann manuell gemaach ginn (bësschen), awer et ass besser eng SPI Interface ze benotzen, déi speziell dofir ugepasst ass, well se op eisem STM32 an zwee Exemplare presentéiert gëtt.

Blue Pille STM32F103

Aféierung: STM32 Controller si vill méi komplex wéi Atmega328 wéi se grujeleg kënne schéngen. Ausserdeem, aus Grënn vun Energiespueren, sinn bal all Peripheriegeräter am Ufank ausgeschalt, an d'Auerfrequenz ass 8 MHz vun der interner Quell. Glécklecherweis hunn STM Programméierer Code geschriwwen, deen den Chip op déi "berechent" 72 MHz bréngt, an d'Auteuren vun all den IDEen, déi ech weess, hunn et an der Initialiséierungsprozedur abegraff, also brauche mir net ze clocken (awer Dir kënnt wann Dir wierklech wëllt). Awer Dir musst d'Peripherieger ausschalten.

Dokumentatioun: Blue Pill ass mam populäre STM32F103C8T6 Chip ausgestatt, et ginn zwee nëtzlech Dokumenter dofir:

Am Informatiounsblat kënne mir interesséiert sinn:

  • Pinouts - Chip Pinouts - am Fall wou mir décidéieren d'Brieder selwer ze maachen;
  • Erënnerung Kaart - Erënnerung Kaart fir eng spezifesch Chip. De Referenzhandbuch huet eng Kaart fir déi ganz Linn, an et ernimmt Registere déi eis net hunn.
  • Pin Definitioun Dësch - Oplëschtung vun den Haapt- an alternativ Funktiounen vun Pins; fir déi "blo Pille" fannt Dir méi praktesch Biller um Internet mat enger Lëscht vu Pins an hir Funktiounen. Dofir google mir direkt Blue Pill pinout an halen dëst Bild bei der Hand:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
NB: et gouf e Feeler am Bild vum Internet, deen an de Kommentarer festgestallt gouf, Merci dofir. D'Bild ass ersat ginn, awer dëst ass eng Lektioun - et ass besser d'Informatioun net aus Datenblieder ze kontrolléieren.

Mir läschen den Dateblat, öffnen de Referenzhandbuch, a vun elo un benotze mir et nëmmen.
Prozedur: mir handelen mat Standard Input / Output, konfiguréieren SPI, schalten déi néideg Peripherieger.

Input Ausgang

Op der Atmega328 gëtt I/O extrem einfach implementéiert, dofir kann d'Heefegkeet vu STM32 Optiounen duerchernee sinn. Elo brauche mir nëmmen Conclusiounen, awer och dës hu véier Optiounen:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
offen Drain, Push-Pull, Alternativ Push-Pull, Alternativ Open Drain

"Pull-Push" (dréckt) ass déi üblech Ausgang vum Arduino, de Pin kann de Wäert entweder HIGH oder LOW huelen. Awer mat "oppenen Drain" ginn et Schwieregkeeten, obwuel eigentlech alles hei einfach ass:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Ausgangskonfiguratioun / wann den Hafen zum Ausgang zougewisen ass: / Ausgangsbuffer aktivéiert: / - Open Drain Modus: "0" am Ausgangsregister aktivéiert N-MOS, "1" am Ausgangsregister léisst den Hafen am Hi-Z Modus ( P-MOS ass net aktivéiert) / - Push-Pull Modus: "0" am Ausgangsregister aktivéiert N-MOS, "1" am Ausgangsregister aktivéiert P-MOS.

All den Ënnerscheed tëscht oppenen Drain (oppen Drain) vun "push-pull" (dréckt) ass datt am éischte Pin den HIGH Staat net akzeptéiere kann: wann een an den Ausgangsregister schreift, geet et an den héije Resistenzmodus (héich Impedanz, Salut-Z). Wann Dir Null schreift, behält de Pin d'selwecht a béide Modi, souwuel logesch wéi och elektresch.

Am normalen Ausgangsmodus verschéckt de Pin einfach den Inhalt vum Ausgangsregister. An der "Alternativ" gëtt se vun den entspriechende Peripherieger kontrolléiert (kuckt 9.1.4):

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Wann e Portbit als alternativ Funktiounspin konfiguréiert ass, gëtt de Pinregister behënnert an de Pin ass mam Peripherie-Pin ugeschloss.

Alternativ Funktionalitéit vun all Pin ass beschriwwen an Pin Definitiounen Den Dateblatt ass um erofgeluede Bild. Op d'Fro wat ze maachen wann e Pin e puer alternativ Funktiounen huet, gëtt d'Äntwert duerch eng Foussnote am Dateblat gegeben:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Wann méi Peripherieger deeselwechte Pin benotzen, fir Konflikt tëscht alternativen Funktiounen ze vermeiden, sollt nëmmen eng Peripherie gläichzäiteg benotzt ginn, gewiesselt mat der Peripherie Auer Enable Bit (am passenden RCC Register).

Schlussendlech hunn Pins am Ausgangsmodus och eng Auergeschwindegkeet. Dëst ass eng aner Energiespuerfunktioun; an eisem Fall setzen mir et just op maximal a vergiessen et.

Also: mir benotzen SPI, dat heescht datt zwee Pins (mat Daten a mat engem Auersignal) "alternativ Push-Pull Funktioun" solle sinn, an en aneren (LAT) sollt "regulär Push-Pull" sinn. Awer ier Dir se zouginn, loosst eis mat SPI beschäftegen.

SPI

En anere klenge pädagogesche Programm

SPI oder Serial Peripheral Interface (Serial Peripheral Interface) ass eng einfach a ganz effektiv Interface fir e MK mat anere MKs an der Äussewelt am Allgemengen ze verbannen. De Prinzip vu senger Operatioun ass schonn uewen beschriwwen, wou iwwer de chinesesche LED Chauffer (am Referenz Handbuch, kuckt Sektioun 25). SPI kann am Master ("Master") a Sklave ("Sklave") Modus operéieren. SPI huet véier Basiskanäl, vun deenen net all kënne benotzt ginn:

  • MOSI, Master Output / Sklave Input: Dëse Pin iwwerdréit Daten am Mastermodus a kritt Daten am Sklavemodus;
  • MISO, Master Input / Sklave Output: am Géigendeel, et kritt am Master, a vermëttelt am Sklave;
  • SCK, Serial Clock: setzt d'Frequenz vun der Datenübertragung am Master oder kritt e Auersignal am Sklave. Wesentlech schloen Beats;
  • SS, Sklave Select: mat der Hëllef vun dësem Kanal weess de Sklave datt eppes vun him gewënscht ass. Op STM32 gëtt et NSS genannt, wou N = negativ, d.h. de Controller gëtt e Sklave wann et Buedem an dësem Kanal ass. Et kombinéiert gutt mam Open Drain Output Modus, awer dat ass eng aner Geschicht.

Wéi alles anescht ass SPI op STM32 räich u Funktionalitéit, wat et e bësse schwéier ze verstoen mécht. Zum Beispill kann et net nëmme mat SPI funktionnéieren, awer och mat enger I2S Interface, an an der Dokumentatioun sinn hir Beschreiwunge gemëscht, et ass néideg fir d'Exzess op eng fristgerecht Manéier ofzeschneiden. Eis Aufgab ass extrem einfach: mir brauchen just Daten mat nëmmen MOSI an SCK ze schécken. Mir ginn an den Abschnitt 25.3.4 (Hallefduplex Kommunikatioun, Hallefduplex Kommunikatioun), wou mir fanne 1 Auer an 1 unidirectional Daten Drot (1 Auersignal an 1 eendirektionalen Datestroum):

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
An dësem Modus benotzt d'Applikatioun SPI an entweder Transmission-nëmmen oder Empfangsmodus. / Iwwerdroung-nëmmen Modus ass ähnlech wéi Duplex Modus: Daten ginn op den Iwwerdroungspin iwwerdroen (MOSI am Master Modus oder MISO am Sklave Modus), an den Empfangspin (MISO respektiv MOSI) kann als normale I/O Pin benotzt ginn . An dësem Fall muss d'Applikatioun nëmmen de Rx-Puffer ignoréieren (wann et gelies gëtt, ginn et keng transferéiert Daten do).

Super, de MISO Pin ass gratis, loosst eis de LAT Signal domat verbannen. Loosst eis de Slave Select kucken, deen um STM32 programmatesch kontrolléiert ka ginn, wat extrem bequem ass. Mir liesen de Paragraphe mam selwechten Numm an der Rubrik 25.3.1 SPI Allgemeng Beschreiwung:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Software Kontroll NSS (SSM = 1) / Sklave Selektiounsinformatioun ass am SSI Bit vum SPI_CR1 Register enthale. Den externen NSS Pin bleift gratis fir aner Applikatiounsbedürfnisser.

Et ass Zäit an d'Register ze schreiwen. Ech hu beschloss SPI2 ze benotzen, kuckt no senger Basisadress am Dateblat - an der Rubrik 3.3 Memory Map:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

Gutt, loosst eis ufänken:

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

Öffnen d'Sektioun 25.3.3 mam selbstverständlechen Titel "Configuring SPI in Master Mode":

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

1. Setzt d'Serien Auerfrequenz mat Bits BR [2:0] am SPI_CR1 Register.

D'Register ginn an der Referenzhandbuch Sektioun mam selwechten Numm gesammelt. Adressverschiebung (Adress Offset) fir CR1 - 0x00, par défaut ginn all Bits geläscht (Wäert zrécksetzen 0x0000):

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

D'BR Bits setzen de Controller Auer Divider, a bestëmmen also d'Frequenz op där de SPI funktionnéiert. Eis STM32 Frequenz wäert 72 MHz sinn, den LED Chauffer, laut sengem Dateblat, funktionnéiert mat enger Frequenz vu bis zu 25 MHz, also musse mir mat véier deelen (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. Setzt d'CPOL a CPHA Bits fir d'Relatioun tëscht Datenübertragung a Serien Auer Timing ze definéieren (kuckt Diagramm op Säit 240)

Well mir en Dateblat hei liesen an net op Schema kucken, kucke mer d'Textbeschreiwung vun den CPOL a CPHA Bits op Säit 704 (SPI Allgemeng Beschreiwung) méi no:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Auerphase a Polaritéit
Mat de CPOL- a CPHA Bits vum SPI_CR1 Register kënnt Dir programmatesch véier Timingverhältnisser auswielen. De CPOL (Clock Polarity) Bit kontrolléiert den Zoustand vum Auersignal wann keng Daten iwwerdroe ginn. Dëse Bit kontrolléiert de Master- a Sklave-Modus. Wann CPOL zréckgesat ass, ass de SCK Pin niddereg am Rescht Modus. Wann de CPOL Bit agestallt ass, ass de SCK Pin héich während Rescht Modus.
Wann de CPHA (Auerphase) Bit agestallt ass, ass den héije Bit Trap Strobe den zweete Rand vum SCK Signal (falen wann CPOL kloer ass, erop wann CPOL gesat ass). D'Donnéeë ginn duerch déi zweet Ännerung am Auersignal erfaasst. Wann de CPHA Bit kloer ass, ass den héije Bit Trap Strobe den Opstiegrand vum SCK Signal (falen Rand wann CPOL gesat ass, Rising Rand wann CPOL geläscht ass). D'Date gi bei der éischter Ännerung vum Auersignal ageholl.

Nodeems mir dëst Wëssen absorbéiert hunn, komme mir zu der Conclusioun datt béid Bits mussen Nullen bleiwen, well Mir wëllen datt d'SCK-Signal niddereg bleift wann se net am Gebrauch sinn, an d'Donnéeën iwwer d'Steigerrand vum Puls iwwerdroe ginn (kuckt Fig. Rising Edge Luet den DM634 Datenblat erof

Iwwregens, hei hu mir fir d'éischt eng Fonktioun vum Vokabulär an ST-Dateblieder begéint: an hinnen ass den Ausdrock "Bit op Null zrécksetzen" geschriwwen e bëssen zréckzetrieden, an net e bëssen ze läschen, wéi zum Beispill Atmega.

3. Setzt den DFF Bit fir ze bestëmmen ob d'Dateblock 8-Bit oder 16-Bit Format ass

Ech hunn speziell e 16-Bit DM634 geholl fir net ze stéieren mat der Iwwerdroung vun 12-Bit PWM Daten, wéi den DM633. Et mécht Sënn fir DFF op een ze setzen:

#define DFF         0x0800

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

4. Konfiguréiert de LSBFIRST Bit am SPI_CR1 Register fir de Blockformat ze bestëmmen

LSBFIRST, wéi säin Numm et scho seet, konfiguréiert d'Transmissioun mat dem mannst bedeitende Bit als éischt. Awer DM634 wëll Daten aus dem bedeitendsten Bit kréien. Dofir loossen mir et zrécksetzen.

5. An Hardware Modus, wann Input vun der NSS PIN néideg ass, gëlle eng héich Signal un der NSS PIN während der ganzer Byte Transfermaart Haaptrei. Am NSS Software Modus setzen d'SSM an SSI Bits am SPI_CR1 Register. Wann den NSS Pin als Ausgang benotzt gëtt, muss nëmmen de SSOE Bit gesat ginn.

Installéiert SSM an SSI fir iwwer den NSS Hardware Modus ze vergiessen:

#define SSI         0x0100
#define SSM         0x0200

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

6. D'MSTR an SPE Bits musse gesat ginn (si bleiwen nëmme festgeluegt wann d'NSS Signal héich ass)

Eigentlech, mat dëse Bits designe mir eise SPI als Master a schalten et un:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI ass konfiguréiert, loosst eis direkt Funktiounen schreiwen déi Bytes un de Chauffer schécken. Weiderliesen 25.3.3 "SPI konfiguréieren am Mastermodus":

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Donnéeën Transfermaart Uerdnung
D'Transmissioun fänkt un wann e Byte an den Tx-Puffer geschriwwe gëtt.
Den Datebyte gëtt an de Verréckelungsregister gelueden parallel Modus (vum internen Bus) während der Iwwerdroung vun der éischter bëssen, no deem et iwwerdroen ass sequenziell MOSI Pin Modus, éischt oder lescht bëssen no vir ofhängeg vun der Astellung vum LSBFIRST Bit am CPI_CR1 Register. Den TXE Fändel gëtt no der Dateniwwerdroung gesat vum Tx-Puffer bis zum Schichtregister, a generéiert och en Ënnerbriechung wann den TXEIE Bit am CPI_CR1 Register agestallt ass.

Ech hunn e puer Wierder an der Iwwersetzung beliicht fir op eng Feature vun der SPI Implementatioun an STM Controller opmierksam ze maachen. Op Atmega den TXE Fändel (Tx eidel, Tx ass eidel a prett fir Daten z'empfänken) gëtt nëmme festgeluecht nodeems de ganze Byte geschéckt gouf aus. An hei ass dëse Fändel gesat nodeems de Byte an den internen Verréckelungsregister agefouert gouf. Well et do mat all de Bits gläichzäiteg gedréckt gëtt (parallel), an dann d'Donnéeën sequenziell transferéiert ginn, gëtt TXE gesat ier de Byte komplett geschéckt gëtt. Dëst ass wichteg well am Fall vun eisem LED-Treiber, musse mir de LAT-Pin no der Sendung zéien всех daten, d.h. Den TXE Fändel eleng geet eis net duer.

Dëst bedeit datt mir en anere Fändel brauchen. Loosst eis 25.3.7 kucken - "Status Fändelen":

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
<…>
Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
BUSY Fändel
De BSY Fändel gëtt vun der Hardware gesat a geläscht (ze schreiwen huet keen Effekt). De BSY Fändel weist den Zoustand vun der SPI Kommunikatiounsschicht un.
Et reset:
wann den Transfer fäerdeg ass (ausser am Mastermodus wann den Transfer kontinuéierlech ass)
wann SPI behënnert ass
wann e Master Modus Feeler geschitt (MODF = 1)
Wann den Transfert net kontinuéierlech ass, gëtt de BSY Fändel tëscht all Datentransfer geläscht

Okay, dëst wäert nëtzlech kommen. Loosst eis erausfannen wou den Tx-Puffer läit. Fir dëst ze maachen, liest "SPI Data Register":

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Bits 15:0 DR[15:0] Dateregister
Donnéeën kritt oder Donnéeën ze iwwerdroen.
D'Dateregister ass an zwee Puffer opgedeelt - ee fir Schreiwen (Transmitbuffer) an een fir ze liesen (Empfangbuffer). Schreiwen an den Dateregister schreift an den Tx-Puffer, a liesen aus dem Dateregister gëtt de Wäert am Rx-Puffer zréck.

Gutt, de Statusregister, wou d'TXE a BSY Fändelen fonnt ginn:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

Mir schreiwen:

#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
}

Gutt, well mir mussen 16 Mol zwee Bytes iwwerdroen, laut der Unzuel vun den LED-Treiberausgaben, eppes wéi dat:

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();
}

Awer mir wësse nach net wéi de LAT Pin ze zéien, also gi mir zréck op I / O.

Pins ze ginn

Am STM32F1 sinn d'Register verantwortlech fir den Zoustand vun de Pins zimlech ongewéinlech. Et ass kloer datt et méi vun hinnen ass wéi Atmega, awer si sinn och anescht wéi aner STM Chips. Sektioun 9.1 Allgemeng Beschreiwung vun GPIO:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Jiddereng vun den allgemengen Zweck ech / O Häfen (GPIO) huet zwee 32-Bit Konfiguratiounsregistre (GPIOx_CRL an GPIOx_CRH), zwee 32-Bit Dateregister (GPIOx_IDR an GPIOx_ODR), en 32-Bit Set/Reset Register (GPIOx_BSRR), e 16-Bit Reset Register (GPIOx_BRR) an en 32- Bit Blockéierungsregister (GPIOx_LCKR).

Déi éischt zwee Registere sinn ongewéinlech, an och zimmlech onbequem, well déi 16 Port Pins iwwer si an engem "véier Bits pro Brudder" Format verspreet sinn. Déi. Pins null bis siwen sinn am CRL, an de Rescht sinn am CRH. Zur selwechter Zäit enthalen déi verbleiwen Registere erfollegräich d'Bits vun all Pins vum Hafen - dacks bleiwen d'Halschent "reservéiert".

Fir Simplicitéit, loosst eis um Enn vun der Lëscht ufänken.

Mir brauche kee Spärregister.

D'Set- an d'Reset-Register sinn zimlech witzeg well se sech deelweis duplizéieren: Dir kënnt alles nëmmen am BSRR schreiwen, wou déi méi héich 16 Bits de Pin op Null zrécksetzen, an déi ënnescht op 1 gesat ginn, oder Dir kënnt och benotzt BRR, déi ënnescht 16 Bits vun deenen nëmmen de Pin zrécksetzen. Ech hu gär déi zweet Optioun. Dës Registere si wichteg well se atomeschen Zougang zu Pins ubidden:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Atomic Set oder Reset
Et gëtt kee Besoin fir Ënnerbriechungen auszeschalten wann Dir GPIOx_ODR um Bitniveau programméiert: een oder méi Bits kënne mat enger eenzeger atomarer Schreifoperatioun APB2 geännert ginn. Dëst gëtt erreecht andeems en "1" an de Set-/Reset-Register (GPIOx_BSRR oder, nëmme fir zréckgesat, GPIOx_BRR) vum Bit geschriwwen deen geännert muss ginn. Aner Stécker bleiwen onverännert.

D'Dateregistere hunn zimlech selbstverständlech Nimm - IDR = Input Richtung Register, Input Register; ODR = Däischterheet Richtung Register, Ausgangsregister. Mir brauche se net am aktuelle Projet.

An endlech Kontroll Registere. Well mir un déi zweet SPI Pins interesséiert sinn, nämlech PB13, PB14 an PB15, kucke mir direkt op CRH:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

A mir gesinn datt mir eppes a Stécker vun 20 bis 31 musse schreiwen.

Mir hu schonn uewendriwwer erausfonnt wat mir vu Pins wëllen, also hei wäert ech ouni Screenshot maachen, ech soen just datt MODE d'Richtung spezifizéiert (Input wann béid Bits op 0 gesat sinn) an d'Pingeschwindegkeet (mir brauchen 50MHz, d.h. béid Pin op "1"), an CNF setzt de Modus: regelméisseg "Push-Pull" - 00, "Alternativ" - 10. Par défaut, wéi mir uewen gesinn, hunn all Pins déi drëtt Bit vun ënnen (CNF0), et setzt se an de Modus schwiewend Input.

Well ech plangen soss eppes mat dësem Chip ze maachen, hunn ech fir Simplicitéit all méiglech MODE- an CNF-Wäerter fir déi ënnescht an iewescht Kontrollregister definéiert.

Iergendwéi esou

#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

Eis Pins sinn um Hafen B (Basisadress - 0x40010C00), Code:

#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;

An deementspriechend kënnt Dir Definitioune fir LAT schreiwen, déi vun de BRR- a BSRR Registere gedréckt ginn:

/*** 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 just duerch Inertie, et war ëmmer esou, loosst et bleiwen)

Elo ass alles super, awer et funktionnéiert net. Well dëst STM32 ass, spuere se Stroum, dat heescht datt Dir d'Auerung vun den erfuerderleche Peripherieger aktivéiere musst.

Aktivéiert d'Auer

D'Auer, och bekannt als Clock, ass verantwortlech fir d'Auer. A mir konnten schonn d'Ofkierzung RCC bemierken. Mir sichen et an der Dokumentatioun: dëst ass Reset a Clock Control.

Wéi schonn uewe gesot, glécklecherweis gouf dee schwieregsten Deel vum Auerthema fir eis vu Leit vum STM gemaach, fir déi mir hinnen villmools Merci soen (emol ginn ech e Link op Dem Di Halt seng Websäit, fir kloer ze maachen wéi konfus et ass). Mir brauche just Registere verantwortlech fir Peripheral Clocking z'erméiglechen (Peripheral Clock Enable Registers). Als éischt, loosst eis d'Basisadress vum RCC fannen, et ass ganz am Ufank vun der "Memory Map":

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

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

An dann entweder klickt op de Link, wou Dir probéiert eppes an der Plack ze fannen, oder, vill besser, duerch d'Beschreiwunge vun den Enabling Registere vun de Rubriken iwwer aktivéieren Registere. Wou mir RCC_APB1ENR an RCC_APB2ENR fannen:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

A si enthalen deementspriechend Bits déi d'Auerung vum SPI2, IOPB (I / O Port B) an alternativ Funktiounen (AFIO) enthalen.

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

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

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

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

De finalen Code kann fonnt ginn hei.

Wann Dir d'Méiglechkeet hutt a Wonsch ze testen, da verbënnt den DM634 esou: DAI op PB15, DCK op PB13, LAT op PB14. Mir machen de Chauffeur vu 5 Volt, vergiesst net d'Terrainen ze verbannen.

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

STM8 PWM

PWM op STM8

Wann ech just dësen Artikel plangen, hunn ech beschloss, als Beispill, ze probéieren e puer Funktionalitéit vun engem onbekannten Chip ze beherrschen mat nëmmen engem Dateblatt, sou datt ech net mat engem Schong ouni Stiwwelen ophalen. STM8 war ideal fir dës Roll: éischtens, ech hat e puer chinesesche Brieder mat STM8S103, an zweetens ass et net ganz populär, an dofir ass d'Versuchung fir ze liesen an eng Léisung um Internet ze fannen op de Mangel vun dëse Léisungen.

Den Chip huet och Datenblat и Referenzhandbuch RM0016, an der éischter gëtt et pinout an aschreiwen Adressen, an der zweeter - alles anescht. STM8 ass an C an enger schrecklecher IDE programméiert ST Visual Entwécklung.

Auer an I/O

Par défaut funktionnéiert STM8 mat enger Frequenz vun 2 MHz, dëst muss direkt korrigéiert ginn.

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
HSI (High Speed ​​​​Intern) Auer
D'HSI Auer Signal ass ofgeleet vun engem internen 16 MHz RC Oszilléierer mat engem programmierbaren Divider (1 bis 8). Et gëtt am Auer Divider Register (CLK_CKDIVR) gesat.
Bemierkung: Am Ufank gëtt en HSI RC Oszilléierer mat engem Divider vun 8 als Haaptquell vum Auersignal ausgewielt.

Mir fannen d'Registrieradress am Dateblat, d'Beschreiwung am refman a gesinn datt de Register muss geläscht ginn:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Well mir PWM lafen an d'LEDs verbannen, kucke mer de Pinout:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

Den Chip ass kleng, vill Funktiounen sinn op déiselwecht Pins suspendéiert. Wat a véiereckege Klammeren ass "alternativ Funktionalitéit", et gëtt duerch "Optiounsbytes" gewiesselt (Optioun Bytes) - eppes wéi Atmega fuses. Dir kënnt hir Wäerter programmatesch änneren, awer et ass net néideg, well Déi nei Funktionalitéit gëtt nëmmen no engem Restart aktivéiert. Et ass méi einfach ST Visual Programmer ze benotzen (erofgeluede mat Visual Develop), deen dës Bytes änneren kann. De Pinout weist datt d'CH1 an CH2 Pins vum éischten Timer an de Quadratklammern verstoppt sinn; et ass néideg fir d'AFR1 an AFR0 Bits an STVP ze setzen, an déi zweet wäert och d'CH1 Ausgang vum zweeten Timer vun PD4 op PC5 transferéieren.

Sou wäerten 6 Pins d'LEDs kontrolléieren: PC6, PC7 an PC3 fir den éischten Timer, PC5, PD3 an PA3 fir den zweeten.

D'I/O Pins selwer op STM8 opzestellen ass méi einfach a méi logesch wéi op STM32:

  • vertraut vum Atmega DDR Datedirektiounsregister (Donnéeën Richtung Register): 1 = Ausgang;
  • den éischte Kontrollregister CR1, beim Ausgang, setzt de Push-Pull-Modus (1) oder Open Drain (0); well ech d'LEDs un den Chip mat Kathoden verbannen, verloossen ech Nullen hei;
  • déi zweet Kontrollregister CR2, wann Ausgang, setzt d'Auergeschwindegkeet: 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 Astellung

Als éischt, loosst eis d'Begrëffer definéieren:

  • PWM Heefegkeet - Frequenz mat där den Timer tickt;
  • Auto-Reload, AR - Autoloadable Wäert bis zu deem den Timer zielt (Pulsperiod);
  • Update Event, UEV - en Event dat geschitt wann den Timer op AR gezielt ass;
  • PWM Duty Cycle - PWM Flicht Zyklus, dacks "Flicht Faktor" genannt;
  • Erfaassen / Vergläichen Wäert - Wäert fir Erfaassung / Verglach, op deen den Timer gezielt huet wäert eppes maachen (am Fall vu PWM, invertéiert d'Ausgangssignal);
  • Preload Wäert - Preloaded Wäert. Vergläichen Wäert kann net änneren wann den Timer tickt, soss brécht de PWM-Zyklus. Dofir ginn nei iwwerdroe Wäerter an engem Puffer gesat an erausgezunn wann den Timer um Enn vu sengem Countdown erreecht an zréckgesat gëtt;
  • Kant ausgeriicht и Zentrum-ausgeriicht Modi - Ausrichtung laanscht d'Grenz an am Zentrum, d'selwecht wéi Atmel's Séier PWM и Phase-korrekt PWM.
  • OCiREF, Ausgang Vergläichen Referenz Signal - Referenz Ausgangssignal, tatsächlech, wat op der entspriechender PIN am PWM Modus erschéngt.

Wéi schonn aus dem Pinout kloer ass, hunn zwee Timer PWM Fäegkeeten - déi éischt an déi zweet. Béid sinn 16-Bit, déi éischt huet vill zousätzlech Funktiounen (besonnesch et kann souwuel erop an erof zielen). Mir mussen allebéid gläich schaffen, also hunn ech decidéiert mat deem offensichtlech méi aarme Zweeten unzefänken, fir net zoufälleg eppes ze benotzen wat net do ass. E puer Problem ass datt d'Beschreiwung vun der PWM Funktionalitéit vun all Timer an der Referenzhandbuch am Kapitel iwwer den éischte Timer ass (17.5.7 PWM Modus), also musst Dir ëmmer an d'Dokument sprangen.

PWM op STM8 huet e wichtege Virdeel iwwer PWM op Atmega:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Grenz Ausgeriicht PWM
Kont Configuratioun vun ënnen no uewen
Bottom-up Zielen ass aktiv wann den DIR Bit am TIM_CR1 Register geläscht ass
Beispill:
D'Beispill benotzt den éischte PWM Modus. De PWM Referenzsignal OCiREF gëtt héich gehal soulaang TIM1_CNT < TIM1_CCRi. Soss brauch et en nidderegen Niveau. Wann de Verglachwäert am TIM1_CCRi Register méi grouss ass wéi den Autoload-Wäert (TIM1_ARR Register), gëtt den OCiREF-Signal op 1 gehal. Wann de Vergläichswäert 0 ass, gëtt OCiREF op Null gehal....

STM8 Timer während update Event kontrolléiert éischt vergläichen Wäert, a produzéiert nëmmen dann e Referenzsignal. Dem Atmega säin Timer schrauwen als éischt op a vergläicht dann, wat resultéiert compare value == 0 den Ausgang ass eng Nadel, déi iergendwéi behandelt muss ginn (zum Beispill andeems d'Logik programmatesch ëmgedréint gëtt).

Also wat mir maache wëllen: 8-Bit PWM (AR == 255), vun ënnen no uewen zielen, Ausriichtung laanscht d'Grenz. Well d'Glühbirnen duerch Kathoden un den Chip verbonne sinn, soll de PWM 0 ausginn (LED op) bis vergläichen Wäert an 1 nach.

Mir hu schonn iwwer e puer gelies PWM Modus, also fanne mir dat erfuerderlecht Register vum zweeten Timer andeems Dir an der Referenzmanual fir dësen Ausdrock (18.6.8 - TIMx_CCMR1) sicht:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
110: Éischte PWM Modus - wann Dir vun ënnen no uewen zielt, ass den éischte Kanal aktiv wärend TIMx_CNT < TIMx_CCR1. Soss ass den éischte Kanal inaktiv. [weider am Dokument gëtt et eng falsch Copy-Paste vum Timer 1] 111: Zweete PWM Modus - wann Dir vun ënnen no uewen zielt, ass den éischte Kanal inaktiv wärend TIMx_CNT < TIMx_CCR1. Soss ass den éischte Kanal aktiv.

Zënter datt d'LEDs mat Kathoden mat der MK verbonne sinn, passt den zweete Modus eis (och deen éischten, awer mir wëssen dat nach net).

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Bit 3 OC1PE: Aktivéiert Pin 1 Preload
0: Preload Register op TIMx_CCR1 ass ausgeschalt. Dir kënnt zu all Moment op TIMx_CCR1 schreiwen. Den neie Wäert funktionnéiert direkt.
1: Preload Register op TIMx_CCR1 ass aktivéiert. Liesen / Schreiwen Operatiounen Zougang zu de Preload Register. De virgelueden Wäert TIMx_CCR1 gëtt während all Update-Event an de Schattenregister gelueden.
*Notiz: Fir de PWM Modus richteg ze funktionnéieren, mussen d'Virlaaschtregistre aktivéiert sinn. Dëst ass net néideg am Single Signal Modus (den OPM Bit ass am TIMx_CR1 Register gesat).

Okay, loosst eis alles ausschalten wat mir brauchen fir déi dräi Kanäl vum zweeten Timer:

#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 besteet aus zwee aacht-Bit Registere, alles ass einfach:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Den zweeten Timer kann nëmmen vun ënnen no uewen zielen, Ausriichtung laanscht d'Grenz, näischt muss geännert ginn. Loosst eis d'Frequenzdeeler setzen, zum Beispill op 256. Fir den zweeten Timer gëtt den Divider am TIM2_PSCR Register gesat an ass eng Kraaft vun zwee:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Alles wat bleift ass d'Conclusiounen an den zweeten Timer selwer opzemaachen. Den éischte Problem gëtt duerch Registere geléist Capture / Vergläichen aktivéiert: et sinn zwee, dräi Kanäl, déi asymmetresch iwwer si verspreet sinn. Hei kënne mir och léieren datt et méiglech ass d'Polaritéit vum Signal z'änneren, d.h. am Prinzip war et méiglech PWM Modus ze benotzen 1. Mir schreiwen:

#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;

A schliisslech starten mir den Timer am TIMx_CR1 Register:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Loosst eis en einfachen Analog vun AnalogWrite () schreiwen, deen déi aktuell Wäerter op den Timer transferéiert fir de Verglach. D'Register ginn prévisibel genannt Erfaassen / Vergläichen Registere, et ginn zwee vun hinnen fir all Kanal: déi niddereg Uerdnung 8 Bits an TIM2_CCRxL an déi héich Uerdnung am TIM2_CCRxH. Well mir en 8-Bit PWM erstallt hunn, ass et genuch fir nëmmen déi mannst bedeitend Bits ze schreiwen:

#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;
}

Den opmerksamen Lieser wäert feststellen datt mir e liicht defekt PWM hunn, net fäeg 100% Fëllung ze produzéieren (bei engem Maximumwäert vun 255 gëtt d'Signal fir een Timerzyklus ëmgedréint). Fir LEDs ass dëst egal, an den opmierksam Lieser ka scho roden wéi et ze fixéieren.

PWM op der zweeter Timer Wierker, loosse mer op déi éischt plënneren op.

Den éischten Timer huet genau déiselwecht Bits an de selwechte Registere (et ass just datt déi Bits, déi am zweeten Timer "reservéiert" bliwwen sinn, aktiv an der éischter fir all Zorte vu fortgeschratt Saachen benotzt ginn). Dofir ass et genuch fir d'Adressen vun de selwechte Registere am Dateblat ze fannen an de Code ze kopéieren. Gutt, ännert de Wäert vum Frequenzdeeler, well ... den éischten Timer wëll net eng Kraaft vun zwee kréien, awer e genee 16-Bit Wäert an zwee Registere Prescaler High и Low. Mir maachen alles an ... den éischten Timer funktionnéiert net. Ëm wat geet et?

De Problem kann nëmme geléist ginn andeems Dir déi ganz Rubrik iwwer d'Kontrollregistere vum Timer 1 kuckt, wou mir no der kucken, déi den zweeten Timer net huet. Do wäerten 17.7.30 Breakregister (TIM1_BKR), wou et dëse bëssen ass:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Aktivéiert Haaptausgang

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Dat ass alles sécher elo, de Code do.

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

STM8 Multiplex

Multiplexing op STM8

Den drëtte Mini-Projet ass fir aacht RGB LEDs mam zweeten Timer am PWM Modus ze verbannen an se verschidde Faarwen ze weisen. Et baséiert op dem Konzept vun LED Multiplexing, dat ass datt wann Dir LEDs ganz, ganz séier ausschalt an ausschalt, et wäert eis schéngen datt se dauernd sinn (Persistenz vun der Visioun, Inertie vun der visueller Perceptioun). Ech hunn et eemol gemaach eppes wéi dëst op Arduino.

D'Aarbecht Algorithmus gesäit esou aus:

  • verbonne d'Anode vun der éischter RGB LED;
  • beliicht et, schéckt déi néideg Signaler un d'Katoden;
  • gewaart bis d'Enn vum PWM Zyklus;
  • verbonne d'Anode vun der zweeter RGB LED;
  • lit et...

Well, etc. Natierlech, fir eng schéin Operatioun ass et néideg datt d'Anode verbonnen ass an d'LED gläichzäiteg "entzündegt". Gutt, oder bal. Op alle Fall musse mir e Code schreiwen, deen Wäerter an dräi Kanäl vum zweeten Timer erausginn, se änneren wann UEV erreecht gëtt, a gläichzäiteg déi aktuell aktiv RGB LED änneren.

Zënter der LED-Schaltung automatesch ass, musse mir e "Video Memory" erstellen, aus deem den Ënnerbriechungshandler Daten kritt. Dëst ass eng einfach Array:

uint8_t colors[8][3];

Fir d'Faarf vun enger spezifescher LED z'änneren, ass et genuch fir déi erfuerderlech Wäerter an dësem Array ze schreiwen. An d'Variabel wäert fir d'Zuel vun der aktiver LED verantwortlech sinn

uint8_t cnt;

Demux

Fir richteg Multiplexing brauche mir, komesch genuch, en CD74HC238 Demultiplexer. Demultiplexer - e Chip deen de Bedreiwer an der Hardware implementéiert <<. Duerch dräi Input Pins (Bits 0, 1 an 2) fidderen mir et eng dräi-Bit Zuel X, an als Äntwert aktivéiert et Ausgangsnummer (1<<X). Déi reschtlech Inputen vum Chip ginn benotzt fir de ganzen Design ze skaléieren. Mir brauchen dësen Chip net nëmmen fir d'Zuel vun de besaten Pins vum Mikrokontroller ze reduzéieren, awer och fir d'Sécherheet - fir net zoufälleg méi LEDs wéi méiglech ze maachen an de MK net ze verbrennen. Den Chip kascht e Penny a sollt ëmmer an Ärem Hausmedizinkabinett gelagert ginn.

Eis CD74HC238 wäert verantwortlech sinn fir d'Spannung un d'Anode vun der gewënschter LED ze liwweren. An engem vollwäertege Multiplex géif et Spannung un d'Kolonn duerch e P-MOSFET liwweren, awer an dëser Demo ass et direkt méiglech, well et zitt 20 mA, no absolute maximal Bewäertungen am Dateblatt. Vun Spezifikatioune vun CD74HC238 mir brauche Pinouts an dëse Cheatsheet:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
H = héich Volt Niveau, L = niddereg Volt Niveau, X - egal

Mir verbannen E2 an E1 mam Buedem, E3, A0, A1 an A3 mat Pins PD5, PC3, PC4 an PC5 vun STM8. Well d'Tabell hei uewen souwuel niddereg wéi och héich Niveauen enthält, konfiguréiere mir dës Pins als Push-Pull Pins.

PWM

PWM um zweeten Timer ass op déiselwecht Manéier konfiguréiert wéi an der viregter Geschicht, mat zwee Differenzen:

Als éischt musse mir den Ënnerbriechung aktivéieren Update Event (UEV) déi eng Funktioun rufft déi déi aktiv LED wiesselt. Dëst gëtt gemaach andeems de Bit geännert gëtt Update Ënnerbriechung Aktivéieren an engem Register mat engem soen Numm

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
Ënnerbriechung aktivéieren aschreiwen

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Den zweeten Ënnerscheed ass mat dem Phänomen vu Multiplexing verbonnen, wéi z ghosting - parasitäre Glanz vun Dioden. An eisem Fall kann et schéngen wéinst der Tatsaach datt den Timer, deen en Ënnerbriechung op der UEV verursaacht huet, weider tickt, an den Ënnerbriechungshandter huet keng Zäit fir d'LED ze wiesselen ier den Timer ufänkt eppes op d'Pins ze schreiwen. Fir dëst ze bekämpfen, musst Dir d'Logik ëmdréinen (0 = maximal Hellegkeet, 255 = näischt ass beliicht) an extremen Duty Cycle Wäerter vermeiden. Déi. sécherstellen, datt no UEV d'LEDs fir ee PWM-Zyklus komplett ausgoen.

Polaritéit änneren:

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

Vermeit r, g a b op 255 ze setzen an erënnere mech un se ëmzedréinen wann Dir se benotzt.

Ënnerbriechungen

D'Essenz vun engem Ënnerbriechung ass datt ënner bestëmmten Ëmstänn den Chip stoppt den Haaptprogramm auszeféieren an eng extern Funktioun ze ruffen. Ënnerbriechungen geschéien wéinst externen oder internen Afloss, dorënner den Timer.

Wann mir éischt e Projet am ST Visual Develop, Zousätzlech zu main.c mir kruten eng Fënster mat engem mysteriéise Fichier stm8_interrupt_vector.c, automatesch am Projet abegraff. An dësem Fichier gëtt eng Funktioun fir all Ënnerbriechung zougewisen NonHandledInterrupt. Mir mussen eis Funktioun un de gewënschten Ënnerbriechung binden.

D'Dateblatt huet eng Tabell vun Ënnerbriechungsvektoren, wou mir déi fannen déi mir brauchen:

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8
13 IM2 Aktualiséierung / Iwwerschwemmung
14 TIM2 erfaassen / vergläichen

Mir mussen d'LED bei UEV änneren, also brauche mir Ënnerbriechung #13.

Deementspriechend éischtens am Dossier stm8_interrupt_vector.c änneren den Default Numm vun der Funktioun responsabel fir Ënnerbriechung Nr 13 (IRQ13) op Är eege:

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

Zweetens musse mir eng Datei erstellen main.h mat folgendem Inhalt:

#ifndef __MAIN_H
#define __MAIN_H

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

A schliisslech schreift dës Funktioun an Ärem 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;
}

Alles wat bleift ass Ënnerbriechungen z'erméiglechen. Dëst gëtt mam Assembler Kommando gemaach rim - Dir musst et no sichen Programméierungshandbuch:

//enable interrupts
_asm("rim");

En aneren Assembler Kommando ass sim - schalt Ënnerbriechungen aus. Si mussen ausgeschalt ginn wärend nei Wäerter an de "Video Memory" geschriwwe ginn, sou datt en Ënnerbriechung, deen am falsche Moment verursaacht gëtt, d'Array net verwinnt.

All Code - op GitHub.

Liesen Dateblieder 2: SPI op STM32; PWM, Timer an Ënnerbriechungen op STM8

Wann op d'mannst een dësen Artikel nëtzlech fënnt, dann hunn ech et net vergeblech geschriwwen. Ech wäert frou kommentéieren an Remarquen ze kréien, Ech probéieren alles ze äntweren.

Source: will.com

Setzt e Commentaire