Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

В la unua parto Mi provis rakonti al ŝatokupaj elektronikaj inĝenieroj, kiuj kreskis de Arduino-pantalono, kiel kaj kial ili devus legi datumajn foliojn kaj alian dokumentadon por mikroregiloj. La teksto montriĝis granda, do mi promesis montri praktikajn ekzemplojn en aparta artikolo. Nu, li nomis sin laktofungo...

Hodiaŭ mi montros al vi kiel uzi datumajn foliojn por solvi sufiĉe simplajn, sed necesajn por multaj projektoj, taskoj sur STM32 (Blua Pilolo) kaj STM8-regiloj. Ĉiuj demo-projektoj estas dediĉitaj al miaj plej ŝatataj LED-oj, ni lumigos ilin en grandaj kvantoj, por kiuj ni devos uzi ĉiajn interesajn ekstercentrajn.

La teksto denove montriĝis grandega, do por oportuno mi faras la enhavon:

STM32 Blue Pill: 16 LED-oj kun DM634-ŝoforo
STM8: Agordante ses PWM-pinglojn
STM8: 8 RGB LED-oj sur tri pingloj, interrompoj

Malgarantio: Mi ne estas inĝeniero, mi ne ŝajnigas havi profundan scion pri elektroniko, la artikolo estas destinita por amatoroj kiel mi. Fakte, mi konsideris min antaŭ du jaroj kiel la celgrupon. Se iu tiam dirus al mi, ke datumfolioj sur nekonata blato ne estas timige legi, mi ne pasigus multe da tempo serĉante kelkajn kodpecojn en Interreto kaj inventante lambastonojn per tondilo kaj glubendo.

La fokuso de ĉi tiu artikolo estas pri datenfolioj, ne projektoj, do la kodo eble ne estas tre neta kaj ofte malvasta. La projektoj mem estas tre simplaj, kvankam taŭgaj por unua konatiĝo kun la nova blato.

Mi esperas, ke mia artikolo helpos iun en simila stadio de mergo en la hobio.

STM32

16 LED-oj kun DM634 kaj SPI

Malgranda projekto uzanta Blue Pill (STM32F103C8T6) kaj DM634 LED-ŝoforon. Uzante datumajn foliojn, ni eltrovos la ŝoforon, STM IO-havenojn kaj agordos SPI.

DM634

Tajvana blato kun 16 16-bitaj PWM-eligaĵoj, povas esti konektita en ĉenoj. La malalta gamo 12-bita modelo estas konata de hejma projekto Luma pako. Siatempe, elektante inter la DM63x kaj la konata TLC5940, mi elektis DM pro pluraj kialoj: 1) TLC sur Aliexpress estas certe falsa, sed ĉi tiu ne; 2) DM havas aŭtonomian PWM kun sia propra frekvenca generatoro; 3) ĝi povus esti aĉetita malmultekoste en Moskvo, prefere ol atendi pakaĵon de Ali. Kaj, kompreneble, estis interese lerni kiel regi la blaton mem, prefere ol uzi pretan bibliotekon. Blatoj nun estas plejparte prezentitaj en la pakaĵo SSOP24; ili estas facile luteblaj al adaptilo.

Ĉar la fabrikanto estas tajvana, datumpaĝo la blato estas skribita en la ĉina angla, kio signifas, ke ĝi estos amuza. Unue ni rigardas la pinout (Pin-Konekto) por kompreni al kiu kruro konekti al kio, kaj priskribo de la pingloj (Pingla Priskribo). 16 pingloj:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
DC Sink Fontoj (Malferma Drenilo)

Sinki / Malferma-drena eligo - malplenigi; fonto de enfluanta kurento; la eligo estas konektita al grundo en la aktiva stato - la LED-oj estas konektitaj al la ŝoforo per katodoj. Elektre, ĉi tio kompreneble ne estas "malferma drenilo" (malfermita drenilo), sed en datenfolioj tiu nomo por pingloj en drenreĝimo ofte estas trovita.

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Eksteraj rezistiloj inter REXT kaj GND por agordi la eligan kurentvaloron

Referenca rezistilo estas instalita inter la REXT-stifto kaj grundo, kiu kontrolas la internan reziston de la eliroj, vidu la grafikaĵon sur paĝo 9 de la datenfolio. En la DM634, ĉi tiu rezisto ankaŭ povas esti kontrolita per programaro, fiksante la ĝeneralan brilon (tutmonda brilo); Mi ne eniros detalojn en ĉi tiu artikolo, mi nur metos 2.2 - 3 kOhm-rezistilon ĉi tie.

Por kompreni kiel kontroli la blaton, ni rigardu la priskribon de la aparata interfaco:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

Jes, jen ĝi, ĉina angla en sia tuta gloro. Traduki ĉi tion estas problema, vi povas kompreni ĝin, se vi volas, sed ekzistas alia maniero - rigardu kiel la konekto al la funkcie simila TLC5940 estas priskribita en la datumfolio:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
... Nur tri pingloj estas bezonataj por enigi datumojn en la aparaton. La ascenda rando de la SCLK-signalo ŝanĝas la datenojn de la SIN-stifto al la interna registro. Post kiam ĉiuj datenoj estis ŝarĝitaj, mallonga alta XLAT-signalo ŝlosas la sinsekve transdonitajn datenojn en la internajn registrojn. Internaj registroj estas pordegoj ekigitaj de la XLAT-signalnivelo. Ĉiuj datumoj estas transdonitaj plej signifa bito unue.

Latch – klinko/ŝlosilo/ŝlosilo.
Leviĝanta rando – fronta eĝo de la pulso
MSB unue – plej signifa (plej maldekstra) iom antaŭen.
por horloĝi datumojn – transdoni datumojn sinsekve (bit post bito).

vorto seruro estas ofte trovita en la dokumentado por blatoj kaj estas tradukita diversmaniere, do por kompreno mi permesos al mi

malgranda eduka programoLa LED-ŝoforo estas esence ŝanĝregistrilo. "Shift" (movo) en la nomo - laŭbit-movo de datumoj ene de la aparato: ĉiu nova bito enŝovita puŝas la tutan ĉenon antaŭen antaŭ ĝi. Ĉar neniu volas observi ĥaosan palpebrumon de la LED-oj dum la deĵoro, la procezo okazas en bufroregistroj apartigitaj de la laborregistroj per dampilo (seruro) estas speco de atendoĉambro kie la bitoj estas aranĝitaj en la dezirata sinsekvo. Kiam ĉio estas preta, la obturatoro malfermiĝas kaj la pecoj funkcias, anstataŭigante la antaŭan aron. Vorto seruro en la dokumentado por mikrocirkvitoj preskaŭ ĉiam implicas tian dampilon, negrave en kiaj kombinaĵoj ĝi estas uzata.

Do, transdono de datumoj al la DM634 okazas tiel: agordu la DAI-enigon al la valoro de la plej signifa bito de la malproksima LED, tiru DCK supren kaj malsupren; starigu la DAI-enigon al la valoro de la sekva bito, tiri DCK; kaj tiel plu ĝis ĉiuj bitoj estos transdonitaj (horloĝiĝis), post kio ni tiras LAT. Ĉi tio povas esti farita permane (bit-bang), sed estas pli bone uzi SPI-interfacon speciale adaptitan por tio, ĉar ĝi estas prezentita sur nia STM32 en du kopioj.

Blua Pilolo STM32F103

Enkonduko: STM32-regiloj estas multe pli kompleksaj ol Atmega328 ol ili eble ŝajnas timigaj. Krome, pro kialoj de ŝparado de energio, preskaŭ ĉiuj ekstercentraj estas malŝaltitaj ĉe la komenco, kaj la horloĝfrekvenco estas 8 MHz de la interna fonto. Feliĉe, STM-programistoj skribis kodon, kiu alportas la blaton ĝis la "kalkulita" 72 MHz, kaj la aŭtoroj de ĉiuj IDEoj, kiujn mi konas, inkludis ĝin en la komenca proceduro, do ni ne bezonas horloĝi (sed vi povas, se vi vere volas). Sed vi devos ŝalti la ekstercentrajn.

Dokumentado: Blue Pill estas ekipita per la populara blato STM32F103C8T6, ekzistas du utilaj dokumentoj por ĝi:

En la datenfolio ni eble interesiĝas pri:

  • Pinouts - chip pinouts - en la okazo ke ni decidas fari la tabulojn mem;
  • Memormapo - memormapo por specifa blato. La Referenca Manlibro havas mapon por la tuta linio, kaj ĝi mencias registrojn, kiujn la nia ne havas.
  • Pindifintabulo - listigante la ĉefajn kaj alternativajn funkciojn de pingloj; por la "blua pilolo" vi povas trovi pli oportunajn bildojn en la Interreto kun listo de pingloj kaj iliaj funkcioj. Tial ni tuj guglos Blue Pill pinout kaj tenas ĉi tiun bildon ĉemane:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
NB: estis eraro en la bildo el la Interreto, kiu estis notita en la komentoj, dankon pro tio. La bildo estis anstataŭigita, sed ĉi tio estas leciono - estas pli bone kontroli informojn ne el datumfolioj.

Ni forigas la datumfolion, malfermas la Referencan Manlibron, kaj de nun ni uzas nur ĝin.
Proceduro: ni traktas norman enigon/eligon, agordas SPI, ŝaltas la necesajn ekstercentrajn.

Enigo Eligo

Sur la Atmega328, I/O estas efektivigita ekstreme simple, tial la abundo de STM32-opcioj povas esti konfuza. Nun ni bezonas nur konkludojn, sed eĉ ĉi tiuj havas kvar eblojn:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
malferma drenilo, push-pull, alternativa push-pull, alternativa malferma drenilo

"Tiro-puŝo" (puŝ-tiri) estas la kutima eligo de la Arduino, la pinglo povas preni la valoron aŭ ALTA aŭ MALALTA. Sed kun "malferma drenilo" ekzistas malfacilaĵoj, kvankam fakte ĉio estas simpla ĉi tie:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Eliga agordo / kiam la haveno estas asignita al eligo: / eligo-bufro ebligita: / – malferma drena reĝimo: "0" en la eligo-registro ebligas N-MOS, "1" en la eligo-registro lasas la havenon en Hi-Z-reĝimo ( P-MOS ne estas aktivigita ) / – push-pull mode: "0" en la eligregistro aktivigas N-MOS, "1" en la eligregistro aktivigas P-MOS.

La tuta diferenco inter malferma drenilo (malfermita drenilo) de "puŝ-tiri" (puŝ-tiri) estas, ke en la unua stifto ne povas akcepti la ALTA ŝtato: skribante unu al la eligregistro, ĝi iras en altan rezistan reĝimon (alta impedanco, Saluton-Z). Dum skribado de nulo, la pinglo kondutas same en ambaŭ reĝimoj, kaj logike kaj elektre.

En normala produktaĵreĝimo, la stifto simple dissendas la enhavon de la produktaĵregistro. En la "alternativo" ĝi estas regata de la respondaj ekstercentraj (vidu 9.1.4):

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Se havenpeco estas agordita kiel alterna funkcio-stifto, la stiftoregistro estas malfunkciigita kaj la stifto estas ligita al la periferia stifto.

Alternativa funkcieco de ĉiu stifto estas priskribita en Pinglaj Difinoj La datenfolio estas sur la elŝutita bildo. Al la demando pri kion fari se pinglo havas plurajn alternativajn funkciojn, la respondo estas donita per piednoto en la datenfolio:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Se pluraj flankaparatoj uzas la saman stifton, por eviti konflikton inter alternativaj funkcioj, nur unu flankaparato devus esti uzata samtempe, ŝanĝita uzante la periferian horloĝan ebligan biton (en la taŭga RCC-registro).

Fine, pingloj en eliriga reĝimo ankaŭ havas horloĝan rapidon. Ĉi tio estas alia energiŝpara funkcio; en nia kazo, ni simple agordas ĝin al maksimumo kaj forgesas ĝin.

Do: ni uzas SPI, kio signifas, ke du pingloj (kun datumoj kaj kun horloĝa signalo) estu "alternativa push-pull-funkcio", kaj alia (LAT) estu "regula push-pull". Sed antaŭ ol atribui ilin, ni traktu SPI.

SPI

Alia malgranda eduka programo

SPI aŭ Serial Peripheral Interface (seria periferia interfaco) estas simpla kaj tre efika interfaco por konekti MK kun aliaj MK kaj la ekstera mondo ĝenerale. La principo de ĝia funkciado jam estis priskribita supre, kie pri la ĉina LED-ŝoforo (en la referenca manlibro, vidu sekcion 25). SPI povas funkcii en majstra ("majstro") kaj sklavo ("sklavo") reĝimo. SPI havas kvar bazajn kanalojn, de kiuj ne ĉiuj povas esti uzitaj:

  • MOSI, Majstra Eligo/Sklava Enigo: ĉi tiu pinglo transdonas datumojn en majstra reĝimo, kaj ricevas datumojn en sklavreĝimo;
  • MISO, Master Input / Slave Output: male, ĝi ricevas en la majstro, kaj transdonas en la sklavo;
  • SCK, Seria Horloĝo: metas la frekvencon de transdono de datumoj en la majstro aŭ ricevas horloĝsignalon en la sklavo. Esence trafante taktojn;
  • SS, Slave Select: helpe de ĉi tiu kanalo, la sklavo scias, ke io estas dezirata de li. Sur STM32 ĝi nomiĝas NSS, kie N = negativa, t.e. la regilo iĝas sklavo se estas grundo en ĉi tiu kanalo. Ĝi bone kombinas kun la Open Drain Output-reĝimo, sed tio estas alia rakonto.

Kiel ĉio alia, SPI sur STM32 estas riĉa je funkcieco, kio faras ĝin iom malfacile komprenebla. Ekzemple, ĝi povas funkcii ne nur kun SPI, sed ankaŭ kun I2S-interfaco, kaj en la dokumentado iliaj priskriboj estas miksitaj, necesas tranĉi la troon ĝustatempe. Nia tasko estas ege simpla: ni nur bezonas sendi datumojn uzante nur MOSI kaj SCK. Ni iras al sekcio 25.3.4 (duondupleksa komunikado, duondupleksa komunikado), kie ni trovas 1 horloĝo kaj 1 unudirekta datumdrato (1 horloĝsignalo kaj 1 unudirekta datumfluo):

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
En ĉi tiu reĝimo, la aplikaĵo uzas SPI en aŭ nur elsenda aŭ nur riceva reĝimo. / Nur elsenda reĝimo similas al dupleksa reĝimo: datumoj estas elsenditaj sur la elsenda pinglo (MOSI en majstra reĝimo aŭ MISO en sklavreĝimo), kaj la riceva pinglo (MISO aŭ MOSI respektive) povas esti uzata kiel regula I/O-stifto. . En ĉi tiu kazo, la aplikaĵo nur bezonas ignori la Rx-bufferon (se ĝi estas legita, tie ne estos transdonitaj datumoj).

Bonege, la MISO-stifto estas senpaga, ni konektu la LAT-signalon al ĝi. Ni rigardu Slave Select, kiu sur la STM32 estas regebla programe, kio estas ege oportuna. Ni legas la alineon de la sama nomo en sekcio 25.3.1 SPI Ĝenerala Priskribo:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Softvarkontrolo NSS (SSM = 1)/Sklavo-elekta informo estas enhavita en la SSI-bito de la SPI_CR1-registro. La ekstera NSS-pinglo restas libera por aliaj aplikaj bezonoj.

Estas tempo skribi al la registroj. Mi decidis uzi SPI2, serĉi ĝian bazan adreson en la datenfolio - en la sekcio 3.3 Memormapo:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

Nu, ni komencu:

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

Malfermu sekcion 25.3.3 kun la memklariga titolo "Agordi SPI en Majstra Reĝimo":

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

1. Agordu la serian horloĝan frekvencon per bitoj BR[2:0] en la registro SPI_CR1.

La registroj estas kolektitaj en la referenca manlibro de la sama nomo. Adresŝanĝo (Adreso ofseto) por CR1 - 0x00, defaŭlte ĉiuj bitoj estas forigitaj (Restarigi valoron 0x0000):

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

La BR-bitoj starigas la regilan horloĝdividilon, tiel determinante la frekvencon ĉe kiu la SPI funkcios. Nia STM32-frekvenco estos 72 MHz, la LED-ŝoforo, laŭ sia datenfolio, funkcias kun ofteco de ĝis 25 MHz, do ni devas dividi per kvar (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. Agordu la CPOL kaj CPHA-bitojn por difini la rilaton inter datumtransigo kaj seria horloĝa tempo (vidu diagramon sur paĝo 240)

Ĉar ni legas ĉi tie datumfolion kaj ne rigardas skemojn, ni rigardu pli detale la tekstan priskribon de la CPOL kaj CPHA-bitoj sur paĝo 704 (Ĝenerala Priskribo de SPI):

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Horloĝa fazo kaj poluseco
Uzante la CPOL kaj CPHA-bitojn de la SPI_CR1-registro, vi povas programe elekti kvar tempajn rilatojn. La CPOL (horloĝpoluseco) bito kontrolas la staton de la horloĝsignalo kiam neniuj datenoj estas elsenditaj. Ĉi tiu bito kontrolas la reĝimojn majstro kaj sklavo. Se CPOL estas rekomencigita, la SCK-stifto estas malalta en ripozreĝimo. Se la CPOL-bito estas metita, la SCK-stifto estas alta dum ripoza reĝimo.
Kiam la CPHA (horloĝfazo) bito estas metita, la alta bita kaptila strobostrobo estas la dua rando de la SCK-signalo (falanta se CPOL estas klara, pliiĝanta se CPOL estas metita). La datumoj estas kaptitaj de la dua ŝanĝo en la horloĝsignalo. Se la CPHA-bito estas klara, la alta bita kaptila stroboskopo estas la altiĝanta rando de la SCK-signalo (malaltiĝanta rando se CPOL estas metita, levrando se CPOL estas malbarita). Datenoj estas kaptitaj ĉe la unua ŝanĝo en la horloĝsignalo.

Ensorbinte ĉi tiun scion, ni venas al la konkludo, ke ambaŭ bitoj devas resti nuloj, ĉar Ni volas, ke la SCK-signalo restu malalta kiam ne estas uzata, kaj datumoj estu elsenditaj sur la altiĝanta rando de la pulso (vidu Fig. Leviĝanta Rando en la datenfolio de DM634).

Cetere, ĉi tie ni unue renkontis karakterizaĵon de la vortprovizo en ST-datumoj: en ili estas skribita la frazo "restarigi la biton al nulo". por iom restarigikaj ne iom malbari, kiel, ekzemple, Atmega.

3. Agordu la DFF-biton por determini ĉu la datumbloko estas 8-bita aŭ 16-bita formato

Mi specife prenis 16-bitan DM634 por ne ĝeni transdoni 12-bitajn PWM-datumojn, kiel la DM633. Estas senco agordi DFF al unu:

#define DFF         0x0800

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

4. Agordu la LSBFIRST-biton en la registro SPI_CR1 por determini la blokformaton

LSBFIRST, kiel ĝia nomo indikas, unue agordas dissendon kun la malplej signifa bito. Sed DM634 volas ricevi datumojn ekde la plej signifa bito. Tial ni lasas ĝin restarigi.

5. En aparatara reĝimo, se enigo de la NSS-pinglo estas postulata, apliku altan signalon al la NSS-stifto dum la tuta bajta transiga sekvenco. En NSS-programa reĝimo, agordu la SSM kaj SSI-bitojn en la registro SPI_CR1. Se la NSS-stifto estas uzota kiel eligo, nur la SSOE-bito devas esti agordita.

Instalu SSM kaj SSI por forgesi pri la aparatara reĝimo de NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. La bitoj MSTR kaj SPE devas esti agordita (ili restas agordita nur se la NSS-signalo estas alta)

Efektive, per ĉi tiuj pecoj ni nomumas nian SPI kiel majstro kaj ŝaltas ĝin:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI estas agordita, ni tuj skribu funkciojn, kiuj sendas bajtojn al la ŝoforo. Daŭrigu legi 25.3.3 "Agordi SPI en majstra reĝimo":

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Ordo de transdono de datumoj
Dissendo komenciĝas kiam bajto estas skribita al la Tx-bufro.
La datenbajto estas ŝarĝita en la ŝanĝregistron ĉe paralela reĝimo (de la interna buso) dum la transdono de la unua bito, post kiu ĝi estas transdonita al sinsekva MOSI-stifta reĝimo, unua aŭ lasta bito antaŭen depende de la fikso de la LSBFIRST-bito en la CPI_CR1-registro. La TXE-flago estas metita post transdono de datumoj de Tx-bufro al ŝanĝregistrilo, kaj ankaŭ generas interrompon se la TXEIE-bito en la CPI_CR1-registro estas metita.

Mi emfazis kelkajn vortojn en la traduko por atentigi unu trajton de la SPI-efektivigo en STM-regiloj. Sur Atmega la TXE-flago (Tx Malplena, Tx estas malplena kaj preta ricevi datumojn) estas agordita nur post kiam la tuta bajto estas sendita eksteren. Kaj ĉi tie ĉi tiu flago estas metita post kiam la bajto estis enmetita en la internan ŝanĝregistron. Ĉar ĝi estas puŝita tien kun ĉiuj bitoj samtempe (paralele), kaj tiam la datenoj estas transdonitaj sinsekve, TXE estas agordita antaŭ ol la bajto estas tute sendita. Ĉi tio estas grava ĉar en la kazo de nia LED-ŝoforo, ni devas tiri la LAT-pinglon post sendo всех datumoj, t.e. La TXE-flago sole ne sufiĉos por ni.

Ĉi tio signifas, ke ni bezonas alian flagon. Ni rigardu 25.3.7 - "Statusaj flagoj":

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
<...>
Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
okupata flago
La BSY-flago estas metita kaj malbarita de aparataro (skribi al ĝi ne efikas). La BSY-flago indikas la staton de la SPI-komunika tavolo.
Ĝi rekomencigas:
kiam la translokigo estas finita (krom en majstra reĝimo se la translokigo estas kontinua)
kiam SPI estas malŝaltita
kiam okazas eraro en majstra reĝimo (MODF=1)
Se la translokigo ne estas kontinua, la BSY-flago estas malbarita inter ĉiu datumtransigo

Bone, ĉi tio estos utila. Ni eksciu, kie troviĝas la Tx-bufro. Por fari tion, legu "SPI Data Register":

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Bitoj 15:0 DR[15:0] Datuma Registro
Datenoj ricevitaj aŭ datumoj transdonotaj.
La datumregistro estas dividita en du bufrojn - unu por skribado (elsenda bufro) kaj unu por legado (ricevu bufro). Skribi al la datenregistro skribas al la Tx-bufro, kaj legado de la datenregistro resendos la valoron enhavitan en la Rx-bufro.

Nu, kaj la statusa registro, kie troviĝas la flagoj TXE kaj BSY:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

Ni skribas:

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

Nu, ĉar ni devas transdoni 16 fojojn du bajtojn, laŭ la nombro da LED-ŝoforaj eliroj, io kiel ĉi tio:

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

Sed ni ankoraŭ ne scias kiel tiri la LAT-pinglon, do ni reiros al I/O.

Asignante pinglojn

En la STM32F1, la registroj respondecaj pri la stato de la pingloj estas sufiĉe nekutimaj. Estas klare, ke estas pli da ili ol Atmega, sed ili ankaŭ diferencas de aliaj STM-blatoj. Sekcio 9.1 Ĝenerala Priskribo de GPIO:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Ĉiu el la ĝeneraluzeblaj I/O-havenoj (GPIO) havas du 32-bitajn konfiguracioregistrojn (GPIOx_CRL kaj GPIOx_CRH), du 32-bitajn datenregistrojn (GPIOx_IDR kaj GPIOx_ODR), 32-bitan agordigan/restarigregistron (GPIOx_BSRR), 16-bitan rekomencigitan registron (GPIOx_BRR) kaj 32- bita blokanta registro (GPIOx_LCKR).

La unuaj du registroj estas nekutimaj, kaj ankaŭ sufiĉe maloportunaj, ĉar la 16 havenaj pingloj estas disigitaj trans ili en formato "kvar bitoj por frato". Tiuj. pingloj nul al sep estas en CRL, kaj la ceteraj estas en CRH. Samtempe, la ceteraj registroj sukcese enhavas la pecojn de ĉiuj pingloj de la haveno - ofte restanta duono "rezervita".

Por simpleco, ni komencu de la fino de la listo.

Ni ne bezonas blokan registron.

La agordaj kaj rekomencigitaj registroj estas sufiĉe amuzaj, ĉar ili parte duobligas unu la alian: vi povas skribi ĉion nur en BSRR, kie la pli altaj 16 bitoj restarigos la pinglon al nulo, kaj la pli malaltaj estos agordita al 1, aŭ vi ankaŭ povas. uzu BRR, kies pli malaltaj 16 bitoj nur restarigi la pinglon. Mi ŝatas la duan eblon. Ĉi tiuj registroj estas gravaj ĉar ili disponigas atoman aliron al pingloj:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Atoma Agordi aŭ Restarigi
Ne necesas malebligi interrompojn dum programado de GPIOx_ODR je la bitonivelo: unu aŭ pluraj bitoj povas esti ŝanĝitaj per ununura atomskriba operacio APB2. Tio estas atingita skribante "1" al la fiksita/restarigi registron (GPIOx_BSRR aŭ, por rekomencigita nur, GPIOx_BRR) de la bito kiu devas esti ŝanĝita. Aliaj pecoj restos senŝanĝaj.

La datenregistroj havas sufiĉe memklarigeblajn nomojn - IDR = Eniro Direction Register, eniga registro; ODR = eligon Direction Register, eligo-registro. Ni ne bezonos ilin en la nuna projekto.

Kaj fine, kontrolaj registroj. Ĉar ni interesiĝas pri la duaj SPI-pingloj, nome PB13, PB14 kaj PB15, ni tuj rigardas CRH:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

Kaj ni vidas, ke ni devos skribi ion per bitoj de 20 ĝis 31.

Ni jam eksciis supre, kion ni volas de pingloj, do ĉi tie mi faros sen ekrankopio, mi nur diros, ke MODO specifas la direkton (enigo se ambaŭ bitoj estas agordita al 0) kaj pingla rapido (ni bezonas 50MHz, t.e. ambaŭ pinglo al "1"), kaj CNF fiksas la reĝimon: regula "push-pull" - 00, "alternativo" - 10. Defaŭlte, kiel ni vidas supre, ĉiuj pingloj havas la trian biton de la malsupro (CNF0), ĝi metas ilin al reĝimo flosanta enigo.

Ĉar mi planas fari ion alian per ĉi tiu blato, por simpleco mi difinis ĉiujn eblajn MODE kaj CNF-valorojn por ambaŭ la malsuperaj kaj supraj kontrolregistroj.

Iel tiel

#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

Niaj pingloj situas sur haveno B (baza adreso - 0x40010C00), kodo:

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

Kaj, sekve, vi povas skribi difinojn por LAT, kiuj estos svingitaj de la registroj BRR kaj 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_malalta nur pro inercio, ĉiam estis tiel, lasu ĝin resti)

Nun ĉio estas bonega, sed ĝi ne funkcias. Ĉar ĉi tio estas STM32, ili ŝparas elektron, kio signifas, ke vi devas ebligi horloĝadon de la postulataj ekstercentraj.

Ŝaltu horloĝon

La horloĝo, ankaŭ konata kiel Horloĝo, respondecas pri horloĝo. Kaj ni jam povis rimarki la mallongigon RCC. Ni serĉas ĝin en la dokumentado: ĉi tio estas Restarigi kaj Horloĝa Kontrolo.

Kiel dirite supre, feliĉe, la plej malfacila parto de la horloĝtemo estis farita por ni de homoj de STM, pro kio ni tre dankas ilin (denove mi donos ligilon al La retejo de Di Halt, por klarigi kiom konfuza ĝi estas). Ni bezonas nur registrojn respondecajn pri ebligado de ekstercentra horloĝo (Peripheral Clock Enable Registers). Unue, ni trovu la bazan adreson de la RCC, ĝi estas ĉe la komenco de la "Memoria Mapo":

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

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

Kaj tiam aŭ alklaku la ligilon, kie vi provas trovi ion en la plato, aŭ, multe pli bone, trairu la priskribojn de la ebligaj registroj el la sekcioj pri ebligi registrojn. Kie ni trovos RCC_APB1ENR kaj RCC_APB2ENR:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

Kaj ili, sekve, enhavas bitojn kiuj inkluzivas la horloĝon de SPI2, IOPB (I/O Port B) kaj alternativajn funkciojn (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;

La fina kodo troveblas tie.

Se vi havas la ŝancon kaj deziron testi, tiam konektu la DM634 tiel: DAI al PB15, DCK al PB13, LAT al PB14. Ni elektas la ŝoforon de 5 voltoj, ne forgesu konekti la grundojn.

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

STM8 PWM

PWM sur STM8

Kiam mi ĵus planis ĉi tiun artikolon, mi decidis, ekzemple, provi regi iun funkciecon de nekonata blato uzante nur datumfolion, por ke mi ne finiĝos kun ŝuisto sen botoj. STM8 estis ideala por ĉi tiu rolo: unue, mi havis kelkajn ĉinajn tabulojn kun STM8S103, kaj due, ĝi ne estas tre populara, kaj tial la tento legi kaj trovi solvon en la Interreto baziĝas sur la manko de tiuj ĉi solvoj.

La blato ankaŭ havas datumpaĝo и referenca manlibro RM0016, en la unua estas pinout kaj registra adresoj, en la dua - ĉio alia. STM8 estas programita en C en terura IDE ST Vida Evoluo.

Horloĝado kaj I/O

Defaŭlte, STM8 funkcias kun frekvenco de 2 MHz, ĉi tio devas esti korektita tuj.

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
HSI (Alta Rapida Interna) Horloĝo
La HSI-horloĝsignalo estas derivita de interna 16 MHz RC-oscilatoro kun programebla disigilo (1 ĝis 8). Ĝi estas agordita en la horloĝdivida registro (CLK_CKDIVR).
Notu: ĉe la komenco, HSI RC-oscilatoro kun dividanto de 8 estas elektita kiel la ĉefa fonto de la horloĝsignalo.

Ni trovas la registradreson en la datenfolio, la priskribon en refman kaj vidas, ke la registro devas esti forigita:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Ĉar ni funkcios PWM kaj konektos la LED-ojn, ni rigardu la pinout:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

La blato estas malgranda, multaj funkcioj estas suspenditaj sur la samaj pingloj. Kio estas en kvadrataj krampoj estas "alternativa funkcio", ĝi estas ŝanĝita per "opciobajtoj" (opciobajtoj) - io kiel Atmega fuzeoj. Vi povas ŝanĝi iliajn valorojn programe, sed ne necesas, ĉar La nova funkcio estas aktivigita nur post rekomenco. Estas pli facile uzi ST Visual Programmer (elŝutita kun Visual Develop), kiu povas ŝanĝi ĉi tiujn bajtojn. La pinout montras ke la CH1 kaj CH2 pingloj de la unua tempigilo estas kaŝitaj en kvadrataj krampoj; necesas agordi la AFR1 kaj AFR0-bitojn en STVP, kaj la dua ankaŭ translokigos la CH1-eligon de la dua tempigilo de PD4 al PC5.

Tiel, 6 pingloj kontrolos la LED-ojn: PC6, PC7 kaj PC3 por la unua tempigilo, PC5, PD3 kaj PA3 por la dua.

Agordi la I/O-pinglojn mem sur STM8 estas pli simpla kaj pli logika ol sur STM32:

  • konata de Atmega DDR-datendirekta registro (Registro de Datumoj): 1 = eligo;
  • la unua kontrolregistro CR1, kiam eligo, fiksas la puŝo-tiran reĝimon (1) aŭ malferman drenilon (0); ĉar mi konektas la LED-ojn al la blato per katodoj, mi lasas nulojn ĉi tie;
  • la dua kontrolregistro CR2, kiam eligo, fiksas la horloĝrapidecon: 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-agordo

Unue, ni difinu la terminojn:

  • PWM-Ofteco – frekvenco kun kiu la temporizilo batas;
  • Aŭtomata reŝargi, AR – aŭtomate ŝargebla valoro ĝis kiu kalkulos la tempigilo (pulsperiodo);
  • Ĝisdatigi Eventon, UEV – evento kiu okazas kiam la temporizilo kalkulis al AR;
  • PWM Duty Cycle - PWM-devociklo, ofte nomata "devofaktoro";
  • Kapti/Komparu Valoron – valoro por kapto/komparo, al kiu la tempigilo kalkulis faros ion (kaze de PWM, ĝi inversigas la eligsignalon);
  • Antaŭŝarĝo Valoro – antaŭŝarĝita valoro. Komparu valoron ne povas ŝanĝi dum la tempigilo tiktakos, alie la PWM-ciklo rompiĝos. Sekve, novaj elsenditaj valoroj estas metitaj en bufron kaj eltiritaj kiam la tempigilo atingas la finon de sia retronombrado kaj estas rekomencigita;
  • Rando-vicigita и Centre-vicigitaj reĝimoj - vicigo laŭ la limo kaj en la centro, sama kiel tiu de Atmel Rapida PWM и Fazĝusta PWM.
  • OCiREF, Eligo Komparu Referenca Signalo - referenco eligo signalo, fakte, kio aperas sur la responda pinglo en PWM reĝimo.

Kiel jam klaras de la pinout, du tempigiloj havas PWM-kapablojn - la unua kaj la dua. Ambaŭ estas 16-bitoj, la unua havas multajn kromajn funkciojn (precipe ĝi povas kalkuli kaj supren kaj malsupren). Ni bezonas ambaŭ labori egale, do mi decidis komenci per la evidente pli malriĉa dua, por ne hazarde uzi ion, kio ne estas tie. Iu problemo estas, ke la priskribo de la PWM-funkcio de ĉiuj tempigiloj en la referenca manlibro estas en la ĉapitro pri la unua tempigilo (17.5.7 PWM Mode), do vi devas salti tien kaj reen tra la dokumento la tutan tempon.

PWM sur STM8 havas gravan avantaĝon super PWM sur Atmega:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Limo Vicigita PWM
Konta agordo de malsupre ĝis supro
Desupra kalkulado estas aktiva se la DIR-bito en la registro TIM_CR1 estas forigita
Ekzemplo:
La ekzemplo uzas la unuan PWM-reĝimon. La PWM-referenca signalo OCiREF estas tenita alta tiel longe kiel TIM1_CNT < TIM1_CCRi. Alie ĝi bezonas malaltan nivelon. Se la kompara valoro en la registro TIM1_CCRi estas pli granda ol la valoro de aŭtoŝarĝo (registro TIM1_ARR), la signalo OCiREF estas tenita ĉe 1. Se la komparvaloro estas 0, OCiREF estas tenita ĉe nulo....

STM8 tempigilo dum ĝisdatigi eventon ĉekoj unue komparu valoron, kaj nur tiam produktas referencan signalon. La tempigilo de Atmega unue ŝraŭbas kaj poste komparas, rezultigante compare value == 0 la eligo estas kudrilo, kiu devas esti traktita iel (ekzemple, programe inversigante la logikon).

Do, kion ni volas fari: 8-bita PWM (AR == 255), kalkulante de malsupre ĝis supro, vicigon laŭ la limo. Ĉar la ampoloj estas konektitaj al la blato per katodoj, la PWM devus eligi 0 (LED ŝaltita) ĝis komparu valoron kaj 1 poste.

Ni jam legis pri iuj PWM-reĝimo, do ni trovas la bezonatan registron de la dua tempigilo serĉante en la referenca manlibro por ĉi tiu frazo (18.6.8 - TIMx_CCMR1):

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
110: Unua PWM-reĝimo - kiam oni kalkulas de malsupre ĝis supro, la unua kanalo estas aktiva dum TIMx_CNT < TIMx_CCR1. Alie, la unua kanalo estas neaktiva. [plie en la dokumento estas erara kopio-gluo de tempigilo 1] 111: Dua PWM-reĝimo – kiam oni kalkulas de malsupre supren, la unua kanalo estas neaktiva dum TIMx_CNT < TIMx_CCR1. Alie, la unua kanalo estas aktiva.

Ĉar la LED-oj estas konektitaj al la MK per katodoj, la dua reĝimo konvenas al ni (ankaŭ la unua, sed tion ni ankoraŭ ne scias).

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Bito 3 OC1PE: Ebligu la antaŭŝarĝon de pinglo 1
0: Preŝargi registron sur TIMx_CCR1 estas malŝaltita. Vi povas skribi al TIMx_CCR1 iam ajn. La nova valoro funkcias tuj.
1: Preŝargi registron sur TIMx_CCR1 estas ebligita. Leg/skribaj operacioj aliras la antaŭŝarĝan registron. La antaŭŝarĝita valoro TIMx_CCR1 estas ŝarĝita en la ombroregistron dum ĉiu ĝisdatiga evento.
*Noto: Por ke PWM-reĝimo funkciu ĝuste, la antaŭŝarĝaj registroj devas esti ebligitaj. Tio ne estas necesa en ununura signalreĝimo (la OPM-bito estas metita en la TIMx_CR1-registro).

Bone, ni ŝaltu ĉion, kion ni bezonas por la tri kanaloj de la dua tempigilo:

#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 konsistas el du ok-bitaj registroj, ĉio estas simpla:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

La dua tempigilo nur povas kalkuli de malsupre ĝis supro, vicigo laŭ la limo, nenio devas esti ŝanĝita. Ni agordu la frekvencdividilon, ekzemple, al 256. Por la dua tempigilo, la disigilo estas agordita en la registro TIM2_PSCR kaj estas potenco de du:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Restas nur ŝalti la konkludojn kaj la duan tempigilon mem. La unua problemo estas solvita per registroj Kapti/Komparu ebligi: estas du, tri kanaloj disigitaj trans ili malsimetrie. Ĉi tie ni ankaŭ povas lerni, ke eblas ŝanĝi la polusecon de la signalo, t.e. principe, eblis uzi PWM Mode 1. Ni skribas:

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

Kaj finfine, ni startas la tempigilon en la registro TIMx_CR1:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Ni skribu simplan analogon de AnalogWrite(), kiu translokigos la realajn valorojn al la tempigilo por komparo. La registroj estas nomitaj antaŭvideble Kapti/Komparu registrojn, estas du el ili por ĉiu kanalo: la malalt-ordaj 8 bitoj en TIM2_CCRxL kaj la alt-ordaj en TIM2_CCRxH. Ĉar ni kreis 8-bitan PWM, sufiĉas skribi nur la malplej signifajn bitojn:

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

La atentema leganto rimarkos, ke ni havas iomete difektan PWM, nekapabla produkti 100% plenigon (ĉe maksimuma valoro de 255, la signalo estas inversigita por unu tempigilo-ciklo). Por LED-oj tio ne gravas, kaj la atentema leganto jam povas diveni kiel ripari ĝin.

PWM sur la dua tempigilo funkcias, ni pluiru al la unua.

La unua tempigilo havas precize la samajn bitojn en la samaj registroj (nur tiuj bitoj kiuj restis "rezervitaj" en la dua tempigilo estas aktive uzataj en la unua por ĉiaj altnivelaj aferoj). Tial sufiĉas trovi la adresojn de la samaj registroj en la datumfolio kaj kopii la kodon. Nu, ŝanĝu la valoron de la frekvencdividilo, ĉar... la unua tempigilo volas ricevi ne potencon de du, sed precizan 16-bitan valoron en du registroj Antaŭskalisto Alta и malalte. Ni faras ĉion kaj... la unua tempigilo ne funkcias. Kio estas la problemo?

La problemo povas esti solvita nur per trarigardado de la tuta sekcio pri la kontrolregistroj de tempigilo 1, kie ni serĉas tiun, kiun la dua tempigilo ne havas. Estos 17.7.30 Rompi registro (TIM1_BKR), kie estas ĉi tiu bito:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Ebligu ĉefan eliron

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Tio estas ĉio certe nun, la kodo tie.

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

STM8 Multeksaĵo

Multipleksado sur STM8

La tria mini-projekto estas konekti ok RGB-LED-ojn al la dua tempigilo en PWM-reĝimo kaj igi ilin montri malsamajn kolorojn. Ĝi baziĝas sur la koncepto de LED-multiplexado, kiu estas, ke se vi ŝaltas kaj malŝaltas LED-ojn tre, tre rapide, ŝajnos al ni, ke ili estas konstante ŝaltitaj (persisto de vizio, inercio de vida percepto). Mi iam faris io tia sur Arduino.

La laboralgoritmo aspektas jene:

  • konektita la anodo de la unua RGB LED;
  • ŝaltis ĝin, sendante la necesajn signalojn al la katodoj;
  • atendis ĝis la fino de la PWM-ciklo;
  • konektita la anodo de la dua RGB LED;
  • ŝaltis ĝin...

Nu, ktp. Kompreneble, por bela funkciado necesas, ke la anodo estas konektita kaj la LED estas "ŝaltita" samtempe. Nu, aŭ preskaŭ. Ĉiukaze, ni devas skribi kodon, kiu eligos valorojn en tri kanaloj de la dua tempigilo, ŝanĝi ilin kiam UEV estas atingita, kaj samtempe ŝanĝi la nuntempe aktivan RGB-LED.

Ĉar LED-ŝanĝo estas aŭtomata, ni devas krei "videomemoron" de kiu la interrompa prizorganto ricevos datumojn. Ĉi tio estas simpla tabelo:

uint8_t colors[8][3];

Por ŝanĝi la koloron de specifa LED, sufiĉos skribi la postulatajn valorojn en ĉi tiun tabelon. Kaj la variablo respondecos pri la nombro de la aktiva LED

uint8_t cnt;

Demux

Por taŭga multipleksado, ni bezonas, sufiĉe strange, CD74HC238 demultiplexor. Demultiplexer - blato kiu efektivigas la funkciigiston en aparataro <<. Per tri enigpingloj (bitoj 0, 1 kaj 2) ni nutras al ĝi tri-bitan nombron X, kaj responde ĝi aktivigas elignumero (1<<X). La ceteraj enigaĵoj de la blato estas uzataj por skali la tutan dezajnon. Ni bezonas ĉi tiun blaton ne nur por redukti la nombron da okupataj pingloj de la mikroregilo, sed ankaŭ por sekureco - por ne hazarde ŝalti pli da LED-oj ol eble kaj ne bruligi la MK. La blato kostas unu pencon kaj ĉiam devas esti konservita en via hejma medikamento.

Nia CD74HC238 respondecas pri liverado de tensio al la anodo de la dezirata LED. En plenkreska plurkinejo, ĝi liverus tension al la kolono per P-MOSFET, sed en ĉi tiu demonstraĵo eblas rekte, ĉar ĝi tiras 20 mA, laŭ absolutaj maksimumaj taksoj en la datenfolio. De Datumbazo CD74HC238 ni bezonas pinouts kaj ĉi tiun trompfolion:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
H = alta tensionivelo, L = malalta tensionivelo, X – ne zorgu

Ni ligas E2 kaj E1 al grundo, E3, A0, A1 kaj A3 al pingloj PD5, PC3, PC4 kaj PC5 de STM8. Ĉar la supra tabelo enhavas kaj malaltajn kaj altajn nivelojn, ni agordas ĉi tiujn pinglojn kiel puŝtirajn pinglojn.

PWM

PWM sur la dua tempigilo estas agordita en la sama maniero kiel en la antaŭa rakonto, kun du diferencoj:

Unue, ni devas ebligi la interrompon Ĝisdatigi Eventon (UEV) kiu vokos funkcion kiu ŝanĝas la aktivan LED. Ĉi tio estas farita ŝanĝante la pecon Ĝisdatigi Interrompon Ebligi en registro kun diranta nomo

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
Interrompi ebligi registron

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

La dua diferenco rilatas al la fenomeno de multipleksado, kiel ekz fantomo – parazita brilo de diodoj. En nia kazo, ĝi povas aperi pro la fakto, ke la tempigilo, kaŭzinte interrompon sur la UEV, daŭre tiktas, kaj la interrompa prizorganto ne havas tempon por ŝanĝi la LED antaŭ ol la tempigilo komencas skribi ion al la pingloj. Por kontraŭbatali ĉi tion, vi devos inversigi la logikon (0 = maksimuma brilo, 255 = nenio estas lumigita) kaj eviti ekstremajn devociklajn valorojn. Tiuj. certigu, ke post UEV la LED-oj tute estingiĝas por unu PWM-ciklo.

Ŝanĝanta polusecon:

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

Evitu agordi r, g kaj b al 255 kaj memoru inversigi ilin kiam vi uzas ilin.

Interrompoj

La esenco de interrompo estas, ke sub certaj cirkonstancoj la blato ĉesas ekzekuti la ĉefan programon kaj vokas iun eksteran funkcion. Interrompoj okazas pro eksteraj aŭ internaj influoj, inkluzive de la tempigilo.

Kiam ni unue kreis projekton en ST Visual Develop, krom main.c ni ricevis fenestron kun mistera dosiero stm8_interrupt_vector.c, aŭtomate inkluzivita en la projekto. En ĉi tiu dosiero, funkcio estas asignita al ĉiu interrompo NonHandledInterrupt. Ni devas ligi nian funkcion al la dezirata interrompo.

La datenfolio havas tabelon de interrompaj vektoroj, kie ni trovas tiujn, kiujn ni bezonas:

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8
13 TIM2 ĝisdatigo/superfluo
14 TIM2 kapti/kompari

Ni devas ŝanĝi la LED ĉe UEV, do ni bezonas interrompon #13.

Sekve, unue, en la dosiero stm8_interrupt_vector.c ŝanĝu la defaŭltan nomon de la funkcio respondeca por interrompo n-ro 13 (IRQ13) al via propra:

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

Due, ni devos krei dosieron main.h kun la sekva enhavo:

#ifndef __MAIN_H
#define __MAIN_H

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

Kaj fine, skribu ĉi tiun funkcion en via 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;
}

Restas nur ebligi interrompojn. Ĉi tio estas farita uzante la asemblerkomandon rim — vi devos serĉi ĝin enen Manlibro pri Programado:

//enable interrupts
_asm("rim");

Alia asemblerkomando estas sim – malŝaltas interrompojn. Ili devas esti malŝaltitaj dum novaj valoroj estas skribitaj en la "videomemoro", por ke interrompo kaŭzita en la malĝusta momento ne difektu la tabelon.

Ĉiu kodo - sur GitHub.

Legante datumfoliojn 2: SPI sur STM32; PWM, tempigiloj kaj interrompoj sur STM8

Se almenaŭ iu trovas ĉi tiun artikolon utila, tiam mi ne vane skribis ĝin. Mi ĝojos ricevi komentojn kaj rimarkojn, mi provos respondi ĉion.

fonto: www.habr.com

Aldoni komenton