Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

В la prima parte Ho provato a spiegare agli ingegneri elettronici per hobby cresciuti con i pantaloni Arduino come e perché dovrebbero leggere le schede tecniche e altra documentazione per i microcontrollori. Il testo si è rivelato ampio, quindi ho promesso di mostrare esempi pratici in un articolo separato. Ebbene, si definiva un fungo di latte...

Oggi ti mostrerò come utilizzare le schede tecniche per risolvere attività abbastanza semplici, ma necessarie per molti progetti, sui controller STM32 (Blue Pill) e STM8. Tutti i progetti demo sono dedicati ai miei LED preferiti, li illumineremo in grandi quantità, per cui dovremo utilizzare ogni tipo di periferica interessante.

Il testo si è rivelato ancora una volta enorme, quindi per comodità sto creando il contenuto:

STM32 Blue Pill: 16 LED con driver DM634
STM8: configurazione di sei pin PWM
STM8: 8 LED RGB su tre pin, interruzioni

Disclaimer: non sono un ingegnere, non pretendo di avere conoscenze approfondite di elettronica, l'articolo è destinato ad amatori come me. In effetti, due anni fa mi consideravo il pubblico target. Se qualcuno mi avesse detto allora che le schede tecniche su un chip sconosciuto non erano spaventose da leggere, non avrei passato molto tempo a cercare qualche pezzo di codice su Internet e a inventare stampelle con forbici e nastro adesivo.

Il focus di questo articolo è sui fogli dati, non sui progetti, quindi il codice potrebbe non essere molto accurato e spesso angusto. I progetti stessi sono molto semplici, sebbene adatti per una prima conoscenza con il nuovo chip.

Spero che il mio articolo possa aiutare qualcuno in una fase simile di immersione nell'hobby.

STM32

16 LED con DM634 e SPI

Un piccolo progetto che utilizza Blue Pill (STM32F103C8T6) e il driver LED DM634. Utilizzando i fogli dati, scopriremo il driver, le porte IO STM e configureremo SPI.

DM634

Chip taiwanese con 16 uscite PWM a 16 bit, collegabili in catene. Il modello low-end a 12 bit è noto da un progetto domestico pacchetto leggero. Un tempo, scegliendo tra il DM63x e il noto TLC5940, ho scelto DM per diversi motivi: 1) TLC su Aliexpress è decisamente falso, ma questo no; 2) DM dispone di un PWM autonomo con proprio generatore di frequenza; 3) potrebbe essere acquistato a buon mercato a Mosca, invece di aspettare un pacco da Ali. E, naturalmente, è stato interessante imparare a controllare da soli il chip, piuttosto che utilizzare una libreria già pronta. I chip sono ora presentati principalmente nel pacchetto SSOP24; sono facili da saldare a un adattatore.

Poiché il produttore è taiwanese, scheda dati il chip è scritto in inglese cinese, il che significa che sarà divertente. Per prima cosa guardiamo la piedinatura (Connessione pin) per capire a quale gamba collegare cosa, e una descrizione dei pin (Descrizione pin). 16 perni:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Sorgenti sink CC (scarico aperto)

Lavello / Uscita a scarico aperto - drenare; fonte di corrente in entrata; l'uscita è collegata a terra nello stato attivo - i LED sono collegati al driver tramite catodi. Elettricamente, questo non è, ovviamente, uno “scarico aperto” (Scarico aperto), ma nelle schede tecniche si trova spesso questa designazione per i pin in modalità drenaggio.

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Resistenze esterne tra REXT e GND per impostare il valore della corrente di uscita

Un resistore di riferimento è installato tra il pin REXT e la terra, che controlla la resistenza interna delle uscite, vedere il grafico a pagina 9 della scheda tecnica. Nel DM634 questa resistenza può essere controllata anche via software, impostando la luminosità complessiva (luminosità globale); Non entrerò nei dettagli in questo articolo, qui inserirò solo una resistenza da 2.2 - 3 kOhm.

Per capire come controllare il chip, diamo un'occhiata alla descrizione dell'interfaccia del dispositivo:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

Sì, eccolo qui, l'inglese cinese in tutto il suo splendore. Tradurlo è problematico, puoi capirlo se lo desideri, ma c'è un altro modo: guarda come è descritta la connessione al TLC5940 funzionalmente simile nella scheda tecnica:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
... Sono necessari solo tre pin per inserire i dati nel dispositivo. Il fronte di salita del segnale SCLK sposta i dati dal pin SIN al registro interno. Dopo che tutti i dati sono stati caricati, un breve segnale XLAT alto aggancia i dati trasferiti in sequenza nei registri interni. I registri interni sono porte attivate dal livello del segnale XLAT. Tutti i dati vengono trasmessi per primi con il bit più significativo.

chiavistello – scrocco/scrocco/serratura.
Bordo ascendente – fronte anteriore dell’impulso
MSB prima – bit più significativo (più a sinistra) in avanti.
per cronometrare i dati – trasmettere i dati in sequenza (bit per bit).

Parola chiavistello si trova spesso nella documentazione dei chip ed è tradotto in vari modi, quindi per motivi di comprensione mi permetto

piccolo programma educativoIl driver LED è essenzialmente un registro a scorrimento. "Spostare" (spostamento) nel nome - movimento bit a bit dei dati all'interno del dispositivo: ogni nuovo bit inserito all'interno spinge in avanti l'intera catena davanti a sé. Poiché nessuno vuole osservare il lampeggiamento caotico dei LED durante il turno, il processo avviene in registri buffer separati dai registri di lavoro da uno smorzatore (chiavistello) è una sorta di sala d'attesa dove i pezzi vengono disposti nella sequenza desiderata. Quando tutto è pronto, l'otturatore si apre e le punte si mettono al lavoro, sostituendo il lotto precedente. Parola chiavistello nella documentazione dei microcircuiti implica quasi sempre un tale smorzatore, indipendentemente dalle combinazioni in cui viene utilizzato.

Quindi, il trasferimento dei dati al DM634 viene effettuato in questo modo: impostare l'ingresso DAI sul valore del bit più significativo del LED lontano, tirare DCK su e giù; imposta l'ingresso DAI sul valore del bit successivo, tira DCK; e così via finché tutti i bit non sono stati trasmessi (timbrato), dopo di che tiriamo LAT. Questa operazione può essere eseguita manualmente (un po'-bang), ma è meglio utilizzare un'interfaccia SPI appositamente studiata per questo, poiché sul nostro STM32 è presentata in due copie.

Pillola blu STM32F103

Introduttivo: i controller STM32 sono molto più complessi di Atmega328 di quanto potrebbero sembrare spaventosi. Inoltre, per ragioni di risparmio energetico, quasi tutte le periferiche vengono spente all'avvio e la frequenza di clock è di 8 MHz dalla sorgente interna. Fortunatamente i programmatori STM hanno scritto il codice che porta il chip fino ai 72 MHz “calcolati”, e gli autori di tutti gli IDE che conosco lo hanno incluso nella procedura di inizializzazione, quindi non abbiamo bisogno di clock (ma puoi se lo vuoi davvero). Ma dovrai accendere le periferiche.

Documentazione: Blue Pill è dotato del popolare chip STM32F103C8T6, ci sono due documenti utili a riguardo:

Nella scheda tecnica potrebbero interessarci:

  • Pinouts – chip pinouts – nel caso in cui decidiamo di realizzare noi stessi le schede;
  • Mappa della memoria: mappa della memoria per un chip specifico. Il Manuale di Riferimento ha una mappa per l'intera linea e menziona registri che la nostra non ha.
  • Tabella delle definizioni dei pin: elenca le funzioni principali e alternative dei pin; per la “pillola blu” potete trovare su Internet immagini più comode con l'elenco dei pin e delle loro funzioni. Cerchiamo quindi subito su Google il pinout di Blue Pill e teniamo questa immagine a portata di mano:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
NB: c'è stato un errore nell'immagine presa da Internet, che è stato segnalato nei commenti, grazie per questo. L'immagine è stata sostituita, ma questa è una lezione: è meglio controllare le informazioni non dalle schede tecniche.

Rimuoviamo la scheda tecnica, apriamo il Manuale di riferimento e da ora in poi utilizziamo solo quello.
Procedura: ci occupiamo di input/output standard, configuriamo SPI, accendiamo le periferiche necessarie.

Input Output

Sull'Atmega328, l'I/O è implementato in modo estremamente semplice, motivo per cui l'abbondanza di opzioni STM32 può creare confusione. Ora mancano solo le conclusioni, ma anche queste hanno quattro opzioni:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
scarico aperto, push-pull, push-pull alternativo, scarico aperto alternativo

"Tira-spingi" (tira e molla) è il solito output di Arduino, il pin può assumere il valore HIGH o LOW. Ma con lo “scarico aperto” ci sono complessità, anche se in realtà qui tutto è semplice:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Configurazione dell'uscita / quando la porta è assegnata all'uscita: / buffer di uscita abilitato: / – modalità open drain: "0" nel registro di uscita abilita N-MOS, "1" nel registro di uscita lascia la porta in modalità Hi-Z ( P-MOS non è attivato ) / – modalità push-pull: “0” nel registro di uscita attiva N-MOS, “1” nel registro di uscita attiva P-MOS.

Tutta la differenza tra scarico aperto (Scarico aperto) da “push-pull” (tira e molla) è che nel primo pin non può accettare lo stato HIGH: quando ne scrive uno nel registro di uscita, entra in modalità alta resistenza (alta impedenza, Ciao-Z). Quando si scrive zero, il pin si comporta allo stesso modo in entrambe le modalità, sia logicamente che elettricamente.

Nella modalità di output normale, il pin trasmette semplicemente il contenuto del registro di output. In "alternativa" è controllato dalle periferiche corrispondenti (vedi 9.1.4):

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Se un bit di porta è configurato come pin con funzione alternativa, il registro pin è disabilitato e il pin è collegato al pin della periferica.

La funzionalità alternativa di ciascun pin è descritta in Definizioni dei Pin La scheda tecnica è sull'immagine scaricata. Alla domanda su cosa fare se un pin ha più funzioni alternative, la risposta è data da una nota nel datasheet:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Se più periferiche utilizzano lo stesso pin, per evitare conflitti tra funzioni alternative, è necessario utilizzare solo una periferica alla volta, attivata utilizzando il bit di abilitazione dell'orologio della periferica (nel registro RCC appropriato).

Infine, anche i pin in modalità output hanno una velocità di clock. Questa è un'altra funzionalità di risparmio energetico; nel nostro caso, la impostiamo semplicemente al massimo e ce ne dimentichiamo.

Quindi: stiamo usando SPI, il che significa che due pin (con i dati e con un segnale di clock) dovrebbero essere “funzione push-pull alternativa”, e un altro (LAT) dovrebbe essere “push-pull normale”. Ma prima di assegnarli, occupiamoci dello SPI.

SPI

Un altro piccolo programma educativo

SPI o Serial Peripheral Interface (interfaccia periferica seriale) è un'interfaccia semplice e molto efficace per connettere un MK con altri MK e con il mondo esterno in generale. Il principio del suo funzionamento è già stato descritto sopra, per quanto riguarda il driver LED cinese (nel manuale di riferimento, vedere la sezione 25). SPI può funzionare in modalità master (“master”) e slave (“slave”). SPI ha quattro canali base, di cui non tutti possono essere utilizzati:

  • MOSI, Uscita Master/Ingresso Slave: questo pin trasmette i dati in modalità master e riceve i dati in modalità slave;
  • MISO, Master Input / Slave Output: al contrario, riceve nel master e trasmette nello slave;
  • SCK, Serial Clock: imposta la frequenza di trasmissione dei dati nel master o riceve un segnale di clock nello slave. Essenzialmente colpiscono i ritmi;
  • SS, Slave Select: con l'aiuto di questo canale lo schiavo sa che si vuole qualcosa da lui. Su STM32 si chiama NSS, dove N = negativo, cioè il controller diventa slave se è presente terra in questo canale. Si combina bene con la modalità Open Drain Output, ma questa è un'altra storia.

Come tutto il resto, SPI su STM32 è ricco di funzionalità, il che lo rende alquanto difficile da comprendere. Ad esempio, può funzionare non solo con SPI, ma anche con un'interfaccia I2S, e nella documentazione le loro descrizioni sono contrastanti, è necessario eliminare l'eccesso in modo tempestivo. Il nostro compito è estremamente semplice: dobbiamo solo inviare i dati utilizzando solo MOSI e SCK. Andiamo alla sezione 25.3.4 (comunicazione half-duplex, comunicazione half-duplex), dove troviamo 1 orologio e 1 cavo dati unidirezionale (1 segnale di clock e 1 flusso di dati unidirezionale):

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
In questa modalità, l'applicazione utilizza SPI in modalità di sola trasmissione o di sola ricezione. / La modalità di sola trasmissione è simile alla modalità duplex: i dati vengono trasmessi sul pin di trasmissione (MOSI in modalità master o MISO in modalità slave) e il pin di ricezione (rispettivamente MISO o MOSI) può essere utilizzato come un normale pin I/O . In questo caso l'applicazione deve solo ignorare il buffer Rx (se viene letto, lì non verranno trasferiti dati).

Ottimo, il pin MISO è libero, colleghiamogli il segnale LAT. Diamo un'occhiata a Slave Select, che sull'STM32 può essere controllato a livello di programmazione, il che è estremamente conveniente. Leggiamo il paragrafo omonimo nel paragrafo 25.3.1 Descrizione Generale SPI:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Controllo software NSS (SSM = 1) / Le informazioni sulla selezione dello slave sono contenute nel bit SSI del registro SPI_CR1. Il pin NSS esterno rimane libero per altre esigenze applicative.

E' il momento di scrivere ai registri. Ho deciso di utilizzare SPI2, cercare il suo indirizzo di base nel foglio dati - nella sezione 3.3 Mappa della memoria:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

Bene, cominciamo:

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

Aprire la sezione 25.3.3 con il titolo autoesplicativo “Configurazione SPI in modalità Master”:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

1. Impostare la frequenza del clock seriale con i bit BR[2:0] nel registro SPI_CR1.

I registri sono raccolti nell'omonima sezione del manuale di riferimento. Spostamento dell'indirizzo (Offset dell'indirizzo) per CR1 – 0x00, per impostazione predefinita tutti i bit vengono cancellati (Ripristina valore 0x0000):

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

I bit BR impostano il divisore del clock del controller, determinando così la frequenza alla quale funzionerà lo SPI. La nostra frequenza STM32 sarà di 72 MHz, il driver LED, secondo la sua scheda tecnica, funziona con una frequenza fino a 25 MHz, quindi dobbiamo dividere per quattro (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. Impostare i bit CPOL e CPHA per definire la relazione tra trasferimento dati e temporizzazione dell'orologio seriale (vedere diagramma a pagina 240)

Poiché qui stiamo leggendo un foglio dati e non guardando gli schemi, diamo un'occhiata più da vicino alla descrizione testuale dei bit CPOL e CPHA a pagina 704 (Descrizione generale SPI):

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Fase e polarità dell'orologio
Utilizzando i bit CPOL e CPHA del registro SPI_CR1, è possibile selezionare a livello di codice quattro relazioni di temporizzazione. Il bit CPOL (polarità del clock) controlla lo stato del segnale del clock quando non vengono trasmessi dati. Questo bit controlla le modalità master e slave. Se CPOL viene ripristinato, il pin SCK è basso in modalità di riposo. Se il bit CPOL è impostato, il pin SCK è alto durante la modalità di riposo.
Quando è impostato il bit CPHA (fase di clock), lo strobe del bit alto è il secondo fronte del segnale SCK (discendente se CPOL è azzerato, crescente se CPOL è impostato). I dati vengono catturati dalla seconda variazione del segnale di clock. Se il bit CPHA è azzerato, lo strobe del trap del bit alto è il fronte di salita del segnale SCK (fronte di discesa se CPOL è impostato, fronte di salita se CPOL è azzerato). I dati vengono acquisiti al primo cambiamento nel segnale dell'orologio.

Avendo assorbito questa conoscenza, arriviamo alla conclusione che entrambi i bit devono rimanere zero, perché Vogliamo che il segnale SCK rimanga basso quando non è in uso e che i dati vengano trasmessi sul fronte di salita dell'impulso (vedere Fig. Bordo ascendente nella scheda tecnica DM634).

A proposito, qui abbiamo incontrato per la prima volta una caratteristica del vocabolario nei fogli dati ST: in essi è scritta la frase "reimposta il bit a zero" per resettare un po'E non per schiarire un po', come, ad esempio, Atmega.

3. Impostare il bit DFF per determinare se il blocco dati è in formato 8 bit o 16 bit

Ho preso appositamente un DM16 a 634 bit per non preoccuparmi di trasmettere dati PWM a 12 bit, come il DM633. Ha senso impostare DFF su uno:

#define DFF         0x0800

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

4. Configurare il bit LSBFIRST nel registro SPI_CR1 per determinare il formato del blocco

LSBFIRST, come suggerisce il nome, configura prima la trasmissione con il bit meno significativo. Ma DM634 vuole ricevere i dati a partire dal bit più significativo. Pertanto, lo lasciamo ripristinato.

5. In modalità hardware, se è richiesto l'input dal pin NSS, applicare un segnale alto al pin NSS durante l'intera sequenza di trasferimento dei byte. Nella modalità software NSS, impostare i bit SSM e SSI nel registro SPI_CR1. Se il pin NSS deve essere utilizzato come uscita, è necessario impostare solo il bit SSOE.

Installa SSM e SSI per dimenticare la modalità hardware NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. I bit MSTR e SPE devono essere impostati (rimangono impostati solo se il segnale NSS è alto)

In realtà, con questi bit designiamo la nostra SPI come master e la accendiamo:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI è configurato, scriviamo subito le funzioni che inviano byte al driver. Continua a leggere 25.3.3 “Configurazione SPI in modalità master”:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Ordine di trasferimento dati
La trasmissione inizia quando un byte viene scritto nel buffer Tx.
Il byte di dati viene caricato nel registro a scorrimento in parallele modalità (dal bus interno) durante la trasmissione del primo bit, dopodiché viene trasmesso a sequenziale Modalità pin MOSI, primo o ultimo bit in avanti a seconda dell'impostazione del bit LSBFIRST nel registro CPI_CR1. Il flag TXE viene impostato dopo la trasmissione dei dati dal buffer Tx al registro a scorrimentoe genera anche un interrupt se è impostato il bit TXEIE nel registro CPI_CR1.

Ho evidenziato alcune parole nella traduzione per attirare l'attenzione su una caratteristica dell'implementazione SPI nei controller STM. Su Atmega la bandiera TXE (Tx vuoto, Tx è vuoto e pronto a ricevere dati) viene impostato solo dopo che l'intero byte è stato inviato fuori. E qui questo flag viene impostato dopo che il byte è stato inserito nel registro a scorrimento interno. Poiché viene spinto lì con tutti i bit contemporaneamente (in parallelo), e quindi i dati vengono trasferiti in sequenza, TXE viene impostato prima che il byte venga completamente inviato. Questo è importante perché nel caso del nostro driver LED, dobbiamo estrarre il pin LAT dopo l'invio tutti dati, cioè La sola bandiera TXE non ci basterà.

Ciò significa che abbiamo bisogno di un'altra bandiera. Diamo un'occhiata a 25.3.7 - “Flag di stato”:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
<…>
Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Indicatore OCCUPATO
Il flag BSY viene impostato e cancellato dall'hardware (la scrittura su di esso non ha alcun effetto). Il flag BSY indica lo stato del livello di comunicazione SPI.
Si resetta:
al termine del trasferimento (eccetto in modalità master se il trasferimento è continuo)
quando SPI è disabilitato
quando si verifica un errore in modalità master (MODF=1)
Se il trasferimento non è continuo, il flag BSY viene cancellato tra ogni trasferimento di dati

Ok, questo tornerà utile. Scopriamo dove si trova il buffer Tx. Per fare ciò leggere “Registro Dati SPI”:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Bit 15:0 DR[15:0] Registro dati
Dati ricevuti o dati da trasmettere.
Il registro dati è diviso in due buffer: uno per la scrittura (buffer di trasmissione) e uno per la lettura (buffer di ricezione). La scrittura nel registro dati scrive nel buffer Tx e la lettura dal registro dati restituirà il valore contenuto nel buffer Rx.

Bene, e il registro di stato, dove si trovano i flag TXE e BSY:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

Noi scriviamo:

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

Bene, poiché dobbiamo trasmettere 16 volte due byte, a seconda del numero di uscite del driver LED, qualcosa del genere:

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 non sappiamo ancora come estrarre il pin LAT, quindi torneremo all'I/O.

Assegnazione dei pin

Nell'STM32F1 i registri responsabili dello stato dei pin sono piuttosto insoliti. È chiaro che ce ne sono più di Atmega, ma sono anche diversi dagli altri chip STM. Sezione 9.1 Descrizione generale del GPIO:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Ciascuna delle porte I/O per uso generale (GPIO) ha due registri di configurazione a 32 bit (GPIOx_CRL e GPIOx_CRH), due registri dati a 32 bit (GPIOx_IDR e GPIOx_ODR), un registro di set/reset a 32 bit (GPIOx_BSRR), un registro di reset a 16 bit (GPIOx_BRR) e un registro di reset a 32 bit registro di blocco bit (GPIOx_LCKR).

I primi due registri sono insoliti, e anche piuttosto scomodi, perché i 16 pin delle porte sono sparsi su di essi in un formato “quattro bit per fratello”. Quelli. i pin da zero a sette sono in CRL e il resto è in CRH. Allo stesso tempo, i restanti registri contengono con successo i bit di tutti i pin della porta, spesso la metà rimanente “riservata”.

Per semplicità partiamo dalla fine della lista.

Non abbiamo bisogno di un registro di blocco.

I registri set e reset sono piuttosto divertenti in quanto si duplicano parzialmente a vicenda: puoi scrivere tutto solo in BSRR, dove i 16 bit più alti resetteranno il pin a zero, e quelli inferiori saranno impostati a 1, oppure puoi anche utilizzare BRR, i cui 16 bit inferiori ripristinano solo il pin . Mi piace la seconda opzione. Questi registri sono importanti perché forniscono accesso atomico ai pin:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Set atomico o ripristino
Non è necessario disabilitare gli interrupt quando si programma GPIOx_ODR a livello di bit: uno o più bit possono essere modificati con una singola operazione di scrittura atomica APB2. Ciò si ottiene scrivendo un "1" nel registro di set/reset (GPIOx_BSRR o, solo per il reset, GPIOx_BRR) del bit che deve essere modificato. Gli altri bit rimarranno invariati.

I registri dati hanno nomi abbastanza autoesplicativi - IDR = Ingresso Registro di direzione, registro di input; ODR = Uscita Registro di direzione, registro di uscita. Non ne avremo bisogno nel progetto attuale.

E infine, controllare i registri. Dato che siamo interessati ai secondi pin SPI, ovvero PB13, PB14 e PB15, guardiamo subito CRH:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

E vediamo che dovremo scrivere qualcosa nei bit da 20 a 31.

Abbiamo già capito sopra cosa vogliamo dai pin, quindi qui farò senza screenshot, dirò solo che MODE specifica la direzione (input se entrambi i bit sono impostati su 0) e la velocità del pin (abbiamo bisogno di 50 MHz, cioè entrambi i pin su "1"), e CNF imposta la modalità: normale "push-pull" - 00, "alternativa" - 10. Per impostazione predefinita, come vediamo sopra, tutti i pin hanno il terzo bit dal basso (CNF0), li imposta sulla modalità ingresso flottante.

Poiché ho intenzione di fare qualcos'altro con questo chip, per semplicità ho definito tutti i possibili valori MODE e CNF sia per il registro di controllo inferiore che per quello superiore.

Bene, qualcosa del genere

#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 si trovano sulla porta B (indirizzo base – 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;

E, di conseguenza, puoi scrivere le definizioni per LAT, che verranno contratte dai registri BRR e 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 solo per inerzia, è sempre stato così, lasciamo stare)

Ora va tutto benissimo, ma non funziona. Poiché si tratta di STM32, risparmiano energia elettrica, il che significa che è necessario abilitare il clock delle periferiche richieste.

Attiva il clock

L'orologio, noto anche come Orologio, è responsabile del conteggio del tempo. E potremmo già notare la sigla RCC. Lo cerchiamo nella documentazione: si tratta di Reset e Clock Control.

Come detto sopra, fortunatamente, la parte più difficile del tema del clock è stata fatta per noi da persone di STM, per le quali li ringraziamo moltissimo (ancora una volta darò un link a Il sito web di Di Halt, per chiarire quanto sia confuso). Abbiamo solo bisogno dei registri responsabili dell'abilitazione del clock periferico (Peripheral Clock Enable Registers). Innanzitutto, troviamo l'indirizzo di base dell'RCC, è all'inizio della "Mappa della memoria":

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

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

E poi o clicca sul link dove cerchi qualcosa nella targa, oppure, molto meglio, scorri le descrizioni dei registri di abilitazione dalle sezioni riguardanti abilitare i registri. Dove troveremo RCC_APB1ENR e RCC_APB2ENR:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

E, di conseguenza, contengono bit che includono il clock di SPI2, IOPB (porta I/O B) e 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;

È possibile trovare il codice finale qui.

Se hai l'opportunità e il desiderio di testare, collega il DM634 in questo modo: DAI a PB15, DCK a PB13, LAT a PB14. Alimentiamo il driver da 5 volt, non dimenticare di collegare la terra.

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

STM8 PWM

PWM su STM8

Mentre stavo progettando questo articolo, ho deciso, ad esempio, di provare a padroneggiare alcune funzionalità di un chip sconosciuto utilizzando solo un foglio dati, in modo da non ritrovarmi con un calzolaio senza stivali. STM8 era l'ideale per questo ruolo: in primo luogo, avevo un paio di schede cinesi con STM8S103, e in secondo luogo, non è molto apprezzata, e quindi la tentazione di leggere e trovare una soluzione su Internet risiede proprio nella mancanza di queste stesse soluzioni.

Anche il chip ha scheda dati и manuale di riferimento RM0016, nel primo c'è il pinout e gli indirizzi di registro, nel secondo - tutto il resto. STM8 è programmato in C in un terribile IDE Sviluppo visivo ST.

Clock e I/O

Per impostazione predefinita, STM8 funziona ad una frequenza di 2 MHz, questo deve essere corretto immediatamente.

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Orologio HSI (interno ad alta velocità).
Il segnale di clock HSI deriva da un oscillatore RC interno da 16 MHz con un divisore programmabile (da 1 a 8). Viene impostato nel registro del divisore dell'orologio (CLK_CKDIVR).
Nota: all'inizio, come sorgente principale del segnale di clock viene selezionato un oscillatore HSI RC con un divisore di 8.

Troviamo l'indirizzo del registro nella scheda tecnica, la descrizione in refman e vediamo che il registro deve essere cancellato:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Dato che eseguiremo PWM e collegheremo i LED, diamo un'occhiata alla piedinatura:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

Il chip è piccolo, molte funzioni sono sospese sugli stessi pin. Ciò che è tra parentesi quadre è "funzionalità alternativa", viene commutato da "byte di opzione" (byte di opzione) – qualcosa come i fusibili Atmega. Puoi modificare i loro valori a livello di codice, ma non è necessario, perché La nuova funzionalità viene attivata solo dopo un riavvio. È più semplice utilizzare ST Visual Programmer (scaricato con Visual Develop), che può modificare questi byte. La piedinatura mostra che i pin CH1 e CH2 del primo timer sono nascosti tra parentesi quadre; è necessario impostare i bit AFR1 e AFR0 in STVP, e il secondo trasferirà anche l'uscita CH1 del secondo timer da PD4 a PC5.

Pertanto, 6 pin controlleranno i LED: PC6, PC7 e PC3 per il primo timer, PC5, PD3 e PA3 per il secondo.

La configurazione dei pin I/O stessi su STM8 è più semplice e più logica rispetto a STM32:

  • familiare dal registro di direzione dei dati Atmega DDR (Registro della direzione dei dati): 1 = uscita;
  • il primo registro di controllo CR1, in uscita, imposta la modalità push-pull (1) o drain aperto (0); poiché collego i LED al chip con i catodi, lascio qui degli zeri;
  • il secondo registro di controllo CR2, in uscita, imposta la velocità del clock: 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

Impostazione PWM

Per prima cosa definiamo i termini:

  • Frequenza PWM – frequenza con cui scorre il timer;
  • Ricarica automatica, AR – valore autocaricabile fino al quale conterà il timer (periodo di impulso);
  • Evento di aggiornamento, UEV – un evento che si verifica quando il timer ha contato fino all'AR;
  • Ciclo di lavoro PWM – Ciclo di lavoro PWM, spesso chiamato “duty factor”;
  • Cattura/Confronta valore – valore per l'acquisizione/confronto su cui ha conteggiato il timer farà qualcosa (nel caso di PWM inverte il segnale in uscita);
  • Valore di precarico – valore precaricato. Confronta valore non può cambiare mentre il timer sta ticchettando, altrimenti il ​​ciclo PWM si interromperà. Pertanto, i nuovi valori trasmessi vengono inseriti in un buffer ed estratti quando il timer raggiunge la fine del conto alla rovescia e viene azzerato;
  • Allineato al bordo и Modalità allineate al centro – allineamento lungo il bordo e al centro, lo stesso di Atmel PWM veloce и PWM con correzione di fase.
  • OCiREF, segnale di riferimento di confronto in uscita – segnale di uscita di riferimento, appunto, quello che appare sul pin corrispondente in modalità PWM.

Come risulta già dalla piedinatura, due timer hanno funzionalità PWM: il primo e il secondo. Entrambi sono a 16 bit, il primo ha molte funzionalità aggiuntive (in particolare può contare sia in alto che in basso). Abbiamo bisogno che entrambi funzionino allo stesso modo, quindi ho deciso di iniziare con il secondo, ovviamente più povero, per non usare accidentalmente qualcosa che non c'è. Qualche problema è che la descrizione della funzionalità PWM di tutti i timer nel manuale di riferimento si trova nel capitolo relativo al primo timer (17.5.7 Modalità PWM), quindi devi saltare avanti e indietro in tutto il documento continuamente.

PWM su STM8 presenta un vantaggio importante rispetto a PWM su Atmega:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
PWM allineato ai confini
Configurazione dell'account dal basso verso l'alto
Il conteggio dal basso verso l'alto è attivo se il bit DIR nel registro TIM_CR1 è azzerato
esempio
L'esempio utilizza la prima modalità PWM. Il segnale di riferimento PWM OCiREF viene mantenuto alto finché TIM1_CNT < TIM1_CCRi. Altrimenti ci vuole un livello basso. Se il valore di confronto nel registro TIM1_CCRi è maggiore del valore di caricamento automatico (registro TIM1_ARR), il segnale OCiREF viene mantenuto a 1. Se il valore di confronto è 0, OCiREF viene mantenuto a zero....

Temporizzatore STM8 durante evento di aggiornamento controlla prima confrontare il valore, e solo allora produce un segnale di riferimento. Il timer di Atmega prima sbaglia e poi confronta, risultando compare value == 0 l'output è un ago, che deve essere gestito in qualche modo (ad esempio, invertendo la logica a livello di codice).

Quindi cosa vogliamo fare: PWM a 8 bit (AR == 255), contando dal basso verso l'alto, allineamento lungo il bordo. Poiché le lampadine sono collegate al chip tramite catodi, il PWM dovrebbe emettere 0 (LED acceso) fino a quando confrontare il valore e 1 dopo.

Di alcuni ne abbiamo già letto Modalità PWM, quindi troviamo il registro richiesto del secondo timer cercando nel manuale di riferimento questa frase (18.6.8 - TIMx_CCMR1):

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
110: Prima modalità PWM – quando si conta dal basso verso l'alto, il primo canale è attivo mentre TIMx_CNT < TIMx_CCR1. Altrimenti il ​​primo canale è inattivo. [più avanti nel documento c'è un copia-incolla errato del timer 1] 111: Seconda modalità PWM – quando si conta dal basso verso l'alto, il primo canale è inattivo mentre TIMx_CNT < TIMx_CCR1. Altrimenti, il primo canale è attivo.

Dato che i LED sono collegati al MK tramite catodi, la seconda modalità è adatta a noi (anche la prima, ma non lo sappiamo ancora).

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Bit 3 OC1PE: abilita il precarico del pin 1
0: il registro di precarico su TIMx_CCR1 è disabilitato. Puoi scrivere su TIMx_CCR1 in qualsiasi momento. Il nuovo valore funziona immediatamente.
1: il registro di precaricamento su TIMx_CCR1 è abilitato. Le operazioni di lettura/scrittura accedono al registro di precaricamento. Il valore precaricato TIMx_CCR1 viene caricato nel registro ombra durante ogni evento di aggiornamento.
*Nota: affinché la modalità PWM funzioni correttamente, i registri di precarico devono essere abilitati. Ciò non è necessario nella modalità a segnale singolo (il bit OPM è impostato nel registro TIMx_CR1).

Ok, accendiamo tutto ciò che ci serve per i tre canali del secondo 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 è composto da due registri a otto bit, tutto è semplice:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Il secondo timer può contare solo dal basso verso l'alto, allineato lungo il bordo, non è necessario modificare nulla. Impostiamo il divisore di frequenza, ad esempio, su 256. Per il secondo timer, il divisore è impostato nel registro TIM2_PSCR ed è una potenza di due:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Non resta che attivare le conclusioni e il secondo timer stesso. Il primo problema è risolto dai registri Cattura/Confronta permettere: ci sono due, tre canali sparsi su di essi in modo asimmetrico. Qui possiamo anche apprendere che è possibile cambiare la polarità del segnale, ad es. in linea di principio, era possibile utilizzare la modalità PWM 1. Scriviamo:

#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, avviamo il timer nel registro TIMx_CR1:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Scriviamo un semplice analogo di AnalogWrite(), che trasferirà i valori effettivi al timer per il confronto. I registri hanno nomi prevedibili Cattura/Confronta registri, ce ne sono due per ciascun canale: gli 8 bit di ordine basso in TIM2_CCRxL e quelli di ordine alto in TIM2_CCRxH. Dato che abbiamo creato un PWM a 8 bit, è sufficiente scrivere solo i bit meno significativi:

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

Il lettore attento noterà che abbiamo un PWM leggermente difettoso, incapace di produrre il riempimento al 100% (ad un valore massimo di 255, il segnale viene invertito per un ciclo del timer). Per i LED questo non ha importanza e il lettore attento può già intuire come ripararlo.

Il PWM sul secondo timer funziona, passiamo al primo.

Il primo timer ha esattamente gli stessi bit negli stessi registri (è solo che quei bit che sono rimasti "riservati" nel secondo timer vengono utilizzati attivamente nel primo per ogni sorta di cose avanzate). Basta quindi ricercare gli indirizzi degli stessi registri nella scheda tecnica e copiare il codice. Bene, cambia il valore del divisore di frequenza, perché... il primo timer non vuole ricevere una potenza di due, ma un valore esatto a 16 bit in due registri Prescaler alto и Basso. Facciamo tutto e... il primo timer non funziona. Qual è il problema?

Il problema può essere risolto solo esaminando l'intera sezione relativa ai registri di controllo del timer 1, dove cerchiamo quello che non ha il secondo timer. Ci sarà 17.7.30 Registro di interruzione (TIM1_BKR), dove c'è questo pezzo:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Abilita l'uscita principale

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Questo è tutto certo adesso, il codice ibid.

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

Multisala STM8

Multiplexing su STM8

Il terzo mini-progetto consiste nel collegare otto LED RGB al secondo timer in modalità PWM e farli mostrare colori diversi. Si basa sul concetto di multiplexing LED, ovvero se accendi e spegni i LED molto, molto velocemente, ci sembrerà che siano costantemente accesi (persistenza della visione, inerzia della percezione visiva). Una volta l'ho fatto qualcosa del genere su Arduino.

L'algoritmo di lavoro è simile al seguente:

  • collegato l'anodo del primo LED RGB;
  • lo accese, inviando i segnali necessari ai catodi;
  • atteso fino alla fine del ciclo PWM;
  • collegato l'anodo del secondo LED RGB;
  • l'ho acceso...

Bene, ecc. Naturalmente, per un buon funzionamento è necessario che l'anodo sia collegato e contemporaneamente il LED sia “acceso”. Bene, o quasi. In ogni caso, dobbiamo scrivere un codice che generi i valori in tre canali del secondo timer, li cambi quando viene raggiunto l'UEV e allo stesso tempo cambi il LED RGB attualmente attivo.

Poiché la commutazione dei LED è automatica, è necessario creare una "memoria video" dalla quale il gestore degli interrupt riceverà i dati. Questo è un semplice array:

uint8_t colors[8][3];

Per cambiare il colore di un LED specifico, sarà sufficiente scrivere i valori richiesti in questo array. E la variabile sarà responsabile del numero del LED attivo

uint8_t cnt;

Demux

Per un corretto multiplexing abbiamo bisogno, stranamente, di un demultiplexer CD74HC238. Demultiplexer: un chip che implementa l'operatore nell'hardware <<. Attraverso tre pin di input (bit 0, 1 e 2) gli forniamo un numero X a tre bit e in risposta attiva il numero di output (1<<X). I restanti input del chip vengono utilizzati per ridimensionare l'intero progetto. Abbiamo bisogno di questo chip non solo per ridurre il numero di pin occupati del microcontrollore, ma anche per sicurezza, in modo da non accendere accidentalmente più LED del possibile e non bruciare l'MK. Il chip costa un centesimo e va tenuto sempre nell’armadietto dei medicinali di casa.

Il nostro CD74HC238 sarà responsabile di fornire tensione all'anodo del LED desiderato. In un multiplex completo fornirebbe tensione alla colonna tramite un P-MOSFET, ma in questa demo è possibile direttamente, perché assorbe 20 mA, secondo Valutazioni massime assolute nella scheda tecnica. Da scheda tecnica CD74HC238 abbiamo bisogno della piedinatura e di questo cheat sheet:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
H = livello di alta tensione, L = livello di bassa tensione, X – non importa

Colleghiamo E2 ed E1 a terra, E3, A0, A1 e A3 ai pin PD5, PC3, PC4 e PC5 di STM8. Poiché la tabella sopra contiene sia i livelli bassi che quelli alti, configuriamo questi pin come pin push-pull.

PWM

Il PWM sul secondo timer è configurato come nella storia precedente, con due differenze:

Innanzitutto, dobbiamo abilitare l'interruzione Aggiorna evento (UEV) che chiamerà una funzione che attiva/disattiva il LED attivo. Questo viene fatto cambiando la punta Abilitazione interruzione aggiornamento in un registro con un nome significativo

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
Registro di abilitazione degli interrupt

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

La seconda differenza è legata al fenomeno del multiplexing, come ad es ghosting – bagliore parassita dei diodi. Nel nostro caso, ciò potrebbe essere dovuto al fatto che il timer, dopo aver causato un'interruzione sull'UEV, continua a ticchettare e il gestore dell'interruzione non ha il tempo di accendere il LED prima che il timer inizi a scrivere qualcosa sui pin. Per contrastare questo problema, dovrai invertire la logica (0 = luminosità massima, 255 = niente illuminato) ed evitare valori di duty cycle estremi. Quelli. assicurarsi che dopo l'UEV i LED si spengano completamente per un ciclo PWM.

Modifica della polarità:

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

Evitare di impostare r, g e b su 255 e ricordarsi di invertirli quando li si utilizza.

Interrompe

L'essenza dell'interruzione è che in determinate circostanze il chip interrompe l'esecuzione del programma principale e richiama alcune funzioni esterne. Le interruzioni si verificano a causa di influenze esterne o interne, incluso il timer.

Quando abbiamo creato per la prima volta un progetto in ST Visual Develop, oltre a main.c abbiamo ricevuto una finestra con un file misterioso stm8_interrupt_vector.c, automaticamente incluso nel progetto. In questo file ad ogni interruzione viene assegnata una funzione NonHandledInterrupt. Dobbiamo associare la nostra funzione all'interruzione desiderata.

La scheda tecnica ha una tabella di vettori di interruzione, dove troviamo quelli di cui abbiamo bisogno:

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8
13 Aggiornamento/overflow TIM2
14 Cattura/confronta TIM2

Dobbiamo cambiare il LED sull'UEV, quindi abbiamo bisogno dell'interruzione n. 13.

Pertanto, in primo luogo, nel fascicolo stm8_interrupt_vector.c modificare il nome predefinito della funzione responsabile dell'interrupt n. 13 (IRQ13) con il proprio:

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

In secondo luogo, dovremo creare un file main.h con il seguente contenuto:

#ifndef __MAIN_H
#define __MAIN_H

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

E infine, scrivi questa funzione nel tuo file 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;
}

Non resta che abilitare gli interrupt. Questo viene fatto usando il comando assembler rim - dovrai cercarlo Manuale di programmazione:

//enable interrupts
_asm("rim");

Un altro comando assemblatore è sim – disattiva gli interrupt. Devono essere disattivati ​​durante la scrittura dei nuovi valori nella "memoria video", in modo che un'interruzione causata nel momento sbagliato non rovini l'array.

Tutto il codice - su GitHub.

Leggi le schede tecniche 2: SPI su STM32; PWM, timer e interrupt su STM8

Se almeno qualcuno trova utile questo articolo, non l'ho scritto invano. Sarò felice di ricevere commenti e osservazioni, cercherò di rispondere a tutto.

Fonte: habr.com

Aggiungi un commento