Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

В a prima parte Aghju pruvatu à dì à l'ingegneri di l'elettronica di hobby chì sò cresciuti da i pantaloni Arduino cumu è perchè duveranu leghje datasheets è altra documentazione per i microcontrollers. U testu hè diventatu grande, cusì aghju prumessu di mustrà esempi pratichi in un articulu separatu. Ebbè, si chjamava caricatore ...

Oghje vi mustrarà cumu utilizà datasheets per risolve i travaglii abbastanza simplici, ma necessarii per parechji prughjetti nantu à i controller STM32 (Blue Pill) è STM8. Tutti i prughjetti demo sò dedicati à i mo LED preferiti, l'illuminemu in grande quantità, per quale avemu da aduprà ogni tipu di periferiche interessanti.

U testu hè tornatu à esse enormu, cusì per comodità aghju fattu u cuntenutu:

STM32 Blue Pill: 16 LED cù driver DM634
STM8: Configurazione di sei pin PWM
STM8: 8 LED RGB nantu à trè pin, interruzioni

Disclaimer: Ùn sò micca un ingegnere, ùn aghju micca fintu di avè una cunniscenza prufonda in l'elettronica, l'articulu hè destinatu à dilettanti cum'è mè. In fatti, aghju cunsideratu dui anni fà cum'è u publicu di destinazione. Sì qualchissia m'hà dettu allora chì ùn hè micca scantu di leghje datasheets per un chip scunnisciutu, ùn avissi micca passatu assai tempu à circà qualchi pezzi di codice in Internet è inventà crutches cù forbici è gessatura adesiva.

U focu di questu articulu hè datasheets, micca drafts, perchè u codice pò esse micca troppu pulitu è ​​spessu una crutch. I prughjetti stessi sò assai simplici, ancu s'elli sò adattati per a prima cunniscenza cù un novu chip.

Spergu chì u mo articulu aiuterà qualcunu in un stadiu simili di immersione in un hobby.

STM 32

16 LED cù DM634 è SPI

Picculu prughjettu cù Blue Pill (STM32F103C8T6) è driver LED DM634. Cù l'aiutu di datasheets, avemu da trattà cù u driver, i porti STM IO è cunfigurà SPI.

DM634

Chip taiwanese cù uscite PWM 16 x 16-bit, pò esse incatenatu. U mudellu più ghjovanu 12-bit hè cunnisciutu da un prughjettu domesticu pacchettu di luce. À un tempu, scegliendu trà DM63x è u famusu TLC5940, aghju stabilitu nantu à DM per parechje ragioni: 1) TLC in Aliexpress hè definitu falsu, ma questu ùn hè micca; 2) DM hà un PWM autonomu cù u so propiu generatore di frequenza; 3) Puderia esse acquistatu à pocu pressu in Mosca, è ùn aspittà micca un pacchettu da Ali. E, sicuru, era interessante per amparà cumu cuntrullà u chip stessu, è micca aduprà una biblioteca pronta. I patatine fritte sò avà presentati principarmenti in u pacchettu SSOP24, sò faciuli di saldarà nantu à l'adattatore.

Siccomu u fabricatore hè taiwanese, datasheet à u chip hè scrittu in Chinese English, chì significa chì serà divertente. Prima fighjate à u pinoutCunnessione Pin) per capisce quale gamba cunnetta ciò chì, è una descrizzione di i pin (Descrizione Pin). 16 pins:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Sink surghjenti DC (focu apertu)

Pica / Uscita di drenu apertu - stock; fonte di corrente entrante; un output cunnessu à a terra in un statu attivu - i LED sò cunnessi à u driver per catodi. Elettricu, questu hè, sicuru, micca "drain apertu" (drenu apertu), ma in datasheets tali designazione per outputs in modu di drenu si trova spessu.

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Resistori esterni trà REXT è GND per stabilisce u valore di corrente di output

Un resistore di riferimentu hè stallatu trà u pin REXT è a terra, chì cuntrolla a resistenza interna di l'outputs, vede u graficu in a pagina 9 di a datasheet. In u DM634, sta resistenza pò ancu esse cuntrullata da u software stabilendu a luminosità generale (luminosità globale); Ùn andaraghju micca in dettagli in questu articulu, solu mette una resistenza 2.2 - 3 kOhm quì.

Per capisce cumu cuntrullà u chip, fighjemu a descrizzione di l'interfaccia di u dispusitivu:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

Iè, quì hè, Chinese English in tutta a so gloria. Hè problematicu di traduce questu, pudete capisce se vulete, ma ci hè un altru modu - per vede cumu a cunnessione in a datasheet à u funziunale TLC5940 hè descritta:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
... Solu trè pin sò richiesti per inserisce dati in u dispusitivu. U filu rising di u signale SCLK cambia a dati da u pin SIN à u registru internu. Dopu chì tutte e dati sò stati caricati, un brevi signali XLAT elevatu chjappà i dati trasferiti in serie in i registri interni. I registri interni sò porte attivate da u livellu di signale XLAT. Tutti i dati sò trasmessi prima MSB.

Latch - scroccata / scroccata / scroccata.
Bordu ascendante hè a punta di u pulsu
MSB prima - u bit più significativu (più a manca) in avanti.
per clock data - trasmette dati in sequenza (bit per bit).

A parolla latch spessu trovu in a documentazione per i chips è hè traduttu in una varietà di manere, cusì per capiscenu mi permetteraghju.

un picculu prugramma educativuU driver LED hè essenzialmente un registru di shift. "Shift" (passata) in u nome - movimentu bit-by-bit di dati in u dispusitivu: ogni novu bit shoved inside pushs the whole chain forward in front of it. Siccomu nimu ùn vole osservà u lampeggiamentu caòticu di i LED durante u turnu, u prucessu si svolge in registri buffer separati da i travagliadori da un otturatore (latch) hè una spezia di vestitori induve i pezzi si allineanu in a sequenza desiderata. Quandu tuttu hè prontu, l'otturatore si apre è i pezzi vanu à travaglià, rimpiazzà u batch precedente. Parola latch in a documentazione per i microcircuiti quasi sempre implica un tali damper, in qualunque cumminazzioni hè utilizatu.

Allora, u trasferimentu di dati à DM634 hè realizatu cusì: stabilisce l'ingaghjamentu DAI à u valore di l'altu bit di u LED luntanu, tirà DCK su è falà; stabilisce l'input DAI à u valore di u prossimu bit, tira DCK; è cusì finu à chì tutti i bit sò stati trasmessi (chjosu in), dopu chì tiramu LAT. Questu pò esse fattu manualmenteun pocu bang), ma hè megliu aduprà l'interfaccia SPI spicialmente affilata per questu, postu chì hè presentata nantu à u nostru STM32 in duie copie.

Tablet blu STM32F103

Introduzione: i cuntrolli STM32 sò assai più complicati cà Atmega328 di ciò chì ponu esse spaventosi. À u listessu tempu, per ragioni di risparmiu energeticu, quasi tutti i periferichi sò disattivati ​​à u principiu, è a freccia di u clock hè 8 MHz da una fonte interna. Fortunatamente, i programatori STM anu scrittu un codice chì porta u chip à u "calculatu" 72 MHz, è l'autori di tutti l'IDE chì cunnoscu includeu in a prucedura d'inizializazione, perchè ùn avemu micca bisognu di clock (ma pudete sè vo vulete veramente). Ma avete da accende i periferichi.

Documentazione: U popular chip STM32F103C8T6 hè stallatu nantu à Blue Pill, ci sò dui documenti utili per questu:

In a datasheet, pudemu esse interessatu in:

  • Pinouts - chip pinouts - in casu chì decidemu di fà tavule noi stessi;
  • Memory Map - una mappa di memoria per un chip specificu. U Manuale di Referenza hà una mappa per tutta a linea, ammenta i registri chì ùn sò micca nantu à a nostra.
  • Pin Definitions table - elencu e funzioni pin principali è alternative; per a "pillola blu" in Internet, pudete truvà ritratti più convenienti cù una lista di pins è e so funzioni. Dunque, avemu subitu Google Blue Pill pinout è mantene sta stampa à manu:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
NB: ci hè statu un errore in a stampa da Internet, nutatu in i cumenti, per quale ringraziu. A stampa hè stata rimpiazzata, ma questu hè una lezione - hè megliu per verificà l'infurmazioni micca da datasheets.

Eliminate a datasheet, apre u Manuale di Riferimentu, da avà l'utilicemu solu.
Prucedura: trattà cù input / output standard, cunfigurà SPI, accende i periferici necessarii.

Input Output

Nantu à l'Atmega328, l'I / O hè estremamente simplice, per quessa chì l'abbundanza di l'opzioni STM32 pò esse cunfusa. Avà avemu solu bisognu di cunclusioni, ma ancu ci sò quattru opzioni:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
uscita di scarico aperto, uscita push-pull, alternativa push-pull, alternativa di scarico aperto

"Pull-push" (push-pull) - l'output abituale da l'Arduino, u pin pò esse HIGH o LOW. Ma cù u "focu apertu" nascenu difficultà, anche se in fatti tuttu hè simplice quì:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Configurazione di output / quandu un portu hè assignatu à un output: / output buffer enabled: / - open drain mode: "0" in u registru di output attiva N-MOS, "1" in u registru di output lascia u portu in modalità Hi-Z (P-MOS ùn hè micca attivatu) / - Modu push-pull: "0" in u registru di output attiva N-MOS, "1" in u registru output attiva P-MOS.

Tutta a differenza di scarico aperta (drenu apertu) da "push-pull" (push-pull) hè chì in u primu pin ùn pò micca piglià u statu ALTA: quandu una unità hè scritta à u registru di output, entra in modu d'alta resistenza (alta impedenza, Salute-Z). Quandu scrivite zero, u pin in i dui modi si cumporta u stessu, sia lògicu sia elettricamente.

In u modu di output normale, u pin si traduce solu u cuntenutu di u registru di output. In "alternativa" hè cuntrullata da u perifericu currispundente (vede 9.1.4):

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Se u bit di u portu hè cunfiguratu cum'è una funzione alternativa di output, u registru di output hè disattivatu è u pin hè cunnessu à u signale di output di u perifericu.

A funziunalità alternativa di ogni pin hè descritta in Definizioni Pin A datasheet hè nantu à a stampa scaricata. Quandu ci hè dumandatu ciò chì deve fà se u pin hà parechje funzioni alternative, a risposta hè datu da una nota di pede in a datasheet:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Sì parechje periferiche utilizanu u stessu pin, per evità u cunflittu trà e funzioni alternative, solu un perifericu deve esse utilizatu à u mumentu, cambiendu utilizendu u bit Peripheral Clock Enable (in u registru RCC currispundente).

Infine, i pin in modu di output anu ancu una velocità di clock. Questa hè una altra funzione di risparmiu d'energia, in u nostru casu, avemu solu stabilitu à u massimu è scurdate.

Allora: usemu SPI, chì significa chì dui pin (cù dati è cun un signalu di clock) deve esse "funzione push-pull alternativa", è un altru (LAT) deve esse "push-pull normale". Ma prima di assignà elli, trattamu di SPI.

SPI

Un altru pocu pirate

SPI o Serial Peripheral Interface (interfaccia periferica seriale) hè una interfaccia simplice è assai efficace per cumunicà MK cù altri MK è u mondu esternu in generale. U principiu di u so funziunamentu hè digià statu discrittu sopra, induve circa lu driver Chinese LED (vede rùbbrica 25 in u manual di riferimentu). SPI pò operà in modi master ("maestru") è slave ("slave"). SPI hà quattru canali basi, chì ùn sò micca tutti implicati:

  • MOSI, Master Output / Slave Input: stu pin manda dati in modu maestru, è riceve dati in modu slave;
  • MISO, Master Input / Slave Output: à u cuntrariu, in u maestru riceve, in u slave dà;
  • SCK, Serial Clock: stabilisce a frequenza di trasmissione di dati in u maestru o riceve un signalu di clock in u slave. Essenzialmente, beats the beats;
  • SS, Slave Select: cù questu canale, u schiavu sapi chì volenu qualcosa da ellu. In STM32 hè chjamatu NSS, induve N = negativu, i.e. u controller diventa schiavu se stu canale hà una terra. Si combina bè cù u modu Open Drain Output, ma hè una altra storia.

Cum'è tuttu u restu, SPI in STM32 hè riccu di funziunalità, chì rende un pocu difficiule di capiscenu. Per esempiu, pò travaglià micca solu cù SPI, ma ancu cù una interfaccia I2S, è in a documentazione i so descrizzioni sò mischiati, hè necessariu taglià l'eccessu in una manera puntuale. U nostru compitu hè estremamente simplice: basta à dà dati cù solu MOSI è SCK. Andemu à a sezione 25.3.4 (comunicazione half-duplex, cumunicazione half-duplex), induve truvamu 1 clock è 1 filu di dati unidirezionale (1 clock è 1 flussu di dati unidirezionale):

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
In questu modu, l'applicazione usa SPI in modu di trasmissione solu o di riceve solu. / U modu di trasmissione solu hè simile à u modu duplex: i dati sò trasmessi nantu à u pin di trasmissione (MOSI in modu maestru o MISO in modu slave), mentre chì u pin di ricezione (MISO o MOSI rispettivamente) pò esse usatu cum'è pin I / O normale. In questu casu, hè abbastanza per l'applicazione per ignurà u buffer Rx (se hè lettu, ùn ci sarà micca dati trasmessi).

Grande, u pin MISO hè liberu, cunnettamu u signalu LAT. Trattemu di Slave Select, chì pò esse cuntrullatu programaticamente in STM32, chì hè estremamente convenientu. Avemu lettu u paràgrafu di u listessu nome in a sezione 25.3.1 SPI General Description:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
U cuntrollu di u software NSS (SSM = 1) / L'infurmazione di selezzione di Slave hè cuntenuta in u bit SSI di u registru SPI_CR1. U pin NSS esternu hè lasciatu liberu per altri bisogni di l'applicazione.

Hè u tempu di scrive à i registri. Aghju decisu d'utilizà SPI2, cerchemu u so indirizzu di basa in a datasheet - in a sezione 3.3 Memory Map (Memory Map):

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

Ebbè, cuminciamu:

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

Apermu a sezione 25.3.3 cù u titulu di cunfigurazione "Configurazione di SPI in modu maestru":

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

1. Stabilite u clock di l'interfaccia seriale cù i bit BR[2:0] in u registru SPI_CR1.

I registri sò cullati in a sezione manuale di riferimentu di u listessu nome. Cambia l'indirizzu (offset di indirizzu) CR1 hà 0x00, per difettu tutti i bit sò sbulicati (Resettate u valore 0x0000):

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

I bits BR stabiliscenu u divisore di l'orologio di u controller, determinendu cusì a frequenza à quale u SPI operarà. A freccia STM32 serà 72 MHz, u driver LED, secondu a so datasheet, opera à una freccia di sin'à 25 MHz, cusì avemu bisognu di dividisce per quattru (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. Impostate i bits CPOL è CPHA per definisce a relazione trà u trasferimentu di dati è u clock di l'interfaccia seriale (vede u schema à a pagina 240)

Siccomu leghjemu una scheda di dati quì, è ùn fighjemu micca schemi, fighjemu un ochju più vicinu à a descrizzione testuale di i bit CPOL è CPHA in a pagina 704 (SPI General Description):

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Fase di u clock è polarità
Utilizendu i bits CPOL è CPHA di u registru SPI_CR1, pudete selezziunà programaticamente quattru opzioni per i rapporti di timing. U bit CPOL (Clock Polarity) cuntrolla u statu di u signale di u clock quandu ùn ci hè micca datu trasmessi. Stu bit cuntrolla i modi maestru è slave. Se CPOL hè resettatu, u pin SCK hè bassu à riposu. Se u bit CPOL hè impostatu, u pin SCK hè altu quandu inattivu.
Se u bit CPHA (Clock Phase) hè impostatu, u strobe trap MSB hè a seconda punta di u signale SCK (caduta si CPOL hè sbulicatu, o rising edge se CPOL hè impostatu). I dati sò latched in u sicondu cambiamentu di u clock. Se u bit CPHA hè sguassatu, u stroboscopiu di trappula di bit high hè u frontu ascendente di u signale SCK (caduta se CPOL hè impostatu, o frontu ascendante si CPOL hè chjaru). I dati sò latched in u primu cambiamentu di u clock.

Dopu avè tastatu sta cunniscenza, ghjunghjemu à a cunclusione chì i dui bits devenu esse zero, perchè vulemu chì u signale SCK fermu bassu quandu ùn hè micca in usu, è e dati per esse trasmessi nantu à a riva di u pulsu (vede fig. bordu ascendante in a scheda di dati DM634).

Per via, quì avemu prima scontru una caratteristica di u vocabulariu in i datasheets ST: in elli a frasa "reset the bit to zero" hè scritta. per resettare un pòè micca per sguassà un pocu, cum'è, per esempiu, Atmega.

3. Stabilisci u bit DFF per determinà u furmatu di bloccu di dati 8-bit o 16-bit

Aghju specificamente pigliatu u DM16 634-bit per ùn avè micca fastidiu cù u trasferimentu di dati PWM 12-bit, cum'è u DM633. DFF hà sensu di mette in unità:

#define DFF         0x0800

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

4. Configurate u bit LSBFIRST in u registru SPI_CR1 per definisce u formatu di bloccu

LSBFIRST, cum'è u so nome implica, stabilisce a trasmissione cù u più pocu significativu prima. Ma u DM634 vole riceve prima dati MSB. Dunque, lascemu u resettore.

5. In u modu di hardware, se l'input da u pin NSS hè necessariu, cunduce u pin NSS altu durante tutta a sequenza di trasferimentu di byte. In u modu di prugramma NSS, stabilisce i bit SSM è SSI in u registru SPI_CR1. Se u pin NSS deve esse output, solu u bit SSOE deve esse stabilitu.

Installa SSM è SSI per scurdà di u modu di hardware NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. I bits MSTR è SPE deve esse stabilitu (si fermanu solu se u NSS hè altu)

In realtà, cù questi bit assignemu u nostru SPI cum'è maestru è l'accendemu:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI hè cunfiguratu, scrivemu immediatamente funzioni chì mandanu byte à u driver. Cuntinuà lettura 25.3.3 "Configurazione SPI in Modu Maestru":

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Prucedura di trasferimentu di dati
U trasferimentu principia quandu un byte hè scrittu à u buffer Tx.
U byte di dati hè caricatu in u registru di shift at parallella modu (da u bus internu) durante a trasmissioni di u primu bit, dopu chì hè trasmessu in coherente Modu pin MOSI, u primu o l'ultimu bit in avanti secondu l'impostazione di u bit LSBFIRST in u registru CPI_CR1. A bandiera TXE hè stabilita dopu a trasmissione di dati da Tx buffer à shift register, è una interruzzione hè generata se u bit TXEIE in u registru CPI_CR1 hè stabilitu.

Aghju evidenziatu uni pochi di parolle in a traduzzione per attirà l'attenzione à una funzione di l'implementazione di SPI in i cuntrolli STM. Nant'à l'Atmega, a bandiera TXE (Tx Viotu, Tx hè viotu è pronta per riceve dati) hè stabilitu solu dopu chì u byte tutale hè statu mandatu fora. E quì sta bandiera hè stallata dopu chì u byte hè statu imbuttatu in u registru di shift internu. Siccomu hè sbuchjatu quì cù tutti i bits à u stessu tempu (in parallelu), è poi i dati sò trasmessi sequentially, TXE hè stabilitu prima chì u byte hè mandatu cumpletamente. Questu hè impurtante perchè in u casu di u nostru driver LED, avemu bisognu à tirà u pin LAT dopu à mandà всех dati, i.e. solu a bandiera TXE ùn serà micca abbastanza per noi.

Chì significa chì avemu bisognu di una altra bandiera. Fighjemu à 25.3.7 - "Status Flags":

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
<...>
Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Bandiera BUSY
A bandiera BSY hè stallata è sguassata da u hardware (scrivu à ellu ùn hà micca effettu). A bandiera BSY indica u statu di a capa di cumunicazione SPI.
Resetta:
quandu u trasferimentu hè cumpletu (eccettu in u modu maestru se u trasferimentu hè cuntinuu)
quandu SPI hè disattivatu
quandu si verifica un errore di modu maestru (MODF = 1)
Se a trasmissione ùn hè micca cuntinuu, a bandiera BSY hè sguassata trà ogni trasmissione di dati.

Va bè, serà utile. Scuprite induve si trova u buffer Tx. Per fà questu, leghjite u "SPI Data Register":

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Bits 15: 0 DR[15: 0] Data Register
Dati ricevuti o dati da trasmette.
U registru di dati hè divisu in dui buffer, unu di scrittura (buffer di trasmissione) è unu di lettura (buffer di ricezione). Una scrittura à u registru di dati scrive in u buffer Tx, è una lettura da u registru di dati restituverà u valore cuntenutu in u buffer Rx.

Ebbè, u registru di statutu, induve ci sò bandiere TXE è BSY:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

Scrivemu:

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

Ebbè, postu chì avemu bisognu di trasfiriri 16 volte dui bytes, secondu u numeru di outputs di u driver LED, qualcosa cum'è questu:

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

Ma ùn sapemu micca ancu cumu tirà u pin LAT, allora vultemu à I / O.

Assegna pins

In STM32F1, i registri rispunsevuli di u statu di i pins sò piuttostu inusual. Hè chjaru chì ci sò più di elli chì Atmega, ma sò ancu diffirenti di l'altri chips STM. Sezione 9.1 GPIO Descrizione generale:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Ciascunu di i porti I/O di u scopu generale (GPIO) hà dui registri di cunfigurazione di 32 bit (GPIOx_CRL è GPIOx_CRH), dui registri di dati di 32 bit (GPIOx_IDR è GPIOx_ODR), un registru di reset / reset di 32 bit (GPIOx_BSRR), un registru di reset di 16 bit (GPIOx_BRR) è un registru di bloccu 32-bit LCKR.

Inusual, è ancu piuttostu inconveniente, sò i primi dui registri quì, perchè i pins 16 di u portu sò spargugliati nantu à elli in un furmatu "quattru bit per fratellu". Quelli. I pin da XNUMX à XNUMX sò in CRL, è u restu sò in CRH. À u listessu tempu, i registri rimanenti si adattanu bè à i pezzi di tutti i pins di u portu - spessu restanu a mità "riservatu".

Per simplicità, cuminciamu à a fine di a lista.

Ùn avemu micca bisognu di un registru di bloccu.

I registri di set è resettate sò abbastanza divertenti in quantu si duplicanu parzialmente: pudete scrive tuttu solu in BSRR, induve i 16 bits superiori resettanu u pin à cero, è i più bassi seranu messi à 1, o pudete ancu aduprà BRR, i 16 bits inferiori di quale solu resettate u pin. Mi piace a seconda opzione. Questi registri sò impurtanti perchè furnisce l'accessu atomicu à i pin:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Installazione atomica o resettate
Ùn avete bisognu di disattivà l'interruzioni quandu u prugramma GPIOx_ODR à u livellu di bit: pudete cambià unu o più bit cù una sola operazione di scrittura atomica APB2. Questu hè ottenutu scrivendu un "1" à u registru set/reset (GPIOx_BSRR o, per reset solu, GPIOx_BRR) di u bit da cambià. L'altri pezzi resteranu invariati.

I registri di dati anu nomi abbastanza parlanti - IDR = entrée Registru di direzzione, registru di input; ODR= radicali avrìanu pututu Registru di direzzione, registru di output. In u prughjettu attuale, ùn avemu micca bisognu.

È infine, i registri di cuntrollu. Siccomu ci interessa à i pins di u sicondu SPI, vale à dì PB13, PB14 è PB15, fighjemu immediatamente CRH:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

È vedemu chì serà necessariu di scrive qualcosa in pezzi da u 20 à u 31.

Avemu digià capitu ciò chì vulemu da i pins sopra, cusì quì aghju da fà senza una screenshot, diceraghju solu chì MODE stabilisce a direzzione (input se i dui bits sò impostati à 0) è a vitezza di u pin (avemu bisognu di 50MHz, vale à dì chì i dui pins sò impostati à "1"), è CNF stabilisce u modu: normale "push-pull" hè 00, "10" per predefinitu, per predefinitu, "0" hè sopra. ov u terzu bit da u fondu (CNFXNUMX) hè registratu, li mette in u modu input flottante.

Siccomu pensa à fà qualcosa d'altru cù stu chip, per simplicità, aghju generalmente definitu tutti i valori MODE è CNF pussibuli per i registri di cuntrollu inferjuri è superiore.

In qualchì modu cusì

#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

I nostri pin sò in u portu B (indirizzu di basa - 0x40010C00), codice:

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

È, in cunsiquenza, pudete scrive definizioni per LAT, chì twitch i registri BRR è BSRR:

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

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

(LAT_low solu per inerzia, in qualchì modu era sempre, lasciate stà per voi)

Avà tuttu hè grande, solu ùn funziona. Perchè questu hè STM32, salvanu l'electricità quì, chì significa chì avete bisognu di accende u clock di i periferici necessarii.

Accende l'orologio

U clock hè rispunsevule per u clock, sò ancu Clock. È pudemu digià nutà l'abbreviazione RCC. Cerchemu in a ducumentazione: questu hè Reset and Clock Control (Gestione di reset è clock).

Cum'è l'esitatu sopra, per furtuna, a ghjente di STM hà fattu a parte più difficiuli di u tema di u clock per noi, per quale assai grazie à elli (di novu vi daraghju un ligame per U situ web di Di Haltper fà chjaru quantu hè cunfusu). Avemu bisognu solu di registri rispunsevuli di attivà u clock perifericu (Registri di attivazione di l'orologio perifericu). Prima, truvamu l'indirizzu di basa di u RCC, hè à u principiu di a "Carta di Memoria":

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

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

È dopu cliccate nant'à u ligame induve pruvà à truvà qualcosa in a tavula, o, assai megliu, andate nantu à e descrizzioni di i registri inclusi da e rùbbriche circa. attivà i registri. Induve truvamu RCC_APB1ENR è RCC_APB2ENR:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

È in elli, rispettivamente, bits chì includenu u clock di SPI2, IOPB (I / O Port B) è funzioni alternative (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;

U codice finali pò esse trovu ccà.

Se ci hè l'uppurtunità è u desideriu di pruvà, allora cunnettamu DM634 cusì: DAI à PB15, DCK à PB13, LAT à PB14. Alimentamu u cunduttore da 5 volti, ùn vi scurdate di cumminà i terreni.

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

STM8 PWM

PWM nantu à STM8

Quandu era ghjustu à pianificà stu articulu, decisu, per esempiu, di pruvà à ammaistrà una certa funziunalità di un chip scunnisciutu cù l'aiutu di solu una datasheet, perchè un calzaturinu ùn esce micca senza stivali. STM8 era perfettu per questu rolu: prima, aghju avutu un paru di schede chinesi cù STM8S103, è in segundu, ùn hè micca assai populari, è per quessa a tentazione di leghje è truvà una suluzione in Internet si basa nantu à l'absenza di sti stessi suluzioni.

U chip hà ancu datasheet и Manuale di riferimento RM0016, in u primu pinout è indirizzi registrati, in u sicondu - tuttu u restu. Programmazione STM8 in C in un IDE bruttu Sviluppu visuale ST.

Clock e I/O

Per automaticamente, STM8 opera à una frequenza di 2 MHz, questu deve esse correttu immediatamente.

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
HSI Clock (High Internal)
U clock HSI hè derivatu da un oscillatore RC internu di 16 MHz cù un divisore programabile (1 à 8). Hè stabilitu in u registru divisore di u clock (CLK_CKDIVR).
Nota: L'oscillatore HSI RC cù un divisore di 8 hè sceltu cum'è fonte di clock master à l'iniziu.

Truvemu l'indirizzu di u registru in a datasheet, a descrizzione in refman è vede chì u registru deve esse sbulicatu:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Siccomu andemu à eseguisce PWM è cunnette LED, fighjemu u pinout:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

U chip hè chjucu, assai funzioni sò suspesi nantu à i stessi pin. Ciò chì hè in parentesi quadrate hè "funzionalità alternativa", hè cambiatu da "opzioni byte" (byte d'opzione) - qualcosa cum'è Atmega fuses. Pudete cambià i so valori programmaticamenti, ma ùn hè micca necessariu, perchè. A nova funziunalità hè attivata solu dopu un reboot. Hè più faciule d'utilizà ST Visual Programmer (scaricatu cù Visual Develop), chì pò cambià questi byte. U pinout mostra chì l'outputs CH1 è CH2 di u primu timer sò oculati in parentesi quadrate; hè necessariu di stabilisce i bits AFR1 è AFR0 in STVP, è u sicondu trasferisce ancu l'output CH1 di u second timer da PD4 à PC5.

Cusì, 6 pins cuntrullà i LED: PC6, PC7 è PC3 per u primu timer, PC5, PD3 è PA3 per u sicondu.

L'installazione di i pin I/O stessi nantu à u STM8 hè più simplice è più logicu chè in u STM32:

  • Registru di direzzione di dati Atmega familiarizatu DDR (Registru di direzzione di dati): 1 = output;
  • u primu registru di cuntrollu CR1, quandu u risultatu, stabilisce u modu push-pull (1) o apre u drain (0); postu ch'e aghju culligatu i LED à u chip cù catodi, lascià cero quì;
  • u sicondu registru di cuntrollu CR2 stabilisce a velocità di u clock quandu u outputting: 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

Configurazione PWM

Prima, definiscemu i termini:

  • Frequenza PWM - a frequenza cù quale u timer tick;
  • Ricaricamentu automaticu, AR - valore caricatu automaticamente, finu à quale u timer conta (periodu di impulsu);
  • Avvenimentu di aghjurnamentu, UEV - un avvenimentu chì si verifica quandu u timer hà cuntatu à AR;
  • PWM Duty Cycle - Duty cycle PWM, spessu chjamatu "duty cycle";
  • Capture / Comparare u valore - valore per catturà / paragunà, cuntendu finu à quale u timer farà qualcosa (in u casu di PWM, inverte u signale di output);
  • valore di preload - valore precaricatu. paragunà u valore ùn pò micca cambià mentre u timer hè in tic-tac, altrimenti u ciculu PWM si rompe. Per quessa, i novi valori trasmessi sò posti in u buffer è tirati fora quandu u timer righjunghji a fine di u countdown è hè resettatu;
  • Allineatu à bordu и Modi allineati à u centru - allinamentu nantu à u cunfini è in u centru, u listessu cum'è atmelovskie PWM veloce и PWM correttu in fase.
  • OCiREF, Output Compare Signal Reference - u signale di output di riferimentu, in fattu, ciò chì appare nantu à u pin currispundente in modu PWM.

Cumu hè digià chjaru da u pinout, dui cronometri anu capacità PWM - u primu è u sicondu. Tramindui sò 16-bit, u primu hà assai funzioni supplementari (in particulare, pò cuntà sia up sia down). Avemu bisognu di tramindui per travaglià a listessa manera, cusì aghju decisu di principià cù u sicondu, ovviamente più poviru, per ùn aduprà accidentalmente qualcosa chì ùn hè micca in questu. Qualchidunu prublema hè chì a descrizzione di a funziunalità PWM di tutti i timers in u manuale di riferimentu hè in u capitulu nantu à u primu cronometru (17.5.7 PWM Mode), perchè avete da saltà avanti è avanti per u documentu tuttu u tempu.

PWM in STM8 hà un vantaghju impurtante annantu à Atmega PWM:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
PWM cù allineamentu di u bordu
Cunfigurazione di u contu da u fondu à a cima
L'up counting hè attivu se u bit DIR in u registru TIM_CR1 hè chjaru
Esempiu:
L'esempiu usa u primu modu PWM. U signale di riferenza PWM OCiREF hè tenutu altu finu à chì TIM1_CNT < TIM1_CCRi. Altrimenti, ci vole un livellu bassu. Se u valore da paragunà in u registru TIM1_CCRi hè più grande di u valore di carica automatica (registru TIM1_ARR), u signale OCiREF hè tenutu à 1. Se u valore di paragone hè 0, OCiREF hè tenutu à zero....

Timer STM8 durante avvenimentu di aghjurnà cuntrolli prima paragunà u valore, è solu allora pruduce un signalu di riferimentu. In Atmega, u timer prima tremula, è poi paragunate, per via di quale, quandu compare value == 0 l'output hè una agulla chì deve esse trattata di qualchì manera (per esempiu, invertendu programaticamente a logica).

Allora ciò chì vulemu fà: 8-bit PWM (AR == 255), cuntendu da u fondu à u cima, allinamentu longu u cunfini. Siccomu i lampadini sò cunnessi à u chip da catodi, u PWM deve esse 0 (LED on) finu à paragunà u valore è 1 dopu.

Avemu digià lettu circa qualchi Modu PWM, cusì truvamu u registru desideratu di u second timer cerchendu in u manuale di riferimentu per sta frasa (18.6.8 - TIMx_CCMR1):

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
110: Primu modu PWM - quandu si cunta da u fondu à a cima, u primu canale hè attivu sempre chì TIMx_CNT < TIMx_CCR1. Altrimenti, u primu canale hè inattivu. [più in u documentu, erroneous copy-paste from timer 1] 111: Second mode PWM - quandu cunta da u fondu à u cima, u primu canale hè inattivu finu à TIMx_CNT < TIMx_CCR1. Altrimenti, u primu canale hè attivu.

Siccomu i LED sò cunnessi à u MK cù catodi, u sicondu modu ci cunvene (u primu ancu, ma ùn sapemu micca ancu questu).

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Bit 3 OC1PE: Abilita Preloading Output 1
0: Preload register in TIMx_CCR1 disattivatu. Pudete scrive à TIMx_CCR1 in ogni mumentu. U novu valore travaglia subitu.
1: Preload register in TIMx_CCR1 attivatu. L'operazioni di lettura / scrittura accede à u registru di precarica. U valore precaricatu di TIMx_CCR1 hè caricatu in u registru d'ombra durante ogni avvenimentu di aghjurnamentu.
* Nota: I registri di precaricamentu devenu esse attivati ​​per u modu PWM per travaglià bè. Questu hè opzionale in u modu di signale unicu (u bit OPM hè stabilitu in u registru TIMx_CR1).

Va bè, attivate tuttu ciò chì avete bisognu per i trè canali di u second 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 si compone di dui registri di ottu bit, tuttu hè simplice quì:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

U second timer pò cuntà solu da u fondu à u cima, l'allinjamentu nantu à u cunfini, nunda ùn deve esse cambiatu. Pone u divisore di freccia, per esempiu, à 256. Per u second timer, u divisore hè stallatu in u registru TIM2_PSCR è hè una putenza di dui:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Resta à accende e cunclusioni è u sicondu timer stessu. U primu compitu hè risolta da i registri Capture / Comparare Enable: ci sò dui, trè canali sò spargugliati asimmetricamente nantu à elli. Quì pudemu ancu amparà chì hè pussibule cambià a polarità di u signale, i.e. in principiu, puderia ancu esse usatu PWM Mode 1. Scrivemu:

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

È infine, cuminciamu u timer in u registru TIMx_CR1:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Scrivemu un analogu simplice di AnalogWrite (), chì passerà i valori attuali à u timer per paragunà. I registri sò prevedibilmente chjamati Capture / Comparare i registri, ci sò dui per ogni canali: i 8 bits bassi in TIM2_CCRxL è i bits alti in TIM2_CCRxH. Siccomu avemu principiatu PWM 8-bit, hè abbastanza per scrive solu i bits bassu:

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

U lettore attentu hà da vede chì avemu un PWM ligeramente difettu, incapaci di dà 100% di riempimentu (à un valore massimu di 255, u signale hè invertitu da un ciclu di timer). Per i LED, questu ùn ghjoca micca un rolu, è u lettore attentu hà digià indovinatu cumu per riparà.

PWM nantu à u secondu timer travaglia, andate à u primu.

U primu timer hà esattamente i stessi bits in i stessi registri (hè solu chì quelli bits chì sò stati "riservati" in u second timer sò attivamente usati per ogni tipu di cose avanzate in u primu). Dunque, hè abbastanza per truvà l'indirizzi di i stessi registri in a datasheet è copià u codice. Ebbè, cambia u valore di u divisore di freccia, perchè. u primu timer ùn vole micca ottene una putenza di dui, ma un valore esatta di 16 bit in dui registri Prescaler High и Low. Facemu tuttu è ... u primu timer ùn funziona micca. Chì ci hè?

L'unicu modu per risolve u prublema hè fighjendu tutta a sezione nantu à i registri di cuntrollu di u timer 1, induve circhemu unu chì u second timer ùn hà micca. ci sarà 17.7.30 Break register (TIM1_BKR), induve ci hè un pocu cusì:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Habilita l'output principale

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Hè tuttu per avà, u codice culà.

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

STM8 Multiplex

Multiplexing nantu à STM8

U terzu mini-prughjettu hè di cunnette ottu LED RGB à u sicondu timer in u modu PWM è fà chì mostranu diversi culori. Hè basatu annantu à u cuncettu di multiplexing LED, chì cunsiste in u fattu chì si accende è spegne i LED assai, assai rapidamente, ci pare chì sò sempre accesi (persistenza di a visione, inerzia di a percepzione visuale). Aghju fattu una volta qualcosa di simile nantu à arduino.

L'algoritmu di u travagliu pare cusì:

  • cunnessu l'anodu di u primu LED RGB;
  • accende, dendu i signali necessarii à i catodi;
  • aspittatu per a fine di u ciculu PWM;
  • cunnessu l'anodu di u sicondu LED RGB;
  • l'accendi...

Ebbè, ecc. Di sicuru, per u travagliu bellu, hè necessariu chì a cunnessione di l'anodu è l'"ignizione" di u LED sò simultaneamente. Bè, quasi. In ogni casu, avemu bisognu di scrive un codice chì pruduverà i valori in trè canali di u secondu timer, cambiassi quandu UEV hè ghjuntu è simultaneamente cambia u LED RGB attualmente attivu.

Siccomu u cambiamentu LED hè automaticu, avemu bisognu di creà "memoria video" da induve u gestore di l'interruzzione riceverà dati. Questu hè un array simplice:

uint8_t colors[8][3];

Per cambià u culore di un LED particulari, sarà abbastanza à scrive i valori necessarii in questa matrice. È a variàbile serà rispunsevuli di u numeru di u LED attivu

uint8_t cnt;

Demux

Per un multiplexing propiu, avemu bisognu, stranamente, u demultiplexer CD74HC238. Demultiplexer - un chip chì implementa l'operatore in hardware <<. Attraversu trè pin di input (bits 0, 1 è 2) l'alimentemu un numeru di trè bit X, è in risposta attiva u numeru di output (1<<X). L'inputs restanti di u chip sò usati per scala u disignu tutale. Avemu bisognu di stu chip micca solu per riduce u nùmeru di pins occupati di u microcontroller, ma ancu per a sicurità - per ùn accende accidentalmente più LED di ciò chì hè pussibule è micca brusgià u MK. U chip costa un centesimu, deve esse sempre guardatu in u kit di primu aiutu in casa.

CD74HC238 serà rispunsevule per furnisce a tensione à l'anodu di u LED desideratu. In un multiplex full-fledged, furnisce a tensione à a colonna attraversu u P-MOSFET, ma in questa demo, pudete fà direttamente, perchè. tira 20mA, secondu valutazione massima assoluta in a datasheet. Da Scheda dati CD74HC238 avemu bisognu di un pinout è di sta cheat sheet:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
H = livellu d'alta tensione, L = livellu di tensione bassa, X - ùn importa micca

Cunnetteremu E2 è E1 à a terra, E3, A0, A1 è A3 à i pins PD5, PC3, PC4 è PC5 di STM8. Siccomu a tavula sopra cuntene i livelli bassi è alti, avemu stallatu questi pins cum'è pins push-pull.

PWM

PWM nantu à u second timer hè cunfiguratu in u listessu modu cum'è in a storia precedente, cù duie differenzi:

Prima, avemu bisognu di attivà l'interruzzione Actualizazione di l'avvenimentu (UEV) chì chjamarà una funzione per cambià u LED attivu. Questu hè fattu da cambià u bit Update Interrupt Enable in un registru cù un nome parlante

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
Interrompe l'abilitazione di u registru

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

A seconda diferenza hè cunnessu cù un tali fenomenu di multiplexing cum'è ghosting - luce parassita di diodi. In u nostru casu, pò apparisce per u fattu chì u cronometru, chì hà causatu una interruzzione in u UEV, cuntinueghja à tick, è u gestore di l'interruzzione ùn hà micca tempu di cambià u LED prima chì u timer cumencia à scrive qualcosa à i outputs. Per cumbattà questu, avete da invertisce a logica (0 = luminosità massima, 255 = nunda ùn hè micca attivu) è ùn permettenu micca valori estremi di u ciclu di duty. Quelli. assicuratevi chì dopu à UEV i LED sò completamente spenti per un ciclu PWM.

Cambia a polarità:

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

Evitate di mette r, g è b à 255 è ricordate di invertisce quandu si usa.

Interrupts

L'essenza di l'interruzzione hè chì, in certi circustanzi, u chip cessà di eseguisce u prugramma principale è chjama una funzione esterna. L'interruzioni sò per via di influenze esterne o internu, ancu da u timer.

Quandu avemu creatu prima un prughjettu in ST Visual Develop, fora di main.c avemu una finestra cù un schedariu misteriosu stm8_interrupt_vector.cautomaticamente inclusu in u prugettu. In questu schedariu, una funzione hè attaccata à ogni interruzzione NonHandledInterrupt. Avemu bisognu di ligà a nostra funzione à l'interruzzione desiderata.

A datasheet hà una tabella di vettori di interruzzione, induve truvamu quelli chì avemu bisognu:

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8
13 TIM2 aghjurnamentu / overflow
14 TIM2 Capture/Compare

Avemu bisognu di cambià u LED à UEV, cusì l'interruzzione #13 hè necessaria.

Per quessa, prima, in u schedariu stm8_interrupt_vector.c cambia u nome di a funzione rispunsevuli di l'interruzzione numeru 13 (IRQ13) per difettu à a nostra propria:

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

Siconda, avemu da creà un schedariu main.h cuntenutu cum'è questu:

#ifndef __MAIN_H
#define __MAIN_H

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

È infine, scrivite sta funzione in u vostru 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;
}

Resta per attivà l'interruzioni. Questu hè fattu cù un cumandamentu assembler. rim - vi tuccherà à circà lu in Manuale di prugrammazione:

//enable interrupts
_asm("rim");

Un'altra struzzione di assembler - sim - Disattiva l'interruzzioni. Deve esse disattivati ​​mentre i novi valori sò scritti in a "memoria video" in modu chì una interruzzione causata in un momentu sfurtunatu ùn sguassate micca l'array.

Tuttu u codice - nantu à Github.

Leghjite datasheets 2: SPI nantu à STM32; PWM, timers è interruzioni nantu à STM8

Se almenu qualchissia questu articulu hè utile, allora ùn l'aghju micca scrittu in vain. Seraghju cuntentu di cumenti è rimarche, pruvaraghju à risponde à tutti.

Source: www.habr.com

Add a comment