Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

В earste diel Ik besocht te fertellen hobby elektroanika yngenieurs dy't opgroeid út Arduino broek hoe en wêrom se moatte lêze datasheets en oare dokumintaasje foar microcontrollers. De tekst blykte grut te wêzen, dat ik haw tasein praktyske foarbylden yn in apart artikel sjen te litten. No, hy neamde himsels in molke paddestoel...

Hjoed sil ik jo sjen litte hoe't jo gegevensblêden brûke om frij ienfâldich, mar nedich foar in protte projekten, taken op STM32 (Blue Pill) en STM8-controllers op te lossen. Alle demo-projekten binne wijd oan myn favorite LED's, wy sille se yn grutte hoemannichten ljochtsje, wêrfoar wy allerhanne nijsgjirrige perifeare apparaten moatte brûke.

De tekst bleek wer enoarm te wêzen, dus foar it gemak meitsje ik de ynhâld:

STM32 Blue Pill: 16 LEDs mei DM634 bestjoerder
STM8: Seis PWM-pins ynstelle
STM8: 8 RGB LED's op trije pins, ûnderbrekt

Disclaimer: ik bin gjin yngenieur, ik pretend net te hawwen djippe kennis yn elektroanika, it artikel is bedoeld foar amateurs lykas my. Eins haw ik mysels twa jier lyn beskôge as de doelgroep. As immen my doe ferteld hie dat datablêden op in ûnbekende chip net skriklik wiene om te lêzen, dan hie ik net in protte tiid bestege oan it sykjen nei guon stikken koade op it ynternet en it útfine fan krukken mei skjirre en plakband.

De fokus fan dit artikel is op gegevensblêden, net op projekten, sadat de koade miskien net heul kreas en faak krap is. De projekten sels binne hiel ienfâldich, hoewol geskikt foar in earste kunde mei de nije chip.

Ik hoopje dat myn artikel immen sil helpe yn in ferlykber stadium fan ûnderdompeling yn 'e hobby.

STM32

16 LED's mei DM634 en SPI

In lyts projekt mei help fan Blue Pill (STM32F103C8T6) en DM634 LED driver. Mei help fan datasheets, wy sille útfine de bestjoerder, STM IO havens en konfigurearje SPI.

DM634

Taiwaneeske chip mei 16 16-bit PWM útgongen, kin wurde ferbûn yn keatlingen. It low-end 12-bit model is bekend fan in húslik projekt Lightpack. Op ien kear, kieze tusken de DM63x en de bekende TLC5940, keas ik DM foar ferskate redenen: 1) TLC op Aliexpress is perfoarst falsk, mar dizze is net; 2) DM hat in autonome PWM mei in eigen frekwinsjegenerator; 3) it koe goedkeap kocht wurde yn Moskou, ynstee fan te wachtsjen op in pakket fan Ali. En, fansels, it wie ynteressant om te learen hoe't jo de chip sels kontrolearje, ynstee fan in klearmakke bibleteek te brûken. Chips wurde no benammen presintearre yn it SSOP24-pakket; se binne maklik te solderjen oan in adapter.

Sûnt de fabrikant is Taiwaneesk, datasheet de chip is skreaun yn Sineesk Ingelsk, wat betsjut dat it sil wêze leuk. Earst sjogge wy nei de pinout (Pin Ferbining) om te begripen hokker skonk wêrmei te ferbinen is, en in beskriuwing fan 'e pinnen (Pin Beskriuwing). 16 pins:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
DC Sink boarnen (iepen drain)

Sinke / Iepen-drain útfier - drain; boarne fan ynstreamende stroom; de útfier is ferbûn mei grûn yn 'e aktive steat - de LED's binne ferbûn mei de bestjoerder troch kathodes. Elektrysk is dit fansels gjin "iepen drain" (iepen drain), mar yn gegevensblêden wurdt dizze oantsjutting foar pinnen yn drainmodus faak fûn.

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Eksterne wjerstannen tusken REXT en GND om de hjoeddeistige wearde fan 'e útfier yn te stellen

In referinsje wjerstân wurdt ynstallearre tusken de REXT pin en grûn, dy't kontrolearret de ynterne wjerstân fan de útgongen, sjoch de grafyk op side 9 fan it datasheet. Yn 'e DM634 kin dizze wjerstân ek wurde regele troch software, it ynstellen fan de totale helderheid (globale helderheid); Ik sil yn dit artikel net yngean op details, ik sil hjir gewoan in wjerstân fan 2.2 - 3 kOhm sette.

Om te begripen hoe't jo de chip kontrolearje, litte wy nei de beskriuwing fan 'e apparaatynterface sjen:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

Ja, hjir is it, Sineesk Ingelsk yn al syn gloarje. It oersetten fan dit is problematysk, jo kinne it begripe as jo wolle, mar d'r is in oare manier - sjoch hoe't de ferbining mei de funksjoneel ferlykbere TLC5940 wurdt beskreaun yn it datablêd:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
... Allinich trije pins binne nedich om gegevens yn it apparaat yn te fieren. De opkommende râne fan it SCLK-sinjaal ferpleatst de gegevens fan 'e SIN-pin nei it ynterne register. Neidat alle gegevens binne laden, in koarte hege XLAT sinjaal latches de sequentially oerdroegen gegevens yn de ynterne registers. Ynterne registers binne poarten trigger troch it XLAT-sinjaalnivo. Alle gegevens wurde oerdroegen meast wichtige bit earst.

Latch - slot / slot / slot.
Rising râne - foaroansteande râne fan 'e pols
MSB earst - meast wichtige (meast lofts) bytsje foarút.
gegevens te klokken - gegevens opfolgjend ferstjoere (bytsje foar bytsje).

It wurd klink wurdt faak fûn yn 'e dokumintaasje foar chips en wurdt oerset op ferskate manieren, dus foar it begryp sil ik mysels tastean

in lyts edukatyf programmaDe LED-bestjoerder is yn wêzen in skiftregister. "Skift" (feroarje) yn 'e namme - bitwize beweging fan gegevens binnen it apparaat: elke nije bit dy't nei binnen skod wurdt, triuwt de hiele ketting foarút. Om't gjinien chaotysk knipperjen fan 'e LED's yn' e skift wol observearje, fynt it proses plak yn bufferregisters skieden fan 'e wurkregisters troch in damper (klink) is in soarte fan wachtkeamer dêr't de bits wurde regele yn de winske folchoarder. As alles klear is, iepenet de sluter en de bits geane oan it wurk, ferfange de foarige batch. Wurd klink Yn de dokumintaasje foar mikrocircuits betsjut hast altyd sa'n damper, nettsjinsteande hokker kombinaasjes it wurdt brûkt.

Dat, gegevensoerdracht nei de DM634 wurdt sa útfierd: set de DAI-ynfier op 'e wearde fan' e meast wichtige bit fan 'e fiere LED, lûk DCK op en del; set de DAI ynfier nei de wearde fan de folgjende bit, pull DCK; en sa fierder oant alle bits binne oerbrocht (ynklokte), wêrnei't wy LAT lûke. Dit kin mei de hân dien wurde (bitsje), mar it is better om in spesjale SPI-ynterface te brûken dy't hjirfoar oanpast is, om't it op ús STM32 yn twa eksimplaren wurdt presintearre.

Blauwe pil STM32F103

Ynliedend: STM32-controllers binne folle komplekser dan Atmega328 dan se miskien lykje eng. Om redenen fan enerzjybesparring wurde hast alle perifeare apparaten by it begjin útskeakele, en de klokfrekwinsje is 8 MHz fan 'e ynterne boarne. Gelokkich skreaunen STM-programmeurs koade dy't de chip bringt nei de "berekkene" 72 MHz, en de auteurs fan alle IDE's dy't ik ken hawwe it opnommen yn 'e inisjalisaasjeproseduere, dus wy hoege net te klokken (mar jo kinne as jo echt wolle). Mar jo moatte de perifeare apparaten ynskeakelje.

Dokumintaasje: Blue Pill is foarsjoen fan de populêre STM32F103C8T6-chip, d'r binne twa nuttige dokuminten foar:

Yn it datablêd kinne wy ​​​​ynteressearre wêze yn:

  • Pinouts - chip pinouts - foar it gefal dat wy beslute om de buorden sels te meitsjen;
  • Memory Map - ûnthâld map foar in spesifike chip. It Reference Manual hat in kaart foar de hiele line, en it neamt registers dy't ús net hawwe.
  • Tabel mei pindefinysjes - listje de wichtichste en alternative funksjes fan pins; foar de "blauwe pil" kinne jo handiger foto's fine op it ynternet mei in list mei pinnen en har funksjes. Dêrom googleje wy daliks Blue Pill pinout en hâlde dizze foto by de hân:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
NB: der stie in flater yn de foto fan it ynternet, dy’t yn de reaksjes notearre waard, tank dêrfoar. De foto is ferfongen, mar dit is in les - it is better om ynformaasje te kontrolearjen net út gegevensblêden.

Wy ferwiderje it gegevensblêd, iepenje it Reference Manual, en fan no ôf brûke wy allinich it.
Proseduere: wy omgean mei standert ynfier / útfier, konfigurearje SPI, oansette de nedige perifeare apparaten.

Ynput Utfier

Op 'e Atmega328 wurdt I / O ekstreem ienfâldich ymplementearre, en dêrom kin de oerfloed fan STM32-opsjes betiizjend wêze. No hawwe wy allinich konklúzjes nedich, mar sels dizze hawwe fjouwer opsjes:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
iepen drain, push-pull, alternative push-pull, alternative iepen drain

"Lûke triuwe" (triuwe-lûke) is de gewoane útfier fan 'e Arduino, de pin kin de wearde HIGH of LOW nimme. Mar mei "iepen drain" binne der swierrichheden, hoewol yn feite alles hjir ienfâldich is:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Utfierkonfiguraasje / as de poarte is tawiisd oan útfier: / útfierbuffer ynskeakele: / - iepen drainmodus: "0" yn it útfierregister stelt N-MOS yn, "1" yn it útfierregister lit de poarte yn Hi-Z-modus ( P-MOS is net aktivearre) / - push-pull modus: "0" yn it útfierregister aktivearret N-MOS, "1" yn it útfierregister aktivearret P-MOS.

Al it ferskil tusken iepen drain (iepen drain) fan "push-pull" (triuwe-lûke) is dat yn 'e earste pin de HIGH-tastân net akseptearje kin: by it skriuwen fan ien nei it útfierregister, giet it yn hege fersetmodus (hege impedânsje, Hoi-Z). By it skriuwen fan nul, gedraacht de pin itselde yn beide modi, sawol logysk as elektrysk.

Yn normale útfiermodus stjoert de pin gewoan de ynhâld fan it útfierregister út. Yn it "alternatyf" wurdt it regele troch de oerienkommende perifeare apparaten (sjoch 9.1.4):

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
As in haven bit is konfigurearre as in alternative funksje pin, de pin register is útskeakele en de pin is ferbûn mei de perifeare pin.

Alternative funksjonaliteit fan elke pin wurdt beskreaun yn Pin Definysjes It gegevensblêd is op 'e ynladen ôfbylding. Op de fraach wat te dwaan as in pin hat ferskate alternative funksjes, it antwurd wurdt jûn troch in fuotnoat yn it datablêd:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
As meardere perifeare apparaten deselde pin brûke, om konflikt tusken alternative funksjes te foarkommen, moat mar ien perifeare apparaat tagelyk brûkt wurde, wikselje mei de perifeare klok ynskeakelje bit (yn it passende RCC-register).

Uteinlik hawwe pins yn útfiermodus ek in kloksnelheid. Dit is in oare enerzjybesparjende funksje; yn ús gefal sette wy it gewoan op maksimaal en ferjitte it.

Dus: wy brûke SPI, wat betsjut dat twa pins (mei gegevens en mei in kloksinjaal) "alternatyf push-pull-funksje" moatte wêze, en in oare (LAT) moat "gewoane push-pull" wêze. Mar foardat se har tawize, litte wy omgean mei SPI.

SPI

In oar lyts edukatyf programma

SPI of Serial Peripheral Interface (seriële perifeare ynterface) is in ienfâldige en tige effektive ynterface foar it ferbinen fan in MK mei oare MK's en de bûtenwrâld yn it algemien. It prinsipe fan syn wurking is al beskreaun hjirboppe, wêr oer de Sineeske LED-bestjoerder (yn de referinsje hantlieding, sjoch paragraaf 25). SPI kin operearje yn master ("master") en slave ("slave") modus. SPI hat fjouwer basiskanalen, wêrfan net allegear kinne wurde brûkt:

  • MOSI, Master Output / Slave Input: dizze pin stjoert gegevens yn master modus, en ûntfangt gegevens yn slave modus;
  • MISO, Master Input / Slave Output: krekt oarsom, it ûntfangt yn 'e master, en stjoert yn' e slaaf;
  • SCK, Serial Clock: stelt de frekwinsje fan gegevens oerdracht yn 'e master of ûntfangt in klok sinjaal yn' e slaaf. Yn essinsje slaan beats;
  • SS, Slave Selektearje: mei help fan dit kanaal wit de slaaf dat der wat fan him woe. Op STM32 hjit it NSS, dêr't N = negatyf, d.w.s. de controller wurdt in slaaf as der grûn yn dit kanaal. It kombinearret goed mei de Open Drain Output-modus, mar dat is in oar ferhaal.

Lykas al it oare is SPI op STM32 ryk oan funksjonaliteit, dat makket it wat lestich om te begripen. It kin bygelyks net allinich wurkje mei SPI, mar ek mei in I2S-ynterface, en yn 'e dokumintaasje binne har beskriuwingen mingd, it is nedich om it oerskot op 'e tiid ôf te snijen. Us taak is ekstreem ienfâldich: wy moatte gewoan gegevens ferstjoere mei allinich MOSI en SCK. Wy geane nei paragraaf 25.3.4 (heal-duplex kommunikaasje, heal-duplex kommunikaasje), dêr't wy fine 1 klok en 1 unidirectional data wire (1 kloksinjaal en 1 ienrjochtingsdatastream):

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Yn dizze modus brûkt de applikaasje SPI yn 'e modus allinich foar ferstjoeren of allinich ûntfangen. / Allinnich útstjoermodus is fergelykber mei dupleksmodus: gegevens wurde oerdroegen op 'e útstjoerpin (MOSI yn mastermodus of MISO yn slavemodus), en de ûntfangstpin (respektyflik MISO of MOSI) kin brûkt wurde as in gewoane I/O-pin . Yn dit gefal hoecht de applikaasje allinich de Rx-buffer te negearjen (as it wurdt lêzen, sil d'r gjin oerdroegen gegevens wêze).

Geweldich, de MISO-pin is fergees, litte wy it LAT-sinjaal dermei ferbine. Litte wy nei Slave Select sjen, dy't op 'e STM32 programmatysk kin wurde regele, wat ekstreem handich is. Wy lêze de paragraaf mei deselde namme yn paragraaf 25.3.1 SPI Algemiene beskriuwing:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Software kontrôle NSS (SSM = 1) / Slave seleksje ynformaasje is befette yn de SSI bit fan de SPI_CR1 register. De eksterne NSS-pin bliuwt fergees foar oare applikaasjebehoeften.

It is tiid om te skriuwen nei de registers. Ik besleat SPI2 te brûken, sykje nei syn basisadres yn it datablêd - yn seksje 3.3 Memory Map:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

No, lit ús begjinne:

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

Iepenje seksje 25.3.3 mei de selsferklearjende titel "SPI konfigurearje yn Master Mode":

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

1. Stel de serial klok frekwinsje mei bits BR [2: 0] yn de SPI_CR1 register.

De registers wurde sammele yn 'e referinsjehânlieding mei deselde namme. Adres ferskowing (Adres offset) foar CR1 - 0x00, standert wurde alle bits wiske (Weromsette wearde 0x0000):

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

De BR-bits sette de controller-klokdeler yn, sadat de frekwinsje bepale wêrop de SPI sil operearje. Us STM32-frekwinsje sil 72 MHz wêze, de LED-bestjoerder, neffens syn gegevensblêd, wurket mei in frekwinsje fan maksimaal 25 MHz, dus wy moatte dielen troch fjouwer (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. Stel de CPOL- en CPHA-bits yn om de relaasje tusken gegevensferfier en seriële kloktiming te definiearjen (sjoch diagram op side 240)

Om't wy hjir in gegevensblêd lêze en net nei skema's sjogge, litte wy de tekstbeskriuwing fan 'e CPOL- en CPHA-bits op side 704 (SPI Algemiene beskriuwing) in tichterby besjen:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Klokfaze en polariteit
Mei de CPOL- en CPHA-bits fan it SPI_CR1-register kinne jo programmatysk fjouwer timingrelaasjes selektearje. De CPOL (klokpolariteit) bit kontrolearret de steat fan it kloksinjaal as gjin gegevens wurde oerdroegen. Dit bit kontrolearret de master- en slavemodus. As CPOL wurdt reset, de SCK pin is leech yn rest modus. As de CPOL-bit is ynsteld, is de SCK-pin heech yn 'e rêstmodus.
As de CPHA (klok faze) bit wurdt ynsteld, is de hege bit trap strobe de twadde râne fan de SCK sinjaal (falle as CPOL is dúdlik, opkommende as CPOL is ynsteld). De gegevens wurde fêstlein troch de twadde feroaring yn it kloksinjaal. As de CPHA bit is dúdlik, de hege bit trap strobe is de opkommende râne fan de SCK sinjaal (fallende râne as CPOL is ynsteld, opkommende râne as CPOL wurdt wiske). Gegevens wurde fêstlein by de earste feroaring yn it kloksinjaal.

Nei't dizze kennis opnommen is, komme wy ta de konklúzje dat beide bits nullen moatte bliuwe, om't Wy wolle dat it SCK-sinjaal leech bliuwt as it net yn gebrûk is, en gegevens wurde oerdroegen oan 'e opkommende râne fan' e pols (sjoch Fig. Rising Edge yn it DM634-datablêd).

Trouwens, hjir hawwe wy foar it earst in skaaimerk fan 'e wurdskat yn ST-datasheets tsjinkaam: dêryn is de sin "set it bit nei nul" skreaun in bytsje weromsette, en net in bytsje skjin te meitsjen, lykas bygelyks Atmega.

3. Stel it DFF-bit yn om te bepalen oft it gegevensblok 8-bit of 16-bit formaat is

Ik naam spesifyk in 16-bit DM634 om net te bemuoien mei it ferstjoeren fan 12-bit PWM-gegevens, lykas de DM633. It makket sin om DFF op ien te setten:

#define DFF         0x0800

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

4. Konfigurearje it LSBFIRST-bit yn it SPI_CR1-register om it blokformaat te bepalen

LSBFIRST, sa't de namme al fermoeden docht, konfigurearret oerdracht mei it minst signifikante bit earst. Mar DM634 wol ûntfange gegevens begjinnend fan de meast wichtige bit. Dêrom litte wy it weromsette.

5. Yn hardware modus, as ynfier fan de NSS pin is nedich, jilde in hege sinjaal oan de NSS pin tidens de hiele byte oerdracht sequence. Yn NSS software modus, set de SSM en SSI bits yn de SPI_CR1 register. As de NSS-pin moat wurde brûkt as in útfier, moat allinich it SSOE-bit ynsteld wurde.

Ynstallearje SSM en SSI om de NSS-hardwaremodus te ferjitten:

#define SSI         0x0100
#define SSM         0x0200

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

6. De MSTR- en SPE-bits moatte ynsteld wurde (se bliuwe allinich ynsteld as it NSS-sinjaal heech is)

Eigentlik, mei dizze bits oanwize wy ús SPI as in master en skeakelje it yn:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI is konfigurearre, lit ús fuortendaliks skriuwe funksjes dy't stjoere bytes nei de bestjoerder. Trochgean mei lêzen 25.3.3 "SPI konfigurearje yn mastermodus":

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Gegevens oerdracht oarder
Oerdracht begjint as in byte wurdt skreaun nei de Tx buffer.
De gegevensbyte wurdt laden yn it skiftregister by parallel modus (út de ynterne bus) tidens de oerdracht fan de earste bit, wêrnei't it wurdt oerdroegen oan sekwinsjele MOSI pin modus, earste of lêste bit foarút ôfhinklik fan de ynstelling fan de LSBFIRST bit yn de CPI_CR1 register. De TXE flagge wurdt ynsteld nei gegevens oerdracht fan Tx buffer nei ferskowingsregister, en genereart ek in ûnderbrekking as it TXEIE-bit yn it CPI_CR1-register is ynsteld.

Ik markearre in pear wurden yn 'e oersetting om omtinken te jaan oan ien funksje fan' e SPI-ymplemintaasje yn STM-controllers. Op Atmega de TXE flagge (Tx leech, Tx is leech en klear om gegevens te ûntfangen) wurdt pas ynsteld nei't de hiele byte ferstjoerd is út. En hjir dizze flagge wurdt ynsteld neidat de byte is ynfoege yn de ynterne shift register. Sûnt it wurdt skood dêr mei alle bits tagelyk (yn parallel), en dan de gegevens wurdt oerdroegen sequentially, TXE wurdt ynsteld foardat de byte wurdt folslein ferstjoerd. Dit is wichtich omdat yn it gefal fan ús LED-bestjoerder moatte wy de LAT-pin nei it ferstjoeren lûke всех gegevens, d.w.z. De TXE flagge allinnich sil net genôch wêze foar ús.

Dit betsjut dat wy in oare flagge nedich hawwe. Litte wy nei 25.3.7 sjen - "Statusflaggen":

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
<…>
Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
BUSY flagge
De BSY-flagge wurdt ynsteld en wiske troch hardware (it skriuwen nei it hat gjin effekt). De BSY flagge jout de steat fan de SPI kommunikaasje laach.
It reset:
as de oerdracht foltôge is (útsein yn mastermodus as de oerdracht kontinu is)
wannear SPI is útskeakele
as in mastermodusflater optreedt (MODF=1)
As de oerdracht net kontinu is, wurdt de BSY-flagge wiske tusken elke gegevensoerdracht

Okee, dit komt goed fan pas. Litte wy útfine wêr't de Tx-buffer leit. Om dit te dwaan, lês "SPI Data Register":

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Bits 15:0 DR[15:0] Data Register
Gegevens ûntfongen of gegevens dy't oerdroegen wurde.
It gegevensregister is ferdield yn twa buffers - ien foar skriuwen (ferstjoerbuffer) en ien foar lêzen (ûntfangbuffer). Skriuwen nei it gegevensregister skriuwt nei de Tx-buffer, en it lêzen fan it gegevensregister sil de wearde weromjaan yn 'e Rx-buffer.

No, en it statusregister, wêr't de flaggen TXE en BSY binne fûn:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

Wy skriuwe:

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

No, om't wy 16 kear twa bytes moatte ferstjoere, neffens it oantal LED-bestjoerderútgongen, sa'n ding:

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

Mar wy witte net hoe te lûken de LAT pin noch, dus wy geane werom nei I / O.

Pins tawize

Yn 'e STM32F1 binne de registers ferantwurdlik foar de steat fan' e pins frij ûngewoan. It is dúdlik dat der mear fan harren as Atmega, mar se binne ek oars as oare STM chips. Seksje 9.1 Algemiene beskriuwing fan GPIO:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Elk fan 'e algemiene doel I / O havens (GPIO) hat twa 32-bit konfiguraasjeregisters (GPIOx_CRL en GPIOx_CRH), twa 32-bit gegevensregisters (GPIOx_IDR en GPIOx_ODR), in 32-bit set-/resetregister (GPIOx_BSRR), in 16-bit resetregister (GPIOx_BRR) en in 32- bit blocking register (GPIOx_LCKR).

De earste twa registers binne ûngewoan, en ek frij ûngemaklik, om't de 16-poarte-pinnen oer har binne ferspraat yn in "fjouwer bits per broer" opmaak. Dy. pins nul oant sân binne yn CRL, en de rest binne yn CRH. Tagelyk befetsje de oerbleaune registers mei súkses de bits fan alle pins fan 'e haven - faak oerbleaun heal "reservearre".

Foar ienfâld, litte wy begjinne fan 'e ein fan' e list.

Wy hawwe gjin blokkearjende register nedich.

De set- en resetregisters binne frij grappich om't se inoar foar in part duplisearje: jo kinne alles allinich skriuwe yn BSRR, wêr't de hegere 16 bits de pin weromsette op nul, en de legere wurde ynsteld op 1, of jo kinne ek brûk BRR, wêrfan de legere 16 bits allinich de pin weromsette. Ik hâld fan 'e twadde opsje. Dizze registers binne wichtich om't se atomêre tagong jouwe ta pinnen:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Atoomset of weromsette
D'r is gjin ferlet om interrupts út te skeakeljen by it programmearjen fan GPIOx_ODR op it bitnivo: ien of mear bits kinne wurde feroare mei in inkele atomêre skriuwoperaasje APB2. Dit wurdt berikt troch it skriuwen fan in "1" nei it set / reset register (GPIOx_BSRR of, foar reset allinne, GPIOx_BRR) fan it bit dat moat wurde feroare. Oare bits sille ûnferoare bliuwe.

De gegevensregisters hawwe frij selsferklearjende nammen - IDR = Ynfier Rjochtingsregister, ynfierregister; ODR = útfier Rjochtingsregister, útfierregister. Wy sille se net nedich wêze yn it hjoeddeistige projekt.

En as lêste, kontrôle registers. Om't wy ynteressearre binne yn 'e twadde SPI-pins, nammentlik PB13, PB14 en PB15, sjogge wy fuortendaliks nei CRH:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

En wy sjogge dat wy wat moatte skriuwe yn bits fan 20 oant 31.

Wy hawwe hjirboppe al útfûn wat wy wolle fan pins, dus hjir sil ik sûnder in skermôfbylding dwaan, ik sil gewoan sizze dat MODE de rjochting spesifisearret (ynfier as beide bits binne ynsteld op 0) en pinsnelheid (wy moatte 50MHz, d.w.s. beide pin nei "1"), en CNF stelt de modus: reguliere "push-pull" - 00, "alternatyf" - 10. Standert, sa't wy sjogge hjirboppe, alle pins hawwe de tredde bit fan 'e boaiem (CNF0), it set se yn modus driuwende ynput.

Om't ik fan plan om wat oars te dwaan mei dizze chip, haw ik foar ienfâld alle mooglike MODE- en CNF-wearden definieare foar sawol de legere as boppeste kontrôleregisters.

Somehow like this

#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

Us pinnen lizze op haven B (basisadres - 0x40010C00), koade:

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

En dêrtroch kinne jo definysjes skriuwe foar LAT, dy't sille wurde twitched troch de BRR- en BSRR-registers:

/*** 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 gewoan troch inertia, it hat altyd sa west, lit it bliuwe)

No is alles geweldich, mar it wurket net. Om't dit STM32 is, besparje se elektrisiteit, wat betsjut dat jo it klokken fan 'e fereaske perifeare apparaten ynskeakelje moatte.

Skeakelje it klokken oan

It horloazje, ek wol bekend as Clock, is ferantwurdlik foar it klokken. En wy koene de ôfkoarting RCC al fernimme. Wy sykje it yn 'e dokumintaasje: dit is Reset en Clock Control.

Lykas hjirboppe sein waard, lokkigernôch, waard it dreechste diel fan it klokûnderwerp foar ús dien troch minsken fan STM, wêrfoar't wy har tige tankje (ik jou nochris in keppeling nei De webside fan Di Halt, om dúdlik te meitsjen hoe betiizjend it is). Wy hawwe allinich registers nedich dy't ferantwurdlik binne foar it ynskeakeljen fan perifeare klokken (Peripheral Clock Enable Registers). Litte wy earst it basisadres fan 'e RCC fine, it is oan it begjin fan' e "Memory Map":

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

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

En dan of klikje op de keppeling wêr't jo besykje wat yn 'e plaat te finen, of, folle better, troch de beskriuwingen fan' e aktivearjende registers te gean út 'e seksjes oer ynskeakelje registers. Wêr't wy RCC_APB1ENR en RCC_APB2ENR fine:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

En se befetsje dêrtroch bits dy't it klokken fan SPI2, IOPB (I/O-poarte B) en alternative funksjes (AFIO) omfetsje.

#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 definitive koade kin fûn wurde hjir.

As jo ​​​​de kâns en winsk hawwe om te testen, ferbine dan de DM634 sa: DAI nei PB15, DCK nei PB13, LAT nei PB14. Wy stjoere de bestjoerder fan 5 volt, ferjit net om de grûn te ferbinen.

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

STM8 PWM

PWM op STM8

Doe't ik dit artikel krekt plande, besleat ik, as foarbyld, om te besykjen om wat funksjonaliteit fan in ûnbekende chip te behearjen mei allinich in gegevensblêd, sadat ik net mei in skuonmakker sûnder laarzen komme soe. STM8 wie ideaal foar dizze rol: as earste hie ik in pear Sineeske boerden mei STM8S103, en twadde, it is net heul populêr, en dêrom is de ferlieding om te lêzen en in oplossing te finen op it ynternet op it ûntbrekken fan dizze oplossingen.

De chip hat ek datasheet и referinsje hantlieding RM0016, yn 'e earste is d'r pinout en registeradressen, yn' e twadde - al it oare. STM8 wurdt programmearre yn C yn in skriklike IDE ST Visual Develop.

Klokken en I/O

Standert wurket STM8 op in frekwinsje fan 2 MHz, dit moat fuortendaliks korrizjearre wurde.

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
HSI (High Speed ​​​​Interne) Klok
It HSI-kloksinjaal is ôflaat fan in ynterne 16 MHz RC-oscillator mei in programmeerbere divider (1 oant 8). It is ynsteld yn de klok divider register (CLK_CKDIVR).
Opmerking: oan it begjin wurdt in HSI RC-oscillator mei in divider fan 8 selektearre as de liedende boarne fan it kloksinjaal.

Wy fine it registeradres yn it datablêd, de beskriuwing yn refman en sjogge dat it register moat wurde wiske:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Om't wy PWM sille útfiere en de LED's ferbine, litte wy nei de pinout sjen:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

De chip is lyts, in protte funksjes wurde ophongen op deselde pins. Wat is yn fjouwerkante heakjes is "alternatyf funksjonaliteit", it wurdt oerskeakele troch "opsje bytes" (opsje bytes) - sokssawat as Atmega fuses. Jo kinne har wearden programmatysk feroarje, mar it is net nedich, om't De nije funksjonaliteit wurdt allinich aktivearre nei in herstart. It is makliker te brûken ST Visual Programmer (ynladen mei Visual Develop), dat kin feroarje dizze bytes. De pinout lit sjen dat de CH1 en CH2 pins fan de earste timer binne ferburgen yn fjouwerkante heakjes; it is nedich om de AFR1- en AFR0-bits yn STVP yn te stellen, en de twadde sil ek de CH1-útfier fan 'e twadde timer oerdrage fan PD4 nei PC5.

Sa sille 6 pins de LED's kontrolearje: PC6, PC7 en PC3 foar de earste timer, PC5, PD3 en PA3 foar de twadde.

It ynstellen fan de I/O-pins sels op STM8 is ienfâldiger en logysker dan op STM32:

  • bekend fan Atmega DDR data rjochtingsregister (Data Rjochting Register): 1 = útfier;
  • de earste kontrôle register CR1, doe't útfier, stelt de push-pull modus (1) of iepen drain (0); sûnt ik ferbine de LEDs oan de chip mei cathodes, Ik lit nullen hjir;
  • de twadde kontrôle register CR2, doe't útfier, stelt de klok snelheid: 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 ynstelling

Litte wy earst de termen definiearje:

  • PWM Frekwinsje - frekwinsje wêrmei't de timer tikket;
  • Auto-reload, AR - automatysk laden wearde oant wêr't de timer sil telle (pulsperioade);
  • Update Event, UEV - in evenemint dat plakfynt as de timer hat teld ta AR;
  • PWM Duty Cycle - PWM duty cycle, faak neamd "duty factor";
  • Capture / Ferlykje wearde - wearde foar capture / ferliking, dêr't de timer hat teld sil wat dwaan (yn it gefal fan PWM, it omkeart it útfiersinjaal);
  • Preload wearde - preloaded wearde. Ferlykje wearde kin net feroarje wylst de timer tikt, oars sil de PWM-syklus brekke. Dêrom wurde nije útstjoerde wearden yn in buffer pleatst en útlutsen as de timer it ein fan syn countdown berikt en weromset wurdt;
  • Edge-aligned и Midden-ôfstimd modi - ôfstimming lâns de grins en yn it sintrum, itselde as Atmel's Fluch PWM и Fase-korrekte PWM.
  • OCiREF, Utfier Ferlykje Reference Signal - de referinsje útfier sinjaal, yn feite, wat ferskynt op de oerienkommende pin yn PWM modus.

Lykas al dúdlik is fan 'e pinout, hawwe twa timers PWM-mooglikheden - de earste en de twadde. Beide binne 16-bit, de earste hat in protte ekstra funksjes (benammen it kin sawol omheech en omleech telle). Wy moatte beide gelyk wurkje, dat ik besleat om te begjinnen mei de fansels earmere twadde, om net per ongeluk eat te brûken dat der net is. Guon probleem is dat de beskriuwing fan de PWM funksjonaliteit fan alle timers yn de referinsje hantlieding is yn it haadstik oer de earste timer (17.5.7 PWM Mode), dus jo moatte springe hinne en wer troch it dokumint hiele tiid.

PWM op STM8 hat in wichtich foardiel boppe PWM op Atmega:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Boundary Aligned PWM
Account konfiguraasje fan ûnderen nei boppen
Bottom-up tellen is aktyf as de DIR bit yn it TIM_CR1 register is wiske
Foarbyld:
It foarbyld brûkt de earste PWM-modus. It PWM-referinsjesinjaal OCiREF wurdt heech hâlden sa lang as TIM1_CNT < TIM1_CCRi. Oars nimt it in leech nivo. As de fergelikingswearde yn it TIM1_CCRi-register grutter is dan de autoloadwearde (TIM1_ARR-register), wurdt it OCiREF-sinjaal op 1 hâlden. As de fergelikingswearde 0 is, wurdt OCiREF op nul hâlden....

STM8 timer tidens update evenemint kontrolearret earst ferlykje wearde, en pas dan produsearret in ferwizing sinjaal. Atmega's timer skroeven earst op en fergeliket dan, wat resulteart yn compare value == 0 de útfier is in naald, dy't op ien of oare manier behannele wurde moat (bygelyks troch programmatysk de logika omkeare).

Dus wat wy wolle dwaan: 8-bit PWM (AR == 255), telle fan ûnderen nei boppen, ôfstimming lâns de grins. Sûnt de gloeilampen binne ferbûn mei de chip troch kathodes, de PWM moat útfiere 0 (LED oan) oant ferlykje wearde en 1 nei.

Wy hawwe al lêzen oer guon PWM-modus, dus wy fine it fereaske register fan 'e twadde timer troch te sykjen yn' e referinsje hantlieding foar dizze frase (18.6.8 - TIMx_CCMR1):

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
110: Earste PWM-modus - by it tellen fan ûnderen nei boppen, is it earste kanaal aktyf wylst TIMx_CNT <TIMx_CCR1. Oars is it earste kanaal ynaktyf. [fierder yn it dokumint is d'r in ferkearde copy-paste fan timer 1] 111: Twadde PWM-modus - by it tellen fan ûnderen nei boppen, is it earste kanaal ynaktyf wylst TIMx_CNT <TIMx_CCR1. Oars is it earste kanaal aktyf.

Sûnt de LED's binne ferbûn mei de MK troch kathodes, past de twadde modus ús (de earste ek, mar wy witte dat noch net).

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Bit 3 OC1PE: Ynskeakelje pin 1 preload
0: Preload register op TIMx_CCR1 is útskeakele. Jo kinne op elk momint skriuwe nei TIMx_CCR1. De nije wearde wurket fuortendaliks.
1: Preload register op TIMx_CCR1 is ynskeakele. Lêzen / skriuwe operaasjes tagong ta it foarladen register. De foarladen wearde TIMx_CCR1 wurdt laden yn it skaad register tidens elk update evenemint.
* Opmerking: Foar PWM-modus om goed te wurkjen, moatte de preloadregisters ynskeakele wurde. Dit is net nedich yn ien sinjaal modus (it OPM bit wurdt ynsteld yn de TIMx_CR1 register).

Okee, litte wy alles ynskeakelje wat wy nedich binne foar de trije kanalen fan 'e twadde 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 bestiet út twa acht-bit registers, alles is ienfâldich:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

De twadde timer kin allinnich telle fan ûnderen nei boppen, ôfstimming lâns de grins, neat moat wurde feroare. Litte wy de frekwinsjeferdieling ynstelle, bygelyks op 256. Foar de twadde timer wurdt de divider ynsteld yn it register TIM2_PSCR en is in krêft fan twa:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Alles wat oerbliuwt is de konklúzjes en de twadde timer sels ynskeakelje. It earste probleem wurdt oplost troch registers Capture / Ferlykje Ynskeakelje: der binne twa, trije kanalen asymmetrysk oer har ferspraat. Hjir kinne wy ​​ek leare dat it mooglik is om de polariteit fan it sinjaal te feroarjen, d.w.s. yn prinsipe wie it mooglik om PWM Mode 1 te brûken. Wy skriuwe:

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

En as lêste begjinne wy ​​de timer yn it register TIMx_CR1:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Litte wy in ienfâldige analoog skriuwe fan AnalogWrite (), dy't de werklike wearden sil oerdrage oan 'e timer foar fergeliking. De registers wurde foarsisber neamd Capture / Ferlykje registers, der binne twa fan harren foar elk kanaal: de lege folchoarder 8 bits yn TIM2_CCRxL en de hege-oarder yn TIM2_CCRxH. Sûnt wy in 8-bit PWM hawwe makke, is it genôch om allinich de minste wichtige bits te skriuwen:

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

De oandachtige lêzer sil fernimme dat wy in bytsje defekt PWM hawwe, net yn steat om 100% filling te produsearjen (op in maksimale wearde fan 255 wurdt it sinjaal omkeard foar ien timersyklus). Foar LED's makket dit neat út, en de oandachtige lêzer kin al riede hoe't se it reparearje.

PWM op 'e twadde timer wurket, litte wy trochgean nei de earste.

De earste timer hat krekt deselde bits yn deselde registers (it is gewoan dat dy bits dy't bleaunen "reservearre" yn de twadde timer wurde aktyf brûkt yn de earste foar allerhanne avansearre dingen). Dêrom is it genôch om de adressen fan deselde registers yn it datablêd te finen en de koade te kopiearjen. No, feroarje de wearde fan 'e frekwinsjedeler, om't ... de earste timer wol net in krêft fan twa ûntfange, mar in krekte 16-bit wearde yn twa registers Prescaler High и Leech. Wy dogge alles en... de earste timer wurket net. Wat is der oan de hân?

It probleem kin allinnich wurde oplost troch te sjen troch de hiele seksje oer de kontrôle registers fan timer 1, dêr't wy sykje foar de iene dat de twadde timer net hat. Der sil wêze 17.7.30 Breakregister (TIM1_BKR), wêr is dit bytsje:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Aktivearje haadútfier

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Dat is no alles wis, de koade op itselde plak.

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

STM8 Multiplex

Multiplexing op STM8

It tredde mini-projekt is om acht RGB LED's te ferbinen mei de twadde timer yn PWM-modus en meitsje se ferskate kleuren sjen litte. It is basearre op it konsept fan LED-multiplexing, dat is dat as jo LED's heul, heul fluch yn- en útskeakelje, it ús liket dat se konstant oan binne (persistinsje fan fyzje, inertia fan fisuele waarnimming). Ik haw it ienris dien sokssawat op Arduino.

It wurkalgoritme sjocht der sa út:

  • ferbûn de anode fan de earste RGB LED;
  • lit it, it stjoeren fan de nedige sinjalen nei de kathodes;
  • wachte oant it ein fan 'e PWM-syklus;
  • ferbûn de anode fan de twadde RGB LED;
  • lit it...

No, ensfh. Fansels, foar prachtige operaasje is it nedich dat de anode is ferbûn en de LED wurdt "ûntstean" tagelyk. No, of hast. Yn alle gefallen moatte wy in koade skriuwe dy't wearden sil útfiere yn trije kanalen fan 'e twadde timer, feroarje se as UEV wurdt berikt, en tagelyk de op it stuit aktive RGB LED feroarje.

Sûnt LED switching is automatysk, wy moatte meitsje in "fideo ûnthâld" út dêr't de interrupt handler sil ûntfange gegevens. Dit is in ienfâldige array:

uint8_t colors[8][3];

Om de kleur fan in spesifike LED te feroarjen, sil it genôch wêze om de fereaske wearden yn dizze array te skriuwen. En de fariabele sil ferantwurdlik wêze foar it oantal aktive LED

uint8_t cnt;

Demux

Foar goede multiplexing, wy moatte, frjemd genôch, in CD74HC238 demultiplexer. Demultiplexer - in chip dy't ymplemintearret de operator yn hardware <<. Troch trije ynfierpinnen (bits 0, 1 en 2) fiede wy it in trije-bit nûmer X, en as antwurd aktivearret it útfiernûmer (1<<X). De oerbleaune yngongen fan 'e chip wurde brûkt om it hiele ûntwerp te skaaljen. Wy hawwe dizze chip net allinich nedich om it oantal besette pins fan 'e mikrocontroller te ferminderjen, mar ek foar feiligens - om net per ongelok mear LED's as mooglik te skeakeljen en de MK net te ferbaarnen. De chip kostet in penny en moat altyd bewarre wurde yn jo medisynkast thús.

Us CD74HC238 sil ferantwurdlik wêze foar it leverjen fan spanning oan 'e anode fan' e winske LED. Yn in folweardige multiplex soe it spanning leverje oan 'e kolom fia in P-MOSFET, mar yn dizze demo is it direkt mooglik, om't it lûkt 20 mA, neffens absolute maksimum wurdearrings yn it datablêd. Fan datasheet CD74HC238 wy hawwe pinouts nedich en dit cheat sheet:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
H = heechspanningsnivo, L = leechspanningsnivo, X - net skele

Wy ferbine E2 en E1 oan grûn, E3, A0, A1 en A3 oan pins PD5, PC3, PC4 en PC5 fan STM8. Sûnt de boppesteande tabel befettet sawol lege as hege nivo, wy konfigurearje dizze pins as push-pull pins.

PWM

PWM op 'e twadde timer is op deselde manier konfigureare as yn it foarige ferhaal, mei twa ferskillen:

Earst moatte wy de ûnderbrekking ynskeakelje Update Event (UEV) dy't in funksje sil neame dy't de aktive LED skeakelet. Dit wurdt dien troch it bit te feroarjen Update ûnderbrekking ynskeakelje yn in register mei in sprekkende namme

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
Underbrekke ynskeakelje register

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

It twadde ferskil is relatearre oan it ferskynsel fan multiplexing, lykas spoekje - parasitêre gloed fan diodes. Yn ús gefal kin it ferskine fanwege it feit dat de timer, dy't in ûnderbrekking op 'e UEV feroarsake hat, bliuwt te tikjen, en de ûnderbrekkingshanneler hat gjin tiid om de LED te wikseljen foardat de timer begjint wat te skriuwen oan' e pins. Om dit te bestriden, moatte jo de logika omkeare (0 = maksimale helderheid, 255 = neat wurdt ferljochte) en ekstreme duty cycle wearden te foarkommen. Dy. soargje derfoar dat nei UEV de LED's folslein útgeane foar ien PWM-syklus.

Polariteit feroarje:

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

Mije it ynstellen fan r, g en b op 255 en tink om se te kearen by it brûken fan se.

Underbrekt

De essinsje fan in ûnderbrekking is dat ûnder bepaalde omstannichheden de chip stopet mei it útfieren fan it haadprogramma en ropt wat eksterne funksje. Underbrekkings komme foar troch eksterne of ynterne ynfloeden, ynklusyf de timer.

Doe't wy earst makke in projekt yn ST Visual Develop, neist main.c wy krigen in rút mei in mysterieuze triem stm8_interrupt_vector.c, automatysk opnommen yn it projekt. Yn dizze triem wurdt in funksje tawiisd oan elke ûnderbrekking NonHandledInterrupt. Wy moatte ús funksje bine oan de winske ûnderbrekking.

It datablêd hat in tabel mei interruptvektoren, wêr't wy dejingen fine dy't wy nedich binne:

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8
13 IM2 update / oerstreaming
14 TIM2 capture / ferlykje

Wy moatte feroarje de LED by UEV, dus wy moatte ûnderbrekke # 13.

Dêrom, earst, yn 'e triem stm8_interrupt_vector.c feroarje de standertnamme fan de funksje ferantwurdlik foar ûnderbrekking No.. 13 (IRQ13) nei jo eigen:

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

As twadde moatte wy in bestân oanmeitsje main.h mei de folgjende ynhâld:

#ifndef __MAIN_H
#define __MAIN_H

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

En as lêste, skriuw dizze funksje yn jo 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 oerbliuwt is om ûnderbrekkingen yn te skeakeljen. Dit wurdt dien mei it kommando assembler rim - jo sille it yn sykje moatte Programmearingshanneling:

//enable interrupts
_asm("rim");

In oare assembler kommando is sim - skeakelet ûnderbrekkings út. Se moatte wurde útskeakele wylst nije wearden wurde skreaun nei it "fideoûnthâld", sadat in ûnderbrekking feroarsake op it ferkearde momint de array net bedjerre.

Alle koade - op GitHub.

Lês datasheets 2: SPI op STM32; PWM, timers en interrupts op STM8

As op syn minst ien dit artikel nuttich fynt, dan haw ik it net om 'e nocht skreaun. Ik sil bliid wêze om opmerkings en opmerkingen te ûntfangen, ik sil besykje alles te beantwurdzjen.

Boarne: www.habr.com

Add a comment