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:
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:
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.
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:
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:
... 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:
Scheda dati per microcontrollori STM32F103x8 e STM32F103xB;
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:
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:
"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:
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):
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:
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):
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:
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:
Aprire la sezione 25.3.3 con il titolo autoesplicativo “Configurazione SPI in modalità Master”:
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):
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).
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):
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:
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:
SPI è configurato, scriviamo subito le funzioni che inviano byte al driver. Continua a leggere 25.3.3 “Configurazione SPI in modalità master”:
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”:
<…>
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”:
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:
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:
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:
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:
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.
(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":
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:
E, di conseguenza, contengono bit che includono il clock di SPI2, IOPB (porta I/O B) e funzioni alternative (AFIO).
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.
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.
Per impostazione predefinita, STM8 funziona ad una frequenza di 2 MHz, questo deve essere corretto immediatamente.
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:
Dato che eseguiremo PWM e collegheremo i LED, diamo un'occhiata alla piedinatura:
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
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:
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):
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).
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:
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:
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:
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:
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:
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:
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
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.
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:
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:
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.
Se almeno qualcuno trova utile questo articolo, non l'ho scritto invano. Sarò felice di ricevere commenti e osservazioni, cercherò di rispondere a tutto.